1818
1919#include <string.h>
2020
21+ #ifdef HAS_RP2350_TRNG
22+ #include "hardware/structs/trng.h"
23+ #include "hardware/sync.h"
24+ #endif
25+
2126// NIST Special Publication 800-90B (draft) recommends several extractors,
2227// including the SHA hash family and states that if the amount of entropy input
2328// is twice the number of bits output from them, that output can be considered
24- // essentially fully random. If every RANDOM_SAFETY_MARGIN bits from
25- // `rosc_hw->randombit` have at least 1 bit of entropy, then this criterion is met.
29+ // essentially fully random.
30+ //
31+ // This works by seeding `random_state` with entropy from hardware sources
32+ // (SHA-256 as the conditioning function), then using that state as a counter
33+ // input (SHA-256 as a CSPRNG), re-seeding at least every 256 blocks (8kB).
2634//
27- // This works by seeding the `random_state` with plenty of random bits (SHA256
28- // as entropy harvesting function), then using that state it as a counter input
29- // (SHA256 as a CSPRNG), re-seeding at least every 256 blocks (8kB).
35+ // On RP2350, entropy comes from both the dedicated TRNG peripheral and the
36+ // ROSC. On RP2040, the ROSC is the only available source.
3037//
3138// In practice, `PractRand` doesn't detect any gross problems with the output
3239// random numbers on samples of 1 to 8 megabytes, no matter the setting of
33- // RANDOM_SAFETY_MARGIN . (it does detect "unusual" results from time to time,
40+ // ROSC_SAFETY_MARGIN . (it does detect "unusual" results from time to time,
3441// as it will with any RNG)
35- #define RANDOM_SAFETY_MARGIN (4)
42+
43+ // Number of ROSC collection rounds on RP2040. Each round feeds
44+ // SHA256_BLOCK_SIZE bytes into the hash; we do 2*N rounds so the
45+ // raw-to-output ratio satisfies 800-90B's 2:1 minimum.
46+ #define ROSC_SAFETY_MARGIN (4)
3647
3748static BYTE random_state [SHA256_BLOCK_SIZE ];
49+
50+ // Collect `count` bytes from the ROSC, one bit per read.
51+ static void rosc_random_bytes (BYTE * buf , size_t count ) {
52+ for (size_t i = 0 ; i < count ; i ++ ) {
53+ buf [i ] = rosc_hw -> randombit & 1 ;
54+ for (int k = 0 ; k < 8 ; k ++ ) {
55+ buf [i ] = (buf [i ] << 1 ) ^ (rosc_hw -> randombit & 1 );
56+ }
57+ }
58+ }
59+
60+ #ifdef HAS_RP2350_TRNG
61+
62+ // TRNG_DEBUG_CONTROL bypass bits:
63+ //
64+ // bit 1 VNC_BYPASS Von Neumann corrector
65+ // bit 2 TRNG_CRNGT_BYPASS Continuous Random Number Generator Test
66+ // bit 3 AUTO_CORRELATE_BYPASS Autocorrelation test
67+ //
68+ // We bypass Von Neumann and autocorrelation but keep CRNGT.
69+ //
70+ // Von Neumann (bypassed): ~4x throughput cost for bias removal.
71+ // Redundant here because SHA-256 conditioning already handles
72+ // biased input -- that's what the 2:1 oversampling ratio is for.
73+ //
74+ // Autocorrelation (bypassed): has a non-trivial false-positive rate
75+ // at high sampling speeds and halts the TRNG until SW reset on
76+ // failure. SHA-256 is not bothered by correlated input. ARM's own
77+ // TZ-TRNG 90B reference configuration also bypasses it (0x0A).
78+ //
79+ // CRNGT (kept): compares consecutive 192-bit EHR outputs. Flags if
80+ // identical -- false-positive rate 2^-192, throughput cost zero.
81+ // This is our early warning for a stuck oscillator or a successful
82+ // injection lock to a fixed state.
83+ #define TRNG_BYPASS_BITS \
84+ (TRNG_TRNG_DEBUG_CONTROL_VNC_BYPASS_BITS | \
85+ TRNG_TRNG_DEBUG_CONTROL_AUTO_CORRELATE_BYPASS_BITS)
86+
87+ // Collect 192 raw bits (6 x 32-bit words) from the TRNG.
88+ // Returns false on CRNGT failure (consecutive identical EHR outputs).
89+ //
90+ // Holds PICO_SPINLOCK_ID_RAND (the SDK's lock for this peripheral)
91+ // with interrupts disabled for the duration of the collection, which
92+ // takes ~192 ROSC cycles (~24us at 8MHz).
93+ static bool trng_collect_192 (uint32_t out [6 ]) {
94+ spin_lock_t * lock = spin_lock_instance (PICO_SPINLOCK_ID_RAND );
95+ uint32_t save = spin_lock_blocking (lock );
96+
97+ trng_hw -> trng_debug_control = TRNG_BYPASS_BITS ;
98+ // One rng_clk cycle between samples. The SDK uses 0 here, but it
99+ // also sets debug_control = -1u (full bypass). The behavior of
100+ // sample_cnt1 = 0 with health tests still active is undocumented,
101+ // so we use 1 to be safe.
102+ trng_hw -> sample_cnt1 = 1 ;
103+ trng_hw -> rnd_source_enable = 1 ;
104+ trng_hw -> rng_icr = 0xFFFFFFFF ;
105+
106+ while (trng_hw -> trng_busy ) {
107+ }
108+
109+ if (trng_hw -> rng_isr & TRNG_RNG_ISR_CRNGT_ERR_BITS ) {
110+ // Drain ehr_data so the hardware starts a fresh collection.
111+ // (Reading the last word clears the valid flag.)
112+ for (int i = 0 ; i < 6 ; i ++ ) {
113+ (void )trng_hw -> ehr_data [i ];
114+ }
115+ trng_hw -> rng_icr = TRNG_RNG_ISR_CRNGT_ERR_BITS ;
116+ spin_unlock (lock , save );
117+ return false;
118+ }
119+
120+ for (int i = 0 ; i < 6 ; i ++ ) {
121+ out [i ] = trng_hw -> ehr_data [i ];
122+ }
123+
124+ // Switch the inverter chain length for the next collection, using
125+ // bits from the sample we just read. Only bits [1:0] matter -- they
126+ // select one of four chain lengths, changing the ROSC frequency.
127+ // This is borrowed from pico_rand's injection-locking countermeasure.
128+ // (The SDK uses its PRNG state here instead of raw output; either
129+ // works since the real defense is SHA-256 conditioning, not this.)
130+ trng_hw -> trng_config = out [0 ];
131+
132+ spin_unlock (lock , save );
133+ return true;
134+ }
135+
136+ #endif // HAS_RP2350_TRNG
137+
38138static void seed_random_bits (BYTE out [SHA256_BLOCK_SIZE ]) {
39139 CRYAL_SHA256_CTX context ;
40140 sha256_init (& context );
41- for (int i = 0 ; i < 2 * RANDOM_SAFETY_MARGIN ; i ++ ) {
42- for (int j = 0 ; j < SHA256_BLOCK_SIZE ; j ++ ) {
43- out [j ] = rosc_hw -> randombit & 1 ;
44- for (int k = 0 ; k < 8 ; k ++ ) {
45- out [j ] = (out [j ] << 1 ) ^ (rosc_hw -> randombit & 1 );
141+
142+ #ifdef HAS_RP2350_TRNG
143+ // 384 bits from TRNG + 384 bits from ROSC = 768 bits into the hash,
144+ // giving a 3:1 ratio over the 256-bit output (800-90B wants >= 2:1).
145+ // Two independent sources so a failure in one doesn't zero the input.
146+
147+ // TRNG: 2 x 192 bits.
148+ for (int i = 0 ; i < 2 ; i ++ ) {
149+ uint32_t trng_buf [6 ] = {0 };
150+ for (int attempt = 0 ; attempt < 3 ; attempt ++ ) {
151+ if (trng_collect_192 (trng_buf )) {
152+ break ;
46153 }
154+ // CRNGT failure. If all 3 retries fail, trng_buf stays zeroed
155+ // and we rely entirely on the ROSC contribution below.
47156 }
157+ sha256_update (& context , (const BYTE * )trng_buf , sizeof (trng_buf ));
158+ }
159+
160+ // ROSC: 2 x 24 bytes = 384 bits.
161+ for (int i = 0 ; i < 2 ; i ++ ) {
162+ BYTE rosc_buf [24 ];
163+ rosc_random_bytes (rosc_buf , sizeof (rosc_buf ));
164+ sha256_update (& context , rosc_buf , sizeof (rosc_buf ));
165+ }
166+ #else
167+ // RP2040: ROSC is the only entropy source.
168+ for (int i = 0 ; i < 2 * ROSC_SAFETY_MARGIN ; i ++ ) {
169+ rosc_random_bytes (out , SHA256_BLOCK_SIZE );
48170 sha256_update (& context , out , SHA256_BLOCK_SIZE );
49171 }
172+ #endif
173+
50174 sha256_final (& context , out );
51175}
52176
@@ -61,10 +185,11 @@ static void get_random_bits(BYTE out[SHA256_BLOCK_SIZE]) {
61185}
62186
63187bool common_hal_os_urandom (uint8_t * buffer , mp_uint_t length ) {
64- #define ROSC_POWER_SAVE (1) // assume ROSC is not necessarily active all the time
188+ #define ROSC_POWER_SAVE (1) // assume ROSC is not necessarily active all the time
65189 #if ROSC_POWER_SAVE
66190 uint32_t old_rosc_ctrl = rosc_hw -> ctrl ;
67- rosc_hw -> ctrl = (old_rosc_ctrl & ~ROSC_CTRL_ENABLE_BITS ) | (ROSC_CTRL_ENABLE_VALUE_ENABLE << 12 );
191+ rosc_hw -> ctrl = (old_rosc_ctrl & ~ROSC_CTRL_ENABLE_BITS )
192+ | (ROSC_CTRL_ENABLE_VALUE_ENABLE << ROSC_CTRL_ENABLE_LSB );
68193 #endif
69194 while (length ) {
70195 size_t n = MIN (length , SHA256_BLOCK_SIZE );
0 commit comments