Skip to content

Commit

Permalink
Merge pull request #277 from pitdicker/switch_stdrng
Browse files Browse the repository at this point in the history
Switch StdRng and thread_rng to HC-128
  • Loading branch information
dhardy authored Mar 3, 2018
2 parents 9a5d7fb + ea09dc0 commit fdb6c38
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 37 deletions.
49 changes: 18 additions & 31 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -290,12 +290,6 @@ pub use error::{ErrorKind, Error};
#[cfg(feature="std")] pub use entropy_rng::EntropyRng;
#[cfg(feature="std")] pub use thread_rng::{ThreadRng, thread_rng, random};

// local use declarations
#[cfg(target_pointer_width = "32")]
use prng::IsaacRng as IsaacWordRng;
#[cfg(target_pointer_width = "64")]
use prng::Isaac64Rng as IsaacWordRng;

use distributions::{Distribution, Uniform, Range};
use distributions::range::SampleRange;

Expand Down Expand Up @@ -1008,16 +1002,21 @@ impl<R: SeedableRng> NewRng for R {
}
}

/// The standard RNG. This is designed to be efficient on the current
/// platform.
/// The standard RNG. The PRNG algorithm in `StdRng` is choosen to be efficient
/// on the current platform, to be statistically strong and unpredictable
/// (meaning a cryptographically secure PRNG).
///
/// The current algorithm used on all platforms is [HC-128].
///
/// Reproducibility of output from this generator is however not required, thus
/// future library versions may use a different internal generator with
/// different output. Further, this generator may not be portable and can
/// produce different output depending on the architecture. If you require
/// reproducible output, use a named RNG, for example `ChaChaRng`.
///
/// Reproducibility of output from this generator is not required, thus future
/// library versions may use a different internal generator with different
/// output. Further, this generator may not be portable and can produce
/// different output depending on the architecture. If you require reproducible
/// output, use a named RNG, for example `ChaChaRng`.
/// [HC-128]: struct.Hc128Rng.html
#[derive(Clone, Debug)]
pub struct StdRng(IsaacWordRng);
pub struct StdRng(Hc128Rng);

impl RngCore for StdRng {
fn next_u32(&mut self) -> u32 {
Expand All @@ -1038,14 +1037,14 @@ impl RngCore for StdRng {
}

impl SeedableRng for StdRng {
type Seed = <IsaacWordRng as SeedableRng>::Seed;
type Seed = <Hc128Rng as SeedableRng>::Seed;

fn from_seed(seed: Self::Seed) -> Self {
StdRng(IsaacWordRng::from_seed(seed))
StdRng(Hc128Rng::from_seed(seed))
}

fn from_rng<R: Rng>(rng: &mut R) -> Result<Self, Error> {
IsaacWordRng::from_rng(rng).map(|rng| StdRng(rng))
Hc128Rng::from_rng(rng).map(|rng| StdRng(rng))
}
}

Expand Down Expand Up @@ -1284,25 +1283,13 @@ mod test {
}

#[test]
#[cfg(target_pointer_width = "32")]
fn test_stdrng_construction() {
let seed = [1,0,0,0, 23,0,0,0, 200,1,0,0, 210,30,0,0,
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0];
let mut rng1 = StdRng::from_seed(seed);
assert_eq!(rng1.next_u32(), 2869442790);

let mut rng2 = StdRng::from_rng(&mut rng1).unwrap();
assert_eq!(rng2.next_u32(), 3094074039);
}
#[test]
#[cfg(target_pointer_width = "64")]
fn test_stdrng_construction() {
let seed = [1,0,0,0, 23,0,0,0, 200,1,0,0, 210,30,0,0,
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0];
let mut rng1 = StdRng::from_seed(seed);
assert_eq!(rng1.next_u64(), 14964555543728284049);
assert_eq!(rng1.next_u64(), 15759097995037006553);

let mut rng2 = StdRng::from_rng(&mut rng1).unwrap();
assert_eq!(rng2.next_u64(), 919595328260451758);
assert_eq!(rng2.next_u64(), 6766915756997287454);
}
}
2 changes: 1 addition & 1 deletion src/prng/hc128.rs
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,7 @@ impl RngCore for Hc128Rng {
filled += filled_u8;
}

let len_remainder = (dest.len() - filled) % 16;
let len_remainder = (dest.len() - filled) % (16 * 4);
let len_direct = dest.len() - len_remainder;

while filled < len_direct {
Expand Down
20 changes: 15 additions & 5 deletions src/thread_rng.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,13 @@ use {RngCore, StdRng, SeedableRng, EntropyRng};
use {Distribution, Uniform, Rng, Error};
use reseeding::ReseedingRng;

// Number of generated bytes after which to reseed `TreadRng`.
//
// The time it takes to reseed HC-128 is roughly equivalent to generating 7 KiB.
// We pick a treshold here that is large enough to not reduce the average
// performance too much, but also small enough to not make reseeding something
// that basically never happens.
const THREAD_RNG_RESEED_THRESHOLD: u64 = 32*1024*1024; // 32 MiB

/// The type returned by [`thread_rng`], essentially just a reference to the
/// PRNG in thread-local memory.
Expand All @@ -29,7 +36,6 @@ pub struct ThreadRng {

thread_local!(
static THREAD_RNG_KEY: Rc<RefCell<ReseedingRng<StdRng, EntropyRng>>> = {
const THREAD_RNG_RESEED_THRESHOLD: u64 = 32_768;
let mut entropy_source = EntropyRng::new();
let r = StdRng::from_rng(&mut entropy_source).unwrap_or_else(|err|
panic!("could not initialize thread_rng: {}", err));
Expand All @@ -46,21 +52,25 @@ thread_local!(
/// `let mut rng = thread_rng();`.
///
/// `ThreadRng` uses [`ReseedingRng`] wrapping a [`StdRng`] which is reseeded
/// after generating 32KiB of random data. A single instance is cached per
/// after generating 32 MiB of random data. A single instance is cached per
/// thread and the returned `ThreadRng` is a reference to this instance — hence
/// `ThreadRng` is neither `Send` nor `Sync` but is safe to use within a single
/// thread. This RNG is seeded and reseeded via [`EntropyRng`] as required.
///
/// Note that the reseeding is done as an extra precaution against entropy
/// leaks and is in theory unnecessary — to predict `thread_rng`'s output, an
/// attacker would have to either determine most of the RNG's seed or internal
/// state, or crack the algorithm used (ISAAC, which has not been proven
/// cryptographically secure, but has no known attack despite a 20-year old
/// challenge).
/// state, or crack the algorithm used.
///
/// Like [`StdRng`], `ThreadRng` is a cryptographically secure PRNG. The current
/// algorithm used is [HC-128], which is an array-based PRNG that trades memory
/// usage for better performance. This makes it similar to ISAAC, the algorithm
/// used in `ThreadRng` before rand 0.5.
///
/// [`ReseedingRng`]: reseeding/struct.ReseedingRng.html
/// [`StdRng`]: struct.StdRng.html
/// [`EntropyRng`]: struct.EntropyRng.html
/// [HC-128]: struct.Hc128Rng.html
pub fn thread_rng() -> ThreadRng {
ThreadRng { rng: THREAD_RNG_KEY.with(|t| t.clone()) }
}
Expand Down

0 comments on commit fdb6c38

Please sign in to comment.