From e85b12d145b8331211947ace05b87d5b865862dd Mon Sep 17 00:00:00 2001 From: Julian Toledano Date: Thu, 17 Aug 2023 11:23:26 +0200 Subject: [PATCH 1/9] add: import hex keys --- client/keys/import.go | 16 +++++++++ client/keys/import_test.go | 56 +++++++++++++++++++++++++++++++ client/keys/root.go | 1 + crypto/keyring/keyring.go | 28 +++++++++++++++- crypto/keyring/keyring_test.go | 60 +++++++++++++++++++++++++++++++++- 5 files changed, 159 insertions(+), 2 deletions(-) diff --git a/client/keys/import.go b/client/keys/import.go index a9d5c185acb7..279229534032 100644 --- a/client/keys/import.go +++ b/client/keys/import.go @@ -38,3 +38,19 @@ func ImportKeyCommand() *cobra.Command { }, } } + +func ImportKeyHexCommand() *cobra.Command { + return &cobra.Command{ + Use: "import-hex ", + Short: "Import private keys into the local keybase", + Long: "Import hex encoded private key into the local keybase.", + Args: cobra.ExactArgs(3), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + return clientCtx.Keyring.ImportPrivKeyHex(args[0], args[1], args[2]) + }, + } +} diff --git a/client/keys/import_test.go b/client/keys/import_test.go index a64d0ee898f1..f40878cffc7a 100644 --- a/client/keys/import_test.go +++ b/client/keys/import_test.go @@ -115,3 +115,59 @@ HbP+c6JmeJy9JXe2rbbF1QtCX1gLqGcDQPBXiCtFvP7/8wTZtVOPj8vREzhZ9ElO }) } } + +func Test_runImportHexCmd(t *testing.T) { + cdc := moduletestutil.MakeTestEncodingConfig().Codec + testCases := []struct { + name string + keyringBackend string + hexKey string + algo string + expectError bool + }{ + { + name: "test backend success", + keyringBackend: keyring.BackendTest, + hexKey: "0xa3e57952e835ed30eea86a2993ac2a61c03e74f2085b3635bd94aa4d7ae0cfdf", + algo: "secp256k1", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + cmd := ImportKeyHexCommand() + cmd.Flags().AddFlagSet(Commands().PersistentFlags()) + mockIn := testutil.ApplyMockIODiscardOutErr(cmd) + + // Now add a temporary keybase + kbHome := t.TempDir() + kb, err := keyring.New(sdk.KeyringServiceName(), tc.keyringBackend, kbHome, nil, cdc) + require.NoError(t, err) + + clientCtx := client.Context{}. + WithKeyringDir(kbHome). + WithKeyring(kb). + WithInput(mockIn). + WithCodec(cdc) + ctx := context.WithValue(context.Background(), client.ClientContextKey, &clientCtx) + + t.Cleanup(cleanupKeys(t, kb, "keyname1")) + + defer func() { + _ = os.RemoveAll(kbHome) + }() + + cmd.SetArgs([]string{ + "keyname1", tc.hexKey, tc.algo, + fmt.Sprintf("--%s=%s", flags.FlagKeyringBackend, tc.keyringBackend), + }) + + err = cmd.ExecuteContext(ctx) + if tc.expectError { + require.Error(t, err) + } else { + require.NoError(t, err) + } + }) + } +} diff --git a/client/keys/root.go b/client/keys/root.go index 4cadecbc1815..9dee7bd48e3a 100644 --- a/client/keys/root.go +++ b/client/keys/root.go @@ -41,6 +41,7 @@ The pass backend requires GnuPG: https://gnupg.org/ AddKeyCommand(), ExportKeyCommand(), ImportKeyCommand(), + ImportKeyHexCommand(), ListKeysCmd(), ListKeyTypesCmd(), ShowKeysCmd(), diff --git a/crypto/keyring/keyring.go b/crypto/keyring/keyring.go index 635b42d43670..a442bdafa810 100644 --- a/crypto/keyring/keyring.go +++ b/crypto/keyring/keyring.go @@ -45,6 +45,8 @@ const ( // temporary pass phrase for exporting a key during a key rename passPhrase = "temp" + // prefix for exported hex private keys + hexPrefix = "0x" ) var ( @@ -115,7 +117,8 @@ type Signer interface { type Importer interface { // ImportPrivKey imports ASCII armored passphrase-encrypted private keys. ImportPrivKey(uid, armor, passphrase string) error - + // ImportPrivKeyHex imports hex encoded keys. + ImportPrivKeyHex(uid, privKey, algoStr string) error // ImportPubKey imports ASCII armored public keys. ImportPubKey(uid, armor string) error } @@ -335,6 +338,29 @@ func (ks keystore) ImportPrivKey(uid, armor, passphrase string) error { return nil } +func (ks keystore) ImportPrivKeyHex(uid, privKey, algoStr string) error { + if _, err := ks.Key(uid); err == nil { + return errorsmod.Wrap(ErrOverwriteKey, uid) + } + if privKey[:2] == hexPrefix { + privKey = privKey[2:] + } + decodedPriv, err := hex.DecodeString(privKey) + if err != nil { + return err + } + algo, err := NewSigningAlgoFromString(algoStr, ks.options.SupportedAlgos) + if err != nil { + return err + } + priv := algo.Generate()(decodedPriv) + _, err = ks.writeLocalKey(uid, priv) + if err != nil { + return err + } + return nil +} + func (ks keystore) ImportPubKey(uid, armor string) error { if _, err := ks.Key(uid); err == nil { return errorsmod.Wrap(ErrOverwriteKey, uid) diff --git a/crypto/keyring/keyring_test.go b/crypto/keyring/keyring_test.go index 9031fa4d06c0..b6827618bc4b 100644 --- a/crypto/keyring/keyring_test.go +++ b/crypto/keyring/keyring_test.go @@ -564,7 +564,65 @@ func TestImportPrivKey(t *testing.T) { } } -func TestExportImportPrivKey(t *testing.T) { +func TestImportPrivKeyHex(t *testing.T) { + cdc := getCodec() + tests := []struct { + name string + uid string + backend string + hexKey string + algo string + expectedErr error + }{ + { + name: "correct import", + uid: "hexImport", + backend: BackendTest, + hexKey: "0xa3e57952e835ed30eea86a2993ac2a61c03e74f2085b3635bd94aa4d7ae0cfdf", + algo: "secp256k1", + expectedErr: nil, + }, + { + name: "correct import without prefix", + uid: "hexImport", + backend: BackendTest, + hexKey: "a3e57952e835ed30eea86a2993ac2a61c03e74f2085b3635bd94aa4d7ae0cfdf", + algo: "secp256k1", + expectedErr: nil, + }, + { + name: "wrong hex length", + uid: "hexImport", + backend: BackendTest, + hexKey: "0xae57952e835ed30eea86a2993ac2a61c03e74f2085b3635bd94aa4d7ae0cfdf", + algo: "secp256k1", + expectedErr: hex.ErrLength, + }, + { + name: "unsupported algo", + uid: "hexImport", + backend: BackendTest, + hexKey: "0xa3e57952e835ed30eea86a2993ac2a61c03e74f2085b3635bd94aa4d7ae0cfdf", + algo: "notSupportedAlgo", + expectedErr: ErrUnsupportedSigningAlgo, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + kb, err := New("TestExport", tt.backend, t.TempDir(), nil, cdc) + require.NoError(t, err) + err = kb.ImportPrivKeyHex(tt.uid, tt.hexKey, tt.algo) + if tt.expectedErr == nil { + require.NoError(t, err) + } else { + require.Error(t, err) + require.True(t, errors.Is(err, tt.expectedErr)) + } + }) + } +} + +func TestExportImportPrivKeyArmor(t *testing.T) { cdc := getCodec() tests := []struct { name string From 7e23bcb875d097b7994d2abc6a79d1dca04641ab Mon Sep 17 00:00:00 2001 From: Julian Toledano Date: Thu, 17 Aug 2023 11:29:55 +0200 Subject: [PATCH 2/9] changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 313e38c0c1de..de8054492021 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,6 +40,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### Features +* (keyring) [#17424](https://github.com/cosmos/cosmos-sdk/pull/17424) Allows to import private keys encoded in hex. * (x/bank) [#16795](https://github.com/cosmos/cosmos-sdk/pull/16852) Add `DenomMetadataByQueryString` query in bank module to support metadata query by query string. * (baseapp) [#16239](https://github.com/cosmos/cosmos-sdk/pull/16239) Add Gas Limits to allow node operators to resource bound queries. From e5b1d2e135250d04b3f01436a4f7757b794cc2aa Mon Sep 17 00:00:00 2001 From: Julian Toledano Date: Thu, 17 Aug 2023 11:58:28 +0200 Subject: [PATCH 3/9] add: improve long message --- client/keys/import.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/keys/import.go b/client/keys/import.go index 279229534032..325bcdb0ad71 100644 --- a/client/keys/import.go +++ b/client/keys/import.go @@ -43,7 +43,7 @@ func ImportKeyHexCommand() *cobra.Command { return &cobra.Command{ Use: "import-hex ", Short: "Import private keys into the local keybase", - Long: "Import hex encoded private key into the local keybase.", + Long: "Import hex encoded private key into the local keybase.\nSupported key-types can be obtained with:\n list-key-types", Args: cobra.ExactArgs(3), RunE: func(cmd *cobra.Command, args []string) error { clientCtx, err := client.GetClientQueryContext(cmd) From cac5afd8ad76b7d67acf98943eb9bfd2d09512d1 Mon Sep 17 00:00:00 2001 From: Julian Toledano Date: Thu, 17 Aug 2023 12:29:22 +0200 Subject: [PATCH 4/9] fix: root test --- client/keys/root_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/keys/root_test.go b/client/keys/root_test.go index bb204a474994..85009c221d06 100644 --- a/client/keys/root_test.go +++ b/client/keys/root_test.go @@ -11,5 +11,5 @@ func TestCommands(t *testing.T) { assert.Assert(t, rootCommands != nil) // Commands are registered - assert.Equal(t, 11, len(rootCommands.Commands())) + assert.Equal(t, 12, len(rootCommands.Commands())) } From 325a77d4ccbad192203f30e1a5f457248fe5fd6d Mon Sep 17 00:00:00 2001 From: Julian Toledano Date: Thu, 17 Aug 2023 13:26:45 +0200 Subject: [PATCH 5/9] update: key-type as flag --- client/keys/import.go | 13 +++++++++---- client/keys/import_test.go | 7 ++++--- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/client/keys/import.go b/client/keys/import.go index 325bcdb0ad71..15be4d8ad984 100644 --- a/client/keys/import.go +++ b/client/keys/import.go @@ -2,6 +2,8 @@ package keys import ( "bufio" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/crypto/hd" "os" "github.com/spf13/cobra" @@ -40,17 +42,20 @@ func ImportKeyCommand() *cobra.Command { } func ImportKeyHexCommand() *cobra.Command { - return &cobra.Command{ - Use: "import-hex ", + cmd := &cobra.Command{ + Use: "import-hex ", Short: "Import private keys into the local keybase", Long: "Import hex encoded private key into the local keybase.\nSupported key-types can be obtained with:\n list-key-types", - Args: cobra.ExactArgs(3), + Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { clientCtx, err := client.GetClientQueryContext(cmd) if err != nil { return err } - return clientCtx.Keyring.ImportPrivKeyHex(args[0], args[1], args[2]) + keyType, _ := cmd.Flags().GetString(flags.FlagKeyType) + return clientCtx.Keyring.ImportPrivKeyHex(args[0], args[1], keyType) }, } + cmd.Flags().String(flags.FlagKeyType, string(hd.Secp256k1Type), "private key signing algorithm kind") + return cmd } diff --git a/client/keys/import_test.go b/client/keys/import_test.go index f40878cffc7a..c6b8ffacf851 100644 --- a/client/keys/import_test.go +++ b/client/keys/import_test.go @@ -122,14 +122,14 @@ func Test_runImportHexCmd(t *testing.T) { name string keyringBackend string hexKey string - algo string + keyType string expectError bool }{ { name: "test backend success", keyringBackend: keyring.BackendTest, hexKey: "0xa3e57952e835ed30eea86a2993ac2a61c03e74f2085b3635bd94aa4d7ae0cfdf", - algo: "secp256k1", + keyType: "secp256k1", }, } @@ -158,7 +158,8 @@ func Test_runImportHexCmd(t *testing.T) { }() cmd.SetArgs([]string{ - "keyname1", tc.hexKey, tc.algo, + "keyname1", tc.hexKey, + fmt.Sprintf("--%s=%s", flags.FlagKeyType, tc.keyType), fmt.Sprintf("--%s=%s", flags.FlagKeyringBackend, tc.keyringBackend), }) From 22d1a07786751afb10b23405282835399ad40399 Mon Sep 17 00:00:00 2001 From: Julian Toledano Date: Thu, 17 Aug 2023 14:10:07 +0200 Subject: [PATCH 6/9] fix: lint --- client/keys/import.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/keys/import.go b/client/keys/import.go index 15be4d8ad984..e5a590fff8c5 100644 --- a/client/keys/import.go +++ b/client/keys/import.go @@ -2,14 +2,14 @@ package keys import ( "bufio" - "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/crypto/hd" "os" "github.com/spf13/cobra" "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/client/input" + "github.com/cosmos/cosmos-sdk/crypto/hd" ) // ImportKeyCommand imports private keys from a keyfile. From 7ec30fafa4ea5912337c03fc437dc1f0eef839a2 Mon Sep 17 00:00:00 2001 From: Julian Toledano Date: Thu, 17 Aug 2023 16:29:21 +0200 Subject: [PATCH 7/9] fix: appName --- client/keys/import.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/client/keys/import.go b/client/keys/import.go index e5a590fff8c5..c0cbc26e9039 100644 --- a/client/keys/import.go +++ b/client/keys/import.go @@ -2,6 +2,8 @@ package keys import ( "bufio" + "fmt" + "github.com/cosmos/cosmos-sdk/version" "os" "github.com/spf13/cobra" @@ -45,7 +47,7 @@ func ImportKeyHexCommand() *cobra.Command { cmd := &cobra.Command{ Use: "import-hex ", Short: "Import private keys into the local keybase", - Long: "Import hex encoded private key into the local keybase.\nSupported key-types can be obtained with:\n list-key-types", + Long: fmt.Sprintf("Import hex encoded private key into the local keybase.\nSupported key-types can be obtained with:\n%s list-key-types", version.AppName), Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { clientCtx, err := client.GetClientQueryContext(cmd) From fe442f2d84afbc934d8c595eeffa33770a258c86 Mon Sep 17 00:00:00 2001 From: Julian Toledano Date: Thu, 17 Aug 2023 16:32:42 +0200 Subject: [PATCH 8/9] lint --- client/keys/import.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client/keys/import.go b/client/keys/import.go index c0cbc26e9039..28303a16801a 100644 --- a/client/keys/import.go +++ b/client/keys/import.go @@ -3,15 +3,16 @@ package keys import ( "bufio" "fmt" - "github.com/cosmos/cosmos-sdk/version" "os" + "github.com/spf13/cobra" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/client/input" "github.com/cosmos/cosmos-sdk/crypto/hd" + "github.com/cosmos/cosmos-sdk/version" ) // ImportKeyCommand imports private keys from a keyfile. From cfb353282838f9cf1f55103482f3bf0c9c255418 Mon Sep 17 00:00:00 2001 From: Julian Toledano Date: Thu, 17 Aug 2023 17:17:48 +0200 Subject: [PATCH 9/9] fix: lint --- client/keys/import.go | 1 - 1 file changed, 1 deletion(-) diff --git a/client/keys/import.go b/client/keys/import.go index 28303a16801a..98ccb6547ff0 100644 --- a/client/keys/import.go +++ b/client/keys/import.go @@ -5,7 +5,6 @@ import ( "fmt" "os" - "github.com/spf13/cobra" "github.com/cosmos/cosmos-sdk/client"