zeros/
keccak.rs

1/*
2==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--
3
4Zeros
5
6Copyright (C) 2019-2025  Anonymous
7
8There are several releases over multiple years,
9they are listed as ranges, such as: "2019-2025".
10
11This program is free software: you can redistribute it and/or modify
12it under the terms of the GNU Lesser General Public License as published by
13the Free Software Foundation, either version 3 of the License, or
14(at your option) any later version.
15
16This program is distributed in the hope that it will be useful,
17but WITHOUT ANY WARRANTY; without even the implied warranty of
18MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19GNU Lesser General Public License for more details.
20
21You should have received a copy of the GNU Lesser General Public License
22along with this program.  If not, see <https://www.gnu.org/licenses/>.
23
24::--::--::--::--::--::--::--::--::--::--::--::--::--::--::--::--
25*/
26
27#![cfg(target_endian="little")]
28#![doc(cfg(target_endian="little"))]
29
30//! # Keccak
31//!
32//! ## Notes
33//!
34//! - In addition to NIST's test data, this implementation has passed millions of sample hashes generated by [OpenSSL][site:openssl].
35//! - 8-bit machines are not supported.
36//!
37//! ## References
38//!
39//! - Specifications: <https://keccak.team/keccak_specs_summary.html>
40//! - Test data: <https://csrc.nist.gov/projects/cryptographic-standards-and-guidelines/example-values>
41//!
42//! ## Examples
43//!
44//! ```
45//! use zeros::keccak::Hash;
46//!
47//! let mut keccak = Hash::Shake128.new_keccak();
48//! keccak.update("test");
49//! assert_eq!(
50//!     keccak.finish(),
51//!     &[
52//!         0xd3, 0xb0, 0xaa, 0x9c, 0xd8, 0xb7, 0x25, 0x56,
53//!         0x22, 0xce, 0xbc, 0x63, 0x1e, 0x86, 0x7d, 0x40,
54//!     ],
55//! );
56//!
57//! ```
58//!
59//! ### Pseudorandom byte generator
60//!
61//! ```
62//! use zeros::keccak::Hash;
63//!
64//! let mut keccak_x = Hash::Shake256.make_unlimited_keccak_x([
65//!     "This is just an example.",
66//!     "You should feed it data from some secure source,",
67//!     "such as /dev/urandom",
68//! ])?;
69//! assert_eq!(keccak_x.size_hint(), (usize::MAX, Some(usize::MAX)));
70//!
71//! // Now read some bytes
72//! let number = keccak_x.next_u64();
73//!
74//! // For what we feed above, we'll always get `0x_0f68_60c9_b7c2_cde6`
75//! assert_eq!(number, 0x_0f68_60c9_b7c2_cde6);
76//!
77//! // Check its size again
78//! assert_eq!(keccak_x.size_hint(), (usize::MAX, Some(usize::MAX)));
79//!
80//! # zeros::Result::Ok(())
81//! ```
82//!
83//! [site:openssl]: https://www.openssl.org
84
85use {
86    core::mem,
87    alloc::{
88        string::String,
89        vec::Vec,
90    },
91    crate::{Bytes, Result},
92    self::{
93        bits::Bits,
94        state::State,
95    },
96};
97
98#[cfg(feature="std")]
99use {
100    std::io::Write,
101    crate::IoResult,
102};
103
104pub use self::{
105    hash::Hash,
106    keccak_x::{KeccakX, XType},
107    r#type::Type,
108};
109
110#[cfg(feature="std")]
111pub use self::{
112    hash_reader::*,
113    hash_writer::*,
114};
115
116macro_rules! u8_bits { () => { 8 }}
117
118mod bits;
119mod hash;
120mod keccak_x;
121mod state;
122mod tests;
123mod r#type;
124
125#[cfg(feature="std")]
126mod hash_reader;
127#[cfg(feature="std")]
128mod hash_writer;
129
130type OneLane = u64;
131const SIZE_OF_ONE_LANE: usize = mem::size_of::<OneLane>();
132
133type Lanes = [OneLane; NUMBER_OF_LANES];
134const NUMBER_OF_LANES: usize = 25;
135const LANES_OF_ZEROS: Lanes = [OneLane::MIN; NUMBER_OF_LANES];
136
137// TODO: replace this with `Type`
138// - Unstable feature: [`adt_const_params`](https://github.com/rust-lang/rust/issues/95174)
139type InnerType = char;
140
141/// # Keccak
142#[derive(Debug, Clone, Eq, PartialEq)]
143pub struct Keccak<const TYPE: InnerType> {
144    hash: Hash,
145    bits: Bits,
146    buf: Vec<u8>,
147    buf_chunk: usize,
148    lanes: Lanes,
149    output_bytes: Option<usize>,
150}
151
152impl<const TYPE: InnerType> Keccak<TYPE> {
153
154    /// # Makes new instance
155    fn with(hash: Hash) -> Keccak<TYPE> {
156        let bits = Bits::B1600;
157        let buf_chunk = hash.rate() / u8_bits!();
158        let buf = Vec::with_capacity(buf_chunk);
159        let output_bytes = Some(hash.output_bytes());
160
161        Self {
162            hash,
163            bits,
164            buf,
165            buf_chunk,
166            lanes: LANES_OF_ZEROS,
167            output_bytes,
168        }
169    }
170
171    /// # Makes new instance
172    pub (crate) fn new(hash: Hash) -> Keccak<{Type::Function.id()}> {
173        Keccak::with(hash)
174    }
175
176    /// # Makes new instance with limited output *bytes*
177    pub (crate) fn make_with_output_bytes(hash: Hash, output_bytes: usize) -> Result<Keccak<{Type::LimitedXOF.id()}>> {
178        if hash.is_extendable_output_function() {
179            if output_bytes >= Hash::MIN_CUSTOM_OUTPUT_BYTES {
180                let mut result = Keccak::with(hash);
181                result.output_bytes = Some(output_bytes);
182                Ok(result)
183            } else {
184                Err(err!("Custom output bytes must be equal to or larger than {min}", min=Hash::MIN_CUSTOM_OUTPUT_BYTES))
185            }
186        } else {
187            Err(err!("{hash} does not support custom output bits", hash=hash))
188        }
189    }
190
191    /// # Makes new instance with limited output *bytes*
192    pub (crate) fn make_with_unlimited_output_bytes(hash: Hash) -> Result<Keccak<{Type::UnlimitedXOF.id()}>> {
193        if hash.is_extendable_output_function() {
194            let mut result = Keccak::with(hash);
195            result.output_bytes = None;
196            Ok(result)
197        } else {
198            Err(err!("{hash} does not support custom output bits", hash=hash))
199        }
200    }
201
202    /// # Gets hash
203    pub const fn hash(&self) -> &Hash {
204        &self.hash
205    }
206
207    /// # Updates new data
208    ///
209    /// Returns length of input bytes.
210    pub fn update<B>(&mut self, bytes: B) -> usize where B: AsRef<[u8]> {
211        macro_rules! proc { ($buf: expr) => {{
212            let buf = $buf.unwrap_or(&self.buf);
213            // Caller should guarantee that we have exactly 8 bytes for the *last* chunk
214            for (idx, c) in {
215                let chunks = buf.chunks_exact(SIZE_OF_ONE_LANE);
216                #[cfg(test)]
217                assert!(chunks.remainder().is_empty());
218                chunks.enumerate()
219            } {
220                self.lanes[idx] ^= u64::from_le_bytes([c[0], c[1], c[2], c[3], c[4], c[5], c[6], c[7]]);
221            }
222            // Update
223            keccak_p_1600_24(&mut self.lanes);
224        }}}
225
226        let mut bytes = bytes.as_ref();
227        let result = bytes.len();
228
229        // Process buffer first
230        if crate::io::fill_buffer(&mut self.buf, self.buf_chunk, &mut bytes) {
231            proc!(None);
232            self.buf.clear();
233        }
234
235        // Now process bytes
236        let chunks = bytes.chunks_exact(self.buf_chunk);
237        self.buf.extend(chunks.remainder());
238        chunks.for_each(|c| proc!(Some(c)));
239
240        result
241    }
242
243    /// # Updates new data
244    ///
245    /// Returns length of input bytes. If overflow happens, `None` will be returned.
246    pub fn update_bytes<'a, const N: usize, B, B0>(&mut self, bytes: B) -> Option<usize> where B: Into<Bytes<'a, N, B0>>, B0: AsRef<[u8]> + 'a {
247        let mut result = Some(usize::MIN);
248        for bytes in bytes.into().as_slice() {
249            let size = self.update(bytes);
250            if let Some(current) = result.as_mut() {
251                match current.checked_add(size) {
252                    Some(new) => *current = new,
253                    None => result = None,
254                };
255            }
256        }
257
258        result
259    }
260
261    /// # Finishes as `KeccakX`
262    fn finish_as_keccak_x<const T: keccak_x::InnerXType>(mut self) -> KeccakX<T> {
263        const ZEROS: &[u8] = &[u8::MIN; 128];
264
265        // Padding form: buf + suffix + 1 + j*0 + 1
266        let suffix = self.hash.suffix();
267        let suffix_len = (u8_bits!() - suffix.leading_zeros()) as u8;
268        let bits = {
269            let j = (|| {
270                let j = isize::try_from(self.buf.len()).ok()?.checked_mul(u8_bits!())?.checked_add(suffix_len.into())?
271                    .checked_neg()?.checked_sub(2)?.rem_euclid(isize::try_from(self.hash.rate()).ok()?);
272                usize::try_from(j).ok()
273            })().unwrap();
274            match j.checked_add(2).map(|n| n.checked_add(suffix_len.into())) {
275                Some(Some(bits)) => bits,
276                _ => panic!("{}", e!()),
277            }
278        };
279
280        let bytes = if bits % u8_bits!() == usize::MIN { bits / u8_bits!() } else { panic!("{}", err!("Invalid bits: {bits}")); };
281        self.buf.reserve(bytes);
282
283        // Padding will make data length a positive multiple of rate. While rate is tested that: rate % 64 == 0
284        self.buf.push(suffix | (1 << u32::from(suffix_len)));
285        if let Some(mut bytes) = bytes.checked_sub(1) {
286            while bytes > usize::MIN {
287                let count = bytes.min(ZEROS.len());
288                self.buf.extend(&ZEROS[..count]);
289                bytes -= count;
290            }
291        }
292        *self.buf.last_mut().unwrap() |= 0b_1000_0000;
293
294        self.update(&[]);
295
296        // Tried to use an external vector. Times taken were similar. See: tests/hash_again.rs
297        KeccakX::new(self.hash, self.lanes, self.buf_chunk, self.output_bytes)
298    }
299
300}
301
302impl Keccak<{Type::Function.id()}> {
303
304    /// # Finishes and returns hash
305    pub fn finish(self) -> Vec<u8> {
306        self.finish_as_keccak_x::<{XType::Limited.id()}>().finish()
307    }
308
309    /// # Finishes and returns hash as a hexadecimal string, in lower-case
310    pub fn finish_as_hex(self) -> String {
311        crate::bytes_to_hex(self.finish())
312    }
313
314}
315
316/// # Extendable-Output Function (limited output)
317impl Keccak<{Type::LimitedXOF.id()}> {
318
319    /// # Finishes as `KeccakX`
320    pub fn finish(self) -> KeccakX<{XType::Limited.id()}> {
321        self.finish_as_keccak_x()
322    }
323
324}
325
326/// # Extendable-Output Function (unlimited output)
327impl Keccak<{Type::UnlimitedXOF.id()}> {
328
329    /// # Finishes as `KeccakX`
330    pub fn finish(self) -> KeccakX<{XType::Unlimited.id()}> {
331        self.finish_as_keccak_x()
332    }
333
334}
335
336impl From<Hash> for Keccak<{Type::Function.id()}> {
337
338    fn from(hash: Hash) -> Self {
339        Self::new(hash)
340    }
341
342}
343
344impl From<&Hash> for Keccak<{Type::Function.id()}> {
345
346    fn from(hash: &Hash) -> Self {
347        Self::new(hash.clone())
348    }
349
350}
351
352#[cfg(feature="std")]
353#[doc(cfg(feature="std"))]
354impl<const TYPE: InnerType> Write for Keccak<TYPE> {
355
356    #[inline(always)]
357    fn write(&mut self, buf: &[u8]) -> IoResult<usize> {
358        Ok(self.update(buf))
359    }
360
361    #[inline(always)]
362    fn flush(&mut self) -> IoResult<()> {
363        Ok(())
364    }
365
366}
367
368/// # Calls `keccak_p()` with `1600` bits and `24` rounds
369#[inline(always)]
370fn keccak_p_1600_24(lanes: &mut Lanes) {
371    keccak_p(&Bits::B1600, 24, lanes);
372}
373
374/// # (See references)
375#[inline(always)]
376fn keccak_p(bits: &Bits, rounds: usize, lanes: &mut Lanes) {
377    let mut state = State::new(lanes);
378
379    let end = 12 + 2 * bits.l();
380    for round_index in end - rounds .. end {
381        state.theta();
382        state.rho_and_pi();
383        state.chi();
384        state.iota(round_index);
385    }
386}
387
388/// # (See references)
389#[allow(unused)]
390fn keccak_f(bits: &Bits, lanes: &mut Lanes) {
391    let rounds = 12 + 2 * bits.l();
392    keccak_p(bits, rounds, lanes);
393}