Refuse weak passphrases without explicit flag.

This commit is contained in:
Jim McDonald
2020-10-30 12:30:57 +00:00
parent 4600f2a0d4
commit 508e2eafcb
11 changed files with 120 additions and 3 deletions

View File

@@ -125,6 +125,12 @@ If set, the `--debug` argument will output additional information about the oper
Commands will have an exit status of 0 on success and 1 on failure. The specific definition of success is specified in the help for each command.
## Passphrase strength
`ethdo` will by default not allow creation or export of accounts or wallets with weak passphrases. If a weak pasphrase is used then `ethdo` will refuse to continue.
If a weak passphrase is required, `ethdo` can be supplied with the `--allow-weak-passphrases` option which will force it to accept any passphrase, even if it is considered weak.
## Rules for account passphrases
Account passphrases are used in various places in `ethdo`. Where they are used, the following rules apply:

View File

@@ -21,6 +21,7 @@ import (
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/wealdtech/ethdo/util"
e2wallet "github.com/wealdtech/go-eth2-wallet"
e2wtypes "github.com/wealdtech/go-eth2-wallet-types/v2"
)
@@ -61,8 +62,12 @@ In quiet mode this will return 0 if the account is created successfully, otherwi
outputIf(debug, fmt.Sprintf("Distributed account has %d/%d threshold", viper.GetUint32("signing-threshold"), viper.GetUint32("participants")))
ctx, cancel := context.WithTimeout(context.Background(), viper.GetDuration("timeout"))
defer cancel()
if getOptionalPassphrase() != "" {
assert(util.AcceptablePassphrase(getPassphrase()), "supplied passphrase is weak; use a stronger one or run with the --allow-weak-passphrases flag")
}
account, err = distributedCreator.CreateDistributedAccount(ctx, accountName, viper.GetUint32("participants"), viper.GetUint32("signing-threshold"), []byte(getOptionalPassphrase()))
} else {
assert(util.AcceptablePassphrase(getPassphrase()), "supplied passphrase is weak; use a stronger one or run with the --allow-weak-passphrases flag")
if viper.GetString("path") != "" {
// Want a pathed account
creator, isCreator := wallet.(e2wtypes.WalletPathedAccountCreator)

View File

@@ -20,6 +20,7 @@ import (
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/wealdtech/ethdo/util"
"github.com/wealdtech/go-bytesutil"
e2wallet "github.com/wealdtech/go-eth2-wallet"
e2wtypes "github.com/wealdtech/go-eth2-wallet-types/v2"
@@ -38,8 +39,8 @@ In quiet mode this will return 0 if the account is imported successfully, otherw
Run: func(cmd *cobra.Command, args []string) {
assert(!remote, "account import not available with remote wallets")
assert(viper.GetString("account") != "", "--account is required")
passphrase := getPassphrase()
assert(accountImportKey != "", "--key is required")
assert(util.AcceptablePassphrase(getPassphrase()), "supplied passphrase is weak; use a stronger one or run with the --allow-weak-passphrases flag")
ctx, cancel := context.WithTimeout(context.Background(), viper.GetDuration("timeout"))
defer cancel()
@@ -64,7 +65,7 @@ In quiet mode this will return 0 if the account is imported successfully, otherw
_, accountName, err := e2wallet.WalletAndAccountNames(viper.GetString("account"))
errCheck(err, "Failed to obtain account name")
account, err := w.(e2wtypes.WalletAccountImporter).ImportAccount(ctx, accountName, key, []byte(passphrase))
account, err := w.(e2wtypes.WalletAccountImporter).ImportAccount(ctx, accountName, key, []byte(getPassphrase()))
errCheck(err, "Failed to create account")
pubKey, err := bestPublicKey(account)

View File

@@ -13,7 +13,9 @@
package cmd
import "github.com/spf13/viper"
import (
"github.com/spf13/viper"
)
// getStorePassphrases() fetches the store passphrase supplied by the user.
func getStorePassphrase() string {

View File

@@ -206,6 +206,10 @@ func init() {
if err := viper.BindPFlag("server-ca-cert", RootCmd.PersistentFlags().Lookup("server-ca-cert")); err != nil {
panic(err)
}
RootCmd.PersistentFlags().Bool("allow-weak-passphrases", false, "allow passphrases that use common words, are short, or generally considered weak")
if err := viper.BindPFlag("allow-weak-passphrases", RootCmd.PersistentFlags().Lookup("allow-weak-passphrases")); err != nil {
panic(err)
}
}
// initConfig reads in config file and ENV variables if set.

View File

@@ -24,6 +24,7 @@ import (
"github.com/spf13/cobra"
"github.com/spf13/viper"
bip39 "github.com/tyler-smith/go-bip39"
"github.com/wealdtech/ethdo/util"
distributed "github.com/wealdtech/go-eth2-wallet-distributed"
keystorev4 "github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4"
hd "github.com/wealdtech/go-eth2-wallet-hd/v2"
@@ -58,6 +59,7 @@ In quiet mode this will return 0 if the wallet is created successfully, otherwis
os.Exit(_exitFailure)
}
assert(getWalletPassphrase() != "", "--walletpassphrase is required for hierarchical deterministic wallets")
assert(util.AcceptablePassphrase(getWalletPassphrase()), "supplied passphrase is weak; use a stronger one or run with the --allow-weak-passphrases flag")
err = walletCreateHD(ctx, viper.GetString("wallet"), getWalletPassphrase(), viper.GetString("mnemonic"))
case "distributed":
assert(viper.GetString("mnemonic") == "", "--mnemonic is not allowed with distributed wallets")

View File

@@ -20,6 +20,7 @@ import (
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/wealdtech/ethdo/util"
types "github.com/wealdtech/go-eth2-wallet-types/v2"
)
@@ -40,6 +41,7 @@ In quiet mode this will return 0 if the wallet is able to be exported, otherwise
assert(viper.GetString("remote") == "", "wallet export not available with remote wallets")
assert(viper.GetString("wallet") != "", "--wallet is required")
assert(walletExportPassphrase != "", "--exportpassphrase is required")
assert(util.AcceptablePassphrase(walletExportPassphrase), "supplied passphrase is weak; use a stronger one or run with the --allow-weak-passphrases flag")
wallet, err := walletFromPath(ctx, viper.GetString("wallet"))
errCheck(err, "Failed to access wallet")

2
go.mod
View File

@@ -12,6 +12,7 @@ require (
github.com/magiconair/properties v1.8.4 // indirect
github.com/mitchellh/go-homedir v1.1.0
github.com/mitchellh/mapstructure v1.3.3 // indirect
github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d
github.com/pelletier/go-toml v1.8.1 // indirect
github.com/pkg/errors v0.9.1
github.com/prysmaticlabs/ethereumapis v0.0.0-20201003171600-a72e5f77d233
@@ -24,6 +25,7 @@ require (
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.5
github.com/spf13/viper v1.7.1
github.com/stretchr/testify v1.6.1
github.com/tyler-smith/go-bip39 v1.0.2
github.com/wealdtech/eth2-signer-api v1.6.0
github.com/wealdtech/go-bytesutil v1.1.1

2
go.sum
View File

@@ -241,6 +241,8 @@ github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RR
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d h1:AREM5mwr4u1ORQBMvzfzBgpsctsbQikCVpvC+tX285E=
github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=

31
util/passphrase.go Normal file
View File

@@ -0,0 +1,31 @@
// Copyright © 2020 Weald Technology Trading
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package util
import (
zxcvbn "github.com/nbutton23/zxcvbn-go"
"github.com/spf13/viper"
)
// minPassphraseScore is the minimum acceptable passphrase score.
const minPassphraseScore = 2
// AcceptablePassphrase returns true if the passphrase is acceptable.
func AcceptablePassphrase(passphrase string) bool {
if viper.GetBool("allow-weak-passphrases") {
return true
}
res := zxcvbn.PasswordStrength(passphrase, nil)
return res.Score >= minPassphraseScore
}

60
util/passphrase_test.go Normal file
View File

@@ -0,0 +1,60 @@
// Copyright © 2020 Weald Technology Trading
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package util_test
import (
"testing"
"github.com/spf13/viper"
"github.com/stretchr/testify/require"
"github.com/wealdtech/ethdo/util"
)
func TestAcceptablePassphrase(t *testing.T) {
tests := []struct {
name string
input string
allowWeak bool
expected bool
}{
{
name: "Empty",
input: ``,
expected: false,
},
{
name: "Simple",
input: `password`,
expected: false,
},
{
name: "AllowedWeak",
input: `password`,
allowWeak: true,
expected: true,
},
{
name: "Complex",
input: `Hu[J"yKH{z&-;[]'7T*Dm1:t`,
expected: true,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
viper.Set("allow-weak-passphrases", test.allowWeak)
require.Equal(t, test.expected, util.AcceptablePassphrase(test.input))
})
}
}