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
12pub 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 #[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 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 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}