/** @file This file contains UEFI wrapper functions for RSA PKCS1v2 OAEP encryption routines. SPDX-License-Identifier: BSD-2-Clause-Patent Copyright (C) Microsoft Corporation. All Rights Reserved. Copyright (c) 2019, Intel Corporation. All rights reserved.
**/ #include "InternalCryptLib.h" #include #include #include #include /** Retrieve a pointer to EVP message digest object. @param[in] DigestLen Length of the message digest. **/ STATIC const EVP_MD * GetEvpMD ( IN UINT16 DigestLen ) { switch (DigestLen) { case SHA1_DIGEST_SIZE: return EVP_sha1 (); break; case SHA256_DIGEST_SIZE: return EVP_sha256 (); break; case SHA384_DIGEST_SIZE: return EVP_sha384 (); break; case SHA512_DIGEST_SIZE: return EVP_sha512 (); break; default: return NULL; } } /** Encrypts a blob using PKCS1v2 (RSAES-OAEP) schema. On success, will return the encrypted message in a newly allocated buffer. Things that can cause a failure include: - X509 key size does not match any known key size. - Fail to parse X509 certificate. - Fail to allocate an intermediate buffer. - Null pointer provided for a non-optional parameter. - Data size is too large for the provided key size (max size is a function of key size and hash digest size). @param[in] Pkey A pointer to an EVP_PKEY struct that will be used to encrypt the data. @param[in] InData Data to be encrypted. @param[in] InDataSize Size of the data buffer. @param[in] PrngSeed [Optional] If provided, a pointer to a random seed buffer to be used when initializing the PRNG. NULL otherwise. @param[in] PrngSeedSize [Optional] If provided, size of the random seed buffer. 0 otherwise. @param[in] DigestLen [Optional] If provided, size of the hash used: SHA1_DIGEST_SIZE SHA256_DIGEST_SIZE SHA384_DIGEST_SIZE SHA512_DIGEST_SIZE 0 to use default (SHA1) @param[out] EncryptedData Pointer to an allocated buffer containing the encrypted message. @param[out] EncryptedDataSize Size of the encrypted message buffer. @retval TRUE Encryption was successful. @retval FALSE Encryption failed. **/ BOOLEAN EFIAPI InternalPkcs1v2Encrypt ( EVP_PKEY *Pkey, IN UINT8 *InData, IN UINTN InDataSize, IN CONST UINT8 *PrngSeed OPTIONAL, IN UINTN PrngSeedSize OPTIONAL, IN UINT16 DigestLen OPTIONAL, OUT UINT8 **EncryptedData, OUT UINTN *EncryptedDataSize ) { BOOLEAN Result; EVP_PKEY_CTX *PkeyCtx; UINT8 *OutData; UINTN OutDataSize; CONST EVP_MD *HashAlg; // // Check input parameters. // if ((Pkey == NULL) || (InData == NULL) || (EncryptedData == NULL) || (EncryptedDataSize == NULL)) { return FALSE; } *EncryptedData = NULL; *EncryptedDataSize = 0; Result = FALSE; PkeyCtx = NULL; OutData = NULL; OutDataSize = 0; // // If it provides a seed then use it. // Ohterwise, we'll seed with fixed values and hope that the PRNG has already been // used enough to generate sufficient entropy. // if (PrngSeed != NULL) { RandomSeed (PrngSeed, PrngSeedSize); } else { RandomSeed (NULL, 0); } // // Create a context for the public key operation. // PkeyCtx = EVP_PKEY_CTX_new (Pkey, NULL); if (PkeyCtx == NULL) { // // Fail to create contex. // goto _Exit; } // // Initialize the context and set the desired padding. // if ((EVP_PKEY_encrypt_init (PkeyCtx) <= 0) || (EVP_PKEY_CTX_set_rsa_padding (PkeyCtx, RSA_PKCS1_OAEP_PADDING) <= 0)) { // // Fail to initialize the context. // goto _Exit; } if (DigestLen != 0) { HashAlg = GetEvpMD (DigestLen); if (HashAlg == NULL) { goto _Exit; } if (EVP_PKEY_CTX_set_rsa_oaep_md (PkeyCtx, HashAlg) <= 0) { goto _Exit; } if (EVP_PKEY_CTX_set_rsa_mgf1_md (PkeyCtx, HashAlg) <= 0) { goto _Exit; } } // // Determine the required buffer length for malloc'ing. // if (EVP_PKEY_encrypt (PkeyCtx, NULL, &OutDataSize, InData, InDataSize) <= 0) { // // Fail to determine output buffer size. // goto _Exit; } // // Allocate a buffer for the output data. // OutData = AllocatePool (OutDataSize); if (OutData == NULL) { // // Fail to allocate the output buffer. // goto _Exit; } // // Encrypt Data. // if (EVP_PKEY_encrypt (PkeyCtx, OutData, &OutDataSize, InData, InDataSize) <= 0) { // // Fail to encrypt data, need to free the output buffer. // FreePool (OutData); OutData = NULL; OutDataSize = 0; goto _Exit; } // // Encrypt done. // *EncryptedData = OutData; *EncryptedDataSize = OutDataSize; Result = TRUE; _Exit: // // Release Resources // if (PkeyCtx != NULL) { EVP_PKEY_CTX_free (PkeyCtx); } return Result; } /** Encrypts a blob using PKCS1v2 (RSAES-OAEP) schema. On success, will return the encrypted message in a newly allocated buffer. Things that can cause a failure include: - X509 key size does not match any known key size. - Fail to parse X509 certificate. - Fail to allocate an intermediate buffer. - Null pointer provided for a non-optional parameter. - Data size is too large for the provided key size (max size is a function of key size and hash digest size). @param[in] PublicKey A pointer to the DER-encoded X509 certificate that will be used to encrypt the data. @param[in] PublicKeySize Size of the X509 cert buffer. @param[in] InData Data to be encrypted. @param[in] InDataSize Size of the data buffer. @param[in] PrngSeed [Optional] If provided, a pointer to a random seed buffer to be used when initializing the PRNG. NULL otherwise. @param[in] PrngSeedSize [Optional] If provided, size of the random seed buffer. 0 otherwise. @param[out] EncryptedData Pointer to an allocated buffer containing the encrypted message. @param[out] EncryptedDataSize Size of the encrypted message buffer. @retval TRUE Encryption was successful. @retval FALSE Encryption failed. **/ BOOLEAN EFIAPI Pkcs1v2Encrypt ( IN CONST UINT8 *PublicKey, IN UINTN PublicKeySize, IN UINT8 *InData, IN UINTN InDataSize, IN CONST UINT8 *PrngSeed OPTIONAL, IN UINTN PrngSeedSize OPTIONAL, OUT UINT8 **EncryptedData, OUT UINTN *EncryptedDataSize ) { BOOLEAN Result; CONST UINT8 *TempPointer; X509 *CertData; EVP_PKEY *Pkey; // // Check input parameters. // if ((PublicKey == NULL) || (InData == NULL) || (EncryptedData == NULL) || (EncryptedDataSize == NULL)) { return FALSE; } // // Check public key size. // if (PublicKeySize > 0xFFFFFFFF) { // // Public key size is too large for implementation. // return FALSE; } *EncryptedData = NULL; *EncryptedDataSize = 0; Result = FALSE; TempPointer = NULL; CertData = NULL; Pkey = NULL; // // Parse the X509 cert and extract the public key. // TempPointer = PublicKey; CertData = d2i_X509 (&CertData, &TempPointer, (UINT32)PublicKeySize); if (CertData == NULL) { // // Fail to parse X509 cert. // goto _Exit; } // // Extract the public key from the x509 cert in a format that // OpenSSL can use. // Pkey = X509_get_pubkey (CertData); if (Pkey == NULL) { // // Fail to extract public key. // goto _Exit; } Result = InternalPkcs1v2Encrypt (Pkey, InData, InDataSize, PrngSeed, PrngSeedSize, 0, EncryptedData, EncryptedDataSize); _Exit: // // Release Resources // if (CertData != NULL) { X509_free (CertData); } if (Pkey != NULL) { EVP_PKEY_free (Pkey); } return Result; } /** Encrypts a blob using PKCS1v2 (RSAES-OAEP) schema. On success, will return the encrypted message in a newly allocated buffer. Things that can cause a failure include: - Fail to allocate an intermediate buffer. - Null pointer provided for a non-optional parameter. - Data size is too large for the provided key size (max size is a function of key size and hash digest size). @param[in] RsaContext A pointer to an RSA context created by RsaNew() and provisioned with a public key using RsaSetKey(). @param[in] InData Data to be encrypted. @param[in] InDataSize Size of the data buffer. @param[in] PrngSeed [Optional] If provided, a pointer to a random seed buffer to be used when initializing the PRNG. NULL otherwise. @param[in] PrngSeedSize [Optional] If provided, size of the random seed buffer. 0 otherwise. @param[in] DigestLen [Optional] If provided, size of the hash used: SHA1_DIGEST_SIZE SHA256_DIGEST_SIZE SHA384_DIGEST_SIZE SHA512_DIGEST_SIZE 0 to use default (SHA1) @param[out] EncryptedData Pointer to an allocated buffer containing the encrypted message. @param[out] EncryptedDataSize Size of the encrypted message buffer. @retval TRUE Encryption was successful. @retval FALSE Encryption failed. **/ BOOLEAN EFIAPI RsaOaepEncrypt ( IN VOID *RsaContext, IN UINT8 *InData, IN UINTN InDataSize, IN CONST UINT8 *PrngSeed OPTIONAL, IN UINTN PrngSeedSize OPTIONAL, IN UINT16 DigestLen OPTIONAL, OUT UINT8 **EncryptedData, OUT UINTN *EncryptedDataSize ) { BOOLEAN Result; EVP_PKEY *Pkey; // // Check input parameters. // if (((RsaContext == NULL) || (InData == NULL)) || (EncryptedData == NULL) || (EncryptedDataSize == NULL)) { return FALSE; } *EncryptedData = NULL; *EncryptedDataSize = 0; Result = FALSE; Pkey = NULL; Pkey = EVP_PKEY_new (); if (Pkey == NULL) { goto _Exit; } if (EVP_PKEY_set1_RSA (Pkey, (RSA *)RsaContext) == 0) { goto _Exit; } Result = InternalPkcs1v2Encrypt (Pkey, InData, InDataSize, PrngSeed, PrngSeedSize, DigestLen, EncryptedData, EncryptedDataSize); _Exit: // // Release Resources // if (Pkey != NULL) { EVP_PKEY_free (Pkey); } return Result; } /** Decrypts a blob using PKCS1v2 (RSAES-OAEP) schema. On success, will return the decrypted message in a newly allocated buffer. Things that can cause a failure include: - Fail to parse private key. - Fail to allocate an intermediate buffer. - Null pointer provided for a non-optional parameter. @param[in] Pkey A pointer to an EVP_PKEY which will decrypt that data. @param[in] EncryptedData Data to be decrypted. @param[in] EncryptedDataSize Size of the encrypted buffer. @param[in] DigestLen [Optional] If provided, size of the hash used: SHA1_DIGEST_SIZE SHA256_DIGEST_SIZE SHA384_DIGEST_SIZE SHA512_DIGEST_SIZE 0 to use default (SHA1) @param[out] OutData Pointer to an allocated buffer containing the encrypted message. @param[out] OutDataSize Size of the encrypted message buffer. @retval TRUE Encryption was successful. @retval FALSE Encryption failed. **/ BOOLEAN EFIAPI InternalPkcs1v2Decrypt ( EVP_PKEY *Pkey, IN UINT8 *EncryptedData, IN UINTN EncryptedDataSize, IN UINT16 DigestLen OPTIONAL, OUT UINT8 **OutData, OUT UINTN *OutDataSize ) { BOOLEAN Result; EVP_PKEY_CTX *PkeyCtx; UINT8 *TempData; UINTN TempDataSize; INTN ReturnCode; CONST EVP_MD *HashAlg; // // Check input parameters. // if ((Pkey == NULL) || (EncryptedData == NULL) || (OutData == NULL) || (OutDataSize == NULL)) { return FALSE; } Result = FALSE; PkeyCtx = NULL; TempData = NULL; TempDataSize = 0; // // Create a context for the decryption operation. // PkeyCtx = EVP_PKEY_CTX_new (Pkey, NULL); if (PkeyCtx == NULL) { // // Fail to create contex. // DEBUG ((DEBUG_ERROR, "[%a] EVP_PKEY_CTK_new() failed\n", __func__)); goto _Exit; } // // Initialize the context and set the desired padding. // if ((EVP_PKEY_decrypt_init (PkeyCtx) <= 0) || (EVP_PKEY_CTX_set_rsa_padding (PkeyCtx, RSA_PKCS1_OAEP_PADDING) <= 0)) { // // Fail to initialize the context. // DEBUG ((DEBUG_ERROR, "[%a] EVP_PKEY_decrypt_init() failed\n", __func__)); goto _Exit; } if (DigestLen != 0) { HashAlg = GetEvpMD (DigestLen); if (HashAlg == NULL) { goto _Exit; } if (EVP_PKEY_CTX_set_rsa_oaep_md (PkeyCtx, HashAlg) <= 0) { goto _Exit; } if (EVP_PKEY_CTX_set_rsa_mgf1_md (PkeyCtx, HashAlg) <= 0) { goto _Exit; } } // // Determine the required buffer length for malloc'ing. // ReturnCode = EVP_PKEY_decrypt (PkeyCtx, NULL, &TempDataSize, EncryptedData, EncryptedDataSize); if (ReturnCode <= 0) { // // Fail to determine output buffer size. // DEBUG ((DEBUG_ERROR, "[%a] EVP_PKEY_decrypt() failed to determine output buffer size (rc=%d)\n", __func__, ReturnCode)); goto _Exit; } // // Allocate a buffer for the output data. // TempData = AllocatePool (TempDataSize); if (TempData == NULL) { // // Fail to allocate the output buffer. // goto _Exit; } // // Decrypt Data. // ReturnCode = EVP_PKEY_decrypt (PkeyCtx, TempData, &TempDataSize, EncryptedData, EncryptedDataSize); if (ReturnCode <= 0) { // // Fail to decrypt data, need to free the output buffer. // FreePool (TempData); TempData = NULL; TempDataSize = 0; DEBUG ((DEBUG_ERROR, "[%a] EVP_PKEY_decrypt(TempData) failed to decrypt (rc=%d)\n", __func__, ReturnCode)); goto _Exit; } // // Decrypt done. // *OutData = TempData; *OutDataSize = TempDataSize; Result = TRUE; _Exit: if (PkeyCtx != NULL) { EVP_PKEY_CTX_free (PkeyCtx); } return Result; } /** Decrypts a blob using PKCS1v2 (RSAES-OAEP) schema. On success, will return the decrypted message in a newly allocated buffer. Things that can cause a failure include: - Fail to parse private key. - Fail to allocate an intermediate buffer. - Null pointer provided for a non-optional parameter. @param[in] PrivateKey A pointer to the DER-encoded private key. @param[in] PrivateKeySize Size of the private key buffer. @param[in] EncryptedData Data to be decrypted. @param[in] EncryptedDataSize Size of the encrypted buffer. @param[out] OutData Pointer to an allocated buffer containing the encrypted message. @param[out] OutDataSize Size of the encrypted message buffer. @retval TRUE Encryption was successful. @retval FALSE Encryption failed. **/ BOOLEAN EFIAPI Pkcs1v2Decrypt ( IN CONST UINT8 *PrivateKey, IN UINTN PrivateKeySize, IN UINT8 *EncryptedData, IN UINTN EncryptedDataSize, OUT UINT8 **OutData, OUT UINTN *OutDataSize ) { BOOLEAN Result; EVP_PKEY *Pkey; CONST UINT8 *TempPointer; // // Check input parameters. // if ((PrivateKey == NULL) || (EncryptedData == NULL) || (OutData == NULL) || (OutDataSize == NULL)) { return FALSE; } Result = FALSE; Pkey = NULL; TempPointer = NULL; // // Parse the private key. // TempPointer = PrivateKey; Pkey = d2i_PrivateKey (EVP_PKEY_RSA, &Pkey, &TempPointer, (UINT32)PrivateKeySize); if (Pkey == NULL) { // // Fail to parse private key. // DEBUG ((DEBUG_ERROR, "[%a] d2i_PrivateKey() failed\n", __func__)); goto _Exit; } Result = InternalPkcs1v2Decrypt (Pkey, EncryptedData, EncryptedDataSize, 0, OutData, OutDataSize); _Exit: if (Pkey != NULL) { EVP_PKEY_free (Pkey); } return Result; } /** Decrypts a blob using PKCS1v2 (RSAES-OAEP) schema. On success, will return the decrypted message in a newly allocated buffer. Things that can cause a failure include: - Fail to parse private key. - Fail to allocate an intermediate buffer. - Null pointer provided for a non-optional parameter. @param[in] RsaContext A pointer to an RSA context created by RsaNew() and provisioned with a private key using RsaSetKey(). @param[in] EncryptedData Data to be decrypted. @param[in] EncryptedDataSize Size of the encrypted buffer. @param[in] DigestLen [Optional] If provided, size of the hash used: SHA1_DIGEST_SIZE SHA256_DIGEST_SIZE SHA384_DIGEST_SIZE SHA512_DIGEST_SIZE 0 to use default (SHA1) @param[out] OutData Pointer to an allocated buffer containing the encrypted message. @param[out] OutDataSize Size of the encrypted message buffer. @retval TRUE Encryption was successful. @retval FALSE Encryption failed. **/ BOOLEAN EFIAPI RsaOaepDecrypt ( IN VOID *RsaContext, IN UINT8 *EncryptedData, IN UINTN EncryptedDataSize, IN UINT16 DigestLen OPTIONAL, OUT UINT8 **OutData, OUT UINTN *OutDataSize ) { BOOLEAN Result; EVP_PKEY *Pkey; // // Check input parameters. // if ((RsaContext == NULL) || (EncryptedData == NULL) || (OutData == NULL) || (OutDataSize == NULL)) { return FALSE; } Result = FALSE; Pkey = NULL; // // Create a context for the decryption operation. // Pkey = EVP_PKEY_new (); if (Pkey == NULL) { goto _Exit; } if (EVP_PKEY_set1_RSA (Pkey, (RSA *)RsaContext) == 0) { goto _Exit; } Result = InternalPkcs1v2Decrypt (Pkey, EncryptedData, EncryptedDataSize, DigestLen, OutData, OutDataSize); _Exit: if (Pkey != NULL) { EVP_PKEY_free (Pkey); } return Result; }