diff options
author | Michael Brown <mcb30@ipxe.org> | 2023-02-20 14:08:49 +0000 |
---|---|---|
committer | Michael Brown <mcb30@ipxe.org> | 2023-02-20 14:53:10 +0000 |
commit | 471599dc7721d454b6658062c901b52038a78be2 (patch) | |
tree | 4829951380f8701b5909a9192e2e0678fe37a36d | |
parent | 7d71cf318a2a6fedde7aaf9303b1cdec0cf51660 (diff) | |
download | ipxe-471599dc7721d454b6658062c901b52038a78be2.zip ipxe-471599dc7721d454b6658062c901b52038a78be2.tar.gz ipxe-471599dc7721d454b6658062c901b52038a78be2.tar.bz2 |
[efi] Split out EFI_RNG_PROTOCOL as a separate entropy source
Commit 7ca801d ("[efi] Use the EFI_RNG_PROTOCOL as an entropy source
if available") added EFI_RNG_PROTOCOL as an alternative entropy source
via an ad-hoc mechanism specific to efi_entropy.c.
Split out EFI_RNG_PROTOCOL to a separate entropy source, and allow the
entropy core to handle the selection of RDRAND, EFI_RNG_PROTOCOL, or
timer ticks as the active source.
The fault detection logic added in commit a87537d ("[efi] Detect and
disable seriously broken EFI_RNG_PROTOCOL implementations") may be
removed completely, since the failure will already be detected by the
generic ANS X9.82-mandated repetition count test and will now be
handled gracefully by the entropy core.
Signed-off-by: Michael Brown <mcb30@ipxe.org>
-rw-r--r-- | src/config/config_entropy.c | 5 | ||||
-rw-r--r-- | src/config/defaults/efi.h | 3 | ||||
-rw-r--r-- | src/include/ipxe/errfile.h | 1 | ||||
-rw-r--r-- | src/interface/efi/efi_entropy.c | 95 | ||||
-rw-r--r-- | src/interface/efi/efi_rng.c | 118 |
5 files changed, 130 insertions, 92 deletions
diff --git a/src/config/config_entropy.c b/src/config/config_entropy.c index e96019a..9f12f1f 100644 --- a/src/config/config_entropy.c +++ b/src/config/config_entropy.c @@ -37,9 +37,12 @@ PROVIDE_REQUIRING_SYMBOL(); #ifdef ENTROPY_RTC REQUIRE_OBJECT ( rtc_entropy ); #endif -#ifdef ENTROPY_EFI +#ifdef ENTROPY_EFITICK REQUIRE_OBJECT ( efi_entropy ); #endif +#ifdef ENTROPY_EFIRNG +REQUIRE_OBJECT ( efi_rng ); +#endif #ifdef ENTROPY_LINUX REQUIRE_OBJECT ( linux_entropy ); #endif diff --git a/src/config/defaults/efi.h b/src/config/defaults/efi.h index 16c5616..8e53b9a 100644 --- a/src/config/defaults/efi.h +++ b/src/config/defaults/efi.h @@ -19,7 +19,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define SMBIOS_EFI #define SANBOOT_EFI #define BOFM_EFI -#define ENTROPY_EFI +#define ENTROPY_EFITICK +#define ENTROPY_EFIRNG #define TIME_EFI #define REBOOT_EFI #define ACPI_EFI diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index d7b6ea1..e6fd852 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -403,6 +403,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ERRFILE_pci_cmd ( ERRFILE_OTHER | 0x00590000 ) #define ERRFILE_dhe ( ERRFILE_OTHER | 0x005a0000 ) #define ERRFILE_efi_cmdline ( ERRFILE_OTHER | 0x005b0000 ) +#define ERRFILE_efi_rng ( ERRFILE_OTHER | 0x005c0000 ) /** @} */ diff --git a/src/interface/efi/efi_entropy.c b/src/interface/efi/efi_entropy.c index e5c3935..cda1c36 100644 --- a/src/interface/efi/efi_entropy.c +++ b/src/interface/efi/efi_entropy.c @@ -25,10 +25,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <errno.h> #include <ipxe/entropy.h> -#include <ipxe/crc32.h> #include <ipxe/profile.h> #include <ipxe/efi/efi.h> -#include <ipxe/efi/Protocol/Rng.h> /** @file * @@ -36,24 +34,7 @@ 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 ); - -/** 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 +struct entropy_source efitick_entropy __entropy_source ( ENTROPY_FALLBACK ); /** Time (in 100ns units) to delay waiting for timer tick * @@ -78,9 +59,6 @@ static int efi_entropy_enable ( void ) { EFI_STATUS efirc; int rc; - DBGC ( &tick, "ENTROPY %s RNG protocol\n", - ( efirng ? "has" : "has no" ) ); - /* Drop to external TPL to allow timer tick event to take place */ bs->RestoreTPL ( efi_external_tpl ); @@ -97,7 +75,7 @@ static int efi_entropy_enable ( void ) { * RTC-based entropy source, and so assume the same * min-entropy per sample. */ - entropy_init ( &efi_entropy, MIN_ENTROPY ( 1.3 ) ); + entropy_init ( &efitick_entropy, MIN_ENTROPY ( 1.3 ) ); return 0; } @@ -155,7 +133,7 @@ static int efi_entropy_tick ( void ) { * @ret noise Noise sample * @ret rc Return status code */ -static int efi_get_noise_ticks ( noise_sample_t *noise ) { +static int efi_get_noise ( noise_sample_t *noise ) { int before; int after; int rc; @@ -180,72 +158,9 @@ static int efi_get_noise_ticks ( 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 ) { - static uint8_t prev[EFI_ENTROPY_RNG_LEN]; - 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; - } - - /* Fail (and permanently disable the EFI RNG) if we get - * consecutive identical results. - */ - if ( memcmp ( buf, prev, sizeof ( buf ) ) == 0 ) { - DBGC ( &tick, "ENTROPY detected broken EFI RNG:\n" ); - DBGC_HDA ( &tick, 0, buf, sizeof ( buf ) ); - efirng = NULL; - return -EIO; - } - memcpy ( prev, buf, sizeof ( prev ) ); - - /* 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; -} - /** EFI entropy source */ -struct entropy_source efi_entropy __entropy_source ( ENTROPY_NORMAL ) = { - .name = "efi", +struct entropy_source efitick_entropy __entropy_source ( ENTROPY_FALLBACK ) = { + .name = "efitick", .enable = efi_entropy_enable, .disable = efi_entropy_disable, .get_noise = efi_get_noise, diff --git a/src/interface/efi/efi_rng.c b/src/interface/efi/efi_rng.c new file mode 100644 index 0000000..b76a6fc --- /dev/null +++ b/src/interface/efi/efi_rng.c @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2015 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 (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 + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +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 + * + * EFI random number generator protocol entropy source + * + */ + +struct entropy_source efirng_entropy __entropy_source ( ENTROPY_NORMAL ); + +/** 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 EFIRNG_LEN 32 + +/** + * Enable entropy gathering + * + * @ret rc Return status code + */ +static int efirng_enable ( void ) { + + /* Check for RNG protocol support */ + if ( ! efirng ) { + DBGC ( &efirng, "EFIRNG has no RNG protocol\n" ); + return -ENOTSUP; + } + + /* Nothing in the EFI specification provides any clue as to + * how much entropy will be returned by GetRNG(). Make a + * totally uninformed (and conservative guess) that each + * sample will contain at least one bit of entropy. + */ + entropy_init ( &efirng_entropy, MIN_ENTROPY ( 1.0 ) ); + + return 0; +} + +/** + * Get noise sample from RNG protocol + * + * @ret noise Noise sample + * @ret rc Return status code + */ +static int efirng_get_noise ( noise_sample_t *noise ) { + uint8_t buf[EFIRNG_LEN]; + EFI_STATUS efirc; + int rc; + + /* Sanity check */ + assert ( efirng != NULL ); + + /* Get the minimum allowed number of random bytes */ + if ( ( efirc = efirng->GetRNG ( efirng, NULL, sizeof ( buf ), + buf ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( &efirng, "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; +} + +/** EFI random number generator protocol entropy source */ +struct entropy_source efirng_entropy __entropy_source ( ENTROPY_NORMAL ) = { + .name = "efirng", + .enable = efirng_enable, + .get_noise = efirng_get_noise, +}; |