use std::fmt;
use argon2::Params;
#[derive(Clone, Copy)]
pub struct PasswordConfig {
memory_kib: u32,
iterations: u32,
parallelism: u32,
}
impl PasswordConfig {
#[must_use]
pub fn new(memory_kib: u32, iterations: u32, parallelism: u32) -> Self {
Self {
memory_kib,
iterations,
parallelism,
}
}
#[must_use]
pub fn recommended() -> Self {
Self::new(19_456, 2, 1)
}
#[must_use]
pub fn memory_kib(&self) -> u32 {
self.memory_kib
}
#[must_use]
pub fn iterations(&self) -> u32 {
self.iterations
}
#[must_use]
pub fn parallelism(&self) -> u32 {
self.parallelism
}
pub(crate) fn to_params(self) -> Result<Params, argon2::Error> {
Params::new(self.memory_kib, self.iterations, self.parallelism, None)
}
}
impl fmt::Debug for PasswordConfig {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_struct("PasswordConfig")
.field("algorithm", &"argon2id")
.field("memory_kib", &self.memory_kib)
.field("iterations", &self.iterations)
.field("parallelism", &self.parallelism)
.finish()
}
}
impl Default for PasswordConfig {
fn default() -> Self {
Self::recommended()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn recommended_params_are_owasp_defaults() {
let config = PasswordConfig::recommended();
assert_eq!(config.memory_kib(), 19_456);
assert_eq!(config.iterations(), 2);
assert_eq!(config.parallelism(), 1);
}
#[test]
fn to_params_succeeds_for_recommended() {
let config = PasswordConfig::recommended();
assert!(config.to_params().is_ok());
}
#[test]
fn debug_does_not_leak_secrets() {
let config = PasswordConfig::recommended();
let debug = format!("{config:?}");
assert!(debug.contains("argon2id"));
assert!(debug.contains("19456"));
}
}