blob: 40360901185f1a0aa2c3d1d8c2314237b48a0e06 [file] [log] [blame] [edit]
// Copyright 2015-2016 Brian Smith.
// SPDX-License-Identifier: ISC
// Modifications copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0 OR ISC
//! 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.
//! # Example
//! ```
//! use aws_lc_rs::{rand, rand::SecureRandom};
//!
//! // Using `rand::fill`
//! let mut rand_bytes = [0u8; 32];
//! rand::fill(&mut rand_bytes).unwrap();
//!
//! // Using `SystemRandom`
//! let rng = rand::SystemRandom::new();
//! rng.fill(&mut rand_bytes).unwrap();
//!
//! // Using `rand::generate`
//! let random_array = rand::generate(&rng).unwrap();
//! let more_rand_bytes: [u8; 64] = random_array.expose();
//! ```
use crate::aws_lc::RAND_bytes;
use crate::error::Unspecified;
use crate::fips::indicator_check;
use core::fmt::Debug;
/// A secure random number generator.
pub trait SecureRandom: sealed::SecureRandom {
/// Fills `dest` with random bytes.
///
/// # Errors
/// `error::Unspecified` if unable to fill `dest`.
fn fill(&self, dest: &mut [u8]) -> Result<(), Unspecified>;
}
impl<T> SecureRandom for T
where
T: sealed::SecureRandom,
{
#[inline]
fn fill(&self, dest: &mut [u8]) -> Result<(), 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`.
///
/// # Errors
/// `error::Unspecified` if unable to fill buffer.
#[inline]
pub fn generate<T: RandomlyConstructable>(
rng: &dyn SecureRandom,
) -> Result<Random<T>, Unspecified> {
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`
}
impl<const T: usize> RandomlyConstructable for [u8; T] {
#[inline]
fn zero() -> Self {
[0; T]
}
#[inline]
fn as_mut_bytes(&mut self) -> &mut [u8] {
&mut self[..]
}
}
}
/// A type that can be returned by `aws_lc_rs::rand::generate()`.
pub trait RandomlyConstructable: sealed::RandomlyConstructable {}
impl<T> RandomlyConstructable for T where T: sealed::RandomlyConstructable {}
/// A secure random number generator where the random values come from the
/// underlying *AWS-LC* libcrypto.
///
/// A single `SystemRandom` may be shared across multiple threads safely.
//
// # FIPS
// Use this implementation for retrieving random bytes.
#[derive(Clone, Debug)]
pub struct SystemRandom(());
const SYSTEM_RANDOM: SystemRandom = SystemRandom(());
impl SystemRandom {
/// Constructs a new `SystemRandom`.
#[inline]
#[must_use]
pub fn new() -> Self {
Self::default()
}
}
impl Default for SystemRandom {
fn default() -> Self {
SYSTEM_RANDOM
}
}
impl sealed::SecureRandom for SystemRandom {
#[inline]
fn fill_impl(&self, dest: &mut [u8]) -> Result<(), Unspecified> {
fill(dest)
}
}
/// Fills `dest` with random bytes.
///
// # FIPS
// Use this for retrieving random bytes or [`SystemRandom`].
//
/// # Errors
/// `error::Unspecified` if unable to fill `dest`.
pub fn fill(dest: &mut [u8]) -> Result<(), Unspecified> {
if 1 != indicator_check!(unsafe { RAND_bytes(dest.as_mut_ptr(), dest.len()) }) {
return Err(Unspecified);
}
Ok(())
}
#[cfg(test)]
mod tests {
use crate::rand;
use core::array::IntoIter;
use crate::rand::{generate, SecureRandom, SystemRandom};
#[test]
fn test_secure_random_fill() {
// Collect enough random values so that the assertions below should never fail again
let mut random_array = [0u8; 1009];
let rng = SystemRandom::new();
rng.fill(&mut random_array).unwrap();
let (mean, variance) = mean_variance(&mut random_array.into_iter());
assert!((106f64..150f64).contains(&mean), "Mean: {mean}");
assert!(variance > 8f64);
println!("Mean: {mean} Variance: {variance}");
}
#[test]
fn test_rand_fill() {
// Collect enough random values so that the assertions below should never fail again
let mut random_array = [0u8; 1009];
rand::fill(&mut random_array).unwrap();
let (mean, variance) = mean_variance(&mut random_array.into_iter());
assert!((106f64..150f64).contains(&mean), "Mean: {mean}");
assert!(variance > 8f64);
println!("Mean: {mean} Variance: {variance}");
}
#[test]
fn test_randomly_constructable() {
let rando = SystemRandom::new();
let random_array = generate(&rando).unwrap();
// Collect enough random values so that the assertions below should never fail again
let random_array: [u8; 1009] = random_array.expose();
let (mean, variance) = mean_variance(&mut random_array.into_iter());
assert!((106f64..150f64).contains(&mean), "Mean: {mean}");
assert!(variance > 8f64);
println!("Mean: {mean} Variance: {variance}");
}
fn mean_variance<T: Into<f64>, const N: usize>(iterable: &mut IntoIter<T, N>) -> (f64, f64) {
let iter = iterable;
let mean: Option<T> = iter.next();
let mut mean = mean.unwrap().into();
let mut var_squared = 0f64;
let mut count = 1f64;
for value in iter.by_ref() {
count += 1f64;
let value = value.into();
let prev_mean = mean;
mean = prev_mean + (value - prev_mean) / count;
var_squared =
var_squared + ((value - prev_mean) * (value - mean) - var_squared) / count;
}
(mean, var_squared.sqrt())
}
}