#[cfg(feature = "non-fips")]
use argon2::Argon2;
#[cfg(not(feature = "non-fips"))]
use openssl::{hash::MessageDigest, pkcs5::pbkdf2_hmac};
use super::secret::Secret;
#[cfg(not(feature = "non-fips"))]
use crate::crypto_bail;
use crate::error::CryptoError;
pub const FIPS_MIN_SALT_SIZE: usize = 16;
pub const FIPS_HLEN: usize = 512;
pub const FIPS_MIN_KLEN: usize = 112;
pub const FIPS_MAX_KLEN: usize = ((1 << 32) - 1) * FIPS_HLEN;
pub const FIPS_MIN_ITER: usize = 210_000;
#[cfg(not(feature = "non-fips"))]
pub fn derive_key_from_password<const LENGTH: usize>(
salt: &[u8; FIPS_MIN_SALT_SIZE],
password: &[u8],
) -> Result<Secret<LENGTH>, CryptoError> {
if LENGTH * 8 < FIPS_MIN_KLEN || LENGTH * 8 > FIPS_MAX_KLEN {
crypto_bail!("Password derivation error: wrong output length argument, got {LENGTH}")
}
let mut output_key_material = Secret::<LENGTH>::new();
pbkdf2_hmac(
password,
salt,
FIPS_MIN_ITER,
MessageDigest::sha512(),
output_key_material.as_mut(),
)?;
Ok(output_key_material)
}
#[cfg(feature = "non-fips")]
pub fn derive_key_from_password<const LENGTH: usize>(
salt: &[u8; FIPS_MIN_SALT_SIZE],
password: &[u8],
) -> Result<Secret<LENGTH>, CryptoError> {
let mut output_key_material = Secret::<LENGTH>::new();
Argon2::default()
.hash_password_into(password, salt, output_key_material.as_mut())
.map_err(|e| CryptoError::Derivation(e.to_string()))?;
Ok(output_key_material)
}
#[test]
#[expect(clippy::unwrap_used)]
fn test_password_derivation() {
#[cfg(not(feature = "non-fips"))]
openssl::provider::Provider::load(None, "fips").unwrap();
let salt = b"rediswithfindex_";
let my_weak_password = b"doglover1234".to_vec();
let secure_mk = derive_key_from_password::<32>(salt, &my_weak_password).unwrap();
assert_eq!(secure_mk.len(), 32);
}
#[test]
#[allow(clippy::unwrap_used)]
#[cfg(all(not(feature = "non-fips"), not(target_os = "windows")))]
fn test_password_derivation_bad_size() {
const BIG_KEY_LENGTH: usize = (((1 << 32) - 1) * 512) / 8 + 1;
let salt = b"rediswithfindex_";
let my_weak_password = b"123princ3ss".to_vec();
let secure_mk_res = derive_key_from_password::<13>(salt, &my_weak_password);
secure_mk_res.unwrap_err();
let secure_mk_res = derive_key_from_password::<BIG_KEY_LENGTH>(salt, &my_weak_password);
secure_mk_res.unwrap_err();
}