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