Change validator password (#5869)

* Created merge command
* Key merging
* Merge branch 'master' into db-manage-validators
* flag comments
* fixed compilation errors
* added comment to Merge function
* Tested that merging copies keys
* Created command to change password for keys
* Merge branch 'master' into change-validator-password
* Merge branch 'master' into change-validator-password
* cleanup after code review
* Merge remote-tracking branch 'rkapka/change-validator-password' into change-validator-password
* Merge branch 'master' into change-validator-password
* Merge branch 'master' into change-validator-password
* Merge branch 'master' into change-validator-password
* Merge branch 'master' into change-validator-password
This commit is contained in:
rkapka
2020-05-18 23:35:17 +02:00
committed by GitHub
parent c07f1600e9
commit 2a1a0ce43f
5 changed files with 146 additions and 12 deletions

View File

@@ -26,10 +26,9 @@ var log = logrus.WithField("prefix", "accounts")
// DecryptKeysFromKeystore extracts a set of validator private keys from
// an encrypted keystore directory and a password string.
func DecryptKeysFromKeystore(directory string, password string) (map[string]*keystore.Key, error) {
validatorPrefix := params.BeaconConfig().ValidatorPrivkeyFileName
func DecryptKeysFromKeystore(directory string, filePrefix string, password string) (map[string]*keystore.Key, error) {
ks := keystore.NewKeystore(directory)
validatorKeys, err := ks.GetKeys(directory, validatorPrefix, password, true /* warnOnFail */)
validatorKeys, err := ks.GetKeys(directory, filePrefix, password, true)
if err != nil {
return nil, errors.Wrap(err, "could not get private key")
}
@@ -47,10 +46,10 @@ func VerifyAccountNotExists(directory string, password string) error {
// First, if the keystore already exists, throws an error as there can only be
// one keystore per validator client.
ks := keystore.NewKeystore(directory)
if _, err := ks.GetKeys(directory, shardWithdrawalKeyFile, password, false /* warnOnFail */); err == nil {
if _, err := ks.GetKeys(directory, shardWithdrawalKeyFile, password, false); err == nil {
return fmt.Errorf("keystore at path already exists: %s", shardWithdrawalKeyFile)
}
if _, err := ks.GetKeys(directory, validatorKeyFile, password, false /* warnOnFail */); err == nil {
if _, err := ks.GetKeys(directory, validatorKeyFile, password, false); err == nil {
return fmt.Errorf("keystore at path already exists: %s", validatorKeyFile)
}
return nil
@@ -158,7 +157,7 @@ func CreateValidatorAccount(path string, passphrase string) (string, string, err
// PrintPublicAndPrivateKeys uses the passed in path and prints out the public and private keys in that directory.
func PrintPublicAndPrivateKeys(path string, passphrase string) error {
keystores, err := DecryptKeysFromKeystore(path, passphrase)
keystores, err := DecryptKeysFromKeystore(path, params.BeaconConfig().ValidatorPrivkeyFileName, passphrase)
if err != nil {
return errors.Wrapf(err, "failed to decrypt keystore keys at path %s", path)
}
@@ -185,8 +184,8 @@ func DefaultValidatorDir() string {
return ""
}
// HandleEmptyFlags checks what the set flags are and allows the user to manually enter them if they're empty.
func HandleEmptyFlags(cliCtx *cli.Context, confirmPassword bool) (string, string, error) {
// HandleEmptyKeystoreFlags checks what the set flags are and allows the user to manually enter them if they're empty.
func HandleEmptyKeystoreFlags(cliCtx *cli.Context, confirmPassword bool) (string, string, error) {
path := cliCtx.String(flags.KeystorePathFlag.Name)
passphrase := cliCtx.String(flags.PasswordFlag.Name)
@@ -215,6 +214,42 @@ func HandleEmptyFlags(cliCtx *cli.Context, confirmPassword bool) (string, string
return path, passphrase, nil
}
// ChangePassword changes the password for all keys located in a keystore.
// Password is changed only for keys that can be decrypted using the old password.
func ChangePassword(keystorePath string, oldPassword string, newPassword string) error {
err := changePasswordForKeyType(
keystorePath,
params.BeaconConfig().ValidatorPrivkeyFileName,
oldPassword,
newPassword)
if err != nil {
return err
}
return changePasswordForKeyType(
keystorePath,
params.BeaconConfig().WithdrawalPrivkeyFileName,
oldPassword,
newPassword)
}
func changePasswordForKeyType(keystorePath string, filePrefix string, oldPassword string, newPassword string) error {
keys, err := DecryptKeysFromKeystore(keystorePath, filePrefix, oldPassword)
if err != nil {
return errors.Wrap(err, "Failed to decrypt keys")
}
keyStore := keystore.NewKeystore(keystorePath)
for _, key := range keys {
keyFileName := keystorePath + filePrefix + hex.EncodeToString(key.PublicKey.Marshal())[:12]
if err := keyStore.StoreKey(keyFileName, key, newPassword); err != nil {
return errors.Wrapf(err, "Failed to encrypt key %s with the new password", keyFileName)
}
}
return nil
}
// homeDir returns home directory path.
func homeDir() string {
if home := os.Getenv("HOME"); home != "" {

View File

@@ -1,6 +1,7 @@
package accounts
import (
"encoding/hex"
"flag"
"fmt"
"io/ioutil"
@@ -62,7 +63,7 @@ func TestHandleEmptyFlags_FlagsSet(t *testing.T) {
set.String(flags.KeystorePathFlag.Name, passedPath, "set keystore path")
set.String(flags.PasswordFlag.Name, passedPassword, "set keystore password")
ctx := cli.NewContext(app, set, nil)
path, passphrase, err := HandleEmptyFlags(ctx, false)
path, passphrase, err := HandleEmptyKeystoreFlags(ctx, false)
if err != nil {
t.Fatal(err)
}
@@ -74,3 +75,69 @@ func TestHandleEmptyFlags_FlagsSet(t *testing.T) {
t.Fatalf("Expected set password to be unchanged, expected %s, received %s", passedPassword, passphrase)
}
}
func TestChangePassword_KeyEncryptedWithNewPassword(t *testing.T) {
directory := testutil.TempDir() + "/testkeystore"
defer func() {
if err := os.RemoveAll(directory); err != nil {
t.Logf("Could not remove directory: %v", err)
}
}()
oldPassword := "old"
newPassword := "new"
validatorKey, err := keystore.NewKey()
if err != nil {
t.Fatalf("Cannot create new key: %v", err)
}
ks := keystore.NewKeystore(directory)
if err := ks.StoreKey(directory+params.BeaconConfig().ValidatorPrivkeyFileName, validatorKey, oldPassword); err != nil {
t.Fatalf("Unable to store key %v", err)
}
if err := ChangePassword(directory, oldPassword, newPassword); err != nil {
t.Fatal(err)
}
keys, err := DecryptKeysFromKeystore(directory, params.BeaconConfig().ValidatorPrivkeyFileName, newPassword)
if err != nil {
t.Fatal(err)
}
if _, ok := keys[hex.EncodeToString(validatorKey.PublicKey.Marshal())]; !ok {
t.Error("Key not encrypted using the new password")
}
}
func TestChangePassword_KeyNotMatchingOldPasswordNotEncryptedWithNewPassword(t *testing.T) {
directory := testutil.TempDir() + "/testkeystore"
defer func() {
if err := os.RemoveAll(directory); err != nil {
t.Logf("Could not remove directory: %v", err)
}
}()
oldPassword := "old"
newPassword := "new"
validatorKey, err := keystore.NewKey()
if err != nil {
t.Fatalf("Cannot create new key: %v", err)
}
ks := keystore.NewKeystore(directory)
if err := ks.StoreKey(directory+params.BeaconConfig().ValidatorPrivkeyFileName, validatorKey, "notmatching"); err != nil {
t.Fatalf("Unable to store key %v", err)
}
if err := ChangePassword(directory, oldPassword, newPassword); err != nil {
t.Fatal(err)
}
keys, err := DecryptKeysFromKeystore(directory, params.BeaconConfig().ValidatorPrivkeyFileName, newPassword)
if err != nil {
t.Fatal(err)
}
if _, ok := keys[hex.EncodeToString(validatorKey.PublicKey.Marshal())]; ok {
t.Error("Key incorrectly encrypted using the new password")
}
}

View File

@@ -20,6 +20,7 @@ go_library(
"//shared/bls:go_default_library",
"//shared/bytesutil:go_default_library",
"//shared/interop:go_default_library",
"//shared/params:go_default_library",
"//validator/accounts:go_default_library",
"@com_github_pkg_errors//:go_default_library",
"@com_github_prysmaticlabs_ethereumapis//eth/v1alpha1:go_default_library",

View File

@@ -5,6 +5,8 @@ import (
"os"
"strings"
"github.com/prysmaticlabs/prysm/shared/params"
"github.com/prysmaticlabs/prysm/shared/bls"
"github.com/prysmaticlabs/prysm/shared/bytesutil"
"github.com/prysmaticlabs/prysm/validator/accounts"
@@ -73,7 +75,7 @@ func NewKeystore(input string) (KeyManager, string, error) {
}
}
keyMap, err := accounts.DecryptKeysFromKeystore(opts.Path, opts.Passphrase)
keyMap, err := accounts.DecryptKeysFromKeystore(opts.Path, params.BeaconConfig().ValidatorPrivkeyFileName, opts.Passphrase)
if err != nil {
return nil, keystoreOptsHelp, err
}

View File

@@ -107,7 +107,7 @@ contract in order to activate the validator client`,
params.UseMinimalConfig()
}
keystorePath, passphrase, err := accounts.HandleEmptyFlags(cliCtx, true /*confirmPassword*/)
keystorePath, passphrase, err := accounts.HandleEmptyKeystoreFlags(cliCtx, true)
if err != nil {
log.WithError(err).Error("Could not list keys")
}
@@ -125,7 +125,7 @@ contract in order to activate the validator client`,
flags.PasswordFlag,
},
Action: func(cliCtx *cli.Context) error {
keystorePath, passphrase, err := accounts.HandleEmptyFlags(cliCtx, false /*confirmPassword*/)
keystorePath, passphrase, err := accounts.HandleEmptyKeystoreFlags(cliCtx, false)
if err != nil {
log.WithError(err).Error("Could not list keys")
}
@@ -135,6 +135,35 @@ contract in order to activate the validator client`,
return nil
},
},
{
Name: "change-password",
Description: "changes password for all keys located in a keystore",
Flags: []cli.Flag{
flags.KeystorePathFlag,
flags.PasswordFlag,
},
Action: func(cliCtx *cli.Context) error {
keystorePath, oldPassword, err := accounts.HandleEmptyKeystoreFlags(cliCtx, false)
if err != nil {
log.WithError(err).Error("Could not read keystore path and/or the old password")
}
log.Info("Please enter the new password")
newPassword, err := cmd.EnterPassword(true, cmd.StdInPasswordReader{})
if err != nil {
log.WithError(err).Error("Could not read the new password")
}
err = accounts.ChangePassword(keystorePath, oldPassword, newPassword)
if err != nil {
log.WithError(err).Error("Changing password failed")
} else {
log.Info("Password changed successfully")
}
return nil
},
},
},
},
}