Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce BLS12-381 Signature Key #87

Merged
merged 4 commits into from
Jun 25, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 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"
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"
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,7 +369,7 @@ 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"
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"
# 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
# This docker image works with go 1.7, we can install here the hook handler that contract-tests is going to use
Expand Down
3 changes: 2 additions & 1 deletion CHANGELOG_PENDING.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
### BREAKING CHANGES:

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

- CLI/RPC/Config

Expand All @@ -14,8 +15,8 @@

- Go API


### FEATURES:
- [BLS] [\#81](https:/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
142 changes: 142 additions & 0 deletions crypto/bls/bls.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
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 {
panic(fmt.Sprintf("Failed to copy the private key: %s", 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("Failed to copy the private key: %s", err))
}
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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason why you used this instead of bytes.Equal()?

Copy link
Contributor Author

@torao torao Jun 24, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm sure that this is a defensive measure against a side-channel attack (timing attack).

The bytes.Equals() is more efficient because it returns early as soon as it finds a difference byte, but such behaviour can be a vulnerability in a security library. The difference in the response time of such a function provides a hint to the attacker who measures it to determine if the input prefix matches.

As you can see by searching the Tendermint repository in ConstantTimeCompare(), the Equals() for all of ed25519, secp256k1 and sr25519 private key implementations in Tendermint use like this.

Copy link
Contributor

@egonspace egonspace Jun 24, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I learned from you. :)

}
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 {
panic(fmt.Sprintf("Failed to copy the public key: [%X] %s", pubKey[:], err))
}
blsSign := bls.Sign{}
err = blsSign.Deserialize(sig)
if err != nil {
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
}
Loading