From 1976a7f7b0971bd060b728388c615e500c46cd99 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Tue, 2 Apr 2019 12:50:53 +0100 Subject: [PATCH 1/9] Replace rand_os implementation with shim around getrandom --- Cargo.toml | 2 +- rand_os/CHANGELOG.md | 4 + rand_os/Cargo.toml | 30 +- rand_os/README.md | 10 +- rand_os/src/cloudabi.rs | 39 -- rand_os/src/dragonfly_haiku_emscripten.rs | 39 -- rand_os/src/dummy_log.rs | 10 - rand_os/src/freebsd.rs | 45 --- rand_os/src/fuchsia.rs | 28 -- rand_os/src/lib.rs | 416 ++-------------------- rand_os/src/linux_android.rs | 186 ---------- rand_os/src/macos.rs | 53 --- rand_os/src/netbsd.rs | 57 --- rand_os/src/openbsd_bitrig.rs | 40 --- rand_os/src/random_device.rs | 70 ---- rand_os/src/redox.rs | 30 -- rand_os/src/sgx.rs | 38 -- rand_os/src/solarish.rs | 195 ---------- rand_os/src/wasm32_bindgen.rs | 92 ----- rand_os/src/wasm32_stdweb.rs | 107 ------ rand_os/src/windows.rs | 44 --- rand_os/tests/mod.rs | 80 ----- src/rngs/adapter/reseeding.rs | 5 +- src/rngs/entropy.rs | 4 +- tests/wasm_bindgen/src/lib.rs | 2 +- 25 files changed, 62 insertions(+), 1564 deletions(-) delete mode 100644 rand_os/src/cloudabi.rs delete mode 100644 rand_os/src/dragonfly_haiku_emscripten.rs delete mode 100644 rand_os/src/dummy_log.rs delete mode 100644 rand_os/src/freebsd.rs delete mode 100644 rand_os/src/fuchsia.rs delete mode 100644 rand_os/src/linux_android.rs delete mode 100644 rand_os/src/macos.rs delete mode 100644 rand_os/src/netbsd.rs delete mode 100644 rand_os/src/openbsd_bitrig.rs delete mode 100644 rand_os/src/random_device.rs delete mode 100644 rand_os/src/redox.rs delete mode 100644 rand_os/src/sgx.rs delete mode 100644 rand_os/src/solarish.rs delete mode 100644 rand_os/src/wasm32_bindgen.rs delete mode 100644 rand_os/src/wasm32_stdweb.rs delete mode 100644 rand_os/src/windows.rs delete mode 100644 rand_os/tests/mod.rs diff --git a/Cargo.toml b/Cargo.toml index 76c40223bfd..340fbb226f2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,7 +50,7 @@ members = [ rand_core = { path = "rand_core", version = "0.4" } rand_pcg = { path = "rand_pcg", version = "0.1" } rand_jitter = { path = "rand_jitter", version = "0.1" } -rand_os = { path = "rand_os", version = "0.1", optional = true } +rand_os = { path = "rand_os", version = "0.2", optional = true } rand_hc = { path = "rand_hc", version = "0.1" } log = { version = "0.4", optional = true } diff --git a/rand_os/CHANGELOG.md b/rand_os/CHANGELOG.md index 1ce36fc6cc7..ddba15f968b 100644 --- a/rand_os/CHANGELOG.md +++ b/rand_os/CHANGELOG.md @@ -5,6 +5,10 @@ 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.2.0] - 2019-04-08 +Replaced implementation with a backwards-compatible shim around +[getrandom](https://crates.io/crates/getrandom). + ## [0.1.3] - 2019-03-05 ### Changes - Fix support for Illumos (#730) diff --git a/rand_os/Cargo.toml b/rand_os/Cargo.toml index cd154fc94b6..a7b643c0274 100644 --- a/rand_os/Cargo.toml +++ b/rand_os/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rand_os" -version = "0.1.3" +version = "0.2.0" authors = ["The Rand Project Developers"] license = "MIT/Apache-2.0" readme = "README.md" @@ -9,30 +9,18 @@ documentation = "https://docs.rs/rand_os" homepage = "https://crates.io/crates/rand_os" description = "OS backed Random Number Generator" keywords = ["random", "rng", "os"] +edition = "2018" [badges] travis-ci = { repository = "rust-random/rand" } appveyor = { repository = "rust-random/rand" } +[features] +log = ["getrandom/log"] +# re-export optional WASM dependencies to avoid breakage: +wasm-bindgen = ["getrandom/wasm-bindgen"] +stdweb = ["getrandom/stdweb"] + [dependencies] rand_core = { path = "../rand_core", version = "0.4", features = ["std"] } -log = { version = "0.4", optional = true } - -[target.'cfg(unix)'.dependencies] -libc = "0.2" - -[target.'cfg(windows)'.dependencies] -winapi = { version = "0.3", features = ["minwindef", "ntsecapi", "winnt"] } - -[target.'cfg(target_os = "cloudabi")'.dependencies] -cloudabi = "0.0.3" - -[target.'cfg(target_os = "fuchsia")'.dependencies] -fuchsia-cprng = "0.1.0" - -[target.wasm32-unknown-unknown.dependencies] -wasm-bindgen = { version = "0.2.12", optional = true } -stdweb = { version = "0.4", optional = true } - -[target.'cfg(target_env = "sgx")'.dependencies] -rdrand = "0.5.0" +getrandom = "0.1" diff --git a/rand_os/README.md b/rand_os/README.md index 2cd31777dcf..ddfd1acbfb3 100644 --- a/rand_os/README.md +++ b/rand_os/README.md @@ -11,12 +11,12 @@ A random number generator that retrieves randomness straight from the operating system. -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 provides `OsRng` as a shim around +[getrandom](https://crates.io/crates/getrandom) +implementing `RngCore` from [rand_core](https://crates.io/crates/rand_core). -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). +Note: the `rand` crate provides an equivalent `OsRng`; the two implementations +are equivalent, though distinct types. Links: diff --git a/rand_os/src/cloudabi.rs b/rand_os/src/cloudabi.rs deleted file mode 100644 index 8b96a2bc9ca..00000000000 --- a/rand_os/src/cloudabi.rs +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2018 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Implementation for CloudABI - -extern crate cloudabi; - -use std::io; -use rand_core::{Error, ErrorKind}; -use super::OsRngImpl; - -#[derive(Clone, Debug)] -pub struct OsRng; - -impl OsRngImpl for OsRng { - fn new() -> Result { Ok(OsRng) } - - fn fill_chunk(&mut self, dest: &mut [u8]) -> Result<(), Error> { - let errno = unsafe { cloudabi::random_get(dest) }; - if errno == cloudabi::errno::SUCCESS { - Ok(()) - } else { - // Cloudlibc provides its own `strerror` implementation so we - // can use `from_raw_os_error` here. - Err(Error::with_cause( - ErrorKind::Unavailable, - "random_get() system call failed", - io::Error::from_raw_os_error(errno as i32), - )) - } - } - - fn method_str(&self) -> &'static str { "cloudabi::random_get" } -} diff --git a/rand_os/src/dragonfly_haiku_emscripten.rs b/rand_os/src/dragonfly_haiku_emscripten.rs deleted file mode 100644 index 6132d7ac3bc..00000000000 --- a/rand_os/src/dragonfly_haiku_emscripten.rs +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2018 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Implementation for DragonFly / Haiku / Emscripten - -use rand_core::Error; -use super::random_device; -use super::OsRngImpl; -use std::fs::File; - -#[derive(Clone, Debug)] -pub struct OsRng(); - -impl OsRngImpl for OsRng { - fn new() -> Result { - random_device::open("/dev/random", &|p| File::open(p))?; - Ok(OsRng()) - } - - fn fill_chunk(&mut self, dest: &mut [u8]) -> Result<(), Error> { - random_device::read(dest) - } - - #[cfg(target_os = "emscripten")] - fn max_chunk_size(&self) -> usize { - // `Crypto.getRandomValues` documents `dest` should be at most 65536 - // bytes. `crypto.randomBytes` documents: "To minimize threadpool - // task length variation, partition large randomBytes requests when - // doing so as part of fulfilling a client request. - 65536 - } - - fn method_str(&self) -> &'static str { "/dev/random" } -} diff --git a/rand_os/src/dummy_log.rs b/rand_os/src/dummy_log.rs deleted file mode 100644 index ccfe4baeff2..00000000000 --- a/rand_os/src/dummy_log.rs +++ /dev/null @@ -1,10 +0,0 @@ -#[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_os/src/freebsd.rs b/rand_os/src/freebsd.rs deleted file mode 100644 index 6b8e6728a42..00000000000 --- a/rand_os/src/freebsd.rs +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2018 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Implementation for FreeBSD - -extern crate libc; - -use rand_core::{Error, ErrorKind}; -use super::OsRngImpl; - -use std::ptr; -use std::io; - -#[derive(Clone, Debug)] -pub struct OsRng; - -impl OsRngImpl for OsRng { - fn new() -> Result { Ok(OsRng) } - - fn fill_chunk(&mut self, dest: &mut [u8]) -> Result<(), Error> { - let mib = [libc::CTL_KERN, libc::KERN_ARND]; - let mut len = dest.len(); - let ret = unsafe { - libc::sysctl(mib.as_ptr(), mib.len() as libc::c_uint, - dest.as_mut_ptr() as *mut _, &mut len, - ptr::null(), 0) - }; - if ret == -1 || len != dest.len() { - return Err(Error::with_cause( - ErrorKind::Unavailable, - "kern.arandom sysctl failed", - io::Error::last_os_error())); - } - Ok(()) - } - - fn max_chunk_size(&self) -> usize { 256 } - - fn method_str(&self) -> &'static str { "kern.arandom" } -} diff --git a/rand_os/src/fuchsia.rs b/rand_os/src/fuchsia.rs deleted file mode 100644 index ada36776129..00000000000 --- a/rand_os/src/fuchsia.rs +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2018 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Implementation for Fuchsia Zircon - -extern crate fuchsia_cprng; - -use rand_core::Error; -use super::OsRngImpl; - -#[derive(Clone, Debug)] -pub struct OsRng; - -impl OsRngImpl for OsRng { - fn new() -> Result { Ok(OsRng) } - - fn fill_chunk(&mut self, dest: &mut [u8]) -> Result<(), Error> { - fuchsia_cprng::cprng_draw(dest); - Ok(()) - } - - fn method_str(&self) -> &'static str { "cprng_draw" } -} diff --git a/rand_os/src/lib.rs b/rand_os/src/lib.rs index 1b9cb254827..a8499bf2880 100644 --- a/rand_os/src/lib.rs +++ b/rand_os/src/lib.rs @@ -9,116 +9,27 @@ //! Interface to the random number generator of the operating system. //! -//! [`OsRng`] is the preferred external source of entropy for most applications. -//! Commonly it is used to initialize a user-space RNG, which can then be used -//! to generate random values with much less overhead than `OsRng`. -//! -//! You may prefer to use [`EntropyRng`] instead of `OsRng`. It is unlikely, but -//! not entirely theoretical, for `OsRng` to fail. In such cases [`EntropyRng`] -//! falls back on a good alternative entropy source. -//! -//! [`OsRng::new()`] is guaranteed to be very cheap (after the first successful -//! call), and will never consume more than one file handle per process. -//! //! # Usage example //! ``` -//! use rand_os::OsRng; -//! use rand_os::rand_core::RngCore; +//! use rand_os::{OsRng, rand_core::RngCore}; //! -//! let mut os_rng = OsRng::new().unwrap(); //! let mut key = [0u8; 16]; -//! os_rng.fill_bytes(&mut key); -//! let random_u64 = os_rng.next_u64(); +//! OsRng.fill_bytes(&mut key); +//! let random_u64 = OsRng.next_u64(); //! ``` //! -//! # Platform sources -//! -//! | OS | interface -//! |------------------|--------------------------------------------------------- -//! | Linux, Android | [`getrandom`][1] system call if available, otherwise [`/dev/urandom`][2] after reading from `/dev/random` once -//! | Windows | [`RtlGenRandom`][3] -//! | macOS, iOS | [`SecRandomCopyBytes`][4] -//! | FreeBSD | [`kern.arandom`][5] -//! | OpenBSD, Bitrig | [`getentropy`][6] -//! | NetBSD | [`/dev/urandom`][7] after reading from `/dev/random` once -//! | Dragonfly BSD | [`/dev/random`][8] -//! | Solaris, illumos | [`getrandom`][9] system call if available, otherwise [`/dev/random`][10] -//! | Fuchsia OS | [`cprng_draw`][11] -//! | Redox | [`rand:`][12] -//! | CloudABI | [`random_get`][13] -//! | Haiku | `/dev/random` (identical to `/dev/urandom`) -//! | Web browsers | [`Crypto.getRandomValues`][14] (see [Support for WebAssembly and ams.js][14]) -//! | Node.js | [`crypto.randomBytes`][15] (see [Support for WebAssembly and ams.js][16]) -//! -//! Rand doesn't have a blanket implementation for all Unix-like operating -//! systems that reads from `/dev/urandom`. This ensures all supported operating -//! systems are using the recommended interface and respect maximum buffer -//! sizes. -//! -//! ## Support for WebAssembly and ams.js -//! -//! The three Emscripten targets `asmjs-unknown-emscripten`, -//! `wasm32-unknown-emscripten` and `wasm32-experimental-emscripten` use -//! Emscripten's emulation of `/dev/random` on web browsers and Node.js. -//! -//! The bare WASM target `wasm32-unknown-unknown` tries to call the javascript -//! methods directly, using either `stdweb` or `wasm-bindgen` depending on what -//! features are activated for this crate. Note that if both features are -//! enabled `wasm-bindgen` will be used. +//! # Blocking and error handling //! -//! ## Early boot +//! It is possible that when used during early boot the first call to `OsRng` +//! will block until the system's RNG is initialised. It is also possible +//! (though highly unlikely) for `OsRng` to fail on some platforms, most +//! likely due to system mis-configuration. //! -//! It is possible that early in the boot process the OS hasn't had enough time -//! yet to collect entropy to securely seed its RNG, especially on virtual -//! machines. +//! After the first successful call, it is highly unlikely that failures or +//! significant delays will occur (although performance should be expected to +//! be much slower than a user-space PRNG). //! -//! Some operating systems always block the thread until the RNG is securely -//! seeded. This can take anywhere from a few seconds to more than a minute. -//! Others make a best effort to use a seed from before the shutdown and don't -//! document much. -//! -//! A few, Linux, NetBSD and Solaris, offer a choice between blocking, and -//! getting an error. With `try_fill_bytes` we choose to get the error -//! ([`ErrorKind::NotReady`]), while the other methods use a blocking interface. -//! -//! On Linux (when the `genrandom` system call is not available) and on NetBSD -//! reading from `/dev/urandom` never blocks, even when the OS hasn't collected -//! enough entropy yet. As a countermeasure we try to do a single read from -//! `/dev/random` until we know the OS RNG is initialized (and store this in a -//! global static). -//! -//! # Panics and error handling -//! -//! We cannot guarantee that `OsRng` will fail, but if it does, it will likely -//! be either when `OsRng::new()` is first called or when data is first read. -//! If you wish to catch errors early, then test reading of at least one byte -//! from `OsRng` via [`try_fill_bytes`]. If this succeeds, it is extremely -//! unlikely that any further errors will occur. -//! -//! Only [`try_fill_bytes`] is able to report the cause of an error; the other -//! [`RngCore`] methods may (depending on the error kind) retry several times, -//! but must eventually panic if the error persists. -//! -//! [`EntropyRng`]: ../rand/rngs/struct.EntropyRng.html -//! [`try_fill_bytes`]: RngCore::try_fill_bytes -//! [`ErrorKind::NotReady`]: rand_core::ErrorKind -//! -//! [1]: http://man7.org/linux/man-pages/man2/getrandom.2.html -//! [2]: http://man7.org/linux/man-pages/man4/urandom.4.html -//! [3]: https://msdn.microsoft.com/en-us/library/windows/desktop/aa387694.aspx -//! [4]: https://developer.apple.com/documentation/security/1399291-secrandomcopybytes?language=objc -//! [5]: https://www.freebsd.org/cgi/man.cgi?query=random&sektion=4 -//! [6]: https://man.openbsd.org/getentropy.2 -//! [7]: http://netbsd.gw.com/cgi-bin/man-cgi?random+4+NetBSD-current -//! [8]: https://leaf.dragonflybsd.org/cgi/web-man?command=random§ion=4 -//! [9]: https://docs.oracle.com/cd/E88353_01/html/E37841/getrandom-2.html -//! [10]: https://docs.oracle.com/cd/E86824_01/html/E54777/random-7d.html -//! [11]: https://fuchsia.googlesource.com/zircon/+/HEAD/docs/syscalls/cprng_draw.md -//! [12]: https://github.com/redox-os/randd/blob/master/src/main.rs -//! [13]: https://github.com/NuxiNL/cloudabi/blob/v0.20/cloudabi.txt#L1826 -//! [14]: https://www.w3.org/TR/WebCryptoAPI/#Crypto-method-getRandomValues -//! [15]: https://nodejs.org/api/crypto.html#crypto_crypto_randombytes_size_callback -//! [16]: #support-for-webassembly-and-amsjs +//! [getrandom]: https://crates.io/crates/getrandom #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk.png", html_favicon_url = "https://www.rust-lang.org/favicon.ico", html_root_url = "https://rust-random.github.io/rand/")] @@ -128,44 +39,30 @@ #![cfg_attr(feature = "stdweb", recursion_limit="128")] -pub extern crate rand_core; -#[cfg(feature = "log")] -#[macro_use] extern crate log; - -// We have to do it here because we load macros -#[cfg(all(target_arch = "wasm32", not(target_os = "emscripten"), - feature = "wasm-bindgen"))] -extern crate wasm_bindgen; -#[cfg(all(target_arch = "wasm32", not(target_os = "emscripten"), - not(feature = "wasm-bindgen"), - feature = "stdweb"))] -#[macro_use] extern crate stdweb; - -#[cfg(target_env = "sgx")] -extern crate rdrand; +#![no_std] // but see getrandom crate -#[cfg(not(feature = "log"))] -#[macro_use] -mod dummy_log; +pub use rand_core; // re-export -use std::fmt; -use rand_core::{CryptoRng, RngCore, Error, impls}; +use getrandom::getrandom; +use rand_core::{CryptoRng, RngCore, Error, ErrorKind, impls}; -/// A random number generator that retrieves randomness straight from the +/// A random number generator that retrieves randomness from from the /// operating system. -#[derive(Clone)] -pub struct OsRng(imp::OsRng); - -impl fmt::Debug for OsRng { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.0.fmt(f) - } -} +/// +/// This is a zero-sized struct. It can be freely constructed with `OsRng`. +/// +/// The implementation is provided by the [getrandom] crate. Refer to +/// [getrandom] documentation for details. +/// +/// [getrandom]: https://crates.io/crates/getrandom +#[derive(Clone, Copy, Debug)] +pub struct OsRng; impl OsRng { /// Create a new `OsRng`. + #[deprecated(since="0.2.0", note="replace OsRng::new().unwrap() with just OsRng")] pub fn new() -> Result { - imp::OsRng::new().map(OsRng) + Ok(OsRng) } } @@ -181,54 +78,8 @@ impl RngCore for OsRng { } fn fill_bytes(&mut self, dest: &mut [u8]) { - use std::{time, thread}; - - // We cannot return Err(..), so we try to handle before panicking. - const MAX_RETRY_PERIOD: u32 = 10; // max 10s - const WAIT_DUR_MS: u32 = 100; // retry every 100ms - let wait_dur = time::Duration::from_millis(WAIT_DUR_MS as u64); - const RETRY_LIMIT: u32 = (MAX_RETRY_PERIOD * 1000) / WAIT_DUR_MS; - const TRANSIENT_RETRIES: u32 = 8; - let mut err_count = 0; - let mut error_logged = false; - - // Maybe block until the OS RNG is initialized - let mut read = 0; - if let Ok(n) = self.0.test_initialized(dest, true) { read = n }; - let dest = &mut dest[read..]; - - loop { - if let Err(e) = self.try_fill_bytes(dest) { - if err_count >= RETRY_LIMIT { - error!("OsRng failed too many times; last error: {}", e); - panic!("OsRng failed too many times; last error: {}", e); - } - - if e.kind.should_wait() { - if !error_logged { - warn!("OsRng failed; waiting up to {}s and retrying. Error: {}", - MAX_RETRY_PERIOD, e); - error_logged = true; - } - err_count += 1; - thread::sleep(wait_dur); - continue; - } else if e.kind.should_retry() { - if !error_logged { - warn!("OsRng failed; retrying up to {} times. Error: {}", - TRANSIENT_RETRIES, e); - error_logged = true; - } - err_count += (RETRY_LIMIT + TRANSIENT_RETRIES - 1) - / TRANSIENT_RETRIES; // round up - continue; - } else { - error!("OsRng failed: {}", e); - panic!("OsRng fatal error: {}", e); - } - } - - break; + if let Err(e) = self.try_fill_bytes(dest) { + panic!("Error: {}", e); } } @@ -236,205 +87,16 @@ impl RngCore for OsRng { // Some systems do not support reading 0 random bytes. // (And why waste a system call?) if dest.len() == 0 { return Ok(()); } - - let read = self.0.test_initialized(dest, false)?; - let dest = &mut dest[read..]; - - let max = self.0.max_chunk_size(); - if dest.len() <= max { - trace!("OsRng: reading {} bytes via {}", - dest.len(), self.0.method_str()); - } else { - trace!("OsRng: reading {} bytes via {} in {} chunks of {} bytes", - dest.len(), self.0.method_str(), (dest.len() + max) / max, max); - } - for slice in dest.chunks_mut(max) { - self.0.fill_chunk(slice)?; - } - Ok(()) + + getrandom(dest).map_err(|e| + Error::with_cause(ErrorKind::Unavailable, "OsRng failed", e)) } } -trait OsRngImpl where Self: Sized { - // Create a new `OsRng` platform interface. - fn new() -> Result; - - // Fill a chunk with random bytes. - fn fill_chunk(&mut self, dest: &mut [u8]) -> Result<(), Error>; - - // Test whether the OS RNG is initialized. This method may not be possible - // to support cheaply (or at all) on all operating systems. - // - // If `blocking` is set, this will cause the OS the block execution until - // its RNG is initialized. - // - // Random values that are read while this are stored in `dest`, the amount - // of read bytes is returned. - fn test_initialized(&mut self, _dest: &mut [u8], _blocking: bool) - -> Result { Ok(0) } - - // Maximum chunk size supported. - fn max_chunk_size(&self) -> usize { ::std::usize::MAX } - - // Name of the OS interface (used for logging). - fn method_str(&self) -> &'static str; -} - -#[cfg(any(target_os = "linux", target_os = "android", - target_os = "netbsd", target_os = "dragonfly", - target_os = "solaris", target_os = "redox", - target_os = "haiku", target_os = "emscripten", - target_os = "illumos"))] -mod random_device; - -macro_rules! mod_use { - ($cond:meta, $module:ident) => { - #[$cond] - mod $module; - #[$cond] - use $module as imp; - } -} - -mod_use!(cfg(target_os = "android"), linux_android); -mod_use!(cfg(target_os = "bitrig"), openbsd_bitrig); -mod_use!(cfg(target_os = "cloudabi"), cloudabi); -mod_use!(cfg(target_os = "dragonfly"), dragonfly_haiku_emscripten); -mod_use!(cfg(target_os = "emscripten"), dragonfly_haiku_emscripten); -mod_use!(cfg(target_os = "freebsd"), freebsd); -mod_use!(cfg(target_os = "fuchsia"), fuchsia); -mod_use!(cfg(target_os = "haiku"), dragonfly_haiku_emscripten); -mod_use!(cfg(target_os = "ios"), macos); -mod_use!(cfg(target_os = "linux"), linux_android); -mod_use!(cfg(target_os = "macos"), macos); -mod_use!(cfg(target_os = "netbsd"), netbsd); -mod_use!(cfg(target_os = "openbsd"), openbsd_bitrig); -mod_use!(cfg(target_os = "redox"), redox); -mod_use!(cfg(any(target_os = "solaris", target_os = "illumos")), solarish); -mod_use!(cfg(windows), windows); -mod_use!(cfg(target_env = "sgx"), sgx); - -mod_use!( - cfg(all( - target_arch = "wasm32", - not(target_os = "emscripten"), - feature = "wasm-bindgen" - )), - wasm32_bindgen -); - -mod_use!( - cfg(all( - target_arch = "wasm32", - not(target_os = "emscripten"), - not(feature = "wasm-bindgen"), - feature = "stdweb", - )), - wasm32_stdweb -); - -/// Per #678 we use run-time failure where WASM bindings are missing -#[cfg(all( - target_arch = "wasm32", - not(target_os = "emscripten"), - not(feature = "wasm-bindgen"), - not(feature = "stdweb"), -))] -mod imp { - use rand_core::{Error, ErrorKind}; - use super::OsRngImpl; - - #[derive(Clone, Debug)] - pub struct OsRng; - - impl OsRngImpl for OsRng { - fn new() -> Result { - Err(Error::new(ErrorKind::Unavailable, - "OsRng: support for wasm32 requires emscripten, stdweb or wasm-bindgen")) - } - - fn fill_chunk(&mut self, _dest: &mut [u8]) -> Result<(), Error> { - unimplemented!() - } - - fn method_str(&self) -> &'static str { unimplemented!() } - } -} - -#[cfg(not(any( - target_os = "android", - target_os = "bitrig", - target_os = "cloudabi", - target_os = "dragonfly", - target_os = "emscripten", - target_os = "freebsd", - target_os = "fuchsia", - target_os = "haiku", - target_os = "ios", - target_os = "linux", - target_os = "macos", - target_os = "netbsd", - target_os = "openbsd", - target_os = "redox", - target_os = "solaris", - target_os = "illumos", - windows, - target_arch = "wasm32", - target_env = "sgx" -)))] -compile_error!("OS RNG support is not available for this platform"); - -// Due to rustwasm/wasm-bindgen#201 this can't be defined in the inner os -// modules, so hack around it for now and place it at the root. -#[cfg(all(feature = "wasm-bindgen", target_arch = "wasm32"))] -#[doc(hidden)] -#[allow(missing_debug_implementations)] -pub mod __wbg_shims { - - // `extern { type Foo; }` isn't supported on 1.22 syntactically, so use a - // macro to work around that. - macro_rules! rust_122_compat { - ($($t:tt)*) => ($($t)*) - } - - rust_122_compat! { - extern crate wasm_bindgen; - - pub use wasm_bindgen::prelude::*; - - #[wasm_bindgen] - extern "C" { - pub type Function; - #[wasm_bindgen(constructor)] - pub fn new(s: &str) -> Function; - #[wasm_bindgen(method)] - pub fn call(this: &Function, self_: &JsValue) -> JsValue; - - pub type This; - #[wasm_bindgen(method, getter, structural, js_name = self)] - pub fn self_(me: &This) -> JsValue; - #[wasm_bindgen(method, getter, structural)] - pub fn crypto(me: &This) -> JsValue; - - #[derive(Clone, Debug)] - pub type BrowserCrypto; - - // TODO: these `structural` annotations here ideally wouldn't be here to - // avoid a JS shim, but for now with feature detection they're - // unavoidable. - #[wasm_bindgen(method, js_name = getRandomValues, structural, getter)] - pub fn get_random_values_fn(me: &BrowserCrypto) -> JsValue; - #[wasm_bindgen(method, js_name = getRandomValues, structural)] - pub fn get_random_values(me: &BrowserCrypto, buf: &mut [u8]); - - #[wasm_bindgen(js_name = require)] - pub fn node_require(s: &str) -> NodeCrypto; - - #[derive(Clone, Debug)] - pub type NodeCrypto; - - #[wasm_bindgen(method, js_name = randomFillSync, structural)] - pub fn random_fill_sync(me: &NodeCrypto, buf: &mut [u8]); - } - } +#[test] +fn test_os_rng() { + let x = OsRng.next_u64(); + let y = OsRng.next_u64(); + assert!(x != 0); + assert!(x != y); } diff --git a/rand_os/src/linux_android.rs b/rand_os/src/linux_android.rs deleted file mode 100644 index 255c2200210..00000000000 --- a/rand_os/src/linux_android.rs +++ /dev/null @@ -1,186 +0,0 @@ -// Copyright 2018 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Implementation for Linux / Android - -extern crate libc; - -use rand_core::{Error, ErrorKind}; -use super::random_device; -use super::OsRngImpl; - -use std::io; -use std::io::Read; -use std::fs::{File, OpenOptions}; -use std::os::unix::fs::OpenOptionsExt; -use std::sync::atomic::{AtomicBool, Ordering}; -#[allow(deprecated)] // Required for compatibility with Rust < 1.24. -use std::sync::atomic::ATOMIC_BOOL_INIT; -use std::sync::{Once, ONCE_INIT}; - -#[derive(Clone, Debug)] -pub struct OsRng { - method: OsRngMethod, - initialized: bool, -} - -#[derive(Clone, Debug)] -enum OsRngMethod { - GetRandom, - RandomDevice, -} - -impl OsRngImpl for OsRng { - fn new() -> Result { - if is_getrandom_available() { - return Ok(OsRng { method: OsRngMethod::GetRandom, - initialized: false }); - } - random_device::open("/dev/urandom", &|p| File::open(p))?; - Ok(OsRng { method: OsRngMethod::RandomDevice, initialized: false }) - } - - fn fill_chunk(&mut self, dest: &mut [u8]) -> Result<(), Error> { - match self.method { - OsRngMethod::GetRandom => getrandom_try_fill(dest, false), - OsRngMethod::RandomDevice => random_device::read(dest), - } - } - - fn test_initialized(&mut self, dest: &mut [u8], blocking: bool) - -> Result - { - #[allow(deprecated)] - static OS_RNG_INITIALIZED: AtomicBool = ATOMIC_BOOL_INIT; - if !self.initialized { - self.initialized = OS_RNG_INITIALIZED.load(Ordering::Relaxed); - } - if self.initialized { return Ok(0); } - - let result = match self.method { - OsRngMethod::GetRandom => { - getrandom_try_fill(dest, blocking)?; - Ok(dest.len()) - } - OsRngMethod::RandomDevice => { - info!("OsRng: testing random device /dev/random"); - let mut file = OpenOptions::new() - .read(true) - .custom_flags(if blocking { 0 } else { libc::O_NONBLOCK }) - .open("/dev/random") - .map_err(random_device::map_err)?; - file.read(&mut dest[..1]).map_err(random_device::map_err)?; - Ok(1) - } - }; - OS_RNG_INITIALIZED.store(true, Ordering::Relaxed); - self.initialized = true; - result - } - - fn method_str(&self) -> &'static str { - match self.method { - OsRngMethod::GetRandom => "getrandom", - OsRngMethod::RandomDevice => "/dev/urandom", - } - } -} - -#[cfg(target_arch = "x86_64")] -const NR_GETRANDOM: libc::c_long = 318; -#[cfg(target_arch = "x86")] -const NR_GETRANDOM: libc::c_long = 355; -#[cfg(target_arch = "arm")] -const NR_GETRANDOM: libc::c_long = 384; -#[cfg(target_arch = "aarch64")] -const NR_GETRANDOM: libc::c_long = 278; - #[cfg(target_arch = "s390x")] -const NR_GETRANDOM: libc::c_long = 349; -#[cfg(target_arch = "powerpc")] -const NR_GETRANDOM: libc::c_long = 359; -#[cfg(target_arch = "powerpc64")] -const NR_GETRANDOM: libc::c_long = 359; -#[cfg(target_arch = "mips")] // old ABI -const NR_GETRANDOM: libc::c_long = 4353; -#[cfg(target_arch = "mips64")] -const NR_GETRANDOM: libc::c_long = 5313; -#[cfg(target_arch = "sparc")] -const NR_GETRANDOM: libc::c_long = 347; -#[cfg(target_arch = "sparc64")] -const NR_GETRANDOM: libc::c_long = 347; -#[cfg(not(any(target_arch = "x86_64", target_arch = "x86", - target_arch = "arm", target_arch = "aarch64", - target_arch = "s390x", target_arch = "powerpc", - target_arch = "powerpc64", target_arch = "mips", - target_arch = "mips64", target_arch = "sparc", - target_arch = "sparc64")))] -const NR_GETRANDOM: libc::c_long = 0; - -fn getrandom(buf: &mut [u8], blocking: bool) -> libc::c_long { - const GRND_NONBLOCK: libc::c_uint = 0x0001; - - if NR_GETRANDOM == 0 { return -1 }; - - unsafe { - libc::syscall(NR_GETRANDOM, buf.as_mut_ptr(), buf.len(), - if blocking { 0 } else { GRND_NONBLOCK }) - } -} - -fn getrandom_try_fill(dest: &mut [u8], blocking: bool) -> Result<(), Error> { - let mut read = 0; - while read < dest.len() { - let result = getrandom(&mut dest[read..], blocking); - if result == -1 { - let err = io::Error::last_os_error(); - let kind = err.kind(); - if kind == io::ErrorKind::Interrupted { - continue; - } else if kind == io::ErrorKind::WouldBlock { - return Err(Error::with_cause( - ErrorKind::NotReady, - "getrandom not ready", - err, - )); - } else { - return Err(Error::with_cause( - ErrorKind::Unavailable, - "unexpected getrandom error", - err, - )); - } - } else { - read += result as usize; - } - } - Ok(()) -} - -fn is_getrandom_available() -> bool { - static CHECKER: Once = ONCE_INIT; - #[allow(deprecated)] - static AVAILABLE: AtomicBool = ATOMIC_BOOL_INIT; - - if NR_GETRANDOM == 0 { return false }; - - CHECKER.call_once(|| { - debug!("OsRng: testing getrandom"); - let mut buf: [u8; 0] = []; - let result = getrandom(&mut buf, false); - let available = if result == -1 { - let err = io::Error::last_os_error().raw_os_error(); - err != Some(libc::ENOSYS) - } else { - true - }; - AVAILABLE.store(available, Ordering::Relaxed); - info!("OsRng: using {}", if available { "getrandom" } else { "/dev/urandom" }); - }); - - AVAILABLE.load(Ordering::Relaxed) -} diff --git a/rand_os/src/macos.rs b/rand_os/src/macos.rs deleted file mode 100644 index 6c67251c0a7..00000000000 --- a/rand_os/src/macos.rs +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2018 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Implementation for MacOS / iOS - -extern crate libc; - -use rand_core::{Error, ErrorKind}; -use super::OsRngImpl; - -use std::io; -use self::libc::{c_int, size_t}; - -#[derive(Clone, Debug)] -pub struct OsRng; - -enum SecRandom {} - -#[allow(non_upper_case_globals)] -const kSecRandomDefault: *const SecRandom = 0 as *const SecRandom; - -#[link(name = "Security", kind = "framework")] -extern { - fn SecRandomCopyBytes(rnd: *const SecRandom, - count: size_t, bytes: *mut u8) -> c_int; -} - -impl OsRngImpl for OsRng { - fn new() -> Result { Ok(OsRng) } - - fn fill_chunk(&mut self, dest: &mut [u8]) -> Result<(), Error> { - let ret = unsafe { - SecRandomCopyBytes(kSecRandomDefault, - dest.len() as size_t, - dest.as_mut_ptr()) - }; - if ret == -1 { - Err(Error::with_cause( - ErrorKind::Unavailable, - "couldn't generate random bytes", - io::Error::last_os_error())) - } else { - Ok(()) - } - } - - fn method_str(&self) -> &'static str { "SecRandomCopyBytes" } -} diff --git a/rand_os/src/netbsd.rs b/rand_os/src/netbsd.rs deleted file mode 100644 index 34517bfeb3f..00000000000 --- a/rand_os/src/netbsd.rs +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2018 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Implementation for NetBSD - -use rand_core::Error; -use super::random_device; -use super::OsRngImpl; - -use std::fs::File; -use std::io::Read; -use std::sync::atomic::{AtomicBool, Ordering}; -#[allow(deprecated)] // Required for compatibility with Rust < 1.24. -use std::sync::atomic::ATOMIC_BOOL_INIT; - -#[derive(Clone, Debug)] -pub struct OsRng { initialized: bool } - -impl OsRngImpl for OsRng { - fn new() -> Result { - random_device::open("/dev/urandom", &|p| File::open(p))?; - Ok(OsRng { initialized: false }) - } - - fn fill_chunk(&mut self, dest: &mut [u8]) -> Result<(), Error> { - random_device::read(dest) - } - - // Read a single byte from `/dev/random` to determine if the OS RNG is - // already seeded. NetBSD always blocks if not yet ready. - fn test_initialized(&mut self, dest: &mut [u8], _blocking: bool) - -> Result - { - #[allow(deprecated)] - static OS_RNG_INITIALIZED: AtomicBool = ATOMIC_BOOL_INIT; - if !self.initialized { - self.initialized = OS_RNG_INITIALIZED.load(Ordering::Relaxed); - } - if self.initialized { return Ok(0); } - - info!("OsRng: testing random device /dev/random"); - let mut file = - File::open("/dev/random").map_err(random_device::map_err)?; - file.read(&mut dest[..1]).map_err(random_device::map_err)?; - - OS_RNG_INITIALIZED.store(true, Ordering::Relaxed); - self.initialized = true; - Ok(1) - } - - fn method_str(&self) -> &'static str { "/dev/urandom" } -} diff --git a/rand_os/src/openbsd_bitrig.rs b/rand_os/src/openbsd_bitrig.rs deleted file mode 100644 index c9b35a6c569..00000000000 --- a/rand_os/src/openbsd_bitrig.rs +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2018 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Implementation for OpenBSD / Bitrig - -extern crate libc; - -use rand_core::{Error, ErrorKind}; -use super::OsRngImpl; - -use std::io; - -#[derive(Clone, Debug)] -pub struct OsRng; - -impl OsRngImpl for OsRng { - fn new() -> Result { Ok(OsRng) } - - fn fill_chunk(&mut self, dest: &mut [u8]) -> Result<(), Error> { - let ret = unsafe { - libc::getentropy(dest.as_mut_ptr() as *mut libc::c_void, dest.len()) - }; - if ret == -1 { - return Err(Error::with_cause( - ErrorKind::Unavailable, - "getentropy failed", - io::Error::last_os_error())); - } - Ok(()) - } - - fn max_chunk_size(&self) -> usize { 256 } - - fn method_str(&self) -> &'static str { "getentropy" } -} diff --git a/rand_os/src/random_device.rs b/rand_os/src/random_device.rs deleted file mode 100644 index 5da91940f2d..00000000000 --- a/rand_os/src/random_device.rs +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2018 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Helper functions to read from a random device such as `/dev/urandom`. -//! -//! All instances use a single internal file handle, to prevent possible -//! exhaustion of file descriptors. -use rand_core::{Error, ErrorKind}; -use std::fs::File; -use std::io; -use std::io::Read; -use std::sync::{Once, Mutex, ONCE_INIT}; - -// TODO: remove outer Option when `Mutex::new(None)` is a constant expression -static mut READ_RNG_FILE: Option>> = None; -static READ_RNG_ONCE: Once = ONCE_INIT; - -#[allow(unused)] -pub fn open(path: &'static str, open_fn: F) -> Result<(), Error> - where F: Fn(&'static str) -> Result -{ - READ_RNG_ONCE.call_once(|| { - unsafe { READ_RNG_FILE = Some(Mutex::new(None)) } - }); - - // We try opening the file outside the `call_once` fn because we cannot - // clone the error, thus we must retry on failure. - - let mutex = unsafe { READ_RNG_FILE.as_ref().unwrap() }; - let mut guard = mutex.lock().unwrap(); - if (*guard).is_none() { - info!("OsRng: opening random device {}", path); - let file = open_fn(path).map_err(map_err)?; - *guard = Some(file); - }; - Ok(()) -} - -pub fn read(dest: &mut [u8]) -> Result<(), Error> { - // We expect this function only to be used after `random_device::open` - // was succesful. Therefore we can assume that our memory was set with a - // valid object. - let mutex = unsafe { READ_RNG_FILE.as_ref().unwrap() }; - let mut guard = mutex.lock().unwrap(); - let file = (*guard).as_mut().unwrap(); - - // Use `std::io::read_exact`, which retries on `ErrorKind::Interrupted`. - file.read_exact(dest).map_err(|err| { - Error::with_cause(ErrorKind::Unavailable, - "error reading random device", err) - }) - -} - -pub fn map_err(err: io::Error) -> Error { - match err.kind() { - io::ErrorKind::Interrupted => - Error::new(ErrorKind::Transient, "interrupted"), - io::ErrorKind::WouldBlock => - Error::with_cause(ErrorKind::NotReady, - "OS RNG not yet seeded", err), - _ => Error::with_cause(ErrorKind::Unavailable, - "error while opening random device", err) - } -} diff --git a/rand_os/src/redox.rs b/rand_os/src/redox.rs deleted file mode 100644 index 36fae2666d6..00000000000 --- a/rand_os/src/redox.rs +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2018 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Implementation for Redox - -use rand_core::Error; -use super::random_device; -use super::OsRngImpl; -use std::fs::File; - -#[derive(Clone, Debug)] -pub struct OsRng(); - -impl OsRngImpl for OsRng { - fn new() -> Result { - random_device::open("rand:", &|p| File::open(p))?; - Ok(OsRng()) - } - - fn fill_chunk(&mut self, dest: &mut [u8]) -> Result<(), Error> { - random_device::read(dest) - } - - fn method_str(&self) -> &'static str { "'rand:'" } -} diff --git a/rand_os/src/sgx.rs b/rand_os/src/sgx.rs deleted file mode 100644 index 43ae0ef79f1..00000000000 --- a/rand_os/src/sgx.rs +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2018 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use super::OsRngImpl; -use Error; -use rdrand::RdRand; -use rand_core::RngCore; -use std::fmt::{Debug, Formatter, Result as FmtResult}; - -#[derive(Clone)] -pub struct OsRng{ - gen: RdRand -} - -impl OsRngImpl for OsRng { - fn new() -> Result { - let rng = RdRand::new()?; - Ok(OsRng{ gen: rng }) - } - - fn fill_chunk(&mut self, dest: &mut [u8]) -> Result<(), Error> { - self.gen.try_fill_bytes(dest) - } - - fn method_str(&self) -> &'static str { "RDRAND" } -} - -impl Debug for OsRng { - fn fmt(&self, f: &mut Formatter) -> FmtResult { - f.debug_struct("OsRng") - .finish() - } -} diff --git a/rand_os/src/solarish.rs b/rand_os/src/solarish.rs deleted file mode 100644 index 471768adc7d..00000000000 --- a/rand_os/src/solarish.rs +++ /dev/null @@ -1,195 +0,0 @@ -// Copyright 2018 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Implementation for the Solaris family -//! -//! Read from `/dev/random`, with chunks of limited size (1040 bytes). -//! `/dev/random` uses the Hash_DRBG with SHA512 algorithm from NIST SP 800-90A. -//! `/dev/urandom` uses the FIPS 186-2 algorithm, which is considered less -//! secure. We choose to read from `/dev/random`. -//! -//! Since Solaris 11.3 the `getrandom` syscall is available. To make sure we can -//! compile on both Solaris and on OpenSolaris derivatives, that do not have the -//! function, we do a direct syscall instead of calling a library function. -//! -//! We have no way to differentiate between Solaris, illumos, SmartOS, etc. -extern crate libc; - -use rand_core::{Error, ErrorKind}; -use super::random_device; -use super::OsRngImpl; - -use std::io; -use std::io::Read; -use std::fs::{File, OpenOptions}; -use std::os::unix::fs::OpenOptionsExt; -use std::sync::atomic::{AtomicBool, Ordering, AtomicUsize}; -#[allow(deprecated)] // Required for compatibility with Rust < 1.24. -use std::sync::atomic::ATOMIC_BOOL_INIT; -use std::cmp; -use std::mem; - -#[derive(Clone, Debug)] -pub struct OsRng { - method: OsRngMethod, - initialized: bool, -} - -#[derive(Clone, Debug)] -enum OsRngMethod { - GetRandom, - RandomDevice, -} - -impl OsRngImpl for OsRng { - fn new() -> Result { - if is_getrandom_available() { - return Ok(OsRng { method: OsRngMethod::GetRandom, - initialized: false }); - } - let open = |p| OpenOptions::new() - .read(true) - .custom_flags(libc::O_NONBLOCK) - .open(p); - random_device::open("/dev/random", &open)?; - Ok(OsRng { method: OsRngMethod::RandomDevice, initialized: false }) - } - - fn fill_chunk(&mut self, dest: &mut [u8]) -> Result<(), Error> { - match self.method { - OsRngMethod::GetRandom => getrandom_try_fill(dest, false), - OsRngMethod::RandomDevice => random_device::read(dest), - } - } - - fn test_initialized(&mut self, dest: &mut [u8], blocking: bool) - -> Result - { - #[allow(deprecated)] - static OS_RNG_INITIALIZED: AtomicBool = ATOMIC_BOOL_INIT; - if !self.initialized { - self.initialized = OS_RNG_INITIALIZED.load(Ordering::Relaxed); - } - if self.initialized { return Ok(0); } - - let chunk_len = cmp::min(1024, dest.len()); - let dest = &mut dest[..chunk_len]; - - match self.method { - OsRngMethod::GetRandom => getrandom_try_fill(dest, blocking)?, - OsRngMethod::RandomDevice => { - if blocking { - info!("OsRng: testing random device /dev/random"); - // We already have a non-blocking handle, but now need a - // blocking one. Not much choice except opening it twice - let mut file = File::open("/dev/random") - .map_err(random_device::map_err)?; - file.read(dest).map_err(random_device::map_err)?; - } else { - self.fill_chunk(dest)?; - } - } - }; - OS_RNG_INITIALIZED.store(true, Ordering::Relaxed); - self.initialized = true; - Ok(chunk_len) - } - - fn max_chunk_size(&self) -> usize { - // This is the largest size that's guaranteed to not block across - // all the Solarish platforms, though some may allow for larger - // sizes. - 256 - } - - fn method_str(&self) -> &'static str { - match self.method { - OsRngMethod::GetRandom => "getrandom", - OsRngMethod::RandomDevice => "/dev/random", - } - } -} - -#[cfg(target_os = "illumos")] -type GetRandomFn = unsafe extern fn(*mut u8, libc::size_t, libc::c_uint) - -> libc::ssize_t; -#[cfg(target_os = "solaris")] -type GetRandomFn = unsafe extern fn(*mut u8, libc::size_t, libc::c_uint) - -> libc::c_int; - -// Use dlsym to determine if getrandom(2) is present in libc. On Solarish -// systems, the syscall interface is not stable and can change between -// updates. Even worse, issuing unsupported syscalls will cause the system -// to generate a SIGSYS signal (usually terminating the program). -// Instead the stable APIs are exposed via libc. Cache the result of the -// lookup for future calls. This is loosely modeled after the -// libstd::sys::unix::weak macro which unfortunately is not exported. -fn fetch() -> Option { - static FPTR: AtomicUsize = AtomicUsize::new(1); - - if FPTR.load(Ordering::SeqCst) == 1 { - let name = "getrandom\0"; - let addr = unsafe { - libc::dlsym(libc::RTLD_DEFAULT, name.as_ptr() as *const _) as usize - }; - FPTR.store(addr, Ordering::SeqCst); - } - - let ptr = FPTR.load(Ordering::SeqCst); - unsafe { - mem::transmute::>(ptr) - } -} - -fn getrandom(buf: &mut [u8], blocking: bool) -> libc::ssize_t { - const GRND_NONBLOCK: libc::c_uint = 0x0001; - const GRND_RANDOM: libc::c_uint = 0x0002; - - if let Some(rand) = fetch() { - let flag = if blocking { 0 } else { GRND_NONBLOCK } | GRND_RANDOM; - unsafe { - rand(buf.as_mut_ptr(), buf.len(), flag) as libc::ssize_t - } - } else { - -1 - } -} - -fn getrandom_try_fill(dest: &mut [u8], blocking: bool) -> Result<(), Error> { - let result = getrandom(dest, blocking); - if result == -1 || result == 0 { - let err = io::Error::last_os_error(); - let kind = err.kind(); - if kind == io::ErrorKind::WouldBlock { - return Err(Error::with_cause( - ErrorKind::NotReady, - "getrandom not ready", - err, - )); - } else { - return Err(Error::with_cause( - ErrorKind::Unavailable, - "unexpected getrandom error", - err, - )); - } - } else if result != dest.len() as libc::ssize_t { - return Err(Error::new(ErrorKind::Unavailable, - "unexpected getrandom error")); - } - Ok(()) -} - -fn is_getrandom_available() -> bool { - let available = match fetch() { - Some(_) => true, - None => false, - }; - info!("OsRng: using {}", if available { "getrandom" } else { "/dev/random" }); - available -} diff --git a/rand_os/src/wasm32_bindgen.rs b/rand_os/src/wasm32_bindgen.rs deleted file mode 100644 index 5ab2d84173b..00000000000 --- a/rand_os/src/wasm32_bindgen.rs +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright 2018 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Implementation for WASM via wasm-bindgen - -use rand_core::{Error, ErrorKind}; -use super::OsRngImpl; -use super::__wbg_shims::*; - -use wasm_bindgen::prelude::*; - - -#[derive(Clone, Debug)] -pub enum OsRng { - Node(NodeCrypto), - Browser(BrowserCrypto), -} - -impl OsRngImpl for OsRng { - fn new() -> Result { - // First up we need to detect if we're running in node.js or a - // browser. To do this we get ahold of the `this` object (in a bit - // of a roundabout fashion). - // - // Once we have `this` we look at its `self` property, which is - // only defined on the web (either a main window or web worker). - let this = Function::new("return this").call(&JsValue::undefined()); - assert!(this != JsValue::undefined()); - let this = This::from(this); - let is_browser = this.self_() != JsValue::undefined(); - - if !is_browser { - return Ok(OsRng::Node(node_require("crypto"))) - } - - // If `self` is defined then we're in a browser somehow (main window - // or web worker). Here we want to try to use - // `crypto.getRandomValues`, but if `crypto` isn't defined we assume - // we're in an older web browser and the OS RNG isn't available. - let crypto = this.crypto(); - if crypto.is_undefined() { - let msg = "self.crypto is undefined"; - return Err(Error::new(ErrorKind::Unavailable, msg)) - } - - // Test if `crypto.getRandomValues` is undefined as well - let crypto: BrowserCrypto = crypto.into(); - if crypto.get_random_values_fn().is_undefined() { - let msg = "crypto.getRandomValues is undefined"; - return Err(Error::new(ErrorKind::Unavailable, msg)) - } - - // Ok! `self.crypto.getRandomValues` is a defined value, so let's - // assume we can do browser crypto. - Ok(OsRng::Browser(crypto)) - } - - fn fill_chunk(&mut self, dest: &mut [u8]) -> Result<(), Error> { - match *self { - OsRng::Node(ref n) => n.random_fill_sync(dest), - OsRng::Browser(ref n) => n.get_random_values(dest), - } - Ok(()) - } - - fn max_chunk_size(&self) -> usize { - match *self { - OsRng::Node(_) => usize::max_value(), - OsRng::Browser(_) => { - // see https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues - // - // where it says: - // - // > A QuotaExceededError DOMException is thrown if the - // > requested length is greater than 65536 bytes. - 65536 - } - } - } - - fn method_str(&self) -> &'static str { - match *self { - OsRng::Node(_) => "crypto.randomFillSync", - OsRng::Browser(_) => "crypto.getRandomValues", - } - } -} diff --git a/rand_os/src/wasm32_stdweb.rs b/rand_os/src/wasm32_stdweb.rs deleted file mode 100644 index 3be0ce6a6fa..00000000000 --- a/rand_os/src/wasm32_stdweb.rs +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright 2018 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Implementation for WASM via stdweb - -use std::mem; -use stdweb::unstable::TryInto; -use stdweb::web::error::Error as WebError; -use rand_core::{Error, ErrorKind}; -use super::OsRngImpl; - -#[derive(Clone, Debug)] -enum OsRngMethod { - Browser, - Node -} - -#[derive(Clone, Debug)] -pub struct OsRng(OsRngMethod); - -impl OsRngImpl for OsRng { - fn new() -> Result { - let result = js! { - try { - if ( - typeof self === "object" && - typeof self.crypto === "object" && - typeof self.crypto.getRandomValues === "function" - ) { - return { success: true, ty: 1 }; - } - - if (typeof require("crypto").randomBytes === "function") { - return { success: true, ty: 2 }; - } - - return { success: false, error: new Error("not supported") }; - } catch(err) { - return { success: false, error: err }; - } - }; - - if js!{ return @{ result.as_ref() }.success } == true { - let ty = js!{ return @{ result }.ty }; - - if ty == 1 { Ok(OsRng(OsRngMethod::Browser)) } - else if ty == 2 { Ok(OsRng(OsRngMethod::Node)) } - else { unreachable!() } - } else { - let err: WebError = js!{ return @{ result }.error }.try_into().unwrap(); - Err(Error::with_cause(ErrorKind::Unavailable, "WASM Error", err)) - } - } - - - fn fill_chunk(&mut self, dest: &mut [u8]) -> Result<(), Error> { - assert_eq!(mem::size_of::(), 4); - - let len = dest.len() as u32; - let ptr = dest.as_mut_ptr() as i32; - - let result = match self.0 { - OsRngMethod::Browser => js! { - try { - let array = new Uint8Array(@{ len }); - self.crypto.getRandomValues(array); - HEAPU8.set(array, @{ ptr }); - - return { success: true }; - } catch(err) { - return { success: false, error: err }; - } - }, - OsRngMethod::Node => js! { - try { - let bytes = require("crypto").randomBytes(@{ len }); - HEAPU8.set(new Uint8Array(bytes), @{ ptr }); - - return { success: true }; - } catch(err) { - return { success: false, error: err }; - } - } - }; - - if js!{ return @{ result.as_ref() }.success } == true { - Ok(()) - } else { - let err: WebError = js!{ return @{ result }.error }.try_into().unwrap(); - Err(Error::with_cause(ErrorKind::Unexpected, "WASM Error", err)) - } - } - - fn max_chunk_size(&self) -> usize { 65536 } - - fn method_str(&self) -> &'static str { - match self.0 { - OsRngMethod::Browser => "Crypto.getRandomValues", - OsRngMethod::Node => "crypto.randomBytes", - } - } -} diff --git a/rand_os/src/windows.rs b/rand_os/src/windows.rs deleted file mode 100644 index 6b06c7abf68..00000000000 --- a/rand_os/src/windows.rs +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2018 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Implementation for Windows - -extern crate winapi; - -use rand_core::{Error, ErrorKind}; -use super::OsRngImpl; - -use std::io; - -use self::winapi::shared::minwindef::ULONG; -use self::winapi::um::ntsecapi::RtlGenRandom; -use self::winapi::um::winnt::PVOID; - -#[derive(Clone, Debug)] -pub struct OsRng; - -impl OsRngImpl for OsRng { - fn new() -> Result { Ok(OsRng) } - - fn fill_chunk(&mut self, dest: &mut [u8]) -> Result<(), Error> { - let ret = unsafe { - RtlGenRandom(dest.as_mut_ptr() as PVOID, dest.len() as ULONG) - }; - if ret == 0 { - return Err(Error::with_cause( - ErrorKind::Unavailable, - "couldn't generate random bytes", - io::Error::last_os_error())); - } - Ok(()) - } - - fn max_chunk_size(&self) -> usize { ::max_value() as usize } - - fn method_str(&self) -> &'static str { "RtlGenRandom" } -} diff --git a/rand_os/tests/mod.rs b/rand_os/tests/mod.rs deleted file mode 100644 index 2130e169bbf..00000000000 --- a/rand_os/tests/mod.rs +++ /dev/null @@ -1,80 +0,0 @@ -extern crate rand_os; - -use rand_os::rand_core::RngCore; -use rand_os::OsRng; - -#[test] -fn test_os_rng() { - let mut r = OsRng::new().unwrap(); - - r.next_u32(); - r.next_u64(); - - let mut v1 = [0u8; 1000]; - r.fill_bytes(&mut v1); - - let mut v2 = [0u8; 1000]; - r.fill_bytes(&mut v2); - - let mut n_diff_bits = 0; - for i in 0..v1.len() { - n_diff_bits += (v1[i] ^ v2[i]).count_ones(); - } - - // Check at least 1 bit per byte differs. p(failure) < 1e-1000 with random input. - assert!(n_diff_bits >= v1.len() as u32); -} - -#[test] -fn test_os_rng_empty() { - let mut r = OsRng::new().unwrap(); - - let mut empty = [0u8; 0]; - r.fill_bytes(&mut empty); -} - -#[test] -fn test_os_rng_huge() { - let mut r = OsRng::new().unwrap(); - - let mut huge = [0u8; 100_000]; - r.fill_bytes(&mut huge); -} - -#[cfg(not(any(target_arch = "wasm32", target_arch = "asmjs")))] -#[test] -fn test_os_rng_tasks() { - use std::sync::mpsc::channel; - use std::thread; - - let mut txs = vec!(); - for _ in 0..20 { - let (tx, rx) = channel(); - txs.push(tx); - - thread::spawn(move|| { - // wait until all the tasks are ready to go. - rx.recv().unwrap(); - - // deschedule to attempt to interleave things as much - // as possible (XXX: is this a good test?) - let mut r = OsRng::new().unwrap(); - thread::yield_now(); - let mut v = [0u8; 1000]; - - for _ in 0..100 { - r.next_u32(); - thread::yield_now(); - r.next_u64(); - thread::yield_now(); - r.fill_bytes(&mut v); - thread::yield_now(); - } - }); - } - - // start all the tasks - for tx in txs.iter() { - tx.send(()).unwrap(); - } -} diff --git a/src/rngs/adapter/reseeding.rs b/src/rngs/adapter/reseeding.rs index 9c1807cdd05..a1ebe78f739 100644 --- a/src/rngs/adapter/reseeding.rs +++ b/src/rngs/adapter/reseeding.rs @@ -67,10 +67,7 @@ use rand_core::block::{BlockRngCore, BlockRng}; /// use rand::rngs::adapter::ReseedingRng; /// /// let prng = ChaChaCore::from_entropy(); -// FIXME: it is better to use EntropyRng as reseeder, but that doesn't implement -// clone yet. -/// let reseeder = OsRng::new().unwrap(); -/// let mut reseeding_rng = ReseedingRng::new(prng, 0, reseeder); +/// let mut reseeding_rng = ReseedingRng::new(prng, 0, OsRng); /// /// println!("{}", reseeding_rng.gen::()); /// diff --git a/src/rngs/entropy.rs b/src/rngs/entropy.rs index 9a2affdffc1..30cc54953df 100644 --- a/src/rngs/entropy.rs +++ b/src/rngs/entropy.rs @@ -197,8 +197,8 @@ pub struct Os(rngs::OsRng); #[cfg(feature="rand_os")] impl EntropySource for Os { fn new_and_fill(dest: &mut [u8]) -> Result { - let mut rng = rngs::OsRng::new()?; - rng.try_fill_bytes(dest)?; + let rng = rngs::OsRng; + rngs::OsRng.try_fill_bytes(dest)?; Ok(Os(rng)) } diff --git a/tests/wasm_bindgen/src/lib.rs b/tests/wasm_bindgen/src/lib.rs index 913f33b9b84..6610b8cbdbc 100644 --- a/tests/wasm_bindgen/src/lib.rs +++ b/tests/wasm_bindgen/src/lib.rs @@ -26,7 +26,7 @@ pub fn generate_from_seed(seed: u32) -> i32 { #[wasm_bindgen] pub fn generate_from_os_rand() -> i32 { - OsRng::new().unwrap().gen() + OsRng.gen() } #[wasm_bindgen] From 23e1f045c09464f888372e4bbc531ba49e22c2f4 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Tue, 2 Apr 2019 13:05:01 +0100 Subject: [PATCH 2/9] Re-implement OsRng in Rand This is controversial: a small amount of code redundancy to avoid an extra import. --- Cargo.toml | 8 ++--- README.md | 6 ++-- rand_jitter/README.md | 4 +-- src/lib.rs | 4 +-- src/rngs/adapter/read.rs | 2 +- src/rngs/entropy.rs | 6 ++-- src/rngs/mod.rs | 5 ++- src/rngs/os.rs | 67 ++++++++++++++++++++++++++++++++++++++++ 8 files changed, 84 insertions(+), 18 deletions(-) create mode 100644 src/rngs/os.rs diff --git a/Cargo.toml b/Cargo.toml index 340fbb226f2..8b7fce000c4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,14 +22,14 @@ appveyor = { repository = "rust-random/rand" } [features] default = ["std"] # without "std" rand uses libcore nightly = ["simd_support"] # enables all features requiring nightly rust -std = ["rand_core/std", "alloc", "rand_os", "rand_jitter/std"] +std = ["rand_core/std", "alloc", "getrandom", "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 serde1 = ["rand_core/serde1", "rand_isaac/serde1", "rand_xorshift/serde1"] # enables serialization for PRNGs # re-export optional WASM dependencies to avoid breakage: -wasm-bindgen = ["rand_os/wasm-bindgen"] -stdweb = ["rand_os/stdweb"] +wasm-bindgen = ["getrandom/wasm-bindgen"] +stdweb = ["getrandom/stdweb"] [workspace] members = [ @@ -50,8 +50,8 @@ members = [ rand_core = { path = "rand_core", version = "0.4" } rand_pcg = { path = "rand_pcg", version = "0.1" } rand_jitter = { path = "rand_jitter", version = "0.1" } -rand_os = { path = "rand_os", version = "0.2", optional = true } rand_hc = { path = "rand_hc", version = "0.1" } +getrandom = { version = "0.1", optional = true } log = { version = "0.4", optional = true } [dependencies.packed_simd] diff --git a/README.md b/README.md index 541a84b69d5..226ed359da1 100644 --- a/README.md +++ b/README.md @@ -75,11 +75,11 @@ pinned version of Rustc if you require compatibility with a specific version. ## Crate Features -Rand is built with the `std` and `rand_os` features enabled by default: +Rand is built with the `std` and `getrandom` features enabled by default: - `std` enables functionality dependent on the `std` lib and implies `alloc` - and `rand_os` -- `rand_os` enables the `rand_os` crate, `rngs::OsRng` and enables its usage; + and `getrandom` +- `getrandom` is an optional crate, providing the code behind `rngs::OsRng`; the continued existance of this feature is not guaranteed so users are encouraged to specify `std` instead diff --git a/rand_jitter/README.md b/rand_jitter/README.md index e64e5825702..8e0805faeb7 100644 --- a/rand_jitter/README.md +++ b/rand_jitter/README.md @@ -1,10 +1,10 @@ # 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) +[![Latest version](https://img.shields.io/crates/v/rand_jitter.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) +[![API](https://docs.rs/rand_jitter/badge.svg)](https://docs.rs/rand_jitter) [![Minimum rustc version](https://img.shields.io/badge/rustc-1.32+-lightgray.svg)](https://github.com/rust-random/rand#rust-version-requirements) Non-physical true random number generator based on timing jitter. diff --git a/src/lib.rs b/src/lib.rs index 6d152f0a363..db9a02c37bb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -59,8 +59,8 @@ #[cfg(feature="simd_support")] extern crate packed_simd; extern crate rand_jitter; -#[cfg(feature = "rand_os")] -extern crate rand_os; +#[cfg(feature = "getrandom")] +extern crate getrandom; extern crate rand_core; extern crate rand_hc; diff --git a/src/rngs/adapter/read.rs b/src/rngs/adapter/read.rs index 9b9c18b3ebc..b7787702d6a 100644 --- a/src/rngs/adapter/read.rs +++ b/src/rngs/adapter/read.rs @@ -40,7 +40,7 @@ use rand_core::{RngCore, Error, ErrorKind, impls}; /// println!("{:x}", rng.gen::()); /// ``` /// -/// [`OsRng`]: rand_os::OsRng +/// [`OsRng`]: rngs::OsRng /// [`try_fill_bytes`]: RngCore::try_fill_bytes #[derive(Debug)] pub struct ReadRng { diff --git a/src/rngs/entropy.rs b/src/rngs/entropy.rs index 30cc54953df..2d72001206a 100644 --- a/src/rngs/entropy.rs +++ b/src/rngs/entropy.rs @@ -38,7 +38,7 @@ use rngs; /// report the error, and only the one from `OsRng`. The other [`RngCore`] /// methods will panic in case of an error. /// -/// [`OsRng`]: rand_os::OsRng +/// [`OsRng`]: rngs::OsRng /// [`thread_rng`]: crate::thread_rng /// [`JitterRng`]: crate::rngs::JitterRng /// [`try_fill_bytes`]: RngCore::try_fill_bytes @@ -190,11 +190,11 @@ impl EntropySource for NoSource { } -#[cfg(feature="rand_os")] +#[cfg(feature="getrandom")] #[derive(Clone, Debug)] pub struct Os(rngs::OsRng); -#[cfg(feature="rand_os")] +#[cfg(feature="getrandom")] impl EntropySource for Os { fn new_and_fill(dest: &mut [u8]) -> Result { let rng = rngs::OsRng; diff --git a/src/rngs/mod.rs b/src/rngs/mod.rs index 6761cc044b0..d5ed6eb97bb 100644 --- a/src/rngs/mod.rs +++ b/src/rngs/mod.rs @@ -135,7 +135,6 @@ //! [`CryptoRng`] is a marker trait cryptographically secure PRNGs can //! implement. //! -//! [`OsRng`]: rand_os::OsRng //! [`SmallRng`]: rngs::SmallRng //! [`StdRng`]: rngs::StdRng //! [`ThreadRng`]: rngs::ThreadRng @@ -163,5 +162,5 @@ pub use self::small::SmallRng; pub use self::std::StdRng; #[cfg(feature="std")] pub use self::thread::ThreadRng; -#[cfg(feature="rand_os")] -pub use rand_os::OsRng; +#[cfg(feature="getrandom")] mod os; +#[cfg(feature="getrandom")] pub use self::os::OsRng; diff --git a/src/rngs/os.rs b/src/rngs/os.rs new file mode 100644 index 00000000000..32ac26418ca --- /dev/null +++ b/src/rngs/os.rs @@ -0,0 +1,67 @@ +// Copyright 2019 Developers of the Rand project. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Interface to the random number generator of the operating system. + +use getrandom::getrandom; +use rand_core::{CryptoRng, RngCore, Error, ErrorKind, impls}; + +/// A random number generator that retrieves randomness from from the +/// operating system. +/// +/// This is a zero-sized struct. It can be freely constructed with `OsRng`. +/// +/// The implementation is provided by the [getrandom] crate. Refer to +/// [getrandom] documentation for details. +/// +/// [getrandom]: https://crates.io/crates/getrandom +#[derive(Clone, Copy, Debug)] +pub struct OsRng; + +impl OsRng { + /// Create a new `OsRng`. + #[deprecated(since="0.2.0", note="replace OsRng::new().unwrap() with just OsRng")] + pub fn new() -> Result { + Ok(OsRng) + } +} + +impl CryptoRng for OsRng {} + +impl RngCore for OsRng { + fn next_u32(&mut self) -> u32 { + impls::next_u32_via_fill(self) + } + + fn next_u64(&mut self) -> u64 { + impls::next_u64_via_fill(self) + } + + fn fill_bytes(&mut self, dest: &mut [u8]) { + if let Err(e) = self.try_fill_bytes(dest) { + panic!("Error: {}", e); + } + } + + fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { + // Some systems do not support reading 0 random bytes. + // (And why waste a system call?) + if dest.len() == 0 { return Ok(()); } + + getrandom(dest).map_err(|e| + Error::with_cause(ErrorKind::Unavailable, "OsRng failed", e)) + } +} + +#[test] +fn test_os_rng() { + let x = OsRng.next_u64(); + let y = OsRng.next_u64(); + assert!(x != 0); + assert!(x != y); +} From 8cfe2101c57aae204706a7d03e9d90233eb962b2 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Tue, 2 Apr 2019 13:37:44 +0100 Subject: [PATCH 3/9] Simplify and deprecate EntropyRng --- README.md | 3 - benches/generators.rs | 6 +- src/lib.rs | 10 +-- src/rngs/entropy.rs | 197 +++--------------------------------------- src/rngs/mod.rs | 8 +- src/rngs/thread.rs | 13 ++- 6 files changed, 29 insertions(+), 208 deletions(-) diff --git a/README.md b/README.md index 226ed359da1..61c7a17fc77 100644 --- a/README.md +++ b/README.md @@ -102,9 +102,6 @@ functionality depending on `std`: - `thread_rng()`, and `random()` are not available, as they require thread-local storage and an entropy source. -- `OsRng` and `EntropyRng` are unavailable. -- `JitterRng` code is still present, but a nanosecond timer must be provided via - `JitterRng::new_with_timer` - Since no external entropy is available, it is not possible to create generators with fresh seeds using the `FromEntropy` trait (user must provide a seed). diff --git a/benches/generators.rs b/benches/generators.rs index a6e3a423202..be401833066 100644 --- a/benches/generators.rs +++ b/benches/generators.rs @@ -25,7 +25,7 @@ use test::{black_box, Bencher}; use rand::prelude::*; use rand::rngs::adapter::ReseedingRng; -use rand::rngs::{OsRng, JitterRng, EntropyRng}; +use rand::rngs::{OsRng, JitterRng}; use rand_isaac::{IsaacRng, Isaac64Rng}; use rand_chacha::ChaChaRng; use rand_hc::{Hc128Rng, Hc128Core}; @@ -185,7 +185,7 @@ const RESEEDING_THRESHOLD: u64 = 1024*1024*1024; // something high enough to get fn reseeding_hc128_bytes(b: &mut Bencher) { let mut rng = ReseedingRng::new(Hc128Core::from_entropy(), RESEEDING_THRESHOLD, - EntropyRng::new()); + OsRng); let mut buf = [0u8; BYTES_LEN]; b.iter(|| { for _ in 0..RAND_BENCH_N { @@ -202,7 +202,7 @@ macro_rules! reseeding_uint { fn $fnn(b: &mut Bencher) { let mut rng = ReseedingRng::new(Hc128Core::from_entropy(), RESEEDING_THRESHOLD, - EntropyRng::new()); + OsRng); b.iter(|| { let mut accum: $ty = 0; for _ in 0..RAND_BENCH_N { diff --git a/src/lib.rs b/src/lib.rs index db9a02c37bb..d02883bb8e4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -456,7 +456,7 @@ impl_as_byte_slice_arrays!(!div 4096, N,N,N,N,N,N,N,); /// entropy. This trait is automatically implemented for any PRNG implementing /// [`SeedableRng`] and is not intended to be implemented by users. /// -/// This is equivalent to using `SeedableRng::from_rng(EntropyRng::new())` then +/// This is equivalent to using `SeedableRng::from_rng(OsRng)` then /// unwrapping the result. /// /// Since this is convenient and secure, it is the recommended way to create @@ -477,7 +477,7 @@ impl_as_byte_slice_arrays!(!div 4096, N,N,N,N,N,N,N,); /// println!("Random die roll: {}", rng.gen_range(1, 7)); /// ``` /// -/// [`EntropyRng`]: rngs::EntropyRng +/// [`OsRng`]: rngs::OsRng #[cfg(feature="std")] pub trait FromEntropy: SeedableRng { /// Creates a new instance, automatically seeded with fresh entropy. @@ -497,11 +497,11 @@ pub trait FromEntropy: SeedableRng { /// ``` /// # use rand::Error; /// use rand::prelude::*; - /// use rand::rngs::EntropyRng; + /// use rand::rngs::OsRng; /// /// # fn try_inner() -> Result<(), Error> { /// // This uses StdRng, but is valid for any R: SeedableRng - /// let mut rng = StdRng::from_rng(EntropyRng::new())?; + /// let mut rng = StdRng::from_rng(OsRng)?; /// /// println!("random number: {}", rng.gen_range(1, 10)); /// # Ok(()) @@ -515,7 +515,7 @@ pub trait FromEntropy: SeedableRng { #[cfg(feature="std")] impl FromEntropy for R { fn from_entropy() -> R { - R::from_rng(rngs::EntropyRng::new()).unwrap_or_else(|err| + R::from_rng(rngs::OsRng).unwrap_or_else(|err| panic!("FromEntropy::from_entropy() failed: {}", err)) } } diff --git a/src/rngs/entropy.rs b/src/rngs/entropy.rs index 2d72001206a..17c13f9d314 100644 --- a/src/rngs/entropy.rs +++ b/src/rngs/entropy.rs @@ -8,51 +8,20 @@ //! Entropy generator, or wrapper around external generators -use rand_core::{RngCore, CryptoRng, Error, ErrorKind, impls}; +#![allow(deprecated)] // whole module is deprecated + +use rand_core::{RngCore, CryptoRng, Error}; #[allow(unused)] -use rngs; +use rngs::OsRng; /// An interface returning random data from external source(s), provided /// specifically for securely seeding algorithmic generators (PRNGs). /// -/// Where possible, `EntropyRng` retrieves random data from the operating -/// system's interface for random numbers ([`OsRng`]); if that fails it will -/// fall back to the [`JitterRng`] entropy collector. In the latter case it will -/// still try to use [`OsRng`] on the next usage. -/// -/// If no secure source of entropy is available `EntropyRng` will panic on use; -/// i.e. it should never output predictable data. -/// -/// This is either a little slow ([`OsRng`] requires a system call) or extremely -/// slow ([`JitterRng`] must use significant CPU time to generate sufficient -/// jitter); for better performance it is common to seed a local PRNG from -/// external entropy then primarily use the local PRNG ([`thread_rng`] is -/// provided as a convenient, local, automatically-seeded CSPRNG). -/// -/// # Panics -/// -/// On most systems, like Windows, Linux, macOS and *BSD on common hardware, it -/// is highly unlikely for both [`OsRng`] and [`JitterRng`] to fail. But on -/// combinations like webassembly without Emscripten or stdweb both sources are -/// unavailable. If both sources fail, only [`try_fill_bytes`] is able to -/// report the error, and only the one from `OsRng`. The other [`RngCore`] -/// methods will panic in case of an error. -/// -/// [`OsRng`]: rngs::OsRng -/// [`thread_rng`]: crate::thread_rng -/// [`JitterRng`]: crate::rngs::JitterRng -/// [`try_fill_bytes`]: RngCore::try_fill_bytes +/// This is deprecated. It is suggested you use [`rngs::OsRng`] instead. #[derive(Debug)] +#[deprecated(since="0.7.0", note="use rngs::OsRng instead")] pub struct EntropyRng { - source: Source, -} - -#[derive(Debug)] -enum Source { - Os(Os), - Custom(Custom), - Jitter(Jitter), - None, + source: OsRng, } impl EntropyRng { @@ -62,7 +31,7 @@ impl EntropyRng { /// those are done on first use. This is done to make `new` infallible, /// and `try_fill_bytes` the only place to report errors. pub fn new() -> Self { - EntropyRng { source: Source::None } + EntropyRng { source: OsRng } } } @@ -74,167 +43,25 @@ impl Default for EntropyRng { impl RngCore for EntropyRng { fn next_u32(&mut self) -> u32 { - impls::next_u32_via_fill(self) + self.source.next_u32() } fn next_u64(&mut self) -> u64 { - impls::next_u64_via_fill(self) + self.source.next_u64() } fn fill_bytes(&mut self, dest: &mut [u8]) { - self.try_fill_bytes(dest).unwrap_or_else(|err| - panic!("all entropy sources failed; first error: {}", err)) + self.source.fill_bytes(dest) } fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { - let mut reported_error = None; - - if let Source::Os(ref mut os_rng) = self.source { - match os_rng.fill(dest) { - Ok(()) => return Ok(()), - Err(err) => { - warn!("EntropyRng: OsRng failed \ - [trying other entropy sources]: {}", err); - reported_error = Some(err); - }, - } - } else if Os::is_supported() { - match Os::new_and_fill(dest) { - Ok(os_rng) => { - debug!("EntropyRng: using OsRng"); - self.source = Source::Os(os_rng); - return Ok(()); - }, - Err(err) => { reported_error = reported_error.or(Some(err)) }, - } - } - - 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)) }, - } - } - - 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")) - } + self.source.try_fill_bytes(dest) } } impl CryptoRng for EntropyRng {} - -trait EntropySource { - fn new_and_fill(dest: &mut [u8]) -> Result - where Self: Sized; - - fn fill(&mut self, dest: &mut [u8]) -> Result<(), Error>; - - fn is_supported() -> bool { true } -} - -#[allow(unused)] -#[derive(Clone, Debug)] -struct NoSource; - -#[allow(unused)] -impl EntropySource for NoSource { - fn new_and_fill(dest: &mut [u8]) -> Result { - Err(Error::new(ErrorKind::Unavailable, "Source not supported")) - } - - fn fill(&mut self, dest: &mut [u8]) -> Result<(), Error> { - unreachable!() - } - - fn is_supported() -> bool { false } -} - - -#[cfg(feature="getrandom")] -#[derive(Clone, Debug)] -pub struct Os(rngs::OsRng); - -#[cfg(feature="getrandom")] -impl EntropySource for Os { - fn new_and_fill(dest: &mut [u8]) -> Result { - let rng = rngs::OsRng; - rngs::OsRng.try_fill_bytes(dest)?; - Ok(Os(rng)) - } - - fn fill(&mut self, dest: &mut [u8]) -> Result<(), Error> { - self.0.try_fill_bytes(dest) - } -} - -#[cfg(not(feature="std"))] -type Os = NoSource; - - -type Custom = NoSource; - - -#[cfg(not(target_arch = "wasm32"))] -#[derive(Clone, Debug)] -pub struct Jitter(rngs::JitterRng); - -#[cfg(not(target_arch = "wasm32"))] -impl EntropySource for Jitter { - fn new_and_fill(dest: &mut [u8]) -> Result { - let mut rng = rngs::JitterRng::new()?; - rng.try_fill_bytes(dest)?; - Ok(Jitter(rng)) - } - - fn fill(&mut self, dest: &mut [u8]) -> Result<(), Error> { - self.0.try_fill_bytes(dest) - } -} - -#[cfg(target_arch = "wasm32")] -type Jitter = NoSource; - - #[cfg(test)] mod test { use super::*; diff --git a/src/rngs/mod.rs b/src/rngs/mod.rs index d5ed6eb97bb..a7caa9c43d7 100644 --- a/src/rngs/mod.rs +++ b/src/rngs/mod.rs @@ -10,7 +10,7 @@ //! //! - [`ThreadRng`], a fast, secure, auto-seeded thread-local generator //! - [`StdRng`] and [`SmallRng`], algorithms to cover typical usage -//! - [`EntropyRng`], [`OsRng`] and [`JitterRng`] as entropy sources +//! - [`OsRng`] as an entropy source //! - [`mock::StepRng`] as a simple counter for tests //! - [`adapter::ReadRng`] to read from a file/stream //! - [`adapter::ReseedingRng`] to reseed a PRNG on clone / process fork etc. @@ -26,8 +26,6 @@ //! use that to seed its own PRNG; [`OsRng`] provides an interface to this. //! [`JitterRng`] is an entropy collector included with Rand that measures //! jitter in the CPU execution time, and jitter in memory access time. -//! [`EntropyRng`] is a wrapper that uses the best entropy source that is -//! available. //! //! ## Pseudo-random number generators //! @@ -78,7 +76,7 @@ //! - [`FromEntropy::from_entropy`]; this is the most convenient way to seed //! with fresh, secure random data. //! - [`SeedableRng::from_rng`]; this allows seeding from another PRNG or -//! from an entropy source such as [`EntropyRng`]. +//! from an entropy source such as [`OsRng`]. //! - [`SeedableRng::from_seed`]; this is mostly useful if you wish to be able //! to reproduce the output sequence by using a fixed seed. (Don't use //! [`StdRng`] or [`SmallRng`] in this case since different algorithms may be @@ -138,7 +136,6 @@ //! [`SmallRng`]: rngs::SmallRng //! [`StdRng`]: rngs::StdRng //! [`ThreadRng`]: rngs::ThreadRng -//! [`EntropyRng`]: rngs::EntropyRng //! [`JitterRng`]: rngs::JitterRng //! [`mock::StepRng`]: rngs::mock::StepRng //! [`adapter::ReadRng`]: rngs::adapter::ReadRng @@ -156,6 +153,7 @@ mod std; pub use rand_jitter::{JitterRng, TimerError}; +#[allow(deprecated)] #[cfg(feature="std")] pub use self::entropy::EntropyRng; pub use self::small::SmallRng; diff --git a/src/rngs/thread.rs b/src/rngs/thread.rs index f1aa787dc4f..ebd67b69801 100644 --- a/src/rngs/thread.rs +++ b/src/rngs/thread.rs @@ -12,7 +12,7 @@ use std::cell::UnsafeCell; use {RngCore, CryptoRng, SeedableRng, Error}; use rngs::adapter::ReseedingRng; -use rngs::EntropyRng; +use rngs::OsRng; use rand_hc::Hc128Core; // Rationale for using `UnsafeCell` in `ThreadRng`: @@ -48,7 +48,7 @@ const THREAD_RNG_RESEED_THRESHOLD: u64 = 32*1024*1024; // 32 MiB /// which is reseeded after generating 32 MiB of random data. A single instance /// is cached per thread and the returned `ThreadRng` is a reference to this /// instance — hence `ThreadRng` is neither `Send` nor `Sync` but is safe to use -/// within a single thread. This RNG is seeded and reseeded via [`EntropyRng`] +/// within a single thread. This RNG is seeded and reseeded via [`OsRng`] /// as required. /// /// Note that the reseeding is done as an extra precaution against entropy @@ -70,17 +70,16 @@ const THREAD_RNG_RESEED_THRESHOLD: u64 = 32*1024*1024; // 32 MiB #[derive(Clone, Debug)] pub struct ThreadRng { // use of raw pointer implies type is neither Send nor Sync - rng: *mut ReseedingRng, + rng: *mut ReseedingRng, } thread_local!( - static THREAD_RNG_KEY: UnsafeCell> = { - let mut entropy_source = EntropyRng::new(); - let r = Hc128Core::from_rng(&mut entropy_source).unwrap_or_else(|err| + static THREAD_RNG_KEY: UnsafeCell> = { + let r = Hc128Core::from_rng(OsRng).unwrap_or_else(|err| panic!("could not initialize thread_rng: {}", err)); let rng = ReseedingRng::new(r, THREAD_RNG_RESEED_THRESHOLD, - entropy_source); + OsRng); UnsafeCell::new(rng) } ); From b231ac20cc45098673ccf2c06f906ccfdef89fc4 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Tue, 2 Apr 2019 13:57:26 +0100 Subject: [PATCH 4/9] Remove dependence on rand_jitter This is a breaking change for anyone using rngs::JitterRng; such users should switch to rand_jitter::JitterRng. --- Cargo.toml | 3 +-- benches/generators.rs | 20 +------------------- src/lib.rs | 32 ++++++-------------------------- src/rngs/mod.rs | 9 ++++----- 4 files changed, 12 insertions(+), 52 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 8b7fce000c4..c79dffede56 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,7 +22,7 @@ appveyor = { repository = "rust-random/rand" } [features] default = ["std"] # without "std" rand uses libcore nightly = ["simd_support"] # enables all features requiring nightly rust -std = ["rand_core/std", "alloc", "getrandom", "rand_jitter/std"] +std = ["rand_core/std", "alloc", "getrandom"] 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 @@ -49,7 +49,6 @@ members = [ [dependencies] rand_core = { path = "rand_core", version = "0.4" } rand_pcg = { path = "rand_pcg", version = "0.1" } -rand_jitter = { path = "rand_jitter", version = "0.1" } rand_hc = { path = "rand_hc", version = "0.1" } getrandom = { version = "0.1", optional = true } log = { version = "0.4", optional = true } diff --git a/benches/generators.rs b/benches/generators.rs index be401833066..b9bb9cd746d 100644 --- a/benches/generators.rs +++ b/benches/generators.rs @@ -25,7 +25,7 @@ use test::{black_box, Bencher}; use rand::prelude::*; use rand::rngs::adapter::ReseedingRng; -use rand::rngs::{OsRng, JitterRng}; +use rand::rngs::OsRng; use rand_isaac::{IsaacRng, Isaac64Rng}; use rand_chacha::ChaChaRng; use rand_hc::{Hc128Rng, Hc128Core}; @@ -129,17 +129,6 @@ gen_uint!(gen_u64_std, u64, StdRng::from_entropy()); gen_uint!(gen_u64_small, u64, SmallRng::from_entropy()); gen_uint!(gen_u64_os, u64, OsRng::new().unwrap()); -// Do not test JitterRng like the others by running it RAND_BENCH_N times per, -// measurement, because it is way too slow. Only run it once. -#[bench] -fn gen_u64_jitter(b: &mut Bencher) { - let mut rng = JitterRng::new().unwrap(); - b.iter(|| { - rng.gen::() - }); - b.bytes = size_of::() as u64; -} - macro_rules! init_gen { ($fnn:ident, $gen:ident) => { #[bench] @@ -170,13 +159,6 @@ init_gen!(init_isaac, IsaacRng); init_gen!(init_isaac64, Isaac64Rng); init_gen!(init_chacha, ChaChaRng); -#[bench] -fn init_jitter(b: &mut Bencher) { - b.iter(|| { - JitterRng::new().unwrap() - }); -} - const RESEEDING_THRESHOLD: u64 = 1024*1024*1024; // something high enough to get // deterministic measurements diff --git a/src/lib.rs b/src/lib.rs index d02883bb8e4..3b59f25086d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -58,7 +58,6 @@ #[cfg(feature="simd_support")] extern crate packed_simd; -extern crate rand_jitter; #[cfg(feature = "getrandom")] extern crate getrandom; @@ -480,35 +479,16 @@ impl_as_byte_slice_arrays!(!div 4096, N,N,N,N,N,N,N,); /// [`OsRng`]: rngs::OsRng #[cfg(feature="std")] pub trait FromEntropy: SeedableRng { - /// Creates a new instance, automatically seeded with fresh entropy. + /// Creates a new instance of the RNG seeded from [`OsRng`]. /// - /// Normally this will use `OsRng`, but if that fails `JitterRng` will be - /// used instead. Both should be suitable for cryptography. It is possible - /// that both entropy sources will fail though unlikely; failures would - /// almost certainly be platform limitations or build issues, i.e. most - /// applications targetting PC/mobile platforms should not need to worry - /// about this failing. + /// This method is equivalent to `SeedableRng::from_rng(OsRng).unwrap()`. /// /// # Panics /// - /// If all entropy sources fail this will panic. If you need to handle - /// errors, use the following code, equivalent aside from error handling: - /// - /// ``` - /// # use rand::Error; - /// use rand::prelude::*; - /// use rand::rngs::OsRng; - /// - /// # fn try_inner() -> Result<(), Error> { - /// // This uses StdRng, but is valid for any R: SeedableRng - /// let mut rng = StdRng::from_rng(OsRng)?; - /// - /// println!("random number: {}", rng.gen_range(1, 10)); - /// # Ok(()) - /// # } - /// - /// # try_inner().unwrap() - /// ``` + /// If [`OsRng`] is unable to obtain secure entropy this method will panic. + /// It is also possible for an RNG overriding the [`SeedableRng::from_rng`] + /// method to cause a panic. Both causes are extremely unlikely to occur; + /// most users need not worry about this. fn from_entropy() -> Self; } diff --git a/src/rngs/mod.rs b/src/rngs/mod.rs index a7caa9c43d7..d0a285de19f 100644 --- a/src/rngs/mod.rs +++ b/src/rngs/mod.rs @@ -24,8 +24,8 @@ //! //! Generally the operating system will collect some entropy, remove bias, and //! use that to seed its own PRNG; [`OsRng`] provides an interface to this. -//! [`JitterRng`] is an entropy collector included with Rand that measures -//! jitter in the CPU execution time, and jitter in memory access time. +//! Some alternatives are available although not normally required; see the +//! [`rdrand`] and [`rand_jitter`] crates. //! //! ## Pseudo-random number generators //! @@ -136,11 +136,12 @@ //! [`SmallRng`]: rngs::SmallRng //! [`StdRng`]: rngs::StdRng //! [`ThreadRng`]: rngs::ThreadRng -//! [`JitterRng`]: rngs::JitterRng //! [`mock::StepRng`]: rngs::mock::StepRng //! [`adapter::ReadRng`]: rngs::adapter::ReadRng //! [`adapter::ReseedingRng`]: rngs::adapter::ReseedingRng //! [`ChaChaRng`]: ../../rand_chacha/struct.ChaChaRng.html +//! [`rdrand`]: https://crates.io/crates/rdrand +//! [`rand_jitter`]: https://crates.io/crates/rand_jitter pub mod adapter; @@ -151,8 +152,6 @@ mod small; mod std; #[cfg(feature="std")] pub(crate) mod thread; - -pub use rand_jitter::{JitterRng, TimerError}; #[allow(deprecated)] #[cfg(feature="std")] pub use self::entropy::EntropyRng; From 23fc4a1bf2116944ee55b018eade45c2775fb465 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Wed, 3 Apr 2019 09:29:16 +0100 Subject: [PATCH 5/9] Review fixes --- src/rngs/os.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/rngs/os.rs b/src/rngs/os.rs index 32ac26418ca..9687d63e54e 100644 --- a/src/rngs/os.rs +++ b/src/rngs/os.rs @@ -25,7 +25,7 @@ pub struct OsRng; impl OsRng { /// Create a new `OsRng`. - #[deprecated(since="0.2.0", note="replace OsRng::new().unwrap() with just OsRng")] + #[deprecated(since="0.7.0", note="replace OsRng::new().unwrap() with just OsRng")] pub fn new() -> Result { Ok(OsRng) } @@ -49,10 +49,6 @@ impl RngCore for OsRng { } fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { - // Some systems do not support reading 0 random bytes. - // (And why waste a system call?) - if dest.len() == 0 { return Ok(()); } - getrandom(dest).map_err(|e| Error::with_cause(ErrorKind::Unavailable, "OsRng failed", e)) } From 8fa74fc445dfe469a5683006c1d8b261027e3dbc Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Thu, 4 Apr 2019 11:56:19 +0100 Subject: [PATCH 6/9] Re-write rngs module documentation --- src/rngs/mod.rs | 210 +++++++++++++++++++----------------------------- 1 file changed, 84 insertions(+), 126 deletions(-) diff --git a/src/rngs/mod.rs b/src/rngs/mod.rs index d0a285de19f..e7c15b97f3e 100644 --- a/src/rngs/mod.rs +++ b/src/rngs/mod.rs @@ -6,135 +6,89 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//! Random number generators and adapters for common usage: -//! -//! - [`ThreadRng`], a fast, secure, auto-seeded thread-local generator -//! - [`StdRng`] and [`SmallRng`], algorithms to cover typical usage -//! - [`OsRng`] as an entropy source -//! - [`mock::StepRng`] as a simple counter for tests -//! - [`adapter::ReadRng`] to read from a file/stream -//! - [`adapter::ReseedingRng`] to reseed a PRNG on clone / process fork etc. -//! -//! # Background — Random number generators (RNGs) -//! -//! Computers are inherently deterministic, so to get *random* numbers one -//! either has to use a hardware generator or collect bits of *entropy* from -//! various sources (e.g. event timestamps, or jitter). This is a relatively -//! slow and complicated operation. -//! -//! Generally the operating system will collect some entropy, remove bias, and -//! use that to seed its own PRNG; [`OsRng`] provides an interface to this. -//! Some alternatives are available although not normally required; see the -//! [`rdrand`] and [`rand_jitter`] crates. -//! -//! ## Pseudo-random number generators -//! -//! What is commonly used instead of "true" random number renerators, are -//! *pseudo-random number generators* (PRNGs), deterministic algorithms that -//! produce an infinite stream of pseudo-random numbers from a small random -//! seed. PRNGs are faster, and have better provable properties. The numbers -//! produced can be statistically of very high quality and can be impossible to -//! predict. (They can also have obvious correlations and be trivial to predict; -//! quality varies.) -//! -//! There are two different types of PRNGs: those developed for simulations -//! and statistics, and those developed for use in cryptography; the latter are -//! called Cryptographically Secure PRNGs (CSPRNG or CPRNG). Both types can -//! have good statistical quality but the latter also have to be impossible to -//! predict, even after seeing many previous output values. Rand provides a good -//! default algorithm from each class: -//! -//! - [`SmallRng`] is a PRNG chosen for low memory usage, high performance and -//! good statistical quality. -//! - [`StdRng`] is a CSPRNG chosen for good performance and trust of security -//! (based on reviews, maturity and usage). The current algorithm is HC-128, -//! which is one of the recommendations by ECRYPT's eSTREAM project. -//! -//! The above PRNGs do not cover all use-cases; more algorithms can be found in -//! the [`prng`][crate::prng] module, as well as in several other crates. For example, you -//! may wish a CSPRNG with significantly lower memory usage than [`StdRng`] -//! while being less concerned about performance, in which case [`ChaChaRng`] -//! (from the `rand_chacha` crate) is a good choice. -//! -//! One complexity is that the internal state of a PRNG must change with every -//! generated number. For APIs this generally means a mutable reference to the -//! state of the PRNG has to be passed around. -//! -//! A solution is [`ThreadRng`]. This is a thread-local implementation of -//! [`StdRng`] with automatic seeding on first use. It is the best choice if you -//! "just" want a convenient, secure, fast random number source. Use via the -//! [`thread_rng`] function, which gets a reference to the current thread's -//! local instance. -//! -//! ## Seeding -//! -//! As mentioned above, PRNGs require a random seed in order to produce random -//! output. This is especially important for CSPRNGs, which are still -//! deterministic algorithms, thus can only be secure if their seed value is -//! also secure. To seed a PRNG, use one of: -//! -//! - [`FromEntropy::from_entropy`]; this is the most convenient way to seed -//! with fresh, secure random data. -//! - [`SeedableRng::from_rng`]; this allows seeding from another PRNG or -//! from an entropy source such as [`OsRng`]. -//! - [`SeedableRng::from_seed`]; this is mostly useful if you wish to be able -//! to reproduce the output sequence by using a fixed seed. (Don't use -//! [`StdRng`] or [`SmallRng`] in this case since different algorithms may be -//! used by future versions of Rand; use an algorithm from the -//! [`prng`] module.) -//! -//! ## Conclusion -//! -//! - [`thread_rng`] is what you often want to use. -//! - If you want more control, flexibility, or better performance, use -//! [`StdRng`], [`SmallRng`] or an algorithm from the [`prng`] module. -//! - Use [`FromEntropy::from_entropy`] to seed new PRNGs. -//! - If you need reproducibility, use [`SeedableRng::from_seed`] combined with -//! a named PRNG. -//! -//! More information and notes on cryptographic security can be found -//! in the [`prng`] module. -//! -//! ## Examples -//! -//! Examples of seeding PRNGs: -//! -//! ``` -//! use rand::prelude::*; -//! # use rand::Error; -//! -//! // StdRng seeded securely by the OS or local entropy collector: -//! let mut rng = StdRng::from_entropy(); -//! # let v: u32 = rng.gen(); -//! -//! // SmallRng seeded from thread_rng: -//! # fn try_inner() -> Result<(), Error> { -//! let mut rng = SmallRng::from_rng(thread_rng())?; -//! # let v: u32 = rng.gen(); -//! # Ok(()) -//! # } -//! # try_inner().unwrap(); -//! -//! // SmallRng seeded by a constant, for deterministic results: -//! let seed = [1,2,3,4, 5,6,7,8, 9,10,11,12, 13,14,15,16]; // byte array -//! let mut rng = SmallRng::from_seed(seed); -//! # let v: u32 = rng.gen(); -//! ``` -//! -//! -//! # Implementing custom RNGs -//! -//! If you want to implement custom RNG, see the [`rand_core`] crate. The RNG -//! will have to implement the [`RngCore`] trait, where the [`Rng`] trait is -//! build on top of. -//! -//! If the RNG needs seeding, also implement the [`SeedableRng`] trait. -//! -//! [`CryptoRng`] is a marker trait cryptographically secure PRNGs can -//! implement. +//! Random number generators and adapters +//! +//! ## Background: Random number generators (RNGs) +//! +//! Computers cannot produce random numbers from nowhere. We classify +//! random number generators as follows: +//! +//! - "True" random number generators (TRNGs) use hard-to-predict data sources +//! (e.g. the high-resolution parts of event timings and sensor jitter) to +//! harvest random bit-sequences, apply algorithms to remove bias and +//! estimate available entropy, then combine these bits into a byte-sequence +//! or an entropy pool. This job is usually done by the operating system or +//! a hardware generator (HRNG). +//! - "Pseudo"-random number generators (PRNGs) use algorithms to transform a +//! seed into a sequence of pseudo-random numbers. These generators can be +//! fast and produce well-distributed unpredictable random numbers (or not). +//! They are usually deterministic: given algorithm and seed, the output +//! sequence can be reproduced. They have finite period and eventually loop; +//! with many algorithms this period is fixed and can be proven sufficiently +//! long, while others are chaotic and the period depends on the seed. +//! - "Cryptographically secure" pseudo-random number generators (CSPRNGs) +//! are the sub-set of PRNGs which are secure. Security of the generator +//! relies both on hiding the internal state and using a strong algorithm. +//! +//! ## Traits and functionality +//! +//! All RNGs implement the [`RngCore`] trait, as a consequence of which the +//! [`Rng`] extension trait is automatically implemented. Secure RNGs may +//! additionally implement the [`CryptoRng`] trait. +//! +//! All PRNGs require a seed to produce their random number sequence. The +//! [`SeedableRng`] trait provides three ways of constructing PRNGs: +//! +//! - `from_seed` accepts a type specific to the PRNG +//! - `from_rng` allows a PRNG to be seeded from any other RNG +//! - `seed_from_u64` allows any PRNG to be seeded from a `u64` insecurely +//! +//! Additionally, [`FromEntropy::from_entropy`] is a shortcut for seeding from +//! [`OsRng`]. +//! +//! Use the [`rand_core`] crate when implementing your own RNGs. +//! +//! ## Our generators +//! +//! This crate provides several random number generators: +//! +//! - [`OsRng`] is an interface to the operating system's random number +//! source. Typically the operating system uses a CSPRNG with entropy +//! provided by a TRNG and some type of on-going re-seeding. +//! - [`ThreadRng`], provided by the [`thread_rng`] function, is a handle to a +//! thread-local CSPRNG with periodic seeding from [`OsRng`]. Because this +//! is local, it is typically much faster than [`OsRng`]. It should be +//! secure, though the paranoid may prefer [`OsRng`]. +//! - [`StdRng`] is a CSPRNG chosen for good performance and trust of security +//! (based on reviews, maturity and usage). The current algorithm is HC-128, +//! which is one of the recommendations by ECRYPT's eSTREAM project. +//! [`StdRng`] provides the algorithm used by [`ThreadRng`] but without +//! periodic reseeding. +//! - [`SmallRng`] is an **insecure** PRNG designed to be fast, simple, require +//! little memory, and have good output quality. +//! +//! The algorithms selected for [`StdRng`] and [`SmallRng`] may change in any +//! release and may be platform-dependent, therefore they should be considered +//! **not reproducible**. +//! +//! ## Additional generators +//! +//! **TRNGs**: The [`rdrand`] crate provides an interface to the RDRAND and +//! RDSEED instructions available in modern Intel and AMD CPUs. +//! The [`rand_jitter`] crate provides a user-space implementation of +//! entropy harvesting from CPU timer jitter, but is very slow and has +//! [security issues](https://github.com/rust-random/rand/issues/699). +//! +//! **PRNGs**: Several companion crates are available, providing individual or +//! families of PRNG algorithms. These provide the implementations behind +//! [`StdRng`] and [`SmallRng`] but can also be used directly, indeed *should* +//! be used directly when **reproducibility** matters. +//! Some suggestions are: [`rand_chacha`], [`rand_pcg`], [`rand_xoshiro`]. +//! A full list can be found by searching for crates with the [`rng` tag]. //! //! [`SmallRng`]: rngs::SmallRng //! [`StdRng`]: rngs::StdRng +//! [`OsRng`]: rngs::OsRng //! [`ThreadRng`]: rngs::ThreadRng //! [`mock::StepRng`]: rngs::mock::StepRng //! [`adapter::ReadRng`]: rngs::adapter::ReadRng @@ -142,6 +96,10 @@ //! [`ChaChaRng`]: ../../rand_chacha/struct.ChaChaRng.html //! [`rdrand`]: https://crates.io/crates/rdrand //! [`rand_jitter`]: https://crates.io/crates/rand_jitter +//! [`rand_chacha`]: https://crates.io/crates/rand_chacha +//! [`rand_pcg`]: https://crates.io/crates/rand_pcg +//! [`rand_xoshiro`]: https://crates.io/crates/rand_xoshiro +//! [`rng` tag]: https://crates.io/keywords/rng pub mod adapter; From 57fbc710b92b68d331c1227c7790e87a19eed2dc Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Thu, 4 Apr 2019 11:56:44 +0100 Subject: [PATCH 7/9] Add OsRng examples Fix rand_os example --- rand_os/src/lib.rs | 18 +++++++++--------- src/rngs/os.rs | 11 +++++++++++ 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/rand_os/src/lib.rs b/rand_os/src/lib.rs index a8499bf2880..19599140e8e 100644 --- a/rand_os/src/lib.rs +++ b/rand_os/src/lib.rs @@ -9,15 +9,6 @@ //! Interface to the random number generator of the operating system. //! -//! # Usage example -//! ``` -//! use rand_os::{OsRng, rand_core::RngCore}; -//! -//! let mut key = [0u8; 16]; -//! OsRng.fill_bytes(&mut key); -//! let random_u64 = OsRng.next_u64(); -//! ``` -//! //! # Blocking and error handling //! //! It is possible that when used during early boot the first call to `OsRng` @@ -54,6 +45,15 @@ use rand_core::{CryptoRng, RngCore, Error, ErrorKind, impls}; /// The implementation is provided by the [getrandom] crate. Refer to /// [getrandom] documentation for details. /// +/// # Usage example +/// ``` +/// use rand_os::{OsRng, rand_core::RngCore}; +/// +/// let mut key = [0u8; 16]; +/// OsRng.fill_bytes(&mut key); +/// let random_u64 = OsRng.next_u64(); +/// ``` +/// /// [getrandom]: https://crates.io/crates/getrandom #[derive(Clone, Copy, Debug)] pub struct OsRng; diff --git a/src/rngs/os.rs b/src/rngs/os.rs index 9687d63e54e..d2b221eb6ce 100644 --- a/src/rngs/os.rs +++ b/src/rngs/os.rs @@ -19,6 +19,17 @@ use rand_core::{CryptoRng, RngCore, Error, ErrorKind, impls}; /// The implementation is provided by the [getrandom] crate. Refer to /// [getrandom] documentation for details. /// +/// ## Example +/// +/// ``` +/// use rand::rngs::{StdRng, OsRng}; +/// use rand::{RngCore, SeedableRng}; +/// +/// println!("Random number, straight from the OS: {}", OsRng.next_u32()); +/// let mut rng = StdRng::from_rng(OsRng).unwrap(); +/// println!("Another random number: {}", rng.next_u32()); +/// ``` +/// /// [getrandom]: https://crates.io/crates/getrandom #[derive(Clone, Copy, Debug)] pub struct OsRng; From b83a4c58008f1bf68e42fe149c06ce3559662af7 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Fri, 5 Apr 2019 16:22:42 +0100 Subject: [PATCH 8/9] Bump version requirement on getrandom to 0.1.1 --- Cargo.toml | 2 +- rand_os/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c79dffede56..c54d85c37b6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,7 +50,7 @@ members = [ rand_core = { path = "rand_core", version = "0.4" } rand_pcg = { path = "rand_pcg", version = "0.1" } rand_hc = { path = "rand_hc", version = "0.1" } -getrandom = { version = "0.1", optional = true } +getrandom = { version = "0.1.1", optional = true } log = { version = "0.4", optional = true } [dependencies.packed_simd] diff --git a/rand_os/Cargo.toml b/rand_os/Cargo.toml index a7b643c0274..402042a9b2d 100644 --- a/rand_os/Cargo.toml +++ b/rand_os/Cargo.toml @@ -23,4 +23,4 @@ stdweb = ["getrandom/stdweb"] [dependencies] rand_core = { path = "../rand_core", version = "0.4", features = ["std"] } -getrandom = "0.1" +getrandom = "0.1.1" From f84dccb18dcf505038576f41a1a84a7a6561de2d Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Sat, 6 Apr 2019 08:25:24 +0100 Subject: [PATCH 9/9] Implement Default for OsRng --- rand_os/src/lib.rs | 8 +++++++- src/rngs/os.rs | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/rand_os/src/lib.rs b/rand_os/src/lib.rs index 19599140e8e..2849f4fa2d8 100644 --- a/rand_os/src/lib.rs +++ b/rand_os/src/lib.rs @@ -55,7 +55,7 @@ use rand_core::{CryptoRng, RngCore, Error, ErrorKind, impls}; /// ``` /// /// [getrandom]: https://crates.io/crates/getrandom -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, Default)] pub struct OsRng; impl OsRng { @@ -100,3 +100,9 @@ fn test_os_rng() { assert!(x != 0); assert!(x != y); } + +#[test] +fn test_construction() { + let mut rng = OsRng::default(); + assert!(rng.next_u64() != 0); +} diff --git a/src/rngs/os.rs b/src/rngs/os.rs index d2b221eb6ce..e71ea80498c 100644 --- a/src/rngs/os.rs +++ b/src/rngs/os.rs @@ -31,7 +31,7 @@ use rand_core::{CryptoRng, RngCore, Error, ErrorKind, impls}; /// ``` /// /// [getrandom]: https://crates.io/crates/getrandom -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, Default)] pub struct OsRng; impl OsRng {