From 82a85c18086fba23a09aa7ccfb3bd50c28e51c19 Mon Sep 17 00:00:00 2001 From: brad Date: Tue, 16 May 2023 12:53:22 -0400 Subject: [PATCH 1/3] [FIX] Allow mnemonic recovery via file The only way to `recover` was to specify the mnemonic as an input via STDIN. This carries a number of risks, as that mnemonic could be exposed via process monitoring, shell history, or over the shoulder observation. I believe allowing the user to specify a file to recover from is a generally more secure path to recovery from a mnemonic. --- rocketpool/api/wallet/commands.go | 39 +++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/rocketpool/api/wallet/commands.go b/rocketpool/api/wallet/commands.go index f6e905221..920b3780e 100644 --- a/rocketpool/api/wallet/commands.go +++ b/rocketpool/api/wallet/commands.go @@ -1,6 +1,10 @@ package wallet import ( + "fmt" + "os" + "strings" + "github.com/urfave/cli" "github.com/rocket-pool/smartnode/shared/utils/api" @@ -86,7 +90,7 @@ func RegisterSubcommands(command *cli.Command, name string, aliases []string) { Name: "recover", Aliases: []string{"r"}, Usage: "Recover a node wallet from a mnemonic phrase", - UsageText: "rocketpool api wallet recover mnemonic", + UsageText: "rocketpool api wallet recover", Flags: []cli.Flag{ cli.BoolFlag{ Name: "skip-validator-key-recovery, k", @@ -96,6 +100,14 @@ func RegisterSubcommands(command *cli.Command, name string, aliases []string) { Name: "derivation-path, d", Usage: "Specify the derivation path for the wallet.\nOmit this flag (or leave it blank) for the default of \"m/44'/60'/0'/0/%d\" (where %d is the index).\nSet this to \"ledgerLive\" to use Ledger Live's path of \"m/44'/60'/%d/0/0\".\nSet this to \"mew\" to use MyEtherWallet's path of \"m/44'/60'/0'/%d\".\nFor custom paths, simply enter them here.", }, + cli.StringFlag{ + Name: "mnemonic-file, f", + Usage: "Specify the path to the mnemonic.\nOmit this flag to enter the mnemonic via plain text.", + }, + cli.StringFlag{ + Name: "mnemonic, m", + Usage: "Specify the full mnemonic. Note - this is not secure and is recommend for testing purposes only.", + }, cli.UintFlag{ Name: "wallet-index, i", Usage: "Specify the index to use with the derivation path when recovering your wallet", @@ -104,11 +116,28 @@ func RegisterSubcommands(command *cli.Command, name string, aliases []string) { }, Action: func(c *cli.Context) error { - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err + // Validate input + if c.String("mnemonic-file") == "" && c.String("mnemonic") == "" { + return fmt.Errorf("Please specify a mnemonic file or mnemonic phrase") } - mnemonic, err := cliutils.ValidateWalletMnemonic("mnemonic", c.Args().Get(0)) + + // Read mnemonic from file + var providedMnemonic string + if c.String("mnemonic-file") != "" { + bytes, err := os.ReadFile(c.String("mnemonic-file")) + if err != nil { + return err + } + providedMnemonic = strings.TrimSpace(string(bytes)) + } + + // Read mnemonic from stdin + if c.String("mnemonic") != "" { + providedMnemonic = c.String("mnemonic") + } + + // Validate mnemonic + mnemonic, err := cliutils.ValidateWalletMnemonic("mnemonic", providedMnemonic) if err != nil { return err } From 52e52dce425c2261471a7fa284b7330bad21e8a2 Mon Sep 17 00:00:00 2001 From: brad Date: Wed, 17 May 2023 09:45:11 -0400 Subject: [PATCH 2/3] Tighten validation --- rocketpool/api/wallet/commands.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/rocketpool/api/wallet/commands.go b/rocketpool/api/wallet/commands.go index 920b3780e..d67c6d4bf 100644 --- a/rocketpool/api/wallet/commands.go +++ b/rocketpool/api/wallet/commands.go @@ -117,8 +117,9 @@ func RegisterSubcommands(command *cli.Command, name string, aliases []string) { Action: func(c *cli.Context) error { // Validate input - if c.String("mnemonic-file") == "" && c.String("mnemonic") == "" { - return fmt.Errorf("Please specify a mnemonic file or mnemonic phrase") + // Must supply either --mnemonic-file or --mnemonic, but not both + if (c.String("mnemonic-file") == "") == (c.String("mnemonic") == "") { + return fmt.Errorf("Please specify a mnemonic file or mnemonic phrase, but not both") } // Read mnemonic from file From e89a0ff39dc2b52cde729b1f95358bcb74f3c797 Mon Sep 17 00:00:00 2001 From: brad Date: Wed, 17 May 2023 16:38:01 -0400 Subject: [PATCH 3/3] Make this backwards compatible --- rocketpool/api/wallet/commands.go | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/rocketpool/api/wallet/commands.go b/rocketpool/api/wallet/commands.go index d67c6d4bf..56c9937c4 100644 --- a/rocketpool/api/wallet/commands.go +++ b/rocketpool/api/wallet/commands.go @@ -90,7 +90,7 @@ func RegisterSubcommands(command *cli.Command, name string, aliases []string) { Name: "recover", Aliases: []string{"r"}, Usage: "Recover a node wallet from a mnemonic phrase", - UsageText: "rocketpool api wallet recover", + UsageText: "rocketpool api wallet recover [options] [mnemonic]", Flags: []cli.Flag{ cli.BoolFlag{ Name: "skip-validator-key-recovery, k", @@ -104,10 +104,6 @@ func RegisterSubcommands(command *cli.Command, name string, aliases []string) { Name: "mnemonic-file, f", Usage: "Specify the path to the mnemonic.\nOmit this flag to enter the mnemonic via plain text.", }, - cli.StringFlag{ - Name: "mnemonic, m", - Usage: "Specify the full mnemonic. Note - this is not secure and is recommend for testing purposes only.", - }, cli.UintFlag{ Name: "wallet-index, i", Usage: "Specify the index to use with the derivation path when recovering your wallet", @@ -117,9 +113,9 @@ func RegisterSubcommands(command *cli.Command, name string, aliases []string) { Action: func(c *cli.Context) error { // Validate input - // Must supply either --mnemonic-file or --mnemonic, but not both - if (c.String("mnemonic-file") == "") == (c.String("mnemonic") == "") { - return fmt.Errorf("Please specify a mnemonic file or mnemonic phrase, but not both") + // Must supply either --mnemonic-file or via stdin, but not both + if (c.String("mnemonic-file") == "") == (c.Args().Get(0) == "") { + return fmt.Errorf("Please specify a mnemonic file or mnemonic via stdin, but not both") } // Read mnemonic from file @@ -133,8 +129,8 @@ func RegisterSubcommands(command *cli.Command, name string, aliases []string) { } // Read mnemonic from stdin - if c.String("mnemonic") != "" { - providedMnemonic = c.String("mnemonic") + if c.Args().Get(0) != "" { + providedMnemonic = c.Args().Get(0) } // Validate mnemonic