use std::ops::RangeInclusive;
use sha2::Sha256;
use crate::{
HashSetup, IntoHashSetup, consteq,
error::Result,
hash::{Hash, HashV},
internal::sha2 as sha2i,
random,
};
pub use sha2i::DEFAULT_ROUNDS;
pub use sha2i::MAX_ROUNDS;
pub use sha2i::MAX_SALT_LEN;
pub use sha2i::MIN_ROUNDS;
const SHA256_MAGIC: &str = "$5$";
const SHA256_TRANSPOSE: &[u8] = b"\x14\x0a\x00\x0b\x01\x15\x02\x16\x0c\x17\x0d\x03\x0e\x04\x18\x05\
\x19\x0f\x1a\x10\x06\x11\x07\x1b\x08\x1c\x12\x1d\x13\x09\x1e\x1f";
pub(crate) const HASH_LENGTH_MIN: usize = SHA256_MAGIC.len() + 7 + 4 + 1 + 1 + 1 + 43;
pub(crate) const HASH_LENGTH_MAX: usize = SHA256_MAGIC.len() + 7 + 9 + 1 + 64 + 1 + 43;
pub(crate) const HASH_LENGTH: RangeInclusive<usize> = HASH_LENGTH_MIN..=HASH_LENGTH_MAX;
#[inline]
fn do_sha256_crypt(pass: &[u8], salt: &str, rounds: Option<u32>) -> Result<String> {
sha2i::sha2_crypt(
pass,
salt,
rounds,
Sha256::default,
SHA256_TRANSPOSE,
SHA256_MAGIC,
)
}
#[deprecated(since = "0.2.0", note = "don't use this algorithm for new passwords")]
pub fn hash<B: AsRef<[u8]>>(pass: B) -> Result<Hash> {
let saltstr = random::gen_salt_str(MAX_SALT_LEN);
let hash = do_sha256_crypt(pass.as_ref(), &saltstr, None)?;
Ok(Hash::Sha256(HashV(hash)))
}
#[inline]
fn parse_sha256_hash(hash: &str) -> Result<HashSetup> {
sha2i::parse_sha2_hash(hash, SHA256_MAGIC)
}
#[deprecated(since = "0.2.0", note = "don't use this algorithm for new passwords")]
#[inline]
pub fn hash_with<'a, IHS, B>(param: IHS, pass: B) -> Result<Hash>
where
IHS: IntoHashSetup<'a>,
B: AsRef<[u8]>,
{
Ok(Hash::Sha256(HashV(sha2i::sha2_hash_with(
IHS::into_hash_setup(param, parse_sha256_hash)?,
pass.as_ref(),
do_sha256_crypt,
)?)))
}
#[inline]
pub fn verify<B: AsRef<[u8]>>(pass: B, hash: &str) -> bool {
#[allow(deprecated)]
consteq(hash, hash_with(hash, pass))
}
#[cfg(test)]
mod tests {
use super::HashSetup;
#[test]
#[allow(deprecated)]
fn custom() {
assert_eq!(
super::hash_with(
"$5$rounds=11858$WH1ABM5sKhxbkgCK$aTQsjPkz0rBsH3lQlJxw9HDTDXPKBxC0LlVeV69P.t1",
"test"
)
.unwrap(),
"$5$rounds=11858$WH1ABM5sKhxbkgCK$aTQsjPkz0rBsH3lQlJxw9HDTDXPKBxC0LlVeV69P.t1"
);
assert_eq!(
super::hash_with(
HashSetup {
salt: Some("WH1ABM5sKhxbkgCK"),
rounds: Some(11858)
},
"test"
)
.unwrap(),
"$5$rounds=11858$WH1ABM5sKhxbkgCK$aTQsjPkz0rBsH3lQlJxw9HDTDXPKBxC0LlVeV69P.t1"
);
}
#[test]
#[allow(deprecated)]
fn implicit_dflt_rounds() {
assert_eq!(
super::hash_with(
"$5$WH1ABM5sKhxbkgCK$sOnTVjQn1Y3EWibd8gWqqJqjH.KaFrxJE5rijqxcPp7",
"test"
)
.unwrap(),
"$5$WH1ABM5sKhxbkgCK$sOnTVjQn1Y3EWibd8gWqqJqjH.KaFrxJE5rijqxcPp7"
);
}
}