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 }