From ab5eb86118f5b446d9cb1996b07cae56e3f45643 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Wed, 17 Feb 2021 16:50:49 -0600 Subject: [PATCH] raspberrypi: implement os.urandom Since the datasheet cast some doubt on the strength of the "rosc_hw->randombit", I use the SHA256 hash function to create a high quality random seed from random values of uncertain entropy, as well as to generate a sequence of random values from that seed using SHA256 as a cryptographically-secure random number generator. In practice, it produces over 100kB/s of random data which does not have any gross problems according to _PractRand_. --- extmod/crypto-algorithms/sha256.c | 1 + ports/raspberrypi/Makefile | 1 + ports/raspberrypi/common-hal/os/__init__.c | 65 +++++++++++++++++++++- 3 files changed, 66 insertions(+), 1 deletion(-) diff --git a/extmod/crypto-algorithms/sha256.c b/extmod/crypto-algorithms/sha256.c index 276611cfd503..9b5e45d23df7 100644 --- a/extmod/crypto-algorithms/sha256.c +++ b/extmod/crypto-algorithms/sha256.c @@ -14,6 +14,7 @@ /*************************** HEADER FILES ***************************/ #include +#include #include "sha256.h" /****************************** MACROS ******************************/ diff --git a/ports/raspberrypi/Makefile b/ports/raspberrypi/Makefile index 7ce5606f8b0c..f5e65f256026 100644 --- a/ports/raspberrypi/Makefile +++ b/ports/raspberrypi/Makefile @@ -199,6 +199,7 @@ SRC_C += \ audio_dma.c \ background.c \ peripherals/pins.c \ + extmod/crypto-algorithms/sha256.c \ fatfs_port.c \ lib/libc/string0.c \ lib/mp-readline/readline.c \ diff --git a/ports/raspberrypi/common-hal/os/__init__.c b/ports/raspberrypi/common-hal/os/__init__.c index dcbd06e937d3..0df7653d391e 100644 --- a/ports/raspberrypi/common-hal/os/__init__.c +++ b/ports/raspberrypi/common-hal/os/__init__.c @@ -30,6 +30,11 @@ #include "py/objtuple.h" #include "py/qstr.h" +#include "extmod/crypto-algorithms/sha256.h" + +#include "hardware/structs/rosc.h" + +#include STATIC const qstr os_uname_info_fields[] = { MP_QSTR_sysname, MP_QSTR_nodename, @@ -57,6 +62,64 @@ mp_obj_t common_hal_os_uname(void) { return (mp_obj_t)&os_uname_info_obj; } +// NIST Special Publication 800-90B (draft) recommends several extractors, +// including the SHA hash family and states that if the amount of entropy input +// is twice the number of bits output from them, that output can be considered +// essentially fully random. If every RANDOM_SAFETY_MARGIN bits from +// `rosc_hw->randombit` have at least 1 bit of entropy, then this criterion is met. +// +// This works by seeding the `random_state` with plenty of random bits (SHA256 +// as entropy harvesting function), then using that state it as a counter input +// (SHA256 as a CSPRNG), re-seeding at least every 256 blocks (8kB). +// +// In practice, `PractRand` doesn't detect any gross problems with the output +// random numbers on samples of 1 to 8 megabytes, no matter the setting of +// RANDOM_SAFETY_MARGIN. (it does detect "unusual" results from time to time, +// as it will with any RNG) +#define RANDOM_SAFETY_MARGIN (4) + +static BYTE random_state[SHA256_BLOCK_SIZE]; +static void seed_random_bits(BYTE out[SHA256_BLOCK_SIZE]) { + CRYAL_SHA256_CTX context; + sha256_init(&context); + for (int i=0; i<2*RANDOM_SAFETY_MARGIN; i++) { + for(int j=0; jrandombit & 1; + for(int k=0; k<8; k++) { + out[j] = (out[j] << 1) ^ (rosc_hw->randombit & 1); + } + } + sha256_update(&context, out, SHA256_BLOCK_SIZE); + } + sha256_final(&context, out); +} + +static void get_random_bits(BYTE out[SHA256_BLOCK_SIZE]) { + if (!random_state[0]++) { + seed_random_bits(random_state); + } + CRYAL_SHA256_CTX context; + sha256_init(&context); + sha256_update(&context, random_state, SHA256_BLOCK_SIZE); + sha256_final(&context, out); +} + bool common_hal_os_urandom(uint8_t* buffer, uint32_t length) { - return false; +#define ROSC_POWER_SAVE (1) // assume ROSC is not necessarily active all the time +#if ROSC_POWER_SAVE + uint32_t old_rosc_ctrl = rosc_hw->ctrl; + rosc_hw->ctrl = (old_rosc_ctrl & ~ROSC_CTRL_ENABLE_BITS) | (ROSC_CTRL_ENABLE_VALUE_ENABLE << 12); +#endif + while (length) { + size_t n = MIN(length, SHA256_BLOCK_SIZE); + BYTE sha_buf[SHA256_BLOCK_SIZE]; + get_random_bits(sha_buf); + memcpy(buffer, sha_buf, n); + buffer += n; + length -= n; + } +#if ROSC_POWER_SAVE + rosc_hw->ctrl = old_rosc_ctrl; +#endif + return true; }