183 lines
4.6 KiB
Go
183 lines
4.6 KiB
Go
package keys
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"os"
|
|
|
|
"github.com/bartekn/go-bip39"
|
|
"github.com/pkg/errors"
|
|
"github.com/spf13/cobra"
|
|
"github.com/spf13/viper"
|
|
|
|
"github.com/cosmos/cosmos-sdk/client"
|
|
"github.com/cosmos/cosmos-sdk/crypto/keys"
|
|
"github.com/cosmos/cosmos-sdk/crypto/keys/hd"
|
|
)
|
|
|
|
const (
|
|
flagNewDefault = "default"
|
|
flagBIP44Path = "bip44-path"
|
|
)
|
|
|
|
func newKeyCommand() *cobra.Command {
|
|
cmd := &cobra.Command{
|
|
Use: "new",
|
|
Short: "Interactive command to derive a new private key, encrypt it, and save to disk",
|
|
Long: `Derive a new private key using an interactive command that will prompt you for each input.
|
|
Optionally specify a bip39 mnemonic, a bip39 passphrase to further secure the mnemonic,
|
|
and a bip32 HD path to derive a specific account. The key will be stored under the given name
|
|
and encrypted with the given password. The only input that is required is the encryption password.`,
|
|
Args: cobra.ExactArgs(1),
|
|
RunE: runNewCmd,
|
|
}
|
|
cmd.Flags().Bool(flagNewDefault, false, "Skip the prompts and just use the default values for everything")
|
|
cmd.Flags().Bool(client.FlagUseLedger, false, "Store a local reference to a private key on a Ledger device")
|
|
cmd.Flags().String(flagBIP44Path, "44'/118'/0'/0/0", "BIP44 path from which to derive a private key")
|
|
return cmd
|
|
}
|
|
|
|
/*
|
|
input
|
|
- bip39 mnemonic
|
|
- bip39 passphrase
|
|
- bip44 path
|
|
- local encryption password
|
|
output
|
|
- armor encrypted private key (saved to file)
|
|
*/
|
|
// nolint: gocyclo
|
|
func runNewCmd(cmd *cobra.Command, args []string) error {
|
|
name := args[0]
|
|
kb, err := GetKeyBase()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
buf := client.BufferStdin()
|
|
_, err = kb.Get(name)
|
|
if err == nil {
|
|
// account exists, ask for user confirmation
|
|
if response, err := client.GetConfirmation(
|
|
fmt.Sprintf("> override the existing name %s", name), buf); err != nil || !response {
|
|
return err
|
|
}
|
|
}
|
|
|
|
flags := cmd.Flags()
|
|
useDefaults, _ := flags.GetBool(flagNewDefault)
|
|
bipFlag := flags.Lookup(flagBIP44Path)
|
|
|
|
bip44Params, err := getBIP44ParamsAndPath(bipFlag.Value.String(), bipFlag.Changed || useDefaults)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// if we're using ledger, only thing we need is the path.
|
|
// generate key and we're done.
|
|
if viper.GetBool(client.FlagUseLedger) {
|
|
|
|
algo := keys.Secp256k1 // SigningAlgo(viper.GetString(flagType))
|
|
path := bip44Params.DerivationPath() // ccrypto.DerivationPath{44, 118, account, 0, index}
|
|
info, err := kb.CreateLedger(name, path, algo)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
printCreate(info, "")
|
|
return nil
|
|
}
|
|
|
|
// get the mnemonic
|
|
var mnemonic string
|
|
if !useDefaults {
|
|
mnemonic, err = client.GetString("> Enter your bip39 mnemonic, or hit enter to generate one.", buf)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if len(mnemonic) == 0 {
|
|
// read entropy seed straight from crypto.Rand and convert to mnemonic
|
|
entropySeed, err := bip39.NewEntropy(mnemonicEntropySize)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
mnemonic, err = bip39.NewMnemonic(entropySeed[:])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// get bip39 passphrase
|
|
var bip39Passphrase string
|
|
if !useDefaults {
|
|
printStep()
|
|
printPrefixed("Enter your bip39 passphrase. This is combined with the mnemonic to derive the seed")
|
|
bip39Passphrase, err = client.GetString("> Most users should just hit enter to use the default, \"\"", buf)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// if they use one, make them re-enter it
|
|
if len(bip39Passphrase) != 0 {
|
|
p2, err := client.GetString("Repeat the passphrase:", buf)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if bip39Passphrase != p2 {
|
|
return errors.New("passphrases don't match")
|
|
}
|
|
}
|
|
}
|
|
|
|
// get the encryption password
|
|
printStep()
|
|
encryptPassword, err := client.GetCheckPassword(
|
|
"> Enter a passphrase to encrypt your key to disk:",
|
|
"> Repeat the passphrase:", buf)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
info, err := kb.Derive(name, mnemonic, bip39Passphrase, encryptPassword, *bip44Params)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_ = info
|
|
|
|
return nil
|
|
}
|
|
|
|
func getBIP44ParamsAndPath(path string, flagSet bool) (*hd.BIP44Params, error) {
|
|
buf := bufio.NewReader(os.Stdin)
|
|
bip44Path := path
|
|
|
|
// if it wasnt set in the flag, give it a chance to overide interactively
|
|
if !flagSet {
|
|
printStep()
|
|
|
|
var err error
|
|
bip44Path, err = client.GetString(fmt.Sprintf("> Enter your bip44 path. Default is %s\n", path), buf)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if len(bip44Path) == 0 {
|
|
bip44Path = path
|
|
}
|
|
}
|
|
|
|
bip44params, err := hd.NewParamsFromPath(bip44Path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return bip44params, nil
|
|
}
|
|
|
|
func printPrefixed(msg string) {
|
|
fmt.Printf("> %s\n", msg)
|
|
}
|
|
|
|
func printStep() {
|
|
printPrefixed("-------------------------------------")
|
|
}
|