opengnsys_ipxe/src/include/ipxe/entropy.h

521 lines
16 KiB
C

#ifndef _IPXE_ENTROPY_H
#define _IPXE_ENTROPY_H
/** @file
*
* Entropy source
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdint.h>
#include <string.h>
#include <assert.h>
#include <ipxe/hash_df.h>
#include <ipxe/sha256.h>
#include <ipxe/tables.h>
#include <config/entropy.h>
/** A noise sample */
typedef uint8_t noise_sample_t;
/** An entropy sample */
typedef uint8_t entropy_sample_t;
/** An amount of min-entropy
*
* Expressed as a fixed-point quantity in order to avoid floating
* point calculations.
*/
typedef unsigned int min_entropy_t;
/** Fixed-point scale for min-entropy amounts */
#define MIN_ENTROPY_SCALE ( 1 << 16 )
/**
* Construct a min-entropy fixed-point value
*
* @v bits min-entropy in bits
* @ret min_entropy min-entropy as a fixed-point value
*/
#define MIN_ENTROPY( bits ) \
( ( min_entropy_t ) ( (bits) * MIN_ENTROPY_SCALE ) )
/**
* Repetition count test state
*
* This is the state for the repetition Count Test defined in ANS
* X9.82 Part 2 (October 2011 Draft) Section 8.5.2.1.2.
*/
struct entropy_repetition_count_test {
/**
* A = the most recently seen sample value
*/
noise_sample_t most_recent_sample;
/**
* B = the number of times that value A has been seen in a row
*/
unsigned int repetition_count;
/**
* C = the cutoff value above which the repetition test should fail
*
* Filled in by entropy_init().
*/
unsigned int cutoff;
};
/**
* Adaptive proportion test state
*
* This is the state for the Adaptive Proportion Test for the Most
* Common Value defined in ANS X9.82 Part 2 (October 2011 Draft)
* Section 8.5.2.1.3.
*/
struct entropy_adaptive_proportion_test {
/**
* A = the sample value currently being counted
*/
noise_sample_t current_counted_sample;
/**
* S = the number of samples examined in this run of the test so far
*/
unsigned int sample_count;
/**
* B = the current number of times that S (sic) has been seen
* in the W (sic) samples examined so far
*/
unsigned int repetition_count;
/**
* C = the cutoff value above which the repetition test should fail
*
* Filled in by entropy_init().
*/
unsigned int cutoff;
};
/**
* Startup test state
*
* 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.
*/
struct entropy_startup_test {
/** Number of startup tests performed */
unsigned int tested;
/**
* Number of startup tests required for one full cycle
*
* Filled in by entropy_init().
*/
unsigned int count;
};
/** 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 state */
struct entropy_repetition_count_test repetition_count_test;
/** Adaptive proportion test state */
struct entropy_adaptive_proportion_test adaptive_proportion_test;
/** Startup test state */
struct entropy_startup_test startup_test;
/**
* Failure status (if any)
*
* Any failure of an entropy source is regarded as permanent.
*/
int rc;
/**
* 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 );
};
/** Entropy source table */
#define ENTROPY_SOURCES __table ( struct entropy_source, "entropy_sources" )
/** Declare an entropy source */
#define __entropy_source( order ) __table_entry ( ENTROPY_SOURCES, order )
/** @defgroup entropy_source_order Entropy source order
*
* @{
*/
#define ENTROPY_PREFERRED 01 /**< Preferred entropy source */
#define ENTROPY_NORMAL 02 /**< Normal entropy source */
#define ENTROPY_FALLBACK 03 /**< Fallback entropy source */
/** @} */
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
*
* Hash_df using SHA-256 is an Approved algorithm in ANS X9.82.
*/
#define entropy_hash_df_algorithm sha256_algorithm
/** Underlying hash algorithm output length (in bytes) */
#define ENTROPY_HASH_DF_OUTLEN_BYTES SHA256_DIGEST_SIZE
/**
* Get noise sample
*
* @v source Entropy source
* @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__ (( always_inline )) int
get_noise ( struct entropy_source *source, noise_sample_t *noise ) {
return source->get_noise ( noise );
}
/**
* 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.
*
* This function is inlined since the entropy amount and 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,
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 );
unsigned int n;
int rc;
/* Sanity check */
build_assert ( min_entropy_bits <= ( 8 * max_len ) );
/* Round up minimum entropy to an integral number of bytes */
min_entropy_bits = ( ( min_entropy_bits + 7 ) & ~7 );
/* (Unnumbered). The output length of the hash function shall
* meet or exceed the security strength indicated by the
* min_entropy parameter.
*/
build_assert ( ( 8 * ENTROPY_HASH_DF_OUTLEN_BYTES ) >=
min_entropy_bits );
/* 1. If ( min_length > max_length ), then return ( FAILURE, Null ) */
build_assert ( min_len <= 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().)
*/
build_assert ( __builtin_constant_p ( tmp_len ) );
build_assert ( n == ( 8 * tmp_len ) );
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 )
* 8. Return ( SUCCESS, tmp )
*/
if ( tmp_len < min_len ) {
/* (Data is already in-place.) */
build_assert ( data == tmp );
memset ( ( data + tmp_len ), 0, ( min_len - tmp_len ) );
return min_len;
} else if ( tmp_len > max_len ) {
build_assert ( tmp == tmp_buf );
hash_df ( &entropy_hash_df_algorithm, tmp, tmp_len,
data, max_len );
return max_len;
} else {
/* (Data is already in-place.) */
build_assert ( data == tmp );
return tmp_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++;
build_assert ( cutoff >= max_repetitions );
/* 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.
*/
build_assert ( __builtin_constant_p ( cutoff ) );
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 */
build_assert ( __builtin_constant_p ( cutoff ) );
/* Fail if cutoff value is N/A */
build_assert ( cutoff != APC_NA );
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;
build_assert ( __builtin_constant_p ( num_samples ) );
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 */
build_assert ( min_entropy_per_sample > MIN_ENTROPY ( 0 ) );
build_assert ( min_entropy_per_sample <=
MIN_ENTROPY ( 8 * sizeof ( noise_sample_t ) ) );
/* 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_test.cutoff = repetition_count_cutoff;
source->adaptive_proportion_test.cutoff = adaptive_proportion_cutoff;
source->startup_test.count = startup_test_count;
}
extern int entropy_enable ( struct entropy_source *source );
extern void entropy_disable ( struct entropy_source *source );
extern int get_noise ( struct entropy_source *source, noise_sample_t *noise );
#endif /* _IPXE_ENTROPY_H */