|
30 | 30 | #include "py/objtuple.h"
|
31 | 31 | #include "py/qstr.h"
|
32 | 32 |
|
| 33 | +#include "extmod/crypto-algorithms/sha256.h" |
| 34 | + |
| 35 | +#include "hardware/structs/rosc.h" |
| 36 | + |
| 37 | +#include <string.h> |
33 | 38 |
|
34 | 39 | STATIC const qstr os_uname_info_fields[] = {
|
35 | 40 | MP_QSTR_sysname, MP_QSTR_nodename,
|
@@ -57,6 +62,64 @@ mp_obj_t common_hal_os_uname(void) {
|
57 | 62 | return (mp_obj_t)&os_uname_info_obj;
|
58 | 63 | }
|
59 | 64 |
|
| 65 | +// NIST Special Publication 800-90B (draft) recommends several extractors, |
| 66 | +// including the SHA hash family and states that if the amount of entropy input |
| 67 | +// is twice the number of bits output from them, that output can be considered |
| 68 | +// essentially fully random. If every RANDOM_SAFETY_MARGIN bits from |
| 69 | +// `rosc_hw->randombit` have at least 1 bit of entropy, then this criterion is met. |
| 70 | +// |
| 71 | +// This works by seeding the `random_state` with plenty of random bits (SHA256 |
| 72 | +// as entropy harvesting function), then using that state it as a counter input |
| 73 | +// (SHA256 as a CSPRNG), re-seeding at least every 256 blocks (8kB). |
| 74 | +// |
| 75 | +// In practice, `PractRand` doesn't detect any gross problems with the output |
| 76 | +// random numbers on samples of 1 to 8 megabytes, no matter the setting of |
| 77 | +// RANDOM_SAFETY_MARGIN. (it does detect "unusual" results from time to time, |
| 78 | +// as it will with any RNG) |
| 79 | +#define RANDOM_SAFETY_MARGIN (4) |
| 80 | + |
| 81 | +static BYTE random_state[SHA256_BLOCK_SIZE]; |
| 82 | +static void seed_random_bits(BYTE out[SHA256_BLOCK_SIZE]) { |
| 83 | + CRYAL_SHA256_CTX context; |
| 84 | + sha256_init(&context); |
| 85 | + for (int i=0; i<2*RANDOM_SAFETY_MARGIN; i++) { |
| 86 | + for(int j=0; j<SHA256_BLOCK_SIZE; j++) { |
| 87 | + out[j] = rosc_hw->randombit & 1; |
| 88 | + for(int k=0; k<8; k++) { |
| 89 | + out[j] = (out[j] << 1) ^ (rosc_hw->randombit & 1); |
| 90 | + } |
| 91 | + } |
| 92 | + sha256_update(&context, out, SHA256_BLOCK_SIZE); |
| 93 | + } |
| 94 | + sha256_final(&context, out); |
| 95 | +} |
| 96 | + |
| 97 | +static void get_random_bits(BYTE out[SHA256_BLOCK_SIZE]) { |
| 98 | + if (!random_state[0]++) { |
| 99 | + seed_random_bits(random_state); |
| 100 | + } |
| 101 | + CRYAL_SHA256_CTX context; |
| 102 | + sha256_init(&context); |
| 103 | + sha256_update(&context, random_state, SHA256_BLOCK_SIZE); |
| 104 | + sha256_final(&context, out); |
| 105 | +} |
| 106 | + |
60 | 107 | bool common_hal_os_urandom(uint8_t* buffer, uint32_t length) {
|
61 |
| - return false; |
| 108 | +#define ROSC_POWER_SAVE (1) // assume ROSC is not necessarily active all the time |
| 109 | +#if ROSC_POWER_SAVE |
| 110 | + uint32_t old_rosc_ctrl = rosc_hw->ctrl; |
| 111 | + rosc_hw->ctrl = (old_rosc_ctrl & ~ROSC_CTRL_ENABLE_BITS) | (ROSC_CTRL_ENABLE_VALUE_ENABLE << 12); |
| 112 | +#endif |
| 113 | + while (length) { |
| 114 | + size_t n = MIN(length, SHA256_BLOCK_SIZE); |
| 115 | + BYTE sha_buf[SHA256_BLOCK_SIZE]; |
| 116 | + get_random_bits(sha_buf); |
| 117 | + memcpy(buffer, sha_buf, n); |
| 118 | + buffer += n; |
| 119 | + length -= n; |
| 120 | + } |
| 121 | +#if ROSC_POWER_SAVE |
| 122 | + rosc_hw->ctrl = old_rosc_ctrl; |
| 123 | +#endif |
| 124 | + return true; |
62 | 125 | }
|
0 commit comments