1use std::{fmt::Formatter, marker::PhantomData, ops::Sub};
7
8use cipher::{
9 AlgorithmName, ArrayLength, Block, BlockSizeUser, Iv, IvSizeUser, Key, KeyInit, KeyIvInit, KeySizeUser,
10 StreamCipher, generic_array::GenericArray, inout::InOut,
11};
12use digest::{Digest, OutputSizeUser};
13use typenum::{B1, Diff, IsEqual, IsGreater, Unsigned};
14
15use crate::crypto_traits::PRP;
16
17#[derive(Clone, zeroize::ZeroizeOnDrop)]
31pub struct Lioness<H: KeySizeUser + OutputSizeUser, S: KeySizeUser + IvSizeUser, B: ArrayLength<u8>>
32where
33 H::OutputSize: IsEqual<S::KeySize, Output = B1>,
37 H::KeySize: IsEqual<S::KeySize, Output = B1>,
39 B: IsGreater<<S as KeySizeUser>::KeySize, Output = B1>,
41{
42 k1: GenericArray<u8, S::KeySize>,
43 k2: GenericArray<u8, H::KeySize>,
44 k3: GenericArray<u8, S::KeySize>,
45 k4: GenericArray<u8, H::KeySize>,
46 iv1: GenericArray<u8, S::IvSize>,
47 iv2: GenericArray<u8, S::IvSize>,
48 _phantom: PhantomData<(H, S, B)>,
49}
50
51impl<H: KeySizeUser + OutputSizeUser, S: KeySizeUser + IvSizeUser, B: ArrayLength<u8>> KeySizeUser for Lioness<H, S, B>
52where
53 H::OutputSize: IsEqual<S::KeySize, Output = B1>,
54 H::KeySize: IsEqual<S::KeySize, Output = B1>,
55 B: IsGreater<<S as KeySizeUser>::KeySize, Output = B1>,
56 H::OutputSize: std::ops::Mul<cipher::consts::U4>,
58 <H::OutputSize as std::ops::Mul<cipher::consts::U4>>::Output: ArrayLength<u8>,
60{
61 type KeySize = typenum::Prod<H::OutputSize, cipher::consts::U4>;
62}
63
64impl<H: KeySizeUser + OutputSizeUser, S: KeySizeUser + IvSizeUser, B: ArrayLength<u8>> IvSizeUser for Lioness<H, S, B>
65where
66 H::OutputSize: IsEqual<S::KeySize, Output = B1>,
67 H::KeySize: IsEqual<S::KeySize, Output = B1>,
68 B: IsGreater<<S as KeySizeUser>::KeySize, Output = B1>,
69 S::IvSize: std::ops::Mul<cipher::consts::U2>,
71 <S::IvSize as std::ops::Mul<cipher::consts::U2>>::Output: ArrayLength<u8>,
73{
74 type IvSize = typenum::Prod<S::IvSize, cipher::consts::U2>;
75}
76
77impl<H: KeySizeUser + OutputSizeUser, S: KeySizeUser + IvSizeUser, B: ArrayLength<u8>> BlockSizeUser
78 for Lioness<H, S, B>
79where
80 H::OutputSize: IsEqual<S::KeySize, Output = B1>,
81 H::KeySize: IsEqual<S::KeySize, Output = B1>,
82 B: IsGreater<<S as KeySizeUser>::KeySize, Output = B1>,
83 H::OutputSize: std::ops::Mul<cipher::consts::U4>,
84 S::IvSize: std::ops::Mul<cipher::consts::U2>,
85 <H::OutputSize as std::ops::Mul<cipher::consts::U4>>::Output: ArrayLength<u8>,
86 <S::IvSize as std::ops::Mul<cipher::consts::U2>>::Output: ArrayLength<u8>,
87{
88 type BlockSize = B;
89}
90
91impl<H: KeySizeUser + OutputSizeUser, S: KeySizeUser + IvSizeUser, B: ArrayLength<u8>> KeyIvInit for Lioness<H, S, B>
92where
93 H::OutputSize: IsEqual<S::KeySize, Output = B1>,
94 H::KeySize: IsEqual<S::KeySize, Output = B1>,
95 B: IsGreater<<S as KeySizeUser>::KeySize, Output = B1>,
96 H::OutputSize: std::ops::Mul<cipher::consts::U4>,
97 S::IvSize: std::ops::Mul<cipher::consts::U2>,
98 <H::OutputSize as std::ops::Mul<cipher::consts::U4>>::Output: ArrayLength<u8>,
99 <S::IvSize as std::ops::Mul<cipher::consts::U2>>::Output: ArrayLength<u8>,
100{
101 fn new(key: &Key<Self>, iv: &Iv<Self>) -> Self {
102 let k = H::OutputSize::to_usize();
103 let i = S::IvSize::to_usize();
104 Self {
105 k1: GenericArray::clone_from_slice(&key[0..k]),
106 k2: GenericArray::clone_from_slice(&key[k..2 * k]),
107 k3: GenericArray::clone_from_slice(&key[2 * k..3 * k]),
108 k4: GenericArray::clone_from_slice(&key[3 * k..4 * k]),
109 iv1: GenericArray::clone_from_slice(&iv[0..i]),
110 iv2: GenericArray::clone_from_slice(&iv[i..2 * i]),
111 _phantom: Default::default(),
112 }
113 }
114}
115
116impl<H: KeySizeUser + OutputSizeUser + AlgorithmName, S: AlgorithmName + KeySizeUser + IvSizeUser, B: ArrayLength<u8>>
117 AlgorithmName for Lioness<H, S, B>
118where
119 H::OutputSize: IsEqual<<S as KeySizeUser>::KeySize, Output = B1>,
120 H::KeySize: IsEqual<S::KeySize, Output = B1>,
121 B: IsGreater<<S as KeySizeUser>::KeySize, Output = B1>,
122{
123 fn write_alg_name(f: &mut Formatter<'_>) -> std::fmt::Result {
124 f.write_str("Lioness<")?;
125 H::write_alg_name(f)?;
126 f.write_str(", ")?;
127 S::write_alg_name(f)?;
128 f.write_str(">")
129 }
130}
131
132impl<H: Digest + KeyInit, S: StreamCipher + KeyIvInit, B: ArrayLength<u8>> Lioness<H, S, B>
133where
134 H::OutputSize: IsEqual<<S as KeySizeUser>::KeySize, Output = B1>,
135 H::KeySize: IsEqual<S::KeySize, Output = B1>,
136 B: IsGreater<<S as KeySizeUser>::KeySize, Output = B1> + Sub<<S as KeySizeUser>::KeySize>,
138 <B as Sub<<S as KeySizeUser>::KeySize>>::Output: ArrayLength<u8>,
140 H::OutputSize: std::ops::Mul<cipher::consts::U4>,
141 <S as IvSizeUser>::IvSize: std::ops::Mul<cipher::consts::U2>,
142 <H::OutputSize as std::ops::Mul<cipher::consts::U4>>::Output: ArrayLength<u8>,
143 <<S as IvSizeUser>::IvSize as std::ops::Mul<cipher::consts::U2>>::Output: ArrayLength<u8>,
144{
145 const K: usize = <S as KeySizeUser>::KeySize::USIZE;
146
147 pub fn encrypt_block(&self, mut block: InOut<'_, '_, Block<Self>>) {
149 let mut left_prime =
151 GenericArray::<u8, <S as KeySizeUser>::KeySize>::clone_from_slice(&block.get_in()[0..Self::K]);
152 for i in 0..Self::K {
153 left_prime[i] ^= self.k1[i];
154 }
155
156 let r_cpy = GenericArray::<u8, Diff<B, <S as KeySizeUser>::KeySize>>::clone_from_slice(
158 &block.get_in()[Self::K..B::USIZE],
159 );
160 S::new(&left_prime, &self.iv1)
161 .apply_keystream_b2b(&r_cpy, &mut block.get_out()[Self::K..B::USIZE])
162 .expect("slices have always equal sizes");
163
164 let r_prime = <H as KeyInit>::new(&self.k2)
166 .chain_update(&block.get_out()[Self::K..B::USIZE])
167 .finalize();
168
169 for i in 0..Self::K {
171 block.get_out()[i] = block.get_in()[i] ^ r_prime[i];
172 }
173
174 let mut left_prime =
176 GenericArray::<u8, <S as KeySizeUser>::KeySize>::clone_from_slice(&block.get_out()[0..Self::K]);
177 for i in 0..Self::K {
178 left_prime[i] ^= self.k3[i];
179 }
180 S::new(&left_prime, &self.iv2).apply_keystream(&mut block.get_out()[Self::K..B::USIZE]);
182
183 let r_prime = <H as KeyInit>::new(&self.k4)
185 .chain_update(&block.get_out()[Self::K..B::USIZE])
186 .finalize();
187
188 for i in 0..Self::K {
190 block.get_out()[i] ^= r_prime[i];
191 }
192 }
193
194 pub fn decrypt_block(&self, mut block: InOut<'_, '_, Block<Self>>) {
196 let r_prime = <H as KeyInit>::new(&self.k4)
198 .chain_update(&block.get_in()[Self::K..B::USIZE])
199 .finalize();
200
201 for i in 0..Self::K {
203 block.get_out()[i] = block.get_in()[i] ^ r_prime[i];
204 }
205
206 let mut left_prime =
208 GenericArray::<u8, <S as KeySizeUser>::KeySize>::clone_from_slice(&block.get_out()[0..Self::K]);
209 for i in 0..Self::K {
210 left_prime[i] ^= self.k3[i];
211 }
212
213 let r_cpy = GenericArray::<u8, Diff<B, <S as KeySizeUser>::KeySize>>::clone_from_slice(
215 &block.get_in()[Self::K..B::USIZE],
216 );
217 S::new(&left_prime, &self.iv2)
218 .apply_keystream_b2b(&r_cpy, &mut block.get_out()[Self::K..B::USIZE])
219 .expect("slices have always equal sizes");
220
221 let r_prime = <H as KeyInit>::new(&self.k2)
223 .chain_update(&block.get_out()[Self::K..B::USIZE])
224 .finalize();
225
226 for i in 0..Self::K {
228 block.get_out()[i] ^= r_prime[i];
229 }
230
231 let mut left_prime =
233 GenericArray::<u8, <S as KeySizeUser>::KeySize>::clone_from_slice(&block.get_out()[0..Self::K]);
234 for i in 0..Self::K {
235 left_prime[i] ^= self.k1[i];
236 }
237
238 S::new(&left_prime, &self.iv1).apply_keystream(&mut block.get_out()[Self::K..B::USIZE]);
240 }
241}
242
243impl<H: Digest + KeyInit, S: StreamCipher + KeyIvInit, B: ArrayLength<u8>> PRP for Lioness<H, S, B>
244where
245 H::OutputSize: IsEqual<<S as KeySizeUser>::KeySize, Output = B1>,
246 H::KeySize: IsEqual<S::KeySize, Output = B1>,
247 B: IsGreater<<S as KeySizeUser>::KeySize, Output = B1> + Sub<<S as KeySizeUser>::KeySize>,
248 <B as Sub<<S as KeySizeUser>::KeySize>>::Output: ArrayLength<u8>,
249 H::OutputSize: std::ops::Mul<cipher::consts::U4>,
250 <S as IvSizeUser>::IvSize: std::ops::Mul<cipher::consts::U2>,
251 <H::OutputSize as std::ops::Mul<cipher::consts::U4>>::Output: ArrayLength<u8>,
252 <<S as IvSizeUser>::IvSize as std::ops::Mul<cipher::consts::U2>>::Output: ArrayLength<u8>,
253{
254 fn forward(&self, data: &mut Block<Self>) {
255 self.encrypt_block(data.into());
256 }
257
258 fn inverse(&self, data: &mut Block<Self>) {
259 self.decrypt_block(data.into());
260 }
261}
262
263pub type LionessBlake3ChaCha20<B> = Lioness<blake3::Hasher, chacha20::ChaCha20, B>;
266
267#[cfg(test)]
268mod tests {
269 use hex_literal::hex;
270 use typenum::{U33, U1024};
271
272 use super::*;
273
274 #[test]
275 fn lioness_sizes() {
276 assert_eq!(
277 <blake3::Hasher as OutputSizeUser>::output_size(),
278 chacha20::ChaCha20::key_size()
279 );
280
281 let key_sz = LionessBlake3ChaCha20::<U33>::key_size();
282 let iv_sz = LionessBlake3ChaCha20::<U33>::iv_size();
283 let block_sz = LionessBlake3ChaCha20::<U33>::block_size();
284
285 assert_eq!(key_sz, <blake3::Hasher as OutputSizeUser>::output_size() * 4);
286 assert_eq!(iv_sz, chacha20::ChaCha20::iv_size() * 2);
287 assert_eq!(block_sz, U33::USIZE);
288 }
289
290 #[test]
291 fn lioness_forward_inverse() {
292 let lioness = LionessBlake3ChaCha20::<U1024>::new(&Default::default(), &Default::default());
293
294 let mut data = GenericArray::<u8, U1024>::default();
295 let data_clone = data.clone();
296 assert_eq!(data, data_clone);
297
298 lioness.encrypt_block((&mut data).into());
299 assert_ne!(data, data_clone);
300
301 lioness.decrypt_block((&mut data).into());
302 assert_eq!(data, data_clone);
303 }
304
305 #[test]
306 fn lioness_forward_kat() {
307 let lioness = LionessBlake3ChaCha20::<U33>::new(&Default::default(), &Default::default());
308
309 let mut data = GenericArray::<u8, U33>::default();
310
311 lioness.encrypt_block((&mut data).into());
312 let ka = hex!("36690b60686f3c997a7bfb3808aa18a1b5808b750587ed04a01ebd836dd3ea97b4");
313 assert_eq!(data.as_slice(), &ka);
314 }
315
316 #[test]
317 fn lioness_inverse_kat() {
318 let lioness = LionessBlake3ChaCha20::<U33>::new(&Default::default(), &Default::default());
319
320 let mut data = GenericArray::<u8, U33>::default();
321
322 lioness.decrypt_block((&mut data).into());
323 let ka = hex!("7857b5bb58995ac8c59eff412dad35af72a7d1e1ff1caba132aef382b15789a6cb");
324 assert_eq!(data.as_slice(), &ka);
325 }
326
327 #[test]
328 fn lioness_forward_inverse_random() {
329 let (k, iv) = LionessBlake3ChaCha20::<U1024>::generate_key_iv(hopr_crypto_random::rng());
330 let lioness = LionessBlake3ChaCha20::<U1024>::new(&k, &iv);
331
332 let mut data = GenericArray::<u8, U1024>::default();
333 hopr_crypto_random::random_fill(&mut data);
334 let data_clone = data.clone();
335 assert_eq!(data, data_clone);
336
337 lioness.encrypt_block((&mut data).into());
338 assert_ne!(data, data_clone);
339
340 lioness.decrypt_block((&mut data).into());
341 assert_eq!(data, data_clone);
342 }
343
344 #[test]
345 fn lioness_forward_inverse_random_separate_buffers() {
346 let (k, iv) = LionessBlake3ChaCha20::<U1024>::generate_key_iv(hopr_crypto_random::rng());
347 let lioness = LionessBlake3ChaCha20::<U1024>::new(&k, &iv);
348
349 let mut data_in = GenericArray::<u8, U1024>::default();
350 let mut data_out = GenericArray::<u8, U1024>::default();
351 hopr_crypto_random::random_fill(&mut data_in);
352 let data_orig = data_in.clone();
353 assert_eq!(data_in, data_orig);
354
355 lioness.encrypt_block((&data_in, &mut data_out).into());
356 assert_ne!(data_out, data_orig);
357
358 let data_in = data_out;
359 let mut data_out = GenericArray::<u8, U1024>::default();
360 lioness.decrypt_block((&data_in, &mut data_out).into());
361 assert_eq!(data_out, data_orig);
362 }
363}