Skip to main content

lib_q_keccak/
lib.rs

1//! Pure Rust implementation of the Keccak [sponge function](https://en.wikipedia.org/wiki/Sponge_function).
2//!
3//! This crate provides low-level Keccak permutation functions (keccak-f and keccak-p variants).
4//! For high-level SHA-3 hash functions, see [`lib-q-sha3`](https://docs.rs/lib-q-sha3).
5//!
6//! ## Features
7//!
8//! - **no_std compatible**: Works in embedded and WASM environments
9//! - **Optimized implementations**: Platform-specific optimizations for ARM64 and x86_64
10//! - **SIMD support**: Parallel processing with portable SIMD
11//! - **Multi-threading**: Concurrent state processing for high-performance applications
12//! - **WebAssembly**: Full WASM support with JavaScript interop
13//!
14//! ## Example
15//!
16//! ```
17//! // Test vectors are from KeccakCodePackage
18//! let mut data = [0u64; 25];
19//!
20//! lib_q_keccak::f1600(&mut data);
21//! ```
22//!
23//! ## Configuration
24//!
25//! To disable loop unrolling (e.g. for constraint targets) use the `no_unroll` feature.
26
27#![cfg_attr(keccak_portable_simd, feature(portable_simd))]
28#![doc(
29    html_logo_url = "https://raw.githubusercontent.com/Enkom-Tech/libQ/main/docs/logo.svg",
30    html_favicon_url = "https://raw.githubusercontent.com/Enkom-Tech/libQ/main/docs/logo.svg"
31)]
32#![allow(non_upper_case_globals)]
33#![warn(
34    clippy::mod_module_files,
35    clippy::unwrap_used,
36    missing_docs,
37    rust_2018_idioms,
38    unused_lifetimes,
39    unused_qualifications
40)]
41#![cfg_attr(not(feature = "std"), no_std)]
42
43// Conditional externs based on features
44#[cfg(feature = "std")]
45extern crate std;
46
47/// # Examples
48///
49/// ```
50/// // Test vectors are from KeccakCodePackage
51/// let mut data = [0u64; 25];
52///
53/// lib_q_keccak::f1600(&mut data);
54/// assert_eq!(
55///     data,
56///     [
57///         0xF1258F7940E1DDE7,
58///         0x84D5CCF933C0478A,
59///         0xD598261EA65AA9EE,
60///         0xBD1547306F80494D,
61///         0x8B284E056253D057,
62///         0xFF97A42D7F8E6FD4,
63///         0x90FEE5A0A44647C4,
64///         0x8C5BDA0CD6192E76,
65///         0xAD30A6F71B19059C,
66///         0x30935AB7D08FFC64,
67///         0xEB5AA93F2317D635,
68///         0xA9A6E6260D712103,
69///         0x81A57C16DBCF555F,
70///         0x43B831CD0347C826,
71///         0x01F22F1A11A5569F,
72///         0x05E5635A21D9AE61,
73///         0x64BEFEF28CC970F2,
74///         0x613670957BC46611,
75///         0xB87C5A554FD00ECB,
76///         0x8C3EE88A1CCF32C8,
77///         0x940C7922AE3A2614,
78///         0x1841F924A2C509E4,
79///         0x16F53526E70465C2,
80///         0x75F644E97F30A13B,
81///         0xEAF1FF7B5CECA249,
82///     ]
83/// );
84/// ```
85use core::fmt::Debug;
86use core::ops::{
87    BitAnd,
88    BitAndAssign,
89    BitXor,
90    BitXorAssign,
91    Not,
92};
93
94#[rustfmt::skip]
95mod unroll;
96
97// ARM64 optimizations are disabled by default to prevent cross-compilation linking issues
98// Enable with --features arm64_sha3 only when building natively on ARM64 hardware
99#[cfg(all(
100    target_arch = "aarch64",
101    feature = "asm",
102    not(target_os = "windows"), // Exclude Windows ARM64 due to different ABI
103    feature = "std",
104    feature = "arm64_sha3" // Require explicit opt-in to avoid cross-compilation issues
105))]
106mod armv8;
107
108#[cfg(all(
109    target_arch = "aarch64",
110    feature = "asm",
111    not(target_os = "windows"), // Exclude Windows ARM64 due to different ABI
112    feature = "std",
113    feature = "arm64_sha3" // Require explicit opt-in to avoid cross-compilation issues
114))]
115#[inline]
116fn armv8_sha3_runtime_available() -> bool {
117    std::arch::is_aarch64_feature_detected!("sha3")
118}
119
120#[cfg(all(target_arch = "x86_64", feature = "asm"))]
121mod x86;
122
123// Stable AVX2 batched (×4) permutation, built on `core::arch` intrinsics (not
124// inline asm, so it is independent of the `asm` feature). It is compiled when
125// AVX2 is either a compile-time guarantee (`target_feature = "avx2"`, e.g.
126// `-C target-cpu=native`) or selectable at runtime (`std`); otherwise `p1600x4`
127// uses the scalar fallback.
128#[cfg(all(
129    target_arch = "x86_64",
130    not(cross_compile),
131    any(target_feature = "avx2", feature = "std")
132))]
133mod x86_simd;
134
135// Stable AVX-512 batched (×8) permutation — the 8-wide sibling of `x86_simd`. Same
136// compile-time-or-runtime gating, keyed on `avx512f` instead of `avx2`.
137#[cfg(all(
138    target_arch = "x86_64",
139    not(cross_compile),
140    any(target_feature = "avx512f", feature = "std")
141))]
142mod x86_simd_avx512;
143
144#[cfg(all(feature = "simd", keccak_portable_simd))]
145mod advanced_simd;
146
147mod features;
148mod optimized_core;
149
150#[cfg(all(feature = "multithreading", feature = "std"))]
151mod multithreading;
152
153const PLEN: usize = 25;
154
155const RHO: [u32; 24] = [
156    1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14, 27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44,
157];
158
159const PI: [usize; 24] = [
160    10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4, 15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1,
161];
162
163// Keccak round constants - keep as-is for cryptographic correctness
164#[allow(clippy::unreadable_literal)]
165const RC: [u64; 24] = [
166    0x0000000000000001,
167    0x0000000000008082,
168    0x800000000000808A,
169    0x8000000080008000,
170    0x000000000000808B,
171    0x0000000080000001,
172    0x8000000080008081,
173    0x8000000000008009,
174    0x000000000000008A,
175    0x0000000000000088,
176    0x0000000080008009,
177    0x000000008000000A,
178    0x000000008000808B,
179    0x800000000000008B,
180    0x8000000000008089,
181    0x8000000000008003,
182    0x8000000000008002,
183    0x8000000000000080,
184    0x000000000000800A,
185    0x800000008000000A,
186    0x8000000080008081,
187    0x8000000000008080,
188    0x0000000080000001,
189    0x8000000080008008,
190];
191
192/// Keccak is a permutation over an array of lanes which comprise the sponge
193/// construction.
194pub trait LaneSize:
195    Copy
196    + Clone
197    + Debug
198    + Default
199    + PartialEq
200    + BitAndAssign
201    + BitAnd<Output = Self>
202    + BitXorAssign
203    + BitXor<Output = Self>
204    + Not<Output = Self>
205{
206    /// Number of rounds of the Keccak-f permutation.
207    const KECCAK_F_ROUND_COUNT: usize;
208
209    /// Truncate function.
210    fn truncate_rc(rc: u64) -> Self;
211
212    /// Rotate left function.
213    fn rotate_left(self, n: u32) -> Self;
214}
215
216macro_rules! impl_lanesize {
217    ($type:ty, $round:expr, $truncate:expr) => {
218        impl LaneSize for $type {
219            const KECCAK_F_ROUND_COUNT: usize = $round;
220
221            fn truncate_rc(rc: u64) -> Self {
222                $truncate(rc)
223            }
224
225            fn rotate_left(self, n: u32) -> Self {
226                self.rotate_left(n)
227            }
228        }
229    };
230}
231
232impl_lanesize!(u8, 18, |rc: u64| { rc.to_le_bytes()[0] });
233impl_lanesize!(u16, 20, |rc: u64| {
234    let tmp = rc.to_le_bytes();
235    // Safe conversion: size_of::<u16>() = 2, and we're taking first 2 bytes
236    let bytes = [tmp[0], tmp[1]];
237    Self::from_le_bytes(bytes)
238});
239impl_lanesize!(u32, 22, |rc: u64| {
240    let tmp = rc.to_le_bytes();
241    // Safe conversion: size_of::<u32>() = 4, and we're taking first 4 bytes
242    let bytes = [tmp[0], tmp[1], tmp[2], tmp[3]];
243    Self::from_le_bytes(bytes)
244});
245impl_lanesize!(u64, 24, |rc: u64| { rc });
246
247macro_rules! impl_keccak {
248    ($pname:ident, $fname:ident, $type:ty) => {
249        /// Keccak-p sponge function
250        pub fn $pname(state: &mut [$type; PLEN], round_count: usize) {
251            keccak_p(state, round_count);
252        }
253
254        /// Keccak-f sponge function
255        pub fn $fname(state: &mut [$type; PLEN]) {
256            keccak_p(state, <$type>::KECCAK_F_ROUND_COUNT);
257        }
258    };
259}
260
261impl_keccak!(p200, f200, u8);
262impl_keccak!(p400, f400, u16);
263impl_keccak!(p800, f800, u32);
264
265// Fallback: use generic Keccak-p/f when ARM64 SHA3 optimizations are not active.
266// When ARM64 SHA3 IS active, the manual `p1600`/`f1600` below handle runtime dispatch.
267#[cfg(not(all(
268    target_arch = "aarch64",
269    feature = "asm",
270    not(target_os = "windows"),
271    feature = "std",
272    feature = "arm64_sha3"
273)))]
274impl_keccak!(p1600, f1600, u64);
275
276/// Keccak-p[1600, rc] permutation.
277#[cfg(all(
278    target_arch = "aarch64",
279    feature = "asm",
280    not(target_os = "windows"),
281    feature = "std",
282    feature = "arm64_sha3"
283))]
284pub fn p1600(state: &mut [u64; PLEN], round_count: usize) {
285    if armv8_sha3_runtime_available() {
286        unsafe { armv8::p1600_armv8_sha3_asm(state, round_count) }
287    } else {
288        keccak_p(state, round_count);
289    }
290}
291
292/// Keccak-f\[1600\] permutation.
293#[cfg(all(
294    target_arch = "aarch64",
295    feature = "asm",
296    not(target_os = "windows"),
297    feature = "std",
298    feature = "arm64_sha3"
299))]
300pub fn f1600(state: &mut [u64; PLEN]) {
301    if armv8_sha3_runtime_available() {
302        unsafe { armv8::p1600_armv8_sha3_asm(state, 24) }
303    } else {
304        keccak_p(state, u64::KECCAK_F_ROUND_COUNT);
305    }
306}
307
308/// Apply Keccak-p\[1600, `round_count`\] to **four independent states** at once.
309///
310/// On x86_64 with the `asm` and `std` features this uses a single AVX2 register
311/// to drive all four states in parallel when the CPU supports `avx2` (detected at
312/// runtime), which is substantially faster than four separate permutations for
313/// batchable workloads (tree hashing such as KangarooTwelve, parallel SHAKE in
314/// hash-based signatures, lattice sampling). On any other target — or when AVX2
315/// is absent — it falls back to four scalar [`p1600`] calls, so the result is
316/// identical everywhere.
317///
318/// `round_count` follows the same convention as [`p1600`]: e.g. `24` for
319/// Keccak-f\[1600\], `12` for the TurboSHAKE/K12 reduced-round permutation.
320// The compile-time-AVX2 branch's `return` mirrors the runtime branch for symmetry/readability
321// and is only flagged `needless` in the all-compile-time-SIMD cfg (where the scalar fallback below
322// is `cfg`-excluded, making it the last statement). Suppress rather than break that symmetry.
323#[allow(clippy::needless_return)]
324pub fn p1600x4(states: &mut [[u64; PLEN]; 4], round_count: usize) {
325    // Compile-time AVX2 (e.g. `-C target-cpu=native`): sound without any runtime
326    // check, and no `std` required.
327    #[cfg(all(target_arch = "x86_64", target_feature = "avx2", not(cross_compile)))]
328    {
329        // SAFETY: the target is compiled with AVX2 enabled.
330        unsafe { x86_simd::p1600x4_avx2(states, round_count) };
331        return;
332    }
333
334    // Otherwise, detect AVX2 at runtime when `std` is available.
335    #[cfg(all(
336        target_arch = "x86_64",
337        feature = "std",
338        not(target_feature = "avx2"),
339        not(cross_compile)
340    ))]
341    {
342        if std::arch::is_x86_feature_detected!("avx2") {
343            // SAFETY: `avx2` was just confirmed available at runtime.
344            unsafe { x86_simd::p1600x4_avx2(states, round_count) };
345            return;
346        }
347    }
348
349    // Scalar fallback (omitted when AVX2 is a compile-time guarantee, where the
350    // first branch already returned).
351    #[cfg(not(all(target_arch = "x86_64", target_feature = "avx2", not(cross_compile))))]
352    for state in states.iter_mut() {
353        p1600(state, round_count);
354    }
355}
356
357/// Apply Keccak-p\[1600, `round_count`\] to **eight independent states** at once.
358///
359/// The 8-wide AVX-512 counterpart of [`p1600x4`]: on x86_64 it drives all eight
360/// states through one `__m512i` when the CPU supports `avx512f` (a compile-time
361/// guarantee via `target_feature`, or detected at runtime under `std`). On any other
362/// target — or when AVX-512 is absent — it falls back to eight scalar [`p1600`]
363/// calls, so the result is identical everywhere.
364///
365/// `round_count` follows the same convention as [`p1600`] (`24` for Keccak-f, `12`
366/// for the TurboSHAKE/K12 reduced-round permutation).
367///
368/// AVX-512 is absent on many consumer CPUs (and all AMD Zen 1–3). The batched XOF
369/// helpers therefore default to [`p1600x4`]; reach for `p1600x8` only where AVX-512
370/// is expected and has been validated on the target hardware.
371// See `p1600x4`: the compile-time-AVX-512 branch's `return` is only `needless` in the
372// all-compile-time-SIMD cfg (scalar fallback `cfg`-excluded); suppressed for symmetry/readability.
373#[allow(clippy::needless_return)]
374pub fn p1600x8(states: &mut [[u64; PLEN]; 8], round_count: usize) {
375    // Compile-time AVX-512 (e.g. `-C target-cpu=native` on an AVX-512 host).
376    #[cfg(all(target_arch = "x86_64", target_feature = "avx512f", not(cross_compile)))]
377    {
378        // SAFETY: the target is compiled with AVX-512F enabled.
379        unsafe { x86_simd_avx512::p1600x8_avx512(states, round_count) };
380        return;
381    }
382
383    // Otherwise, detect AVX-512F at runtime when `std` is available.
384    #[cfg(all(
385        target_arch = "x86_64",
386        feature = "std",
387        not(target_feature = "avx512f"),
388        not(cross_compile)
389    ))]
390    {
391        if std::arch::is_x86_feature_detected!("avx512f") {
392            // SAFETY: `avx512f` was just confirmed available at runtime.
393            unsafe { x86_simd_avx512::p1600x8_avx512(states, round_count) };
394            return;
395        }
396    }
397
398    // Scalar fallback (omitted when AVX-512 is a compile-time guarantee).
399    #[cfg(not(all(target_arch = "x86_64", target_feature = "avx512f", not(cross_compile))))]
400    for state in states.iter_mut() {
401        p1600(state, round_count);
402    }
403}
404
405#[cfg(all(feature = "simd", keccak_portable_simd))]
406/// SIMD implementations for Keccak-f1600 sponge function
407pub mod simd {
408    pub use core::simd::{
409        u64x2,
410        u64x4,
411        u64x8,
412    };
413
414    use crate::{
415        LaneSize,
416        PLEN,
417        keccak_p,
418    };
419
420    macro_rules! impl_lanesize_simd_u64xn {
421        ($type:ty) => {
422            impl LaneSize for $type {
423                const KECCAK_F_ROUND_COUNT: usize = 24;
424
425                fn truncate_rc(rc: u64) -> Self {
426                    Self::splat(rc)
427                }
428
429                fn rotate_left(self, n: u32) -> Self {
430                    self << Self::splat(n.into()) | self >> Self::splat((64 - n).into())
431                }
432            }
433        };
434    }
435
436    impl_lanesize_simd_u64xn!(u64x2);
437    impl_lanesize_simd_u64xn!(u64x4);
438    impl_lanesize_simd_u64xn!(u64x8);
439
440    impl_keccak!(p1600x2, f1600x2, u64x2);
441    impl_keccak!(p1600x4, f1600x4, u64x4);
442    impl_keccak!(p1600x8, f1600x8, u64x8);
443}
444
445#[cfg(all(feature = "simd", keccak_portable_simd))]
446/// Advanced SIMD optimizations using nightly features
447pub mod advanced {
448    pub use super::advanced_simd::*;
449}
450
451#[allow(unused_assignments)]
452/// Generic Keccak-p sponge function
453pub fn keccak_p<L: LaneSize>(state: &mut [L; PLEN], round_count: usize) {
454    // Safety: round_count should never exceed KECCAK_F_ROUND_COUNT in practice
455    // All callers use valid round counts (typically 12, 24, etc.)
456    if round_count > L::KECCAK_F_ROUND_COUNT {
457        // This should never happen in practice, but if it does, we skip the operation
458        return;
459    }
460
461    // https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf#page=25
462    // "the rounds of KECCAK-p[b, nr] match the last rounds of KECCAK-f[b]"
463    let round_consts = &RC[(L::KECCAK_F_ROUND_COUNT - round_count)..L::KECCAK_F_ROUND_COUNT];
464
465    // not unrolling this loop results in a much smaller function, plus
466    // it positively influences performance due to the smaller load on I-cache
467    for &rc in round_consts {
468        let mut array = [L::default(); 5];
469
470        // Theta
471        unroll5!(x, {
472            unroll5!(y, {
473                array[x] ^= state[5 * y + x];
474            });
475        });
476
477        unroll5!(x, {
478            let t1 = array[(x + 4) % 5];
479            let t2 = array[(x + 1) % 5].rotate_left(1);
480            unroll5!(y, {
481                state[5 * y + x] ^= t1 ^ t2;
482            });
483        });
484
485        // Rho and pi
486        let mut last = state[1];
487        unroll24!(x, {
488            array[0] = state[PI[x]];
489            state[PI[x]] = last.rotate_left(RHO[x]);
490            last = array[0];
491        });
492
493        // Chi
494        unroll5!(y_step, {
495            let y = 5 * y_step;
496
497            array.copy_from_slice(&state[y..][..5]);
498
499            unroll5!(x, {
500                let t1 = !array[(x + 1) % 5];
501                let t2 = array[(x + 2) % 5];
502                state[y + x] = array[x] ^ (t1 & t2);
503            });
504        });
505
506        // Iota
507        state[0] ^= L::truncate_rc(rc);
508    }
509}
510
511// Re-export optimized functions
512// Re-export feature configuration
513#[cfg(all(feature = "simd", keccak_portable_simd))]
514pub use crate::advanced_simd::{
515    AdvancedLaneSize,
516    SimdConfig,
517    SimdSecurityValidator,
518    parallel as simd_parallel,
519};
520pub use crate::features::{
521    FeatureConfig,
522    FeatureReport,
523    detection,
524    get_global_config,
525    reset_global_config,
526    set_global_config,
527};
528// Re-export multi-threading functionality
529#[cfg(all(feature = "multithreading", feature = "std"))]
530pub use crate::multithreading::{
531    AffinityStrategy,
532    CryptoThreadPool,
533    ThreadingConfig,
534    WorkerStats,
535    get_global_thread_pool,
536    init_global_thread_pool,
537    process_keccak_states_global,
538};
539#[cfg(feature = "simd")]
540pub use crate::optimized_core::parallel;
541#[cfg(all(feature = "multithreading", feature = "std", feature = "simd"))]
542pub use crate::optimized_core::parallel::p1600_multithreaded;
543pub use crate::optimized_core::{
544    OptimizationLevel,
545    fast_loop_absorb_optimized,
546    p1600_optimized,
547};
548
549#[cfg(test)]
550#[allow(clippy::unreadable_literal)] // Test vectors should remain as-is
551mod tests {
552    use crate::{
553        LaneSize,
554        PLEN,
555        keccak_p,
556    };
557
558    fn keccak_f<L: LaneSize>(state_first: [L; PLEN], state_second: [L; PLEN]) {
559        let mut state = [L::default(); PLEN];
560
561        keccak_p(&mut state, L::KECCAK_F_ROUND_COUNT);
562        assert_eq!(state, state_first);
563
564        keccak_p(&mut state, L::KECCAK_F_ROUND_COUNT);
565        assert_eq!(state, state_second);
566    }
567
568    #[test]
569    fn keccak_f200() {
570        // Test vectors are copied from XKCP (eXtended Keccak Code Package)
571        // https://github.com/XKCP/XKCP/blob/master/tests/TestVectors/KeccakF-200-IntermediateValues.txt
572        let state_first = [
573            0x3C, 0x28, 0x26, 0x84, 0x1C, 0xB3, 0x5C, 0x17, 0x1E, 0xAA, 0xE9, 0xB8, 0x11, 0x13,
574            0x4C, 0xEA, 0xA3, 0x85, 0x2C, 0x69, 0xD2, 0xC5, 0xAB, 0xAF, 0xEA,
575        ];
576        let state_second = [
577            0x1B, 0xEF, 0x68, 0x94, 0x92, 0xA8, 0xA5, 0x43, 0xA5, 0x99, 0x9F, 0xDB, 0x83, 0x4E,
578            0x31, 0x66, 0xA1, 0x4B, 0xE8, 0x27, 0xD9, 0x50, 0x40, 0x47, 0x9E,
579        ];
580
581        keccak_f::<u8>(state_first, state_second);
582    }
583
584    #[test]
585    fn keccak_f400() {
586        // Test vectors are copied from XKCP (eXtended Keccak Code Package)
587        // https://github.com/XKCP/XKCP/blob/master/tests/TestVectors/KeccakF-400-IntermediateValues.txt
588        let state_first = [
589            0x09F5, 0x40AC, 0x0FA9, 0x14F5, 0xE89F, 0xECA0, 0x5BD1, 0x7870, 0xEFF0, 0xBF8F, 0x0337,
590            0x6052, 0xDC75, 0x0EC9, 0xE776, 0x5246, 0x59A1, 0x5D81, 0x6D95, 0x6E14, 0x633E, 0x58EE,
591            0x71FF, 0x714C, 0xB38E,
592        ];
593        let state_second = [
594            0xE537, 0xD5D6, 0xDBE7, 0xAAF3, 0x9BC7, 0xCA7D, 0x86B2, 0xFDEC, 0x692C, 0x4E5B, 0x67B1,
595            0x15AD, 0xA7F7, 0xA66F, 0x67FF, 0x3F8A, 0x2F99, 0xE2C2, 0x656B, 0x5F31, 0x5BA6, 0xCA29,
596            0xC224, 0xB85C, 0x097C,
597        ];
598
599        keccak_f::<u16>(state_first, state_second);
600    }
601
602    #[test]
603    fn keccak_f800() {
604        // Test vectors are copied from XKCP (eXtended Keccak Code Package)
605        // https://github.com/XKCP/XKCP/blob/master/tests/TestVectors/KeccakF-800-IntermediateValues.txt
606        let state_first = [
607            0xE531D45D, 0xF404C6FB, 0x23A0BF99, 0xF1F8452F, 0x51FFD042, 0xE539F578, 0xF00B80A7,
608            0xAF973664, 0xBF5AF34C, 0x227A2424, 0x88172715, 0x9F685884, 0xB15CD054, 0x1BF4FC0E,
609            0x6166FA91, 0x1A9E599A, 0xA3970A1F, 0xAB659687, 0xAFAB8D68, 0xE74B1015, 0x34001A98,
610            0x4119EFF3, 0x930A0E76, 0x87B28070, 0x11EFE996,
611        ];
612        let state_second = [
613            0x75BF2D0D, 0x9B610E89, 0xC826AF40, 0x64CD84AB, 0xF905BDD6, 0xBC832835, 0x5F8001B9,
614            0x15662CCE, 0x8E38C95E, 0x701FE543, 0x1B544380, 0x89ACDEFF, 0x51EDB5DE, 0x0E9702D9,
615            0x6C19AA16, 0xA2913EEE, 0x60754E9A, 0x9819063C, 0xF4709254, 0xD09F9084, 0x772DA259,
616            0x1DB35DF7, 0x5AA60162, 0x358825D5, 0xB3783BAB,
617        ];
618
619        keccak_f::<u32>(state_first, state_second);
620    }
621
622    #[test]
623    fn keccak_f1600() {
624        // Test vectors are copied from XKCP (eXtended Keccak Code Package)
625        // https://github.com/XKCP/XKCP/blob/master/tests/TestVectors/KeccakF-1600-IntermediateValues.txt
626        let state_first = [
627            0xF1258F7940E1DDE7,
628            0x84D5CCF933C0478A,
629            0xD598261EA65AA9EE,
630            0xBD1547306F80494D,
631            0x8B284E056253D057,
632            0xFF97A42D7F8E6FD4,
633            0x90FEE5A0A44647C4,
634            0x8C5BDA0CD6192E76,
635            0xAD30A6F71B19059C,
636            0x30935AB7D08FFC64,
637            0xEB5AA93F2317D635,
638            0xA9A6E6260D712103,
639            0x81A57C16DBCF555F,
640            0x43B831CD0347C826,
641            0x01F22F1A11A5569F,
642            0x05E5635A21D9AE61,
643            0x64BEFEF28CC970F2,
644            0x613670957BC46611,
645            0xB87C5A554FD00ECB,
646            0x8C3EE88A1CCF32C8,
647            0x940C7922AE3A2614,
648            0x1841F924A2C509E4,
649            0x16F53526E70465C2,
650            0x75F644E97F30A13B,
651            0xEAF1FF7B5CECA249,
652        ];
653        let state_second = [
654            0x2D5C954DF96ECB3C,
655            0x6A332CD07057B56D,
656            0x093D8D1270D76B6C,
657            0x8A20D9B25569D094,
658            0x4F9C4F99E5E7F156,
659            0xF957B9A2DA65FB38,
660            0x85773DAE1275AF0D,
661            0xFAF4F247C3D810F7,
662            0x1F1B9EE6F79A8759,
663            0xE4FECC0FEE98B425,
664            0x68CE61B6B9CE68A1,
665            0xDEEA66C4BA8F974F,
666            0x33C43D836EAFB1F5,
667            0xE00654042719DBD9,
668            0x7CF8A9F009831265,
669            0xFD5449A6BF174743,
670            0x97DDAD33D8994B40,
671            0x48EAD5FC5D0BE774,
672            0xE3B8C8EE55B7B03C,
673            0x91A0226E649E42E9,
674            0x900E3129E7BADD7B,
675            0x202A9EC5FAA3CCE8,
676            0x5B3402464E1C3DB6,
677            0x609F4E62A44C1059,
678            0x20D06CD26A8FBF5C,
679        ];
680
681        keccak_f::<u64>(state_first, state_second);
682    }
683
684    #[cfg(all(test, feature = "simd", keccak_portable_simd))]
685    mod test_simd {
686        use core::simd::{
687            u64x2,
688            u64x4,
689            u64x8,
690        };
691
692        use crate::tests::keccak_f;
693
694        macro_rules! impl_keccak_f1600xn {
695            ($name:ident, $type:ty) => {
696                #[test]
697                fn $name() {
698                    // Test vectors are copied from XKCP (eXtended Keccak Code Package)
699                    // https://github.com/XKCP/XKCP/blob/master/tests/TestVectors/KeccakF-1600-IntermediateValues.txt
700                    let state_first = [
701                        <$type>::splat(0xF1258F7940E1DDE7),
702                        <$type>::splat(0x84D5CCF933C0478A),
703                        <$type>::splat(0xD598261EA65AA9EE),
704                        <$type>::splat(0xBD1547306F80494D),
705                        <$type>::splat(0x8B284E056253D057),
706                        <$type>::splat(0xFF97A42D7F8E6FD4),
707                        <$type>::splat(0x90FEE5A0A44647C4),
708                        <$type>::splat(0x8C5BDA0CD6192E76),
709                        <$type>::splat(0xAD30A6F71B19059C),
710                        <$type>::splat(0x30935AB7D08FFC64),
711                        <$type>::splat(0xEB5AA93F2317D635),
712                        <$type>::splat(0xA9A6E6260D712103),
713                        <$type>::splat(0x81A57C16DBCF555F),
714                        <$type>::splat(0x43B831CD0347C826),
715                        <$type>::splat(0x01F22F1A11A5569F),
716                        <$type>::splat(0x05E5635A21D9AE61),
717                        <$type>::splat(0x64BEFEF28CC970F2),
718                        <$type>::splat(0x613670957BC46611),
719                        <$type>::splat(0xB87C5A554FD00ECB),
720                        <$type>::splat(0x8C3EE88A1CCF32C8),
721                        <$type>::splat(0x940C7922AE3A2614),
722                        <$type>::splat(0x1841F924A2C509E4),
723                        <$type>::splat(0x16F53526E70465C2),
724                        <$type>::splat(0x75F644E97F30A13B),
725                        <$type>::splat(0xEAF1FF7B5CECA249),
726                    ];
727                    let state_second = [
728                        <$type>::splat(0x2D5C954DF96ECB3C),
729                        <$type>::splat(0x6A332CD07057B56D),
730                        <$type>::splat(0x093D8D1270D76B6C),
731                        <$type>::splat(0x8A20D9B25569D094),
732                        <$type>::splat(0x4F9C4F99E5E7F156),
733                        <$type>::splat(0xF957B9A2DA65FB38),
734                        <$type>::splat(0x85773DAE1275AF0D),
735                        <$type>::splat(0xFAF4F247C3D810F7),
736                        <$type>::splat(0x1F1B9EE6F79A8759),
737                        <$type>::splat(0xE4FECC0FEE98B425),
738                        <$type>::splat(0x68CE61B6B9CE68A1),
739                        <$type>::splat(0xDEEA66C4BA8F974F),
740                        <$type>::splat(0x33C43D836EAFB1F5),
741                        <$type>::splat(0xE00654042719DBD9),
742                        <$type>::splat(0x7CF8A9F009831265),
743                        <$type>::splat(0xFD5449A6BF174743),
744                        <$type>::splat(0x97DDAD33D8994B40),
745                        <$type>::splat(0x48EAD5FC5D0BE774),
746                        <$type>::splat(0xE3B8C8EE55B7B03C),
747                        <$type>::splat(0x91A0226E649E42E9),
748                        <$type>::splat(0x900E3129E7BADD7B),
749                        <$type>::splat(0x202A9EC5FAA3CCE8),
750                        <$type>::splat(0x5B3402464E1C3DB6),
751                        <$type>::splat(0x609F4E62A44C1059),
752                        <$type>::splat(0x20D06CD26A8FBF5C),
753                    ];
754
755                    keccak_f::<$type>(state_first, state_second);
756                }
757            };
758        }
759
760        impl_keccak_f1600xn!(keccak_f1600x2, u64x2);
761        impl_keccak_f1600xn!(keccak_f1600x4, u64x4);
762        impl_keccak_f1600xn!(keccak_f1600x8, u64x8);
763    }
764}