Skip to main content

scirs2_core/random/
secure.rs

1//! Cryptographically secure random number generation
2//!
3//! This module provides cryptographically secure random number generation
4//! suitable for security-sensitive applications including key generation,
5//! nonce creation, and other cryptographic operations.
6
7use crate::random::core::Random;
8use rand::rngs::StdRng;
9use rand::{Rng, RngExt, SeedableRng};
10use rand_distr::{Distribution, Uniform};
11use std::collections::hash_map::DefaultHasher;
12use std::hash::{Hash, Hasher};
13use std::process;
14use std::thread;
15use std::time::{SystemTime, UNIX_EPOCH};
16
17/// Cryptographically secure random number generator
18///
19/// This generator uses multiple entropy sources and a cryptographically secure
20/// PRNG to provide high-quality random numbers suitable for security applications.
21pub struct SecureRandom {
22    rng: Random<StdRng>,
23}
24
25impl Default for SecureRandom {
26    fn default() -> Self {
27        Self::new()
28    }
29}
30
31impl SecureRandom {
32    /// Create a new cryptographically secure RNG
33    ///
34    /// Uses multiple entropy sources including:
35    /// - System time (nanoseconds since UNIX epoch)
36    /// - Process ID
37    /// - Thread ID
38    /// - Hash combining all sources
39    pub fn new() -> Self {
40        // Use system entropy to generate a secure seed for StdRng
41        let time_nanos = SystemTime::now()
42            .duration_since(UNIX_EPOCH)
43            .map(|d| d.as_nanos())
44            .unwrap_or(0);
45
46        let process_id = process::id() as u128;
47        let thread_id = thread::current().id();
48
49        // Combine multiple entropy sources
50        let mut hasher = DefaultHasher::new();
51        time_nanos.hash(&mut hasher);
52        process_id.hash(&mut hasher);
53        thread_id.hash(&mut hasher);
54
55        let seed_u64 = hasher.finish();
56
57        // Create a 32-byte seed from the hash
58        let mut seed = [0u8; 32];
59        for (i, chunk) in seed.chunks_mut(8).enumerate() {
60            let offset_seed = seed_u64.wrapping_add((i as u64).wrapping_mul(0x9E3779B97F4A7C15));
61            let bytes = offset_seed.to_le_bytes();
62            chunk.copy_from_slice(&bytes[..chunk.len()]);
63        }
64
65        let std_rng = StdRng::from_seed(seed);
66        Self {
67            rng: Random { rng: std_rng },
68        }
69    }
70
71    /// Create a secure RNG from a provided seed
72    ///
73    /// # Warning
74    /// Using a predictable seed defeats the purpose of cryptographic security.
75    /// This method should only be used for testing or when the seed comes from
76    /// a high-entropy source.
77    pub fn from_seed(seed: [u8; 32]) -> Self {
78        let std_rng = StdRng::from_seed(seed);
79        Self {
80            rng: Random { rng: std_rng },
81        }
82    }
83
84    /// Generate a cryptographically secure random value
85    pub fn sample<D, T>(&mut self, distribution: D) -> T
86    where
87        D: Distribution<T>,
88    {
89        self.rng.sample(distribution)
90    }
91
92    /// Generate cryptographically secure random bytes
93    ///
94    /// This is suitable for generating keys, nonces, and other cryptographic material.
95    pub fn random_bytes(&mut self, count: usize) -> Vec<u8> {
96        (0..count)
97            .map(|_| self.sample(Uniform::new(0u8, 255u8).expect("Operation failed")))
98            .collect()
99    }
100
101    /// Generate a cryptographically secure random key
102    ///
103    /// Alias for `random_bytes` with more descriptive naming for key generation.
104    pub fn generate_key(&mut self, length: usize) -> Vec<u8> {
105        self.random_bytes(length)
106    }
107
108    /// Generate a cryptographically secure random nonce
109    ///
110    /// Nonces should be unique for each use. This generates random bytes
111    /// suitable for use as a nonce in cryptographic protocols.
112    pub fn generate_nonce(&mut self, length: usize) -> Vec<u8> {
113        self.random_bytes(length)
114    }
115
116    /// Generate a cryptographically secure random float in [0, 1)
117    pub fn random_f64(&mut self) -> f64 {
118        self.sample(Uniform::new(0.0, 1.0).expect("Operation failed"))
119    }
120
121    /// Generate a cryptographically secure random float in [0, 1) as f32
122    pub fn random_f32(&mut self) -> f32 {
123        self.sample(Uniform::new(0.0f32, 1.0f32).expect("Operation failed"))
124    }
125
126    /// Generate cryptographically secure random integers in a range
127    pub fn random_range<T>(&mut self, min: T, max: T) -> T
128    where
129        T: rand_distr::uniform::SampleUniform + PartialOrd + Copy,
130    {
131        self.sample(Uniform::new(min, max).expect("Operation failed"))
132    }
133
134    /// Generate a cryptographically secure random boolean
135    pub fn random_bool(&mut self) -> bool {
136        self.rng.rng.random_bool(0.5)
137    }
138
139    /// Generate a cryptographically secure random boolean with given probability
140    pub fn random_bool_with_probability(&mut self, p: f64) -> bool {
141        self.rng.rng.random_bool(p)
142    }
143
144    /// Generate cryptographically secure random alphanumeric string
145    ///
146    /// Useful for generating random passwords, tokens, or identifiers.
147    pub fn random_alphanumeric(&mut self, length: usize) -> String {
148        const CHARSET: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
149        (0..length)
150            .map(|_| {
151                let idx = self.random_range(0, CHARSET.len());
152                CHARSET[idx] as char
153            })
154            .collect()
155    }
156
157    /// Generate cryptographically secure random hex string
158    ///
159    /// Useful for generating random hex-encoded keys or identifiers.
160    pub fn random_hex(&mut self, byte_length: usize) -> String {
161        let bytes = self.random_bytes(byte_length);
162        hex_encode(&bytes)
163    }
164
165    /// Generate cryptographically secure UUID v4
166    ///
167    /// Generates a random UUID suitable for use as a unique identifier.
168    pub fn random_uuid(&mut self) -> String {
169        let bytes = self.random_bytes(16);
170        format!(
171            "{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-4{:01x}{:02x}-{:02x}{:02x}-{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}",
172            bytes[0], bytes[1], bytes[2], bytes[3],
173            bytes[4], bytes[5],
174            bytes[6] & 0x0f, bytes[7],
175            bytes[8] & 0x3f | 0x80, bytes[9],
176            bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15]
177        )
178    }
179
180    /// Access the underlying RNG for advanced operations
181    pub fn rng_mut(&mut self) -> &mut Random<StdRng> {
182        &mut self.rng
183    }
184}
185
186/// Thread-safe secure random number generator pool
187///
188/// Provides secure random number generation in multi-threaded environments
189/// by maintaining separate secure RNG instances per thread.
190pub struct SecureRngPool {
191    seed_base: u64,
192}
193
194impl SecureRngPool {
195    /// Create a new secure RNG pool
196    pub fn new() -> Self {
197        let time_nanos = SystemTime::now()
198            .duration_since(UNIX_EPOCH)
199            .map(|d| d.as_nanos() as u64)
200            .unwrap_or(0);
201
202        Self {
203            seed_base: time_nanos,
204        }
205    }
206
207    /// Get a thread-local secure RNG
208    pub fn get_secure_rng(&self) -> SecureRandom {
209        let thread_id = thread::current().id();
210        let mut hasher = DefaultHasher::new();
211        self.seed_base.hash(&mut hasher);
212        thread_id.hash(&mut hasher);
213
214        let thread_seed = hasher.finish();
215        let mut seed = [0u8; 32];
216
217        for (i, chunk) in seed.chunks_mut(8).enumerate() {
218            let offset_seed = thread_seed.wrapping_add((i as u64).wrapping_mul(0x9E3779B97F4A7C15));
219            let bytes = offset_seed.to_le_bytes();
220            chunk.copy_from_slice(&bytes[..chunk.len()]);
221        }
222
223        SecureRandom::from_seed(seed)
224    }
225}
226
227impl Default for SecureRngPool {
228    fn default() -> Self {
229        Self::new()
230    }
231}
232
233/// Secure random utilities
234pub mod utils {
235    use super::*;
236
237    /// Generate a cryptographically secure random salt
238    pub fn generate_salt(length: usize) -> Vec<u8> {
239        let mut rng = SecureRandom::new();
240        rng.random_bytes(length)
241    }
242
243    /// Generate a cryptographically secure random session ID
244    pub fn generate_session_id() -> String {
245        let mut rng = SecureRandom::new();
246        rng.random_alphanumeric(32)
247    }
248
249    /// Generate a cryptographically secure random API key
250    pub fn generate_api_key(length: usize) -> String {
251        let mut rng = SecureRandom::new();
252        rng.random_hex(length)
253    }
254
255    /// Generate a cryptographically secure random password
256    pub fn generate_password(length: usize, include_symbols: bool) -> String {
257        let mut rng = SecureRandom::new();
258
259        let charset: &[u8] = if include_symbols {
260            b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()_+-=[]{}|;:,.<>?"
261        } else {
262            b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
263        };
264
265        (0..length)
266            .map(|_| {
267                let idx = rng.random_range(0, charset.len());
268                charset[idx] as char
269            })
270            .collect()
271    }
272
273    /// Generate cryptographically secure random initialization vector (IV)
274    pub fn generate_iv(length: usize) -> Vec<u8> {
275        let mut rng = SecureRandom::new();
276        rng.random_bytes(length)
277    }
278}
279
280/// Encode bytes as a lowercase hexadecimal string.
281///
282/// Std-only implementation that avoids pulling in the external `hex` crate.
283fn hex_encode(bytes: &[u8]) -> String {
284    const HEX_CHARS: &[u8; 16] = b"0123456789abcdef";
285    let mut out = String::with_capacity(bytes.len() * 2);
286    for &byte in bytes {
287        out.push(HEX_CHARS[usize::from(byte >> 4)] as char);
288        out.push(HEX_CHARS[usize::from(byte & 0x0f)] as char);
289    }
290    out
291}
292
293#[cfg(test)]
294mod tests {
295    use super::*;
296
297    #[test]
298    fn test_secure_random_creation() {
299        let mut secure_rng = SecureRandom::new();
300        let value = secure_rng.random_f64();
301        assert!((0.0..1.0).contains(&value));
302    }
303
304    #[test]
305    fn test_secure_random_bytes() {
306        let mut secure_rng = SecureRandom::new();
307        let bytes = secure_rng.random_bytes(32);
308        assert_eq!(bytes.len(), 32);
309    }
310
311    #[test]
312    fn test_secure_key_generation() {
313        let mut secure_rng = SecureRandom::new();
314        let key = secure_rng.generate_key(16);
315        assert_eq!(key.len(), 16);
316    }
317
318    #[test]
319    fn test_secure_alphanumeric() {
320        let mut secure_rng = SecureRandom::new();
321        let text = secure_rng.random_alphanumeric(20);
322        assert_eq!(text.len(), 20);
323        assert!(text.chars().all(|c| c.is_alphanumeric()));
324    }
325
326    #[test]
327    fn test_hex_encode() {
328        assert_eq!(hex_encode(&[]), "");
329        assert_eq!(hex_encode(&[0x00]), "00");
330        assert_eq!(hex_encode(&[0xff]), "ff");
331        assert_eq!(hex_encode(&[0xde, 0xad, 0xbe, 0xef]), "deadbeef");
332        assert_eq!(hex_encode(&[0x01, 0x0a, 0x10, 0x7f]), "010a107f");
333    }
334
335    #[test]
336    fn test_secure_hex() {
337        let mut secure_rng = SecureRandom::new();
338        let hex = secure_rng.random_hex(16);
339        assert_eq!(hex.len(), 32); // 16 bytes = 32 hex characters
340        assert!(hex.chars().all(|c| c.is_ascii_hexdigit()));
341    }
342
343    #[test]
344    fn test_secure_uuid() {
345        let mut secure_rng = SecureRandom::new();
346        let uuid = secure_rng.random_uuid();
347
348        println!("Generated UUID: '{}' (length: {})", uuid, uuid.len());
349
350        // Basic UUID format check
351        assert_eq!(
352            uuid.len(),
353            36,
354            "UUID should be 36 characters, got: '{}'",
355            uuid
356        );
357        assert_eq!(uuid.chars().nth(8).expect("Operation failed"), '-');
358        assert_eq!(uuid.chars().nth(13).expect("Operation failed"), '-');
359        assert_eq!(uuid.chars().nth(18).expect("Operation failed"), '-');
360        assert_eq!(uuid.chars().nth(23).expect("Operation failed"), '-');
361    }
362
363    #[test]
364    fn test_secure_rng_pool() {
365        let pool = SecureRngPool::new();
366        let mut rng = pool.get_secure_rng();
367        let value = rng.random_f64();
368        assert!((0.0..1.0).contains(&value));
369    }
370
371    #[test]
372    fn test_secure_utils() {
373        let salt = utils::generate_salt(16);
374        assert_eq!(salt.len(), 16);
375
376        let session_id = utils::generate_session_id();
377        assert_eq!(session_id.len(), 32);
378
379        let api_key = utils::generate_api_key(24);
380        assert_eq!(api_key.len(), 48); // 24 bytes = 48 hex characters
381
382        let password = utils::generate_password(12, false);
383        assert_eq!(password.len(), 12);
384
385        let password_with_symbols = utils::generate_password(12, true);
386        assert_eq!(password_with_symbols.len(), 12);
387
388        let iv = utils::generate_iv(16);
389        assert_eq!(iv.len(), 16);
390    }
391
392    #[test]
393    fn test_secure_random_range() {
394        let mut secure_rng = SecureRandom::new();
395
396        for _ in 0..100 {
397            let value = secure_rng.random_range(10, 20);
398            assert!((10..20).contains(&value));
399        }
400    }
401
402    #[test]
403    fn test_secure_random_bool() {
404        let mut secure_rng = SecureRandom::new();
405
406        // Test basic boolean generation
407        let _bool_val = secure_rng.random_bool();
408
409        // Test boolean with probability
410        let always_true = secure_rng.random_bool_with_probability(1.0);
411        assert!(always_true);
412
413        let always_false = secure_rng.random_bool_with_probability(0.0);
414        assert!(!always_false);
415    }
416}