[rng] Add ANS X9.82 Approved Source of Entropy Input

ANS X9.82 specifies several Approved Sources of Entropy Input (SEI).
One such SEI uses an entropy source as the Source of Entropy Input,
condensing each entropy source output after each GetEntropy call.
This can be implemented relatively cheaply in iPXE and avoids the need
to allocate potentially very large buffers.

(Note that the terms "entropy source" and "Source of Entropy Input"
are not synonyms within the context of ANS X9.82.)

Use the iPXE API mechanism to allow entropy sources to be selected at
compilation time.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
This commit is contained in:
Michael Brown
2012-02-19 22:14:06 +00:00
parent c2668b61ea
commit 073f41085f
12 changed files with 410 additions and 65 deletions

View File

@@ -10,9 +10,69 @@
FILE_LICENCE ( GPL2_OR_LATER );
#include <stdint.h>
#include <string.h>
#include <assert.h>
#include <ipxe/api.h>
#include <ipxe/hash_df.h>
#include <config/entropy.h>
/** min-entropy per entropy sample
/**
* 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;
/** An entropy sample */
typedef uint8_t entropy_sample_t;
/* Include all architecture-independent entropy API headers */
#include <ipxe/null_entropy.h>
/* Include all architecture-dependent entropy API headers */
#include <bits/entropy.h>
/**
* Enable entropy gathering
*
*/
void entropy_enable ( void );
/**
* Disable entropy gathering
*
*/
void entropy_disable ( void );
/**
* 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
@@ -20,71 +80,125 @@ FILE_LICENCE ( GPL2_OR_LATER );
* H_min = -log2 ( p_max )
*
* where p_max is the probability of the most likely sample value.
*
* This must be a compile-time constant.
*/
#define MIN_ENTROPY_PER_SAMPLE 0.16
/** Length of each entropy sample (in bits) */
#define ENTROPY_SAMPLE_LEN_BITS 12
double min_entropy_per_sample ( void );
/**
* Calculate entropy buffer size
* Get noise sample
*
* @v entropy_bits Amount of entropy required, in bits
* @v min_len Minimum buffer size, in bytes
* @v max_len Maximum buffer size, in bytes
* @ret len Buffer size, in bytes
* @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.
*/
static inline __attribute__ (( const, always_inline )) size_t
entropy_bufsize ( unsigned int entropy_bits, size_t min_len, size_t max_len ) {
unsigned int min_len_bits;
double min_samples;
double samples;
unsigned int samples_int;
unsigned int len_bits;
size_t len;
int get_noise ( noise_sample_t *noise );
/* Sanity check */
linker_assert ( MIN_ENTROPY_PER_SAMPLE <= ENTROPY_SAMPLE_LEN_BITS,
extern int get_entropy_input_tmp ( unsigned int num_samples,
uint8_t *tmp, size_t tmp_len );
/**
* Obtain entropy input
*
* @v min_entropy_bits Minimum amount of entropy, in bits
* @v data Data buffer
* @v min_len Minimum length of entropy input, in bytes
* @v max_len Maximum length of entropy input, in bytes
* @ret len Length of entropy input, in bytes, or negative error
*
* This is the implementation of the Get_entropy_input function (using
* an entropy source as the source of entropy input 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.
*/
static inline __attribute__ (( always_inline )) int
get_entropy_input ( unsigned int min_entropy_bits, void *data, size_t min_len,
size_t max_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() <=
( 8 * sizeof ( noise_sample_t ) ) ),
min_entropy_per_sample_is_impossibly_high );
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 */
samples = ( ( entropy_bits * 1.0 ) / MIN_ENTROPY_PER_SAMPLE );
/* Increase to minimum length if necessary */
min_len_bits = ( min_len * 8 );
min_samples = ( ( min_len_bits * 1.0 ) / ENTROPY_SAMPLE_LEN_BITS );
if ( samples < min_samples )
samples = min_samples;
min_samples = ( ( min_entropy_bits * 1.0 ) / 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.
*/
samples_int = samples;
if ( samples_int < samples )
samples_int++;
assert ( samples_int >= samples );
/* Calculate buffer length in bits */
len_bits = ( samples_int * ENTROPY_SAMPLE_LEN_BITS );
/* Calculate buffer length in bytes (rounding up) */
len = ( ( len_bits + 7 ) / 8 );
/* Check that buffer is within allowed lengths */
linker_assert ( len >= min_len, entropy_bufsize_too_short );
linker_assert ( len <= max_len, entropy_bufsize_too_long );
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 length is a compile-time constant.
* unless the calculated number of samples is a compile-time
* constant.
*/
linker_assert ( __builtin_constant_p ( len ),
entropy_bufsize_not_constant );
linker_assert ( __builtin_constant_p ( num_samples ),
num_samples_not_constant );
return len;
/* 1. If ( min_length > max_length ), then return ( FAILURE, Null ) */
linker_assert ( ( min_len <= max_len ), min_len_greater_than_max_len );
/* 2. n = 2 * min_entropy */
n = ( 2 * min_entropy_bits );
/* 3. entropy_total = 0
* 4. tmp = a fixed n-bit value, such as 0^n
* 5. While ( entropy_total < min_entropy )
* 5.1. ( status, entropy_bitstring, assessed_entropy )
* = GetEntropy()
* 5.2. If status indicates an error, return ( status, Null )
* 5.3. nonce = MakeNextNonce()
* 5.4. tmp = tmp XOR df ( ( nonce || entropy_bitstring ), n )
* 5.5. entropy_total = entropy_total + assessed_entropy
*
* (The implementation of these steps is inside the function
* get_entropy_input_tmp().)
*/
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 )
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 )
* 8. Return ( SUCCESS, tmp )
*/
if ( tmp_len < min_len ) {
/* (Data is already in-place.) */
linker_assert ( ( data == tmp ), data_not_inplace );
memset ( ( data + tmp_len ), 0, ( min_len - tmp_len ) );
return min_len;
} else if ( tmp_len > max_len ) {
linker_assert ( ( tmp == tmp_buf ), data_inplace );
hash_df ( tmp, tmp_len, data, max_len );
return max_len;
} else {
/* (Data is already in-place.) */
linker_assert ( ( data == tmp ), data_not_inplace );
return tmp_len;
}
}
extern int get_entropy_input ( unsigned int entropy_bits, void *data,
size_t min_len, size_t max_len );
#endif /* _IPXE_ENTROPY_H */

View File

@@ -59,9 +59,9 @@ FILE_LICENCE ( GPL2_OR_LATER );
* according to ANS X9.82 Part 3-2007 Section 10.2.1 Table 2 (NIST SP
* 800-90 Section 10.1 Table 2).
*
* We choose to allow up to 2^32-1 bytes (i.e. 2^35-8 bits).
* We choose to allow up to 32 bytes.
*/
#define HMAC_DRBG_MAX_ENTROPY_LEN_BYTES 0xffffffffUL
#define HMAC_DRBG_MAX_ENTROPY_LEN_BYTES 32
/** Maximum personalisation string length
*

View File

@@ -0,0 +1,51 @@
#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 );
#include <stdint.h>
#ifdef ENTROPY_NULL
#define ENTROPY_PREFIX_null
#else
#define ENTROPY_PREFIX_null __null_
#endif
static inline __always_inline void
ENTROPY_INLINE ( null, entropy_enable ) ( void ) {
/* Do nothing */
}
static inline __always_inline void
ENTROPY_INLINE ( null, entropy_disable ) ( void ) {
/* Do nothing */
}
static inline __always_inline double
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 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 */