Skip to content

Commit

Permalink
Suport for input file & stdin propmt, impr. tolerance for key file co…
Browse files Browse the repository at this point in the history
…ntent
  • Loading branch information
pbukva committed Oct 31, 2024
1 parent feb3133 commit 7f27af0
Show file tree
Hide file tree
Showing 3 changed files with 119 additions and 22 deletions.
117 changes: 109 additions & 8 deletions client/keys/import.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@ package keys

import (
"bufio"
"encoding/hex"
"fmt"
"github.com/cosmos/cosmos-sdk/crypto/hd"
"github.com/cosmos/cosmos-sdk/crypto/keyring"
"os"
"strings"

"github.com/spf13/cobra"

Expand All @@ -25,30 +28,128 @@ func ImportKeyCommand() *cobra.Command {
return err
}
buf := bufio.NewReader(clientCtx.Input)
passphrase, err := input.GetPassword("Enter passphrase to decrypt your key:", buf)
if err != nil {
return err
}

bz, err := os.ReadFile(args[1])
if err != nil {
return err
}

unarmored, _ := cmd.Flags().GetBool(flagUnarmoredHex)
return clientCtx.Keyring.ImportPrivKey(args[0], string(bz), passphrase)
},
}

return cmd
}

// ImportUnarmoredKeyCommand imports private keys from a keyfile.
func ImportUnarmoredKeyCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "import-unarmored <name> [keyfile]",
Short: "Import unarmored private key into the local keybase",
Long: `Import hex encoded unarmored private key into the local keybase
Key must be hex encoded, and can be passed in either via file, or via
user password prompt.
If the 2nd positional argument [keyfile] has been provided, private key
will be read from that file. The keyfile must contain hex encoded
unarmored raw private key on the very 1st line, and that line must
contain only the private key.
Otherwise, if the [keyfile] is not provided, the private key will be
requested via password prompt where it will be read from stdin.
At the moment, only the secp256k1 curve/algo is supported.`,
Args: cobra.RangeArgs(1, 2),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientQueryContext(cmd)
if err != nil {
return err
}

buf := bufio.NewReader(clientCtx.Input)
var privKeyHex string
if len(args) == 1 {
privKeyHex, err = input.GetPassword("Enter hex encoded private key:", buf)
if err != nil {
return err
}
} else {
filename := args[1]
f, err := os.OpenFile(filename, os.O_RDONLY, os.ModePerm)
if err != nil {
return fmt.Errorf("open file \"%s\" error: %w", filename, err)
}
defer f.Close()

sc := bufio.NewScanner(f)
if sc.Scan() {
firstLine := sc.Text()
privKeyHex = strings.TrimSpace(firstLine)
} else {
return fmt.Errorf("unable to read 1st line from the \"%s\" file", filename)
}

if err := sc.Err(); err != nil {
return fmt.Errorf("error while scanning the \"%s\" file: %w", filename, err)
}
}

algo, _ := cmd.Flags().GetString(flagUnarmoredKeyAlgo)

if unarmored {
return clientCtx.Keyring.ImportUnarmoredPrivKey(args[0], string(bz), algo)
privKeyHexLC := strings.ToLower(privKeyHex)
if strings.HasPrefix(privKeyHexLC, "0x") {
privKeyHexLC = privKeyHexLC[2:]
} else if strings.HasPrefix(privKeyHexLC, "x") {
privKeyHexLC = privKeyHexLC[1:]
}

passphrase, err := input.GetPassword("Enter passphrase to decrypt your key:", buf)
privKeyRaw, err := hex.DecodeString(privKeyHexLC)
if err != nil {
return err
return fmt.Errorf("failed to decode provided hex value of private key: %w", err)
}

return clientCtx.Keyring.ImportPrivKey(args[0], string(bz), passphrase)
info, err := clientCtx.Keyring.ImportUnarmoredPrivKey(args[0], privKeyRaw, algo)
if err != nil {
return fmt.Errorf("importing unarmored private key: %w", err)

}

if err := printCreateUnarmored(cmd, info, clientCtx.OutputFormat); err != nil {
return fmt.Errorf("printing private key info: %w", err)
}

return nil
},
}

