[U-Boot] [RFC PATCH 0/3] collect entropy, populate /chosen/rng-seed

[This is very much an early Request For Comments - it builds and shows a sketch of what I have in mind, but has not really been tested. The rest of the cover letter is copied from patch 3.]
A recurring theme on LKML is the boot process deadlocking due to some process blocking waiting for random numbers, while the kernel's Cryptographic Random Number Generator (crng) is not initalized yet, but that very blocking means no activity happens that would generate the entropy necessary to finalize seeding the crng.
This is not a problem on boards that have a good hwrng (when the kernel is configured to trust it), whether in the CPU or in a TPM or elsewhere. However, that's far from all boards out there.
Moreover, when booting with an initrd, all the "disk activity" that would otherwise generate some timing variances has already been done by U-boot. Hence it makes sense to try to collect that entropy in U-boot and pass it on to the kernel. On the kernel side, support for that has just landed in master (commit 428826f5358c "fdt: add support for rng-seed").
By itself, this does not help with the initialization of the crng, since the kernel only considers the rng-seed "trustworthy" if CONFIG_RANDOM_TRUST_BOOTLOADER is set (it is always fed into the crng, but entropy is only accounted when that config option is set).
This adds some basic infrastructure for collecting entropy in U-boot, and then it's up to the BSP developer to decide if CONFIG_RANDOM_TRUST_BOOTLOADER should be enabled in the kernel.
If this is accepted, I think we should add entropy() calls before and after most disk and network activities. Moreover, at least some boards seem to have a rather reliable source of randomness in the contents of RAM after a cold boot [*], so I imagine exposing the entropy_mix() either via a command "entropy <addr> <len>" that can be run during a boot script, or perhaps to be done automatically (and as early as possible to reduce risk of "tainting") via some CONFIG_ENTROPY_RAM_{ADDR,LEN}.
There's probably good sources of entropy to be had from the SPL phase, but I couldn't find a good way of passing that on other than by putting the sha256_context inside global_data, which would bloat that by over 100 bytes.
[*] Looking at a slightly arbitrary place in the middle of physical memory I got these
40d07670: 34081000 400a8020 00002040 20024400 ...4 ..@@ ...D. 40d07670: 343c1000 640a9120 00036040 6006e400 ..<4 ..d@`.....` 40d07670: 353c1040 600a9120 00026041 6246e400 @.<5 ..`A`....Fb 40d07670: 34281000 600a9100 00026040 2046e400 ..(4...`@`....F
So some bits are always the same, but there's quite a few that flip randomly between boots - so mixing in a MB or two seems that it should provide plenty of real entropy.
Rasmus Villemoes (3): u-boot/sha256.h: add SHA256_INIT macro u-boot/sha256.h: include linux/types.h add infrastructure for collecting entropy
common/fdt_support.c | 12 ++++++++ include/common.h | 3 ++ include/entropy.h | 42 +++++++++++++++++++++++++++ include/u-boot/sha256.h | 13 +++++++++ lib/Kconfig | 10 +++++++ lib/Makefile | 1 + lib/entropy.c | 63 +++++++++++++++++++++++++++++++++++++++++ 7 files changed, 144 insertions(+) create mode 100644 include/entropy.h create mode 100644 lib/entropy.c

To be used for statically initializing a sha256 context.
Signed-off-by: Rasmus Villemoes rasmus.villemoes@prevas.dk --- include/u-boot/sha256.h | 11 +++++++++++ 1 file changed, 11 insertions(+)
diff --git a/include/u-boot/sha256.h b/include/u-boot/sha256.h index 9aa1251789..10f42091ee 100644 --- a/include/u-boot/sha256.h +++ b/include/u-boot/sha256.h @@ -22,4 +22,15 @@ void sha256_finish(sha256_context * ctx, uint8_t digest[SHA256_SUM_LEN]); void sha256_csum_wd(const unsigned char *input, unsigned int ilen, unsigned char *output, unsigned int chunk_sz);
+#define SHA256_INIT { \ + .state[0] = 0x6A09E667, \ + .state[1] = 0xBB67AE85, \ + .state[2] = 0x3C6EF372, \ + .state[3] = 0xA54FF53A, \ + .state[4] = 0x510E527F, \ + .state[5] = 0x9B05688C, \ + .state[6] = 0x1F83D9AB, \ + .state[7] = 0x5BE0CD19, \ + } + #endif /* _SHA256_H */

One cannot use sha256.h by itself - the includer must already have made sure that uint32_t and friends are defined; i.e., having included linux/types.h either directly or indirectly. That's a little annoying, so just make the header self-contained.
Signed-off-by: Rasmus Villemoes rasmus.villemoes@prevas.dk --- include/u-boot/sha256.h | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/include/u-boot/sha256.h b/include/u-boot/sha256.h index 10f42091ee..9e16b9715e 100644 --- a/include/u-boot/sha256.h +++ b/include/u-boot/sha256.h @@ -1,6 +1,8 @@ #ifndef _SHA256_H #define _SHA256_H
+#include <linux/types.h> + #define SHA256_SUM_LEN 32 #define SHA256_DER_LEN 19

A recurring theme on LKML is the boot process deadlocking due to some process blocking waiting for random numbers, while the kernel's Cryptographic Random Number Generator (crng) is not initalized yet, but that very blocking means no activity happens that would generate the entropy necessary to finalize seeding the crng.
This is not a problem on boards that have a good hwrng (when the kernel is configured to trust it), whether in the CPU or in a TPM or elsewhere. However, that's far from all boards out there.
Moreover, when booting with an initrd, all the "disk activity" that would otherwise generate some timing variances has already been done by U-boot. Hence it makes sense to try to collect that entropy in U-boot and pass it on to the kernel. On the kernel side, support for that has just landed in master (commit 428826f5358c "fdt: add support for rng-seed").
By itself, this does not help with the initialization of the crng, since the kernel only considers the rng-seed "trustworthy" if CONFIG_RANDOM_TRUST_BOOTLOADER is set (it is always fed into the crng, but entropy is only accounted when that config option is set).
This adds some basic infrastructure for collecting entropy in U-boot, and then it's up to the BSP developer to decide if CONFIG_RANDOM_TRUST_BOOTLOADER should be enabled in the kernel.
If this is accepted, I think we should add entropy() calls before and after most disk and network activities. Moreover, at least some boards seem to have a rather reliable source of randomness in the contents of RAM after a cold boot [*], so I imagine exposing the entropy_mix() either via a command "entropy <addr> <len>" that can be run during a boot script, or perhaps to be done automatically (and as early as possible to reduce risk of "tainting") via some CONFIG_ENTROPY_RAM_{ADDR,LEN}.
There's probably good sources of entropy to be had from the SPL phase, but I couldn't find a good way of passing that on other than by putting the sha256_context inside global_data, which would bloat that by over 100 bytes.
[*] Looking at a slightly arbitrary place in the middle of physical memory I got these
40d07670: 34081000 400a8020 00002040 20024400 ...4 ..@@ ...D. 40d07670: 343c1000 640a9120 00036040 6006e400 ..<4 ..d@`.....` 40d07670: 353c1040 600a9120 00026041 6246e400 @.<5 ..`A`....Fb 40d07670: 34281000 600a9100 00026040 2046e400 ..(4...`@`....F
So some bits are always the same, but there's quite a few that flip randomly between boots - so mixing in a MB or two seems that it should provide plenty of real entropy.
Signed-off-by: Rasmus Villemoes rasmus.villemoes@prevas.dk --- common/fdt_support.c | 12 +++++++++ include/common.h | 3 +++ include/entropy.h | 42 +++++++++++++++++++++++++++++ lib/Kconfig | 10 +++++++ lib/Makefile | 1 + lib/entropy.c | 63 ++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 131 insertions(+) create mode 100644 include/entropy.h create mode 100644 lib/entropy.c
diff --git a/common/fdt_support.c b/common/fdt_support.c index baf7924ff6..e071abaabb 100644 --- a/common/fdt_support.c +++ b/common/fdt_support.c @@ -274,6 +274,7 @@ int fdt_initrd(void *fdt, ulong initrd_start, ulong initrd_end)
int fdt_chosen(void *fdt) { + uint8_t digest[SHA256_SUM_LEN]; int nodeoffset; int err; char *str; /* used to set string properties */ @@ -299,6 +300,17 @@ int fdt_chosen(void *fdt) return err; } } + if (IS_ENABLED(CONFIG_ENTROPY)) { + entropy_digest(digest); + + err = fdt_setprop(fdt, nodeoffset, "rng-seed", digest, + sizeof(digest)); + if (err < 0) { + printf("WARNING: could not set rng-seed %s.\n", + fdt_strerror(err)); + return err; + } + }
return fdt_fixup_stdout(fdt, nodeoffset); } diff --git a/include/common.h b/include/common.h index d8f302ea92..55df86b6e7 100644 --- a/include/common.h +++ b/include/common.h @@ -331,6 +331,9 @@ void srand(unsigned int seed); unsigned int rand(void); unsigned int rand_r(unsigned int *seedp);
+/* lib/entropy.c */ +#include <entropy.h> + /* * STDIO based functions (can always be used) */ diff --git a/include/entropy.h b/include/entropy.h new file mode 100644 index 0000000000..47fa73a3a3 --- /dev/null +++ b/include/entropy.h @@ -0,0 +1,42 @@ +#ifndef __ENTROPY_H +#define __ENTROPY_H + +#include <u-boot/sha256.h> + +#if defined(CONFIG_ENTROPY) && !defined(CONFIG_SPL_BUILD) + +/* Overridable, should preferably include some timer with microsecond-or-better resolution. */ +uint64_t entropy_token(void); + +/* + * Call entropy_token() and mix in the return value - this is the main + * function for adding entropy from timing variances. Call it before + * and after any operation that may have some variation in its + * execution time. + */ +void entropy(void); + +/* + * Mix in some buffer which one has some reason to believe contains at + * least some randomness. On some boards, that might be the contents + * of some never-touched part of RAM. + */ +void entropy_mix(const void *input, unsigned int len); + +void entropy_digest(uint8_t digest[SHA256_SUM_LEN]); + +#else + +static inline void entropy(void) +{ +} +static inline void entropy_mix(const void *input, unsigned int len) +{ +} +static inline void entropy_digest(uint8_t *digest) +{ +} + +#endif + +#endif /* __ENTROPY_H */ diff --git a/lib/Kconfig b/lib/Kconfig index 3da45a5ec3..f0db0589d3 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -287,6 +287,16 @@ config TPL_TPM for the low-level TPM interface, but only one TPM is supported at a time by the TPM library.
+config ENTROPY + bool "Enable collecting entropy" + select SHA256 + help + This enables collection of entropy (randomness) from + variations in execution time of various functions, as well + as other sources. A digest of this is then passed on to the + kernel in the "rng-seed" property of the "chosen" node, + which in turn is then used to seed the kernel's crng. + endmenu
menu "Android Verified Boot" diff --git a/lib/Makefile b/lib/Makefile index 2fffd68f94..e8edb74c8a 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_ARCH_AT91) += at91/ obj-$(CONFIG_OPTEE) += optee/
obj-$(CONFIG_AES) += aes.o +obj-$(CONFIG_ENTROPY) += entropy.o
ifndef API_BUILD ifneq ($(CONFIG_UT_UNICODE)$(CONFIG_EFI_LOADER),) diff --git a/lib/entropy.c b/lib/entropy.c new file mode 100644 index 0000000000..5379c54da1 --- /dev/null +++ b/lib/entropy.c @@ -0,0 +1,63 @@ +#include <common.h> +#include <u-boot/sha256.h> +#include <watchdog.h> + +static sha256_context entropy_ctx = SHA256_INIT; + +__weak uint64_t entropy_token(void) +{ + return timer_get_us(); +} + +static void __entropy(sha256_context *ctx) +{ + uint64_t x = entropy_token(); + + sha256_update(ctx, (void*)&x, sizeof(x)); +} + +void entropy(void) +{ + sha256_context *ctx = &entropy_ctx; + + __entropy(ctx); +} + +/* + * This mixes in the @len bytes starting at @input, taking care to + * reset the watchdog every once in a while. Whether or not there is a + * watchdog to handle, we also use the opportunity to periodically mix + * in a timestamp or whatever other small ever-changing token + * get_entropy() provides. + */ +void entropy_mix(const void *input, unsigned int len) +{ + sha256_context *ctx = &entropy_ctx; + unsigned int chunk; + + while (len) { + chunk = min_t(unsigned, len, CHUNKSZ_SHA256); + sha256_update(ctx, input, chunk); + input += chunk; + len -= chunk; + + __entropy(ctx); + WATCHDOG_RESET(); + } +} + +void entropy_digest(uint8_t digest[32]) +{ + sha256_context ctx; + + /* + * Allow this to be called more than once by calling + * sha256_finish() on a copy of the context (this is for + * example necessary to allow the command "fdt chosen" to be + * run more than once). And make sure each call provides a + * different answer by updating the global context first. + */ + entropy(); + memcpy(&ctx, &entropy_ctx, sizeof(ctx)); + sha256_finish(&ctx, digest); +}
participants (1)
-
Rasmus Villemoes