From fe452f240eed5718b4e9813ad74446d5aa6d1ee3 Mon Sep 17 00:00:00 2001 From: Kilian <79536516+K1li4nL@users.noreply.github.com> Date: Wed, 25 Sep 2024 11:36:44 +0200 Subject: [PATCH] Remove BLS signatures aggregate (#540) * Remove all aggregation code from BLS * Update BLS documentation * Move aggregation bench to BDN --- benchmark/benchmark.go | 6 +- pairing/bls12381/bls12381_test.go | 43 +++++-- pairing/bn256/bls_test.go | 28 ---- sign/bdn/bdn.go | 2 +- sign/bdn/bdn_test.go | 6 +- sign/bls/bls.go | 83 +----------- sign/bls/bls_test.go | 206 ------------------------------ util/test/benchmark.go | 21 +-- 8 files changed, 55 insertions(+), 340 deletions(-) diff --git a/benchmark/benchmark.go b/benchmark/benchmark.go index b76725c55..9747de1d8 100644 --- a/benchmark/benchmark.go +++ b/benchmark/benchmark.go @@ -146,7 +146,7 @@ func benchmarkSign(sigType string) map[string]interface{} { // Signing for _, i := range keys { results["sign"][fmt.Sprintf("%d", i)] = testing.Benchmark(func(b *testing.B) { - _, scheme, _, privates, _, _ := test.PrepareBLS(i) + scheme, _, privates, _, _ := test.PrepareBLS(i) test.BenchSign(b, scheme, benchMessage, privates) }) } @@ -154,8 +154,8 @@ func benchmarkSign(sigType string) map[string]interface{} { // Verification for _, i := range keys { results["verify"][fmt.Sprintf("%d", i)] = testing.Benchmark(func(b *testing.B) { - suite, scheme, publics, _, msgs, sigs := test.PrepareBLS(i) - test.BLSBenchVerify(b, sigs, scheme, suite, publics, msgs) + scheme, publics, _, msgs, sigs := test.PrepareBLS(i) + test.BLSBenchVerify(b, sigs, scheme, publics, msgs) }) } } diff --git a/pairing/bls12381/bls12381_test.go b/pairing/bls12381/bls12381_test.go index 74d70537b..790f929fa 100644 --- a/pairing/bls12381/bls12381_test.go +++ b/pairing/bls12381/bls12381_test.go @@ -19,6 +19,7 @@ import ( "go.dedis.ch/kyber/v4/pairing" circl "go.dedis.ch/kyber/v4/pairing/bls12381/circl" kilic "go.dedis.ch/kyber/v4/pairing/bls12381/kilic" + "go.dedis.ch/kyber/v4/sign/bdn" "go.dedis.ch/kyber/v4/sign/bls" "go.dedis.ch/kyber/v4/sign/tbls" "go.dedis.ch/kyber/v4/util/random" @@ -665,14 +666,15 @@ var ( var result interface{} func BenchmarkKilic(b *testing.B) { - BLSBenchmark(b, "kilic") + BDNBenchmark(b, "kilic") } func BenchmarkCircl(b *testing.B) { - BLSBenchmark(b, "circl") + BDNBenchmark(b, "circl") } -func BLSBenchmark(b *testing.B, curveOption string) { +//nolint: gocyclo,cyclop // breaking this down doesn't make sense +func BDNBenchmark(b *testing.B, curveOption string) { b.Logf("----------------------") b.Logf("Payload to sign: %d bytes\n", dataSize) b.Logf("Numbers of signatures: %v\n", numSigs) @@ -700,8 +702,8 @@ func BLSBenchmark(b *testing.B, curveOption string) { panic(fmt.Errorf("invalid curve option: %s", curveOption)) } - schemeOnG1 := bls.NewSchemeOnG1(suite) - schemeOnG2 := bls.NewSchemeOnG2(suite) + schemeOnG1 := bdn.NewSchemeOnG1(suite) + schemeOnG2 := bdn.NewSchemeOnG2(suite) maxN := 1 for _, s := range numSigs { @@ -730,31 +732,52 @@ func BLSBenchmark(b *testing.B, curveOption string) { } } + // Prepare masks for aggregation + maskG1, err := bdn.NewMask(suite.G1(), pubKeysOnG1, nil) + if err != nil { + panic(err) + } + maskG2, err := bdn.NewMask(suite.G2(), pubKeysOnG2, nil) + if err != nil { + panic(err) + } + for _, n := range numSigs { + for i := 0; i < n; i++ { + maskG1.SetBit(i, true) + maskG2.SetBit(i, true) + } + // Benchmark aggregation of public keys b.Run(fmt.Sprintf("AggregatePublicKeys-G1 on %d signs", n), func(bb *testing.B) { for j := 0; j < bb.N; j++ { - result = schemeOnG1.AggregatePublicKeys(pubKeysOnG1[:n]...) + result, err = schemeOnG1.AggregatePublicKeys(maskG1) + if err != nil { + require.NoError(b, err) + } } }) b.Run(fmt.Sprintf("AggregatePublicKeys-G2 on %d signs", n), func(bb *testing.B) { for j := 0; j < bb.N; j++ { - result = schemeOnG2.AggregatePublicKeys(pubKeysOnG2[:n]...) + result, err = schemeOnG2.AggregatePublicKeys(maskG2) + if err != nil { + panic(err) + } } }) // Benchmark aggregation of signatures b.Run(fmt.Sprintf("AggregateSign-G1 on %d signs", n), func(bb *testing.B) { for j := 0; j < bb.N; j++ { - result, err = schemeOnG1.AggregateSignatures(sigsOnG1[:n]...) + result, err = schemeOnG1.AggregateSignatures(sigsOnG1[:n], maskG1) if err != nil { panic(err) } } }) - b.Run(fmt.Sprintf("AggregateSign-G1 on %d signs", n), func(bb *testing.B) { + b.Run(fmt.Sprintf("AggregateSign-G2 on %d signs", n), func(bb *testing.B) { for j := 0; j < bb.N; j++ { - result, err = schemeOnG2.AggregateSignatures(sigsOnG2[:n]...) + result, err = schemeOnG2.AggregateSignatures(sigsOnG2[:n], maskG2) if err != nil { panic(err) } diff --git a/pairing/bn256/bls_test.go b/pairing/bn256/bls_test.go index 83d9d8826..75b542cc2 100644 --- a/pairing/bn256/bls_test.go +++ b/pairing/bn256/bls_test.go @@ -3,10 +3,8 @@ package bn256 import ( "testing" - "github.com/stretchr/testify/require" "go.dedis.ch/kyber/v4/internal/test" "go.dedis.ch/kyber/v4/sign/bls" - "go.dedis.ch/kyber/v4/util/random" ) func TestBLSSchemeBN256G1(t *testing.T) { @@ -14,29 +12,3 @@ func TestBLSSchemeBN256G1(t *testing.T) { s := bls.NewSchemeOnG1(suite) test.SchemeTesting(t, s) } - -func TestBinaryMarshalAfterAggregation_issue400(t *testing.T) { - suite := NewSuite() - s := bls.NewSchemeOnG1(suite) - _, public1 := s.NewKeyPair(random.New()) - _, public2 := s.NewKeyPair(random.New()) - - workingKey := s.AggregatePublicKeys(public1, public2, public1) - - workingBits, err := workingKey.MarshalBinary() - require.Nil(t, err) - - workingPoint := suite.G2().Point() - err = workingPoint.UnmarshalBinary(workingBits) - require.Nil(t, err) - - // this was failing before the fix - aggregatedKey := s.AggregatePublicKeys(public1, public1, public2) - - bits, err := aggregatedKey.MarshalBinary() - require.Nil(t, err) - - point := suite.G2().Point() - err = point.UnmarshalBinary(bits) - require.Nil(t, err) -} diff --git a/sign/bdn/bdn.go b/sign/bdn/bdn.go index 141a0b708..c62be5e8a 100644 --- a/sign/bdn/bdn.go +++ b/sign/bdn/bdn.go @@ -63,7 +63,7 @@ func hashPointToR(group kyber.Group, pubs []kyber.Point) ([]kyber.Scalar, error) } type Scheme struct { - blsScheme sign.AggregatableScheme + blsScheme sign.Scheme sigGroup kyber.Group keyGroup kyber.Group pairing func(signature, public, hashedPoint kyber.Point) bool diff --git a/sign/bdn/bdn_test.go b/sign/bdn/bdn_test.go index 64eec5719..94082a6fe 100644 --- a/sign/bdn/bdn_test.go +++ b/sign/bdn/bdn_test.go @@ -124,15 +124,11 @@ func TestBDN_RogueAttack(t *testing.T) { sig, err := Sign(suite, private2, msg) require.NoError(t, err) - // Old scheme not resistant to the attack - agg := scheme.AggregatePublicKeys(pubs...) - require.NoError(t, scheme.Verify(agg, msg, sig)) - // New scheme that should detect mask, _ := NewMask(suite, pubs, nil) mask.SetBit(0, true) mask.SetBit(1, true) - agg, err = AggregatePublicKeys(suite, mask) + agg, err := AggregatePublicKeys(suite, mask) require.NoError(t, err) require.Error(t, Verify(suite, agg, msg, sig)) } diff --git a/sign/bls/bls.go b/sign/bls/bls.go index b08cfa6bb..6d865adef 100644 --- a/sign/bls/bls.go +++ b/sign/bls/bls.go @@ -2,18 +2,18 @@ // was introduced in the paper "Short Signatures from the Weil Pairing". BLS // requires pairing-based cryptography. // -// Deprecated: This version is vulnerable to rogue public-key attack and the -// new version of the protocol should be used to make sure a signature +// When using aggregated signatures, this version is vulnerable to rogue +// public-key attack. +// The `sign/bdn` package should be used to make sure a signature // aggregate cannot be verified by a forged key. You can find the protocol // in kyber/sign/bdn. Note that only the aggregation is broken against the -// attack and a later version will merge bls and asmbls. +// attack and for that reason, the code performing aggregation was removed. // // See the paper: https://crypto.stanford.edu/~dabo/pubs/papers/BLSmultisig.html package bls import ( "crypto/cipher" - "crypto/sha256" "errors" "fmt" @@ -30,7 +30,7 @@ type scheme struct { // NewSchemeOnG1 returns a sign.Scheme that uses G1 for its signature space and G2 // for its public keys -func NewSchemeOnG1(suite pairing.Suite) sign.AggregatableScheme { +func NewSchemeOnG1(suite pairing.Suite) sign.Scheme { sigGroup := suite.G1() keyGroup := suite.G2() pairing := func(public, hashedMsg, sigPoint kyber.Point) bool { @@ -45,7 +45,7 @@ func NewSchemeOnG1(suite pairing.Suite) sign.AggregatableScheme { // NewSchemeOnG2 returns a sign.Scheme that uses G2 for its signature space and // G1 for its public key -func NewSchemeOnG2(suite pairing.Suite) sign.AggregatableScheme { +func NewSchemeOnG2(suite pairing.Suite) sign.Scheme { sigGroup := suite.G2() keyGroup := suite.G1() pairing := func(public, hashedMsg, sigPoint kyber.Point) bool { @@ -94,74 +94,3 @@ func (s *scheme) Verify(X kyber.Point, msg, sig []byte) error { } return nil } - -func (s *scheme) AggregateSignatures(sigs ...[]byte) ([]byte, error) { - sig := s.sigGroup.Point() - for _, sigBytes := range sigs { - sigToAdd := s.sigGroup.Point() - if err := sigToAdd.UnmarshalBinary(sigBytes); err != nil { - return nil, err - } - sig.Add(sig, sigToAdd) - } - return sig.MarshalBinary() -} - -func (s *scheme) AggregatePublicKeys(Xs ...kyber.Point) kyber.Point { - aggregated := s.keyGroup.Point() - for _, X := range Xs { - aggregated.Add(aggregated, X) - } - return aggregated -} - -// BatchVerify verifies a large number of publicKey/msg pairings with a single aggregated signature. -// Since aggregation is generally much faster than verification, this can be a speed enhancement. -// Benchmarks show a roughly 50% performance increase over individual signature verification -// Every msg must be unique or there is the possibility to accept an invalid signature -// see: https://crypto.stackexchange.com/questions/56288/is-bls-signature-scheme-strongly-unforgeable/56290 -// for a description of why each message must be unique. -func BatchVerify(suite pairing.Suite, publics []kyber.Point, msgs [][]byte, sig []byte) error { - if !distinct(msgs) { - return fmt.Errorf("bls: error, messages must be distinct") - } - - s := suite.G1().Point() - if err := s.UnmarshalBinary(sig); err != nil { - return err - } - - var aggregatedLeft kyber.Point - for i := range msgs { - hashable, ok := suite.G1().Point().(kyber.HashablePoint) - if !ok { - return errors.New("bls: point needs to implement hashablePoint") - } - hm := hashable.Hash(msgs[i]) - pair := suite.Pair(hm, publics[i]) - - if i == 0 { - aggregatedLeft = pair - } else { - aggregatedLeft.Add(aggregatedLeft, pair) - } - } - - right := suite.Pair(s, suite.G2().Point().Base()) - if !aggregatedLeft.Equal(right) { - return errors.New("bls: invalid signature") - } - return nil -} - -func distinct(msgs [][]byte) bool { - m := make(map[[32]byte]bool) - for _, msg := range msgs { - h := sha256.Sum256(msg) - if m[h] { - return false - } - m[h] = true - } - return true -} diff --git a/sign/bls/bls_test.go b/sign/bls/bls_test.go index cd1edcedb..521cb396d 100644 --- a/sign/bls/bls_test.go +++ b/sign/bls/bls_test.go @@ -1,11 +1,9 @@ package bls import ( - "crypto/rand" "testing" "github.com/stretchr/testify/require" - "go.dedis.ch/kyber/v4" "go.dedis.ch/kyber/v4/pairing/bn256" "go.dedis.ch/kyber/v4/util/random" "go.dedis.ch/kyber/v4/xof/blake2xb" @@ -62,116 +60,6 @@ func TestBLSFailKey(t *testing.T) { } } -func TestBLSAggregateSignatures(t *testing.T) { - msg := []byte("Hello Boneh-Lynn-Shacham") - suite := bn256.NewSuite() - scheme := NewSchemeOnG1(suite) - private1, public1 := scheme.NewKeyPair(random.New()) - private2, public2 := scheme.NewKeyPair(random.New()) - sig1, err := scheme.Sign(private1, msg) - require.Nil(t, err) - sig2, err := scheme.Sign(private2, msg) - require.Nil(t, err) - aggregatedSig, err := scheme.AggregateSignatures(sig1, sig2) - require.Nil(t, err) - - aggregatedKey := scheme.AggregatePublicKeys(public1, public2) - - err = scheme.Verify(aggregatedKey, msg, aggregatedSig) - require.Nil(t, err) -} - -func TestBLSFailAggregatedSig(t *testing.T) { - msg := []byte("Hello Boneh-Lynn-Shacham") - suite := bn256.NewSuite() - scheme := NewSchemeOnG1(suite) - private1, public1 := scheme.NewKeyPair(random.New()) - private2, public2 := scheme.NewKeyPair(random.New()) - sig1, err := scheme.Sign(private1, msg) - require.Nil(t, err) - sig2, err := scheme.Sign(private2, msg) - require.Nil(t, err) - aggregatedSig, err := scheme.AggregateSignatures(sig1, sig2) - require.Nil(t, err) - aggregatedKey := scheme.AggregatePublicKeys(public1, public2) - - aggregatedSig[0] ^= 0x01 - if scheme.Verify(aggregatedKey, msg, aggregatedSig) == nil { - t.Fatal("bls: verification succeeded unexpectedly") - } -} -func TestBLSFailAggregatedKey(t *testing.T) { - msg := []byte("Hello Boneh-Lynn-Shacham") - suite := bn256.NewSuite() - scheme := NewSchemeOnG1(suite) - private1, public1 := scheme.NewKeyPair(random.New()) - private2, public2 := scheme.NewKeyPair(random.New()) - _, public3 := scheme.NewKeyPair(random.New()) - sig1, err := scheme.Sign(private1, msg) - require.Nil(t, err) - sig2, err := scheme.Sign(private2, msg) - require.Nil(t, err) - aggregatedSig, err := scheme.AggregateSignatures(sig1, sig2) - require.Nil(t, err) - badAggregatedKey := scheme.AggregatePublicKeys(public1, public2, public3) - - if scheme.Verify(badAggregatedKey, msg, aggregatedSig) == nil { - t.Fatal("bls: verification succeeded unexpectedly") - } -} - -func TestBLSBatchVerify(t *testing.T) { - msg1 := []byte("Hello Boneh-Lynn-Shacham") - msg2 := []byte("Hello Dedis & Boneh-Lynn-Shacham") - suite := bn256.NewSuite() - scheme := NewSchemeOnG1(suite) - private1, public1 := scheme.NewKeyPair(random.New()) - private2, public2 := scheme.NewKeyPair(random.New()) - sig1, err := scheme.Sign(private1, msg1) - require.Nil(t, err) - sig2, err := scheme.Sign(private2, msg2) - require.Nil(t, err) - aggregatedSig, err := scheme.AggregateSignatures(sig1, sig2) - require.Nil(t, err) - - err = BatchVerify(suite, []kyber.Point{public1, public2}, [][]byte{msg1, msg2}, aggregatedSig) - require.Nil(t, err) -} - -func TestBLSFailBatchVerify(t *testing.T) { - msg1 := []byte("Hello Boneh-Lynn-Shacham") - msg2 := []byte("Hello Dedis & Boneh-Lynn-Shacham") - suite := bn256.NewSuite() - scheme := NewSchemeOnG1(suite) - private1, public1 := scheme.NewKeyPair(random.New()) - private2, public2 := scheme.NewKeyPair(random.New()) - sig1, err := scheme.Sign(private1, msg1) - require.Nil(t, err) - sig2, err := scheme.Sign(private2, msg2) - require.Nil(t, err) - - t.Run("fails with a bad signature", func(t *testing.T) { - aggregatedSig, err := scheme.AggregateSignatures(sig1, sig2) - require.Nil(t, err) - msg2[0] ^= 0x01 - if BatchVerify(suite, []kyber.Point{public1, public2}, [][]byte{msg1, msg2}, aggregatedSig) == nil { - t.Fatal("bls: verification succeeded unexpectedly") - } - }) - - t.Run("fails with a duplicate msg", func(t *testing.T) { - private3, public3 := scheme.NewKeyPair(random.New()) - sig3, err := scheme.Sign(private3, msg1) - require.Nil(t, err) - aggregatedSig, err := scheme.AggregateSignatures(sig1, sig2, sig3) - require.Nil(t, err) - - if BatchVerify(suite, []kyber.Point{public1, public2, public3}, [][]byte{msg1, msg2, msg1}, aggregatedSig) == nil { - t.Fatal("bls: verification succeeded unexpectedly") - } - }) -} - func BenchmarkBLSKeyCreation(b *testing.B) { suite := bn256.NewSuite() scheme := NewSchemeOnG1(suite) @@ -192,97 +80,3 @@ func BenchmarkBLSSign(b *testing.B) { require.Nil(b, err) } } - -func BenchmarkBLSAggregateSigs(b *testing.B) { - suite := bn256.NewSuite() - scheme := NewSchemeOnG1(suite) - private1, _ := scheme.NewKeyPair(random.New()) - private2, _ := scheme.NewKeyPair(random.New()) - msg := []byte("Hello many times Boneh-Lynn-Shacham") - sig1, err := scheme.Sign(private1, msg) - require.Nil(b, err) - sig2, err := scheme.Sign(private2, msg) - require.Nil(b, err) - - b.ResetTimer() - for i := 0; i < b.N; i++ { - _, err := scheme.AggregateSignatures(sig1, sig2) - require.Nil(b, err) - } -} - -func BenchmarkBLSVerifyAggregate(b *testing.B) { - suite := bn256.NewSuite() - scheme := NewSchemeOnG1(suite) - private1, public1 := scheme.NewKeyPair(random.New()) - private2, public2 := scheme.NewKeyPair(random.New()) - msg := []byte("Hello many times Boneh-Lynn-Shacham") - sig1, err := scheme.Sign(private1, msg) - require.Nil(b, err) - sig2, err := scheme.Sign(private2, msg) - require.Nil(b, err) - sig, err := scheme.AggregateSignatures(sig1, sig2) - require.Nil(b, err) - key := scheme.AggregatePublicKeys(public1, public2) - b.ResetTimer() - for i := 0; i < b.N; i++ { - err := scheme.Verify(key, msg, sig) - require.Nil(b, err) - } -} - -func BenchmarkBLSVerifyBatchVerify(b *testing.B) { - suite := bn256.NewSuite() - scheme := NewSchemeOnG1(suite) - numSigs := 100 - privates := make([]kyber.Scalar, numSigs) - publics := make([]kyber.Point, numSigs) - msgs := make([][]byte, numSigs) - sigs := make([][]byte, numSigs) - for i := 0; i < numSigs; i++ { - private, public := scheme.NewKeyPair(random.New()) - privates[i] = private - publics[i] = public - msg := make([]byte, 64) - _, err := rand.Read(msg) - require.Nil(b, err) - msgs[i] = msg - sig, err := scheme.Sign(private, msg) - require.Nil(b, err) - sigs[i] = sig - } - - b.ResetTimer() - for i := 0; i < b.N; i++ { - aggregateSig, _ := scheme.AggregateSignatures(sigs...) - err := BatchVerify(suite, publics, msgs, aggregateSig) - require.Nil(b, err) - } -} - -func TestBinaryMarshalAfterAggregation_issue400(t *testing.T) { - suite := bn256.NewSuite() - scheme := NewSchemeOnG1(suite) - - _, public1 := scheme.NewKeyPair(random.New()) - _, public2 := scheme.NewKeyPair(random.New()) - - workingKey := scheme.AggregatePublicKeys(public1, public2, public1) - - workingBits, err := workingKey.MarshalBinary() - require.Nil(t, err) - - workingPoint := suite.G2().Point() - err = workingPoint.UnmarshalBinary(workingBits) - require.Nil(t, err) - - // this was failing before the fix - aggregatedKey := scheme.AggregatePublicKeys(public1, public1, public2) - - bits, err := aggregatedKey.MarshalBinary() - require.Nil(t, err) - - point := suite.G2().Point() - err = point.UnmarshalBinary(bits) - require.Nil(t, err) -} diff --git a/util/test/benchmark.go b/util/test/benchmark.go index a1c1f69d5..39b2c5b4d 100644 --- a/util/test/benchmark.go +++ b/util/test/benchmark.go @@ -12,9 +12,9 @@ import ( "go.dedis.ch/kyber/v4/util/random" ) -func PrepareBLS(numSigs int) (suite *bn256.Suite, scheme sign.AggregatableScheme, +func PrepareBLS(numSigs int) (scheme sign.Scheme, publics []kyber.Point, privates []kyber.Scalar, msgs [][]byte, sigs [][]byte) { - suite = bn256.NewSuite() + suite := bn256.NewSuite() scheme = bls.NewSchemeOnG1(suite) publics = make([]kyber.Point, numSigs) @@ -37,10 +37,10 @@ func PrepareBLS(numSigs int) (suite *bn256.Suite, scheme sign.AggregatableScheme } sigs[i] = sig } - return suite, scheme, publics, privates, msgs, sigs + return scheme, publics, privates, msgs, sigs } -func BenchCreateKeys(b *testing.B, scheme sign.AggregatableScheme, n int) { +func BenchCreateKeys(b *testing.B, scheme sign.Scheme, n int) { b.ResetTimer() for i := 0; i < b.N; i++ { for j := 0; j < n; j++ { @@ -49,7 +49,7 @@ func BenchCreateKeys(b *testing.B, scheme sign.AggregatableScheme, n int) { } } -func BenchSign(b *testing.B, scheme sign.AggregatableScheme, msg []byte, privates []kyber.Scalar) { +func BenchSign(b *testing.B, scheme sign.Scheme, msg []byte, privates []kyber.Scalar) { b.ResetTimer() for i := 0; i < b.N; i++ { for _, private := range privates { @@ -59,12 +59,13 @@ func BenchSign(b *testing.B, scheme sign.AggregatableScheme, msg []byte, private } } -func BLSBenchVerify(b *testing.B, sigs [][]byte, scheme sign.AggregatableScheme, - suite *bn256.Suite, publics []kyber.Point, msgs [][]byte) { +func BLSBenchVerify(b *testing.B, sigs [][]byte, scheme sign.Scheme, + publics []kyber.Point, msgs [][]byte) { b.ResetTimer() for i := 0; i < b.N; i++ { - aggregateSig, _ := scheme.AggregateSignatures(sigs...) - err := bls.BatchVerify(suite, publics, msgs, aggregateSig) - require.NoError(b, err) + for j, p := range publics { + err := scheme.Verify(p, msgs[j], sigs[j]) + require.NoError(b, err) + } } }