mirror of
https://github.com/logos-blockchain/sponges.git
synced 2026-07-30 02:23:31 +00:00
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:
parent
806d446c41
commit
1efeff7d02
49
.github/workflows/keccak.yml
vendored
49
.github/workflows/keccak.yml
vendored
@ -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
27
Cargo.lock
generated
@ -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"
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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
60
keccak/src/backends.rs
Normal 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>()
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
59
keccak/src/backends/simd.rs
Normal file
59
keccak/src/backends/simd.rs
Normal 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
177
keccak/src/backends/soft.rs
Normal 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
48
keccak/src/consts.rs
Normal 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,
|
||||
];
|
||||
@ -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
35
keccak/src/types.rs
Normal 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::*;
|
||||
@ -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
39
keccak/tests/parallel.rs
Normal 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
140
keccak/tests/xkcp.rs
Normal 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);
|
||||
});
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user