use core::{arch::asm, simd::i64x2};
#[derive(Clone)]
#[repr(C, align(16))]
pub(in crate::aead) struct PpcRoundKeys {
rk: [i64x2; 15],
}
impl PpcRoundKeys {
pub(super) fn zeroize(&mut self) {
let bytes = unsafe { core::slice::from_raw_parts_mut(self.rk.as_mut_ptr().cast::<u8>(), 15usize.strict_mul(16)) };
crate::traits::ct::zeroize(bytes);
}
}
#[inline]
fn load_block_be(ptr: *const u8) -> i64x2 {
let bytes: [u8; 16] = unsafe { core::ptr::read_unaligned(ptr.cast()) };
let dw0 = i64::from_be_bytes([
bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
]);
let dw1 = i64::from_be_bytes([
bytes[8], bytes[9], bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15],
]);
#[cfg(target_endian = "little")]
{
i64x2::from_array([dw1, dw0])
}
#[cfg(target_endian = "big")]
{
i64x2::from_array([dw0, dw1])
}
}
#[inline]
fn store_block_be(ptr: *mut u8, block: i64x2) {
let elems = block.to_array();
#[cfg(target_endian = "little")]
let (hi, lo) = (elems[1].to_be_bytes(), elems[0].to_be_bytes());
#[cfg(target_endian = "big")]
let (hi, lo) = (elems[0].to_be_bytes(), elems[1].to_be_bytes());
let mut bytes = [0u8; 16];
bytes[0..8].copy_from_slice(&hi);
bytes[8..16].copy_from_slice(&lo);
unsafe { core::ptr::write_unaligned(ptr.cast(), bytes) };
}
pub(super) fn from_portable(rk: &[u32; 60]) -> PpcRoundKeys {
let mut keys = [i64x2::from_array([0, 0]); 15];
let mut i = 0usize;
while i < 15 {
let base = i.strict_mul(4);
let mut bytes = [0u8; 16];
bytes[0..4].copy_from_slice(&rk[base].to_be_bytes());
bytes[4..8].copy_from_slice(&rk[base.strict_add(1)].to_be_bytes());
bytes[8..12].copy_from_slice(&rk[base.strict_add(2)].to_be_bytes());
bytes[12..16].copy_from_slice(&rk[base.strict_add(3)].to_be_bytes());
keys[i] = load_block_be(bytes.as_ptr());
i = i.strict_add(1);
}
PpcRoundKeys { rk: keys }
}
#[target_feature(enable = "altivec,vsx,power8-vector,power8-crypto")]
#[inline]
unsafe fn sub_word_hw(w: u32) -> u32 {
let word = w.to_be_bytes();
let bytes = [
word[0], word[1], word[2], word[3], word[0], word[1], word[2], word[3], word[0], word[1], word[2], word[3],
word[0], word[1], word[2], word[3],
];
let state = load_block_be(bytes.as_ptr());
unsafe {
let out: i64x2;
asm!(
"vsbox {out}, {state}",
out = lateout(vreg) out,
state = in(vreg) state,
options(nomem, nostack, pure),
);
let mut out_bytes = [0u8; 16];
store_block_be(out_bytes.as_mut_ptr(), out);
u32::from_be_bytes([out_bytes[0], out_bytes[1], out_bytes[2], out_bytes[3]])
}
}
#[target_feature(enable = "altivec,vsx,power8-vector,power8-crypto")]
#[inline]
pub(super) unsafe fn expand_key_hw(key: &[u8; 32]) -> PpcRoundKeys {
unsafe {
let mut rk = [0u32; super::EXPANDED_KEY_WORDS];
let mut i = 0usize;
while i < 8 {
let base = i.strict_mul(4);
rk[i] = u32::from_be_bytes([
key[base],
key[base.strict_add(1)],
key[base.strict_add(2)],
key[base.strict_add(3)],
]);
i = i.strict_add(1);
}
i = 8;
while i < super::EXPANDED_KEY_WORDS {
let mut temp = rk[i.strict_sub(1)];
if i.strict_rem(8) == 0 {
temp = sub_word_hw(super::rot_word(temp)) ^ super::RCON[i.strict_div(8).strict_sub(1)];
} else if i.strict_rem(8) == 4 {
temp = sub_word_hw(temp);
}
rk[i] = rk[i.strict_sub(8)] ^ temp;
i = i.strict_add(1);
}
let keys = from_portable(&rk);
crate::traits::ct::zeroize(core::slice::from_raw_parts_mut(
rk.as_mut_ptr().cast::<u8>(),
super::EXPANDED_KEY_WORDS.strict_mul(4),
));
keys
}
}
#[target_feature(enable = "altivec,vsx,power8-vector,power8-crypto")]
pub(super) unsafe fn expand_key(key: &[u8; 32]) -> PpcRoundKeys {
unsafe { expand_key_hw(key) }
}
#[target_feature(enable = "altivec,vsx,power8-vector,power8-crypto")]
#[inline]
pub(super) unsafe fn encrypt_block_core(keys: &PpcRoundKeys, block: &mut [u8; 16]) {
unsafe {
let k = &keys.rk;
let mut state = load_block_be(block.as_ptr());
macro_rules! vcipher_round {
($rk:expr) => {
asm!(
"vcipher {s}, {s}, {rk}",
s = inlateout(vreg) state,
rk = in(vreg) $rk,
options(nomem, nostack),
);
};
}
asm!(
"vxor {s}, {s}, {rk}",
s = inlateout(vreg) state,
rk = in(vreg) k[0],
options(nomem, nostack),
);
vcipher_round!(k[1]);
vcipher_round!(k[2]);
vcipher_round!(k[3]);
vcipher_round!(k[4]);
vcipher_round!(k[5]);
vcipher_round!(k[6]);
vcipher_round!(k[7]);
vcipher_round!(k[8]);
vcipher_round!(k[9]);
vcipher_round!(k[10]);
vcipher_round!(k[11]);
vcipher_round!(k[12]);
vcipher_round!(k[13]);
asm!(
"vcipherlast {s}, {s}, {rk}",
s = inlateout(vreg) state,
rk = in(vreg) k[14],
options(nomem, nostack),
);
store_block_be(block.as_mut_ptr(), state);
}
}
#[target_feature(enable = "altivec,vsx,power8-vector,power8-crypto")]
pub(super) unsafe fn encrypt_block(keys: &PpcRoundKeys, block: &mut [u8; 16]) {
unsafe { encrypt_block_core(keys, block) }
}