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