use crate::mac::Mac;
use alloc::vec::Vec;
use core::iter::repeat;
fn calculate_block<M: Mac>(
mac: &mut M,
salt: &[u8],
c: u32,
idx: u32,
scratch: &mut [u8],
block: &mut [u8],
) {
mac.input(salt);
mac.input(&idx.to_be_bytes());
mac.raw_result(block);
mac.reset();
if c > 1 {
mac.input(block);
mac.raw_result(scratch);
mac.reset();
for (output, &input) in block.iter_mut().zip(scratch.iter()) {
*output ^= input;
}
}
for _ in 2..c {
mac.input(scratch);
mac.raw_result(scratch);
mac.reset();
for (output, &input) in block.iter_mut().zip(scratch.iter()) {
*output ^= input;
}
}
}
pub fn pbkdf2<M: Mac>(mac: &mut M, salt: &[u8], c: u32, output: &mut [u8]) {
assert!(c > 0);
let os = mac.output_bytes();
let mut scratch: Vec<u8> = repeat(0).take(os).collect();
let mut idx: u32 = 0;
for chunk in output.chunks_mut(os) {
idx = idx.checked_add(1).expect("PBKDF2 size limit exceeded.");
if chunk.len() == os {
calculate_block(mac, salt, c, idx, &mut scratch, chunk);
} else {
let mut tmp: Vec<u8> = repeat(0).take(os).collect();
calculate_block(mac, salt, c, idx, &mut scratch[..], &mut tmp[..]);
let chunk_len = chunk.len();
chunk[0..chunk_len].copy_from_slice(&tmp[..chunk_len]);
}
}
}
#[cfg(test)]
mod test {
use super::pbkdf2;
use crate::hmac::Hmac;
use crate::sha1::Sha1;
#[test]
fn test1() {
let password = b"password";
let salt = b"salt";
let c = 2;
let mut out = [0u8; 20];
pbkdf2(&mut Hmac::new(Sha1::new(), password), salt, c, &mut out);
assert_eq!(
out,
[
0xea, 0x6c, 0x01, 0x4d, 0xc7, 0x2d, 0x6f, 0x8c, 0xcd, 0x1e, 0xd9, 0x2a, 0xce, 0x1d,
0x41, 0xf0, 0xd8, 0xde, 0x89, 0x57
]
)
}
}