Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Switch StdRng and thread_rng to HC-128 #277

Merged
merged 3 commits into from
Mar 3, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -1285,25 +1284,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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess this means it can implement CryptoRng now.

///
/// [`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