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 
28 use crate::error;
29 
30 /// A secure random number generator.
31 pub trait SecureRandom: sealed::SecureRandom {
32     /// Fills `dest` with random bytes.
fill(&self, dest: &mut [u8]) -> Result<(), error::Unspecified>33     fn fill(&self, dest: &mut [u8]) -> Result<(), error::Unspecified>;
34 }
35 
36 impl<T> SecureRandom for T
37 where
38     T: sealed::SecureRandom,
39 {
40     #[inline(always)]
fill(&self, dest: &mut [u8]) -> Result<(), error::Unspecified>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`.
50 pub struct Random<T: RandomlyConstructable>(T);
51 
52 impl<T: RandomlyConstructable> Random<T> {
53     /// Expose the random value.
54     #[inline]
expose(self) -> T55     pub fn expose(self) -> T {
56         self.0
57     }
58 }
59 
60 /// Generate the new random value using `rng`.
61 #[inline]
generate<T: RandomlyConstructable>( rng: &dyn SecureRandom, ) -> Result<Random<T>, error::Unspecified> where T: RandomlyConstructable,62 pub fn generate<T: RandomlyConstructable>(
63     rng: &dyn SecureRandom,
64 ) -> Result<Random<T>, error::Unspecified>
65 where
66     T: RandomlyConstructable,
67 {
68     let mut r = T::zero();
69     rng.fill(r.as_mut_bytes())?;
70     Ok(Random(r))
71 }
72 
73 pub(crate) mod sealed {
74     use crate::error;
75 
76     pub trait SecureRandom: core::fmt::Debug {
77         /// Fills `dest` with random bytes.
fill_impl(&self, dest: &mut [u8]) -> Result<(), error::Unspecified>78         fn fill_impl(&self, dest: &mut [u8]) -> Result<(), error::Unspecified>;
79     }
80 
81     pub trait RandomlyConstructable: Sized {
zero() -> Self82         fn zero() -> Self; // `Default::default()`
as_mut_bytes(&mut self) -> &mut [u8]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()`.
104 pub trait RandomlyConstructable: self::sealed::RandomlyConstructable {}
105 impl<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)]
147 pub struct SystemRandom(());
148 
149 impl SystemRandom {
150     /// Constructs a new `SystemRandom`.
151     #[inline(always)]
new() -> Self152     pub fn new() -> Self {
153         Self(())
154     }
155 }
156 
157 impl sealed::SecureRandom for SystemRandom {
158     #[inline(always)]
fill_impl(&self, dest: &mut [u8]) -> Result<(), error::Unspecified>159     fn fill_impl(&self, dest: &mut [u8]) -> Result<(), error::Unspecified> {
160         fill_impl(dest)
161     }
162 }
163 
164 impl 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 ))]
174 use self::sysrand::fill as fill_impl;
175 
176 #[cfg(all(
177     any(target_os = "android", target_os = "linux"),
178     feature = "dev_urandom_fallback"
179 ))]
180 use 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 ))]
190 use self::urandom::fill as fill_impl;
191 
192 #[cfg(any(target_os = "macos", target_os = "ios"))]
193 use self::darwin::fill as fill_impl;
194 
195 #[cfg(any(target_os = "fuchsia"))]
196 use self::fuchsia::fill as fill_impl;
197 
198 #[cfg(any(target_os = "android", target_os = "linux"))]
199 mod sysrand_chunk {
200     use crate::{c, error};
201 
202     #[inline]
chunk(dest: &mut [u8]) -> Result<usize, error::Unspecified>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 ))]
253 mod sysrand_chunk {
254     use crate::error;
255 
chunk(mut dest: &mut [u8]) -> Result<usize, error::Unspecified>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)]
276 mod sysrand_chunk {
277     use crate::{error, polyfill};
278 
279     #[inline]
chunk(dest: &mut [u8]) -> Result<usize, error::Unspecified>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 ))]
305 mod sysrand {
306     use super::sysrand_chunk::chunk;
307     use crate::error;
308 
fill(dest: &mut [u8]) -> Result<(), error::Unspecified>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 ))]
324 mod sysrand_or_urandom {
325     use crate::error;
326 
327     enum Mechanism {
328         Sysrand,
329         DevURandom,
330     }
331 
332     #[inline]
fill(dest: &mut [u8]) -> Result<(), error::Unspecified>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 ))]
363 mod urandom {
364     use crate::error;
365 
366     #[cfg_attr(any(target_os = "android", target_os = "linux"), cold, inline(never))]
fill(dest: &mut [u8]) -> Result<(), error::Unspecified>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"))]
386 mod darwin {
387     use crate::{c, error};
388 
fill(dest: &mut [u8]) -> Result<(), error::Unspecified>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]
SecRandomCopyBytes( rnd: &'static SecRandomRef, count: c::size_t, bytes: *mut u8, ) -> c::int410         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"))]
419 mod fuchsia {
420     use crate::error;
421 
fill(dest: &mut [u8]) -> Result<(), error::Unspecified>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" {
zx_cprng_draw(buffer: *mut u8, length: usize)431         fn zx_cprng_draw(buffer: *mut u8, length: usize);
432     }
433 }
434