use core::cell::RefCell;
use core::future::{Future, ready};
use embassy_sync::blocking_mutex::Mutex;
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
use esp_hal::rng::Rng;
#[cfg(not(any(feature = "esp32c5", feature = "esp32c61")))]
use esp_hal::{
peripherals::{ADC1, RNG},
rng::{Trng, TrngSource},
};
use ssh_stamp_hal::{HalError, RngHal};
use static_cell::StaticCell;
static RNG: StaticCell<Rng> = StaticCell::new();
static RNG_MUTEX: Mutex<CriticalSectionRawMutex, RefCell<Option<&'static mut Rng>>> =
Mutex::new(RefCell::new(None));
pub fn register_custom_rng(rng: Rng) {
let rng_ref = RNG.init(rng);
RNG_MUTEX.lock(|t| t.borrow_mut().replace(rng_ref));
}
#[must_use = "dropping switches the boot-time entropy source off"]
pub struct EntropySource {
#[cfg(not(any(feature = "esp32c5", feature = "esp32c61")))]
_source: TrngSource<'static>,
}
cfg_if::cfg_if! {
if #[cfg(any(feature = "esp32c5", feature = "esp32c61"))] {
pub fn init_entropy() -> (Rng, EntropySource) {
log::warn!(
"No TRNG on this chip, RNG is not cryptographically secure until the radio is up"
);
let rng = Rng::new();
register_custom_rng(rng);
(rng, EntropySource {})
}
#[must_use]
pub fn entropy_source_active() -> bool {
true
}
} else {
pub fn init_entropy(
rng: RNG<'static>,
adc: ADC1<'static>,
) -> (Rng, EntropySource) {
let source = TrngSource::new(rng, adc);
let trng = Trng::try_new().expect("TrngSource was just created");
let handle = trng.downgrade();
register_custom_rng(handle);
(handle, EntropySource { _source: source })
}
#[must_use]
pub fn entropy_source_active() -> bool {
TrngSource::is_enabled()
}
}
}
#[macro_export]
macro_rules! init_entropy {
($peripherals:expr) => {{
#[cfg(any(feature = "esp32c5", feature = "esp32c61"))]
let out = $crate::init_entropy();
#[cfg(not(any(feature = "esp32c5", feature = "esp32c61")))]
let out = $crate::init_entropy($peripherals.RNG, $peripherals.ADC1);
out
}};
}
pub struct EspRng;
impl EspRng {
#[must_use]
pub fn new() -> Self {
Self
}
}
impl Default for EspRng {
fn default() -> Self {
Self::new()
}
}
impl RngHal for EspRng {
fn fill_bytes(&mut self, buf: &mut [u8]) -> impl Future<Output = Result<(), HalError>> {
ready(RNG_MUTEX.lock(|t| {
let mut rng = t.borrow_mut();
let rng = rng.as_mut().ok_or(HalError::Rng)?;
rng.read(buf);
Ok(())
}))
}
}
pub fn fill_bytes(buf: &mut [u8]) -> Result<(), getrandom::Error> {
RNG_MUTEX.lock(|t| {
let mut rng_ref = t.borrow_mut();
let rng = rng_ref.as_mut().ok_or(getrandom::Error::UNEXPECTED)?;
rng.read(buf);
Ok(())
})
}
#[macro_export]
macro_rules! getrandom_backend {
() => {
#[unsafe(no_mangle)]
unsafe extern "Rust" fn __getrandom_v03_custom(
dest: *mut u8,
len: usize,
) -> Result<(), $crate::getrandom::Error> {
let buf = unsafe {
::core::ptr::write_bytes(dest, 0, len);
::core::slice::from_raw_parts_mut(dest, len)
};
$crate::rng_fill_bytes(buf)
}
};
}