#![allow(unsafe_code)]
use core::ptr;
pub(crate) const PAR_BLOCKS: usize = 8;
pub(crate) use imp::RoundKeys;
#[repr(transparent)]
pub(crate) struct Aes256(imp::Aes256);
impl Aes256 {
pub(crate) fn init_in_place(dst: *mut Self, key: &[u8; 32]) -> Option<()> {
imp::Aes256::init_in_place(dst.cast(), key)
}
pub(crate) fn encrypt_block(&self, block: &mut [u8; 16]) {
self.0.encrypt_block(block);
}
pub(crate) fn encrypt8(&self, blocks: &mut [[u8; 16]; PAR_BLOCKS]) {
self.0.encrypt8(blocks);
}
pub(crate) fn round_keys(&self) -> &RoundKeys {
self.0.round_keys()
}
}
pub(crate) fn hardware_available() -> bool {
imp::hardware_available()
}
#[allow(clippy::cast_ptr_alignment)] unsafe fn volatile_zero<T>(value: *mut T) {
let len = core::mem::size_of::<T>();
let bytes = value.cast::<u8>();
let mut offset = 0_usize;
if bytes.addr().is_multiple_of(core::mem::align_of::<u128>()) {
while offset + core::mem::size_of::<u128>() <= len {
unsafe { ptr::write_volatile(bytes.add(offset).cast::<u128>(), 0) };
offset += core::mem::size_of::<u128>();
}
} else if bytes.addr().is_multiple_of(core::mem::align_of::<u64>()) {
while offset + core::mem::size_of::<u64>() <= len {
unsafe { ptr::write_volatile(bytes.add(offset).cast::<u64>(), 0) };
offset += core::mem::size_of::<u64>();
}
}
for offset in offset..len {
unsafe { ptr::write_volatile(bytes.add(offset), 0) };
}
}
#[cfg(any(test, miri))]
pub(crate) const AES_SBOX: [u8; 256] = [
0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16,
];
#[cfg(any(test, miri))]
pub(crate) fn software_key_schedule(key: &[u8; 32]) -> [[u8; 16]; 15] {
const RCON: [u8; 7] = [0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40];
let sub = |w: [u8; 4]| {
[
AES_SBOX[w[0] as usize],
AES_SBOX[w[1] as usize],
AES_SBOX[w[2] as usize],
AES_SBOX[w[3] as usize],
]
};
let mut w = [[0_u8; 4]; 60];
for i in 0..8 {
w[i] = [key[4 * i], key[4 * i + 1], key[4 * i + 2], key[4 * i + 3]];
}
for i in 8..60 {
let mut t = w[i - 1];
if i % 8 == 0 {
t = sub([t[1], t[2], t[3], t[0]]); t[0] ^= RCON[i / 8 - 1];
} else if i % 8 == 4 {
t = sub(t);
}
for j in 0..4 {
w[i][j] = w[i - 8][j] ^ t[j];
}
}
let mut rk = [[0_u8; 16]; 15];
for r in 0..15 {
for c in 0..4 {
rk[r][4 * c..4 * c + 4].copy_from_slice(&w[4 * r + c]);
}
}
rk
}
#[cfg(target_arch = "aarch64")]
mod imp {
use super::volatile_zero;
use core::arch::aarch64::{
uint8x16_t, vaeseq_u8, vaesmcq_u8, vdupq_n_u32, vdupq_n_u8, veorq_u8, vextq_u8,
vgetq_lane_u32, vld1q_u8, vreinterpretq_u32_u8, vreinterpretq_u8_u32, vst1q_u8,
};
use core::sync::atomic::{compiler_fence, Ordering};
const AES256_ROUND_KEY_COUNT: usize = 15;
const AES_RCON: [u32; 7] = [0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40];
pub(crate) type RoundKeys = [uint8x16_t; AES256_ROUND_KEY_COUNT];
pub(super) struct Aes256 {
round_keys: [uint8x16_t; AES256_ROUND_KEY_COUNT],
}
impl Aes256 {
pub(crate) fn round_keys(&self) -> &RoundKeys {
&self.round_keys
}
pub(super) fn init_in_place(dst: *mut Self, key: &[u8; 32]) -> Option<()> {
if !hardware_available() {
return None;
}
unsafe { Self::init_inner(dst, key) };
Some(())
}
pub(super) fn encrypt_block(&self, block: &mut [u8; 16]) {
unsafe { self.encrypt_block_inner(block) };
}
pub(super) fn encrypt8(&self, blocks: &mut [[u8; 16]; 8]) {
unsafe { self.encrypt8_inner(blocks) };
}
#[target_feature(enable = "aes", enable = "neon")]
unsafe fn init_inner(dst: *mut Self, key: &[u8; 32]) {
let round_keys = unsafe { core::ptr::addr_of_mut!((*dst).round_keys) };
aes256_key_expansion(round_keys, key);
}
#[target_feature(enable = "aes", enable = "neon")]
unsafe fn encrypt_block_inner(&self, block: &mut [u8; 16]) {
let mut state = unsafe { vld1q_u8(block.as_ptr()) };
state = vaesmcq_u8(vaeseq_u8(state, self.round_keys[0]));
state = vaesmcq_u8(vaeseq_u8(state, self.round_keys[1]));
state = vaesmcq_u8(vaeseq_u8(state, self.round_keys[2]));
state = vaesmcq_u8(vaeseq_u8(state, self.round_keys[3]));
state = vaesmcq_u8(vaeseq_u8(state, self.round_keys[4]));
state = vaesmcq_u8(vaeseq_u8(state, self.round_keys[5]));
state = vaesmcq_u8(vaeseq_u8(state, self.round_keys[6]));
state = vaesmcq_u8(vaeseq_u8(state, self.round_keys[7]));
state = vaesmcq_u8(vaeseq_u8(state, self.round_keys[8]));
state = vaesmcq_u8(vaeseq_u8(state, self.round_keys[9]));
state = vaesmcq_u8(vaeseq_u8(state, self.round_keys[10]));
state = vaesmcq_u8(vaeseq_u8(state, self.round_keys[11]));
state = vaesmcq_u8(vaeseq_u8(state, self.round_keys[12]));
state = vaeseq_u8(state, self.round_keys[13]);
state = veorq_u8(state, self.round_keys[14]);
unsafe { vst1q_u8(block.as_mut_ptr(), state) };
}
#[target_feature(enable = "aes", enable = "neon")]
unsafe fn encrypt8_inner(&self, blocks: &mut [[u8; 16]; 8]) {
let mut state = [vdupq_n_u8(0); 8];
for (lane, block) in state.iter_mut().zip(blocks.iter()) {
*lane = unsafe { vld1q_u8(block.as_ptr()) };
}
for round_key in &self.round_keys[..13] {
for lane in &mut state {
*lane = vaesmcq_u8(vaeseq_u8(*lane, *round_key));
}
}
for lane in &mut state {
*lane = veorq_u8(vaeseq_u8(*lane, self.round_keys[13]), self.round_keys[14]);
}
for (lane, block) in state.iter().zip(blocks.iter_mut()) {
unsafe { vst1q_u8(block.as_mut_ptr(), *lane) };
}
}
}
impl Drop for Aes256 {
fn drop(&mut self) {
unsafe { volatile_zero(core::ptr::addr_of_mut!(self.round_keys)) };
compiler_fence(Ordering::SeqCst);
}
}
pub(super) fn hardware_available() -> bool {
if cfg!(target_feature = "aes") && cfg!(target_feature = "neon") {
return true;
}
std::arch::is_aarch64_feature_detected!("aes")
&& std::arch::is_aarch64_feature_detected!("neon")
}
#[target_feature(enable = "aes", enable = "neon")]
unsafe fn aes256_key_expansion(
round_keys: *mut [uint8x16_t; AES256_ROUND_KEY_COUNT],
key: &[u8; 32],
) {
let mut even = unsafe { vld1q_u8(key.as_ptr()) };
let mut odd = unsafe { vld1q_u8(key[16..].as_ptr()) };
unsafe { core::ptr::addr_of_mut!((*round_keys)[0]).write(even) };
unsafe { core::ptr::addr_of_mut!((*round_keys)[1]).write(odd) };
for (index, rcon) in AES_RCON.iter().enumerate() {
even = next_even_key(even, odd, *rcon);
unsafe { core::ptr::addr_of_mut!((*round_keys)[(index * 2) + 2]).write(even) };
if index < 6 {
odd = next_odd_key(odd, even);
unsafe { core::ptr::addr_of_mut!((*round_keys)[(index * 2) + 3]).write(odd) };
}
}
}
#[target_feature(enable = "aes", enable = "neon")]
fn next_even_key(prev_even: uint8x16_t, prev_odd: uint8x16_t, rcon: u32) -> uint8x16_t {
let word = vgetq_lane_u32::<3>(vreinterpretq_u32_u8(prev_odd));
let derived = sub_word(word).rotate_right(8) ^ rcon;
veorq_u8(
prefix_cascade(prev_even),
vreinterpretq_u8_u32(vdupq_n_u32(derived)),
)
}
#[target_feature(enable = "aes", enable = "neon")]
fn next_odd_key(prev_odd: uint8x16_t, new_even: uint8x16_t) -> uint8x16_t {
let word = vgetq_lane_u32::<3>(vreinterpretq_u32_u8(new_even));
let derived = sub_word(word);
veorq_u8(
prefix_cascade(prev_odd),
vreinterpretq_u8_u32(vdupq_n_u32(derived)),
)
}
#[target_feature(enable = "aes", enable = "neon")]
fn prefix_cascade(key: uint8x16_t) -> uint8x16_t {
let zero = vdupq_n_u8(0);
let mut key = veorq_u8(key, vextq_u8(zero, key, 12));
key = veorq_u8(key, vextq_u8(zero, key, 12));
veorq_u8(key, vextq_u8(zero, key, 12))
}
#[target_feature(enable = "aes", enable = "neon")]
fn sub_word(input: u32) -> u32 {
let input = vreinterpretq_u8_u32(vdupq_n_u32(input));
vgetq_lane_u32::<0>(vreinterpretq_u32_u8(vaeseq_u8(input, vdupq_n_u8(0))))
}
}
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
mod imp {
use super::volatile_zero;
use core::sync::atomic::{compiler_fence, Ordering};
#[cfg(target_arch = "x86")]
use core::arch::x86::{
__m128i, _mm_aesenc_si128, _mm_aesenclast_si128, _mm_loadu_si128, _mm_storeu_si128,
_mm_xor_si128,
};
#[cfg(target_arch = "x86_64")]
use core::arch::x86_64::{
__m128i, _mm_aesenc_si128, _mm_aesenclast_si128, _mm_loadu_si128, _mm_storeu_si128,
_mm_xor_si128,
};
#[cfg(all(not(miri), target_arch = "x86"))]
use core::arch::x86::{_mm_aeskeygenassist_si128, _mm_shuffle_epi32, _mm_slli_si128};
#[cfg(all(not(miri), target_arch = "x86_64"))]
use core::arch::x86_64::{_mm_aeskeygenassist_si128, _mm_shuffle_epi32, _mm_slli_si128};
const AES256_ROUND_KEY_COUNT: usize = 15;
pub(crate) type RoundKeys = [__m128i; AES256_ROUND_KEY_COUNT];
pub(super) struct Aes256 {
round_keys: [__m128i; AES256_ROUND_KEY_COUNT],
}
impl Aes256 {
pub(crate) fn round_keys(&self) -> &RoundKeys {
&self.round_keys
}
pub(super) fn init_in_place(dst: *mut Self, key: &[u8; 32]) -> Option<()> {
if !hardware_available() {
return None;
}
unsafe { Self::init_inner(dst, key) };
Some(())
}
pub(super) fn encrypt_block(&self, block: &mut [u8; 16]) {
unsafe { self.encrypt_block_inner(block) };
}
pub(super) fn encrypt8(&self, blocks: &mut [[u8; 16]; 8]) {
unsafe { self.encrypt8_inner(blocks) };
}
#[target_feature(enable = "sse2", enable = "aes")]
unsafe fn init_inner(dst: *mut Self, key: &[u8; 32]) {
let round_keys = unsafe { core::ptr::addr_of_mut!((*dst).round_keys) };
aes256_key_expansion(round_keys, key);
}
#[target_feature(enable = "sse2", enable = "aes")]
unsafe fn encrypt_block_inner(&self, block: &mut [u8; 16]) {
let input = unsafe { _mm_loadu_si128(block.as_ptr().cast()) };
let mut state = _mm_xor_si128(input, self.round_keys[0]);
state = _mm_aesenc_si128(state, self.round_keys[1]);
state = _mm_aesenc_si128(state, self.round_keys[2]);
state = _mm_aesenc_si128(state, self.round_keys[3]);
state = _mm_aesenc_si128(state, self.round_keys[4]);
state = _mm_aesenc_si128(state, self.round_keys[5]);
state = _mm_aesenc_si128(state, self.round_keys[6]);
state = _mm_aesenc_si128(state, self.round_keys[7]);
state = _mm_aesenc_si128(state, self.round_keys[8]);
state = _mm_aesenc_si128(state, self.round_keys[9]);
state = _mm_aesenc_si128(state, self.round_keys[10]);
state = _mm_aesenc_si128(state, self.round_keys[11]);
state = _mm_aesenc_si128(state, self.round_keys[12]);
state = _mm_aesenc_si128(state, self.round_keys[13]);
state = _mm_aesenclast_si128(state, self.round_keys[14]);
unsafe { _mm_storeu_si128(block.as_mut_ptr().cast(), state) };
}
#[target_feature(enable = "sse2", enable = "aes")]
unsafe fn encrypt8_inner(&self, blocks: &mut [[u8; 16]; 8]) {
let mut state = [self.round_keys[0]; 8];
for (lane, block) in state.iter_mut().zip(blocks.iter()) {
let input = unsafe { _mm_loadu_si128(block.as_ptr().cast()) };
*lane = _mm_xor_si128(input, self.round_keys[0]);
}
for round_key in &self.round_keys[1..14] {
for lane in &mut state {
*lane = _mm_aesenc_si128(*lane, *round_key);
}
}
for lane in &mut state {
*lane = _mm_aesenclast_si128(*lane, self.round_keys[14]);
}
for (lane, block) in state.iter().zip(blocks.iter_mut()) {
unsafe { _mm_storeu_si128(block.as_mut_ptr().cast(), *lane) };
}
}
}
impl Drop for Aes256 {
fn drop(&mut self) {
unsafe { volatile_zero(core::ptr::addr_of_mut!(self.round_keys)) };
compiler_fence(Ordering::SeqCst);
}
}
pub(super) fn hardware_available() -> bool {
if cfg!(target_feature = "aes") && cfg!(target_feature = "sse2") {
return true;
}
if std::arch::is_x86_feature_detected!("aes") && std::arch::is_x86_feature_detected!("sse2")
{
return true;
}
cpuid_confirms_aes_sse2()
}
#[cfg(miri)]
fn cpuid_confirms_aes_sse2() -> bool {
false
}
#[cfg(not(miri))]
#[cold]
#[inline(never)]
#[allow(unused_unsafe)]
fn cpuid_confirms_aes_sse2() -> bool {
#[cfg(target_arch = "x86")]
use core::arch::x86::__cpuid;
#[cfg(target_arch = "x86_64")]
use core::arch::x86_64::__cpuid;
const ECX_AES: u32 = 1 << 25;
const EDX_SSE2: u32 = 1 << 26;
let info = unsafe { __cpuid(1) };
(info.ecx & ECX_AES) != 0 && (info.edx & EDX_SSE2) != 0
}
#[cfg(miri)]
#[target_feature(enable = "sse2", enable = "aes")]
unsafe fn aes256_key_expansion(
expanded_keys: *mut [__m128i; AES256_ROUND_KEY_COUNT],
key: &[u8; 32],
) {
let schedule = super::software_key_schedule(key);
for (i, rk) in schedule.iter().enumerate() {
unsafe {
core::ptr::addr_of_mut!((*expanded_keys)[i])
.write(_mm_loadu_si128(rk.as_ptr().cast()));
}
}
}
#[cfg(not(miri))]
#[target_feature(enable = "sse2", enable = "aes")]
unsafe fn aes256_key_expansion(
expanded_keys: *mut [__m128i; AES256_ROUND_KEY_COUNT],
key: &[u8; 32],
) {
#[target_feature(enable = "sse2", enable = "aes")]
fn generate_round_keys<const RCON: i32, const RNUM: usize>(
expanded_keys: *mut [__m128i; AES256_ROUND_KEY_COUNT],
) {
let prev_key_0 = unsafe { core::ptr::addr_of!((*expanded_keys)[RNUM * 2]).read() };
let prev_key_1 =
unsafe { core::ptr::addr_of!((*expanded_keys)[(RNUM * 2) + 1]).read() };
let mut temp = _mm_aeskeygenassist_si128::<RCON>(prev_key_1);
temp = _mm_shuffle_epi32::<0xFF>(temp);
let mut key = _mm_xor_si128(prev_key_0, _mm_slli_si128::<0x4>(prev_key_0));
key = _mm_xor_si128(key, _mm_slli_si128::<0x4>(key));
key = _mm_xor_si128(key, _mm_slli_si128::<0x4>(key));
key = _mm_xor_si128(temp, key);
unsafe { core::ptr::addr_of_mut!((*expanded_keys)[(RNUM * 2) + 2]).write(key) };
if RNUM < 6 {
let mut temp = _mm_aeskeygenassist_si128::<0x00>(key);
temp = _mm_shuffle_epi32::<0xAA>(temp);
let mut key = _mm_xor_si128(prev_key_1, _mm_slli_si128::<0x4>(prev_key_1));
key = _mm_xor_si128(key, _mm_slli_si128::<0x4>(key));
key = _mm_xor_si128(key, _mm_slli_si128::<0x4>(key));
key = _mm_xor_si128(temp, key);
unsafe { core::ptr::addr_of_mut!((*expanded_keys)[(RNUM * 2) + 3]).write(key) };
}
}
let first = unsafe { _mm_loadu_si128(key.as_ptr().cast()) };
let second = unsafe { _mm_loadu_si128(key[16..].as_ptr().cast()) };
unsafe { core::ptr::addr_of_mut!((*expanded_keys)[0]).write(first) };
unsafe { core::ptr::addr_of_mut!((*expanded_keys)[1]).write(second) };
generate_round_keys::<0x01, 0>(expanded_keys);
generate_round_keys::<0x02, 1>(expanded_keys);
generate_round_keys::<0x04, 2>(expanded_keys);
generate_round_keys::<0x08, 3>(expanded_keys);
generate_round_keys::<0x10, 4>(expanded_keys);
generate_round_keys::<0x20, 5>(expanded_keys);
generate_round_keys::<0x40, 6>(expanded_keys);
}
}
#[cfg(not(any(target_arch = "aarch64", target_arch = "x86", target_arch = "x86_64")))]
mod imp {
pub(crate) type RoundKeys = [[u8; 16]; 15];
pub(super) enum Aes256 {}
impl Aes256 {
pub(crate) fn round_keys(&self) -> &RoundKeys {
match *self {}
}
pub(super) fn init_in_place(_dst: *mut Self, _key: &[u8; 32]) -> Option<()> {
None
}
pub(super) fn encrypt_block(&self, _block: &mut [u8; 16]) {
match *self {}
}
pub(super) fn encrypt8(&self, _blocks: &mut [[u8; 16]; 8]) {
match *self {}
}
}
pub(super) const fn hardware_available() -> bool {
false
}
}
#[cfg(all(
test,
any(target_arch = "aarch64", target_arch = "x86", target_arch = "x86_64")
))]
mod tests {
#![allow(clippy::cast_possible_truncation, clippy::expect_used)]
use super::{software_key_schedule, Aes256, AES_SBOX};
use core::mem::MaybeUninit;
fn hardware_round_keys(key: &[u8; 32]) -> [[u8; 16]; 15] {
let mut slot = MaybeUninit::<Aes256>::uninit();
Aes256::init_in_place(slot.as_mut_ptr(), key).expect("hardware AES available");
let aes = unsafe { slot.assume_init() };
let mut out = [[0_u8; 16]; 15];
for (dst, rk) in out.iter_mut().zip(aes.round_keys().iter()) {
#[cfg(target_arch = "aarch64")]
unsafe {
core::arch::aarch64::vst1q_u8(dst.as_mut_ptr(), *rk);
}
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
unsafe {
#[cfg(target_arch = "x86")]
use core::arch::x86::_mm_storeu_si128;
#[cfg(target_arch = "x86_64")]
use core::arch::x86_64::_mm_storeu_si128;
_mm_storeu_si128(dst.as_mut_ptr().cast(), *rk);
}
}
out
}
#[test]
fn software_schedule_matches_hardware() {
let mut keys = vec![[0_u8; 32]];
let mut fips = [0_u8; 32];
for (i, b) in fips.iter_mut().enumerate() {
*b = i as u8;
}
keys.push(fips);
let mut state = 0x2545_f491_4f6c_dd1d_u64;
for _ in 0..16 {
let mut k = [0_u8; 32];
for b in &mut k {
state ^= state << 13;
state ^= state >> 7;
state ^= state << 17;
*b = (state >> 24) as u8;
}
keys.push(k);
}
for key in &keys {
assert_eq!(
software_key_schedule(key),
hardware_round_keys(key),
"software key schedule diverged from hardware"
);
}
}
#[test]
fn aes_sbox_is_fips197_affine_inverse() {
fn gf_mul(mut a: u8, mut b: u8) -> u8 {
let mut p = 0_u8;
for _ in 0..8 {
if b & 1 != 0 {
p ^= a;
}
let carry = a & 0x80;
a <<= 1;
if carry != 0 {
a ^= 0x1b;
}
b >>= 1;
}
p
}
fn gf_inv(x: u8) -> u8 {
if x == 0 {
return 0;
}
(1_u16..256)
.map(|y| y as u8)
.find(|&y| gf_mul(x, y) == 1)
.expect("every nonzero element of GF(2^8) is invertible")
}
fn sbox_construct(x: u8) -> u8 {
let b = gf_inv(x);
b ^ b.rotate_left(1) ^ b.rotate_left(2) ^ b.rotate_left(3) ^ b.rotate_left(4) ^ 0x63
}
for x in 0..=255_u8 {
assert_eq!(
AES_SBOX[x as usize],
sbox_construct(x),
"AES_SBOX[{x:#04x}] is not affine(inverse(x))"
);
}
let mut seen = [false; 256];
for &v in &AES_SBOX {
assert!(!seen[v as usize], "AES_SBOX is not a permutation");
seen[v as usize] = true;
}
}
}