use crate::constants::{PBKDF2_ITERATIONS, SALT_SIZE};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct KeyDerivationParams {
pub salt: [u8; SALT_SIZE],
pub iterations: u32,
}
impl Default for KeyDerivationParams {
fn default() -> Self {
let mut salt = [0u8; SALT_SIZE];
rand::RngCore::fill_bytes(&mut rand::thread_rng(), &mut salt);
Self {
salt,
iterations: PBKDF2_ITERATIONS,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_key_derivation_params_default() {
let params = KeyDerivationParams::default();
assert_eq!(params.iterations, PBKDF2_ITERATIONS);
assert_eq!(params.salt.len(), SALT_SIZE);
}
#[test]
fn test_default_salt_is_random() {
let params1 = KeyDerivationParams::default();
let params2 = KeyDerivationParams::default();
assert_ne!(params1.salt, params2.salt);
assert_ne!(params1.salt, [0u8; SALT_SIZE]);
assert_ne!(params2.salt, [0u8; SALT_SIZE]);
}
#[test]
fn test_key_derivation_params_clone() {
let params = KeyDerivationParams::default();
let cloned = params.clone();
assert_eq!(params, cloned);
assert_eq!(params.salt, cloned.salt);
assert_eq!(params.iterations, cloned.iterations);
}
#[test]
fn test_key_derivation_params_equality() {
let params1 = KeyDerivationParams {
salt: [42u8; SALT_SIZE],
iterations: 100_000,
};
let params2 = KeyDerivationParams {
salt: [42u8; SALT_SIZE],
iterations: 100_000,
};
let params3 = KeyDerivationParams {
salt: [43u8; SALT_SIZE],
iterations: 100_000,
};
assert_eq!(params1, params2);
assert_ne!(params1, params3);
}
#[test]
fn test_key_derivation_params_serialization() {
let params = KeyDerivationParams {
salt: [123u8; SALT_SIZE],
iterations: 200_000,
};
let json = serde_json::to_string(¶ms).expect("Serialization failed");
let deserialized: KeyDerivationParams =
serde_json::from_str(&json).expect("Deserialization failed");
assert_eq!(params, deserialized);
assert_eq!(params.salt, deserialized.salt);
assert_eq!(params.iterations, deserialized.iterations);
}
#[test]
fn test_key_derivation_params_debug() {
let params = KeyDerivationParams {
salt: [1u8; SALT_SIZE],
iterations: 150_000,
};
let debug_str = format!("{params:?}");
assert!(debug_str.contains("KeyDerivationParams"));
assert!(debug_str.contains("salt"));
assert!(debug_str.contains("iterations"));
}
#[test]
fn test_iterations_constant_is_reasonable() {
#[allow(clippy::assertions_on_constants)]
{
assert!(
PBKDF2_ITERATIONS >= 100_000,
"Iterations too low for security"
);
}
#[allow(clippy::assertions_on_constants)]
{
assert!(
PBKDF2_ITERATIONS <= 10_000_000,
"Iterations too high for usability"
);
}
}
#[test]
fn test_salt_size_is_reasonable() {
#[allow(clippy::assertions_on_constants)]
{
assert!(SALT_SIZE >= 16, "Salt size too small for security");
}
#[allow(clippy::assertions_on_constants)]
{
assert!(SALT_SIZE <= 64, "Salt size unnecessarily large");
}
}
#[test]
fn test_manual_construction() {
let custom_salt = [99u8; SALT_SIZE];
let custom_iterations = 500_000;
let params = KeyDerivationParams {
salt: custom_salt,
iterations: custom_iterations,
};
assert_eq!(params.salt, custom_salt);
assert_eq!(params.iterations, custom_iterations);
}
#[test]
fn test_different_defaults_have_different_salts() {
let instances: Vec<KeyDerivationParams> =
(0..5).map(|_| KeyDerivationParams::default()).collect();
for i in 0..instances.len() {
for j in (i + 1)..instances.len() {
assert_ne!(
instances[i].salt, instances[j].salt,
"Salts at indices {i} and {j} should be different"
);
}
}
}
}