[#466] cli: support NEP-2 and NEP-6 key formats

NEP-2 is contains WIF encrypted with password.
NEP-6 is format used for neo-go wallets.

Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
This commit is contained in:
Evgenii Stratonikov 2021-04-21 15:27:32 +03:00 committed by Alex Vanin
parent e9b3488aab
commit 60d74bce67
7 changed files with 150 additions and 37 deletions

View file

@ -34,17 +34,17 @@ var accountingBalanceCmd = &cobra.Command{
ctx = context.Background() ctx = context.Background()
) )
cli, err := getSDKClient() key, err := getKey()
if err != nil {
return err
}
cli, err := getSDKClient(key)
if err != nil { if err != nil {
return err return err
} }
if balanceOwner == "" { if balanceOwner == "" {
key, err := getKey()
if err != nil {
return err
}
wallet, err := owner.NEO3WalletFromPublicKey(&key.PublicKey) wallet, err := owner.NEO3WalletFromPublicKey(&key.PublicKey)
if err != nil { if err != nil {
return err return err

View file

@ -73,17 +73,17 @@ var listContainersCmd = &cobra.Command{
ctx = context.Background() ctx = context.Background()
) )
cli, err := getSDKClient() key, err := getKey()
if err != nil {
return err
}
cli, err := getSDKClient(key)
if err != nil { if err != nil {
return err return err
} }
if containerOwner == "" { if containerOwner == "" {
key, err := getKey()
if err != nil {
return err
}
wallet, err := owner.NEO3WalletFromPublicKey(&key.PublicKey) wallet, err := owner.NEO3WalletFromPublicKey(&key.PublicKey)
if err != nil { if err != nil {
return err return err
@ -117,7 +117,12 @@ It will be stored in sidechain when inner ring will accepts it.`,
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background() ctx := context.Background()
cli, err := getSDKClient() key, err := getKey()
if err != nil {
return err
}
cli, err := getSDKClient(key)
if err != nil { if err != nil {
return err return err
} }
@ -183,7 +188,12 @@ Only owner of the container has a permission to remove container.`,
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background() ctx := context.Background()
cli, err := getSDKClient() key, err := getKey()
if err != nil {
return err
}
cli, err := getSDKClient(key)
if err != nil { if err != nil {
return err return err
} }
@ -227,7 +237,12 @@ var listContainerObjectsCmd = &cobra.Command{
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background() ctx := context.Background()
cli, err := getSDKClient() key, err := getKey()
if err != nil {
return err
}
cli, err := getSDKClient(key)
if err != nil { if err != nil {
return err return err
} }
@ -288,7 +303,12 @@ var getContainerInfoCmd = &cobra.Command{
return errors.Wrap(err, "can't unmarshal container") return errors.Wrap(err, "can't unmarshal container")
} }
} else { } else {
cli, err := getSDKClient() key, err := getKey()
if err != nil {
return err
}
cli, err := getSDKClient(key)
if err != nil { if err != nil {
return err return err
} }
@ -341,7 +361,12 @@ var getExtendedACLCmd = &cobra.Command{
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background() ctx := context.Background()
cli, err := getSDKClient() key, err := getKey()
if err != nil {
return err
}
cli, err := getSDKClient(key)
if err != nil { if err != nil {
return err return err
} }
@ -400,7 +425,12 @@ Container ID in EACL table will be substituted with ID from the CLI.`,
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background() ctx := context.Background()
cli, err := getSDKClient() key, err := getKey()
if err != nil {
return err
}
cli, err := getSDKClient(key)
if err != nil { if err != nil {
return err return err
} }

View file

@ -78,7 +78,7 @@ func healthCheck(cmd *cobra.Command, _ []string) error {
return err return err
} }
cli, err := getSDKClient() cli, err := getSDKClient(key)
if err != nil { if err != nil {
return err return err
} }
@ -130,7 +130,7 @@ func setNetmapStatus(cmd *cobra.Command, _ []string) error {
return err return err
} }
cli, err := getSDKClient() cli, err := getSDKClient(key)
if err != nil { if err != nil {
return err return err
} }
@ -196,7 +196,7 @@ var dropObjectsCmd = &cobra.Command{
return err return err
} }
cli, err := getSDKClient() cli, err := getSDKClient(key)
if err != nil { if err != nil {
return err return err
} }

View file

@ -51,7 +51,12 @@ var getEpochCmd = &cobra.Command{
Short: "Get current epoch number", Short: "Get current epoch number",
Long: "Get current epoch number", Long: "Get current epoch number",
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
cli, err := getSDKClient() key, err := getKey()
if err != nil {
return err
}
cli, err := getSDKClient(key)
if err != nil { if err != nil {
return err return err
} }
@ -72,7 +77,12 @@ var localNodeInfoCmd = &cobra.Command{
Short: "Get local node info", Short: "Get local node info",
Long: `Get local node info`, Long: `Get local node info`,
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
cli, err := getSDKClient() key, err := getKey()
if err != nil {
return err
}
cli, err := getSDKClient(key)
if err != nil { if err != nil {
return err return err
} }
@ -105,7 +115,7 @@ var snapshotCmd = &cobra.Command{
return err return err
} }
cli, err := getSDKClient() cli, err := getSDKClient(key)
if err != nil { if err != nil {
return err return err
} }
@ -134,7 +144,12 @@ var netInfoCmd = &cobra.Command{
Short: "Get information about NeoFS network", Short: "Get information about NeoFS network",
Long: "Get information about NeoFS network", Long: "Get information about NeoFS network",
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
cli, err := getSDKClient() key, err := getKey()
if err != nil {
return err
}
cli, err := getSDKClient(key)
if err != nil { if err != nil {
return err return err
} }

View file

@ -194,7 +194,11 @@ func init() {
} }
func initSession(ctx context.Context) (client.Client, *token.SessionToken, error) { func initSession(ctx context.Context) (client.Client, *token.SessionToken, error) {
cli, err := getSDKClient() key, err := getKey()
if err != nil {
return nil, nil, fmt.Errorf("can't fetch private key: %w", err)
}
cli, err := getSDKClient(key)
if err != nil { if err != nil {
return nil, nil, fmt.Errorf("can't create client: %w", err) return nil, nil, fmt.Errorf("can't create client: %w", err)
} }

View file

@ -10,6 +10,10 @@ import (
"strings" "strings"
"github.com/mitchellh/go-homedir" "github.com/mitchellh/go-homedir"
"github.com/nspcc-dev/neo-go/cli/flags"
"github.com/nspcc-dev/neo-go/cli/input"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/wallet"
"github.com/nspcc-dev/neofs-api-go/pkg" "github.com/nspcc-dev/neofs-api-go/pkg"
"github.com/nspcc-dev/neofs-api-go/pkg/client" "github.com/nspcc-dev/neofs-api-go/pkg/client"
"github.com/nspcc-dev/neofs-api-go/pkg/owner" "github.com/nspcc-dev/neofs-api-go/pkg/owner"
@ -53,6 +57,8 @@ var (
errInvalidKey = errors.New("provided key is incorrect") errInvalidKey = errors.New("provided key is incorrect")
errInvalidEndpoint = errors.New("provided RPC endpoint is incorrect") errInvalidEndpoint = errors.New("provided RPC endpoint is incorrect")
errCantGenerateKey = errors.New("can't generate new private key") errCantGenerateKey = errors.New("can't generate new private key")
errInvalidAddress = errors.New("--address option must be specified and valid")
errInvalidPassword = errors.New("invalid password for the encrypted key")
) )
// Execute adds all child commands to the root command and sets flags appropriately. // Execute adds all child commands to the root command and sets flags appropriately.
@ -75,9 +81,12 @@ func init() {
rootCmd.PersistentFlags().StringVarP(&cfgFile, "config", "c", "", "config file (default is $HOME/.config/neofs-cli/config.yaml)") rootCmd.PersistentFlags().StringVarP(&cfgFile, "config", "c", "", "config file (default is $HOME/.config/neofs-cli/config.yaml)")
rootCmd.PersistentFlags().StringP("key", "k", "", "private key in hex, WIF or filepath (use `--key new` to generate key for request)") rootCmd.PersistentFlags().StringP("key", "k", "", "private key in hex, WIF, NEP-2 or filepath (use `--key new` to generate key for request)")
_ = viper.BindPFlag("key", rootCmd.PersistentFlags().Lookup("key")) _ = viper.BindPFlag("key", rootCmd.PersistentFlags().Lookup("key"))
rootCmd.PersistentFlags().StringP("address", "", "", "address of wallet account")
_ = viper.BindPFlag("address", rootCmd.PersistentFlags().Lookup("address"))
rootCmd.PersistentFlags().StringP("rpc-endpoint", "r", "", "remote node address (as 'multiaddr' or '<host>:<port>')") rootCmd.PersistentFlags().StringP("rpc-endpoint", "r", "", "remote node address (as 'multiaddr' or '<host>:<port>')")
_ = viper.BindPFlag("rpc", rootCmd.PersistentFlags().Lookup("rpc-endpoint")) _ = viper.BindPFlag("rpc", rootCmd.PersistentFlags().Lookup("rpc-endpoint"))
@ -122,6 +131,8 @@ func initConfig() {
} }
} }
const nep2Base58Length = 58
// getKey returns private key that was provided in global arguments. // getKey returns private key that was provided in global arguments.
func getKey() (*ecdsa.PrivateKey, error) { func getKey() (*ecdsa.PrivateKey, error) {
privateKey := viper.GetString("key") privateKey := viper.GetString("key")
@ -139,11 +150,68 @@ func getKey() (*ecdsa.PrivateKey, error) {
} }
key, err := crypto.LoadPrivateKey(privateKey) key, err := crypto.LoadPrivateKey(privateKey)
if err != nil { if err == nil {
return nil, errInvalidKey return key, nil
} }
return key, nil w, err := wallet.NewWalletFromFile(privateKey)
if err == nil {
return getKeyFromWallet(w, viper.GetString("address"))
}
if len(privateKey) == nep2Base58Length {
return getKeyFromNEP2(privateKey)
}
return nil, errInvalidKey
}
func getKeyFromNEP2(encryptedWif string) (*ecdsa.PrivateKey, error) {
pass, err := input.ReadPassword("Enter password > ")
if err != nil {
printVerbose("Can't read password: %v", err)
return nil, errInvalidPassword
}
k, err := keys.NEP2Decrypt(encryptedWif, pass)
if err != nil {
printVerbose("Invalid key or password: %v", err)
return nil, errInvalidPassword
}
return &k.PrivateKey, nil
}
func getKeyFromWallet(w *wallet.Wallet, addrStr string) (*ecdsa.PrivateKey, error) {
if addrStr == "" {
printVerbose("Address is empty")
return nil, errInvalidAddress
}
addr, err := flags.ParseAddress(addrStr)
if err != nil {
printVerbose("Can't parse address: %s", addrStr)
return nil, errInvalidAddress
}
acc := w.GetAccount(addr)
if acc == nil {
printVerbose("Can't find wallet account for %s", addrStr)
return nil, errInvalidAddress
}
pass, err := input.ReadPassword("Enter password > ")
if err != nil {
printVerbose("Can't read password: %v", err)
return nil, errInvalidPassword
}
if err := acc.Decrypt(pass); err != nil {
printVerbose("Can't decrypt account: %v", err)
return nil, errInvalidPassword
}
return &acc.PrivateKey().PrivateKey, nil
} }
// getEndpointAddress returns network address structure that stores multiaddr // getEndpointAddress returns network address structure that stores multiaddr
@ -161,12 +229,7 @@ func getEndpointAddress() (*network.Address, error) {
// getSDKClient returns default neofs-api-go sdk client. Consider using // getSDKClient returns default neofs-api-go sdk client. Consider using
// opts... to provide TTL or other global configuration flags. // opts... to provide TTL or other global configuration flags.
func getSDKClient() (client.Client, error) { func getSDKClient(key *ecdsa.PrivateKey) (client.Client, error) {
key, err := getKey()
if err != nil {
return nil, err
}
netAddr, err := getEndpointAddress() netAddr, err := getEndpointAddress()
if err != nil { if err != nil {
return nil, err return nil, err
@ -177,7 +240,8 @@ func getSDKClient() (client.Client, error) {
return nil, errInvalidEndpoint return nil, errInvalidEndpoint
} }
return client.New(client.WithAddress(ipAddr), client.WithDefaultPrivateKey(key)) c, err := client.New(client.WithAddress(ipAddr), client.WithDefaultPrivateKey(key))
return c, err
} }
func getTTL() uint32 { func getTTL() uint32 {

BIN
go.sum

Binary file not shown.