mirror of
https://github.com/tendermint/tendermint.git
synced 2026-05-23 15:41:30 +00:00
BLS library from Ostracon (Tendermint fork)
* Files retrieved from Ostracon implementation on Github, master
branch, commit 051475802c94f46763cae4ef4c18e01d4cfa952e.
This commit is contained in:
@@ -3,13 +3,19 @@ module github.com/tendermint/tendermint/crypto/bls
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/herumi/bls-eth-go-binary v0.0.0-20220216073600-600054663ec1
|
||||
github.com/line/ostracon v1.0.4
|
||||
github.com/stretchr/testify v1.7.1
|
||||
github.com/supranational/blst v0.3.7
|
||||
github.com/tendermint/tendermint v0.35.4
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/oasisprotocol/curve25519-voi v0.0.0-20210609091139-0a56a4bca00b // indirect
|
||||
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/sasha-s/go-deadlock v0.2.1-0.20190427202633-1595213edefa // indirect
|
||||
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad // indirect
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
|
||||
)
|
||||
|
||||
233
crypto/bls/ostracon/bls.go
Normal file
233
crypto/bls/ostracon/bls.go
Normal file
@@ -0,0 +1,233 @@
|
||||
package bls
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha512"
|
||||
"crypto/subtle"
|
||||
"fmt"
|
||||
|
||||
tmjson "github.com/line/ostracon/libs/json"
|
||||
|
||||
"github.com/herumi/bls-eth-go-binary/bls"
|
||||
|
||||
"github.com/line/ostracon/crypto"
|
||||
"github.com/line/ostracon/crypto/tmhash"
|
||||
)
|
||||
|
||||
var _ crypto.PrivKey = PrivKey{}
|
||||
|
||||
const (
|
||||
PrivKeyName = "ostracon/PrivKeyBLS12"
|
||||
PubKeyName = "ostracon/PubKeyBLS12"
|
||||
PrivKeySize = 32
|
||||
PubKeySize = 48
|
||||
SignatureSize = 96
|
||||
KeyType = "bls12-381"
|
||||
)
|
||||
|
||||
func init() {
|
||||
tmjson.RegisterType(PubKey{}, PubKeyName)
|
||||
tmjson.RegisterType(PrivKey{}, PrivKeyName)
|
||||
|
||||
err := bls.Init(bls.BLS12_381)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("ERROR: %s", err))
|
||||
}
|
||||
err = bls.SetETHmode(bls.EthModeLatest)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("ERROR: %s", err))
|
||||
}
|
||||
}
|
||||
|
||||
// PrivKey implements crypto.PrivKey.
|
||||
type PrivKey [PrivKeySize]byte
|
||||
|
||||
// AddSignature adds a BLS signature to the init. When the init is nil, then a new aggregate signature is built
|
||||
// from specified signature.
|
||||
func AddSignature(init []byte, signature []byte) (aggrSign []byte, err error) {
|
||||
if init == nil {
|
||||
blsSign := bls.Sign{}
|
||||
init = blsSign.Serialize()
|
||||
} else if len(init) != SignatureSize {
|
||||
err = fmt.Errorf("invalid BLS signature: aggregated signature size %d is not valid size %d",
|
||||
len(init), SignatureSize)
|
||||
return
|
||||
}
|
||||
if len(signature) != SignatureSize {
|
||||
err = fmt.Errorf("invalid BLS signature: signature size %d is not valid size %d",
|
||||
len(signature), SignatureSize)
|
||||
return
|
||||
}
|
||||
blsSign := bls.Sign{}
|
||||
err = blsSign.Deserialize(signature)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
aggrBLSSign := bls.Sign{}
|
||||
err = aggrBLSSign.Deserialize(init)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
aggrBLSSign.Add(&blsSign)
|
||||
aggrSign = aggrBLSSign.Serialize()
|
||||
return
|
||||
}
|
||||
|
||||
func VerifyAggregatedSignature(aggregatedSignature []byte, pubKeys []PubKey, msgs [][]byte) error {
|
||||
if len(pubKeys) != len(msgs) {
|
||||
return fmt.Errorf("the number of public keys %d doesn't match the one of messages %d",
|
||||
len(pubKeys), len(msgs))
|
||||
}
|
||||
if aggregatedSignature == nil {
|
||||
if len(pubKeys) == 0 {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf(
|
||||
"the aggregate signature was omitted, even though %d public keys were specified", len(pubKeys))
|
||||
}
|
||||
aggrSign := bls.Sign{}
|
||||
err := aggrSign.Deserialize(aggregatedSignature)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
blsPubKeys := make([]bls.PublicKey, len(pubKeys))
|
||||
hashes := make([][]byte, len(msgs))
|
||||
for i := 0; i < len(pubKeys); i++ {
|
||||
blsPubKeys[i] = bls.PublicKey{}
|
||||
err = blsPubKeys[i].Deserialize(pubKeys[i][:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
hash := sha512.Sum512_256(msgs[i])
|
||||
hashes[i] = hash[:]
|
||||
}
|
||||
if !aggrSign.VerifyAggregateHashes(blsPubKeys, hashes) {
|
||||
return fmt.Errorf("failed to verify the aggregated hashes by %d public keys", len(blsPubKeys))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GenPrivKey generates a new BLS12-381 private key.
|
||||
func GenPrivKey() PrivKey {
|
||||
sigKey := bls.SecretKey{}
|
||||
sigKey.SetByCSPRNG()
|
||||
sigKeyBinary := PrivKey{}
|
||||
binary := sigKey.Serialize()
|
||||
if len(binary) != PrivKeySize {
|
||||
panic(fmt.Sprintf("unexpected BLS private key size: %d != %d", len(binary), PrivKeySize))
|
||||
}
|
||||
copy(sigKeyBinary[:], binary)
|
||||
return sigKeyBinary
|
||||
}
|
||||
|
||||
// Bytes marshals the privkey using amino encoding.
|
||||
func (privKey PrivKey) Bytes() []byte {
|
||||
return privKey[:]
|
||||
}
|
||||
|
||||
// Sign produces a signature on the provided message.
|
||||
func (privKey PrivKey) Sign(msg []byte) ([]byte, error) {
|
||||
if msg == nil {
|
||||
panic("Nil specified as the message")
|
||||
}
|
||||
blsKey := bls.SecretKey{}
|
||||
err := blsKey.Deserialize(privKey[:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
hash := sha512.Sum512_256(msg)
|
||||
sign := blsKey.SignHash(hash[:])
|
||||
return sign.Serialize(), nil
|
||||
}
|
||||
|
||||
// VRFProve is not supported in BLS12.
|
||||
func (privKey PrivKey) VRFProve(seed []byte) (crypto.Proof, error) {
|
||||
return nil, fmt.Errorf("VRF prove is not supported by the BLS12")
|
||||
}
|
||||
|
||||
// PubKey gets the corresponding public key from the private key.
|
||||
func (privKey PrivKey) PubKey() crypto.PubKey {
|
||||
blsKey := bls.SecretKey{}
|
||||
err := blsKey.Deserialize(privKey[:])
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("Not a BLS12-381 private key: %X", privKey[:]))
|
||||
}
|
||||
pubKey := blsKey.GetPublicKey()
|
||||
pubKeyBinary := PubKey{}
|
||||
binary := pubKey.Serialize()
|
||||
if len(binary) != PubKeySize {
|
||||
panic(fmt.Sprintf("unexpected BLS public key size: %d != %d", len(binary), PubKeySize))
|
||||
}
|
||||
copy(pubKeyBinary[:], binary)
|
||||
return pubKeyBinary
|
||||
}
|
||||
|
||||
// Equals - you probably don't need to use this.
|
||||
// Runs in constant time based on length of the keys.
|
||||
func (privKey PrivKey) Equals(other crypto.PrivKey) bool {
|
||||
if otherEd, ok := other.(PrivKey); ok {
|
||||
return subtle.ConstantTimeCompare(privKey[:], otherEd[:]) == 1
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Type returns information to identify the type of this key.
|
||||
func (privKey PrivKey) Type() string {
|
||||
return KeyType
|
||||
}
|
||||
|
||||
var _ crypto.PubKey = PubKey{}
|
||||
|
||||
// PubKey implements crypto.PubKey for the BLS12-381 signature scheme.
|
||||
type PubKey [PubKeySize]byte
|
||||
|
||||
// Address is the SHA256-20 of the raw pubkey bytes.
|
||||
func (pubKey PubKey) Address() crypto.Address {
|
||||
return tmhash.SumTruncated(pubKey[:])
|
||||
}
|
||||
|
||||
// Bytes marshals the PubKey using amino encoding.
|
||||
func (pubKey PubKey) Bytes() []byte {
|
||||
return pubKey[:]
|
||||
}
|
||||
|
||||
func (pubKey PubKey) VerifySignature(msg []byte, sig []byte) bool {
|
||||
// make sure we use the same algorithm to sign
|
||||
if len(sig) != SignatureSize {
|
||||
return false
|
||||
}
|
||||
blsPubKey := bls.PublicKey{}
|
||||
err := blsPubKey.Deserialize(pubKey[:])
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
blsSign := bls.Sign{}
|
||||
err = blsSign.Deserialize(sig)
|
||||
if err != nil {
|
||||
fmt.Printf("Signature Deserialize failed: %s", err)
|
||||
return false
|
||||
}
|
||||
hash := sha512.Sum512_256(msg)
|
||||
return blsSign.VerifyHash(&blsPubKey, hash[:])
|
||||
}
|
||||
|
||||
// VRFVerify is not supported in BLS12.
|
||||
func (pubKey PubKey) VRFVerify(proof crypto.Proof, seed []byte) (crypto.Output, error) {
|
||||
return nil, fmt.Errorf("VRF verify is not supported by the BLS12")
|
||||
}
|
||||
|
||||
func (pubKey PubKey) String() string {
|
||||
return fmt.Sprintf("PubKeyBLS12{%X}", pubKey[:])
|
||||
}
|
||||
|
||||
func (pubKey PubKey) Equals(other crypto.PubKey) bool {
|
||||
if otherEd, ok := other.(PubKey); ok {
|
||||
return bytes.Equal(pubKey[:], otherEd[:])
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Type returns information to identify the type of this key.
|
||||
func (pubKey PubKey) Type() string {
|
||||
return KeyType
|
||||
}
|
||||
373
crypto/bls/ostracon/bls_test.go
Normal file
373
crypto/bls/ostracon/bls_test.go
Normal file
@@ -0,0 +1,373 @@
|
||||
package bls_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
b "github.com/herumi/bls-eth-go-binary/bls"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/line/ostracon/crypto"
|
||||
"github.com/line/ostracon/crypto/bls"
|
||||
)
|
||||
|
||||
func TestBasicSignatureFunctions(t *testing.T) {
|
||||
privateKey := b.SecretKey{}
|
||||
privateKey.SetByCSPRNG()
|
||||
publicKey := privateKey.GetPublicKey()
|
||||
|
||||
duplicatedPrivateKey := b.SecretKey{}
|
||||
err := duplicatedPrivateKey.Deserialize(privateKey.Serialize())
|
||||
if err != nil {
|
||||
t.Fatalf("Private key deserialization failed.")
|
||||
}
|
||||
|
||||
if len(privateKey.Serialize()) != bls.PrivKeySize {
|
||||
t.Fatalf("The constant size %d of the private key is different from the actual size %d.",
|
||||
bls.PrivKeySize, len(privateKey.Serialize()))
|
||||
}
|
||||
|
||||
duplicatedPublicKey := b.PublicKey{}
|
||||
err = duplicatedPublicKey.Deserialize(publicKey.Serialize())
|
||||
if err != nil {
|
||||
t.Fatalf("Public key deserialization failed.")
|
||||
}
|
||||
|
||||
if len(publicKey.Serialize()) != bls.PubKeySize {
|
||||
t.Fatalf("The constant size %d of the public key is different from the actual size %d.",
|
||||
bls.PubKeySize, len(publicKey.Serialize()))
|
||||
}
|
||||
|
||||
duplicatedSignature := func(sig *b.Sign) *b.Sign {
|
||||
duplicatedSign := b.Sign{}
|
||||
err := duplicatedSign.Deserialize(sig.Serialize())
|
||||
if err != nil {
|
||||
t.Fatalf("Signature deserialization failed.")
|
||||
}
|
||||
|
||||
if len(sig.Serialize()) != bls.SignatureSize {
|
||||
t.Fatalf("The constant size %d of the signature is different from the actual size %d.",
|
||||
bls.SignatureSize, len(sig.Serialize()))
|
||||
}
|
||||
return &duplicatedSign
|
||||
}
|
||||
|
||||
msg := []byte("hello, world")
|
||||
for _, privKey := range []b.SecretKey{privateKey, duplicatedPrivateKey} {
|
||||
for _, pubKey := range []*b.PublicKey{publicKey, &duplicatedPublicKey} {
|
||||
signature := privKey.SignByte(msg)
|
||||
if !signature.VerifyByte(pubKey, msg) {
|
||||
t.Errorf("Signature verification failed.")
|
||||
}
|
||||
|
||||
if !duplicatedSignature(signature).VerifyByte(pubKey, msg) {
|
||||
t.Errorf("Signature verification failed.")
|
||||
}
|
||||
|
||||
for i := 0; i < len(msg); i++ {
|
||||
for j := 0; j < 8; j++ {
|
||||
garbled := make([]byte, len(msg))
|
||||
copy(garbled, msg)
|
||||
garbled[i] ^= 1 << (8 - j - 1)
|
||||
if bytes.Equal(msg, garbled) {
|
||||
t.Fatalf("Not a barbled message")
|
||||
}
|
||||
if signature.VerifyByte(pubKey, garbled) {
|
||||
t.Errorf("Signature verification was successful against a garbled byte sequence.")
|
||||
}
|
||||
if duplicatedSignature(signature).VerifyByte(pubKey, garbled) {
|
||||
t.Errorf("Signature verification was successful against a garbled byte sequence.")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSignatureAggregationAndVerify(t *testing.T) {
|
||||
privKeys := make([]b.SecretKey, 25)
|
||||
pubKeys := make([]b.PublicKey, len(privKeys))
|
||||
msgs := make([][]byte, len(privKeys))
|
||||
hash32s := make([][32]byte, len(privKeys))
|
||||
signatures := make([]b.Sign, len(privKeys))
|
||||
for i, privKey := range privKeys {
|
||||
privKey.SetByCSPRNG()
|
||||
pubKeys[i] = *privKey.GetPublicKey()
|
||||
msgs[i] = []byte(fmt.Sprintf("hello, world #%d", i))
|
||||
hash32s[i] = sha256.Sum256(msgs[i])
|
||||
signatures[i] = *privKey.SignHash(hash32s[i][:])
|
||||
|
||||
// normal single-hash case
|
||||
if !signatures[i].VerifyHash(&pubKeys[i], hash32s[i][:]) {
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
// in case where 1-bit of hash was garbled
|
||||
garbledHash := make([]byte, len(msgs[i]))
|
||||
copy(garbledHash, msgs[i])
|
||||
garbledHash[0] ^= 1 << 0
|
||||
if garbledHash[0] == msgs[i][0] || signatures[i].VerifyByte(&pubKeys[i], garbledHash) {
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
// Verification using an invalid public key
|
||||
}
|
||||
|
||||
// aggregation
|
||||
multiSig := b.Sign{}
|
||||
multiSig.Aggregate(signatures)
|
||||
|
||||
// normal case
|
||||
hashes := make([][]byte, len(privKeys))
|
||||
for i := 0; i < len(hashes); i++ {
|
||||
hashes[i] = hash32s[i][:]
|
||||
}
|
||||
if !multiSig.VerifyAggregateHashes(pubKeys, hashes) {
|
||||
t.Fatalf("failed to validate the aggregate signature of the hashed message")
|
||||
}
|
||||
|
||||
// in case where 1-bit of signature was garbled
|
||||
multiSigBytes := multiSig.Serialize()
|
||||
for i := range multiSigBytes {
|
||||
for j := 0; j < 8; j++ {
|
||||
garbledMultiSigBytes := make([]byte, len(multiSigBytes))
|
||||
copy(garbledMultiSigBytes, multiSigBytes)
|
||||
garbledMultiSigBytes[i] ^= 1 << j
|
||||
if garbledMultiSigBytes[i] == multiSigBytes[i] {
|
||||
t.Fail()
|
||||
}
|
||||
garbledMultiSig := b.Sign{}
|
||||
err := garbledMultiSig.Deserialize(garbledMultiSigBytes)
|
||||
if err == nil {
|
||||
// Note that in some cases Deserialize() fails
|
||||
if garbledMultiSig.VerifyAggregateHashes(pubKeys, hashes) {
|
||||
t.Errorf("successfully verified the redacted signature")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// in case a public key used for verification is replaced
|
||||
invalidPrivKey := b.SecretKey{}
|
||||
invalidPrivKey.SetByCSPRNG()
|
||||
invalidPubKeys := make([]b.PublicKey, len(pubKeys))
|
||||
copy(invalidPubKeys, pubKeys)
|
||||
invalidPubKeys[len(invalidPubKeys)-1] = *invalidPrivKey.GetPublicKey()
|
||||
if multiSig.VerifyAggregateHashes(invalidPubKeys, hashes) {
|
||||
t.Fatalf("successfully verified that it contains a public key that was not involved in the signing")
|
||||
}
|
||||
|
||||
// in case a hash used for verification is replaced
|
||||
invalidHashes := make([][]byte, len(hashes))
|
||||
copy(invalidHashes, hashes)
|
||||
invalidHash := sha256.Sum256([]byte("hello, world #99"))
|
||||
invalidHashes[len(invalidHashes)-1] = invalidHash[:]
|
||||
if multiSig.VerifyAggregateHashes(pubKeys, invalidHashes) {
|
||||
t.Fatalf("successfully verified that it contains a hash that was not involved in the signing")
|
||||
}
|
||||
}
|
||||
|
||||
func generatePubKeysAndSigns(t *testing.T, size int) ([]bls.PubKey, [][]byte, [][]byte) {
|
||||
pubKeys := make([]bls.PubKey, size)
|
||||
msgs := make([][]byte, len(pubKeys))
|
||||
sigs := make([][]byte, len(pubKeys))
|
||||
for i := 0; i < len(pubKeys); i++ {
|
||||
var err error
|
||||
privKey := bls.GenPrivKey()
|
||||
pubKeys[i] = blsPublicKey(t, privKey.PubKey())
|
||||
msgs[i] = []byte(fmt.Sprintf("hello, workd #%d", i))
|
||||
sigs[i], err = privKey.Sign(msgs[i])
|
||||
if err != nil {
|
||||
t.Fatal(fmt.Sprintf("fail to sign: %s", err))
|
||||
}
|
||||
if !pubKeys[i].VerifySignature(msgs[i], sigs[i]) {
|
||||
t.Fatal("fail to verify signature")
|
||||
}
|
||||
}
|
||||
return pubKeys, msgs, sigs
|
||||
}
|
||||
|
||||
func blsPublicKey(t *testing.T, pubKey crypto.PubKey) bls.PubKey {
|
||||
blsPubKey, ok := pubKey.(bls.PubKey)
|
||||
if !ok {
|
||||
var keyType string
|
||||
if t := reflect.TypeOf(pubKey); t.Kind() == reflect.Ptr {
|
||||
keyType = "*" + t.Elem().Name()
|
||||
} else {
|
||||
keyType = t.Name()
|
||||
}
|
||||
t.Fatal(fmt.Sprintf("specified public key is not for BLS: %s", keyType))
|
||||
}
|
||||
return blsPubKey
|
||||
}
|
||||
|
||||
func aggregateSignatures(init []byte, signatures [][]byte) (aggrSig []byte, err error) {
|
||||
aggrSig = init
|
||||
for _, sign := range signatures {
|
||||
aggrSig, err = bls.AddSignature(aggrSig, sign)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func TestAggregatedSignature(t *testing.T) {
|
||||
|
||||
// generate BLS signatures and public keys
|
||||
pubKeys, msgs, sigs := generatePubKeysAndSigns(t, 25)
|
||||
|
||||
// aggregate signatures
|
||||
aggrSig, err := aggregateSignatures(nil, sigs)
|
||||
if err != nil {
|
||||
t.Errorf("fail to aggregate BLS signatures: %s", err)
|
||||
}
|
||||
if len(aggrSig) != bls.SignatureSize {
|
||||
t.Errorf("inconpatible signature size: %d != %d", len(aggrSig), bls.SignatureSize)
|
||||
}
|
||||
|
||||
// validate the aggregated signature
|
||||
if err := bls.VerifyAggregatedSignature(aggrSig, pubKeys, msgs); err != nil {
|
||||
t.Errorf("fail to verify aggregated signature: %s", err)
|
||||
}
|
||||
|
||||
// validate with the public keys and messages pair in random order
|
||||
t.Run("Doesn't Depend on the Order of PublicKey-Message Pairs", func(t *testing.T) {
|
||||
shuffledPubKeys := make([]bls.PubKey, len(pubKeys))
|
||||
shuffledMsgs := make([][]byte, len(msgs))
|
||||
copy(shuffledPubKeys, pubKeys)
|
||||
copy(shuffledMsgs, msgs)
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
rand.Shuffle(len(shuffledPubKeys), func(i, j int) {
|
||||
shuffledPubKeys[i], shuffledPubKeys[j] = shuffledPubKeys[j], shuffledPubKeys[i]
|
||||
shuffledMsgs[i], shuffledMsgs[j] = shuffledMsgs[j], shuffledMsgs[i]
|
||||
})
|
||||
if err := bls.VerifyAggregatedSignature(aggrSig, shuffledPubKeys, shuffledMsgs); err != nil {
|
||||
t.Errorf("fail to verify the aggregated signature with random order: %s", err)
|
||||
}
|
||||
})
|
||||
|
||||
// validate with the public keys in random order
|
||||
t.Run("Incorrect Public Key Order", func(t *testing.T) {
|
||||
shuffledPubKeys := make([]bls.PubKey, len(pubKeys))
|
||||
copy(shuffledPubKeys, pubKeys)
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
rand.Shuffle(len(shuffledPubKeys), func(i, j int) {
|
||||
shuffledPubKeys[i], shuffledPubKeys[j] = shuffledPubKeys[j], shuffledPubKeys[i]
|
||||
})
|
||||
if err := bls.VerifyAggregatedSignature(aggrSig, shuffledPubKeys, msgs); err == nil {
|
||||
t.Error("successfully validated with public keys of different order")
|
||||
}
|
||||
})
|
||||
|
||||
// validate with the messages in random order
|
||||
t.Run("Incorrect Message Order", func(t *testing.T) {
|
||||
shuffledMsgs := make([][]byte, len(msgs))
|
||||
copy(shuffledMsgs, msgs)
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
rand.Shuffle(len(shuffledMsgs), func(i, j int) {
|
||||
shuffledMsgs[i], shuffledMsgs[j] = shuffledMsgs[j], shuffledMsgs[i]
|
||||
})
|
||||
if err := bls.VerifyAggregatedSignature(aggrSig, pubKeys, shuffledMsgs); err == nil {
|
||||
t.Error("successfully validated with messages of different order")
|
||||
}
|
||||
})
|
||||
|
||||
// replace one public key with another and detect
|
||||
t.Run("Replace One Public Key", func(t *testing.T) {
|
||||
pubKey, _ := bls.GenPrivKey().PubKey().(bls.PubKey)
|
||||
replacedPubKeys := make([]bls.PubKey, len(pubKeys))
|
||||
copy(replacedPubKeys, pubKeys)
|
||||
replacedPubKeys[0] = pubKey
|
||||
if err := bls.VerifyAggregatedSignature(aggrSig, replacedPubKeys, msgs); err == nil {
|
||||
t.Error("verification with an invalid key was successful")
|
||||
}
|
||||
})
|
||||
|
||||
// replace one message with another and detect
|
||||
t.Run("Replace One Message", func(t *testing.T) {
|
||||
msg := []byte(fmt.Sprintf("hello, world #%d replaced", len(msgs)))
|
||||
replacedMsgs := make([][]byte, len(msgs))
|
||||
copy(replacedMsgs, msgs)
|
||||
replacedMsgs[0] = msg
|
||||
if err := bls.VerifyAggregatedSignature(aggrSig, pubKeys, replacedMsgs); err == nil {
|
||||
t.Error("verification with an invalid message was successful")
|
||||
}
|
||||
})
|
||||
|
||||
// add new signature to existing aggregated signature and verify
|
||||
t.Run("Incremental Update", func(t *testing.T) {
|
||||
msg := []byte(fmt.Sprintf("hello, world #%d", len(msgs)))
|
||||
privKey := bls.GenPrivKey()
|
||||
pubKey := privKey.PubKey()
|
||||
sig, err := privKey.Sign(msg)
|
||||
assert.Nilf(t, err, "%s", err)
|
||||
newAggrSig, _ := aggregateSignatures(aggrSig, [][]byte{sig})
|
||||
newPubKeys := make([]bls.PubKey, len(pubKeys))
|
||||
copy(newPubKeys, pubKeys)
|
||||
newPubKeys = append(newPubKeys, blsPublicKey(t, pubKey))
|
||||
newMsgs := make([][]byte, len(msgs))
|
||||
copy(newMsgs, msgs)
|
||||
newMsgs = append(newMsgs, msg)
|
||||
if err := bls.VerifyAggregatedSignature(newAggrSig, newPubKeys, newMsgs); err != nil {
|
||||
t.Errorf("fail to verify the aggregate signature with the new signature: %s", err)
|
||||
}
|
||||
})
|
||||
|
||||
// nil is returned for nil and empty signature
|
||||
nilSig, _ := aggregateSignatures(nil, [][]byte{})
|
||||
assert.Nil(t, nilSig)
|
||||
|
||||
// a non-BLS signature contained
|
||||
func() {
|
||||
_, err = aggregateSignatures(nil, [][]byte{make([]byte, 0)})
|
||||
assert.NotNil(t, err)
|
||||
}()
|
||||
}
|
||||
|
||||
func TestSignatureAggregation(t *testing.T) {
|
||||
publicKeys := make([]b.PublicKey, 25)
|
||||
aggregatedSignature := b.Sign{}
|
||||
aggregatedPublicKey := b.PublicKey{}
|
||||
msg := []byte("hello, world")
|
||||
for i := 0; i < len(publicKeys); i++ {
|
||||
privateKey := b.SecretKey{}
|
||||
privateKey.SetByCSPRNG()
|
||||
publicKeys[i] = *privateKey.GetPublicKey()
|
||||
aggregatedSignature.Add(privateKey.SignByte(msg))
|
||||
aggregatedPublicKey.Add(&publicKeys[i])
|
||||
}
|
||||
|
||||
if !aggregatedSignature.FastAggregateVerify(publicKeys, msg) {
|
||||
t.Errorf("Aggregated signature verification failed.")
|
||||
}
|
||||
if !aggregatedSignature.VerifyByte(&aggregatedPublicKey, msg) {
|
||||
t.Errorf("Aggregated signature verification failed.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSignAndValidateBLS12(t *testing.T) {
|
||||
privKey := bls.GenPrivKey()
|
||||
pubKey := privKey.PubKey()
|
||||
|
||||
msg := crypto.CRandBytes(128)
|
||||
sig, err := privKey.Sign(msg)
|
||||
require.Nil(t, err)
|
||||
fmt.Printf("restoring signature: %x\n", sig)
|
||||
|
||||
// Test the signature
|
||||
assert.True(t, pubKey.VerifySignature(msg, sig))
|
||||
|
||||
// Mutate the signature, just one bit.
|
||||
// TODO: Replace this with a much better fuzzer, tendermint/ed25519/issues/10
|
||||
sig[7] ^= byte(0x01)
|
||||
|
||||
assert.False(t, pubKey.VerifySignature(msg, sig))
|
||||
}
|
||||
Reference in New Issue
Block a user