use super::{
DIGEST_LENGTH, Digest,
padding::{BLOCK_LENGTH, IV, K, block, block_count},
};
use core::arch::aarch64::*;
use sha2::block_api::compress512;
pub(super) const LANES: usize = 2;
#[target_feature(enable = "sha3")]
pub(super) fn hash(messages: &[&[u8]; LANES]) -> [Digest; LANES] {
let counts = [
block_count(messages[0].len()),
block_count(messages[1].len()),
];
let shared = counts[0].min(counts[1]);
let mut state = [IV; LANES];
for index in 0..shared {
compress(
&mut state,
&[block(messages[0], index), block(messages[1], index)],
);
}
for ((words, message), count) in state.iter_mut().zip(messages).zip(counts) {
for index in shared..count {
compress512(words, &[block(message, index)]);
}
}
let mut out = [Digest([0; DIGEST_LENGTH]); LANES];
for (digest, words) in out.iter_mut().zip(state) {
for (bytes, word) in digest.0.as_chunks_mut::<8>().0.iter_mut().zip(words) {
*bytes = word.to_be_bytes();
}
}
out
}
#[target_feature(enable = "sha3")]
fn compress(state: &mut [[u64; 8]; LANES], blocks: &[[u8; BLOCK_LENGTH]; LANES]) {
let mut s = [[vdupq_n_u64(0); 4]; LANES];
let mut w = [[vdupq_n_u64(0); 8]; LANES];
for lane in 0..LANES {
for (reg, words) in s[lane].iter_mut().zip(state[lane].as_chunks::<2>().0) {
*reg = unsafe { vld1q_u64(words.as_ptr()) };
}
for (reg, bytes) in w[lane].iter_mut().zip(blocks[lane].as_chunks::<16>().0) {
let bytes = unsafe { vld1q_u8(bytes.as_ptr()) };
*reg = vreinterpretq_u64_u8(vrev64q_u8(bytes));
}
}
macro_rules! rounds {
($k:expr, $schedule:expr) => {
rounds!(@ $k, $schedule, 0 1 2 3 4 5 6 7)
};
(@ $k:expr, $schedule:expr, $($j:literal)*) => {$(
if $schedule {
for words in &mut w {
words[$j] = vsha512su1q_u64(
vsha512su0q_u64(words[$j], words[($j + 1) % 8]),
words[($j + 7) % 8],
vextq_u64::<1>(words[($j + 4) % 8], words[($j + 5) % 8]),
);
}
}
let k = unsafe { vld1q_u64($k.as_ptr().add(2 * $j)) };
let kw0 = vaddq_u64(w[0][$j], k);
let kw1 = vaddq_u64(w[1][$j], k);
let sum0 = vaddq_u64(vextq_u64::<1>(kw0, kw0), s[0][(7 - $j) % 4]);
let sum1 = vaddq_u64(vextq_u64::<1>(kw1, kw1), s[1][(7 - $j) % 4]);
let t0 = vsha512hq_u64(
sum0,
vextq_u64::<1>(s[0][(6 - $j % 4) % 4], s[0][(7 - $j) % 4]),
vextq_u64::<1>(s[0][(5 - $j % 4) % 4], s[0][(6 - $j % 4) % 4]),
);
let t1 = vsha512hq_u64(
sum1,
vextq_u64::<1>(s[1][(6 - $j % 4) % 4], s[1][(7 - $j) % 4]),
vextq_u64::<1>(s[1][(5 - $j % 4) % 4], s[1][(6 - $j % 4) % 4]),
);
s[0][(7 - $j) % 4] = vsha512h2q_u64(
t0, s[0][(5 - $j % 4) % 4], s[0][(4 - $j % 4) % 4],
);
s[1][(7 - $j) % 4] = vsha512h2q_u64(
t1, s[1][(5 - $j % 4) % 4], s[1][(4 - $j % 4) % 4],
);
s[0][(5 - $j % 4) % 4] = vaddq_u64(s[0][(5 - $j % 4) % 4], t0);
s[1][(5 - $j % 4) % 4] = vaddq_u64(s[1][(5 - $j % 4) % 4], t1);
)*};
}
let (chunks, _) = K.as_chunks::<16>();
rounds!(chunks[0], false);
for k in &chunks[1..] {
rounds!(k, true);
}
for (words, working) in state.iter_mut().zip(s) {
for (pair, reg) in words.as_chunks_mut::<2>().0.iter_mut().zip(working) {
unsafe { vst1q_u64(pair.as_mut_ptr(), vaddq_u64(vld1q_u64(pair.as_ptr()), reg)) };
}
}
}