/** @file | |
Support routines for RDRAND instruction access. | |
Copyright (c) 2013, Intel Corporation. All rights reserved.<BR> | |
(C) Copyright 2015 Hewlett Packard Enterprise Development LP<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 <Library/RngLib.h> | |
#include "RdRand.h" | |
#include "AesCore.h" | |
/** | |
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 | |
) | |
{ | |
BOOLEAN IsRandom; | |
UINT64 TempRand[2]; | |
while (Length > 0) { | |
IsRandom = GetRandomNumber128 (TempRand); | |
if (!IsRandom) { | |
return EFI_NOT_READY; | |
} | |
if (Length >= sizeof (TempRand)) { | |
WriteUnaligned64 ((UINT64*)RandBuffer, TempRand[0]); | |
RandBuffer += sizeof (UINT64); | |
WriteUnaligned64 ((UINT64*)RandBuffer, TempRand[1]); | |
RandBuffer += sizeof (UINT64); | |
Length -= sizeof (TempRand); | |
} else { | |
CopyMem (RandBuffer, TempRand, Length); | |
Length = 0; | |
} | |
} | |
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; | |
} |