/** @file PKCS7 Decryption implementation over mbedtls Mbed-TLS does not provide a PKCS#7 envelopedData decrypt API (its pkcs7 module only handles signedData), so, like the matching Pkcs7Encrypt, this file parses the ASN.1/DER by hand. It is the parsing mirror of CryptPkcs7Encrypt.c and is able to decrypt envelopes produced by that writer as well as standards-conformant envelopes produced by other implementations (e.g. OpenSSL), covering both RSAES-PKCS1-v1.5 and RSAES-OAEP (with any digest the mbedtls configuration supports, where the label digest and MGF1 use the same hash) key transport and AES-CBC content encryption. Copyright (c) 2026, Microsoft Corporation. All rights reserved. SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include "CryptPkcs7Internal.h" #include #include #include #include // // Largest supported content-encryption key (AES-256). // #define MAX_CONTENT_ENC_KEY_LEN 32 /** Read an OBJECT IDENTIFIER at *P into an ASN.1 buffer, advancing *P past it. @param[in,out] P Current read position. @param[in] End End of the buffer. @param[out] Oid Receives the OID contents. @retval TRUE Success. @retval FALSE The element at *P is not a well-formed OBJECT IDENTIFIER. **/ STATIC BOOLEAN Pkcs7Asn1GetOid ( UINT8 **P, CONST UINT8 *End, mbedtls_asn1_buf *Oid ) { size_t Len; if (mbedtls_asn1_get_tag (P, End, &Len, MBEDTLS_ASN1_OID) != 0) { return FALSE; } Oid->tag = MBEDTLS_ASN1_OID; Oid->len = Len; Oid->p = *P; *P += Len; return TRUE; } /** Compare an OID against a reference OID. This is the equivalent of the MBEDTLS_OID_CMP macro, expressed with CompareMem so that this file does not depend on the C library memcmp. @param[in] Oid OID to test. @param[in] Ref Reference OID contents. @param[in] RefLen Length of Ref. @retval TRUE The OIDs match. @retval FALSE The OIDs differ. **/ STATIC BOOLEAN Pkcs7OidEquals ( CONST mbedtls_asn1_buf *Oid, CONST CHAR8 *Ref, UINTN RefLen ) { return (BOOLEAN)((Oid->len == RefLen) && (CompareMem (Oid->p, Ref, RefLen) == 0)); } /** Read a DigestAlgorithmIdentifier SEQUENCE { OID, optional NULL parameters } at *P and map its OID to an mbedtls message-digest type, advancing *P past the SEQUENCE. @param[in,out] P Current read position. @param[in] End End of the buffer. @param[out] MdType Receives the mbedtls digest type. @retval TRUE Parsed successfully and the digest is supported. @retval FALSE Parse error or unsupported digest. **/ STATIC BOOLEAN Pkcs7ReadDigestAlgId ( UINT8 **P, CONST UINT8 *End, mbedtls_md_type_t *MdType ) { mbedtls_asn1_buf Alg; if (mbedtls_asn1_get_alg_null (P, End, &Alg) != 0) { return FALSE; } return (BOOLEAN)(mbedtls_oid_get_md_alg (&Alg, MdType) == 0); } /** Parse RSAES-OAEP-params to determine the hash used for the OAEP label digest and for MGF1. Absent or DEFAULT fields imply SHA-1. Because mbedtls configures a single hash for both the OAEP digest and MGF1, this fails if the two differ. RSAES-OAEP-params ::= SEQUENCE { hashAlgorithm [0] AlgorithmIdentifier DEFAULT sha1, maskGenAlgorithm [1] AlgorithmIdentifier DEFAULT mgf1SHA1, pSourceAlgorithm [2] AlgorithmIdentifier DEFAULT pSpecifiedEmpty } @param[in] Params The keyEncryptionAlgorithm parameters, as returned by mbedtls_asn1_get_alg(). A zeroized (absent) or NULL parameter selects all DEFAULTs. @param[out] MdType Receives the hash to pass to mbedtls_rsa_set_padding. @retval TRUE Parameters parsed and supported. @retval FALSE Parse error or unsupported parameters. **/ STATIC BOOLEAN Pkcs7ParseOaepHash ( CONST mbedtls_asn1_buf *Params, mbedtls_md_type_t *MdType ) { mbedtls_md_type_t HashMd; mbedtls_md_type_t MgfMd; UINT8 *P; UINT8 *SeqEnd; size_t Len; // // Absent parameters (or an explicit NULL) imply all DEFAULTs (SHA-1). // if ((Params->tag == 0) || (Params->tag == MBEDTLS_ASN1_NULL)) { *MdType = MBEDTLS_MD_SHA1; return TRUE; } if (Params->tag != (MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) { return FALSE; } HashMd = MBEDTLS_MD_SHA1; MgfMd = MBEDTLS_MD_SHA1; P = Params->p; SeqEnd = Params->p + Params->len; // // hashAlgorithm [0] EXPLICIT AlgorithmIdentifier // if ((P < SeqEnd) && (*P == (MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_CONTEXT_SPECIFIC | 0))) { UINT8 *FieldEnd; if (mbedtls_asn1_get_tag (&P, SeqEnd, &Len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_CONTEXT_SPECIFIC | 0) != 0) { return FALSE; } FieldEnd = P + Len; if (!Pkcs7ReadDigestAlgId (&P, FieldEnd, &HashMd)) { return FALSE; } P = FieldEnd; } // // maskGenAlgorithm [1] EXPLICIT AlgorithmIdentifier { id-mgf1, HashAlgorithm } // if ((P < SeqEnd) && (*P == (MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_CONTEXT_SPECIFIC | 1))) { UINT8 *FieldEnd; UINT8 *MgfEnd; mbedtls_asn1_buf MgfOid; if (mbedtls_asn1_get_tag (&P, SeqEnd, &Len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_CONTEXT_SPECIFIC | 1) != 0) { return FALSE; } FieldEnd = P + Len; if (mbedtls_asn1_get_tag (&P, FieldEnd, &Len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE) != 0) { return FALSE; } MgfEnd = P + Len; if (!Pkcs7Asn1GetOid (&P, MgfEnd, &MgfOid) || !Pkcs7OidEquals (&MgfOid, MBEDTLS_OID_MGF1, MBEDTLS_OID_SIZE (MBEDTLS_OID_MGF1))) { return FALSE; // Only MGF1 is supported. } // // The MGF1 parameter is itself a HashAlgorithm AlgorithmIdentifier. // if (!Pkcs7ReadDigestAlgId (&P, MgfEnd, &MgfMd)) { return FALSE; } P = FieldEnd; } // // pSourceAlgorithm [2] is ignored: mbedtls uses an empty label, which is the // DEFAULT (pSpecifiedEmpty). A non-empty label would simply fail to decrypt. // if (HashMd != MgfMd) { return FALSE; // mbedtls uses one hash for both the OAEP digest and MGF1. } *MdType = HashMd; return TRUE; } /** AES-CBC-decrypt EncData (with PKCS#7 padding) using the content-encryption key Cek and initialization vector Iv into a freshly allocated buffer. @param[in] CipherInfo mbedtls cipher description to use. @param[in] Cek Content-encryption key. @param[in] CekLen Length of Cek in bytes. @param[in] Iv Initialization vector. @param[in] IvLen Length of Iv in bytes. @param[in] EncData Encrypted content. @param[in] EncDataLen Length of EncData in bytes. @param[out] OutData Receives the allocated plaintext buffer. @param[out] OutDataSize Receives the plaintext length in bytes. @retval TRUE Decryption succeeded. @retval FALSE Decryption failed (wrong key, bad padding, allocation failure). **/ STATIC BOOLEAN Pkcs7AesCbcDecryptContent ( mbedtls_cipher_info_t const *CipherInfo, CONST UINT8 *Cek, UINTN CekLen, CONST UINT8 *Iv, UINTN IvLen, CONST UINT8 *EncData, UINTN EncDataLen, UINT8 **OutData, UINTN *OutDataSize ) { BOOLEAN Succeeded; mbedtls_cipher_context_t CipherCtx; UINT8 *Plain; size_t Olen; Succeeded = FALSE; Plain = NULL; Olen = 0; mbedtls_cipher_init (&CipherCtx); if (EncDataLen == 0) { goto Cleanup; } // // The IV must be exactly one cipher block. mbedtls copies a full block from the // IV pointer regardless of the length passed to mbedtls_cipher_crypt, so an IV // shorter than the block size would read past the supplied bytes; reject any // size mismatch here. // if (IvLen != mbedtls_cipher_info_get_iv_size (CipherInfo)) { goto Cleanup; } if (mbedtls_cipher_setup (&CipherCtx, CipherInfo) != 0) { goto Cleanup; } if (mbedtls_cipher_setkey (&CipherCtx, Cek, (int)(CekLen * 8), MBEDTLS_DECRYPT) != 0) { goto Cleanup; } if (mbedtls_cipher_set_padding_mode (&CipherCtx, MBEDTLS_PADDING_PKCS7) != 0) { goto Cleanup; } // // The decrypted output (after padding removal) is never larger than the input. // Plain = AllocatePool (EncDataLen); if (Plain == NULL) { goto Cleanup; } if (mbedtls_cipher_crypt (&CipherCtx, Iv, IvLen, EncData, EncDataLen, Plain, &Olen) != 0) { // // Bad padding etc. -- the recipient/key did not match. // goto Cleanup; } *OutData = Plain; *OutDataSize = (UINTN)Olen; Plain = NULL; Succeeded = TRUE; Cleanup: if (Plain != NULL) { FreePool (Plain); } mbedtls_cipher_free (&CipherCtx); return Succeeded; } /** Decrypts a DER-encoded PKCS#7 ContentInfo containing an envelopedData structure (such as one produced by Pkcs7Encrypt) and recovers the original content. The private key is supplied in PEM form so that this interface is not tied to any particular key algorithm (currently supports RSA). The correct recipient within the envelopedData is identified automatically. The envelopedData carries, for each recipient, an issuer name and serial number that identify the recipient certificate. If RecipientCert is supplied, its issuer name and serial number are used to select the matching recipient. If RecipientCert is NULL, the supplied private key is tried against every recipient in the envelope. If this interface is not supported, return FALSE. @param[in] PemData Pointer to the PEM-encoded private key of a recipient of the message. May be an RSA or other supported key type. @param[in] PemSize Size of the PEM key data in bytes. @param[in] Password [Optional] NULL-terminated passphrase used to decrypt an encrypted PEM key. Pass NULL if the PEM key is not password protected. @param[in] RecipientCert [Optional] Pointer to the DER-encoded X.509 certificate of the recipient whose private key is supplied in PemData. If provided, it is used to select the matching recipient in the envelope by issuer name and serial number. Pass NULL to try the private key against every recipient. @param[in] RecipientCertSize Size of the DER-encoded certificate in bytes. Ignored (and may be 0) when RecipientCert is NULL. @param[in] ContentInfo Pointer to the PKCS#7 DER-encoded ContentInfo that wraps an envelopedData to be decrypted. @param[in] ContentInfoSize Size of the ContentInfo in bytes. @param[in] Flags Flags for the decryption operation. Currently only CRYPTO_PKCS7_DEFAULT is supported, which indicates that the decrypted content is treated as binary data. @param[out] OutData Receives a pointer to the newly allocated buffer containing the decrypted content. The caller must free the returned buffer with FreePool(). @param[out] OutDataSize Receives the size of the decrypted content in bytes. @retval TRUE PKCS#7 data decryption succeeded. @retval FALSE PKCS#7 data decryption failed. @retval FALSE This interface is not supported. **/ BOOLEAN EFIAPI Pkcs7Decrypt ( IN CONST UINT8 *PemData, IN UINTN PemSize, IN CONST CHAR8 *Password OPTIONAL, IN CONST UINT8 *RecipientCert OPTIONAL, IN UINTN RecipientCertSize OPTIONAL, IN CONST UINT8 *ContentInfo, IN UINTN ContentInfoSize, IN UINT32 Flags, OUT UINT8 **OutData, OUT UINTN *OutDataSize ) { BOOLEAN Succeeded; VOID *Rsa; mbedtls_x509_crt Cert; UINT8 *ReturnData; UINTN ReturnSize; UINT8 *P; UINT8 *End; size_t Len; // // Parsed EncryptedContentInfo. // mbedtls_cipher_info_t const *CipherInfo; UINTN ContentKeyLen; UINT8 *Iv; UINTN IvLen; UINT8 *EncContent; UINTN EncContentLen; // // Bounds of the recipientInfos SET OF contents. // UINT8 *RiCur; UINT8 *RiEnd; Succeeded = FALSE; Rsa = NULL; ReturnData = NULL; ReturnSize = 0; CipherInfo = NULL; ContentKeyLen = 0; Iv = NULL; IvLen = 0; EncContent = NULL; EncContentLen = 0; RiCur = NULL; RiEnd = NULL; mbedtls_x509_crt_init (&Cert); // // Validate parameters (mirrors the OpenSSL instance). // if ((PemData == NULL) || (PemSize == 0) || (PemSize > INT_MAX) || (ContentInfo == NULL) || (ContentInfoSize == 0) || (ContentInfoSize > INT_MAX) || (Flags != CRYPTO_PKCS7_DEFAULT) || (OutData == NULL) || (OutDataSize == NULL)) { goto Done; } if ((RecipientCert != NULL) && ((RecipientCertSize == 0) || (RecipientCertSize > INT_MAX))) { goto Done; } // // Retrieve the recipient's private key from the PEM data. // if (!RsaGetPrivateKeyFromPem (PemData, PemSize, Password, &Rsa)) { goto Done; // Invalid PEM key data or incorrect password. } // // Optionally parse the recipient certificate for issuer/serial recipient selection. // if (RecipientCert != NULL) { if (mbedtls_x509_crt_parse_der (&Cert, RecipientCert, RecipientCertSize) != 0) { goto Done; } } // // Parse the outer ContentInfo: // ContentInfo ::= SEQUENCE { contentType OID(envelopedData), content [0] EXPLICIT EnvelopedData } // P = (UINT8 *)ContentInfo; End = (UINT8 *)ContentInfo + ContentInfoSize; if (mbedtls_asn1_get_tag (&P, End, &Len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE) != 0) { goto Done; } End = P + Len; // Restrict to the ContentInfo contents. { mbedtls_asn1_buf Oid; if (!Pkcs7Asn1GetOid (&P, End, &Oid) || !Pkcs7OidEquals (&Oid, MBEDTLS_OID_PKCS7_ENVELOPED_DATA, MBEDTLS_OID_SIZE (MBEDTLS_OID_PKCS7_ENVELOPED_DATA))) { goto Done; // Not an envelopedData. } } // // content [0] EXPLICIT // if (mbedtls_asn1_get_tag (&P, End, &Len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_CONTEXT_SPECIFIC | 0) != 0) { goto Done; } End = P + Len; // // EnvelopedData ::= SEQUENCE { // version, [0] originatorInfo OPTIONAL, recipientInfos SET, encryptedContentInfo, ... } // if (mbedtls_asn1_get_tag (&P, End, &Len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE) != 0) { goto Done; } End = P + Len; { int Version; if (mbedtls_asn1_get_int (&P, End, &Version) != 0) { goto Done; } } // // Skip optional originatorInfo [0] IMPLICIT. // if ((P < End) && (*P == (MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_CONTEXT_SPECIFIC | 0))) { if (mbedtls_asn1_get_tag (&P, End, &Len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_CONTEXT_SPECIFIC | 0) != 0) { goto Done; } P += Len; } // // recipientInfos SET OF RecipientInfo -- record its bounds and skip past it. // if (mbedtls_asn1_get_tag (&P, End, &Len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SET) != 0) { goto Done; } RiCur = P; RiEnd = P + Len; P = RiEnd; // // EncryptedContentInfo ::= SEQUENCE { // contentType OID(id-data), // contentEncryptionAlgorithm AlgorithmIdentifier { aes-cbc OID, IV OCTET STRING }, // encryptedContent [0] IMPLICIT OCTET STRING } // if (mbedtls_asn1_get_tag (&P, End, &Len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE) != 0) { goto Done; } { UINT8 *EciEnd; mbedtls_asn1_buf Oid; mbedtls_asn1_buf Alg; mbedtls_asn1_buf Params; mbedtls_cipher_type_t CipherType; EciEnd = P + Len; // // contentType (id-data) // if (!Pkcs7Asn1GetOid (&P, EciEnd, &Oid) || !Pkcs7OidEquals (&Oid, MBEDTLS_OID_PKCS7_DATA, MBEDTLS_OID_SIZE (MBEDTLS_OID_PKCS7_DATA))) { goto Done; } // // contentEncryptionAlgorithm AlgorithmIdentifier { aes-cbc OID, IV OCTET STRING } // if (mbedtls_asn1_get_alg (&P, EciEnd, &Alg, &Params) != 0) { goto Done; } if (mbedtls_oid_get_cipher_alg (&Alg, &CipherType) != 0) { goto Done; // Unsupported content-encryption algorithm. } CipherInfo = mbedtls_cipher_info_from_type (CipherType); if (CipherInfo == NULL) { goto Done; // Cipher not built into this configuration. } ContentKeyLen = mbedtls_cipher_info_get_key_bitlen (CipherInfo) / 8; // // The AES-CBC parameter is the IV as an OCTET STRING. // if (Params.tag != MBEDTLS_ASN1_OCTET_STRING) { goto Done; } Iv = Params.p; IvLen = Params.len; // // encryptedContent [0] IMPLICIT OCTET STRING (primitive) // if (mbedtls_asn1_get_tag (&P, EciEnd, &Len, MBEDTLS_ASN1_CONTEXT_SPECIFIC | 0) != 0) { goto Done; } EncContent = P; EncContentLen = Len; } if ((ContentKeyLen == 0) || (ContentKeyLen > MAX_CONTENT_ENC_KEY_LEN) || (EncContentLen == 0)) { goto Done; } // // Walk the recipientInfos, attempting to recover the content-encryption key. // while (RiCur < RiEnd) { UINT8 *KtEnd; UINT8 *IssuerName; UINTN IssuerNameLen; UINT8 *Serial; UINTN SerialLen; mbedtls_asn1_buf KeaAlg; mbedtls_asn1_buf KeaParams; UINT8 *EncKey; UINTN EncKeyLen; BOOLEAN UseOaep; // // RecipientInfo is a CHOICE. Only KeyTransRecipientInfo (ktri), encoded as a // plain SEQUENCE, is supported. Skip the other alternatives (kari [1], // kekri [2], pwri [3], ori [4]) rather than failing, so an envelope that also // carries a supported ktri recipient can still be decrypted. // if (*RiCur != (MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) { UINT8 *SkipCur; SkipCur = RiCur; if (mbedtls_asn1_get_tag (&SkipCur, RiEnd, &Len, *RiCur) != 0) { goto Done; // Malformed length. } RiCur = SkipCur + Len; continue; } // // RecipientInfo (KeyTransRecipientInfo) SEQUENCE // if (mbedtls_asn1_get_tag (&RiCur, RiEnd, &Len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE) != 0) { goto Done; } KtEnd = RiCur + Len; { int Version; if (mbedtls_asn1_get_int (&RiCur, KtEnd, &Version) != 0) { goto Done; } } // // rid IssuerAndSerialNumber ::= SEQUENCE { issuer Name, serialNumber INTEGER } // // rid is a CHOICE of issuerAndSerialNumber (SEQUENCE) or subjectKeyIdentifier // [0]. Only issuerAndSerialNumber is supported; skip recipients that use the // subjectKeyIdentifier form rather than failing. // if ((RiCur >= KtEnd) || (*RiCur != (MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE))) { RiCur = KtEnd; continue; } if (mbedtls_asn1_get_tag (&RiCur, KtEnd, &Len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE) != 0) { goto Done; } { UINT8 *IsnEnd; IsnEnd = RiCur + Len; // // issuer Name -- capture the full SEQUENCE (tag+len+value) for comparison. // IssuerName = RiCur; if (mbedtls_asn1_get_tag (&RiCur, IsnEnd, &Len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE) != 0) { goto Done; } RiCur = RiCur + Len; IssuerNameLen = (UINTN)(RiCur - IssuerName); // // serialNumber INTEGER -- capture its content bytes. // if (mbedtls_asn1_get_tag (&RiCur, IsnEnd, &Len, MBEDTLS_ASN1_INTEGER) != 0) { goto Done; } Serial = RiCur; SerialLen = Len; RiCur = IsnEnd; } if ((RecipientCert != NULL) && ((IssuerNameLen != Cert.issuer_raw.len) || (CompareMem (IssuerName, Cert.issuer_raw.p, IssuerNameLen) != 0) || (SerialLen != Cert.serial.len) || (CompareMem (Serial, Cert.serial.p, SerialLen) != 0))) { RiCur = KtEnd; continue; // Not this recipient. } // // keyEncryptionAlgorithm AlgorithmIdentifier // if (mbedtls_asn1_get_alg (&RiCur, KtEnd, &KeaAlg, &KeaParams) != 0) { goto Done; } UseOaep = Pkcs7OidEquals (&KeaAlg, MBEDTLS_OID_RSAES_OAEP, MBEDTLS_OID_SIZE (MBEDTLS_OID_RSAES_OAEP)); if (!UseOaep && !Pkcs7OidEquals (&KeaAlg, MBEDTLS_OID_PKCS1_RSA, MBEDTLS_OID_SIZE (MBEDTLS_OID_PKCS1_RSA))) { // // Unsupported key transport algorithm; skip this recipient. // RiCur = KtEnd; continue; } // // encryptedKey OCTET STRING // if (mbedtls_asn1_get_tag (&RiCur, KtEnd, &Len, MBEDTLS_ASN1_OCTET_STRING) != 0) { goto Done; } EncKey = RiCur; EncKeyLen = Len; RiCur = KtEnd; // // mbedtls_rsa_pkcs1_decrypt always reads a full modulus worth of input, so // reject any recipient whose encryptedKey is a different size. // if (EncKeyLen != mbedtls_rsa_get_len ((mbedtls_rsa_context *)Rsa)) { continue; } // // Select the RSA padding scheme indicated by the recipient info. // if (UseOaep) { mbedtls_md_type_t OaepMd; if (!Pkcs7ParseOaepHash (&KeaParams, &OaepMd) || (mbedtls_rsa_set_padding ((mbedtls_rsa_context *)Rsa, MBEDTLS_RSA_PKCS_V21, OaepMd) != 0)) { continue; } } else { (VOID)mbedtls_rsa_set_padding ((mbedtls_rsa_context *)Rsa, MBEDTLS_RSA_PKCS_V15, MBEDTLS_MD_NONE); } // // RSA-decrypt the content-encryption key. // { UINT8 Cek[MAX_CONTENT_ENC_KEY_LEN]; size_t CekLen; BOOLEAN DecryptOk; CekLen = 0; if (mbedtls_rsa_pkcs1_decrypt ( (mbedtls_rsa_context *)Rsa, MbedtlsRand, NULL, &CekLen, EncKey, Cek, sizeof (Cek) ) != 0) { ZeroMem (Cek, sizeof (Cek)); continue; // Try the next recipient. } if (CekLen != ContentKeyLen) { ZeroMem (Cek, sizeof (Cek)); continue; } DecryptOk = Pkcs7AesCbcDecryptContent ( CipherInfo, Cek, CekLen, Iv, IvLen, EncContent, EncContentLen, &ReturnData, &ReturnSize ); ZeroMem (Cek, sizeof (Cek)); if (DecryptOk) { Succeeded = TRUE; goto Done; } // // Otherwise fall through and try the next recipient. // } } Done: // // Normalize a zero-length success to a 1-byte allocation so callers always get a // freeable, non-NULL buffer (matching the OpenSSL instance). // if (Succeeded && (ReturnData == NULL)) { ReturnData = AllocateZeroPool (1); if (ReturnData == NULL) { Succeeded = FALSE; } else { ReturnSize = 0; } } if (!Succeeded && (ReturnData != NULL)) { FreePool (ReturnData); ReturnData = NULL; ReturnSize = 0; } if (Rsa != NULL) { RsaFree (Rsa); } mbedtls_x509_crt_free (&Cert); if (OutData != NULL) { *OutData = ReturnData; } if (OutDataSize != NULL) { *OutDataSize = ReturnSize; } return Succeeded; }