]>
git.proxmox.com Git - mirror_edk2.git/blob - SecurityPkg/RandomNumberGenerator/RngDxe/RdRand.c
2 Support routines for RDRAND instruction access.
4 Copyright (c) 2013, Intel Corporation. All rights reserved.<BR>
5 This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution. The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
19 // Bit mask used to determine if RdRand instruction is supported.
21 #define RDRAND_MASK 0x40000000
24 Determines whether or not RDRAND instruction is supported by the host hardware.
26 @retval EFI_SUCCESS RDRAND instruction supported.
27 @retval EFI_UNSUPPORTED RDRAND instruction not supported.
43 Status
= EFI_UNSUPPORTED
;
47 // Checks whether the current processor is an Intel product by CPUID.
49 AsmCpuid (0, &RegEax
, &RegEbx
, &RegEcx
, &RegEdx
);
50 if ((CompareMem ((CHAR8
*)(&RegEbx
), "Genu", 4) == 0) &&
51 (CompareMem ((CHAR8
*)(&RegEdx
), "ineI", 4) == 0) &&
52 (CompareMem ((CHAR8
*)(&RegEcx
), "ntel", 4) == 0)) {
58 // Determine RDRAND support by examining bit 30 of the ECX register returned by CPUID.
59 // A value of 1 indicates that processor supports RDRAND instruction.
61 AsmCpuid (1, 0, 0, &RegEcx
, 0);
63 if ((RegEcx
& RDRAND_MASK
) == RDRAND_MASK
) {
72 Calls RDRAND to obtain a 16-bit random number.
74 @param[out] Rand Buffer pointer to store the random result.
75 @param[in] NeedRetry Determine whether or not to loop retry.
77 @retval EFI_SUCCESS RDRAND call was successful.
78 @retval EFI_NOT_READY Failed attempts to call RDRAND.
92 RetryCount
= RETRY_LIMIT
;
98 // Perform a single call to RDRAND, or enter a loop call until RDRAND succeeds.
100 for (Index
= 0; Index
< RetryCount
; Index
++) {
101 if (RdRand16Step (Rand
)) {
106 return EFI_NOT_READY
;
110 Calls RDRAND to obtain a 32-bit random number.
112 @param[out] Rand Buffer pointer to store the random result.
113 @param[in] NeedRetry Determine whether or not to loop retry.
115 @retval EFI_SUCCESS RDRAND call was successful.
116 @retval EFI_NOT_READY Failed attempts to call RDRAND.
130 RetryCount
= RETRY_LIMIT
;
136 // Perform a single call to RDRAND, or enter a loop call until RDRAND succeeds.
138 for (Index
= 0; Index
< RetryCount
; Index
++) {
139 if (RdRand32Step (Rand
)) {
144 return EFI_NOT_READY
;
148 Calls RDRAND to obtain a 64-bit random number.
150 @param[out] Rand Buffer pointer to store the random result.
151 @param[in] NeedRetry Determine whether or not to loop retry.
153 @retval EFI_SUCCESS RDRAND call was successful.
154 @retval EFI_NOT_READY Failed attempts to call RDRAND.
168 RetryCount
= RETRY_LIMIT
;
174 // Perform a single call to RDRAND, or enter a loop call until RDRAND succeeds.
176 for (Index
= 0; Index
< RetryCount
; Index
++) {
177 if (RdRand64Step (Rand
)) {
182 return EFI_NOT_READY
;
186 Calls RDRAND to fill a buffer of arbitrary size with random bytes.
188 @param[in] Length Size of the buffer, in bytes, to fill with.
189 @param[out] RandBuffer Pointer to the buffer to store the random result.
191 @retval EFI_SUCCESS Random bytes generation succeeded.
192 @retval EFI_NOT_READY Failed to request random bytes.
199 OUT UINT8
*RandBuffer
204 UINT8
*ResidualStart
;
213 ResidualStart
= NULL
;
217 // Compute the address of the first word aligned (32/64-bit) block in the
218 // destination buffer, depending on whether we are in 32- or 64-bit mode.
221 if (((UINT32
)(UINTN
)Start
% (UINT32
)sizeof(UINTN
)) == 0) {
222 BlockStart
= (UINTN
*)Start
;
226 BlockStart
= (UINTN
*)(((UINTN
)Start
& ~(UINTN
)(sizeof(UINTN
) - 1)) + (UINTN
)sizeof(UINTN
));
227 Count
= Length
- (sizeof (UINTN
) - (UINT32
)((UINTN
)Start
% sizeof (UINTN
)));
228 StartLen
= (UINT32
)((UINTN
)BlockStart
- (UINTN
)Start
);
232 // Compute the number of word blocks and the remaining number of bytes.
234 Residual
= Count
% sizeof (UINTN
);
235 BlockNum
= Count
/ sizeof (UINTN
);
237 ResidualStart
= (UINT8
*) (BlockStart
+ BlockNum
);
241 // Obtain a temporary random number for use in the residuals. Failout if retry fails.
244 Status
= RdRandWord ((UINTN
*) &TempRand
, TRUE
);
245 if (EFI_ERROR (Status
)) {
251 // Populate the starting mis-aligned block.
253 for (Index
= 0; Index
< StartLen
; Index
++) {
254 Start
[Index
] = (UINT8
)(TempRand
& 0xff);
255 TempRand
= TempRand
>> 8;
259 // Populate the central aligned block. Fail out if retry fails.
261 Status
= RdRandGetWords (BlockNum
, (UINTN
*)(BlockStart
));
262 if (EFI_ERROR (Status
)) {
266 // Populate the final mis-aligned block.
269 Status
= RdRandWord ((UINTN
*)&TempRand
, TRUE
);
270 if (EFI_ERROR (Status
)) {
273 for (Index
= 0; Index
< Residual
; Index
++) {
274 ResidualStart
[Index
] = (UINT8
)(TempRand
& 0xff);
275 TempRand
= TempRand
>> 8;
283 Creates a 128bit random value that is fully forward and backward prediction resistant,
284 suitable for seeding a NIST SP800-90 Compliant, FIPS 1402-2 certifiable SW DRBG.
285 This function takes multiple random numbers through RDRAND without intervening
286 delays to ensure reseeding and performs AES-CBC-MAC over the data to compute the
289 @param[out] SeedBuffer Pointer to a 128bit buffer to store the random seed.
291 @retval EFI_SUCCESS Random seed generation succeeded.
292 @retval EFI_NOT_READY Failed to request random bytes.
298 OUT UINT8
*SeedBuffer
310 // Chose an arbitary key and zero the feed_forward_value (FFV)
312 for (Index
= 0; Index
< 16; Index
++) {
313 Key
[Index
] = (UINT8
) Index
;
318 // Perform CBC_MAC over 32 * 128 bit values, with 10us gaps between 128 bit value
319 // The 10us gaps will ensure multiple reseeds within the HW RNG with a large design margin.
321 for (Index
= 0; Index
< 32; Index
++) {
322 MicroSecondDelay (10);
323 Status
= RdRandGetBytes (16, RandByte
);
324 if (EFI_ERROR (Status
)) {
329 // Perform XOR operations on two 128-bit value.
331 for (Index2
= 0; Index2
< 16; Index2
++) {
332 Xored
[Index2
] = RandByte
[Index2
] ^ Ffv
[Index2
];
335 AesEncrypt (Key
, Xored
, Ffv
);
338 for (Index
= 0; Index
< 16; Index
++) {
339 SeedBuffer
[Index
] = Ffv
[Index
];
346 Generate high-quality entropy source through RDRAND.
348 @param[in] Length Size of the buffer, in bytes, to fill with.
349 @param[out] Entropy Pointer to the buffer to store the entropy data.
351 @retval EFI_SUCCESS Entropy generation succeeded.
352 @retval EFI_NOT_READY Failed to request random data.
357 RdRandGenerateEntropy (
367 Status
= EFI_NOT_READY
;
368 BlockCount
= Length
/ 16;
369 Ptr
= (UINT8
*)Entropy
;
372 // Generate high-quality seed for DRBG Entropy
374 while (BlockCount
> 0) {
375 Status
= RdRandGetSeed128 (Seed
);
376 if (EFI_ERROR (Status
)) {
379 CopyMem (Ptr
, Seed
, 16);
386 // Populate the remained data as request.
388 Status
= RdRandGetSeed128 (Seed
);
389 if (EFI_ERROR (Status
)) {
392 CopyMem (Ptr
, Seed
, (Length
% 16));