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}