use std::iter::repeat;
use std::io;
use cryptoutil::copy_memory;
use rand::{OsRng, Rng};
use serialize::base64;
use serialize::base64::{FromBase64, ToBase64};
use cryptoutil::{read_u32_be, write_u32_be};
use hmac::Hmac;
use mac::Mac;
use sha2::Sha256;
use util::fixed_time_eq;
fn calculate_block<M: Mac>(
mac: &mut M,
salt: &[u8],
c: u32,
idx: u32,
scratch: &mut [u8],
block: &mut [u8]) {
mac.input(salt);
let mut idx_buf = [0u8; 4];
write_u32_be(&mut idx_buf, idx);
mac.input(&idx_buf);
mac.raw_result(block);
mac.reset();
if c > 1 {
mac.input(block);
mac.raw_result(scratch);
mac.reset();
for (output, &input) in block.iter_mut().zip(scratch.iter()) {
*output ^= input;
}
}
for _ in 2..c {
mac.input(scratch);
mac.raw_result(scratch);
mac.reset();
for (output, &input) in block.iter_mut().zip(scratch.iter()) {
*output ^= input;
}
}
}
pub fn pbkdf2<M: Mac>(mac: &mut M, salt: &[u8], c: u32, output: &mut [u8]) {
assert!(c > 0);
let os = mac.output_bytes();
let mut scratch: Vec<u8> = repeat(0).take(os).collect();
let mut idx: u32 = 0;
for chunk in output.chunks_mut(os) {
idx = idx.checked_add(1).expect("PBKDF2 size limit exceeded.");
if chunk.len() == os {
calculate_block(mac, salt, c, idx, &mut scratch, chunk);
} else {
let mut tmp: Vec<u8> = repeat(0).take(os).collect();
calculate_block(mac, salt, c, idx, &mut scratch[..], &mut tmp[..]);
let chunk_len = chunk.len();
copy_memory(&tmp[..chunk_len], chunk);
}
}
}
pub fn pbkdf2_simple(password: &str, c: u32) -> io::Result<String> {
let mut rng = try!(OsRng::new());
let salt: Vec<u8> = rng.gen_iter::<u8>().take(16).collect();
let mut dk = [0u8; 32];
let mut mac = Hmac::new(Sha256::new(), password.as_bytes());
pbkdf2(&mut mac, &salt[..], c, &mut dk);
let mut result = "$rpbkdf2$0$".to_string();
let mut tmp = [0u8; 4];
write_u32_be(&mut tmp, c);
result.push_str(&tmp.to_base64(base64::STANDARD)[..]);
result.push('$');
result.push_str(&salt.to_base64(base64::STANDARD)[..]);
result.push('$');
result.push_str(&dk.to_base64(base64::STANDARD)[..]);
result.push('$');
Ok(result)
}
pub fn pbkdf2_check(password: &str, hashed_value: &str) -> Result<bool, &'static str> {
static ERR_STR: &'static str = "Hash is not in Rust PBKDF2 format.";
let mut iter = hashed_value.split('$');
match iter.next() {
Some(x) => if x != "" { return Err(ERR_STR); },
None => return Err(ERR_STR)
}
match iter.next() {
Some(t) => if t != "rpbkdf2" { return Err(ERR_STR); },
None => return Err(ERR_STR)
}
match iter.next() {
Some(fstr) => {
match fstr {
"0" => { }
_ => return Err(ERR_STR)
}
}
None => return Err(ERR_STR)
}
let c = match iter.next() {
Some(pstr) => match pstr.from_base64() {
Ok(pvec) => {
if pvec.len() != 4 { return Err(ERR_STR); }
read_u32_be(&pvec[..])
}
Err(_) => return Err(ERR_STR)
},
None => return Err(ERR_STR)
};
let salt = match iter.next() {
Some(sstr) => match sstr.from_base64() {
Ok(salt) => salt,
Err(_) => return Err(ERR_STR)
},
None => return Err(ERR_STR)
};
let hash = match iter.next() {
Some(hstr) => match hstr.from_base64() {
Ok(hash) => hash,
Err(_) => return Err(ERR_STR)
},
None => return Err(ERR_STR)
};
match iter.next() {
Some(x) => if x != "" { return Err(ERR_STR); },
None => return Err(ERR_STR)
}
match iter.next() {
Some(_) => return Err(ERR_STR),
None => { }
}
let mut mac = Hmac::new(Sha256::new(), password.as_bytes());
let mut output: Vec<u8> = repeat(0).take(hash.len()).collect();
pbkdf2(&mut mac, &salt[..], c, &mut output[..]);
Ok(fixed_time_eq(&output[..], &hash[..]))
}
#[cfg(test)]
mod test {
use std::iter::repeat;
use pbkdf2::{pbkdf2, pbkdf2_simple, pbkdf2_check};
use hmac::Hmac;
use sha1::Sha1;
struct Test {
password: Vec<u8>,
salt: Vec<u8>,
c: u32,
expected: Vec<u8>
}
fn tests() -> Vec<Test> {
vec![
Test {
password: b"password".to_vec(),
salt: b"salt".to_vec(),
c: 1,
expected: vec![
0x0c, 0x60, 0xc8, 0x0f, 0x96, 0x1f, 0x0e, 0x71,
0xf3, 0xa9, 0xb5, 0x24, 0xaf, 0x60, 0x12, 0x06,
0x2f, 0xe0, 0x37, 0xa6 ]
},
Test {
password: b"password".to_vec(),
salt: b"salt".to_vec(),
c: 2,
expected: vec![
0xea, 0x6c, 0x01, 0x4d, 0xc7, 0x2d, 0x6f, 0x8c,
0xcd, 0x1e, 0xd9, 0x2a, 0xce, 0x1d, 0x41, 0xf0,
0xd8, 0xde, 0x89, 0x57 ]
},
Test {
password: b"password".to_vec(),
salt: b"salt".to_vec(),
c: 4096,
expected: vec![
0x4b, 0x00, 0x79, 0x01, 0xb7, 0x65, 0x48, 0x9a,
0xbe, 0xad, 0x49, 0xd9, 0x26, 0xf7, 0x21, 0xd0,
0x65, 0xa4, 0x29, 0xc1 ]
},
Test {
password: b"passwordPASSWORDpassword".to_vec(),
salt: b"saltSALTsaltSALTsaltSALTsaltSALTsalt".to_vec(),
c: 4096,
expected: vec![
0x3d, 0x2e, 0xec, 0x4f, 0xe4, 0x1c, 0x84, 0x9b,
0x80, 0xc8, 0xd8, 0x36, 0x62, 0xc0, 0xe4, 0x4a,
0x8b, 0x29, 0x1a, 0x96, 0x4c, 0xf2, 0xf0, 0x70, 0x38 ]
},
Test {
password: vec![112, 97, 115, 115, 0, 119, 111, 114, 100],
salt: vec![115, 97, 0, 108, 116],
c: 4096,
expected: vec![
0x56, 0xfa, 0x6a, 0xa7, 0x55, 0x48, 0x09, 0x9d,
0xcc, 0x37, 0xd7, 0xf0, 0x34, 0x25, 0xe0, 0xc3 ]
}
]
}
#[test]
fn test_pbkdf2() {
let tests = tests();
for t in tests.iter() {
let mut mac = Hmac::new(Sha1::new(), &t.password[..]);
let mut result: Vec<u8> = repeat(0).take(t.expected.len()).collect();
pbkdf2(&mut mac, &t.salt[..], t.c, &mut result);
assert!(result == t.expected);
}
}
#[test]
fn test_pbkdf2_simple() {
let password = "password";
let out1 = pbkdf2_simple(password, 1024).unwrap();
let out2 = pbkdf2_simple(password, 1024).unwrap();
assert!(out1 != out2);
match pbkdf2_check(password, &out1[..]) {
Ok(r) => assert!(r),
Err(_) => panic!()
}
match pbkdf2_check(password, &out2[..]) {
Ok(r) => assert!(r),
Err(_) => panic!()
}
match pbkdf2_check("wrong", &out1[..]) {
Ok(r) => assert!(!r),
Err(_) => panic!()
}
match pbkdf2_check("wrong", &out2[..]) {
Ok(r) => assert!(!r),
Err(_) => panic!()
}
}
}