]> git.proxmox.com Git - mirror_edk2.git/blame - SecurityPkg/RandomNumberGenerator/RngDxe/RdRand.c
MdePkg: Create GetRandomNumber128 in RngLib
[mirror_edk2.git] / SecurityPkg / RandomNumberGenerator / RngDxe / RdRand.c
CommitLineData
3aa8dc6c
LQ
1/** @file\r
2 Support routines for RDRAND instruction access.\r
3\r
4Copyright (c) 2013, Intel Corporation. All rights reserved.<BR>\r
5This program and the accompanying materials\r
6are licensed and made available under the terms and conditions of the BSD License\r
7which accompanies this distribution. The full text of the license may be found at\r
8http://opensource.org/licenses/bsd-license.php\r
9\r
10THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
11WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
12\r
13**/\r
14\r
15#include "RdRand.h"\r
16#include "AesCore.h"\r
17\r
18//\r
19// Bit mask used to determine if RdRand instruction is supported.\r
20//\r
21#define RDRAND_MASK 0x40000000\r
22\r
23/**\r
24 Determines whether or not RDRAND instruction is supported by the host hardware.\r
25\r
26 @retval EFI_SUCCESS RDRAND instruction supported.\r
27 @retval EFI_UNSUPPORTED RDRAND instruction not supported.\r
28\r
29**/\r
30EFI_STATUS\r
31EFIAPI\r
32IsRdRandSupported (\r
33 VOID\r
34 )\r
35{\r
36 EFI_STATUS Status;\r
37 UINT32 RegEax;\r
38 UINT32 RegEbx;\r
39 UINT32 RegEcx;\r
40 UINT32 RegEdx;\r
41 BOOLEAN IsIntelCpu;\r
42\r
43 Status = EFI_UNSUPPORTED;\r
44 IsIntelCpu = FALSE;\r
45 \r
46 //\r
47 // Checks whether the current processor is an Intel product by CPUID.\r
48 //\r
49 AsmCpuid (0, &RegEax, &RegEbx, &RegEcx, &RegEdx);\r
50 if ((CompareMem ((CHAR8 *)(&RegEbx), "Genu", 4) == 0) &&\r
51 (CompareMem ((CHAR8 *)(&RegEdx), "ineI", 4) == 0) &&\r
52 (CompareMem ((CHAR8 *)(&RegEcx), "ntel", 4) == 0)) {\r
53 IsIntelCpu = TRUE;\r
54 }\r
55\r
56 if (IsIntelCpu) {\r
57 //\r
58 // Determine RDRAND support by examining bit 30 of the ECX register returned by CPUID.\r
59 // A value of 1 indicates that processor supports RDRAND instruction.\r
60 //\r
61 AsmCpuid (1, 0, 0, &RegEcx, 0);\r
62\r
63 if ((RegEcx & RDRAND_MASK) == RDRAND_MASK) {\r
64 Status = EFI_SUCCESS;\r
65 }\r
66 }\r
67\r
68 return Status;\r
69}\r
70\r
71/**\r
72 Calls RDRAND to obtain a 16-bit random number.\r
73\r
74 @param[out] Rand Buffer pointer to store the random result.\r
75 @param[in] NeedRetry Determine whether or not to loop retry.\r
76\r
77 @retval EFI_SUCCESS RDRAND call was successful.\r
78 @retval EFI_NOT_READY Failed attempts to call RDRAND.\r
79\r
80**/\r
81EFI_STATUS\r
82EFIAPI\r
83RdRand16 (\r
84 OUT UINT16 *Rand,\r
85 IN BOOLEAN NeedRetry\r
86 )\r
87{\r
88 UINT32 Index;\r
89 UINT32 RetryCount;\r
90\r
91 if (NeedRetry) {\r
92 RetryCount = RETRY_LIMIT;\r
93 } else {\r
94 RetryCount = 1;\r
95 }\r
96\r
97 //\r
98 // Perform a single call to RDRAND, or enter a loop call until RDRAND succeeds.\r
99 //\r
100 for (Index = 0; Index < RetryCount; Index++) {\r
101 if (RdRand16Step (Rand)) {\r
102 return EFI_SUCCESS;\r
103 }\r
104 }\r
105 \r
106 return EFI_NOT_READY;\r
107}\r
108\r
109/**\r
110 Calls RDRAND to obtain a 32-bit random number.\r
111\r
112 @param[out] Rand Buffer pointer to store the random result.\r
113 @param[in] NeedRetry Determine whether or not to loop retry.\r
114\r
115 @retval EFI_SUCCESS RDRAND call was successful.\r
116 @retval EFI_NOT_READY Failed attempts to call RDRAND.\r
117\r
118**/\r
119EFI_STATUS\r
120EFIAPI\r
121RdRand32 (\r
122 OUT UINT32 *Rand,\r
123 IN BOOLEAN NeedRetry\r
124 )\r
125{\r
126 UINT32 Index;\r
127 UINT32 RetryCount;\r
128\r
129 if (NeedRetry) {\r
130 RetryCount = RETRY_LIMIT;\r
131 } else {\r
132 RetryCount = 1;\r
133 }\r
134\r
135 //\r
136 // Perform a single call to RDRAND, or enter a loop call until RDRAND succeeds.\r
137 //\r
138 for (Index = 0; Index < RetryCount; Index++) {\r
139 if (RdRand32Step (Rand)) {\r
140 return EFI_SUCCESS;\r
141 }\r
142 }\r
143 \r
144 return EFI_NOT_READY;\r
145}\r
146\r
147/**\r
148 Calls RDRAND to obtain a 64-bit random number.\r
149\r
150 @param[out] Rand Buffer pointer to store the random result.\r
151 @param[in] NeedRetry Determine whether or not to loop retry.\r
152\r
153 @retval EFI_SUCCESS RDRAND call was successful.\r
154 @retval EFI_NOT_READY Failed attempts to call RDRAND.\r
155\r
156**/\r
157EFI_STATUS\r
158EFIAPI\r
159RdRand64 (\r
160 OUT UINT64 *Rand,\r
161 IN BOOLEAN NeedRetry\r
162 )\r
163{\r
164 UINT32 Index;\r
165 UINT32 RetryCount;\r
166\r
167 if (NeedRetry) {\r
168 RetryCount = RETRY_LIMIT;\r
169 } else {\r
170 RetryCount = 1;\r
171 }\r
172\r
173 //\r
174 // Perform a single call to RDRAND, or enter a loop call until RDRAND succeeds.\r
175 //\r
176 for (Index = 0; Index < RetryCount; Index++) {\r
177 if (RdRand64Step (Rand)) {\r
178 return EFI_SUCCESS;\r
179 }\r
180 }\r
181 \r
182 return EFI_NOT_READY;\r
183}\r
184\r
185/**\r
186 Calls RDRAND to fill a buffer of arbitrary size with random bytes.\r
187\r
188 @param[in] Length Size of the buffer, in bytes, to fill with.\r
189 @param[out] RandBuffer Pointer to the buffer to store the random result.\r
190\r
191 @retval EFI_SUCCESS Random bytes generation succeeded.\r
192 @retval EFI_NOT_READY Failed to request random bytes.\r
193\r
194**/\r
195EFI_STATUS\r
196EFIAPI\r
197RdRandGetBytes (\r
198 IN UINTN Length,\r
199 OUT UINT8 *RandBuffer\r
200 )\r
201{\r
202 EFI_STATUS Status;\r
203 UINT8 *Start;\r
204 UINT8 *ResidualStart;\r
205 UINTN *BlockStart;\r
206 UINTN TempRand;\r
207 UINTN Count;\r
208 UINTN Residual;\r
209 UINTN StartLen;\r
210 UINTN BlockNum;\r
211 UINTN Index;\r
212\r
213 ResidualStart = NULL;\r
214 TempRand = 0;\r
215\r
216 //\r
217 // Compute the address of the first word aligned (32/64-bit) block in the \r
218 // destination buffer, depending on whether we are in 32- or 64-bit mode.\r
219 //\r
220 Start = RandBuffer;\r
221 if (((UINT32)(UINTN)Start % (UINT32)sizeof(UINTN)) == 0) {\r
222 BlockStart = (UINTN *)Start;\r
223 Count = Length;\r
224 StartLen = 0;\r
225 } else {\r
226 BlockStart = (UINTN *)(((UINTN)Start & ~(UINTN)(sizeof(UINTN) - 1)) + (UINTN)sizeof(UINTN));\r
227 Count = Length - (sizeof (UINTN) - (UINT32)((UINTN)Start % sizeof (UINTN)));\r
228 StartLen = (UINT32)((UINTN)BlockStart - (UINTN)Start);\r
229 }\r
230\r
231 //\r
232 // Compute the number of word blocks and the remaining number of bytes.\r
233 //\r
234 Residual = Count % sizeof (UINTN);\r
235 BlockNum = Count / sizeof (UINTN);\r
236 if (Residual != 0) {\r
237 ResidualStart = (UINT8 *) (BlockStart + BlockNum);\r
238 }\r
239\r
240 //\r
241 // Obtain a temporary random number for use in the residuals. Failout if retry fails.\r
242 //\r
243 if (StartLen > 0) {\r
244 Status = RdRandWord ((UINTN *) &TempRand, TRUE);\r
245 if (EFI_ERROR (Status)) {\r
246 return Status;\r
247 }\r
248 }\r
249\r
250 //\r
251 // Populate the starting mis-aligned block.\r
252 //\r
253 for (Index = 0; Index < StartLen; Index++) {\r
254 Start[Index] = (UINT8)(TempRand & 0xff);\r
255 TempRand = TempRand >> 8;\r
256 }\r
257\r
258 //\r
259 // Populate the central aligned block. Fail out if retry fails.\r
260 //\r
261 Status = RdRandGetWords (BlockNum, (UINTN *)(BlockStart));\r
262 if (EFI_ERROR (Status)) {\r
263 return Status;\r
264 }\r
265 //\r
266 // Populate the final mis-aligned block.\r
267 //\r
268 if (Residual > 0) {\r
269 Status = RdRandWord ((UINTN *)&TempRand, TRUE);\r
270 if (EFI_ERROR (Status)) {\r
271 return Status;\r
272 }\r
273 for (Index = 0; Index < Residual; Index++) {\r
274 ResidualStart[Index] = (UINT8)(TempRand & 0xff);\r
275 TempRand = TempRand >> 8;\r
276 }\r
277 }\r
278\r
279 return EFI_SUCCESS;\r
280}\r
281\r
282/**\r
283 Creates a 128bit random value that is fully forward and backward prediction resistant,\r
284 suitable for seeding a NIST SP800-90 Compliant, FIPS 1402-2 certifiable SW DRBG.\r
285 This function takes multiple random numbers through RDRAND without intervening\r
286 delays to ensure reseeding and performs AES-CBC-MAC over the data to compute the\r
287 seed value.\r
288 \r
289 @param[out] SeedBuffer Pointer to a 128bit buffer to store the random seed.\r
290\r
291 @retval EFI_SUCCESS Random seed generation succeeded.\r
292 @retval EFI_NOT_READY Failed to request random bytes.\r
293\r
294**/\r
295EFI_STATUS\r
296EFIAPI\r
297RdRandGetSeed128 (\r
298 OUT UINT8 *SeedBuffer\r
299 )\r
300{\r
301 EFI_STATUS Status;\r
302 UINT8 RandByte[16];\r
303 UINT8 Key[16];\r
304 UINT8 Ffv[16];\r
305 UINT8 Xored[16];\r
306 UINT32 Index;\r
307 UINT32 Index2;\r
308\r
309 //\r
310 // Chose an arbitary key and zero the feed_forward_value (FFV)\r
311 //\r
312 for (Index = 0; Index < 16; Index++) {\r
313 Key[Index] = (UINT8) Index;\r
314 Ffv[Index] = 0;\r
315 }\r
316\r
317 //\r
318 // Perform CBC_MAC over 32 * 128 bit values, with 10us gaps between 128 bit value\r
319 // The 10us gaps will ensure multiple reseeds within the HW RNG with a large design margin.\r
320 //\r
321 for (Index = 0; Index < 32; Index++) {\r
322 MicroSecondDelay (10);\r
323 Status = RdRandGetBytes (16, RandByte);\r
324 if (EFI_ERROR (Status)) {\r
325 return Status;\r
326 }\r
327\r
328 //\r
329 // Perform XOR operations on two 128-bit value.\r
330 //\r
331 for (Index2 = 0; Index2 < 16; Index2++) {\r
332 Xored[Index2] = RandByte[Index2] ^ Ffv[Index2];\r
333 }\r
334\r
335 AesEncrypt (Key, Xored, Ffv);\r
336 }\r
337\r
338 for (Index = 0; Index < 16; Index++) {\r
339 SeedBuffer[Index] = Ffv[Index];\r
340 }\r
341\r
342 return EFI_SUCCESS;\r
343}\r
344\r
345/**\r
346 Generate high-quality entropy source through RDRAND.\r
347\r
348 @param[in] Length Size of the buffer, in bytes, to fill with.\r
349 @param[out] Entropy Pointer to the buffer to store the entropy data.\r
350\r
351 @retval EFI_SUCCESS Entropy generation succeeded.\r
352 @retval EFI_NOT_READY Failed to request random data.\r
353\r
354**/\r
355EFI_STATUS\r
356EFIAPI\r
357RdRandGenerateEntropy (\r
358 IN UINTN Length,\r
359 OUT UINT8 *Entropy\r
360 )\r
361{\r
362 EFI_STATUS Status;\r
363 UINTN BlockCount;\r
364 UINT8 Seed[16];\r
365 UINT8 *Ptr;\r
366\r
367 Status = EFI_NOT_READY;\r
368 BlockCount = Length / 16;\r
369 Ptr = (UINT8 *)Entropy;\r
370\r
371 //\r
372 // Generate high-quality seed for DRBG Entropy\r
373 //\r
374 while (BlockCount > 0) {\r
375 Status = RdRandGetSeed128 (Seed);\r
376 if (EFI_ERROR (Status)) {\r
377 return Status;\r
378 }\r
379 CopyMem (Ptr, Seed, 16);\r
380\r
381 BlockCount--;\r
382 Ptr = Ptr + 16;\r
383 }\r
384\r
385 //\r
386 // Populate the remained data as request.\r
387 //\r
388 Status = RdRandGetSeed128 (Seed);\r
389 if (EFI_ERROR (Status)) {\r
390 return Status;\r
391 }\r
392 CopyMem (Ptr, Seed, (Length % 16));\r
393\r
394 return Status;\r
395}\r