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
157impl sealed::SecureRandom for SystemRandom {
158    #[inline(always)]
159    fn fill_impl(&self, dest: &mut [u8]) -> Result<(), error::Unspecified> {
160        fill_impl(dest)
161    }
162}
163
164impl crate::sealed::Sealed for SystemRandom {}
165
166#[cfg(any(
167    all(
168        any(target_os = "android", target_os = "linux"),
169        not(feature = "dev_urandom_fallback")
170    ),
171    target_arch = "wasm32",
172    windows
173))]
174use self::sysrand::fill as fill_impl;
175
176#[cfg(all(
177    any(target_os = "android", target_os = "linux"),
178    feature = "dev_urandom_fallback"
179))]
180use self::sysrand_or_urandom::fill as fill_impl;
181
182#[cfg(any(
183    target_os = "dragonfly",
184    target_os = "freebsd",
185    target_os = "illumos",
186    target_os = "netbsd",
187    target_os = "openbsd",
188    target_os = "solaris",
189))]
190use self::urandom::fill as fill_impl;
191
192#[cfg(any(target_os = "macos", target_os = "ios"))]
193use self::darwin::fill as fill_impl;
194
195#[cfg(any(target_os = "fuchsia"))]
196use self::fuchsia::fill as fill_impl;
197
198#[cfg(any(target_os = "android", target_os = "linux"))]
199mod sysrand_chunk {
200    use crate::{c, error};
201
202    #[inline]
203    pub fn chunk(dest: &mut [u8]) -> Result<usize, error::Unspecified> {
204        use libc::c_long;
205
206        // See `SYS_getrandom` in #include <sys/syscall.h>.
207
208        #[cfg(target_arch = "aarch64")]
209        const SYS_GETRANDOM: c_long = 278;
210
211        #[cfg(target_arch = "arm")]
212        const SYS_GETRANDOM: c_long = 384;
213
214        #[cfg(target_arch = "x86")]
215        const SYS_GETRANDOM: c_long = 355;
216
217        #[cfg(target_arch = "x86_64")]
218        const SYS_GETRANDOM: c_long = 318;
219
220        let chunk_len: c::size_t = dest.len();
221        let r = unsafe { libc::syscall(SYS_GETRANDOM, dest.as_mut_ptr(), chunk_len, 0) };
222        if r < 0 {
223            let errno;
224
225            #[cfg(target_os = "linux")]
226            {
227                errno = unsafe { *libc::__errno_location() };
228            }
229
230            #[cfg(target_os = "android")]
231            {
232                errno = unsafe { *libc::__errno() };
233            }
234
235            if errno == libc::EINTR {
236                // If an interrupt occurs while getrandom() is blocking to wait
237                // for the entropy pool, then EINTR is returned. Returning 0
238                // will cause the caller to try again.
239                return Ok(0);
240            }
241            return Err(error::Unspecified);
242        }
243        Ok(r as usize)
244    }
245}
246
247#[cfg(all(
248    target_arch = "wasm32",
249    target_vendor = "unknown",
250    target_os = "unknown",
251    target_env = "",
252))]
253mod sysrand_chunk {
254    use crate::error;
255
256    pub fn chunk(mut dest: &mut [u8]) -> Result<usize, error::Unspecified> {
257        // This limit is specified in
258        // https://www.w3.org/TR/WebCryptoAPI/#Crypto-method-getRandomValues.
259        const MAX_LEN: usize = 65_536;
260        if dest.len() > MAX_LEN {
261            dest = &mut dest[..MAX_LEN];
262        };
263
264        let _ = web_sys::window()
265            .ok_or(error::Unspecified)?
266            .crypto()
267            .map_err(|_| error::Unspecified)?
268            .get_random_values_with_u8_array(dest)
269            .map_err(|_| error::Unspecified)?;
270
271        Ok(dest.len())
272    }
273}
274
275#[cfg(windows)]
276mod sysrand_chunk {
277    use crate::{error, polyfill};
278
279    #[inline]
280    pub fn chunk(dest: &mut [u8]) -> Result<usize, error::Unspecified> {
281        use winapi::shared::wtypesbase::ULONG;
282
283        assert!(core::mem::size_of::<usize>() >= core::mem::size_of::<ULONG>());
284        let len = core::cmp::min(dest.len(), polyfill::usize_from_u32(ULONG::max_value()));
285        let result = unsafe {
286            winapi::um::ntsecapi::RtlGenRandom(
287                dest.as_mut_ptr() as *mut winapi::ctypes::c_void,
288                len as ULONG,
289            )
290        };
291        if result == 0 {
292            return Err(error::Unspecified);
293        }
294
295        Ok(len)
296    }
297}
298
299#[cfg(any(
300    target_os = "android",
301    target_os = "linux",
302    target_arch = "wasm32",
303    windows
304))]
305mod sysrand {
306    use super::sysrand_chunk::chunk;
307    use crate::error;
308
309    pub fn fill(dest: &mut [u8]) -> Result<(), error::Unspecified> {
310        let mut read_len = 0;
311        while read_len < dest.len() {
312            let chunk_len = chunk(&mut dest[read_len..])?;
313            read_len += chunk_len;
314        }
315        Ok(())
316    }
317}
318
319// Keep the `cfg` conditions in sync with the conditions in lib.rs.
320#[cfg(all(
321    any(target_os = "android", target_os = "linux"),
322    feature = "dev_urandom_fallback"
323))]
324mod sysrand_or_urandom {
325    use crate::error;
326
327    enum Mechanism {
328        Sysrand,
329        DevURandom,
330    }
331
332    #[inline]
333    pub fn fill(dest: &mut [u8]) -> Result<(), error::Unspecified> {
334        use once_cell::sync::Lazy;
335        static MECHANISM: Lazy<Mechanism> = Lazy::new(|| {
336            let mut dummy = [0u8; 1];
337            if super::sysrand_chunk::chunk(&mut dummy[..]).is_err() {
338                Mechanism::DevURandom
339            } else {
340                Mechanism::Sysrand
341            }
342        });
343
344        match *MECHANISM {
345            Mechanism::Sysrand => super::sysrand::fill(dest),
346            Mechanism::DevURandom => super::urandom::fill(dest),
347        }
348    }
349}
350
351#[cfg(any(
352    all(
353        any(target_os = "android", target_os = "linux"),
354        feature = "dev_urandom_fallback"
355    ),
356    target_os = "dragonfly",
357    target_os = "freebsd",
358    target_os = "netbsd",
359    target_os = "openbsd",
360    target_os = "solaris",
361    target_os = "illumos"
362))]
363mod urandom {
364    use crate::error;
365
366    #[cfg_attr(any(target_os = "android", target_os = "linux"), cold, inline(never))]
367    pub fn fill(dest: &mut [u8]) -> Result<(), error::Unspecified> {
368        extern crate std;
369
370        use once_cell::sync::Lazy;
371
372        static FILE: Lazy<Result<std::fs::File, std::io::Error>> =
373            Lazy::new(|| std::fs::File::open("/dev/urandom"));
374
375        match *FILE {
376            Ok(ref file) => {
377                use std::io::Read;
378                (&*file).read_exact(dest).map_err(|_| error::Unspecified)
379            }
380            Err(_) => Err(error::Unspecified),
381        }
382    }
383}
384
385#[cfg(any(target_os = "macos", target_os = "ios"))]
386mod darwin {
387    use crate::{c, error};
388
389    pub fn fill(dest: &mut [u8]) -> Result<(), error::Unspecified> {
390        let r = unsafe { SecRandomCopyBytes(kSecRandomDefault, dest.len(), dest.as_mut_ptr()) };
391        match r {
392            0 => Ok(()),
393            _ => Err(error::Unspecified),
394        }
395    }
396
397    // XXX: This is emulating an opaque type with a non-opaque type. TODO: Fix
398    // this when
399    // https://github.com/rust-lang/rfcs/pull/1861#issuecomment-274613536 is
400    // resolved.
401    #[repr(C)]
402    struct SecRandomRef([u8; 0]);
403
404    #[link(name = "Security", kind = "framework")]
405    extern "C" {
406        static kSecRandomDefault: &'static SecRandomRef;
407
408        // For now `rnd` must be `kSecRandomDefault`.
409        #[must_use]
410        fn SecRandomCopyBytes(
411            rnd: &'static SecRandomRef,
412            count: c::size_t,
413            bytes: *mut u8,
414        ) -> c::int;
415    }
416}
417
418#[cfg(any(target_os = "fuchsia"))]
419mod fuchsia {
420    use crate::error;
421
422    pub fn fill(dest: &mut [u8]) -> Result<(), error::Unspecified> {
423        unsafe {
424            zx_cprng_draw(dest.as_mut_ptr(), dest.len());
425        }
426        Ok(())
427    }
428
429    #[link(name = "zircon")]
430    extern "C" {
431        fn zx_cprng_draw(buffer: *mut u8, length: usize);
432    }
433}