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