Skip to content

Commit

Permalink
bls signature for basic account
Browse files Browse the repository at this point in the history
  • Loading branch information
kitty committed Jul 14, 2021
1 parent 717e05b commit 8fee622
Show file tree
Hide file tree
Showing 11 changed files with 844 additions and 1 deletion.
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{})
}
45 changes: 45 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,43 @@ 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
233 changes: 233 additions & 0 deletions crypto/keys/bls12381/bls12381.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
package bls12381

import (
"crypto/subtle"
"fmt"
"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"
"io"

)

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
// Compressed signature
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)
pk_bytes := pk.Serialize()

return &PubKey{Key: pk_bytes}
}


// 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!")
}

sig_bytes := sig.Compress()

return sig_bytes, 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!")
}

sk_bytes := sk.Serialize()

return sk_bytes
}

// 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_bytes := blst.KeyGen(ikm[:]).Serialize()

return &PrivKey{Key: sk_bytes}
}


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
}

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

import (
"github.com/cosmos/cosmos-sdk/crypto/keys/bls12381"
"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))

}
Loading

0 comments on commit 8fee622

Please sign in to comment.