#![allow(clippy::indexing_slicing)]
#[cfg(all(target_arch = "aarch64", not(miri)))]
pub(crate) mod aarch64;
#[cfg(any(test, feature = "diag"))]
#[doc(hidden)]
pub(crate) mod dispatch;
#[cfg(any(test, feature = "diag"))]
#[doc(hidden)]
pub(crate) mod dispatch_tables;
#[cfg(test)]
pub(crate) mod kernel_test;
#[cfg(any(test, feature = "diag"))]
pub(crate) mod kernels;
#[cfg(all(target_arch = "s390x", not(miri)))]
pub(crate) mod s390x;
const KECCAKF_ROUNDS: usize = 24;
const RC: [u64; KECCAKF_ROUNDS] = [
0x0000_0000_0000_0001,
0x0000_0000_0000_8082,
0x8000_0000_0000_808a,
0x8000_0000_8000_8000,
0x0000_0000_0000_808b,
0x0000_0000_8000_0001,
0x8000_0000_8000_8081,
0x8000_0000_0000_8009,
0x0000_0000_0000_008a,
0x0000_0000_0000_0088,
0x0000_0000_8000_8009,
0x0000_0000_8000_000a,
0x0000_0000_8000_808b,
0x8000_0000_0000_008b,
0x8000_0000_0000_8089,
0x8000_0000_0000_8003,
0x8000_0000_0000_8002,
0x8000_0000_0000_0080,
0x0000_0000_0000_800a,
0x8000_0000_8000_000a,
0x8000_0000_8000_8081,
0x8000_0000_0000_8080,
0x0000_0000_8000_0001,
0x8000_0000_8000_8008,
];
#[cfg(not(target_arch = "aarch64"))]
#[inline]
#[allow(unused_assignments)] pub(crate) fn keccakf_portable(state: &mut [u64; 25]) {
macro_rules! rho_pi {
($state:ident, $last:ident, $(($pi:expr, $rho:expr)),+ $(,)?) => {
$(
let tmp = $state[$pi];
$state[$pi] = $last.rotate_left($rho);
$last = tmp;
)+
};
}
state[1] = !state[1];
state[2] = !state[2];
state[8] = !state[8];
state[12] = !state[12];
state[17] = !state[17];
state[20] = !state[20];
for &rc in &RC {
let c0 = state[0] ^ state[5] ^ state[10] ^ state[15] ^ state[20];
let c1 = state[1] ^ state[6] ^ state[11] ^ state[16] ^ state[21];
let c2 = state[2] ^ state[7] ^ state[12] ^ state[17] ^ state[22];
let c3 = state[3] ^ state[8] ^ state[13] ^ state[18] ^ state[23];
let c4 = state[4] ^ state[9] ^ state[14] ^ state[19] ^ state[24];
let d0 = c4 ^ c1.rotate_left(1);
let d1 = c0 ^ c2.rotate_left(1);
let d2 = c1 ^ c3.rotate_left(1);
let d3 = c2 ^ c4.rotate_left(1);
let d4 = c3 ^ c0.rotate_left(1);
state[0] ^= d0;
state[5] ^= d0;
state[10] ^= d0;
state[15] ^= d0;
state[20] ^= d0;
state[1] ^= d1;
state[6] ^= d1;
state[11] ^= d1;
state[16] ^= d1;
state[21] ^= d1;
state[2] ^= d2;
state[7] ^= d2;
state[12] ^= d2;
state[17] ^= d2;
state[22] ^= d2;
state[3] ^= d3;
state[8] ^= d3;
state[13] ^= d3;
state[18] ^= d3;
state[23] ^= d3;
state[4] ^= d4;
state[9] ^= d4;
state[14] ^= d4;
state[19] ^= d4;
state[24] ^= d4;
let mut last = state[1];
rho_pi!(
state,
last,
(10, 1),
(7, 3),
(11, 6),
(17, 10),
(18, 15),
(3, 21),
(5, 28),
(16, 36),
(8, 45),
(21, 55),
(24, 2),
(4, 14),
(15, 27),
(23, 41),
(19, 56),
(13, 8),
(12, 25),
(2, 43),
(20, 62),
(14, 18),
(22, 39),
(9, 61),
(6, 20),
(1, 44),
);
{
let (b0, b1, b2, b3, b4) = (state[0], state[1], state[2], state[3], state[4]);
state[0] = b0 ^ (b1 | b2);
state[1] = b1 ^ ((!b2) | b3);
state[2] = b2 ^ (b3 & b4);
state[3] = b3 ^ (b4 | b0);
state[4] = b4 ^ (b0 & b1);
}
{
let (b0, b1, b2, b3, b4) = (state[5], state[6], state[7], state[8], state[9]);
state[5] = b0 ^ (b1 | b2);
state[6] = b1 ^ (b2 & b3);
state[7] = b2 ^ (b3 | (!b4));
state[8] = b3 ^ (b4 | b0);
state[9] = b4 ^ (b0 & b1);
}
{
let (b0, b1, b2, b3, b4) = (state[10], state[11], state[12], state[13], state[14]);
let nb3 = !b3;
state[10] = b0 ^ (b1 | b2);
state[11] = b1 ^ (b2 & b3);
state[12] = b2 ^ (nb3 & b4);
state[13] = nb3 ^ (b4 | b0);
state[14] = b4 ^ (b0 & b1);
}
{
let (b0, b1, b2, b3, b4) = (state[15], state[16], state[17], state[18], state[19]);
let nb3 = !b3;
state[15] = b0 ^ (b1 & b2);
state[16] = b1 ^ (b2 | b3);
state[17] = b2 ^ (nb3 | b4);
state[18] = nb3 ^ (b4 & b0);
state[19] = b4 ^ (b0 | b1);
}
{
let (b0, b1, b2, b3, b4) = (state[20], state[21], state[22], state[23], state[24]);
let nb1 = !b1;
state[20] = b0 ^ (nb1 & b2);
state[21] = nb1 ^ (b2 | b3);
state[22] = b2 ^ (b3 & b4);
state[23] = b3 ^ (b4 | b0);
state[24] = b4 ^ (b0 & b1);
}
state[0] ^= rc;
}
state[1] = !state[1];
state[2] = !state[2];
state[8] = !state[8];
state[12] = !state[12];
state[17] = !state[17];
state[20] = !state[20];
}
#[cfg(target_arch = "aarch64")]
macro_rules! keccak_round_loop_aarch64 {
($a0:ident, $a1:ident, $a2:ident, $a3:ident, $a4:ident,
$a5:ident, $a6:ident, $a7:ident, $a8:ident, $a9:ident,
$a10:ident, $a11:ident, $a12:ident, $a13:ident, $a14:ident,
$a15:ident, $a16:ident, $a17:ident, $a18:ident, $a19:ident,
$a20:ident, $a21:ident, $a22:ident, $a23:ident, $a24:ident) => {{
for &rc in &RC {
let c0 = $a0 ^ $a5 ^ $a10 ^ $a15 ^ $a20;
let c1 = $a1 ^ $a6 ^ $a11 ^ $a16 ^ $a21;
let c2 = $a2 ^ $a7 ^ $a12 ^ $a17 ^ $a22;
let c3 = $a3 ^ $a8 ^ $a13 ^ $a18 ^ $a23;
let c4 = $a4 ^ $a9 ^ $a14 ^ $a19 ^ $a24;
let d0 = c4 ^ c1.rotate_left(1);
let d1 = c0 ^ c2.rotate_left(1);
let d2 = c1 ^ c3.rotate_left(1);
let d3 = c2 ^ c4.rotate_left(1);
let d4 = c3 ^ c0.rotate_left(1);
$a0 ^= d0;
$a5 ^= d0;
$a10 ^= d0;
$a15 ^= d0;
$a20 ^= d0;
$a1 ^= d1;
$a6 ^= d1;
$a11 ^= d1;
$a16 ^= d1;
$a21 ^= d1;
$a2 ^= d2;
$a7 ^= d2;
$a12 ^= d2;
$a17 ^= d2;
$a22 ^= d2;
$a3 ^= d3;
$a8 ^= d3;
$a13 ^= d3;
$a18 ^= d3;
$a23 ^= d3;
$a4 ^= d4;
$a9 ^= d4;
$a14 ^= d4;
$a19 ^= d4;
$a24 ^= d4;
let b0 = $a0;
let b10 = $a1.rotate_left(1);
let b20 = $a2.rotate_left(62);
let b5 = $a3.rotate_left(28);
let b15 = $a4.rotate_left(27);
let b16 = $a5.rotate_left(36);
let b1 = $a6.rotate_left(44);
let b11 = $a7.rotate_left(6);
let b21 = $a8.rotate_left(55);
let b6 = $a9.rotate_left(20);
let b7 = $a10.rotate_left(3);
let b17 = $a11.rotate_left(10);
let b2 = $a12.rotate_left(43);
let b12 = $a13.rotate_left(25);
let b22 = $a14.rotate_left(39);
let b23 = $a15.rotate_left(41);
let b8 = $a16.rotate_left(45);
let b18 = $a17.rotate_left(15);
let b3 = $a18.rotate_left(21);
let b13 = $a19.rotate_left(8);
let b14 = $a20.rotate_left(18);
let b24 = $a21.rotate_left(2);
let b9 = $a22.rotate_left(61);
let b19 = $a23.rotate_left(56);
let b4 = $a24.rotate_left(14);
$a0 = b0 ^ ((!b1) & b2);
$a1 = b1 ^ ((!b2) & b3);
$a2 = b2 ^ ((!b3) & b4);
$a3 = b3 ^ ((!b4) & b0);
$a4 = b4 ^ ((!b0) & b1);
$a5 = b5 ^ ((!b6) & b7);
$a6 = b6 ^ ((!b7) & b8);
$a7 = b7 ^ ((!b8) & b9);
$a8 = b8 ^ ((!b9) & b5);
$a9 = b9 ^ ((!b5) & b6);
$a10 = b10 ^ ((!b11) & b12);
$a11 = b11 ^ ((!b12) & b13);
$a12 = b12 ^ ((!b13) & b14);
$a13 = b13 ^ ((!b14) & b10);
$a14 = b14 ^ ((!b10) & b11);
$a15 = b15 ^ ((!b16) & b17);
$a16 = b16 ^ ((!b17) & b18);
$a17 = b17 ^ ((!b18) & b19);
$a18 = b18 ^ ((!b19) & b15);
$a19 = b19 ^ ((!b15) & b16);
$a20 = b20 ^ ((!b21) & b22);
$a21 = b21 ^ ((!b22) & b23);
$a22 = b22 ^ ((!b23) & b24);
$a23 = b23 ^ ((!b24) & b20);
$a24 = b24 ^ ((!b20) & b21);
$a0 ^= rc;
}
}};
}
#[cfg(target_arch = "aarch64")]
#[inline]
pub(crate) fn keccakf_portable(state: &mut [u64; 25]) {
let mut a0 = state[0];
let mut a1 = state[1];
let mut a2 = state[2];
let mut a3 = state[3];
let mut a4 = state[4];
let mut a5 = state[5];
let mut a6 = state[6];
let mut a7 = state[7];
let mut a8 = state[8];
let mut a9 = state[9];
let mut a10 = state[10];
let mut a11 = state[11];
let mut a12 = state[12];
let mut a13 = state[13];
let mut a14 = state[14];
let mut a15 = state[15];
let mut a16 = state[16];
let mut a17 = state[17];
let mut a18 = state[18];
let mut a19 = state[19];
let mut a20 = state[20];
let mut a21 = state[21];
let mut a22 = state[22];
let mut a23 = state[23];
let mut a24 = state[24];
keccak_round_loop_aarch64!(
a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24
);
state[0] = a0;
state[1] = a1;
state[2] = a2;
state[3] = a3;
state[4] = a4;
state[5] = a5;
state[6] = a6;
state[7] = a7;
state[8] = a8;
state[9] = a9;
state[10] = a10;
state[11] = a11;
state[12] = a12;
state[13] = a13;
state[14] = a14;
state[15] = a15;
state[16] = a16;
state[17] = a17;
state[18] = a18;
state[19] = a19;
state[20] = a20;
state[21] = a21;
state[22] = a22;
state[23] = a23;
state[24] = a24;
}
#[cfg(target_arch = "aarch64")]
#[inline]
#[allow(dead_code)] fn keccakf_absorb_portable<const RATE: usize>(state: &mut [u64; 25], block: &[u8; RATE]) {
debug_assert_eq!(RATE % 8, 0);
let lanes = RATE / 8;
#[inline(always)]
fn read_block_lane(block: *const u8, i: usize) -> u64 {
u64::from_le(unsafe { core::ptr::read_unaligned(block.cast::<u64>().add(i)) })
}
let ptr = block.as_ptr();
macro_rules! fused_load {
($i:expr) => {
if $i < lanes {
state[$i] ^ read_block_lane(ptr, $i)
} else {
state[$i]
}
};
}
let mut a0 = fused_load!(0);
let mut a1 = fused_load!(1);
let mut a2 = fused_load!(2);
let mut a3 = fused_load!(3);
let mut a4 = fused_load!(4);
let mut a5 = fused_load!(5);
let mut a6 = fused_load!(6);
let mut a7 = fused_load!(7);
let mut a8 = fused_load!(8);
let mut a9 = fused_load!(9);
let mut a10 = fused_load!(10);
let mut a11 = fused_load!(11);
let mut a12 = fused_load!(12);
let mut a13 = fused_load!(13);
let mut a14 = fused_load!(14);
let mut a15 = fused_load!(15);
let mut a16 = fused_load!(16);
let mut a17 = fused_load!(17);
let mut a18 = fused_load!(18);
let mut a19 = fused_load!(19);
let mut a20 = fused_load!(20);
let mut a21 = fused_load!(21);
let mut a22 = fused_load!(22);
let mut a23 = fused_load!(23);
let mut a24 = fused_load!(24);
keccak_round_loop_aarch64!(
a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24
);
state[0] = a0;
state[1] = a1;
state[2] = a2;
state[3] = a3;
state[4] = a4;
state[5] = a5;
state[6] = a6;
state[7] = a7;
state[8] = a8;
state[9] = a9;
state[10] = a10;
state[11] = a11;
state[12] = a12;
state[13] = a13;
state[14] = a14;
state[15] = a15;
state[16] = a16;
state[17] = a17;
state[18] = a18;
state[19] = a19;
state[20] = a20;
state[21] = a21;
state[22] = a22;
state[23] = a23;
state[24] = a24;
}
pub(crate) trait Permuter: Copy {
fn permute(self, state: &mut [u64; 25], len_hint: usize);
#[inline(always)]
fn absorb_block<const RATE: usize>(self, state: &mut [u64; 25], block: &[u8; RATE]) {
xor_block_into::<RATE>(state, block);
self.permute(state, RATE);
}
#[inline(always)]
fn permute_x2(self, state_a: &mut [u64; 25], state_b: &mut [u64; 25], len_hint: usize) {
self.permute(state_a, len_hint);
self.permute(state_b, len_hint);
}
#[inline(always)]
fn try_absorb_blocks(self, _state: &mut [u64; 25], _blocks: &[u8], _rate: usize) -> bool {
false
}
}
#[derive(Clone, Copy, Default)]
#[allow(dead_code)] pub(crate) struct InlinePermuter;
impl Permuter for InlinePermuter {
#[inline(always)]
fn permute(self, state: &mut [u64; 25], _len_hint: usize) {
keccakf_portable(state);
}
}
#[cfg(all(target_arch = "aarch64", target_feature = "sha3", not(miri)))]
#[derive(Clone, Copy, Default)]
pub(crate) struct Aarch64Permuter;
#[cfg(all(target_arch = "aarch64", not(target_feature = "sha3"), not(miri)))]
#[derive(Clone, Copy)]
pub(crate) struct Aarch64Permuter {
has_sha3: bool,
}
#[cfg(all(target_arch = "aarch64", not(target_feature = "sha3"), not(miri)))]
impl Default for Aarch64Permuter {
#[inline]
fn default() -> Self {
Self {
has_sha3: {
use crate::platform::caps::aarch64 as aarch64_caps;
crate::platform::caps().has(aarch64_caps::SHA3)
},
}
}
}
#[cfg(all(target_arch = "aarch64", target_feature = "sha3", not(miri)))]
impl Permuter for Aarch64Permuter {
#[inline(always)]
fn permute(self, state: &mut [u64; 25], _len_hint: usize) {
if cfg!(target_vendor = "apple") {
aarch64::keccakf_aarch64_sha3_single(state);
} else {
keccakf_portable(state);
}
}
#[inline(always)]
fn absorb_block<const RATE: usize>(self, state: &mut [u64; 25], block: &[u8; RATE]) {
if cfg!(target_vendor = "apple") {
if RATE <= 144 {
aarch64::keccakf_aarch64_sha3_absorb_single::<RATE>(state, block);
} else {
xor_block_into::<RATE>(state, block);
aarch64::keccakf_aarch64_sha3_single(state);
}
} else {
keccakf_absorb_portable::<RATE>(state, block);
}
}
#[inline(always)]
fn permute_x2(self, state_a: &mut [u64; 25], state_b: &mut [u64; 25], _len_hint: usize) {
aarch64::keccakf_aarch64_sha3_x2(state_a, state_b);
}
}
#[cfg(all(target_arch = "aarch64", not(target_feature = "sha3"), not(miri)))]
impl Permuter for Aarch64Permuter {
#[inline(always)]
fn permute(self, state: &mut [u64; 25], _len_hint: usize) {
if cfg!(target_vendor = "apple") && self.has_sha3 {
aarch64::keccakf_aarch64_sha3_single(state);
} else {
keccakf_portable(state);
}
}
#[inline(always)]
fn absorb_block<const RATE: usize>(self, state: &mut [u64; 25], block: &[u8; RATE]) {
if cfg!(target_vendor = "apple") && self.has_sha3 {
if RATE <= 144 {
aarch64::keccakf_aarch64_sha3_absorb_single::<RATE>(state, block);
} else {
xor_block_into::<RATE>(state, block);
aarch64::keccakf_aarch64_sha3_single(state);
}
} else {
keccakf_absorb_portable::<RATE>(state, block);
}
}
#[inline(always)]
fn permute_x2(self, state_a: &mut [u64; 25], state_b: &mut [u64; 25], len_hint: usize) {
if self.has_sha3 {
aarch64::keccakf_aarch64_sha3_x2(state_a, state_b);
} else {
self.permute(state_a, len_hint);
self.permute(state_b, len_hint);
}
}
}
#[cfg(target_arch = "s390x")]
#[derive(Clone, Copy)]
pub(crate) struct S390xPermuter {
has_kimd: bool,
}
#[cfg(target_arch = "s390x")]
impl Default for S390xPermuter {
#[inline]
fn default() -> Self {
Self {
has_kimd: {
use crate::platform::caps::s390x as s390x_caps;
crate::platform::caps().has(s390x_caps::MSA8)
},
}
}
}
#[cfg(target_arch = "s390x")]
impl Permuter for S390xPermuter {
#[inline(always)]
fn permute(self, state: &mut [u64; 25], _len_hint: usize) {
keccakf_portable(state);
}
#[inline(always)]
fn try_absorb_blocks(self, state: &mut [u64; 25], blocks: &[u8], rate: usize) -> bool {
if self.has_kimd
&& let Some(fc) = s390x::kimd_fc_for_rate(rate)
{
unsafe {
s390x::absorb_blocks_kimd(state, blocks, fc);
}
return true;
}
false
}
}
#[cfg(all(target_arch = "aarch64", not(miri)))]
pub(crate) type PlatformPermuter = Aarch64Permuter;
#[cfg(all(target_arch = "s390x", not(miri)))]
pub(crate) type PlatformPermuter = S390xPermuter;
#[cfg(any(miri, not(any(target_arch = "aarch64", target_arch = "s390x"))))]
pub(crate) type PlatformPermuter = InlinePermuter;
pub(crate) type KeccakCore<const RATE: usize> = KeccakCoreImpl<RATE, PlatformPermuter, true>;
pub(crate) type PublicKeccakCore<const RATE: usize> = KeccakCoreImpl<RATE, PlatformPermuter, false>;
pub(crate) type KeccakXof<const RATE: usize> = KeccakXofImpl<RATE, PlatformPermuter, true>;
pub(crate) type PublicKeccakXof<const RATE: usize> = KeccakXofImpl<RATE, PlatformPermuter, false>;
#[derive(Clone)]
pub(crate) struct KeccakCoreImpl<const RATE: usize, P: Permuter, const ZEROIZE: bool> {
state: [u64; 25],
buf_len: usize,
permuter: P,
}
impl<const RATE: usize, const ZEROIZE: bool> Default for KeccakCoreImpl<RATE, PlatformPermuter, ZEROIZE> {
#[inline]
fn default() -> Self {
Self {
state: [0u64; 25],
buf_len: 0,
permuter: PlatformPermuter::default(),
}
}
}
#[cfg(all(
any(test, feature = "std"),
not(miri),
any(target_arch = "aarch64", target_arch = "s390x")
))]
impl<const RATE: usize, const ZEROIZE: bool> Default for KeccakCoreImpl<RATE, InlinePermuter, ZEROIZE> {
#[inline]
fn default() -> Self {
Self {
state: [0u64; 25],
buf_len: 0,
permuter: InlinePermuter,
}
}
}
impl<const RATE: usize, P: Permuter, const ZEROIZE: bool> Drop for KeccakCoreImpl<RATE, P, ZEROIZE> {
fn drop(&mut self) {
if ZEROIZE {
for word in self.state.iter_mut() {
unsafe { core::ptr::write_volatile(word, 0) };
}
core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
}
}
}
impl<const RATE: usize, P: Permuter, const ZEROIZE: bool> KeccakCoreImpl<RATE, P, ZEROIZE> {
pub(crate) fn update(&mut self, mut data: &[u8]) {
if data.is_empty() {
return;
}
if data.len() < RATE - self.buf_len {
xor_bytes_into_state::<RATE>(&mut self.state, self.buf_len, data);
self.buf_len = self.buf_len.strict_add(data.len());
return;
}
let permuter = self.permuter;
if self.buf_len != 0 {
let take = core::cmp::min(RATE - self.buf_len, data.len());
xor_bytes_into_state::<RATE>(&mut self.state, self.buf_len, &data[..take]);
self.buf_len = self.buf_len.strict_add(take);
data = &data[take..];
if self.buf_len == RATE {
permuter.permute(&mut self.state, RATE);
self.buf_len = 0;
}
}
let state = &mut self.state;
let (blocks, rest) = data.as_chunks::<RATE>();
if !blocks.is_empty() {
let block_bytes = &data[..blocks.len().strict_mul(RATE)];
if !permuter.try_absorb_blocks(state, block_bytes, RATE) {
for block in blocks {
permuter.absorb_block(state, block);
}
}
}
data = rest;
if !data.is_empty() {
xor_bytes_into_state::<RATE>(&mut self.state, 0, data);
self.buf_len = data.len();
}
}
#[inline(always)]
fn finalize_state(&self, ds: u8) -> [u64; 25] {
let permuter = self.permuter;
let mut state = self.state;
let buf_len = self.buf_len;
debug_assert!(buf_len < RATE, "buf_len={} should be < RATE={}", buf_len, RATE);
pad_absorbed_state::<RATE>(&mut state, buf_len, ds);
permuter.permute(&mut state, 0);
state
}
pub(crate) fn finalize_into_fixed<const OUT: usize>(&self, ds: u8, out: &mut [u8; OUT]) {
debug_assert!(OUT <= RATE);
let state = self.finalize_state(ds);
let (chunks, rem) = out.as_chunks_mut::<8>();
for (chunk, &word) in chunks.iter_mut().zip(state.iter()) {
*chunk = word.to_le_bytes();
}
if !rem.is_empty() {
let bytes = state[chunks.len()].to_le_bytes();
rem.copy_from_slice(&bytes[..rem.len()]);
}
}
pub(crate) fn finalize_xof(&self, ds: u8) -> KeccakXofImpl<RATE, P, ZEROIZE> {
let permuter = self.permuter;
let state = self.finalize_state(ds);
KeccakXofImpl {
state,
pos: 0,
permuter,
}
}
}
impl<const RATE: usize, P: Permuter> KeccakCoreImpl<RATE, P, false> {
pub(crate) fn into_xof(self, ds: u8) -> KeccakXofImpl<RATE, P, false> {
let mut this = core::mem::ManuallyDrop::new(self);
let inner = &mut *this;
let permuter = inner.permuter;
let buf_len = inner.buf_len;
debug_assert!(buf_len < RATE, "buf_len={} should be < RATE={}", buf_len, RATE);
pad_absorbed_state::<RATE>(&mut inner.state, buf_len, ds);
permuter.permute(&mut inner.state, 0);
KeccakXofImpl {
state: unsafe { core::ptr::read(&inner.state) },
pos: 0,
permuter,
}
}
}
#[inline]
pub(crate) fn oneshot_fixed<const RATE: usize, const OUT: usize>(ds: u8, data: &[u8]) -> [u8; OUT] {
debug_assert!(OUT <= RATE);
debug_assert_eq!(RATE % 8, 0);
let permuter = PlatformPermuter::default();
let mut state = [0u64; 25];
let (blocks, rest) = data.as_chunks::<RATE>();
if !blocks.is_empty() {
let block_bytes = &data[..blocks.len().strict_mul(RATE)];
if !permuter.try_absorb_blocks(&mut state, block_bytes, RATE) {
for block in blocks {
permuter.absorb_block(&mut state, block);
}
}
}
finalize_pad_absorb::<RATE>(&mut state, rest, ds, permuter);
let mut out = [0u8; OUT];
extract_output::<OUT>(&state, &mut out);
out
}
#[inline(always)]
fn xor_block_into<const RATE: usize>(state: &mut [u64; 25], block: &[u8; RATE]) {
debug_assert_eq!(RATE % 8, 0);
let lanes = RATE / 8;
let ptr = block.as_ptr() as *const u64;
let mut i = 0usize;
while i < lanes {
let v = unsafe { core::ptr::read_unaligned(ptr.add(i)) };
state[i] ^= u64::from_le(v);
i += 1;
}
}
#[inline(always)]
fn xor_bytes_into_state<const RATE: usize>(state: &mut [u64; 25], mut offset: usize, mut data: &[u8]) {
debug_assert_eq!(RATE % 8, 0);
debug_assert!(offset <= RATE);
debug_assert!(data.len() <= RATE - offset);
while !data.is_empty() && (offset & 7) != 0 {
state[offset / 8] ^= (data[0] as u64) << ((offset & 7).strict_mul(8));
offset = offset.strict_add(1);
data = &data[1..];
}
while data.len() >= 8 {
let word = unsafe { core::ptr::read_unaligned(data.as_ptr().cast::<u64>()) };
state[offset / 8] ^= u64::from_le(word);
offset = offset.strict_add(8);
data = &data[8..];
}
for &byte in data {
state[offset / 8] ^= (byte as u64) << ((offset & 7).strict_mul(8));
offset = offset.strict_add(1);
}
}
#[inline(always)]
fn pad_absorbed_state<const RATE: usize>(state: &mut [u64; 25], pos: usize, ds: u8) {
debug_assert_eq!(RATE % 8, 0);
debug_assert!(pos < RATE);
state[pos / 8] ^= (ds as u64) << ((pos & 7).strict_mul(8));
let last = RATE - 1;
state[last / 8] ^= 0x80_u64 << ((last & 7).strict_mul(8));
}
#[inline(always)]
fn pad_into_state<const RATE: usize>(state: &mut [u64; 25], remainder: &[u8], ds: u8) {
debug_assert_eq!(RATE % 8, 0);
debug_assert!(remainder.len() < RATE);
xor_bytes_into_state::<RATE>(state, 0, remainder);
pad_absorbed_state::<RATE>(state, remainder.len(), ds);
}
#[inline(always)]
fn finalize_pad_absorb<const RATE: usize>(state: &mut [u64; 25], remainder: &[u8], ds: u8, permuter: PlatformPermuter) {
pad_into_state::<RATE>(state, remainder, ds);
permuter.permute(state, 0);
}
#[inline(always)]
fn extract_output<const OUT: usize>(state: &[u64; 25], out: &mut [u8; OUT]) {
macro_rules! write_lane {
($i:expr) => {
if OUT >= ($i + 1) * 8 {
out[$i * 8..($i + 1) * 8].copy_from_slice(&state[$i].to_le_bytes());
}
};
}
write_lane!(0);
write_lane!(1);
write_lane!(2);
write_lane!(3);
write_lane!(4);
write_lane!(5);
write_lane!(6);
write_lane!(7);
let rem = OUT & 7;
if rem != 0 {
let offset = OUT & !7;
let bytes = state[offset / 8].to_le_bytes();
out[offset..].copy_from_slice(&bytes[..rem]);
}
}
pub(crate) fn oneshot_pair<const RATE: usize, const OUT: usize>(
ds: u8,
data_a: &[u8],
data_b: &[u8],
) -> ([u8; OUT], [u8; OUT]) {
debug_assert!(OUT <= RATE);
let permuter = PlatformPermuter::default();
let mut state_a = [0u64; 25];
let mut state_b = [0u64; 25];
let (blocks_a, rest_a) = data_a.as_chunks::<RATE>();
let (blocks_b, rest_b) = data_b.as_chunks::<RATE>();
let min_blocks = core::cmp::min(blocks_a.len(), blocks_b.len());
for i in 0..min_blocks {
xor_block_into::<RATE>(&mut state_a, &blocks_a[i]);
xor_block_into::<RATE>(&mut state_b, &blocks_b[i]);
permuter.permute_x2(&mut state_a, &mut state_b, 0);
}
for block in &blocks_a[min_blocks..] {
permuter.absorb_block(&mut state_a, block);
}
for block in &blocks_b[min_blocks..] {
permuter.absorb_block(&mut state_b, block);
}
pad_into_state::<RATE>(&mut state_a, rest_a, ds);
pad_into_state::<RATE>(&mut state_b, rest_b, ds);
permuter.permute_x2(&mut state_a, &mut state_b, 0);
let mut out_a = [0u8; OUT];
let mut out_b = [0u8; OUT];
extract_output::<OUT>(&state_a, &mut out_a);
extract_output::<OUT>(&state_b, &mut out_b);
(out_a, out_b)
}
#[derive(Clone)]
pub(crate) struct KeccakXofImpl<const RATE: usize, P: Permuter, const ZEROIZE: bool> {
state: [u64; 25],
pos: usize,
permuter: P,
}
impl<const RATE: usize, P: Permuter, const ZEROIZE: bool> Drop for KeccakXofImpl<RATE, P, ZEROIZE> {
fn drop(&mut self) {
if ZEROIZE {
for word in self.state.iter_mut() {
unsafe { core::ptr::write_volatile(word, 0) };
}
core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
}
}
}
impl<const RATE: usize, P: Permuter, const ZEROIZE: bool> KeccakXofImpl<RATE, P, ZEROIZE> {
#[inline(always)]
fn copy_state_first_32(state: &[u64; 25], out: &mut [u8]) {
debug_assert!(out.len() == 32);
out[0..8].copy_from_slice(&state[0].to_le_bytes());
out[8..16].copy_from_slice(&state[1].to_le_bytes());
out[16..24].copy_from_slice(&state[2].to_le_bytes());
out[24..32].copy_from_slice(&state[3].to_le_bytes());
}
#[inline(always)]
fn copy_state_prefix(state: &[u64; 25], out: &mut [u8]) {
debug_assert!(out.len() <= RATE);
let (chunks, rem) = out.as_chunks_mut::<8>();
for (chunk, &word) in chunks.iter_mut().zip(state.iter()) {
*chunk = word.to_le_bytes();
}
if !rem.is_empty() {
let bytes = state[chunks.len()].to_le_bytes();
rem.copy_from_slice(&bytes[..rem.len()]);
}
}
#[inline(always)]
fn copy_state_bytes(state: &[u64; 25], mut pos: usize, mut out: &mut [u8]) {
debug_assert!(pos <= RATE);
debug_assert!(out.len() <= RATE - pos);
while !out.is_empty() {
let lane = pos / 8;
let byte = pos % 8;
let bytes = state[lane].to_le_bytes();
let take = core::cmp::min(8 - byte, out.len());
out[..take].copy_from_slice(&bytes[byte..byte.strict_add(take)]);
pos = pos.strict_add(take);
out = &mut out[take..];
}
}
pub(crate) fn squeeze_into(&mut self, mut out: &mut [u8]) {
if self.pos == 0 && out.len() == 32 && RATE >= 32 {
Self::copy_state_first_32(&self.state, out);
self.pos = 32;
return;
}
if self.pos == 0 && out.len() <= RATE {
Self::copy_state_prefix(&self.state, out);
self.pos = out.len();
return;
}
while !out.is_empty() {
if self.pos == RATE {
let permuter = self.permuter;
permuter.permute(&mut self.state, 0);
self.pos = 0;
}
let take = core::cmp::min(RATE - self.pos, out.len());
Self::copy_state_bytes(&self.state, self.pos, &mut out[..take]);
self.pos = self.pos.strict_add(take);
out = &mut out[take..];
}
}
}