ya_rand/
secure.rs

1use crate::rng::{SecureYARandGenerator, YARandGenerator};
2use chacha20::{
3    rand_core::{RngCore, SeedableRng},
4    ChaCha8Rng,
5};
6
7/// A cryptographically secure random number generator.
8///
9/// The current implementation is ChaCha with 8 rounds,
10/// as supplied by the [`chacha20`] crate.
11#[derive(Debug)]
12pub struct SecureRng {
13    internal: ChaCha8Rng,
14}
15
16impl SecureYARandGenerator for SecureRng {
17    #[inline(never)]
18    fn fill_bytes(&mut self, dst: &mut [u8]) {
19        self.internal.fill_bytes(dst);
20    }
21}
22
23impl YARandGenerator for SecureRng {
24    fn try_new() -> Result<Self, getrandom::Error> {
25        const SEED_LEN: usize = 32;
26        const STREAM_LEN: usize = 12;
27        // Using a combined array so we only need a single syscall.
28        let mut data = [0; SEED_LEN + STREAM_LEN];
29        getrandom::fill(&mut data)?;
30        // Both of these unwraps get optimized out.
31        let seed: [u8; SEED_LEN] = data[..SEED_LEN].try_into().unwrap();
32        let stream: [u8; STREAM_LEN] = data[SEED_LEN..].try_into().unwrap();
33        let mut internal = ChaCha8Rng::from_seed(seed);
34        // Randomizing the stream number further decreases the
35        // already-low odds of two instances colliding.
36        internal.set_stream(stream);
37        Ok(Self { internal })
38    }
39
40    #[cfg_attr(feature = "inline", inline)]
41    fn u64(&mut self) -> u64 {
42        self.internal.next_u64()
43    }
44}