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
22pub type Shake128 = Shake<168>;
24pub type Shake256 = Shake<136>;
26
27pub type Shake128Reader = ShakeReader<168>;
29pub type Shake256Reader = ShakeReader<136>;
31
32#[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 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 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#[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
198impl CollisionResistance for Shake128 {
201 type CollisionResistance = U16;
202}
203
204impl CollisionResistance for Shake256 {
205 type CollisionResistance = U32;
206}