use anyhow::Result;
use rand::rngs::adapter::ReseedingRng;
use rand::rngs::OsRng;
use rand::{thread_rng, Rng, RngCore, SeedableRng};
use rand_chacha::ChaCha20Core;
use rand_hc::Hc128Core;
use rand_jitter::JitterRng;
use rdrand::{RdRand, RdSeed};
use zeroize::Zeroize;
pub struct CompositeRng<Rng1: RngCore, Rng2: RngCore> {
rng1: Rng1,
rng2: Rng2,
}
impl<Rng1: RngCore, Rng2: RngCore> CompositeRng<Rng1, Rng2> {
pub fn new(rng1: Rng1, rng2: Rng2) -> Self {
Self { rng1, rng2 }
}
}
impl<Rng1: RngCore, Rng2: RngCore> RngCore for CompositeRng<Rng1, Rng2> {
fn next_u32(&mut self) -> u32 {
self.rng1.next_u32() ^ self.rng2.next_u32()
}
fn next_u64(&mut self) -> u64 {
self.rng1.next_u64() ^ self.rng2.next_u64()
}
fn fill_bytes(&mut self, dest: &mut [u8]) {
self.rng1.fill_bytes(dest);
let mut buffer = vec![0; dest.len()];
self.rng2.fill_bytes(&mut buffer);
for i in 0..dest.len() {
dest[i] ^= buffer[i];
}
}
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand::Error> {
self.rng1.try_fill_bytes(dest)?;
let mut buffer = vec![0; dest.len()];
self.rng2.try_fill_bytes(&mut buffer)?;
for i in 0..dest.len() {
dest[i] ^= buffer[i];
}
Ok(())
}
}
#[macro_export]
macro_rules! composite_rng {
($rng1:expr, $rng2:expr) => {
crate::random::CompositeRng::new($rng1, $rng2)
};
($rng1:expr, $rng2:expr, $($tail:expr),+) => {
crate::random::CompositeRng::new($rng1, composite_rng!($rng2, $($tail),+))
};
}
pub fn secure_rng() -> Result<impl Rng> {
const RESEED_THRESHOLD: u64 = 1024 * 32;
let rdseed = rdseed_or_zeroes();
let rdrand = rdrand_or_zeroes();
let jitter = jitter_rng();
let chacha = ReseedingRng::new(ChaCha20Core::from_rng(OsRng)?, RESEED_THRESHOLD, OsRng);
let hc = ReseedingRng::new(Hc128Core::from_rng(OsRng)?, RESEED_THRESHOLD, OsRng);
let thread = thread_rng();
Ok(composite_rng!(
OsRng, rdseed, rdrand, jitter, chacha, hc, thread
))
}
struct RngOrZeroes<R: RngCore>(Option<R>);
impl<R: RngCore> RngCore for RngOrZeroes<R> {
fn next_u32(&mut self) -> u32 {
if let Some(rng) = &mut self.0 {
rng.next_u32()
} else {
0
}
}
fn next_u64(&mut self) -> u64 {
if let Some(rng) = &mut self.0 {
rng.next_u64()
} else {
0
}
}
fn fill_bytes(&mut self, dest: &mut [u8]) {
if let Some(rng) = &mut self.0 {
rng.fill_bytes(dest)
} else {
Zeroize::zeroize(dest);
}
}
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand::Error> {
if let Some(rng) = &mut self.0 {
rng.try_fill_bytes(dest)
} else {
Zeroize::zeroize(dest);
Ok(())
}
}
}
struct RandCore5Wrapper<R: rand_core_5::RngCore>(R);
impl<R: rand_core_5::RngCore> RngCore for RandCore5Wrapper<R> {
fn next_u32(&mut self) -> u32 {
rand_core_5::RngCore::next_u32(&mut self.0)
}
fn next_u64(&mut self) -> u64 {
rand_core_5::RngCore::next_u64(&mut self.0)
}
fn fill_bytes(&mut self, dest: &mut [u8]) {
rand_core_5::RngCore::fill_bytes(&mut self.0, dest)
}
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand::Error> {
rand_core_5::RngCore::try_fill_bytes(&mut self.0, dest)
.map_err(|err| rand::Error::new(err.take_inner()))
}
}
fn rdseed_or_zeroes() -> impl RngCore {
match RdSeed::new() {
Ok(rdseed) => RngOrZeroes(Some(rdseed)),
Err(err) => {
println!("Warning: Not able to use RDSEED random generator. Generated keys might be less random. Error message: {}", err);
RngOrZeroes(None)
}
}
}
fn rdrand_or_zeroes() -> impl RngCore {
match RdRand::new() {
Ok(rdrand) => RngOrZeroes(Some(rdrand)),
Err(err) => {
println!("Warning: Not able to use RDRAND random generator. Generated keys might be less random. Error message: {}", err);
RngOrZeroes(None)
}
}
}
fn jitter_rng() -> impl RngCore {
fn get_nstime() -> u64 {
use std::time::{SystemTime, UNIX_EPOCH};
let dur = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
dur.as_secs() * 1_000_000_000 + dur.subsec_nanos() as u64
}
RandCore5Wrapper(JitterRng::new_with_timer(get_nstime))
}