Skip to main content

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};
13use zeroize::Zeroize;
14
15use crate::{fp::Fp, fp2::Fp2, fp6::Fp6};
16
17/// This represents an element $c_0 + c_1 w$ of $\mathbb{F}_{p^12} = \mathbb{F}_{p^6} / w^2 - v$.
18#[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        // ff::helpers::sqrt_ratio_generic(num, div)
253        unimplemented!()
254    }
255}
256
257impl Fp12 {
258    /// Constructs an element of `Fp12`.
259    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
318// non_residue^((modulus^i-1)/6) for i=0,...,11
319const FROBENIUS_COEFF_FP12_C1: [blst_fp2; 12] = [
320    // Fp2(u + 1)**(((q^0) - 1) / 6)
321    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    // Fp2(u + 1)**(((q^1) - 1) / 6)
339    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    // Fp2(u + 1)**(((q^2) - 1) / 6)
364    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    // Fp2(u + 1)**(((q^3) - 1) / 6)
382    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    // Fp2(u + 1)**(((q^4) - 1) / 6)
407    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    // Fp2(u + 1)**(((q^5) - 1) / 6)
425    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    // Fp2(u + 1)**(((q^6) - 1) / 6)
450    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    // Fp2(u + 1)**(((q^7) - 1) / 6)
468    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    // Fp2(u + 1)**(((q^8) - 1) / 6)
493    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    // Fp2(u + 1)**(((q^9) - 1) / 6)
511    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    // Fp2(u + 1)**(((q^10) - 1) / 6)
536    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    // Fp2(u + 1)**(((q^11) - 1) / 6)
554    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}