| /** @file | |
| BaseRng Library that uses the TimerLib to provide reasonably random numbers. | |
| Do not use this on a production system. | |
| Copyright (c) 2023, Arm Limited. All rights reserved. | |
| Copyright (c) Microsoft Corporation. | |
| SPDX-License-Identifier: BSD-2-Clause-Patent | |
| **/ | |
| #include <Base.h> | |
| #include <Uefi.h> | |
| #include <Library/BaseLib.h> | |
| #include <Library/BaseMemoryLib.h> | |
| #include <Library/DebugLib.h> | |
| #include <Library/TimerLib.h> | |
| #include <Guid/RngAlgorithm.h> | |
| #define DEFAULT_DELAY_TIME_IN_MICROSECONDS 10 | |
| /** | |
| Using the TimerLib GetPerformanceCounterProperties() we delay | |
| for enough time for the PerformanceCounter to increment. | |
| If the return value from GetPerformanceCounterProperties (TimerLib) | |
| is zero, this function will return 10 and attempt to assert. | |
| **/ | |
| STATIC | |
| UINT32 | |
| CalculateMinimumDecentDelayInMicroseconds ( | |
| VOID | |
| ) | |
| { | |
| UINT64 CounterHz; | |
| // Get the counter properties | |
| CounterHz = GetPerformanceCounterProperties (NULL, NULL); | |
| // Make sure we won't divide by zero | |
| if (CounterHz == 0) { | |
| ASSERT (CounterHz != 0); // Assert so the developer knows something is wrong | |
| return DEFAULT_DELAY_TIME_IN_MICROSECONDS; | |
| } | |
| // Calculate the minimum delay based on 1.5 microseconds divided by the hertz. | |
| // We calculate the length of a cycle (1/CounterHz) and multiply it by 1.5 microseconds | |
| // This ensures that the performance counter has increased by at least one | |
| return (UINT32)(MAX (DivU64x64Remainder (1500000, CounterHz, NULL), 1)); | |
| } | |
| /** | |
| Generates a 16-bit random number. | |
| if Rand is NULL, then ASSERT(). | |
| @param[out] Rand Buffer pointer to store the 16-bit random value. | |
| @retval TRUE Random number generated successfully. | |
| @retval FALSE Failed to generate the random number. | |
| **/ | |
| BOOLEAN | |
| EFIAPI | |
| GetRandomNumber16 ( | |
| OUT UINT16 *Rand | |
| ) | |
| { | |
| UINT32 Index; | |
| UINT8 *RandPtr; | |
| UINT32 DelayInMicroSeconds; | |
| ASSERT (Rand != NULL); | |
| if (Rand == NULL) { | |
| return FALSE; | |
| } | |
| DelayInMicroSeconds = CalculateMinimumDecentDelayInMicroseconds (); | |
| RandPtr = (UINT8 *)Rand; | |
| // Get 2 bytes of random ish data | |
| for (Index = 0; Index < sizeof (UINT16); Index++) { | |
| *RandPtr = (UINT8)(GetPerformanceCounter () & 0xFF); | |
| // Delay to give the performance counter a chance to change | |
| MicroSecondDelay (DelayInMicroSeconds); | |
| RandPtr++; | |
| } | |
| return TRUE; | |
| } | |
| /** | |
| Generates a 32-bit random number. | |
| if Rand is NULL, then ASSERT(). | |
| @param[out] Rand Buffer pointer to store the 32-bit random value. | |
| @retval TRUE Random number generated successfully. | |
| @retval FALSE Failed to generate the random number. | |
| **/ | |
| BOOLEAN | |
| EFIAPI | |
| GetRandomNumber32 ( | |
| OUT UINT32 *Rand | |
| ) | |
| { | |
| UINT32 Index; | |
| UINT8 *RandPtr; | |
| UINT32 DelayInMicroSeconds; | |
| ASSERT (Rand != NULL); | |
| if (NULL == Rand) { | |
| return FALSE; | |
| } | |
| RandPtr = (UINT8 *)Rand; | |
| DelayInMicroSeconds = CalculateMinimumDecentDelayInMicroseconds (); | |
| // Get 4 bytes of random ish data | |
| for (Index = 0; Index < sizeof (UINT32); Index++) { | |
| *RandPtr = (UINT8)(GetPerformanceCounter () & 0xFF); | |
| // Delay to give the performance counter a chance to change | |
| MicroSecondDelay (DelayInMicroSeconds); | |
| RandPtr++; | |
| } | |
| return TRUE; | |
| } | |
| /** | |
| Generates a 64-bit random number. | |
| if Rand is NULL, then ASSERT(). | |
| @param[out] Rand Buffer pointer to store the 64-bit random value. | |
| @retval TRUE Random number generated successfully. | |
| @retval FALSE Failed to generate the random number. | |
| **/ | |
| BOOLEAN | |
| EFIAPI | |
| GetRandomNumber64 ( | |
| OUT UINT64 *Rand | |
| ) | |
| { | |
| UINT32 Index; | |
| UINT8 *RandPtr; | |
| UINT32 DelayInMicroSeconds; | |
| ASSERT (Rand != NULL); | |
| if (NULL == Rand) { | |
| return FALSE; | |
| } | |
| RandPtr = (UINT8 *)Rand; | |
| DelayInMicroSeconds = CalculateMinimumDecentDelayInMicroseconds (); | |
| // Get 8 bytes of random ish data | |
| for (Index = 0; Index < sizeof (UINT64); Index++) { | |
| *RandPtr = (UINT8)(GetPerformanceCounter () & 0xFF); | |
| // Delay to give the performance counter a chance to change | |
| MicroSecondDelay (DelayInMicroSeconds); | |
| RandPtr++; | |
| } | |
| return TRUE; | |
| } | |
| /** | |
| Generates a 128-bit random number. | |
| if Rand is NULL, then ASSERT(). | |
| @param[out] Rand Buffer pointer to store the 128-bit random value. | |
| @retval TRUE Random number generated successfully. | |
| @retval FALSE Failed to generate the random number. | |
| **/ | |
| BOOLEAN | |
| EFIAPI | |
| GetRandomNumber128 ( | |
| OUT UINT64 *Rand | |
| ) | |
| { | |
| ASSERT (Rand != NULL); | |
| // This should take around 80ms | |
| // Read first 64 bits | |
| if (!GetRandomNumber64 (Rand)) { | |
| return FALSE; | |
| } | |
| // Read second 64 bits | |
| return GetRandomNumber64 (++Rand); | |
| } | |
| /** | |
| Get a GUID identifying the RNG algorithm implementation. | |
| @param [out] RngGuid If success, contains the GUID identifying | |
| the RNG algorithm implementation. | |
| @retval EFI_SUCCESS Success. | |
| @retval EFI_UNSUPPORTED Not supported. | |
| @retval EFI_INVALID_PARAMETER Invalid parameter. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| GetRngGuid ( | |
| GUID *RngGuid | |
| ) | |
| { | |
| if (RngGuid == NULL) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| CopyMem (RngGuid, &gEdkiiRngAlgorithmUnSafe, sizeof (*RngGuid)); | |
| return EFI_SUCCESS; | |
| } |