Repository navigation
v0.6.1
Pre-releasedcrypt-primitives v0.6.1
Type System Refactoring with Generic Const Parameters
This release implements a comprehensive refactoring of the dcrypt-primitives type system to consistently use generic types with const parameters throughout the library. The new approach significantly improves type safety for cryptographic operations while maintaining zero-cost abstractions.
Key Improvements
- Generic Type System: Replace concrete types (e.g.,
Nonce12) with generic types (e.g.,Nonce<12>) - Compile-Time Safety: Enforce algorithm compatibility constraints at compile time with marker traits
- Type-Level Security: Prevent invalid key sizes and algorithm combinations
- Zero-Cost Abstractions: Maintain runtime performance through monomorphization
- Improved Error Handling: Provide clear, consistent error messages for runtime validation
Implementation Details
The refactoring introduces a sealed trait hierarchy to enforce cryptographic invariants at the type level:
// Core sealed trait to prevent external implementations
pub mod sealed {
pub trait Sealed {}
}
// Algorithm-specific compatibility traits
pub trait ChaCha20Compatible: sealed::Sealed {}
impl sealed::Sealed for Nonce<12> {}
impl ChaCha20Compatible for Nonce<12> {}
pub trait AesGcmCompatible: sealed::Sealed {}
impl AesGcmCompatible for Nonce<12> {}
impl sealed::Sealed for Nonce<16> {} // Only added once
impl AesGcmCompatible for Nonce<16> {}This design allows functions to constrain generic parameters to only those combinations that are cryptographically sound:
impl ChaCha20 {
pub fn new<const N: usize>(key: &[u8; CHACHA20_KEY_SIZE], nonce: &Nonce<N>) -> Self
where
Nonce<N>: ChaCha20Compatible,
{
Self::with_counter(key, nonce, 0)
}
}If a developer attempts to use an invalid nonce size, they'll receive a clear compiler error rather than encountering a runtime failure.
Security Benefits
- Prevents Entire Classes of Vulnerabilities: Incorrect usage of cryptographic primitives is caught at compile time
- Guarantees Proper Algorithm Combinations: Ensures only valid key sizes, nonce lengths, and algorithm pairings
- Reduces Side-Channel Risk: Improves constant-time behavior by enforcing proper type constraints
- Maintains Integrity: Sealed traits prevent external implementations that could violate cryptographic invariants
Example Usage
// Create a nonce of the correct size for ChaCha20
let nonce = Nonce::<12>::random(&mut rng);
// This compiles - ChaCha20 accepts 12-byte nonces
let cipher = ChaCha20::new(&key, &nonce);
// This would fail at compile time:
let invalid_nonce = Nonce::<16>::random(&mut rng);
// let cipher = ChaCha20::new(&key, &invalid_nonce); // Compiler error!For types that must be constructed from external data, fallible constructors are provided:
// Safe conversion from a byte slice with proper error handling
let nonce_result = Nonce::<12>::from_slice(external_data);
match nonce_result {
Ok(nonce) => {
// Use the validated nonce
let cipher = ChaCha20::new(&key, &nonce);
// ...
},
Err(e) => {
// Handle the error appropriately
}
}Acknowledgments
Thanks to all contributors who helped design, implement, and test this refactoring.