edk2/ArmVirtPkg/Library/ArmCcaBootSyncCryptoLib/ArmCcaBootSyncCryptoLib.c
Sami Mujawar 3fa842faf9 ArmVirtPkg: Add Crypto helper library for Boot Sync
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>
2026-07-20 23:02:37 +00:00

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 ();
}