1use super::fp::Fp;
2use super::fp2::Fp2;
3use crate::ff::Field;
4use core::ops::{Add, Mul, Neg, Sub};
5use rand::RngCore;
6use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
7
8pub(crate) const V_CUBE: Fp2 = Fp2 {
14 c0: Fp::from_raw([
16 0xddb6da4b5b6db6e8,
17 0x833bf7b35b701d98,
18 0x3f6072240ebe2483,
19 0x73cd928ee056022c,
20 0xce4a7f2a7bcb4495,
21 0xdbda9924971b3a9a,
22 0x0cdb6db6db6dc3b6,
23 ]),
24 c1: Fp::from_raw([
26 0xeb6db62d36db6db3,
27 0xb523fb0536dcde8e,
28 0x8c6d1148d5a5491b,
29 0x457b57ef5366ce1a,
30 0x489319197d79f5f3,
31 0xb71cc2492776bcc3,
32 0x07b6db6db6db756d,
33 ]),
34};
35
36#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]
37pub struct Fp6 {
39 pub c0: Fp2,
40 pub c1: Fp2,
41 pub c2: Fp2,
42}
43
44impl ConditionallySelectable for Fp6 {
45 fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
46 Fp6 {
47 c0: Fp2::conditional_select(&a.c0, &b.c0, choice),
48 c1: Fp2::conditional_select(&a.c1, &b.c1, choice),
49 c2: Fp2::conditional_select(&a.c2, &b.c2, choice),
50 }
51 }
52}
53
54impl ConstantTimeEq for Fp6 {
55 fn ct_eq(&self, other: &Self) -> Choice {
56 self.c0.ct_eq(&other.c0) & self.c1.ct_eq(&other.c1) & self.c2.ct_eq(&other.c2)
57 }
58}
59
60impl Neg for Fp6 {
61 type Output = Fp6;
62
63 #[inline]
64 fn neg(self) -> Fp6 {
65 -&self
66 }
67}
68
69impl<'a> Neg for &'a Fp6 {
70 type Output = Fp6;
71
72 #[inline]
73 fn neg(self) -> Fp6 {
74 self.neg()
75 }
76}
77
78impl<'a, 'b> Sub<&'b Fp6> for &'a Fp6 {
79 type Output = Fp6;
80
81 #[inline]
82 fn sub(self, rhs: &'b Fp6) -> Fp6 {
83 self.sub(rhs)
84 }
85}
86
87impl<'a, 'b> Add<&'b Fp6> for &'a Fp6 {
88 type Output = Fp6;
89
90 #[inline]
91 fn add(self, rhs: &'b Fp6) -> Fp6 {
92 self.add(rhs)
93 }
94}
95
96impl<'a, 'b> Mul<&'b Fp6> for &'a Fp6 {
97 type Output = Fp6;
98
99 #[inline]
100 fn mul(self, rhs: &'b Fp6) -> Fp6 {
101 self.mul(rhs)
102 }
103}
104
105use crate::{
106 impl_add_binop_specify_output, impl_binops_additive, impl_binops_additive_specify_output,
107 impl_binops_multiplicative, impl_binops_multiplicative_mixed, impl_sub_binop_specify_output,
108 impl_sum_prod,
109};
110impl_binops_additive!(Fp6, Fp6);
111impl_binops_multiplicative!(Fp6, Fp6);
112impl_sum_prod!(Fp6);
113
114impl Fp6 {
115 #[inline]
116 pub const fn zero() -> Self {
117 Fp6 {
118 c0: Fp2::ZERO,
119 c1: Fp2::ZERO,
120 c2: Fp2::ZERO,
121 }
122 }
123
124 #[inline]
125 pub const fn one() -> Self {
126 Fp6 {
127 c0: Fp2::ONE,
128 c1: Fp2::ZERO,
129 c2: Fp2::ZERO,
130 }
131 }
132
133 pub fn mul_assign(&mut self, other: &Self) {
134 let mut a_a = self.c0;
135 let mut b_b = self.c1;
136 let mut c_c = self.c2;
137 a_a *= &other.c0;
138 b_b *= &other.c1;
139 c_c *= &other.c2;
140
141 let mut t1 = other.c1;
142 t1 += &other.c2;
143 {
144 let mut tmp = self.c1;
145 tmp += &self.c2;
146
147 t1 *= &tmp;
148 t1 -= &b_b;
149 t1 -= &c_c;
150 t1.mul_by_nonresidue();
151 t1 += &a_a;
152 }
153
154 let mut t3 = other.c0;
155 t3 += &other.c2;
156 {
157 let mut tmp = self.c0;
158 tmp += &self.c2;
159
160 t3 *= &tmp;
161 t3 -= &a_a;
162 t3 += &b_b;
163 t3 -= &c_c;
164 }
165
166 let mut t2 = other.c0;
167 t2 += &other.c1;
168 {
169 let mut tmp = self.c0;
170 tmp += &self.c1;
171
172 t2 *= &tmp;
173 t2 -= &a_a;
174 t2 -= &b_b;
175 c_c.mul_by_nonresidue();
176 t2 += &c_c;
177 }
178
179 self.c0 = t1;
180 self.c1 = t2;
181 self.c2 = t3;
182 }
183
184 pub fn square_assign(&mut self) {
185 let mut s0 = self.c0;
187 s0.square_assign();
188 let mut ab = self.c0;
190 ab *= &self.c1;
191 let mut s1 = ab;
192 s1.double_assign();
193 let mut s2 = self.c0;
195 s2 -= &self.c1;
196 s2 += &self.c2;
197 s2.square_assign();
198 let mut bc = self.c1;
200 bc *= &self.c2;
201 let mut s3 = bc;
203 s3.double_assign();
204 let mut s4 = self.c2;
206 s4.square_assign();
207
208 self.c0 = s3;
210 self.c0.mul_by_nonresidue();
211 self.c0 += &s0;
213
214 self.c1 = s4;
216 self.c1.mul_by_nonresidue();
217 self.c1 += &s1;
219
220 self.c2 = s1;
222 self.c2 += &s2;
223 self.c2 += &s3;
224 self.c2 -= &s0;
225 self.c2 -= &s4;
226 }
227
228 pub fn double(&self) -> Self {
229 Self {
230 c0: self.c0.double(),
231 c1: self.c1.double(),
232 c2: self.c2.double(),
233 }
234 }
235
236 pub fn double_assign(&mut self) {
237 self.c0 = self.c0.double();
238 self.c1 = self.c1.double();
239 self.c2 = self.c2.double();
240 }
241
242 pub fn add(&self, other: &Self) -> Self {
243 Self {
244 c0: self.c0 + other.c0,
245 c1: self.c1 + other.c1,
246 c2: self.c2 + other.c2,
247 }
248 }
249
250 pub fn sub(&self, other: &Self) -> Self {
251 Self {
252 c0: self.c0 - other.c0,
253 c1: self.c1 - other.c1,
254 c2: self.c2 - other.c2,
255 }
256 }
257
258 pub fn mul(&self, other: &Self) -> Self {
259 let mut t = *other;
260 t.mul_assign(self);
261 t
262 }
263
264 pub fn square(&self) -> Self {
265 let mut t = *self;
266 t.square_assign();
267 t
268 }
269
270 pub fn neg(&self) -> Self {
271 Self {
272 c0: -self.c0,
273 c1: -self.c1,
274 c2: -self.c2,
275 }
276 }
277
278 pub fn frobenius_map(&mut self, power: usize) {
279 self.c0.frobenius_map(power);
280 self.c1.frobenius_map(power);
281 self.c2.frobenius_map(power);
282
283 self.c1.mul_assign(&FROBENIUS_COEFF_FP6_C1[power % 6]);
284 self.c2.mul_assign(&FROBENIUS_COEFF_FP6_C2[power % 6]);
285 }
286
287 pub fn mul_by_nonresidue(&mut self) {
289 use std::mem::swap;
290 swap(&mut self.c0, &mut self.c1);
291 swap(&mut self.c0, &mut self.c2);
292 self.c0.mul_by_nonresidue();
294 }
295
296 pub fn mul_by_1(&mut self, c1: &Fp2) {
297 let mut b_b = self.c1;
298 b_b *= c1;
299
300 let mut t1 = *c1;
301 {
302 let mut tmp = self.c1;
303 tmp += &self.c2;
304
305 t1 *= &tmp;
306 t1 -= &b_b;
307 t1.mul_by_nonresidue();
308 }
309
310 let mut t2 = *c1;
311 {
312 let mut tmp = self.c0;
313 tmp += &self.c1;
314
315 t2 *= &tmp;
316 t2 -= &b_b;
317 }
318
319 self.c0 = t1;
320 self.c1 = t2;
321 self.c2 = b_b;
322 }
323
324 pub fn mul_by_01(&mut self, c0: &Fp2, c1: &Fp2) {
325 let mut a_a = self.c0;
326 let mut b_b = self.c1;
327 a_a *= c0;
328 b_b *= c1;
329
330 let mut t1 = *c1;
331 {
332 let mut tmp = self.c1;
333 tmp += &self.c2;
334
335 t1 *= &tmp;
336 t1 -= &b_b;
337 t1.mul_by_nonresidue();
338 t1 += &a_a;
339 }
340
341 let mut t3 = *c0;
342 {
343 let mut tmp = self.c0;
344 tmp += &self.c2;
345
346 t3 *= &tmp;
347 t3 -= &a_a;
348 t3 += &b_b;
349 }
350
351 let mut t2 = *c0;
352 t2 += c1;
353 {
354 let mut tmp = self.c0;
355 tmp += &self.c1;
356
357 t2 *= &tmp;
358 t2 -= &a_a;
359 t2 -= &b_b;
360 }
361
362 self.c0 = t1;
363 self.c1 = t2;
364 self.c2 = t3;
365 }
366
367 fn invert(&self) -> CtOption<Self> {
368 let mut c0 = self.c2;
369 c0.mul_by_nonresidue();
370 c0 *= &self.c1;
371 c0 = -c0;
372 {
373 let mut c0s = self.c0;
374 c0s.square_assign();
375 c0 += &c0s;
376 }
377 let mut c1 = self.c2;
378 c1.square_assign();
379 c1.mul_by_nonresidue();
380 {
381 let mut c01 = self.c0;
382 c01 *= &self.c1;
383 c1 -= &c01;
384 }
385 let mut c2 = self.c1;
386 c2.square_assign();
387 {
388 let mut c02 = self.c0;
389 c02 *= &self.c2;
390 c2 -= &c02;
391 }
392
393 let mut tmp1 = self.c2;
394 tmp1 *= &c1;
395 let mut tmp2 = self.c1;
396 tmp2 *= &c2;
397 tmp1 += &tmp2;
398 tmp1.mul_by_nonresidue();
399 tmp2 = self.c0;
400 tmp2 *= &c0;
401 tmp1 += &tmp2;
402
403 tmp1.invert().map(|t| {
404 let mut tmp = Fp6 {
405 c0: t,
406 c1: t,
407 c2: t,
408 };
409 tmp.c0 *= &c0;
410 tmp.c1 *= &c1;
411 tmp.c2 *= &c2;
412
413 tmp
414 })
415 }
416}
417
418impl Field for Fp6 {
419 const ZERO: Self = Self::zero();
420 const ONE: Self = Self::one();
421
422 fn random(mut rng: impl RngCore) -> Self {
423 Fp6 {
424 c0: Fp2::random(&mut rng),
425 c1: Fp2::random(&mut rng),
426 c2: Fp2::random(&mut rng),
427 }
428 }
429
430 fn is_zero(&self) -> Choice {
431 self.c0.is_zero() & self.c1.is_zero()
432 }
433
434 fn square(&self) -> Self {
435 self.square()
436 }
437
438 fn double(&self) -> Self {
439 self.double()
440 }
441
442 fn sqrt(&self) -> CtOption<Self> {
443 unimplemented!()
444 }
445
446 fn sqrt_ratio(_num: &Self, _div: &Self) -> (Choice, Self) {
447 unimplemented!()
448 }
449
450 fn invert(&self) -> CtOption<Self> {
451 self.invert()
452 }
453}
454
455pub(crate) const FROBENIUS_COEFF_FP6_C1: [Fp2; 6] = [
457 Fp2::ONE,
459 Fp2 {
461 c0: Fp::from_raw([
463 0xa001825382850d03,
464 0x52e787634eccc335,
465 0x041bc4bb14cc42dc,
466 0x6f93678bc921dcd5,
467 0xe3886170077a5098,
468 0x3bc0d351f4c70da1,
469 0x120de97f024c55bc,
470 ]),
471 c1: Fp::from_raw([
473 0x2772fea378a9b322,
474 0x13be1b7cd3c9ea0e,
475 0x5efa1ddf46580804,
476 0xa1a7f5502b709196,
477 0x132461908eadbe3d,
478 0xd82becc2ef081b76,
479 0x2096f3f804d973af,
480 ]),
481 },
482 Fp2 {
484 c0: Fp::from_raw([
486 0x100004c37ffffffe,
487 0xc8ad8b38dffaf50c,
488 0xc956d01c903d720d,
489 0x50000d7ee0e4a803,
490 0x00000000360001c9,
491 0x0000000000004800,
492 0x0000000000000000,
493 ]),
494 c1: Fp::ZERO,
495 },
496 Fp2 {
498 c0: Fp::from_raw([
500 0xa04518fa783eb497,
501 0x8b43b17b4c3ea938,
502 0x7cd52ae30d09b78a,
503 0x2913e38315a71c28,
504 0x833b798e78bd98c0,
505 0x2511749de232911d,
506 0x1f9cd069c59f50a7,
507 ]),
508 c1: Fp::from_raw([
510 0x77240d4066e42ac5,
511 0x4a728fcb77b150ee,
512 0xe8faacb3479a973a,
513 0xc0aa2122aa7cb689,
514 0x3f4af36699fe6d74,
515 0xc26943f93dc9eab6,
516 0x23affd628747cbae,
517 ]),
518 },
519 Fp2 {
521 c0: Fp::from_raw([
523 0x8ffff80f80000002,
524 0xd9fa5d8a200bc439,
525 0x1b50d5e1ff708dc8,
526 0xf43f8cddf9a5c478,
527 0xa803ca76be3924a5,
528 0x0130e0000d7f28e4,
529 0x2400000000002400,
530 ]),
531 c1: Fp::ZERO,
532 },
533 Fp2 {
535 c0: Fp::from_raw([
537 0xffb95e58053c3e68,
538 0x672498a76502061c,
539 0x485e5c5efd860546,
540 0xefd7e9aad64bdffa,
541 0xe943b9ef683a6385,
542 0xa18f781044054309,
543 0x165546173814a19c,
544 ]),
545 c1: Fp::from_raw([
547 0xa168edc22072221b,
548 0xe71f263db492378e,
549 0x815a816a9169606e,
550 0x262d1e46df2790d7,
551 0xfd983ff6bfc6212c,
552 0x67cc8f43ee2cdb9c,
553 0x03b90ea573df08a1,
554 ]),
555 },
556];
557
558pub(crate) const FROBENIUS_COEFF_FP6_C2: [Fp2; 6] = [
560 Fp2::ONE,
562 Fp2 {
564 c0: Fp::from_raw([
566 0xb0a818e422737f7c,
567 0xd0ec132a7b38f09d,
568 0xc00e17ffb4a61950,
569 0x8c9549048e7b424c,
570 0xdc590efb038c8299,
571 0xc34610bac6bd22c4,
572 0x093733692ce3cdcf,
573 ]),
574 c1: Fp::from_raw([
576 0x240ce398421dc7dc,
577 0x61c90b4940d9031c,
578 0xcdac3209d9df5460,
579 0x6f61d0bd35f39ebe,
580 0x26f29bd46039e303,
581 0xba3593aa6f3e6bf4,
582 0x12cb19daadc92882,
583 ]),
584 },
585 Fp2 {
587 c0: Fp::from_raw([
589 0x8ffff80f80000002,
590 0xd9fa5d8a200bc439,
591 0x1b50d5e1ff708dc8,
592 0xf43f8cddf9a5c478,
593 0xa803ca76be3924a5,
594 0x0130e0000d7f28e4,
595 0x2400000000002400,
596 ]),
597 c1: Fp::ZERO,
598 },
599 Fp2 {
601 c0: Fp::from_raw([
603 0x6885980d6c8d2186,
604 0x8f57e1c7a87f7555,
605 0x9aa5ace8b26b78d8,
606 0x1657aaf1e85b8229,
607 0xb2ce0cbc85b8561e,
608 0x5f7dd2f9f1405312,
609 0x085cc83a7eeba2ef,
610 ]),
611 c1: Fp::from_raw([
613 0x2bca2a953ab51c9b,
614 0x0bbe8a0dd9ea1d31,
615 0x58d5ebe487e52368,
616 0xee0ea8b684b9400d,
617 0x7abc03d404620899,
618 0x6af75e8ec0dbd23e,
619 0x0da3357ee4e6a983,
620 ]),
621 },
622 Fp2 {
624 c0: Fp::from_raw([
626 0x100004c37ffffffe,
627 0xc8ad8b38dffaf50c,
628 0xc956d01c903d720d,
629 0x50000d7ee0e4a803,
630 0x00000000360001c9,
631 0x0000000000004800,
632 0x0000000000000000,
633 ]),
634 c1: Fp::ZERO,
635 },
636 Fp2 {
638 c0: Fp::from_raw([
640 0x86d24be170ff5eff,
641 0x4263f3d0dc4e5352,
642 0x89f3e116289c6dad,
643 0xa152a66663b3a805,
644 0x18dcaebf6af44db7,
645 0xde6cfc4b5581fb0d,
646 0x126c045c5430b340,
647 ]),
648 c1: Fp::from_raw([
650 0x5028eea5832d1b8a,
651 0x3520536be54398f8,
652 0xbe2588102de9880e,
653 0xe6cf20e91fdd8daf,
654 0x06552ace8f9d3ad1,
655 0xdc03edc6dd6532b2,
656 0x0391b0a66d5051f9,
657 ]),
658 },
659];
660
661#[cfg(test)]
662use rand::SeedableRng;
663#[cfg(test)]
664use rand_xorshift::XorShiftRng;
665
666#[test]
667fn test_fp6_mul_nonresidue() {
668 let mut rng = XorShiftRng::from_seed([
669 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
670 0xe5,
671 ]);
672
673 let nqr = Fp6 {
674 c0: Fp2::zero(),
675 c1: Fp2::one(),
676 c2: Fp2::zero(),
677 };
678
679 for _ in 0..1000 {
680 let mut a = Fp6::random(&mut rng);
681 let mut b = a;
682 a.mul_by_nonresidue();
683 b.mul_assign(&nqr);
684
685 assert_eq!(a, b);
686 }
687}
688
689#[test]
690fn test_fp6_mul_by_1() {
691 let mut rng = XorShiftRng::from_seed([
692 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
693 0xe5,
694 ]);
695
696 for _ in 0..1000 {
697 let c1 = Fp2::random(&mut rng);
698 let mut a = Fp6::random(&mut rng);
699 let mut b = a;
700
701 a.mul_by_1(&c1);
702 b.mul_assign(&Fp6 {
703 c0: Fp2::zero(),
704 c1,
705 c2: Fp2::zero(),
706 });
707
708 assert_eq!(a, b);
709 }
710}
711
712#[test]
713fn test_fp6_mul_by_01() {
714 let mut rng = XorShiftRng::from_seed([
715 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
716 0xe5,
717 ]);
718
719 for _ in 0..1000 {
720 let c0 = Fp2::random(&mut rng);
721 let c1 = Fp2::random(&mut rng);
722 let mut a = Fp6::random(&mut rng);
723 let mut b = a;
724
725 a.mul_by_01(&c0, &c1);
726 b.mul_assign(&Fp6 {
727 c0,
728 c1,
729 c2: Fp2::zero(),
730 });
731
732 assert_eq!(a, b);
733 }
734}
735
736#[test]
737fn test_squaring() {
738 let mut rng = XorShiftRng::from_seed([
739 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
740 0xe5,
741 ]);
742
743 for _ in 0..1000 {
744 let mut a = Fp6::random(&mut rng);
745 let mut b = a;
746 b.mul_assign(&a);
747 a.square_assign();
748 assert_eq!(a, b);
749 }
750}
751
752#[test]
753fn test_frobenius() {
754 let mut rng = XorShiftRng::from_seed([
755 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
756 0xe5,
757 ]);
758
759 for _ in 0..50 {
760 for i in 0..8 {
761 let mut a = Fp6::random(&mut rng);
762 let mut b = a;
763
764 for _ in 0..i {
765 a = a.pow_vartime([
766 0x9ffffcd300000001,
768 0xa2a7e8c30006b945,
769 0xe4a7a5fe8fadffd6,
770 0x443f9a5cda8a6c7b,
771 0xa803ca76f439266f,
772 0x0130e0000d7f70e4,
773 0x2400000000002400,
774 ]);
775 }
776 b.frobenius_map(i);
777
778 assert_eq!(a, b);
779 }
780 }
781}
782
783#[test]
784fn test_field() {
785 crate::tests::field::random_field_tests::<Fp6>("fp6".to_string());
786}