|
1 | | -/* rp2350_rng.c - RP2350 ring-oscillator entropy for wolfCrypt |
| 1 | +/* rp2350_rng.c - RP2350 hardware TRNG seed source for wolfCrypt |
2 | 2 | * |
3 | 3 | * Copyright (C) 2026 wolfSSL Inc. |
4 | 4 | * |
|
13 | 13 | * (CUSTOM_RAND_GENERATE_SEED in user_settings.h), which in turn generates the |
14 | 14 | * WPA 4-way SNonce and the WPA3-SAE ephemeral rand/mask scalars. |
15 | 15 | * |
16 | | - * The RP2350 ROSC_RANDOMBIT register is documented as biased and strongly |
17 | | - * autocorrelated and is explicitly NOT a cryptographic entropy source on its |
18 | | - * own. So this does three things before handing bytes to the DRBG: |
19 | | - * 1. von Neumann debiasing - sample raw ROSC bits in pairs and keep one only |
20 | | - * when the pair differs, removing first-order bias; |
21 | | - * 2. heavy oversampling - a large debiased pool feeds each output block; |
22 | | - * 3. cryptographic conditioning - SHA-256 over the pool produces the seed |
23 | | - * block, compressing a conservative per-bit entropy estimate into a |
24 | | - * full-entropy 256-bit block. |
25 | | - * A startup/continuous health test fails closed (returns -1) if the oscillator |
26 | | - * stops producing differing pairs, rather than emitting a weak seed. |
| 16 | + * Entropy comes from the RP2350 hardware TRNG (0x400f0000): a ring-oscillator |
| 17 | + * true-RNG with built-in autocorrelation, continuous (CRNGT) and von-Neumann |
| 18 | + * health tests (RP2350 datasheet sec. 12.12). Its 192-bit EHR output is a NIST |
| 19 | + * SP800-90B entropy source, so it is fed straight into the DRBG seed buffer - |
| 20 | + * wolfCrypt's Hash-DRBG (SP800-90A) does the conditioning. The register access |
| 21 | + * lives in rp2350_rng.h so the non-crypto wolfIP_getrandom() path can share it. |
27 | 22 | * |
28 | | - * NOTE: this conditioning is the minimum bar for enabling the SAE/EAP |
29 | | - * supplicant paths on real hardware; if the RP2350 TRNG/SHA hardware entropy |
30 | | - * peripheral is brought up later it should supersede the ROSC pool here. |
| 23 | + * A block read fails closed (returns -1) if the source is stuck or a health |
| 24 | + * test does not clear, rather than emitting a weak seed. |
31 | 25 | */ |
32 | 26 | #include <stdint.h> |
33 | 27 | #include <wolfssl/wolfcrypt/settings.h> |
34 | | -#include <wolfssl/wolfcrypt/sha256.h> |
| 28 | +#include <wolfssl/wolfcrypt/memory.h> /* wc_ForceZero */ |
35 | 29 |
|
36 | | -/* ROSC random-bit register (RP2350: ROSC base 0x400C8000, RANDOMBIT |
37 | | - * at offset 0x1C). Bit 0 is the raw oscillator-jitter bit. */ |
38 | | -#define ROSC_RANDOMBIT (*(volatile uint32_t *)0x400C801CUL) |
39 | | - |
40 | | -/* Per debiased bit: how many raw pairs to sample before declaring the |
41 | | - * oscillator stuck. A healthy ROSC produces a differing pair roughly every |
42 | | - * other sample, so this budget is only hit on a hardware fault. */ |
43 | | -#define ROSC_VN_MAX_PAIRS 1024U |
44 | | -/* Debiased bytes hashed into each 32-byte conditioned output block. */ |
45 | | -#define ROSC_POOL_BYTES 64U |
46 | | - |
47 | | -static unsigned int rosc_bit(void) |
48 | | -{ |
49 | | - volatile int d; |
50 | | - /* Let the ROSC bit settle so consecutive raw samples decorrelate. */ |
51 | | - for (d = 0; d < 16; d++) { |
52 | | - } |
53 | | - return (unsigned int)(ROSC_RANDOMBIT & 1U); |
54 | | -} |
55 | | - |
56 | | -/* von Neumann extractor: emit a bit only when a raw pair differs (10 -> 1, |
57 | | - * 01 -> 0). Returns 0 and sets *out_bit on success, -1 if the per-bit pair |
58 | | - * budget is exhausted (oscillator stuck). */ |
59 | | -static int rosc_vn_bit(unsigned int *out_bit) |
60 | | -{ |
61 | | - unsigned int a, b, tries; |
62 | | - |
63 | | - for (tries = 0; tries < ROSC_VN_MAX_PAIRS; tries++) { |
64 | | - a = rosc_bit(); |
65 | | - b = rosc_bit(); |
66 | | - if (a != b) { |
67 | | - *out_bit = a; |
68 | | - return 0; |
69 | | - } |
70 | | - } |
71 | | - return -1; |
72 | | -} |
| 30 | +#include "rp2350_rng.h" |
73 | 31 |
|
74 | 32 | int rp2350_wc_genseed(unsigned char *output, unsigned int sz) |
75 | 33 | { |
76 | | - wc_Sha256 sha; |
77 | | - unsigned char digest[WC_SHA256_DIGEST_SIZE]; |
78 | | - unsigned char pool[ROSC_POOL_BYTES]; |
79 | | - unsigned int produced; |
80 | | - unsigned int pi, nbits, bit, take, j; |
81 | | - unsigned char byte; |
82 | | - int ret; |
| 34 | + uint32_t block[RP2350_TRNG_EHR_WORDS]; |
| 35 | + unsigned int produced, take, i; |
83 | 36 |
|
84 | 37 | if (output == NULL) { |
85 | 38 | return -1; |
86 | 39 | } |
87 | 40 |
|
88 | 41 | for (produced = 0; produced < sz; ) { |
89 | | - /* Fill the pool with von Neumann-debiased ROSC bits. */ |
90 | | - for (pi = 0; pi < ROSC_POOL_BYTES; pi++) { |
91 | | - byte = 0; |
92 | | - for (nbits = 0; nbits < 8U; nbits++) { |
93 | | - if (rosc_vn_bit(&bit) != 0) { |
94 | | - /* Health test failed: fail closed, do not emit weak seed. */ |
95 | | - wc_ForceZero(pool, sizeof(pool)); |
96 | | - return -1; |
97 | | - } |
98 | | - byte = (unsigned char)((byte << 1) | (bit & 1U)); |
99 | | - } |
100 | | - pool[pi] = byte; |
101 | | - } |
102 | | - |
103 | | - /* SHA-256 conditioner over the pool (+ running offset so per-block |
104 | | - * hashes never collide even on an identical pool). */ |
105 | | - ret = wc_InitSha256(&sha); |
106 | | - if (ret == 0) { |
107 | | - ret = wc_Sha256Update(&sha, pool, (word32)sizeof(pool)); |
108 | | - } |
109 | | - if (ret == 0) { |
110 | | - ret = wc_Sha256Update(&sha, (const unsigned char *)&produced, |
111 | | - (word32)sizeof(produced)); |
112 | | - } |
113 | | - if (ret == 0) { |
114 | | - ret = wc_Sha256Final(&sha, digest); |
115 | | - } |
116 | | - wc_Sha256Free(&sha); |
117 | | - if (ret != 0) { |
118 | | - wc_ForceZero(pool, sizeof(pool)); |
119 | | - wc_ForceZero(digest, sizeof(digest)); |
| 42 | + if (rp2350_trng_read_block(block) != 0) { |
| 43 | + /* Health test failed: fail closed, do not emit a weak seed. */ |
| 44 | + wc_ForceZero(block, sizeof(block)); |
120 | 45 | return -1; |
121 | 46 | } |
122 | | - |
123 | 47 | take = sz - produced; |
124 | | - if (take > (unsigned int)sizeof(digest)) { |
125 | | - take = (unsigned int)sizeof(digest); |
| 48 | + if (take > (unsigned int)sizeof(block)) { |
| 49 | + take = (unsigned int)sizeof(block); |
126 | 50 | } |
127 | | - for (j = 0; j < take; j++) { |
128 | | - output[produced + j] = digest[j]; |
| 51 | + for (i = 0; i < take; i++) { |
| 52 | + output[produced + i] = ((const unsigned char *)block)[i]; |
129 | 53 | } |
130 | 54 | produced += take; |
131 | 55 | } |
132 | 56 |
|
133 | | - wc_ForceZero(pool, sizeof(pool)); |
134 | | - wc_ForceZero(digest, sizeof(digest)); |
| 57 | + wc_ForceZero(block, sizeof(block)); |
135 | 58 | return 0; |
136 | 59 | } |
0 commit comments