use digest::{ExtendableOutput, Update};
use rand_core::TryRngCore;
use sha3::Shake256;
use zeroize::Zeroizing;
pub trait EntropicRng: rand_core::CryptoRng {}
impl EntropicRng for CautiousRng {}
#[cfg(feature = "testing")]
mod testing {
#[allow(clippy::exhaustive_structs)]
pub struct FakeEntropicRng<R>(pub R);
impl<R: rand_core::RngCore> rand_core::RngCore for FakeEntropicRng<R> {
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, dst: &mut [u8]) {
self.0.fill_bytes(dst);
}
}
impl<R: rand_core::CryptoRng> rand_core::CryptoRng for FakeEntropicRng<R> {}
impl<R: rand_core::CryptoRng> super::EntropicRng for FakeEntropicRng<R> {}
}
#[cfg(feature = "testing")]
pub use testing::FakeEntropicRng;
#[derive(Default)]
#[allow(clippy::exhaustive_structs)]
pub struct CautiousRng;
impl rand_core::RngCore for CautiousRng {
fn next_u32(&mut self) -> u32 {
let mut buf = Zeroizing::new([0_u8; 4]);
self.fill_bytes(buf.as_mut());
u32::from_le_bytes(*buf)
}
fn next_u64(&mut self) -> u64 {
let mut buf = Zeroizing::new([0_u8; 8]);
self.fill_bytes(buf.as_mut());
u64::from_le_bytes(*buf)
}
fn fill_bytes(&mut self, dest: &mut [u8]) {
let mut xof = Shake256::default();
let mut buf = Zeroizing::new([0_u8; 32]);
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
if let Ok(mut rdrand) = rdrand::RdRand::new() {
let _ignore_failure = rdrand.try_fill_bytes(buf.as_mut());
xof.update(buf.as_ref());
}
#[cfg(not(target_arch = "wasm32"))]
{
if let Some(mut rng) = backup::backup_rng() {
rng.fill_bytes(buf.as_mut());
xof.update(buf.as_ref());
}
}
rand::rng().fill_bytes(buf.as_mut());
xof.update(buf.as_ref());
rand_core::OsRng
.try_fill_bytes(buf.as_mut())
.expect("No strong entropy source was available: cannot proceed");
xof.update(buf.as_ref());
xof.finalize_xof_into(dest);
}
}
impl rand_core::CryptoRng for CautiousRng {}
#[cfg(not(target_arch = "wasm32"))]
mod backup {
use rand::{RngCore, rngs::ReseedingRng};
use rand_chacha::ChaCha20Core;
use std::sync::LazyLock;
use std::sync::{Mutex, MutexGuard};
type BackupRng = ReseedingRng<ChaCha20Core, Box<dyn RngCore + Send>>;
static JITTER_BACKUP: LazyLock<Option<Mutex<BackupRng>>> = LazyLock::new(new_backup_rng);
fn new_backup_rng() -> Option<Mutex<BackupRng>> {
let jitter = rand_jitter::JitterRng::new().ok()?;
let jitter: Box<dyn RngCore + Send> = Box::new(jitter);
let reseeding = ReseedingRng::new(1024, jitter).ok()?;
Some(Mutex::new(reseeding))
}
pub(super) fn backup_rng() -> Option<MutexGuard<'static, BackupRng>> {
JITTER_BACKUP
.as_ref()
.map(|mutex| mutex.lock().expect("lock poisoned"))
}
}