Skip to main content

keccak/backends/
soft.rs

1use crate::{consts::*, types::Fn1600};
2use core::ops::{BitAnd, BitAndAssign, BitXor, BitXorAssign, Not};
3#[cfg(feature = "parallel")]
4use hybrid_array::typenum::U1;
5
6/// Keccak is a permutation over an array of lanes which comprise the sponge construction.
7pub trait LaneSize:
8    Copy
9    + Clone
10    + Default
11    + PartialEq
12    + BitAndAssign
13    + BitAnd<Output = Self>
14    + BitXorAssign
15    + BitXor<Output = Self>
16    + Not<Output = Self>
17    + 'static
18{
19    /// Round constants
20    const RC: &[Self];
21
22    /// Rotate left function.
23    #[must_use]
24    fn rotate_left(self, n: u32) -> Self;
25}
26
27macro_rules! impl_lanesize {
28    ($type:ty, $round:expr) => {
29        impl LaneSize for $type {
30            const RC: &[Self] = &{
31                let mut res = [0; $round];
32                let mut i = 0;
33                #[allow(clippy::cast_possible_truncation, trivial_numeric_casts)]
34                while i < res.len() {
35                    res[i] = RC[i] as Self;
36                    i += 1;
37                }
38                res
39            };
40
41            fn rotate_left(self, n: u32) -> Self {
42                self.rotate_left(n)
43            }
44        }
45    };
46}
47
48impl_lanesize!(u8, F200_ROUNDS);
49impl_lanesize!(u16, F400_ROUNDS);
50impl_lanesize!(u32, F800_ROUNDS);
51impl_lanesize!(u64, F1600_ROUNDS);
52
53/// Generic Keccak-p sponge function.
54///
55/// # Panics
56/// If the `ROUNDS` is greater than `L::KECCAK_F_ROUND_COUNT`.
57pub(crate) fn keccak_p<L: LaneSize, const ROUNDS: usize>(state: &mut [L; PLEN]) {
58    const { assert!(ROUNDS <= L::RC.len()) };
59
60    // https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf#page=25
61    // "the rounds of KECCAK-p[b, nr] match the last rounds of KECCAK-f[b]"
62    let round_consts = L::RC
63        .last_chunk::<ROUNDS>()
64        .expect("Number of rounds is checked above");
65
66    // Not unrolling this loop results in a much smaller function, plus
67    // it positively influences performance due to the smaller load on I-cache
68    for rc in round_consts {
69        let mut array = [L::default(); 5];
70
71        // Theta
72        for x in 0..5 {
73            for y in 0..5 {
74                array[x] ^= state[5 * y + x];
75            }
76        }
77
78        for x in 0..5 {
79            let t1 = array[(x + 4) % 5];
80            let t2 = array[(x + 1) % 5].rotate_left(1);
81            for y in 0..5 {
82                state[5 * y + x] ^= t1 ^ t2;
83            }
84        }
85
86        // Rho and pi
87        let mut last = state[1];
88        for x in 0..24 {
89            array[0] = state[PI[x]];
90            state[PI[x]] = last.rotate_left(RHO[x]);
91            last = array[0];
92        }
93
94        // Chi
95        for y_step in 0..5 {
96            let y = 5 * y_step;
97
98            array.copy_from_slice(&state[y..][..5]);
99
100            for x in 0..5 {
101                let t1 = !array[(x + 1) % 5];
102                let t2 = array[(x + 2) % 5];
103                state[y + x] = array[x] ^ (t1 & t2);
104            }
105        }
106
107        // Iota
108        state[0] ^= *rc;
109    }
110}
111
112/// Default backend based on software implementation.
113pub(crate) struct Backend;
114
115impl super::Backend for Backend {
116    #[cfg(feature = "parallel")]
117    type ParSize1600 = U1;
118
119    #[inline]
120    fn get_p1600<const ROUNDS: usize>() -> Fn1600 {
121        keccak_p::<u64, ROUNDS>
122    }
123}