Skip to content

Commit

Permalink
Faster final exponentiation for BLS12 (#211)
Browse files Browse the repository at this point in the history
  • Loading branch information
Pratyush authored Mar 17, 2021
1 parent d37ff02 commit 4e316d1
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 77 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ The main features of this release are:
- #201 (ark-ec, ark-ff, ark-test-curves, ark-test-templates) Remove the dependency on `rand_xorshift`
- #205 (ark-ec, ark-ff) Unroll loops and conditionally use intrinsics in `biginteger` arithmetic, and reduce copies in `ff` and `ec` arithmetic.
- #207 (ark-ff) Improve performance of extension fields when the non-residue is negative. (Improves fq2, fq12, and g2 speed on bls12 and bn curves)
- #211 (ark-ec) Improve performance of BLS12 final exponentiation.
- #214 (ark-poly) Utilise a more efficient way of evaluating a polynomial at a single point

### Bug fixes
Expand Down
89 changes: 52 additions & 37 deletions ec/src/models/bls12/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,24 @@ use ark_ff::fields::{
fp6_3over2::Fp6Parameters,
BitIteratorBE, Field, Fp2, PrimeField, SquareRootField,
};
use num_traits::One;
use num_traits::{One, Zero};

use core::marker::PhantomData;

/// A particular BLS12 group can have G2 being either a multiplicative or a divisive twist.
pub enum TwistType {
M,
D,
}

pub trait Bls12Parameters: 'static {
/// Parameterizes the BLS12 family.
const X: &'static [u64];
/// Is `Self::X` negative?
const X_IS_NEGATIVE: bool;
/// What kind of twist is this?
const TWIST_TYPE: TwistType;

type Fp: PrimeField + SquareRootField + Into<<Self::Fp as PrimeField>::BigInt>;
type Fp2Params: Fp2Parameters<Fp = Self::Fp>;
type Fp6Params: Fp6Parameters<Fp2Params = Self::Fp2Params>;
Expand Down Expand Up @@ -65,12 +70,12 @@ impl<P: Bls12Parameters> Bls12<P> {
}
}

fn exp_by_x(mut f: Fp12<P::Fp12Params>) -> Fp12<P::Fp12Params> {
f = f.cyclotomic_exp(P::X);
// Exponentiates `f` by `Self::X`, and stores the result in `result`.
fn exp_by_x(f: &Fp12<P::Fp12Params>, result: &mut Fp12<P::Fp12Params>) {
*result = f.cyclotomic_exp(P::X);
if P::X_IS_NEGATIVE {
f.conjugate();
result.conjugate();
}
f
}
}

