diff --git a/cmd/clef/main.go b/cmd/clef/main.go index 44a72057be..10cb4628b8 100644 --- a/cmd/clef/main.go +++ b/cmd/clef/main.go @@ -40,7 +40,6 @@ import ( "github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/ethereum/go-ethereum/console/prompt" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/internal/ethapi" @@ -289,7 +288,7 @@ func initializeSecrets(c *cli.Context) error { text := "The master seed of clef will be locked with a password.\nPlease specify a password. Do not forget this password!" var password string for { - password = getPassPhrase(text, true) + password = utils.GetPassPhrase(text, true) if err := core.ValidatePasswordFormat(password); err != nil { fmt.Printf("invalid password: %v\n", err) } else { @@ -362,7 +361,7 @@ func setCredential(ctx *cli.Context) error { utils.Fatalf("Invalid address specified: %s", addr) } address := common.HexToAddress(addr) - password := getPassPhrase("Please enter a password to store for this address:", true) + password := utils.GetPassPhrase("Please enter a password to store for this address:", true) fmt.Println() stretchedKey, err := readMasterKey(ctx, nil) @@ -720,7 +719,7 @@ func readMasterKey(ctx *cli.Context, ui core.UIClientAPI) ([]byte, error) { } password = resp.Text } else { - password = getPassPhrase("Decrypt master seed of clef", false) + password = utils.GetPassPhrase("Decrypt master seed of clef", false) } masterSeed, err := decryptSeed(cipherKey, password) if err != nil { @@ -915,27 +914,6 @@ func testExternalUI(api *core.SignerAPI) { } -// getPassPhrase retrieves the password associated with clef, either fetched -// from a list of preloaded passphrases, or requested interactively from the user. -// TODO: there are many `getPassPhrase` functions, it will be better to abstract them into one. -func getPassPhrase(query string, confirmation bool) string { - fmt.Println(query) - password, err := prompt.Stdin.PromptPassword("Password: ") - if err != nil { - utils.Fatalf("Failed to read password: %v", err) - } - if confirmation { - confirm, err := prompt.Stdin.PromptPassword("Repeat password: ") - if err != nil { - utils.Fatalf("Failed to read password confirmation: %v", err) - } - if password != confirm { - utils.Fatalf("Passwords do not match") - } - } - return password -} - type encryptedSeedStorage struct { Description string `json:"description"` Version int `json:"version"` diff --git a/cmd/ethkey/changepassword.go b/cmd/ethkey/changepassword.go index efff83d836..b9402c2f96 100644 --- a/cmd/ethkey/changepassword.go +++ b/cmd/ethkey/changepassword.go @@ -67,7 +67,7 @@ Change the password of a keyfile.`, } newPhrase = strings.TrimRight(string(content), "\r\n") } else { - newPhrase = promptPassphrase(true) + newPhrase = utils.GetPassPhrase("", true) } // Encrypt the key with the new passphrase. diff --git a/cmd/ethkey/utils.go b/cmd/ethkey/utils.go index 1863bbda84..f2986e8ee9 100644 --- a/cmd/ethkey/utils.go +++ b/cmd/ethkey/utils.go @@ -23,32 +23,10 @@ import ( "strings" "github.com/ethereum/go-ethereum/cmd/utils" - "github.com/ethereum/go-ethereum/console/prompt" "github.com/ethereum/go-ethereum/crypto" "gopkg.in/urfave/cli.v1" ) -// promptPassphrase prompts the user for a passphrase. Set confirmation to true -// to require the user to confirm the passphrase. -func promptPassphrase(confirmation bool) string { - passphrase, err := prompt.Stdin.PromptPassword("Password: ") - if err != nil { - utils.Fatalf("Failed to read password: %v", err) - } - - if confirmation { - confirm, err := prompt.Stdin.PromptPassword("Repeat password: ") - if err != nil { - utils.Fatalf("Failed to read password confirmation: %v", err) - } - if passphrase != confirm { - utils.Fatalf("Passwords do not match") - } - } - - return passphrase -} - // getPassphrase obtains a passphrase given by the user. It first checks the // --passfile command line flag and ultimately prompts the user for a // passphrase. @@ -65,7 +43,7 @@ func getPassphrase(ctx *cli.Context, confirmation bool) string { } // Otherwise prompt the user for the passphrase. - return promptPassphrase(confirmation) + return utils.GetPassPhrase("", confirmation) } // signHash is a helper function that calculates a hash for the given message diff --git a/cmd/geth/accountcmd.go b/cmd/geth/accountcmd.go index 1723ef99fa..6d45c88763 100644 --- a/cmd/geth/accountcmd.go +++ b/cmd/geth/accountcmd.go @@ -23,7 +23,6 @@ import ( "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts/keystore" "github.com/ethereum/go-ethereum/cmd/utils" - prompt2 "github.com/ethereum/go-ethereum/console/prompt" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/log" "gopkg.in/urfave/cli.v1" @@ -212,7 +211,7 @@ func unlockAccount(ks *keystore.KeyStore, address string, i int, passwords []str } for trials := 0; trials < 3; trials++ { prompt := fmt.Sprintf("Unlocking account %s | Attempt %d/%d", address, trials+1, 3) - password := getPassPhrase(prompt, false, i, passwords) + password := utils.GetPassPhraseWithList(prompt, false, i, passwords) err = ks.Unlock(account, password) if err == nil { log.Info("Unlocked account", "address", account.Address.Hex()) @@ -233,36 +232,6 @@ func unlockAccount(ks *keystore.KeyStore, address string, i int, passwords []str return accounts.Account{}, "" } -// getPassPhrase retrieves the password associated with an account, either fetched -// from a list of preloaded passphrases, or requested interactively from the user. -func getPassPhrase(prompt string, confirmation bool, i int, passwords []string) string { - // If a list of passwords was supplied, retrieve from them - if len(passwords) > 0 { - if i < len(passwords) { - return passwords[i] - } - return passwords[len(passwords)-1] - } - // Otherwise prompt the user for the password - if prompt != "" { - fmt.Println(prompt) - } - password, err := prompt2.Stdin.PromptPassword("Password: ") - if err != nil { - utils.Fatalf("Failed to read password: %v", err) - } - if confirmation { - confirm, err := prompt2.Stdin.PromptPassword("Repeat password: ") - if err != nil { - utils.Fatalf("Failed to read password confirmation: %v", err) - } - if password != confirm { - utils.Fatalf("Passwords do not match") - } - } - return password -} - func ambiguousAddrRecovery(ks *keystore.KeyStore, err *keystore.AmbiguousAddrError, auth string) accounts.Account { fmt.Printf("Multiple key files exist for address %x:\n", err.Addr) for _, a := range err.Matches { @@ -305,7 +274,7 @@ func accountCreate(ctx *cli.Context) error { utils.Fatalf("Failed to read configuration: %v", err) } - password := getPassPhrase("Your new account is locked with a password. Please give a password. Do not forget this password.", true, 0, utils.MakePasswordList(ctx)) + password := utils.GetPassPhraseWithList("Your new account is locked with a password. Please give a password. Do not forget this password.", true, 0, utils.MakePasswordList(ctx)) account, err := keystore.StoreKey(keydir, password, scryptN, scryptP) @@ -333,7 +302,7 @@ func accountUpdate(ctx *cli.Context) error { for _, addr := range ctx.Args() { account, oldPassword := unlockAccount(ks, addr, 0, nil) - newPassword := getPassPhrase("Please give a new password. Do not forget this password.", true, 0, nil) + newPassword := utils.GetPassPhraseWithList("Please give a new password. Do not forget this password.", true, 0, nil) if err := ks.Update(account, oldPassword, newPassword); err != nil { utils.Fatalf("Could not update the account: %v", err) } @@ -352,7 +321,7 @@ func importWallet(ctx *cli.Context) error { } stack, _ := makeConfigNode(ctx) - passphrase := getPassPhrase("", false, 0, utils.MakePasswordList(ctx)) + passphrase := utils.GetPassPhraseWithList("", false, 0, utils.MakePasswordList(ctx)) ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore) acct, err := ks.ImportPreSaleKey(keyJSON, passphrase) @@ -373,7 +342,7 @@ func accountImport(ctx *cli.Context) error { utils.Fatalf("Failed to load the private key: %v", err) } stack, _ := makeConfigNode(ctx) - passphrase := getPassPhrase("Your new account is locked with a password. Please give a password. Do not forget this password.", true, 0, utils.MakePasswordList(ctx)) + passphrase := utils.GetPassPhraseWithList("Your new account is locked with a password. Please give a password. Do not forget this password.", true, 0, utils.MakePasswordList(ctx)) ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore) acct, err := ks.ImportECDSA(key, passphrase) diff --git a/cmd/utils/prompt.go b/cmd/utils/prompt.go new file mode 100644 index 0000000000..5c7cbabcb0 --- /dev/null +++ b/cmd/utils/prompt.go @@ -0,0 +1,62 @@ +// Copyright 2020 The go-ethereum Authors +// This file is part of go-ethereum. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Package utils contains internal helper functions for go-ethereum commands. +package utils + +import ( + "fmt" + + "github.com/ethereum/go-ethereum/console/prompt" +) + +// GetPassPhrase displays the given text(prompt) to the user and requests some textual +// data to be entered, but one which must not be echoed out into the terminal. +// The method returns the input provided by the user. +func GetPassPhrase(text string, confirmation bool) string { + if text != "" { + fmt.Println(text) + } + password, err := prompt.Stdin.PromptPassword("Password: ") + if err != nil { + Fatalf("Failed to read password: %v", err) + } + if confirmation { + confirm, err := prompt.Stdin.PromptPassword("Repeat password: ") + if err != nil { + Fatalf("Failed to read password confirmation: %v", err) + } + if password != confirm { + Fatalf("Passwords do not match") + } + } + return password +} + +// GetPassPhraseWithList retrieves the password associated with an account, either fetched +// from a list of preloaded passphrases, or requested interactively from the user. +func GetPassPhraseWithList(text string, confirmation bool, index int, passwords []string) string { + // If a list of passwords was supplied, retrieve from them + if len(passwords) > 0 { + if index < len(passwords) { + return passwords[index] + } + return passwords[len(passwords)-1] + } + // Otherwise prompt the user for the password + password := GetPassPhrase(text, confirmation) + return password +} diff --git a/cmd/utils/prompt_test.go b/cmd/utils/prompt_test.go new file mode 100644 index 0000000000..62ea75a3f6 --- /dev/null +++ b/cmd/utils/prompt_test.go @@ -0,0 +1,74 @@ +// Copyright 2020 The go-ethereum Authors +// This file is part of go-ethereum. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Package utils contains internal helper functions for go-ethereum commands. +package utils + +import ( + "testing" +) + +func TestGetPassPhraseWithList(t *testing.T) { + type args struct { + text string + confirmation bool + index int + passwords []string + } + tests := []struct { + name string + args args + want string + }{ + { + "test1", + args{ + "text1", + false, + 0, + []string{"zero", "one", "two"}, + }, + "zero", + }, + { + "test2", + args{ + "text2", + false, + 5, + []string{"zero", "one", "two"}, + }, + "two", + }, + { + "test3", + args{ + "text3", + true, + 1, + []string{"zero", "one", "two"}, + }, + "one", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := GetPassPhraseWithList(tt.args.text, tt.args.confirmation, tt.args.index, tt.args.passwords); got != tt.want { + t.Errorf("GetPassPhraseWithList() = %v, want %v", got, tt.want) + } + }) + } +}