use super::cpu::HaveNeon;
unsafe extern "C" {
#[link_name = aws_lc_symbol!("KeccakF1600")]
fn keccak_f1600_int(a: *mut [[u32; 2]; 25]);
#[link_name = aws_lc_symbol!("KeccakF1600_neon")]
fn keccak_f1600_neon_regs();
#[link_name = aws_lc_symbol!("keccak_f1600_neon")]
fn keccak_f1600_neon(a: *mut [u64; 25]);
}
core::arch::global_asm!(
".text",
".fpu neon",
".code 32",
".globl {KeccakF1600}",
".globl {KeccakF1600_neon}",
include_str!("openssl/keccak1600-armv4.s"),
".globl {keccak_f1600_neon}",
".type {keccak_f1600_neon}, %function",
".align 5",
"{keccak_f1600_neon}:",
"stmdb sp!, {{r4, lr}}",
"vstmdb sp!, {{d8-d15}}",
"mov r4, r0",
"vld1.32 {{d0}}, [r0,:64]!",
"vld1.32 {{d2}}, [r0,:64]!",
"vld1.32 {{d4}}, [r0,:64]!",
"vld1.32 {{d6}}, [r0,:64]!",
"vld1.32 {{d8}}, [r0,:64]!",
"vld1.32 {{d1}}, [r0,:64]!",
"vld1.32 {{d3}}, [r0,:64]!",
"vld1.32 {{d5}}, [r0,:64]!",
"vld1.32 {{d7}}, [r0,:64]!",
"vld1.32 {{d9}}, [r0,:64]!",
"vld1.32 {{d10}}, [r0,:64]!",
"vld1.32 {{d12}}, [r0,:64]!",
"vld1.32 {{d14}}, [r0,:64]!",
"vld1.32 {{d16}}, [r0,:64]!",
"vld1.32 {{d18}}, [r0,:64]!",
"vld1.32 {{d11}}, [r0,:64]!",
"vld1.32 {{d13}}, [r0,:64]!",
"vld1.32 {{d15}}, [r0,:64]!",
"vld1.32 {{d17}}, [r0,:64]!",
"vld1.32 {{d19}}, [r0,:64]!",
"vld1.32 {{d20-d23}}, [r0,:64]!",
"vld1.32 {{d24}}, [r0,:64]",
"mov r0, r4",
"bl {KeccakF1600_neon}",
"mov r0, r4",
"vst1.32 {{d0}}, [r0,:64]!",
"vst1.32 {{d2}}, [r0,:64]!",
"vst1.32 {{d4}}, [r0,:64]!",
"vst1.32 {{d6}}, [r0,:64]!",
"vst1.32 {{d8}}, [r0,:64]!",
"vst1.32 {{d1}}, [r0,:64]!",
"vst1.32 {{d3}}, [r0,:64]!",
"vst1.32 {{d5}}, [r0,:64]!",
"vst1.32 {{d7}}, [r0,:64]!",
"vst1.32 {{d9}}, [r0,:64]!",
"vst1.32 {{d10}}, [r0,:64]!",
"vst1.32 {{d12}}, [r0,:64]!",
"vst1.32 {{d14}}, [r0,:64]!",
"vst1.32 {{d16}}, [r0,:64]!",
"vst1.32 {{d18}}, [r0,:64]!",
"vst1.32 {{d11}}, [r0,:64]!",
"vst1.32 {{d13}}, [r0,:64]!",
"vst1.32 {{d15}}, [r0,:64]!",
"vst1.32 {{d17}}, [r0,:64]!",
"vst1.32 {{d19}}, [r0,:64]!",
"vst1.32 {{d20-d23}}, [r0,:64]!",
"vst1.32 {{d24}}, [r0,:64]",
"vldmia sp!, {{d8-d15}}",
"ldmia sp!, {{r4, pc}}",
".size {keccak_f1600_neon}, .-{keccak_f1600_neon}",
".fpu vfpv3",
KeccakF1600 = sym keccak_f1600_int,
KeccakF1600_neon = sym keccak_f1600_neon_regs,
keccak_f1600_neon = sym keccak_f1600_neon,
);
pub(crate) fn sha3_keccak_f1600(a: &mut [u64; 25], _rc: &[u64; 24]) {
match HaveNeon::check() {
Some(neon) => permute_neon(neon, a),
None => permute_int(a),
}
}
pub(crate) fn sha3_keccak4_f1600(a: &mut [[u64; 25]; 4], rc: &[u64; 24]) {
sha3_keccak_nof4_f1600::<4>(a, rc)
}
pub(crate) fn sha3_keccak_nof4_f1600<const N: usize>(a: &mut [[u64; 25]; 4], _rc: &[u64; 24]) {
match HaveNeon::check() {
Some(neon) => a[..N].iter_mut().for_each(|a| permute_neon(neon, a)),
None => a[..N].iter_mut().for_each(permute_int),
}
}
fn permute_neon(_: HaveNeon, a: &mut [u64; 25]) {
unsafe { keccak_f1600_neon(a) }
}
fn permute_int(a: &mut [u64; 25]) {
let mut s = a.map(interleave);
unsafe { keccak_f1600_int(&mut s) };
*a = s.map(deinterleave);
}
fn evens(x: u32) -> u32 {
let mut x = x & 0x5555_5555;
x = (x | (x >> 1)) & 0x3333_3333;
x = (x | (x >> 2)) & 0x0f0f_0f0f;
x = (x | (x >> 4)) & 0x00ff_00ff;
(x | (x >> 8)) & 0x0000_ffff
}
fn spread(x: u32) -> u32 {
let mut x = x & 0x0000_ffff;
x = (x | (x << 8)) & 0x00ff_00ff;
x = (x | (x << 4)) & 0x0f0f_0f0f;
x = (x | (x << 2)) & 0x3333_3333;
(x | (x << 1)) & 0x5555_5555
}
fn interleave(lane: u64) -> [u32; 2] {
let (lo, hi) = (lane as u32, (lane >> 32) as u32);
[
evens(lo) | (evens(hi) << 16),
evens(lo >> 1) | (evens(hi >> 1) << 16),
]
}
fn deinterleave([even, odd]: [u32; 2]) -> u64 {
let lo = spread(even) | (spread(odd) << 1);
let hi = spread(even >> 16) | (spread(odd >> 16) << 1);
lo as u64 | ((hi as u64) << 32)
}
#[cfg(test)]
#[cfg_attr(coverage_nightly, coverage(off))]
mod tests {
use super::*;
#[test]
fn neon_matches_int() {
let Some(neon) = HaveNeon::check() else {
return;
};
let mut want: [u64; 25] =
core::array::from_fn(|i| (i as u64 + 1).wrapping_mul(0x9e37_79b9_7f4a_7c15));
let mut got = want;
for _ in 0..3 {
permute_int(&mut want);
permute_neon(neon, &mut got);
assert_eq!(got, want);
}
}
#[test]
fn interleave_round_trips() {
for lane in [0, u64::MAX, 1, 1 << 63, 0x0123_4567_89ab_cdef] {
assert_eq!(deinterleave(interleave(lane)), lane);
}
assert_eq!(interleave(0b10), [0, 1]);
assert_eq!(interleave(1 << 32), [1 << 16, 0]);
}
}