github.com/ethereum/go-ethereum@v1.14.3/tests/fuzzers/bls12381/bls12381_fuzz.go (about)

     1  // Copyright 2021 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  //go:build cgo
    18  // +build cgo
    19  
    20  package bls
    21  
    22  import (
    23  	"bytes"
    24  	"crypto/rand"
    25  	"fmt"
    26  	"io"
    27  	"math/big"
    28  
    29  	"github.com/consensys/gnark-crypto/ecc"
    30  	gnark "github.com/consensys/gnark-crypto/ecc/bls12-381"
    31  	"github.com/consensys/gnark-crypto/ecc/bls12-381/fp"
    32  	"github.com/consensys/gnark-crypto/ecc/bls12-381/fr"
    33  	"github.com/ethereum/go-ethereum/common"
    34  	bls12381 "github.com/kilic/bls12-381"
    35  	blst "github.com/supranational/blst/bindings/go"
    36  )
    37  
    38  func fuzzG1SubgroupChecks(data []byte) int {
    39  	input := bytes.NewReader(data)
    40  	kpG1, cpG1, blG1, err := getG1Points(input)
    41  	if err != nil {
    42  		return 0
    43  	}
    44  	inSubGroupKilic := bls12381.NewG1().InCorrectSubgroup(kpG1)
    45  	inSubGroupGnark := cpG1.IsInSubGroup()
    46  	inSubGroupBLST := blG1.InG1()
    47  	if inSubGroupKilic != inSubGroupGnark {
    48  		panic(fmt.Sprintf("differing subgroup check, kilic %v, gnark %v", inSubGroupKilic, inSubGroupGnark))
    49  	}
    50  	if inSubGroupKilic != inSubGroupBLST {
    51  		panic(fmt.Sprintf("differing subgroup check, kilic %v, blst %v", inSubGroupKilic, inSubGroupBLST))
    52  	}
    53  	return 1
    54  }
    55  
    56  func fuzzG2SubgroupChecks(data []byte) int {
    57  	input := bytes.NewReader(data)
    58  	kpG2, cpG2, blG2, err := getG2Points(input)
    59  	if err != nil {
    60  		return 0
    61  	}
    62  	inSubGroupKilic := bls12381.NewG2().InCorrectSubgroup(kpG2)
    63  	inSubGroupGnark := cpG2.IsInSubGroup()
    64  	inSubGroupBLST := blG2.InG2()
    65  	if inSubGroupKilic != inSubGroupGnark {
    66  		panic(fmt.Sprintf("differing subgroup check, kilic %v, gnark %v", inSubGroupKilic, inSubGroupGnark))
    67  	}
    68  	if inSubGroupKilic != inSubGroupBLST {
    69  		panic(fmt.Sprintf("differing subgroup check, kilic %v, blst %v", inSubGroupKilic, inSubGroupBLST))
    70  	}
    71  	return 1
    72  }
    73  
    74  func fuzzCrossPairing(data []byte) int {
    75  	input := bytes.NewReader(data)
    76  
    77  	// get random G1 points
    78  	kpG1, cpG1, blG1, err := getG1Points(input)
    79  	if err != nil {
    80  		return 0
    81  	}
    82  
    83  	// get random G2 points
    84  	kpG2, cpG2, blG2, err := getG2Points(input)
    85  	if err != nil {
    86  		return 0
    87  	}
    88  
    89  	// compute pairing using geth
    90  	engine := bls12381.NewEngine()
    91  	engine.AddPair(kpG1, kpG2)
    92  	kResult := engine.Result()
    93  
    94  	// compute pairing using gnark
    95  	cResult, err := gnark.Pair([]gnark.G1Affine{*cpG1}, []gnark.G2Affine{*cpG2})
    96  	if err != nil {
    97  		panic(fmt.Sprintf("gnark/bls12381 encountered error: %v", err))
    98  	}
    99  
   100  	// compare result
   101  	if !(bytes.Equal(cResult.Marshal(), bls12381.NewGT().ToBytes(kResult))) {
   102  		panic("pairing mismatch gnark / geth ")
   103  	}
   104  
   105  	// compute pairing using blst
   106  	blstResult := blst.Fp12MillerLoop(blG2, blG1)
   107  	blstResult.FinalExp()
   108  	res := massageBLST(blstResult.ToBendian())
   109  	if !(bytes.Equal(res, bls12381.NewGT().ToBytes(kResult))) {
   110  		panic("pairing mismatch blst / geth")
   111  	}
   112  
   113  	return 1
   114  }
   115  
   116  func massageBLST(in []byte) []byte {
   117  	out := make([]byte, len(in))
   118  	len := 12 * 48
   119  	// 1
   120  	copy(out[0:], in[len-1*48:len])
   121  	copy(out[1*48:], in[len-2*48:len-1*48])
   122  	// 2
   123  	copy(out[6*48:], in[len-3*48:len-2*48])
   124  	copy(out[7*48:], in[len-4*48:len-3*48])
   125  	// 3
   126  	copy(out[2*48:], in[len-5*48:len-4*48])
   127  	copy(out[3*48:], in[len-6*48:len-5*48])
   128  	// 4
   129  	copy(out[8*48:], in[len-7*48:len-6*48])
   130  	copy(out[9*48:], in[len-8*48:len-7*48])
   131  	// 5
   132  	copy(out[4*48:], in[len-9*48:len-8*48])
   133  	copy(out[5*48:], in[len-10*48:len-9*48])
   134  	// 6
   135  	copy(out[10*48:], in[len-11*48:len-10*48])
   136  	copy(out[11*48:], in[len-12*48:len-11*48])
   137  	return out
   138  }
   139  
   140  func fuzzCrossG1Add(data []byte) int {
   141  	input := bytes.NewReader(data)
   142  
   143  	// get random G1 points
   144  	kp1, cp1, bl1, err := getG1Points(input)
   145  	if err != nil {
   146  		return 0
   147  	}
   148  
   149  	// get random G1 points
   150  	kp2, cp2, bl2, err := getG1Points(input)
   151  	if err != nil {
   152  		return 0
   153  	}
   154  
   155  	// compute kp = kp1 + kp2
   156  	g1 := bls12381.NewG1()
   157  	kp := bls12381.PointG1{}
   158  	g1.Add(&kp, kp1, kp2)
   159  
   160  	// compute cp = cp1 + cp2
   161  	_cp1 := new(gnark.G1Jac).FromAffine(cp1)
   162  	_cp2 := new(gnark.G1Jac).FromAffine(cp2)
   163  	cp := new(gnark.G1Affine).FromJacobian(_cp1.AddAssign(_cp2))
   164  
   165  	// compare result
   166  	if !(bytes.Equal(cp.Marshal(), g1.ToBytes(&kp))) {
   167  		panic("G1 point addition mismatch gnark / geth ")
   168  	}
   169  
   170  	bl3 := blst.P1AffinesAdd([]*blst.P1Affine{bl1, bl2})
   171  	if !(bytes.Equal(cp.Marshal(), bl3.Serialize())) {
   172  		panic("G1 point addition mismatch blst / geth ")
   173  	}
   174  
   175  	return 1
   176  }
   177  
   178  func fuzzCrossG2Add(data []byte) int {
   179  	input := bytes.NewReader(data)
   180  
   181  	// get random G2 points
   182  	kp1, cp1, bl1, err := getG2Points(input)
   183  	if err != nil {
   184  		return 0
   185  	}
   186  
   187  	// get random G2 points
   188  	kp2, cp2, bl2, err := getG2Points(input)
   189  	if err != nil {
   190  		return 0
   191  	}
   192  
   193  	// compute kp = kp1 + kp2
   194  	g2 := bls12381.NewG2()
   195  	kp := bls12381.PointG2{}
   196  	g2.Add(&kp, kp1, kp2)
   197  
   198  	// compute cp = cp1 + cp2
   199  	_cp1 := new(gnark.G2Jac).FromAffine(cp1)
   200  	_cp2 := new(gnark.G2Jac).FromAffine(cp2)
   201  	cp := new(gnark.G2Affine).FromJacobian(_cp1.AddAssign(_cp2))
   202  
   203  	// compare result
   204  	if !(bytes.Equal(cp.Marshal(), g2.ToBytes(&kp))) {
   205  		panic("G2 point addition mismatch gnark / geth ")
   206  	}
   207  
   208  	bl3 := blst.P2AffinesAdd([]*blst.P2Affine{bl1, bl2})
   209  	if !(bytes.Equal(cp.Marshal(), bl3.Serialize())) {
   210  		panic("G1 point addition mismatch blst / geth ")
   211  	}
   212  
   213  	return 1
   214  }
   215  
   216  func fuzzCrossG1MultiExp(data []byte) int {
   217  	var (
   218  		input        = bytes.NewReader(data)
   219  		gethScalars  []*bls12381.Fr
   220  		gnarkScalars []fr.Element
   221  		gethPoints   []*bls12381.PointG1
   222  		gnarkPoints  []gnark.G1Affine
   223  	)
   224  	// n random scalars (max 17)
   225  	for i := 0; i < 17; i++ {
   226  		// note that geth/crypto/bls12381 works only with scalars <= 32bytes
   227  		s, err := randomScalar(input, fr.Modulus())
   228  		if err != nil {
   229  			break
   230  		}
   231  		// get a random G1 point as basis
   232  		kp1, cp1, _, err := getG1Points(input)
   233  		if err != nil {
   234  			break
   235  		}
   236  		gethScalars = append(gethScalars, bls12381.NewFr().FromBytes(s.Bytes()))
   237  		var gnarkScalar = &fr.Element{}
   238  		gnarkScalar = gnarkScalar.SetBigInt(s)
   239  		gnarkScalars = append(gnarkScalars, *gnarkScalar)
   240  
   241  		gethPoints = append(gethPoints, new(bls12381.PointG1).Set(kp1))
   242  		gnarkPoints = append(gnarkPoints, *cp1)
   243  	}
   244  	if len(gethScalars) == 0 {
   245  		return 0
   246  	}
   247  	// compute multi exponentiation
   248  	g1 := bls12381.NewG1()
   249  	kp := bls12381.PointG1{}
   250  	if _, err := g1.MultiExp(&kp, gethPoints, gethScalars); err != nil {
   251  		panic(fmt.Sprintf("G1 multi exponentiation errored (geth): %v", err))
   252  	}
   253  	// note that geth/crypto/bls12381.MultiExp mutates the scalars slice (and sets all the scalars to zero)
   254  
   255  	// gnark multi exp
   256  	cp := new(gnark.G1Affine)
   257  	cp.MultiExp(gnarkPoints, gnarkScalars, ecc.MultiExpConfig{})
   258  
   259  	// compare result
   260  	if !(bytes.Equal(cp.Marshal(), g1.ToBytes(&kp))) {
   261  		panic("G1 multi exponentiation mismatch gnark / geth ")
   262  	}
   263  
   264  	return 1
   265  }
   266  
   267  func getG1Points(input io.Reader) (*bls12381.PointG1, *gnark.G1Affine, *blst.P1Affine, error) {
   268  	// sample a random scalar
   269  	s, err := randomScalar(input, fp.Modulus())
   270  	if err != nil {
   271  		return nil, nil, nil, err
   272  	}
   273  
   274  	// compute a random point
   275  	cp := new(gnark.G1Affine)
   276  	_, _, g1Gen, _ := gnark.Generators()
   277  	cp.ScalarMultiplication(&g1Gen, s)
   278  	cpBytes := cp.Marshal()
   279  
   280  	// marshal gnark point -> geth point
   281  	g1 := bls12381.NewG1()
   282  	kp, err := g1.FromBytes(cpBytes)
   283  	if err != nil {
   284  		panic(fmt.Sprintf("Could not marshal gnark.G1 -> geth.G1: %v", err))
   285  	}
   286  	if !bytes.Equal(g1.ToBytes(kp), cpBytes) {
   287  		panic("bytes(gnark.G1) != bytes(geth.G1)")
   288  	}
   289  
   290  	// marshal gnark point -> blst point
   291  	scalar := new(blst.Scalar).FromBEndian(common.LeftPadBytes(s.Bytes(), 32))
   292  	p1 := new(blst.P1Affine).From(scalar)
   293  	if !bytes.Equal(p1.Serialize(), cpBytes) {
   294  		panic("bytes(blst.G1) != bytes(geth.G1)")
   295  	}
   296  
   297  	return kp, cp, p1, nil
   298  }
   299  
   300  func getG2Points(input io.Reader) (*bls12381.PointG2, *gnark.G2Affine, *blst.P2Affine, error) {
   301  	// sample a random scalar
   302  	s, err := randomScalar(input, fp.Modulus())
   303  	if err != nil {
   304  		return nil, nil, nil, err
   305  	}
   306  
   307  	// compute a random point
   308  	cp := new(gnark.G2Affine)
   309  	_, _, _, g2Gen := gnark.Generators()
   310  	cp.ScalarMultiplication(&g2Gen, s)
   311  	cpBytes := cp.Marshal()
   312  
   313  	// marshal gnark point -> geth point
   314  	g2 := bls12381.NewG2()
   315  	kp, err := g2.FromBytes(cpBytes)
   316  	if err != nil {
   317  		panic(fmt.Sprintf("Could not marshal gnark.G2 -> geth.G2: %v", err))
   318  	}
   319  	if !bytes.Equal(g2.ToBytes(kp), cpBytes) {
   320  		panic("bytes(gnark.G2) != bytes(geth.G2)")
   321  	}
   322  
   323  	// marshal gnark point -> blst point
   324  	// Left pad the scalar to 32 bytes
   325  	scalar := new(blst.Scalar).FromBEndian(common.LeftPadBytes(s.Bytes(), 32))
   326  	p2 := new(blst.P2Affine).From(scalar)
   327  	if !bytes.Equal(p2.Serialize(), cpBytes) {
   328  		panic("bytes(blst.G2) != bytes(geth.G2)")
   329  	}
   330  
   331  	return kp, cp, p2, nil
   332  }
   333  
   334  func randomScalar(r io.Reader, max *big.Int) (k *big.Int, err error) {
   335  	for {
   336  		k, err = rand.Int(r, max)
   337  		if err != nil || k.Sign() > 0 {
   338  			return
   339  		}
   340  	}
   341  }