Compare commits

...

8 Commits

Author SHA1 Message Date
Daniel Cason
383a64dcfe BLS: sign/verify benchmarks for blst and ostracon
* Using Ed25519 signatures as reference for simple signatures
     production and (successful) verification with 1k messages.
2022-04-19 15:10:10 +02:00
Daniel Cason
a5f9732ba0 BLS: simple sign and verify using ostracon library
* FIXME: when a signature verification fails, an error is printed.
2022-04-19 14:54:29 +02:00
Daniel Cason
18ad380271 BLS/ostracon: tests use Tendermint's crypto API 2022-04-19 14:51:15 +02:00
Daniel Cason
a38aeea648 BLS/ostracon: using Tendermint's crypto interfaces
* Ostracon augmented the crypto.PubKey and crypto.PrivKey interfaces
     with Verificable Random Function (VRF) proofs, which however, are
     not yet implemented (so their code was commented).
2022-04-19 14:46:34 +02:00
Daniel Cason
2bd0a6527e BLS library from Ostracon (Tendermint fork)
* Files retrieved from Ostracon implementation on Github, master
     branch, commit 051475802c94f46763cae4ef4c18e01d4cfa952e.
2022-04-19 14:41:32 +02:00
Daniel Cason
006ebf50df BLS: simple sign and verify using blst library 2022-04-19 14:38:48 +02:00
Daniel Cason
d12b3538a8 BLS: wrapper for blst BLS12-381 signature library
* blst package implements Tendermint's crypto package interfaces
     PrivKey and PubKey, used for simple signatures verification.
2022-04-19 14:33:06 +02:00
Daniel Cason
0ad230b99e BLS signatures: simple test unit for Ed25519 keys 2022-04-19 14:28:20 +02:00
6 changed files with 1001 additions and 0 deletions

87
crypto/bls/blst/blst.go Normal file
View File

@@ -0,0 +1,87 @@
package blst
import (
"crypto/rand"
bls "github.com/supranational/blst/bindings/go"
"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/crypto/tmhash"
"github.com/tendermint/tendermint/libs/bytes"
)
const (
PrivKeyName = "blst/PrivKey"
PubKeyName = "blst/PubKey"
PrivKeySize = 32
PubKeySize = 192
SignatureSize = 96
)
var dst = []byte("BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_NUL_")
// PrivKey implements tendermint.crypto.PrivKey.
type PrivKey struct {
sk *bls.SecretKey
}
func GenPrivKey() *PrivKey {
var ikm [32]byte
_, _ = rand.Read(ikm[:])
return &PrivKey{bls.KeyGen(ikm[:])}
}
func (k *PrivKey) Bytes() []byte {
return k.sk.Serialize()
}
func (k *PrivKey) Equals(other crypto.PrivKey) bool {
if otherK, ok := other.(*PrivKey); ok {
return k.sk.Equals(otherK.sk)
}
return false
}
func (k *PrivKey) PubKey() crypto.PubKey {
return &PubKey{new(bls.P2Affine).From(k.sk)}
}
func (k *PrivKey) Sign(msg []byte) ([]byte, error) {
sig := new(bls.P1Affine).Sign(k.sk, msg, dst)
return sig.Serialize(), nil
}
func (k *PrivKey) Type() string {
return PrivKeyName
}
// PubKey implements tendermint.crypto.PubKey.
type PubKey struct {
pk *bls.P2Affine
}
func (k *PubKey) Address() bytes.HexBytes {
return tmhash.SumTruncated(k.Bytes())
}
func (k *PubKey) Bytes() []byte {
return k.pk.Serialize()
}
func (k *PubKey) Equals(other crypto.PubKey) bool {
if otherK, ok := other.(*PubKey); ok {
return k.pk.Equals(otherK.pk)
}
return false
}
func (k *PubKey) Type() string {
return PubKeyName
}
func (k *PubKey) VerifySignature(msg []byte, bsig []byte) bool {
sig := new(bls.P1Affine).Deserialize(bsig)
if sig == nil {
return false
}
return sig.Verify(true, k.pk, false, msg, dst)
}

