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}