]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[rng] Add ANS X9.82 Approved Source of Entropy Input
authorMichael Brown <mcb30@ipxe.org>
Sun, 19 Feb 2012 22:14:06 +0000 (22:14 +0000)
committerMichael Brown <mcb30@ipxe.org>
Tue, 21 Feb 2012 12:42:38 +0000 (12:42 +0000)
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>
12 files changed:
src/arch/i386/include/bits/entropy.h [new file with mode: 0644]
src/arch/x86_64/include/bits/entropy.h [new file with mode: 0644]
src/config/defaults/efi.h
src/config/defaults/linux.h
src/config/defaults/pcbios.h
src/config/entropy.h [new file with mode: 0644]
src/crypto/drbg.c
src/crypto/entropy.c
src/crypto/null_entropy.c [new file with mode: 0644]
src/include/ipxe/entropy.h
src/include/ipxe/hmac_drbg.h
src/include/ipxe/null_entropy.h [new file with mode: 0644]

diff --git a/src/arch/i386/include/bits/entropy.h b/src/arch/i386/include/bits/entropy.h
new file mode 100644 (file)
index 0000000..db8ba18
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef _BITS_ENTROPY_H
+#define _BITS_ENTROPY_H
+
+/** @file
+ *
+ * i386-specific entropy API implementations
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#endif /* _BITS_ENTROPY_H */
diff --git a/src/arch/x86_64/include/bits/entropy.h b/src/arch/x86_64/include/bits/entropy.h
new file mode 100644 (file)
index 0000000..9c64c83
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef _BITS_ENTROPY_H
+#define _BITS_ENTROPY_H
+
+/** @file
+ *
+ * x86_64-specific entropy API implementations
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#endif /* _BITS_ENTROPY_H */
index 693f55ad98c913dc6cb9d1fcc4ba76aa2072e4d1..c0bb78da9d72500f006638fcaa2453b1c30780a9 100644 (file)
@@ -17,6 +17,7 @@
 #define SMBIOS_EFI
 #define SANBOOT_NULL
 #define BOFM_EFI
+#define ENTROPY_NULL
 
 #define        IMAGE_EFI               /* EFI image support */
 #define        IMAGE_SCRIPT            /* iPXE script image support */
index 647dc0a530c001aee1995a6f9f026396e73bbb73..6b24da48028068084f29be85c83c4c9fecd030f1 100644 (file)
@@ -14,6 +14,7 @@
 #define NAP_LINUX
 #define SMBIOS_LINUX
 #define SANBOOT_NULL
+#define ENTROPY_NULL
 
 #define DRIVERS_LINUX
 
index 59adbf785093d68581f49a13295485cac2cf4e43..fb44d0e1615b10fa3f3d20484a79b0d63241b428 100644 (file)
@@ -18,6 +18,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
 #define UMALLOC_MEMTOP
 #define SMBIOS_PCBIOS
 #define SANBOOT_PCBIOS
+#define ENTROPY_NULL
 
 #define        IMAGE_ELF               /* ELF image support */
 #define        IMAGE_MULTIBOOT         /* MultiBoot image support */
