use crate::hash::HashFunction;
use crate::pass::error::Error;
use crate::pass::phc::PHCData;
use crate::pass::{
Algorithm, DEFAULT_USER_VERSION, Hasher, INTERNAL_VERSION, LengthCalculationMethod,
Normalization, PasswordStorageStandard, XHMAC, std_default, std_nist,
};
use std::collections::HashMap;
use std::str::FromStr;
macro_rules! get_pepper {
($pepper: ident) => {
$pepper
.as_ref()
.ok_or(Error::InvalidPasswordFormat)?
.to_vec()
};
}
pub struct HashBuilder {
pub(crate) standard: PasswordStorageStandard,
pub(crate) normalization: Normalization,
pub(crate) min_len: usize,
pub(crate) max_len: usize,
pub(crate) algorithm: Algorithm,
pub(crate) parameters: HashMap<String, String>,
pub(crate) ref_salt: Option<Vec<u8>>,
pub(crate) ref_hash: Option<Vec<u8>>,
pub(crate) salt_len: usize,
pub(crate) length_calculation: LengthCalculationMethod,
pub(crate) version: usize,
pub(crate) xhmac: XHMAC,
pub(crate) xhmax_alg: HashFunction,
}
impl Default for HashBuilder {
fn default() -> Self {
Self::new()
}
}
impl HashBuilder {
pub fn new() -> Self {
Self::new_std(PasswordStorageStandard::NoStandard)
}
pub fn new_std(std: PasswordStorageStandard) -> Self {
match std {
PasswordStorageStandard::NoStandard => Self {
standard: PasswordStorageStandard::NoStandard,
normalization: std_default::DEFAULT_NORMALIZATION,
min_len: std_default::DEFAULT_PASSWORD_MIN_LEN,
max_len: std_default::DEFAULT_PASSWORD_MAX_LEN,
algorithm: std_default::DEFAULT_ALGORITHM,
parameters: HashMap::new(),
ref_salt: None,
ref_hash: None,
salt_len: std_default::DEFAULT_SALT_LEN,
length_calculation: std_default::DEFAULT_LENGTH_CALCULATION,
version: DEFAULT_USER_VERSION + INTERNAL_VERSION,
xhmac: XHMAC::None,
xhmax_alg: std_default::DEFAULT_XHMAC_ALGORITHM,
},
PasswordStorageStandard::Nist80063b => Self {
standard: PasswordStorageStandard::Nist80063b,
normalization: std_nist::DEFAULT_NORMALIZATION,
min_len: std_nist::DEFAULT_PASSWORD_MIN_LEN,
max_len: std_nist::DEFAULT_PASSWORD_MAX_LEN,
algorithm: std_nist::DEFAULT_ALGORITHM,
parameters: HashMap::new(),
ref_salt: None,
ref_hash: None,
salt_len: std_nist::DEFAULT_SALT_LEN,
length_calculation: std_nist::DEFAULT_LENGTH_CALCULATION,
version: DEFAULT_USER_VERSION + INTERNAL_VERSION,
xhmac: XHMAC::None,
xhmax_alg: std_nist::DEFAULT_XHMAC_ALGORITHM,
},
}
}
pub fn from_phc(data: &str) -> Result<Hasher, Error> {
Self::from_phc_internal(data, None)
}
pub fn from_phc_xhmac(data: &str, pepper: &[u8]) -> Result<Hasher, Error> {
Self::from_phc_internal(data, Some(pepper.to_vec()))
}
fn from_phc_internal(data: &str, pepper: Option<Vec<u8>>) -> Result<Hasher, Error> {
let mut phc = match PHCData::from_str(data) {
Ok(v) => v,
Err(_) => return Err(Error::InvalidPasswordFormat),
};
let lc = match phc.parameters.remove("len-calc") {
Some(v) => match v.as_str() {
"bytes" => LengthCalculationMethod::Bytes,
"chars" => LengthCalculationMethod::Characters,
"codepoints" => LengthCalculationMethod::CodePoints,
"graphemes" => LengthCalculationMethod::Graphemes,
_ => return Err(Error::InvalidPasswordFormat),
},
None => LengthCalculationMethod::CodePoints,
};
let norm = match phc.parameters.remove("norm") {
Some(v) => match v.as_str() {
"nfd" => Normalization::Nfd,
"nfkd" => Normalization::Nfkd,
"nfc" => Normalization::Nfc,
"nfkc" => Normalization::Nfkc,
"none" => Normalization::None,
_ => return Err(Error::InvalidPasswordFormat),
},
None => Normalization::Nfkc,
};
let max_l = match phc.parameters.remove("pmax") {
Some(v) => match v.parse::<usize>() {
Ok(l) => l,
Err(_) => return Err(Error::InvalidPasswordFormat),
},
None => std_default::DEFAULT_PASSWORD_MAX_LEN,
};
let min_l = match phc.parameters.remove("pmin") {
Some(v) => match v.parse::<usize>() {
Ok(l) => l,
Err(_) => return Err(Error::InvalidPasswordFormat),
},
None => std_default::DEFAULT_PASSWORD_MIN_LEN,
};
let version = match phc.parameters.remove("ver") {
Some(v) => match v.parse::<usize>() {
Ok(l) => l,
Err(_) => return Err(Error::InvalidPasswordFormat),
},
None => DEFAULT_USER_VERSION + INTERNAL_VERSION,
};
let xhmac = match phc.parameters.remove("xhmac") {
Some(when) => match when.to_lowercase().as_str() {
"before" => XHMAC::Before(get_pepper!(pepper)),
"after" => XHMAC::After(get_pepper!(pepper)),
"none" => XHMAC::None,
_ => return Err(Error::InvalidPasswordFormat),
},
None => XHMAC::None,
};
if xhmac == XHMAC::None && pepper.is_some() {
return Err(Error::InvalidPasswordFormat);
}
let xhmax_alg = match phc.parameters.remove("xhmac-alg") {
Some(alg_str) => {
HashFunction::from_str(&alg_str).map_err(|_| Error::InvalidPasswordFormat)?
}
None => std_default::DEFAULT_XHMAC_ALGORITHM,
};
let hash_builder = Self {
standard: PasswordStorageStandard::NoStandard,
normalization: norm,
min_len: min_l,
max_len: max_l,
algorithm: match phc.id.as_str() {
"argon2" => Algorithm::Argon2,
"pbkdf2" => Algorithm::Pbkdf2,
_ => return Err(Error::InvalidPasswordFormat),
},
parameters: phc.parameters.clone(),
ref_hash: phc.hash,
salt_len: match &phc.salt {
Some(s) => s.len(),
None => std_default::DEFAULT_SALT_LEN,
},
ref_salt: phc.salt,
length_calculation: lc,
version,
xhmac,
xhmax_alg,
};
hash_builder.finalize()
}
pub fn finalize(&self) -> Result<Hasher, Error> {
match self.standard {
PasswordStorageStandard::Nist80063b => {
if !std_nist::is_valid(self) {
return Err(Error::InvalidPasswordFormat);
}
}
PasswordStorageStandard::NoStandard => {}
}
Ok(Hasher {
normalization: self.normalization,
min_len: self.min_len,
max_len: self.max_len,
algorithm: self.algorithm,
parameters: self.parameters.clone(),
ref_salt: self.ref_salt.clone(),
ref_hash: self.ref_hash.clone(),
salt_len: self.salt_len,
length_calculation: self.length_calculation,
version: self.version,
xhmac: self.xhmac.clone(),
xhmax_alg: self.xhmax_alg,
})
}
pub fn normalization(&mut self, normalization: Normalization) -> &mut Self {
self.normalization = normalization;
self
}
pub fn algorithm(&mut self, algorithm: Algorithm) -> &mut Self {
self.algorithm = algorithm;
self.parameters = HashMap::new();
self
}
pub fn length_calculation(&mut self, method: LengthCalculationMethod) -> &mut Self {
self.length_calculation = method;
self
}
pub fn salt_len(&mut self, len: usize) -> &mut Self {
self.salt_len = len;
self
}
pub fn min_len(&mut self, len: usize) -> &mut Self {
self.min_len = len;
self
}
pub fn max_len(&mut self, len: usize) -> &mut Self {
self.max_len = len;
self
}
pub fn add_param(&mut self, key: &str, value: &str) -> &mut Self {
self.parameters.insert(key.to_string(), value.to_string());
self
}
pub fn version(&mut self, version: usize) -> &mut Self {
self.version = version + INTERNAL_VERSION;
self
}
pub fn xhmac(&mut self, hash_func: HashFunction) -> &mut Self {
self.xhmax_alg = hash_func;
self
}
pub fn xhmac_before(&mut self, pepper: &[u8]) -> &mut Self {
self.xhmac = XHMAC::Before(pepper.to_vec());
self
}
pub fn xhmac_after(&mut self, pepper: &[u8]) -> &mut Self {
self.xhmac = XHMAC::After(pepper.to_vec());
self
}
}