1use crate::{
2 counter_high, counter_low, CVBytes, CVWords, IncrementCounter, BLOCK_LEN, IV, MSG_SCHEDULE,
3 OUT_LEN,
4};
5use arrayref::{array_mut_ref, array_ref};
6
7#[inline(always)]
8fn g(state: &mut [u32; 16], a: usize, b: usize, c: usize, d: usize, x: u32, y: u32) {
9 state[a] = state[a].wrapping_add(state[b]).wrapping_add(x);
10 state[d] = (state[d] ^ state[a]).rotate_right(16);
11 state[c] = state[c].wrapping_add(state[d]);
12 state[b] = (state[b] ^ state[c]).rotate_right(12);
13 state[a] = state[a].wrapping_add(state[b]).wrapping_add(y);
14 state[d] = (state[d] ^ state[a]).rotate_right(8);
15 state[c] = state[c].wrapping_add(state[d]);
16 state[b] = (state[b] ^ state[c]).rotate_right(7);
17}
18
19#[inline(always)]
20fn round(state: &mut [u32; 16], msg: &[u32; 16], round: usize) {
21 let schedule = MSG_SCHEDULE[round];
23
24 g(state, 0, 4, 8, 12, msg[schedule[0]], msg[schedule[1]]);
26 g(state, 1, 5, 9, 13, msg[schedule[2]], msg[schedule[3]]);
27 g(state, 2, 6, 10, 14, msg[schedule[4]], msg[schedule[5]]);
28 g(state, 3, 7, 11, 15, msg[schedule[6]], msg[schedule[7]]);
29
30 g(state, 0, 5, 10, 15, msg[schedule[8]], msg[schedule[9]]);
32 g(state, 1, 6, 11, 12, msg[schedule[10]], msg[schedule[11]]);
33 g(state, 2, 7, 8, 13, msg[schedule[12]], msg[schedule[13]]);
34 g(state, 3, 4, 9, 14, msg[schedule[14]], msg[schedule[15]]);
35}
36
37#[inline(always)]
38fn compress_pre(
39 cv: &CVWords,
40 block: &[u8; BLOCK_LEN],
41 block_len: u8,
42 counter: u64,
43 flags: u8,
44) -> [u32; 16] {
45 let block_words = crate::platform::words_from_le_bytes_64(block);
46
47 let mut state = [
48 cv[0],
49 cv[1],
50 cv[2],
51 cv[3],
52 cv[4],
53 cv[5],
54 cv[6],
55 cv[7],
56 IV[0],
57 IV[1],
58 IV[2],
59 IV[3],
60 counter_low(counter),
61 counter_high(counter),
62 block_len as u32,
63 flags as u32,
64 ];
65
66 round(&mut state, &block_words, 0);
67 round(&mut state, &block_words, 1);
68 round(&mut state, &block_words, 2);
69 round(&mut state, &block_words, 3);
70 round(&mut state, &block_words, 4);
71 round(&mut state, &block_words, 5);
72 round(&mut state, &block_words, 6);
73
74 state
75}
76
77pub fn compress_in_place(
78 cv: &mut CVWords,
79 block: &[u8; BLOCK_LEN],
80 block_len: u8,
81 counter: u64,
82 flags: u8,
83) {
84 let state = compress_pre(cv, block, block_len, counter, flags);
85
86 cv[0] = state[0] ^ state[8];
87 cv[1] = state[1] ^ state[9];
88 cv[2] = state[2] ^ state[10];
89 cv[3] = state[3] ^ state[11];
90 cv[4] = state[4] ^ state[12];
91 cv[5] = state[5] ^ state[13];
92 cv[6] = state[6] ^ state[14];
93 cv[7] = state[7] ^ state[15];
94}
95
96pub fn compress_xof(
97 cv: &CVWords,
98 block: &[u8; BLOCK_LEN],
99 block_len: u8,
100 counter: u64,
101 flags: u8,
102) -> [u8; 64] {
103 let mut state = compress_pre(cv, block, block_len, counter, flags);
104 state[0] ^= state[8];
105 state[1] ^= state[9];
106 state[2] ^= state[10];
107 state[3] ^= state[11];
108 state[4] ^= state[12];
109 state[5] ^= state[13];
110 state[6] ^= state[14];
111 state[7] ^= state[15];
112 state[8] ^= cv[0];
113 state[9] ^= cv[1];
114 state[10] ^= cv[2];
115 state[11] ^= cv[3];
116 state[12] ^= cv[4];
117 state[13] ^= cv[5];
118 state[14] ^= cv[6];
119 state[15] ^= cv[7];
120 crate::platform::le_bytes_from_words_64(&state)
121}
122
123pub fn hash1<const N: usize>(
124 input: &[u8; N],
125 key: &CVWords,
126 counter: u64,
127 flags: u8,
128 flags_start: u8,
129 flags_end: u8,
130 out: &mut CVBytes,
131) {
132 debug_assert_eq!(N % BLOCK_LEN, 0, "uneven blocks");
133 let mut cv = *key;
134 let mut block_flags = flags | flags_start;
135 let mut slice = &input[..];
136 while slice.len() >= BLOCK_LEN {
137 if slice.len() == BLOCK_LEN {
138 block_flags |= flags_end;
139 }
140 compress_in_place(
141 &mut cv,
142 array_ref!(slice, 0, BLOCK_LEN),
143 BLOCK_LEN as u8,
144 counter,
145 block_flags,
146 );
147 block_flags = flags;
148 slice = &slice[BLOCK_LEN..];
149 }
150 *out = crate::platform::le_bytes_from_words_32(&cv);
151}
152
153pub fn hash_many<const N: usize>(
154 inputs: &[&[u8; N]],
155 key: &CVWords,
156 mut counter: u64,
157 increment_counter: IncrementCounter,
158 flags: u8,
159 flags_start: u8,
160 flags_end: u8,
161 out: &mut [u8],
162) {
163 debug_assert!(out.len() >= inputs.len() * OUT_LEN, "out too short");
164 for (&input, output) in inputs.iter().zip(out.chunks_exact_mut(OUT_LEN)) {
165 hash1(
166 input,
167 key,
168 counter,
169 flags,
170 flags_start,
171 flags_end,
172 array_mut_ref!(output, 0, OUT_LEN),
173 );
174 if increment_counter.yes() {
175 counter += 1;
176 }
177 }
178}
179
180#[cfg(test)]
181pub mod test {
182 use super::*;
183
184 #[test]
189 fn test_compress() {
190 crate::test::test_compress_fn(compress_in_place, compress_xof);
191 }
192
193 #[test]
195 fn test_hash_many() {
196 crate::test::test_hash_many_fn(hash_many, hash_many);
197 }
198}