Compare commits

...

1 Commits

Author SHA1 Message Date
5412db1370 WIP - entropy 2023-02-17 16:56:11 +00:00
15 changed files with 476 additions and 619 deletions

View File

@ -1,12 +0,0 @@
#ifndef _BITS_ENTROPY_H
#define _BITS_ENTROPY_H
/** @file
*
* ARM-specific entropy API implementations
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#endif /* _BITS_ENTROPY_H */

View File

@ -1,12 +0,0 @@
#ifndef _BITS_ENTROPY_H
#define _BITS_ENTROPY_H
/** @file
*
* LoongArch64-specific entropy API implementations
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#endif /* _BITS_ENTROPY_H */

View File

@ -32,12 +32,15 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <errno.h>
#include <ipxe/cpuid.h>
#include <ipxe/entropy.h>
#include <ipxe/drbg.h>
struct entropy_source rdrand_entropy __entropy_source ( ENTROPY_PREFERRED );
/** Number of times to retry RDRAND instruction */
#define RDRAND_RETRY_COUNT 16
/** Colour for debug messages */
#define colour CPUID_FEATURES_INTEL_ECX_RDRAND
#define colour &rdrand_entropy
/**
* Enable entropy gathering
@ -54,18 +57,17 @@ static int rdrand_entropy_enable ( void ) {
return -ENOTSUP;
}
/* Data returned by RDRAND is theoretically full entropy, up
* to a security strength of 128 bits, so assume that each
* sample contains exactly 8 bits of entropy.
*/
if ( DRBG_SECURITY_STRENGTH > 128 )
return -ENOTSUP;
entropy_init ( &rdrand_entropy, MIN_ENTROPY ( 8.0 ) );
return 0;
}
/**
* Disable entropy gathering
*
*/
static void rdrand_entropy_disable ( void ) {
/* Nothing to do */
}
/**
* Get noise sample
*
@ -93,7 +95,9 @@ static int rdrand_get_noise ( noise_sample_t *noise ) {
return 0;
}
PROVIDE_ENTROPY_INLINE ( rdrand, min_entropy_per_sample );
PROVIDE_ENTROPY ( rdrand, entropy_enable, rdrand_entropy_enable );
PROVIDE_ENTROPY ( rdrand, entropy_disable, rdrand_entropy_disable );
PROVIDE_ENTROPY ( rdrand, get_noise, rdrand_get_noise );
/** Hardware random number generator entropy source */
struct entropy_source rdrand_entropy __entropy_source ( ENTROPY_PREFERRED ) = {
.name = "rdrand",
.enable = rdrand_entropy_enable,
.get_noise = rdrand_get_noise,
};

View File

@ -1,15 +0,0 @@
#ifndef _BITS_ENTROPY_H
#define _BITS_ENTROPY_H
/** @file
*
* x86-specific entropy API implementations
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ipxe/rtc_entropy.h>
#include <ipxe/rdrand.h>
#endif /* _BITS_ENTROPY_H */

View File

@ -1,37 +0,0 @@
#ifndef _IPXE_RDRAND_H
#define _IPXE_RDRAND_H
/** @file
*
* Hardware random number generator
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdint.h>
#include <ipxe/drbg.h>
#ifdef ENTROPY_RDRAND
#define ENTROPY_PREFIX_rdrand
#else
#define ENTROPY_PREFIX_rdrand __rdrand_
#endif
/**
* min-entropy per sample
*
* @ret min_entropy min-entropy of each sample
*/
static inline __always_inline min_entropy_t
ENTROPY_INLINE ( rdrand, min_entropy_per_sample ) ( void ) {
/* Data returned by RDRAND is theoretically full entropy, up
* to a security strength of 128 bits.
*/
if ( DRBG_SECURITY_STRENGTH > 128 )
return 0;
return MIN_ENTROPY ( 8 * sizeof ( noise_sample_t ) );
}
#endif /* _IPXE_RDRAND_H */

View File

@ -1,62 +0,0 @@
#ifndef _IPXE_RTC_ENTROPY_H
#define _IPXE_RTC_ENTROPY_H
/** @file
*
* RTC-based entropy source
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdint.h>
#ifdef ENTROPY_RTC
#define ENTROPY_PREFIX_rtc
#else
#define ENTROPY_PREFIX_rtc __rtc_
#endif
/**
* min-entropy per sample
*
* @ret min_entropy min-entropy of each sample
*/
static inline __always_inline min_entropy_t
ENTROPY_INLINE ( rtc, min_entropy_per_sample ) ( void ) {
/* The min-entropy has been measured on several platforms
* using the entropy_sample test code. Modelling the samples
* as independent, and using a confidence level of 99.99%, the
* measurements were as follows:
*
* qemu-kvm : 7.38 bits
* VMware : 7.46 bits
* Physical hardware : 2.67 bits
*
* We choose the lowest of these (2.67 bits) and apply a 50%
* safety margin to allow for some potential non-independence
* of samples.
*/
return MIN_ENTROPY ( 1.3 );
}
extern uint8_t rtc_sample ( void );
/**
* Get noise sample
*
* @ret noise Noise sample
* @ret rc Return status code
*/
static inline __always_inline int
ENTROPY_INLINE ( rtc, get_noise ) ( noise_sample_t *noise ) {
/* Get sample */
*noise = rtc_sample();
/* Always successful */
return 0;
}
#endif /* _IPXE_RTC_ENTROPY_H */

View File

