mod password_hash_v1;
use super::DataType;
use super::Error;
use super::Header;
use super::HeaderType;
use super::PasswordHashSubtype;
pub use super::PasswordHashVersion;
use super::Result;
use password_hash_v1::PasswordHashV1;
use std::borrow::Borrow;
use std::convert::TryFrom;
#[cfg(feature = "fuzz")]
use arbitrary::Arbitrary;
#[derive(Clone, Debug)]
#[cfg_attr(feature = "fuzz", derive(Arbitrary))]
pub struct PasswordHash {
pub(crate) header: Header<PasswordHash>,
payload: PasswordHashPayload,
}
impl HeaderType for PasswordHash {
type Version = PasswordHashVersion;
type Subtype = PasswordHashSubtype;
fn data_type() -> DataType {
DataType::PasswordHash
}
}
#[derive(Clone, Debug)]
#[cfg_attr(feature = "fuzz", derive(Arbitrary))]
enum PasswordHashPayload {
V1(PasswordHashV1),
}
pub fn hash_password(
password: &[u8],
iterations: u32,
version: PasswordHashVersion,
) -> PasswordHash {
let mut header = Header::default();
let payload = match version {
PasswordHashVersion::V1 | PasswordHashVersion::Latest => {
header.version = PasswordHashVersion::V1;
PasswordHashPayload::V1(PasswordHashV1::hash_password(password, iterations))
}
};
PasswordHash { header, payload }
}
impl PasswordHash {
pub fn verify_password(&self, password: &[u8]) -> bool {
match &self.payload {
PasswordHashPayload::V1(x) => x.verify_password(password),
}
}
}
impl From<PasswordHash> for Vec<u8> {
fn from(data: PasswordHash) -> Self {
let mut header: Self = data.header.borrow().into();
let mut payload: Self = data.payload.into();
header.append(&mut payload);
header
}
}
impl TryFrom<&[u8]> for PasswordHash {
type Error = Error;
fn try_from(data: &[u8]) -> Result<Self> {
if data.len() < Header::len() {
return Err(Error::InvalidLength);
};
let header = Header::try_from(&data[0..Header::len()])?;
let payload = match header.version {
PasswordHashVersion::V1 => {
PasswordHashPayload::V1(PasswordHashV1::try_from(&data[Header::len()..])?)
}
_ => return Err(Error::UnknownVersion),
};
Ok(Self { header, payload })
}
}
impl From<PasswordHashPayload> for Vec<u8> {
fn from(data: PasswordHashPayload) -> Self {
match data {
PasswordHashPayload::V1(x) => x.into(),
}
}
}
#[test]
fn password_test() {
let pass = "thisisaveryveryverystrongPa$$w0rd , //".as_bytes();
let iterations = 10u32;
let hash = hash_password(pass, iterations, PasswordHashVersion::Latest);
assert!(hash.verify_password(pass));
assert!(!hash.verify_password("averybadpassword".as_bytes()))
}