pub use mcf::{PasswordHash, PasswordHashRef};
use crate::{Params, Yescrypt, yescrypt};
use alloc::vec;
use mcf::Base64;
use password_hash::{
CustomizedPasswordHasher, Error, PasswordHasher, PasswordVerifier, Result, Version,
};
#[cfg(feature = "password-hash")]
const YESCRYPT_MCF_ID: &str = "y";
const YESCRYPT_BASE64: Base64 = Base64::Crypt;
impl CustomizedPasswordHasher<PasswordHash> for Yescrypt {
type Params = Params;
fn hash_password_customized(
&self,
password: &[u8],
salt: &[u8],
alg_id: Option<&str>,
version: Option<Version>,
params: Params,
) -> Result<PasswordHash> {
const HASH_SIZE: usize = 32;
match alg_id {
Some(YESCRYPT_MCF_ID) | None => (),
_ => return Err(Error::Algorithm),
}
if version.is_some() {
return Err(Error::Version);
}
let mut out = [0u8; HASH_SIZE];
yescrypt(password, salt, ¶ms, &mut out)?;
let mut mcf_hash = PasswordHash::from_id(YESCRYPT_MCF_ID).expect("should be valid");
mcf_hash
.push_displayable(params)
.map_err(|_| Error::EncodingInvalid)?;
mcf_hash.push_base64(salt, YESCRYPT_BASE64);
mcf_hash.push_base64(&out, YESCRYPT_BASE64);
Ok(mcf_hash)
}
}
impl PasswordHasher<PasswordHash> for Yescrypt {
fn hash_password_with_salt(&self, password: &[u8], salt: &[u8]) -> Result<PasswordHash> {
self.hash_password_customized(password, salt, None, None, self.params)
}
}
impl PasswordVerifier<PasswordHash> for Yescrypt {
fn verify_password(&self, password: &[u8], hash: &PasswordHash) -> Result<()> {
self.verify_password(password, hash.as_password_hash_ref())
}
}
impl PasswordVerifier<PasswordHashRef> for Yescrypt {
fn verify_password(&self, password: &[u8], hash: &PasswordHashRef) -> Result<()> {
if hash.id() != YESCRYPT_MCF_ID {
return Err(Error::Algorithm);
}
let mut fields = hash.fields();
let params: Params = fields
.next()
.ok_or(Error::EncodingInvalid)?
.as_str()
.parse()?;
let salt = fields
.next()
.ok_or(Error::EncodingInvalid)?
.decode_base64(YESCRYPT_BASE64)
.map_err(|_| Error::EncodingInvalid)?;
let expected = fields
.next()
.ok_or(Error::EncodingInvalid)?
.decode_base64(YESCRYPT_BASE64)
.map_err(|_| Error::EncodingInvalid)?;
if fields.next().is_some() {
return Err(Error::EncodingInvalid);
}
let mut actual = vec![0u8; expected.len()];
yescrypt(password, &salt, ¶ms, &mut actual)?;
if ctutils::CtEq::ct_ne(actual.as_slice(), &expected).into() {
return Err(Error::PasswordInvalid);
}
Ok(())
}
}
impl PasswordVerifier<str> for Yescrypt {
fn verify_password(&self, password: &[u8], hash: &str) -> Result<()> {
let hash = PasswordHashRef::new(hash).map_err(|_| Error::EncodingInvalid)?;
self.verify_password(password, hash)
}
}