Skip to main content

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 when the "wasm32_unknown_unknown_js" feature is
138/// enabled, `fill()` is implemented using `window.crypto.getRandomValues()`.
139/// It must be used in a context where the global object is a `Window`; i.e. it
140/// must not be used in a Worker or a 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    feature = "wasm32_unknown_unknown_js",
249    target_arch = "wasm32",
250    target_vendor = "unknown",
251    target_os = "unknown",
252    target_env = "",
253))]
254mod sysrand_chunk {
255    use crate::error;
256
257    pub fn chunk(mut dest: &mut [u8]) -> Result<usize, error::Unspecified> {
258        // This limit is specified in
259        // https://www.w3.org/TR/WebCryptoAPI/#Crypto-method-getRandomValues.
260        const MAX_LEN: usize = 65_536;
261        if dest.len() > MAX_LEN {
262            dest = &mut dest[..MAX_LEN];
263        };
264
265        let _ = web_sys::window()
266            .ok_or(error::Unspecified)?
267            .crypto()
268            .map_err(|_| error::Unspecified)?
269            .get_random_values_with_u8_array(dest)
270            .map_err(|_| error::Unspecified)?;
271
272        Ok(dest.len())
273    }
274}
275
276#[cfg(windows)]
277mod sysrand_chunk {
278    use crate::{error, polyfill};
279
280    #[inline]
281    pub fn chunk(dest: &mut [u8]) -> Result<usize, error::Unspecified> {
282        use winapi::shared::wtypesbase::ULONG;
283
284        assert!(core::mem::size_of::<usize>() >= core::mem::size_of::<ULONG>());
285        let len = core::cmp::min(dest.len(), polyfill::usize_from_u32(ULONG::max_value()));
286        let result = unsafe {
287            winapi::um::ntsecapi::RtlGenRandom(
288                dest.as_mut_ptr() as *mut winapi::ctypes::c_void,
289                len as ULONG,
290            )
291        };
292        if result == 0 {
293            return Err(error::Unspecified);
294        }
295
296        Ok(len)
297    }
298}
299
300#[cfg(any(
301    target_os = "android",
302    target_os = "linux",
303    target_arch = "wasm32",
304    windows
305))]
306mod sysrand {
307    use super::sysrand_chunk::chunk;
308    use crate::error;
309
310    pub fn fill(dest: &mut [u8]) -> Result<(), error::Unspecified> {
311        let mut read_len = 0;
312        while read_len < dest.len() {
313            let chunk_len = chunk(&mut dest[read_len..])?;
314            read_len += chunk_len;
315        }
316        Ok(())
317    }
318}
319
320// Keep the `cfg` conditions in sync with the conditions in lib.rs.
321#[cfg(all(
322    any(target_os = "android", target_os = "linux"),
323    feature = "dev_urandom_fallback"
324))]
325mod sysrand_or_urandom {
326    use crate::error;
327
328    enum Mechanism {
329        Sysrand,
330        DevURandom,
331    }
332
333    #[inline]
334    pub fn fill(dest: &mut [u8]) -> Result<(), error::Unspecified> {
335        use once_cell::sync::Lazy;
336        static MECHANISM: Lazy<Mechanism> = Lazy::new(|| {
337            let mut dummy = [0u8; 1];
338            if super::sysrand_chunk::chunk(&mut dummy[..]).is_err() {
339                Mechanism::DevURandom
340            } else {
341                Mechanism::Sysrand
342            }
343        });
344
345        match *MECHANISM {
346            Mechanism::Sysrand => super::sysrand::fill(dest),
347            Mechanism::DevURandom => super::urandom::fill(dest),
348        }
349    }
350}
351
352#[cfg(any(
353    all(
354        any(target_os = "android", target_os = "linux"),
355        feature = "dev_urandom_fallback"
356    ),
357    target_os = "dragonfly",
358    target_os = "freebsd",
359    target_os = "netbsd",
360    target_os = "openbsd",
361    target_os = "solaris",
362    target_os = "illumos"
363))]
364mod urandom {
365    use crate::error;
366
367    #[cfg_attr(any(target_os = "android", target_os = "linux"), cold, inline(never))]
368    pub fn fill(dest: &mut [u8]) -> Result<(), error::Unspecified> {
369        extern crate std;
370
371        use once_cell::sync::Lazy;
372
373        static FILE: Lazy<Result<std::fs::File, std::io::Error>> =
374            Lazy::new(|| std::fs::File::open("/dev/urandom"));
375
376        match *FILE {
377            Ok(ref file) => {
378                use std::io::Read;
379                (&*file).read_exact(dest).map_err(|_| error::Unspecified)
380            }
381            Err(_) => Err(error::Unspecified),
382        }
383    }
384}
385
386#[cfg(any(target_os = "macos", target_os = "ios"))]
387mod darwin {
388    use crate::{c, error};
389
390    pub fn fill(dest: &mut [u8]) -> Result<(), error::Unspecified> {
391        let r = unsafe { SecRandomCopyBytes(kSecRandomDefault, dest.len(), dest.as_mut_ptr()) };
392        match r {
393            0 => Ok(()),
394            _ => Err(error::Unspecified),
395        }
396    }
397
398    // XXX: This is emulating an opaque type with a non-opaque type. TODO: Fix
399    // this when
400    // https://github.com/rust-lang/rfcs/pull/1861#issuecomment-274613536 is
401    // resolved.
402    #[repr(C)]
403    struct SecRandomRef([u8; 0]);
404
405    #[link(name = "Security", kind = "framework")]
406    extern "C" {
407        static kSecRandomDefault: &'static SecRandomRef;
408
409        // For now `rnd` must be `kSecRandomDefault`.
410        #[must_use]
411        fn SecRandomCopyBytes(
412            rnd: &'static SecRandomRef,
413            count: c::size_t,
414            bytes: *mut u8,
415        ) -> c::int;
416    }
417}
418
419#[cfg(any(target_os = "fuchsia"))]
420mod fuchsia {
421    use crate::error;
422
423    pub fn fill(dest: &mut [u8]) -> Result<(), error::Unspecified> {
424        unsafe {
425            zx_cprng_draw(dest.as_mut_ptr(), dest.len());
426        }
427        Ok(())
428    }
429
430    #[link(name = "zircon")]
431    extern "C" {
432        fn zx_cprng_draw(buffer: *mut u8, length: usize);
433    }
434}