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#[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 unimplemented!()
320 }
321
322 fn sqrt_ratio(_num: &Self, _div: &Self) -> (Choice, Self) {
323 unimplemented!()
327 }
328
329 fn invert(&self) -> CtOption<Self> {
330 self.invert()
331 }
332}
333
334pub const FROBENIUS_COEFF_FP12_C1: [Fp2; 12] = [
336 Fp2::ONE,
338 Fp2 {
340 c0: Fp::from_raw([
342 0x2ee965dfef2303d3,
343 0x4e70648622c750f3,
344 0x1df055384b9e6cfd,
345 0x5d89c647b90de8f4,
346 0xd9892dedd51f3069,
347 0xdf0709dc343113cc,
348 0x03c3ad3da8b99cb1,
349 ]),
350 c1: Fp::from_raw([
352 0xfa0be24ad758a5ff,
353 0x59f9373f4cf44e6a,
354 0x9f03876b2ddb1435,
355 0xf333d05254029488,
356 0xccb29838628f04b6,
357 0xaaa3b912182da22d,
358 0x149fd9ed2c7affe7,
359 ]),
360 },
361 Fp2 {
363 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 {
377 c0: Fp::from_raw([
379 0x28a3be71be4995ca,
380 0xf4af912a897f60a1,
381 0x89d390efef216fe4,
382 0xa5d8739325984fc8,
383 0xaf5ca1e56a2a81e6,
384 0x5764b80089c40010,
385 0x1baee9e044d94d20,
386 ]),
387 c1: Fp::from_raw([
389 0x678f4479a1151232,
390 0x6f45d28e4ef1ecb9,
391 0x0b3c35a77291b29d,
392 0x07cd6b44b03ef113,
393 0xe65e47a7716bc8bc,
394 0x9b26f1795339413b,
395 0x20d4c11700e83282,
396 ]),
397 },
398 Fp2 {
400 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 {
414 c0: Fp::from_raw([
416 0xf9ba5891cf2691f7,
417 0xa63f2ca466b80fad,
418 0x6be33bb7a38302e7,
419 0x484ead4b6c8a66d4,
420 0xd5d373f7950b517d,
421 0x785dae245592ec43,
422 0x17eb3ca29c1fb06e,
423 ]),
424 c1: Fp::from_raw([
426 0x6d83622ec9bc6c33,
427 0x154c9b4f01fd9e4e,
428 0x6c38ae3c44b69e68,
429 0x14999af25c3c5c8a,
430 0x19abaf6f0edcc405,
431 0xf08338673b0b9f0e,
432 0x0c34e729d46d329a,
433 ]),
434 },
435 Fp2 {
437 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 {
451 c0: Fp::from_raw([
453 0x711696f310dcfc2e,
454 0x5437843cdd3f6852,
455 0xc6b750c6440f92d9,
456 0xe6b5d415217c8387,
457 0xce7a9c891f19f605,
458 0x2229d623d94e5d17,
459 0x203c52c25746874e,
460 ]),
461 c1: Fp::from_raw([
463 0xa5f41a8828a75a02,
464 0x48aeb183b3126ada,
465 0x45a41e9361d2eba1,
466 0x510bca0a8687d7f3,
467 0xdb51323e91aa21b8,
468 0x568d26edf551ceb6,
469 0x0f602612d3852418,
470 ]),
471 },
472 Fp2 {
474 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 {
488 c0: Fp::from_raw([
490 0x775c3e6141b66a37,
491 0xadf85798768758a4,
492 0x5ad4150ea08c8ff1,
493 0x9e6726c9b4f21cb3,
494 0xf8a728918a0ea488,
495 0xa9cc27ff83bb70d3,
496 0x0851161fbb26d6df,
497 ]),
498 c1: Fp::from_raw([
500 0x3870b8595eeaedcf,
501 0x33621634b114cc8c,
502 0xd96b70571d1c4d39,
503 0x3c722f182a4b7b68,
504 0xc1a582cf82cd5db3,
505 0x6609ee86ba462fa8,
506 0x032b3ee8ff17f17d,
507 ]),
508 },
509 Fp2 {
511 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 {
525 c0: Fp::from_raw([
527 0xa645a44130d96e0a,
528 0xfc68bc1e994ea997,
529 0x78c46a46ec2afcee,
530 0xfbf0ed116e0005a7,
531 0xd230567f5f2dd4f1,
532 0x88d331dbb7ec84a0,
533 0x0c14c35d63e07391,
534 ]),
535 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}