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