#[cfg(feature = "alloc")]
use alloc::{boxed::Box, rc::Rc, sync::Arc};
use generic_array::{ArrayLength, GenericArray};
#[cfg(all(feature = "getrandom", not(target_os = "vxworks")))]
pub use getrandom;
#[cfg(feature = "rand_compat")]
#[cfg_attr(docsrs, doc(cfg(feature = "rand_compat")))]
pub use rand;
pub trait Csprng {
fn fill_bytes(&self, dst: &mut [u8]);
}
impl<R: Csprng + ?Sized> Csprng for &R {
fn fill_bytes(&self, dst: &mut [u8]) {
R::fill_bytes(self, dst)
}
}
impl<R: Csprng + ?Sized> Csprng for &mut R {
fn fill_bytes(&self, dst: &mut [u8]) {
R::fill_bytes(self, dst)
}
}
#[cfg(feature = "alloc")]
impl<R: Csprng + ?Sized> Csprng for Box<R> {
fn fill_bytes(&self, dst: &mut [u8]) {
R::fill_bytes(self, dst)
}
}
#[cfg(feature = "alloc")]
impl<R: Csprng + ?Sized> Csprng for Rc<R> {
fn fill_bytes(&self, dst: &mut [u8]) {
R::fill_bytes(self, dst)
}
}
#[cfg(feature = "alloc")]
impl<R: Csprng + ?Sized> Csprng for Arc<R> {
fn fill_bytes(&self, dst: &mut [u8]) {
R::fill_bytes(self, dst)
}
}
#[cfg(feature = "getrandom")]
#[cfg_attr(docsrs, doc(cfg(feature = "getrandom")))]
impl Csprng for rand_core::OsRng {
fn fill_bytes(&self, dst: &mut [u8]) {
rand_core::RngCore::fill_bytes(&mut { *self }, dst)
}
}
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
impl Csprng for rand::rngs::ThreadRng {
fn fill_bytes(&self, dst: &mut [u8]) {
rand_core::RngCore::fill_bytes(&mut self.clone(), dst)
}
}
#[cfg(feature = "rand_compat")]
impl rand_core::CryptoRng for &dyn Csprng {}
#[cfg(feature = "rand_compat")]
impl rand_core::RngCore for &dyn Csprng {
fn next_u32(&mut self) -> u32 {
rand_core::impls::next_u32_via_fill(self)
}
fn next_u64(&mut self) -> u64 {
rand_core::impls::next_u64_via_fill(self)
}
fn fill_bytes(&mut self, dst: &mut [u8]) {
Csprng::fill_bytes(self, dst);
}
fn try_fill_bytes(&mut self, dst: &mut [u8]) -> Result<(), rand_core::Error> {
Csprng::fill_bytes(self, dst);
Ok(())
}
}
pub trait Random {
fn random<R: Csprng>(rng: R) -> Self;
}
impl<N: ArrayLength> Random for GenericArray<u8, N> {
fn random<R: Csprng>(rng: R) -> Self {
let mut v = Self::default();
rng.fill_bytes(&mut v);
v
}
}
impl<const N: usize> Random for [u8; N] {
fn random<R: Csprng>(rng: R) -> Self {
let mut v = [0u8; N];
rng.fill_bytes(&mut v);
v
}
}
macro_rules! rand_int_impl {
($($name:ty)* $(,)?) => {
$(
impl $crate::csprng::Random for $name {
fn random<R: $crate::csprng::Csprng>(rng: R) -> Self {
let mut v = [0u8; ::core::mem::size_of::<$name>()];
rng.fill_bytes(&mut v);
<$name>::from_le_bytes(v)
}
}
)*
};
}
rand_int_impl!(u8 u16 u32 u64 u128 usize);
rand_int_impl!(i8 i16 i32 i64 i128 isize);
#[cfg(feature = "trng")]
pub(crate) mod trng {
use core::{
iter::{IntoIterator, Iterator},
mem::MaybeUninit,
ptr,
sync::atomic::{self, Ordering},
};
use cfg_if::cfg_if;
use rand_chacha::ChaCha8Rng;
use rand_core::{RngCore, SeedableRng};
use crate::{csprng::Csprng, kdf::Kdf, zeroize::ZeroizeOnDrop};
cfg_if! {
if #[cfg(feature = "bearssl")] {
use crate::bearssl::HkdfSha512;
} else {
use crate::rust::HkdfSha512;
}
}
#[derive(Clone)]
pub struct ThreadRng(inner::ThreadRng);
pub fn thread_rng() -> ThreadRng {
ThreadRng(inner::thread_rng())
}
impl Csprng for ThreadRng {
fn fill_bytes(&self, dst: &mut [u8]) {
self.0.fill_bytes_and_reseed(dst);
}
}
#[cfg(feature = "std")]
mod inner {
use std::{cell::UnsafeCell, rc::Rc};
use super::{ChaCha8Csprng, HkdfSha512, OsTrng};
thread_local! {
static THREAD_RNG: Rc<UnsafeCell<ChaCha8Csprng>> =
Rc::new(UnsafeCell::new(ChaCha8Csprng::from_trng::<_, HkdfSha512>(OsTrng)));
}
pub(super) fn thread_rng() -> ThreadRng {
let rng = THREAD_RNG.with(|t| t.clone());
ThreadRng { rng }
}
#[derive(Clone)]
pub(super) struct ThreadRng {
rng: Rc<UnsafeCell<ChaCha8Csprng>>,
}
impl ThreadRng {
#[inline(always)]
pub(super) fn fill_bytes_and_reseed(&self, dst: &mut [u8]) {
let rng = unsafe { &mut *self.rng.get() };
rng.fill_bytes_and_reseed(dst);
}
}
}
#[cfg(not(feature = "std"))]
mod inner {
use spin::mutex::SpinMutex;
use super::{ChaCha8Csprng, HkdfSha512, OsTrng};
fn with_rng<F: FnOnce(&mut ChaCha8Csprng)>(f: F) {
static THREAD_RNG: SpinMutex<Option<ChaCha8Csprng>> = SpinMutex::new(None);
let mut rng = THREAD_RNG.lock();
let rng = rng.get_or_insert_with(|| ChaCha8Csprng::from_trng::<_, HkdfSha512>(OsTrng));
f(rng);
}
pub(super) fn thread_rng() -> ThreadRng {
ThreadRng
}
#[derive(Clone)]
pub(super) struct ThreadRng;
impl ThreadRng {
#[inline(always)]
pub(super) fn fill_bytes_and_reseed(&self, dst: &mut [u8]) {
with_rng(|rng| rng.fill_bytes_and_reseed(dst))
}
}
}
struct OsTrng;
impl Iterator for OsTrng {
type Item = u32;
fn next(&mut self) -> Option<u32> {
extern "C" {
fn OS_hardware_rand() -> u32;
}
let x = unsafe { OS_hardware_rand() };
Some(x)
}
}
#[derive(Clone)]
struct ChaCha8Csprng {
rng: ChaCha8Rng,
}
impl ChaCha8Csprng {
#[inline(always)]
fn from_seed(seed: [u8; 32]) -> Self {
let rng = ChaCha8Rng::from_seed(seed);
Self { rng }
}
fn from_trng<I, K>(trng: I) -> Self
where
I: IntoIterator<Item = u32>,
K: Kdf,
{
let seed = random_seed::<_, K>(trng);
Self::from_seed(seed)
}
#[inline(always)]
fn fill_bytes_and_reseed(&mut self, dst: &mut [u8]) {
self.rng.fill_bytes(dst);
self.reseed();
}
#[inline(always)]
fn reseed(&mut self) {
let mut seed = [0; 32];
self.rng.fill_bytes(&mut seed);
self.rng = ChaCha8Rng::from_seed(seed);
}
}
impl ZeroizeOnDrop for ChaCha8Csprng {}
impl Drop for ChaCha8Csprng {
fn drop(&mut self) {
let size = size_of_val(&self.rng);
let ptr = ptr::addr_of_mut!(self.rng).cast::<MaybeUninit<u8>>();
for i in 0..size {
let ptr = unsafe { ptr.add(i) };
unsafe {
ptr.write_volatile(MaybeUninit::zeroed());
}
}
atomic::compiler_fence(Ordering::SeqCst);
}
}
fn random_seed<I, K>(trng: I) -> [u8; 32]
where
I: IntoIterator<Item = u32>,
K: Kdf,
{
let mut trng = trng.into_iter();
let mut seed = [0u8; 10 * 32];
for chunk in seed.chunks_exact_mut(4) {
let x = trng.next().expect("TRNG should not fail");
chunk.copy_from_slice(&x.to_le_bytes());
}
let mut key = [0u8; 32];
K::extract_and_expand(&mut key, &seed, &[], b"seed for chacha8 csprng")
.expect("invalid KDF");
key
}
#[cfg(test)]
mod tests {
use rand_core::{OsRng, RngCore};
use super::{random_seed, thread_rng, ChaCha8Csprng, ThreadRng};
use crate::{csprng::Csprng, kdf::Kdf};
#[no_mangle]
extern "C" fn OS_hardware_rand() -> u32 {
OsRng.next_u32()
}
#[test]
#[cfg(feature = "bearssl")]
fn test_random_seed_bearssl() {
test_random_seed::<crate::bearssl::HkdfSha512>();
}
#[test]
fn test_random_seed_rust() {
use crate::{hkdf::hkdf_impl, rust::Sha512};
hkdf_impl!(HkdfSha512, "HKDF-SHA512", Sha512);
test_random_seed::<HkdfSha512>();
}
fn test_random_seed<K: Kdf>() {
const TRNG: [u32; 80] = [
1028003493, 2792012860, 1099769980, 2128370902, 756815533, 2414602873, 311122750,
307405647, 2104290982, 530412394, 404676639, 182813750, 3425358440, 1260096186,
2462344801, 399173164, 2135830142, 2699860934, 887799328, 1433459368, 289238002,
1683313852, 3676428769, 1357185267, 3661058213, 1833683120, 3822579273, 2285597052,
958698916, 2519770651, 1572529299, 2790931779, 420475008, 963064624, 1824154675,
118275351, 4287391074, 1832189034, 50997640, 130225725, 1173499583, 610709929,
2965402324, 1231825150, 2405225696, 3322754931, 3455205006, 3243476928, 234695516,
93699511, 3838575301, 4027966375, 2597847841, 3510230663, 519341910, 571863882,
3553626094, 3335867058, 1729293762, 1283510227, 1952190125, 1170477288, 2418110188,
540190490, 4215328104, 1922401658, 3651883646, 2015091372, 1155297874, 1031749841,
3836924763, 3524495878, 3395345112, 111962728, 2269910968, 1987501596, 841111076,
328762168, 1383411217, 3898745338,
];
let got = random_seed::<_, K>(TRNG);
const WANT: [u8; 32] = [
0xe, 0x6b, 0xc5, 0x9d, 0x68, 0x3e, 0x41, 0x16, 0x6b, 0x31, 0x76, 0x82, 0xe, 0xcb,
0x7c, 0x30, 0x15, 0x6b, 0x72, 0x12, 0xda, 0x7d, 0x23, 0x94, 0x81, 0x5f, 0xe2, 0xc3,
0xc3, 0x1f, 0x77, 0x2f,
];
assert_eq!(got, WANT);
}
#[test]
fn test_chacha8csprng_reseed() {
const SEED: [u8; 32] = *b"ABCDEFGHIJKLMNOPQRSTUVWXYZ123456";
let mut rng = ChaCha8Csprng::from_seed(SEED);
let old = rng.rng.get_seed();
rng.reseed();
let new = rng.rng.get_seed();
assert_ne!(old, new);
}
#[test]
fn test_thread_rng() {
fn get_bytes(rng: &ThreadRng) -> [u8; 32] {
let mut b = [0; 32];
rng.fill_bytes(&mut b);
b
}
let rng = thread_rng();
assert_ne!(get_bytes(&rng), get_bytes(&rng));
assert_ne!(get_bytes(&thread_rng()), get_bytes(&thread_rng()));
assert_ne!(get_bytes(&thread_rng()), [0; 32]);
let rng = thread_rng();
let a = rng.clone();
let b = thread_rng();
assert_ne!(get_bytes(&a), get_bytes(&b));
}
}
}