Add UEFI RNG Protocol support. The driver will leverage Intel Secure Key technology to produce the Random Number Generator protocol, which is used to provide high-quality random numbers for use in applications, or entropy for seeding other random number generators. Refer to http://software.intel.com/en-us/articles/intel-digital-random-number-generator-drng-software-implementation-guide/ for more information about Intel Secure Key technology.
Signed-off-by: Long, Qin <qin.long@intel.com> Reviewed-by: Fu, Siyuan <siyuan.fu@intel.com> Reviewed-by: Rosenbaum, Lee G <lee.g.rosenbaum@intel.com> git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@14858 6f19259b-4bc3-4df7-8a09-765794883524
This commit is contained in:
395
SecurityPkg/RandomNumberGenerator/RngDxe/RdRand.c
Normal file
395
SecurityPkg/RandomNumberGenerator/RngDxe/RdRand.c
Normal file
@@ -0,0 +1,395 @@
|
||||
/** @file
|
||||
Support routines for RDRAND instruction access.
|
||||
|
||||
Copyright (c) 2013, Intel Corporation. All rights reserved.<BR>
|
||||
This program and the accompanying materials
|
||||
are licensed and made available under the terms and conditions of the BSD License
|
||||
which accompanies this distribution. The full text of the license may be found at
|
||||
http://opensource.org/licenses/bsd-license.php
|
||||
|
||||
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
|
||||
|
||||
**/
|
||||
|
||||
#include "RdRand.h"
|
||||
#include "AesCore.h"
|
||||
|
||||
//
|
||||
// Bit mask used to determine if RdRand instruction is supported.
|
||||
//
|
||||
#define RDRAND_MASK 0x40000000
|
||||
|
||||
/**
|
||||
Determines whether or not RDRAND instruction is supported by the host hardware.
|
||||
|
||||
@retval EFI_SUCCESS RDRAND instruction supported.
|
||||
@retval EFI_UNSUPPORTED RDRAND instruction not supported.
|
||||
|
||||
**/
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
IsRdRandSupported (
|
||||
VOID
|
||||
)
|
||||
{
|
||||
EFI_STATUS Status;
|
||||
UINT32 RegEax;
|
||||
UINT32 RegEbx;
|
||||
UINT32 RegEcx;
|
||||
UINT32 RegEdx;
|
||||
BOOLEAN IsIntelCpu;
|
||||
|
||||
Status = EFI_UNSUPPORTED;
|
||||
IsIntelCpu = FALSE;
|
||||
|
||||
//
|
||||
// Checks whether the current processor is an Intel product by CPUID.
|
||||
//
|
||||
AsmCpuid (0, &RegEax, &RegEbx, &RegEcx, &RegEdx);
|
||||
if ((CompareMem ((CHAR8 *)(&RegEbx), "Genu", 4) == 0) &&
|
||||
(CompareMem ((CHAR8 *)(&RegEdx), "ineI", 4) == 0) &&
|
||||
(CompareMem ((CHAR8 *)(&RegEcx), "ntel", 4) == 0)) {
|
||||
IsIntelCpu = TRUE;
|
||||
}
|
||||
|
||||
if (IsIntelCpu) {
|
||||
//
|
||||
// Determine RDRAND support by examining bit 30 of the ECX register returned by CPUID.
|
||||
// A value of 1 indicates that processor supports RDRAND instruction.
|
||||
//
|
||||
AsmCpuid (1, 0, 0, &RegEcx, 0);
|
||||
|
||||
if ((RegEcx & RDRAND_MASK) == RDRAND_MASK) {
|
||||
Status = EFI_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
/**
|
||||
Calls RDRAND to obtain a 16-bit random number.
|
||||
|
||||
@param[out] Rand Buffer pointer to store the random result.
|
||||
@param[in] NeedRetry Determine whether or not to loop retry.
|
||||
|
||||
@retval EFI_SUCCESS RDRAND call was successful.
|
||||
@retval EFI_NOT_READY Failed attempts to call RDRAND.
|
||||
|
||||
**/
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
RdRand16 (
|
||||
OUT UINT16 *Rand,
|
||||
IN BOOLEAN NeedRetry
|
||||
)
|
||||
{
|
||||
UINT32 Index;
|
||||
UINT32 RetryCount;
|
||||
|
||||
if (NeedRetry) {
|
||||
RetryCount = RETRY_LIMIT;
|
||||
} else {
|
||||
RetryCount = 1;
|
||||
}
|
||||
|
||||
//
|
||||
// Perform a single call to RDRAND, or enter a loop call until RDRAND succeeds.
|
||||
//
|
||||
for (Index = 0; Index < RetryCount; Index++) {
|
||||
if (RdRand16Step (Rand)) {
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
return EFI_NOT_READY;
|
||||
}
|
||||
|
||||
/**
|
||||
Calls RDRAND to obtain a 32-bit random number.
|
||||
|
||||
@param[out] Rand Buffer pointer to store the random result.
|
||||
@param[in] NeedRetry Determine whether or not to loop retry.
|
||||
|
||||
@retval EFI_SUCCESS RDRAND call was successful.
|
||||
@retval EFI_NOT_READY Failed attempts to call RDRAND.
|
||||
|
||||
**/
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
RdRand32 (
|
||||
OUT UINT32 *Rand,
|
||||
IN BOOLEAN NeedRetry
|
||||
)
|
||||
{
|
||||
UINT32 Index;
|
||||
UINT32 RetryCount;
|
||||
|
||||
if (NeedRetry) {
|
||||
RetryCount = RETRY_LIMIT;
|
||||
} else {
|
||||
RetryCount = 1;
|
||||
}
|
||||
|
||||
//
|
||||
// Perform a single call to RDRAND, or enter a loop call until RDRAND succeeds.
|
||||
//
|
||||
for (Index = 0; Index < RetryCount; Index++) {
|
||||
if (RdRand32Step (Rand)) {
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
return EFI_NOT_READY;
|
||||
}
|
||||
|
||||
/**
|
||||
Calls RDRAND to obtain a 64-bit random number.
|
||||
|
||||
@param[out] Rand Buffer pointer to store the random result.
|
||||
@param[in] NeedRetry Determine whether or not to loop retry.
|
||||
|
||||
@retval EFI_SUCCESS RDRAND call was successful.
|
||||
@retval EFI_NOT_READY Failed attempts to call RDRAND.
|
||||
|
||||
**/
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
RdRand64 (
|
||||
OUT UINT64 *Rand,
|
||||
IN BOOLEAN NeedRetry
|
||||
)
|
||||
{
|
||||
UINT32 Index;
|
||||
UINT32 RetryCount;
|
||||
|
||||
if (NeedRetry) {
|
||||
RetryCount = RETRY_LIMIT;
|
||||
} else {
|
||||
RetryCount = 1;
|
||||
}
|
||||
|
||||
//
|
||||
// Perform a single call to RDRAND, or enter a loop call until RDRAND succeeds.
|
||||
//
|
||||
for (Index = 0; Index < RetryCount; Index++) {
|
||||
if (RdRand64Step (Rand)) {
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
return EFI_NOT_READY;
|
||||
}
|
||||
|
||||
/**
|
||||
Calls RDRAND to fill a buffer of arbitrary size with random bytes.
|
||||
|
||||
@param[in] Length Size of the buffer, in bytes, to fill with.
|
||||
@param[out] RandBuffer Pointer to the buffer to store the random result.
|
||||
|
||||
@retval EFI_SUCCESS Random bytes generation succeeded.
|
||||
@retval EFI_NOT_READY Failed to request random bytes.
|
||||
|
||||
**/
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
RdRandGetBytes (
|
||||
IN UINTN Length,
|
||||
OUT UINT8 *RandBuffer
|
||||
)
|
||||
{
|
||||
EFI_STATUS Status;
|
||||
UINT8 *Start;
|
||||
UINT8 *ResidualStart;
|
||||
UINTN *BlockStart;
|
||||
UINTN TempRand;
|
||||
UINTN Count;
|
||||
UINTN Residual;
|
||||
UINTN StartLen;
|
||||
UINTN BlockNum;
|
||||
UINTN Index;
|
||||
|
||||
ResidualStart = NULL;
|
||||
TempRand = 0;
|
||||
|
||||
//
|
||||
// Compute the address of the first word aligned (32/64-bit) block in the
|
||||
// destination buffer, depending on whether we are in 32- or 64-bit mode.
|
||||
//
|
||||
Start = RandBuffer;
|
||||
if (((UINT32)(UINTN)Start % (UINT32)sizeof(UINTN)) == 0) {
|
||||
BlockStart = (UINTN *)Start;
|
||||
Count = Length;
|
||||
StartLen = 0;
|
||||
} else {
|
||||
BlockStart = (UINTN *)(((UINTN)Start & ~(UINTN)(sizeof(UINTN) - 1)) + (UINTN)sizeof(UINTN));
|
||||
Count = Length - (sizeof (UINTN) - (UINT32)((UINTN)Start % sizeof (UINTN)));
|
||||
StartLen = (UINT32)((UINTN)BlockStart - (UINTN)Start);
|
||||
}
|
||||
|
||||
//
|
||||
// Compute the number of word blocks and the remaining number of bytes.
|
||||
//
|
||||
Residual = Count % sizeof (UINTN);
|
||||
BlockNum = Count / sizeof (UINTN);
|
||||
if (Residual != 0) {
|
||||
ResidualStart = (UINT8 *) (BlockStart + BlockNum);
|
||||
}
|
||||
|
||||
//
|
||||
// Obtain a temporary random number for use in the residuals. Failout if retry fails.
|
||||
//
|
||||
if (StartLen > 0) {
|
||||
Status = RdRandWord ((UINTN *) &TempRand, TRUE);
|
||||
if (EFI_ERROR (Status)) {
|
||||
return Status;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Populate the starting mis-aligned block.
|
||||
//
|
||||
for (Index = 0; Index < StartLen; Index++) {
|
||||
Start[Index] = (UINT8)(TempRand & 0xff);
|
||||
TempRand = TempRand >> 8;
|
||||
}
|
||||
|
||||
//
|
||||
// Populate the central aligned block. Fail out if retry fails.
|
||||
//
|
||||
Status = RdRandGetWords (BlockNum, (UINTN *)(BlockStart));
|
||||
if (EFI_ERROR (Status)) {
|
||||
return Status;
|
||||
}
|
||||
//
|
||||
// Populate the final mis-aligned block.
|
||||
//
|
||||
if (Residual > 0) {
|
||||
Status = RdRandWord ((UINTN *)&TempRand, TRUE);
|
||||
if (EFI_ERROR (Status)) {
|
||||
return Status;
|
||||
}
|
||||
for (Index = 0; Index < Residual; Index++) {
|
||||
ResidualStart[Index] = (UINT8)(TempRand & 0xff);
|
||||
TempRand = TempRand >> 8;
|
||||
}
|
||||
}
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
Creates a 128bit random value that is fully forward and backward prediction resistant,
|
||||
suitable for seeding a NIST SP800-90 Compliant, FIPS 1402-2 certifiable SW DRBG.
|
||||
This function takes multiple random numbers through RDRAND without intervening
|
||||
delays to ensure reseeding and performs AES-CBC-MAC over the data to compute the
|
||||
seed value.
|
||||
|
||||
@param[out] SeedBuffer Pointer to a 128bit buffer to store the random seed.
|
||||
|
||||
@retval EFI_SUCCESS Random seed generation succeeded.
|
||||
@retval EFI_NOT_READY Failed to request random bytes.
|
||||
|
||||
**/
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
RdRandGetSeed128 (
|
||||
OUT UINT8 *SeedBuffer
|
||||
)
|
||||
{
|
||||
EFI_STATUS Status;
|
||||
UINT8 RandByte[16];
|
||||
UINT8 Key[16];
|
||||
UINT8 Ffv[16];
|
||||
UINT8 Xored[16];
|
||||
UINT32 Index;
|
||||
UINT32 Index2;
|
||||
|
||||
//
|
||||
// Chose an arbitary key and zero the feed_forward_value (FFV)
|
||||
//
|
||||
for (Index = 0; Index < 16; Index++) {
|
||||
Key[Index] = (UINT8) Index;
|
||||
Ffv[Index] = 0;
|
||||
}
|
||||
|
||||
//
|
||||
// Perform CBC_MAC over 32 * 128 bit values, with 10us gaps between 128 bit value
|
||||
// The 10us gaps will ensure multiple reseeds within the HW RNG with a large design margin.
|
||||
//
|
||||
for (Index = 0; Index < 32; Index++) {
|
||||
MicroSecondDelay (10);
|
||||
Status = RdRandGetBytes (16, RandByte);
|
||||
if (EFI_ERROR (Status)) {
|
||||
return Status;
|
||||
}
|
||||
|
||||
//
|
||||
// Perform XOR operations on two 128-bit value.
|
||||
//
|
||||
for (Index2 = 0; Index2 < 16; Index2++) {
|
||||
Xored[Index2] = RandByte[Index2] ^ Ffv[Index2];
|
||||
}
|
||||
|
||||
AesEncrypt (Key, Xored, Ffv);
|
||||
}
|
||||
|
||||
for (Index = 0; Index < 16; Index++) {
|
||||
SeedBuffer[Index] = Ffv[Index];
|
||||
}
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
Generate high-quality entropy source through RDRAND.
|
||||
|
||||
@param[in] Length Size of the buffer, in bytes, to fill with.
|
||||
@param[out] Entropy Pointer to the buffer to store the entropy data.
|
||||
|
||||
@retval EFI_SUCCESS Entropy generation succeeded.
|
||||
@retval EFI_NOT_READY Failed to request random data.
|
||||
|
||||
**/
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
RdRandGenerateEntropy (
|
||||
IN UINTN Length,
|
||||
OUT UINT8 *Entropy
|
||||
)
|
||||
{
|
||||
EFI_STATUS Status;
|
||||
UINTN BlockCount;
|
||||
UINT8 Seed[16];
|
||||
UINT8 *Ptr;
|
||||
|
||||
Status = EFI_NOT_READY;
|
||||
BlockCount = Length / 16;
|
||||
Ptr = (UINT8 *)Entropy;
|
||||
|
||||
//
|
||||
// Generate high-quality seed for DRBG Entropy
|
||||
//
|
||||
while (BlockCount > 0) {
|
||||
Status = RdRandGetSeed128 (Seed);
|
||||
if (EFI_ERROR (Status)) {
|
||||
return Status;
|
||||
}
|
||||
CopyMem (Ptr, Seed, 16);
|
||||
|
||||
BlockCount--;
|
||||
Ptr = Ptr + 16;
|
||||
}
|
||||
|
||||
//
|
||||
// Populate the remained data as request.
|
||||
//
|
||||
Status = RdRandGetSeed128 (Seed);
|
||||
if (EFI_ERROR (Status)) {
|
||||
return Status;
|
||||
}
|
||||
CopyMem (Ptr, Seed, (Length % 16));
|
||||
|
||||
return Status;
|
||||
}
|
Reference in New Issue
Block a user