use crate::{Error, Result};
use core::num::NonZeroU32;
#[cfg(feature = "password-hash")]
use password_hash::{errors::InvalidValue, ParamsString, PasswordHash};
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Params {
pub s_cost: NonZeroU32,
pub t_cost: NonZeroU32,
pub p_cost: NonZeroU32,
}
impl Params {
pub const DEFAULT_S_COST: u32 = 1024;
pub const DEFAULT_T_COST: u32 = 3;
pub const DEFAULT_P_COST: u32 = 1;
pub fn new(s_cost: u32, t_cost: u32, p_cost: u32) -> Result<Self> {
Ok(Self {
s_cost: NonZeroU32::new(s_cost).ok_or(Error::MemoryTooLittle)?,
t_cost: NonZeroU32::new(t_cost).ok_or(Error::TimeTooSmall)?,
p_cost: NonZeroU32::new(p_cost).ok_or(Error::ThreadsTooFew)?,
})
}
}
impl Default for Params {
fn default() -> Self {
Self::new(
Self::DEFAULT_S_COST,
Self::DEFAULT_T_COST,
Self::DEFAULT_P_COST,
)
.unwrap()
}
}
#[cfg(feature = "password-hash")]
#[cfg_attr(docsrs, doc(cfg(feature = "password-hash")))]
impl<'a> TryFrom<&'a PasswordHash<'a>> for Params {
type Error = password_hash::Error;
fn try_from(hash: &'a PasswordHash<'a>) -> password_hash::Result<Self> {
let mut params = Self::default();
for (ident, value) in hash.params.iter() {
match ident.as_str() {
"s" => {
params.s_cost = NonZeroU32::new(value.decimal()?)
.ok_or_else(|| InvalidValue::TooShort.param_error())?;
}
"t" => {
params.t_cost = NonZeroU32::new(value.decimal()?)
.ok_or_else(|| InvalidValue::TooShort.param_error())?;
}
"p" => {
params.p_cost = NonZeroU32::new(value.decimal()?)
.ok_or_else(|| InvalidValue::TooShort.param_error())?;
}
_ => return Err(password_hash::Error::ParamNameInvalid),
}
}
Ok(params)
}
}
#[cfg(feature = "password-hash")]
#[cfg_attr(docsrs, doc(cfg(feature = "password-hash")))]
impl TryFrom<Params> for ParamsString {
type Error = password_hash::Error;
fn try_from(params: Params) -> password_hash::Result<ParamsString> {
ParamsString::try_from(¶ms)
}
}
#[cfg(feature = "password-hash")]
#[cfg_attr(docsrs, doc(cfg(feature = "password-hash")))]
impl TryFrom<&Params> for ParamsString {
type Error = password_hash::Error;
fn try_from(params: &Params) -> password_hash::Result<ParamsString> {
let mut output = ParamsString::new();
output.add_decimal("s", params.s_cost.get())?;
output.add_decimal("t", params.t_cost.get())?;
output.add_decimal("p", params.p_cost.get())?;
Ok(output)
}
}