Skip to content

Commit

Permalink
introduce BLS12-381 signature key
Browse files Browse the repository at this point in the history
  • Loading branch information
torao committed Jun 22, 2020
1 parent b8b7770 commit 3e6b745
Show file tree
Hide file tree
Showing 10 changed files with 304 additions and 17 deletions.
15 changes: 12 additions & 3 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ commands:
command: |
sudo apt-get update && sudo apt-get -y install libtool
make libsodium
setup_bls_library_build:
steps:
- run:
name: "Install build tools to build BLS library"
command: |
sudo apt-get update && sudo apt-get -y install gcc
run_test:
parameters:
script_path:
Expand All @@ -48,6 +54,7 @@ commands:
keys:
- go-mod-v1-{{ checksum "go.sum" }}
- make_libsodium
- setup_bls_library_build
- run:
name: "Running test"
command: |
Expand Down Expand Up @@ -75,6 +82,7 @@ jobs:
- attach_workspace:
at: /tmp/workspace
- make_libsodium
- setup_bls_library_build
- run:
name: "Build binaries"
command: make install install_abci
Expand Down Expand Up @@ -147,7 +155,7 @@ jobs:
name: run localnet and exit on failure
command: |
set -x
docker run --rm -v "$PWD":/go/src/github.com/tendermint/tendermint -w /go/src/github.com/tendermint/tendermint golang:1.14.1-alpine /bin/sh -c "apk add --update make && make build-linux"
make build-linux
make localnet-start &
./scripts/localnet-blocks-test.sh 40 5 10 localhost
Expand All @@ -165,6 +173,7 @@ jobs:
steps:
- checkout_with_submodules
- make_libsodium
- setup_bls_library_build
- run: mkdir -p $GOPATH/src/github.com/tendermint
- run: ln -sf /home/circleci/project $GOPATH/src/github.com/tendermint/tendermint
- run: bash test/p2p/circleci.sh << parameters.ipv >>
Expand Down Expand Up @@ -360,9 +369,9 @@ jobs:
./scripts/get_nodejs.sh
# build the binaries with a proper version of Go
# Build Tendermint
docker run --rm -v "$PWD":/go/src/github.com/tendermint/tendermint -w /go/src/github.com/tendermint/tendermint golang:1.14.1-alpine /bin/sh -c "apk add --update make git && make build-linux"
make build-linux
# Build contract-tests
docker run --rm -v "$PWD":/go/src/github.com/tendermint/tendermint -w /go/src/github.com/tendermint/tendermint ubuntu:20.10 ./scripts/prepare_dredd_test.sh
docker run --rm -v "$$PWD":/go/src/github.com/tendermint/tendermint -w /go/src/github.com/tendermint/tendermint ubuntu:20.10 ./scripts/prepare_dredd_test.sh
# This docker image works with go 1.7, we can install here the hook handler that contract-tests is going to use
go get github.com/snikch/goodman/cmd/goodman
make contract-tests
Expand Down
5 changes: 3 additions & 2 deletions CHANGELOG_PENDING.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

