use core::arch::aarch64::*;
use super::cpu::HaveCryptoExt;
use super::vpaes_armv8::VpaesKey;
use crate::low;
pub(crate) enum AesKey {
Hw(HwKey),
Vpaes(VpaesKey),
}
impl AesKey {
pub(crate) fn new(key: &[u8]) -> Self {
match (key.len(), HaveCryptoExt::check()) {
(16, Some(token)) => Self::Hw(HwKey::Aes128(AesKey128::new(
key.try_into().unwrap(),
token,
))),
(32, Some(token)) => Self::Hw(HwKey::Aes256(AesKey256::new(
key.try_into().unwrap(),
token,
))),
(16 | 32, None) => Self::Vpaes(VpaesKey::new(key)),
(24, _) => panic!("aes-192 not supported"),
_ => panic!("invalid aes key size"),
}
}
pub(crate) fn encrypt_block(&self, inout: &mut [u8; 16]) {
match self {
Self::Hw(HwKey::Aes128(a128)) => a128.encrypt_block(inout),
Self::Hw(HwKey::Aes256(a256)) => a256.encrypt_block(inout),
Self::Vpaes(key) => key.encrypt_block(inout),
}
}
}
pub(crate) enum HwKey {
Aes128(AesKey128),
Aes256(AesKey256),
}
impl Drop for AesKey {
fn drop(&mut self) {
low::zeroise_value(self);
}
}
impl low::generic::zeroise::Zeroable for AesKey {}
pub(crate) struct AesKey128 {
round_keys: [uint8x16_t; 10 + 1],
}
impl AesKey128 {
pub(crate) fn new(key: &[u8; 16], _: HaveCryptoExt) -> Self {
let mut round_keys = [zero(); 10 + 1];
unsafe { aes128_expand(key, &mut round_keys) };
Self { round_keys }
}
pub(crate) fn encrypt_block(&self, inout: &mut [u8; 16]) {
unsafe { aes128_block(&self.round_keys, inout) }
}
pub(crate) fn round_keys(&self) -> &[uint8x16_t; 11] {
&self.round_keys
}
}
pub(crate) struct AesKey256 {
round_keys: [uint8x16_t; 14 + 1],
}
impl AesKey256 {
pub(crate) fn new(key: &[u8; 32], _: HaveCryptoExt) -> Self {
let mut round_keys = [zero(); 14 + 1];
unsafe { aes256_expand(key, &mut round_keys) };
Self { round_keys }
}
pub(crate) fn encrypt_block(&self, inout: &mut [u8; 16]) {
unsafe { aes256_block(&self.round_keys, inout) }
}
pub(crate) fn round_keys(&self) -> &[uint8x16_t; 15] {
&self.round_keys
}
}
fn zero() -> uint8x16_t {
unsafe { vdupq_n_u8(0) }
}
const RCON: [u32; 10] = [0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36];
#[target_feature(enable = "aes")]
fn aes128_expand(key: &[u8; 16], out: &mut [uint8x16_t; 11]) {
let mut t = unsafe { vld1q_u8(key.as_ptr()) };
out[0] = t;
for (rk, rcon) in out[1..].iter_mut().zip(RCON) {
t = veorq_u8(xor_prefix(t), sub_rot_word(t, rcon));
*rk = t;
}
}
#[target_feature(enable = "aes")]
fn aes256_expand(key: &[u8; 32], out: &mut [uint8x16_t; 15]) {
let (mut a, mut b) = unsafe { (vld1q_u8(key.as_ptr()), vld1q_u8(key[16..].as_ptr())) };
out[0] = a;
out[1] = b;
for (i, rcon) in RCON[..7].iter().enumerate() {
a = veorq_u8(xor_prefix(a), sub_rot_word(b, *rcon));
out[2 * i + 2] = a;
if i == 6 {
break;
}
let w3 = vreinterpretq_u8_u32(vdupq_laneq_u32::<3>(vreinterpretq_u32_u8(a)));
b = veorq_u8(xor_prefix(b), vaeseq_u8(w3, zero()));
out[2 * i + 3] = b;
}
}
#[inline]
#[target_feature(enable = "aes")]
fn sub_rot_word(t: uint8x16_t, rcon: u32) -> uint8x16_t {
const ROT_WORD_3: [u8; 16] = [
13, 14, 15, 12, 13, 14, 15, 12, 13, 14, 15, 12, 13, 14, 15, 12,
];
let rot = vqtbl1q_u8(t, unsafe { vld1q_u8(ROT_WORD_3.as_ptr()) });
veorq_u8(
vaeseq_u8(rot, zero()),
vreinterpretq_u8_u32(vdupq_n_u32(rcon)),
)
}
#[inline]
#[target_feature(enable = "neon")]
fn xor_prefix(t: uint8x16_t) -> uint8x16_t {
let t = veorq_u8(t, vextq_u8::<12>(zero(), t));
veorq_u8(t, vextq_u8::<8>(zero(), t))
}
#[target_feature(enable = "aes")]
fn aes128_block(round_keys: &[uint8x16_t; 11], block_inout: &mut [u8]) {
let block = unsafe { vld1q_u8(block_inout.as_ptr().cast()) };
let block = _aes128_block(round_keys, block);
unsafe { vst1q_u8(block_inout.as_mut_ptr().cast(), block) };
}
#[target_feature(enable = "aes")]
#[inline]
pub(crate) fn _aes128_block(round_keys: &[uint8x16_t; 11], block: uint8x16_t) -> uint8x16_t {
let block = vaeseq_u8(block, round_keys[0]);
let block = vaesmcq_u8(block);
let block = vaeseq_u8(block, round_keys[1]);
let block = vaesmcq_u8(block);
let block = vaeseq_u8(block, round_keys[2]);
let block = vaesmcq_u8(block);
let block = vaeseq_u8(block, round_keys[3]);
let block = vaesmcq_u8(block);
let block = vaeseq_u8(block, round_keys[4]);
let block = vaesmcq_u8(block);
let block = vaeseq_u8(block, round_keys[5]);
let block = vaesmcq_u8(block);
let block = vaeseq_u8(block, round_keys[6]);
let block = vaesmcq_u8(block);
let block = vaeseq_u8(block, round_keys[7]);
let block = vaesmcq_u8(block);
let block = vaeseq_u8(block, round_keys[8]);
let block = vaesmcq_u8(block);
let block = vaeseq_u8(block, round_keys[9]);
veorq_u8(block, round_keys[10])
}
macro_rules! round_8 {
($b0:ident, $b1:ident, $b2:ident, $b3:ident, $b4:ident, $b5:ident, $b6:ident, $b7:ident, $rk:expr) => {
let rk = $rk;
$b0 = vaeseq_u8($b0, rk);
$b0 = vaesmcq_u8($b0);
$b1 = vaeseq_u8($b1, rk);
$b1 = vaesmcq_u8($b1);
$b2 = vaeseq_u8($b2, rk);
$b2 = vaesmcq_u8($b2);
$b3 = vaeseq_u8($b3, rk);
$b3 = vaesmcq_u8($b3);
$b4 = vaeseq_u8($b4, rk);
$b4 = vaesmcq_u8($b4);
$b5 = vaeseq_u8($b5, rk);
$b5 = vaesmcq_u8($b5);
$b6 = vaeseq_u8($b6, rk);
$b6 = vaesmcq_u8($b6);
$b7 = vaeseq_u8($b7, rk);
$b7 = vaesmcq_u8($b7);
};
}
#[target_feature(enable = "aes")]
#[inline]
pub(crate) fn _aes128_8_blocks(
round_keys: &[uint8x16_t; 11],
mut b0: uint8x16_t,
mut b1: uint8x16_t,
mut b2: uint8x16_t,
mut b3: uint8x16_t,
mut b4: uint8x16_t,
mut b5: uint8x16_t,
mut b6: uint8x16_t,
mut b7: uint8x16_t,
) -> (
uint8x16_t,
uint8x16_t,
uint8x16_t,
uint8x16_t,
uint8x16_t,
uint8x16_t,
uint8x16_t,
uint8x16_t,
) {
round_8!(b0, b1, b2, b3, b4, b5, b6, b7, round_keys[0]);
round_8!(b0, b1, b2, b3, b4, b5, b6, b7, round_keys[1]);
round_8!(b0, b1, b2, b3, b4, b5, b6, b7, round_keys[2]);
round_8!(b0, b1, b2, b3, b4, b5, b6, b7, round_keys[3]);
round_8!(b0, b1, b2, b3, b4, b5, b6, b7, round_keys[4]);
round_8!(b0, b1, b2, b3, b4, b5, b6, b7, round_keys[5]);
round_8!(b0, b1, b2, b3, b4, b5, b6, b7, round_keys[6]);
round_8!(b0, b1, b2, b3, b4, b5, b6, b7, round_keys[7]);
round_8!(b0, b1, b2, b3, b4, b5, b6, b7, round_keys[8]);
let b0 = vaeseq_u8(b0, round_keys[9]);
let b1 = vaeseq_u8(b1, round_keys[9]);
let b2 = vaeseq_u8(b2, round_keys[9]);
let b3 = vaeseq_u8(b3, round_keys[9]);
let b4 = vaeseq_u8(b4, round_keys[9]);
let b5 = vaeseq_u8(b5, round_keys[9]);
let b6 = vaeseq_u8(b6, round_keys[9]);
let b7 = vaeseq_u8(b7, round_keys[9]);
(
veorq_u8(b0, round_keys[10]),
veorq_u8(b1, round_keys[10]),
veorq_u8(b2, round_keys[10]),
veorq_u8(b3, round_keys[10]),
veorq_u8(b4, round_keys[10]),
veorq_u8(b5, round_keys[10]),
veorq_u8(b6, round_keys[10]),
veorq_u8(b7, round_keys[10]),
)
}
#[target_feature(enable = "aes")]
fn aes256_block(round_keys: &[uint8x16_t; 15], block_inout: &mut [u8; 16]) {
let block = unsafe { vld1q_u8(block_inout.as_ptr().cast()) };
let block = _aes256_block(round_keys, block);
unsafe { vst1q_u8(block_inout.as_mut_ptr().cast(), block) };
}
#[target_feature(enable = "aes")]
#[inline]
pub(crate) fn _aes256_block(round_keys: &[uint8x16_t; 15], block: uint8x16_t) -> uint8x16_t {
let block = vaeseq_u8(block, round_keys[0]);
let block = vaesmcq_u8(block);
let block = vaeseq_u8(block, round_keys[1]);
let block = vaesmcq_u8(block);
let block = vaeseq_u8(block, round_keys[2]);
let block = vaesmcq_u8(block);
let block = vaeseq_u8(block, round_keys[3]);
let block = vaesmcq_u8(block);
let block = vaeseq_u8(block, round_keys[4]);
let block = vaesmcq_u8(block);
let block = vaeseq_u8(block, round_keys[5]);
let block = vaesmcq_u8(block);
let block = vaeseq_u8(block, round_keys[6]);
let block = vaesmcq_u8(block);
let block = vaeseq_u8(block, round_keys[7]);
let block = vaesmcq_u8(block);
let block = vaeseq_u8(block, round_keys[8]);
let block = vaesmcq_u8(block);
let block = vaeseq_u8(block, round_keys[9]);
let block = vaesmcq_u8(block);
let block = vaeseq_u8(block, round_keys[10]);
let block = vaesmcq_u8(block);
let block = vaeseq_u8(block, round_keys[11]);
let block = vaesmcq_u8(block);
let block = vaeseq_u8(block, round_keys[12]);
let block = vaesmcq_u8(block);
let block = vaeseq_u8(block, round_keys[13]);
veorq_u8(block, round_keys[14])
}
#[target_feature(enable = "aes")]
#[inline]
pub(crate) fn _aes256_8_blocks(
round_keys: &[uint8x16_t; 15],
mut b0: uint8x16_t,
mut b1: uint8x16_t,
mut b2: uint8x16_t,
mut b3: uint8x16_t,
mut b4: uint8x16_t,
mut b5: uint8x16_t,
mut b6: uint8x16_t,
mut b7: uint8x16_t,
) -> (
uint8x16_t,
uint8x16_t,
uint8x16_t,
uint8x16_t,
uint8x16_t,
uint8x16_t,
uint8x16_t,
uint8x16_t,
) {
round_8!(b0, b1, b2, b3, b4, b5, b6, b7, round_keys[0]);
round_8!(b0, b1, b2, b3, b4, b5, b6, b7, round_keys[1]);
round_8!(b0, b1, b2, b3, b4, b5, b6, b7, round_keys[2]);
round_8!(b0, b1, b2, b3, b4, b5, b6, b7, round_keys[3]);
round_8!(b0, b1, b2, b3, b4, b5, b6, b7, round_keys[4]);
round_8!(b0, b1, b2, b3, b4, b5, b6, b7, round_keys[5]);
round_8!(b0, b1, b2, b3, b4, b5, b6, b7, round_keys[6]);
round_8!(b0, b1, b2, b3, b4, b5, b6, b7, round_keys[7]);
round_8!(b0, b1, b2, b3, b4, b5, b6, b7, round_keys[8]);
round_8!(b0, b1, b2, b3, b4, b5, b6, b7, round_keys[9]);
round_8!(b0, b1, b2, b3, b4, b5, b6, b7, round_keys[10]);
round_8!(b0, b1, b2, b3, b4, b5, b6, b7, round_keys[11]);
round_8!(b0, b1, b2, b3, b4, b5, b6, b7, round_keys[12]);
let b0 = vaeseq_u8(b0, round_keys[13]);
let b1 = vaeseq_u8(b1, round_keys[13]);
let b2 = vaeseq_u8(b2, round_keys[13]);
let b3 = vaeseq_u8(b3, round_keys[13]);
let b4 = vaeseq_u8(b4, round_keys[13]);
let b5 = vaeseq_u8(b5, round_keys[13]);
let b6 = vaeseq_u8(b6, round_keys[13]);
let b7 = vaeseq_u8(b7, round_keys[13]);
(
veorq_u8(b0, round_keys[14]),
veorq_u8(b1, round_keys[14]),
veorq_u8(b2, round_keys[14]),
veorq_u8(b3, round_keys[14]),
veorq_u8(b4, round_keys[14]),
veorq_u8(b5, round_keys[14]),
veorq_u8(b6, round_keys[14]),
veorq_u8(b7, round_keys[14]),
)
}
#[cfg(test)]
#[cfg_attr(coverage_nightly, coverage(off))]
mod tests {
#[cfg(panic = "unwind")]
use std::panic;
use super::*;
use crate::low::aes_ctr;
fn to_u128(v: uint8x16_t) -> u128 {
let mut buf = [0u8; 16];
unsafe {
vst1q_u8(buf.as_mut_ptr(), v);
}
u128::from_be_bytes(buf)
}
#[test]
#[cfg(panic = "unwind")]
fn test_construct() {
let input: [u8; 33] = [
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b,
0x1c, 0x1d, 0x1e, 0x1f, 0xff,
];
for len in 0..=input.len() {
let num_bits = len * 8;
match panic::catch_unwind(|| AesKey::new(&input[..len])) {
Ok(key) => match key {
AesKey::Hw(HwKey::Aes128(_)) => assert_eq!(num_bits, 128),
AesKey::Hw(HwKey::Aes256(_)) => assert_eq!(num_bits, 256),
AesKey::Vpaes(_) => assert!(matches!(num_bits, 128 | 256)),
},
Err(_) => {
assert_ne!(num_bits, 128);
assert_ne!(num_bits, 256);
}
}
}
}
#[test]
fn test_key_expansion_128() {
let Some(token) = HaveCryptoExt::check() else {
return;
};
let context = AesKey128::new(
&[
0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf,
0x4f, 0x3c,
],
token,
);
let expected = [
0x2b7e1516_28aed2a6_abf71588_09cf4f3c,
0xa0fafe17_88542cb1_23a33939_2a6c7605,
0xf2c295f2_7a96b943_5935807a_7359f67f,
0x3d80477d_4716fe3e_1e237e44_6d7a883b,
0xef44a541_a8525b7f_b671253b_db0bad00,
0xd4d1c6f8_7c839d87_caf2b8bc_11f915bc,
0x6d88a37a_110b3efd_dbf98641_ca0093fd,
0x4e54f70e_5f5fc9f3_84a64fb2_4ea6dc4f,
0xead27321_b58dbad2_312bf560_7f8d292f,
0xac7766f3_19fadc21_28d12941_575c006e,
0xd014f9a8_c9ee2589_e13f0cc8_b6630ca6,
];
for (i, expect) in expected.into_iter().enumerate() {
assert_eq!(to_u128(context.round_keys[i]), expect);
}
}
#[test]
fn test_key_expansion_256() {
let Some(token) = HaveCryptoExt::check() else {
return;
};
let context = AesKey256::new(
&[
0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe, 0x2b, 0x73, 0xae, 0xf0, 0x85, 0x7d,
0x77, 0x81, 0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7, 0x2d, 0x98, 0x10, 0xa3,
0x09, 0x14, 0xdf, 0xf4,
],
token,
);
let expected = [
0x603deb10_15ca71be_2b73aef0_857d7781,
0x1f352c07_3b6108d7_2d9810a3_0914dff4,
0x9ba35411_8e6925af_a51a8b5f_2067fcde,
0xa8b09c1a_93d194cd_be49846e_b75d5b9a,
0xd59aecb8_5bf3c917_fee94248_de8ebe96,
0xb5a9328a_2678a647_98312229_2f6c79b3,
0x812c81ad_dadf48ba_24360af2_fab8b464,
0x98c5bfc9_bebd198e_268c3ba7_09e04214,
0x68007bac_b2df3316_96e939e4_6c518d80,
0xc814e204_76a9fb8a_5025c02d_59c58239,
0xde136967_6ccc5a71_fa256395_9674ee15,
0x5886ca5d_2e2f31d7_7e0af1fa_27cf73c3,
0x749c47ab_18501dda_e2757e4f_7401905a,
0xcafaaae3_e4d59b34_9adf6ace_bd10190d,
0xfe4890d1_e6188d0b_046df344_706c631e,
];
for (i, expect) in expected.into_iter().enumerate() {
assert_eq!(to_u128(context.round_keys[i]), expect);
}
}
#[test]
fn test_block_128() {
let context = AesKey::new(&[
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
0x0e, 0x0f,
]);
let mut block = [
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd,
0xee, 0xff,
];
context.encrypt_block(&mut block);
assert_eq!(
block,
[
0x69, 0xc4, 0xe0, 0xd8, 0x6a, 0x7b, 0x04, 0x30, 0xd8, 0xcd, 0xb7, 0x80, 0x70, 0xb4,
0xc5, 0x5a
]
);
}
#[test]
fn test_block_256() {
let context = AesKey::new(&[
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b,
0x1c, 0x1d, 0x1e, 0x1f,
]);
let mut block = [
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd,
0xee, 0xff,
];
context.encrypt_block(&mut block);
assert_eq!(
block,
[
0x8e, 0xa2, 0xb7, 0xca, 0x51, 0x67, 0x45, 0xbf, 0xea, 0xfc, 0x49, 0x90, 0x4b, 0x49,
0x60, 0x89
]
);
}
#[test]
fn hw_matches_vpaes() {
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, seed) in [16, 32].into_iter().zip(1..) {
let key = bytes(key_len, seed);
let hw = AesKey::Hw(match key_len {
16 => HwKey::Aes128(AesKey128::new(key[..].try_into().unwrap(), token)),
_ => HwKey::Aes256(AesKey256::new(key[..].try_into().unwrap(), token)),
});
let vpaes = AesKey::Vpaes(VpaesKey::new(&key));
let mut want: [u8; 16] = bytes(16, seed).try_into().unwrap();
let mut got = want;
hw.encrypt_block(&mut want);
vpaes.encrypt_block(&mut got);
assert_eq!(got, want, "{key_len}-byte key block");
let nonce = bytes(12, seed).try_into().unwrap();
for counter in [0, 1, u32::MAX - 3, u32::MAX] {
for len in [1, 15, 16, 17, 127, 128, 129, 300] {
let mut want = bytes(len, counter);
let mut got = want.clone();
aes_ctr::apply(&hw, &nonce, counter, &mut want);
aes_ctr::apply(&vpaes, &nonce, counter, &mut got);
assert_eq!(
got, want,
"{key_len}-byte key, counter {counter}, {len} bytes"
);
}
}
}
}
}