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 => SecurityLevel::Deterministic,
270            crate::traits::EntropySourceType::User => SecurityLevel::CryptographicallySecure,
271        };
272
273        let deterministic =
274            entropy_source.source_type() == crate::traits::EntropySourceType::Deterministic;
275
276        Self {
277            entropy_source,
278            validator,
279            security_level,
280            deterministic,
281            reseed_counter: 0,
282            bytes_generated: 0,
283            reseed_interval: if deterministic {
284                None
285            } else {
286                Some(1024 * 1024)
287            },
288        }
289    }
290
291    /// Create a new RNG with custom configuration
292    ///
293    /// This method allows creating an RNG with specific configuration
294    /// parameters for specialized use cases.
295    ///
296    /// # Arguments
297    ///
298    /// * `config` - RNG configuration parameters
299    ///
300    /// # Errors
301    ///
302    /// Returns an error if the configuration is invalid or if the RNG
303    /// cannot be created with the specified parameters.
304    pub fn with_config(config: &RngConfig) -> Result<Self> {
305        let entropy_source = if let Some(_source) = &config.entropy_source {
306            // We can't move out of a reference, so we need to create a new one
307            // This is a limitation of the current design
308            crate::entropy::EntropySourceFactory::create_best_available()?
309        } else {
310            crate::entropy::EntropySourceFactory::create_best_available()?
311        };
312
313        // Use appropriate validator settings based on security level
314        let validator = match config.security_level {
315            SecurityLevel::Hardware => EntropyValidator::with_settings(64, 8192, 0.4, false),
316            SecurityLevel::CryptographicallySecure => {
317                EntropyValidator::with_settings(64, 8192, 0.3, false)
318            }
319            SecurityLevel::Deterministic => EntropyValidator::with_settings(32, 1024, 0.1, false),
320            SecurityLevel::Software => EntropyValidator::with_settings(64, 8192, 0.3, false),
321        };
322        let deterministic =
323            entropy_source.source_type() == crate::traits::EntropySourceType::Deterministic;
324
325        Ok(Self {
326            entropy_source,
327            validator,
328            security_level: config.security_level,
329            deterministic,
330            reseed_counter: 0,
331            bytes_generated: 0,
332            reseed_interval: config.reseed_interval,
333        })
334    }
335
336    /// Check if this RNG is deterministic
337    pub fn is_deterministic(&self) -> bool {
338        self.deterministic
339    }
340
341    /// Get the security level of this RNG
342    pub fn security_level(&self) -> SecurityLevel {
343        self.security_level
344    }
345
346    /// Get the entropy source name
347    pub fn entropy_source_name(&self) -> &'static str {
348        self.entropy_source.name()
349    }
350
351    /// Get the entropy source type
352    pub fn entropy_source_type(&self) -> crate::traits::EntropySourceType {
353        self.entropy_source.source_type()
354    }
355
356    /// Get the reseed counter
357    pub fn reseed_counter(&self) -> u32 {
358        self.reseed_counter
359    }
360
361    /// Get the bytes generated since last reseed
362    pub fn bytes_generated(&self) -> usize {
363        self.bytes_generated
364    }
365
366    /// Check if this RNG is cryptographically secure
367    pub fn is_secure(&self) -> bool {
368        self.security_level == SecurityLevel::CryptographicallySecure
369    }
370
371    /// Get the entropy quality estimate (0.0 to 1.0)
372    pub fn entropy_quality(&self) -> f64 {
373        match self.security_level {
374            SecurityLevel::CryptographicallySecure => 1.0,
375            SecurityLevel::Deterministic => 0.0,
376            SecurityLevel::Hardware => 0.95,
377            SecurityLevel::Software => 0.8,
378        }
379    }
380
381    /// Check if reseeding is needed
382    fn needs_reseed(&self) -> bool {
383        if let Some(interval) = self.reseed_interval {
384            self.bytes_generated >= interval
385        } else {
386            false
387        }
388    }
389
390    /// Perform reseeding if needed
391    fn reseed_if_needed(&mut self) -> Result<()> {
392        if self.needs_reseed() {
393            self.reseed()?;
394        }
395        Ok(())
396    }
397}
398
399#[cfg(feature = "alloc")]
400impl SecureRng for LibQRng {
401    fn fill_bytes_secure(&mut self, dest: &mut [u8]) -> Result<()> {
402        // Check if reseeding is needed
403        self.reseed_if_needed()?;
404
405        // Get entropy from the source
406        self.entropy_source.get_entropy(dest)?;
407
408        // Validate entropy quality if not deterministic
409        if !self.deterministic {
410            // Only validate if we have enough data
411            if dest.len() >= 8 {
412                let _ = self.validator.validate_entropy(dest);
413            }
414        }
415
416        // Update counters
417        self.bytes_generated += dest.len();
418
419        Ok(())
420    }
421
422    fn next_u32_secure(&mut self) -> Result<u32> {
423        let mut bytes = [0u8; 4];
424        self.fill_bytes_secure(&mut bytes)?;
425        Ok(u32::from_le_bytes(bytes))
426    }
427
428    fn next_u64_secure(&mut self) -> Result<u64> {
429        let mut bytes = [0u8; 8];
430        self.fill_bytes_secure(&mut bytes)?;
431        Ok(u64::from_le_bytes(bytes))
432    }
433
434    fn initialize(&mut self, entropy: &[u8]) -> Result<()> {
435        // For deterministic RNGs, we can reinitialize with new seed
436        if self.deterministic {
437            let seed: [u8; 32] = entropy.try_into().map_err(|_| {
438                crate::Error::invalid_configuration(
439                    "deterministic seed",
440                    "exactly 32 bytes",
441                    "slice length is not 32",
442                )
443            })?;
444            let new_source =
445                crate::entropy::EntropySourceFactory::create_deterministic_entropy(seed);
446            self.entropy_source = new_source;
447            self.reseed_counter = 0;
448            self.bytes_generated = 0;
449        }
450        // For secure RNGs, we can't reinitialize with user entropy
451        // as it would compromise security
452        Ok(())
453    }
454
455    fn is_secure(&self) -> bool {
456        !self.deterministic
457    }
458
459    fn entropy_quality(&self) -> f64 {
460        self.entropy_source.quality()
461    }
462
463    fn security_level(&self) -> SecurityLevel {
464        self.security_level
465    }
466
467    fn reseed(&mut self) -> Result<()> {
468        if self.deterministic {
469            return Ok(()); // No reseeding for deterministic RNGs
470        }
471
472        // For secure RNGs, reseeding is handled by the entropy source
473        // We just update our counters
474        self.reseed_counter = self.reseed_counter.wrapping_add(1);
475        self.bytes_generated = 0;
476
477        Ok(())
478    }
479
480    fn state_size(&self) -> usize {
481        // This is an estimate - the actual state size depends on the entropy source
482        64
483    }
484
485    fn reseed_interval(&self) -> Option<usize> {
486        self.reseed_interval
487    }
488}
489
490#[cfg(feature = "alloc")]
491impl TryRng for LibQRng {
492    type Error = core::convert::Infallible;
493
494    fn try_next_u32(&mut self) -> core::result::Result<u32, Self::Error> {
495        match self.next_u32_secure() {
496            Ok(value) => Ok(value),
497            Err(_) => rng_abort(),
498        }
499    }
500
501    fn try_next_u64(&mut self) -> core::result::Result<u64, Self::Error> {
502        match self.next_u64_secure() {
503            Ok(value) => Ok(value),
504            Err(_) => rng_abort(),
505        }
506    }
507
508    fn try_fill_bytes(&mut self, dest: &mut [u8]) -> core::result::Result<(), Self::Error> {
509        match self.fill_bytes_secure(dest) {
510            Ok(()) => Ok(()),
511            Err(_) => rng_abort(),
512        }
513    }
514}
515
516/// Hard stop on unrecoverable entropy failure (avoids `panic!` / `eprintln!` for strict Clippy).
517// `clippy::panic` is denied in non-test builds (see `lib.rs` lint config),
518// but the `no_std` branch of this abort path has no `std::process::abort`
519// alternative, so `panic!` is the only way out. Allow it on the function so
520// the attribute targets an item rather than a macro invocation.
521#[cfg(feature = "alloc")]
522#[inline(never)]
523#[allow(clippy::panic)]
524fn rng_abort() -> ! {
525    #[cfg(feature = "std")]
526    std::process::abort();
527    #[cfg(not(feature = "std"))]
528    panic!("CRITICAL SECURITY FAILURE: RNG entropy unavailable");
529}
530
531#[cfg(feature = "alloc")]
532impl TryCryptoRng for LibQRng {}
533
534#[cfg(feature = "alloc")]
535impl LibQRng {
536    /// Fill a slice with random values of any integer type
537    ///
538    /// This method provides a convenient way to fill slices of different integer types
539    /// with random values, handling the byte conversion internally.
540    ///
541    /// # Examples
542    ///
543    /// ```rust
544    /// use lib_q_random::LibQRng;
545    ///
546    /// let mut rng = LibQRng::new_secure().unwrap();
547    /// let mut u16_array = [0u16; 10];
548    /// rng.fill(&mut u16_array);
549    /// ```
550    pub fn fill<T>(&mut self, dest: &mut [T])
551    where
552        T: Copy + Default,
553    {
554        if dest.is_empty() {
555            return;
556        }
557
558        // Calculate the number of bytes needed
559        let size = core::mem::size_of::<T>();
560        let total_bytes = core::mem::size_of_val(dest);
561
562        // Create a temporary byte buffer
563        let mut bytes = vec![0u8; total_bytes];
564
565        // Fill with random bytes
566        let _ = self.fill_bytes_secure(&mut bytes);
567
568        // Convert bytes back to the target type
569        for (i, chunk) in bytes.chunks_exact(size).enumerate() {
570            if i < dest.len() {
571                // This is safe because we're copying the exact number of bytes
572                // that the type occupies in memory
573                unsafe {
574                    let ptr = dest.as_mut_ptr().add(i).cast::<u8>();
575                    core::ptr::copy_nonoverlapping(chunk.as_ptr(), ptr, size);
576                }
577            }
578        }
579    }
580}
581
582// LibQRng implements rand_core::Rng and TryCryptoRng, so CryptoRng and Rng
583// are provided by rand_core blanket impls. The signature crate uses rand_core
584// and will see these implementations when using the same rand_core version.
585
586#[cfg(feature = "alloc")]
587impl fmt::Display for LibQRng {
588    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
589        write!(
590            f,
591            "LibQRng(security_level: {}, entropy_source: {}, deterministic: {}, reseed_counter: {})",
592            self.security_level,
593            self.entropy_source.name(),
594            self.deterministic,
595            self.reseed_counter
596        )
597    }
598}
599
600/// RNG provider factory
601///
602/// This factory provides convenient methods for creating RNG instances
603/// with different characteristics and configurations.
604pub struct LibQRngProvider;
605
606impl LibQRngProvider {
607    /// Create a new RNG provider
608    pub fn new() -> Self {
609        Self
610    }
611}
612
613#[cfg(feature = "alloc")]
614impl RngProvider for LibQRngProvider {
615    fn create_rng(&self, config: &RngConfig) -> Result<Box<dyn SecureRng>> {
616        let rng = LibQRng::with_config(config)?;
617        Ok(Box::new(rng))
618    }
619
620    fn name(&self) -> &'static str {
621        "libQ RNG Provider"
622    }
623
624    fn capabilities(&self) -> ProviderCapabilities {
625        ProviderCapabilities {
626            secure: true,
627            deterministic: true,
628            hardware: true,
629            reseeding: true,
630            custom_entropy: true,
631            no_std: true,
632            wasm: true,
633        }
634    }
635
636    fn supports_config(&self, config: &RngConfig) -> bool {
637        // We support all configurations
638        let _ = config;
639        true
640    }
641
642    fn priority(&self) -> u32 {
643        100 // High priority as the main provider
644    }
645}
646
647impl Default for LibQRngProvider {
648    fn default() -> Self {
649        Self::new()
650    }
651}
652
653#[cfg(test)]
654mod tests {
655    #[cfg(all(not(feature = "std"), feature = "alloc"))]
656    use alloc::format;
657
658    #[cfg(feature = "alloc")]
659    use rand_core::Rng;
660
661    #[cfg(feature = "alloc")]
662    use super::*;
663
664    #[test]
665    #[cfg(feature = "alloc")]
666    fn test_libq_rng_deterministic_creation() {
667        let mut seed = [0u8; 32];
668        seed[..8].copy_from_slice(&[1, 2, 3, 4, 5, 6, 7, 8]);
669        let rng = LibQRng::new_deterministic(seed);
670        assert!(rng.is_deterministic());
671        assert_eq!(rng.security_level(), SecurityLevel::Deterministic);
672        assert!(!rng.is_secure());
673    }
674
675    #[test]
676    #[cfg(feature = "alloc")]
677    fn test_libq_rng_deterministic_consistency() {
678        let seed = [42u8; 32];
679        let mut rng1 = LibQRng::new_deterministic(seed);
680        let mut rng2 = LibQRng::new_deterministic(seed);
681
682        let mut bytes1 = [0u8; 32];
683        let mut bytes2 = [0u8; 32];
684
685        rng1.fill_bytes(&mut bytes1);
686        rng2.fill_bytes(&mut bytes2);
687
688        assert_eq!(bytes1, bytes2);
689    }
690
691    #[test]
692    #[cfg(feature = "alloc")]
693    fn test_libq_rng_deterministic_golden_zero_seed() {
694        use crate::kt128_expander::Kt128Expander;
695
696        let expected = crate::kt128_expander::KT128_DET_GOLDEN_ZERO_SEED_64;
697        let mut rng = LibQRng::new_deterministic([0u8; 32]);
698        let mut out = [0u8; 64];
699        rng.fill_bytes(&mut out);
700        let mut direct = Kt128Expander::from_det_seed_32([0u8; 32]);
701        let mut expected_direct = [0u8; 64];
702        direct.fill_bytes(&mut expected_direct);
703        assert_eq!(out, expected);
704        assert_eq!(out, expected_direct);
705    }
706
707    /// Regression: deterministic RNG must use the full 256-bit seed (KT128), not a
708    /// collapsed 64-bit state where distant seed bytes could be ignored.
709    #[test]
710    #[cfg(feature = "alloc")]
711    fn test_libq_rng_deterministic_seeds_differ_in_final_byte_yield_different_streams() {
712        let seed_a = [0u8; 32];
713        let mut seed_b = [0u8; 32];
714        seed_b[31] = 1;
715
716        let mut rng_a = LibQRng::new_deterministic(seed_a);
717        let mut rng_b = LibQRng::new_deterministic(seed_b);
718
719        let mut out_a = [0u8; 64];
720        let mut out_b = [0u8; 64];
721        rng_a.fill_bytes(&mut out_a);
722        rng_b.fill_bytes(&mut out_b);
723
724        assert_ne!(
725            out_a, out_b,
726            "KT128 streams from different 32-byte keys must diverge immediately"
727        );
728    }
729
730    #[test]
731    #[cfg(feature = "alloc")]
732    fn test_libq_rng_custom_creation() {
733        let entropy_data = vec![1, 2, 3, 4, 5, 6, 7, 8];
734        let entropy_source = crate::entropy::UserEntropySource::new(entropy_data);
735        let rng = LibQRng::new_custom(entropy_source);
736        assert!(!rng.is_deterministic());
737        assert_eq!(rng.security_level(), SecurityLevel::CryptographicallySecure);
738    }
739
740    #[test]
741    #[cfg(feature = "alloc")]
742    fn test_libq_rng_config_creation() {
743        let config = RngConfig::default();
744        let rng = LibQRng::with_config(&config);
745        assert!(rng.is_ok());
746    }
747
748    #[test]
749    #[cfg(feature = "alloc")]
750    fn test_libq_rng_provider_creation() {
751        let provider = LibQRngProvider::new();
752        assert_eq!(provider.name(), "libQ RNG Provider");
753        assert_eq!(provider.priority(), 100);
754    }
755
756    #[test]
757    #[cfg(feature = "alloc")]
758    fn test_libq_rng_provider_capabilities() {
759        let provider = LibQRngProvider::new();
760        let caps = provider.capabilities();
761        assert!(caps.secure);
762        assert!(caps.deterministic);
763        assert!(caps.hardware);
764        assert!(caps.reseeding);
765        assert!(caps.custom_entropy);
766        assert!(caps.no_std);
767        assert!(caps.wasm);
768    }
769
770    #[test]
771    #[cfg(feature = "alloc")]
772    fn test_libq_rng_provider_create_rng() {
773        let provider = LibQRngProvider::new();
774        let config = RngConfig::default();
775        let rng = provider.create_rng(&config);
776        assert!(rng.is_ok());
777    }
778
779    #[test]
780    #[cfg(feature = "alloc")]
781    fn test_libq_rng_reseed_counter() {
782        let mut seed = [0u8; 32];
783        seed[..4].copy_from_slice(&[1, 2, 3, 4]);
784        let rng = LibQRng::new_deterministic(seed);
785        assert_eq!(rng.reseed_counter(), 0);
786        assert_eq!(rng.bytes_generated(), 0);
787    }
788
789    #[test]
790    #[cfg(feature = "alloc")]
791    fn test_libq_rng_entropy_source_info() {
792        let mut seed = [0u8; 32];
793        seed[..4].copy_from_slice(&[1, 2, 3, 4]);
794        let rng = LibQRng::new_deterministic(seed);
795        assert!(!rng.entropy_source_name().is_empty());
796        assert_eq!(
797            rng.entropy_source_type(),
798            crate::traits::EntropySourceType::Deterministic
799        );
800    }
801
802    #[test]
803    #[cfg(feature = "alloc")]
804    fn test_libq_rng_display() {
805        let mut seed = [0u8; 32];
806        seed[..4].copy_from_slice(&[1, 2, 3, 4]);
807        let rng = LibQRng::new_deterministic(seed);
808        let display = format!("{rng}");
809        assert!(display.contains("LibQRng"));
810        assert!(display.contains("Deterministic"));
811    }
812}