From 7361269eb669146e0d5bfdf660006344e91212e1 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Thu, 22 Feb 2018 16:49:32 +0100 Subject: [PATCH] Updated keys add --- client/keys.go | 14 ++++- client/keys/add.go | 128 +++++++++++++++++++++++++++++++++++++++++++ client/keys/new.go | 98 --------------------------------- client/keys/utils.go | 4 +- 4 files changed, 142 insertions(+), 102 deletions(-) create mode 100644 client/keys/add.go delete mode 100644 client/keys/new.go diff --git a/client/keys.go b/client/keys.go index 73c2226e5e..f64ba405f6 100644 --- a/client/keys.go +++ b/client/keys.go @@ -9,8 +9,8 @@ import ( // KeyDBName is the directory under root where we store the keys const KeyDBName = "keys" -// GetKeyManager initializes a key manager based on the configuration -func GetKeyManager(rootDir string) (keys.Keybase, error) { +// GetKeyBase initializes a keybase based on the configuration +func GetKeyBase(rootDir string) (keys.Keybase, error) { db, err := dbm.NewGoLevelDB(KeyDBName, rootDir) if err != nil { return nil, err @@ -21,3 +21,13 @@ func GetKeyManager(rootDir string) (keys.Keybase, error) { ) return keybase, nil } + +// MockKeyBase generates an in-memory keybase that will be discarded +// useful for --dry-run to generate a seed phrase without +// storing the key +func MockKeyBase() keys.Keybase { + return keys.New( + dbm.NewMemDB(), + words.MustLoadCodec("english"), + ) +} diff --git a/client/keys/add.go b/client/keys/add.go new file mode 100644 index 0000000000..f6f1698b7b --- /dev/null +++ b/client/keys/add.go @@ -0,0 +1,128 @@ +// Copyright © 2017 Ethan Frey +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package keys + +import ( + "fmt" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/pkg/errors" + "github.com/spf13/cobra" + "github.com/spf13/viper" + + "github.com/tendermint/go-crypto/keys" + "github.com/tendermint/tmlibs/cli" +) + +const ( + flagType = "type" + flagRecover = "recover" + flagNoBackup = "no-backup" + flagDryRun = "dry-run" +) + +func addKeyCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "add ", + Short: "Create a new key, or import from seed", + Long: `Add a public/private key pair to the key store. +If you select --seed/-s you can recover a key from the seed +phrase, otherwise, a new key will be generated.`, + RunE: runAddCmd, + } + cmd.Flags().StringP(flagType, "t", "ed25519", "Type of private key (ed25519|secp256k1|ledger)") + 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") + return cmd +} + +func runAddCmd(cmd *cobra.Command, args []string) error { + var kb keys.Keybase + var err error + var name, pass string + + if viper.GetBool(flagDryRun) { + // we throw this away, so don't enforce args, + // we want to get a new random seed phrase quickly + kb = client.MockKeyBase() + pass = "throwing-this-key-away" + name = "inmemorykey" + } else { + if len(args) != 1 || len(args[0]) == 0 { + return errors.New("You must provide a name for the key") + } + name = args[0] + kb, err = GetKeyBase() + if err != nil { + return err + } + pass, err = getCheckPassword("Enter a passphrase for your key:", "Repeat the passphrase:") + if err != nil { + return err + } + } + + if viper.GetBool(flagRecover) { + seed, err := getPassword("Enter your recovery seed phrase:") + if err != nil { + return err + } + info, err := kb.Recover(name, pass, seed) + if err != nil { + return err + } + // print out results without the seed phrase + viper.Set(flagNoBackup, true) + printCreate(info, "") + } else { + algo := keys.CryptoAlgo(viper.GetString(flagType)) + info, seed, err := kb.Create(name, pass, algo) + if err != nil { + return err + } + printCreate(info, seed) + } + return nil +} + +// addOutput lets us json format the data +type addOutput struct { + Key keys.Info `json:"key"` + Seed string `json:"seed"` +} + +func printCreate(info keys.Info, seed string) { + switch viper.Get(cli.OutputFlag) { + case "text": + printInfo(info) + // print seed unless requested not to. + if !viper.GetBool(flagNoBackup) { + fmt.Println("**Important** write this seed phrase in a safe place.") + fmt.Println("It is the only way to recover your account if you ever forget your password.\n") + fmt.Println(seed) + } + case "json": + out := addOutput{Key: info} + if !viper.GetBool(flagNoBackup) { + out.Seed = seed + } + json, err := MarshalJSON(out) + if err != nil { + panic(err) // really shouldn't happen... + } + fmt.Println(string(json)) + } +} diff --git a/client/keys/new.go b/client/keys/new.go deleted file mode 100644 index 8884f60a07..0000000000 --- a/client/keys/new.go +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright © 2017 Ethan Frey -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package keys - -import ( - "fmt" - - "github.com/pkg/errors" - "github.com/spf13/cobra" - "github.com/spf13/viper" - - "github.com/tendermint/go-crypto/keys" - "github.com/tendermint/tmlibs/cli" -) - -const ( - flagType = "type" - flagNoBackup = "no-backup" -) - -// newCmd represents the new command -var newCmd = &cobra.Command{ - Use: "new [name]", - Short: "Create a new public/private key pair", - Long: `Add a public/private key pair to the key store. -The password must be entered in the terminal and not -passed as a command line argument for security.`, - RunE: runNewCmd, -} - -func init() { - newCmd.Flags().StringP(flagType, "t", "ed25519", "Type of key (ed25519|secp256k1|ledger") - newCmd.Flags().Bool(flagNoBackup, false, "Don't print out seed phrase (if others are watching the terminal)") -} - -func runNewCmd(cmd *cobra.Command, args []string) error { - if len(args) != 1 || len(args[0]) == 0 { - return errors.New("You must provide a name for the key") - } - name := args[0] - algo := keys.CryptoAlgo(viper.GetString(flagType)) - - pass, err := getCheckPassword("Enter a passphrase:", "Repeat the passphrase:") - if err != nil { - return err - } - - kb, err := GetKeyBase() - if err != nil { - return err - } - - info, seed, err := kb.Create(name, pass, algo) - if err == nil { - printCreate(info, seed) - } - return err -} - -type NewOutput struct { - Key keys.Info `json:"key"` - Seed string `json:"seed"` -} - -func printCreate(info keys.Info, seed string) { - switch viper.Get(cli.OutputFlag) { - case "text": - printInfo(info) - // print seed unless requested not to. - if !viper.GetBool(flagNoBackup) { - fmt.Println("**Important** write this seed phrase in a safe place.") - fmt.Println("It is the only way to recover your account if you ever forget your password.\n") - fmt.Println(seed) - } - case "json": - out := NewOutput{Key: info} - if !viper.GetBool(flagNoBackup) { - out.Seed = seed - } - json, err := MarshalJSON(out) - if err != nil { - panic(err) // really shouldn't happen... - } - fmt.Println(string(json)) - } -} diff --git a/client/keys/utils.go b/client/keys/utils.go index 7e34dcd8eb..346aa17bcf 100644 --- a/client/keys/utils.go +++ b/client/keys/utils.go @@ -29,11 +29,11 @@ var ( func GetKeyBase() (keys.Keybase, error) { if keybase == nil { rootDir := viper.GetString(cli.HomeFlag) - keyman, err := client.GetKeyManager(rootDir) + kb, err := client.GetKeyBase(rootDir) if err != nil { return nil, err } - keybase = keyman + keybase = kb } return keybase, nil }