diff --git a/src/config/entropy.h b/src/config/entropy.h
new file mode 100644 (file)
index 0000000..7de2f67
--- /dev/null
@@ -0,0 +1,16 @@
+#ifndef CONFIG_ENTROPY_H
+#define CONFIG_ENTROPY_H
+
+/** @file
+ *
+ * Entropy API configuration
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <config/defaults.h>
+
+#include <config/local/entropy.h>
+
+#endif /* CONFIG_ENTROPY_H */
index 88cf3acde657dd32611074cdf45a2088ede7205a..afd75da3fa91130c6bf0cfdc4f007f7ac3a68c04 100644 (file)
@@ -63,7 +63,7 @@ int drbg_instantiate ( struct drbg_state *state, const void *personal,
        unsigned int entropy_bits = ( ( 3 * DRBG_SECURITY_STRENGTH + 1 ) / 2 );
        size_t min_len = DRBG_MIN_ENTROPY_LEN_BYTES;
        size_t max_len = DRBG_MAX_ENTROPY_LEN_BYTES;
-       uint8_t data[ entropy_bufsize ( entropy_bits, min_len, max_len ) ];
+       uint8_t data[max_len];
        int len;
        int rc;
 
@@ -175,7 +175,7 @@ int drbg_reseed ( struct drbg_state *state, const void *additional,
        unsigned int entropy_bits = DRBG_SECURITY_STRENGTH;
        size_t min_len = DRBG_MIN_ENTROPY_LEN_BYTES;
        size_t max_len = DRBG_MAX_ENTROPY_LEN_BYTES;
-       uint8_t data[ entropy_bufsize ( entropy_bits, min_len, max_len ) ];
+       uint8_t data[max_len];
        int len;
        int rc;
 
index 86fa89783e7d8637ee3633438549aa4de408b881..d5d06e57961c2f73d285e962296a6bca99cbbe2b 100644 (file)
@@ -22,27 +22,129 @@ FILE_LICENCE ( GPL2_OR_LATER );
  *
  * Entropy source
  *
+ * This algorithm is designed to comply with ANS X9.82 Part 4 (April
+ * 2011 Draft) Section 13.3.  This standard is unfortunately not
+ * freely available.
  */
 
+#include <stdint.h>
+#include <assert.h>
 #include <string.h>
+#include <ipxe/crypto.h>
+#include <ipxe/hash_df.h>
 #include <ipxe/entropy.h>
 
 /**
- * Obtain entropy input
+ * Get entropy sample
  *
- * @v 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
+ * @ret entropy                Entropy sample
+ * @ret rc             Return status code
+ *
+ * This is the GetEntropy function defined in ANS X9.82 Part 2
+ * (October 2011 Draft) Section 6.5.1.
+ */
+static int get_entropy ( entropy_sample_t *entropy ) {
+       noise_sample_t noise;
+       int rc;
+
+       /* Get noise sample */
+       if ( ( rc = get_noise ( &noise ) ) != 0 )
+               return rc;
+
+       /* We do not use any optional conditioning component */
+       *entropy = noise;
+
+       return 0;
+}
+
+/**
+ * Create next nonce value
+ *
+ * @ret nonce          Nonce
+ *
+ * This is the MakeNextNonce function defined in ANS X9.82 Part 4
+ * (April 2011 Draft) Section 13.3.4.2.
  */
-int get_entropy_input ( unsigned int entropy_bits, void *data, size_t min_len,
-                       size_t max_len ) {
+static uint32_t make_next_nonce ( void ) {
+       static uint32_t nonce;
+
+       /* The simplest implementation of a nonce uses a large counter */
+       nonce++;
+
+       return nonce;
+}
+
+/**
+ * Obtain entropy input temporary buffer
+ *
+ * @v num_samples      Number of entropy samples
+ * @v tmp              Temporary buffer
+ * @v tmp_len          Length of temporary buffer
+ * @ret rc             Return status code
+ *
+ * This is (part of) 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.
+ */
+int get_entropy_input_tmp ( unsigned int num_samples, uint8_t *tmp,
+                           size_t tmp_len ) {
+       struct {
+               uint32_t nonce;
+               entropy_sample_t sample;
+       } __attribute__ (( packed )) data;;
+       uint8_t df_buf[tmp_len];
+       unsigned int i;
+       int rc;
+
+       /* Enable entropy gathering */
+       entropy_enable();
+
+       /* 3.  entropy_total = 0
+        *
+        * (Nothing to do; the number of entropy samples required has
+        * already been precalculated.)
+        */
+
+       /* 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-- ) {
+               /* 5.1.  ( status, entropy_bitstring, assessed_entropy )
+                *       = GetEntropy()
+                * 5.2.  If status indicates an error, return ( status, Null )
+                */
+               if ( ( rc = get_entropy ( &data.sample ) ) != 0 )
+                       goto err_get_entropy;
+
+               /* 5.3.  nonce = MakeNextNonce() */
+               data.nonce = make_next_nonce();
+
+               /* 5.4.  tmp = tmp XOR
+                *             df ( ( nonce || entropy_bitstring ), n )
+                */
+               hash_df ( &data, sizeof ( data ), df_buf, sizeof ( df_buf ) );
+               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.)
+                */
+       }
+
+       /* Disable entropy gathering */
+       entropy_disable();
 
-       /* Placeholder to allow remainder of RBG code to be tested */
-       ( void ) entropy_bits;
-       ( void ) min_len;
-       memset ( data, 0x01, max_len );
+       return 0;
 
-       return max_len;
+ err_get_entropy:
+       entropy_disable();
+       return rc;
 }
diff --git a/src/crypto/null_entropy.c b/src/crypto/null_entropy.c
new file mode 100644 (file)
index 0000000..be2acae
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+/** @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 );
index d9b70848c4aeeee4e0e359ba3e8bed26cef6fd1b..8a1974a1ee5e08182def9fd2fd2c8ba629e2cde3 100644 (file)
 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
+double min_entropy_per_sample ( void );
 
-/** Length of each entropy sample (in bits) */
-#define ENTROPY_SAMPLE_LEN_BITS 12
+/**
+ * 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 );
 
 /**
- * Calculate entropy buffer size
+ * 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
  *
- * @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
+ * 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__ (( const, always_inline )) size_t
-entropy_bufsize ( unsigned int entropy_bits, size_t min_len, size_t max_len ) {
-       unsigned int min_len_bits;
+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;
-       double samples;
-       unsigned int samples_int;
-       unsigned int len_bits;
-       size_t len;
+       unsigned int num_samples;
+       unsigned int n;
+       int rc;
 
-       /* Sanity check */
-       linker_assert ( MIN_ENTROPY_PER_SAMPLE <= ENTROPY_SAMPLE_LEN_BITS,
+       /* 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 );
 
-       /* Calculate number of samples required to contain sufficient entropy */
-       samples = ( ( entropy_bits * 1.0 ) / MIN_ENTROPY_PER_SAMPLE );
+       /* Round up minimum entropy to an integral number of bytes */
+       min_entropy_bits = ( ( min_entropy_bits + 7 ) & ~7 );
 
-       /* 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;
+       /* Calculate number of samples required to contain sufficient entropy */
+       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 );
-
-       return len;
+       linker_assert ( __builtin_constant_p ( num_samples ),
+                       num_samples_not_constant );
+
+       /* 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 */
index c751cbcb6291c30aa24a9159aef7c4531abf06f3..b3dfe3682475b007190a6082eddfefd32ba730f9 100644 (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
  *
diff --git a/src/include/ipxe/null_entropy.h b/src/include/ipxe/null_entropy.h
new file mode 100644 (file)
index 0000000..0bfec80
--- /dev/null
@@ -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 */