mod ciphertext_v1;
mod ciphertext_v2;
use super::CiphertextSubtype;
pub use super::CiphertextVersion;
use super::DataType;
use super::Error;
use super::Header;
use super::HeaderType;
use super::Result;
use super::key::{PrivateKey, PublicKey};
use ciphertext_v1::CiphertextV1;
use ciphertext_v2::{CiphertextV2Asymmetric, CiphertextV2Symmetric};
use std::borrow::Borrow;
use std::convert::TryFrom;
#[cfg(feature = "fuzz")]
use arbitrary::Arbitrary;
#[derive(Clone, Debug)]
#[cfg_attr(feature = "fuzz", derive(Arbitrary))]
pub struct Ciphertext {
pub(crate) header: Header<Ciphertext>,
payload: CiphertextPayload,
}
impl HeaderType for Ciphertext {
type Version = CiphertextVersion;
type Subtype = CiphertextSubtype;
fn data_type() -> DataType {
DataType::Ciphertext
}
}
#[derive(Clone, Debug)]
#[cfg_attr(feature = "fuzz", derive(Arbitrary))]
enum CiphertextPayload {
V1(CiphertextV1),
V2Symmetric(CiphertextV2Symmetric),
V2Asymmetric(CiphertextV2Asymmetric),
}
pub fn encrypt(data: &[u8], key: &[u8], version: CiphertextVersion) -> Result<Ciphertext> {
encrypt_with_aad(data, key, [].as_slice(), version)
}
pub fn encrypt_with_aad(
data: &[u8],
key: &[u8],
aad: &[u8],
version: CiphertextVersion,
) -> Result<Ciphertext> {
let mut header = Header::default();
header.data_subtype = CiphertextSubtype::Symmetric;
let payload = match version {
CiphertextVersion::V1 => {
header.version = CiphertextVersion::V1;
CiphertextPayload::V1(CiphertextV1::encrypt(data, key, aad, &header)?)
}
CiphertextVersion::V2 | CiphertextVersion::Latest => {
header.version = CiphertextVersion::V2;
CiphertextPayload::V2Symmetric(CiphertextV2Symmetric::encrypt(data, key, aad, &header)?)
} };
Ok(Ciphertext { header, payload })
}
pub fn encrypt_asymmetric(
data: &[u8],
public_key: &PublicKey,
version: CiphertextVersion,
) -> Result<Ciphertext> {
encrypt_asymmetric_with_aad(data, public_key, [].as_slice(), version)
}
pub fn encrypt_asymmetric_with_aad(
data: &[u8],
public_key: &PublicKey,
aad: &[u8],
version: CiphertextVersion,
) -> Result<Ciphertext> {
let mut header = Header::default();
header.data_subtype = CiphertextSubtype::Asymmetric;
let payload = match version {
CiphertextVersion::V2 | CiphertextVersion::Latest => {
header.version = CiphertextVersion::V2;
CiphertextPayload::V2Asymmetric(CiphertextV2Asymmetric::encrypt(
data, public_key, aad, &header,
)?)
}
_ => return Err(Error::UnknownVersion),
};
Ok(Ciphertext { header, payload })
}
impl Ciphertext {
pub fn decrypt(&self, key: &[u8]) -> Result<Vec<u8>> {
self.decrypt_with_aad(key, [].as_slice())
}
pub fn decrypt_with_aad(&self, key: &[u8], aad: &[u8]) -> Result<Vec<u8>> {
match &self.payload {
CiphertextPayload::V1(x) => x.decrypt(key, aad, &self.header),
CiphertextPayload::V2Symmetric(x) => x.decrypt(key, aad, &self.header),
_ => Err(Error::InvalidDataType),
}
}
pub fn decrypt_asymmetric(&self, private_key: &PrivateKey) -> Result<Vec<u8>> {
self.decrypt_asymmetric_with_aad(private_key, [].as_slice())
}
pub fn decrypt_asymmetric_with_aad(
&self,
private_key: &PrivateKey,
aad: &[u8],
) -> Result<Vec<u8>> {
match &self.payload {
CiphertextPayload::V2Asymmetric(x) => x.decrypt(private_key, aad, &self.header),
CiphertextPayload::V1(_) => Err(Error::UnknownVersion),
_ => Err(Error::InvalidDataType),
}
}
}
impl From<Ciphertext> for Vec<u8> {
fn from(data: Ciphertext) -> 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 Ciphertext {
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 {
CiphertextVersion::V1 => {
CiphertextPayload::V1(CiphertextV1::try_from(&data[Header::len()..])?)
}
CiphertextVersion::V2 => match header.data_subtype {
CiphertextSubtype::Symmetric | CiphertextSubtype::None => {
CiphertextPayload::V2Symmetric(CiphertextV2Symmetric::try_from(
&data[Header::len()..],
)?)
}
CiphertextSubtype::Asymmetric => CiphertextPayload::V2Asymmetric(
CiphertextV2Asymmetric::try_from(&data[Header::len()..])?,
),
},
_ => return Err(Error::UnknownVersion),
};
Ok(Self { header, payload })
}
}
impl From<CiphertextPayload> for Vec<u8> {
fn from(data: CiphertextPayload) -> Self {
match data {
CiphertextPayload::V1(x) => x.into(),
CiphertextPayload::V2Symmetric(x) => x.into(),
CiphertextPayload::V2Asymmetric(x) => x.into(),
}
}
}
#[test]
fn encrypt_decrypt_test() {
let key = "0123456789abcdefghijkl".as_bytes();
let data = "This is a very complex string of character that we need to encrypt".as_bytes();
let encrypted = encrypt(data, key, CiphertextVersion::Latest).unwrap();
let encrypted: Vec<u8> = encrypted.into();
let encrypted = Ciphertext::try_from(encrypted.as_slice()).unwrap();
let decrypted = encrypted.decrypt(key).unwrap();
assert_eq!(decrypted, data);
}
#[test]
fn encrypt_decrypt_aad_test() {
let key = b"0123456789abcdefghijkl";
let data = b"This is a very complex string of character that we need to encrypt";
let aad = b"This is some public data that we want to authenticate";
let wrong_aad = b"this is some public data that we want to authenticate";
let encrypted = encrypt_with_aad(data, key, aad, CiphertextVersion::Latest).unwrap();
let encrypted: Vec<u8> = encrypted.into();
let encrypted = Ciphertext::try_from(encrypted.as_slice()).unwrap();
let decrypted = encrypted.decrypt_with_aad(key, aad).unwrap();
assert_eq!(decrypted, data);
let decrypted = encrypted.decrypt_with_aad(key, wrong_aad);
assert!(decrypted.is_err());
let decrypted = encrypted.decrypt(key);
assert!(decrypted.is_err());
}
#[test]
fn encrypt_decrypt_v1_test() {
let key = "0123456789abcdefghijkl".as_bytes();
let data = "This is a very complex string of character that we need to encrypt".as_bytes();
let encrypted = encrypt(data, key, CiphertextVersion::V1).unwrap();
assert_eq!(encrypted.header.version, CiphertextVersion::V1);
let encrypted: Vec<u8> = encrypted.into();
let encrypted = Ciphertext::try_from(encrypted.as_slice()).unwrap();
let decrypted = encrypted.decrypt(key).unwrap();
assert_eq!(decrypted, data);
}
#[test]
fn encrypt_decrypt_aad_v1_test() {
let key = b"0123456789abcdefghijkl";
let data = b"This is a very complex string of character that we need to encrypt";
let aad = b"This is some public data that we want to authenticate";
let wrong_aad = b"this is some public data that we want to authenticate";
let encrypted = encrypt_with_aad(data, key, aad, CiphertextVersion::V1).unwrap();
let encrypted: Vec<u8> = encrypted.into();
let encrypted = Ciphertext::try_from(encrypted.as_slice()).unwrap();
let decrypted = encrypted.decrypt_with_aad(key, aad).unwrap();
assert_eq!(decrypted, data);
let decrypted = encrypted.decrypt_with_aad(key, wrong_aad);
assert!(decrypted.is_err());
let decrypted = encrypted.decrypt(key);
assert!(decrypted.is_err());
}
#[test]
fn encrypt_decrypt_v2_test() {
let key = "0123456789abcdefghijkl".as_bytes();
let data = "This is a very complex string of character that we need to encrypt".as_bytes();
let encrypted = encrypt(data, key, CiphertextVersion::V2).unwrap();
assert_eq!(encrypted.header.version, CiphertextVersion::V2);
let encrypted: Vec<u8> = encrypted.into();
let encrypted = Ciphertext::try_from(encrypted.as_slice()).unwrap();
let decrypted = encrypted.decrypt(key).unwrap();
assert_eq!(decrypted, data);
}
#[test]
fn encrypt_decrypt_aad_v2_test() {
let key = b"0123456789abcdefghijkl";
let data = b"This is a very complex string of character that we need to encrypt";
let aad = b"This is some public data that we want to authenticate";
let wrong_aad = b"this is some public data that we want to authenticate";
let encrypted = encrypt_with_aad(data, key, aad, CiphertextVersion::V2).unwrap();
let encrypted: Vec<u8> = encrypted.into();
let encrypted = Ciphertext::try_from(encrypted.as_slice()).unwrap();
let decrypted = encrypted.decrypt_with_aad(key, aad).unwrap();
assert_eq!(decrypted, data);
let decrypted = encrypted.decrypt_with_aad(key, wrong_aad);
assert!(decrypted.is_err());
let decrypted = encrypted.decrypt(key);
assert!(decrypted.is_err());
}
#[test]
fn asymmetric_test() {
use super::key::{generate_keypair, KeyVersion};
let test_plaintext = b"this is a test data";
let keypair = generate_keypair(KeyVersion::Latest);
let encrypted_data = encrypt_asymmetric(
test_plaintext,
&keypair.public_key,
CiphertextVersion::Latest,
)
.unwrap();
let encrypted_data_vec: Vec<u8> = encrypted_data.into();
assert_ne!(encrypted_data_vec.len(), 0);
let encrypted_data = Ciphertext::try_from(encrypted_data_vec.as_slice()).unwrap();
let decrypted_data = encrypted_data
.decrypt_asymmetric(&keypair.private_key)
.unwrap();
assert_eq!(decrypted_data, test_plaintext);
}
#[test]
fn asymmetric_aad_test() {
use super::key::{generate_keypair, KeyVersion};
let test_plaintext = b"this is a test data";
let aad = b"This is some public data that we want to authenticate";
let wrong_aad = b"this is some public data that we want to authenticate";
let keypair = generate_keypair(KeyVersion::Latest);
let encrypted_data = encrypt_asymmetric_with_aad(
test_plaintext,
&keypair.public_key,
aad,
CiphertextVersion::Latest,
)
.unwrap();
let encrypted_data_vec: Vec<u8> = encrypted_data.into();
assert_ne!(encrypted_data_vec.len(), 0);
let encrypted_data = Ciphertext::try_from(encrypted_data_vec.as_slice()).unwrap();
let decrypted_data = encrypted_data
.decrypt_asymmetric_with_aad(&keypair.private_key, aad)
.unwrap();
assert_eq!(decrypted_data, test_plaintext);
let decrypted = encrypted_data.decrypt_asymmetric_with_aad(&keypair.private_key, wrong_aad);
assert!(decrypted.is_err());
let decrypted = encrypted_data.decrypt_asymmetric(&keypair.private_key);
assert!(decrypted.is_err());
}
#[test]
fn asymmetric_test_v2() {
use super::key::{generate_keypair, KeyVersion};
let test_plaintext = b"this is a test data";
let keypair = generate_keypair(KeyVersion::V1);
let encrypted_data =
encrypt_asymmetric(test_plaintext, &keypair.public_key, CiphertextVersion::V2).unwrap();
assert_eq!(encrypted_data.header.version, CiphertextVersion::V2);
let encrypted_data_vec: Vec<u8> = encrypted_data.into();
assert_ne!(encrypted_data_vec.len(), 0);
let encrypted_data = Ciphertext::try_from(encrypted_data_vec.as_slice()).unwrap();
let decrypted_data = encrypted_data
.decrypt_asymmetric(&keypair.private_key)
.unwrap();
assert_eq!(decrypted_data, test_plaintext);
}
#[test]
fn asymmetric_aad_test_v2() {
use super::key::{generate_keypair, KeyVersion};
let test_plaintext = b"this is a test data";
let aad = b"This is some public data that we want to authenticate";
let wrong_aad = b"this is some public data that we want to authenticate";
let keypair = generate_keypair(KeyVersion::V1);
let encrypted_data = encrypt_asymmetric_with_aad(
test_plaintext,
&keypair.public_key,
aad,
CiphertextVersion::V2,
)
.unwrap();
let encrypted_data_vec: Vec<u8> = encrypted_data.into();
assert_ne!(encrypted_data_vec.len(), 0);
let encrypted_data = Ciphertext::try_from(encrypted_data_vec.as_slice()).unwrap();
let decrypted_data = encrypted_data
.decrypt_asymmetric_with_aad(&keypair.private_key, aad)
.unwrap();
assert_eq!(decrypted_data, test_plaintext);
let decrypted = encrypted_data.decrypt_asymmetric_with_aad(&keypair.private_key, wrong_aad);
assert!(decrypted.is_err());
let decrypted = encrypted_data.decrypt_asymmetric(&keypair.private_key);
assert!(decrypted.is_err());
}