@lib-q/slh-dsa
v0.0.10
Published
SLH-DSA / SPHINCS+ (FIPS 205) signatures for Node.js
Maintainers
Readme
lib-Q SLH-DSA - Stateless Hash-based Digital Signature Algorithm
This crate provides a complete implementation of SLH-DSA (Stateless Hash-based Digital Signature Algorithm) based on the finalized NIST FIPS-205 standard. SLH-DSA is a post-quantum digital signature scheme designed to be resistant to quantum computers.
For provider-style integration, see lib-q-sig (features slh-dsa / slh-dsa-std / slh-dsa-wasm).
Features
- NIST FIPS-205 Compliant: Implements all 12 standardized parameter sets
- Post-Quantum Security: Resistant to both classical and quantum attacks
- Stateless Design: No state management required, making it suitable for distributed systems
- Memory Safe: Zero unsafe code with automatic memory zeroization
no_stdCompatible: Works in constrained environments- WASM Support: JavaScript-compatible bindings for web environments
Supported Parameter Sets
All 12 FIPS 205 parameter sets are implemented. FIPS 205 standardized only the simple instances —
the "robust" variants from the SPHINCS+ submission were not standardized and are not provided
here. s = small signature / slower signing, f = fast signing / larger signature.
SHA2-based (lib-q-slh-dsa type in parentheses)
- SLH-DSA-SHA2-128s (
Sha2_128s) / SLH-DSA-SHA2-128f (Sha2_128f): Level 1 security (128-bit) - SLH-DSA-SHA2-192s (
Sha2_192s) / SLH-DSA-SHA2-192f (Sha2_192f): Level 3 security (192-bit) - SLH-DSA-SHA2-256s (
Sha2_256s) / SLH-DSA-SHA2-256f (Sha2_256f): Level 5 security (256-bit)
SHAKE-based
- SLH-DSA-SHAKE-128s (
Shake128s) / SLH-DSA-SHAKE-128f (Shake128f): Level 1 security (128-bit) - SLH-DSA-SHAKE-192s (
Shake192s) / SLH-DSA-SHAKE-192f (Shake192f): Level 3 security (192-bit) - SLH-DSA-SHAKE-256s (
Shake256s) / SLH-DSA-SHAKE-256f (Shake256f): Level 5 security (256-bit)
The default test suite exercises Shake128f + Sha2_128f; enable the all-parameter-set-tests
feature to run all 12 (slow).
Reachable surface differs by entry point. All 12 are available when you use this crate directly (
SigningKey::<Sha2_192s>::new(..)). The workspace-widelib_q_types::Algorithmenum — the path used bylib-q-core,lib-q-sig, and the WASM/npm bindings — currently exposes only six SLH-DSA rows (SlhDsaSha256{128,192,256}fRobust,SlhDsaShake256{128,192,256}fRobust): thefvariants only, under legacy SPHINCS+Robustnames that FIPS 205 does not define. Those enum names are historical and do not describe what is computed — the underlyingParameterSet::NAMEvalues are the correct FIPS 205 designations (SLH-DSA-SHA2-128f,SLH-DSA-SHAKE-192f, …). Renaming the enum variants is a breaking change and is tracked separately; until then, do not read a-Robustenum name as a claim that a "robust" instance is implemented.
Usage
Basic Usage
use lib_q_slh_dsa::*;
use lib_q_random::new_secure_rng;
use signature::*;
let mut rng = new_secure_rng().expect("Failed to create secure RNG");
// Generate a signing key using the SHAKE256-128f parameter set
let sk = SigningKey::<Shake128f>::new(&mut rng);
// Generate the corresponding public key
let vk = sk.verifying_key();
// Sign a message
let message = b"Hello, SLH-DSA!";
let sig = sk.sign_with_rng(&mut rng, message);
// Verify the signature
assert!(vk.verify(message, &sig).is_ok());Integration with lib-Q
use lib_q_core::{Algorithm, SignatureContext, create_signature_context};
// use lib_q_sig::LibQSignatureProvider;
fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
// Create signature context with provider
// let mut ctx = create_signature_context();
// ctx.set_provider(Box::new(LibQSignatureProvider::new()?));
// Generate keypair for SLH-DSA-SHAKE256-128f-Robust
// let keypair = ctx.generate_keypair(Algorithm::SlhDsaShake256128fRobust, None)?;
// Sign message
// let message = b"Hello, lib-Q SLH-DSA!";
// let signature = ctx.sign(Algorithm::SlhDsaShake256128fRobust, keypair.secret_key(), message, None)?;
// Verify signature
// let is_valid = ctx.verify(Algorithm::SlhDsaShake256128fRobust, keypair.public_key(), message, &signature)?;
// assert!(is_valid);
Ok(())
}External Randomness (no_std environments)
use lib_q_slh_dsa::{SigningKey, Shake128f};
use signature::*;
fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
// In no_std environments, you must provide cryptographically secure randomness externally
// This example shows the pattern, but uses placeholder values for illustration
// ⚠️ NEVER use predictable values like this in production!
// Use a hardware RNG or other cryptographically secure source
let key_randomness = [
0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef,
0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10,
0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88,
0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x00,
0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x00, 0x11,
0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99
]; // 48 bytes for Shake128f
// Generate keypair with external randomness
let sk = SigningKey::<Shake128f>::try_from(&key_randomness[..])?;
let vk = sk.verifying_key();
// In real no_std environments, you'd need to provide your own RNG for signing
// This example demonstrates the API structure
let message = b"Hello, no_std SLH-DSA!";
// let mut rng = YourCustomRng::new(&signing_randomness);
// let signature = sk.sign_with_rng(&mut rng, message)?;
// let is_valid = vk.verify(message, &signature).is_ok();
// assert!(is_valid);
Ok(())
}Performance Characteristics
Key Generation Performance
- 128-bit security: ~0.5-1ms per keypair
- 192-bit security: ~1-2ms per keypair
- 256-bit security: ~2-4ms per keypair
Signing Performance
- 128-bit security: ~1-2ms per signature
- 192-bit security: ~2-4ms per signature
- 256-bit security: ~4-8ms per signature
Verification Performance
- 128-bit security: ~0.5-1ms per signature
- 192-bit security: ~1-2ms per signature
- 256-bit security: ~2-4ms per signature
Memory Usage
- Key sizes: 48-96 bytes (signing keys), 32-64 bytes (verifying keys)
- Signature sizes: 7KB-50KB depending on parameter set
- Stack usage: ~2-8KB during operations (varies by parameter set)
Performance Optimization Tips
- Batch Operations: Process multiple signatures together when possible
- Parameter Selection: Choose appropriate security level for your use case
- Memory Management: Consider heap allocation for large signatures in constrained environments
- RNG Performance: Use fast, secure RNGs for better overall performance
Benchmarking
Run the included benchmarks to measure performance on your specific hardware:
cargo bench --package lib-q-slh-dsaSecurity Considerations
Cryptographic Security
- Post-Quantum Security: SLH-DSA provides security against both classical and quantum attacks
- NIST Standardization: All parameter sets are NIST-approved and follow FIPS-205 specification
- Hash Function Security: Uses SHA-256 and SHAKE-256, both cryptographically secure hash functions
Implementation Security
- Memory Safety: Automatic zeroization of sensitive data when keys are dropped
- Constant-Time Operations: Critical operations are implemented in constant time where possible
- Input Validation: Comprehensive validation of all inputs to prevent attacks
- Error Handling: Secure error handling that doesn't leak sensitive information
Operational Security
- Randomness Requirements: All operations require cryptographically secure randomness
- Key Management: Signing keys must be stored securely and zeroized after use
- Parameter Selection: Choose appropriate parameter sets based on security requirements
- Signature Verification: Always verify signatures before trusting them
Security Best Practices
Key Generation
use lib_q_slh_dsa::{SigningKey, Shake128f};
use lib_q_random::new_secure_rng;
// ✅ Good: Use secure RNG
let mut rng = new_secure_rng().expect("Failed to create RNG");
let sk = SigningKey::<Shake128f>::new(&mut rng);
// ❌ Bad: Don't use predictable randomness
let predictable_bytes = [0u8; 48];
// let sk = SigningKey::<Shake128f>::try_from(&predictable_bytes[..]).unwrap(); // This will fail!Key Storage
use lib_q_slh_dsa::{SigningKey, Shake128f};
use lib_q_random::new_secure_rng;
let mut rng = new_secure_rng().expect("Failed to create RNG");
let sk = SigningKey::<Shake128f>::new(&mut rng);
// ✅ Good: Store keys securely
let sk_bytes = sk.to_bytes();
// Store in secure key management system
// secure_key_store.store("user_key", &sk_bytes);
// ❌ Bad: Don't store keys in plaintext
let sk_bytes = sk.to_bytes();
// std::fs::write("key.txt", &sk_bytes).unwrap(); // Insecure!Signature Verification
use lib_q_slh_dsa::{SigningKey, Shake128f};
use lib_q_random::new_secure_rng;
use signature::*;
let mut rng = new_secure_rng().expect("Failed to create RNG");
let sk = SigningKey::<Shake128f>::new(&mut rng);
let vk = sk.verifying_key();
let message = b"Hello, world!";
let signature = sk.sign_with_rng(&mut rng, message);
// ✅ Good: Always verify signatures
let is_valid = vk.verify(message, &signature).is_ok();
if !is_valid {
panic!("Invalid signature");
}
// ❌ Bad: Don't skip verification
// let is_valid = vk.verify(message, &signature)?; // Missing this!
// process_message(message); // Dangerous!Security Considerations by Environment
Standard Library (std)
- Advantages: Full RNG support, comprehensive error handling
- Security: Highest level of security features available
- Use Case: General-purpose applications, servers, desktop applications
no_std Environments
- Advantages: Minimal dependencies, embedded-friendly
- Security: Requires external randomness management
- Use Case: Embedded systems,
IoTdevices, constrained environments
WebAssembly (WASM)
- Advantages: Cross-platform compatibility, sandboxed execution
- Security: Limited RNG options, requires careful randomness management
- Use Case: Web applications, browser-based cryptography
Threat Model Considerations
Classical Attacks
- Brute Force: SLH-DSA provides 128/192/256-bit security levels
- Side-Channel: Implementation includes constant-time operations
- Implementation Bugs: Comprehensive testing and validation
Quantum Attacks
- Grover's Algorithm: Security levels account for quantum speedup
- Shor's Algorithm: Not applicable to hash-based signatures
- Future Quantum: Designed to resist known quantum attacks
Performance vs Security Trade-offs
| Parameter Set | Security Level | Signature Size | Performance | Use Case | |---------------|----------------|----------------|-------------|----------| | SHA256-128f | 128-bit | ~7KB | Fast | General purpose | | SHA256-192f | 192-bit | ~11KB | Medium | High security | | SHA256-256f | 256-bit | ~15KB | Slower | Maximum security |
Common Security Pitfalls
- Weak Randomness: Never use predictable or weak random number generators
- Key Reuse: Don't reuse signing keys across different contexts
- Signature Forgery: Always verify signatures before processing messages
- Memory Leaks: Ensure keys are properly zeroized when no longer needed
- Parameter Mismatch: Use consistent parameter sets across your application
Feature Flags
alloc: Enable heap allocation (required for most operations)std: Enable standard library features
License
This crate is licensed under the Apache-2.0 license.
Subresource integrity (SHA-384)
Paths in integrity-manifest.json are relative to the package root (including web/ and nodejs/ when both ship).
