summaryrefslogtreecommitdiffstats
path: root/src/arch/x86/interface/efi/efi_entropy.c
diff options
context:
space:
mode:
authorMichael Brown2015-04-14 18:53:13 +0200
committerMichael Brown2015-04-14 18:53:13 +0200
commit7ca801d637de3e2941f02f376ee1f0e26eabdfcb (patch)
treed145554705718f0598a96ae837fa35561089396a /src/arch/x86/interface/efi/efi_entropy.c
parent[efi] Poll for TX completions only when there is an outstanding TX buffer (diff)
downloadipxe-7ca801d637de3e2941f02f376ee1f0e26eabdfcb.tar.gz
ipxe-7ca801d637de3e2941f02f376ee1f0e26eabdfcb.tar.xz
ipxe-7ca801d637de3e2941f02f376ee1f0e26eabdfcb.zip
[efi] Use the EFI_RNG_PROTOCOL as an entropy source if available
Entropy gathering via timer ticks is slow under UEFI (of the order of 20-30 seconds on some machines). Use the EFI_RNG_PROTOCOL if available, to speed up the process of entropy gathering. Note that some implementations (including EDK2) will fail if we request fewer than 32 random bytes at a time, and that the RNG protocol provides no guarantees about the amount of entropy provided by a call to GetRNG(). We take the (hopefully pessimistic) view that a 32-byte block returned by GetRNG() will contain at least the 1.3 bits of entropy claimed by min_entropy_per_sample(). Signed-off-by: Michael Brown <mcb30@ipxe.org>
Diffstat (limited to 'src/arch/x86/interface/efi/efi_entropy.c')
-rw-r--r--src/arch/x86/interface/efi/efi_entropy.c79
1 files changed, 76 insertions, 3 deletions
diff --git a/src/arch/x86/interface/efi/efi_entropy.c b/src/arch/x86/interface/efi/efi_entropy.c
index 54d61e91..a54bd12e 100644
--- a/src/arch/x86/interface/efi/efi_entropy.c
+++ b/src/arch/x86/interface/efi/efi_entropy.c
@@ -25,7 +25,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <errno.h>
#include <ipxe/entropy.h>
+#include <ipxe/crc32.h>
#include <ipxe/efi/efi.h>
+#include <ipxe/efi/Protocol/Rng.h>
/** @file
*
@@ -33,6 +35,23 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
*
*/
+/** Random number generator protocol */
+static EFI_RNG_PROTOCOL *efirng;
+EFI_REQUEST_PROTOCOL ( EFI_RNG_PROTOCOL, &efirng );
+
+/** Minimum number of bytes to request from RNG
+ *
+ * The UEFI spec states (for no apparently good reason) that "When a
+ * Deterministic Random Bit Generator (DRBG) is used on the output of
+ * a (raw) entropy source, its security level must be at least 256
+ * bits." The EDK2 codebase (mis)interprets this to mean that the
+ * call to GetRNG() should fail if given a buffer less than 32 bytes.
+ *
+ * Incidentally, nothing in the EFI RNG protocol provides any way to
+ * report the actual amount of entropy returned by GetRNG().
+ */
+#define EFI_ENTROPY_RNG_LEN 32
+
/** Time (in 100ns units) to delay waiting for timer tick
*
* In theory, UEFI allows us to specify a trigger time of zero to
@@ -56,6 +75,9 @@ static int efi_entropy_enable ( void ) {
EFI_STATUS efirc;
int rc;
+ DBGC ( &tick, "ENTROPY %s RNG protocol\n",
+ ( efirng ? "has" : "has no" ) );
+
/* Create timer tick event */
if ( ( efirc = bs->CreateEvent ( EVT_TIMER, TPL_NOTIFY, NULL, NULL,
&tick ) ) != 0 ) {
@@ -80,7 +102,7 @@ static void efi_entropy_disable ( void ) {
}
/**
- * Wait for an RTC tick
+ * Wait for a timer tick
*
* @ret low TSC low-order bits, or negative error
*/
@@ -114,12 +136,12 @@ static int efi_entropy_tick ( void ) {
}
/**
- * Get noise sample
+ * Get noise sample from timer ticks
*
* @ret noise Noise sample
* @ret rc Return status code
*/
-static int efi_get_noise ( noise_sample_t *noise ) {
+static int efi_get_noise_ticks ( noise_sample_t *noise ) {
int before;
int after;
int rc;
@@ -144,6 +166,57 @@ static int efi_get_noise ( noise_sample_t *noise ) {
return 0;
}
+/**
+ * Get noise sample from RNG protocol
+ *
+ * @ret noise Noise sample
+ * @ret rc Return status code
+ */
+static int efi_get_noise_rng ( noise_sample_t *noise ) {
+ uint8_t buf[EFI_ENTROPY_RNG_LEN];
+ EFI_STATUS efirc;
+ int rc;
+
+ /* Fail if we have no EFI RNG protocol */
+ if ( ! efirng )
+ return -ENOTSUP;
+
+ /* Get the minimum allowed number of random bytes */
+ if ( ( efirc = efirng->GetRNG ( efirng, NULL, EFI_ENTROPY_RNG_LEN,
+ buf ) ) != 0 ) {
+ rc = -EEFI ( efirc );
+ DBGC ( &tick, "ENTROPY could not read from RNG: %s\n",
+ strerror ( rc ) );
+ return rc;
+ }
+
+ /* Reduce random bytes to a single noise sample. This seems
+ * like overkill, but we have no way of knowing how much
+ * entropy is actually present in the bytes returned by the
+ * RNG protocol.
+ */
+ *noise = crc32_le ( 0, buf, sizeof ( buf ) );
+
+ return 0;
+}
+
+/**
+ * Get noise sample
+ *
+ * @ret noise Noise sample
+ * @ret rc Return status code
+ */
+static int efi_get_noise ( noise_sample_t *noise ) {
+ int rc;
+
+ /* Try RNG first, falling back to timer ticks */
+ if ( ( ( rc = efi_get_noise_rng ( noise ) ) != 0 ) &&
+ ( ( rc = efi_get_noise_ticks ( noise ) ) != 0 ) )
+ return rc;
+
+ 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 );