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
7/// construction.
8pub trait LaneSize:
9    Copy
10    + Clone
11    + Default
12    + PartialEq
13    + BitAndAssign
14    + BitAnd<Output = Self>
15    + BitXorAssign
16    + BitXor<Output = Self>
17    + Not<Output = Self>
18{
19    /// Number of rounds of the Keccak-f permutation.
20    const KECCAK_F_ROUND_COUNT: usize;
21
22    /// Truncate function.
23    fn truncate_rc(rc: u64) -> Self;
24
25    /// Rotate left function.
26    #[must_use]
27    fn rotate_left(self, n: u32) -> Self;
28}
29
30macro_rules! impl_lanesize {
31    ($type:ty, $round:expr) => {
32        impl LaneSize for $type {
33            const KECCAK_F_ROUND_COUNT: usize = $round;
34
35            #[allow(clippy::cast_possible_truncation, trivial_numeric_casts)]
36            fn truncate_rc(rc: u64) -> Self {
37                rc as Self
38            }
39
40            fn rotate_left(self, n: u32) -> Self {
41                self.rotate_left(n)
42            }
43        }
44    };
45}
46
47impl_lanesize!(u8, F200_ROUNDS);
48impl_lanesize!(u16, F400_ROUNDS);
49impl_lanesize!(u32, F800_ROUNDS);
50impl_lanesize!(u64, F1600_ROUNDS);
51
52#[rustfmt::skip]
53macro_rules! unroll5 {
54    ($var: ident, $body: block) => {
55        #[cfg(not(keccak_backend_soft = "compact"))]
56        {
57            { const $var: usize = 0; $body; }
58            { const $var: usize = 1; $body; }
59            { const $var: usize = 2; $body; }
60            { const $var: usize = 3; $body; }
61            { const $var: usize = 4; $body; }
62        }
63        #[cfg(keccak_backend_soft = "compact")]
64        {
65            for $var in 0..5 $body
66        }
67    };
68}
69
70#[rustfmt::skip]
71macro_rules! unroll24 {
72    ($var: ident, $body: block) => {
73        #[cfg(not(keccak_backend_soft = "compact"))]
74        {
75            { const $var: usize = 0; $body; }
76            { const $var: usize = 1; $body; }
77            { const $var: usize = 2; $body; }
78            { const $var: usize = 3; $body; }
79            { const $var: usize = 4; $body; }
80            { const $var: usize = 5; $body; }
81            { const $var: usize = 6; $body; }
82            { const $var: usize = 7; $body; }
83            { const $var: usize = 8; $body; }
84            { const $var: usize = 9; $body; }
85            { const $var: usize = 10; $body; }
86            { const $var: usize = 11; $body; }
87            { const $var: usize = 12; $body; }
88            { const $var: usize = 13; $body; }
89            { const $var: usize = 14; $body; }
90            { const $var: usize = 15; $body; }
91            { const $var: usize = 16; $body; }
92            { const $var: usize = 17; $body; }
93            { const $var: usize = 18; $body; }
94            { const $var: usize = 19; $body; }
95            { const $var: usize = 20; $body; }
96            { const $var: usize = 21; $body; }
97            { const $var: usize = 22; $body; }
98            { const $var: usize = 23; $body; }
99        }
100        #[cfg(keccak_backend_soft = "compact")]
101        {
102            for $var in 0..24 $body
103        }
104    };
105}
106
107/// Generic Keccak-p sponge function.
108///
109/// # Panics
110/// If the `ROUNDS` is greater than `L::KECCAK_F_ROUND_COUNT`.
111#[allow(non_upper_case_globals, unused_assignments)]
112pub(crate) fn keccak_p<L: LaneSize, const ROUNDS: usize>(state: &mut [L; PLEN]) {
113    // https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf#page=25
114    // "the rounds of KECCAK-p[b, nr] match the last rounds of KECCAK-f[b]"
115    let round_consts = RC[..L::KECCAK_F_ROUND_COUNT]
116        .last_chunk::<ROUNDS>()
117        .expect("Number of rounds greater than `KECCAK_F_ROUND_COUNT` is not supported!")
118        .map(L::truncate_rc);
119
120    // Not unrolling this loop results in a much smaller function, plus
121    // it positively influences performance due to the smaller load on I-cache
122    for rc in round_consts {
123        let mut array = [L::default(); 5];
124
125        // Theta
126        unroll5!(x, {
127            unroll5!(y, {
128                array[x] ^= state[5 * y + x];
129            });
130        });
131
132        unroll5!(x, {
133            let t1 = array[(x + 4) % 5];
134            let t2 = array[(x + 1) % 5].rotate_left(1);
135            unroll5!(y, {
136                state[5 * y + x] ^= t1 ^ t2;
137            });
138        });
139
140        // Rho and pi
141        let mut last = state[1];
142        unroll24!(x, {
143            array[0] = state[PI[x]];
144            state[PI[x]] = last.rotate_left(RHO[x]);
145            last = array[0];
146        });
147
148        // Chi
149        unroll5!(y_step, {
150            let y = 5 * y_step;
151
152            array.copy_from_slice(&state[y..][..5]);
153
154            unroll5!(x, {
155                let t1 = !array[(x + 1) % 5];
156                let t2 = array[(x + 2) % 5];
157                state[y + x] = array[x] ^ (t1 & t2);
158            });
159        });
160
161        // Iota
162        state[0] ^= rc;
163    }
164}
165
166/// Default backend based on software implementation.
167pub(crate) struct Backend;
168
169impl super::Backend for Backend {
170    #[cfg(feature = "parallel")]
171    type ParSize1600 = U1;
172
173    #[inline]
174    fn get_p1600<const ROUNDS: usize>() -> Fn1600 {
175        keccak_p::<u64, ROUNDS>
176    }
177}