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