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}