Merge pull request #512 from pitdicker/complicate_entropy_rng_0.5
Complicate entropy rng 0.5
diff --git a/src/lib.rs b/src/lib.rs
index 03d505f..4324c98 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -249,11 +249,16 @@
extern crate rand_core;
#[cfg(feature = "log")] #[macro_use] extern crate log;
+#[allow(unused)]
#[cfg(not(feature = "log"))] macro_rules! trace { ($($x:tt)*) => () }
+#[allow(unused)]
#[cfg(not(feature = "log"))] macro_rules! debug { ($($x:tt)*) => () }
-#[cfg(all(feature="std", not(feature = "log")))] macro_rules! info { ($($x:tt)*) => () }
+#[allow(unused)]
+#[cfg(not(feature = "log"))] macro_rules! info { ($($x:tt)*) => () }
+#[allow(unused)]
#[cfg(not(feature = "log"))] macro_rules! warn { ($($x:tt)*) => () }
-#[cfg(all(feature="std", not(feature = "log")))] macro_rules! error { ($($x:tt)*) => () }
+#[allow(unused)]
+#[cfg(not(feature = "log"))] macro_rules! error { ($($x:tt)*) => () }
// Re-exports from rand_core
@@ -276,13 +281,60 @@
#[cfg(feature="std")] #[doc(hidden)] pub use rngs::adapter::read;
#[doc(hidden)] pub use rngs::adapter::ReseedingRng;
-#[doc(hidden)] pub use rngs::jitter;
-#[cfg(feature="std")] #[doc(hidden)] pub use rngs::{os, EntropyRng, OsRng};
+#[allow(deprecated)]
+#[cfg(feature="std")] #[doc(hidden)] pub use rngs::EntropyRng;
+
+#[allow(deprecated)]
+#[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")
+)))]
+#[doc(hidden)]
+pub use rngs::OsRng;
#[doc(hidden)] pub use prng::{ChaChaRng, IsaacRng, Isaac64Rng, XorShiftRng};
#[doc(hidden)] pub use rngs::StdRng;
+#[allow(deprecated)]
+#[doc(hidden)]
+pub mod jitter {
+ pub use rngs::{JitterRng, TimerError};
+}
+#[allow(deprecated)]
+#[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")
+)))]
+#[doc(hidden)]
+pub mod os {
+ pub use rngs::OsRng;
+}
+#[allow(deprecated)]
#[doc(hidden)]
pub mod chacha {
//! The ChaCha random number generator.
diff --git a/src/rngs/entropy.rs b/src/rngs/entropy.rs
index e260af9..b8f4be7 100644
--- a/src/rngs/entropy.rs
+++ b/src/rngs/entropy.rs
@@ -10,8 +10,9 @@
//! Entropy generator, or wrapper around external generators
-use rand_core::{RngCore, CryptoRng, Error, impls};
-use rngs::{OsRng, JitterRng};
+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).
@@ -46,13 +47,14 @@
/// [`try_fill_bytes`]: ../trait.RngCore.html#method.tymethod.try_fill_bytes
#[derive(Debug)]
pub struct EntropyRng {
- rng: EntropySource,
+ source: Source,
}
#[derive(Debug)]
-enum EntropySource {
- Os(OsRng),
- Jitter(JitterRng),
+enum Source {
+ Os(Os),
+ Custom(Custom),
+ Jitter(Jitter),
None,
}
@@ -63,7 +65,7 @@
/// 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 }
+ EntropyRng { source: Source::None }
}
}
@@ -88,82 +90,199 @@
}
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
- fn try_os_new(dest: &mut [u8]) -> Result<OsRng, Error>
- {
- let mut rng = OsRng::new()?;
- rng.try_fill_bytes(dest)?;
- Ok(rng)
- }
+ let mut reported_error = None;
- fn try_jitter_new(dest: &mut [u8]) -> Result<JitterRng, Error>
- {
- 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) => {
- debug!("EntropyRng: using OsRng");
- 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) => {
- debug!("EntropyRng: using JitterRng");
- switch_rng = Some(EntropySource::Jitter(jitter_rng));
- }
- Err(_jitter_error) => {
- warn!("EntropyRng: JitterRng failed: {}",
- _jitter_error);
- return Err(os_rng_error);
- }
- }
- }
- }
+ 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);
+ },
}
- 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) => {
- debug!("EntropyRng: using JitterRng");
- 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) {
+ } else if Os::is_supported() {
+ match Os::new_and_fill(dest) {
+ Ok(os_rng) => {
debug!("EntropyRng: using OsRng");
- switch_rng = Some(EntropySource::Os(os_rng));
- } else {
- return rng.try_fill_bytes(dest); // use JitterRng
- }
+ self.source = Source::Os(os_rng);
+ return Ok(());
+ },
+ Err(err) => { reported_error = reported_error.or(Some(err)) },
}
}
- if let Some(rng) = switch_rng {
- self.rng = rng;
+
+ 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)) },
+ }
}
- Ok(())
+
+ 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")
+)))]
+#[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")
+)))]
+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")
+))))]
+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::*;
diff --git a/src/rngs/jitter.rs b/src/rngs/jitter.rs
index a31a1df..311682c 100644
--- a/src/rngs/jitter.rs
+++ b/src/rngs/jitter.rs
@@ -24,7 +24,7 @@
use rand_core::{RngCore, CryptoRng, Error, ErrorKind, impls};
use core::{fmt, mem, ptr};
-#[cfg(feature="std")]
+#[cfg(all(feature="std", not(target_arch = "wasm32")))]
use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};
const MEMORY_BLOCKS: usize = 64;
@@ -54,6 +54,10 @@
/// This implementation is based on
/// [Jitterentropy](http://www.chronox.de/jent.html) version 2.1.0.
///
+/// Note: There is no accurate timer available on Wasm platforms, to help
+/// prevent fingerprinting or timing side-channel attacks. Therefore
+/// [`JitterRng::new()`] is not available on Wasm.
+///
/// # Quality testing
///
/// [`JitterRng::new()`] has build-in, but limited, quality testing, however
@@ -268,7 +272,7 @@
}
// Initialise to zero; must be positive
-#[cfg(feature="std")]
+#[cfg(all(feature="std", not(target_arch = "wasm32")))]
static JITTER_ROUNDS: AtomicUsize = ATOMIC_USIZE_INIT;
impl JitterRng {
@@ -279,7 +283,7 @@
/// During initialization CPU execution timing jitter is measured a few
/// hundred times. If this does not pass basic quality tests, an error is
/// returned. The test result is cached to make subsequent calls faster.
- #[cfg(feature="std")]
+ #[cfg(all(feature="std", not(target_arch = "wasm32")))]
pub fn new() -> Result<JitterRng, TimerError> {
let mut state = JitterRng::new_with_timer(platform::get_nstime);
let mut rounds = JITTER_ROUNDS.load(Ordering::Relaxed) as u8;
@@ -607,7 +611,6 @@
/// of the failure will be returned.
///
/// [`TimerError`]: enum.TimerError.html
- #[cfg(not(all(target_arch = "wasm32", not(target_os = "emscripten"))))]
pub fn test_timer(&mut self) -> Result<u8, TimerError> {
debug!("JitterRng: testing timer ...");
// We could add a check for system capabilities such as `clock_getres`
@@ -744,10 +747,6 @@
Ok(log2_lookup[delta_average as usize])
}
}
- #[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))]
- pub fn test_timer(&mut self) -> Result<u8, TimerError> {
- return Err(TimerError::NoTimer);
- }
/// Statistical test: return the timer delta of one normal run of the
/// `JitterRng` entropy collector.
@@ -763,22 +762,22 @@
///
/// See [Quality testing](struct.JitterRng.html#quality-testing) on how to
/// use `timer_stats` to test the quality of `JitterRng`.
- #[cfg(feature="std")]
pub fn timer_stats(&mut self, var_rounds: bool) -> i64 {
let mut mem = [0; MEMORY_SIZE];
- let time = platform::get_nstime();
+ let time = (self.timer)();
self.memaccess(&mut mem, var_rounds);
self.lfsr_time(time, var_rounds);
- let time2 = platform::get_nstime();
+ let time2 = (self.timer)();
time2.wrapping_sub(time) as i64
}
}
#[cfg(feature="std")]
mod platform {
- #[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "windows",
- all(target_arch = "wasm32", not(target_os = "emscripten")))))]
+ #[cfg(not(any(target_os = "macos", target_os = "ios",
+ target_os = "windows",
+ target_arch = "wasm32")))]
pub fn get_nstime() -> u64 {
use std::time::{SystemTime, UNIX_EPOCH};
@@ -811,11 +810,6 @@
*t.QuadPart() as u64
}
}
-
- #[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))]
- pub fn get_nstime() -> u64 {
- unreachable!()
- }
}
// A function that is opaque to the optimizer to assist in avoiding dead-code
@@ -866,7 +860,7 @@
mod test_jitter_init {
use jitter::JitterRng;
- #[cfg(feature="std")]
+ #[cfg(all(feature="std", not(target_arch = "wasm32")))]
#[test]
fn test_jitter_init() {
use RngCore;
diff --git a/src/rngs/mod.rs b/src/rngs/mod.rs
index 3e5c3fa..acc207d 100644
--- a/src/rngs/mod.rs
+++ b/src/rngs/mod.rs
@@ -169,7 +169,6 @@
#[doc(hidden)] pub mod jitter;
pub mod mock; // Public so we don't export `StepRng` directly, making it a bit
// more clear it is intended for testing.
-#[cfg(feature="std")] #[doc(hidden)] pub mod os;
mod small;
mod std;
#[cfg(feature="std")] pub(crate) mod thread;
@@ -177,8 +176,43 @@
pub use self::jitter::{JitterRng, TimerError};
#[cfg(feature="std")] pub use self::entropy::EntropyRng;
-#[cfg(feature="std")] pub use self::os::OsRng;
pub use self::small::SmallRng;
pub use self::std::StdRng;
#[cfg(feature="std")] pub use self::thread::ThreadRng;
+
+#[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")
+)))]
+mod os;
+
+#[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")
+)))]
+pub use self::os::OsRng;