github.com/consensys/gnark-crypto@v0.14.0/ecc/bls12-378/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 bls12378 16 17 import ( 18 "errors" 19 20 "github.com/consensys/gnark-crypto/ecc/bls12-378/fp" 21 "github.com/consensys/gnark-crypto/ecc/bls12-378/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].Expt(&result) 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] = 0 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 162 if n >= 2 { 163 // k = 1, separately to avoid MulBy014 (res × ℓ) 164 // (res is also a line at this point, so we use Mul014By014 ℓ × ℓ) 165 166 // qProj[1] ← 2qProj[1] and l1 the tangent ℓ passing 2qProj[1] 167 qProj[1].doubleStep(&l1) 168 // line evaluation at P[1] 169 l1.r1.MulByElement(&l1.r1, &p[1].X) 170 l1.r2.MulByElement(&l1.r2, &p[1].Y) 171 // ℓ × res 172 prodLines = fptower.Mul014By014(&l1.r0, &l1.r1, &l1.r2, &result.C0.B0, &result.C0.B1, &result.C1.B1) 173 result.C0.B0 = prodLines[0] 174 result.C0.B1 = prodLines[1] 175 result.C0.B2 = prodLines[2] 176 result.C1.B1 = prodLines[3] 177 result.C1.B2 = prodLines[4] 178 } 179 180 // k >= 2 181 for k := 2; k < n; k++ { 182 // qProj[k] ← 2qProj[k] and l1 the tangent ℓ passing 2qProj[k] 183 qProj[k].doubleStep(&l1) 184 // line evaluation at P[k] 185 l1.r1.MulByElement(&l1.r1, &p[k].X) 186 l1.r2.MulByElement(&l1.r2, &p[k].Y) 187 // ℓ × res 188 result.MulBy014(&l1.r0, &l1.r1, &l1.r2) 189 } 190 191 // i <= 61 192 for i := len(LoopCounter) - 3; i >= 1; i-- { 193 // mutualize the square among n Miller loops 194 // (∏ᵢfᵢ)² 195 result.Square(&result) 196 197 for k := 0; k < n; k++ { 198 // qProj[k] ← 2qProj[k] and l1 the tangent ℓ passing 2qProj[k] 199 qProj[k].doubleStep(&l1) 200 // line evaluation at P[k] 201 l1.r1.MulByElement(&l1.r1, &p[k].X) 202 l1.r2.MulByElement(&l1.r2, &p[k].Y) 203 204 if LoopCounter[i] == 0 { 205 // ℓ × res 206 result.MulBy014(&l1.r0, &l1.r1, &l1.r2) 207 } else { 208 // qProj[k] ← qProj[k]+Q[k] and 209 // l2 the line ℓ passing qProj[k] and Q[k] 210 qProj[k].addMixedStep(&l2, &q[k]) 211 // line evaluation at P[k] 212 l2.r1.MulByElement(&l2.r1, &p[k].X) 213 l2.r2.MulByElement(&l2.r2, &p[k].Y) 214 // ℓ × ℓ 215 prodLines = fptower.Mul014By014(&l1.r0, &l1.r1, &l1.r2, &l2.r0, &l2.r1, &l2.r2) 216 // (ℓ × ℓ) × result 217 result.MulBy01245(&prodLines) 218 } 219 } 220 } 221 222 // i = 0, separately to avoid a point addition 223 // LoopCounter[0] = 1 224 result.Square(&result) 225 for k := 0; k < n; k++ { 226 // qProj[k] ← 2qProj[k] and l1 the tangent ℓ passing 2qProj[k] 227 qProj[k].doubleStep(&l1) 228 // line evaluation at P[k] 229 l1.r1.MulByElement(&l1.r1, &p[k].X) 230 l1.r2.MulByElement(&l1.r2, &p[k].Y) 231 232 // l2 the line passing qProj[k] and Q 233 qProj[k].lineCompute(&l2, &q[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(&l1.r0, &l1.r1, &l1.r2, &l2.r0, &l2.r1, &l2.r2) 239 // (ℓ × ℓ) × result 240 result.MulBy01245(&prodLines) 241 } 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.Mul(&D, &bTwistCurveCoeff) 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 // lineCompute computes the line through p in Homogenous projective coordinates 326 // and a in affine coordinates. It does not compute the resulting point p+a. 327 func (p *g2Proj) lineCompute(evaluations *lineEvaluation, a *G2Affine) { 328 329 // get some Element from our pool 330 var Y2Z1, X2Z1, O, L, t2, J fptower.E2 331 Y2Z1.Mul(&a.Y, &p.z) 332 O.Sub(&p.y, &Y2Z1) 333 X2Z1.Mul(&a.X, &p.z) 334 L.Sub(&p.x, &X2Z1) 335 t2.Mul(&L, &a.Y) 336 J.Mul(&a.X, &O). 337 Sub(&J, &t2) 338 339 // Line evaluation 340 evaluations.r0.Set(&J) 341 evaluations.r1.Neg(&O) 342 evaluations.r2.Set(&L) 343 } 344 345 // ---------------------- 346 // Fixed-argument pairing 347 // ---------------------- 348 349 type LineEvaluationAff struct { 350 R0 fptower.E2 351 R1 fptower.E2 352 } 353 354 // PairFixedQ calculates the reduced pairing for a set of points 355 // ∏ᵢ e(Pᵢ, Qᵢ) where Q are fixed points in G2. 356 // 357 // This function doesn't check that the inputs are in the correct subgroup. See IsInSubGroup. 358 func PairFixedQ(P []G1Affine, lines [][2][len(LoopCounter) - 1]LineEvaluationAff) (GT, error) { 359 f, err := MillerLoopFixedQ(P, lines) 360 if err != nil { 361 return GT{}, err 362 } 363 return FinalExponentiation(&f), nil 364 } 365 366 // PairingCheckFixedQ calculates the reduced pairing for a set of points and returns True if the result is One 367 // ∏ᵢ e(Pᵢ, Qᵢ) =? 1 where Q are fixed points in G2. 368 // 369 // This function doesn't check that the inputs are in the correct subgroup. See IsInSubGroup. 370 func PairingCheckFixedQ(P []G1Affine, lines [][2][len(LoopCounter) - 1]LineEvaluationAff) (bool, error) { 371 f, err := PairFixedQ(P, lines) 372 if err != nil { 373 return false, err 374 } 375 var one GT 376 one.SetOne() 377 return f.Equal(&one), nil 378 } 379 380 // PrecomputeLines precomputes the lines for the fixed-argument Miller loop 381 func PrecomputeLines(Q G2Affine) (PrecomputedLines [2][len(LoopCounter) - 1]LineEvaluationAff) { 382 var accQ G2Affine 383 accQ.Set(&Q) 384 385 n := len(LoopCounter) 386 for i := n - 2; i >= 0; i-- { 387 accQ.doubleStep(&PrecomputedLines[0][i]) 388 if LoopCounter[i] == 0 { 389 continue 390 } else { 391 accQ.addStep(&PrecomputedLines[1][i], &Q) 392 } 393 } 394 return PrecomputedLines 395 } 396 397 // MillerLoopFixedQ computes the multi-Miller loop as in MillerLoop 398 // but Qᵢ are fixed points in G2 known in advance. 399 func MillerLoopFixedQ(P []G1Affine, lines [][2][len(LoopCounter) - 1]LineEvaluationAff) (GT, error) { 400 n := len(P) 401 if n == 0 || n != len(lines) { 402 return GT{}, errors.New("invalid inputs sizes") 403 } 404 405 // no need to filter infinity points: 406 // 1. if Pᵢ=(0,0) then -x/y=1/y=0 by gnark-crypto convention and so 407 // lines R0 and R1 are 0. It happens that result will stay, through 408 // the Miller loop, in 𝔽p⁶ because MulBy01(0,0,1), 409 // Mul01By01(0,0,1,0,0,1) and MulBy01245 set result.C0 to 0. At the 410 // end result will be in a proper subgroup of Fp¹² so it be reduced to 411 // 1 in FinalExponentiation. 412 // 413 // and/or 414 // 415 // 2. if Qᵢ=(0,0) then PrecomputeLines(Qᵢ) will return lines R0 and R1 416 // that are 0 because of gnark-convention (*/0==0) in doubleStep and 417 // addStep. Similarly to Pᵢ=(0,0) it happens that result be 1 418 // after the FinalExponentiation. 419 420 // precomputations 421 yInv := make([]fp.Element, n) 422 xNegOverY := make([]fp.Element, n) 423 for k := 0; k < n; k++ { 424 yInv[k].Set(&P[k].Y) 425 } 426 yInv = fp.BatchInvert(yInv) 427 for k := 0; k < n; k++ { 428 xNegOverY[k].Mul(&P[k].X, &yInv[k]). 429 Neg(&xNegOverY[k]) 430 } 431 432 var result GT 433 result.SetOne() 434 var prodLines [5]E2 435 436 // Compute ∏ᵢ { fᵢ_{x₀,Q}(P) } 437 for i := len(LoopCounter) - 2; i >= 0; i-- { 438 // mutualize the square among n Miller loops 439 // (∏ᵢfᵢ)² 440 result.Square(&result) 441 442 for k := 0; k < n; k++ { 443 // line evaluation at P[k] 444 lines[k][0][i].R1. 445 MulByElement( 446 &lines[k][0][i].R1, 447 &yInv[k], 448 ) 449 lines[k][0][i].R0. 450 MulByElement(&lines[k][0][i].R0, 451 &xNegOverY[k], 452 ) 453 if LoopCounter[i] == 0 { 454 // ℓ × res 455 result.MulBy01( 456 &lines[k][0][i].R1, 457 &lines[k][0][i].R0, 458 ) 459 460 } else { 461 lines[k][1][i].R1. 462 MulByElement( 463 &lines[k][1][i].R1, 464 &yInv[k], 465 ) 466 lines[k][1][i].R0. 467 MulByElement( 468 &lines[k][1][i].R0, 469 &xNegOverY[k], 470 ) 471 prodLines = fptower.Mul01By01( 472 &lines[k][0][i].R1, &lines[k][0][i].R0, 473 &lines[k][1][i].R1, &lines[k][1][i].R0, 474 ) 475 result.MulBy01245(&prodLines) 476 } 477 } 478 } 479 480 return result, nil 481 } 482 483 func (p *G2Affine) doubleStep(evaluations *LineEvaluationAff) { 484 485 var n, d, λ, xr, yr fptower.E2 486 // λ = 3x²/2y 487 n.Square(&p.X) 488 λ.Double(&n). 489 Add(&λ, &n) 490 d.Double(&p.Y) 491 λ.Div(&λ, &d) 492 493 // xr = λ²-2x 494 xr.Square(&λ). 495 Sub(&xr, &p.X). 496 Sub(&xr, &p.X) 497 498 // yr = λ(x-xr)-y 499 yr.Sub(&p.X, &xr). 500 Mul(&yr, &λ). 501 Sub(&yr, &p.Y) 502 503 evaluations.R0.Set(&λ) 504 evaluations.R1.Mul(&λ, &p.X). 505 Sub(&evaluations.R1, &p.Y) 506 507 p.X.Set(&xr) 508 p.Y.Set(&yr) 509 } 510 511 func (p *G2Affine) addStep(evaluations *LineEvaluationAff, a *G2Affine) { 512 var n, d, λ, λλ, xr, yr fptower.E2 513 514 // compute λ = (y2-y1)/(x2-x1) 515 n.Sub(&a.Y, &p.Y) 516 d.Sub(&a.X, &p.X) 517 λ.Div(&n, &d) 518 519 // xr = λ²-x1-x2 520 λλ.Square(&λ) 521 n.Add(&p.X, &a.X) 522 xr.Sub(&λλ, &n) 523 524 // yr = λ(x1-xr) - y1 525 yr.Sub(&p.X, &xr). 526 Mul(&yr, &λ). 527 Sub(&yr, &p.Y) 528 529 evaluations.R0.Set(&λ) 530 evaluations.R1.Mul(&λ, &p.X). 531 Sub(&evaluations.R1, &p.Y) 532 533 p.X.Set(&xr) 534 p.Y.Set(&yr) 535 }