1use core::iter::{Product, Sum};
12use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
13use core::{array, ptr};
14
15use p3_field::{
16 Algebra, Field, PackedField, PackedFieldPow2, PackedValue, PrimeCharacteristicRing,
17};
18use rand::distr::{Distribution, StandardUniform};
19use rand::{Rng, RngExt};
20
21use super::engine::{invert_slice, mul_slice};
22use super::{ByteMatrix, Rijndael8b};
23use crate::Gf2;
24use crate::gf2::characteristic_two_methods;
25
26#[cfg(all(
32 target_arch = "x86_64",
33 target_feature = "gfni",
34 target_feature = "avx512f",
35 target_feature = "avx512bw"
36))]
37pub(super) const PACKING_WIDTH: usize = 64;
38
39#[cfg(not(all(
45 target_arch = "x86_64",
46 target_feature = "gfni",
47 target_feature = "avx512f",
48 target_feature = "avx512bw"
49)))]
50pub(super) const PACKING_WIDTH: usize = 16;
51
52#[derive(Clone, Copy, Debug, PartialEq, Eq)]
54#[repr(transparent)]
56#[must_use]
57pub struct PackedRijndael8b<const N: usize>([Rijndael8b; N]);
58
59impl<const N: usize> Default for PackedRijndael8b<N> {
60 #[inline]
61 fn default() -> Self {
62 Self::ZERO
63 }
64}
65
66impl<const N: usize> PackedRijndael8b<N> {
67 #[inline]
69 const fn splat(value: Rijndael8b) -> Self {
70 Self([value; N])
71 }
72
73 #[inline]
75 const fn bytes(&self) -> &[u8; N] {
76 unsafe { &*ptr::from_ref(self).cast::<[u8; N]>() }
80 }
81
82 #[inline]
84 const fn bytes_mut(&mut self) -> &mut [u8; N] {
85 unsafe { &mut *ptr::from_mut(self).cast::<[u8; N]>() }
87 }
88
89 #[inline]
91 pub fn apply(mut self, map: ByteMatrix) -> Self {
92 map.apply_slice(self.bytes_mut());
93 self
94 }
95
96 #[inline]
102 pub fn frobenius(self, power_log: usize) -> Self {
103 self.apply(Rijndael8b::frobenius_map(power_log))
104 }
105
106 #[inline]
112 pub fn invert_or_zero(mut self) -> Self {
113 invert_slice(self.bytes_mut());
114 self
115 }
116}
117
118impl<const N: usize> From<Rijndael8b> for PackedRijndael8b<N> {
119 #[inline]
120 fn from(value: Rijndael8b) -> Self {
121 Self::splat(value)
122 }
123}
124
125impl<const N: usize> From<Gf2> for PackedRijndael8b<N> {
126 #[inline]
128 fn from(value: Gf2) -> Self {
129 Self::splat(Rijndael8b::from(value))
130 }
131}
132
133impl<const N: usize> Add for PackedRijndael8b<N> {
134 type Output = Self;
135
136 #[inline]
137 #[allow(clippy::suspicious_arithmetic_impl)]
138 fn add(self, rhs: Self) -> Self {
139 Self(array::from_fn(|i| self.0[i] + rhs.0[i]))
141 }
142}
143
144impl<const N: usize> Sub for PackedRijndael8b<N> {
145 type Output = Self;
146
147 #[inline]
148 #[allow(clippy::suspicious_arithmetic_impl)]
149 fn sub(self, rhs: Self) -> Self {
150 self + rhs
152 }
153}
154
155impl<const N: usize> Neg for PackedRijndael8b<N> {
156 type Output = Self;
157
158 #[inline]
159 fn neg(self) -> Self {
160 self
162 }
163}
164
165impl<const N: usize> Mul for PackedRijndael8b<N> {
166 type Output = Self;
167
168 #[inline]
169 fn mul(self, rhs: Self) -> Self {
170 let mut out = self;
171 mul_slice(out.bytes_mut(), rhs.bytes());
172 out
173 }
174}
175
176impl<const N: usize> Div for PackedRijndael8b<N> {
177 type Output = Self;
178
179 #[inline]
182 #[allow(clippy::suspicious_arithmetic_impl)]
183 fn div(self, rhs: Self) -> Self {
184 assert!(!rhs.bytes().contains(&0), "tried to invert zero in a block");
185 self * rhs.invert_or_zero()
186 }
187}
188
189impl<const N: usize> DivAssign for PackedRijndael8b<N> {
190 #[inline]
191 fn div_assign(&mut self, rhs: Self) {
192 *self = *self / rhs;
193 }
194}
195
196impl<const N: usize> Add<Rijndael8b> for PackedRijndael8b<N> {
197 type Output = Self;
198
199 #[inline]
200 fn add(self, rhs: Rijndael8b) -> Self {
201 self + Self::splat(rhs)
202 }
203}
204
205impl<const N: usize> Add<PackedRijndael8b<N>> for Rijndael8b {
206 type Output = PackedRijndael8b<N>;
207
208 #[inline]
209 fn add(self, rhs: PackedRijndael8b<N>) -> Self::Output {
210 rhs + self
211 }
212}
213
214impl<const N: usize> Sub<Rijndael8b> for PackedRijndael8b<N> {
215 type Output = Self;
216
217 #[inline]
218 fn sub(self, rhs: Rijndael8b) -> Self {
219 self - Self::splat(rhs)
220 }
221}
222
223impl<const N: usize> Sub<PackedRijndael8b<N>> for Rijndael8b {
224 type Output = PackedRijndael8b<N>;
225
226 #[inline]
227 fn sub(self, rhs: PackedRijndael8b<N>) -> Self::Output {
228 PackedRijndael8b::splat(self) - rhs
229 }
230}
231
232impl<const N: usize> Mul<Rijndael8b> for PackedRijndael8b<N> {
233 type Output = Self;
234
235 #[inline]
239 fn mul(self, rhs: Rijndael8b) -> Self {
240 self * Self::splat(rhs)
241 }
242}
243
244impl<const N: usize> Mul<PackedRijndael8b<N>> for Rijndael8b {
245 type Output = PackedRijndael8b<N>;
246
247 #[inline]
248 fn mul(self, rhs: PackedRijndael8b<N>) -> Self::Output {
249 rhs * self
250 }
251}
252
253impl<const N: usize> Div<Rijndael8b> for PackedRijndael8b<N> {
254 type Output = Self;
255
256 #[inline]
257 #[allow(clippy::suspicious_arithmetic_impl)]
258 fn div(self, rhs: Rijndael8b) -> Self {
259 self * rhs.inverse()
260 }
261}
262
263impl<const N: usize> DivAssign<Rijndael8b> for PackedRijndael8b<N> {
264 #[inline]
265 #[allow(clippy::suspicious_op_assign_impl)]
266 fn div_assign(&mut self, rhs: Rijndael8b) {
267 *self *= rhs.inverse();
268 }
269}
270
271impl<const N: usize, T: Into<Self>> AddAssign<T> for PackedRijndael8b<N> {
272 #[inline]
273 fn add_assign(&mut self, rhs: T) {
274 *self = *self + rhs.into();
275 }
276}
277
278impl<const N: usize, T: Into<Self>> SubAssign<T> for PackedRijndael8b<N> {
279 #[inline]
280 fn sub_assign(&mut self, rhs: T) {
281 *self = *self - rhs.into();
282 }
283}
284
285impl<const N: usize, T: Into<Self>> MulAssign<T> for PackedRijndael8b<N> {
286 #[inline]
287 fn mul_assign(&mut self, rhs: T) {
288 *self = *self * rhs.into();
289 }
290}
291
292impl<const N: usize> Sum for PackedRijndael8b<N> {
293 #[inline]
294 fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
295 iter.reduce(|x, y| x + y).unwrap_or(Self::ZERO)
296 }
297}
298
299impl<const N: usize> Product for PackedRijndael8b<N> {
300 #[inline]
301 fn product<I: Iterator<Item = Self>>(iter: I) -> Self {
302 iter.reduce(|x, y| x * y).unwrap_or(Self::ONE)
303 }
304}
305
306impl<const N: usize> Sum<Rijndael8b> for PackedRijndael8b<N> {
307 #[inline]
308 fn sum<I: Iterator<Item = Rijndael8b>>(iter: I) -> Self {
309 iter.sum::<Rijndael8b>().into()
310 }
311}
312
313impl<const N: usize> Product<Rijndael8b> for PackedRijndael8b<N> {
314 #[inline]
315 fn product<I: Iterator<Item = Rijndael8b>>(iter: I) -> Self {
316 iter.product::<Rijndael8b>().into()
317 }
318}
319
320impl<const N: usize> Distribution<PackedRijndael8b<N>> for StandardUniform {
321 #[inline]
322 fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> PackedRijndael8b<N> {
323 PackedRijndael8b(array::from_fn(|_| rng.random()))
324 }
325}
326
327impl<const N: usize> PrimeCharacteristicRing for PackedRijndael8b<N> {
328 type PrimeSubfield = Gf2;
329
330 const ZERO: Self = Self::splat(Rijndael8b::ZERO);
331 const ONE: Self = Self::splat(Rijndael8b::ONE);
332 const TWO: Self = Self::splat(Rijndael8b::ZERO);
334 const NEG_ONE: Self = Self::splat(Rijndael8b::ONE);
336
337 #[inline]
338 fn from_prime_subfield(f: Self::PrimeSubfield) -> Self {
339 Self::splat(Rijndael8b::from_prime_subfield(f))
340 }
341
342 characteristic_two_methods!();
343
344 #[inline]
346 fn exp_power_of_2(&self, power_log: usize) -> Self {
347 self.frobenius(power_log)
348 }
349}
350
351impl<const N: usize> Algebra<Rijndael8b> for PackedRijndael8b<N> {}
352
353impl<const N: usize> Algebra<Gf2> for PackedRijndael8b<N> {}
354
355impl<const N: usize> Add<Gf2> for PackedRijndael8b<N> {
356 type Output = Self;
357
358 #[inline]
359 fn add(self, rhs: Gf2) -> Self {
360 self + Self::from(rhs)
361 }
362}
363
364impl<const N: usize> Sub<Gf2> for PackedRijndael8b<N> {
365 type Output = Self;
366
367 #[inline]
368 fn sub(self, rhs: Gf2) -> Self {
369 self - Self::from(rhs)
370 }
371}
372
373impl<const N: usize> Mul<Gf2> for PackedRijndael8b<N> {
374 type Output = Self;
375
376 #[inline]
377 fn mul(self, rhs: Gf2) -> Self {
378 if rhs.is_one() { self } else { Self::ZERO }
380 }
381}
382
383unsafe impl<const N: usize> PackedValue for PackedRijndael8b<N> {
385 type Value = Rijndael8b;
386
387 const WIDTH: usize = {
391 assert!(
392 N.is_power_of_two(),
393 "the block width must be a power of two"
394 );
395 N
396 };
397
398 #[inline]
399 fn from_slice(slice: &[Self::Value]) -> &Self {
400 assert_eq!(slice.len(), Self::WIDTH);
401 unsafe { &*slice.as_ptr().cast() }
403 }
404
405 #[inline]
406 fn from_slice_mut(slice: &mut [Self::Value]) -> &mut Self {
407 assert_eq!(slice.len(), Self::WIDTH);
408 unsafe { &mut *slice.as_mut_ptr().cast() }
410 }
411
412 #[inline]
413 fn as_slice(&self) -> &[Self::Value] {
414 &self.0
415 }
416
417 #[inline]
418 fn as_slice_mut(&mut self) -> &mut [Self::Value] {
419 &mut self.0
420 }
421
422 #[inline]
423 fn from_fn<F: FnMut(usize) -> Self::Value>(f: F) -> Self {
424 Self(array::from_fn(f))
425 }
426}
427
428unsafe impl<const N: usize> PackedField for PackedRijndael8b<N> {
432 type Scalar = Rijndael8b;
433}
434
435unsafe impl<const N: usize> PackedFieldPow2 for PackedRijndael8b<N> {
437 #[inline]
440 fn interleave(&self, other: Self, block_len: usize) -> (Self, Self) {
441 const {
442 assert!(
443 N.is_power_of_two(),
444 "the block width must be a power of two"
445 );
446 };
447 assert!(
448 block_len.is_power_of_two() && block_len <= N && N.is_multiple_of(block_len),
449 "unsupported block_len"
450 );
451
452 if block_len == N {
454 return (*self, other);
455 }
456
457 let block = |from: &Self, index: usize, offset: usize| from.0[index * block_len + offset];
462 let left = array::from_fn(|i| {
463 let (m, offset) = (i / block_len, i % block_len);
464 if m % 2 == 0 {
465 block(self, m, offset)
466 } else {
467 block(&other, m - 1, offset)
468 }
469 });
470 let right = array::from_fn(|i| {
471 let (m, offset) = (i / block_len, i % block_len);
472 if m % 2 == 0 {
473 block(self, m + 1, offset)
474 } else {
475 block(&other, m, offset)
476 }
477 });
478 (Self(left), Self(right))
479 }
480}
481
482#[cfg(test)]
483mod tests {
484 use alloc::vec::Vec;
485
486 use p3_field::{PackedFieldPow2, PackedValue, PrimeCharacteristicRing};
487 use proptest::prelude::*;
488
489 use super::PackedRijndael8b;
490 use crate::Rijndael8b;
491 use crate::aes::{invert_byte, mul_bytes};
492
493 type Narrow = PackedRijndael8b<16>;
497 type Middle = PackedRijndael8b<32>;
498 type Wide = PackedRijndael8b<64>;
499
500 const SPECIAL: [u8; 4] = [0, 1, 0x1b, 0x80];
509
510 fn specials<const N: usize>() -> PackedRijndael8b<N> {
512 PackedValue::from_fn(|i| Rijndael8b::from_byte(SPECIAL[i % SPECIAL.len()]))
513 }
514
515 fn block_agrees<const N: usize>(a: [u8; N], b: [u8; N]) -> Result<(), TestCaseError> {
517 let pack =
518 |bytes: [u8; N]| PackedRijndael8b::<N>::from_fn(|i| Rijndael8b::from_byte(bytes[i]));
519 let (x, y) = (pack(a), pack(b));
520
521 let expected = pack(core::array::from_fn(|i| mul_bytes(a[i], b[i])));
522 prop_assert_eq!(x * y, expected);
523
524 let expected = pack(core::array::from_fn(|i| a[i] ^ b[i]));
525 prop_assert_eq!(x + y, expected);
526
527 let scalar = Rijndael8b::from_byte(b[0]);
529 let expected = pack(core::array::from_fn(|i| mul_bytes(a[i], b[0])));
530 prop_assert_eq!(x * scalar, expected);
531
532 prop_assert_eq!(
533 x.invert_or_zero(),
534 pack(core::array::from_fn(|i| invert_byte(a[i])))
535 );
536
537 Ok(())
538 }
539
540 #[test]
541 fn every_width_agrees_with_the_scalar_routines_at_the_corners() {
542 for (i, &x) in SPECIAL.iter().enumerate() {
548 for (j, &y) in SPECIAL.iter().enumerate() {
549 let a = |lane: usize| {
550 if lane == 0 {
551 x
552 } else {
553 SPECIAL[(i + lane) % 4]
554 }
555 };
556 let b = |lane: usize| {
557 if lane == 0 {
558 y
559 } else {
560 SPECIAL[(j + lane) % 4]
561 }
562 };
563 block_agrees::<16>(core::array::from_fn(a), core::array::from_fn(b)).unwrap();
564 block_agrees::<32>(core::array::from_fn(a), core::array::from_fn(b)).unwrap();
565 block_agrees::<64>(core::array::from_fn(a), core::array::from_fn(b)).unwrap();
566 }
567 }
568 }
569
570 proptest! {
571 #![proptest_config(ProptestConfig::with_cases(256))]
572
573 #[test]
574 fn every_width_agrees_with_the_scalar_routines(
575 a in prop::collection::vec(any::<u8>(), 64),
576 b in prop::collection::vec(any::<u8>(), 64),
577 ) {
578 let take = |from: &Vec<u8>, n: usize| -> Vec<u8> { from[..n].to_vec() };
579 let fixed = |v: Vec<u8>| -> [u8; 16] { core::array::from_fn(|i| v[i]) };
580 block_agrees::<16>(fixed(take(&a, 16)), fixed(take(&b, 16)))?;
581
582 let fixed = |v: Vec<u8>| -> [u8; 32] { core::array::from_fn(|i| v[i]) };
583 block_agrees::<32>(fixed(take(&a, 32)), fixed(take(&b, 32)))?;
584
585 let fixed = |v: Vec<u8>| -> [u8; 64] { core::array::from_fn(|i| v[i]) };
586 block_agrees::<64>(fixed(a), fixed(b))?;
587 }
588 }
589
590 #[test]
591 fn the_interleave_convention_is_the_two_by_two_transpose() {
592 let pack = |base: u8| Narrow::from_fn(|i| Rijndael8b::from_byte(base + i as u8));
601 let (a, b) = (pack(0), pack(16));
602 let expect = |bytes: [u8; 16]| Narrow::from_fn(|i| Rijndael8b::from_byte(bytes[i]));
603
604 let (left, right) = a.interleave(b, 1);
605 let want_left = [0, 16, 2, 18, 4, 20, 6, 22, 8, 24, 10, 26, 12, 28, 14, 30];
606 assert_eq!(left, expect(want_left));
607 assert_eq!(
608 right,
609 expect([1, 17, 3, 19, 5, 21, 7, 23, 9, 25, 11, 27, 13, 29, 15, 31])
610 );
611
612 let naive_left = [0, 16, 1, 17, 2, 18, 3, 19, 4, 20, 5, 21, 6, 22, 7, 23];
614 assert_ne!(want_left, naive_left, "the anchor must discriminate");
615
616 let (left, right) = a.interleave(b, 2);
617 assert_eq!(
618 left,
619 expect([0, 1, 16, 17, 4, 5, 20, 21, 8, 9, 24, 25, 12, 13, 28, 29])
620 );
621 assert_eq!(
622 right,
623 expect([2, 3, 18, 19, 6, 7, 22, 23, 10, 11, 26, 27, 14, 15, 30, 31])
624 );
625
626 let (left, right) = a.interleave(b, 8);
627 assert_eq!(
628 left,
629 expect([0, 1, 2, 3, 4, 5, 6, 7, 16, 17, 18, 19, 20, 21, 22, 23])
630 );
631 assert_eq!(
632 right,
633 expect([8, 9, 10, 11, 12, 13, 14, 15, 24, 25, 26, 27, 28, 29, 30, 31])
634 );
635
636 assert_eq!(a.interleave(b, 16), (a, b));
638 }
639
640 #[test]
641 #[should_panic = "tried to invert zero in a block"]
642 fn dividing_by_a_block_holding_zero_is_rejected() {
643 let _quotient = Narrow::ONE / Narrow::ZERO;
644 }
645
646 mod narrow {
650 use p3_field_testing::test_packed_binary_field;
651
652 test_packed_binary_field!(
653 super::super::Narrow,
654 &[super::super::Narrow::ZERO],
655 &[super::super::Narrow::ONE],
656 super::super::specials::<16>()
657 );
658 }
659
660 mod middle {
661 use p3_field_testing::test_packed_binary_field;
662
663 test_packed_binary_field!(
664 super::super::Middle,
665 &[super::super::Middle::ZERO],
666 &[super::super::Middle::ONE],
667 super::super::specials::<32>()
668 );
669 }
670
671 mod wide {
672 use p3_field_testing::test_packed_binary_field;
673
674 test_packed_binary_field!(
675 super::super::Wide,
676 &[super::super::Wide::ZERO],
677 &[super::super::Wide::ONE],
678 super::super::specials::<64>()
679 );
680 }
681}