diff --git a/src/jitter.rs b/src/jitter.rs index a3914834b5d..3bf7963aac6 100644 --- a/src/jitter.rs +++ b/src/jitter.rs @@ -742,7 +742,12 @@ impl Rng for JitterRng { } fn fill_bytes(&mut self, dest: &mut [u8]) { - impls::fill_bytes_via_u64(self, dest) + // Fill using `next_u32`. This is faster for filling small slices (four + // bytes or less), while the overhead is negligible. + // + // This is done especially for wrappers that implement `next_u32` + // themselves via `fill_bytes`. + impls::fill_bytes_via_u32(self, dest) } } diff --git a/src/lib.rs b/src/lib.rs index fbec03b23f5..23b417b1a80 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -287,7 +287,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; // public modules pub mod distributions; @@ -843,29 +843,7 @@ pub trait NewRng: SeedableRng { #[cfg(feature="std")] impl NewRng 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) - } - - trace!("Seeding new RNG"); - new_os().or_else(|e1| { - warn!("OsRng failed [falling back to JitterRng]: {:?}", e1); - new_jitter().map_err(|_e2| { - warn!("JitterRng failed: {:?}", _e2); - // 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) - }) - }) + R::from_rng(EntropyRng::new()) } } @@ -963,20 +941,19 @@ pub fn weak_rng() -> XorShiftRng { #[cfg(feature="std")] #[derive(Clone, Debug)] pub struct ThreadRng { - rng: Rc>>, + rng: Rc>>, } #[cfg(feature="std")] thread_local!( - static THREAD_RNG_KEY: Rc>> = { + static THREAD_RNG_KEY: Rc>> = { const THREAD_RNG_RESEED_THRESHOLD: u64 = 32_768; - let r = match StdRng::new() { - Ok(r) => r, - Err(e) => panic!("could not initialize thread_rng: {:?}", e) - }; + let mut entropy_source = EntropyRng::new(); + let r = StdRng::from_rng(&mut entropy_source) + .expect("could not initialize thread_rng"); let rng = ReseedingRng::new(r, THREAD_RNG_RESEED_THRESHOLD, - ReseedWithNew); + entropy_source); Rc::new(RefCell::new(rng)) } ); @@ -1017,6 +994,127 @@ impl Rng for ThreadRng { } } +/// An RNG provided specifically for seeding PRNG's. +/// +/// `EntropyRng` uses the interface for random numbers provided by the operating +/// system ([`OsRng`]). If that returns an error, it will fall back to the +/// [`JitterRng`] entropy collector. Every time it will then check if `OsRng` +/// is still not available, and switch back if possible. +/// +/// [`OsRng`]: os/struct.OsRng.html +/// [`JitterRng`]: jitter/struct.JitterRng.html +#[cfg(feature="std")] +#[derive(Debug)] +pub struct EntropyRng { + rng: EntropySource, +} + +#[cfg(feature="std")] +#[derive(Debug)] +enum EntropySource { + Os(OsRng), + Jitter(JitterRng), + None, +} + +#[cfg(feature="std")] +impl EntropyRng { + /// Create a new `EntropyRng`. + /// + /// This method will do no system calls or other initialization routines, + /// those are done on first use. This is done to make `new` infallible, + /// and `try_fill_bytes` the only place to report errors. + pub fn new() -> Self { + EntropyRng { rng: EntropySource::None } + } +} + +#[cfg(feature="std")] +impl Rng for EntropyRng { + fn next_u32(&mut self) -> u32 { + impls::next_u32_via_fill(self) + } + + fn next_u64(&mut self) -> u64 { + impls::next_u64_via_fill(self) + } + + fn fill_bytes(&mut self, dest: &mut [u8]) { + self.try_fill_bytes(dest).unwrap(); + } + + fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { + fn try_os_new(dest: &mut [u8]) -> Result + { + let mut rng = OsRng::new()?; + rng.try_fill_bytes(dest)?; + Ok(rng) + } + + fn try_jitter_new(dest: &mut [u8]) -> Result + { + let mut rng = JitterRng::new()?; + rng.try_fill_bytes(dest)?; + Ok(rng) + } + + let mut switch_rng = None; + match self.rng { + EntropySource::None => { + let os_rng_result = try_os_new(dest); + match os_rng_result { + Ok(os_rng) => { + switch_rng = Some(EntropySource::Os(os_rng)); + } + Err(os_rng_error) => { + warn!("EntropyRng: OsRng failed [falling back to JitterRng]: {}", + os_rng_error); + match try_jitter_new(dest) { + Ok(jitter_rng) => { + switch_rng = Some(EntropySource::Jitter(jitter_rng)); + } + Err(_jitter_error) => { + warn!("EntropyRng: JitterRng failed: {}", + _jitter_error); + return Err(os_rng_error); + } + } + } + } + } + EntropySource::Os(ref mut rng) => { + let os_rng_result = rng.try_fill_bytes(dest); + if let Err(os_rng_error) = os_rng_result { + warn!("EntropyRng: OsRng failed [falling back to JitterRng]: {}", + os_rng_error); + match try_jitter_new(dest) { + Ok(jitter_rng) => { + switch_rng = Some(EntropySource::Jitter(jitter_rng)); + } + Err(_jitter_error) => { + warn!("EntropyRng: JitterRng failed: {}", + _jitter_error); + return Err(os_rng_error); + } + } + } + } + EntropySource::Jitter(ref mut rng) => { + if let Ok(os_rng) = try_os_new(dest) { + info!("EntropyRng: OsRng available [switching back from JitterRng]"); + switch_rng = Some(EntropySource::Os(os_rng)); + } else { + return rng.try_fill_bytes(dest); // use JitterRng + } + } + } + if let Some(rng) = switch_rng { + self.rng = rng; + } + Ok(()) + } +} + /// Generates a random value using the thread-local random number generator. /// /// `random()` can generate various types of random things, and so may require diff --git a/src/reseeding.rs b/src/reseeding.rs index b9a9b7a2f56..aab46f44b91 100644 --- a/src/reseeding.rs +++ b/src/reseeding.rs @@ -11,9 +11,7 @@ //! A wrapper around another RNG that reseeds it after it //! generates a certain number of random bytes. -use {Rng, Error}; -#[cfg(feature="std")] -use NewRng; +use {Rng, SeedableRng, Error}; /// A wrapper around any RNG which reseeds the underlying RNG after it /// has generated a certain number of random bytes. @@ -23,10 +21,10 @@ pub struct ReseedingRng { generation_threshold: u64, bytes_generated: u64, /// Controls the behaviour when reseeding the RNG. - pub reseeder: Rsdr, + reseeder: Rsdr, } -impl> ReseedingRng { +impl ReseedingRng { /// Create a new `ReseedingRng` with the given parameters. /// /// # Arguments @@ -48,14 +46,14 @@ impl> ReseedingRng { pub fn reseed_if_necessary(&mut self) { if self.bytes_generated >= self.generation_threshold { trace!("Reseeding RNG after {} bytes", self.bytes_generated); - self.reseeder.reseed(&mut self.rng).unwrap(); + R::from_rng(&mut self.reseeder).map(|result| self.rng = result).unwrap(); self.bytes_generated = 0; } } } -impl> Rng for ReseedingRng { +impl Rng for ReseedingRng { fn next_u32(&mut self) -> u32 { self.reseed_if_necessary(); self.bytes_generated += 4; @@ -81,35 +79,10 @@ impl> Rng for ReseedingRng { } } -/// Something that can be used to reseed an RNG via `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 { - /// Reseed the given RNG. - /// - /// 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 `NewRng` to replace the current instance. -#[cfg(feature="std")] -#[derive(Debug)] -pub struct ReseedWithNew; - -#[cfg(feature="std")] -impl Reseeder for ReseedWithNew { - fn reseed(&mut self, rng: &mut R) -> Result<(), Error> { - R::new().map(|result| *rng = result) - } -} - #[cfg(test)] mod test { use {impls, le}; - use super::{ReseedingRng, Reseeder}; + use super::{ReseedingRng}; use {SeedableRng, Rng, Error}; struct Counter { @@ -140,17 +113,20 @@ mod test { } #[derive(Debug, Clone)] - struct ReseedCounter; - impl Reseeder for ReseedCounter { - fn reseed(&mut self, rng: &mut Counter) -> Result<(), Error> { - *rng = Counter { i: 0 }; + struct ResetCounter; + impl Rng for ResetCounter { + fn next_u32(&mut self) -> u32 { unimplemented!() } + fn next_u64(&mut self) -> u64 { unimplemented!() } + fn fill_bytes(&mut self, _dest: &mut [u8]) { unimplemented!() } + fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { + for i in dest.iter_mut() { *i = 0; } Ok(()) } } #[test] fn test_reseeding() { - let mut rs = ReseedingRng::new(Counter {i:0}, 400, ReseedCounter); + let mut rs = ReseedingRng::new(Counter {i:0}, 400, ResetCounter); let mut i = 0; for _ in 0..1000 {