1 /** @file 2 Support routines for RDRAND instruction access. 3 4 Copyright (c) 2013, Intel Corporation. All rights reserved.<BR> 5 (C) Copyright 2015 Hewlett Packard Enterprise Development LP<BR> 6 This program and the accompanying materials 7 are licensed and made available under the terms and conditions of the BSD License 8 which accompanies this distribution. The full text of the license may be found at 9 http://opensource.org/licenses/bsd-license.php 10 11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, 12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. 13 14 **/ 15 #include <Library/RngLib.h> 16 17 #include "RdRand.h" 18 #include "AesCore.h" 19 20 /** 21 Calls RDRAND to fill a buffer of arbitrary size with random bytes. 22 23 @param[in] Length Size of the buffer, in bytes, to fill with. 24 @param[out] RandBuffer Pointer to the buffer to store the random result. 25 26 @retval EFI_SUCCESS Random bytes generation succeeded. 27 @retval EFI_NOT_READY Failed to request random bytes. 28 29 **/ 30 EFI_STATUS 31 EFIAPI 32 RdRandGetBytes ( 33 IN UINTN Length, 34 OUT UINT8 *RandBuffer 35 ) 36 { 37 BOOLEAN IsRandom; 38 UINT64 TempRand[2]; 39 40 while (Length > 0) { 41 IsRandom = GetRandomNumber128 (TempRand); 42 if (!IsRandom) { 43 return EFI_NOT_READY; 44 } 45 if (Length >= sizeof (TempRand)) { 46 WriteUnaligned64 ((UINT64*)RandBuffer, TempRand[0]); 47 RandBuffer += sizeof (UINT64); 48 WriteUnaligned64 ((UINT64*)RandBuffer, TempRand[1]); 49 RandBuffer += sizeof (UINT64); 50 Length -= sizeof (TempRand); 51 } else { 52 CopyMem (RandBuffer, TempRand, Length); 53 Length = 0; 54 } 55 } 56 57 return EFI_SUCCESS; 58 } 59 60 /** 61 Creates a 128bit random value that is fully forward and backward prediction resistant, 62 suitable for seeding a NIST SP800-90 Compliant, FIPS 1402-2 certifiable SW DRBG. 63 This function takes multiple random numbers through RDRAND without intervening 64 delays to ensure reseeding and performs AES-CBC-MAC over the data to compute the 65 seed value. 66 67 @param[out] SeedBuffer Pointer to a 128bit buffer to store the random seed. 68 69 @retval EFI_SUCCESS Random seed generation succeeded. 70 @retval EFI_NOT_READY Failed to request random bytes. 71 72 **/ 73 EFI_STATUS 74 EFIAPI 75 RdRandGetSeed128 ( 76 OUT UINT8 *SeedBuffer 77 ) 78 { 79 EFI_STATUS Status; 80 UINT8 RandByte[16]; 81 UINT8 Key[16]; 82 UINT8 Ffv[16]; 83 UINT8 Xored[16]; 84 UINT32 Index; 85 UINT32 Index2; 86 87 // 88 // Chose an arbitary key and zero the feed_forward_value (FFV) 89 // 90 for (Index = 0; Index < 16; Index++) { 91 Key[Index] = (UINT8) Index; 92 Ffv[Index] = 0; 93 } 94 95 // 96 // Perform CBC_MAC over 32 * 128 bit values, with 10us gaps between 128 bit value 97 // The 10us gaps will ensure multiple reseeds within the HW RNG with a large design margin. 98 // 99 for (Index = 0; Index < 32; Index++) { 100 MicroSecondDelay (10); 101 Status = RdRandGetBytes (16, RandByte); 102 if (EFI_ERROR (Status)) { 103 return Status; 104 } 105 106 // 107 // Perform XOR operations on two 128-bit value. 108 // 109 for (Index2 = 0; Index2 < 16; Index2++) { 110 Xored[Index2] = RandByte[Index2] ^ Ffv[Index2]; 111 } 112 113 AesEncrypt (Key, Xored, Ffv); 114 } 115 116 for (Index = 0; Index < 16; Index++) { 117 SeedBuffer[Index] = Ffv[Index]; 118 } 119 120 return EFI_SUCCESS; 121 } 122 123 /** 124 Generate high-quality entropy source through RDRAND. 125 126 @param[in] Length Size of the buffer, in bytes, to fill with. 127 @param[out] Entropy Pointer to the buffer to store the entropy data. 128 129 @retval EFI_SUCCESS Entropy generation succeeded. 130 @retval EFI_NOT_READY Failed to request random data. 131 132 **/ 133 EFI_STATUS 134 EFIAPI 135 RdRandGenerateEntropy ( 136 IN UINTN Length, 137 OUT UINT8 *Entropy 138 ) 139 { 140 EFI_STATUS Status; 141 UINTN BlockCount; 142 UINT8 Seed[16]; 143 UINT8 *Ptr; 144 145 Status = EFI_NOT_READY; 146 BlockCount = Length / 16; 147 Ptr = (UINT8 *)Entropy; 148 149 // 150 // Generate high-quality seed for DRBG Entropy 151 // 152 while (BlockCount > 0) { 153 Status = RdRandGetSeed128 (Seed); 154 if (EFI_ERROR (Status)) { 155 return Status; 156 } 157 CopyMem (Ptr, Seed, 16); 158 159 BlockCount--; 160 Ptr = Ptr + 16; 161 } 162 163 // 164 // Populate the remained data as request. 165 // 166 Status = RdRandGetSeed128 (Seed); 167 if (EFI_ERROR (Status)) { 168 return Status; 169 } 170 CopyMem (Ptr, Seed, (Length % 16)); 171 172 return Status; 173 } 174