use crate::{
aligned_buffer::{AlignedBufHolder, AlignedBufRouter},
arch::Simd,
base::{aegis::Aegis, block::BlockDoubleRate},
careful::Nonce128,
easy::{AuthTag, AuthTag128, AuthTag256, Key128},
utils::num_bits,
};
use zerocopy::FromZeros;
pub struct Aegis128L<S: Simd, StateBlock, const OUTPUT_RATE_BYTES: usize> {
s0: StateBlock,
s1: StateBlock,
s2: StateBlock,
s3: StateBlock,
s4: StateBlock,
s5: StateBlock,
s6: StateBlock,
s7: StateBlock,
simd: S,
}
impl<S, StateBlock, const OUTPUT_RATE_BYTES: usize>
Aegis128L<S, StateBlock, OUTPUT_RATE_BYTES>
where
S: Simd,
StateBlock: BlockDoubleRate<OUTPUT_RATE_BYTES>,
{
#[inline(always)]
fn update(&mut self, m0: StateBlock, m1: StateBlock) {
let orig_s7 = self.s7;
self.s7 = StateBlock::aes_encrypt_round(self.s6, self.s7);
self.s6 = StateBlock::aes_encrypt_round(self.s5, self.s6);
self.s5 = StateBlock::aes_encrypt_round(self.s4, self.s5);
self.s4 = StateBlock::aes_encrypt_round(self.s3, self.s4);
self.s3 = StateBlock::aes_encrypt_round(self.s2, self.s3);
self.s2 = StateBlock::aes_encrypt_round(self.s1, self.s2);
self.s1 = StateBlock::aes_encrypt_round(self.s0, self.s1);
self.s0 = StateBlock::aes_encrypt_round(orig_s7, self.s0);
self.s0 = self.s0 ^ m0;
self.s4 = self.s4 ^ m1;
}
}
impl<S, StateBlock, const OUTPUT_RATE_BYTES: usize> Aegis<OUTPUT_RATE_BYTES>
for Aegis128L<S, StateBlock, OUTPUT_RATE_BYTES>
where
S: Simd,
StateBlock: BlockDoubleRate<OUTPUT_RATE_BYTES, Simd = S>,
AlignedBufHolder: AlignedBufRouter<OUTPUT_RATE_BYTES>,
{
type AlignedBuf =
<AlignedBufHolder as AlignedBufRouter<OUTPUT_RATE_BYTES>>::AlignedBuf;
type Simd = S;
type Key = Key128;
type Nonce = Nonce128;
#[inline(always)]
fn init(simd: Self::Simd, key_raw: &Key128, nonce_raw: Nonce128) -> Self {
let key = StateBlock::from_128_bits(simd, *key_raw.expose_secret());
let nonce = StateBlock::from_128_bits(simd, nonce_raw.into_array());
let mut state = Self {
s0: key ^ nonce,
s1: StateBlock::c1(simd),
s2: StateBlock::c0(simd),
s3: StateBlock::c1(simd),
s4: key ^ nonce,
s5: key ^ StateBlock::c0(simd),
s6: key ^ StateBlock::c1(simd),
s7: key ^ StateBlock::c0(simd),
simd,
};
for _ in 0..10 {
state.s3 = state.s3 ^ StateBlock::ctx(simd);
state.s7 = state.s7 ^ StateBlock::ctx(simd);
state.update(nonce, key);
}
state
}
#[inline(always)]
fn absorb(&mut self, chunk: &[u8; OUTPUT_RATE_BYTES]) {
let [t0, t1] = StateBlock::split(self.simd, chunk);
self.update(t0, t1);
}
#[inline(always)]
fn enc(
&mut self,
plaintext_chunk: &[u8; OUTPUT_RATE_BYTES],
ciphertext_chunk: &mut [u8; OUTPUT_RATE_BYTES],
) {
let [t0, t1] = StateBlock::split(self.simd, plaintext_chunk);
let mut z0 = t0 ^ self.s1;
let mut z1 = t1 ^ self.s2;
z0 = z0 ^ self.s6;
z1 = z1 ^ self.s5;
let tmp0 = self.s2 & self.s3;
let tmp1 = self.s6 & self.s7;
z0 = z0 ^ tmp0;
z1 = z1 ^ tmp1;
*ciphertext_chunk = StateBlock::concat(z0, z1);
self.update(t0, t1); }
#[inline(always)]
fn dec(
&mut self,
ciphertext_chunk: &[u8; OUTPUT_RATE_BYTES],
plaintext_chunk: &mut [u8; OUTPUT_RATE_BYTES],
) {
let [t0, t1] = StateBlock::split(self.simd, ciphertext_chunk);
let mut z0 = t0 ^ self.s1;
let mut z1 = t1 ^ self.s2;
z0 = z0 ^ self.s6;
z1 = z1 ^ self.s5;
let tmp0 = self.s2 & self.s3;
let tmp1 = self.s6 & self.s7;
z0 = z0 ^ tmp0;
z1 = z1 ^ tmp1;
*plaintext_chunk = StateBlock::concat(z0, z1);
self.update(z0, z1); }
#[inline(always)]
fn dec_partial(
&mut self,
ciphertext_chunk: &[u8; OUTPUT_RATE_BYTES],
plaintext_remainder: &mut [u8],
) {
let [t0, t1] = StateBlock::split(self.simd, ciphertext_chunk);
let mut z0 = t0 ^ self.s1;
let mut z1 = t1 ^ self.s2;
z0 = z0 ^ self.s6;
z1 = z1 ^ self.s5;
let tmp0 = self.s2 & self.s3;
let tmp1 = self.s6 & self.s7;
z0 = z0 ^ tmp0;
z1 = z1 ^ tmp1;
let mut out = StateBlock::concat(z0, z1);
out[plaintext_remainder.len()..].zero(); plaintext_remainder.copy_from_slice(&out[..plaintext_remainder.len()]);
let [v0, v1] = StateBlock::split(self.simd, &out);
self.update(v0, v1);
}
#[inline(always)]
fn finalize<Tag: AuthTag>(
&mut self,
plaintext: &[u8],
associated_data: &[u8],
) -> Tag {
let lengths = StateBlock::from_ints(
self.simd,
num_bits(associated_data),
num_bits(plaintext),
);
let t = self.s2 ^ lengths;
for _ in 0..7 {
self.update(t, t);
}
let mut tag = Tag::default();
match size_of::<Tag>() {
AuthTag128::BYTES => {
let block =
self.s0 ^ self.s1 ^ self.s2 ^ self.s3 ^ self.s4 ^ self.s5 ^ self.s6;
tag.as_mut().copy_from_slice(&block.xor_down());
},
AuthTag256::BYTES => {
let high = (self.s0 ^ self.s1 ^ self.s2 ^ self.s3).xor_down();
let low = (self.s4 ^ self.s5 ^ self.s6 ^ self.s7).xor_down();
tag.as_mut().copy_from_slice([high, low].as_flattened());
},
_ => unreachable!("Tag limited to 16 or 32 bytes at compile-time."),
}
tag
}
}