use core::num::NonZeroU32;
use super::hash::Hash;
use super::hmac::{self, HmacKey, Keyed};
use crate::Error;
use crate::low::{ct, ct_equal};
#[derive(Debug)]
pub struct Algorithm(&'static hmac::Algorithm);
pub static PBKDF2_HMAC_SHA1: Algorithm = Algorithm(&hmac::HMAC_SHA1);
pub static PBKDF2_HMAC_SHA256: Algorithm = Algorithm(&hmac::HMAC_SHA256);
pub static PBKDF2_HMAC_SHA384: Algorithm = Algorithm(&hmac::HMAC_SHA384);
pub static PBKDF2_HMAC_SHA512: Algorithm = Algorithm(&hmac::HMAC_SHA512);
pub struct Secret(hmac::Key);
impl Secret {
pub fn new(algorithm: &'static Algorithm, secret: &[u8]) -> Self {
Self(hmac::Key::new(algorithm.0, secret))
}
pub fn derive(&self, iterations: NonZeroU32, salt: &[u8], out: &mut [u8]) -> Result<(), Error> {
self.check_len(out.len())?;
self.blocks(iterations, salt, out.len(), |offset, block| {
out[offset..offset + block.len()].copy_from_slice(block);
});
Ok(())
}
pub fn verify(
&self,
iterations: NonZeroU32,
salt: &[u8],
previously_derived: &[u8],
) -> Result<(), Error> {
if previously_derived.len() < self.0.algorithm().tag_len() {
return Err(Error::WrongLength);
}
self.check_len(previously_derived.len())?;
let mut equal = true;
self.blocks(
iterations,
salt,
previously_derived.len(),
|offset, block| {
equal &= ct_equal(block, &previously_derived[offset..offset + block.len()]);
},
);
match ct::into_public(equal) {
true => Ok(()),
false => Err(Error::BadSignature),
}
}
fn check_len(&self, len: usize) -> Result<(), Error> {
let blocks = len.div_ceil(self.0.algorithm().tag_len());
match blocks == 0 || blocks > u32::MAX as usize {
true => Err(Error::WrongLength),
false => Ok(()),
}
}
fn blocks(
&self,
iterations: NonZeroU32,
salt: &[u8],
len: usize,
sink: impl FnMut(usize, &[u8]),
) {
fn blocks<H: Hash>(
key: &HmacKey<H>,
iterations: NonZeroU32,
salt: &[u8],
len: usize,
mut sink: impl FnMut(usize, &[u8]),
) {
let mut salted = key.start();
salted.update(salt);
let hash_len = H::zeroed_output().as_ref().len();
for (index, offset) in (1..=u32::MAX).zip((0..len).step_by(hash_len)) {
let mut ctx = salted.clone();
ctx.update(index.to_be_bytes());
let mut u = ctx.finish();
let mut t = u.clone();
for _ in 1..iterations.get() {
u = key.sign(u.as_ref());
for (t, u) in t.as_mut().iter_mut().zip(u.as_ref()) {
*t ^= u;
}
}
sink(offset, &t.as_ref()[..hash_len.min(len - offset)]);
}
}
match &self.0.0 {
Keyed::Sha1(key) => blocks(key, iterations, salt, len, sink),
Keyed::Sha256(key) => blocks(key, iterations, salt, len, sink),
Keyed::Sha384(key) => blocks(key, iterations, salt, len, sink),
Keyed::Sha512(key) => blocks(key, iterations, salt, len, sink),
}
}
}
#[cfg(test)]
#[cfg_attr(coverage_nightly, coverage(off))]
mod tests {
use super::*;
const ONE: NonZeroU32 = NonZeroU32::MIN;
static ALGORITHMS: [&Algorithm; 4] = [
&PBKDF2_HMAC_SHA1,
&PBKDF2_HMAC_SHA256,
&PBKDF2_HMAC_SHA384,
&PBKDF2_HMAC_SHA512,
];
#[test]
fn lengths() {
for algorithm in ALGORITHMS {
let secret = Secret::new(algorithm, b"password");
assert_eq!(
secret.derive(ONE, b"salt", &mut []),
Err(Error::WrongLength)
);
let hash_len = algorithm.0.tag_len();
let mut key = vec![0u8; hash_len + 1];
secret.derive(ONE, b"salt", &mut key).unwrap();
assert_eq!(secret.verify(ONE, b"salt", &[]), Err(Error::WrongLength));
assert_eq!(
secret.verify(ONE, b"salt", &key[..hash_len - 1]),
Err(Error::WrongLength)
);
assert_eq!(secret.verify(ONE, b"salt", &key[..hash_len]), Ok(()));
assert_eq!(secret.verify(ONE, b"salt", &key), Ok(()));
}
}
#[test]
fn verify_checks_every_byte() {
let iterations = NonZeroU32::new(3).unwrap();
let secret = Secret::new(&PBKDF2_HMAC_SHA256, b"password");
let mut key = [0u8; 70];
secret.derive(iterations, b"salt", &mut key).unwrap();
assert_eq!(secret.verify(iterations, b"salt", &key), Ok(()));
for i in 0..key.len() {
let mut wrong = key;
wrong[i] ^= 1;
assert_eq!(
secret.verify(iterations, b"salt", &wrong),
Err(Error::BadSignature)
);
}
let other = NonZeroU32::new(4).unwrap();
assert_eq!(
secret.verify(other, b"salt", &key),
Err(Error::BadSignature)
);
assert_eq!(
secret.verify(iterations, b"pepper", &key),
Err(Error::BadSignature)
);
}
#[test]
fn secret_password() {
let iterations = NonZeroU32::new(3).unwrap();
for algorithm in ALGORITHMS {
for password_len in [16, 200] {
let password = vec![0x5a; password_len];
let mut wanted = [0u8; 100];
Secret::new(algorithm, &password)
.derive(iterations, b"salt", &mut wanted)
.unwrap();
ct::secret_slice(&password);
let secret = Secret::new(algorithm, &password);
let mut got = [0u8; 100];
secret.derive(iterations, b"salt", &mut got).unwrap();
ct::public_slice(&got);
assert_eq!(got, wanted);
assert_eq!(secret.verify(iterations, b"salt", &wanted), Ok(()));
}
}
}
}