/** @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 /// /// Enough to store any signature generated by PKCS7 /// #define MAX_SIGNATURE_SIZE 1024 GLOBAL_REMOVE_IF_UNREFERENCED UINT8 MbedtlsOidDigestAlgSha256[] = MBEDTLS_OID_DIGEST_ALG_SHA256; GLOBAL_REMOVE_IF_UNREFERENCED UINT8 MbedtlsOidPkcs1Rsa[] = MBEDTLS_OID_PKCS1_RSA; /** Write DigestAlgorithmIdentifier. @param[in, out] Ptr The reference to the current position pointer. @param[in] Start The start of the buffer, for bounds-checking. @param[in] DigestType Digest Type @retval number The number of bytes written to p on success. @retval negative A negative MBEDTLS_ERR_ASN1_XXX error code on failure. **/ STATIC INT32 MbedTlsPkcs7WriteDigestAlgorithm ( UINT8 **Ptr, UINT8 *Start, mbedtls_md_type_t DigestType ) { UINT8 *OidPtr; UINTN OidLen; INT32 Ret; Ret = mbedtls_oid_get_oid_by_md (DigestType, (CONST CHAR8 **)&OidPtr, &OidLen); if (Ret == 0) { return mbedtls_asn1_write_oid (Ptr, (CONST UINT8 *)Start, (CONST CHAR8 *)OidPtr, OidLen); } return 0; } /** DigestAlgorithmIdentifiers ::= SET OF DigestAlgorithmIdentifier. @param[in, out] Ptr The reference to the current position pointer. @param[in] Start The start of the buffer, for bounds-checking. @param[in] DigestTypes Digest Type array. @param[in] Count The index for Digest Type. @retval number The number of bytes written to p on success. @retval negative A negative MBEDTLS_ERR_ASN1_XXX error code on failure. **/ STATIC INT32 MbedTlsPkcs7WriteDigestAlgorithmSet ( UINT8 **Ptr, UINT8 *Start, mbedtls_md_type_t *DigestTypes, INTN Count ) { INTN Idx; INT32 Len; INT32 Ret; Len = 0; EDKII_ASN1_CHK_ADD (Len, mbedtls_asn1_write_null (Ptr, Start)); for (Idx = 0; Idx < Count; Idx++) { EDKII_ASN1_CHK_ADD ( Len, MbedTlsPkcs7WriteDigestAlgorithm (Ptr, Start, DigestTypes[Idx]) ); } EDKII_ASN1_CHK_ADD (Len, mbedtls_asn1_write_len (Ptr, Start, (UINTN)Len)); EDKII_ASN1_CHK_ADD ( Len, mbedtls_asn1_write_tag ( Ptr, Start, (MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE) ) ); EDKII_ASN1_CHK_ADD (Len, mbedtls_asn1_write_len (Ptr, Start, (UINTN)Len)); EDKII_ASN1_CHK_ADD ( Len, mbedtls_asn1_write_tag ( Ptr, Start, (MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SET) ) ); return Len; } /** ContentInfo ::= SEQUENCE { contentType ContentType, content [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL }. @param[in, out] Ptr The reference to the current position pointer. @param[in] Start The start of the buffer, for bounds-checking. @param[in] Content ContentInfo. @param[in] ContentLen Size of ContentInfo. @retval number The number of bytes written to p on success. @retval negative A negative MBEDTLS_ERR_ASN1_XXX error code on failure. **/ STATIC INT32 MbedTlsPkcs7WriteContentInfo ( UINT8 **Ptr, UINT8 *Start, UINT8 *Content, INTN ContentLen ) { INT32 Ret; INT32 Len; Len = 0; if (Content != NULL) { EDKII_ASN1_CHK_ADD (Len, mbedtls_asn1_write_octet_string (Ptr, Start, Content, ContentLen)); } EDKII_ASN1_CHK_ADD ( Len, mbedtls_asn1_write_oid ( Ptr, Start, MBEDTLS_OID_PKCS7_DATA, sizeof (MBEDTLS_OID_PKCS7_DATA) - 1 ) ); EDKII_ASN1_CHK_ADD (Len, mbedtls_asn1_write_len (Ptr, Start, Len)); EDKII_ASN1_CHK_ADD (Len, mbedtls_asn1_write_tag (Ptr, Start, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)); return Len; } /** certificates :: SET OF ExtendedCertificateOrCertificate, ExtendedCertificateOrCertificate ::= CHOICE { certificate Certificate -- x509, extendedCertificate[0] IMPLICIT ExtendedCertificate }. @param[in, out] Ptr The reference to the current position pointer. @param[in] Start The start of the buffer, for bounds-checking. @param[in] Cert Certificate. @param[in] OtherCerts Ohter Certificate. @retval number The number of bytes written to p on success. @retval negative A negative MBEDTLS_ERR_ASN1_XXX error code on failure. **/ STATIC INT32 MbedTlsPkcs7WriteCertificates ( UINT8 **Ptr, UINT8 *Start, mbedtls_x509_crt *Cert, mbedtls_x509_crt *OtherCerts ) { INT32 Ret; INT32 Len; mbedtls_x509_crt *TmpCert; Len = 0; /// Write OtherCerts TmpCert = OtherCerts; while (TmpCert != NULL) { EDKII_ASN1_CHK_ADD (Len, mbedtls_asn1_write_raw_buffer (Ptr, Start, TmpCert->raw.p, TmpCert->raw.len)); TmpCert = TmpCert->next; } /// Write Cert EDKII_ASN1_CHK_ADD (Len, mbedtls_asn1_write_raw_buffer (Ptr, Start, Cert->raw.p, Cert->raw.len)); /// Write NextContext EDKII_ASN1_CHK_ADD (Len, mbedtls_asn1_write_len (Ptr, Start, Len)); EDKII_ASN1_CHK_ADD (Len, mbedtls_asn1_write_tag (Ptr, Start, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_CONTEXT_SPECIFIC)); return Len; } /** write Pkcs7 Int. @param[in, out] Ptr The reference to the current position pointer. @param[in] Start The start of the buffer, for bounds-checking. @param[in] SerialRaw SerialRaw. @param[in] SerialRawLen Size of SerialRaw. @retval number The number of bytes written to p on success. @retval negative A negative MBEDTLS_ERR_ASN1_XXX error code on failure. **/ STATIC INT32 MbedTlsPkcs7WriteInt ( UINT8 **Ptr, UINT8 *Start, UINT8 *SerialRaw, INTN SerialRawLen ) { INT32 Ret; UINT8 *Pt; INT32 Len; Len = 0; Pt = SerialRaw + SerialRawLen; while (Pt > SerialRaw) { *--(*Ptr) = *--Pt; Len++; } EDKII_ASN1_CHK_ADD (Len, mbedtls_asn1_write_len (Ptr, Start, Len)); EDKII_ASN1_CHK_ADD (Len, mbedtls_asn1_write_tag (Ptr, Start, MBEDTLS_ASN1_INTEGER)); return Len; } /** write Pkcs7 Issuer And SerialNumber. @param[in, out] Ptr The reference to the current position pointer. @param[in] Start The start of the buffer, for bounds-checking. @param[in] Serial Serial. @param[in] SerialLen Size of Serial. @param[in] IssuerRaw IssuerRawLen. @param[in] IssuerRawLen Size of IssuerRawLen. @retval number The number of bytes written to p on success. @retval negative A negative MBEDTLS_ERR_ASN1_XXX error code on failure. **/ STATIC INT32 MbedTlsPkcs7WriteIssuerAndSerialNumber ( UINT8 **Ptr, UINT8 *Start, UINT8 *Serial, INTN SerialLen, UINT8 *IssuerRaw, INTN IssuerRawLen ) { INT32 Ret; INT32 Len; Len = 0; EDKII_ASN1_CHK_ADD (Len, MbedTlsPkcs7WriteInt (Ptr, Start, Serial, SerialLen)); EDKII_ASN1_CHK_ADD (Len, mbedtls_asn1_write_raw_buffer (Ptr, Start, IssuerRaw, IssuerRawLen)); EDKII_ASN1_CHK_ADD (Len, mbedtls_asn1_write_len (Ptr, Start, Len)); EDKII_ASN1_CHK_ADD (Len, mbedtls_asn1_write_tag (Ptr, Start, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)); return Len; } /** SignerInfo ::= SEQUENCE { version Version; issuerAndSerialNumber IssuerAndSerialNumber, digestAlgorithm DigestAlgorithmIdentifier, authenticatedAttributes [0] IMPLICIT Attributes OPTIONAL, digestEncryptionAlgorithm DigestEncryptionAlgorithmIdentifier, encryptedDigest EncryptedDigest, unauthenticatedAttributes [1] IMPLICIT Attributes OPTIONAL. @param[in, out] Ptr The reference to the current position pointer. @param[in] Start The start of the buffer, for bounds-checking. @param[in] SignerInfo SignerInfo. @retval number The number of bytes written to p on success. @retval negative A negative MBEDTLS_ERR_ASN1_XXX error code on failure. **/ STATIC INT32 MbedTlsPkcs7WriteSignerInfo ( UINT8 **Ptr, UINT8 *Start, MbedtlsPkcs7SignerInfo *SignerInfo ) { INT32 Ret; INT32 Len; Len = 0; EDKII_ASN1_CHK_ADD (Len, mbedtls_asn1_write_octet_string (Ptr, Start, SignerInfo->Sig.p, SignerInfo->Sig.len)); EDKII_ASN1_CHK_ADD (Len, mbedtls_asn1_write_algorithm_identifier (Ptr, Start, (CONST CHAR8 *)SignerInfo->SigAlgIdentifier.p, SignerInfo->SigAlgIdentifier.len, 0)); EDKII_ASN1_CHK_ADD (Len, mbedtls_asn1_write_algorithm_identifier (Ptr, Start, (CONST CHAR8 *)SignerInfo->AlgIdentifier.p, SignerInfo->AlgIdentifier.len, 0)); EDKII_ASN1_CHK_ADD (Len, MbedTlsPkcs7WriteIssuerAndSerialNumber (Ptr, Start, SignerInfo->Serial.p, SignerInfo->Serial.len, SignerInfo->IssuerRaw.p, SignerInfo->IssuerRaw.len)); EDKII_ASN1_CHK_ADD (Len, mbedtls_asn1_write_int (Ptr, Start, SignerInfo->Version)); EDKII_ASN1_CHK_ADD (Len, mbedtls_asn1_write_len (Ptr, Start, Len)); EDKII_ASN1_CHK_ADD (Len, mbedtls_asn1_write_tag (Ptr, Start, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)); return Len; } /** write Pkcs7 Signers Info Set. @param[in, out] Ptr The reference to the current position pointer. @param[in] Start The start of the buffer, for bounds-checking. @param[in] SignersSet SignerInfo Set. @retval number The number of bytes written to p on success. @retval negative A negative MBEDTLS_ERR_ASN1_XXX error code on failure. **/ STATIC INT32 MbedTlsPkcs7WriteSignersInfoSet ( UINT8 **Ptr, UINT8 *Start, MbedtlsPkcs7SignerInfo *SignersSet ) { MbedtlsPkcs7SignerInfo *SignerInfo; INT32 Ret; INT32 Len; SignerInfo = SignersSet; Len = 0; while (SignerInfo != NULL) { EDKII_ASN1_CHK_ADD (Len, MbedTlsPkcs7WriteSignerInfo (Ptr, Start, SignerInfo)); // move to next SignerInfo = SignerInfo->Next; } EDKII_ASN1_CHK_ADD (Len, mbedtls_asn1_write_len (Ptr, Start, Len)); EDKII_ASN1_CHK_ADD (Len, mbedtls_asn1_write_tag (Ptr, Start, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SET)); return Len; } /** Signed Data Type SignedData ::= SEQUENCE { version Version, digestAlgorithms DigestAlgorithmIdentifiers, contentInfo ContentInfo, certificates [0] IMPLICIT ExtendedCertificatesAndCertificates OPTIONAL, crls [1] IMPLICIT CertificateRevocationLists OPTIONAL, signerInfos SignerInfos } DigestAlgorithmIdentifiers ::= SET OF DigestAlgorithmIdentifier SignerInfos ::= SET OF SignerInfo. @param[in, out] Ptr The reference to the current position pointer. @param[in] Start The start of the buffer, for bounds-checking. @param[in] Pkcs7 MbedtlsPkcs7 @retval number The number of bytes written to p on success. @retval negative A negative MBEDTLS_ERR_ASN1_XXX error code on failure. **/ STATIC INT32 MbedTlsPkcs7WriteDer ( UINT8 **Ptr, UINT8 *Start, MbedtlsPkcs7 *Pkcs7 ) { INT32 Ret; INT32 Len; mbedtls_md_type_t DigestAlg[1]; DigestAlg[0] = MBEDTLS_MD_SHA256; Len = 0; EDKII_ASN1_CHK_ADD (Len, MbedTlsPkcs7WriteSignersInfoSet (Ptr, Start, &(Pkcs7->SignedData.SignerInfos))); EDKII_ASN1_CHK_ADD (Len, MbedTlsPkcs7WriteCertificates (Ptr, Start, &(Pkcs7->SignedData.Certificates), Pkcs7->SignedData.Certificates.next)); EDKII_ASN1_CHK_ADD (Len, MbedTlsPkcs7WriteContentInfo (Ptr, Start, NULL, 0)); EDKII_ASN1_CHK_ADD (Len, MbedTlsPkcs7WriteDigestAlgorithmSet (Ptr, Start, DigestAlg, 1)); EDKII_ASN1_CHK_ADD (Len, mbedtls_asn1_write_int (Ptr, Start, Pkcs7->SignedData.Version)); EDKII_ASN1_CHK_ADD (Len, mbedtls_asn1_write_len (Ptr, Start, Len)); EDKII_ASN1_CHK_ADD (Len, mbedtls_asn1_write_tag (Ptr, Start, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)); return Len; } /** Creates a PKCS#7 signedData as described in "PKCS #7: Cryptographic Message Syntax Standard, version 1.5". This interface is only intended to be used for application to perform PKCS#7 functionality validation. If this interface is not supported, then return FALSE. @param[in] PrivateKey Pointer to the PEM-formatted private key data for data signing. @param[in] PrivateKeySize Size of the PEM private key data in bytes. @param[in] KeyPassword NULL-terminated passphrase used for encrypted PEM key data. @param[in] InData Pointer to the content to be signed. @param[in] InDataSize Size of InData in bytes. @param[in] SignCert Pointer to signer's DER-encoded certificate to sign with. @param[in] OtherCerts Pointer to an optional additional set of certificates to include in the PKCS#7 signedData (e.g. any intermediate CAs in the chain). @param[out] SignedData Pointer to output PKCS#7 signedData. It's caller's responsibility to free the buffer with FreePool(). @param[out] SignedDataSize Size of SignedData in bytes. @retval TRUE PKCS#7 data signing succeeded. @retval FALSE PKCS#7 data signing failed. @retval FALSE This interface is not supported. **/ BOOLEAN EFIAPI Pkcs7Sign ( IN CONST UINT8 *PrivateKey, IN UINTN PrivateKeySize, IN CONST UINT8 *KeyPassword, IN UINT8 *InData, IN UINTN InDataSize, IN UINT8 *SignCert, IN UINT8 *OtherCerts OPTIONAL, OUT UINT8 **SignedData, OUT UINTN *SignedDataSize ) { BOOLEAN Status; INT32 Ret; mbedtls_pk_context Pkey; UINT8 HashValue[SHA256_DIGEST_SIZE]; UINT8 Signature[MAX_SIGNATURE_SIZE]; UINTN SignatureLen; UINT8 *NewPrivateKey; mbedtls_x509_crt *Crt; MbedtlsPkcs7 Pkcs7; MbedtlsPkcs7SignerInfo SignerInfo; UINT8 *Buffer; INTN BufferSize; UINT8 *Ptr; INT32 Len; // // Check input parameters. // if ((PrivateKey == NULL) || (KeyPassword == NULL) || (InData == NULL) || (SignCert == NULL) || (SignedData == NULL) || (SignedDataSize == NULL) || (InDataSize > INT_MAX)) { return FALSE; } Buffer = NULL; BufferSize = 4096; SignatureLen = MAX_SIGNATURE_SIZE; Crt = (mbedtls_x509_crt *)SignCert; NewPrivateKey = NULL; if (PrivateKey[PrivateKeySize - 1] != 0) { NewPrivateKey = AllocateZeroPool (PrivateKeySize + 1); if (NewPrivateKey == NULL) { return FALSE; } CopyMem (NewPrivateKey, PrivateKey, PrivateKeySize); NewPrivateKey[PrivateKeySize] = 0; PrivateKeySize++; } else { NewPrivateKey = AllocateZeroPool (PrivateKeySize); if (NewPrivateKey == NULL) { return FALSE; } CopyMem (NewPrivateKey, PrivateKey, PrivateKeySize); } mbedtls_pk_init (&Pkey); Ret = mbedtls_pk_parse_key ( &Pkey, NewPrivateKey, PrivateKeySize, KeyPassword, KeyPassword == NULL ? 0 : AsciiStrLen ((CONST CHAR8 *)KeyPassword), NULL, NULL ); if (Ret != 0) { Status = FALSE; goto Cleanup; } /// Calculate InData Digest ZeroMem (HashValue, SHA256_DIGEST_SIZE); Status = Sha256HashAll (InData, InDataSize, HashValue); if (!Status) { goto Cleanup; } /// Pk Sign ZeroMem (Signature, MAX_SIGNATURE_SIZE); Ret = mbedtls_pk_sign ( &Pkey, MBEDTLS_MD_SHA256, HashValue, SHA256_DIGEST_SIZE, Signature, MAX_SIGNATURE_SIZE, &SignatureLen, MbedtlsRand, NULL ); if (Ret != 0) { Status = FALSE; goto Cleanup; } ZeroMem (&Pkcs7, sizeof (MbedtlsPkcs7)); Pkcs7.SignedData.Version = 1; Crt->next = (mbedtls_x509_crt *)OtherCerts; Pkcs7.SignedData.Certificates = *Crt; SignerInfo.Next = NULL; SignerInfo.Sig.p = Signature; SignerInfo.Sig.len = SignatureLen; SignerInfo.Version = 1; SignerInfo.AlgIdentifier.p = MbedtlsOidDigestAlgSha256; SignerInfo.AlgIdentifier.len = sizeof (MBEDTLS_OID_DIGEST_ALG_SHA256) - 1; if (mbedtls_pk_get_type (&Pkey) == MBEDTLS_PK_RSA) { SignerInfo.SigAlgIdentifier.p = MbedtlsOidPkcs1Rsa; SignerInfo.SigAlgIdentifier.len = sizeof (MBEDTLS_OID_PKCS1_RSA) - 1; } else { Ret = mbedtls_oid_get_oid_by_sig_alg (MBEDTLS_PK_ECDSA, MBEDTLS_MD_SHA256, (CONST CHAR8 **)&SignerInfo.SigAlgIdentifier.p, &SignerInfo.SigAlgIdentifier.len); if (Ret != 0) { Status = FALSE; goto Cleanup; } } SignerInfo.Serial = ((mbedtls_x509_crt *)SignCert)->serial; SignerInfo.IssuerRaw = ((mbedtls_x509_crt *)SignCert)->issuer_raw; Pkcs7.SignedData.SignerInfos = SignerInfo; Buffer = AllocateZeroPool (BufferSize); if (Buffer == NULL) { Status = FALSE; goto Cleanup; } Ptr = Buffer + BufferSize; Len = MbedTlsPkcs7WriteDer (&Ptr, Buffer, &Pkcs7); /// Enlarge buffer if buffer is too small while (Len == MBEDTLS_ERR_ASN1_BUF_TOO_SMALL) { BufferSize = BufferSize * 2; Ptr = Buffer + BufferSize; FreePool (Buffer); Buffer = AllocateZeroPool (BufferSize); if (Buffer == NULL) { Status = FALSE; goto Cleanup; } Ptr = Buffer + BufferSize; Len = MbedTlsPkcs7WriteDer (&Ptr, Buffer, &Pkcs7); } if (Len <= 0) { Status = FALSE; goto Cleanup; } *SignedData = AllocateZeroPool (Len); if (*SignedData == NULL) { Status = FALSE; goto Cleanup; } *SignedDataSize = Len; CopyMem (*SignedData, Ptr, Len); Status = TRUE; Cleanup: if (&Pkey != NULL) { mbedtls_pk_free (&Pkey); } if (NewPrivateKey != NULL) { memset (NewPrivateKey, 0, PrivateKeySize); FreePool (NewPrivateKey); } if (Buffer != NULL) { memset (Buffer, 0, BufferSize); FreePool (Buffer); } return Status; }