Skip to main content

k12/
block_api.rs

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                // Digest customization
110                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                // Read leftover data from buffer
121                self.buffer[self.bufpos..(self.bufpos + buffer.get_pos())]
122                    .copy_from_slice(buffer.get_data());
123                self.bufpos += buffer.get_pos();
124
125                // Calculate final node
126                if self.chain_length == 0 {
127                    // Input did not exceed a single chaining value
128                    let tshk = $ts_name::<0x07>::default()
129                        .chain(&self.buffer[..self.bufpos])
130                        .finalize_xof_reset();
131                    return $reader_name { tshk };
132                }
133
134                // Calculate last chaining value
135                self.process_chaining_chunk();
136
137                // Pad final node calculation
138                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                    // final_tshk and chain_tshk zeroized by their Drop impl
190                }
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        // `Sha3ReaderCore` and the wrapper are zeroized by their Drop impls
215        #[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; // TurboSHAKE128 block size
239}
240
241impl BlockSizeUser for Kt256ReaderCore {
242    type BlockSize = U136; // TurboSHAKE256 block size
243}
244
245/// Core KT128 hasher state.
246#[deprecated(since = "0.4.0-pre", note = "use `Kt128Core` instead")]
247pub type KangarooTwelveCore<'cs> = Kt128Core<'cs>;
248
249/// Core KT128 reader state.
250#[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}