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, 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// Hex encoding utilities (simple implementation to avoid external dependency)
281mod hex {
282    pub fn encode(bytes: Vec<u8>) -> String {
283        bytes.iter().map(|b| format!("{:02x}", b)).collect()
284    }
285}
286
287#[cfg(test)]
288mod tests {
289    use super::*;
290
291    #[test]
292    fn test_secure_random_creation() {
293        let mut secure_rng = SecureRandom::new();
294        let value = secure_rng.random_f64();
295        assert!((0.0..1.0).contains(&value));
296    }
297
298    #[test]
299    fn test_secure_random_bytes() {
300        let mut secure_rng = SecureRandom::new();
301        let bytes = secure_rng.random_bytes(32);
302        assert_eq!(bytes.len(), 32);
303    }
304
305    #[test]
306    fn test_secure_key_generation() {
307        let mut secure_rng = SecureRandom::new();
308        let key = secure_rng.generate_key(16);
309        assert_eq!(key.len(), 16);
310    }
311
312    #[test]
313    fn test_secure_alphanumeric() {
314        let mut secure_rng = SecureRandom::new();
315        let text = secure_rng.random_alphanumeric(20);
316        assert_eq!(text.len(), 20);
317        assert!(text.chars().all(|c| c.is_alphanumeric()));
318    }
319
320    #[test]
321    fn test_secure_hex() {
322        let mut secure_rng = SecureRandom::new();
323        let hex = secure_rng.random_hex(16);
324        assert_eq!(hex.len(), 32); // 16 bytes = 32 hex characters
325        assert!(hex.chars().all(|c| c.is_ascii_hexdigit()));
326    }
327
328    #[test]
329    fn test_secure_uuid() {
330        let mut secure_rng = SecureRandom::new();
331        let uuid = secure_rng.random_uuid();
332
333        println!("Generated UUID: '{}' (length: {})", uuid, uuid.len());
334
335        // Basic UUID format check
336        assert_eq!(
337            uuid.len(),
338            36,
339            "UUID should be 36 characters, got: '{}'",
340            uuid
341        );
342        assert_eq!(uuid.chars().nth(8).expect("Operation failed"), '-');
343        assert_eq!(uuid.chars().nth(13).expect("Operation failed"), '-');
344        assert_eq!(uuid.chars().nth(18).expect("Operation failed"), '-');
345        assert_eq!(uuid.chars().nth(23).expect("Operation failed"), '-');
346    }
347
348    #[test]
349    fn test_secure_rng_pool() {
350        let pool = SecureRngPool::new();
351        let mut rng = pool.get_secure_rng();
352        let value = rng.random_f64();
353        assert!((0.0..1.0).contains(&value));
354    }
355
356    #[test]
357    fn test_secure_utils() {
358        let salt = utils::generate_salt(16);
359        assert_eq!(salt.len(), 16);
360
361        let session_id = utils::generate_session_id();
362        assert_eq!(session_id.len(), 32);
363
364        let api_key = utils::generate_api_key(24);
365        assert_eq!(api_key.len(), 48); // 24 bytes = 48 hex characters
366
367        let password = utils::generate_password(12, false);
368        assert_eq!(password.len(), 12);
369
370        let password_with_symbols = utils::generate_password(12, true);
371        assert_eq!(password_with_symbols.len(), 12);
372
373        let iv = utils::generate_iv(16);
374        assert_eq!(iv.len(), 16);
375    }
376
377    #[test]
378    fn test_secure_random_range() {
379        let mut secure_rng = SecureRandom::new();
380
381        for _ in 0..100 {
382            let value = secure_rng.random_range(10, 20);
383            assert!((10..20).contains(&value));
384        }
385    }
386
387    #[test]
388    fn test_secure_random_bool() {
389        let mut secure_rng = SecureRandom::new();
390
391        // Test basic boolean generation
392        let _bool_val = secure_rng.random_bool();
393
394        // Test boolean with probability
395        let always_true = secure_rng.random_bool_with_probability(1.0);
396        assert!(always_true);
397
398        let always_false = secure_rng.random_bool_with_probability(0.0);
399        assert!(!always_false);
400    }
401}