sm9_core/
groups.rs

1#![allow(dead_code)]
2
3use core::{
4    fmt,
5    ops::{Add, AddAssign, Mul, Neg, Sub},
6};
7use hex_literal::hex;
8use rand::Rng;
9
10use crate::{
11    One, Zero,
12    fields::{FieldElement, Fq, Fq2, Fr},
13    u256::U256,
14};
15
16const SM9_P1X: [u8; 32] =
17    hex!("93DE051D 62BF718F F5ED0704 487D01D6 E1E40869 09DC3280 E8C4E481 7C66DDDD");
18const SM9_P1Y: [u8; 32] =
19    hex!("21FE8DDA 4F21E607 63106512 5C395BBC 1C1C00CB FA602435 0C464CD7 0A3EA616");
20const SM9_P2X1: [u8; 32] =
21    hex!("85AEF3D0 78640C98 597B6027 B441A01F F1DD2C19 0F5E93C4 54806C11 D8806141");
22const SM9_P2X0: [u8; 32] =
23    hex!("37227552 92130B08 D2AAB97F D34EC120 EE265948 D19C17AB F9B7213B AF82D65B");
24const SM9_P2Y1: [u8; 32] =
25    hex!("17509B09 2E845C12 66BA0D26 2CBEE6ED 0736A96F A347C8BD 856DC76B 84EBEB96");
26const SM9_P2Y0: [u8; 32] =
27    hex!("A7CF28D5 19BE3DA6 5F317015 3D278FF2 47EFBA98 A71A0811 6215BBA5 C999A7C7");
28
29pub trait GroupElement:
30    Sized
31    + Copy
32    + Clone
33    + Zero
34    + PartialEq
35    + Eq
36    + fmt::Debug
37    + Add<Output = Self>
38    + Sub<Output = Self>
39    + Neg<Output = Self>
40    + Mul<Fr, Output = Self>
41{
42    fn one() -> Self;
43    fn random<R: Rng>(rng: &mut R) -> Self;
44    fn double(&self) -> Self;
45}
46
47pub trait GroupParams: Sized + fmt::Debug {
48    type Base: FieldElement;
49
50    fn name() -> &'static str;
51    fn one() -> G<Self>;
52    fn coeff_b() -> Self::Base;
53    fn check_order() -> bool {
54        false
55    }
56}
57
58#[repr(C)]
59pub struct G<P: GroupParams> {
60    pub(crate) x: P::Base,
61    pub(crate) y: P::Base,
62    pub(crate) z: P::Base,
63}
64
65impl<P: GroupParams> G<P> {
66    pub fn new(x: P::Base, y: P::Base, z: P::Base) -> Self {
67        G { x, y, z }
68    }
69
70    pub fn x(&self) -> &P::Base {
71        &self.x
72    }
73
74    pub fn x_mut(&mut self) -> &mut P::Base {
75        &mut self.x
76    }
77
78    pub fn y(&self) -> &P::Base {
79        &self.y
80    }
81
82    pub fn y_mut(&mut self) -> &mut P::Base {
83        &mut self.y
84    }
85
86    pub fn z(&self) -> &P::Base {
87        &self.z
88    }
89
90    pub fn z_mut(&mut self) -> &mut P::Base {
91        &mut self.z
92    }
93}
94
95#[derive(Debug)]
96pub struct AffineG<P: GroupParams> {
97    x: P::Base,
98    y: P::Base,
99}
100
101#[derive(Debug)]
102pub enum Error {
103    NotOnCurve,
104    NotInSubgroup,
105}
106
107impl<P: GroupParams> AffineG<P> {
108    pub fn new(x: P::Base, y: P::Base) -> Result<Self, Error> {
109        if y.squared() == (x.squared() * x) + P::coeff_b() {
110            if P::check_order() {
111                let p: G<P> = G {
112                    x,
113                    y,
114                    z: P::Base::one(),
115                };
116
117                if (p * (-Fr::one())) + p != G::zero() {
118                    return Err(Error::NotInSubgroup);
119                }
120            }
121
122            Ok(AffineG { x, y })
123        } else {
124            Err(Error::NotOnCurve)
125        }
126    }
127
128    pub fn x(&self) -> &P::Base {
129        &self.x
130    }
131
132    pub fn x_mut(&mut self) -> &mut P::Base {
133        &mut self.x
134    }
135
136    pub fn y(&self) -> &P::Base {
137        &self.y
138    }
139
140    pub fn y_mut(&mut self) -> &mut P::Base {
141        &mut self.y
142    }
143}
144
145impl<P: GroupParams> PartialEq for AffineG<P> {
146    fn eq(&self, other: &Self) -> bool {
147        self.x == other.x && self.y == other.y
148    }
149}
150
151impl<P: GroupParams> Eq for AffineG<P> {}
152
153impl<P: GroupParams> fmt::Debug for G<P> {
154    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
155        write!(f, "{}({:?}, {:?}, {:?})", P::name(), self.x, self.y, self.z)
156    }
157}
158
159impl<P: GroupParams> Clone for G<P> {
160    fn clone(&self) -> Self {
161        *self
162    }
163}
164
165impl<P: GroupParams> Copy for G<P> {}
166
167impl<P: GroupParams> Clone for AffineG<P> {
168    fn clone(&self) -> Self {
169        *self
170    }
171}
172
173impl<P: GroupParams> Copy for AffineG<P> {}
174
175impl<P: GroupParams> PartialEq for G<P> {
176    fn eq(&self, other: &Self) -> bool {
177        if self.is_zero() {
178            return other.is_zero();
179        }
180
181        if other.is_zero() {
182            return false;
183        }
184
185        let z1_squared = self.z.squared();
186        let z2_squared = other.z.squared();
187
188        if self.x * z2_squared != other.x * z1_squared {
189            return false;
190        }
191
192        let z1_cubed = self.z * z1_squared;
193        let z2_cubed = other.z * z2_squared;
194
195        if self.y * z2_cubed != other.y * z1_cubed {
196            return false;
197        }
198
199        true
200    }
201}
202impl<P: GroupParams> Eq for G<P> {}
203
204impl<P: GroupParams> G<P> {
205    pub fn to_affine(self) -> Option<AffineG<P>> {
206        if self.z.is_zero() {
207            None
208        } else if self.z == P::Base::one() {
209            Some(AffineG {
210                x: self.x,
211                y: self.y,
212            })
213        } else {
214            let zinv = self.z.inverse()?;
215            let zinv_squared = zinv.squared();
216
217            Some(AffineG {
218                x: self.x * zinv_squared,
219                y: self.y * (zinv_squared * zinv),
220            })
221        }
222    }
223}
224
225impl<P: GroupParams> AffineG<P> {
226    pub fn to_jacobian(self) -> G<P> {
227        G {
228            x: self.x,
229            y: self.y,
230            z: P::Base::one(),
231        }
232    }
233}
234
235impl<P: GroupParams> Zero for G<P> {
236    fn zero() -> Self {
237        G {
238            x: P::Base::zero(),
239            y: P::Base::one(),
240            z: P::Base::zero(),
241        }
242    }
243
244    fn is_zero(&self) -> bool {
245        self.z.is_zero()
246    }
247}
248
249impl<P: GroupParams> GroupElement for G<P> {
250    fn one() -> Self {
251        P::one()
252    }
253
254    fn random<R: Rng>(rng: &mut R) -> Self {
255        P::one() * Fr::random(rng)
256    }
257
258    // SM9 dentity-based cryptographic algorithms
259    // Part 1: General
260    // Annex A  A.1.3.3.2
261    // 𝜆1=3𝑥1^2, 𝜆2=4𝑥1𝑦1^2, 𝜆3=8𝑦1^4, 𝑥3=𝜆1^2−2𝜆2, 𝑦3=𝜆1(𝜆2−𝑥3)−𝜆3, 𝑧3=2𝑦1𝑧1
262    // http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l
263    fn double(&self) -> Self {
264        let a = self.x.squared();
265        let b = self.y.squared();
266        let c = b.squared();
267        let d = ((self.x + b).squared() - a - c).double();
268        let e = a.triple();
269        let f = e.squared();
270        let x3 = f - d.double();
271        let eight_c = c.double().double().double();
272        let y1z1 = self.y * self.z;
273
274        G {
275            x: x3,
276            y: e * (d - x3) - eight_c,
277            z: y1z1.double(),
278        }
279    }
280}
281impl<P: GroupParams> Mul<Fr> for G<P> {
282    type Output = G<P>;
283    #[allow(clippy::suspicious_arithmetic_impl)]
284    /// Standard double-and-add method for multiplication by a scalar.
285    fn mul(self, other: Fr) -> G<P> {
286        let mut res = G::zero();
287        for i in U256::from(other).bits_without_leading_zeros() {
288            res = res.double();
289            if i {
290                res += self;
291            }
292        }
293
294        res
295    }
296}
297impl<P: GroupParams> AddAssign<G<P>> for G<P> {
298    fn add_assign(&mut self, other: G<P>) {
299        *self = *self + other;
300    }
301}
302impl<P: GroupParams> AddAssign<&G<P>> for G<P> {
303    fn add_assign(&mut self, rhs: &G<P>) {
304        *self = *self + rhs;
305    }
306}
307impl<P: GroupParams> Add<G<P>> for G<P> {
308    type Output = G<P>;
309    // SM9 dentity-based cryptographic algorithms
310    // Part 1: General
311    // Annex A  A.1.3.3.2
312    // 𝜆1=𝑥1𝑧2^2, 𝜆2=𝑥2𝑧1^2, 𝜆3=𝜆1−𝜆2, 𝜆4=𝑦1𝑧2^3, 𝜆5=𝑦2𝑧1^3, 𝜆6=𝜆4−𝜆5, 𝜆7=𝜆1+𝜆2, 𝜆8=𝜆4+𝜆5, 𝜆9=𝜆7𝜆3^2,
313    // 𝑥3=𝜆6^2−𝜆9, 𝜆10=𝜆9^2−2𝑥3, 𝑦3=(𝜆10𝜆6−𝜆8𝜆3^3)/2, 𝑧3=𝑧1𝑧2𝜆3
314    // http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-add-1998-cmo-2
315    fn add(self, other: G<P>) -> G<P> {
316        if self.is_zero() {
317            return other;
318        }
319        if other.is_zero() {
320            return self;
321        }
322        match (self.z == P::Base::one(), other.z == P::Base::one()) {
323            (true, true) => {
324                let h = other.x - self.x;
325                let r = other.y - self.y;
326                if r.is_zero() && h.is_zero() {
327                    return self.double();
328                }
329                let hh = h.squared();
330                let hhh = h * hh;
331                let v = self.x * hh;
332                let x = r.squared() - hhh - v.double();
333                let y = r * (v - x) - self.y * hhh;
334                let z = h;
335                G { x, y, z }
336            }
337            (false, true) => {
338                let z1_squared = self.z.squared();
339                let u2 = other.x * z1_squared;
340                let z1_cubed = self.z * z1_squared;
341                let s2 = other.y * z1_cubed;
342                let h = u2 - self.x;
343                let r = s2 - self.y;
344                if r.is_zero() && h.is_zero() {
345                    return self.double();
346                }
347                let hh = h.squared();
348                let hhh = h * hh;
349                let v = self.x * hh;
350                let x = r.squared() - hhh - v.double();
351                let y = r * (v - x) - self.y * hhh;
352                let z = self.z * h;
353                G { x, y, z }
354            }
355            (true, false) => other + self,
356            (false, false) => {
357                let z1_squared = self.z.squared();
358                let z2_squared = other.z.squared();
359                let u1 = self.x * z2_squared;
360                let u2 = other.x * z1_squared;
361                let z1_cubed = self.z * z1_squared;
362                let z2_cubed = other.z * z2_squared;
363                let s1 = self.y * z2_cubed;
364                let s2 = other.y * z1_cubed;
365
366                let r = s2 - s1;
367                let h = u2 - u1;
368                let t6 = s1 + s2;
369                if r.is_zero() {
370                    if h.is_zero() {
371                        return self.double();
372                    }
373                    if t6.is_zero() {
374                        return Self::zero();
375                    }
376                }
377                let hh = h.squared();
378                let hhh = h * hh;
379                let v = u1 * hh;
380                let x = r.squared() - hhh - v.double();
381                let y = r * (v - x) - s1 * hhh;
382                let z = self.z * other.z * h;
383                G { x, y, z }
384            }
385        }
386    }
387}
388impl<P: GroupParams> Add<&G<P>> for G<P> {
389    type Output = G<P>;
390
391    fn add(self, rhs: &G<P>) -> Self::Output {
392        self + *rhs
393    }
394}
395impl<P: GroupParams> Add<G<P>> for &G<P> {
396    type Output = G<P>;
397
398    fn add(self, rhs: G<P>) -> Self::Output {
399        *self + rhs
400    }
401}
402
403impl<P: GroupParams> Neg for G<P> {
404    type Output = G<P>;
405
406    fn neg(self) -> G<P> {
407        if self.is_zero() {
408            self
409        } else {
410            G {
411                x: self.x,
412                y: -self.y,
413                z: self.z,
414            }
415        }
416    }
417}
418
419impl<P: GroupParams> Neg for AffineG<P> {
420    type Output = AffineG<P>;
421
422    fn neg(self) -> AffineG<P> {
423        AffineG {
424            x: self.x,
425            y: -self.y,
426        }
427    }
428}
429
430impl<P: GroupParams> Sub<G<P>> for G<P> {
431    type Output = G<P>;
432
433    fn sub(self, other: G<P>) -> G<P> {
434        self + (-other)
435    }
436}
437
438#[derive(Debug)]
439pub struct G1Params;
440
441impl GroupParams for G1Params {
442    type Base = Fq;
443
444    fn name() -> &'static str {
445        "G1"
446    }
447    //generator
448    fn one() -> G<Self> {
449        G {
450            x: Fq::from_slice(&SM9_P1X).unwrap(),
451            y: Fq::from_slice(&SM9_P1Y).unwrap(),
452            z: Fq::one(),
453        }
454    }
455
456    fn coeff_b() -> Fq {
457        Fq::from_str("5").expect("5 is a valid field element")
458    }
459}
460
461pub type G1 = G<G1Params>;
462
463pub type AffineG1 = AffineG<G1Params>;
464
465#[derive(Debug)]
466pub struct G2Params;
467
468impl GroupParams for G2Params {
469    type Base = Fq2;
470
471    fn name() -> &'static str {
472        "G2"
473    }
474    // generator
475    fn one() -> G<Self> {
476        G {
477            x: Fq2::new(
478                Fq::from_slice(&SM9_P2X0).unwrap(),
479                Fq::from_slice(&SM9_P2X1).unwrap(),
480            ),
481            y: Fq2::new(
482                Fq::from_slice(&SM9_P2Y0).unwrap(),
483                Fq::from_slice(&SM9_P2Y1).unwrap(),
484            ),
485            z: Fq2::one(),
486        }
487    }
488
489    fn coeff_b() -> Fq2 {
490        Fq2::i().scale(&Fq::from_str("5").expect("5 is a valid field element"))
491    }
492
493    fn check_order() -> bool {
494        true
495    }
496}
497
498pub type G2 = G<G2Params>;
499
500pub type AffineG2 = AffineG<G2Params>;
501
502/* ************************************************************************************************ */
503#[cfg(test)]
504mod tests {
505    use super::*;
506
507    fn random_test_addition<G: GroupElement, R: Rng>(rng: &mut R) {
508        for _ in 0..50 {
509            let r1 = G::random(rng);
510            let r2 = G::random(rng);
511            let r3 = G::random(rng);
512
513            assert_eq!((r1 + r2) + r3, r1 + (r2 + r3));
514            assert!(((r1 + r2 + r3) - r2 - r3 - r1).is_zero());
515        }
516    }
517    fn random_test_doubling<G: GroupElement, R: Rng>(rng: &mut R) {
518        for _ in 0..50 {
519            let r1 = G::random(rng);
520            let r2 = G::random(rng);
521            let ti = Fr::from_str("2").unwrap().inverse().unwrap();
522
523            assert_eq!((r1 + r2) + r1, r1.double() + r2);
524            assert_eq!(r1, r1.double() * ti);
525        }
526    }
527    pub fn group_trials<G: GroupElement>() {
528        assert!(G::zero().is_zero());
529        assert!((G::one() - G::one()).is_zero());
530        assert_eq!(G::one() + G::one(), G::one() * Fr::from_str("2").unwrap());
531        assert!(G::zero().double().is_zero());
532
533        assert!((G::one() * (-Fr::one()) + G::one()).is_zero());
534        use rand::{SeedableRng, rngs::StdRng};
535        let seed = [
536            0, 0, 0, 0, 0, 0, 64, 13, // 103245
537            0, 0, 0, 0, 0, 0, 176, 2, // 191922
538            0, 0, 0, 0, 0, 0, 0, 13, // 1293
539            0, 0, 0, 0, 0, 0, 96, 7u8, // 192103
540        ];
541        let mut rng = StdRng::from_seed(seed);
542
543        random_test_addition::<G, _>(&mut rng);
544        random_test_doubling::<G, _>(&mut rng);
545    }
546
547    fn test_projective_negation_and_subtraction<G: GroupElement>() {
548        let a = G::one().double();
549        assert_eq!(a + (-a), G::zero());
550        assert_eq!(a + (-a), a - a);
551    }
552
553    fn test_projective_double_and_add<G: GroupElement>() {
554        let a = G::one().double().double(); // 4P
555        let b = G::one().double(); // 2P
556        let c = a + b; //6P
557
558        let mut d = G::one();
559        for _ in 0..5 {
560            d = d + G::one();
561        }
562        assert_eq!(c, d);
563    }
564
565    #[test]
566    fn test_g1_negation_and_subtraction() {
567        test_projective_negation_and_subtraction::<G1>();
568    }
569    #[test]
570    fn test_g2_negation_and_subtraction() {
571        test_projective_negation_and_subtraction::<G2>();
572    }
573
574    #[test]
575    fn test_g1() {
576        group_trials::<G1>();
577    }
578
579    const P1X: [u8; 32] = hex!("917be49d159184fba140f4dfc5d653464e94f718fe195b226b3f715829e6e768");
580    const P1Y: [u8; 32] = hex!("288578d9505d462867a50acee40ee143b896e72505be10e8ce4c6b0c945b642b");
581    const P2X: [u8; 32] = hex!("593417680f252445fd0522383e23c77a54b11fe222de4a886eabc26e16bffa3c");
582    const P2Y: [u8; 32] = hex!("38e8fc9a8b60f5ba0c6c411f721c117044435a833757d8fee65828511b8b245d");
583
584    const P_DBL_X: [u8; 32] =
585        hex!("268def7968f1e8c51635e277425403df88355fb2ecf16f7920f112eb2a7e50c9");
586    const P_DBL_Y: [u8; 32] =
587        hex!("5c596b534bbaa85c1d3aecf436e61ff1bfd9f70856f0309c2a63d8248205d84e");
588    const P_ADD_X: [u8; 32] =
589        hex!("056610cb69f8d5659ea94e4a67bbf3b93fb0bd449672d7ca2525ec3b68c894d1");
590    const P_ADD_Y: [u8; 32] =
591        hex!("88f3f99ce78ed3ffe6ca1cface5242570cb5d053f16a8e0baae10414babd86a7");
592    const P_NEG_X: [u8; 32] =
593        hex!("917be49d159184fba140f4dfc5d653464e94f718fe195b226b3f715829e6e768");
594    const P_NEG_Y: [u8; 32] =
595        hex!("8dba8726b24660c96e5ea081117fe601695bac2614bcddf31723301b4ef5e152");
596    const P_SUB_X: [u8; 32] =
597        hex!("29e4a54cad98da9939b95f677784bff3b1dd9334c83d93e351e0f8f7c4ce2dc5");
598    const P_SUB_Y: [u8; 32] =
599        hex!("4473eba3b8ff990b8456c41ec0727b76cb2b0f960495b144949f70bf95643b82");
600    const P_MUL_X: [u8; 32] =
601        hex!("997fcff625adbae62566f684f9e89181713f972c5a9cd9ce6764636761ba87d1");
602    const P_MUL_Y: [u8; 32] =
603        hex!("8142a28d1bd109501452a649e2d68f012e265460e0c7d3da743fb036eb23b03b");
604
605    const HEX_IV: [u8; 32] =
606        hex!("123456789abcdef00fedcba987654321123456789abcdef00fedcba987654321");
607
608    #[test]
609    fn test_g1_dbl() {
610        let p1 = G1::new(
611            Fq::from_slice(&P1X).unwrap(),
612            Fq::from_slice(&P1Y).unwrap(),
613            Fq::one(),
614        );
615        let r = G1::new(
616            Fq::from_slice(&P_DBL_X).unwrap(),
617            Fq::from_slice(&P_DBL_Y).unwrap(),
618            Fq::one(),
619        );
620        assert_eq!(r, p1.double());
621    }
622
623    #[test]
624    fn test_g1_add() {
625        let p1 = G1::new(
626            Fq::from_slice(&P1X).unwrap(),
627            Fq::from_slice(&P1Y).unwrap(),
628            Fq::one(),
629        );
630        let p2 = G1::new(
631            Fq::from_slice(&P2X).unwrap(),
632            Fq::from_slice(&P2Y).unwrap(),
633            Fq::one(),
634        );
635        let r = G1::new(
636            Fq::from_slice(&P_ADD_X).unwrap(),
637            Fq::from_slice(&P_ADD_Y).unwrap(),
638            Fq::one(),
639        );
640        assert_eq!(r, p1 + p2);
641        test_projective_double_and_add::<G1>();
642    }
643
644    #[test]
645    fn test_g1_sub() {
646        let p1 = G1::new(
647            Fq::from_slice(&P1X).unwrap(),
648            Fq::from_slice(&P1Y).unwrap(),
649            Fq::one(),
650        );
651        let p2 = G1::new(
652            Fq::from_slice(&P2X).unwrap(),
653            Fq::from_slice(&P2Y).unwrap(),
654            Fq::one(),
655        );
656        let r = G1::new(
657            Fq::from_slice(&P_NEG_X).unwrap(),
658            Fq::from_slice(&P_NEG_Y).unwrap(),
659            Fq::one(),
660        );
661        assert_eq!(r, -p1);
662        let r = G1::new(
663            Fq::from_slice(&P_SUB_X).unwrap(),
664            Fq::from_slice(&P_SUB_Y).unwrap(),
665            Fq::one(),
666        );
667        assert_eq!(r, p1 - p2);
668    }
669
670    #[test]
671    fn test_g1_mul() {
672        let p1 = G1::new(
673            Fq::from_slice(&P1X).unwrap(),
674            Fq::from_slice(&P1Y).unwrap(),
675            Fq::one(),
676        );
677        let r = G1::new(
678            Fq::from_slice(&P_MUL_X).unwrap(),
679            Fq::from_slice(&P_MUL_Y).unwrap(),
680            Fq::one(),
681        );
682        let k = Fr::from_slice(&HEX_IV).unwrap();
683        assert_eq!(r, p1 * k);
684    }
685
686    const G2_P1XY: [u8; 32] =
687        hex!("83f6a65d85d51ec72eacf19bc38384e0369eb22a134a725a0191faa6e4f192ef");
688    const G2_P1XX: [u8; 32] =
689        hex!("9a79bfd491ef1cb32d9b57f7d0590ccff6b1cfe63dd15c0823d692fafbe96dbc");
690    const G2_P1YY: [u8; 32] =
691        hex!("9ed11c499291db0454d738555af0ce8a1df960056ee7425a6bf296eae60a5037");
692    const G2_P1YX: [u8; 32] =
693        hex!("849d4434eb7113fc9fb3809b51d54064fa2f20503423d256bc044905b1eba3fb");
694
695    const G2_P2XY: [u8; 32] =
696        hex!("a36232a9713f69157b7cdceef54aa0237b3ba0642a80dbb597af8935aea2c130");
697    const G2_P2XX: [u8; 32] =
698        hex!("624b19114e49f00281e2aee1f1b9d4f0a081a135868f8bbdb7b7a7b7da5fd6bc");
699    const G2_P2YY: [u8; 32] =
700        hex!("77966917ec1c5a294dd836c34691ab5e891f8c9f017443902c0a73ec54d449d8");
701    const G2_P2YX: [u8; 32] =
702        hex!("1be45454b6fa085a53744b22fd398238e400c3e031c8796e59e1bd6222048af0");
703
704    const G2_DBLXY: [u8; 32] =
705        hex!("73cbced58a8e76ef5235b480050a74e906e4d27185bd85d7ebdcd43ad24475fd");
706    const G2_DBLXX: [u8; 32] =
707        hex!("58400f0eb23000d814f5b5d0706749a72909795b7b04f26d6d58b2cf478ad9c9");
708    const G2_DBLYY: [u8; 32] =
709        hex!("19b460e09ac9ddbb380d6441e078a47bfcaa7d4c3d60b3a6c0d05f896472dc3c");
710    const G2_DBLYX: [u8; 32] =
711        hex!("1d69f785f47d6f25cb901b131612c37edc5e89ee9ba2dac8c401ced40e340a39");
712
713    const G2_ADDXY: [u8; 32] =
714        hex!("5f443752a19e368f404b89abae20a386d2b534c424b93ededdbfd04d4c569e6b");
715    const G2_ADDXX: [u8; 32] =
716        hex!("a411bbd84ee92a6ee53e5ca9cb81bacc192c6ba406f6fdcb2b04d0ab9c42ae44");
717    const G2_ADDYY: [u8; 32] =
718        hex!("6a3dadfcaac134e8353dd3abf37d487b206ca28dfab1e0a9376649df748f1605");
719    const G2_ADDYX: [u8; 32] =
720        hex!("4fa25e5e6100a023d4923df385dd236749c6a7f8e68db55e0bd1e2263fc04d28");
721
722    const G2_MULXY: [u8; 32] =
723        hex!("5d704de3261290dbba39dbd14e6bc416025240fd1ed65ec982efed685ae41e8b");
724    const G2_MULXX: [u8; 32] =
725        hex!("705c9ca4b5ef465c4e5db80ca4880627a6d9d6bcefd4756496baba9d5eaa3304");
726    const G2_MULYY: [u8; 32] =
727        hex!("4e96eb3543aabf1e9a65cae24177b9d13b0f7fae9472145ba7ae2b14bb447aef");
728    const G2_MULYX: [u8; 32] =
729        hex!("5d7ba50d7eac49a00b18fee2069afd3cc9719993fa78271e66b7a3efed46ac8b");
730
731    const G2_MULGXY: [u8; 32] =
732        hex!("920ef6fb3a2acff52aa0c004c18feca149dfd33d98086f8f402ea9e0de303c49");
733    const G2_MULGXX: [u8; 32] =
734        hex!("1f97dd359f2b065d63e0987f5bea2f3dc865c2cc112d7d161b46b83451716fd8");
735    const G2_MULGYY: [u8; 32] =
736        hex!("614881d4d05fef3173a4990465876c5200f58c5015e13354b23ae401c20c4aef");
737    const G2_MULGYX: [u8; 32] =
738        hex!("18a22e02b7d395a49f0646a79438e79cd37c32f163fe8923c13d56bab668e8a7");
739
740    #[test]
741    fn test_g2() {
742        group_trials::<G2>();
743    }
744
745    #[test]
746    fn test_g2_dbl() {
747        let x = Fq2::new(
748            Fq::from_slice(&G2_P1XX).unwrap(),
749            Fq::from_slice(&G2_P1XY).unwrap(),
750        );
751        let y = Fq2::new(
752            Fq::from_slice(&G2_P1YX).unwrap(),
753            Fq::from_slice(&G2_P1YY).unwrap(),
754        );
755        let p1 = G2::new(x, y, Fq2::one());
756        let x = Fq2::new(
757            Fq::from_slice(&G2_DBLXX).unwrap(),
758            Fq::from_slice(&G2_DBLXY).unwrap(),
759        );
760        let y = Fq2::new(
761            Fq::from_slice(&G2_DBLYX).unwrap(),
762            Fq::from_slice(&G2_DBLYY).unwrap(),
763        );
764        let r = G2::new(x, y, Fq2::one());
765        assert_eq!(r, p1.double());
766    }
767
768    #[test]
769    fn test_g2_add() {
770        let x = Fq2::new(
771            Fq::from_slice(&G2_P1XX).unwrap(),
772            Fq::from_slice(&G2_P1XY).unwrap(),
773        );
774        let y = Fq2::new(
775            Fq::from_slice(&G2_P1YX).unwrap(),
776            Fq::from_slice(&G2_P1YY).unwrap(),
777        );
778        let p1 = G2::new(x, y, Fq2::one());
779        let x = Fq2::new(
780            Fq::from_slice(&G2_P2XX).unwrap(),
781            Fq::from_slice(&G2_P2XY).unwrap(),
782        );
783        let y = Fq2::new(
784            Fq::from_slice(&G2_P2YX).unwrap(),
785            Fq::from_slice(&G2_P2YY).unwrap(),
786        );
787        let p2 = G2::new(x, y, Fq2::one());
788        let x = Fq2::new(
789            Fq::from_slice(&G2_ADDXX).unwrap(),
790            Fq::from_slice(&G2_ADDXY).unwrap(),
791        );
792        let y = Fq2::new(
793            Fq::from_slice(&G2_ADDYX).unwrap(),
794            Fq::from_slice(&G2_ADDYY).unwrap(),
795        );
796        let r = G2::new(x, y, Fq2::one());
797        assert_eq!(r, p1 + p2);
798        test_projective_double_and_add::<G2>();
799    }
800
801    #[test]
802    fn test_g2_mul() {
803        let x = Fq2::new(
804            Fq::from_slice(&G2_P1XX).unwrap(),
805            Fq::from_slice(&G2_P1XY).unwrap(),
806        );
807        let y = Fq2::new(
808            Fq::from_slice(&G2_P1YX).unwrap(),
809            Fq::from_slice(&G2_P1YY).unwrap(),
810        );
811        let p1 = G2::new(x, y, Fq2::one());
812        let x = Fq2::new(
813            Fq::from_slice(&G2_MULXX).unwrap(),
814            Fq::from_slice(&G2_MULXY).unwrap(),
815        );
816        let y = Fq2::new(
817            Fq::from_slice(&G2_MULYX).unwrap(),
818            Fq::from_slice(&G2_MULYY).unwrap(),
819        );
820        let r = G2::new(x, y, Fq2::one());
821        let k = Fr::from_slice(&HEX_IV).unwrap();
822        assert_eq!(r, p1 * k);
823    }
824
825    #[test]
826    fn test_g2_mulg() {
827        let x = Fq2::new(
828            Fq::from_slice(&G2_MULGXX).unwrap(),
829            Fq::from_slice(&G2_MULGXY).unwrap(),
830        );
831        let y = Fq2::new(
832            Fq::from_slice(&G2_MULGYX).unwrap(),
833            Fq::from_slice(&G2_MULGYY).unwrap(),
834        );
835        let r = G2::new(x, y, Fq2::one());
836        let k = Fr::from_slice(&HEX_IV).unwrap();
837        assert_eq!(r, G2::one() * k);
838    }
839
840    #[test]
841    fn affine_fail() {
842        let res = AffineG1::new(Fq::one(), Fq::one());
843        assert!(
844            res.is_err(),
845            "Affine initialization should fail because the point is not on curve"
846        );
847        let res = AffineG1::new(Fq::one(), G1Params::coeff_b());
848        assert!(
849            res.is_err(),
850            "Affine initialization should fail because the point is not on curve"
851        );
852    }
853
854    #[test]
855    fn affine_ok() {
856        let res = AffineG1::new(
857            Fq::from_slice(&SM9_P1X).unwrap(),
858            Fq::from_slice(&SM9_P1Y).unwrap(),
859        );
860        assert!(
861            res.is_ok(),
862            "Affine initialization should be ok because the point is on the curve"
863        );
864    }
865    #[test]
866    fn test_projective_point_equality() {
867        let a = G1::one();
868        let b = G1::zero();
869
870        assert!(a == a);
871        assert!(b == b);
872        assert!(a != b);
873        assert!(b != a);
874    }
875    #[test]
876    fn test_y_at_point_at_infinity() {
877        assert!(G1::zero().y == Fq::one());
878        assert!((-G1::zero()).y == Fq::one());
879
880        assert!(G2::zero().y == Fq2::one());
881        assert!((-G2::zero()).y == Fq2::one());
882    }
883}