#![forbid(unsafe_code)]
#![deny(missing_docs)]
#![deny(unused_must_use)]
#![deny(unused_mut)]
use std::sync::{Mutex, Once};
use std::time::{SystemTime, UNIX_EPOCH};
use anyhow::{bail, Error};
use getrandom::getrandom;
use sha2::{Digest, Sha256};
const BACKUP_FREQUENCY: u128 = 512;
const CLOCK_ERR: &str = "system clock could not be read";
struct EntropyState {
entropy_pool: [u8; 32],
backup_pool: [u8; 32],
usage_counter: u128,
}
static ENTROPY_STATE: Mutex<EntropyState> = Mutex::new(EntropyState {
entropy_pool: [0; 32],
backup_pool: [0; 32],
usage_counter: 0,
});
static INIT: Once = Once::new();
fn hash(b: &[u8]) -> [u8; 32] {
let mut hasher = Sha256::new();
hasher.update(b);
let r = hasher.finalize();
let mut dest = [0u8; 32];
dest.copy_from_slice(&r);
dest
}
fn init() {
INIT.call_once(|| {
let mut base = [0u8; 32];
let mut backup = [0u8; 32];
match getrandom(&mut base) {
Ok(_) => {}
Err(error) => {
debug_assert!(false, "unable to get base randomness from OS: {}", error);
}
}
match getrandom(&mut backup) {
Ok(_) => {}
Err(error) => {
debug_assert!(false, "unable to get backup randomness from OS: {}", error);
}
}
let start = SystemTime::now();
let mut iters = 0;
while start.elapsed().expect(CLOCK_ERR).as_millis() < 25 || iters < 512 {
iters += 1;
let time = SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect(CLOCK_ERR)
.as_nanos()
.to_le_bytes();
for i in 0..16 {
base[i] ^= time[i];
}
base = hash(&base);
}
let mut es = ENTROPY_STATE.lock().unwrap();
es.entropy_pool.copy_from_slice(&base);
es.backup_pool.copy_from_slice(&backup);
drop(es);
});
}
pub fn random256() -> [u8; 32] {
init();
let mut es_lock = ENTROPY_STATE.lock().unwrap();
let mut entropy_pool = es_lock.entropy_pool;
let mut backup_pool = es_lock.backup_pool;
let usage_counter = es_lock.usage_counter;
es_lock.usage_counter += 1;
drop(es_lock);
let usage_bytes: [u8; 16] = usage_counter.to_le_bytes();
let start: [u8; 16] = SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect(CLOCK_ERR)
.as_nanos()
.to_le_bytes();
for i in 0..15 {
entropy_pool[i] ^= start[i];
backup_pool[i] ^= start[i];
}
for i in 16..31 {
entropy_pool[i] ^= usage_bytes[i - 16];
backup_pool[i] ^= usage_bytes[i - 16];
}
let output = hash(&entropy_pool);
let output_timing_entropy = SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect(CLOCK_ERR)
.as_nanos()
.to_le_bytes();
for i in 0..15 {
backup_pool[i] ^= output_timing_entropy[i];
}
let new_backup = hash(&backup_pool);
let backup_timing_entropy = SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect(CLOCK_ERR)
.as_nanos()
.to_le_bytes();
for i in 0..15 {
entropy_pool[i] ^= backup_timing_entropy[i];
}
let new_entropy = hash(&entropy_pool);
let mut es_lock = ENTROPY_STATE.lock().unwrap();
for i in 0..32 {
es_lock.entropy_pool[i] ^= new_entropy[i];
es_lock.backup_pool[i] ^= new_backup[i];
if usage_counter % BACKUP_FREQUENCY == 0 {
es_lock.entropy_pool[i] ^= es_lock.backup_pool[i];
}
}
drop(es_lock);
output
}
pub fn range64(start: u64, end: u64) -> Result<u64, Error> {
if start >= end {
bail!("start must be strictly smaller than end");
}
let range = (end - start) as u128;
let result: u64;
let umax = u128::MAX;
let limit = umax - (umax % range);
loop {
let mut base = [0u8; 16];
let rng = random256();
base.copy_from_slice(&rng[..16]);
let rand = u128::from_le_bytes(base);
if rand > limit {
continue;
}
result = (rand % range) as u64;
break;
}
Ok(result + start)
}
pub struct Csprng {
}
impl rand_core::CryptoRng for Csprng {}
impl rand_core::RngCore for Csprng {
fn next_u32(&mut self) -> u32 {
u32::from_le_bytes(random256()[..4].try_into().unwrap())
}
fn next_u64(&mut self) -> u64 {
u64::from_le_bytes(random256()[..8].try_into().unwrap())
}
fn fill_bytes(&mut self, dest: &mut [u8]) {
let dlen = dest.len();
let mut i = 0;
while i + 32 <= dlen {
let rand = random256();
dest[i..i + 32].copy_from_slice(&rand);
i += 32;
}
if dlen % 32 == 0 {
return;
}
let rand = random256();
let need = dlen - i;
dest[i..dlen].copy_from_slice(&rand[..need]);
}
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core::Error> {
self.fill_bytes(dest);
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use ed25519_dalek::Signer;
use rand_core::RngCore;
#[test]
fn check_random256() {
let tries = 100_000;
let mut frequencies = std::collections::HashMap::new();
for _ in 0..tries {
let rand = random256();
for i in 0..rand.len() - 1 {
match frequencies.get(&rand[i]) {
Some(num) => frequencies.insert(rand[i], num + 1),
None => frequencies.insert(rand[i], 1),
};
}
}
for i in 0..=255 {
let num = frequencies.get(&i).unwrap();
assert!(num > &(tries * 32 * 80 / 256 / 100));
assert!(num < &(tries * 32 * 112 / 256 / 100));
}
}
#[test]
fn check_range64() {
let tries = 10_000;
for _ in 0..tries {
let i = range64(0, 1).unwrap();
assert!(i == 0);
let i = range64(1, 2).unwrap();
assert!(i == 1);
range64(1, 1).unwrap_err();
range64(1, 0).unwrap_err();
}
let tries = 200_000;
let mut frequencies = std::collections::HashMap::new();
for _ in 0..tries {
let rand = range64(1, 256).unwrap();
match frequencies.get(&rand) {
Some(num) => frequencies.insert(rand, num + 1),
None => frequencies.insert(rand, 1),
};
}
for i in 1..256 {
let num = frequencies.get(&i).unwrap();
if *num < tries / 256 * 80 / 100 {
panic!(
"value {} appeared fewer times than expected: {} :: {}",
i,
num,
tries / 256 * 80 / 100
);
}
if *num > tries / 256 * 125 / 100 {
panic!(
"value {} appeared greater times than expected: {} :: {}",
i,
num,
tries / 256 * 125 / 100
);
}
}
}
#[test]
fn check_prng_impl() {
let mut csprng = Csprng {};
let mut counter = std::collections::HashMap::new();
let mut total_bytes = 0;
for i in 1..100 {
let mut bytes = vec![0u8; i];
csprng.fill_bytes(&mut bytes);
let base = bytes.clone();
let mut different = false;
for _ in 0..32 {
csprng.fill_bytes(&mut bytes);
if bytes != base {
different = true;
}
for i in 0..bytes.len() {
let b = bytes[i];
match counter.get(&b) {
None => counter.insert(b, 1),
Some(v) => counter.insert(b, v+1),
};
total_bytes += 1;
}
}
assert!(different);
}
assert!(counter.len() == 256);
for i in 0..=255 {
let v = *counter.get(&i).unwrap();
assert!((v as f64) > total_bytes as f64 * 0.7 / 256.0);
assert!((v as f64) < total_bytes as f64 * 1.3 / 256.0);
}
let keypair = ed25519_dalek::Keypair::generate(&mut csprng);
let msg = b"example message";
let sig = keypair.sign(msg);
keypair.public.verify_strict(msg, &sig).unwrap();
let mut csprng = Csprng {};
let keypair2 = ed25519_dalek::Keypair::generate(&mut csprng);
assert!(keypair.to_bytes() != keypair2.to_bytes());
let mut counter = std::collections::HashMap::new();
for _ in 0..10_000 {
let t = csprng.next_u32() as u64;
match counter.get(&t) {
None => counter.insert(t, 1),
Some(v) => counter.insert(t, v + 1),
};
}
for _ in 0..10_000 {
let t = csprng.next_u64();
match counter.get(&t) {
None => counter.insert(t, 1),
Some(v) => counter.insert(t, v + 1),
};
}
for _ in 0..100 {
let mut bytes = [0u8; 8008];
csprng.fill_bytes(&mut bytes);
for i in 0..1001 {
let t = u64::from_le_bytes(bytes[i * 8..(i + 1) * 8].try_into().unwrap());
match counter.get(&t) {
None => counter.insert(t, 1),
Some(v) => counter.insert(t, v + 1),
};
}
let mut bytes = [0u8; 8008];
csprng.try_fill_bytes(&mut bytes).unwrap();
for i in 0..1001 {
let t = u64::from_le_bytes(bytes[i * 8..(i + 1) * 8].try_into().unwrap());
match counter.get(&t) {
None => counter.insert(t, 1),
Some(v) => counter.insert(t, v + 1),
};
}
let mut bytes = [0u8; 32];
csprng.fill_bytes(&mut bytes);
for i in 0..1 {
let t = u64::from_le_bytes(bytes[i * 8..(i + 1) * 8].try_into().unwrap());
match counter.get(&t) {
None => counter.insert(t, 1),
Some(v) => counter.insert(t, v + 1),
};
}
let mut bytes = [0u8; 64];
csprng.try_fill_bytes(&mut bytes).unwrap();
for i in 0..2 {
let t = u64::from_le_bytes(bytes[i * 8..(i + 1) * 8].try_into().unwrap());
match counter.get(&t) {
None => counter.insert(t, 1),
Some(v) => counter.insert(t, v + 1),
};
}
let mut bytes = [0u8; 128];
csprng.fill_bytes(&mut bytes);
for i in 0..1 {
let t = u64::from_le_bytes(bytes[i * 8..(i + 1) * 8].try_into().unwrap());
match counter.get(&t) {
None => counter.insert(t, 1),
Some(v) => counter.insert(t, v + 1),
};
}
let mut bytes = [0u8; 56];
csprng.try_fill_bytes(&mut bytes).unwrap();
for i in 0..2 {
let t = u64::from_le_bytes(bytes[i * 8..(i + 1) * 8].try_into().unwrap());
match counter.get(&t) {
None => counter.insert(t, 1),
Some(v) => counter.insert(t, v + 1),
};
}
}
for (key, value) in counter {
if value > 10 {
panic!("distribution does not appear to be even: {}:{}", key, value);
}
}
}
}