]>
Commit | Line | Data |
---|---|---|
416331ca XL |
1 | // Copyright 2019 Developers of the Rand project. |
2 | // | |
3 | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | |
4 | // https://www.apache.org/licenses/LICENSE-2.0> or the MIT license | |
5 | // <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your | |
6 | // option. This file may not be copied, modified, or distributed | |
7 | // except according to those terms. | |
8 | ||
9 | //! Interface to the random number generator of the operating system. | |
10 | //! | |
11 | //! # Platform sources | |
12 | //! | |
13 | //! | OS | interface | |
14 | //! |------------------|--------------------------------------------------------- | |
e1599b0c | 15 | //! | Linux, Android | [`getrandom`][1] system call if available, otherwise [`/dev/urandom`][2] after successfully polling `/dev/random` |
416331ca XL |
16 | //! | Windows | [`RtlGenRandom`][3] |
17 | //! | macOS | [`getentropy()`][19] if available, otherwise [`/dev/random`][20] (identical to `/dev/urandom`) | |
18 | //! | iOS | [`SecRandomCopyBytes`][4] | |
19 | //! | FreeBSD | [`getrandom()`][21] if available, otherwise [`kern.arandom`][5] | |
20 | //! | OpenBSD | [`getentropy`][6] | |
dfeec247 | 21 | //! | NetBSD | [`kern.arandom`][7] |
416331ca XL |
22 | //! | Dragonfly BSD | [`/dev/random`][8] |
23 | //! | Solaris, illumos | [`getrandom`][9] system call if available, otherwise [`/dev/random`][10] | |
24 | //! | Fuchsia OS | [`cprng_draw`][11] | |
25 | //! | Redox | [`rand:`][12] | |
26 | //! | CloudABI | [`cloudabi_sys_random_get`][13] | |
27 | //! | Haiku | `/dev/random` (identical to `/dev/urandom`) | |
28 | //! | L4RE, SGX, UEFI | [RDRAND][18] | |
29 | //! | Hermit | [RDRAND][18] as [`sys_rand`][22] is currently broken. | |
dfeec247 XL |
30 | //! | VxWorks | `randABytes` after checking entropy pool initialization with `randSecure` |
31 | //! | Web browsers | [`Crypto.getRandomValues`][14] (see [Support for WebAssembly and asm.js][16]) | |
32 | //! | Node.js | [`crypto.randomBytes`][15] (see [Support for WebAssembly and asm.js][16]) | |
416331ca XL |
33 | //! | WASI | [`__wasi_random_get`][17] |
34 | //! | |
35 | //! Getrandom doesn't have a blanket implementation for all Unix-like operating | |
36 | //! systems that reads from `/dev/urandom`. This ensures all supported operating | |
37 | //! systems are using the recommended interface and respect maximum buffer | |
38 | //! sizes. | |
39 | //! | |
e1599b0c | 40 | //! ## Unsupported targets |
416331ca | 41 | //! |
e1599b0c XL |
42 | //! By default, compiling `getrandom` for an unsupported target will result in |
43 | //! a compilation error. If you want to build an application which uses `getrandom` | |
44 | //! for such target, you can either: | |
45 | //! - Use [`[replace]`][replace] or [`[patch]`][patch] section in your `Cargo.toml` | |
46 | //! to switch to a custom implementation with a support of your target. | |
47 | //! - Enable the `dummy` feature to have getrandom use an implementation that always | |
48 | //! fails at run-time on unsupported targets. | |
416331ca | 49 | //! |
e1599b0c XL |
50 | //! [replace]: https://doc.rust-lang.org/cargo/reference/manifest.html#the-replace-section |
51 | //! [patch]: https://doc.rust-lang.org/cargo/reference/manifest.html#the-patch-section | |
416331ca | 52 | //! |
e1599b0c | 53 | //! ## Support for WebAssembly and asm.js |
416331ca | 54 | //! |
e1599b0c XL |
55 | //! Getrandom supports all of Rust's current `wasm32` targets, and it works with |
56 | //! both Node.js and web browsers. The three Emscripten targets | |
57 | //! `asmjs-unknown-emscripten`, `wasm32-unknown-emscripten`, and | |
58 | //! `wasm32-experimental-emscripten` use Emscripten's `/dev/random` emulation. | |
59 | //! The WASI target `wasm32-wasi` uses the [`__wasi_random_get`][17] function | |
60 | //! defined by the WASI standard. | |
61 | //! | |
62 | //! Getrandom also supports `wasm32-unknown-unknown` by directly calling | |
63 | //! JavaScript methods. Rust currently has two ways to do this: [bindgen] and | |
64 | //! [stdweb]. Getrandom supports using either one by enabling the | |
65 | //! `wasm-bindgen` or `stdweb` crate features. Note that if both features are | |
66 | //! enabled, `wasm-bindgen` will be used. If neither feature is enabled, calls | |
67 | //! to `getrandom` will always fail at runtime. | |
68 | //! | |
69 | //! [bindgen]: https://github.com/rust-lang/rust-bindgen | |
70 | //! [stdweb]: https://github.com/koute/stdweb | |
416331ca XL |
71 | //! |
72 | //! ## Early boot | |
73 | //! | |
74 | //! It is possible that early in the boot process the OS hasn't had enough time | |
75 | //! yet to collect entropy to securely seed its RNG, especially on virtual | |
76 | //! machines. | |
77 | //! | |
78 | //! Some operating systems always block the thread until the RNG is securely | |
79 | //! seeded. This can take anywhere from a few seconds to more than a minute. | |
80 | //! Others make a best effort to use a seed from before the shutdown and don't | |
81 | //! document much. | |
82 | //! | |
83 | //! A few, Linux, NetBSD and Solaris, offer a choice between blocking and | |
84 | //! getting an error; in these cases we always choose to block. | |
85 | //! | |
dfeec247 | 86 | //! On Linux (when the `getrandom` system call is not available) and on NetBSD |
416331ca XL |
87 | //! reading from `/dev/urandom` never blocks, even when the OS hasn't collected |
88 | //! enough entropy yet. To avoid returning low-entropy bytes, we first read from | |
89 | //! `/dev/random` and only switch to `/dev/urandom` once this has succeeded. | |
90 | //! | |
91 | //! # Error handling | |
92 | //! | |
93 | //! We always choose failure over returning insecure "random" bytes. In general, | |
94 | //! on supported platforms, failure is highly unlikely, though not impossible. | |
95 | //! If an error does occur, then it is likely that it will occur on every call to | |
96 | //! `getrandom`, hence after the first successful call one can be reasonably | |
97 | //! confident that no errors will occur. | |
98 | //! | |
99 | //! On unsupported platforms, `getrandom` always fails. See the [`Error`] type | |
100 | //! for more information on what data is returned on failure. | |
101 | //! | |
102 | //! [1]: http://man7.org/linux/man-pages/man2/getrandom.2.html | |
103 | //! [2]: http://man7.org/linux/man-pages/man4/urandom.4.html | |
104 | //! [3]: https://docs.microsoft.com/en-us/windows/desktop/api/ntsecapi/nf-ntsecapi-rtlgenrandom | |
105 | //! [4]: https://developer.apple.com/documentation/security/1399291-secrandomcopybytes?language=objc | |
106 | //! [5]: https://www.freebsd.org/cgi/man.cgi?query=random&sektion=4 | |
107 | //! [6]: https://man.openbsd.org/getentropy.2 | |
dfeec247 | 108 | //! [7]: https://netbsd.gw.com/cgi-bin/man-cgi?sysctl+7+NetBSD-8.0 |
416331ca XL |
109 | //! [8]: https://leaf.dragonflybsd.org/cgi/web-man?command=random§ion=4 |
110 | //! [9]: https://docs.oracle.com/cd/E88353_01/html/E37841/getrandom-2.html | |
111 | //! [10]: https://docs.oracle.com/cd/E86824_01/html/E54777/random-7d.html | |
e1599b0c | 112 | //! [11]: https://fuchsia.dev/fuchsia-src/zircon/syscalls/cprng_draw |
416331ca XL |
113 | //! [12]: https://github.com/redox-os/randd/blob/master/src/main.rs |
114 | //! [13]: https://github.com/nuxinl/cloudabi#random_get | |
115 | //! [14]: https://www.w3.org/TR/WebCryptoAPI/#Crypto-method-getRandomValues | |
116 | //! [15]: https://nodejs.org/api/crypto.html#crypto_crypto_randombytes_size_callback | |
dfeec247 | 117 | //! [16]: #support-for-webassembly-and-asmjs |
416331ca XL |
118 | //! [17]: https://github.com/WebAssembly/WASI/blob/master/design/WASI-core.md#__wasi_random_get |
119 | //! [18]: https://software.intel.com/en-us/articles/intel-digital-random-number-generator-drng-software-implementation-guide | |
120 | //! [19]: https://www.unix.com/man-page/mojave/2/getentropy/ | |
121 | //! [20]: https://www.unix.com/man-page/mojave/4/random/ | |
122 | //! [21]: https://www.freebsd.org/cgi/man.cgi?query=getrandom&manpath=FreeBSD+12.0-stable | |
123 | //! [22]: https://github.com/hermitcore/libhermit-rs/blob/09c38b0371cee6f56a541400ba453e319e43db53/src/syscalls/random.rs#L21 | |
124 | ||
125 | #![doc( | |
126 | html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk.png", | |
127 | html_favicon_url = "https://www.rust-lang.org/favicon.ico", | |
128 | html_root_url = "https://rust-random.github.io/rand/" | |
129 | )] | |
130 | #![no_std] | |
131 | #![cfg_attr(feature = "stdweb", recursion_limit = "128")] | |
e1599b0c | 132 | #![warn(rust_2018_idioms, unused_lifetimes, missing_docs)] |
416331ca XL |
133 | |
134 | #[macro_use] | |
135 | extern crate cfg_if; | |
136 | ||
137 | cfg_if! { | |
138 | if #[cfg(feature = "log")] { | |
139 | #[allow(unused)] | |
140 | #[macro_use] | |
141 | extern crate log; | |
142 | } else { | |
143 | #[allow(unused)] | |
144 | macro_rules! error { | |
145 | ($($x:tt)*) => {}; | |
146 | } | |
e1599b0c XL |
147 | #[allow(unused)] |
148 | macro_rules! warn { | |
149 | ($($x:tt)*) => {}; | |
150 | } | |
151 | #[allow(unused)] | |
152 | macro_rules! info { | |
153 | ($($x:tt)*) => {}; | |
154 | } | |
416331ca XL |
155 | } |
156 | } | |
157 | ||
158 | mod error; | |
159 | pub use crate::error::Error; | |
160 | ||
161 | #[allow(dead_code)] | |
162 | mod util; | |
416331ca | 163 | |
dfeec247 XL |
164 | #[cfg(target_os = "vxworks")] |
165 | #[allow(dead_code)] | |
166 | mod util_libc; | |
167 | ||
e1599b0c XL |
168 | cfg_if! { |
169 | // Unlike the other Unix, Fuchsia and iOS don't use the libc to make any calls. | |
170 | if #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "emscripten", | |
171 | target_os = "freebsd", target_os = "haiku", target_os = "illumos", | |
172 | target_os = "linux", target_os = "macos", target_os = "netbsd", | |
173 | target_os = "openbsd", target_os = "redox", target_os = "solaris"))] { | |
174 | #[allow(dead_code)] | |
175 | mod util_libc; | |
dfeec247 | 176 | // Keep std-only trait definitions for backwards compatibility |
e1599b0c XL |
177 | mod error_impls; |
178 | } else if #[cfg(feature = "std")] { | |
179 | mod error_impls; | |
180 | } | |
181 | } | |
416331ca XL |
182 | |
183 | // These targets read from a file as a fallback method. | |
184 | #[cfg(any( | |
185 | target_os = "android", | |
186 | target_os = "linux", | |
187 | target_os = "macos", | |
188 | target_os = "solaris", | |
189 | target_os = "illumos", | |
190 | ))] | |
191 | mod use_file; | |
192 | ||
193 | // System-specific implementations. | |
194 | // | |
195 | // These should all provide getrandom_inner with the same signature as getrandom. | |
196 | cfg_if! { | |
197 | if #[cfg(target_os = "android")] { | |
198 | #[path = "linux_android.rs"] mod imp; | |
199 | } else if #[cfg(target_os = "cloudabi")] { | |
200 | #[path = "cloudabi.rs"] mod imp; | |
201 | } else if #[cfg(target_os = "dragonfly")] { | |
202 | #[path = "use_file.rs"] mod imp; | |
203 | } else if #[cfg(target_os = "emscripten")] { | |
204 | #[path = "use_file.rs"] mod imp; | |
205 | } else if #[cfg(target_os = "freebsd")] { | |
dfeec247 | 206 | #[path = "bsd_arandom.rs"] mod imp; |
416331ca XL |
207 | } else if #[cfg(target_os = "fuchsia")] { |
208 | #[path = "fuchsia.rs"] mod imp; | |
209 | } else if #[cfg(target_os = "haiku")] { | |
210 | #[path = "use_file.rs"] mod imp; | |
211 | } else if #[cfg(target_os = "illumos")] { | |
212 | #[path = "solaris_illumos.rs"] mod imp; | |
213 | } else if #[cfg(target_os = "ios")] { | |
214 | #[path = "ios.rs"] mod imp; | |
215 | } else if #[cfg(target_os = "linux")] { | |
216 | #[path = "linux_android.rs"] mod imp; | |
217 | } else if #[cfg(target_os = "macos")] { | |
218 | #[path = "macos.rs"] mod imp; | |
219 | } else if #[cfg(target_os = "netbsd")] { | |
dfeec247 | 220 | #[path = "bsd_arandom.rs"] mod imp; |
416331ca XL |
221 | } else if #[cfg(target_os = "openbsd")] { |
222 | #[path = "openbsd.rs"] mod imp; | |
223 | } else if #[cfg(target_os = "redox")] { | |
224 | #[path = "use_file.rs"] mod imp; | |
225 | } else if #[cfg(target_os = "solaris")] { | |
226 | #[path = "solaris_illumos.rs"] mod imp; | |
227 | } else if #[cfg(target_os = "wasi")] { | |
228 | #[path = "wasi.rs"] mod imp; | |
dfeec247 XL |
229 | } else if #[cfg(target_os = "vxworks")] { |
230 | #[path = "vxworks.rs"] mod imp; | |
e1599b0c XL |
231 | } else if #[cfg(all(windows, getrandom_uwp))] { |
232 | #[path = "windows_uwp.rs"] mod imp; | |
416331ca XL |
233 | } else if #[cfg(windows)] { |
234 | #[path = "windows.rs"] mod imp; | |
235 | } else if #[cfg(all(target_arch = "x86_64", any( | |
236 | target_os = "hermit", | |
237 | target_os = "l4re", | |
238 | target_os = "uefi", | |
239 | target_env = "sgx", | |
240 | )))] { | |
241 | #[path = "rdrand.rs"] mod imp; | |
e1599b0c | 242 | } else if #[cfg(all(target_arch = "wasm32", target_os = "unknown"))] { |
416331ca XL |
243 | cfg_if! { |
244 | if #[cfg(feature = "wasm-bindgen")] { | |
245 | #[path = "wasm32_bindgen.rs"] mod imp; | |
246 | } else if #[cfg(feature = "stdweb")] { | |
247 | #[path = "wasm32_stdweb.rs"] mod imp; | |
248 | } else { | |
e1599b0c XL |
249 | // Always have an implementation for wasm32-unknown-unknown. |
250 | // See https://github.com/rust-random/getrandom/issues/87 | |
416331ca XL |
251 | #[path = "dummy.rs"] mod imp; |
252 | } | |
253 | } | |
e1599b0c | 254 | } else if #[cfg(feature = "dummy")] { |
416331ca | 255 | #[path = "dummy.rs"] mod imp; |
e1599b0c XL |
256 | } else { |
257 | compile_error!("\ | |
258 | target is not supported, for more information see: \ | |
259 | https://docs.rs/getrandom/#unsupported-targets\ | |
260 | "); | |
416331ca XL |
261 | } |
262 | } | |
263 | ||
264 | /// Fill `dest` with random bytes from the system's preferred random number | |
265 | /// source. | |
266 | /// | |
267 | /// This function returns an error on any failure, including partial reads. We | |
dfeec247 XL |
268 | /// make no guarantees regarding the contents of `dest` on error. If `dest` is |
269 | /// empty, `getrandom` immediately returns success, making no calls to the | |
270 | /// underlying operating system. | |
416331ca XL |
271 | /// |
272 | /// Blocking is possible, at least during early boot; see module documentation. | |
273 | /// | |
274 | /// In general, `getrandom` will be fast enough for interactive usage, though | |
275 | /// significantly slower than a user-space CSPRNG; for the latter consider | |
276 | /// [`rand::thread_rng`](https://docs.rs/rand/*/rand/fn.thread_rng.html). | |
277 | pub fn getrandom(dest: &mut [u8]) -> Result<(), error::Error> { | |
dfeec247 XL |
278 | if dest.is_empty() { |
279 | return Ok(()); | |
280 | } | |
416331ca XL |
281 | imp::getrandom_inner(dest) |
282 | } |