forked from TrueCloudLab/frostfs-node
[#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:
parent
e9b3488aab
commit
60d74bce67
7 changed files with 152 additions and 37 deletions
|
@ -34,17 +34,17 @@ var accountingBalanceCmd = &cobra.Command{
|
|||
ctx = context.Background()
|
||||
)
|
||||
|
||||
cli, err := getSDKClient()
|
||||
key, err := getKey()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cli, err := getSDKClient(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if balanceOwner == "" {
|
||||
key, err := getKey()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
wallet, err := owner.NEO3WalletFromPublicKey(&key.PublicKey)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
@ -73,17 +73,17 @@ var listContainersCmd = &cobra.Command{
|
|||
ctx = context.Background()
|
||||
)
|
||||
|
||||
cli, err := getSDKClient()
|
||||
key, err := getKey()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cli, err := getSDKClient(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if containerOwner == "" {
|
||||
key, err := getKey()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
wallet, err := owner.NEO3WalletFromPublicKey(&key.PublicKey)
|
||||
if err != nil {
|
||||
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 {
|
||||
ctx := context.Background()
|
||||
|
||||
cli, err := getSDKClient()
|
||||
key, err := getKey()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cli, err := getSDKClient(key)
|
||||
if err != nil {
|
||||
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 {
|
||||
ctx := context.Background()
|
||||
|
||||
cli, err := getSDKClient()
|
||||
key, err := getKey()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cli, err := getSDKClient(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -227,7 +237,12 @@ var listContainerObjectsCmd = &cobra.Command{
|
|||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
ctx := context.Background()
|
||||
|
||||
cli, err := getSDKClient()
|
||||
key, err := getKey()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cli, err := getSDKClient(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -288,7 +303,12 @@ var getContainerInfoCmd = &cobra.Command{
|
|||
return errors.Wrap(err, "can't unmarshal container")
|
||||
}
|
||||
} else {
|
||||
cli, err := getSDKClient()
|
||||
key, err := getKey()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cli, err := getSDKClient(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -341,7 +361,12 @@ var getExtendedACLCmd = &cobra.Command{
|
|||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
ctx := context.Background()
|
||||
|
||||
cli, err := getSDKClient()
|
||||
key, err := getKey()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cli, err := getSDKClient(key)
|
||||
if err != nil {
|
||||
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 {
|
||||
ctx := context.Background()
|
||||
|
||||
cli, err := getSDKClient()
|
||||
key, err := getKey()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cli, err := getSDKClient(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -78,7 +78,7 @@ func healthCheck(cmd *cobra.Command, _ []string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
cli, err := getSDKClient()
|
||||
cli, err := getSDKClient(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -130,7 +130,7 @@ func setNetmapStatus(cmd *cobra.Command, _ []string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
cli, err := getSDKClient()
|
||||
cli, err := getSDKClient(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -196,7 +196,7 @@ var dropObjectsCmd = &cobra.Command{
|
|||
return err
|
||||
}
|
||||
|
||||
cli, err := getSDKClient()
|
||||
cli, err := getSDKClient(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -51,7 +51,12 @@ var getEpochCmd = &cobra.Command{
|
|||
Short: "Get current epoch number",
|
||||
Long: "Get current epoch number",
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
|
@ -72,7 +77,12 @@ var localNodeInfoCmd = &cobra.Command{
|
|||
Short: "Get local node info",
|
||||
Long: `Get local node info`,
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
|
@ -105,7 +115,7 @@ var snapshotCmd = &cobra.Command{
|
|||
return err
|
||||
}
|
||||
|
||||
cli, err := getSDKClient()
|
||||
cli, err := getSDKClient(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -134,7 +144,12 @@ var netInfoCmd = &cobra.Command{
|
|||
Short: "Get information about NeoFS network",
|
||||
Long: "Get information about NeoFS network",
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -194,7 +194,11 @@ func init() {
|
|||
}
|
||||
|
||||
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 {
|
||||
return nil, nil, fmt.Errorf("can't create client: %w", err)
|
||||
}
|
||||
|
|
|
@ -10,6 +10,10 @@ import (
|
|||
"strings"
|
||||
|
||||
"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/client"
|
||||
"github.com/nspcc-dev/neofs-api-go/pkg/owner"
|
||||
|
@ -53,6 +57,8 @@ var (
|
|||
errInvalidKey = errors.New("provided key is incorrect")
|
||||
errInvalidEndpoint = errors.New("provided RPC endpoint is incorrect")
|
||||
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.
|
||||
|
@ -75,9 +81,12 @@ func init() {
|
|||
|
||||
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"))
|
||||
|
||||
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>')")
|
||||
_ = 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.
|
||||
func getKey() (*ecdsa.PrivateKey, error) {
|
||||
privateKey := viper.GetString("key")
|
||||
|
@ -139,11 +150,68 @@ func getKey() (*ecdsa.PrivateKey, error) {
|
|||
}
|
||||
|
||||
key, err := crypto.LoadPrivateKey(privateKey)
|
||||
if err != nil {
|
||||
return nil, errInvalidKey
|
||||
if err == nil {
|
||||
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
|
||||
|
@ -161,12 +229,7 @@ func getEndpointAddress() (*network.Address, error) {
|
|||
|
||||
// getSDKClient returns default neofs-api-go sdk client. Consider using
|
||||
// opts... to provide TTL or other global configuration flags.
|
||||
func getSDKClient() (client.Client, error) {
|
||||
key, err := getKey()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func getSDKClient(key *ecdsa.PrivateKey) (client.Client, error) {
|
||||
netAddr, err := getEndpointAddress()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -177,7 +240,8 @@ func getSDKClient() (client.Client, error) {
|
|||
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 {
|
||||
|
|
2
go.sum
2
go.sum
|
@ -383,6 +383,7 @@ github.com/syndtr/goleveldb v0.0.0-20180307113352-169b1b37be73/go.mod h1:Z4AUp2K
|
|||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
|
||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||
github.com/urfave/cli v1.20.0 h1:fDqGv3UG/4jbVl/QkFwEdddtEDjh/5Ov6X+0B/3bPaw=
|
||||
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||
|
@ -499,6 +500,7 @@ golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf h1:MZ2shdL+ZM/XzY3ZGOnh4Nlpnxz5GSOhOmtHo3iPU6M=
|
||||
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
|
|
Loading…
Reference in a new issue