#![allow(clippy::indexing_slicing)]
use crate::digest::Digest;
use crate::hmac::Hmac;
use crate::sha256::Sha256;
use crate::sha512::Sha512;
use crate::zeroize::Zeroize;
use crate::Error;
pub fn pbkdf2<H: Digest>(
password: &[u8],
salt: &[u8],
iterations: u32,
output: &mut [u8],
) -> Result<(), Error> {
if iterations == 0 {
return Err(Error::InvalidIterations(0));
}
let prf = Hmac::<H>::new(password);
for (i, chunk) in output.chunks_mut(H::OUTPUT_SIZE).enumerate() {
let block_index = (i as u32).wrapping_add(1);
let mut mac = prf.clone();
mac.update(salt);
mac.update(&block_index.to_be_bytes());
let mut u = mac.finalize();
let mut t = u;
for _ in 1..iterations {
let mut mac = prf.clone();
mac.update(u.as_ref());
u = mac.finalize();
for (x, y) in t.as_mut().iter_mut().zip(u.as_ref()) {
*x ^= *y;
}
}
chunk.copy_from_slice(&t.as_ref()[..chunk.len()]);
u.as_mut().zeroize();
t.as_mut().zeroize();
}
Ok(())
}
pub fn pbkdf2_hmac_sha256(password: &[u8], salt: &[u8], iterations: u32, output: &mut [u8]) -> Result<(), Error> {
pbkdf2::<Sha256>(password, salt, iterations, output)
}
pub fn pbkdf2_hmac_sha512(password: &[u8], salt: &[u8], iterations: u32, output: &mut [u8]) -> Result<(), Error> {
pbkdf2::<Sha512>(password, salt, iterations, output)
}
pub fn pbkdf2_hmac_sha256_array<const N: usize>(password: &[u8], salt: &[u8], iterations: u32) -> Result<[u8; N], Error> {
let mut out = [0u8; N];
pbkdf2_hmac_sha256(password, salt, iterations, &mut out)?;
Ok(out)
}
pub fn pbkdf2_hmac_sha512_array<const N: usize>(password: &[u8], salt: &[u8], iterations: u32) -> Result<[u8; N], Error> {
let mut out = [0u8; N];
pbkdf2_hmac_sha512(password, salt, iterations, &mut out)?;
Ok(out)
}
pub const AESCRYPT_LEGACY_ITERATIONS: usize = 8192;
pub fn aescrypt_legacy(password: &[u8], iv: &[u8; 16]) -> [u8; 32] {
let mut digest = [0u8; 32];
digest[..16].copy_from_slice(iv);
for _ in 0..AESCRYPT_LEGACY_ITERATIONS {
let mut hasher = Sha256::new();
hasher.update(&digest);
hasher.update(password);
let next = hasher.finalize();
digest.zeroize();
digest = next;
}
digest
}
pub fn utf16le(password: &str) -> Vec<u8> {
password.encode_utf16().flat_map(u16::to_le_bytes).collect()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn zero_iterations_is_an_error() {
let mut out = [0u8; 32];
assert!(matches!(pbkdf2_hmac_sha256(b"p", b"s", 0, &mut out), Err(Error::InvalidIterations(0))));
}
#[test]
fn utf16le_encoding() {
assert_eq!(utf16le("Ab"), vec![0x41, 0x00, 0x62, 0x00]);
assert_eq!(utf16le("é"), vec![0xE9, 0x00]);
assert_eq!(utf16le("😀"), vec![0x3D, 0xD8, 0x00, 0xDE]);
}
}