rand_os/
lib.rs

1// Copyright 2018 Developers of the Rand project.
2// Copyright 2013-2015 The Rust Project Developers.
3//
4// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
5// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
7// option. This file may not be copied, modified, or distributed
8// except according to those terms.
9
10//! Interface to the random number generator of the operating system.
11//!
12//! [`OsRng`] is the preferred external source of entropy for most applications.
13//! Commonly it is used to initialize a user-space RNG, which can then be used
14//! to generate random values with much less overhead than `OsRng`.
15//!
16//! You may prefer to use [`EntropyRng`] instead of `OsRng`. It is unlikely, but
17//! not entirely theoretical, for `OsRng` to fail. In such cases [`EntropyRng`]
18//! falls back on a good alternative entropy source.
19//!
20//! [`OsRng::new()`] is guaranteed to be very cheap (after the first successful
21//! call), and will never consume more than one file handle per process.
22//!
23//! # Usage example
24//! ```
25//! use rand_os::OsRng;
26//! use rand_os::rand_core::RngCore;
27//!
28//! let mut os_rng = OsRng::new().unwrap();
29//! let mut key = [0u8; 16];
30//! os_rng.fill_bytes(&mut key);
31//! let random_u64 = os_rng.next_u64();
32//! ```
33//!
34//! # Platform sources
35//!
36//! | OS               | interface
37//! |------------------|---------------------------------------------------------
38//! | Linux, Android   | [`getrandom`][1] system call if available, otherwise [`/dev/urandom`][2] after reading from `/dev/random` once
39//! | Windows          | [`RtlGenRandom`][3]
40//! | macOS, iOS       | [`SecRandomCopyBytes`][4]
41//! | FreeBSD          | [`kern.arandom`][5]
42//! | OpenBSD, Bitrig  | [`getentropy`][6]
43//! | NetBSD           | [`/dev/urandom`][7] after reading from `/dev/random` once
44//! | Dragonfly BSD    | [`/dev/random`][8]
45//! | Solaris, illumos | [`getrandom`][9] system call if available, otherwise [`/dev/random`][10]
46//! | Fuchsia OS       | [`cprng_draw`][11]
47//! | Redox            | [`rand:`][12]
48//! | CloudABI         | [`random_get`][13]
49//! | Haiku            | `/dev/random` (identical to `/dev/urandom`)
50//! | Web browsers     | [`Crypto.getRandomValues`][14] (see [Support for WebAssembly and ams.js][14])
51//! | Node.js          | [`crypto.randomBytes`][15] (see [Support for WebAssembly and ams.js][16])
52//!
53//! Rand doesn't have a blanket implementation for all Unix-like operating
54//! systems that reads from `/dev/urandom`. This ensures all supported operating
55//! systems are using the recommended interface and respect maximum buffer
56//! sizes.
57//!
58//! ## Support for WebAssembly and ams.js
59//!
60//! The three Emscripten targets `asmjs-unknown-emscripten`,
61//! `wasm32-unknown-emscripten` and `wasm32-experimental-emscripten` use
62//! Emscripten's emulation of `/dev/random` on web browsers and Node.js.
63//!
64//! The bare WASM target `wasm32-unknown-unknown` tries to call the javascript
65//! methods directly, using either `stdweb` or `wasm-bindgen` depending on what
66//! features are activated for this crate. Note that if both features are
67//! enabled `wasm-bindgen` will be used.
68//!
69//! ## Early boot
70//!
71//! It is possible that early in the boot process the OS hasn't had enough time
72//! yet to collect entropy to securely seed its RNG, especially on virtual
73//! machines.
74//!
75//! Some operating systems always block the thread until the RNG is securely
76//! seeded. This can take anywhere from a few seconds to more than a minute.
77//! Others make a best effort to use a seed from before the shutdown and don't
78//! document much.
79//!
80//! A few, Linux, NetBSD and Solaris, offer a choice between blocking, and
81//! getting an error. With `try_fill_bytes` we choose to get the error
82//! ([`ErrorKind::NotReady`]), while the other methods use a blocking interface.
83//!
84//! On Linux (when the `genrandom` system call is not available) and on NetBSD
85//! reading from `/dev/urandom` never blocks, even when the OS hasn't collected
86//! enough entropy yet. As a countermeasure we try to do a single read from
87//! `/dev/random` until we know the OS RNG is initialized (and store this in a
88//! global static).
89//!
90//! # Panics and error handling
91//!
92//! We cannot guarantee that `OsRng` will fail, but if it does, it will likely
93//! be either when `OsRng::new()` is first called or when data is first read.
94//! If you wish to catch errors early, then test reading of at least one byte
95//! from `OsRng` via [`try_fill_bytes`]. If this succeeds, it is extremely
96//! unlikely that any further errors will occur.
97//! 
98//! Only [`try_fill_bytes`] is able to report the cause of an error; the other
99//! [`RngCore`] methods may (depending on the error kind) retry several times,
100//! but must eventually panic if the error persists.
101//!
102//! [`EntropyRng`]: ../rand/rngs/struct.EntropyRng.html
103//! [`try_fill_bytes`]: RngCore::try_fill_bytes
104//! [`ErrorKind::NotReady`]: rand_core::ErrorKind
105//!
106//! [1]: http://man7.org/linux/man-pages/man2/getrandom.2.html
107//! [2]: http://man7.org/linux/man-pages/man4/urandom.4.html
108//! [3]: https://msdn.microsoft.com/en-us/library/windows/desktop/aa387694.aspx
109//! [4]: https://developer.apple.com/documentation/security/1399291-secrandomcopybytes?language=objc
110//! [5]: https://www.freebsd.org/cgi/man.cgi?query=random&sektion=4
111//! [6]: https://man.openbsd.org/getentropy.2
112//! [7]: http://netbsd.gw.com/cgi-bin/man-cgi?random+4+NetBSD-current
113//! [8]: https://leaf.dragonflybsd.org/cgi/web-man?command=random&section=4
114//! [9]: https://docs.oracle.com/cd/E88353_01/html/E37841/getrandom-2.html
115//! [10]: https://docs.oracle.com/cd/E86824_01/html/E54777/random-7d.html
116//! [11]: https://fuchsia.googlesource.com/zircon/+/HEAD/docs/syscalls/cprng_draw.md
117//! [12]: https://github.com/redox-os/randd/blob/master/src/main.rs
118//! [13]: https://github.com/NuxiNL/cloudabi/blob/v0.20/cloudabi.txt#L1826
119//! [14]: https://www.w3.org/TR/WebCryptoAPI/#Crypto-method-getRandomValues
120//! [15]: https://nodejs.org/api/crypto.html#crypto_crypto_randombytes_size_callback
121//! [16]: #support-for-webassembly-and-amsjs
122#![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk.png",
123       html_favicon_url = "https://www.rust-lang.org/favicon.ico",
124       html_root_url = "https://rust-random.github.io/rand/")]
125#![deny(missing_docs)]
126#![deny(missing_debug_implementations)]
127#![doc(test(attr(allow(unused_variables), deny(warnings))))]
128
129#![cfg_attr(feature = "stdweb", recursion_limit="128")]
130
131pub extern crate rand_core;
132#[cfg(feature = "log")]
133#[macro_use] extern crate log;
134
135// We have to do it here because we load macros
136#[cfg(all(target_arch = "wasm32", not(target_os = "emscripten"),
137          feature = "wasm-bindgen"))]
138extern crate wasm_bindgen;
139#[cfg(all(target_arch = "wasm32", not(target_os = "emscripten"),
140          not(feature = "wasm-bindgen"),
141          feature = "stdweb"))]
142#[macro_use] extern crate stdweb;
143
144#[cfg(target_env = "sgx")]
145extern crate rdrand;
146
147#[cfg(not(feature = "log"))]
148#[macro_use]
149mod dummy_log;
150
151use std::fmt;
152use rand_core::{CryptoRng, RngCore, Error, impls};
153
154/// A random number generator that retrieves randomness straight from the
155/// operating system.
156#[derive(Clone)]
157pub struct OsRng(imp::OsRng);
158
159impl fmt::Debug for OsRng {
160    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
161        self.0.fmt(f)
162    }
163}
164
165impl OsRng {
166    /// Create a new `OsRng`.
167    pub fn new() -> Result<OsRng, Error> {
168        imp::OsRng::new().map(OsRng)
169    }
170}
171
172impl CryptoRng for OsRng {}
173
174impl RngCore for OsRng {
175    fn next_u32(&mut self) -> u32 {
176        impls::next_u32_via_fill(self)
177    }
178
179    fn next_u64(&mut self) -> u64 {
180        impls::next_u64_via_fill(self)
181    }
182
183    fn fill_bytes(&mut self, dest: &mut [u8]) {
184        use std::{time, thread};
185
186        // We cannot return Err(..), so we try to handle before panicking.
187        const MAX_RETRY_PERIOD: u32 = 10; // max 10s
188        const WAIT_DUR_MS: u32 = 100; // retry every 100ms
189        let wait_dur = time::Duration::from_millis(WAIT_DUR_MS as u64);
190        const RETRY_LIMIT: u32 = (MAX_RETRY_PERIOD * 1000) / WAIT_DUR_MS;
191        const TRANSIENT_RETRIES: u32 = 8;
192        let mut err_count = 0;
193        let mut error_logged = false;
194
195        // Maybe block until the OS RNG is initialized
196        let mut read = 0;
197        if let Ok(n) = self.0.test_initialized(dest, true) { read = n };
198        let dest = &mut dest[read..];
199
200        loop {
201            if let Err(e) = self.try_fill_bytes(dest) {
202                if err_count >= RETRY_LIMIT {
203                    error!("OsRng failed too many times; last error: {}", e);
204                    panic!("OsRng failed too many times; last error: {}", e);
205                }
206
207                if e.kind.should_wait() {
208                    if !error_logged {
209                        warn!("OsRng failed; waiting up to {}s and retrying. Error: {}",
210                                MAX_RETRY_PERIOD, e);
211                        error_logged = true;
212                    }
213                    err_count += 1;
214                    thread::sleep(wait_dur);
215                    continue;
216                } else if e.kind.should_retry() {
217                    if !error_logged {
218                        warn!("OsRng failed; retrying up to {} times. Error: {}",
219                                TRANSIENT_RETRIES, e);
220                        error_logged = true;
221                    }
222                    err_count += (RETRY_LIMIT + TRANSIENT_RETRIES - 1)
223                            / TRANSIENT_RETRIES;    // round up
224                    continue;
225                } else {
226                    error!("OsRng failed: {}", e);
227                    panic!("OsRng fatal error: {}", e);
228                }
229            }
230
231            break;
232        }
233    }
234
235    fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
236        // Some systems do not support reading 0 random bytes.
237        // (And why waste a system call?)
238        if dest.len() == 0 { return Ok(()); }
239
240        let read = self.0.test_initialized(dest, false)?;
241        let dest = &mut dest[read..];
242
243        let max = self.0.max_chunk_size();
244        if dest.len() <= max {
245            trace!("OsRng: reading {} bytes via {}",
246                   dest.len(), self.0.method_str());
247        } else {
248            trace!("OsRng: reading {} bytes via {} in {} chunks of {} bytes",
249                   dest.len(), self.0.method_str(), (dest.len() + max) / max, max);
250        }
251        for slice in dest.chunks_mut(max) {
252            self.0.fill_chunk(slice)?;
253        }
254        Ok(())
255    }
256}
257
258trait OsRngImpl where Self: Sized {
259    // Create a new `OsRng` platform interface.
260    fn new() -> Result<Self, Error>;
261
262    // Fill a chunk with random bytes.
263    fn fill_chunk(&mut self, dest: &mut [u8]) -> Result<(), Error>;
264
265    // Test whether the OS RNG is initialized. This method may not be possible
266    // to support cheaply (or at all) on all operating systems.
267    //
268    // If `blocking` is set, this will cause the OS the block execution until
269    // its RNG is initialized.
270    //
271    // Random values that are read while this are stored in `dest`, the amount
272    // of read bytes is returned.
273    fn test_initialized(&mut self, _dest: &mut [u8], _blocking: bool)
274        -> Result<usize, Error> { Ok(0) }
275
276    // Maximum chunk size supported.
277    fn max_chunk_size(&self) -> usize { ::std::usize::MAX }
278
279    // Name of the OS interface (used for logging).
280    fn method_str(&self) -> &'static str;
281}
282
283#[cfg(any(target_os = "linux", target_os = "android",
284          target_os = "netbsd", target_os = "dragonfly",
285          target_os = "solaris", target_os = "redox",
286          target_os = "haiku", target_os = "emscripten",
287          target_os = "illumos"))]
288mod random_device;
289
290macro_rules! mod_use {
291    ($cond:meta, $module:ident) => {
292        #[$cond]
293        mod $module;
294        #[$cond]
295        use $module as imp;
296    }
297}
298
299mod_use!(cfg(target_os = "android"), linux_android);
300mod_use!(cfg(target_os = "bitrig"), openbsd_bitrig);
301mod_use!(cfg(target_os = "cloudabi"), cloudabi);
302mod_use!(cfg(target_os = "dragonfly"), dragonfly_haiku_emscripten);
303mod_use!(cfg(target_os = "emscripten"), dragonfly_haiku_emscripten);
304mod_use!(cfg(target_os = "freebsd"), freebsd);
305mod_use!(cfg(target_os = "fuchsia"), fuchsia);
306mod_use!(cfg(target_os = "haiku"), dragonfly_haiku_emscripten);
307mod_use!(cfg(target_os = "ios"), macos);
308mod_use!(cfg(target_os = "linux"), linux_android);
309mod_use!(cfg(target_os = "macos"), macos);
310mod_use!(cfg(target_os = "netbsd"), netbsd);
311mod_use!(cfg(target_os = "openbsd"), openbsd_bitrig);
312mod_use!(cfg(target_os = "redox"), redox);
313mod_use!(cfg(any(target_os = "solaris", target_os = "illumos")), solarish);
314mod_use!(cfg(windows), windows);
315mod_use!(cfg(target_env = "sgx"), sgx);
316
317mod_use!(
318    cfg(all(
319        target_arch = "wasm32",
320        not(target_os = "emscripten"),
321        feature = "wasm-bindgen"
322    )),
323    wasm32_bindgen
324);
325
326mod_use!(
327    cfg(all(
328        target_arch = "wasm32",
329        not(target_os = "emscripten"),
330        not(feature = "wasm-bindgen"),
331        feature = "stdweb",
332    )),
333    wasm32_stdweb
334);
335
336/// Per #678 we use run-time failure where WASM bindings are missing
337#[cfg(all(
338    target_arch = "wasm32",
339    not(target_os = "emscripten"),
340    not(feature = "wasm-bindgen"),
341    not(feature = "stdweb"),
342))]
343mod imp {
344    use rand_core::{Error, ErrorKind};
345    use super::OsRngImpl;
346    
347    #[derive(Clone, Debug)]
348    pub struct OsRng;
349
350    impl OsRngImpl for OsRng {
351        fn new() -> Result<OsRng, Error> {
352            Err(Error::new(ErrorKind::Unavailable,
353                "OsRng: support for wasm32 requires emscripten, stdweb or wasm-bindgen"))
354        }
355
356        fn fill_chunk(&mut self, _dest: &mut [u8]) -> Result<(), Error> {
357            unimplemented!()
358        }
359
360        fn method_str(&self) -> &'static str { unimplemented!() }
361    }
362}
363
364#[cfg(not(any(
365    target_os = "android",
366    target_os = "bitrig",
367    target_os = "cloudabi",
368    target_os = "dragonfly",
369    target_os = "emscripten",
370    target_os = "freebsd",
371    target_os = "fuchsia",
372    target_os = "haiku",
373    target_os = "ios",
374    target_os = "linux",
375    target_os = "macos",
376    target_os = "netbsd",
377    target_os = "openbsd",
378    target_os = "redox",
379    target_os = "solaris",
380    target_os = "illumos",
381    windows,
382    target_arch = "wasm32",
383    target_env = "sgx"
384)))]
385compile_error!("OS RNG support is not available for this platform");
386
387// Due to rustwasm/wasm-bindgen#201 this can't be defined in the inner os
388// modules, so hack around it for now and place it at the root.
389#[cfg(all(feature = "wasm-bindgen", target_arch = "wasm32"))]
390#[doc(hidden)]
391#[allow(missing_debug_implementations)]
392pub mod __wbg_shims {
393
394    // `extern { type Foo; }` isn't supported on 1.22 syntactically, so use a
395    // macro to work around that.
396    macro_rules! rust_122_compat {
397        ($($t:tt)*) => ($($t)*)
398    }
399
400    rust_122_compat! {
401        extern crate wasm_bindgen;
402
403        pub use wasm_bindgen::prelude::*;
404
405        #[wasm_bindgen]
406        extern "C" {
407            pub type Function;
408            #[wasm_bindgen(constructor)]
409            pub fn new(s: &str) -> Function;
410            #[wasm_bindgen(method)]
411            pub fn call(this: &Function, self_: &JsValue) -> JsValue;
412
413            pub type This;
414            #[wasm_bindgen(method, getter, structural, js_name = self)]
415            pub fn self_(me: &This) -> JsValue;
416            #[wasm_bindgen(method, getter, structural)]
417            pub fn crypto(me: &This) -> JsValue;
418
419            #[derive(Clone, Debug)]
420            pub type BrowserCrypto;
421
422            // TODO: these `structural` annotations here ideally wouldn't be here to
423            // avoid a JS shim, but for now with feature detection they're
424            // unavoidable.
425            #[wasm_bindgen(method, js_name = getRandomValues, structural, getter)]
426            pub fn get_random_values_fn(me: &BrowserCrypto) -> JsValue;
427            #[wasm_bindgen(method, js_name = getRandomValues, structural)]
428            pub fn get_random_values(me: &BrowserCrypto, buf: &mut [u8]);
429
430            #[wasm_bindgen(js_name = require)]
431            pub fn node_require(s: &str) -> NodeCrypto;
432
433            #[derive(Clone, Debug)]
434            pub type NodeCrypto;
435
436            #[wasm_bindgen(method, js_name = randomFillSync, structural)]
437            pub fn random_fill_sync(me: &NodeCrypto, buf: &mut [u8]);
438        }
439    }
440}