Skip to content

Commit

Permalink
Remove BLS signatures aggregate (#540)
Browse files Browse the repository at this point in the history
* Remove all aggregation code from BLS

* Update BLS documentation

* Move aggregation bench to BDN
  • Loading branch information
K1li4nL authored Sep 25, 2024
1 parent 4de3374 commit fe452f2
Show file tree
Hide file tree
Showing 8 changed files with 55 additions and 340 deletions.
6 changes: 3 additions & 3 deletions benchmark/benchmark.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,16 +146,16 @@ 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)
})
}

// 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)
})
}
}
Expand Down
43 changes: 33 additions & 10 deletions pairing/bls12381/bls12381_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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)
}
Expand Down
28 changes: 0 additions & 28 deletions pairing/bn256/bls_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,40 +3,12 @@ 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) {
suite := NewSuite()
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)
}
2 changes: 1 addition & 1 deletion sign/bdn/bdn.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 1 addition & 5 deletions sign/bdn/bdn_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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))
}
Expand Down
83 changes: 6 additions & 77 deletions sign/bls/bls.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand All @@ -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 {
Expand All @@ -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 {
Expand Down Expand Up @@ -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
}
Loading

0 comments on commit fe452f2

Please sign in to comment.