use crate::errors::{CrabError, CrabResult};
use crate::secrets::SecretVec;
use pbkdf2::pbkdf2_hmac;
use sha2::{Sha256, Sha512};
pub const PBKDF2_SHA256_RECOMMENDED_ITERATIONS: u32 = 600_000;
pub const PBKDF2_SHA512_RECOMMENDED_ITERATIONS: u32 = 210_000;
pub fn pbkdf2_derive_sha256(
password: &[u8],
salt: &[u8],
iterations: u32,
key_len: usize,
) -> CrabResult<SecretVec> {
if salt.len() < 8 {
return Err(CrabError::invalid_input("Salt should be at least 8 bytes (16+ recommended)"));
}
if iterations < 10_000 {
return Err(CrabError::invalid_input(
"Iteration count too low (minimum 10,000, recommend 600,000+)",
));
}
let mut output = vec![0u8; key_len];
pbkdf2_hmac::<Sha256>(password, salt, iterations, &mut output);
Ok(SecretVec::new(output))
}
pub fn pbkdf2_derive_sha512(
password: &[u8],
salt: &[u8],
iterations: u32,
key_len: usize,
) -> CrabResult<SecretVec> {
if salt.len() < 8 {
return Err(CrabError::invalid_input("Salt should be at least 8 bytes (16+ recommended)"));
}
if iterations < 10_000 {
return Err(CrabError::invalid_input(
"Iteration count too low (minimum 10,000, recommend 210,000+)",
));
}
let mut output = vec![0u8; key_len];
pbkdf2_hmac::<Sha512>(password, salt, iterations, &mut output);
Ok(SecretVec::new(output))
}
pub fn pbkdf2_derive(
password: &[u8],
salt: &[u8],
iterations: u32,
key_len: usize,
) -> CrabResult<SecretVec> {
pbkdf2_derive_sha256(password, salt, iterations, key_len)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_pbkdf2_sha256_basic() {
let password = b"password";
let salt = b"saltsaltsaltsalt";
let key = pbkdf2_derive_sha256(password, salt, 10_000, 32).unwrap();
assert_eq!(key.len(), 32);
}
#[test]
fn test_pbkdf2_sha256_deterministic() {
let password = b"test_password";
let salt = b"test_salt_16byte";
let key1 = pbkdf2_derive_sha256(password, salt, 10_000, 32).unwrap();
let key2 = pbkdf2_derive_sha256(password, salt, 10_000, 32).unwrap();
assert_eq!(key1.as_slice(), key2.as_slice());
}
#[test]
fn test_pbkdf2_sha256_different_passwords() {
let salt = b"same_salt_16byte";
let key1 = pbkdf2_derive_sha256(b"password1", salt, 10_000, 32).unwrap();
let key2 = pbkdf2_derive_sha256(b"password2", salt, 10_000, 32).unwrap();
assert_ne!(key1.as_slice(), key2.as_slice());
}
#[test]
fn test_pbkdf2_sha256_different_salts() {
let password = b"same_password";
let key1 = pbkdf2_derive_sha256(password, b"salt1_16bytes!!!", 10_000, 32).unwrap();
let key2 = pbkdf2_derive_sha256(password, b"salt2_16bytes!!!", 10_000, 32).unwrap();
assert_ne!(key1.as_slice(), key2.as_slice());
}
#[test]
fn test_pbkdf2_salt_too_short() {
let result = pbkdf2_derive_sha256(b"password", b"short", 10_000, 32);
assert!(result.is_err());
}
#[test]
fn test_pbkdf2_iterations_too_low() {
let result = pbkdf2_derive_sha256(b"password", b"saltsaltsaltsalt", 100, 32);
assert!(result.is_err());
}
#[test]
fn test_pbkdf2_sha512_basic() {
let password = b"password";
let salt = b"saltsaltsaltsalt";
let key = pbkdf2_derive_sha512(password, salt, 10_000, 64).unwrap();
assert_eq!(key.len(), 64);
}
#[test]
fn test_pbkdf2_rfc_vector() {
let password = b"password";
let salt = b"salt";
let iterations = 1;
let mut output = [0u8; 20];
pbkdf2_hmac::<Sha256>(password, salt, iterations, &mut output);
let mut output2 = [0u8; 20];
pbkdf2_hmac::<Sha256>(password, salt, iterations, &mut output2);
assert_eq!(output, output2);
}
}