use crate::hash::HashFunction;
use crate::key::KeyBuilder;
use crate::pass::error::Error;
use crate::pass::{HashingFunction, Normalization, std_default};
use hmac::Hmac;
use pbkdf2::pbkdf2;
use sha1::Sha1;
use sha2::{Sha224, Sha256, Sha384, Sha512, Sha512_224, Sha512_256};
use sha3::{Keccak224, Keccak256, Keccak384, Keccak512, Sha3_224, Sha3_256, Sha3_384, Sha3_512};
use std::collections::HashMap;
use std::str::FromStr;
pub const DEFAULT_HASH_FUNCTION: HashFunction = HashFunction::Sha512;
pub const DEFAULT_ITER: u32 = 45_000;
const MIN_SALT_LENGTH: usize = 4; const MAX_SALT_LENGTH: usize = 256; const MIN_ITER: u32 = 10_000;
const MAX_ITER: u32 = 200_000;
macro_rules! process_pbkdf2 {
($obj: ident, $input: ident, $hash: ty, $len: expr) => {{
let mut out = [0u8; $len];
pbkdf2::<Hmac<$hash>>($input, $obj.salt.as_slice(), $obj.nb_iter, &mut out[..$len])
.unwrap();
out.to_vec()
}};
}
pub struct Pbkdf2Hash {
hash_function: HashFunction,
nb_iter: u32,
salt: Vec<u8>,
norm: Normalization,
}
impl Pbkdf2Hash {
pub fn new() -> Self {
Self {
hash_function: DEFAULT_HASH_FUNCTION,
nb_iter: DEFAULT_ITER,
salt: KeyBuilder::new()
.size(std_default::DEFAULT_SALT_LEN)
.as_vec(),
norm: Normalization::Nfkc,
}
}
}
impl HashingFunction for Pbkdf2Hash {
fn get_id(&self) -> String {
"pbkdf2".to_string()
}
fn get_parameters(&self) -> HashMap<String, String> {
let mut params = HashMap::new();
set_normalization!(self, norm, params, "norm".to_string());
params.insert("iter".to_string(), self.nb_iter.to_string());
params.insert(
"hmac".to_string(),
self.hash_function.to_string().to_lowercase(),
);
params
}
fn set_parameter(&mut self, name: &str, value: &str) -> Result<(), Error> {
match name {
"iter" => match value.parse::<u32>() {
Ok(i) => match i {
MIN_ITER..=MAX_ITER => {
self.nb_iter = i;
Ok(())
}
_ => Err(Error::InvalidPasswordFormat),
},
Err(_) => Err(Error::InvalidPasswordFormat),
},
"hash" | "hmac" => match HashFunction::from_str(value) {
Ok(h) => {
self.hash_function = h;
Ok(())
}
Err(_) => Err(Error::InvalidPasswordFormat),
},
_ => Err(Error::InvalidPasswordFormat),
}
}
fn get_salt(&self) -> Option<Vec<u8>> {
Some(self.salt.clone())
}
fn set_salt(&mut self, salt: Vec<u8>) -> Result<(), Error> {
match salt.len() {
MIN_SALT_LENGTH..=MAX_SALT_LENGTH => {
self.salt = salt;
Ok(())
}
_ => Err(Error::InvalidPasswordFormat),
}
}
fn set_salt_len(&mut self, salt_len: usize) -> Result<(), Error> {
let salt = KeyBuilder::new().size(salt_len).as_vec();
self.set_salt(salt)
}
fn set_normalization(&mut self, norm: Normalization) -> Result<(), Error> {
self.norm = norm;
Ok(())
}
fn hash(&self, input: &[u8]) -> Vec<u8> {
match self.hash_function {
HashFunction::Sha1 => process_pbkdf2!(self, input, Sha1, 20),
HashFunction::Sha224 => process_pbkdf2!(self, input, Sha224, 28),
HashFunction::Sha256 => process_pbkdf2!(self, input, Sha256, 32),
HashFunction::Sha384 => process_pbkdf2!(self, input, Sha384, 48),
HashFunction::Sha512 => process_pbkdf2!(self, input, Sha512, 64),
HashFunction::Sha512Trunc224 => process_pbkdf2!(self, input, Sha512_224, 28),
HashFunction::Sha512Trunc256 => process_pbkdf2!(self, input, Sha512_256, 32),
HashFunction::Keccak224 => process_pbkdf2!(self, input, Keccak224, 32),
HashFunction::Keccak256 => process_pbkdf2!(self, input, Keccak256, 32),
HashFunction::Keccak384 => process_pbkdf2!(self, input, Keccak384, 32),
HashFunction::Keccak512 => process_pbkdf2!(self, input, Keccak512, 32),
HashFunction::Sha3_224 => process_pbkdf2!(self, input, Sha3_224, 28),
HashFunction::Sha3_256 => process_pbkdf2!(self, input, Sha3_256, 32),
HashFunction::Sha3_384 => process_pbkdf2!(self, input, Sha3_384, 48),
HashFunction::Sha3_512 => process_pbkdf2!(self, input, Sha3_512, 64),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_id() {
let lst = [
Pbkdf2Hash::new(),
Pbkdf2Hash {
hash_function: HashFunction::Sha1,
nb_iter: 42,
salt: vec![0, 1, 2, 3, 4, 5],
norm: Normalization::Nfkc,
},
Pbkdf2Hash {
hash_function: HashFunction::Sha256,
nb_iter: 42,
salt: vec![0, 1, 2, 3, 4, 5],
norm: Normalization::Nfkc,
},
Pbkdf2Hash {
hash_function: HashFunction::Sha512,
nb_iter: 42,
salt: vec![0, 1, 2, 3, 4, 5],
norm: Normalization::Nfkc,
},
];
for h in lst.iter() {
assert_eq!(h.get_id(), "pbkdf2".to_string());
}
}
#[test]
fn test_get_salt() {
let h = Pbkdf2Hash {
hash_function: HashFunction::Sha1,
nb_iter: 42,
salt: vec![0, 1, 2, 3, 4, 5],
norm: Normalization::Nfkc,
};
assert_eq!(h.get_salt().unwrap(), vec![0, 1, 2, 3, 4, 5]);
}
#[test]
fn test_default_salt_len() {
let h = Pbkdf2Hash::new();
assert!(h.get_salt().unwrap().len() >= 4);
}
#[test]
fn test_salt_randomness() {
assert_ne!(
Pbkdf2Hash::new().get_salt().unwrap(),
Pbkdf2Hash::new().get_salt().unwrap()
);
}
#[test]
fn test_default_iterations() {
assert!(Pbkdf2Hash::new().nb_iter >= 10000);
}
#[test]
fn test_vectors() {
let lst = [
(
"sha1",
1,
"salt",
"password",
vec![
0x0c as u8, 0x60, 0xc8, 0x0f, 0x96, 0x1f, 0x0e, 0x71, 0xf3, 0xa9, 0xb5, 0x24,
0xaf, 0x60, 0x12, 0x06, 0x2f, 0xe0, 0x37, 0xa6,
],
),
(
"sha1",
2,
"salt",
"password",
vec![
0xea, 0x6c, 0x01, 0x4d, 0xc7, 0x2d, 0x6f, 0x8c, 0xcd, 0x1e, 0xd9, 0x2a, 0xce,
0x1d, 0x41, 0xf0, 0xd8, 0xde, 0x89, 0x57,
],
),
(
"sha1",
4096,
"salt",
"password",
vec![
0x4b, 0x00, 0x79, 0x01, 0xb7, 0x65, 0x48, 0x9a, 0xbe, 0xad, 0x49, 0xd9, 0x26,
0xf7, 0x21, 0xd0, 0x65, 0xa4, 0x29, 0xc1,
],
),
(
"sha224",
498,
"msEf7FpL",
"DigIpIXYIwc",
vec![
0x3, 0xe8, 0x4b, 0xa7, 0x57, 0xd1, 0xcd, 0xc8, 0xd5, 0x97, 0x2, 0xb, 0xae,
0x86, 0xd1, 0x70, 0xec, 0x45, 0xfa, 0xf7, 0xd9, 0xb8, 0x67, 0x28, 0x5b, 0xad,
0xf1, 0x3e,
],
),
(
"sha224",
2741,
"y9irX",
"JmvZv6Ut",
vec![
0xa6, 0xf4, 0x7, 0x6c, 0xa3, 0xd3, 0x6a, 0xcd, 0x23, 0x86, 0xc6, 0xd1, 0x57,
0x93, 0x88, 0x3c, 0x1e, 0x51, 0x54, 0xcc, 0xfb, 0x3f, 0x97, 0x31, 0x92, 0x30,
0x72, 0x37,
],
),
(
"sha256",
3853,
"CHhs6n",
"DAfuHjm77",
vec![
0x4b, 0x99, 0xc5, 0x91, 0x14, 0xc, 0x6, 0xa3, 0x16, 0x4e, 0x1e, 0xd2, 0xbc,
0x99, 0x79, 0x2a, 0x74, 0x7f, 0x5d, 0xb4, 0xe0, 0xf8, 0xaf, 0xae, 0xbe, 0x79,
0xea, 0x6d, 0xe4, 0x5c, 0x53, 0xc0,
],
),
(
"sha256",
3590,
"GJd4x5G",
"2KJo38IJsfRH",
vec![
0x2d, 0xbf, 0x2d, 0xf5, 0xee, 0xe1, 0xe7, 0x99, 0x8b, 0x79, 0xc3, 0x69, 0xb4,
0x1f, 0xa8, 0x51, 0x9f, 0xa1, 0x7f, 0x51, 0x63, 0x4f, 0xbd, 0xbf, 0x7d, 0xef,
0x9, 0x8f, 0xc4, 0xe1, 0x34, 0xc3,
],
),
(
"sha384",
480,
"tKVt",
"KdNomtQ4d",
vec![
0x0, 0x56, 0x8b, 0x64, 0xab, 0xf9, 0x26, 0x60, 0xbb, 0x2b, 0xa8, 0x5d, 0xca,
0xc, 0xfb, 0xc2, 0xa0, 0x9c, 0xf6, 0x9, 0x61, 0xba, 0x6, 0x2b, 0x79, 0xd9,
0x8d, 0xd, 0x97, 0x63, 0xe5, 0x20, 0xd7, 0xd, 0xe1, 0xae, 0x2b, 0xb0, 0x75,
0x1a, 0x13, 0x14, 0xea, 0x44, 0xf0, 0xb7, 0x91, 0x8,
],
),
(
"sha384",
3388,
"G3KX",
"OHNbhPKuE",
vec![
0x89, 0xcb, 0x4c, 0xf8, 0xe4, 0xa8, 0x43, 0x7d, 0x6d, 0xef, 0xdb, 0x1f, 0x1f,
0x66, 0x21, 0xaa, 0xbd, 0x8f, 0x19, 0xeb, 0x9a, 0xc9, 0xbb, 0xc5, 0x64, 0xd2,
0xc9, 0xf, 0x57, 0x6e, 0xd9, 0xfd, 0xe8, 0xf1, 0x6c, 0x36, 0xda, 0x14, 0xa9,
0x23, 0xa3, 0x92, 0x10, 0x42, 0xff, 0x8d, 0x44, 0x63,
],
),
(
"sha512",
2394,
"oQuyuv3Q",
"80gfY4kIump",
vec![
0x5f, 0x1a, 0x23, 0x65, 0x2e, 0xd1, 0xa7, 0x98, 0xf3, 0xa2, 0x7d, 0xd9, 0x22,
0x83, 0x1e, 0xa5, 0xdb, 0x63, 0xe4, 0xcb, 0xff, 0x5a, 0x1, 0xe3, 0x4, 0x8f,
0x9, 0x1b, 0x7a, 0x71, 0x7b, 0x2e, 0x44, 0x99, 0x50, 0xa0, 0x45, 0x74, 0x41,
0x57, 0x5e, 0xbc, 0xf2, 0xb8, 0xfd, 0x54, 0xcc, 0x16, 0x88, 0x6, 0x1d, 0x4f,
0x8d, 0x67, 0xa, 0xad, 0xbb, 0xff, 0x32, 0x36, 0xc8, 0x9d, 0x9e, 0x7a,
],
),
(
"sha512",
1605,
"Ejj2M0Mo",
"LdUEx0sZfn7X",
vec![
0x87, 0x97, 0x2, 0x55, 0xef, 0x70, 0x99, 0x16, 0xb6, 0x99, 0x99, 0xa2, 0xd8,
0x7f, 0x5b, 0xaf, 0x2, 0x8c, 0xc3, 0x5, 0x8b, 0x3f, 0xba, 0xec, 0x7e, 0x79,
0xe6, 0xed, 0xdd, 0x28, 0x67, 0xcb, 0xb, 0xc9, 0x42, 0x1f, 0x56, 0xdf, 0xee,
0x64, 0xd1, 0x5c, 0x8a, 0xac, 0xc5, 0x15, 0x3b, 0x29, 0x18, 0xe5, 0x92, 0x50,
0x78, 0xc8, 0x7e, 0x67, 0x48, 0xf6, 0x65, 0x24, 0x48, 0xb5, 0xce, 0x2f,
],
),
];
for &(func, nbi, salt, key, ref result) in lst.iter() {
let h = Pbkdf2Hash {
hash_function: match func {
"sha1" => HashFunction::Sha1,
"sha224" => HashFunction::Sha224,
"sha256" => HashFunction::Sha256,
"sha384" => HashFunction::Sha384,
"sha512" => HashFunction::Sha512,
"sha512t224" => HashFunction::Sha512Trunc224,
"sha512t256" => HashFunction::Sha512Trunc256,
"keccak224" => HashFunction::Keccak224,
"keccak256" => HashFunction::Keccak256,
"keccak384" => HashFunction::Keccak384,
"keccak512" => HashFunction::Keccak512,
"sha3-224" => HashFunction::Sha3_224,
"sha3-256" => HashFunction::Sha3_256,
"sha3-384" => HashFunction::Sha3_384,
"sha3-512" => HashFunction::Sha3_512,
_ => {
panic!();
}
},
nb_iter: nbi,
salt: salt.to_string().into_bytes(),
norm: Normalization::Nfkc,
};
assert_eq!(&h.hash(&key.to_string().into_bytes()), result);
}
}
}