Skip to main content

nam_blstrs/
g2.rs

1//! An implementation of the $\mathbb{G}_2$ group of BLS12-381.
2
3use core::{
4    borrow::Borrow,
5    fmt,
6    iter::Sum,
7    ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign},
8};
9
10use blst::*;
11use group::{
12    Curve, Group, GroupEncoding, UncompressedEncoding, WnafGroup,
13    prime::{PrimeCurve, PrimeCurveAffine, PrimeGroup},
14};
15use rand_core::RngCore;
16use subtle::{Choice, ConditionallySelectable, CtOption};
17use zeroize::Zeroize;
18
19use crate::{Bls12, Engine, G1Affine, Gt, PairingCurveAffine, Scalar, fp2::Fp2};
20
21/// This is an element of $\mathbb{G}_2$ represented in the affine coordinate space.
22/// It is ideal to keep elements in this representation to reduce memory usage and
23/// improve performance through the use of mixed curve model arithmetic.
24#[derive(Copy, Clone, Zeroize)]
25#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
26#[repr(transparent)]
27pub struct G2Affine(pub(crate) blst_p2_affine);
28
29const COMPRESSED_SIZE: usize = 96;
30const UNCOMPRESSED_SIZE: usize = 192;
31
32impl fmt::Debug for G2Affine {
33    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
34        let is_ident: bool = self.is_identity().into();
35        f.debug_struct("G2Affine")
36            .field("x", &self.x())
37            .field("y", &self.y())
38            .field("infinity", &is_ident)
39            .finish()
40    }
41}
42
43impl fmt::Display for G2Affine {
44    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
45        if self.is_identity().into() {
46            write!(f, "G2Affine(Infinity)")
47        } else {
48            write!(f, "G2Affine(x={}, y={})", self.x(), self.y())
49        }
50    }
51}
52
53impl AsRef<blst_p2_affine> for G2Affine {
54    fn as_ref(&self) -> &blst_p2_affine {
55        &self.0
56    }
57}
58
59impl AsMut<blst_p2_affine> for G2Affine {
60    fn as_mut(&mut self) -> &mut blst_p2_affine {
61        &mut self.0
62    }
63}
64
65impl Default for G2Affine {
66    fn default() -> G2Affine {
67        G2Affine::identity()
68    }
69}
70
71impl From<&G2Projective> for G2Affine {
72    fn from(p: &G2Projective) -> G2Affine {
73        let mut out = blst_p2_affine::default();
74
75        unsafe { blst_p2_to_affine(&mut out, &p.0) };
76
77        G2Affine(out)
78    }
79}
80
81impl From<G2Projective> for G2Affine {
82    fn from(p: G2Projective) -> G2Affine {
83        G2Affine::from(&p)
84    }
85}
86
87impl Eq for G2Affine {}
88impl PartialEq for G2Affine {
89    #[inline]
90    fn eq(&self, other: &Self) -> bool {
91        unsafe { blst_p2_affine_is_equal(&self.0, &other.0) }
92    }
93}
94
95impl Neg for &G2Projective {
96    type Output = G2Projective;
97
98    #[inline]
99    fn neg(self) -> G2Projective {
100        -*self
101    }
102}
103
104impl Neg for G2Projective {
105    type Output = G2Projective;
106
107    #[inline]
108    fn neg(mut self) -> G2Projective {
109        unsafe { blst_p2_cneg(&mut self.0, true) }
110        self
111    }
112}
113
114impl Neg for &G2Affine {
115    type Output = G2Affine;
116
117    #[inline]
118    fn neg(self) -> G2Affine {
119        -*self
120    }
121}
122
123impl Neg for G2Affine {
124    type Output = G2Affine;
125
126    #[inline]
127    fn neg(mut self) -> G2Affine {
128        // Missing for affine in blst
129        if (!self.is_identity()).into() {
130            unsafe {
131                blst_fp2_cneg(&mut self.0.y, &self.0.y, true);
132            }
133        }
134        self
135    }
136}
137
138impl Add<&G2Projective> for &G2Projective {
139    type Output = G2Projective;
140
141    #[inline]
142    fn add(self, rhs: &G2Projective) -> G2Projective {
143        let mut out = blst_p2::default();
144        unsafe { blst_p2_add_or_double(&mut out, &self.0, &rhs.0) };
145        G2Projective(out)
146    }
147}
148
149impl Add<&G2Affine> for &G2Projective {
150    type Output = G2Projective;
151
152    #[inline]
153    fn add(self, rhs: &G2Affine) -> G2Projective {
154        self.add_mixed(rhs)
155    }
156}
157
158impl Add<&G2Projective> for &G2Affine {
159    type Output = G2Projective;
160
161    #[inline]
162    fn add(self, rhs: &G2Projective) -> G2Projective {
163        rhs.add_mixed(self)
164    }
165}
166
167impl Sub<&G2Projective> for &G2Projective {
168    type Output = G2Projective;
169
170    #[inline]
171    fn sub(self, rhs: &G2Projective) -> G2Projective {
172        self + -rhs
173    }
174}
175
176impl Sub<&G2Projective> for &G2Affine {
177    type Output = G2Projective;
178
179    #[inline]
180    fn sub(self, rhs: &G2Projective) -> G2Projective {
181        self + -rhs
182    }
183}
184
185impl Sub<&G2Affine> for &G2Projective {
186    type Output = G2Projective;
187
188    #[inline]
189    fn sub(self, rhs: &G2Affine) -> G2Projective {
190        self + -rhs
191    }
192}
193
194impl AddAssign<&G2Projective> for G2Projective {
195    #[inline]
196    fn add_assign(&mut self, rhs: &G2Projective) {
197        unsafe { blst_p2_add_or_double(&mut self.0, &self.0, &rhs.0) };
198    }
199}
200
201impl SubAssign<&G2Projective> for G2Projective {
202    #[inline]
203    fn sub_assign(&mut self, rhs: &G2Projective) {
204        *self += &-rhs
205    }
206}
207
208impl AddAssign<&G2Affine> for G2Projective {
209    #[inline]
210    fn add_assign(&mut self, rhs: &G2Affine) {
211        unsafe { blst_p2_add_or_double_affine(&mut self.0, &self.0, &rhs.0) };
212    }
213}
214
215impl SubAssign<&G2Affine> for G2Projective {
216    #[inline]
217    fn sub_assign(&mut self, rhs: &G2Affine) {
218        *self += &-rhs;
219    }
220}
221
222impl Mul<&Scalar> for &G2Projective {
223    type Output = G2Projective;
224
225    fn mul(self, scalar: &Scalar) -> Self::Output {
226        self.multiply(scalar)
227    }
228}
229
230impl Mul<&Scalar> for &G2Affine {
231    type Output = G2Projective;
232
233    fn mul(self, scalar: &Scalar) -> Self::Output {
234        G2Projective::from(self).multiply(scalar)
235    }
236}
237
238impl MulAssign<&Scalar> for G2Projective {
239    #[inline]
240    fn mul_assign(&mut self, rhs: &Scalar) {
241        *self = *self * rhs;
242    }
243}
244
245impl MulAssign<&Scalar> for G2Affine {
246    #[inline]
247    fn mul_assign(&mut self, rhs: &Scalar) {
248        *self = (*self * rhs).into();
249    }
250}
251
252impl_add_sub!(G2Projective);
253impl_add_sub!(G2Projective, G2Affine);
254impl_add_sub!(G2Affine, G2Projective, G2Projective);
255
256impl_add_sub_assign!(G2Projective);
257impl_add_sub_assign!(G2Projective, G2Affine);
258
259impl_mul!(G2Projective, Scalar);
260impl_mul!(G2Affine, Scalar, G2Projective);
261
262impl_mul_assign!(G2Projective, Scalar);
263impl_mul_assign!(G2Affine, Scalar);
264
265impl<T> Sum<T> for G2Projective
266where
267    T: Borrow<G2Projective>,
268{
269    fn sum<I>(iter: I) -> Self
270    where
271        I: Iterator<Item = T>,
272    {
273        iter.fold(Self::identity(), |acc, item| acc + item.borrow())
274    }
275}
276
277impl PrimeCurveAffine for G2Affine {
278    type Scalar = Scalar;
279    type Curve = G2Projective;
280
281    fn identity() -> Self {
282        G2Affine(blst_p2_affine::default())
283    }
284
285    fn generator() -> Self {
286        G2Affine(unsafe { *blst_p2_affine_generator() })
287    }
288
289    fn is_identity(&self) -> Choice {
290        unsafe { Choice::from(blst_p2_affine_is_inf(&self.0) as u8) }
291    }
292
293    fn to_curve(&self) -> Self::Curve {
294        self.into()
295    }
296}
297
298impl ConditionallySelectable for G2Affine {
299    fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
300        G2Affine(blst_p2_affine {
301            x: Fp2::conditional_select(&a.x(), &b.x(), choice).0,
302            y: Fp2::conditional_select(&a.y(), &b.y(), choice).0,
303        })
304    }
305}
306
307impl ConditionallySelectable for G2Projective {
308    fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
309        G2Projective(blst_p2 {
310            x: Fp2::conditional_select(&a.x(), &b.x(), choice).0,
311            y: Fp2::conditional_select(&a.y(), &b.y(), choice).0,
312            z: Fp2::conditional_select(&a.z(), &b.z(), choice).0,
313        })
314    }
315}
316
317impl G2Affine {
318    /// Serializes this element into compressed form.
319    pub fn to_compressed(&self) -> [u8; COMPRESSED_SIZE] {
320        let mut out = [0u8; COMPRESSED_SIZE];
321
322        unsafe {
323            blst_p2_affine_compress(out.as_mut_ptr(), &self.0);
324        }
325
326        out
327    }
328
329    /// Serializes this element into uncompressed form.
330    pub fn to_uncompressed(&self) -> [u8; UNCOMPRESSED_SIZE] {
331        let mut out = [0u8; UNCOMPRESSED_SIZE];
332
333        unsafe {
334            blst_p2_affine_serialize(out.as_mut_ptr(), &self.0);
335        }
336
337        out
338    }
339
340    /// Attempts to deserialize an uncompressed element.
341    pub fn from_uncompressed(bytes: &[u8; UNCOMPRESSED_SIZE]) -> CtOption<Self> {
342        G2Affine::from_uncompressed_unchecked(bytes)
343            .and_then(|p| CtOption::new(p, p.is_on_curve() & p.is_torsion_free()))
344    }
345
346    /// Attempts to deserialize an uncompressed element, not checking if the
347    /// element is on the curve and not checking if it is in the correct subgroup.
348    ///
349    /// **This is dangerous to call unless you trust the bytes you are reading; otherwise,
350    /// API invariants may be broken.** Please consider using `from_uncompressed()` instead.
351    pub fn from_uncompressed_unchecked(bytes: &[u8; UNCOMPRESSED_SIZE]) -> CtOption<Self> {
352        let mut raw = blst_p2_affine::default();
353        let success =
354            unsafe { blst_p2_deserialize(&mut raw, bytes.as_ptr()) == BLST_ERROR::BLST_SUCCESS };
355        CtOption::new(G2Affine(raw), Choice::from(success as u8))
356    }
357
358    /// Attempts to deserialize a compressed element.
359    pub fn from_compressed(bytes: &[u8; COMPRESSED_SIZE]) -> CtOption<Self> {
360        G2Affine::from_compressed_unchecked(bytes)
361            .and_then(|p| CtOption::new(p, p.is_on_curve() & p.is_torsion_free()))
362    }
363
364    /// Attempts to deserialize an uncompressed element, not checking if the
365    /// element is in the correct subgroup.
366    ///
367    /// **This is dangerous to call unless you trust the bytes you are reading; otherwise,
368    /// API invariants may be broken.** Please consider using `from_compressed()` instead.
369    pub fn from_compressed_unchecked(bytes: &[u8; COMPRESSED_SIZE]) -> CtOption<Self> {
370        let mut raw = blst_p2_affine::default();
371        let success =
372            unsafe { blst_p2_uncompress(&mut raw, bytes.as_ptr()) == BLST_ERROR::BLST_SUCCESS };
373        CtOption::new(G2Affine(raw), Choice::from(success as u8))
374    }
375
376    /// Returns true if this point is free of an $h$-torsion component, and so it
377    /// exists within the $q$-order subgroup $\mathbb{G}_2$. This should always return true
378    /// unless an "unchecked" API was used.
379    pub fn is_torsion_free(&self) -> Choice {
380        unsafe { Choice::from(blst_p2_affine_in_g2(&self.0) as u8) }
381    }
382
383    /// Returns true if this point is on the curve. This should always return
384    /// true unless an "unchecked" API was used.
385    pub fn is_on_curve(&self) -> Choice {
386        // FIXME: is_identity check should happen in blst
387        unsafe { Choice::from(blst_p2_affine_on_curve(&self.0) as u8) }
388    }
389
390    pub fn from_raw_unchecked(x: Fp2, y: Fp2, _infinity: bool) -> Self {
391        // FIXME: what about infinity?
392        let raw = blst_p2_affine { x: x.0, y: y.0 };
393
394        G2Affine(raw)
395    }
396
397    /// Returns the x coordinate.
398    pub fn x(&self) -> Fp2 {
399        Fp2(self.0.x)
400    }
401
402    /// Returns the y coordinate.
403    pub fn y(&self) -> Fp2 {
404        Fp2(self.0.y)
405    }
406
407    pub const fn uncompressed_size() -> usize {
408        UNCOMPRESSED_SIZE
409    }
410
411    pub const fn compressed_size() -> usize {
412        COMPRESSED_SIZE
413    }
414}
415
416/// This is an element of $\mathbb{G}_2$ represented in the projective coordinate space.
417#[derive(Copy, Clone, Zeroize)]
418#[repr(transparent)]
419pub struct G2Projective(pub(crate) blst_p2);
420
421impl fmt::Debug for G2Projective {
422    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
423        f.debug_struct("G2Projective")
424            .field("x", &self.x())
425            .field("y", &self.y())
426            .field("z", &self.z())
427            .finish()
428    }
429}
430
431impl fmt::Display for G2Projective {
432    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
433        write!(f, "{}", G2Affine::from(self))
434    }
435}
436
437impl AsRef<blst_p2> for G2Projective {
438    fn as_ref(&self) -> &blst_p2 {
439        &self.0
440    }
441}
442
443impl AsMut<blst_p2> for G2Projective {
444    fn as_mut(&mut self) -> &mut blst_p2 {
445        &mut self.0
446    }
447}
448
449impl From<&G2Affine> for G2Projective {
450    fn from(p: &G2Affine) -> G2Projective {
451        let mut out = blst_p2::default();
452
453        unsafe { blst_p2_from_affine(&mut out, &p.0) };
454
455        G2Projective(out)
456    }
457}
458
459impl From<G2Affine> for G2Projective {
460    fn from(p: G2Affine) -> G2Projective {
461        G2Projective::from(&p)
462    }
463}
464
465impl Eq for G2Projective {}
466impl PartialEq for G2Projective {
467    #[inline]
468    fn eq(&self, other: &Self) -> bool {
469        let self_is_zero: bool = self.is_identity().into();
470        let other_is_zero: bool = other.is_identity().into();
471        (self_is_zero && other_is_zero)
472            || (!self_is_zero && !other_is_zero && unsafe { blst_p2_is_equal(&self.0, &other.0) })
473    }
474}
475
476impl G2Projective {
477    /// Serializes this element into compressed form.
478    pub fn to_compressed(&self) -> [u8; COMPRESSED_SIZE] {
479        let mut out = [0u8; COMPRESSED_SIZE];
480
481        unsafe {
482            blst_p2_compress(out.as_mut_ptr(), &self.0);
483        }
484
485        out
486    }
487
488    /// Serializes this element into uncompressed form.
489    pub fn to_uncompressed(&self) -> [u8; UNCOMPRESSED_SIZE] {
490        let mut out = [0u8; UNCOMPRESSED_SIZE];
491
492        unsafe {
493            blst_p2_serialize(out.as_mut_ptr(), &self.0);
494        }
495
496        out
497    }
498
499    /// Attempts to deserialize an uncompressed element.
500    pub fn from_uncompressed(bytes: &[u8; UNCOMPRESSED_SIZE]) -> CtOption<Self> {
501        G2Affine::from_uncompressed(bytes).map(Into::into)
502    }
503
504    /// Attempts to deserialize an uncompressed element, not checking if the
505    /// element is on the curve and not checking if it is in the correct subgroup.
506    ///
507    /// **This is dangerous to call unless you trust the bytes you are reading; otherwise,
508    /// API invariants may be broken.** Please consider using `from_uncompressed()` instead.
509    pub fn from_uncompressed_unchecked(bytes: &[u8; UNCOMPRESSED_SIZE]) -> CtOption<Self> {
510        G2Affine::from_uncompressed_unchecked(bytes).map(Into::into)
511    }
512
513    /// Attempts to deserialize a compressed element.
514    pub fn from_compressed(bytes: &[u8; COMPRESSED_SIZE]) -> CtOption<Self> {
515        G2Affine::from_compressed(bytes).map(Into::into)
516    }
517
518    /// Attempts to deserialize an uncompressed element, not checking if the
519    /// element is in the correct subgroup.
520    ///
521    /// **This is dangerous to call unless you trust the bytes you are reading; otherwise,
522    /// API invariants may be broken.** Please consider using `from_compressed()` instead.
523    pub fn from_compressed_unchecked(bytes: &[u8; COMPRESSED_SIZE]) -> CtOption<Self> {
524        G2Affine::from_compressed_unchecked(bytes).map(Into::into)
525    }
526
527    /// Adds this point to another point in the affine model.
528    pub fn add_mixed(&self, rhs: &G2Affine) -> G2Projective {
529        let mut out = blst_p2::default();
530
531        unsafe { blst_p2_add_or_double_affine(&mut out, &self.0, &rhs.0) };
532
533        G2Projective(out)
534    }
535
536    /// Returns true if this point is on the curve. This should always return
537    /// true unless an "unchecked" API was used.
538    pub fn is_on_curve(&self) -> Choice {
539        let is_on_curve = unsafe { Choice::from(blst_p2_on_curve(&self.0) as u8) };
540        is_on_curve | self.is_identity()
541    }
542
543    fn multiply(&self, scalar: &Scalar) -> G2Projective {
544        let mut out = blst_p2::default();
545
546        // Sclar is 255 bits wide.
547        const NBITS: usize = 255;
548
549        unsafe { blst_p2_mult(&mut out, &self.0, scalar.to_bytes_le().as_ptr(), NBITS) };
550
551        G2Projective(out)
552    }
553
554    pub fn from_raw_unchecked(x: Fp2, y: Fp2, z: Fp2) -> Self {
555        let raw = blst_p2 {
556            x: x.0,
557            y: y.0,
558            z: z.0,
559        };
560
561        G2Projective(raw)
562    }
563
564    /// Returns the x coordinate.
565    pub fn x(&self) -> Fp2 {
566        Fp2(self.0.x)
567    }
568
569    /// Returns the y coordinate.
570    pub fn y(&self) -> Fp2 {
571        Fp2(self.0.y)
572    }
573
574    /// Returns the z coordinate.
575    pub fn z(&self) -> Fp2 {
576        Fp2(self.0.z)
577    }
578
579    /// Hash to curve algorithm.
580    pub fn hash_to_curve(msg: &[u8], dst: &[u8], aug: &[u8]) -> Self {
581        let mut res = Self::identity();
582        unsafe {
583            blst_hash_to_g2(
584                &mut res.0,
585                msg.as_ptr(),
586                msg.len(),
587                dst.as_ptr(),
588                dst.len(),
589                aug.as_ptr(),
590                aug.len(),
591            );
592        }
593        res
594    }
595
596    /// Perform a multi-exponentiation, aka "multi-scalar-multiplication" (MSM) using `blst`'s implementation of Pippenger's algorithm.
597    /// Note: `scalars` is cloned in this method.
598    pub fn multi_exp(points: &[Self], scalars: &[Scalar]) -> Self {
599        let n = if points.len() < scalars.len() {
600            points.len()
601        } else {
602            scalars.len()
603        };
604
605        let points =
606            unsafe { std::slice::from_raw_parts(points.as_ptr() as *const blst_p2, points.len()) };
607        let points = p2_affines::from(points);
608
609        let mut scalar_bytes: Vec<u8> = Vec::with_capacity(n * 32);
610        for a in scalars.iter().map(|s| s.to_bytes_le()) {
611            scalar_bytes.extend_from_slice(&a);
612        }
613
614        let res = points.mult(scalar_bytes.as_slice(), 255);
615
616        G2Projective(res)
617    }
618}
619
620impl Group for G2Projective {
621    type Scalar = Scalar;
622
623    fn random(mut rng: impl RngCore) -> Self {
624        let mut out = blst_p2::default();
625        let mut msg = [0u8; 64];
626        rng.fill_bytes(&mut msg);
627        const DST: [u8; 16] = [0; 16];
628        const AUG: [u8; 16] = [0; 16];
629
630        unsafe {
631            blst_encode_to_g2(
632                &mut out,
633                msg.as_ptr(),
634                msg.len(),
635                DST.as_ptr(),
636                DST.len(),
637                AUG.as_ptr(),
638                AUG.len(),
639            )
640        };
641
642        G2Projective(out)
643    }
644
645    fn identity() -> Self {
646        G2Projective(blst_p2::default())
647    }
648
649    fn generator() -> Self {
650        G2Projective(unsafe { *blst_p2_generator() })
651    }
652
653    fn is_identity(&self) -> Choice {
654        unsafe { Choice::from(blst_p2_is_inf(&self.0) as u8) }
655    }
656
657    fn double(&self) -> Self {
658        let mut double = blst_p2::default();
659        unsafe { blst_p2_double(&mut double, &self.0) };
660        G2Projective(double)
661    }
662}
663
664impl WnafGroup for G2Projective {
665    fn recommended_wnaf_for_num_scalars(num_scalars: usize) -> usize {
666        const RECOMMENDATIONS: [usize; 11] = [1, 3, 8, 20, 47, 126, 260, 826, 1501, 4555, 84071];
667
668        let mut ret = 4;
669        for r in &RECOMMENDATIONS {
670            if num_scalars > *r {
671                ret += 1;
672            } else {
673                break;
674            }
675        }
676
677        ret
678    }
679}
680
681impl PrimeGroup for G2Projective {}
682
683impl Curve for G2Projective {
684    type AffineRepr = G2Affine;
685
686    fn to_affine(&self) -> Self::AffineRepr {
687        self.into()
688    }
689}
690
691impl PrimeCurve for G2Projective {
692    type Affine = G2Affine;
693}
694
695impl GroupEncoding for G2Projective {
696    type Repr = G2Compressed;
697
698    fn from_bytes(bytes: &Self::Repr) -> CtOption<Self> {
699        Self::from_compressed(&bytes.0)
700    }
701
702    fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption<Self> {
703        Self::from_compressed_unchecked(&bytes.0)
704    }
705
706    fn to_bytes(&self) -> Self::Repr {
707        G2Compressed(self.to_compressed())
708    }
709}
710
711impl GroupEncoding for G2Affine {
712    type Repr = G2Compressed;
713
714    fn from_bytes(bytes: &Self::Repr) -> CtOption<Self> {
715        Self::from_compressed(&bytes.0)
716    }
717
718    fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption<Self> {
719        Self::from_compressed_unchecked(&bytes.0)
720    }
721
722    fn to_bytes(&self) -> Self::Repr {
723        G2Compressed(self.to_compressed())
724    }
725}
726
727impl UncompressedEncoding for G2Affine {
728    type Uncompressed = G2Uncompressed;
729
730    fn from_uncompressed(bytes: &Self::Uncompressed) -> CtOption<Self> {
731        Self::from_uncompressed(&bytes.0)
732    }
733
734    fn from_uncompressed_unchecked(bytes: &Self::Uncompressed) -> CtOption<Self> {
735        Self::from_uncompressed_unchecked(&bytes.0)
736    }
737
738    fn to_uncompressed(&self) -> Self::Uncompressed {
739        G2Uncompressed(self.to_uncompressed())
740    }
741}
742
743#[derive(Clone, Debug, Zeroize)]
744pub struct G2Prepared {
745    pub(crate) lines: Vec<blst_fp6>,
746    infinity: bool,
747}
748
749impl From<G2Affine> for G2Prepared {
750    fn from(affine: G2Affine) -> Self {
751        if affine.is_identity().into() {
752            G2Prepared {
753                lines: Vec::new(),
754                infinity: true,
755            }
756        } else {
757            let mut lines = vec![blst_fp6::default(); 68];
758            unsafe { blst_precompute_lines(lines.as_mut_ptr(), &affine.0) }
759            G2Prepared {
760                lines,
761                infinity: false,
762            }
763        }
764    }
765}
766
767impl G2Prepared {
768    pub fn is_identity(&self) -> Choice {
769        Choice::from(self.infinity as u8)
770    }
771}
772
773impl PairingCurveAffine for G2Affine {
774    type Pair = G1Affine;
775    type PairingResult = Gt;
776
777    fn pairing_with(&self, other: &Self::Pair) -> Self::PairingResult {
778        <Bls12 as Engine>::pairing(other, self)
779    }
780}
781
782#[cfg(feature = "gpu")]
783impl ec_gpu::GpuName for G2Affine {
784    fn name() -> String {
785        ec_gpu::name!()
786    }
787}
788
789#[derive(Copy, Clone, Zeroize)]
790#[repr(transparent)]
791pub struct G2Uncompressed([u8; UNCOMPRESSED_SIZE]);
792
793encoded_point_delegations!(G2Uncompressed);
794
795impl Default for G2Uncompressed {
796    fn default() -> Self {
797        G2Uncompressed([0u8; UNCOMPRESSED_SIZE])
798    }
799}
800
801impl fmt::Debug for G2Uncompressed {
802    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
803        self.0[..].fmt(formatter)
804    }
805}
806
807#[derive(Copy, Clone, Zeroize)]
808#[repr(transparent)]
809pub struct G2Compressed([u8; COMPRESSED_SIZE]);
810
811encoded_point_delegations!(G2Compressed);
812
813impl Default for G2Compressed {
814    fn default() -> Self {
815        G2Compressed([0u8; COMPRESSED_SIZE])
816    }
817}
818
819impl fmt::Debug for G2Compressed {
820    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
821        self.0[..].fmt(formatter)
822    }
823}
824
825#[cfg(test)]
826mod tests {
827    #![allow(clippy::eq_op)]
828
829    use super::*;
830
831    use crate::fp::Fp;
832    use ff::Field;
833    use rand_core::SeedableRng;
834    use rand_xorshift::XorShiftRng;
835
836    #[test]
837    fn curve_tests() {
838        let mut rng = XorShiftRng::from_seed([
839            0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
840            0xbc, 0xe5,
841        ]);
842
843        // Negation edge case with zero.
844        {
845            let mut z = G2Projective::identity();
846            z = z.neg();
847            assert_eq!(z.is_identity().unwrap_u8(), 1);
848        }
849
850        // Doubling edge case with zero.
851        {
852            let mut z = G2Projective::identity();
853            z = z.double();
854            assert_eq!(z.is_identity().unwrap_u8(), 1);
855        }
856
857        // Addition edge cases with zero
858        {
859            let mut r = G2Projective::random(&mut rng);
860            let rcopy = r;
861            r += &G2Projective::identity();
862            assert_eq!(r, rcopy);
863            r += &G2Affine::identity();
864            assert_eq!(r, rcopy);
865
866            let mut z = G2Projective::identity();
867            z += &G2Projective::identity();
868            assert_eq!(z.is_identity().unwrap_u8(), 1);
869            z += &G2Affine::identity();
870            assert_eq!(z.is_identity().unwrap_u8(), 1);
871
872            let mut z2 = z;
873            z2 += &r;
874
875            z += &G2Affine::from(r);
876
877            assert_eq!(z, z2);
878            assert_eq!(z, r);
879        }
880
881        // Transformations
882        {
883            let a = G2Projective::random(&mut rng);
884            let b: G2Projective = G2Affine::from(a).into();
885            let c = G2Projective::from(G2Affine::from(G2Projective::from(G2Affine::from(a))));
886
887            assert_eq!(a, b);
888            assert_eq!(b, c);
889        }
890    }
891
892    #[test]
893    fn g2_test_is_valid() {
894        // Reject point on isomorphic twist (b = 3 * (u + 1))
895        {
896            let p = G2Affine::from_raw_unchecked(
897                Fp2::new(
898                    Fp::from_u64s_le(&[
899                        0xa757072d9fa35ba9,
900                        0xae3fb2fb418f6e8a,
901                        0xc1598ec46faa0c7c,
902                        0x7a17a004747e3dbe,
903                        0xcc65406a7c2e5a73,
904                        0x10b8c03d64db4d0c,
905                    ])
906                    .unwrap(),
907                    Fp::from_u64s_le(&[
908                        0xd30e70fe2f029778,
909                        0xda30772df0f5212e,
910                        0x5b47a9ff9a233a50,
911                        0xfb777e5b9b568608,
912                        0x789bac1fec71a2b9,
913                        0x1342f02e2da54405,
914                    ])
915                    .unwrap(),
916                ),
917                Fp2::new(
918                    Fp::from_u64s_le(&[
919                        0xfe0812043de54dca,
920                        0xe455171a3d47a646,
921                        0xa493f36bc20be98a,
922                        0x663015d9410eb608,
923                        0x78e82a79d829a544,
924                        0x40a00545bb3c1e,
925                    ])
926                    .unwrap(),
927                    Fp::from_u64s_le(&[
928                        0x4709802348e79377,
929                        0xb5ac4dc9204bcfbd,
930                        0xda361c97d02f42b2,
931                        0x15008b1dc399e8df,
932                        0x68128fd0548a3829,
933                        0x16a613db5c873aaa,
934                    ])
935                    .unwrap(),
936                ),
937                false,
938            );
939            assert_eq!(p.is_on_curve().unwrap_u8(), 0);
940        }
941
942        // Reject point on a twist (b = 2 * (u + 1))
943        {
944            let p = G2Affine::from_raw_unchecked(
945                Fp2::new(
946                    Fp::from_u64s_le(&[
947                        0xf4fdfe95a705f917,
948                        0xc2914df688233238,
949                        0x37c6b12cca35a34b,
950                        0x41abba710d6c692c,
951                        0xffcc4b2b62ce8484,
952                        0x6993ec01b8934ed,
953                    ])
954                    .unwrap(),
955                    Fp::from_u64s_le(&[
956                        0xb94e92d5f874e26,
957                        0x44516408bc115d95,
958                        0xe93946b290caa591,
959                        0xa5a0c2b7131f3555,
960                        0x83800965822367e7,
961                        0x10cf1d3ad8d90bfa,
962                    ])
963                    .unwrap(),
964                ),
965                Fp2::new(
966                    Fp::from_u64s_le(&[
967                        0xbf00334c79701d97,
968                        0x4fe714f9ff204f9a,
969                        0xab70b28002f3d825,
970                        0x5a9171720e73eb51,
971                        0x38eb4fd8d658adb7,
972                        0xb649051bbc1164d,
973                    ])
974                    .unwrap(),
975                    Fp::from_u64s_le(&[
976                        0x9225814253d7df75,
977                        0xc196c2513477f887,
978                        0xe05e2fbd15a804e0,
979                        0x55f2b8efad953e04,
980                        0x7379345eda55265e,
981                        0x377f2e6208fd4cb,
982                    ])
983                    .unwrap(),
984                ),
985                false,
986            );
987            assert_eq!(p.is_on_curve().unwrap_u8(), 0);
988            assert_eq!(p.is_torsion_free().unwrap_u8(), 0);
989        }
990
991        // Reject point in an invalid subgroup
992        // There is only one r-order subgroup, as r does not divide the cofactor.
993        {
994            let p = G2Affine::from_raw_unchecked(
995                Fp2::new(
996                    Fp::from_u64s_le(&[
997                        0x262cea73ea1906c,
998                        0x2f08540770fabd6,
999                        0x4ceb92d0a76057be,
1000                        0x2199bc19c48c393d,
1001                        0x4a151b732a6075bf,
1002                        0x17762a3b9108c4a7,
1003                    ])
1004                    .unwrap(),
1005                    Fp::from_u64s_le(&[
1006                        0x26f461e944bbd3d1,
1007                        0x298f3189a9cf6ed6,
1008                        0x74328ad8bc2aa150,
1009                        0x7e147f3f9e6e241,
1010                        0x72a9b63583963fff,
1011                        0x158b0083c000462,
1012                    ])
1013                    .unwrap(),
1014                ),
1015                Fp2::new(
1016                    Fp::from_u64s_le(&[
1017                        0x91fb0b225ecf103b,
1018                        0x55d42edc1dc46ba0,
1019                        0x43939b11997b1943,
1020                        0x68cad19430706b4d,
1021                        0x3ccfb97b924dcea8,
1022                        0x1660f93434588f8d,
1023                    ])
1024                    .unwrap(),
1025                    Fp::from_u64s_le(&[
1026                        0xaaed3985b6dcb9c7,
1027                        0xc1e985d6d898d9f4,
1028                        0x618bd2ac3271ac42,
1029                        0x3940a2dbb914b529,
1030                        0xbeb88137cf34f3e7,
1031                        0x1699ee577c61b694,
1032                    ])
1033                    .unwrap(),
1034                ),
1035                false,
1036            );
1037            assert_eq!(p.is_on_curve().unwrap_u8(), 1);
1038            assert_eq!(p.is_torsion_free().unwrap_u8(), 0);
1039        }
1040    }
1041
1042    #[test]
1043    fn test_g2_addition_correctness() {
1044        let mut p = G2Projective::from_raw_unchecked(
1045            Fp2::new(
1046                Fp::from_u64s_le(&[
1047                    0x6c994cc1e303094e,
1048                    0xf034642d2c9e85bd,
1049                    0x275094f1352123a9,
1050                    0x72556c999f3707ac,
1051                    0x4617f2e6774e9711,
1052                    0x100b2fe5bffe030b,
1053                ])
1054                .unwrap(),
1055                Fp::from_u64s_le(&[
1056                    0x7a33555977ec608,
1057                    0xe23039d1fe9c0881,
1058                    0x19ce4678aed4fcb5,
1059                    0x4637c4f417667e2e,
1060                    0x93ebe7c3e41f6acc,
1061                    0xde884f89a9a371b,
1062                ])
1063                .unwrap(),
1064            ),
1065            Fp2::new(
1066                Fp::from_u64s_le(&[
1067                    0xe073119472e1eb62,
1068                    0x44fb3391fe3c9c30,
1069                    0xaa9b066d74694006,
1070                    0x25fd427b4122f231,
1071                    0xd83112aace35cae,
1072                    0x191b2432407cbb7f,
1073                ])
1074                .unwrap(),
1075                Fp::from_u64s_le(&[
1076                    0xf68ae82fe97662f5,
1077                    0xe986057068b50b7d,
1078                    0x96c30f0411590b48,
1079                    0x9eaa6d19de569196,
1080                    0xf6a03d31e2ec2183,
1081                    0x3bdafaf7ca9b39b,
1082                ])
1083                .unwrap(),
1084            ),
1085            Fp2::ONE,
1086        );
1087
1088        p.add_assign(&G2Projective::from_raw_unchecked(
1089            Fp2::new(
1090                Fp::from_u64s_le(&[
1091                    0xa8c763d25910bdd3,
1092                    0x408777b30ca3add4,
1093                    0x6115fcc12e2769e,
1094                    0x8e73a96b329ad190,
1095                    0x27c546f75ee1f3ab,
1096                    0xa33d27add5e7e82,
1097                ])
1098                .unwrap(),
1099                Fp::from_u64s_le(&[
1100                    0x93b1ebcd54870dfe,
1101                    0xf1578300e1342e11,
1102                    0x8270dca3a912407b,
1103                    0x2089faf462438296,
1104                    0x828e5848cd48ea66,
1105                    0x141ecbac1deb038b,
1106                ])
1107                .unwrap(),
1108            ),
1109            Fp2::new(
1110                Fp::from_u64s_le(&[
1111                    0xf5d2c28857229c3f,
1112                    0x8c1574228757ca23,
1113                    0xe8d8102175f5dc19,
1114                    0x2767032fc37cc31d,
1115                    0xd5ee2aba84fd10fe,
1116                    0x16576ccd3dd0a4e8,
1117                ])
1118                .unwrap(),
1119                Fp::from_u64s_le(&[
1120                    0x4da9b6f6a96d1dd2,
1121                    0x9657f7da77f1650e,
1122                    0xbc150712f9ffe6da,
1123                    0x31898db63f87363a,
1124                    0xabab040ddbd097cc,
1125                    0x11ad236b9ba02990,
1126                ])
1127                .unwrap(),
1128            ),
1129            Fp2::ONE,
1130        ));
1131
1132        let p = G2Affine::from(p);
1133
1134        assert_eq!(
1135            p,
1136            G2Affine::from_raw_unchecked(
1137                Fp2::new(
1138                    Fp::from_u64s_le(&[
1139                        0xcde7ee8a3f2ac8af,
1140                        0xfc642eb35975b069,
1141                        0xa7de72b7dd0e64b7,
1142                        0xf1273e6406eef9cc,
1143                        0xababd760ff05cb92,
1144                        0xd7c20456617e89
1145                    ])
1146                    .unwrap(),
1147                    Fp::from_u64s_le(&[
1148                        0xd1a50b8572cbd2b8,
1149                        0x238f0ac6119d07df,
1150                        0x4dbe924fe5fd6ac2,
1151                        0x8b203284c51edf6b,
1152                        0xc8a0b730bbb21f5e,
1153                        0x1a3b59d29a31274
1154                    ])
1155                    .unwrap(),
1156                ),
1157                Fp2::new(
1158                    Fp::from_u64s_le(&[
1159                        0x9e709e78a8eaa4c9,
1160                        0xd30921c93ec342f4,
1161                        0x6d1ef332486f5e34,
1162                        0x64528ab3863633dc,
1163                        0x159384333d7cba97,
1164                        0x4cb84741f3cafe8
1165                    ])
1166                    .unwrap(),
1167                    Fp::from_u64s_le(&[
1168                        0x242af0dc3640e1a4,
1169                        0xe90a73ad65c66919,
1170                        0x2bd7ca7f4346f9ec,
1171                        0x38528f92b689644d,
1172                        0xb6884deec59fb21f,
1173                        0x3c075d3ec52ba90
1174                    ])
1175                    .unwrap(),
1176                ),
1177                false,
1178            )
1179        );
1180    }
1181
1182    #[test]
1183    fn test_g2_doubling_correctness() {
1184        let mut p = G2Projective::from_raw_unchecked(
1185            Fp2::new(
1186                Fp::from_u64s_le(&[
1187                    0x6c994cc1e303094e,
1188                    0xf034642d2c9e85bd,
1189                    0x275094f1352123a9,
1190                    0x72556c999f3707ac,
1191                    0x4617f2e6774e9711,
1192                    0x100b2fe5bffe030b,
1193                ])
1194                .unwrap(),
1195                Fp::from_u64s_le(&[
1196                    0x7a33555977ec608,
1197                    0xe23039d1fe9c0881,
1198                    0x19ce4678aed4fcb5,
1199                    0x4637c4f417667e2e,
1200                    0x93ebe7c3e41f6acc,
1201                    0xde884f89a9a371b,
1202                ])
1203                .unwrap(),
1204            ),
1205            Fp2::new(
1206                Fp::from_u64s_le(&[
1207                    0xe073119472e1eb62,
1208                    0x44fb3391fe3c9c30,
1209                    0xaa9b066d74694006,
1210                    0x25fd427b4122f231,
1211                    0xd83112aace35cae,
1212                    0x191b2432407cbb7f,
1213                ])
1214                .unwrap(),
1215                Fp::from_u64s_le(&[
1216                    0xf68ae82fe97662f5,
1217                    0xe986057068b50b7d,
1218                    0x96c30f0411590b48,
1219                    0x9eaa6d19de569196,
1220                    0xf6a03d31e2ec2183,
1221                    0x3bdafaf7ca9b39b,
1222                ])
1223                .unwrap(),
1224            ),
1225            Fp2::ONE,
1226        );
1227
1228        p = p.double();
1229
1230        let p = G2Affine::from(p);
1231
1232        assert_eq!(
1233            p,
1234            G2Affine::from_raw_unchecked(
1235                Fp2::new(
1236                    Fp::from_u64s_le(&[
1237                        0x91ccb1292727c404,
1238                        0x91a6cb182438fad7,
1239                        0x116aee59434de902,
1240                        0xbcedcfce1e52d986,
1241                        0x9755d4a3926e9862,
1242                        0x18bab73760fd8024
1243                    ])
1244                    .unwrap(),
1245                    Fp::from_u64s_le(&[
1246                        0x4e7c5e0a2ae5b99e,
1247                        0x96e582a27f028961,
1248                        0xc74d1cf4ef2d5926,
1249                        0xeb0cf5e610ef4fe7,
1250                        0x7b4c2bae8db6e70b,
1251                        0xf136e43909fca0
1252                    ])
1253                    .unwrap(),
1254                ),
1255                Fp2::new(
1256                    Fp::from_u64s_le(&[
1257                        0x954d4466ab13e58,
1258                        0x3ee42eec614cf890,
1259                        0x853bb1d28877577e,
1260                        0xa5a2a51f7fde787b,
1261                        0x8b92866bc6384188,
1262                        0x81a53fe531d64ef
1263                    ])
1264                    .unwrap(),
1265                    Fp::from_u64s_le(&[
1266                        0x4c5d607666239b34,
1267                        0xeddb5f48304d14b3,
1268                        0x337167ee6e8e3cb6,
1269                        0xb271f52f12ead742,
1270                        0x244e6c2015c83348,
1271                        0x19e2deae6eb9b441
1272                    ])
1273                    .unwrap(),
1274                ),
1275                false,
1276            )
1277        );
1278    }
1279
1280    #[test]
1281    fn test_affine_point_equality() {
1282        let a = G2Affine::generator();
1283        let b = G2Affine::identity();
1284
1285        assert_eq!(a, a);
1286        assert_eq!(b, b);
1287        assert_ne!(a, b);
1288        assert_ne!(b, a);
1289    }
1290
1291    #[test]
1292    fn test_projective_point_equality() {
1293        let a = G2Projective::generator();
1294        let b = G2Projective::identity();
1295
1296        assert_eq!(a, a);
1297        assert_eq!(b, b);
1298        assert_ne!(a, b);
1299        assert_ne!(b, a);
1300    }
1301
1302    #[test]
1303    fn g2_curve_tests() {
1304        use group::tests::curve_tests;
1305        curve_tests::<G2Projective>();
1306    }
1307
1308    #[test]
1309    fn test_g2_is_identity() {
1310        assert_eq!(G2Projective::identity().is_identity().unwrap_u8(), 1);
1311        assert_eq!(G2Projective::generator().is_identity().unwrap_u8(), 0);
1312        assert_eq!(G2Affine::identity().is_identity().unwrap_u8(), 1);
1313        assert_eq!(G2Affine::generator().is_identity().unwrap_u8(), 0);
1314    }
1315
1316    #[test]
1317    fn test_g2_serialization_roundtrip() {
1318        let mut rng = XorShiftRng::from_seed([
1319            0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
1320            0xbc, 0xe5,
1321        ]);
1322
1323        for _ in 0..100 {
1324            let el: G2Affine = G2Projective::random(&mut rng).into();
1325            let c = el.to_compressed();
1326            assert_eq!(G2Affine::from_compressed(&c).unwrap(), el);
1327            assert_eq!(G2Affine::from_compressed_unchecked(&c).unwrap(), el);
1328
1329            let u = el.to_uncompressed();
1330            assert_eq!(G2Affine::from_uncompressed(&u).unwrap(), el);
1331            assert_eq!(G2Affine::from_uncompressed_unchecked(&u).unwrap(), el);
1332
1333            let c = el.to_bytes();
1334            assert_eq!(G2Affine::from_bytes(&c).unwrap(), el);
1335            assert_eq!(G2Affine::from_bytes_unchecked(&c).unwrap(), el);
1336
1337            let el = G2Projective::random(&mut rng);
1338            let c = el.to_compressed();
1339            assert_eq!(G2Projective::from_compressed(&c).unwrap(), el);
1340            assert_eq!(G2Projective::from_compressed_unchecked(&c).unwrap(), el);
1341
1342            let u = el.to_uncompressed();
1343            assert_eq!(G2Projective::from_uncompressed(&u).unwrap(), el);
1344            assert_eq!(G2Projective::from_uncompressed_unchecked(&u).unwrap(), el);
1345
1346            let c = el.to_bytes();
1347            assert_eq!(G2Projective::from_bytes(&c).unwrap(), el);
1348            assert_eq!(G2Projective::from_bytes_unchecked(&c).unwrap(), el);
1349        }
1350    }
1351
1352    #[test]
1353    fn test_multi_exp() {
1354        const SIZE: usize = 128;
1355        let mut rng = XorShiftRng::from_seed([
1356            0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
1357            0xbc, 0xe5,
1358        ]);
1359
1360        let points: Vec<G2Projective> = (0..SIZE).map(|_| G2Projective::random(&mut rng)).collect();
1361        let scalars: Vec<Scalar> = (0..SIZE).map(|_| Scalar::random(&mut rng)).collect();
1362
1363        let mut naive = points[0] * scalars[0];
1364        for i in 1..SIZE {
1365            naive += points[i] * scalars[i];
1366        }
1367
1368        let pippenger = G2Projective::multi_exp(points.as_slice(), scalars.as_slice());
1369
1370        assert_eq!(naive, pippenger);
1371    }
1372}