1use crate::constants::FLAT_TO_TOWER_BIT_MASKS_8;
22use crate::towers::bit::Bit;
23use crate::{
24 CanonicalDeserialize, CanonicalSerialize, Flat, HardwareField, PackableField, PackedFlat,
25 TowerField, constants,
26};
27use core::ops::{Add, AddAssign, BitXor, Mul, MulAssign, Sub, SubAssign};
28use serde::{Deserialize, Serialize};
29use zeroize::Zeroize;
30
31#[cfg(not(feature = "table-math"))]
32#[repr(align(64))]
33struct CtConvertBasisU8<const N: usize>([u8; N]);
34
35#[cfg(not(feature = "table-math"))]
36static TOWER_TO_FLAT_BASIS_8: CtConvertBasisU8<8> =
37 CtConvertBasisU8(constants::RAW_TOWER_TO_FLAT_8);
38
39#[cfg(not(feature = "table-math"))]
40static FLAT_TO_TOWER_BASIS_8: CtConvertBasisU8<8> =
41 CtConvertBasisU8(constants::RAW_FLAT_TO_TOWER_8);
42
43#[cfg(feature = "table-math")]
54const EXP_TABLE: [u8; 256] = generate_exp_table();
55
56#[cfg(feature = "table-math")]
61const LOG_TABLE: [u8; 256] = generate_log_table();
62
63#[cfg(feature = "table-math")]
64const TAU_TABLE: [u8; 256] = generate_tau_table();
65
66#[derive(Copy, Clone, Default, Debug, Eq, PartialEq, Serialize, Deserialize, Zeroize)]
70#[repr(transparent)]
71pub struct Block8(pub u8);
72
73impl Block8 {
74 pub const fn new(val: u8) -> Self {
76 Self(val)
77 }
78
79 #[inline(always)]
81 pub fn square(self) -> Self {
82 #[cfg(feature = "table-math")]
83 {
84 if self.0 == 0 {
85 return Self::ZERO;
86 }
87
88 let k = (LOG_TABLE[self.0 as usize] as usize) << 1;
89 let idx = if k >= 255 { k - 255 } else { k };
90
91 Self(EXP_TABLE[idx])
92 }
93
94 #[cfg(not(feature = "table-math"))]
95 {
96 let mut s = self.0 as u16;
99 s = (s | (s << 4)) & 0x0f0f;
100 s = (s | (s << 2)) & 0x3333;
101 s = (s | (s << 1)) & 0x5555;
102
103 let hi = s >> 8;
104 let s = (s & 0x00ff) ^ (hi ^ (hi << 1) ^ (hi << 3) ^ (hi << 4));
105
106 let hi = s >> 8;
107
108 Block8(((s & 0x00ff) ^ (hi ^ (hi << 1) ^ (hi << 3) ^ (hi << 4))) as u8)
109 }
110 }
111
112 #[inline(always)]
113 pub(crate) fn mul_tau(self) -> Self {
114 #[cfg(feature = "table-math")]
115 {
116 Block8(TAU_TABLE[self.0 as usize])
117 }
118
119 #[cfg(not(feature = "table-math"))]
120 {
121 Block8(mul_tau_fold(self.0))
122 }
123 }
124}
125
126impl TowerField for Block8 {
127 const BITS: usize = 8;
128 const ZERO: Self = Block8(0);
129 const ONE: Self = Block8(1);
130
131 const EXTENSION_TAU: Self = Block8(0x20);
132
133 fn invert(&self) -> Self {
134 #[cfg(feature = "table-math")]
135 {
136 if self.0 == 0 {
137 return Self::ZERO;
138 }
139
140 let i = LOG_TABLE[self.0 as usize] as usize;
141 Block8(EXP_TABLE[255 - i])
142 }
143
144 #[cfg(not(feature = "table-math"))]
145 {
146 let x = *self;
150 let x2 = x * x;
151 let x4 = x2 * x2;
152 let x8 = x4 * x4;
153 let x16 = x8 * x8;
154 let x32 = x16 * x16;
155 let x64 = x32 * x32;
156 let x128 = x64 * x64;
157
158 x128 * x64 * x32 * x16 * x8 * x4 * x2
160 }
161 }
162
163 fn from_uniform_bytes(bytes: &[u8; 32]) -> Self {
164 Self(bytes[0])
165 }
166}
167
168impl Add for Block8 {
170 type Output = Self;
171
172 fn add(self, rhs: Self) -> Self::Output {
173 Self(self.0.bitxor(rhs.0))
174 }
175}
176
177impl Sub for Block8 {
178 type Output = Self;
179
180 fn sub(self, rhs: Self) -> Self::Output {
181 self.add(rhs)
182 }
183}
184
185impl Mul for Block8 {
187 type Output = Self;
188
189 #[inline]
190 fn mul(self, rhs: Self) -> Self::Output {
191 #[cfg(feature = "table-math")]
192 {
193 if self.0 == 0 || rhs.0 == 0 {
195 return Self::ZERO;
196 }
197
198 let i = LOG_TABLE[self.0 as usize] as usize;
202 let j = LOG_TABLE[rhs.0 as usize] as usize;
203
204 let k = i + j;
208 let idx = if k >= 255 { k - 255 } else { k };
209
210 Self(EXP_TABLE[idx])
212 }
213
214 #[cfg(not(feature = "table-math"))]
215 {
216 #[cfg(target_arch = "aarch64")]
217 {
218 neon::mul_8(self, rhs)
219 }
220
221 #[cfg(not(target_arch = "aarch64"))]
222 {
223 let mut a = self.0;
224 let mut b = rhs.0;
225 let mut res = 0u8;
226
227 for _ in 0..8 {
230 let bit = b & 1;
231 let mask = 0u8.wrapping_sub(bit);
232 res ^= a & mask;
233
234 let high_bit = a >> 7;
235 let overflow_mask = 0u8.wrapping_sub(high_bit);
236 a = (a << 1) ^ (0x1B & overflow_mask);
237
238 b >>= 1;
239 }
240
241 Self(res)
242 }
243 }
244 }
245}
246
247impl AddAssign for Block8 {
248 fn add_assign(&mut self, rhs: Self) {
249 *self = *self + rhs;
250 }
251}
252
253impl SubAssign for Block8 {
254 fn sub_assign(&mut self, rhs: Self) {
255 *self = *self - rhs;
256 }
257}
258
259impl MulAssign for Block8 {
260 fn mul_assign(&mut self, rhs: Self) {
261 *self = *self * rhs;
262 }
263}
264
265impl CanonicalSerialize for Block8 {
266 #[inline]
267 fn serialized_size(&self) -> usize {
268 1
269 }
270
271 #[inline]
272 fn serialize(&self, writer: &mut [u8]) -> Result<(), ()> {
273 if writer.is_empty() {
274 return Err(());
275 }
276
277 writer[0] = self.0;
278
279 Ok(())
280 }
281}
282
283impl CanonicalDeserialize for Block8 {
284 fn deserialize(bytes: &[u8]) -> Result<Self, ()> {
285 if bytes.is_empty() {
286 return Err(());
287 }
288
289 Ok(Self(bytes[0]))
290 }
291}
292
293impl From<u8> for Block8 {
294 #[inline]
295 fn from(val: u8) -> Self {
296 Self::new(val)
297 }
298}
299
300impl From<u32> for Block8 {
301 #[inline]
302 fn from(val: u32) -> Self {
303 Self(val as u8)
304 }
305}
306
307impl From<u64> for Block8 {
308 #[inline]
309 fn from(val: u64) -> Self {
310 Self(val as u8)
311 }
312}
313
314impl From<u128> for Block8 {
315 #[inline]
316 fn from(val: u128) -> Self {
317 Self(val as u8)
318 }
319}
320
321impl From<Bit> for Block8 {
326 #[inline(always)]
327 fn from(val: Bit) -> Self {
328 Self(val.get())
329 }
330}
331
332pub const PACKED_WIDTH_8: usize = 16;
339
340#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
342#[repr(C, align(16))]
343pub struct PackedBlock8(pub [Block8; PACKED_WIDTH_8]);
344
345impl PackedBlock8 {
346 #[inline(always)]
348 pub fn zero() -> Self {
349 Self([Block8::ZERO; PACKED_WIDTH_8])
350 }
351}
352
353impl PackableField for Block8 {
354 type Packed = PackedBlock8;
355
356 const WIDTH: usize = PACKED_WIDTH_8;
357
358 #[inline(always)]
359 fn pack(chunk: &[Self]) -> Self::Packed {
360 assert!(
361 chunk.len() >= PACKED_WIDTH_8,
362 "PackableField::pack: input slice too short",
363 );
364
365 let mut arr = [Self::ZERO; PACKED_WIDTH_8];
366 arr.copy_from_slice(&chunk[..PACKED_WIDTH_8]);
367
368 PackedBlock8(arr)
369 }
370
371 #[inline(always)]
372 fn unpack(packed: Self::Packed, output: &mut [Self]) {
373 assert!(
374 output.len() >= PACKED_WIDTH_8,
375 "PackableField::unpack: output slice too short",
376 );
377
378 output[..PACKED_WIDTH_8].copy_from_slice(&packed.0);
379 }
380}
381
382impl Add for PackedBlock8 {
383 type Output = Self;
384
385 #[inline(always)]
386 fn add(self, rhs: Self) -> Self {
387 let mut res = [Block8::ZERO; PACKED_WIDTH_8];
388 for ((out, l), r) in res.iter_mut().zip(self.0.iter()).zip(rhs.0.iter()) {
389 *out = *l + *r;
390 }
391
392 Self(res)
393 }
394}
395
396impl AddAssign for PackedBlock8 {
397 #[inline(always)]
398 fn add_assign(&mut self, rhs: Self) {
399 for (l, r) in self.0.iter_mut().zip(rhs.0.iter()) {
400 *l += *r;
401 }
402 }
403}
404
405impl Sub for PackedBlock8 {
406 type Output = Self;
407
408 #[inline(always)]
409 fn sub(self, rhs: Self) -> Self {
410 self.add(rhs)
411 }
412}
413
414impl SubAssign for PackedBlock8 {
415 #[inline(always)]
416 fn sub_assign(&mut self, rhs: Self) {
417 self.add_assign(rhs);
418 }
419}
420
421impl Mul for PackedBlock8 {
422 type Output = Self;
423
424 #[inline(always)]
425 fn mul(self, rhs: Self) -> Self {
426 #[cfg(target_arch = "aarch64")]
427 {
428 let mut res = [Block8::ZERO; PACKED_WIDTH_8];
429 for ((out, l), r) in res.iter_mut().zip(self.0.iter()).zip(rhs.0.iter()) {
430 *out = mul_iso_8(*l, *r);
431 }
432
433 Self(res)
434 }
435
436 #[cfg(not(target_arch = "aarch64"))]
437 {
438 let mut res = [Block8::ZERO; PACKED_WIDTH_8];
439 for ((out, l), r) in res.iter_mut().zip(self.0.iter()).zip(rhs.0.iter()) {
440 *out = *l * *r;
441 }
442
443 Self(res)
444 }
445 }
446}
447
448impl MulAssign for PackedBlock8 {
449 #[inline(always)]
450 fn mul_assign(&mut self, rhs: Self) {
451 *self = *self * rhs;
452 }
453}
454
455impl Mul<Block8> for PackedBlock8 {
456 type Output = Self;
457
458 #[inline(always)]
459 fn mul(self, rhs: Block8) -> Self {
460 let mut res = [Block8::ZERO; PACKED_WIDTH_8];
461 for (out, v) in res.iter_mut().zip(self.0.iter()) {
462 *out = *v * rhs;
463 }
464
465 Self(res)
466 }
467}
468
469impl HardwareField for Block8 {
474 #[inline(always)]
475 fn to_hardware(self) -> Flat<Self> {
476 #[cfg(feature = "table-math")]
477 {
478 Flat::from_raw(apply_matrix_8(self, &constants::TOWER_TO_FLAT_8))
479 }
480
481 #[cfg(not(feature = "table-math"))]
482 {
483 Flat::from_raw(Block8(map_ct_8(self.0, &TOWER_TO_FLAT_BASIS_8.0)))
484 }
485 }
486
487 #[inline(always)]
488 fn from_hardware(value: Flat<Self>) -> Self {
489 let value = value.into_raw();
490 #[cfg(feature = "table-math")]
491 {
492 apply_matrix_8(value, &constants::FLAT_TO_TOWER_8)
493 }
494
495 #[cfg(not(feature = "table-math"))]
496 {
497 Block8(map_ct_8(value.0, &FLAT_TO_TOWER_BASIS_8.0))
498 }
499 }
500
501 #[inline(always)]
502 fn add_hardware(lhs: Flat<Self>, rhs: Flat<Self>) -> Flat<Self> {
503 Flat::from_raw(lhs.into_raw() + rhs.into_raw())
504 }
505
506 #[inline(always)]
507 fn add_hardware_packed(lhs: PackedFlat<Self>, rhs: PackedFlat<Self>) -> PackedFlat<Self> {
508 let lhs = lhs.into_raw();
509 let rhs = rhs.into_raw();
510 #[cfg(target_arch = "aarch64")]
511 {
512 PackedFlat::from_raw(neon::add_packed_8(lhs, rhs))
513 }
514
515 #[cfg(not(target_arch = "aarch64"))]
516 {
517 PackedFlat::from_raw(lhs + rhs)
518 }
519 }
520
521 #[inline(always)]
522 fn mul_hardware(lhs: Flat<Self>, rhs: Flat<Self>) -> Flat<Self> {
523 let lhs = lhs.into_raw();
524 let rhs = rhs.into_raw();
525 #[cfg(target_arch = "aarch64")]
526 {
527 Flat::from_raw(neon::mul_8(lhs, rhs))
528 }
529
530 #[cfg(not(target_arch = "aarch64"))]
531 {
532 let a_tower = Self::from_hardware(Flat::from_raw(lhs));
533 let b_tower = Self::from_hardware(Flat::from_raw(rhs));
534
535 (a_tower * b_tower).to_hardware()
536 }
537 }
538
539 #[inline(always)]
540 fn mul_hardware_packed(lhs: PackedFlat<Self>, rhs: PackedFlat<Self>) -> PackedFlat<Self> {
541 let lhs = lhs.into_raw();
542 let rhs = rhs.into_raw();
543
544 #[cfg(target_arch = "aarch64")]
545 {
546 PackedFlat::from_raw(neon::mul_flat_packed_8(lhs, rhs))
547 }
548
549 #[cfg(not(target_arch = "aarch64"))]
550 {
551 let mut l = [Self::ZERO; <Self as PackableField>::WIDTH];
552 let mut r = [Self::ZERO; <Self as PackableField>::WIDTH];
553 let mut res = [Self::ZERO; <Self as PackableField>::WIDTH];
554
555 Self::unpack(lhs, &mut l);
556 Self::unpack(rhs, &mut r);
557
558 for i in 0..<Self as PackableField>::WIDTH {
559 res[i] = Self::mul_hardware(Flat::from_raw(l[i]), Flat::from_raw(r[i])).into_raw();
560 }
561
562 PackedFlat::from_raw(Self::pack(&res))
563 }
564 }
565
566 #[inline(always)]
567 fn mul_hardware_scalar_packed(lhs: PackedFlat<Self>, rhs: Flat<Self>) -> PackedFlat<Self> {
568 let broadcasted = PackedBlock8([rhs.into_raw(); PACKED_WIDTH_8]);
569 Self::mul_hardware_packed(lhs, PackedFlat::from_raw(broadcasted))
570 }
571
572 #[inline(always)]
573 fn tower_bit_from_hardware(value: Flat<Self>, bit_idx: usize) -> u8 {
574 let mask = FLAT_TO_TOWER_BIT_MASKS_8[bit_idx];
575
576 let mut v = value.into_raw().0 & mask;
578 v ^= v >> 4;
579 v ^= v >> 2;
580 v ^= v >> 1;
581
582 v & 1
583 }
584}
585
586#[cfg(target_arch = "aarch64")]
591#[inline(always)]
592fn mul_iso_8(a: Block8, b: Block8) -> Block8 {
593 let a_f = a.to_hardware();
594 let b_f = b.to_hardware();
595 let c_f = Flat::from_raw(neon::mul_8(a_f.into_raw(), b_f.into_raw()));
596
597 c_f.to_tower()
598}
599
600#[cfg(feature = "table-math")]
601#[inline(always)]
602fn apply_matrix_8(val: Block8, table: &[u8; 256]) -> Block8 {
603 let idx = val.0 as usize;
604
605 Block8(unsafe { *table.get_unchecked(idx) })
608}
609
610#[cfg(not(feature = "table-math"))]
611#[inline(always)]
612fn map_ct_8(x: u8, basis: &[u8; 8]) -> u8 {
613 let mut acc = 0u8;
614 let mut i = 0usize;
615
616 while i < 8 {
617 let bit = (x >> i) & 1;
618 let mask = 0u8.wrapping_sub(bit);
619 acc ^= basis[i] & mask;
620 i += 1;
621 }
622
623 acc
624}
625
626const fn mul_tau_fold(a: u8) -> u8 {
627 let p = (a as u16) << 5;
628
629 let hi = p >> 8;
630 let s = (p & 0x00ff) ^ (hi ^ (hi << 1) ^ (hi << 3) ^ (hi << 4));
631
632 let hi = s >> 8;
633
634 ((s & 0x00ff) ^ (hi ^ (hi << 1) ^ (hi << 3) ^ (hi << 4))) as u8
635}
636
637#[cfg(feature = "table-math")]
638const fn generate_exp_table() -> [u8; 256] {
639 let mut table = [0u8; 256];
640 let mut val: u8 = 1;
641
642 let mut i = 0;
649 while i < 256 {
650 table[i] = val;
651
652 let high_bit = val & 0x80;
656 let mut shifted = val << 1;
657
658 if high_bit != 0 {
662 shifted ^= 0x1B;
663 }
664
665 val = shifted ^ val;
666 i += 1;
667 }
668
669 table
670}
671
672#[cfg(feature = "table-math")]
673const fn generate_log_table() -> [u8; 256] {
674 let mut table = [0u8; 256];
675
676 let mut val: u8 = 1;
685 let mut i = 0;
686
687 while i < 255 {
688 table[val as usize] = i as u8;
689
690 let high_bit = val & 0x80;
691 let mut shifted = val << 1;
692
693 if high_bit != 0 {
694 shifted ^= 0x1B;
695 }
696
697 val = shifted ^ val;
698
699 i += 1;
700 }
701
702 table
705}
706
707#[cfg(feature = "table-math")]
708const fn generate_tau_table() -> [u8; 256] {
709 let mut table = [0u8; 256];
710 let mut i = 0;
711
712 while i < 256 {
713 table[i] = mul_tau_fold(i as u8);
714 i += 1;
715 }
716
717 table
718}
719
720#[cfg(feature = "table-math")]
721const fn gf8_mul_const(mut a: u8, mut b: u8) -> u8 {
722 let mut res = 0u8;
723 let mut i = 0;
724
725 while i < 8 {
726 if b & 1 == 1 {
727 res ^= a;
728 }
729
730 let carry = a & 0x80;
731
732 a <<= 1;
733
734 if carry != 0 {
735 a ^= 0x1B;
736 }
737
738 b >>= 1;
739 i += 1;
740 }
741
742 res
743}
744
745#[cfg(feature = "table-math")]
746const _: () = {
747 assert!(EXP_TABLE[0] == 1, "EXP_TABLE[0] is not g^0");
748
749 let mut x: usize = 0;
750 while x < 256 {
751 assert!(
752 TAU_TABLE[x] == gf8_mul_const(x as u8, <Block8 as TowerField>::EXTENSION_TAU.0),
753 "TAU_TABLE is not multiplication by EXTENSION_TAU"
754 );
755
756 if x < 255 {
757 assert!(
758 EXP_TABLE[x + 1] == gf8_mul_const(EXP_TABLE[x], 3),
759 "EXP_TABLE is not the power chain of the generator 3"
760 );
761 }
762
763 if x != 0 {
764 let i = LOG_TABLE[x] as usize;
765
766 assert!(
767 EXP_TABLE[i] as usize == x,
768 "EXP_TABLE does not invert LOG_TABLE"
769 );
770 assert!(
771 gf8_mul_const(x as u8, EXP_TABLE[255 - i]) == 1,
772 "EXP_TABLE[255 - LOG_TABLE[x]] is not the inverse of x"
773 );
774 }
775
776 x += 1;
777 }
778};
779
780#[cfg(target_arch = "aarch64")]
785mod neon {
786 use super::*;
787 use core::arch::aarch64::*;
788 use core::mem::transmute;
789
790 const _: () = assert!(
791 constants::POLY_8 == 0x1b,
792 "reduction tables and verus twins hardcode R = 0x1b"
793 );
794
795 #[inline(always)]
796 pub fn add_packed_8(lhs: PackedBlock8, rhs: PackedBlock8) -> PackedBlock8 {
797 unsafe {
798 let res = veorq_u8(
799 transmute::<[Block8; 16], uint8x16_t>(lhs.0),
800 transmute::<[Block8; 16], uint8x16_t>(rhs.0),
801 );
802 transmute(res)
803 }
804 }
805
806 #[inline(always)]
807 pub fn mul_8(a: Block8, b: Block8) -> Block8 {
808 unsafe {
809 let a_poly = transmute::<uint8x8_t, poly8x8_t>(vdup_n_u8(a.0));
812 let b_poly = transmute::<uint8x8_t, poly8x8_t>(vdup_n_u8(b.0));
813
814 let prod = vmull_p8(a_poly, b_poly);
817
818 let prod_u16 = vgetq_lane_u16(transmute::<poly16x8_t, uint16x8_t>(prod), 0);
820
821 let l = (prod_u16 & 0xFF) as u8;
822 let h = (prod_u16 >> 8) as u8;
823
824 let r_val = constants::POLY_8; let h_poly = transmute::<uint8x8_t, poly8x8_t>(vdup_n_u8(h));
829 let r_poly = transmute::<uint8x8_t, poly8x8_t>(vdup_n_u8(r_val));
830 let h_red = vmull_p8(h_poly, r_poly);
831
832 let h_red_u16 = vgetq_lane_u16(transmute::<poly16x8_t, uint16x8_t>(h_red), 0);
833
834 let folded = (h_red_u16 & 0xFF) as u8;
835 let carry = (h_red_u16 >> 8) as u8;
836
837 let mut res = l ^ folded;
838
839 let c_poly = transmute::<uint8x8_t, poly8x8_t>(vdup_n_u8(carry));
843 let c_red = vmull_p8(c_poly, r_poly);
844 let c_red_u16 = vgetq_lane_u16(transmute::<poly16x8_t, uint16x8_t>(c_red), 0);
845
846 res ^= (c_red_u16 & 0xFF) as u8;
847
848 Block8(res)
849 }
850 }
851
852 #[inline(always)]
855 pub fn mul_flat_packed_8(lhs: PackedBlock8, rhs: PackedBlock8) -> PackedBlock8 {
856 unsafe {
857 let a: uint8x16_t = transmute(lhs.0);
858 let b: uint8x16_t = transmute(rhs.0);
859
860 let a_lo = vget_low_u8(a);
862 let a_hi = vget_high_u8(a);
863 let b_lo = vget_low_u8(b);
864 let b_hi = vget_high_u8(b);
865
866 let res_lo = vmull_p8(
869 transmute::<uint8x8_t, poly8x8_t>(a_lo),
870 transmute::<uint8x8_t, poly8x8_t>(b_lo),
871 );
872 let res_hi = vmull_p8(
873 transmute::<uint8x8_t, poly8x8_t>(a_hi),
874 transmute::<uint8x8_t, poly8x8_t>(b_hi),
875 );
876
877 let tbl_lo = vld1q_u8(
880 [
881 0x00, 0x1b, 0x36, 0x2d, 0x6c, 0x77, 0x5a, 0x41, 0xd8, 0xc3, 0xee, 0xf5, 0xb4,
882 0xaf, 0x82, 0x99,
883 ]
884 .as_ptr(),
885 );
886
887 let tbl_hi = vld1q_u8(
888 [
889 0x00, 0xab, 0x4d, 0xe6, 0x9a, 0x31, 0xd7, 0x7c, 0x2f, 0x84, 0x62, 0xc9, 0xb5,
890 0x1e, 0xf8, 0x53,
891 ]
892 .as_ptr(),
893 );
894
895 let reduce_tbl = |val_poly: poly16x8_t| -> uint8x8_t {
899 let val: uint16x8_t = transmute(val_poly);
900
901 let data = vmovn_u16(val);
903 let carry_u16 = vshrq_n_u16(val, 8);
904 let carry = vmovn_u16(carry_u16);
905
906 let mask_lo = vdup_n_u8(0x0F);
908 let h_lo = vand_u8(carry, mask_lo);
909 let h_hi = vshr_n_u8(carry, 4);
910
911 let r_lo = vqtbl1_u8(tbl_lo, h_lo);
916 let r_hi = vqtbl1_u8(tbl_hi, h_hi);
917
918 veor_u8(data, veor_u8(r_lo, r_hi))
920 };
921
922 let final_lo = reduce_tbl(res_lo);
923 let final_hi = reduce_tbl(res_hi);
924
925 let res = vcombine_u8(final_lo, final_hi);
928
929 PackedBlock8(transmute::<uint8x16_t, [Block8; 16]>(res))
930 }
931 }
932}
933
934#[cfg(test)]
935mod tests {
936 use super::*;
937 use rand::{RngExt, rng};
938
939 #[test]
944 fn tower_constants() {
945 assert_eq!(Block8::EXTENSION_TAU, Block8(0x20));
948 }
949
950 #[test]
951 fn add_truth() {
952 let zero = Block8::ZERO;
953 let one = Block8::ONE;
954
955 assert_eq!(zero + zero, zero);
956 assert_eq!(zero + one, one);
957 assert_eq!(one + zero, one);
958 assert_eq!(one + one, zero);
959 }
960
961 #[test]
962 fn mul_truth() {
963 let zero = Block8::ZERO;
964 let one = Block8::ONE;
965
966 assert_eq!(zero * zero, zero);
967 assert_eq!(zero * one, zero);
968 assert_eq!(one * one, one);
969 }
970
971 #[test]
972 fn add() {
973 assert_eq!(Block8(5) + Block8(3), Block8(6));
976 }
977
978 #[test]
979 fn mul_simple() {
980 assert_eq!(Block8(2) * Block8(2), Block8(4));
983 }
984
985 #[test]
986 fn mul_overflow() {
987 assert_eq!(Block8(0x57) * Block8(0x83), Block8(0xC1));
991 }
992
993 #[test]
994 fn square_exhaustive() {
995 for i in 0u16..=255 {
996 let x = Block8(i as u8);
997 assert_eq!(x.square(), x * x, "Block8 square mismatch at {i:#04x}");
998 }
999 }
1000
1001 #[test]
1002 fn mul_tau_exhaustive() {
1003 for i in 0u16..=255 {
1004 let x = Block8(i as u8);
1005 assert_eq!(
1006 x.mul_tau(),
1007 x * Block8::EXTENSION_TAU,
1008 "Block8 mul_tau mismatch at {i:#04x}"
1009 );
1010 }
1011 }
1012
1013 #[test]
1014 fn security_zeroize() {
1015 let mut secret_val = Block8::from(0xFF_u32);
1016 assert_ne!(secret_val, Block8::ZERO);
1017
1018 secret_val.zeroize();
1019
1020 assert_eq!(secret_val, Block8::ZERO);
1021 assert_eq!(secret_val.0, 0, "Block8 memory leak detected");
1022 }
1023
1024 #[test]
1025 fn inversion_exhaustive() {
1026 for i in 0u8..=255 {
1028 let val = Block8(i);
1029
1030 if val == Block8::ZERO {
1031 assert_eq!(val.invert(), Block8::ZERO, "invert(0) must return 0");
1034 } else {
1035 let inv = val.invert();
1038 let product = val * inv;
1039
1040 assert_eq!(
1041 product,
1042 Block8::ONE,
1043 "Inversion identity failed: a * a^-1 != 1"
1044 );
1045 }
1046 }
1047 }
1048
1049 #[test]
1054 fn isomorphism_roundtrip() {
1055 let mut rng = rng();
1056 for _ in 0..1000 {
1057 let val = Block8::from(rng.random::<u8>());
1058
1059 assert_eq!(
1062 val.to_hardware().to_tower(),
1063 val,
1064 "Block8 isomorphism roundtrip failed"
1065 );
1066 }
1067 }
1068
1069 #[test]
1070 fn isomorphism_is_frobenius() {
1071 for i in 0u16..=255 {
1072 let x = Block8(i as u8);
1073
1074 let mut x8 = x;
1075 for _ in 0..3 {
1076 x8 = x8.square();
1077 }
1078
1079 let mut x32 = x8;
1080 for _ in 0..2 {
1081 x32 = x32.square();
1082 }
1083
1084 assert_eq!(
1085 x.to_hardware().into_raw(),
1086 x32,
1087 "Block8 to_hardware is not a^32 at {i:#04x}"
1088 );
1089 assert_eq!(
1090 Flat::from_raw(x).to_tower(),
1091 x8,
1092 "Block8 from_hardware is not a^8 at {i:#04x}"
1093 );
1094 }
1095 }
1096
1097 #[test]
1098 fn parity_masks_match_from_hardware() {
1099 for x in 0u16..=255 {
1102 let x_flat = x as u8;
1103 let tower = Block8::from_hardware(Flat::from_raw(Block8(x_flat))).0;
1104
1105 for (k, &mask) in FLAT_TO_TOWER_BIT_MASKS_8.iter().enumerate() {
1106 let parity = ((x_flat & mask).count_ones() & 1) as u8;
1107 let bit = (tower >> k) & 1;
1108 assert_eq!(
1109 parity, bit,
1110 "Block8 mask mismatch at x={x_flat:#04x}, k={k}"
1111 );
1112
1113 let via_api = Flat::from_raw(Block8(x_flat)).tower_bit(k);
1114 assert_eq!(via_api, bit, "Block8 tower_bit_from_hardware mismatch");
1115 }
1116 }
1117 }
1118
1119 #[test]
1120 fn flat_mul_homomorphism() {
1121 let mut rng = rng();
1122 for _ in 0..1000 {
1123 let a = Block8::from(rng.random::<u8>());
1124 let b = Block8::from(rng.random::<u8>());
1125
1126 let expected_flat = (a * b).to_hardware();
1127 let actual_flat = a.to_hardware() * b.to_hardware();
1128
1129 assert_eq!(
1131 actual_flat, expected_flat,
1132 "Block8 flat multiplication mismatch"
1133 );
1134 }
1135 }
1136
1137 #[test]
1138 fn packed_consistency() {
1139 let mut rng = rng();
1140 for _ in 0..100 {
1141 let mut a_vals = [Block8::ZERO; 16];
1142 let mut b_vals = [Block8::ZERO; 16];
1143
1144 for i in 0..16 {
1145 a_vals[i] = Block8::from(rng.random::<u8>());
1146 b_vals[i] = Block8::from(rng.random::<u8>());
1147 }
1148
1149 let a_flat_vals = a_vals.map(|x| x.to_hardware());
1150 let b_flat_vals = b_vals.map(|x| x.to_hardware());
1151 let a_packed = Flat::<Block8>::pack(&a_flat_vals);
1152 let b_packed = Flat::<Block8>::pack(&b_flat_vals);
1153
1154 let add_res = Block8::add_hardware_packed(a_packed, b_packed);
1156
1157 let mut add_out = [Block8::ZERO.to_hardware(); 16];
1158 Flat::<Block8>::unpack(add_res, &mut add_out);
1159
1160 for i in 0..16 {
1161 assert_eq!(
1162 add_out[i],
1163 (a_vals[i] + b_vals[i]).to_hardware(),
1164 "Block8 packed add mismatch"
1165 );
1166 }
1167
1168 let mul_res = Block8::mul_hardware_packed(a_packed, b_packed);
1170
1171 let mut mul_out = [Block8::ZERO.to_hardware(); 16];
1172 Flat::<Block8>::unpack(mul_res, &mut mul_out);
1173
1174 for i in 0..16 {
1175 assert_eq!(
1176 mul_out[i],
1177 (a_vals[i] * b_vals[i]).to_hardware(),
1178 "Block8 packed mul mismatch"
1179 );
1180 }
1181 }
1182 }
1183
1184 #[test]
1189 fn pack_unpack_roundtrip() {
1190 let mut rng = rng();
1191 let mut data = [Block8::ZERO; PACKED_WIDTH_8];
1192
1193 for v in data.iter_mut() {
1194 *v = Block8(rng.random());
1195 }
1196
1197 let packed = Block8::pack(&data);
1198 let mut unpacked = [Block8::ZERO; PACKED_WIDTH_8];
1199 Block8::unpack(packed, &mut unpacked);
1200
1201 assert_eq!(data, unpacked, "Block8 pack/unpack roundtrip failed");
1202 }
1203
1204 #[test]
1205 fn packed_add_consistency() {
1206 let mut rng = rng();
1207 let mut a_vals = [Block8::ZERO; PACKED_WIDTH_8];
1208 let mut b_vals = [Block8::ZERO; PACKED_WIDTH_8];
1209
1210 for i in 0..PACKED_WIDTH_8 {
1211 a_vals[i] = Block8(rng.random());
1212 b_vals[i] = Block8(rng.random());
1213 }
1214
1215 let a_packed = Block8::pack(&a_vals);
1216 let b_packed = Block8::pack(&b_vals);
1217 let res_packed = a_packed + b_packed;
1218
1219 let mut res_unpacked = [Block8::ZERO; PACKED_WIDTH_8];
1220 Block8::unpack(res_packed, &mut res_unpacked);
1221
1222 for i in 0..PACKED_WIDTH_8 {
1223 assert_eq!(
1224 res_unpacked[i],
1225 a_vals[i] + b_vals[i],
1226 "Block8 packed add mismatch at index {}",
1227 i
1228 );
1229 }
1230 }
1231
1232 #[test]
1233 fn packed_mul_consistency() {
1234 let mut rng = rng();
1235
1236 for _ in 0..1000 {
1237 let mut a_arr = [Block8::ZERO; PACKED_WIDTH_8];
1238 let mut b_arr = [Block8::ZERO; PACKED_WIDTH_8];
1239
1240 for i in 0..PACKED_WIDTH_8 {
1241 let val_a: u8 = rng.random();
1242 let val_b: u8 = rng.random();
1243 a_arr[i] = Block8(val_a);
1244 b_arr[i] = Block8(val_b);
1245 }
1246
1247 let a_packed = PackedBlock8(a_arr);
1248 let b_packed = PackedBlock8(b_arr);
1249 let c_packed = a_packed * b_packed;
1250
1251 let mut c_expected = [Block8::ZERO; PACKED_WIDTH_8];
1252 for i in 0..PACKED_WIDTH_8 {
1253 c_expected[i] = a_arr[i] * b_arr[i];
1254 }
1255
1256 assert_eq!(c_packed.0, c_expected, "SIMD Block8 mismatch!");
1257 }
1258 }
1259}