Skip to main content

shake/
lib.rs

1#![no_std]
2#![doc = include_str!("../README.md")]
3#![doc(
4    html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
5    html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg"
6)]
7#![cfg_attr(docsrs, feature(doc_cfg))]
8#![forbid(unsafe_code)]
9
10pub use digest;
11pub use digest::{ExtendableOutput, Update, XofReader};
12
13use core::fmt;
14use digest::{
15    CollisionResistance, ExtendableOutputReset, HashMarker, Reset,
16    common::AlgorithmName,
17    consts::{U16, U32},
18};
19use keccak::{Keccak, State1600};
20use sponge_cursor::SpongeCursor;
21
22/// SHAKE128 hasher.
23pub type Shake128 = Shake<168>;
24/// SHAKE256 hasher.
25pub type Shake256 = Shake<136>;
26
27/// SHAKE128 XOF reader.
28pub type Shake128Reader = ShakeReader<168>;
29/// SHAKE256 XOF reader.
30pub type Shake256Reader = ShakeReader<136>;
31
32/// SHAKE hasher generic over rate.
33///
34/// Rate MUST be either 168 or 136 for SHAKE128 and SHAKE256 respectively.
35#[derive(Clone)]
36pub struct Shake<const RATE: usize> {
37    state: State1600,
38    cursor: SpongeCursor<RATE>,
39    keccak: Keccak,
40}
41
42impl<const RATE: usize> Default for Shake<RATE> {
43    #[inline]
44    fn default() -> Self {
45        const { assert!(matches!(RATE, 136 | 168)) }
46
47        Self {
48            state: Default::default(),
49            cursor: Default::default(),
50            keccak: Keccak::new(),
51        }
52    }
53}
54
55impl<const RATE: usize> HashMarker for Shake<RATE> {}
56
57impl<const RATE: usize> Update for Shake<RATE> {
58    #[inline]
59    fn update(&mut self, data: &[u8]) {
60        self.keccak.with_f1600(|f1600| {
61            self.cursor.absorb_u64_le(&mut self.state, f1600, data);
62        });
63    }
64}
65
66impl<const RATE: usize> Reset for Shake<RATE> {
67    #[inline]
68    fn reset(&mut self) {
69        self.state = Default::default();
70        self.cursor = Default::default();
71    }
72}
73
74impl<const RATE: usize> Shake<RATE> {
75    fn pad(&mut self) {
76        const SHAKE_PAD: u8 = 0x1F;
77
78        let pos = self.cursor.pos();
79        let word_offset = pos / 8;
80        let byte_offset = pos % 8;
81
82        let pad = u64::from(SHAKE_PAD) << (8 * byte_offset);
83        self.state[word_offset] ^= pad;
84        self.state[RATE / 8 - 1] ^= 1 << 63;
85    }
86}
87
88impl<const RATE: usize> ExtendableOutput for Shake<RATE> {
89    type Reader = ShakeReader<RATE>;
90
91    #[inline]
92    fn finalize_xof(mut self) -> Self::Reader {
93        self.pad();
94        // Note that `ShakeReader` applies the permutation to the state before reading from it
95        Self::Reader {
96            state: self.state,
97            cursor: Default::default(),
98            keccak: self.keccak,
99        }
100    }
101}
102
103impl<const RATE: usize> ExtendableOutputReset for Shake<RATE> {
104    #[inline]
105    fn finalize_xof_reset(&mut self) -> Self::Reader {
106        self.pad();
107        let state = self.state;
108        self.reset();
109        // Note that `ShakeReader` applies the permutation to the state before reading from it
110        Self::Reader {
111            state,
112            cursor: Default::default(),
113            keccak: self.keccak,
114        }
115    }
116}
117
118impl<const RATE: usize> AlgorithmName for Shake<RATE> {
119    #[inline]
120    fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
121        let alg_name = match RATE {
122            168 => "SHAKE128",
123            136 => "SHAKE256",
124            _ => unreachable!(),
125        };
126        f.write_str(alg_name)
127    }
128}
129
130impl<const RATE: usize> fmt::Debug for Shake<RATE> {
131    #[inline]
132    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
133        let debug_str = match RATE {
134            168 => "Shake128 { ... }",
135            136 => "Shake256 { ... }",
136            _ => unreachable!(),
137        };
138        f.write_str(debug_str)
139    }
140}
141
142impl<const RATE: usize> Drop for Shake<RATE> {
143    #[inline]
144    fn drop(&mut self) {
145        #[cfg(feature = "zeroize")]
146        {
147            use digest::zeroize::Zeroize;
148            self.state.zeroize();
149            self.cursor.zeroize();
150        }
151    }
152}
153
154#[cfg(feature = "zeroize")]
155impl<const RATE: usize> digest::zeroize::ZeroizeOnDrop for Shake<RATE> {}
156
157/// SHAKE XOF reader generic over rate.
158#[derive(Clone)]
159pub struct ShakeReader<const RATE: usize> {
160    state: State1600,
161    cursor: SpongeCursor<RATE>,
162    keccak: Keccak,
163}
164
165impl<const RATE: usize> XofReader for ShakeReader<RATE> {
166    #[inline]
167    fn read(&mut self, buf: &mut [u8]) {
168        self.keccak.with_f1600(|f1600| {
169            self.cursor.squeeze_read_u64_le(&mut self.state, f1600, buf);
170        });
171    }
172}
173
174impl<const RATE: usize> fmt::Debug for ShakeReader<RATE> {
175    #[inline]
176    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
177        let debug_str = match RATE {
178            168 => "ShakeReader128 { ... }",
179            136 => "ShakeReader256 { ... }",
180            _ => unreachable!(),
181        };
182        f.write_str(debug_str)
183    }
184}
185
186impl<const RATE: usize> Drop for ShakeReader<RATE> {
187    #[inline]
188    fn drop(&mut self) {
189        #[cfg(feature = "zeroize")]
190        {
191            use digest::zeroize::Zeroize;
192            self.state.zeroize();
193            self.cursor.zeroize();
194        }
195    }
196}
197
198// See Section 8.3 of NIST SP 800-185:
199// https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-185.pdf
200impl CollisionResistance for Shake128 {
201    type CollisionResistance = U16;
202}
203
204impl CollisionResistance for Shake256 {
205    type CollisionResistance = U32;
206}