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

Bls #104

Merged
merged 8 commits into from
Jul 20, 2021
Merged

Bls #104

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
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
225 changes: 225 additions & 0 deletions crypto/keys/bls12381/bls12381.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
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 secp256k1 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.
skBytes := blst.KeyGen(ikm).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_")
ejfitzgerald marked this conversation as resolved.
Show resolved Hide resolved

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)
}
36 changes: 36 additions & 0 deletions crypto/keys/bls12381/bls12381_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package bls12381_test

import (
"github.com/cosmos/cosmos-sdk/crypto/keys/bls12381"
bench "github.com/cosmos/cosmos-sdk/crypto/keys/internal/benchmarking"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/tendermint/tendermint/crypto"
"testing"
)

func TestSignAndValidateBls12381(t *testing.T) {
privKey := bls12381.GenPrivKey()
pubKey := privKey.PubKey()

msg := crypto.CRandBytes(1000)
sig, err := privKey.Sign(msg)
require.Nil(t, err)

// Test the signature
assert.True(t, pubKey.VerifySignature(msg, sig))

}

func BenchmarkSignBls(b *testing.B) {
privKey := bls12381.GenPrivKey()

bench.BenchmarkSigning(b, privKey)

}

func BenchmarkVerifyBls(b *testing.B) {
privKey := bls12381.GenPrivKey()

bench.BenchmarkVerification(b, privKey)
}
Loading