halo2curves_axiom/pluto_eris/fields/
fp12.rs

1use super::fp::Fp;
2use super::fp2::Fp2;
3use super::fp6::Fp6;
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#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]
13pub struct Fp12 {
14    c0: Fp6,
15    c1: Fp6,
16}
17
18impl ConditionallySelectable for Fp12 {
19    fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
20        Fp12 {
21            c0: Fp6::conditional_select(&a.c0, &b.c0, choice),
22            c1: Fp6::conditional_select(&a.c1, &b.c1, choice),
23        }
24    }
25}
26
27impl ConstantTimeEq for Fp12 {
28    fn ct_eq(&self, other: &Self) -> Choice {
29        self.c0.ct_eq(&other.c0) & self.c1.ct_eq(&other.c1)
30    }
31}
32
33impl Neg for Fp12 {
34    type Output = Fp12;
35
36    #[inline]
37    fn neg(self) -> Fp12 {
38        -&self
39    }
40}
41
42impl<'a> Neg for &'a Fp12 {
43    type Output = Fp12;
44
45    #[inline]
46    fn neg(self) -> Fp12 {
47        self.neg()
48    }
49}
50
51impl<'a, 'b> Sub<&'b Fp12> for &'a Fp12 {
52    type Output = Fp12;
53
54    #[inline]
55    fn sub(self, rhs: &'b Fp12) -> Fp12 {
56        self.sub(rhs)
57    }
58}
59
60impl<'a, 'b> Add<&'b Fp12> for &'a Fp12 {
61    type Output = Fp12;
62
63    #[inline]
64    fn add(self, rhs: &'b Fp12) -> Fp12 {
65        self.add(rhs)
66    }
67}
68
69impl<'a, 'b> Mul<&'b Fp12> for &'a Fp12 {
70    type Output = Fp12;
71
72    #[inline]
73    fn mul(self, rhs: &'b Fp12) -> Fp12 {
74        self.mul(rhs)
75    }
76}
77
78use crate::{
79    impl_add_binop_specify_output, impl_binops_additive, impl_binops_additive_specify_output,
80    impl_binops_multiplicative, impl_binops_multiplicative_mixed, impl_sub_binop_specify_output,
81    impl_sum_prod,
82};
83impl_binops_additive!(Fp12, Fp12);
84impl_binops_multiplicative!(Fp12, Fp12);
85impl_sum_prod!(Fp12);
86
87impl Fp12 {
88    #[inline]
89    pub const fn zero() -> Self {
90        Fp12 {
91            c0: Fp6::ZERO,
92            c1: Fp6::ZERO,
93        }
94    }
95
96    #[inline]
97    pub const fn one() -> Self {
98        Fp12 {
99            c0: Fp6::ONE,
100            c1: Fp6::ZERO,
101        }
102    }
103
104    pub fn mul_assign(&mut self, other: &Self) {
105        let t0 = self.c0 * other.c0;
106        let mut t1 = self.c1 * other.c1;
107        let t2 = other.c0 + other.c1;
108
109        self.c1 += &self.c0;
110        self.c1 *= &t2;
111        self.c1 -= &t0;
112        self.c1 -= &t1;
113
114        t1.mul_by_nonresidue();
115        self.c0 = t0 + t1;
116    }
117
118    pub fn square_assign(&mut self) {
119        let mut ab = self.c0 * self.c1;
120
121        let c0c1 = self.c0 + self.c1;
122
123        let mut c0 = self.c1;
124        c0.mul_by_nonresidue();
125        c0 += &self.c0;
126        c0 *= &c0c1;
127        c0 -= &ab;
128        self.c1 = ab;
129        self.c1 += &ab;
130        ab.mul_by_nonresidue();
131        c0 -= &ab;
132        self.c0 = c0;
133    }
134
135    pub fn double(&self) -> Self {
136        Self {
137            c0: self.c0.double(),
138            c1: self.c1.double(),
139        }
140    }
141
142    pub fn double_assign(&mut self) {
143        self.c0 = self.c0.double();
144        self.c1 = self.c1.double();
145    }
146
147    pub fn add(&self, other: &Self) -> Self {
148        Self {
149            c0: self.c0 + other.c0,
150            c1: self.c1 + other.c1,
151        }
152    }
153
154    pub fn sub(&self, other: &Self) -> Self {
155        Self {
156            c0: self.c0 - other.c0,
157            c1: self.c1 - other.c1,
158        }
159    }
160
161    pub fn mul(&self, other: &Self) -> Self {
162        let mut t = *other;
163        t.mul_assign(self);
164        t
165    }
166
167    pub fn square(&self) -> Self {
168        let mut t = *self;
169        t.square_assign();
170        t
171    }
172
173    #[inline(always)]
174    pub fn neg(&self) -> Self {
175        Self {
176            c0: -self.c0,
177            c1: -self.c1,
178        }
179    }
180
181    #[inline(always)]
182    pub fn conjugate(&mut self) {
183        self.c1 = -self.c1;
184    }
185
186    pub fn frobenius_map(&mut self, power: usize) {
187        self.c0.frobenius_map(power);
188        self.c1.frobenius_map(power);
189
190        self.c1.c0.mul_assign(&FROBENIUS_COEFF_FP12_C1[power % 12]);
191        self.c1.c1.mul_assign(&FROBENIUS_COEFF_FP12_C1[power % 12]);
192        self.c1.c2.mul_assign(&FROBENIUS_COEFF_FP12_C1[power % 12]);
193    }
194
195    pub fn mul_by_014(&mut self, c0: &Fp2, c1: &Fp2, c4: &Fp2) {
196        let mut aa = self.c0;
197        aa.mul_by_01(c0, c1);
198        let mut bb = self.c1;
199        bb.mul_by_1(c4);
200        let o = c1 + c4;
201        self.c1 += &self.c0;
202        self.c1.mul_by_01(c0, &o);
203        self.c1 -= &aa;
204        self.c1 -= &bb;
205        self.c0 = bb;
206        self.c0.mul_by_nonresidue();
207        self.c0 += &aa;
208    }
209
210    pub fn mul_by_034(&mut self, c0: &Fp2, c3: &Fp2, c4: &Fp2) {
211        let t0 = Fp6 {
212            c0: self.c0.c0 * c0,
213            c1: self.c0.c1 * c0,
214            c2: self.c0.c2 * c0,
215        };
216        let mut t1 = self.c1;
217        t1.mul_by_01(c3, c4);
218        let o = c0 + c3;
219        let mut t2 = self.c0 + self.c1;
220        t2.mul_by_01(&o, c4);
221        t2 -= t0;
222        self.c1 = t2 - t1;
223        t1.mul_by_nonresidue();
224        self.c0 = t0 + t1;
225    }
226
227    pub fn invert(&self) -> CtOption<Self> {
228        let mut c0s = self.c0;
229        c0s.square_assign();
230        let mut c1s = self.c1;
231        c1s.square_assign();
232        c1s.mul_by_nonresidue();
233        c0s -= &c1s;
234
235        c0s.invert().map(|t| {
236            let mut tmp = Fp12 { c0: t, c1: t };
237            tmp.c0.mul_assign(&self.c0);
238            tmp.c1.mul_assign(&self.c1);
239            tmp.c1 = tmp.c1.neg();
240
241            tmp
242        })
243    }
244
245    pub fn cyclotomic_square(&mut self) {
246        fn fp4_square(c0: &mut Fp2, c1: &mut Fp2, a0: &Fp2, a1: &Fp2) {
247            let t0 = a0.square();
248            let t1 = a1.square();
249            let mut t2 = t1;
250            t2.mul_by_nonresidue();
251            *c0 = t2 + t0;
252            t2 = a0 + a1;
253            t2.square_assign();
254            t2 -= t0;
255            *c1 = t2 - t1;
256        }
257
258        let mut t3 = Fp2::zero();
259        let mut t4 = Fp2::zero();
260        let mut t5 = Fp2::zero();
261        let mut t6 = Fp2::zero();
262
263        fp4_square(&mut t3, &mut t4, &self.c0.c0, &self.c1.c1);
264        let mut t2 = t3 - self.c0.c0;
265        t2.double_assign();
266        self.c0.c0 = t2 + t3;
267
268        t2 = t4 + self.c1.c1;
269        t2.double_assign();
270        self.c1.c1 = t2 + t4;
271
272        fp4_square(&mut t3, &mut t4, &self.c1.c0, &self.c0.c2);
273        fp4_square(&mut t5, &mut t6, &self.c0.c1, &self.c1.c2);
274
275        t2 = t3 - self.c0.c1;
276        t2.double_assign();
277        self.c0.c1 = t2 + t3;
278        t2 = t4 + self.c1.c2;
279        t2.double_assign();
280        self.c1.c2 = t2 + t4;
281        t3 = t6;
282        t3.mul_by_nonresidue();
283        t2 = t3 + self.c1.c0;
284        t2.double_assign();
285        self.c1.c0 = t2 + t3;
286        t2 = t5 - self.c0.c2;
287        t2.double_assign();
288        self.c0.c2 = t2 + t5;
289    }
290}
291
292impl Field for Fp12 {
293    const ZERO: Self = Self::zero();
294    const ONE: Self = Self::one();
295
296    fn random(mut rng: impl RngCore) -> Self {
297        Fp12 {
298            c0: Fp6::random(&mut rng),
299            c1: Fp6::random(&mut rng),
300        }
301    }
302
303    fn is_zero(&self) -> Choice {
304        self.c0.is_zero() & self.c1.is_zero()
305    }
306
307    fn square(&self) -> Self {
308        self.square()
309    }
310
311    fn double(&self) -> Self {
312        self.double()
313    }
314
315    fn sqrt(&self) -> CtOption<Self> {
316        // The square root method is typically only required for finding y-coordinate
317        // given the x-coordinate of an EC point. Fields over which we have not
318        // defined a curve do not need this method.
319        unimplemented!()
320    }
321
322    fn sqrt_ratio(_num: &Self, _div: &Self) -> (Choice, Self) {
323        // The square root method is typically only required for finding y-coordinate
324        // given the x-coordinate of an EC point. Fields over which we have not
325        // defined a curve do not need this method.
326        unimplemented!()
327    }
328
329    fn invert(&self) -> CtOption<Self> {
330        self.invert()
331    }
332}
333
334/// Fp2(v)^((p^i-1)/6) for i=0,...,11
335pub const FROBENIUS_COEFF_FP12_C1: [Fp2; 12] = [
336    // Fp2(v)**(((p^0) - 1) / 6)
337    Fp2::ONE,
338    // Fp2(v)**(((p^1) - 1) / 6)
339    Fp2 {
340        // 0x3c3ad3da8b99cb1df0709dc343113ccd9892dedd51f30695d89c647b90de8f41df055384b9e6cfd4e70648622c750f32ee965dfef2303d3
341        c0: Fp::from_raw([
342            0x2ee965dfef2303d3,
343            0x4e70648622c750f3,
344            0x1df055384b9e6cfd,
345            0x5d89c647b90de8f4,
346            0xd9892dedd51f3069,
347            0xdf0709dc343113cc,
348            0x03c3ad3da8b99cb1,
349        ]),
350        // 0x149fd9ed2c7affe7aaa3b912182da22dccb29838628f04b6f333d052540294889f03876b2ddb143559f9373f4cf44e6afa0be24ad758a5ff
351        c1: Fp::from_raw([
352            0xfa0be24ad758a5ff,
353            0x59f9373f4cf44e6a,
354            0x9f03876b2ddb1435,
355            0xf333d05254029488,
356            0xccb29838628f04b6,
357            0xaaa3b912182da22d,
358            0x149fd9ed2c7affe7,
359        ]),
360    },
361    // Fp2(v)**(((p^2) - 1) / 6)
362    Fp2 {
363        // 0x480000000000360001c950000d7ee0e4a803c956d01c903d720dc8ad8b38dffaf50c100004c37fffffff
364        c0: Fp::from_raw([
365            0x100004c37fffffff,
366            0xc8ad8b38dffaf50c,
367            0xc956d01c903d720d,
368            0x50000d7ee0e4a803,
369            0x00000000360001c9,
370            0x0000000000004800,
371            0x0000000000000000,
372        ]),
373        c1: Fp::ZERO,
374    },
375    // Fp2(v)**(((p^3) - 1) / 6)
376    Fp2 {
377        // 0x1baee9e044d94d205764b80089c40010af5ca1e56a2a81e6a5d8739325984fc889d390efef216fe4f4af912a897f60a128a3be71be4995ca
378        c0: Fp::from_raw([
379            0x28a3be71be4995ca,
380            0xf4af912a897f60a1,
381            0x89d390efef216fe4,
382            0xa5d8739325984fc8,
383            0xaf5ca1e56a2a81e6,
384            0x5764b80089c40010,
385            0x1baee9e044d94d20,
386        ]),
387        // 0x20d4c11700e832829b26f1795339413be65e47a7716bc8bc07cd6b44b03ef1130b3c35a77291b29d6f45d28e4ef1ecb9678f4479a1151232
388        c1: Fp::from_raw([
389            0x678f4479a1151232,
390            0x6f45d28e4ef1ecb9,
391            0x0b3c35a77291b29d,
392            0x07cd6b44b03ef113,
393            0xe65e47a7716bc8bc,
394            0x9b26f1795339413b,
395            0x20d4c11700e83282,
396        ]),
397    },
398    // Fp2(v)**(((p^4) - 1) / 6)
399    Fp2 {
400        // 0x480000000000360001c950000d7ee0e4a803c956d01c903d720dc8ad8b38dffaf50c100004c37ffffffe
401        c0: Fp::from_raw([
402            0x100004c37ffffffe,
403            0xc8ad8b38dffaf50c,
404            0xc956d01c903d720d,
405            0x50000d7ee0e4a803,
406            0x00000000360001c9,
407            0x0000000000004800,
408            0x0000000000000000,
409        ]),
410        c1: Fp::ZERO,
411    },
412    // Fp2(v)**(((p^5) - 1) / 6)
413    Fp2 {
414        // 0x17eb3ca29c1fb06e785dae245592ec43d5d373f7950b517d484ead4b6c8a66d46be33bb7a38302e7a63f2ca466b80fadf9ba5891cf2691f7
415        c0: Fp::from_raw([
416            0xf9ba5891cf2691f7,
417            0xa63f2ca466b80fad,
418            0x6be33bb7a38302e7,
419            0x484ead4b6c8a66d4,
420            0xd5d373f7950b517d,
421            0x785dae245592ec43,
422            0x17eb3ca29c1fb06e,
423        ]),
424        // 0xc34e729d46d329af08338673b0b9f0e19abaf6f0edcc40514999af25c3c5c8a6c38ae3c44b69e68154c9b4f01fd9e4e6d83622ec9bc6c33
425        c1: Fp::from_raw([
426            0x6d83622ec9bc6c33,
427            0x154c9b4f01fd9e4e,
428            0x6c38ae3c44b69e68,
429            0x14999af25c3c5c8a,
430            0x19abaf6f0edcc405,
431            0xf08338673b0b9f0e,
432            0x0c34e729d46d329a,
433        ]),
434    },
435    // Fp2(v)**(((p^6) - 1) / 6)
436    Fp2 {
437        // 0x24000000000024000130e0000d7f70e4a803ca76f439266f443f9a5cda8a6c7be4a7a5fe8fadffd6a2a7e8c30006b9459ffffcd300000000
438        c0: Fp::from_raw([
439            0x9ffffcd300000000,
440            0xa2a7e8c30006b945,
441            0xe4a7a5fe8fadffd6,
442            0x443f9a5cda8a6c7b,
443            0xa803ca76f439266f,
444            0x0130e0000d7f70e4,
445            0x2400000000002400,
446        ]),
447        c1: Fp::ZERO,
448    },
449    // Fp2(v)**(((p^7) - 1) / 6)
450    Fp2 {
451        // 0x203c52c25746874e2229d623d94e5d17ce7a9c891f19f605e6b5d415217c8387c6b750c6440f92d95437843cdd3f6852711696f310dcfc2e
452        c0: Fp::from_raw([
453            0x711696f310dcfc2e,
454            0x5437843cdd3f6852,
455            0xc6b750c6440f92d9,
456            0xe6b5d415217c8387,
457            0xce7a9c891f19f605,
458            0x2229d623d94e5d17,
459            0x203c52c25746874e,
460        ]),
461        // 0xf602612d3852418568d26edf551ceb6db51323e91aa21b8510bca0a8687d7f345a41e9361d2eba148aeb183b3126adaa5f41a8828a75a02
462        c1: Fp::from_raw([
463            0xa5f41a8828a75a02,
464            0x48aeb183b3126ada,
465            0x45a41e9361d2eba1,
466            0x510bca0a8687d7f3,
467            0xdb51323e91aa21b8,
468            0x568d26edf551ceb6,
469            0x0f602612d3852418,
470        ]),
471    },
472    // Fp2(v)**(((p^8) - 1) / 6)
473    Fp2 {
474        // 0x24000000000024000130e0000d7f28e4a803ca76be3924a5f43f8cddf9a5c4781b50d5e1ff708dc8d9fa5d8a200bc4398ffff80f80000002
475        c0: Fp::from_raw([
476            0x8ffff80f80000002,
477            0xd9fa5d8a200bc439,
478            0x1b50d5e1ff708dc8,
479            0xf43f8cddf9a5c478,
480            0xa803ca76be3924a5,
481            0x0130e0000d7f28e4,
482            0x2400000000002400,
483        ]),
484        c1: Fp::ZERO,
485    },
486    // Fp2(v)**(((p^9) - 1) / 6)
487    Fp2 {
488        // 0x851161fbb26d6dfa9cc27ff83bb70d3f8a728918a0ea4889e6726c9b4f21cb35ad4150ea08c8ff1adf85798768758a4775c3e6141b66a37
489        c0: Fp::from_raw([
490            0x775c3e6141b66a37,
491            0xadf85798768758a4,
492            0x5ad4150ea08c8ff1,
493            0x9e6726c9b4f21cb3,
494            0xf8a728918a0ea488,
495            0xa9cc27ff83bb70d3,
496            0x0851161fbb26d6df,
497        ]),
498        // 0x32b3ee8ff17f17d6609ee86ba462fa8c1a582cf82cd5db33c722f182a4b7b68d96b70571d1c4d3933621634b114cc8c3870b8595eeaedcf
499        c1: Fp::from_raw([
500            0x3870b8595eeaedcf,
501            0x33621634b114cc8c,
502            0xd96b70571d1c4d39,
503            0x3c722f182a4b7b68,
504            0xc1a582cf82cd5db3,
505            0x6609ee86ba462fa8,
506            0x032b3ee8ff17f17d,
507        ]),
508    },
509    // Fp2(v)**(((p^10) - 1) / 6)
510    Fp2 {
511        // 0x24000000000024000130e0000d7f28e4a803ca76be3924a5f43f8cddf9a5c4781b50d5e1ff708dc8d9fa5d8a200bc4398ffff80f80000003
512        c0: Fp::from_raw([
513            0x8ffff80f80000003,
514            0xd9fa5d8a200bc439,
515            0x1b50d5e1ff708dc8,
516            0xf43f8cddf9a5c478,
517            0xa803ca76be3924a5,
518            0x0130e0000d7f28e4,
519            0x2400000000002400,
520        ]),
521        c1: Fp::ZERO,
522    },
523    // Fp2(v)**(((p^11) - 1) / 6)
524    Fp2 {
525        // 0xc14c35d63e0739188d331dbb7ec84a0d230567f5f2dd4f1fbf0ed116e0005a778c46a46ec2afceefc68bc1e994ea997a645a44130d96e0a
526        c0: Fp::from_raw([
527            0xa645a44130d96e0a,
528            0xfc68bc1e994ea997,
529            0x78c46a46ec2afcee,
530            0xfbf0ed116e0005a7,
531            0xd230567f5f2dd4f1,
532            0x88d331dbb7ec84a0,
533            0x0c14c35d63e07391,
534        ]),
535        // 0x17cb18d62b92f16510ada798d273d1d68e581b07e55c626a2fa5ff6a7e4e0ff1786ef7c24af7616e8d5b4d73fe091af7327c9aa4364393ce
536        c1: Fp::from_raw([
537            0x327c9aa4364393ce,
538            0x8d5b4d73fe091af7,
539            0x786ef7c24af7616e,
540            0x2fa5ff6a7e4e0ff1,
541            0x8e581b07e55c626a,
542            0x10ada798d273d1d6,
543            0x17cb18d62b92f165,
544        ]),
545    },
546];
547
548#[cfg(test)]
549use rand::SeedableRng;
550#[cfg(test)]
551use rand_xorshift::XorShiftRng;
552
553#[test]
554fn test_fp12_mul_by_014() {
555    let mut rng = XorShiftRng::from_seed([
556        0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
557        0xe5,
558    ]);
559
560    for _ in 0..1000 {
561        let c0 = Fp2::random(&mut rng);
562        let c1 = Fp2::random(&mut rng);
563        let c5 = Fp2::random(&mut rng);
564        let mut a = Fp12::random(&mut rng);
565        let mut b = a;
566
567        a.mul_by_014(&c0, &c1, &c5);
568        b.mul_assign(&Fp12 {
569            c0: Fp6 {
570                c0,
571                c1,
572                c2: Fp2::zero(),
573            },
574            c1: Fp6 {
575                c0: Fp2::zero(),
576                c1: c5,
577                c2: Fp2::zero(),
578            },
579        });
580
581        assert_eq!(a, b);
582    }
583}
584
585#[test]
586fn test_fp12_mul_by_034() {
587    let mut rng = XorShiftRng::from_seed([
588        0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
589        0xe5,
590    ]);
591
592    for _ in 0..1000 {
593        let c0 = Fp2::random(&mut rng);
594        let c3 = Fp2::random(&mut rng);
595        let c4 = Fp2::random(&mut rng);
596        let mut a = Fp12::random(&mut rng);
597        let mut b = a;
598
599        a.mul_by_034(&c0, &c3, &c4);
600        b.mul_assign(&Fp12 {
601            c0: Fp6 {
602                c0,
603                c1: Fp2::zero(),
604                c2: Fp2::zero(),
605            },
606            c1: Fp6 {
607                c0: c3,
608                c1: c4,
609                c2: Fp2::zero(),
610            },
611        });
612
613        assert_eq!(a, b);
614    }
615}
616
617#[test]
618fn test_squaring() {
619    let mut rng = XorShiftRng::from_seed([
620        0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
621        0xe5,
622    ]);
623
624    for _ in 0..1000 {
625        let mut a = Fp12::random(&mut rng);
626        let mut b = a;
627        b.mul_assign(&a);
628        a.square_assign();
629        assert_eq!(a, b);
630    }
631}
632
633#[test]
634fn test_frobenius() {
635    let mut rng = XorShiftRng::from_seed([
636        0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
637        0xe5,
638    ]);
639
640    for _ in 0..50 {
641        for i in 0..13 {
642            let mut a = Fp12::random(&mut rng);
643            let mut b = a;
644
645            for _ in 0..i {
646                a = a.pow_vartime([
647                    0x9ffffcd300000001,
648                    0xa2a7e8c30006b945,
649                    0xe4a7a5fe8fadffd6,
650                    0x443f9a5cda8a6c7b,
651                    0xa803ca76f439266f,
652                    0x0130e0000d7f70e4,
653                    0x2400000000002400,
654                ]);
655            }
656            b.frobenius_map(i);
657
658            assert_eq!(a, b);
659        }
660    }
661}
662
663#[test]
664fn test_field() {
665    crate::tests::field::random_field_tests::<Fp12>("fp12".to_string());
666}