Merge pull request #19 from saitima/cyclotomic_squaring

Add implementation of cyclotomic squaring for final exponentiation step
This commit is contained in:
ebfull 2019-11-22 14:13:36 -07:00 committed by GitHub
commit 5f63f860eb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 109 additions and 52 deletions

View File

@ -1,5 +1,6 @@
use crate::fp12::Fp12;
use crate::fp2::Fp2;
use crate::fp6::Fp6;
use crate::{G1Affine, G2Affine, G2Projective, Scalar, BLS_X, BLS_X_IS_NEGATIVE};
use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign};
@ -23,18 +24,83 @@ impl ConditionallySelectable for MillerLoopResult {
impl MillerLoopResult {
/// This performs a "final exponentiation" routine to convert the result
/// of a Miller loop into an element of `Gt` so that it can be compared
/// with other elements of `Gt`.
/// of a Miller loop into an element of `Gt` with help of efficient squaring
/// operation in the so-called `cyclotomic subgroup` of `Fq6` so that
/// it can be compared with other elements of `Gt`.
pub fn final_exponentiation(&self) -> Gt {
let r = &self.0;
#[must_use]
fn exp_by_x(f: Fp12, x: u64) -> Fp12 {
fn fp4_square(a: Fp2, b: Fp2) -> (Fp2, Fp2) {
let t0 = a.square();
let t1 = b.square();
let mut t2 = t1.mul_by_nonresidue();
let c0 = t2 + t0;
t2 = a + b;
t2 = t2.square();
t2 -= t0;
let c1 = t2 - t1;
(c0, c1)
}
// Adaptation of Algorithm 5.5.4, Guide to Pairing-Based Cryptography
// Faster Squaring in the Cyclotomic Subgroup of Sixth Degree Extensions
// https://eprint.iacr.org/2009/565.pdf
#[must_use]
fn cyclotomic_square(f: Fp12) -> Fp12 {
let mut z0 = f.c0.c0.clone();
let mut z4 = f.c0.c1.clone();
let mut z3 = f.c0.c2.clone();
let mut z2 = f.c1.c0.clone();
let mut z1 = f.c1.c1.clone();
let mut z5 = f.c1.c2.clone();
let (t0, t1) = fp4_square(z0, z1);
// For A
z0 = t0 - z0;
z0 += z0 + t0;
z1 = t1 + z1;
z1 += z1 + t1;
let (mut t0, t1) = fp4_square(z2, z3);
let (t2, t3) = fp4_square(z4, z5);
// For C
z4 = t0 - z4;
z4 += z4 + t0;
z5 = t1 + z5;
z5 += z5 + t1;
// For B
t0 = t3.mul_by_nonresidue();
z2 = t0 + z2;
z2 += z2 + t0;
z3 = t2 - z3;
z3 += z3 + t2;
Fp12 {
c0: Fp6 {
c0: z0,
c1: z4,
c2: z3,
},
c1: Fp6 {
c0: z2,
c1: z1,
c2: z5,
},
}
}
#[must_use]
fn cycolotomic_exp(f: Fp12) -> Fp12 {
let x = BLS_X;
let mut tmp = Fp12::one();
let mut found_one = false;
for i in (0..64).rev().map(|b| ((x >> b) & 1) == 1) {
if found_one {
tmp = tmp.square()
tmp = cyclotomic_square(tmp)
} else {
found_one = i;
}
@ -44,55 +110,46 @@ impl MillerLoopResult {
}
}
if BLS_X_IS_NEGATIVE {
tmp = tmp.conjugate();
}
tmp
tmp.conjugate()
}
let f1 = r.conjugate();
let mut f = self.0.clone();
let mut t0 = f
.frobenius_map()
.frobenius_map()
.frobenius_map()
.frobenius_map()
.frobenius_map()
.frobenius_map();
Gt(f.invert()
.map(|mut t1| {
let mut t2 = t0 * t1;
t1 = t2.clone();
t2 = t2.frobenius_map().frobenius_map();
t2 *= t1;
t1 = cyclotomic_square(t2).conjugate();
let mut t3 = cycolotomic_exp(t2);
let mut t4 = cyclotomic_square(t3);
let mut t5 = t1 * t3;
t1 = cycolotomic_exp(t5);
t0 = cycolotomic_exp(t1);
let mut t6 = cycolotomic_exp(t0);
t6 *= t4;
t4 = cycolotomic_exp(t6);
t5 = t5.conjugate();
t4 *= t5 * t2;
t5 = t2.conjugate();
t1 *= t2;
t1 = t1.frobenius_map().frobenius_map().frobenius_map();
t6 *= t5;
t6 = t6.frobenius_map();
t3 *= t0;
t3 = t3.frobenius_map().frobenius_map();
t3 *= t1;
t3 *= t6;
f = t3 * t4;
Gt(r.invert()
.map(|mut f2| {
let mut r = f1;
r *= f2;
f2 = r;
r = r.frobenius_map().frobenius_map();
r *= f2;
let mut x = BLS_X;
let y0 = r.square();
let mut y1 = y0;
y1 = exp_by_x(y1, x);
x >>= 1;
let mut y2 = y1;
y2 = exp_by_x(y2, x);
x <<= 1;
let mut y3 = r;
y3 = y3.conjugate();
y1 *= y3;
y1 = y1.conjugate();
y1 *= y2;
y2 = y1;
y2 = exp_by_x(y2, x);
y3 = y2;
y3 = exp_by_x(y3, x);
y1 = y1.conjugate();
y3 *= y1;
y1 = y1.conjugate();
y1 = y1.frobenius_map().frobenius_map().frobenius_map();
y2 = y2.frobenius_map().frobenius_map();
y1 *= y2;
y2 = y3;
y2 = exp_by_x(y2, x);
y2 *= y0;
y2 *= r;
y1 *= y2;
y2 = y3;
y2 = y2.frobenius_map();
y1 *= y2;
y1
f
})
// We unwrap() because `MillerLoopResult` can only be constructed
// by a function within this crate, and we uphold the invariant