mirror of https://github.com/ethereum/go-ethereum
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
410 lines
11 KiB
410 lines
11 KiB
// Copyright 2021 The go-ethereum Authors
|
|
// This file is part of the go-ethereum library.
|
|
//
|
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU Lesser General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU Lesser General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Lesser General Public License
|
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
//go:build cgo
|
|
// +build cgo
|
|
|
|
package bls
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/rand"
|
|
"fmt"
|
|
"io"
|
|
"math/big"
|
|
|
|
"github.com/consensys/gnark-crypto/ecc"
|
|
gnark "github.com/consensys/gnark-crypto/ecc/bls12-381"
|
|
"github.com/consensys/gnark-crypto/ecc/bls12-381/fp"
|
|
"github.com/consensys/gnark-crypto/ecc/bls12-381/fr"
|
|
"github.com/ethereum/go-ethereum/common"
|
|
blst "github.com/supranational/blst/bindings/go"
|
|
)
|
|
|
|
func fuzzG1SubgroupChecks(data []byte) int {
|
|
input := bytes.NewReader(data)
|
|
cpG1, blG1, err := getG1Points(input)
|
|
if err != nil {
|
|
return 0
|
|
}
|
|
inSubGroupGnark := cpG1.IsInSubGroup()
|
|
inSubGroupBLST := blG1.InG1()
|
|
if inSubGroupGnark != inSubGroupBLST {
|
|
panic(fmt.Sprintf("differing subgroup check, gnark %v, blst %v", inSubGroupGnark, inSubGroupBLST))
|
|
}
|
|
return 1
|
|
}
|
|
|
|
func fuzzG2SubgroupChecks(data []byte) int {
|
|
input := bytes.NewReader(data)
|
|
gpG2, blG2, err := getG2Points(input)
|
|
if err != nil {
|
|
return 0
|
|
}
|
|
inSubGroupGnark := gpG2.IsInSubGroup()
|
|
inSubGroupBLST := blG2.InG2()
|
|
if inSubGroupGnark != inSubGroupBLST {
|
|
panic(fmt.Sprintf("differing subgroup check, gnark %v, blst %v", inSubGroupGnark, inSubGroupBLST))
|
|
}
|
|
return 1
|
|
}
|
|
|
|
func fuzzCrossPairing(data []byte) int {
|
|
input := bytes.NewReader(data)
|
|
|
|
// get random G1 points
|
|
cpG1, blG1, err := getG1Points(input)
|
|
if err != nil {
|
|
return 0
|
|
}
|
|
|
|
// get random G2 points
|
|
cpG2, blG2, err := getG2Points(input)
|
|
if err != nil {
|
|
return 0
|
|
}
|
|
|
|
// compute pairing using gnark
|
|
cResult, err := gnark.Pair([]gnark.G1Affine{*cpG1}, []gnark.G2Affine{*cpG2})
|
|
if err != nil {
|
|
panic(fmt.Sprintf("gnark/bls12381 encountered error: %v", err))
|
|
}
|
|
|
|
// compute pairing using blst
|
|
blstResult := blst.Fp12MillerLoop(blG2, blG1)
|
|
blstResult.FinalExp()
|
|
res := massageBLST(blstResult.ToBendian())
|
|
if !(bytes.Equal(res, cResult.Marshal())) {
|
|
panic("pairing mismatch blst / geth")
|
|
}
|
|
|
|
return 1
|
|
}
|
|
|
|
func massageBLST(in []byte) []byte {
|
|
out := make([]byte, len(in))
|
|
len := 12 * 48
|
|
// 1
|
|
copy(out[0:], in[len-1*48:len])
|
|
copy(out[1*48:], in[len-2*48:len-1*48])
|
|
// 2
|
|
copy(out[6*48:], in[len-3*48:len-2*48])
|
|
copy(out[7*48:], in[len-4*48:len-3*48])
|
|
// 3
|
|
copy(out[2*48:], in[len-5*48:len-4*48])
|
|
copy(out[3*48:], in[len-6*48:len-5*48])
|
|
// 4
|
|
copy(out[8*48:], in[len-7*48:len-6*48])
|
|
copy(out[9*48:], in[len-8*48:len-7*48])
|
|
// 5
|
|
copy(out[4*48:], in[len-9*48:len-8*48])
|
|
copy(out[5*48:], in[len-10*48:len-9*48])
|
|
// 6
|
|
copy(out[10*48:], in[len-11*48:len-10*48])
|
|
copy(out[11*48:], in[len-12*48:len-11*48])
|
|
return out
|
|
}
|
|
|
|
func fuzzCrossG1Add(data []byte) int {
|
|
input := bytes.NewReader(data)
|
|
|
|
// get random G1 points
|
|
cp1, bl1, err := getG1Points(input)
|
|
if err != nil {
|
|
return 0
|
|
}
|
|
|
|
// get random G1 points
|
|
cp2, bl2, err := getG1Points(input)
|
|
if err != nil {
|
|
return 0
|
|
}
|
|
|
|
// compute cp = cp1 + cp2
|
|
_cp1 := new(gnark.G1Jac).FromAffine(cp1)
|
|
_cp2 := new(gnark.G1Jac).FromAffine(cp2)
|
|
cp := new(gnark.G1Affine).FromJacobian(_cp1.AddAssign(_cp2))
|
|
|
|
bl3 := blst.P1AffinesAdd([]*blst.P1Affine{bl1, bl2})
|
|
if !(bytes.Equal(cp.Marshal(), bl3.Serialize())) {
|
|
panic("G1 point addition mismatch blst / geth ")
|
|
}
|
|
|
|
return 1
|
|
}
|
|
|
|
func fuzzCrossG2Add(data []byte) int {
|
|
input := bytes.NewReader(data)
|
|
|
|
// get random G2 points
|
|
gp1, bl1, err := getG2Points(input)
|
|
if err != nil {
|
|
return 0
|
|
}
|
|
|
|
// get random G2 points
|
|
gp2, bl2, err := getG2Points(input)
|
|
if err != nil {
|
|
return 0
|
|
}
|
|
|
|
// compute cp = cp1 + cp2
|
|
_gp1 := new(gnark.G2Jac).FromAffine(gp1)
|
|
_gp2 := new(gnark.G2Jac).FromAffine(gp2)
|
|
gp := new(gnark.G2Affine).FromJacobian(_gp1.AddAssign(_gp2))
|
|
|
|
bl3 := blst.P2AffinesAdd([]*blst.P2Affine{bl1, bl2})
|
|
if !(bytes.Equal(gp.Marshal(), bl3.Serialize())) {
|
|
panic("G1 point addition mismatch blst / geth ")
|
|
}
|
|
|
|
return 1
|
|
}
|
|
|
|
func fuzzCrossG1MultiExp(data []byte) int {
|
|
var (
|
|
input = bytes.NewReader(data)
|
|
gnarkScalars []fr.Element
|
|
gnarkPoints []gnark.G1Affine
|
|
blstScalars []*blst.Scalar
|
|
blstPoints []*blst.P1Affine
|
|
)
|
|
// n random scalars (max 17)
|
|
for i := 0; i < 17; i++ {
|
|
// note that geth/crypto/bls12381 works only with scalars <= 32bytes
|
|
s, err := randomScalar(input, fr.Modulus())
|
|
if err != nil {
|
|
break
|
|
}
|
|
// get a random G1 point as basis
|
|
cp1, bl1, err := getG1Points(input)
|
|
if err != nil {
|
|
break
|
|
}
|
|
|
|
gnarkScalar := new(fr.Element).SetBigInt(s)
|
|
gnarkScalars = append(gnarkScalars, *gnarkScalar)
|
|
gnarkPoints = append(gnarkPoints, *cp1)
|
|
|
|
blstScalar := new(blst.Scalar).FromBEndian(common.LeftPadBytes(s.Bytes(), 32))
|
|
blstScalars = append(blstScalars, blstScalar)
|
|
blstPoints = append(blstPoints, bl1)
|
|
}
|
|
|
|
if len(gnarkScalars) == 0 || len(gnarkScalars) != len(gnarkPoints) {
|
|
return 0
|
|
}
|
|
|
|
// gnark multi exp
|
|
cp := new(gnark.G1Affine)
|
|
cp.MultiExp(gnarkPoints, gnarkScalars, ecc.MultiExpConfig{})
|
|
|
|
expectedGnark := multiExpG1Gnark(gnarkPoints, gnarkScalars)
|
|
if !bytes.Equal(cp.Marshal(), expectedGnark.Marshal()) {
|
|
panic("g1 multi exponentiation mismatch")
|
|
}
|
|
|
|
// blst multi exp
|
|
expectedBlst := blst.P1AffinesMult(blstPoints, blstScalars, 256).ToAffine()
|
|
if !bytes.Equal(cp.Marshal(), expectedBlst.Serialize()) {
|
|
panic("g1 multi exponentiation mismatch, gnark/blst")
|
|
}
|
|
return 1
|
|
}
|
|
|
|
func fuzzCrossG1Mul(data []byte) int {
|
|
input := bytes.NewReader(data)
|
|
gp, blpAffine, err := getG1Points(input)
|
|
if err != nil {
|
|
return 0
|
|
}
|
|
scalar, err := randomScalar(input, fp.Modulus())
|
|
if err != nil {
|
|
return 0
|
|
}
|
|
|
|
blScalar := new(blst.Scalar).FromBEndian(common.LeftPadBytes(scalar.Bytes(), 32))
|
|
|
|
blp := new(blst.P1)
|
|
blp.FromAffine(blpAffine)
|
|
|
|
resBl := blp.Mult(blScalar)
|
|
resGeth := (new(gnark.G1Affine)).ScalarMultiplication(gp, scalar)
|
|
|
|
if !bytes.Equal(resGeth.Marshal(), resBl.Serialize()) {
|
|
panic("bytes(blst.G1) != bytes(geth.G1)")
|
|
}
|
|
return 1
|
|
}
|
|
|
|
func fuzzCrossG2Mul(data []byte) int {
|
|
input := bytes.NewReader(data)
|
|
gp, blpAffine, err := getG2Points(input)
|
|
if err != nil {
|
|
return 0
|
|
}
|
|
scalar, err := randomScalar(input, fp.Modulus())
|
|
if err != nil {
|
|
return 0
|
|
}
|
|
|
|
blScalar := new(blst.Scalar).FromBEndian(common.LeftPadBytes(scalar.Bytes(), 32))
|
|
|
|
blp := new(blst.P2)
|
|
blp.FromAffine(blpAffine)
|
|
|
|
resBl := blp.Mult(blScalar)
|
|
resGeth := (new(gnark.G2Affine)).ScalarMultiplication(gp, scalar)
|
|
|
|
if !bytes.Equal(resGeth.Marshal(), resBl.Serialize()) {
|
|
panic("bytes(blst.G1) != bytes(geth.G1)")
|
|
}
|
|
return 1
|
|
}
|
|
|
|
func fuzzCrossG2MultiExp(data []byte) int {
|
|
var (
|
|
input = bytes.NewReader(data)
|
|
gnarkScalars []fr.Element
|
|
gnarkPoints []gnark.G2Affine
|
|
blstScalars []*blst.Scalar
|
|
blstPoints []*blst.P2Affine
|
|
)
|
|
// n random scalars (max 17)
|
|
for i := 0; i < 17; i++ {
|
|
// note that geth/crypto/bls12381 works only with scalars <= 32bytes
|
|
s, err := randomScalar(input, fr.Modulus())
|
|
if err != nil {
|
|
break
|
|
}
|
|
// get a random G1 point as basis
|
|
cp1, bl1, err := getG2Points(input)
|
|
if err != nil {
|
|
break
|
|
}
|
|
|
|
gnarkScalar := new(fr.Element).SetBigInt(s)
|
|
gnarkScalars = append(gnarkScalars, *gnarkScalar)
|
|
gnarkPoints = append(gnarkPoints, *cp1)
|
|
|
|
blstScalar := new(blst.Scalar).FromBEndian(common.LeftPadBytes(s.Bytes(), 32))
|
|
blstScalars = append(blstScalars, blstScalar)
|
|
blstPoints = append(blstPoints, bl1)
|
|
}
|
|
|
|
if len(gnarkScalars) == 0 || len(gnarkScalars) != len(gnarkPoints) {
|
|
return 0
|
|
}
|
|
|
|
// gnark multi exp
|
|
cp := new(gnark.G2Affine)
|
|
cp.MultiExp(gnarkPoints, gnarkScalars, ecc.MultiExpConfig{})
|
|
|
|
expectedGnark := multiExpG2Gnark(gnarkPoints, gnarkScalars)
|
|
if !bytes.Equal(cp.Marshal(), expectedGnark.Marshal()) {
|
|
panic("g1 multi exponentiation mismatch")
|
|
}
|
|
|
|
// blst multi exp
|
|
expectedBlst := blst.P2AffinesMult(blstPoints, blstScalars, 256).ToAffine()
|
|
if !bytes.Equal(cp.Marshal(), expectedBlst.Serialize()) {
|
|
panic("g1 multi exponentiation mismatch, gnark/blst")
|
|
}
|
|
return 1
|
|
}
|
|
|
|
func getG1Points(input io.Reader) (*gnark.G1Affine, *blst.P1Affine, error) {
|
|
// sample a random scalar
|
|
s, err := randomScalar(input, fp.Modulus())
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
// compute a random point
|
|
cp := new(gnark.G1Affine)
|
|
_, _, g1Gen, _ := gnark.Generators()
|
|
cp.ScalarMultiplication(&g1Gen, s)
|
|
cpBytes := cp.Marshal()
|
|
|
|
// marshal gnark point -> blst point
|
|
scalar := new(blst.Scalar).FromBEndian(common.LeftPadBytes(s.Bytes(), 32))
|
|
p1 := new(blst.P1Affine).From(scalar)
|
|
blstRes := p1.Serialize()
|
|
if !bytes.Equal(blstRes, cpBytes) {
|
|
panic(fmt.Sprintf("bytes(blst.G1) != bytes(geth.G1)\nblst.G1: %x\ngeth.G1: %x\n", blstRes, cpBytes))
|
|
}
|
|
|
|
return cp, p1, nil
|
|
}
|
|
|
|
func getG2Points(input io.Reader) (*gnark.G2Affine, *blst.P2Affine, error) {
|
|
// sample a random scalar
|
|
s, err := randomScalar(input, fp.Modulus())
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
// compute a random point
|
|
gp := new(gnark.G2Affine)
|
|
_, _, _, g2Gen := gnark.Generators()
|
|
gp.ScalarMultiplication(&g2Gen, s)
|
|
cpBytes := gp.Marshal()
|
|
|
|
// marshal gnark point -> blst point
|
|
// Left pad the scalar to 32 bytes
|
|
scalar := new(blst.Scalar).FromBEndian(common.LeftPadBytes(s.Bytes(), 32))
|
|
p2 := new(blst.P2Affine).From(scalar)
|
|
if !bytes.Equal(p2.Serialize(), cpBytes) {
|
|
panic("bytes(blst.G2) != bytes(bls12381.G2)")
|
|
}
|
|
|
|
return gp, p2, nil
|
|
}
|
|
|
|
func randomScalar(r io.Reader, max *big.Int) (k *big.Int, err error) {
|
|
for {
|
|
k, err = rand.Int(r, max)
|
|
if err != nil || k.Sign() > 0 {
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
// multiExpG1Gnark is a naive implementation of G1 multi-exponentiation
|
|
func multiExpG1Gnark(gs []gnark.G1Affine, scalars []fr.Element) gnark.G1Affine {
|
|
res := gnark.G1Affine{}
|
|
for i := 0; i < len(gs); i++ {
|
|
tmp := new(gnark.G1Affine)
|
|
sb := scalars[i].Bytes()
|
|
scalarBytes := new(big.Int).SetBytes(sb[:])
|
|
tmp.ScalarMultiplication(&gs[i], scalarBytes)
|
|
res.Add(&res, tmp)
|
|
}
|
|
return res
|
|
}
|
|
|
|
// multiExpG1Gnark is a naive implementation of G1 multi-exponentiation
|
|
func multiExpG2Gnark(gs []gnark.G2Affine, scalars []fr.Element) gnark.G2Affine {
|
|
res := gnark.G2Affine{}
|
|
for i := 0; i < len(gs); i++ {
|
|
tmp := new(gnark.G2Affine)
|
|
sb := scalars[i].Bytes()
|
|
scalarBytes := new(big.Int).SetBytes(sb[:])
|
|
tmp.ScalarMultiplication(&gs[i], scalarBytes)
|
|
res.Add(&res, tmp)
|
|
}
|
|
return res
|
|
}
|
|
|