#[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::cell::RefCell;
use core::num::NonZeroU32;
use core::ops::DerefMut;
use critical_section::Mutex;
use rand_chacha::ChaCha20Rng;
use sha2::{Digest, Sha256};
use embassy_rp::gpio::Pin;
use rand_chacha::rand_core::{RngCore, SeedableRng};
pub const CAPRAND_ERR: u32 = getrandom::Error::CUSTOM_START + 510132368;
pub fn error() -> getrandom::Error {
let c: NonZeroU32 = CAPRAND_ERR.try_into().unwrap();
c.into()
}
static RNG: Mutex<RefCell<Option<CapRng>>> = Mutex::new(RefCell::new(None));
pub fn getrandom(buf: &mut [u8]) -> Result<(), getrandom::Error> {
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(error())
}
})
}
pub fn setup(pin: &mut impl Pin) -> Result<(), getrandom::Error> {
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 rand::CryptoRng for CapRng {}
impl CapRng {
pub const SEED_SAMPLES: usize = 256 * 100;
const MAX_FAILURES: usize = 3;
pub fn new(pin: &mut impl Pin) -> Result<Self, getrandom::Error> {
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(error())
}
}
}
h.update([v]);
}
let seed: [u8; 32] = h.finalize().into();
Ok(Self(ChaCha20Rng::from_seed(seed)))
}
}
impl rand::RngCore for CapRng {
fn next_u32(&mut self) -> u32 {
self.0.next_u32()
}
fn next_u64(&mut self) -> u64 {
self.0.next_u64()
}
fn fill_bytes(&mut self, dest: &mut [u8]) {
self.0.fill_bytes(dest)
}
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand::Error> {
self.0.try_fill_bytes(dest)
}
}