1use blst::*;
4
5use core::{
6 fmt,
7 ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign},
8};
9
10use ff::Field;
11use rand_core::RngCore;
12use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
13
14use crate::{fp::Fp, fp2::Fp2, fp6::Fp6};
15
16#[derive(Copy, Clone, PartialEq, Eq)]
18#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
19#[repr(transparent)]
20pub struct Fp12(pub(crate) blst_fp12);
21
22impl fmt::Debug for Fp12 {
23 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
24 f.debug_struct("Fp12")
25 .field("c0", &self.c0())
26 .field("c1", &self.c1())
27 .finish()
28 }
29}
30
31impl fmt::Display for Fp12 {
32 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
33 write!(f, "{:?} + {:?}*u", self.c0(), self.c1())
34 }
35}
36
37impl From<Fp> for Fp12 {
38 fn from(f: Fp) -> Fp12 {
39 Fp12::new(Fp6::from(f), Fp6::ZERO)
40 }
41}
42
43impl From<Fp2> for Fp12 {
44 fn from(f: Fp2) -> Fp12 {
45 Fp12::new(Fp6::from(f), Fp6::ZERO)
46 }
47}
48
49impl From<Fp6> for Fp12 {
50 fn from(f: Fp6) -> Fp12 {
51 Fp12::new(f, Fp6::ZERO)
52 }
53}
54
55impl From<blst_fp12> for Fp12 {
56 fn from(val: blst_fp12) -> Fp12 {
57 Fp12(val)
58 }
59}
60
61impl From<Fp12> for blst_fp12 {
62 fn from(val: Fp12) -> blst_fp12 {
63 val.0
64 }
65}
66
67impl ConstantTimeEq for Fp12 {
68 fn ct_eq(&self, other: &Self) -> Choice {
69 self.c0().ct_eq(&other.c0()) & self.c1().ct_eq(&other.c1())
70 }
71}
72
73impl ConditionallySelectable for Fp12 {
74 fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
75 Fp12(blst_fp12 {
76 fp6: [
77 Fp6::conditional_select(&a.c0(), &b.c0(), choice).0,
78 Fp6::conditional_select(&a.c1(), &b.c1(), choice).0,
79 ],
80 })
81 }
82}
83
84impl Default for Fp12 {
85 fn default() -> Self {
86 Fp12::ZERO
87 }
88}
89
90macro_rules! op {
91 ($lhs:expr, $op:expr, $rhs:expr) => {
92 unsafe {
93 $op(
94 &mut $lhs.0.fp6[0].fp2[0],
95 &$lhs.0.fp6[0].fp2[0],
96 &$rhs.0.fp6[0].fp2[0],
97 );
98 $op(
99 &mut $lhs.0.fp6[0].fp2[1],
100 &$lhs.0.fp6[0].fp2[1],
101 &$rhs.0.fp6[0].fp2[1],
102 );
103 $op(
104 &mut $lhs.0.fp6[0].fp2[2],
105 &$lhs.0.fp6[0].fp2[2],
106 &$rhs.0.fp6[0].fp2[2],
107 );
108 $op(
109 &mut $lhs.0.fp6[1].fp2[0],
110 &$lhs.0.fp6[1].fp2[0],
111 &$rhs.0.fp6[1].fp2[0],
112 );
113 $op(
114 &mut $lhs.0.fp6[1].fp2[1],
115 &$lhs.0.fp6[1].fp2[1],
116 &$rhs.0.fp6[1].fp2[1],
117 );
118 $op(
119 &mut $lhs.0.fp6[1].fp2[2],
120 &$lhs.0.fp6[1].fp2[2],
121 &$rhs.0.fp6[1].fp2[2],
122 );
123 }
124 };
125}
126
127impl Neg for &Fp12 {
128 type Output = Fp12;
129
130 #[inline]
131 fn neg(self) -> Fp12 {
132 -*self
133 }
134}
135
136impl Neg for Fp12 {
137 type Output = Fp12;
138
139 #[inline]
140 fn neg(mut self) -> Fp12 {
141 unsafe {
142 blst_fp2_cneg(&mut self.0.fp6[0].fp2[0], &self.0.fp6[0].fp2[0], true);
143 blst_fp2_cneg(&mut self.0.fp6[0].fp2[1], &self.0.fp6[0].fp2[1], true);
144 blst_fp2_cneg(&mut self.0.fp6[0].fp2[2], &self.0.fp6[0].fp2[2], true);
145 blst_fp2_cneg(&mut self.0.fp6[1].fp2[0], &self.0.fp6[1].fp2[0], true);
146 blst_fp2_cneg(&mut self.0.fp6[1].fp2[1], &self.0.fp6[1].fp2[1], true);
147 blst_fp2_cneg(&mut self.0.fp6[1].fp2[2], &self.0.fp6[1].fp2[2], true);
148 }
149 self
150 }
151}
152
153impl Sub<&Fp12> for &Fp12 {
154 type Output = Fp12;
155
156 #[inline]
157 fn sub(self, rhs: &Fp12) -> Fp12 {
158 let mut out = *self;
159 out -= rhs;
160 out
161 }
162}
163
164impl Add<&Fp12> for &Fp12 {
165 type Output = Fp12;
166
167 #[inline]
168 fn add(self, rhs: &Fp12) -> Fp12 {
169 let mut out = *self;
170 out += rhs;
171 out
172 }
173}
174
175impl Mul<&Fp12> for &Fp12 {
176 type Output = Fp12;
177
178 #[inline]
179 fn mul(self, rhs: &Fp12) -> Fp12 {
180 let mut out = blst_fp12::default();
181 unsafe { blst_fp12_mul(&mut out, &self.0, &rhs.0) };
182 Fp12(out)
183 }
184}
185
186impl AddAssign<&Fp12> for Fp12 {
187 #[inline]
188 fn add_assign(&mut self, rhs: &Fp12) {
189 op!(self, blst_fp2_add, rhs);
190 }
191}
192
193impl SubAssign<&Fp12> for Fp12 {
194 #[inline]
195 fn sub_assign(&mut self, rhs: &Fp12) {
196 op!(self, blst_fp2_sub, rhs);
197 }
198}
199
200impl MulAssign<&Fp12> for Fp12 {
201 #[inline]
202 fn mul_assign(&mut self, rhs: &Fp12) {
203 unsafe { blst_fp12_mul(&mut self.0, &self.0, &rhs.0) };
204 }
205}
206
207impl_add_sub!(Fp12);
208impl_add_sub_assign!(Fp12);
209impl_mul!(Fp12);
210impl_mul_assign!(Fp12);
211impl_sum!(Fp12);
212impl_product!(Fp12);
213
214impl Field for Fp12 {
215 fn random(mut rng: impl RngCore) -> Self {
216 Fp12::new(Fp6::random(&mut rng), Fp6::random(&mut rng))
217 }
218
219 const ZERO: Self = Fp12::new(Fp6::ZERO, Fp6::ZERO);
220
221 const ONE: Self = Fp12::new(Fp6::ONE, Fp6::ZERO);
222
223 fn is_zero(&self) -> Choice {
224 self.c0().is_zero() & self.c1().is_zero()
225 }
226
227 fn double(&self) -> Self {
228 let mut out = *self;
229 out += self;
230 out
231 }
232
233 fn square(&self) -> Self {
234 let mut sq = *self;
235 unsafe { blst_fp12_sqr(&mut sq.0, &self.0) };
236 sq
237 }
238
239 fn invert(&self) -> CtOption<Self> {
240 let is_zero = self.ct_eq(&Self::ZERO);
241 let mut inv = *self;
242 unsafe { blst_fp12_inverse(&mut inv.0, &self.0) }
243 CtOption::new(inv, !is_zero)
244 }
245
246 fn sqrt(&self) -> CtOption<Self> {
247 unimplemented!()
248 }
249
250 fn sqrt_ratio(_num: &Self, _div: &Self) -> (Choice, Self) {
251 unimplemented!()
253 }
254}
255
256impl Fp12 {
257 pub const fn new(c0: Fp6, c1: Fp6) -> Fp12 {
259 Fp12(blst_fp12 { fp6: [c0.0, c1.0] })
260 }
261
262 pub fn frobenius_map(&mut self, power: usize) {
263 if power > 0 && power < 4 {
264 unsafe { blst_fp12_frobenius_map(&mut self.0, &self.0, power) }
265 } else {
266 let mut c0 = self.c0();
267 c0.frobenius_map(power);
268 let mut c1 = self.c1();
269 c1.frobenius_map(power);
270
271 let mut c0_raw = blst_fp2::default();
272 let mut c1_raw = blst_fp2::default();
273 let mut c2_raw = blst_fp2::default();
274 unsafe {
275 blst_fp2_mul(
276 &mut c0_raw,
277 &c1.0.fp2[0],
278 &FROBENIUS_COEFF_FP12_C1[power % 12],
279 );
280 blst_fp2_mul(
281 &mut c1_raw,
282 &c1.0.fp2[1],
283 &FROBENIUS_COEFF_FP12_C1[power % 12],
284 );
285 blst_fp2_mul(
286 &mut c2_raw,
287 &c1.0.fp2[2],
288 &FROBENIUS_COEFF_FP12_C1[power % 12],
289 );
290 }
291 c1.0.fp2 = [c0_raw, c1_raw, c2_raw];
292
293 self.0.fp6 = [c0.0, c1.0];
294 }
295 }
296
297 pub fn c0(&self) -> Fp6 {
298 Fp6(self.0.fp6[0])
299 }
300
301 pub fn c1(&self) -> Fp6 {
302 Fp6(self.0.fp6[1])
303 }
304
305 pub fn conjugate(&mut self) {
306 unsafe { blst_fp12_conjugate(&mut self.0) };
307 }
308}
309
310#[cfg(feature = "gpu")]
311impl ec_gpu::GpuName for Fp12 {
312 fn name() -> String {
313 ec_gpu::name!()
314 }
315}
316
317const FROBENIUS_COEFF_FP12_C1: [blst_fp2; 12] = [
319 blst_fp2 {
321 fp: [
322 blst_fp {
323 l: [
324 0x760900000002fffd,
325 0xebf4000bc40c0002,
326 0x5f48985753c758ba,
327 0x77ce585370525745,
328 0x5c071a97a256ec6d,
329 0x15f65ec3fa80e493,
330 ],
331 },
332 blst_fp {
333 l: [0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
334 },
335 ],
336 },
337 blst_fp2 {
339 fp: [
340 blst_fp {
341 l: [
342 0x7089552b319d465,
343 0xc6695f92b50a8313,
344 0x97e83cccd117228f,
345 0xa35baecab2dc29ee,
346 0x1ce393ea5daace4d,
347 0x8f2220fb0fb66eb,
348 ],
349 },
350 blst_fp {
351 l: [
352 0xb2f66aad4ce5d646,
353 0x5842a06bfc497cec,
354 0xcf4895d42599d394,
355 0xc11b9cba40a8e8d0,
356 0x2e3813cbe5a0de89,
357 0x110eefda88847faf,
358 ],
359 },
360 ],
361 },
362 blst_fp2 {
364 fp: [
365 blst_fp {
366 l: [
367 0xecfb361b798dba3a,
368 0xc100ddb891865a2c,
369 0xec08ff1232bda8e,
370 0xd5c13cc6f1ca4721,
371 0x47222a47bf7b5c04,
372 0x110f184e51c5f59,
373 ],
374 },
375 blst_fp {
376 l: [0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
377 },
378 ],
379 },
380 blst_fp2 {
382 fp: [
383 blst_fp {
384 l: [
385 0x3e2f585da55c9ad1,
386 0x4294213d86c18183,
387 0x382844c88b623732,
388 0x92ad2afd19103e18,
389 0x1d794e4fac7cf0b9,
390 0xbd592fc7d825ec8,
391 ],
392 },
393 blst_fp {
394 l: [
395 0x7bcfa7a25aa30fda,
396 0xdc17dec12a927e7c,
397 0x2f088dd86b4ebef1,
398 0xd1ca2087da74d4a7,
399 0x2da2596696cebc1d,
400 0xe2b7eedbbfd87d2,
401 ],
402 },
403 ],
404 },
405 blst_fp2 {
407 fp: [
408 blst_fp {
409 l: [
410 0x30f1361b798a64e8,
411 0xf3b8ddab7ece5a2a,
412 0x16a8ca3ac61577f7,
413 0xc26a2ff874fd029b,
414 0x3636b76660701c6e,
415 0x51ba4ab241b6160,
416 ],
417 },
418 blst_fp {
419 l: [0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
420 },
421 ],
422 },
423 blst_fp2 {
425 fp: [
426 blst_fp {
427 l: [
428 0x3726c30af242c66c,
429 0x7c2ac1aad1b6fe70,
430 0xa04007fbba4b14a2,
431 0xef517c3266341429,
432 0x95ba654ed2226b,
433 0x2e370eccc86f7dd,
434 ],
435 },
436 blst_fp {
437 l: [
438 0x82d83cf50dbce43f,
439 0xa2813e53df9d018f,
440 0xc6f0caa53c65e181,
441 0x7525cf528d50fe95,
442 0x4a85ed50f4798a6b,
443 0x171da0fd6cf8eebd,
444 ],
445 },
446 ],
447 },
448 blst_fp2 {
450 fp: [
451 blst_fp {
452 l: [
453 0x43f5fffffffcaaae,
454 0x32b7fff2ed47fffd,
455 0x7e83a49a2e99d69,
456 0xeca8f3318332bb7a,
457 0xef148d1ea0f4c069,
458 0x40ab3263eff0206,
459 ],
460 },
461 blst_fp {
462 l: [0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
463 },
464 ],
465 },
466 blst_fp2 {
468 fp: [
469 blst_fp {
470 l: [
471 0xb2f66aad4ce5d646,
472 0x5842a06bfc497cec,
473 0xcf4895d42599d394,
474 0xc11b9cba40a8e8d0,
475 0x2e3813cbe5a0de89,
476 0x110eefda88847faf,
477 ],
478 },
479 blst_fp {
480 l: [
481 0x7089552b319d465,
482 0xc6695f92b50a8313,
483 0x97e83cccd117228f,
484 0xa35baecab2dc29ee,
485 0x1ce393ea5daace4d,
486 0x8f2220fb0fb66eb,
487 ],
488 },
489 ],
490 },
491 blst_fp2 {
493 fp: [
494 blst_fp {
495 l: [
496 0xcd03c9e48671f071,
497 0x5dab22461fcda5d2,
498 0x587042afd3851b95,
499 0x8eb60ebe01bacb9e,
500 0x3f97d6e83d050d2,
501 0x18f0206554638741,
502 ],
503 },
504 blst_fp {
505 l: [0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
506 },
507 ],
508 },
509 blst_fp2 {
511 fp: [
512 blst_fp {
513 l: [
514 0x7bcfa7a25aa30fda,
515 0xdc17dec12a927e7c,
516 0x2f088dd86b4ebef1,
517 0xd1ca2087da74d4a7,
518 0x2da2596696cebc1d,
519 0xe2b7eedbbfd87d2,
520 ],
521 },
522 blst_fp {
523 l: [
524 0x3e2f585da55c9ad1,
525 0x4294213d86c18183,
526 0x382844c88b623732,
527 0x92ad2afd19103e18,
528 0x1d794e4fac7cf0b9,
529 0xbd592fc7d825ec8,
530 ],
531 },
532 ],
533 },
534 blst_fp2 {
536 fp: [
537 blst_fp {
538 l: [
539 0x890dc9e4867545c3,
540 0x2af322533285a5d5,
541 0x50880866309b7e2c,
542 0xa20d1b8c7e881024,
543 0x14e4f04fe2db9068,
544 0x14e56d3f1564853a,
545 ],
546 },
547 blst_fp {
548 l: [0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
549 },
550 ],
551 },
552 blst_fp2 {
554 fp: [
555 blst_fp {
556 l: [
557 0x82d83cf50dbce43f,
558 0xa2813e53df9d018f,
559 0xc6f0caa53c65e181,
560 0x7525cf528d50fe95,
561 0x4a85ed50f4798a6b,
562 0x171da0fd6cf8eebd,
563 ],
564 },
565 blst_fp {
566 l: [
567 0x3726c30af242c66c,
568 0x7c2ac1aad1b6fe70,
569 0xa04007fbba4b14a2,
570 0xef517c3266341429,
571 0x95ba654ed2226b,
572 0x2e370eccc86f7dd,
573 ],
574 },
575 ],
576 },
577];
578
579#[cfg(test)]
580mod tests {
581 use super::*;
582
583 use rand_core::SeedableRng;
584 use rand_xorshift::XorShiftRng;
585
586 #[test]
587 fn test_fp12_eq() {
588 assert_eq!(Fp12::ONE, Fp12::ONE);
589 assert_eq!(Fp12::ZERO, Fp12::ZERO);
590 assert_ne!(Fp12::ZERO, Fp12::ONE);
591 }
592
593 #[test]
594 fn fp12_random_frobenius_tests() {
595 use std::convert::TryFrom;
596
597 let mut rng = XorShiftRng::from_seed([
598 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
599 0xbc, 0xe5,
600 ]);
601
602 let characteristic: Vec<u64> = Fp::char()
603 .chunks(8)
604 .map(|chunk| u64::from_le_bytes(<[u8; 8]>::try_from(chunk).unwrap()))
605 .collect();
606
607 let maxpower = 13;
608
609 for _ in 0..100 {
610 for i in 0..(maxpower + 1) {
611 let mut a = Fp12::random(&mut rng);
612 let mut b = a;
613
614 for _ in 0..i {
615 a = a.pow_vartime(&characteristic);
616 }
617 b.frobenius_map(i);
618
619 assert_eq!(a, b, "{}", i);
620 }
621 }
622 }
623
624 #[test]
625 fn fp12_random_field_tests() {
626 crate::tests::field::random_field_tests::<Fp12>();
627 }
628}