ya_rand/
secure.rs

1use crate::rng::*;
2use chachacha::*;
3use core::mem::MaybeUninit;
4
5/// A cryptographically secure random number generator.
6///
7/// The current implementation is Chacha with 8 rounds, using the original variation
8/// (64-bit counter). This allows for 1 ZiB (2<sup>70</sup> bytes)  of output
9/// before cycling. That's over 147 quintillion calls to [`SecureRng::u64`].
10pub struct SecureRng {
11    index: usize,
12    buf: [u64; BUF_LEN_U64],
13    internal: ChaCha8Djb,
14}
15
16impl SecureYARandGenerator for SecureRng {
17    #[inline]
18    fn fill_bytes(&mut self, dst: &mut [u8]) {
19        // The `chachacha` crate provides a thoroughly tested fill implementation.
20        self.internal.fill(dst);
21    }
22}
23
24impl YARandGenerator for SecureRng {
25    #[inline]
26    fn try_new() -> Result<Self, getrandom::Error> {
27        // We randomize **all** bits of the matrix, even the counter.
28        // If used as a cipher this approach is completely braindead,
29        // but since this is exclusively for use as a CRNG it's fine.
30        #[allow(invalid_value)]
31        let mut state = unsafe { MaybeUninit::<[u8; SEED_LEN_U8]>::uninit().assume_init() };
32        getrandom::fill(&mut state)?;
33        let mut internal = ChaCha8Djb::new(state);
34        let buf = internal.get_block_u64();
35        let index = 0;
36        Ok(SecureRng {
37            index,
38            buf,
39            internal,
40        })
41    }
42
43    #[cfg_attr(feature = "inline", inline)]
44    fn u64(&mut self) -> u64 {
45        if self.index >= self.buf.len() {
46            self.internal.fill_block_u64(&mut self.buf);
47            self.index = 0;
48        }
49        let result = self.buf[self.index];
50        self.index += 1;
51        result
52    }
53}