From aed3bb4b075cf302f9093ae31c9b45f5e5305281 Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Thu, 14 Jun 2018 08:24:24 +0200 Subject: [PATCH 1/4] Allow unused logging macros --- src/lib.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 38354853f37..fb399d0e68b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -249,11 +249,16 @@ extern crate stdweb; 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 From 28b27298caa773a73a2298a7e7cff3b43b72d30d Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Thu, 14 Jun 2018 19:44:09 +0200 Subject: [PATCH 2/4] Make JitterRng::new and dummy timer unavailable on WASM --- src/deprecated.rs | 2 +- src/rngs/jitter.rs | 27 +++++++++++---------------- 2 files changed, 12 insertions(+), 17 deletions(-) diff --git a/src/deprecated.rs b/src/deprecated.rs index aa1a3d89611..281fdb282f3 100644 --- a/src/deprecated.rs +++ b/src/deprecated.rs @@ -361,7 +361,7 @@ impl RngCore for JitterRng { } impl JitterRng { - #[cfg(feature="std")] + #[cfg(all(feature="std", not(target_arch = "wasm32")))] pub fn new() -> Result { rngs::JitterRng::new().map(JitterRng) } diff --git a/src/rngs/jitter.rs b/src/rngs/jitter.rs index 6aa30294ffd..292aae998e4 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 @@ const MEMORY_SIZE: usize = MEMORY_BLOCKS * MEMORY_BLOCKSIZE; /// 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 @@ impl From for Error { } // 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 @@ impl JitterRng { /// 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 { let mut state = JitterRng::new_with_timer(platform::get_nstime); let mut rounds = JITTER_ROUNDS.load(Ordering::Relaxed) as u8; @@ -771,8 +775,9 @@ impl JitterRng { #[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}; @@ -805,16 +810,6 @@ mod platform { *t.QuadPart() as u64 } } - - #[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))] - pub fn get_nstime() -> u64 { - // We don't use the timer from the standard library, because it panics - // at runtime. - // - // There is no accurate timer available on Wasm platforms, to help - // prevent fingerprinting or timing side-channel attacks. - 0 // Will make `test_timer` fail with `NoTimer`. - } } // A function that is opaque to the optimizer to assist in avoiding dead-code @@ -865,7 +860,7 @@ impl CryptoRng for JitterRng {} mod test_jitter_init { use super::JitterRng; - #[cfg(feature="std")] + #[cfg(all(feature="std", not(target_arch = "wasm32")))] #[test] fn test_jitter_init() { use RngCore; From 2a18a4d671627861dd1fd4a656d6f2b74d0bfe87 Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Thu, 14 Jun 2018 19:45:02 +0200 Subject: [PATCH 3/4] Make OsRng unavailable on unsupported platforms --- src/deprecated.rs | 65 ++++++++++++++++++++++++++++++++++++++++++++++- src/lib.rs | 39 ++++++++++++++++++++++++++-- src/rngs/mod.rs | 39 ++++++++++++++++++++++++++-- 3 files changed, 138 insertions(+), 5 deletions(-) diff --git a/src/deprecated.rs b/src/deprecated.rs index 281fdb282f3..0b6a71d502f 100644 --- a/src/deprecated.rs +++ b/src/deprecated.rs @@ -249,11 +249,42 @@ impl SeedableRng for StdRng { impl CryptoRng for StdRng {} -#[cfg(feature="std")] +#[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)] #[deprecated(since="0.6.0", note="import with rand::rngs::OsRng instead")] pub struct OsRng(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") +)))] #[cfg(feature="std")] impl RngCore for OsRng { #[inline(always)] @@ -277,6 +308,22 @@ impl RngCore for 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") +)))] #[cfg(feature="std")] impl OsRng { pub fn new() -> Result { @@ -284,6 +331,22 @@ impl 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") +)))] #[cfg(feature="std")] impl CryptoRng for OsRng {} diff --git a/src/lib.rs b/src/lib.rs index fb399d0e68b..bd9440b0721 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -284,7 +284,27 @@ pub mod rngs; #[doc(hidden)] pub use deprecated::ReseedingRng; #[allow(deprecated)] -#[cfg(feature="std")] #[doc(hidden)] pub use deprecated::{EntropyRng, OsRng}; +#[cfg(feature="std")] #[doc(hidden)] pub use deprecated::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 deprecated::OsRng; #[allow(deprecated)] #[doc(hidden)] pub use deprecated::{ChaChaRng, IsaacRng, Isaac64Rng, XorShiftRng}; @@ -299,7 +319,22 @@ pub mod jitter { pub use rngs::TimerError; } #[allow(deprecated)] -#[cfg(feature="std")] +#[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 deprecated::OsRng; diff --git a/src/rngs/mod.rs b/src/rngs/mod.rs index 29546eb1360..eb9ec2ff800 100644 --- a/src/rngs/mod.rs +++ b/src/rngs/mod.rs @@ -169,7 +169,7 @@ pub mod adapter; 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")] mod os; + mod small; mod std; #[cfg(feature="std")] pub(crate) mod thread; @@ -177,8 +177,43 @@ mod std; 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; From c9d35fc3645ad22248d359438d3527ff9e1beda7 Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Thu, 14 Jun 2018 19:47:42 +0200 Subject: [PATCH 4/4] Rewrite EntropyRng --- src/rngs/entropy.rs | 259 ++++++++++++++++++++++++++++++++------------ 1 file changed, 189 insertions(+), 70 deletions(-) diff --git a/src/rngs/entropy.rs b/src/rngs/entropy.rs index e260af9cb17..b8f4be799b6 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 @@ use rngs::{OsRng, JitterRng}; /// [`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 @@ impl EntropyRng { /// 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 @@ impl RngCore for EntropyRng { } 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) - } + let mut reported_error = None; - fn try_jitter_new(dest: &mut [u8]) -> Result - { - let mut rng = JitterRng::new()?; - rng.try_fill_bytes(dest)?; - Ok(rng) + 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)) }, + } } - 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::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); + }, } - 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); - } - } - } + } 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)) }, } - EntropySource::Jitter(ref mut rng) => { - if let Ok(os_rng) = try_os_new(dest) { - debug!("EntropyRng: using OsRng"); - switch_rng = Some(EntropySource::Os(os_rng)); - } else { - return rng.try_fill_bytes(dest); // use JitterRng - } + } + + 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(rng) = switch_rng { - self.rng = rng; + + 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")) } - Ok(()) } } impl CryptoRng for EntropyRng {} + + +trait EntropySource { + fn new_and_fill(dest: &mut [u8]) -> Result + 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 { + 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 { + 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 { + 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::*;