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