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
RdRandGetBytes(IN UINTN Length,OUT UINT8 * RandBuffer)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
RdRandGetSeed128(OUT UINT8 * SeedBuffer)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
RdRandGenerateEntropy(IN UINTN Length,OUT UINT8 * Entropy)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