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