use std::marker::PhantomData;
use crate::error::EnardError;
use cipher::{IvSizeUser, KeyIvInit, KeySizeUser};
#[cfg(feature = "random")]
use rand::{CryptoRng, Rng};
type TResult<T> = Result<T, EnardError>;
pub trait CipherName {
fn name() -> &'static [u8];
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct CipherMeta {
pub name: &'static [u8],
pub key_size: usize,
pub iv_size: usize,
}
impl CipherMeta {
#[cfg(feature = "random")]
pub fn generate_iv<R: CryptoRng + Rng>(&self, rng: &mut R) -> Vec<u8> {
let mut iv = vec![0u8; self.iv_size];
rng.fill_bytes(iv.as_mut_slice());
iv
}
}
pub trait CipherFactory<C> {
fn get_meta(&self, name: &[u8]) -> TResult<CipherMeta>;
fn create(&self, name: &[u8], key: &[u8], iv: &[u8]) -> TResult<C>;
}
pub struct SimpleCipherFactory<C> {
phantom: PhantomData<C>,
}
impl<C> SimpleCipherFactory<C> {
pub fn new() -> Self {
Self {
phantom: PhantomData,
}
}
}
impl<C> CipherFactory<C> for SimpleCipherFactory<C>
where
C: CipherName + IvSizeUser + KeySizeUser + KeyIvInit,
{
fn get_meta(&self, name: &[u8]) -> TResult<CipherMeta> {
check_supported_name::<C>(name)?;
Ok(CipherMeta {
name: C::name(),
key_size: C::key_size(),
iv_size: C::iv_size(),
})
}
fn create(&self, name: &[u8], key: &[u8], iv: &[u8]) -> TResult<C> {
check_supported_name::<C>(name)?;
Ok(C::new_from_slices(key, iv)?)
}
}
pub fn check_supported_name<C: CipherName>(name: &[u8]) -> TResult<()> {
if name == b"" || name == C::name() {
Ok(())
} else {
Err(EnardError::new_unsupported_encryption(name))
}
}
pub trait GetFactory<Cf> {
fn factory() -> Cf;
}
impl<C> GetFactory<SimpleCipherFactory<C>> for C
where
C: CipherName + IvSizeUser + KeySizeUser + KeyIvInit,
{
fn factory() -> SimpleCipherFactory<C> {
SimpleCipherFactory::new()
}
}
macro_rules! impl_cipher_name {
(for $type:ty) => {
impl $crate::cipher_factory::CipherName for $type {
fn name() -> &'static [u8] {
stringify!($type).as_bytes()
}
}
};
(for $type:ty => $name:expr) => {
impl $crate::cipher_factory::CipherName for $type {
fn name() -> &'static [u8] {
$name.as_bytes()
}
}
};
}
pub(crate) use impl_cipher_name;