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)]
g(state: &mut [u32; 16], a: usize, b: usize, c: usize, d: usize, x: u32, y: u32)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)]
round(state: &mut [u32; 16], msg: &[u32; 16], round: usize)20 fn round(state: &mut [u32; 16], msg: &[u32; 16], round: usize) {
21     // Select the message schedule based on the round.
22     let schedule = MSG_SCHEDULE[round];
23 
24     // Mix the columns.
25     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     // Mix the diagonals.
31     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)]
compress_pre( cv: &CVWords, block: &[u8; BLOCK_LEN], block_len: u8, counter: u64, flags: u8, ) -> [u32; 16]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 
compress_in_place( cv: &mut CVWords, block: &[u8; BLOCK_LEN], block_len: u8, counter: u64, flags: u8, )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 = 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 
compress_xof( cv: &CVWords, block: &[u8; BLOCK_LEN], block_len: u8, counter: u64, flags: u8, ) -> [u8; 64]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 = 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 
hash1<const N: usize>( input: &[u8; N], key: &CVWords, counter: u64, flags: u8, flags_start: u8, flags_end: u8, out: &mut CVBytes, )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 = *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 
hash_many<const N: usize>( inputs: &[&[u8; N]], key: &CVWords, mut counter: u64, increment_counter: IncrementCounter, flags: u8, flags_start: u8, flags_end: u8, out: &mut [u8], )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, 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)]
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]
test_compress()189     fn test_compress() {
190         crate::test::test_compress_fn(compress_in_place, compress_xof);
191     }
192 
193     // Ditto.
194     #[test]
test_hash_many()195     fn test_hash_many() {
196         crate::test::test_hash_many_fn(hash_many, hash_many);
197     }
198 }
199