BLS library from Ostracon (Tendermint fork)

* Files retrieved from Ostracon implementation on Github, master
     branch, commit 051475802c94f46763cae4ef4c18e01d4cfa952e.
This commit is contained in:
Daniel Cason
2022-04-19 14:41:32 +02:00
parent 006ebf50df
commit 2bd0a6527e
3 changed files with 612 additions and 0 deletions

View File

@@ -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
View 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
}

View 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))
}