Skip to main content

bee_crypto/ternary/sponge/curlp/
mod.rs

1// Copyright 2020-2021 IOTA Stiftung
2// SPDX-License-Identifier: Apache-2.0
3
4mod batched;
5mod unrolled;
6
7pub use batched::{BatchHasher, BATCH_SIZE};
8pub use unrolled::UnrolledCurlP81;
9
10use crate::ternary::{sponge::Sponge, HASH_LENGTH};
11
12use bee_ternary::{Btrit, TritBuf, Trits};
13
14use std::{
15    convert::Infallible,
16    ops::{Deref, DerefMut},
17};
18
19const STATE_LENGTH: usize = HASH_LENGTH * 3;
20const HALF_STATE_LENGTH: usize = STATE_LENGTH / 2;
21
22const TRUTH_TABLE: [Btrit; 9] = [
23    Btrit::PlusOne,
24    Btrit::Zero,
25    Btrit::NegOne,
26    Btrit::PlusOne,
27    Btrit::NegOne,
28    Btrit::Zero,
29    Btrit::NegOne,
30    Btrit::PlusOne,
31    Btrit::Zero,
32];
33
34/// Available round numbers for `CurlP`.
35#[derive(Copy, Clone)]
36pub enum CurlPRounds {
37    /// 27 rounds.
38    Rounds27 = 27,
39    /// 81 rounds.
40    Rounds81 = 81,
41}
42
43/// State of the ternary cryptographic function `CurlP`.
44pub struct CurlP {
45    /// The number of rounds of hashing to apply to the state.
46    rounds: CurlPRounds,
47    /// The internal state.
48    state: TritBuf,
49    /// Workspace for performing transformations.
50    work_state: TritBuf,
51}
52
53impl CurlP {
54    /// Create a new `CurlP` sponge with `rounds` of iterations.
55    pub fn new(rounds: CurlPRounds) -> Self {
56        Self {
57            rounds,
58            state: TritBuf::zeros(STATE_LENGTH),
59            work_state: TritBuf::zeros(STATE_LENGTH),
60        }
61    }
62
63    /// Transforms the internal state of the `CurlP` sponge after the input was copied into the internal state.
64    ///
65    /// The essence of this transformation is the application of a substitution box to the internal state, which happens
66    /// `rounds` number of times.
67    fn transform(&mut self) {
68        /// # Safety
69        ///
70        /// For performance reasons, this method is unsafe.
71        /// It is however fine since:
72        /// - It is not publicly exposed.
73        /// - `state` is indexed with `p` and `q` that come from iteration on `state`.
74        /// - `TRUTH_TABLE`, of size 9, is indexed with a value that is in [0, 8].
75        #[inline]
76        unsafe fn truth_table_get(state: &Trits, p: usize, q: usize) -> Btrit {
77            #[allow(clippy::cast_sign_loss)] // Reason: "`BTrit`'s repr is between `-1` and `1`
78            *TRUTH_TABLE
79                .get_unchecked((3 * (state.get_unchecked(q) as i8 + 1) + (state.get_unchecked(p) as i8 + 1)) as usize)
80        }
81
82        /// # Safety
83        ///
84        /// For performance reasons, this method is unsafe.
85        /// It is however fine since:
86        /// - It is not publicly exposed.
87        /// - `input` and `output` have the same known sizes.
88        #[inline]
89        unsafe fn substitution_box(input: &Trits, output: &mut Trits) {
90            output.set_unchecked(0, truth_table_get(input, 0, HALF_STATE_LENGTH));
91
92            for state_index in 0..HALF_STATE_LENGTH {
93                let left_idx = HALF_STATE_LENGTH - state_index;
94                let right_idx = STATE_LENGTH - state_index - 1;
95                let state_index_2 = 2 * state_index;
96
97                output.set_unchecked(state_index_2 + 1, truth_table_get(input, left_idx, right_idx));
98                output.set_unchecked(state_index_2 + 2, truth_table_get(input, right_idx, left_idx - 1));
99            }
100        }
101
102        let (lhs, rhs) = (&mut self.state, &mut self.work_state);
103
104        for _ in 0..self.rounds as usize {
105            unsafe {
106                substitution_box(lhs, rhs);
107            }
108            std::mem::swap(lhs, rhs);
109        }
110    }
111}
112
113impl Sponge for CurlP {
114    type Error = Infallible;
115
116    /// Reset the internal state by overwriting it with zeros.
117    fn reset(&mut self) {
118        self.state.fill(Btrit::Zero);
119    }
120
121    /// Absorb `input` into the sponge by copying `HASH_LENGTH` chunks of it into its internal state and transforming
122    /// the state before moving on to the next chunk.
123    ///
124    /// If `input` is not a multiple of `HASH_LENGTH` with the last chunk having `n < HASH_LENGTH` trits, the last chunk
125    /// will be copied to the first `n` slots of the internal state. The remaining data in the internal state is then
126    /// just the result of the last transformation before the data was copied, and will be reused for the next
127    /// transformation.
128    fn absorb(&mut self, input: &Trits) -> Result<(), Self::Error> {
129        for chunk in input.chunks(HASH_LENGTH) {
130            self.state[0..chunk.len()].copy_from(chunk);
131            self.transform();
132        }
133        Ok(())
134    }
135
136    /// Squeeze the sponge by copying the state into the provided `buf`. This will fill the buffer in chunks of
137    /// `HASH_LENGTH` at a time.
138    ///
139    /// If the last chunk is smaller than `HASH_LENGTH`, then only the fraction that fits is written into it.
140    fn squeeze_into(&mut self, buf: &mut Trits) -> Result<(), Self::Error> {
141        for chunk in buf.chunks_mut(HASH_LENGTH) {
142            chunk.copy_from(&self.state[0..chunk.len()]);
143            self.transform()
144        }
145        Ok(())
146    }
147}
148
149/// `CurlP` with a fixed number of 27 rounds.
150pub struct CurlP27(CurlP);
151
152impl CurlP27 {
153    /// Creates a new `CurlP27`.
154    pub fn new() -> Self {
155        Self(CurlP::new(CurlPRounds::Rounds27))
156    }
157}
158
159impl Default for CurlP27 {
160    fn default() -> Self {
161        CurlP27::new()
162    }
163}
164
165impl Deref for CurlP27 {
166    type Target = CurlP;
167
168    fn deref(&self) -> &Self::Target {
169        &self.0
170    }
171}
172
173impl DerefMut for CurlP27 {
174    fn deref_mut(&mut self) -> &mut Self::Target {
175        &mut self.0
176    }
177}
178
179/// `CurlP` with a fixed number of 81 rounds.
180pub struct CurlP81(CurlP);
181
182impl CurlP81 {
183    /// Creates a new `CurlP81`.
184    pub fn new() -> Self {
185        Self(CurlP::new(CurlPRounds::Rounds81))
186    }
187}
188
189impl Default for CurlP81 {
190    fn default() -> Self {
191        CurlP81::new()
192    }
193}
194
195impl Deref for CurlP81 {
196    type Target = CurlP;
197
198    fn deref(&self) -> &Self::Target {
199        &self.0
200    }
201}
202
203impl DerefMut for CurlP81 {
204    fn deref_mut(&mut self) -> &mut Self::Target {
205        &mut self.0
206    }
207}