diff options
author | Valentin Popov <valentin@popov.link> | 2024-07-19 15:37:58 +0300 |
---|---|---|
committer | Valentin Popov <valentin@popov.link> | 2024-07-19 15:37:58 +0300 |
commit | a990de90fe41456a23e58bd087d2f107d321f3a1 (patch) | |
tree | 15afc392522a9e85dc3332235e311b7d39352ea9 /vendor/rand/src/jitter.rs | |
parent | 3d48cd3f81164bbfc1a755dc1d4a9a02f98c8ddd (diff) | |
download | fparkan-a990de90fe41456a23e58bd087d2f107d321f3a1.tar.xz fparkan-a990de90fe41456a23e58bd087d2f107d321f3a1.zip |
Deleted vendor folder
Diffstat (limited to 'vendor/rand/src/jitter.rs')
-rw-r--r-- | vendor/rand/src/jitter.rs | 754 |
1 files changed, 0 insertions, 754 deletions
diff --git a/vendor/rand/src/jitter.rs b/vendor/rand/src/jitter.rs deleted file mode 100644 index 3693481..0000000 --- a/vendor/rand/src/jitter.rs +++ /dev/null @@ -1,754 +0,0 @@ -// Copyright 2017 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or -// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. -// -// Based on jitterentropy-library, http://www.chronox.de/jent.html. -// Copyright Stephan Mueller <smueller@chronox.de>, 2014 - 2017. -// -// With permission from Stephan Mueller to relicense the Rust translation under -// the MIT license. - -//! Non-physical true random number generator based on timing jitter. - -use Rng; - -use core::{fmt, mem, ptr}; -#[cfg(feature="std")] -use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering}; - -const MEMORY_BLOCKS: usize = 64; -const MEMORY_BLOCKSIZE: usize = 32; -const MEMORY_SIZE: usize = MEMORY_BLOCKS * MEMORY_BLOCKSIZE; - -/// A true random number generator based on jitter in the CPU execution time, -/// and jitter in memory access time. -/// -/// This is a true random number generator, as opposed to pseudo-random -/// generators. Random numbers generated by `JitterRng` can be seen as fresh -/// entropy. A consequence is that is orders of magnitude slower than `OsRng` -/// and PRNGs (about 10^3 .. 10^6 slower). -/// -/// There are very few situations where using this RNG is appropriate. Only very -/// few applications require true entropy. A normal PRNG can be statistically -/// indistinguishable, and a cryptographic PRNG should also be as impossible to -/// predict. -/// -/// Use of `JitterRng` is recommended for initializing cryptographic PRNGs when -/// `OsRng` is not available. -/// -/// This implementation is based on -/// [Jitterentropy](http://www.chronox.de/jent.html) version 2.1.0. -// -// Note: the C implementation relies on being compiled without optimizations. -// This implementation goes through lengths to make the compiler not optimise -// out what is technically dead code, but that does influence timing jitter. -pub struct JitterRng { - data: u64, // Actual random number - // Number of rounds to run the entropy collector per 64 bits - rounds: u32, - // Timer and previous time stamp, used by `measure_jitter` - timer: fn() -> u64, - prev_time: u64, - // Deltas used for the stuck test - last_delta: i64, - last_delta2: i64, - // Memory for the Memory Access noise source - mem_prev_index: usize, - mem: [u8; MEMORY_SIZE], - // Make `next_u32` not waste 32 bits - data_remaining: Option<u32>, -} - -// Custom Debug implementation that does not expose the internal state -impl fmt::Debug for JitterRng { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "JitterRng {{}}") - } -} - -/// An error that can occur when `test_timer` fails. -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum TimerError { - /// No timer available. - NoTimer, - /// Timer too coarse to use as an entropy source. - CoarseTimer, - /// Timer is not monotonically increasing. - NotMonotonic, - /// Variations of deltas of time too small. - TinyVariantions, - /// Too many stuck results (indicating no added entropy). - TooManyStuck, - #[doc(hidden)] - __Nonexhaustive, -} - -impl TimerError { - fn description(&self) -> &'static str { - match *self { - TimerError::NoTimer => "no timer available", - TimerError::CoarseTimer => "coarse timer", - TimerError::NotMonotonic => "timer not monotonic", - TimerError::TinyVariantions => "time delta variations too small", - TimerError::TooManyStuck => "too many stuck results", - TimerError::__Nonexhaustive => unreachable!(), - } - } -} - -impl fmt::Display for TimerError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.description()) - } -} - -#[cfg(feature="std")] -impl ::std::error::Error for TimerError { - fn description(&self) -> &str { - self.description() - } -} - -// Initialise to zero; must be positive -#[cfg(feature="std")] -static JITTER_ROUNDS: AtomicUsize = ATOMIC_USIZE_INIT; - -impl JitterRng { - /// Create a new `JitterRng`. - /// Makes use of `std::time` for a timer. - /// - /// During initialization CPU execution timing jitter is measured a few - /// hundred times. If this does not pass basic quality tests, an error is - /// returned. The test result is cached to make subsequent calls faster. - #[cfg(feature="std")] - pub fn new() -> Result<JitterRng, TimerError> { - let mut ec = JitterRng::new_with_timer(platform::get_nstime); - let mut rounds = JITTER_ROUNDS.load(Ordering::Relaxed) as u32; - if rounds == 0 { - // No result yet: run test. - // This allows the timer test to run multiple times; we don't care. - rounds = ec.test_timer()?; - JITTER_ROUNDS.store(rounds as usize, Ordering::Relaxed); - } - ec.set_rounds(rounds); - Ok(ec) - } - - /// Create a new `JitterRng`. - /// A custom timer can be supplied, making it possible to use `JitterRng` in - /// `no_std` environments. - /// - /// The timer must have nanosecond precision. - /// - /// This method is more low-level than `new()`. It is the responsibility of - /// the caller to run `test_timer` before using any numbers generated with - /// `JitterRng`, and optionally call `set_rounds()`. - pub fn new_with_timer(timer: fn() -> u64) -> JitterRng { - let mut ec = JitterRng { - data: 0, - rounds: 64, - timer: timer, - prev_time: 0, - last_delta: 0, - last_delta2: 0, - mem_prev_index: 0, - mem: [0; MEMORY_SIZE], - data_remaining: None, - }; - - // Fill `data`, `prev_time`, `last_delta` and `last_delta2` with - // non-zero values. - ec.prev_time = timer(); - ec.gen_entropy(); - - // Do a single read from `self.mem` to make sure the Memory Access noise - // source is not optimised out. - // Note: this read is important, it effects optimisations for the entire - // module! - black_box(ec.mem[0]); - - ec - } - - /// Configures how many rounds are used to generate each 64-bit value. - /// This must be greater than zero, and has a big impact on performance - /// and output quality. - /// - /// `new_with_timer` conservatively uses 64 rounds, but often less rounds - /// can be used. The `test_timer()` function returns the minimum number of - /// rounds required for full strength (platform dependent), so one may use - /// `rng.set_rounds(rng.test_timer()?);` or cache the value. - pub fn set_rounds(&mut self, rounds: u32) { - assert!(rounds > 0); - self.rounds = rounds; - } - - // Calculate a random loop count used for the next round of an entropy - // collection, based on bits from a fresh value from the timer. - // - // The timer is folded to produce a number that contains at most `n_bits` - // bits. - // - // Note: A constant should be added to the resulting random loop count to - // prevent loops that run 0 times. - #[inline(never)] - fn random_loop_cnt(&mut self, n_bits: u32) -> u32 { - let mut rounds = 0; - - let mut time = (self.timer)(); - // Mix with the current state of the random number balance the random - // loop counter a bit more. - time ^= self.data; - - // We fold the time value as much as possible to ensure that as many - // bits of the time stamp are included as possible. - let folds = (64 + n_bits - 1) / n_bits; - let mask = (1 << n_bits) - 1; - for _ in 0..folds { - rounds ^= time & mask; - time = time >> n_bits; - } - - rounds as u32 - } - - // CPU jitter noise source - // Noise source based on the CPU execution time jitter - // - // This function injects the individual bits of the time value into the - // entropy pool using an LFSR. - // - // The code is deliberately inefficient with respect to the bit shifting. - // This function not only acts as folding operation, but this function's - // execution is used to measure the CPU execution time jitter. Any change to - // the loop in this function implies that careful retesting must be done. - #[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 = tmp >> (64 - 1); - - // Fibonacci LSFR with polynomial of - // x^64 + x^61 + x^56 + x^31 + x^28 + x^23 + 1 which is - // primitive according to - // http://poincare.matf.bg.ac.rs/~ezivkovm/publications/primpol1.pdf - // (the shift values are the polynomial values minus one - // due to counting bits from 0 to 63). As the current - // position is always the LSB, the polynomial only needs - // to shift data in from the left without wrap. - 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 - } - - // Note: in the reference implementation only the last round effects - // `self.data`, all the other results are ignored. To make sure the - // other rounds are not optimised out, we first run all but the last - // round on a throw-away value instead of the real `self.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); - } - - // Memory Access noise source - // This is a noise source based on variations in memory access times - // - // This function performs memory accesses which will add to the timing - // variations due to an unknown amount of CPU wait states that need to be - // added when accessing memory. The memory size should be larger than the L1 - // caches as outlined in the documentation and the associated testing. - // - // The L1 cache has a very high bandwidth, albeit its access rate is usually - // slower than accessing CPU registers. Therefore, L1 accesses only add - // minimal variations as the CPU has hardly to wait. Starting with L2, - // significant variations are added because L2 typically does not belong to - // the CPU any more and therefore a wider range of CPU wait states is - // necessary for accesses. L3 and real memory accesses have even a wider - // range of wait states. However, to reliably access either L3 or memory, - // the `self.mem` memory must be quite large which is usually not desirable. - #[inline(never)] - fn memaccess(&mut self, 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; - for _ in 0..acc_loop_cnt { - // Addition of memblocksize - 1 to index with wrap around logic to - // ensure that every memory location is hit evenly. - // The modulus also allows the compiler to remove the indexing - // bounds check. - index = (index + MEMORY_BLOCKSIZE - 1) % MEMORY_SIZE; - - // memory access: just add 1 to one byte - // memory access implies read from and write to memory location - let tmp = self.mem[index]; - self.mem[index] = tmp.wrapping_add(1); - } - self.mem_prev_index = index; - } - - - // Stuck test by checking the: - // - 1st derivation of the jitter measurement (time delta) - // - 2nd derivation of the jitter measurement (delta of time deltas) - // - 3rd derivation of the jitter measurement (delta of delta of time - // deltas) - // - // All values must always be non-zero. - // This test is a heuristic to see whether the last measurement holds - // entropy. - fn stuck(&mut self, current_delta: i64) -> 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 - } - - // This is the heart of the entropy generation: calculate time deltas and - // use the CPU jitter in the time deltas. The jitter is injected into the - // entropy pool. - // - // Ensure that `self.prev_time` is primed before using the output of this - // function. This can be done by calling this function and not using its - // result. - fn measure_jitter(&mut self) -> Option<()> { - // Invoke one noise source before time measurement to add variations - self.memaccess(true); - - // Get time stamp and calculate time delta to previous - // invocation to measure the timing variations - let time = (self.timer)(); - // Note: wrapping_sub combined with a cast to `i64` generates a correct - // delta, even in the unlikely case this is a timer that is not strictly - // monotonic. - let current_delta = time.wrapping_sub(self.prev_time) as i64; - self.prev_time = time; - - // Call the next noise source which also injects the data - self.lfsr_time(current_delta as u64, true); - - // Check whether we have a stuck measurement (i.e. does the last - // measurement holds entropy?). - if self.stuck(current_delta) { return None }; - - // Rotate the data buffer by a prime number (any odd number would - // do) to ensure that every bit position of the input time stamp - // has an even chance of being merged with a bit position in the - // entropy pool. We do not use one here as the adjacent bits in - // successive time deltas may have some form of dependency. The - // chosen value of 7 implies that the low 7 bits of the next - // time delta value is concatenated with the current time delta. - self.data = self.data.rotate_left(7); - - Some(()) - } - - // Shuffle the pool a bit by mixing some value with a bijective function - // (XOR) into the pool. - // - // The function generates a mixer value that depends on the bits set and - // the location of the set bits in the random number generated by the - // entropy source. Therefore, based on the generated random number, this - // mixer value can have 2^64 different values. That mixer value is - // initialized with the first two SHA-1 constants. After obtaining the - // mixer value, it is XORed into the random number. - // - // The mixer value is not assumed to contain any entropy. But due to the - // XOR operation, it can also not destroy any entropy present in the - // entropy pool. - #[inline(never)] - fn stir_pool(&mut self) { - // This constant is derived from the first two 32 bit initialization - // vectors of SHA-1 as defined in FIPS 180-4 section 5.3.1 - // The order does not really matter as we do not rely on the specific - // numbers. We just pick the SHA-1 constants as they have a good mix of - // bit set and unset. - const CONSTANT: u64 = 0x67452301efcdab89; - - // The start value of the mixer variable is derived from the third - // and fourth 32 bit initialization vector of SHA-1 as defined in - // FIPS 180-4 section 5.3.1 - let mut mixer = 0x98badcfe10325476; - - // This is a constant time function to prevent leaking timing - // information about the random number. - // The normal code is: - // ``` - // for i in 0..64 { - // if ((self.data >> i) & 1) == 1 { mixer ^= CONSTANT; } - // } - // ``` - // This is a bit fragile, as LLVM really wants to use branches here, and - // we rely on it to not recognise the opportunity. - 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 { - // Prime `self.prev_time`, and run the noice sources to make sure the - // first loop round collects the expected entropy. - let _ = self.measure_jitter(); - - for _ in 0..self.rounds { - // If a stuck measurement is received, repeat measurement - // Note: we do not guard against an infinite loop, that would mean - // the timer suddenly became broken. - while self.measure_jitter().is_none() {} - } - - self.stir_pool(); - self.data - } - - /// Basic quality tests on the timer, by measuring CPU timing jitter a few - /// hundred times. - /// - /// If succesful, this will return the estimated number of rounds necessary - /// to collect 64 bits of entropy. Otherwise a `TimerError` with the cause - /// of the failure will be returned. - pub fn test_timer(&mut self) -> Result<u32, TimerError> { - // We could add a check for system capabilities such as `clock_getres` - // or check for `CONFIG_X86_TSC`, but it does not make much sense as the - // following sanity checks verify that we have a high-resolution timer. - - #[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))] - return Err(TimerError::NoTimer); - - 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; - - // TESTLOOPCOUNT needs some loops to identify edge systems. - // 100 is definitely too little. - const TESTLOOPCOUNT: u64 = 300; - const CLEARCACHE: u64 = 100; - - for i in 0..(CLEARCACHE + TESTLOOPCOUNT) { - // Measure time delta of core entropy collection logic - let time = (self.timer)(); - self.memaccess(true); - self.lfsr_time(time, true); - let time2 = (self.timer)(); - - // Test whether timer works - if time == 0 || time2 == 0 { - return Err(TimerError::NoTimer); - } - let delta = time2.wrapping_sub(time) as i64; - - // Test whether timer is fine grained enough to provide delta even - // when called shortly after each other -- this implies that we also - // have a high resolution timer - if delta == 0 { - return Err(TimerError::CoarseTimer); - } - - // Up to here we did not modify any variable that will be - // evaluated later, but we already performed some work. Thus we - // already have had an impact on the caches, branch prediction, - // etc. with the goal to clear it to get the worst case - // measurements. - if i < CLEARCACHE { continue; } - - if self.stuck(delta) { count_stuck += 1; } - - // Test whether we have an increasing timer. - if !(time2 > time) { time_backwards += 1; } - - // Count the number of times the counter increases in steps of 100ns - // or greater. - if (delta % 100) == 0 { count_mod += 1; } - - // Ensure that we have a varying delta timer which is necessary for - // the calculation of entropy -- perform this check only after the - // first loop is executed as we need to prime the old_delta value - delta_sum += (delta - old_delta).abs() as u64; - old_delta = delta; - } - - // We allow the time to run backwards for up to three times. - // This can happen if the clock is being adjusted by NTP operations. - // If such an operation just happens to interfere with our test, it - // should not fail. The value of 3 should cover the NTP case being - // performed during our test run. - if time_backwards > 3 { - return Err(TimerError::NotMonotonic); - } - - // Test that the available amount of entropy per round does not get to - // low. We expect 1 bit of entropy per round as a reasonable minimum - // (although less is possible, it means the collector loop has to run - // much more often). - // `assert!(delta_average >= log2(1))` - // `assert!(delta_sum / TESTLOOPCOUNT >= 1)` - // `assert!(delta_sum >= TESTLOOPCOUNT)` - if delta_sum < TESTLOOPCOUNT { - return Err(TimerError::TinyVariantions); - } - - // Ensure that we have variations in the time stamp below 100 for at - // least 10% of all checks -- on some platforms, the counter increments - // in multiples of 100, but not always - if count_mod > (TESTLOOPCOUNT * 9 / 10) { - return Err(TimerError::CoarseTimer); - } - - // If we have more than 90% stuck results, then this Jitter RNG is - // likely to not work well. - if count_stuck > (TESTLOOPCOUNT * 9 / 10) { - return Err(TimerError::TooManyStuck); - } - - // Estimate the number of `measure_jitter` rounds necessary for 64 bits - // of entropy. - // - // We don't try very hard to come up with a good estimate of the - // available bits of entropy per round here for two reasons: - // 1. Simple estimates of the available bits (like Shannon entropy) are - // too optimistic. - // 2) Unless we want to waste a lot of time during intialization, there - // only a small number of samples are available. - // - // Therefore we use a very simple and conservative estimate: - // `let bits_of_entropy = log2(delta_average) / 2`. - // - // The number of rounds `measure_jitter` should run to collect 64 bits - // of entropy is `64 / bits_of_entropy`. - // - // To have smaller rounding errors, intermediate values are multiplied - // by `FACTOR`. To compensate for `log2` and division rounding down, - // add 1. - let delta_average = delta_sum / TESTLOOPCOUNT; - // println!("delta_average: {}", delta_average); - - const FACTOR: u32 = 3; - fn log2(x: u64) -> u32 { 64 - x.leading_zeros() } - - // pow(δ, FACTOR) must be representable; if you have overflow reduce FACTOR - Ok(64 * 2 * FACTOR / (log2(delta_average.pow(FACTOR)) + 1)) - } - - /// Statistical test: return the timer delta of one normal run of the - /// `JitterEntropy` entropy collector. - /// - /// Setting `var_rounds` to `true` will execute the memory access and the - /// CPU jitter noice sources a variable amount of times (just like a real - /// `JitterEntropy` round). - /// - /// Setting `var_rounds` to `false` will execute the noice sources the - /// minimal number of times. This can be used to measure the minimum amount - /// of entropy one round of entropy collector can collect in the worst case. - /// - /// # Example - /// - /// Use `timer_stats` to run the [NIST SP 800-90B Entropy Estimation Suite] - /// (https://github.com/usnistgov/SP800-90B_EntropyAssessment). - /// - /// This is the recommended way to test the quality of `JitterRng`. It - /// should be run before using the RNG on untested hardware, after changes - /// that could effect how the code is optimised, and after major compiler - /// compiler changes, like a new LLVM version. - /// - /// First generate two files `jitter_rng_var.bin` and `jitter_rng_var.min`. - /// - /// Execute `python noniid_main.py -v jitter_rng_var.bin 8`, and validate it - /// with `restart.py -v jitter_rng_var.bin 8 <min-entropy>`. - /// This number is the expected amount of entropy that is at least available - /// for each round of the entropy collector. This number should be greater - /// than the amount estimated with `64 / test_timer()`. - /// - /// Execute `python noniid_main.py -v -u 4 jitter_rng_var.bin 4`, and - /// validate it with `restart.py -v -u 4 jitter_rng_var.bin 4 <min-entropy>`. - /// This number is the expected amount of entropy that is available in the - /// last 4 bits of the timer delta after running noice sources. Note that - /// a value of 3.70 is the minimum estimated entropy for true randomness. - /// - /// Execute `python noniid_main.py -v -u 4 jitter_rng_var.bin 4`, and - /// validate it with `restart.py -v -u 4 jitter_rng_var.bin 4 <min-entropy>`. - /// This number is the expected amount of entropy that is available to the - /// entropy collecter if both noice sources only run their minimal number of - /// times. This measures the absolute worst-case, and gives a lower bound - /// for the available entropy. - /// - /// ```rust,no_run - /// use rand::JitterRng; - /// - /// # use std::error::Error; - /// # use std::fs::File; - /// # use std::io::Write; - /// # - /// # fn try_main() -> Result<(), Box<Error>> { - /// fn get_nstime() -> u64 { - /// use std::time::{SystemTime, UNIX_EPOCH}; - /// - /// let dur = SystemTime::now().duration_since(UNIX_EPOCH).unwrap(); - /// // The correct way to calculate the current time is - /// // `dur.as_secs() * 1_000_000_000 + dur.subsec_nanos() as u64` - /// // But this is faster, and the difference in terms of entropy is - /// // negligible (log2(10^9) == 29.9). - /// dur.as_secs() << 30 | dur.subsec_nanos() as u64 - /// } - /// - /// // Do not initialize with `JitterRng::new`, but with `new_with_timer`. - /// // 'new' always runst `test_timer`, and can therefore fail to - /// // initialize. We want to be able to get the statistics even when the - /// // timer test fails. - /// let mut rng = JitterRng::new_with_timer(get_nstime); - /// - /// // 1_000_000 results are required for the NIST SP 800-90B Entropy - /// // Estimation Suite - /// // FIXME: this number is smaller here, otherwise the Doc-test is too slow - /// const ROUNDS: usize = 10_000; - /// let mut deltas_variable: Vec<u8> = Vec::with_capacity(ROUNDS); - /// let mut deltas_minimal: Vec<u8> = Vec::with_capacity(ROUNDS); - /// - /// for _ in 0..ROUNDS { - /// deltas_variable.push(rng.timer_stats(true) as u8); - /// deltas_minimal.push(rng.timer_stats(false) as u8); - /// } - /// - /// // Write out after the statistics collection loop, to not disturb the - /// // test results. - /// File::create("jitter_rng_var.bin")?.write(&deltas_variable)?; - /// File::create("jitter_rng_min.bin")?.write(&deltas_minimal)?; - /// # - /// # Ok(()) - /// # } - /// # - /// # fn main() { - /// # try_main().unwrap(); - /// # } - /// ``` - #[cfg(feature="std")] - pub fn timer_stats(&mut self, var_rounds: bool) -> i64 { - let time = platform::get_nstime(); - self.memaccess(var_rounds); - self.lfsr_time(time, var_rounds); - let time2 = platform::get_nstime(); - time2.wrapping_sub(time) as i64 - } -} - -#[cfg(feature="std")] -mod platform { - #[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "windows", all(target_arch = "wasm32", not(target_os = "emscripten")))))] - pub fn get_nstime() -> u64 { - use std::time::{SystemTime, UNIX_EPOCH}; - - let dur = SystemTime::now().duration_since(UNIX_EPOCH).unwrap(); - // The correct way to calculate the current time is - // `dur.as_secs() * 1_000_000_000 + dur.subsec_nanos() as u64` - // But this is faster, and the difference in terms of entropy is negligible - // (log2(10^9) == 29.9). - dur.as_secs() << 30 | dur.subsec_nanos() as u64 - } - - #[cfg(any(target_os = "macos", target_os = "ios"))] - pub fn get_nstime() -> u64 { - extern crate libc; - // On Mac OS and iOS std::time::SystemTime only has 1000ns resolution. - // We use `mach_absolute_time` instead. This provides a CPU dependent unit, - // to get real nanoseconds the result should by multiplied by numer/denom - // from `mach_timebase_info`. - // But we are not interested in the exact nanoseconds, just entropy. So we - // use the raw result. - unsafe { libc::mach_absolute_time() } - } - - #[cfg(target_os = "windows")] - pub fn get_nstime() -> u64 { - extern crate winapi; - unsafe { - let mut t = super::mem::zeroed(); - winapi::um::profileapi::QueryPerformanceCounter(&mut t); - *t.QuadPart() as u64 - } - } - - #[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))] - pub fn get_nstime() -> u64 { - unreachable!() - } -} - -// A function that is opaque to the optimizer to assist in avoiding dead-code -// elimination. Taken from `bencher`. -fn black_box<T>(dummy: T) -> T { - unsafe { - let ret = ptr::read_volatile(&dummy); - mem::forget(dummy); - ret - } -} - -impl Rng for JitterRng { - fn next_u32(&mut self) -> u32 { - // We want to use both parts of the generated entropy - if let Some(high) = self.data_remaining.take() { - high - } else { - let data = self.next_u64(); - self.data_remaining = Some((data >> 32) as u32); - data as u32 - } - } - - fn next_u64(&mut self) -> u64 { - self.gen_entropy() - } - - fn fill_bytes(&mut self, dest: &mut [u8]) { - let mut left = dest; - while left.len() >= 8 { - let (l, r) = {left}.split_at_mut(8); - left = r; - let chunk: [u8; 8] = unsafe { - mem::transmute(self.next_u64().to_le()) - }; - l.copy_from_slice(&chunk); - } - let n = left.len(); - if n > 0 { - let chunk: [u8; 8] = unsafe { - mem::transmute(self.next_u64().to_le()) - }; - left.copy_from_slice(&chunk[..n]); - } - } -} - -// There are no tests included because (1) this is an "external" RNG, so output -// is not reproducible and (2) `test_timer` *will* fail on some platforms. |