ring/
rand.rs

1// Copyright 2015-2016 Brian Smith.
2//
3// Permission to use, copy, modify, and/or distribute this software for any
4// purpose with or without fee is hereby granted, provided that the above
5// copyright notice and this permission notice appear in all copies.
6//
7// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
8// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
10// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
12// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
13// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14
15//! Cryptographic pseudo-random number generation.
16//!
17//! An application should create a single `SystemRandom` and then use it for
18//! all randomness generation. Functions that generate random bytes should take
19//! a `&dyn SecureRandom` parameter instead of instantiating their own. Besides
20//! being more efficient, this also helps document where non-deterministic
21//! (random) outputs occur. Taking a reference to a `SecureRandom` also helps
22//! with testing techniques like fuzzing, where it is useful to use a
23//! (non-secure) deterministic implementation of `SecureRandom` so that results
24//! can be replayed. Following this pattern also may help with sandboxing
25//! (seccomp filters on Linux in particular). See `SystemRandom`'s
26//! documentation for more details.
27
28use crate::error;
29
30/// A secure random number generator.
31pub trait SecureRandom: sealed::SecureRandom {
32    /// Fills `dest` with random bytes.
33    fn fill(&self, dest: &mut [u8]) -> Result<(), error::Unspecified>;
34}
35
36impl<T> SecureRandom for T
37where
38    T: sealed::SecureRandom,
39{
40    #[inline(always)]
41    fn fill(&self, dest: &mut [u8]) -> Result<(), error::Unspecified> {
42        self.fill_impl(dest)
43    }
44}
45
46/// A random value constructed from a `SecureRandom` that hasn't been exposed
47/// through any safe Rust interface.
48///
49/// Intentionally does not implement any traits other than `Sized`.
50pub struct Random<T: RandomlyConstructable>(T);
51
52impl<T: RandomlyConstructable> Random<T> {
53    /// Expose the random value.
54    #[inline]
55    pub fn expose(self) -> T {
56        self.0
57    }
58}
59
60/// Generate the new random value using `rng`.
61#[inline]
62pub fn generate<T: RandomlyConstructable>(
63    rng: &dyn SecureRandom,
64) -> Result<Random<T>, error::Unspecified>
65where
66    T: RandomlyConstructable,
67{
68    let mut r = T::zero();
69    rng.fill(r.as_mut_bytes())?;
70    Ok(Random(r))
71}
72
73pub(crate) mod sealed {
74    use crate::error;
75
76    pub trait SecureRandom: core::fmt::Debug {
77        /// Fills `dest` with random bytes.
78        fn fill_impl(&self, dest: &mut [u8]) -> Result<(), error::Unspecified>;
79    }
80
81    pub trait RandomlyConstructable: Sized {
82        fn zero() -> Self; // `Default::default()`
83        fn as_mut_bytes(&mut self) -> &mut [u8]; // `AsMut<[u8]>::as_mut`
84    }
85
86    macro_rules! impl_random_arrays {
87        [ $($len:expr)+ ] => {
88            $(
89                impl RandomlyConstructable for [u8; $len] {
90                    #[inline]
91                    fn zero() -> Self { [0; $len] }
92
93                    #[inline]
94                    fn as_mut_bytes(&mut self) -> &mut [u8] { &mut self[..] }
95                }
96            )+
97        }
98    }
99
100    impl_random_arrays![4 8 16 32 48 64 128 256];
101}
102
103/// A type that can be returned by `ring::rand::generate()`.
104pub trait RandomlyConstructable: self::sealed::RandomlyConstructable {}
105impl<T> RandomlyConstructable for T where T: self::sealed::RandomlyConstructable {}
106
107/// A secure random number generator where the random values come directly
108/// from the operating system.
109///
110/// A single `SystemRandom` may be shared across multiple threads safely.
111///
112/// `new()` is guaranteed to always succeed and to have low latency; it won't
113/// try to open or read from a file or do similar things. The first call to
114/// `fill()` may block a substantial amount of time since any and all
115/// initialization is deferred to it. Therefore, it may be a good idea to call
116/// `fill()` once at a non-latency-sensitive time to minimize latency for
117/// future calls.
118///
119/// On Linux (including Android), `fill()` will use the [`getrandom`] syscall.
120/// If the kernel is too old to support `getrandom` then by default `fill()`
121/// falls back to reading from `/dev/urandom`. This decision is made the first
122/// time `fill` *succeeds*. The fallback to `/dev/urandom` can be disabled by
123/// disabling the `dev_urandom_fallback` default feature; this should be done
124/// whenever the target system is known to support `getrandom`. When
125/// `/dev/urandom` is used, a file handle for `/dev/urandom` won't be opened
126/// until `fill` is called; `SystemRandom::new()` will not open `/dev/urandom`
127/// or do other potentially-high-latency things. The file handle will never be
128/// closed, until the operating system closes it at process shutdown. All
129/// instances of `SystemRandom` will share a single file handle. To properly
130/// implement seccomp filtering when the `dev_urandom_fallback` default feature
131/// is disabled, allow `getrandom` through. When the fallback is enabled, allow
132/// file opening, `getrandom`, and `read` up until the first call to `fill()`
133/// succeeds; after that, allow `getrandom` and `read`.
134///
135/// On macOS and iOS, `fill()` is implemented using `SecRandomCopyBytes`.
136///
137/// On wasm32-unknown-unknown (non-WASI), `fill()` is implemented using
138/// `window.crypto.getRandomValues()`. It must be used in a context where the
139/// global object is a `Window`; i.e. it must not be used in a Worker or a
140/// non-browser context.
141///
142/// On Windows, `fill` is implemented using the platform's API for secure
143/// random number generation.
144///
145/// [`getrandom`]: http://man7.org/linux/man-pages/man2/getrandom.2.html
146#[derive(Clone, Debug)]
147pub struct SystemRandom(());
148
149impl SystemRandom {
150    /// Constructs a new `SystemRandom`.
151    #[inline(always)]
152    pub fn new() -> Self {
153        Self(())
154    }
155}
156
157#[cfg(any(target_os = "wasi", target_os = "wasix"))]
158impl sealed::SecureRandom for SystemRandom {
159    #[inline(always)]
160    fn fill_impl(&self, dest: &mut [u8]) -> Result<(), error::Unspecified> {
161        getrandom::getrandom(dest).map_err(|_| error::Unspecified)
162    }
163}
164
165#[cfg(not(any(target_os = "wasi", target_os = "wasix")))]
166impl sealed::SecureRandom for SystemRandom {
167    #[inline(always)]
168    fn fill_impl(&self, dest: &mut [u8]) -> Result<(), error::Unspecified> {
169        fill_impl(dest)
170    }
171}
172
173impl crate::sealed::Sealed for SystemRandom {}
174
175#[cfg(any(
176    all(
177        any(target_os = "android", target_os = "linux"),
178        not(feature = "dev_urandom_fallback")
179    ),
180    all(target_arch = "wasm32", target_os = "unknown"),
181    windows
182))]
183use self::sysrand::fill as fill_impl;
184
185#[cfg(all(
186    any(target_os = "android", target_os = "linux"),
187    feature = "dev_urandom_fallback"
188))]
189use self::sysrand_or_urandom::fill as fill_impl;
190
191#[cfg(any(
192    target_os = "dragonfly",
193    target_os = "freebsd",
194    target_os = "illumos",
195    target_os = "netbsd",
196    target_os = "openbsd",
197    target_os = "solaris",
198))]
199use self::urandom::fill as fill_impl;
200
201#[cfg(any(target_os = "macos", target_os = "ios"))]
202use self::darwin::fill as fill_impl;
203
204#[cfg(any(target_os = "fuchsia"))]
205use self::fuchsia::fill as fill_impl;
206
207#[cfg(any(target_os = "android", target_os = "linux"))]
208mod sysrand_chunk {
209    use crate::{c, error};
210
211    #[inline]
212    pub fn chunk(dest: &mut [u8]) -> Result<usize, error::Unspecified> {
213        use libc::c_long;
214
215        // See `SYS_getrandom` in #include <sys/syscall.h>.
216
217        #[cfg(target_arch = "aarch64")]
218        const SYS_GETRANDOM: c_long = 278;
219
220        #[cfg(target_arch = "arm")]
221        const SYS_GETRANDOM: c_long = 384;
222
223        #[cfg(target_arch = "x86")]
224        const SYS_GETRANDOM: c_long = 355;
225
226        #[cfg(target_arch = "x86_64")]
227        const SYS_GETRANDOM: c_long = 318;
228
229        let chunk_len: c::size_t = dest.len();
230        let r = unsafe { libc::syscall(SYS_GETRANDOM, dest.as_mut_ptr(), chunk_len, 0) };
231        if r < 0 {
232            let errno;
233
234            #[cfg(target_os = "linux")]
235            {
236                errno = unsafe { *libc::__errno_location() };
237            }
238
239            #[cfg(target_os = "android")]
240            {
241                errno = unsafe { *libc::__errno() };
242            }
243
244            if errno == libc::EINTR {
245                // If an interrupt occurs while getrandom() is blocking to wait
246                // for the entropy pool, then EINTR is returned. Returning 0
247                // will cause the caller to try again.
248                return Ok(0);
249            }
250            return Err(error::Unspecified);
251        }
252        Ok(r as usize)
253    }
254}
255
256#[cfg(all(
257    target_arch = "wasm32",
258    target_vendor = "unknown",
259    target_os = "unknown",
260    target_env = "",
261))]
262mod sysrand_chunk {
263    use crate::error;
264
265    pub fn chunk(mut dest: &mut [u8]) -> Result<usize, error::Unspecified> {
266        // This limit is specified in
267        // https://www.w3.org/TR/WebCryptoAPI/#Crypto-method-getRandomValues.
268        const MAX_LEN: usize = 65_536;
269        if dest.len() > MAX_LEN {
270            dest = &mut dest[..MAX_LEN];
271        };
272
273        let _ = web_sys::window()
274            .ok_or(error::Unspecified)?
275            .crypto()
276            .map_err(|_| error::Unspecified)?
277            .get_random_values_with_u8_array(dest)
278            .map_err(|_| error::Unspecified)?;
279
280        Ok(dest.len())
281    }
282}
283
284#[cfg(windows)]
285mod sysrand_chunk {
286    use crate::{error, polyfill};
287
288    #[inline]
289    pub fn chunk(dest: &mut [u8]) -> Result<usize, error::Unspecified> {
290        use winapi::shared::wtypesbase::ULONG;
291
292        assert!(core::mem::size_of::<usize>() >= core::mem::size_of::<ULONG>());
293        let len = core::cmp::min(dest.len(), polyfill::usize_from_u32(ULONG::max_value()));
294        let result = unsafe {
295            winapi::um::ntsecapi::RtlGenRandom(
296                dest.as_mut_ptr() as *mut winapi::ctypes::c_void,
297                len as ULONG,
298            )
299        };
300        if result == 0 {
301            return Err(error::Unspecified);
302        }
303
304        Ok(len)
305    }
306}
307
308#[cfg(any(
309    target_os = "android",
310    target_os = "linux",
311    all(target_arch = "wasm32", target_os = "unknown"),
312    windows
313))]
314mod sysrand {
315    use super::sysrand_chunk::chunk;
316    use crate::error;
317
318    pub fn fill(dest: &mut [u8]) -> Result<(), error::Unspecified> {
319        let mut read_len = 0;
320        while read_len < dest.len() {
321            let chunk_len = chunk(&mut dest[read_len..])?;
322            read_len += chunk_len;
323        }
324        Ok(())
325    }
326}
327
328// Keep the `cfg` conditions in sync with the conditions in lib.rs.
329#[cfg(all(
330    any(target_os = "android", target_os = "linux"),
331    feature = "dev_urandom_fallback"
332))]
333mod sysrand_or_urandom {
334    use crate::error;
335
336    enum Mechanism {
337        Sysrand,
338        DevURandom,
339    }
340
341    #[inline]
342    pub fn fill(dest: &mut [u8]) -> Result<(), error::Unspecified> {
343        use once_cell::sync::Lazy;
344        static MECHANISM: Lazy<Mechanism> = Lazy::new(|| {
345            let mut dummy = [0u8; 1];
346            if super::sysrand_chunk::chunk(&mut dummy[..]).is_err() {
347                Mechanism::DevURandom
348            } else {
349                Mechanism::Sysrand
350            }
351        });
352
353        match *MECHANISM {
354            Mechanism::Sysrand => super::sysrand::fill(dest),
355            Mechanism::DevURandom => super::urandom::fill(dest),
356        }
357    }
358}
359
360#[cfg(any(
361    all(
362        any(target_os = "android", target_os = "linux"),
363        feature = "dev_urandom_fallback"
364    ),
365    target_os = "dragonfly",
366    target_os = "freebsd",
367    target_os = "netbsd",
368    target_os = "openbsd",
369    target_os = "solaris",
370    target_os = "illumos"
371))]
372mod urandom {
373    use crate::error;
374
375    #[cfg_attr(any(target_os = "android", target_os = "linux"), cold, inline(never))]
376    pub fn fill(dest: &mut [u8]) -> Result<(), error::Unspecified> {
377        extern crate std;
378
379        use once_cell::sync::Lazy;
380
381        static FILE: Lazy<Result<std::fs::File, std::io::Error>> =
382            Lazy::new(|| std::fs::File::open("/dev/urandom"));
383
384        match *FILE {
385            Ok(ref file) => {
386                use std::io::Read;
387                (&*file).read_exact(dest).map_err(|_| error::Unspecified)
388            }
389            Err(_) => Err(error::Unspecified),
390        }
391    }
392}
393
394#[cfg(any(target_os = "macos", target_os = "ios"))]
395mod darwin {
396    use crate::{c, error};
397
398    pub fn fill(dest: &mut [u8]) -> Result<(), error::Unspecified> {
399        let r = unsafe { SecRandomCopyBytes(kSecRandomDefault, dest.len(), dest.as_mut_ptr()) };
400        match r {
401            0 => Ok(()),
402            _ => Err(error::Unspecified),
403        }
404    }
405
406    // XXX: This is emulating an opaque type with a non-opaque type. TODO: Fix
407    // this when
408    // https://github.com/rust-lang/rfcs/pull/1861#issuecomment-274613536 is
409    // resolved.
410    #[repr(C)]
411    struct SecRandomRef([u8; 0]);
412
413    #[link(name = "Security", kind = "framework")]
414    extern "C" {
415        static kSecRandomDefault: &'static SecRandomRef;
416
417        // For now `rnd` must be `kSecRandomDefault`.
418        #[must_use]
419        fn SecRandomCopyBytes(
420            rnd: &'static SecRandomRef,
421            count: c::size_t,
422            bytes: *mut u8,
423        ) -> c::int;
424    }
425}
426
427#[cfg(any(target_os = "fuchsia"))]
428mod fuchsia {
429    use crate::error;
430
431    pub fn fill(dest: &mut [u8]) -> Result<(), error::Unspecified> {
432        unsafe {
433            zx_cprng_draw(dest.as_mut_ptr(), dest.len());
434        }
435        Ok(())
436    }
437
438    #[link(name = "zircon")]
439    extern "C" {
440        fn zx_cprng_draw(buffer: *mut u8, length: usize);
441    }
442}