use super::aes::HwKey;
use super::aes128_gcm_dec::aes128_gcm_dec;
use super::aes128_gcm_enc::aes128_gcm_enc;
use super::aes256_gcm_dec::aes256_gcm_dec;
use super::aes256_gcm_dec_alt::aes256_gcm_dec_alt;
use super::aes256_gcm_enc::aes256_gcm_enc;
use super::aes256_gcm_enc_alt::aes256_gcm_enc_alt;
use crate::low::ghash::{Ghash, GhashTable};
use crate::low::{AesKey, aes_ctr};
pub(crate) fn seal(
key: &AesKey,
table: &GhashTable,
nonce: &[u8; 12],
aad: &[u8],
cipher_inout: &mut [u8],
) -> [u8; 16] {
tagged::<true>(key, table, nonce, aad, cipher_inout)
}
pub(crate) fn open(
key: &AesKey,
table: &GhashTable,
nonce: &[u8; 12],
aad: &[u8],
cipher_inout: &mut [u8],
) -> [u8; 16] {
tagged::<false>(key, table, nonce, aad, cipher_inout)
}
fn tagged<const ENC: bool>(
key: &AesKey,
table: &GhashTable,
nonce: &[u8; 12],
aad: &[u8],
cipher_inout: &mut [u8],
) -> [u8; 16] {
let mut y0 = [0u8; 16];
y0[..12].copy_from_slice(nonce);
y0[15] = 0x01;
let mut ghash = Ghash::new(table);
cipher::<ENC>(key, &mut ghash, &y0, aad, cipher_inout);
let mut lengths = [0u8; 16];
lengths[..8].copy_from_slice(&((aad.len() * 8) as u64).to_be_bytes());
lengths[8..].copy_from_slice(&((cipher_inout.len() * 8) as u64).to_be_bytes());
ghash.add(&lengths);
let mut tag = y0;
key.encrypt_block(&mut tag);
for (t, x) in tag.iter_mut().zip(ghash.into_bytes()) {
*t ^= x;
}
tag
}
fn cipher<const ENC: bool>(
key: &AesKey,
ghash: &mut Ghash<'_>,
initial_counter: &[u8; 16],
aad: &[u8],
cipher_inout: &mut [u8],
) {
ghash.add(aad);
let mut counter = *initial_counter;
inc32(&mut counter);
let (AesKey::Hw(hw_key), GhashTable::Pmull(table)) = (key, ghash.table) else {
return composed::<ENC>(key, ghash, &counter, cipher_inout);
};
let (blocks, remainder) = cipher_inout.as_chunks_mut::<16>();
if !blocks.is_empty() {
let mut tag = ghash.state();
let htable = &table.htable;
let [htable4 @ .., _, _, _, _, _, _] = htable;
let (inout, len) = (blocks.as_mut_ptr(), blocks.len());
unsafe {
match (hw_key, ENC, table.eight_way) {
(HwKey::Aes128(k), true, _) => {
aes128_gcm_enc(inout, len, &mut tag, &mut counter, k.round_keys(), htable4)
}
(HwKey::Aes128(k), false, _) => {
aes128_gcm_dec(inout, len, &mut tag, &mut counter, k.round_keys(), htable4)
}
(HwKey::Aes256(k), true, None) => {
aes256_gcm_enc(inout, len, &mut tag, &mut counter, k.round_keys(), htable4)
}
(HwKey::Aes256(k), false, None) => {
aes256_gcm_dec(inout, len, &mut tag, &mut counter, k.round_keys(), htable4)
}
(HwKey::Aes256(k), true, Some(_)) => {
aes256_gcm_enc_alt(inout, len, &mut tag, &mut counter, k.round_keys(), htable)
}
(HwKey::Aes256(k), false, Some(_)) => {
aes256_gcm_dec_alt(inout, len, &mut tag, &mut counter, k.round_keys(), htable)
}
}
}
ghash.set_state(tag);
}
if !remainder.is_empty() {
if !ENC {
ghash.add(remainder);
}
let mut keystream = counter;
key.encrypt_block(&mut keystream);
for (c, k) in remainder.iter_mut().zip(keystream) {
*c ^= k;
}
if ENC {
ghash.add(remainder);
}
}
}
fn composed<const ENC: bool>(
key: &AesKey,
ghash: &mut Ghash<'_>,
counter: &[u8; 16],
cipher_inout: &mut [u8],
) {
let (nonce, ctr) = counter.split_at(12);
let nonce = nonce.try_into().unwrap();
let mut ctr = u32::from_be_bytes(ctr.try_into().unwrap());
let (chunks, remainder) = cipher_inout.as_chunks_mut::<128>();
for chunk in chunks {
if !ENC {
ghash.add(chunk);
}
aes_ctr::apply(key, nonce, ctr, chunk);
if ENC {
ghash.add(chunk);
}
ctr = ctr.wrapping_add(8);
}
if !ENC {
ghash.add(remainder);
}
aes_ctr::apply(key, nonce, ctr, remainder);
if ENC {
ghash.add(remainder);
}
}
fn inc32(block: &mut [u8; 16]) {
let b = u128::from_be_bytes(*block);
let ctr = (b as u32).wrapping_add(1);
*block = (b & !0xffff_ffff | ctr as u128).to_be_bytes();
}
#[cfg(test)]
#[cfg_attr(coverage_nightly, coverage(off))]
mod tests {
use super::*;
use crate::low::aarch64::cpu::HaveCryptoExt;
use crate::low::aarch64::ghash::PmullTable;
use crate::low::aarch64::ghash_neon_armv8::NeonTable;
use crate::low::aarch64::vpaes_armv8::VpaesKey;
#[test]
fn kernels_match_composed() {
let Some(token) = HaveCryptoExt::check() else {
return;
};
let bytes = |n: usize, seed: u32| -> Vec<u8> {
(0..n as u32)
.map(|i| (i.wrapping_add(seed).wrapping_mul(0x9e3779b9) >> 24) as u8)
.collect()
};
for key_len in [16, 32] {
let key = bytes(key_len, 1);
let hw = AesKey::new(&key);
let vpaes = AesKey::Vpaes(VpaesKey::new(&key));
let mut h = [0u8; 16];
vpaes.encrypt_block(&mut h);
let pmull = GhashTable::Pmull(PmullTable::new(u128::from_be_bytes(h), token));
let neon = GhashTable::Neon(NeonTable::new(&h));
for aad_len in [0, 1, 16, 17] {
for msg_len in [0, 1, 15, 16, 17, 112, 127, 128, 129, 255, 256, 1000] {
let nonce = bytes(12, msg_len as u32).try_into().unwrap();
let aad = bytes(aad_len, 2);
let plain = bytes(msg_len, 3);
let mut want = plain.clone();
let want_tag = seal(&hw, &pmull, &nonce, &aad, &mut want);
let mut got = plain.clone();
let tag = seal(&vpaes, &neon, &nonce, &aad, &mut got);
let what = format!("{key_len} {aad_len} {msg_len}");
assert_eq!(got, want, "{what}");
assert_eq!(tag, want_tag, "{what}");
let tag = open(&vpaes, &neon, &nonce, &aad, &mut got);
assert_eq!(got, plain, "{what}");
assert_eq!(tag, want_tag, "{what}");
}
}
}
}
}