/** @file | |
Arm Firmware TRNG interface library. | |
Copyright (c) 2021 - 2022, Arm Limited. All rights reserved.<BR> | |
SPDX-License-Identifier: BSD-2-Clause-Patent | |
@par Reference(s): | |
- [1] Arm True Random Number Generator Firmware, Interface 1.0, | |
Platform Design Document. | |
(https://developer.arm.com/documentation/den0098/latest/) | |
- [2] NIST Special Publication 800-90B, Recommendation for the Entropy | |
Sources Used for Random Bit Generation. | |
(https://csrc.nist.gov/publications/detail/sp/800-90b/final) | |
@par Glossary: | |
- TRNG - True Random Number Generator | |
- FID - Function ID | |
**/ | |
#include <Base.h> | |
#include <Library/ArmLib.h> | |
#include <Library/ArmMonitorLib.h> | |
#include <Library/BaseMemoryLib.h> | |
#include <Library/DebugLib.h> | |
#include "ArmTrngDefs.h" | |
/** Convert TRNG status codes to RETURN status codes. | |
@param [in] TrngStatus TRNG status code. | |
@retval RETURN_SUCCESS Success. | |
@retval RETURN_UNSUPPORTED Function not implemented or | |
negative return code. | |
@retval RETURN_INVALID_PARAMETER A parameter is invalid. | |
@retval RETURN_NOT_READY No Entropy available. | |
**/ | |
STATIC | |
RETURN_STATUS | |
TrngStatusToReturnStatus ( | |
IN INT32 TrngStatus | |
) | |
{ | |
switch (TrngStatus) { | |
case TRNG_STATUS_NOT_SUPPORTED: | |
return RETURN_UNSUPPORTED; | |
case TRNG_STATUS_INVALID_PARAMETER: | |
return RETURN_INVALID_PARAMETER; | |
case TRNG_STATUS_NO_ENTROPY: | |
return RETURN_NOT_READY; | |
case TRNG_STATUS_SUCCESS: | |
return RETURN_SUCCESS; | |
default: | |
if (TrngStatus < 0) { | |
return RETURN_UNSUPPORTED; | |
} | |
return RETURN_SUCCESS; | |
} | |
} | |
/** Get the version of the Arm TRNG backend. | |
A TRNG may be implemented by the system firmware, in which case this | |
function shall return the version of the Arm TRNG backend. | |
The implementation must return NOT_SUPPORTED if a Back end is not present. | |
@param [out] MajorRevision Major revision. | |
@param [out] MinorRevision Minor revision. | |
@retval RETURN_SUCCESS The function completed successfully. | |
@retval RETURN_INVALID_PARAMETER Invalid parameter. | |
@retval RETURN_UNSUPPORTED Backend not present. | |
**/ | |
RETURN_STATUS | |
EFIAPI | |
GetArmTrngVersion ( | |
OUT UINT16 *MajorRevision, | |
OUT UINT16 *MinorRevision | |
) | |
{ | |
RETURN_STATUS Status; | |
ARM_MONITOR_ARGS Parameters; | |
INT32 Revision; | |
if ((MajorRevision == NULL) || (MinorRevision == NULL)) { | |
return RETURN_INVALID_PARAMETER; | |
} | |
ZeroMem (&Parameters, sizeof (Parameters)); | |
Parameters.Arg0 = ARM_SMC_ID_TRNG_VERSION; | |
ArmMonitorCall (&Parameters); | |
Revision = (INT32)Parameters.Arg0; | |
Status = TrngStatusToReturnStatus (Revision); | |
if (RETURN_ERROR (Status)) { | |
return Status; | |
} | |
*MinorRevision = (Revision & TRNG_REV_MINOR_MASK); | |
*MajorRevision = ((Revision >> TRNG_REV_MAJOR_SHIFT) & TRNG_REV_MAJOR_MASK); | |
return RETURN_SUCCESS; | |
} | |
/** Get the features supported by the Arm TRNG backend. | |
The caller can determine if functions defined in the Arm TRNG ABI are | |
present in the ABI implementation. | |
@param [in] FunctionId Function Id. | |
@param [out] Capability Function specific capability if present. | |
@retval RETURN_SUCCESS The function completed successfully. | |
@retval RETURN_INVALID_PARAMETER Invalid parameter. | |
@retval RETURN_UNSUPPORTED Function not implemented. | |
**/ | |
STATIC | |
RETURN_STATUS | |
EFIAPI | |
GetArmTrngFeatures ( | |
IN CONST UINT32 FunctionId, | |
OUT UINT32 *Capability OPTIONAL | |
) | |
{ | |
ARM_MONITOR_ARGS Parameters; | |
RETURN_STATUS Status; | |
ZeroMem (&Parameters, sizeof (Parameters)); | |
Parameters.Arg0 = ARM_SMC_ID_TRNG_FEATURES; | |
Parameters.Arg1 = FunctionId; | |
ArmMonitorCall (&Parameters); | |
Status = TrngStatusToReturnStatus (Parameters.Arg0); | |
if (RETURN_ERROR (Status)) { | |
return Status; | |
} | |
if (Capability != NULL) { | |
*Capability = (UINT32)Parameters.Arg0; | |
} | |
return RETURN_SUCCESS; | |
} | |
/** Get the UUID of the Arm TRNG backend. | |
A TRNG may be implemented by the system firmware, in which case this | |
function shall return the UUID of the TRNG backend. | |
Returning the Arm TRNG UUID is optional and if not implemented, | |
RETURN_UNSUPPORTED shall be returned. | |
Note: The caller must not rely on the returned UUID as a trustworthy Arm TRNG | |
Back end identity | |
@param [out] Guid UUID of the Arm TRNG backend. | |
@retval RETURN_SUCCESS The function completed successfully. | |
@retval RETURN_INVALID_PARAMETER Invalid parameter. | |
@retval RETURN_UNSUPPORTED Function not implemented. | |
**/ | |
RETURN_STATUS | |
EFIAPI | |
GetArmTrngUuid ( | |
OUT GUID *Guid | |
) | |
{ | |
ARM_MONITOR_ARGS Parameters; | |
if (Guid == NULL) { | |
return RETURN_INVALID_PARAMETER; | |
} | |
ZeroMem (&Parameters, sizeof (Parameters)); | |
Parameters.Arg0 = ARM_SMC_ID_TRNG_GET_UUID; | |
ArmMonitorCall (&Parameters); | |
// Only invalid value is TRNG_STATUS_NOT_SUPPORTED (-1). | |
if ((INT32)Parameters.Arg0 == TRNG_STATUS_NOT_SUPPORTED) { | |
return TrngStatusToReturnStatus ((INT32)Parameters.Arg0); | |
} | |
Guid->Data1 = (Parameters.Arg0 & MAX_UINT32); | |
Guid->Data2 = (Parameters.Arg1 & MAX_UINT16); | |
Guid->Data3 = ((Parameters.Arg1 >> 16) & MAX_UINT16); | |
Guid->Data4[0] = (Parameters.Arg2 & MAX_UINT8); | |
Guid->Data4[1] = ((Parameters.Arg2 >> 8) & MAX_UINT8); | |
Guid->Data4[2] = ((Parameters.Arg2 >> 16) & MAX_UINT8); | |
Guid->Data4[3] = ((Parameters.Arg2 >> 24) & MAX_UINT8); | |
Guid->Data4[4] = (Parameters.Arg3 & MAX_UINT8); | |
Guid->Data4[5] = ((Parameters.Arg3 >> 8) & MAX_UINT8); | |
Guid->Data4[6] = ((Parameters.Arg3 >> 16) & MAX_UINT8); | |
Guid->Data4[7] = ((Parameters.Arg3 >> 24) & MAX_UINT8); | |
DEBUG ((DEBUG_INFO, "FW-TRNG: UUID %g\n", Guid)); | |
return RETURN_SUCCESS; | |
} | |
/** Returns maximum number of entropy bits that can be returned in a single | |
call. | |
@return Returns the maximum number of Entropy bits that can be returned | |
in a single call to GetArmTrngEntropy(). | |
**/ | |
UINTN | |
EFIAPI | |
GetArmTrngMaxSupportedEntropyBits ( | |
VOID | |
) | |
{ | |
return MAX_ENTROPY_BITS; | |
} | |
/** Returns N bits of conditioned entropy. | |
See [2] Section 2.3.1 GetEntropy: An Interface to the Entropy Source | |
GetEntropy | |
Input: | |
bits_of_entropy: the requested amount of entropy | |
Output: | |
entropy_bitstring: The string that provides the requested entropy. | |
status: A Boolean value that is TRUE if the request has been satisfied, | |
and is FALSE otherwise. | |
@param [in] EntropyBits Number of entropy bits requested. | |
@param [in] BufferSize Size of the Buffer in bytes. | |
@param [out] Buffer Buffer to return the entropy bits. | |
@retval RETURN_SUCCESS The function completed successfully. | |
@retval RETURN_INVALID_PARAMETER Invalid parameter. | |
@retval RETURN_UNSUPPORTED Function not implemented. | |
@retval RETURN_BAD_BUFFER_SIZE Buffer size is too small. | |
@retval RETURN_NOT_READY No Entropy available. | |
**/ | |
RETURN_STATUS | |
EFIAPI | |
GetArmTrngEntropy ( | |
IN UINTN EntropyBits, | |
IN UINTN BufferSize, | |
OUT UINT8 *Buffer | |
) | |
{ | |
RETURN_STATUS Status; | |
ARM_MONITOR_ARGS Parameters; | |
UINTN EntropyBytes; | |
UINTN LastValidBits; | |
UINTN BytesToClear; | |
UINTN EntropyData[3]; | |
if ((EntropyBits == 0) || | |
(EntropyBits > MAX_ENTROPY_BITS) || | |
(Buffer == NULL)) | |
{ | |
return RETURN_INVALID_PARAMETER; | |
} | |
EntropyBytes = (EntropyBits + 7) >> 3; | |
if (EntropyBytes > BufferSize) { | |
return RETURN_BAD_BUFFER_SIZE; | |
} | |
ZeroMem (Buffer, BufferSize); | |
ZeroMem (&Parameters, sizeof (Parameters)); | |
Parameters.Arg0 = ARM_SMC_ID_TRNG_RND; | |
Parameters.Arg1 = EntropyBits; | |
ArmMonitorCall (&Parameters); | |
Status = TrngStatusToReturnStatus ((INT32)Parameters.Arg0); | |
if (RETURN_ERROR (Status)) { | |
return Status; | |
} | |
// The entropy data is returned in the Parameters.Arg<3..1> | |
// With the lower order bytes in Parameters.Arg3 and the higher | |
// order bytes being stored in Parameters.Arg1. | |
EntropyData[0] = Parameters.Arg3; | |
EntropyData[1] = Parameters.Arg2; | |
EntropyData[2] = Parameters.Arg1; | |
CopyMem (Buffer, EntropyData, EntropyBytes); | |
// Mask off any unused top bytes, in accordance with specification. | |
BytesToClear = BufferSize - EntropyBytes; | |
if (BytesToClear != 0) { | |
ZeroMem (&Buffer[EntropyBytes], BytesToClear); | |
} | |
// Clear the unused MSB bits of the last byte. | |
LastValidBits = EntropyBits & 0x7; | |
if (LastValidBits != 0) { | |
Buffer[EntropyBytes - 1] &= (0xFF >> (8 - LastValidBits)); | |
} | |
return Status; | |
} | |
/** The constructor checks that the FW-TRNG interface is supported | |
by the host firmware. | |
It will ASSERT() if FW-TRNG is not supported. | |
It will always return RETURN_SUCCESS. | |
@retval RETURN_SUCCESS The constructor always returns RETURN_SUCCESS. | |
**/ | |
RETURN_STATUS | |
EFIAPI | |
ArmTrngLibConstructor ( | |
VOID | |
) | |
{ | |
ARM_MONITOR_ARGS Parameters; | |
RETURN_STATUS Status; | |
UINT16 MajorRev; | |
UINT16 MinorRev; | |
GUID Guid; | |
ZeroMem (&Parameters, sizeof (Parameters)); | |
Parameters.Arg0 = SMCCC_VERSION; | |
ArmMonitorCall (&Parameters); | |
Status = TrngStatusToReturnStatus ((INT32)Parameters.Arg0); | |
if (RETURN_ERROR (Status)) { | |
goto ErrorHandler; | |
} | |
// Cf [1] s2.1.3 'Caller responsibilities', | |
// SMCCC version must be greater or equal than 1.1 | |
if ((INT32)Parameters.Arg0 < 0x10001) { | |
goto ErrorHandler; | |
} | |
Status = GetArmTrngVersion (&MajorRev, &MinorRev); | |
if (RETURN_ERROR (Status)) { | |
goto ErrorHandler; | |
} | |
// Check that the required features are present. | |
Status = GetArmTrngFeatures (ARM_SMC_ID_TRNG_RND, NULL); | |
if (RETURN_ERROR (Status)) { | |
goto ErrorHandler; | |
} | |
// Check if TRNG UUID is supported and if so trace the GUID. | |
Status = GetArmTrngFeatures (ARM_SMC_ID_TRNG_GET_UUID, NULL); | |
if (RETURN_ERROR (Status)) { | |
goto ErrorHandler; | |
} | |
DEBUG_CODE_BEGIN (); | |
Status = GetArmTrngUuid (&Guid); | |
if (RETURN_ERROR (Status)) { | |
goto ErrorHandler; | |
} | |
DEBUG (( | |
DEBUG_INFO, | |
"FW-TRNG: Version %d.%d, GUID {%g}\n", | |
MajorRev, | |
MinorRev, | |
&Guid | |
)); | |
DEBUG_CODE_END (); | |
return RETURN_SUCCESS; | |
ErrorHandler: | |
DEBUG ((DEBUG_ERROR, "ArmTrngLib could not be correctly initialized.\n")); | |
return RETURN_SUCCESS; | |
} |