use super::aes::HwKey;
use super::cpu::{HaveAvx512ForAesGcm, HaveVaesAvx2ForAesGcm};
use super::ghash::Ghash;
#[derive(Clone, Copy)]
enum Width {
Avx2(HaveVaesAvx2ForAesGcm),
Avx512(HaveAvx512ForAesGcm),
}
#[repr(C, align(64))]
struct Htable([[u64; 2]; 16]);
pub(crate) struct VaesKernel {
htable: Htable,
width: Width,
}
impl VaesKernel {
pub(crate) fn avx2(h: u128, token: HaveVaesAvx2ForAesGcm) -> Self {
Self::new(h, Width::Avx2(token))
}
pub(crate) fn avx512(h: u128, token: HaveAvx512ForAesGcm) -> Self {
Self::new(h, Width::Avx512(token))
}
fn new(h: u128, width: Width) -> Self {
let words = [(h >> 64) as u64, h as u64];
let mut htable = Htable([[0; 2]; 16]);
unsafe {
match width {
Width::Avx2(_) => gcm_init_vpclmulqdq_avx2(&mut htable, &words),
Width::Avx512(_) => gcm_init_vpclmulqdq_avx512(&mut htable, &words),
}
}
Self { htable, width }
}
pub(crate) fn cipher<const ENC: bool>(
&self,
key: &HwKey,
y0: &[u8; 16],
ghash: &mut Ghash<'_>,
inout: &mut [u8],
) {
let mut counter = *y0;
add_to_counter(&mut counter, 1);
let bulk = match self.width {
Width::Avx2(_) => inout.len() & !15,
Width::Avx512(_) => inout.len(),
};
let (bulk, partial) = inout.split_at_mut(bulk);
if !bulk.is_empty() {
let mut xi = ghash.xi();
let (inout, len, key, htable, xi_ptr) = (
bulk.as_mut_ptr(),
bulk.len(),
key.as_aes_key(),
&self.htable,
&mut xi,
);
unsafe {
match (self.width, ENC) {
(Width::Avx2(_), true) => aes_gcm_enc_update_vaes_avx2(
inout, inout, len, key, &counter, htable, xi_ptr,
),
(Width::Avx2(_), false) => aes_gcm_dec_update_vaes_avx2(
inout, inout, len, key, &counter, htable, xi_ptr,
),
(Width::Avx512(_), true) => aes_gcm_enc_update_vaes_avx512(
inout, inout, len, key, &counter, htable, xi_ptr,
),
(Width::Avx512(_), false) => aes_gcm_dec_update_vaes_avx512(
inout, inout, len, key, &counter, htable, xi_ptr,
),
}
}
ghash.set_xi(xi);
}
if !partial.is_empty() {
add_to_counter(&mut counter, (bulk.len() / 16) as u32);
let mut keystream = counter;
key.encrypt_block(&mut keystream);
if !ENC {
ghash.add(partial);
}
for (b, k) in partial.iter_mut().zip(keystream) {
*b ^= k;
}
if ENC {
ghash.add(partial);
}
}
}
}
fn add_to_counter(block: &mut [u8; 16], n: u32) {
let [.., a, b, c, d] = block;
let ctr = u32::from_be_bytes([*a, *b, *c, *d]).wrapping_add(n);
[*a, *b, *c, *d] = ctr.to_be_bytes();
}
#[repr(C, align(64))]
struct Table<const N: usize>([u64; N]);
static AVX2_BSWAP_MASK: Table<2> = Table(include!("boringssl/aes-gcm-avx2-x86_64.bswap_mask.rs"));
static AVX2_GFPOLY: Table<2> = Table(include!("boringssl/aes-gcm-avx2-x86_64.gfpoly.rs"));
static AVX2_GFPOLY_AND_INTERNAL_CARRYBIT: Table<2> = Table(include!(
"boringssl/aes-gcm-avx2-x86_64.gfpoly_and_internal_carrybit.rs"
));
static AVX2_CTR_PATTERN: Table<4> = Table(include!("boringssl/aes-gcm-avx2-x86_64.ctr_pattern.rs"));
static AVX2_INC_2BLOCKS: Table<4> = Table(include!("boringssl/aes-gcm-avx2-x86_64.inc_2blocks.rs"));
static AVX512_BSWAP_MASK: Table<2> =
Table(include!("boringssl/aes-gcm-avx512-x86_64.bswap_mask.rs"));
static AVX512_GFPOLY: Table<2> = Table(include!("boringssl/aes-gcm-avx512-x86_64.gfpoly.rs"));
static AVX512_GFPOLY_AND_INTERNAL_CARRYBIT: Table<2> = Table(include!(
"boringssl/aes-gcm-avx512-x86_64.gfpoly_and_internal_carrybit.rs"
));
static AVX512_CTR_PATTERN: Table<8> =
Table(include!("boringssl/aes-gcm-avx512-x86_64.ctr_pattern.rs"));
static AVX512_INC_4BLOCKS: Table<2> =
Table(include!("boringssl/aes-gcm-avx512-x86_64.inc_4blocks.rs"));
macro_rules! kernel {
(fn $name:ident($($arg:ident: $ty:ty),* $(,)?); $($op:ident = $table:ident),* $(,)?) => {
#[unsafe(naked)]
unsafe extern "sysv64" fn $name($($arg: $ty),*) {
#[cfg(not(target_vendor = "apple"))]
core::arch::naked_asm!(
include_str!(concat!("boringssl/", stringify!($name), ".linux.s")),
$($op = sym $table,)*
options(att_syntax),
);
#[cfg(target_vendor = "apple")]
core::arch::naked_asm!(
include_str!(concat!("boringssl/", stringify!($name), ".apple.s")),
$($op = sym $table,)*
options(att_syntax),
);
}
};
}
kernel! {
fn gcm_init_vpclmulqdq_avx2(htable: *mut Htable, h: *const [u64; 2]);
gfpoly = AVX2_GFPOLY,
gfpoly_and_internal_carrybit = AVX2_GFPOLY_AND_INTERNAL_CARRYBIT,
}
kernel! {
fn aes_gcm_enc_update_vaes_avx2(
input: *const u8,
output: *mut u8,
len: usize,
key: *const u8,
ivec: *const [u8; 16],
htable: *const Htable,
xi: *mut [u8; 16],
);
bswap_mask = AVX2_BSWAP_MASK,
ctr_pattern = AVX2_CTR_PATTERN,
gfpoly = AVX2_GFPOLY,
inc_2blocks = AVX2_INC_2BLOCKS,
}
kernel! {
fn aes_gcm_dec_update_vaes_avx2(
input: *const u8,
output: *mut u8,
len: usize,
key: *const u8,
ivec: *const [u8; 16],
htable: *const Htable,
xi: *mut [u8; 16],
);
bswap_mask = AVX2_BSWAP_MASK,
ctr_pattern = AVX2_CTR_PATTERN,
gfpoly = AVX2_GFPOLY,
inc_2blocks = AVX2_INC_2BLOCKS,
}
kernel! {
fn gcm_init_vpclmulqdq_avx512(htable: *mut Htable, h: *const [u64; 2]);
gfpoly = AVX512_GFPOLY,
gfpoly_and_internal_carrybit = AVX512_GFPOLY_AND_INTERNAL_CARRYBIT,
}
kernel! {
fn aes_gcm_enc_update_vaes_avx512(
input: *const u8,
output: *mut u8,
len: usize,
key: *const u8,
ivec: *const [u8; 16],
htable: *const Htable,
xi: *mut [u8; 16],
);
bswap_mask = AVX512_BSWAP_MASK,
ctr_pattern = AVX512_CTR_PATTERN,
gfpoly = AVX512_GFPOLY,
inc_4blocks = AVX512_INC_4BLOCKS,
}
kernel! {
fn aes_gcm_dec_update_vaes_avx512(
input: *const u8,
output: *mut u8,
len: usize,
key: *const u8,
ivec: *const [u8; 16],
htable: *const Htable,
xi: *mut [u8; 16],
);
bswap_mask = AVX512_BSWAP_MASK,
ctr_pattern = AVX512_CTR_PATTERN,
gfpoly = AVX512_GFPOLY,
inc_4blocks = AVX512_INC_4BLOCKS,
}