Skip to main content

dcrypt_algorithms/kdf/
mod.rs

1//! Key Derivation Functions with operation pattern and type-level guarantees
2//!
3//! This module provides implementations of key derivation functions (KDFs)
4//! with improved type safety and ergonomic APIs.
5//!
6/// ## Example usage
7///
8/// ```
9/// # use dcrypt_internal::random::ChaCha20Rng;
10/// use dcrypt_algorithms::kdf::{TypedHkdf, KeyDerivationFunction, KdfOperation};
11/// use dcrypt_algorithms::hash::Sha256;
12///
13/// // Create KDF instance
14/// let kdf = TypedHkdf::<Sha256>::new();
15///
16/// // Generate a random salt
17/// let mut rng = ChaCha20Rng::from_seed([0x42; 32]);
18/// let salt = TypedHkdf::<Sha256>::generate_salt(&mut rng).unwrap();
19///
20/// // Traditional API
21/// let key1 = kdf.derive_key(
22///     b"password123",
23///     Some(salt.as_ref()),
24///     Some(b"context info"),
25///     32
26/// ).unwrap();
27///
28/// // Operation pattern API
29/// let key2 = kdf.builder()
30///     .with_ikm(b"password123")
31///     .with_salt(salt.as_ref())
32///     .with_info(b"context info")
33///     .with_output_length(32)
34///     .derive().unwrap();
35///
36/// // Derive to fixed-size array
37/// let key3: dcrypt_internal::zeroing::Zeroizing<[u8; 32]> = kdf.builder()
38///     .with_ikm(b"password123")
39///     .with_salt(salt.as_ref())
40///     .with_info(b"context info")
41///     .derive_array().unwrap();
42///
43/// assert_eq!(key1, key2);
44/// assert_eq!(&key1[..], &key3[..]);
45/// ```
46// Conditional imports for no_std
47#[cfg(feature = "alloc")]
48extern crate alloc;
49
50#[cfg(feature = "alloc")]
51use crate::alloc_prelude::*;
52
53#[cfg(feature = "std")]
54use std::time::Duration;
55
56#[cfg(not(feature = "std"))]
57use core::time::Duration;
58
59use ::core::marker::PhantomData;
60use dcrypt_internal::random::{CryptoRng, RngCore};
61#[cfg(feature = "alloc")]
62use dcrypt_internal::zeroing::{Zeroizing, ZeroizingBytes};
63
64// Import the new error types
65use crate::error::{Error, Result};
66use crate::hash::HashFunction;
67use crate::types::Salt;
68use dcrypt_internal::zeroing::Zeroize;
69
70pub mod common;
71pub mod params;
72
73#[cfg(feature = "alloc")]
74pub mod hkdf;
75
76#[cfg(feature = "alloc")]
77pub mod pbkdf2;
78
79#[cfg(feature = "alloc")]
80pub mod argon2;
81
82pub use common::SecurityLevel;
83pub use params::{ParamProvider, PasswordHash};
84
85// Re-exports for convenience
86#[cfg(feature = "alloc")]
87pub use hkdf::Hkdf;
88
89#[cfg(feature = "alloc")]
90pub use pbkdf2::{Pbkdf2, Pbkdf2Params};
91
92#[cfg(feature = "alloc")]
93pub use argon2::{Algorithm as Argon2Type, Argon2, Params as Argon2Params};
94
95/// Marker trait for KDF algorithms
96pub trait KdfAlgorithm {
97    /// Minimum salt size in bytes
98    const MIN_SALT_SIZE: usize;
99
100    /// Default output size in bytes
101    const DEFAULT_OUTPUT_SIZE: usize;
102
103    /// Static algorithm identifier for compile-time checking
104    const ALGORITHM_ID: &'static str;
105
106    /// Returns the KDF algorithm name
107    fn name() -> String {
108        Self::ALGORITHM_ID.to_string()
109    }
110
111    /// Security level provided by this KDF
112    fn security_level() -> SecurityLevel;
113}
114
115/// Operation for KDF operations with improved type safety
116pub trait KdfOperation<'a, A: KdfAlgorithm, T = ZeroizingBytes>: Sized {
117    /// Set the input keying material
118    fn with_ikm(self, ikm: &'a [u8]) -> Self;
119
120    /// Set the salt
121    fn with_salt(self, salt: &'a [u8]) -> Self;
122
123    /// Set the info/context data
124    fn with_info(self, info: &'a [u8]) -> Self;
125
126    /// Set the desired output length
127    fn with_output_length(self, length: usize) -> Self;
128
129    /// Execute the key derivation
130    fn derive(self) -> Result<T>;
131
132    /// Execute the key derivation into a fixed-size array
133    fn derive_array<const N: usize>(self) -> Result<Zeroizing<[u8; N]>>;
134}
135
136/// Common trait for all key derivation functions
137pub trait KeyDerivationFunction {
138    /// The algorithm this KDF implements
139    type Algorithm: KdfAlgorithm;
140
141    /// Salt type with appropriate validation
142    type Salt: AsRef<[u8]> + AsMut<[u8]> + Clone;
143
144    /// Creates a new instance of the KDF with default parameters
145    fn new() -> Self;
146
147    /// Derives a key using the KDF parameters
148    ///
149    /// # Arguments
150    /// * `input` - Input keying material
151    /// * `salt` - Optional salt value
152    /// * `info` - Optional context and application-specific information
153    /// * `length` - Length of the output key in bytes
154    ///
155    /// # Returns
156    /// The derived key in exact-size, zeroizing storage.
157    #[cfg(feature = "alloc")]
158    fn derive_key(
159        &self,
160        input: &[u8],
161        salt: Option<&[u8]>,
162        info: Option<&[u8]>,
163        length: usize,
164    ) -> Result<ZeroizingBytes>;
165
166    /// Creates a builder for fluent API usage - FIXED: Elided lifetime
167    fn builder(&self) -> impl KdfOperation<'_, Self::Algorithm>
168    where
169        Self: Sized;
170
171    /// Returns the security level of the KDF in bits
172    fn security_level() -> SecurityLevel {
173        Self::Algorithm::security_level()
174    }
175
176    /// Generate a random salt with appropriate size
177    fn generate_salt<R: RngCore + CryptoRng>(rng: &mut R) -> Result<Self::Salt>;
178}
179
180/// Type-level constants for HKDF algorithm
181pub enum HkdfAlgorithm<H: HashFunction> {
182    /// Phantom field for the hash function
183    _Hash(PhantomData<H>),
184}
185
186impl<H: HashFunction> KdfAlgorithm for HkdfAlgorithm<H> {
187    const MIN_SALT_SIZE: usize = 16;
188    const DEFAULT_OUTPUT_SIZE: usize = 32;
189    const ALGORITHM_ID: &'static str = "HKDF";
190
191    fn name() -> String {
192        format!("{}-{}", Self::ALGORITHM_ID, H::name())
193    }
194
195    fn security_level() -> SecurityLevel {
196        match H::output_size() * 8 {
197            bits if bits >= 512 => SecurityLevel::L256,
198            bits if bits >= 384 => SecurityLevel::L192,
199            bits if bits >= 256 => SecurityLevel::L128,
200            bits => SecurityLevel::Custom(bits as u32 / 2),
201        }
202    }
203}
204
205/// Enhanced HKDF implementation with type-level guarantees
206#[cfg(feature = "alloc")]
207pub struct TypedHkdf<H: HashFunction + Clone> {
208    inner: hkdf::Hkdf<H, 16>, // Use default size of 16
209    _phantom: PhantomData<H>,
210}
211
212#[cfg(feature = "alloc")]
213impl<H: HashFunction + Clone> KeyDerivationFunction for TypedHkdf<H> {
214    type Algorithm = HkdfAlgorithm<H>;
215    type Salt = Salt<16>; // Updated to use generic Salt with size
216
217    fn new() -> Self {
218        Self {
219            inner: hkdf::Hkdf::<H, 16>::new(),
220            _phantom: PhantomData,
221        }
222    }
223
224    #[cfg(feature = "alloc")]
225    fn derive_key(
226        &self,
227        input: &[u8],
228        salt: Option<&[u8]>,
229        info: Option<&[u8]>,
230        length: usize,
231    ) -> Result<ZeroizingBytes> {
232        self.inner.derive_key(input, salt, info, length)
233    }
234
235    // FIXED: Elided lifetime
236    fn builder(&self) -> impl KdfOperation<'_, Self::Algorithm> {
237        HKdfOperation {
238            kdf: self,
239            ikm: None,
240            salt: None,
241            info: None,
242            length: Self::Algorithm::DEFAULT_OUTPUT_SIZE,
243        }
244    }
245
246    // FIXED: Removed unnecessary let binding
247    fn generate_salt<R: RngCore + CryptoRng>(rng: &mut R) -> Result<Self::Salt> {
248        Salt::random_with_size(rng, Self::Algorithm::MIN_SALT_SIZE)
249    }
250}
251
252/// HKDF builder implementation
253#[cfg(feature = "alloc")]
254pub struct HKdfOperation<'a, H: HashFunction + Clone> {
255    kdf: &'a TypedHkdf<H>,
256    ikm: Option<&'a [u8]>,
257    salt: Option<&'a [u8]>,
258    info: Option<&'a [u8]>,
259    length: usize,
260}
261
262#[cfg(feature = "alloc")]
263impl<'a, H: HashFunction + Clone> KdfOperation<'a, HkdfAlgorithm<H>> for HKdfOperation<'a, H> {
264    fn with_ikm(mut self, ikm: &'a [u8]) -> Self {
265        self.ikm = Some(ikm);
266        self
267    }
268
269    fn with_salt(mut self, salt: &'a [u8]) -> Self {
270        self.salt = Some(salt);
271        self
272    }
273
274    fn with_info(mut self, info: &'a [u8]) -> Self {
275        self.info = Some(info);
276        self
277    }
278
279    fn with_output_length(mut self, length: usize) -> Self {
280        self.length = length;
281        self
282    }
283
284    fn derive(self) -> Result<ZeroizingBytes> {
285        let ikm = self
286            .ikm
287            .ok_or_else(|| Error::param("ikm", "Input keying material is required"))?;
288
289        self.kdf.derive_key(ikm, self.salt, self.info, self.length)
290    }
291
292    fn derive_array<const N: usize>(self) -> Result<Zeroizing<[u8; N]>> {
293        // Ensure the requested size matches
294        if self.length != N {
295            return Err(Error::Length {
296                context: "HKDF output",
297                expected: N,
298                actual: self.length,
299            });
300        }
301
302        let vec = self.derive()?;
303
304        // Convert to fixed-size array
305        let mut array = Zeroizing::new([0u8; N]);
306        array.copy_from_slice(&vec);
307        Ok(array)
308    }
309}
310
311/// Type-level constants for PBKDF2 algorithm
312pub enum Pbkdf2Algorithm<H: HashFunction> {
313    /// Phantom field for the hash function
314    _Hash(PhantomData<H>),
315}
316
317impl<H: HashFunction> KdfAlgorithm for Pbkdf2Algorithm<H> {
318    const MIN_SALT_SIZE: usize = 16;
319    const DEFAULT_OUTPUT_SIZE: usize = 32;
320    const ALGORITHM_ID: &'static str = "PBKDF2";
321
322    fn name() -> String {
323        format!("{}-{}", Self::ALGORITHM_ID, H::name())
324    }
325
326    fn security_level() -> SecurityLevel {
327        // PBKDF2 security depends on iterations and hash size
328        match H::output_size() * 8 {
329            bits if bits >= 512 => SecurityLevel::L128, // Conservative estimate
330            bits if bits >= 384 => SecurityLevel::L128,
331            bits if bits >= 256 => SecurityLevel::L128,
332            bits => SecurityLevel::Custom(bits as u32 / 2),
333        }
334    }
335}
336
337/// Enhanced PBKDF2 implementation with type-level guarantees
338#[cfg(feature = "alloc")]
339pub struct TypedPbkdf2<H: HashFunction + Clone> {
340    inner: pbkdf2::Pbkdf2<H, 16>, // Use default size of 16
341    _phantom: PhantomData<H>,
342}
343
344#[cfg(feature = "alloc")]
345impl<H: HashFunction + Clone> KeyDerivationFunction for TypedPbkdf2<H> {
346    type Algorithm = Pbkdf2Algorithm<H>;
347    type Salt = Salt<16>; // Updated to use generic Salt with size
348
349    fn new() -> Self {
350        Self {
351            inner: pbkdf2::Pbkdf2::<H, 16>::new(),
352            _phantom: PhantomData,
353        }
354    }
355
356    #[cfg(feature = "alloc")]
357    fn derive_key(
358        &self,
359        input: &[u8],
360        salt: Option<&[u8]>,
361        info: Option<&[u8]>,
362        length: usize,
363    ) -> Result<ZeroizingBytes> {
364        self.inner.derive_key(input, salt, info, length)
365    }
366
367    // FIXED: Elided lifetime
368    fn builder(&self) -> impl KdfOperation<'_, Self::Algorithm> {
369        Pbkdf2Builder {
370            kdf: self,
371            password: None,
372            salt: None,
373            iterations: 600_000, // OWASP recommended minimum
374            length: Self::Algorithm::DEFAULT_OUTPUT_SIZE,
375        }
376    }
377
378    // FIXED: Removed unnecessary let binding
379    fn generate_salt<R: RngCore + CryptoRng>(rng: &mut R) -> Result<Self::Salt> {
380        Salt::random_with_size(rng, Self::Algorithm::MIN_SALT_SIZE)
381    }
382}
383
384/// PBKDF2 builder implementation
385#[cfg(feature = "alloc")]
386pub struct Pbkdf2Builder<'a, H: HashFunction + Clone> {
387    kdf: &'a TypedPbkdf2<H>,
388    password: Option<&'a [u8]>,
389    salt: Option<&'a [u8]>,
390    iterations: u32,
391    length: usize,
392}
393
394// FIXED: Elided lifetime in impl block
395#[cfg(feature = "alloc")]
396impl<H: HashFunction + Clone> Pbkdf2Builder<'_, H> {
397    /// Set the number of iterations
398    pub fn with_iterations(mut self, iterations: u32) -> Self {
399        self.iterations = iterations;
400        self
401    }
402}
403
404#[cfg(feature = "alloc")]
405impl<'a, H: HashFunction + Clone> KdfOperation<'a, Pbkdf2Algorithm<H>> for Pbkdf2Builder<'a, H> {
406    fn with_ikm(mut self, password: &'a [u8]) -> Self {
407        self.password = Some(password);
408        self
409    }
410
411    fn with_salt(mut self, salt: &'a [u8]) -> Self {
412        self.salt = Some(salt);
413        self
414    }
415
416    fn with_info(self, _info: &'a [u8]) -> Self {
417        // PBKDF2 doesn't use info, but we implement for API compatibility
418        self
419    }
420
421    fn with_output_length(mut self, length: usize) -> Self {
422        self.length = length;
423        self
424    }
425
426    fn derive(self) -> Result<ZeroizingBytes> {
427        let password = self
428            .password
429            .ok_or_else(|| Error::param("password", "Password is required"))?;
430        let salt = self
431            .salt
432            .ok_or_else(|| Error::param("salt", "Salt is required"))?;
433
434        // Adjust inner Pbkdf2Params
435        let mut params = self.kdf.inner.params().clone();
436        params.iterations = self.iterations;
437        params.key_length = self.length;
438
439        // Use inner implementation
440        let mut kdf = self.kdf.inner.clone();
441        kdf.set_params(params);
442
443        kdf.derive_key(password, Some(salt), None, self.length)
444    }
445
446    fn derive_array<const N: usize>(self) -> Result<Zeroizing<[u8; N]>> {
447        // Ensure the requested size matches
448        if self.length != N {
449            return Err(Error::Length {
450                context: "PBKDF2 output",
451                expected: N,
452                actual: self.length,
453            });
454        }
455
456        let vec = self.derive()?;
457
458        // Convert to fixed-size array
459        let mut array = Zeroizing::new([0u8; N]);
460        array.copy_from_slice(&vec);
461        Ok(array)
462    }
463}
464
465/// Trait for password hashing functions with type-level guarantees
466pub trait PasswordHashFunction: KeyDerivationFunction + ParamProvider {
467    /// Password type with zeroizing
468    type Password: AsRef<[u8]> + AsMut<[u8]> + Clone + Zeroize;
469
470    /// Hashes a password with the configured parameters
471    fn hash_password(&self, password: &Self::Password) -> Result<PasswordHash>;
472
473    /// Verifies a password against a hash
474    fn verify(&self, password: &Self::Password, hash: &PasswordHash) -> Result<bool>;
475
476    /// Benchmarks the current parameters on this system
477    fn benchmark(&self) -> Duration;
478
479    /// Recommends parameters based on a target duration
480    fn recommended_params(target_duration: Duration) -> Self::Params;
481}