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        f.debug_struct("Seed").finish_non_exhaustive()
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        {
129            let seed = Seed::from(1);
130            let mut rng: Rng = seed.rng();
131
132            let mut data = [0u8; 2];
133
134            rng.fill_bytes(&mut data);
135
136            assert_eq!([62u8, 186u8], data);
137        }
138
139        {
140            let seed = Seed::from(1);
141            let mut rng: Rng = seed.rng();
142
143            let mut data = [0u8; 2];
144
145            rng.try_fill_bytes(&mut data).unwrap();
146
147            assert_eq!([62u8, 186u8], data);
148        }
149
150        {
151            let seed = Seed::from(1);
152            let mut rng: Rng = seed.rng();
153
154            let datum: u32 = rng.next_u32();
155
156            assert_eq!(649050686, datum);
157        }
158
159        {
160            let seed = Seed::from(1);
161            let mut rng: Rng = seed.rng();
162
163            let datum: u64 = rng.next_u64();
164
165            assert_eq!(15639741899973048894, datum);
166        }
167    }
168
169    #[test]
170    fn it_obfuscates_sensitive_data_when_debugged() {
171        let seed = Seed::default();
172
173        let dbg = format!("{:?}", seed);
174
175        assert_eq!("Seed { .. }", dbg);
176
177        let dbg = format!("{:?}", seed.rng());
178
179        assert_eq!("Rng { .. }", dbg);
180    }
181}