github.com/palisadeinc/bor@v0.0.0-20230615125219-ab7196213d15/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 gofuzz
    18  // +build gofuzz
    19  
    20  package bls
    21  
    22  import (
    23  	"bytes"
    24  	"crypto/rand"
    25  	"fmt"
    26  	"io"
    27  	"math/big"
    28  
    29  	gnark "github.com/consensys/gnark-crypto/ecc/bls12-381"
    30  	"github.com/consensys/gnark-crypto/ecc/bls12-381/fp"
    31  	"github.com/consensys/gnark-crypto/ecc/bls12-381/fr"
    32  	"github.com/ethereum/go-ethereum/crypto/bls12381"
    33  )
    34  
    35  func FuzzCrossPairing(data []byte) int {
    36  	input := bytes.NewReader(data)
    37  
    38  	// get random G1 points
    39  	kpG1, cpG1, err := getG1Points(input)
    40  	if err != nil {
    41  		return 0
    42  	}
    43  
    44  	// get random G2 points
    45  	kpG2, cpG2, err := getG2Points(input)
    46  	if err != nil {
    47  		return 0
    48  	}
    49  
    50  	// compute pairing using geth
    51  	engine := bls12381.NewPairingEngine()
    52  	engine.AddPair(kpG1, kpG2)
    53  	kResult := engine.Result()
    54  
    55  	// compute pairing using gnark
    56  	cResult, err := gnark.Pair([]gnark.G1Affine{*cpG1}, []gnark.G2Affine{*cpG2})
    57  	if err != nil {
    58  		panic(fmt.Sprintf("gnark/bls12381 encountered error: %v", err))
    59  	}
    60  
    61  	// compare result
    62  	if !(bytes.Equal(cResult.Marshal(), bls12381.NewGT().ToBytes(kResult))) {
    63  		panic("pairing mismatch gnark / geth ")
    64  	}
    65  
    66  	return 1
    67  }
    68  
    69  func FuzzCrossG1Add(data []byte) int {
    70  	input := bytes.NewReader(data)
    71  
    72  	// get random G1 points
    73  	kp1, cp1, err := getG1Points(input)
    74  	if err != nil {
    75  		return 0
    76  	}
    77  
    78  	// get random G1 points
    79  	kp2, cp2, err := getG1Points(input)
    80  	if err != nil {
    81  		return 0
    82  	}
    83  
    84  	// compute kp = kp1 + kp2
    85  	g1 := bls12381.NewG1()
    86  	kp := bls12381.PointG1{}
    87  	g1.Add(&kp, kp1, kp2)
    88  
    89  	// compute cp = cp1 + cp2
    90  	_cp1 := new(gnark.G1Jac).FromAffine(cp1)
    91  	_cp2 := new(gnark.G1Jac).FromAffine(cp2)
    92  	cp := new(gnark.G1Affine).FromJacobian(_cp1.AddAssign(_cp2))
    93  
    94  	// compare result
    95  	if !(bytes.Equal(cp.Marshal(), g1.ToBytes(&kp))) {
    96  		panic("G1 point addition mismatch gnark / geth ")
    97  	}
    98  
    99  	return 1
   100  }
   101  
   102  func FuzzCrossG2Add(data []byte) int {
   103  	input := bytes.NewReader(data)
   104  
   105  	// get random G2 points
   106  	kp1, cp1, err := getG2Points(input)
   107  	if err != nil {
   108  		return 0
   109  	}
   110  
   111  	// get random G2 points
   112  	kp2, cp2, err := getG2Points(input)
   113  	if err != nil {
   114  		return 0
   115  	}
   116  
   117  	// compute kp = kp1 + kp2
   118  	g2 := bls12381.NewG2()
   119  	kp := bls12381.PointG2{}
   120  	g2.Add(&kp, kp1, kp2)
   121  
   122  	// compute cp = cp1 + cp2
   123  	_cp1 := new(gnark.G2Jac).FromAffine(cp1)
   124  	_cp2 := new(gnark.G2Jac).FromAffine(cp2)
   125  	cp := new(gnark.G2Affine).FromJacobian(_cp1.AddAssign(_cp2))
   126  
   127  	// compare result
   128  	if !(bytes.Equal(cp.Marshal(), g2.ToBytes(&kp))) {
   129  		panic("G2 point addition mismatch gnark / geth ")
   130  	}
   131  
   132  	return 1
   133  }
   134  
   135  func FuzzCrossG1MultiExp(data []byte) int {
   136  	var (
   137  		input        = bytes.NewReader(data)
   138  		gethScalars  []*big.Int
   139  		gnarkScalars []fr.Element
   140  		gethPoints   []*bls12381.PointG1
   141  		gnarkPoints  []gnark.G1Affine
   142  	)
   143  	// n random scalars (max 17)
   144  	for i := 0; i < 17; i++ {
   145  		// note that geth/crypto/bls12381 works only with scalars <= 32bytes
   146  		s, err := randomScalar(input, fr.Modulus())
   147  		if err != nil {
   148  			break
   149  		}
   150  		// get a random G1 point as basis
   151  		kp1, cp1, err := getG1Points(input)
   152  		if err != nil {
   153  			break
   154  		}
   155  		gethScalars = append(gethScalars, s)
   156  		var gnarkScalar = &fr.Element{}
   157  		gnarkScalar = gnarkScalar.SetBigInt(s).FromMont()
   158  		gnarkScalars = append(gnarkScalars, *gnarkScalar)
   159  
   160  		gethPoints = append(gethPoints, new(bls12381.PointG1).Set(kp1))
   161  		gnarkPoints = append(gnarkPoints, *cp1)
   162  	}
   163  	if len(gethScalars) == 0 {
   164  		return 0
   165  	}
   166  	// compute multi exponentiation
   167  	g1 := bls12381.NewG1()
   168  	kp := bls12381.PointG1{}
   169  	if _, err := g1.MultiExp(&kp, gethPoints, gethScalars); err != nil {
   170  		panic(fmt.Sprintf("G1 multi exponentiation errored (geth): %v", err))
   171  	}
   172  	// note that geth/crypto/bls12381.MultiExp mutates the scalars slice (and sets all the scalars to zero)
   173  
   174  	// gnark multi exp
   175  	cp := new(gnark.G1Affine)
   176  	cp.MultiExp(gnarkPoints, gnarkScalars)
   177  
   178  	// compare result
   179  	if !(bytes.Equal(cp.Marshal(), g1.ToBytes(&kp))) {
   180  		panic("G1 multi exponentiation mismatch gnark / geth ")
   181  	}
   182  
   183  	return 1
   184  }
   185  
   186  func getG1Points(input io.Reader) (*bls12381.PointG1, *gnark.G1Affine, error) {
   187  	// sample a random scalar
   188  	s, err := randomScalar(input, fp.Modulus())
   189  	if err != nil {
   190  		return nil, nil, err
   191  	}
   192  
   193  	// compute a random point
   194  	cp := new(gnark.G1Affine)
   195  	_, _, g1Gen, _ := gnark.Generators()
   196  	cp.ScalarMultiplication(&g1Gen, s)
   197  	cpBytes := cp.Marshal()
   198  
   199  	// marshal gnark point -> geth point
   200  	g1 := bls12381.NewG1()
   201  	kp, err := g1.FromBytes(cpBytes)
   202  	if err != nil {
   203  		panic(fmt.Sprintf("Could not marshal gnark.G1 -> geth.G1: %v", err))
   204  	}
   205  	if !bytes.Equal(g1.ToBytes(kp), cpBytes) {
   206  		panic("bytes(gnark.G1) != bytes(geth.G1)")
   207  	}
   208  
   209  	return kp, cp, nil
   210  }
   211  
   212  func getG2Points(input io.Reader) (*bls12381.PointG2, *gnark.G2Affine, error) {
   213  	// sample a random scalar
   214  	s, err := randomScalar(input, fp.Modulus())
   215  	if err != nil {
   216  		return nil, nil, err
   217  	}
   218  
   219  	// compute a random point
   220  	cp := new(gnark.G2Affine)
   221  	_, _, _, g2Gen := gnark.Generators()
   222  	cp.ScalarMultiplication(&g2Gen, s)
   223  	cpBytes := cp.Marshal()
   224  
   225  	// marshal gnark point -> geth point
   226  	g2 := bls12381.NewG2()
   227  	kp, err := g2.FromBytes(cpBytes)
   228  	if err != nil {
   229  		panic(fmt.Sprintf("Could not marshal gnark.G2 -> geth.G2: %v", err))
   230  	}
   231  	if !bytes.Equal(g2.ToBytes(kp), cpBytes) {
   232  		panic("bytes(gnark.G2) != bytes(geth.G2)")
   233  	}
   234  
   235  	return kp, cp, nil
   236  }
   237  
   238  func randomScalar(r io.Reader, max *big.Int) (k *big.Int, err error) {
   239  	for {
   240  		k, err = rand.Int(r, max)
   241  		if err != nil || k.Sign() > 0 {
   242  			return
   243  		}
   244  	}
   245  }