Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cmd: Add passphrase confirmation when creating wallet from existing seed #3303

Merged
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 60 additions & 49 deletions cmd/lncli/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -1398,33 +1398,12 @@ func create(ctx *cli.Context) error {
client, cleanUp := getWalletUnlockerClient(ctx)
defer cleanUp()

// First, we'll prompt the user for their passphrase twice to ensure
// both attempts match up properly.
fmt.Printf("Input wallet password: ")
pw1, err := terminal.ReadPassword(int(syscall.Stdin))
if err != nil {
return err
}
fmt.Println()

fmt.Printf("Confirm wallet password: ")
pw2, err := terminal.ReadPassword(int(syscall.Stdin))
walletPassword, err := capturePassword(
"Input wallet password: ", false, walletunlocker.ValidatePassword,
)
if err != nil {
return err
}
fmt.Println()

// If the passwords don't match, then we'll return an error.
if !bytes.Equal(pw1, pw2) {
return fmt.Errorf("passwords don't match")
}

// If the password length is less than 8 characters, then we'll
// return an error.
pwErr := walletunlocker.ValidatePassword(pw1)
if pwErr != nil {
return pwErr
}

// Next, we'll see if the user has 24-word mnemonic they want to use to
// derive a seed within the wallet.
Expand Down Expand Up @@ -1539,47 +1518,30 @@ mnemonicCheck:
// want to use, we'll generate a fresh one with the GenSeed
// command.
fmt.Println("Your cipher seed can optionally be encrypted.")
fmt.Printf("Input your passphrase if you wish to encrypt it " +

instruction := "Input your passphrase if you wish to encrypt it " +
"(or press enter to proceed without a cipher seed " +
"passphrase): ")
aezeedPass1, err := terminal.ReadPassword(int(syscall.Stdin))
"passphrase): "
aezeedPass, err = capturePassword(
instruction, true, func(_ []byte) error { return nil },
)
if err != nil {
return err
}
fmt.Println()

if len(aezeedPass1) != 0 {
fmt.Printf("Confirm cipher seed passphrase: ")
aezeedPass2, err := terminal.ReadPassword(
int(syscall.Stdin),
)
if err != nil {
return err
}
fmt.Println()

// If the passwords don't match, then we'll return an
// error.
if !bytes.Equal(aezeedPass1, aezeedPass2) {
return fmt.Errorf("cipher seed pass phrases " +
"don't match")
}
}

fmt.Println()
fmt.Println("Generating fresh cipher seed...")
fmt.Println()

genSeedReq := &lnrpc.GenSeedRequest{
AezeedPassphrase: aezeedPass1,
AezeedPassphrase: aezeedPass,
}
seedResp, err := client.GenSeed(ctxb, genSeedReq)
if err != nil {
return fmt.Errorf("unable to generate seed: %v", err)
}

cipherSeedMnemonic = seedResp.CipherSeedMnemonic
aezeedPass = aezeedPass1
}

// Before we initialize the wallet, we'll display the cipher seed to
Expand Down Expand Up @@ -1635,7 +1597,7 @@ mnemonicCheck:
// With either the user's prior cipher seed, or a newly generated one,
// we'll go ahead and initialize the wallet.
req := &lnrpc.InitWalletRequest{
WalletPassword: pw1,
WalletPassword: walletPassword,
CipherSeedMnemonic: cipherSeedMnemonic,
AezeedPassphrase: aezeedPass,
RecoveryWindow: recoveryWindow,
Expand All @@ -1650,6 +1612,55 @@ mnemonicCheck:
return nil
}

// capturePassword returns a password value that has been entered twice by the
// user, to ensure that the user knows what password they have entered. The user
// will be prompted to retry until the passwords match. If the optional param is
// true, the function may return an empty byte array if the user opts against
// using a password.
func capturePassword(instruction string, optional bool,
validate func([]byte) error) ([]byte, error) {

for {
fmt.Printf(instruction)
password, err := terminal.ReadPassword(int(syscall.Stdin))
if err != nil {
return nil, err
}
fmt.Println()

// Do not require users to repeat password if
// it is optional and they are not using one.
if len(password) == 0 && optional {
return nil, nil
}

// If the password provided is not valid, restart
// password capture process from the beginning.
if err := validate(password); err != nil {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call to do validation before asking to repeat! 😀

fmt.Println(err.Error())
fmt.Println()
continue
}

fmt.Println("Confirm password:")
passwordConfirmed, err := terminal.ReadPassword(
int(syscall.Stdin),
)
if err != nil {
return nil, err
}
fmt.Println()

if bytes.Equal(password, passwordConfirmed) {
return password, nil
}

fmt.Println("Passwords don't match, " +
"please try again")
fmt.Println()
}
}

var unlockCommand = cli.Command{
Name: "unlock",
Category: "Startup",
Expand Down