Skip to content
Snippets Groups Projects
Commit 864405f8 authored by lunfardo314's avatar lunfardo314
Browse files

added BLS signature aggregation added

parent 3535036a
No related branches found
No related tags found
No related merge requests found
......@@ -19,7 +19,7 @@ type Address [Length]byte
const (
// every signature scheme has a version byte associated to it.
VERSION_ED25519 = byte(1)
VERSION_BLS_BDN = byte(2)
VERSION_BLS = byte(2)
)
// Random creates a random address, which can for example be used in unit tests.
......@@ -70,7 +70,7 @@ func FromED25519PubKey(key ed25119.PublicKey) (address Address) {
func FromBLSPubKey(pubKey []byte) (address Address) {
digest := blake2b.Sum256(pubKey)
address[0] = VERSION_BLS_BDN
address[0] = VERSION_BLS
copy(address[1:], digest[:])
return
......
package signaturescheme
import (
"bytes"
"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address"
"go.dedis.ch/kyber/v3"
"go.dedis.ch/kyber/v3/pairing/bn256"
"go.dedis.ch/kyber/v3/sign/bdn"
"go.dedis.ch/kyber/v3/util/random"
)
// BLS_BDN implements BLS signature scheme which is robust agains rogue public key attacks
// it uses go.dedis/kyber library
// more info https://github.com/dedis/kyber/blob/master/sign/bdn/bdn.go
// usually BLS signatures are used as threshold signatures.
// This package don't implement any threshold signature related primitives.
// it only implements what is needed for the node to check validity of the BLS signatures against addresses
// and also minimum signing required for testing
var suite = bn256.NewSuite()
// blsBdnSignatureScheme defines an interface for the BLS signatures.
// here it is unmarshaled because only used for testing
type blsBdnSignatureScheme struct {
privKey kyber.Scalar
pubKey kyber.Point
}
// RandBLS_BDN creates an RANDOM instance of a signature scheme, that is used to sign the corresponding address.
// mostly intended for testing.
func RandBLS_BDN() SignatureScheme {
ret := &blsBdnSignatureScheme{}
ret.privKey, ret.pubKey = bdn.NewKeyPair(suite, random.New()) // only for testing: deterministic!
return ret
}
func (sigscheme *blsBdnSignatureScheme) Version() byte {
return address.VERSION_BLS_BDN
}
func (sigscheme *blsBdnSignatureScheme) Address() address.Address {
b, err := sigscheme.pubKey.MarshalBinary()
if err != nil {
panic(err)
}
return address.FromBLSPubKey(b)
}
func (sigscheme *blsBdnSignatureScheme) Sign(data []byte) Signature {
sig, err := bdn.Sign(suite, sigscheme.privKey, data)
if err != nil {
panic(err)
}
pubKeyBin, err := sigscheme.pubKey.MarshalBinary()
if err != nil {
panic(err)
}
ret := &blsBdnSignature{}
copy(ret.pubKey[:], pubKeyBin)
copy(ret.signature[:], sig)
return ret
}
// interface contract (allow the compiler to check if the implementation has all of the required methods).
var _ SignatureScheme = &blsBdnSignatureScheme{}
// BLS-BDN signature, in binary marshaled form
const (
BLS_SIGNATURE_SIZE = 64
BLS_PUBLIC_KEY_SIZE = 128
)
type blsBdnSignature struct {
pubKey [BLS_PUBLIC_KEY_SIZE]byte
signature [BLS_SIGNATURE_SIZE]byte
}
func (sig *blsBdnSignature) IsValid(signedData []byte) bool {
// unmarshal public key
pubKey := suite.G2().Point()
if err := pubKey.UnmarshalBinary(sig.pubKey[:]); err != nil {
return false
}
return bdn.Verify(suite, pubKey, signedData, sig.signature[:]) == nil
}
func (sig *blsBdnSignature) Bytes() []byte {
b := make([]byte, 1+BLS_PUBLIC_KEY_SIZE+BLS_SIGNATURE_SIZE)
buf := bytes.NewBuffer(b)
buf.WriteByte(address.VERSION_BLS_BDN)
buf.Write(sig.pubKey[:])
buf.Write(sig.signature[:])
return b
}
func (sig *blsBdnSignature) Address() address.Address {
return address.FromBLSPubKey(sig.pubKey[:])
}
// interface contract (allow the compiler to check if the implementation has all of the required methods).
var _ Signature = &blsBdnSignature{}
package signaturescheme
import (
"fmt"
"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address"
"github.com/mr-tron/base58"
"go.dedis.ch/kyber/v3"
"go.dedis.ch/kyber/v3/pairing/bn256"
"go.dedis.ch/kyber/v3/sign"
"go.dedis.ch/kyber/v3/sign/bdn"
"go.dedis.ch/kyber/v3/util/random"
"math/rand"
)
// BLS implements BLS signature scheme which is robust against rogue public key attacks, or BDN
// it uses go.dedis/kyber library
// more info https://github.com/dedis/kyber/blob/master/sign/bdn/bdn.go
// usually BLS signatures are used as threshold signatures.
// This package doesn't implement any threshold signature related primitives.
// it only contains what is needed for the node to check validity of the BLS signatures against addresses
// and also minimum signing required for testing
var suite = bn256.NewSuite()
const (
BLS_SIGNATURE_SIZE = 64
BLS_PUBLIC_KEY_SIZE = 128
BLS_PRIVATE_KEY_SIZE = 32
)
// ---------------- implements SignatureScheme interface
// blsSignatureScheme defines an interface for the key pairs of BLS signatures.
type blsSignatureScheme struct {
priKey kyber.Scalar
pubKey kyber.Point
}
// deterministic sequence
var rnd = random.New(rand.New(rand.NewSource(42)))
// RandBLS creates a RANDOM instance of a signature scheme, that is used to sign the corresponding address.
// mostly intended for testing.
// only for testing: each time same sequence!
func RandBLS() SignatureScheme {
ret := &blsSignatureScheme{}
ret.priKey, ret.pubKey = bdn.NewKeyPair(suite, rnd)
return ret
}
// BLS creates an instance of BLS signature scheme
// from given private and public keys in marshaled binary form
func BLS(priKey, pubKey []byte) (SignatureScheme, error) {
if len(priKey) != BLS_PRIVATE_KEY_SIZE || len(pubKey) != BLS_PUBLIC_KEY_SIZE {
return nil, fmt.Errorf("wrong key size")
}
ret := &blsSignatureScheme{
priKey: suite.G2().Scalar(),
pubKey: suite.G2().Point(),
}
if err := ret.pubKey.UnmarshalBinary(pubKey); err != nil {
return nil, err
}
if err := ret.priKey.UnmarshalBinary(priKey); err != nil {
return nil, err
}
return ret, nil
}
func (sigscheme *blsSignatureScheme) Version() byte {
return address.VERSION_BLS
}
func (sigscheme *blsSignatureScheme) Address() address.Address {
b, err := sigscheme.pubKey.MarshalBinary()
if err != nil {
panic(err)
}
return address.FromBLSPubKey(b)
}
func (sigscheme *blsSignatureScheme) Sign(data []byte) Signature {
sig, err := bdn.Sign(suite, sigscheme.priKey, data)
if err != nil {
panic(err)
}
pubKeyBin, err := sigscheme.pubKey.MarshalBinary()
if err != nil {
panic(err)
}
return newBLSSignature(pubKeyBin, sig)
}
func (sigscheme *blsSignatureScheme) String() string {
pri, err := sigscheme.priKey.MarshalBinary()
if err != nil {
return fmt.Sprintf("BLS sigsheme: %v", err)
}
pub, err := sigscheme.pubKey.MarshalBinary()
if err != nil {
return fmt.Sprintf("BLS sigsheme: %v", err)
}
return base58.Encode(pri) + ", " + base58.Encode(pub)
}
// interface contract (allow the compiler to check if the implementation has all of the required methods).
var _ SignatureScheme = &blsSignatureScheme{}
// ---------------- implements Signature interface
type blsSignature [1 + BLS_PUBLIC_KEY_SIZE + BLS_SIGNATURE_SIZE]byte
func newBLSSignature(pubKey, signature []byte) *blsSignature {
var ret blsSignature
ret[0] = address.VERSION_BLS
copy(ret.pubKey(), pubKey)
copy(ret.signature(), signature)
return &ret
}
func (sig *blsSignature) pubKey() []byte {
return sig[1 : BLS_PUBLIC_KEY_SIZE+1]
}
func (sig *blsSignature) signature() []byte {
return sig[1+BLS_PUBLIC_KEY_SIZE:]
}
func (sig *blsSignature) IsValid(signedData []byte) bool {
if sig[0] != address.VERSION_BLS {
return false
}
// unmarshal public key
pubKey := suite.G2().Point()
if err := pubKey.UnmarshalBinary(sig.pubKey()); err != nil {
return false
}
return bdn.Verify(suite, pubKey, signedData, sig.signature()) == nil
}
func (sig *blsSignature) Bytes() []byte {
return sig[:]
}
func (sig *blsSignature) Address() address.Address {
return address.FromBLSPubKey(sig.pubKey())
}
func (sig *blsSignature) String() string {
return base58.Encode(sig[:])
}
func AggregateBLSSignatureSchemes(sigSchemes ...SignatureScheme) (SignatureScheme, error) {
priKeys := make([]kyber.Scalar, len(sigSchemes))
pubKeys := make([]kyber.Point, len(sigSchemes))
for i, s := range sigSchemes {
ss, ok := s.(*blsSignatureScheme)
if !ok {
return nil, fmt.Errorf("not a BLS signature scheme")
}
priKeys[i] = ss.priKey
pubKeys[i] = ss.pubKey
}
aggregatedPriKey := suite.G2().Scalar().Zero()
// sum up all private keys
for i := range priKeys {
aggregatedPriKey = aggregatedPriKey.Add(aggregatedPriKey, priKeys[i])
}
mask, _ := sign.NewMask(suite, pubKeys, nil)
for i := range pubKeys {
_ = mask.SetBit(i, true)
}
aggregatedPubKey, err := bdn.AggregatePublicKeys(suite, mask)
if err != nil {
return nil, err
}
return &blsSignatureScheme{
priKey: aggregatedPriKey,
pubKey: aggregatedPubKey,
}, nil
}
func AggregateBLSSignatures(sigs ...Signature) (Signature, error) {
if len(sigs) == 0 {
return nil, fmt.Errorf("must be at least one signature to aggregate")
}
if len(sigs) == 1 {
return sigs[0], nil
}
pubKeys := make([]kyber.Point, len(sigs))
signatures := make([][]byte, len(sigs))
var err error
for i, sig := range sigs {
sigBls, ok := sig.(*blsSignature)
if !ok {
return nil, fmt.Errorf("not a BLS signature")
}
pubKeys[i] = suite.G2().Point()
if err = pubKeys[i].UnmarshalBinary(sigBls.pubKey()); err != nil {
return nil, err
}
signatures[i] = sigBls.signature()
}
mask, _ := sign.NewMask(suite, pubKeys, nil)
for i := range pubKeys {
_ = mask.SetBit(i, true)
}
aggregatedSignature, err := bdn.AggregateSignatures(suite, signatures, mask)
if err != nil {
return nil, err
}
sigBin, err := aggregatedSignature.MarshalBinary()
if err != nil {
return nil, err
}
aggregatedPubKey, err := bdn.AggregatePublicKeys(suite, mask)
if err != nil {
return nil, err
}
pubKeyBin, err := aggregatedPubKey.MarshalBinary()
if err != nil {
return nil, err
}
return newBLSSignature(pubKeyBin, sigBin), nil
}
// interface contract (allow the compiler to check if the implementation has all of the required methods).
var _ Signature = &blsSignature{}
......@@ -5,12 +5,40 @@ import (
"testing"
)
func TestBLS_BDN(t *testing.T) {
dataToSign := []byte("testtesttest")
var dataToSign = []byte("Hello Boneh-Lynn-Shacham (BLS) --> Boneh-Drijvers-Neven (BDN)")
blsSigScheme := RandBLS_BDN()
func TestBLS_base(t *testing.T) {
blsSigScheme := RandBLS()
t.Logf("generating random BLS signature scheme: %s\n", blsSigScheme.(*blsSignatureScheme).String())
signature := blsSigScheme.Sign(dataToSign)
assert.Equal(t, blsSigScheme.Address() == signature.Address(), true)
assert.Equal(t, signature.IsValid(dataToSign), true)
assert.Equal(t, blsSigScheme.Address(), signature.Address())
res := signature.IsValid(dataToSign)
assert.Equal(t, res, true)
}
// number of signatures to aggregate
const numSigs = 100
func TestBLS_aggregation(t *testing.T) {
sigs := make([]Signature, numSigs)
sigSchemes := make([]SignatureScheme, numSigs)
for i := range sigs {
sigSchemes[i] = RandBLS()
sigs[i] = sigSchemes[i].Sign(dataToSign)
}
aggregatedSig1, err := AggregateBLSSignatures(sigs...)
assert.Equal(t, err, nil)
assert.Equal(t, aggregatedSig1.IsValid(dataToSign), true)
aggregatedScheme, err := AggregateBLSSignatureSchemes(sigSchemes...)
assert.Equal(t, err, nil)
if err == nil {
aggregatedSig2 := aggregatedScheme.Sign(dataToSign)
assert.Equal(t, aggregatedSig2, aggregatedSig2)
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment