halo2curves_axiom/bn256/
fq6.rs

1use super::fq::Fq;
2use super::fq2::Fq2;
3use crate::ff::Field;
4use core::ops::{Add, Mul, Neg, Sub};
5use rand::RngCore;
6use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
7
8/// -BETA is a cubic non-residue in Fp2. Fp6 = Fp2\[X\]/(X^3 + BETA)
9/// We introduce the variable v such that v^3 = -BETA
10// BETA = - (u + 9)
11
12/// An element of Fq6, represented by c0 + c1 * v + c2 * v^2.
13#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]
14pub struct Fq6 {
15    pub c0: Fq2,
16    pub c1: Fq2,
17    pub c2: Fq2,
18}
19
20impl ConditionallySelectable for Fq6 {
21    fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
22        Fq6 {
23            c0: Fq2::conditional_select(&a.c0, &b.c0, choice),
24            c1: Fq2::conditional_select(&a.c1, &b.c1, choice),
25            c2: Fq2::conditional_select(&a.c2, &b.c2, choice),
26        }
27    }
28}
29
30impl ConstantTimeEq for Fq6 {
31    fn ct_eq(&self, other: &Self) -> Choice {
32        self.c0.ct_eq(&other.c0) & self.c1.ct_eq(&other.c1) & self.c2.ct_eq(&other.c2)
33    }
34}
35
36impl Neg for Fq6 {
37    type Output = Fq6;
38
39    #[inline]
40    fn neg(self) -> Fq6 {
41        -&self
42    }
43}
44
45impl<'a> Neg for &'a Fq6 {
46    type Output = Fq6;
47
48    #[inline]
49    fn neg(self) -> Fq6 {
50        self.neg()
51    }
52}
53
54impl<'a, 'b> Sub<&'b Fq6> for &'a Fq6 {
55    type Output = Fq6;
56
57    #[inline]
58    fn sub(self, rhs: &'b Fq6) -> Fq6 {
59        self.sub(rhs)
60    }
61}
62
63impl<'a, 'b> Add<&'b Fq6> for &'a Fq6 {
64    type Output = Fq6;
65
66    #[inline]
67    fn add(self, rhs: &'b Fq6) -> Fq6 {
68        self.add(rhs)
69    }
70}
71
72impl<'a, 'b> Mul<&'b Fq6> for &'a Fq6 {
73    type Output = Fq6;
74
75    #[inline]
76    fn mul(self, rhs: &'b Fq6) -> Fq6 {
77        self.mul(rhs)
78    }
79}
80
81use crate::{
82    impl_add_binop_specify_output, impl_binops_additive, impl_binops_additive_specify_output,
83    impl_binops_multiplicative, impl_binops_multiplicative_mixed, impl_sub_binop_specify_output,
84    impl_sum_prod,
85};
86impl_binops_additive!(Fq6, Fq6);
87impl_binops_multiplicative!(Fq6, Fq6);
88impl_sum_prod!(Fq6);
89
90impl Fq6 {
91    #[inline]
92    pub const fn zero() -> Self {
93        Fq6 {
94            c0: Fq2::ZERO,
95            c1: Fq2::ZERO,
96            c2: Fq2::ZERO,
97        }
98    }
99
100    #[inline]
101    pub const fn one() -> Self {
102        Fq6 {
103            c0: Fq2::ONE,
104            c1: Fq2::ZERO,
105            c2: Fq2::ZERO,
106        }
107    }
108
109    pub fn mul_assign(&mut self, other: &Self) {
110        let mut a_a = self.c0;
111        let mut b_b = self.c1;
112        let mut c_c = self.c2;
113        a_a *= &other.c0;
114        b_b *= &other.c1;
115        c_c *= &other.c2;
116
117        let mut t1 = other.c1;
118        t1 += &other.c2;
119        {
120            let mut tmp = self.c1;
121            tmp += &self.c2;
122
123            t1 *= &tmp;
124            t1 -= &b_b;
125            t1 -= &c_c;
126            t1.mul_by_nonresidue();
127            t1 += &a_a;
128        }
129
130        let mut t3 = other.c0;
131        t3 += &other.c2;
132        {
133            let mut tmp = self.c0;
134            tmp += &self.c2;
135
136            t3 *= &tmp;
137            t3 -= &a_a;
138            t3 += &b_b;
139            t3 -= &c_c;
140        }
141
142        let mut t2 = other.c0;
143        t2 += &other.c1;
144        {
145            let mut tmp = self.c0;
146            tmp += &self.c1;
147
148            t2 *= &tmp;
149            t2 -= &a_a;
150            t2 -= &b_b;
151            c_c.mul_by_nonresidue();
152            t2 += &c_c;
153        }
154
155        self.c0 = t1;
156        self.c1 = t2;
157        self.c2 = t3;
158    }
159
160    pub fn square_assign(&mut self) {
161        // s0 = a^2
162        let mut s0 = self.c0;
163        s0.square_assign();
164        // s1 = 2ab
165        let mut ab = self.c0;
166        ab *= &self.c1;
167        let mut s1 = ab;
168        s1.double_assign();
169        // s2 = (a - b + c)^2
170        let mut s2 = self.c0;
171        s2 -= &self.c1;
172        s2 += &self.c2;
173        s2.square_assign();
174        // bc
175        let mut bc = self.c1;
176        bc *= &self.c2;
177        // s3 = 2bc
178        let mut s3 = bc;
179        s3.double_assign();
180        // s4 = c^2
181        let mut s4 = self.c2;
182        s4.square_assign();
183
184        // new c0 = 2bc.mul_by_xi + a^2
185        self.c0 = s3;
186        self.c0.mul_by_nonresidue();
187        // self.c0.mul_by_xi();
188        self.c0 += &s0;
189
190        // new c1 = (c^2).mul_by_xi + 2ab
191        self.c1 = s4;
192        self.c1.mul_by_nonresidue();
193        self.c1 += &s1;
194
195        // new c2 = 2ab + (a - b + c)^2 + 2bc - a^2 - c^2 = b^2 + 2ac
196        self.c2 = s1;
197        self.c2 += &s2;
198        self.c2 += &s3;
199        self.c2 -= &s0;
200        self.c2 -= &s4;
201    }
202
203    pub fn double(&self) -> Self {
204        Self {
205            c0: self.c0.double(),
206            c1: self.c1.double(),
207            c2: self.c2.double(),
208        }
209    }
210
211    pub fn double_assign(&mut self) {
212        self.c0 = self.c0.double();
213        self.c1 = self.c1.double();
214        self.c2 = self.c2.double();
215    }
216
217    pub fn add(&self, other: &Self) -> Self {
218        Self {
219            c0: self.c0 + other.c0,
220            c1: self.c1 + other.c1,
221            c2: self.c2 + other.c2,
222        }
223    }
224
225    pub fn sub(&self, other: &Self) -> Self {
226        Self {
227            c0: self.c0 - other.c0,
228            c1: self.c1 - other.c1,
229            c2: self.c2 - other.c2,
230        }
231    }
232
233    pub fn mul(&self, other: &Self) -> Self {
234        let mut t = *other;
235        t.mul_assign(self);
236        t
237    }
238
239    pub fn square(&self) -> Self {
240        let mut t = *self;
241        t.square_assign();
242        t
243    }
244
245    pub fn neg(&self) -> Self {
246        Self {
247            c0: -self.c0,
248            c1: -self.c1,
249            c2: -self.c2,
250        }
251    }
252
253    pub fn frobenius_map(&mut self, power: usize) {
254        self.c0.frobenius_map(power);
255        self.c1.frobenius_map(power);
256        self.c2.frobenius_map(power);
257
258        self.c1.mul_assign(&FROBENIUS_COEFF_FQ6_C1[power % 6]);
259        self.c2.mul_assign(&FROBENIUS_COEFF_FQ6_C2[power % 6]);
260    }
261
262    /// Multiply by cubic nonresidue v.
263    pub fn mul_by_nonresidue(&mut self) {
264        use std::mem::swap;
265        swap(&mut self.c0, &mut self.c1);
266        swap(&mut self.c0, &mut self.c2);
267        // c0, c1, c2 -> c2, c0, c1
268        self.c0.mul_by_nonresidue();
269    }
270
271    pub fn mul_by_1(&mut self, c1: &Fq2) {
272        let mut b_b = self.c1;
273        b_b *= c1;
274
275        let mut t1 = *c1;
276        {
277            let mut tmp = self.c1;
278            tmp += &self.c2;
279
280            t1 *= &tmp;
281            t1 -= &b_b;
282            t1.mul_by_nonresidue();
283        }
284
285        let mut t2 = *c1;
286        {
287            let mut tmp = self.c0;
288            tmp += &self.c1;
289
290            t2 *= &tmp;
291            t2 -= &b_b;
292        }
293
294        self.c0 = t1;
295        self.c1 = t2;
296        self.c2 = b_b;
297    }
298
299    pub fn mul_by_01(&mut self, c0: &Fq2, c1: &Fq2) {
300        let mut a_a = self.c0;
301        let mut b_b = self.c1;
302        a_a *= c0;
303        b_b *= c1;
304
305        let mut t1 = *c1;
306        {
307            let mut tmp = self.c1;
308            tmp += &self.c2;
309
310            t1 *= &tmp;
311            t1 -= &b_b;
312            t1.mul_by_nonresidue();
313            t1 += &a_a;
314        }
315
316        let mut t3 = *c0;
317        {
318            let mut tmp = self.c0;
319            tmp += &self.c2;
320
321            t3 *= &tmp;
322            t3 -= &a_a;
323            t3 += &b_b;
324        }
325
326        let mut t2 = *c0;
327        t2 += c1;
328        {
329            let mut tmp = self.c0;
330            tmp += &self.c1;
331
332            t2 *= &tmp;
333            t2 -= &a_a;
334            t2 -= &b_b;
335        }
336
337        self.c0 = t1;
338        self.c1 = t2;
339        self.c2 = t3;
340    }
341
342    fn invert(&self) -> CtOption<Self> {
343        let mut c0 = self.c2;
344        c0.mul_by_nonresidue();
345        c0 *= &self.c1;
346        c0 = -c0;
347        {
348            let mut c0s = self.c0;
349            c0s.square_assign();
350            c0 += &c0s;
351        }
352        let mut c1 = self.c2;
353        c1.square_assign();
354        c1.mul_by_nonresidue();
355        {
356            let mut c01 = self.c0;
357            c01 *= &self.c1;
358            c1 -= &c01;
359        }
360        let mut c2 = self.c1;
361        c2.square_assign();
362        {
363            let mut c02 = self.c0;
364            c02 *= &self.c2;
365            c2 -= &c02;
366        }
367
368        let mut tmp1 = self.c2;
369        tmp1 *= &c1;
370        let mut tmp2 = self.c1;
371        tmp2 *= &c2;
372        tmp1 += &tmp2;
373        tmp1.mul_by_nonresidue();
374        tmp2 = self.c0;
375        tmp2 *= &c0;
376        tmp1 += &tmp2;
377
378        tmp1.invert().map(|t| {
379            let mut tmp = Fq6 {
380                c0: t,
381                c1: t,
382                c2: t,
383            };
384            tmp.c0 *= &c0;
385            tmp.c1 *= &c1;
386            tmp.c2 *= &c2;
387
388            tmp
389        })
390    }
391}
392
393impl Field for Fq6 {
394    const ZERO: Self = Self::zero();
395    const ONE: Self = Self::one();
396
397    fn random(mut rng: impl RngCore) -> Self {
398        Fq6 {
399            c0: Fq2::random(&mut rng),
400            c1: Fq2::random(&mut rng),
401            c2: Fq2::random(&mut rng),
402        }
403    }
404
405    fn is_zero(&self) -> Choice {
406        self.c0.is_zero() & self.c1.is_zero()
407    }
408
409    fn square(&self) -> Self {
410        self.square()
411    }
412
413    fn double(&self) -> Self {
414        self.double()
415    }
416
417    fn sqrt(&self) -> CtOption<Self> {
418        unimplemented!()
419    }
420
421    fn sqrt_ratio(_num: &Self, _div: &Self) -> (Choice, Self) {
422        unimplemented!()
423    }
424
425    fn invert(&self) -> CtOption<Self> {
426        self.invert()
427    }
428}
429
430pub const FROBENIUS_COEFF_FQ6_C1: [Fq2; 6] = [
431    // Fq2(u + 9)**(((q^0) - 1) / 3)
432    Fq2 {
433        c0: Fq([
434            0xd35d438dc58f0d9d,
435            0x0a78eb28f5c70b3d,
436            0x666ea36f7879462c,
437            0x0e0a77c19a07df2f,
438        ]),
439        c1: Fq([0x0, 0x0, 0x0, 0x0]),
440    },
441    // Fq2(u + 9)**(((q^1) - 1) / 3)
442    // taken from go-ethereum and also re-calculated manually
443    Fq2 {
444        c0: Fq([
445            0xb5773b104563ab30,
446            0x347f91c8a9aa6454,
447            0x7a007127242e0991,
448            0x1956bcd8118214ec,
449        ]),
450        c1: Fq([
451            0x6e849f1ea0aa4757,
452            0xaa1c7b6d89f89141,
453            0xb6e713cdfae0ca3a,
454            0x26694fbb4e82ebc3,
455        ]),
456    },
457    // Fq2(u + 9)**(((q^2) - 1) / 3)
458    // this one and other below are recalculated manually
459    Fq2 {
460        c0: Fq([
461            0x3350c88e13e80b9c,
462            0x7dce557cdb5e56b9,
463            0x6001b4b8b615564a,
464            0x2682e617020217e0,
465        ]),
466        c1: Fq([0x0, 0x0, 0x0, 0x0]),
467    },
468    // Fq2(u + 9)**(((q^3) - 1) / 3)
469    Fq2 {
470        c0: Fq([
471            0xc9af22f716ad6bad,
472            0xb311782a4aa662b2,
473            0x19eeaf64e248c7f4,
474            0x20273e77e3439f82,
475        ]),
476        c1: Fq([
477            0xacc02860f7ce93ac,
478            0x3933d5817ba76b4c,
479            0x69e6188b446c8467,
480            0x0a46036d4417cc55,
481        ]),
482    },
483    // Fq2(u + 9)**(((q^4) - 1) / 3)
484    Fq2 {
485        c0: Fq([
486            0x71930c11d782e155,
487            0xa6bb947cffbe3323,
488            0xaa303344d4741444,
489            0x2c3b3f0d26594943,
490        ]),
491        c1: Fq([0x0, 0x0, 0x0, 0x0]),
492    },
493    // Fq2(u + 9)**(((q^5) - 1) / 3)
494    Fq2 {
495        c0: Fq([
496            0xf91aba2654e8e3b1,
497            0x4771cb2fdc92ce12,
498            0xdcb16ae0fc8bdf35,
499            0x274aa195cd9d8be4,
500        ]),
501        c1: Fq([
502            0x5cfc50ae18811f8b,
503            0x4bb28433cb43988c,
504            0x4fd35f13c3b56219,
505            0x301949bd2fc8883a,
506        ]),
507    },
508];
509
510pub const FROBENIUS_COEFF_FQ6_C2: [Fq2; 6] = [
511    // Fq2(u + 9)**(((2q^0) - 2) / 3)
512    Fq2 {
513        c0: Fq([
514            0xd35d438dc58f0d9d,
515            0x0a78eb28f5c70b3d,
516            0x666ea36f7879462c,
517            0x0e0a77c19a07df2f,
518        ]),
519        c1: Fq([0x0, 0x0, 0x0, 0x0]),
520    },
521    // Fq2(u + 9)**(((2q^1) - 2) / 3)
522    Fq2 {
523        c0: Fq([
524            0x7361d77f843abe92,
525            0xa5bb2bd3273411fb,
526            0x9c941f314b3e2399,
527            0x15df9cddbb9fd3ec,
528        ]),
529        c1: Fq([
530            0x5dddfd154bd8c949,
531            0x62cb29a5a4445b60,
532            0x37bc870a0c7dd2b9,
533            0x24830a9d3171f0fd,
534        ]),
535    },
536    // Fq2(u + 9)**(((2q^2) - 2) / 3)
537    Fq2 {
538        c0: Fq([
539            0x71930c11d782e155,
540            0xa6bb947cffbe3323,
541            0xaa303344d4741444,
542            0x2c3b3f0d26594943,
543        ]),
544        c1: Fq([0x0, 0x0, 0x0, 0x0]),
545    },
546    // Fq2(u + 9)**(((2q^3) - 2) / 3)
547    Fq2 {
548        c0: Fq([
549            0x448a93a57b6762df,
550            0xbfd62df528fdeadf,
551            0xd858f5d00e9bd47a,
552            0x06b03d4d3476ec58,
553        ]),
554        c1: Fq([
555            0x2b19daf4bcc936d1,
556            0xa1a54e7a56f4299f,
557            0xb533eee05adeaef1,
558            0x170c812b84dda0b2,
559        ]),
560    },
561    // Fq2(u + 9)**(((2q^4) - 2) / 3)
562    Fq2 {
563        c0: Fq([
564            0x3350c88e13e80b9c,
565            0x7dce557cdb5e56b9,
566            0x6001b4b8b615564a,
567            0x2682e617020217e0,
568        ]),
569        c1: Fq([0x0, 0x0, 0x0, 0x0]),
570    },
571    // Fq2(u + 9)**(((2q^5) - 2) / 3)
572    Fq2 {
573        c0: Fq([
574            0x843420f1d8dadbd6,
575            0x31f010c9183fcdb2,
576            0x436330b527a76049,
577            0x13d47447f11adfe4,
578        ]),
579        c1: Fq([
580            0xef494023a857fa74,
581            0x2a925d02d5ab101a,
582            0x83b015829ba62f10,
583            0x2539111d0c13aea3,
584        ]),
585    },
586];
587
588#[cfg(test)]
589use rand::SeedableRng;
590#[cfg(test)]
591use rand_xorshift::XorShiftRng;
592
593#[test]
594fn test_fq6_mul_nonresidue() {
595    let mut rng = XorShiftRng::from_seed([
596        0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
597        0xe5,
598    ]);
599
600    let nqr = Fq6 {
601        c0: Fq2::zero(),
602        c1: Fq2::one(),
603        c2: Fq2::zero(),
604    };
605
606    for _ in 0..1000 {
607        let mut a = Fq6::random(&mut rng);
608        let mut b = a;
609        a.mul_by_nonresidue();
610        b.mul_assign(&nqr);
611
612        assert_eq!(a, b);
613    }
614}
615
616#[test]
617fn test_fq6_mul_by_1() {
618    let mut rng = XorShiftRng::from_seed([
619        0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
620        0xe5,
621    ]);
622
623    for _ in 0..1000 {
624        let c1 = Fq2::random(&mut rng);
625        let mut a = Fq6::random(&mut rng);
626        let mut b = a;
627
628        a.mul_by_1(&c1);
629        b.mul_assign(&Fq6 {
630            c0: Fq2::zero(),
631            c1,
632            c2: Fq2::zero(),
633        });
634
635        assert_eq!(a, b);
636    }
637}
638
639#[test]
640fn test_fq6_mul_by_01() {
641    let mut rng = XorShiftRng::from_seed([
642        0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
643        0xe5,
644    ]);
645
646    for _ in 0..1000 {
647        let c0 = Fq2::random(&mut rng);
648        let c1 = Fq2::random(&mut rng);
649        let mut a = Fq6::random(&mut rng);
650        let mut b = a;
651
652        a.mul_by_01(&c0, &c1);
653        b.mul_assign(&Fq6 {
654            c0,
655            c1,
656            c2: Fq2::zero(),
657        });
658
659        assert_eq!(a, b);
660    }
661}
662
663#[test]
664fn test_squaring() {
665    let mut rng = XorShiftRng::from_seed([
666        0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
667        0xe5,
668    ]);
669
670    for _ in 0..1000 {
671        let mut a = Fq6::random(&mut rng);
672        let mut b = a;
673        b.mul_assign(&a);
674        a.square_assign();
675        assert_eq!(a, b);
676    }
677}
678
679#[test]
680fn test_frobenius() {
681    let mut rng = XorShiftRng::from_seed([
682        0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
683        0xe5,
684    ]);
685
686    for _ in 0..100 {
687        for i in 0..14 {
688            let mut a = Fq6::random(&mut rng);
689            let mut b = a;
690
691            for _ in 0..i {
692                a = a.pow_vartime([
693                    0x3c208c16d87cfd47,
694                    0x97816a916871ca8d,
695                    0xb85045b68181585d,
696                    0x30644e72e131a029,
697                ]);
698            }
699            b.frobenius_map(i);
700
701            assert_eq!(a, b);
702        }
703    }
704}
705
706#[test]
707fn test_field() {
708    crate::tests::field::random_field_tests::<Fq6>("fq6".to_string());
709}