mirror of
https://github.com/tianocore/edk2
synced 2026-08-27 00:23:19 -04:00
Introduce a helper library that implements wrappers for the
cryptographic functionality required by the Arm Boot Sync
Blocks protocol implementation.
This library implements the following:
- Key Exchange: ECDH key using the ECC Curve-P384
- Key Derivation: SHA512 HMAC-based Extract-and-Expand HKDF
- Encryption/Decryption: AEAD AES-GCM authenticated encryption
and decryption
Also register the library class in ArmVirtPkg.dec, wire the
instance into the KvmTool guest firmware and update the CI YAML
file to add CryptoPkg/CryptoPkg.dec as an acceptable dependency.
Signed-off-by: Sami Mujawar <sami.mujawar@arm.com>
614 lines
16 KiB
C
614 lines
16 KiB
C
/** @file
|
|
Arm CCA Boot Sync Crypto library.
|
|
|
|
Copyright (c) 2024, Arm Limited. All rights reserved.<BR>
|
|
SPDX-License-Identifier: BSD-2-Clause-Patent
|
|
|
|
@par Glossary:
|
|
- BS - Boot Sync
|
|
|
|
@par Reference(s):
|
|
- Realm Host Interface (RHI) Specification, version 1.0-alp0
|
|
(https://developer.arm.com/documentation/den0148/)
|
|
**/
|
|
|
|
#include <Base.h>
|
|
#include <Library/BaseLib.h>
|
|
#include <Library/BaseCryptLib.h>
|
|
#include <Library/BaseMemoryLib.h>
|
|
#include <Library/DebugLib.h>
|
|
#include <Library/MemoryAllocationLib.h>
|
|
#include <Library/PrintLib.h>
|
|
#include <Library/RngLib.h>
|
|
|
|
#include "ArmCcaBootSyncCrypto.h"
|
|
|
|
/**
|
|
Generate a 256-bit random number as initial seed and
|
|
prime the pseudo-random number generator.
|
|
|
|
@retval EFI_ABORTED Failed to generate seed.
|
|
@retval EFI_SUCCESS Success.
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
EFIAPI
|
|
ArmCcaInitRandomNumberSeed (
|
|
VOID
|
|
)
|
|
{
|
|
UINT64 RngSeed[4];
|
|
|
|
ZeroMem (RngSeed, sizeof (RngSeed));
|
|
// Generate a 256 bit random number as an inital seed.
|
|
if (!GetRandomNumber128 (RngSeed)) {
|
|
return EFI_ABORTED;
|
|
}
|
|
|
|
if (!GetRandomNumber128 (&RngSeed[2])) {
|
|
return EFI_ABORTED;
|
|
}
|
|
|
|
// Prime the pseudo-random number generator with the initial seed.
|
|
if (!RandomSeed ((UINT8 *)RngSeed, 32)) {
|
|
return EFI_ABORTED;
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Generate an ECDH key using the ECC Curve-P384 and retrive the public key.
|
|
|
|
@param[out] Context Pointer to the key handle.
|
|
|
|
@retval EFI_INVALID_PARAMETER A parameter was invalid.
|
|
@retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
|
|
@retval EFI_ABORTED An operation failed.
|
|
@retval EFI_SUCCESS Success.
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
ArmCcaBootSyncCryptoGenerateKey (
|
|
OUT VOID **Context
|
|
)
|
|
{
|
|
BOOLEAN Result;
|
|
EFI_STATUS Status;
|
|
|
|
ARM_CCA_BOOTSYNC_KEY_CONTEXT *BsKey;
|
|
VOID *EcContext;
|
|
UINT8 *PublicKey;
|
|
UINTN PublicKeySize;
|
|
UINTN EcCurveNid;
|
|
|
|
if (Context == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
BsKey = (ARM_CCA_BOOTSYNC_KEY_CONTEXT *)AllocateZeroPool (
|
|
sizeof (ARM_CCA_BOOTSYNC_KEY_CONTEXT)
|
|
);
|
|
if (BsKey == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
// Arm CCA Security Model document section '12.3.1 Recommended parameter
|
|
// sizes' recommends using ECC Curve-P384
|
|
EcCurveNid = CRYPTO_NID_SECP384R1;
|
|
|
|
EcContext = EcNewByNid (EcCurveNid);
|
|
if (EcContext == NULL) {
|
|
Status = EFI_ABORTED;
|
|
goto exit_handler;
|
|
}
|
|
|
|
PublicKeySize = ECC_CURVE_P384_PUB_KEY_SIZE;
|
|
PublicKey = (UINT8 *)AllocateZeroPool (PublicKeySize);
|
|
if (PublicKey == NULL) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto exit_handler1;
|
|
}
|
|
|
|
Result = EcGenerateKey (
|
|
EcContext,
|
|
PublicKey,
|
|
&PublicKeySize
|
|
);
|
|
if (!Result) {
|
|
Status = EFI_ABORTED;
|
|
goto exit_handler2;
|
|
}
|
|
|
|
BsKey->EcContext = EcContext;
|
|
BsKey->PublicKey = PublicKey;
|
|
BsKey->PublicKeySize = PublicKeySize;
|
|
BsKey->EcCurveNid = EcCurveNid;
|
|
|
|
*Context = BsKey;
|
|
return EFI_SUCCESS;
|
|
|
|
exit_handler2:
|
|
FreePool (PublicKey);
|
|
exit_handler1:
|
|
EcFree (EcContext);
|
|
exit_handler:
|
|
FreePool (BsKey);
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Delete the key and associated data.
|
|
|
|
@param[in] Context Pointer to the key handle.
|
|
|
|
@retval EFI_INVALID_PARAMETER A parameter was invalid.
|
|
@retval EFI_SUCCESS Success.
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
ArmCcaBootSyncCryptoDeleteKey (
|
|
VOID *Context
|
|
)
|
|
{
|
|
ARM_CCA_BOOTSYNC_KEY_CONTEXT *BsKey;
|
|
|
|
if (Context == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
BsKey = (ARM_CCA_BOOTSYNC_KEY_CONTEXT *)Context;
|
|
|
|
if (BsKey->PublicKey != NULL) {
|
|
ZeroMem (BsKey->PublicKey, BsKey->PublicKeySize);
|
|
FreePool (BsKey->PublicKey);
|
|
}
|
|
|
|
if (BsKey->CommonKey != NULL) {
|
|
ZeroMem (BsKey->CommonKey, BsKey->CommonKeySize);
|
|
FreePool (BsKey->CommonKey);
|
|
}
|
|
|
|
if (BsKey->EcContext != NULL) {
|
|
EcFree (BsKey->EcContext);
|
|
}
|
|
|
|
FreePool (BsKey);
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Get the public key in PEM format.
|
|
|
|
Note: The caller is responsible to free the PubKeyPem buffer
|
|
by calling FreePool().
|
|
|
|
@param[in] Context Pointer to the key handle.
|
|
@param[out] PubKeyPem Pointer to store the PEM data.
|
|
@param[out] PubKeyPemSize PEM data size.
|
|
|
|
@retval EFI_INVALID_PARAMETER A parameter was invalid.
|
|
@retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
|
|
@retval EFI_ABORTED An operation failed.
|
|
@retval EFI_SUCCESS Success.
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
ArmCcaBootSyncCryptoGetPublicKey (
|
|
IN VOID *Context,
|
|
OUT UINT8 **PubKeyPem,
|
|
OUT UINTN *PubKeyPemSize
|
|
)
|
|
{
|
|
BOOLEAN Result;
|
|
EFI_STATUS Status;
|
|
|
|
ARM_CCA_BOOTSYNC_KEY_CONTEXT *BsKey;
|
|
UINT8 *PemData;
|
|
UINTN PemSize;
|
|
|
|
if ((Context == NULL) || (PubKeyPem == NULL) || (PubKeyPemSize == NULL)) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
BsKey = (ARM_CCA_BOOTSYNC_KEY_CONTEXT *)Context;
|
|
|
|
// First get the PEM size.
|
|
PemData = NULL;
|
|
PemSize = 0;
|
|
Result = EcPublicKeyToPEM (
|
|
BsKey->EcContext,
|
|
PemData,
|
|
&PemSize
|
|
);
|
|
if ((!Result) && (PemSize == 0)) {
|
|
return EFI_ABORTED;
|
|
}
|
|
|
|
PemData = AllocateZeroPool (PemSize);
|
|
if (PemData == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
Result = EcPublicKeyToPEM (
|
|
BsKey->EcContext,
|
|
PemData,
|
|
&PemSize
|
|
);
|
|
if (!Result) {
|
|
Status = EFI_ABORTED;
|
|
goto exit_handler;
|
|
}
|
|
|
|
*PubKeyPem = PemData;
|
|
*PubKeyPemSize = PemSize;
|
|
return EFI_SUCCESS;
|
|
|
|
exit_handler:
|
|
FreePool (PemData);
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Get the peer key using the peer public PEM data.
|
|
|
|
Note: The caller is responsible for freeing the peer key by calling
|
|
ArmCcaBootSyncCryptoDeleteKey() for the PeerContext.
|
|
|
|
@param[out] PeerContext Pointer to the peer key handle.
|
|
@param[in] PeerPubKeyPem Pointer to the peer PEM data.
|
|
@param[in] PeerPubKeyPemSize PEM data size.
|
|
|
|
@retval EFI_INVALID_PARAMETER A parameter was invalid.
|
|
@retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
|
|
@retval EFI_ABORTED An operation failed.
|
|
@retval EFI_SUCCESS Success.
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
ArmCcaBootSyncCryptoGeneratePeerKey (
|
|
OUT VOID **PeerContext,
|
|
IN UINT8 *PeerPubKeyPem,
|
|
IN UINTN PeerPubKeyPemSize
|
|
)
|
|
{
|
|
BOOLEAN Result;
|
|
EFI_STATUS Status;
|
|
|
|
ARM_CCA_BOOTSYNC_KEY_CONTEXT *BsKey;
|
|
VOID *EcContextRetrieved;
|
|
UINT8 *PublicKey;
|
|
UINTN PublicKeySize;
|
|
UINTN PeerKeyNid;
|
|
|
|
if ((PeerContext == NULL) ||
|
|
(PeerPubKeyPem == NULL) ||
|
|
(PeerPubKeyPemSize == 0))
|
|
{
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
BsKey = (ARM_CCA_BOOTSYNC_KEY_CONTEXT *)AllocateZeroPool (
|
|
sizeof (ARM_CCA_BOOTSYNC_KEY_CONTEXT)
|
|
);
|
|
if (BsKey == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
Result = EcGetPublicKeyFromPem (
|
|
PeerPubKeyPem,
|
|
PeerPubKeyPemSize,
|
|
NULL,
|
|
&EcContextRetrieved
|
|
);
|
|
if (!Result) {
|
|
Status = EFI_ABORTED;
|
|
goto exit_handler;
|
|
}
|
|
|
|
Result = EcGetCurveNid (EcContextRetrieved, &PeerKeyNid);
|
|
if ((!Result) || (PeerKeyNid != CRYPTO_NID_SECP384R1)) {
|
|
Status = EFI_INVALID_PARAMETER;
|
|
goto exit_handler1;
|
|
}
|
|
|
|
PublicKeySize = ECC_CURVE_P384_PUB_KEY_SIZE;
|
|
PublicKey = AllocateZeroPool (PublicKeySize);
|
|
if (PublicKey == NULL) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto exit_handler1;
|
|
}
|
|
|
|
Result = EcGetPubKey (
|
|
EcContextRetrieved,
|
|
PublicKey,
|
|
&PublicKeySize
|
|
);
|
|
if (!Result) {
|
|
Status = EFI_ABORTED;
|
|
goto exit_handler2;
|
|
}
|
|
|
|
BsKey->EcContext = EcContextRetrieved;
|
|
BsKey->PublicKey = PublicKey;
|
|
BsKey->PublicKeySize = PublicKeySize;
|
|
BsKey->EcCurveNid = CRYPTO_NID_SECP384R1;
|
|
|
|
*PeerContext = BsKey;
|
|
return EFI_SUCCESS;
|
|
|
|
exit_handler2:
|
|
FreePool (PublicKey);
|
|
exit_handler1:
|
|
EcFree (EcContextRetrieved);
|
|
exit_handler:
|
|
FreePool (BsKey);
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Generate the common key.
|
|
|
|
Note: The common key is freed when the caller frees the Context
|
|
by calling ArmCcaBootSyncCryptoDeleteKey().
|
|
|
|
@param[in] Context Pointer to the key handle.
|
|
@param[in] PeerContext Pointer to the peer key handle.
|
|
|
|
@retval EFI_INVALID_PARAMETER A parameter was invalid.
|
|
@retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
|
|
@retval EFI_ABORTED An operation failed.
|
|
@retval EFI_SUCCESS Success.
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
ArmCcaBootSyncCryptoGenerateCommonKey (
|
|
IN VOID *Context,
|
|
IN VOID *PeerContext
|
|
)
|
|
{
|
|
BOOLEAN Result;
|
|
EFI_STATUS Status;
|
|
|
|
ARM_CCA_BOOTSYNC_KEY_CONTEXT *BsKey;
|
|
ARM_CCA_BOOTSYNC_KEY_CONTEXT *BsKeyPeer;
|
|
UINT8 *CommKey;
|
|
UINTN CommKeySize;
|
|
|
|
if ((Context == NULL) ||
|
|
(PeerContext == NULL))
|
|
{
|
|
ASSERT (0);
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
BsKey = (ARM_CCA_BOOTSYNC_KEY_CONTEXT *)Context;
|
|
BsKeyPeer = (ARM_CCA_BOOTSYNC_KEY_CONTEXT *)PeerContext;
|
|
|
|
if ((BsKey->CommonKey != NULL) ||
|
|
(BsKey->CommonKeySize != 0))
|
|
{
|
|
ASSERT (0);
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
CommKeySize = ECC_CURVE_P384_PUB_KEY_SIZE;
|
|
CommKey = (UINT8 *)AllocateZeroPool (CommKeySize);
|
|
if (CommKey == NULL) {
|
|
ASSERT (0);
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
Result = EcDhComputeKey (
|
|
BsKey->EcContext,
|
|
BsKeyPeer->PublicKey,
|
|
BsKeyPeer->PublicKeySize,
|
|
NULL,
|
|
CommKey,
|
|
&CommKeySize
|
|
);
|
|
if (!Result) {
|
|
Status = EFI_ABORTED;
|
|
ASSERT (0);
|
|
goto exit_handler;
|
|
}
|
|
|
|
BsKey->CommonKey = CommKey;
|
|
BsKey->CommonKeySize = CommKeySize;
|
|
return EFI_SUCCESS;
|
|
|
|
exit_handler:
|
|
FreePool (CommKey);
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Derive keys using SHA512 HMAC-based Extract-and-Expand Key
|
|
Derivation Function (HKDF).
|
|
|
|
@param[in] Context Pointer to the key handle.
|
|
@param[in] Salt Pointer to the salt(non-secret) value.
|
|
@param[in] SaltSize Salt size in bytes.
|
|
@param[in] Info Pointer to the application specific info.
|
|
@param[in] InfoSize Info size in bytes.
|
|
@param[out] Out Pointer to buffer to receive hkdf value.
|
|
@param[in] OutSize Size of hkdf bytes to generate.
|
|
|
|
@retval TRUE Key derevation successful.
|
|
@retval FALSE Key derevation failed.
|
|
**/
|
|
BOOLEAN
|
|
EFIAPI
|
|
ArmCcaBootSyncCryptoDeriveKey (
|
|
IN VOID *Context,
|
|
IN CONST UINT8 *Salt,
|
|
IN UINTN SaltSize,
|
|
IN CONST UINT8 *Info,
|
|
IN UINTN InfoSize,
|
|
OUT UINT8 *Out,
|
|
IN UINTN OutSize
|
|
)
|
|
{
|
|
ARM_CCA_BOOTSYNC_KEY_CONTEXT *BsKey;
|
|
|
|
if ((Context == NULL) ||
|
|
(Salt == NULL) ||
|
|
(Info == NULL) ||
|
|
(Out == NULL))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
BsKey = (ARM_CCA_BOOTSYNC_KEY_CONTEXT *)Context;
|
|
|
|
if ((BsKey->CommonKey == NULL) ||
|
|
(BsKey->CommonKeySize == 0))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// Derive SHA512 HMAC-based Extract-and-Expand Key Derivation Function (HKDF).
|
|
return HkdfSha512ExtractAndExpand (
|
|
BsKey->CommonKey,
|
|
BsKey->CommonKeySize,
|
|
Salt,
|
|
SaltSize,
|
|
Info,
|
|
InfoSize,
|
|
Out,
|
|
OutSize
|
|
);
|
|
}
|
|
|
|
/**
|
|
Performs AEAD AES-GCM authenticated encryption on a data buffer and
|
|
additional authenticated data (AAD).
|
|
|
|
IvSize must be 12, otherwise FALSE is returned.
|
|
KeySize must be 16, 24 or 32, otherwise FALSE is returned.
|
|
TagSize must be 12, 13, 14, 15, 16, otherwise FALSE is returned.
|
|
|
|
@param[in] Key Pointer to the encryption key.
|
|
@param[in] KeySize Size of the encryption key in bytes.
|
|
@param[in] Iv Pointer to the IV value.
|
|
@param[in] IvSize Size of the IV value in bytes.
|
|
@param[in] AData Pointer to the additional authenticated data (AAD).
|
|
@param[in] ADataSize Size of the additional authenticated data (AAD) in bytes.
|
|
@param[in] DataIn Pointer to the input data buffer to be encrypted.
|
|
@param[in] DataInSize Size of the input data buffer in bytes.
|
|
@param[out] TagOut Pointer to a buffer that receives the authentication tag output.
|
|
@param[in] TagSize Size of the authentication tag in bytes.
|
|
@param[out] DataOut Pointer to a buffer that receives the encryption output.
|
|
@param[out] DataOutSize Size of the output data buffer in bytes.
|
|
|
|
@retval TRUE AEAD AES-GCM authenticated encryption succeeded.
|
|
@retval FALSE AEAD AES-GCM authenticated encryption failed.
|
|
**/
|
|
BOOLEAN
|
|
EFIAPI
|
|
ArmCcaBootSyncCryptoEncrypt (
|
|
IN CONST UINT8 *Key,
|
|
IN UINTN KeySize,
|
|
IN CONST UINT8 *Iv,
|
|
IN UINTN IvSize,
|
|
IN CONST UINT8 *AData,
|
|
IN UINTN ADataSize,
|
|
IN CONST UINT8 *DataIn,
|
|
IN UINTN DataInSize,
|
|
OUT UINT8 *TagOut,
|
|
IN UINTN TagSize,
|
|
OUT UINT8 *DataOut,
|
|
OUT UINTN *DataOutSize
|
|
)
|
|
{
|
|
return AeadAesGcmEncrypt (
|
|
Key,
|
|
KeySize,
|
|
Iv,
|
|
IvSize,
|
|
AData,
|
|
ADataSize,
|
|
DataIn,
|
|
DataInSize,
|
|
TagOut,
|
|
TagSize,
|
|
DataOut,
|
|
DataOutSize
|
|
);
|
|
}
|
|
|
|
/**
|
|
Performs AEAD AES-GCM authenticated decryption on a data buffer and
|
|
additional authenticated data (AAD).
|
|
|
|
IvSize must be 12, otherwise FALSE is returned.
|
|
KeySize must be 16, 24 or 32, otherwise FALSE is returned.
|
|
TagSize must be 12, 13, 14, 15, 16, otherwise FALSE is returned.
|
|
If additional authenticated data verification fails, FALSE is returned.
|
|
|
|
@param[in] Key Pointer to the encryption key.
|
|
@param[in] KeySize Size of the encryption key in bytes.
|
|
@param[in] Iv Pointer to the IV value.
|
|
@param[in] IvSize Size of the IV value in bytes.
|
|
@param[in] AData Pointer to the additional authenticated data (AAD).
|
|
@param[in] ADataSize Size of the additional authenticated data (AAD) in bytes.
|
|
@param[in] DataIn Pointer to the input data buffer to be decrypted.
|
|
@param[in] DataInSize Size of the input data buffer in bytes.
|
|
@param[in] Tag Pointer to a buffer that contains the authentication tag.
|
|
@param[in] TagSize Size of the authentication tag in bytes.
|
|
@param[out] DataOut Pointer to a buffer that receives the decryption output.
|
|
@param[out] DataOutSize Size of the output data buffer in bytes.
|
|
|
|
@retval TRUE AEAD AES-GCM authenticated decryption succeeded.
|
|
@retval FALSE AEAD AES-GCM authenticated decryption failed.
|
|
**/
|
|
BOOLEAN
|
|
EFIAPI
|
|
ArmCcaBootSyncCryptoDecrypt (
|
|
IN CONST UINT8 *Key,
|
|
IN UINTN KeySize,
|
|
IN CONST UINT8 *Iv,
|
|
IN UINTN IvSize,
|
|
IN CONST UINT8 *AData,
|
|
IN UINTN ADataSize,
|
|
IN CONST UINT8 *DataIn,
|
|
IN UINTN DataInSize,
|
|
IN CONST UINT8 *Tag,
|
|
IN UINTN TagSize,
|
|
OUT UINT8 *DataOut,
|
|
OUT UINTN *DataOutSize
|
|
)
|
|
{
|
|
return AeadAesGcmDecrypt (
|
|
Key,
|
|
KeySize,
|
|
Iv,
|
|
IvSize,
|
|
AData,
|
|
ADataSize,
|
|
DataIn,
|
|
DataInSize,
|
|
Tag,
|
|
TagSize,
|
|
DataOut,
|
|
DataOutSize
|
|
);
|
|
}
|
|
|
|
/**
|
|
Perform initialisation required for cryptographic operations.
|
|
|
|
Note: This API must be called once before any other APIs in this library
|
|
are used.
|
|
|
|
@retval EFI_ABORTED Failed to generate seed.
|
|
@retval EFI_SUCCESS Success.
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
ArmCcaBootSyncCryptoInit (
|
|
VOID
|
|
)
|
|
{
|
|
return ArmCcaInitRandomNumberSeed ();
|
|
}
|