use core::arch::x86_64::*;
const P1: u64 = 0xC200_0000_0000_0000;
pub(super) fn available() -> bool {
std::arch::is_x86_feature_detected!("aes")
&& std::arch::is_x86_feature_detected!("pclmulqdq")
&& std::arch::is_x86_feature_detected!("ssse3")
&& std::arch::is_x86_feature_detected!("sse4.1")
&& !std::arch::is_x86_feature_detected!("vaes")
}
pub(super) fn instructions_present() -> bool {
std::arch::is_x86_feature_detected!("aes")
&& std::arch::is_x86_feature_detected!("pclmulqdq")
&& std::arch::is_x86_feature_detected!("ssse3")
&& std::arch::is_x86_feature_detected!("sse4.1")
}
pub(super) struct Keys {
rk: [__m128i; 15],
h: [__m128i; 8],
d: [__m128i; 8],
}
impl Drop for Keys {
fn drop(&mut self) {
unsafe {
for v in self.rk.iter_mut().chain(self.h.iter_mut()).chain(self.d.iter_mut()) {
core::ptr::write_volatile(v, _mm_setzero_si128());
}
}
core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
}
}
#[inline(always)]
unsafe fn xor(a: __m128i, b: __m128i) -> __m128i {
_mm_xor_si128(a, b)
}
#[inline(always)]
unsafe fn bswap(x: __m128i) -> __m128i {
_mm_shuffle_epi8(x, _mm_set_epi8(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15))
}
#[inline(always)]
unsafe fn load(p: *const u8) -> __m128i {
_mm_loadu_si128(p as *const __m128i)
}
#[inline(always)]
unsafe fn store(p: *mut u8, v: __m128i) {
_mm_storeu_si128(p as *mut __m128i, v)
}
#[inline(always)]
unsafe fn assist1(mut t1: __m128i, t2: __m128i) -> __m128i {
let t2 = _mm_shuffle_epi32(t2, 0xff);
let mut t4 = _mm_slli_si128(t1, 4);
t1 = xor(t1, t4);
t4 = _mm_slli_si128(t4, 4);
t1 = xor(t1, t4);
t4 = _mm_slli_si128(t4, 4);
xor(xor(t1, t4), t2)
}
#[inline(always)]
unsafe fn assist2(t1: __m128i, mut t3: __m128i) -> __m128i {
let t2 = _mm_shuffle_epi32(_mm_aeskeygenassist_si128::<0>(t1), 0xaa);
let mut t4 = _mm_slli_si128(t3, 4);
t3 = xor(t3, t4);
t4 = _mm_slli_si128(t4, 4);
t3 = xor(t3, t4);
t4 = _mm_slli_si128(t4, 4);
xor(xor(t3, t4), t2)
}
#[inline(always)]
unsafe fn expand(key: &[u8; 32]) -> [__m128i; 15] {
let mut rk = [_mm_setzero_si128(); 15];
let mut t1 = load(key.as_ptr());
let mut t3 = load(key.as_ptr().add(16));
rk[0] = t1;
rk[1] = t3;
macro_rules! round {
($i:expr, $rcon:expr) => {
t1 = assist1(t1, _mm_aeskeygenassist_si128::<$rcon>(t3));
rk[$i] = t1;
t3 = assist2(t1, t3);
rk[$i + 1] = t3;
};
}
round!(2, 0x01);
round!(4, 0x02);
round!(6, 0x04);
round!(8, 0x08);
round!(10, 0x10);
round!(12, 0x20);
rk[14] = assist1(t1, _mm_aeskeygenassist_si128::<0x40>(t3));
rk
}
#[inline(always)]
unsafe fn encrypt1(rk: &[__m128i; 15], block: __m128i) -> __m128i {
let mut x = xor(block, rk[0]);
for k in &rk[1..14] {
x = _mm_aesenc_si128(x, *k);
}
_mm_aesenclast_si128(x, rk[14])
}
#[inline(always)]
unsafe fn companion(h: __m128i) -> __m128i {
let p = _mm_set_epi64x(P1 as i64, 0);
xor(_mm_shuffle_epi32(h, 0x4e), _mm_clmulepi64_si128(h, p, 0x10))
}
#[inline(always)]
unsafe fn rf(m: __m128i, h: __m128i, d: __m128i) -> (__m128i, __m128i) {
let r = xor(_mm_clmulepi64_si128(m, d, 0x10), _mm_clmulepi64_si128(m, h, 0x11));
let f = xor(_mm_clmulepi64_si128(m, d, 0x00), _mm_clmulepi64_si128(m, h, 0x01));
(r, f)
}
#[inline(always)]
unsafe fn reduce(r: __m128i, f: __m128i) -> __m128i {
let p1 = _mm_set_epi64x(0, P1 as i64);
let x = xor(r, _mm_srli_si128(f, 8));
let x = xor(x, _mm_slli_si128(f, 8));
xor(x, _mm_clmulepi64_si128(f, p1, 0x00))
}
#[inline(always)]
unsafe fn mul(m: __m128i, h: __m128i, d: __m128i) -> __m128i {
let (r, f) = rf(m, h, d);
reduce(r, f)
}
#[inline(always)]
unsafe fn ghash1(k: &Keys, acc: __m128i, x: __m128i) -> __m128i {
mul(xor(acc, x), k.h[0], k.d[0])
}
#[inline(always)]
unsafe fn ghash8(k: &Keys, acc: __m128i, x: &[__m128i; 8]) -> __m128i {
let (mut r, mut f) = rf(xor(acc, x[0]), k.h[7], k.d[7]);
for (i, xi) in x.iter().enumerate().skip(1) {
let (ri, fi) = rf(*xi, k.h[7 - i], k.d[7 - i]);
r = xor(r, ri);
f = xor(f, fi);
}
reduce(r, f)
}
fn mulx(v: u128) -> u128 {
let hi = v >> 127;
(v << 1) ^ hi ^ (hi << 127) ^ (hi << 126) ^ (hi << 121)
}
pub(super) struct Gcm {
keys: Keys,
j0: __m128i,
counter: u32,
acc: __m128i,
tag_mask: __m128i,
}
impl Drop for Gcm {
fn drop(&mut self) {
unsafe {
for v in [&mut self.acc, &mut self.tag_mask, &mut self.j0] {
core::ptr::write_volatile(v, _mm_setzero_si128());
}
}
}
}
impl Gcm {
#[target_feature(enable = "aes,pclmulqdq,sse2,ssse3,sse4.1")]
pub(super) unsafe fn new(key: &[u8; 32], nonce: &[u8; 16]) -> Self {
let rk = expand(key);
let mut raw = [0u8; 16];
store(raw.as_mut_ptr(), encrypt1(&rk, _mm_setzero_si128()));
raw.reverse();
let mut h1 = mulx(u128::from_le_bytes(raw)).to_le_bytes();
crate::crypto::wipe(&mut raw);
let mut h = [load(h1.as_ptr()); 8];
let mut d = [companion(h[0]); 8];
for (hi, lo) in [(1, 0), (2, 1), (3, 1), (4, 3), (5, 3), (6, 3), (7, 3)] {
h[hi] = mul(h[hi - lo - 1], h[lo], d[lo]);
d[hi] = companion(h[hi]);
}
crate::crypto::wipe(&mut h1);
let keys = Keys { rk, h, d };
let mut j = ghash1(&keys, _mm_setzero_si128(), bswap(load(nonce.as_ptr())));
let mut lens = [0u8; 16];
lens[8..].copy_from_slice(&128u64.to_be_bytes());
j = ghash1(&keys, j, bswap(load(lens.as_ptr())));
let j0 = bswap(j);
let counter = (_mm_extract_epi32::<3>(j0) as u32).swap_bytes();
let tag_mask = encrypt1(&keys.rk, j0);
Gcm { keys, j0, counter: counter.wrapping_add(1), acc: _mm_setzero_si128(), tag_mask }
}
#[cfg(test)]
#[target_feature(enable = "sse2,sse4.1")]
pub(super) unsafe fn seek(&mut self, block: [u8; 16]) {
self.j0 = load(block.as_ptr());
self.counter = u32::from_be_bytes(block[12..].try_into().unwrap());
}
#[inline(always)]
unsafe fn counter_block(&self, c: u32) -> __m128i {
_mm_insert_epi32::<3>(self.j0, c.swap_bytes() as i32)
}
#[inline(always)]
unsafe fn ctr8(&self, c: u32) -> [__m128i; 8] {
let rk = &self.keys.rk;
let mut b: [__m128i; 8] = core::array::from_fn(|i| xor(self.counter_block(c.wrapping_add(i as u32)), rk[0]));
for k in &rk[1..14] {
for x in b.iter_mut() {
*x = _mm_aesenc_si128(*x, *k);
}
}
for x in b.iter_mut() {
*x = _mm_aesenclast_si128(*x, rk[14]);
}
b
}
#[inline(always)]
unsafe fn stitched8(&mut self, c: u32, hashed: *const u8) -> [__m128i; 8] {
let rk = &self.keys.rk;
let mut b: [__m128i; 8] = core::array::from_fn(|i| xor(self.counter_block(c.wrapping_add(i as u32)), rk[0]));
let (mut r, mut f) = (_mm_setzero_si128(), _mm_setzero_si128());
for (round, k) in rk.iter().enumerate().take(14).skip(1) {
for x in b.iter_mut() {
*x = _mm_aesenc_si128(*x, *k);
}
if round <= 8 {
let i = round - 1;
let mut m = bswap(load(hashed.add(16 * i)));
if i == 0 {
m = xor(m, self.acc);
}
let (ri, fi) = rf(m, self.keys.h[7 - i], self.keys.d[7 - i]);
r = xor(r, ri);
f = xor(f, fi);
} else if round == 9 {
self.acc = reduce(r, f);
}
}
for x in b.iter_mut() {
*x = _mm_aesenclast_si128(*x, rk[14]);
}
b
}
#[target_feature(enable = "aes,pclmulqdq,sse2,ssse3,sse4.1")]
pub(super) unsafe fn encrypt(&mut self, data: &mut [u8]) {
let groups = data.len() / 128;
let p = data.as_mut_ptr();
for g in 0..groups {
let base = p.add(128 * g);
let ks = if g > 0 {
self.stitched8(self.counter, p.add(128 * (g - 1)))
} else {
self.ctr8(self.counter)
};
self.counter = self.counter.wrapping_add(8);
for (i, k) in ks.iter().enumerate() {
store(base.add(16 * i), xor(load(base.add(16 * i)), *k));
}
}
if groups > 0 {
let prev = p.add(128 * (groups - 1));
self.acc = ghash8(&self.keys, self.acc, &core::array::from_fn(|i| bswap(load(prev.add(16 * i)))));
}
self.tail(&mut data[128 * groups..], true);
}
#[target_feature(enable = "aes,pclmulqdq,sse2,ssse3,sse4.1")]
pub(super) unsafe fn decrypt(&mut self, data: &mut [u8]) {
let groups = data.len() / 128;
let p = data.as_mut_ptr();
for g in 0..groups {
let base = p.add(128 * g);
let ks = self.stitched8(self.counter, base);
self.counter = self.counter.wrapping_add(8);
for (i, k) in ks.iter().enumerate() {
store(base.add(16 * i), xor(load(base.add(16 * i)), *k));
}
}
self.tail(&mut data[128 * groups..], false);
}
#[inline(always)]
unsafe fn tail(&mut self, data: &mut [u8], encrypting: bool) {
for block in data.chunks_mut(16) {
let ks = encrypt1(&self.keys.rk, self.counter_block(self.counter));
self.counter = self.counter.wrapping_add(1);
let mut buf = [0u8; 16];
buf[..block.len()].copy_from_slice(block);
let input = load(buf.as_ptr());
let out = xor(input, ks);
store(buf.as_mut_ptr(), out);
buf[block.len()..].fill(0);
let ct = if encrypting { load(buf.as_ptr()) } else { input };
self.acc = ghash1(&self.keys, self.acc, bswap(ct));
block.copy_from_slice(&buf[..block.len()]);
}
}
#[target_feature(enable = "aes,pclmulqdq,sse2,ssse3,sse4.1")]
pub(super) unsafe fn finish(mut self, len: u64) -> [u8; 16] {
let mut lens = [0u8; 16];
lens[8..].copy_from_slice(&(len * 8).to_be_bytes());
self.acc = ghash1(&self.keys, self.acc, bswap(load(lens.as_ptr())));
let mut tag = [0u8; 16];
store(tag.as_mut_ptr(), xor(bswap(self.acc), self.tag_mask));
tag
}
}