Skip to main content

primitives/algebra/field/binary/
gf2_128_field.rs

1use std::{
2    iter::{Product, Sum},
3    mem::MaybeUninit,
4    ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign},
5};
6
7use ff::Field;
8use hybrid_array::Array;
9use rand::RngCore;
10use serde::{Deserialize, Serialize};
11use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
12use typenum::{U1, U128, U16};
13
14use crate::{
15    algebra::{
16        field::{
17            binary::{
18                gf2_ext::{Gf2LimbsWide, MulWide},
19                Gf2_128,
20            },
21            FieldExtension,
22        },
23        ops::{AccReduce, DefaultDotProduct, IntoWide, MulAccReduce, ReduceWide},
24        uniform_bytes::FromUniformBytes,
25    },
26    errors::PrimitiveError,
27    random::{CryptoRngCore, Random},
28    types::{HeapArray, Positive},
29    utils::codec::InPlaceCodec,
30};
31
32/// GF(2^128) presented as a standalone field (`Subfield = Self`).
33///
34/// Unlike [`Gf2_128`], whose `FieldExtension` impl has `Subfield = Gf2` (so
35/// `FieldShare<Gf2_128>` = `BitShare` authenticates a single bit), this wrapper is its own
36/// subfield: `FieldShare<Gf2_128Field>` authenticates a full 128-bit secret.
37///
38/// Caveat:
39/// - Order-dependent plaintext circuit ops (`Gt`/`Ge`, signed `BitExtract`, `EuclDiv`/`Mod`) are
40///   well-defined on the byte representation but carry no algebraic meaning in char 2.
41#[derive(
42    Copy, Clone, Default, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize,
43)]
44#[serde(transparent)]
45#[repr(transparent)]
46pub struct Gf2_128Field(pub Gf2_128);
47
48// SAFETY: `Gf2_128Field` is `#[repr(transparent)]` over `Gf2_128`; its encoding is exactly
49// `Gf2_128`'s `InPlaceCodec` encoding.
50unsafe impl InPlaceCodec for Gf2_128Field {
51    const ENCODED_SIZE: usize = Gf2_128::ENCODED_SIZE;
52
53    fn write_le_bytes(&self, out: &mut [MaybeUninit<u8>]) {
54        self.0.write_le_bytes(out);
55    }
56
57    fn read_le_bytes(bytes: &[u8]) -> Result<Self, PrimitiveError> {
58        Gf2_128::read_le_bytes(bytes).map(Gf2_128Field)
59    }
60}
61
62///////////////////////////////////////////////////////////////////////////////////////////////////
63// Arithmetic ops
64///////////////////////////////////////////////////////////////////////////////////////////////////
65
66#[macros::op_variants(owned)]
67impl<'a> MulAssign<&'a Gf2_128Field> for Gf2_128Field {
68    #[inline]
69    fn mul_assign(&mut self, rhs: &'a Gf2_128Field) {
70        self.0 *= rhs.0;
71    }
72}
73
74#[macros::op_variants(owned)]
75impl<'a> Mul<&'a Gf2_128Field> for Gf2_128Field {
76    type Output = Self;
77
78    #[inline]
79    fn mul(mut self, rhs: &'a Gf2_128Field) -> Self::Output {
80        self.mul_assign(rhs);
81        self
82    }
83}
84
85#[macros::op_variants(owned)]
86impl<'a> AddAssign<&'a Gf2_128Field> for Gf2_128Field {
87    #[inline]
88    fn add_assign(&mut self, rhs: &'a Gf2_128Field) {
89        self.0 += rhs.0;
90    }
91}
92
93#[macros::op_variants(owned)]
94impl<'a> Add<&'a Gf2_128Field> for Gf2_128Field {
95    type Output = Self;
96
97    #[inline]
98    fn add(mut self, rhs: &'a Gf2_128Field) -> Self::Output {
99        self.add_assign(rhs);
100        self
101    }
102}
103
104#[macros::op_variants(owned)]
105impl<'a> SubAssign<&'a Gf2_128Field> for Gf2_128Field {
106    #[inline]
107    fn sub_assign(&mut self, rhs: &'a Gf2_128Field) {
108        self.0 -= rhs.0;
109    }
110}
111
112#[macros::op_variants(owned)]
113impl<'a> Sub<&'a Gf2_128Field> for Gf2_128Field {
114    type Output = Self;
115
116    #[inline]
117    fn sub(mut self, rhs: &'a Gf2_128Field) -> Self::Output {
118        self.sub_assign(rhs);
119        self
120    }
121}
122
123#[macros::op_variants(borrowed)]
124impl Neg for Gf2_128Field {
125    type Output = Gf2_128Field;
126
127    #[inline]
128    fn neg(self) -> Self::Output {
129        self
130    }
131}
132
133impl Sum for Gf2_128Field {
134    fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
135        iter.fold(<Self as Field>::ZERO, |acc, x| acc + x)
136    }
137}
138
139impl<'a> Sum<&'a Self> for Gf2_128Field {
140    fn sum<I: Iterator<Item = &'a Self>>(iter: I) -> Self {
141        iter.fold(<Self as Field>::ZERO, |acc, x| acc + x)
142    }
143}
144
145impl Product for Gf2_128Field {
146    fn product<I: Iterator<Item = Self>>(iter: I) -> Self {
147        iter.fold(<Self as Field>::ONE, |acc, x| acc * x)
148    }
149}
150
151impl<'a> Product<&'a Self> for Gf2_128Field {
152    fn product<I: Iterator<Item = &'a Self>>(iter: I) -> Self {
153        iter.fold(<Self as Field>::ONE, |acc, x| acc * x)
154    }
155}
156
157///////////////////////////////////////////////////////////////////////////////////////////////////
158// Constant time
159///////////////////////////////////////////////////////////////////////////////////////////////////
160
161impl ConditionallySelectable for Gf2_128Field {
162    #[inline]
163    fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
164        Self(Gf2_128::conditional_select(&a.0, &b.0, choice))
165    }
166}
167
168impl ConstantTimeEq for Gf2_128Field {
169    #[inline]
170    fn ct_eq(&self, other: &Self) -> Choice {
171        self.0.ct_eq(&other.0)
172    }
173}
174
175///////////////////////////////////////////////////////////////////////////////////////////////////
176// Field traits
177///////////////////////////////////////////////////////////////////////////////////////////////////
178
179impl Field for Gf2_128Field {
180    const ZERO: Self = Self(<Gf2_128 as Field>::ZERO);
181    const ONE: Self = Self(<Gf2_128 as Field>::ONE);
182
183    fn random(rng: impl RngCore) -> Self {
184        Self(<Gf2_128 as Field>::random(rng))
185    }
186
187    fn square(&self) -> Self {
188        Self(self.0.square())
189    }
190
191    fn double(&self) -> Self {
192        Self(self.0.double())
193    }
194
195    fn invert(&self) -> CtOption<Self> {
196        self.0.invert().map(Self)
197    }
198
199    fn sqrt_ratio(num: &Self, div: &Self) -> (Choice, Self) {
200        let (is_valid, root) = Gf2_128::sqrt_ratio(&num.0, &div.0);
201        (is_valid, Self(root))
202    }
203
204    fn sqrt(&self) -> CtOption<Self> {
205        self.0.sqrt().map(Self)
206    }
207}
208
209impl FieldExtension for Gf2_128Field {
210    type Subfield = Self;
211    type Degree = U1;
212    type FieldBitSize = U128;
213    type FieldBytesSize = U16;
214
215    fn to_subfield_elements(&self) -> Array<Self::Subfield, Self::Degree> {
216        Array([*self])
217    }
218
219    fn from_subfield_elements(elems: Array<Self::Subfield, Self::Degree>) -> Self {
220        elems[0]
221    }
222
223    fn to_le_bytes(&self) -> Array<u8, Self::FieldBytesSize> {
224        self.0.to_le_bytes()
225    }
226
227    fn from_le_bytes(bytes: &[u8]) -> Option<Self> {
228        Gf2_128::from_le_bytes(bytes).map(Self)
229    }
230
231    fn mul_by_subfield(&self, other: &Self::Subfield) -> Self {
232        *self * other
233    }
234
235    fn generator() -> Self {
236        Self(Gf2_128::generator())
237    }
238
239    // Must delegate to the char-2 override: the trait default `*self * 2` is the zero map in a
240    // binary field.
241    fn linear_orthomorphism(&self) -> Self {
242        Self(self.0.linear_orthomorphism())
243    }
244}
245
246impl Random for Gf2_128Field {
247    fn random(rng: impl CryptoRngCore) -> Self {
248        Self(Random::random(rng))
249    }
250
251    fn random_array<M: Positive>(mut rng: impl CryptoRngCore) -> HeapArray<Self, M> {
252        let mut buf = HeapArray::<Self, M>::default().into_box_bytes();
253        rng.fill_bytes(&mut buf);
254        HeapArray::from_box_bytes(buf)
255    }
256}
257
258unsafe impl bytemuck::Zeroable for Gf2_128Field {}
259unsafe impl bytemuck::Pod for Gf2_128Field {}
260
261impl FromUniformBytes for Gf2_128Field {
262    type UniformBytes = U16;
263
264    fn from_uniform_bytes(bytes: &Array<u8, Self::UniformBytes>) -> Self {
265        Self(Gf2_128::from_uniform_bytes(bytes))
266    }
267}
268
269impl From<u64> for Gf2_128Field {
270    fn from(val: u64) -> Self {
271        Self(Gf2_128::from(val))
272    }
273}
274
275impl From<u128> for Gf2_128Field {
276    fn from(val: u128) -> Self {
277        Self(Gf2_128::from(val))
278    }
279}
280
281///////////////////////////////////////////////////////////////////////////////////////////////////
282// Wide ops
283///////////////////////////////////////////////////////////////////////////////////////////////////
284
285impl IntoWide<Gf2LimbsWide<2>> for Gf2_128Field {
286    #[inline]
287    fn to_wide(&self) -> Gf2LimbsWide<2> {
288        <Gf2_128 as IntoWide<Gf2LimbsWide<2>>>::to_wide(&self.0)
289    }
290
291    #[inline]
292    fn zero_wide() -> Gf2LimbsWide<2> {
293        <Gf2_128 as IntoWide<Gf2LimbsWide<2>>>::zero_wide()
294    }
295}
296
297impl ReduceWide<Gf2LimbsWide<2>> for Gf2_128Field {
298    #[inline]
299    fn reduce_mod_order(a: Gf2LimbsWide<2>) -> Self {
300        Self(Gf2_128::reduce_mod_order(a))
301    }
302}
303
304impl IntoWide for Gf2_128Field {
305    #[inline]
306    fn to_wide(&self) -> Self {
307        *self
308    }
309
310    #[inline]
311    fn zero_wide() -> Self {
312        <Self as Field>::ZERO
313    }
314}
315
316impl ReduceWide for Gf2_128Field {
317    #[inline]
318    fn reduce_mod_order(a: Self) -> Self {
319        a
320    }
321}
322
323impl MulAccReduce for Gf2_128Field {
324    type WideType = Gf2LimbsWide<2>;
325
326    #[inline]
327    fn mul_acc(acc: &mut Self::WideType, a: Self, b: Self) {
328        *acc += a.0.mul_wide(b.0);
329    }
330}
331
332impl MulAccReduce<Self, &Self> for Gf2_128Field {
333    type WideType = Gf2LimbsWide<2>;
334
335    #[inline]
336    fn mul_acc(acc: &mut Self::WideType, a: Self, b: &Self) {
337        <Self as MulAccReduce>::mul_acc(acc, a, *b);
338    }
339}
340
341impl MulAccReduce<&Self, Self> for Gf2_128Field {
342    type WideType = Gf2LimbsWide<2>;
343
344    #[inline]
345    fn mul_acc(acc: &mut Self::WideType, a: &Self, b: Self) {
346        <Self as MulAccReduce>::mul_acc(acc, *a, b);
347    }
348}
349
350impl MulAccReduce<&Self, &Self> for Gf2_128Field {
351    type WideType = Gf2LimbsWide<2>;
352
353    #[inline]
354    fn mul_acc(acc: &mut Self::WideType, a: &Self, b: &Self) {
355        <Self as MulAccReduce>::mul_acc(acc, *a, *b);
356    }
357}
358
359impl AccReduce for Gf2_128Field {
360    type WideType = Self;
361
362    #[inline]
363    fn acc(acc: &mut Self, a: Self) {
364        *acc += a;
365    }
366}
367
368impl AccReduce<&Self> for Gf2_128Field {
369    type WideType = Self;
370
371    #[inline]
372    fn acc(acc: &mut Self, a: &Self) {
373        *acc += a;
374    }
375}
376
377impl DefaultDotProduct for Gf2_128Field {}
378impl DefaultDotProduct<Self, &Self> for Gf2_128Field {}
379impl DefaultDotProduct<&Self, Self> for Gf2_128Field {}
380impl DefaultDotProduct<&Self, &Self> for Gf2_128Field {}
381
382#[cfg(test)]
383mod test {
384    use ff::Field;
385    use subtle::ConstantTimeEq;
386    use typenum::Unsigned;
387
388    use super::Gf2_128Field;
389    use crate::{
390        algebra::{field::FieldExtension, ops::DotProduct},
391        random::{test_rng, Random},
392        utils::bincode_io,
393    };
394
395    type M = typenum::U100;
396
397    #[test]
398    fn test_field_axioms() {
399        let mut rng = test_rng();
400        for _ in 0..M::to_usize() {
401            let a: Gf2_128Field = Random::random(&mut rng);
402            let b: Gf2_128Field = Random::random(&mut rng);
403
404            // Char 2: a + a = 0, -a = a
405            assert_eq!(a + a, Gf2_128Field::ZERO);
406            assert_eq!(-a, a);
407            assert_eq!(a - b, a + b);
408
409            // Distributivity
410            assert_eq!(a * (a + b), a * a + a * b);
411
412            // Frobenius: a^(2^128) = a
413            let mut cumul = a;
414            for _ in 0..128 {
415                cumul *= cumul;
416            }
417            assert_eq!(a, cumul);
418        }
419    }
420
421    #[test]
422    fn test_invert_and_sqrt() {
423        let mut rng = test_rng();
424
425        assert!(bool::from(Gf2_128Field::ZERO.invert().is_none()));
426
427        for _ in 0..M::to_usize() {
428            let a: Gf2_128Field = Random::random(&mut rng);
429            if bool::from(a.is_zero()) {
430                continue;
431            }
432            assert_eq!(a * a.invert().unwrap(), Gf2_128Field::ONE);
433
434            let root = a.sqrt().unwrap();
435            assert_eq!(root * root, a);
436
437            let (is_valid, ratio_root) = Gf2_128Field::sqrt_ratio_ext(&Gf2_128Field::ONE, &a);
438            assert!(bool::from(is_valid));
439            assert_eq!(ratio_root * ratio_root * a, Gf2_128Field::ONE);
440        }
441    }
442
443    #[test]
444    fn test_linear_orthomorphism() {
445        // σ and σ'(x) = σ(x) + x must both be permutations; in particular neither may be the zero
446        // map (the trait default `x * 2` is zero in char 2).
447        let mut rng = test_rng();
448        for _ in 0..M::to_usize() {
449            let a: Gf2_128Field = Random::random(&mut rng);
450            if bool::from(a.is_zero()) {
451                continue;
452            }
453            let sigma = a.linear_orthomorphism();
454            assert_ne!(sigma, Gf2_128Field::ZERO);
455            assert_ne!(sigma + a, Gf2_128Field::ZERO);
456        }
457    }
458
459    #[test]
460    fn test_byte_roundtrips() {
461        let mut rng = test_rng();
462        for _ in 0..M::to_usize() {
463            let a: Gf2_128Field = Random::random(&mut rng);
464
465            let bytes = a.to_le_bytes();
466            assert_eq!(Gf2_128Field::from_le_bytes(&bytes), Some(a));
467
468            let serialized = bincode_io::serialize(&a).unwrap();
469            assert_eq!(
470                bincode_io::deserialize::<Gf2_128Field>(&serialized).unwrap(),
471                a
472            );
473        }
474    }
475
476    #[test]
477    fn test_dot_product() {
478        let mut rng = test_rng();
479        let a = Gf2_128Field::random_array::<typenum::U13>(&mut rng);
480        let b = Gf2_128Field::random_array::<typenum::U13>(&mut rng);
481
482        let expected = a
483            .iter()
484            .zip(b.iter())
485            .fold(Gf2_128Field::ZERO, |acc, (x, y)| acc + *x * y);
486        let actual = Gf2_128Field::dot(a, b);
487        assert_eq!(expected, actual);
488
489        assert_eq!(
490            Gf2_128Field::ZERO,
491            Gf2_128Field::dot(
492                std::iter::empty::<Gf2_128Field>(),
493                std::iter::empty::<Gf2_128Field>()
494            )
495        );
496    }
497
498    #[test]
499    fn test_constant_time_eq() {
500        let a = Gf2_128Field::from(42u64);
501        assert!(bool::from(a.ct_eq(&Gf2_128Field::from(42u64))));
502        assert!(!bool::from(a.ct_eq(&Gf2_128Field::from(43u64))));
503    }
504}