#![cfg(feature="include_simple")]
use std::io;
use std::string::String;
use std::string::ToString;
use subtle::ConstantTimeEq;
use rand::{OsRng, RngCore};
use hmac::Hmac;
use sha2::Sha256;
use errors::CheckError;
use base64;
use super::pbkdf2;
use byteorder::{ByteOrder, BigEndian};
pub fn pbkdf2_simple(password: &str, c: u32) -> io::Result<String> {
let mut rng = OsRng::new()?;
let mut salt = [0u8; 16];
rng.try_fill_bytes(&mut salt)?;
let mut dk = [0u8; 32];
pbkdf2::<Hmac<Sha256>>(password.as_bytes(), &salt, c as usize, &mut dk);
let mut result = "$rpbkdf2$0$".to_string();
let mut tmp = [0u8; 4];
BigEndian::write_u32(&mut tmp, c);
result.push_str(&base64::encode(&tmp));
result.push('$');
result.push_str(&base64::encode(&salt));
result.push('$');
result.push_str(&base64::encode(&dk));
result.push('$');
Ok(result)
}
pub fn pbkdf2_check(password: &str, hashed_value: &str)
-> Result<(), CheckError> {
let mut iter = hashed_value.split('$');
if iter.next() != Some("") { Err(CheckError::InvalidFormat)?; }
if iter.next() != Some("rpbkdf2") { Err(CheckError::InvalidFormat)?; }
match iter.next() {
Some(fstr) => {
match fstr {
"0" => { }
_ => return Err(CheckError::InvalidFormat)
}
}
None => return Err(CheckError::InvalidFormat)
}
let c = match iter.next() {
Some(pstr) => match base64::decode(pstr) {
Ok(pvec) => {
if pvec.len() != 4 { return Err(CheckError::InvalidFormat); }
BigEndian::read_u32(&pvec[..])
}
Err(_) => return Err(CheckError::InvalidFormat)
},
None => return Err(CheckError::InvalidFormat)
};
let salt = match iter.next() {
Some(sstr) => match base64::decode(sstr) {
Ok(salt) => salt,
Err(_) => return Err(CheckError::InvalidFormat)
},
None => return Err(CheckError::InvalidFormat)
};
let hash = match iter.next() {
Some(hstr) => match base64::decode(hstr) {
Ok(hash) => hash,
Err(_) => return Err(CheckError::InvalidFormat)
},
None => return Err(CheckError::InvalidFormat)
};
if iter.next() != Some("") { Err(CheckError::InvalidFormat)?; }
if iter.next() != None { Err(CheckError::InvalidFormat)?; }
let mut output = vec![0u8; hash.len()];
pbkdf2::<Hmac<Sha256>>(password.as_bytes(), &salt, c as usize, &mut output);
if output.ct_eq(&hash).unwrap_u8() == 1 {
Ok(())
} else {
Err(CheckError::HashMismatch)
}
}