nam_blstrs/
fp12.rs

1//! This module implements arithmetic over the quadratic extension field Fp12.
2
3use 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/// This represents an element $c_0 + c_1 w$ of $\mathbb{F}_{p^12} = \mathbb{F}_{p^6} / w^2 - v$.
17#[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        // ff::helpers::sqrt_ratio_generic(num, div)
252        unimplemented!()
253    }
254}
255
256impl Fp12 {
257    /// Constructs an element of `Fp12`.
258    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
317// non_residue^((modulus^i-1)/6) for i=0,...,11
318const FROBENIUS_COEFF_FP12_C1: [blst_fp2; 12] = [
319    // Fp2(u + 1)**(((q^0) - 1) / 6)
320    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    // Fp2(u + 1)**(((q^1) - 1) / 6)
338    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    // Fp2(u + 1)**(((q^2) - 1) / 6)
363    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    // Fp2(u + 1)**(((q^3) - 1) / 6)
381    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    // Fp2(u + 1)**(((q^4) - 1) / 6)
406    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    // Fp2(u + 1)**(((q^5) - 1) / 6)
424    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    // Fp2(u + 1)**(((q^6) - 1) / 6)
449    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    // Fp2(u + 1)**(((q^7) - 1) / 6)
467    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    // Fp2(u + 1)**(((q^8) - 1) / 6)
492    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    // Fp2(u + 1)**(((q^9) - 1) / 6)
510    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    // Fp2(u + 1)**(((q^10) - 1) / 6)
535    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    // Fp2(u + 1)**(((q^11) - 1) / 6)
553    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}