#![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk.png",
html_favicon_url = "https://www.rust-lang.org/favicon.ico",
html_root_url = "https://rust-random.github.io/rand/")]
#![deny(missing_docs)]
#![deny(missing_debug_implementations)]
#![doc(test(attr(allow(unused_variables), deny(warnings))))]
#![no_std]
pub extern crate rand_core;
#[cfg(feature = "std")]
extern crate std;
#[cfg(feature = "log")]
#[macro_use] extern crate log;
#[cfg(any(target_os = "macos", target_os = "ios"))]
extern crate libc;
#[cfg(target_os = "windows")]
extern crate winapi;
#[cfg(not(feature = "log"))]
#[macro_use] mod dummy_log;
#[cfg(feature = "std")]
mod platform;
mod error;
use rand_core::{RngCore, CryptoRng, Error, impls};
pub use error::TimerError;
use core::{fmt, mem, ptr};
#[cfg(feature = "std")]
use std::sync::atomic::{AtomicUsize, Ordering};
#[cfg(feature = "std")]
#[allow(deprecated)] use std::sync::atomic::ATOMIC_USIZE_INIT;
const MEMORY_BLOCKS: usize = 64;
const MEMORY_BLOCKSIZE: usize = 32;
const MEMORY_SIZE: usize = MEMORY_BLOCKS * MEMORY_BLOCKSIZE;
pub struct JitterRng {
data: u64, rounds: u8,
timer: fn() -> u64,
mem_prev_index: u16,
data_half_used: bool,
}
struct EcState {
prev_time: u64,
last_delta: i32,
last_delta2: i32,
mem: [u8; MEMORY_SIZE],
}
impl EcState {
fn stuck(&mut self, current_delta: i32) -> bool {
let delta2 = self.last_delta - current_delta;
let delta3 = delta2 - self.last_delta2;
self.last_delta = current_delta;
self.last_delta2 = delta2;
current_delta == 0 || delta2 == 0 || delta3 == 0
}
}
impl fmt::Debug for JitterRng {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "JitterRng {{}}")
}
}
impl Clone for JitterRng {
fn clone(&self) -> JitterRng {
JitterRng {
data: self.data,
rounds: self.rounds,
timer: self.timer,
mem_prev_index: self.mem_prev_index,
data_half_used: false,
}
}
}
#[cfg(all(feature = "std", not(target_arch = "wasm32")))]
#[allow(deprecated)]
static JITTER_ROUNDS: AtomicUsize = ATOMIC_USIZE_INIT;
impl JitterRng {
#[cfg(all(feature = "std", not(target_arch = "wasm32")))]
pub fn new() -> Result<JitterRng, TimerError> {
if cfg!(target_arch = "wasm32") {
return Err(TimerError::NoTimer);
}
let mut state = JitterRng::new_with_timer(platform::get_nstime);
let mut rounds = JITTER_ROUNDS.load(Ordering::Relaxed) as u8;
if rounds == 0 {
rounds = state.test_timer()?;
JITTER_ROUNDS.store(rounds as usize, Ordering::Relaxed);
info!("JitterRng: using {} rounds per u64 output", rounds);
}
state.set_rounds(rounds);
state.gen_entropy();
Ok(state)
}
pub fn new_with_timer(timer: fn() -> u64) -> JitterRng {
JitterRng {
data: 0,
rounds: 64,
timer,
mem_prev_index: 0,
data_half_used: false,
}
}
pub fn set_rounds(&mut self, rounds: u8) {
assert!(rounds > 0);
self.rounds = rounds;
}
#[inline(never)]
fn random_loop_cnt(&mut self, n_bits: u32) -> u32 {
let mut rounds = 0;
let mut time = (self.timer)();
time ^= self.data;
let folds = (64 + n_bits - 1) / n_bits;
let mask = (1 << n_bits) - 1;
for _ in 0..folds {
rounds ^= time & mask;
time >>= n_bits;
}
rounds as u32
}
#[inline(never)]
fn lfsr_time(&mut self, time: u64, var_rounds: bool) {
fn lfsr(mut data: u64, time: u64) -> u64{
for i in 1..65 {
let mut tmp = time << (64 - i);
tmp >>= 64 - 1;
data ^= tmp;
data ^= (data >> 63) & 1;
data ^= (data >> 60) & 1;
data ^= (data >> 55) & 1;
data ^= (data >> 30) & 1;
data ^= (data >> 27) & 1;
data ^= (data >> 22) & 1;
data = data.rotate_left(1);
}
data
}
let mut lfsr_loop_cnt = 0;
if var_rounds { lfsr_loop_cnt = self.random_loop_cnt(4) };
let mut throw_away: u64 = 0;
for _ in 0..lfsr_loop_cnt {
throw_away = lfsr(throw_away, time);
}
black_box(throw_away);
self.data = lfsr(self.data, time);
}
#[inline(never)]
fn memaccess(&mut self, mem: &mut [u8; MEMORY_SIZE], var_rounds: bool) {
let mut acc_loop_cnt = 128;
if var_rounds { acc_loop_cnt += self.random_loop_cnt(4) };
let mut index = self.mem_prev_index as usize;
for _ in 0..acc_loop_cnt {
index = (index + MEMORY_BLOCKSIZE - 1) % MEMORY_SIZE;
mem[index] = mem[index].wrapping_add(1);
}
self.mem_prev_index = index as u16;
}
fn measure_jitter(&mut self, ec: &mut EcState) -> Option<()> {
self.memaccess(&mut ec.mem, true);
let time = (self.timer)();
let current_delta = time.wrapping_sub(ec.prev_time) as i64 as i32;
ec.prev_time = time;
self.lfsr_time(current_delta as u64, true);
if ec.stuck(current_delta) { return None };
self.data = self.data.rotate_left(7);
Some(())
}
#[inline(never)]
fn stir_pool(&mut self) {
const CONSTANT: u64 = 0x67452301efcdab89;
let mut mixer = 0x98badcfe10325476;
for i in 0..64 {
let apply = (self.data >> i) & 1;
let mask = !apply.wrapping_sub(1);
mixer ^= CONSTANT & mask;
mixer = mixer.rotate_left(1);
}
self.data ^= mixer;
}
fn gen_entropy(&mut self) -> u64 {
trace!("JitterRng: collecting entropy");
let mut ec = EcState {
prev_time: (self.timer)(),
last_delta: 0,
last_delta2: 0,
mem: [0; MEMORY_SIZE],
};
let _ = self.measure_jitter(&mut ec);
for _ in 0..self.rounds {
while self.measure_jitter(&mut ec).is_none() {}
}
black_box(ec.mem[0]);
self.stir_pool();
self.data
}
pub fn test_timer(&mut self) -> Result<u8, TimerError> {
debug!("JitterRng: testing timer ...");
let mut delta_sum = 0;
let mut old_delta = 0;
let mut time_backwards = 0;
let mut count_mod = 0;
let mut count_stuck = 0;
let mut ec = EcState {
prev_time: (self.timer)(),
last_delta: 0,
last_delta2: 0,
mem: [0; MEMORY_SIZE],
};
const TESTLOOPCOUNT: u64 = 300;
const CLEARCACHE: u64 = 100;
for i in 0..(CLEARCACHE + TESTLOOPCOUNT) {
let time = (self.timer)();
self.memaccess(&mut ec.mem, true);
self.lfsr_time(time, true);
let time2 = (self.timer)();
if time == 0 || time2 == 0 {
return Err(TimerError::NoTimer);
}
let delta = time2.wrapping_sub(time) as i64 as i32;
if delta == 0 {
return Err(TimerError::CoarseTimer);
}
if i < CLEARCACHE { continue; }
if ec.stuck(delta) { count_stuck += 1; }
if !(time2 > time) { time_backwards += 1; }
if (delta % 100) == 0 { count_mod += 1; }
delta_sum += (delta - old_delta).abs() as u64;
old_delta = delta;
}
black_box(ec.mem[0]);
if time_backwards > 3 {
return Err(TimerError::NotMonotonic);
}
if delta_sum < TESTLOOPCOUNT {
return Err(TimerError::TinyVariantions);
}
if count_mod > (TESTLOOPCOUNT * 9 / 10) {
return Err(TimerError::CoarseTimer);
}
if count_stuck > (TESTLOOPCOUNT * 9 / 10) {
return Err(TimerError::TooManyStuck);
}
let delta_average = delta_sum / TESTLOOPCOUNT;
if delta_average >= 16 {
let log2 = 64 - delta_average.leading_zeros();
Ok( ((64u32 * 2 + log2 - 1) / log2) as u8)
} else {
let log2_lookup = [0, 0, 128, 81, 64, 56, 50, 46,
43, 41, 39, 38, 36, 35, 34, 33];
Ok(log2_lookup[delta_average as usize])
}
}
pub fn timer_stats(&mut self, var_rounds: bool) -> i64 {
let mut mem = [0; MEMORY_SIZE];
let time = (self.timer)();
self.memaccess(&mut mem, var_rounds);
self.lfsr_time(time, var_rounds);
let time2 = (self.timer)();
time2.wrapping_sub(time) as i64
}
}
fn black_box<T>(dummy: T) -> T {
unsafe {
let ret = ptr::read_volatile(&dummy);
mem::forget(dummy);
ret
}
}
impl RngCore for JitterRng {
fn next_u32(&mut self) -> u32 {
if self.data_half_used {
self.data_half_used = false;
(self.data >> 32) as u32
} else {
self.data = self.next_u64();
self.data_half_used = true;
self.data as u32
}
}
fn next_u64(&mut self) -> u64 {
self.data_half_used = false;
self.gen_entropy()
}
fn fill_bytes(&mut self, dest: &mut [u8]) {
impls::fill_bytes_via_next(self, dest)
}
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
Ok(self.fill_bytes(dest))
}
}
impl CryptoRng for JitterRng {}