use crate::error::{Error, Result};
use crate::models::hash_algorithm::HashingAlgorithm;
use scrypt::scrypt as scrypt_kdf;
use scrypt::Params;
use serde::{Deserialize, Serialize};
pub const DEFAULT_OUTPUT_LEN: usize = 64;
pub fn owasp_minimum_2025() -> ScryptParams {
ScryptParams {
log_n: 17,
r: 8,
p: 1,
dk_len: DEFAULT_OUTPUT_LEN,
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub struct ScryptParams {
pub log_n: u8,
pub r: u32,
pub p: u32,
pub dk_len: usize,
}
impl Default for ScryptParams {
fn default() -> Self {
owasp_minimum_2025()
}
}
impl ScryptParams {
pub fn to_native(self) -> Result<Params> {
Params::new(self.log_n, self.r, self.p, self.dk_len)
.map_err(|e| Error::InvalidParameter(e.to_string().into()))
}
}
#[derive(
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd,
Serialize,
Deserialize,
)]
pub struct Scrypt;
impl HashingAlgorithm for Scrypt {
fn hash_password(password: &str, salt: &str) -> Result<Vec<u8>> {
Self::hash_with(password, salt, ScryptParams::default())
}
}
impl Scrypt {
pub fn hash_with(
password: &str,
salt: &str,
params: ScryptParams,
) -> Result<Vec<u8>> {
let native = params.to_native()?;
let mut output = vec![0u8; params.dk_len];
scrypt_kdf(
password.as_bytes(),
salt.as_bytes(),
&native,
&mut output,
)
.map_err(|e| {
Error::hashing(
crate::error::HashingErrorKind::Scrypt,
e.to_string(),
)
})?;
Ok(output)
}
}