use crate::algorithms::aead::{AeadAlgorithm, AesKeySize};
use crate::error::{Error, FormatError, Result};
use crate::keys::aead::{AeadKey as UntypedAeadKey, TypedAeadKey};
use crate::traits::AeadAlgorithmTrait;
use rand::TryRngCore;
use rand::rngs::OsRng;
use seal_crypto::prelude::{AeadCipher, AeadDecryptor, AeadEncryptor, Key};
use seal_crypto::schemes::aead::aes_gcm::{Aes128Gcm, Aes256Gcm};
use seal_crypto::schemes::aead::chacha20_poly1305::{ChaCha20Poly1305, XChaCha20Poly1305};
use std::ops::Deref;
macro_rules! impl_aead_algorithm {
($wrapper:ident, $algo:ty, $algo_enum:expr) => {
#[derive(Clone, Debug, Default)]
pub struct $wrapper;
impl $wrapper {
pub fn new() -> Self {
Self
}
}
impl From<$wrapper> for Box<dyn AeadAlgorithmTrait> {
fn from(wrapper: $wrapper) -> Self {
Box::new(wrapper)
}
}
impl AeadAlgorithmTrait for $wrapper {
fn encrypt(
&self,
plaintext: &[u8],
key: &TypedAeadKey,
nonce: &[u8],
aad: Option<&[u8]>,
) -> Result<Vec<u8>> {
if key.algorithm() != $algo_enum {
return Err(Error::FormatError(FormatError::InvalidKeyType));
}
type KT = $algo;
let key =
<KT as seal_crypto::prelude::SymmetricKeySet>::Key::from_bytes(key.as_bytes())?;
KT::encrypt(&key, nonce, plaintext, aad).map_err(Error::from)
}
fn encrypt_to_buffer(
&self,
plaintext: &[u8],
output: &mut [u8],
key: &TypedAeadKey,
nonce: &[u8],
aad: Option<&[u8]>,
) -> Result<usize> {
if key.algorithm() != $algo_enum {
return Err(Error::FormatError(FormatError::InvalidKeyType));
}
type KT = $algo;
let key =
<KT as seal_crypto::prelude::SymmetricKeySet>::Key::from_bytes(key.as_bytes())?;
KT::encrypt_to_buffer(&key, nonce, plaintext, output, aad).map_err(Error::from)
}
fn decrypt(
&self,
ciphertext: &[u8],
key: &TypedAeadKey,
nonce: &[u8],
aad: Option<&[u8]>,
) -> Result<Vec<u8>> {
if key.algorithm() != $algo_enum {
return Err(Error::FormatError(FormatError::InvalidKeyType));
}
type KT = $algo;
let key =
<KT as seal_crypto::prelude::SymmetricKeySet>::Key::from_bytes(key.as_bytes())?;
KT::decrypt(&key, nonce, ciphertext, aad).map_err(Error::from)
}
fn decrypt_to_buffer(
&self,
ciphertext: &[u8],
output: &mut [u8],
key: &TypedAeadKey,
nonce: &[u8],
aad: Option<&[u8]>,
) -> Result<usize> {
if key.algorithm() != $algo_enum {
return Err(Error::FormatError(FormatError::InvalidKeyType));
}
type KT = $algo;
let key =
<KT as seal_crypto::prelude::SymmetricKeySet>::Key::from_bytes(key.as_bytes())?;
KT::decrypt_to_buffer(&key, nonce, ciphertext, output, aad).map_err(Error::from)
}
fn clone_box(&self) -> Box<dyn AeadAlgorithmTrait> {
Box::new(self.clone())
}
fn algorithm(&self) -> AeadAlgorithm {
$algo_enum
}
fn key_size(&self) -> usize {
<$algo>::KEY_SIZE
}
fn nonce_size(&self) -> usize {
<$algo>::NONCE_SIZE
}
fn tag_size(&self) -> usize {
<$algo>::TAG_SIZE
}
fn generate_typed_key(&self) -> Result<TypedAeadKey> {
TypedAeadKey::generate($algo_enum)
}
fn generate_untyped_key(&self) -> Result<UntypedAeadKey> {
self.generate_typed_key().map(|k| k.untyped())
}
fn into_boxed(self) -> Box<dyn AeadAlgorithmTrait> {
Box::new(self)
}
}
};
}
#[derive(Clone, Debug)]
pub struct AeadAlgorithmWrapper {
pub(crate) algorithm: Box<dyn AeadAlgorithmTrait>,
}
impl Deref for AeadAlgorithmWrapper {
type Target = Box<dyn AeadAlgorithmTrait>;
fn deref(&self) -> &Self::Target {
&self.algorithm
}
}
impl Into<Box<dyn AeadAlgorithmTrait>> for AeadAlgorithmWrapper {
fn into(self) -> Box<dyn AeadAlgorithmTrait> {
self.algorithm
}
}
impl AeadAlgorithmWrapper {
pub fn new(algorithm: Box<dyn AeadAlgorithmTrait>) -> Self {
Self { algorithm }
}
pub fn from_enum(algorithm: AeadAlgorithm) -> Self {
let algorithm: Box<dyn AeadAlgorithmTrait> = match algorithm {
AeadAlgorithm::AesGcm(AesKeySize::K128) => Box::new(Aes128GcmWrapper::new()),
AeadAlgorithm::AesGcm(AesKeySize::K256) => Box::new(Aes256GcmWrapper::new()),
AeadAlgorithm::ChaCha20Poly1305 => Box::new(ChaCha20Poly1305Wrapper::new()),
AeadAlgorithm::XChaCha20Poly1305 => Box::new(XChaCha20Poly1305Wrapper::new()),
};
Self::new(algorithm)
}
pub fn generate_typed_key(&self) -> Result<TypedAeadKey> {
self.algorithm.generate_typed_key()
}
pub fn generate_untyped_key(&self) -> Result<UntypedAeadKey> {
self.algorithm.generate_untyped_key()
}
pub fn generate_nonce(&self) -> Result<Vec<u8>> {
let mut nonce = vec![0u8; self.nonce_size()];
OsRng.try_fill_bytes(&mut nonce)?;
Ok(nonce)
}
}
impl AeadAlgorithmTrait for AeadAlgorithmWrapper {
fn encrypt(
&self,
plaintext: &[u8],
key: &TypedAeadKey,
nonce: &[u8],
aad: Option<&[u8]>,
) -> Result<Vec<u8>> {
self.algorithm.encrypt(plaintext, key, nonce, aad)
}
fn encrypt_to_buffer(
&self,
plaintext: &[u8],
output: &mut [u8],
key: &TypedAeadKey,
nonce: &[u8],
aad: Option<&[u8]>,
) -> Result<usize> {
self.algorithm
.encrypt_to_buffer(plaintext, output, key, nonce, aad)
}
fn decrypt(
&self,
ciphertext: &[u8],
key: &TypedAeadKey,
nonce: &[u8],
aad: Option<&[u8]>,
) -> Result<Vec<u8>> {
self.algorithm.decrypt(ciphertext, key, nonce, aad)
}
fn decrypt_to_buffer(
&self,
ciphertext: &[u8],
output: &mut [u8],
key: &TypedAeadKey,
nonce: &[u8],
aad: Option<&[u8]>,
) -> Result<usize> {
self.algorithm
.decrypt_to_buffer(ciphertext, output, key, nonce, aad)
}
fn generate_typed_key(&self) -> Result<TypedAeadKey> {
self.algorithm.generate_typed_key()
}
fn generate_untyped_key(&self) -> Result<UntypedAeadKey> {
self.algorithm.generate_untyped_key()
}
fn algorithm(&self) -> AeadAlgorithm {
self.algorithm.algorithm()
}
fn key_size(&self) -> usize {
self.algorithm.key_size()
}
fn nonce_size(&self) -> usize {
self.algorithm.nonce_size()
}
fn tag_size(&self) -> usize {
self.algorithm.tag_size()
}
fn into_boxed(self) -> Box<dyn AeadAlgorithmTrait> {
self.algorithm
}
fn clone_box(&self) -> Box<dyn AeadAlgorithmTrait> {
Box::new(self.clone())
}
}
impl From<AeadAlgorithm> for AeadAlgorithmWrapper {
fn from(algorithm: AeadAlgorithm) -> Self {
Self::from_enum(algorithm)
}
}
impl From<Box<dyn AeadAlgorithmTrait>> for AeadAlgorithmWrapper {
fn from(algorithm: Box<dyn AeadAlgorithmTrait>) -> Self {
Self::new(algorithm)
}
}
impl_aead_algorithm!(
Aes128GcmWrapper,
Aes128Gcm,
AeadAlgorithm::AesGcm(AesKeySize::K128)
);
impl_aead_algorithm!(
Aes256GcmWrapper,
Aes256Gcm,
AeadAlgorithm::AesGcm(AesKeySize::K256)
);
impl_aead_algorithm!(
ChaCha20Poly1305Wrapper,
ChaCha20Poly1305,
AeadAlgorithm::ChaCha20Poly1305
);
impl_aead_algorithm!(
XChaCha20Poly1305Wrapper,
XChaCha20Poly1305,
AeadAlgorithm::XChaCha20Poly1305
);
#[cfg(test)]
mod tests {
#[cfg(feature = "kdf")]
use crate::algorithms::kdf::{key::KdfKeyAlgorithm, passwd::KdfPasswordAlgorithm};
#[cfg(feature = "xof")]
use crate::algorithms::xof::XofAlgorithm;
use crate::keys::aead::{AeadKey, TypedAeadKey};
use crate::prelude::AeadAlgorithm;
use seal_crypto::secrecy::SecretBox;
#[test]
fn test_symmetric_key_generate() {
use crate::keys::aead::AeadKey;
use seal_crypto::{prelude::AeadCipher, schemes::aead::aes_gcm::Aes256Gcm};
let key_len = <Aes256Gcm as AeadCipher>::KEY_SIZE;
let key1 = AeadKey::generate(key_len).unwrap();
let key2 = AeadKey::generate(key_len).unwrap();
assert_eq!(key1.as_bytes().len(), key_len);
assert_eq!(key2.as_bytes().len(), key_len);
assert_ne!(
key1.as_bytes(),
key2.as_bytes(),
"Generated keys should be unique"
);
}
#[test]
fn test_symmetric_key_from_bytes() {
let key_bytes = vec![0u8; 32];
let key = AeadKey::new(key_bytes.clone());
assert_eq!(key.as_bytes(), key_bytes.as_slice());
}
#[test]
#[cfg(feature = "kdf")]
fn test_typed_symmetric_key_derive_from_kdf() {
let master_key_bytes = vec![0u8; 32];
let salt = b"salt_value";
let info1 = b"encryption_key";
let info2 = b"signing_key";
let kdf_algorithm = KdfKeyAlgorithm::build().hkdf_sha256();
let symmetric_algorithm = AeadAlgorithm::build().aes256_gcm();
let derived_key1 = TypedAeadKey::derive_from_kdf(
&master_key_bytes,
kdf_algorithm.clone(),
Some(salt),
Some(info1),
symmetric_algorithm,
)
.unwrap();
let derived_key2 = TypedAeadKey::derive_from_kdf(
&master_key_bytes,
kdf_algorithm.clone(),
Some(salt),
Some(info2),
symmetric_algorithm,
)
.unwrap();
let derived_key1_again = TypedAeadKey::derive_from_kdf(
&master_key_bytes,
kdf_algorithm.clone(),
Some(salt),
Some(info1),
symmetric_algorithm,
)
.unwrap();
assert_ne!(derived_key1.as_bytes(), derived_key2.as_bytes());
assert_eq!(derived_key1.as_bytes(), derived_key1_again.as_bytes());
assert_eq!(derived_key1.algorithm(), symmetric_algorithm);
}
#[test]
#[cfg(feature = "kdf")]
fn test_typed_symmetric_key_derive_from_password() {
let password = SecretBox::new(Box::from(b"my_secure_password".as_slice()));
let salt = b"random_salt_value";
let symmetric_algorithm = AeadAlgorithm::build().aes256_gcm();
let deriver = KdfPasswordAlgorithm::build()
.pbkdf2_sha256_with_params(1000)
.into_wrapper();
let derived_key1 = TypedAeadKey::derive_from_password(
&password,
deriver.clone(),
salt,
symmetric_algorithm,
)
.unwrap();
let derived_key2 = TypedAeadKey::derive_from_password(
&password,
deriver.clone(),
salt,
symmetric_algorithm,
)
.unwrap();
assert_eq!(derived_key1.as_bytes(), derived_key2.as_bytes());
let different_password = SecretBox::new(Box::from(b"different_password".as_slice()));
let derived_key3 = TypedAeadKey::derive_from_password(
&different_password,
deriver.clone(),
salt,
symmetric_algorithm,
)
.unwrap();
assert_ne!(derived_key1.as_bytes(), derived_key3.as_bytes());
let different_salt = b"different_salt_value";
let derived_key4 = TypedAeadKey::derive_from_password(
&password,
deriver.clone(),
different_salt,
symmetric_algorithm,
)
.unwrap();
assert_ne!(derived_key1.as_bytes(), derived_key4.as_bytes());
assert_eq!(derived_key1.algorithm(), symmetric_algorithm);
}
#[test]
#[cfg(feature = "xof")]
fn test_typed_symmetric_key_derive_from_xof() {
use crate::traits::XofAlgorithmTrait;
let seed = [0u8; 32];
let symmetric_algo_1 = AeadAlgorithm::build().aes128_gcm();
let symmetric_algo_2 = AeadAlgorithm::build().aes256_gcm();
let mut reader = XofAlgorithm::build()
.shake128()
.into_wrapper()
.reader(&seed, None, None)
.unwrap();
let key1 = TypedAeadKey::derive_from_xof(&mut reader, symmetric_algo_1).unwrap();
let key2 = TypedAeadKey::derive_from_xof(&mut reader, symmetric_algo_2).unwrap();
assert_ne!(key1.as_bytes(), key2.as_bytes());
assert_eq!(key1.as_bytes().len(), 16);
assert_eq!(key1.algorithm(), symmetric_algo_1);
assert_eq!(key2.as_bytes().len(), 32);
assert_eq!(key2.algorithm(), symmetric_algo_2);
}
#[test]
#[cfg(feature = "kdf")]
fn test_kdf_derivation_output_length() {
let master_key_bytes = vec![0u8; 32];
let kdf_algorithm = KdfKeyAlgorithm::build().hkdf_sha256();
let salt = b"salt";
let info = b"info";
let sym_alg_128 = AeadAlgorithm::build().aes128_gcm();
let sym_alg_256 = AeadAlgorithm::build().aes256_gcm();
let key_128 = TypedAeadKey::derive_from_kdf(
&master_key_bytes,
kdf_algorithm.clone(),
Some(salt),
Some(info),
sym_alg_128,
)
.unwrap();
let key_256 = TypedAeadKey::derive_from_kdf(
&master_key_bytes,
kdf_algorithm.clone(),
Some(salt),
Some(info),
sym_alg_256,
)
.unwrap();
assert_eq!(key_128.as_bytes().len(), 16);
assert_eq!(key_128.algorithm(), sym_alg_128);
assert_eq!(key_256.as_bytes().len(), 32);
assert_eq!(key_256.algorithm(), sym_alg_256);
}
}