bls12_381/
fp12.rs

1use crate::fp::*;
2use crate::fp2::*;
3use crate::fp6::*;
4
5use core::fmt;
6use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign};
7use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
8
9#[cfg(feature = "pairings")]
10use rand_core::RngCore;
11
12/// This represents an element $c_0 + c_1 w$ of $\mathbb{F}_{p^12} = \mathbb{F}_{p^6} / w^2 - v$.
13pub struct Fp12 {
14    pub c0: Fp6,
15    pub c1: Fp6,
16}
17
18impl From<Fp> for Fp12 {
19    fn from(f: Fp) -> Fp12 {
20        Fp12 {
21            c0: Fp6::from(f),
22            c1: Fp6::zero(),
23        }
24    }
25}
26
27impl From<Fp2> for Fp12 {
28    fn from(f: Fp2) -> Fp12 {
29        Fp12 {
30            c0: Fp6::from(f),
31            c1: Fp6::zero(),
32        }
33    }
34}
35
36impl From<Fp6> for Fp12 {
37    fn from(f: Fp6) -> Fp12 {
38        Fp12 {
39            c0: f,
40            c1: Fp6::zero(),
41        }
42    }
43}
44
45impl PartialEq for Fp12 {
46    fn eq(&self, other: &Fp12) -> bool {
47        self.ct_eq(other).into()
48    }
49}
50
51impl Copy for Fp12 {}
52impl Clone for Fp12 {
53    #[inline]
54    fn clone(&self) -> Self {
55        *self
56    }
57}
58
59impl Default for Fp12 {
60    fn default() -> Self {
61        Fp12::zero()
62    }
63}
64
65#[cfg(feature = "zeroize")]
66impl zeroize::DefaultIsZeroes for Fp12 {}
67
68impl fmt::Debug for Fp12 {
69    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
70        write!(f, "{:?} + ({:?})*w", self.c0, self.c1)
71    }
72}
73
74impl ConditionallySelectable for Fp12 {
75    #[inline(always)]
76    fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
77        Fp12 {
78            c0: Fp6::conditional_select(&a.c0, &b.c0, choice),
79            c1: Fp6::conditional_select(&a.c1, &b.c1, choice),
80        }
81    }
82}
83
84impl ConstantTimeEq for Fp12 {
85    #[inline(always)]
86    fn ct_eq(&self, other: &Self) -> Choice {
87        self.c0.ct_eq(&other.c0) & self.c1.ct_eq(&other.c1)
88    }
89}
90
91impl Fp12 {
92    #[inline]
93    pub fn zero() -> Self {
94        Fp12 {
95            c0: Fp6::zero(),
96            c1: Fp6::zero(),
97        }
98    }
99
100    #[inline]
101    pub fn one() -> Self {
102        Fp12 {
103            c0: Fp6::one(),
104            c1: Fp6::zero(),
105        }
106    }
107
108    #[cfg(feature = "pairings")]
109    pub(crate) fn random(mut rng: impl RngCore) -> Self {
110        Fp12 {
111            c0: Fp6::random(&mut rng),
112            c1: Fp6::random(&mut rng),
113        }
114    }
115
116    pub fn mul_by_014(&self, c0: &Fp2, c1: &Fp2, c4: &Fp2) -> Fp12 {
117        let aa = self.c0.mul_by_01(c0, c1);
118        let bb = self.c1.mul_by_1(c4);
119        let o = c1 + c4;
120        let c1 = self.c1 + self.c0;
121        let c1 = c1.mul_by_01(c0, &o);
122        let c1 = c1 - aa - bb;
123        let c0 = bb;
124        let c0 = c0.mul_by_nonresidue();
125        let c0 = c0 + aa;
126
127        Fp12 { c0, c1 }
128    }
129
130    #[inline(always)]
131    pub fn is_zero(&self) -> Choice {
132        self.c0.is_zero() & self.c1.is_zero()
133    }
134
135    #[inline(always)]
136    pub fn conjugate(&self) -> Self {
137        Fp12 {
138            c0: self.c0,
139            c1: -self.c1,
140        }
141    }
142
143    /// Raises this element to p.
144    #[inline(always)]
145    pub fn frobenius_map(&self) -> Self {
146        let c0 = self.c0.frobenius_map();
147        let c1 = self.c1.frobenius_map();
148
149        // c1 = c1 * (u + 1)^((p - 1) / 6)
150        let c1 = c1
151            * Fp6::from(Fp2 {
152                c0: Fp::from_raw_unchecked([
153                    0x0708_9552_b319_d465,
154                    0xc669_5f92_b50a_8313,
155                    0x97e8_3ccc_d117_228f,
156                    0xa35b_aeca_b2dc_29ee,
157                    0x1ce3_93ea_5daa_ce4d,
158                    0x08f2_220f_b0fb_66eb,
159                ]),
160                c1: Fp::from_raw_unchecked([
161                    0xb2f6_6aad_4ce5_d646,
162                    0x5842_a06b_fc49_7cec,
163                    0xcf48_95d4_2599_d394,
164                    0xc11b_9cba_40a8_e8d0,
165                    0x2e38_13cb_e5a0_de89,
166                    0x110e_efda_8884_7faf,
167                ]),
168            });
169
170        Fp12 { c0, c1 }
171    }
172
173    #[inline]
174    pub fn square(&self) -> Self {
175        let ab = self.c0 * self.c1;
176        let c0c1 = self.c0 + self.c1;
177        let c0 = self.c1.mul_by_nonresidue();
178        let c0 = c0 + self.c0;
179        let c0 = c0 * c0c1;
180        let c0 = c0 - ab;
181        let c1 = ab + ab;
182        let c0 = c0 - ab.mul_by_nonresidue();
183
184        Fp12 { c0, c1 }
185    }
186
187    pub fn invert(&self) -> CtOption<Self> {
188        (self.c0.square() - self.c1.square().mul_by_nonresidue())
189            .invert()
190            .map(|t| Fp12 {
191                c0: self.c0 * t,
192                c1: self.c1 * -t,
193            })
194    }
195}
196
197impl<'a, 'b> Mul<&'b Fp12> for &'a Fp12 {
198    type Output = Fp12;
199
200    #[inline]
201    fn mul(self, other: &'b Fp12) -> Self::Output {
202        let aa = self.c0 * other.c0;
203        let bb = self.c1 * other.c1;
204        let o = other.c0 + other.c1;
205        let c1 = self.c1 + self.c0;
206        let c1 = c1 * o;
207        let c1 = c1 - aa;
208        let c1 = c1 - bb;
209        let c0 = bb.mul_by_nonresidue();
210        let c0 = c0 + aa;
211
212        Fp12 { c0, c1 }
213    }
214}
215
216impl<'a, 'b> Add<&'b Fp12> for &'a Fp12 {
217    type Output = Fp12;
218
219    #[inline]
220    fn add(self, rhs: &'b Fp12) -> Self::Output {
221        Fp12 {
222            c0: self.c0 + rhs.c0,
223            c1: self.c1 + rhs.c1,
224        }
225    }
226}
227
228impl<'a> Neg for &'a Fp12 {
229    type Output = Fp12;
230
231    #[inline]
232    fn neg(self) -> Self::Output {
233        Fp12 {
234            c0: -self.c0,
235            c1: -self.c1,
236        }
237    }
238}
239
240impl Neg for Fp12 {
241    type Output = Fp12;
242
243    #[inline]
244    fn neg(self) -> Self::Output {
245        -&self
246    }
247}
248
249impl<'a, 'b> Sub<&'b Fp12> for &'a Fp12 {
250    type Output = Fp12;
251
252    #[inline]
253    fn sub(self, rhs: &'b Fp12) -> Self::Output {
254        Fp12 {
255            c0: self.c0 - rhs.c0,
256            c1: self.c1 - rhs.c1,
257        }
258    }
259}
260
261impl_binops_additive!(Fp12, Fp12);
262impl_binops_multiplicative!(Fp12, Fp12);
263
264#[test]
265fn test_arithmetic() {
266    use crate::fp::*;
267    use crate::fp2::*;
268
269    let a = Fp12 {
270        c0: Fp6 {
271            c0: Fp2 {
272                c0: Fp::from_raw_unchecked([
273                    0x47f9_cb98_b1b8_2d58,
274                    0x5fe9_11eb_a3aa_1d9d,
275                    0x96bf_1b5f_4dd8_1db3,
276                    0x8100_d27c_c925_9f5b,
277                    0xafa2_0b96_7464_0eab,
278                    0x09bb_cea7_d8d9_497d,
279                ]),
280                c1: Fp::from_raw_unchecked([
281                    0x0303_cb98_b166_2daa,
282                    0xd931_10aa_0a62_1d5a,
283                    0xbfa9_820c_5be4_a468,
284                    0x0ba3_643e_cb05_a348,
285                    0xdc35_34bb_1f1c_25a6,
286                    0x06c3_05bb_19c0_e1c1,
287                ]),
288            },
289            c1: Fp2 {
290                c0: Fp::from_raw_unchecked([
291                    0x46f9_cb98_b162_d858,
292                    0x0be9_109c_f7aa_1d57,
293                    0xc791_bc55_fece_41d2,
294                    0xf84c_5770_4e38_5ec2,
295                    0xcb49_c1d9_c010_e60f,
296                    0x0acd_b8e1_58bf_e3c8,
297                ]),
298                c1: Fp::from_raw_unchecked([
299                    0x8aef_cb98_b15f_8306,
300                    0x3ea1_108f_e4f2_1d54,
301                    0xcf79_f69f_a1b7_df3b,
302                    0xe4f5_4aa1_d16b_1a3c,
303                    0xba5e_4ef8_6105_a679,
304                    0x0ed8_6c07_97be_e5cf,
305                ]),
306            },
307            c2: Fp2 {
308                c0: Fp::from_raw_unchecked([
309                    0xcee5_cb98_b15c_2db4,
310                    0x7159_1082_d23a_1d51,
311                    0xd762_30e9_44a1_7ca4,
312                    0xd19e_3dd3_549d_d5b6,
313                    0xa972_dc17_01fa_66e3,
314                    0x12e3_1f2d_d6bd_e7d6,
315                ]),
316                c1: Fp::from_raw_unchecked([
317                    0xad2a_cb98_b173_2d9d,
318                    0x2cfd_10dd_0696_1d64,
319                    0x0739_6b86_c6ef_24e8,
320                    0xbd76_e2fd_b1bf_c820,
321                    0x6afe_a7f6_de94_d0d5,
322                    0x1099_4b0c_5744_c040,
323                ]),
324            },
325        },
326        c1: Fp6 {
327            c0: Fp2 {
328                c0: Fp::from_raw_unchecked([
329                    0x47f9_cb98_b1b8_2d58,
330                    0x5fe9_11eb_a3aa_1d9d,
331                    0x96bf_1b5f_4dd8_1db3,
332                    0x8100_d27c_c925_9f5b,
333                    0xafa2_0b96_7464_0eab,
334                    0x09bb_cea7_d8d9_497d,
335                ]),
336                c1: Fp::from_raw_unchecked([
337                    0x0303_cb98_b166_2daa,
338                    0xd931_10aa_0a62_1d5a,
339                    0xbfa9_820c_5be4_a468,
340                    0x0ba3_643e_cb05_a348,
341                    0xdc35_34bb_1f1c_25a6,
342                    0x06c3_05bb_19c0_e1c1,
343                ]),
344            },
345            c1: Fp2 {
346                c0: Fp::from_raw_unchecked([
347                    0x46f9_cb98_b162_d858,
348                    0x0be9_109c_f7aa_1d57,
349                    0xc791_bc55_fece_41d2,
350                    0xf84c_5770_4e38_5ec2,
351                    0xcb49_c1d9_c010_e60f,
352                    0x0acd_b8e1_58bf_e3c8,
353                ]),
354                c1: Fp::from_raw_unchecked([
355                    0x8aef_cb98_b15f_8306,
356                    0x3ea1_108f_e4f2_1d54,
357                    0xcf79_f69f_a1b7_df3b,
358                    0xe4f5_4aa1_d16b_1a3c,
359                    0xba5e_4ef8_6105_a679,
360                    0x0ed8_6c07_97be_e5cf,
361                ]),
362            },
363            c2: Fp2 {
364                c0: Fp::from_raw_unchecked([
365                    0xcee5_cb98_b15c_2db4,
366                    0x7159_1082_d23a_1d51,
367                    0xd762_30e9_44a1_7ca4,
368                    0xd19e_3dd3_549d_d5b6,
369                    0xa972_dc17_01fa_66e3,
370                    0x12e3_1f2d_d6bd_e7d6,
371                ]),
372                c1: Fp::from_raw_unchecked([
373                    0xad2a_cb98_b173_2d9d,
374                    0x2cfd_10dd_0696_1d64,
375                    0x0739_6b86_c6ef_24e8,
376                    0xbd76_e2fd_b1bf_c820,
377                    0x6afe_a7f6_de94_d0d5,
378                    0x1099_4b0c_5744_c040,
379                ]),
380            },
381        },
382    };
383
384    let b = Fp12 {
385        c0: Fp6 {
386            c0: Fp2 {
387                c0: Fp::from_raw_unchecked([
388                    0x47f9_cb98_b1b8_2d58,
389                    0x5fe9_11eb_a3aa_1d9d,
390                    0x96bf_1b5f_4dd8_1db3,
391                    0x8100_d272_c925_9f5b,
392                    0xafa2_0b96_7464_0eab,
393                    0x09bb_cea7_d8d9_497d,
394                ]),
395                c1: Fp::from_raw_unchecked([
396                    0x0303_cb98_b166_2daa,
397                    0xd931_10aa_0a62_1d5a,
398                    0xbfa9_820c_5be4_a468,
399                    0x0ba3_643e_cb05_a348,
400                    0xdc35_34bb_1f1c_25a6,
401                    0x06c3_05bb_19c0_e1c1,
402                ]),
403            },
404            c1: Fp2 {
405                c0: Fp::from_raw_unchecked([
406                    0x46f9_cb98_b162_d858,
407                    0x0be9_109c_f7aa_1d57,
408                    0xc791_bc55_fece_41d2,
409                    0xf84c_5770_4e38_5ec2,
410                    0xcb49_c1d9_c010_e60f,
411                    0x0acd_b8e1_58bf_e348,
412                ]),
413                c1: Fp::from_raw_unchecked([
414                    0x8aef_cb98_b15f_8306,
415                    0x3ea1_108f_e4f2_1d54,
416                    0xcf79_f69f_a1b7_df3b,
417                    0xe4f5_4aa1_d16b_1a3c,
418                    0xba5e_4ef8_6105_a679,
419                    0x0ed8_6c07_97be_e5cf,
420                ]),
421            },
422            c2: Fp2 {
423                c0: Fp::from_raw_unchecked([
424                    0xcee5_cb98_b15c_2db4,
425                    0x7159_1082_d23a_1d51,
426                    0xd762_30e9_44a1_7ca4,
427                    0xd19e_3dd3_549d_d5b6,
428                    0xa972_dc17_01fa_66e3,
429                    0x12e3_1f2d_d6bd_e7d6,
430                ]),
431                c1: Fp::from_raw_unchecked([
432                    0xad2a_cb98_b173_2d9d,
433                    0x2cfd_10dd_0696_1d64,
434                    0x0739_6b86_c6ef_24e8,
435                    0xbd76_e2fd_b1bf_c820,
436                    0x6afe_a7f6_de94_d0d5,
437                    0x1099_4b0c_5744_c040,
438                ]),
439            },
440        },
441        c1: Fp6 {
442            c0: Fp2 {
443                c0: Fp::from_raw_unchecked([
444                    0x47f9_cb98_b1b8_2d58,
445                    0x5fe9_11eb_a3aa_1d9d,
446                    0x96bf_1b5f_4dd2_1db3,
447                    0x8100_d27c_c925_9f5b,
448                    0xafa2_0b96_7464_0eab,
449                    0x09bb_cea7_d8d9_497d,
450                ]),
451                c1: Fp::from_raw_unchecked([
452                    0x0303_cb98_b166_2daa,
453                    0xd931_10aa_0a62_1d5a,
454                    0xbfa9_820c_5be4_a468,
455                    0x0ba3_643e_cb05_a348,
456                    0xdc35_34bb_1f1c_25a6,
457                    0x06c3_05bb_19c0_e1c1,
458                ]),
459            },
460            c1: Fp2 {
461                c0: Fp::from_raw_unchecked([
462                    0x46f9_cb98_b162_d858,
463                    0x0be9_109c_f7aa_1d57,
464                    0xc791_bc55_fece_41d2,
465                    0xf84c_5770_4e38_5ec2,
466                    0xcb49_c1d9_c010_e60f,
467                    0x0acd_b8e1_58bf_e3c8,
468                ]),
469                c1: Fp::from_raw_unchecked([
470                    0x8aef_cb98_b15f_8306,
471                    0x3ea1_108f_e4f2_1d54,
472                    0xcf79_f69f_a117_df3b,
473                    0xe4f5_4aa1_d16b_1a3c,
474                    0xba5e_4ef8_6105_a679,
475                    0x0ed8_6c07_97be_e5cf,
476                ]),
477            },
478            c2: Fp2 {
479                c0: Fp::from_raw_unchecked([
480                    0xcee5_cb98_b15c_2db4,
481                    0x7159_1082_d23a_1d51,
482                    0xd762_30e9_44a1_7ca4,
483                    0xd19e_3dd3_549d_d5b6,
484                    0xa972_dc17_01fa_66e3,
485                    0x12e3_1f2d_d6bd_e7d6,
486                ]),
487                c1: Fp::from_raw_unchecked([
488                    0xad2a_cb98_b173_2d9d,
489                    0x2cfd_10dd_0696_1d64,
490                    0x0739_6b86_c6ef_24e8,
491                    0xbd76_e2fd_b1bf_c820,
492                    0x6afe_a7f6_de94_d0d5,
493                    0x1099_4b0c_5744_c040,
494                ]),
495            },
496        },
497    };
498
499    let c = Fp12 {
500        c0: Fp6 {
501            c0: Fp2 {
502                c0: Fp::from_raw_unchecked([
503                    0x47f9_cb98_71b8_2d58,
504                    0x5fe9_11eb_a3aa_1d9d,
505                    0x96bf_1b5f_4dd8_1db3,
506                    0x8100_d27c_c925_9f5b,
507                    0xafa2_0b96_7464_0eab,
508                    0x09bb_cea7_d8d9_497d,
509                ]),
510                c1: Fp::from_raw_unchecked([
511                    0x0303_cb98_b166_2daa,
512                    0xd931_10aa_0a62_1d5a,
513                    0xbfa9_820c_5be4_a468,
514                    0x0ba3_643e_cb05_a348,
515                    0xdc35_34bb_1f1c_25a6,
516                    0x06c3_05bb_19c0_e1c1,
517                ]),
518            },
519            c1: Fp2 {
520                c0: Fp::from_raw_unchecked([
521                    0x46f9_cb98_b162_d858,
522                    0x0be9_109c_f7aa_1d57,
523                    0x7791_bc55_fece_41d2,
524                    0xf84c_5770_4e38_5ec2,
525                    0xcb49_c1d9_c010_e60f,
526                    0x0acd_b8e1_58bf_e3c8,
527                ]),
528                c1: Fp::from_raw_unchecked([
529                    0x8aef_cb98_b15f_8306,
530                    0x3ea1_108f_e4f2_1d54,
531                    0xcf79_f69f_a1b7_df3b,
532                    0xe4f5_4aa1_d16b_133c,
533                    0xba5e_4ef8_6105_a679,
534                    0x0ed8_6c07_97be_e5cf,
535                ]),
536            },
537            c2: Fp2 {
538                c0: Fp::from_raw_unchecked([
539                    0xcee5_cb98_b15c_2db4,
540                    0x7159_1082_d23a_1d51,
541                    0xd762_40e9_44a1_7ca4,
542                    0xd19e_3dd3_549d_d5b6,
543                    0xa972_dc17_01fa_66e3,
544                    0x12e3_1f2d_d6bd_e7d6,
545                ]),
546                c1: Fp::from_raw_unchecked([
547                    0xad2a_cb98_b173_2d9d,
548                    0x2cfd_10dd_0696_1d64,
549                    0x0739_6b86_c6ef_24e8,
550                    0xbd76_e2fd_b1bf_c820,
551                    0x6afe_a7f6_de94_d0d5,
552                    0x1099_4b0c_1744_c040,
553                ]),
554            },
555        },
556        c1: Fp6 {
557            c0: Fp2 {
558                c0: Fp::from_raw_unchecked([
559                    0x47f9_cb98_b1b8_2d58,
560                    0x5fe9_11eb_a3aa_1d9d,
561                    0x96bf_1b5f_4dd8_1db3,
562                    0x8100_d27c_c925_9f5b,
563                    0xafa2_0b96_7464_0eab,
564                    0x09bb_cea7_d8d9_497d,
565                ]),
566                c1: Fp::from_raw_unchecked([
567                    0x0303_cb98_b166_2daa,
568                    0xd931_10aa_0a62_1d5a,
569                    0xbfa9_820c_5be4_a468,
570                    0x0ba3_643e_cb05_a348,
571                    0xdc35_34bb_1f1c_25a6,
572                    0x06c3_05bb_19c0_e1c1,
573                ]),
574            },
575            c1: Fp2 {
576                c0: Fp::from_raw_unchecked([
577                    0x46f9_cb98_b162_d858,
578                    0x0be9_109c_f7aa_1d57,
579                    0xc791_bc55_fece_41d2,
580                    0xf84c_5770_4e38_5ec2,
581                    0xcb49_c1d3_c010_e60f,
582                    0x0acd_b8e1_58bf_e3c8,
583                ]),
584                c1: Fp::from_raw_unchecked([
585                    0x8aef_cb98_b15f_8306,
586                    0x3ea1_108f_e4f2_1d54,
587                    0xcf79_f69f_a1b7_df3b,
588                    0xe4f5_4aa1_d16b_1a3c,
589                    0xba5e_4ef8_6105_a679,
590                    0x0ed8_6c07_97be_e5cf,
591                ]),
592            },
593            c2: Fp2 {
594                c0: Fp::from_raw_unchecked([
595                    0xcee5_cb98_b15c_2db4,
596                    0x7159_1082_d23a_1d51,
597                    0xd762_30e9_44a1_7ca4,
598                    0xd19e_3dd3_549d_d5b6,
599                    0xa972_dc17_01fa_66e3,
600                    0x12e3_1f2d_d6bd_e7d6,
601                ]),
602                c1: Fp::from_raw_unchecked([
603                    0xad2a_cb98_b173_2d9d,
604                    0x2cfd_10dd_0696_1d64,
605                    0x0739_6b86_c6ef_24e8,
606                    0xbd76_e2fd_b1bf_c820,
607                    0x6afe_a7f6_de94_d0d5,
608                    0x1099_4b0c_5744_1040,
609                ]),
610            },
611        },
612    };
613
614    // because a and b and c are similar to each other and
615    // I was lazy, this is just some arbitrary way to make
616    // them a little more different
617    let a = a.square().invert().unwrap().square() + c;
618    let b = b.square().invert().unwrap().square() + a;
619    let c = c.square().invert().unwrap().square() + b;
620
621    assert_eq!(a.square(), a * a);
622    assert_eq!(b.square(), b * b);
623    assert_eq!(c.square(), c * c);
624
625    assert_eq!((a + b) * c.square(), (c * c * a) + (c * c * b));
626
627    assert_eq!(
628        a.invert().unwrap() * b.invert().unwrap(),
629        (a * b).invert().unwrap()
630    );
631    assert_eq!(a.invert().unwrap() * a, Fp12::one());
632
633    assert!(a != a.frobenius_map());
634    assert_eq!(
635        a,
636        a.frobenius_map()
637            .frobenius_map()
638            .frobenius_map()
639            .frobenius_map()
640            .frobenius_map()
641            .frobenius_map()
642            .frobenius_map()
643            .frobenius_map()
644            .frobenius_map()
645            .frobenius_map()
646            .frobenius_map()
647            .frobenius_map()
648    );
649}
650
651#[cfg(feature = "zeroize")]
652#[test]
653fn test_zeroize() {
654    use zeroize::Zeroize;
655
656    let mut a = Fp12::one();
657    a.zeroize();
658    assert!(bool::from(a.is_zero()));
659}