blob: 1d0208e7717f0985ab543c5869fbbcc22c2c107a [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 `&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::Sealed {
/// Fills `dest` with random bytes.
fn fill(&self, dest: &mut [u8]) -> Result<(), error::Unspecified>;
}
/// 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, `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`. Library crates should avoid
/// explicitly enabling the `dev_urandom_fallback` feature.
///
/// On macOS and iOS, `fill()` is implemented using `SecRandomCopyBytes`.
///
/// On Redox, `fill()` is implemented by reading from `rand:`.
///
/// On Windows, `fill` is implemented using the platform's API for secure
/// random number generation.
///
/// Otherwise, `fill()` is implemented by reading from `/dev/urandom`. (This is
/// something that should be improved for any platform that adds something
/// better.)
///
/// When `/dev/urandom` is used, a file handle for `/dev/urandom` won't be
/// opened until `fill` is called. In particular, `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.
///
/// On Linux, 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`.
///
/// [`getrandom`]: http://man7.org/linux/man-pages/man2/getrandom.2.html
pub struct SystemRandom;
impl SystemRandom {
/// Constructs a new `SystemRandom`.
#[inline(always)]
pub fn new() -> SystemRandom { SystemRandom }
}
impl SecureRandom for SystemRandom {
#[inline(always)]
fn fill(&self, dest: &mut [u8]) -> Result<(), error::Unspecified> { fill_impl(dest) }
}
impl sealed::Sealed for SystemRandom {}
#[cfg(all(
feature = "use_heap",
not(any(
target_os = "linux",
target_os = "macos",
target_os = "ios",
target_os = "fuchsia",
windows
))
))]
use self::urandom::fill as fill_impl;
#[cfg(any(
all(target_os = "linux", not(feature = "dev_urandom_fallback")),
windows
))]
use self::sysrand::fill as fill_impl;
#[cfg(all(target_os = "linux", feature = "dev_urandom_fallback"))]
use self::sysrand_or_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;
use crate::sealed;
#[cfg(target_os = "linux")]
mod sysrand_chunk {
use crate::{c, error};
use libc;
extern "C" {
static GFp_SYS_GETRANDOM: c::long;
}
#[inline]
pub fn chunk(dest: &mut [u8]) -> Result<usize, error::Unspecified> {
let chunk_len: c::size_t = dest.len();
let flags: c::uint = 0;
let r = unsafe { libc::syscall(GFp_SYS_GETRANDOM, dest.as_mut_ptr(), chunk_len, flags) };
if r < 0 {
if unsafe { *libc::__errno_location() } == 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(windows)]
mod sysrand_chunk {
use crate::{c, error};
use core;
#[link(name = "advapi32")]
extern "system" {
#[link_name = "SystemFunction036"]
#[must_use]
fn RtlGenRandom(
random_buffer: *mut u8, random_buffer_length: c::win32::ULONG,
) -> c::win32::BOOLEAN;
}
#[inline]
pub fn chunk(dest: &mut [u8]) -> Result<usize, error::Unspecified> {
assert!(core::mem::size_of::<usize>() >= core::mem::size_of::<c::win32::ULONG>());
let max_chunk_len = c::win32::ULONG::from(0u32).wrapping_sub(1) as usize;
assert_eq!(max_chunk_len, 0xffff_ffff);
let len = core::cmp::min(dest.len(), max_chunk_len);
if unsafe { RtlGenRandom(dest.as_mut_ptr(), len as c::win32::ULONG) } == 0 {
return Err(error::Unspecified);
}
Ok(len)
}
}
#[cfg(any(target_os = "linux", 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(
feature = "use_heap",
any(target_os = "redox", unix),
not(any(target_os = "macos", target_os = "ios")),
not(all(target_os = "linux", not(feature = "dev_urandom_fallback"))),
not(any(target_os = "fuchsia")),
))]
mod urandom {
use crate::error;
use std;
pub fn fill(dest: &mut [u8]) -> Result<(), error::Unspecified> {
use lazy_static::lazy_static;
#[cfg(target_os = "redox")]
static RANDOM_PATH: &str = "rand:";
#[cfg(unix)]
static RANDOM_PATH: &str = "/dev/urandom";
lazy_static! {
static ref FILE: Result<std::fs::File, std::io::Error> =
std::fs::File::open(RANDOM_PATH);
}
match *FILE {
Ok(ref file) => {
use std::io::Read;
(&*file).read_exact(dest).map_err(|_| error::Unspecified)
},
Err(_) => Err(error::Unspecified),
}
}
}
// Keep the `cfg` conditions in sync with the conditions in lib.rs.
#[cfg(all(target_os = "linux", feature = "dev_urandom_fallback"))]
mod sysrand_or_urandom {
use crate::error;
enum Mechanism {
Sysrand,
DevURandom,
}
pub fn fill(dest: &mut [u8]) -> Result<(), error::Unspecified> {
use lazy_static::lazy_static;
lazy_static! {
static ref MECHANISM: Mechanism = {
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(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);
}
}
#[cfg(test)]
mod tests {
use crate::rand::{self, SecureRandom};
#[test]
fn test_system_random_lengths() {
// Test that `fill` succeeds for various interesting lengths. `256` and
// multiples thereof are interesting because that's an edge case for
// `getrandom` on Linux.
let lengths = [0, 1, 2, 3, 96, 255, 256, 257, 511, 512, 513, 4096];
for len in lengths.iter() {
let mut buf = vec![0; *len];
let rng = rand::SystemRandom::new();
assert!(rng.fill(&mut buf).is_ok());
// If `len` < 96 then there's a big chance of false positives, but
// otherwise the likelihood of a false positive is so too low to
// worry about.
if *len >= 96 {
assert!(buf.iter().any(|x| *x != 0));
}
}
}
}