From 7a07c36a74a59b29a605d8457d1301aaee987df4 Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Thu, 18 Jan 2018 08:47:40 +0100 Subject: [PATCH] Rename to EntropyRng and make new infallible --- src/lib.rs | 128 +++++++++++++++++++++++++++++------------------------ 1 file changed, 70 insertions(+), 58 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 6a89205a06..7ac9186248 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -830,8 +830,7 @@ pub trait NewRng: SeedableRng { #[cfg(feature="std")] impl NewRng for R { fn new() -> Result { - let mut source = EntropySource::new()?; - R::from_rng(&mut source) + R::from_rng(EntropyRng::new()) } } @@ -929,15 +928,14 @@ 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 mut entropy_source = EntropySource::new() - .expect("could not initialize thread_rng"); + 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, @@ -985,49 +983,42 @@ impl Rng for ThreadRng { /// An RNG provided specifically for seeding PRNG's. /// -/// `EntropySource` 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. Occasionally it will then check if -/// `OsRng` is still not available, and switch back if possible. +/// `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. Occasionally 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 EntropySource { - rng: EntropySourceInner, +pub struct EntropyRng { + rng: EntropySource, counter: u32, } #[cfg(feature="std")] #[derive(Debug)] -enum EntropySourceInner { +enum EntropySource { Os(OsRng), Jitter(JitterRng), + None, } #[cfg(feature="std")] -impl EntropySource { - pub fn new() -> Result { - match OsRng::new() { - Ok(r) => - Ok(EntropySource { rng: EntropySourceInner::Os(r), - counter: 0u32 }), - Err(e1) => { - match JitterRng::new() { - Ok(r) => - Ok(EntropySource { rng: EntropySourceInner::Jitter(r), - counter: 0 }), - Err(_) => - Err(Error::with_cause( - ErrorKind::Unavailable, - "Both OS and Jitter entropy sources are unavailable", - e1)) - } - } - } +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, counter: 0u32 } } } #[cfg(feature="std")] -impl Rng for EntropySource { +impl Rng for EntropyRng { fn next_u32(&mut self) -> u32 { impls::next_u32_via_fill(self) } @@ -1041,45 +1032,66 @@ impl Rng for EntropySource { } 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; - let mut result; match self.rng { - EntropySourceInner::Os(ref mut rng) => { - result = rng.try_fill_bytes(dest); - if result.is_err() { - // Fall back to JitterRng. - let mut rng2_result = JitterRng::new(); - if let Ok(mut rng2) = rng2_result { - result = rng2.try_fill_bytes(dest); // Can't fail - switch_rng = Some(EntropySourceInner::Jitter(rng2)); + EntropySource::None => { + let os_rng_result = try_os_new(dest); + match os_rng_result { + Ok(os_rng) => { + switch_rng = Some(EntropySource::Os(os_rng)); } - } - } - EntropySourceInner::Jitter(ref mut rng) => { - if self.counter < 8 { - result = rng.try_fill_bytes(dest); // use JitterRng - self.counter = (self.counter + 1) % 8; - } else { - // Try if OsRng is still unavailable - let os_rng_result = OsRng::new(); - if let Ok(mut os_rng) = os_rng_result { - result = os_rng.try_fill_bytes(dest); - if result.is_ok() { - switch_rng = Some(EntropySourceInner::Os(os_rng)); + Err(os_rng_error) => { + if let Ok(jitter_rng) = try_jitter_new(dest) { + switch_rng = Some(EntropySource::Jitter(jitter_rng)); } else { - result = rng.try_fill_bytes(dest); // use JitterRng + return Err(os_rng_error); } + } + } + } + EntropySource::Os(ref mut rng) => { + let os_rng_result = rng.try_fill_bytes(dest); + if os_rng_result.is_err() { + if let Ok(jitter_rng) = try_jitter_new(dest) { + switch_rng = Some(EntropySource::Jitter(jitter_rng)); } else { - result = rng.try_fill_bytes(dest); // use JitterRng + return os_rng_result; } } } + EntropySource::Jitter(ref mut rng) => { + if self.counter >= 8 { + if let Ok(os_rng) = try_os_new(dest) { + switch_rng = Some(EntropySource::Os(os_rng)); + } else { + self.counter = (self.counter + 1) % 8; + return rng.try_fill_bytes(dest); // use JitterRng + } + } else { + self.counter = (self.counter + 1) % 8; + return rng.try_fill_bytes(dest); // use JitterRng + } + } } if let Some(rng) = switch_rng { self.rng = rng; self.counter = 0; } - result + Ok(()) } }