Skip to main content

asjeeves_encryption/
seed.rs

1//! Cryptographic Randomization
2
3use std::fmt;
4use std::sync::Arc;
5use std::sync::atomic::{AtomicU64, Ordering};
6
7use rand_chacha::ChaCha20Rng;
8use rand_core::{CryptoRng, RngCore, SeedableRng};
9
10static STREAM: AtomicU64 = AtomicU64::new(0);
11
12/// Holds a 256-bit seed used for randomization.
13/// The default seed uses `rand::FromEntropy::from_entropy` to generate a random value, this
14/// uses the underlying OS getrandom() syscall (on linux /dev/random)
15/// which is both convient and safe.
16/// ## Example
17///     use asjeeves_encryption::prelude::*;
18///     // Generate a seed from a u64 (useful for testing)
19///     let seed = Seed::from(1);
20///     // Generate a random seed (recomeneded for production).
21///     let seed = Seed::default();
22///     // Get an rng
23///     let mut rng : Rng = seed.rng();
24///     let kek = KeyEncryptionKey::generate(&mut rng);
25#[derive(Clone)]
26pub struct Seed(Arc<[u8; 32]>);
27
28/// ChaCha20 randomizer.
29pub struct Rng(ChaCha20Rng);
30
31impl AsRef<ChaCha20Rng> for Rng {
32    fn as_ref(&self) -> &ChaCha20Rng {
33        &self.0
34    }
35}
36
37impl AsMut<ChaCha20Rng> for Rng {
38    fn as_mut(&mut self) -> &mut ChaCha20Rng {
39        &mut self.0
40    }
41}
42
43impl CryptoRng for Rng {}
44
45impl fmt::Debug for Rng {
46    // We don't actually want debug info for Rng as its cryptographically sensitive.
47    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
48        f.debug_struct("Rng").finish_non_exhaustive()
49    }
50}
51
52impl RngCore for Rng {
53    fn next_u32(&mut self) -> u32 {
54        self.0.next_u32()
55    }
56
57    fn next_u64(&mut self) -> u64 {
58        self.0.next_u64()
59    }
60
61    fn fill_bytes(&mut self, dest: &mut [u8]) {
62        self.0.fill_bytes(dest);
63    }
64
65    fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core::Error> {
66        self.0.try_fill_bytes(dest)
67    }
68}
69
70impl Seed {
71    /// Returns a randomizer [Rng]. This uses the ChaCha20 Algorithm.
72    /// See: [ChaCha Wikipedia Page](https://en.wikipedia.org/wiki/Salsa20#ChaCha_variant)
73    pub fn rng(&self) -> Rng {
74        let local_stream = STREAM.fetch_add(1, Ordering::Relaxed);
75
76        let mut rng = ChaCha20Rng::from_seed(*self.0);
77
78        rng.set_stream(local_stream);
79
80        Rng(rng)
81    }
82
83    fn reset_stream() {
84        STREAM.store(0, Ordering::SeqCst)
85    }
86}
87
88impl fmt::Debug for Seed {
89    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
90        write!(f, "Seed")
91    }
92}
93
94impl Default for Seed {
95    fn default() -> Self {
96        let mut rng = ChaCha20Rng::from_entropy();
97        let mut seed = [0u8; 32];
98
99        rng.fill_bytes(&mut seed);
100
101        let seed = Arc::new(seed);
102
103        Self(seed)
104    }
105}
106
107impl From<u64> for Seed {
108    fn from(value: u64) -> Self {
109        let mut rng = ChaCha20Rng::seed_from_u64(value);
110        let mut seed = [0u8; 32];
111
112        rng.fill_bytes(&mut seed);
113
114        let seed = Arc::new(seed);
115
116        Seed::reset_stream();
117
118        Self(seed)
119    }
120}
121
122#[cfg(test)]
123mod test {
124    use super::*;
125
126    #[test]
127    fn it_generates_a_random_number() {
128        let seed = Seed::from(1);
129        let mut rng: Rng = seed.rng();
130
131        let mut data = [0u8; 2];
132
133        rng.fill_bytes(&mut data);
134
135        assert_eq!([62u8, 186u8], data);
136    }
137}