use std::{fmt::Formatter, marker::PhantomData, ops::Sub};
#[allow(deprecated)] use cipher::{
AlgorithmName, ArrayLength, Block, BlockSizeUser, Iv, IvSizeUser, Key, KeyInit, KeyIvInit,
KeySizeUser, StreamCipher, generic_array::GenericArray, inout::InOut,
};
use digest::{Digest, OutputSizeUser};
use typenum::{B1, Diff, IsEqual, IsGreater, Unsigned};
use crate::crypto::crypto_traits::PRP;
#[derive(Clone, zeroize::ZeroizeOnDrop)]
pub struct Lioness<H: KeySizeUser + OutputSizeUser, S: KeySizeUser + IvSizeUser, B: ArrayLength<u8>>
where
H::OutputSize: IsEqual<S::KeySize, Output = B1>,
H::KeySize: IsEqual<S::KeySize, Output = B1>,
B: IsGreater<<S as KeySizeUser>::KeySize, Output = B1>,
{
k1: GenericArray<u8, S::KeySize>,
k2: GenericArray<u8, H::KeySize>,
k3: GenericArray<u8, S::KeySize>,
k4: GenericArray<u8, H::KeySize>,
iv1: GenericArray<u8, S::IvSize>,
iv2: GenericArray<u8, S::IvSize>,
_phantom: PhantomData<(H, S, B)>,
}
impl<H: KeySizeUser + OutputSizeUser, S: KeySizeUser + IvSizeUser, B: ArrayLength<u8>> KeySizeUser
for Lioness<H, S, B>
where
H::OutputSize: IsEqual<S::KeySize, Output = B1>,
H::KeySize: IsEqual<S::KeySize, Output = B1>,
B: IsGreater<<S as KeySizeUser>::KeySize, Output = B1>,
H::OutputSize: std::ops::Mul<cipher::consts::U4>,
<H::OutputSize as std::ops::Mul<cipher::consts::U4>>::Output: ArrayLength<u8>,
{
type KeySize = typenum::Prod<H::OutputSize, cipher::consts::U4>;
}
impl<H: KeySizeUser + OutputSizeUser, S: KeySizeUser + IvSizeUser, B: ArrayLength<u8>> IvSizeUser
for Lioness<H, S, B>
where
H::OutputSize: IsEqual<S::KeySize, Output = B1>,
H::KeySize: IsEqual<S::KeySize, Output = B1>,
B: IsGreater<<S as KeySizeUser>::KeySize, Output = B1>,
S::IvSize: std::ops::Mul<cipher::consts::U2>,
<S::IvSize as std::ops::Mul<cipher::consts::U2>>::Output: ArrayLength<u8>,
{
type IvSize = typenum::Prod<S::IvSize, cipher::consts::U2>;
}
impl<H: KeySizeUser + OutputSizeUser, S: KeySizeUser + IvSizeUser, B: ArrayLength<u8>> BlockSizeUser
for Lioness<H, S, B>
where
H::OutputSize: IsEqual<S::KeySize, Output = B1>,
H::KeySize: IsEqual<S::KeySize, Output = B1>,
B: IsGreater<<S as KeySizeUser>::KeySize, Output = B1>,
H::OutputSize: std::ops::Mul<cipher::consts::U4>,
S::IvSize: std::ops::Mul<cipher::consts::U2>,
<H::OutputSize as std::ops::Mul<cipher::consts::U4>>::Output: ArrayLength<u8>,
<S::IvSize as std::ops::Mul<cipher::consts::U2>>::Output: ArrayLength<u8>,
{
type BlockSize = B;
}
impl<H: KeySizeUser + OutputSizeUser, S: KeySizeUser + IvSizeUser, B: ArrayLength<u8>> KeyIvInit
for Lioness<H, S, B>
where
H::OutputSize: IsEqual<S::KeySize, Output = B1>,
H::KeySize: IsEqual<S::KeySize, Output = B1>,
B: IsGreater<<S as KeySizeUser>::KeySize, Output = B1>,
H::OutputSize: std::ops::Mul<cipher::consts::U4>,
S::IvSize: std::ops::Mul<cipher::consts::U2>,
<H::OutputSize as std::ops::Mul<cipher::consts::U4>>::Output: ArrayLength<u8>,
<S::IvSize as std::ops::Mul<cipher::consts::U2>>::Output: ArrayLength<u8>,
{
fn new(key: &Key<Self>, iv: &Iv<Self>) -> Self {
let k = H::OutputSize::to_usize();
let i = S::IvSize::to_usize();
Self {
k1: GenericArray::clone_from_slice(&key[0..k]),
k2: GenericArray::clone_from_slice(&key[k..2 * k]),
k3: GenericArray::clone_from_slice(&key[2 * k..3 * k]),
k4: GenericArray::clone_from_slice(&key[3 * k..4 * k]),
iv1: GenericArray::clone_from_slice(&iv[0..i]),
iv2: GenericArray::clone_from_slice(&iv[i..2 * i]),
_phantom: Default::default(),
}
}
}
impl<
H: KeySizeUser + OutputSizeUser + AlgorithmName,
S: AlgorithmName + KeySizeUser + IvSizeUser,
B: ArrayLength<u8>,
> AlgorithmName for Lioness<H, S, B>
where
H::OutputSize: IsEqual<<S as KeySizeUser>::KeySize, Output = B1>,
H::KeySize: IsEqual<S::KeySize, Output = B1>,
B: IsGreater<<S as KeySizeUser>::KeySize, Output = B1>,
{
fn write_alg_name(f: &mut Formatter<'_>) -> std::fmt::Result {
f.write_str("Lioness<")?;
H::write_alg_name(f)?;
f.write_str(", ")?;
S::write_alg_name(f)?;
f.write_str(">")
}
}
impl<H: Digest + KeyInit, S: StreamCipher + KeyIvInit, B: ArrayLength<u8>> Lioness<H, S, B>
where
H::OutputSize: IsEqual<<S as KeySizeUser>::KeySize, Output = B1>,
H::KeySize: IsEqual<S::KeySize, Output = B1>,
B: IsGreater<<S as KeySizeUser>::KeySize, Output = B1> + Sub<<S as KeySizeUser>::KeySize>,
<B as Sub<<S as KeySizeUser>::KeySize>>::Output: ArrayLength<u8>,
H::OutputSize: std::ops::Mul<cipher::consts::U4>,
<S as IvSizeUser>::IvSize: std::ops::Mul<cipher::consts::U2>,
<H::OutputSize as std::ops::Mul<cipher::consts::U4>>::Output: ArrayLength<u8>,
<<S as IvSizeUser>::IvSize as std::ops::Mul<cipher::consts::U2>>::Output: ArrayLength<u8>,
{
const K: usize = <S as KeySizeUser>::KeySize::USIZE;
pub fn encrypt_block(&self, mut block: InOut<'_, '_, Block<Self>>) {
let mut left_prime = GenericArray::<u8, <S as KeySizeUser>::KeySize>::clone_from_slice(
&block.get_in()[0..Self::K],
);
for i in 0..Self::K {
left_prime[i] ^= self.k1[i];
}
let r_cpy = GenericArray::<u8, Diff<B, <S as KeySizeUser>::KeySize>>::clone_from_slice(
&block.get_in()[Self::K..B::USIZE],
);
S::new(&left_prime, &self.iv1)
.apply_keystream_b2b(&r_cpy, &mut block.get_out()[Self::K..B::USIZE])
.expect("slices have always equal sizes");
let r_prime = <H as KeyInit>::new(&self.k2)
.chain_update(&block.get_out()[Self::K..B::USIZE])
.finalize();
for i in 0..Self::K {
block.get_out()[i] = block.get_in()[i] ^ r_prime[i];
}
let mut left_prime = GenericArray::<u8, <S as KeySizeUser>::KeySize>::clone_from_slice(
&block.get_out()[0..Self::K],
);
for i in 0..Self::K {
left_prime[i] ^= self.k3[i];
}
S::new(&left_prime, &self.iv2).apply_keystream(&mut block.get_out()[Self::K..B::USIZE]);
let r_prime = <H as KeyInit>::new(&self.k4)
.chain_update(&block.get_out()[Self::K..B::USIZE])
.finalize();
for i in 0..Self::K {
block.get_out()[i] ^= r_prime[i];
}
}
pub fn decrypt_block(&self, mut block: InOut<'_, '_, Block<Self>>) {
let r_prime = <H as KeyInit>::new(&self.k4)
.chain_update(&block.get_in()[Self::K..B::USIZE])
.finalize();
for i in 0..Self::K {
block.get_out()[i] = block.get_in()[i] ^ r_prime[i];
}
let mut left_prime = GenericArray::<u8, <S as KeySizeUser>::KeySize>::clone_from_slice(
&block.get_out()[0..Self::K],
);
for i in 0..Self::K {
left_prime[i] ^= self.k3[i];
}
let r_cpy = GenericArray::<u8, Diff<B, <S as KeySizeUser>::KeySize>>::clone_from_slice(
&block.get_in()[Self::K..B::USIZE],
);
S::new(&left_prime, &self.iv2)
.apply_keystream_b2b(&r_cpy, &mut block.get_out()[Self::K..B::USIZE])
.expect("slices have always equal sizes");
let r_prime = <H as KeyInit>::new(&self.k2)
.chain_update(&block.get_out()[Self::K..B::USIZE])
.finalize();
for i in 0..Self::K {
block.get_out()[i] ^= r_prime[i];
}
let mut left_prime = GenericArray::<u8, <S as KeySizeUser>::KeySize>::clone_from_slice(
&block.get_out()[0..Self::K],
);
for i in 0..Self::K {
left_prime[i] ^= self.k1[i];
}
S::new(&left_prime, &self.iv1).apply_keystream(&mut block.get_out()[Self::K..B::USIZE]);
}
}
impl<H: Digest + KeyInit, S: StreamCipher + KeyIvInit, B: ArrayLength<u8>> PRP for Lioness<H, S, B>
where
H::OutputSize: IsEqual<<S as KeySizeUser>::KeySize, Output = B1>,
H::KeySize: IsEqual<S::KeySize, Output = B1>,
B: IsGreater<<S as KeySizeUser>::KeySize, Output = B1> + Sub<<S as KeySizeUser>::KeySize>,
<B as Sub<<S as KeySizeUser>::KeySize>>::Output: ArrayLength<u8>,
H::OutputSize: std::ops::Mul<cipher::consts::U4>,
<S as IvSizeUser>::IvSize: std::ops::Mul<cipher::consts::U2>,
<H::OutputSize as std::ops::Mul<cipher::consts::U4>>::Output: ArrayLength<u8>,
<<S as IvSizeUser>::IvSize as std::ops::Mul<cipher::consts::U2>>::Output: ArrayLength<u8>,
{
fn forward(&self, data: &mut Block<Self>) {
self.encrypt_block(data.into());
}
fn inverse(&self, data: &mut Block<Self>) {
self.decrypt_block(data.into());
}
}
pub type LionessBlake3ChaCha20<B> = Lioness<blake3::Hasher, chacha20::ChaCha20, B>;
#[cfg(test)]
mod tests {
use hex_literal::hex;
use typenum::{U33, U1024};
use super::*;
#[test]
fn lioness_sizes() {
assert_eq!(
<blake3::Hasher as OutputSizeUser>::output_size(),
chacha20::ChaCha20::key_size()
);
let key_sz = LionessBlake3ChaCha20::<U33>::key_size();
let iv_sz = LionessBlake3ChaCha20::<U33>::iv_size();
let block_sz = LionessBlake3ChaCha20::<U33>::block_size();
assert_eq!(
key_sz,
<blake3::Hasher as OutputSizeUser>::output_size() * 4
);
assert_eq!(iv_sz, chacha20::ChaCha20::iv_size() * 2);
assert_eq!(block_sz, U33::USIZE);
}
#[test]
fn lioness_forward_inverse() {
let lioness = LionessBlake3ChaCha20::<U1024>::new(&Default::default(), &Default::default());
let mut data = GenericArray::<u8, U1024>::default();
let data_clone = data;
assert_eq!(data, data_clone);
lioness.encrypt_block((&mut data).into());
assert_ne!(data, data_clone);
lioness.decrypt_block((&mut data).into());
assert_eq!(data, data_clone);
}
#[test]
fn lioness_forward_kat() {
let lioness = LionessBlake3ChaCha20::<U33>::new(&Default::default(), &Default::default());
let mut data = GenericArray::<u8, U33>::default();
lioness.encrypt_block((&mut data).into());
let ka = hex!("36690b60686f3c997a7bfb3808aa18a1b5808b750587ed04a01ebd836dd3ea97b4");
assert_eq!(data.as_slice(), &ka);
}
#[test]
fn lioness_inverse_kat() {
let lioness = LionessBlake3ChaCha20::<U33>::new(&Default::default(), &Default::default());
let mut data = GenericArray::<u8, U33>::default();
lioness.decrypt_block((&mut data).into());
let ka = hex!("7857b5bb58995ac8c59eff412dad35af72a7d1e1ff1caba132aef382b15789a6cb");
assert_eq!(data.as_slice(), &ka);
}
#[test]
fn lioness_forward_inverse_random() {
let mut k = Key::<LionessBlake3ChaCha20<U1024>>::default();
let mut iv = Iv::<LionessBlake3ChaCha20<U1024>>::default();
crate::crypto_random::random_fill(&mut k);
crate::crypto_random::random_fill(&mut iv);
let lioness = LionessBlake3ChaCha20::<U1024>::new(&k, &iv);
let mut data = GenericArray::<u8, U1024>::default();
crate::crypto_random::random_fill(&mut data);
let data_clone = data;
assert_eq!(data, data_clone);
lioness.encrypt_block((&mut data).into());
assert_ne!(data, data_clone);
lioness.decrypt_block((&mut data).into());
assert_eq!(data, data_clone);
}
#[test]
fn lioness_forward_inverse_random_separate_buffers() {
let mut k = Key::<LionessBlake3ChaCha20<U1024>>::default();
let mut iv = Iv::<LionessBlake3ChaCha20<U1024>>::default();
crate::crypto_random::random_fill(&mut k);
crate::crypto_random::random_fill(&mut iv);
let lioness = LionessBlake3ChaCha20::<U1024>::new(&k, &iv);
let mut data_in = GenericArray::<u8, U1024>::default();
let mut data_out = GenericArray::<u8, U1024>::default();
crate::crypto_random::random_fill(&mut data_in);
let data_orig = data_in;
assert_eq!(data_in, data_orig);
lioness.encrypt_block((&data_in, &mut data_out).into());
assert_ne!(data_out, data_orig);
let data_in = data_out;
let mut data_out = GenericArray::<u8, U1024>::default();
lioness.decrypt_block((&data_in, &mut data_out).into());
assert_eq!(data_out, data_orig);
}
}