/// Native Rust implementation of scrypt. pub use self::fastpbkdf2::Pbkdf2; pub use self::ring_pbkdf2::Pbkdf2 as RingPbkdf2; /// Native Rust implementation of scrypt. mod ring_pbkdf2 { use primitives::{Primitive, PrimitiveImpl}; use sod::Sod; use ring::{digest, pbkdf2}; use serde_mcf::Hashes; use std::fmt; use std::sync::Arc; use super::super::hash_to_id; /// Struct holding `PBKDF2` parameters. /// /// This implementation is backed by `ring`. pub struct Pbkdf2 { iterations: u32, algorithm: &'static digest::Algorithm, } impl Pbkdf2 { /// Create a new PBKDF2 instance using defaults. pub fn default() -> Primitive { Primitive(Sod::Dynamic(Arc::clone(&DEFAULT))) } /// Create a new PBKDF2 instance. pub fn new(iterations: u32, algorithm: &'static digest::Algorithm) -> Primitive { Self::new_impl(iterations, algorithm).into() } fn new_impl(iterations: u32, algorithm: &'static digest::Algorithm) -> Self { Self { iterations: iterations, algorithm: algorithm, } } } lazy_static! { static ref DEFAULT: Arc> = { Arc::new(Box::new(Pbkdf2::new_impl(10_000, &digest::SHA256))) }; } impl ::primitives::PrimitiveImpl for Pbkdf2 { /// Compute the scrypt hash fn compute<'a>(&'a self, password: &[u8], salt: &[u8]) -> Vec { let mut hash = vec![0_u8; 32]; pbkdf2::derive(self.algorithm, self.iterations, salt, password, &mut hash); hash } /// Convert parameters into a vector of (key, value) tuples /// for serializing. fn params_as_vec(&self) -> Vec<(&'static str, String)> { vec![("n", self.iterations.to_string())] } fn hash_id(&self) -> Hashes { match hash_to_id(self.algorithm).as_ref() { "SHA1" => Hashes::Pbkdf2Sha1, "SHA256" => Hashes::Pbkdf2Sha256, "SHA512" => Hashes::Pbkdf2Sha512, _ => panic!("unexpected digest algorithm"), } } } impl fmt::Debug for Pbkdf2 { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { write!(f, "PBKDF2-{:?}, iterations: {}", self.algorithm, self.iterations) } } } /// Native Rust implementation of scrypt. mod fastpbkdf2 { extern crate fastpbkdf2; use self::fastpbkdf2::*; use primitives::{Primitive, PrimitiveImpl}; use sod::Sod; use ring::digest; use serde_mcf::Hashes; use std::fmt; use std::sync::Arc; use super::super::hash_to_id; /// Struct holding `PBKDF2` parameters. /// /// This implementation is backed by `fastpbkdf2`. pub struct Pbkdf2 { iterations: u32, algorithm: fn(&[u8], &[u8], u32, &mut [u8]), alg_id: &'static str, } lazy_static! { static ref DEFAULT: Arc> = { Arc::new(Box::new(Pbkdf2::new_impl(10_000, &digest::SHA256))) }; } impl Pbkdf2 { /// Create a new PBKDF2 instance using defaults. pub fn default() -> Primitive { Primitive(Sod::Dynamic(Arc::clone(&DEFAULT))) } /// Create a new PBKDF2 instance. pub fn new(iterations: u32, algorithm: &'static digest::Algorithm) -> Primitive { Self::new_impl(iterations, algorithm).into() } fn new_impl(iterations: u32, algorithm: &'static digest::Algorithm) -> Self { match hash_to_id(algorithm).as_ref() { "SHA1" => { Self { iterations: iterations, algorithm: pbkdf2_hmac_sha1, alg_id: "SHA1", } } "SHA256" => { Self { iterations: iterations, algorithm: pbkdf2_hmac_sha256, alg_id: "SHA256", } } "SHA512" => { Self { iterations: iterations, algorithm: pbkdf2_hmac_sha512, alg_id: "SHA512", } } _ => panic!("unexpected digest algorithm"), } } } impl ::primitives::PrimitiveImpl for Pbkdf2 { /// Compute the scrypt hash fn compute<'a>(&'a self, password: &[u8], salt: &[u8]) -> Vec { let mut hash = vec![0_u8; 32]; (self.algorithm)(password, salt, self.iterations, &mut hash); hash } /// Convert parameters into a vector of (key, value) tuples /// for serializing. fn params_as_vec(&self) -> Vec<(&'static str, String)> { vec![("n", self.iterations.to_string())] } fn hash_id(&self) -> Hashes { match self.alg_id { "SHA1" => Hashes::Pbkdf2Sha1, "SHA256" => Hashes::Pbkdf2Sha256, "SHA512" => Hashes::Pbkdf2Sha512, _ => panic!("unexpected digest algorithm"), } } } impl fmt::Debug for Pbkdf2 { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { write!(f, "PBKDF2-{:?}, iterations: {}", self.alg_id, self.iterations) } } } #[cfg(test)] mod test { use ::hashing::*; use ring::digest; use serde_mcf; #[test] fn sanity_check() { let password = "hunter2"; let params = super::Pbkdf2::default(); println!("{:?}", params); let salt = ::get_salt(); let hash = params.compute(password.as_bytes(), &salt); let hash2 = params.compute(password.as_bytes(), &salt); assert_eq!(hash, hash2); let out = Output { alg: Algorithm::Single(params.into()), salt: salt, hash: hash, }; println!("{:?}", serde_mcf::to_string(&out).unwrap()); } #[test] fn sanity_check_ring() { let password = "hunter2"; let params = super::RingPbkdf2::default(); println!("{:?}", params); let salt = ::get_salt(); let hash = params.compute(password.as_bytes(), &salt); let hash2 = params.compute(password.as_bytes(), &salt); assert_eq!(hash, hash2); let out = Output { alg: Algorithm::Single(params.into()), salt: salt, hash: hash, }; println!("{:?}", serde_mcf::to_string(&out).unwrap()); } macro_rules! primitive_round_trip { ($prim:expr) => ( let hash = serde_mcf::to_string(&$prim.hash(&"hunter2")).unwrap(); let _output: Output = serde_mcf::from_str(&hash).unwrap(); ) } #[test] fn pbkdf2_params() { let params = Algorithm::Single(super::Pbkdf2::new(1_000, &digest::SHA1)); primitive_round_trip!(params); let params = Algorithm::Single(super::Pbkdf2::new(1_000, &digest::SHA256)); primitive_round_trip!(params); let params = Algorithm::Single(super::Pbkdf2::new(1_000, &digest::SHA512)); primitive_round_trip!(params); let params = Algorithm::Single(super::RingPbkdf2::new(1_000, &digest::SHA1)); primitive_round_trip!(params); let params = Algorithm::Single(super::RingPbkdf2::new(1_000, &digest::SHA256)); primitive_round_trip!(params); let params = Algorithm::Single(super::RingPbkdf2::new(1_000, &digest::SHA512)); primitive_round_trip!(params); } } #[cfg(features = "bench")] mod ring_bench { use super::*; benches!(RingPbkdf2); } #[cfg(features = "bench")] mod fast_bench { use super::*; benches!(Pbkdf2); }