Skip to main content

k12/
lib.rs

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::{self, ExtendableOutput, Update, XofReader};
11
12use core::fmt;
13use digest::{
14    CollisionResistance, ExtendableOutputReset, HashMarker, Reset,
15    common::AlgorithmName,
16    consts::{U16, U32},
17};
18
19mod consts;
20/// Customized variants.
21pub mod custom;
22/// Implementation of TurboSHAKE specialized for computation of chaining values on full nodes
23mod node_turbo_shake;
24/// Implementation of the XOF reader
25mod reader;
26/// Vendored implementation of TurboSHAKE
27mod turbo_shake;
28/// Implementation of the update closure generic over Keccak backend
29mod update;
30/// Utility functions
31mod utils;
32
33pub use custom::*;
34pub use reader::KtReader;
35
36use consts::{CHUNK_SIZE_U64, FINAL_NODE_DS, ROUNDS, SINGLE_NODE_DS};
37use turbo_shake::TurboShake;
38use utils::length_encode;
39
40/// KangarooTwelve hasher generic over rate.
41///
42/// Only `136` and `168` rates are supported which correspond to KT256 and KT128 respectively.
43/// Using other rates will result in a compilation error.
44#[derive(Clone)]
45pub struct Kt<const RATE: usize> {
46    accum_tshk: TurboShake<RATE>,
47    node_tshk: TurboShake<RATE>,
48    consumed_len: u64,
49    keccak: keccak::Keccak,
50}
51
52impl<const RATE: usize> Default for Kt<RATE> {
53    #[inline]
54    fn default() -> Self {
55        const { assert!(matches!(RATE, 136 | 168)) }
56        Self {
57            accum_tshk: Default::default(),
58            node_tshk: Default::default(),
59            consumed_len: 0,
60            keccak: Default::default(),
61        }
62    }
63}
64
65impl<const RATE: usize> fmt::Debug for Kt<RATE> {
66    #[inline]
67    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
68        write!(f, "Kt{} {{ ... }}", 4 * (200 - RATE))
69    }
70}
71
72impl<const RATE: usize> AlgorithmName for Kt<RATE> {
73    #[inline]
74    fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
75        write!(f, "KT{}", 4 * (200 - RATE))
76    }
77}
78
79impl<const RATE: usize> HashMarker for Kt<RATE> {}
80
81impl<const RATE: usize> Update for Kt<RATE> {
82    #[inline]
83    fn update(&mut self, data: &[u8]) {
84        let keccak = self.keccak;
85        let closure = update::Closure::<'_, RATE> { data, kt: self };
86        keccak.with_backend(closure);
87    }
88}
89
90impl<const RATE: usize> Reset for Kt<RATE> {
91    #[inline]
92    fn reset(&mut self) {
93        self.accum_tshk.reset();
94        self.node_tshk.reset();
95        self.consumed_len = 0;
96    }
97}
98
99impl<const RATE: usize> Kt<RATE> {
100    #[inline]
101    fn raw_finalize(&mut self) -> KtReader<RATE> {
102        let keccak = self.keccak;
103
104        // Note that the reader applies permutation before reading from the state,
105        // so we only need to absorb the remaining data and pad the state
106        if self.consumed_len <= CHUNK_SIZE_U64 {
107            self.accum_tshk.pad::<SINGLE_NODE_DS>();
108        } else {
109            keccak.with_p1600::<ROUNDS>(|p1600| {
110                let nodes_len = (self.consumed_len - 1) / CHUNK_SIZE_U64;
111                let partial_node_len = self.consumed_len % CHUNK_SIZE_U64;
112
113                if partial_node_len != 0 {
114                    // TODO: this should be [0u8; {200 - RATE}]
115                    let cv_dst = &mut [0u8; 200][..200 - RATE];
116                    self.node_tshk.finalize_intermediate_node(p1600, cv_dst);
117                    self.accum_tshk.absorb(p1600, cv_dst);
118                }
119
120                length_encode(nodes_len, |enc_len| self.accum_tshk.absorb(p1600, enc_len));
121                self.accum_tshk.absorb(p1600, b"\xFF\xFF");
122                self.accum_tshk.pad::<FINAL_NODE_DS>();
123            });
124        };
125
126        KtReader::new(self.accum_tshk.state(), keccak)
127    }
128}
129
130impl<const RATE: usize> ExtendableOutput for Kt<RATE> {
131    type Reader = KtReader<RATE>;
132
133    #[inline]
134    fn finalize_xof(mut self) -> Self::Reader {
135        self.update(&[0x00]);
136        self.raw_finalize()
137    }
138}
139
140impl<const RATE: usize> ExtendableOutputReset for Kt<RATE> {
141    #[inline]
142    fn finalize_xof_reset(&mut self) -> Self::Reader {
143        self.update(&[0x00]);
144        let reader = self.raw_finalize();
145        self.reset();
146        reader
147    }
148}
149
150impl<const RATE: usize> Drop for Kt<RATE> {
151    fn drop(&mut self) {
152        #[cfg(feature = "zeroize")]
153        {
154            use digest::zeroize::Zeroize;
155            self.consumed_len.zeroize();
156            // `accum_tshk` and `node_tshk` are zeroized by `Drop`
157        }
158    }
159}
160
161#[cfg(feature = "zeroize")]
162impl<const RATE: usize> digest::zeroize::ZeroizeOnDrop for Kt<RATE> {}
163
164/// KT128 hasher.
165pub type Kt128 = Kt<168>;
166/// KT256 hasher.
167pub type Kt256 = Kt<136>;
168
169/// KT128 XOF reader.
170pub type Kt128Reader = KtReader<168>;
171/// KT256 XOF reader.
172pub type Kt256Reader = KtReader<136>;
173
174// https://www.rfc-editor.org/rfc/rfc9861.html#section-7-7
175impl CollisionResistance for Kt128 {
176    type CollisionResistance = U16;
177}
178
179// https://www.rfc-editor.org/rfc/rfc9861.html#section-7-8
180impl CollisionResistance for Kt256 {
181    type CollisionResistance = U32;
182}