1#![no_std]
5#![cfg_attr(docsrs, feature(doc_auto_cfg))]
6#![doc = include_str!("../README.md")]
7#![forbid(unsafe_code)]
8#![warn(missing_docs)]
9
10use core::mem::size_of;
11
12#[cfg(feature = "zeroize")]
13use zeroize::{Zeroize, ZeroizeOnDrop};
14
15#[inline(always)]
17const fn round_constant(round: u64) -> u64 {
18 ((0xfu64 - round) << 4) | round
19}
20
21#[derive(Clone, Debug, Default)]
25pub struct State {
26 x: [u64; 5],
27}
28
29const fn round(x: [u64; 5], c: u64) -> [u64; 5] {
31 let x0 = x[0] ^ x[4];
33 let x2 = x[2] ^ x[1] ^ c; let x4 = x[4] ^ x[3];
35
36 let tx0 = x0 ^ (!x[1] & x2);
37 let tx1 = x[1] ^ (!x2 & x[3]);
38 let tx2 = x2 ^ (!x[3] & x4);
39 let tx3 = x[3] ^ (!x4 & x0);
40 let tx4 = x4 ^ (!x0 & x[1]);
41 let tx1 = tx1 ^ tx0;
42 let tx3 = tx3 ^ tx2;
43 let tx0 = tx0 ^ tx4;
44
45 let x0 = tx0 ^ tx0.rotate_right(9);
47 let x1 = tx1 ^ tx1.rotate_right(22);
48 let x2 = tx2 ^ tx2.rotate_right(5);
49 let x3 = tx3 ^ tx3.rotate_right(7);
50 let x4 = tx4 ^ tx4.rotate_right(34);
51 [
52 tx0 ^ x0.rotate_right(19),
53 tx1 ^ x1.rotate_right(39),
54 !(tx2 ^ x2.rotate_right(1)),
55 tx3 ^ x3.rotate_right(10),
56 tx4 ^ x4.rotate_right(7),
57 ]
58}
59
60impl State {
61 pub fn new(x0: u64, x1: u64, x2: u64, x3: u64, x4: u64) -> Self {
63 State {
64 x: [x0, x1, x2, x3, x4],
65 }
66 }
67
68 #[cfg(not(feature = "no_unroll"))]
69 pub fn permute_12(&mut self) {
71 self.x = round(
75 round(
76 round(
77 round(
78 round(
79 round(
80 round(
81 round(
82 round(round(round(round(self.x, 0xf0), 0xe1), 0xd2), 0xc3),
83 0xb4,
84 ),
85 0xa5,
86 ),
87 0x96,
88 ),
89 0x87,
90 ),
91 0x78,
92 ),
93 0x69,
94 ),
95 0x5a,
96 ),
97 0x4b,
98 );
99 }
100
101 #[cfg(feature = "no_unroll")]
102 pub fn permute_12(&mut self) {
104 self.x = [
105 0xf0, 0xe1, 0xd2, 0xc3, 0xb4, 0xa5, 0x96, 0x87, 0x78, 0x69, 0x5a, 0x4b,
106 ]
107 .into_iter()
108 .fold(self.x, round);
109 }
110
111 #[cfg(not(feature = "no_unroll"))]
112 pub fn permute_8(&mut self) {
114 self.x = round(
115 round(
116 round(
117 round(
118 round(round(round(round(self.x, 0xb4), 0xa5), 0x96), 0x87),
119 0x78,
120 ),
121 0x69,
122 ),
123 0x5a,
124 ),
125 0x4b,
126 );
127 }
128
129 #[cfg(feature = "no_unroll")]
130 pub fn permute_8(&mut self) {
132 self.x = [0xb4, 0xa5, 0x96, 0x87, 0x78, 0x69, 0x5a, 0x4b]
133 .into_iter()
134 .fold(self.x, round);
135 }
136
137 pub fn permute_1(&mut self) {
139 self.x = round(self.x, 0x4b);
140 }
141
142 pub fn permute_n(&mut self, rounds: usize) {
146 debug_assert!(rounds <= 12);
147
148 let start = 12 - rounds;
149 self.x = (start..12).fold(self.x, |x, round_index| {
150 round(x, round_constant(round_index as u64))
151 });
152 }
153
154 pub fn as_bytes(&self) -> [u8; 40] {
156 let mut bytes = [0u8; size_of::<u64>() * 5];
157 for (dst, src) in bytes
158 .chunks_exact_mut(size_of::<u64>())
159 .zip(self.x.into_iter())
160 {
161 dst.copy_from_slice(&u64::to_be_bytes(src));
162 }
163 bytes
164 }
165}
166
167impl core::ops::Index<usize> for State {
168 type Output = u64;
169
170 #[inline(always)]
171 fn index(&self, index: usize) -> &Self::Output {
172 &self.x[index]
173 }
174}
175
176impl core::ops::IndexMut<usize> for State {
177 #[inline(always)]
178 fn index_mut(&mut self, index: usize) -> &mut Self::Output {
179 &mut self.x[index]
180 }
181}
182
183impl TryFrom<&[u64]> for State {
184 type Error = ();
185
186 fn try_from(value: &[u64]) -> Result<Self, Self::Error> {
187 match value.len() {
188 5 => Ok(Self::new(value[0], value[1], value[2], value[3], value[4])),
189 _ => Err(()),
190 }
191 }
192}
193
194impl From<&[u64; 5]> for State {
195 fn from(value: &[u64; 5]) -> Self {
196 Self { x: *value }
197 }
198}
199
200impl TryFrom<&[u8]> for State {
201 type Error = ();
202
203 fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
204 if value.len() != core::mem::size_of::<u64>() * 5 {
205 return Err(());
206 }
207
208 let mut state = Self::default();
209 for (src, dst) in value
210 .chunks_exact(core::mem::size_of::<u64>())
211 .zip(state.x.iter_mut())
212 {
213 *dst = u64::from_be_bytes(src.try_into().unwrap());
214 }
215 Ok(state)
216 }
217}
218
219impl From<&[u8; size_of::<u64>() * 5]> for State {
220 fn from(value: &[u8; size_of::<u64>() * 5]) -> Self {
221 let mut state = Self::default();
222 for (src, dst) in value
223 .chunks_exact(core::mem::size_of::<u64>())
224 .zip(state.x.iter_mut())
225 {
226 *dst = u64::from_be_bytes(src.try_into().unwrap());
227 }
228 state
229 }
230}
231
232impl AsRef<[u64]> for State {
233 fn as_ref(&self) -> &[u64] {
234 &self.x
235 }
236}
237
238#[cfg(feature = "zeroize")]
239impl Drop for State {
240 fn drop(&mut self) {
241 self.x.zeroize();
242 }
243}
244
245#[cfg(feature = "zeroize")]
246impl ZeroizeOnDrop for State {}
247
248#[cfg(test)]
249mod tests {
250 use super::*;
251
252 #[test]
253 fn round_constants() {
254 assert_eq!(round_constant(0), 0xf0);
255 assert_eq!(round_constant(1), 0xe1);
256 assert_eq!(round_constant(2), 0xd2);
257 assert_eq!(round_constant(3), 0xc3);
258 assert_eq!(round_constant(4), 0xb4);
259 assert_eq!(round_constant(5), 0xa5);
260 assert_eq!(round_constant(6), 0x96);
261 assert_eq!(round_constant(7), 0x87);
262 assert_eq!(round_constant(8), 0x78);
263 assert_eq!(round_constant(9), 0x69);
264 assert_eq!(round_constant(10), 0x5a);
265 assert_eq!(round_constant(11), 0x4b);
266 }
267
268 #[test]
269 fn one_round() {
270 let state = round(
271 [
272 0x0123456789abcdef,
273 0x23456789abcdef01,
274 0x456789abcdef0123,
275 0x6789abcdef012345,
276 0x89abcde01234567f,
277 ],
278 0x1f,
279 );
280 assert_eq!(
281 state,
282 [
283 0x3c1748c9be2892ce,
284 0x5eafb305cd26164f,
285 0xf9470254bb3a4213,
286 0xf0428daf0c5d3948,
287 0x281375af0b294899
288 ]
289 );
290 }
291
292 #[test]
293 fn state_permute_12() {
294 let mut state = State::new(
295 0x0123456789abcdef,
296 0xef0123456789abcd,
297 0xcdef0123456789ab,
298 0xabcdef0123456789,
299 0x89abcdef01234567,
300 );
301 state.permute_12();
302 assert_eq!(state[0], 0x206416dfc624bb14);
303 assert_eq!(state[1], 0x1b0c47a601058aab);
304 assert_eq!(state[2], 0x8934cfc93814cddd);
305 assert_eq!(state[3], 0xa9738d287a748e4b);
306 assert_eq!(state[4], 0xddd934f058afc7e1);
307 }
308
309 #[test]
310 fn state_permute_8() {
311 let mut state = State::new(
312 0x0123456789abcdef,
313 0xef0123456789abcd,
314 0xcdef0123456789ab,
315 0xabcdef0123456789,
316 0x89abcdef01234567,
317 );
318 state.permute_8();
319 assert_eq!(state[0], 0x67ed228272f46eee);
320 assert_eq!(state[1], 0x80bc0b097aad7944);
321 assert_eq!(state[2], 0x2fa599382c6db215);
322 assert_eq!(state[3], 0x368133fae2f7667a);
323 assert_eq!(state[4], 0x28cefb195a7c651c);
324 }
325
326 #[test]
327 fn state_permute_n() {
328 let mut state = State::new(
329 0x0123456789abcdef,
330 0xef0123456789abcd,
331 0xcdef0123456789ab,
332 0xabcdef0123456789,
333 0x89abcdef01234567,
334 );
335 let mut state2 = state.clone();
336
337 state.permute_8();
338 state2.permute_n(8);
339 assert_eq!(state.x, state2.x);
340
341 state.permute_12();
342 state2.permute_n(12);
343 assert_eq!(state.x, state2.x);
344 }
345
346 #[test]
347 fn state_convert_bytes() {
348 let state = State::new(
349 0x0123456789abcdef,
350 0xef0123456789abcd,
351 0xcdef0123456789ab,
352 0xabcdef0123456789,
353 0x89abcdef01234567,
354 );
355 let bytes = state.as_bytes();
356
357 let state2 = State::try_from(&bytes[..]);
359 assert_eq!(state2.expect("try_from bytes").x, state.x);
360
361 let state2 = State::from(&bytes);
362 assert_eq!(state2.x, state.x);
363 }
364}