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 `ChaCha20` 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 a `ChaCha20` stream keyed only by the caller’s 32-byte seed.
155//!   They are for tests, KATs, and reproducible benchmarks: treat the seed like a symmetric key;
156//!   if it is guessable, the entire stream is guessable.
157//! - Entropy validation applies to non-deterministic sources.
158//! - Secure memory clearing and constant-time expectations are documented per algorithm crate.
159
160#![cfg_attr(not(feature = "std"), no_std)]
161#![warn(missing_docs, clippy::all, clippy::pedantic)]
162#![allow(clippy::module_name_repetitions)]
163
164// Heap-backed APIs (`Vec`, `Box`, …) require explicit `alloc` (see Cargo features). With `cdylib`,
165// standalone `cargo check` of this crate uses the `no_std` feature, which enables `no_std_panic_handler`.
166#[cfg(feature = "alloc")]
167extern crate alloc;
168
169#[cfg(all(not(feature = "std"), feature = "no_std_panic_handler"))]
170mod no_std_panic_handler {
171    use core::panic::PanicInfo;
172
173    #[panic_handler]
174    #[allow(clippy::empty_loop)]
175    fn panic(_info: &PanicInfo) -> ! {
176        loop {}
177    }
178}
179
180mod hardware_rng;
181
182// Core modules
183pub mod entropy;
184pub mod error;
185pub mod provider;
186pub mod traits;
187pub mod validation;
188
189#[cfg(feature = "wasm")]
190mod wasm;
191
192// Specialized RNG implementations for different algorithms
193pub mod specialized;
194
195// no_std RNG implementation
196#[cfg(any(not(feature = "std"), feature = "no_std"))]
197pub mod no_std_rng;
198
199// Deterministic RNG for STARK/ZKP use
200pub mod deterministic_rng;
201pub use deterministic_rng::DeterministicRng;
202
203// Custom entropy source system for no_std/WASM environments
204#[cfg(feature = "custom-entropy")]
205pub mod custom_entropy;
206
207// Re-export main types
208pub use error::{
209    Error,
210    Result,
211};
212#[cfg(feature = "alloc")]
213pub use provider::LibQRng;
214// Re-export specialized implementations
215#[cfg(feature = "classical-mceliece")]
216pub use specialized::ClassicalMcElieceRng;
217#[cfg(feature = "fn-dsa")]
218pub use specialized::FnDsaRng;
219#[cfg(feature = "hpke")]
220pub use specialized::Kt128Rng;
221#[cfg(feature = "alloc")]
222pub use traits::RngProvider;
223pub use traits::{
224    EntropySource,
225    SecureRng,
226    SecurityLevel,
227};
228// Re-export validation types
229pub use validation::{
230    EntropyQuality,
231    EntropyValidator,
232};
233
234/// Version information
235pub const VERSION: &str = env!("CARGO_PKG_VERSION");
236
237/// Minimum entropy bits required for cryptographic operations
238pub const MIN_ENTROPY_BITS: usize = 128;
239
240/// Maximum entropy bits for validation
241pub const MAX_ENTROPY_BITS: usize = 4096;
242
243/// Default entropy buffer size
244pub const DEFAULT_ENTROPY_SIZE: usize = 32;
245
246/// Fill `dest` with cryptographically secure bytes from the OS entropy source.
247///
248/// Requires the `getrandom` feature. Returns `Err` if that feature is absent
249/// rather than silently producing weak output.
250///
251/// # Errors
252///
253/// Returns [`Error::FeatureNotAvailable`] when the `getrandom` feature is not
254/// enabled. Returns [`Error::EntropySourceUnavailable`] when getrandom fails.
255pub fn fill_entropy(dest: &mut [u8]) -> Result<()> {
256    #[cfg(feature = "getrandom")]
257    {
258        getrandom::fill(dest).map_err(|_| Error::EntropySourceUnavailable {
259            source: "system",
260            context: Some("getrandom failed"),
261        })
262    }
263    #[cfg(not(feature = "getrandom"))]
264    {
265        let _ = dest;
266        Err(Error::FeatureNotAvailable {
267            feature: "secure entropy",
268            required_features: &["getrandom"],
269        })
270    }
271}
272
273/// Create a new secure RNG instance
274///
275/// This function creates a cryptographically secure RNG using the best available
276/// entropy source for the current platform.
277///
278/// # Errors
279///
280/// Returns an error if no secure entropy source is available.
281///
282/// # Examples
283///
284/// ```rust
285/// use lib_q_random::new_secure_rng;
286/// use rand_core::Rng;
287///
288/// let mut rng = new_secure_rng().unwrap();
289/// let mut bytes = [0u8; 32];
290/// rng.fill_bytes(&mut bytes);
291/// ```
292#[cfg(feature = "alloc")]
293pub fn new_secure_rng() -> Result<LibQRng> {
294    LibQRng::new_secure()
295}
296
297/// Create a new secure RNG instance for `no_std` environments
298///
299/// This function creates a cryptographically secure RNG that works in `no_std`
300/// environments using getrandom for entropy.
301///
302/// # Errors
303///
304/// Returns an error if getrandom is not available or fails to initialize.
305///
306/// # Examples
307///
308/// ```rust,no_run
309/// use lib_q_random::new_secure_rng_no_std;
310/// use rand_core::Rng;
311///
312/// let mut rng = new_secure_rng_no_std().unwrap();
313/// let mut bytes = [0u8; 32];
314/// rng.fill_bytes(&mut bytes);
315/// ```
316#[cfg(not(feature = "alloc"))]
317pub fn new_secure_rng_no_std() -> Result<no_std_rng::NoStdRng> {
318    no_std_rng::NoStdRng::new()
319}
320
321/// Create a new deterministic RNG instance
322///
323/// This function creates a deterministic RNG suitable for testing and
324/// reproducible operations. Output is a `ChaCha20` stream from `seed`.
325/// **Unpredictability is only as strong as the seed**; use [`new_secure_rng`]
326/// for production cryptography.
327///
328/// # Arguments
329///
330/// * `seed` - 32-byte `ChaCha20` key (same interpretation as `ChaCha20Rng::from_seed`)
331///
332/// # Examples
333///
334/// ```rust
335/// use lib_q_random::new_deterministic_rng;
336/// use rand_core::Rng;
337///
338/// let mut rng = new_deterministic_rng([1; 32]);
339/// let mut bytes = [0u8; 32];
340/// rng.fill_bytes(&mut bytes);
341/// ```
342#[cfg(feature = "alloc")]
343#[must_use]
344pub fn new_deterministic_rng(seed: [u8; 32]) -> LibQRng {
345    LibQRng::new_deterministic(seed)
346}
347
348/// Create a new deterministic RNG instance for `no_std` environments
349///
350/// This function creates a deterministic RNG suitable for testing and
351/// reproducible operations in `no_std` environments. `ChaCha20` stream from `seed`;
352/// **not** unpredictable unless the seed is secret and high-entropy.
353///
354/// # Arguments
355///
356/// * `seed` - 32-byte `ChaCha20` key
357///
358/// # Examples
359///
360/// ```rust,no_run
361/// use lib_q_random::new_deterministic_rng_no_std;
362/// use rand_core::Rng;
363///
364/// let mut rng = new_deterministic_rng_no_std([1; 32]);
365/// let mut bytes = [0u8; 32];
366/// rng.fill_bytes(&mut bytes);
367/// ```
368#[cfg(not(feature = "alloc"))]
369#[must_use]
370pub fn new_deterministic_rng_no_std(seed: [u8; 32]) -> no_std_rng::NoStdRng {
371    no_std_rng::NoStdRng::new_deterministic(seed)
372}
373
374/// Register a custom entropy source for the current thread
375///
376/// This function allows developers to register a custom entropy source that will
377/// be used by `NoStdRng` when generating random bytes. The entropy source must
378/// remain valid for the lifetime of the registration.
379///
380/// # Arguments
381///
382/// * `source` - The custom entropy source to register
383///
384/// # Safety
385///
386/// The `source` must remain valid for the lifetime of the registration.
387/// The caller is responsible for ensuring the source is not dropped
388/// while registered.
389///
390/// # Examples
391///
392/// ```rust,no_run
393/// use lib_q_random::custom_entropy::{
394///     CustomEntropyConfig,
395///     CustomEntropySource,
396///     EntropyContext,
397///     EntropyQuality,
398/// };
399/// use lib_q_random::{
400///     register_custom_entropy_source,
401///     unregister_custom_entropy_source,
402/// };
403/// use rand_core::Rng;
404///
405/// // Define a custom entropy callback
406/// unsafe extern "C" fn my_entropy_callback(
407///     dest: *mut u8,
408///     len: usize,
409///     _context: *mut u8,
410/// ) -> i32 {
411///     // Fill dest with len bytes of entropy
412///     // Return 0 on success, non-zero on failure
413///     0
414/// }
415///
416/// // Create and register the entropy source
417/// let context = EntropyContext::empty();
418/// let config = CustomEntropyConfig::default();
419/// let source = unsafe {
420///     CustomEntropySource::new(
421///         my_entropy_callback,
422///         context,
423///         EntropyQuality::User,
424///         config,
425///         "my_custom_source",
426///     )
427/// };
428///
429/// unsafe {
430///     register_custom_entropy_source(&source);
431/// }
432///
433/// // Now NoStdRng will use the custom entropy source
434/// // (In no_std environments, use new_secure_rng_no_std())
435/// // let mut rng = new_secure_rng_no_std().unwrap();
436/// // let mut bytes = [0u8; 32];
437/// // rng.fill_bytes(&mut bytes);
438///
439/// // Clean up
440/// unregister_custom_entropy_source();
441/// ```
442#[cfg(feature = "custom-entropy")]
443pub unsafe fn register_custom_entropy_source(source: *const custom_entropy::CustomEntropySource) {
444    unsafe { custom_entropy::register_custom_entropy_source(source) };
445}
446
447/// Unregister the current custom entropy source
448///
449/// This function removes the currently registered custom entropy source,
450/// causing `NoStdRng` to fall back to the default entropy source (getrandom).
451#[cfg(feature = "custom-entropy")]
452pub fn unregister_custom_entropy_source() {
453    custom_entropy::unregister_custom_entropy_source();
454}
455
456/// Check if a custom entropy source is currently registered
457///
458/// # Returns
459///
460/// Returns `true` if a custom entropy source is registered, `false` otherwise.
461#[cfg(feature = "custom-entropy")]
462#[must_use]
463pub fn has_custom_entropy_source() -> bool {
464    custom_entropy::has_custom_entropy_source()
465}
466
467/// Get information about the currently registered entropy source
468///
469/// # Returns
470///
471/// Returns a tuple of (`source_id`, quality) if a source is registered.
472#[cfg(feature = "custom-entropy")]
473#[must_use]
474pub fn get_custom_entropy_source_info() -> Option<(&'static str, custom_entropy::EntropyQuality)> {
475    custom_entropy::get_entropy_source_info()
476}
477
478/// Create a new RNG with custom entropy source
479///
480/// This function allows creating an RNG with a custom entropy source,
481/// useful for specialized applications or testing.
482///
483/// # Arguments
484///
485/// * `entropy_source` - Custom entropy source implementation
486///
487/// # Examples
488///
489/// ```rust
490/// use lib_q_random::{
491///     EntropySource,
492///     new_custom_rng,
493/// };
494/// use rand_core::Rng;
495///
496/// struct MyEntropySource;
497/// impl EntropySource for MyEntropySource {
498///     fn get_entropy(
499///         &mut self,
500///         dest: &mut [u8],
501///     ) -> Result<(), lib_q_random::Error> {
502///         // Implementation details...
503///         Ok(())
504///     }
505/// }
506///
507/// let mut rng = new_custom_rng(MyEntropySource);
508/// ```
509#[cfg(feature = "alloc")]
510pub fn new_custom_rng<T: EntropySource + 'static>(entropy_source: T) -> LibQRng {
511    LibQRng::new_custom(entropy_source)
512}
513
514#[cfg(test)]
515mod tests {
516    #[cfg(not(feature = "alloc"))]
517    use rand_core::Rng;
518
519    use super::*;
520
521    #[test]
522    fn test_version_constant() {
523        // VERSION is a non-empty string constant
524        #[allow(clippy::const_is_empty)]
525        {
526            assert!(!VERSION.is_empty());
527        }
528    }
529
530    #[test]
531    fn test_constants() {
532        // Test that constants have reasonable values
533        #[allow(clippy::assertions_on_constants)]
534        {
535            assert!(MIN_ENTROPY_BITS >= 128);
536            assert!(MAX_ENTROPY_BITS > MIN_ENTROPY_BITS);
537            assert!(DEFAULT_ENTROPY_SIZE > 0);
538        }
539    }
540
541    #[test]
542    #[cfg(not(feature = "alloc"))]
543    fn test_deterministic_rng_creation() {
544        let mut seed = [0u8; 32];
545        seed[..8].copy_from_slice(&[1, 2, 3, 4, 5, 6, 7, 8]);
546        let rng = new_deterministic_rng_no_std(seed);
547        assert!(rng.is_deterministic());
548    }
549
550    #[test]
551    #[cfg(not(feature = "alloc"))]
552    fn test_deterministic_rng_consistency() {
553        let seed = [42u8; 32];
554        let mut rng1 = new_deterministic_rng_no_std(seed);
555        let mut rng2 = new_deterministic_rng_no_std(seed);
556
557        let mut bytes1 = [0u8; 32];
558        let mut bytes2 = [0u8; 32];
559
560        rng1.fill_bytes(&mut bytes1);
561        rng2.fill_bytes(&mut bytes2);
562
563        assert_eq!(bytes1, bytes2);
564    }
565}