mod key_derivation_v1;
mod key_derivation_v2;
pub use key_derivation_v1::Pbkdf2;
pub use key_derivation_v2::Argon2;
use key_derivation_v1::KeyDerivationV1;
use key_derivation_v2::KeyDerivationV2;
use std::borrow::Borrow;
use std::convert::TryFrom;
#[cfg(feature = "fuzz")]
use arbitrary::Arbitrary;
#[cfg(feature = "wbindgen")]
use wasm_bindgen::prelude::*;
use zeroize::Zeroizing;
use crate::key::{secret_key_from_raw, SecretKey};
#[cfg(feature = "fuzz")]
use crate::Argon2Parameters;
use crate::{DataType, Error, Header, HeaderType, KeyDerivationVersion, Result};
use super::enums::KeyDerivationSubtype;
#[derive(Clone, Debug)]
#[cfg_attr(feature = "fuzz", derive(Arbitrary))]
#[cfg_attr(feature = "wbindgen", wasm_bindgen(inspectable))]
pub struct DerivationParameters {
pub(crate) header: Header<DerivationParameters>,
pub(super) payload: DerivationParametersPayload,
}
impl HeaderType for DerivationParameters {
type Version = KeyDerivationVersion;
type Subtype = KeyDerivationSubtype;
fn data_type() -> DataType {
DataType::KeyDerivation
}
}
#[derive(Clone, Debug)]
#[cfg_attr(feature = "fuzz", derive(Arbitrary))]
pub(super) enum DerivationParametersPayload {
V1(KeyDerivationV1),
V2(KeyDerivationV2),
}
#[cfg(feature = "fuzz")]
impl<'a> Arbitrary<'a> for KeyDerivationV1 {
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
Ok(KeyDerivationV1 {
iterations: u32::arbitrary(u)?,
salt: Vec::<u8>::arbitrary(u)?,
})
}
}
#[cfg(feature = "fuzz")]
impl<'a> Arbitrary<'a> for KeyDerivationV2 {
fn arbitrary(_u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
Ok(KeyDerivationV2 {
params: Argon2Parameters::default(),
})
}
}
impl From<DerivationParameters> for Vec<u8> {
fn from(data: DerivationParameters) -> Self {
let mut header: Self = data.header.borrow().into();
let mut payload: Self = data.payload.into();
header.append(&mut payload);
header
}
}
impl TryFrom<&[u8]> for DerivationParameters {
type Error = Error;
fn try_from(data: &[u8]) -> Result<Self> {
if data.len() < Header::len() {
return Err(Error::InvalidLength);
}
let header = Header::try_from(&data[0..Header::len()])?;
let payload = match header.version {
KeyDerivationVersion::V1 => {
DerivationParametersPayload::V1(KeyDerivationV1::try_from(&data[Header::len()..])?)
}
KeyDerivationVersion::V2 => {
DerivationParametersPayload::V2(KeyDerivationV2::try_from(&data[Header::len()..])?)
}
KeyDerivationVersion::Latest => return Err(Error::UnknownVersion),
};
Ok(Self { header, payload })
}
}
impl From<DerivationParametersPayload> for Vec<u8> {
fn from(payload: DerivationParametersPayload) -> Self {
match payload {
DerivationParametersPayload::V1(v1) => Vec::from(&v1),
DerivationParametersPayload::V2(v2) => Vec::from(&v2),
}
}
}
impl DerivationParameters {
pub fn derive(&self, password: &[u8]) -> Result<SecretKey> {
let raw = match &self.payload {
DerivationParametersPayload::V1(v1) => v1.derive(password),
DerivationParametersPayload::V2(v2) => v2.derive(password)?,
};
secret_key_from_raw(raw)
}
pub fn compute(&self, password: &[u8]) -> Result<Zeroizing<Vec<u8>>> {
match &self.payload {
DerivationParametersPayload::V1(v1) => Ok(v1.derive(password)),
DerivationParametersPayload::V2(v2) => v2.derive(password),
}
}
pub fn output_length(&self) -> usize {
match &self.payload {
DerivationParametersPayload::V1(_) => key_derivation_v1::KEY_LENGTH,
DerivationParametersPayload::V2(v2) => v2.params.length as usize,
}
}
}
pub fn derive_key(
password: &[u8],
version: KeyDerivationVersion,
) -> Result<(SecretKey, DerivationParameters)> {
match version {
KeyDerivationVersion::V1 => Pbkdf2::new().derive(password),
KeyDerivationVersion::V2 | KeyDerivationVersion::Latest => Argon2::new().derive(password),
}
}
#[cfg(test)]
mod tests {
use std::convert::TryFrom;
use crate::key::secret_key_from_raw;
use crate::Argon2Parameters;
use super::*;
#[test]
fn pbkdf2_derive_same_input_same_salt_produces_same_key() {
let pbkdf2 = Pbkdf2::with_params(10);
let salt = b"fixed_salt_value";
let (key1, _) = pbkdf2.derive_with_salt(b"password", salt).unwrap();
let (key2, _) = pbkdf2.derive_with_salt(b"password", salt).unwrap();
assert_eq!(key1.as_bytes(), key2.as_bytes());
}
#[test]
fn pbkdf2_derive_different_password_produces_different_key() {
let pbkdf2 = Pbkdf2::with_params(10);
let salt = b"fixed_salt_value";
let (key1, _) = pbkdf2.derive_with_salt(b"password1", salt).unwrap();
let (key2, _) = pbkdf2.derive_with_salt(b"password2", salt).unwrap();
assert_ne!(key1.as_bytes(), key2.as_bytes());
}
#[test]
fn pbkdf2_derive_different_salt_produces_different_key() {
let pbkdf2 = Pbkdf2::with_params(10);
let (key1, _) = pbkdf2.derive_with_salt(b"password", b"salt_one").unwrap();
let (key2, _) = pbkdf2.derive_with_salt(b"password", b"salt_two").unwrap();
assert_ne!(key1.as_bytes(), key2.as_bytes());
}
#[test]
fn pbkdf2_derive_generates_random_salt() {
let pbkdf2 = Pbkdf2::with_params(10);
let (_, params1) = pbkdf2.derive(b"password").unwrap();
let (_, params2) = pbkdf2.derive(b"password").unwrap();
let bytes1: Vec<u8> = params1.into();
let bytes2: Vec<u8> = params2.into();
assert_ne!(bytes1, bytes2);
}
#[test]
fn pbkdf2_derive_with_salt_roundtrip() {
let pbkdf2 = Pbkdf2::with_params(10);
let salt = b"roundtrip_salt!!";
let (key1, params) = pbkdf2.derive_with_salt(b"password", salt).unwrap();
let params_bytes: Vec<u8> = params.into();
let params2 = DerivationParameters::try_from(params_bytes.as_slice()).unwrap();
let payload_bytes: Vec<u8> = params2.payload.into();
let v1 = KeyDerivationV1::try_from(payload_bytes.as_slice()).unwrap();
let raw = v1.derive(b"password");
let key2 = secret_key_from_raw(raw).unwrap();
assert_eq!(key1.as_bytes(), key2.as_bytes());
}
#[test]
fn pbkdf2_derivation_parameters_serialize_roundtrip() {
let pbkdf2 = Pbkdf2::with_params(12345);
let (_, params) = pbkdf2
.derive_with_salt(b"password", b"some_salt_here!!")
.unwrap();
let bytes: Vec<u8> = params.into();
let params2 = DerivationParameters::try_from(bytes.as_slice()).unwrap();
assert_eq!(params2.header.version, KeyDerivationVersion::V1);
}
#[test]
fn argon2_derive_same_params_same_input_produces_same_key() {
let mut argon2_params = Argon2Parameters::default();
argon2_params.iterations = 2;
argon2_params.memory = 64;
argon2_params.set_salt(b"fixed_salt_16byt".to_vec());
let argon2 = Argon2::with_params(argon2_params.clone());
let (key1, _) = argon2.derive(b"password").unwrap();
let argon2 = Argon2::with_params(argon2_params);
let (key2, _) = argon2.derive(b"password").unwrap();
assert_eq!(key1.as_bytes(), key2.as_bytes());
}
#[test]
fn argon2_derive_different_salt_produces_different_key() {
let (key1, _) = Argon2::new().derive(b"password").unwrap();
let (key2, _) = Argon2::new().derive(b"password").unwrap();
assert_ne!(key1.as_bytes(), key2.as_bytes());
}
#[test]
fn argon2_derivation_parameters_serialize_roundtrip() {
let mut argon2_params = Argon2Parameters::default();
argon2_params.iterations = 2;
argon2_params.memory = 64;
let (_, params) = Argon2::with_params(argon2_params)
.derive(b"password")
.unwrap();
let bytes: Vec<u8> = params.into();
let params2 = DerivationParameters::try_from(bytes.as_slice()).unwrap();
assert_eq!(params2.header.version, KeyDerivationVersion::V2);
}
#[test]
fn validate_header_accepts_key_derivation() {
use crate::utils::validate_header;
let (_, params) = Pbkdf2::with_params(10)
.derive_with_salt(b"pw", b"salt_16bytes!!!")
.unwrap();
let bytes: Vec<u8> = params.into();
assert!(validate_header(&bytes, DataType::KeyDerivation));
}
#[test]
fn validate_header_rejects_wrong_type() {
use crate::utils::validate_header;
use crate::DataType;
let (_, params) = Pbkdf2::with_params(10)
.derive_with_salt(b"pw", b"salt_16bytes!!!")
.unwrap();
let bytes: Vec<u8> = params.into();
assert!(!validate_header(&bytes, DataType::Ciphertext));
assert!(!validate_header(&bytes, DataType::Key));
assert!(!validate_header(&bytes, DataType::PasswordHash));
}
}