From 0485096dcd44068f26cf9d9c8dccdd247a8f1902 Mon Sep 17 00:00:00 2001 From: Sait Imamoglu Date: Fri, 22 Nov 2019 16:57:38 +0300 Subject: [PATCH] Add implementation of cyclotomic squaring for final exponentiation Fix linter errors Force ci to run with previous amended commit --- src/pairings.rs | 161 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 109 insertions(+), 52 deletions(-) diff --git a/src/pairings.rs b/src/pairings.rs index 9a3539673..a1e6640f9 100644 --- a/src/pairings.rs +++ b/src/pairings.rs @@ -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