halo2curves_axiom/pluto_eris/
engine.rs

1#![allow(clippy::suspicious_arithmetic_impl)]
2use crate::ff::{Field, PrimeField};
3use crate::group::cofactor::CofactorCurveAffine;
4use crate::group::Group;
5use crate::pairing::{Engine, MillerLoopResult, MultiMillerLoop, PairingCurveAffine};
6use crate::pluto_eris::curve::*;
7use crate::pluto_eris::fields::fp::*;
8use crate::pluto_eris::fields::fp12::*;
9use crate::pluto_eris::fields::fp2::*;
10use crate::pluto_eris::fields::fp6::FROBENIUS_COEFF_FP6_C1;
11use crate::pluto_eris::fields::fq::*;
12use core::borrow::Borrow;
13use core::iter::Sum;
14use core::ops::{Add, Mul, MulAssign, Neg, Sub};
15use rand_core::RngCore;
16use subtle::{Choice, ConditionallySelectable, ConstantTimeEq};
17
18/// Adaptation of Algorithm 1, https://eprint.iacr.org/2013/722.pdf
19/// the parameter for the curve Pluto: u = -0x4000000000001000008780000000
20const NEG_PLUTO_U: u128 = 0x4000000000001000008780000000;
21
22const NEG_SIX_U_PLUS_2_NAF: [i8; 114] = [
23    0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
24    0, 1, 0, -1, 0, -1, 0, 1, 0, -1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
25    -1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
26    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 1,
27];
28
29/// Value of (57/(u + 3))^((p - 1)/2) where u^2 + 5 = 0 in Fp2.
30const XI_TO_P_MINUS_1_OVER_2: Fp2 = Fp2 {
31    c0: Fp::from_raw([
32        0x54cf5ad1c0926216,
33        0x186c1f3ce4a46d4e,
34        0x9c23800ce9c9452f,
35        0x50e0d09ff6d6c08b,
36        0x7cf421e4d46f6666,
37        0x678664ba4b6d8343,
38        0x21cc26d5de0f80f4,
39    ]),
40
41    c1: Fp::from_raw([
42        0xc0505f4c260e91f4,
43        0xe7bbd15f10723657,
44        0xb4b3e0c35358097e,
45        0x87c56f42a558750d,
46        0x4b7211d23f34f0ae,
47        0xf6839d29e2f0d250,
48        0x16ebe8b2e12a1106,
49    ]),
50};
51
52impl PairingCurveAffine for G1Affine {
53    type Pair = G2Affine;
54    type PairingResult = Gt;
55
56    fn pairing_with(&self, other: &Self::Pair) -> Self::PairingResult {
57        Pluto::pairing(self, other)
58    }
59}
60
61impl PairingCurveAffine for G2Affine {
62    type Pair = G1Affine;
63    type PairingResult = Gt;
64
65    fn pairing_with(&self, other: &Self::Pair) -> Self::PairingResult {
66        Pluto::pairing(other, self)
67    }
68}
69
70#[derive(Copy, Clone, Debug, Default)]
71pub struct Gt(pub(crate) Fp12);
72
73impl std::fmt::Display for Gt {
74    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
75        write!(f, "{:?}", self)
76    }
77}
78
79impl ConstantTimeEq for Gt {
80    fn ct_eq(&self, other: &Self) -> Choice {
81        self.0.ct_eq(&other.0)
82    }
83}
84
85impl ConditionallySelectable for Gt {
86    fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
87        Gt(Fp12::conditional_select(&a.0, &b.0, choice))
88    }
89}
90
91impl Eq for Gt {}
92impl PartialEq for Gt {
93    #[inline]
94    fn eq(&self, other: &Self) -> bool {
95        bool::from(self.ct_eq(other))
96    }
97}
98
99impl Gt {
100    /// Returns the group identity, which is $1$.
101    pub const fn identity() -> Gt {
102        Gt(Fp12::ONE)
103    }
104
105    /// Doubles this group element.
106    pub fn double(&self) -> Gt {
107        Gt(self.0.square())
108    }
109}
110
111impl<'a> Neg for &'a Gt {
112    type Output = Gt;
113
114    #[inline]
115    fn neg(self) -> Gt {
116        // The element is unitary, so we just conjugate.
117        let mut u = self.0;
118        u.conjugate();
119        Gt(u)
120    }
121}
122
123impl Neg for Gt {
124    type Output = Gt;
125
126    #[inline]
127    fn neg(self) -> Gt {
128        -&self
129    }
130}
131
132impl<'a, 'b> Add<&'b Gt> for &'a Gt {
133    type Output = Gt;
134
135    #[inline]
136    fn add(self, rhs: &'b Gt) -> Gt {
137        Gt(self.0 * rhs.0)
138    }
139}
140
141impl<'a, 'b> Sub<&'b Gt> for &'a Gt {
142    type Output = Gt;
143
144    #[inline]
145    fn sub(self, rhs: &'b Gt) -> Gt {
146        self + (-rhs)
147    }
148}
149
150impl<'a, 'b> Mul<&'b Fq> for &'a Gt {
151    type Output = Gt;
152
153    fn mul(self, other: &'b Fq) -> Self::Output {
154        let mut acc = Gt::identity();
155
156        for bit in other
157            .to_repr()
158            .iter()
159            .rev()
160            .flat_map(|byte| (0..8).rev().map(move |i| Choice::from((byte >> i) & 1u8)))
161            .skip(1)
162        {
163            acc = acc.double();
164            acc = Gt::conditional_select(&acc, &(acc + self), bit);
165        }
166
167        acc
168    }
169}
170
171use crate::{
172    impl_add_binop_specify_output, impl_binops_additive, impl_binops_additive_specify_output,
173    impl_binops_multiplicative, impl_binops_multiplicative_mixed, impl_sub_binop_specify_output,
174};
175impl_binops_additive!(Gt, Gt);
176impl_binops_multiplicative!(Gt, Fq);
177
178impl<T> Sum<T> for Gt
179where
180    T: Borrow<Gt>,
181{
182    fn sum<I>(iter: I) -> Self
183    where
184        I: Iterator<Item = T>,
185    {
186        iter.fold(Self::identity(), |acc, item| acc + item.borrow())
187    }
188}
189
190impl Group for Gt {
191    type Scalar = Fq;
192
193    fn random(_: impl RngCore) -> Self {
194        unimplemented!();
195    }
196
197    fn identity() -> Self {
198        Self::identity()
199    }
200
201    fn generator() -> Self {
202        unimplemented!();
203    }
204
205    fn is_identity(&self) -> Choice {
206        self.ct_eq(&Self::identity())
207    }
208
209    #[must_use]
210    fn double(&self) -> Self {
211        self.double()
212    }
213}
214
215/// Points of G2 in Jacobian coordinates.
216/// These are points lie in the twisted curve E'(Fp2).
217#[derive(Clone, Debug)]
218pub struct G2Prepared {
219    pub(crate) coeffs: Vec<(Fp2, Fp2, Fp2)>,
220    pub(crate) infinity: bool,
221}
222
223impl G2Prepared {
224    /// Returns true if `self` is the infinity point.
225    pub fn is_zero(&self) -> bool {
226        self.infinity
227    }
228
229    /// Prepares a G2 point in affine coordinates.
230    pub fn from_affine(q: G2Affine) -> Self {
231        if bool::from(q.is_identity()) {
232            return G2Prepared {
233                coeffs: vec![],
234                infinity: true,
235            };
236        }
237
238        /// Adaptation of Algorithm 26, https://eprint.iacr.org/2010/354.pdf
239        fn doubling_step(r: &mut G2) -> (Fp2, Fp2, Fp2) {
240            let mut tmp0 = r.x;
241            tmp0.square_assign();
242
243            let mut tmp1 = r.y;
244            tmp1.square_assign();
245
246            let mut tmp2 = tmp1;
247            tmp2.square_assign();
248
249            let mut tmp3 = tmp1;
250            tmp3 += &r.x;
251            tmp3.square_assign();
252            tmp3 -= &tmp0;
253            tmp3 -= &tmp2;
254            tmp3.double_assign();
255
256            let mut tmp4 = tmp0;
257            tmp4.double_assign();
258            tmp4 += &tmp0;
259
260            let mut tmp6 = r.x;
261            tmp6 += &tmp4;
262
263            let mut tmp5 = tmp4;
264            tmp5.square_assign();
265
266            let mut zsquared = r.z;
267            zsquared.square_assign();
268
269            r.x = tmp5;
270            r.x -= &tmp3;
271            r.x -= &tmp3;
272
273            r.z += &r.y;
274            r.z.square_assign();
275            r.z -= &tmp1;
276            r.z -= &zsquared;
277
278            r.y = tmp3;
279            r.y -= &r.x;
280            r.y.mul_assign(&tmp4);
281
282            tmp2.double_assign();
283            tmp2.double_assign();
284            tmp2.double_assign();
285
286            r.y -= &tmp2;
287
288            // up to here everything was by algorith, line 11
289            // use R instead of new T
290
291            // tmp3 is the first part of line 12
292            tmp3 = tmp4;
293            tmp3.mul_assign(&zsquared);
294            tmp3.double_assign();
295            tmp3 = tmp3.neg();
296
297            // tmp6 is from line 14
298            tmp6.square_assign();
299            tmp6 -= &tmp0;
300            tmp6 -= &tmp5;
301
302            tmp1.double_assign();
303            tmp1.double_assign();
304
305            tmp6 -= &tmp1;
306
307            // tmp0 is the first part of line 16
308            tmp0 = r.z;
309            tmp0.mul_assign(&zsquared);
310            tmp0.double_assign();
311
312            (tmp0, tmp3, tmp6)
313        }
314
315        // Adaptation of Algorithm 27, https://eprint.iacr.org/2010/354.pdf
316        fn addition_step(r: &mut G2, q: &G2Affine) -> (Fp2, Fp2, Fp2) {
317            let mut zsquared = r.z;
318            zsquared.square_assign();
319
320            let mut ysquared = q.y;
321            ysquared.square_assign();
322
323            // t0 corresponds to line 1
324            let mut t0 = zsquared;
325            t0.mul_assign(&q.x);
326
327            // t1 corresponds to lines 2 and 3
328            let mut t1 = q.y;
329            t1 += &r.z;
330            t1.square_assign();
331            t1 -= &ysquared;
332            t1 -= &zsquared;
333            t1.mul_assign(&zsquared);
334
335            // t2 corresponds to line 4
336            let mut t2 = t0;
337            t2 -= &r.x;
338
339            // t3 corresponds to line 5
340            let mut t3 = t2;
341            t3.square_assign();
342
343            // t4 corresponds to line 6
344            let mut t4 = t3;
345            t4.double_assign();
346            t4.double_assign();
347
348            // t5 corresponds to line 7
349            let mut t5 = t4;
350            t5.mul_assign(&t2);
351
352            // t6 corresponds to line 8
353            let mut t6 = t1;
354            t6 -= &r.y;
355            t6 -= &r.y;
356
357            // t9 corresponds to line 9
358            let mut t9 = t6;
359            t9.mul_assign(&q.x);
360
361            // corresponds to line 10
362            let mut t7 = t4;
363            t7.mul_assign(&r.x);
364
365            // corresponds to line 11, but assigns to r.x instead of T.x
366            r.x = t6;
367            r.x.square_assign();
368            r.x -= &t5;
369            r.x -= &t7;
370            r.x -= &t7;
371
372            // corresponds to line 12, but assigns to r.z instead of T.z
373            r.z += &t2;
374            r.z.square_assign();
375            r.z -= &zsquared;
376            r.z -= &t3;
377
378            // corresponds to line 13
379            let mut t10 = q.y;
380            t10 += &r.z;
381
382            // corresponds to line 14
383            let mut t8 = t7;
384            t8 -= &r.x;
385            t8.mul_assign(&t6);
386
387            // corresponds to line 15
388            t0 = r.y;
389            t0.mul_assign(&t5);
390            t0.double_assign();
391
392            // corresponds to line 12, but assigns to r.y instead of T.y
393            r.y = t8;
394            r.y -= &t0;
395
396            // corresponds to line 17
397            t10.square_assign();
398            t10 -= &ysquared;
399
400            let mut ztsquared = r.z;
401            ztsquared.square_assign();
402
403            t10 -= &ztsquared;
404
405            // corresponds to line 18
406            t9.double_assign();
407            t9 -= &t10;
408
409            // t10 = 2*Zt from Algo 27, line 19
410            t10 = r.z;
411            t10.double_assign();
412
413            // t1 = first multiplicator of line 21
414            t6 = t6.neg();
415
416            t1 = t6;
417            t1.double_assign();
418
419            // t9 corresponds to t9 from Algo 27
420            (t10, t1, t9)
421        }
422
423        let mut coeffs = vec![];
424        let mut r: G2 = q.into();
425
426        let mut negq = q;
427        negq = -negq;
428
429        coeffs.push(doubling_step(&mut r));
430
431        let last_position = NEG_SIX_U_PLUS_2_NAF.len() - 2;
432        match NEG_SIX_U_PLUS_2_NAF[last_position] {
433            1 => {
434                coeffs.push(addition_step(&mut r, &q));
435            }
436            -1 => {
437                coeffs.push(addition_step(&mut r, &negq));
438            }
439            _ => (),
440        }
441
442        for i in (0..last_position).rev() {
443            coeffs.push(doubling_step(&mut r));
444
445            match NEG_SIX_U_PLUS_2_NAF[i] {
446                1 => {
447                    coeffs.push(addition_step(&mut r, &q));
448                }
449                -1 => {
450                    coeffs.push(addition_step(&mut r, &negq));
451                }
452                _ => continue,
453            }
454        }
455
456        let mut neg_r = r;
457        neg_r = -neg_r;
458
459        let mut q1 = q;
460
461        q1.x.c1 = q1.x.c1.neg();
462        q1.x.mul_assign(&FROBENIUS_COEFF_FP6_C1[1]);
463
464        q1.y.c1 = q1.y.c1.neg();
465        q1.y.mul_assign(&XI_TO_P_MINUS_1_OVER_2);
466
467        coeffs.push(addition_step(&mut neg_r, &q1));
468
469        let mut minusq2 = q;
470        minusq2.x.mul_assign(&FROBENIUS_COEFF_FP6_C1[2]);
471
472        coeffs.push(addition_step(&mut neg_r, &minusq2));
473
474        G2Prepared {
475            coeffs,
476            infinity: false,
477        }
478    }
479}
480
481impl From<G2Affine> for G2Prepared {
482    fn from(q: G2Affine) -> G2Prepared {
483        G2Prepared::from_affine(q)
484    }
485}
486
487impl MillerLoopResult for Gt {
488    type Gt = Self;
489    fn final_exponentiation(&self) -> Gt {
490        fn exp_by_x(f: &mut Fp12) {
491            let x = NEG_PLUTO_U;
492            let mut res = Fp12::ONE;
493            for i in (0..111).rev() {
494                res.cyclotomic_square();
495                if ((x >> i) & 1) == 1 {
496                    res.mul_assign(f);
497                }
498            }
499            res.conjugate();
500            *f = res;
501        }
502
503        let r = self.0;
504        let mut f1 = self.0;
505        f1.conjugate();
506
507        Gt(r.invert()
508            .map(|mut f2| {
509                let mut r = f1;
510                r.mul_assign(&f2);
511                f2 = r;
512                r.frobenius_map(2);
513                r.mul_assign(&f2);
514
515                let mut fp = r;
516                fp.frobenius_map(1);
517
518                let mut fp2 = r;
519                fp2.frobenius_map(2);
520                let mut fp3 = fp2;
521                fp3.frobenius_map(1);
522
523                let mut fu = r;
524                exp_by_x(&mut fu);
525
526                let mut fu2 = fu;
527                exp_by_x(&mut fu2);
528
529                let mut fu3 = fu2;
530                exp_by_x(&mut fu3);
531
532                let mut y3 = fu;
533                y3.frobenius_map(1);
534
535                let mut fu2p = fu2;
536                fu2p.frobenius_map(1);
537
538                let mut fu3p = fu3;
539                fu3p.frobenius_map(1);
540
541                let mut y2 = fu2;
542                y2.frobenius_map(2);
543
544                let mut y0 = fp;
545                y0.mul_assign(&fp2);
546                y0.mul_assign(&fp3);
547
548                let mut y1 = r;
549                y1.conjugate();
550
551                let mut y5 = fu2;
552                y5.conjugate();
553
554                y3.conjugate();
555
556                let mut y4 = fu;
557                y4.mul_assign(&fu2p);
558                y4.conjugate();
559
560                let mut y6 = fu3;
561                y6.mul_assign(&fu3p);
562                y6.conjugate();
563
564                y6.cyclotomic_square();
565                y6.mul_assign(&y4);
566                y6.mul_assign(&y5);
567
568                let mut t1 = y3;
569                t1.mul_assign(&y5);
570                t1.mul_assign(&y6);
571
572                y6.mul_assign(&y2);
573
574                t1.cyclotomic_square();
575                t1.mul_assign(&y6);
576                t1.cyclotomic_square();
577
578                let mut t0 = t1;
579                t0.mul_assign(&y1);
580
581                t1.mul_assign(&y0);
582
583                t0.cyclotomic_square();
584                t0.mul_assign(&t1);
585
586                t0
587            })
588            .unwrap())
589    }
590}
591impl MultiMillerLoop for Pluto {
592    /// The prepared form of `Self::G2Affine`.
593    type G2Prepared = G2Prepared;
594
595    /// The type returned by `Engine::miller_loop`.
596    type Result = Gt;
597    fn multi_miller_loop(terms: &[(&G1Affine, &G2Prepared)]) -> Self::Result {
598        let mut pairs = vec![];
599        for &(p, q) in terms {
600            if !bool::from(p.is_identity()) && !q.is_zero() {
601                pairs.push((p, q.coeffs.iter()));
602            }
603        }
604
605        // Final steps of the line function on prepared coefficients
606        fn ell(f: &mut Fp12, coeffs: &(Fp2, Fp2, Fp2), p: &G1Affine) {
607            let mut c0 = coeffs.0;
608            let mut c1 = coeffs.1;
609
610            c0.c0.mul_assign(&p.y);
611            c0.c1.mul_assign(&p.y);
612
613            c1.c0.mul_assign(&p.x);
614            c1.c1.mul_assign(&p.x);
615
616            // Sparse multiplication in Fq12
617            f.mul_by_034(&c0, &c1, &coeffs.2);
618        }
619
620        let mut f = Fp12::ONE;
621
622        for &mut (p, ref mut coeffs) in &mut pairs {
623            ell(&mut f, coeffs.next().unwrap(), p);
624        }
625
626        // length - 2
627        let len_min2 = NEG_SIX_U_PLUS_2_NAF.len() - 2;
628
629        if NEG_SIX_U_PLUS_2_NAF[len_min2] != 0 {
630            for &mut (p, ref mut coeffs) in &mut pairs {
631                ell(&mut f, coeffs.next().unwrap(), p);
632            }
633        }
634
635        for x in NEG_SIX_U_PLUS_2_NAF[..len_min2].iter().rev() {
636            f.square_assign();
637
638            for &mut (p, ref mut coeffs) in &mut pairs {
639                ell(&mut f, coeffs.next().unwrap(), p);
640            }
641            if *x != 0 {
642                for &mut (p, ref mut coeffs) in &mut pairs {
643                    ell(&mut f, coeffs.next().unwrap(), p);
644                }
645            }
646        }
647
648        f.conjugate();
649
650        for &mut (p, ref mut coeffs) in &mut pairs {
651            ell(&mut f, coeffs.next().unwrap(), p);
652        }
653
654        for &mut (p, ref mut coeffs) in &mut pairs {
655            ell(&mut f, coeffs.next().unwrap(), p);
656        }
657
658        for &mut (_p, ref mut coeffs) in &mut pairs {
659            assert_eq!(coeffs.next(), None);
660        }
661
662        Gt(f)
663    }
664}
665
666/// Pluto pairing-friendly curve. See: <https://github.com/daira/pluto-eris>
667#[derive(Clone, Debug)]
668pub struct Pluto;
669
670impl Engine for Pluto {
671    type Fr = Fq;
672    type G1 = G1;
673    type G1Affine = G1Affine;
674    type G2 = G2;
675    type G2Affine = G2Affine;
676    type Gt = Gt;
677
678    fn pairing(p: &Self::G1Affine, q: &Self::G2Affine) -> Self::Gt {
679        let q = G2Prepared::from_affine(*q);
680        let terms: &[(&G1Affine, &G2Prepared)] = &[(p, &q)];
681        let u = Self::multi_miller_loop(terms);
682        u.final_exponentiation()
683    }
684}
685
686#[cfg(test)]
687use rand::SeedableRng;
688#[cfg(test)]
689use rand_xorshift::XorShiftRng;
690
691#[test]
692fn test_pairing() {
693    let g1 = G1::generator();
694    let mut g2 = G2::generator();
695    g2 = g2.double();
696    let pair12 = Pluto::pairing(&G1Affine::from(g1), &G2Affine::from(g2));
697
698    let mut g1 = G1::generator();
699    let g2 = G2::generator();
700    g1 = g1.double();
701    let pair21 = Pluto::pairing(&G1Affine::from(g1), &G2Affine::from(g2));
702
703    assert_eq!(pair12, pair21);
704
705    let g1 = G1::generator();
706    let mut g2 = G2::generator();
707    g2 = g2.double().double();
708    let pair12 = Pluto::pairing(&G1Affine::from(g1), &G2Affine::from(g2));
709
710    let mut g1 = G1::generator();
711    let mut g2 = G2::generator();
712    g1 = g1.double();
713    g2 = g2.double();
714    let pair21 = Pluto::pairing(&G1Affine::from(g1), &G2Affine::from(g2));
715
716    assert_eq!(pair12, pair21);
717
718    let mut rng = XorShiftRng::from_seed([
719        0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
720        0xe5,
721    ]);
722    for _ in 0..100 {
723        let a = Fq::random(&mut rng);
724        let b = Fq::random(&mut rng);
725
726        let mut g1 = G1::generator();
727        g1.mul_assign(a);
728
729        let mut g2 = G2::generator();
730        g1.mul_assign(b);
731
732        let pair_ab = Pluto::pairing(&G1Affine::from(g1), &G2Affine::from(g2));
733
734        g1 = G1::generator();
735        g1.mul_assign(b);
736
737        g2 = G2::generator();
738        g1.mul_assign(a);
739
740        let pair_ba = Pluto::pairing(&G1Affine::from(g1), &G2Affine::from(g2));
741
742        assert_eq!(pair_ab, pair_ba);
743    }
744}
745
746#[test]
747fn tricking_miller_loop_result() {
748    assert_eq!(
749        Pluto::multi_miller_loop(&[(&G1Affine::identity(), &G2Affine::generator().into())]).0,
750        Fp12::one()
751    );
752    assert_eq!(
753        Pluto::multi_miller_loop(&[(&G1Affine::generator(), &G2Affine::identity().into())]).0,
754        Fp12::one()
755    );
756    assert_ne!(
757        Pluto::multi_miller_loop(&[
758            (&G1Affine::generator(), &G2Affine::generator().into()),
759            (&-G1Affine::generator(), &G2Affine::generator().into())
760        ])
761        .0,
762        Fp12::one()
763    );
764    assert_eq!(
765        Pluto::multi_miller_loop(&[
766            (&G1Affine::generator(), &G2Affine::generator().into()),
767            (&-G1Affine::generator(), &G2Affine::generator().into())
768        ])
769        .final_exponentiation(),
770        Gt::identity()
771    );
772}
773
774#[test]
775fn random_bilinearity_tests() {
776    let mut rng = XorShiftRng::from_seed([
777        0x55, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
778        0xe5,
779    ]);
780
781    for _ in 0..10 {
782        let mut a = G1::generator();
783        let ka = Fq::random(&mut rng);
784        a.mul_assign(ka);
785
786        let mut b = G2::generator();
787        let kb = Fq::random(&mut rng);
788        b.mul_assign(kb);
789
790        let c = Fq::random(&mut rng);
791        let d = Fq::random(&mut rng);
792
793        let mut ac = a;
794        ac.mul_assign(c);
795
796        let mut ad = a;
797        ad.mul_assign(d);
798
799        let mut bc = b;
800        bc.mul_assign(c);
801
802        let mut bd = b;
803        bd.mul_assign(d);
804
805        let acbd = Pluto::pairing(&G1Affine::from(ac), &G2Affine::from(bd));
806        let adbc = Pluto::pairing(&G1Affine::from(ad), &G2Affine::from(bc));
807
808        let mut cd = c;
809        cd.mul_assign(&d);
810
811        cd *= Fq([1, 0, 0, 0, 0, 0, 0]);
812
813        let abcd = Gt(Pluto::pairing(&G1Affine::from(a), &G2Affine::from(b))
814            .0
815            .pow_vartime(cd.0));
816
817        assert_eq!(acbd, adbc);
818        assert_eq!(acbd, abcd);
819    }
820}
821
822#[test]
823pub fn engine_tests() {
824    let mut rng = XorShiftRng::from_seed([
825        0x56, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
826        0xe5,
827    ]);
828
829    for _ in 0..10 {
830        let a = G1Affine::from(G1::random(&mut rng));
831        let b = G2Affine::from(G2::random(&mut rng));
832
833        assert!(a.pairing_with(&b) == b.pairing_with(&a));
834        assert!(a.pairing_with(&b) == Pluto::pairing(&a, &b));
835    }
836
837    for _ in 0..10 {
838        let z1 = G1Affine::identity();
839        let z2 = G2Prepared::from(G2Affine::identity());
840
841        let a = G1Affine::from(G1::random(&mut rng));
842        let b = G2Prepared::from(G2Affine::from(G2::random(&mut rng)));
843        let c = G1Affine::from(G1::random(&mut rng));
844        let d = G2Prepared::from(G2Affine::from(G2::random(&mut rng)));
845
846        assert_eq!(
847            Fp12::ONE,
848            Pluto::multi_miller_loop(&[(&z1, &b)])
849                .final_exponentiation()
850                .0,
851        );
852
853        assert_eq!(
854            Fp12::ONE,
855            Pluto::multi_miller_loop(&[(&a, &z2)])
856                .final_exponentiation()
857                .0,
858        );
859
860        assert_eq!(
861            Pluto::multi_miller_loop(&[(&z1, &b), (&c, &d)]).final_exponentiation(),
862            Pluto::multi_miller_loop(&[(&a, &z2), (&c, &d)]).final_exponentiation(),
863        );
864
865        assert_eq!(
866            Pluto::multi_miller_loop(&[(&a, &b), (&z1, &d)]).final_exponentiation(),
867            Pluto::multi_miller_loop(&[(&a, &b), (&c, &z2)]).final_exponentiation(),
868        );
869    }
870}
871
872#[test]
873fn random_miller_loop_tests() {
874    let mut rng = XorShiftRng::from_seed([
875        0x58, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
876        0xe5,
877    ]);
878
879    // Exercise a double miller loop
880    for _ in 0..10 {
881        let a = G1Affine::from(G1::random(&mut rng));
882        let b = G2Affine::from(G2::random(&mut rng));
883        let c = G1Affine::from(G1::random(&mut rng));
884        let d = G2Affine::from(G2::random(&mut rng));
885
886        let ab = Pluto::pairing(&a, &b);
887        let cd = Pluto::pairing(&c, &d);
888
889        let mut abcd = ab;
890        abcd = Gt(abcd.0 * cd.0);
891
892        let b = G2Prepared::from(b);
893        let d = G2Prepared::from(d);
894
895        let abcd_with_double_loop =
896            Pluto::multi_miller_loop(&[(&a, &b), (&c, &d)]).final_exponentiation();
897
898        assert_eq!(abcd, abcd_with_double_loop);
899    }
900}
901
902#[test]
903pub fn multi_miller_final_exp_tests() {
904    let g1 = G1::generator();
905    let g2 = G2::generator();
906
907    let mut rng = XorShiftRng::from_seed([
908        0x56, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
909        0xe5,
910    ]);
911
912    for _ in 0..10 {
913        let s = Fq::random(&mut rng);
914
915        let mut s_g1 = g1;
916        s_g1.mul_assign(s);
917
918        let mut s_g2 = g2;
919        s_g2.mul_assign(s);
920
921        let s_g2_prepared = G2Prepared::from(G2Affine::from(s_g2));
922        let g2_prepared = G2Prepared::from(G2Affine::from(g2));
923
924        let (term_1, term_2) = (
925            (&G1Affine::from(g1), &s_g2_prepared),
926            (&-G1Affine::from(s_g1), &g2_prepared),
927        );
928
929        let terms = &[term_1, term_2];
930
931        assert!(
932            bool::from(
933                Pluto::multi_miller_loop(&terms[..])
934                    .final_exponentiation()
935                    .is_identity(),
936            ),
937            "trivial pairing check failed"
938        );
939
940        let lhs = Pluto::pairing(&G1Affine::from(g1), &G2Affine::from(s_g2));
941        let rhs = Pluto::pairing(&G1Affine::from(s_g1), &G2Affine::from(g2));
942
943        assert_eq!(lhs, rhs, "failed trivial check");
944    }
945}