#[cfg(not(feature = "defmt"))]
#[allow(unused_imports)]
use log::{debug, error, info, trace, warn};
#[cfg(feature = "defmt")]
#[allow(unused_imports)]
use defmt::{debug, error, info, panic, trace, warn};
use core::ops::DerefMut;
use core::{cell::RefCell, convert::Infallible};
use critical_section::Mutex;
use rand_chacha::ChaCha20Rng;
use sha2::{Digest, Sha256};
use embassy_rp::{gpio::Pin, Peri};
use rand::Rng;
use rand_chacha::rand_core::SeedableRng;
static RNG: Mutex<RefCell<Option<CapRng>>> = Mutex::new(RefCell::new(None));
pub fn getrandom(buf: &mut [u8]) -> Result<(), ()> {
critical_section::with(|cs| {
let mut rng = RNG.borrow_ref_mut(cs);
let rng = rng.deref_mut();
if let Some(rng) = rng {
rng.0.fill_bytes(buf);
Ok(())
} else {
error!("setup() not called");
Err(())
}
})
}
pub unsafe fn getrandom_raw(dest: *mut u8, len: usize) -> Result<(), ()> {
let buf = unsafe {
core::ptr::write_bytes(dest, 0, len);
core::slice::from_raw_parts_mut(dest, len)
};
getrandom(buf)
}
pub fn setup(pin: Peri<impl Pin>) -> Result<(), ()> {
let r = CapRng::new(pin)?;
critical_section::with(|cs| {
let mut rng = RNG.borrow_ref_mut(cs);
let _ = rng.insert(r);
});
Ok(())
}
pub struct CapRng(ChaCha20Rng);
impl CapRng {
pub const SEED_SAMPLES: usize = 256 * 100;
const MAX_FAILURES: usize = 3;
pub fn new(pin: Peri<impl Pin>) -> Result<Self, ()> {
let low_cycles = 1;
let mut noise = crate::cap::RawNoise::new(pin, low_cycles);
let mut valid_samples = 0;
let mut h = Sha256::new();
let mut health = crate::health::TotalHealth::new();
let mut failures = 0;
while valid_samples < Self::SEED_SAMPLES {
let (v, valid) = noise
.next()
.unwrap();
if valid {
valid_samples += 1;
if health.test(v).is_err() {
valid_samples = 0;
failures += 1;
if failures > Self::MAX_FAILURES {
error!(
"Health tests failed after {} retries",
Self::MAX_FAILURES
);
return Err(());
}
}
}
h.update([v]);
}
let seed: [u8; 32] = h.finalize().into();
Ok(Self(ChaCha20Rng::from_seed(seed)))
}
}
impl rand::TryCryptoRng for CapRng {}
impl rand::TryRng for CapRng {
type Error = Infallible;
fn try_next_u32(&mut self) -> Result<u32, Self::Error> {
self.0.try_next_u32()
}
fn try_next_u64(&mut self) -> Result<u64, Self::Error> {
self.0.try_next_u64()
}
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Self::Error> {
self.0.try_fill_bytes(dest)
}
}