mirror of https://github.com/acidanthera/audk.git
396 lines
9.6 KiB
C
396 lines
9.6 KiB
C
|
/** @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;
|
||
|
}
|