Skip to content

Commit

Permalink
feat: backport caelum bls on v0.42 sdk (#119)
Browse files Browse the repository at this point in the history
* Bls (#104)

* bls signature for basic account

* benchmark for bls and ed25519

* added bls sig verify cost to genesis

* Revert "Merge branch 'fetchai:master' into bls"

This reverts commit a5dd8ea, reversing
changes made to 082e071.

* format using go tools

* nuisance golangci-lint errors

* Bls (#105)

* bls signature for basic account

* benchmark for bls and ed25519

* added bls sig verify cost to genesis

* Revert "Merge branch 'fetchai:master' into bls"

This reverts commit a5dd8ea, reversing
changes made to 082e071.

* format using go tools

* nuisance golangci-lint errors

* POP interfaces in accounts and authentication

* add bls multsig operations

* fixed golangci-lint error

* changes after comments

* change codespace for invalid pop error

* extend migrate with bls cost

* set bls cost multiplier

* fix ante test errors

* feat: Caelum (#111)

* bls signature for basic account

* benchmark for bls and ed25519

* added bls sig verify cost to genesis

* Revert "Merge branch 'fetchai:master' into bls"

This reverts commit a5dd8ea, reversing
changes made to 082e071.

* format using go tools

* nuisance golangci-lint errors

* Bls (#104)

* bls signature for basic account

* benchmark for bls and ed25519

* added bls sig verify cost to genesis

* Revert "Merge branch 'fetchai:master' into bls"

This reverts commit a5dd8ea, reversing
changes made to 082e071.

* format using go tools

* nuisance golangci-lint errors

* POP interfaces in accounts and authentication

* add bls multsig operations

* fixed golangci-lint error

* changes after comments

* initial commit from regen-ledger/x/group v1.0.0

* minor changes to bls12381 key generation

* initial commit from regen-ledger/proto/regen/group v1.0.0

* group module compatibility for fetchai cosomos-sdk

* add bls account restriction to group members

* fix bug in setting pop

* make msg uniqueness checking optional

* add bls basic/aggregate vote

* add checking on empty messages/public keys

* add gas caclulation/consumption for verifying aggregated votes

* minor change to gas calculation for voteagg

* initial commit for orm and types from regen-ledger v2.0.0-beta1

* upgrade testsuite to regen-ledger v2.0.0-beta1

* make bls requirement for group members optional

* add tests for bls related group operations

* client and server for poll and aggregated votes and integration tests

* fix bls related test errors

* fix proto-lint errors

* goimport format

* proto comments

* update blst to v0.3.5 and more tests for bls

* Update x/auth/ante/sigverify.go

Co-authored-by: daeMOn <flavien.binet@gmail.com>

* Update x/group/client/util.go

Co-authored-by: daeMOn <flavien.binet@gmail.com>

Co-authored-by: daeMOn <flavien.binet@gmail.com>

* fix: bls12381 compat with sdk v0.42

* fix: buf lint / breaking command changes

* fix: staking test want more gas

* chores: drop arm from CI test matrix

* chores: fix lint

Co-authored-by: kitounliu <58184672+kitounliu@users.noreply.github.com>
Co-authored-by: kitty <jia.liu@fetch.ai>
  • Loading branch information
3 people committed Nov 24, 2021
1 parent e88e7cd commit b615805
Show file tree
Hide file tree
Showing 35 changed files with 1,632 additions and 88 deletions.
3 changes: 2 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
go-arch: ["amd64", "arm", "arm64"]
# dropped arm because of bls
go-arch: ["amd64"] #, "arm", "arm64"]
steps:
- uses: actions/checkout@v2
- uses: actions/setup-go@v2.1.3
Expand Down
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -396,10 +396,10 @@ proto-format:
find ./ -not -path "./third_party/*" -name *.proto -exec clang-format -i {}; fi

proto-lint:
@$(DOCKER_BUF) check lint --error-format=json
@$(DOCKER_BUF) lint --error-format=json

proto-check-breaking:
@$(DOCKER_BUF) check breaking --against-input $(HTTPS_GIT)#branch=master
@$(DOCKER_BUF) breaking --against-input $(HTTPS_GIT)#branch=master


TM_URL = https://raw.githubusercontent.com/tendermint/tendermint/v0.34.0-rc6/proto/tendermint
Expand Down
2 changes: 1 addition & 1 deletion client/flags/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const (
// failures due to state changes that might occur between the tx simulation
// and the actual run.
DefaultGasAdjustment = 1.0
DefaultGasLimit = 200000
DefaultGasLimit = 210000
GasFlagAuto = "auto"

// DefaultKeyringBackend
Expand Down
5 changes: 5 additions & 0 deletions crypto/codec/amino.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package codec

import (
"github.com/cosmos/cosmos-sdk/crypto/keys/bls12381"
"github.com/tendermint/tendermint/crypto/sr25519"

"github.com/cosmos/cosmos-sdk/codec"
Expand All @@ -22,6 +23,8 @@ func RegisterCrypto(cdc *codec.LegacyAmino) {
secp256k1.PubKeyName, nil)
cdc.RegisterConcrete(&kmultisig.LegacyAminoPubKey{},
kmultisig.PubKeyAminoRoute, nil)
cdc.RegisterConcrete(&bls12381.PubKey{},
bls12381.PubKeyName, nil)

cdc.RegisterInterface((*cryptotypes.PrivKey)(nil), nil)
cdc.RegisterConcrete(sr25519.PrivKey{},
Expand All @@ -30,4 +33,6 @@ func RegisterCrypto(cdc *codec.LegacyAmino) {
ed25519.PrivKeyName, nil)
cdc.RegisterConcrete(&secp256k1.PrivKey{},
secp256k1.PrivKeyName, nil)
cdc.RegisterConcrete(&bls12381.PrivKey{},
bls12381.PrivKeyName, nil)
}
2 changes: 2 additions & 0 deletions crypto/codec/proto.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package codec

import (
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
"github.com/cosmos/cosmos-sdk/crypto/keys/bls12381"
"github.com/cosmos/cosmos-sdk/crypto/keys/ed25519"
"github.com/cosmos/cosmos-sdk/crypto/keys/multisig"
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
Expand All @@ -14,4 +15,5 @@ func RegisterInterfaces(registry codectypes.InterfaceRegistry) {
registry.RegisterImplementations((*cryptotypes.PubKey)(nil), &ed25519.PubKey{})
registry.RegisterImplementations((*cryptotypes.PubKey)(nil), &secp256k1.PubKey{})
registry.RegisterImplementations((*cryptotypes.PubKey)(nil), &multisig.LegacyAminoPubKey{})
registry.RegisterImplementations((*cryptotypes.PubKey)(nil), &bls12381.PubKey{})
}
42 changes: 42 additions & 0 deletions crypto/hd/algo.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package hd

import (
"github.com/cosmos/cosmos-sdk/crypto/keys/bls12381"
bip39 "github.com/cosmos/go-bip39"

"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
Expand All @@ -20,11 +21,15 @@ const (
Ed25519Type = PubKeyType("ed25519")
// Sr25519Type represents the Sr25519Type signature system.
Sr25519Type = PubKeyType("sr25519")
// Bls12381Type represents the Bls12381Type signature system.
Bls12381Type = PubKeyType("bls12381")
)

var (
// Secp256k1 uses the Bitcoin secp256k1 ECDSA parameters.
Secp256k1 = secp256k1Algo{}
// Bls12381 uses blst implememtation of bls signatures
Bls12381 = bls12381Algo{}
)

type DeriveFn func(mnemonic string, bip39Passphrase, hdPath string) ([]byte, error)
Expand Down Expand Up @@ -69,3 +74,40 @@ func (s secp256k1Algo) Generate() GenerateFn {
return &secp256k1.PrivKey{Key: bzArr}
}
}

type bls12381Algo struct {
}

func (s bls12381Algo) Name() PubKeyType {
return Bls12381Type
}

// todo: replace bitcoin private key generation
// Derive derives and returns the bls12381 private key for the given seed and HD path.
func (s bls12381Algo) Derive() DeriveFn {
return func(mnemonic string, bip39Passphrase, hdPath string) ([]byte, error) {
seed, err := bip39.NewSeedWithErrorChecking(mnemonic, bip39Passphrase)
if err != nil {
return nil, err
}

masterPriv, ch := ComputeMastersFromSeed(seed)
if len(hdPath) == 0 {
return masterPriv[:], nil
}
derivedKey, err := DerivePrivateKeyForPath(masterPriv, ch, hdPath)

return derivedKey, err
}
}

// Generate generates a bls12381 private key from the given bytes.
func (s bls12381Algo) Generate() GenerateFn {
return func(bz []byte) types.PrivKey {
var bzArr = make([]byte, bls12381.SeedSize)
copy(bzArr, bz)
sk := bls12381.GenPrivKeyFromSecret(bzArr)

return sk
}
}
2 changes: 1 addition & 1 deletion crypto/keyring/keyring.go
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ type keystore struct {
func newKeystore(kr keyring.Keyring, opts ...Option) keystore {
// Default options for keybase
options := Options{
SupportedAlgos: SigningAlgoList{hd.Secp256k1},
SupportedAlgos: SigningAlgoList{hd.Secp256k1, hd.Bls12381},
SupportedAlgosLedger: SigningAlgoList{hd.Secp256k1},
}

Expand Down
230 changes: 230 additions & 0 deletions crypto/keys/bls12381/bls12381.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
package bls12381

import (
"crypto/subtle"
"fmt"
"io"

"github.com/cosmos/cosmos-sdk/codec"
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
"github.com/cosmos/cosmos-sdk/types/errors"
blst "github.com/supranational/blst/bindings/go"
"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/crypto/tmhash"
)

const (
PrivKeyName = "tendermint/PrivKeyBls12381"
PubKeyName = "tendermint/PubKeyBls12381"
// PubKeySize is is the size, in bytes, of public keys as used in this package.
PubKeySize = 96
// PrivKeySize is the size, in bytes, of private keys as used in this package.
// Uncompressed public key
PrivKeySize = 32
// SignatureSize is the size of a bls signature. Namely the size of a compressed
// G2 point.
SignatureSize = 96
keyType = "bls12381"
SeedSize = 32
)

var _ cryptotypes.PrivKey = &PrivKey{}
var _ codec.AminoMarshaler = &PrivKey{}

// Bytes returns the byte representation of the Private Key.
func (privKey *PrivKey) Bytes() []byte {
return privKey.Key
}

// PubKey performs the point-scalar multiplication from the privKey on the
// generator point to get the pubkey.
func (privKey *PrivKey) PubKey() cryptotypes.PubKey {
sk := new(blst.SecretKey).Deserialize(privKey.Key)
if sk == nil {
panic("Failed to deserialize secret key!")
}
pk := new(blst.P1Affine).From(sk)
pkBytes := pk.Serialize()

return &PubKey{Key: pkBytes}
}

// Sign produces a signature on the provided message.
// This assumes the privkey is wellformed in the golang format.

func (privKey *PrivKey) Sign(msg []byte) ([]byte, error) {
dst := []byte("BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_")
sk := new(blst.SecretKey).Deserialize(privKey.Key)
if sk == nil {
panic("Failed to deserialize secret key!")
}

sig := new(blst.P2Affine).Sign(sk, msg, dst)
if sig == nil {
panic("Failed to sign message!")
}

sigBytes := sig.Compress()

return sigBytes, nil
}

// Equals - you probably don't need to use this.
// Runs in constant time based on length of the
func (privKey *PrivKey) Equals(other cryptotypes.LedgerPrivKey) bool {
return privKey.Type() == other.Type() && subtle.ConstantTimeCompare(privKey.Bytes(), other.Bytes()) == 1
}

func (privKey *PrivKey) Type() string {
return keyType
}

// MarshalAmino overrides Amino binary marshalling.
func (privKey PrivKey) MarshalAmino() ([]byte, error) {
return privKey.Key, nil
}

// UnmarshalAmino overrides Amino binary marshalling.
func (privKey *PrivKey) UnmarshalAmino(bz []byte) error {
if len(bz) != PrivKeySize {
return fmt.Errorf("invalid privkey size")
}
privKey.Key = bz

return nil
}

// MarshalAminoJSON overrides Amino JSON marshalling.
func (privKey PrivKey) MarshalAminoJSON() ([]byte, error) {
// When we marshal to Amino JSON, we don't marshal the "key" field itself,
// just its contents (i.e. the key bytes).
return privKey.MarshalAmino()
}

// UnmarshalAminoJSON overrides Amino JSON marshalling.
func (privKey *PrivKey) UnmarshalAminoJSON(bz []byte) error {
return privKey.UnmarshalAmino(bz)
}

// GenPrivKey generates a new BLS private key on curve bls12-381 private key.
// It uses OS randomness to generate the private key.
func GenPrivKey() *PrivKey {
return &PrivKey{Key: genPrivKey(crypto.CReader())}
}

// genPrivKey generates a new bls12381 private key using the provided reader.
func genPrivKey(rand io.Reader) []byte {
var ikm [SeedSize]byte
_, err := io.ReadFull(rand, ikm[:])
if err != nil {
panic(err)
}

sk := blst.KeyGen(ikm[:])
if sk == nil {
panic("failed to generate secret key!")
}

skBytes := sk.Serialize()

return skBytes
}

// GenPrivKeyFromSecret hashes the secret with SHA2, and uses
// that 32 byte output to create the private key.
// NOTE: secret should be the output of a KDF like bcrypt,
// if it's derived from user input.
func GenPrivKeyFromSecret(secret []byte) *PrivKey {
ikm := crypto.Sha256(secret) // Not Ripemd160 because we want 32 bytes.

sk := blst.KeyGen(ikm)
if sk == nil {
panic("failed to generate secret key from ikm")
}
skBytes := sk.Serialize()

return &PrivKey{Key: skBytes}
}

var _ cryptotypes.PubKey = &PubKey{}
var _ codec.AminoMarshaler = &PubKey{}

// Validate public key, infinity and subgroup checking
func (pubKey *PubKey) Validate() bool {
pk := new(blst.P1Affine).Deserialize(pubKey.Key)
return pk.KeyValidate()
}

// Address is the SHA256-20 of the raw pubkey bytes.
func (pubKey *PubKey) Address() crypto.Address {
if len(pubKey.Key) != PubKeySize {
panic("pubkey is incorrect size")
}
return crypto.Address(tmhash.SumTruncated(pubKey.Key))
}

// Bytes returns the PubKey byte format.
func (pubKey *PubKey) Bytes() []byte {
return pubKey.Key
}

// VerifySignature assumes public key is already validated
func (pubKey *PubKey) VerifySignature(msg []byte, sig []byte) bool {
// make sure we use the same algorithm to sign
pk := new(blst.P1Affine).Deserialize(pubKey.Key)
if pk == nil {
panic("Failed to deserialize public key")
}

sigma := new(blst.P2Affine).Uncompress(sig)
if sigma == nil {
panic("Failed to deserialize signature")
}

dst := []byte("BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_")

return sigma.Verify(true, pk, false, msg, dst)
}

func (pubKey *PubKey) String() string {
return fmt.Sprintf("PubKeyBls12381{%X}", pubKey.Key)
}

func (pubKey *PubKey) Type() string {
return keyType
}

func (pubKey *PubKey) Equals(other cryptotypes.PubKey) bool {
if pubKey.Type() != other.Type() {
return false
}

return subtle.ConstantTimeCompare(pubKey.Bytes(), other.Bytes()) == 1
}

// MarshalAmino overrides Amino binary marshalling.
func (pubKey PubKey) MarshalAmino() ([]byte, error) {
return pubKey.Key, nil
}

// UnmarshalAmino overrides Amino binary marshalling.
func (pubKey *PubKey) UnmarshalAmino(bz []byte) error {
if len(bz) != PubKeySize {
return errors.Wrap(errors.ErrInvalidPubKey, "invalid pubkey size")
}
pubKey.Key = bz

return nil
}

// MarshalAminoJSON overrides Amino JSON marshalling.
func (pubKey PubKey) MarshalAminoJSON() ([]byte, error) {
// When we marshal to Amino JSON, we don't marshal the "key" field itself,
// just its contents (i.e. the key bytes).
return pubKey.MarshalAmino()
}

// UnmarshalAminoJSON overrides Amino JSON marshalling.
func (pubKey *PubKey) UnmarshalAminoJSON(bz []byte) error {
return pubKey.UnmarshalAmino(bz)
}
Loading

0 comments on commit b615805

Please sign in to comment.