2022-10-28 17:32:49 +02:00
|
|
|
/** @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,
|
2022-11-10 14:47:36 +01:00
|
|
|
&Guid
|
2022-10-28 17:32:49 +02:00
|
|
|
));
|
|
|
|
|
|
|
|
DEBUG_CODE_END ();
|
|
|
|
|
|
|
|
return RETURN_SUCCESS;
|
|
|
|
|
|
|
|
ErrorHandler:
|
|
|
|
DEBUG ((DEBUG_ERROR, "ArmTrngLib could not be correctly initialized.\n"));
|
|
|
|
return RETURN_SUCCESS;
|
|
|
|
}
|