snarkvm_curves/templates/bls12/
bls12.rs

1// Copyright (c) 2019-2025 Provable Inc.
2// This file is part of the snarkVM library.
3
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at:
7
8// http://www.apache.org/licenses/LICENSE-2.0
9
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16use crate::{
17    AffineCurve,
18    templates::{
19        bls12::{
20            g1::{G1Affine, G1Prepared, G1Projective},
21            g2::{G2Affine, G2Prepared, G2Projective},
22        },
23        short_weierstrass_jacobian,
24    },
25    traits::{ModelParameters, PairingCurve, PairingEngine, ShortWeierstrassParameters},
26};
27use snarkvm_fields::{
28    Field,
29    Fp2,
30    Fp2Parameters,
31    Fp12,
32    Fp12Parameters,
33    One,
34    PrimeField,
35    SquareRootField,
36    Zero,
37    fp6_3over2::Fp6Parameters,
38};
39use snarkvm_utilities::bititerator::BitIteratorBE;
40
41use core::{fmt::Debug, hash::Hash, marker::PhantomData};
42use serde::{Deserialize, Serialize};
43
44pub enum TwistType {
45    M,
46    D,
47}
48
49pub trait Bls12Parameters: 'static + Copy + Clone + Debug + PartialEq + Eq + Hash + Send + Sync + Sized {
50    const X: &'static [u64];
51    const X_IS_NEGATIVE: bool;
52    const TWIST_TYPE: TwistType;
53    type Fp: PrimeField + SquareRootField + Into<<Self::Fp as PrimeField>::BigInteger>;
54    type Fp2Params: Fp2Parameters<Fp = Self::Fp>;
55    type Fp6Params: Fp6Parameters<Fp2Params = Self::Fp2Params>;
56    type Fp12Params: Fp12Parameters<Fp6Params = Self::Fp6Params>;
57    type G1Parameters: ShortWeierstrassParameters<BaseField = Self::Fp>;
58    type G2Parameters: ShortWeierstrassParameters<
59            BaseField = Fp2<Self::Fp2Params>,
60            ScalarField = <Self::G1Parameters as ModelParameters>::ScalarField,
61        >;
62
63    fn g1_is_in_correct_subgroup(p: &short_weierstrass_jacobian::Affine<Self::G1Parameters>) -> bool {
64        p.mul_bits(BitIteratorBE::new(<Self::G1Parameters as ModelParameters>::ScalarField::characteristic())).is_zero()
65    }
66
67    fn g2_is_in_correct_subgroup(p: &short_weierstrass_jacobian::Affine<Self::G1Parameters>) -> bool {
68        p.mul_bits(BitIteratorBE::new(<Self::G1Parameters as ModelParameters>::ScalarField::characteristic())).is_zero()
69    }
70}
71
72#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
73pub struct Bls12<P: Bls12Parameters>(PhantomData<P>);
74
75type CoeffTriplet<T> = (Fp2<T>, Fp2<T>, Fp2<T>);
76
77impl<P: Bls12Parameters> Bls12<P> {
78    /// Evaluate the line function at point p.
79    fn ell(f: &mut Fp12<P::Fp12Params>, coeffs: &CoeffTriplet<P::Fp2Params>, p: &G1Affine<P>) {
80        let mut c0 = coeffs.0;
81        let mut c1 = coeffs.1;
82        let mut c2 = coeffs.2;
83
84        match P::TWIST_TYPE {
85            TwistType::M => {
86                c2.mul_by_fp(&p.y);
87                c1.mul_by_fp(&p.x);
88                f.mul_by_014(&c0, &c1, &c2);
89            }
90            TwistType::D => {
91                c0.mul_by_fp(&p.y);
92                c1.mul_by_fp(&p.x);
93                f.mul_by_034(&c0, &c1, &c2);
94            }
95        }
96    }
97
98    fn exp_by_x(mut f: Fp12<P::Fp12Params>) -> Fp12<P::Fp12Params> {
99        f = f.cyclotomic_exp(P::X);
100        if P::X_IS_NEGATIVE {
101            f.conjugate();
102        }
103        f
104    }
105}
106
107impl<P: Bls12Parameters> PairingEngine for Bls12<P>
108where
109    G1Affine<P>: PairingCurve<
110            BaseField = <P::G1Parameters as ModelParameters>::BaseField,
111            ScalarField = <P::G1Parameters as ModelParameters>::ScalarField,
112            Projective = G1Projective<P>,
113            PairWith = G2Affine<P>,
114            Prepared = G1Prepared<P>,
115            PairingResult = Fp12<P::Fp12Params>,
116        >,
117    G2Affine<P>: PairingCurve<
118            BaseField = <P::G2Parameters as ModelParameters>::BaseField,
119            ScalarField = <P::G1Parameters as ModelParameters>::ScalarField,
120            Projective = G2Projective<P>,
121            PairWith = G1Affine<P>,
122            Prepared = G2Prepared<P>,
123            PairingResult = Fp12<P::Fp12Params>,
124        >,
125{
126    type Fq = P::Fp;
127    type Fqe = Fp2<P::Fp2Params>;
128    type Fqk = Fp12<P::Fp12Params>;
129    type Fr = <P::G1Parameters as ModelParameters>::ScalarField;
130    type G1Affine = G1Affine<P>;
131    type G1Projective = G1Projective<P>;
132    type G2Affine = G2Affine<P>;
133    type G2Projective = G2Projective<P>;
134
135    fn miller_loop<'a, I>(i: I) -> Self::Fqk
136    where
137        I: Iterator<
138            Item = (&'a <Self::G1Affine as PairingCurve>::Prepared, &'a <Self::G2Affine as PairingCurve>::Prepared),
139        >,
140    {
141        let mut pairs = vec![];
142        for (p, q) in i {
143            if !p.is_zero() && !q.is_zero() {
144                pairs.push((p, q.ell_coeffs.iter()));
145            }
146        }
147
148        let mut f = Self::Fqk::one();
149
150        for i in BitIteratorBE::new(P::X).skip(1) {
151            f.square_in_place();
152
153            for &mut (p, ref mut coeffs) in &mut pairs {
154                Self::ell(&mut f, coeffs.next().unwrap(), &p.0);
155            }
156
157            if i {
158                for &mut (p, ref mut coeffs) in &mut pairs {
159                    Self::ell(&mut f, coeffs.next().unwrap(), &p.0);
160                }
161            }
162        }
163
164        if P::X_IS_NEGATIVE {
165            f.conjugate();
166        }
167
168        f
169    }
170
171    fn final_exponentiation(f: &Self::Fqk) -> Option<Self::Fqk> {
172        // Computing the final exponentiation following
173        // https://eprint.iacr.org/2016/130.pdf.
174        // We don't use their "faster" formula because it is difficult to make
175        // it work for curves with odd `P::X`.
176        // Hence we implement the algorithm from Table 1 below.
177
178        // f1 = r.conjugate() = f^(p^6)
179        let mut f1 = *f;
180        f1.conjugate();
181
182        match f.inverse() {
183            Some(mut f2) => {
184                // f2 = f^(-1);
185                // r = f^(p^6 - 1)
186                let mut r = f1 * f2;
187
188                // f2 = f^(p^6 - 1)
189                f2 = r;
190                // r = f^((p^6 - 1)(p^2))
191                r.frobenius_map(2);
192
193                // r = f^((p^6 - 1)(p^2) + (p^6 - 1))
194                // r = f^((p^6 - 1)(p^2 + 1))
195                r *= &f2;
196
197                // Hard part of the final exponentiation is below:
198                // From https://eprint.iacr.org/2016/130.pdf, Table 1
199                let mut y0 = r.cyclotomic_square();
200                y0.conjugate();
201
202                let mut y5 = Self::exp_by_x(r);
203
204                let mut y1 = y5.cyclotomic_square();
205                let mut y3 = y0 * y5;
206                y0 = Self::exp_by_x(y3);
207                let y2 = Self::exp_by_x(y0);
208                let mut y4 = Self::exp_by_x(y2);
209                y4 *= &y1;
210                y1 = Self::exp_by_x(y4);
211                y3.conjugate();
212                y1 *= &y3;
213                y1 *= &r;
214                y3 = r;
215                y3.conjugate();
216                y0 *= &r;
217                y0.frobenius_map(3);
218                y4 *= &y3;
219                y4.frobenius_map(1);
220                y5 *= &y2;
221                y5.frobenius_map(2);
222                y5 *= &y0;
223                y5 *= &y4;
224                y5 *= &y1;
225                Some(y5)
226            }
227            None => None,
228        }
229    }
230}