use super::{Result, HashSetup, IntoHashSetup, consteq};
use crate::des_crypt::bsdi_crypt;
use crate::enc_dec::decode_val;
use crate::error::Error;
use crate::random;
use crate::parse::{self, HashIterator};
const MIN_ROUNDS: u32 = 1;
const MAX_ROUNDS: u32 = (1 << 24) - 1;
pub const DEFAULT_ROUNDS: u32 = 7250;
pub const SALT_LEN: usize = 4;
const ROUNDS_LEN: usize = 4;
#[deprecated(since="0.2.0", note="don't use this algorithm for new passwords")]
pub fn hash<B: AsRef<[u8]>>(pass: B) -> Result<String> {
let saltstr = random::gen_salt_str(SALT_LEN);
bsdi_crypt(pass.as_ref(), &saltstr, DEFAULT_ROUNDS)
}
fn parse_bsdi_hash(hash: &str) -> Result<HashSetup> {
let mut hs = parse::HashSlice::new(hash);
if hs.take(1).unwrap_or("X") != "_" {
return Err(Error::InvalidHashString);
}
let rounds = if let Some(rounds_enc) = hs.take(ROUNDS_LEN) {
decode_val(rounds_enc, SALT_LEN)?
} else {
return Err(Error::InvalidHashString);
};
let salt = if let Some(salt) = hs.take(SALT_LEN) {
salt
} else {
return Err(Error::InvalidHashString);
};
Ok(HashSetup { salt: Some(salt), rounds: Some(rounds) })
}
#[deprecated(since="0.2.0", note="don't use this algorithm for new passwords")]
pub fn hash_with<'a, IHS, B>(param: IHS, pass: B) -> Result<String>
where IHS: IntoHashSetup<'a>, B: AsRef<[u8]>
{
let hs = IHS::into_hash_setup(param, parse_bsdi_hash)?;
let rounds = if let Some(r) = hs.rounds {
if r < MIN_ROUNDS || r > MAX_ROUNDS {
return Err(Error::InvalidRounds);
}
r
} else { DEFAULT_ROUNDS };
if hs.salt.is_some() {
bsdi_crypt(pass.as_ref(), hs.salt.unwrap(), rounds)
} else {
let saltstr = random::gen_salt_str(SALT_LEN);
bsdi_crypt(pass.as_ref(), &saltstr, rounds)
}
}
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(HashSetup { salt: Some("K0Ay"), rounds: None }, "password").unwrap(),
"_Gl/.K0Ay.aosctsbJ1k");
assert_eq!(super::hash_with("_Gl/.K0Ay.aosctsbJ1k", "password").unwrap(), "_Gl/.K0Ay.aosctsbJ1k");
}
#[test]
#[allow(deprecated)]
#[should_panic(expected="value: InvalidRounds")]
fn bad_rounds() {
let _ = super::hash_with(HashSetup { salt: Some("K0Ay"), rounds: Some(0) }, "password").unwrap();
}
}