Skip to main content

lib_q_random/
lib.rs

1#![allow(
2    clippy::uninlined_format_args,
3    clippy::must_use_candidate,
4    clippy::cast_precision_loss,
5    clippy::cast_lossless,
6    clippy::manual_clamp,
7    clippy::unused_self,
8    clippy::unnecessary_wraps,
9    clippy::struct_excessive_bools,
10    clippy::doc_markdown,
11    clippy::too_many_lines,
12    clippy::similar_names
13)]
14// Unit tests and test-only modules use unwrap/expect/println! for brevity; production `--lib` checks
15// still enforce these lints because `cfg(test)` is off for that build.
16#![cfg_attr(
17    test,
18    allow(
19        clippy::unwrap_used,
20        clippy::expect_used,
21        clippy::panic,
22        clippy::print_stdout,
23        clippy::print_stderr
24    )
25)]
26
27//! # lib-q-random: Secure Random Number Generation for libQ
28//!
29//! This crate provides a comprehensive, secure random number generation system
30//! designed specifically for post-quantum cryptography applications in the libQ ecosystem.
31//!
32//! ## Features
33//!
34//! - **Cryptographically Secure**: Uses OS entropy sources; on x86 / `x86_64`
35//!   with `std`, optional **RDRAND** is available through the hardware entropy source
36//! - **Multiple Providers**: Support for OS, deterministic, and hardware entropy sources
37//! - **Entropy Validation**: Comprehensive entropy quality assessment and validation
38//! - **`no_std` Support**: Works in constrained environments without standard library
39//! - **WASM Compatible**: Full support for `WebAssembly` and browser environments
40//! - **Zero-Copy**: Efficient memory usage with minimal allocations
41//! - **Thread-Safe**: Safe for use in multi-threaded environments
42//! - **Extensible**: Plugin architecture for custom entropy sources
43//! - **Custom Entropy Sources**: Secure callback-based system for plugging in custom entropy sources in `no_std` and WASM environments
44//!
45//! ## Quick Start
46//!
47//! ### With std/alloc features
48//! ```rust
49//! #[cfg(feature = "alloc")]
50//! {
51//!     use lib_q_random::{
52//!         EntropySource,
53//!         LibQRng,
54//!         RngProvider,
55//!     };
56//!     use rand_core::Rng;
57//!
58//!     // Create a secure RNG for production use
59//!     let mut rng = LibQRng::new_secure().unwrap();
60//!
61//!     // Generate random bytes
62//!     let mut bytes = [0u8; 32];
63//!     rng.fill_bytes(&mut bytes);
64//!
65//!     // Create a deterministic RNG for testing (32-byte KT128 seed)
66//!     let mut test_rng = LibQRng::new_deterministic([1; 32]);
67//! }
68//! ```
69//!
70//! ### Custom Entropy Sources (`no_std`/WASM)
71//! ```rust
72//! #[cfg(feature = "custom-entropy")]
73//! {
74//!     use lib_q_random::{
75//!         custom_entropy::{CustomEntropySource, EntropyContext, EntropyQuality, CustomEntropyConfig},
76//!         register_custom_entropy_source, unregister_custom_entropy_source,
77//!         new_secure_rng
78//!     };
79//!     use rand_core::Rng;
80//!
81//!     // Define your custom entropy callback
82//!     unsafe extern "C" fn my_entropy_callback(dest: *mut u8, len: usize, _context: *mut u8) -> i32 {
83//!         // Fill dest with len bytes of entropy from your source
84//!         for i in 0..len {
85//!             unsafe {
86//!                 *dest.add(i) = (i as u8).wrapping_add(42); // Example entropy source
87//!             }
88//!         }
89//!         0
90//!     }
91//!
92//!     // Create and register the custom entropy source
93//!     let context = EntropyContext::empty();
94//!     let config = CustomEntropyConfig::default();
95//!     let source = CustomEntropySource {
96//!         callback: my_entropy_callback,
97//!         context,
98//!         quality: EntropyQuality::Hardware,
99//!         config,
100//!         source_id: "my_hardware_rng",
101//!     };
102//!
103//!     // Register the source (must remain valid for the lifetime of usage)
104//!     unsafe {
105//!         register_custom_entropy_source(&source);
106//!     }
107//!
108//!     // Now create RNGs that will use your custom entropy source
109//!     let mut rng = new_secure_rng().unwrap();
110//!     let mut bytes = [0u8; 32];
111//!     rng.fill_bytes(&mut bytes);
112//!
113//!     // Clean up when done
114//!     unregister_custom_entropy_source();
115//! }
116//! ```
117//!
118//! ### With `no_std` features
119//! ```rust,no_run
120//! #[cfg(not(feature = "alloc"))]
121//! {
122//!     use lib_q_random::{
123//!         new_deterministic_rng_no_std,
124//!         new_secure_rng_no_std,
125//!     };
126//!     use rand_core::Rng;
127//!
128//!     // Create a secure RNG for production use
129//!     let mut rng = new_secure_rng_no_std().unwrap();
130//!
131//!     // Generate random bytes
132//!     let mut bytes = [0u8; 32];
133//!     rng.fill_bytes(&mut bytes);
134//!
135//!     // Create a deterministic RNG for testing
136//!     let mut test_rng = new_deterministic_rng_no_std([1; 32]);
137//! }
138//! ```
139//!
140//! ## Architecture
141//!
142//! The crate is organized into several key components:
143//!
144//! - **Core Traits**: Define the interface for RNG providers and entropy sources
145//! - **Providers**: Implement different RNG strategies (OS, deterministic, hardware)
146//! - **Validation**: Entropy quality assessment and security validation
147//! - **Factory**: RNG creation and configuration management
148//!
149//! ## Security Considerations
150//!
151//! - **Production randomness** comes from OS or registered hardware/custom entropy sources;
152//!   APIs return an error when secure entropy is unavailable instead of substituting weak RNGs.
153//! - **Deterministic constructors** (`LibQRng::new_deterministic`, `NoStdRng::new_deterministic`,
154//!   `new_deterministic_rng`, etc.) use **KT128** (`KangarooTwelve`) XOF expansion keyed by the caller’s
155//!   32-byte seed (or SplitMix64-expanded `u64` for `*_from_u64`).
156//!   They are for tests, KATs, and reproducible benchmarks: treat the seed like a symmetric key;
157//!   if it is guessable, the entire stream is guessable.
158//! - Entropy validation applies to non-deterministic sources.
159//! - Secure memory clearing and constant-time expectations are documented per algorithm crate.
160
161#![cfg_attr(not(feature = "std"), no_std)]
162#![warn(missing_docs, clippy::all, clippy::pedantic)]
163#![allow(clippy::module_name_repetitions)]
164
165// Heap-backed APIs (`Vec`, `Box`, …) require explicit `alloc` (see Cargo features). With `cdylib`,
166// standalone `cargo check` of this crate uses the `no_std` feature, which enables `no_std_panic_handler`.
167#[cfg(feature = "alloc")]
168extern crate alloc;
169
170#[cfg(all(not(feature = "std"), feature = "no_std_panic_handler"))]
171mod no_std_panic_handler {
172    use core::panic::PanicInfo;
173
174    #[panic_handler]
175    #[allow(clippy::empty_loop)]
176    fn panic(_info: &PanicInfo) -> ! {
177        loop {}
178    }
179}
180
181mod hardware_rng;
182pub mod kt128_expander;
183
184#[cfg(feature = "deterministic-saturnin")]
185pub mod saturnin_det;
186
187// Core modules
188pub mod entropy;
189pub mod error;
190pub mod provider;
191pub mod traits;
192pub mod validation;
193
194#[cfg(feature = "wasm")]
195mod wasm;
196
197// Specialized RNG implementations for different algorithms
198pub mod specialized;
199
200// no_std RNG implementation
201#[cfg(any(not(feature = "std"), feature = "no_std"))]
202pub mod no_std_rng;
203
204// Deterministic RNG for STARK/ZKP use
205pub mod deterministic_rng;
206pub use deterministic_rng::DeterministicRng;
207pub use kt128_expander::{
208    DOMAIN_HPKE_RNG,
209    DOMAIN_LIBQ_DET_RNG,
210    KT128_DET_GOLDEN_U64_SEED_64,
211    KT128_DET_GOLDEN_ZERO_SEED_64,
212    Kt128Expander,
213};
214
215// Custom entropy source system for no_std/WASM environments
216#[cfg(feature = "custom-entropy")]
217pub mod custom_entropy;
218
219// Re-export main types
220pub use error::{
221    Error,
222    Result,
223};
224#[cfg(feature = "alloc")]
225pub use provider::LibQRng;
226// Re-export specialized implementations
227#[cfg(feature = "classical-mceliece")]
228pub use specialized::ClassicalMcElieceRng;
229#[cfg(feature = "fn-dsa")]
230pub use specialized::FnDsaRng;
231#[cfg(all(feature = "hpke", feature = "hash"))]
232pub use specialized::Kt128Rng;
233#[cfg(feature = "alloc")]
234pub use traits::RngProvider;
235pub use traits::{
236    EntropySource,
237    SecureRng,
238    SecurityLevel,
239};
240// Re-export validation types
241pub use validation::{
242    EntropyQuality,
243    EntropyValidator,
244};
245
246/// Version information
247pub const VERSION: &str = env!("CARGO_PKG_VERSION");
248
249/// Minimum entropy bits required for cryptographic operations
250pub const MIN_ENTROPY_BITS: usize = 128;
251
252/// Maximum entropy bits for validation
253pub const MAX_ENTROPY_BITS: usize = 4096;
254
255/// Default entropy buffer size
256pub const DEFAULT_ENTROPY_SIZE: usize = 32;
257
258/// Fill `dest` with cryptographically secure bytes from the OS entropy source.
259///
260/// Requires the `getrandom` feature. Returns `Err` if that feature is absent
261/// rather than silently producing weak output.
262///
263/// # Errors
264///
265/// Returns [`Error::FeatureNotAvailable`] when the `getrandom` feature is not
266/// enabled. Returns [`Error::EntropySourceUnavailable`] when getrandom fails.
267pub fn fill_entropy(dest: &mut [u8]) -> Result<()> {
268    #[cfg(feature = "getrandom")]
269    {
270        getrandom::fill(dest).map_err(|_| Error::EntropySourceUnavailable {
271            source: "system",
272            context: Some("getrandom failed"),
273        })
274    }
275    #[cfg(not(feature = "getrandom"))]
276    {
277        let _ = dest;
278        Err(Error::FeatureNotAvailable {
279            feature: "secure entropy",
280            required_features: &["getrandom"],
281        })
282    }
283}
284
285/// Create a new secure RNG instance
286///
287/// This function creates a cryptographically secure RNG using the best available
288/// entropy source for the current platform.
289///
290/// # Errors
291///
292/// Returns an error if no secure entropy source is available.
293///
294/// # Examples
295///
296/// ```rust
297/// use lib_q_random::new_secure_rng;
298/// use rand_core::Rng;
299///
300/// let mut rng = new_secure_rng().unwrap();
301/// let mut bytes = [0u8; 32];
302/// rng.fill_bytes(&mut bytes);
303/// ```
304#[cfg(feature = "alloc")]
305pub fn new_secure_rng() -> Result<LibQRng> {
306    LibQRng::new_secure()
307}
308
309/// Create a new secure RNG instance for `no_std` environments
310///
311/// This function creates a cryptographically secure RNG that works in `no_std`
312/// environments using getrandom for entropy.
313///
314/// # Errors
315///
316/// Returns an error if getrandom is not available or fails to initialize.
317///
318/// # Examples
319///
320/// ```rust,no_run
321/// use lib_q_random::new_secure_rng_no_std;
322/// use rand_core::Rng;
323///
324/// let mut rng = new_secure_rng_no_std().unwrap();
325/// let mut bytes = [0u8; 32];
326/// rng.fill_bytes(&mut bytes);
327/// ```
328#[cfg(not(feature = "alloc"))]
329pub fn new_secure_rng_no_std() -> Result<no_std_rng::NoStdRng> {
330    no_std_rng::NoStdRng::new()
331}
332
333/// Create a new deterministic RNG instance
334///
335/// This function creates a deterministic RNG suitable for testing and
336/// reproducible operations. Output is a **KT128** XOF stream from `seed`.
337/// **Unpredictability is only as strong as the seed**; use [`new_secure_rng`]
338/// for production cryptography.
339///
340/// # Arguments
341///
342/// * `seed` - 32-byte seed for KT128 expansion
343///
344/// # Examples
345///
346/// ```rust
347/// use lib_q_random::new_deterministic_rng;
348/// use rand_core::Rng;
349///
350/// let mut rng = new_deterministic_rng([1; 32]);
351/// let mut bytes = [0u8; 32];
352/// rng.fill_bytes(&mut bytes);
353/// ```
354#[cfg(feature = "alloc")]
355#[must_use]
356pub fn new_deterministic_rng(seed: [u8; 32]) -> LibQRng {
357    LibQRng::new_deterministic(seed)
358}
359
360/// Create a deterministic RNG from a `u64` test seed (`SplitMix64` → KT128).
361#[cfg(feature = "alloc")]
362#[must_use]
363pub fn new_deterministic_rng_from_u64(seed: u64) -> LibQRng {
364    LibQRng::new_deterministic_from_u64(seed)
365}
366
367/// Create a new deterministic RNG instance for `no_std` environments
368///
369/// This function creates a deterministic RNG suitable for testing and
370/// reproducible operations in `no_std` environments. KT128 stream from `seed`;
371/// **not** unpredictable unless the seed is secret and high-entropy.
372///
373/// # Arguments
374///
375/// * `seed` - 32-byte seed
376///
377/// # Examples
378///
379/// ```rust,no_run
380/// use lib_q_random::new_deterministic_rng_no_std;
381/// use rand_core::Rng;
382///
383/// let mut rng = new_deterministic_rng_no_std([1; 32]);
384/// let mut bytes = [0u8; 32];
385/// rng.fill_bytes(&mut bytes);
386/// ```
387#[cfg(not(feature = "alloc"))]
388#[must_use]
389pub fn new_deterministic_rng_no_std(seed: [u8; 32]) -> no_std_rng::NoStdRng {
390    no_std_rng::NoStdRng::new_deterministic(seed)
391}
392
393/// Create a deterministic `no_std` RNG from a `u64` test seed (`SplitMix64` → KT128).
394#[cfg(not(feature = "alloc"))]
395#[must_use]
396pub fn new_deterministic_rng_no_std_from_u64(seed: u64) -> no_std_rng::NoStdRng {
397    no_std_rng::NoStdRng::new_deterministic_from_u64(seed)
398}
399
400/// Register a custom entropy source for the current thread
401///
402/// This function allows developers to register a custom entropy source that will
403/// be used by `NoStdRng` when generating random bytes. The entropy source must
404/// remain valid for the lifetime of the registration.
405///
406/// # Arguments
407///
408/// * `source` - The custom entropy source to register
409///
410/// # Safety
411///
412/// The `source` must remain valid for the lifetime of the registration.
413/// The caller is responsible for ensuring the source is not dropped
414/// while registered.
415///
416/// # Examples
417///
418/// ```rust,no_run
419/// use lib_q_random::custom_entropy::{
420///     CustomEntropyConfig,
421///     CustomEntropySource,
422///     EntropyContext,
423///     EntropyQuality,
424/// };
425/// use lib_q_random::{
426///     register_custom_entropy_source,
427///     unregister_custom_entropy_source,
428/// };
429/// use rand_core::Rng;
430///
431/// // Define a custom entropy callback
432/// unsafe extern "C" fn my_entropy_callback(
433///     dest: *mut u8,
434///     len: usize,
435///     _context: *mut u8,
436/// ) -> i32 {
437///     // Fill dest with len bytes of entropy
438///     // Return 0 on success, non-zero on failure
439///     0
440/// }
441///
442/// // Create and register the entropy source
443/// let context = EntropyContext::empty();
444/// let config = CustomEntropyConfig::default();
445/// let source = unsafe {
446///     CustomEntropySource::new(
447///         my_entropy_callback,
448///         context,
449///         EntropyQuality::User,
450///         config,
451///         "my_custom_source",
452///     )
453/// };
454///
455/// unsafe {
456///     register_custom_entropy_source(&source);
457/// }
458///
459/// // Now NoStdRng will use the custom entropy source
460/// // (In no_std environments, use new_secure_rng_no_std())
461/// // let mut rng = new_secure_rng_no_std().unwrap();
462/// // let mut bytes = [0u8; 32];
463/// // rng.fill_bytes(&mut bytes);
464///
465/// // Clean up
466/// unregister_custom_entropy_source();
467/// ```
468#[cfg(feature = "custom-entropy")]
469pub unsafe fn register_custom_entropy_source(source: *const custom_entropy::CustomEntropySource) {
470    unsafe { custom_entropy::register_custom_entropy_source(source) };
471}
472
473/// Unregister the current custom entropy source
474///
475/// This function removes the currently registered custom entropy source,
476/// causing `NoStdRng` to fall back to the default entropy source (getrandom).
477#[cfg(feature = "custom-entropy")]
478pub fn unregister_custom_entropy_source() {
479    custom_entropy::unregister_custom_entropy_source();
480}
481
482/// Check if a custom entropy source is currently registered
483///
484/// # Returns
485///
486/// Returns `true` if a custom entropy source is registered, `false` otherwise.
487#[cfg(feature = "custom-entropy")]
488#[must_use]
489pub fn has_custom_entropy_source() -> bool {
490    custom_entropy::has_custom_entropy_source()
491}
492
493/// Get information about the currently registered entropy source
494///
495/// # Returns
496///
497/// Returns a tuple of (`source_id`, quality) if a source is registered.
498#[cfg(feature = "custom-entropy")]
499#[must_use]
500pub fn get_custom_entropy_source_info() -> Option<(&'static str, custom_entropy::EntropyQuality)> {
501    custom_entropy::get_entropy_source_info()
502}
503
504/// Create a new RNG with custom entropy source
505///
506/// This function allows creating an RNG with a custom entropy source,
507/// useful for specialized applications or testing.
508///
509/// # Arguments
510///
511/// * `entropy_source` - Custom entropy source implementation
512///
513/// # Examples
514///
515/// ```rust
516/// use lib_q_random::{
517///     EntropySource,
518///     new_custom_rng,
519/// };
520/// use rand_core::Rng;
521///
522/// struct MyEntropySource;
523/// impl EntropySource for MyEntropySource {
524///     fn get_entropy(
525///         &mut self,
526///         dest: &mut [u8],
527///     ) -> Result<(), lib_q_random::Error> {
528///         // Implementation details...
529///         Ok(())
530///     }
531/// }
532///
533/// let mut rng = new_custom_rng(MyEntropySource);
534/// ```
535#[cfg(feature = "alloc")]
536pub fn new_custom_rng<T: EntropySource + 'static>(entropy_source: T) -> LibQRng {
537    LibQRng::new_custom(entropy_source)
538}
539
540#[cfg(test)]
541mod tests {
542    #[cfg(not(feature = "alloc"))]
543    use rand_core::Rng;
544
545    use super::*;
546
547    #[test]
548    fn test_version_constant() {
549        // VERSION is a non-empty string constant
550        #[allow(clippy::const_is_empty)]
551        {
552            assert!(!VERSION.is_empty());
553        }
554    }
555
556    #[test]
557    fn test_constants() {
558        // Test that constants have reasonable values
559        #[allow(clippy::assertions_on_constants)]
560        {
561            assert!(MIN_ENTROPY_BITS >= 128);
562            assert!(MAX_ENTROPY_BITS > MIN_ENTROPY_BITS);
563            assert!(DEFAULT_ENTROPY_SIZE > 0);
564        }
565    }
566
567    #[test]
568    #[cfg(not(feature = "alloc"))]
569    fn test_deterministic_rng_creation() {
570        let mut seed = [0u8; 32];
571        seed[..8].copy_from_slice(&[1, 2, 3, 4, 5, 6, 7, 8]);
572        let rng = new_deterministic_rng_no_std(seed);
573        assert!(rng.is_deterministic());
574    }
575
576    #[test]
577    #[cfg(not(feature = "alloc"))]
578    fn test_deterministic_rng_consistency() {
579        let seed = [42u8; 32];
580        let mut rng1 = new_deterministic_rng_no_std(seed);
581        let mut rng2 = new_deterministic_rng_no_std(seed);
582
583        let mut bytes1 = [0u8; 32];
584        let mut bytes2 = [0u8; 32];
585
586        rng1.fill_bytes(&mut bytes1);
587        rng2.fill_bytes(&mut bytes2);
588
589        assert_eq!(bytes1, bytes2);
590    }
591}