use crate::{errors::UnknownCryptoError, hazardous::mac::hmac};
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 block_idx: u32 = 1u32.checked_add(idx as u32).unwrap();
_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 mod sha256 {
use super::*;
use crate::hazardous::hash::sha2::sha256::{self, Sha256};
construct_hmac_key! {
(Password, Sha256, sha256::SHA256_OUTSIZE, test_pbkdf2_password, sha256::SHA256_BLOCKSIZE)
}
#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."]
pub fn derive_key(
password: &Password,
salt: &[u8],
iterations: usize,
dst_out: &mut [u8],
) -> Result<(), UnknownCryptoError> {
_derive_key::<hmac::sha256::HmacSha256, { sha256::SHA256_OUTSIZE }>(
password.unprotected_as_bytes(),
salt,
iterations,
dst_out,
)
}
#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."]
pub fn verify(
expected: &[u8],
password: &Password,
salt: &[u8],
iterations: usize,
dst_out: &mut [u8],
) -> Result<(), UnknownCryptoError> {
_verify::<hmac::sha256::HmacSha256, { sha256::SHA256_OUTSIZE }>(
expected,
password.unprotected_as_bytes(),
salt,
iterations,
dst_out,
)
}
}
pub mod sha384 {
use super::*;
use crate::hazardous::hash::sha2::sha384::{self, Sha384};
construct_hmac_key! {
(Password, Sha384, sha384::SHA384_OUTSIZE, test_pbkdf2_password, sha384::SHA384_BLOCKSIZE)
}
#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."]
pub fn derive_key(
password: &Password,
salt: &[u8],
iterations: usize,
dst_out: &mut [u8],
) -> Result<(), UnknownCryptoError> {
_derive_key::<hmac::sha384::HmacSha384, { sha384::SHA384_OUTSIZE }>(
password.unprotected_as_bytes(),
salt,
iterations,
dst_out,
)
}
#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."]
pub fn verify(
expected: &[u8],
password: &Password,
salt: &[u8],
iterations: usize,
dst_out: &mut [u8],
) -> Result<(), UnknownCryptoError> {
_verify::<hmac::sha384::HmacSha384, { sha384::SHA384_OUTSIZE }>(
expected,
password.unprotected_as_bytes(),
salt,
iterations,
dst_out,
)
}
}
pub mod sha512 {
use super::*;
use crate::hazardous::hash::sha2::sha512::{self, Sha512};
construct_hmac_key! {
(Password, Sha512, sha512::SHA512_OUTSIZE, test_pbkdf2_password, sha512::SHA512_BLOCKSIZE)
}
#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."]
pub fn derive_key(
password: &Password,
salt: &[u8],
iterations: usize,
dst_out: &mut [u8],
) -> Result<(), UnknownCryptoError> {
_derive_key::<hmac::sha512::HmacSha512, { sha512::SHA512_OUTSIZE }>(
password.unprotected_as_bytes(),
salt,
iterations,
dst_out,
)
}
#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."]
pub fn verify(
expected: &[u8],
password: &Password,
salt: &[u8],
iterations: usize,
dst_out: &mut [u8],
) -> Result<(), UnknownCryptoError> {
_verify::<hmac::sha512::HmacSha512, { sha512::SHA512_OUTSIZE }>(
expected,
password.unprotected_as_bytes(),
salt,
iterations,
dst_out,
)
}
}
#[cfg(test)]
mod public {
use super::*;
mod test_verify {
use super::*;
#[test]
fn verify_true() {
let password_256 = sha256::Password::from_slice("pass\0word".as_bytes()).unwrap();
let password_384 = sha384::Password::from_slice("pass\0word".as_bytes()).unwrap();
let password_512 = sha512::Password::from_slice("pass\0word".as_bytes()).unwrap();
let salt = "sa\0lt".as_bytes();
let iterations: usize = 128;
let mut okm_out = [0u8; 16];
let mut okm_out_verify = [0u8; 16];
sha256::derive_key(&password_256, salt, iterations, &mut okm_out).unwrap();
assert!(sha256::verify(
&okm_out,
&password_256,
salt,
iterations,
&mut okm_out_verify
)
.is_ok());
sha384::derive_key(&password_384, salt, iterations, &mut okm_out).unwrap();
assert!(sha384::verify(
&okm_out,
&password_384,
salt,
iterations,
&mut okm_out_verify
)
.is_ok());
sha512::derive_key(&password_512, salt, iterations, &mut okm_out).unwrap();
assert!(sha512::verify(
&okm_out,
&password_512,
salt,
iterations,
&mut okm_out_verify
)
.is_ok());
}
#[test]
fn verify_false_wrong_salt() {
let password_256 = sha256::Password::from_slice("pass\0word".as_bytes()).unwrap();
let password_384 = sha384::Password::from_slice("pass\0word".as_bytes()).unwrap();
let password_512 = sha512::Password::from_slice("pass\0word".as_bytes()).unwrap();
let salt = "sa\0lt".as_bytes();
let iterations: usize = 128;
let mut okm_out = [0u8; 16];
let mut okm_out_verify = [0u8; 16];
sha256::derive_key(&password_256, salt, iterations, &mut okm_out).unwrap();
assert!(sha256::verify(
&okm_out,
&password_256,
b"",
iterations,
&mut okm_out_verify
)
.is_err());
sha384::derive_key(&password_384, salt, iterations, &mut okm_out).unwrap();
assert!(sha384::verify(
&okm_out,
&password_384,
b"",
iterations,
&mut okm_out_verify
)
.is_err());
sha512::derive_key(&password_512, salt, iterations, &mut okm_out).unwrap();
assert!(sha512::verify(
&okm_out,
&password_512,
b"",
iterations,
&mut okm_out_verify
)
.is_err());
}
#[test]
fn verify_false_wrong_password() {
let password_256 = sha256::Password::from_slice("pass\0word".as_bytes()).unwrap();
let password_384 = sha384::Password::from_slice("pass\0word".as_bytes()).unwrap();
let password_512 = sha512::Password::from_slice("pass\0word".as_bytes()).unwrap();
let salt = "sa\0lt".as_bytes();
let iterations: usize = 128;
let mut okm_out = [0u8; 16];
let mut okm_out_verify = [0u8; 16];
sha256::derive_key(&password_256, salt, iterations, &mut okm_out).unwrap();
assert!(sha256::verify(
&okm_out,
&sha256::Password::from_slice(b"pass").unwrap(),
salt,
iterations,
&mut okm_out_verify
)
.is_err());
sha384::derive_key(&password_384, salt, iterations, &mut okm_out).unwrap();
assert!(sha384::verify(
&okm_out,
&sha384::Password::from_slice(b"pass").unwrap(),
salt,
iterations,
&mut okm_out_verify
)
.is_err());
sha512::derive_key(&password_512, salt, iterations, &mut okm_out).unwrap();
assert!(sha512::verify(
&okm_out,
&sha512::Password::from_slice(b"pass").unwrap(),
salt,
iterations,
&mut okm_out_verify
)
.is_err());
}
#[test]
fn verify_diff_dklen_error() {
let password_256 = sha256::Password::from_slice("pass\0word".as_bytes()).unwrap();
let password_384 = sha384::Password::from_slice("pass\0word".as_bytes()).unwrap();
let password_512 = sha512::Password::from_slice("pass\0word".as_bytes()).unwrap();
let salt = "sa\0lt".as_bytes();
let iterations: usize = 128;
let mut okm_out = [0u8; 16];
let mut okm_out_verify = [0u8; 32];
sha256::derive_key(&password_256, salt, iterations, &mut okm_out).unwrap();
assert!(sha256::verify(
&okm_out,
&password_256,
salt,
iterations,
&mut okm_out_verify
)
.is_err());
sha384::derive_key(&password_384, salt, iterations, &mut okm_out).unwrap();
assert!(sha384::verify(
&okm_out,
&password_384,
salt,
iterations,
&mut okm_out_verify
)
.is_err());
sha512::derive_key(&password_512, salt, iterations, &mut okm_out).unwrap();
assert!(sha512::verify(
&okm_out,
&password_512,
salt,
iterations,
&mut okm_out_verify
)
.is_err());
}
#[test]
fn verify_diff_iter_error() {
let password_256 = sha256::Password::from_slice("pass\0word".as_bytes()).unwrap();
let password_384 = sha384::Password::from_slice("pass\0word".as_bytes()).unwrap();
let password_512 = sha512::Password::from_slice("pass\0word".as_bytes()).unwrap();
let salt = "sa\0lt".as_bytes();
let iterations: usize = 128;
let mut okm_out = [0u8; 16];
let mut okm_out_verify = [0u8; 16];
sha256::derive_key(&password_256, salt, iterations, &mut okm_out).unwrap();
assert!(
sha256::verify(&okm_out, &password_256, salt, 127, &mut okm_out_verify).is_err()
);
sha384::derive_key(&password_384, salt, iterations, &mut okm_out).unwrap();
assert!(
sha384::verify(&okm_out, &password_384, salt, 127, &mut okm_out_verify).is_err()
);
sha512::derive_key(&password_512, salt, iterations, &mut okm_out).unwrap();
assert!(
sha512::verify(&okm_out, &password_512, salt, 127, &mut okm_out_verify).is_err()
);
}
}
mod test_derive_key {
use super::*;
#[test]
fn zero_iterations_err() {
let password_256 = sha256::Password::from_slice("pass\0word".as_bytes()).unwrap();
let password_384 = sha384::Password::from_slice("pass\0word".as_bytes()).unwrap();
let password_512 = sha512::Password::from_slice("pass\0word".as_bytes()).unwrap();
let salt = "salt".as_bytes();
let iterations: usize = 0;
let mut okm_out = [0u8; 15];
assert!(sha256::derive_key(&password_256, salt, iterations, &mut okm_out).is_err());
assert!(sha384::derive_key(&password_384, salt, iterations, &mut okm_out).is_err());
assert!(sha512::derive_key(&password_512, salt, iterations, &mut okm_out).is_err());
}
#[test]
fn zero_dklen_err() {
let password_256 = sha256::Password::from_slice("pass\0word".as_bytes()).unwrap();
let password_384 = sha384::Password::from_slice("pass\0word".as_bytes()).unwrap();
let password_512 = sha512::Password::from_slice("pass\0word".as_bytes()).unwrap();
let salt = "salt".as_bytes();
let iterations: usize = 1;
let mut okm_out = [0u8; 0];
assert!(sha256::derive_key(&password_256, salt, iterations, &mut okm_out).is_err());
assert!(sha384::derive_key(&password_384, salt, iterations, &mut okm_out).is_err());
assert!(sha512::derive_key(&password_512, salt, iterations, &mut okm_out).is_err());
}
}
}