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;
207
208// Cryptographic (KT128-backed) RNG for ZK hiding salts/blinding
209pub mod kt128_rng;
210pub use kt128_expander::{
211 DOMAIN_HPKE_RNG,
212 DOMAIN_LIBQ_DET_RNG,
213 KT128_DET_GOLDEN_U64_SEED_64,
214 KT128_DET_GOLDEN_ZERO_SEED_64,
215 Kt128Expander,
216};
217pub use kt128_rng::Kt128Rng;
218
219// Custom entropy source system for no_std/WASM environments
220#[cfg(feature = "custom-entropy")]
221pub mod custom_entropy;
222
223// Re-export main types
224pub use error::{
225 Error,
226 Result,
227};
228#[cfg(feature = "alloc")]
229pub use provider::LibQRng;
230// Re-export specialized implementations
231#[cfg(feature = "classical-mceliece")]
232pub use specialized::ClassicalMcElieceRng;
233#[cfg(feature = "fn-dsa")]
234pub use specialized::FnDsaRng;
235// NOTE: `specialized::Kt128Rng` (the HPKE/RFC-9861 RNG) is deliberately NOT re-exported here.
236// The canonical crate-root `Kt128Rng` is the seedable `kt128_rng::Kt128Rng` (re-exported above and
237// used across the workspace via `from_u64`/`from_seed_bytes`, e.g. lib-q-zkp, lib-q-hpke). Adding a
238// second `pub use specialized::Kt128Rng` collided with it (E0252) under `hpke + hash` — a different
239// type with the same name. The HPKE variant remains reachable as `lib_q_random::specialized::Kt128Rng`.
240#[cfg(feature = "alloc")]
241pub use traits::RngProvider;
242pub use traits::{
243 EntropySource,
244 SecureRng,
245 SecurityLevel,
246};
247// Re-export validation types
248pub use validation::{
249 EntropyQuality,
250 EntropyValidator,
251};
252
253/// Version information
254pub const VERSION: &str = env!("CARGO_PKG_VERSION");
255
256/// Minimum entropy bits required for cryptographic operations
257pub const MIN_ENTROPY_BITS: usize = 128;
258
259/// Maximum entropy bits for validation
260pub const MAX_ENTROPY_BITS: usize = 4096;
261
262/// Default entropy buffer size
263pub const DEFAULT_ENTROPY_SIZE: usize = 32;
264
265/// Fill `dest` with cryptographically secure bytes from the OS entropy source.
266///
267/// Requires the `getrandom` feature. Returns `Err` if that feature is absent
268/// rather than silently producing weak output.
269///
270/// # Errors
271///
272/// Returns [`Error::FeatureNotAvailable`] when the `getrandom` feature is not
273/// enabled. Returns [`Error::EntropySourceUnavailable`] when getrandom fails.
274pub fn fill_entropy(dest: &mut [u8]) -> Result<()> {
275 #[cfg(feature = "getrandom")]
276 {
277 getrandom::fill(dest).map_err(|_| Error::EntropySourceUnavailable {
278 source: "system",
279 context: Some("getrandom failed"),
280 })
281 }
282 #[cfg(not(feature = "getrandom"))]
283 {
284 let _ = dest;
285 Err(Error::FeatureNotAvailable {
286 feature: "secure entropy",
287 required_features: &["getrandom"],
288 })
289 }
290}
291
292/// Create a new secure RNG instance
293///
294/// This function creates a cryptographically secure RNG using the best available
295/// entropy source for the current platform.
296///
297/// # Errors
298///
299/// Returns an error if no secure entropy source is available.
300///
301/// # Examples
302///
303/// ```rust
304/// use lib_q_random::new_secure_rng;
305/// use rand_core::Rng;
306///
307/// let mut rng = new_secure_rng().unwrap();
308/// let mut bytes = [0u8; 32];
309/// rng.fill_bytes(&mut bytes);
310/// ```
311#[cfg(feature = "alloc")]
312pub fn new_secure_rng() -> Result<LibQRng> {
313 LibQRng::new_secure()
314}
315
316/// Create a new secure RNG instance for `no_std` environments
317///
318/// This function creates a cryptographically secure RNG that works in `no_std`
319/// environments using getrandom for entropy.
320///
321/// # Errors
322///
323/// Returns an error if getrandom is not available or fails to initialize.
324///
325/// # Examples
326///
327/// ```rust,no_run
328/// use lib_q_random::new_secure_rng_no_std;
329/// use rand_core::Rng;
330///
331/// let mut rng = new_secure_rng_no_std().unwrap();
332/// let mut bytes = [0u8; 32];
333/// rng.fill_bytes(&mut bytes);
334/// ```
335#[cfg(not(feature = "alloc"))]
336pub fn new_secure_rng_no_std() -> Result<no_std_rng::NoStdRng> {
337 no_std_rng::NoStdRng::new()
338}
339
340/// Create a new deterministic RNG instance
341///
342/// This function creates a deterministic RNG suitable for testing and
343/// reproducible operations. Output is a **KT128** XOF stream from `seed`.
344/// **Unpredictability is only as strong as the seed**; use [`new_secure_rng`]
345/// for production cryptography.
346///
347/// # Arguments
348///
349/// * `seed` - 32-byte seed for KT128 expansion
350///
351/// # Examples
352///
353/// ```rust
354/// use lib_q_random::new_deterministic_rng;
355/// use rand_core::Rng;
356///
357/// let mut rng = new_deterministic_rng([1; 32]);
358/// let mut bytes = [0u8; 32];
359/// rng.fill_bytes(&mut bytes);
360/// ```
361#[cfg(feature = "alloc")]
362#[must_use]
363pub fn new_deterministic_rng(seed: [u8; 32]) -> LibQRng {
364 LibQRng::new_deterministic(seed)
365}
366
367/// Create a deterministic RNG from a `u64` test seed (`SplitMix64` → KT128).
368#[cfg(feature = "alloc")]
369#[must_use]
370pub fn new_deterministic_rng_from_u64(seed: u64) -> LibQRng {
371 LibQRng::new_deterministic_from_u64(seed)
372}
373
374/// Create a new deterministic RNG instance for `no_std` environments
375///
376/// This function creates a deterministic RNG suitable for testing and
377/// reproducible operations in `no_std` environments. KT128 stream from `seed`;
378/// **not** unpredictable unless the seed is secret and high-entropy.
379///
380/// # Arguments
381///
382/// * `seed` - 32-byte seed
383///
384/// # Examples
385///
386/// ```rust,no_run
387/// use lib_q_random::new_deterministic_rng_no_std;
388/// use rand_core::Rng;
389///
390/// let mut rng = new_deterministic_rng_no_std([1; 32]);
391/// let mut bytes = [0u8; 32];
392/// rng.fill_bytes(&mut bytes);
393/// ```
394#[cfg(not(feature = "alloc"))]
395#[must_use]
396pub fn new_deterministic_rng_no_std(seed: [u8; 32]) -> no_std_rng::NoStdRng {
397 no_std_rng::NoStdRng::new_deterministic(seed)
398}
399
400/// Create a deterministic `no_std` RNG from a `u64` test seed (`SplitMix64` → KT128).
401#[cfg(not(feature = "alloc"))]
402#[must_use]
403pub fn new_deterministic_rng_no_std_from_u64(seed: u64) -> no_std_rng::NoStdRng {
404 no_std_rng::NoStdRng::new_deterministic_from_u64(seed)
405}
406
407/// Register a custom entropy source for the current thread
408///
409/// This function allows developers to register a custom entropy source that will
410/// be used by `NoStdRng` when generating random bytes. The entropy source must
411/// remain valid for the lifetime of the registration.
412///
413/// # Arguments
414///
415/// * `source` - The custom entropy source to register
416///
417/// # Safety
418///
419/// The `source` must remain valid for the lifetime of the registration.
420/// The caller is responsible for ensuring the source is not dropped
421/// while registered.
422///
423/// # Examples
424///
425/// ```rust,no_run
426/// use lib_q_random::custom_entropy::{
427/// CustomEntropyConfig,
428/// CustomEntropySource,
429/// EntropyContext,
430/// EntropyQuality,
431/// };
432/// use lib_q_random::{
433/// register_custom_entropy_source,
434/// unregister_custom_entropy_source,
435/// };
436/// use rand_core::Rng;
437///
438/// // Define a custom entropy callback
439/// unsafe extern "C" fn my_entropy_callback(
440/// dest: *mut u8,
441/// len: usize,
442/// _context: *mut u8,
443/// ) -> i32 {
444/// // Fill dest with len bytes of entropy
445/// // Return 0 on success, non-zero on failure
446/// 0
447/// }
448///
449/// // Create and register the entropy source
450/// let context = EntropyContext::empty();
451/// let config = CustomEntropyConfig::default();
452/// let source = unsafe {
453/// CustomEntropySource::new(
454/// my_entropy_callback,
455/// context,
456/// EntropyQuality::User,
457/// config,
458/// "my_custom_source",
459/// )
460/// };
461///
462/// unsafe {
463/// register_custom_entropy_source(&source);
464/// }
465///
466/// // Now NoStdRng will use the custom entropy source
467/// // (In no_std environments, use new_secure_rng_no_std())
468/// // let mut rng = new_secure_rng_no_std().unwrap();
469/// // let mut bytes = [0u8; 32];
470/// // rng.fill_bytes(&mut bytes);
471///
472/// // Clean up
473/// unregister_custom_entropy_source();
474/// ```
475#[cfg(feature = "custom-entropy")]
476pub unsafe fn register_custom_entropy_source(source: *const custom_entropy::CustomEntropySource) {
477 unsafe { custom_entropy::register_custom_entropy_source(source) };
478}
479
480/// Unregister the current custom entropy source
481///
482/// This function removes the currently registered custom entropy source,
483/// causing `NoStdRng` to fall back to the default entropy source (getrandom).
484#[cfg(feature = "custom-entropy")]
485pub fn unregister_custom_entropy_source() {
486 custom_entropy::unregister_custom_entropy_source();
487}
488
489/// Check if a custom entropy source is currently registered
490///
491/// # Returns
492///
493/// Returns `true` if a custom entropy source is registered, `false` otherwise.
494#[cfg(feature = "custom-entropy")]
495#[must_use]
496pub fn has_custom_entropy_source() -> bool {
497 custom_entropy::has_custom_entropy_source()
498}
499
500/// Get information about the currently registered entropy source
501///
502/// # Returns
503///
504/// Returns a tuple of (`source_id`, quality) if a source is registered.
505#[cfg(feature = "custom-entropy")]
506#[must_use]
507pub fn get_custom_entropy_source_info() -> Option<(&'static str, custom_entropy::EntropyQuality)> {
508 custom_entropy::get_entropy_source_info()
509}
510
511/// Create a new RNG with custom entropy source
512///
513/// This function allows creating an RNG with a custom entropy source,
514/// useful for specialized applications or testing.
515///
516/// # Arguments
517///
518/// * `entropy_source` - Custom entropy source implementation
519///
520/// # Examples
521///
522/// ```rust
523/// use lib_q_random::{
524/// EntropySource,
525/// new_custom_rng,
526/// };
527/// use rand_core::Rng;
528///
529/// struct MyEntropySource;
530/// impl EntropySource for MyEntropySource {
531/// fn get_entropy(
532/// &mut self,
533/// dest: &mut [u8],
534/// ) -> Result<(), lib_q_random::Error> {
535/// // Implementation details...
536/// Ok(())
537/// }
538/// }
539///
540/// let mut rng = new_custom_rng(MyEntropySource);
541/// ```
542#[cfg(feature = "alloc")]
543pub fn new_custom_rng<T: EntropySource + 'static>(entropy_source: T) -> LibQRng {
544 LibQRng::new_custom(entropy_source)
545}
546
547#[cfg(test)]
548mod tests {
549 #[cfg(not(feature = "alloc"))]
550 use rand_core::Rng;
551
552 use super::*;
553
554 #[test]
555 fn test_version_constant() {
556 // VERSION is a non-empty string constant
557 #[allow(clippy::const_is_empty)]
558 {
559 assert!(!VERSION.is_empty());
560 }
561 }
562
563 #[test]
564 fn test_constants() {
565 // Test that constants have reasonable values
566 #[allow(clippy::assertions_on_constants)]
567 {
568 assert!(MIN_ENTROPY_BITS >= 128);
569 assert!(MAX_ENTROPY_BITS > MIN_ENTROPY_BITS);
570 assert!(DEFAULT_ENTROPY_SIZE > 0);
571 }
572 }
573
574 #[test]
575 #[cfg(not(feature = "alloc"))]
576 fn test_deterministic_rng_creation() {
577 let mut seed = [0u8; 32];
578 seed[..8].copy_from_slice(&[1, 2, 3, 4, 5, 6, 7, 8]);
579 let rng = new_deterministic_rng_no_std(seed);
580 assert!(rng.is_deterministic());
581 }
582
583 #[test]
584 #[cfg(not(feature = "alloc"))]
585 fn test_deterministic_rng_consistency() {
586 let seed = [42u8; 32];
587 let mut rng1 = new_deterministic_rng_no_std(seed);
588 let mut rng2 = new_deterministic_rng_no_std(seed);
589
590 let mut bytes1 = [0u8; 32];
591 let mut bytes2 = [0u8; 32];
592
593 rng1.fill_bytes(&mut bytes1);
594 rng2.fill_bytes(&mut bytes2);
595
596 assert_eq!(bytes1, bytes2);
597 }
598}