Expand Down Expand Up @@ -122,10 +127,8 @@ impl<P: Bls12Parameters> PairingEngine for Bls12<P> {

fn final_exponentiation(f: &Self::Fqk) -> Option<Self::Fqk> {
// Computing the final exponentation following
// https://eprint.iacr.org/2016/130.pdf.
// We don't use their "faster" formula because it is difficult to make
// it work for curves with odd `P::X`.
// Hence we implement the algorithm from Table 1 below.
// https://eprint.iacr.org/2020/875
// Adapted from the implementation in https://github.com/ConsenSys/gurvy/pull/29

// f1 = r.conjugate() = f^(p^6)
let mut f1 = *f;
Expand All @@ -145,35 +148,47 @@ impl<P: Bls12Parameters> PairingEngine for Bls12<P> {
// r = f^((p^6 - 1)(p^2 + 1))
r *= &f2;

// Hard part of the final exponentation is below:
// From https://eprint.iacr.org/2016/130.pdf, Table 1
// Hard part of the final exponentation:
// t[0].CyclotomicSquare(&result)
let mut y0 = r.cyclotomic_square();
y0.conjugate();

let mut y5 = Self::exp_by_x(r);

let mut y1 = y5.cyclotomic_square();
let mut y3 = y0 * &y5;
y0 = Self::exp_by_x(y3);
let y2 = Self::exp_by_x(y0);
let mut y4 = Self::exp_by_x(y2);
y4 *= &y1;
y1 = Self::exp_by_x(y4);
y3.conjugate();
y1 *= &y3;
y1 *= &r;
y3 = r;
y3.conjugate();
y0 *= &r;
y0.frobenius_map(3);
y4 *= &y3;
y4.frobenius_map(1);
y5 *= &y2;
y5.frobenius_map(2);
y5 *= &y0;
y5 *= &y4;
y5 *= &y1;
y5
// t[1].Expt(&result)
let mut y1 = Fp12::zero();
Self::exp_by_x(&r, &mut y1);
// t[2].InverseUnitary(&result)
let mut y2 = r;
y2.conjugate();
// t[1].Mul(&t[1], &t[2])
y1 *= &y2;
// t[2].Expt(&t[1])
Self::exp_by_x(&y1, &mut y2);
// t[1].InverseUnitary(&t[1])
y1.conjugate();
// t[1].Mul(&t[1], &t[2])
y1 *= &y2;
// t[2].Expt(&t[1])
Self::exp_by_x(&y1, &mut y2);
// t[1].Frobenius(&t[1])
y1.frobenius_map(1);
// t[1].Mul(&t[1], &t[2])
y1 *= &y2;
// result.Mul(&result, &t[0])
r *= &y0;
// t[0].Expt(&t[1])
Self::exp_by_x(&y1, &mut y0);
// t[2].Expt(&t[0])
Self::exp_by_x(&y0, &mut y2);
// t[0].FrobeniusSquare(&t[1])
y0 = y1;
y0.frobenius_map(2);
// t[1].InverseUnitary(&t[1])
y1.conjugate();
// t[1].Mul(&t[1], &t[2])
y1 *= &y2;
// t[1].Mul(&t[1], &t[0])
y1 *= &y0;
// result.Mul(&result, &t[1])
r *= &y1;
r
})
}
}
90 changes: 50 additions & 40 deletions ff/src/fields/models/fp12_2over3over2.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use super::quadratic_extension::*;
use crate::{
fields::{fp6_3over2::*, Field, Fp2, Fp2Parameters},
One, Zero,
One,
};
use core::marker::PhantomData;
use core::ops::{AddAssign, SubAssign};
Expand Down Expand Up @@ -53,15 +53,15 @@ impl<P: Fp12Parameters> QuadExtParameters for Fp12ParamsWrapper<P> {

fn cyclotomic_exp(fe: &Fp12<P>, exponent: impl AsRef<[u64]>) -> Fp12<P> {
let mut res = QuadExtField::one();
let mut self_inverse = fe.clone();
self_inverse.conjugate();
let mut fe_inverse = *fe;
fe_inverse.conjugate();

let mut found_nonzero = false;
let naf = crate::biginteger::arithmetic::find_wnaf(exponent.as_ref());

for &value in naf.iter().rev() {
if found_nonzero {
res = res.cyclotomic_square();
res.cyclotomic_square_in_place();
}

if value != 0 {
Expand All @@ -70,7 +70,7 @@ impl<P: Fp12Parameters> QuadExtParameters for Fp12ParamsWrapper<P> {
if value > 0 {
res *= fe;
} else {
res *= &self_inverse;
res *= &fe_inverse;
}
}
}
Expand Down Expand Up @@ -131,78 +131,88 @@ impl<P: Fp12Parameters> Fp12<P> {
self.c0.add_assign(&aa);
}

pub fn cyclotomic_square(&self) -> Self {
pub fn cyclotomic_square_in_place(&mut self) {
// Faster Squaring in the Cyclotomic Subgroup of Sixth Degree Extensions
// - Robert Granger and Michael Scott
//
if characteristic_square_mod_6_is_one(Self::characteristic()) {
let mut result = Self::zero();
let fp2_nr = <P::Fp6Params as Fp6Parameters>::mul_fp2_by_nonresidue;

let mut z0 = self.c0.c0;
let mut z4 = self.c0.c1;
let mut z3 = self.c0.c2;
let mut z2 = self.c1.c0;
let mut z1 = self.c1.c1;
let mut z5 = self.c1.c2;
let r0 = &self.c0.c0;
let r4 = &self.c0.c1;
let r3 = &self.c0.c2;
let r2 = &self.c1.c0;
let r1 = &self.c1.c1;
let r5 = &self.c1.c2;

// t0 + t1*y = (z0 + z1*y)^2 = a^2
let mut tmp = z0 * &z1;
let t0 = (z0 + &z1) * &(z0 + &fp2_nr(&z1)) - &tmp - &fp2_nr(&tmp);
let mut tmp = *r0 * r1;
let t0 = (*r0 + r1) * &(fp2_nr(&r1) + r0) - &tmp - &fp2_nr(&tmp);
let t1 = tmp.double();

// t2 + t3*y = (z2 + z3*y)^2 = b^2
tmp = z2 * &z3;
let t2 = (z2 + &z3) * &(z2 + &fp2_nr(&z3)) - &tmp - &fp2_nr(&tmp);
tmp = *r2 * r3;
let t2 = (*r2 + r3) * &(fp2_nr(&r3) + r2) - &tmp - &fp2_nr(&tmp);
let t3 = tmp.double();

// t4 + t5*y = (z4 + z5*y)^2 = c^2
tmp = z4 * &z5;
let t4 = (z4 + &z5) * &(z4 + &fp2_nr(&z5)) - &tmp - &fp2_nr(&tmp);
tmp = *r4 * r5;
let t4 = (*r4 + r5) * &(fp2_nr(&r5) + r4) - &tmp - &fp2_nr(&tmp);
let t5 = tmp.double();

let z0 = &mut self.c0.c0;
let z4 = &mut self.c0.c1;
let z3 = &mut self.c0.c2;
let z2 = &mut self.c1.c0;
let z1 = &mut self.c1.c1;
let z5 = &mut self.c1.c2;

// for A

// z0 = 3 * t0 - 2 * z0
z0 = t0 - &z0;
z0 = z0 + &z0;
result.c0.c0 = z0 + &t0;
*z0 = t0 - &*z0;
z0.double_in_place();
*z0 += &t0;

// z1 = 3 * t1 + 2 * z1
z1 = t1 + &z1;
z1 = z1 + &z1;
result.c1.c1 = z1 + &t1;
*z1 = t1 + &*z1;
z1.double_in_place();
*z1 += &t1;

// for B

// z2 = 3 * (xi * t5) + 2 * z2
tmp = fp2_nr(&t5);
z2 = tmp + &z2;
z2 = z2 + &z2;
result.c1.c0 = z2 + &tmp;
*z2 += tmp;
z2.double_in_place();
*z2 += &tmp;

// z3 = 3 * t4 - 2 * z3
z3 = t4 - &z3;
z3 = z3 + &z3;
result.c0.c2 = z3 + &t4;
*z3 = t4 - &*z3;
z3.double_in_place();
*z3 += &t4;

// for C

// z4 = 3 * t2 - 2 * z4
z4 = t2 - &z4;
z4 = z4 + &z4;
result.c0.c1 = z4 + &t2;
*z4 = t2 - &*z4;
z4.double_in_place();
*z4 += &t2;

// z5 = 3 * t3 + 2 * z5
z5 = t3 + &z5;
z5 = z5 + &z5;
result.c1.c2 = z5 + &t3;

result
*z5 += t3;
z5.double_in_place();
*z5 += &t3;
} else {
self.square()
self.square_in_place();
}
}

pub fn cyclotomic_square(&self) -> Self {
let mut result = *self;
result.cyclotomic_square_in_place();
result
}
}

// TODO: make `const fn` in 1.46.
Expand Down

0 comments on commit 4e316d1

Please sign in to comment.