@ -39,6 +39,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ipxe/cpuid.h>
#include <ipxe/entropy.h>
struct entropy_source rtc_entropy __entropy_source ( ENTROPY_NORMAL );
/** Maximum time to wait for an RTC interrupt, in milliseconds */
#define RTC_MAX_WAIT_MS 100
@ -203,6 +205,21 @@ static int rtc_entropy_enable ( void ) {
if ( ( rc = rtc_entropy_check() ) != 0 )
goto err_check;
/* The min-entropy has been measured on several platforms
* using the entropy_sample test code. Modelling the samples
* as independent, and using a confidence level of 99.99%, the
* measurements were as follows:
*
* qemu-kvm : 7.38 bits
* VMware : 7.46 bits
* Physical hardware : 2.67 bits
*
* We choose the lowest of these (2.67 bits) and apply a 50%
* safety margin to allow for some potential non-independence
* of samples.
*/
entropy_init ( &rtc_entropy, MIN_ENTROPY ( 1.3 ) );
return 0;
err_check:
@ -226,11 +243,12 @@ static void rtc_entropy_disable ( void ) {
}
/**
* Measure a single RTC tick
* Get noise sample
*
* @ret delta Length of RTC tick (in TSC units)
* @ret noise Noise sample
* @ret rc Return status code
*/
uint8_t rtc_sample ( void ) {
static int rtc_get_noise ( noise_sample_t *noise ) {
uint32_t before;
uint32_t after;
uint32_t temp;
@ -265,10 +283,14 @@ uint8_t rtc_sample ( void ) {
: "=a" ( after ), "=d" ( before ), "=Q" ( temp )
: "2" ( 0 ) );
return ( after - before );
*noise = ( after - before );
return 0;
}
PROVIDE_ENTROPY_INLINE ( rtc, min_entropy_per_sample );
PROVIDE_ENTROPY ( rtc, entropy_enable, rtc_entropy_enable );
PROVIDE_ENTROPY ( rtc, entropy_disable, rtc_entropy_disable );
PROVIDE_ENTROPY_INLINE ( rtc, get_noise );
/** RTC entropy source */
struct entropy_source rtc_entropy __entropy_source ( ENTROPY_NORMAL ) = {
.name = "rtc",
.enable = rtc_entropy_enable,
.disable = rtc_entropy_disable,
.get_noise = rtc_get_noise,
};

View File

@ -50,46 +50,61 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#define EINFO_EPIPE_ADAPTIVE_PROPORTION_TEST \
__einfo_uniqify ( EINFO_EPIPE, 0x02, "Adaptive proportion test failed" )
/** Current entropy source */
static struct entropy_source *entropy_source;
/**
* Calculate cutoff value for the repetition count test
* Enable entropy gathering
*
* @ret cutoff Cutoff value
*
* This is the cutoff value for the Repetition Count Test defined in
* ANS X9.82 Part 2 (October 2011 Draft) Section 8.5.2.1.2.
* @ret rc Return status code
*/
static inline __attribute__ (( always_inline )) unsigned int
repetition_count_cutoff ( void ) {
double max_repetitions;
unsigned int cutoff;
int entropy_enable ( void ) {
int rc;
/* The cutoff formula for the repetition test is:
*
* C = ( 1 + ( -log2(W) / H_min ) )
*
* where W is set at 2^(-30) (in ANS X9.82 Part 2 (October
* 2011 Draft) Section 8.5.2.1.3.1).
*/
max_repetitions = ( 1 + ( MIN_ENTROPY ( 30 ) /
min_entropy_per_sample() ) );
/* Enable selected source, if applicable */
if ( entropy_source ) {
/* Round up to a whole number of repetitions. We don't have
* the ceil() function available, so do the rounding by hand.
*/
cutoff = max_repetitions;
if ( cutoff < max_repetitions )
cutoff++;
linker_assert ( ( cutoff >= max_repetitions ), rounding_error );
/* Enable entropy source */
if ( ( rc = entropy_source->enable() ) != 0 ) {
DBGC ( &entropy_source, "ENTROPY could not enable "
"source %s: %s\n", entropy_source->name,
strerror ( rc ) );
return rc;
}
/* Floating-point operations are not allowed in iPXE since we
* never set up a suitable environment. Abort the build
* unless the calculated number of repetitions is a
* compile-time constant.
*/
linker_assert ( __builtin_constant_p ( cutoff ),
repetition_count_cutoff_not_constant );
/* Sanity checks */
assert ( entropy_source->min_entropy_per_sample > 0 );
assert ( entropy_source->repetition_count_cutoff > 0 );
assert ( entropy_source->adaptive_proportion_cutoff > 0 );
assert ( entropy_source->startup_test_count > 0 );
return cutoff;
return 0;
}
/* Find the first working source */
rc = -ENOENT;
for_each_table_entry ( entropy_source, ENTROPY_SOURCES ) {
if ( ( rc = entropy_enable() ) == 0 ) {
DBGC ( &entropy_source, "ENTROPY using source %s\n",
entropy_source->name );
break;
}
}
return rc;
}
/**
* Disable entropy gathering
*
*/
void entropy_disable ( void ) {
/* Sanity check */
assert ( entropy_source != NULL );
/* Disable entropy gathering, if applicable */
if ( entropy_source->disable )
entropy_source->disable();
}
/**
@ -104,6 +119,8 @@ repetition_count_cutoff ( void ) {
static int repetition_count_test ( noise_sample_t sample ) {
static noise_sample_t most_recent_sample;
static unsigned int repetition_count = 0;
unsigned int repetition_count_cutoff =
entropy_source->repetition_count_cutoff;
/* A = the most recently seen sample value
* B = the number of times that value A has been seen in a row
@ -124,7 +141,7 @@ static int repetition_count_test ( noise_sample_t sample ) {
/* i. If B >= C, then an error condition is raised
* due to a failure of the test
*/
if ( repetition_count >= repetition_count_cutoff() )
if ( repetition_count >= repetition_count_cutoff )
return -EPIPE_REPETITION_COUNT_TEST;
} else {
@ -141,117 +158,6 @@ static int repetition_count_test ( noise_sample_t sample ) {
return 0;
}
/**
* Window size for the adaptive proportion test
*
* ANS X9.82 Part 2 (October 2011 Draft) Section 8.5.2.1.3.1.1 allows
* five possible window sizes: 16, 64, 256, 4096 and 65536.
*
* We expect to generate relatively few (<256) entropy samples during
* a typical iPXE run; the use of a large window size would mean that
* the test would never complete a single cycle. We use a window size
* of 64, which is the smallest window size that permits values of
* H_min down to one bit per sample.
*/
#define ADAPTIVE_PROPORTION_WINDOW_SIZE 64
/**
* Combine adaptive proportion test window size and min-entropy
*
* @v n N (window size)
* @v h H (min-entropy)
* @ret n_h (N,H) combined value
*/
#define APC_N_H( n, h ) ( ( (n) << 8 ) | (h) )
/**
* Define a row of the adaptive proportion cutoff table
*
* @v h H (min-entropy)
* @v c16 Cutoff for N=16
* @v c64 Cutoff for N=64
* @v c256 Cutoff for N=256
* @v c4096 Cutoff for N=4096
* @v c65536 Cutoff for N=65536
*/
#define APC_TABLE_ROW( h, c16, c64, c256, c4096, c65536) \
case APC_N_H ( 16, h ) : return c16; \
case APC_N_H ( 64, h ) : return c64; \
case APC_N_H ( 256, h ) : return c256; \
case APC_N_H ( 4096, h ) : return c4096; \
case APC_N_H ( 65536, h ) : return c65536;
/** Value used to represent "N/A" in adaptive proportion cutoff table */
#define APC_NA 0
/**
* Look up value in adaptive proportion test cutoff table
*
* @v n N (window size)
* @v h H (min-entropy)
* @ret cutoff Cutoff
*
* This is the table of cutoff values defined in ANS X9.82 Part 2
* (October 2011 Draft) Section 8.5.2.1.3.1.2.
*/
static inline __attribute__ (( always_inline )) unsigned int
adaptive_proportion_cutoff_lookup ( unsigned int n, unsigned int h ) {
switch ( APC_N_H ( n, h ) ) {
APC_TABLE_ROW ( 1, APC_NA, 51, 168, 2240, 33537 );
APC_TABLE_ROW ( 2, APC_NA, 35, 100, 1193, 17053 );
APC_TABLE_ROW ( 3, 10, 24, 61, 643, 8705 );
APC_TABLE_ROW ( 4, 8, 16, 38, 354, 4473 );
APC_TABLE_ROW ( 5, 6, 12, 25, 200, 2321 );
APC_TABLE_ROW ( 6, 5, 9, 17, 117, 1220 );
APC_TABLE_ROW ( 7, 4, 7, 15, 71, 653 );
APC_TABLE_ROW ( 8, 4, 5, 9, 45, 358 );
APC_TABLE_ROW ( 9, 3, 4, 7, 30, 202 );
APC_TABLE_ROW ( 10, 3, 4, 5, 21, 118 );
APC_TABLE_ROW ( 11, 2, 3, 4, 15, 71 );
APC_TABLE_ROW ( 12, 2, 3, 4, 11, 45 );
APC_TABLE_ROW ( 13, 2, 2, 3, 9, 30 );
APC_TABLE_ROW ( 14, 2, 2, 3, 7, 21 );
APC_TABLE_ROW ( 15, 1, 2, 2, 6, 15 );
APC_TABLE_ROW ( 16, 1, 2, 2, 5, 11 );
APC_TABLE_ROW ( 17, 1, 1, 2, 4, 9 );
APC_TABLE_ROW ( 18, 1, 1, 2, 4, 7 );
APC_TABLE_ROW ( 19, 1, 1, 1, 3, 6 );
APC_TABLE_ROW ( 20, 1, 1, 1, 3, 5 );
default:
return APC_NA;
}
}
/**
* Calculate cutoff value for the adaptive proportion test
*
* @ret cutoff Cutoff value
*
* This is the cutoff value for the Adaptive Proportion Test defined
* in ANS X9.82 Part 2 (October 2011 Draft) Section 8.5.2.1.3.1.2.
*/
static inline __attribute__ (( always_inline )) unsigned int
adaptive_proportion_cutoff ( void ) {
unsigned int h;
unsigned int n;
unsigned int cutoff;
/* Look up cutoff value in cutoff table */
n = ADAPTIVE_PROPORTION_WINDOW_SIZE;
h = ( min_entropy_per_sample() / MIN_ENTROPY_SCALE );
cutoff = adaptive_proportion_cutoff_lookup ( n, h );
/* Fail unless cutoff value is a build-time constant */
linker_assert ( __builtin_constant_p ( cutoff ),
adaptive_proportion_cutoff_not_constant );
/* Fail if cutoff value is N/A */
linker_assert ( ( cutoff != APC_NA ),
adaptive_proportion_cutoff_not_applicable );
return cutoff;
}
/**
* Perform adaptive proportion test
*
@ -265,6 +171,8 @@ static int adaptive_proportion_test ( noise_sample_t sample ) {
static noise_sample_t current_counted_sample;
static unsigned int sample_count = ADAPTIVE_PROPORTION_WINDOW_SIZE;
static unsigned int repetition_count;
unsigned int adaptive_proportion_cutoff =
entropy_source->adaptive_proportion_cutoff;
/* A = the sample value currently being counted
* B = the number of samples examined in this run of the test so far
@ -312,7 +220,7 @@ static int adaptive_proportion_test ( noise_sample_t sample ) {
* condition, because the test has
* detected a failure
*/
if ( repetition_count > adaptive_proportion_cutoff() )
if ( repetition_count > adaptive_proportion_cutoff )
return -EPIPE_ADAPTIVE_PROPORTION_TEST;
}
@ -321,6 +229,23 @@ static int adaptive_proportion_test ( noise_sample_t sample ) {
return 0;
}
/**
* Get noise sample
*
* @ret noise Noise sample
* @ret rc Return status code
*
* This is the GetNoise function defined in ANS X9.82 Part 2
* (October 2011 Draft) Section 6.5.2.
*/
int get_noise ( noise_sample_t *noise ) {
/* Sanity check */
assert ( entropy_source != NULL );
return entropy_source->get_noise ( noise );
}
/**
* Get entropy sample
*
@ -334,6 +259,9 @@ static int get_entropy ( entropy_sample_t *entropy ) {
static int rc = 0;
noise_sample_t noise;
/* Sanity check */
assert ( entropy_source != NULL );
/* Any failure is permanent */
if ( rc != 0 )
return rc;
@ -357,31 +285,6 @@ static int get_entropy ( entropy_sample_t *entropy ) {
return 0;
}
/**
* Calculate number of samples required for startup tests
*
* @ret num_samples Number of samples required
*
* ANS X9.82 Part 2 (October 2011 Draft) Section 8.5.2.1.5 requires
* that at least one full cycle of the continuous tests must be
* performed at start-up.
*/
static inline __attribute__ (( always_inline )) unsigned int
startup_test_count ( void ) {
unsigned int num_samples;
/* At least max(N,C) samples shall be generated by the noise
* source for start-up testing.
*/
num_samples = repetition_count_cutoff();
if ( num_samples < adaptive_proportion_cutoff() )
num_samples = adaptive_proportion_cutoff();
linker_assert ( __builtin_constant_p ( num_samples ),
startup_test_count_not_constant );
return num_samples;
}
/**
* Create next nonce value
*
@ -402,7 +305,7 @@ static uint32_t make_next_nonce ( void ) {
/**
* Obtain entropy input temporary buffer
*
* @v num_samples Number of entropy samples
* @v min_entropy Min-entropy required
* @v tmp Temporary buffer
* @v tmp_len Length of temporary buffer
* @ret rc Return status code
@ -412,11 +315,8 @@ static uint32_t make_next_nonce ( void ) {
* and condensing each entropy source output after each GetEntropy
* call) as defined in ANS X9.82 Part 4 (April 2011 Draft) Section
* 13.3.4.2.
*
* To minimise code size, the number of samples required is calculated
* at compilation time.
*/
int get_entropy_input_tmp ( unsigned int num_samples, uint8_t *tmp,
int get_entropy_input_tmp ( min_entropy_t min_entropy, uint8_t *tmp,
size_t tmp_len ) {
static unsigned int startup_tested = 0;
struct {
@ -424,6 +324,7 @@ int get_entropy_input_tmp ( unsigned int num_samples, uint8_t *tmp,
entropy_sample_t sample;
} __attribute__ (( packed )) data;;
uint8_t df_buf[tmp_len];
min_entropy_t entropy_total;
unsigned int i;
int rc;
@ -432,22 +333,20 @@ int get_entropy_input_tmp ( unsigned int num_samples, uint8_t *tmp,
return rc;
/* Perform mandatory startup tests, if not yet performed */
for ( ; startup_tested < startup_test_count() ; startup_tested++ ) {
for ( ; startup_tested < entropy_source->startup_test_count ;
startup_tested++ ) {
if ( ( rc = get_entropy ( &data.sample ) ) != 0 )
goto err_get_entropy;
}
/* 3. entropy_total = 0
*
* (Nothing to do; the number of entropy samples required has
* already been precalculated.)
*/
/* 3. entropy_total = 0 */
entropy_total = MIN_ENTROPY ( 0 );
/* 4. tmp = a fixed n-bit value, such as 0^n */
memset ( tmp, 0, tmp_len );
/* 5. While ( entropy_total < min_entropy ) */
while ( num_samples-- ) {
while ( entropy_total < min_entropy ) {
/* 5.1. ( status, entropy_bitstring, assessed_entropy )
* = GetEntropy()
* 5.2. If status indicates an error, return ( status, Null )
@ -466,11 +365,8 @@ int get_entropy_input_tmp ( unsigned int num_samples, uint8_t *tmp,
for ( i = 0 ; i < tmp_len ; i++ )
tmp[i] ^= df_buf[i];
/* 5.5. entropy_total = entropy_total + assessed_entropy
*
* (Nothing to do; the number of entropy samples
* required has already been precalculated.)
*/
/* 5.5. entropy_total = entropy_total + assessed_entropy */
entropy_total += entropy_source->min_entropy_per_sample;
}
/* Disable entropy gathering */

View File

@ -1,40 +0,0 @@
/*
* Copyright (C) 2012 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/** @file
*
* Nonexistent entropy source
*
*
* This source provides no entropy and must NOT be used in a
* security-sensitive environment.
*/
#include <ipxe/entropy.h>
PROVIDE_ENTROPY_INLINE ( null, min_entropy_per_sample );
PROVIDE_ENTROPY_INLINE ( null, entropy_enable );
PROVIDE_ENTROPY_INLINE ( null, entropy_disable );
PROVIDE_ENTROPY_INLINE ( null, get_noise );

View File

@ -1,35 +0,0 @@
#ifndef _IPXE_EFI_ENTROPY_H
#define _IPXE_EFI_ENTROPY_H
/** @file
*
* EFI entropy source
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdint.h>
#ifdef ENTROPY_EFI
#define ENTROPY_PREFIX_efi
#else
#define ENTROPY_PREFIX_efi __efi_
#endif
/**
* min-entropy per sample
*
* @ret min_entropy min-entropy of each sample
*/
static inline __always_inline min_entropy_t
ENTROPY_INLINE ( efi, min_entropy_per_sample ) ( void ) {
/* We use essentially the same mechanism as for the BIOS
* RTC-based entropy source, and so assume the same
* min-entropy per sample.
*/
return MIN_ENTROPY ( 1.3 );
}
#endif /* _IPXE_EFI_ENTROPY_H */

View File

@ -12,40 +12,11 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdint.h>
#include <string.h>
#include <assert.h>
#include <ipxe/api.h>
#include <ipxe/hash_df.h>
#include <ipxe/sha256.h>
#include <ipxe/tables.h>
#include <config/entropy.h>
/**
* Calculate static inline entropy API function name
*
* @v _prefix Subsystem prefix
* @v _api_func API function
* @ret _subsys_func Subsystem API function
*/
#define ENTROPY_INLINE( _subsys, _api_func ) \
SINGLE_API_INLINE ( ENTROPY_PREFIX_ ## _subsys, _api_func )
/**
* Provide a entropy API implementation
*
* @v _prefix Subsystem prefix
* @v _api_func API function
* @v _func Implementing function
*/
#define PROVIDE_ENTROPY( _subsys, _api_func, _func ) \
PROVIDE_SINGLE_API ( ENTROPY_PREFIX_ ## _subsys, _api_func, _func )
/**
* Provide a static inline entropy API implementation
*
* @v _prefix Subsystem prefix
* @v _api_func API function
*/
#define PROVIDE_ENTROPY_INLINE( _subsys, _api_func ) \
PROVIDE_SINGLE_API_INLINE ( ENTROPY_PREFIX_ ## _subsys, _api_func )
/** A noise sample */
typedef uint8_t noise_sample_t;
@ -71,56 +42,93 @@ typedef unsigned int min_entropy_t;
#define MIN_ENTROPY( bits ) \
( ( min_entropy_t ) ( (bits) * MIN_ENTROPY_SCALE ) )
/* Include all architecture-independent entropy API headers */
#include <ipxe/null_entropy.h>
#include <ipxe/efi/efi_entropy.h>
#include <ipxe/linux/linux_entropy.h>
/** An entropy source */
struct entropy_source {
/** Name */
const char *name;
/**
* min-entropy per sample
*
* min-entropy is defined in ANS X9.82 Part 1-2006 Section 8.3 and in
* NIST SP 800-90 Appendix C.3 as
*
* H_min = -log2 ( p_max )
*
* where p_max is the probability of the most likely sample value.
*
* Filled in by entropy_init().
*/
min_entropy_t min_entropy_per_sample;
/**
* Repetition count test cutoff value
*
* This is the cutoff value for the Repetition Count Test
* defined in ANS X9.82 Part 2 (October 2011 Draft) Section
* 8.5.2.1.2.
*
* Filled in by entropy_init().
*/
unsigned int repetition_count_cutoff;
/**
* Adaptive proportion test cutoff value
*
* This is the cutoff value for the Adaptive Proportion Test
* defined in ANS X9.82 Part 2 (October 2011 Draft) Section
* 8.5.2.1.3.1.2.
*
* Filled in by entropy_init().
*/
unsigned int adaptive_proportion_cutoff;
/**
* Startup test count
*
* ANS X9.82 Part 2 (October 2011 Draft) Section 8.5.2.1.5
* requires that at least one full cycle of the continuous
* tests must be performed at start-up.
*/
unsigned int startup_test_count;
/**
* Enable entropy gathering
*
* @ret rc Return status code
*/
int ( * enable ) ( void );
/**
* Disable entropy gathering
*
*/
void ( * disable ) ( void );
/**
* Get noise sample
*
* @ret noise Noise sample
* @ret rc Return status code
*
* This is the GetNoise function defined in ANS X9.82 Part 2
* (October 2011 Draft) Section 6.5.2.
*/
int ( * get_noise ) ( noise_sample_t *noise );
};
/* Include all architecture-dependent entropy API headers */
#include <bits/entropy.h>
/** Entropy source table */
#define ENTROPY_SOURCES __table ( struct entropy_source, "entropy_sources" )
/**
* Enable entropy gathering
/** Declare an entropy source */
#define __entropy_source( order ) __table_entry ( ENTROPY_SOURCES, order )
/** @defgroup entropy_source_order Entropy source order
*
* @ret rc Return status code
* @{
*/
int entropy_enable ( void );
/**
* Disable entropy gathering
*
*/
void entropy_disable ( void );
#define ENTROPY_PREFERRED 01 /**< Preferred entropy source */
#define ENTROPY_NORMAL 02 /**< Normal entropy source */
#define ENTROPY_FALLBACK 03 /**< Fallback entropy source */
/**
* min-entropy per sample
*
* @ret min_entropy min-entropy of each sample
*
* min-entropy is defined in ANS X9.82 Part 1-2006 Section 8.3 and in
* NIST SP 800-90 Appendix C.3 as
*
* H_min = -log2 ( p_max )
*
* where p_max is the probability of the most likely sample value.
*
* This must be a compile-time constant.
*/
min_entropy_t min_entropy_per_sample ( void );
/** @} */
/**
* Get noise sample
*
* @ret noise Noise sample
* @ret rc Return status code
*
* This is the GetNoise function defined in ANS X9.82 Part 2
* (October 2011 Draft) Section 6.5.2.
*/
int get_noise ( noise_sample_t *noise );
extern int get_entropy_input_tmp ( unsigned int num_samples,
uint8_t *tmp, size_t tmp_len );
extern int get_entropy_input_tmp ( min_entropy_t min_entropy, uint8_t *tmp,
size_t tmp_len );
/** Use SHA-256 as the underlying hash algorithm for Hash_df
*
@ -145,8 +153,9 @@ extern int get_entropy_input_tmp ( unsigned int num_samples,
* each entropy source output after each GetEntropy call) as defined
* in ANS X9.82 Part 4 (April 2011 Draft) Section 13.3.4.2.
*
* To minimise code size, the number of samples required is calculated
* at compilation time.
* This function is inlined to avoid unnecessarily linking in
* hash_df(), since the length inputs are always compile-time
* constants.
*/
static inline __attribute__ (( always_inline )) int
get_entropy_input ( unsigned int min_entropy_bits, void *data, size_t min_len,
@ -154,41 +163,16 @@ get_entropy_input ( unsigned int min_entropy_bits, void *data, size_t min_len,
size_t tmp_len = ( ( ( min_entropy_bits * 2 ) + 7 ) / 8 );
uint8_t tmp_buf[ tmp_len ];
uint8_t *tmp = ( ( tmp_len > max_len ) ? tmp_buf : data );
double min_samples;
unsigned int num_samples;
unsigned int n;
int rc;
/* Sanity checks */
linker_assert ( ( min_entropy_per_sample() <=
MIN_ENTROPY ( 8 * sizeof ( noise_sample_t ) ) ),
min_entropy_per_sample_is_impossibly_high );
/* Sanity check */
linker_assert ( ( min_entropy_bits <= ( 8 * max_len ) ),
entropy_buffer_too_small );
/* Round up minimum entropy to an integral number of bytes */
min_entropy_bits = ( ( min_entropy_bits + 7 ) & ~7 );
/* Calculate number of samples required to contain sufficient entropy */
min_samples = ( MIN_ENTROPY ( min_entropy_bits ) /
min_entropy_per_sample() );
/* Round up to a whole number of samples. We don't have the
* ceil() function available, so do the rounding by hand.
*/
num_samples = min_samples;
if ( num_samples < min_samples )
num_samples++;
linker_assert ( ( num_samples >= min_samples ), rounding_error );
/* Floating-point operations are not allowed in iPXE since we
* never set up a suitable environment. Abort the build
* unless the calculated number of samples is a compile-time
* constant.
*/
linker_assert ( __builtin_constant_p ( num_samples ),
num_samples_not_constant );
/* (Unnumbered). The output length of the hash function shall
* meet or exceed the security strength indicated by the
* min_entropy parameter.
@ -218,8 +202,10 @@ get_entropy_input ( unsigned int min_entropy_bits, void *data, size_t min_len,
linker_assert ( __builtin_constant_p ( tmp_len ),
tmp_len_not_constant );
linker_assert ( ( n == ( 8 * tmp_len ) ), tmp_len_mismatch );
if ( ( rc = get_entropy_input_tmp ( num_samples, tmp, tmp_len ) ) != 0 )
if ( ( rc = get_entropy_input_tmp ( MIN_ENTROPY ( min_entropy_bits ),
tmp, tmp_len ) ) != 0 ) {
return rc;
}
/* 6. If ( n < min_length ), then tmp = tmp || 0^(min_length-n)
* 7. If ( n > max_length ), then tmp = df ( tmp, max_length )
@ -242,4 +228,229 @@ get_entropy_input ( unsigned int min_entropy_bits, void *data, size_t min_len,
}
}
/**
* Calculate cutoff value for the repetition count test
*
* @v min_entropy_per_sample Min-entropy per sample
* @ret cutoff Cutoff value
*
* This is the cutoff value for the Repetition Count Test defined in
* ANS X9.82 Part 2 (October 2011 Draft) Section 8.5.2.1.2.
*/
static inline __attribute__ (( always_inline )) unsigned int
entropy_repetition_count_cutoff ( min_entropy_t min_entropy_per_sample ) {
double max_repetitions;
unsigned int cutoff;
/* The cutoff formula for the repetition test is:
*
* C = ( 1 + ( -log2(W) / H_min ) )
*
* where W is set at 2^(-30) (in ANS X9.82 Part 2 (October
* 2011 Draft) Section 8.5.2.1.3.1).
*/
max_repetitions = ( 1 + ( MIN_ENTROPY ( 30 ) /
min_entropy_per_sample ) );
/* Round up to a whole number of repetitions. We don't have
* the ceil() function available, so do the rounding by hand.
*/
cutoff = max_repetitions;
if ( cutoff < max_repetitions )
cutoff++;
linker_assert ( ( cutoff >= max_repetitions ), rounding_error );
/* Floating-point operations are not allowed in iPXE since we
* never set up a suitable environment. Abort the build
* unless the calculated number of repetitions is a
* compile-time constant.
*/
linker_assert ( __builtin_constant_p ( cutoff ),
repetition_count_cutoff_not_constant );
return cutoff;
}
/**
* Window size for the adaptive proportion test
*
* ANS X9.82 Part 2 (October 2011 Draft) Section 8.5.2.1.3.1.1 allows
* five possible window sizes: 16, 64, 256, 4096 and 65536.
*
* We expect to generate relatively few (<256) entropy samples during
* a typical iPXE run; the use of a large window size would mean that
* the test would never complete a single cycle. We use a window size
* of 64, which is the smallest window size that permits values of
* H_min down to one bit per sample.
*/
#define ADAPTIVE_PROPORTION_WINDOW_SIZE 64
/**
* Combine adaptive proportion test window size and min-entropy
*
* @v n N (window size)
* @v h H (min-entropy)
* @ret n_h (N,H) combined value
*/
#define APC_N_H( n, h ) ( ( (n) << 8 ) | (h) )
/**
* Define a row of the adaptive proportion cutoff table
*
* @v h H (min-entropy)
* @v c16 Cutoff for N=16
* @v c64 Cutoff for N=64
* @v c256 Cutoff for N=256
* @v c4096 Cutoff for N=4096
* @v c65536 Cutoff for N=65536
*/
#define APC_TABLE_ROW( h, c16, c64, c256, c4096, c65536) \
case APC_N_H ( 16, h ) : return c16; \
case APC_N_H ( 64, h ) : return c64; \
case APC_N_H ( 256, h ) : return c256; \
case APC_N_H ( 4096, h ) : return c4096; \
case APC_N_H ( 65536, h ) : return c65536;
/** Value used to represent "N/A" in adaptive proportion cutoff table */
#define APC_NA 0
/**
* Look up value in adaptive proportion test cutoff table
*
* @v n N (window size)
* @v h H (min-entropy)
* @ret cutoff Cutoff
*
* This is the table of cutoff values defined in ANS X9.82 Part 2
* (October 2011 Draft) Section 8.5.2.1.3.1.2.
*/
static inline __attribute__ (( always_inline )) unsigned int
entropy_adaptive_proportion_cutoff_lookup ( unsigned int n, unsigned int h ) {
switch ( APC_N_H ( n, h ) ) {
APC_TABLE_ROW ( 1, APC_NA, 51, 168, 2240, 33537 );
APC_TABLE_ROW ( 2, APC_NA, 35, 100, 1193, 17053 );
APC_TABLE_ROW ( 3, 10, 24, 61, 643, 8705 );
APC_TABLE_ROW ( 4, 8, 16, 38, 354, 4473 );
APC_TABLE_ROW ( 5, 6, 12, 25, 200, 2321 );
APC_TABLE_ROW ( 6, 5, 9, 17, 117, 1220 );
APC_TABLE_ROW ( 7, 4, 7, 15, 71, 653 );
APC_TABLE_ROW ( 8, 4, 5, 9, 45, 358 );
APC_TABLE_ROW ( 9, 3, 4, 7, 30, 202 );
APC_TABLE_ROW ( 10, 3, 4, 5, 21, 118 );
APC_TABLE_ROW ( 11, 2, 3, 4, 15, 71 );
APC_TABLE_ROW ( 12, 2, 3, 4, 11, 45 );
APC_TABLE_ROW ( 13, 2, 2, 3, 9, 30 );
APC_TABLE_ROW ( 14, 2, 2, 3, 7, 21 );
APC_TABLE_ROW ( 15, 1, 2, 2, 6, 15 );
APC_TABLE_ROW ( 16, 1, 2, 2, 5, 11 );
APC_TABLE_ROW ( 17, 1, 1, 2, 4, 9 );
APC_TABLE_ROW ( 18, 1, 1, 2, 4, 7 );
APC_TABLE_ROW ( 19, 1, 1, 1, 3, 6 );
APC_TABLE_ROW ( 20, 1, 1, 1, 3, 5 );
default:
return APC_NA;
}
}
/**
* Calculate cutoff value for the adaptive proportion test
*
* @v min_entropy_per_sample Min-entropy per sample
* @ret cutoff Cutoff value
*
* This is the cutoff value for the Adaptive Proportion Test defined
* in ANS X9.82 Part 2 (October 2011 Draft) Section 8.5.2.1.3.1.2.
*/
static inline __attribute__ (( always_inline )) unsigned int
entropy_adaptive_proportion_cutoff ( min_entropy_t min_entropy_per_sample ) {
unsigned int h;
unsigned int n;
unsigned int cutoff;
/* Look up cutoff value in cutoff table */
n = ADAPTIVE_PROPORTION_WINDOW_SIZE;
h = ( min_entropy_per_sample / MIN_ENTROPY_SCALE );
cutoff = entropy_adaptive_proportion_cutoff_lookup ( n, h );
/* Fail unless cutoff value is a compile-time constant */
linker_assert ( __builtin_constant_p ( cutoff ),
adaptive_proportion_cutoff_not_constant );
/* Fail if cutoff value is N/A */
linker_assert ( ( cutoff != APC_NA ),
adaptive_proportion_cutoff_not_applicable );
return cutoff;
}
/**
* Calculate number of samples required for startup tests
*
* @v repetition_count_cutoff Repetition count test cutoff value
* @v adaptive_proportion_cutoff Adaptive proportion test cutoff value
* @ret num_samples Number of samples required
*
* ANS X9.82 Part 2 (October 2011 Draft) Section 8.5.2.1.5 requires
* that at least one full cycle of the continuous tests must be
* performed at start-up.
*/
static inline __attribute__ (( always_inline )) unsigned int
entropy_startup_test_count ( unsigned int repetition_count_cutoff,
unsigned int adaptive_proportion_cutoff ) {
unsigned int num_samples;
/* At least max(N,C) samples shall be generated by the noise
* source for start-up testing.
*/
num_samples = repetition_count_cutoff;
if ( num_samples < adaptive_proportion_cutoff )
num_samples = adaptive_proportion_cutoff;
linker_assert ( __builtin_constant_p ( num_samples ),
startup_test_count_not_constant );
return num_samples;
}
/**
* Initialise entropy source
*
* @v source Entropy source
* @v min_entropy_per_sample Min-entropy per sample
*
* The cutoff value calculations for the repetition count test and the
* adaptive proportion test are provided as static inline functions
* since the results will always be compile-time constants.
*/
static inline __attribute__ (( always_inline )) void
entropy_init ( struct entropy_source *source,
min_entropy_t min_entropy_per_sample ) {
unsigned int repetition_count_cutoff;
unsigned int adaptive_proportion_cutoff;
unsigned int startup_test_count;
/* Sanity check */
linker_assert ( ( min_entropy_per_sample <=
MIN_ENTROPY ( 8 * sizeof ( noise_sample_t ) ) ),
min_entropy_per_sample_is_impossibly_high );
/* Calculate test cutoff values */
repetition_count_cutoff =
entropy_repetition_count_cutoff ( min_entropy_per_sample );
adaptive_proportion_cutoff =
entropy_adaptive_proportion_cutoff ( min_entropy_per_sample );
startup_test_count =
entropy_startup_test_count ( repetition_count_cutoff,
adaptive_proportion_cutoff );
/* Record min-entropy per sample and test cutoff values */
source->min_entropy_per_sample = min_entropy_per_sample;
source->repetition_count_cutoff = repetition_count_cutoff;
source->adaptive_proportion_cutoff = adaptive_proportion_cutoff;
source->startup_test_count = startup_test_count;
}
extern int entropy_enable ( void );
extern void entropy_disable ( void );
extern int get_noise ( noise_sample_t *noise );
#endif /* _IPXE_ENTROPY_H */

View File

@ -1,34 +0,0 @@
#ifndef _IPXE_LINUX_ENTROPY_H
#define _IPXE_LINUX_ENTROPY_H
/** @file
*
* /dev/random-based entropy source
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#ifdef ENTROPY_LINUX
#define ENTROPY_PREFIX_linux
#else
#define ENTROPY_PREFIX_linux __linux_
#endif
/**
* min-entropy per sample
*
* @ret min_entropy min-entropy of each sample
*/
static inline __always_inline min_entropy_t
ENTROPY_INLINE ( linux, min_entropy_per_sample ) ( void ) {
/* linux_get_noise() reads a single byte from /dev/random,
* which is supposed to block until a sufficient amount of
* entropy is available. We therefore assume that each sample
* contains exactly 8 bits of entropy.
*/
return MIN_ENTROPY ( 8.0 );
}
#endif /* _IPXE_LINUX_ENTROPY_H */

View File

@ -1,52 +0,0 @@
#ifndef _IPXE_NULL_ENTROPY_H
#define _IPXE_NULL_ENTROPY_H
/** @file
*
* Nonexistent entropy source
*
* This source provides no entropy and must NOT be used in a
* security-sensitive environment.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdint.h>
#ifdef ENTROPY_NULL
#define ENTROPY_PREFIX_null
#else
#define ENTROPY_PREFIX_null __null_
#endif
static inline __always_inline int
ENTROPY_INLINE ( null, entropy_enable ) ( void ) {
/* Do nothing */
return 0;
}
static inline __always_inline void
ENTROPY_INLINE ( null, entropy_disable ) ( void ) {
/* Do nothing */
}
static inline __always_inline min_entropy_t
ENTROPY_INLINE ( null, min_entropy_per_sample ) ( void ) {
/* Actual amount of min-entropy is zero. To avoid
* division-by-zero errors and to allow compilation of
* entropy-consuming code, pretend to have 1 bit of entropy in
* each sample.
*/
return MIN_ENTROPY ( 1.0 );
}
static inline __always_inline int
ENTROPY_INLINE ( null, get_noise ) ( noise_sample_t *noise ) {
/* All sample values are constant */
*noise = 0x01;
return 0;
}
#endif /* _IPXE_NULL_ENTROPY_H */

View File

@ -36,6 +36,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
*
*/
struct entropy_source efi_entropy __entropy_source ( ENTROPY_NORMAL );
/** Random number generator protocol */
static EFI_RNG_PROTOCOL *efirng;
EFI_REQUEST_PROTOCOL ( EFI_RNG_PROTOCOL, &efirng );
@ -91,6 +93,12 @@ static int efi_entropy_enable ( void ) {
return rc;
}
/* We use essentially the same mechanism as for the BIOS
* RTC-based entropy source, and so assume the same
* min-entropy per sample.
*/
entropy_init ( &efi_entropy, MIN_ENTROPY ( 1.3 ) );
return 0;
}
@ -235,7 +243,10 @@ static int efi_get_noise ( noise_sample_t *noise ) {
return 0;
}
PROVIDE_ENTROPY_INLINE ( efi, min_entropy_per_sample );
PROVIDE_ENTROPY ( efi, entropy_enable, efi_entropy_enable );
PROVIDE_ENTROPY ( efi, entropy_disable, efi_entropy_disable );
PROVIDE_ENTROPY ( efi, get_noise, efi_get_noise );
/** EFI entropy source */
struct entropy_source efi_entropy __entropy_source ( ENTROPY_NORMAL ) = {
.name = "efi",
.enable = efi_entropy_enable,
.disable = efi_entropy_disable,
.get_noise = efi_get_noise,
};

View File

@ -34,6 +34,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ipxe/linux_api.h>
#include <ipxe/entropy.h>
struct entropy_source linux_entropy __entropy_source ( ENTROPY_NORMAL );
/** Entropy source filename */
static const char entropy_filename[] = "/dev/random";
@ -55,6 +57,13 @@ static int linux_entropy_enable ( void ) {
return entropy_fd;
}
/* linux_get_noise() reads a single byte from /dev/random,
* which is supposed to block until a sufficient amount of
* entropy is available. We therefore assume that each sample
* contains exactly 8 bits of entropy.
*/
entropy_init ( &linux_entropy, MIN_ENTROPY ( 8.0 ) );
return 0;
}
@ -95,7 +104,10 @@ static int linux_get_noise ( noise_sample_t *noise ) {
return 0;
}
PROVIDE_ENTROPY_INLINE ( linux, min_entropy_per_sample );
PROVIDE_ENTROPY ( linux, entropy_enable, linux_entropy_enable );
PROVIDE_ENTROPY ( linux, entropy_disable, linux_entropy_disable );
PROVIDE_ENTROPY ( linux, get_noise, linux_get_noise );
/** Linux entropy source */
struct entropy_source linux_entropy __entropy_source ( ENTROPY_NORMAL ) = {
.name = "linux",
.enable = linux_entropy_enable,
.disable = linux_entropy_disable,
.get_noise = linux_get_noise,
};