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