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  }