/** @file PKCS7 Encryption implementation over OpenSSL Copyright (c) 2023, Microsoft Corporation. All rights reserved. SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include "InternalCryptLib.h" #include #include #include #include #include /** Creates a DER-encoded PKCS#7 ContentInfo containing an envelopedData structure that wraps content encrypted for secure transmission to one or more recipients. If this interface is not supported, return FALSE. @param[in] X509Stack Pointer to a stack of X.509 certificates for the intended recipients of this message, created using X509ConstructCertificateStack or similar. Each certificate must provide an RSA public key. Any of the corresponding private keys will be able to decrypt the content of the returned ContentInfo. @param[in] InData Pointer to the content to be encrypted. @param[in] InDataSize Size of the content to be encrypted in bytes. @param[in] CipherNid NID of the symmetric cipher to use for encryption. Supported values are CRYPTO_NID_AES128CBC, CRYPTO_NID_AES192CBC, and CRYPTO_NID_AES256CBC. @param[in] Flags Flags for the encryption operation. Currently only CRYPTO_PKCS7_DEFAULT is supported, which indicates that the input data is treated as binary data. @param[out] ContentInfo Receives a pointer to the output, which is a PKCS#7 DER-encoded ContentInfo that wraps an envelopedData. The caller must free the returned buffer with FreePool(). @param[out] ContentInfoSize Receives the size of the output in bytes. @retval TRUE PKCS#7 data encryption succeeded. @retval FALSE PKCS#7 data encryption failed. @retval FALSE This interface is not supported. **/ BOOLEAN EFIAPI Pkcs7Encrypt ( IN UINT8 *X509Stack, IN UINT8 *InData, IN UINTN InDataSize, IN UINT32 CipherNid, IN UINT32 Flags, OUT UINT8 **ContentInfo, OUT UINTN *ContentInfoSize ) { BOOLEAN Succeeded; EVP_CIPHER const *Cipher; UINT8 *ReturnData; UINTN ReturnSize; BIO *Bio; PKCS7 *Pkcs7; UINT8 *Pkcs7Data; int I2dResult; ReturnData = NULL; ReturnSize = 0; Bio = NULL; Pkcs7 = NULL; if ((X509Stack == NULL) || (InData == NULL) || (InDataSize > INT_MAX) || (Flags != CRYPTO_PKCS7_DEFAULT) || (ContentInfo == NULL) || (ContentInfoSize == NULL)) { Succeeded = FALSE; // Invalid argument. goto Done; } switch (CipherNid) { case CRYPTO_NID_AES128CBC: Cipher = EVP_aes_128_cbc (); break; case CRYPTO_NID_AES192CBC: Cipher = EVP_aes_192_cbc (); break; case CRYPTO_NID_AES256CBC: Cipher = EVP_aes_256_cbc (); break; default: Cipher = NULL; // Unsupported cipher NID. break; } if (Cipher == NULL) { Succeeded = FALSE; // Unsupported cipher NID. goto Done; } Bio = BIO_new_mem_buf (InData, (int)InDataSize); if (Bio == NULL) { Succeeded = FALSE; // BIO creation failed. goto Done; } // // Create a new PKCS#7 structure // Pkcs7 = PKCS7_encrypt ((struct stack_st_X509 *)X509Stack, Bio, Cipher, PKCS7_BINARY); if (Pkcs7 == NULL) { Succeeded = FALSE; // PKCS7 creation failed. goto Done; } I2dResult = i2d_PKCS7 (Pkcs7, NULL); if (I2dResult <= 0) { Succeeded = FALSE; // DER encoding failed. goto Done; } ReturnSize = (UINTN)I2dResult; ReturnData = (UINT8 *)AllocateZeroPool (ReturnSize); if (ReturnData == NULL) { ReturnSize = 0; Succeeded = FALSE; // Memory allocation failed. goto Done; } Pkcs7Data = ReturnData; I2dResult = i2d_PKCS7 (Pkcs7, &Pkcs7Data); if ((I2dResult <= 0) || (ReturnSize != (UINTN)I2dResult)) { FreePool (ReturnData); ReturnData = NULL; ReturnSize = 0; Succeeded = FALSE; // DER encoding returned an unexpected result. goto Done; } Succeeded = TRUE; // Success, result in ReturnData/ReturnSize. Done: if (Pkcs7 != NULL) { PKCS7_free (Pkcs7); Pkcs7 = NULL; } if (Bio != NULL) { BIO_free (Bio); Bio = NULL; } if (ContentInfo != NULL) { *ContentInfo = ReturnData; } if (ContentInfoSize != NULL) { *ContentInfoSize = ReturnSize; } return Succeeded; }