#![no_std]
use core::marker::PhantomData;
use blake2::Blake2b;
use cipher::{
InOutBuf,
stream::{StreamCipher, StreamCipherError},
};
use crypto_common::{KeyInit, KeyIvInit, KeySizeUser, OutputSizeUser};
use digest::{CustomizedInit, FixedOutput, Update};
use hybrid_array::{Array, ArraySize};
use thiserror::Error;
use typenum::{IsLessOrEqual, True, U64, Unsigned};
use zerocopy::FromZeros;
const SALT_1: &[u8] = b"k1";
const SALT_2: &[u8] = b"k2";
const SALT_3: &[u8] = b"k3";
const SALT_4: &[u8] = b"k4";
#[derive(Error, Debug, Clone)]
pub enum Error {
#[error("given input data is too short")]
InputTooShort,
}
pub struct Lioness<S, H>
where
S: KeySizeUser,
H: KeySizeUser,
{
k_1: Array<u8, S::KeySize>,
k_2: Array<u8, H::KeySize>,
k_3: Array<u8, S::KeySize>,
k_4: Array<u8, H::KeySize>,
}
fn hash_key<L>(salt: &[u8], input: &[u8]) -> Array<u8, L>
where
L: ArraySize + IsLessOrEqual<U64, Output = True>,
{
let mut hasher = Blake2b::new_customized(salt);
hasher.update(input);
hasher.finalize_fixed()
}
fn xor_assign<L: ArraySize>(a: &mut L::ArrayType<u8>, b: &L::ArrayType<u8>) {
a.as_mut()
.iter_mut()
.zip(b.as_ref().iter())
.for_each(|(x, y)| *x ^= *y);
}
type Key<K> = <<K as KeySizeUser>::KeySize as ArraySize>::ArrayType<u8>;
impl<S, H> Lioness<S, H>
where
S: KeySizeUser<KeySize = H::OutputSize> + KeyInit + StreamCipher,
H: KeySizeUser + KeyInit + FixedOutput + Update,
S::KeySize: IsLessOrEqual<U64, Output = True>,
H::KeySize: IsLessOrEqual<U64, Output = True>,
Array<u8, S::KeySize>: FromZeros,
{
pub fn new_dynamic(key: &[u8]) -> Self {
Self::new(
hash_key::<S::KeySize>(SALT_1, key).0,
hash_key::<H::KeySize>(SALT_2, key).0,
hash_key::<S::KeySize>(SALT_3, key).0,
hash_key::<H::KeySize>(SALT_4, key).0,
)
}
}
impl<S, H> Lioness<S, H>
where
S: KeySizeUser<KeySize = H::OutputSize> + KeyInit + StreamCipher,
H: KeySizeUser + KeyInit + FixedOutput + Update,
Array<u8, S::KeySize>: FromZeros,
{
pub fn new(k_1: Key<S>, k_2: Key<H>, k_3: Key<S>, k_4: Key<H>) -> Self {
Self {
k_1: k_1.into(),
k_2: k_2.into(),
k_3: k_3.into(),
k_4: k_4.into(),
}
}
pub fn encrypt(&self, data: &mut [u8]) -> Result<(), Error> {
let l_len = S::KeySize::USIZE;
if data.len() < l_len {
return Err(Error::InputTooShort);
}
assert!(data.len() >= l_len);
let mut key = <Array<u8, S::KeySize> as FromZeros>::new_zeroed();
key.copy_from_slice(&data[..l_len]);
xor_assign::<S::KeySize>(&mut key.0, &self.k_1.0);
Self::apply_keystream(key.into(), &mut data[l_len..]);
let hash = Self::hash(&self.k_2, &data[l_len..]);
data[..l_len]
.iter_mut()
.zip(hash)
.for_each(|(x, y)| *x ^= y);
let mut key = <Array<u8, S::KeySize> as FromZeros>::new_zeroed();
key.copy_from_slice(&data[..l_len]);
xor_assign::<S::KeySize>(&mut key.0, &self.k_3.0);
Self::apply_keystream(key.into(), &mut data[l_len..]);
let hash = Self::hash(&self.k_4, &data[l_len..]);
data[..l_len]
.iter_mut()
.zip(hash)
.for_each(|(x, y)| *x ^= y);
Ok(())
}
pub fn decrypt(&self, data: &mut [u8]) -> Result<(), Error> {
let l_len = S::KeySize::USIZE;
if data.len() < l_len {
return Err(Error::InputTooShort);
}
assert!(data.len() >= l_len);
let hash = Self::hash(&self.k_4, &data[l_len..]);
data[..l_len]
.iter_mut()
.zip(hash)
.for_each(|(x, y)| *x ^= y);
let mut key = <Array<u8, S::KeySize> as FromZeros>::new_zeroed();
key.copy_from_slice(&data[..l_len]);
xor_assign::<S::KeySize>(&mut key.0, &self.k_3.0);
Self::apply_keystream(key.into(), &mut data[l_len..]);
let hash = Self::hash(&self.k_2, &data[l_len..]);
data[..l_len]
.iter_mut()
.zip(hash)
.for_each(|(x, y)| *x ^= y);
let mut key = <Array<u8, S::KeySize> as FromZeros>::new_zeroed();
key.copy_from_slice(&data[..l_len]);
xor_assign::<S::KeySize>(&mut key.0, &self.k_1.0);
Self::apply_keystream(key.into(), &mut data[l_len..]);
Ok(())
}
fn apply_keystream(key: Key<S>, data: &mut [u8]) {
let mut cipher = S::new(&Array(key));
cipher.apply_keystream(data);
}
fn hash(key: &Array<u8, H::KeySize>, data: &[u8]) -> Array<u8, H::OutputSize> {
let mut hasher = H::new(key);
hasher.update(data);
hasher.finalize_fixed()
}
}
#[derive(Debug, Clone)]
pub struct ZeroIv<C>(C);
impl<C: KeySizeUser> KeySizeUser for ZeroIv<C> {
type KeySize = C::KeySize;
}
impl<C: KeyIvInit> KeyInit for ZeroIv<C>
where
Array<u8, C::IvSize>: FromZeros,
{
fn new(key: &Array<u8, Self::KeySize>) -> Self {
let iv = <Array<u8, C::IvSize> as FromZeros>::new_zeroed();
Self(C::new(key, &iv))
}
}
impl<C: StreamCipher> StreamCipher for ZeroIv<C> {
fn check_remaining(&self, data_len: usize) -> Result<(), StreamCipherError> {
self.0.check_remaining(data_len)
}
fn unchecked_apply_keystream_inout(&mut self, buf: InOutBuf<'_, '_, u8>) {
self.0.unchecked_apply_keystream_inout(buf)
}
fn unchecked_write_keystream(&mut self, buf: &mut [u8]) {
self.0.unchecked_write_keystream(buf)
}
}
#[derive(Debug, Clone)]
pub struct KeyedHash<L, H>(H, PhantomData<L>);
impl<L: ArraySize, H> KeySizeUser for KeyedHash<L, H> {
type KeySize = L;
}
impl<L: ArraySize, H: Default + Update> KeyInit for KeyedHash<L, H> {
fn new(key: &Array<u8, Self::KeySize>) -> Self {
let mut hasher = H::default();
hasher.update(key);
KeyedHash(hasher, PhantomData)
}
}
impl<L: ArraySize, H: OutputSizeUser> OutputSizeUser for KeyedHash<L, H> {
type OutputSize = H::OutputSize;
}
impl<L: ArraySize, H: Update> Update for KeyedHash<L, H> {
fn update(&mut self, data: &[u8]) {
self.0.update(data)
}
}
impl<L: ArraySize, H: FixedOutput> FixedOutput for KeyedHash<L, H> {
fn finalize_into(self, buffer: &mut Array<u8, Self::OutputSize>) {
self.0.finalize_into(buffer)
}
}