github.com/consensys/gnark-crypto@v0.14.0/ecc/bls12-377/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 bls12377 16 17 import ( 18 "errors" 19 20 "github.com/consensys/gnark-crypto/ecc/bls12-377/fp" 21 "github.com/consensys/gnark-crypto/ecc/bls12-377/internal/fptower" 22 ) 23 24 // GT target group of the pairing 25 type GT = fptower.E12 26 27 type lineEvaluation struct { 28 r0 fptower.E2 29 r1 fptower.E2 30 r2 fptower.E2 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¹²-1)/r = (p¹²-1)/Φ₁₂(p) ⋅ Φ₁₂(p)/r = (p⁶-1)(p²+1)(p⁴ - p² +1)/r 61 // we use instead d=s ⋅ (p⁶-1)(p²+1)(p⁴ - p² +1)/r 62 // where s is the cofactor 3 (Hayashida et al.) 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 t [3]GT 73 74 // Easy part 75 // (p⁶-1)(p²+1) 76 t[0].Conjugate(&result) 77 result.Inverse(&result) 78 t[0].Mul(&t[0], &result) 79 result.FrobeniusSquare(&t[0]). 80 Mul(&result, &t[0]) 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 // Daiki Hayashida, Kenichiro Hayasaka and Tadanori Teruya 90 // https://eprint.iacr.org/2020/875.pdf 91 t[0].CyclotomicSquare(&result) 92 t[1].Expt(&result) 93 t[2].InverseUnitary(&result) 94 t[1].Mul(&t[1], &t[2]) 95 t[2].Expt(&t[1]) 96 t[1].InverseUnitary(&t[1]) 97 t[1].Mul(&t[1], &t[2]) 98 t[2].Expt(&t[1]) 99 t[1].Frobenius(&t[1]) 100 t[1].Mul(&t[1], &t[2]) 101 result.Mul(&result, &t[0]) 102 t[0].Expt(&t[1]) 103 t[2].Expt(&t[0]) 104 t[0].FrobeniusSquare(&t[1]) 105 t[1].InverseUnitary(&t[1]) 106 t[1].Mul(&t[1], &t[2]) 107 t[1].Mul(&t[1], &t[0]) 108 result.Mul(&result, &t[1]) 109 110 return result 111 } 112 113 // MillerLoop computes the multi-Miller loop 114 // ∏ᵢ MillerLoop(Pᵢ, Qᵢ) = ∏ᵢ { fᵢ_{x,Qᵢ}(Pᵢ) } 115 func MillerLoop(P []G1Affine, Q []G2Affine) (GT, error) { 116 // check input size match 117 n := len(P) 118 if n == 0 || n != len(Q) { 119 return GT{}, errors.New("invalid inputs sizes") 120 } 121 122 // filter infinity points 123 p := make([]G1Affine, 0, n) 124 q := make([]G2Affine, 0, n) 125 126 for k := 0; k < n; k++ { 127 if P[k].IsInfinity() || Q[k].IsInfinity() { 128 continue 129 } 130 p = append(p, P[k]) 131 q = append(q, Q[k]) 132 } 133 134 n = len(p) 135 136 // projective points for Q 137 qProj := make([]g2Proj, n) 138 for k := 0; k < n; k++ { 139 qProj[k].FromAffine(&q[k]) 140 } 141 142 var result GT 143 result.SetOne() 144 var l1, l2 lineEvaluation 145 var prodLines [5]E2 146 147 // Compute ∏ᵢ { fᵢ_{x₀,Q}(P) } 148 if n >= 1 { 149 // i = 62, separately to avoid an E12 Square 150 // (Square(res) = 1² = 1) 151 // LoopCounter[62] = 0 152 // k = 0, separately to avoid MulBy034 (res × ℓ) 153 // (assign line to res) 154 155 // qProj[0] ← 2qProj[0] and l1 the tangent ℓ passing 2qProj[0] 156 qProj[0].doubleStep(&l1) 157 // line evaluation at P[0] (assign) 158 result.C0.B0.MulByElement(&l1.r0, &p[0].Y) 159 result.C1.B0.MulByElement(&l1.r1, &p[0].X) 160 result.C1.B1.Set(&l1.r2) 161 } 162 163 if n >= 2 { 164 // k = 1, separately to avoid MulBy034 (res × ℓ) 165 // (res is also a line at this point, so we use Mul034By034 ℓ × ℓ) 166 167 // qProj[1] ← 2qProj[1] and l1 the tangent ℓ passing 2qProj[1] 168 qProj[1].doubleStep(&l1) 169 // line evaluation at P[1] 170 l1.r0.MulByElement(&l1.r0, &p[1].Y) 171 l1.r1.MulByElement(&l1.r1, &p[1].X) 172 // ℓ × res 173 prodLines = fptower.Mul034By034(&l1.r0, &l1.r1, &l1.r2, &result.C0.B0, &result.C1.B0, &result.C1.B1) 174 result.C0.B0 = prodLines[0] 175 result.C0.B1 = prodLines[1] 176 result.C0.B2 = prodLines[2] 177 result.C1.B0 = prodLines[3] 178 result.C1.B1 = prodLines[4] 179 } 180 181 // k >= 2 182 for k := 2; k < n; k++ { 183 // qProj[k] ← 2qProj[k] and l1 the tangent ℓ passing 2qProj[k] 184 qProj[k].doubleStep(&l1) 185 // line evaluation at P[k] 186 l1.r0.MulByElement(&l1.r0, &p[k].Y) 187 l1.r1.MulByElement(&l1.r1, &p[k].X) 188 // ℓ × res 189 result.MulBy034(&l1.r0, &l1.r1, &l1.r2) 190 } 191 192 // i <= 61 193 for i := len(LoopCounter) - 3; i >= 1; i-- { 194 // mutualize the square among n Miller loops 195 // (∏ᵢfᵢ)² 196 result.Square(&result) 197 198 for k := 0; k < n; k++ { 199 // qProj[k] ← 2qProj[k] and l1 the tangent ℓ passing 2qProj[k] 200 qProj[k].doubleStep(&l1) 201 // line evaluation at P[k] 202 l1.r0.MulByElement(&l1.r0, &p[k].Y) 203 l1.r1.MulByElement(&l1.r1, &p[k].X) 204 205 if LoopCounter[i] == 0 { 206 // ℓ × res 207 result.MulBy034(&l1.r0, &l1.r1, &l1.r2) 208 } else { 209 // qProj[k] ← qProj[k]+Q[k] and 210 // l2 the line ℓ passing qProj[k] and Q[k] 211 qProj[k].addMixedStep(&l2, &q[k]) 212 // line evaluation at P[k] 213 l2.r0.MulByElement(&l2.r0, &p[k].Y) 214 l2.r1.MulByElement(&l2.r1, &p[k].X) 215 // ℓ × ℓ 216 prodLines = fptower.Mul034By034(&l1.r0, &l1.r1, &l1.r2, &l2.r0, &l2.r1, &l2.r2) 217 // (ℓ × ℓ) × res 218 result.MulBy01234(&prodLines) 219 } 220 } 221 222 } 223 224 // i = 0, separately to avoid a point addition 225 // LoopCounter[0] = 1 226 result.Square(&result) 227 for k := 0; k < n; k++ { 228 // qProj[k] ← 2qProj[k] and l1 the tangent ℓ passing 2qProj[k] 229 qProj[k].doubleStep(&l1) 230 // line evaluation at P[k] 231 l1.r0.MulByElement(&l1.r0, &p[k].Y) 232 l1.r1.MulByElement(&l1.r1, &p[k].X) 233 234 // l2 the line passing qProj[k] and Q 235 qProj[k].lineCompute(&l2, &q[k]) 236 // line evaluation at P[k] 237 l2.r0.MulByElement(&l2.r0, &p[k].Y) 238 l2.r1.MulByElement(&l2.r1, &p[k].X) 239 // ℓ × ℓ 240 prodLines = fptower.Mul034By034(&l1.r0, &l1.r1, &l1.r2, &l2.r0, &l2.r1, &l2.r2) 241 // (ℓ × ℓ) × res 242 result.MulBy01234(&prodLines) 243 } 244 245 return result, nil 246 } 247 248 // doubleStep doubles a point in Homogenous projective coordinates, and evaluates the line in Miller loop 249 // https://eprint.iacr.org/2013/722.pdf (Section 4.3) 250 func (p *g2Proj) doubleStep(evaluations *lineEvaluation) { 251 252 // get some Element from our pool 253 var t1, A, B, C, D, E, EE, F, G, H, I, J, K fptower.E2 254 A.Mul(&p.x, &p.y) 255 A.Halve() 256 B.Square(&p.y) 257 C.Square(&p.z) 258 D.Double(&C). 259 Add(&D, &C) 260 E.MulBybTwistCurveCoeff(&D) 261 F.Double(&E). 262 Add(&F, &E) 263 G.Add(&B, &F) 264 G.Halve() 265 H.Add(&p.y, &p.z). 266 Square(&H) 267 t1.Add(&B, &C) 268 H.Sub(&H, &t1) 269 I.Sub(&E, &B) 270 J.Square(&p.x) 271 EE.Square(&E) 272 K.Double(&EE). 273 Add(&K, &EE) 274 275 // X, Y, Z 276 p.x.Sub(&B, &F). 277 Mul(&p.x, &A) 278 p.y.Square(&G). 279 Sub(&p.y, &K) 280 p.z.Mul(&B, &H) 281 282 // Line evaluation 283 evaluations.r0.Neg(&H) 284 evaluations.r1.Double(&J). 285 Add(&evaluations.r1, &J) 286 evaluations.r2.Set(&I) 287 } 288 289 // addMixedStep point addition in Mixed Homogenous projective and Affine coordinates 290 // https://eprint.iacr.org/2013/722.pdf (Section 4.3) 291 func (p *g2Proj) addMixedStep(evaluations *lineEvaluation, a *G2Affine) { 292 293 // get some Element from our pool 294 var Y2Z1, X2Z1, O, L, C, D, E, F, G, H, t0, t1, t2, J fptower.E2 295 Y2Z1.Mul(&a.Y, &p.z) 296 O.Sub(&p.y, &Y2Z1) 297 X2Z1.Mul(&a.X, &p.z) 298 L.Sub(&p.x, &X2Z1) 299 C.Square(&O) 300 D.Square(&L) 301 E.Mul(&L, &D) 302 F.Mul(&p.z, &C) 303 G.Mul(&p.x, &D) 304 t0.Double(&G) 305 H.Add(&E, &F). 306 Sub(&H, &t0) 307 t1.Mul(&p.y, &E) 308 309 // X, Y, Z 310 p.x.Mul(&L, &H) 311 p.y.Sub(&G, &H). 312 Mul(&p.y, &O). 313 Sub(&p.y, &t1) 314 p.z.Mul(&E, &p.z) 315 316 t2.Mul(&L, &a.Y) 317 J.Mul(&a.X, &O). 318 Sub(&J, &t2) 319 320 // Line evaluation 321 evaluations.r0.Set(&L) 322 evaluations.r1.Neg(&O) 323 evaluations.r2.Set(&J) 324 } 325 326 // lineCompute computes the line through p in Homogenous projective coordinates 327 // and a in affine coordinates. It does not compute the resulting point p+a. 328 func (p *g2Proj) lineCompute(evaluations *lineEvaluation, a *G2Affine) { 329 330 // get some Element from our pool 331 var Y2Z1, X2Z1, O, L, t2, J fptower.E2 332 Y2Z1.Mul(&a.Y, &p.z) 333 O.Sub(&p.y, &Y2Z1) 334 X2Z1.Mul(&a.X, &p.z) 335 L.Sub(&p.x, &X2Z1) 336 t2.Mul(&L, &a.Y) 337 J.Mul(&a.X, &O). 338 Sub(&J, &t2) 339 340 // Line evaluation 341 evaluations.r0.Set(&L) 342 evaluations.r1.Neg(&O) 343 evaluations.r2.Set(&J) 344 } 345 346 // ---------------------- 347 // Fixed-argument pairing 348 // ---------------------- 349 350 type LineEvaluationAff struct { 351 R0 fptower.E2 352 R1 fptower.E2 353 } 354 355 // PairFixedQ calculates the reduced pairing for a set of points 356 // ∏ᵢ e(Pᵢ, Qᵢ) where Q are fixed points in G2. 357 // 358 // This function doesn't check that the inputs are in the correct subgroup. See IsInSubGroup. 359 func PairFixedQ(P []G1Affine, lines [][2][len(LoopCounter) - 1]LineEvaluationAff) (GT, error) { 360 f, err := MillerLoopFixedQ(P, lines) 361 if err != nil { 362 return GT{}, err 363 } 364 return FinalExponentiation(&f), nil 365 } 366 367 // PairingCheckFixedQ calculates the reduced pairing for a set of points and returns True if the result is One 368 // ∏ᵢ e(Pᵢ, Qᵢ) =? 1 where Q are fixed points in G2. 369 // 370 // This function doesn't check that the inputs are in the correct subgroup. See IsInSubGroup. 371 func PairingCheckFixedQ(P []G1Affine, lines [][2][len(LoopCounter) - 1]LineEvaluationAff) (bool, error) { 372 f, err := PairFixedQ(P, lines) 373 if err != nil { 374 return false, err 375 } 376 var one GT 377 one.SetOne() 378 return f.Equal(&one), nil 379 } 380 381 // PrecomputeLines precomputes the lines for the fixed-argument Miller loop 382 func PrecomputeLines(Q G2Affine) (PrecomputedLines [2][len(LoopCounter) - 1]LineEvaluationAff) { 383 var accQ G2Affine 384 accQ.Set(&Q) 385 386 for i := len(LoopCounter) - 2; i >= 0; i-- { 387 if LoopCounter[i] == 0 { 388 accQ.doubleStep(&PrecomputedLines[0][i]) 389 } else { 390 accQ.doubleAndAddStep(&PrecomputedLines[0][i], &PrecomputedLines[1][i], &Q) 391 } 392 } 393 return PrecomputedLines 394 } 395 396 // MillerLoopFixedQ computes the multi-Miller loop as in MillerLoop 397 // but Qᵢ are fixed points in G2 known in advance. 398 func MillerLoopFixedQ(P []G1Affine, lines [][2][len(LoopCounter) - 1]LineEvaluationAff) (GT, error) { 399 n := len(P) 400 if n == 0 || n != len(lines) { 401 return GT{}, errors.New("invalid inputs sizes") 402 } 403 404 // no need to filter infinity points: 405 // 1. if Pᵢ=(0,0) then -x/y=1/y=0 by gnark-crypto convention and so 406 // lines R0 and R1 are 0. At the end it happens that result will stay 407 // 1 through the Miller loop because MulBy34(1,0,0)==1 408 // Mul34By34(1,0,0,1,0,0)==1 and MulBy01234(1,0,0,0,0)==1. 409 // 410 // 2. if Qᵢ=(0,0) then PrecomputeLines(Qᵢ) will return lines R0 and R1 411 // that are 0 because of gnark-convention (*/0==0) in doubleStep and 412 // addStep. Similarly to Pᵢ=(0,0) it happens that result stays 1 413 // throughout the MillerLoop. 414 415 // precomputations 416 yInv := make([]fp.Element, n) 417 xNegOverY := make([]fp.Element, n) 418 for k := 0; k < n; k++ { 419 yInv[k].Set(&P[k].Y) 420 } 421 yInv = fp.BatchInvert(yInv) 422 for k := 0; k < n; k++ { 423 xNegOverY[k].Mul(&P[k].X, &yInv[k]). 424 Neg(&xNegOverY[k]) 425 } 426 427 var result GT 428 result.SetOne() 429 var prodLines [5]E2 430 431 // Compute ∏ᵢ { fᵢ_{x₀,Q}(P) } 432 if n >= 1 { 433 // i = 62, separately to avoid an E12 Square 434 // (Square(res) = 1² = 1) 435 // LoopCounter[62] = 0 436 // k = 0, separately to avoid MulBy34 (res × ℓ) 437 // (assign line to res) 438 439 // line evaluation at P[0] (assign) 440 result.C1.B0.MulByElement(&lines[0][0][62].R0, &xNegOverY[0]) 441 result.C1.B1.MulByElement(&lines[0][0][62].R1, &yInv[0]) 442 // the coefficient which MulBy34 sets to 1 happens to be already 1 (result = 1) 443 } 444 445 if n >= 2 { 446 // k = 1, separately to avoid MulBy34 (res × ℓ) 447 // (res is also a line at this point, so we use Mul34By34 ℓ × ℓ) 448 // line evaluation at P[1] 449 lines[1][0][62].R0.MulByElement(&lines[1][0][62].R0, &xNegOverY[1]) 450 lines[1][0][62].R1.MulByElement(&lines[1][0][62].R1, &yInv[1]) 451 // ℓ × res 452 prodLines = fptower.Mul34By34(&lines[1][0][62].R0, &lines[1][0][62].R1, &result.C1.B0, &result.C1.B1) 453 result.C0.B0 = prodLines[0] 454 result.C0.B1 = prodLines[1] 455 result.C0.B2 = prodLines[2] 456 result.C1.B0 = prodLines[3] 457 result.C1.B1 = prodLines[4] 458 } 459 460 // k >= 2 461 for k := 2; k < n; k++ { 462 // line evaluation at P[k] 463 lines[k][0][62].R0.MulByElement(&lines[k][0][62].R0, &xNegOverY[k]) 464 lines[k][0][62].R1.MulByElement(&lines[k][0][62].R1, &yInv[k]) 465 // ℓ × res 466 result.MulBy34( 467 &lines[k][0][62].R0, 468 &lines[k][0][62].R1, 469 ) 470 } 471 472 for i := len(LoopCounter) - 3; i >= 0; i-- { 473 // mutualize the square among n Miller loops 474 // (∏ᵢfᵢ)² 475 result.Square(&result) 476 477 for k := 0; k < n; k++ { 478 // line evaluation at P[k] 479 lines[k][0][i].R0. 480 MulByElement( 481 &lines[k][0][i].R0, 482 &xNegOverY[k], 483 ) 484 lines[k][0][i].R1. 485 MulByElement( 486 &lines[k][0][i].R1, 487 &yInv[k], 488 ) 489 490 if LoopCounter[i] == 0 { 491 // ℓ × res 492 result.MulBy34( 493 &lines[k][0][i].R0, 494 &lines[k][0][i].R1, 495 ) 496 } else { 497 // line evaluation at P[k] 498 lines[k][1][i].R0. 499 MulByElement( 500 &lines[k][1][i].R0, 501 &xNegOverY[k], 502 ) 503 lines[k][1][i].R1. 504 MulByElement( 505 &lines[k][1][i].R1, 506 &yInv[k], 507 ) 508 // ℓ × ℓ 509 prodLines = fptower.Mul34By34( 510 &lines[k][0][i].R0, &lines[k][0][i].R1, 511 &lines[k][1][i].R0, &lines[k][1][i].R1, 512 ) 513 // (ℓ × ℓ) × res 514 result.MulBy01234(&prodLines) 515 } 516 } 517 } 518 519 return result, nil 520 } 521 522 func (p *G2Affine) doubleStep(evaluations *LineEvaluationAff) { 523 524 var n, d, λ, xr, yr fptower.E2 525 // λ = 3x²/2y 526 n.Square(&p.X) 527 λ.Double(&n). 528 Add(&λ, &n) 529 d.Double(&p.Y) 530 λ.Div(&λ, &d) 531 532 // xr = λ²-2x 533 xr.Square(&λ). 534 Sub(&xr, &p.X). 535 Sub(&xr, &p.X) 536 537 // yr = λ(x-xr)-y 538 yr.Sub(&p.X, &xr). 539 Mul(&yr, &λ). 540 Sub(&yr, &p.Y) 541 542 evaluations.R0.Set(&λ) 543 evaluations.R1.Mul(&λ, &p.X). 544 Sub(&evaluations.R1, &p.Y) 545 546 p.X.Set(&xr) 547 p.Y.Set(&yr) 548 } 549 550 func (p *G2Affine) addStep(evaluations *LineEvaluationAff, a *G2Affine) { 551 var n, d, λ, λλ, xr, yr fptower.E2 552 553 // compute λ = (y2-y1)/(x2-x1) 554 n.Sub(&a.Y, &p.Y) 555 d.Sub(&a.X, &p.X) 556 λ.Div(&n, &d) 557 558 // xr = λ²-x1-x2 559 λλ.Square(&λ) 560 n.Add(&p.X, &a.X) 561 xr.Sub(&λλ, &n) 562 563 // yr = λ(x1-xr) - y1 564 yr.Sub(&p.X, &xr). 565 Mul(&yr, &λ). 566 Sub(&yr, &p.Y) 567 568 evaluations.R0.Set(&λ) 569 evaluations.R1.Mul(&λ, &p.X). 570 Sub(&evaluations.R1, &p.Y) 571 572 p.X.Set(&xr) 573 p.Y.Set(&yr) 574 } 575 576 func (p *G2Affine) doubleAndAddStep(evaluations1, evaluations2 *LineEvaluationAff, a *G2Affine) { 577 var n, d, l1, x3, l2, x4, y4 fptower.E2 578 579 // compute λ1 = (y2-y1)/(x2-x1) 580 n.Sub(&p.Y, &a.Y) 581 d.Sub(&p.X, &a.X) 582 l1.Div(&n, &d) 583 584 // compute x3 =λ1²-x1-x2 585 x3.Square(&l1) 586 x3.Sub(&x3, &p.X) 587 x3.Sub(&x3, &a.X) 588 589 // omit y3 computation 590 591 // compute line1 592 evaluations1.R0.Set(&l1) 593 evaluations1.R1.Mul(&l1, &p.X) 594 evaluations1.R1.Sub(&evaluations1.R1, &p.Y) 595 596 // compute λ2 = -λ1-2y1/(x3-x1) 597 n.Double(&p.Y) 598 d.Sub(&x3, &p.X) 599 l2.Div(&n, &d) 600 l2.Add(&l2, &l1) 601 l2.Neg(&l2) 602 603 // compute x4 = λ2²-x1-x3 604 x4.Square(&l2) 605 x4.Sub(&x4, &p.X) 606 x4.Sub(&x4, &x3) 607 608 // compute y4 = λ2(x1 - x4)-y1 609 y4.Sub(&p.X, &x4) 610 y4.Mul(&l2, &y4) 611 y4.Sub(&y4, &p.Y) 612 613 // compute line2 614 evaluations2.R0.Set(&l2) 615 evaluations2.R1.Mul(&l2, &p.X) 616 evaluations2.R1.Sub(&evaluations2.R1, &p.Y) 617 618 p.X.Set(&x4) 619 p.Y.Set(&y4) 620 }