github.com/consensys/gnark-crypto@v0.14.0/ecc/bw6-633/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 bw6633 16 17 import ( 18 "errors" 19 20 "github.com/consensys/gnark-crypto/ecc/bw6-633/fp" 21 "github.com/consensys/gnark-crypto/ecc/bw6-633/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^5-x^4-x) (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 // Hard part (up to permutation) 89 // (x₀^5-x₀^4-x₀)(p²-p+1)/r 90 // Algorithm 4.5 from https://yelhousni.github.io/phd.pdf 91 var a, b, c, d, e, f, g, h, i, t, mp GT 92 mp.Frobenius(&result) 93 a.ExptMinus1Squared(&mp) 94 a.ExptSquarePlus1(&a) 95 a.Mul(&result, &a) 96 t.Conjugate(&mp) 97 b.ExptPlus1(&a). 98 Mul(&b, &t) 99 t.CyclotomicSquare(&a). 100 Mul(&t, &a) 101 a.Conjugate(&t) 102 c.ExptMinus1Div3(&b) 103 d.ExptMinus1(&c) 104 d.ExptSquarePlus1(&d) 105 e.ExptMinus1Squared(&d) 106 e.ExptSquarePlus1(&e) 107 e.Mul(&e, &d) 108 f.ExptPlus1(&e). 109 Mul(&f, &c). 110 Conjugate(&f). 111 Mul(&f, &d) 112 g.Mul(&f, &d). 113 Conjugate(&g) 114 h.ExptPlus1(&g). 115 Mul(&h, &c). 116 Mul(&h, &b) 117 // ht = −7, hy = −1 118 // c1 = (ht-hy)/2 = -3 119 i.Expc1(&f). 120 Mul(&i, &e) 121 // c2 = (ht^2+3*hy^2)/4 = 13 122 t.CyclotomicSquare(&i). 123 Mul(&t, &i). 124 Mul(&t, &b) 125 i.Expc2(&h). 126 Mul(&i, &t) 127 result.Mul(&a, &i) 128 129 return result 130 } 131 132 // MillerLoop computes the multi-Miller loop 133 // computes the multi-Miller loop ∏ᵢ MillerLoop(Pᵢ, Qᵢ) 134 // Alg.2 in https://eprint.iacr.org/2021/1359.qdf 135 func MillerLoop(P []G1Affine, Q []G2Affine) (GT, error) { 136 // check input size match 137 n := len(P) 138 if n == 0 || n != len(Q) { 139 return GT{}, errors.New("invalid inputs sizes") 140 } 141 142 // filter infinity points 143 p := make([]G1Affine, 0, n) 144 q0 := make([]G2Affine, 0, n) 145 146 for k := 0; k < n; k++ { 147 if P[k].IsInfinity() || Q[k].IsInfinity() { 148 continue 149 } 150 p = append(p, P[k]) 151 q0 = append(q0, Q[k]) 152 } 153 154 n = len(p) 155 156 // precomputations 157 qProj0 := make([]g2Proj, n) 158 q1 := make([]G2Affine, n) 159 q1Neg := make([]G2Affine, n) 160 q0Neg := make([]G2Affine, n) 161 for k := 0; k < n; k++ { 162 q1[k].Y.Neg(&q0[k].Y) 163 q0Neg[k].X.Set(&q0[k].X) 164 q0Neg[k].Y.Set(&q1[k].Y) 165 q1[k].X.Mul(&q0[k].X, &thirdRootOneG2) 166 qProj0[k].FromAffine(&q0[k]) 167 q1Neg[k].Neg(&q1[k]) 168 } 169 170 // f_{a0+λ*a1,Q}(P) 171 var result GT 172 result.SetOne() 173 var l, l0 lineEvaluation 174 var prodLines [5]fp.Element 175 176 if n >= 1 { 177 // i = 157, 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 // qProj0[0] ← 2qProj0[0] and l0 the tangent ℓ passing 2qProj0[0] 183 qProj0[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 // qProj0[1] ← 2qProj0[1] and l0 the tangent ℓ passing 2qProj0[1] 193 qProj0[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 // qProj0[k] ← 2qProj0[k] and l0 the tangent ℓ passing 2qProj0[k] 208 qProj0[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 := len(LoopCounter) - 3; i >= 1; i-- { 217 // (∏ᵢfᵢ)² 218 // mutualize the square among n Miller loops 219 result.Square(&result) 220 221 j := LoopCounter[i]*3 + LoopCounter1[i] 222 223 for k := 0; k < n; k++ { 224 // qProj0[1] ← 2pProj0[1] and l0 the tangent ℓ qassing 2pProj0[1] 225 qProj0[k].doubleStep(&l0) 226 // line evaluation at Q[k] 227 l0.r1.Mul(&l0.r1, &p[k].X) 228 l0.r2.Mul(&l0.r2, &p[k].Y) 229 230 switch j { 231 // cases -4, -2, 2, 4 do not occur given the static LoopCounters 232 case -3: 233 // qProj0[k] ← qProj0[k]-q1[k] and 234 // l the line ℓ qassing qProj0[k] and -q1[k] 235 qProj0[k].addMixedStep(&l, &q1Neg[k]) 236 // line evaluation at Q[k] 237 l.r1.Mul(&l.r1, &p[k].X) 238 l.r2.Mul(&l.r2, &p[k].Y) 239 // ℓ × ℓ 240 prodLines = fptower.Mul014By014(&l.r0, &l.r1, &l.r2, &l0.r0, &l0.r1, &l0.r2) 241 // (ℓ × ℓ) × res 242 result.MulBy01245(&prodLines) 243 case -1: 244 // qProj0[k] ← qProj0[k]-q0[k] and 245 // l the line ℓ qassing qProj0[k] and -q0[k] 246 qProj0[k].addMixedStep(&l, &q0Neg[k]) 247 // line evaluation at Q[k] 248 l.r1.Mul(&l.r1, &p[k].X) 249 l.r2.Mul(&l.r2, &p[k].Y) 250 // ℓ × ℓ 251 prodLines = fptower.Mul014By014(&l.r0, &l.r1, &l.r2, &l0.r0, &l0.r1, &l0.r2) 252 // (ℓ × ℓ) × res 253 result.MulBy01245(&prodLines) 254 case 0: 255 // ℓ × res 256 result.MulBy014(&l0.r0, &l0.r1, &l0.r2) 257 case 1: 258 // qProj0[k] ← qProj0[k]+q0[k] and 259 // l the line ℓ qassing qProj0[k] and q0[k] 260 qProj0[k].addMixedStep(&l, &q0[k]) 261 // line evaluation at Q[k] 262 l.r1.Mul(&l.r1, &p[k].X) 263 l.r2.Mul(&l.r2, &p[k].Y) 264 // ℓ × ℓ 265 prodLines = fptower.Mul014By014(&l.r0, &l.r1, &l.r2, &l0.r0, &l0.r1, &l0.r2) 266 // (ℓ × ℓ) × res 267 result.MulBy01245(&prodLines) 268 case 3: 269 // qProj0[k] ← qProj0[k]+q1[k] and 270 // l the line ℓ qassing qProj0[k] and q1[k] 271 qProj0[k].addMixedStep(&l, &q1[k]) 272 // line evaluation at Q[k] 273 l.r1.Mul(&l.r1, &p[k].X) 274 l.r2.Mul(&l.r2, &p[k].Y) 275 // (ℓ × ℓ) × res 276 prodLines = fptower.Mul014By014(&l.r0, &l.r1, &l.r2, &l0.r0, &l0.r1, &l0.r2) 277 // (ℓ × ℓ) × res 278 result.MulBy01245(&prodLines) 279 default: 280 return GT{}, errors.New("invalid LoopCounter") 281 } 282 } 283 } 284 285 // i = 0, j = 1 286 result.Square(&result) 287 for k := 0; k < n; k++ { 288 // qProj0[1] ← 2pProj0[1] and l0 the tangent ℓ qassing 2pProj0[1] 289 qProj0[k].doubleStep(&l0) 290 // line evaluation at Q[k] 291 l0.r1.Mul(&l0.r1, &p[k].X) 292 l0.r2.Mul(&l0.r2, &p[k].Y) 293 // qProj0[k] ← qProj0[k]+q0[k] and 294 // l the line ℓ qassing qProj0[k] and q0[k] 295 qProj0[k].lineCompute(&l, &q0[k]) 296 // line evaluation at Q[k] 297 l.r1.Mul(&l.r1, &p[k].X) 298 l.r2.Mul(&l.r2, &p[k].Y) 299 // ℓ × ℓ 300 prodLines = fptower.Mul014By014(&l.r0, &l.r1, &l.r2, &l0.r0, &l0.r1, &l0.r2) 301 // (ℓ × ℓ) × res 302 result.MulBy01245(&prodLines) 303 } 304 305 // negative x₀ 306 result.Conjugate(&result) 307 308 return result, nil 309 } 310 311 // doubleStep doubles a point in Homogenous projective coordinates, and evaluates the line in Miller loop 312 // https://eprint.iacr.org/2013/722.pdf (Section 4.3) 313 func (p *g2Proj) doubleStep(evaluations *lineEvaluation) { 314 315 // get some Element from our pool 316 var t1, A, B, C, D, E, EE, F, G, H, I, J, K fp.Element 317 A.Mul(&p.x, &p.y) 318 A.Halve() 319 B.Square(&p.y) 320 C.Square(&p.z) 321 D.Double(&C). 322 Add(&D, &C) 323 E.Double(&D).Double(&E).Double(&E) 324 F.Double(&E). 325 Add(&F, &E) 326 G.Add(&B, &F) 327 G.Halve() 328 H.Add(&p.y, &p.z). 329 Square(&H) 330 t1.Add(&B, &C) 331 H.Sub(&H, &t1) 332 I.Sub(&E, &B) 333 J.Square(&p.x) 334 EE.Square(&E) 335 K.Double(&EE). 336 Add(&K, &EE) 337 338 // X, Y, Z 339 p.x.Sub(&B, &F). 340 Mul(&p.x, &A) 341 p.y.Square(&G). 342 Sub(&p.y, &K) 343 p.z.Mul(&B, &H) 344 345 // Line evaluation 346 evaluations.r0.Set(&I) 347 evaluations.r1.Double(&J). 348 Add(&evaluations.r1, &J) 349 evaluations.r2.Neg(&H) 350 } 351 352 // addMixedStep point addition in Mixed Homogenous projective and Affine coordinates 353 // https://eprint.iacr.org/2013/722.pdf (Section 4.3) 354 func (p *g2Proj) addMixedStep(evaluations *lineEvaluation, a *G2Affine) { 355 356 // get some Element from our pool 357 var Y2Z1, X2Z1, O, L, C, D, E, F, G, H, t0, t1, t2, J fp.Element 358 Y2Z1.Mul(&a.Y, &p.z) 359 O.Sub(&p.y, &Y2Z1) 360 X2Z1.Mul(&a.X, &p.z) 361 L.Sub(&p.x, &X2Z1) 362 C.Square(&O) 363 D.Square(&L) 364 E.Mul(&L, &D) 365 F.Mul(&p.z, &C) 366 G.Mul(&p.x, &D) 367 t0.Double(&G) 368 H.Add(&E, &F). 369 Sub(&H, &t0) 370 t1.Mul(&p.y, &E) 371 372 // X, Y, Z 373 p.x.Mul(&L, &H) 374 p.y.Sub(&G, &H). 375 Mul(&p.y, &O). 376 Sub(&p.y, &t1) 377 p.z.Mul(&E, &p.z) 378 379 t2.Mul(&L, &a.Y) 380 J.Mul(&a.X, &O). 381 Sub(&J, &t2) 382 383 // Line evaluation 384 evaluations.r0.Set(&J) 385 evaluations.r1.Neg(&O) 386 evaluations.r2.Set(&L) 387 } 388 389 // lineCompute computes the line through p in Homogenous projective coordinates 390 // and a in affine coordinates. It does not compute the resulting point p+a. 391 func (p *g2Proj) lineCompute(evaluations *lineEvaluation, a *G2Affine) { 392 393 // get some Element from our pool 394 var Y2Z1, X2Z1, O, L, t2, J fp.Element 395 Y2Z1.Mul(&a.Y, &p.z) 396 O.Sub(&p.y, &Y2Z1) 397 X2Z1.Mul(&a.X, &p.z) 398 L.Sub(&p.x, &X2Z1) 399 t2.Mul(&L, &a.Y) 400 J.Mul(&a.X, &O). 401 Sub(&J, &t2) 402 403 // Line evaluation 404 evaluations.r0.Set(&J) 405 evaluations.r1.Neg(&O) 406 evaluations.r2.Set(&L) 407 } 408 409 // ---------------------- 410 // Fixed-argument pairing 411 // ---------------------- 412 413 type LineEvaluationAff struct { 414 R0 fp.Element 415 R1 fp.Element 416 } 417 418 // PairFixedQ calculates the reduced pairing for a set of points 419 // ∏ᵢ e(Pᵢ, Qᵢ) where Q are fixed points in G2. 420 // 421 // This function doesn't check that the inputs are in the correct subgroup. See IsInSubGroup. 422 func PairFixedQ(P []G1Affine, lines [][2][len(LoopCounter) - 1]LineEvaluationAff) (GT, error) { 423 f, err := MillerLoopFixedQ(P, lines) 424 if err != nil { 425 return GT{}, err 426 } 427 return FinalExponentiation(&f), nil 428 } 429 430 // PairingCheckFixedQ calculates the reduced pairing for a set of points and returns True if the result is One 431 // ∏ᵢ e(Pᵢ, Qᵢ) =? 1 where Q are fixed points in G2. 432 // 433 // This function doesn't check that the inputs are in the correct subgroup. See IsInSubGroup. 434 func PairingCheckFixedQ(P []G1Affine, lines [][2][len(LoopCounter) - 1]LineEvaluationAff) (bool, error) { 435 f, err := PairFixedQ(P, lines) 436 if err != nil { 437 return false, err 438 } 439 var one GT 440 one.SetOne() 441 return f.Equal(&one), nil 442 } 443 444 // PrecomputeLines precomputes the lines for the fixed-argument Miller loop 445 func PrecomputeLines(Q G2Affine) (PrecomputedLines [2][len(LoopCounter) - 1]LineEvaluationAff) { 446 447 // precomputations 448 var accQ, imQ, imQneg, negQ G2Affine 449 imQ.Y.Neg(&Q.Y) 450 negQ.X.Set(&Q.X) 451 negQ.Y.Set(&imQ.Y) 452 imQ.X.Mul(&Q.X, &thirdRootOneG2) 453 accQ.Set(&Q) 454 imQneg.Neg(&imQ) 455 456 for i := len(LoopCounter) - 2; i >= 0; i-- { 457 458 accQ.doubleStep(&PrecomputedLines[0][i]) 459 460 j := LoopCounter[i]*3 + LoopCounter1[i] 461 switch j { 462 // cases -4, -2, 2, 4 do not occur given the static LoopCounters 463 case -3: 464 accQ.addStep(&PrecomputedLines[1][i], &imQneg) 465 case -1: 466 accQ.addStep(&PrecomputedLines[1][i], &negQ) 467 case 0: 468 continue 469 case 1: 470 accQ.addStep(&PrecomputedLines[1][i], &Q) 471 case 3: 472 accQ.addStep(&PrecomputedLines[1][i], &imQ) 473 default: 474 return [2][len(LoopCounter) - 1]LineEvaluationAff{} 475 } 476 } 477 478 return PrecomputedLines 479 } 480 481 // MillerLoopFixedQ computes the multi-Miller loop as in MillerLoop 482 // but Qᵢ are fixed points in G2 known in advance. 483 func MillerLoopFixedQ(P []G1Affine, lines [][2][len(LoopCounter) - 1]LineEvaluationAff) (GT, error) { 484 // check input size match 485 n := len(P) 486 if n == 0 || n != len(lines) { 487 return GT{}, errors.New("invalid inputs sizes") 488 } 489 490 // no need to filter infinity points: 491 // 1. if Pᵢ=(0,0) then -x/y=1/y=0 by gnark-crypto convention and so 492 // lines R0 and R1 are 0. It happens that result will stay, through 493 // the Miller loop, in 𝔽p⁶ because MulBy01(0,0,1), 494 // Mul01By01(0,0,1,0,0,1) and MulBy01245 set result.C0 to 0. At the 495 // end result will be in a proper subgroup of Fp¹² so it be reduced to 496 // 1 in FinalExponentiation. 497 // 498 // and/or 499 // 500 // 2. if Qᵢ=(0,0) then PrecomputeLines(Qᵢ) will return lines R0 and R1 501 // that are 0 because of gnark-convention (*/0==0) in doubleStep and 502 // addStep. Similarly to Pᵢ=(0,0) it happens that result be 1 503 // after the FinalExponentiation. 504 505 // precomputations 506 yInv := make([]fp.Element, n) 507 xNegOverY := make([]fp.Element, n) 508 for k := 0; k < n; k++ { 509 yInv[k].Set(&P[k].Y) 510 } 511 yInv = fp.BatchInvert(yInv) 512 for k := 0; k < n; k++ { 513 xNegOverY[k].Mul(&P[k].X, &yInv[k]). 514 Neg(&xNegOverY[k]) 515 } 516 517 // f_{a0+λ*a1,Q}(P) 518 var result GT 519 result.SetOne() 520 var prodLines [5]fp.Element 521 522 for i := len(LoopCounter) - 2; i >= 0; i-- { 523 result.Square(&result) 524 525 j := LoopCounter[i]*3 + LoopCounter1[i] 526 for k := 0; k < n; k++ { 527 lines[k][0][i].R1. 528 Mul( 529 &lines[k][0][i].R1, 530 &yInv[k], 531 ) 532 lines[k][0][i].R0. 533 Mul(&lines[k][0][i].R0, 534 &xNegOverY[k], 535 ) 536 if j == 0 { 537 result.MulBy01( 538 &lines[k][0][i].R1, 539 &lines[k][0][i].R0, 540 ) 541 542 } else { 543 lines[k][1][i].R1. 544 Mul( 545 &lines[k][1][i].R1, 546 &yInv[k], 547 ) 548 lines[k][1][i].R0. 549 Mul( 550 &lines[k][1][i].R0, 551 &xNegOverY[k], 552 ) 553 prodLines = fptower.Mul01By01( 554 &lines[k][0][i].R1, &lines[k][0][i].R0, 555 &lines[k][1][i].R1, &lines[k][1][i].R0, 556 ) 557 result.MulBy01245(&prodLines) 558 } 559 } 560 } 561 562 // negative x₀ 563 result.Conjugate(&result) 564 565 return result, nil 566 567 } 568 569 func (p *G2Affine) doubleStep(evaluations *LineEvaluationAff) { 570 571 var n, d, λ, xr, yr fp.Element 572 // λ = 3x²/2y 573 n.Square(&p.X) 574 λ.Double(&n). 575 Add(&λ, &n) 576 d.Double(&p.Y) 577 λ.Div(&λ, &d) 578 579 // xr = λ²-2x 580 xr.Square(&λ). 581 Sub(&xr, &p.X). 582 Sub(&xr, &p.X) 583 584 // yr = λ(x-xr)-y 585 yr.Sub(&p.X, &xr). 586 Mul(&yr, &λ). 587 Sub(&yr, &p.Y) 588 589 evaluations.R0.Set(&λ) 590 evaluations.R1.Mul(&λ, &p.X). 591 Sub(&evaluations.R1, &p.Y) 592 593 p.X.Set(&xr) 594 p.Y.Set(&yr) 595 } 596 597 func (p *G2Affine) addStep(evaluations *LineEvaluationAff, a *G2Affine) { 598 var n, d, λ, λλ, xr, yr fp.Element 599 600 // compute λ = (y2-y1)/(x2-x1) 601 n.Sub(&a.Y, &p.Y) 602 d.Sub(&a.X, &p.X) 603 λ.Div(&n, &d) 604 605 // xr = λ²-x1-x2 606 λλ.Square(&λ) 607 n.Add(&p.X, &a.X) 608 xr.Sub(&λλ, &n) 609 610 // yr = λ(x1-xr) - y1 611 yr.Sub(&p.X, &xr). 612 Mul(&yr, &λ). 613 Sub(&yr, &p.Y) 614 615 evaluations.R0.Set(&λ) 616 evaluations.R1.Mul(&λ, &p.X). 617 Sub(&evaluations.R1, &p.Y) 618 619 p.X.Set(&xr) 620 p.Y.Set(&yr) 621 }