use super::PasswordHasher;
use argon2rs::{ Argon2, Variant };
use std::cmp::{ PartialEq, Eq, max };
use std::fmt;
use argon2rs;
pub use argon2rs::ParamErr as Argon2Err;
#[derive(Serialize, Deserialize, PartialEq, Eq, Copy, Clone, Debug)]
pub struct Argon2Settings {
pub passes: u32,
pub lanes: u32,
pub kib: u32,
}
impl Argon2Settings {
pub fn new() -> Argon2Settings { Argon2Settings {
passes: max(argon2rs::defaults::PASSES, 12),
lanes: argon2rs::defaults::LANES,
kib: argon2rs::defaults::KIB,
}}
pub fn with_passes(&mut self, passes: u32) -> &mut Argon2Settings {
self.passes = passes;
self
}
pub fn with_lanes(&mut self, lanes: u32) -> &mut Argon2Settings {
self.lanes = lanes;
self
}
pub fn with_kib(&mut self, kib: u32) -> &mut Argon2Settings {
self.kib = kib;
self
}
pub fn try_create(self) -> Result<Argon2PasswordHasher, Argon2Err> {
Argon2PasswordHasher::new(self.passes, self.lanes, self.kib)
}
}
impl Default for Argon2Settings {
fn default() -> Self { Argon2Settings::new() }
}
pub struct Argon2PasswordHasher {
argon2i: Argon2,
}
impl Argon2PasswordHasher {
pub fn new(passes: u32, lanes: u32, kib: u32) -> Result<Argon2PasswordHasher, Argon2Err> {
if passes <= 10 { return Err(Argon2Err::TooFewPasses); }
Ok(Argon2PasswordHasher {
argon2i: Argon2::new(passes, lanes, kib, Variant::Argon2i)?
})
}
pub fn params(&self) -> (u32, u32, u32) {
let (_, kib, passes, lanes) = self.argon2i.params();
(passes, lanes, kib)
}
pub fn settings(&self) -> Argon2Settings {
let (_, kib, passes, lanes) = self.argon2i.params();
Argon2Settings { passes: passes, lanes: lanes, kib: kib }
}
}
impl Default for Argon2PasswordHasher {
fn default() -> Self {
Argon2PasswordHasher { argon2i: Argon2::default(Variant::Argon2i) }
}
}
impl PasswordHasher for Argon2PasswordHasher {
unsafe fn hash(&self, hashed_password: &mut [u8], clear_text_password: &[u8], hash_salt: &[u8]) {
self.argon2i.hash(hashed_password, clear_text_password, hash_salt, &[], &[]);
}
}
impl PartialEq for Argon2PasswordHasher {
fn eq(&self, other: &Argon2PasswordHasher) -> bool {
self.params() == other.params()
}
}
impl Eq for Argon2PasswordHasher {}
impl fmt::Debug for Argon2PasswordHasher {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let (passes, lanes, kib) = self.params();
write!(f,
"Argon2PasswordHasher {{ passes: {}, lanes: {}, kib: {} }}",
passes, lanes, kib
)
}
}
#[cfg(test)]
mod test {
use super::{ Argon2PasswordHasher, Argon2Err, Argon2Settings };
use serde_test::{ Token, assert_tokens };
#[test]
fn require_many_passes() {
assert_eq!(Err(Argon2Err::TooFewPasses), Argon2PasswordHasher::new(5, 8, 8*8));
}
#[test]
fn debug_does_its_thing() {
assert_eq!(
"Argon2PasswordHasher { passes: 13, lanes: 8, kib: 64 }",
format!("{:?}", Argon2PasswordHasher::new(13, 8, 8*8).unwrap())
);
}
#[test]
fn eq_does_its_thing() {
assert!(Argon2PasswordHasher::default() == Argon2PasswordHasher::default());
assert!(Argon2PasswordHasher::new(13,8,64) != Argon2PasswordHasher::new(14,8,64));
}
#[test]
fn params_does_its_thing() {
assert_eq!(
(13_u32, 8_u32, 64_u32),
Argon2PasswordHasher::new(13, 8, 64).unwrap().params()
);
}
#[test]
fn serde_works() {
let builder = Argon2Settings { passes: 3, lanes: 4, kib: 7 };
let mut tokens: Vec<Token> = Vec::with_capacity(11);
tokens.push(Token::StructStart("Argon2Settings", 3));
tokens.push(Token::StructSep);
tokens.push(Token::Str("passes"));
tokens.push(Token::U32(3));
tokens.push(Token::StructSep);
tokens.push(Token::Str("lanes"));
tokens.push(Token::U32(4));
tokens.push(Token::StructSep);
tokens.push(Token::Str("kib"));
tokens.push(Token::U32(7));
tokens.push(Token::StructEnd);
assert_tokens(&builder, &tokens[..]);
}
}