ya_rand/
secure.rs

1use crate::rng::{SecureYARandGenerator, YARandGenerator};
2use chacha20::{
3    rand_core::{RngCore, SeedableRng},
4    ChaCha8Rng,
5};
6use core::{mem::size_of, ptr::write_volatile, sync::atomic};
7
8/// A cryptographically secure random number generator.
9///
10/// The current implementation is ChaCha with 8 rounds,
11/// as supplied by the [`chacha20`] crate.
12#[derive(Debug)]
13pub struct SecureRng {
14    internal: ChaCha8Rng,
15}
16
17// This is a manual implementation of ZeroizeOnDrop from the zeroize trait.
18//
19// https://github.com/RustCrypto/utils/blob/zeroize-v1.8.1/zeroize/src/lib.rs#L773
20// https://github.com/RustCrypto/utils/blob/zeroize-v1.8.1/zeroize/src/lib.rs#L754
21impl Drop for SecureRng {
22    fn drop(&mut self) {
23        let self_ptr = (self as *mut Self).cast::<u8>();
24        for i in 0..size_of::<SecureRng>() {
25            // SAFETY: Trust me bro (see the first link).
26            unsafe {
27                write_volatile(self_ptr.add(i), 0);
28            }
29        }
30        atomic::compiler_fence(atomic::Ordering::SeqCst);
31    }
32}
33
34impl SecureYARandGenerator for SecureRng {
35    #[inline(never)]
36    fn fill_bytes(&mut self, dest: &mut [u8]) {
37        self.internal.fill_bytes(dest);
38    }
39}
40
41impl YARandGenerator for SecureRng {
42    fn try_new() -> Result<Self, getrandom::Error> {
43        const SEED_LEN: usize = 32;
44        const STREAM_LEN: usize = 12;
45        // Using a combined array so we only need a single syscall.
46        let mut data = [0; SEED_LEN + STREAM_LEN];
47        getrandom::fill(&mut data)?;
48        // Both of these unwraps are removed during compilation.
49        let seed: [u8; SEED_LEN] = data[..SEED_LEN].try_into().unwrap();
50        let stream: [u8; STREAM_LEN] = data[SEED_LEN..].try_into().unwrap();
51        let mut internal = ChaCha8Rng::from_seed(seed);
52        internal.set_stream(stream);
53        Ok(Self { internal })
54    }
55
56    #[cfg_attr(feature = "inline", inline)]
57    fn u64(&mut self) -> u64 {
58        self.internal.next_u64()
59    }
60}