mod kdf_encrypted_data_v1;
use std::borrow::Borrow;
use std::convert::TryFrom;
use crate::ciphertext;
use crate::enums::KdfEncryptedDataSubtype;
use crate::key_derivation::DerivationParameters;
use crate::{
CiphertextVersion, DataType, Error, Header, HeaderType, KdfEncryptedDataVersion, Result,
};
use kdf_encrypted_data_v1::KdfEncryptedDataV1;
#[cfg(feature = "fuzz")]
use arbitrary::Arbitrary;
#[derive(Clone, Debug)]
#[cfg_attr(feature = "fuzz", derive(Arbitrary))]
pub struct KdfEncryptedData {
pub(crate) header: Header<KdfEncryptedData>,
payload: KdfEncryptedDataPayload,
}
impl HeaderType for KdfEncryptedData {
type Version = KdfEncryptedDataVersion;
type Subtype = KdfEncryptedDataSubtype;
fn data_type() -> DataType {
DataType::KdfEncryptedData
}
}
#[derive(Clone, Debug)]
#[cfg_attr(feature = "fuzz", derive(Arbitrary))]
enum KdfEncryptedDataPayload {
V1(KdfEncryptedDataV1),
}
pub fn encrypt_with_password(
data: &[u8],
password: &[u8],
derivation_parameters: DerivationParameters,
ciphertext_version: CiphertextVersion,
) -> Result<KdfEncryptedData> {
encrypt_with_password_and_aad(
data,
password,
[].as_slice(),
derivation_parameters,
ciphertext_version,
)
}
pub fn encrypt_with_password_and_aad(
data: &[u8],
password: &[u8],
aad: &[u8],
derivation_parameters: DerivationParameters,
ciphertext_version: CiphertextVersion,
) -> Result<KdfEncryptedData> {
let mut header: Header<KdfEncryptedData> = Header::default();
header.version = KdfEncryptedDataVersion::V1;
let secret_key = derivation_parameters.derive(password)?;
let ciphertext =
ciphertext::encrypt_with_secret_key_and_aad(data, &secret_key, aad, ciphertext_version)?;
Ok(KdfEncryptedData {
header,
payload: KdfEncryptedDataPayload::V1(KdfEncryptedDataV1 {
derivation_parameters,
ciphertext,
}),
})
}
impl KdfEncryptedData {
pub fn decrypt_with_password(&self, password: &[u8]) -> Result<Vec<u8>> {
self.decrypt_with_password_and_aad(password, [].as_slice())
}
pub fn decrypt_with_password_and_aad(&self, password: &[u8], aad: &[u8]) -> Result<Vec<u8>> {
match &self.payload {
KdfEncryptedDataPayload::V1(payload) => {
let secret_key = payload.derivation_parameters.derive(password)?;
payload
.ciphertext
.decrypt_with_secret_key_and_aad(&secret_key, aad)
}
}
}
}
impl From<KdfEncryptedData> for Vec<u8> {
fn from(data: KdfEncryptedData) -> Self {
let mut header: Self = data.header.borrow().into();
let mut payload: Self = data.payload.into();
header.append(&mut payload);
header
}
}
impl From<KdfEncryptedDataPayload> for Vec<u8> {
fn from(data: KdfEncryptedDataPayload) -> Self {
match data {
KdfEncryptedDataPayload::V1(v1) => v1.into(),
}
}
}
impl TryFrom<&[u8]> for KdfEncryptedData {
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 {
KdfEncryptedDataVersion::V1 => {
KdfEncryptedDataPayload::V1(KdfEncryptedDataV1::try_from(&data[Header::len()..])?)
}
KdfEncryptedDataVersion::Latest => return Err(Error::UnknownVersion),
};
Ok(Self { header, payload })
}
}
#[cfg(test)]
mod tests {
use crate::key_derivation::Argon2;
use crate::utils::validate_header;
use crate::Pbkdf2;
use super::*;
#[test]
fn derive_encrypt_roundtrip_latest() {
let data = b"derive encrypt payload";
let password = b"a very strong password";
let aad = b"public data";
let params = Argon2::new().parameters();
let wrapped =
encrypt_with_password_and_aad(data, password, aad, params, CiphertextVersion::Latest)
.unwrap();
let wrapped_raw: Vec<u8> = wrapped.into();
let wrapped = KdfEncryptedData::try_from(wrapped_raw.as_slice()).unwrap();
let decrypted = wrapped
.decrypt_with_password_and_aad(password, aad)
.unwrap();
assert_eq!(decrypted, data);
}
#[test]
fn derive_encrypt_pbkdf2() {
let data = b"derive encrypt payload";
let password = b"a very strong password";
let params = Pbkdf2::with_params(10).parameters().unwrap();
let wrapped =
encrypt_with_password(data, password, params, CiphertextVersion::Latest).unwrap();
assert!(wrapped.decrypt_with_password(b"wrong password").is_err());
}
#[test]
fn derive_encrypt_wrong_password_fails() {
let data = b"derive encrypt payload";
let password = b"a very strong password";
let params = Argon2::new().parameters();
let wrapped =
encrypt_with_password(data, password, params, CiphertextVersion::Latest).unwrap();
assert!(wrapped.decrypt_with_password(b"wrong password").is_err());
}
#[test]
fn derive_encrypt_wrong_aad_fails() {
let data = b"derive encrypt payload";
let password = b"a very strong password";
let params = Argon2::new().parameters();
let wrapped = encrypt_with_password_and_aad(
data,
password,
b"the right aad",
params,
CiphertextVersion::Latest,
)
.unwrap();
assert!(wrapped
.decrypt_with_password_and_aad(password, b"wrong aad")
.is_err());
}
#[test]
fn validate_header_accepts_derive_encrypt() {
let password = b"a very strong password";
let params = Argon2::new().parameters();
let wrapped = encrypt_with_password(
b"derive encrypt payload",
password,
params,
CiphertextVersion::Latest,
)
.unwrap();
let wrapped_raw: Vec<u8> = wrapped.into();
assert!(validate_header(
wrapped_raw.as_slice(),
DataType::KdfEncryptedData
));
assert!(!validate_header(
wrapped_raw.as_slice(),
DataType::Ciphertext
));
}
}