keccak: refactor to a closure-based API (#113)

The new API provides a better way for exposing support for parallel
processing in implemented backends and resolves the issue with branching
on each application of a Keccak permutation.
This commit is contained in:
Artyom Pavlov 2026-03-06 16:07:46 +03:00 committed by GitHub
parent 806d446c41
commit 1efeff7d02
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 834 additions and 728 deletions

View File

@ -42,8 +42,9 @@ jobs:
toolchain: ${{ matrix.rust }}
targets: ${{ matrix.target }}
- run: cargo build --target ${{ matrix.target }}
- run: cargo build --target ${{ matrix.target }} --features parallel
- env:
RUSTFLAGS: '-Dwarnings --cfg keccak_backend="soft-compact"'
RUSTFLAGS: '-Dwarnings --cfg keccak_backend="soft"'
run: cargo build --target ${{ matrix.target }}
minimal-versions:
@ -80,25 +81,36 @@ jobs:
targets: ${{ matrix.target }}
- run: cargo check --target ${{ matrix.target }}
- run: cargo test --target ${{ matrix.target }}
- run: cargo test --release --target ${{ matrix.target }}
- run: cargo test --target ${{ matrix.target }} --features parallel
- run: cargo test --release --target ${{ matrix.target }} --features parallel
- env:
RUSTFLAGS: '-Dwarnings --cfg keccak_backend="armv8_asm"'
RUSTFLAGS: '-Dwarnings --cfg keccak_soft_compact'
run: cargo test --release --target ${{ matrix.target }}
- env:
RUSTFLAGS: '-Dwarnings --cfg keccak_backend="soft-compact"'
RUSTFLAGS: '-Dwarnings --cfg keccak_backend="soft"'
run: cargo test --release --target ${{ matrix.target }}
- if: contains(matrix.target, 'aarch64')
env:
RUSTFLAGS: '-Dwarnings --cfg keccak_backend="aarch64_sha3"'
run: cargo test --release --target ${{ matrix.target }}
test-simd:
runs-on: ubuntu-latest
env:
RUSTFLAGS: '-Dwarnings --cfg keccak_backend="simd"'
steps:
- uses: actions/checkout@v4
- uses: RustCrypto/actions/cargo-cache@master
- uses: dtolnay/rust-toolchain@master
with:
toolchain: nightly
- run: cargo test
- env:
RUSTFLAGS: '-Dwarnings --cfg keccak_backend="simd128"'
run: cargo test --features parallel
- env:
RUSTFLAGS: '-Dwarnings --cfg keccak_backend="simd256"'
run: cargo test --features parallel
- env:
RUSTFLAGS: '-Dwarnings --cfg keccak_backend="simd512"'
run: cargo test --features parallel
test-miri:
runs-on: ubuntu-latest
@ -117,15 +129,22 @@ jobs:
components: miri
- run: cargo miri setup
- run: cargo miri test --target ${{ matrix.target }}
- run: cargo miri test --target ${{ matrix.target }} --features parallel
- env:
RUSTFLAGS: '-Dwarnings --cfg keccak_backend="armv8_asm"'
run: cargo miri test --release --target ${{ matrix.target }}
RUSTFLAGS: '-Dwarnings --cfg keccak_soft_compact'
run: cargo miri test --release --target ${{ matrix.target }} --features parallel
- env:
RUSTFLAGS: '-Dwarnings --cfg keccak_backend="simd"'
run: cargo miri test --release --target ${{ matrix.target }}
RUSTFLAGS: '-Dwarnings --cfg keccak_backend="simd128"'
run: cargo miri test --release --target ${{ matrix.target }} --features parallel
- env:
RUSTFLAGS: '-Dwarnings --cfg keccak_backend="soft-compact"'
run: cargo miri test --release --target ${{ matrix.target }}
RUSTFLAGS: '-Dwarnings --cfg keccak_backend="simd256"'
run: cargo miri test --release --target ${{ matrix.target }} --features parallel
- env:
RUSTFLAGS: '-Dwarnings --cfg keccak_backend="simd512"'
run: cargo miri test --release --target ${{ matrix.target }} --features parallel
- env:
RUSTFLAGS: '-Dwarnings --cfg keccak_backend="soft"'
run: cargo miri test --release --target ${{ matrix.target }} --features parallel
aarch64-sha3:
needs: set-msrv
@ -154,8 +173,8 @@ jobs:
mv /tmp/cross ~/.cargo/bin
shell: bash
- env:
RUSTFLAGS: '-C target-feature=+sha3 --cfg keccak_backend="armv8_asm"'
RUSTFLAGS: '-C target-feature=+sha3 --cfg keccak_backend="aarch64_sha3"'
run: |
cd keccak
# Cross doesn't enable `sha3` by default, but QEMU supports it.
cross test --target aarch64-unknown-linux-gnu --no-default-features
cross test --target aarch64-unknown-linux-gnu --features parallel

27
Cargo.lock generated
View File

@ -13,6 +13,12 @@ dependencies = [
name = "bash-f"
version = "0.1.0"
[[package]]
name = "cfg-if"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "cpufeatures"
version = "0.3.0"
@ -22,18 +28,35 @@ dependencies = [
"libc",
]
[[package]]
name = "hybrid-array"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1b229d73f5803b562cc26e4da0396c8610a4ee209f4fac8fa4f8d709166dc45"
dependencies = [
"typenum",
]
[[package]]
name = "keccak"
version = "0.2.0-rc.2"
dependencies = [
"cfg-if",
"cpufeatures",
"hybrid-array",
]
[[package]]
name = "libc"
version = "0.2.177"
version = "0.2.182"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976"
checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112"
[[package]]
name = "typenum"
version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb"
[[package]]
name = "zeroize"

View File

@ -16,9 +16,16 @@ readme = "README.md"
edition = "2024"
rust-version = "1.85"
[dependencies]
cfg-if = "1"
hybrid-array = { version = "0.4", optional = true }
[target.'cfg(target_arch = "aarch64")'.dependencies]
cpufeatures = "0.3"
[features]
parallel = ["dep:hybrid-array"]
[lints.rust]
missing_debug_implementations = "warn"
missing_docs = "warn"
@ -29,7 +36,10 @@ unused_qualifications = "warn"
[lints.rust.unexpected_cfgs]
level = "warn"
check-cfg = ['cfg(keccak_backend, values("simd", "soft-compact"))']
check-cfg = [
'cfg(keccak_soft_compact)',
'cfg(keccak_backend, values("aarch64_sha3", "simd128", "simd256", "simd512", "soft"))',
]
[lints.clippy]
borrow_as_ptr = "warn"

View File

@ -7,10 +7,8 @@
![Rust Version][rustc-image]
[![Project Chat][chat-image]][chat-link]
Pure Rust implementation of the [Keccak Sponge Function][keccak] including the keccak-f
and keccak-p variants.
[Documentation][docs-link]
Pure Rust implementation of the [Keccak Sponge Function][keccak] including the `keccak-f`
and `keccak-p` variants.
## About
@ -20,17 +18,48 @@ cryptographic functions are built.
For the SHA-3 family including the SHAKE XOFs, see the [`sha3`] crate, which
is built on this crate.
## Minimum Supported Rust Version
## Examples
Rust **1.85** or higher.
```rust
// Test vector from KeccakCodePackage
let mut state = [0u64; 25];
Minimum supported Rust version can be changed in the future, but it will be
done with a minor version bump.
keccak::Keccak::new().with_f1600(|f1600| {
f1600(&mut state);
assert_eq!(state, [
0xF1258F7940E1DDE7, 0x84D5CCF933C0478A, 0xD598261EA65AA9EE, 0xBD1547306F80494D,
0x8B284E056253D057, 0xFF97A42D7F8E6FD4, 0x90FEE5A0A44647C4, 0x8C5BDA0CD6192E76,
0xAD30A6F71B19059C, 0x30935AB7D08FFC64, 0xEB5AA93F2317D635, 0xA9A6E6260D712103,
0x81A57C16DBCF555F, 0x43B831CD0347C826, 0x01F22F1A11A5569F, 0x05E5635A21D9AE61,
0x64BEFEF28CC970F2, 0x613670957BC46611, 0xB87C5A554FD00ECB, 0x8C3EE88A1CCF32C8,
0x940C7922AE3A2614, 0x1841F924A2C509E4, 0x16F53526E70465C2, 0x75F644E97F30A13B,
0xEAF1FF7B5CECA249,
]);
## SemVer Policy
f1600(&mut state);
assert_eq!(state, [
0x2D5C954DF96ECB3C, 0x6A332CD07057B56D, 0x093D8D1270D76B6C, 0x8A20D9B25569D094,
0x4F9C4F99E5E7F156, 0xF957B9A2DA65FB38, 0x85773DAE1275AF0D, 0xFAF4F247C3D810F7,
0x1F1B9EE6F79A8759, 0xE4FECC0FEE98B425, 0x68CE61B6B9CE68A1, 0xDEEA66C4BA8F974F,
0x33C43D836EAFB1F5, 0xE00654042719DBD9, 0x7CF8A9F009831265, 0xFD5449A6BF174743,
0x97DDAD33D8994B40, 0x48EAD5FC5D0BE774, 0xE3B8C8EE55B7B03C, 0x91A0226E649E42E9,
0x900E3129E7BADD7B, 0x202A9EC5FAA3CCE8, 0x5B3402464E1C3DB6, 0x609F4E62A44C1059,
0x20D06CD26A8FBF5C,
]);
});
```
- All on-by-default features of this library are covered by SemVer
- MSRV is considered exempt from SemVer as noted above
## Configuration flags
You can modify crate using the following configuration flags:
- `keccak_backend`: select the specified backend. Supported values:
`aarch64_sha3`, `simd128`, `simd256`, `simd512`, `soft`.
- `keccak_soft_compact`: do not unroll loops in the software backend.
Reduces performance, but results in a more compact binary code.
The flags can be enabled using `RUSTFLAGS` environment variable
(e.g. `RUSTFLAGS="--cfg aes_compact"`) or by modifying `.cargo/config`.
## License

View File

@ -1,45 +1,22 @@
//! keccak benchmarks
#![feature(test)]
#![cfg_attr(keccak_backend = "simd", feature(portable_simd))]
extern crate keccak;
extern crate test;
use keccak::{f200, f400, f800, f1600, p1600};
use core::hint::black_box;
use keccak::Keccak;
macro_rules! impl_bench {
($name:ident, $fn:ident, $type:expr) => {
#[bench]
fn $name(b: &mut test::Bencher) {
let mut data = [$type; 25];
b.iter(|| $fn(&mut data));
let mut data = black_box([0; 25]);
Keccak::new().$fn(|f| b.iter(|| black_box(f(&mut data))))
}
};
}
impl_bench!(b_f200, f200, 0u8);
impl_bench!(b_f400, f400, 0u16);
impl_bench!(b_f800, f800, 0u32);
impl_bench!(b_f1600, f1600, 0u64);
#[bench]
fn b_p1600_24(b: &mut test::Bencher) {
let mut data = [0u64; 25];
b.iter(|| p1600(&mut data, 24));
}
#[bench]
fn b_p1600_16(b: &mut test::Bencher) {
let mut data = [0u64; 25];
b.iter(|| p1600(&mut data, 16));
}
#[cfg(keccak_backend = "simd")]
mod simd {
use keccak::simd::{f1600x2, f1600x4, f1600x8, u64x2, u64x4, u64x8};
impl_bench!(b_f1600x2, f1600x2, u64x2::splat(0));
impl_bench!(b_f1600x4, f1600x4, u64x4::splat(0));
impl_bench!(b_f1600x8, f1600x8, u64x8::splat(0));
}
impl_bench!(keccak_f200, with_f200, 0u8);
impl_bench!(keccak_f400, with_f400, 0u16);
impl_bench!(keccak_f800, with_f800, 0u32);
impl_bench!(keccak_f1600, with_f1600, 0u64);

60
keccak/src/backends.rs Normal file
View File

@ -0,0 +1,60 @@
//! Keccak backend implementations.
use crate::consts::F1600_ROUNDS;
use crate::types::*;
#[cfg(feature = "parallel")]
use hybrid_array::ArraySize;
#[cfg(target_arch = "aarch64")]
pub(crate) mod aarch64_sha3;
#[cfg(any(
keccak_backend = "simd128",
keccak_backend = "simd256",
keccak_backend = "simd512",
))]
pub(crate) mod simd;
pub(crate) mod soft;
/// Trait used to define a closure which operates over Keccak backends.
pub trait BackendClosure {
/// Execute closure with the provided backend.
fn call_once<B: Backend>(self);
}
/// Trait implemented by a Keccak backend.
pub trait Backend {
/// Parallelism width supported by the backend for [`State1600`].
#[cfg(feature = "parallel")]
type ParSize1600: ArraySize;
/// Get scalar `p1600` function with the specified number of rounds.
///
/// # Panics
/// If `ROUNDS` is bigger than [`F1600_ROUNDS`].
#[must_use]
fn get_p1600<const ROUNDS: usize>() -> Fn1600;
/// Get parallel `p1600` function with the specified number of rounds.
///
/// # Panics
/// If `ROUNDS` is bigger than [`F1600_ROUNDS`].
#[cfg(feature = "parallel")]
#[inline]
#[must_use]
fn get_par_p1600<const ROUNDS: usize>() -> ParFn1600<Self> {
|par_state| par_state.iter_mut().for_each(Self::get_p1600::<ROUNDS>())
}
/// Get scalar `f1600` function.
#[must_use]
fn get_f1600() -> Fn1600 {
Self::get_p1600::<F1600_ROUNDS>()
}
/// Get parallel `f1600` function.
#[cfg(feature = "parallel")]
#[inline]
#[must_use]
fn get_par_f1600() -> ParFn1600<Self> {
Self::get_par_p1600::<F1600_ROUNDS>()
}
}

