1const RC: [u64; 24] = [
8 0x0000000000000001, 0x0000000000008082, 0x800000000000808a, 0x8000000080008000,
9 0x000000000000808b, 0x0000000080000001, 0x8000000080008081, 0x8000000000008009,
10 0x000000000000008a, 0x0000000000000088, 0x0000000080008009, 0x000000008000000a,
11 0x000000008000808b, 0x800000000000008b, 0x8000000000008089, 0x8000000000008003,
12 0x8000000000008002, 0x8000000000000080, 0x000000000000800a, 0x800000008000000a,
13 0x8000000080008081, 0x8000000000008080, 0x0000000080000001, 0x8000000080008008,
14];
15pub fn keccak_f1600(st: &mut [u64; 25]) {
17 let [mut a0, mut a1, mut a2, mut a3, mut a4, mut a5, mut a6, mut a7, mut a8, mut a9, mut a10, mut a11, mut a12, mut a13, mut a14, mut a15, mut a16, mut a17, mut a18, mut a19, mut a20, mut a21, mut a22, mut a23, mut a24] =
21 *st;
22 for &rc in RC.iter() {
23 let c0 = a0 ^ a5 ^ a10 ^ a15 ^ a20;
25 let c1 = a1 ^ a6 ^ a11 ^ a16 ^ a21;
26 let c2 = a2 ^ a7 ^ a12 ^ a17 ^ a22;
27 let c3 = a3 ^ a8 ^ a13 ^ a18 ^ a23;
28 let c4 = a4 ^ a9 ^ a14 ^ a19 ^ a24;
29 let d0 = c4 ^ c1.rotate_left(1);
30 let d1 = c0 ^ c2.rotate_left(1);
31 let d2 = c1 ^ c3.rotate_left(1);
32 let d3 = c2 ^ c4.rotate_left(1);
33 let d4 = c3 ^ c0.rotate_left(1);
34 a0 ^= d0; a5 ^= d0; a10 ^= d0; a15 ^= d0; a20 ^= d0;
35 a1 ^= d1; a6 ^= d1; a11 ^= d1; a16 ^= d1; a21 ^= d1;
36 a2 ^= d2; a7 ^= d2; a12 ^= d2; a17 ^= d2; a22 ^= d2;
37 a3 ^= d3; a8 ^= d3; a13 ^= d3; a18 ^= d3; a23 ^= d3;
38 a4 ^= d4; a9 ^= d4; a14 ^= d4; a19 ^= d4; a24 ^= d4;
39 let b0 = a0;
41 let b1 = a6.rotate_left(44);
42 let b2 = a12.rotate_left(43);
43 let b3 = a18.rotate_left(21);
44 let b4 = a24.rotate_left(14);
45 let b5 = a3.rotate_left(28);
46 let b6 = a9.rotate_left(20);
47 let b7 = a10.rotate_left(3);
48 let b8 = a16.rotate_left(45);
49 let b9 = a22.rotate_left(61);
50 let b10 = a1.rotate_left(1);
51 let b11 = a7.rotate_left(6);
52 let b12 = a13.rotate_left(25);
53 let b13 = a19.rotate_left(8);
54 let b14 = a20.rotate_left(18);
55 let b15 = a4.rotate_left(27);
56 let b16 = a5.rotate_left(36);
57 let b17 = a11.rotate_left(10);
58 let b18 = a17.rotate_left(15);
59 let b19 = a23.rotate_left(56);
60 let b20 = a2.rotate_left(62);
61 let b21 = a8.rotate_left(55);
62 let b22 = a14.rotate_left(39);
63 let b23 = a15.rotate_left(41);
64 let b24 = a21.rotate_left(2);
65 a0 = b0 ^ (!b1 & b2); a1 = b1 ^ (!b2 & b3); a2 = b2 ^ (!b3 & b4); a3 = b3 ^ (!b4 & b0); a4 = b4 ^ (!b0 & b1);
67 a5 = b5 ^ (!b6 & b7); a6 = b6 ^ (!b7 & b8); a7 = b7 ^ (!b8 & b9); a8 = b8 ^ (!b9 & b5); a9 = b9 ^ (!b5 & b6);
68 a10 = b10 ^ (!b11 & b12); a11 = b11 ^ (!b12 & b13); a12 = b12 ^ (!b13 & b14); a13 = b13 ^ (!b14 & b10); a14 = b14 ^ (!b10 & b11);
69 a15 = b15 ^ (!b16 & b17); a16 = b16 ^ (!b17 & b18); a17 = b17 ^ (!b18 & b19); a18 = b18 ^ (!b19 & b15); a19 = b19 ^ (!b15 & b16);
70 a20 = b20 ^ (!b21 & b22); a21 = b21 ^ (!b22 & b23); a22 = b22 ^ (!b23 & b24); a23 = b23 ^ (!b24 & b20); a24 = b24 ^ (!b20 & b21);
71 a0 ^= rc;
73 }
74 *st = [a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24];
75}
76
77#[cfg(target_arch = "x86_64")]
80#[target_feature(enable = "avx2")]
81#[inline]
82unsafe fn rotl64_x4(x: std::arch::x86_64::__m256i, n: i32) -> std::arch::x86_64::__m256i {
83 use std::arch::x86_64::*;
84 _mm256_or_si256(
85 _mm256_sll_epi64(x, _mm_cvtsi32_si128(n)),
86 _mm256_srl_epi64(x, _mm_cvtsi32_si128(64 - n)),
87 )
88}
89
90#[cfg(target_arch = "x86_64")]
94#[target_feature(enable = "avx2")]
95pub(crate) unsafe fn keccak_f1600_x4(st: &mut [std::arch::x86_64::__m256i; 25]) {
96 use std::arch::x86_64::*;
97 macro_rules! xor {
98 ($a:expr, $b:expr) => {
99 _mm256_xor_si256($a, $b)
100 };
101 }
102 let mut a = *st;
103 for &rc in RC.iter() {
104 let c0 = xor!(xor!(xor!(xor!(a[0], a[5]), a[10]), a[15]), a[20]);
106 let c1 = xor!(xor!(xor!(xor!(a[1], a[6]), a[11]), a[16]), a[21]);
107 let c2 = xor!(xor!(xor!(xor!(a[2], a[7]), a[12]), a[17]), a[22]);
108 let c3 = xor!(xor!(xor!(xor!(a[3], a[8]), a[13]), a[18]), a[23]);
109 let c4 = xor!(xor!(xor!(xor!(a[4], a[9]), a[14]), a[19]), a[24]);
110 let d0 = xor!(c4, rotl64_x4(c1, 1));
111 let d1 = xor!(c0, rotl64_x4(c2, 1));
112 let d2 = xor!(c1, rotl64_x4(c3, 1));
113 let d3 = xor!(c2, rotl64_x4(c4, 1));
114 let d4 = xor!(c3, rotl64_x4(c0, 1));
115 a[0] = xor!(a[0], d0); a[5] = xor!(a[5], d0); a[10] = xor!(a[10], d0); a[15] = xor!(a[15], d0); a[20] = xor!(a[20], d0);
116 a[1] = xor!(a[1], d1); a[6] = xor!(a[6], d1); a[11] = xor!(a[11], d1); a[16] = xor!(a[16], d1); a[21] = xor!(a[21], d1);
117 a[2] = xor!(a[2], d2); a[7] = xor!(a[7], d2); a[12] = xor!(a[12], d2); a[17] = xor!(a[17], d2); a[22] = xor!(a[22], d2);
118 a[3] = xor!(a[3], d3); a[8] = xor!(a[8], d3); a[13] = xor!(a[13], d3); a[18] = xor!(a[18], d3); a[23] = xor!(a[23], d3);
119 a[4] = xor!(a[4], d4); a[9] = xor!(a[9], d4); a[14] = xor!(a[14], d4); a[19] = xor!(a[19], d4); a[24] = xor!(a[24], d4);
120 let b0 = a[0];
122 let b1 = rotl64_x4(a[6], 44);
123 let b2 = rotl64_x4(a[12], 43);
124 let b3 = rotl64_x4(a[18], 21);
125 let b4 = rotl64_x4(a[24], 14);
126 let b5 = rotl64_x4(a[3], 28);
127 let b6 = rotl64_x4(a[9], 20);
128 let b7 = rotl64_x4(a[10], 3);
129 let b8 = rotl64_x4(a[16], 45);
130 let b9 = rotl64_x4(a[22], 61);
131 let b10 = rotl64_x4(a[1], 1);
132 let b11 = rotl64_x4(a[7], 6);
133 let b12 = rotl64_x4(a[13], 25);
134 let b13 = rotl64_x4(a[19], 8);
135 let b14 = rotl64_x4(a[20], 18);
136 let b15 = rotl64_x4(a[4], 27);
137 let b16 = rotl64_x4(a[5], 36);
138 let b17 = rotl64_x4(a[11], 10);
139 let b18 = rotl64_x4(a[17], 15);
140 let b19 = rotl64_x4(a[23], 56);
141 let b20 = rotl64_x4(a[2], 62);
142 let b21 = rotl64_x4(a[8], 55);
143 let b22 = rotl64_x4(a[14], 39);
144 let b23 = rotl64_x4(a[15], 41);
145 let b24 = rotl64_x4(a[21], 2);
146 a[0] = xor!(b0, _mm256_andnot_si256(b1, b2)); a[1] = xor!(b1, _mm256_andnot_si256(b2, b3)); a[2] = xor!(b2, _mm256_andnot_si256(b3, b4)); a[3] = xor!(b3, _mm256_andnot_si256(b4, b0)); a[4] = xor!(b4, _mm256_andnot_si256(b0, b1));
148 a[5] = xor!(b5, _mm256_andnot_si256(b6, b7)); a[6] = xor!(b6, _mm256_andnot_si256(b7, b8)); a[7] = xor!(b7, _mm256_andnot_si256(b8, b9)); a[8] = xor!(b8, _mm256_andnot_si256(b9, b5)); a[9] = xor!(b9, _mm256_andnot_si256(b5, b6));
149 a[10] = xor!(b10, _mm256_andnot_si256(b11, b12)); a[11] = xor!(b11, _mm256_andnot_si256(b12, b13)); a[12] = xor!(b12, _mm256_andnot_si256(b13, b14)); a[13] = xor!(b13, _mm256_andnot_si256(b14, b10)); a[14] = xor!(b14, _mm256_andnot_si256(b10, b11));
150 a[15] = xor!(b15, _mm256_andnot_si256(b16, b17)); a[16] = xor!(b16, _mm256_andnot_si256(b17, b18)); a[17] = xor!(b17, _mm256_andnot_si256(b18, b19)); a[18] = xor!(b18, _mm256_andnot_si256(b19, b15)); a[19] = xor!(b19, _mm256_andnot_si256(b15, b16));
151 a[20] = xor!(b20, _mm256_andnot_si256(b21, b22)); a[21] = xor!(b21, _mm256_andnot_si256(b22, b23)); a[22] = xor!(b22, _mm256_andnot_si256(b23, b24)); a[23] = xor!(b23, _mm256_andnot_si256(b24, b20)); a[24] = xor!(b24, _mm256_andnot_si256(b20, b21));
152 a[0] = xor!(a[0], _mm256_set1_epi64x(rc as i64));
154 }
155 *st = a;
156}
157
158#[cfg(target_arch = "x86_64")]
163#[target_feature(enable = "avx2")]
164pub(crate) unsafe fn shake128_x4_absorb_once(
165 blocks: &[[u8; 168]; 4],
166) -> [std::arch::x86_64::__m256i; 25] {
167 use std::arch::x86_64::*;
168 let mut st = [_mm256_setzero_si256(); 25];
169 for (lane, slot) in st.iter_mut().enumerate().take(21) {
170 let l = |s: usize| -> i64 {
171 i64::from_le_bytes(blocks[s][lane * 8..lane * 8 + 8].try_into().unwrap())
172 };
173 *slot = _mm256_set_epi64x(l(3), l(2), l(1), l(0));
174 }
175 keccak_f1600_x4(&mut st);
176 st
177}
178
179#[cfg(target_arch = "x86_64")]
182#[target_feature(enable = "avx2")]
183pub(crate) unsafe fn shake128_x4_squeeze_block(
184 st: &[std::arch::x86_64::__m256i; 25],
185) -> [[u8; 168]; 4] {
186 use std::arch::x86_64::*;
187 let mut out = [[0u8; 168]; 4];
188 let mut lane_bytes = [0u64; 4];
189 for (lane, slot) in st.iter().enumerate().take(21) {
190 _mm256_storeu_si256(lane_bytes.as_mut_ptr() as *mut __m256i, *slot);
191 for (state, &v) in lane_bytes.iter().enumerate() {
192 out[state][lane * 8..lane * 8 + 8].copy_from_slice(&v.to_le_bytes());
193 }
194 }
195 out
196}
197
198#[cfg(target_arch = "x86_64")]
204#[target_feature(enable = "avx2")]
205pub(crate) unsafe fn shake256_x4_128(inputs: &[&[u8]; 4]) -> [[u8; 128]; 4] {
206 use std::arch::x86_64::*;
207 const RATE: usize = 136;
208 let mut blocks = [[0u8; RATE]; 4];
209 for (b, inp) in blocks.iter_mut().zip(inputs.iter()) {
210 debug_assert!(inp.len() < RATE, "shake256_x4_128 expects single-block inputs");
211 b[..inp.len()].copy_from_slice(inp);
212 b[inp.len()] = 0x1f;
213 b[RATE - 1] |= 0x80;
214 }
215 let mut st = [_mm256_setzero_si256(); 25];
216 for (lane, slot) in st.iter_mut().enumerate().take(RATE / 8) {
217 let l = |s: usize| -> i64 {
218 i64::from_le_bytes(blocks[s][lane * 8..lane * 8 + 8].try_into().unwrap())
219 };
220 *slot = _mm256_set_epi64x(l(3), l(2), l(1), l(0));
221 }
222 keccak_f1600_x4(&mut st);
223 let mut out = [[0u8; 128]; 4];
224 let mut lane_bytes = [0u64; 4];
225 for (lane, slot) in st.iter().enumerate().take(16) {
226 _mm256_storeu_si256(lane_bytes.as_mut_ptr() as *mut __m256i, *slot);
227 for (state, &v) in lane_bytes.iter().enumerate() {
228 out[state][lane * 8..lane * 8 + 8].copy_from_slice(&v.to_le_bytes());
229 }
230 }
231 out
232}
233
234pub(crate) fn shake128_absorb(input: &[u8]) -> [u64; 25] {
238 const RATE: usize = 168;
239 const LANES: usize = RATE / 8;
240 let mut st = [0u64; 25];
241 let mut chunks = input.chunks_exact(RATE);
242 for block in &mut chunks {
243 for i in 0..LANES {
244 st[i] ^= u64::from_le_bytes(block[i * 8..i * 8 + 8].try_into().unwrap());
245 }
246 keccak_f1600(&mut st);
247 }
248 let rem = chunks.remainder();
249 let mut block = [0u8; 200];
250 block[..rem.len()].copy_from_slice(rem);
251 block[rem.len()] = 0x1f;
252 block[RATE - 1] |= 0x80;
253 for i in 0..LANES {
254 st[i] ^= u64::from_le_bytes(block[i * 8..i * 8 + 8].try_into().unwrap());
255 }
256 keccak_f1600(&mut st);
257 st
258}
259
260pub(crate) fn shake_absorb(input: &[u8], rate: usize) -> [u64; 25] {
264 let lanes = rate / 8;
265 let mut st = [0u64; 25];
266 let mut chunks = input.chunks_exact(rate);
267 for block in &mut chunks {
268 for (i, lane) in st.iter_mut().enumerate().take(lanes) {
269 *lane ^= u64::from_le_bytes(block[i * 8..i * 8 + 8].try_into().unwrap());
270 }
271 keccak_f1600(&mut st);
272 }
273 let rem = chunks.remainder();
274 let mut block = [0u8; 200];
275 block[..rem.len()].copy_from_slice(rem);
276 block[rem.len()] = 0x1f;
277 block[rate - 1] |= 0x80;
278 for (i, lane) in st.iter_mut().enumerate().take(lanes) {
279 *lane ^= u64::from_le_bytes(block[i * 8..i * 8 + 8].try_into().unwrap());
280 }
281 keccak_f1600(&mut st);
282 st
283}
284
285pub(crate) fn shake_squeeze_block(st: &[u64; 25], out: &mut [u8], rate: usize) {
287 for i in 0..rate / 8 {
288 out[i * 8..i * 8 + 8].copy_from_slice(&st[i].to_le_bytes());
289 }
290}
291
292pub(crate) fn keccak_permute(st: &mut [u64; 25]) {
294 keccak_f1600(st);
295}
296
297fn keccak(rate: usize, delim: u8, input: &[u8], out: &mut [u8]) {
300 let mut st = [0u64; 25];
301 let lanes = rate / 8;
302
303 let mut chunks = input.chunks_exact(rate);
305 for block in &mut chunks {
306 for i in 0..lanes {
307 st[i] ^= u64::from_le_bytes(block[i * 8..i * 8 + 8].try_into().unwrap());
308 }
309 keccak_f1600(&mut st);
310 }
311 let rem = chunks.remainder();
313 let mut block = [0u8; 200];
314 block[..rem.len()].copy_from_slice(rem);
315 block[rem.len()] = delim;
316 block[rate - 1] |= 0x80;
317 for i in 0..lanes {
318 st[i] ^= u64::from_le_bytes(block[i * 8..i * 8 + 8].try_into().unwrap());
319 }
320 keccak_f1600(&mut st);
321
322 let mut off = 0;
324 while off < out.len() {
325 let n = (out.len() - off).min(rate);
326 let mut produced = [0u8; 200];
327 for i in 0..lanes {
328 produced[i * 8..i * 8 + 8].copy_from_slice(&st[i].to_le_bytes());
329 }
330 out[off..off + n].copy_from_slice(&produced[..n]);
331 off += n;
332 if off < out.len() {
333 keccak_f1600(&mut st);
334 }
335 }
336}
337
338pub fn sha3_256_bytes(input: &[u8]) -> [u8; 32] {
339 let mut out = [0u8; 32];
340 keccak(136, 0x06, input, &mut out);
341 out
342}
343pub fn sha3_512_bytes(input: &[u8]) -> [u8; 64] {
344 let mut out = [0u8; 64];
345 keccak(72, 0x06, input, &mut out);
346 out
347}
348pub fn shake128_bytes(input: &[u8], outlen: usize) -> Vec<u8> {
349 let mut out = vec![0u8; outlen];
350 keccak(168, 0x1f, input, &mut out);
351 out
352}
353pub fn shake256_bytes(input: &[u8], outlen: usize) -> Vec<u8> {
354 let mut out = vec![0u8; outlen];
355 keccak(136, 0x1f, input, &mut out);
356 out
357}
358
359fn seq_to_bytes(s: &[i64]) -> Vec<u8> {
362 s.iter().map(|&x| x.rem_euclid(256) as u8).collect()
363}
364fn bytes_to_seq(b: &[u8]) -> logicaffeine_data::LogosSeq<i64> {
365 logicaffeine_data::LogosSeq::from_vec(b.iter().map(|&x| x as i64).collect())
366}
367
368pub fn sha3_256(input: &[i64]) -> logicaffeine_data::LogosSeq<i64> {
370 bytes_to_seq(&sha3_256_bytes(&seq_to_bytes(input)))
371}
372pub fn sha3_512(input: &[i64]) -> logicaffeine_data::LogosSeq<i64> {
374 bytes_to_seq(&sha3_512_bytes(&seq_to_bytes(input)))
375}
376pub fn shake128(
378 input: &[i64],
379 outlen: i64,
380) -> logicaffeine_data::LogosSeq<i64> {
381 bytes_to_seq(&shake128_bytes(&seq_to_bytes(input), outlen.max(0) as usize))
382}
383pub fn shake256(
385 input: &[i64],
386 outlen: i64,
387) -> logicaffeine_data::LogosSeq<i64> {
388 bytes_to_seq(&shake256_bytes(&seq_to_bytes(input), outlen.max(0) as usize))
389}
390
391#[cfg(test)]
392mod tests {
393 use super::*;
394
395 fn hex(b: &[u8]) -> String {
396 b.iter().map(|x| format!("{x:02x}")).collect()
397 }
398
399 #[test]
400 #[cfg(target_arch = "x86_64")]
401 fn shake256_x4_128_matches_four_scalar() {
402 if !std::is_x86_feature_detected!("avx2") {
403 return;
404 }
405 let inputs: [Vec<u8>; 4] = [
407 (0..33u8).collect(),
408 b"logos ml-kem noise stream one".to_vec(),
409 vec![0xABu8; 100],
410 (0..135u8).map(|i| i.wrapping_mul(7)).collect(),
411 ];
412 let refs: [&[u8]; 4] = [&inputs[0], &inputs[1], &inputs[2], &inputs[3]];
413 let got = unsafe { shake256_x4_128(&refs) };
414 for (lane, inp) in refs.iter().enumerate() {
415 let want = shake256_bytes(inp, 128);
416 assert_eq!(&got[lane][..], &want[..], "shake256_x4 lane {lane} must equal scalar SHAKE256");
417 }
418 }
419
420 #[test]
421 fn sha3_matches_nist_kats() {
422 assert_eq!(
424 hex(&sha3_256_bytes(b"")),
425 "a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a"
426 );
427 assert_eq!(
428 hex(&sha3_256_bytes(b"abc")),
429 "3a985da74fe225b2045c172d6bd390bd855f086e3e9d525b46bfe24511431532"
430 );
431 assert_eq!(
432 hex(&sha3_512_bytes(b"")),
433 "a69f73cca23a9ac5c8b567dc185a756e97c982164fe25859e0d1dcc1475c80a615b2123af1f5f94c11e3e9402c3ac558f500199d95b6d3e301758586281dcd26"
434 );
435 assert_eq!(
436 hex(&sha3_512_bytes(b"abc")),
437 "b751850b1a57168a5693cd924b6b096e08f621827444f70d884f5d0240d2712e10e116e9192af3c91a7ec57647e3934057340b4cf408d5a56592f8274eec53f0"
438 );
439 }
440
441 #[test]
442 fn shake_matches_nist_kats() {
443 assert_eq!(
444 hex(&shake128_bytes(b"", 32)),
445 "7f9c2ba4e88f827d616045507605853ed73b8093f6efbc88eb1a6eacfa66ef26"
446 );
447 assert_eq!(
448 hex(&shake256_bytes(b"", 32)),
449 "46b9dd2b0ba88d13233b3feb743eeb243fcd52ea62b81b82b50c27646ed5762fb5cb7df11ff5f88f60c5c7e1eb2e83d7ea4f81b7"[..64].to_string()
450 );
451 let long = shake128_bytes(b"abc", 200);
453 assert_eq!(long.len(), 200);
454 assert_eq!(&hex(&long)[..32], "5881092dd818bf5cf8a3ddb793fbcba7");
455 }
456
457 #[test]
458 #[cfg(target_arch = "x86_64")]
459 fn keccak_f1600_x4_matches_four_scalar_permutations() {
460 use std::arch::x86_64::*;
461 if !std::is_x86_feature_detected!("avx2") {
462 return;
463 }
464 let mut s = 0x9E3779B97F4A7C15u64;
465 let mut rng = || {
466 s = s.wrapping_mul(6364136223846793005).wrapping_add(1442695040888963407);
467 s
468 };
469 for _ in 0..200 {
470 let mut states: [[u64; 25]; 4] = [[0u64; 25]; 4];
472 for st in states.iter_mut() {
473 for lane in st.iter_mut() {
474 *lane = rng();
475 }
476 }
477 let mut want = states;
479 for st in want.iter_mut() {
480 keccak_f1600(st);
481 }
482 let mut packed = [unsafe { _mm256_setzero_si256() }; 25];
484 for (lane, slot) in packed.iter_mut().enumerate() {
485 *slot = unsafe {
486 _mm256_set_epi64x(
487 states[3][lane] as i64,
488 states[2][lane] as i64,
489 states[1][lane] as i64,
490 states[0][lane] as i64,
491 )
492 };
493 }
494 unsafe { keccak_f1600_x4(&mut packed) };
495 for (lane, slot) in packed.iter().enumerate() {
497 let mut out = [0u64; 4];
498 unsafe { _mm256_storeu_si256(out.as_mut_ptr() as *mut __m256i, *slot) };
499 for state in 0..4 {
500 assert_eq!(
501 out[state], want[state][lane],
502 "4-way Keccak lane {lane} of state {state} must equal the scalar permutation"
503 );
504 }
505 }
506 }
507 }
508
509 #[test]
510 fn logos_wrappers_round_trip_bytes() {
511 let input: Vec<i64> = b"abc".iter().map(|&x| x as i64).collect();
512 let out = sha3_256(&input);
513 let bytes: Vec<u8> = out.borrow().iter().map(|&x| x as u8).collect();
514 assert_eq!(hex(&bytes), "3a985da74fe225b2045c172d6bd390bd855f086e3e9d525b46bfe24511431532");
515 }
516}
517
518#[cfg(all(test, target_arch = "x86_64"))]
519mod x4_lanes_ab {
520 use super::*;
521 use logicaffeine_base::Lanes4Word64 as L;
522
523 fn keccak_f1600_x4_lanes(a: &mut [L; 25]) {
526 for &rc in RC.iter() {
527 let c0 = a[0].bitxor(a[5]).bitxor(a[10]).bitxor(a[15]).bitxor(a[20]);
528 let c1 = a[1].bitxor(a[6]).bitxor(a[11]).bitxor(a[16]).bitxor(a[21]);
529 let c2 = a[2].bitxor(a[7]).bitxor(a[12]).bitxor(a[17]).bitxor(a[22]);
530 let c3 = a[3].bitxor(a[8]).bitxor(a[13]).bitxor(a[18]).bitxor(a[23]);
531 let c4 = a[4].bitxor(a[9]).bitxor(a[14]).bitxor(a[19]).bitxor(a[24]);
532 let d0 = c4.bitxor(c1.rotl(1));
533 let d1 = c0.bitxor(c2.rotl(1));
534 let d2 = c1.bitxor(c3.rotl(1));
535 let d3 = c2.bitxor(c4.rotl(1));
536 let d4 = c3.bitxor(c0.rotl(1));
537 a[0] = a[0].bitxor(d0); a[5] = a[5].bitxor(d0); a[10] = a[10].bitxor(d0); a[15] = a[15].bitxor(d0); a[20] = a[20].bitxor(d0);
538 a[1] = a[1].bitxor(d1); a[6] = a[6].bitxor(d1); a[11] = a[11].bitxor(d1); a[16] = a[16].bitxor(d1); a[21] = a[21].bitxor(d1);
539 a[2] = a[2].bitxor(d2); a[7] = a[7].bitxor(d2); a[12] = a[12].bitxor(d2); a[17] = a[17].bitxor(d2); a[22] = a[22].bitxor(d2);
540 a[3] = a[3].bitxor(d3); a[8] = a[8].bitxor(d3); a[13] = a[13].bitxor(d3); a[18] = a[18].bitxor(d3); a[23] = a[23].bitxor(d3);
541 a[4] = a[4].bitxor(d4); a[9] = a[9].bitxor(d4); a[14] = a[14].bitxor(d4); a[19] = a[19].bitxor(d4); a[24] = a[24].bitxor(d4);
542 let b0 = a[0];
543 let b1 = a[6].rotl(44); let b2 = a[12].rotl(43); let b3 = a[18].rotl(21); let b4 = a[24].rotl(14);
544 let b5 = a[3].rotl(28); let b6 = a[9].rotl(20); let b7 = a[10].rotl(3); let b8 = a[16].rotl(45); let b9 = a[22].rotl(61);
545 let b10 = a[1].rotl(1); let b11 = a[7].rotl(6); let b12 = a[13].rotl(25); let b13 = a[19].rotl(8); let b14 = a[20].rotl(18);
546 let b15 = a[4].rotl(27); let b16 = a[5].rotl(36); let b17 = a[11].rotl(10); let b18 = a[17].rotl(15); let b19 = a[23].rotl(56);
547 let b20 = a[2].rotl(62); let b21 = a[8].rotl(55); let b22 = a[14].rotl(39); let b23 = a[15].rotl(41); let b24 = a[21].rotl(2);
548 a[0] = b0.bitxor(b1.andnot(b2)); a[1] = b1.bitxor(b2.andnot(b3)); a[2] = b2.bitxor(b3.andnot(b4)); a[3] = b3.bitxor(b4.andnot(b0)); a[4] = b4.bitxor(b0.andnot(b1));
549 a[5] = b5.bitxor(b6.andnot(b7)); a[6] = b6.bitxor(b7.andnot(b8)); a[7] = b7.bitxor(b8.andnot(b9)); a[8] = b8.bitxor(b9.andnot(b5)); a[9] = b9.bitxor(b5.andnot(b6));
550 a[10] = b10.bitxor(b11.andnot(b12)); a[11] = b11.bitxor(b12.andnot(b13)); a[12] = b12.bitxor(b13.andnot(b14)); a[13] = b13.bitxor(b14.andnot(b10)); a[14] = b14.bitxor(b10.andnot(b11));
551 a[15] = b15.bitxor(b16.andnot(b17)); a[16] = b16.bitxor(b17.andnot(b18)); a[17] = b17.bitxor(b18.andnot(b19)); a[18] = b18.bitxor(b19.andnot(b15)); a[19] = b19.bitxor(b15.andnot(b16));
552 a[20] = b20.bitxor(b21.andnot(b22)); a[21] = b21.bitxor(b22.andnot(b23)); a[22] = b22.bitxor(b23.andnot(b24)); a[23] = b23.bitxor(b24.andnot(b20)); a[24] = b24.bitxor(b20.andnot(b21));
553 a[0] = a[0].bitxor(L::splat(rc));
554 }
555 }
556
557 #[test]
558 fn x4_lanes_matches_raw_and_ab() {
559 use std::arch::x86_64::*;
560 use std::time::Instant;
561 let mut seed = 0x1234_5678_9abc_def0u64;
562 let mut rnd = || { seed = seed.wrapping_mul(6364136223846793005).wrapping_add(1442695040888963407); seed };
563 let states: [[u64; 25]; 4] = std::array::from_fn(|_| std::array::from_fn(|_| rnd()));
564 let mut lanes: [L; 25] = std::array::from_fn(|i| L([states[0][i], states[1][i], states[2][i], states[3][i]]));
565 unsafe {
566 let mut raw: [__m256i; 25] = std::array::from_fn(|i| _mm256_set_epi64x(states[3][i] as i64, states[2][i] as i64, states[1][i] as i64, states[0][i] as i64));
567 keccak_f1600_x4(&mut raw);
568 keccak_f1600_x4_lanes(&mut lanes);
569 for i in 0..25 {
570 let mut got = [0u64; 4];
571 _mm256_storeu_si256(got.as_mut_ptr() as *mut __m256i, raw[i]);
572 assert_eq!(lanes[i].0, got, "Lanes4Word64 x4 Keccak must equal raw keccak_f1600_x4 (lane {i})");
573 }
574 let mut sc = states[0];
576 keccak_f1600(&mut sc);
577 let l0: [u64; 25] = std::array::from_fn(|i| lanes[i].0[0]);
578 assert_eq!(l0, sc, "lane 0 of the x4 must equal one scalar keccak_f1600");
579
580 macro_rules! t {
582 ($l:expr, $init:expr, $body:expr) => {{
583 for _ in 0..100 { let mut s = $init; std::hint::black_box($body(&mut s)); }
584 let start = Instant::now();
585 for _ in 0..20000u32 { let mut s = $init; std::hint::black_box($body(&mut s)); }
586 eprintln!("{:<22} {:>8.1} ns/op", $l, start.elapsed().as_nanos() as f64 / 20000.0);
587 }};
588 }
589 let raw0: [__m256i; 25] = std::array::from_fn(|i| _mm256_set_epi64x(states[3][i] as i64, states[2][i] as i64, states[1][i] as i64, states[0][i] as i64));
590 let lanes0: [L; 25] = std::array::from_fn(|i| L([states[0][i], states[1][i], states[2][i], states[3][i]]));
591 eprintln!("\n=== 4-way Keccak-f[1600] A/B (this box) ===");
592 t!("raw keccak_f1600_x4", raw0, |s: &mut [__m256i; 25]| keccak_f1600_x4(s));
593 t!("lanes x4 (Logos form)", lanes0, |s: &mut [L; 25]| keccak_f1600_x4_lanes(s));
594 }
595 }
596}