ya_rand/
secure.rs

1use crate::rng::*;
2use chachacha::{BUF_LEN_U64, ChaCha8Djb, SEED_LEN_U8};
3use core::fmt::{self, Debug, Formatter};
4use core::mem::MaybeUninit;
5use getrandom::fill;
6
7/// A cryptographically secure random number generator.
8///
9/// The current implementation uses ChaCha with 8 rounds and a 64-bit counter.
10/// This allows for 1 ZiB (2<sup>70</sup> bytes) of output before cycling.
11/// That's over 147 **quintillion** calls to [`SecureRng::u64`].
12pub struct SecureRng {
13    buf: [u64; BUF_LEN_U64],
14    index: usize,
15    internal: ChaCha8Djb,
16}
17
18impl Debug for SecureRng {
19    #[inline]
20    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
21        f.write_str("all `SecureRng` fields are private")
22    }
23}
24
25impl SecureYARandGenerator for SecureRng {
26    #[inline]
27    fn fill_bytes(&mut self, dst: &mut [u8]) {
28        // The `chachacha` crate provides a thoroughly tested and
29        // extremely fast fill implementation.
30        self.internal.fill(dst);
31    }
32}
33
34impl YARandGenerator for SecureRng {
35    #[inline]
36    fn try_new() -> Result<Self, getrandom::Error> {
37        // We randomize **all** bits of the matrix, even the counter.
38        // If used as a cipher this approach is completely braindead,
39        // but since this is exclusively for use as a CRNG it's fine.
40        #[allow(invalid_value)]
41        let mut state = unsafe { MaybeUninit::<[u8; SEED_LEN_U8]>::uninit().assume_init() };
42        fill(&mut state)?;
43        let mut internal = ChaCha8Djb::from(state);
44        let buf = internal.get_block_u64();
45        let index = 0;
46        Ok(Self {
47            buf,
48            index,
49            internal,
50        })
51    }
52
53    #[cfg_attr(feature = "inline", inline)]
54    fn u64(&mut self) -> u64 {
55        // TODO: This is the cold path, occuring only once every
56        // 32 calls to `Self::u64`. If there is ever a cold/unlikely
57        // intrinsic moved to stable then test the performance impact
58        // of applying it to this branch.
59        if self.index >= self.buf.len() {
60            self.index = 0;
61            self.internal.fill_block_u64(&mut self.buf);
62        }
63        // Bounds check is elided thanks to above code.
64        let result = self.buf[self.index];
65        self.index += 1;
66        result
67    }
68}