use std::convert::Infallible;
use digest::{ExtendableOutput, Update};
use rand::rngs::SysRng;
use rand_core::TryRng;
use sha3::Shake256;
use zeroize::Zeroizing;
pub trait EntropicRng: rand_core::CryptoRng {}
impl EntropicRng for CautiousRng {}
#[cfg(feature = "testing")]
mod testing {
use std::convert::Infallible;
#[allow(clippy::exhaustive_structs)]
pub struct FakeEntropicRng<R>(pub R);
impl<R: rand_core::TryRng<Error = Infallible>> rand_core::TryRng for FakeEntropicRng<R> {
type Error = Infallible;
fn try_next_u32(&mut self) -> Result<u32, Infallible> {
self.0.try_next_u32()
}
fn try_next_u64(&mut self) -> Result<u64, Infallible> {
self.0.try_next_u64()
}
fn try_fill_bytes(&mut self, dst: &mut [u8]) -> Result<(), Infallible> {
self.0.try_fill_bytes(dst)
}
}
impl<R: rand_core::TryCryptoRng<Error = Infallible>> rand_core::TryCryptoRng
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 TryRng for CautiousRng {
type Error = Infallible;
fn try_next_u32(&mut self) -> Result<u32, Infallible> {
let mut buf = Zeroizing::new([0_u8; 4]);
self.try_fill_bytes(buf.as_mut())?;
Ok(u32::from_le_bytes(*buf))
}
fn try_next_u64(&mut self) -> Result<u64, Infallible> {
let mut buf = Zeroizing::new([0_u8; 8]);
self.try_fill_bytes(buf.as_mut())?;
Ok(u64::from_le_bytes(*buf))
}
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Infallible> {
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() {
let _ignore_failure = rng.try_fill_bytes(buf.as_mut());
xof.update(buf.as_ref());
}
}
rand::rng().try_fill_bytes(buf.as_mut())?;
xof.update(buf.as_ref());
SysRng
.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);
Ok(())
}
}
impl rand_core::TryCryptoRng for CautiousRng {}
#[cfg(not(target_arch = "wasm32"))]
mod backup {
use rand::TryRng;
use rand_chacha::ChaCha20Rng;
use reseeding_rng::ReseedingRng;
use std::convert::Infallible;
use std::sync::LazyLock;
use std::sync::{Mutex, MutexGuard};
type BackupRng = ReseedingRng<ChaCha20Rng, Box<dyn TryRng<Error = Infallible> + 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 TryRng<Error = Infallible> + Send> = Box::new(jitter);
let reseeding = ReseedingRng::try_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"))
}
}