]> git.proxmox.com Git - mirror_edk2.git/blob - SecurityPkg/RandomNumberGenerator/RngDxe/RdRand.c
Add UEFI RNG Protocol support. The driver will leverage Intel Secure Key technology...
[mirror_edk2.git] / SecurityPkg / RandomNumberGenerator / RngDxe / RdRand.c
1 /** @file
2 Support routines for RDRAND instruction access.
3
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
9
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.
12
13 **/
14
15 #include "RdRand.h"
16 #include "AesCore.h"
17
18 //
19 // Bit mask used to determine if RdRand instruction is supported.
20 //
21 #define RDRAND_MASK 0x40000000
22
23 /**
24 Determines whether or not RDRAND instruction is supported by the host hardware.
25
26 @retval EFI_SUCCESS RDRAND instruction supported.
27 @retval EFI_UNSUPPORTED RDRAND instruction not supported.
28
29 **/
30 EFI_STATUS
31 EFIAPI
32 IsRdRandSupported (
33 VOID
34 )
35 {
36 EFI_STATUS Status;
37 UINT32 RegEax;
38 UINT32 RegEbx;
39 UINT32 RegEcx;
40 UINT32 RegEdx;
41 BOOLEAN IsIntelCpu;
42
43 Status = EFI_UNSUPPORTED;
44 IsIntelCpu = FALSE;
45
46 //
47 // Checks whether the current processor is an Intel product by CPUID.
48 //
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)) {
53 IsIntelCpu = TRUE;
54 }
55
56 if (IsIntelCpu) {
57 //
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.
60 //
61 AsmCpuid (1, 0, 0, &RegEcx, 0);
62
63 if ((RegEcx & RDRAND_MASK) == RDRAND_MASK) {
64 Status = EFI_SUCCESS;
65 }
66 }
67
68 return Status;
69 }
70
71 /**
72 Calls RDRAND to obtain a 16-bit random number.
73
74 @param[out] Rand Buffer pointer to store the random result.
75 @param[in] NeedRetry Determine whether or not to loop retry.
76
77 @retval EFI_SUCCESS RDRAND call was successful.
78 @retval EFI_NOT_READY Failed attempts to call RDRAND.
79
80 **/
81 EFI_STATUS
82 EFIAPI
83 RdRand16 (
84 OUT UINT16 *Rand,
85 IN BOOLEAN NeedRetry
86 )
87 {
88 UINT32 Index;
89 UINT32 RetryCount;
90
91 if (NeedRetry) {
92 RetryCount = RETRY_LIMIT;
93 } else {
94 RetryCount = 1;
95 }
96
97 //
98 // Perform a single call to RDRAND, or enter a loop call until RDRAND succeeds.
99 //
100 for (Index = 0; Index < RetryCount; Index++) {
101 if (RdRand16Step (Rand)) {
102 return EFI_SUCCESS;
103 }
104 }
105
106 return EFI_NOT_READY;
107 }
108
109 /**
110 Calls RDRAND to obtain a 32-bit random number.
111
112 @param[out] Rand Buffer pointer to store the random result.
113 @param[in] NeedRetry Determine whether or not to loop retry.
114
115 @retval EFI_SUCCESS RDRAND call was successful.
116 @retval EFI_NOT_READY Failed attempts to call RDRAND.
117
118 **/
119 EFI_STATUS
120 EFIAPI
121 RdRand32 (
122 OUT UINT32 *Rand,
123 IN BOOLEAN NeedRetry
124 )
125 {
126 UINT32 Index;
127 UINT32 RetryCount;
128
129 if (NeedRetry) {
130 RetryCount = RETRY_LIMIT;
131 } else {
132 RetryCount = 1;
133 }
134
135 //
136 // Perform a single call to RDRAND, or enter a loop call until RDRAND succeeds.
137 //
138 for (Index = 0; Index < RetryCount; Index++) {
139 if (RdRand32Step (Rand)) {
140 return EFI_SUCCESS;
141 }
142 }
143
144 return EFI_NOT_READY;
145 }
146
147 /**
148 Calls RDRAND to obtain a 64-bit random number.
149
150 @param[out] Rand Buffer pointer to store the random result.
151 @param[in] NeedRetry Determine whether or not to loop retry.
152
153 @retval EFI_SUCCESS RDRAND call was successful.
154 @retval EFI_NOT_READY Failed attempts to call RDRAND.
155
156 **/
157 EFI_STATUS
158 EFIAPI
159 RdRand64 (
160 OUT UINT64 *Rand,
161 IN BOOLEAN NeedRetry
162 )
163 {
164 UINT32 Index;
165 UINT32 RetryCount;
166
167 if (NeedRetry) {
168 RetryCount = RETRY_LIMIT;
169 } else {
170 RetryCount = 1;
171 }
172
173 //
174 // Perform a single call to RDRAND, or enter a loop call until RDRAND succeeds.
175 //
176 for (Index = 0; Index < RetryCount; Index++) {
177 if (RdRand64Step (Rand)) {
178 return EFI_SUCCESS;
179 }
180 }
181
182 return EFI_NOT_READY;
183 }
184
185 /**
186 Calls RDRAND to fill a buffer of arbitrary size with random bytes.
187
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.
190
191 @retval EFI_SUCCESS Random bytes generation succeeded.
192 @retval EFI_NOT_READY Failed to request random bytes.
193
194 **/
195 EFI_STATUS
196 EFIAPI
197 RdRandGetBytes (
198 IN UINTN Length,
199 OUT UINT8 *RandBuffer
200 )
201 {
202 EFI_STATUS Status;
203 UINT8 *Start;
204 UINT8 *ResidualStart;
205 UINTN *BlockStart;
206 UINTN TempRand;
207 UINTN Count;
208 UINTN Residual;
209 UINTN StartLen;
210 UINTN BlockNum;
211 UINTN Index;
212
213 ResidualStart = NULL;
214 TempRand = 0;
215
216 //
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.
219 //
220 Start = RandBuffer;
221 if (((UINT32)(UINTN)Start % (UINT32)sizeof(UINTN)) == 0) {
222 BlockStart = (UINTN *)Start;
223 Count = Length;
224 StartLen = 0;
225 } else {
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);
229 }
230
231 //
232 // Compute the number of word blocks and the remaining number of bytes.
233 //
234 Residual = Count % sizeof (UINTN);
235 BlockNum = Count / sizeof (UINTN);
236 if (Residual != 0) {
237 ResidualStart = (UINT8 *) (BlockStart + BlockNum);
238 }
239
240 //
241 // Obtain a temporary random number for use in the residuals. Failout if retry fails.
242 //
243 if (StartLen > 0) {
244 Status = RdRandWord ((UINTN *) &TempRand, TRUE);
245 if (EFI_ERROR (Status)) {
246 return Status;
247 }
248 }
249
250 //
251 // Populate the starting mis-aligned block.
252 //
253 for (Index = 0; Index < StartLen; Index++) {
254 Start[Index] = (UINT8)(TempRand & 0xff);
255 TempRand = TempRand >> 8;
256 }
257
258 //
259 // Populate the central aligned block. Fail out if retry fails.
260 //
261 Status = RdRandGetWords (BlockNum, (UINTN *)(BlockStart));
262 if (EFI_ERROR (Status)) {
263 return Status;
264 }
265 //
266 // Populate the final mis-aligned block.
267 //
268 if (Residual > 0) {
269 Status = RdRandWord ((UINTN *)&TempRand, TRUE);
270 if (EFI_ERROR (Status)) {
271 return Status;
272 }
273 for (Index = 0; Index < Residual; Index++) {
274 ResidualStart[Index] = (UINT8)(TempRand & 0xff);
275 TempRand = TempRand >> 8;
276 }
277 }
278
279 return EFI_SUCCESS;
280 }
281
282 /**
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
287 seed value.
288
289 @param[out] SeedBuffer Pointer to a 128bit buffer to store the random seed.
290
291 @retval EFI_SUCCESS Random seed generation succeeded.
292 @retval EFI_NOT_READY Failed to request random bytes.
293
294 **/
295 EFI_STATUS
296 EFIAPI
297 RdRandGetSeed128 (
298 OUT UINT8 *SeedBuffer
299 )
300 {
301 EFI_STATUS Status;
302 UINT8 RandByte[16];
303 UINT8 Key[16];
304 UINT8 Ffv[16];
305 UINT8 Xored[16];
306 UINT32 Index;
307 UINT32 Index2;
308
309 //
310 // Chose an arbitary key and zero the feed_forward_value (FFV)
311 //
312 for (Index = 0; Index < 16; Index++) {
313 Key[Index] = (UINT8) Index;
314 Ffv[Index] = 0;
315 }
316
317 //
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.
320 //
321 for (Index = 0; Index < 32; Index++) {
322 MicroSecondDelay (10);
323 Status = RdRandGetBytes (16, RandByte);
324 if (EFI_ERROR (Status)) {
325 return Status;
326 }
327
328 //
329 // Perform XOR operations on two 128-bit value.
330 //
331 for (Index2 = 0; Index2 < 16; Index2++) {
332 Xored[Index2] = RandByte[Index2] ^ Ffv[Index2];
333 }
334
335 AesEncrypt (Key, Xored, Ffv);
336 }
337
338 for (Index = 0; Index < 16; Index++) {
339 SeedBuffer[Index] = Ffv[Index];
340 }
341
342 return EFI_SUCCESS;
343 }
344
345 /**
346 Generate high-quality entropy source through RDRAND.
347
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.
350
351 @retval EFI_SUCCESS Entropy generation succeeded.
352 @retval EFI_NOT_READY Failed to request random data.
353
354 **/
355 EFI_STATUS
356 EFIAPI
357 RdRandGenerateEntropy (
358 IN UINTN Length,
359 OUT UINT8 *Entropy
360 )
361 {
362 EFI_STATUS Status;
363 UINTN BlockCount;
364 UINT8 Seed[16];
365 UINT8 *Ptr;
366
367 Status = EFI_NOT_READY;
368 BlockCount = Length / 16;
369 Ptr = (UINT8 *)Entropy;
370
371 //
372 // Generate high-quality seed for DRBG Entropy
373 //
374 while (BlockCount > 0) {
375 Status = RdRandGetSeed128 (Seed);
376 if (EFI_ERROR (Status)) {
377 return Status;
378 }
379 CopyMem (Ptr, Seed, 16);
380
381 BlockCount--;
382 Ptr = Ptr + 16;
383 }
384
385 //
386 // Populate the remained data as request.
387 //
388 Status = RdRandGetSeed128 (Seed);
389 if (EFI_ERROR (Status)) {
390 return Status;
391 }
392 CopyMem (Ptr, Seed, (Length % 16));
393
394 return Status;
395 }