use ffi;
use randombytes::randombytes_into;
use libc::{c_ulonglong, size_t};
use rustc_serialize;
pub const SALTBYTES: usize = ffi::crypto_pwhash_scryptsalsa208sha256_SALTBYTES;
pub const STRBYTES: usize = ffi::crypto_pwhash_scryptsalsa208sha256_STRBYTES;
pub const STRPREFIX: &'static str = ffi::crypto_pwhash_scryptsalsa208sha256_STRPREFIX;
pub const OPSLIMIT_INTERACTIVE: OpsLimit =
OpsLimit(ffi::crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_INTERACTIVE);
pub const MEMLIMIT_INTERACTIVE: MemLimit =
MemLimit(ffi::crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_INTERACTIVE);
pub const OPSLIMIT_SENSITIVE: OpsLimit =
OpsLimit(ffi::crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_SENSITIVE);
pub const MEMLIMIT_SENSITIVE: MemLimit =
MemLimit(ffi::crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_SENSITIVE);
#[derive(Copy, Clone)]
pub struct OpsLimit(pub usize);
#[derive(Copy, Clone)]
pub struct MemLimit(pub usize);
#[derive(Copy)]
pub struct Salt(pub [u8; SALTBYTES]);
newtype_clone!(Salt);
newtype_impl!(Salt, SALTBYTES);
#[derive(Copy)]
pub struct HashedPassword(pub [u8; STRBYTES]);
newtype_clone!(HashedPassword);
newtype_impl!(HashedPassword, STRBYTES);
non_secret_newtype_impl!(HashedPassword);
pub fn gen_salt() -> Salt {
let mut salt = Salt([0; SALTBYTES]);
{
let Salt(ref mut sb) = salt;
randombytes_into(sb);
}
salt
}
pub fn derive_key(key: &mut [u8], passwd: &[u8], &Salt(ref sb): &Salt,
OpsLimit(opslimit): OpsLimit,
MemLimit(memlimit): MemLimit) -> Option<()> {
if unsafe {
ffi::crypto_pwhash_scryptsalsa208sha256(key.as_mut_ptr(),
key.len() as c_ulonglong,
passwd.as_ptr(),
passwd.len() as c_ulonglong,
sb,
opslimit as c_ulonglong,
memlimit as size_t)
} == 0 {
Some(())
} else {
None
}
}
pub fn pwhash(passwd: &[u8], OpsLimit(opslimit): OpsLimit,
MemLimit(memlimit): MemLimit) -> Option<HashedPassword> {
let mut out = HashedPassword([0; STRBYTES]);
if unsafe {
let HashedPassword(ref mut str_) = out;
ffi::crypto_pwhash_scryptsalsa208sha256_str(str_,
passwd.as_ptr(),
passwd.len() as c_ulonglong,
opslimit as c_ulonglong,
memlimit as size_t)
} == 0 {
Some(out)
} else {
None
}
}
pub fn pwhash_verify(&HashedPassword(ref str_): &HashedPassword,
passwd: &[u8]) -> bool {
unsafe {
ffi::crypto_pwhash_scryptsalsa208sha256_str_verify(str_,
passwd.as_ptr(),
passwd.len() as c_ulonglong)
== 0
}
}
#[cfg(test)]
mod test {
use super::*;
use crypto::test_utils::round_trip;
#[test]
fn test_derive_key() {
let mut kb = [0u8; 32];
let salt = Salt([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]);
let pw = b"Correct Horse Battery Staple";
let kb_expected = [0xf1, 0xbb, 0xb8, 0x7c, 0x43, 0x36, 0x5b, 0x03,
0x3b, 0x9a, 0xe8, 0x3e, 0x05, 0xef, 0xad, 0x25,
0xdb, 0x8d, 0x83, 0xb8, 0x3d, 0xb1, 0xde, 0xe3,
0x6b, 0xdb, 0xf5, 0x4d, 0xcd, 0x3a, 0x1a, 0x11];
derive_key(&mut kb, pw, &salt, OPSLIMIT_INTERACTIVE, MEMLIMIT_INTERACTIVE);
assert_eq!(kb, kb_expected);
}
#[test]
fn test_pwhash_verify() {
use randombytes::randombytes;
for i in (0..32usize) {
let pw = randombytes(i);
let pwh = pwhash(&pw, OPSLIMIT_INTERACTIVE, MEMLIMIT_INTERACTIVE).unwrap();
assert!(pwhash_verify(&pwh, &pw));
}
}
#[test]
fn test_pwhash_verify_tamper() {
use randombytes::randombytes;
for i in (0..16usize) {
let mut pw = randombytes(i);
let pwh = pwhash(&pw, OPSLIMIT_INTERACTIVE, MEMLIMIT_INTERACTIVE).unwrap();
for j in (0..pw.len()) {
pw[j] ^= 0x20;
assert!(!pwhash_verify(&pwh, &pw));
pw[j] ^= 0x20;
}
}
}
#[test]
fn test_serialisation() {
use randombytes::randombytes;
for i in (0..32usize) {
let pw = randombytes(i);
let pwh = pwhash(&pw, OPSLIMIT_INTERACTIVE, MEMLIMIT_INTERACTIVE).unwrap();
let salt = gen_salt();
round_trip(pwh);
round_trip(salt);
}
}
}