client/keys: support export of unarmored private key (#8043)
The --unarmored-hex and --unsafe flags are added to the keys export command. Users must use both to export private keys material. The output would be in hexadecimal format and unarmored. See #8042 for scope and motivations. introduce new UnsafeKeyring interface. Unsafe operations are supported by UnsafeKeyring types. By doing so, we try to make the client developer aware of the risks. Co-authored-by: Sunny Aggarwal <sunnya97@protonmail.ch>
This commit is contained in:
co-authored by
Sunny Aggarwal
parent
6476b09b64
commit
513dabaec8
+49
-3
@@ -2,23 +2,45 @@ package keys
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/client/input"
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keyring"
|
||||
)
|
||||
|
||||
const (
|
||||
flagUnarmoredHex = "unarmored-hex"
|
||||
flagUnsafe = "unsafe"
|
||||
)
|
||||
|
||||
// ExportKeyCommand exports private keys from the key store.
|
||||
func ExportKeyCommand() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
cmd := &cobra.Command{
|
||||
Use: "export <name>",
|
||||
Short: "Export private keys",
|
||||
Long: `Export a private key from the local keybase in ASCII-armored encrypted format.`,
|
||||
Args: cobra.ExactArgs(1),
|
||||
Long: `Export a private key from the local keyring in ASCII-armored encrypted format.
|
||||
|
||||
When both the --unarmored-hex and --unsafe flags are selected, cryptographic
|
||||
private key material is exported in an INSECURE fashion that is designed to
|
||||
allow users to import their keys in hot wallets. This feature is for advanced
|
||||
users only that are confident about how to handle private keys work and are
|
||||
FULLY AWARE OF THE RISKS. If you are unsure, you may want to do some research
|
||||
and export your keys in ASCII-armored encrypted format.`,
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
buf := bufio.NewReader(cmd.InOrStdin())
|
||||
clientCtx := client.GetClientContextFromCmd(cmd)
|
||||
unarmored, _ := cmd.Flags().GetBool(flagUnarmoredHex)
|
||||
unsafe, _ := cmd.Flags().GetBool(flagUnsafe)
|
||||
|
||||
if unarmored && unsafe {
|
||||
return exportUnsafeUnarmored(cmd, args[0], buf, clientCtx.Keyring)
|
||||
} else if unarmored || unsafe {
|
||||
return fmt.Errorf("the flags %s and %s must be used together", flagUnsafe, flagUnarmoredHex)
|
||||
}
|
||||
|
||||
encryptPassword, err := input.GetPassword("Enter passphrase to encrypt the exported key:", buf)
|
||||
if err != nil {
|
||||
@@ -31,7 +53,31 @@ func ExportKeyCommand() *cobra.Command {
|
||||
}
|
||||
|
||||
cmd.Println(armored)
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Flags().Bool(flagUnarmoredHex, false, "Export unarmored hex privkey. Requires --unsafe.")
|
||||
cmd.Flags().Bool(flagUnsafe, false, "Enable unsafe operations. This flag must be switched on along with all unsafe operation-specific options.")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func exportUnsafeUnarmored(cmd *cobra.Command, uid string, buf *bufio.Reader, kr keyring.Keyring) error {
|
||||
// confirm deletion, unless -y is passed
|
||||
if yes, err := input.GetConfirmation("WARNING: The private key will be exported as an unarmored hexadecimal string. USE AT YOUR OWN RISK. Continue?", buf, cmd.ErrOrStderr()); err != nil {
|
||||
return err
|
||||
} else if !yes {
|
||||
return nil
|
||||
}
|
||||
|
||||
hexPrivKey, err := keyring.NewUnsafe(kr).UnsafeExportPrivKeyHex(uid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd.Println(hexPrivKey)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -36,15 +36,34 @@ func Test_runExportCmd(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
// Now enter password
|
||||
mockIn.Reset("123456789\n123456789\n")
|
||||
cmd.SetArgs([]string{
|
||||
args := []string{
|
||||
"keyname1",
|
||||
fmt.Sprintf("--%s=%s", flags.FlagHome, kbHome),
|
||||
fmt.Sprintf("--%s=%s", flags.FlagKeyringBackend, keyring.BackendTest),
|
||||
})
|
||||
}
|
||||
|
||||
mockIn.Reset("123456789\n123456789\n")
|
||||
cmd.SetArgs(args)
|
||||
|
||||
clientCtx := client.Context{}.WithKeyring(kb)
|
||||
ctx := context.WithValue(context.Background(), client.ClientContextKey, &clientCtx)
|
||||
|
||||
require.NoError(t, cmd.ExecuteContext(ctx))
|
||||
|
||||
argsUnsafeOnly := append(args, "--unsafe")
|
||||
cmd.SetArgs(argsUnsafeOnly)
|
||||
require.Error(t, cmd.ExecuteContext(ctx))
|
||||
|
||||
argsUnarmoredHexOnly := append(args, "--unarmored-hex")
|
||||
cmd.SetArgs(argsUnarmoredHexOnly)
|
||||
require.Error(t, cmd.ExecuteContext(ctx))
|
||||
|
||||
argsUnsafeUnarmoredHex := append(args, "--unsafe", "--unarmored-hex")
|
||||
cmd.SetArgs(argsUnsafeUnarmoredHex)
|
||||
require.Error(t, cmd.ExecuteContext(ctx))
|
||||
|
||||
mockIn, mockOut := testutil.ApplyMockIO(cmd)
|
||||
mockIn.Reset("y\n")
|
||||
require.NoError(t, cmd.ExecuteContext(ctx))
|
||||
require.Equal(t, "2485e33678db4175dc0ecef2d6e1fc493d4a0d7f7ce83324b6ed70afe77f3485\n", mockOut.String())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user