From c02b2b0102a9c35dfa44f023f1550f0fdcffa7c1 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Tue, 17 Jun 2025 23:02:36 +0200 Subject: [PATCH] tests/fuzzers: added bn marshaling fuzzers #32053 Adds marshaling fuzzing for G1 and G2 to oss-fuzz. Also aligns the behavior of the google library to that of gnark and cloudflare, which only ever read the first 64 / 128 bytes of the input, regardless of how long the input is --- crypto/bn256/google/bn256.go | 4 +-- tests/fuzzers/bn256/bn256_fuzz.go | 57 +++++++++++++++++++++++++++++-- tests/fuzzers/bn256/bn256_test.go | 49 ++++++++++++++++++++++++++ 3 files changed, 105 insertions(+), 5 deletions(-) create mode 100644 tests/fuzzers/bn256/bn256_test.go diff --git a/crypto/bn256/google/bn256.go b/crypto/bn256/google/bn256.go index aca9cf62de1..e427b8bf42a 100644 --- a/crypto/bn256/google/bn256.go +++ b/crypto/bn256/google/bn256.go @@ -128,7 +128,7 @@ func (e *G1) Marshal() []byte { func (e *G1) Unmarshal(m []byte) ([]byte, error) { // Each value is a 256-bit number. const numBytes = 256 / 8 - if len(m) != 2*numBytes { + if len(m) < 2*numBytes { return nil, errors.New("bn256: not enough data") } // Unmarshal the points and check their caps @@ -253,7 +253,7 @@ func (n *G2) Marshal() []byte { func (e *G2) Unmarshal(m []byte) ([]byte, error) { // Each value is a 256-bit number. const numBytes = 256 / 8 - if len(m) != 4*numBytes { + if len(m) < 4*numBytes { return nil, errors.New("bn256: not enough data") } // Unmarshal the points and check their caps diff --git a/tests/fuzzers/bn256/bn256_fuzz.go b/tests/fuzzers/bn256/bn256_fuzz.go index b96e3bb8cc8..dac80f6b133 100644 --- a/tests/fuzzers/bn256/bn256_fuzz.go +++ b/tests/fuzzers/bn256/bn256_fuzz.go @@ -14,9 +14,6 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -//go:build gofuzz -// +build gofuzz - package bn256 import ( @@ -164,6 +161,60 @@ func fuzzPair(data []byte) int { return 1 } +func fuzzUnmarshalG1(input []byte) int { + rc := new(cloudflare.G1) + _, errC := rc.Unmarshal(input) + + rg := new(google.G1) + _, errG := rg.Unmarshal(input) + + rs := new(gnark.G1) + _, errS := rs.Unmarshal(input) + + if errC != nil && errG != nil && errS != nil { + return 0 // bad input + } + if errC == nil && errG == nil && errS == nil { + //make sure we unmarshalled the same points: + if !bytes.Equal(rc.Marshal(), rg.Marshal()) { + panic("marshaling mismatch: cloudflare/google") + } + if !bytes.Equal(rc.Marshal(), rs.Marshal()) { + panic("marshaling mismatch: cloudflare/gnark") + } + return 1 + } else { + panic(fmt.Sprintf("error missmatch: cf: %v g: %v gn: %v", errC, errG, errS)) + } +} + +func fuzzUnmarshalG2(input []byte) int { + rc := new(cloudflare.G2) + _, errC := rc.Unmarshal(input) + + rg := new(google.G2) + _, errG := rg.Unmarshal(input) + + rs := new(gnark.G2) + _, errS := rs.Unmarshal(input) + + if errC != nil && errG != nil && errS != nil { + return 0 // bad input + } + if errC == nil && errG == nil && errS == nil { + //make sure we unmarshalled the same points: + if !bytes.Equal(rc.Marshal(), rg.Marshal()) { + panic("marshaling mismatch: cloudflare/google") + } + if !bytes.Equal(rc.Marshal(), rs.Marshal()) { + panic("marshaling mismatch: cloudflare/gnark") + } + return 1 + } else { + panic(fmt.Sprintf("error missmatch: cf: %v g: %v gn: %v", errC, errG, errS)) + } +} + // normalizeGTToGnark scales a Cloudflare/Google GT element by `s` // so that it can be compared with a gnark GT point. // diff --git a/tests/fuzzers/bn256/bn256_test.go b/tests/fuzzers/bn256/bn256_test.go new file mode 100644 index 00000000000..80fb850103a --- /dev/null +++ b/tests/fuzzers/bn256/bn256_test.go @@ -0,0 +1,49 @@ +// Copyright 2023 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 . + +package bn256 + +import "testing" + +func FuzzAdd(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + fuzzAdd(data) + }) +} + +func FuzzMul(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + fuzzMul(data) + }) +} + +func FuzzPair(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + fuzzPair(data) + }) +} + +func FuzzUnmarshalG1(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + fuzzUnmarshalG1(data) + }) +} + +func FuzzUnmarshalG2(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + fuzzUnmarshalG2(data) + }) +}