| /** @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; | |
| } |