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