Skip to main content

airbender_crypto/bls12_381/curves/
pairing_impl.rs

1use super::*;
2use crate::bls12_381::{Fq12, Fq2};
3use ark_ec::bls12::g2::EllCoeff;
4use ark_ec::pairing::Pairing;
5use ark_ec::pairing::PairingOutput;
6use ark_ec::short_weierstrass::SWCurveConfig;
7use ark_ec::AdditiveGroup;
8use ark_ec::AffineRepr;
9use ark_ec::CurveGroup;
10use ark_ff::BitIteratorBE;
11use ark_ff::One;
12use ark_ff::{CyclotomicMultSubgroup, Field, Zero};
13use ark_serialize::CanonicalDeserialize;
14use ark_serialize::CanonicalSerialize;
15
16impl Bls12_381 {
17    /// Evaluates the line function at point p.
18    fn ell(f: &mut Fq12, coeffs: &EllCoeff<Config>, p: &G1Affine) {
19        let mut c0 = coeffs.0;
20        let mut c1 = coeffs.1;
21        let mut c2 = coeffs.2;
22        let (px, py) = p.xy().unwrap();
23
24        match Config::TWIST_TYPE {
25            TwistType::M => {
26                c2.mul_assign_by_fp(&py);
27                c1.mul_assign_by_fp(&px);
28                f.mul_by_014(&c0, &c1, &c2);
29            }
30            TwistType::D => {
31                c0.mul_assign_by_fp(&py);
32                c1.mul_assign_by_fp(&px);
33                f.mul_by_034(&c0, &c1, &c2);
34            }
35        }
36    }
37
38    // Exponentiates `f` by `Self::X`, and stores the result in `result`.
39    fn exp_by_x(f: &Fq12, result: &mut Fq12) {
40        *result = *f;
41        Self::spec_cyclotomic_exp_by_x_inplace(result);
42
43        if Config::X_IS_NEGATIVE {
44            result.cyclotomic_inverse_in_place();
45        }
46    }
47
48    fn spec_cyclotomic_exp_by_x_inplace(f: &mut Fq12) {
49        use ark_ff::Zero;
50        if f.is_zero() {
51            return;
52        }
53
54        Self::fast_exp_loop_with_naf(f, Self::X_NAF.iter().copied());
55    }
56
57    const X_NAF: [i8; 65] = [
58        1, 0, -1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
59        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
60        0, 0, 0, 0, 0,
61    ];
62
63    /// `exp_loop` taken from arkworks
64    fn fast_exp_loop_with_naf<I: Iterator<Item = i8>>(f: &mut Fq12, e: I) {
65        let self_inverse = f.cyclotomic_inverse().unwrap();
66        let mut res = Fq12::one();
67        let mut found_nonzero = false;
68        for value in e {
69            if found_nonzero {
70                res.cyclotomic_square_in_place();
71            }
72
73            if value != 0 {
74                found_nonzero = true;
75
76                if value > 0 {
77                    res *= &*f;
78                } else {
79                    res *= &self_inverse;
80                }
81            }
82        }
83        *f = res;
84    }
85}
86
87impl Pairing for Bls12_381 {
88    type BaseField = Fq;
89    type ScalarField = crate::bls12_381::Fr;
90    type G1 = G1Projective;
91    type G1Affine = G1Affine;
92    type G1Prepared = ark_ec::bls12::G1Prepared<Config>;
93    type G2 = G2Projective;
94    type G2Affine = G2Affine;
95    type G2Prepared = G2PreparedNoAlloc;
96    type TargetField = Fq12;
97
98    fn multi_miller_loop(
99        a: impl IntoIterator<Item = impl Into<Self::G1Prepared>>,
100        b: impl IntoIterator<Item = impl Into<Self::G2Prepared>>,
101    ) -> ark_ec::pairing::MillerLoopOutput<Self> {
102        let mut a = a.into_iter();
103        let mut b = b.into_iter();
104        let mut result = Fq12::one();
105        loop {
106            match (a.next(), b.next()) {
107                (Some(p), Some(q)) => {
108                    let p: Self::G1Prepared = p.into();
109                    if p.is_zero() {
110                        continue;
111                    }
112                    let q: Self::G2Prepared = q.into();
113                    if q.is_zero() {
114                        continue;
115                    }
116
117                    let mut f = Fq12::one();
118                    let mut ell_coeffs = q.ell_coeffs.iter();
119
120                    for i in BitIteratorBE::without_leading_zeros(Config::X).skip(1) {
121                        f.square_in_place();
122                        Self::ell(&mut f, &ell_coeffs.next().unwrap(), &p.0);
123                        if i {
124                            Self::ell(&mut f, &ell_coeffs.next().unwrap(), &p.0);
125                        }
126                    }
127
128                    result *= f;
129                }
130                (None, None) => break,
131                _ => {
132                    panic!("Caller must check input lengths");
133                }
134            }
135        }
136
137        if Config::X_IS_NEGATIVE {
138            result.cyclotomic_inverse_in_place();
139        }
140
141        ark_ec::pairing::MillerLoopOutput(result)
142    }
143
144    fn final_exponentiation(
145        f: ark_ec::pairing::MillerLoopOutput<Self>,
146    ) -> Option<PairingOutput<Self>> {
147        // Computing the final exponentiation following
148        // https://eprint.iacr.org/2020/875
149        // Adapted from the implementation in https://github.com/ConsenSys/gurvy/pull/29
150
151        // f1 = r.cyclotomic_inverse_in_place() = f^(p^6)
152        let f = f.0;
153        let mut f1 = f;
154        f1.cyclotomic_inverse_in_place();
155
156        f.inverse().map(|mut f2| {
157            // f2 = f^(-1);
158            // r = f^(p^6 - 1)
159            let mut r = f1 * &f2;
160
161            // f2 = f^(p^6 - 1)
162            f2 = r;
163            // r = f^((p^6 - 1)(p^2))
164            r.frobenius_map_in_place(2);
165
166            // r = f^((p^6 - 1)(p^2) + (p^6 - 1))
167            // r = f^((p^6 - 1)(p^2 + 1))
168            r *= &f2;
169
170            // Hard part of the final exponentiation:
171            // t[0].CyclotomicSquare(&result)
172            let mut y0 = r.cyclotomic_square();
173            // t[1].Expt(&result)
174            let mut y1 = Fq12::zero();
175            Self::exp_by_x(&r, &mut y1);
176            // t[2].InverseUnitary(&result)
177            let mut y2 = r;
178            y2.cyclotomic_inverse_in_place();
179            // t[1].Mul(&t[1], &t[2])
180            y1 *= &y2;
181            // t[2].Expt(&t[1])
182            Self::exp_by_x(&y1, &mut y2);
183            // t[1].InverseUnitary(&t[1])
184            y1.cyclotomic_inverse_in_place();
185            // t[1].Mul(&t[1], &t[2])
186            y1 *= &y2;
187            // t[2].Expt(&t[1])
188            Self::exp_by_x(&y1, &mut y2);
189            // t[1].Frobenius(&t[1])
190            y1.frobenius_map_in_place(1);
191            // t[1].Mul(&t[1], &t[2])
192            y1 *= &y2;
193            // result.Mul(&result, &t[0])
194            r *= &y0;
195            // t[0].Expt(&t[1])
196            Self::exp_by_x(&y1, &mut y0);
197            // t[2].Expt(&t[0])
198            Self::exp_by_x(&y0, &mut y2);
199            // t[0].FrobeniusSquare(&t[1])
200            y0 = y1;
201            y0.frobenius_map_in_place(2);
202            // t[1].InverseUnitary(&t[1])
203            y1.cyclotomic_inverse_in_place();
204            // t[1].Mul(&t[1], &t[2])
205            y1 *= &y2;
206            // t[1].Mul(&t[1], &t[0])
207            y1 *= &y0;
208            // result.Mul(&result, &t[1])
209            r *= &y1;
210            PairingOutput(r)
211        })
212    }
213}
214
215impl From<G2Affine> for G2PreparedNoAlloc {
216    fn from(q: G2Affine) -> Self {
217        if q.infinity {
218            // coeffs should not be used
219            Self {
220                ell_coeffs: [Default::default(); BLS12_381_NUM_ELL_COEFFS],
221                infinity: true,
222            }
223        } else {
224            use ark_ff::{AdditiveGroup, One};
225            let two_inv = Fq::one().double().inverse().unwrap();
226            let mut i = 0;
227            let mut ell_coeffs: [core::mem::MaybeUninit<EllCoeff<Config>>;
228                BLS12_381_NUM_ELL_COEFFS] =
229                [const { core::mem::MaybeUninit::uninit() }; BLS12_381_NUM_ELL_COEFFS];
230
231            let mut r = G2HomProjective {
232                x: q.x,
233                y: q.y,
234                z: Fq2::one(),
235            };
236
237            for bit in BitIteratorBE::new(Config::X).skip(1) {
238                ell_coeffs[i].write(r.double_in_place(&two_inv));
239                i += 1;
240
241                if bit {
242                    ell_coeffs[i].write(r.add_in_place(&q));
243                    i += 1;
244                }
245            }
246
247            assert_eq!(i, ell_coeffs.len());
248
249            Self {
250                ell_coeffs: unsafe { ell_coeffs.map(|el| el.assume_init()) },
251                infinity: false,
252            }
253        }
254    }
255}
256
257#[derive(Clone, Copy, Debug)]
258struct G2HomProjective {
259    x: Fq2,
260    y: Fq2,
261    z: Fq2,
262}
263
264impl G2HomProjective {
265    fn double_in_place(&mut self, two_inv: &Fq) -> EllCoeff<Config> {
266        // Formula for line function when working with
267        // homogeneous projective coordinates.
268
269        let mut a = self.x * &self.y;
270        a.mul_assign_by_fp(two_inv);
271        let b = self.y.square();
272        let c = self.z.square();
273        let e = <Config as Bls12Config>::G2Config::COEFF_B * &(c.double() + &c);
274        let f = e.double() + &e;
275        let mut g = b + &f;
276        g.mul_assign_by_fp(two_inv);
277        let h = (self.y + &self.z).square() - &(b + &c);
278        let i = e - &b;
279        let j = self.x.square();
280        let e_square = e.square();
281
282        self.x = a * &(b - &f);
283        self.y = g.square() - &(e_square.double() + &e_square);
284        self.z = b * &h;
285        match Config::TWIST_TYPE {
286            TwistType::M => (i, j.double() + &j, -h),
287            TwistType::D => (-h, j.double() + &j, i),
288        }
289    }
290
291    fn add_in_place(&mut self, q: &G2Affine) -> EllCoeff<Config> {
292        // Formula for line function when working with
293        // homogeneous projective coordinates.
294        let theta = self.y - &(q.y * &self.z);
295        let lambda = self.x - &(q.x * &self.z);
296        let c = theta.square();
297        let d = lambda.square();
298        let e = lambda * &d;
299        let f = self.z * &c;
300        let g = self.x * &d;
301        let h = e + &f - &g.double();
302        self.x = lambda * &h;
303        self.y = theta * &(g - &h) - &(e * &self.y);
304        self.z *= &e;
305        let j = theta * &q.x - &(lambda * &q.y);
306
307        match Config::TWIST_TYPE {
308            TwistType::M => (j, -theta, lambda),
309            TwistType::D => (lambda, -theta, j),
310        }
311    }
312}
313
314impl Default for G2PreparedNoAlloc {
315    fn default() -> Self {
316        Self::from(G2Affine::generator())
317    }
318}
319
320impl From<G2Projective> for G2PreparedNoAlloc {
321    fn from(q: G2Projective) -> Self {
322        q.into_affine().into()
323    }
324}
325
326impl<'a> From<&'a G2Affine> for G2PreparedNoAlloc {
327    fn from(other: &'a G2Affine) -> Self {
328        (*other).into()
329    }
330}
331
332impl<'a> From<&'a G2Projective> for G2PreparedNoAlloc {
333    fn from(q: &'a G2Projective) -> Self {
334        q.into_affine().into()
335    }
336}
337
338impl G2PreparedNoAlloc {
339    pub fn is_zero(&self) -> bool {
340        self.infinity
341    }
342}
343
344pub const BLS12_381_NUM_ELL_COEFFS: usize = const {
345    let num_bits_except_top_one = (u64::BITS - Config::X[0].leading_zeros() - 1) as usize;
346    let mut result = num_bits_except_top_one;
347    let num_non_zero_bits = Config::X[0].count_ones();
348    // all non-zero bits except top one
349    result += num_non_zero_bits as usize - 1;
350
351    result
352};
353
354#[derive(Clone, Debug, PartialEq, Eq)]
355pub struct G2PreparedNoAlloc {
356    pub ell_coeffs: [ark_ec::bls12::g2::EllCoeff<Config>; BLS12_381_NUM_ELL_COEFFS],
357    pub infinity: bool,
358}
359
360impl CanonicalSerialize for G2PreparedNoAlloc {
361    fn serialize_with_mode<W: ark_serialize::Write>(
362        &self,
363        _writer: W,
364        _compress: ark_serialize::Compress,
365    ) -> Result<(), ark_serialize::SerializationError> {
366        unimplemented!("not supported");
367    }
368
369    fn serialized_size(&self, _compress: ark_serialize::Compress) -> usize {
370        unimplemented!("not supported");
371    }
372}
373
374impl ark_serialize::Valid for G2PreparedNoAlloc {
375    fn check(&self) -> Result<(), ark_serialize::SerializationError> {
376        unimplemented!("not supported");
377    }
378}
379
380impl CanonicalDeserialize for G2PreparedNoAlloc {
381    fn deserialize_with_mode<R: ark_serialize::Read>(
382        _reader: R,
383        _compress: ark_serialize::Compress,
384        _validate: ark_serialize::Validate,
385    ) -> Result<Self, ark_serialize::SerializationError> {
386        unimplemented!("not supported");
387    }
388}
389
390#[cfg(test)]
391mod test {
392    use super::*;
393
394    #[test]
395    fn compute_x_naf() {
396        let t = ark_ff::biginteger::arithmetic::find_naf(Config::X);
397        let t: Vec<_> = t.into_iter().rev().collect();
398        dbg!(t);
399    }
400}