k12/
lib.rs

1//! Pure Rust implementation of the KangarooTwelve cryptographic hash
2//! algorithm, based on the reference implementation:
3//!
4//! <https://datatracker.ietf.org/doc/draft-irtf-cfrg-kangarootwelve/>
5
6#![no_std]
7#![doc(
8    html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
9    html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg"
10)]
11#![forbid(unsafe_code)]
12#![warn(missing_docs, rust_2018_idioms)]
13
14pub use digest;
15
16use core::fmt;
17use digest::block_buffer::Eager;
18use digest::consts::{U128, U168};
19use digest::core_api::{
20    AlgorithmName, Block, BlockSizeUser, Buffer, BufferKindUser, CoreWrapper, ExtendableOutputCore,
21    UpdateCore, XofReaderCore, XofReaderCoreWrapper,
22};
23use digest::{ExtendableOutputReset, HashMarker, Reset, Update, XofReader};
24
25use sha3::{TurboShake128, TurboShake128Core, TurboShake128ReaderCore};
26
27const CHUNK_SIZE: usize = 8192;
28const CHAINING_VALUE_SIZE: usize = 32;
29const LENGTH_ENCODE_SIZE: usize = 255;
30
31/// Core [`KangarooTwelve`] hasher state.
32#[derive(Clone)]
33#[allow(non_camel_case_types)]
34pub struct KangarooTwelveCore<'cs> {
35    customization: &'cs [u8],
36    buffer: [u8; CHUNK_SIZE],
37    bufpos: usize,
38    final_tshk: TurboShake128,
39    chain_tshk: TurboShake128,
40    chain_length: usize,
41}
42
43impl<'cs> KangarooTwelveCore<'cs> {
44    /// Creates a new KangarooTwelve instance with the given customization.
45    pub fn new(customization: &'cs [u8]) -> Self {
46        Self {
47            customization,
48            buffer: [0u8; CHUNK_SIZE],
49            bufpos: 0usize,
50            final_tshk: TurboShake128::from_core(<TurboShake128Core>::new(0x06)),
51            chain_tshk: TurboShake128::from_core(<TurboShake128Core>::new(0x0B)),
52            chain_length: 0usize,
53        }
54    }
55}
56
57impl HashMarker for KangarooTwelveCore<'_> {}
58
59impl BlockSizeUser for KangarooTwelveCore<'_> {
60    type BlockSize = U128;
61}
62
63impl BufferKindUser for KangarooTwelveCore<'_> {
64    type BufferKind = Eager;
65}
66
67impl UpdateCore for KangarooTwelveCore<'_> {
68    #[inline]
69    fn update_blocks(&mut self, blocks: &[Block<Self>]) {
70        for block in blocks {
71            self.buffer[self.bufpos..self.bufpos + 128].clone_from_slice(block);
72            self.bufpos += 128;
73
74            if self.bufpos != CHUNK_SIZE {
75                continue;
76            }
77
78            if self.chain_length == 0 {
79                self.final_tshk.update(&self.buffer);
80                self.final_tshk
81                    .update(&[0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
82            } else {
83                let mut result = [0u8; CHAINING_VALUE_SIZE];
84                self.chain_tshk.update(&self.buffer);
85                self.chain_tshk.finalize_xof_reset_into(&mut result);
86                self.final_tshk.update(&result);
87            }
88
89            self.chain_length += 1;
90            self.buffer = [0u8; CHUNK_SIZE];
91            self.bufpos = 0;
92        }
93    }
94}
95
96impl ExtendableOutputCore for KangarooTwelveCore<'_> {
97    type ReaderCore = KangarooTwelveReaderCore;
98
99    #[inline]
100    fn finalize_xof_core(&mut self, buffer: &mut Buffer<Self>) -> Self::ReaderCore {
101        let mut lenbuf = [0u8; LENGTH_ENCODE_SIZE];
102
103        // Digest customization
104        buffer.digest_blocks(self.customization, |block| self.update_blocks(block));
105        buffer.digest_blocks(
106            length_encode(self.customization.len(), &mut lenbuf),
107            |block| self.update_blocks(block),
108        );
109
110        // Read leftover data from buffer
111        self.buffer[self.bufpos..(self.bufpos + buffer.get_pos())]
112            .copy_from_slice(buffer.get_data());
113        self.bufpos += buffer.get_pos();
114
115        // Calculate final node
116        if self.chain_length == 0 {
117            // Input didnot exceed a single chaining value
118            let tshk = TurboShake128::from_core(<TurboShake128Core>::new(0x07))
119                .chain(&self.buffer[..self.bufpos])
120                .finalize_xof_reset();
121            return KangarooTwelveReaderCore { tshk };
122        }
123        // Calculate last chaining value
124        let mut result = [0u8; CHAINING_VALUE_SIZE];
125        self.chain_tshk.update(&self.buffer[..self.bufpos]);
126        self.chain_tshk.finalize_xof_reset_into(&mut result);
127        self.final_tshk.update(&result);
128        // Pad final node calculation
129        self.final_tshk
130            .update(length_encode(self.chain_length, &mut lenbuf));
131        self.final_tshk.update(&[0xff, 0xff]);
132
133        KangarooTwelveReaderCore {
134            tshk: self.final_tshk.finalize_xof_reset(),
135        }
136    }
137}
138
139impl Default for KangarooTwelveCore<'_> {
140    #[inline]
141    fn default() -> Self {
142        Self {
143            customization: &[],
144            buffer: [0u8; CHUNK_SIZE],
145            bufpos: 0usize,
146            final_tshk: TurboShake128::from_core(<TurboShake128Core>::new(0x06)),
147            chain_tshk: TurboShake128::from_core(<TurboShake128Core>::new(0x0B)),
148            chain_length: 0usize,
149        }
150    }
151}
152
153impl Reset for KangarooTwelveCore<'_> {
154    #[inline]
155    fn reset(&mut self) {
156        *self = Self::new(self.customization);
157    }
158}
159
160impl AlgorithmName for KangarooTwelveCore<'_> {
161    fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
162        f.write_str(stringify!(KangarooTwelve))
163    }
164}
165
166impl fmt::Debug for KangarooTwelveCore<'_> {
167    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
168        f.write_str(concat!(stringify!(KangarooTwelveCore), " { ... }"))
169    }
170}
171
172/// Core [`KangarooTwelve`] reader state.
173#[derive(Clone)]
174#[allow(non_camel_case_types)]
175pub struct KangarooTwelveReaderCore {
176    tshk: XofReaderCoreWrapper<TurboShake128ReaderCore>,
177}
178
179impl BlockSizeUser for KangarooTwelveReaderCore {
180    type BlockSize = U168; // TurboSHAKE128 block size
181}
182
183impl XofReaderCore for KangarooTwelveReaderCore {
184    #[inline]
185    fn read_block(&mut self) -> Block<Self> {
186        let mut block = Block::<Self>::default();
187        self.tshk.read(&mut block);
188        block
189    }
190}
191
192/// [`KangarooTwelve`] hasher state.
193pub type KangarooTwelve<'cs> = CoreWrapper<KangarooTwelveCore<'cs>>;
194
195/// [`KangarooTwelve`] reader state.
196pub type KangarooTwelveReader = XofReaderCoreWrapper<KangarooTwelveReaderCore>;
197
198fn length_encode(mut length: usize, buffer: &mut [u8; LENGTH_ENCODE_SIZE]) -> &mut [u8] {
199    let mut bufpos = 0usize;
200    while length > 0 {
201        buffer[bufpos] = (length % 256) as u8;
202        length /= 256;
203        bufpos += 1;
204    }
205    buffer[..bufpos].reverse();
206
207    buffer[bufpos] = bufpos as u8;
208    bufpos += 1;
209
210    &mut buffer[..bufpos]
211}
212
213#[test]
214fn test_length_encode() {
215    let mut buffer = [0u8; LENGTH_ENCODE_SIZE];
216    assert_eq!(length_encode(0, &mut buffer), &[0x00]);
217    assert_eq!(length_encode(12, &mut buffer), &[0x0C, 0x01]);
218    assert_eq!(length_encode(65538, &mut buffer), &[0x01, 0x00, 0x02, 0x03]);
219}