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