halo2curves_axiom/bn256/
fq12.rs

1use super::fq::Fq;
2use super::fq2::Fq2;
3use super::fq6::Fq6;
4use crate::ff::Field;
5use core::ops::{Add, Mul, Neg, Sub};
6use rand::RngCore;
7use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
8
9/// -GAMMA is a quadratic non-residue in Fp6. Fp12 = Fp6\[X\]/(X^2 + GAMMA)
10/// We introduce the variable w such that w^2 = -GAMMA
11// GAMMA = - v
12
13/// An element of Fq12, represented by c0 + c1 * w.
14#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]
15pub struct Fq12 {
16    pub c0: Fq6,
17    pub c1: Fq6,
18}
19
20impl ConditionallySelectable for Fq12 {
21    fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
22        Fq12 {
23            c0: Fq6::conditional_select(&a.c0, &b.c0, choice),
24            c1: Fq6::conditional_select(&a.c1, &b.c1, choice),
25        }
26    }
27}
28
29impl ConstantTimeEq for Fq12 {
30    fn ct_eq(&self, other: &Self) -> Choice {
31        self.c0.ct_eq(&other.c0) & self.c1.ct_eq(&other.c1)
32    }
33}
34
35impl Neg for Fq12 {
36    type Output = Fq12;
37
38    #[inline]
39    fn neg(self) -> Fq12 {
40        -&self
41    }
42}
43
44impl<'a> Neg for &'a Fq12 {
45    type Output = Fq12;
46
47    #[inline]
48    fn neg(self) -> Fq12 {
49        self.neg()
50    }
51}
52
53impl<'a, 'b> Sub<&'b Fq12> for &'a Fq12 {
54    type Output = Fq12;
55
56    #[inline]
57    fn sub(self, rhs: &'b Fq12) -> Fq12 {
58        self.sub(rhs)
59    }
60}
61
62impl<'a, 'b> Add<&'b Fq12> for &'a Fq12 {
63    type Output = Fq12;
64
65    #[inline]
66    fn add(self, rhs: &'b Fq12) -> Fq12 {
67        self.add(rhs)
68    }
69}
70
71impl<'a, 'b> Mul<&'b Fq12> for &'a Fq12 {
72    type Output = Fq12;
73
74    #[inline]
75    fn mul(self, rhs: &'b Fq12) -> Fq12 {
76        self.mul(rhs)
77    }
78}
79
80use crate::{
81    impl_add_binop_specify_output, impl_binops_additive, impl_binops_additive_specify_output,
82    impl_binops_multiplicative, impl_binops_multiplicative_mixed, impl_sub_binop_specify_output,
83    impl_sum_prod,
84};
85impl_binops_additive!(Fq12, Fq12);
86impl_binops_multiplicative!(Fq12, Fq12);
87impl_sum_prod!(Fq12);
88
89impl Fq12 {
90    #[inline]
91    pub const fn zero() -> Self {
92        Fq12 {
93            c0: Fq6::ZERO,
94            c1: Fq6::ZERO,
95        }
96    }
97
98    #[inline]
99    pub const fn one() -> Self {
100        Fq12 {
101            c0: Fq6::ONE,
102            c1: Fq6::ZERO,
103        }
104    }
105
106    pub fn mul_assign(&mut self, other: &Self) {
107        let t0 = self.c0 * other.c0;
108        let mut t1 = self.c1 * other.c1;
109        let t2 = other.c0 + other.c1;
110
111        self.c1 += &self.c0;
112        self.c1 *= &t2;
113        self.c1 -= &t0;
114        self.c1 -= &t1;
115
116        t1.mul_by_nonresidue();
117        self.c0 = t0 + t1;
118    }
119
120    pub fn square_assign(&mut self) {
121        let mut ab = self.c0 * self.c1;
122
123        let c0c1 = self.c0 + self.c1;
124
125        let mut c0 = self.c1;
126        c0.mul_by_nonresidue();
127        c0 += &self.c0;
128        c0 *= &c0c1;
129        c0 -= &ab;
130        self.c1 = ab;
131        self.c1 += &ab;
132        ab.mul_by_nonresidue();
133        c0 -= &ab;
134        self.c0 = c0;
135    }
136
137    pub fn double(&self) -> Self {
138        Self {
139            c0: self.c0.double(),
140            c1: self.c1.double(),
141        }
142    }
143
144    pub fn double_assign(&mut self) {
145        self.c0 = self.c0.double();
146        self.c1 = self.c1.double();
147    }
148
149    pub fn add(&self, other: &Self) -> Self {
150        Self {
151            c0: self.c0 + other.c0,
152            c1: self.c1 + other.c1,
153        }
154    }
155
156    pub fn sub(&self, other: &Self) -> Self {
157        Self {
158            c0: self.c0 - other.c0,
159            c1: self.c1 - other.c1,
160        }
161    }
162
163    pub fn mul(&self, other: &Self) -> Self {
164        let mut t = *other;
165        t.mul_assign(self);
166        t
167    }
168
169    pub fn square(&self) -> Self {
170        let mut t = *self;
171        t.square_assign();
172        t
173    }
174
175    #[inline(always)]
176    pub fn neg(&self) -> Self {
177        Self {
178            c0: -self.c0,
179            c1: -self.c1,
180        }
181    }
182
183    #[inline(always)]
184    pub fn conjugate(&mut self) {
185        self.c1 = -self.c1;
186    }
187
188    // pub fn conjugate(&self) -> Self {
189    //     Self {
190    //         c0: self.c0,
191    //         c1: -self.c1,
192    //     }
193    // }
194
195    pub fn frobenius_map(&mut self, power: usize) {
196        self.c0.frobenius_map(power);
197        self.c1.frobenius_map(power);
198
199        self.c1.c0.mul_assign(&FROBENIUS_COEFF_FQ12_C1[power % 12]);
200        self.c1.c1.mul_assign(&FROBENIUS_COEFF_FQ12_C1[power % 12]);
201        self.c1.c2.mul_assign(&FROBENIUS_COEFF_FQ12_C1[power % 12]);
202    }
203
204    pub fn mul_by_014(&mut self, c0: &Fq2, c1: &Fq2, c4: &Fq2) {
205        let mut aa = self.c0;
206        aa.mul_by_01(c0, c1);
207        let mut bb = self.c1;
208        bb.mul_by_1(c4);
209        let o = c1 + c4;
210        self.c1 += &self.c0;
211        self.c1.mul_by_01(c0, &o);
212        self.c1 -= &aa;
213        self.c1 -= &bb;
214        self.c0 = bb;
215        self.c0.mul_by_nonresidue();
216        self.c0 += &aa;
217    }
218
219    pub fn mul_by_034(&mut self, c0: &Fq2, c3: &Fq2, c4: &Fq2) {
220        let t0 = Fq6 {
221            c0: self.c0.c0 * c0,
222            c1: self.c0.c1 * c0,
223            c2: self.c0.c2 * c0,
224        };
225        let mut t1 = self.c1;
226        t1.mul_by_01(c3, c4);
227        let o = c0 + c3;
228        let mut t2 = self.c0 + self.c1;
229        t2.mul_by_01(&o, c4);
230        t2 -= t0;
231        self.c1 = t2 - t1;
232        t1.mul_by_nonresidue();
233        self.c0 = t0 + t1;
234    }
235
236    pub fn invert(&self) -> CtOption<Self> {
237        let mut c0s = self.c0;
238        c0s.square_assign();
239        let mut c1s = self.c1;
240        c1s.square_assign();
241        c1s.mul_by_nonresidue();
242        c0s -= &c1s;
243
244        c0s.invert().map(|t| {
245            let mut tmp = Fq12 { c0: t, c1: t };
246            tmp.c0.mul_assign(&self.c0);
247            tmp.c1.mul_assign(&self.c1);
248            tmp.c1 = tmp.c1.neg();
249
250            tmp
251        })
252    }
253
254    pub fn cyclotomic_square(&mut self) {
255        fn fp4_square(c0: &mut Fq2, c1: &mut Fq2, a0: &Fq2, a1: &Fq2) {
256            let t0 = a0.square();
257            let t1 = a1.square();
258            let mut t2 = t1;
259            t2.mul_by_nonresidue();
260            *c0 = t2 + t0;
261            t2 = a0 + a1;
262            t2.square_assign();
263            t2 -= t0;
264            *c1 = t2 - t1;
265        }
266
267        let mut t3 = Fq2::zero();
268        let mut t4 = Fq2::zero();
269        let mut t5 = Fq2::zero();
270        let mut t6 = Fq2::zero();
271
272        fp4_square(&mut t3, &mut t4, &self.c0.c0, &self.c1.c1);
273        let mut t2 = t3 - self.c0.c0;
274        t2.double_assign();
275        self.c0.c0 = t2 + t3;
276
277        t2 = t4 + self.c1.c1;
278        t2.double_assign();
279        self.c1.c1 = t2 + t4;
280
281        fp4_square(&mut t3, &mut t4, &self.c1.c0, &self.c0.c2);
282        fp4_square(&mut t5, &mut t6, &self.c0.c1, &self.c1.c2);
283
284        t2 = t3 - self.c0.c1;
285        t2.double_assign();
286        self.c0.c1 = t2 + t3;
287        t2 = t4 + self.c1.c2;
288        t2.double_assign();
289        self.c1.c2 = t2 + t4;
290        t3 = t6;
291        t3.mul_by_nonresidue();
292        t2 = t3 + self.c1.c0;
293        t2.double_assign();
294        self.c1.c0 = t2 + t3;
295        t2 = t5 - self.c0.c2;
296        t2.double_assign();
297        self.c0.c2 = t2 + t5;
298    }
299}
300
301impl Field for Fq12 {
302    const ZERO: Self = Self::zero();
303    const ONE: Self = Self::one();
304
305    fn random(mut rng: impl RngCore) -> Self {
306        Fq12 {
307            c0: Fq6::random(&mut rng),
308            c1: Fq6::random(&mut rng),
309        }
310    }
311
312    fn is_zero(&self) -> Choice {
313        self.c0.is_zero() & self.c1.is_zero()
314    }
315
316    fn square(&self) -> Self {
317        self.square()
318    }
319
320    fn double(&self) -> Self {
321        self.double()
322    }
323
324    fn sqrt(&self) -> CtOption<Self> {
325        unimplemented!()
326    }
327
328    fn sqrt_ratio(_num: &Self, _div: &Self) -> (Choice, Self) {
329        unimplemented!()
330    }
331
332    fn invert(&self) -> CtOption<Self> {
333        self.invert()
334    }
335}
336
337// non_residue^((modulus^i-1)/6) for i=0,...,11
338pub const FROBENIUS_COEFF_FQ12_C1: [Fq2; 12] = [
339    // Fq2(u + 9)**(((q^0) - 1) / 6)
340    // Fq points are represented in Montgomery form with R = 2^256
341    Fq2 {
342        c0: Fq([
343            0xd35d438dc58f0d9d,
344            0x0a78eb28f5c70b3d,
345            0x666ea36f7879462c,
346            0x0e0a77c19a07df2f,
347        ]),
348        c1: Fq([0x0, 0x0, 0x0, 0x0]),
349    },
350    // Fq2(u + 9)**(((q^1) - 1) / 6)
351    Fq2 {
352        c0: Fq([
353            0xaf9ba69633144907,
354            0xca6b1d7387afb78a,
355            0x11bded5ef08a2087,
356            0x02f34d751a1f3a7c,
357        ]),
358        c1: Fq([
359            0xa222ae234c492d72,
360            0xd00f02a4565de15b,
361            0xdc2ff3a253dfc926,
362            0x10a75716b3899551,
363        ]),
364    },
365    // Fq2(u + 9)**(((q^2) - 1) / 6)
366    Fq2 {
367        c0: Fq([
368            0xca8d800500fa1bf2,
369            0xf0c5d61468b39769,
370            0x0e201271ad0d4418,
371            0x04290f65bad856e6,
372        ]),
373        c1: Fq([0x0, 0x0, 0x0, 0x0]),
374    },
375    // Fq2(u + 9)**(((q^3) - 1) / 6)
376    Fq2 {
377        c0: Fq([
378            0x365316184e46d97d,
379            0x0af7129ed4c96d9f,
380            0x659da72fca1009b5,
381            0x08116d8983a20d23,
382        ]),
383        c1: Fq([
384            0xb1df4af7c39c1939,
385            0x3d9f02878a73bf7f,
386            0x9b2220928caf0ae0,
387            0x26684515eff054a6,
388        ]),
389    },
390    // Fq2(u + 9)**(((q^4) - 1) / 6)
391    Fq2 {
392        c0: Fq([
393            0x3350c88e13e80b9c,
394            0x7dce557cdb5e56b9,
395            0x6001b4b8b615564a,
396            0x2682e617020217e0,
397        ]),
398        c1: Fq([0x0, 0x0, 0x0, 0x0]),
399    },
400    // Fq2(u + 9)**(((q^5) - 1) / 6)
401    Fq2 {
402        c0: Fq([
403            0x86b76f821b329076,
404            0x408bf52b4d19b614,
405            0x53dfb9d0d985e92d,
406            0x051e20146982d2a7,
407        ]),
408        c1: Fq([
409            0x0fbc9cd47752ebc7,
410            0x6d8fffe33415de24,
411            0xbef22cf038cf41b9,
412            0x15c0edff3c66bf54,
413        ]),
414    },
415    // Fq2(u + 9)**(((q^6) - 1) / 6)
416    Fq2 {
417        c0: Fq([
418            0x68c3488912edefaa,
419            0x8d087f6872aabf4f,
420            0x51e1a24709081231,
421            0x2259d6b14729c0fa,
422        ]),
423        c1: Fq([0x0, 0x0, 0x0, 0x0]),
424    },
425    // Fq2(u + 9)**(((q^7) - 1) / 6)
426    Fq2 {
427        c0: Fq([
428            0x8c84e580a568b440,
429            0xcd164d1de0c21302,
430            0xa692585790f737d5,
431            0x2d7100fdc71265ad,
432        ]),
433        c1: Fq([
434            0x99fdddf38c33cfd5,
435            0xc77267ed1213e931,
436            0xdc2052142da18f36,
437            0x1fbcf75c2da80ad7,
438        ]),
439    },
440    // Fq2(u + 9)**(((q^8) - 1) / 6)
441    Fq2 {
442        c0: Fq([
443            0x71930c11d782e155,
444            0xa6bb947cffbe3323,
445            0xaa303344d4741444,
446            0x2c3b3f0d26594943,
447        ]),
448        c1: Fq([0x0, 0x0, 0x0, 0x0]),
449    },
450    // Fq2(u + 9)**(((q^9) - 1) / 6)
451    Fq2 {
452        c0: Fq([
453            0x05cd75fe8a3623ca,
454            0x8c8a57f293a85cee,
455            0x52b29e86b7714ea8,
456            0x2852e0e95d8f9306,
457        ]),
458        c1: Fq([
459            0x8a41411f14e0e40e,
460            0x59e26809ddfe0b0d,
461            0x1d2e2523f4d24d7d,
462            0x09fc095cf1414b83,
463        ]),
464    },
465    // Fq2(u + 9)**(((q^10) - 1) / 6)
466    Fq2 {
467        c0: Fq([
468            0x08cfc388c494f1ab,
469            0x19b315148d1373d4,
470            0x584e90fdcb6c0213,
471            0x09e1685bdf2f8849,
472        ]),
473        c1: Fq([0x0, 0x0, 0x0, 0x0]),
474    },
475    // Fq2(u + 9)**(((q^11) - 1) / 6)
476    Fq2 {
477        c0: Fq([
478            0xb5691c94bd4a6cd1,
479            0x56f575661b581478,
480            0x64708be5a7fb6f30,
481            0x2b462e5e77aecd82,
482        ]),
483        c1: Fq([
484            0x2c63ef42612a1180,
485            0x29f16aae345bec69,
486            0xf95e18c648b216a4,
487            0x1aa36073a4cae0d4,
488        ]),
489    },
490];
491
492#[cfg(test)]
493use rand::SeedableRng;
494#[cfg(test)]
495use rand_xorshift::XorShiftRng;
496
497#[test]
498fn test_fq12_mul_by_014() {
499    let mut rng = XorShiftRng::from_seed([
500        0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
501        0xe5,
502    ]);
503
504    for _ in 0..1000 {
505        let c0 = Fq2::random(&mut rng);
506        let c1 = Fq2::random(&mut rng);
507        let c5 = Fq2::random(&mut rng);
508        let mut a = Fq12::random(&mut rng);
509        let mut b = a;
510
511        a.mul_by_014(&c0, &c1, &c5);
512        b.mul_assign(&Fq12 {
513            c0: Fq6 {
514                c0,
515                c1,
516                c2: Fq2::zero(),
517            },
518            c1: Fq6 {
519                c0: Fq2::zero(),
520                c1: c5,
521                c2: Fq2::zero(),
522            },
523        });
524
525        assert_eq!(a, b);
526    }
527}
528
529#[test]
530fn test_fq12_mul_by_034() {
531    let mut rng = XorShiftRng::from_seed([
532        0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
533        0xe5,
534    ]);
535
536    for _ in 0..1000 {
537        let c0 = Fq2::random(&mut rng);
538        let c3 = Fq2::random(&mut rng);
539        let c4 = Fq2::random(&mut rng);
540        let mut a = Fq12::random(&mut rng);
541        let mut b = a;
542
543        a.mul_by_034(&c0, &c3, &c4);
544        b.mul_assign(&Fq12 {
545            c0: Fq6 {
546                c0,
547                c1: Fq2::zero(),
548                c2: Fq2::zero(),
549            },
550            c1: Fq6 {
551                c0: c3,
552                c1: c4,
553                c2: Fq2::zero(),
554            },
555        });
556
557        assert_eq!(a, b);
558    }
559}
560
561#[test]
562fn test_squaring() {
563    let mut rng = XorShiftRng::from_seed([
564        0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
565        0xe5,
566    ]);
567
568    for _ in 0..1000 {
569        let mut a = Fq12::random(&mut rng);
570        let mut b = a;
571        b.mul_assign(&a);
572        a.square_assign();
573        assert_eq!(a, b);
574    }
575}
576
577#[test]
578fn test_frobenius() {
579    let mut rng = XorShiftRng::from_seed([
580        0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
581        0xe5,
582    ]);
583
584    for _ in 0..100 {
585        for i in 0..14 {
586            let mut a = Fq12::random(&mut rng);
587            let mut b = a;
588
589            for _ in 0..i {
590                a = a.pow_vartime([
591                    0x3c208c16d87cfd47,
592                    0x97816a916871ca8d,
593                    0xb85045b68181585d,
594                    0x30644e72e131a029,
595                ]);
596            }
597            b.frobenius_map(i);
598
599            assert_eq!(a, b);
600        }
601    }
602}
603
604#[test]
605fn test_field() {
606    crate::tests::field::random_field_tests::<Fq12>("fq12".to_string());
607}