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;