1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
use crate::{bits_to_rate, keccakp::KeccakP, EncodedLen, Hasher, IntoXof, KeccakState, Xof};
fn encode_len(len: usize) -> EncodedLen {
let len_view = (len as u64).to_be_bytes();
let offset = len_view.iter().position(|i| *i != 0).unwrap_or(8);
let mut buffer = [0u8; 9];
buffer[..8].copy_from_slice(&len_view);
buffer[8] = 8 - offset as u8;
EncodedLen { offset, buffer }
}
#[derive(Clone)]
pub struct KangarooTwelve<T> {
state: KeccakState<KeccakP>,
current_chunk: KeccakState<KeccakP>,
custom_string: Option<T>,
written: usize,
chunks: usize,
}
impl<T> KangarooTwelve<T> {
const MAX_CHUNK_SIZE: usize = 8192;
pub fn new(custom_string: T) -> Self {
let rate = bits_to_rate(128);
KangarooTwelve {
state: KeccakState::new(rate, 0),
current_chunk: KeccakState::new(rate, 0x0b),
custom_string: Some(custom_string),
written: 0,
chunks: 0,
}
}
}
impl<T: AsRef<[u8]>> Hasher for KangarooTwelve<T> {
fn update(&mut self, input: &[u8]) {
let mut to_absorb = input;
if self.chunks == 0 {
let todo = core::cmp::min(Self::MAX_CHUNK_SIZE - self.written, to_absorb.len());
self.state.update(&to_absorb[..todo]);
self.written += todo;
to_absorb = &to_absorb[todo..];
if to_absorb.len() > 0 && self.written == Self::MAX_CHUNK_SIZE {
self.state.update(&[0x03, 0, 0, 0, 0, 0, 0, 0]);
self.written = 0;
self.chunks += 1;
}
}
while to_absorb.len() > 0 {
if self.written == Self::MAX_CHUNK_SIZE {
let mut chunk_hash = [0u8; 32];
let current_chunk = self.current_chunk.clone();
self.current_chunk.reset();
current_chunk.finalize(&mut chunk_hash);
self.state.update(&chunk_hash);
self.written = 0;
self.chunks += 1;
}
let todo = core::cmp::min(Self::MAX_CHUNK_SIZE - self.written, to_absorb.len());
self.current_chunk.update(&to_absorb[..todo]);
self.written += todo;
to_absorb = &to_absorb[todo..];
}
}
fn finalize(self, output: &mut [u8]) {
let mut xof = self.into_xof();
xof.squeeze(output);
}
}
#[derive(Clone)]
pub struct KangarooTwelveXof {
state: KeccakState<KeccakP>,
}
impl<T: AsRef<[u8]>> IntoXof for KangarooTwelve<T> {
type Xof = KangarooTwelveXof;
fn into_xof(mut self) -> KangarooTwelveXof {
let custom_string = self
.custom_string
.take()
.expect("KangarooTwelve cannot be initialized without custom_string; qed");
let encoded_len = encode_len(custom_string.as_ref().len());
self.update(custom_string.as_ref());
self.update(encoded_len.value());
if self.chunks == 0 {
self.state.delim = 0x07;
} else {
let encoded_chunks = encode_len(self.chunks);
let mut tmp_chunk = [0u8; 32];
self.current_chunk.finalize(&mut tmp_chunk);
self.state.update(&tmp_chunk);
self.state.update(encoded_chunks.value());
self.state.update(&[0xff, 0xff]);
self.state.delim = 0x06;
}
KangarooTwelveXof { state: self.state }
}
}
impl Xof for KangarooTwelveXof {
fn squeeze(&mut self, output: &mut [u8]) {
self.state.squeeze(output);
}
}