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