binius_field/
aes_field.rs

1// Copyright 2024-2025 Irreducible Inc.
2
3use std::{
4	any::TypeId,
5	fmt::{Debug, Display, Formatter},
6	iter::{Product, Sum},
7	marker::PhantomData,
8	ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign},
9};
10
11use binius_utils::{
12	DeserializeBytes, SerializationError, SerializationMode, SerializeBytes,
13	bytes::{Buf, BufMut},
14};
15use bytemuck::{Pod, Zeroable};
16use rand::RngCore;
17use subtle::{Choice, ConditionallySelectable, ConstantTimeEq};
18
19use super::{
20	BinaryField8b, Error, PackedExtension, PackedSubfield,
21	arithmetic_traits::InvertOrZero,
22	binary_field::{BinaryField, BinaryField1b, binary_field, impl_field_extension},
23	binary_field_arithmetic::TowerFieldArithmetic,
24	mul_by_binary_field_1b,
25};
26use crate::{
27	BinaryField16b, BinaryField32b, BinaryField64b, BinaryField128b, ExtensionField, Field,
28	TowerField,
29	as_packed_field::AsPackedField,
30	binary_field_arithmetic::{impl_arithmetic_using_packed, impl_mul_primitive},
31	binary_tower,
32	linear_transformation::{
33		FieldLinearTransformation, PackedTransformationFactory, Transformation,
34	},
35	packed::PackedField,
36	underlier::U1,
37};
38
39// These fields represent a tower based on AES GF(2^8) field (GF(256)/x^8+x^4+x^3+x+1)
40// that is isomorphically included into binary tower, i.e.:
41//  - AESTowerField16b is GF(2^16) / (x^2 + x * x_2 + 1) where `x_2` is 0x10 from
42// BinaryField8b isomorphically projected to AESTowerField8b.
43//  - AESTowerField32b is GF(2^32) / (x^2 + x * x_3 + 1), where `x_3` is 0x1000 from
44//    AESTowerField16b.
45//  ...
46binary_field!(pub AESTowerField8b(u8), 0xD0);
47binary_field!(pub AESTowerField16b(u16), 0x4745);
48binary_field!(pub AESTowerField32b(u32), 0xBD478FAB);
49binary_field!(pub AESTowerField64b(u64), 0x0DE1555D2BD78EB4);
50binary_field!(pub AESTowerField128b(u128), 0x6DB54066349EDB96C33A87244A742678);
51
52unsafe impl Pod for AESTowerField8b {}
53unsafe impl Pod for AESTowerField16b {}
54unsafe impl Pod for AESTowerField32b {}
55unsafe impl Pod for AESTowerField64b {}
56unsafe impl Pod for AESTowerField128b {}
57
58binary_tower!(
59	AESTowerField8b(u8, BinaryField8b)
60	< AESTowerField16b(u16, BinaryField16b)
61	< AESTowerField32b(u32, BinaryField32b)
62	< AESTowerField64b(u64, BinaryField64b)
63	< AESTowerField128b(u128, BinaryField128b)
64);
65
66impl_field_extension!(BinaryField1b(U1) < @3 => AESTowerField8b(u8));
67impl_field_extension!(BinaryField1b(U1) < @4 => AESTowerField16b(u16));
68impl_field_extension!(BinaryField1b(U1) < @5 => AESTowerField32b(u32));
69impl_field_extension!(BinaryField1b(U1) < @6 => AESTowerField64b(u64));
70impl_field_extension!(BinaryField1b(U1) < @7 => AESTowerField128b(u128));
71
72mul_by_binary_field_1b!(AESTowerField8b);
73mul_by_binary_field_1b!(AESTowerField16b);
74mul_by_binary_field_1b!(AESTowerField32b);
75mul_by_binary_field_1b!(AESTowerField64b);
76mul_by_binary_field_1b!(AESTowerField128b);
77
78impl_arithmetic_using_packed!(AESTowerField8b);
79impl_arithmetic_using_packed!(AESTowerField16b);
80impl_arithmetic_using_packed!(AESTowerField32b);
81impl_arithmetic_using_packed!(AESTowerField64b);
82impl_arithmetic_using_packed!(AESTowerField128b);
83
84impl TowerField for AESTowerField8b {
85	type Canonical = BinaryField8b;
86
87	fn min_tower_level(self) -> usize {
88		match self {
89			Self::ZERO | Self::ONE => 0,
90			_ => 3,
91		}
92	}
93
94	fn mul_primitive(self, iota: usize) -> Result<Self, Error> {
95		match iota {
96			0..=1 => Ok(self * ISOMORPHIC_ALPHAS[iota]),
97			2 => Ok(self.multiply_alpha()),
98			_ => Err(Error::ExtensionDegreeMismatch),
99		}
100	}
101}
102
103/// Returns true if `F`` is AES tower field.
104#[inline(always)]
105pub fn is_aes_tower<F: TowerField>() -> bool {
106	TypeId::of::<F>() == TypeId::of::<F>()
107		|| TypeId::of::<F>() == TypeId::of::<AESTowerField16b>()
108		|| TypeId::of::<F>() == TypeId::of::<AESTowerField32b>()
109		|| TypeId::of::<F>() == TypeId::of::<AESTowerField64b>()
110		|| TypeId::of::<F>() == TypeId::of::<AESTowerField128b>()
111}
112
113pub const AES_TO_BINARY_LINEAR_TRANSFORMATION: FieldLinearTransformation<BinaryField8b> =
114	FieldLinearTransformation::new_const(&[
115		BinaryField8b(0x01),
116		BinaryField8b(0x3c),
117		BinaryField8b(0x8c),
118		BinaryField8b(0x8a),
119		BinaryField8b(0x59),
120		BinaryField8b(0x7a),
121		BinaryField8b(0x53),
122		BinaryField8b(0x27),
123	]);
124
125impl From<AESTowerField8b> for BinaryField8b {
126	fn from(value: AESTowerField8b) -> Self {
127		AES_TO_BINARY_LINEAR_TRANSFORMATION.transform(&value)
128	}
129}
130
131pub const BINARY_TO_AES_LINEAR_TRANSFORMATION: FieldLinearTransformation<AESTowerField8b> =
132	FieldLinearTransformation::new_const(&[
133		AESTowerField8b(0x01),
134		AESTowerField8b(0xbc),
135		AESTowerField8b(0xb0),
136		AESTowerField8b(0xec),
137		AESTowerField8b(0xd3),
138		AESTowerField8b(0x8d),
139		AESTowerField8b(0x2e),
140		AESTowerField8b(0x58),
141	]);
142
143impl From<BinaryField8b> for AESTowerField8b {
144	fn from(value: BinaryField8b) -> Self {
145		BINARY_TO_AES_LINEAR_TRANSFORMATION.transform(&value)
146	}
147}
148
149/// A 3- step transformation :
150/// 1. Cast to base b-bit packed field
151/// 2. Apply linear transformation between aes and binary b8 tower fields
152/// 3. Cast back to the target field
153pub struct SubfieldTransformer<IF, OF, T> {
154	inner_transform: T,
155	_ip_pd: PhantomData<IF>,
156	_op_pd: PhantomData<OF>,
157}
158
159impl<IF, OF, T> SubfieldTransformer<IF, OF, T> {
160	const fn new(inner_transform: T) -> Self {
161		Self {
162			inner_transform,
163			_ip_pd: PhantomData,
164			_op_pd: PhantomData,
165		}
166	}
167}
168
169impl<IF, OF, IEP, OEP, T> Transformation<IEP, OEP> for SubfieldTransformer<IF, OF, T>
170where
171	IF: Field,
172	OF: Field,
173	IEP: PackedExtension<IF>,
174	OEP: PackedExtension<OF>,
175	T: Transformation<PackedSubfield<IEP, IF>, PackedSubfield<OEP, OF>>,
176{
177	fn transform(&self, input: &IEP) -> OEP {
178		OEP::cast_ext(self.inner_transform.transform(IEP::cast_base_ref(input)))
179	}
180}
181
182pub type AesToBinaryTransformation<IP, OP> = SubfieldTransformer<
183	AESTowerField8b,
184	BinaryField8b,
185	<PackedSubfield<IP, AESTowerField8b> as PackedTransformationFactory<
186		PackedSubfield<OP, BinaryField8b>,
187	>>::PackedTransformation<&'static [BinaryField8b]>,
188>;
189pub type BinaryToAesTransformation<IP, OP> = SubfieldTransformer<
190	BinaryField8b,
191	AESTowerField8b,
192	<PackedSubfield<IP, BinaryField8b> as PackedTransformationFactory<
193		PackedSubfield<OP, AESTowerField8b>,
194	>>::PackedTransformation<&'static [AESTowerField8b]>,
195>;
196
197/// Creates transformation object from AES tower to binary tower for packed field.
198/// Note that creation of this object is not cheap, so it is better to create it once and reuse.
199pub fn make_aes_to_binary_packed_transformer<IP, OP>() -> AesToBinaryTransformation<IP, OP>
200where
201	IP: PackedExtension<AESTowerField8b>,
202	OP: PackedExtension<BinaryField8b>,
203	PackedSubfield<IP, AESTowerField8b>:
204		PackedTransformationFactory<PackedSubfield<OP, BinaryField8b>>,
205{
206	SubfieldTransformer::<AESTowerField8b, BinaryField8b, _>::new(PackedSubfield::<
207		IP,
208		AESTowerField8b,
209	>::make_packed_transformation(
210		AES_TO_BINARY_LINEAR_TRANSFORMATION,
211	))
212}
213
214/// Creates transformation object from AES tower to binary tower for packed field.
215/// Note that creation of this object is not cheap, so it is better to create it once and reuse.
216pub fn make_binary_to_aes_packed_transformer<IP, OP>() -> BinaryToAesTransformation<IP, OP>
217where
218	IP: PackedExtension<BinaryField8b>,
219	OP: PackedExtension<AESTowerField8b>,
220	PackedSubfield<IP, BinaryField8b>:
221		PackedTransformationFactory<PackedSubfield<OP, AESTowerField8b>>,
222{
223	SubfieldTransformer::<BinaryField8b, AESTowerField8b, _>::new(
224		PackedSubfield::<IP, BinaryField8b>::make_packed_transformation(
225			BINARY_TO_AES_LINEAR_TRANSFORMATION,
226		),
227	)
228}
229
230/// Values isomorphic to 0x02, 0x04 and 0x10 in BinaryField8b
231const ISOMORPHIC_ALPHAS: [AESTowerField8b; 3] = [
232	AESTowerField8b(0xBC),
233	AESTowerField8b(0xB0),
234	AESTowerField8b(0xD3),
235];
236
237// MulPrimitive implementation for AES tower
238impl_mul_primitive!(AESTowerField16b,
239	mul_by 0 => ISOMORPHIC_ALPHAS[0],
240	mul_by 1 => ISOMORPHIC_ALPHAS[1],
241	repack 2 => AESTowerField8b,
242	repack 3 => AESTowerField16b,
243);
244impl_mul_primitive!(AESTowerField32b,
245	mul_by 0 => ISOMORPHIC_ALPHAS[0],
246	mul_by 1 => ISOMORPHIC_ALPHAS[1],
247	repack 2 => AESTowerField8b,
248	repack 3 => AESTowerField16b,
249	repack 4 => AESTowerField32b,
250);
251impl_mul_primitive!(AESTowerField64b,
252	mul_by 0 => ISOMORPHIC_ALPHAS[0],
253	mul_by 1 => ISOMORPHIC_ALPHAS[1],
254	repack 2 => AESTowerField8b,
255	repack 3 => AESTowerField16b,
256	repack 4 => AESTowerField32b,
257	repack 5 => AESTowerField64b,
258);
259impl_mul_primitive!(AESTowerField128b,
260	mul_by 0 => ISOMORPHIC_ALPHAS[0],
261	mul_by 1 => ISOMORPHIC_ALPHAS[1],
262	repack 2 => AESTowerField8b,
263	repack 3 => AESTowerField16b,
264	repack 4 => AESTowerField32b,
265	repack 5 => AESTowerField64b,
266	repack 6 => AESTowerField128b,
267);
268
269/// We use this function to define isomorphisms between AES and binary tower fields.
270/// Repack field as 8b packed field and apply isomorphism for each 8b element
271fn convert_as_packed_8b<F1, F2, Scalar1, Scalar2>(val: F1) -> F2
272where
273	Scalar1: Field,
274	Scalar2: Field + From<Scalar1>,
275	F1: AsPackedField<Scalar1>,
276	F2: AsPackedField<Scalar2>,
277{
278	assert_eq!(F1::Packed::WIDTH, F2::Packed::WIDTH);
279
280	let val_repacked = val.to_packed();
281	let converted_repacked = F2::Packed::from_fn(|i| val_repacked.get(i).into());
282
283	F2::from_packed(converted_repacked)
284}
285
286macro_rules! impl_tower_field_conversion {
287	($aes_field:ty, $binary_field:ty) => {
288		impl From<$aes_field> for $binary_field {
289			fn from(value: $aes_field) -> Self {
290				convert_as_packed_8b::<_, _, AESTowerField8b, BinaryField8b>(value)
291			}
292		}
293
294		impl From<$binary_field> for $aes_field {
295			fn from(value: $binary_field) -> Self {
296				convert_as_packed_8b::<_, _, BinaryField8b, AESTowerField8b>(value)
297			}
298		}
299	};
300}
301
302impl_tower_field_conversion!(AESTowerField16b, BinaryField16b);
303impl_tower_field_conversion!(AESTowerField32b, BinaryField32b);
304impl_tower_field_conversion!(AESTowerField64b, BinaryField64b);
305impl_tower_field_conversion!(AESTowerField128b, BinaryField128b);
306
307macro_rules! serialize_deserialize_non_canonical {
308	($field:ident, canonical=$canonical:ident) => {
309		impl SerializeBytes for $field {
310			fn serialize(
311				&self,
312				write_buf: impl BufMut,
313				mode: SerializationMode,
314			) -> Result<(), SerializationError> {
315				match mode {
316					SerializationMode::Native => self.0.serialize(write_buf, mode),
317					SerializationMode::CanonicalTower => {
318						$canonical::from(*self).serialize(write_buf, mode)
319					}
320				}
321			}
322		}
323
324		impl DeserializeBytes for $field {
325			fn deserialize(
326				read_buf: impl Buf,
327				mode: SerializationMode,
328			) -> Result<Self, SerializationError>
329			where
330				Self: Sized,
331			{
332				match mode {
333					SerializationMode::Native => {
334						Ok(Self(DeserializeBytes::deserialize(read_buf, mode)?))
335					}
336					SerializationMode::CanonicalTower => {
337						Ok(Self::from($canonical::deserialize(read_buf, mode)?))
338					}
339				}
340			}
341		}
342	};
343}
344
345serialize_deserialize_non_canonical!(AESTowerField8b, canonical = BinaryField8b);
346serialize_deserialize_non_canonical!(AESTowerField16b, canonical = BinaryField16b);
347serialize_deserialize_non_canonical!(AESTowerField32b, canonical = BinaryField32b);
348serialize_deserialize_non_canonical!(AESTowerField64b, canonical = BinaryField64b);
349serialize_deserialize_non_canonical!(AESTowerField128b, canonical = BinaryField128b);
350
351#[cfg(test)]
352mod tests {
353	use binius_utils::{SerializationMode, SerializeBytes, bytes::BytesMut};
354	use proptest::{arbitrary::any, proptest};
355	use rand::thread_rng;
356
357	use super::*;
358	use crate::{
359		PackedAESBinaryField4x32b, PackedAESBinaryField8x32b, PackedAESBinaryField16x32b,
360		PackedBinaryField4x32b, PackedBinaryField8x32b, PackedBinaryField16x32b,
361		binary_field::tests::is_binary_field_valid_generator, underlier::WithUnderlier,
362	};
363
364	fn check_square(f: impl Field) {
365		assert_eq!(f.square(), f * f);
366	}
367
368	proptest! {
369		#[test]
370		fn test_square_8(a in any::<u8>()) {
371			check_square(AESTowerField8b::from(a))
372		}
373
374		#[test]
375		fn test_square_16(a in any::<u16>()) {
376			check_square(AESTowerField16b::from(a))
377		}
378
379		#[test]
380		fn test_square_32(a in any::<u32>()) {
381			check_square(AESTowerField32b::from(a))
382		}
383
384		#[test]
385		fn test_square_64(a in any::<u64>()) {
386			check_square(AESTowerField64b::from(a))
387		}
388
389		#[test]
390		fn test_square_128(a in any::<u128>()) {
391			check_square(AESTowerField128b::from(a))
392		}
393	}
394
395	fn check_invert(f: impl Field) {
396		let inversed = f.invert();
397		if f.is_zero() {
398			assert!(inversed.is_none());
399		} else {
400			assert_eq!(inversed.unwrap() * f, Field::ONE);
401		}
402	}
403
404	fn check_isomorphism_preserves_ops<F1: Field, F2: Field + From<F1>>(a: F1, b: F1) {
405		assert_eq!(F2::from(a * b), F2::from(a) * F2::from(b));
406		assert_eq!(F2::from(a + b), F2::from(a) + F2::from(b));
407	}
408
409	proptest! {
410		#[test]
411		fn test_invert_8(a in any::<u8>()) {
412			check_invert(AESTowerField8b::from(a))
413		}
414
415		#[test]
416		fn test_invert_16(a in any::<u16>()) {
417			check_invert(AESTowerField16b::from(a))
418		}
419
420		#[test]
421		fn test_invert_32(a in any::<u32>()) {
422			check_invert(AESTowerField32b::from(a))
423		}
424
425		#[test]
426		fn test_invert_64(a in any::<u64>()) {
427			check_invert(AESTowerField64b::from(a))
428		}
429
430		#[test]
431		fn test_invert_128(a in any::<u128>()) {
432			check_invert(AESTowerField128b::from(a))
433		}
434
435		#[test]
436		fn test_isomorphism_to_binary_tower8b_roundtrip(a in any::<u8>()) {
437			let a_val = AESTowerField8b(a);
438			let projected = BinaryField8b::from(a_val);
439			let restored = AESTowerField8b::from(projected);
440			assert_eq!(a_val, restored);
441		}
442
443		#[test]
444		fn test_isomorphism_8b(a in any::<u8>(), b in any::<u8>()) {
445			check_isomorphism_preserves_ops::<AESTowerField8b, BinaryField8b>(a.into(), b.into());
446			check_isomorphism_preserves_ops::<BinaryField8b, AESTowerField8b>(a.into(), b.into());
447		}
448
449		#[test]
450		fn test_isomorphism_16b(a in any::<u16>(), b in any::<u16>()) {
451			check_isomorphism_preserves_ops::<AESTowerField16b, BinaryField16b>(a.into(), b.into());
452			check_isomorphism_preserves_ops::<BinaryField16b, AESTowerField16b>(a.into(), b.into());
453		}
454
455		#[test]
456		fn test_isomorphism_32b(a in any::<u32>(), b in any::<u32>()) {
457			check_isomorphism_preserves_ops::<AESTowerField32b, BinaryField32b>(a.into(), b.into());
458			check_isomorphism_preserves_ops::<BinaryField32b, AESTowerField32b>(a.into(), b.into());
459		}
460
461		#[test]
462		fn test_isomorphism_64b(a in any::<u64>(), b in any::<u64>()) {
463			check_isomorphism_preserves_ops::<AESTowerField64b, BinaryField64b>(a.into(), b.into());
464			check_isomorphism_preserves_ops::<BinaryField64b, AESTowerField64b>(a.into(), b.into());
465		}
466
467		#[test]
468		fn test_isomorphism_128b(a in any::<u128>(), b in any::<u128>()) {
469			check_isomorphism_preserves_ops::<AESTowerField128b, BinaryField128b>(a.into(), b.into());
470			check_isomorphism_preserves_ops::<BinaryField128b, AESTowerField128b>(a.into(), b.into());
471		}
472	}
473
474	fn check_mul_by_one<F: Field>(f: F) {
475		assert_eq!(F::ONE * f, f);
476		assert_eq!(f * F::ONE, f);
477	}
478
479	fn check_commutative<F: Field>(f_1: F, f_2: F) {
480		assert_eq!(f_1 * f_2, f_2 * f_1);
481	}
482
483	fn check_associativity_and_lineraity<F: Field>(f_1: F, f_2: F, f_3: F) {
484		assert_eq!(f_1 * (f_2 * f_3), (f_1 * f_2) * f_3);
485		assert_eq!(f_1 * (f_2 + f_3), f_1 * f_2 + f_1 * f_3);
486	}
487
488	fn check_mul<F: Field>(f_1: F, f_2: F, f_3: F) {
489		check_mul_by_one(f_1);
490		check_mul_by_one(f_2);
491		check_mul_by_one(f_3);
492
493		check_commutative(f_1, f_2);
494		check_commutative(f_1, f_3);
495		check_commutative(f_2, f_3);
496
497		check_associativity_and_lineraity(f_1, f_2, f_3);
498		check_associativity_and_lineraity(f_1, f_3, f_2);
499		check_associativity_and_lineraity(f_2, f_1, f_3);
500		check_associativity_and_lineraity(f_2, f_3, f_1);
501		check_associativity_and_lineraity(f_3, f_1, f_2);
502		check_associativity_and_lineraity(f_3, f_2, f_1);
503	}
504
505	proptest! {
506		#[test]
507		fn test_mul_8(a in any::<u8>(), b in any::<u8>(), c in any::<u8>()) {
508			check_mul(AESTowerField8b::from(a), AESTowerField8b::from(b), AESTowerField8b::from(c))
509		}
510
511		#[test]
512		fn test_mul_16(a in any::<u16>(), b in any::<u16>(), c in any::<u16>()) {
513			check_mul(AESTowerField16b::from(a), AESTowerField16b::from(b), AESTowerField16b::from(c))
514		}
515
516		#[test]
517		fn test_mul_32(a in any::<u32>(), b in any::<u32>(), c in any::<u32>()) {
518			check_mul(AESTowerField32b::from(a), AESTowerField32b::from(b), AESTowerField32b::from(c))
519		}
520
521		#[test]
522		fn test_mul_64(a in any::<u64>(), b in any::<u64>(), c in any::<u64>()) {
523			check_mul(AESTowerField64b::from(a), AESTowerField64b::from(b), AESTowerField64b::from(c))
524		}
525
526		#[test]
527		fn test_mul_128(a in any::<u128>(), b in any::<u128>(), c in any::<u128>()) {
528			check_mul(AESTowerField128b::from(a), AESTowerField128b::from(b), AESTowerField128b::from(c))
529		}
530
531		#[test]
532		fn test_conversion_roundtrip(a in any::<u8>()) {
533			let a_val = AESTowerField8b(a);
534			let converted = BinaryField8b::from(a_val);
535			assert_eq!(a_val, AESTowerField8b::from(converted));
536		}
537	}
538
539	#[test]
540	fn test_multiplicative_generators() {
541		assert!(is_binary_field_valid_generator::<AESTowerField8b>());
542		assert!(is_binary_field_valid_generator::<AESTowerField16b>());
543		assert!(is_binary_field_valid_generator::<AESTowerField32b>());
544		assert!(is_binary_field_valid_generator::<AESTowerField64b>());
545		assert!(is_binary_field_valid_generator::<AESTowerField128b>());
546	}
547
548	fn test_mul_primitive<F: TowerField + WithUnderlier<Underlier: From<u8>>>(val: F, iota: usize) {
549		let result = val.mul_primitive(iota);
550		let expected = match iota {
551			0..=2 => {
552				Ok(val
553					* F::from_underlier(F::Underlier::from(ISOMORPHIC_ALPHAS[iota].to_underlier())))
554			}
555			_ => <F as ExtensionField<BinaryField1b>>::basis_checked(1 << iota).map(|b| val * b),
556		};
557		assert_eq!(result.is_ok(), expected.is_ok());
558		if result.is_ok() {
559			assert_eq!(result.unwrap(), expected.unwrap());
560		} else {
561			assert!(matches!(result.unwrap_err(), Error::ExtensionDegreeMismatch));
562		}
563	}
564
565	proptest! {
566		#[test]
567		fn test_mul_primitive_8b(val in 0u8.., iota in 3usize..8) {
568			test_mul_primitive::<AESTowerField8b>(val.into(), iota)
569		}
570
571		#[test]
572		fn test_mul_primitive_16b(val in 0u16.., iota in 3usize..8) {
573			test_mul_primitive::<AESTowerField16b>(val.into(), iota)
574		}
575
576		#[test]
577		fn test_mul_primitive_32b(val in 0u32.., iota in 3usize..8) {
578			test_mul_primitive::<AESTowerField32b>(val.into(), iota)
579		}
580
581		#[test]
582		fn test_mul_primitive_64b(val in 0u64.., iota in 3usize..8) {
583			test_mul_primitive::<AESTowerField64b>(val.into(), iota)
584		}
585
586		#[test]
587		fn test_mul_primitive_128b(val in 0u128.., iota in 3usize..8) {
588			test_mul_primitive::<AESTowerField128b>(val.into(), iota)
589		}
590	}
591
592	fn convert_pairwise<IP, OP>(val: IP) -> OP
593	where
594		IP: PackedField + WithUnderlier,
595		OP: PackedField<Scalar: From<IP::Scalar>> + WithUnderlier<Underlier = IP::Underlier>,
596	{
597		OP::from_fn(|i| val.get(i).into())
598	}
599
600	proptest! {
601		#[test]
602		fn test_aes_to_binary_packed_transform_128(val in 0u128..) {
603			let transform = make_aes_to_binary_packed_transformer::<PackedAESBinaryField4x32b, PackedBinaryField4x32b>();
604			let input = PackedAESBinaryField4x32b::from(val);
605			let result: PackedBinaryField4x32b = transform.transform(&input);
606			assert_eq!(result, convert_pairwise(input));
607		}
608
609		#[test]
610		fn test_binary_to_aes_packed_transform_128(val in 0u128..) {
611			let transform = make_binary_to_aes_packed_transformer::<PackedBinaryField4x32b, PackedAESBinaryField4x32b>();
612			let input = PackedBinaryField4x32b::from(val);
613			let result: PackedAESBinaryField4x32b = transform.transform(&input);
614			assert_eq!(result, convert_pairwise(input));
615		}
616
617		#[test]
618		fn test_aes_to_binary_packed_transform_256(val in any::<[u128; 2]>()) {
619			let transform = make_aes_to_binary_packed_transformer::<PackedAESBinaryField8x32b, PackedBinaryField8x32b>();
620			let input = PackedAESBinaryField8x32b::from(val);
621			let result: PackedBinaryField8x32b = transform.transform(&input);
622			assert_eq!(result, convert_pairwise(input));
623		}
624
625		#[test]
626		fn test_binary_to_aes_packed_transform_256(val in any::<[u128; 2]>()) {
627			let transform = make_binary_to_aes_packed_transformer::<PackedBinaryField8x32b, PackedAESBinaryField8x32b>();
628			let input = PackedBinaryField8x32b::from(val);
629			let result: PackedAESBinaryField8x32b = transform.transform(&input);
630			assert_eq!(result, convert_pairwise(input));
631		}
632
633		#[test]
634		fn test_aes_to_binary_packed_transform_512(val in any::<[u128; 4]>()) {
635			let transform = make_aes_to_binary_packed_transformer::<PackedAESBinaryField16x32b, PackedBinaryField16x32b>();
636			let input = PackedAESBinaryField16x32b::from_underlier(val.into());
637			let result: PackedBinaryField16x32b = transform.transform(&input);
638			assert_eq!(result, convert_pairwise(input));
639		}
640
641		#[test]
642		fn test_binary_to_aes_packed_transform_512(val in any::<[u128; 4]>()) {
643			let transform = make_binary_to_aes_packed_transformer::<PackedBinaryField16x32b, PackedAESBinaryField16x32b>();
644			let input = PackedBinaryField16x32b::from_underlier(val.into());
645			let result: PackedAESBinaryField16x32b = transform.transform(&input);
646			assert_eq!(result, convert_pairwise(input));
647		}
648	}
649
650	#[test]
651	fn test_canonical_serialization() {
652		let mut buffer = BytesMut::new();
653		let mut rng = thread_rng();
654		let aes8 = <AESTowerField8b as Field>::random(&mut rng);
655		let aes16 = <AESTowerField16b as Field>::random(&mut rng);
656		let aes32 = <AESTowerField32b as Field>::random(&mut rng);
657		let aes64 = <AESTowerField64b as Field>::random(&mut rng);
658		let aes128 = <AESTowerField128b as Field>::random(&mut rng);
659
660		let mode = SerializationMode::CanonicalTower;
661
662		SerializeBytes::serialize(&aes8, &mut buffer, mode).unwrap();
663		SerializeBytes::serialize(&aes16, &mut buffer, mode).unwrap();
664		SerializeBytes::serialize(&aes32, &mut buffer, mode).unwrap();
665		SerializeBytes::serialize(&aes64, &mut buffer, mode).unwrap();
666		SerializeBytes::serialize(&aes128, &mut buffer, mode).unwrap();
667
668		SerializeBytes::serialize(&aes128, &mut buffer, mode).unwrap();
669
670		let mut read_buffer = buffer.freeze();
671
672		assert_eq!(AESTowerField8b::deserialize(&mut read_buffer, mode).unwrap(), aes8);
673		assert_eq!(AESTowerField16b::deserialize(&mut read_buffer, mode).unwrap(), aes16);
674		assert_eq!(AESTowerField32b::deserialize(&mut read_buffer, mode).unwrap(), aes32);
675		assert_eq!(AESTowerField64b::deserialize(&mut read_buffer, mode).unwrap(), aes64);
676		assert_eq!(AESTowerField128b::deserialize(&mut read_buffer, mode).unwrap(), aes128);
677
678		assert_eq!(BinaryField128b::deserialize(&mut read_buffer, mode).unwrap(), aes128.into())
679	}
680}