getrandom/lib.rs
1//! Interface to the operating system's random number generator.
2//!
3//! # Supported targets
4//!
5//! | Target | Target Triple | Implementation
6//! | ----------------- | ------------------ | --------------
7//! | Linux, Android | `*‑linux‑*` | [`getrandom`][1] system call if available, otherwise [`/dev/urandom`][2] after successfully polling `/dev/random`
8//! | Windows | `*‑windows‑*` | [`BCryptGenRandom`]
9//! | macOS | `*‑apple‑darwin` | [`getentropy`][3]
10//! | iOS, tvOS, watchOS | `*‑apple‑ios`, `*-apple-tvos`, `*-apple-watchos` | [`CCRandomGenerateBytes`]
11//! | FreeBSD | `*‑freebsd` | [`getrandom`][5]
12//! | OpenBSD | `*‑openbsd` | [`getentropy`][7]
13//! | NetBSD | `*‑netbsd` | [`getrandom`][16] if available, otherwise [`kern.arandom`][8]
14//! | Dragonfly BSD | `*‑dragonfly` | [`getrandom`][9]
15//! | Solaris | `*‑solaris` | [`getrandom`][11] (with `GRND_RANDOM`)
16//! | illumos | `*‑illumos` | [`getrandom`][12]
17//! | Fuchsia OS | `*‑fuchsia` | [`cprng_draw`]
18//! | Redox | `*‑redox` | `/dev/urandom`
19//! | Haiku | `*‑haiku` | `/dev/urandom` (identical to `/dev/random`)
20//! | Hermit | `*-hermit` | [`sys_read_entropy`]
21//! | Hurd | `*-hurd-*` | [`getrandom`][17]
22//! | SGX | `x86_64‑*‑sgx` | [`RDRAND`]
23//! | VxWorks | `*‑wrs‑vxworks‑*` | `randABytes` after checking entropy pool initialization with `randSecure`
24//! | ESP-IDF | `*‑espidf` | [`esp_fill_random`]
25//! | Emscripten | `*‑emscripten` | [`getentropy`][13]
26//! | WASI | `wasm32‑wasi` | [`random_get`]
27//! | Web Browser and Node.js | `wasm*‑*‑unknown` | [`Crypto.getRandomValues`] if available, then [`crypto.randomFillSync`] if on Node.js, see [WebAssembly support]
28//! | SOLID | `*-kmc-solid_*` | `SOLID_RNG_SampleRandomBytes`
29//! | Nintendo 3DS | `*-nintendo-3ds` | [`getrandom`][18]
30//! | PS Vita | `*-vita-*` | [`getentropy`][13]
31//! | QNX Neutrino | `*‑nto-qnx*` | [`/dev/urandom`][14] (identical to `/dev/random`)
32//! | AIX | `*-ibm-aix` | [`/dev/urandom`][15]
33//!
34//! Pull Requests that add support for new targets to `getrandom` are always welcome.
35//!
36//! ## Unsupported targets
37//!
38//! By default, `getrandom` will not compile on unsupported targets, but certain
39//! features allow a user to select a "fallback" implementation if no supported
40//! implementation exists.
41//!
42//! All of the below mechanisms only affect unsupported
43//! targets. Supported targets will _always_ use their supported implementations.
44//! This prevents a crate from overriding a secure source of randomness
45//! (either accidentally or intentionally).
46//!
47//! ## `/dev/urandom` fallback on Linux and Android
48//!
49//! On Linux targets the fallback is present only if either `target_env` is `musl`,
50//! or `target_arch` is one of the following: `aarch64`, `arm`, `powerpc`, `powerpc64`,
51//! `s390x`, `x86`, `x86_64`. Other supported targets [require][platform-support]
52//! kernel versions which support `getrandom` system call, so fallback is not needed.
53//!
54//! On Android targets the fallback is present only for the following `target_arch`es:
55//! `aarch64`, `arm`, `x86`, `x86_64`. Other `target_arch`es (e.g. RISC-V) require
56//! sufficiently high API levels.
57//!
58//! The fallback can be disabled by enabling the `linux_disable_fallback` crate feature.
59//! Note that doing so will bump minimum supported Linux kernel version to 3.17 and
60//! Android API level to 23 (Marshmallow).
61//!
62//! ### RDRAND on x86
63//!
64//! *If the `rdrand` Cargo feature is enabled*, `getrandom` will fallback to using
65//! the [`RDRAND`] instruction to get randomness on `no_std` `x86`/`x86_64`
66//! targets. This feature has no effect on other CPU architectures.
67//!
68//! ### WebAssembly support
69//!
70//! This crate fully supports the
71//! [`wasm32-wasi`](https://github.com/CraneStation/wasi) and
72//! [`wasm32-unknown-emscripten`](https://www.hellorust.com/setup/emscripten/)
73//! targets. However, the `wasm32-unknown-unknown` target (i.e. the target used
74//! by `wasm-pack`) is not automatically
75//! supported since, from the target name alone, we cannot deduce which
76//! JavaScript interface is in use (or if JavaScript is available at all).
77//!
78//! Instead, *if the `js` Cargo feature is enabled*, this crate will assume
79//! that you are building for an environment containing JavaScript, and will
80//! call the appropriate methods. Both web browser (main window and Web Workers)
81//! and Node.js environments are supported, invoking the methods
82//! [described above](#supported-targets) using the [`wasm-bindgen`] toolchain.
83//!
84//! To enable the `js` Cargo feature, add the following to the `dependencies`
85//! section in your `Cargo.toml` file:
86//! ```toml
87//! [dependencies]
88//! getrandom = { version = "0.2", features = ["js"] }
89//! ```
90//!
91//! This can be done even if `getrandom` is not a direct dependency. Cargo
92//! allows crates to enable features for indirect dependencies.
93//!
94//! This feature should only be enabled for binary, test, or benchmark crates.
95//! Library crates should generally not enable this feature, leaving such a
96//! decision to *users* of their library. Also, libraries should not introduce
97//! their own `js` features *just* to enable `getrandom`'s `js` feature.
98//!
99//! This feature has no effect on targets other than `wasm32-unknown-unknown`.
100//!
101//! #### Node.js ES module support
102//!
103//! Node.js supports both [CommonJS modules] and [ES modules]. Due to
104//! limitations in wasm-bindgen's [`module`] support, we cannot directly
105//! support ES Modules running on Node.js. However, on Node v15 and later, the
106//! module author can add a simple shim to support the Web Cryptography API:
107//! ```js
108//! import { webcrypto } from 'node:crypto'
109//! globalThis.crypto = webcrypto
110//! ```
111//! This crate will then use the provided `webcrypto` implementation.
112//!
113//! ### Platform Support
114//! This crate generally supports the same operating system and platform versions
115//! that the Rust standard library does. Additional targets may be supported using
116//! pluggable custom implementations.
117//!
118//! This means that as Rust drops support for old versions of operating systems
119//! (such as old Linux kernel versions, Android API levels, etc) in stable releases,
120//! `getrandom` may create new patch releases (`0.N.x`) that remove support for
121//! outdated platform versions.
122//!
123//! ### Custom implementations
124//!
125//! The [`register_custom_getrandom!`] macro allows a user to mark their own
126//! function as the backing implementation for [`getrandom`]. See the macro's
127//! documentation for more information about writing and registering your own
128//! custom implementations.
129//!
130//! Note that registering a custom implementation only has an effect on targets
131//! that would otherwise not compile. Any supported targets (including those
132//! using `rdrand` and `js` Cargo features) continue using their normal
133//! implementations even if a function is registered.
134//!
135//! ## Early boot
136//!
137//! Sometimes, early in the boot process, the OS has not collected enough
138//! entropy to securely seed its RNG. This is especially common on virtual
139//! machines, where standard "random" events are hard to come by.
140//!
141//! Some operating system interfaces always block until the RNG is securely
142//! seeded. This can take anywhere from a few seconds to more than a minute.
143//! A few (Linux, NetBSD and Solaris) offer a choice between blocking and
144//! getting an error; in these cases, we always choose to block.
145//!
146//! On Linux (when the `getrandom` system call is not available), reading from
147//! `/dev/urandom` never blocks, even when the OS hasn't collected enough
148//! entropy yet. To avoid returning low-entropy bytes, we first poll
149//! `/dev/random` and only switch to `/dev/urandom` once this has succeeded.
150//!
151//! On OpenBSD, this kind of entropy accounting isn't available, and on
152//! NetBSD, blocking on it is discouraged. On these platforms, nonblocking
153//! interfaces are used, even when reliable entropy may not be available.
154//! On the platforms where it is used, the reliability of entropy accounting
155//! itself isn't free from controversy. This library provides randomness
156//! sourced according to the platform's best practices, but each platform has
157//! its own limits on the grade of randomness it can promise in environments
158//! with few sources of entropy.
159//!
160//! ## Error handling
161//!
162//! We always choose failure over returning known insecure "random" bytes. In
163//! general, on supported platforms, failure is highly unlikely, though not
164//! impossible. If an error does occur, then it is likely that it will occur
165//! on every call to `getrandom`, hence after the first successful call one
166//! can be reasonably confident that no errors will occur.
167//!
168//! [1]: https://manned.org/getrandom.2
169//! [2]: https://manned.org/urandom.4
170//! [3]: https://www.unix.com/man-page/mojave/2/getentropy/
171//! [4]: https://www.unix.com/man-page/mojave/4/urandom/
172//! [5]: https://www.freebsd.org/cgi/man.cgi?query=getrandom&manpath=FreeBSD+12.0-stable
173//! [7]: https://man.openbsd.org/getentropy.2
174//! [8]: https://man.netbsd.org/sysctl.7
175//! [9]: https://leaf.dragonflybsd.org/cgi/web-man?command=getrandom
176//! [11]: https://docs.oracle.com/cd/E88353_01/html/E37841/getrandom-2.html
177//! [12]: https://illumos.org/man/2/getrandom
178//! [13]: https://github.com/emscripten-core/emscripten/pull/12240
179//! [14]: https://www.qnx.com/developers/docs/7.1/index.html#com.qnx.doc.neutrino.utilities/topic/r/random.html
180//! [15]: https://www.ibm.com/docs/en/aix/7.3?topic=files-random-urandom-devices
181//! [16]: https://man.netbsd.org/getrandom.2
182//! [17]: https://www.gnu.org/software/libc/manual/html_mono/libc.html#index-getrandom
183//! [18]: https://github.com/rust3ds/shim-3ds/commit/b01d2568836dea2a65d05d662f8e5f805c64389d
184//!
185//! [`BCryptGenRandom`]: https://docs.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptgenrandom
186//! [`Crypto.getRandomValues`]: https://www.w3.org/TR/WebCryptoAPI/#Crypto-method-getRandomValues
187//! [`RDRAND`]: https://software.intel.com/en-us/articles/intel-digital-random-number-generator-drng-software-implementation-guide
188//! [`CCRandomGenerateBytes`]: https://opensource.apple.com/source/CommonCrypto/CommonCrypto-60074/include/CommonRandom.h.auto.html
189//! [`cprng_draw`]: https://fuchsia.dev/fuchsia-src/zircon/syscalls/cprng_draw
190//! [`crypto.randomFillSync`]: https://nodejs.org/api/crypto.html#cryptorandomfillsyncbuffer-offset-size
191//! [`esp_fill_random`]: https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/random.html#_CPPv415esp_fill_randomPv6size_t
192//! [`random_get`]: https://github.com/WebAssembly/WASI/blob/main/phases/snapshot/docs.md#-random_getbuf-pointeru8-buf_len-size---errno
193//! [WebAssembly support]: #webassembly-support
194//! [`wasm-bindgen`]: https://github.com/rustwasm/wasm-bindgen
195//! [`module`]: https://rustwasm.github.io/wasm-bindgen/reference/attributes/on-js-imports/module.html
196//! [CommonJS modules]: https://nodejs.org/api/modules.html
197//! [ES modules]: https://nodejs.org/api/esm.html
198//! [`sys_read_entropy`]: https://github.com/hermit-os/kernel/blob/315f58ff5efc81d9bf0618af85a59963ff55f8b1/src/syscalls/entropy.rs#L47-L55
199//! [platform-support]: https://doc.rust-lang.org/stable/rustc/platform-support.html
200
201#![doc(
202 html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk.png",
203 html_favicon_url = "https://www.rust-lang.org/favicon.ico",
204 html_root_url = "https://docs.rs/getrandom/0.2.15"
205)]
206#![no_std]
207#![warn(rust_2018_idioms, unused_lifetimes, missing_docs)]
208#![cfg_attr(docsrs, feature(doc_auto_cfg))]
209
210#[macro_use]
211extern crate cfg_if;
212
213use crate::util::{slice_as_uninit_mut, slice_assume_init_mut};
214use core::mem::MaybeUninit;
215
216mod error;
217mod util;
218// To prevent a breaking change when targets are added, we always export the
219// register_custom_getrandom macro, so old Custom RNG crates continue to build.
220#[cfg(feature = "custom")]
221mod custom;
222#[cfg(feature = "std")]
223mod error_impls;
224
225pub use crate::error::Error;
226
227// System-specific implementations.
228//
229// These should all provide getrandom_inner with the signature
230// `fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error>`.
231// The function MUST fully initialize `dest` when `Ok(())` is returned.
232// The function MUST NOT ever write uninitialized bytes into `dest`,
233// regardless of what value it returns.
234cfg_if! {
235 if #[cfg(any(target_os = "haiku", target_os = "redox", target_os = "nto", target_os = "aix"))] {
236 mod util_libc;
237 #[path = "use_file.rs"] mod imp;
238 } else if #[cfg(any(
239 target_os = "macos",
240 target_os = "openbsd",
241 target_os = "vita",
242 target_os = "emscripten",
243 ))] {
244 mod util_libc;
245 #[path = "getentropy.rs"] mod imp;
246 } else if #[cfg(any(
247 target_os = "dragonfly",
248 target_os = "freebsd",
249 target_os = "hurd",
250 target_os = "illumos",
251 // Check for target_arch = "arm" to only include the 3DS. Does not
252 // include the Nintendo Switch (which is target_arch = "aarch64").
253 all(target_os = "horizon", target_arch = "arm"),
254 ))] {
255 mod util_libc;
256 #[path = "getrandom.rs"] mod imp;
257 } else if #[cfg(all(
258 not(feature = "linux_disable_fallback"),
259 any(
260 // Rust supports Android API level 19 (KitKat) [0] and the next upgrade targets
261 // level 21 (Lollipop) [1], while `getrandom(2)` was added only in
262 // level 23 (Marshmallow). Note that it applies only to the "old" `target_arch`es,
263 // RISC-V Android targets sufficiently new API level, same will apply for potential
264 // new Android `target_arch`es.
265 // [0]: https://blog.rust-lang.org/2023/01/09/android-ndk-update-r25.html
266 // [1]: https://github.com/rust-lang/rust/pull/120593
267 all(
268 target_os = "android",
269 any(
270 target_arch = "aarch64",
271 target_arch = "arm",
272 target_arch = "x86",
273 target_arch = "x86_64",
274 ),
275 ),
276 // Only on these `target_arch`es Rust supports Linux kernel versions (3.2+)
277 // that precede the version (3.17) in which `getrandom(2)` was added:
278 // https://doc.rust-lang.org/stable/rustc/platform-support.html
279 all(
280 target_os = "linux",
281 any(
282 target_arch = "aarch64",
283 target_arch = "arm",
284 target_arch = "powerpc",
285 target_arch = "powerpc64",
286 target_arch = "s390x",
287 target_arch = "x86",
288 target_arch = "x86_64",
289 // Minimum supported Linux kernel version for MUSL targets
290 // is not specified explicitly (as of Rust 1.77) and they
291 // are used in practice to target pre-3.17 kernels.
292 target_env = "musl",
293 ),
294 )
295 ),
296 ))] {
297 mod util_libc;
298 mod use_file;
299 mod lazy;
300 #[path = "linux_android_with_fallback.rs"] mod imp;
301 } else if #[cfg(any(target_os = "android", target_os = "linux"))] {
302 mod util_libc;
303 #[path = "linux_android.rs"] mod imp;
304 } else if #[cfg(target_os = "solaris")] {
305 mod util_libc;
306 #[path = "solaris.rs"] mod imp;
307 } else if #[cfg(target_os = "netbsd")] {
308 mod util_libc;
309 #[path = "netbsd.rs"] mod imp;
310 } else if #[cfg(target_os = "fuchsia")] {
311 #[path = "fuchsia.rs"] mod imp;
312 } else if #[cfg(any(target_os = "ios", target_os = "visionos", target_os = "watchos", target_os = "tvos"))] {
313 #[path = "apple-other.rs"] mod imp;
314 } else if #[cfg(all(target_arch = "wasm32", target_os = "wasi"))] {
315 #[path = "wasi.rs"] mod imp;
316 } else if #[cfg(target_os = "hermit")] {
317 #[path = "hermit.rs"] mod imp;
318 } else if #[cfg(target_os = "vxworks")] {
319 mod util_libc;
320 #[path = "vxworks.rs"] mod imp;
321 } else if #[cfg(target_os = "solid_asp3")] {
322 #[path = "solid.rs"] mod imp;
323 } else if #[cfg(target_os = "espidf")] {
324 #[path = "espidf.rs"] mod imp;
325 } else if #[cfg(windows)] {
326 #[path = "windows.rs"] mod imp;
327 } else if #[cfg(all(target_arch = "x86_64", target_env = "sgx"))] {
328 mod lazy;
329 #[path = "rdrand.rs"] mod imp;
330 } else if #[cfg(all(feature = "rdrand",
331 any(target_arch = "x86_64", target_arch = "x86")))] {
332 mod lazy;
333 #[path = "rdrand.rs"] mod imp;
334 } else if #[cfg(all(feature = "js",
335 any(target_arch = "wasm32", target_arch = "wasm64"),
336 target_os = "unknown"))] {
337 #[path = "js.rs"] mod imp;
338 } else if #[cfg(feature = "custom")] {
339 use custom as imp;
340 } else if #[cfg(all(any(target_arch = "wasm32", target_arch = "wasm64"),
341 target_os = "unknown"))] {
342 compile_error!("the wasm*-unknown-unknown targets are not supported by \
343 default, you may need to enable the \"js\" feature. \
344 For more information see: \
345 https://docs.rs/getrandom/#webassembly-support");
346 } else {
347 compile_error!("target is not supported, for more information see: \
348 https://docs.rs/getrandom/#unsupported-targets");
349 }
350}
351
352/// Fill `dest` with random bytes from the system's preferred random number
353/// source.
354///
355/// This function returns an error on any failure, including partial reads. We
356/// make no guarantees regarding the contents of `dest` on error. If `dest` is
357/// empty, `getrandom` immediately returns success, making no calls to the
358/// underlying operating system.
359///
360/// Blocking is possible, at least during early boot; see module documentation.
361///
362/// In general, `getrandom` will be fast enough for interactive usage, though
363/// significantly slower than a user-space CSPRNG; for the latter consider
364/// [`rand::thread_rng`](https://docs.rs/rand/*/rand/fn.thread_rng.html).
365#[inline]
366pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> {
367 // SAFETY: The `&mut MaybeUninit<_>` reference doesn't escape, and
368 // `getrandom_uninit` guarantees it will never de-initialize any part of
369 // `dest`.
370 getrandom_uninit(unsafe { slice_as_uninit_mut(dest) })?;
371 Ok(())
372}
373
374/// Version of the `getrandom` function which fills `dest` with random bytes
375/// returns a mutable reference to those bytes.
376///
377/// On successful completion this function is guaranteed to return a slice
378/// which points to the same memory as `dest` and has the same length.
379/// In other words, it's safe to assume that `dest` is initialized after
380/// this function has returned `Ok`.
381///
382/// No part of `dest` will ever be de-initialized at any point, regardless
383/// of what is returned.
384///
385/// # Examples
386///
387/// ```ignore
388/// # // We ignore this test since `uninit_array` is unstable.
389/// #![feature(maybe_uninit_uninit_array)]
390/// # fn main() -> Result<(), getrandom::Error> {
391/// let mut buf = core::mem::MaybeUninit::uninit_array::<1024>();
392/// let buf: &mut [u8] = getrandom::getrandom_uninit(&mut buf)?;
393/// # Ok(()) }
394/// ```
395#[inline]
396pub fn getrandom_uninit(dest: &mut [MaybeUninit<u8>]) -> Result<&mut [u8], Error> {
397 if !dest.is_empty() {
398 imp::getrandom_inner(dest)?;
399 }
400 // SAFETY: `dest` has been fully initialized by `imp::getrandom_inner`
401 // since it returned `Ok`.
402 Ok(unsafe { slice_assume_init_mut(dest) })
403}