1use std::{
20 fmt::{Debug, Display, Formatter},
21 iter::{Product, Sum},
22 ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign},
23};
24
25use binius_utils::{
26 DeserializeBytes, FixedSizeSerializeBytes, SerializationError, SerializeBytes,
27 bytes::{Buf, BufMut},
28};
29use bytemuck::{Pod, Zeroable};
30
31use super::{
32 binary_field::{BinaryField, BinaryField1b, binary_field, impl_field_extension},
33 extension::ExtensionField,
34};
35use crate::{
36 BinaryField128bGhash, Divisible, Field, PackedBinaryGhash2x128b, PackedField,
37 arch::{M128, M256, m256_from_u128s},
38 mul_by_binary_field_1b,
39 underlier::U1,
40};
41
42binary_field!(pub GhashSq256b(M256), m256_from_u128s(0, 1));
50
51unsafe impl Pod for GhashSq256b {}
52
53impl GhashSq256b {
54 #[inline]
56 fn to_coeffs(self) -> [BinaryField128bGhash; 2] {
57 let packed = PackedBinaryGhash2x128b::from_underlier(self.0);
60 [packed.get(0), packed.get(1)]
61 }
62
63 #[inline]
65 fn from_coeffs(coeffs: [BinaryField128bGhash; 2]) -> Self {
66 Self(PackedBinaryGhash2x128b::from_scalars(coeffs).to_underlier())
67 }
68}
69
70impl_field_extension!(BinaryField128bGhash(M128) < @1 => GhashSq256b(M256));
74
75impl_field_extension!(BinaryField1b(U1) < @8 => GhashSq256b(M256));
77
78mul_by_binary_field_1b!(GhashSq256b);
79
80impl Mul<BinaryField128bGhash> for GhashSq256b {
82 type Output = Self;
83
84 #[inline]
85 fn mul(self, rhs: BinaryField128bGhash) -> Self::Output {
86 let [a, b] = self.to_coeffs();
87 Self::from_coeffs([a * rhs, b * rhs])
88 }
89}
90
91impl SerializeBytes for GhashSq256b {
92 fn serialize(&self, write_buf: impl BufMut) -> Result<(), SerializationError> {
93 self.0.serialize(write_buf)
94 }
95}
96
97impl DeserializeBytes for GhashSq256b {
98 fn deserialize(read_buf: impl Buf) -> Result<Self, SerializationError>
99 where
100 Self: Sized,
101 {
102 Ok(Self(DeserializeBytes::deserialize(read_buf)?))
103 }
104}
105
106impl FixedSizeSerializeBytes for GhashSq256b {
107 const BYTE_SIZE: usize = 32;
108}
109
110#[cfg(test)]
111mod tests {
112 use proptest::prelude::*;
113
114 use super::*;
115 use crate::arithmetic_traits::{InvertOrZero, Square, WideMul};
116
117 const GHASH_X: u128 = 0x02;
119
120 const ORDER_PRIME_FACTORS: [u128; 11] = [
123 3,
124 5,
125 17,
126 257,
127 65537,
128 641,
129 6700417,
130 274177,
131 67280421310721,
132 59649589127497217,
133 5704689200685129054721,
134 ];
135
136 fn ghash_sq(a: u128, b: u128) -> GhashSq256b {
137 GhashSq256b::from_coeffs([BinaryField128bGhash::new(a), BinaryField128bGhash::new(b)])
138 }
139
140 fn arb_elem() -> impl Strategy<Value = GhashSq256b> {
141 any::<[u128; 2]>().prop_map(|[a, b]| ghash_sq(a, b))
142 }
143
144 fn naive_square_transpose<F: BinaryField>(values: &[GhashSq256b]) -> Vec<GhashSq256b>
149 where
150 GhashSq256b: ExtensionField<F>,
151 {
152 let degree = <GhashSq256b as ExtensionField<F>>::DEGREE;
153 assert_eq!(values.len(), degree);
154 let coords: Vec<F> = values
155 .iter()
156 .flat_map(|v| ExtensionField::<F>::iter_bases(v))
157 .collect();
158 (0..degree)
159 .map(|i| {
160 <GhashSq256b as ExtensionField<F>>::from_bases(
161 (0..degree).map(|j| coords[j * degree + i]),
162 )
163 })
164 .collect()
165 }
166
167 fn order_cofactor(p: u128) -> [u64; 4] {
170 let mut quotient = [0u64; 4];
171 let mut rem: u128 = 0;
172 for bit in (0..256).rev() {
174 rem = (rem << 1) | 1;
175 let q_bit = if rem >= p {
176 rem -= p;
177 1u64
178 } else {
179 0
180 };
181 quotient[bit / 64] |= q_bit << (bit % 64);
182 }
183 quotient
184 }
185
186 fn is_generator(g: GhashSq256b) -> bool {
189 ORDER_PRIME_FACTORS
190 .iter()
191 .all(|&p| Field::pow(&g, order_cofactor(p)) != GhashSq256b::ONE)
192 }
193
194 #[test]
195 fn test_quadratic_relation() {
196 let y = ghash_sq(0, 1);
199 assert_eq!(y * y, ghash_sq(GHASH_X, GHASH_X));
200 }
201
202 #[test]
203 fn test_subfield_embedding() {
204 let a = BinaryField128bGhash::new(0x0123456789abcdef0123456789abcdef);
206 let b = BinaryField128bGhash::new(0xfedcba9876543210fedcba9876543210);
207 assert_eq!(GhashSq256b::from(a) * GhashSq256b::from(b), GhashSq256b::from(a * b),);
208 }
209
210 #[test]
211 fn test_multiplicative_generator() {
212 assert!(
213 is_generator(GhashSq256b::MULTIPLICATIVE_GENERATOR),
214 "baked MULTIPLICATIVE_GENERATOR is not a generator of GF(2^256)*",
215 );
216 }
217
218 #[test]
219 #[ignore = "search utility: prints a valid generator literal to bake into the field"]
220 fn find_generator() {
221 for b in 1u128..256 {
222 for a in 0u128..256 {
223 let candidate = ghash_sq(a, b);
224 if is_generator(candidate) {
225 let [low, high] = candidate.to_coeffs();
226 panic!(
227 "found generator: a={a:#x}, b={b:#x} -> m256_from_u128s({:#034x}, {:#034x})",
228 u128::from(low.val()),
229 u128::from(high.val()),
230 );
231 }
232 }
233 }
234 panic!("no generator found in search range");
235 }
236
237 proptest! {
238 #[test]
239 fn test_mul_commutative(a in arb_elem(), b in arb_elem()) {
240 prop_assert_eq!(a * b, b * a);
241 }
242
243 #[test]
244 fn test_mul_associative(a in arb_elem(), b in arb_elem(), c in arb_elem()) {
245 prop_assert_eq!((a * b) * c, a * (b * c));
246 }
247
248 #[test]
249 fn test_mul_distributive(a in arb_elem(), b in arb_elem(), c in arb_elem()) {
250 prop_assert_eq!(a * (b + c), a * b + a * c);
251 }
252
253 #[test]
254 fn test_mul_identity(a in arb_elem()) {
255 prop_assert_eq!(a * GhashSq256b::ONE, a);
256 }
257
258 #[test]
259 fn test_square_equals_mul(a in arb_elem()) {
260 prop_assert_eq!(Square::square(a), a * a);
261 }
262
263 #[test]
264 fn test_wide_mul_deferred_reduction(
265 pairs in prop::collection::vec((arb_elem(), arb_elem()), 1..16),
266 ) {
267 let eager: GhashSq256b = pairs.iter().map(|&(a, b)| a * b).sum();
275 let deferred = GhashSq256b::reduce(
276 pairs.iter().map(|&(a, b)| GhashSq256b::wide_mul(a, b)).sum(),
277 );
278 prop_assert_eq!(deferred, eager);
279 }
280
281 #[test]
282 fn test_invert(a in arb_elem()) {
283 let inv = a.invert_or_zero();
284 if a == GhashSq256b::ZERO {
285 prop_assert_eq!(inv, GhashSq256b::ZERO);
286 } else {
287 prop_assert_eq!(a * inv, GhashSq256b::ONE);
288 }
289 }
290
291 #[test]
292 fn test_serialization_roundtrip(a in arb_elem()) {
293 let mut buf = Vec::new();
294 a.serialize(&mut buf).unwrap();
295 prop_assert_eq!(buf.len(), GhashSq256b::BYTE_SIZE);
296 let b = GhashSq256b::deserialize(buf.as_slice()).unwrap();
297 prop_assert_eq!(a, b);
298 }
299
300 #[test]
301 fn test_ghash_extension_bases_roundtrip(a in arb_elem()) {
302 let bases: Vec<BinaryField128bGhash> =
303 ExtensionField::<BinaryField128bGhash>::iter_bases(&a).collect();
304 prop_assert_eq!(bases.len(), 2);
305 prop_assert_eq!(
306 <GhashSq256b as ExtensionField<BinaryField128bGhash>>::from_bases(bases),
307 a,
308 );
309 }
310
311 #[test]
312 fn test_b1b_extension_bases_roundtrip(a in arb_elem()) {
313 let bases: Vec<BinaryField1b> =
314 ExtensionField::<BinaryField1b>::iter_bases(&a).collect();
315 prop_assert_eq!(bases.len(), 256);
316 prop_assert_eq!(
317 <GhashSq256b as ExtensionField<BinaryField1b>>::from_bases(bases),
318 a,
319 );
320 }
321
322 #[test]
323 fn test_square_transpose_ghash(a in arb_elem(), b in arb_elem()) {
324 let mut values = [a, b];
325 let expected = naive_square_transpose::<BinaryField128bGhash>(&values);
326 <GhashSq256b as ExtensionField<BinaryField128bGhash>>::square_transpose(&mut values);
327 prop_assert_eq!(values.as_slice(), expected.as_slice());
328 }
329
330 #[test]
331 fn test_square_transpose_b1b(values in prop::collection::vec(arb_elem(), 256)) {
332 let mut values = values;
333 let expected = naive_square_transpose::<BinaryField1b>(&values);
334 <GhashSq256b as ExtensionField<BinaryField1b>>::square_transpose(&mut values);
335 prop_assert_eq!(values, expected);
336 }
337 }
338}