Interchangable PrivKey implementations in keybase (#5278)

Allow for the keybase to be configured to override the implementation
of the key that is saved to the keybase.

Closes: #4941
This commit is contained in:
Austin Abell
2019-12-12 21:52:24 +00:00
committed by Alessio Treglia
parent efcd5fd315
commit 0e28da23e7
32 changed files with 273 additions and 118 deletions
+25 -16
View File
@@ -5,6 +5,7 @@ import (
"bytes"
"errors"
"fmt"
"io"
"sort"
bip39 "github.com/bartekn/go-bip39"
@@ -36,7 +37,8 @@ const (
DefaultKeyPass = "12345678"
)
func addKeyCommand() *cobra.Command {
// AddKeyCommand defines a keys command to add a generated or recovered private key to keybase.
func AddKeyCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "add <name>",
Short: "Add an encrypted private key (either newly generated or recovered), encrypt it, and save to disk",
@@ -75,6 +77,24 @@ the flag --nosort is set.
return cmd
}
func getKeybase(transient bool, buf io.Reader) (keys.Keybase, error) {
if transient {
return keys.NewInMemory(), nil
}
return NewKeyringFromHomeFlag(buf)
}
func runAddCmd(cmd *cobra.Command, args []string) error {
inBuf := bufio.NewReader(cmd.InOrStdin())
kb, err := getKeybase(viper.GetBool(flagDryRun), inBuf)
if err != nil {
return err
}
return RunAddCmd(cmd, args, kb, inBuf)
}
/*
input
- bip39 mnemonic
@@ -84,26 +104,15 @@ input
output
- armor encrypted private key (saved to file)
*/
func runAddCmd(cmd *cobra.Command, args []string) error {
var kb keys.Keybase
func RunAddCmd(cmd *cobra.Command, args []string, kb keys.Keybase, inBuf *bufio.Reader) error {
var err error
inBuf := bufio.NewReader(cmd.InOrStdin())
name := args[0]
interactive := viper.GetBool(flagInteractive)
showMnemonic := !viper.GetBool(flagNoBackup)
if viper.GetBool(flagDryRun) {
// we throw this away, so don't enforce args,
// we want to get a new random seed phrase quickly
kb = keys.NewInMemory()
} else {
kb, err = NewKeyringFromHomeFlag(cmd.InOrStdin())
if err != nil {
return err
}
if !viper.GetBool(flagDryRun) {
_, err = kb.Get(name)
if err == nil {
// account exists, ask for user confirmation
@@ -273,9 +282,9 @@ func printCreate(cmd *cobra.Command, info keys.Info, showMnemonic bool, mnemonic
var jsonString []byte
if viper.GetBool(flags.FlagIndentResponse) {
jsonString, err = cdc.MarshalJSONIndent(out, "", " ")
jsonString, err = KeysCdc.MarshalJSONIndent(out, "", " ")
} else {
jsonString, err = cdc.MarshalJSON(out)
jsonString, err = KeysCdc.MarshalJSON(out)
}
if err != nil {
+2 -2
View File
@@ -33,7 +33,7 @@ func Test_runAddCmdLedgerWithCustomCoinType(t *testing.T) {
config.SetBech32PrefixForValidator(bech32PrefixValAddr, bech32PrefixValPub)
config.SetBech32PrefixForConsensusNode(bech32PrefixConsAddr, bech32PrefixConsPub)
cmd := addKeyCommand()
cmd := AddKeyCommand()
require.NotNil(t, cmd)
// Prepare a keybase
@@ -80,7 +80,7 @@ func Test_runAddCmdLedgerWithCustomCoinType(t *testing.T) {
func Test_runAddCmdLedger(t *testing.T) {
runningUnattended := isRunningUnattended()
cmd := addKeyCommand()
cmd := AddKeyCommand()
require.NotNil(t, cmd)
mockIn, _, _ := tests.ApplyMockIO(cmd)
+1 -1
View File
@@ -15,7 +15,7 @@ import (
func Test_runAddCmdBasic(t *testing.T) {
runningUnattended := isRunningUnattended()
cmd := addKeyCommand()
cmd := AddKeyCommand()
assert.NotNil(t, cmd)
mockIn, _, _ := tests.ApplyMockIO(cmd)
+7 -6
View File
@@ -4,20 +4,21 @@ import (
"github.com/cosmos/cosmos-sdk/codec"
)
var cdc *codec.Codec
// KeysCdc defines codec to be used with key operations
var KeysCdc *codec.Codec
func init() {
cdc = codec.New()
codec.RegisterCrypto(cdc)
cdc.Seal()
KeysCdc = codec.New()
codec.RegisterCrypto(KeysCdc)
KeysCdc.Seal()
}
// marshal keys
func MarshalJSON(o interface{}) ([]byte, error) {
return cdc.MarshalJSON(o)
return KeysCdc.MarshalJSON(o)
}
// unmarshal json
func UnmarshalJSON(bz []byte, ptr interface{}) error {
return cdc.UnmarshalJSON(bz, ptr)
return KeysCdc.UnmarshalJSON(bz, ptr)
}
+2 -1
View File
@@ -16,7 +16,8 @@ const (
flagForce = "force"
)
func deleteKeyCommand() *cobra.Command {
// DeleteKeyCommand deletes a key from the key store.
func DeleteKeyCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "delete <name>...",
Short: "Delete the given keys",
+1 -1
View File
@@ -14,7 +14,7 @@ import (
func Test_runDeleteCmd(t *testing.T) {
runningUnattended := isRunningUnattended()
deleteKeyCommand := deleteKeyCommand()
deleteKeyCommand := DeleteKeyCommand()
mockIn, _, _ := tests.ApplyMockIO(deleteKeyCommand)
yesF, _ := deleteKeyCommand.Flags().GetBool(flagYes)
+2 -1
View File
@@ -8,7 +8,8 @@ import (
"github.com/cosmos/cosmos-sdk/client/input"
)
func exportKeyCommand() *cobra.Command {
// ExportKeyCommand exports private keys from the key store.
func ExportKeyCommand() *cobra.Command {
return &cobra.Command{
Use: "export <name>",
Short: "Export private keys",
+1 -1
View File
@@ -12,7 +12,7 @@ import (
func Test_runExportCmd(t *testing.T) {
runningUnattended := isRunningUnattended()
exportKeyCommand := exportKeyCommand()
exportKeyCommand := ExportKeyCommand()
mockIn, _, _ := tests.ApplyMockIO(exportKeyCommand)
// Now add a temporary keybase
+2 -1
View File
@@ -9,7 +9,8 @@ import (
"github.com/cosmos/cosmos-sdk/client/input"
)
func importKeyCommand() *cobra.Command {
// ImportKeyCommand imports private keys from a keyfile.
func ImportKeyCommand() *cobra.Command {
return &cobra.Command{
Use: "import <name> <keyfile>",
Short: "Import private keys into the local keybase",
+1 -1
View File
@@ -14,7 +14,7 @@ import (
func Test_runImportCmd(t *testing.T) {
runningUnattended := isRunningUnattended()
importKeyCommand := importKeyCommand()
importKeyCommand := ImportKeyCommand()
mockIn, _, _ := tests.ApplyMockIO(importKeyCommand)
// Now add a temporary keybase
+2 -1
View File
@@ -9,7 +9,8 @@ import (
const flagListNames = "list-names"
func listKeysCmd() *cobra.Command {
// ListKeysCmd lists all keys in the key store.
func ListKeysCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "list",
Short: "List all keys",
+1 -1
View File
@@ -18,7 +18,7 @@ func Test_runListCmd(t *testing.T) {
args []string
}
cmdBasic := listKeysCmd()
cmdBasic := ListKeysCmd()
// Prepare some keybases
kbHome1, cleanUp1 := tests.NewTestCaseDir(t)
+2 -1
View File
@@ -18,7 +18,8 @@ import (
// is not needed for importing into the Keyring keystore.
const migratePassphrase = "NOOP_PASSPHRASE"
func migrateCommand() *cobra.Command {
// MigrateCommand migrates key information from legacy keybase to OS secret store.
func MigrateCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "migrate",
Short: "Migrate key information from the lagacy key database to the OS secret store, or encrypted file store as a fall-back and save it",
+2 -2
View File
@@ -13,7 +13,7 @@ import (
)
func Test_runMigrateCmd(t *testing.T) {
cmd := addKeyCommand()
cmd := AddKeyCommand()
assert.NotNil(t, cmd)
mockIn, _, _ := tests.ApplyMockIO(cmd)
@@ -29,7 +29,7 @@ func Test_runMigrateCmd(t *testing.T) {
assert.NoError(t, err)
viper.Set(flags.FlagDryRun, true)
cmd = migrateCommand()
cmd = MigrateCommand()
mockIn, _, _ = tests.ApplyMockIO(cmd)
mockIn.Reset("test1234\n")
assert.NoError(t, runMigrateCmd(cmd, []string{}))
+2 -1
View File
@@ -17,7 +17,8 @@ const (
mnemonicEntropySize = 256
)
func mnemonicKeyCommand() *cobra.Command {
// MnemonicKeyCommand computes the bip39 memonic for input entropy.
func MnemonicKeyCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "mnemonic",
Short: "Compute the bip39 mnemonic for some input entropy",
+2 -2
View File
@@ -11,13 +11,13 @@ import (
)
func Test_RunMnemonicCmdNormal(t *testing.T) {
cmdBasic := mnemonicKeyCommand()
cmdBasic := MnemonicKeyCommand()
err := runMnemonicCmd(cmdBasic, []string{})
require.NoError(t, err)
}
func Test_RunMnemonicCmdUser(t *testing.T) {
cmdUser := mnemonicKeyCommand()
cmdUser := MnemonicKeyCommand()
err := cmdUser.Flags().Set(flagUserEntropy, "1")
assert.NoError(t, err)
+4 -3
View File
@@ -67,7 +67,8 @@ func (bo bech32Output) String() string {
return fmt.Sprintf("Bech32 Formats:\n%s", strings.Join(out, "\n"))
}
func parseKeyStringCommand() *cobra.Command {
// ParseKeyStringCommand parses an address from hex to bech32 and vice versa.
func ParseKeyStringCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "parse <hex-or-bech32-address>",
Short: "Parse address from hex to bech32 and vice versa",
@@ -124,9 +125,9 @@ func displayParseKeyInfo(stringer fmt.Stringer) {
case OutputFormatJSON:
if viper.GetBool(flags.FlagIndentResponse) {
out, err = cdc.MarshalJSONIndent(stringer, "", " ")
out, err = KeysCdc.MarshalJSONIndent(stringer, "", " ")
} else {
out = cdc.MustMarshalJSON(stringer)
out = KeysCdc.MustMarshalJSON(stringer)
}
}
+10 -10
View File
@@ -20,17 +20,17 @@ func Commands() *cobra.Command {
needs to sign with a private key.`,
}
cmd.AddCommand(
mnemonicKeyCommand(),
addKeyCommand(),
exportKeyCommand(),
importKeyCommand(),
listKeysCmd(),
showKeysCmd(),
MnemonicKeyCommand(),
AddKeyCommand(),
ExportKeyCommand(),
ImportKeyCommand(),
ListKeysCmd(),
ShowKeysCmd(),
flags.LineBreak,
deleteKeyCommand(),
updateKeyCommand(),
parseKeyStringCommand(),
migrateCommand(),
DeleteKeyCommand(),
UpdateKeyCommand(),
ParseKeyStringCommand(),
MigrateCommand(),
)
cmd.PersistentFlags().String(flags.FlagKeyringBackend, flags.DefaultKeyringBackend, "Select keyring's backend (os|file|test)")
viper.BindPFlag(flags.FlagKeyringBackend, cmd.Flags().Lookup(flags.FlagKeyringBackend))
+2 -1
View File
@@ -32,7 +32,8 @@ const (
defaultMultiSigKeyName = "multi"
)
func showKeysCmd() *cobra.Command {
// ShowKeysCmd shows key information for a given key name.
func ShowKeysCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "show [name [name...]]",
Short: "Show key info for the given name",
+2 -2
View File
@@ -28,7 +28,7 @@ func Test_multiSigKey_Properties(t *testing.T) {
}
func Test_showKeysCmd(t *testing.T) {
cmd := showKeysCmd()
cmd := ShowKeysCmd()
require.NotNil(t, cmd)
require.Equal(t, "false", cmd.Flag(FlagAddress).DefValue)
require.Equal(t, "false", cmd.Flag(FlagPublicKey).DefValue)
@@ -36,7 +36,7 @@ func Test_showKeysCmd(t *testing.T) {
func Test_runShowCmd(t *testing.T) {
runningUnattended := isRunningUnattended()
cmd := showKeysCmd()
cmd := ShowKeysCmd()
mockIn, _, _ := tests.ApplyMockIO(cmd)
require.EqualError(t, runShowCmd(cmd, []string{"invalid"}), "The specified item could not be found in the keyring")
require.EqualError(t, runShowCmd(cmd, []string{"invalid1", "invalid2"}), "The specified item could not be found in the keyring")
+3 -1
View File
@@ -8,7 +8,9 @@ import (
"github.com/cosmos/cosmos-sdk/client/input"
)
func updateKeyCommand() *cobra.Command {
// UpdateKeyCommand changes the password of a key in the keybase.
// It takes no effect on keys managed by new the keyring-based keybase implementation.
func UpdateKeyCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "update <name>",
Short: "Change the password used to protect private key",
+2 -2
View File
@@ -11,7 +11,7 @@ import (
)
func Test_updateKeyCommand(t *testing.T) {
cmd := updateKeyCommand()
cmd := UpdateKeyCommand()
assert.NotNil(t, cmd)
// No flags or defaults to validate
}
@@ -20,7 +20,7 @@ func Test_runUpdateCmd(t *testing.T) {
fakeKeyName1 := "runUpdateCmd_Key1"
fakeKeyName2 := "runUpdateCmd_Key2"
cmd := updateKeyCommand()
cmd := UpdateKeyCommand()
// fails because it requests a password
assert.EqualError(t, runUpdateCmd(cmd, []string{fakeKeyName1}), "EOF")
+19 -16
View File
@@ -26,30 +26,33 @@ const (
type bechKeyOutFn func(keyInfo keys.Info) (keys.KeyOutput, error)
// NewKeyBaseFromHomeFlag initializes a Keybase based on the configuration.
func NewKeyBaseFromHomeFlag() (keys.Keybase, error) {
// NewKeyBaseFromHomeFlag initializes a Keybase based on the configuration. Keybase
// options can be applied when generating this new Keybase.
func NewKeyBaseFromHomeFlag(opts ...keys.KeybaseOption) (keys.Keybase, error) {
rootDir := viper.GetString(flags.FlagHome)
return NewKeyBaseFromDir(rootDir)
return NewKeyBaseFromDir(rootDir, opts...)
}
// NewKeyBaseFromDir initializes a keybase at a particular dir.
func NewKeyBaseFromDir(rootDir string) (keys.Keybase, error) {
return getLazyKeyBaseFromDir(rootDir)
// NewKeyBaseFromDir initializes a keybase at the rootDir directory. Keybase
// options can be applied when generating this new Keybase.
func NewKeyBaseFromDir(rootDir string, opts ...keys.KeybaseOption) (keys.Keybase, error) {
return getLazyKeyBaseFromDir(rootDir, opts...)
}
// NewInMemoryKeyBase returns a storage-less keybase.
func NewInMemoryKeyBase() keys.Keybase { return keys.NewInMemory() }
// NewKeyBaseFromHomeFlag initializes a keyring based on configuration.
func NewKeyringFromHomeFlag(input io.Reader) (keys.Keybase, error) {
return NewKeyringFromDir(viper.GetString(flags.FlagHome), input)
// NewKeyBaseFromHomeFlag initializes a keyring based on configuration. Keybase
// options can be applied when generating this new Keybase.
func NewKeyringFromHomeFlag(input io.Reader, opts ...keys.KeybaseOption) (keys.Keybase, error) {
return NewKeyringFromDir(viper.GetString(flags.FlagHome), input, opts...)
}
// NewKeyBaseFromDir initializes a keyring at the given directory.
// If the viper flag flags.FlagKeyringBackend is set to file, it returns an on-disk keyring with
// CLI prompt support only. If flags.FlagKeyringBackend is set to test it will return an on-disk,
// password-less keyring that could be used for testing purposes.
func NewKeyringFromDir(rootDir string, input io.Reader) (keys.Keybase, error) {
func NewKeyringFromDir(rootDir string, input io.Reader, opts ...keys.KeybaseOption) (keys.Keybase, error) {
keyringBackend := viper.GetString(flags.FlagKeyringBackend)
switch keyringBackend {
case flags.KeyringBackendTest:
@@ -62,8 +65,8 @@ func NewKeyringFromDir(rootDir string, input io.Reader) (keys.Keybase, error) {
return nil, fmt.Errorf("unknown keyring backend %q", keyringBackend)
}
func getLazyKeyBaseFromDir(rootDir string) (keys.Keybase, error) {
return keys.New(defaultKeyDBName, filepath.Join(rootDir, "keys")), nil
func getLazyKeyBaseFromDir(rootDir string, opts ...keys.KeybaseOption) (keys.Keybase, error) {
return keys.New(defaultKeyDBName, filepath.Join(rootDir, "keys"), opts...), nil
}
func printKeyInfo(keyInfo keys.Info, bechKeyOut bechKeyOutFn) {
@@ -80,9 +83,9 @@ func printKeyInfo(keyInfo keys.Info, bechKeyOut bechKeyOutFn) {
var out []byte
var err error
if viper.GetBool(flags.FlagIndentResponse) {
out, err = cdc.MarshalJSONIndent(ko, "", " ")
out, err = KeysCdc.MarshalJSONIndent(ko, "", " ")
} else {
out, err = cdc.MarshalJSON(ko)
out, err = KeysCdc.MarshalJSON(ko)
}
if err != nil {
panic(err)
@@ -107,9 +110,9 @@ func printInfos(infos []keys.Info) {
var err error
if viper.GetBool(flags.FlagIndentResponse) {
out, err = cdc.MarshalJSONIndent(kos, "", " ")
out, err = KeysCdc.MarshalJSONIndent(kos, "", " ")
} else {
out, err = cdc.MarshalJSON(kos)
out, err = KeysCdc.MarshalJSON(kos)
}
if err != nil {