View File

@@ -0,0 +1,97 @@
package blst
import "testing"
func TestKeyGen(t *testing.T) {
key1 := GenPrivKey()
if key1 == nil {
t.Fatal("GenPrivKey() return nil")
}
b := key1.Bytes()
if len(b) != PrivKeySize {
t.Error("Serilalized private key", len(b), b)
}
key2 := GenPrivKey()
if key2 == nil {
t.Fatal("GenPrivKey() return nil")
}
b = key2.Bytes()
if len(b) != PrivKeySize {
t.Error("Serilalized private key", len(b), b)
}
if !key1.Equals(key1) {
t.Error("Private key not equal to itself", key1)
}
if !key2.Equals(key2) {
t.Error("Private key not equal to itself", key1)
}
if key1.Equals(key2) || key2.Equals(key1) {
t.Error("Different private keys equal", key1, key2)
}
pub1 := key1.PubKey()
if pub1 == nil {
t.Fatal("PubKey() return nil")
}
b = pub1.Bytes()
if len(b) != PubKeySize {
t.Error("Serilalized public key", len(b), b)
}
pub2 := key2.PubKey()
if pub2 == nil {
t.Fatal("PubKey() return nil")
}
b = pub2.Bytes()
if len(b) != PubKeySize {
t.Error("Serilalized public key", len(b), b)
}
if !pub1.Equals(pub1) {
t.Error("Public key not equal to itself", pub1)
}
if !pub2.Equals(pub2) {
t.Error("Public key not equal to itself", pub1)
}
if pub1.Equals(pub2) || pub2.Equals(pub1) {
t.Error("Different public keys equal", pub1, pub2)
}
}
func TestKeySignVerify(t *testing.T) {
msg := []byte("a test message")
key1 := GenPrivKey()
sig1, err := key1.Sign(msg)
if sig1 == nil || err != nil {
t.Fatal("Failed to sign message", sig1, err)
}
if len(sig1) != SignatureSize {
t.Error("Signature size", len(sig1), sig1)
}
key2 := GenPrivKey()
sig2, err := key2.Sign(msg)
if sig2 == nil || err != nil {
t.Fatal("Failed to sign message", sig2, err)
}
if len(sig2) != SignatureSize {
t.Error("Signature size", len(sig2), sig2)
}
pub1 := key1.PubKey()
pub2 := key2.PubKey()
if !pub1.VerifySignature(msg, sig1) {
t.Error("Failed to verify own signature", msg, sig1)
}
if !pub2.VerifySignature(msg, sig2) {
t.Error("Failed to verify own signature", msg, sig2)
}
if pub1.VerifySignature(msg, sig2) {
t.Error("Signature verified by wrong key", msg, sig2)
}
if pub2.VerifySignature(msg, sig1) {
t.Error("Signature verified by wrong key", msg, sig1)
}
}

21
crypto/bls/go.mod Normal file
View File

@@ -0,0 +1,21 @@
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
)

235
crypto/bls/ostracon/bls.go Normal file
View File

@@ -0,0 +1,235 @@
package ostracon
import (
"bytes"
"crypto/sha512"
"crypto/subtle"
"fmt"
tmjson "github.com/line/ostracon/libs/json"
"github.com/herumi/bls-eth-go-binary/bls"
"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/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
}
// FIXME: Type crypto.Proof not defined in Tendermint.
//// 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[:])
}
// FIXME: Types crypto.Proof and crypto.Output not defined in Tendermint.
//// 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
}

View File