- State
- [state] [\#92](https://github.com/line/tendermint/pull/92) Genesis state

- CLI/RPC/Config

- Apps
Expand All @@ -17,11 +17,12 @@

- [types] [\#83](https://github.com/line/tendermint/pull/83) Add `StakingPower` to `Validator`
- [consensus] [\#83](https://github.com/line/tendermint/pull/83) Change calculation of `VotingPower`

### FEATURES:
- [rpc] [\#78](https://github.com/line/tendermint/pull/78) Add `Voters` rpc
- [consensus] [\#83](https://github.com/line/tendermint/pull/83) Selection voters using random sampling without replacement
- [consensus] [\#92](https://github.com/line/tendermint/pull/92) Apply calculation of voter count
- [BLS] [\#81](https://github.com/line/tendermint/issues/81) Modify to generate at the same time as Ed25519 key generation

### IMPROVEMENTS:

Expand Down
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -222,8 +222,8 @@ build-docker:
###############################################################################

# Build linux binary on other platforms
build-linux: tools $(PREPARE_LIBSODIUM_TARGET) $(LIBSODIUM_TARGET)
GOOS=linux GOARCH=amd64 $(MAKE) build
build-linux:
docker run --rm -v `pwd`:/go/src/github.com/tendermint/tendermint -w /go/src/github.com/tendermint/tendermint golang:1.14.1-alpine /bin/sh -c "apk add --update git make gcc libc-dev build-base && make build"
.PHONY: build-linux

build-docker-localnode:
Expand Down
143 changes: 143 additions & 0 deletions crypto/bls/bls.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
package bls

import (
"bytes"
"crypto/subtle"
"fmt"

"github.com/herumi/bls-eth-go-binary/bls"
amino "github.com/tendermint/go-amino"
"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/crypto/tmhash"
)

var _ crypto.PrivKey = PrivKeyBLS12{}

const (
PrivKeyAminoName = "tendermint/PrivKeyBLS12"
PubKeyAminoName = "tendermint/PubKeyBLS12"
PrivKeyBLS12Size = 32
PubKeyBLS12Size = 48
SignatureSize = 96
)

var cdc = amino.NewCodec()

func init() {
cdc.RegisterInterface((*crypto.PubKey)(nil), nil)
cdc.RegisterConcrete(PubKeyBLS12{},
PubKeyAminoName, nil)

cdc.RegisterInterface((*crypto.PrivKey)(nil), nil)
cdc.RegisterConcrete(PrivKeyBLS12{},
PrivKeyAminoName, nil)

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

// PrivKeyBLS12 implements crypto.PrivKey.
type PrivKeyBLS12 [PrivKeyBLS12Size]byte

// GenPrivKey generates a new BLS12-381 private key.
func GenPrivKey() PrivKeyBLS12 {
sigKey := bls.SecretKey{}
sigKey.SetByCSPRNG()
sigKeyBinary := PrivKeyBLS12{}
copy(sigKeyBinary[:], sigKey.Serialize())
return sigKeyBinary
}

// Bytes marshals the privkey using amino encoding.
func (privKey PrivKeyBLS12) Bytes() []byte {
return cdc.MustMarshalBinaryBare(privKey)
}

// Sign produces a signature on the provided message.
func (privKey PrivKeyBLS12) Sign(msg []byte) ([]byte, error) {
blsKey := bls.SecretKey{}
err := blsKey.Deserialize(privKey[:])
if err != nil {
return nil, err
}
sign := blsKey.SignByte(msg)
return sign.Serialize(), nil
}

// PubKey gets the corresponding public key from the private key.
func (privKey PrivKeyBLS12) 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 := PubKeyBLS12{}
copy(pubKeyBinary[:], pubKey.Serialize())
return pubKeyBinary
}

// Equals - you probably don't need to use this.
// Runs in constant time based on length of the keys.
func (privKey PrivKeyBLS12) Equals(other crypto.PrivKey) bool {
if otherEd, ok := other.(PrivKeyBLS12); ok {
return subtle.ConstantTimeCompare(privKey[:], otherEd[:]) == 1
}
return false
}

var _ crypto.PubKey = PubKeyBLS12{}

// PubKeyBLS12 implements crypto.PubKey for the BLS12-381 signature scheme.
type PubKeyBLS12 [PubKeyBLS12Size]byte

// Address is the SHA256-20 of the raw pubkey bytes.
func (pubKey PubKeyBLS12) Address() crypto.Address {
return tmhash.SumTruncated(pubKey[:])
}

// Bytes marshals the PubKey using amino encoding.
func (pubKey PubKeyBLS12) Bytes() []byte {
bz, err := cdc.MarshalBinaryBare(pubKey)
if err != nil {
panic(err)
}
return bz
}

func (pubKey PubKeyBLS12) VerifyBytes(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
}
return blsSign.VerifyByte(&blsPubKey, msg)
}

func (pubKey PubKeyBLS12) String() string {
return fmt.Sprintf("PubKeyBLS12{%X}", pubKey[:])
}

// nolint: golint
func (pubKey PubKeyBLS12) Equals(other crypto.PubKey) bool {
if otherEd, ok := other.(PubKeyBLS12); ok {
return bytes.Equal(pubKey[:], otherEd[:])
}
return false
}
127 changes: 127 additions & 0 deletions crypto/bls/bls_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
package bls_test

import (
"bytes"
"fmt"
"testing"

b "github.com/herumi/bls-eth-go-binary/bls"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/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.PrivKeyBLS12Size {
t.Fatalf("The constant size %d of the private key is different from the actual size %d.",
bls.PrivKeyBLS12Size, 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.PubKeyBLS12Size {
t.Fatalf("The constant size %d of the public key is different from the actual size %d.",
bls.PubKeyBLS12Size, 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 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.VerifyBytes(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.VerifyBytes(msg, sig))
}
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ require (
github.com/golang/protobuf v1.4.0
github.com/gorilla/websocket v1.4.2
github.com/gtank/merlin v0.1.1
github.com/herumi/bls-eth-go-binary v0.0.0-20200522010937-01d282b5380b
github.com/libp2p/go-buffer-pool v0.0.2
github.com/magiconair/properties v1.8.1
github.com/pkg/errors v0.9.1
Expand Down
Loading

0 comments on commit 3e6b745

Please sign in to comment.