iPXE
entropy.c
Go to the documentation of this file.
1/*
2 * Copyright (C) 2012 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 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
24FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
25FILE_SECBOOT ( PERMITTED );
26
27/** @file
28 *
29 * Entropy source
30 *
31 * This algorithm is designed to comply with ANS X9.82 Part 4 (April
32 * 2011 Draft) Section 13.3. This standard is unfortunately not
33 * freely available.
34 */
35
36#include <stdint.h>
37#include <assert.h>
38#include <string.h>
39#include <errno.h>
40#include <ipxe/crypto.h>
41#include <ipxe/hash_df.h>
42#include <ipxe/entropy.h>
43
44/* Disambiguate the various error causes */
45#define EPIPE_REPETITION_COUNT_TEST \
46 __einfo_error ( EINFO_EPIPE_REPETITION_COUNT_TEST )
47#define EINFO_EPIPE_REPETITION_COUNT_TEST \
48 __einfo_uniqify ( EINFO_EPIPE, 0x01, "Repetition count test failed" )
49#define EPIPE_ADAPTIVE_PROPORTION_TEST \
50 __einfo_error ( EINFO_EPIPE_ADAPTIVE_PROPORTION_TEST )
51#define EINFO_EPIPE_ADAPTIVE_PROPORTION_TEST \
52 __einfo_uniqify ( EINFO_EPIPE, 0x02, "Adaptive proportion test failed" )
53
54/**
55 * Initialise repetition count test
56 *
57 * @v source Entropy source
58 */
59static void repetition_count_test_init ( struct entropy_source *source ) {
61 &source->repetition_count_test;
62
63 /* Sanity checks */
64 assert ( test->repetition_count == 0 );
65 assert ( test->cutoff > 0 );
66}
67
68/**
69 * Perform repetition count test
70 *
71 * @v source Entropy source
72 * @v sample Noise sample
73 * @ret rc Return status code
74 *
75 * This is the Repetition Count Test defined in ANS X9.82 Part 2
76 * (October 2011 Draft) Section 8.5.2.1.2.
77 */
78static int repetition_count_test ( struct entropy_source *source,
79 noise_sample_t sample ) {
81 &source->repetition_count_test;
82
83 /* A = the most recently seen sample value
84 * B = the number of times that value A has been seen in a row
85 * C = the cutoff value above which the repetition test should fail
86 */
87
88 /* 1. For each new sample processed:
89 *
90 * (Note that the test for "repetition_count > 0" ensures that
91 * the initial value of most_recent_sample is treated as being
92 * undefined.)
93 */
94 if ( ( sample == test->most_recent_sample ) &&
95 ( test->repetition_count > 0 ) ) {
96
97 /* a) If the new sample = A, then B is incremented by one. */
98 test->repetition_count++;
99
100 /* i. If B >= C, then an error condition is raised
101 * due to a failure of the test
102 */
103 if ( test->repetition_count >= test->cutoff ) {
104 DBGC ( source, "ENTROPY %s excessively repeated "
105 "value %d (%d/%d)\n", source->name, sample,
106 test->repetition_count, test->cutoff );
108 }
109
110 } else {
111
112 /* b) Else:
113 * i. A = new sample
114 */
115 test->most_recent_sample = sample;
116
117 /* ii. B = 1 */
118 test->repetition_count = 1;
119 }
120
121 return 0;
122}
123
124/**
125 * Initialise adaptive proportion test
126 *
127 * @v source Entropy source
128 */
129static void adaptive_proportion_test_init ( struct entropy_source *source ) {
132
133 /* Sanity checks */
134 assert ( test->sample_count == 0 );
135 assert ( test->repetition_count == 0 );
136 assert ( test->cutoff > 0 );
137
138 /* Ensure that a new test run starts immediately */
140}
141
142/**
143 * Perform adaptive proportion test
144 *
145 * @v source Entropy source
146 * @v sample Noise sample
147 * @ret rc Return status code
148 *
149 * This is the Adaptive Proportion Test for the Most Common Value
150 * defined in ANS X9.82 Part 2 (October 2011 Draft) Section 8.5.2.1.3.
151 */
152static int adaptive_proportion_test ( struct entropy_source *source,
153 noise_sample_t sample ) {
156
157 /* A = the sample value currently being counted
158 * S = the number of samples examined in this run of the test so far
159 * N = the total number of samples that must be observed in
160 * one run of the test, also known as the "window size" of
161 * the test
162 * B = the current number of times that S (sic) has been seen
163 * in the W (sic) samples examined so far
164 * C = the cutoff value above which the repetition test should fail
165 * W = the probability of a false positive: 2^-30
166 */
167
168 /* 1. The entropy source draws the current sample from the
169 * noise source.
170 *
171 * (Nothing to do; we already have the current sample.)
172 */
173
174 /* 2. If S = N, then a new run of the test begins: */
175 if ( test->sample_count == ADAPTIVE_PROPORTION_WINDOW_SIZE ) {
176
177 /* a. A = the current sample */
178 test->current_counted_sample = sample;
179
180 /* b. S = 0 */
181 test->sample_count = 0;
182
183 /* c. B = 0 */
184 test->repetition_count = 0;
185
186 } else {
187
188 /* Else: (the test is already running)
189 * a. S = S + 1
190 */
191 test->sample_count++;
192
193 /* b. If A = the current sample, then: */
194 if ( sample == test->current_counted_sample ) {
195
196 /* i. B = B + 1 */
197 test->repetition_count++;
198
199 /* ii. If S (sic) > C then raise an error
200 * condition, because the test has
201 * detected a failure
202 */
203 if ( test->repetition_count > test->cutoff ) {
204 DBGC ( source, "ENTROPY %s excessively "
205 "repeated value %d (%d/%d)\n",
206 source->name, sample,
207 test->repetition_count, test->cutoff );
209 }
210 }
211 }
212
213 return 0;
214}
215
216/**
217 * Get entropy sample
218 *
219 * @v source Entropy source
220 * @ret entropy Entropy sample
221 * @ret rc Return status code
222 *
223 * This is the GetEntropy function defined in ANS X9.82 Part 2
224 * (October 2011 Draft) Section 6.5.1.
225 */
226static int get_entropy ( struct entropy_source *source,
227 entropy_sample_t *entropy ) {
228 noise_sample_t noise;
229 int rc;
230
231 /* Any failure is permanent */
232 if ( ( rc = source->rc ) != 0 )
233 goto err_broken;
234
235 /* Get noise sample */
236 if ( ( rc = get_noise ( source, &noise ) ) != 0 )
237 goto err_get_noise;
238
239 /* Perform Repetition Count Test and Adaptive Proportion Test
240 * as mandated by ANS X9.82 Part 2 (October 2011 Draft)
241 * Section 8.5.2.1.1.
242 */
243 if ( ( rc = repetition_count_test ( source, noise ) ) != 0 )
244 goto err_repetition_count_test;
245 if ( ( rc = adaptive_proportion_test ( source, noise ) ) != 0 )
246 goto err_adaptive_proportion_test;
247
248 /* We do not use any optional conditioning component */
249 *entropy = noise;
250
251 return 0;
252
253 err_adaptive_proportion_test:
254 err_repetition_count_test:
255 err_get_noise:
256 source->rc = rc;
257 err_broken:
258 return rc;
259}
260
261/**
262 * Initialise startup test
263 *
264 * @v source Entropy source
265 */
266static void startup_test_init ( struct entropy_source *source ) {
267 struct entropy_startup_test *test = &source->startup_test;
268
269 /* Sanity check */
270 assert ( test->tested == 0 );
271 assert ( test->count > 0 );
272}
273
274/**
275 * Perform startup test
276 *
277 * @v source Entropy source
278 * @ret rc Return status code
279 */
280static int startup_test ( struct entropy_source *source ) {
281 struct entropy_startup_test *test = &source->startup_test;
282 entropy_sample_t sample;
283 int rc;
284
285 /* Perform mandatory number of startup tests */
286 for ( ; test->tested < test->count ; test->tested++ ) {
287 if ( ( rc = get_entropy ( source, &sample ) ) != 0 ) {
288 DBGC ( source, "ENTROPY %s failed: %s\n",
289 source->name, strerror ( rc ) );
290 return rc;
291 }
292 }
293
294 return 0;
295}
296
297/**
298 * Enable entropy gathering
299 *
300 * @v source Entropy source
301 * @ret rc Return status code
302 */
303int entropy_enable ( struct entropy_source *source ) {
304 int rc;
305
306 /* Refuse to enable a previously failed source */
307 if ( ( rc = source->rc ) != 0 )
308 return rc;
309
310 /* Enable entropy source */
311 if ( ( rc = source->enable() ) != 0 ) {
312 DBGC ( source, "ENTROPY %s could not enable: %s\n",
313 source->name, strerror ( rc ) );
314 source->rc = rc;
315 return rc;
316 }
317
318 /* Sanity check */
319 assert ( source->min_entropy_per_sample > 0 );
320
321 /* Initialise test state if this source has not previously been used */
322 if ( source->startup_test.tested == 0 ) {
325 startup_test_init ( source );
326 }
327
328 DBGC ( source, "ENTROPY %s enabled\n", source->name );
329 return 0;
330}
331
332/**
333 * Enable and test entropy source
334 *
335 * @v source Entropy source
336 * @ret rc Return status code
337 */
338static int entropy_enable_and_test ( struct entropy_source *source ) {
339 int rc;
340
341 /* Enable source */
342 if ( ( rc = entropy_enable ( source ) ) != 0 )
343 goto err_enable;
344
345 /* Test source */
346 if ( ( rc = startup_test ( source ) ) != 0 )
347 goto err_test;
348
349 DBGC ( source, "ENTROPY %s passed %d startup tests\n",
350 source->name, source->startup_test.count );
351 return 0;
352
353 err_test:
354 entropy_disable ( source );
355 err_enable:
356 assert ( source->rc == rc );
357 return rc;
358}
359
360/**
361 * Enable first working entropy source
362 *
363 * @v source Entropy source to fill in
364 * @ret rc Return status code
365 */
366static int entropy_enable_working ( struct entropy_source **source ) {
367 int rc;
368
369 /* Find the first working source */
370 rc = -ENOENT;
372 if ( ( rc = entropy_enable_and_test ( *source ) ) == 0 )
373 return 0;
374 }
375
376 DBGC ( *source, "ENTROPY has no working sources: %s\n",
377 strerror ( rc ) );
378 return rc;
379}
380
381/**
382 * Disable entropy gathering
383 *
384 * @v source Entropy source
385 */
386void entropy_disable ( struct entropy_source *source ) {
387
388 /* Disable entropy gathering, if applicable */
389 if ( source->disable )
390 source->disable();
391
392 DBGC ( source, "ENTROPY %s disabled\n", source->name );
393}
394
395/**
396 * Create next nonce value
397 *
398 * @ret nonce Nonce
399 *
400 * This is the MakeNextNonce function defined in ANS X9.82 Part 4
401 * (April 2011 Draft) Section 13.3.4.2.
402 */
403static uint32_t make_next_nonce ( void ) {
404 static uint32_t nonce;
405
406 /* The simplest implementation of a nonce uses a large counter */
407 nonce++;
408
409 return nonce;
410}
411
412/**
413 * Obtain entropy input temporary buffer
414 *
415 * @v min_entropy Min-entropy required
416 * @v tmp Temporary buffer
417 * @v tmp_len Length of temporary buffer
418 * @ret rc Return status code
419 *
420 * This is (part of) the implementation of the Get_entropy_input
421 * function (using an entropy source as the source of entropy input
422 * and condensing each entropy source output after each GetEntropy
423 * call) as defined in ANS X9.82 Part 4 (April 2011 Draft) Section
424 * 13.3.4.2.
425 */
427 size_t tmp_len ) {
428 struct entropy_source *source;
429 struct {
431 entropy_sample_t sample;
432 } __attribute__ (( packed )) data;;
433 uint8_t df_buf[tmp_len];
434 min_entropy_t entropy_total;
435 unsigned int num_samples;
436 unsigned int i;
437 int rc;
438
439 /* Enable entropy gathering */
440 if ( ( rc = entropy_enable_working ( &source ) ) != 0 )
441 goto err_enable_working;
442
443 /* Sanity checks */
444 assert ( source->startup_test.count > 0 );
445 assert ( source->startup_test.tested >= source->startup_test.count );
446
447 /* 3. entropy_total = 0 */
448 entropy_total = MIN_ENTROPY ( 0 );
449
450 /* 4. tmp = a fixed n-bit value, such as 0^n */
451 memset ( tmp, 0, tmp_len );
452
453 /* 5. While ( entropy_total < min_entropy ) */
454 for ( num_samples = 0 ; entropy_total < min_entropy ; num_samples++ ) {
455 /* 5.1. ( status, entropy_bitstring, assessed_entropy )
456 * = GetEntropy()
457 * 5.2. If status indicates an error, return ( status, Null )
458 */
459 if ( ( rc = get_entropy ( source, &data.sample ) ) != 0 )
460 goto err_get_entropy;
461
462 /* 5.3. nonce = MakeNextNonce() */
463 data.nonce = make_next_nonce();
464
465 /* 5.4. tmp = tmp XOR
466 * df ( ( nonce || entropy_bitstring ), n )
467 */
469 df_buf, sizeof ( df_buf ) );
470 for ( i = 0 ; i < tmp_len ; i++ )
471 tmp[i] ^= df_buf[i];
472
473 /* 5.5. entropy_total = entropy_total + assessed_entropy */
474 entropy_total += source->min_entropy_per_sample;
475 }
476
477 /* Disable entropy gathering */
478 entropy_disable ( source );
479
480 DBGC ( source, "ENTROPY %s gathered %d bits in %d samples\n",
481 source->name, ( min_entropy / MIN_ENTROPY_SCALE ), num_samples );
482 return 0;
483
484 err_get_entropy:
485 entropy_disable ( source );
486 assert ( source->rc == rc );
487 err_enable_working:
488 return rc;
489}
490
491/* Drag in objects via entropy_enable */
493
494/* Drag in entropy configuration */
495REQUIRE_OBJECT ( config_entropy );
struct arbelprm_rc_send_wqe rc
Definition arbel.h:3
unsigned int uint32_t
Definition stdint.h:12
unsigned char uint8_t
Definition stdint.h:10
Assertions.
#define assert(condition)
Assert a condition at run-time.
Definition assert.h:50
uint8_t data[48]
Additional event data.
Definition ena.h:11
void entropy_disable(struct entropy_source *source)
Disable entropy gathering.
Definition entropy.c:386
static int entropy_enable_and_test(struct entropy_source *source)
Enable and test entropy source.
Definition entropy.c:338
static int entropy_enable_working(struct entropy_source **source)
Enable first working entropy source.
Definition entropy.c:366
#define EPIPE_ADAPTIVE_PROPORTION_TEST
Definition entropy.c:49
int get_entropy_input_tmp(min_entropy_t min_entropy, uint8_t *tmp, size_t tmp_len)
Obtain entropy input temporary buffer.
Definition entropy.c:426
static void startup_test_init(struct entropy_source *source)
Initialise startup test.
Definition entropy.c:266
static void repetition_count_test_init(struct entropy_source *source)
Initialise repetition count test.
Definition entropy.c:59
int entropy_enable(struct entropy_source *source)
Enable entropy gathering.
Definition entropy.c:303
static int repetition_count_test(struct entropy_source *source, noise_sample_t sample)
Perform repetition count test.
Definition entropy.c:78
static void adaptive_proportion_test_init(struct entropy_source *source)
Initialise adaptive proportion test.
Definition entropy.c:129
#define EPIPE_REPETITION_COUNT_TEST
Definition entropy.c:45
static int get_entropy(struct entropy_source *source, entropy_sample_t *entropy)
Get entropy sample.
Definition entropy.c:226
static int startup_test(struct entropy_source *source)
Perform startup test.
Definition entropy.c:280
static int adaptive_proportion_test(struct entropy_source *source, noise_sample_t sample)
Perform adaptive proportion test.
Definition entropy.c:152
static uint32_t make_next_nonce(void)
Create next nonce value.
Definition entropy.c:403
static int test
Definition epic100.c:73
Error codes.
#define DBGC(...)
Definition compiler.h:505
#define FILE_LICENCE(_licence)
Declare a particular licence as applying to a file.
Definition compiler.h:896
#define REQUIRE_OBJECT(object)
Require an object.
Definition compiler.h:202
#define ENOENT
No such file or directory.
Definition errno.h:515
#define FILE_SECBOOT(_status)
Declare a file's UEFI Secure Boot permission status.
Definition compiler.h:926
#define REQUIRING_SYMBOL(symbol)
Specify the file's requiring symbol.
Definition compiler.h:140
void hash_df(struct digest_algorithm *hash, const void *input, size_t input_len, void *output, size_t output_len)
Distribute entropy throughout a buffer.
Definition hash_df.c:85
Hash-based derivation function (Hash_df)
#define __attribute__(x)
Definition compiler.h:10
Cryptographic API.
Entropy source.
#define MIN_ENTROPY(bits)
Construct a min-entropy fixed-point value.
Definition entropy.h:43
uint8_t entropy_sample_t
An entropy sample.
Definition entropy.h:25
#define MIN_ENTROPY_SCALE
Fixed-point scale for min-entropy amounts.
Definition entropy.h:35
#define entropy_hash_df_algorithm
Use SHA-256 as the underlying hash algorithm for Hash_df.
Definition entropy.h:193
#define ENTROPY_SOURCES
Entropy source table.
Definition entropy.h:170
uint8_t noise_sample_t
A noise sample.
Definition entropy.h:22
unsigned int min_entropy_t
An amount of min-entropy.
Definition entropy.h:32
static int get_noise(struct entropy_source *source, noise_sample_t *noise)
Get noise sample.
Definition entropy.h:209
#define ADAPTIVE_PROPORTION_WINDOW_SIZE
Window size for the adaptive proportion test.
Definition entropy.h:354
String functions.
void * memset(void *dest, int character, size_t len) __nonnull
unsigned long tmp
Definition linux_pci.h:65
char * strerror(int errno)
Retrieve string representation of error number.
Definition strerror.c:79
Adaptive proportion test state.
Definition entropy.h:76
Repetition count test state.
Definition entropy.h:52
An entropy source.
Definition entropy.h:117
struct entropy_repetition_count_test repetition_count_test
Repetition count test state.
Definition entropy.h:134
min_entropy_t min_entropy_per_sample
min-entropy per sample
Definition entropy.h:132
int rc
Failure status (if any)
Definition entropy.h:144
struct entropy_startup_test startup_test
Startup test state.
Definition entropy.h:138
void(* disable)(void)
Disable entropy gathering.
Definition entropy.h:156
const char * name
Name.
Definition entropy.h:119
struct entropy_adaptive_proportion_test adaptive_proportion_test
Adaptive proportion test state.
Definition entropy.h:136
int(* enable)(void)
Enable entropy gathering.
Definition entropy.h:151
Startup test state.
Definition entropy.h:105
unsigned int count
Number of startup tests required for one full cycle.
Definition entropy.h:113
unsigned int tested
Number of startup tests performed.
Definition entropy.h:107
#define for_each_table_entry(pointer, table)
Iterate through all entries within a linker table.
Definition tables.h:386
u8 nonce[32]
Nonce value.
Definition wpa.h:25