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;
11use keccak::{Keccak, State1600};
12use sponge_cursor::SpongeCursor;
13
14use core::fmt;
15use digest::{
16 CollisionResistance, ExtendableOutput, ExtendableOutputReset, HashMarker, Reset, Update,
17 XofReader,
18 common::AlgorithmName,
19 consts::{U16, U32},
20};
21
22const ROUNDS: usize = 12;
24
25pub const DEFAULT_DS: u8 = 0x1F;
27
28#[derive(Clone)]
35pub struct TurboShake<const RATE: usize, const DS: u8> {
36 state: State1600,
37 cursor: SpongeCursor<RATE>,
38 keccak: Keccak,
39}
40
41impl<const RATE: usize, const DS: u8> Default for TurboShake<RATE, DS> {
42 #[inline]
43 fn default() -> Self {
44 const {
45 assert!(DS >= 0x01 && DS <= 0x7F, "invalid domain separator");
46 assert!(RATE == 168 || RATE == 136, "unsupported rate");
47 }
48 Self {
49 state: Default::default(),
50 cursor: Default::default(),
51 keccak: Keccak::new(),
52 }
53 }
54}
55
56impl<const RATE: usize, const DS: u8> HashMarker for TurboShake<RATE, DS> {}
57
58impl<const RATE: usize, const DS: u8> Update for TurboShake<RATE, DS> {
59 #[inline]
60 fn update(&mut self, data: &[u8]) {
61 self.keccak.with_p1600::<ROUNDS>(|p1600| {
62 self.cursor.absorb_u64_le(&mut self.state, p1600, data);
63 });
64 }
65}
66
67impl<const RATE: usize, const DS: u8> TurboShake<RATE, DS> {
68 fn pad(&mut self) {
69 let pos = self.cursor.pos();
70 let word_offset = pos / 8;
71 let byte_offset = pos % 8;
72
73 let pad = u64::from(DS) << (8 * byte_offset);
74 self.state[word_offset] ^= pad;
75 self.state[RATE / 8 - 1] ^= 1 << 63;
76 }
77}
78
79impl<const RATE: usize, const DS: u8> ExtendableOutput for TurboShake<RATE, DS> {
80 type Reader = TurboShakeReader<RATE>;
81
82 #[inline]
83 fn finalize_xof(mut self) -> Self::Reader {
84 self.pad();
85 Self::Reader {
86 state: self.state,
87 cursor: Default::default(),
88 keccak: self.keccak,
89 }
90 }
91}
92
93impl<const RATE: usize, const DS: u8> ExtendableOutputReset for TurboShake<RATE, DS> {
94 #[inline]
95 fn finalize_xof_reset(&mut self) -> Self::Reader {
96 self.pad();
97 let reader = Self::Reader {
98 state: self.state,
99 cursor: Default::default(),
100 keccak: self.keccak,
101 };
102 self.reset();
103 reader
104 }
105}
106
107impl<const RATE: usize, const DS: u8> Reset for TurboShake<RATE, DS> {
108 #[inline]
109 fn reset(&mut self) {
110 self.state = Default::default();
111 self.cursor = Default::default();
112 }
113}
114
115impl<const RATE: usize, const DS: u8> AlgorithmName for TurboShake<RATE, DS> {
116 fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
117 let alg_name = match RATE {
118 168 => "TurboSHAKE128",
119 136 => "TurboSHAKE256",
120 _ => unreachable!(),
121 };
122 f.write_str(alg_name)
123 }
124}
125
126impl<const RATE: usize, const DS: u8> fmt::Debug for TurboShake<RATE, DS> {
127 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
128 let debug_str = match RATE {
129 168 => "TurboShake128 { ... }",
130 136 => "TurboShake256 { ... }",
131 _ => unreachable!(),
132 };
133 f.write_str(debug_str)
134 }
135}
136
137impl<const RATE: usize, const DS: u8> Drop for TurboShake<RATE, DS> {
138 fn drop(&mut self) {
139 #[cfg(feature = "zeroize")]
140 {
141 use digest::zeroize::Zeroize;
142 self.state.zeroize();
143 self.cursor.zeroize();
144 }
145 }
146}
147
148#[cfg(feature = "zeroize")]
149impl<const RATE: usize, const DS: u8> digest::zeroize::ZeroizeOnDrop for TurboShake<RATE, DS> {}
150
151#[derive(Clone)]
153pub struct TurboShakeReader<const RATE: usize> {
154 state: State1600,
155 cursor: SpongeCursor<RATE>,
156 keccak: Keccak,
157}
158
159impl<const RATE: usize> XofReader for TurboShakeReader<RATE> {
160 #[inline]
161 fn read(&mut self, buf: &mut [u8]) {
162 self.keccak.with_p1600::<ROUNDS>(|p1600| {
163 self.cursor.squeeze_read_u64_le(&mut self.state, p1600, buf);
164 });
165 }
166}
167
168impl<const RATE: usize> fmt::Debug for TurboShakeReader<RATE> {
169 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
170 let debug_str = match RATE {
171 168 => "TurboShakeReader128 { ... }",
172 136 => "TurboShakeReader256 { ... }",
173 _ => unreachable!(),
174 };
175 f.write_str(debug_str)
176 }
177}
178
179impl<const RATE: usize> Drop for TurboShakeReader<RATE> {
180 fn drop(&mut self) {
181 #[cfg(feature = "zeroize")]
182 {
183 use digest::zeroize::Zeroize;
184 self.state.zeroize();
185 self.cursor.zeroize();
186 }
187 }
188}
189
190#[cfg(feature = "zeroize")]
191impl<const RATE: usize> digest::zeroize::ZeroizeOnDrop for TurboShakeReader<RATE> {}
192
193pub type CTurboShake128<const DS: u8> = TurboShake<168, DS>;
197pub type CTurboShake256<const DS: u8> = TurboShake<136, DS>;
201
202pub type TurboShake128 = CTurboShake128<DEFAULT_DS>;
204pub type TurboShake256 = CTurboShake256<DEFAULT_DS>;
206
207pub type TurboShake128Reader = TurboShakeReader<168>;
209pub type TurboShake256Reader = TurboShakeReader<136>;
211
212impl<const DS: u8> CollisionResistance for CTurboShake128<DS> {
213 type CollisionResistance = U16;
215}
216
217impl<const DS: u8> CollisionResistance for CTurboShake256<DS> {
218 type CollisionResistance = U32;
220}