| // Copyright 2018 Developers of the Rand project. |
| // |
| // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or |
| // https://www.apache.org/licenses/LICENSE-2.0> or the MIT license |
| // <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your |
| // option. This file may not be copied, modified, or distributed |
| // except according to those terms. |
| |
| //! Entropy generator, or wrapper around external generators |
| |
| use rand_core::{RngCore, CryptoRng, Error, ErrorKind, impls}; |
| #[allow(unused)] |
| use rngs; |
| |
| /// An interface returning random data from external source(s), provided |
| /// specifically for securely seeding algorithmic generators (PRNGs). |
| /// |
| /// Where possible, `EntropyRng` retrieves random data from the operating |
| /// system's interface for random numbers ([`OsRng`]); if that fails it will |
| /// fall back to the [`JitterRng`] entropy collector. In the latter case it will |
| /// still try to use [`OsRng`] on the next usage. |
| /// |
| /// If no secure source of entropy is available `EntropyRng` will panic on use; |
| /// i.e. it should never output predictable data. |
| /// |
| /// This is either a little slow ([`OsRng`] requires a system call) or extremely |
| /// slow ([`JitterRng`] must use significant CPU time to generate sufficient |
| /// jitter); for better performance it is common to seed a local PRNG from |
| /// external entropy then primarily use the local PRNG ([`thread_rng`] is |
| /// provided as a convenient, local, automatically-seeded CSPRNG). |
| /// |
| /// # Panics |
| /// |
| /// On most systems, like Windows, Linux, macOS and *BSD on common hardware, it |
| /// is highly unlikely for both [`OsRng`] and [`JitterRng`] to fail. But on |
| /// combinations like webassembly without Emscripten or stdweb both sources are |
| /// unavailable. If both sources fail, only [`try_fill_bytes`] is able to |
| /// report the error, and only the one from `OsRng`. The other [`RngCore`] |
| /// methods will panic in case of an error. |
| /// |
| /// [`OsRng`]: struct.OsRng.html |
| /// [`JitterRng`]: jitter/struct.JitterRng.html |
| /// [`thread_rng`]: ../fn.thread_rng.html |
| /// [`RngCore`]: ../trait.RngCore.html |
| /// [`try_fill_bytes`]: ../trait.RngCore.html#method.tymethod.try_fill_bytes |
| #[derive(Debug)] |
| pub struct EntropyRng { |
| source: Source, |
| } |
| |
| #[derive(Debug)] |
| enum Source { |
| Os(Os), |
| Custom(Custom), |
| Jitter(Jitter), |
| None, |
| } |
| |
| 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 { source: Source::None } |
| } |
| } |
| |
| impl Default for EntropyRng { |
| fn default() -> Self { |
| EntropyRng::new() |
| } |
| } |
| |
| impl RngCore 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_or_else(|err| |
| panic!("all entropy sources failed; first error: {}", err)) |
| } |
| |
| fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { |
| let mut reported_error = None; |
| |
| if let Source::Os(ref mut os_rng) = self.source { |
| match os_rng.fill(dest) { |
| Ok(()) => return Ok(()), |
| Err(err) => { |
| warn!("EntropyRng: OsRng failed \ |
| [trying other entropy sources]: {}", err); |
| reported_error = Some(err); |
| }, |
| } |
| } else if Os::is_supported() { |
| match Os::new_and_fill(dest) { |
| Ok(os_rng) => { |
| debug!("EntropyRng: using OsRng"); |
| self.source = Source::Os(os_rng); |
| return Ok(()); |
| }, |
| Err(err) => { reported_error = reported_error.or(Some(err)) }, |
| } |
| } |
| |
| if let Source::Custom(ref mut rng) = self.source { |
| match rng.fill(dest) { |
| Ok(()) => return Ok(()), |
| Err(err) => { |
| warn!("EntropyRng: custom entropy source failed \ |
| [trying other entropy sources]: {}", err); |
| reported_error = Some(err); |
| }, |
| } |
| } else if Custom::is_supported() { |
| match Custom::new_and_fill(dest) { |
| Ok(custom) => { |
| debug!("EntropyRng: using custom entropy source"); |
| self.source = Source::Custom(custom); |
| return Ok(()); |
| }, |
| Err(err) => { reported_error = reported_error.or(Some(err)) }, |
| } |
| } |
| |
| if let Source::Jitter(ref mut jitter_rng) = self.source { |
| match jitter_rng.fill(dest) { |
| Ok(()) => return Ok(()), |
| Err(err) => { |
| warn!("EntropyRng: JitterRng failed: {}", err); |
| reported_error = Some(err); |
| }, |
| } |
| } else if Jitter::is_supported() { |
| match Jitter::new_and_fill(dest) { |
| Ok(jitter_rng) => { |
| debug!("EntropyRng: using JitterRng"); |
| self.source = Source::Jitter(jitter_rng); |
| return Ok(()); |
| }, |
| Err(err) => { reported_error = reported_error.or(Some(err)) }, |
| } |
| } |
| |
| if let Some(err) = reported_error { |
| Err(Error::with_cause(ErrorKind::Unavailable, |
| "All entropy sources failed", |
| err)) |
| } else { |
| Err(Error::new(ErrorKind::Unavailable, |
| "No entropy sources available")) |
| } |
| } |
| } |
| |
| impl CryptoRng for EntropyRng {} |
| |
| |
| |
| trait EntropySource { |
| fn new_and_fill(dest: &mut [u8]) -> Result<Self, Error> |
| where Self: Sized; |
| |
| fn fill(&mut self, dest: &mut [u8]) -> Result<(), Error>; |
| |
| fn is_supported() -> bool { true } |
| } |
| |
| #[allow(unused)] |
| #[derive(Clone, Debug)] |
| struct NoSource; |
| |
| #[allow(unused)] |
| impl EntropySource for NoSource { |
| fn new_and_fill(dest: &mut [u8]) -> Result<Self, Error> { |
| Err(Error::new(ErrorKind::Unavailable, "Source not supported")) |
| } |
| |
| fn fill(&mut self, dest: &mut [u8]) -> Result<(), Error> { |
| unreachable!() |
| } |
| |
| fn is_supported() -> bool { false } |
| } |
| |
| |
| #[cfg(all(feature="std", |
| any(target_os = "linux", target_os = "android", |
| target_os = "netbsd", |
| target_os = "dragonfly", |
| target_os = "haiku", |
| target_os = "emscripten", |
| target_os = "solaris", |
| target_os = "cloudabi", |
| target_os = "macos", target_os = "ios", |
| target_os = "freebsd", |
| target_os = "openbsd", target_os = "bitrig", |
| target_os = "redox", |
| target_os = "fuchsia", |
| windows, |
| all(target_arch = "wasm32", feature = "stdweb"), |
| all(target_arch = "wasm32", feature = "wasm-bindgen"), |
| )))] |
| #[derive(Clone, Debug)] |
| pub struct Os(rngs::OsRng); |
| |
| #[cfg(all(feature="std", |
| any(target_os = "linux", target_os = "android", |
| target_os = "netbsd", |
| target_os = "dragonfly", |
| target_os = "haiku", |
| target_os = "emscripten", |
| target_os = "solaris", |
| target_os = "cloudabi", |
| target_os = "macos", target_os = "ios", |
| target_os = "freebsd", |
| target_os = "openbsd", target_os = "bitrig", |
| target_os = "redox", |
| target_os = "fuchsia", |
| windows, |
| all(target_arch = "wasm32", feature = "stdweb"), |
| all(target_arch = "wasm32", feature = "wasm-bindgen"), |
| )))] |
| impl EntropySource for Os { |
| fn new_and_fill(dest: &mut [u8]) -> Result<Self, Error> { |
| let mut rng = rngs::OsRng::new()?; |
| rng.try_fill_bytes(dest)?; |
| Ok(Os(rng)) |
| } |
| |
| fn fill(&mut self, dest: &mut [u8]) -> Result<(), Error> { |
| self.0.try_fill_bytes(dest) |
| } |
| } |
| |
| #[cfg(not(all(feature="std", |
| any(target_os = "linux", target_os = "android", |
| target_os = "netbsd", |
| target_os = "dragonfly", |
| target_os = "haiku", |
| target_os = "emscripten", |
| target_os = "solaris", |
| target_os = "cloudabi", |
| target_os = "macos", target_os = "ios", |
| target_os = "freebsd", |
| target_os = "openbsd", target_os = "bitrig", |
| target_os = "redox", |
| target_os = "fuchsia", |
| windows, |
| all(target_arch = "wasm32", feature = "stdweb"), |
| all(target_arch = "wasm32", feature = "wasm-bindgen"), |
| ))))] |
| type Os = NoSource; |
| |
| |
| type Custom = NoSource; |
| |
| |
| #[cfg(not(target_arch = "wasm32"))] |
| #[derive(Clone, Debug)] |
| pub struct Jitter(rngs::JitterRng); |
| |
| #[cfg(not(target_arch = "wasm32"))] |
| impl EntropySource for Jitter { |
| fn new_and_fill(dest: &mut [u8]) -> Result<Self, Error> { |
| let mut rng = rngs::JitterRng::new()?; |
| rng.try_fill_bytes(dest)?; |
| Ok(Jitter(rng)) |
| } |
| |
| fn fill(&mut self, dest: &mut [u8]) -> Result<(), Error> { |
| self.0.try_fill_bytes(dest) |
| } |
| } |
| |
| #[cfg(target_arch = "wasm32")] |
| type Jitter = NoSource; |
| |
| |
| #[cfg(test)] |
| mod test { |
| use super::*; |
| |
| #[test] |
| fn test_entropy() { |
| let mut rng = EntropyRng::new(); |
| let n = (rng.next_u32() ^ rng.next_u32()).count_ones(); |
| assert!(n >= 2); // p(failure) approx 1e-7 |
| } |
| } |