cmd.Flags().Bool(flagUnarmoredHex, false, "Import unarmored hex privkey")
cmd.Flags().String(flagUnarmoredKeyAlgo, string(hd.Secp256k1Type), fmt.Sprintf("defines cryptographic scheme algorithm of the private key (%s, %s, %s, %s)", hd.Secp256k1Type, hd.Ed25519Type, hd.Sr25519Type, hd.MultiType))
cmd.Flags().String(flagUnarmoredKeyAlgo, string(hd.Secp256k1Type), fmt.Sprintf("defines cryptographic scheme algorithm of the private key (\"%s\", \"%s\"). At the moent *ONLY* the \"%s\" is supported. Defaults to \"%s\".", hd.Secp256k1Type, hd.Ed25519Type, hd.Secp256k1Type, hd.Secp256k1Type))

return cmd
}

func printCreateUnarmored(cmd *cobra.Command, info keyring.Info, outputFormat string) error {
switch outputFormat {
case OutputFormatText:
cmd.PrintErrln()
printKeyInfo(cmd.OutOrStdout(), info, keyring.MkAccKeyOutput, outputFormat)
case OutputFormatJSON:
out, err := keyring.MkAccKeyOutput(info)
if err != nil {
return err
}

jsonString, err := KeysCdc.MarshalJSON(out)
if err != nil {
return err
}

cmd.Println(string(jsonString))

default:
return fmt.Errorf("invalid output format %s", outputFormat)
}

return nil
}
1 change: 1 addition & 0 deletions client/keys/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ The pass backend requires GnuPG: https://gnupg.org/
AddKeyCommand(),
ExportKeyCommand(),
ImportKeyCommand(),
ImportUnarmoredKeyCommand(),
ListKeysCmd(),
ShowKeysCmd(),
DeleteKeyCommand(),
Expand Down
23 changes: 9 additions & 14 deletions crypto/keyring/keyring.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,8 @@ type Importer interface {
// ImportPrivKey imports ASCII armored passphrase-encrypted private keys.
ImportPrivKey(uid, armor, passphrase string) error

// ImportUnarmoredPrivKey imports HEX encoded UNARMORED private keys.
ImportUnarmoredPrivKey(uid, unarmoredPrivKeyHex, algo string) error
// ImportUnarmoredPrivKey imports UNARMORED private key.
ImportUnarmoredPrivKey(uid string, unarmoredPrivKeyRaw []byte, algo string) (Info, error)

// ImportPubKey imports ASCII armored public keys.
ImportPubKey(uid string, armor string) error
Expand Down Expand Up @@ -308,41 +308,36 @@ func (ks keystore) ImportPrivKey(uid, armor, passphrase string) error {
return nil
}

func (ks keystore) ImportUnarmoredPrivKey(uid, unarmoredPrivKeyHex, algo string) error {
func (ks keystore) ImportUnarmoredPrivKey(uid string, unarmoredPrivKeyRaw []byte, algo string) (Info, error) {
if _, err := ks.Key(uid); err == nil {
return fmt.Errorf("cannot overwrite key: %s", uid)
}

privKeyRaw, err := hex.DecodeString(unarmoredPrivKeyHex)
if err != nil {
return errors.Wrap(err, "failed to decode provided hex value of private key")
return nil, fmt.Errorf("cannot overwrite key: %s", uid)
}

var privKey types.PrivKey
switch hd.PubKeyType(algo) {
case hd.Secp256k1Type:
privKey = &secp256k1.PrivKey{Key: privKeyRaw}
privKey = &secp256k1.PrivKey{Key: unarmoredPrivKeyRaw}
case hd.Ed25519Type:
fallthrough
case hd.Sr25519Type:
fallthrough
case hd.MultiType:
fallthrough
default:
return fmt.Errorf("only the \"%s\" algo is supported at the moment", hd.Secp256k1Type)
return nil, fmt.Errorf("only the \"%s\" algo is supported at the moment", hd.Secp256k1Type)
}

//privKey, err := legacy.PrivKeyFromBytes(privKeyRaw)
//if err != nil {
// return errors.Wrap(err, "failed to create private key from provided hex value")
//}

_, err = ks.writeLocalKey(uid, privKey, hd.PubKeyType(algo))
info, err := ks.writeLocalKey(uid, privKey, hd.PubKeyType(algo))
if err != nil {
return err
return nil, err
}

return nil
return info, nil
}

func (ks keystore) ImportPubKey(uid string, armor string) error {
Expand Down

0 comments on commit 7f27af0

Please sign in to comment.