View File

@ -3,8 +3,35 @@
// TODO(tarcieri): remove when MSRV 1.87
#![allow(unsafe_op_in_unsafe_fn)]
use crate::{PLEN, RC};
use crate::consts::{PLEN, RC};
use crate::types::Fn1600;
#[cfg(feature = "parallel")]
use crate::types::ParFn1600;
use core::{arch::aarch64::*, array};
#[cfg(feature = "parallel")]
use hybrid_array::{Array, typenum::U2};
/// AArch64 backend implemented using the SHA3 extension.
pub(crate) struct Backend;
impl super::Backend for Backend {
#[cfg(feature = "parallel")]
type ParSize1600 = U2;
#[inline]
fn get_p1600<const ROUNDS: usize>() -> Fn1600 {
// SAFETY: the backend is used only after required target feature checks
|state| unsafe { p1600_armv8_sha3(state, ROUNDS) }
}
#[cfg(feature = "parallel")]
#[inline]
fn get_par_p1600<const ROUNDS: usize>() -> ParFn1600<Self> {
// SAFETY: the backend is used only after required target feature checks
|Array(state)| unsafe { p1600_armv8_sha3_times2(state, ROUNDS) }
}
}
/// Keccak-p1600 on ARMv8.4-A with `FEAT_SHA3`.
///
@ -12,7 +39,7 @@ use core::{arch::aarch64::*, array};
/// Adapted from the Keccak-f1600 implementation in the XKCP/K12.
/// see <https://github.com/XKCP/K12/blob/df6a21e6d1f34c1aa36e8d702540899c97dba5a0/lib/ARMv8Asha3/KeccakP-1600-ARMv8Asha3.S#L69>
#[target_feature(enable = "sha3")]
pub unsafe fn p1600_armv8_sha3(state: &mut [u64; PLEN], round_count: usize) {
unsafe fn p1600_armv8_sha3(state: &mut [u64; PLEN], round_count: usize) {
let mut s = [*state, Default::default()];
// SAFETY: both functions have the same safety invariants, namely they require the `sha3`
// target feature is available, and the caller is responsible for ensuring support
@ -27,11 +54,10 @@ pub unsafe fn p1600_armv8_sha3(state: &mut [u64; PLEN], round_count: usize) {
///
/// <https://github.com/XKCP/K12/blob/df6a21e/lib/ARMv8Asha3/KeccakP-1600-ARMv8Asha3.S#L69>
#[target_feature(enable = "sha3")]
pub unsafe fn p1600_armv8_sha3_times2(state: &mut [[u64; PLEN]; 2], round_count: usize) {
unsafe fn p1600_armv8_sha3_times2(state: &mut [[u64; PLEN]; 2], round_count: usize) {
assert!(
matches!(round_count, 1..=24),
"invalid round count (must be 1-24): {}",
round_count
round_count <= 24,
"invalid round count greater than 24: {round_count}",
);
let mut s: [uint64x2_t; PLEN] =
@ -142,74 +168,3 @@ unsafe fn chi_iota(t: &[uint64x2_t; 25], rc: u64) -> [uint64x2_t; 25] {
v19, v20, v21, v22, v23, v24,
]
}
#[cfg(all(test, target_feature = "sha3"))]
mod tests {
use super::*;
#[test]
fn test_keccak_f1600() {
// Test vectors are copied from XKCP (eXtended Keccak Code Package)
// https://github.com/XKCP/XKCP/blob/master/tests/TestVectors/KeccakF-1600-IntermediateValues.txt
let state_first = [
0xF1258F7940E1DDE7,
0x84D5CCF933C0478A,
0xD598261EA65AA9EE,
0xBD1547306F80494D,
0x8B284E056253D057,
0xFF97A42D7F8E6FD4,
0x90FEE5A0A44647C4,
0x8C5BDA0CD6192E76,
0xAD30A6F71B19059C,
0x30935AB7D08FFC64,
0xEB5AA93F2317D635,
0xA9A6E6260D712103,
0x81A57C16DBCF555F,
0x43B831CD0347C826,
0x01F22F1A11A5569F,
0x05E5635A21D9AE61,
0x64BEFEF28CC970F2,
0x613670957BC46611,
0xB87C5A554FD00ECB,
0x8C3EE88A1CCF32C8,
0x940C7922AE3A2614,
0x1841F924A2C509E4,
0x16F53526E70465C2,
0x75F644E97F30A13B,
0xEAF1FF7B5CECA249,
];
let state_second = [
0x2D5C954DF96ECB3C,
0x6A332CD07057B56D,
0x093D8D1270D76B6C,
0x8A20D9B25569D094,
0x4F9C4F99E5E7F156,
0xF957B9A2DA65FB38,
0x85773DAE1275AF0D,
0xFAF4F247C3D810F7,
0x1F1B9EE6F79A8759,
0xE4FECC0FEE98B425,
0x68CE61B6B9CE68A1,
0xDEEA66C4BA8F974F,
0x33C43D836EAFB1F5,
0xE00654042719DBD9,
0x7CF8A9F009831265,
0xFD5449A6BF174743,
0x97DDAD33D8994B40,
0x48EAD5FC5D0BE774,
0xE3B8C8EE55B7B03C,
0x91A0226E649E42E9,
0x900E3129E7BADD7B,
0x202A9EC5FAA3CCE8,
0x5B3402464E1C3DB6,
0x609F4E62A44C1059,
0x20D06CD26A8FBF5C,
];
let mut state = [0u64; 25];
unsafe { p1600_armv8_sha3(&mut state, 24) };
assert_eq!(state, state_first);
unsafe { p1600_armv8_sha3(&mut state, 24) };
assert_eq!(state, state_second);
}
}

View File

@ -0,0 +1,59 @@
/// Backend implementation using the portable SIMD API.
use super::soft::{LaneSize, keccak_p};
use crate::types::{Fn1600, ParFn1600};
use core::array;
use hybrid_array::{Array, typenum};
#[cfg(keccak_backend = "simd128")]
use core::simd::u64x2 as u64xN;
#[cfg(keccak_backend = "simd256")]
use core::simd::u64x4 as u64xN;
#[cfg(keccak_backend = "simd512")]
use core::simd::u64x8 as u64xN;
impl LaneSize for u64xN {
const KECCAK_F_ROUND_COUNT: usize = crate::consts::F1600_ROUNDS;
fn truncate_rc(rc: u64) -> Self {
Self::splat(rc)
}
fn rotate_left(self, n: u32) -> Self {
self << Self::splat(n.into()) | self >> Self::splat((64 - n).into())
}
}
/// Portable SIMD backend
pub(crate) struct Backend;
impl super::Backend for Backend {
#[cfg(all(feature = "parallel", keccak_backend = "simd128"))]
type ParSize1600 = typenum::U2;
#[cfg(all(feature = "parallel", keccak_backend = "simd256"))]
type ParSize1600 = typenum::U4;
#[cfg(all(feature = "parallel", keccak_backend = "simd512"))]
type ParSize1600 = typenum::U8;
#[inline]
fn get_p1600<const ROUNDS: usize>() -> Fn1600 {
keccak_p::<u64, ROUNDS>
}
#[cfg(feature = "parallel")]
#[inline]
fn get_par_p1600<const ROUNDS: usize>() -> ParFn1600<Self> {
|Array(state)| {
let mut simd_state = array::from_fn(|i| {
let t = array::from_fn(|j| state[j][i]);
u64xN::from_array(t)
});
keccak_p::<_, ROUNDS>(&mut simd_state);
for i in 0..simd_state.len() {
let s = simd_state[i].to_array();
for j in 0..s.len() {
state[j][i] = s[j];
}
}
}
}
}

177
keccak/src/backends/soft.rs Normal file
View File

@ -0,0 +1,177 @@
use crate::{consts::*, types::Fn1600};
use core::ops::{BitAnd, BitAndAssign, BitXor, BitXorAssign, Not};
#[cfg(feature = "parallel")]
use hybrid_array::typenum::U1;
/// Keccak is a permutation over an array of lanes which comprise the sponge
/// construction.
pub trait LaneSize:
Copy
+ Clone
+ Default
+ PartialEq
+ BitAndAssign
+ BitAnd<Output = Self>
+ BitXorAssign
+ BitXor<Output = Self>
+ Not<Output = Self>
{
/// Number of rounds of the Keccak-f permutation.
const KECCAK_F_ROUND_COUNT: usize;
/// Truncate function.
fn truncate_rc(rc: u64) -> Self;
/// Rotate left function.
#[must_use]
fn rotate_left(self, n: u32) -> Self;
}
macro_rules! impl_lanesize {
($type:ty, $round:expr) => {
impl LaneSize for $type {
const KECCAK_F_ROUND_COUNT: usize = $round;
#[allow(clippy::cast_possible_truncation, trivial_numeric_casts)]
fn truncate_rc(rc: u64) -> Self {
rc as Self
}
fn rotate_left(self, n: u32) -> Self {
self.rotate_left(n)
}
}
};
}
impl_lanesize!(u8, F200_ROUNDS);
impl_lanesize!(u16, F400_ROUNDS);
impl_lanesize!(u32, F800_ROUNDS);
impl_lanesize!(u64, F1600_ROUNDS);
#[rustfmt::skip]
macro_rules! unroll5 {
($var: ident, $body: block) => {
#[cfg(not(keccak_soft_compact))]
{
{ const $var: usize = 0; $body; }
{ const $var: usize = 1; $body; }
{ const $var: usize = 2; $body; }
{ const $var: usize = 3; $body; }
{ const $var: usize = 4; $body; }
}
#[cfg(keccak_soft_compact)]
{
for $var in 0..5 $body
}
};
}
#[rustfmt::skip]
macro_rules! unroll24 {
($var: ident, $body: block) => {
#[cfg(not(keccak_soft_compact))]
{
{ const $var: usize = 0; $body; }
{ const $var: usize = 1; $body; }
{ const $var: usize = 2; $body; }
{ const $var: usize = 3; $body; }
{ const $var: usize = 4; $body; }
{ const $var: usize = 5; $body; }
{ const $var: usize = 6; $body; }
{ const $var: usize = 7; $body; }
{ const $var: usize = 8; $body; }
{ const $var: usize = 9; $body; }
{ const $var: usize = 10; $body; }
{ const $var: usize = 11; $body; }
{ const $var: usize = 12; $body; }
{ const $var: usize = 13; $body; }
{ const $var: usize = 14; $body; }
{ const $var: usize = 15; $body; }
{ const $var: usize = 16; $body; }
{ const $var: usize = 17; $body; }
{ const $var: usize = 18; $body; }
{ const $var: usize = 19; $body; }
{ const $var: usize = 20; $body; }
{ const $var: usize = 21; $body; }
{ const $var: usize = 22; $body; }
{ const $var: usize = 23; $body; }
}
#[cfg(keccak_soft_compact)]
{
for $var in 0..24 $body
}
};
}
/// Generic Keccak-p sponge function.
///
/// # Panics
/// If the `ROUNDS` is greater than `L::KECCAK_F_ROUND_COUNT`.
#[allow(non_upper_case_globals, unused_assignments)]
pub(crate) fn keccak_p<L: LaneSize, const ROUNDS: usize>(state: &mut [L; PLEN]) {
// https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf#page=25
// "the rounds of KECCAK-p[b, nr] match the last rounds of KECCAK-f[b]"
let round_consts = RC[..L::KECCAK_F_ROUND_COUNT]
.last_chunk::<ROUNDS>()
.expect("Number of rounds greater than `KECCAK_F_ROUND_COUNT` is not supported!")
.map(L::truncate_rc);
// Not unrolling this loop results in a much smaller function, plus
// it positively influences performance due to the smaller load on I-cache
for rc in round_consts {
let mut array = [L::default(); 5];
// Theta
unroll5!(x, {
unroll5!(y, {
array[x] ^= state[5 * y + x];
});
});
unroll5!(x, {
let t1 = array[(x + 4) % 5];
let t2 = array[(x + 1) % 5].rotate_left(1);
unroll5!(y, {
state[5 * y + x] ^= t1 ^ t2;
});
});
// Rho and pi
let mut last = state[1];
unroll24!(x, {
array[0] = state[PI[x]];
state[PI[x]] = last.rotate_left(RHO[x]);
last = array[0];
});
// Chi
unroll5!(y_step, {
let y = 5 * y_step;
array.copy_from_slice(&state[y..][..5]);
unroll5!(x, {
let t1 = !array[(x + 1) % 5];
let t2 = array[(x + 2) % 5];
state[y + x] = array[x] ^ (t1 & t2);
});
});
// Iota
state[0] ^= rc;
}
}
/// Default backend based on software implementation.
pub(crate) struct Backend;
impl super::Backend for Backend {
#[cfg(feature = "parallel")]
type ParSize1600 = U1;
#[inline]
fn get_p1600<const ROUNDS: usize>() -> Fn1600 {
keccak_p::<u64, ROUNDS>
}
}

48
keccak/src/consts.rs Normal file
View File

@ -0,0 +1,48 @@
//! Constants used by the Keccak functions.
/// Length of permuted state.
pub const PLEN: usize = 25;
/// Number of rounds used by the `f200` function.
pub const F200_ROUNDS: usize = 18;
/// Number of rounds used by the `f400` function.
pub const F400_ROUNDS: usize = 20;
/// Number of rounds used by the `f800` function.
pub const F800_ROUNDS: usize = 22;
/// Number of rounds used by the `f1600` function.
pub const F1600_ROUNDS: usize = 24;
pub(crate) const RHO: [u32; 24] = [
1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14, 27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44,
];
pub(crate) const PI: [usize; 24] = [
10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4, 15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1,
];
pub(crate) const RC: [u64; 24] = [
0x0000000000000001,
0x0000000000008082,
0x800000000000808a,
0x8000000080008000,
0x000000000000808b,
0x0000000080000001,
0x8000000080008081,
0x8000000000008009,
0x000000000000008a,
0x0000000000000088,
0x0000000080008009,
0x000000008000000a,
0x000000008000808b,
0x800000000000008b,
0x8000000000008089,
0x8000000000008003,
0x8000000000008002,
0x8000000000000080,
0x000000000000800a,
0x800000008000000a,
0x8000000080008081,
0x8000000000008080,
0x0000000080000001,
0x8000000080008008,
];

View File

@ -1,561 +1,158 @@
#![no_std]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![cfg_attr(keccak_backend = "simd", feature(portable_simd))]
#![cfg_attr(
any(
keccak_backend = "simd128",
keccak_backend = "simd256",
keccak_backend = "simd512",
),
feature(portable_simd)
)]
#![doc = include_str!("../README.md")]
#![doc(
html_logo_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg",
html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg"
)]
#![allow(non_upper_case_globals)]
//! ## Usage
//!
//! To disable loop unrolling (e.g. for constrained targets) use `no_unroll` feature.
//!
//! ```
//! // Test vectors are from KeccakCodePackage
//! let mut data = [0u64; 25];
//!
//! keccak::f1600(&mut data);
//! assert_eq!(data, [
//! 0xF1258F7940E1DDE7, 0x84D5CCF933C0478A, 0xD598261EA65AA9EE, 0xBD1547306F80494D,
//! 0x8B284E056253D057, 0xFF97A42D7F8E6FD4, 0x90FEE5A0A44647C4, 0x8C5BDA0CD6192E76,
//! 0xAD30A6F71B19059C, 0x30935AB7D08FFC64, 0xEB5AA93F2317D635, 0xA9A6E6260D712103,
//! 0x81A57C16DBCF555F, 0x43B831CD0347C826, 0x01F22F1A11A5569F, 0x05E5635A21D9AE61,
//! 0x64BEFEF28CC970F2, 0x613670957BC46611, 0xB87C5A554FD00ECB, 0x8C3EE88A1CCF32C8,
//! 0x940C7922AE3A2614, 0x1841F924A2C509E4, 0x16F53526E70465C2, 0x75F644E97F30A13B,
//! 0xEAF1FF7B5CECA249,
//! ]);
//!
//! keccak::f1600(&mut data);
//! assert_eq!(data, [
//! 0x2D5C954DF96ECB3C, 0x6A332CD07057B56D, 0x093D8D1270D76B6C, 0x8A20D9B25569D094,
//! 0x4F9C4F99E5E7F156, 0xF957B9A2DA65FB38, 0x85773DAE1275AF0D, 0xFAF4F247C3D810F7,
//! 0x1F1B9EE6F79A8759, 0xE4FECC0FEE98B425, 0x68CE61B6B9CE68A1, 0xDEEA66C4BA8F974F,
//! 0x33C43D836EAFB1F5, 0xE00654042719DBD9, 0x7CF8A9F009831265, 0xFD5449A6BF174743,
//! 0x97DDAD33D8994B40, 0x48EAD5FC5D0BE774, 0xE3B8C8EE55B7B03C, 0x91A0226E649E42E9,
//! 0x900E3129E7BADD7B, 0x202A9EC5FAA3CCE8, 0x5B3402464E1C3DB6, 0x609F4E62A44C1059,
//! 0x20D06CD26A8FBF5C,
//! ]);
//! ```
use core::{
fmt::Debug,
mem::size_of,
ops::{BitAnd, BitAndAssign, BitXor, BitXorAssign, Not},
};
#[rustfmt::skip]
mod unroll;
#[cfg(target_arch = "aarch64")]
mod armv8;
#[cfg(target_arch = "aarch64")]
cpufeatures::new!(armv8_sha3_intrinsics, "sha3");
const PLEN: usize = 25;
pub mod backends;
pub mod consts;
pub mod types;
const RHO: [u32; 24] = [
1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14, 27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44,
];
pub use backends::*;
pub use consts::*;
pub use types::*;
const PI: [usize; 24] = [
10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4, 15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1,
];
const RC: [u64; 24] = [
0x0000000000000001,
0x0000000000008082,
0x800000000000808a,
0x8000000080008000,
0x000000000000808b,
0x0000000080000001,
0x8000000080008081,
0x8000000000008009,
0x000000000000008a,
0x0000000000000088,
0x0000000080008009,
0x000000008000000a,
0x000000008000808b,
0x800000000000008b,
0x8000000000008089,
0x8000000000008003,
0x8000000000008002,
0x8000000000000080,
0x000000000000800a,
0x800000008000000a,
0x8000000080008081,
0x8000000000008080,
0x0000000080000001,
0x8000000080008008,
];
/// Keccak is a permutation over an array of lanes which comprise the sponge
/// construction.
pub trait LaneSize:
Copy
+ Clone
+ Debug
+ Default
+ PartialEq
+ BitAndAssign
+ BitAnd<Output = Self>
+ BitXorAssign
+ BitXor<Output = Self>
+ Not<Output = Self>
{
/// Number of rounds of the Keccak-f permutation.
const KECCAK_F_ROUND_COUNT: usize;
/// Truncate function.
fn truncate_rc(rc: u64) -> Self;
/// Rotate left function.
#[must_use]
fn rotate_left(self, n: u32) -> Self;
}
macro_rules! impl_lanesize {
($type:ty, $round:expr, $truncate:expr) => {
impl LaneSize for $type {
const KECCAK_F_ROUND_COUNT: usize = $round;
fn truncate_rc(rc: u64) -> Self {
$truncate(rc)
}
fn rotate_left(self, n: u32) -> Self {
self.rotate_left(n)
}
}
};
}
impl_lanesize!(u8, 18, |rc: u64| { rc.to_le_bytes()[0] });
impl_lanesize!(u16, 20, |rc: u64| {
let tmp = rc.to_le_bytes();
#[allow(clippy::unwrap_used)]
Self::from_le_bytes(tmp[..size_of::<Self>()].try_into().unwrap())
});
impl_lanesize!(u32, 22, |rc: u64| {
let tmp = rc.to_le_bytes();
#[allow(clippy::unwrap_used)]
Self::from_le_bytes(tmp[..size_of::<Self>()].try_into().unwrap())
});
impl_lanesize!(u64, 24, |rc: u64| { rc });
macro_rules! impl_keccak {
($pname:ident, $fname:ident, $type:ty) => {
/// Keccak-p sponge function
pub fn $pname(state: &mut [$type; PLEN], round_count: usize) {
keccak_p(state, round_count);
}
/// Keccak-f sponge function
pub fn $fname(state: &mut [$type; PLEN]) {
keccak_p(state, <$type>::KECCAK_F_ROUND_COUNT);
}
};
}
impl_keccak!(p200, f200, u8);
impl_keccak!(p400, f400, u16);
impl_keccak!(p800, f800, u32);
impl_keccak!(p1600, f1600, u64);
/// Keccak permutation with `b=1600` state (i.e. for Keccak-p1600/Keccak-f1600) with optional CPU
/// feature detection support.
#[derive(Clone, Debug)]
pub struct KeccakP1600 {
state: [u64; PLEN],
/// Struct which handles switching between available backends.
#[derive(Debug, Copy, Clone)]
pub struct Keccak {
#[cfg(target_arch = "aarch64")]
has_intrinsics: armv8_sha3_intrinsics::InitToken,
armv8_sha3: armv8_sha3_intrinsics::InitToken,
}
impl KeccakP1600 {
/// Create a new 1600-bit Keccak state from the given input array.
impl Default for Keccak {
#[inline]
#[must_use]
pub fn new(state: [u64; PLEN]) -> Self {
Self {
state,
#[cfg(target_arch = "aarch64")]
has_intrinsics: armv8_sha3_intrinsics::init(),
}
}
/// `Keccak-p[1600, rc]` permutation.
pub fn p1600(&mut self, round_count: usize) {
#[cfg(target_arch = "aarch64")]
if self.has_intrinsics.get() {
// SAFETY: we just performed runtime CPU feature detection above
unsafe { armv8::p1600_armv8_sha3(&mut self.state, round_count) }
return;
}
p1600(&mut self.state, round_count);
}
/// `Keccak-f[1600]` permutation.
pub fn f1600(&mut self) {
#[cfg(target_arch = "aarch64")]
if self.has_intrinsics.get() {
// SAFETY: we just performed runtime CPU feature detection above
unsafe { armv8::p1600_armv8_sha3(&mut self.state, u64::KECCAK_F_ROUND_COUNT) }
return;
}
f1600(&mut self.state);
}
/// Extract the state array.
#[must_use]
pub fn into_inner(self) -> [u64; PLEN] {
self.state
}
}
impl AsRef<[u64; PLEN]> for KeccakP1600 {
#[inline]
fn as_ref(&self) -> &[u64; PLEN] {
&self.state
}
}
impl AsMut<[u64; PLEN]> for KeccakP1600 {
#[inline]
fn as_mut(&mut self) -> &mut [u64; PLEN] {
&mut self.state
}
}
impl Default for KeccakP1600 {
fn default() -> Self {
Self::new(Default::default())
Self {
#[cfg(target_arch = "aarch64")]
armv8_sha3: armv8_sha3_intrinsics::init(),
}
}
}
impl From<[u64; PLEN]> for KeccakP1600 {
impl Keccak {
/// Create new Keccak backend.
#[inline]
fn from(state: [u64; PLEN]) -> Self {
Self::new(state)
#[must_use]
pub fn new() -> Self {
Self::default()
}
}
impl From<&[u64; PLEN]> for KeccakP1600 {
/// Execute the provided backend closure with Keccak backend.
#[inline]
fn from(state: &[u64; PLEN]) -> Self {
Self::new(*state)
}
}
// The auto-detection code will not be reached if `keccak_backend` is set.
#[allow(unreachable_code)]
pub fn with_backend(&self, f: impl BackendClosure) {
cfg_if::cfg_if!(
if #[cfg(any(
keccak_backend = "simd128",
keccak_backend = "simd256",
keccak_backend = "simd512",
))] {
return f.call_once::<simd::Backend>()
} else if #[cfg(keccak_backend = "aarch64_sha3")] {
#[cfg(not(target_arch = "aarch64"))]
compile_error!("aarch64_sha3 backend can be used only on AArch64 targets!");
#[cfg(not(target_feature = "sha3"))]
compile_error!("aarch64_sha3 backend requires sha3 target feature to be enabled!");
impl From<KeccakP1600> for [u64; PLEN] {
#[inline]
fn from(keccak: KeccakP1600) -> Self {
keccak.into_inner()
}
}
#[cfg(keccak_backend = "simd")]
/// SIMD implementations for Keccak-f1600 sponge function
pub mod simd {
use crate::{LaneSize, PLEN, keccak_p};
pub use core::simd::{u64x2, u64x4, u64x8};
macro_rules! impl_lanesize_simd_u64xn {
($type:ty) => {
impl LaneSize for $type {
const KECCAK_F_ROUND_COUNT: usize = 24;
fn truncate_rc(rc: u64) -> Self {
Self::splat(rc)
}
fn rotate_left(self, n: u32) -> Self {
self << Self::splat(n.into()) | self >> Self::splat((64 - n).into())
}
return f.call_once::<aarch64_sha3::Backend>()
} else if #[cfg(keccak_backend = "soft")] {
return f.call_once::<soft::Backend>()
}
};
}
);
impl_lanesize_simd_u64xn!(u64x2);
impl_lanesize_simd_u64xn!(u64x4);
impl_lanesize_simd_u64xn!(u64x8);
impl_keccak!(p1600x2, f1600x2, u64x2);
impl_keccak!(p1600x4, f1600x4, u64x4);
impl_keccak!(p1600x8, f1600x8, u64x8);
}
/// Generic Keccak-p sponge function.
///
/// # Panics
/// If the round count is greater than `L::KECCAK_F_ROUND_COUNT`.
#[allow(unused_assignments)]
pub fn keccak_p<L: LaneSize>(state: &mut [L; PLEN], round_count: usize) {
assert!(
round_count <= L::KECCAK_F_ROUND_COUNT,
"A round_count greater than KECCAK_F_ROUND_COUNT is not supported!"
);
// https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf#page=25
// "the rounds of KECCAK-p[b, nr] match the last rounds of KECCAK-f[b]"
let round_consts = &RC[(L::KECCAK_F_ROUND_COUNT - round_count)..L::KECCAK_F_ROUND_COUNT];
// not unrolling this loop results in a much smaller function, plus
// it positively influences performance due to the smaller load on I-cache
for &rc in round_consts {
let mut array = [L::default(); 5];
// Theta
unroll5!(x, {
unroll5!(y, {
array[x] ^= state[5 * y + x];
});
});
unroll5!(x, {
let t1 = array[(x + 4) % 5];
let t2 = array[(x + 1) % 5].rotate_left(1);
unroll5!(y, {
state[5 * y + x] ^= t1 ^ t2;
});
});
// Rho and pi
let mut last = state[1];
unroll24!(x, {
array[0] = state[PI[x]];
state[PI[x]] = last.rotate_left(RHO[x]);
last = array[0];
});
// Chi
unroll5!(y_step, {
let y = 5 * y_step;
array.copy_from_slice(&state[y..][..5]);
unroll5!(x, {
let t1 = !array[(x + 1) % 5];
let t2 = array[(x + 2) % 5];
state[y + x] = array[x] ^ (t1 & t2);
});
});
// Iota
state[0] ^= L::truncate_rc(rc);
}
}
#[cfg(test)]
mod tests {
use crate::{KeccakP1600, LaneSize, PLEN, keccak_p};
fn keccak_f<L: LaneSize>(state_first: [L; PLEN], state_second: [L; PLEN]) {
let mut state = [L::default(); PLEN];
keccak_p(&mut state, L::KECCAK_F_ROUND_COUNT);
assert_eq!(state, state_first);
keccak_p(&mut state, L::KECCAK_F_ROUND_COUNT);
assert_eq!(state, state_second);
}
#[test]
fn keccak_f200() {
// Test vectors are copied from XKCP (eXtended Keccak Code Package)
// https://github.com/XKCP/XKCP/blob/master/tests/TestVectors/KeccakF-200-IntermediateValues.txt
let state_first = [
0x3C, 0x28, 0x26, 0x84, 0x1C, 0xB3, 0x5C, 0x17, 0x1E, 0xAA, 0xE9, 0xB8, 0x11, 0x13,
0x4C, 0xEA, 0xA3, 0x85, 0x2C, 0x69, 0xD2, 0xC5, 0xAB, 0xAF, 0xEA,
];
let state_second = [
0x1B, 0xEF, 0x68, 0x94, 0x92, 0xA8, 0xA5, 0x43, 0xA5, 0x99, 0x9F, 0xDB, 0x83, 0x4E,
0x31, 0x66, 0xA1, 0x4B, 0xE8, 0x27, 0xD9, 0x50, 0x40, 0x47, 0x9E,
];
keccak_f::<u8>(state_first, state_second);
}
#[test]
fn keccak_f400() {
// Test vectors are copied from XKCP (eXtended Keccak Code Package)
// https://github.com/XKCP/XKCP/blob/master/tests/TestVectors/KeccakF-400-IntermediateValues.txt
let state_first = [
0x09F5, 0x40AC, 0x0FA9, 0x14F5, 0xE89F, 0xECA0, 0x5BD1, 0x7870, 0xEFF0, 0xBF8F, 0x0337,
0x6052, 0xDC75, 0x0EC9, 0xE776, 0x5246, 0x59A1, 0x5D81, 0x6D95, 0x6E14, 0x633E, 0x58EE,
0x71FF, 0x714C, 0xB38E,
];
let state_second = [
0xE537, 0xD5D6, 0xDBE7, 0xAAF3, 0x9BC7, 0xCA7D, 0x86B2, 0xFDEC, 0x692C, 0x4E5B, 0x67B1,
0x15AD, 0xA7F7, 0xA66F, 0x67FF, 0x3F8A, 0x2F99, 0xE2C2, 0x656B, 0x5F31, 0x5BA6, 0xCA29,
0xC224, 0xB85C, 0x097C,
];
keccak_f::<u16>(state_first, state_second);
}
#[test]
fn keccak_f800() {
// Test vectors are copied from XKCP (eXtended Keccak Code Package)
// https://github.com/XKCP/XKCP/blob/master/tests/TestVectors/KeccakF-800-IntermediateValues.txt
let state_first = [
0xE531D45D, 0xF404C6FB, 0x23A0BF99, 0xF1F8452F, 0x51FFD042, 0xE539F578, 0xF00B80A7,
0xAF973664, 0xBF5AF34C, 0x227A2424, 0x88172715, 0x9F685884, 0xB15CD054, 0x1BF4FC0E,
0x6166FA91, 0x1A9E599A, 0xA3970A1F, 0xAB659687, 0xAFAB8D68, 0xE74B1015, 0x34001A98,
0x4119EFF3, 0x930A0E76, 0x87B28070, 0x11EFE996,
];
let state_second = [
0x75BF2D0D, 0x9B610E89, 0xC826AF40, 0x64CD84AB, 0xF905BDD6, 0xBC832835, 0x5F8001B9,
0x15662CCE, 0x8E38C95E, 0x701FE543, 0x1B544380, 0x89ACDEFF, 0x51EDB5DE, 0x0E9702D9,
0x6C19AA16, 0xA2913EEE, 0x60754E9A, 0x9819063C, 0xF4709254, 0xD09F9084, 0x772DA259,
0x1DB35DF7, 0x5AA60162, 0x358825D5, 0xB3783BAB,
];
keccak_f::<u32>(state_first, state_second);
}
#[test]
fn keccak_f1600() {
// Test vectors are copied from XKCP (eXtended Keccak Code Package)
// https://github.com/XKCP/XKCP/blob/master/tests/TestVectors/KeccakF-1600-IntermediateValues.txt
let state_first = [
0xF1258F7940E1DDE7,
0x84D5CCF933C0478A,
0xD598261EA65AA9EE,
0xBD1547306F80494D,
0x8B284E056253D057,
0xFF97A42D7F8E6FD4,
0x90FEE5A0A44647C4,
0x8C5BDA0CD6192E76,
0xAD30A6F71B19059C,
0x30935AB7D08FFC64,
0xEB5AA93F2317D635,
0xA9A6E6260D712103,
0x81A57C16DBCF555F,
0x43B831CD0347C826,
0x01F22F1A11A5569F,
0x05E5635A21D9AE61,
0x64BEFEF28CC970F2,
0x613670957BC46611,
0xB87C5A554FD00ECB,
0x8C3EE88A1CCF32C8,
0x940C7922AE3A2614,
0x1841F924A2C509E4,
0x16F53526E70465C2,
0x75F644E97F30A13B,
0xEAF1FF7B5CECA249,
];
let state_second = [
0x2D5C954DF96ECB3C,
0x6A332CD07057B56D,
0x093D8D1270D76B6C,
0x8A20D9B25569D094,
0x4F9C4F99E5E7F156,
0xF957B9A2DA65FB38,
0x85773DAE1275AF0D,
0xFAF4F247C3D810F7,
0x1F1B9EE6F79A8759,
0xE4FECC0FEE98B425,
0x68CE61B6B9CE68A1,
0xDEEA66C4BA8F974F,
0x33C43D836EAFB1F5,
0xE00654042719DBD9,
0x7CF8A9F009831265,
0xFD5449A6BF174743,
0x97DDAD33D8994B40,
0x48EAD5FC5D0BE774,
0xE3B8C8EE55B7B03C,
0x91A0226E649E42E9,
0x900E3129E7BADD7B,
0x202A9EC5FAA3CCE8,
0x5B3402464E1C3DB6,
0x609F4E62A44C1059,
0x20D06CD26A8FBF5C,
];
keccak_f::<u64>(state_first, state_second);
let mut keccak = KeccakP1600::new(state_first);
keccak.f1600();
assert_eq!(keccak.into_inner(), state_second);
}
#[cfg(keccak_backend = "simd")]
mod simd {
use super::keccak_f;
use core::simd::{u64x2, u64x4, u64x8};
macro_rules! impl_keccak_f1600xn {
($name:ident, $type:ty) => {
#[test]
fn $name() {
// Test vectors are copied from XKCP (eXtended Keccak Code Package)
// https://github.com/XKCP/XKCP/blob/master/tests/TestVectors/KeccakF-1600-IntermediateValues.txt
let state_first = [
<$type>::splat(0xF1258F7940E1DDE7),
<$type>::splat(0x84D5CCF933C0478A),
<$type>::splat(0xD598261EA65AA9EE),
<$type>::splat(0xBD1547306F80494D),
<$type>::splat(0x8B284E056253D057),
<$type>::splat(0xFF97A42D7F8E6FD4),
<$type>::splat(0x90FEE5A0A44647C4),
<$type>::splat(0x8C5BDA0CD6192E76),
<$type>::splat(0xAD30A6F71B19059C),
<$type>::splat(0x30935AB7D08FFC64),
<$type>::splat(0xEB5AA93F2317D635),
<$type>::splat(0xA9A6E6260D712103),
<$type>::splat(0x81A57C16DBCF555F),
<$type>::splat(0x43B831CD0347C826),
<$type>::splat(0x01F22F1A11A5569F),
<$type>::splat(0x05E5635A21D9AE61),
<$type>::splat(0x64BEFEF28CC970F2),
<$type>::splat(0x613670957BC46611),
<$type>::splat(0xB87C5A554FD00ECB),
<$type>::splat(0x8C3EE88A1CCF32C8),
<$type>::splat(0x940C7922AE3A2614),
<$type>::splat(0x1841F924A2C509E4),
<$type>::splat(0x16F53526E70465C2),
<$type>::splat(0x75F644E97F30A13B),
<$type>::splat(0xEAF1FF7B5CECA249),
];
let state_second = [
<$type>::splat(0x2D5C954DF96ECB3C),
<$type>::splat(0x6A332CD07057B56D),
<$type>::splat(0x093D8D1270D76B6C),
<$type>::splat(0x8A20D9B25569D094),
<$type>::splat(0x4F9C4F99E5E7F156),
<$type>::splat(0xF957B9A2DA65FB38),
<$type>::splat(0x85773DAE1275AF0D),
<$type>::splat(0xFAF4F247C3D810F7),
<$type>::splat(0x1F1B9EE6F79A8759),
<$type>::splat(0xE4FECC0FEE98B425),
<$type>::splat(0x68CE61B6B9CE68A1),
<$type>::splat(0xDEEA66C4BA8F974F),
<$type>::splat(0x33C43D836EAFB1F5),
<$type>::splat(0xE00654042719DBD9),
<$type>::splat(0x7CF8A9F009831265),
<$type>::splat(0xFD5449A6BF174743),
<$type>::splat(0x97DDAD33D8994B40),
<$type>::splat(0x48EAD5FC5D0BE774),
<$type>::splat(0xE3B8C8EE55B7B03C),
<$type>::splat(0x91A0226E649E42E9),
<$type>::splat(0x900E3129E7BADD7B),
<$type>::splat(0x202A9EC5FAA3CCE8),
<$type>::splat(0x5B3402464E1C3DB6),
<$type>::splat(0x609F4E62A44C1059),
<$type>::splat(0x20D06CD26A8FBF5C),
];
keccak_f::<$type>(state_first, state_second);
}
};
#[cfg(target_arch = "aarch64")]
if self.armv8_sha3.get() {
#[target_feature(enable = "sha3")]
unsafe fn aarch64_sha3_inner(f: impl BackendClosure) {
f.call_once::<aarch64_sha3::Backend>();
}
// SAFETY: we checked target feature availability above
return unsafe { aarch64_sha3_inner(f) };
}
impl_keccak_f1600xn!(keccak_f1600x2, u64x2);
impl_keccak_f1600xn!(keccak_f1600x4, u64x4);
impl_keccak_f1600xn!(keccak_f1600x8, u64x8);
f.call_once::<soft::Backend>();
}
/// Execute the closure with `f200` function.
#[inline]
pub fn with_f200(&self, f: impl FnOnce(Fn200)) {
self.with_p200::<F200_ROUNDS>(f);
}
/// Execute the closure with `f400` function.
#[inline]
pub fn with_f400(&self, f: impl FnOnce(Fn400)) {
self.with_p400::<F400_ROUNDS>(f);
}
/// Execute the closure with `f800` function.
#[inline]
pub fn with_f800(&self, f: impl FnOnce(Fn800)) {
self.with_p800::<F800_ROUNDS>(f);
}
/// Execute the closure with `f1600` function.
#[inline]
pub fn with_f1600(&self, f: impl FnOnce(Fn1600)) {
self.with_p1600::<F1600_ROUNDS>(f);
}
/// Execute the closure with `p200` function with the specified number of rounds.
///
/// # Panics
/// If `ROUNDS` is bigger than [`F200_ROUNDS`].
#[inline]
pub fn with_p200<const ROUNDS: usize>(&self, f: impl FnOnce(Fn200)) {
f(soft::keccak_p::<u8, ROUNDS>);
}
/// Execute the closure with `p200` function with the specified number of rounds.
///
/// # Panics
/// If `ROUNDS` is bigger than [`F400_ROUNDS`].
#[inline]
pub fn with_p400<const ROUNDS: usize>(&self, f: impl FnOnce(Fn400)) {
f(soft::keccak_p::<u16, ROUNDS>);
}
/// Execute the closure with `p800` function with the specified number of rounds.
///
/// # Panics
/// If `ROUNDS` is bigger than [`F800_ROUNDS`].
#[inline]
pub fn with_p800<const ROUNDS: usize>(&self, f: impl FnOnce(Fn800)) {
f(soft::keccak_p::<u32, ROUNDS>);
}
/// Execute the closure with `p1600` function with the specified number of rounds.
///
/// # Panics
/// If `ROUNDS` is bigger than [`F1600_ROUNDS`].
#[inline]
pub fn with_p1600<const ROUNDS: usize>(&self, f: impl FnOnce(Fn1600)) {
struct Closure<const ROUNDS: usize, F: FnOnce(Fn1600)>(F);
impl<const ROUNDS: usize, F: FnOnce(Fn1600)> BackendClosure for Closure<ROUNDS, F> {
#[inline(always)]
fn call_once<B: Backend>(self) {
(self.0)(B::get_p1600::<ROUNDS>());
}
}
self.with_backend(Closure::<ROUNDS, _>(f));
}
}

35
keccak/src/types.rs Normal file
View File

@ -0,0 +1,35 @@
//! Helper type aliases.
use crate::PLEN;
/// 200-bit Keccak state.
pub type State200 = [u8; PLEN];
/// 400-bit Keccak state.
pub type State400 = [u16; PLEN];
/// 800-bit Keccak state.
pub type State800 = [u32; PLEN];
/// 1600-bit Keccak state.
pub type State1600 = [u64; PLEN];
/// A Keccak function which permutates [`State200`].
pub type Fn200 = fn(&mut State200);
/// A Keccak function which permutates [`State400`].
pub type Fn400 = fn(&mut State400);
/// A Keccak function which permutates [`State800`].
pub type Fn800 = fn(&mut State800);
/// A Keccak function which permutates [`State1600`].
pub type Fn1600 = fn(&mut State1600);
#[cfg(feature = "parallel")]
mod parallel {
use super::State1600;
use crate::Backend;
use hybrid_array::Array;
/// 1600xN-bit state processed in parallel by a [`Backend`] implementation.
pub type ParState1600<B> = Array<State1600, <B as Backend>::ParSize1600>;
/// A Keccak function which permutates [`ParState1600`].
pub type ParFn1600<B> = fn(&mut ParState1600<B>);
}
#[cfg(feature = "parallel")]
pub use parallel::*;

View File

@ -1,62 +0,0 @@
/// unroll5
#[cfg(not(keccak_backend = "soft-compact"))]
#[macro_export]
macro_rules! unroll5 {
($var:ident, $body:block) => {
{ const $var: usize = 0; $body; }
{ const $var: usize = 1; $body; }
{ const $var: usize = 2; $body; }
{ const $var: usize = 3; $body; }
{ const $var: usize = 4; $body; }
};
}
/// unroll5
#[cfg(keccak_backend = "soft-compact")]
#[macro_export]
macro_rules! unroll5 {
($var:ident, $body:block) => {
for $var in 0..5 $body
}
}
/// unroll24
#[cfg(not(keccak_backend = "soft-compact"))]
#[macro_export]
macro_rules! unroll24 {
($var: ident, $body: block) => {
{ const $var: usize = 0; $body; }
{ const $var: usize = 1; $body; }
{ const $var: usize = 2; $body; }
{ const $var: usize = 3; $body; }
{ const $var: usize = 4; $body; }
{ const $var: usize = 5; $body; }
{ const $var: usize = 6; $body; }
{ const $var: usize = 7; $body; }
{ const $var: usize = 8; $body; }
{ const $var: usize = 9; $body; }
{ const $var: usize = 10; $body; }
{ const $var: usize = 11; $body; }
{ const $var: usize = 12; $body; }
{ const $var: usize = 13; $body; }
{ const $var: usize = 14; $body; }
{ const $var: usize = 15; $body; }
{ const $var: usize = 16; $body; }
{ const $var: usize = 17; $body; }
{ const $var: usize = 18; $body; }
{ const $var: usize = 19; $body; }
{ const $var: usize = 20; $body; }
{ const $var: usize = 21; $body; }
{ const $var: usize = 22; $body; }
{ const $var: usize = 23; $body; }
};
}
/// unroll24
#[cfg(keccak_backend = "soft-compact")]
#[macro_export]
macro_rules! unroll24 {
($var:ident, $body:block) => {
for $var in 0..24 $body
}
}

39
keccak/tests/parallel.rs Normal file
View File

@ -0,0 +1,39 @@
//! Tests for the `parallel` crate feature
#![cfg(feature = "parallel")]
use core::array;
use hybrid_array::Array;
use keccak::{Backend, BackendClosure, State1600};
const N: usize = 50;
/// Test that we get the same result for scalar and parallel functions.
fn test_fn<B: Backend>() {
let f1600 = B::get_f1600();
let par_f1600 = B::get_par_f1600();
let mut buf: [State1600; N] = array::from_fn(|i| array::from_fn(|_| i as u64));
let expected: [State1600; N] = buf.map(|mut s| {
f1600(&mut s);
s
});
let (chunks, tail) = Array::slice_as_chunks_mut(&mut buf[..]);
chunks.iter_mut().for_each(par_f1600);
tail.iter_mut().for_each(f1600);
assert_eq!(buf, expected);
}
#[test]
fn keccak_par_f1600() {
struct Closure;
impl BackendClosure for Closure {
fn call_once<B: Backend>(self) {
test_fn::<B>();
}
}
keccak::Keccak::new().with_backend(Closure);
}

140
keccak/tests/xkcp.rs Normal file
View File

@ -0,0 +1,140 @@
//! Test vectors are copied from [XKCP] (eXtended Keccak Code Package).
//!
//! [XKCP]: https://github.com/XKCP/XKCP/blob/master/tests/TestVectors
use keccak::{Keccak, PLEN};
/// Test vector from KeccakF-200-IntermediateValues.txt
#[test]
fn keccak_f200() {
let before = [
0x3C, 0x28, 0x26, 0x84, 0x1C, 0xB3, 0x5C, 0x17, 0x1E, 0xAA, 0xE9, 0xB8, 0x11, 0x13, 0x4C,
0xEA, 0xA3, 0x85, 0x2C, 0x69, 0xD2, 0xC5, 0xAB, 0xAF, 0xEA,
];
let after = [
0x1B, 0xEF, 0x68, 0x94, 0x92, 0xA8, 0xA5, 0x43, 0xA5, 0x99, 0x9F, 0xDB, 0x83, 0x4E, 0x31,
0x66, 0xA1, 0x4B, 0xE8, 0x27, 0xD9, 0x50, 0x40, 0x47, 0x9E,
];
Keccak::new().with_f200(|f200| {
let mut buf = [0; PLEN];
f200(&mut buf);
assert_eq!(buf, before);
f200(&mut buf);
assert_eq!(buf, after);
});
}
/// Test vector from KeccakF-400-IntermediateValues.txt
#[test]
fn keccak_f400() {
let before = [
0x09F5, 0x40AC, 0x0FA9, 0x14F5, 0xE89F, 0xECA0, 0x5BD1, 0x7870, 0xEFF0, 0xBF8F, 0x0337,
0x6052, 0xDC75, 0x0EC9, 0xE776, 0x5246, 0x59A1, 0x5D81, 0x6D95, 0x6E14, 0x633E, 0x58EE,
0x71FF, 0x714C, 0xB38E,
];
let after = [
0xE537, 0xD5D6, 0xDBE7, 0xAAF3, 0x9BC7, 0xCA7D, 0x86B2, 0xFDEC, 0x692C, 0x4E5B, 0x67B1,
0x15AD, 0xA7F7, 0xA66F, 0x67FF, 0x3F8A, 0x2F99, 0xE2C2, 0x656B, 0x5F31, 0x5BA6, 0xCA29,
0xC224, 0xB85C, 0x097C,
];
Keccak::new().with_f400(|f400| {
let mut buf = [0; PLEN];
f400(&mut buf);
assert_eq!(buf, before);
f400(&mut buf);
assert_eq!(buf, after);
});
}
/// Test vector from KeccakF-800-IntermediateValues.txt
#[test]
fn keccak_f800() {
let before = [
0xE531D45D, 0xF404C6FB, 0x23A0BF99, 0xF1F8452F, 0x51FFD042, 0xE539F578, 0xF00B80A7,
0xAF973664, 0xBF5AF34C, 0x227A2424, 0x88172715, 0x9F685884, 0xB15CD054, 0x1BF4FC0E,
0x6166FA91, 0x1A9E599A, 0xA3970A1F, 0xAB659687, 0xAFAB8D68, 0xE74B1015, 0x34001A98,
0x4119EFF3, 0x930A0E76, 0x87B28070, 0x11EFE996,
];
let after = [
0x75BF2D0D, 0x9B610E89, 0xC826AF40, 0x64CD84AB, 0xF905BDD6, 0xBC832835, 0x5F8001B9,
0x15662CCE, 0x8E38C95E, 0x701FE543, 0x1B544380, 0x89ACDEFF, 0x51EDB5DE, 0x0E9702D9,
0x6C19AA16, 0xA2913EEE, 0x60754E9A, 0x9819063C, 0xF4709254, 0xD09F9084, 0x772DA259,
0x1DB35DF7, 0x5AA60162, 0x358825D5, 0xB3783BAB,
];
Keccak::new().with_f800(|f800| {
let mut buf = [0; PLEN];
f800(&mut buf);
assert_eq!(buf, before);
f800(&mut buf);
assert_eq!(buf, after);
});
}
/// Test vector from KeccakF-1600-IntermediateValues.txt
#[test]
fn keccak_f1600() {
let before = [
0xF1258F7940E1DDE7,
0x84D5CCF933C0478A,
0xD598261EA65AA9EE,
0xBD1547306F80494D,
0x8B284E056253D057,
0xFF97A42D7F8E6FD4,
0x90FEE5A0A44647C4,
0x8C5BDA0CD6192E76,
0xAD30A6F71B19059C,
0x30935AB7D08FFC64,
0xEB5AA93F2317D635,
0xA9A6E6260D712103,
0x81A57C16DBCF555F,
0x43B831CD0347C826,
0x01F22F1A11A5569F,
0x05E5635A21D9AE61,
0x64BEFEF28CC970F2,
0x613670957BC46611,
0xB87C5A554FD00ECB,
0x8C3EE88A1CCF32C8,
0x940C7922AE3A2614,
0x1841F924A2C509E4,
0x16F53526E70465C2,
0x75F644E97F30A13B,
0xEAF1FF7B5CECA249,
];
let after = [
0x2D5C954DF96ECB3C,
0x6A332CD07057B56D,
0x093D8D1270D76B6C,
0x8A20D9B25569D094,
0x4F9C4F99E5E7F156,
0xF957B9A2DA65FB38,
0x85773DAE1275AF0D,
0xFAF4F247C3D810F7,
0x1F1B9EE6F79A8759,
0xE4FECC0FEE98B425,
0x68CE61B6B9CE68A1,
0xDEEA66C4BA8F974F,
0x33C43D836EAFB1F5,
0xE00654042719DBD9,
0x7CF8A9F009831265,
0xFD5449A6BF174743,
0x97DDAD33D8994B40,
0x48EAD5FC5D0BE774,
0xE3B8C8EE55B7B03C,
0x91A0226E649E42E9,
0x900E3129E7BADD7B,
0x202A9EC5FAA3CCE8,
0x5B3402464E1C3DB6,
0x609F4E62A44C1059,
0x20D06CD26A8FBF5C,
];
Keccak::new().with_f1600(|f1600| {
let mut buf = [0; PLEN];
f1600(&mut buf);
assert_eq!(buf, before);
f1600(&mut buf);
assert_eq!(buf, after);
});
}