Skip to main content

dcrypt_algorithms/ec/bls12_381/
g2.rs

1//! G₂ group implementation for BLS12-381.
2
3use crate::error::{Error, Result};
4use core::borrow::Borrow;
5use core::fmt;
6use core::iter::Sum;
7use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign};
8use rand_core::RngCore;
9use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
10
11#[cfg(feature = "alloc")]
12use alloc::vec;
13#[cfg(feature = "alloc")]
14use alloc::vec::Vec;
15
16use super::field::fp::Fp;
17use super::field::fp2::Fp2;
18use super::Scalar;
19
20/// G₂ affine point representation.
21#[derive(Copy, Clone, Debug)]
22pub struct G2Affine {
23    pub(crate) x: Fp2,
24    pub(crate) y: Fp2,
25    infinity: Choice,
26}
27
28impl Default for G2Affine {
29    fn default() -> G2Affine {
30        G2Affine::identity()
31    }
32}
33
34#[cfg(feature = "zeroize")]
35impl zeroize::DefaultIsZeroes for G2Affine {}
36
37impl fmt::Display for G2Affine {
38    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
39        write!(f, "{:?}", self)
40    }
41}
42
43impl<'a> From<&'a G2Projective> for G2Affine {
44    fn from(p: &'a G2Projective) -> G2Affine {
45        let zinv = p.z.invert().unwrap_or(Fp2::zero());
46        let x = p.x * zinv;
47        let y = p.y * zinv;
48
49        let tmp = G2Affine {
50            x,
51            y,
52            infinity: Choice::from(0u8),
53        };
54
55        G2Affine::conditional_select(&tmp, &G2Affine::identity(), zinv.is_zero())
56    }
57}
58
59impl From<G2Projective> for G2Affine {
60    fn from(p: G2Projective) -> G2Affine {
61        G2Affine::from(&p)
62    }
63}
64
65impl ConstantTimeEq for G2Affine {
66    fn ct_eq(&self, other: &Self) -> Choice {
67        (self.infinity & other.infinity)
68            | ((!self.infinity)
69                & (!other.infinity)
70                & self.x.ct_eq(&other.x)
71                & self.y.ct_eq(&other.y))
72    }
73}
74
75impl ConditionallySelectable for G2Affine {
76    fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
77        G2Affine {
78            x: Fp2::conditional_select(&a.x, &b.x, choice),
79            y: Fp2::conditional_select(&a.y, &b.y, choice),
80            infinity: Choice::conditional_select(&a.infinity, &b.infinity, choice),
81        }
82    }
83}
84
85impl Eq for G2Affine {}
86impl PartialEq for G2Affine {
87    #[inline]
88    fn eq(&self, other: &Self) -> bool {
89        bool::from(self.ct_eq(other))
90    }
91}
92
93impl<'a> Neg for &'a G2Affine {
94    type Output = G2Affine;
95
96    #[inline]
97    fn neg(self) -> G2Affine {
98        G2Affine {
99            x: self.x,
100            y: Fp2::conditional_select(&-self.y, &Fp2::one(), self.infinity),
101            infinity: self.infinity,
102        }
103    }
104}
105
106impl Neg for G2Affine {
107    type Output = G2Affine;
108
109    #[inline]
110    fn neg(self) -> G2Affine {
111        -&self
112    }
113}
114
115impl<'a, 'b> Add<&'b G2Projective> for &'a G2Affine {
116    type Output = G2Projective;
117
118    #[inline]
119    fn add(self, rhs: &'b G2Projective) -> G2Projective {
120        rhs.add_mixed(self)
121    }
122}
123
124impl<'a, 'b> Add<&'b G2Affine> for &'a G2Projective {
125    type Output = G2Projective;
126
127    #[inline]
128    fn add(self, rhs: &'b G2Affine) -> G2Projective {
129        self.add_mixed(rhs)
130    }
131}
132
133impl<'a, 'b> Sub<&'b G2Projective> for &'a G2Affine {
134    type Output = G2Projective;
135
136    #[inline]
137    fn sub(self, rhs: &'b G2Projective) -> G2Projective {
138        self + &(-rhs)
139    }
140}
141
142impl<'a, 'b> Sub<&'b G2Affine> for &'a G2Projective {
143    type Output = G2Projective;
144
145    #[inline]
146    fn sub(self, rhs: &'b G2Affine) -> G2Projective {
147        self + &(-rhs)
148    }
149}
150
151impl<T> Sum<T> for G2Projective
152where
153    T: Borrow<G2Projective>,
154{
155    fn sum<I>(iter: I) -> Self
156    where
157        I: Iterator<Item = T>,
158    {
159        iter.fold(Self::identity(), |acc, item| acc + item.borrow())
160    }
161}
162
163// Binop implementations for G2Projective + G2Affine
164impl<'b> Add<&'b G2Affine> for G2Projective {
165    type Output = G2Projective;
166    #[inline]
167    fn add(self, rhs: &'b G2Affine) -> G2Projective {
168        &self + rhs
169    }
170}
171impl<'a> Add<G2Affine> for &'a G2Projective {
172    type Output = G2Projective;
173    #[inline]
174    fn add(self, rhs: G2Affine) -> G2Projective {
175        self + &rhs
176    }
177}
178impl Add<G2Affine> for G2Projective {
179    type Output = G2Projective;
180    #[inline]
181    fn add(self, rhs: G2Affine) -> G2Projective {
182        &self + &rhs
183    }
184}
185impl<'b> Sub<&'b G2Affine> for G2Projective {
186    type Output = G2Projective;
187    #[inline]
188    fn sub(self, rhs: &'b G2Affine) -> G2Projective {
189        &self - rhs
190    }
191}
192impl<'a> Sub<G2Affine> for &'a G2Projective {
193    type Output = G2Projective;
194    #[inline]
195    fn sub(self, rhs: G2Affine) -> G2Projective {
196        self - &rhs
197    }
198}
199impl Sub<G2Affine> for G2Projective {
200    type Output = G2Projective;
201    #[inline]
202    fn sub(self, rhs: G2Affine) -> G2Projective {
203        &self - &rhs
204    }
205}
206impl SubAssign<G2Affine> for G2Projective {
207    #[inline]
208    fn sub_assign(&mut self, rhs: G2Affine) {
209        *self = &*self - &rhs;
210    }
211}
212impl AddAssign<G2Affine> for G2Projective {
213    #[inline]
214    fn add_assign(&mut self, rhs: G2Affine) {
215        *self = &*self + &rhs;
216    }
217}
218impl<'b> SubAssign<&'b G2Affine> for G2Projective {
219    #[inline]
220    fn sub_assign(&mut self, rhs: &'b G2Affine) {
221        *self = &*self - rhs;
222    }
223}
224impl<'b> AddAssign<&'b G2Affine> for G2Projective {
225    #[inline]
226    fn add_assign(&mut self, rhs: &'b G2Affine) {
227        *self = &*self + rhs;
228    }
229}
230
231// Binop implementations for G2Affine + G2Projective
232impl<'b> Add<&'b G2Projective> for G2Affine {
233    type Output = G2Projective;
234    #[inline]
235    fn add(self, rhs: &'b G2Projective) -> G2Projective {
236        &self + rhs
237    }
238}
239impl<'a> Add<G2Projective> for &'a G2Affine {
240    type Output = G2Projective;
241    #[inline]
242    fn add(self, rhs: G2Projective) -> G2Projective {
243        self + &rhs
244    }
245}
246impl Add<G2Projective> for G2Affine {
247    type Output = G2Projective;
248    #[inline]
249    fn add(self, rhs: G2Projective) -> G2Projective {
250        &self + &rhs
251    }
252}
253impl<'b> Sub<&'b G2Projective> for G2Affine {
254    type Output = G2Projective;
255    #[inline]
256    fn sub(self, rhs: &'b G2Projective) -> G2Projective {
257        &self - rhs
258    }
259}
260impl<'a> Sub<G2Projective> for &'a G2Affine {
261    type Output = G2Projective;
262    #[inline]
263    fn sub(self, rhs: G2Projective) -> G2Projective {
264        self - &rhs
265    }
266}
267impl Sub<G2Projective> for G2Affine {
268    type Output = G2Projective;
269    #[inline]
270    fn sub(self, rhs: G2Projective) -> G2Projective {
271        &self - &rhs
272    }
273}
274
275/// Curve constant B = 4(u+1)
276const B: Fp2 = Fp2 {
277    c0: Fp::from_raw_unchecked([
278        0xaa27_0000_000c_fff3,
279        0x53cc_0032_fc34_000a,
280        0x478f_e97a_6b0a_807f,
281        0xb1d3_7ebe_e6ba_24d7,
282        0x8ec9_733b_bf78_ab2f,
283        0x09d6_4551_3d83_de7e,
284    ]),
285    c1: Fp::from_raw_unchecked([
286        0xaa27_0000_000c_fff3,
287        0x53cc_0032_fc34_000a,
288        0x478f_e97a_6b0a_807f,
289        0xb1d3_7ebe_e6ba_24d7,
290        0x8ec9_733b_bf78_ab2f,
291        0x09d6_4551_3d83_de7e,
292    ]),
293};
294
295/// 3B for efficient doubling
296const B3: Fp2 = Fp2::add(&Fp2::add(&B, &B), &B);
297
298#[inline(always)]
299fn mul_by_3b(a: Fp2) -> Fp2 {
300    a * B3
301}
302
303impl G2Affine {
304    /// Point at infinity.
305    pub fn identity() -> G2Affine {
306        G2Affine {
307            x: Fp2::zero(),
308            y: Fp2::one(),
309            infinity: Choice::from(1u8),
310        }
311    }
312
313    /// Fixed generator.
314    pub fn generator() -> G2Affine {
315        G2Affine {
316            x: Fp2 {
317                c0: Fp::from_raw_unchecked([
318                    0xf5f2_8fa2_0294_0a10,
319                    0xb3f5_fb26_87b4_961a,
320                    0xa1a8_93b5_3e2a_e580,
321                    0x9894_999d_1a3c_aee9,
322                    0x6f67_b763_1863_366b,
323                    0x0581_9192_4350_bcd7,
324                ]),
325                c1: Fp::from_raw_unchecked([
326                    0xa5a9_c075_9e23_f606,
327                    0xaaa0_c59d_bccd_60c3,
328                    0x3bb1_7e18_e286_7806,
329                    0x1b1a_b6cc_8541_b367,
330                    0xc2b6_ed0e_f215_8547,
331                    0x1192_2a09_7360_edf3,
332                ]),
333            },
334            y: Fp2 {
335                c0: Fp::from_raw_unchecked([
336                    0x4c73_0af8_6049_4c4a,
337                    0x597c_fa1f_5e36_9c5a,
338                    0xe7e6_856c_aa0a_635a,
339                    0xbbef_b5e9_6e0d_495f,
340                    0x07d3_a975_f0ef_25a2,
341                    0x0083_fd8e_7e80_dae5,
342                ]),
343                c1: Fp::from_raw_unchecked([
344                    0xadc0_fc92_df64_b05d,
345                    0x18aa_270a_2b14_61dc,
346                    0x86ad_ac6a_3be4_eba0,
347                    0x7949_5c4e_c93d_a33a,
348                    0xe717_5850_a43c_caed,
349                    0x0b2b_c2a1_63de_1bf2,
350                ]),
351            },
352            infinity: Choice::from(0u8),
353        }
354    }
355
356    /// Compress to 96 bytes.
357    pub fn to_compressed(&self) -> [u8; 96] {
358        let x = Fp2::conditional_select(&self.x, &Fp2::zero(), self.infinity);
359        let mut res = [0; 96];
360
361        res[0..48].copy_from_slice(&x.c1.to_bytes());
362        res[48..96].copy_from_slice(&x.c0.to_bytes());
363
364        res[0] |= 1u8 << 7; // Compression flag
365        res[0] |= u8::conditional_select(&0u8, &(1u8 << 6), self.infinity); // Infinity flag
366        res[0] |= u8::conditional_select(
367            &0u8,
368            &(1u8 << 5),
369            (!self.infinity) & self.y.lexicographically_largest(), // Sort flag
370        );
371        res
372    }
373
374    /// Serialize to 192 bytes uncompressed.
375    pub fn to_uncompressed(&self) -> [u8; 192] {
376        let mut res = [0; 192];
377        let x = Fp2::conditional_select(&self.x, &Fp2::zero(), self.infinity);
378        let y = Fp2::conditional_select(&self.y, &Fp2::zero(), self.infinity);
379
380        res[0..48].copy_from_slice(&x.c1.to_bytes());
381        res[48..96].copy_from_slice(&x.c0.to_bytes());
382        res[96..144].copy_from_slice(&y.c1.to_bytes());
383        res[144..192].copy_from_slice(&y.c0.to_bytes());
384
385        res[0] |= u8::conditional_select(&0u8, &(1u8 << 6), self.infinity);
386        res
387    }
388
389    /// Deserialize from uncompressed bytes with validation.
390    pub fn from_uncompressed(bytes: &[u8; 192]) -> CtOption<Self> {
391        Self::from_uncompressed_unchecked(bytes)
392            .and_then(|p| CtOption::new(p, p.is_on_curve() & p.is_torsion_free()))
393    }
394
395    /// Deserialize from uncompressed bytes without validation.
396    pub fn from_uncompressed_unchecked(bytes: &[u8; 192]) -> CtOption<Self> {
397        let compression_flag_set = Choice::from((bytes[0] >> 7) & 1);
398        let infinity_flag_set = Choice::from((bytes[0] >> 6) & 1);
399        let sort_flag_set = Choice::from((bytes[0] >> 5) & 1);
400
401        let xc1 = {
402            let mut tmp = [0; 48];
403            tmp.copy_from_slice(&bytes[0..48]);
404            tmp[0] &= 0b0001_1111;
405            Fp::from_bytes(&tmp)
406        };
407        let xc0 = Fp::from_bytes(<&[u8; 48]>::try_from(&bytes[48..96]).unwrap());
408        let yc1 = Fp::from_bytes(<&[u8; 48]>::try_from(&bytes[96..144]).unwrap());
409        let yc0 = Fp::from_bytes(<&[u8; 48]>::try_from(&bytes[144..192]).unwrap());
410
411        xc1.and_then(|xc1| {
412            xc0.and_then(|xc0| {
413                yc1.and_then(|yc1| {
414                    yc0.and_then(|yc0| {
415                        let x = Fp2 { c0: xc0, c1: xc1 };
416                        let y = Fp2 { c0: yc0, c1: yc1 };
417
418                        let p = G2Affine::conditional_select(
419                            &G2Affine {
420                                x,
421                                y,
422                                infinity: infinity_flag_set,
423                            },
424                            &G2Affine::identity(),
425                            infinity_flag_set,
426                        );
427                        CtOption::new(
428                            p,
429                            ((!infinity_flag_set)
430                                | (infinity_flag_set & x.is_zero() & y.is_zero()))
431                                & (!compression_flag_set)
432                                & (!sort_flag_set),
433                        )
434                    })
435                })
436            })
437        })
438    }
439
440    /// Deserialize from compressed bytes with validation.
441    pub fn from_compressed(bytes: &[u8; 96]) -> CtOption<Self> {
442        Self::from_compressed_unchecked(bytes).and_then(|p| CtOption::new(p, p.is_torsion_free()))
443    }
444
445    /// Deserialize from compressed bytes without validation.
446    pub fn from_compressed_unchecked(bytes: &[u8; 96]) -> CtOption<Self> {
447        let compression_flag_set = Choice::from((bytes[0] >> 7) & 1);
448        let infinity_flag_set = Choice::from((bytes[0] >> 6) & 1);
449        let sort_flag_set = Choice::from((bytes[0] >> 5) & 1);
450
451        let xc1 = {
452            let mut tmp = [0; 48];
453            tmp.copy_from_slice(&bytes[0..48]);
454            tmp[0] &= 0b0001_1111;
455            Fp::from_bytes(&tmp)
456        };
457        let xc0 = Fp::from_bytes(<&[u8; 48]>::try_from(&bytes[48..96]).unwrap());
458
459        xc1.and_then(|xc1| {
460            xc0.and_then(|xc0| {
461                let x = Fp2 { c0: xc0, c1: xc1 };
462                CtOption::new(
463                    G2Affine::identity(),
464                    infinity_flag_set & compression_flag_set & (!sort_flag_set) & x.is_zero(),
465                )
466                .or_else(|| {
467                    ((x.square() * x) + B).sqrt().and_then(|y| {
468                        let y = Fp2::conditional_select(
469                            &y,
470                            &-y,
471                            y.lexicographically_largest() ^ sort_flag_set,
472                        );
473                        CtOption::new(
474                            G2Affine {
475                                x,
476                                y,
477                                infinity: infinity_flag_set,
478                            },
479                            (!infinity_flag_set) & compression_flag_set,
480                        )
481                    })
482                })
483            })
484        })
485    }
486
487    /// Check if point at infinity.
488    #[inline]
489    pub fn is_identity(&self) -> Choice {
490        self.infinity
491    }
492
493    /// Check if on curve y² = x³ + B.
494    pub fn is_on_curve(&self) -> Choice {
495        (self.y.square() - (self.x.square() * self.x)).ct_eq(&B) | self.infinity
496    }
497
498    /// Check subgroup membership using psi endomorphism.
499    pub fn is_torsion_free(&self) -> Choice {
500        // Algorithm from Section 4 of https://eprint.iacr.org/2021/1130
501        // Updated proof: https://eprint.iacr.org/2022/352
502        let p = G2Projective::from(*self);
503        p.psi().ct_eq(&p.mul_by_x())
504    }
505}
506
507/// G₂ projective point representation.
508#[derive(Copy, Clone, Debug)]
509pub struct G2Projective {
510    pub(crate) x: Fp2,
511    pub(crate) y: Fp2,
512    pub(crate) z: Fp2,
513}
514
515impl Default for G2Projective {
516    fn default() -> G2Projective {
517        G2Projective::identity()
518    }
519}
520
521#[cfg(feature = "zeroize")]
522impl zeroize::DefaultIsZeroes for G2Projective {}
523
524impl fmt::Display for G2Projective {
525    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
526        write!(f, "{:?}", self)
527    }
528}
529
530impl<'a> From<&'a G2Affine> for G2Projective {
531    fn from(p: &'a G2Affine) -> G2Projective {
532        G2Projective {
533            x: p.x,
534            y: p.y,
535            z: Fp2::conditional_select(&Fp2::one(), &Fp2::zero(), p.infinity),
536        }
537    }
538}
539
540impl From<G2Affine> for G2Projective {
541    fn from(p: G2Affine) -> G2Projective {
542        G2Projective::from(&p)
543    }
544}
545
546impl ConstantTimeEq for G2Projective {
547    fn ct_eq(&self, other: &Self) -> Choice {
548        let x1 = self.x * other.z;
549        let x2 = other.x * self.z;
550        let y1 = self.y * other.z;
551        let y2 = other.y * self.z;
552        let self_is_zero = self.z.is_zero();
553        let other_is_zero = other.z.is_zero();
554
555        (self_is_zero & other_is_zero)
556            | ((!self_is_zero) & (!other_is_zero) & x1.ct_eq(&x2) & y1.ct_eq(&y2))
557    }
558}
559
560impl ConditionallySelectable for G2Projective {
561    fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
562        G2Projective {
563            x: Fp2::conditional_select(&a.x, &b.x, choice),
564            y: Fp2::conditional_select(&a.y, &b.y, choice),
565            z: Fp2::conditional_select(&a.z, &b.z, choice),
566        }
567    }
568}
569
570impl Eq for G2Projective {}
571impl PartialEq for G2Projective {
572    #[inline]
573    fn eq(&self, other: &Self) -> bool {
574        bool::from(self.ct_eq(other))
575    }
576}
577
578impl<'a> Neg for &'a G2Projective {
579    type Output = G2Projective;
580
581    #[inline]
582    fn neg(self) -> G2Projective {
583        G2Projective {
584            x: self.x,
585            y: -self.y,
586            z: self.z,
587        }
588    }
589}
590
591impl Neg for G2Projective {
592    type Output = G2Projective;
593
594    #[inline]
595    fn neg(self) -> G2Projective {
596        -&self
597    }
598}
599
600impl<'a, 'b> Add<&'b G2Projective> for &'a G2Projective {
601    type Output = G2Projective;
602
603    #[inline]
604    fn add(self, rhs: &'b G2Projective) -> G2Projective {
605        self.add(rhs)
606    }
607}
608
609impl<'a, 'b> Sub<&'b G2Projective> for &'a G2Projective {
610    type Output = G2Projective;
611
612    #[inline]
613    fn sub(self, rhs: &'b G2Projective) -> G2Projective {
614        self + &(-rhs)
615    }
616}
617
618impl<'a, 'b> Mul<&'b Scalar> for &'a G2Projective {
619    type Output = G2Projective;
620
621    fn mul(self, other: &'b Scalar) -> Self::Output {
622        self.multiply(&other.to_bytes())
623    }
624}
625
626impl<'a, 'b> Mul<&'b G2Projective> for &'a Scalar {
627    type Output = G2Projective;
628
629    #[inline]
630    fn mul(self, rhs: &'b G2Projective) -> Self::Output {
631        rhs * self
632    }
633}
634
635impl<'a, 'b> Mul<&'b Scalar> for &'a G2Affine {
636    type Output = G2Projective;
637
638    fn mul(self, other: &'b Scalar) -> Self::Output {
639        G2Projective::from(self).multiply(&other.to_bytes())
640    }
641}
642
643impl<'a, 'b> Mul<&'b G2Affine> for &'a Scalar {
644    type Output = G2Projective;
645
646    #[inline]
647    fn mul(self, rhs: &'b G2Affine) -> Self::Output {
648        rhs * self
649    }
650}
651
652// Binop implementations for G2Projective
653impl<'b> Add<&'b G2Projective> for G2Projective {
654    type Output = G2Projective;
655    #[inline]
656    fn add(self, rhs: &'b G2Projective) -> G2Projective {
657        &self + rhs
658    }
659}
660impl<'a> Add<G2Projective> for &'a G2Projective {
661    type Output = G2Projective;
662    #[inline]
663    fn add(self, rhs: G2Projective) -> G2Projective {
664        self + &rhs
665    }
666}
667impl Add<G2Projective> for G2Projective {
668    type Output = G2Projective;
669    #[inline]
670    fn add(self, rhs: G2Projective) -> G2Projective {
671        &self + &rhs
672    }
673}
674impl<'b> Sub<&'b G2Projective> for G2Projective {
675    type Output = G2Projective;
676    #[inline]
677    fn sub(self, rhs: &'b G2Projective) -> G2Projective {
678        &self - rhs
679    }
680}
681impl<'a> Sub<G2Projective> for &'a G2Projective {
682    type Output = G2Projective;
683    #[inline]
684    fn sub(self, rhs: G2Projective) -> G2Projective {
685        self - &rhs
686    }
687}
688impl Sub<G2Projective> for G2Projective {
689    type Output = G2Projective;
690    #[inline]
691    fn sub(self, rhs: G2Projective) -> G2Projective {
692        &self - &rhs
693    }
694}
695impl SubAssign<G2Projective> for G2Projective {
696    #[inline]
697    fn sub_assign(&mut self, rhs: G2Projective) {
698        *self = &*self - &rhs;
699    }
700}
701impl AddAssign<G2Projective> for G2Projective {
702    #[inline]
703    fn add_assign(&mut self, rhs: G2Projective) {
704        *self = &*self + &rhs;
705    }
706}
707impl<'b> SubAssign<&'b G2Projective> for G2Projective {
708    #[inline]
709    fn sub_assign(&mut self, rhs: &'b G2Projective) {
710        *self = &*self - rhs;
711    }
712}
713impl<'b> AddAssign<&'b G2Projective> for G2Projective {
714    #[inline]
715    fn add_assign(&mut self, rhs: &'b G2Projective) {
716        *self = &*self + rhs;
717    }
718}
719
720// Scalar multiplication implementations
721impl<'b> Mul<&'b Scalar> for G2Projective {
722    type Output = G2Projective;
723    #[inline]
724    fn mul(self, rhs: &'b Scalar) -> G2Projective {
725        &self * rhs
726    }
727}
728impl<'a> Mul<Scalar> for &'a G2Projective {
729    type Output = G2Projective;
730    #[inline]
731    fn mul(self, rhs: Scalar) -> G2Projective {
732        self * &rhs
733    }
734}
735impl Mul<Scalar> for G2Projective {
736    type Output = G2Projective;
737    #[inline]
738    fn mul(self, rhs: Scalar) -> G2Projective {
739        &self * &rhs
740    }
741}
742impl MulAssign<Scalar> for G2Projective {
743    #[inline]
744    fn mul_assign(&mut self, rhs: Scalar) {
745        *self = &*self * &rhs;
746    }
747}
748impl<'b> MulAssign<&'b Scalar> for G2Projective {
749    #[inline]
750    fn mul_assign(&mut self, rhs: &'b Scalar) {
751        *self = &*self * rhs;
752    }
753}
754
755// Mixed scalar multiplication for G2Affine
756impl<'b> Mul<&'b Scalar> for G2Affine {
757    type Output = G2Projective;
758    #[inline]
759    fn mul(self, rhs: &'b Scalar) -> G2Projective {
760        &self * rhs
761    }
762}
763impl<'a> Mul<Scalar> for &'a G2Affine {
764    type Output = G2Projective;
765    #[inline]
766    fn mul(self, rhs: Scalar) -> G2Projective {
767        self * &rhs
768    }
769}
770impl Mul<Scalar> for G2Affine {
771    type Output = G2Projective;
772    #[inline]
773    fn mul(self, rhs: Scalar) -> G2Projective {
774        &self * &rhs
775    }
776}
777
778// Scalar * G2Affine
779impl<'b> Mul<&'b G2Affine> for Scalar {
780    type Output = G2Projective;
781    #[inline]
782    fn mul(self, rhs: &'b G2Affine) -> G2Projective {
783        &self * rhs
784    }
785}
786impl<'a> Mul<G2Affine> for &'a Scalar {
787    type Output = G2Projective;
788    #[inline]
789    fn mul(self, rhs: G2Affine) -> G2Projective {
790        self * &rhs
791    }
792}
793impl Mul<G2Affine> for Scalar {
794    type Output = G2Projective;
795    #[inline]
796    fn mul(self, rhs: G2Affine) -> G2Projective {
797        &self * &rhs
798    }
799}
800
801// Scalar * G2Projective
802impl<'b> Mul<&'b G2Projective> for Scalar {
803    type Output = G2Projective;
804    #[inline]
805    fn mul(self, rhs: &'b G2Projective) -> G2Projective {
806        &self * rhs
807    }
808}
809impl<'a> Mul<G2Projective> for &'a Scalar {
810    type Output = G2Projective;
811    #[inline]
812    fn mul(self, rhs: G2Projective) -> G2Projective {
813        self * &rhs
814    }
815}
816impl Mul<G2Projective> for Scalar {
817    type Output = G2Projective;
818    #[inline]
819    fn mul(self, rhs: G2Projective) -> G2Projective {
820        &self * &rhs
821    }
822}
823
824impl G2Projective {
825    /// Point at infinity.
826    pub fn identity() -> G2Projective {
827        G2Projective {
828            x: Fp2::zero(),
829            y: Fp2::one(),
830            z: Fp2::zero(),
831        }
832    }
833
834    /// Fixed generator.
835    pub fn generator() -> G2Projective {
836        G2Projective {
837            x: G2Affine::generator().x,
838            y: G2Affine::generator().y,
839            z: Fp2::one(),
840        }
841    }
842
843    /// Random non-identity element.
844    pub fn random(mut rng: impl RngCore) -> Self {
845        loop {
846            let x = Fp2::random(&mut rng);
847            let flip_sign = rng.next_u32() % 2 != 0;
848
849            let p = ((x.square() * x) + B).sqrt().map(|y| G2Affine {
850                x,
851                y: if flip_sign { -y } else { y },
852                infinity: 0.into(),
853            });
854
855            if p.is_some().into() {
856                let p_proj = G2Projective::from(p.unwrap());
857                let p_cleared = p_proj.clear_cofactor();
858                if !bool::from(p_cleared.is_identity()) {
859                    return p_cleared;
860                }
861            }
862        }
863    }
864
865    // ============================================================================
866    // START: New MSM Implementation
867    // ============================================================================
868
869    /// Multi-scalar multiplication using a variable-time Pippenger's algorithm.
870    ///
871    /// This method is faster for non-sensitive operations where timing side-channels
872    /// are not a concern, as it contains input-dependent branches.
873    ///
874    /// # Panics
875    /// Panics if `points.len() != scalars.len()`.
876    #[cfg(feature = "alloc")]
877    pub fn msm_vartime(points: &[G2Affine], scalars: &[Scalar]) -> Result<Self> {
878        if points.len() != scalars.len() {
879            return Err(Error::Parameter {
880                name: "points/scalars".into(),
881                reason: "Input slices must have the same length".into(),
882            });
883        }
884        Ok(Self::pippenger(points, scalars, true))
885    }
886
887    /// Multi-scalar multiplication using a constant-time Pippenger's algorithm.
888    ///
889    /// This method is suitable for cryptographic operations where resistance to
890    /// timing side-channels is required.
891    ///
892    /// # Panics
893    /// Panics if `points.len() != scalars.len()`.
894    #[cfg(feature = "alloc")]
895    pub fn msm(points: &[G2Affine], scalars: &[Scalar]) -> Result<Self> {
896        if points.len() != scalars.len() {
897            return Err(Error::Parameter {
898                name: "points/scalars".into(),
899                reason: "Input slices must have the same length".into(),
900            });
901        }
902        Ok(Self::pippenger(points, scalars, false))
903    }
904
905    /// Internal Pippenger's algorithm implementation.
906    #[cfg(feature = "alloc")]
907    fn pippenger(points: &[G2Affine], scalars: &[Scalar], is_vartime: bool) -> Self {
908        if points.is_empty() {
909            return Self::identity();
910        }
911
912        let num_entries = points.len();
913        let scalar_bits = 255; // BLS12-381 scalar size
914
915        // 1. Choose window size `c`.
916        // If constant-time, we limit window size to avoid excessive bucket scanning.
917        let c = if is_vartime {
918            if num_entries < 32 {
919                3
920            } else {
921                // Integer log2 equivalent: floor(log2(n))
922                // Works in no_std without libm
923                let log2 = (usize::BITS - num_entries.leading_zeros() - 1) as usize;
924                log2 + 2
925            }
926        } else {
927            // Fixed window size for constant time to ensure predictable performance.
928            // c=4 means 16 buckets.
929            4
930        };
931
932        let num_windows = (scalar_bits + c - 1) / c;
933        let num_buckets = 1 << c;
934        let mut global_acc = Self::identity();
935
936        // 2. Iterate through each window
937        for w in (0..num_windows).rev() {
938            let mut window_acc = Self::identity();
939            let mut buckets = vec![Self::identity(); num_buckets];
940
941            // 3. Populate buckets for the current window
942            for i in 0..num_entries {
943                let scalar_bytes = scalars[i].to_bytes();
944
945                // Extract c-bit window from scalar
946                let mut k = 0;
947                for bit_idx in 0..c {
948                    let total_bit_idx = w * c + bit_idx;
949                    if total_bit_idx < scalar_bits {
950                        let byte_idx = total_bit_idx / 8;
951                        let inner_bit_idx = total_bit_idx % 8;
952                        let byte = scalar_bytes[byte_idx];
953                        let bit = (byte >> inner_bit_idx) & 1;
954                        k |= (bit as usize) << bit_idx;
955                    }
956                }
957
958                if is_vartime {
959                    if k > 0 {
960                        buckets[k - 1] = buckets[k - 1].add_mixed(&points[i]);
961                    }
962                } else {
963                    // Constant-time bucket accumulation
964                    // Iterate all buckets, conditionally add point if bucket index matches k-1.
965                    // If k=0 (scalar chunk is 0), we don't add to any bucket (add identity).
966
967                    for b in 0..num_buckets {
968                        let bucket_idx = b + 1;
969                        // Check if k == bucket_idx in constant time using subtle
970                        let choice = k.ct_eq(&bucket_idx);
971
972                        let res = buckets[b].add_mixed(&points[i]);
973                        buckets[b] = G2Projective::conditional_select(&buckets[b], &res, choice);
974                    }
975                }
976            }
977
978            // 4. Sum up buckets to get the window result
979            let mut running_sum = Self::identity();
980            for i in (0..num_buckets).rev() {
981                running_sum = running_sum.add(&buckets[i]);
982                window_acc = window_acc.add(&running_sum);
983            }
984
985            // 5. Add to global accumulator
986            global_acc = global_acc.add(&window_acc);
987
988            // Scale accumulator for next window if not the last one
989            if w > 0 {
990                for _ in 0..c {
991                    global_acc = global_acc.double();
992                }
993            }
994        }
995
996        global_acc
997    }
998
999    // ============================================================================
1000    // END: New MSM Implementation
1001    // ============================================================================
1002
1003    /// Point doubling.
1004    pub fn double(&self) -> G2Projective {
1005        let t0 = self.y.square();
1006        let z3 = t0 + t0;
1007        let z3 = z3 + z3;
1008        let z3 = z3 + z3;
1009        let t1 = self.y * self.z;
1010        let t2 = self.z.square();
1011        let t2 = mul_by_3b(t2);
1012        let x3 = t2 * z3;
1013        let y3 = t0 + t2;
1014        let z3 = t1 * z3;
1015        let t1 = t2 + t2;
1016        let t2 = t1 + t2;
1017        let t0 = t0 - t2;
1018        let y3 = t0 * y3;
1019        let y3 = x3 + y3;
1020        let t1 = self.x * self.y;
1021        let x3 = t0 * t1;
1022        let x3 = x3 + x3;
1023
1024        let tmp = G2Projective {
1025            x: x3,
1026            y: y3,
1027            z: z3,
1028        };
1029        G2Projective::conditional_select(&tmp, &G2Projective::identity(), self.is_identity())
1030    }
1031
1032    /// Point addition.
1033    pub fn add(&self, rhs: &G2Projective) -> G2Projective {
1034        let t0 = self.x * rhs.x;
1035        let t1 = self.y * rhs.y;
1036        let t2 = self.z * rhs.z;
1037        let t3 = self.x + self.y;
1038        let t4 = rhs.x + rhs.y;
1039        let t3 = t3 * t4;
1040        let t4 = t0 + t1;
1041        let t3 = t3 - t4;
1042        let t4 = self.y + self.z;
1043        let x3 = rhs.y + rhs.z;
1044        let t4 = t4 * x3;
1045        let x3 = t1 + t2;
1046        let t4 = t4 - x3;
1047        let x3 = self.x + self.z;
1048        let y3 = rhs.x + rhs.z;
1049        let x3 = x3 * y3;
1050        let y3 = t0 + t2;
1051        let y3 = x3 - y3;
1052        let x3 = t0 + t0;
1053        let t0 = x3 + t0;
1054        let t2 = mul_by_3b(t2);
1055        let z3 = t1 + t2;
1056        let t1 = t1 - t2;
1057        let y3 = mul_by_3b(y3);
1058        let x3 = t4 * y3;
1059        let t2 = t3 * t1;
1060        let x3 = t2 - x3;
1061        let y3 = y3 * t0;
1062        let t1 = t1 * z3;
1063        let y3 = t1 + y3;
1064        let t0 = t0 * t3;
1065        let z3 = z3 * t4;
1066        let z3 = z3 + t0;
1067
1068        G2Projective {
1069            x: x3,
1070            y: y3,
1071            z: z3,
1072        }
1073    }
1074
1075    /// Mixed addition with affine point.
1076    pub fn add_mixed(&self, rhs: &G2Affine) -> G2Projective {
1077        let t0 = self.x * rhs.x;
1078        let t1 = self.y * rhs.y;
1079        let t3 = rhs.x + rhs.y;
1080        let t4 = self.x + self.y;
1081        let t3 = t3 * t4;
1082        let t4 = t0 + t1;
1083        let t3 = t3 - t4;
1084        let t4 = rhs.y * self.z;
1085        let t4 = t4 + self.y;
1086        let y3 = rhs.x * self.z;
1087        let y3 = y3 + self.x;
1088        let x3 = t0 + t0;
1089        let t0 = x3 + t0;
1090        let t2 = mul_by_3b(self.z);
1091        let z3 = t1 + t2;
1092        let t1 = t1 - t2;
1093        let y3 = mul_by_3b(y3);
1094        let x3 = t4 * y3;
1095        let t2 = t3 * t1;
1096        let x3 = t2 - x3;
1097        let y3 = y3 * t0;
1098        let t1 = t1 * z3;
1099        let y3 = t1 + y3;
1100        let t0 = t0 * t3;
1101        let z3 = z3 * t4;
1102        let z3 = z3 + t0;
1103
1104        let tmp = G2Projective {
1105            x: x3,
1106            y: y3,
1107            z: z3,
1108        };
1109        G2Projective::conditional_select(&tmp, self, rhs.is_identity())
1110    }
1111
1112    /// Scalar multiplication.
1113    fn multiply(&self, by: &[u8; 32]) -> G2Projective {
1114        let mut acc = G2Projective::identity();
1115        for &byte in by.iter().rev() {
1116            for i in (0..8).rev() {
1117                acc = acc.double();
1118                let bit = Choice::from((byte >> i) & 1u8);
1119                acc = G2Projective::conditional_select(&acc, &(acc + self), bit);
1120            }
1121        }
1122        acc
1123    }
1124
1125    /// Clear cofactor.
1126    pub fn clear_cofactor(&self) -> G2Projective {
1127        let t1 = self.mul_by_x();
1128        let t2 = self.psi();
1129        self.double().psi2() + (t1 + t2).mul_by_x() - t1 - t2 - *self
1130    }
1131
1132    /// Multiply by curve parameter x.
1133    fn mul_by_x(&self) -> G2Projective {
1134        let mut xself = G2Projective::identity();
1135        let mut x = super::BLS_X >> 1;
1136        let mut acc = *self;
1137        while x != 0 {
1138            acc = acc.double();
1139            if x % 2 == 1 {
1140                xself += acc;
1141            }
1142            x >>= 1;
1143        }
1144        if super::BLS_X_IS_NEGATIVE {
1145            xself = -xself;
1146        }
1147        xself
1148    }
1149
1150    /// Apply psi endomorphism.
1151    fn psi(&self) -> G2Projective {
1152        // 1 / ((u+1) ^ ((q-1)/3))
1153        let psi_coeff_x = Fp2 {
1154            c0: Fp::zero(),
1155            c1: Fp::from_raw_unchecked([
1156                0x890d_c9e4_8675_45c3,
1157                0x2af3_2253_3285_a5d5,
1158                0x5088_0866_309b_7e2c,
1159                0xa20d_1b8c_7e88_1024,
1160                0x14e4_f04f_e2db_9068,
1161                0x14e5_6d3f_1564_853a,
1162            ]),
1163        };
1164        // 1 / ((u+1) ^ (p-1)/2)
1165        let psi_coeff_y = Fp2 {
1166            c0: Fp::from_raw_unchecked([
1167                0x3e2f_585d_a55c_9ad1,
1168                0x4294_213d_86c1_8183,
1169                0x3828_44c8_8b62_3732,
1170                0x92ad_2afd_1910_3e18,
1171                0x1d79_4e4f_ac7c_f0b9,
1172                0x0bd5_92fc_7d82_5ec8,
1173            ]),
1174            c1: Fp::from_raw_unchecked([
1175                0x7bcf_a7a2_5aa3_0fda,
1176                0xdc17_dec1_2a92_7e7c,
1177                0x2f08_8dd8_6b4e_bef1,
1178                0xd1ca_2087_da74_d4a7,
1179                0x2da2_5966_96ce_bc1d,
1180                0x0e2b_7eed_bbfd_87d2,
1181            ]),
1182        };
1183
1184        G2Projective {
1185            x: self.x.frobenius_map() * psi_coeff_x,
1186            y: self.y.frobenius_map() * psi_coeff_y,
1187            z: self.z.frobenius_map(),
1188        }
1189    }
1190
1191    /// Apply psi^2 endomorphism.
1192    fn psi2(&self) -> G2Projective {
1193        // 1 / 2 ^ ((q-1)/3)
1194        let psi2_coeff_x = Fp2 {
1195            c0: Fp::from_raw_unchecked([
1196                0xcd03_c9e4_8671_f071,
1197                0x5dab_2246_1fcd_a5d2,
1198                0x5870_42af_d385_1b95,
1199                0x8eb6_0ebe_01ba_cb9e,
1200                0x03f9_7d6e_83d0_50d2,
1201                0x18f0_2065_5463_8741,
1202            ]),
1203            c1: Fp::zero(),
1204        };
1205
1206        G2Projective {
1207            x: self.x * psi2_coeff_x,
1208            y: self.y.neg(),
1209            z: self.z,
1210        }
1211    }
1212
1213    /// Batch conversion to affine.
1214    pub fn batch_normalize(p: &[Self], q: &mut [G2Affine]) {
1215        assert_eq!(p.len(), q.len());
1216        let mut acc = Fp2::one();
1217        for (p, q) in p.iter().zip(q.iter_mut()) {
1218            q.x = acc;
1219            acc = Fp2::conditional_select(&(acc * p.z), &acc, p.is_identity());
1220        }
1221        acc = acc.invert().unwrap();
1222        for (p, q) in p.iter().rev().zip(q.iter_mut().rev()) {
1223            let skip = p.is_identity();
1224            let tmp = q.x * acc;
1225            acc = Fp2::conditional_select(&(acc * p.z), &acc, skip);
1226            q.x = p.x * tmp;
1227            q.y = p.y * tmp;
1228            q.infinity = Choice::from(0u8);
1229            *q = G2Affine::conditional_select(q, &G2Affine::identity(), skip);
1230        }
1231    }
1232
1233    /// Check if point at infinity.
1234    #[inline]
1235    pub fn is_identity(&self) -> Choice {
1236        self.z.is_zero()
1237    }
1238
1239    /// Check if on curve y² = x³ + B.
1240    pub fn is_on_curve(&self) -> Choice {
1241        (self.y.square() * self.z).ct_eq(&(self.x.square() * self.x + self.z.square() * self.z * B))
1242            | self.z.is_zero()
1243    }
1244
1245    /// Deserialize from compressed bytes.
1246    pub fn from_bytes(bytes: &[u8; 96]) -> CtOption<Self> {
1247        G2Affine::from_compressed(bytes).map(G2Projective::from)
1248    }
1249
1250    /// Deserialize without validation.
1251    pub fn from_bytes_unchecked(bytes: &[u8; 96]) -> CtOption<Self> {
1252        G2Affine::from_compressed_unchecked(bytes).map(G2Projective::from)
1253    }
1254
1255    /// Serialize to compressed bytes.
1256    pub fn to_bytes(&self) -> [u8; 96] {
1257        G2Affine::from(self).to_compressed()
1258    }
1259}
1260
1261#[cfg(test)]
1262mod tests {
1263    use super::*;
1264
1265    #[test]
1266    fn test_g2_msm() {
1267        let g = G2Affine::generator();
1268        let s1 = Scalar::from(5u64);
1269        let s2 = Scalar::from(6u64);
1270        let s3 = Scalar::from(7u64);
1271
1272        let p1 = G2Affine::from(G2Projective::from(g) * s1); // [5]G
1273        let p2 = G2Affine::from(G2Projective::from(g) * s2); // [6]G
1274        let p3 = G2Affine::from(G2Projective::from(g) * s3); // [7]G
1275
1276        let scalars = vec![s1, s2, s3];
1277        let points = vec![p1, p2, p3];
1278
1279        // Expected result: 5*[5]G + 6*[6]G + 7*[7]G = (25 + 36 + 49)[G] = [110]G
1280        let expected = G2Projective::from(g) * Scalar::from(110u64);
1281
1282        // Naive MSM for comparison
1283        let naive_result = (p1 * s1) + (p2 * s2) + (p3 * s3);
1284        assert_eq!(G2Affine::from(naive_result), G2Affine::from(expected));
1285
1286        // Test msm_vartime
1287        let msm_result_vartime = G2Projective::msm_vartime(&points, &scalars).unwrap();
1288        assert_eq!(G2Affine::from(msm_result_vartime), G2Affine::from(expected));
1289
1290        // Test msm
1291        let msm_result = G2Projective::msm(&points, &scalars).unwrap();
1292        assert_eq!(G2Affine::from(msm_result), G2Affine::from(expected));
1293
1294        // Test empty input
1295        let empty_res = G2Projective::msm(&[], &[]).unwrap();
1296        assert_eq!(empty_res, G2Projective::identity());
1297    }
1298}