summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Brown <mcb30@ipxe.org>2023-02-17 16:56:11 +0000
committerMichael Brown <mcb30@ipxe.org>2023-02-17 21:29:51 +0000
commit9f17d1116d27696ec76c48c5c77df34cba521380 (patch)
tree381ddb4988380ab1c5c4c59bb27ab7aea9bfc081
parent2733c4763a50b9eb0c206e7430d4d0638451e5e9 (diff)
downloadqemu-ipxe-9f17d1116d27696ec76c48c5c77df34cba521380.tar.gz
[rng] Allow entropy source to be selected at runtime
As noted in commit 3c83843 ("[rng] Check for several functioning RTC interrupts"), experimentation shows that Hyper-V cannot be trusted to reliably generate RTC interrupts. (As noted in commit f3ba0fb ("[hyperv] Provide timer based on the 10MHz time reference count MSR"), Hyper-V appears to suffer from a general problem in reliably generating any legacy interrupts.) An alternative entropy source is therefore required for an image that may be used in a Hyper-V Gen1 virtual machine. The x86 RDRAND instruction provides a suitable alternative entropy source, but may not be supported by all CPUs. We must therefore allow for multiple entropy sources to be compiled in, with the single active entropy source selected only at runtime. Restructure the internal entropy API to allow a working entropy source to be detected and chosen at runtime. Enable the RDRAND entropy source for all x86 builds, since it is likely to be substantially faster than any other source. Signed-off-by: Michael Brown <mcb30@ipxe.org>
-rw-r--r--src/arch/arm/include/bits/entropy.h12
-rw-r--r--src/arch/loong64/include/bits/entropy.h12
-rw-r--r--src/arch/x86/core/rdrand.c32
-rw-r--r--src/arch/x86/include/bits/entropy.h15
-rw-r--r--src/arch/x86/include/ipxe/rdrand.h37
-rw-r--r--src/arch/x86/include/ipxe/rtc_entropy.h62
-rw-r--r--src/arch/x86/interface/pcbios/rtc_entropy.c38
-rw-r--r--src/config/config_entropy.c (renamed from src/crypto/null_entropy.c)32
-rw-r--r--src/config/defaults/efi.h1
-rw-r--r--src/config/defaults/linux.h4
-rw-r--r--src/config/defaults/pcbios.h1
-rw-r--r--src/crypto/entropy.c283
-rw-r--r--src/include/ipxe/efi/efi_entropy.h35
-rw-r--r--src/include/ipxe/entropy.h414
-rw-r--r--src/include/ipxe/linux/linux_entropy.h34
-rw-r--r--src/include/ipxe/null_entropy.h52
-rw-r--r--src/interface/efi/efi_entropy.c19
-rw-r--r--src/interface/linux/linux_entropy.c20
18 files changed, 512 insertions, 591 deletions
diff --git a/src/arch/arm/include/bits/entropy.h b/src/arch/arm/include/bits/entropy.h
deleted file mode 100644
index 75fdc90e..00000000
--- a/src/arch/arm/include/bits/entropy.h
+++ /dev/null
@@ -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 */
diff --git a/src/arch/loong64/include/bits/entropy.h b/src/arch/loong64/include/bits/entropy.h
deleted file mode 100644
index 8d372693..00000000
--- a/src/arch/loong64/include/bits/entropy.h
+++ /dev/null
@@ -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 */
diff --git a/src/arch/x86/core/rdrand.c b/src/arch/x86/core/rdrand.c
index 29605ab2..850ab1f1 100644
--- a/src/arch/x86/core/rdrand.c
+++ b/src/arch/x86/core/rdrand.c
@@ -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,16 +57,15 @@ static int rdrand_entropy_enable ( void ) {
return -ENOTSUP;
}
- return 0;
-}
-
-/**
- * Disable entropy gathering
- *
- */
-static void rdrand_entropy_disable ( void ) {
+ /* 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 ) );
- /* Nothing to do */
+ return 0;
}
/**
@@ -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,
+};
diff --git a/src/arch/x86/include/bits/entropy.h b/src/arch/x86/include/bits/entropy.h
deleted file mode 100644
index 7accea33..00000000
--- a/src/arch/x86/include/bits/entropy.h
+++ /dev/null
@@ -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 */
diff --git a/src/arch/x86/include/ipxe/rdrand.h b/src/arch/x86/include/ipxe/rdrand.h
deleted file mode 100644
index c9c170fb..00000000
--- a/src/arch/x86/include/ipxe/rdrand.h
+++ /dev/null
@@ -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 */
diff --git a/src/arch/x86/include/ipxe/rtc_entropy.h b/src/arch/x86/include/ipxe/rtc_entropy.h
deleted file mode 100644
index 581abcd3..00000000
--- a/src/arch/x86/include/ipxe/rtc_entropy.h
+++ /dev/null
@@ -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 */
diff --git a/src/arch/x86/interface/pcbios/rtc_entropy.c b/src/arch/x86/interface/pcbios/rtc_entropy.c
index c400d8a7..8f47ff6b 100644
--- a/src/arch/x86/interface/pcbios/rtc_entropy.c
+++ b/src/arch/x86/interface/pcbios/rtc_entropy.c
@@ -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,
+};
diff --git a/src/crypto/null_entropy.c b/src/config/config_entropy.c
index d1e1a6f7..e96019a5 100644
--- a/src/crypto/null_entropy.c
+++ b/src/config/config_entropy.c
@@ -1,10 +1,8 @@
/*
- * 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.
+ * License, or (at your option) 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
@@ -23,18 +21,28 @@
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+#include <config/entropy.h>
+
/** @file
*
- * Nonexistent entropy source
- *
+ * Entropy configuration options
*
- * This source provides no entropy and must NOT be used in a
- * security-sensitive environment.
*/
-#include <ipxe/entropy.h>
+PROVIDE_REQUIRING_SYMBOL();
-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 );
+/*
+ * Drag in entropy sources
+ */
+#ifdef ENTROPY_RTC
+REQUIRE_OBJECT ( rtc_entropy );
+#endif
+#ifdef ENTROPY_EFI
+REQUIRE_OBJECT ( efi_entropy );
+#endif
+#ifdef ENTROPY_LINUX
+REQUIRE_OBJECT ( linux_entropy );
+#endif
+#ifdef ENTROPY_RDRAND
+REQUIRE_OBJECT ( rdrand );
+#endif
diff --git a/src/config/defaults/efi.h b/src/config/defaults/efi.h
index 625ae055..16c56166 100644
--- a/src/config/defaults/efi.h
+++ b/src/config/defaults/efi.h
@@ -50,6 +50,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#if defined ( __i386__ ) || defined ( __x86_64__ )
#define IOAPI_X86
#define NAP_EFIX86
+#define ENTROPY_RDRAND
#define CPUID_CMD /* x86 CPU feature detection command */
#define UNSAFE_STD /* Avoid setting direction flag */
#endif
diff --git a/src/config/defaults/linux.h b/src/config/defaults/linux.h
index 5c4106d3..21de2a2e 100644
--- a/src/config/defaults/linux.h
+++ b/src/config/defaults/linux.h
@@ -33,4 +33,8 @@ FILE_LICENCE ( GPL2_OR_LATER );
#define SANBOOT_PROTO_FCP
#define SANBOOT_PROTO_HTTP
+#if defined ( __i386__ ) || defined ( __x86_64__ )
+#define ENTROPY_RDRAND
+#endif
+
#endif /* CONFIG_DEFAULTS_LINUX_H */
diff --git a/src/config/defaults/pcbios.h b/src/config/defaults/pcbios.h
index 83835805..ee342d41 100644
--- a/src/config/defaults/pcbios.h
+++ b/src/config/defaults/pcbios.h
@@ -20,6 +20,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#define SMBIOS_PCBIOS
#define SANBOOT_PCBIOS
#define ENTROPY_RTC
+#define ENTROPY_RDRAND
#define TIME_RTC
#define REBOOT_PCBIOS
#define ACPI_RSDP
diff --git a/src/crypto/entropy.c b/src/crypto/entropy.c
index ced6fd92..204e6bb1 100644
--- a/src/crypto/entropy.c
+++ b/src/crypto/entropy.c
@@ -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
- *
- * @ret cutoff Cutoff value
+ * Enable entropy gathering
*
- * 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 );
-
- /* 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 );
+ /* 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;
+ }
- return cutoff;
+ /* 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 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 {
@@ -142,117 +159,6 @@ static int repetition_count_test ( noise_sample_t sample ) {
}
/**
- * 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
*
* @v sample Noise sample
@@ -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;
}
@@ -322,6 +230,23 @@ static int adaptive_proportion_test ( noise_sample_t sample ) {
}
/**
+ * 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
*
* @ret entropy 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;
@@ -358,31 +286,6 @@ static int get_entropy ( entropy_sample_t *entropy ) {
}
/**
- * 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
*
* @ret nonce Nonce
@@ -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,8 @@ 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 num_samples;
unsigned int i;
int rc;
@@ -432,22 +334,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-- ) {
+ for ( num_samples = 0 ; entropy_total < min_entropy ; num_samples++ ) {
/* 5.1. ( status, entropy_bitstring, assessed_entropy )
* = GetEntropy()
* 5.2. If status indicates an error, return ( status, Null )
@@ -466,19 +366,24 @@ 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 */
entropy_disable();
+ DBGC ( &entropy_source, "ENTROPY gathered %d bits in %d samples\n",
+ ( min_entropy / MIN_ENTROPY_SCALE ), num_samples );
return 0;
err_get_entropy:
entropy_disable();
return rc;
}
+
+/* Drag in objects via entropy_enable */
+REQUIRING_SYMBOL ( entropy_enable );
+
+/* Drag in entropy configuration */
+REQUIRE_OBJECT ( config_entropy );
diff --git a/src/include/ipxe/efi/efi_entropy.h b/src/include/ipxe/efi/efi_entropy.h
deleted file mode 100644
index 5b16fd7f..00000000
--- a/src/include/ipxe/efi/efi_entropy.h
+++ /dev/null
@@ -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 */
diff --git a/src/include/ipxe/entropy.h b/src/include/ipxe/entropy.h
index d2e3ce50..108c3766 100644
--- a/src/include/ipxe/entropy.h
+++ b/src/include/ipxe/entropy.h
@@ -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
- *
- * @ret rc Return status code
- */
-int entropy_enable ( void );
+/** Declare an entropy source */
+#define __entropy_source( order ) __table_entry ( ENTROPY_SOURCES, order )
-/**
- * Disable entropy gathering
+/** @defgroup entropy_source_order Entropy source order
*
+ * @{
*/
-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
- *
- * 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 );
+#define ENTROPY_PREFERRED 01 /**< Preferred entropy source */
+#define ENTROPY_NORMAL 02 /**< Normal entropy source */
+#define ENTROPY_FALLBACK 03 /**< Fallback entropy source */
-/**
- * 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,8 @@ 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 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,
@@ -154,41 +162,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 +201,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 +227,231 @@ 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 ( 0 ),
+ min_entropy_per_sample_is_zero );
+ 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 */
diff --git a/src/include/ipxe/linux/linux_entropy.h b/src/include/ipxe/linux/linux_entropy.h
deleted file mode 100644
index ea8c1f16..00000000
--- a/src/include/ipxe/linux/linux_entropy.h
+++ /dev/null
@@ -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 */
diff --git a/src/include/ipxe/null_entropy.h b/src/include/ipxe/null_entropy.h
deleted file mode 100644
index 5a6bb621..00000000
--- a/src/include/ipxe/null_entropy.h
+++ /dev/null
@@ -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 */
diff --git a/src/interface/efi/efi_entropy.c b/src/interface/efi/efi_entropy.c
index 1e8ddfb6..e5c39356 100644
--- a/src/interface/efi/efi_entropy.c
+++ b/src/interface/efi/efi_entropy.c
@@ -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,
+};
diff --git a/src/interface/linux/linux_entropy.c b/src/interface/linux/linux_entropy.c
index 257e993a..f2496979 100644
--- a/src/interface/linux/linux_entropy.c
+++ b/src/interface/linux/linux_entropy.c
@@ -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,
+};