use crate::Digest;
use crate::hmac::{hmac, hmac_multi};
use alloc::vec;
use core::num::NonZeroU32;
pub fn pbkdf2<H: Digest>(password: &[u8], salt: &[u8], iterations: NonZeroU32, dk: &mut [u8]) {
let hlen = H::OUTPUT_LEN;
let c = iterations.get();
let mut block_index: u32 = 1;
let mut offset = 0;
while offset < dk.len() {
let mut u = vec![0u8; hlen];
hmac_multi::<H>(password, &[salt, &block_index.to_be_bytes()], &mut u);
let mut t = u.clone();
let mut next = vec![0u8; hlen];
for _ in 1..c {
hmac::<H>(password, &u, &mut next);
for k in 0..hlen {
t[k] ^= next[k];
}
u.copy_from_slice(&next);
}
let take = core::cmp::min(hlen, dk.len() - offset);
dk[offset..offset + take].copy_from_slice(&t[..take]);
offset += take;
block_index += 1;
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::sha1::Sha1;
use alloc::vec::Vec;
fn hx(h: &str) -> Vec<u8> {
(0..h.len())
.step_by(2)
.map(|i| u8::from_str_radix(&h[i..i + 2], 16).unwrap())
.collect()
}
fn nz(n: u32) -> NonZeroU32 {
NonZeroU32::new(n).unwrap()
}
#[test]
fn rfc6070_case1_sha1() {
let mut dk = [0u8; 20];
pbkdf2::<Sha1>(b"password", b"salt", nz(1), &mut dk);
assert_eq!(dk[..], hx("0c60c80f961f0e71f3a9b524af6012062fe037a6")[..]);
}
#[test]
fn rfc6070_case2_sha1() {
let mut dk = [0u8; 20];
pbkdf2::<Sha1>(b"password", b"salt", nz(2), &mut dk);
assert_eq!(dk[..], hx("ea6c014dc72d6f8ccd1ed92ace1d41f0d8de8957")[..]);
}
}