Merge PR #5439: Keybase: Multiple Signature Algorithms

This commit is contained in:
Sunny Aggarwal 2020-01-14 10:40:10 -05:00 committed by Alexander Bezobchuk
parent d452e9398b
commit f367087731
21 changed files with 419 additions and 201 deletions

View File

@ -101,7 +101,16 @@ if the provided arguments are invalid.
* (modules) [\#5299](https://github.com/cosmos/cosmos-sdk/pull/5299) `HandleDoubleSign` along with params `MaxEvidenceAge` * (modules) [\#5299](https://github.com/cosmos/cosmos-sdk/pull/5299) `HandleDoubleSign` along with params `MaxEvidenceAge`
and `DoubleSignJailEndTime` have moved from the `x/slashing` module to the `x/evidence` module. and `DoubleSignJailEndTime` have moved from the `x/slashing` module to the `x/evidence` module.
* (keys) [\#4941](https://github.com/cosmos/cosmos-sdk/issues/4941) Initializing a new keybase through `NewKeyringFromHomeFlag`, `NewKeyringFromDir`, `NewKeyBaseFromHomeFlag`, `NewKeyBaseFromDir`, or `NewInMemory` functions now accept optional parameters of type `KeybaseOption`. These optional parameters are also added on the keys subcommands functions, which are now public, and allows these options to be set on the commands or ignored to default to previous behavior. * (keys) [\#4941](https://github.com/cosmos/cosmos-sdk/issues/4941) Initializing a new keybase through `NewKeyringFromHomeFlag`, `NewKeyringFromDir`, `NewKeyBaseFromHomeFlag`, `NewKeyBaseFromDir`, or `NewInMemory` functions now accept optional parameters of type `KeybaseOption`. These optional parameters are also added on the keys subcommands functions, which are now public, and allows these options to be set on the commands or ignored to default to previous behavior.
* The option introduced in this PR is `WithKeygenFunc` which allows a custom bytes to key implementation to be defined when keys are created. * [\#5439](https://github.com/cosmos/cosmos-sdk/pull/5439) Further modularization was done to the `keybase`
package to make it more suitable for use with different key formats and algorithms:
* The `WithKeygenFunc` function added as a `KeybaseOption` which allows a custom bytes to key
implementation to be defined when keys are created.
* The `WithDeriveFunc` function added as a `KeybaseOption` allows custom logic for deriving a key
from a mnemonic, bip39 password, and HD Path.
* BIP44 is no longer build into `keybase.CreateAccount()`. It is however the default when using
the `client/keys` add command.
* `SupportedAlgos` and `SupportedAlgosLedger` functions return a slice of `SigningAlgo`s that are
supported by the keybase and the ledger integration respectively.
* (simapp) [\#5419](https://github.com/cosmos/cosmos-sdk/pull/5419) simapp/helpers.GenTx() now accepts a gas argument. * (simapp) [\#5419](https://github.com/cosmos/cosmos-sdk/pull/5419) simapp/helpers.GenTx() now accepts a gas argument.
* (baseapp) [\#5455](https://github.com/cosmos/cosmos-sdk/issues/5455) An `sdk.Context` is passed into the `router.Route()` * (baseapp) [\#5455](https://github.com/cosmos/cosmos-sdk/issues/5455) An `sdk.Context` is passed into the `router.Route()`
function. function.
@ -181,6 +190,9 @@ that allows for arbitrary vesting periods.
* Introduces cli commands and rest routes to query historical information at a given height * Introduces cli commands and rest routes to query historical information at a given height
* (modules) [\#5249](https://github.com/cosmos/cosmos-sdk/pull/5249) Funds are now allowed to be directly sent to the community pool (via the distribution module account). * (modules) [\#5249](https://github.com/cosmos/cosmos-sdk/pull/5249) Funds are now allowed to be directly sent to the community pool (via the distribution module account).
* (keys) [\#4941](https://github.com/cosmos/cosmos-sdk/issues/4941) Introduce keybase option to allow overriding the default private key implementation of a key generated through the `keys add` cli command. * (keys) [\#4941](https://github.com/cosmos/cosmos-sdk/issues/4941) Introduce keybase option to allow overriding the default private key implementation of a key generated through the `keys add` cli command.
* (keys) [\#5439](https://github.com/cosmos/cosmos-sdk/pull/5439) Flags `--algo` and `--hd-path` are added to
`keys add` command in order to make use of keybase modularized. By default, it uses (0, 0) bip44
HD path and secp256k1 keys, so is non-breaking.
* (types) [\#5447](https://github.com/cosmos/cosmos-sdk/pull/5447) Added `ApproxRoot` function to sdk.Decimal type in order to get the nth root for a decimal number, where n is a positive integer. * (types) [\#5447](https://github.com/cosmos/cosmos-sdk/pull/5447) Added `ApproxRoot` function to sdk.Decimal type in order to get the nth root for a decimal number, where n is a positive integer.
* An `ApproxSqrt` function was also added for convenience around the common case of n=2. * An `ApproxSqrt` function was also added for convenience around the common case of n=2.

View File

@ -32,6 +32,8 @@ const (
flagIndex = "index" flagIndex = "index"
flagMultisig = "multisig" flagMultisig = "multisig"
flagNoSort = "nosort" flagNoSort = "nosort"
flagHDPath = "hd-path"
flagKeyAlgo = "algo"
// DefaultKeyPass contains the default key password for genesis transactions // DefaultKeyPass contains the default key password for genesis transactions
DefaultKeyPass = "12345678" DefaultKeyPass = "12345678"
@ -71,9 +73,11 @@ the flag --nosort is set.
cmd.Flags().Bool(flagRecover, false, "Provide seed phrase to recover existing key instead of creating") cmd.Flags().Bool(flagRecover, false, "Provide seed phrase to recover existing key instead of creating")
cmd.Flags().Bool(flagNoBackup, false, "Don't print out seed phrase (if others are watching the terminal)") cmd.Flags().Bool(flagNoBackup, false, "Don't print out seed phrase (if others are watching the terminal)")
cmd.Flags().Bool(flagDryRun, false, "Perform action, but don't add key to local keystore") cmd.Flags().Bool(flagDryRun, false, "Perform action, but don't add key to local keystore")
cmd.Flags().String(flagHDPath, "", "Manual HD Path derivation (overrides BIP44 config)")
cmd.Flags().Uint32(flagAccount, 0, "Account number for HD derivation") cmd.Flags().Uint32(flagAccount, 0, "Account number for HD derivation")
cmd.Flags().Uint32(flagIndex, 0, "Address index number for HD derivation") cmd.Flags().Uint32(flagIndex, 0, "Address index number for HD derivation")
cmd.Flags().Bool(flags.FlagIndentResponse, false, "Add indent to JSON response") cmd.Flags().Bool(flags.FlagIndentResponse, false, "Add indent to JSON response")
cmd.Flags().String(flagKeyAlgo, string(keys.Secp256k1), "Key signing algorithm to generate keys for")
return cmd return cmd
} }
@ -112,6 +116,14 @@ func RunAddCmd(cmd *cobra.Command, args []string, kb keys.Keybase, inBuf *bufio.
interactive := viper.GetBool(flagInteractive) interactive := viper.GetBool(flagInteractive)
showMnemonic := !viper.GetBool(flagNoBackup) showMnemonic := !viper.GetBool(flagNoBackup)
algo := keys.SigningAlgo(viper.GetString(flagKeyAlgo))
if algo == keys.SigningAlgo("") {
algo = keys.Secp256k1
}
if !keys.IsSupportedAlgorithm(kb.SupportedAlgos(), algo) {
return keys.ErrUnsupportedSigningAlgo
}
if !viper.GetBool(flagDryRun) { if !viper.GetBool(flagDryRun) {
_, err = kb.Get(name) _, err = kb.Get(name)
if err == nil { if err == nil {
@ -164,7 +176,7 @@ func RunAddCmd(cmd *cobra.Command, args []string, kb keys.Keybase, inBuf *bufio.
if err != nil { if err != nil {
return err return err
} }
_, err = kb.CreateOffline(name, pk) _, err = kb.CreateOffline(name, pk, algo)
if err != nil { if err != nil {
return err return err
} }
@ -174,8 +186,26 @@ func RunAddCmd(cmd *cobra.Command, args []string, kb keys.Keybase, inBuf *bufio.
account := uint32(viper.GetInt(flagAccount)) account := uint32(viper.GetInt(flagAccount))
index := uint32(viper.GetInt(flagIndex)) index := uint32(viper.GetInt(flagIndex))
useBIP44 := !viper.IsSet(flagHDPath)
var hdPath string
if useBIP44 {
hdPath = keys.CreateHDPath(account, index).String()
} else {
hdPath = viper.GetString(flagHDPath)
}
// If we're using ledger, only thing we need is the path and the bech32 prefix. // If we're using ledger, only thing we need is the path and the bech32 prefix.
if viper.GetBool(flags.FlagUseLedger) { if viper.GetBool(flags.FlagUseLedger) {
if !useBIP44 {
return errors.New("cannot set custom bip32 path with ledger")
}
if !keys.IsSupportedAlgorithm(kb.SupportedAlgosLedger(), algo) {
return keys.ErrUnsupportedSigningAlgo
}
bech32PrefixAccAddr := sdk.GetConfig().GetBech32AccountAddrPrefix() bech32PrefixAccAddr := sdk.GetConfig().GetBech32AccountAddrPrefix()
info, err := kb.CreateLedger(name, keys.Secp256k1, bech32PrefixAccAddr, account, index) info, err := kb.CreateLedger(name, keys.Secp256k1, bech32PrefixAccAddr, account, index)
if err != nil { if err != nil {
@ -240,7 +270,7 @@ func RunAddCmd(cmd *cobra.Command, args []string, kb keys.Keybase, inBuf *bufio.
} }
} }
info, err := kb.CreateAccount(name, mnemonic, bip39Passphrase, DefaultKeyPass, account, index) info, err := kb.CreateAccount(name, mnemonic, bip39Passphrase, DefaultKeyPass, hdPath, algo)
if err != nil { if err != nil {
return err return err
} }

View File

@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/crypto/keys"
"github.com/cosmos/cosmos-sdk/tests" "github.com/cosmos/cosmos-sdk/tests"
) )
@ -44,13 +45,13 @@ func Test_runDeleteCmd(t *testing.T) {
if runningUnattended { if runningUnattended {
mockIn.Reset("testpass1\ntestpass1\n") mockIn.Reset("testpass1\ntestpass1\n")
} }
_, err = kb.CreateAccount(fakeKeyName1, tests.TestMnemonic, "", "", 0, 0) _, err = kb.CreateAccount(fakeKeyName1, tests.TestMnemonic, "", "", "0", keys.Secp256k1)
require.NoError(t, err) require.NoError(t, err)
if runningUnattended { if runningUnattended {
mockIn.Reset("testpass1\ntestpass1\n") mockIn.Reset("testpass1\ntestpass1\n")
} }
_, err = kb.CreateAccount(fakeKeyName2, tests.TestMnemonic, "", "", 0, 1) _, err = kb.CreateAccount(fakeKeyName2, tests.TestMnemonic, "", "", "1", keys.Secp256k1)
require.NoError(t, err) require.NoError(t, err)
if runningUnattended { if runningUnattended {

View File

@ -7,6 +7,7 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/crypto/keys"
"github.com/cosmos/cosmos-sdk/tests" "github.com/cosmos/cosmos-sdk/tests"
) )
@ -32,7 +33,7 @@ func Test_runExportCmd(t *testing.T) {
if runningUnattended { if runningUnattended {
mockIn.Reset("testpass1\ntestpass1\n") mockIn.Reset("testpass1\ntestpass1\n")
} }
_, err = kb.CreateAccount("keyname1", tests.TestMnemonic, "", "123456789", 0, 0) _, err = kb.CreateAccount("keyname1", tests.TestMnemonic, "", "123456789", "", keys.Secp256k1)
require.NoError(t, err) require.NoError(t, err)
// Now enter password // Now enter password

View File

@ -8,6 +8,7 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/crypto/keys"
"github.com/cosmos/cosmos-sdk/tests" "github.com/cosmos/cosmos-sdk/tests"
) )
@ -36,7 +37,7 @@ func Test_runListCmd(t *testing.T) {
mockIn.Reset("testpass1\ntestpass1\n") mockIn.Reset("testpass1\ntestpass1\n")
} }
_, err = kb.CreateAccount("something", tests.TestMnemonic, "", "", 0, 0) _, err = kb.CreateAccount("something", tests.TestMnemonic, "", "", "", keys.Secp256k1)
require.NoError(t, err) require.NoError(t, err)
defer func() { defer func() {

View File

@ -58,13 +58,13 @@ func Test_runShowCmd(t *testing.T) {
if runningUnattended { if runningUnattended {
mockIn.Reset("testpass1\ntestpass1\n") mockIn.Reset("testpass1\ntestpass1\n")
} }
_, err = kb.CreateAccount(fakeKeyName1, tests.TestMnemonic, "", "", 0, 0) _, err = kb.CreateAccount(fakeKeyName1, tests.TestMnemonic, "", "", "0", keys.Secp256k1)
require.NoError(t, err) require.NoError(t, err)
if runningUnattended { if runningUnattended {
mockIn.Reset("testpass1\n") mockIn.Reset("testpass1\n")
} }
_, err = kb.CreateAccount(fakeKeyName2, tests.TestMnemonic, "", "", 0, 1) _, err = kb.CreateAccount(fakeKeyName2, tests.TestMnemonic, "", "", "1", keys.Secp256k1)
require.NoError(t, err) require.NoError(t, err)
// Now try single key // Now try single key

View File

@ -7,6 +7,7 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/crypto/keys"
"github.com/cosmos/cosmos-sdk/tests" "github.com/cosmos/cosmos-sdk/tests"
) )
@ -38,9 +39,9 @@ func Test_runUpdateCmd(t *testing.T) {
kb, err := NewKeyBaseFromHomeFlag() kb, err := NewKeyBaseFromHomeFlag()
assert.NoError(t, err) assert.NoError(t, err)
_, err = kb.CreateAccount(fakeKeyName1, tests.TestMnemonic, "", "", 0, 0) _, err = kb.CreateAccount(fakeKeyName1, tests.TestMnemonic, "", "", "0", keys.Secp256k1)
assert.NoError(t, err) assert.NoError(t, err)
_, err = kb.CreateAccount(fakeKeyName2, tests.TestMnemonic, "", "", 0, 1) _, err = kb.CreateAccount(fakeKeyName2, tests.TestMnemonic, "", "", "1", keys.Secp256k1)
assert.NoError(t, err) assert.NoError(t, err)
// Try again now that we have keys // Try again now that we have keys

View File

@ -10,7 +10,6 @@ import (
cryptoAmino "github.com/tendermint/tendermint/crypto/encoding/amino" cryptoAmino "github.com/tendermint/tendermint/crypto/encoding/amino"
dbm "github.com/tendermint/tm-db" dbm "github.com/tendermint/tm-db"
"github.com/cosmos/cosmos-sdk/crypto/keys/hd"
"github.com/cosmos/cosmos-sdk/crypto/keys/keyerror" "github.com/cosmos/cosmos-sdk/crypto/keys/keyerror"
"github.com/cosmos/cosmos-sdk/crypto/keys/mintkey" "github.com/cosmos/cosmos-sdk/crypto/keys/mintkey"
"github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types"
@ -57,7 +56,7 @@ const (
var ( var (
// ErrUnsupportedSigningAlgo is raised when the caller tries to use a // ErrUnsupportedSigningAlgo is raised when the caller tries to use a
// different signing scheme than secp256k1. // different signing scheme than secp256k1.
ErrUnsupportedSigningAlgo = errors.New("unsupported signing algo: only secp256k1 is supported") ErrUnsupportedSigningAlgo = errors.New("unsupported signing algo")
// ErrUnsupportedLanguage is raised when the caller tries to use a // ErrUnsupportedLanguage is raised when the caller tries to use a
// different language than english for creating a mnemonic sentence. // different language than english for creating a mnemonic sentence.
@ -101,18 +100,10 @@ func (kb dbKeybase) CreateMnemonic(
// CreateAccount converts a mnemonic to a private key and persists it, encrypted // CreateAccount converts a mnemonic to a private key and persists it, encrypted
// with the given password. // with the given password.
func (kb dbKeybase) CreateAccount( func (kb dbKeybase) CreateAccount(
name, mnemonic, bip39Passwd, encryptPasswd string, account uint32, index uint32, name, mnemonic, bip39Passwd, encryptPasswd, hdPath string, algo SigningAlgo,
) (Info, error) { ) (Info, error) {
return kb.base.CreateAccount(kb, name, mnemonic, bip39Passwd, encryptPasswd, account, index) return kb.base.CreateAccount(kb, name, mnemonic, bip39Passwd, encryptPasswd, hdPath, algo)
}
// Derive computes a BIP39 seed from th mnemonic and bip39Passwd.
func (kb dbKeybase) Derive(
name, mnemonic, bip39Passphrase, encryptPasswd string, params hd.BIP44Params,
) (Info, error) {
return kb.base.Derive(kb, name, mnemonic, bip39Passphrase, encryptPasswd, params)
} }
// CreateLedger creates a new locally-stored reference to a Ledger keypair. // CreateLedger creates a new locally-stored reference to a Ledger keypair.
@ -126,8 +117,8 @@ func (kb dbKeybase) CreateLedger(
// CreateOffline creates a new reference to an offline keypair. It returns the // CreateOffline creates a new reference to an offline keypair. It returns the
// created key info. // created key info.
func (kb dbKeybase) CreateOffline(name string, pub tmcrypto.PubKey) (Info, error) { func (kb dbKeybase) CreateOffline(name string, pub tmcrypto.PubKey, algo SigningAlgo) (Info, error) {
return kb.base.writeOfflineKey(kb, name, pub), nil return kb.base.writeOfflineKey(kb, name, pub, algo), nil
} }
// CreateMulti creates a new reference to a multisig (offline) keypair. It // CreateMulti creates a new reference to a multisig (offline) keypair. It
@ -199,7 +190,7 @@ func (kb dbKeybase) Sign(name, passphrase string, msg []byte) (sig []byte, pub t
return return
} }
priv, err = mintkey.UnarmorDecryptPrivKey(i.PrivKeyArmor, passphrase) priv, _, err = mintkey.UnarmorDecryptPrivKey(i.PrivKeyArmor, passphrase)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
@ -238,7 +229,7 @@ func (kb dbKeybase) ExportPrivateKeyObject(name string, passphrase string) (tmcr
return nil, err return nil, err
} }
priv, err = mintkey.UnarmorDecryptPrivKey(linfo.PrivKeyArmor, passphrase) priv, _, err = mintkey.UnarmorDecryptPrivKey(linfo.PrivKeyArmor, passphrase)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -272,7 +263,7 @@ func (kb dbKeybase) ExportPubKey(name string) (armor string, err error) {
return return
} }
return mintkey.ArmorPubKeyBytes(info.GetPubKey().Bytes()), nil return mintkey.ArmorPubKeyBytes(info.GetPubKey().Bytes(), string(info.GetAlgo())), nil
} }
// ExportPrivKey returns a private key in ASCII armored format. // ExportPrivKey returns a private key in ASCII armored format.
@ -285,7 +276,12 @@ func (kb dbKeybase) ExportPrivKey(name string, decryptPassphrase string,
return "", err return "", err
} }
return mintkey.EncryptArmorPrivKey(priv, encryptPassphrase), nil info, err := kb.Get(name)
if err != nil {
return "", err
}
return mintkey.EncryptArmorPrivKey(priv, encryptPassphrase, string(info.GetAlgo())), nil
} }
// ImportPrivKey imports a private key in ASCII armor format. It returns an // ImportPrivKey imports a private key in ASCII armor format. It returns an
@ -296,12 +292,12 @@ func (kb dbKeybase) ImportPrivKey(name string, armor string, passphrase string)
return errors.New("Cannot overwrite key " + name) return errors.New("Cannot overwrite key " + name)
} }
privKey, err := mintkey.UnarmorDecryptPrivKey(armor, passphrase) privKey, algo, err := mintkey.UnarmorDecryptPrivKey(armor, passphrase)
if err != nil { if err != nil {
return errors.Wrap(err, "couldn't import private key") return errors.Wrap(err, "couldn't import private key")
} }
kb.writeLocalKey(name, privKey, passphrase) kb.writeLocalKey(name, privKey, passphrase, SigningAlgo(algo))
return nil return nil
} }
@ -329,7 +325,7 @@ func (kb dbKeybase) ImportPubKey(name string, armor string) (err error) {
return errors.New("Cannot overwrite data for name " + name) return errors.New("Cannot overwrite data for name " + name)
} }
pubBytes, err := mintkey.UnarmorPubKeyBytes(armor) pubBytes, algo, err := mintkey.UnarmorPubKeyBytes(armor)
if err != nil { if err != nil {
return return
} }
@ -339,7 +335,7 @@ func (kb dbKeybase) ImportPubKey(name string, armor string) (err error) {
return return
} }
kb.base.writeOfflineKey(kb, name, pubKey) kb.base.writeOfflineKey(kb, name, pubKey, SigningAlgo(algo))
return return
} }
@ -355,7 +351,7 @@ func (kb dbKeybase) Delete(name, passphrase string, skipPass bool) error {
} }
if linfo, ok := info.(localInfo); ok && !skipPass { if linfo, ok := info.(localInfo); ok && !skipPass {
if _, err = mintkey.UnarmorDecryptPrivKey(linfo.PrivKeyArmor, passphrase); err != nil { if _, _, err = mintkey.UnarmorDecryptPrivKey(linfo.PrivKeyArmor, passphrase); err != nil {
return err return err
} }
} }
@ -382,7 +378,7 @@ func (kb dbKeybase) Update(name, oldpass string, getNewpass func() (string, erro
case localInfo: case localInfo:
linfo := i linfo := i
key, err := mintkey.UnarmorDecryptPrivKey(linfo.PrivKeyArmor, oldpass) key, _, err := mintkey.UnarmorDecryptPrivKey(linfo.PrivKeyArmor, oldpass)
if err != nil { if err != nil {
return err return err
} }
@ -392,7 +388,7 @@ func (kb dbKeybase) Update(name, oldpass string, getNewpass func() (string, erro
return err return err
} }
kb.writeLocalKey(name, key, newpass) kb.writeLocalKey(name, key, newpass, i.GetAlgo())
return nil return nil
default: default:
@ -405,13 +401,23 @@ func (kb dbKeybase) CloseDB() {
kb.db.Close() kb.db.Close()
} }
func (kb dbKeybase) writeLocalKey(name string, priv tmcrypto.PrivKey, passphrase string) Info { // SupportedAlgos returns a list of supported signing algorithms.
func (kb dbKeybase) SupportedAlgos() []SigningAlgo {
return kb.base.SupportedAlgos()
}
// SupportedAlgosLedger returns a list of supported ledger signing algorithms.
func (kb dbKeybase) SupportedAlgosLedger() []SigningAlgo {
return kb.base.SupportedAlgosLedger()
}
func (kb dbKeybase) writeLocalKey(name string, priv tmcrypto.PrivKey, passphrase string, algo SigningAlgo) Info {
// encrypt private key using passphrase // encrypt private key using passphrase
privArmor := mintkey.EncryptArmorPrivKey(priv, passphrase) privArmor := mintkey.EncryptArmorPrivKey(priv, passphrase, string(algo))
// make Info // make Info
pub := priv.PubKey() pub := priv.PubKey()
info := newLocalInfo(name, pub, privArmor) info := newLocalInfo(name, pub, privArmor, algo)
kb.writeInfo(name, info) kb.writeInfo(name, info)
return info return info

View File

@ -18,6 +18,9 @@ import (
type ( type (
kbOptions struct { kbOptions struct {
keygenFunc PrivKeyGenFunc keygenFunc PrivKeyGenFunc
deriveFunc DeriveKeyFunc
supportedAlgos []SigningAlgo
supportedAlgosLedger []SigningAlgo
} }
// baseKeybase is an auxiliary type that groups Keybase storage agnostic features // baseKeybase is an auxiliary type that groups Keybase storage agnostic features
@ -32,7 +35,7 @@ type (
} }
writeLocalKeyer interface { writeLocalKeyer interface {
writeLocalKey(name string, priv tmcrypto.PrivKey, passphrase string) Info writeLocalKey(name string, priv tmcrypto.PrivKey, passphrase string, algo SigningAlgo) Info
} }
infoWriter interface { infoWriter interface {
@ -47,10 +50,36 @@ func WithKeygenFunc(f PrivKeyGenFunc) KeybaseOption {
} }
} }
// WithDeriveFunc applies an overridden key derivation function to generate the private key.
func WithDeriveFunc(f DeriveKeyFunc) KeybaseOption {
return func(o *kbOptions) {
o.deriveFunc = f
}
}
// WithSupportedAlgos defines the list of accepted SigningAlgos.
func WithSupportedAlgos(algos []SigningAlgo) KeybaseOption {
return func(o *kbOptions) {
o.supportedAlgos = algos
}
}
// WithSupportedAlgosLedger defines the list of accepted SigningAlgos compatible with Ledger.
func WithSupportedAlgosLedger(algos []SigningAlgo) KeybaseOption {
return func(o *kbOptions) {
o.supportedAlgosLedger = algos
}
}
// newBaseKeybase generates the base keybase with defaulting to tendermint SECP256K1 key type // newBaseKeybase generates the base keybase with defaulting to tendermint SECP256K1 key type
func newBaseKeybase(optionsFns ...KeybaseOption) baseKeybase { func newBaseKeybase(optionsFns ...KeybaseOption) baseKeybase {
// Default options for keybase // Default options for keybase
options := kbOptions{keygenFunc: baseSecpPrivKeyGen} options := kbOptions{
keygenFunc: StdPrivKeyGen,
deriveFunc: StdDeriveKey,
supportedAlgos: []SigningAlgo{Secp256k1},
supportedAlgosLedger: []SigningAlgo{Secp256k1},
}
for _, optionFn := range optionsFns { for _, optionFn := range optionsFns {
optionFn(&options) optionFn(&options)
@ -59,9 +88,20 @@ func newBaseKeybase(optionsFns ...KeybaseOption) baseKeybase {
return baseKeybase{options: options} return baseKeybase{options: options}
} }
// baseSecpPrivKeyGen generates a secp256k1 private key from the given bytes // StdPrivKeyGen is the default PrivKeyGen function in the keybase.
func baseSecpPrivKeyGen(bz [32]byte) tmcrypto.PrivKey { // For now, it only supports Secp256k1
return secp256k1.PrivKeySecp256k1(bz) func StdPrivKeyGen(bz []byte, algo SigningAlgo) (tmcrypto.PrivKey, error) {
if algo == Secp256k1 {
return SecpPrivKeyGen(bz), nil
}
return nil, ErrUnsupportedSigningAlgo
}
// SecpPrivKeyGen generates a secp256k1 private key from the given bytes
func SecpPrivKeyGen(bz []byte) tmcrypto.PrivKey {
var bzArr [32]byte
copy(bzArr[:], bz)
return secp256k1.PrivKeySecp256k1(bzArr)
} }
// SignWithLedger signs a binary message with the ledger device referenced by an Info object // SignWithLedger signs a binary message with the ledger device referenced by an Info object
@ -111,29 +151,26 @@ func (kb baseKeybase) DecodeSignature(info Info, msg []byte) (sig []byte, pub tm
// CreateAccount creates an account Info object. // CreateAccount creates an account Info object.
func (kb baseKeybase) CreateAccount( func (kb baseKeybase) CreateAccount(
keyWriter keyWriter, name, mnemonic, bip39Passwd, encryptPasswd string, account, index uint32, keyWriter keyWriter, name, mnemonic, bip39Passphrase, encryptPasswd, hdPath string, algo SigningAlgo,
) (Info, error) {
hdPath := CreateHDPath(account, index)
return kb.Derive(keyWriter, name, mnemonic, bip39Passwd, encryptPasswd, *hdPath)
}
func (kb baseKeybase) persistDerivedKey(
keyWriter keyWriter, seed []byte, passwd, name, fullHdPath string,
) (Info, error) { ) (Info, error) {
// create master key and derive first key for keyring // create master key and derive first key for keyring
derivedPriv, err := ComputeDerivedKey(seed, fullHdPath) derivedPriv, err := kb.options.deriveFunc(mnemonic, bip39Passphrase, hdPath, algo)
if err != nil {
return nil, err
}
privKey, err := kb.options.keygenFunc(derivedPriv, algo)
if err != nil { if err != nil {
return nil, err return nil, err
} }
var info Info var info Info
if passwd != "" { if encryptPasswd != "" {
info = keyWriter.writeLocalKey(name, kb.options.keygenFunc(derivedPriv), passwd) info = keyWriter.writeLocalKey(name, privKey, encryptPasswd, algo)
} else { } else {
info = kb.writeOfflineKey(keyWriter, name, kb.options.keygenFunc(derivedPriv).PubKey()) info = kb.writeOfflineKey(keyWriter, name, privKey.PubKey(), algo)
} }
return info, nil return info, nil
@ -145,7 +182,7 @@ func (kb baseKeybase) CreateLedger(
w infoWriter, name string, algo SigningAlgo, hrp string, account, index uint32, w infoWriter, name string, algo SigningAlgo, hrp string, account, index uint32,
) (Info, error) { ) (Info, error) {
if !IsAlgoSupported(algo) { if !IsSupportedAlgorithm(kb.SupportedAlgosLedger(), algo) {
return nil, ErrUnsupportedSigningAlgo return nil, ErrUnsupportedSigningAlgo
} }
@ -157,7 +194,7 @@ func (kb baseKeybase) CreateLedger(
return nil, err return nil, err
} }
return kb.writeLedgerKey(w, name, priv.PubKey(), *hdPath), nil return kb.writeLedgerKey(w, name, priv.PubKey(), *hdPath, algo), nil
} }
// CreateMnemonic generates a new key with the given algorithm and language pair. // CreateMnemonic generates a new key with the given algorithm and language pair.
@ -169,7 +206,7 @@ func (kb baseKeybase) CreateMnemonic(
return nil, "", ErrUnsupportedLanguage return nil, "", ErrUnsupportedLanguage
} }
if !IsAlgoSupported(algo) { if !IsSupportedAlgorithm(kb.SupportedAlgos(), algo) {
return nil, "", ErrUnsupportedSigningAlgo return nil, "", ErrUnsupportedSigningAlgo
} }
@ -185,37 +222,22 @@ func (kb baseKeybase) CreateMnemonic(
return nil, "", err return nil, "", err
} }
info, err = kb.persistDerivedKey( info, err = kb.CreateAccount(keyWriter, name, mnemonic, DefaultBIP39Passphrase, passwd, types.GetConfig().GetFullFundraiserPath(), algo)
keyWriter, if err != nil {
bip39.NewSeed(mnemonic, DefaultBIP39Passphrase), passwd, return nil, "", err
name, types.GetConfig().GetFullFundraiserPath(), }
)
return info, mnemonic, err return info, mnemonic, err
} }
// Derive computes a BIP39 seed from the mnemonic and bip39Passphrase. It creates func (kb baseKeybase) writeLedgerKey(w infoWriter, name string, pub tmcrypto.PubKey, path hd.BIP44Params, algo SigningAlgo) Info {
// a private key from the seed using the BIP44 params. info := newLedgerInfo(name, pub, path, algo)
func (kb baseKeybase) Derive(
keyWriter keyWriter, name, mnemonic, bip39Passphrase, encryptPasswd string, params hd.BIP44Params, // nolint:interfacer
) (Info, error) {
seed, err := bip39.NewSeedWithErrorChecking(mnemonic, bip39Passphrase)
if err != nil {
return nil, err
}
return kb.persistDerivedKey(keyWriter, seed, encryptPasswd, name, params.String())
}
func (kb baseKeybase) writeLedgerKey(w infoWriter, name string, pub tmcrypto.PubKey, path hd.BIP44Params) Info {
info := newLedgerInfo(name, pub, path)
w.writeInfo(name, info) w.writeInfo(name, info)
return info return info
} }
func (kb baseKeybase) writeOfflineKey(w infoWriter, name string, pub tmcrypto.PubKey) Info { func (kb baseKeybase) writeOfflineKey(w infoWriter, name string, pub tmcrypto.PubKey, algo SigningAlgo) Info {
info := newOfflineInfo(name, pub) info := newOfflineInfo(name, pub, algo)
w.writeInfo(name, info) w.writeInfo(name, info)
return info return info
} }
@ -226,10 +248,28 @@ func (kb baseKeybase) writeMultisigKey(w infoWriter, name string, pub tmcrypto.P
return info return info
} }
// ComputeDerivedKey derives and returns the private key for the given seed and HD path. // StdDeriveKey is the default DeriveKey function in the keybase.
func ComputeDerivedKey(seed []byte, fullHdPath string) ([32]byte, error) { // For now, it only supports Secp256k1
func StdDeriveKey(mnemonic string, bip39Passphrase, hdPath string, algo SigningAlgo) ([]byte, error) {
if algo == Secp256k1 {
return SecpDeriveKey(mnemonic, bip39Passphrase, hdPath)
}
return nil, ErrUnsupportedSigningAlgo
}
// SecpDeriveKey derives and returns the secp256k1 private key for the given seed and HD path.
func SecpDeriveKey(mnemonic string, bip39Passphrase, hdPath string) ([]byte, error) {
seed, err := bip39.NewSeedWithErrorChecking(mnemonic, bip39Passphrase)
if err != nil {
return nil, err
}
masterPriv, ch := hd.ComputeMastersFromSeed(seed) masterPriv, ch := hd.ComputeMastersFromSeed(seed)
return hd.DerivePrivateKeyForPath(masterPriv, ch, fullHdPath) if len(hdPath) == 0 {
return masterPriv[:], nil
}
derivedKey, err := hd.DerivePrivateKeyForPath(masterPriv, ch, hdPath)
return derivedKey[:], err
} }
// CreateHDPath returns BIP 44 object from account and index parameters. // CreateHDPath returns BIP 44 object from account and index parameters.
@ -237,9 +277,22 @@ func CreateHDPath(account uint32, index uint32) *hd.BIP44Params {
return hd.NewFundraiserParams(account, types.GetConfig().GetCoinType(), index) return hd.NewFundraiserParams(account, types.GetConfig().GetCoinType(), index)
} }
// IsAlgoSupported returns whether the signing algorithm is supported. // SupportedAlgos returns a list of supported signing algorithms.
// func (kb baseKeybase) SupportedAlgos() []SigningAlgo {
// TODO: Refactor this to be configurable to support interchangeable key signing return kb.options.supportedAlgos
// and addressing. }
// Ref: https://github.com/cosmos/cosmos-sdk/issues/4941
func IsAlgoSupported(algo SigningAlgo) bool { return algo == Secp256k1 } // SupportedAlgosLedger returns a list of supported ledger signing algorithms.
func (kb baseKeybase) SupportedAlgosLedger() []SigningAlgo {
return kb.options.supportedAlgosLedger
}
// IsSupportedAlgorithm returns whether the signing algorithm is in the passed-in list of supported algorithms.
func IsSupportedAlgorithm(supported []SigningAlgo, algo SigningAlgo) bool {
for _, supportedAlgo := range supported {
if algo == supportedAlgo {
return true
}
}
return false
}

View File

@ -38,25 +38,43 @@ func TestCreateAccountInvalidMnemonic(t *testing.T) {
_, err := kb.CreateAccount( _, err := kb.CreateAccount(
"some_account", "some_account",
"malarkey pair crucial catch public canyon evil outer stage ten gym tornado", "malarkey pair crucial catch public canyon evil outer stage ten gym tornado",
"", "", 0, 1) "", "", CreateHDPath(0, 0).String(), Secp256k1)
assert.Error(t, err) assert.Error(t, err)
assert.Equal(t, "Invalid mnemonic", err.Error()) assert.Equal(t, "Invalid mnemonic", err.Error())
} }
func TestCreateLedgerUnsupportedAlgo(t *testing.T) { func TestCreateLedgerUnsupportedAlgo(t *testing.T) {
kb := NewInMemory() kb := NewInMemory()
supportedLedgerAlgos := kb.SupportedAlgosLedger()
for _, supportedAlgo := range supportedLedgerAlgos {
if Ed25519 == supportedAlgo {
assert.FailNow(t, "Was not an unsupported algorithm")
}
}
_, err := kb.CreateLedger("some_account", Ed25519, "cosmos", 0, 1) _, err := kb.CreateLedger("some_account", Ed25519, "cosmos", 0, 1)
assert.Error(t, err) assert.Error(t, err)
assert.Equal(t, "unsupported signing algo: only secp256k1 is supported", err.Error()) assert.Equal(t, "unsupported signing algo", err.Error())
} }
func TestCreateLedger(t *testing.T) { func TestCreateLedger(t *testing.T) {
kb := NewInMemory() kb := NewInMemory(WithSupportedAlgosLedger([]SigningAlgo{Secp256k1, Ed25519}))
// test_cover and test_unit will result in different answers // test_cover and test_unit will result in different answers
// test_cover does not compile some dependencies so ledger is disabled // test_cover does not compile some dependencies so ledger is disabled
// test_unit may add a ledger mock // test_unit may add a ledger mock
// both cases are acceptable // both cases are acceptable
supportedLedgerAlgos := kb.SupportedAlgosLedger()
secpSupported := false
edSupported := false
for _, supportedAlgo := range supportedLedgerAlgos {
secpSupported = secpSupported || (supportedAlgo == Secp256k1)
edSupported = edSupported || (supportedAlgo == Ed25519)
}
assert.True(t, secpSupported)
assert.True(t, edSupported)
ledger, err := kb.CreateLedger("some_account", Secp256k1, "cosmos", 3, 1) ledger, err := kb.CreateLedger("some_account", Secp256k1, "cosmos", 3, 1)
if err != nil { if err != nil {
@ -92,7 +110,21 @@ func TestCreateLedger(t *testing.T) {
// TestKeyManagement makes sure we can manipulate these keys well // TestKeyManagement makes sure we can manipulate these keys well
func TestKeyManagement(t *testing.T) { func TestKeyManagement(t *testing.T) {
// make the storage with reasonable defaults // make the storage with reasonable defaults
cstore := NewInMemory() cstore := NewInMemory(WithSupportedAlgos([]SigningAlgo{Secp256k1, Sr25519}))
// Test modified supported algos
supportedAlgos := cstore.SupportedAlgos()
secpSupported := false
edSupported := false
srSupported := false
for _, supportedAlgo := range supportedAlgos {
secpSupported = secpSupported || (supportedAlgo == Secp256k1)
edSupported = edSupported || (supportedAlgo == Ed25519)
srSupported = srSupported || (supportedAlgo == Sr25519)
}
assert.True(t, secpSupported)
assert.False(t, edSupported)
assert.True(t, srSupported)
algo := Secp256k1 algo := Secp256k1
n1, n2, n3 := "personal", "business", "other" n1, n2, n3 := "personal", "business", "other"
@ -152,10 +184,12 @@ func TestKeyManagement(t *testing.T) {
o1 := "offline" o1 := "offline"
priv1 := ed25519.GenPrivKey() priv1 := ed25519.GenPrivKey()
pub1 := priv1.PubKey() pub1 := priv1.PubKey()
i, err = cstore.CreateOffline(o1, pub1) i, err = cstore.CreateOffline(o1, pub1, algo)
require.Nil(t, err) require.Nil(t, err)
require.Equal(t, pub1, i.GetPubKey()) require.Equal(t, pub1, i.GetPubKey())
require.Equal(t, o1, i.GetName()) require.Equal(t, o1, i.GetName())
iOffline := i.(*offlineInfo)
require.Equal(t, algo, iOffline.GetAlgo())
keyS, err = cstore.List() keyS, err = cstore.List()
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 2, len(keyS)) require.Equal(t, 2, len(keyS))
@ -393,7 +427,8 @@ func TestSeedPhrase(t *testing.T) {
// let us re-create it from the mnemonic-phrase // let us re-create it from the mnemonic-phrase
params := *hd.NewFundraiserParams(0, sdk.CoinType, 0) params := *hd.NewFundraiserParams(0, sdk.CoinType, 0)
newInfo, err := cstore.Derive(n2, mnemonic, DefaultBIP39Passphrase, p2, params) hdPath := params.String()
newInfo, err := cstore.CreateAccount(n2, mnemonic, DefaultBIP39Passphrase, p2, hdPath, Secp256k1)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, n2, newInfo.GetName()) require.Equal(t, n2, newInfo.GetName())
require.Equal(t, info.GetPubKey().Address(), newInfo.GetPubKey().Address()) require.Equal(t, info.GetPubKey().Address(), newInfo.GetPubKey().Address())
@ -402,7 +437,11 @@ func TestSeedPhrase(t *testing.T) {
func ExampleNew() { func ExampleNew() {
// Select the encryption and storage for your cryptostore // Select the encryption and storage for your cryptostore
customKeyGenFunc := func(bz [32]byte) crypto.PrivKey { return secp256k1.PrivKeySecp256k1(bz) } customKeyGenFunc := func(bz []byte, algo SigningAlgo) (crypto.PrivKey, error) {
var bzArr [32]byte
copy(bzArr[:], bz)
return secp256k1.PrivKeySecp256k1(bzArr), nil
}
cstore := NewInMemory(WithKeygenFunc(customKeyGenFunc)) cstore := NewInMemory(WithKeygenFunc(customKeyGenFunc))
sec := Secp256k1 sec := Secp256k1

View File

@ -20,7 +20,6 @@ import (
cryptoAmino "github.com/tendermint/tendermint/crypto/encoding/amino" cryptoAmino "github.com/tendermint/tendermint/crypto/encoding/amino"
"github.com/cosmos/cosmos-sdk/client/input" "github.com/cosmos/cosmos-sdk/client/input"
"github.com/cosmos/cosmos-sdk/crypto/keys/hd"
"github.com/cosmos/cosmos-sdk/crypto/keys/keyerror" "github.com/cosmos/cosmos-sdk/crypto/keys/keyerror"
"github.com/cosmos/cosmos-sdk/crypto/keys/mintkey" "github.com/cosmos/cosmos-sdk/crypto/keys/mintkey"
"github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types"
@ -90,19 +89,10 @@ func (kb keyringKeybase) CreateMnemonic(
// CreateAccount converts a mnemonic to a private key and persists it, encrypted // CreateAccount converts a mnemonic to a private key and persists it, encrypted
// with the given password. // with the given password.
func (kb keyringKeybase) CreateAccount( func (kb keyringKeybase) CreateAccount(
name, mnemonic, bip39Passwd, encryptPasswd string, account, index uint32, name, mnemonic, bip39Passwd, encryptPasswd, hdPath string, algo SigningAlgo,
) (Info, error) { ) (Info, error) {
return kb.base.CreateAccount(kb, name, mnemonic, bip39Passwd, encryptPasswd, account, index) return kb.base.CreateAccount(kb, name, mnemonic, bip39Passwd, encryptPasswd, hdPath, algo)
}
// Derive computes a BIP39 seed from th mnemonic and bip39Passphrase. It creates
// a private key from the seed using the BIP44 params.
func (kb keyringKeybase) Derive(
name, mnemonic, bip39Passphrase, encryptPasswd string, params hd.BIP44Params,
) (info Info, err error) {
return kb.base.Derive(kb, name, mnemonic, bip39Passphrase, encryptPasswd, params)
} }
// CreateLedger creates a new locally-stored reference to a Ledger keypair. // CreateLedger creates a new locally-stored reference to a Ledger keypair.
@ -116,8 +106,8 @@ func (kb keyringKeybase) CreateLedger(
// CreateOffline creates a new reference to an offline keypair. It returns the // CreateOffline creates a new reference to an offline keypair. It returns the
// created key info. // created key info.
func (kb keyringKeybase) CreateOffline(name string, pub tmcrypto.PubKey) (Info, error) { func (kb keyringKeybase) CreateOffline(name string, pub tmcrypto.PubKey, algo SigningAlgo) (Info, error) {
return kb.base.writeOfflineKey(kb, name, pub), nil return kb.base.writeOfflineKey(kb, name, pub, algo), nil
} }
// CreateMulti creates a new reference to a multisig (offline) keypair. It // CreateMulti creates a new reference to a multisig (offline) keypair. It
@ -284,7 +274,7 @@ func (kb keyringKeybase) ExportPubKey(name string) (armor string, err error) {
return "", fmt.Errorf("no key to export with name: %s", name) return "", fmt.Errorf("no key to export with name: %s", name)
} }
return mintkey.ArmorPubKeyBytes(bz.GetPubKey().Bytes()), nil return mintkey.ArmorPubKeyBytes(bz.GetPubKey().Bytes(), string(bz.GetAlgo())), nil
} }
// Import imports armored private key. // Import imports armored private key.
@ -330,7 +320,12 @@ func (kb keyringKeybase) ExportPrivKey(name, decryptPassphrase, encryptPassphras
return "", err return "", err
} }
return mintkey.EncryptArmorPrivKey(priv, encryptPassphrase), nil info, err := kb.Get(name)
if err != nil {
return "", err
}
return mintkey.EncryptArmorPrivKey(priv, encryptPassphrase, string(info.GetAlgo())), nil
} }
// ImportPrivKey imports a private key in ASCII armor format. An error is returned // ImportPrivKey imports a private key in ASCII armor format. An error is returned
@ -341,13 +336,13 @@ func (kb keyringKeybase) ImportPrivKey(name, armor, passphrase string) error {
return fmt.Errorf("cannot overwrite key: %s", name) return fmt.Errorf("cannot overwrite key: %s", name)
} }
privKey, err := mintkey.UnarmorDecryptPrivKey(armor, passphrase) privKey, algo, err := mintkey.UnarmorDecryptPrivKey(armor, passphrase)
if err != nil { if err != nil {
return errors.Wrap(err, "failed to decrypt private key") return errors.Wrap(err, "failed to decrypt private key")
} }
// NOTE: The keyring keystore has no need for a passphrase. // NOTE: The keyring keystore has no need for a passphrase.
kb.writeLocalKey(name, privKey, "") kb.writeLocalKey(name, privKey, "", SigningAlgo(algo))
return nil return nil
} }
@ -370,7 +365,7 @@ func (kb keyringKeybase) ImportPubKey(name string, armor string) error {
} }
} }
pubBytes, err := mintkey.UnarmorPubKeyBytes(armor) pubBytes, algo, err := mintkey.UnarmorPubKeyBytes(armor)
if err != nil { if err != nil {
return err return err
} }
@ -380,7 +375,7 @@ func (kb keyringKeybase) ImportPubKey(name string, armor string) error {
return err return err
} }
kb.base.writeOfflineKey(kb, name, pubKey) kb.base.writeOfflineKey(kb, name, pubKey, SigningAlgo(algo))
return nil return nil
} }
@ -419,7 +414,7 @@ func (kb keyringKeybase) Update(name, oldpass string, getNewpass func() (string,
switch linfo := info.(type) { switch linfo := info.(type) {
case localInfo: case localInfo:
key, err := mintkey.UnarmorDecryptPrivKey(linfo.PrivKeyArmor, oldpass) key, _, err := mintkey.UnarmorDecryptPrivKey(linfo.PrivKeyArmor, oldpass)
if err != nil { if err != nil {
return err return err
} }
@ -429,7 +424,7 @@ func (kb keyringKeybase) Update(name, oldpass string, getNewpass func() (string,
return err return err
} }
kb.writeLocalKey(name, key, newpass) kb.writeLocalKey(name, key, newpass, linfo.GetAlgo())
return nil return nil
default: default:
@ -437,13 +432,23 @@ func (kb keyringKeybase) Update(name, oldpass string, getNewpass func() (string,
} }
} }
// SupportedAlgos returns a list of supported signing algorithms.
func (kb keyringKeybase) SupportedAlgos() []SigningAlgo {
return kb.base.SupportedAlgos()
}
// SupportedAlgosLedger returns a list of supported ledger signing algorithms.
func (kb keyringKeybase) SupportedAlgosLedger() []SigningAlgo {
return kb.base.SupportedAlgosLedger()
}
// CloseDB releases the lock and closes the storage backend. // CloseDB releases the lock and closes the storage backend.
func (kb keyringKeybase) CloseDB() {} func (kb keyringKeybase) CloseDB() {}
func (kb keyringKeybase) writeLocalKey(name string, priv tmcrypto.PrivKey, _ string) Info { func (kb keyringKeybase) writeLocalKey(name string, priv tmcrypto.PrivKey, _ string, algo SigningAlgo) Info {
// encrypt private key using keyring // encrypt private key using keyring
pub := priv.PubKey() pub := priv.PubKey()
info := newLocalInfo(name, pub, string(priv.Bytes())) info := newLocalInfo(name, pub, string(priv.Bytes()), algo)
kb.writeInfo(name, info) kb.writeInfo(name, info)
return info return info

View File

@ -79,7 +79,7 @@ func TestLazyKeyManagementKeyRing(t *testing.T) {
o1 := "offline" o1 := "offline"
priv1 := ed25519.GenPrivKey() priv1 := ed25519.GenPrivKey()
pub1 := priv1.PubKey() pub1 := priv1.PubKey()
i, err = kb.CreateOffline(o1, pub1) i, err = kb.CreateOffline(o1, pub1, Ed25519)
require.Nil(t, err) require.Nil(t, err)
require.Equal(t, pub1, i.GetPubKey()) require.Equal(t, pub1, i.GetPubKey())
require.Equal(t, o1, i.GetName()) require.Equal(t, o1, i.GetName())
@ -209,10 +209,11 @@ func TestLazyExportImportPubKeyKeyRing(t *testing.T) {
defer cleanup() defer cleanup()
kb, err := NewTestKeyring("keybasename", dir) kb, err := NewTestKeyring("keybasename", dir)
require.NoError(t, err) require.NoError(t, err)
algo := Secp256k1
// CreateMnemonic a private-public key pair and ensure consistency // CreateMnemonic a private-public key pair and ensure consistency
notPasswd := "n9y25ah7" notPasswd := "n9y25ah7"
info, _, err := kb.CreateMnemonic("john", English, notPasswd, Secp256k1) info, _, err := kb.CreateMnemonic("john", English, notPasswd, algo)
require.Nil(t, err) require.Nil(t, err)
require.NotEqual(t, info, "") require.NotEqual(t, info, "")
require.Equal(t, info.GetName(), "john") require.Equal(t, info.GetName(), "john")
@ -318,7 +319,8 @@ func TestLazySeedPhraseKeyRing(t *testing.T) {
// let us re-create it from the mnemonic-phrase // let us re-create it from the mnemonic-phrase
params := *hd.NewFundraiserParams(0, sdk.CoinType, 0) params := *hd.NewFundraiserParams(0, sdk.CoinType, 0)
newInfo, err := kb.Derive(n2, mnemonic, DefaultBIP39Passphrase, p2, params) hdPath := params.String()
newInfo, err := kb.CreateAccount(n2, mnemonic, DefaultBIP39Passphrase, p2, hdPath, Secp256k1)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, n2, newInfo.GetName()) require.Equal(t, n2, newInfo.GetName())
require.Equal(t, info.GetPubKey().Address(), newInfo.GetPubKey().Address()) require.Equal(t, info.GetPubKey().Address(), newInfo.GetPubKey().Address())

View File

@ -4,9 +4,13 @@ package keys
type SigningAlgo string type SigningAlgo string
const ( const (
// MultiAlgo implies that a pubkey is a multisignature
MultiAlgo = SigningAlgo("multi")
// Secp256k1 uses the Bitcoin secp256k1 ECDSA parameters. // Secp256k1 uses the Bitcoin secp256k1 ECDSA parameters.
Secp256k1 = SigningAlgo("secp256k1") Secp256k1 = SigningAlgo("secp256k1")
// Ed25519 represents the Ed25519 signature system. // Ed25519 represents the Ed25519 signature system.
// It is currently not supported for end-user keys (wallets/ledgers). // It is currently not supported for end-user keys (wallets/ledgers).
Ed25519 = SigningAlgo("ed25519") Ed25519 = SigningAlgo("ed25519")
// Sr25519 represents the Sr25519 signature system.
Sr25519 = SigningAlgo("sr25519")
) )

View File

@ -6,7 +6,6 @@ import (
"github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto"
cmn "github.com/tendermint/tendermint/libs/common" cmn "github.com/tendermint/tendermint/libs/common"
"github.com/cosmos/cosmos-sdk/crypto/keys/hd"
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
) )
@ -88,7 +87,7 @@ func (lkb lazyKeybase) CreateMnemonic(name string, language Language, passwd str
return newDBKeybase(db, lkb.options...).CreateMnemonic(name, language, passwd, algo) return newDBKeybase(db, lkb.options...).CreateMnemonic(name, language, passwd, algo)
} }
func (lkb lazyKeybase) CreateAccount(name, mnemonic, bip39Passwd, encryptPasswd string, account uint32, index uint32) (Info, error) { func (lkb lazyKeybase) CreateAccount(name, mnemonic, bip39Passwd, encryptPasswd, hdPath string, algo SigningAlgo) (Info, error) {
db, err := sdk.NewLevelDB(lkb.name, lkb.dir) db, err := sdk.NewLevelDB(lkb.name, lkb.dir)
if err != nil { if err != nil {
return nil, err return nil, err
@ -96,17 +95,7 @@ func (lkb lazyKeybase) CreateAccount(name, mnemonic, bip39Passwd, encryptPasswd
defer db.Close() defer db.Close()
return newDBKeybase(db, return newDBKeybase(db,
lkb.options...).CreateAccount(name, mnemonic, bip39Passwd, encryptPasswd, account, index) lkb.options...).CreateAccount(name, mnemonic, bip39Passwd, encryptPasswd, hdPath, algo)
}
func (lkb lazyKeybase) Derive(name, mnemonic, bip39Passwd, encryptPasswd string, params hd.BIP44Params) (Info, error) {
db, err := sdk.NewLevelDB(lkb.name, lkb.dir)
if err != nil {
return nil, err
}
defer db.Close()
return newDBKeybase(db, lkb.options...).Derive(name, mnemonic, bip39Passwd, encryptPasswd, params)
} }
func (lkb lazyKeybase) CreateLedger(name string, algo SigningAlgo, hrp string, account, index uint32) (info Info, err error) { func (lkb lazyKeybase) CreateLedger(name string, algo SigningAlgo, hrp string, account, index uint32) (info Info, err error) {
@ -119,14 +108,14 @@ func (lkb lazyKeybase) CreateLedger(name string, algo SigningAlgo, hrp string, a
return newDBKeybase(db, lkb.options...).CreateLedger(name, algo, hrp, account, index) return newDBKeybase(db, lkb.options...).CreateLedger(name, algo, hrp, account, index)
} }
func (lkb lazyKeybase) CreateOffline(name string, pubkey crypto.PubKey) (info Info, err error) { func (lkb lazyKeybase) CreateOffline(name string, pubkey crypto.PubKey, algo SigningAlgo) (info Info, err error) {
db, err := sdk.NewLevelDB(lkb.name, lkb.dir) db, err := sdk.NewLevelDB(lkb.name, lkb.dir)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer db.Close() defer db.Close()
return newDBKeybase(db, lkb.options...).CreateOffline(name, pubkey) return newDBKeybase(db, lkb.options...).CreateOffline(name, pubkey, algo)
} }
func (lkb lazyKeybase) CreateMulti(name string, pubkey crypto.PubKey) (info Info, err error) { func (lkb lazyKeybase) CreateMulti(name string, pubkey crypto.PubKey) (info Info, err error) {
@ -221,4 +210,14 @@ func (lkb lazyKeybase) ExportPrivKey(name string, decryptPassphrase string,
return newDBKeybase(db, lkb.options...).ExportPrivKey(name, decryptPassphrase, encryptPassphrase) return newDBKeybase(db, lkb.options...).ExportPrivKey(name, decryptPassphrase, encryptPassphrase)
} }
// SupportedAlgos returns a list of supported signing algorithms.
func (lkb lazyKeybase) SupportedAlgos() []SigningAlgo {
return newBaseKeybase(lkb.options...).SupportedAlgos()
}
// SupportedAlgosLedger returns a list of supported ledger signing algorithms.
func (lkb lazyKeybase) SupportedAlgosLedger() []SigningAlgo {
return newBaseKeybase(lkb.options...).SupportedAlgosLedger()
}
func (lkb lazyKeybase) CloseDB() {} func (lkb lazyKeybase) CloseDB() {}

View File

@ -89,7 +89,7 @@ func TestLazyKeyManagement(t *testing.T) {
o1 := "offline" o1 := "offline"
priv1 := ed25519.GenPrivKey() priv1 := ed25519.GenPrivKey()
pub1 := priv1.PubKey() pub1 := priv1.PubKey()
i, err = kb.CreateOffline(o1, pub1) i, err = kb.CreateOffline(o1, pub1, algo)
require.Nil(t, err) require.Nil(t, err)
require.Equal(t, pub1, i.GetPubKey()) require.Equal(t, pub1, i.GetPubKey())
require.Equal(t, o1, i.GetName()) require.Equal(t, o1, i.GetName())
@ -245,10 +245,11 @@ func TestLazyExportImportPubKey(t *testing.T) {
dir, cleanup := tests.NewTestCaseDir(t) dir, cleanup := tests.NewTestCaseDir(t)
defer cleanup() defer cleanup()
kb := New("keybasename", dir) kb := New("keybasename", dir)
algo := Secp256k1
// CreateMnemonic a private-public key pair and ensure consistency // CreateMnemonic a private-public key pair and ensure consistency
notPasswd := "n9y25ah7" notPasswd := "n9y25ah7"
info, _, err := kb.CreateMnemonic("john", English, notPasswd, Secp256k1) info, _, err := kb.CreateMnemonic("john", English, notPasswd, algo)
require.Nil(t, err) require.Nil(t, err)
require.NotEqual(t, info, "") require.NotEqual(t, info, "")
require.Equal(t, info.GetName(), "john") require.Equal(t, info.GetName(), "john")
@ -368,7 +369,8 @@ func TestLazySeedPhrase(t *testing.T) {
// let us re-create it from the mnemonic-phrase // let us re-create it from the mnemonic-phrase
params := *hd.NewFundraiserParams(0, sdk.CoinType, 0) params := *hd.NewFundraiserParams(0, sdk.CoinType, 0)
newInfo, err := kb.Derive(n2, mnemonic, DefaultBIP39Passphrase, p2, params) hdPath := params.String()
newInfo, err := kb.CreateAccount(n2, mnemonic, DefaultBIP39Passphrase, p2, hdPath, algo)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, n2, newInfo.GetName()) require.Equal(t, n2, newInfo.GetName())
require.Equal(t, info.GetPubKey().Address(), newInfo.GetPubKey().Address()) require.Equal(t, info.GetPubKey().Address(), newInfo.GetPubKey().Address())
@ -421,9 +423,9 @@ func TestKeygenOverride(t *testing.T) {
CryptoCdc = testCdc CryptoCdc = testCdc
overrideCalled := false overrideCalled := false
dummyFunc := func(bz [32]byte) crypto.PrivKey { dummyFunc := func(bz []byte, algo SigningAlgo) (crypto.PrivKey, error) {
overrideCalled = true overrideCalled = true
return testPriv(bz[:]) return testPriv(bz[:]), nil
} }
kb := New("keybasename", dir, WithKeygenFunc(dummyFunc)) kb := New("keybasename", dir, WithKeygenFunc(dummyFunc))

View File

@ -20,6 +20,11 @@ const (
blockTypePrivKey = "TENDERMINT PRIVATE KEY" blockTypePrivKey = "TENDERMINT PRIVATE KEY"
blockTypeKeyInfo = "TENDERMINT KEY INFO" blockTypeKeyInfo = "TENDERMINT KEY INFO"
blockTypePubKey = "TENDERMINT PUBLIC KEY" blockTypePubKey = "TENDERMINT PUBLIC KEY"
defaultAlgo = "secp256k1"
headerVersion = "version"
headerType = "type"
) )
// Make bcrypt security parameter var, so it can be changed within the lcd test // Make bcrypt security parameter var, so it can be changed within the lcd test
@ -42,36 +47,58 @@ var BcryptSecurityParameter = 12
// Armor the InfoBytes // Armor the InfoBytes
func ArmorInfoBytes(bz []byte) string { func ArmorInfoBytes(bz []byte) string {
return armorBytes(bz, blockTypeKeyInfo) header := map[string]string{
headerType: "Info",
headerVersion: "0.0.0",
}
return armor.EncodeArmor(blockTypeKeyInfo, header, bz)
} }
// Armor the PubKeyBytes // Armor the PubKeyBytes
func ArmorPubKeyBytes(bz []byte) string { func ArmorPubKeyBytes(bz []byte, algo string) string {
return armorBytes(bz, blockTypePubKey)
}
func armorBytes(bz []byte, blockType string) string {
header := map[string]string{ header := map[string]string{
"type": "Info", headerVersion: "0.0.1",
"version": "0.0.0",
} }
return armor.EncodeArmor(blockType, header, bz) if algo != "" {
header[headerType] = algo
}
return armor.EncodeArmor(blockTypePubKey, header, bz)
} }
//----------------------------------------------------------------- //-----------------------------------------------------------------
// remove armor // remove armor
// Unarmor the InfoBytes // Unarmor the InfoBytes
func UnarmorInfoBytes(armorStr string) (bz []byte, err error) { func UnarmorInfoBytes(armorStr string) ([]byte, error) {
return unarmorBytes(armorStr, blockTypeKeyInfo) bz, header, err := unarmorBytes(armorStr, blockTypeKeyInfo)
if err != nil {
return nil, err
} }
// Unarmor the PubKeyBytes if header[headerVersion] != "0.0.0" {
func UnarmorPubKeyBytes(armorStr string) (bz []byte, err error) { return nil, fmt.Errorf("unrecognized version: %v", header[headerVersion])
return unarmorBytes(armorStr, blockTypePubKey) }
return bz, nil
} }
func unarmorBytes(armorStr, blockType string) (bz []byte, err error) { // UnarmorPubKeyBytes returns the pubkey byte slice, a string of the algo type, and an error
func UnarmorPubKeyBytes(armorStr string) (bz []byte, algo string, err error) {
bz, header, err := unarmorBytes(armorStr, blockTypePubKey)
switch header[headerVersion] {
case "0.0.0":
return bz, defaultAlgo, err
case "0.0.1":
if header[headerType] == "" {
header[headerType] = defaultAlgo
}
return bz, header[headerType], err
default:
err = fmt.Errorf("unrecognized version: %v", header[headerVersion])
return nil, "", err
}
}
func unarmorBytes(armorStr, blockType string) (bz []byte, header map[string]string, err error) {
bType, header, bz, err := armor.DecodeArmor(armorStr) bType, header, bz, err := armor.DecodeArmor(armorStr)
if err != nil { if err != nil {
return return
@ -80,10 +107,6 @@ func unarmorBytes(armorStr, blockType string) (bz []byte, err error) {
err = fmt.Errorf("unrecognized armor type %q, expected: %q", bType, blockType) err = fmt.Errorf("unrecognized armor type %q, expected: %q", bType, blockType)
return return
} }
if header["version"] != "0.0.0" {
err = fmt.Errorf("unrecognized version: %v", header["version"])
return
}
return return
} }
@ -91,12 +114,15 @@ func unarmorBytes(armorStr, blockType string) (bz []byte, err error) {
// encrypt/decrypt with armor // encrypt/decrypt with armor
// Encrypt and armor the private key. // Encrypt and armor the private key.
func EncryptArmorPrivKey(privKey crypto.PrivKey, passphrase string) string { func EncryptArmorPrivKey(privKey crypto.PrivKey, passphrase string, algo string) string {
saltBytes, encBytes := encryptPrivKey(privKey, passphrase) saltBytes, encBytes := encryptPrivKey(privKey, passphrase)
header := map[string]string{ header := map[string]string{
"kdf": "bcrypt", "kdf": "bcrypt",
"salt": fmt.Sprintf("%X", saltBytes), "salt": fmt.Sprintf("%X", saltBytes),
} }
if algo != "" {
header[headerType] = algo
}
armorStr := armor.EncodeArmor(blockTypePrivKey, header, encBytes) armorStr := armor.EncodeArmor(blockTypePrivKey, header, encBytes)
return armorStr return armorStr
} }
@ -115,28 +141,31 @@ func encryptPrivKey(privKey crypto.PrivKey, passphrase string) (saltBytes []byte
return saltBytes, xsalsa20symmetric.EncryptSymmetric(privKeyBytes, key) return saltBytes, xsalsa20symmetric.EncryptSymmetric(privKeyBytes, key)
} }
// Unarmor and decrypt the private key. // UnarmorDecryptPrivKey returns the privkey byte slice, a string of the algo type, and an error
func UnarmorDecryptPrivKey(armorStr string, passphrase string) (crypto.PrivKey, error) { func UnarmorDecryptPrivKey(armorStr string, passphrase string) (privKey crypto.PrivKey, algo string, err error) {
var privKey crypto.PrivKey
blockType, header, encBytes, err := armor.DecodeArmor(armorStr) blockType, header, encBytes, err := armor.DecodeArmor(armorStr)
if err != nil { if err != nil {
return privKey, err return privKey, "", err
} }
if blockType != blockTypePrivKey { if blockType != blockTypePrivKey {
return privKey, fmt.Errorf("unrecognized armor type: %v", blockType) return privKey, "", fmt.Errorf("unrecognized armor type: %v", blockType)
} }
if header["kdf"] != "bcrypt" { if header["kdf"] != "bcrypt" {
return privKey, fmt.Errorf("unrecognized KDF type: %v", header["KDF"]) return privKey, "", fmt.Errorf("unrecognized KDF type: %v", header["KDF"])
} }
if header["salt"] == "" { if header["salt"] == "" {
return privKey, fmt.Errorf("missing salt bytes") return privKey, "", fmt.Errorf("missing salt bytes")
} }
saltBytes, err := hex.DecodeString(header["salt"]) saltBytes, err := hex.DecodeString(header["salt"])
if err != nil { if err != nil {
return privKey, fmt.Errorf("error decoding salt: %v", err.Error()) return privKey, "", fmt.Errorf("error decoding salt: %v", err.Error())
} }
privKey, err = decryptPrivKey(saltBytes, encBytes, passphrase) privKey, err = decryptPrivKey(saltBytes, encBytes, passphrase)
return privKey, err
if header[headerType] == "" {
header[headerType] = defaultAlgo
}
return privKey, header[headerType], err
} }
func decryptPrivKey(saltBytes []byte, encBytes []byte, passphrase string) (privKey crypto.PrivKey, err error) { func decryptPrivKey(saltBytes []byte, encBytes []byte, passphrase string) (privKey crypto.PrivKey, err error) {

View File

@ -13,11 +13,12 @@ import (
func TestArmorUnarmorPrivKey(t *testing.T) { func TestArmorUnarmorPrivKey(t *testing.T) {
priv := secp256k1.GenPrivKey() priv := secp256k1.GenPrivKey()
armor := mintkey.EncryptArmorPrivKey(priv, "passphrase") armor := mintkey.EncryptArmorPrivKey(priv, "passphrase", "")
_, err := mintkey.UnarmorDecryptPrivKey(armor, "wrongpassphrase") _, _, err := mintkey.UnarmorDecryptPrivKey(armor, "wrongpassphrase")
require.Error(t, err) require.Error(t, err)
decrypted, err := mintkey.UnarmorDecryptPrivKey(armor, "passphrase") decrypted, algo, err := mintkey.UnarmorDecryptPrivKey(armor, "passphrase")
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, string(keys.Secp256k1), algo)
require.True(t, priv.Equals(decrypted)) require.True(t, priv.Equals(decrypted))
} }
@ -28,10 +29,11 @@ func TestArmorUnarmorPubKey(t *testing.T) {
// Add keys and see they return in alphabetical order // Add keys and see they return in alphabetical order
info, _, err := cstore.CreateMnemonic("Bob", keys.English, "passphrase", keys.Secp256k1) info, _, err := cstore.CreateMnemonic("Bob", keys.English, "passphrase", keys.Secp256k1)
require.NoError(t, err) require.NoError(t, err)
armor := mintkey.ArmorPubKeyBytes(info.GetPubKey().Bytes()) armor := mintkey.ArmorPubKeyBytes(info.GetPubKey().Bytes(), "")
pubBytes, err := mintkey.UnarmorPubKeyBytes(armor) pubBytes, algo, err := mintkey.UnarmorPubKeyBytes(armor)
require.NoError(t, err) require.NoError(t, err)
pub, err := cryptoAmino.PubKeyFromBytes(pubBytes) pub, err := cryptoAmino.PubKeyFromBytes(pubBytes)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, string(keys.Secp256k1), algo)
require.True(t, pub.Equals(info.GetPubKey())) require.True(t, pub.Equals(info.GetPubKey()))
} }

View File

@ -31,21 +31,15 @@ type Keybase interface {
// same name. // same name.
CreateMnemonic(name string, language Language, passwd string, algo SigningAlgo) (info Info, seed string, err error) CreateMnemonic(name string, language Language, passwd string, algo SigningAlgo) (info Info, seed string, err error)
// CreateAccount converts a mnemonic to a private key using a BIP44 path 44'/118'/{account}'/0/{index} // CreateAccount converts a mnemonic to a private key and BIP 32 HD Path
// and persists it, encrypted with the given password. // and persists it, encrypted with the given password.
CreateAccount(name, mnemonic, bip39Passwd, encryptPasswd string, account uint32, index uint32) (Info, error) CreateAccount(name, mnemonic, bip39Passwd, encryptPasswd, hdPath string, algo SigningAlgo) (Info, error)
// Derive computes a BIP39 seed from th mnemonic and bip39Passwd.
// Derive private key from the seed using the BIP44 params.
// Encrypt the key to disk using encryptPasswd.
// See https://github.com/cosmos/cosmos-sdk/issues/2095
Derive(name, mnemonic, bip39Passwd, encryptPasswd string, params hd.BIP44Params) (Info, error)
// CreateLedger creates, stores, and returns a new Ledger key reference // CreateLedger creates, stores, and returns a new Ledger key reference
CreateLedger(name string, algo SigningAlgo, hrp string, account, index uint32) (info Info, err error) CreateLedger(name string, algo SigningAlgo, hrp string, account, index uint32) (info Info, err error)
// CreateOffline creates, stores, and returns a new offline key reference // CreateOffline creates, stores, and returns a new offline key reference
CreateOffline(name string, pubkey crypto.PubKey) (info Info, err error) CreateOffline(name string, pubkey crypto.PubKey, algo SigningAlgo) (info Info, err error)
// CreateMulti creates, stores, and returns a new multsig (offline) key reference // CreateMulti creates, stores, and returns a new multsig (offline) key reference
CreateMulti(name string, pubkey crypto.PubKey) (info Info, err error) CreateMulti(name string, pubkey crypto.PubKey) (info Info, err error)
@ -81,6 +75,12 @@ type Keybase interface {
// ExportPrivateKeyObject *only* works on locally-stored keys. Temporary method until we redo the exporting API // ExportPrivateKeyObject *only* works on locally-stored keys. Temporary method until we redo the exporting API
ExportPrivateKeyObject(name string, passphrase string) (crypto.PrivKey, error) ExportPrivateKeyObject(name string, passphrase string) (crypto.PrivKey, error)
// SupportedAlgos returns a list of signing algorithms supported by the keybase
SupportedAlgos() []SigningAlgo
// SupportedAlgosLedger returns a list of signing algorithms supported by the keybase's ledger integration
SupportedAlgosLedger() []SigningAlgo
// CloseDB closes the database. // CloseDB closes the database.
CloseDB() CloseDB()
} }
@ -118,6 +118,8 @@ type Info interface {
GetPubKey() crypto.PubKey GetPubKey() crypto.PubKey
// Address // Address
GetAddress() types.AccAddress GetAddress() types.AccAddress
// Algo
GetAlgo() SigningAlgo
// Bip44 Path // Bip44 Path
GetPath() (*hd.BIP44Params, error) GetPath() (*hd.BIP44Params, error)
} }
@ -132,15 +134,17 @@ var (
// localInfo is the public information about a locally stored key // localInfo is the public information about a locally stored key
type localInfo struct { type localInfo struct {
Name string `json:"name"` Name string `json:"name"`
Algo SigningAlgo `json:"algo"`
PubKey crypto.PubKey `json:"pubkey"` PubKey crypto.PubKey `json:"pubkey"`
PrivKeyArmor string `json:"privkey.armor"` PrivKeyArmor string `json:"privkey.armor"`
} }
func newLocalInfo(name string, pub crypto.PubKey, privArmor string) Info { func newLocalInfo(name string, pub crypto.PubKey, privArmor string, algo SigningAlgo) Info {
return &localInfo{ return &localInfo{
Name: name, Name: name,
PubKey: pub, PubKey: pub,
PrivKeyArmor: privArmor, PrivKeyArmor: privArmor,
Algo: algo,
} }
} }
@ -164,6 +168,11 @@ func (i localInfo) GetAddress() types.AccAddress {
return i.PubKey.Address().Bytes() return i.PubKey.Address().Bytes()
} }
// GetType implements Info interface
func (i localInfo) GetAlgo() SigningAlgo {
return i.Algo
}
// GetType implements Info interface // GetType implements Info interface
func (i localInfo) GetPath() (*hd.BIP44Params, error) { func (i localInfo) GetPath() (*hd.BIP44Params, error) {
return nil, fmt.Errorf("BIP44 Paths are not available for this type") return nil, fmt.Errorf("BIP44 Paths are not available for this type")
@ -174,13 +183,15 @@ type ledgerInfo struct {
Name string `json:"name"` Name string `json:"name"`
PubKey crypto.PubKey `json:"pubkey"` PubKey crypto.PubKey `json:"pubkey"`
Path hd.BIP44Params `json:"path"` Path hd.BIP44Params `json:"path"`
Algo SigningAlgo `json:"algo"`
} }
func newLedgerInfo(name string, pub crypto.PubKey, path hd.BIP44Params) Info { func newLedgerInfo(name string, pub crypto.PubKey, path hd.BIP44Params, algo SigningAlgo) Info {
return &ledgerInfo{ return &ledgerInfo{
Name: name, Name: name,
PubKey: pub, PubKey: pub,
Path: path, Path: path,
Algo: algo,
} }
} }
@ -204,6 +215,11 @@ func (i ledgerInfo) GetAddress() types.AccAddress {
return i.PubKey.Address().Bytes() return i.PubKey.Address().Bytes()
} }
// GetPath implements Info interface
func (i ledgerInfo) GetAlgo() SigningAlgo {
return i.Algo
}
// GetPath implements Info interface // GetPath implements Info interface
func (i ledgerInfo) GetPath() (*hd.BIP44Params, error) { func (i ledgerInfo) GetPath() (*hd.BIP44Params, error) {
tmp := i.Path tmp := i.Path
@ -213,13 +229,15 @@ func (i ledgerInfo) GetPath() (*hd.BIP44Params, error) {
// offlineInfo is the public information about an offline key // offlineInfo is the public information about an offline key
type offlineInfo struct { type offlineInfo struct {
Name string `json:"name"` Name string `json:"name"`
Algo SigningAlgo `json:"algo"`
PubKey crypto.PubKey `json:"pubkey"` PubKey crypto.PubKey `json:"pubkey"`
} }
func newOfflineInfo(name string, pub crypto.PubKey) Info { func newOfflineInfo(name string, pub crypto.PubKey, algo SigningAlgo) Info {
return &offlineInfo{ return &offlineInfo{
Name: name, Name: name,
PubKey: pub, PubKey: pub,
Algo: algo,
} }
} }
@ -238,6 +256,11 @@ func (i offlineInfo) GetPubKey() crypto.PubKey {
return i.PubKey return i.PubKey
} }
// GetAlgo returns the signing algorithm for the key
func (i offlineInfo) GetAlgo() SigningAlgo {
return i.Algo
}
// GetAddress implements Info interface // GetAddress implements Info interface
func (i offlineInfo) GetAddress() types.AccAddress { func (i offlineInfo) GetAddress() types.AccAddress {
return i.PubKey.Address().Bytes() return i.PubKey.Address().Bytes()
@ -299,6 +322,11 @@ func (i multiInfo) GetAddress() types.AccAddress {
return i.PubKey.Address().Bytes() return i.PubKey.Address().Bytes()
} }
// GetPath implements Info interface
func (i multiInfo) GetAlgo() SigningAlgo {
return MultiAlgo
}
// GetPath implements Info interface // GetPath implements Info interface
func (i multiInfo) GetPath() (*hd.BIP44Params, error) { func (i multiInfo) GetPath() (*hd.BIP44Params, error) {
return nil, fmt.Errorf("BIP44 Paths are not available for this type") return nil, fmt.Errorf("BIP44 Paths are not available for this type")
@ -316,8 +344,10 @@ func unmarshalInfo(bz []byte) (info Info, err error) {
} }
type ( type (
// DeriveKeyFunc defines the function to derive a new key from a seed and hd path
DeriveKeyFunc func(mnemonic string, bip39Passphrase, hdPath string, algo SigningAlgo) ([]byte, error)
// PrivKeyGenFunc defines the function to convert derived key bytes to a tendermint private key // PrivKeyGenFunc defines the function to convert derived key bytes to a tendermint private key
PrivKeyGenFunc func(bz [32]byte) crypto.PrivKey PrivKeyGenFunc func(bz []byte, algo SigningAlgo) (crypto.PrivKey, error)
// KeybaseOption overrides options for the db // KeybaseOption overrides options for the db
KeybaseOption func(*kbOptions) KeybaseOption func(*kbOptions)

View File

@ -17,7 +17,7 @@ func Test_writeReadLedgerInfo(t *testing.T) {
bz, _ := hex.DecodeString("035AD6810A47F073553FF30D2FCC7E0D3B1C0B74B61A1AAA2582344037151E143A") bz, _ := hex.DecodeString("035AD6810A47F073553FF30D2FCC7E0D3B1C0B74B61A1AAA2582344037151E143A")
copy(tmpKey[:], bz) copy(tmpKey[:], bz)
lInfo := newLedgerInfo("some_name", tmpKey, *hd.NewFundraiserParams(5, types.CoinType, 1)) lInfo := newLedgerInfo("some_name", tmpKey, *hd.NewFundraiserParams(5, types.CoinType, 1), Secp256k1)
assert.Equal(t, TypeLedger, lInfo.GetType()) assert.Equal(t, TypeLedger, lInfo.GetType())
path, err := lInfo.GetPath() path, err := lInfo.GetPath()

View File

@ -17,7 +17,7 @@ func TestGenerateCoinKey(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
// Test creation // Test creation
info, err := keys.NewInMemoryKeyBase().CreateAccount("xxx", mnemonic, "", "012345678", 0, 0) info, err := keys.NewInMemoryKeyBase().CreateAccount("xxx", mnemonic, "", "012345678", crkeys.CreateHDPath(0, 0).String(), crkeys.Secp256k1)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, addr, info.GetAddress()) require.Equal(t, addr, info.GetAddress())
} }
@ -39,7 +39,7 @@ func TestGenerateSaveCoinKey(t *testing.T) {
require.Equal(t, addr, info.GetAddress()) require.Equal(t, addr, info.GetAddress())
// Test in-memory recovery // Test in-memory recovery
info, err = keys.NewInMemoryKeyBase().CreateAccount("xxx", mnemonic, "", "012345678", 0, 0) info, err = keys.NewInMemoryKeyBase().CreateAccount("xxx", mnemonic, "", "012345678", crkeys.CreateHDPath(0, 0).String(), crkeys.Secp256k1)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, addr, info.GetAddress()) require.Equal(t, addr, info.GetAddress())
} }

View File

@ -312,7 +312,7 @@ func DefaultSigVerificationGasConsumer(
var multisignature multisig.Multisignature var multisignature multisig.Multisignature
codec.Cdc.MustUnmarshalBinaryBare(sig, &multisignature) codec.Cdc.MustUnmarshalBinaryBare(sig, &multisignature)
consumeMultisignatureVerificationGas(meter, multisignature, pubkey, params) ConsumeMultisignatureVerificationGas(meter, multisignature, pubkey, params)
return nil return nil
default: default:
@ -320,7 +320,8 @@ func DefaultSigVerificationGasConsumer(
} }
} }
func consumeMultisignatureVerificationGas(meter sdk.GasMeter, // ConsumeMultisignatureVerificationGas consumes gas from a GasMeter for verifying a multisig pubkey signature
func ConsumeMultisignatureVerificationGas(meter sdk.GasMeter,
sig multisig.Multisignature, pubkey multisig.PubKeyMultisigThreshold, sig multisig.Multisignature, pubkey multisig.PubKeyMultisigThreshold,
params types.Params) { params types.Params) {
size := sig.BitArray.Size() size := sig.BitArray.Size()