Merge pull request #685 from newpavlov/jitter

Split JitterRng into a separate crate 
diff --git a/.travis.yml b/.travis.yml
index b41e681..18084df 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -74,6 +74,7 @@
         - cargo test --manifest-path rand_xoshiro/Cargo.toml
         - cargo test --manifest-path rand_chacha/Cargo.toml
         - cargo test --manifest-path rand_hc/Cargo.toml
+        - cargo test --manifest-path rand_jitter/Cargo.toml
         - cargo test --manifest-path rand_os/Cargo.toml
 
     - rust: stable
@@ -95,6 +96,7 @@
         - cargo test --manifest-path rand_xoshiro/Cargo.toml
         - cargo test --manifest-path rand_chacha/Cargo.toml
         - cargo test --manifest-path rand_hc/Cargo.toml
+        - cargo test --manifest-path rand_jitter/Cargo.toml
         - cargo test --manifest-path rand_os/Cargo.toml
         - cargo build --target=aarch64-apple-ios
 
@@ -122,6 +124,7 @@
         - cargo test --manifest-path rand_xoshiro/Cargo.toml
         - cargo test --manifest-path rand_chacha/Cargo.toml
         - cargo test --manifest-path rand_hc/Cargo.toml
+        - cargo test --manifest-path rand_jitter/Cargo.toml
         - cargo test --manifest-path rand_os/Cargo.toml
         # remove cached documentation, otherwise files from previous PRs can get included
         - rm -rf target/doc
@@ -208,6 +211,7 @@
   - cargo test --manifest-path rand_xoshiro/Cargo.toml
   - cargo test --manifest-path rand_chacha/Cargo.toml
   - cargo test --manifest-path rand_hc/Cargo.toml
+  - cargo test --manifest-path rand_jitter/Cargo.toml
   - cargo test --manifest-path rand_os/Cargo.toml
 
 after_script: set +e
diff --git a/Cargo.toml b/Cargo.toml
index d802d36..c9100d8 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -20,9 +20,9 @@
 appveyor = { repository = "rust-random/rand" }
 
 [features]
-default = ["std", "rand_os"] # without "std" rand uses libcore
+default = ["std"] # without "std" rand uses libcore
 nightly = ["simd_support"] # enables all features requiring nightly rust
-std = ["rand_core/std", "alloc", "rand_os"]
+std = ["rand_core/std", "alloc", "rand_os", "rand_jitter/std"]
 alloc = ["rand_core/alloc"]  # enables Vec and Box support (without std)
 i128_support = [] # enables i128 and u128 support
 simd_support = ["packed_simd"] # enables SIMD support