@@ -0,0 +1,372 @@
package ostracon
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/tendermint/tendermint/crypto"
)
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()) != PrivKeySize {
t.Fatalf("The constant size %d of the private key is different from the actual size %d.",
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()) != PubKeySize {
t.Fatalf("The constant size %d of the public key is different from the actual size %d.",
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()) != SignatureSize {
t.Fatalf("The constant size %d of the signature is different from the actual size %d.",
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) ([]PubKey, [][]byte, [][]byte) {
pubKeys := make([]PubKey, size)
msgs := make([][]byte, len(pubKeys))
sigs := make([][]byte, len(pubKeys))
for i := 0; i < len(pubKeys); i++ {
var err error
privKey := 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) PubKey {
blsPubKey, ok := pubKey.(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 = 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) != SignatureSize {
t.Errorf("inconpatible signature size: %d != %d", len(aggrSig), SignatureSize)
}
// validate the aggregated signature
if err := 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([]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 := 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([]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 := 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 := 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, _ := GenPrivKey().PubKey().(PubKey)
replacedPubKeys := make([]PubKey, len(pubKeys))
copy(replacedPubKeys, pubKeys)
replacedPubKeys[0] = pubKey
if err := 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 := 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 := GenPrivKey()
pubKey := privKey.PubKey()
sig, err := privKey.Sign(msg)
assert.Nilf(t, err, "%s", err)
newAggrSig, _ := aggregateSignatures(aggrSig, [][]byte{sig})
newPubKeys := make([]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 := 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 := 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))
}

View File

@@ -0,0 +1,189 @@
package bls
import (
"crypto/rand"
"testing"
"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/crypto/bls/blst"
"github.com/tendermint/tendermint/crypto/bls/ostracon"
"github.com/tendermint/tendermint/crypto/ed25519"
)
func TestEd25519SignVerify(t *testing.T) {
m := []byte("a test message to be signed")
privKey := ed25519.GenPrivKey()
pubKey := privKey.PubKey()
sig, err := privKey.Sign(m)
if err != nil {
t.Error("Unexpected nil signature or error", m, sig, err)
}
if !pubKey.VerifySignature(m, sig) {
t.Error("Failed to verify signature produced by the key", m, sig)
}
if pubKey.VerifySignature(scrambleBytes(m), sig) {
t.Error("Unexpected to verify scrambled message", scrambleBytes(m), sig)
}
if pubKey.VerifySignature(m, scrambleBytes(sig)) {
t.Error("Unexpected to verify scrambled signature", m, scrambleBytes(sig))
}
}
func TestBlstSignVerify(t *testing.T) {
m := []byte("a test message to be signed")
privKey := blst.GenPrivKey()
pubKey := privKey.PubKey()
sig, err := privKey.Sign(m)
if err != nil {
t.Error("Unexpected nil signature or error", m, sig, err)
}
if !pubKey.VerifySignature(m, sig) {
t.Error("Failed to verify signature produced by the key", m, sig)
}
if pubKey.VerifySignature(scrambleBytes(m), sig) {
t.Error("Unexpected to verify scrambled message", scrambleBytes(m), sig)
}
if pubKey.VerifySignature(m, scrambleBytes(sig)) {
t.Error("Unexpected to verify scrambled signature", m, scrambleBytes(sig))
}
}
func TestOstraconSignVerify(t *testing.T) {
m := []byte("a test message to be signed")
privKey := ostracon.GenPrivKey()
pubKey := privKey.PubKey()
sig, err := privKey.Sign(m)
if err != nil {
t.Error("Unexpected nil signature or error", m, sig, err)
}
if !pubKey.VerifySignature(m, sig) {
t.Error("Failed to verify signature produced by the key", m, sig)
}
if pubKey.VerifySignature(scrambleBytes(m), sig) {
t.Error("Unexpected to verify scrambled message", scrambleBytes(m), sig)
}
if pubKey.VerifySignature(m, scrambleBytes(sig)) {
t.Error("Unexpected to verify scrambled signature", m, scrambleBytes(sig))
}
}
// Scrambles a byte array, currently just flipping a bit.
// TODO: implement a more complex scrambling method.
func scrambleBytes(b []byte) []byte {
bb := make([]byte, len(b))
copy(bb, b)
index := len(b) / 2
bb[index] ^= 1
return bb
}
// Retrieve a set of public keys from a set of private keys.
func pubKeysFromPrivKeys(privKeys []crypto.PrivKey) []crypto.PubKey {
pubKeys := make([]crypto.PubKey, len(privKeys))
for i := 0; i < len(privKeys); i++ {
pubKeys[i] = privKeys[i].PubKey()
}
return pubKeys
}
// Generate n random messages with the given size.
func randomMessages(n, size int) [][]byte {
messages := make([][]byte, n)
for i := 0; i < n; i++ {
messages[i] = make([]byte, size)
rand.Read(messages[i])
}
return messages
}
// Sign a set of messages with a number of private keys.
func signMessages(messages [][]byte, privKeys []crypto.PrivKey) [][]byte {
sigs := make([][]byte, len(messages))
for i := 0; i < len(messages); i++ {
sigs[i], _ = privKeys[i%len(privKeys)].Sign(messages[i])
}
return sigs
}
var _sig []byte
func BenchmarkEd25519Sign1k(t *testing.B) {
messages := randomMessages(1000, 1024)
privKeys := make([]crypto.PrivKey, 128)
for i := 0; i < len(privKeys); i++ {
privKeys[i] = ed25519.GenPrivKey()
}
t.ResetTimer()
for i := 0; i < t.N; i++ {
_sig, _ = privKeys[i%len(privKeys)].Sign(messages[i%len(messages)])
}
}
func BenchmarkEd25519Verify1k(t *testing.B) {
messages := randomMessages(1000, 1024)
privKeys := make([]crypto.PrivKey, 128)
for i := 0; i < len(privKeys); i++ {
privKeys[i] = ed25519.GenPrivKey()
}
pubKeys := pubKeysFromPrivKeys(privKeys)
sigs := signMessages(messages, privKeys)
t.ResetTimer()
for i := 0; i < t.N; i++ {
pubKeys[i%len(pubKeys)].VerifySignature(messages[i%len(messages)], sigs[i%len(sigs)])
}
}
func BenchmarkBlstSign1k(t *testing.B) {
messages := randomMessages(1000, 1024)
privKeys := make([]crypto.PrivKey, 128)
for i := 0; i < len(privKeys); i++ {
privKeys[i] = blst.GenPrivKey()
}
t.ResetTimer()
for i := 0; i < t.N; i++ {
_sig, _ = privKeys[i%len(privKeys)].Sign(messages[i%len(messages)])
}
}
func BenchmarkBlstVerify1k(t *testing.B) {
messages := randomMessages(1000, 1024)
privKeys := make([]crypto.PrivKey, 128)
for i := 0; i < len(privKeys); i++ {
privKeys[i] = blst.GenPrivKey()
}
pubKeys := pubKeysFromPrivKeys(privKeys)
sigs := signMessages(messages, privKeys)
t.ResetTimer()
for i := 0; i < t.N; i++ {
pubKeys[i%len(pubKeys)].VerifySignature(messages[i%len(messages)], sigs[i%len(sigs)])
}
}
func BenchmarkOstraconSign1k(t *testing.B) {
messages := randomMessages(1000, 1024)
privKeys := make([]crypto.PrivKey, 128)
for i := 0; i < len(privKeys); i++ {
privKeys[i] = ostracon.GenPrivKey()
}
t.ResetTimer()
for i := 0; i < t.N; i++ {
_sig, _ = privKeys[i%len(privKeys)].Sign(messages[i%len(messages)])
}
}
func BenchmarkOstraconVerify1k(t *testing.B) {
messages := randomMessages(1000, 1024)
privKeys := make([]crypto.PrivKey, 128)
for i := 0; i < len(privKeys); i++ {
privKeys[i] = ostracon.GenPrivKey()
}
pubKeys := pubKeysFromPrivKeys(privKeys)
sigs := signMessages(messages, privKeys)
t.ResetTimer()
for i := 0; i < t.N; i++ {
pubKeys[i%len(pubKeys)].VerifySignature(messages[i%len(messages)], sigs[i%len(sigs)])
}
}