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§ion=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}