#![allow(clippy::module_name_repetitions)]
#[cfg(all(unix, feature = "std"))]
use std::fs::File;
#[cfg(all(unix, feature = "std"))]
use std::io::Read;
#[cfg(feature = "std")]
use std::collections::hash_map::RandomState;
#[cfg(feature = "std")]
use std::hash::{BuildHasher, Hasher};
pub trait EntropySource {
fn fill(&mut self, destination: &mut [u8]);
fn seed<T: FromRaw>(&mut self) -> T
where
Self: Sized,
{
T::from_raw(self)
}
}
pub trait FromRaw {
fn from_raw<T: EntropySource>(entropy_source: &mut T) -> Self;
}
impl FromRaw for u64 {
fn from_raw<T: EntropySource>(entropy_source: &mut T) -> Self {
let mut raw = [0; 8];
entropy_source.fill(&mut raw);
u64::from_be_bytes(raw)
}
}
impl FromRaw for u128 {
fn from_raw<T: EntropySource>(entropy_source: &mut T) -> Self {
let mut raw = [0; 16];
entropy_source.fill(&mut raw);
u128::from_be_bytes(raw)
}
}
#[cfg(all(unix, feature = "std"))]
pub type DefaultEntropy = DevUrandom;
#[cfg(all(not(unix), feature = "allow-getrandom"))]
pub type DefaultEntropy = GetRandom;
#[cfg(all(not(unix), not(feature = "allow-getrandom"), feature = "std"))]
pub type DefaultEntropy = HashMapEntropy;
#[cfg(all(unix, feature = "std"))]
pub struct DevUrandom {
dev_random: File,
}
#[cfg(all(unix, feature = "std"))]
impl DevUrandom {
#[must_use]
pub fn new() -> Self {
Self {
dev_random: File::open("/dev/urandom").expect("Failed to open /dev/urandom"),
}
}
}
#[cfg(all(unix, feature = "std"))]
impl Default for DevUrandom {
fn default() -> Self {
Self::new()
}
}
#[cfg(all(unix, feature = "std"))]
impl EntropySource for DevUrandom {
fn fill(&mut self, destination: &mut [u8]) {
self.dev_random
.read_exact(destination)
.expect("Failed to read from /dev/urandom");
}
}
#[cfg(all(not(unix), feature = "allow-getrandom"))]
pub struct GetRandom;
#[cfg(all(not(unix), feature = "allow-getrandom"))]
impl GetRandom {
#[must_use]
pub fn new() -> Self {
Self {}
}
}
#[cfg(all(not(unix), feature = "allow-getrandom"))]
impl Default for GetRandom {
fn default() -> Self {
Self::new()
}
}
#[cfg(all(not(unix), feature = "allow-getrandom"))]
impl EntropySource for GetRandom {
fn fill(&mut self, destination: &mut [u8]) {
getrandom::fill(destination).expect("getrandom::fill failed");
}
}
#[cfg(feature = "std")]
#[derive(Default)]
pub struct HashMapEntropy;
#[cfg(feature = "std")]
impl HashMapEntropy {
#[must_use]
pub fn new() -> Self {
Self {}
}
}
#[cfg(feature = "std")]
impl EntropySource for HashMapEntropy {
fn fill(&mut self, destination: &mut [u8]) {
let mut chunks = destination.chunks_exact_mut(core::mem::size_of::<u64>());
for chunk in chunks.by_ref() {
let value = RandomState::new().build_hasher().finish();
chunk.copy_from_slice(&value.to_be_bytes());
}
let remainder = chunks.into_remainder();
if !remainder.is_empty() {
let value = RandomState::new().build_hasher().finish();
remainder.copy_from_slice(&value.to_be_bytes()[..remainder.len()]);
}
}
}
pub struct SplitMix {
state: u64,
}
impl SplitMix {
#[must_use]
pub fn new(seed: u64) -> Self {
Self { state: seed }
}
fn next(&mut self) -> u64 {
self.state = self.state.wrapping_add(0x9e37_79b9_7f4a_7c15);
let mut z = self.state;
z = (z ^ (z >> 30)).wrapping_mul(0xbf58_476d_1ce4_e5b9);
z = (z ^ (z >> 27)).wrapping_mul(0x94d0_49bb_1331_11eb);
z ^ (z >> 31)
}
}
impl EntropySource for SplitMix {
fn fill(&mut self, destination: &mut [u8]) {
let mut out_inx: usize = 0;
while out_inx < destination.len() {
let num = usize::min(8, destination.len() - out_inx);
destination[out_inx..(out_inx + num)]
.copy_from_slice(&self.next().to_be_bytes()[0..num]);
out_inx += num;
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::EntropySource;
#[cfg(all(unix, feature = "std"))]
#[test]
fn generate_64_bit_seed_with_dev_random() {
let seed1: u64 = DevUrandom::new().seed();
let seed2: u64 = DevUrandom::new().seed();
assert_ne!(seed1, seed2);
}
#[cfg(all(unix, feature = "std"))]
#[test]
fn generate_128_bit_seed_dev_random() {
let seed1: u128 = DevUrandom::new().seed();
let seed2: u128 = DevUrandom::new().seed();
assert_ne!(seed1, seed2);
}
#[cfg(all(not(unix), feature = "allow-getrandom"))]
#[test]
fn generate_64_bit_seed_with_get_random() {
let seed1: u64 = GetRandom::new().seed();
let seed2: u64 = GetRandom::new().seed();
assert_ne!(seed1, seed2);
}
#[test]
fn test_splitmix_1() {
let mut dev = SplitMix::new(42);
let mut output = [0; 12];
dev.fill(&mut output);
assert_eq!(
output,
[189, 215, 50, 38, 47, 235, 110, 149, 40, 239, 227, 51]
);
}
#[test]
fn test_splitmix_2() {
let mut dev = SplitMix::new(1234567);
assert_eq!(dev.seed::<u64>(), 6457827717110365317);
assert_eq!(dev.seed::<u64>(), 3203168211198807973);
assert_eq!(dev.seed::<u64>(), 9817491932198370423);
assert_eq!(dev.seed::<u64>(), 4593380528125082431);
assert_eq!(dev.seed::<u64>(), 16408922859458223821);
}
#[test]
fn test_ints_from_splitmix() {
let mut dev = SplitMix::new(1234567);
assert_eq!(u64::from_raw(&mut dev), 6457827717110365317);
assert_eq!(
u128::from_raw(&mut dev),
59088024217026436390996329455714204791
);
}
#[cfg(feature = "std")]
#[test]
fn test_hash_map_entropy_smoke_test() {
let mut dev = HashMapEntropy::new();
assert_ne!(dev.seed::<u64>(), dev.seed::<u64>());
}
#[cfg(feature = "std")]
#[test]
fn test_hash_map_entropy_fills_remainder() {
let mut dev = HashMapEntropy::new();
let mut output = [0_u8; 13]; dev.fill(&mut output);
assert_ne!([0_u8; 5], output[8..]);
}
}