#![allow(clippy::indexing_slicing, clippy::arithmetic_side_effects)]
#![allow(unsafe_code)]
#[cfg(target_arch = "x86")]
use core::arch::x86::*;
#[cfg(target_arch = "x86_64")]
use core::arch::x86_64::*;
use super::soft::expand_encryption_key;
use super::KeyRef;
use crate::algorithms::put_u32;
use crate::zeroize::Zeroize;
#[derive(Clone, Copy, Debug)]
pub(crate) struct AesNi(());
impl AesNi {
pub(crate) fn detect() -> Option<Self> {
if cfg!(miri) {
return None;
}
if std::is_x86_feature_detected!("aes") && std::is_x86_feature_detected!("sse2") {
Some(AesNi(()))
} else {
None
}
}
}
pub(crate) fn available() -> bool {
AesNi::detect().is_some()
}
#[derive(Clone)]
pub(crate) struct KeySchedule {
token: AesNi,
ek: [__m128i; 15],
dk: [__m128i; 15],
nr: usize,
}
impl KeySchedule {
pub(crate) fn new(key: KeyRef<'_>) -> Option<Self> {
let token = AesNi::detect()?;
Some(unsafe { expand(token, key) })
}
pub(crate) fn encrypt(&self, block: &mut [u8; 16]) {
unsafe { encrypt(self.token, self, block) }
}
pub(crate) fn decrypt(&self, block: &mut [u8; 16]) {
unsafe { decrypt(self.token, self, block) }
}
pub(crate) fn rounds(&self) -> usize {
self.nr
}
}
impl Drop for KeySchedule {
fn drop(&mut self) {
for key in self.ek.iter_mut().chain(self.dk.iter_mut()) {
unsafe { core::ptr::write_volatile(key, core::mem::zeroed()) };
}
core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
}
}
#[target_feature(enable = "aes,sse2")]
#[allow(unused_unsafe)]
unsafe fn expand(token: AesNi, key: KeyRef<'_>) -> KeySchedule {
let (mut words, nr) = expand_encryption_key(key, |w| unsafe { sub_word(token, w) });
let ks = unsafe {
let zero = _mm_setzero_si128();
let mut ks = KeySchedule { token, ek: [zero; 15], dk: [zero; 15], nr };
for round in 0..=nr {
let mut bytes = [0u8; 16];
for j in 0..4 {
put_u32(words[round * 4 + j], &mut bytes, j * 4);
}
ks.ek[round] = _mm_loadu_si128(bytes.as_ptr() as *const __m128i);
bytes.zeroize();
}
ks.dk[0] = ks.ek[nr];
for round in 1..nr {
ks.dk[round] = _mm_aesimc_si128(ks.ek[nr - round]);
}
ks.dk[nr] = ks.ek[0];
ks
};
words.zeroize();
ks
}
#[target_feature(enable = "aes,sse2")]
#[allow(unused_unsafe)]
unsafe fn sub_word(_token: AesNi, w: u32) -> u32 {
unsafe {
let state = _mm_set1_epi32(w as i32);
_mm_cvtsi128_si32(_mm_aesenclast_si128(state, _mm_setzero_si128())) as u32
}
}
#[target_feature(enable = "aes,sse2")]
#[allow(unused_unsafe)]
unsafe fn encrypt(_token: AesNi, ks: &KeySchedule, block: &mut [u8; 16]) {
unsafe {
let mut state = _mm_loadu_si128(block.as_ptr() as *const __m128i);
state = _mm_xor_si128(state, ks.ek[0]);
for round in 1..ks.nr {
state = _mm_aesenc_si128(state, ks.ek[round]);
}
state = _mm_aesenclast_si128(state, ks.ek[ks.nr]);
_mm_storeu_si128(block.as_mut_ptr() as *mut __m128i, state);
}
}
#[target_feature(enable = "aes,sse2")]
#[allow(unused_unsafe)]
unsafe fn decrypt(_token: AesNi, ks: &KeySchedule, block: &mut [u8; 16]) {
unsafe {
let mut state = _mm_loadu_si128(block.as_ptr() as *const __m128i);
state = _mm_xor_si128(state, ks.dk[0]);
for round in 1..ks.nr {
state = _mm_aesdec_si128(state, ks.dk[round]);
}
state = _mm_aesdeclast_si128(state, ks.dk[ks.nr]);
_mm_storeu_si128(block.as_mut_ptr() as *mut __m128i, state);
}
}