Skip to main content

lib_q_sig/
lib.rs

1//! lib-Q SIG - Post-quantum Digital Signatures
2//!
3//! This crate provides implementations of post-quantum digital signature schemes
4//! following the lib-Q architecture with proper security validation and provider pattern integration.
5//!
6//! ## Architecture
7//!
8//! This implementation follows the lib-Q provider pattern:
9//! - **Provider Pattern**: Implements `SignatureOperations` trait for integration with lib-q-core
10//! - **Security Validation**: Comprehensive input validation and security checks using `SecurityValidator`
11//! - **Algorithm Support**: Full support for NIST-approved signature algorithms
12//! - **Memory Safety**: Automatic zeroization of sensitive data with secure memory management
13//! - **no_std Support**: Works in constrained environments with external randomness
14//! - **Context Integration**: Seamless integration with `SignatureContext` for unified API
15//!
16//! ## Supported Algorithms
17//!
18//! - **ML-DSA**: CRYSTALS-ML-DSA (Levels 1, 3, 4)
19//!   - ML-DSA-44: Level 1 security (128-bit)
20//!   - ML-DSA-65: Level 3 security (192-bit)
21//!   - ML-DSA-87: Level 4 security (256-bit)
22//! - **FN-DSA**: FIPS 206 FN-DSA (Levels 1, 5)
23//!   - FN-DSA: Level 1 security (128-bit)
24//!   - FN-DSA-512: Level 1 security (128-bit)
25//!   - FN-DSA-1024: Level 5 security (256-bit)
26//! - **SLH-DSA**: SPHINCS+ (Levels 1, 3, 5)
27//!   - SLH-DSA-SHA256-128f-Robust: Level 1 security (128-bit)
28//!   - SLH-DSA-SHA256-192f-Robust: Level 3 security (192-bit)
29//!   - SLH-DSA-SHA256-256f-Robust: Level 5 security (256-bit)
30//!   - SLH-DSA-SHAKE256-128f-Robust: Level 1 security (128-bit)
31//!   - SLH-DSA-SHAKE256-192f-Robust: Level 3 security (192-bit)
32//!   - SLH-DSA-SHAKE256-256f-Robust: Level 5 security (256-bit)
33//!
34//! ## Feature Support
35//!
36//! All signature algorithms support:
37//! - **no_std**: Works in constrained environments with external randomness
38//! - **WASM**: JavaScript-compatible bindings for web environments
39//! - **Security validation**: Comprehensive input validation and security checks
40//! - **Memory safety**: Automatic zeroization of sensitive data
41//! - **Provider integration**: Full integration with lib-q-core provider system
42//!
43//! ### SLH-DSA features and tests
44//!
45//! - **`slh-dsa`**: SLH-DSA with caller-supplied randomness (e.g. `generate_keypair_with_randomness`, provider calls with `Some(&buf)`). This is the integration surface for `no_std` and embedder-provided entropy.
46//! - **`slh-dsa-std`**: Adds `lib-q-random` / std so APIs that take optional randomness can use `None` as “OS-backed RNG” on typical std targets. Integration tests that exercise that implicit-RNG path are gated on this feature.
47//!
48//! ```text
49//! cargo test -p lib-q-sig --features slh-dsa
50//! cargo test -p lib-q-sig --features slh-dsa-std
51//! ```
52//!
53//! ## Usage
54//!
55//! ### With std (automatic randomness)
56//! ```rust
57//! # #[cfg(feature = "std")]
58//! # {
59//! use lib_q_core::{
60//!     Algorithm,
61//!     SignatureContext,
62//!     create_signature_context,
63//! };
64//! use lib_q_sig::LibQSignatureProvider;
65//!
66//! fn main() -> Result<(), Box<dyn std::error::Error>> {
67//!     // Create signature context with provider
68//!     let mut ctx = create_signature_context();
69//!     ctx.set_provider(Box::new(LibQSignatureProvider::new()?));
70//!
71//!     // Generate keypair (requires std feature for automatic randomness)
72//!     let keypair = ctx.generate_keypair(Algorithm::MlDsa65, None)?;
73//!
74//!     // Sign message
75//!     let message = b"Hello, lib-Q!";
76//!     let signature =
77//!         ctx.sign(Algorithm::MlDsa65, keypair.secret_key(), message, None)?;
78//!
79//!     // Verify signature
80//!     let is_valid = ctx.verify(
81//!         Algorithm::MlDsa65,
82//!         keypair.public_key(),
83//!         message,
84//!         &signature,
85//!     )?;
86//!     assert!(is_valid);
87//!     Ok(())
88//! }
89//! # }
90//! ```
91//!
92//! ### Without std (external randomness)
93//! ```rust
94//! # #[cfg(feature = "ml-dsa")]
95//! # {
96//! use lib_q_core::{
97//!     Algorithm,
98//!     SignatureContext,
99//!     create_signature_context,
100//! };
101//! use lib_q_sig::LibQSignatureProvider;
102//!
103//! fn main() -> Result<(), Box<dyn std::error::Error>> {
104//!     // Create signature context with provider
105//!     let mut ctx = create_signature_context();
106//!     ctx.set_provider(Box::new(LibQSignatureProvider::new()?));
107//!
108//!     // Provide randomness externally (required in no_std environments)
109//!     let key_randomness = [0u8; 32]; // Get from hardware RNG
110//!     let signing_randomness = [0u8; 32]; // Get from hardware RNG
111//!
112//!     // Generate keypair with external randomness
113//!     let keypair =
114//!         ctx.generate_keypair(Algorithm::MlDsa65, Some(&key_randomness))?;
115//!
116//!     // Sign message with external randomness
117//!     let message = b"Hello, lib-Q!";
118//!     let signature = ctx.sign(
119//!         Algorithm::MlDsa65,
120//!         keypair.secret_key(),
121//!         message,
122//!         Some(&signing_randomness),
123//!     )?;
124//!
125//!     // Verify signature
126//!     let is_valid = ctx.verify(
127//!         Algorithm::MlDsa65,
128//!         keypair.public_key(),
129//!         message,
130//!         &signature,
131//!     )?;
132//!     assert!(is_valid);
133//!     Ok(())
134//! }
135//! # }
136//! ```
137//!
138//! ### WASM (JavaScript) Environment
139//! ```rust
140//! # #[cfg(feature = "wasm")]
141//! # {
142//! use js_sys::Uint8Array;
143//! use lib_q_sig::ml_dsa::MlDsa;
144//!
145//! fn main() -> Result<(), Box<dyn std::error::Error>> {
146//!     // Create ML-DSA instance
147//!     let ml_dsa = MlDsa::ml_dsa_65();
148//!
149//!     // Generate keypair (with optional randomness)
150//!     let keypair = ml_dsa.generate_keypair_wasm(None)?;
151//!
152//!     // Sign message
153//!     let message = Uint8Array::from(b"Hello, WASM!");
154//!     let signature =
155//!         ml_dsa.sign_wasm(keypair.secret_key(), message, None)?;
156//!
157//!     // Verify signature
158//!     let is_valid =
159//!         ml_dsa.verify_wasm(keypair.public_key(), message, signature)?;
160//!     assert!(is_valid);
161//!     Ok(())
162//! }
163//! # }
164//! ```
165//!
166//! ## Provider Pattern Integration
167//!
168//! The `LibQSignatureProvider` implements the `SignatureOperations` trait and integrates
169//! seamlessly with the lib-q-core provider system:
170//!
171//! ```rust
172//! # #[cfg(feature = "std")]
173//! # {
174//! use lib_q_core::{
175//!     Algorithm,
176//!     SignatureContext,
177//!     create_signature_context,
178//! };
179//! use lib_q_sig::LibQSignatureProvider;
180//!
181//! fn example() -> Result<(), Box<dyn std::error::Error>> {
182//!     // Create provider and integrate with context
183//!     let provider = LibQSignatureProvider::new()?;
184//!     let mut ctx = create_signature_context();
185//!     ctx.set_provider(Box::new(provider));
186//!
187//!     // All operations go through the provider with security validation
188//!     let keypair = ctx.generate_keypair(Algorithm::MlDsa65, None)?;
189//!     let signature = ctx.sign(
190//!         Algorithm::MlDsa65,
191//!         keypair.secret_key(),
192//!         b"message",
193//!         None,
194//!     )?;
195//!     let is_valid = ctx.verify(
196//!         Algorithm::MlDsa65,
197//!         keypair.public_key(),
198//!         b"message",
199//!         &signature,
200//!     )?;
201//!     Ok(())
202//! }
203//! # }
204//! ```
205//!
206//! ## Security Features
207//!
208//! The implementation includes comprehensive security measures:
209//!
210//! - **Input Validation**: All inputs are validated for correctness and security
211//! - **Memory Safety**: Sensitive data is automatically zeroized after use
212//! - **Algorithm Validation**: Only NIST-approved algorithms are supported
213//! - **Key Size Validation**: Keys are validated against expected sizes for each algorithm
214//! - **Randomness Validation**: Randomness inputs are validated for quality and size
215//! - **Error Handling**: Secure error messages that don't leak sensitive information
216//!
217//! ## Feature Flags
218//!
219//! - `ml-dsa`: Enable ML-DSA signature algorithms
220//! - `fn-dsa`: Enable FN-DSA signature algorithms  
221//! - `slh-dsa`: Enable SLH-DSA signature algorithms
222//! - `std`: Enable standard library features (automatic randomness generation)
223//! - `alloc`: Enable heap allocation (required for most operations)
224//! - `wasm`: Enable WebAssembly bindings for JavaScript environments
225
226#![cfg_attr(not(feature = "std"), no_std)]
227#![deny(unsafe_code)]
228#![deny(unused_qualifications)]
229
230#[cfg(not(feature = "std"))]
231extern crate alloc;
232
233// Re-export core types for public use
234pub use lib_q_core::{
235    Algorithm,
236    AlgorithmCategory,
237    Error,
238    Result,
239    SigKeypair,
240    SigPublicKey,
241    SigSecretKey,
242    Signature,
243    SignatureContext,
244    SignatureOperations,
245};
246
247// Provider implementation
248pub mod provider;
249
250// Algorithm implementations
251#[cfg(feature = "ml-dsa")]
252pub mod ml_dsa;
253
254#[cfg(feature = "fn-dsa")]
255pub mod fn_dsa;
256
257#[cfg(feature = "slh-dsa")]
258pub mod slh_dsa;
259
260// Re-export provider
261#[cfg(feature = "alloc")]
262pub use provider::LibQSignatureProvider;
263
264/// Get available signature algorithms with proper NIST naming
265#[cfg(feature = "std")]
266pub fn available_algorithms() -> Vec<&'static str> {
267    let mut algorithms = Vec::new();
268
269    #[cfg(feature = "ml-dsa")]
270    {
271        algorithms.extend(["ML-DSA-44", "ML-DSA-65", "ML-DSA-87"]);
272    }
273
274    #[cfg(feature = "fn-dsa")]
275    {
276        algorithms.extend(["FN-DSA", "FN-DSA-512", "FN-DSA-1024"]);
277    }
278
279    #[cfg(feature = "slh-dsa")]
280    {
281        algorithms.extend([
282            "SLH-DSA-SHA256-128f-Robust",
283            "SLH-DSA-SHA256-192f-Robust",
284            "SLH-DSA-SHA256-256f-Robust",
285            "SLH-DSA-SHAKE256-128f-Robust",
286            "SLH-DSA-SHAKE256-192f-Robust",
287            "SLH-DSA-SHAKE256-256f-Robust",
288        ]);
289    }
290
291    algorithms
292}
293
294/// Get available signature algorithms (no_std version)
295#[cfg(not(feature = "std"))]
296pub fn available_algorithms() -> &'static [&'static str] {
297    &[
298        #[cfg(feature = "ml-dsa")]
299        "ML-DSA-44",
300        #[cfg(feature = "ml-dsa")]
301        "ML-DSA-65",
302        #[cfg(feature = "ml-dsa")]
303        "ML-DSA-87",
304        #[cfg(feature = "fn-dsa")]
305        "FN-DSA",
306        #[cfg(feature = "fn-dsa")]
307        "FN-DSA-512",
308        #[cfg(feature = "fn-dsa")]
309        "FN-DSA-1024",
310        #[cfg(feature = "slh-dsa")]
311        "SLH-DSA-SHA256-128f-Robust",
312        #[cfg(feature = "slh-dsa")]
313        "SLH-DSA-SHA256-192f-Robust",
314        #[cfg(feature = "slh-dsa")]
315        "SLH-DSA-SHA256-256f-Robust",
316        #[cfg(feature = "slh-dsa")]
317        "SLH-DSA-SHAKE256-128f-Robust",
318        #[cfg(feature = "slh-dsa")]
319        "SLH-DSA-SHAKE256-192f-Robust",
320        #[cfg(feature = "slh-dsa")]
321        "SLH-DSA-SHAKE256-256f-Robust",
322    ]
323}
324
325/// Create a signature instance by algorithm name (legacy compatibility)
326#[cfg(feature = "std")]
327pub fn create_signature(algorithm: &str) -> Result<Box<dyn Signature>> {
328    match algorithm {
329        #[cfg(feature = "ml-dsa")]
330        "ml-dsa" | "mldsa65" | "ML-DSA-65" => Ok(Box::new(ml_dsa::MlDsa::ml_dsa_65())),
331        #[cfg(feature = "ml-dsa")]
332        "mldsa44" | "ML-DSA-44" => Ok(Box::new(ml_dsa::MlDsa::ml_dsa_44())),
333        #[cfg(feature = "ml-dsa")]
334        "mldsa87" | "ML-DSA-87" => Ok(Box::new(ml_dsa::MlDsa::ml_dsa_87())),
335
336        #[cfg(feature = "fn-dsa")]
337        "fn-dsa" | "FN-DSA" => Ok(Box::new(fn_dsa::FnDsa::level1())),
338        #[cfg(feature = "fn-dsa")]
339        "fn-dsa-512" | "FN-DSA-512" => Ok(Box::new(fn_dsa::FnDsa512::new())),
340        #[cfg(feature = "fn-dsa")]
341        "fn-dsa-1024" | "FN-DSA-1024" => Ok(Box::new(fn_dsa::FnDsa1024::new())),
342
343        #[cfg(feature = "slh-dsa")]
344        "slh-dsa" |
345        "SLH-DSA-SHA256-128f-Robust" |
346        "slh-dsa-sha256-128f-robust" |
347        "SLH-DSA-SHA256-192f-Robust" |
348        "slh-dsa-sha256-192f-robust" |
349        "SLH-DSA-SHA256-256f-Robust" |
350        "slh-dsa-sha256-256f-robust" |
351        "SLH-DSA-SHAKE256-128f-Robust" |
352        "slh-dsa-shake256-128f-robust" |
353        "SLH-DSA-SHAKE256-192f-Robust" |
354        "slh-dsa-shake256-192f-robust" |
355        "SLH-DSA-SHAKE256-256f-Robust" |
356        "slh-dsa-shake256-256f-robust" => Ok(Box::new(slh_dsa::SlhDsa::new())),
357
358        _ => Err(Error::InvalidAlgorithm {
359            algorithm: "Unknown algorithm",
360        }),
361    }
362}
363
364/// Create a signature context for the specified algorithm
365pub fn create_signature_context(algorithm: Algorithm) -> Result<SignatureContext> {
366    // Validate that this algorithm supports signature operations
367    if !algorithm.supports_category(AlgorithmCategory::Signature) {
368        return Err(Error::InvalidAlgorithm {
369            algorithm: "Algorithm does not support signature operations",
370        });
371    }
372
373    Ok(SignatureContext::new())
374}
375
376#[cfg(test)]
377mod tests {
378    use super::*;
379
380    #[test]
381    fn test_available_algorithms() {
382        let algorithms = available_algorithms();
383        // Should have algorithms if features are enabled
384        #[cfg(any(feature = "ml-dsa", feature = "fn-dsa", feature = "slh-dsa"))]
385        assert!(
386            !algorithms.is_empty(),
387            "Should have at least one algorithm when features are enabled"
388        );
389    }
390
391    #[test]
392    fn test_create_signature_context() {
393        // Test that context creation works for valid signature algorithms
394        let result = create_signature_context(Algorithm::MlDsa65);
395        assert!(
396            result.is_ok(),
397            "Context creation should succeed for valid signature algorithm"
398        );
399
400        // The context itself doesn't have providers - those are set up by the main lib-q crate
401        // This test just verifies the basic context creation and structure
402        let mut ctx = result.unwrap();
403
404        // Without a provider, keypair generation must fail (no crypto backend wired).
405        let keypair_result = ctx.generate_keypair(Algorithm::MlDsa65, None);
406        assert!(
407            keypair_result.is_err(),
408            "Keypair generation should fail without provider"
409        );
410        if let Err(err) = keypair_result {
411            assert!(
412                matches!(
413                    err,
414                    Error::NotImplemented { .. } | Error::ProviderNotConfigured { .. }
415                ),
416                "unexpected error without provider: {:?}",
417                err
418            );
419        }
420    }
421
422    #[test]
423    fn test_create_signature_context_invalid_algorithm() {
424        let result = create_signature_context(Algorithm::MlKem512);
425        assert!(result.is_err());
426    }
427
428    #[test]
429    fn test_provider_creation() {
430        let provider = LibQSignatureProvider::new();
431        assert!(provider.is_ok(), "Provider should be created successfully");
432    }
433
434    #[test]
435    fn test_provider_algorithm_support() {
436        let provider = LibQSignatureProvider::new().unwrap();
437
438        // Test ML-DSA algorithms
439        #[cfg(feature = "ml-dsa")]
440        {
441            let result = provider.generate_keypair(Algorithm::MlDsa65, None);
442            // Should either succeed or return NotImplemented (depending on feature flags)
443            match result {
444                Ok(_) => {
445                    // Success case - this is expected with std feature
446                }
447                Err(Error::NotImplemented { .. }) => {
448                    // Expected when std feature is not available
449                }
450                Err(Error::RandomGenerationFailed { .. }) => {
451                    // Expected when std feature is not available for randomness generation
452                }
453                Err(e) => {
454                    panic!("Unexpected error type: {:?}", e);
455                }
456            }
457        }
458
459        // Test unsupported algorithm
460        let result = provider.generate_keypair(Algorithm::Sha3_256, None);
461        assert!(result.is_err());
462        if let Err(Error::InvalidAlgorithm { .. }) = result {
463            // Expected error type
464        } else {
465            panic!("Expected InvalidAlgorithm error for non-signature algorithm");
466        }
467    }
468
469    #[test]
470    fn test_algorithm_naming_consistency() {
471        let algorithms = available_algorithms();
472
473        // Check that algorithm names follow NIST conventions
474        for algorithm in algorithms {
475            assert!(
476                algorithm.starts_with("ML-DSA-") ||
477                    algorithm.starts_with("FN-DSA") ||
478                    algorithm.starts_with("SLH-DSA-"),
479                "Algorithm name '{}' should follow NIST naming conventions",
480                algorithm
481            );
482        }
483    }
484
485    #[cfg(feature = "std")]
486    #[test]
487    fn test_legacy_compatibility() {
488        #[cfg(feature = "ml-dsa")]
489        {
490            // Test legacy algorithm names still work
491            let result = create_signature("ml-dsa");
492            assert!(result.is_ok(), "Legacy 'ml-dsa' name should work");
493
494            let result = create_signature("mldsa65");
495            assert!(result.is_ok(), "Legacy 'mldsa65' name should work");
496
497            // Test new NIST names work
498            let result = create_signature("ML-DSA-65");
499            assert!(result.is_ok(), "NIST 'ML-DSA-65' name should work");
500        }
501    }
502
503    #[cfg(feature = "std")]
504    #[test]
505    fn test_create_signature_unknown_algorithm() {
506        let result = create_signature("not-a-real-signature");
507        assert!(matches!(result, Err(Error::InvalidAlgorithm { .. })));
508    }
509}