diff --git a/packages/binary/valuetransfer/address/address.go b/packages/binary/valuetransfer/address/address.go index d721526f521976bd7890e49e0dd11166c7e8ebb3..b73092fd6fd8d9c522be45fd802fda80824088fe 100644 --- a/packages/binary/valuetransfer/address/address.go +++ b/packages/binary/valuetransfer/address/address.go @@ -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 diff --git a/packages/binary/valuetransfer/address/signaturescheme/bls-bdn.go b/packages/binary/valuetransfer/address/signaturescheme/bls-bdn.go deleted file mode 100644 index fe001cbda901c639c68ce2bb71aeacee04705405..0000000000000000000000000000000000000000 --- a/packages/binary/valuetransfer/address/signaturescheme/bls-bdn.go +++ /dev/null @@ -1,103 +0,0 @@ -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{} diff --git a/packages/binary/valuetransfer/address/signaturescheme/bls.go b/packages/binary/valuetransfer/address/signaturescheme/bls.go new file mode 100644 index 0000000000000000000000000000000000000000..b20d3273933d17a7ea1a41c1f77892bd4deb3335 --- /dev/null +++ b/packages/binary/valuetransfer/address/signaturescheme/bls.go @@ -0,0 +1,232 @@ +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{} diff --git a/packages/binary/valuetransfer/address/signaturescheme/bls_test.go b/packages/binary/valuetransfer/address/signaturescheme/bls_test.go index b181fc5926477c20586cf29fe0a05f92fe327773..28e1a3a78b508d3d304f2aa365eabeb696e44694 100644 --- a/packages/binary/valuetransfer/address/signaturescheme/bls_test.go +++ b/packages/binary/valuetransfer/address/signaturescheme/bls_test.go @@ -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) + } }