Skip to content

Commit ba440ab

Browse files
authored
Store Hashed Password to Wallet Path (#7295)
* store wallet pass on creation * store wallet hash * save hash to wallet dir * initialize wallet hash * gaz * imports spacing * remove mentions of hashed pass from db schema * simplify the wallet hash code * comment removal * Merge branch 'master' into fix-password-override * pass tests * test fix * Merge branch 'fix-password-override' of github.com:prysmaticlabs/prysm into fix-password-override * remove extraneous printfs * tests passing * require auth for create wallet * gaz * fix signup logic * Merge refs/heads/master into fix-password-override * Merge refs/heads/master into fix-password-override * Merge refs/heads/master into fix-password-override * Merge refs/heads/master into fix-password-override * Merge branch 'master' into fix-password-override
1 parent 09640ae commit ba440ab

22 files changed

+162
-135
lines changed

validator/accounts/v2/accounts_create_test.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ func (p *passwordReader) passwordReaderFunc(file *os.File) ([]byte, error) {
8080
func Test_KeysConsistency_Direct(t *testing.T) {
8181
walletDir, passwordsDir, walletPasswordFile := setupWalletAndPasswordsDir(t)
8282

83-
//Specify the 'initial'/correct password locally to this file for convenience.
83+
// Specify the 'initial'/correct password locally to this file for convenience.
8484
require.NoError(t, ioutil.WriteFile(walletPasswordFile, []byte("Pa$sW0rD0__Fo0xPr"), os.ModePerm))
8585

8686
cliCtx := setupWalletCtx(t, &testWalletConfig{
@@ -106,7 +106,10 @@ func Test_KeysConsistency_Direct(t *testing.T) {
106106

107107
// Now we change the password to "SecoNDxyzPass__9!@#"
108108
require.NoError(t, ioutil.WriteFile(walletPasswordFile, []byte("SecoNDxyzPass__9!@#"), os.ModePerm))
109-
// OpenWalletOrElseCli() doesn't really 'challenge' the wrong password
109+
w, err = wallet.OpenWalletOrElseCli(cliCtx, CreateAndSaveWalletCli)
110+
require.ErrorContains(t, "wrong password for wallet", err)
111+
112+
require.NoError(t, ioutil.WriteFile(walletPasswordFile, []byte("Pa$sW0rD0__Fo0xPr"), os.ModePerm))
110113
w, err = wallet.OpenWalletOrElseCli(cliCtx, CreateAndSaveWalletCli)
111114
require.NoError(t, err)
112115

validator/accounts/v2/accounts_exit_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,5 +125,5 @@ func TestPrepareWallet_EmptyWalletReturnsError(t *testing.T) {
125125
})
126126
require.NoError(t, err)
127127
_, _, err = prepareWallet(cliCtx)
128-
assert.ErrorContains(t, "wallet is empty, no accounts to perform voluntary exit", err)
128+
assert.ErrorContains(t, "please recreate your wallet", err)
129129
}

validator/accounts/v2/accounts_import.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,10 @@ func ImportAccountsCli(cliCtx *cli.Context) error {
9090
if err = createDirectKeymanagerWallet(cliCtx.Context, w); err != nil {
9191
return nil, errors.Wrap(err, "could not create keymanager")
9292
}
93+
// We store the hashed password to disk.
94+
if err := w.SaveHashedPassword(cliCtx.Context); err != nil {
95+
return nil, err
96+
}
9397
log.WithField("wallet-path", cfg.WalletCfg.WalletDir).Info(
9498
"Successfully created new wallet",
9599
)

validator/accounts/v2/wallet/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ go_library(
2121
"@com_github_pkg_errors//:go_default_library",
2222
"@com_github_sirupsen_logrus//:go_default_library",
2323
"@com_github_urfave_cli_v2//:go_default_library",
24+
"@org_golang_x_crypto//bcrypt:go_default_library",
2425
],
2526
)
2627

validator/accounts/v2/wallet/wallet.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,16 @@ import (
2323
"github.com/prysmaticlabs/prysm/validator/keymanager/v2/remote"
2424
"github.com/sirupsen/logrus"
2525
"github.com/urfave/cli/v2"
26+
"golang.org/x/crypto/bcrypt"
2627
)
2728

2829
var log = logrus.WithField("prefix", "wallet")
2930

3031
const (
3132
// KeymanagerConfigFileName for the keymanager used by the wallet: direct, derived, or remote.
3233
KeymanagerConfigFileName = "keymanageropts.json"
34+
// HashedPasswordFileName for the wallet.
35+
HashedPasswordFileName = "hash"
3336
// DirectoryPermissions for directories created under the wallet path.
3437
DirectoryPermissions = os.ModePerm
3538
// NewWalletPasswordPromptText for wallet creation.
@@ -38,6 +41,7 @@ const (
3841
WalletPasswordPromptText = "Wallet password"
3942
// ConfirmPasswordPromptText for confirming a wallet password.
4043
ConfirmPasswordPromptText = "Confirm password"
44+
hashCost = 8
4145
)
4246

4347
var (
@@ -136,6 +140,16 @@ func OpenWalletOrElseCli(cliCtx *cli.Context, otherwise func(cliCtx *cli.Context
136140
false, /* Do not confirm password */
137141
ValidateExistingPass,
138142
)
143+
if fileutil.FileExists(filepath.Join(walletDir, HashedPasswordFileName)) {
144+
hashedPassword, err := fileutil.ReadFileAsBytes(filepath.Join(walletDir, HashedPasswordFileName))
145+
if err != nil {
146+
return nil, err
147+
}
148+
// Compare the wallet password here.
149+
if err := bcrypt.CompareHashAndPassword(hashedPassword, []byte(walletPassword)); err != nil {
150+
return nil, errors.Wrap(err, "wrong password for wallet")
151+
}
152+
}
139153
return OpenWallet(cliCtx.Context, &Config{
140154
WalletDir: walletDir,
141155
WalletPassword: walletPassword,
@@ -217,6 +231,18 @@ func (w *Wallet) InitializeKeymanager(
217231
if err != nil {
218232
return nil, errors.Wrap(err, "could not initialize direct keymanager")
219233
}
234+
if !fileutil.FileExists(filepath.Join(w.walletDir, HashedPasswordFileName)) {
235+
keys, err := keymanager.FetchValidatingPublicKeys(ctx)
236+
if err != nil {
237+
return nil, err
238+
}
239+
if keys == nil || len(keys) == 0 {
240+
return nil, errors.New("please recreate your wallet with wallet-v2 create")
241+
}
242+
if err := w.SaveHashedPassword(ctx); err != nil {
243+
return nil, errors.Wrap(err, "could not save hashed password to disk")
244+
}
245+
}
220246
case v2keymanager.Derived:
221247
opts, err := derived.UnmarshalOptionsFile(configFile)
222248
if err != nil {
@@ -230,6 +256,11 @@ func (w *Wallet) InitializeKeymanager(
230256
if err != nil {
231257
return nil, errors.Wrap(err, "could not initialize derived keymanager")
232258
}
259+
if !fileutil.FileExists(filepath.Join(w.walletDir, HashedPasswordFileName)) {
260+
if err := w.SaveHashedPassword(ctx); err != nil {
261+
return nil, errors.Wrap(err, "could not save hashed password to disk")
262+
}
263+
}
233264
case v2keymanager.Remote:
234265
opts, err := remote.UnmarshalOptionsFile(configFile)
235266
if err != nil {
@@ -375,6 +406,20 @@ func (w *Wallet) WriteEncryptedSeedToDisk(ctx context.Context, encoded []byte) e
375406
return nil
376407
}
377408

409+
// SaveHashedPassword to disk for the wallet.
410+
func (w *Wallet) SaveHashedPassword(ctx context.Context) error {
411+
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(w.walletPassword), hashCost)
412+
if err != nil {
413+
return errors.Wrap(err, "could not generate hashed password")
414+
}
415+
hashFilePath := filepath.Join(w.walletDir, HashedPasswordFileName)
416+
// Write the config file to disk.
417+
if err := ioutil.WriteFile(hashFilePath, hashedPassword, params.BeaconIoConfig().ReadWritePermissions); err != nil {
418+
return errors.Wrap(err, "could not write hashed password for wallet to disk")
419+
}
420+
return nil
421+
}
422+
378423
func readKeymanagerKindFromWalletPath(walletPath string) (v2keymanager.Kind, error) {
379424
walletItem, err := os.Open(walletPath)
380425
if err != nil {

validator/accounts/v2/wallet_create.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,15 @@ func CreateAndSaveWalletCli(cliCtx *cli.Context) (*wallet.Wallet, error) {
5151
return nil, errors.New("a wallet of this type already exists at this location. Please input an" +
5252
" alternative location for the new wallet or remove the current wallet")
5353
}
54-
return CreateWalletWithKeymanager(cliCtx.Context, createWalletConfig)
54+
w, err := CreateWalletWithKeymanager(cliCtx.Context, createWalletConfig)
55+
if err != nil {
56+
return nil, errors.Wrap(err, "could not create wallet with keymanager")
57+
}
58+
// We store the hashed password to disk.
59+
if err := w.SaveHashedPassword(cliCtx.Context); err != nil {
60+
return nil, errors.Wrap(err, "could not save hashed password to database")
61+
}
62+
return w, nil
5563
}
5664

5765
// CreateWalletWithKeymanager specified by configuration options.

validator/accounts/v2/wallet_recover.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,12 +62,17 @@ func RecoverWalletCli(cliCtx *cli.Context) error {
6262
if err != nil {
6363
return errors.Wrap(err, "could not get number of accounts to recover")
6464
}
65-
if _, err := RecoverWallet(cliCtx.Context, &RecoverWalletConfig{
65+
w, err := RecoverWallet(cliCtx.Context, &RecoverWalletConfig{
6666
WalletDir: walletDir,
6767
WalletPassword: walletPassword,
6868
Mnemonic: mnemonic,
6969
NumAccounts: numAccounts,
70-
}); err != nil {
70+
})
71+
if err != nil {
72+
return err
73+
}
74+
// We store the hashed password to disk.
75+
if err := w.SaveHashedPassword(cliCtx.Context); err != nil {
7176
return err
7277
}
7378
log.Infof(

validator/accounts/v2/wallet_recover_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,8 @@ func TestRecoverDerivedWallet(t *testing.T) {
5656

5757
ctx := context.Background()
5858
w, err := wallet.OpenWallet(cliCtx.Context, &wallet.Config{
59-
WalletDir: walletDir,
59+
WalletDir: walletDir,
60+
WalletPassword: password,
6061
})
6162
assert.NoError(t, err)
6263

validator/db/iface/interface.go

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,4 @@ type ValidatorDB interface {
2525
// Attester protection related methods.
2626
AttestationHistoryForPubKeys(ctx context.Context, publicKeys [][48]byte) (map[[48]byte]*slashpb.AttestationHistory, error)
2727
SaveAttestationHistoryForPubKeys(ctx context.Context, historyByPubKey map[[48]byte]*slashpb.AttestationHistory) error
28-
// Validator RPC authentication methods.
29-
SaveHashedPasswordForAPI(ctx context.Context, hashedPassword []byte) error
30-
HashedPasswordForAPI(ctx context.Context) ([]byte, error)
3128
}

validator/db/kv/BUILD.bazel

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ go_library(
1010
"new_proposal_history.go",
1111
"proposal_history.go",
1212
"schema.go",
13-
"web_api.go",
1413
],
1514
importpath = "github.com/prysmaticlabs/prysm/validator/db/kv",
1615
visibility = ["//validator:__subpackages__"],
@@ -36,7 +35,6 @@ go_test(
3635
"manage_test.go",
3736
"new_proposal_history_test.go",
3837
"proposal_history_test.go",
39-
"web_api_test.go",
4038
],
4139
embed = [":go_default_library"],
4240
deps = [

validator/db/kv/db.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,6 @@ func NewKVStore(dirPath string, pubKeys [][48]byte) (*Store, error) {
7777
tx,
7878
historicProposalsBucket,
7979
historicAttestationsBucket,
80-
validatorAPIBucket,
8180
newhistoricProposalsBucket,
8281
)
8382
}); err != nil {

validator/db/kv/schema.go

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,4 @@ var (
77
newhistoricProposalsBucket = []byte("proposal-history-bucket-interchange")
88
// Validator slashing protection from slashable attestations.
99
historicAttestationsBucket = []byte("attestation-history-bucket")
10-
// Bucket for storing important information regarding the validator API
11-
// such as a password hash for API authentication.
12-
validatorAPIBucket = []byte("validator-api-bucket")
13-
// Bucket key for retrieving the hashed password used for
14-
// authentication to the validator API.
15-
apiHashedPasswordKey = []byte("hashed-password")
1610
)

validator/db/kv/web_api.go

Lines changed: 0 additions & 29 deletions
This file was deleted.

validator/db/kv/web_api_test.go

Lines changed: 0 additions & 26 deletions
This file was deleted.

validator/keymanager/v2/derived/derived.go

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,6 @@ type SetupConfig struct {
6767
Opts *KeymanagerOpts
6868
Wallet iface.Wallet
6969
SkipMnemonicConfirm bool
70-
WalletPassword string
7170
Mnemonic string
7271
}
7372

@@ -99,9 +98,8 @@ func NewKeymanager(
9998
// Check if the wallet seed file exists. If it does not, we initialize one
10099
// by creating a new mnemonic and writing the encrypted file to disk.
101100
var encodedSeedFile []byte
102-
cfg.WalletPassword = cfg.Wallet.Password()
103101
if !fileutil.FileExists(filepath.Join(cfg.Wallet.AccountsDir(), EncryptedSeedFileName)) {
104-
seedConfig, err := initializeWalletSeedFile(cfg.WalletPassword, cfg.SkipMnemonicConfirm)
102+
seedConfig, err := initializeWalletSeedFile(cfg.Wallet.Password(), cfg.SkipMnemonicConfirm)
105103
if err != nil {
106104
return nil, errors.Wrap(err, "could not initialize new wallet seed file")
107105
}
@@ -132,7 +130,7 @@ func NewKeymanager(
132130
return nil, errors.Wrap(err, "could not unmarshal seed configuration")
133131
}
134132
decryptor := keystorev4.New()
135-
seed, err := decryptor.Decrypt(seedConfig.Crypto, cfg.WalletPassword)
133+
seed, err := decryptor.Decrypt(seedConfig.Crypto, cfg.Wallet.Password())
136134
if err != nil {
137135
return nil, errors.Wrap(err, "could not decrypt seed configuration with password")
138136
}
@@ -162,7 +160,7 @@ func KeymanagerForPhrase(
162160
// Check if the wallet seed file exists. If it does not, we initialize one
163161
// by creating a new mnemonic and writing the encrypted file to disk.
164162
var encodedSeedFile []byte
165-
seedConfig, err := seedFileFromMnemonic(cfg.Mnemonic, cfg.WalletPassword)
163+
seedConfig, err := seedFileFromMnemonic(cfg.Mnemonic, cfg.Wallet.Password())
166164
if err != nil {
167165
return nil, errors.Wrap(err, "could not initialize new wallet seed file")
168166
}
@@ -174,7 +172,7 @@ func KeymanagerForPhrase(
174172
return nil, errors.Wrap(err, "could not write encrypted wallet seed config to disk")
175173
}
176174
decryptor := keystorev4.New()
177-
seed, err := decryptor.Decrypt(seedConfig.Crypto, cfg.WalletPassword)
175+
seed, err := decryptor.Decrypt(seedConfig.Crypto, cfg.Wallet.Password())
178176
if err != nil {
179177
return nil, errors.Wrap(err, "could not decrypt seed configuration with password")
180178
}

validator/rpc/BUILD.bazel

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ go_library(
1616
deps = [
1717
"//proto/validator/accounts/v2:go_default_library",
1818
"//shared/event:go_default_library",
19+
"//shared/fileutil:go_default_library",
20+
"//shared/params:go_default_library",
1921
"//shared/petnames:go_default_library",
2022
"//shared/promptutil:go_default_library",
2123
"//shared/rand:go_default_library",
@@ -64,6 +66,8 @@ go_test(
6466
"//proto/validator/accounts/v2:go_default_library",
6567
"//shared/bls:go_default_library",
6668
"//shared/event:go_default_library",
69+
"//shared/fileutil:go_default_library",
70+
"//shared/params:go_default_library",
6771
"//shared/testutil:go_default_library",
6872
"//shared/testutil/assert:go_default_library",
6973
"//shared/testutil/require:go_default_library",
@@ -72,7 +76,7 @@ go_test(
7276
"//validator/client:go_default_library",
7377
"//validator/db/testing:go_default_library",
7478
"//validator/keymanager/v2:go_default_library",
75-
"//validator/keymanager/v2/direct:go_default_library",
79+
"//validator/keymanager/v2/derived:go_default_library",
7680
"@com_github_gogo_protobuf//types:go_default_library",
7781
"@com_github_google_uuid//:go_default_library",
7882
"@com_github_prysmaticlabs_ethereumapis//eth/v1alpha1:go_default_library",

validator/rpc/accounts_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import (
1111
v2 "github.com/prysmaticlabs/prysm/validator/accounts/v2"
1212
"github.com/prysmaticlabs/prysm/validator/accounts/v2/wallet"
1313
v2keymanager "github.com/prysmaticlabs/prysm/validator/keymanager/v2"
14-
"github.com/prysmaticlabs/prysm/validator/keymanager/v2/direct"
14+
"github.com/prysmaticlabs/prysm/validator/keymanager/v2/derived"
1515
)
1616

1717
func TestServer_CreateAccount(t *testing.T) {
@@ -23,7 +23,7 @@ func TestServer_CreateAccount(t *testing.T) {
2323
w, err := v2.CreateWalletWithKeymanager(ctx, &v2.CreateWalletConfig{
2424
WalletCfg: &wallet.Config{
2525
WalletDir: defaultWalletPath,
26-
KeymanagerKind: v2keymanager.Direct,
26+
KeymanagerKind: v2keymanager.Derived,
2727
WalletPassword: strongPass,
2828
},
2929
SkipMnemonicConfirm: true,
@@ -50,7 +50,7 @@ func TestServer_ListAccounts(t *testing.T) {
5050
w, err := v2.CreateWalletWithKeymanager(ctx, &v2.CreateWalletConfig{
5151
WalletCfg: &wallet.Config{
5252
WalletDir: defaultWalletPath,
53-
KeymanagerKind: v2keymanager.Direct,
53+
KeymanagerKind: v2keymanager.Derived,
5454
WalletPassword: strongPass,
5555
},
5656
SkipMnemonicConfirm: true,
@@ -66,7 +66,7 @@ func TestServer_ListAccounts(t *testing.T) {
6666
numAccounts := 5
6767
keys := make([][]byte, numAccounts)
6868
for i := 0; i < numAccounts; i++ {
69-
key, err := km.(*direct.Keymanager).CreateAccount(ctx)
69+
key, err := km.(*derived.Keymanager).CreateAccount(ctx, false /* log account info */)
7070
require.NoError(t, err)
7171
keys[i] = key
7272
}

0 commit comments

Comments
 (0)