use hmac::Hmac;
use pbkdf2::pbkdf2;
use sha2::Sha256;
pub(crate) fn password_bytes_utf16le(password: &str) -> Vec<u8> {
let mut out = Vec::with_capacity(password.len().saturating_mul(2));
for unit in password.encode_utf16() {
let bytes = unit.to_le_bytes();
out.extend_from_slice(&bytes);
}
out
}
pub(crate) fn derive_key_buggy_700_preview3(
password: &str,
salt: &[u8; 16],
iterations: u32,
) -> [u8; 32] {
let mut key = derive_key(password, salt, iterations);
let u1 = derive_key(password, salt, 1);
for (k, u) in key.iter_mut().zip(u1.iter()) {
*k ^= u;
}
key
}
pub(crate) fn derive_key(password: &str, salt: &[u8; 16], iterations: u32) -> [u8; 32] {
let pwd_bytes = password_bytes_utf16le(password);
let mut out = [0u8; 32];
let _ = pbkdf2::<Hmac<Sha256>>(&pwd_bytes, salt, iterations, &mut out);
out
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn utf16le_no_bom_no_nul() {
assert_eq!(password_bytes_utf16le("abc"), b"a\0b\0c\0");
let pwd = password_bytes_utf16le("\u{1F980}");
assert_eq!(pwd, vec![0x3E, 0xD8, 0x80, 0xDD]);
}
#[test]
fn raw_pbkdf2_sha256_one_iteration_matches_python() {
let mut out = [0u8; 32];
let _ = pbkdf2::<Hmac<Sha256>>(b"password", b"salt", 1, &mut out);
let expected: [u8; 32] = [
0x12, 0x0f, 0xb6, 0xcf, 0xfc, 0xf8, 0xb3, 0x2c, 0x43, 0xe7, 0x22, 0x52, 0x56, 0xc4,
0xf8, 0x37, 0xa8, 0x65, 0x48, 0xc9, 0x2c, 0xcc, 0x35, 0x48, 0x08, 0x05, 0x98, 0x7c,
0xb7, 0x0b, 0xe1, 0x7b,
];
assert_eq!(out, expected);
}
#[test]
fn raw_pbkdf2_sha256_4096_iterations_matches_python() {
let mut out = [0u8; 32];
let _ = pbkdf2::<Hmac<Sha256>>(b"password", b"salt", 4096, &mut out);
let expected: [u8; 32] = [
0xc5, 0xe4, 0x78, 0xd5, 0x92, 0x88, 0xc8, 0x41, 0xaa, 0x53, 0x0d, 0xb6, 0x84, 0x5c,
0x4c, 0x8d, 0x96, 0x28, 0x93, 0xa0, 0x01, 0xce, 0x4e, 0x11, 0xa4, 0x96, 0x38, 0x73,
0xaa, 0x98, 0x13, 0x4a,
];
assert_eq!(out, expected);
}
#[test]
fn derive_key_matches_python_utf16le_pipeline() {
let salt = [0u8; 16];
let key = derive_key("test123", &salt, 1);
let mut expected = [0u8; 32];
let pwd_utf16: Vec<u8> = "test123"
.encode_utf16()
.flat_map(u16::to_le_bytes)
.collect();
let _ = pbkdf2::<Hmac<Sha256>>(&pwd_utf16, &salt, 1, &mut expected);
assert_eq!(key, expected, "derive_key should round-trip");
}
}