1use crate::fp::*;
2use crate::fp2::*;
3
4use core::fmt;
5use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign};
6use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
7
8#[cfg(feature = "pairings")]
9use rand_core::RngCore;
10
11pub struct Fp6 {
13 pub c0: Fp2,
14 pub c1: Fp2,
15 pub c2: Fp2,
16}
17
18impl From<Fp> for Fp6 {
19 fn from(f: Fp) -> Fp6 {
20 Fp6 {
21 c0: Fp2::from(f),
22 c1: Fp2::zero(),
23 c2: Fp2::zero(),
24 }
25 }
26}
27
28impl From<Fp2> for Fp6 {
29 fn from(f: Fp2) -> Fp6 {
30 Fp6 {
31 c0: f,
32 c1: Fp2::zero(),
33 c2: Fp2::zero(),
34 }
35 }
36}
37
38impl PartialEq for Fp6 {
39 fn eq(&self, other: &Fp6) -> bool {
40 self.ct_eq(other).into()
41 }
42}
43
44impl Copy for Fp6 {}
45impl Clone for Fp6 {
46 #[inline]
47 fn clone(&self) -> Self {
48 *self
49 }
50}
51
52impl Default for Fp6 {
53 fn default() -> Self {
54 Fp6::zero()
55 }
56}
57
58#[cfg(feature = "zeroize")]
59impl zeroize::DefaultIsZeroes for Fp6 {}
60
61impl fmt::Debug for Fp6 {
62 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
63 write!(f, "{:?} + ({:?})*v + ({:?})*v^2", self.c0, self.c1, self.c2)
64 }
65}
66
67impl ConditionallySelectable for Fp6 {
68 #[inline(always)]
69 fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
70 Fp6 {
71 c0: Fp2::conditional_select(&a.c0, &b.c0, choice),
72 c1: Fp2::conditional_select(&a.c1, &b.c1, choice),
73 c2: Fp2::conditional_select(&a.c2, &b.c2, choice),
74 }
75 }
76}
77
78impl ConstantTimeEq for Fp6 {
79 #[inline(always)]
80 fn ct_eq(&self, other: &Self) -> Choice {
81 self.c0.ct_eq(&other.c0) & self.c1.ct_eq(&other.c1) & self.c2.ct_eq(&other.c2)
82 }
83}
84
85impl Fp6 {
86 #[inline]
87 pub fn zero() -> Self {
88 Fp6 {
89 c0: Fp2::zero(),
90 c1: Fp2::zero(),
91 c2: Fp2::zero(),
92 }
93 }
94
95 #[inline]
96 pub fn one() -> Self {
97 Fp6 {
98 c0: Fp2::one(),
99 c1: Fp2::zero(),
100 c2: Fp2::zero(),
101 }
102 }
103
104 #[cfg(feature = "pairings")]
105 pub(crate) fn random(mut rng: impl RngCore) -> Self {
106 Fp6 {
107 c0: Fp2::random(&mut rng),
108 c1: Fp2::random(&mut rng),
109 c2: Fp2::random(&mut rng),
110 }
111 }
112
113 pub fn mul_by_1(&self, c1: &Fp2) -> Fp6 {
114 Fp6 {
115 c0: (self.c2 * c1).mul_by_nonresidue(),
116 c1: self.c0 * c1,
117 c2: self.c1 * c1,
118 }
119 }
120
121 pub fn mul_by_01(&self, c0: &Fp2, c1: &Fp2) -> Fp6 {
122 let a_a = self.c0 * c0;
123 let b_b = self.c1 * c1;
124
125 let t1 = (self.c2 * c1).mul_by_nonresidue() + a_a;
126
127 let t2 = (c0 + c1) * (self.c0 + self.c1) - a_a - b_b;
128
129 let t3 = self.c2 * c0 + b_b;
130
131 Fp6 {
132 c0: t1,
133 c1: t2,
134 c2: t3,
135 }
136 }
137
138 pub fn mul_by_nonresidue(&self) -> Self {
140 Fp6 {
146 c0: self.c2.mul_by_nonresidue(),
147 c1: self.c0,
148 c2: self.c1,
149 }
150 }
151
152 #[inline(always)]
154 pub fn frobenius_map(&self) -> Self {
155 let c0 = self.c0.frobenius_map();
156 let c1 = self.c1.frobenius_map();
157 let c2 = self.c2.frobenius_map();
158
159 let c1 = c1
161 * Fp2 {
162 c0: Fp::zero(),
163 c1: Fp::from_raw_unchecked([
164 0xcd03_c9e4_8671_f071,
165 0x5dab_2246_1fcd_a5d2,
166 0x5870_42af_d385_1b95,
167 0x8eb6_0ebe_01ba_cb9e,
168 0x03f9_7d6e_83d0_50d2,
169 0x18f0_2065_5463_8741,
170 ]),
171 };
172
173 let c2 = c2
175 * Fp2 {
176 c0: Fp::from_raw_unchecked([
177 0x890d_c9e4_8675_45c3,
178 0x2af3_2253_3285_a5d5,
179 0x5088_0866_309b_7e2c,
180 0xa20d_1b8c_7e88_1024,
181 0x14e4_f04f_e2db_9068,
182 0x14e5_6d3f_1564_853a,
183 ]),
184 c1: Fp::zero(),
185 };
186
187 Fp6 { c0, c1, c2 }
188 }
189
190 #[inline(always)]
191 pub fn is_zero(&self) -> Choice {
192 self.c0.is_zero() & self.c1.is_zero() & self.c2.is_zero()
193 }
194
195 #[inline]
200 fn mul_interleaved(&self, b: &Self) -> Self {
201 let a = self;
237 let b10_p_b11 = b.c1.c0 + b.c1.c1;
238 let b10_m_b11 = b.c1.c0 - b.c1.c1;
239 let b20_p_b21 = b.c2.c0 + b.c2.c1;
240 let b20_m_b21 = b.c2.c0 - b.c2.c1;
241
242 Fp6 {
243 c0: Fp2 {
244 c0: Fp::sum_of_products(
245 [a.c0.c0, -a.c0.c1, a.c1.c0, -a.c1.c1, a.c2.c0, -a.c2.c1],
246 [b.c0.c0, b.c0.c1, b20_m_b21, b20_p_b21, b10_m_b11, b10_p_b11],
247 ),
248 c1: Fp::sum_of_products(
249 [a.c0.c0, a.c0.c1, a.c1.c0, a.c1.c1, a.c2.c0, a.c2.c1],
250 [b.c0.c1, b.c0.c0, b20_p_b21, b20_m_b21, b10_p_b11, b10_m_b11],
251 ),
252 },
253 c1: Fp2 {
254 c0: Fp::sum_of_products(
255 [a.c0.c0, -a.c0.c1, a.c1.c0, -a.c1.c1, a.c2.c0, -a.c2.c1],
256 [b.c1.c0, b.c1.c1, b.c0.c0, b.c0.c1, b20_m_b21, b20_p_b21],
257 ),
258 c1: Fp::sum_of_products(
259 [a.c0.c0, a.c0.c1, a.c1.c0, a.c1.c1, a.c2.c0, a.c2.c1],
260 [b.c1.c1, b.c1.c0, b.c0.c1, b.c0.c0, b20_p_b21, b20_m_b21],
261 ),
262 },
263 c2: Fp2 {
264 c0: Fp::sum_of_products(
265 [a.c0.c0, -a.c0.c1, a.c1.c0, -a.c1.c1, a.c2.c0, -a.c2.c1],
266 [b.c2.c0, b.c2.c1, b.c1.c0, b.c1.c1, b.c0.c0, b.c0.c1],
267 ),
268 c1: Fp::sum_of_products(
269 [a.c0.c0, a.c0.c1, a.c1.c0, a.c1.c1, a.c2.c0, a.c2.c1],
270 [b.c2.c1, b.c2.c0, b.c1.c1, b.c1.c0, b.c0.c1, b.c0.c0],
271 ),
272 },
273 }
274 }
275
276 #[inline]
277 pub fn square(&self) -> Self {
278 let s0 = self.c0.square();
279 let ab = self.c0 * self.c1;
280 let s1 = ab + ab;
281 let s2 = (self.c0 - self.c1 + self.c2).square();
282 let bc = self.c1 * self.c2;
283 let s3 = bc + bc;
284 let s4 = self.c2.square();
285
286 Fp6 {
287 c0: s3.mul_by_nonresidue() + s0,
288 c1: s4.mul_by_nonresidue() + s1,
289 c2: s1 + s2 + s3 - s0 - s4,
290 }
291 }
292
293 #[inline]
294 pub fn invert(&self) -> CtOption<Self> {
295 let c0 = (self.c1 * self.c2).mul_by_nonresidue();
296 let c0 = self.c0.square() - c0;
297
298 let c1 = self.c2.square().mul_by_nonresidue();
299 let c1 = c1 - (self.c0 * self.c1);
300
301 let c2 = self.c1.square();
302 let c2 = c2 - (self.c0 * self.c2);
303
304 let tmp = ((self.c1 * c2) + (self.c2 * c1)).mul_by_nonresidue();
305 let tmp = tmp + (self.c0 * c0);
306
307 tmp.invert().map(|t| Fp6 {
308 c0: t * c0,
309 c1: t * c1,
310 c2: t * c2,
311 })
312 }
313}
314
315impl<'a, 'b> Mul<&'b Fp6> for &'a Fp6 {
316 type Output = Fp6;
317
318 #[inline]
319 fn mul(self, other: &'b Fp6) -> Self::Output {
320 self.mul_interleaved(other)
321 }
322}
323
324impl<'a, 'b> Add<&'b Fp6> for &'a Fp6 {
325 type Output = Fp6;
326
327 #[inline]
328 fn add(self, rhs: &'b Fp6) -> Self::Output {
329 Fp6 {
330 c0: self.c0 + rhs.c0,
331 c1: self.c1 + rhs.c1,
332 c2: self.c2 + rhs.c2,
333 }
334 }
335}
336
337impl<'a> Neg for &'a Fp6 {
338 type Output = Fp6;
339
340 #[inline]
341 fn neg(self) -> Self::Output {
342 Fp6 {
343 c0: -self.c0,
344 c1: -self.c1,
345 c2: -self.c2,
346 }
347 }
348}
349
350impl Neg for Fp6 {
351 type Output = Fp6;
352
353 #[inline]
354 fn neg(self) -> Self::Output {
355 -&self
356 }
357}
358
359impl<'a, 'b> Sub<&'b Fp6> for &'a Fp6 {
360 type Output = Fp6;
361
362 #[inline]
363 fn sub(self, rhs: &'b Fp6) -> Self::Output {
364 Fp6 {
365 c0: self.c0 - rhs.c0,
366 c1: self.c1 - rhs.c1,
367 c2: self.c2 - rhs.c2,
368 }
369 }
370}
371
372impl_binops_additive!(Fp6, Fp6);
373impl_binops_multiplicative!(Fp6, Fp6);
374
375#[test]
376fn test_arithmetic() {
377 use crate::fp::*;
378
379 let a = Fp6 {
380 c0: Fp2 {
381 c0: Fp::from_raw_unchecked([
382 0x47f9_cb98_b1b8_2d58,
383 0x5fe9_11eb_a3aa_1d9d,
384 0x96bf_1b5f_4dd8_1db3,
385 0x8100_d27c_c925_9f5b,
386 0xafa2_0b96_7464_0eab,
387 0x09bb_cea7_d8d9_497d,
388 ]),
389 c1: Fp::from_raw_unchecked([
390 0x0303_cb98_b166_2daa,
391 0xd931_10aa_0a62_1d5a,
392 0xbfa9_820c_5be4_a468,
393 0x0ba3_643e_cb05_a348,
394 0xdc35_34bb_1f1c_25a6,
395 0x06c3_05bb_19c0_e1c1,
396 ]),
397 },
398 c1: Fp2 {
399 c0: Fp::from_raw_unchecked([
400 0x46f9_cb98_b162_d858,
401 0x0be9_109c_f7aa_1d57,
402 0xc791_bc55_fece_41d2,
403 0xf84c_5770_4e38_5ec2,
404 0xcb49_c1d9_c010_e60f,
405 0x0acd_b8e1_58bf_e3c8,
406 ]),
407 c1: Fp::from_raw_unchecked([
408 0x8aef_cb98_b15f_8306,
409 0x3ea1_108f_e4f2_1d54,
410 0xcf79_f69f_a1b7_df3b,
411 0xe4f5_4aa1_d16b_1a3c,
412 0xba5e_4ef8_6105_a679,
413 0x0ed8_6c07_97be_e5cf,
414 ]),
415 },
416 c2: Fp2 {
417 c0: Fp::from_raw_unchecked([
418 0xcee5_cb98_b15c_2db4,
419 0x7159_1082_d23a_1d51,
420 0xd762_30e9_44a1_7ca4,
421 0xd19e_3dd3_549d_d5b6,
422 0xa972_dc17_01fa_66e3,
423 0x12e3_1f2d_d6bd_e7d6,
424 ]),
425 c1: Fp::from_raw_unchecked([
426 0xad2a_cb98_b173_2d9d,
427 0x2cfd_10dd_0696_1d64,
428 0x0739_6b86_c6ef_24e8,
429 0xbd76_e2fd_b1bf_c820,
430 0x6afe_a7f6_de94_d0d5,
431 0x1099_4b0c_5744_c040,
432 ]),
433 },
434 };
435
436 let b = Fp6 {
437 c0: Fp2 {
438 c0: Fp::from_raw_unchecked([
439 0xf120_cb98_b16f_d84b,
440 0x5fb5_10cf_f3de_1d61,
441 0x0f21_a5d0_69d8_c251,
442 0xaa1f_d62f_34f2_839a,
443 0x5a13_3515_7f89_913f,
444 0x14a3_fe32_9643_c247,
445 ]),
446 c1: Fp::from_raw_unchecked([
447 0x3516_cb98_b16c_82f9,
448 0x926d_10c2_e126_1d5f,
449 0x1709_e01a_0cc2_5fba,
450 0x96c8_c960_b825_3f14,
451 0x4927_c234_207e_51a9,
452 0x18ae_b158_d542_c44e,
453 ]),
454 },
455 c1: Fp2 {
456 c0: Fp::from_raw_unchecked([
457 0xbf0d_cb98_b169_82fc,
458 0xa679_10b7_1d1a_1d5c,
459 0xb7c1_47c2_b8fb_06ff,
460 0x1efa_710d_47d2_e7ce,
461 0xed20_a79c_7e27_653c,
462 0x02b8_5294_dac1_dfba,
463 ]),
464 c1: Fp::from_raw_unchecked([
465 0x9d52_cb98_b180_82e5,
466 0x621d_1111_5176_1d6f,
467 0xe798_8260_3b48_af43,
468 0x0ad3_1637_a4f4_da37,
469 0xaeac_737c_5ac1_cf2e,
470 0x006e_7e73_5b48_b824,
471 ]),
472 },
473 c2: Fp2 {
474 c0: Fp::from_raw_unchecked([
475 0xe148_cb98_b17d_2d93,
476 0x94d5_1104_3ebe_1d6c,
477 0xef80_bca9_de32_4cac,
478 0xf77c_0969_2827_95b1,
479 0x9dc1_009a_fbb6_8f97,
480 0x0479_3199_9a47_ba2b,
481 ]),
482 c1: Fp::from_raw_unchecked([
483 0x253e_cb98_b179_d841,
484 0xc78d_10f7_2c06_1d6a,
485 0xf768_f6f3_811b_ea15,
486 0xe424_fc9a_ab5a_512b,
487 0x8cd5_8db9_9cab_5001,
488 0x0883_e4bf_d946_bc32,
489 ]),
490 },
491 };
492
493 let c = Fp6 {
494 c0: Fp2 {
495 c0: Fp::from_raw_unchecked([
496 0x6934_cb98_b176_82ef,
497 0xfa45_10ea_194e_1d67,
498 0xff51_313d_2405_877e,
499 0xd0cd_efcc_2e8d_0ca5,
500 0x7bea_1ad8_3da0_106b,
501 0x0c8e_97e6_1845_be39,
502 ]),
503 c1: Fp::from_raw_unchecked([
504 0x4779_cb98_b18d_82d8,
505 0xb5e9_1144_4daa_1d7a,
506 0x2f28_6bda_a653_2fc2,
507 0xbca6_94f6_8bae_ff0f,
508 0x3d75_e6b8_1a3a_7a5d,
509 0x0a44_c3c4_98cc_96a3,
510 ]),
511 },
512 c1: Fp2 {
513 c0: Fp::from_raw_unchecked([
514 0x8b6f_cb98_b18a_2d86,
515 0xe8a1_1137_3af2_1d77,
516 0x3710_a624_493c_cd2b,
517 0xa94f_8828_0ee1_ba89,
518 0x2c8a_73d6_bb2f_3ac7,
519 0x0e4f_76ea_d7cb_98aa,
520 ]),
521 c1: Fp::from_raw_unchecked([
522 0xcf65_cb98_b186_d834,
523 0x1b59_112a_283a_1d74,
524 0x3ef8_e06d_ec26_6a95,
525 0x95f8_7b59_9214_7603,
526 0x1b9f_00f5_5c23_fb31,
527 0x125a_2a11_16ca_9ab1,
528 ]),
529 },
530 c2: Fp2 {
531 c0: Fp::from_raw_unchecked([
532 0x135b_cb98_b183_82e2,
533 0x4e11_111d_1582_1d72,
534 0x46e1_1ab7_8f10_07fe,
535 0x82a1_6e8b_1547_317d,
536 0x0ab3_8e13_fd18_bb9b,
537 0x1664_dd37_55c9_9cb8,
538 ]),
539 c1: Fp::from_raw_unchecked([
540 0xce65_cb98_b131_8334,
541 0xc759_0fdb_7c3a_1d2e,
542 0x6fcb_8164_9d1c_8eb3,
543 0x0d44_004d_1727_356a,
544 0x3746_b738_a7d0_d296,
545 0x136c_144a_96b1_34fc,
546 ]),
547 },
548 };
549
550 assert_eq!(a.square(), a * a);
551 assert_eq!(b.square(), b * b);
552 assert_eq!(c.square(), c * c);
553
554 assert_eq!((a + b) * c.square(), (c * c * a) + (c * c * b));
555
556 assert_eq!(
557 a.invert().unwrap() * b.invert().unwrap(),
558 (a * b).invert().unwrap()
559 );
560 assert_eq!(a.invert().unwrap() * a, Fp6::one());
561}
562
563#[cfg(feature = "zeroize")]
564#[test]
565fn test_zeroize() {
566 use zeroize::Zeroize;
567
568 let mut a = Fp6::one();
569 a.zeroize();
570 assert!(bool::from(a.is_zero()));
571}