Skip to content

Commit 6c666be

Browse files
authored
Merge pull request #277 from pitdicker/switch_stdrng
Switch StdRng and thread_rng to HC-128
2 parents a7a12e6 + 9d5e4c8 commit 6c666be

File tree

3 files changed

+34
-37
lines changed

3 files changed

+34
-37
lines changed

src/lib.rs

Lines changed: 18 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -290,12 +290,6 @@ pub use error::{ErrorKind, Error};
290290
#[cfg(feature="std")] pub use entropy_rng::EntropyRng;
291291
#[cfg(feature="std")] pub use thread_rng::{ThreadRng, thread_rng, random};
292292

293-
// local use declarations
294-
#[cfg(target_pointer_width = "32")]
295-
use prng::IsaacRng as IsaacWordRng;
296-
#[cfg(target_pointer_width = "64")]
297-
use prng::Isaac64Rng as IsaacWordRng;
298-
299293
use distributions::{Distribution, Uniform, Range};
300294
use distributions::range::SampleRange;
301295

@@ -1008,16 +1002,21 @@ impl<R: SeedableRng> NewRng for R {
10081002
}
10091003
}
10101004

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

10221021
impl RngCore for StdRng {
10231022
fn next_u32(&mut self) -> u32 {
@@ -1038,14 +1037,14 @@ impl RngCore for StdRng {
10381037
}
10391038

10401039
impl SeedableRng for StdRng {
1041-
type Seed = <IsaacWordRng as SeedableRng>::Seed;
1040+
type Seed = <Hc128Rng as SeedableRng>::Seed;
10421041

10431042
fn from_seed(seed: Self::Seed) -> Self {
1044-
StdRng(IsaacWordRng::from_seed(seed))
1043+
StdRng(Hc128Rng::from_seed(seed))
10451044
}
10461045

10471046
fn from_rng<R: Rng>(rng: &mut R) -> Result<Self, Error> {
1048-
IsaacWordRng::from_rng(rng).map(|rng| StdRng(rng))
1047+
Hc128Rng::from_rng(rng).map(|rng| StdRng(rng))
10491048
}
10501049
}
10511050

@@ -1284,25 +1283,13 @@ mod test {
12841283
}
12851284

12861285
#[test]
1287-
#[cfg(target_pointer_width = "32")]
1288-
fn test_stdrng_construction() {
1289-
let seed = [1,0,0,0, 23,0,0,0, 200,1,0,0, 210,30,0,0,
1290-
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0];
1291-
let mut rng1 = StdRng::from_seed(seed);
1292-
assert_eq!(rng1.next_u32(), 2869442790);
1293-
1294-
let mut rng2 = StdRng::from_rng(&mut rng1).unwrap();
1295-
assert_eq!(rng2.next_u32(), 3094074039);
1296-
}
1297-
#[test]
1298-
#[cfg(target_pointer_width = "64")]
12991286
fn test_stdrng_construction() {
13001287
let seed = [1,0,0,0, 23,0,0,0, 200,1,0,0, 210,30,0,0,
13011288
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0];
13021289
let mut rng1 = StdRng::from_seed(seed);
1303-
assert_eq!(rng1.next_u64(), 14964555543728284049);
1290+
assert_eq!(rng1.next_u64(), 15759097995037006553);
13041291

13051292
let mut rng2 = StdRng::from_rng(&mut rng1).unwrap();
1306-
assert_eq!(rng2.next_u64(), 919595328260451758);
1293+
assert_eq!(rng2.next_u64(), 6766915756997287454);
13071294
}
13081295
}

src/prng/hc128.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -350,7 +350,7 @@ impl RngCore for Hc128Rng {
350350
filled += filled_u8;
351351
}
352352

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

356356
while filled < len_direct {

src/thread_rng.rs

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,13 @@ use {RngCore, StdRng, SeedableRng, EntropyRng};
1717
use {Distribution, Uniform, Rng, Error};
1818
use reseeding::ReseedingRng;
1919

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

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

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

0 commit comments

Comments
 (0)