lib_q_random/provider.rs
1// Allow clippy warnings in provider code
2// These are legitimate patterns for API design
3#![allow(clippy::must_use_candidate)]
4
5//! RNG provider implementations
6//!
7//! This module provides the main RNG provider implementation and factory
8//! for creating and managing RNG instances with different characteristics.
9
10#[cfg(feature = "alloc")]
11use alloc::boxed::Box;
12#[cfg(feature = "alloc")]
13use core::fmt;
14
15#[cfg(feature = "alloc")]
16use rand_core::{
17 TryCryptoRng,
18 TryRng,
19};
20
21#[cfg(feature = "alloc")]
22use crate::Result;
23#[cfg(feature = "alloc")]
24use crate::traits::{
25 EntropySource,
26 ProviderCapabilities,
27 RngConfig,
28 RngProvider,
29 SecureRng,
30 SecurityLevel,
31};
32#[cfg(feature = "alloc")]
33use crate::validation::EntropyValidator;
34
35/// Main libQ random number generator
36///
37/// This is the primary RNG implementation for the libQ ecosystem, providing
38/// a unified interface for secure random number generation across different
39/// platforms and use cases.
40#[cfg(feature = "alloc")]
41pub struct LibQRng {
42 /// Entropy source for random data
43 entropy_source: Box<dyn EntropySource>,
44 /// Entropy validator for quality assessment
45 validator: EntropyValidator,
46 /// Security level of this RNG
47 security_level: SecurityLevel,
48 /// Whether this RNG is deterministic
49 deterministic: bool,
50 /// Reseed counter for security
51 reseed_counter: u32,
52 /// Bytes generated since last reseed
53 bytes_generated: usize,
54 /// Reseed interval in bytes
55 reseed_interval: Option<usize>,
56}
57
58/// Marker for types [`LibQRng::fill`] may write raw CSPRNG bytes over.
59///
60/// # Contract
61///
62/// Implementing this asserts that **every bit pattern of the type is a valid
63/// value**. `fill` reinterprets the destination slice as bytes and overwrites
64/// it with CSPRNG output, so any type for which some bit pattern is invalid
65/// would be left holding an invalid value — undefined behaviour independent of
66/// whether the value is ever read.
67///
68/// # Why it is sealed, and why it is not `Copy + Default`
69///
70/// `fill`'s bound was `T: Copy + Default` until card `t_1594295d`. That is a
71/// weaker and different property: `bool` is `Copy + Default` and only
72/// `0x00`/`0x01` are valid bit patterns; `char` is `Copy + Default` and must be
73/// a Unicode scalar value. Random bytes satisfy neither. The bound read as
74/// "any simple value type", and the two most obvious simple value types it
75/// admitted were exactly the two it must not.
76///
77/// The trait is sealed so the validity claim cannot be asserted from outside
78/// this crate, where it would not be checkable. It is implemented for the
79/// integer primitives only — the types that genuinely accept every bit
80/// pattern. Notably absent and deliberately so: `bool`, `char`, `f32`/`f64`
81/// (every pattern is a valid float, including signalling `NaNs`, but a random
82/// "float" is almost never what a caller means — ask for integers and convert),
83/// and `NonZero*` (zero is invalid by construction).
84pub trait FillableBytes: private::Sealed + Copy {}
85
86mod private {
87 /// Seals [`super::FillableBytes`] against outside implementations.
88 pub trait Sealed {}
89}
90
91macro_rules! impl_fillable_bytes {
92 ($($t:ty),* $(,)?) => {
93 $(
94 impl private::Sealed for $t {}
95 impl FillableBytes for $t {}
96 )*
97 };
98}
99
100// Every bit pattern of a fixed-width integer is a valid value of that integer.
101impl_fillable_bytes!(
102 u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize
103);
104
105#[cfg(feature = "alloc")]
106impl LibQRng {
107 /// Create a new secure RNG using the best available entropy source
108 ///
109 /// This method creates a cryptographically secure RNG using the highest
110 /// quality entropy source available on the current platform.
111 ///
112 /// # Errors
113 ///
114 /// Returns an error if no secure entropy source is available.
115 ///
116 /// # Examples
117 ///
118 /// ```rust
119 /// use lib_q_random::LibQRng;
120 /// use rand_core::Rng;
121 ///
122 /// let mut rng = LibQRng::new_secure().unwrap();
123 /// let mut bytes = [0u8; 32];
124 /// rng.fill_bytes(&mut bytes);
125 /// ```
126 pub fn new_secure() -> Result<Self> {
127 let entropy_source = crate::entropy::EntropySourceFactory::create_best_available()?;
128 // Use relaxed validation settings for real-world entropy sources
129 let validator = EntropyValidator::with_settings(
130 64, // min_entropy_bits: 64 bits minimum (8 bytes)
131 8192, // max_entropy_bits: 8KB maximum
132 0.3, // quality_threshold: More realistic threshold
133 false, // strict_mode: Disabled for real-world usage
134 );
135
136 Ok(Self {
137 entropy_source,
138 validator,
139 security_level: SecurityLevel::CryptographicallySecure,
140 deterministic: false,
141 reseed_counter: 0,
142 bytes_generated: 0,
143 reseed_interval: Some(1024 * 1024), // 1MB reseed interval
144 })
145 }
146
147 /// Create a new deterministic RNG for testing
148 ///
149 /// Initializes a **KT128** (`KangarooTwelve`) XOF byte stream from a **256-bit** seed.
150 /// Suitable for KATs and regression
151 /// tests. **Unpredictability is only as strong as the seed**: this is not a
152 /// substitute for [`Self::new_secure`] in production.
153 ///
154 /// # Arguments
155 ///
156 /// * `seed` - 32-byte seed; must be chosen explicitly for tests
157 ///
158 /// # Examples
159 ///
160 /// ```rust
161 /// use lib_q_random::LibQRng;
162 /// use rand_core::Rng;
163 ///
164 /// let mut rng = LibQRng::new_deterministic([1; 32]);
165 /// let mut bytes = [0u8; 32];
166 /// rng.fill_bytes(&mut bytes);
167 /// ```
168 pub fn new_deterministic(seed: [u8; 32]) -> Self {
169 let entropy_source =
170 crate::entropy::EntropySourceFactory::create_deterministic_entropy(seed);
171 // Deterministic RNGs don't need strict validation since they're not cryptographically secure
172 let validator = EntropyValidator::with_settings(
173 32, // min_entropy_bits: Lower threshold for deterministic
174 1024, // max_entropy_bits: Smaller limit
175 0.1, // quality_threshold: Very low threshold since it's deterministic
176 false, // strict_mode: Disabled
177 );
178
179 Self {
180 entropy_source,
181 validator,
182 security_level: SecurityLevel::Deterministic,
183 deterministic: true,
184 reseed_counter: 0,
185 bytes_generated: 0,
186 reseed_interval: None, // No reseeding for deterministic RNGs
187 }
188 }
189
190 /// Create a deterministic RNG from a `u64` test seed (`SplitMix64` → KT128).
191 pub fn new_deterministic_from_u64(seed: u64) -> Self {
192 let entropy_source =
193 crate::entropy::EntropySourceFactory::create_deterministic_entropy_from_u64(seed);
194 let validator = EntropyValidator::with_settings(32, 1024, 0.1, false);
195
196 Self {
197 entropy_source,
198 validator,
199 security_level: SecurityLevel::Deterministic,
200 deterministic: true,
201 reseed_counter: 0,
202 bytes_generated: 0,
203 reseed_interval: None,
204 }
205 }
206
207 /// Create a deterministic RNG using Saturnin CTR keystream (`deterministic-saturnin` feature).
208 ///
209 /// Requires `alloc`. Uses domain [`crate::kt128_expander::DOMAIN_LIBQ_DET_SATURNIN`] for the CTR nonce.
210 ///
211 /// # Errors
212 ///
213 /// Returns an error if Saturnin keystream generation fails.
214 #[cfg(feature = "deterministic-saturnin")]
215 pub fn new_deterministic_saturnin(seed: [u8; 32]) -> Result<Self> {
216 let entropy_source = alloc::boxed::Box::new(
217 crate::saturnin_det::SaturninDeterministicEntropySource::new(seed)?,
218 );
219 let validator = EntropyValidator::with_settings(32, 1024, 0.1, false);
220 Ok(Self {
221 entropy_source,
222 validator,
223 security_level: SecurityLevel::Deterministic,
224 deterministic: true,
225 reseed_counter: 0,
226 bytes_generated: 0,
227 reseed_interval: None,
228 })
229 }
230
231 /// Create a new RNG with NIST AES256-CTR-DRBG for KAT test compatibility
232 ///
233 /// This method creates an RNG using the NIST AES256-CTR-DRBG algorithm,
234 /// which is required for compatibility with NIST KAT test vectors.
235 ///
236 /// # Arguments
237 ///
238 /// * `entropy_input` - 48-byte entropy input for DRBG initialization
239 ///
240 /// # Examples
241 ///
242 /// ```rust
243 /// use lib_q_random::LibQRng;
244 /// use rand_core::Rng;
245 ///
246 /// let entropy_input = [0u8; 48]; // 48-byte seed
247 /// let mut rng = LibQRng::new_nist_drbg(entropy_input);
248 /// let mut bytes = [0u8; 32];
249 /// rng.fill_bytes(&mut bytes);
250 /// ```
251 #[cfg(feature = "nist-drbg")]
252 pub fn new_nist_drbg(entropy_input: [u8; 48]) -> Self {
253 let entropy_source =
254 crate::entropy::EntropySourceFactory::create_nist_drbg_entropy(entropy_input);
255 // NIST DRBG provides high quality entropy
256 let validator = EntropyValidator::with_settings(
257 256, // min_entropy_bits: High threshold for NIST DRBG
258 4096, // max_entropy_bits: Higher limit
259 0.9, // quality_threshold: High threshold for NIST DRBG
260 true, // strict_mode: Enabled for NIST DRBG
261 );
262
263 Self {
264 entropy_source,
265 validator,
266 security_level: SecurityLevel::CryptographicallySecure,
267 deterministic: true, // NIST DRBG is deterministic but cryptographically secure
268 reseed_counter: 0,
269 bytes_generated: 0,
270 reseed_interval: Some(1_000_000), // NIST recommendation
271 }
272 }
273
274 /// Create a new RNG with a custom entropy source
275 ///
276 /// This method allows creating an RNG with a custom entropy source,
277 /// useful for specialized applications or testing.
278 ///
279 /// # Arguments
280 ///
281 /// * `entropy_source` - Custom entropy source implementation
282 ///
283 /// # Examples
284 ///
285 /// ```rust
286 /// use lib_q_random::LibQRng;
287 /// use lib_q_random::entropy::UserEntropySource;
288 /// use rand_core::Rng;
289 ///
290 /// let entropy_data = vec![1, 2, 3, 4, 5, 6, 7, 8];
291 /// let entropy_source = UserEntropySource::new(entropy_data);
292 /// let mut rng = LibQRng::new_custom(entropy_source);
293 /// ```
294 pub fn new_custom<T: EntropySource + 'static>(entropy_source: T) -> Self {
295 let entropy_source = Box::new(entropy_source);
296 // Use appropriate validator settings based on entropy source type
297 let validator = match entropy_source.source_type() {
298 crate::traits::EntropySourceType::Hardware => {
299 EntropyValidator::with_settings(64, 8192, 0.4, false)
300 }
301 crate::traits::EntropySourceType::OperatingSystem => {
302 EntropyValidator::with_settings(64, 8192, 0.3, false)
303 }
304 _ => EntropyValidator::with_settings(64, 8192, 0.3, false),
305 };
306
307 // Determine security level based on entropy source type
308 let security_level = match entropy_source.source_type() {
309 crate::traits::EntropySourceType::Hardware => SecurityLevel::Hardware,
310 crate::traits::EntropySourceType::OperatingSystem => {
311 SecurityLevel::CryptographicallySecure
312 }
313 crate::traits::EntropySourceType::Deterministic |
314 crate::traits::EntropySourceType::User => SecurityLevel::Deterministic,
315 };
316
317 let deterministic = matches!(
318 entropy_source.source_type(),
319 crate::traits::EntropySourceType::Deterministic |
320 crate::traits::EntropySourceType::User
321 );
322
323 Self {
324 entropy_source,
325 validator,
326 security_level,
327 deterministic,
328 reseed_counter: 0,
329 bytes_generated: 0,
330 reseed_interval: if deterministic {
331 None
332 } else {
333 Some(1024 * 1024)
334 },
335 }
336 }
337
338 /// Create a new RNG with custom configuration
339 ///
340 /// This method allows creating an RNG with specific configuration
341 /// parameters for specialized use cases.
342 ///
343 /// # Arguments
344 ///
345 /// * `config` - RNG configuration parameters
346 ///
347 /// # Errors
348 ///
349 /// Returns an error if the configuration is invalid or if the RNG
350 /// cannot be created with the specified parameters.
351 pub fn with_config(config: &RngConfig) -> Result<Self> {
352 let entropy_source = if let Some(_source) = &config.entropy_source {
353 // We can't move out of a reference, so we need to create a new one
354 // This is a limitation of the current design
355 crate::entropy::EntropySourceFactory::create_best_available()?
356 } else {
357 crate::entropy::EntropySourceFactory::create_best_available()?
358 };
359
360 // Use appropriate validator settings based on security level
361 let validator = match config.security_level {
362 SecurityLevel::Hardware => EntropyValidator::with_settings(64, 8192, 0.4, false),
363 SecurityLevel::CryptographicallySecure => {
364 EntropyValidator::with_settings(64, 8192, 0.3, false)
365 }
366 SecurityLevel::Deterministic => EntropyValidator::with_settings(32, 1024, 0.1, false),
367 SecurityLevel::Software => EntropyValidator::with_settings(64, 8192, 0.3, false),
368 };
369 let deterministic =
370 entropy_source.source_type() == crate::traits::EntropySourceType::Deterministic;
371
372 Ok(Self {
373 entropy_source,
374 validator,
375 security_level: config.security_level,
376 deterministic,
377 reseed_counter: 0,
378 bytes_generated: 0,
379 reseed_interval: config.reseed_interval,
380 })
381 }
382
383 /// Check if this RNG is deterministic
384 pub fn is_deterministic(&self) -> bool {
385 self.deterministic
386 }
387
388 /// Get the security level of this RNG
389 pub fn security_level(&self) -> SecurityLevel {
390 self.security_level
391 }
392
393 /// Get the entropy source name
394 pub fn entropy_source_name(&self) -> &'static str {
395 self.entropy_source.name()
396 }
397
398 /// Get the entropy source type
399 pub fn entropy_source_type(&self) -> crate::traits::EntropySourceType {
400 self.entropy_source.source_type()
401 }
402
403 /// Get the reseed counter
404 pub fn reseed_counter(&self) -> u32 {
405 self.reseed_counter
406 }
407
408 /// Get the bytes generated since last reseed
409 pub fn bytes_generated(&self) -> usize {
410 self.bytes_generated
411 }
412
413 /// Check if this RNG is cryptographically secure
414 pub fn is_secure(&self) -> bool {
415 self.security_level == SecurityLevel::CryptographicallySecure
416 }
417
418 /// Get the entropy quality estimate (0.0 to 1.0)
419 pub fn entropy_quality(&self) -> f64 {
420 match self.security_level {
421 SecurityLevel::CryptographicallySecure => 1.0,
422 SecurityLevel::Deterministic => 0.0,
423 SecurityLevel::Hardware => 0.95,
424 SecurityLevel::Software => 0.8,
425 }
426 }
427
428 /// Check if reseeding is needed
429 fn needs_reseed(&self) -> bool {
430 if let Some(interval) = self.reseed_interval {
431 self.bytes_generated >= interval
432 } else {
433 false
434 }
435 }
436
437 /// Perform reseeding if needed
438 fn reseed_if_needed(&mut self) -> Result<()> {
439 if self.needs_reseed() {
440 self.reseed()?;
441 }
442 Ok(())
443 }
444}
445
446#[cfg(feature = "alloc")]
447impl SecureRng for LibQRng {
448 fn fill_bytes_secure(&mut self, dest: &mut [u8]) -> Result<()> {
449 // Check if reseeding is needed
450 self.reseed_if_needed()?;
451
452 // Get entropy from the source
453 self.entropy_source.get_entropy(dest)?;
454
455 // Validate entropy quality if not deterministic.
456 // Skip validation for buffers < 64 bytes: statistical quality tests are
457 // unreliable on small samples and produce false positives that abort the
458 // process. For larger buffers, validate only the first 64 bytes so we
459 // never exceed the validator's max_entropy_bits / 8 limit (1 KB) even
460 // when the caller requests several kilobytes at once.
461 if !self.deterministic && dest.len() >= 64 {
462 self.validator.validate_entropy(&dest[..64])?;
463 }
464
465 // Update counters
466 self.bytes_generated += dest.len();
467
468 Ok(())
469 }
470
471 fn next_u32_secure(&mut self) -> Result<u32> {
472 let mut bytes = [0u8; 4];
473 self.fill_bytes_secure(&mut bytes)?;
474 Ok(u32::from_le_bytes(bytes))
475 }
476
477 fn next_u64_secure(&mut self) -> Result<u64> {
478 let mut bytes = [0u8; 8];
479 self.fill_bytes_secure(&mut bytes)?;
480 Ok(u64::from_le_bytes(bytes))
481 }
482
483 fn initialize(&mut self, entropy: &[u8]) -> Result<()> {
484 // For deterministic RNGs, we can reinitialize with new seed
485 if self.deterministic {
486 let seed: [u8; 32] = entropy.try_into().map_err(|_| {
487 crate::Error::invalid_configuration(
488 "deterministic seed",
489 "exactly 32 bytes",
490 "slice length is not 32",
491 )
492 })?;
493 let new_source =
494 crate::entropy::EntropySourceFactory::create_deterministic_entropy(seed);
495 self.entropy_source = new_source;
496 self.reseed_counter = 0;
497 self.bytes_generated = 0;
498 }
499 // For secure RNGs, we can't reinitialize with user entropy
500 // as it would compromise security
501 Ok(())
502 }
503
504 fn is_secure(&self) -> bool {
505 !self.deterministic
506 }
507
508 fn entropy_quality(&self) -> f64 {
509 self.entropy_source.quality()
510 }
511
512 fn security_level(&self) -> SecurityLevel {
513 self.security_level
514 }
515
516 fn reseed(&mut self) -> Result<()> {
517 if self.deterministic {
518 return Ok(()); // No reseeding for deterministic RNGs
519 }
520
521 // For secure RNGs, reseeding is handled by the entropy source
522 // We just update our counters
523 self.reseed_counter = self.reseed_counter.wrapping_add(1);
524 self.bytes_generated = 0;
525
526 Ok(())
527 }
528
529 fn state_size(&self) -> usize {
530 // This is an estimate - the actual state size depends on the entropy source
531 64
532 }
533
534 fn reseed_interval(&self) -> Option<usize> {
535 self.reseed_interval
536 }
537}
538
539#[cfg(feature = "alloc")]
540impl TryRng for LibQRng {
541 type Error = core::convert::Infallible;
542
543 fn try_next_u32(&mut self) -> core::result::Result<u32, Self::Error> {
544 match self.next_u32_secure() {
545 Ok(value) => Ok(value),
546 Err(_) => rng_abort(),
547 }
548 }
549
550 fn try_next_u64(&mut self) -> core::result::Result<u64, Self::Error> {
551 match self.next_u64_secure() {
552 Ok(value) => Ok(value),
553 Err(_) => rng_abort(),
554 }
555 }
556
557 fn try_fill_bytes(&mut self, dest: &mut [u8]) -> core::result::Result<(), Self::Error> {
558 match self.fill_bytes_secure(dest) {
559 Ok(()) => Ok(()),
560 Err(_) => rng_abort(),
561 }
562 }
563}
564
565/// Hard stop on unrecoverable entropy failure (avoids `panic!` / `eprintln!` for strict Clippy).
566// `clippy::panic` is denied in non-test builds (see `lib.rs` lint config),
567// but the `no_std` branch of this abort path has no `std::process::abort`
568// alternative, so `panic!` is the only way out. Allow it on the function so
569// the attribute targets an item rather than a macro invocation.
570#[cfg(feature = "alloc")]
571#[inline(never)]
572#[allow(clippy::panic)]
573fn rng_abort() -> ! {
574 #[cfg(feature = "std")]
575 std::process::abort();
576 #[cfg(not(feature = "std"))]
577 panic!("CRITICAL SECURITY FAILURE: RNG entropy unavailable");
578}
579
580#[cfg(feature = "alloc")]
581impl TryCryptoRng for LibQRng {}
582
583#[cfg(feature = "alloc")]
584impl LibQRng {
585 /// Fill a slice with random values of any integer type
586 ///
587 /// This method provides a convenient way to fill slices of different integer types
588 /// with random values, handling the byte conversion internally.
589 ///
590 /// # Which `T` is accepted, and why it is not `Copy + Default`
591 ///
592 /// `T` must implement [`FillableBytes`], a sealed marker for types where
593 /// **every bit pattern is a valid value**. This bound used to be
594 /// `Copy + Default`, which is not the same property and admitted types
595 /// this function cannot legally fill: `bool` (only `0x00`/`0x01` are
596 /// valid) and `char` (must be a Unicode scalar value) both satisfy
597 /// `Copy + Default`, and writing random bytes over one produces an
598 /// invalid value — undefined behaviour before anything even reads it.
599 /// See card `t_1594295d`.
600 ///
601 /// # Guarantee: no unzeroized intermediate buffer
602 ///
603 /// CSPRNG output is written **directly into `dest`**'s backing memory; this
604 /// function never stages the bytes it generates in a temporary heap
605 /// allocation. That matters because `dest` is frequently key material
606 /// (e.g. wrapped in `zeroize::Zeroizing` by the caller) — a temporary
607 /// `Vec<u8>` copy would be dropped without scrubbing and could strand a
608 /// copy of that key material in freed heap memory. Callers do not need to
609 /// take any extra precaution against a hidden intermediate: there isn't
610 /// one.
611 ///
612 /// # Examples
613 ///
614 /// ```rust
615 /// use lib_q_random::LibQRng;
616 ///
617 /// let mut rng = LibQRng::new_secure().unwrap();
618 /// let mut u16_array = [0u16; 10];
619 /// rng.fill(&mut u16_array);
620 /// ```
621 ///
622 /// `bool` is rejected at compile time. It is `Copy + Default`, so the old
623 /// bound accepted it, and a random byte that is neither `0x00` nor `0x01`
624 /// is an invalid `bool` — UB. This doctest is the regression pin for that:
625 /// it fails the build if the bound is ever loosened back.
626 ///
627 /// ```compile_fail
628 /// use lib_q_random::LibQRng;
629 ///
630 /// let mut rng = LibQRng::new_secure().unwrap();
631 /// let mut flags = [false; 8];
632 /// rng.fill(&mut flags);
633 /// ```
634 ///
635 /// `char` likewise — most 4-byte patterns are not Unicode scalar values.
636 ///
637 /// ```compile_fail
638 /// use lib_q_random::LibQRng;
639 ///
640 /// let mut rng = LibQRng::new_secure().unwrap();
641 /// let mut chars = [' '; 4];
642 /// rng.fill(&mut chars);
643 /// ```
644 pub fn fill<T>(&mut self, dest: &mut [T])
645 where
646 T: FillableBytes,
647 {
648 if dest.is_empty() {
649 return;
650 }
651
652 // Calculate the number of bytes needed
653 let size = core::mem::size_of::<T>();
654 if size == 0 {
655 return;
656 }
657 let total_bytes = core::mem::size_of_val(dest);
658
659 // SAFETY: `dest` is a valid, exclusively-borrowed `&mut [T]` of
660 // `dest.len()` elements, so it denotes exactly `total_bytes` bytes of
661 // valid, properly aligned, writable memory for the lifetime of this
662 // borrow. Reinterpreting it as `&mut [u8]` is sound because `u8` has
663 // no alignment requirement (any `T` alignment satisfies it) and every
664 // byte in the region is being written by `fill_bytes_secure` before
665 // it is read as part of `T` again, so we never observe uninitialized
666 // padding as a `T`-typed value through this reference. Writing
667 // straight into `dest` this way (rather than through a temporary
668 // `Vec<u8>`) is what gives `fill` the no-intermediate-buffer
669 // guarantee documented above.
670 let bytes =
671 unsafe { core::slice::from_raw_parts_mut(dest.as_mut_ptr().cast::<u8>(), total_bytes) };
672
673 // Entropy failure must never yield predictable output; abort like the
674 // infallible `RngCore` path instead of leaving `dest` with whatever
675 // bytes were written so far.
676 if self.fill_bytes_secure(bytes).is_err() {
677 rng_abort();
678 }
679 }
680}
681
682// LibQRng implements rand_core::Rng and TryCryptoRng, so CryptoRng and Rng
683// are provided by rand_core blanket impls. The signature crate uses rand_core
684// and will see these implementations when using the same rand_core version.
685
686#[cfg(feature = "alloc")]
687impl fmt::Display for LibQRng {
688 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
689 write!(
690 f,
691 "LibQRng(security_level: {}, entropy_source: {}, deterministic: {}, reseed_counter: {})",
692 self.security_level,
693 self.entropy_source.name(),
694 self.deterministic,
695 self.reseed_counter
696 )
697 }
698}
699
700/// RNG provider factory
701///
702/// This factory provides convenient methods for creating RNG instances
703/// with different characteristics and configurations.
704pub struct LibQRngProvider;
705
706impl LibQRngProvider {
707 /// Create a new RNG provider
708 pub fn new() -> Self {
709 Self
710 }
711}
712
713#[cfg(feature = "alloc")]
714impl RngProvider for LibQRngProvider {
715 fn create_rng(&self, config: &RngConfig) -> Result<Box<dyn SecureRng>> {
716 let rng = LibQRng::with_config(config)?;
717 Ok(Box::new(rng))
718 }
719
720 fn name(&self) -> &'static str {
721 "libQ RNG Provider"
722 }
723
724 fn capabilities(&self) -> ProviderCapabilities {
725 ProviderCapabilities {
726 secure: true,
727 deterministic: true,
728 hardware: true,
729 reseeding: true,
730 custom_entropy: true,
731 no_std: true,
732 wasm: true,
733 }
734 }
735
736 fn supports_config(&self, config: &RngConfig) -> bool {
737 // We support all configurations
738 let _ = config;
739 true
740 }
741
742 fn priority(&self) -> u32 {
743 100 // High priority as the main provider
744 }
745}
746
747impl Default for LibQRngProvider {
748 fn default() -> Self {
749 Self::new()
750 }
751}
752
753#[cfg(test)]
754mod tests {
755 #[cfg(all(not(feature = "std"), feature = "alloc"))]
756 use alloc::format;
757
758 #[cfg(feature = "alloc")]
759 use rand_core::Rng;
760
761 #[cfg(feature = "alloc")]
762 use super::*;
763
764 #[test]
765 #[cfg(feature = "alloc")]
766 fn test_libq_rng_deterministic_creation() {
767 let mut seed = [0u8; 32];
768 seed[..8].copy_from_slice(&[1, 2, 3, 4, 5, 6, 7, 8]);
769 let rng = LibQRng::new_deterministic(seed);
770 assert!(rng.is_deterministic());
771 assert_eq!(rng.security_level(), SecurityLevel::Deterministic);
772 assert!(!rng.is_secure());
773 }
774
775 #[test]
776 #[cfg(feature = "alloc")]
777 fn test_libq_rng_deterministic_consistency() {
778 let seed = [42u8; 32];
779 let mut rng1 = LibQRng::new_deterministic(seed);
780 let mut rng2 = LibQRng::new_deterministic(seed);
781
782 let mut bytes1 = [0u8; 32];
783 let mut bytes2 = [0u8; 32];
784
785 rng1.fill_bytes(&mut bytes1);
786 rng2.fill_bytes(&mut bytes2);
787
788 assert_eq!(bytes1, bytes2);
789 }
790
791 #[test]
792 #[cfg(feature = "alloc")]
793 fn test_libq_rng_deterministic_golden_zero_seed() {
794 use crate::kt128_expander::Kt128Expander;
795
796 let expected = crate::kt128_expander::KT128_DET_GOLDEN_ZERO_SEED_64;
797 let mut rng = LibQRng::new_deterministic([0u8; 32]);
798 let mut out = [0u8; 64];
799 rng.fill_bytes(&mut out);
800 let mut direct = Kt128Expander::from_det_seed_32([0u8; 32]);
801 let mut expected_direct = [0u8; 64];
802 direct.fill_bytes(&mut expected_direct);
803 assert_eq!(out, expected);
804 assert_eq!(out, expected_direct);
805 }
806
807 /// Regression: deterministic RNG must use the full 256-bit seed (KT128), not a
808 /// collapsed 64-bit state where distant seed bytes could be ignored.
809 #[test]
810 #[cfg(feature = "alloc")]
811 fn test_libq_rng_deterministic_seeds_differ_in_final_byte_yield_different_streams() {
812 let seed_a = [0u8; 32];
813 let mut seed_b = [0u8; 32];
814 seed_b[31] = 1;
815
816 let mut rng_a = LibQRng::new_deterministic(seed_a);
817 let mut rng_b = LibQRng::new_deterministic(seed_b);
818
819 let mut out_a = [0u8; 64];
820 let mut out_b = [0u8; 64];
821 rng_a.fill_bytes(&mut out_a);
822 rng_b.fill_bytes(&mut out_b);
823
824 assert_ne!(
825 out_a, out_b,
826 "KT128 streams from different 32-byte keys must diverge immediately"
827 );
828 }
829
830 #[test]
831 #[cfg(feature = "alloc")]
832 fn test_libq_rng_custom_creation() {
833 let entropy_data = vec![1, 2, 3, 4, 5, 6, 7, 8];
834 let entropy_source = crate::entropy::UserEntropySource::new(entropy_data);
835 let rng = LibQRng::new_custom(entropy_source);
836 assert!(rng.is_deterministic());
837 assert_eq!(rng.security_level(), SecurityLevel::Deterministic);
838 }
839
840 #[test]
841 #[cfg(feature = "alloc")]
842 fn test_libq_rng_config_creation() {
843 let config = RngConfig::default();
844 let rng = LibQRng::with_config(&config);
845 assert!(rng.is_ok());
846 }
847
848 #[test]
849 #[cfg(feature = "alloc")]
850 fn test_libq_rng_provider_creation() {
851 let provider = LibQRngProvider::new();
852 assert_eq!(provider.name(), "libQ RNG Provider");
853 assert_eq!(provider.priority(), 100);
854 }
855
856 #[test]
857 #[cfg(feature = "alloc")]
858 fn test_libq_rng_provider_capabilities() {
859 let provider = LibQRngProvider::new();
860 let caps = provider.capabilities();
861 assert!(caps.secure);
862 assert!(caps.deterministic);
863 assert!(caps.hardware);
864 assert!(caps.reseeding);
865 assert!(caps.custom_entropy);
866 assert!(caps.no_std);
867 assert!(caps.wasm);
868 }
869
870 #[test]
871 #[cfg(feature = "alloc")]
872 fn test_libq_rng_provider_create_rng() {
873 let provider = LibQRngProvider::new();
874 let config = RngConfig::default();
875 let rng = provider.create_rng(&config);
876 assert!(rng.is_ok());
877 }
878
879 #[test]
880 #[cfg(feature = "alloc")]
881 fn test_libq_rng_reseed_counter() {
882 let mut seed = [0u8; 32];
883 seed[..4].copy_from_slice(&[1, 2, 3, 4]);
884 let rng = LibQRng::new_deterministic(seed);
885 assert_eq!(rng.reseed_counter(), 0);
886 assert_eq!(rng.bytes_generated(), 0);
887 }
888
889 #[test]
890 #[cfg(feature = "alloc")]
891 fn test_libq_rng_entropy_source_info() {
892 let mut seed = [0u8; 32];
893 seed[..4].copy_from_slice(&[1, 2, 3, 4]);
894 let rng = LibQRng::new_deterministic(seed);
895 assert!(!rng.entropy_source_name().is_empty());
896 assert_eq!(
897 rng.entropy_source_type(),
898 crate::traits::EntropySourceType::Deterministic
899 );
900 }
901
902 #[test]
903 #[cfg(feature = "alloc")]
904 fn test_libq_rng_display() {
905 let mut seed = [0u8; 32];
906 seed[..4].copy_from_slice(&[1, 2, 3, 4]);
907 let rng = LibQRng::new_deterministic(seed);
908 let display = format!("{rng}");
909 assert!(display.contains("LibQRng"));
910 assert!(display.contains("Deterministic"));
911 }
912}