This is a random number generator for a RP2040. It requires one hardware component, a capacitor between a GPIO pin and ground. It should be treated as a proof of concept, it requires further analysis before use in applications with consequences.
This is based on Peter Allan’s twocent, with half the component and pin count.
An end program would use getrandom with custom
feature,
see rand example.
caprand::setup(&mut p.PIN_10).expect("RNG setup failed");
getrandom::register_custom_getrandom!(caprand::random);
// ...
let mut key = [0u8; 32];
getrandom::getrandom(&key).unwrap();
There is a capacitor between a GPIO pin and ground. The capacitor is discharged briefly via the GPIO pin pulling low (1 cycle) then the RP2040 internal pullup on that pin brings the capacitor high again. It spins in a tight loop waiting for the pin to read as “high” input.
The time taken is measured to an exact system clock cycle (125MHz default), in bursts of 5 bits (limited by number of registers). That low order part of the pullup time is output as a noise sample. Samples are hashed together to form a seed, which seeds a ChaCha20 cryptographic DRBG.
The noise source has not been thoroughly quantified. Empirical testing seems to show 1-2 bits per sample. As a workaround it takes 100 noise samples per bit of output, hashing 25600 input noise samples to seed the DRBG.
(typical 25600 samples taken from usbnoise example)
Basic health tests (Adaptive Proportion and Repetition) are applied.
The hardware scheme has no protection against local interference (similar to the RP2040 itself).
Testing was performed with a 10nF Y5V SMD chip capacitor soldered between GP10 pad and the adjacent GND pad, on a Pico W board. Other capacitor values should also work OK - 100nF was tested, 1nF is likely to work.
Examples must be built with --features defmt
.
rand uses getrandom as a normal program would.
usbnoise outputs raw samples as hex values, as a USB serial device
sernoise outputs raw samples as hex values, via the rp2040 uart