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