1 | use crate::{ |
2 | counter_high, counter_low, CVBytes, CVWords, IncrementCounter, BLOCK_LEN, IV, MSG_SCHEDULE, |
3 | OUT_LEN, |
4 | }; |
5 | use arrayref::{array_mut_ref, array_ref}; |
6 | |
7 | #[inline (always)] |
8 | fn 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)] |
20 | fn round(state: &mut [u32; 16], msg: &[u32; 16], round: usize) { |
21 | // Select the message schedule based on the round. |
22 | let schedule: [usize; 16] = MSG_SCHEDULE[round]; |
23 | |
24 | // Mix the columns. |
25 | g(state, a:0, b:4, c:8, d:12, x:msg[schedule[0]], y:msg[schedule[1]]); |
26 | g(state, a:1, b:5, c:9, d:13, x:msg[schedule[2]], y:msg[schedule[3]]); |
27 | g(state, a:2, b:6, c:10, d:14, x:msg[schedule[4]], y:msg[schedule[5]]); |
28 | g(state, a:3, b:7, c:11, d:15, x:msg[schedule[6]], y:msg[schedule[7]]); |
29 | |
30 | // Mix the diagonals. |
31 | g(state, a:0, b:5, c:10, d:15, x:msg[schedule[8]], y:msg[schedule[9]]); |
32 | g(state, a:1, b:6, c:11, d:12, x:msg[schedule[10]], y:msg[schedule[11]]); |
33 | g(state, a:2, b:7, c:8, d:13, x:msg[schedule[12]], y:msg[schedule[13]]); |
34 | g(state, a:3, b:4, c:9, d:14, x:msg[schedule[14]], y:msg[schedule[15]]); |
35 | } |
36 | |
37 | #[inline (always)] |
38 | fn 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 | |
77 | pub 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: [u32; 16] = 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 | |
96 | pub 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: [u32; 16] = 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 | |
123 | pub 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: [u32; 8] = *key; |
134 | let mut block_flags: u8 = flags | flags_start; |
135 | let mut slice: &[u8] = &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 | block: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 | |
153 | pub 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: &[u8; N], output: &mut [u8]) 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 | out:array_mut_ref!(output, 0, OUT_LEN), |
173 | ); |
174 | if increment_counter.yes() { |
175 | counter += 1; |
176 | } |
177 | } |
178 | } |
179 | |
180 | #[cfg (test)] |
181 | pub mod test { |
182 | use super::*; |
183 | |
184 | // This is basically testing the portable implementation against itself, |
185 | // but it also checks that compress_in_place and compress_xof are |
186 | // consistent. And there are tests against the reference implementation and |
187 | // against hardcoded test vectors elsewhere. |
188 | #[test ] |
189 | fn test_compress() { |
190 | crate::test::test_compress_fn(compress_in_place, compress_xof); |
191 | } |
192 | |
193 | // Ditto. |
194 | #[test ] |
195 | fn test_hash_many() { |
196 | crate::test::test_hash_many_fn(hash_many, hash_many); |
197 | } |
198 | } |
199 | |