/** @file PKCS#7 SignedData Sign Wrapper and PKCS#7 SignedData Verification Wrapper Implementation over mbedtls. RFC 8422 - Elliptic Curve Cryptography (ECC) Cipher Suites FIPS 186-4 - Digital Signature Standard (DSS) Copyright (c) 2024, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include "CryptPkcs7Internal.h" #include /* Profile for backward compatibility. Allows RSA 1024, unlike the default profile. */ STATIC mbedtls_x509_crt_profile gCompatProfile = { /* Hashes from SHA-256 and above. Note that this selection * should be aligned with ssl_preset_default_hashes in ssl_tls.c. */ #ifndef DISABLE_SHA1_DEPRECATED_INTERFACES MBEDTLS_X509_ID_FLAG (MBEDTLS_MD_SHA1) | #endif MBEDTLS_X509_ID_FLAG (MBEDTLS_MD_SHA256) | MBEDTLS_X509_ID_FLAG (MBEDTLS_MD_SHA384) | MBEDTLS_X509_ID_FLAG (MBEDTLS_MD_SHA512), 0xFFFFFFF, /* Any PK alg */ /* Curves at or above 128-bit security level. Note that this selection * should be aligned with ssl_preset_default_curves in ssl_tls.c. */ MBEDTLS_X509_ID_FLAG (MBEDTLS_ECP_DP_SECP256R1) | MBEDTLS_X509_ID_FLAG (MBEDTLS_ECP_DP_SECP384R1) | MBEDTLS_X509_ID_FLAG (MBEDTLS_ECP_DP_SECP521R1) | MBEDTLS_X509_ID_FLAG (MBEDTLS_ECP_DP_BP256R1) | MBEDTLS_X509_ID_FLAG (MBEDTLS_ECP_DP_BP384R1) | MBEDTLS_X509_ID_FLAG (MBEDTLS_ECP_DP_BP512R1) | 0, 1024, }; /** Init MbedtlsPkcs7. @param[in] Pkcs7 MbedtlsPkcs7. **/ STATIC VOID MbedTlsPkcs7Init ( MbedtlsPkcs7 *Pkcs7 ) { ZeroMem (Pkcs7, sizeof (MbedtlsPkcs7)); } /** Get Pkcs7 Next Content Len. @param[in] Ptr The start of the buffer. @param[in] End The end of the buffer. @param[out] Len MbedtlsPkcs7 Content Len. @retval 0 Success. @retval negative A negative MBEDTLS_ERR_ASN1_XXX error code on failure. **/ STATIC INT32 MbedTlsPkcs7GetNextContentLen ( UINT8 **Ptr, UINT8 *End, UINTN *Len ) { INT32 Ret; Ret = mbedtls_asn1_get_tag (Ptr, End, Len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_CONTEXT_SPECIFIC); return Ret; } /** Get Pkcs7 Version.. @param[in] Ptr The start of the buffer. @param[in] End The end of the buffer. @param[out] Ver MbedtlsPkcs7 Version. @retval 0 Success. @retval negative A negative MBEDTLS_ERR_ASN1_XXX error code on failure. **/ STATIC INT32 MbedTlsPkcs7GetVersion ( UINT8 **Ptr, UINT8 *End, INT32 *Ver ) { INT32 Ret; Ret = mbedtls_asn1_get_int (Ptr, End, Ver); return Ret; } /** ContentInfo ::= SEQUENCE { contentType ContentType, content [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL }. @param[in] Ptr The start of the buffer. @param[in] End The end of the buffer. @param[out] Pkcs7 MbedtlsPkcs7. @retval 0 Success. @retval negative A negative MBEDTLS_ERR_ASN1_XXX error code on failure. **/ STATIC INT32 Pkcs7GetContentInfoType ( UINT8 **Ptr, UINT8 *End, mbedtls_asn1_buf *Pkcs7 ) { UINTN Len; int Ret; Len = 0; Ret = mbedtls_asn1_get_tag ( Ptr, End, &Len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE ); if (Ret == 0) { Ret = mbedtls_asn1_get_tag (Ptr, End, &Len, MBEDTLS_ASN1_OID); } if (Ret == 0) { Pkcs7->tag = MBEDTLS_ASN1_OID; Pkcs7->len = Len; Pkcs7->p = *Ptr; } return Ret; } /** DigestAlgorithmIdentifier ::= AlgorithmIdentifier. @param[in] Ptr The start of the buffer. @param[in] End The end of the buffer. @param[out] Alg MbedtlsPkcs7 AlgorithmIdentifier. @retval 0 Success. @retval negative A negative MBEDTLS_ERR_ASN1_XXX error code on failure. **/ STATIC INT32 MbedTlsPkcs7GetDigestAlgorithm ( UINT8 **Ptr, UINT8 *End, mbedtls_x509_buf *Alg ) { INT32 Ret; Ret = mbedtls_asn1_get_alg_null (Ptr, End, Alg); return Ret; } /** DigestAlgorithmIdentifiers :: SET of DigestAlgorithmIdentifier. @param[in] Ptr The start of the buffer. @param[in] End The end of the buffer. @param[out] Alg MbedtlsPkcs7 AlgorithmIdentifier. @retval 0 Success. @retval negative A negative MBEDTLS_ERR_ASN1_XXX error code on failure. **/ STATIC INT32 MbedTlsPkcs7GetDigestAlgorithmSet ( UINT8 **Ptr, UINT8 *End, mbedtls_x509_buf *Alg ) { UINTN Len; INT32 Ret; Len = 0; Ret = mbedtls_asn1_get_tag ( Ptr, End, &Len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SET ); if (Ret == 0) { End = *Ptr + Len; // assume only one digest algorithm Ret = mbedtls_asn1_get_alg_null (Ptr, End, Alg); } return Ret; } /** certificates :: SET OF ExtendedCertificateOrCertificate, ExtendedCertificateOrCertificate ::= CHOICE { certificate Certificate -- x509, extendedCertificate[0] IMPLICIT ExtendedCertificate }. @param[in] Ptr The start of the buffer. @param[in] Plen The buffer len. @param[out] Certs mbedtls_x509_crt cert. @retval 0 Success. @retval negative A negative MBEDTLS_ERR_ASN1_XXX error code on failure. **/ STATIC INT32 MbedTlsPkcs7GetCertificates ( UINT8 **Ptr, INTN Plen, mbedtls_x509_crt *Certs ) { INT32 Ret; Ret = mbedtls_x509_crt_parse (Certs, *Ptr, Plen); return Ret; } /** EncryptedDigest ::= OCTET STRING. @param[in] Ptr The start of the buffer. @param[in] End The end of the buffer. @param[out] Signature Signature. @retval 0 Success. @retval negative A negative MBEDTLS_ERR_ASN1_XXX error code on failure. **/ STATIC INT32 Pkcs7GetSignature ( UINT8 **Ptr, UINT8 *End, mbedtls_asn1_buf *Signature ) { INT32 Ret; UINTN Len; Len = 0; Ret = mbedtls_asn1_get_tag (Ptr, End, &Len, MBEDTLS_ASN1_OCTET_STRING); if (Ret == 0) { Signature->tag = MBEDTLS_ASN1_OCTET_STRING; Signature->len = Len; Signature->p = *Ptr; } return Ret; } /** SignerInfo ::= SEQUENCE { version Version; issuerAndSerialNumber IssuerAndSerialNumber, digestAlgorithm DigestAlgorithmIdentifier, authenticatedAttributes [0] IMPLICIT Attributes OPTIONAL, digestEncryptionAlgorithm DigestEncryptionAlgorithmIdentifier, encryptedDigest EncryptedDigest, unauthenticatedAttributes [1] IMPLICIT Attributes OPTIONAL. @param[in] Ptr The start of the buffer. @param[in] End The end of the buffer. @param[out] SignersSet MbedtlsPkcs7SignerInfo. @retval 0 Success. @retval negative A negative MBEDTLS_ERR_ASN1_XXX error code on failure. **/ STATIC INT32 MbedTlsPkcs7GetSignersInfoSet ( UINT8 **Ptr, UINT8 *End, MbedtlsPkcs7SignerInfo *SignersSet ) { UINT8 *EndSet; INT32 Ret; UINTN Len; UINT8 *TempP; Len = 0; Ret = mbedtls_asn1_get_tag ( Ptr, End, &Len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SET ); if (Ret == 0) { EndSet = *Ptr + Len; Ret = mbedtls_asn1_get_tag ( Ptr, EndSet, &Len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE ); } if (Ret == 0) { Ret = mbedtls_asn1_get_int (Ptr, EndSet, &SignersSet->Version); } if (Ret == 0) { Ret = mbedtls_asn1_get_tag ( Ptr, EndSet, &Len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE ); } if (Ret == 0) { SignersSet->IssuerRaw.p = *Ptr; Ret = mbedtls_asn1_get_tag ( Ptr, EndSet, &Len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE ); } if (Ret == 0) { Ret = mbedtls_x509_get_name (Ptr, *Ptr + Len, &SignersSet->Issuer); } if (Ret == 0) { SignersSet->IssuerRaw.len = *Ptr - SignersSet->IssuerRaw.p; Ret = mbedtls_x509_get_serial (Ptr, EndSet, &SignersSet->Serial); } if (Ret == 0) { Ret = MbedTlsPkcs7GetDigestAlgorithm (Ptr, EndSet, &SignersSet->AlgIdentifier); } // OPTIONAL AuthenticatedAttributes if (Ret == 0) { TempP = *Ptr; if (mbedtls_asn1_get_tag (&TempP, EndSet, &Len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_CONTEXT_SPECIFIC) == 0) { SignersSet->AuthAttr.len = Len + (TempP - *Ptr); SignersSet->AuthAttr.p = *Ptr; *Ptr = TempP + Len; } else { SignersSet->AuthAttr.p = NULL; } } if (Ret == 0) { Ret = MbedTlsPkcs7GetDigestAlgorithm (Ptr, EndSet, &SignersSet->SigAlgIdentifier); } if (Ret == 0) { Ret = Pkcs7GetSignature (Ptr, End, &SignersSet->Sig); } if (Ret == 0) { SignersSet->Next = NULL; } return Ret; } /** SignedData ::= SEQUENCE { version Version, digestAlgorithms DigestAlgorithmIdentifiers, contentInfo ContentInfo, certificates [0] IMPLICIT ExtendedCertificatesAndCertificates OPTIONAL, crls [0] IMPLICIT CertificateRevocationLists OPTIONAL, signerInfos SignerInfos }. @param[in] Buffer The start of the buffer. @param[in] BufferLen The len the buffer. @param[out] SignedData MbedtlsPkcs7SignedData. @retval 0 Success. @retval negative A negative MBEDTLS_ERR_ASN1_XXX error code on failure. **/ STATIC INT32 Pkcs7GetSignedData ( UINT8 *Buffer, INTN BufferLen, MbedtlsPkcs7SignedData *SignedData ) { UINT8 *Ptr; UINT8 *End; UINTN Len; INT32 Ret; UINT8 *CertP; UINTN CertLen; UINT8 *OldCertP; UINTN TotalCertLen; mbedtls_x509_crt *MoreCert; UINT8 CertNum; mbedtls_x509_crt *LastCert; mbedtls_x509_crt *TempCrt; Len = 0; Ptr = Buffer; End = Buffer + BufferLen; MoreCert = NULL; Ret = mbedtls_asn1_get_tag ( &Ptr, End, &Len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE ); if (Ret == 0) { // version Ret = MbedTlsPkcs7GetVersion (&Ptr, End, &SignedData->Version); } if ((Ret == 0) && (SignedData->Version != 1)) { Ret = -1; } if (Ret == 0) { // digest algorithm Ret = MbedTlsPkcs7GetDigestAlgorithmSet ( &Ptr, End, &SignedData->DigestAlgorithms ); } if (Ret == 0) { if ( #ifndef DISABLE_SHA1_DEPRECATED_INTERFACES ((SignedData->DigestAlgorithms.len == sizeof (MBEDTLS_OID_DIGEST_ALG_SHA1) - 1) && (CompareMem ( SignedData->DigestAlgorithms.p, MBEDTLS_OID_DIGEST_ALG_SHA1, SignedData->DigestAlgorithms.len ) == 0)) || #endif ((SignedData->DigestAlgorithms.len == sizeof (MBEDTLS_OID_DIGEST_ALG_SHA256) - 1) && (CompareMem ( SignedData->DigestAlgorithms.p, MBEDTLS_OID_DIGEST_ALG_SHA256, SignedData->DigestAlgorithms.len ) == 0)) || ((SignedData->DigestAlgorithms.len == sizeof (MBEDTLS_OID_DIGEST_ALG_SHA384) - 1) && (CompareMem ( SignedData->DigestAlgorithms.p, MBEDTLS_OID_DIGEST_ALG_SHA384, SignedData->DigestAlgorithms.len ) == 0)) || ((SignedData->DigestAlgorithms.len == sizeof (MBEDTLS_OID_DIGEST_ALG_SHA512) - 1) && (CompareMem ( SignedData->DigestAlgorithms.p, MBEDTLS_OID_DIGEST_ALG_SHA512, SignedData->DigestAlgorithms.len ) == 0))) { Ret = 0; } else { Ret = -1; } } if (Ret == 0) { Ret = Pkcs7GetContentInfoType (&Ptr, End, &SignedData->ContentInfo.Oid); } if (Ret == 0) { // move to next Ptr = Ptr + SignedData->ContentInfo.Oid.len; Ret = MbedTlsPkcs7GetNextContentLen (&Ptr, End, &Len); CertP = Ptr + Len; // move to actual cert, if there are more [0] if (MbedTlsPkcs7GetNextContentLen (&CertP, End, &CertLen) == 0) { Len = CertLen; Ptr = CertP; } } // certificates: may have many certs CertP = Ptr; TotalCertLen = 0; MoreCert = &SignedData->Certificates; CertNum = 0; while (TotalCertLen < Len) { OldCertP = CertP; Ret = mbedtls_asn1_get_tag (&CertP, End, &CertLen, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); if (Ret != 0) { goto Out; } // cert total len CertLen = CertLen + (CertP - OldCertP); // move to next cert CertP = OldCertP + CertLen; // change TotalCertLen TotalCertLen += CertLen; mbedtls_x509_crt_init (MoreCert); Ret = MbedTlsPkcs7GetCertificates (&OldCertP, CertLen, MoreCert); if (Ret != 0) { goto Out; } CertNum++; MoreCert->next = mbedtls_calloc (1, sizeof (mbedtls_x509_crt)); MoreCert = MoreCert->next; } if (TotalCertLen != Len) { Ret = -1; goto Out; } LastCert = &(SignedData->Certificates); while (CertNum--) { if (CertNum == 0) { LastCert->next = NULL; break; } else { LastCert = LastCert->next; } } // signers info if (Ret == 0) { Ptr = Ptr + Len; Ret = MbedTlsPkcs7GetSignersInfoSet (&Ptr, End, &SignedData->SignerInfos); } Out: if (Ret == 0) { if (MoreCert != NULL) { mbedtls_x509_crt_free (MoreCert); MoreCert = NULL; } } else { if (SignedData->Certificates.next != NULL) { TempCrt = SignedData->Certificates.next; mbedtls_x509_crt_free (TempCrt); } } return Ret; } /** Parse MbedtlsPkcs7 to Der format. @param[in] Buffer The start of the buffer. @param[in] BufferLen The len the buffer. @param[out] Pkcs7 MbedtlsPkcs7. @retval 0 Success. @retval negative A negative MBEDTLS_ERR_ASN1_XXX error code on failure. **/ STATIC INT32 MbedtlsPkcs7ParseDer ( CONST UINT8 *Buffer, INTN BufferLen, MbedtlsPkcs7 *Pkcs7 ) { UINT8 *Ptr; UINT8 *End; UINTN Len; INT32 Ret; if (Pkcs7 == NULL) { return -1; } Len = 0; Ptr = (UINT8 *)Buffer; End = Ptr + BufferLen; Ret = Pkcs7GetContentInfoType (&Ptr, End, &Pkcs7->ContentTypeOid); if (Ret != 0) { goto Out; } if ((CompareMem (Pkcs7->ContentTypeOid.p, MBEDTLS_OID_PKCS7_DATA, Pkcs7->ContentTypeOid.len) == 0) || (CompareMem (Pkcs7->ContentTypeOid.p, MBEDTLS_OID_PKCS7_ENCRYPTED_DATA, Pkcs7->ContentTypeOid.len) == 0) || (CompareMem (Pkcs7->ContentTypeOid.p, MBEDTLS_OID_PKCS7_ENVELOPED_DATA, Pkcs7->ContentTypeOid.len) == 0) || (CompareMem (Pkcs7->ContentTypeOid.p, MBEDTLS_OID_PKCS7_SIGNED_AND_ENVELOPED_DATA, Pkcs7->ContentTypeOid.len) == 0) || (CompareMem (Pkcs7->ContentTypeOid.p, MBEDTLS_OID_PKCS7_DIGESTED_DATA, Pkcs7->ContentTypeOid.len) == 0)) { // Invalid PKCS7 data type; Ret = -1; goto Out; } if (CompareMem (Pkcs7->ContentTypeOid.p, MBEDTLS_OID_PKCS7_SIGNED_DATA, Pkcs7->ContentTypeOid.len) != 0) { // Invalid PKCS7 data type; Ret = -1; goto Out; } // Content type is SignedData Ptr = Ptr + Pkcs7->ContentTypeOid.len; Ret = MbedTlsPkcs7GetNextContentLen (&Ptr, End, &Len); if (Ret != 0) { goto Out; } Ret = Pkcs7GetSignedData (Ptr, Len, &Pkcs7->SignedData); if (Ret != 0) { goto Out; } Out: return Ret; } /** MbedtlsPkcs7 verify MbedtlsPkcs7SignerInfo. @param[in] SignerInfo MbedtlsPkcs7 SignerInfo. @param[in] Cert cert. @param[in] Data Pointer for data. @param[in] DataLen The len the buffer. @retval 0 Success. @retval negative A negative MBEDTLS_ERR_ASN1_XXX error code on failure. **/ STATIC INT32 MbedtlsPkcs7SignedDataVerifySigners ( MbedtlsPkcs7SignerInfo *SignerInfo, mbedtls_x509_crt *Cert, CONST UINT8 *Data, INTN DataLen ) { INT32 Ret; UINT8 Hash[MBEDTLS_MD_MAX_SIZE]; mbedtls_pk_context Pk; CONST mbedtls_md_info_t *MdInfo; INTN HashLen; UINT8 TempAuthAttr; Pk = Cert->pk; ZeroMem (Hash, MBEDTLS_MD_MAX_SIZE); // all the hash algo #ifndef DISABLE_SHA1_DEPRECATED_INTERFACES MdInfo = mbedtls_md_info_from_type (MBEDTLS_MD_SHA1); HashLen = mbedtls_md_get_size (MdInfo); mbedtls_md (MdInfo, Data, DataLen, Hash); if (SignerInfo->AuthAttr.p != NULL) { TempAuthAttr = *(SignerInfo->AuthAttr.p); *(SignerInfo->AuthAttr.p) = MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SET; mbedtls_md (MdInfo, SignerInfo->AuthAttr.p, SignerInfo->AuthAttr.len, Hash); // Restore content *(SignerInfo->AuthAttr.p) = TempAuthAttr; } Ret = mbedtls_pk_verify (&Pk, MBEDTLS_MD_SHA1, Hash, HashLen, SignerInfo->Sig.p, SignerInfo->Sig.len); if (Ret == 0) { return Ret; } #endif MdInfo = mbedtls_md_info_from_type (MBEDTLS_MD_SHA256); HashLen = mbedtls_md_get_size (MdInfo); ZeroMem (Hash, MBEDTLS_MD_MAX_SIZE); mbedtls_md (MdInfo, Data, DataLen, Hash); if (SignerInfo->AuthAttr.p != NULL) { TempAuthAttr = *(SignerInfo->AuthAttr.p); *(SignerInfo->AuthAttr.p) = MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SET; mbedtls_md (MdInfo, SignerInfo->AuthAttr.p, SignerInfo->AuthAttr.len, Hash); // Restore content *(SignerInfo->AuthAttr.p) = TempAuthAttr; } Ret = mbedtls_pk_verify (&Pk, MBEDTLS_MD_SHA256, Hash, HashLen, SignerInfo->Sig.p, SignerInfo->Sig.len); if (Ret == 0) { return Ret; } MdInfo = mbedtls_md_info_from_type (MBEDTLS_MD_SHA384); HashLen = mbedtls_md_get_size (MdInfo); ZeroMem (Hash, MBEDTLS_MD_MAX_SIZE); mbedtls_md (MdInfo, Data, DataLen, Hash); if (SignerInfo->AuthAttr.p != NULL) { TempAuthAttr = *(SignerInfo->AuthAttr.p); *(SignerInfo->AuthAttr.p) = MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SET; mbedtls_md (MdInfo, SignerInfo->AuthAttr.p, SignerInfo->AuthAttr.len, Hash); // Restore content *(SignerInfo->AuthAttr.p) = TempAuthAttr; } Ret = mbedtls_pk_verify (&Pk, MBEDTLS_MD_SHA384, Hash, HashLen, SignerInfo->Sig.p, SignerInfo->Sig.len); if (Ret == 0) { return Ret; } MdInfo = mbedtls_md_info_from_type (MBEDTLS_MD_SHA512); HashLen = mbedtls_md_get_size (MdInfo); ZeroMem (Hash, MBEDTLS_MD_MAX_SIZE); mbedtls_md (MdInfo, Data, DataLen, Hash); if (SignerInfo->AuthAttr.p != NULL) { TempAuthAttr = *(SignerInfo->AuthAttr.p); *(SignerInfo->AuthAttr.p) = MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SET; mbedtls_md (MdInfo, SignerInfo->AuthAttr.p, SignerInfo->AuthAttr.len, Hash); // Restore content *(SignerInfo->AuthAttr.p) = TempAuthAttr; } Ret = mbedtls_pk_verify (&Pk, MBEDTLS_MD_SHA512, Hash, HashLen, SignerInfo->Sig.p, SignerInfo->Sig.len); if (Ret == 0) { return Ret; } return Ret; } /** Find signer cert in MbedtlsPkcs7SignerInfo. @param[in] SignerInfo MbedtlsPkcs7 SignerInfo. @param[in] Certs MbedtlsPkcs7 SignerInfo certs. @retval cert Signer Cert. **/ STATIC mbedtls_x509_crt * MbedTlsPkcs7FindSignerCert ( MbedtlsPkcs7SignerInfo *SignerInfo, mbedtls_x509_crt *Certs ) { mbedtls_x509_crt *Cert; Cert = Certs; while (Cert != NULL) { if ((Cert->serial.p == NULL) || (Cert->issuer_raw.p == NULL)) { return NULL; } if ((Cert->issuer_raw.len == SignerInfo->IssuerRaw.len) && (CompareMem (Cert->issuer_raw.p, SignerInfo->IssuerRaw.p, Cert->issuer_raw.len) == 0) && (Cert->serial.len == SignerInfo->Serial.len) && (CompareMem (Cert->serial.p, SignerInfo->Serial.p, Cert->serial.len) == 0)) { break; } Cert = Cert->next; } return Cert; } /** verify cert. @param[in] Ca CA cert. @param[in] CaCrl CRL. @param[in] End Cert which need be verified. @retval TRUE Verify successfully. @retval FALSE Verify failed. **/ STATIC BOOLEAN MbedTlsPkcs7VerifyCert ( mbedtls_x509_crt *Ca, mbedtls_x509_crl *CaCrl, mbedtls_x509_crt *End ) { INT32 Ret; UINT32 VFlag; mbedtls_x509_crt_profile Profile; VFlag = 0; CopyMem (&Profile, &gCompatProfile, sizeof (mbedtls_x509_crt_profile)); Ret = mbedtls_x509_crt_verify_with_profile (End, Ca, CaCrl, &Profile, NULL, &VFlag, NULL, NULL); return Ret == 0; } /** verify cert chain. @param[in] Pkcs7 MbedtlsPkcs7. @param[in] Ca CA cert. @param[in] End Cert which need be verified. @retval TRUE Verify successfully. @retval FALSE Verify failed. **/ STATIC BOOLEAN MbedTlsPkcs7VerifyCertChain ( MbedtlsPkcs7 *Pkcs7, mbedtls_x509_crt *Ca, mbedtls_x509_crt *End ) { mbedtls_x509_crt *AllCert; mbedtls_x509_crt *InterCert; AllCert = &(Pkcs7->SignedData.Certificates); InterCert = NULL; while (AllCert != NULL) { if ((AllCert->next == End) && (MbedTlsPkcs7VerifyCert (AllCert, NULL, End))) { InterCert = AllCert; break; } AllCert = AllCert->next; } if (InterCert == NULL) { return FALSE; } if (MbedTlsPkcs7VerifyCert (Ca, &(Pkcs7->SignedData.Crls), InterCert)) { return TRUE; } else { return MbedTlsPkcs7VerifyCertChain (Pkcs7, Ca, InterCert); } } /** MbedTlsPkcs7 Verify SignedData. @param[in] Pkcs7 MbedtlsPkcs7. @param[in] TrustCert CA cert. @param[in] Data Pointer for data. @param[in] DataLen The len the buffer. @retval TRUE Verify successfully. @retval FALSE Verify failed. **/ STATIC BOOLEAN MbedTlsPkcs7SignedDataVerify ( MbedtlsPkcs7 *Pkcs7, mbedtls_x509_crt *TrustCert, CONST UINT8 *Data, INTN DataLen ) { MbedtlsPkcs7SignerInfo *SignerInfo; mbedtls_x509_crt *Cert; mbedtls_x509_crt *AllCert; BOOLEAN Result; SignerInfo = &(Pkcs7->SignedData.SignerInfos); Result = TRUE; // // Traverse signers and verify each signers // while (SignerInfo != NULL) { Result = FALSE; // 1. Find signers cert Cert = MbedTlsPkcs7FindSignerCert (SignerInfo, &(Pkcs7->SignedData.Certificates)); if (Cert != NULL) { // 2. Check signer cert is trusted by trustCert if (MbedTlsPkcs7VerifyCert (TrustCert, &(Pkcs7->SignedData.Crls), Cert)) { // root cert verify pass Result = TRUE; } else { if (MbedTlsPkcs7VerifyCertChain (Pkcs7, TrustCert, Cert)) { Result = TRUE; } else { Result = FALSE; } } if (Result == TRUE) { // 3. Check signed data AllCert = &(Pkcs7->SignedData.Certificates); while (AllCert != NULL) { if (MbedtlsPkcs7SignedDataVerifySigners (SignerInfo, AllCert, Data, DataLen) == 0) { return TRUE; } AllCert = AllCert->next; } Result = FALSE; } } // move to next SignerInfo = SignerInfo->Next; } return Result; } /** Check input P7Data is a wrapped ContentInfo structure or not. If not construct a new structure to wrap P7Data. Caution: This function may receive untrusted input. UEFI Authenticated Variable is external input, so this function will do basic check for PKCS#7 data structure. @param[in] P7Data Pointer to the PKCS#7 message to verify. @param[in] P7Length Length of the PKCS#7 message in bytes. @param[out] WrapFlag If TRUE P7Data is a ContentInfo structure, otherwise return FALSE. @param[out] WrapData If return status of this function is TRUE: 1) when WrapFlag is TRUE, pointer to P7Data. 2) when WrapFlag is FALSE, pointer to a new ContentInfo structure. It's caller's responsibility to free this buffer. @param[out] WrapDataSize Length of ContentInfo structure in bytes. @retval TRUE The operation is finished successfully. @retval FALSE The operation is failed due to lack of resources. **/ BOOLEAN WrapPkcs7Data ( IN CONST UINT8 *P7Data, IN UINTN P7Length, OUT BOOLEAN *WrapFlag, OUT UINT8 **WrapData, OUT UINTN *WrapDataSize ) { BOOLEAN Wrapped; UINT8 *SignedData; // // Check whether input P7Data is a wrapped ContentInfo structure or not. // Wrapped = FALSE; if ((P7Data[4] == MBEDTLS_ASN1_OID) && (P7Data[5] == sizeof (MBEDTLS_OID_PKCS7_SIGNED_DATA) - 1)) { if (CompareMem (P7Data + 6, MBEDTLS_OID_PKCS7_SIGNED_DATA, sizeof (MBEDTLS_OID_PKCS7_SIGNED_DATA) - 1) == 0) { if ((P7Data[15] == (MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_CONTEXT_SPECIFIC)) && (P7Data[16] == 0x82)) { Wrapped = TRUE; } } } if (Wrapped) { *WrapData = (UINT8 *)P7Data; *WrapDataSize = P7Length; } else { // // Wrap PKCS#7 signeddata to a ContentInfo structure - add a header in 19 bytes. // *WrapDataSize = P7Length + 19; *WrapData = AllocateZeroPool (*WrapDataSize); if (*WrapData == NULL) { *WrapFlag = Wrapped; return FALSE; } SignedData = *WrapData; // // Part1: 0x30, 0x82. // SignedData[0] = MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE; SignedData[1] = 0x82; // // Part2: Length1 = P7Length + 19 - 4, in big endian. // SignedData[2] = (UINT8)(((UINT16)(*WrapDataSize - 4)) >> 8); SignedData[3] = (UINT8)(((UINT16)(*WrapDataSize - 4)) & 0xff); // // Part3: 0x06, 0x09. // SignedData[4] = MBEDTLS_ASN1_OID; SignedData[5] = sizeof (MBEDTLS_OID_PKCS7_SIGNED_DATA) - 1; // // Part4: OID value -- 0x2A 0x86 0x48 0x86 0xF7 0x0D 0x01 0x07 0x02. // CopyMem (SignedData + 6, MBEDTLS_OID_PKCS7_SIGNED_DATA, sizeof (MBEDTLS_OID_PKCS7_SIGNED_DATA) - 1); // // Part5: 0xA0, 0x82. // SignedData[15] = MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_CONTEXT_SPECIFIC; SignedData[16] = 0x82; // // Part6: Length2 = P7Length, in big endian. // SignedData[17] = (UINT8)(((UINT16)P7Length) >> 8); SignedData[18] = (UINT8)(((UINT16)P7Length) & 0xff); // // Part7: P7Data. // CopyMem (SignedData + 19, P7Data, P7Length); } *WrapFlag = Wrapped; return TRUE; } /** Verifies the validity of a PKCS#7 signed data as described in "PKCS #7: Cryptographic Message Syntax Standard". The input signed data could be wrapped in a ContentInfo structure. If P7Data, TrustedCert or InData is NULL, then return FALSE. If P7Length, CertLength or DataLength overflow, then return FALSE. If this interface is not supported, then return FALSE. @param[in] P7Data Pointer to the PKCS#7 message to verify. @param[in] P7Length Length of the PKCS#7 message in bytes. @param[in] TrustedCert Pointer to a trusted/root certificate encoded in DER, which is used for certificate chain verification. @param[in] CertLength Length of the trusted certificate in bytes. @param[in] InData Pointer to the content to be verified. @param[in] DataLength Length of InData in bytes. @retval TRUE The specified PKCS#7 signed data is valid. @retval FALSE Invalid PKCS#7 signed data. @retval FALSE This interface is not supported. **/ BOOLEAN EFIAPI Pkcs7Verify ( IN CONST UINT8 *P7Data, IN UINTN P7Length, IN CONST UINT8 *TrustedCert, IN UINTN CertLength, IN CONST UINT8 *InData, IN UINTN DataLength ) { BOOLEAN Status; UINT8 *WrapData; UINTN WrapDataSize; BOOLEAN Wrapped; MbedtlsPkcs7 Pkcs7; INT32 Ret; mbedtls_x509_crt Crt; mbedtls_x509_crt *TempCrt; // // Check input parameters. // if ((P7Data == NULL) || (TrustedCert == NULL) || (InData == NULL) || (P7Length > INT_MAX) || (CertLength > INT_MAX) || (DataLength > INT_MAX)) { return FALSE; } Status = WrapPkcs7Data (P7Data, P7Length, &Wrapped, &WrapData, &WrapDataSize); if (!Status) { return FALSE; } Status = FALSE; MbedTlsPkcs7Init (&Pkcs7); mbedtls_x509_crt_init (&Crt); Ret = MbedtlsPkcs7ParseDer (WrapData, (INT32)WrapDataSize, &Pkcs7); if (Ret != 0) { goto Cleanup; } Ret = mbedtls_x509_crt_parse_der (&Crt, TrustedCert, CertLength); if (Ret != 0) { goto Cleanup; } Status = MbedTlsPkcs7SignedDataVerify (&Pkcs7, &Crt, InData, (INT32)DataLength); Cleanup: if (&Crt != NULL) { mbedtls_x509_crt_free (&Crt); } if (Pkcs7.SignedData.Certificates.next != NULL) { TempCrt = Pkcs7.SignedData.Certificates.next; mbedtls_x509_crt_free (TempCrt); } return Status; } /** Wrap function to use free() to free allocated memory for certificates. @param[in] Certs Pointer to the certificates to be freed. **/ VOID EFIAPI Pkcs7FreeSigners ( IN UINT8 *Certs ) { if (Certs == NULL) { return; } FreePool (Certs); } /** Get the signer's certificates from PKCS#7 signed data as described in "PKCS #7: Cryptographic Message Syntax Standard". The input signed data could be wrapped in a ContentInfo structure. If P7Data, CertStack, StackLength, TrustedCert or CertLength is NULL, then return FALSE. If P7Length overflow, then return FALSE. Caution: This function may receive untrusted input. UEFI Authenticated Variable is external input, so this function will do basic check for PKCS#7 data structure. @param[in] P7Data Pointer to the PKCS#7 message to verify. @param[in] P7Length Length of the PKCS#7 message in bytes. @param[out] CertStack Pointer to Signer's certificates retrieved from P7Data. It's caller's responsibility to free the buffer with Pkcs7FreeSigners(). This data structure is EFI_CERT_STACK type. @param[out] StackLength Length of signer's certificates in bytes. @param[out] TrustedCert Pointer to a trusted certificate from Signer's certificates. It's caller's responsibility to free the buffer with Pkcs7FreeSigners(). @param[out] CertLength Length of the trusted certificate in bytes. @retval TRUE The operation is finished successfully. @retval FALSE Error occurs during the operation. **/ BOOLEAN EFIAPI Pkcs7GetSigners ( IN CONST UINT8 *P7Data, IN UINTN P7Length, OUT UINT8 **CertStack, OUT UINTN *StackLength, OUT UINT8 **TrustedCert, OUT UINTN *CertLength ) { MbedtlsPkcs7SignerInfo *SignerInfo; mbedtls_x509_crt *Cert; MbedtlsPkcs7 Pkcs7; BOOLEAN Status; UINT8 *WrapData; UINTN WrapDataSize; BOOLEAN Wrapped; mbedtls_x509_crt *TempCrt; UINTN CertSize; UINT8 Index; UINT8 *CertBuf; UINT8 *OldBuf; UINTN BufferSize; UINTN OldSize; if ((P7Data == NULL) || (CertStack == NULL) || (StackLength == NULL) || (TrustedCert == NULL) || (CertLength == NULL) || (P7Length > INT_MAX)) { return FALSE; } Status = WrapPkcs7Data (P7Data, P7Length, &Wrapped, &WrapData, &WrapDataSize); if (!Status) { return FALSE; } Status = FALSE; CertBuf = NULL; OldBuf = NULL; Cert = NULL; MbedTlsPkcs7Init (&Pkcs7); if (MbedtlsPkcs7ParseDer (WrapData, (INT32)WrapDataSize, &Pkcs7) != 0) { goto _Exit; } SignerInfo = &(Pkcs7.SignedData.SignerInfos); // // Traverse each signers // // Convert CertStack to buffer in following format: // UINT8 CertNumber; // UINT32 Cert1Length; // UINT8 Cert1[]; // UINT32 Cert2Length; // UINT8 Cert2[]; // ... // UINT32 CertnLength; // UINT8 Certn[]; // BufferSize = sizeof (UINT8); OldSize = BufferSize; Index = 0; while (SignerInfo != NULL) { // Find signers cert Cert = MbedTlsPkcs7FindSignerCert (SignerInfo, &(Pkcs7.SignedData.Certificates)); if (Cert == NULL) { goto _Exit; } CertSize = Cert->raw.len; OldSize = BufferSize; OldBuf = CertBuf; BufferSize = OldSize + CertSize + sizeof (UINT32); CertBuf = AllocateZeroPool (BufferSize); if (CertBuf == NULL) { goto _Exit; } if (OldBuf != NULL) { CopyMem (CertBuf, OldBuf, OldSize); FreePool (OldBuf); OldBuf = NULL; } WriteUnaligned32 ((UINT32 *)(CertBuf + OldSize), (UINT32)CertSize); CopyMem (CertBuf + OldSize + sizeof (UINT32), Cert->raw.p, CertSize); Index++; // move to next SignerInfo = SignerInfo->Next; } if (CertBuf != NULL) { // // Update CertNumber. // CertBuf[0] = Index; *CertLength = BufferSize - OldSize - sizeof (UINT32); *TrustedCert = AllocateZeroPool (*CertLength); if (*TrustedCert == NULL) { goto _Exit; } CopyMem (*TrustedCert, CertBuf + OldSize + sizeof (UINT32), *CertLength); *CertStack = CertBuf; *StackLength = BufferSize; Status = TRUE; } _Exit: // // Release Resources // if (!Status && (CertBuf != NULL)) { FreePool (CertBuf); *CertStack = NULL; } if (Status) { if (Pkcs7.SignedData.Certificates.next != NULL) { TempCrt = Pkcs7.SignedData.Certificates.next; mbedtls_x509_crt_free (TempCrt); } } if (OldBuf != NULL) { FreePool (OldBuf); } return Status; } /** Retrieves all embedded certificates from PKCS#7 signed data as described in "PKCS #7: Cryptographic Message Syntax Standard", and outputs two certificate lists chained and unchained to the signer's certificates. The input signed data could be wrapped in a ContentInfo structure. @param[in] P7Data Pointer to the PKCS#7 message. @param[in] P7Length Length of the PKCS#7 message in bytes. @param[out] SignerChainCerts Pointer to the certificates list chained to signer's certificate. It's caller's responsibility to free the buffer with Pkcs7FreeSigners(). This data structure is EFI_CERT_STACK type. @param[out] ChainLength Length of the chained certificates list buffer in bytes. @param[out] UnchainCerts Pointer to the unchained certificates lists. It's caller's responsibility to free the buffer with Pkcs7FreeSigners(). This data structure is EFI_CERT_STACK type. @param[out] UnchainLength Length of the unchained certificates list buffer in bytes. @retval TRUE The operation is finished successfully. @retval FALSE Error occurs during the operation. **/ BOOLEAN EFIAPI Pkcs7GetCertificatesList ( IN CONST UINT8 *P7Data, IN UINTN P7Length, OUT UINT8 **SignerChainCerts, OUT UINTN *ChainLength, OUT UINT8 **UnchainCerts, OUT UINTN *UnchainLength ) { ASSERT (FALSE); return FALSE; }