iPXE
efi_entropy.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 of the
7  * License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17  * 02110-1301, USA.
18  *
19  * You can also choose to distribute this program under the terms of
20  * the Unmodified Binary Distribution Licence (as given in the file
21  * COPYING.UBDL), provided that you have satisfied its requirements.
22  */
23 
24 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
25 
26 #include <errno.h>
27 #include <ipxe/entropy.h>
28 #include <ipxe/crc32.h>
29 #include <ipxe/profile.h>
30 #include <ipxe/efi/efi.h>
31 #include <ipxe/efi/Protocol/Rng.h>
32 
33 /** @file
34  *
35  * EFI entropy source
36  *
37  */
38 
39 /** Random number generator protocol */
42 
43 /** Minimum number of bytes to request from RNG
44  *
45  * The UEFI spec states (for no apparently good reason) that "When a
46  * Deterministic Random Bit Generator (DRBG) is used on the output of
47  * a (raw) entropy source, its security level must be at least 256
48  * bits." The EDK2 codebase (mis)interprets this to mean that the
49  * call to GetRNG() should fail if given a buffer less than 32 bytes.
50  *
51  * Incidentally, nothing in the EFI RNG protocol provides any way to
52  * report the actual amount of entropy returned by GetRNG().
53  */
54 #define EFI_ENTROPY_RNG_LEN 32
55 
56 /** Time (in 100ns units) to delay waiting for timer tick
57  *
58  * In theory, UEFI allows us to specify a trigger time of zero to
59  * simply wait for the next timer tick. In practice, specifying zero
60  * seems to often return immediately, which produces almost no
61  * entropy. Specify a delay of 1000ns to try to force an existent
62  * delay.
63  */
64 #define EFI_ENTROPY_TRIGGER_TIME 10
65 
66 /** Event used to wait for timer tick */
67 static EFI_EVENT tick;
68 
69 /**
70  * Enable entropy gathering
71  *
72  * @ret rc Return status code
73  */
74 static int efi_entropy_enable ( void ) {
76  EFI_STATUS efirc;
77  int rc;
78 
79  DBGC ( &tick, "ENTROPY %s RNG protocol\n",
80  ( efirng ? "has" : "has no" ) );
81 
82  /* Drop to external TPL to allow timer tick event to take place */
84 
85  /* Create timer tick event */
86  if ( ( efirc = bs->CreateEvent ( EVT_TIMER, TPL_NOTIFY, NULL, NULL,
87  &tick ) ) != 0 ) {
88  rc = -EEFI ( efirc );
89  DBGC ( &tick, "ENTROPY could not create event: %s\n",
90  strerror ( rc ) );
91  return rc;
92  }
93 
94  return 0;
95 }
96 
97 /**
98  * Disable entropy gathering
99  *
100  */
101 static void efi_entropy_disable ( void ) {
103 
104  /* Close timer tick event */
105  bs->CloseEvent ( tick );
106 
107  /* Return to TPL_CALLBACK */
108  bs->RaiseTPL ( TPL_CALLBACK );
109 }
110 
111 /**
112  * Wait for a timer tick
113  *
114  * @ret low CPU profiling low-order bits, or negative error
115  */
116 static int efi_entropy_tick ( void ) {
118  UINTN index;
119  uint16_t low;
120  EFI_STATUS efirc;
121  int rc;
122 
123  /* Wait for next timer tick */
124  if ( ( efirc = bs->SetTimer ( tick, TimerRelative,
125  EFI_ENTROPY_TRIGGER_TIME ) ) != 0 ) {
126  rc = -EEFI ( efirc );
127  DBGC ( &tick, "ENTROPY could not set timer: %s\n",
128  strerror ( rc ) );
129  return rc;
130  }
131  if ( ( efirc = bs->WaitForEvent ( 1, &tick, &index ) ) != 0 ) {
132  rc = -EEFI ( efirc );
133  DBGC ( &tick, "ENTROPY could not wait for timer tick: %s\n",
134  strerror ( rc ) );
135  return rc;
136  }
137 
138  /* Get current CPU profiling timestamp low-order bits */
139  low = profile_timestamp();
140 
141  return low;
142 }
143 
144 /**
145  * Get noise sample from timer ticks
146  *
147  * @ret noise Noise sample
148  * @ret rc Return status code
149  */
150 static int efi_get_noise_ticks ( noise_sample_t *noise ) {
151  int before;
152  int after;
153  int rc;
154 
155  /* Wait for a timer tick */
156  before = efi_entropy_tick();
157  if ( before < 0 ) {
158  rc = before;
159  return rc;
160  }
161 
162  /* Wait for another timer tick */
163  after = efi_entropy_tick();
164  if ( after < 0 ) {
165  rc = after;
166  return rc;
167  }
168 
169  /* Use TSC delta as noise sample */
170  *noise = ( after - before );
171 
172  return 0;
173 }
174 
175 /**
176  * Get noise sample from RNG protocol
177  *
178  * @ret noise Noise sample
179  * @ret rc Return status code
180  */
181 static int efi_get_noise_rng ( noise_sample_t *noise ) {
182  static uint8_t prev[EFI_ENTROPY_RNG_LEN];
184  EFI_STATUS efirc;
185  int rc;
186 
187  /* Fail if we have no EFI RNG protocol */
188  if ( ! efirng )
189  return -ENOTSUP;
190 
191  /* Get the minimum allowed number of random bytes */
192  if ( ( efirc = efirng->GetRNG ( efirng, NULL, EFI_ENTROPY_RNG_LEN,
193  buf ) ) != 0 ) {
194  rc = -EEFI ( efirc );
195  DBGC ( &tick, "ENTROPY could not read from RNG: %s\n",
196  strerror ( rc ) );
197  return rc;
198  }
199 
200  /* Fail (and permanently disable the EFI RNG) if we get
201  * consecutive identical results.
202  */
203  if ( memcmp ( buf, prev, sizeof ( buf ) ) == 0 ) {
204  DBGC ( &tick, "ENTROPY detected broken EFI RNG:\n" );
205  DBGC_HDA ( &tick, 0, buf, sizeof ( buf ) );
206  efirng = NULL;
207  return -EIO;
208  }
209  memcpy ( prev, buf, sizeof ( prev ) );
210 
211  /* Reduce random bytes to a single noise sample. This seems
212  * like overkill, but we have no way of knowing how much
213  * entropy is actually present in the bytes returned by the
214  * RNG protocol.
215  */
216  *noise = crc32_le ( 0, buf, sizeof ( buf ) );
217 
218  return 0;
219 }
220 
221 /**
222  * Get noise sample
223  *
224  * @ret noise Noise sample
225  * @ret rc Return status code
226  */
227 static int efi_get_noise ( noise_sample_t *noise ) {
228  int rc;
229 
230  /* Try RNG first, falling back to timer ticks */
231  if ( ( ( rc = efi_get_noise_rng ( noise ) ) != 0 ) &&
232  ( ( rc = efi_get_noise_ticks ( noise ) ) != 0 ) )
233  return rc;
234 
235  return 0;
236 }
237 
EFI_BOOT_SERVICES * BootServices
A pointer to the EFI Boot Services Table.
Definition: UefiSpec.h:2000
EFI_RNG_PROTOCOL as defined in UEFI 2.4.
struct arbelprm_rc_send_wqe rc
Definition: arbel.h:14
unsigned short uint16_t
Definition: stdint.h:11
static int efi_entropy_tick(void)
Wait for a timer tick.
Definition: efi_entropy.c:116
EFI_REQUEST_PROTOCOL(EFI_RNG_PROTOCOL, &efirng)
uint32_t low
Low 16 bits of address.
Definition: myson.h:19
void entropy_disable(void)
Disable entropy gathering.
#define EEFI(efirc)
Convert an EFI status code to an iPXE status code.
Definition: efi.h:167
EFI_RAISE_TPL RaiseTPL
Definition: UefiSpec.h:1845
An event is to be signaled once at a specified interval from the current time.
Definition: UefiSpec.h:493
Error codes.
static EFI_EVENT tick
Event used to wait for timer tick.
Definition: efi_entropy.c:67
static EFI_RNG_PROTOCOL * efirng
Random number generator protocol.
Definition: efi_entropy.c:40
VOID * EFI_EVENT
Handle to an event structure.
Definition: UefiBaseType.h:43
static int efi_get_noise(noise_sample_t *noise)
Get noise sample.
Definition: efi_entropy.c:227
min_entropy_t min_entropy_per_sample(void)
min-entropy per sample
#define DBGC(...)
Definition: compiler.h:505
int get_noise(noise_sample_t *noise)
Get noise sample.
#define EFI_ENTROPY_RNG_LEN
Minimum number of bytes to request from RNG.
Definition: efi_entropy.c:54
EFI_CLOSE_EVENT CloseEvent
Definition: UefiSpec.h:1864
EFI_SET_TIMER SetTimer
Definition: UefiSpec.h:1861
The Random Number Generator (RNG) protocol provides random bits for use in applications,...
Definition: Rng.h:145
#define EFI_ENTROPY_TRIGGER_TIME
Time (in 100ns units) to delay waiting for timer tick.
Definition: efi_entropy.c:64
static void efi_entropy_disable(void)
Disable entropy gathering.
Definition: efi_entropy.c:101
#define TPL_NOTIFY
Definition: UefiSpec.h:592
#define ENOTSUP
Operation not supported.
Definition: errno.h:589
u32 crc32_le(u32 seed, const void *data, size_t len)
Calculate 32-bit little-endian CRC checksum.
Definition: crc32.c:39
static int efi_entropy_enable(void)
Enable entropy gathering.
Definition: efi_entropy.c:74
void * memcpy(void *dest, const void *src, size_t len) __nonnull
PROVIDE_ENTROPY(efi, entropy_enable, efi_entropy_enable)
#define DBGC_HDA(...)
Definition: compiler.h:506
PROVIDE_ENTROPY_INLINE(efi, min_entropy_per_sample)
static int efi_get_noise_rng(noise_sample_t *noise)
Get noise sample from RNG protocol.
Definition: efi_entropy.c:181
EFI_CREATE_EVENT CreateEvent
Definition: UefiSpec.h:1860
Profiling.
char * strerror(int errno)
Retrieve string representation of error number.
Definition: strerror.c:78
EFI Boot Services Table.
Definition: UefiSpec.h:1836
FILE_LICENCE(GPL2_OR_LATER_OR_UBDL)
int entropy_enable(void)
Enable entropy gathering.
#define TPL_CALLBACK
Definition: UefiSpec.h:591
unsigned char uint8_t
Definition: stdint.h:10
UINT64 UINTN
Unsigned value of native width.
Definition: ProcessorBind.h:71
EFI_WAIT_FOR_EVENT WaitForEvent
Definition: UefiSpec.h:1862
EFI API.
static int efi_get_noise_ticks(noise_sample_t *noise)
Get noise sample from timer ticks.
Definition: efi_entropy.c:150
RETURN_STATUS EFI_STATUS
Function return status for EFI API.
Definition: UefiBaseType.h:35
#define EIO
Input/output error.
Definition: errno.h:433
EFI_RNG_GET_RNG GetRNG
Definition: Rng.h:147
uint8_t noise_sample_t
A noise sample.
Definition: entropy.h:50
EFI_SYSTEM_TABLE * efi_systab
EFI_RESTORE_TPL RestoreTPL
Definition: UefiSpec.h:1846
uint64_t index
Index of the first segment within the content.
Definition: pccrc.h:21
int memcmp(const void *first, const void *second, size_t len)
Compare memory regions.
Definition: string.c:113
#define NULL
NULL pointer (VOID *)
Definition: Base.h:362
Entropy source.
#define EVT_TIMER
Definition: UefiSpec.h:391
EFI_TPL efi_external_tpl
External task priority level.
Definition: efi_init.c:51