use crate::{errors::UnknownCryptoError, hazardous::mac::hmac};
use core::marker::PhantomData;
fn _function_f<Hmac>(
salt: &[u8],
iterations: usize,
index: u32,
dk_block: &mut [u8],
block_len: usize,
u_step: &mut [u8],
hmac: &mut Hmac,
) -> Result<(), UnknownCryptoError>
where
Hmac: hmac::HmacFunction,
{
debug_assert_eq!(u_step.len(), Hmac::HASH_FUNC_OUTSIZE);
hmac._update(salt)?;
hmac._update(&index.to_be_bytes())?;
hmac._finalize(u_step)?;
debug_assert!(block_len <= u_step.len());
dk_block.copy_from_slice(&u_step[..block_len]);
if iterations > 1 {
for _ in 1..iterations {
hmac._reset();
hmac._update(u_step)?;
hmac._finalize(u_step)?;
xor_slices!(u_step, dk_block);
}
}
Ok(())
}
fn _derive_key<Hmac, const OUTSIZE: usize>(
padded_password: &[u8],
salt: &[u8],
iterations: usize,
dest: &mut [u8],
) -> Result<(), UnknownCryptoError>
where
Hmac: hmac::HmacFunction,
{
debug_assert_eq!(OUTSIZE, Hmac::HASH_FUNC_OUTSIZE);
if dest.is_empty() || iterations < 1 {
return Err(UnknownCryptoError);
}
let mut u_step = [0u8; OUTSIZE];
let mut hmac = Hmac::_new(padded_password)?;
for (idx, dk_block) in dest.chunks_mut(Hmac::HASH_FUNC_OUTSIZE).enumerate() {
let idx = u32::try_from(idx).map_err(|_| UnknownCryptoError)?;
let block_idx = if let Some(bidx) = 1u32.checked_add(idx) {
bidx
} else {
return Err(UnknownCryptoError);
};
_function_f(
salt,
iterations,
block_idx,
dk_block,
dk_block.len(),
&mut u_step,
&mut hmac,
)?;
hmac._reset();
}
Ok(())
}
fn _verify<Hmac, const OUTSIZE: usize>(
expected: &[u8],
padded_password: &[u8],
salt: &[u8],
iterations: usize,
dest: &mut [u8],
) -> Result<(), UnknownCryptoError>
where
Hmac: hmac::HmacFunction,
{
debug_assert_eq!(OUTSIZE, Hmac::HASH_FUNC_OUTSIZE);
_derive_key::<Hmac, { OUTSIZE }>(padded_password, salt, iterations, dest)?;
crate::util::secure_cmp(expected, dest)
}
pub(crate) mod sealed {
pub trait Sealed {}
pub trait Variant: Sealed {}
}
#[derive(Debug, PartialEq)]
pub struct SHA256;
impl sealed::Sealed for SHA256 {}
impl sealed::Variant for SHA256 {}
#[derive(Debug, PartialEq)]
pub struct SHA384;
impl sealed::Sealed for SHA384 {}
impl sealed::Variant for SHA384 {}
#[derive(Debug, PartialEq)]
pub struct SHA512;
impl sealed::Sealed for SHA512 {}
impl sealed::Variant for SHA512 {}
#[derive(Debug)]
pub struct Pbkdf2<V: sealed::Variant> {
_variant: PhantomData<V>,
}
impl Pbkdf2<SHA256> {
#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."]
pub fn derive_key(
password: &[u8],
salt: &[u8],
iterations: usize,
dst_out: &mut [u8],
) -> Result<(), UnknownCryptoError> {
let padded = hmac::sha256::SecretKey::try_from(password)?;
_derive_key::<
hmac::sha256::HmacSha256,
{ crate::hazardous::hash::sha2::sha256::SHA256_OUTSIZE },
>(padded.unprotected_as_ref(), salt, iterations, dst_out)
}
#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."]
pub fn verify(
expected: &[u8],
password: &[u8],
salt: &[u8],
iterations: usize,
dst_out: &mut [u8],
) -> Result<(), UnknownCryptoError> {
let padded = hmac::sha256::SecretKey::try_from(password)?;
_verify::<hmac::sha256::HmacSha256, { crate::hazardous::hash::sha2::sha256::SHA256_OUTSIZE }>(
expected,
padded.unprotected_as_ref(),
salt,
iterations,
dst_out,
)
}
}
impl Pbkdf2<SHA384> {
#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."]
pub fn derive_key(
password: &[u8],
salt: &[u8],
iterations: usize,
dst_out: &mut [u8],
) -> Result<(), UnknownCryptoError> {
let padded = hmac::sha384::SecretKey::try_from(password)?;
_derive_key::<
hmac::sha384::HmacSha384,
{ crate::hazardous::hash::sha2::sha384::SHA384_OUTSIZE },
>(padded.unprotected_as_ref(), salt, iterations, dst_out)
}
#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."]
pub fn verify(
expected: &[u8],
password: &[u8],
salt: &[u8],
iterations: usize,
dst_out: &mut [u8],
) -> Result<(), UnknownCryptoError> {
let padded = hmac::sha384::SecretKey::try_from(password)?;
_verify::<hmac::sha384::HmacSha384, { crate::hazardous::hash::sha2::sha384::SHA384_OUTSIZE }>(
expected,
padded.unprotected_as_ref(),
salt,
iterations,
dst_out,
)
}
}
impl Pbkdf2<SHA512> {
#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."]
pub fn derive_key(
password: &[u8],
salt: &[u8],
iterations: usize,
dst_out: &mut [u8],
) -> Result<(), UnknownCryptoError> {
let padded = hmac::sha512::SecretKey::try_from(password)?;
_derive_key::<
hmac::sha512::HmacSha512,
{ crate::hazardous::hash::sha2::sha512::SHA512_OUTSIZE },
>(padded.unprotected_as_ref(), salt, iterations, dst_out)
}
#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."]
pub fn verify(
expected: &[u8],
password: &[u8],
salt: &[u8],
iterations: usize,
dst_out: &mut [u8],
) -> Result<(), UnknownCryptoError> {
let padded = hmac::sha512::SecretKey::try_from(password)?;
_verify::<hmac::sha512::HmacSha512, { crate::hazardous::hash::sha2::sha512::SHA512_OUTSIZE }>(
expected,
padded.unprotected_as_ref(),
salt,
iterations,
dst_out,
)
}
}
#[cfg(test)]
mod public {
use super::*;
mod test_verify {
use super::*;
#[test]
fn verify_true() {
let password = b"pass\0word";
let salt = "sa\0lt".as_bytes();
let iterations: usize = 128;
let mut okm_out = [0u8; 16];
let mut okm_out_verify = [0u8; 16];
Pbkdf2::<SHA256>::derive_key(password, salt, iterations, &mut okm_out).unwrap();
assert!(
Pbkdf2::<SHA256>::verify(&okm_out, password, salt, iterations, &mut okm_out_verify)
.is_ok()
);
Pbkdf2::<SHA384>::derive_key(password, salt, iterations, &mut okm_out).unwrap();
assert!(
Pbkdf2::<SHA384>::verify(&okm_out, password, salt, iterations, &mut okm_out_verify)
.is_ok()
);
Pbkdf2::<SHA512>::derive_key(password, salt, iterations, &mut okm_out).unwrap();
assert!(
Pbkdf2::<SHA512>::verify(&okm_out, password, salt, iterations, &mut okm_out_verify)
.is_ok()
);
}
#[test]
fn verify_false_wrong_salt() {
let password = b"pass\0word";
let salt = "sa\0lt".as_bytes();
let iterations: usize = 128;
let mut okm_out = [0u8; 16];
let mut okm_out_verify = [0u8; 16];
Pbkdf2::<SHA256>::derive_key(password, salt, iterations, &mut okm_out).unwrap();
assert!(
Pbkdf2::<SHA256>::verify(&okm_out, password, b"", iterations, &mut okm_out_verify)
.is_err()
);
Pbkdf2::<SHA384>::derive_key(password, salt, iterations, &mut okm_out).unwrap();
assert!(
Pbkdf2::<SHA384>::verify(&okm_out, password, b"", iterations, &mut okm_out_verify)
.is_err()
);
Pbkdf2::<SHA512>::derive_key(password, salt, iterations, &mut okm_out).unwrap();
assert!(
Pbkdf2::<SHA512>::verify(&okm_out, password, b"", iterations, &mut okm_out_verify)
.is_err()
);
}
#[test]
fn verify_false_wrong_password() {
let password = b"pass\0word";
let salt = "sa\0lt".as_bytes();
let iterations: usize = 128;
let mut okm_out = [0u8; 16];
let mut okm_out_verify = [0u8; 16];
Pbkdf2::<SHA256>::derive_key(password, salt, iterations, &mut okm_out).unwrap();
assert!(
Pbkdf2::<SHA256>::verify(&okm_out, b"pass", salt, iterations, &mut okm_out_verify)
.is_err()
);
Pbkdf2::<SHA384>::derive_key(password, salt, iterations, &mut okm_out).unwrap();
assert!(
Pbkdf2::<SHA384>::verify(&okm_out, b"pass", salt, iterations, &mut okm_out_verify)
.is_err()
);
Pbkdf2::<SHA512>::derive_key(password, salt, iterations, &mut okm_out).unwrap();
assert!(
Pbkdf2::<SHA512>::verify(&okm_out, b"pass", salt, iterations, &mut okm_out_verify)
.is_err()
);
}
#[test]
fn verify_diff_dklen_error() {
let password = b"pass\0word";
let salt = "sa\0lt".as_bytes();
let iterations: usize = 128;
let mut okm_out = [0u8; 16];
let mut okm_out_verify = [0u8; 32];
Pbkdf2::<SHA256>::derive_key(password, salt, iterations, &mut okm_out).unwrap();
assert!(
Pbkdf2::<SHA256>::verify(&okm_out, password, salt, iterations, &mut okm_out_verify)
.is_err()
);
Pbkdf2::<SHA384>::derive_key(password, salt, iterations, &mut okm_out).unwrap();
assert!(
Pbkdf2::<SHA384>::verify(&okm_out, password, salt, iterations, &mut okm_out_verify)
.is_err()
);
Pbkdf2::<SHA512>::derive_key(password, salt, iterations, &mut okm_out).unwrap();
assert!(
Pbkdf2::<SHA512>::verify(&okm_out, password, salt, iterations, &mut okm_out_verify)
.is_err()
);
}
#[test]
fn verify_diff_iter_error() {
let password = b"pass\0word";
let salt = "sa\0lt".as_bytes();
let iterations: usize = 128;
let mut okm_out = [0u8; 16];
let mut okm_out_verify = [0u8; 16];
Pbkdf2::<SHA256>::derive_key(password, salt, iterations, &mut okm_out).unwrap();
assert!(
Pbkdf2::<SHA256>::verify(&okm_out, password, salt, 127, &mut okm_out_verify)
.is_err()
);
Pbkdf2::<SHA384>::derive_key(password, salt, iterations, &mut okm_out).unwrap();
assert!(
Pbkdf2::<SHA384>::verify(&okm_out, password, salt, 127, &mut okm_out_verify)
.is_err()
);
Pbkdf2::<SHA512>::derive_key(password, salt, iterations, &mut okm_out).unwrap();
assert!(
Pbkdf2::<SHA512>::verify(&okm_out, password, salt, 127, &mut okm_out_verify)
.is_err()
);
}
}
mod test_derive_key {
use super::*;
#[test]
fn zero_iterations_err() {
let password = b"pass\0word";
let salt = "salt".as_bytes();
let iterations: usize = 0;
let mut okm_out = [0u8; 15];
assert!(
Pbkdf2::<SHA256>::derive_key(password, salt, iterations, &mut okm_out).is_err()
);
assert!(
Pbkdf2::<SHA384>::derive_key(password, salt, iterations, &mut okm_out).is_err()
);
assert!(
Pbkdf2::<SHA512>::derive_key(password, salt, iterations, &mut okm_out).is_err()
);
}
#[test]
fn zero_dklen_err() {
let password = b"pass\0word";
let salt = "salt".as_bytes();
let iterations: usize = 1;
let mut okm_out = [0u8; 0];
assert!(
Pbkdf2::<SHA256>::derive_key(password, salt, iterations, &mut okm_out).is_err()
);
assert!(
Pbkdf2::<SHA384>::derive_key(password, salt, iterations, &mut okm_out).is_err()
);
assert!(
Pbkdf2::<SHA512>::derive_key(password, salt, iterations, &mut okm_out).is_err()
);
}
}
}