@@ -34,6 +34,7 @@
 [workspace]
 members = [
     "rand_core",
+    "rand_jitter",
     "rand_os",
     "rand_isaac",
     "rand_chacha",
@@ -46,6 +47,7 @@
 [dependencies]
 rand_core = { path = "rand_core", version = "0.3", default-features = false }
 rand_pcg = { path = "rand_pcg", version = "0.1" }
+rand_jitter = { path = "rand_jitter", version = "0.1", default-features = false }
 rand_os = { path = "rand_os", version = "0.1", optional = true  }
 # only for deprecations and benches:
 rand_isaac = { path = "rand_isaac", version = "0.1" }
diff --git a/rand_jitter/CHANGELOG.md b/rand_jitter/CHANGELOG.md
new file mode 100644
index 0000000..b23c990
--- /dev/null
+++ b/rand_jitter/CHANGELOG.md
@@ -0,0 +1,8 @@
+# Changelog
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
+and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## [0.1.0] - 2019-01-04
+Initial release.
diff --git a/rand_jitter/COPYRIGHT b/rand_jitter/COPYRIGHT
new file mode 100644
index 0000000..468d907
--- /dev/null
+++ b/rand_jitter/COPYRIGHT
@@ -0,0 +1,12 @@
+Copyrights in the Rand project are retained by their contributors. No
+copyright assignment is required to contribute to the Rand project.
+
+For full authorship information, see the version control history.
+
+Except as otherwise noted (below and/or in individual files), Rand is
+licensed under the Apache License, Version 2.0 <LICENSE-APACHE> or
+<http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+<LICENSE-MIT> or <http://opensource.org/licenses/MIT>, at your option.
+
+The Rand project includes code from the Rust project
+published under these same licenses.
diff --git a/rand_jitter/Cargo.toml b/rand_jitter/Cargo.toml
new file mode 100644
index 0000000..22cc80e
--- /dev/null
+++ b/rand_jitter/Cargo.toml
@@ -0,0 +1,29 @@
+[package]
+name = "rand_jitter"
+version = "0.1.0"
+authors = ["The Rand Project Developers"]
+license = "MIT/Apache-2.0"
+readme = "README.md"
+repository = "https://github.com/rust-random/rand"
+documentation = "https://docs.rs/rand_jitter"
+description = "OS backed Random Number Generator"
+keywords = ["random", "rng", "os"]
+
+[badges]
+travis-ci = { repository = "rust-random/rand" }
+appveyor = { repository = "rust-random/rand" }
+
+[dependencies]
+rand_core = { path = "../rand_core", version = "0.3", default-features = false }
+log = { version = "0.4", optional = true }
+
+[target.'cfg(any(target_os = "macos", target_os = "ios"))'.dependencies]
+libc = "0.2"
+
+[target.'cfg(target_os = "windows")'.dependencies]
+winapi = { version = "0.3", features = ["profileapi"] }
+
+[features]
+default = ["std"]
+std = []
+
diff --git a/rand_jitter/LICENSE-APACHE b/rand_jitter/LICENSE-APACHE
new file mode 100644
index 0000000..17d7468
--- /dev/null
+++ b/rand_jitter/LICENSE-APACHE
@@ -0,0 +1,201 @@
+                              Apache License
+                        Version 2.0, January 2004
+                     https://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+   "License" shall mean the terms and conditions for use, reproduction,
+   and distribution as defined by Sections 1 through 9 of this document.
+
+   "Licensor" shall mean the copyright owner or entity authorized by
+   the copyright owner that is granting the License.
+
+   "Legal Entity" shall mean the union of the acting entity and all
+   other entities that control, are controlled by, or are under common
+   control with that entity. For the purposes of this definition,
+   "control" means (i) the power, direct or indirect, to cause the
+   direction or management of such entity, whether by contract or
+   otherwise, or (ii) ownership of fifty percent (50%) or more of the
+   outstanding shares, or (iii) beneficial ownership of such entity.
+
+   "You" (or "Your") shall mean an individual or Legal Entity
+   exercising permissions granted by this License.
+
+   "Source" form shall mean the preferred form for making modifications,
+   including but not limited to software source code, documentation
+   source, and configuration files.
+
+   "Object" form shall mean any form resulting from mechanical
+   transformation or translation of a Source form, including but
+   not limited to compiled object code, generated documentation,
+   and conversions to other media types.
+
+   "Work" shall mean the work of authorship, whether in Source or
+   Object form, made available under the License, as indicated by a
+   copyright notice that is included in or attached to the work
+   (an example is provided in the Appendix below).
+
+   "Derivative Works" shall mean any work, whether in Source or Object
+   form, that is based on (or derived from) the Work and for which the
+   editorial revisions, annotations, elaborations, or other modifications
+   represent, as a whole, an original work of authorship. For the purposes
+   of this License, Derivative Works shall not include works that remain
+   separable from, or merely link (or bind by name) to the interfaces of,
+   the Work and Derivative Works thereof.
+
+   "Contribution" shall mean any work of authorship, including
+   the original version of the Work and any modifications or additions
+   to that Work or Derivative Works thereof, that is intentionally
+   submitted to Licensor for inclusion in the Work by the copyright owner
+   or by an individual or Legal Entity authorized to submit on behalf of
+   the copyright owner. For the purposes of this definition, "submitted"
+   means any form of electronic, verbal, or written communication sent
+   to the Licensor or its representatives, including but not limited to
+   communication on electronic mailing lists, source code control systems,
+   and issue tracking systems that are managed by, or on behalf of, the
+   Licensor for the purpose of discussing and improving the Work, but
+   excluding communication that is conspicuously marked or otherwise
+   designated in writing by the copyright owner as "Not a Contribution."
+
+   "Contributor" shall mean Licensor and any individual or Legal Entity
+   on behalf of whom a Contribution has been received by Licensor and
+   subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of
+   this License, each Contributor hereby grants to You a perpetual,
+   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+   copyright license to reproduce, prepare Derivative Works of,
+   publicly display, publicly perform, sublicense, and distribute the
+   Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of
+   this License, each Contributor hereby grants to You a perpetual,
+   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+   (except as stated in this section) patent license to make, have made,
+   use, offer to sell, sell, import, and otherwise transfer the Work,
+   where such license applies only to those patent claims licensable
+   by such Contributor that are necessarily infringed by their
+   Contribution(s) alone or by combination of their Contribution(s)
+   with the Work to which such Contribution(s) was submitted. If You
+   institute patent litigation against any entity (including a
+   cross-claim or counterclaim in a lawsuit) alleging that the Work
+   or a Contribution incorporated within the Work constitutes direct
+   or contributory patent infringement, then any patent licenses
+   granted to You under this License for that Work shall terminate
+   as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the
+   Work or Derivative Works thereof in any medium, with or without
+   modifications, and in Source or Object form, provided that You
+   meet the following conditions:
+
+   (a) You must give any other recipients of the Work or
+       Derivative Works a copy of this License; and
+
+   (b) You must cause any modified files to carry prominent notices
+       stating that You changed the files; and
+
+   (c) You must retain, in the Source form of any Derivative Works
+       that You distribute, all copyright, patent, trademark, and
+       attribution notices from the Source form of the Work,
+       excluding those notices that do not pertain to any part of
+       the Derivative Works; and
+
+   (d) If the Work includes a "NOTICE" text file as part of its
+       distribution, then any Derivative Works that You distribute must
+       include a readable copy of the attribution notices contained
+       within such NOTICE file, excluding those notices that do not
+       pertain to any part of the Derivative Works, in at least one
+       of the following places: within a NOTICE text file distributed
+       as part of the Derivative Works; within the Source form or
+       documentation, if provided along with the Derivative Works; or,
+       within a display generated by the Derivative Works, if and
+       wherever such third-party notices normally appear. The contents
+       of the NOTICE file are for informational purposes only and
+       do not modify the License. You may add Your own attribution
+       notices within Derivative Works that You distribute, alongside
+       or as an addendum to the NOTICE text from the Work, provided
+       that such additional attribution notices cannot be construed
+       as modifying the License.
+
+   You may add Your own copyright statement to Your modifications and
+   may provide additional or different license terms and conditions
+   for use, reproduction, or distribution of Your modifications, or
+   for any such Derivative Works as a whole, provided Your use,
+   reproduction, and distribution of the Work otherwise complies with
+   the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise,
+   any Contribution intentionally submitted for inclusion in the Work
+   by You to the Licensor shall be under the terms and conditions of
+   this License, without any additional terms or conditions.
+   Notwithstanding the above, nothing herein shall supersede or modify
+   the terms of any separate license agreement you may have executed
+   with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade
+   names, trademarks, service marks, or product names of the Licensor,
+   except as required for reasonable and customary use in describing the
+   origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or
+   agreed to in writing, Licensor provides the Work (and each
+   Contributor provides its Contributions) on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+   implied, including, without limitation, any warranties or conditions
+   of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+   PARTICULAR PURPOSE. You are solely responsible for determining the
+   appropriateness of using or redistributing the Work and assume any
+   risks associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory,
+   whether in tort (including negligence), contract, or otherwise,
+   unless required by applicable law (such as deliberate and grossly
+   negligent acts) or agreed to in writing, shall any Contributor be
+   liable to You for damages, including any direct, indirect, special,
+   incidental, or consequential damages of any character arising as a
+   result of this License or out of the use or inability to use the
+   Work (including but not limited to damages for loss of goodwill,
+   work stoppage, computer failure or malfunction, or any and all
+   other commercial damages or losses), even if such Contributor
+   has been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing
+   the Work or Derivative Works thereof, You may choose to offer,
+   and charge a fee for, acceptance of support, warranty, indemnity,
+   or other liability obligations and/or rights consistent with this
+   License. However, in accepting such obligations, You may act only
+   on Your own behalf and on Your sole responsibility, not on behalf
+   of any other Contributor, and only if You agree to indemnify,
+   defend, and hold each Contributor harmless for any liability
+   incurred by, or claims asserted against, such Contributor by reason
+   of your accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
+
+APPENDIX: How to apply the Apache License to your work.
+
+   To apply the Apache License to your work, attach the following
+   boilerplate notice, with the fields enclosed by brackets "[]"
+   replaced with your own identifying information. (Don't include
+   the brackets!)  The text should be enclosed in the appropriate
+   comment syntax for the file format. We also recommend that a
+   file or class name and description of purpose be included on the
+   same "printed page" as the copyright notice for easier
+   identification within third-party archives.
+
+Copyright [yyyy] [name of copyright owner]
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+	https://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
diff --git a/rand_jitter/LICENSE-MIT b/rand_jitter/LICENSE-MIT
new file mode 100644
index 0000000..d93b5ba
--- /dev/null
+++ b/rand_jitter/LICENSE-MIT
@@ -0,0 +1,26 @@
+Copyright 2018 Developers of the Rand project
+Copyright (c) 2014 The Rust Project Developers
+
+Permission is hereby granted, free of charge, to any
+person obtaining a copy of this software and associated
+documentation files (the "Software"), to deal in the
+Software without restriction, including without
+limitation the rights to use, copy, modify, merge,
+publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software
+is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice
+shall be included in all copies or substantial portions
+of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
+IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
diff --git a/rand_jitter/README.md b/rand_jitter/README.md
new file mode 100644
index 0000000..4da9d3e
--- /dev/null
+++ b/rand_jitter/README.md
@@ -0,0 +1,98 @@
+# rand_jitter
+[![Build Status](https://travis-ci.org/rust-random/rand.svg?branch=master)](https://travis-ci.org/rust-random/rand)
+[![Build Status](https://ci.appveyor.com/api/projects/status/github/rust-random/rand?svg=true)](https://ci.appveyor.com/project/rust-random/rand)
+[![Latest version](https://img.shields.io/crates/v/rand_os.svg)](https://crates.io/crates/rand_jitter)
+[![Book](https://img.shields.io/badge/book-master-yellow.svg)](https://rust-random.github.io/book/)
+[![API](https://img.shields.io/badge/api-master-yellow.svg)](https://rust-random.github.io/rand/rand_jitter)
+[![API](https://docs.rs/rand_os/badge.svg)](https://docs.rs/rand_jitter)
+[![Minimum rustc version](https://img.shields.io/badge/rustc-1.22+-lightgray.svg)](https://github.com/rust-random/rand#rust-version-requirements)
+
+Non-physical true random number generator based on timing jitter.
+
+This crate depends on [rand_core](https://crates.io/crates/rand_core) and is
+part of the [Rand project](https://github.com/rust-random/rand).
+
+This crate aims to support all of Rust's `std` platforms with a system-provided
+entropy source. Unlike other Rand crates, this crate does not support `no_std`
+(handling this gracefully is a current discussion topic).
+
+Links:
+
+-   [API documentation (master)](https://rust-random.github.io/rand/rand_jitter)
+-   [API documentation (docs.rs)](https://docs.rs/rand_jitter)
+-   [Changelog](CHANGELOG.md)
+
+## Quality testing
+
+`JitterRng::new()` has build-in, but limited, quality testing, however
+before using `JitterRng` on untested hardware, or after changes that could
+effect how the code is optimized (such as a new LLVM version), it is
+recommend to run the much more stringent
+[NIST SP 800-90B Entropy Estimation Suite](https://github.com/usnistgov/SP800-90B_EntropyAssessment).
+
+Use the following code using `timer_stats` to collect the data:
+
+```rust
+use rand_jitter::JitterRng;
+
+use std::error::Error;
+use std::fs::File;
+use std::io::Write;
+
+fn main() -> Result<(), Box<Error>> {
+    let mut rng = JitterRng::new()?;
+
+    // 1_000_000 results are required for the
+    // NIST SP 800-90B Entropy Estimation Suite
+    const ROUNDS: usize = 1_000_000;
+    let mut deltas_variable: Vec<u8> = Vec::with_capacity(ROUNDS);
+    let mut deltas_minimal: Vec<u8> = Vec::with_capacity(ROUNDS);
+
+    for _ in 0..ROUNDS {
+        deltas_variable.push(rng.timer_stats(true) as u8);
+        deltas_minimal.push(rng.timer_stats(false) as u8);
+    }
+
+    // Write out after the statistics collection loop, to not disturb the
+    // test results.
+    File::create("jitter_rng_var.bin")?.write(&deltas_variable)?;
+    File::create("jitter_rng_min.bin")?.write(&deltas_minimal)?;
+    Ok(())
+}
+```
+
+This will produce two files: `jitter_rng_var.bin` and `jitter_rng_min.bin`.
+Run the Entropy Estimation Suite in three configurations, as outlined below.
+Every run has two steps. One step to produce an estimation, another to
+validate the estimation.
+
+1. Estimate the expected amount of entropy that is at least available with
+   each round of the entropy collector. This number should be greater than
+   the amount estimated with `64 / test_timer()`.
+   ```sh
+   python noniid_main.py -v jitter_rng_var.bin 8
+   restart.py -v jitter_rng_var.bin 8 <min-entropy>
+   ```
+2. Estimate the expected amount of entropy that is available in the last 4
+   bits of the timer delta after running noice sources. Note that a value of
+   `3.70` is the minimum estimated entropy for true randomness.
+   ```sh
+   python noniid_main.py -v -u 4 jitter_rng_var.bin 4
+   restart.py -v -u 4 jitter_rng_var.bin 4 <min-entropy>
+   ```
+3. Estimate the expected amount of entropy that is available to the entropy
+   collector if both noise sources only run their minimal number of times.
+   This measures the absolute worst-case, and gives a lower bound for the
+   available entropy.
+   ```sh
+   python noniid_main.py -v -u 4 jitter_rng_min.bin 4
+   restart.py -v -u 4 jitter_rng_min.bin 4 <min-entropy>
+   ```
+
+## License
+
+`rand_jitter` is distributed under the terms of both the MIT license and the
+Apache License (Version 2.0).
+
+See [LICENSE-APACHE](LICENSE-APACHE) and [LICENSE-MIT](LICENSE-MIT), and
+[COPYRIGHT](COPYRIGHT) for details.
diff --git a/rand_jitter/benches/mod.rs b/rand_jitter/benches/mod.rs
new file mode 100644
index 0000000..23b3447
--- /dev/null
+++ b/rand_jitter/benches/mod.rs
@@ -0,0 +1,18 @@
+#![feature(test)]
+extern crate test;
+extern crate rand_jitter;
+
+use test::Bencher;
+use rand_jitter::rand_core::RngCore;
+
+#[bench]
+fn bench_add_two(b: &mut Bencher) {
+    let mut rng = rand_jitter::JitterRng::new().unwrap();
+    let mut buf = [0u8; 1024];
+    b.iter(|| {
+        rng.fill_bytes(&mut buf[..]);
+        test::black_box(&buf);
+    });
+    b.bytes = buf.len() as u64;
+}
+
diff --git a/rand_jitter/src/dummy_log.rs b/rand_jitter/src/dummy_log.rs
new file mode 100644
index 0000000..ccfe4ba
--- /dev/null
+++ b/rand_jitter/src/dummy_log.rs
@@ -0,0 +1,10 @@
+#[allow(unused)]
+macro_rules! trace { ($($x:tt)*) => () }
+#[allow(unused)]
+macro_rules! debug { ($($x:tt)*) => () }
+#[allow(unused)]
+macro_rules! info { ($($x:tt)*) => () }
+#[allow(unused)]
+macro_rules! warn { ($($x:tt)*) => () }
+#[allow(unused)]
+macro_rules! error { ($($x:tt)*) => () }
diff --git a/rand_jitter/src/error.rs b/rand_jitter/src/error.rs
new file mode 100644
index 0000000..b8bc37f
--- /dev/null
+++ b/rand_jitter/src/error.rs
@@ -0,0 +1,66 @@
+// Copyright 2018 Developers of the Rand project.
+// Copyright 2013-2015 The Rust Project Developers.
+//
+// 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.
+
+use rand_core::{Error, ErrorKind};
+use core::fmt;
+
+/// An error that can occur when [`JitterRng::test_timer`] fails.
+///
+/// [`JitterRng::test_timer`]: crate::JitterRng::test_timer
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum TimerError {
+    /// No timer available.
+    NoTimer,
+    /// Timer too coarse to use as an entropy source.
+    CoarseTimer,
+    /// Timer is not monotonically increasing.
+    NotMonotonic,
+    /// Variations of deltas of time too small.
+    TinyVariantions,
+    /// Too many stuck results (indicating no added entropy).
+    TooManyStuck,
+    #[doc(hidden)]
+    __Nonexhaustive,
+}
+
+impl TimerError {
+    fn description(&self) -> &'static str {
+        match *self {
+            TimerError::NoTimer => "no timer available",
+            TimerError::CoarseTimer => "coarse timer",
+            TimerError::NotMonotonic => "timer not monotonic",
+            TimerError::TinyVariantions => "time delta variations too small",
+            TimerError::TooManyStuck => "too many stuck results",
+            TimerError::__Nonexhaustive => unreachable!(),
+        }
+    }
+}
+
+impl fmt::Display for TimerError {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "{}", self.description())
+    }
+}
+
+#[cfg(feature = "std")]
+impl ::std::error::Error for TimerError {
+    fn description(&self) -> &str {
+        self.description()
+    }
+}
+
+impl From<TimerError> for Error {
+    fn from(err: TimerError) -> Error {
+        // Timer check is already quite permissive of failures so we don't
+        // expect false-positive failures, i.e. any error is irrecoverable.
+        Error::with_cause(ErrorKind::Unavailable,
+                              "timer jitter failed basic quality tests", err)
+    }
+}
+
diff --git a/src/rngs/jitter.rs b/rand_jitter/src/lib.rs
similarity index 74%
rename from src/rngs/jitter.rs
rename to rand_jitter/src/lib.rs
index 9c75f6c..4a4dddf 100644
--- a/src/rngs/jitter.rs
+++ b/rand_jitter/src/lib.rs
@@ -13,16 +13,60 @@
 // the MIT license.
 
 //! Non-physical true random number generator based on timing jitter.
+//!
+//! This is a true random number generator, as opposed to pseudo-random
+//! generators. Random numbers generated by `JitterRng` can be seen as fresh
+//! entropy. A consequence is that it is orders of magnitude slower than `OsRng`
+//! and PRNGs (about 10<sup>3</sup>..10<sup>6</sup> slower).
+//!
+//! There are very few situations where using this RNG is appropriate. Only very
+//! few applications require true entropy. A normal PRNG can be statistically
+//! indistinguishable, and a cryptographic PRNG should also be as impossible to
+//! predict.
+//!
+//! Use of `JitterRng` is recommended for initializing cryptographic PRNGs when
+//! `OsRng` is not available.
+//!
+//! `JitterRng` can be used without the standard library, but not conveniently,
+//! you must provide a high-precision timer and carefully have to follow the
+//! instructions of [`JitterRng::new_with_timer`].
+//!
+//! This implementation is based on [Jitterentropy] 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. It is also unavailable
+//! with disabled `std` feature.
+//!
+//! [Jitterentropy]: http://www.chronox.de/jent.html
 
 // Note: the C implementation of `Jitterentropy` relies on being compiled
 // without optimizations. This implementation goes through lengths to make the
 // compiler not optimize out code which does influence timing jitter, but is
 // technically dead code.
+#![no_std]
+pub extern crate rand_core;
+#[cfg(feature = "std")]
+extern crate std;
+#[cfg(feature = "log")]
+#[macro_use] extern crate log;
+#[cfg(any(target_os = "macos", target_os = "ios"))]
+extern crate libc;
+#[cfg(target_os = "windows")]
+extern crate winapi;
 
-use rand_core::{RngCore, CryptoRng, Error, ErrorKind, impls};
+
+#[cfg(not(feature = "log"))]
+#[macro_use] mod dummy_log;
+#[cfg(feature = "std")]
+mod platform;
+mod error;
+
+use rand_core::{RngCore, CryptoRng, Error, impls};
+pub use error::TimerError;
 
 use core::{fmt, mem, ptr};
-#[cfg(all(feature="std", not(target_arch = "wasm32")))]
+#[cfg(feature = "std")]
 use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};
 
 const MEMORY_BLOCKS: usize = 64;
@@ -31,107 +75,6 @@
 
 /// A true random number generator based on jitter in the CPU execution time,
 /// and jitter in memory access time.
-///
-/// This is a true random number generator, as opposed to pseudo-random
-/// generators. Random numbers generated by `JitterRng` can be seen as fresh
-/// entropy. A consequence is that is orders of magnitude slower than [`OsRng`]
-/// and PRNGs (about 10<sup>3</sup>..10<sup>6</sup> slower).
-///
-/// There are very few situations where using this RNG is appropriate. Only very
-/// few applications require true entropy. A normal PRNG can be statistically
-/// indistinguishable, and a cryptographic PRNG should also be as impossible to
-/// predict.
-///
-/// Use of `JitterRng` is recommended for initializing cryptographic PRNGs when
-/// [`OsRng`] is not available.
-///
-/// `JitterRng` can be used without the standard library, but not conveniently,
-/// you must provide a high-precision timer and carefully have to follow the
-/// instructions of [`new_with_timer`].
-///
-/// 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
-/// before using `JitterRng` on untested hardware, or after changes that could
-/// effect how the code is optimized (such as a new LLVM version), it is
-/// recommend to run the much more stringent
-/// [NIST SP 800-90B Entropy Estimation Suite](
-/// https://github.com/usnistgov/SP800-90B_EntropyAssessment).
-///
-/// Use the following code using [`timer_stats`] to collect the data:
-///
-/// ```no_run
-/// use rand::rngs::JitterRng;
-/// #
-/// # use std::error::Error;
-/// # use std::fs::File;
-/// # use std::io::Write;
-/// #
-/// # fn try_main() -> Result<(), Box<Error>> {
-/// let mut rng = JitterRng::new()?;
-///
-/// // 1_000_000 results are required for the
-/// // NIST SP 800-90B Entropy Estimation Suite
-/// const ROUNDS: usize = 1_000_000;
-/// let mut deltas_variable: Vec<u8> = Vec::with_capacity(ROUNDS);
-/// let mut deltas_minimal: Vec<u8> = Vec::with_capacity(ROUNDS);
-///
-/// for _ in 0..ROUNDS {
-///     deltas_variable.push(rng.timer_stats(true) as u8);
-///     deltas_minimal.push(rng.timer_stats(false) as u8);
-/// }
-///
-/// // Write out after the statistics collection loop, to not disturb the
-/// // test results.
-/// File::create("jitter_rng_var.bin")?.write(&deltas_variable)?;
-/// File::create("jitter_rng_min.bin")?.write(&deltas_minimal)?;
-/// #
-/// # Ok(())
-/// # }
-/// #
-/// # fn main() {
-/// #     try_main().unwrap();
-/// # }
-/// ```
-///
-/// This will produce two files: `jitter_rng_var.bin` and `jitter_rng_min.bin`.
-/// Run the Entropy Estimation Suite in three configurations, as outlined below.
-/// Every run has two steps. One step to produce an estimation, another to
-/// validate the estimation.
-///
-/// 1. Estimate the expected amount of entropy that is at least available with
-///    each round of the entropy collector. This number should be greater than
-///    the amount estimated with `64 / test_timer()`.
-///    ```sh
-///    python noniid_main.py -v jitter_rng_var.bin 8
-///    restart.py -v jitter_rng_var.bin 8 <min-entropy>
-///    ```
-/// 2. Estimate the expected amount of entropy that is available in the last 4
-///    bits of the timer delta after running noice sources. Note that a value of
-///    `3.70` is the minimum estimated entropy for true randomness.
-///    ```sh
-///    python noniid_main.py -v -u 4 jitter_rng_var.bin 4
-///    restart.py -v -u 4 jitter_rng_var.bin 4 <min-entropy>
-///    ```
-/// 3. Estimate the expected amount of entropy that is available to the entropy
-///    collector if both noice sources only run their minimal number of times.
-///    This measures the absolute worst-case, and gives a lower bound for the
-///    available entropy.
-///    ```sh
-///    python noniid_main.py -v -u 4 jitter_rng_min.bin 4
-///    restart.py -v -u 4 jitter_rng_min.bin 4 <min-entropy>
-///    ```
-///
-/// [`OsRng`]: rand_os::OsRng
-/// [`new_with_timer`]: JitterRng::new_with_timer
-/// [`timer_stats`]: JitterRng::timer_stats
 pub struct JitterRng {
     data: u64, // Actual random number
     // Number of rounds to run the entropy collector per 64 bits
@@ -214,74 +157,23 @@
     }
 }
 
-/// An error that can occur when [`JitterRng::test_timer`] fails.
-///
-/// [`JitterRng::test_timer`]: JitterRng::test_timer
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub enum TimerError {
-    /// No timer available.
-    NoTimer,
-    /// Timer too coarse to use as an entropy source.
-    CoarseTimer,
-    /// Timer is not monotonically increasing.
-    NotMonotonic,
-    /// Variations of deltas of time too small.
-    TinyVariantions,
-    /// Too many stuck results (indicating no added entropy).
-    TooManyStuck,
-    #[doc(hidden)]
-    __Nonexhaustive,
-}
-
-impl TimerError {
-    fn description(&self) -> &'static str {
-        match *self {
-            TimerError::NoTimer => "no timer available",
-            TimerError::CoarseTimer => "coarse timer",
-            TimerError::NotMonotonic => "timer not monotonic",
-            TimerError::TinyVariantions => "time delta variations too small",
-            TimerError::TooManyStuck => "too many stuck results",
-            TimerError::__Nonexhaustive => unreachable!(),
-        }
-    }
-}
-
-impl fmt::Display for TimerError {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        write!(f, "{}", self.description())
-    }
-}
-
-#[cfg(feature="std")]
-impl ::std::error::Error for TimerError {
-    fn description(&self) -> &str {
-        self.description()
-    }
-}
-
-impl From<TimerError> for Error {
-    fn from(err: TimerError) -> Error {
-        // Timer check is already quite permissive of failures so we don't
-        // expect false-positive failures, i.e. any error is irrecoverable.
-        Error::with_cause(ErrorKind::Unavailable,
-                              "timer jitter failed basic quality tests", err)
-    }
-}
-
 // Initialise to zero; must be positive
-#[cfg(all(feature="std", not(target_arch = "wasm32")))]
+#[cfg(all(feature = "std", not(target_arch = "wasm32")))]
 static JITTER_ROUNDS: AtomicUsize = ATOMIC_USIZE_INIT;
 
 impl JitterRng {
-    /// Create a new `JitterRng`. Makes use of [`std::time`] for a timer, or a
+    /// Create a new `JitterRng`. Makes use of `std::time` for a timer, or a
     /// platform-specific function with higher accuracy if necessary and
     /// available.
     ///
     /// 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(all(feature="std", not(target_arch = "wasm32")))]
+    #[cfg(all(feature = "std", not(target_arch = "wasm32")))]
     pub fn new() -> Result<JitterRng, TimerError> {
+        if cfg!(target_arch = "wasm32") {
+            return Err(TimerError::NoTimer);
+        }
         let mut state = JitterRng::new_with_timer(platform::get_nstime);
         let mut rounds = JITTER_ROUNDS.load(Ordering::Relaxed) as u8;
         if rounds == 0 {
@@ -313,8 +205,8 @@
     /// # Example
     ///
     /// ```
-    /// # use rand::{Rng, Error};
-    /// use rand::rngs::JitterRng;
+    /// # use rand_jitter::rand_core::{RngCore, Error};
+    /// use rand_jitter::JitterRng;
     ///
     /// # fn try_inner() -> Result<(), Error> {
     /// fn get_nstime() -> u64 {
@@ -331,10 +223,10 @@
     /// let mut rng = JitterRng::new_with_timer(get_nstime);
     /// let rounds = rng.test_timer()?;
     /// rng.set_rounds(rounds); // optional
-    /// let _ = rng.gen::<u64>();
+    /// let _ = rng.next_u64();
     ///
     /// // Ready for use
-    /// let v: u64 = rng.gen();
+    /// let v: u64 = rng.next_u64();
     /// # Ok(())
     /// # }
     ///
@@ -768,45 +660,6 @@
     }
 }
 
-#[cfg(feature="std")]
-mod platform {
-    #[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};
-
-        let dur = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
-        // The correct way to calculate the current time is
-        // `dur.as_secs() * 1_000_000_000 + dur.subsec_nanos() as u64`
-        // But this is faster, and the difference in terms of entropy is
-        // negligible (log2(10^9) == 29.9).
-        dur.as_secs() << 30 | dur.subsec_nanos() as u64
-    }
-
-    #[cfg(any(target_os = "macos", target_os = "ios"))]
-    pub fn get_nstime() -> u64 {
-        extern crate libc;
-        // On Mac OS and iOS std::time::SystemTime only has 1000ns resolution.
-        // We use `mach_absolute_time` instead. This provides a CPU dependent
-        // unit, to get real nanoseconds the result should by multiplied by
-        // numer/denom from `mach_timebase_info`.
-        // But we are not interested in the exact nanoseconds, just entropy. So
-        // we use the raw result.
-        unsafe { libc::mach_absolute_time() }
-    }
-
-    #[cfg(target_os = "windows")]
-    pub fn get_nstime() -> u64 {
-        extern crate winapi;
-        unsafe {
-            let mut t = super::mem::zeroed();
-            winapi::um::profileapi::QueryPerformanceCounter(&mut t);
-            *t.QuadPart() as u64
-        }
-    }
-}
-
 // A function that is opaque to the optimizer to assist in avoiding dead-code
 // elimination. Taken from `bencher`.
 fn black_box<T>(dummy: T) -> T {
@@ -851,32 +704,3 @@
 
 impl CryptoRng for JitterRng {}
 
-#[cfg(test)]
-mod test_jitter_init {
-    use super::JitterRng;
-
-    #[cfg(all(feature="std", not(target_arch = "wasm32")))]
-    #[test]
-    fn test_jitter_init() {
-        use RngCore;
-        // Because this is a debug build, measurements here are not representive
-        // of the final release build.
-        // Don't fail this test if initializing `JitterRng` fails because of a
-        // bad timer (the timer from the standard library may not have enough
-        // accuracy on all platforms).
-        match JitterRng::new() {
-            Ok(ref mut rng) => {
-                // false positives are possible, but extremely unlikely
-                assert!(rng.next_u32() | rng.next_u32() != 0);
-            },
-            Err(_) => {},
-        }
-    }
-
-    #[test]
-    fn test_jitter_bad_timer() {
-        fn bad_timer() -> u64 { 0 }
-        let mut rng = JitterRng::new_with_timer(bad_timer);
-        assert!(rng.test_timer().is_err());
-    }
-}
diff --git a/rand_jitter/src/platform.rs b/rand_jitter/src/platform.rs
new file mode 100644
index 0000000..f96d1cd
--- /dev/null
+++ b/rand_jitter/src/platform.rs
@@ -0,0 +1,40 @@
+// Copyright 2018 Developers of the Rand project.
+// Copyright 2013-2015 The Rust Project Developers.
+//
+// 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.
+
+#[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "windows")))]
+pub fn get_nstime() -> u64 {
+    use std::time::{SystemTime, UNIX_EPOCH};
+
+    let dur = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
+    // The correct way to calculate the current time is
+    // `dur.as_secs() * 1_000_000_000 + dur.subsec_nanos() as u64`
+    // But this is faster, and the difference in terms of entropy is
+    // negligible (log2(10^9) == 29.9).
+    dur.as_secs() << 30 | dur.subsec_nanos() as u64
+}
+
+#[cfg(any(target_os = "macos", target_os = "ios"))]
+pub fn get_nstime() -> u64 {
+    // On Mac OS and iOS std::time::SystemTime only has 1000ns resolution.
+    // We use `mach_absolute_time` instead. This provides a CPU dependent
+    // unit, to get real nanoseconds the result should by multiplied by
+    // numer/denom from `mach_timebase_info`.
+    // But we are not interested in the exact nanoseconds, just entropy. So
+    // we use the raw result.
+    unsafe { libc::mach_absolute_time() }
+}
+
+#[cfg(target_os = "windows")]
+pub fn get_nstime() -> u64 {
+    unsafe {
+        let mut t = super::mem::zeroed();
+        winapi::um::profileapi::QueryPerformanceCounter(&mut t);
+        *t.QuadPart() as u64
+    }
+}
diff --git a/rand_jitter/tests/mod.rs b/rand_jitter/tests/mod.rs
new file mode 100644
index 0000000..ba7e54c
--- /dev/null
+++ b/rand_jitter/tests/mod.rs
@@ -0,0 +1,29 @@
+extern crate rand_jitter;
+extern crate rand_core;
+
+use rand_jitter::JitterRng;
+use rand_core::RngCore;
+
+#[test]
+fn test_jitter_init() {
+    // Because this is a debug build, measurements here are not representive
+    // of the final release build.
+    // Don't fail this test if initializing `JitterRng` fails because of a
+    // bad timer (the timer from the standard library may not have enough
+    // accuracy on all platforms).
+    match JitterRng::new() {
+        Ok(ref mut rng) => {
+            // false positives are possible, but extremely unlikely
+            assert!(rng.next_u32() | rng.next_u32() != 0);
+        },
+        Err(_) => {},
+    }
+}
+
+#[test]
+fn test_jitter_bad_timer() {
+    fn bad_timer() -> u64 { 0 }
+    let mut rng = JitterRng::new_with_timer(bad_timer);
+    assert!(rng.test_timer().is_err());
+}
+
diff --git a/src/lib.rs b/src/lib.rs
index 491eb7e..9c0482f 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -58,6 +58,7 @@
 
 #[cfg(feature="simd_support")] extern crate packed_simd;
 
+extern crate rand_jitter;
 #[cfg(feature = "rand_os")]
 extern crate rand_os;
 
diff --git a/src/rngs/mod.rs b/src/rngs/mod.rs
index 5630597..b3f05a0 100644
--- a/src/rngs/mod.rs
+++ b/src/rngs/mod.rs
@@ -149,7 +149,6 @@
 pub mod adapter;
 
 #[cfg(feature="std")] mod entropy;
-mod jitter;
 pub mod mock;   // Public so we don't export `StepRng` directly, making it a bit
                 // more clear it is intended for testing.
 mod small;
@@ -157,7 +156,7 @@
 #[cfg(feature="std")] pub(crate) mod thread;
 
 
-pub use self::jitter::{JitterRng, TimerError};
+pub use rand_jitter::{JitterRng, TimerError};
 #[cfg(feature="std")] pub use self::entropy::EntropyRng;
 
 pub use self::small::SmallRng;
diff --git a/utils/ci/script.sh b/utils/ci/script.sh
index e8c1189..d6338cc 100644
--- a/utils/ci/script.sh
+++ b/utils/ci/script.sh
@@ -16,6 +16,7 @@
     cross test --target $TARGET --manifest-path rand_chacha/Cargo.toml
     cross test --target $TARGET --manifest-path rand_hc/Cargo.toml
     cross test --target $TARGET --manifest-path rand_os/Cargo.toml
+    cross test --target $TARGET --manifest-path rand_jitter/Cargo.toml
 }
 
 # we don't run the "test phase" when doing deploys