Skip to main content

lib_q_random/
specialized.rs

1//! Specialized random number generation implementations for different algorithms
2//!
3//! This module provides algorithm-specific RNG implementations that are optimized
4//! for particular use cases while maintaining the unified libQ random interface.
5
6use core::fmt;
7
8// `Rng` is only referenced on the `all(rand, std)` ThreadRng path (see `FnDsaRng`); allow it to be
9// unused whenever that path is off (no `rand`, or `rand` without `std`, e.g. bare-metal builds).
10#[cfg_attr(not(all(feature = "rand", feature = "std")), allow(unused_imports))]
11use rand_core::{
12    Rng,
13    TryCryptoRng,
14    TryRng,
15};
16
17#[cfg(feature = "hash")]
18use crate::Error;
19
20/// Classical `McEliece` compatible RNG
21///
22/// This RNG provides the same interface as the original `AesState` but uses
23/// libQ's standard RNG infrastructure instead of AES-based generation.
24#[derive(Clone, Debug, PartialEq)]
25pub struct ClassicalMcElieceRng {
26    /// Internal state for deterministic mode
27    state: u64,
28    /// Counter for deterministic mode
29    counter: u64,
30    /// Whether this RNG is in deterministic mode
31    deterministic: bool,
32    /// Reseed counter for security
33    reseed_counter: u32,
34}
35
36impl ClassicalMcElieceRng {
37    /// Create a new secure RNG using system entropy
38    ///
39    /// Random output is drawn with `getrandom::fill`. The `classical-mceliece` crate feature
40    /// enables the `getrandom` dependency so non-test builds always have OS entropy available
41    /// (including `wasm_js` on `wasm32-unknown-unknown` when configured in the dependency graph).
42    #[must_use]
43    pub fn new() -> Self {
44        Self {
45            state: 0,
46            counter: 0,
47            deterministic: false,
48            reseed_counter: 0,
49        }
50    }
51
52    /// Create a new deterministic RNG for testing
53    ///
54    /// This creates an RNG that produces deterministic output based on the seed.
55    /// This is useful for testing and reproducible key generation.
56    #[must_use]
57    pub fn new_deterministic(seed: u64) -> Self {
58        Self {
59            state: seed
60                .wrapping_mul(6_364_136_223_846_793_005_u64)
61                .wrapping_add(1_442_695_040_888_963_407_u64),
62            counter: 0,
63            deterministic: true,
64            reseed_counter: 0,
65        }
66    }
67
68    /// Create a new deterministic RNG from byte array
69    ///
70    /// This creates an RNG that produces deterministic output based on the byte array.
71    /// The bytes are hashed to create a 64-bit seed.
72    #[must_use]
73    pub fn new_deterministic_from_bytes(seed_bytes: &[u8]) -> Self {
74        let mut hash = 0u64;
75        for (i, &byte) in seed_bytes.iter().enumerate() {
76            hash = hash.wrapping_add(u64::from(byte) << (i % 8));
77        }
78        Self::new_deterministic(hash)
79    }
80
81    /// Initialize the RNG with entropy (for deterministic mode)
82    ///
83    /// This method is provided for compatibility with the original `AesState` interface.
84    /// In deterministic mode, it updates the internal state.
85    /// In secure mode, it's a no-op as the RNG uses system entropy.
86    pub fn randombytes_init(&mut self, entropy_input: [u8; 48]) {
87        if self.deterministic {
88            // Mix the entropy into the state using a simple hash-like function
89            let mut hash = 0u64;
90            for (i, &byte) in entropy_input.iter().enumerate() {
91                hash = hash.wrapping_add(u64::from(byte) << (i % 8));
92            }
93            self.state = self.state.wrapping_add(hash);
94            self.counter = 0;
95            self.reseed_counter = 1;
96        }
97        // In secure mode, we don't need to initialize with entropy
98        // as the RNG uses system entropy sources
99    }
100
101    /// Generate random bytes using the appropriate method
102    fn generate_bytes(&mut self, dest: &mut [u8]) {
103        if self.deterministic {
104            self.generate_deterministic_bytes(dest);
105        } else {
106            self.generate_secure_bytes(dest);
107        }
108    }
109
110    /// Generate deterministic random bytes for testing
111    ///
112    /// This implementation provides better statistical properties for CB-KEM
113    /// by using a more sophisticated deterministic generation approach.
114    fn generate_deterministic_bytes(&mut self, dest: &mut [u8]) {
115        for chunk in dest.chunks_mut(8) {
116            // Use a more sophisticated deterministic generation
117            // that provides better statistical properties for CB-KEM
118
119            // Multiple LCG iterations for better distribution
120            for _ in 0..3 {
121                self.state = self
122                    .state
123                    .wrapping_mul(6_364_136_223_846_793_005_u64)
124                    .wrapping_add(1_442_695_040_888_963_407_u64);
125            }
126
127            self.counter = self.counter.wrapping_add(1);
128
129            // Enhanced entropy mixing for better statistical properties
130            // This addresses the specific needs of CB-KEM
131            let mut value = self.state ^
132                self.counter ^
133                (u64::from(self.reseed_counter) << 32) ^
134                (u64::from(self.reseed_counter) << 16);
135
136            // Additional mixing for better statistical properties
137            // This helps ensure the RNG output has properties suitable for CB-KEM
138            value = value.wrapping_mul(0x9E37_79B9_7F4A_7C15_u64); // Golden ratio constant
139            value ^= value >> 33;
140            value = value.wrapping_mul(0x9E37_79B9_7F4A_7C15_u64);
141            value ^= value >> 29;
142            value = value.wrapping_mul(0xC4CE_B9FE_1A85_EC53_u64);
143            value ^= value >> 32;
144
145            let bytes = value.to_le_bytes();
146
147            let len = chunk.len().min(8);
148            chunk[..len].copy_from_slice(&bytes[..len]);
149        }
150
151        self.reseed_counter = self.reseed_counter.wrapping_add(1);
152    }
153
154    /// Generate secure random bytes using system entropy
155    ///
156    /// If this crate was built without the `getrandom` feature, this function panics instead of
157    /// emitting predictable bytes. If `getrandom::fill` fails at runtime, this function also
158    /// panics (`TryRng` uses [`core::convert::Infallible`], so errors cannot be propagated).
159    fn generate_secure_bytes(&mut self, dest: &mut [u8]) {
160        #[cfg(feature = "getrandom")]
161        {
162            // Never recurse: `TryRng` uses `Infallible`, so refuse OS RNG failure loudly.
163            assert!(
164                getrandom::fill(dest).is_ok(),
165                "lib_q_random::ClassicalMcElieceRng: getrandom::fill failed; \
166                 refusing non-OS RNG output (enable `custom-entropy` or fix the environment)"
167            );
168        }
169
170        #[cfg(not(feature = "getrandom"))]
171        {
172            let _ = dest;
173            // Supported configurations that expose secure `ClassicalMcElieceRng` enable `getrandom`
174            // (see the `classical-mceliece` feature). If this path is hit, the crate was built
175            // without OS entropy support; do not substitute deterministic or PRNG output.
176            panic!(
177                "lib_q_random: ClassicalMcElieceRng requires the `getrandom` crate feature for secure output; \
178                 enable `getrandom`/`secure`/`classical-mceliece`, or use `new_deterministic` for tests"
179            );
180        }
181
182        self.reseed_counter = self.reseed_counter.wrapping_add(1);
183    }
184}
185
186impl Default for ClassicalMcElieceRng {
187    fn default() -> Self {
188        Self::new()
189    }
190}
191
192impl Eq for ClassicalMcElieceRng {}
193
194impl fmt::Display for ClassicalMcElieceRng {
195    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
196        writeln!(f, "ClassicalMcElieceRng {{")?;
197        writeln!(f, "  state = {}", self.state)?;
198        writeln!(f, "  counter = {}", self.counter)?;
199        writeln!(f, "  deterministic = {}", self.deterministic)?;
200        writeln!(f, "  reseed_counter = {}", self.reseed_counter)?;
201        writeln!(f, "}}")
202    }
203}
204
205impl TryRng for ClassicalMcElieceRng {
206    type Error = core::convert::Infallible;
207
208    fn try_next_u32(&mut self) -> Result<u32, Self::Error> {
209        let mut bytes = [0u8; 4];
210        self.try_fill_bytes(&mut bytes)?;
211        Ok(u32::from_le_bytes(bytes))
212    }
213
214    fn try_next_u64(&mut self) -> Result<u64, Self::Error> {
215        let mut bytes = [0u8; 8];
216        self.try_fill_bytes(&mut bytes)?;
217        Ok(u64::from_le_bytes(bytes))
218    }
219
220    fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Self::Error> {
221        self.generate_bytes(dest);
222        Ok(())
223    }
224}
225
226impl TryCryptoRng for ClassicalMcElieceRng {}
227
228/// HPKE-compatible RNG using RFC 9861 **KT128** (`KangarooTwelve` / `TurboSHAKE128`)
229///
230/// This implementation provides cryptographically secure random number generation
231/// using libQ's **KT128** (`KangarooTwelve`) primitive. K12 is significantly
232/// faster than SHAKE256 while maintaining the same security properties.
233#[cfg(feature = "hash")]
234#[derive(Clone, Debug)]
235pub struct Kt128Rng {
236    expander: crate::kt128_expander::Kt128Expander,
237}
238
239#[cfg(feature = "hash")]
240impl Kt128Rng {
241    /// Create a new secure RNG with system entropy
242    ///
243    /// # Errors
244    ///
245    /// Returns an error if entropy source is unavailable or fails to initialize.
246    pub fn new() -> crate::Result<Self> {
247        #[cfg(feature = "getrandom")]
248        {
249            // Use system entropy to seed the RNG
250            let mut seed = [0u8; 32];
251            getrandom::fill(&mut seed).map_err(|_| Error::EntropySourceUnavailable {
252                source: "system",
253                context: Some("getrandom failed"),
254            })?;
255            Ok(Self::from_seed(&seed))
256        }
257        #[cfg(not(feature = "getrandom"))]
258        {
259            // No insecure fallback - fail fast if getrandom is not available
260            Err(Error::FeatureNotAvailable {
261                feature: "secure entropy",
262                required_features: &["getrandom"],
263            })
264        }
265    }
266
267    /// Create a new secure RNG with explicit seed
268    #[must_use]
269    pub fn from_seed(seed: &[u8]) -> Self {
270        Self {
271            expander: crate::kt128_expander::Kt128Expander::from_seed(
272                crate::kt128_expander::DOMAIN_HPKE_RNG,
273                seed,
274            ),
275        }
276    }
277
278    /// Refill the internal buffer with new random data (advances the KT128 chain).
279    pub fn refill(&mut self) {
280        self.expander.refill();
281    }
282}
283
284#[cfg(feature = "hash")]
285impl TryRng for Kt128Rng {
286    type Error = core::convert::Infallible;
287
288    fn try_next_u32(&mut self) -> Result<u32, Self::Error> {
289        let mut bytes = [0u8; 4];
290        self.try_fill_bytes(&mut bytes)?;
291        Ok(u32::from_le_bytes(bytes))
292    }
293
294    fn try_next_u64(&mut self) -> Result<u64, Self::Error> {
295        let mut bytes = [0u8; 8];
296        self.try_fill_bytes(&mut bytes)?;
297        Ok(u64::from_le_bytes(bytes))
298    }
299
300    fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Self::Error> {
301        self.expander.fill_bytes(dest);
302        Ok(())
303    }
304}
305
306#[cfg(feature = "hash")]
307#[cfg(test)]
308mod kt128_rng_tests {
309    use super::*;
310    use crate::kt128_expander::Kt128Expander;
311
312    /// HPKE domain + seed must match the shared expander (regression for refactor).
313    #[test]
314    fn test_kt128_rng_matches_hpke_domain_expander() {
315        let seed = [9u8; 32];
316        let mut rng = Kt128Rng::from_seed(&seed);
317        let mut exp = Kt128Expander::from_seed(crate::kt128_expander::DOMAIN_HPKE_RNG, &seed);
318        let mut a = [0u8; 128];
319        let mut b = [0u8; 128];
320        rng.fill_bytes(&mut a);
321        exp.fill_bytes(&mut b);
322        assert_eq!(a, b);
323    }
324}
325
326#[cfg(feature = "hash")]
327impl TryCryptoRng for Kt128Rng {}
328
329// HPKE-specific trait implementation for compatibility
330// This will be implemented in the HPKE crate itself to avoid circular dependencies
331
332/// FN-DSA compatible RNG with environment-specific implementations
333pub struct FnDsaRng {
334    // `rand::rngs::ThreadRng` lives in `std` (it needs `rand`'s `thread_rng`). On `no_std` — even
335    // when `rand` is enabled — there is no ThreadRng, so fall back to getrandom/deterministic below.
336    #[cfg(all(feature = "rand", feature = "std"))]
337    rng: Option<rand::rngs::ThreadRng>,
338}
339
340impl Default for FnDsaRng {
341    fn default() -> Self {
342        Self::new()
343    }
344}
345
346impl FnDsaRng {
347    /// Create a new FN-DSA compatible RNG
348    #[must_use]
349    pub fn new() -> Self {
350        #[cfg(all(feature = "rand", feature = "std"))]
351        {
352            Self {
353                rng: Some(rand::rng()),
354            }
355        }
356        #[cfg(not(all(feature = "rand", feature = "std")))]
357        {
358            Self {}
359        }
360    }
361}
362
363impl TryRng for FnDsaRng {
364    type Error = core::convert::Infallible;
365
366    fn try_next_u32(&mut self) -> Result<u32, Self::Error> {
367        #[cfg(all(feature = "rand", feature = "std"))]
368        {
369            if let Some(ref mut rng) = self.rng {
370                Ok(rng.next_u32())
371            } else {
372                let mut bytes = [0u8; 4];
373                self.try_fill_bytes(&mut bytes)?;
374                Ok(u32::from_le_bytes(bytes))
375            }
376        }
377        #[cfg(not(all(feature = "rand", feature = "std")))]
378        {
379            #[cfg(feature = "getrandom")]
380            {
381                let mut bytes = [0u8; 4];
382                getrandom::fill(&mut bytes).expect("Failed to get random bytes from getrandom");
383                Ok(u32::from_le_bytes(bytes))
384            }
385            #[cfg(not(feature = "getrandom"))]
386            {
387                panic!(
388                    "FnDsaRng requires the 'std' (ThreadRng) or 'getrandom' feature. \
389                       Use deterministic RNG for testing without these features."
390                );
391            }
392        }
393    }
394
395    fn try_next_u64(&mut self) -> Result<u64, Self::Error> {
396        #[cfg(all(feature = "getrandom", not(all(feature = "rand", feature = "std"))))]
397        {
398            let mut bytes = [0u8; 8];
399            getrandom::fill(&mut bytes).expect("Failed to get random bytes from getrandom");
400            Ok(u64::from_le_bytes(bytes))
401        }
402
403        #[cfg(not(all(feature = "getrandom", not(all(feature = "rand", feature = "std")))))]
404        {
405            let upper = u64::from(self.try_next_u32()?);
406            let lower = u64::from(self.try_next_u32()?);
407            Ok((upper << 32) | lower)
408        }
409    }
410
411    fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Self::Error> {
412        // `rand` disabled + `getrandom` enabled (e.g. WASM with `wasm_js`): fill the whole buffer in
413        // one call. The default path uses `try_next_u32` per 4-byte chunk, which would invoke
414        // `getrandom::fill` once per chunk and amplify syscall / JS bridge overhead.
415        #[cfg(all(feature = "getrandom", not(all(feature = "rand", feature = "std"))))]
416        {
417            getrandom::fill(dest).expect("Failed to get random bytes from getrandom");
418            Ok(())
419        }
420
421        #[cfg(not(all(feature = "getrandom", not(all(feature = "rand", feature = "std")))))]
422        {
423            for chunk in dest.chunks_mut(4) {
424                let bytes = self.try_next_u32()?.to_le_bytes();
425                let len = chunk.len().min(4);
426                chunk[..len].copy_from_slice(&bytes[..len]);
427            }
428            Ok(())
429        }
430    }
431}
432
433impl TryCryptoRng for FnDsaRng {}
434
435#[cfg(test)]
436mod tests {
437    use rand_core::Rng;
438
439    use super::*;
440
441    #[test]
442    fn test_classical_mceliece_rng_creation() {
443        let rng = ClassicalMcElieceRng::new();
444        assert!(!rng.deterministic);
445        assert_eq!(rng.state, 0);
446        assert_eq!(rng.counter, 0);
447    }
448
449    #[test]
450    fn test_classical_mceliece_deterministic_rng_creation() {
451        let rng = ClassicalMcElieceRng::new_deterministic(12345);
452        assert!(rng.deterministic);
453        // The state is transformed by the LCG, so we check it's not zero
454        assert_ne!(rng.state, 0);
455        assert_eq!(rng.counter, 0);
456    }
457
458    #[test]
459    fn test_classical_mceliece_deterministic_rng_consistency() {
460        let mut rng1 = ClassicalMcElieceRng::new_deterministic(42);
461        let mut rng2 = ClassicalMcElieceRng::new_deterministic(42);
462
463        let mut bytes1 = [0u8; 32];
464        let mut bytes2 = [0u8; 32];
465
466        rng1.fill_bytes(&mut bytes1);
467        rng2.fill_bytes(&mut bytes2);
468
469        assert_eq!(bytes1, bytes2);
470    }
471
472    #[test]
473    fn test_classical_mceliece_rng_interface() {
474        let mut rng = ClassicalMcElieceRng::new_deterministic(100);
475
476        // Test fill_bytes
477        let mut bytes = [0u8; 16];
478        rng.fill_bytes(&mut bytes);
479        assert_ne!(bytes, [0u8; 16]); // Should not be all zeros
480
481        // Test next_u32
482        let val1 = rng.next_u32();
483        let val2 = rng.next_u32();
484        assert_ne!(val1, val2); // Should be different
485
486        // Test next_u64
487        let val3 = rng.next_u64();
488        let val4 = rng.next_u64();
489        assert_ne!(val3, val4); // Should be different
490    }
491
492    #[test]
493    fn test_classical_mceliece_randombytes_init() {
494        let mut rng = ClassicalMcElieceRng::new_deterministic(0);
495        let entropy = [1u8; 48];
496
497        rng.randombytes_init(entropy);
498
499        // State should be updated
500        assert_ne!(rng.state, 0);
501        assert_eq!(rng.reseed_counter, 1);
502    }
503
504    #[test]
505    fn test_fn_dsa_rng_creation() {
506        let _rng = FnDsaRng::new();
507        // Should create without panicking
508        // Test passes if we reach this point
509    }
510
511    #[test]
512    #[cfg(any(feature = "rand", feature = "getrandom"))]
513    fn test_fn_dsa_rng_interface() {
514        let mut rng = FnDsaRng::new();
515
516        // Test fill_bytes
517        let mut bytes = [0u8; 16];
518        rng.fill_bytes(&mut bytes);
519        // Should not panic
520
521        // Test next_u32
522        let val1 = rng.next_u32();
523        let val2 = rng.next_u32();
524        // Should not panic and should be different (very high probability)
525        assert_ne!(val1, val2);
526
527        // Test next_u64
528        let val3 = rng.next_u64();
529        let val4 = rng.next_u64();
530        // Should not panic and should be different (very high probability)
531        assert_ne!(val3, val4);
532    }
533
534    /// Odd-length buffer: `getrandom`-only `try_fill_bytes` must use a single `getrandom::fill`
535    /// (not one `fill` per 4-byte chunk via `try_next_u32`).
536    #[test]
537    #[cfg(all(feature = "getrandom", not(feature = "rand")))]
538    fn test_fn_dsa_rng_fill_bytes_getrandom_only_odd_length() {
539        let mut rng = FnDsaRng::new();
540        let mut buf = [0u8; 1281];
541        rng.fill_bytes(&mut buf);
542    }
543}