Skip to main content

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