github.com/consensys/gnark-crypto@v0.14.0/ecc/stark-curve/g1_test.go (about) 1 // Copyright 2020 ConsenSys Software Inc. 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 // FOO 16 17 package starkcurve 18 19 import ( 20 "crypto/rand" 21 "math/big" 22 "testing" 23 24 "github.com/consensys/gnark-crypto/ecc/stark-curve/fp" 25 26 "github.com/consensys/gnark-crypto/ecc/stark-curve/fr" 27 "github.com/leanovate/gopter" 28 "github.com/leanovate/gopter/prop" 29 ) 30 31 const ( 32 nbFuzzShort = 10 33 nbFuzz = 100 34 ) 35 36 // define Gopters generators 37 38 // GenFr generates an Fr element 39 func GenFr() gopter.Gen { 40 return func(genParams *gopter.GenParameters) *gopter.GenResult { 41 var elmt fr.Element 42 43 if _, err := elmt.SetRandom(); err != nil { 44 panic(err) 45 } 46 47 return gopter.NewGenResult(elmt, gopter.NoShrinker) 48 } 49 } 50 51 // GenFp generates an Fp element 52 func GenFp() gopter.Gen { 53 return func(genParams *gopter.GenParameters) *gopter.GenResult { 54 var elmt fp.Element 55 56 if _, err := elmt.SetRandom(); err != nil { 57 panic(err) 58 } 59 60 return gopter.NewGenResult(elmt, gopter.NoShrinker) 61 } 62 } 63 64 // GenBigInt generates a big.Int 65 func GenBigInt() gopter.Gen { 66 return func(genParams *gopter.GenParameters) *gopter.GenResult { 67 var s big.Int 68 var b [fp.Bytes]byte 69 _, err := rand.Read(b[:]) //#nosec G404 weak rng is fine here 70 if err != nil { 71 panic(err) 72 } 73 s.SetBytes(b[:]) 74 genResult := gopter.NewGenResult(s, gopter.NoShrinker) 75 return genResult 76 } 77 } 78 func TestG1AffineIsOnCurve(t *testing.T) { 79 t.Parallel() 80 parameters := gopter.DefaultTestParameters() 81 if testing.Short() { 82 parameters.MinSuccessfulTests = nbFuzzShort 83 } else { 84 parameters.MinSuccessfulTests = nbFuzz 85 } 86 87 properties := gopter.NewProperties(parameters) 88 89 properties.Property("[STARK-CURVE] g1Gen (affine) should be on the curve", prop.ForAll( 90 func(a fp.Element) bool { 91 var op1, op2 G1Affine 92 op1.FromJacobian(&g1Gen) 93 op2.Set(&op1) 94 op2.Y.Mul(&op2.Y, &a) 95 return op1.IsOnCurve() && !op2.IsOnCurve() 96 }, 97 GenFp(), 98 )) 99 100 properties.Property("[STARK-CURVE] g1Gen (Jacobian) should be on the curve", prop.ForAll( 101 func(a fp.Element) bool { 102 var op1, op2, op3 G1Jac 103 op1.Set(&g1Gen) 104 op3.Set(&g1Gen) 105 106 op2 = fuzzG1Jac(&g1Gen, a) 107 op3.Y.Mul(&op3.Y, &a) 108 return op1.IsOnCurve() && op2.IsOnCurve() && !op3.IsOnCurve() 109 }, 110 GenFp(), 111 )) 112 113 properties.Property("[STARK-CURVE] IsInSubGroup and MulBy subgroup order should be the same", prop.ForAll( 114 func(a fp.Element) bool { 115 var op1, op2 G1Jac 116 op1 = fuzzG1Jac(&g1Gen, a) 117 _r := fr.Modulus() 118 op2.ScalarMultiplication(&op1, _r) 119 return op1.IsInSubGroup() && op2.Z.IsZero() 120 }, 121 GenFp(), 122 )) 123 124 properties.TestingRun(t, gopter.ConsoleReporter(false)) 125 } 126 127 func TestG1AffineConversions(t *testing.T) { 128 t.Parallel() 129 parameters := gopter.DefaultTestParameters() 130 if testing.Short() { 131 parameters.MinSuccessfulTests = nbFuzzShort 132 } else { 133 parameters.MinSuccessfulTests = nbFuzz 134 } 135 136 properties := gopter.NewProperties(parameters) 137 138 properties.Property("[STARK-CURVE] Affine representation should be independent of the Jacobian representative", prop.ForAll( 139 func(a fp.Element) bool { 140 g := fuzzG1Jac(&g1Gen, a) 141 var op1 G1Affine 142 op1.FromJacobian(&g) 143 return op1.X.Equal(&g1Gen.X) && op1.Y.Equal(&g1Gen.Y) 144 }, 145 GenFp(), 146 )) 147 148 properties.Property("[STARK-CURVE] Affine representation should be independent of a Extended Jacobian representative", prop.ForAll( 149 func(a fp.Element) bool { 150 var g g1JacExtended 151 g.X.Set(&g1Gen.X) 152 g.Y.Set(&g1Gen.Y) 153 g.ZZ.Set(&g1Gen.Z) 154 g.ZZZ.Set(&g1Gen.Z) 155 gfuzz := fuzzg1JacExtended(&g, a) 156 157 var op1 G1Affine 158 op1.fromJacExtended(&gfuzz) 159 return op1.X.Equal(&g1Gen.X) && op1.Y.Equal(&g1Gen.Y) 160 }, 161 GenFp(), 162 )) 163 164 properties.Property("[STARK-CURVE] Jacobian representation should be the same as the affine representative", prop.ForAll( 165 func(a fp.Element) bool { 166 var g G1Jac 167 var op1 G1Affine 168 op1.X.Set(&g1Gen.X) 169 op1.Y.Set(&g1Gen.Y) 170 171 var one fp.Element 172 one.SetOne() 173 174 g.FromAffine(&op1) 175 176 return g.X.Equal(&g1Gen.X) && g.Y.Equal(&g1Gen.Y) && g.Z.Equal(&one) 177 }, 178 GenFp(), 179 )) 180 181 properties.Property("[STARK-CURVE] Converting affine symbol for infinity to Jacobian should output correct infinity in Jacobian", prop.ForAll( 182 func() bool { 183 var g G1Affine 184 g.X.SetZero() 185 g.Y.SetZero() 186 var op1 G1Jac 187 op1.FromAffine(&g) 188 var one, zero fp.Element 189 one.SetOne() 190 return op1.X.Equal(&one) && op1.Y.Equal(&one) && op1.Z.Equal(&zero) 191 }, 192 )) 193 194 properties.Property("[STARK-CURVE] Converting infinity in extended Jacobian to affine should output infinity symbol in Affine", prop.ForAll( 195 func() bool { 196 var g G1Affine 197 var op1 g1JacExtended 198 var zero fp.Element 199 op1.X.Set(&g1Gen.X) 200 op1.Y.Set(&g1Gen.Y) 201 g.fromJacExtended(&op1) 202 return g.X.Equal(&zero) && g.Y.Equal(&zero) 203 }, 204 )) 205 206 properties.Property("[STARK-CURVE] Converting infinity in extended Jacobian to Jacobian should output infinity in Jacobian", prop.ForAll( 207 func() bool { 208 var g G1Jac 209 var op1 g1JacExtended 210 var zero, one fp.Element 211 one.SetOne() 212 op1.X.Set(&g1Gen.X) 213 op1.Y.Set(&g1Gen.Y) 214 g.fromJacExtended(&op1) 215 return g.X.Equal(&one) && g.Y.Equal(&one) && g.Z.Equal(&zero) 216 }, 217 )) 218 219 properties.Property("[STARK-CURVE] [Jacobian] Two representatives of the same class should be equal", prop.ForAll( 220 func(a, b fp.Element) bool { 221 op1 := fuzzG1Jac(&g1Gen, a) 222 op2 := fuzzG1Jac(&g1Gen, b) 223 return op1.Equal(&op2) 224 }, 225 GenFp(), 226 GenFp(), 227 )) 228 properties.Property("[STARK-CURVE] BatchJacobianToAffineG1 and FromJacobian should output the same result", prop.ForAll( 229 func(a, b fp.Element) bool { 230 g1 := fuzzG1Jac(&g1Gen, a) 231 g2 := fuzzG1Jac(&g1Gen, b) 232 var op1, op2 G1Affine 233 op1.FromJacobian(&g1) 234 op2.FromJacobian(&g2) 235 baseTableAff := BatchJacobianToAffineG1([]G1Jac{g1, g2}) 236 return op1.Equal(&baseTableAff[0]) && op2.Equal(&baseTableAff[1]) 237 }, 238 GenFp(), 239 GenFp(), 240 )) 241 242 properties.TestingRun(t, gopter.ConsoleReporter(false)) 243 } 244 245 func TestG1AffineOps(t *testing.T) { 246 t.Parallel() 247 parameters := gopter.DefaultTestParameters() 248 parameters.MinSuccessfulTests = 10 249 250 properties := gopter.NewProperties(parameters) 251 252 genScalar := GenFr() 253 254 properties.Property("[STARK-CURVE] [Jacobian] Add should call double when having adding the same point", prop.ForAll( 255 func(a, b fp.Element) bool { 256 fop1 := fuzzG1Jac(&g1Gen, a) 257 fop2 := fuzzG1Jac(&g1Gen, b) 258 var op1, op2 G1Jac 259 op1.Set(&fop1).AddAssign(&fop2) 260 op2.Double(&fop2) 261 return op1.Equal(&op2) 262 }, 263 GenFp(), 264 GenFp(), 265 )) 266 267 properties.Property("[STARK-CURVE] [Jacobian] Adding the opposite of a point to itself should output inf", prop.ForAll( 268 func(a, b fp.Element) bool { 269 fop1 := fuzzG1Jac(&g1Gen, a) 270 fop2 := fuzzG1Jac(&g1Gen, b) 271 fop2.Neg(&fop2) 272 fop1.AddAssign(&fop2) 273 return fop1.Equal(&g1Infinity) 274 }, 275 GenFp(), 276 GenFp(), 277 )) 278 279 properties.Property("[STARK-CURVE] [Jacobian] Adding the inf to a point should not modify the point", prop.ForAll( 280 func(a fp.Element) bool { 281 fop1 := fuzzG1Jac(&g1Gen, a) 282 fop1.AddAssign(&g1Infinity) 283 var op2 G1Jac 284 op2.Set(&g1Infinity) 285 op2.AddAssign(&g1Gen) 286 return fop1.Equal(&g1Gen) && op2.Equal(&g1Gen) 287 }, 288 GenFp(), 289 )) 290 291 properties.Property("[STARK-CURVE] [Jacobian Extended] addMixed (-G) should equal subMixed(G)", prop.ForAll( 292 func(a fp.Element) bool { 293 fop1 := fuzzG1Jac(&g1Gen, a) 294 var p1, p1Neg G1Affine 295 p1.FromJacobian(&fop1) 296 p1Neg = p1 297 p1Neg.Y.Neg(&p1Neg.Y) 298 var o1, o2 g1JacExtended 299 o1.addMixed(&p1Neg) 300 o2.subMixed(&p1) 301 302 return o1.X.Equal(&o2.X) && 303 o1.Y.Equal(&o2.Y) && 304 o1.ZZ.Equal(&o2.ZZ) && 305 o1.ZZZ.Equal(&o2.ZZZ) 306 }, 307 GenFp(), 308 )) 309 310 properties.Property("[STARK-CURVE] [Jacobian Extended] doubleMixed (-G) should equal doubleNegMixed(G)", prop.ForAll( 311 func(a fp.Element) bool { 312 fop1 := fuzzG1Jac(&g1Gen, a) 313 var p1, p1Neg G1Affine 314 p1.FromJacobian(&fop1) 315 p1Neg = p1 316 p1Neg.Y.Neg(&p1Neg.Y) 317 var o1, o2 g1JacExtended 318 o1.doubleMixed(&p1Neg) 319 o2.doubleNegMixed(&p1) 320 321 return o1.X.Equal(&o2.X) && 322 o1.Y.Equal(&o2.Y) && 323 o1.ZZ.Equal(&o2.ZZ) && 324 o1.ZZZ.Equal(&o2.ZZZ) 325 }, 326 GenFp(), 327 )) 328 329 properties.Property("[STARK-CURVE] [Jacobian] Addmix the negation to itself should output 0", prop.ForAll( 330 func(a fp.Element) bool { 331 fop1 := fuzzG1Jac(&g1Gen, a) 332 fop1.Neg(&fop1) 333 var op2 G1Affine 334 op2.FromJacobian(&g1Gen) 335 fop1.AddMixed(&op2) 336 return fop1.Equal(&g1Infinity) 337 }, 338 GenFp(), 339 )) 340 341 properties.Property("[STARK-CURVE] scalar multiplication (double and add) should depend only on the scalar mod r", prop.ForAll( 342 func(s fr.Element) bool { 343 344 r := fr.Modulus() 345 var g G1Jac 346 g.mulWindowed(&g1Gen, r) 347 348 var scalar, blindedScalar, rminusone big.Int 349 var op1, op2, op3, gneg G1Jac 350 rminusone.SetUint64(1).Sub(r, &rminusone) 351 op3.mulWindowed(&g1Gen, &rminusone) 352 gneg.Neg(&g1Gen) 353 s.BigInt(&scalar) 354 blindedScalar.Mul(&scalar, r).Add(&blindedScalar, &scalar) 355 op1.mulWindowed(&g1Gen, &scalar) 356 op2.mulWindowed(&g1Gen, &blindedScalar) 357 358 return op1.Equal(&op2) && g.Equal(&g1Infinity) && !op1.Equal(&g1Infinity) && gneg.Equal(&op3) 359 360 }, 361 genScalar, 362 )) 363 364 properties.Property("[STARK-CURVE] JointScalarMultiplicationBase and ScalarMultiplication should output the same results", prop.ForAll( 365 func(s1, s2 fr.Element) bool { 366 367 var op1, op2, op3, temp G1Jac 368 var a G1Affine 369 370 temp.Double(&g1Gen) 371 a.FromJacobian(&temp) 372 373 op1.JointScalarMultiplicationBase(&a, s1.BigInt(new(big.Int)), s2.BigInt(new(big.Int))) 374 op2.JointScalarMultiplication(&g1Gen, &temp, s1.BigInt(new(big.Int)), s2.BigInt(new(big.Int))) 375 temp.ScalarMultiplication(&temp, s2.BigInt(new(big.Int))) 376 op3.ScalarMultiplication(&g1Gen, s1.BigInt(new(big.Int))). 377 AddAssign(&temp) 378 379 return op1.Equal(&op2) && op2.Equal(&op3) 380 381 }, 382 genScalar, 383 genScalar, 384 )) 385 386 properties.TestingRun(t, gopter.ConsoleReporter(false)) 387 } 388 389 // ------------------------------------------------------------ 390 // benches 391 392 func BenchmarkG1JacIsInSubGroup(b *testing.B) { 393 var a G1Jac 394 a.Set(&g1Gen) 395 b.ResetTimer() 396 for i := 0; i < b.N; i++ { 397 a.IsInSubGroup() 398 } 399 400 } 401 402 func BenchmarkG1JacScalarMultiplication(b *testing.B) { 403 404 var scalar big.Int 405 r := fr.Modulus() 406 scalar.SetString("5243587517512619047944770508185965837690552500527637822603658699938581184513", 10) 407 scalar.Add(&scalar, r) 408 409 var doubleAndAdd G1Jac 410 411 b.Run("double and add", func(b *testing.B) { 412 b.ResetTimer() 413 for j := 0; j < b.N; j++ { 414 doubleAndAdd.mulWindowed(&g1Gen, &scalar) 415 } 416 }) 417 418 } 419 420 func BenchmarkG1JacAdd(b *testing.B) { 421 var a G1Jac 422 a.Double(&g1Gen) 423 b.ResetTimer() 424 for i := 0; i < b.N; i++ { 425 a.AddAssign(&g1Gen) 426 } 427 } 428 429 func BenchmarkG1JacAddMixed(b *testing.B) { 430 var a G1Jac 431 a.Double(&g1Gen) 432 433 var c G1Affine 434 c.FromJacobian(&g1Gen) 435 b.ResetTimer() 436 for i := 0; i < b.N; i++ { 437 a.AddMixed(&c) 438 } 439 440 } 441 442 func BenchmarkG1JacDouble(b *testing.B) { 443 var a G1Jac 444 a.Set(&g1Gen) 445 b.ResetTimer() 446 for i := 0; i < b.N; i++ { 447 a.DoubleAssign() 448 } 449 450 } 451 452 func BenchmarkG1JacExtAddMixed(b *testing.B) { 453 var a g1JacExtended 454 a.doubleMixed(&g1GenAff) 455 456 var c G1Affine 457 c.FromJacobian(&g1Gen) 458 b.ResetTimer() 459 for i := 0; i < b.N; i++ { 460 a.addMixed(&c) 461 } 462 } 463 464 func BenchmarkG1JacExtSubMixed(b *testing.B) { 465 var a g1JacExtended 466 a.doubleMixed(&g1GenAff) 467 468 var c G1Affine 469 c.FromJacobian(&g1Gen) 470 b.ResetTimer() 471 for i := 0; i < b.N; i++ { 472 a.subMixed(&c) 473 } 474 } 475 476 func BenchmarkG1JacExtDoubleMixed(b *testing.B) { 477 var a g1JacExtended 478 a.doubleMixed(&g1GenAff) 479 480 var c G1Affine 481 c.FromJacobian(&g1Gen) 482 b.ResetTimer() 483 for i := 0; i < b.N; i++ { 484 a.doubleMixed(&c) 485 } 486 } 487 488 func BenchmarkG1JacExtDoubleNegMixed(b *testing.B) { 489 var a g1JacExtended 490 a.doubleMixed(&g1GenAff) 491 492 var c G1Affine 493 c.FromJacobian(&g1Gen) 494 b.ResetTimer() 495 for i := 0; i < b.N; i++ { 496 a.doubleNegMixed(&c) 497 } 498 } 499 500 func BenchmarkG1JacExtAdd(b *testing.B) { 501 var a, c g1JacExtended 502 a.doubleMixed(&g1GenAff) 503 c.double(&a) 504 505 b.ResetTimer() 506 for i := 0; i < b.N; i++ { 507 a.add(&c) 508 } 509 } 510 511 func BenchmarkG1JacExtDouble(b *testing.B) { 512 var a g1JacExtended 513 a.doubleMixed(&g1GenAff) 514 515 b.ResetTimer() 516 for i := 0; i < b.N; i++ { 517 a.double(&a) 518 } 519 } 520 521 func fuzzG1Jac(p *G1Jac, f fp.Element) G1Jac { 522 var res G1Jac 523 res.X.Mul(&p.X, &f).Mul(&res.X, &f) 524 res.Y.Mul(&p.Y, &f).Mul(&res.Y, &f).Mul(&res.Y, &f) 525 res.Z.Mul(&p.Z, &f) 526 return res 527 } 528 529 func fuzzg1JacExtended(p *g1JacExtended, f fp.Element) g1JacExtended { 530 var res g1JacExtended 531 var ff, fff fp.Element 532 ff.Square(&f) 533 fff.Mul(&ff, &f) 534 res.X.Mul(&p.X, &ff) 535 res.Y.Mul(&p.Y, &fff) 536 res.ZZ.Mul(&p.ZZ, &ff) 537 res.ZZZ.Mul(&p.ZZZ, &fff) 538 return res 539 }