Skip to content

v0.6.1

Pre-release
Pre-release

Choose a tag to compare

@NoCentralHub NoCentralHub released this 09 May 18:45
· 323 commits to master since this release

dcrypt-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.