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