1#[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
64use 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#[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
95pub trait KdfAlgorithm {
97 const MIN_SALT_SIZE: usize;
99
100 const DEFAULT_OUTPUT_SIZE: usize;
102
103 const ALGORITHM_ID: &'static str;
105
106 fn name() -> String {
108 Self::ALGORITHM_ID.to_string()
109 }
110
111 fn security_level() -> SecurityLevel;
113}
114
115pub trait KdfOperation<'a, A: KdfAlgorithm, T = ZeroizingBytes>: Sized {
117 fn with_ikm(self, ikm: &'a [u8]) -> Self;
119
120 fn with_salt(self, salt: &'a [u8]) -> Self;
122
123 fn with_info(self, info: &'a [u8]) -> Self;
125
126 fn with_output_length(self, length: usize) -> Self;
128
129 fn derive(self) -> Result<T>;
131
132 fn derive_array<const N: usize>(self) -> Result<Zeroizing<[u8; N]>>;
134}
135
136pub trait KeyDerivationFunction {
138 type Algorithm: KdfAlgorithm;
140
141 type Salt: AsRef<[u8]> + AsMut<[u8]> + Clone;
143
144 fn new() -> Self;
146
147 #[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 fn builder(&self) -> impl KdfOperation<'_, Self::Algorithm>
168 where
169 Self: Sized;
170
171 fn security_level() -> SecurityLevel {
173 Self::Algorithm::security_level()
174 }
175
176 fn generate_salt<R: RngCore + CryptoRng>(rng: &mut R) -> Result<Self::Salt>;
178}
179
180pub enum HkdfAlgorithm<H: HashFunction> {
182 _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#[cfg(feature = "alloc")]
207pub struct TypedHkdf<H: HashFunction + Clone> {
208 inner: hkdf::Hkdf<H, 16>, _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>; 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 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 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#[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 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 let mut array = Zeroizing::new([0u8; N]);
306 array.copy_from_slice(&vec);
307 Ok(array)
308 }
309}
310
311pub enum Pbkdf2Algorithm<H: HashFunction> {
313 _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 match H::output_size() * 8 {
329 bits if bits >= 512 => SecurityLevel::L128, 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#[cfg(feature = "alloc")]
339pub struct TypedPbkdf2<H: HashFunction + Clone> {
340 inner: pbkdf2::Pbkdf2<H, 16>, _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>; 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 fn builder(&self) -> impl KdfOperation<'_, Self::Algorithm> {
369 Pbkdf2Builder {
370 kdf: self,
371 password: None,
372 salt: None,
373 iterations: 600_000, length: Self::Algorithm::DEFAULT_OUTPUT_SIZE,
375 }
376 }
377
378 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#[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#[cfg(feature = "alloc")]
396impl<H: HashFunction + Clone> Pbkdf2Builder<'_, H> {
397 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 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 let mut params = self.kdf.inner.params().clone();
436 params.iterations = self.iterations;
437 params.key_length = self.length;
438
439 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 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 let mut array = Zeroizing::new([0u8; N]);
460 array.copy_from_slice(&vec);
461 Ok(array)
462 }
463}
464
465pub trait PasswordHashFunction: KeyDerivationFunction + ParamProvider {
467 type Password: AsRef<[u8]> + AsMut<[u8]> + Clone + Zeroize;
469
470 fn hash_password(&self, password: &Self::Password) -> Result<PasswordHash>;
472
473 fn verify(&self, password: &Self::Password, hash: &PasswordHash) -> Result<bool>;
475
476 fn benchmark(&self) -> Duration;
478
479 fn recommended_params(target_duration: Duration) -> Self::Params;
481}