From 01eee83c036b59244cf12c001229e03b05a7890a Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Fri, 12 Jan 2018 21:46:30 +0100 Subject: [PATCH 1/8] Rework SeedableRng trait --- src/lib.rs | 188 +++++++++++++++++++++++-------------------- src/prng/chacha.rs | 104 +++++++----------------- src/prng/hc128.rs | 51 +++++------- src/prng/isaac.rs | 94 +++++++++++----------- src/prng/isaac64.rs | 108 ++++++++++++------------- src/prng/xorshift.rs | 64 ++++++++------- src/rand_impls.rs | 8 +- src/reseeding.rs | 90 +++------------------ src/seq.rs | 5 +- 9 files changed, 300 insertions(+), 412 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index db3d930e8f5..eab4903897a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -250,8 +250,7 @@ #[cfg(feature="std")] extern crate std as core; #[cfg(all(feature = "alloc", not(feature="std")))] extern crate alloc; -use core::marker; -use core::mem; +use core::{marker, mem, slice}; #[cfg(feature="std")] use std::cell::RefCell; #[cfg(feature="std")] use std::rc::Rc; #[cfg(all(feature="alloc", not(feature="std")))] use alloc::boxed::Box; @@ -732,36 +731,66 @@ impl<'a, R: Rng> Iterator for AsciiGenerator<'a, R> { } } -/// A random number generator that can be explicitly seeded to produce -/// the same stream of randomness multiple times. -pub trait SeedableRng: Rng { - /// Reseed an RNG with the given seed. +mod private { + pub trait Sealed {} + impl Sealed for [u8; 4] {} + impl Sealed for [u8; 8] {} + impl Sealed for [u8; 12] {} + impl Sealed for [u8; 16] {} + impl Sealed for [u8; 24] {} + impl Sealed for [u8; 32] {} +} + +/// The seed type is restricted to these types. This trait is sealed to prevent +/// user-extension. +/// +/// Use of byte-arrays avoids endianness issues. We may extend this to allow +/// byte arrays of other lengths in the future. +pub trait SeedRestriction: private::Sealed + Default + AsMut<[u8]> {} +impl SeedRestriction for S where S: private::Sealed + Default + AsMut<[u8]> {} + +/// A random number generator that can be explicitly seeded. +/// +/// There are two subtle differences between `from_rng` and`from_seed` (beyond +/// the obvious): first, that `from_rng` has no reproducibility requirement, and +/// second, that `from_rng` may directly fill internal states larger than +/// `SeedableRng::Seed`, where `from_seed` may need some extra step to expand +/// the input. +pub trait SeedableRng: Sized { + /// Seed type. + type Seed: SeedRestriction; + + /// Create a new PRNG using the given seed. /// - /// # Example + /// Each PRNG should implement this. /// - /// ```rust - /// use rand::{Rng, SeedableRng, StdRng}; + /// Reproducibility is required; that is, a fixed PRNG seeded using this + /// function with a fixed seed should produce the same sequence of output + /// today, and in the future. PRNGs not able to satisfy this should make + /// clear notes in their documentation. It is however not required that this + /// function yield the same state as a reference implementation of the PRNG + /// given equivalent seed; if necessary another constructor should be used. /// - /// let seed: &[_] = &[1, 2, 3, 4]; - /// let mut rng: StdRng = SeedableRng::from_seed(seed); - /// println!("{}", rng.gen::()); - /// rng.reseed(&[5, 6, 7, 8]); - /// println!("{}", rng.gen::()); - /// ``` - fn reseed(&mut self, Seed); + /// It may be expected that bits in the seed are well distributed, i.e. that + /// values like 0, 1 and (size - 1) are unlikely. + fn from_seed(seed: Self::Seed) -> Self; - /// Create a new RNG with the given seed. + /// Create a new PRNG seeded from another `Rng`. /// - /// # Example - /// - /// ```rust - /// use rand::{Rng, SeedableRng, StdRng}; - /// - /// let seed: &[_] = &[1, 2, 3, 4]; - /// let mut rng: StdRng = SeedableRng::from_seed(seed); - /// println!("{}", rng.gen::()); - /// ``` - fn from_seed(seed: Seed) -> Self; + /// Seeding from a cryptographic generator should be fine. On the other + /// hand, seeding a simple numerical generator from another of the same + /// type sometimes has serious side effects such as effectively cloning the + /// generator. + fn from_rng(mut rng: R) -> Result { + let mut seed = Self::Seed::default(); + let size = mem::size_of::() as usize; + unsafe { + let ptr = seed.as_mut().as_mut_ptr() as *mut u8; + let slice = slice::from_raw_parts_mut(ptr, size); + rng.try_fill_bytes(slice)?; + } + Ok(Self::from_seed(seed)) + } } /// A wrapper for generating floating point numbers uniformly in the @@ -801,10 +830,11 @@ pub struct Closed01(pub F); /// The standard RNG. This is designed to be efficient on the current /// platform. +/// +/// The underlying algorithm is not fixed, thus values from this generator +/// cannot be guaranteed to be reproducible. #[derive(Clone, Debug)] -pub struct StdRng { - rng: IsaacWordRng, -} +pub struct StdRng(IsaacWordRng); impl StdRng { /// Create a randomly seeded instance of `StdRng`. @@ -821,10 +851,10 @@ impl StdRng { #[cfg(feature="std")] pub fn new() -> Result { match OsRng::new() { - Ok(mut r) => Ok(StdRng { rng: r.gen() }), + Ok(mut r) => Ok(StdRng(r.gen())), Err(e1) => { match JitterRng::new() { - Ok(mut r) => Ok(StdRng { rng: r.gen() }), + Ok(mut r) => Ok(StdRng(r.gen())), Err(_) => { Err(e1) } @@ -835,36 +865,32 @@ impl StdRng { } impl Rng for StdRng { - #[inline] fn next_u32(&mut self) -> u32 { - self.rng.next_u32() + self.0.next_u32() } - #[inline] fn next_u64(&mut self) -> u64 { - self.rng.next_u64() + self.0.next_u64() } - #[inline] fn fill_bytes(&mut self, dest: &mut [u8]) { - self.rng.fill_bytes(dest) + self.0.fill_bytes(dest); } - - #[inline] + fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { - self.rng.try_fill_bytes(dest) + self.0.try_fill_bytes(dest) } } -impl<'a> SeedableRng<&'a [usize]> for StdRng { - fn reseed(&mut self, seed: &'a [usize]) { - // the internal RNG can just be seeded from the above - // randomness. - self.rng.reseed(unsafe {mem::transmute(seed)}) +impl SeedableRng for StdRng { + type Seed = ::Seed; + + fn from_seed(seed: Self::Seed) -> Self { + StdRng(IsaacWordRng::from_seed(seed)) } - fn from_seed(seed: &'a [usize]) -> StdRng { - StdRng { rng: SeedableRng::from_seed(unsafe {mem::transmute(seed)}) } + fn from_rng(rng: R) -> Result { + IsaacWordRng::from_rng(rng).map(|rng| StdRng(rng)) } } @@ -1037,7 +1063,7 @@ pub fn sample(rng: &mut R, iterable: I, amount: usize) -> Vec mod test { use impls; #[cfg(feature="std")] - use super::{random, thread_rng, weak_rng}; + use super::{random, thread_rng}; use super::{Rng, SeedableRng, StdRng}; #[cfg(feature="alloc")] use alloc::boxed::Box; @@ -1057,8 +1083,21 @@ mod test { } pub fn rng(seed: u64) -> TestRng { - let seed = [seed as usize]; - TestRng { inner: StdRng::from_seed(&seed) } + // TODO: use from_hashable + let mut state = seed; + let mut seed = ::Seed::default(); + for x in seed.iter_mut() { + // PCG algorithm + const MUL: u64 = 6364136223846793005; + const INC: u64 = 11634580027462260723; + let oldstate = state; + state = oldstate.wrapping_mul(MUL).wrapping_add(INC); + + let xorshifted = (((oldstate >> 18) ^ oldstate) >> 27) as u32; + let rot = (oldstate >> 59) as u32; + *x = xorshifted.rotate_right(rot) as u8; + } + TestRng { inner: StdRng::from_seed(seed) } } struct ConstRng { i: u64 } @@ -1071,23 +1110,6 @@ mod test { } } - pub fn iter_eq(i: I, j: J) -> bool - where I: IntoIterator, - J: IntoIterator, - I::Item: Eq - { - // make sure the iterators have equal length - let mut i = i.into_iter(); - let mut j = j.into_iter(); - loop { - match (i.next(), j.next()) { - (Some(ref ei), Some(ref ej)) if ei == ej => { } - (None, None) => return true, - _ => return false, - } - } - } - #[test] fn test_fill_bytes_default() { let mut r = ConstRng { i: 0x11_22_33_44_55_66_77_88 }; @@ -1250,35 +1272,23 @@ mod test { #[test] #[cfg(target_pointer_width = "32")] fn test_stdrng_construction() { - let seed = [1, 23, 456, 7890, 0, 0, 0, 0]; - let mut rng1 = StdRng::from_seed(&seed); + 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); - /* FIXME: enable once `from_rng` has landed let mut rng2 = StdRng::from_rng(&mut rng1).unwrap(); - assert_eq!(rng1.next_u32(), 3094074039); - */ + assert_eq!(rng2.next_u32(), 3094074039); } #[test] #[cfg(target_pointer_width = "64")] fn test_stdrng_construction() { - let seed = [1, 23, 456, 7890, 0, 0, 0, 0]; - let mut rng1 = StdRng::from_seed(&seed); - assert_eq!(rng1.next_u32(), 3477963620); + 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); - /* FIXME: enable once `from_rng` has landed let mut rng2 = StdRng::from_rng(&mut rng1).unwrap(); - assert_eq!(rng1.next_u32(), 3094074039); - */ - } - - #[test] - #[cfg(feature="std")] - fn test_weak_rng() { - let s = weak_rng().gen_iter::().take(256).collect::>(); - let mut ra: StdRng = SeedableRng::from_seed(&s[..]); - let mut rb: StdRng = SeedableRng::from_seed(&s[..]); - assert!(iter_eq(ra.gen_ascii_chars().take(100), - rb.gen_ascii_chars().take(100))); + assert_eq!(rng2.next_u64(), 919595328260451758); } } diff --git a/src/prng/chacha.rs b/src/prng/chacha.rs index 61d475fde10..12c3c623031 100644 --- a/src/prng/chacha.rs +++ b/src/prng/chacha.rs @@ -11,12 +11,13 @@ //! The ChaCha random number generator. use core::fmt; -use {Rng, SeedableRng, Rand}; -use impls; +use {Rng, SeedableRng}; +use {impls, le}; -const KEY_WORDS : usize = 8; // 8 words for the 256-bit key -const STATE_WORDS : usize = 16; -const CHACHA_ROUNDS: u32 = 20; // Cryptographically secure from 8 upwards as of this writing +const SEED_WORDS: usize = 8; // 8 words for the 256-bit key +const STATE_WORDS: usize = 16; +const CHACHA_ROUNDS: u32 = 20; // Cryptographically secure from 8 upwards as of + // this writing /// A random number generator that uses the ChaCha20 algorithm [1]. /// @@ -79,7 +80,6 @@ fn core(new: &mut [u32; STATE_WORDS], input: &[u32; STATE_WORDS]) { } impl ChaChaRng { - /// Create an ChaCha random number generator using the default /// fixed key of 8 zero words. /// @@ -100,13 +100,7 @@ impl ChaChaRng { /// - 2917185654 /// - 2419978656 pub fn new_unseeded() -> ChaChaRng { - let mut rng = ChaChaRng { - buffer: [0; STATE_WORDS], - state: [0; STATE_WORDS], - index: STATE_WORDS - }; - rng.init(&[0; KEY_WORDS]); - rng + ChaChaRng::init([0; SEED_WORDS]) } /// Sets the internal 128-bit ChaCha counter to @@ -154,22 +148,15 @@ impl ChaChaRng { /// ``` /// [1]: Daniel J. Bernstein. [*Extending the Salsa20 /// nonce.*](https://cr.yp.to/papers.html#xsalsa) - fn init(&mut self, key: &[u32; KEY_WORDS]) { - self.state[0] = 0x61707865; - self.state[1] = 0x3320646E; - self.state[2] = 0x79622D32; - self.state[3] = 0x6B206574; - - for i in 0..KEY_WORDS { - self.state[4+i] = key[i]; - } - - self.state[12] = 0; - self.state[13] = 0; - self.state[14] = 0; - self.state[15] = 0; - - self.index = STATE_WORDS; + fn init(seed: [u32; SEED_WORDS]) -> Self { + ChaChaRng { + buffer: [0; STATE_WORDS], + state: [0x61707865, 0x3320646E, 0x79622D32, 0x6B206574, // constants + seed[0], seed[1], seed[2], seed[3], // seed + seed[4], seed[5], seed[6], seed[7], // seed + 0, 0, 0, 0], // counter + index: STATE_WORDS, // generate on first use + } } /// Refill the internal output buffer (`self.buffer`) @@ -213,45 +200,15 @@ impl Rng for ChaChaRng { } } -impl<'a> SeedableRng<&'a [u32]> for ChaChaRng { - - fn reseed(&mut self, seed: &'a [u32]) { - *self = Self::from_seed(seed); - } - - /// Create a ChaCha generator from a seed, - /// obtained from a variable-length u32 array. - /// Only up to 8 words are used; if less than 8 - /// words are used, the remaining are set to zero. - fn from_seed(seed: &'a [u32]) -> ChaChaRng { - let mut rng = ChaChaRng { - buffer: [0; STATE_WORDS], - state: [0; STATE_WORDS], - index: STATE_WORDS - }; - rng.init(&[0u32; KEY_WORDS]); - // set key in place - { - let key = &mut rng.state[4 .. 4+KEY_WORDS]; - for (k, s) in key.iter_mut().zip(seed.iter()) { - *k = *s; - } - } - rng +impl SeedableRng for ChaChaRng { + type Seed = [u8; SEED_WORDS*4]; + fn from_seed(seed: Self::Seed) -> Self { + let mut seed_u32 = [0u32; SEED_WORDS]; + le::read_u32_into(&seed, &mut seed_u32); + ChaChaRng::init(seed_u32) } } -impl Rand for ChaChaRng { - fn rand(other: &mut R) -> ChaChaRng { - let mut key : [u32; KEY_WORDS] = [0; KEY_WORDS]; - for word in key.iter_mut() { - *word = other.gen(); - } - SeedableRng::from_seed(&key[..]) - } -} - - #[cfg(test)] mod test { use {Rng, SeedableRng}; @@ -259,21 +216,22 @@ mod test { #[test] fn test_chacha_construction() { - let seed = [0, 0, 1, 0, 2, 0, 3, 0]; - let mut rng1 = ChaChaRng::from_seed(&seed); + let seed = [0,0,0,0,0,0,0,0, + 1,0,0,0,0,0,0,0, + 2,0,0,0,0,0,0,0, + 3,0,0,0,0,0,0,0]; + let mut rng1 = ChaChaRng::from_seed(seed); assert_eq!(rng1.next_u32(), 137206642); - /* FIXME: enable once `from_rng` has landed let mut rng2 = ChaChaRng::from_rng(&mut rng1).unwrap(); assert_eq!(rng2.next_u32(), 1325750369); - */ } #[test] fn test_chacha_true_values() { // Test vectors 1 and 2 from // https://tools.ietf.org/html/draft-nir-cfrg-chacha20-poly1305-04 - let seed: &[_] = &[0u32; 8]; + let seed = [0u8; 32]; let mut rng1: ChaChaRng = SeedableRng::from_seed(seed); let mut results = [0u32; 16]; @@ -292,7 +250,7 @@ mod test { assert_eq!(results, expected); - let seed: &[_] = &[0,1,2,3,4,5,6,7]; + let seed = [0,0,0,0, 1,0,0,0, 2,0,0,0, 3,0,0,0, 4,0,0,0, 5,0,0,0, 6,0,0,0, 7,0,0,0]; let mut rng2: ChaChaRng = SeedableRng::from_seed(seed); // Store the 17*i-th 32-bit word, @@ -312,7 +270,7 @@ mod test { #[test] fn test_chacha_true_bytes() { - let seed: &[_] = &[0u32; 8]; + let seed = [0u8; 32]; let mut rng: ChaChaRng = SeedableRng::from_seed(seed); let mut results = [0u8; 32]; rng.fill_bytes(&mut results); @@ -326,7 +284,7 @@ mod test { #[test] fn test_chacha_clone() { - let seed: &[_] = &[0,1,2,3,4,5,6,7]; + let seed = [0,0,0,0, 1,0,0,0, 2,0,0,0, 3,0,0,0, 4,0,0,0, 5,0,0,0, 6,0,0,0, 7,0,0,0]; let mut rng: ChaChaRng = SeedableRng::from_seed(seed); let mut clone = rng.clone(); for _ in 0..16 { diff --git a/src/prng/hc128.rs b/src/prng/hc128.rs index 6ac2f27eb40..f5c621516e7 100644 --- a/src/prng/hc128.rs +++ b/src/prng/hc128.rs @@ -11,8 +11,8 @@ //! The HC-128 random number generator. use core::{fmt, slice}; -use {Rng, SeedableRng, Rand}; -use impls; +use {Rng, SeedableRng}; +use {impls, le}; const SEED_WORDS: usize = 8; // 128 bit key followed by 128 bit iv @@ -393,27 +393,16 @@ impl Rng for Hc128Rng { } } -impl Rand for Hc128Rng { - fn rand(other: &mut R) -> Hc128Rng { - let mut seed = [0u32; 8]; - unsafe { - let ptr = seed.as_mut_ptr() as *mut u8; - let slice = slice::from_raw_parts_mut(ptr, 8 * 4); - other.fill_bytes(slice); - } - Hc128Rng::init(seed) - } -} +impl SeedableRng for Hc128Rng { + type Seed = [u8; SEED_WORDS*4]; -impl SeedableRng<[u32; SEED_WORDS]> for Hc128Rng { - fn reseed(&mut self, seed: [u32; SEED_WORDS]) { - *self = Self::from_seed(seed); - } /// Create an HC-128 random number generator with a seed. The seed has to be /// 256 bits in length, matching the 128 bit `key` followed by 128 bit `iv` /// when HC-128 where to be used as a stream cipher. - fn from_seed(seed: [u32; SEED_WORDS]) -> Hc128Rng { - Hc128Rng::init(seed) + fn from_seed(seed: Self::Seed) -> Self { + let mut seed_u32 = [0u32; SEED_WORDS]; + le::read_u32_into(&seed, &mut seed_u32); + Hc128Rng::init(seed_u32) } } @@ -425,8 +414,8 @@ mod test { #[test] // Test vector 1 from the paper "The Stream Cipher HC-128" fn test_hc128_true_values_a() { - let seed = [0u32, 0, 0, 0, // key - 0, 0, 0, 0]; // iv + let seed = [0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // key + 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0]; // iv let mut rng = Hc128Rng::from_seed(seed); let mut results = [0u32; 16]; @@ -441,8 +430,8 @@ mod test { #[test] // Test vector 2 from the paper "The Stream Cipher HC-128" fn test_hc128_true_values_b() { - let seed = [0u32, 0, 0, 0, // key - 1, 0, 0, 0]; // iv + let seed = [0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // key + 1,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0]; // iv let mut rng = Hc128Rng::from_seed(seed); let mut results = [0u32; 16]; @@ -457,8 +446,8 @@ mod test { #[test] // Test vector 3 from the paper "The Stream Cipher HC-128" fn test_hc128_true_values_c() { - let seed = [0x55u32, 0, 0, 0, // key - 0, 0, 0, 0]; // iv + let seed = [0x55,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // key + 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0]; // iv let mut rng = Hc128Rng::from_seed(seed); let mut results = [0u32; 16]; @@ -472,8 +461,8 @@ mod test { #[test] fn test_hc128_true_values_u64() { - let seed = [0u32, 0, 0, 0, // key - 0, 0, 0, 0]; // iv + let seed = [0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // key + 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0]; // iv let mut rng = Hc128Rng::from_seed(seed); let mut results = [0u64; 8]; @@ -499,8 +488,8 @@ mod test { #[test] fn test_hc128_true_values_bytes() { - let seed = [0x55u32, 0, 0, 0, // key - 0, 0, 0, 0]; // iv + let seed = [0x55,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // key + 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0]; // iv let mut rng = Hc128Rng::from_seed(seed); let expected = [0x31, 0xf9, 0x2a, 0xb0, 0x32, 0xf0, 0x39, 0x06, 0x7a, 0xa4, 0xb4, 0xbc, 0x0b, 0x48, 0x22, 0x57, @@ -536,8 +525,8 @@ mod test { #[test] fn test_hc128_clone() { - let seed = [0x55, 0, 0, 0, // key - 0, 0, 0, 0]; // iv + let seed = [0x55,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // key + 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0]; // iv let mut rng1 = Hc128Rng::from_seed(seed); let mut rng2 = rng1.clone(); for _ in 0..16 { diff --git a/src/prng/isaac.rs b/src/prng/isaac.rs index 0d217009b76..5fb4ef1dd51 100644 --- a/src/prng/isaac.rs +++ b/src/prng/isaac.rs @@ -10,13 +10,11 @@ //! The ISAAC random number generator. -use core::slice; -use core::iter::repeat; +use core::{fmt, slice}; use core::num::Wrapping as w; -use core::fmt; -use {Rng, SeedableRng, Rand}; -use impls; +use {Rng, SeedableRng, Error}; +use {impls, le}; #[allow(non_camel_case_types)] type w32 = w; @@ -333,41 +331,34 @@ fn mix(a: &mut w32, b: &mut w32, c: &mut w32, d: &mut w32, *h ^= *a >> 9; *c += *h; *a += *b; } -impl Rand for IsaacRng { - fn rand(other: &mut R) -> IsaacRng { - let mut key = [w(0); RAND_SIZE]; - unsafe { - let ptr = key.as_mut_ptr() as *mut u8; +impl SeedableRng for IsaacRng { + type Seed = [u8; 32]; - let slice = slice::from_raw_parts_mut(ptr, RAND_SIZE * 4); - other.fill_bytes(slice); + fn from_seed(seed: Self::Seed) -> Self { + let mut seed_u32 = [0u32; 8]; + le::read_u32_into(&seed, &mut seed_u32); + let mut seed_extended = [w(0); RAND_SIZE]; + for (x, y) in seed_extended.iter_mut().zip(seed_u32.iter()) { + *x = w(*y); } - - init(key, 2) - } -} - -impl<'a> SeedableRng<&'a [u32]> for IsaacRng { - fn reseed(&mut self, seed: &'a [u32]) { - *self = Self::from_seed(seed); + init(seed_extended, 2) } - /// Create an ISAAC random number generator with a seed. This can - /// be any length, although the maximum number of elements used is - /// 256 and any more will be silently ignored. A generator - /// constructed with a given seed will generate the same sequence - /// of values as all other generators constructed with that seed. - fn from_seed(seed: &'a [u32]) -> IsaacRng { - let mut key = [w(0); RAND_SIZE]; - // make the seed into [seed[0], seed[1], ..., seed[seed.len() - // - 1], 0, 0, ...], to fill `key`. - let seed_iter = seed.iter().map(|&x| x).chain(repeat(0u32)); + fn from_rng(mut rng: R) -> Result { + // Custom `from_rng` implementation that fills a seed with the same size + // as the entire state. + let mut seed = [w(0u32); RAND_SIZE]; + unsafe { + let ptr = seed.as_mut_ptr() as *mut u8; - for (rsl_elem, seed_elem) in key.iter_mut().zip(seed_iter) { - *rsl_elem = w(seed_elem); + let slice = slice::from_raw_parts_mut(ptr, RAND_SIZE * 4); + rng.try_fill_bytes(slice)?; + } + for i in seed.iter_mut() { + *i = w(i.0.to_le()); } - init(key, 2) + Ok(init(seed, 2)) } } @@ -378,20 +369,21 @@ mod test { #[test] fn test_isaac_construction() { - let seed = [1, 23, 456, 7890, 0, 0, 0, 0]; - let mut rng1 = IsaacRng::from_seed(&seed); + // Test that various construction techniques produce a working RNG. + 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 = IsaacRng::from_seed(seed); assert_eq!(rng1.next_u32(), 2869442790); - /* FIXME: enable once `from_rng` has landed let mut rng2 = IsaacRng::from_rng(&mut rng1).unwrap(); - assert_eq!(rng1.next_u32(), 3094074039); - */ + assert_eq!(rng2.next_u32(), 3094074039); } #[test] fn test_isaac_true_values_32() { - let seed: &[_] = &[1, 23, 456, 7890, 12345]; - let mut rng1: IsaacRng = SeedableRng::from_seed(seed); + let seed = [1,0,0,0, 23,0,0,0, 200,1,0,0, 210,30,0,0, + 57,48,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0]; + let mut rng1 = IsaacRng::from_seed(seed); let mut results = [0u32; 10]; for i in results.iter_mut() { *i = rng1.next_u32(); } let expected = [ @@ -399,8 +391,9 @@ mod test { 4203127393, 264982119, 2765226902, 2737944514, 3900253796]; assert_eq!(results, expected); - let seed: &[_] = &[12345, 67890, 54321, 9876]; - let mut rng2: IsaacRng = SeedableRng::from_seed(seed); + let seed = [57,48,0,0, 50,9,1,0, 49,212,0,0, 148,38,0,0, + 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0]; + let mut rng2 = IsaacRng::from_seed(seed); // skip forward to the 10000th number for _ in 0..10000 { rng2.next_u32(); } @@ -414,8 +407,9 @@ mod test { #[test] fn test_isaac_true_values_64() { // As above, using little-endian versions of above values - let seed: &[_] = &[1, 23, 456, 7890, 12345]; - let mut rng: IsaacRng = SeedableRng::from_seed(seed); + let seed = [1,0,0,0, 23,0,0,0, 200,1,0,0, 210,30,0,0, + 57,48,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0]; + let mut rng = IsaacRng::from_seed(seed); let mut results = [0u64; 5]; for i in results.iter_mut() { *i = rng.next_u64(); } let expected = [ @@ -426,7 +420,8 @@ mod test { #[test] fn test_isaac_true_bytes() { - let seed: &[_] = &[1, 23, 456, 7890, 12345]; + let seed = [1,0,0,0, 23,0,0,0, 200,1,0,0, 210,30,0,0, + 57,48,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0]; let mut rng = IsaacRng::from_seed(seed); let mut results = [0u8; 32]; rng.fill_bytes(&mut results); @@ -458,11 +453,12 @@ mod test { #[test] fn test_isaac_clone() { - let seed: &[_] = &[1, 23, 456, 7890, 12345]; - let mut rng: IsaacRng = SeedableRng::from_seed(seed); - let mut clone = rng.clone(); + let seed = [1,0,0,0, 23,0,0,0, 200,1,0,0, 210,30,0,0, + 57,48,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0]; + let mut rng1 = IsaacRng::from_seed(seed); + let mut rng2 = rng1.clone(); for _ in 0..16 { - assert_eq!(rng.next_u64(), clone.next_u64()); + assert_eq!(rng1.next_u32(), rng2.next_u32()); } } } diff --git a/src/prng/isaac64.rs b/src/prng/isaac64.rs index f791c2440b2..0e675281af8 100644 --- a/src/prng/isaac64.rs +++ b/src/prng/isaac64.rs @@ -10,13 +10,11 @@ //! The ISAAC-64 random number generator. -use core::slice; -use core::iter::repeat; +use core::{fmt, slice}; use core::num::Wrapping as w; -use core::fmt; -use {Rng, SeedableRng, Rand}; -use impls; +use {Rng, SeedableRng, Error}; +use {impls, le}; #[allow(non_camel_case_types)] type w64 = w; @@ -285,19 +283,15 @@ fn init(mut mem: [w64; RAND_SIZE], rounds: u32) -> Isaac64Rng { } } - let mut rng = Isaac64Rng { + Isaac64Rng { rsl: [0; RAND_SIZE], mem: mem, a: w(0), b: w(0), c: w(0), - index: 0, + index: RAND_SIZE as u32, // generate on first use half_used: false, - }; - - // Prepare the first set of results - rng.isaac64(); - rng + } } fn mix(a: &mut w64, b: &mut w64, c: &mut w64, d: &mut w64, @@ -312,41 +306,34 @@ fn mix(a: &mut w64, b: &mut w64, c: &mut w64, d: &mut w64, *h -= *d; *e ^= *g << 14; *g += *h; } -impl Rand for Isaac64Rng { - fn rand(other: &mut R) -> Isaac64Rng { - let mut key = [w(0); RAND_SIZE]; - unsafe { - let ptr = key.as_mut_ptr() as *mut u8; +impl SeedableRng for Isaac64Rng { + type Seed = [u8; 32]; - let slice = slice::from_raw_parts_mut(ptr, RAND_SIZE * 8); - other.fill_bytes(slice); + fn from_seed(seed: Self::Seed) -> Self { + let mut seed_u64 = [0u64; 4]; + le::read_u64_into(&seed, &mut seed_u64); + let mut seed_extended = [w(0); RAND_SIZE]; + for (x, y) in seed_extended.iter_mut().zip(seed_u64.iter()) { + *x = w(*y); } - init(key, 2) + init(seed_extended, 2) } -} -impl<'a> SeedableRng<&'a [u64]> for Isaac64Rng { - fn reseed(&mut self, seed: &'a [u64]) { - *self = Self::from_seed(seed); - } - - /// Create an ISAAC random number generator with a seed. This can - /// be any length, although the maximum number of elements used is - /// 256 and any more will be silently ignored. A generator - /// constructed with a given seed will generate the same sequence - /// of values as all other generators constructed with that seed. - fn from_seed(seed: &'a [u64]) -> Isaac64Rng { - let mut key = [w(0); RAND_SIZE]; - - // make the seed into [seed[0], seed[1], ..., seed[seed.len() - // - 1], 0, 0, ...], to fill `key`. - let seed_iter = seed.iter().map(|&x| x).chain(repeat(0u64)); + fn from_rng(mut rng: R) -> Result { + // Custom `from_rng` implementation that fills a seed with the same size + // as the entire state. + let mut seed = [w(0u64); RAND_SIZE]; + unsafe { + let ptr = seed.as_mut_ptr() as *mut u8; - for (rsl_elem, seed_elem) in key.iter_mut().zip(seed_iter) { - *rsl_elem = w(seed_elem); + let slice = slice::from_raw_parts_mut(ptr, RAND_SIZE * 8); + rng.try_fill_bytes(slice)?; + } + for i in seed.iter_mut() { + *i = w(i.0.to_le()); } - init(key, 2) + Ok(init(seed, 2)) } } @@ -358,20 +345,20 @@ mod test { #[test] fn test_isaac64_construction() { // Test that various construction techniques produce a working RNG. - let seed = [98784247809, 33887291965896, 0, 0]; - let mut rng1 = Isaac64Rng::from_seed(&seed); + 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 = Isaac64Rng::from_seed(seed); assert_eq!(rng1.next_u64(), 14964555543728284049); - /* FIXME: enable once `from_rng` has landed let mut rng2 = Isaac64Rng::from_rng(&mut rng1).unwrap(); assert_eq!(rng2.next_u64(), 919595328260451758); - */ } #[test] fn test_isaac64_true_values_64() { - let seed: &[_] = &[1, 23, 456, 7890]; - let mut rng1: Isaac64Rng = SeedableRng::from_seed(seed); + let seed = [1,0,0,0, 0,0,0,0, 23,0,0,0, 0,0,0,0, + 200,1,0,0, 0,0,0,0, 210,30,0,0, 0,0,0,0]; + let mut rng1 = Isaac64Rng::from_seed(seed); let mut results = [0u64; 10]; for i in results.iter_mut() { *i = rng1.next_u64(); } let expected = [ @@ -382,8 +369,9 @@ mod test { 14673053937227185542, 1677046725350116783]; assert_eq!(results, expected); - let seed: &[_] = &[12345, 67890, 54321, 9876]; - let mut rng2: Isaac64Rng = SeedableRng::from_seed(seed); + let seed = [57,48,0,0, 0,0,0,0, 50,9,1,0, 0,0,0,0, + 49,212,0,0, 0,0,0,0, 148,38,0,0, 0,0,0,0]; + let mut rng2 = Isaac64Rng::from_seed(seed); // skip forward to the 10000th number for _ in 0..10000 { rng2.next_u64(); } @@ -398,7 +386,8 @@ mod test { #[test] fn test_isaac64_true_values_32() { - let seed: &[_] = &[1, 23, 456, 7890]; + let seed = [1,0,0,0, 0,0,0,0, 23,0,0,0, 0,0,0,0, + 200,1,0,0, 0,0,0,0, 210,30,0,0, 0,0,0,0]; let mut rng = Isaac64Rng::from_seed(seed); let mut results = [0u32; 12]; for i in results.iter_mut() { *i = rng.next_u32(); } @@ -415,7 +404,8 @@ mod test { #[test] fn test_isaac64_true_values_mixed() { - let seed: &[_] = &[1, 23, 456, 7890]; + let seed = [1,0,0,0, 0,0,0,0, 23,0,0,0, 0,0,0,0, + 200,1,0,0, 0,0,0,0, 210,30,0,0, 0,0,0,0]; let mut rng = Isaac64Rng::from_seed(seed); // Test alternating between `next_u64` and `next_u32` works as expected. // Values are the same as `test_isaac64_true_values` and @@ -432,15 +422,16 @@ mod test { #[test] fn test_isaac64_true_bytes() { - let seed: &[_] = &[1, 23, 456, 7890]; + let seed = [1,0,0,0, 0,0,0,0, 23,0,0,0, 0,0,0,0, + 200,1,0,0, 0,0,0,0, 210,30,0,0, 0,0,0,0]; let mut rng = Isaac64Rng::from_seed(seed); let mut results = [0u8; 32]; rng.fill_bytes(&mut results); // Same as first values in test_isaac64_true_values as bytes in LE order let expected = [100, 131, 77, 207, 155, 181, 40, 209, - 102, 176, 255, 40, 238, 155, 35, 107, - 61, 123, 136, 13, 246, 243, 99, 150, - 216, 167, 15, 241, 62, 149, 34, 75]; + 102, 176, 255, 40, 238, 155, 35, 107, + 61, 123, 136, 13, 246, 243, 99, 150, + 216, 167, 15, 241, 62, 149, 34, 75]; assert_eq!(results, expected); } @@ -466,11 +457,12 @@ mod test { #[test] fn test_isaac64_clone() { - let seed: &[_] = &[1, 23, 456, 7890, 12345]; - let mut rng: Isaac64Rng = SeedableRng::from_seed(seed); - let mut clone = rng.clone(); + let seed = [1,0,0,0, 0,0,0,0, 23,0,0,0, 0,0,0,0, + 200,1,0,0, 0,0,0,0, 210,30,0,0, 0,0,0,0]; + let mut rng1 = Isaac64Rng::from_seed(seed); + let mut rng2 = rng1.clone(); for _ in 0..16 { - assert_eq!(rng.next_u64(), clone.next_u64()); + assert_eq!(rng1.next_u64(), rng2.next_u64()); } } } diff --git a/src/prng/xorshift.rs b/src/prng/xorshift.rs index ea83697996d..f3103e4f84c 100644 --- a/src/prng/xorshift.rs +++ b/src/prng/xorshift.rs @@ -11,9 +11,9 @@ //! Xorshift generators use core::num::Wrapping as w; -use core::fmt; -use {Rng, SeedableRng, Rand}; -use impls; +use core::{fmt, slice}; +use {Rng, SeedableRng, Error}; +use {impls, le}; /// An Xorshift[1] random number /// generator. @@ -79,39 +79,45 @@ impl Rng for XorShiftRng { } } -impl SeedableRng<[u32; 4]> for XorShiftRng { - /// Reseed an XorShiftRng. This will panic if `seed` is entirely 0. - fn reseed(&mut self, seed: [u32; 4]) { - assert!(!seed.iter().all(|&x| x == 0), - "XorShiftRng.reseed called with an all zero seed."); +impl SeedableRng for XorShiftRng { + type Seed = [u8; 16]; - self.x = w(seed[0]); - self.y = w(seed[1]); - self.z = w(seed[2]); - self.w = w(seed[3]); - } + fn from_seed(seed: Self::Seed) -> Self { + let mut seed_u32 = [0u32; 4]; + le::read_u32_into(&seed, &mut seed_u32); - /// Create a new XorShiftRng. This will panic if `seed` is entirely 0. - fn from_seed(seed: [u32; 4]) -> XorShiftRng { - assert!(!seed.iter().all(|&x| x == 0), - "XorShiftRng::from_seed called with an all zero seed."); + // Xorshift cannot be seeded with 0 and we cannot return an Error, but + // also do not wish to panic (because a random seed can legitimately be + // 0); our only option is therefore to use a preset value. + if seed_u32.iter().all(|&x| x == 0) { + seed_u32 = [0xBAD_5EED, 0xBAD_5EED, 0xBAD_5EED, 0xBAD_5EED]; + } XorShiftRng { - x: w(seed[0]), - y: w(seed[1]), - z: w(seed[2]), - w: w(seed[3]), + x: w(seed_u32[0]), + y: w(seed_u32[1]), + z: w(seed_u32[2]), + w: w(seed_u32[3]), } } -} -impl Rand for XorShiftRng { - fn rand(rng: &mut R) -> XorShiftRng { - let mut tuple: (u32, u32, u32, u32) = rng.gen(); - while tuple == (0, 0, 0, 0) { - tuple = rng.gen(); + fn from_rng(mut rng: R) -> Result { + let mut seed_u32 = [0u32; 4]; + loop { + unsafe { + let ptr = seed_u32.as_mut_ptr() as *mut u8; + + let slice = slice::from_raw_parts_mut(ptr, 4 * 4); + rng.try_fill_bytes(slice)?; + } + if !seed_u32.iter().all(|&x| x == 0) { break; } } - let (x, y, z, w_) = tuple; - XorShiftRng { x: w(x), y: w(y), z: w(z), w: w(w_) } + + Ok(XorShiftRng { + x: w(seed_u32[0]), + y: w(seed_u32[1]), + z: w(seed_u32[2]), + w: w(seed_u32[3]), + }) } } diff --git a/src/rand_impls.rs b/src/rand_impls.rs index 4b520b41bc5..e8c70f523d4 100644 --- a/src/rand_impls.rs +++ b/src/rand_impls.rs @@ -12,7 +12,7 @@ use core::{char, mem}; -use {Rand,Rng}; +use {Rand, Rng, SeedableRng}; impl Rand for isize { #[inline] @@ -246,6 +246,12 @@ impl Rand for Option { } } +impl Rand for T { + fn rand(rng: &mut R) -> Self { + Self::from_rng(rng).unwrap() + } +} + #[cfg(test)] mod tests { use impls; diff --git a/src/reseeding.rs b/src/reseeding.rs index 02427d27af2..1626d210dc1 100644 --- a/src/reseeding.rs +++ b/src/reseeding.rs @@ -13,11 +13,7 @@ use core::default::Default; -use {Rng, SeedableRng, Error}; - -/// How many bytes of entropy the underling RNG is allowed to generate -/// before it is reseeded -const DEFAULT_GENERATION_THRESHOLD: u64 = 32 * 1024; +use {Rng, Error}; /// A wrapper around any RNG which reseeds the underlying RNG after it /// has generated a certain number of random bytes. @@ -84,54 +80,11 @@ impl> Rng for ReseedingRng { } } -impl, Rsdr: Reseeder + Default> - SeedableRng<(Rsdr, S)> for ReseedingRng { - fn reseed(&mut self, (rsdr, seed): (Rsdr, S)) { - self.rng.reseed(seed); - self.reseeder = rsdr; - self.bytes_generated = 0; - } - - /// Create a new `ReseedingRng` from the given reseeder and - /// seed. This uses a default value for `generation_threshold`. - fn from_seed((rsdr, seed): (Rsdr, S)) -> ReseedingRng { - ReseedingRng { - rng: SeedableRng::from_seed(seed), - generation_threshold: DEFAULT_GENERATION_THRESHOLD, - bytes_generated: 0, - reseeder: rsdr - } - } -} - /// Something that can be used to reseed an RNG via `ReseedingRng`. /// -/// # Example -/// -/// ```rust -/// use rand::{Rng, SeedableRng, StdRng}; -/// use rand::reseeding::{Reseeder, ReseedingRng}; -/// -/// struct TickTockReseeder { tick: bool } -/// impl Reseeder for TickTockReseeder { -/// fn reseed(&mut self, rng: &mut StdRng) { -/// let val = if self.tick {0} else {1}; -/// rng.reseed(&[val]); -/// self.tick = !self.tick; -/// } -/// } -/// fn main() { -/// let rsdr = TickTockReseeder { tick: true }; -/// -/// let inner = StdRng::new().unwrap(); -/// let mut rng = ReseedingRng::new(inner, 10, rsdr); -/// -/// // this will repeat, because it gets reseeded very regularly. -/// let s: String = rng.gen_ascii_chars().take(100).collect(); -/// println!("{}", s); -/// } -/// -/// ``` +/// Note that implementations should support `Clone` only if reseeding is +/// deterministic (no external entropy source). This is so that a `ReseedingRng` +/// only supports `Clone` if fully deterministic. pub trait Reseeder { /// Reseed the given RNG. fn reseed(&mut self, rng: &mut R); @@ -153,7 +106,7 @@ impl Default for ReseedWithDefault { #[cfg(test)] mod test { - use impls; + use {impls, le}; use core::default::Default; use super::{ReseedingRng, ReseedWithDefault}; use {SeedableRng, Rng}; @@ -181,12 +134,12 @@ mod test { Counter { i: 0 } } } - impl SeedableRng for Counter { - fn reseed(&mut self, seed: u32) { - self.i = seed; - } - fn from_seed(seed: u32) -> Counter { - Counter { i: seed } + impl SeedableRng for Counter { + type Seed = [u8; 4]; + fn from_seed(seed: Self::Seed) -> Self { + let mut seed_u32 = [0u32; 1]; + le::read_u32_into(&seed, &mut seed_u32); + Counter { i: seed_u32[0] } } } type MyRng = ReseedingRng; @@ -202,27 +155,6 @@ mod test { } } - #[test] - fn test_rng_seeded() { - let mut ra: MyRng = SeedableRng::from_seed((ReseedWithDefault, 2)); - let mut rb: MyRng = SeedableRng::from_seed((ReseedWithDefault, 2)); - assert!(::test::iter_eq(ra.gen_ascii_chars().take(100), - rb.gen_ascii_chars().take(100))); - } - - #[test] - fn test_rng_reseed() { - let mut rng: MyRng = SeedableRng::from_seed((ReseedWithDefault, 3)); - let mut results1 = [0u32; 32]; - for i in results1.iter_mut() { *i = rng.next_u32(); } - - rng.reseed((ReseedWithDefault, 3)); - - let mut results2 = [0u32; 32]; - for i in results2.iter_mut() { *i = rng.next_u32(); } - assert_eq!(results1, results2); - } - const FILL_BYTES_V_LEN: usize = 13579; #[test] fn test_rng_fill_bytes() { diff --git a/src/seq.rs b/src/seq.rs index 765ceb29958..7c071afb500 100644 --- a/src/seq.rs +++ b/src/seq.rs @@ -303,9 +303,8 @@ mod test { for length in 1usize..max_range { let amount = r.gen_range(0, length); - let seed: [u32; 4] = [ - r.next_u32(), r.next_u32(), r.next_u32(), r.next_u32() - ]; + let mut seed = [0u8; 16]; + r.fill_bytes(&mut seed); // assert that the two index methods give exactly the same result let inplace = sample_indices_inplace( From 99fefbecedc8a45e91251b0cfe81ab865b7e6b85 Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Sat, 13 Jan 2018 18:06:35 +0100 Subject: [PATCH 2/8] Implement NewSeeded trait --- src/jitter.rs | 9 +++++- src/lib.rs | 82 +++++++++++++++++++++++++++++++++------------------ 2 files changed, 62 insertions(+), 29 deletions(-) diff --git a/src/jitter.rs b/src/jitter.rs index 5c6a328c37d..c7f8ed679cf 100644 --- a/src/jitter.rs +++ b/src/jitter.rs @@ -16,7 +16,7 @@ //! Non-physical true random number generator based on timing jitter. -use {Rng, impls}; +use {Rng, Error, ErrorKind, impls}; use core::{fmt, mem, ptr}; #[cfg(feature="std")] @@ -115,6 +115,13 @@ impl ::std::error::Error for TimerError { } } +impl From for Error { + fn from(err: TimerError) -> Error { + Error::with_cause(ErrorKind::Unavailable, + "timer jitter failed basic quality tests", err) + } +} + // Initialise to zero; must be positive #[cfg(feature="std")] static JITTER_ROUNDS: AtomicUsize = ATOMIC_USIZE_INIT; diff --git a/src/lib.rs b/src/lib.rs index eab4903897a..dbeb294ce80 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -793,6 +793,60 @@ pub trait SeedableRng: Sized { } } + +/// Seeding mechanism for PRNGs, providing a `new` function. +/// This is the recommended way to create (pseudo) random number generators, +/// unless a deterministic seed is desired (in which case +/// `SeedableRng::from_seed` should be used). +/// +/// Note: this trait is automatically implemented for any PRNG implementing +/// `SeedableRng` and is not intended to be implemented by users. +/// +/// ## Example +/// +/// ``` +/// use rand::{StdRng, Rng, NewSeeded}; +/// +/// let mut rng = StdRng::new().unwrap(); +/// println!("Random die roll: {}", rng.gen_range(1, 7)); +/// ``` +#[cfg(feature="std")] +pub trait NewSeeded: SeedableRng { + /// Creates a new instance, automatically seeded with fresh entropy. + /// + /// Normally this will use `OsRng`, but if that fails `JitterRng` will be + /// used instead. Both should be suitable for cryptography. It is possible + /// that both entropy sources will fail though unlikely. + fn new() -> Result; +} + +#[cfg(feature="std")] +impl NewSeeded for R { + fn new() -> Result { + // Note: error handling would be easier with try/catch blocks + fn new_os() -> Result { + let mut r = OsRng::new()?; + T::from_rng(&mut r) + } + + fn new_jitter() -> Result { + let mut r = JitterRng::new()?; + T::from_rng(&mut r) + } + + new_os().or_else(|e1| { + new_jitter().map_err(|_e2| { + // TODO: log + // TODO: can we somehow return both error sources? + Error::with_cause( + ErrorKind::Unavailable, + "seeding a new RNG failed: both OS and Jitter entropy sources failed", + e1) + }) + }) + } +} + /// A wrapper for generating floating point numbers uniformly in the /// open interval `(0,1)` (not including either endpoint). /// @@ -836,34 +890,6 @@ pub struct Closed01(pub F); #[derive(Clone, Debug)] pub struct StdRng(IsaacWordRng); -impl StdRng { - /// Create a randomly seeded instance of `StdRng`. - /// - /// This is a very expensive operation as it has to read - /// randomness from the operating system and use this in an - /// expensive seeding operation. If one is only generating a small - /// number of random numbers, or doesn't need the utmost speed for - /// generating each number, `thread_rng` and/or `random` may be more - /// appropriate. - /// - /// Reading the randomness from the OS may fail, and any error is - /// propagated via the `io::Result` return value. - #[cfg(feature="std")] - pub fn new() -> Result { - match OsRng::new() { - Ok(mut r) => Ok(StdRng(r.gen())), - Err(e1) => { - match JitterRng::new() { - Ok(mut r) => Ok(StdRng(r.gen())), - Err(_) => { - Err(e1) - } - } - } - } - } -} - impl Rng for StdRng { fn next_u32(&mut self) -> u32 { self.0.next_u32() From 14f27dbc9054062326dcbf4a77f3ad21433f56fd Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Sat, 13 Jan 2018 19:20:26 +0100 Subject: [PATCH 3/8] Replace ReseedWithDefault with ReseedWithNew --- src/lib.rs | 60 +++++++++++++++++------------------------------- src/reseeding.rs | 36 ++++++++++++++--------------- 2 files changed, 39 insertions(+), 57 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index dbeb294ce80..38aee1c2629 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -276,6 +276,7 @@ use prng::Isaac64Rng as IsaacWordRng; use distributions::{Range, IndependentSample}; use distributions::range::SampleRange; +use reseeding::{ReseedingRng, ReseedWithNew}; // public modules pub mod distributions; @@ -933,58 +934,39 @@ pub fn weak_rng() -> XorShiftRng { thread_rng().gen() } -/// Controls how the thread-local RNG is reseeded. -#[cfg(feature="std")] -#[derive(Debug)] -struct ThreadRngReseeder; - -#[cfg(feature="std")] -impl reseeding::Reseeder for ThreadRngReseeder { - fn reseed(&mut self, rng: &mut StdRng) { - match StdRng::new() { - Ok(r) => *rng = r, - Err(e) => panic!("No entropy available: {}", e), - } - } -} -#[cfg(feature="std")] -const THREAD_RNG_RESEED_THRESHOLD: u64 = 32_768; -#[cfg(feature="std")] -type ThreadRngInner = reseeding::ReseedingRng; /// The thread-local RNG. #[cfg(feature="std")] #[derive(Clone, Debug)] pub struct ThreadRng { - rng: Rc>, + rng: Rc>>, } -/// Retrieve the lazily-initialized thread-local random number -/// generator, seeded by the system. Intended to be used in method -/// chaining style, e.g. `thread_rng().gen::()`. -/// -/// After generating a certain amount of randomness, the RNG will reseed itself -/// from the operating system or, if the operating system RNG returns an error, -/// a seed based on the current system time. -/// -/// The internal RNG used is platform and architecture dependent, even -/// if the operating system random number generator is rigged to give -/// the same sequence always. If absolute consistency is required, -/// explicitly select an RNG, e.g. `IsaacRng` or `Isaac64Rng`. #[cfg(feature="std")] -pub fn thread_rng() -> ThreadRng { - // used to make space in TLS for a random number generator - thread_local!(static THREAD_RNG_KEY: Rc> = { +thread_local!( + static THREAD_RNG_KEY: Rc>> = { + const THREAD_RNG_RESEED_THRESHOLD: u64 = 32_768; let r = match StdRng::new() { Ok(r) => r, - Err(e) => panic!("No entropy available: {}", e), + Err(e) => panic!("could not initialize thread_rng: {:?}", e) }; - let rng = reseeding::ReseedingRng::new(r, - THREAD_RNG_RESEED_THRESHOLD, - ThreadRngReseeder); + let rng = ReseedingRng::new(r, + THREAD_RNG_RESEED_THRESHOLD, + ReseedWithNew); Rc::new(RefCell::new(rng)) - }); + } +); +/// Retrieve the lazily-initialized thread-local random number +/// generator, seeded by the system. Intended to be used in method +/// chaining style, e.g. `thread_rng().gen::()`. +/// +/// The internal RNG used is the one defined by `StdRng`. After generating 32KiB +/// of random bytes, the RNG will reseed itself from the operating system or, if +/// the operating system RNG returns an error, the `JitterRng` entropy +/// collector. +#[cfg(feature="std")] +pub fn thread_rng() -> ThreadRng { ThreadRng { rng: THREAD_RNG_KEY.with(|t| t.clone()) } } diff --git a/src/reseeding.rs b/src/reseeding.rs index 1626d210dc1..cf389626a3c 100644 --- a/src/reseeding.rs +++ b/src/reseeding.rs @@ -11,9 +11,9 @@ //! A wrapper around another RNG that reseeds it after it //! generates a certain number of random bytes. -use core::default::Default; - use {Rng, Error}; +#[cfg(feature="std")] +use NewSeeded; /// A wrapper around any RNG which reseeds the underlying RNG after it /// has generated a certain number of random bytes. @@ -47,7 +47,7 @@ impl> ReseedingRng { /// generated exceed the threshold. pub fn reseed_if_necessary(&mut self) { if self.bytes_generated >= self.generation_threshold { - self.reseeder.reseed(&mut self.rng); + self.reseeder.reseed(&mut self.rng).unwrap(); self.bytes_generated = 0; } } @@ -85,30 +85,31 @@ impl> Rng for ReseedingRng { /// Note that implementations should support `Clone` only if reseeding is /// deterministic (no external entropy source). This is so that a `ReseedingRng` /// only supports `Clone` if fully deterministic. -pub trait Reseeder { +pub trait Reseeder { /// Reseed the given RNG. - fn reseed(&mut self, rng: &mut R); + /// + /// On error, this should just forward the source error; errors are handled + /// by the caller. + fn reseed(&mut self, rng: &mut R) -> Result<(), Error>; } -/// Reseed an RNG using a `Default` instance. This reseeds by -/// replacing the RNG with the result of a `Default::default` call. -#[derive(Clone, Copy, Debug)] -pub struct ReseedWithDefault; +/// Reseed an RNG using `NewSeeded` to replace the current instance. +#[cfg(feature="std")] +#[derive(Debug)] +pub struct ReseedWithNew; -impl Reseeder for ReseedWithDefault { - fn reseed(&mut self, rng: &mut R) { - *rng = Default::default(); +#[cfg(feature="std")] +impl Reseeder for ReseedWithNew { + fn reseed(&mut self, rng: &mut R) -> Result<(), Error> { + R::new().map(|result| *rng = result) } } -impl Default for ReseedWithDefault { - fn default() -> ReseedWithDefault { ReseedWithDefault } -} #[cfg(test)] mod test { use {impls, le}; use core::default::Default; - use super::{ReseedingRng, ReseedWithDefault}; + use super::{ReseedingRng, ReseedWithNew}; use {SeedableRng, Rng}; struct Counter { @@ -142,11 +143,10 @@ mod test { Counter { i: seed_u32[0] } } } - type MyRng = ReseedingRng; #[test] fn test_reseeding() { - let mut rs = ReseedingRng::new(Counter {i:0}, 400, ReseedWithDefault); + let mut rs = ReseedingRng::new(Counter {i:0}, 400, ReseedWithNew); let mut i = 0; for _ in 0..1000 { From 449cb389929c1b654f3633257e9fdc621c4031f8 Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Sat, 13 Jan 2018 21:36:12 +0100 Subject: [PATCH 4/8] Fix benchmarks and no_std --- benches/bench.rs | 2 +- benches/generators.rs | 2 +- src/lib.rs | 2 +- src/reseeding.rs | 32 ++++++++++++-------------------- 4 files changed, 15 insertions(+), 23 deletions(-) diff --git a/benches/bench.rs b/benches/bench.rs index d396f25b5e9..cf181cfa1ce 100644 --- a/benches/bench.rs +++ b/benches/bench.rs @@ -9,7 +9,7 @@ mod distributions; use std::mem::size_of; use test::{black_box, Bencher}; -use rand::{StdRng, Rng}; +use rand::{StdRng, Rng, NewSeeded}; #[bench] fn rand_f32(b: &mut Bencher) { diff --git a/benches/generators.rs b/benches/generators.rs index 5172551083f..7e6e4803110 100644 --- a/benches/generators.rs +++ b/benches/generators.rs @@ -9,7 +9,7 @@ const BYTES_LEN: usize = 1024; use std::mem::size_of; use test::{black_box, Bencher}; -use rand::{Rng, StdRng, OsRng, JitterRng}; +use rand::{Rng, NewSeeded, StdRng, OsRng, JitterRng}; use rand::{XorShiftRng, Hc128Rng, IsaacRng, Isaac64Rng, ChaChaRng}; macro_rules! gen_bytes { diff --git a/src/lib.rs b/src/lib.rs index 38aee1c2629..ab8967ecd5f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -276,7 +276,7 @@ use prng::Isaac64Rng as IsaacWordRng; use distributions::{Range, IndependentSample}; use distributions::range::SampleRange; -use reseeding::{ReseedingRng, ReseedWithNew}; +#[cfg(feature="std")]use reseeding::{ReseedingRng, ReseedWithNew}; // public modules pub mod distributions; diff --git a/src/reseeding.rs b/src/reseeding.rs index cf389626a3c..1fdb5dc20b8 100644 --- a/src/reseeding.rs +++ b/src/reseeding.rs @@ -108,9 +108,8 @@ impl Reseeder for ReseedWithNew { #[cfg(test)] mod test { use {impls, le}; - use core::default::Default; - use super::{ReseedingRng, ReseedWithNew}; - use {SeedableRng, Rng}; + use super::{ReseedingRng, Reseeder}; + use {SeedableRng, Rng, Error}; struct Counter { i: u32 @@ -130,11 +129,6 @@ mod test { impls::fill_bytes_via_u64(self, dest) } } - impl Default for Counter { - fn default() -> Counter { - Counter { i: 0 } - } - } impl SeedableRng for Counter { type Seed = [u8; 4]; fn from_seed(seed: Self::Seed) -> Self { @@ -144,9 +138,18 @@ mod test { } } + #[derive(Debug, Clone)] + struct ReseedCounter; + impl Reseeder for ReseedCounter { + fn reseed(&mut self, rng: &mut Counter) -> Result<(), Error> { + *rng = Counter { i: 0 }; + Ok(()) + } + } + #[test] fn test_reseeding() { - let mut rs = ReseedingRng::new(Counter {i:0}, 400, ReseedWithNew); + let mut rs = ReseedingRng::new(Counter {i:0}, 400, ReseedCounter); let mut i = 0; for _ in 0..1000 { @@ -154,15 +157,4 @@ mod test { i += 1; } } - - const FILL_BYTES_V_LEN: usize = 13579; - #[test] - fn test_rng_fill_bytes() { - let mut v = [0u8; FILL_BYTES_V_LEN]; - ::test::rng(321).fill_bytes(&mut v); - - // To test that `fill_bytes` actually did something, check that not all - // bytes are zero. - assert!(!v.iter().all(|&x| x == 0)); - } } From b21d1a9b45869bcffe2b4b272cc6a7e275710689 Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Sun, 14 Jan 2018 14:53:53 +0100 Subject: [PATCH 5/8] Improve documentation --- src/lib.rs | 67 +++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 46 insertions(+), 21 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index ab8967ecd5f..9c25a7f5cea 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -752,36 +752,58 @@ impl SeedRestriction for S where S: private::Sealed + Default + AsMut<[u8]> { /// A random number generator that can be explicitly seeded. /// -/// There are two subtle differences between `from_rng` and`from_seed` (beyond -/// the obvious): first, that `from_rng` has no reproducibility requirement, and -/// second, that `from_rng` may directly fill internal states larger than -/// `SeedableRng::Seed`, where `from_seed` may need some extra step to expand -/// the input. +/// Each pseudo-random number generator (PRNG) should implement this. pub trait SeedableRng: Sized { - /// Seed type. + /// Seed type, which is restricted to `u8` arrays with a length of + /// 4, 8, 12, 16, 24 and 32. + /// + /// It is recommended to seed PRNG's with a seed of more than circa + /// 100 bits, which means an array of `[u8; 12]` or greater to avoid picking + /// RNG's with partially overlapping periods. + /// + /// For cryptographic RNG's a seed of 256 bits is recommended, `[u8; 32]`. type Seed: SeedRestriction; /// Create a new PRNG using the given seed. /// - /// Each PRNG should implement this. + /// PRNG implementations are allowed to assume that bits in the seed are + /// well distributed. That means usually that the number of one and zero + /// bits are about equal, and values like 0, 1 and (size - 1) are unlikely. /// - /// Reproducibility is required; that is, a fixed PRNG seeded using this - /// function with a fixed seed should produce the same sequence of output - /// today, and in the future. PRNGs not able to satisfy this should make - /// clear notes in their documentation. It is however not required that this - /// function yield the same state as a reference implementation of the PRNG - /// given equivalent seed; if necessary another constructor should be used. + /// PRNG implementations are recommended to be reproducible. A PRNG seeded + /// using this function with a fixed seed should produce the same sequence + /// of output in the future and on different architectures (with for example + /// different endianness). /// - /// It may be expected that bits in the seed are well distributed, i.e. that - /// values like 0, 1 and (size - 1) are unlikely. + /// It is however not required that this function yield the same state as a + /// reference implementation of the PRNG given equivalent seed; if necessary + /// another constructor can be used. fn from_seed(seed: Self::Seed) -> Self; /// Create a new PRNG seeded from another `Rng`. /// - /// Seeding from a cryptographic generator should be fine. On the other - /// hand, seeding a simple numerical generator from another of the same - /// type sometimes has serious side effects such as effectively cloning the - /// generator. + /// This is the recommended way to initialize PRNGs. See the `NewSeeded` + /// trait that provides a convenient `new` method using `from_rng` and a + /// good entropy source. + /// + /// It is recommended to use a good source of randomness to initialize the + /// PRNG. Otherwise small PRNG's could show statistical bias in the first + /// couple of results, and possibly not use their entire period well. + /// Cryptographic PRNG's can be less secure or even insecure when they are + /// seeded from a non-cryptographic PRNG. + /// + /// Examples of good RNG's for seeding are entropy sources like `OsRng` and + /// `JitterRng`. Also cryptographically secure PRNG's (like `thread_rng`) + /// can be used without hesitation. + /// + /// Seeding a small PRNG from another small PRNG is be possible, but + /// something to be careful with. An extreme example of how this can go + /// wrong is seeding an Xorshift RNG from another Xorshift RNG. That will + /// effectively clone the generator. + /// + /// PRNG implementations are allowed to assume that a good RNG is provided + /// for seeding, and that it is cryptographically secure when appropriate. + /// There are no reproducibility requirements like endianness conversion. fn from_rng(mut rng: R) -> Result { let mut seed = Self::Seed::default(); let size = mem::size_of::() as usize; @@ -886,8 +908,11 @@ pub struct Closed01(pub F); /// The standard RNG. This is designed to be efficient on the current /// platform. /// -/// The underlying algorithm is not fixed, thus values from this generator -/// cannot be guaranteed to be reproducible. +/// 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`. #[derive(Clone, Debug)] pub struct StdRng(IsaacWordRng); From 3d4e68659d10906836e0d16e6beaeae7d78c6a1b Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Thu, 18 Jan 2018 19:41:19 +0100 Subject: [PATCH 6/8] Remove unsafe from from_seed --- src/lib.rs | 29 +++-------------------------- 1 file changed, 3 insertions(+), 26 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 9c25a7f5cea..58fcd3e8866 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -250,7 +250,7 @@ #[cfg(feature="std")] extern crate std as core; #[cfg(all(feature = "alloc", not(feature="std")))] extern crate alloc; -use core::{marker, mem, slice}; +use core::{marker, mem}; #[cfg(feature="std")] use std::cell::RefCell; #[cfg(feature="std")] use std::rc::Rc; #[cfg(all(feature="alloc", not(feature="std")))] use alloc::boxed::Box; @@ -732,24 +732,6 @@ impl<'a, R: Rng> Iterator for AsciiGenerator<'a, R> { } } -mod private { - pub trait Sealed {} - impl Sealed for [u8; 4] {} - impl Sealed for [u8; 8] {} - impl Sealed for [u8; 12] {} - impl Sealed for [u8; 16] {} - impl Sealed for [u8; 24] {} - impl Sealed for [u8; 32] {} -} - -/// The seed type is restricted to these types. This trait is sealed to prevent -/// user-extension. -/// -/// Use of byte-arrays avoids endianness issues. We may extend this to allow -/// byte arrays of other lengths in the future. -pub trait SeedRestriction: private::Sealed + Default + AsMut<[u8]> {} -impl SeedRestriction for S where S: private::Sealed + Default + AsMut<[u8]> {} - /// A random number generator that can be explicitly seeded. /// /// Each pseudo-random number generator (PRNG) should implement this. @@ -762,7 +744,7 @@ pub trait SeedableRng: Sized { /// RNG's with partially overlapping periods. /// /// For cryptographic RNG's a seed of 256 bits is recommended, `[u8; 32]`. - type Seed: SeedRestriction; + type Seed: Sized + Default + AsMut<[u8]>; /// Create a new PRNG using the given seed. /// @@ -806,12 +788,7 @@ pub trait SeedableRng: Sized { /// There are no reproducibility requirements like endianness conversion. fn from_rng(mut rng: R) -> Result { let mut seed = Self::Seed::default(); - let size = mem::size_of::() as usize; - unsafe { - let ptr = seed.as_mut().as_mut_ptr() as *mut u8; - let slice = slice::from_raw_parts_mut(ptr, size); - rng.try_fill_bytes(slice)?; - } + rng.try_fill_bytes(seed.as_mut())?; Ok(Self::from_seed(seed)) } } From 2f4269895dd61b3b9b5c3134d38c98a6123645e1 Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Sat, 20 Jan 2018 15:09:06 +0100 Subject: [PATCH 7/8] Rename NewSeeded to NewRng --- benches/bench.rs | 2 +- benches/generators.rs | 23 +++-------------------- src/lib.rs | 8 ++++---- src/reseeding.rs | 6 +++--- 4 files changed, 11 insertions(+), 28 deletions(-) diff --git a/benches/bench.rs b/benches/bench.rs index cf181cfa1ce..bb9f54d92d8 100644 --- a/benches/bench.rs +++ b/benches/bench.rs @@ -9,7 +9,7 @@ mod distributions; use std::mem::size_of; use test::{black_box, Bencher}; -use rand::{StdRng, Rng, NewSeeded}; +use rand::{StdRng, Rng, NewRng}; #[bench] fn rand_f32(b: &mut Bencher) { diff --git a/benches/generators.rs b/benches/generators.rs index 7e6e4803110..fe54a883510 100644 --- a/benches/generators.rs +++ b/benches/generators.rs @@ -9,27 +9,10 @@ const BYTES_LEN: usize = 1024; use std::mem::size_of; use test::{black_box, Bencher}; -use rand::{Rng, NewSeeded, StdRng, OsRng, JitterRng}; +use rand::{Rng, NewRng, StdRng, OsRng, JitterRng}; use rand::{XorShiftRng, Hc128Rng, IsaacRng, Isaac64Rng, ChaChaRng}; macro_rules! gen_bytes { - ($fnn:ident, $gen:ident) => { - #[bench] - fn $fnn(b: &mut Bencher) { - let mut rng: $gen = OsRng::new().unwrap().gen(); - let mut buf = [0u8; BYTES_LEN]; - b.iter(|| { - for _ in 0..RAND_BENCH_N { - rng.fill_bytes(&mut buf); - black_box(buf); - } - }); - b.bytes = BYTES_LEN as u64 * RAND_BENCH_N; - } - } -} - -macro_rules! gen_bytes_new { ($fnn:ident, $gen:ident) => { #[bench] fn $fnn(b: &mut Bencher) { @@ -51,8 +34,8 @@ gen_bytes!(gen_bytes_hc128, Hc128Rng); gen_bytes!(gen_bytes_isaac, IsaacRng); gen_bytes!(gen_bytes_isaac64, Isaac64Rng); gen_bytes!(gen_bytes_chacha, ChaChaRng); -gen_bytes_new!(gen_bytes_std, StdRng); -gen_bytes_new!(gen_bytes_os, OsRng); +gen_bytes!(gen_bytes_std, StdRng); +gen_bytes!(gen_bytes_os, OsRng); macro_rules! gen_uint { diff --git a/src/lib.rs b/src/lib.rs index 58fcd3e8866..27214ae5f24 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -764,7 +764,7 @@ pub trait SeedableRng: Sized { /// Create a new PRNG seeded from another `Rng`. /// - /// This is the recommended way to initialize PRNGs. See the `NewSeeded` + /// This is the recommended way to initialize PRNGs. See the `NewRng` /// trait that provides a convenient `new` method using `from_rng` and a /// good entropy source. /// @@ -805,13 +805,13 @@ pub trait SeedableRng: Sized { /// ## Example /// /// ``` -/// use rand::{StdRng, Rng, NewSeeded}; +/// use rand::{StdRng, Rng, NewRng}; /// /// let mut rng = StdRng::new().unwrap(); /// println!("Random die roll: {}", rng.gen_range(1, 7)); /// ``` #[cfg(feature="std")] -pub trait NewSeeded: SeedableRng { +pub trait NewRng: SeedableRng { /// Creates a new instance, automatically seeded with fresh entropy. /// /// Normally this will use `OsRng`, but if that fails `JitterRng` will be @@ -821,7 +821,7 @@ pub trait NewSeeded: SeedableRng { } #[cfg(feature="std")] -impl NewSeeded for R { +impl NewRng for R { fn new() -> Result { // Note: error handling would be easier with try/catch blocks fn new_os() -> Result { diff --git a/src/reseeding.rs b/src/reseeding.rs index 1fdb5dc20b8..e119e1c0350 100644 --- a/src/reseeding.rs +++ b/src/reseeding.rs @@ -13,7 +13,7 @@ use {Rng, Error}; #[cfg(feature="std")] -use NewSeeded; +use NewRng; /// A wrapper around any RNG which reseeds the underlying RNG after it /// has generated a certain number of random bytes. @@ -93,13 +93,13 @@ pub trait Reseeder { fn reseed(&mut self, rng: &mut R) -> Result<(), Error>; } -/// Reseed an RNG using `NewSeeded` to replace the current instance. +/// Reseed an RNG using `NewRng` to replace the current instance. #[cfg(feature="std")] #[derive(Debug)] pub struct ReseedWithNew; #[cfg(feature="std")] -impl Reseeder for ReseedWithNew { +impl Reseeder for ReseedWithNew { fn reseed(&mut self, rng: &mut R) -> Result<(), Error> { R::new().map(|result| *rng = result) } From 34239fa520fa2c03052118d22cfef84655dbc65d Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Sat, 20 Jan 2018 16:30:13 +0100 Subject: [PATCH 8/8] Documentation improvements --- src/lib.rs | 55 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 31 insertions(+), 24 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 27214ae5f24..2af8c34f5f1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -276,7 +276,7 @@ use prng::Isaac64Rng as IsaacWordRng; use distributions::{Range, IndependentSample}; use distributions::range::SampleRange; -#[cfg(feature="std")]use reseeding::{ReseedingRng, ReseedWithNew}; +#[cfg(feature="std")] use reseeding::{ReseedingRng, ReseedWithNew}; // public modules pub mod distributions; @@ -736,12 +736,12 @@ impl<'a, R: Rng> Iterator for AsciiGenerator<'a, R> { /// /// Each pseudo-random number generator (PRNG) should implement this. pub trait SeedableRng: Sized { - /// Seed type, which is restricted to `u8` arrays with a length of - /// 4, 8, 12, 16, 24 and 32. + /// Seed type, which is restricted to types mutably-dereferencable as `u8` + /// arrays (we recommend `[u8; N]` for some `N`). /// - /// It is recommended to seed PRNG's with a seed of more than circa - /// 100 bits, which means an array of `[u8; 12]` or greater to avoid picking - /// RNG's with partially overlapping periods. + /// It is recommended to seed PRNGs with a seed of at least circa 100 bits, + /// which means an array of `[u8; 12]` or greater to avoid picking RNGs with + /// partially overlapping periods. /// /// For cryptographic RNG's a seed of 256 bits is recommended, `[u8; 32]`. type Seed: Sized + Default + AsMut<[u8]>; @@ -764,28 +764,32 @@ pub trait SeedableRng: Sized { /// Create a new PRNG seeded from another `Rng`. /// - /// This is the recommended way to initialize PRNGs. See the `NewRng` - /// trait that provides a convenient `new` method using `from_rng` and a - /// good entropy source. + /// This is the recommended way to initialize PRNGs. The [`NewRng`] trait + /// provides a convenient new method based on `from_rng`. /// - /// It is recommended to use a good source of randomness to initialize the - /// PRNG. Otherwise small PRNG's could show statistical bias in the first - /// couple of results, and possibly not use their entire period well. - /// Cryptographic PRNG's can be less secure or even insecure when they are - /// seeded from a non-cryptographic PRNG. + /// It is important to use a good source of randomness to initialize the + /// PRNG. Cryptographic PRNG may be rendered insecure when seeded from a + /// non-cryptographic PRNG or with insufficient entropy. + /// Many non-cryptographic PRNGs will show statistical bias in their first + /// results if their seed numbers are small or if there is a simple pattern + /// between them. /// - /// Examples of good RNG's for seeding are entropy sources like `OsRng` and - /// `JitterRng`. Also cryptographically secure PRNG's (like `thread_rng`) - /// can be used without hesitation. + /// Prefer to seed from a strong external entropy source like [`OsRng`] or + /// from a cryptographic PRNG; if creating a new generator for cryptography + /// you *must* do this. /// - /// Seeding a small PRNG from another small PRNG is be possible, but + /// Seeding a small PRNG from another small PRNG is possible, but /// something to be careful with. An extreme example of how this can go /// wrong is seeding an Xorshift RNG from another Xorshift RNG. That will - /// effectively clone the generator. + /// effectively clone the generator. In general seeding from a generator + /// which is hard to predict is probably okay. /// /// PRNG implementations are allowed to assume that a good RNG is provided /// for seeding, and that it is cryptographically secure when appropriate. /// There are no reproducibility requirements like endianness conversion. + /// + /// [`NewRng`]: trait.NewRng.html + /// [`OsRng`]: os/struct.OsRng.html fn from_rng(mut rng: R) -> Result { let mut seed = Self::Seed::default(); rng.try_fill_bytes(seed.as_mut())?; @@ -794,13 +798,14 @@ pub trait SeedableRng: Sized { } -/// Seeding mechanism for PRNGs, providing a `new` function. -/// This is the recommended way to create (pseudo) random number generators, -/// unless a deterministic seed is desired (in which case -/// `SeedableRng::from_seed` should be used). +/// A convenient way to seed new algorithmic generators, otherwise known as +/// pseudo-random number generators (PRNGs). +/// +/// This is the recommended way to create PRNGs, unless a deterministic seed is +/// desired (in which case `SeedableRng::from_seed` should be used). /// /// Note: this trait is automatically implemented for any PRNG implementing -/// `SeedableRng` and is not intended to be implemented by users. +/// [`SeedableRng`] and is not intended to be implemented by users. /// /// ## Example /// @@ -810,6 +815,8 @@ pub trait SeedableRng: Sized { /// let mut rng = StdRng::new().unwrap(); /// println!("Random die roll: {}", rng.gen_range(1, 7)); /// ``` +/// +/// [`SeedableRng`]: trait.SeedableRng.html #[cfg(feature="std")] pub trait NewRng: SeedableRng { /// Creates a new instance, automatically seeded with fresh entropy.