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