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