blob: 9d1864fa1417d426535cec767cb384bdae2bad25 [file] [log] [blame]
// Copyright 2015-2016 Brian Smith.
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
//! Cryptographic pseudo-random number generation.
//!
//! An application should create a single `SystemRandom` and then use it for
//! all randomness generation. Functions that generate random bytes should take
//! a `&dyn SecureRandom` parameter instead of instantiating their own. Besides
//! being more efficient, this also helps document where non-deterministic
//! (random) outputs occur. Taking a reference to a `SecureRandom` also helps
//! with testing techniques like fuzzing, where it is useful to use a
//! (non-secure) deterministic implementation of `SecureRandom` so that results
//! can be replayed. Following this pattern also may help with sandboxing
//! (seccomp filters on Linux in particular). See `SystemRandom`'s
//! documentation for more details.
use crate::error;
/// A secure random number generator.
pub trait SecureRandom: sealed::SecureRandom {
/// Fills `dest` with random bytes.
fn fill(&self, dest: &mut [u8]) -> Result<(), error::Unspecified>;
}
impl<T> SecureRandom for T
where
T: sealed::SecureRandom,
{
#[inline(always)]
fn fill(&self, dest: &mut [u8]) -> Result<(), error::Unspecified> {
self.fill_impl(dest)
}
}
/// A random value constructed from a `SecureRandom` that hasn't been exposed
/// through any safe Rust interface.
///
/// Intentionally does not implement any traits other than `Sized`.
pub struct Random<T: RandomlyConstructable>(T);
impl<T: RandomlyConstructable> Random<T> {
/// Expose the random value.
#[inline]
pub fn expose(self) -> T {
self.0
}
}
/// Generate the new random value using `rng`.
#[inline]
pub fn generate<T: RandomlyConstructable>(
rng: &dyn SecureRandom,
) -> Result<Random<T>, error::Unspecified>
where
T: RandomlyConstructable,
{
let mut r = T::zero();
rng.fill(r.as_mut_bytes())?;
Ok(Random(r))
}
pub(crate) mod sealed {
use crate::error;
pub trait SecureRandom: core::fmt::Debug {
/// Fills `dest` with random bytes.
fn fill_impl(&self, dest: &mut [u8]) -> Result<(), error::Unspecified>;
}
pub trait RandomlyConstructable: Sized {
fn zero() -> Self; // `Default::default()`
fn as_mut_bytes(&mut self) -> &mut [u8]; // `AsMut<[u8]>::as_mut`
}
macro_rules! impl_random_arrays {
[ $($len:expr)+ ] => {
$(
impl RandomlyConstructable for [u8; $len] {
#[inline]
fn zero() -> Self { [0; $len] }
#[inline]
fn as_mut_bytes(&mut self) -> &mut [u8] { &mut self[..] }
}
)+
}
}
impl_random_arrays![4 8 16 32 48 64 128 256];
}
/// A type that can be returned by `ring::rand::generate()`.
pub trait RandomlyConstructable: self::sealed::RandomlyConstructable {}
impl<T> RandomlyConstructable for T where T: self::sealed::RandomlyConstructable {}
/// A secure random number generator where the random values come directly
/// from the operating system.
///
/// A single `SystemRandom` may be shared across multiple threads safely.
///
/// `new()` is guaranteed to always succeed and to have low latency; it won't
/// try to open or read from a file or do similar things. The first call to
/// `fill()` may block a substantial amount of time since any and all
/// initialization is deferred to it. Therefore, it may be a good idea to call
/// `fill()` once at a non-latency-sensitive time to minimize latency for
/// future calls.
///
/// On Linux (including Android), `fill()` will use the [`getrandom`] syscall.
/// If the kernel is too old to support `getrandom` then by default `fill()`
/// falls back to reading from `/dev/urandom`. This decision is made the first
/// time `fill` *succeeds*. The fallback to `/dev/urandom` can be disabled by
/// disabling the `dev_urandom_fallback` default feature; this should be done
/// whenever the target system is known to support `getrandom`. When
/// `/dev/urandom` is used, a file handle for `/dev/urandom` won't be opened
/// until `fill` is called; `SystemRandom::new()` will not open `/dev/urandom`
/// or do other potentially-high-latency things. The file handle will never be
/// closed, until the operating system closes it at process shutdown. All
/// instances of `SystemRandom` will share a single file handle. To properly
/// implement seccomp filtering when the `dev_urandom_fallback` default feature
/// is disabled, allow `getrandom` through. When the fallback is enabled, allow
/// file opening, `getrandom`, and `read` up until the first call to `fill()`
/// succeeds; after that, allow `getrandom` and `read`.
///
/// On macOS and iOS, `fill()` is implemented using `SecRandomCopyBytes`.
///
/// On wasm32-unknown-unknown (non-WASI), `fill()` is implemented using
/// `window.crypto.getRandomValues()`. It must be used in a context where the
/// global object is a `Window`; i.e. it must not be used in a Worker or a
/// non-browser context.
///
/// On Windows, `fill` is implemented using the platform's API for secure
/// random number generation.
///
/// [`getrandom`]: http://man7.org/linux/man-pages/man2/getrandom.2.html
#[derive(Clone, Debug)]
pub struct SystemRandom(());
impl SystemRandom {
/// Constructs a new `SystemRandom`.
#[inline(always)]
pub fn new() -> Self {
Self(())
}
}
impl sealed::SecureRandom for SystemRandom {
#[inline(always)]
fn fill_impl(&self, dest: &mut [u8]) -> Result<(), error::Unspecified> {
fill_impl(dest)
}
}
impl crate::sealed::Sealed for SystemRandom {}
#[cfg(any(
all(
any(target_os = "android", target_os = "linux"),
not(feature = "dev_urandom_fallback")
),
target_arch = "wasm32",
windows
))]
use self::sysrand::fill as fill_impl;
#[cfg(all(
any(target_os = "android", target_os = "linux"),
feature = "dev_urandom_fallback"
))]
use self::sysrand_or_urandom::fill as fill_impl;
#[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "illumos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "solaris",
))]
use self::urandom::fill as fill_impl;
#[cfg(any(target_os = "macos", target_os = "ios"))]
use self::darwin::fill as fill_impl;
#[cfg(any(target_os = "fuchsia"))]
use self::fuchsia::fill as fill_impl;
#[cfg(any(target_os = "android", target_os = "linux"))]
mod sysrand_chunk {
use crate::{c, error};
#[inline]
pub fn chunk(dest: &mut [u8]) -> Result<usize, error::Unspecified> {
use libc::c_long;
// See `SYS_getrandom` in #include <sys/syscall.h>.
#[cfg(target_arch = "aarch64")]
const SYS_GETRANDOM: c_long = 278;
#[cfg(target_arch = "arm")]
const SYS_GETRANDOM: c_long = 384;
#[cfg(target_arch = "x86")]
const SYS_GETRANDOM: c_long = 355;
#[cfg(target_arch = "x86_64")]
const SYS_GETRANDOM: c_long = 318;
let chunk_len: c::size_t = dest.len();
let r = unsafe { libc::syscall(SYS_GETRANDOM, dest.as_mut_ptr(), chunk_len, 0) };
if r < 0 {
let errno;
#[cfg(target_os = "linux")]
{
errno = unsafe { *libc::__errno_location() };
}
#[cfg(target_os = "android")]
{
errno = unsafe { *libc::__errno() };
}
if errno == libc::EINTR {
// If an interrupt occurs while getrandom() is blocking to wait
// for the entropy pool, then EINTR is returned. Returning 0
// will cause the caller to try again.
return Ok(0);
}
return Err(error::Unspecified);
}
Ok(r as usize)
}
}
#[cfg(all(
target_arch = "wasm32",
target_vendor = "unknown",
target_os = "unknown",
target_env = "",
))]
mod sysrand_chunk {
use crate::error;
pub fn chunk(mut dest: &mut [u8]) -> Result<usize, error::Unspecified> {
// This limit is specified in
// https://www.w3.org/TR/WebCryptoAPI/#Crypto-method-getRandomValues.
const MAX_LEN: usize = 65_536;
if dest.len() > MAX_LEN {
dest = &mut dest[..MAX_LEN];
};
let _ = web_sys::window()
.ok_or(error::Unspecified)?
.crypto()
.map_err(|_| error::Unspecified)?
.get_random_values_with_u8_array(dest)
.map_err(|_| error::Unspecified)?;
Ok(dest.len())
}
}
#[cfg(windows)]
mod sysrand_chunk {
use crate::{error, polyfill};
#[inline]
pub fn chunk(dest: &mut [u8]) -> Result<usize, error::Unspecified> {
use winapi::shared::wtypesbase::ULONG;
assert!(core::mem::size_of::<usize>() >= core::mem::size_of::<ULONG>());
let len = core::cmp::min(dest.len(), polyfill::usize_from_u32(ULONG::max_value()));
let result = unsafe {
winapi::um::ntsecapi::RtlGenRandom(
dest.as_mut_ptr() as *mut winapi::ctypes::c_void,
len as ULONG,
)
};
if result == 0 {
return Err(error::Unspecified);
}
Ok(len)
}
}
#[cfg(any(
target_os = "android",
target_os = "linux",
target_arch = "wasm32",
windows
))]
mod sysrand {
use super::sysrand_chunk::chunk;
use crate::error;
pub fn fill(dest: &mut [u8]) -> Result<(), error::Unspecified> {
let mut read_len = 0;
while read_len < dest.len() {
let chunk_len = chunk(&mut dest[read_len..])?;
read_len += chunk_len;
}
Ok(())
}
}
// Keep the `cfg` conditions in sync with the conditions in lib.rs.
#[cfg(all(
any(target_os = "android", target_os = "linux"),
feature = "dev_urandom_fallback"
))]
mod sysrand_or_urandom {
use crate::error;
enum Mechanism {
Sysrand,
DevURandom,
}
#[inline]
pub fn fill(dest: &mut [u8]) -> Result<(), error::Unspecified> {
use once_cell::sync::Lazy;
static MECHANISM: Lazy<Mechanism> = Lazy::new(|| {
let mut dummy = [0u8; 1];
if super::sysrand_chunk::chunk(&mut dummy[..]).is_err() {
Mechanism::DevURandom
} else {
Mechanism::Sysrand
}
});
match *MECHANISM {
Mechanism::Sysrand => super::sysrand::fill(dest),
Mechanism::DevURandom => super::urandom::fill(dest),
}
}
}
#[cfg(any(
all(
any(target_os = "android", target_os = "linux"),
feature = "dev_urandom_fallback"
),
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd",
target_os = "solaris",
target_os = "illumos"
))]
mod urandom {
use crate::error;
#[cfg_attr(any(target_os = "android", target_os = "linux"), cold, inline(never))]
pub fn fill(dest: &mut [u8]) -> Result<(), error::Unspecified> {
extern crate std;
use once_cell::sync::Lazy;
static FILE: Lazy<Result<std::fs::File, std::io::Error>> =
Lazy::new(|| std::fs::File::open("/dev/urandom"));
match *FILE {
Ok(ref file) => {
use std::io::Read;
(&*file).read_exact(dest).map_err(|_| error::Unspecified)
}
Err(_) => Err(error::Unspecified),
}
}
}
#[cfg(any(target_os = "macos", target_os = "ios"))]
mod darwin {
use crate::{c, error};
pub fn fill(dest: &mut [u8]) -> Result<(), error::Unspecified> {
let r = unsafe { SecRandomCopyBytes(kSecRandomDefault, dest.len(), dest.as_mut_ptr()) };
match r {
0 => Ok(()),
_ => Err(error::Unspecified),
}
}
// XXX: This is emulating an opaque type with a non-opaque type. TODO: Fix
// this when
// https://github.com/rust-lang/rfcs/pull/1861#issuecomment-274613536 is
// resolved.
#[repr(C)]
struct SecRandomRef([u8; 0]);
#[link(name = "Security", kind = "framework")]
extern "C" {
static kSecRandomDefault: &'static SecRandomRef;
// For now `rnd` must be `kSecRandomDefault`.
#[must_use]
fn SecRandomCopyBytes(
rnd: &'static SecRandomRef,
count: c::size_t,
bytes: *mut u8,
) -> c::int;
}
}
#[cfg(any(target_os = "fuchsia"))]
mod fuchsia {
use crate::error;
pub fn fill(dest: &mut [u8]) -> Result<(), error::Unspecified> {
unsafe {
zx_cprng_draw(dest.as_mut_ptr(), dest.len());
}
Ok(())
}
#[link(name = "zircon")]
extern "C" {
fn zx_cprng_draw(buffer: *mut u8, length: usize);
}
}