/** @file | |
OpenSSL_1_1_1b doesn't implement rand_pool_* functions for UEFI. | |
The file implement these functions. | |
Copyright (c) 2019, Intel Corporation. All rights reserved.<BR> | |
SPDX-License-Identifier: BSD-2-Clause-Patent | |
**/ | |
#include "internal/rand_int.h" | |
#include <openssl/aes.h> | |
#include <Uefi.h> | |
#include <Library/TimerLib.h> | |
#include "rand_pool_noise.h" | |
/** | |
Get some randomness from low-order bits of GetPerformanceCounter results. | |
And combine them to the 64-bit value | |
@param[out] Rand Buffer pointer to store the 64-bit random value. | |
@retval TRUE Random number generated successfully. | |
@retval FALSE Failed to generate. | |
**/ | |
STATIC | |
BOOLEAN | |
EFIAPI | |
GetRandNoise64FromPerformanceCounter( | |
OUT UINT64 *Rand | |
) | |
{ | |
UINT32 Index; | |
UINT32 *RandPtr; | |
if (NULL == Rand) { | |
return FALSE; | |
} | |
RandPtr = (UINT32 *) Rand; | |
for (Index = 0; Index < 2; Index ++) { | |
*RandPtr = (UINT32) (GetPerformanceCounter () & 0xFF); | |
MicroSecondDelay (10); | |
RandPtr++; | |
} | |
return TRUE; | |
} | |
/** | |
Calls RandomNumber64 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. | |
**/ | |
STATIC | |
BOOLEAN | |
EFIAPI | |
RandGetBytes ( | |
IN UINTN Length, | |
OUT UINT8 *RandBuffer | |
) | |
{ | |
BOOLEAN Ret; | |
UINT64 TempRand; | |
Ret = FALSE; | |
while (Length > 0) { | |
// | |
// Get random noise from platform. | |
// If it failed, fallback to PerformanceCounter | |
// If you really care about security, you must override | |
// GetRandomNoise64FromPlatform. | |
// | |
Ret = GetRandomNoise64 (&TempRand); | |
if (Ret == FALSE) { | |
Ret = GetRandNoise64FromPerformanceCounter (&TempRand); | |
} | |
if (!Ret) { | |
return Ret; | |
} | |
if (Length >= sizeof (TempRand)) { | |
*((UINT64*) RandBuffer) = TempRand; | |
RandBuffer += sizeof (UINT64); | |
Length -= sizeof (TempRand); | |
} else { | |
CopyMem (RandBuffer, &TempRand, Length); | |
Length = 0; | |
} | |
} | |
return Ret; | |
} | |
/** | |
Creates a 128bit random value that is fully forward and backward prediction resistant, | |
suitable for seeding a NIST SP800-90 Compliant. | |
This function takes multiple random numbers from PerformanceCounter 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 TRUE Random seed generation succeeded. | |
@retval FALSE Failed to request random bytes. | |
**/ | |
STATIC | |
BOOLEAN | |
EFIAPI | |
RandGetSeed128 ( | |
OUT UINT8 *SeedBuffer | |
) | |
{ | |
BOOLEAN Ret; | |
UINT8 RandByte[16]; | |
UINT8 Key[16]; | |
UINT8 Ffv[16]; | |
UINT8 Xored[16]; | |
UINT32 Index; | |
UINT32 Index2; | |
AES_KEY AESKey; | |
// | |
// Chose an arbitary key and zero the feed_forward_value (FFV) | |
// | |
for (Index = 0; Index < 16; Index++) { | |
Key[Index] = (UINT8) Index; | |
Ffv[Index] = 0; | |
} | |
AES_set_encrypt_key (Key, 16 * 8, &AESKey); | |
// | |
// Perform CBC_MAC over 32 * 128 bit values, with 10us gaps between 128 bit value | |
// The 10us gaps will ensure multiple reseeds within the system time with a large | |
// design margin. | |
// | |
for (Index = 0; Index < 32; Index++) { | |
MicroSecondDelay (10); | |
Ret = RandGetBytes (16, RandByte); | |
if (!Ret) { | |
return Ret; | |
} | |
// | |
// Perform XOR operations on two 128-bit value. | |
// | |
for (Index2 = 0; Index2 < 16; Index2++) { | |
Xored[Index2] = RandByte[Index2] ^ Ffv[Index2]; | |
} | |
AES_encrypt (Xored, Ffv, &AESKey); | |
} | |
for (Index = 0; Index < 16; Index++) { | |
SeedBuffer[Index] = Ffv[Index]; | |
} | |
return Ret; | |
} | |
/** | |
Generate high-quality entropy source. | |
@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. | |
**/ | |
STATIC | |
BOOLEAN | |
EFIAPI | |
RandGenerateEntropy ( | |
IN UINTN Length, | |
OUT UINT8 *Entropy | |
) | |
{ | |
BOOLEAN Ret; | |
UINTN BlockCount; | |
UINT8 Seed[16]; | |
UINT8 *Ptr; | |
BlockCount = Length / 16; | |
Ptr = (UINT8 *) Entropy; | |
// | |
// Generate high-quality seed for DRBG Entropy | |
// | |
while (BlockCount > 0) { | |
Ret = RandGetSeed128 (Seed); | |
if (!Ret) { | |
return Ret; | |
} | |
CopyMem (Ptr, Seed, 16); | |
BlockCount--; | |
Ptr = Ptr + 16; | |
} | |
// | |
// Populate the remained data as request. | |
// | |
Ret = RandGetSeed128 (Seed); | |
if (!Ret) { | |
return Ret; | |
} | |
CopyMem (Ptr, Seed, (Length % 16)); | |
return Ret; | |
} | |
/* | |
* Add random bytes to the pool to acquire requested amount of entropy | |
* | |
* This function is platform specific and tries to acquire the requested | |
* amount of entropy by polling platform specific entropy sources. | |
* | |
* This is OpenSSL required interface. | |
*/ | |
size_t rand_pool_acquire_entropy(RAND_POOL *pool) | |
{ | |
BOOLEAN Ret; | |
size_t bytes_needed; | |
unsigned char * buffer; | |
bytes_needed = rand_pool_bytes_needed(pool, 1 /*entropy_factor*/); | |
if (bytes_needed > 0) { | |
buffer = rand_pool_add_begin(pool, bytes_needed); | |
if (buffer != NULL) { | |
Ret = RandGenerateEntropy(bytes_needed, buffer); | |
if (FALSE == Ret) { | |
rand_pool_add_end(pool, 0, 0); | |
} else { | |
rand_pool_add_end(pool, bytes_needed, 8 * bytes_needed); | |
} | |
} | |
} | |
return rand_pool_entropy_available(pool); | |
} | |
/* | |
* Implementation for UEFI | |
* | |
* This is OpenSSL required interface. | |
*/ | |
int rand_pool_add_nonce_data(RAND_POOL *pool) | |
{ | |
struct { | |
UINT64 Rand; | |
UINT64 TimerValue; | |
} data = { 0 }; | |
RandGetBytes(8, (UINT8 *)&(data.Rand)); | |
data.TimerValue = GetPerformanceCounter(); | |
return rand_pool_add(pool, (unsigned char*)&data, sizeof(data), 0); | |
} | |
/* | |
* Implementation for UEFI | |
* | |
* This is OpenSSL required interface. | |
*/ | |
int rand_pool_add_additional_data(RAND_POOL *pool) | |
{ | |
struct { | |
UINT64 Rand; | |
UINT64 TimerValue; | |
} data = { 0 }; | |
RandGetBytes(8, (UINT8 *)&(data.Rand)); | |
data.TimerValue = GetPerformanceCounter(); | |
return rand_pool_add(pool, (unsigned char*)&data, sizeof(data), 0); | |
} | |
/* | |
* Dummy Implememtation for UEFI | |
* | |
* This is OpenSSL required interface. | |
*/ | |
int rand_pool_init(void) | |
{ | |
return 1; | |
} | |
/* | |
* Dummy Implememtation for UEFI | |
* | |
* This is OpenSSL required interface. | |
*/ | |
void rand_pool_cleanup(void) | |
{ | |
} | |
/* | |
* Dummy Implememtation for UEFI | |
* | |
* This is OpenSSL required interface. | |
*/ | |
void rand_pool_keep_random_devices_open(int keep) | |
{ | |
} | |