use super::prf::Prf;
use super::Error;
use crypto::pbkdf2::pbkdf2;
#[cfg(target_os = "windows")]
use crypto::scrypt::{scrypt, ScryptParams};
#[cfg(all(unix))]
use rust_scrypt::{scrypt, ScryptParams};
use std::fmt;
use std::str::FromStr;
pub const PBKDF2_KDF_NAME: &str = "pbkdf2";
pub const SCRYPT_KDF_NAME: &str = "scrypt";
#[derive(Clone, Copy, Debug)]
pub enum KdfDepthLevel {
Normal = 1024,
High = 8096,
Ultra = 262_144,
}
impl fmt::Display for KdfDepthLevel {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let printable = match *self {
KdfDepthLevel::Normal => "normal",
KdfDepthLevel::High => "high",
KdfDepthLevel::Ultra => "ultra",
};
write!(f, "{}", printable)
}
}
impl FromStr for KdfDepthLevel {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"normal" => Ok(KdfDepthLevel::Normal),
"high" => Ok(KdfDepthLevel::High),
"ultra" => Ok(KdfDepthLevel::Ultra),
v => Err(Error::InvalidKdfDepth(v.to_string())),
}
}
}
impl Default for KdfDepthLevel {
fn default() -> Self {
KdfDepthLevel::Normal
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Kdf {
Pbkdf2 {
prf: Prf,
c: u32,
},
Scrypt {
n: u32,
r: u32,
p: u32,
},
}
impl Kdf {
pub fn derive(&self, len: usize, kdf_salt: &[u8], passphrase: &str) -> Vec<u8> {
let mut key = vec![0u8; len];
match *self {
Kdf::Pbkdf2 { prf, c } => {
match prf {
Prf::HmacSha256 => {
let mut hmac = prf.hmac(passphrase);
pbkdf2(&mut hmac, kdf_salt, c, &mut key);
}
Prf::HmacSha512 => {
let mut hmac = prf.hmac512(passphrase);
pbkdf2(&mut hmac, kdf_salt, c, &mut key);
}
};
}
#[cfg(target_os = "windows")]
Kdf::Scrypt { n, r, p } => {
let log_n = (n as f64).log2().round() as u8;
let params = ScryptParams::new(log_n, r, p);
scrypt(passphrase.as_bytes(), kdf_salt, ¶ms, &mut key);
}
#[cfg(all(unix))]
Kdf::Scrypt { n, r, p } => {
let params = ScryptParams::new(u64::from(n), r, p);
scrypt(passphrase.as_bytes(), kdf_salt, ¶ms, &mut key);
}
}
key
}
}
impl Default for Kdf {
fn default() -> Self {
Kdf::Scrypt {
n: 1024,
r: 8,
p: 1,
}
}
}
impl From<KdfDepthLevel> for Kdf {
fn from(sec: KdfDepthLevel) -> Self {
Kdf::from((sec as u32, 8, 1))
}
}
impl From<u32> for Kdf {
fn from(c: u32) -> Self {
Kdf::Pbkdf2 {
prf: Prf::default(),
c,
}
}
}
impl From<(u32, u32, u32)> for Kdf {
fn from(t: (u32, u32, u32)) -> Self {
Kdf::Scrypt {
n: t.0,
r: t.1,
p: t.2,
}
}
}
impl FromStr for Kdf {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
_ if s == PBKDF2_KDF_NAME => Ok(Kdf::Pbkdf2 {
prf: Prf::default(),
c: 262_144,
}),
_ if s == SCRYPT_KDF_NAME => Ok(Kdf::default()),
_ => Err(Error::UnsupportedKdf(s.to_string())),
}
}
}
impl fmt::Display for Kdf {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Kdf::Pbkdf2 { .. } => f.write_str(PBKDF2_KDF_NAME),
Kdf::Scrypt { .. } => f.write_str(SCRYPT_KDF_NAME),
}
}
}
#[cfg(test)]
pub mod tests {
use super::*;
use tests::*;
#[test]
fn should_derive_key_via_pbkdf2() {
let kdf_salt =
to_32bytes("ae3cd4e7013836a3df6bd7241b12db061dbe2c6785853cce422d148a624ce0bd");
assert_eq!(
Kdf::from(8).derive(32, &kdf_salt, "testpassword").to_hex(),
"031dc7e0f4f375f6d6fdab7ad8d71834d844e39a6b62f9fb98d942bab76db0f9"
);
}
#[test]
fn should_derive_key_via_scrypt() {
let kdf_salt =
to_32bytes("fd4acb81182a2c8fa959d180967b374277f2ccf2f7f401cb08d042cc785464b4");
assert_eq!(
Kdf::from((2, 8, 1))
.derive(32, &kdf_salt, "1234567890")
.to_hex(),
"52a5dacfcf80e5111d2c7fbed177113a1b48a882b066a017f2c856086680fac7"
);
}
}