Skip to content

Commit

Permalink
Merge PR cosmos#5439: Keybase: Multiple Signature Algorithms
Browse files Browse the repository at this point in the history
  • Loading branch information
sunnya97 authored and alexanderbez committed Jan 14, 2020
1 parent d452e93 commit f367087
Show file tree
Hide file tree
Showing 21 changed files with 418 additions and 200 deletions.
18 changes: 15 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`
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.
* 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.
* (baseapp) [\#5455](https://github.com/cosmos/cosmos-sdk/issues/5455) An `sdk.Context` is passed into the `router.Route()`
function.
Expand Down Expand Up @@ -177,10 +186,13 @@ that allows for arbitrary vesting periods.
* `IncrementSequenceDecorator`: Increments the account sequence for each signer to prevent replay attacks.
* (cli) [\#5223](https://github.com/cosmos/cosmos-sdk/issues/5223) Cosmos Ledger App v2.0.0 is now supported. The changes are backwards compatible and App v1.5.x is still supported.
* (x/staking) [\#5380](https://github.com/cosmos/cosmos-sdk/pull/5380) Introduced ability to store historical info entries in staking keeper, allows applications to introspect specified number of past headers and validator sets
* Introduces new parameter `HistoricalEntries` which allows applications to determine how many recent historical info entries they want to persist in store. Default value is 0.
* Introduces cli commands and rest routes to query historical information at a given height
* Introduces new parameter `HistoricalEntries` which allows applications to determine how many recent historical info entries they want to persist in store. Default value is 0.
* 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).
* (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.
* An `ApproxSqrt` function was also added for convenience around the common case of n=2.

Expand Down
34 changes: 32 additions & 2 deletions client/keys/add.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ const (
flagIndex = "index"
flagMultisig = "multisig"
flagNoSort = "nosort"
flagHDPath = "hd-path"
flagKeyAlgo = "algo"

// DefaultKeyPass contains the default key password for genesis transactions
DefaultKeyPass = "12345678"
Expand Down Expand Up @@ -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(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().String(flagHDPath, "", "Manual HD Path derivation (overrides BIP44 config)")
cmd.Flags().Uint32(flagAccount, 0, "Account 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().String(flagKeyAlgo, string(keys.Secp256k1), "Key signing algorithm to generate keys for")
return cmd
}

Expand Down Expand Up @@ -112,6 +116,14 @@ func RunAddCmd(cmd *cobra.Command, args []string, kb keys.Keybase, inBuf *bufio.
interactive := viper.GetBool(flagInteractive)
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) {
_, err = kb.Get(name)
if err == nil {
Expand Down Expand Up @@ -164,7 +176,7 @@ func RunAddCmd(cmd *cobra.Command, args []string, kb keys.Keybase, inBuf *bufio.
if err != nil {
return err
}
_, err = kb.CreateOffline(name, pk)
_, err = kb.CreateOffline(name, pk, algo)
if err != nil {
return err
}
Expand All @@ -174,8 +186,26 @@ func RunAddCmd(cmd *cobra.Command, args []string, kb keys.Keybase, inBuf *bufio.
account := uint32(viper.GetInt(flagAccount))
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 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()
info, err := kb.CreateLedger(name, keys.Secp256k1, bech32PrefixAccAddr, account, index)
if err != nil {
Expand Down Expand Up @@ -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 {
return err
}
Expand Down
5 changes: 3 additions & 2 deletions client/keys/delete_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/require"

"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/crypto/keys"
"github.com/cosmos/cosmos-sdk/tests"
)

Expand Down Expand Up @@ -44,13 +45,13 @@ func Test_runDeleteCmd(t *testing.T) {
if runningUnattended {
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)

if runningUnattended {
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)

if runningUnattended {
Expand Down
3 changes: 2 additions & 1 deletion client/keys/export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"github.com/stretchr/testify/require"

"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/crypto/keys"
"github.com/cosmos/cosmos-sdk/tests"
)

Expand All @@ -32,7 +33,7 @@ func Test_runExportCmd(t *testing.T) {
if runningUnattended {
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)

// Now enter password
Expand Down
3 changes: 2 additions & 1 deletion client/keys/list_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/stretchr/testify/require"

"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/crypto/keys"
"github.com/cosmos/cosmos-sdk/tests"
)

Expand Down Expand Up @@ -36,7 +37,7 @@ func Test_runListCmd(t *testing.T) {
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)

defer func() {
Expand Down
4 changes: 2 additions & 2 deletions client/keys/show_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,13 @@ func Test_runShowCmd(t *testing.T) {
if runningUnattended {
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)

if runningUnattended {
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)

// Now try single key
Expand Down
5 changes: 3 additions & 2 deletions client/keys/update_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"github.com/stretchr/testify/assert"

"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/crypto/keys"
"github.com/cosmos/cosmos-sdk/tests"
)

Expand Down Expand Up @@ -38,9 +39,9 @@ func Test_runUpdateCmd(t *testing.T) {

kb, err := NewKeyBaseFromHomeFlag()
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)
_, err = kb.CreateAccount(fakeKeyName2, tests.TestMnemonic, "", "", 0, 1)
_, err = kb.CreateAccount(fakeKeyName2, tests.TestMnemonic, "", "", "1", keys.Secp256k1)
assert.NoError(t, err)

// Try again now that we have keys
Expand Down
62 changes: 34 additions & 28 deletions crypto/keys/keybase.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import (
cryptoAmino "github.com/tendermint/tendermint/crypto/encoding/amino"
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/mintkey"
"github.com/cosmos/cosmos-sdk/types"
Expand Down Expand Up @@ -57,7 +56,7 @@ const (
var (
// ErrUnsupportedSigningAlgo is raised when the caller tries to use a
// 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
// different language than english for creating a mnemonic sentence.
Expand Down Expand Up @@ -101,18 +100,10 @@ func (kb dbKeybase) CreateMnemonic(
// CreateAccount converts a mnemonic to a private key and persists it, encrypted
// with the given password.
func (kb dbKeybase) CreateAccount(
name, mnemonic, bip39Passwd, encryptPasswd string, account uint32, index uint32,
name, mnemonic, bip39Passwd, encryptPasswd, hdPath string, algo SigningAlgo,
) (Info, error) {

return kb.base.CreateAccount(kb, name, mnemonic, bip39Passwd, encryptPasswd, account, index)
}

// 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)
return kb.base.CreateAccount(kb, name, mnemonic, bip39Passwd, encryptPasswd, hdPath, algo)
}

// CreateLedger creates a new locally-stored reference to a Ledger keypair.
Expand All @@ -126,8 +117,8 @@ func (kb dbKeybase) CreateLedger(

// CreateOffline creates a new reference to an offline keypair. It returns the
// created key info.
func (kb dbKeybase) CreateOffline(name string, pub tmcrypto.PubKey) (Info, error) {
return kb.base.writeOfflineKey(kb, name, pub), nil
func (kb dbKeybase) CreateOffline(name string, pub tmcrypto.PubKey, algo SigningAlgo) (Info, error) {
return kb.base.writeOfflineKey(kb, name, pub, algo), nil
}

// CreateMulti creates a new reference to a multisig (offline) keypair. It
Expand Down Expand Up @@ -199,7 +190,7 @@ func (kb dbKeybase) Sign(name, passphrase string, msg []byte) (sig []byte, pub t
return
}

priv, err = mintkey.UnarmorDecryptPrivKey(i.PrivKeyArmor, passphrase)
priv, _, err = mintkey.UnarmorDecryptPrivKey(i.PrivKeyArmor, passphrase)
if err != nil {
return nil, nil, err
}
Expand Down Expand Up @@ -238,7 +229,7 @@ func (kb dbKeybase) ExportPrivateKeyObject(name string, passphrase string) (tmcr
return nil, err
}

priv, err = mintkey.UnarmorDecryptPrivKey(linfo.PrivKeyArmor, passphrase)
priv, _, err = mintkey.UnarmorDecryptPrivKey(linfo.PrivKeyArmor, passphrase)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -272,7 +263,7 @@ func (kb dbKeybase) ExportPubKey(name string) (armor string, err error) {
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.
Expand All @@ -285,7 +276,12 @@ func (kb dbKeybase) ExportPrivKey(name string, decryptPassphrase string,
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
Expand All @@ -296,12 +292,12 @@ func (kb dbKeybase) ImportPrivKey(name string, armor string, passphrase string)
return errors.New("Cannot overwrite key " + name)
}

privKey, err := mintkey.UnarmorDecryptPrivKey(armor, passphrase)
privKey, algo, err := mintkey.UnarmorDecryptPrivKey(armor, passphrase)
if err != nil {
return errors.Wrap(err, "couldn't import private key")
}

kb.writeLocalKey(name, privKey, passphrase)
kb.writeLocalKey(name, privKey, passphrase, SigningAlgo(algo))
return nil
}

Expand Down Expand Up @@ -329,7 +325,7 @@ func (kb dbKeybase) ImportPubKey(name string, armor string) (err error) {
return errors.New("Cannot overwrite data for name " + name)
}

pubBytes, err := mintkey.UnarmorPubKeyBytes(armor)
pubBytes, algo, err := mintkey.UnarmorPubKeyBytes(armor)
if err != nil {
return
}
Expand All @@ -339,7 +335,7 @@ func (kb dbKeybase) ImportPubKey(name string, armor string) (err error) {
return
}

kb.base.writeOfflineKey(kb, name, pubKey)
kb.base.writeOfflineKey(kb, name, pubKey, SigningAlgo(algo))
return
}

Expand All @@ -355,7 +351,7 @@ func (kb dbKeybase) Delete(name, passphrase string, skipPass bool) error {
}

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
}
}
Expand All @@ -382,7 +378,7 @@ func (kb dbKeybase) Update(name, oldpass string, getNewpass func() (string, erro
case localInfo:
linfo := i

key, err := mintkey.UnarmorDecryptPrivKey(linfo.PrivKeyArmor, oldpass)
key, _, err := mintkey.UnarmorDecryptPrivKey(linfo.PrivKeyArmor, oldpass)
if err != nil {
return err
}
Expand All @@ -392,7 +388,7 @@ func (kb dbKeybase) Update(name, oldpass string, getNewpass func() (string, erro
return err
}

kb.writeLocalKey(name, key, newpass)
kb.writeLocalKey(name, key, newpass, i.GetAlgo())
return nil

default:
Expand All @@ -405,13 +401,23 @@ func (kb dbKeybase) CloseDB() {
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
privArmor := mintkey.EncryptArmorPrivKey(priv, passphrase)
privArmor := mintkey.EncryptArmorPrivKey(priv, passphrase, string(algo))

// make Info
pub := priv.PubKey()
info := newLocalInfo(name, pub, privArmor)
info := newLocalInfo(name, pub, privArmor, algo)

kb.writeInfo(name, info)
return info
Expand Down
Loading

0 comments on commit f367087

Please sign in to comment.