github.com/consensys/gnark-crypto@v0.14.0/ecc/bw6-756/pairing.go (about) 1 // Copyright 2020 ConsenSys AG 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package bw6756 16 17 import ( 18 "errors" 19 20 "github.com/consensys/gnark-crypto/ecc/bw6-756/fp" 21 "github.com/consensys/gnark-crypto/ecc/bw6-756/internal/fptower" 22 ) 23 24 // GT target group of the pairing 25 type GT = fptower.E6 26 27 type lineEvaluation struct { 28 r0 fp.Element 29 r1 fp.Element 30 r2 fp.Element 31 } 32 33 // Pair calculates the reduced pairing for a set of points 34 // ∏ᵢ e(Pᵢ, Qᵢ). 35 // 36 // This function doesn't check that the inputs are in the correct subgroup. See IsInSubGroup. 37 func Pair(P []G1Affine, Q []G2Affine) (GT, error) { 38 f, err := MillerLoop(P, Q) 39 if err != nil { 40 return GT{}, err 41 } 42 return FinalExponentiation(&f), nil 43 } 44 45 // PairingCheck calculates the reduced pairing for a set of points and returns True if the result is One 46 // ∏ᵢ e(Pᵢ, Qᵢ) =? 1 47 // 48 // This function doesn't check that the inputs are in the correct subgroup. See IsInSubGroup. 49 func PairingCheck(P []G1Affine, Q []G2Affine) (bool, error) { 50 f, err := Pair(P, Q) 51 if err != nil { 52 return false, err 53 } 54 var one GT 55 one.SetOne() 56 return f.Equal(&one), nil 57 } 58 59 // FinalExponentiation computes the exponentiation (∏ᵢ zᵢ)ᵈ 60 // where d = (p^6-1)/r = (p^6-1)/Φ_6(p) ⋅ Φ_6(p)/r = (p^3-1)(p+1)(p^2 - p +1)/r 61 // we use instead d=s ⋅ (p^3-1)(p+1)(p^2 - p +1)/r 62 // where s is the cofactor (x_0+1) (El Housni and Guillevic) 63 func FinalExponentiation(z *GT, _z ...*GT) GT { 64 65 var result GT 66 result.Set(z) 67 68 for _, e := range _z { 69 result.Mul(&result, e) 70 } 71 72 var buf GT 73 74 // Easy part 75 // (p^3-1)(p+1) 76 buf.Conjugate(&result) 77 result.Inverse(&result) 78 buf.Mul(&buf, &result) 79 result.Frobenius(&buf). 80 Mul(&result, &buf) 81 82 var one GT 83 one.SetOne() 84 if result.Equal(&one) { 85 return result 86 } 87 88 // 2. Hard part (up to permutation) 89 // (x₀+1)(p²-p+1)/r 90 // Algorithm 4.4 from https://yelhousni.github.io/phd.pdf 91 var a, b, c, d, e, f, g, h, i, j, k, t GT 92 a.ExptMinus1Square(&result) 93 t.Frobenius(&result) 94 a.Mul(&a, &t) 95 b.ExptPlus1(&a) 96 t.Conjugate(&result) 97 b.Mul(&b, &t) 98 t.CyclotomicSquare(&a) 99 a.Mul(&a, &t) 100 c.ExptMinus1Div3(&b) 101 d.ExptMinus1(&c) 102 e.ExptMinus1Square(&d) 103 e.Mul(&e, &d) 104 d.Conjugate(&d) 105 f.Mul(&d, &b) 106 g.ExptPlus1(&e) 107 g.Mul(&g, &f) 108 h.Mul(&g, &c) 109 i.Mul(&g, &d) 110 i.ExptPlus1(&i) 111 t.Conjugate(&f) 112 i.Mul(&i, &t) 113 // ht, hy = -1, -1 114 // c1 = (ht+hy)/2 = -1 115 j.Conjugate(&h) 116 j.Mul(&j, &e) 117 k.CyclotomicSquare(&j) 118 k.Mul(&k, &j) 119 k.Mul(&k, &b) 120 // c2 = (ht**2+3*hy**2)/4 = 1 121 k.Mul(&k, &i) 122 result.Mul(&a, &k) 123 124 return result 125 } 126 127 // MillerLoop computes the multi-Miller loop 128 // ∏ᵢ MillerLoop(Pᵢ, Qᵢ) = 129 // ∏ᵢ { fᵢ_{x₀+1+λ(x₀³-x₀²-x₀),Qᵢ}(Pᵢ) } 130 // 131 // Alg.2 in https://eprint.iacr.org/2021/1359.pdf 132 // Eq. (6') in https://hackmd.io/@gnark/BW6-761-changes 133 func MillerLoop(P []G1Affine, Q []G2Affine) (GT, error) { 134 // check input size match 135 n := len(P) 136 if n == 0 || n != len(Q) { 137 return GT{}, errors.New("invalid inputs sizes") 138 } 139 140 // filter infinity points 141 p := make([]G1Affine, 0, n) 142 q0 := make([]G2Affine, 0, n) 143 144 for k := 0; k < n; k++ { 145 if P[k].IsInfinity() || Q[k].IsInfinity() { 146 continue 147 } 148 p = append(p, P[k]) 149 q0 = append(q0, Q[k]) 150 } 151 152 n = len(p) 153 154 // precomputations 155 qProj1 := make([]g2Proj, n) 156 q1 := make([]G2Affine, n) 157 q1Neg := make([]G2Affine, n) 158 q0Neg := make([]G2Affine, n) 159 for k := 0; k < n; k++ { 160 q1[k].Y.Neg(&q0[k].Y) 161 q0Neg[k].X.Set(&q0[k].X) 162 q0Neg[k].Y.Set(&q1[k].Y) 163 q1[k].X.Mul(&q0[k].X, &thirdRootOneG1) 164 qProj1[k].FromAffine(&q1[k]) 165 q1Neg[k].Neg(&q1[k]) 166 } 167 168 // f_{a0+λ*a1,Q}(P) 169 var result GT 170 result.SetOne() 171 var l, l0 lineEvaluation 172 var prodLines [5]fp.Element 173 174 var j int8 175 176 if n >= 1 { 177 // i = 189, separately to avoid an E12 Square 178 // (Square(res) = 1² = 1) 179 // j = 0 180 // k = 0, separately to avoid MulBy014 (res × ℓ) 181 // (assign line to res) 182 // qProj1[0] ← 2qProj1[0] and l0 the tangent ℓ passing 2qProj1[0] 183 qProj1[0].doubleStep(&l0) 184 // line evaluation at Q[0] (assign) 185 result.B0.A0.Set(&l0.r0) 186 result.B0.A1.Mul(&l0.r1, &p[0].X) 187 result.B1.A1.Mul(&l0.r2, &p[0].Y) 188 } 189 190 // k = 1 191 if n >= 2 { 192 // qProj1[1] ← 2qProj1[1] and l0 the tangent ℓ passing 2qProj1[1] 193 qProj1[1].doubleStep(&l0) 194 // line evaluation at Q[1] 195 l0.r1.Mul(&l0.r1, &p[1].X) 196 l0.r2.Mul(&l0.r2, &p[1].Y) 197 prodLines = fptower.Mul014By014(&l0.r0, &l0.r1, &l0.r2, &result.B0.A0, &result.B0.A1, &result.B1.A1) 198 result.B0.A0 = prodLines[0] 199 result.B0.A1 = prodLines[1] 200 result.B0.A2 = prodLines[2] 201 result.B1.A1 = prodLines[3] 202 result.B1.A2 = prodLines[4] 203 } 204 205 // k >= 2 206 for k := 2; k < n; k++ { 207 // qProj1[k] ← 2qProj1[k] and l0 the tangent ℓ passing 2qProj1[k] 208 qProj1[k].doubleStep(&l0) 209 // line evaluation at Q[k] 210 l0.r1.Mul(&l0.r1, &p[k].X) 211 l0.r2.Mul(&l0.r2, &p[k].Y) 212 // ℓ × res 213 result.MulBy014(&l0.r0, &l0.r1, &l0.r2) 214 } 215 216 for i := 188; i >= 1; i-- { 217 result.Square(&result) 218 219 j = LoopCounter1[i]*3 + LoopCounter[i] 220 221 for k := 0; k < n; k++ { 222 qProj1[k].doubleStep(&l0) 223 l0.r1.Mul(&l0.r1, &p[k].X) 224 l0.r2.Mul(&l0.r2, &p[k].Y) 225 226 switch j { 227 // cases -4, -2, 2, 4 do not occur, given the static LoopCounters 228 case -3: 229 qProj1[k].addMixedStep(&l, &q1Neg[k]) 230 l.r1.Mul(&l.r1, &p[k].X) 231 l.r2.Mul(&l.r2, &p[k].Y) 232 prodLines = fptower.Mul014By014(&l0.r0, &l0.r1, &l0.r2, &l.r0, &l.r1, &l.r2) 233 result.MulBy01245(&prodLines) 234 case -1: 235 qProj1[k].addMixedStep(&l, &q0Neg[k]) 236 l.r1.Mul(&l.r1, &p[k].X) 237 l.r2.Mul(&l.r2, &p[k].Y) 238 prodLines = fptower.Mul014By014(&l0.r0, &l0.r1, &l0.r2, &l.r0, &l.r1, &l.r2) 239 result.MulBy01245(&prodLines) 240 case 0: 241 result.MulBy014(&l0.r0, &l0.r1, &l0.r2) 242 case 1: 243 qProj1[k].addMixedStep(&l, &q0[k]) 244 l.r1.Mul(&l.r1, &p[k].X) 245 l.r2.Mul(&l.r2, &p[k].Y) 246 prodLines = fptower.Mul014By014(&l0.r0, &l0.r1, &l0.r2, &l.r0, &l.r1, &l.r2) 247 result.MulBy01245(&prodLines) 248 case 3: 249 qProj1[k].addMixedStep(&l, &q1[k]) 250 l.r1.Mul(&l.r1, &p[k].X) 251 l.r2.Mul(&l.r2, &p[k].Y) 252 prodLines = fptower.Mul014By014(&l0.r0, &l0.r1, &l0.r2, &l.r0, &l.r1, &l.r2) 253 result.MulBy01245(&prodLines) 254 default: 255 return GT{}, errors.New("invalid LoopCounter") 256 } 257 } 258 } 259 260 // i = 0, separately to avoid a point addition 261 // j = -3 262 result.Square(&result) 263 for k := 0; k < n; k++ { 264 qProj1[k].doubleStep(&l0) 265 l0.r1.Mul(&l0.r1, &p[k].X) 266 l0.r2.Mul(&l0.r2, &p[k].Y) 267 qProj1[k].lineCompute(&l, &q1Neg[k]) 268 l.r1.Mul(&l.r1, &p[k].X) 269 l.r2.Mul(&l.r2, &p[k].Y) 270 prodLines = fptower.Mul014By014(&l0.r0, &l0.r1, &l0.r2, &l.r0, &l.r1, &l.r2) 271 result.MulBy01245(&prodLines) 272 } 273 274 return result, nil 275 276 } 277 278 // doubleStep doubles a point in Homogenous projective coordinates, and evaluates the line in Miller loop 279 // https://eprint.iacr.org/2013/722.pdf (Section 4.3) 280 func (p *g2Proj) doubleStep(evaluations *lineEvaluation) { 281 282 // get some Element from our pool 283 var t1, A, B, C, D, E, EE, F, G, H, I, J, K fp.Element 284 A.Mul(&p.x, &p.y) 285 A.Halve() 286 B.Square(&p.y) 287 C.Square(&p.z) 288 D.Double(&C). 289 Add(&D, &C) 290 E.Mul(&D, &bTwistCurveCoeff) 291 F.Double(&E). 292 Add(&F, &E) 293 G.Add(&B, &F) 294 G.Halve() 295 H.Add(&p.y, &p.z). 296 Square(&H) 297 t1.Add(&B, &C) 298 H.Sub(&H, &t1) 299 I.Sub(&E, &B) 300 J.Square(&p.x) 301 EE.Square(&E) 302 K.Double(&EE). 303 Add(&K, &EE) 304 305 // X, Y, Z 306 p.x.Sub(&B, &F). 307 Mul(&p.x, &A) 308 p.y.Square(&G). 309 Sub(&p.y, &K) 310 p.z.Mul(&B, &H) 311 312 // Line evaluation 313 evaluations.r0.Set(&I) 314 evaluations.r1.Double(&J). 315 Add(&evaluations.r1, &J) 316 evaluations.r2.Neg(&H) 317 } 318 319 // addMixedStep point addition in Mixed Homogenous projective and Affine coordinates 320 // https://eprint.iacr.org/2013/722.pdf (Section 4.3) 321 func (p *g2Proj) addMixedStep(evaluations *lineEvaluation, a *G2Affine) { 322 323 // get some Element from our pool 324 var Y2Z1, X2Z1, O, L, C, D, E, F, G, H, t0, t1, t2, J fp.Element 325 Y2Z1.Mul(&a.Y, &p.z) 326 O.Sub(&p.y, &Y2Z1) 327 X2Z1.Mul(&a.X, &p.z) 328 L.Sub(&p.x, &X2Z1) 329 C.Square(&O) 330 D.Square(&L) 331 E.Mul(&L, &D) 332 F.Mul(&p.z, &C) 333 G.Mul(&p.x, &D) 334 t0.Double(&G) 335 H.Add(&E, &F). 336 Sub(&H, &t0) 337 t1.Mul(&p.y, &E) 338 339 // X, Y, Z 340 p.x.Mul(&L, &H) 341 p.y.Sub(&G, &H). 342 Mul(&p.y, &O). 343 Sub(&p.y, &t1) 344 p.z.Mul(&E, &p.z) 345 346 t2.Mul(&L, &a.Y) 347 J.Mul(&a.X, &O). 348 Sub(&J, &t2) 349 350 // Line evaluation 351 evaluations.r0.Set(&J) 352 evaluations.r1.Neg(&O) 353 evaluations.r2.Set(&L) 354 } 355 356 // lineCompute computes the line through p in Homogenous projective coordinates 357 // and a in affine coordinates. It does not compute the resulting point p+a. 358 func (p *g2Proj) lineCompute(evaluations *lineEvaluation, a *G2Affine) { 359 360 // get some Element from our pool 361 var Y2Z1, X2Z1, O, L, t2, J fp.Element 362 Y2Z1.Mul(&a.Y, &p.z) 363 O.Sub(&p.y, &Y2Z1) 364 X2Z1.Mul(&a.X, &p.z) 365 L.Sub(&p.x, &X2Z1) 366 t2.Mul(&L, &a.Y) 367 J.Mul(&a.X, &O). 368 Sub(&J, &t2) 369 370 // Line evaluation 371 evaluations.r0.Set(&J) 372 evaluations.r1.Neg(&O) 373 evaluations.r2.Set(&L) 374 } 375 376 // ---------------------- 377 // Fixed-argument pairing 378 // ---------------------- 379 380 type LineEvaluationAff struct { 381 R0 fp.Element 382 R1 fp.Element 383 } 384 385 // PairFixedQ calculates the reduced pairing for a set of points 386 // ∏ᵢ e(Pᵢ, Qᵢ) where Q are fixed points in G2. 387 // 388 // This function doesn't check that the inputs are in the correct subgroup. See IsInSubGroup. 389 func PairFixedQ(P []G1Affine, lines [][2][len(LoopCounter) - 1]LineEvaluationAff) (GT, error) { 390 f, err := MillerLoopFixedQ(P, lines) 391 if err != nil { 392 return GT{}, err 393 } 394 return FinalExponentiation(&f), nil 395 } 396 397 // PairingCheckFixedQ calculates the reduced pairing for a set of points and returns True if the result is One 398 // ∏ᵢ e(Pᵢ, Qᵢ) =? 1 where Q are fixed points in G2. 399 // 400 // This function doesn't check that the inputs are in the correct subgroup. See IsInSubGroup. 401 func PairingCheckFixedQ(P []G1Affine, lines [][2][len(LoopCounter) - 1]LineEvaluationAff) (bool, error) { 402 f, err := PairFixedQ(P, lines) 403 if err != nil { 404 return false, err 405 } 406 var one GT 407 one.SetOne() 408 return f.Equal(&one), nil 409 } 410 411 // PrecomputeLines precomputes the lines for the fixed-argument Miller loop 412 func PrecomputeLines(Q G2Affine) (PrecomputedLines [2][len(LoopCounter) - 1]LineEvaluationAff) { 413 414 // precomputations 415 var accQ, imQ, imQneg, negQ G2Affine 416 imQ.Y.Neg(&Q.Y) 417 negQ.X.Set(&Q.X) 418 negQ.Y.Set(&imQ.Y) 419 imQ.X.Mul(&Q.X, &thirdRootOneG1) 420 accQ.Set(&imQ) 421 imQneg.Neg(&imQ) 422 423 for i := len(LoopCounter) - 2; i >= 0; i-- { 424 accQ.doubleStep(&PrecomputedLines[0][i]) 425 426 switch LoopCounter1[i]*3 + LoopCounter[i] { 427 // cases -4, -2, 2, 4 do not occur, given the static LoopCounters 428 case -3: 429 accQ.addStep(&PrecomputedLines[1][i], &imQneg) 430 case -1: 431 accQ.addStep(&PrecomputedLines[1][i], &negQ) 432 case 0: 433 continue 434 case 1: 435 accQ.addStep(&PrecomputedLines[1][i], &Q) 436 case 3: 437 accQ.addStep(&PrecomputedLines[1][i], &imQ) 438 default: 439 return [2][len(LoopCounter) - 1]LineEvaluationAff{} 440 } 441 } 442 443 return PrecomputedLines 444 } 445 446 // MillerLoopFixedQ computes the multi-Miller loop as in MillerLoop 447 // but Qᵢ are fixed points in G2 known in advance. 448 func MillerLoopFixedQ(P []G1Affine, lines [][2][len(LoopCounter) - 1]LineEvaluationAff) (GT, error) { 449 // check input size match 450 n := len(P) 451 if n == 0 || n != len(lines) { 452 return GT{}, errors.New("invalid inputs sizes") 453 } 454 455 // no need to filter infinity points: 456 // 1. if Pᵢ=(0,0) then -x/y=1/y=0 by gnark-crypto convention and so 457 // lines R0 and R1 are 0. It happens that result will stay, through 458 // the Miller loop, in 𝔽p⁶ because MulBy01(0,0,1), 459 // Mul01By01(0,0,1,0,0,1) and MulBy01245 set result.C0 to 0. At the 460 // end result will be in a proper subgroup of Fp¹² so it be reduced to 461 // 1 in FinalExponentiation. 462 // 463 // and/or 464 // 465 // 2. if Qᵢ=(0,0) then PrecomputeLines(Qᵢ) will return lines R0 and R1 466 // that are 0 because of gnark-convention (*/0==0) in doubleStep and 467 // addStep. Similarly to Pᵢ=(0,0) it happens that result be 1 468 // after the FinalExponentiation. 469 470 // precomputations 471 yInv := make([]fp.Element, n) 472 xNegOverY := make([]fp.Element, n) 473 for k := 0; k < n; k++ { 474 yInv[k].Set(&P[k].Y) 475 } 476 yInv = fp.BatchInvert(yInv) 477 for k := 0; k < n; k++ { 478 xNegOverY[k].Mul(&P[k].X, &yInv[k]). 479 Neg(&xNegOverY[k]) 480 } 481 482 // f_{a0+λ*a1,Q}(P) 483 var result GT 484 result.SetOne() 485 var prodLines [5]fp.Element 486 487 for i := len(LoopCounter) - 2; i >= 0; i-- { 488 result.Square(&result) 489 490 j := LoopCounter1[i]*3 + LoopCounter[i] 491 for k := 0; k < n; k++ { 492 lines[k][0][i].R1. 493 Mul( 494 &lines[k][0][i].R1, 495 &yInv[k], 496 ) 497 lines[k][0][i].R0. 498 Mul(&lines[k][0][i].R0, 499 &xNegOverY[k], 500 ) 501 if j == 0 { 502 result.MulBy01( 503 &lines[k][0][i].R1, 504 &lines[k][0][i].R0, 505 ) 506 507 } else { 508 lines[k][1][i].R1. 509 Mul( 510 &lines[k][1][i].R1, 511 &yInv[k], 512 ) 513 lines[k][1][i].R0. 514 Mul( 515 &lines[k][1][i].R0, 516 &xNegOverY[k], 517 ) 518 prodLines = fptower.Mul01By01( 519 &lines[k][0][i].R1, &lines[k][0][i].R0, 520 &lines[k][1][i].R1, &lines[k][1][i].R0, 521 ) 522 result.MulBy01245(&prodLines) 523 } 524 } 525 } 526 527 return result, nil 528 529 } 530 531 func (p *G2Affine) doubleStep(evaluations *LineEvaluationAff) { 532 533 var n, d, λ, xr, yr fp.Element 534 // λ = 3x²/2y 535 n.Square(&p.X) 536 λ.Double(&n). 537 Add(&λ, &n) 538 d.Double(&p.Y) 539 λ.Div(&λ, &d) 540 541 // xr = λ²-2x 542 xr.Square(&λ). 543 Sub(&xr, &p.X). 544 Sub(&xr, &p.X) 545 546 // yr = λ(x-xr)-y 547 yr.Sub(&p.X, &xr). 548 Mul(&yr, &λ). 549 Sub(&yr, &p.Y) 550 551 evaluations.R0.Set(&λ) 552 evaluations.R1.Mul(&λ, &p.X). 553 Sub(&evaluations.R1, &p.Y) 554 555 p.X.Set(&xr) 556 p.Y.Set(&yr) 557 } 558 559 func (p *G2Affine) addStep(evaluations *LineEvaluationAff, a *G2Affine) { 560 var n, d, λ, λλ, xr, yr fp.Element 561 562 // compute λ = (y2-y1)/(x2-x1) 563 n.Sub(&a.Y, &p.Y) 564 d.Sub(&a.X, &p.X) 565 λ.Div(&n, &d) 566 567 // xr = λ²-x1-x2 568 λλ.Square(&λ) 569 n.Add(&p.X, &a.X) 570 xr.Sub(&λλ, &n) 571 572 // yr = λ(x1-xr) - y1 573 yr.Sub(&p.X, &xr). 574 Mul(&yr, &λ). 575 Sub(&yr, &p.Y) 576 577 evaluations.R0.Set(&λ) 578 evaluations.R1.Mul(&λ, &p.X). 579 Sub(&evaluations.R1, &p.Y) 580 581 p.X.Set(&xr) 582 p.Y.Set(&yr) 583 }