1use core::fmt;
2use digest::{
3 ExtendableOutputReset, HashMarker, Reset, Update, XofReader,
4 block_api::{
5 AlgorithmName, Block, BlockSizeUser, Buffer, BufferKindUser, Eager, ExtendableOutputCore,
6 UpdateCore, XofReaderCore,
7 },
8 consts::{U128, U136, U168},
9};
10use sha3::{TurboShake128, TurboShake128Reader, TurboShake256, TurboShake256Reader};
11
12const CHUNK_SIZE: usize = 8192;
13const LENGTH_ENCODE_SIZE: usize = 255;
14
15macro_rules! impl_k12_core {
16 (
17 $name:ident, $reader_name:ident, $ts_name:ident, $ts_reader_name:ident, $cv_size:literal,
18 $alg_name:literal,
19 ) => {
20 #[doc = "Core"]
21 #[doc = $alg_name]
22 #[doc = "hasher state."]
23 #[derive(Clone)]
24 #[allow(non_camel_case_types)]
25 pub struct $name<'cs> {
26 customization: &'cs [u8],
27 buffer: [u8; CHUNK_SIZE],
28 bufpos: usize,
29 final_tshk: $ts_name<0x06>,
30 chain_tshk: $ts_name<0x0B>,
31 chain_length: usize,
32 }
33
34 impl<'cs> $name<'cs> {
35 const CHAINING_VALUE_SIZE: usize = $cv_size;
36
37 #[doc = "Creates a new"]
38 #[doc = $alg_name]
39 #[doc = "instance with the given customization."]
40 pub fn new(customization: &'cs [u8]) -> Self {
41 Self {
42 customization,
43 buffer: [0u8; CHUNK_SIZE],
44 bufpos: 0usize,
45 final_tshk: Default::default(),
46 chain_tshk: Default::default(),
47 chain_length: 0usize,
48 }
49 }
50
51 fn process_chunk(&mut self) {
52 debug_assert!(self.bufpos == CHUNK_SIZE);
53 if self.chain_length == 0 {
54 self.final_tshk.update(&self.buffer);
55 } else {
56 self.process_chaining_chunk();
57 }
58
59 self.chain_length += 1;
60 self.buffer = [0u8; CHUNK_SIZE];
61 self.bufpos = 0;
62 }
63
64 fn process_chaining_chunk(&mut self) {
65 debug_assert!(self.bufpos != 0);
66 if self.chain_length == 1 {
67 self.final_tshk
68 .update(&[0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
69 }
70
71 let mut result = [0u8; Self::CHAINING_VALUE_SIZE];
72 self.chain_tshk.update(&self.buffer[..self.bufpos]);
73 self.chain_tshk.finalize_xof_reset_into(&mut result);
74 self.final_tshk.update(&result);
75 }
76 }
77
78 impl HashMarker for $name<'_> {}
79
80 impl BlockSizeUser for $name<'_> {
81 type BlockSize = U128;
82 }
83
84 impl BufferKindUser for $name<'_> {
85 type BufferKind = Eager;
86 }
87
88 impl UpdateCore for $name<'_> {
89 #[inline]
90 fn update_blocks(&mut self, blocks: &[Block<Self>]) {
91 for block in blocks {
92 if self.bufpos == CHUNK_SIZE {
93 self.process_chunk();
94 }
95
96 self.buffer[self.bufpos..self.bufpos + 128].clone_from_slice(block);
97 self.bufpos += 128;
98 }
99 }
100 }
101
102 impl ExtendableOutputCore for $name<'_> {
103 type ReaderCore = $reader_name;
104
105 #[inline]
106 fn finalize_xof_core(&mut self, buffer: &mut Buffer<Self>) -> Self::ReaderCore {
107 let mut lenbuf = [0u8; LENGTH_ENCODE_SIZE];
108
109 buffer.digest_blocks(self.customization, |block| self.update_blocks(block));
111 buffer.digest_blocks(
112 length_encode(self.customization.len(), &mut lenbuf),
113 |block| self.update_blocks(block),
114 );
115
116 if self.bufpos == CHUNK_SIZE && buffer.get_pos() != 0 {
117 self.process_chunk();
118 }
119
120 self.buffer[self.bufpos..(self.bufpos + buffer.get_pos())]
122 .copy_from_slice(buffer.get_data());
123 self.bufpos += buffer.get_pos();
124
125 if self.chain_length == 0 {
127 let tshk = $ts_name::<0x07>::default()
129 .chain(&self.buffer[..self.bufpos])
130 .finalize_xof_reset();
131 return $reader_name { tshk };
132 }
133
134 self.process_chaining_chunk();
136
137 self.final_tshk
139 .update(length_encode(self.chain_length, &mut lenbuf));
140 self.final_tshk.update(&[0xff, 0xff]);
141
142 $reader_name {
143 tshk: self.final_tshk.finalize_xof_reset(),
144 }
145 }
146 }
147
148 impl Default for $name<'_> {
149 #[inline]
150 fn default() -> Self {
151 Self {
152 customization: &[],
153 buffer: [0u8; CHUNK_SIZE],
154 bufpos: 0usize,
155 final_tshk: Default::default(),
156 chain_tshk: Default::default(),
157 chain_length: 0usize,
158 }
159 }
160 }
161
162 impl Reset for $name<'_> {
163 #[inline]
164 fn reset(&mut self) {
165 *self = Self::new(self.customization);
166 }
167 }
168
169 impl AlgorithmName for $name<'_> {
170 fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
171 f.write_str($alg_name)
172 }
173 }
174
175 impl fmt::Debug for $name<'_> {
176 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
177 f.write_str(concat!(stringify!($name), " { ... }"))
178 }
179 }
180
181 impl Drop for $name<'_> {
182 fn drop(&mut self) {
183 #[cfg(feature = "zeroize")]
184 {
185 use digest::zeroize::Zeroize;
186 self.buffer.zeroize();
187 self.bufpos.zeroize();
188 self.chain_length.zeroize();
189 }
191 }
192 }
193
194 #[cfg(feature = "zeroize")]
195 impl digest::zeroize::ZeroizeOnDrop for $name<'_> {}
196
197 #[doc = "Core"]
198 #[doc = $alg_name]
199 #[doc = "reader state."]
200 #[derive(Clone)]
201 pub struct $reader_name {
202 tshk: $ts_reader_name,
203 }
204
205 impl XofReaderCore for $reader_name {
206 #[inline]
207 fn read_block(&mut self) -> Block<Self> {
208 let mut block = Block::<Self>::default();
209 self.tshk.read(&mut block);
210 block
211 }
212 }
213
214 #[cfg(feature = "zeroize")]
216 impl digest::zeroize::ZeroizeOnDrop for $reader_name {}
217 };
218}
219
220impl_k12_core!(
221 Kt128Core,
222 Kt128ReaderCore,
223 TurboShake128,
224 TurboShake128Reader,
225 32,
226 "KT128",
227);
228impl_k12_core!(
229 Kt256Core,
230 Kt256ReaderCore,
231 TurboShake256,
232 TurboShake256Reader,
233 64,
234 "KT256",
235);
236
237impl BlockSizeUser for Kt128ReaderCore {
238 type BlockSize = U168; }
240
241impl BlockSizeUser for Kt256ReaderCore {
242 type BlockSize = U136; }
244
245#[deprecated(since = "0.4.0-pre", note = "use `Kt128Core` instead")]
247pub type KangarooTwelveCore<'cs> = Kt128Core<'cs>;
248
249#[deprecated(since = "0.4.0-pre", note = "use `Kt128ReaderCore` instead")]
251pub type KangarooTwelveReaderCore = Kt128ReaderCore;
252
253fn length_encode(mut length: usize, buffer: &mut [u8; LENGTH_ENCODE_SIZE]) -> &mut [u8] {
254 let mut bufpos = 0usize;
255 while length > 0 {
256 buffer[bufpos] = (length % 256) as u8;
257 length /= 256;
258 bufpos += 1;
259 }
260 buffer[..bufpos].reverse();
261
262 buffer[bufpos] = bufpos as u8;
263 bufpos += 1;
264
265 &mut buffer[..bufpos]
266}
267
268#[test]
269fn test_length_encode() {
270 let mut buffer = [0u8; LENGTH_ENCODE_SIZE];
271 assert_eq!(length_encode(0, &mut buffer), &[0x00]);
272 assert_eq!(length_encode(12, &mut buffer), &[0x0C, 0x01]);
273 assert_eq!(length_encode(65538, &mut buffer), &[0x01, 0x00, 0x02, 0x03]);
274}