From e8031d4fc8f7f1662cac2fa5f8f175021e4c010e Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Thu, 19 Oct 2023 19:14:55 -0400 Subject: [PATCH 1/6] refactor(bw6-761): use revisited Ate pairing instead of Tate --- ecc/bw6-761/g2.go | 35 ++++ ecc/bw6-761/internal/fptower/e6_pairing.go | 56 +++++++ ecc/bw6-761/pairing.go | 186 ++++++++++++++++++++- internal/generator/config/bw6-761.go | 1 + 4 files changed, 277 insertions(+), 1 deletion(-) diff --git a/ecc/bw6-761/g2.go b/ecc/bw6-761/g2.go index 590621d9f8..e8bd0d4a31 100644 --- a/ecc/bw6-761/g2.go +++ b/ecc/bw6-761/g2.go @@ -40,6 +40,11 @@ type g2JacExtended struct { X, Y, ZZ, ZZZ fp.Element } +// g2Proj point in projective coordinates +type g2Proj struct { + x, y, z fp.Element +} + // ------------------------------------------------------------------------------------------------- // Affine @@ -882,6 +887,36 @@ func (p *g2JacExtended) doubleMixed(q *G2Affine) *g2JacExtended { return p } +// ------------------------------------------------------------------------------------------------- +// Homogenous projective + +// Set sets p to the provided point +func (p *g2Proj) Set(a *g2Proj) *g2Proj { + p.x, p.y, p.z = a.x, a.y, a.z + return p +} + +// Neg computes -G +func (p *g2Proj) Neg(a *g2Proj) *g2Proj { + *p = *a + p.y.Neg(&a.y) + return p +} + +// FromAffine sets p = Q, p in homogenous projective, Q in affine +func (p *g2Proj) FromAffine(Q *G2Affine) *g2Proj { + if Q.X.IsZero() && Q.Y.IsZero() { + p.z.SetZero() + p.x.SetOne() + p.y.SetOne() + return p + } + p.z.SetOne() + p.x.Set(&Q.X) + p.y.Set(&Q.Y) + return p +} + // BatchScalarMultiplicationG2 multiplies the same base by all scalars // and return resulting points in affine coordinates // uses a simple windowed-NAF like exponentiation algorithm diff --git a/ecc/bw6-761/internal/fptower/e6_pairing.go b/ecc/bw6-761/internal/fptower/e6_pairing.go index 21178257e0..39df689e6f 100644 --- a/ecc/bw6-761/internal/fptower/e6_pairing.go +++ b/ecc/bw6-761/internal/fptower/e6_pairing.go @@ -218,3 +218,59 @@ func (z *E6) MulBy01234(x *[5]fp.Element) *E6 { return z } + +// MulBy014 multiplication by sparse element (c0,c1,0,0,c4,0) +func (z *E6) MulBy014(c0, c1, c4 *fp.Element) *E6 { + + var a, b E3 + var d fp.Element + + a.Set(&z.B0) + a.MulBy01(c0, c1) + + b.Set(&z.B1) + b.MulBy1(c4) + d.Add(c1, c4) + + z.B1.Add(&z.B1, &z.B0) + z.B1.MulBy01(c0, &d) + z.B1.Sub(&z.B1, &a) + z.B1.Sub(&z.B1, &b) + z.B0.MulByNonResidue(&b) + z.B0.Add(&z.B0, &a) + + return z +} + +// Mul014By014 multiplication of sparse element (c0,c1,0,0,c4,0) by sparse element (d0,d1,0,0,d4,0) +func (z *E6) Mul014By014(d0, d1, d4, c0, c1, c4 *fp.Element) *E6 { + var tmp, x0, x1, x4, x04, x01, x14 fp.Element + x0.Mul(c0, d0) + x1.Mul(c1, d1) + x4.Mul(c4, d4) + tmp.Add(c0, c4) + x04.Add(d0, d4). + Mul(&x04, &tmp). + Sub(&x04, &x0). + Sub(&x04, &x4) + tmp.Add(c0, c1) + x01.Add(d0, d1). + Mul(&x01, &tmp). + Sub(&x01, &x0). + Sub(&x01, &x1) + tmp.Add(c1, c4) + x14.Add(d1, d4). + Mul(&x14, &tmp). + Sub(&x14, &x1). + Sub(&x14, &x4) + + z.B0.A0.MulByNonResidue(&x4). + Add(&z.B0.A0, &x0) + z.B0.A1.Set(&x01) + z.B0.A2.Set(&x1) + z.B1.A0.SetZero() + z.B1.A1.Set(&x04) + z.B1.A2.Set(&x14) + + return z +} diff --git a/ecc/bw6-761/pairing.go b/ecc/bw6-761/pairing.go index 12ca16ba03..60f9d3ff33 100644 --- a/ecc/bw6-761/pairing.go +++ b/ecc/bw6-761/pairing.go @@ -121,6 +121,17 @@ func FinalExponentiation(z *GT, _z ...*GT) GT { return result } +// MillerLoop computes the multi-Miller loop +func MillerLoop(P []G1Affine, Q []G2Affine) (GT, error) { + // result, err := millerLoopTate(P, Q) + result, err := millerLoopAte(P, Q) + if err != nil { + return GT{}, err + } + + return result, nil +} + // MillerLoop Optimal Tate alternative (or twisted ate or Eta revisited) // computes the multi-Miller loop // ∏ᵢ MillerLoop(Pᵢ, Qᵢ) = @@ -128,7 +139,7 @@ func FinalExponentiation(z *GT, _z ...*GT) GT { // // Alg.2 in https://eprint.iacr.org/2021/1359.pdf // Eq. (6) in https://hackmd.io/@gnark/BW6-761-changes -func MillerLoop(P []G1Affine, Q []G2Affine) (GT, error) { +func millerLoopTate(P []G1Affine, Q []G2Affine) (GT, error) { // check input size match n := len(P) if n == 0 || n != len(Q) { @@ -305,6 +316,179 @@ func MillerLoop(P []G1Affine, Q []G2Affine) (GT, error) { return result, nil } +// MillerLoop Optimal ate alternative +// computes the multi-Miller loop +// ∏ᵢ MillerLoop(Pᵢ, Qᵢ) = +// ∏ᵢ { fᵢ_{x₀+1+λ(x₀³-x₀²-x₀),Qᵢ}(Pᵢ) } +// +// Alg.2 in https://eprint.iacr.org/2021/1359.pdf +// Eq. (6') in https://hackmd.io/@gnark/BW6-761-changes +func millerLoopAte(P []G1Affine, Q []G2Affine) (GT, error) { + // check input size match + n := len(P) + if n == 0 || n != len(Q) { + return GT{}, errors.New("invalid inputs sizes") + } + + // filter infinity points + p := make([]G1Affine, 0, n) + q0 := make([]G2Affine, 0, n) + + for k := 0; k < n; k++ { + if P[k].IsInfinity() || Q[k].IsInfinity() { + continue + } + p = append(p, P[k]) + q0 = append(q0, Q[k]) + } + + n = len(p) + + // precomputations + qProj1 := make([]g2Proj, n) + q1 := make([]G2Affine, n) + for k := 0; k < n; k++ { + q1[k].Y.Neg(&q0[k].Y) + q1[k].X.Mul(&q0[k].X, &thirdRootOneG1) + qProj1[k].FromAffine(&q1[k]) + } + + // f_{a0+λ*a1,Q}(P) + var result GT + result.SetOne() + var l, l0 lineEvaluation + + var j int8 + + var tmp G2Affine + for i := 188; i >= 0; i-- { + result.Square(&result) + + j = loopCounter1[i]*3 + loopCounter0[i] + + for k := 0; k < n; k++ { + qProj1[k].doubleStep(&l0) + l0.r1.Mul(&l0.r1, &p[k].X) + l0.r2.Mul(&l0.r2, &p[k].Y) + + switch j { + // cases -4, -2, 2, 4 do not occur, given the static loopCounters + case -3: + tmp.Neg(&q1[k]) + qProj1[k].addMixedStep(&l, &tmp) + l.r1.Mul(&l.r1, &p[k].X) + l.r2.Mul(&l.r2, &p[k].Y) + result.MulBy014(&l0.r0, &l0.r1, &l0.r2) + result.MulBy014(&l.r0, &l.r1, &l.r2) + case -1: + tmp.Neg(&q0[k]) + qProj1[k].addMixedStep(&l, &tmp) + l.r1.Mul(&l.r1, &p[k].X) + l.r2.Mul(&l.r2, &p[k].Y) + result.MulBy014(&l0.r0, &l0.r1, &l0.r2) + result.MulBy014(&l.r0, &l.r1, &l.r2) + case 0: + result.MulBy014(&l0.r0, &l0.r1, &l0.r2) + case 1: + qProj1[k].addMixedStep(&l, &q0[k]) + l.r1.Mul(&l.r1, &p[k].X) + l.r2.Mul(&l.r2, &p[k].Y) + result.MulBy014(&l0.r0, &l0.r1, &l0.r2) + result.MulBy014(&l.r0, &l.r1, &l.r2) + case 3: + qProj1[k].addMixedStep(&l, &q1[k]) + l.r1.Mul(&l.r1, &p[k].X) + l.r2.Mul(&l.r2, &p[k].Y) + result.MulBy014(&l0.r0, &l0.r1, &l0.r2) + result.MulBy014(&l.r0, &l.r1, &l.r2) + default: + return GT{}, errors.New("invalid loopCounter") + } + } + } + + return result, nil + +} + +// doubleStep doubles a point in Homogenous projective coordinates, and evaluates the line in Miller loop +// https://eprint.iacr.org/2013/722.pdf (Section 4.3) +func (p *g2Proj) doubleStep(evaluations *lineEvaluation) { + + // get some Element from our pool + var t1, A, B, C, D, E, EE, F, G, H, I, J, K fp.Element + A.Mul(&p.x, &p.y) + A.Halve() + B.Square(&p.y) + C.Square(&p.z) + D.Double(&C). + Add(&D, &C) + E.Mul(&D, &bTwistCurveCoeff) + F.Double(&E). + Add(&F, &E) + G.Add(&B, &F) + G.Halve() + H.Add(&p.y, &p.z). + Square(&H) + t1.Add(&B, &C) + H.Sub(&H, &t1) + I.Sub(&E, &B) + J.Square(&p.x) + EE.Square(&E) + K.Double(&EE). + Add(&K, &EE) + + // X, Y, Z + p.x.Sub(&B, &F). + Mul(&p.x, &A) + p.y.Square(&G). + Sub(&p.y, &K) + p.z.Mul(&B, &H) + + // Line evaluation + evaluations.r0.Set(&I) + evaluations.r1.Double(&J). + Add(&evaluations.r1, &J) + evaluations.r2.Neg(&H) +} + +// addMixedStep point addition in Mixed Homogenous projective and Affine coordinates +// https://eprint.iacr.org/2013/722.pdf (Section 4.3) +func (p *g2Proj) addMixedStep(evaluations *lineEvaluation, a *G2Affine) { + + // get some Element from our pool + var Y2Z1, X2Z1, O, L, C, D, E, F, G, H, t0, t1, t2, J fp.Element + Y2Z1.Mul(&a.Y, &p.z) + O.Sub(&p.y, &Y2Z1) + X2Z1.Mul(&a.X, &p.z) + L.Sub(&p.x, &X2Z1) + C.Square(&O) + D.Square(&L) + E.Mul(&L, &D) + F.Mul(&p.z, &C) + G.Mul(&p.x, &D) + t0.Double(&G) + H.Add(&E, &F). + Sub(&H, &t0) + t1.Mul(&p.y, &E) + + // X, Y, Z + p.x.Mul(&L, &H) + p.y.Sub(&G, &H). + Mul(&p.y, &O). + Sub(&p.y, &t1) + p.z.Mul(&E, &p.z) + + t2.Mul(&L, &a.Y) + J.Mul(&a.X, &O). + Sub(&J, &t2) + + // Line evaluation + evaluations.r0.Set(&J) + evaluations.r1.Neg(&O) + evaluations.r2.Set(&L) +} + // doubleStep doubles a point in Homogenous projective coordinates, and evaluates the line in Miller loop // https://eprint.iacr.org/2013/722.pdf (Section 4.3) func (p *g1Proj) doubleStep(evaluations *lineEvaluation) { diff --git a/internal/generator/config/bw6-761.go b/internal/generator/config/bw6-761.go index 33f34ae699..532846af88 100644 --- a/internal/generator/config/bw6-761.go +++ b/internal/generator/config/bw6-761.go @@ -22,6 +22,7 @@ var BW6_761 = Curve{ GLV: true, CofactorCleaning: true, CRange: []int{4, 5, 8, 16}, + Projective: true, }, // 2-isogeny HashE1: &HashSuiteSswu{ From ad96bf39f87bc892f398e98ef99f16ea863db72b Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Fri, 20 Oct 2023 14:42:44 -0400 Subject: [PATCH 2/6] refactor(bw6-761): clean and optimize new pairing --- ecc/bw6-761/g1.go | 80 ----- ecc/bw6-761/internal/fptower/e3.go | 28 ++ ecc/bw6-761/internal/fptower/e6_pairing.go | 105 ++---- ecc/bw6-761/pairing.go | 353 ++++----------------- internal/generator/config/bw6-761.go | 1 - 5 files changed, 112 insertions(+), 455 deletions(-) diff --git a/ecc/bw6-761/g1.go b/ecc/bw6-761/g1.go index 70a51e6989..aaded0f483 100644 --- a/ecc/bw6-761/g1.go +++ b/ecc/bw6-761/g1.go @@ -40,11 +40,6 @@ type g1JacExtended struct { X, Y, ZZ, ZZZ fp.Element } -// g1Proj point in projective coordinates -type g1Proj struct { - x, y, z fp.Element -} - // ------------------------------------------------------------------------------------------------- // Affine @@ -976,81 +971,6 @@ func (p *g1JacExtended) doubleMixed(q *G1Affine) *g1JacExtended { return p } -// ------------------------------------------------------------------------------------------------- -// Homogenous projective - -// Set sets p to the provided point -func (p *g1Proj) Set(a *g1Proj) *g1Proj { - p.x, p.y, p.z = a.x, a.y, a.z - return p -} - -// Neg computes -G -func (p *g1Proj) Neg(a *g1Proj) *g1Proj { - *p = *a - p.y.Neg(&a.y) - return p -} - -// FromAffine sets p = Q, p in homogenous projective, Q in affine -func (p *g1Proj) FromAffine(Q *G1Affine) *g1Proj { - if Q.X.IsZero() && Q.Y.IsZero() { - p.z.SetZero() - p.x.SetOne() - p.y.SetOne() - return p - } - p.z.SetOne() - p.x.Set(&Q.X) - p.y.Set(&Q.Y) - return p -} - -// BatchProjectiveToAffineG1 converts points in Projective coordinates to Affine coordinates -// performing a single field inversion (Montgomery batch inversion trick). -func BatchProjectiveToAffineG1(points []g1Proj) []G1Affine { - result := make([]G1Affine, len(points)) - zeroes := make([]bool, len(points)) - accumulator := fp.One() - - // batch invert all points[].Z coordinates with Montgomery batch inversion trick - // (stores points[].Z^-1 in result[i].X to avoid allocating a slice of fr.Elements) - for i := 0; i < len(points); i++ { - if points[i].z.IsZero() { - zeroes[i] = true - continue - } - result[i].X = accumulator - accumulator.Mul(&accumulator, &points[i].z) - } - - var accInverse fp.Element - accInverse.Inverse(&accumulator) - - for i := len(points) - 1; i >= 0; i-- { - if zeroes[i] { - // do nothing, (X=0, Y=0) is infinity point in affine - continue - } - result[i].X.Mul(&result[i].X, &accInverse) - accInverse.Mul(&accInverse, &points[i].z) - } - - // batch convert to affine. - parallel.Execute(len(points), func(start, end int) { - for i := start; i < end; i++ { - if zeroes[i] { - // do nothing, (X=0, Y=0) is infinity point in affine - continue - } - a := result[i].X - result[i].X.Mul(&points[i].x, &a) - result[i].Y.Mul(&points[i].y, &a) - } - }) - return result -} - // BatchJacobianToAffineG1 converts points in Jacobian coordinates to Affine coordinates // performing a single field inversion (Montgomery batch inversion trick). func BatchJacobianToAffineG1(points []G1Jac) []G1Affine { diff --git a/ecc/bw6-761/internal/fptower/e3.go b/ecc/bw6-761/internal/fptower/e3.go index 7434ebd59c..29e7fd7ff7 100644 --- a/ecc/bw6-761/internal/fptower/e3.go +++ b/ecc/bw6-761/internal/fptower/e3.go @@ -140,6 +140,34 @@ func (z *E3) MulByElement(x *E3, y *fp.Element) *E3 { return z } +// MulBy12 multiplication by sparse element (0,b1,b2) +func (x *E3) MulBy12(b1, b2 *fp.Element) *E3 { + var t1, t2, c0, tmp, c1, c2 fp.Element + t1.Mul(&x.A1, b1) + t2.Mul(&x.A2, b2) + c0.Add(&x.A1, &x.A2) + tmp.Add(b1, b2) + c0.Mul(&c0, &tmp) + c0.Sub(&c0, &t1) + c0.Sub(&c0, &t2) + c0.MulByNonResidue(&c0) + c1.Add(&x.A0, &x.A1) + c1.Mul(&c1, b1) + c1.Sub(&c1, &t1) + tmp.MulByNonResidue(&t2) + c1.Add(&c1, &tmp) + tmp.Add(&x.A0, &x.A2) + c2.Mul(b2, &tmp) + c2.Sub(&c2, &t2) + c2.Add(&c2, &t1) + + x.A0 = c0 + x.A1 = c1 + x.A2 = c2 + + return x +} + // MulBy01 multiplication by sparse element (c0,c1,0) func (z *E3) MulBy01(c0, c1 *fp.Element) *E3 { diff --git a/ecc/bw6-761/internal/fptower/e6_pairing.go b/ecc/bw6-761/internal/fptower/e6_pairing.go index 39df689e6f..6f9de4819d 100644 --- a/ecc/bw6-761/internal/fptower/e6_pairing.go +++ b/ecc/bw6-761/internal/fptower/e6_pairing.go @@ -149,76 +149,6 @@ func (z *E6) Expc2(x *E6) *E6 { return z } -// MulBy034 multiplication by sparse element (c0,0,0,c3,c4,0) -func (z *E6) MulBy034(c0, c3, c4 *fp.Element) *E6 { - - var a, b, d E3 - - a.MulByElement(&z.B0, c0) - - b.Set(&z.B1) - b.MulBy01(c3, c4) - - c0.Add(c0, c3) - d.Add(&z.B0, &z.B1) - d.MulBy01(c0, c4) - - z.B1.Add(&a, &b).Neg(&z.B1).Add(&z.B1, &d) - z.B0.MulByNonResidue(&b).Add(&z.B0, &a) - - return z -} - -// Mul034By034 multiplication of sparse element (c0,0,0,c3,c4,0) by sparse element (d0,0,0,d3,d4,0) -func Mul034By034(d0, d3, d4, c0, c3, c4 *fp.Element) [5]fp.Element { - var z00, tmp, x0, x3, x4, x04, x03, x34 fp.Element - x0.Mul(c0, d0) - x3.Mul(c3, d3) - x4.Mul(c4, d4) - tmp.Add(c0, c4) - x04.Add(d0, d4). - Mul(&x04, &tmp). - Sub(&x04, &x0). - Sub(&x04, &x4) - tmp.Add(c0, c3) - x03.Add(d0, d3). - Mul(&x03, &tmp). - Sub(&x03, &x0). - Sub(&x03, &x3) - tmp.Add(c3, c4) - x34.Add(d3, d4). - Mul(&x34, &tmp). - Sub(&x34, &x3). - Sub(&x34, &x4) - - z00.MulByNonResidue(&x4). - Add(&z00, &x0) - - return [5]fp.Element{z00, x3, x34, x03, x04} -} - -// MulBy01234 multiplies z by an E12 sparse element of the form (x0, x1, x2, x3, x4, 0) -func (z *E6) MulBy01234(x *[5]fp.Element) *E6 { - var c1, a, b, c, z0, z1 E3 - c0 := &E3{A0: x[0], A1: x[1], A2: x[2]} - c1.A0 = x[3] - c1.A1 = x[4] - a.Add(&z.B0, &z.B1) - b.Add(c0, &c1) - a.Mul(&a, &b) - b.Mul(&z.B0, c0) - c.Set(&z.B1).MulBy01(&x[3], &x[4]) - z1.Sub(&a, &b) - z1.Sub(&z1, &c) - z0.MulByNonResidue(&c) - z0.Add(&z0, &b) - - z.B0 = z0 - z.B1 = z1 - - return z -} - // MulBy014 multiplication by sparse element (c0,c1,0,0,c4,0) func (z *E6) MulBy014(c0, c1, c4 *fp.Element) *E6 { @@ -243,8 +173,8 @@ func (z *E6) MulBy014(c0, c1, c4 *fp.Element) *E6 { } // Mul014By014 multiplication of sparse element (c0,c1,0,0,c4,0) by sparse element (d0,d1,0,0,d4,0) -func (z *E6) Mul014By014(d0, d1, d4, c0, c1, c4 *fp.Element) *E6 { - var tmp, x0, x1, x4, x04, x01, x14 fp.Element +func Mul014By014(d0, d1, d4, c0, c1, c4 *fp.Element) [5]fp.Element { + var z00, tmp, x0, x1, x4, x04, x01, x14 fp.Element x0.Mul(c0, d0) x1.Mul(c1, d1) x4.Mul(c4, d4) @@ -264,13 +194,30 @@ func (z *E6) Mul014By014(d0, d1, d4, c0, c1, c4 *fp.Element) *E6 { Sub(&x14, &x1). Sub(&x14, &x4) - z.B0.A0.MulByNonResidue(&x4). - Add(&z.B0.A0, &x0) - z.B0.A1.Set(&x01) - z.B0.A2.Set(&x1) - z.B1.A0.SetZero() - z.B1.A1.Set(&x04) - z.B1.A2.Set(&x14) + z00.MulByNonResidue(&x4). + Add(&z00, &x0) + + return [5]fp.Element{z00, x01, x1, x04, x14} +} + +// MulBy01245 multiplies z by an E12 sparse element of the form (x0, x1, x2, 0, x4, x5) +func (z *E6) MulBy01245(x *[5]fp.Element) *E6 { + var c1, a, b, c, z0, z1 E3 + c0 := &E3{A0: x[0], A1: x[1], A2: x[2]} + c1.A1 = x[3] + c1.A2 = x[4] + a.Add(&z.B0, &z.B1) + b.Add(c0, &c1) + a.Mul(&a, &b) + b.Mul(&z.B0, c0) + c.Set(&z.B1).MulBy12(&x[3], &x[4]) + z1.Sub(&a, &b) + z1.Sub(&z1, &c) + z0.MulByNonResidue(&c) + z0.Add(&z0, &b) + + z.B0 = z0 + z.B1 = z1 return z } diff --git a/ecc/bw6-761/pairing.go b/ecc/bw6-761/pairing.go index 60f9d3ff33..eee6ac5a45 100644 --- a/ecc/bw6-761/pairing.go +++ b/ecc/bw6-761/pairing.go @@ -122,208 +122,12 @@ func FinalExponentiation(z *GT, _z ...*GT) GT { } // MillerLoop computes the multi-Miller loop -func MillerLoop(P []G1Affine, Q []G2Affine) (GT, error) { - // result, err := millerLoopTate(P, Q) - result, err := millerLoopAte(P, Q) - if err != nil { - return GT{}, err - } - - return result, nil -} - -// MillerLoop Optimal Tate alternative (or twisted ate or Eta revisited) -// computes the multi-Miller loop -// ∏ᵢ MillerLoop(Pᵢ, Qᵢ) = -// ∏ᵢ { fᵢ_{x₀+1+λ(x₀³-x₀²-x₀),Pᵢ}(Qᵢ) } -// -// Alg.2 in https://eprint.iacr.org/2021/1359.pdf -// Eq. (6) in https://hackmd.io/@gnark/BW6-761-changes -func millerLoopTate(P []G1Affine, Q []G2Affine) (GT, error) { - // check input size match - n := len(P) - if n == 0 || n != len(Q) { - return GT{}, errors.New("invalid inputs sizes") - } - - // filter infinity points - p0 := make([]G1Affine, 0, n) - q := make([]G2Affine, 0, n) - - for k := 0; k < n; k++ { - if P[k].IsInfinity() || Q[k].IsInfinity() { - continue - } - p0 = append(p0, P[k]) - q = append(q, Q[k]) - } - - n = len(q) - - // precomputations - pProj1 := make([]g1Proj, n) - p1 := make([]G1Affine, n) - for k := 0; k < n; k++ { - p1[k].Y.Neg(&p0[k].Y) - p1[k].X.Mul(&p0[k].X, &thirdRootOneG2) - pProj1[k].FromAffine(&p1[k]) - } - - // f_{a0+λ*a1,P}(Q) - var result GT - result.SetOne() - var l, l0 lineEvaluation - var prodLines [5]fp.Element - - var j int8 - - if n >= 1 { - // i = len(loopCounter0) - 2, separately to avoid an E12 Square - // (Square(res) = 1² = 1) - // j = 0 - // k = 0, separately to avoid MulBy034 (res × ℓ) - // (assign line to res) - - // pProj1[0] ← 2pProj1[0] and l0 the tangent ℓ passing 2pProj1[0] - pProj1[0].doubleStep(&l0) - // line evaluation at Q[0] (assign) - result.B1.A0.Mul(&l0.r1, &q[0].X) - result.B0.A0.Mul(&l0.r0, &q[0].Y) - result.B1.A1.Set(&l0.r2) - } - - // k = 1 - if n >= 2 { - // pProj1[1] ← 2pProj1[1] and l0 the tangent ℓ passing 2pProj1[1] - pProj1[1].doubleStep(&l0) - // line evaluation at Q[0] - l0.r1.Mul(&l0.r1, &q[1].X) - l0.r0.Mul(&l0.r0, &q[1].Y) - // ℓ × res - prodLines = fptower.Mul034By034(&l0.r0, &l0.r1, &l0.r2, &result.B0.A0, &result.B1.A0, &result.B1.A1) - result.B0.A0 = prodLines[0] - result.B0.A1 = prodLines[1] - result.B0.A2 = prodLines[2] - result.B1.A0 = prodLines[3] - result.B1.A1 = prodLines[4] - } - - // k >= 2 - for k := 2; k < n; k++ { - // pProj1[1] ← 2pProj1[1] and l0 the tangent ℓ passing 2pProj1[1] - pProj1[k].doubleStep(&l0) - // line evaluation at Q[k] - l0.r1.Mul(&l0.r1, &q[k].X) - l0.r0.Mul(&l0.r0, &q[k].Y) - // ℓ × res - result.MulBy034(&l0.r0, &l0.r1, &l0.r2) - } - - var tmp G1Affine - for i := len(loopCounter0) - 3; i >= 1; i-- { - // (∏ᵢfᵢ)² - // mutualize the square among n Miller loops - result.Square(&result) - - j = loopCounter1[i]*3 + loopCounter0[i] - - for k := 0; k < n; k++ { - // pProj1[1] ← 2pProj1[1] and l0 the tangent ℓ passing 2pProj1[1] - pProj1[k].doubleStep(&l0) - // line evaluation at Q[k] - l0.r1.Mul(&l0.r1, &q[k].X) - l0.r0.Mul(&l0.r0, &q[k].Y) - - switch j { - // cases -4, -2, 2, 4 do not occur, given the static loopCounters - case -3: - tmp.Neg(&p1[k]) - // pProj1[k] ← pProj1[k]-p1[k] and - // l the line ℓ passing pProj1[k] and -p1[k] - pProj1[k].addMixedStep(&l, &tmp) - // line evaluation at Q[k] - l.r1.Mul(&l.r1, &q[k].X) - l.r0.Mul(&l.r0, &q[k].Y) - // ℓ × ℓ - prodLines = fptower.Mul034By034(&l.r0, &l.r1, &l.r2, &l0.r0, &l0.r1, &l0.r2) - // (ℓ × ℓ) × res - result.MulBy01234(&prodLines) - case -1: - tmp.Neg(&p0[k]) - // pProj1[k] ← pProj1[k]-p0[k] and - // l the line ℓ passing pProj1[k] and -p0[k] - pProj1[k].addMixedStep(&l, &tmp) - // line evaluation at Q[k] - l.r1.Mul(&l.r1, &q[k].X) - l.r0.Mul(&l.r0, &q[k].Y) - // ℓ × ℓ - prodLines = fptower.Mul034By034(&l.r0, &l.r1, &l.r2, &l0.r0, &l0.r1, &l0.r2) - // (ℓ × ℓ) × res - result.MulBy01234(&prodLines) - case 0: - // ℓ × res - result.MulBy034(&l0.r0, &l0.r1, &l0.r2) - case 1: - // pProj1[k] ← pProj1[k]+p0[k] and - // l the line ℓ passing pProj1[k] and p0[k] - pProj1[k].addMixedStep(&l, &p0[k]) - // line evaluation at Q[k] - l.r1.Mul(&l.r1, &q[k].X) - l.r0.Mul(&l.r0, &q[k].Y) - // ℓ × ℓ - prodLines = fptower.Mul034By034(&l.r0, &l.r1, &l.r2, &l0.r0, &l0.r1, &l0.r2) - // (ℓ × ℓ) × res - result.MulBy01234(&prodLines) - case 3: - // pProj1[k] ← pProj1[k]+p1[k] and - // l the line ℓ passing pProj1[k] and p1[k] - pProj1[k].addMixedStep(&l, &p1[k]) - // line evaluation at Q[k] - l.r1.Mul(&l.r1, &q[k].X) - l.r0.Mul(&l.r0, &q[k].Y) - // ℓ × ℓ - prodLines = fptower.Mul034By034(&l.r0, &l.r1, &l.r2, &l0.r0, &l0.r1, &l0.r2) - // (ℓ × ℓ) × res - result.MulBy01234(&prodLines) - default: - return GT{}, errors.New("invalid loopCounter") - } - } - } - - // i = 0, separately to avoid a point addition - // j = -3 - result.Square(&result) - for k := 0; k < n; k++ { - // pProj1[k] ← 2pProj1[k] and l0 the tangent ℓ passing 2pProj1[k] - pProj1[k].doubleStep(&l0) - // line evaluation at Q[k] - l0.r1.Mul(&l0.r1, &q[k].X) - l0.r0.Mul(&l0.r0, &q[k].Y) - - tmp.Neg(&p1[k]) - // l the line passing pProj1[k] and -p1 - pProj1[k].lineCompute(&l, &tmp) - // line evaluation at Q[k] - l.r1.Mul(&l.r1, &q[k].X) - l.r0.Mul(&l.r0, &q[k].Y) - // ℓ × ℓ - prodLines = fptower.Mul034By034(&l.r0, &l.r1, &l.r2, &l0.r0, &l0.r1, &l0.r2) - // (ℓ × ℓ) × res - result.MulBy034(&l0.r0, &l0.r1, &l0.r2) - } - - return result, nil -} - -// MillerLoop Optimal ate alternative -// computes the multi-Miller loop // ∏ᵢ MillerLoop(Pᵢ, Qᵢ) = // ∏ᵢ { fᵢ_{x₀+1+λ(x₀³-x₀²-x₀),Qᵢ}(Pᵢ) } // // Alg.2 in https://eprint.iacr.org/2021/1359.pdf // Eq. (6') in https://hackmd.io/@gnark/BW6-761-changes -func millerLoopAte(P []G1Affine, Q []G2Affine) (GT, error) { +func MillerLoop(P []G1Affine, Q []G2Affine) (GT, error) { // check input size match n := len(P) if n == 0 || n != len(Q) { @@ -333,13 +137,16 @@ func millerLoopAte(P []G1Affine, Q []G2Affine) (GT, error) { // filter infinity points p := make([]G1Affine, 0, n) q0 := make([]G2Affine, 0, n) + q0Neg := make([]G2Affine, 0, n) + var tNeg G2Affine for k := 0; k < n; k++ { if P[k].IsInfinity() || Q[k].IsInfinity() { continue } p = append(p, P[k]) q0 = append(q0, Q[k]) + q0Neg = append(q0Neg, *tNeg.Neg(&q0[k])) } n = len(p) @@ -347,21 +154,46 @@ func millerLoopAte(P []G1Affine, Q []G2Affine) (GT, error) { // precomputations qProj1 := make([]g2Proj, n) q1 := make([]G2Affine, n) + q1Neg := make([]G2Affine, n) for k := 0; k < n; k++ { q1[k].Y.Neg(&q0[k].Y) q1[k].X.Mul(&q0[k].X, &thirdRootOneG1) qProj1[k].FromAffine(&q1[k]) + q1Neg[k].Neg(&q1[k]) } // f_{a0+λ*a1,Q}(P) var result GT result.SetOne() var l, l0 lineEvaluation + var prodLines [5]fp.Element var j int8 - var tmp G2Affine - for i := 188; i >= 0; i-- { + // i = len(loopCounter0) - 2, separately to avoid an E12 Square + // (Square(res) = 1² = 1) + // j = 0 + // k = 0, separately to avoid MulBy014 (res × ℓ) + // (assign line to res) + // qProj1[0] ← 2qProj1[0] and l0 the tangent ℓ passing 2qProj1[0] + qProj1[0].doubleStep(&l0) + // line evaluation at Q[0] (assign) + result.B0.A0.Set(&l0.r0) + result.B0.A1.Mul(&l0.r1, &p[0].X) + result.B1.A1.Mul(&l0.r2, &p[0].Y) + + // k >= 1 + for k := 1; k < n; k++ { + // qProj1[1] ← 2qProj1[1] and l0 the tangent ℓ passing 2qProj1[1] + qProj1[k].doubleStep(&l0) + // line evaluation at Q[k] + l0.r1.Mul(&l0.r1, &p[k].X) + l0.r2.Mul(&l0.r2, &p[k].Y) + // ℓ × res + result.MulBy014(&l0.r0, &l0.r1, &l0.r2) + } + + for i := 187; i >= 1; i-- { result.Square(&result) j = loopCounter1[i]*3 + loopCounter0[i] @@ -374,39 +206,51 @@ func millerLoopAte(P []G1Affine, Q []G2Affine) (GT, error) { switch j { // cases -4, -2, 2, 4 do not occur, given the static loopCounters case -3: - tmp.Neg(&q1[k]) - qProj1[k].addMixedStep(&l, &tmp) + qProj1[k].addMixedStep(&l, &q1Neg[k]) l.r1.Mul(&l.r1, &p[k].X) l.r2.Mul(&l.r2, &p[k].Y) - result.MulBy014(&l0.r0, &l0.r1, &l0.r2) - result.MulBy014(&l.r0, &l.r1, &l.r2) + prodLines = fptower.Mul014By014(&l0.r0, &l0.r1, &l0.r2, &l.r0, &l.r1, &l.r2) + result.MulBy01245(&prodLines) case -1: - tmp.Neg(&q0[k]) - qProj1[k].addMixedStep(&l, &tmp) + qProj1[k].addMixedStep(&l, &q0Neg[k]) l.r1.Mul(&l.r1, &p[k].X) l.r2.Mul(&l.r2, &p[k].Y) - result.MulBy014(&l0.r0, &l0.r1, &l0.r2) - result.MulBy014(&l.r0, &l.r1, &l.r2) + prodLines = fptower.Mul014By014(&l0.r0, &l0.r1, &l0.r2, &l.r0, &l.r1, &l.r2) + result.MulBy01245(&prodLines) case 0: result.MulBy014(&l0.r0, &l0.r1, &l0.r2) case 1: qProj1[k].addMixedStep(&l, &q0[k]) l.r1.Mul(&l.r1, &p[k].X) l.r2.Mul(&l.r2, &p[k].Y) - result.MulBy014(&l0.r0, &l0.r1, &l0.r2) - result.MulBy014(&l.r0, &l.r1, &l.r2) + prodLines = fptower.Mul014By014(&l0.r0, &l0.r1, &l0.r2, &l.r0, &l.r1, &l.r2) + result.MulBy01245(&prodLines) case 3: qProj1[k].addMixedStep(&l, &q1[k]) l.r1.Mul(&l.r1, &p[k].X) l.r2.Mul(&l.r2, &p[k].Y) - result.MulBy014(&l0.r0, &l0.r1, &l0.r2) - result.MulBy014(&l.r0, &l.r1, &l.r2) + prodLines = fptower.Mul014By014(&l0.r0, &l0.r1, &l0.r2, &l.r0, &l.r1, &l.r2) + result.MulBy01245(&prodLines) default: return GT{}, errors.New("invalid loopCounter") } } } + // i = 0, separately to avoid a point addition + // j = -3 + result.Square(&result) + for k := 0; k < n; k++ { + qProj1[k].doubleStep(&l0) + l0.r1.Mul(&l0.r1, &p[k].X) + l0.r2.Mul(&l0.r2, &p[k].Y) + qProj1[k].lineCompute(&l, &q1Neg[k]) + l.r1.Mul(&l.r1, &p[k].X) + l.r2.Mul(&l.r2, &p[k].Y) + prodLines = fptower.Mul014By014(&l0.r0, &l0.r1, &l0.r2, &l.r0, &l.r1, &l.r2) + result.MulBy01245(&prodLines) + } + return result, nil } @@ -489,90 +333,9 @@ func (p *g2Proj) addMixedStep(evaluations *lineEvaluation, a *G2Affine) { evaluations.r2.Set(&L) } -// doubleStep doubles a point in Homogenous projective coordinates, and evaluates the line in Miller loop -// https://eprint.iacr.org/2013/722.pdf (Section 4.3) -func (p *g1Proj) doubleStep(evaluations *lineEvaluation) { - - // get some Element from our pool - var t1, A, B, C, D, E, EE, F, G, H, I, J, K fp.Element - A.Mul(&p.x, &p.y) - A.Halve() - B.Square(&p.y) - C.Square(&p.z) - D.Double(&C). - Add(&D, &C) - - // E.Mul(&D, &bCurveCoeff) - E.Neg(&D) - - F.Double(&E). - Add(&F, &E) - G.Add(&B, &F) - G.Halve() - H.Add(&p.y, &p.z). - Square(&H) - t1.Add(&B, &C) - H.Sub(&H, &t1) - I.Sub(&E, &B) - J.Square(&p.x) - EE.Square(&E) - K.Double(&EE). - Add(&K, &EE) - - // X, Y, Z - p.x.Sub(&B, &F). - Mul(&p.x, &A) - p.y.Square(&G). - Sub(&p.y, &K) - p.z.Mul(&B, &H) - - // Line evaluation - evaluations.r0.Neg(&H) - evaluations.r1.Double(&J). - Add(&evaluations.r1, &J) - evaluations.r2.Set(&I) -} - -// addMixedStep point addition in Mixed Homogenous projective and Affine coordinates -// https://eprint.iacr.org/2013/722.pdf (Section 4.3) -func (p *g1Proj) addMixedStep(evaluations *lineEvaluation, a *G1Affine) { - - // get some Element from our pool - var Y2Z1, X2Z1, O, L, C, D, E, F, G, H, t0, t1, t2, J fp.Element - Y2Z1.Mul(&a.Y, &p.z) - O.Sub(&p.y, &Y2Z1) - X2Z1.Mul(&a.X, &p.z) - L.Sub(&p.x, &X2Z1) - C.Square(&O) - D.Square(&L) - E.Mul(&L, &D) - F.Mul(&p.z, &C) - G.Mul(&p.x, &D) - t0.Double(&G) - H.Add(&E, &F). - Sub(&H, &t0) - t1.Mul(&p.y, &E) - - // X, Y, Z - p.x.Mul(&L, &H) - p.y.Sub(&G, &H). - Mul(&p.y, &O). - Sub(&p.y, &t1) - p.z.Mul(&E, &p.z) - - t2.Mul(&L, &a.Y) - J.Mul(&a.X, &O). - Sub(&J, &t2) - - // Line evaluation - evaluations.r0.Set(&L) - evaluations.r1.Neg(&O) - evaluations.r2.Set(&J) -} - // lineCompute computes the line through p in Homogenous projective coordinates // and a in affine coordinates. It does not compute the resulting point p+a. -func (p *g1Proj) lineCompute(evaluations *lineEvaluation, a *G1Affine) { +func (p *g2Proj) lineCompute(evaluations *lineEvaluation, a *G2Affine) { // get some Element from our pool var Y2Z1, X2Z1, O, L, t2, J fp.Element @@ -585,7 +348,7 @@ func (p *g1Proj) lineCompute(evaluations *lineEvaluation, a *G1Affine) { Sub(&J, &t2) // Line evaluation - evaluations.r0.Set(&L) + evaluations.r0.Set(&J) evaluations.r1.Neg(&O) - evaluations.r2.Set(&J) + evaluations.r2.Set(&L) } diff --git a/internal/generator/config/bw6-761.go b/internal/generator/config/bw6-761.go index 532846af88..aa2ec6c363 100644 --- a/internal/generator/config/bw6-761.go +++ b/internal/generator/config/bw6-761.go @@ -13,7 +13,6 @@ var BW6_761 = Curve{ GLV: true, CofactorCleaning: true, CRange: []int{4, 5, 8, 16}, - Projective: true, }, G2: Point{ CoordType: "fp.Element", From 5d72d24bad74320af341cc61b641af40fd0241b9 Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Fri, 20 Oct 2023 14:50:32 -0400 Subject: [PATCH 3/6] refactor(bw6-756): use revisited Ate pairing instead of Tate --- ecc/bw6-633/g1.go | 80 -------- ecc/bw6-633/g2.go | 35 ++++ ecc/bw6-756/g1.go | 80 -------- ecc/bw6-756/g2.go | 35 ++++ ecc/bw6-756/internal/fptower/e3.go | 28 +++ ecc/bw6-756/internal/fptower/e6_pairing.go | 65 ++++--- ecc/bw6-756/pairing.go | 215 ++++++++------------- internal/generator/config/bw6-633.go | 2 +- internal/generator/config/bw6-756.go | 2 +- 9 files changed, 216 insertions(+), 326 deletions(-) diff --git a/ecc/bw6-633/g1.go b/ecc/bw6-633/g1.go index 282352cc91..39d083298b 100644 --- a/ecc/bw6-633/g1.go +++ b/ecc/bw6-633/g1.go @@ -40,11 +40,6 @@ type g1JacExtended struct { X, Y, ZZ, ZZZ fp.Element } -// g1Proj point in projective coordinates -type g1Proj struct { - x, y, z fp.Element -} - // ------------------------------------------------------------------------------------------------- // Affine @@ -965,81 +960,6 @@ func (p *g1JacExtended) doubleMixed(q *G1Affine) *g1JacExtended { return p } -// ------------------------------------------------------------------------------------------------- -// Homogenous projective - -// Set sets p to the provided point -func (p *g1Proj) Set(a *g1Proj) *g1Proj { - p.x, p.y, p.z = a.x, a.y, a.z - return p -} - -// Neg computes -G -func (p *g1Proj) Neg(a *g1Proj) *g1Proj { - *p = *a - p.y.Neg(&a.y) - return p -} - -// FromAffine sets p = Q, p in homogenous projective, Q in affine -func (p *g1Proj) FromAffine(Q *G1Affine) *g1Proj { - if Q.X.IsZero() && Q.Y.IsZero() { - p.z.SetZero() - p.x.SetOne() - p.y.SetOne() - return p - } - p.z.SetOne() - p.x.Set(&Q.X) - p.y.Set(&Q.Y) - return p -} - -// BatchProjectiveToAffineG1 converts points in Projective coordinates to Affine coordinates -// performing a single field inversion (Montgomery batch inversion trick). -func BatchProjectiveToAffineG1(points []g1Proj) []G1Affine { - result := make([]G1Affine, len(points)) - zeroes := make([]bool, len(points)) - accumulator := fp.One() - - // batch invert all points[].Z coordinates with Montgomery batch inversion trick - // (stores points[].Z^-1 in result[i].X to avoid allocating a slice of fr.Elements) - for i := 0; i < len(points); i++ { - if points[i].z.IsZero() { - zeroes[i] = true - continue - } - result[i].X = accumulator - accumulator.Mul(&accumulator, &points[i].z) - } - - var accInverse fp.Element - accInverse.Inverse(&accumulator) - - for i := len(points) - 1; i >= 0; i-- { - if zeroes[i] { - // do nothing, (X=0, Y=0) is infinity point in affine - continue - } - result[i].X.Mul(&result[i].X, &accInverse) - accInverse.Mul(&accInverse, &points[i].z) - } - - // batch convert to affine. - parallel.Execute(len(points), func(start, end int) { - for i := start; i < end; i++ { - if zeroes[i] { - // do nothing, (X=0, Y=0) is infinity point in affine - continue - } - a := result[i].X - result[i].X.Mul(&points[i].x, &a) - result[i].Y.Mul(&points[i].y, &a) - } - }) - return result -} - // BatchJacobianToAffineG1 converts points in Jacobian coordinates to Affine coordinates // performing a single field inversion (Montgomery batch inversion trick). func BatchJacobianToAffineG1(points []G1Jac) []G1Affine { diff --git a/ecc/bw6-633/g2.go b/ecc/bw6-633/g2.go index 17af770dbe..3eb3db0e83 100644 --- a/ecc/bw6-633/g2.go +++ b/ecc/bw6-633/g2.go @@ -40,6 +40,11 @@ type g2JacExtended struct { X, Y, ZZ, ZZZ fp.Element } +// g2Proj point in projective coordinates +type g2Proj struct { + x, y, z fp.Element +} + // ------------------------------------------------------------------------------------------------- // Affine @@ -875,6 +880,36 @@ func (p *g2JacExtended) doubleMixed(q *G2Affine) *g2JacExtended { return p } +// ------------------------------------------------------------------------------------------------- +// Homogenous projective + +// Set sets p to the provided point +func (p *g2Proj) Set(a *g2Proj) *g2Proj { + p.x, p.y, p.z = a.x, a.y, a.z + return p +} + +// Neg computes -G +func (p *g2Proj) Neg(a *g2Proj) *g2Proj { + *p = *a + p.y.Neg(&a.y) + return p +} + +// FromAffine sets p = Q, p in homogenous projective, Q in affine +func (p *g2Proj) FromAffine(Q *G2Affine) *g2Proj { + if Q.X.IsZero() && Q.Y.IsZero() { + p.z.SetZero() + p.x.SetOne() + p.y.SetOne() + return p + } + p.z.SetOne() + p.x.Set(&Q.X) + p.y.Set(&Q.Y) + return p +} + // BatchScalarMultiplicationG2 multiplies the same base by all scalars // and return resulting points in affine coordinates // uses a simple windowed-NAF like exponentiation algorithm diff --git a/ecc/bw6-756/g1.go b/ecc/bw6-756/g1.go index 49437c7d8f..3858f8ce95 100644 --- a/ecc/bw6-756/g1.go +++ b/ecc/bw6-756/g1.go @@ -40,11 +40,6 @@ type g1JacExtended struct { X, Y, ZZ, ZZZ fp.Element } -// g1Proj point in projective coordinates -type g1Proj struct { - x, y, z fp.Element -} - // ------------------------------------------------------------------------------------------------- // Affine @@ -965,81 +960,6 @@ func (p *g1JacExtended) doubleMixed(q *G1Affine) *g1JacExtended { return p } -// ------------------------------------------------------------------------------------------------- -// Homogenous projective - -// Set sets p to the provided point -func (p *g1Proj) Set(a *g1Proj) *g1Proj { - p.x, p.y, p.z = a.x, a.y, a.z - return p -} - -// Neg computes -G -func (p *g1Proj) Neg(a *g1Proj) *g1Proj { - *p = *a - p.y.Neg(&a.y) - return p -} - -// FromAffine sets p = Q, p in homogenous projective, Q in affine -func (p *g1Proj) FromAffine(Q *G1Affine) *g1Proj { - if Q.X.IsZero() && Q.Y.IsZero() { - p.z.SetZero() - p.x.SetOne() - p.y.SetOne() - return p - } - p.z.SetOne() - p.x.Set(&Q.X) - p.y.Set(&Q.Y) - return p -} - -// BatchProjectiveToAffineG1 converts points in Projective coordinates to Affine coordinates -// performing a single field inversion (Montgomery batch inversion trick). -func BatchProjectiveToAffineG1(points []g1Proj) []G1Affine { - result := make([]G1Affine, len(points)) - zeroes := make([]bool, len(points)) - accumulator := fp.One() - - // batch invert all points[].Z coordinates with Montgomery batch inversion trick - // (stores points[].Z^-1 in result[i].X to avoid allocating a slice of fr.Elements) - for i := 0; i < len(points); i++ { - if points[i].z.IsZero() { - zeroes[i] = true - continue - } - result[i].X = accumulator - accumulator.Mul(&accumulator, &points[i].z) - } - - var accInverse fp.Element - accInverse.Inverse(&accumulator) - - for i := len(points) - 1; i >= 0; i-- { - if zeroes[i] { - // do nothing, (X=0, Y=0) is infinity point in affine - continue - } - result[i].X.Mul(&result[i].X, &accInverse) - accInverse.Mul(&accInverse, &points[i].z) - } - - // batch convert to affine. - parallel.Execute(len(points), func(start, end int) { - for i := start; i < end; i++ { - if zeroes[i] { - // do nothing, (X=0, Y=0) is infinity point in affine - continue - } - a := result[i].X - result[i].X.Mul(&points[i].x, &a) - result[i].Y.Mul(&points[i].y, &a) - } - }) - return result -} - // BatchJacobianToAffineG1 converts points in Jacobian coordinates to Affine coordinates // performing a single field inversion (Montgomery batch inversion trick). func BatchJacobianToAffineG1(points []G1Jac) []G1Affine { diff --git a/ecc/bw6-756/g2.go b/ecc/bw6-756/g2.go index 012542286c..20b0c193c6 100644 --- a/ecc/bw6-756/g2.go +++ b/ecc/bw6-756/g2.go @@ -40,6 +40,11 @@ type g2JacExtended struct { X, Y, ZZ, ZZZ fp.Element } +// g2Proj point in projective coordinates +type g2Proj struct { + x, y, z fp.Element +} + // ------------------------------------------------------------------------------------------------- // Affine @@ -868,6 +873,36 @@ func (p *g2JacExtended) doubleMixed(q *G2Affine) *g2JacExtended { return p } +// ------------------------------------------------------------------------------------------------- +// Homogenous projective + +// Set sets p to the provided point +func (p *g2Proj) Set(a *g2Proj) *g2Proj { + p.x, p.y, p.z = a.x, a.y, a.z + return p +} + +// Neg computes -G +func (p *g2Proj) Neg(a *g2Proj) *g2Proj { + *p = *a + p.y.Neg(&a.y) + return p +} + +// FromAffine sets p = Q, p in homogenous projective, Q in affine +func (p *g2Proj) FromAffine(Q *G2Affine) *g2Proj { + if Q.X.IsZero() && Q.Y.IsZero() { + p.z.SetZero() + p.x.SetOne() + p.y.SetOne() + return p + } + p.z.SetOne() + p.x.Set(&Q.X) + p.y.Set(&Q.Y) + return p +} + // BatchScalarMultiplicationG2 multiplies the same base by all scalars // and return resulting points in affine coordinates // uses a simple windowed-NAF like exponentiation algorithm diff --git a/ecc/bw6-756/internal/fptower/e3.go b/ecc/bw6-756/internal/fptower/e3.go index b2fa2859d0..15fc2d4bbe 100644 --- a/ecc/bw6-756/internal/fptower/e3.go +++ b/ecc/bw6-756/internal/fptower/e3.go @@ -140,6 +140,34 @@ func (z *E3) MulByElement(x *E3, y *fp.Element) *E3 { return z } +// MulBy12 multiplication by sparse element (0,b1,b2) +func (x *E3) MulBy12(b1, b2 *fp.Element) *E3 { + var t1, t2, c0, tmp, c1, c2 fp.Element + t1.Mul(&x.A1, b1) + t2.Mul(&x.A2, b2) + c0.Add(&x.A1, &x.A2) + tmp.Add(b1, b2) + c0.Mul(&c0, &tmp) + c0.Sub(&c0, &t1) + c0.Sub(&c0, &t2) + c0.MulByNonResidue(&c0) + c1.Add(&x.A0, &x.A1) + c1.Mul(&c1, b1) + c1.Sub(&c1, &t1) + tmp.MulByNonResidue(&t2) + c1.Add(&c1, &tmp) + tmp.Add(&x.A0, &x.A2) + c2.Mul(b2, &tmp) + c2.Sub(&c2, &t2) + c2.Add(&c2, &t1) + + x.A0 = c0 + x.A1 = c1 + x.A2 = c2 + + return x +} + // MulBy01 multiplication by sparse element (c0,c1,0) func (z *E3) MulBy01(c0, c1 *fp.Element) *E3 { diff --git a/ecc/bw6-756/internal/fptower/e6_pairing.go b/ecc/bw6-756/internal/fptower/e6_pairing.go index f4af22821d..fe7eee34c5 100644 --- a/ecc/bw6-756/internal/fptower/e6_pairing.go +++ b/ecc/bw6-756/internal/fptower/e6_pairing.go @@ -126,65 +126,68 @@ func (z *E6) ExptMinus1Div3(x *E6) *E6 { return z } -// MulBy034 multiplication by sparse element (c0,0,0,c3,c4,0) -func (z *E6) MulBy034(c0, c3, c4 *fp.Element) *E6 { +// MulBy014 multiplication by sparse element (c0,c1,0,0,c4,0) +func (z *E6) MulBy014(c0, c1, c4 *fp.Element) *E6 { - var a, b, d E3 + var a, b E3 + var d fp.Element - a.MulByElement(&z.B0, c0) + a.Set(&z.B0) + a.MulBy01(c0, c1) b.Set(&z.B1) - b.MulBy01(c3, c4) + b.MulBy1(c4) + d.Add(c1, c4) - c0.Add(c0, c3) - d.Add(&z.B0, &z.B1) - d.MulBy01(c0, c4) - - z.B1.Add(&a, &b).Neg(&z.B1).Add(&z.B1, &d) - z.B0.MulByNonResidue(&b).Add(&z.B0, &a) + z.B1.Add(&z.B1, &z.B0) + z.B1.MulBy01(c0, &d) + z.B1.Sub(&z.B1, &a) + z.B1.Sub(&z.B1, &b) + z.B0.MulByNonResidue(&b) + z.B0.Add(&z.B0, &a) return z } -// Mul034By034 multiplication of sparse element (c0,0,0,c3,c4,0) by sparse element (d0,0,0,d3,d4,0) -func Mul034By034(d0, d3, d4, c0, c3, c4 *fp.Element) [5]fp.Element { - var z00, tmp, x0, x3, x4, x04, x03, x34 fp.Element +// Mul014By014 multiplication of sparse element (c0,c1,0,0,c4,0) by sparse element (d0,d1,0,0,d4,0) +func Mul014By014(d0, d1, d4, c0, c1, c4 *fp.Element) [5]fp.Element { + var z00, tmp, x0, x1, x4, x04, x01, x14 fp.Element x0.Mul(c0, d0) - x3.Mul(c3, d3) + x1.Mul(c1, d1) x4.Mul(c4, d4) tmp.Add(c0, c4) x04.Add(d0, d4). Mul(&x04, &tmp). Sub(&x04, &x0). Sub(&x04, &x4) - tmp.Add(c0, c3) - x03.Add(d0, d3). - Mul(&x03, &tmp). - Sub(&x03, &x0). - Sub(&x03, &x3) - tmp.Add(c3, c4) - x34.Add(d3, d4). - Mul(&x34, &tmp). - Sub(&x34, &x3). - Sub(&x34, &x4) + tmp.Add(c0, c1) + x01.Add(d0, d1). + Mul(&x01, &tmp). + Sub(&x01, &x0). + Sub(&x01, &x1) + tmp.Add(c1, c4) + x14.Add(d1, d4). + Mul(&x14, &tmp). + Sub(&x14, &x1). + Sub(&x14, &x4) z00.MulByNonResidue(&x4). Add(&z00, &x0) - return [5]fp.Element{z00, x3, x34, x03, x04} + return [5]fp.Element{z00, x01, x1, x04, x14} } -// MulBy01234 multiplies z by an E12 sparse element of the form (x0, x1, x2, x3, x4, 0) -func (z *E6) MulBy01234(x *[5]fp.Element) *E6 { +// MulBy01245 multiplies z by an E12 sparse element of the form (x0, x1, x2, 0, x4, x5) +func (z *E6) MulBy01245(x *[5]fp.Element) *E6 { var c1, a, b, c, z0, z1 E3 c0 := &E3{A0: x[0], A1: x[1], A2: x[2]} - c1.A0 = x[3] - c1.A1 = x[4] + c1.A1 = x[3] + c1.A2 = x[4] a.Add(&z.B0, &z.B1) b.Add(c0, &c1) a.Mul(&a, &b) b.Mul(&z.B0, c0) - c.Set(&z.B1).MulBy01(&x[3], &x[4]) + c.Set(&z.B1).MulBy12(&x[3], &x[4]) z1.Sub(&a, &b) z1.Sub(&z1, &c) z0.MulByNonResidue(&c) diff --git a/ecc/bw6-756/pairing.go b/ecc/bw6-756/pairing.go index bd77856426..f750fc2849 100644 --- a/ecc/bw6-756/pairing.go +++ b/ecc/bw6-756/pairing.go @@ -124,13 +124,12 @@ func FinalExponentiation(z *GT, _z ...*GT) GT { return result } -// MillerLoop Optimal Tate alternative (or twisted ate or Eta revisited) -// computes the multi-Miller loop +// MillerLoop computes the multi-Miller loop // ∏ᵢ MillerLoop(Pᵢ, Qᵢ) = -// ∏ᵢ { fᵢ_{x₀+1+λ(x₀³-x₀²-x₀),Pᵢ}(Qᵢ) } +// ∏ᵢ { fᵢ_{x₀+1+λ(x₀³-x₀²-x₀),Qᵢ}(Pᵢ) } // // Alg.2 in https://eprint.iacr.org/2021/1359.pdf -// Eq. (6) in https://hackmd.io/@gnark/BW6-761-changes +// Eq. (6') in https://hackmd.io/@gnark/BW6-761-changes func MillerLoop(P []G1Affine, Q []G2Affine) (GT, error) { // check input size match n := len(P) @@ -139,29 +138,34 @@ func MillerLoop(P []G1Affine, Q []G2Affine) (GT, error) { } // filter infinity points - p0 := make([]G1Affine, 0, n) - q := make([]G2Affine, 0, n) + p := make([]G1Affine, 0, n) + q0 := make([]G2Affine, 0, n) + q0Neg := make([]G2Affine, 0, n) + var tNeg G2Affine for k := 0; k < n; k++ { if P[k].IsInfinity() || Q[k].IsInfinity() { continue } - p0 = append(p0, P[k]) - q = append(q, Q[k]) + p = append(p, P[k]) + q0 = append(q0, Q[k]) + q0Neg = append(q0Neg, *tNeg.Neg(&q0[k])) } - n = len(q) + n = len(p) // precomputations - pProj1 := make([]g1Proj, n) - p1 := make([]G1Affine, n) + qProj1 := make([]g2Proj, n) + q1 := make([]G2Affine, n) + q1Neg := make([]G2Affine, n) for k := 0; k < n; k++ { - p1[k].Y.Neg(&p0[k].Y) - p1[k].X.Mul(&p0[k].X, &thirdRootOneG2) - pProj1[k].FromAffine(&p1[k]) + q1[k].Y.Neg(&q0[k].Y) + q1[k].X.Mul(&q0[k].X, &thirdRootOneG1) + qProj1[k].FromAffine(&q1[k]) + q1Neg[k].Neg(&q1[k]) } - // f_{a0+λ*a1,P}(Q) + // f_{a0+λ*a1,Q}(P) var result GT result.SetOne() var l, l0 lineEvaluation @@ -169,114 +173,67 @@ func MillerLoop(P []G1Affine, Q []G2Affine) (GT, error) { var j int8 - if n >= 1 { - // i = len(loopCounter0) - 2, separately to avoid an E12 Square - // (Square(res) = 1² = 1) - // j = 0 - // k = 0, separately to avoid MulBy034 (res × ℓ) - // (assign line to res) - - // pProj1[0] ← 2pProj1[0] and l0 the tangent ℓ passing 2pProj1[0] - pProj1[0].doubleStep(&l0) - // line evaluation at Q[0] (assign) - result.B1.A0.Mul(&l0.r1, &q[0].X) - result.B0.A0.Mul(&l0.r0, &q[0].Y) - result.B1.A1.Set(&l0.r2) - } - - // k = 1 - if n >= 2 { - // pProj1[1] ← 2pProj1[1] and l0 the tangent ℓ passing 2pProj1[1] - pProj1[1].doubleStep(&l0) - // line evaluation at Q[0] - l0.r1.Mul(&l0.r1, &q[1].X) - l0.r0.Mul(&l0.r0, &q[1].Y) - // ℓ × res - prodLines = fptower.Mul034By034(&l0.r0, &l0.r1, &l0.r2, &result.B0.A0, &result.B1.A0, &result.B1.A1) - result.B0.A0 = prodLines[0] - result.B0.A1 = prodLines[1] - result.B0.A2 = prodLines[2] - result.B1.A0 = prodLines[3] - result.B1.A1 = prodLines[4] - } - - // k >= 2 - for k := 2; k < n; k++ { - // pProj1[1] ← 2pProj1[1] and l0 the tangent ℓ passing 2pProj1[1] - pProj1[k].doubleStep(&l0) + // i = len(loopCounter0) - 2, separately to avoid an E12 Square + // (Square(res) = 1² = 1) + // j = 0 + // k = 0, separately to avoid MulBy014 (res × ℓ) + // (assign line to res) + // qProj1[0] ← 2qProj1[0] and l0 the tangent ℓ passing 2qProj1[0] + qProj1[0].doubleStep(&l0) + // line evaluation at Q[0] (assign) + result.B0.A0.Set(&l0.r0) + result.B0.A1.Mul(&l0.r1, &p[0].X) + result.B1.A1.Mul(&l0.r2, &p[0].Y) + + // k >= 1 + for k := 1; k < n; k++ { + // qProj1[1] ← 2qProj1[1] and l0 the tangent ℓ passing 2qProj1[1] + qProj1[k].doubleStep(&l0) // line evaluation at Q[k] - l0.r1.Mul(&l0.r1, &q[k].X) - l0.r0.Mul(&l0.r0, &q[k].Y) + l0.r1.Mul(&l0.r1, &p[k].X) + l0.r2.Mul(&l0.r2, &p[k].Y) // ℓ × res - result.MulBy034(&l0.r0, &l0.r1, &l0.r2) + result.MulBy014(&l0.r0, &l0.r1, &l0.r2) } - var tmp G1Affine - for i := len(loopCounter0) - 3; i >= 1; i-- { - // (∏ᵢfᵢ)² - // mutualize the square among n Miller loops + for i := 188; i >= 1; i-- { result.Square(&result) j = loopCounter1[i]*3 + loopCounter0[i] for k := 0; k < n; k++ { - // pProj1[1] ← 2pProj1[1] and l0 the tangent ℓ passing 2pProj1[1] - pProj1[k].doubleStep(&l0) - // line evaluation at Q[k] - l0.r1.Mul(&l0.r1, &q[k].X) - l0.r0.Mul(&l0.r0, &q[k].Y) + qProj1[k].doubleStep(&l0) + l0.r1.Mul(&l0.r1, &p[k].X) + l0.r2.Mul(&l0.r2, &p[k].Y) switch j { // cases -4, -2, 2, 4 do not occur, given the static loopCounters case -3: - tmp.Neg(&p1[k]) - // pProj1[k] ← pProj1[k]-p1[k] and - // l the line ℓ passing pProj1[k] and -p1[k] - pProj1[k].addMixedStep(&l, &tmp) - // line evaluation at Q[k] - l.r1.Mul(&l.r1, &q[k].X) - l.r0.Mul(&l.r0, &q[k].Y) - // ℓ × ℓ - prodLines = fptower.Mul034By034(&l.r0, &l.r1, &l.r2, &l0.r0, &l0.r1, &l0.r2) - // (ℓ × ℓ) × res - result.MulBy01234(&prodLines) + qProj1[k].addMixedStep(&l, &q1Neg[k]) + l.r1.Mul(&l.r1, &p[k].X) + l.r2.Mul(&l.r2, &p[k].Y) + prodLines = fptower.Mul014By014(&l0.r0, &l0.r1, &l0.r2, &l.r0, &l.r1, &l.r2) + result.MulBy01245(&prodLines) case -1: - tmp.Neg(&p0[k]) - // pProj1[k] ← pProj1[k]-p0[k] and - // l the line ℓ passing pProj1[k] and -p0[k] - pProj1[k].addMixedStep(&l, &tmp) - // line evaluation at Q[k] - l.r1.Mul(&l.r1, &q[k].X) - l.r0.Mul(&l.r0, &q[k].Y) - // ℓ × ℓ - prodLines = fptower.Mul034By034(&l.r0, &l.r1, &l.r2, &l0.r0, &l0.r1, &l0.r2) - // (ℓ × ℓ) × res - result.MulBy01234(&prodLines) + qProj1[k].addMixedStep(&l, &q0Neg[k]) + l.r1.Mul(&l.r1, &p[k].X) + l.r2.Mul(&l.r2, &p[k].Y) + prodLines = fptower.Mul014By014(&l0.r0, &l0.r1, &l0.r2, &l.r0, &l.r1, &l.r2) + result.MulBy01245(&prodLines) case 0: - // ℓ × res - result.MulBy034(&l0.r0, &l0.r1, &l0.r2) + result.MulBy014(&l0.r0, &l0.r1, &l0.r2) case 1: - // pProj1[k] ← pProj1[k]+p0[k] and - // l the line ℓ passing pProj1[k] and p0[k] - pProj1[k].addMixedStep(&l, &p0[k]) - // line evaluation at Q[k] - l.r1.Mul(&l.r1, &q[k].X) - l.r0.Mul(&l.r0, &q[k].Y) - // ℓ × ℓ - prodLines = fptower.Mul034By034(&l.r0, &l.r1, &l.r2, &l0.r0, &l0.r1, &l0.r2) - // (ℓ × ℓ) × res - result.MulBy01234(&prodLines) + qProj1[k].addMixedStep(&l, &q0[k]) + l.r1.Mul(&l.r1, &p[k].X) + l.r2.Mul(&l.r2, &p[k].Y) + prodLines = fptower.Mul014By014(&l0.r0, &l0.r1, &l0.r2, &l.r0, &l.r1, &l.r2) + result.MulBy01245(&prodLines) case 3: - // pProj1[k] ← pProj1[k]+p1[k] and - // l the line ℓ passing pProj1[k] and p1[k] - pProj1[k].addMixedStep(&l, &p1[k]) - // line evaluation at Q[k] - l.r1.Mul(&l.r1, &q[k].X) - l.r0.Mul(&l.r0, &q[k].Y) - // ℓ × ℓ - prodLines = fptower.Mul034By034(&l.r0, &l.r1, &l.r2, &l0.r0, &l0.r1, &l0.r2) - // (ℓ × ℓ) × res - result.MulBy01234(&prodLines) + qProj1[k].addMixedStep(&l, &q1[k]) + l.r1.Mul(&l.r1, &p[k].X) + l.r2.Mul(&l.r2, &p[k].Y) + prodLines = fptower.Mul014By014(&l0.r0, &l0.r1, &l0.r2, &l.r0, &l.r1, &l.r2) + result.MulBy01245(&prodLines) default: return GT{}, errors.New("invalid loopCounter") } @@ -287,30 +244,23 @@ func MillerLoop(P []G1Affine, Q []G2Affine) (GT, error) { // j = -3 result.Square(&result) for k := 0; k < n; k++ { - // pProj1[k] ← 2pProj1[k] and l0 the tangent ℓ passing 2pProj1[k] - pProj1[k].doubleStep(&l0) - // line evaluation at Q[k] - l0.r1.Mul(&l0.r1, &q[k].X) - l0.r0.Mul(&l0.r0, &q[k].Y) - - tmp.Neg(&p1[k]) - // l the line passing pProj1[k] and -p1 - pProj1[k].lineCompute(&l, &tmp) - // line evaluation at Q[k] - l.r1.Mul(&l.r1, &q[k].X) - l.r0.Mul(&l.r0, &q[k].Y) - // ℓ × ℓ - prodLines = fptower.Mul034By034(&l.r0, &l.r1, &l.r2, &l0.r0, &l0.r1, &l0.r2) - // (ℓ × ℓ) × res - result.MulBy01234(&prodLines) + qProj1[k].doubleStep(&l0) + l0.r1.Mul(&l0.r1, &p[k].X) + l0.r2.Mul(&l0.r2, &p[k].Y) + qProj1[k].lineCompute(&l, &q1Neg[k]) + l.r1.Mul(&l.r1, &p[k].X) + l.r2.Mul(&l.r2, &p[k].Y) + prodLines = fptower.Mul014By014(&l0.r0, &l0.r1, &l0.r2, &l.r0, &l.r1, &l.r2) + result.MulBy01245(&prodLines) } return result, nil + } // doubleStep doubles a point in Homogenous projective coordinates, and evaluates the line in Miller loop // https://eprint.iacr.org/2013/722.pdf (Section 4.3) -func (p *g1Proj) doubleStep(evaluations *lineEvaluation) { +func (p *g2Proj) doubleStep(evaluations *lineEvaluation) { // get some Element from our pool var t1, A, B, C, D, E, EE, F, G, H, I, J, K fp.Element @@ -320,8 +270,7 @@ func (p *g1Proj) doubleStep(evaluations *lineEvaluation) { C.Square(&p.z) D.Double(&C). Add(&D, &C) - // E.Mul(&D, &bCurveCoeff) - E.Set(&D) + E.Mul(&D, &bTwistCurveCoeff) F.Double(&E). Add(&F, &E) G.Add(&B, &F) @@ -344,15 +293,15 @@ func (p *g1Proj) doubleStep(evaluations *lineEvaluation) { p.z.Mul(&B, &H) // Line evaluation - evaluations.r0.Neg(&H) + evaluations.r0.Set(&I) evaluations.r1.Double(&J). Add(&evaluations.r1, &J) - evaluations.r2.Set(&I) + evaluations.r2.Neg(&H) } // addMixedStep point addition in Mixed Homogenous projective and Affine coordinates // https://eprint.iacr.org/2013/722.pdf (Section 4.3) -func (p *g1Proj) addMixedStep(evaluations *lineEvaluation, a *G1Affine) { +func (p *g2Proj) addMixedStep(evaluations *lineEvaluation, a *G2Affine) { // get some Element from our pool var Y2Z1, X2Z1, O, L, C, D, E, F, G, H, t0, t1, t2, J fp.Element @@ -382,14 +331,14 @@ func (p *g1Proj) addMixedStep(evaluations *lineEvaluation, a *G1Affine) { Sub(&J, &t2) // Line evaluation - evaluations.r0.Set(&L) + evaluations.r0.Set(&J) evaluations.r1.Neg(&O) - evaluations.r2.Set(&J) + evaluations.r2.Set(&L) } // lineCompute computes the line through p in Homogenous projective coordinates // and a in affine coordinates. It does not compute the resulting point p+a. -func (p *g1Proj) lineCompute(evaluations *lineEvaluation, a *G1Affine) { +func (p *g2Proj) lineCompute(evaluations *lineEvaluation, a *G2Affine) { // get some Element from our pool var Y2Z1, X2Z1, O, L, t2, J fp.Element @@ -402,7 +351,7 @@ func (p *g1Proj) lineCompute(evaluations *lineEvaluation, a *G1Affine) { Sub(&J, &t2) // Line evaluation - evaluations.r0.Set(&L) + evaluations.r0.Set(&J) evaluations.r1.Neg(&O) - evaluations.r2.Set(&J) + evaluations.r2.Set(&L) } diff --git a/internal/generator/config/bw6-633.go b/internal/generator/config/bw6-633.go index a0584e29ea..9805f1caca 100644 --- a/internal/generator/config/bw6-633.go +++ b/internal/generator/config/bw6-633.go @@ -13,7 +13,6 @@ var BW6_633 = Curve{ GLV: true, CofactorCleaning: true, CRange: []int{4, 5, 8, 16}, - Projective: true, }, G2: Point{ CoordType: "fp.Element", @@ -22,6 +21,7 @@ var BW6_633 = Curve{ GLV: true, CofactorCleaning: true, CRange: []int{4, 5, 8, 16}, + Projective: true, }, // 7-isogeny HashE1: &HashSuiteSswu{ diff --git a/internal/generator/config/bw6-756.go b/internal/generator/config/bw6-756.go index 4657c6cc4b..21ab364ccf 100644 --- a/internal/generator/config/bw6-756.go +++ b/internal/generator/config/bw6-756.go @@ -13,7 +13,6 @@ var BW6_756 = Curve{ GLV: true, CofactorCleaning: true, CRange: []int{4, 5, 8, 16}, - Projective: true, }, G2: Point{ CoordType: "fp.Element", @@ -22,6 +21,7 @@ var BW6_756 = Curve{ GLV: true, CofactorCleaning: true, CRange: []int{4, 5, 8, 16}, + Projective: true, }, // 2-isogeny HashE1: &HashSuiteSswu{ From fcfade4db4ba6dadeaf34b34f9bd9bbb472dee59 Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Fri, 20 Oct 2023 15:11:25 -0400 Subject: [PATCH 4/6] refactor(bw6-633): clean pairing --- ecc/bw6-633/g1.go | 35 ++++++++++++++ ecc/bw6-633/g2.go | 35 -------------- internal/generator/config/bw6-633.go | 2 +- internal/generator/ecc/template/point.go.tmpl | 46 ------------------- 4 files changed, 36 insertions(+), 82 deletions(-) diff --git a/ecc/bw6-633/g1.go b/ecc/bw6-633/g1.go index 39d083298b..b911740a77 100644 --- a/ecc/bw6-633/g1.go +++ b/ecc/bw6-633/g1.go @@ -40,6 +40,11 @@ type g1JacExtended struct { X, Y, ZZ, ZZZ fp.Element } +// g1Proj point in projective coordinates +type g1Proj struct { + x, y, z fp.Element +} + // ------------------------------------------------------------------------------------------------- // Affine @@ -960,6 +965,36 @@ func (p *g1JacExtended) doubleMixed(q *G1Affine) *g1JacExtended { return p } +// ------------------------------------------------------------------------------------------------- +// Homogenous projective + +// Set sets p to the provided point +func (p *g1Proj) Set(a *g1Proj) *g1Proj { + p.x, p.y, p.z = a.x, a.y, a.z + return p +} + +// Neg computes -G +func (p *g1Proj) Neg(a *g1Proj) *g1Proj { + *p = *a + p.y.Neg(&a.y) + return p +} + +// FromAffine sets p = Q, p in homogenous projective, Q in affine +func (p *g1Proj) FromAffine(Q *G1Affine) *g1Proj { + if Q.X.IsZero() && Q.Y.IsZero() { + p.z.SetZero() + p.x.SetOne() + p.y.SetOne() + return p + } + p.z.SetOne() + p.x.Set(&Q.X) + p.y.Set(&Q.Y) + return p +} + // BatchJacobianToAffineG1 converts points in Jacobian coordinates to Affine coordinates // performing a single field inversion (Montgomery batch inversion trick). func BatchJacobianToAffineG1(points []G1Jac) []G1Affine { diff --git a/ecc/bw6-633/g2.go b/ecc/bw6-633/g2.go index 3eb3db0e83..17af770dbe 100644 --- a/ecc/bw6-633/g2.go +++ b/ecc/bw6-633/g2.go @@ -40,11 +40,6 @@ type g2JacExtended struct { X, Y, ZZ, ZZZ fp.Element } -// g2Proj point in projective coordinates -type g2Proj struct { - x, y, z fp.Element -} - // ------------------------------------------------------------------------------------------------- // Affine @@ -880,36 +875,6 @@ func (p *g2JacExtended) doubleMixed(q *G2Affine) *g2JacExtended { return p } -// ------------------------------------------------------------------------------------------------- -// Homogenous projective - -// Set sets p to the provided point -func (p *g2Proj) Set(a *g2Proj) *g2Proj { - p.x, p.y, p.z = a.x, a.y, a.z - return p -} - -// Neg computes -G -func (p *g2Proj) Neg(a *g2Proj) *g2Proj { - *p = *a - p.y.Neg(&a.y) - return p -} - -// FromAffine sets p = Q, p in homogenous projective, Q in affine -func (p *g2Proj) FromAffine(Q *G2Affine) *g2Proj { - if Q.X.IsZero() && Q.Y.IsZero() { - p.z.SetZero() - p.x.SetOne() - p.y.SetOne() - return p - } - p.z.SetOne() - p.x.Set(&Q.X) - p.y.Set(&Q.Y) - return p -} - // BatchScalarMultiplicationG2 multiplies the same base by all scalars // and return resulting points in affine coordinates // uses a simple windowed-NAF like exponentiation algorithm diff --git a/internal/generator/config/bw6-633.go b/internal/generator/config/bw6-633.go index 9805f1caca..a0584e29ea 100644 --- a/internal/generator/config/bw6-633.go +++ b/internal/generator/config/bw6-633.go @@ -13,6 +13,7 @@ var BW6_633 = Curve{ GLV: true, CofactorCleaning: true, CRange: []int{4, 5, 8, 16}, + Projective: true, }, G2: Point{ CoordType: "fp.Element", @@ -21,7 +22,6 @@ var BW6_633 = Curve{ GLV: true, CofactorCleaning: true, CRange: []int{4, 5, 8, 16}, - Projective: true, }, // 7-isogeny HashE1: &HashSuiteSswu{ diff --git a/internal/generator/ecc/template/point.go.tmpl b/internal/generator/ecc/template/point.go.tmpl index dfabf475fd..411f14d489 100644 --- a/internal/generator/ecc/template/point.go.tmpl +++ b/internal/generator/ecc/template/point.go.tmpl @@ -1521,52 +1521,6 @@ func (p *{{ $TProjective }}) FromAffine(Q *{{ $TAffine }}) *{{ $TProjective }} { return p } -{{- if eq .PointName "g1"}} -// BatchProjectiveToAffine{{ toUpper .PointName }} converts points in Projective coordinates to Affine coordinates -// performing a single field inversion (Montgomery batch inversion trick). -func BatchProjectiveToAffine{{ toUpper .PointName }}(points []{{ $TProjective }}) []{{ $TAffine }} { - result := make([]{{ $TAffine }}, len(points)) - zeroes := make([]bool, len(points)) - accumulator := fp.One() - - // batch invert all points[].Z coordinates with Montgomery batch inversion trick - // (stores points[].Z^-1 in result[i].X to avoid allocating a slice of fr.Elements) - for i := 0; i < len(points); i++ { - if points[i].z.IsZero() { - zeroes[i] = true - continue - } - result[i].X = accumulator - accumulator.Mul(&accumulator, &points[i].z) - } - - var accInverse fp.Element - accInverse.Inverse(&accumulator) - - for i := len(points) - 1; i >= 0; i-- { - if zeroes[i] { - // do nothing, (X=0, Y=0) is infinity point in affine - continue - } - result[i].X.Mul(&result[i].X, &accInverse) - accInverse.Mul(&accInverse, &points[i].z) - } - - // batch convert to affine. - parallel.Execute(len(points), func(start, end int) { - for i := start; i < end; i++ { - if zeroes[i] { - // do nothing, (X=0, Y=0) is infinity point in affine - continue - } - a := result[i].X - result[i].X.Mul(&points[i].x, &a) - result[i].Y.Mul(&points[i].y, &a) - } - }) - return result -} -{{end }} {{end }} From 10e569577bc8f144039c04687211d2771b576adb Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Fri, 20 Oct 2023 16:45:37 -0400 Subject: [PATCH 5/6] fix(bw6/pairing): skipping points at inf --- ecc/bw6-756/pairing.go | 50 ++++++++++++++++++++++-------------------- ecc/bw6-761/pairing.go | 50 ++++++++++++++++++++++-------------------- 2 files changed, 52 insertions(+), 48 deletions(-) diff --git a/ecc/bw6-756/pairing.go b/ecc/bw6-756/pairing.go index f750fc2849..f4f91fb788 100644 --- a/ecc/bw6-756/pairing.go +++ b/ecc/bw6-756/pairing.go @@ -140,16 +140,13 @@ func MillerLoop(P []G1Affine, Q []G2Affine) (GT, error) { // filter infinity points p := make([]G1Affine, 0, n) q0 := make([]G2Affine, 0, n) - q0Neg := make([]G2Affine, 0, n) - var tNeg G2Affine for k := 0; k < n; k++ { if P[k].IsInfinity() || Q[k].IsInfinity() { continue } p = append(p, P[k]) q0 = append(q0, Q[k]) - q0Neg = append(q0Neg, *tNeg.Neg(&q0[k])) } n = len(p) @@ -158,8 +155,11 @@ func MillerLoop(P []G1Affine, Q []G2Affine) (GT, error) { qProj1 := make([]g2Proj, n) q1 := make([]G2Affine, n) q1Neg := make([]G2Affine, n) + q0Neg := make([]G2Affine, n) for k := 0; k < n; k++ { q1[k].Y.Neg(&q0[k].Y) + q0Neg[k].X.Set(&q0[k].X) + q0Neg[k].Y.Set(&q1[k].Y) q1[k].X.Mul(&q0[k].X, &thirdRootOneG1) qProj1[k].FromAffine(&q1[k]) q1Neg[k].Neg(&q1[k]) @@ -173,27 +173,29 @@ func MillerLoop(P []G1Affine, Q []G2Affine) (GT, error) { var j int8 - // i = len(loopCounter0) - 2, separately to avoid an E12 Square - // (Square(res) = 1² = 1) - // j = 0 - // k = 0, separately to avoid MulBy014 (res × ℓ) - // (assign line to res) - // qProj1[0] ← 2qProj1[0] and l0 the tangent ℓ passing 2qProj1[0] - qProj1[0].doubleStep(&l0) - // line evaluation at Q[0] (assign) - result.B0.A0.Set(&l0.r0) - result.B0.A1.Mul(&l0.r1, &p[0].X) - result.B1.A1.Mul(&l0.r2, &p[0].Y) - - // k >= 1 - for k := 1; k < n; k++ { - // qProj1[1] ← 2qProj1[1] and l0 the tangent ℓ passing 2qProj1[1] - qProj1[k].doubleStep(&l0) - // line evaluation at Q[k] - l0.r1.Mul(&l0.r1, &p[k].X) - l0.r2.Mul(&l0.r2, &p[k].Y) - // ℓ × res - result.MulBy014(&l0.r0, &l0.r1, &l0.r2) + if n >= 1 { + // i = 189, separately to avoid an E12 Square + // (Square(res) = 1² = 1) + // j = 0 + // k = 0, separately to avoid MulBy014 (res × ℓ) + // (assign line to res) + // qProj1[0] ← 2qProj1[0] and l0 the tangent ℓ passing 2qProj1[0] + qProj1[0].doubleStep(&l0) + // line evaluation at Q[0] (assign) + result.B0.A0.Set(&l0.r0) + result.B0.A1.Mul(&l0.r1, &p[0].X) + result.B1.A1.Mul(&l0.r2, &p[0].Y) + + // k >= 1 + for k := 1; k < n; k++ { + // qProj1[1] ← 2qProj1[1] and l0 the tangent ℓ passing 2qProj1[1] + qProj1[k].doubleStep(&l0) + // line evaluation at Q[k] + l0.r1.Mul(&l0.r1, &p[k].X) + l0.r2.Mul(&l0.r2, &p[k].Y) + // ℓ × res + result.MulBy014(&l0.r0, &l0.r1, &l0.r2) + } } for i := 188; i >= 1; i-- { diff --git a/ecc/bw6-761/pairing.go b/ecc/bw6-761/pairing.go index eee6ac5a45..974890f186 100644 --- a/ecc/bw6-761/pairing.go +++ b/ecc/bw6-761/pairing.go @@ -137,16 +137,13 @@ func MillerLoop(P []G1Affine, Q []G2Affine) (GT, error) { // filter infinity points p := make([]G1Affine, 0, n) q0 := make([]G2Affine, 0, n) - q0Neg := make([]G2Affine, 0, n) - var tNeg G2Affine for k := 0; k < n; k++ { if P[k].IsInfinity() || Q[k].IsInfinity() { continue } p = append(p, P[k]) q0 = append(q0, Q[k]) - q0Neg = append(q0Neg, *tNeg.Neg(&q0[k])) } n = len(p) @@ -155,8 +152,11 @@ func MillerLoop(P []G1Affine, Q []G2Affine) (GT, error) { qProj1 := make([]g2Proj, n) q1 := make([]G2Affine, n) q1Neg := make([]G2Affine, n) + q0Neg := make([]G2Affine, n) for k := 0; k < n; k++ { q1[k].Y.Neg(&q0[k].Y) + q0Neg[k].X.Set(&q0[k].X) + q0Neg[k].Y.Set(&q1[k].Y) q1[k].X.Mul(&q0[k].X, &thirdRootOneG1) qProj1[k].FromAffine(&q1[k]) q1Neg[k].Neg(&q1[k]) @@ -170,27 +170,29 @@ func MillerLoop(P []G1Affine, Q []G2Affine) (GT, error) { var j int8 - // i = len(loopCounter0) - 2, separately to avoid an E12 Square - // (Square(res) = 1² = 1) - // j = 0 - // k = 0, separately to avoid MulBy014 (res × ℓ) - // (assign line to res) - // qProj1[0] ← 2qProj1[0] and l0 the tangent ℓ passing 2qProj1[0] - qProj1[0].doubleStep(&l0) - // line evaluation at Q[0] (assign) - result.B0.A0.Set(&l0.r0) - result.B0.A1.Mul(&l0.r1, &p[0].X) - result.B1.A1.Mul(&l0.r2, &p[0].Y) - - // k >= 1 - for k := 1; k < n; k++ { - // qProj1[1] ← 2qProj1[1] and l0 the tangent ℓ passing 2qProj1[1] - qProj1[k].doubleStep(&l0) - // line evaluation at Q[k] - l0.r1.Mul(&l0.r1, &p[k].X) - l0.r2.Mul(&l0.r2, &p[k].Y) - // ℓ × res - result.MulBy014(&l0.r0, &l0.r1, &l0.r2) + if n >= 1 { + // i = 188, separately to avoid an E12 Square + // (Square(res) = 1² = 1) + // j = 0 + // k = 0, separately to avoid MulBy014 (res × ℓ) + // (assign line to res) + // qProj1[0] ← 2qProj1[0] and l0 the tangent ℓ passing 2qProj1[0] + qProj1[0].doubleStep(&l0) + // line evaluation at Q[0] (assign) + result.B0.A0.Set(&l0.r0) + result.B0.A1.Mul(&l0.r1, &p[0].X) + result.B1.A1.Mul(&l0.r2, &p[0].Y) + + // k >= 1 + for k := 1; k < n; k++ { + // qProj1[1] ← 2qProj1[1] and l0 the tangent ℓ passing 2qProj1[1] + qProj1[k].doubleStep(&l0) + // line evaluation at Q[k] + l0.r1.Mul(&l0.r1, &p[k].X) + l0.r2.Mul(&l0.r2, &p[k].Y) + // ℓ × res + result.MulBy014(&l0.r0, &l0.r1, &l0.r2) + } } for i := 187; i >= 1; i-- { From 0aaff184e36839b81ace718bc706c22667b7ab63 Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Fri, 20 Oct 2023 17:13:28 -0400 Subject: [PATCH 6/6] perf(bw6/pairing): isolate first iterations --- ecc/bw6-756/pairing.go | 35 +++++++++++++++++++++++++---------- ecc/bw6-761/pairing.go | 35 +++++++++++++++++++++++++---------- 2 files changed, 50 insertions(+), 20 deletions(-) diff --git a/ecc/bw6-756/pairing.go b/ecc/bw6-756/pairing.go index f4f91fb788..d4a65e0f0d 100644 --- a/ecc/bw6-756/pairing.go +++ b/ecc/bw6-756/pairing.go @@ -185,17 +185,32 @@ func MillerLoop(P []G1Affine, Q []G2Affine) (GT, error) { result.B0.A0.Set(&l0.r0) result.B0.A1.Mul(&l0.r1, &p[0].X) result.B1.A1.Mul(&l0.r2, &p[0].Y) + } - // k >= 1 - for k := 1; k < n; k++ { - // qProj1[1] ← 2qProj1[1] and l0 the tangent ℓ passing 2qProj1[1] - qProj1[k].doubleStep(&l0) - // line evaluation at Q[k] - l0.r1.Mul(&l0.r1, &p[k].X) - l0.r2.Mul(&l0.r2, &p[k].Y) - // ℓ × res - result.MulBy014(&l0.r0, &l0.r1, &l0.r2) - } + // k = 1 + if n >= 2 { + // qProj1[1] ← 2qProj1[1] and l0 the tangent ℓ passing 2qProj1[1] + qProj1[1].doubleStep(&l0) + // line evaluation at Q[1] + l0.r1.Mul(&l0.r1, &p[1].X) + l0.r2.Mul(&l0.r2, &p[1].Y) + prodLines = fptower.Mul014By014(&l0.r0, &l0.r1, &l0.r2, &result.B0.A0, &result.B0.A1, &result.B1.A1) + result.B0.A0 = prodLines[0] + result.B0.A1 = prodLines[1] + result.B0.A2 = prodLines[2] + result.B1.A1 = prodLines[3] + result.B1.A2 = prodLines[4] + } + + // k >= 2 + for k := 2; k < n; k++ { + // qProj1[k] ← 2qProj1[k] and l0 the tangent ℓ passing 2qProj1[k] + qProj1[k].doubleStep(&l0) + // line evaluation at Q[k] + l0.r1.Mul(&l0.r1, &p[k].X) + l0.r2.Mul(&l0.r2, &p[k].Y) + // ℓ × res + result.MulBy014(&l0.r0, &l0.r1, &l0.r2) } for i := 188; i >= 1; i-- { diff --git a/ecc/bw6-761/pairing.go b/ecc/bw6-761/pairing.go index 974890f186..d1160bbe76 100644 --- a/ecc/bw6-761/pairing.go +++ b/ecc/bw6-761/pairing.go @@ -182,17 +182,32 @@ func MillerLoop(P []G1Affine, Q []G2Affine) (GT, error) { result.B0.A0.Set(&l0.r0) result.B0.A1.Mul(&l0.r1, &p[0].X) result.B1.A1.Mul(&l0.r2, &p[0].Y) + } - // k >= 1 - for k := 1; k < n; k++ { - // qProj1[1] ← 2qProj1[1] and l0 the tangent ℓ passing 2qProj1[1] - qProj1[k].doubleStep(&l0) - // line evaluation at Q[k] - l0.r1.Mul(&l0.r1, &p[k].X) - l0.r2.Mul(&l0.r2, &p[k].Y) - // ℓ × res - result.MulBy014(&l0.r0, &l0.r1, &l0.r2) - } + // k = 1 + if n >= 2 { + // qProj1[1] ← 2qProj1[1] and l0 the tangent ℓ passing 2qProj1[1] + qProj1[1].doubleStep(&l0) + // line evaluation at Q[1] + l0.r1.Mul(&l0.r1, &p[1].X) + l0.r2.Mul(&l0.r2, &p[1].Y) + prodLines = fptower.Mul014By014(&l0.r0, &l0.r1, &l0.r2, &result.B0.A0, &result.B0.A1, &result.B1.A1) + result.B0.A0 = prodLines[0] + result.B0.A1 = prodLines[1] + result.B0.A2 = prodLines[2] + result.B1.A1 = prodLines[3] + result.B1.A2 = prodLines[4] + } + + // k >= 2 + for k := 2; k < n; k++ { + // qProj1[k] ← 2qProj1[k] and l0 the tangent ℓ passing 2qProj1[k] + qProj1[k].doubleStep(&l0) + // line evaluation at Q[k] + l0.r1.Mul(&l0.r1, &p[k].X) + l0.r2.Mul(&l0.r2, &p[k].Y) + // ℓ × res + result.MulBy014(&l0.r0, &l0.r1, &l0.r2) } for i := 187; i >= 1; i-- {