consolidate: remove passphrase support for public keys #37
14 changed files with 243 additions and 513 deletions
|
@ -266,11 +266,11 @@ func sweep() error {
|
||||||
sourceOutputs[unspentOutput.Address] = append(sourceAddressOutputs, unspentOutput)
|
sourceOutputs[unspentOutput.Address] = append(sourceAddressOutputs, unspentOutput)
|
||||||
}
|
}
|
||||||
|
|
||||||
var privatePassphrase string
|
var passphrase string
|
||||||
if len(sourceOutputs) != 0 {
|
if len(sourceOutputs) != 0 {
|
||||||
privatePassphrase, err = promptSecret("Wallet private passphrase")
|
passphrase, err = promptSecret("Wallet passphrase")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errContext(err, "failed to read private passphrase")
|
return errContext(err, "failed to read passphrase")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -294,7 +294,7 @@ func sweep() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unlock the wallet, sign the transaction, and immediately lock.
|
// Unlock the wallet, sign the transaction, and immediately lock.
|
||||||
err = rpcClient.WalletPassphrase(privatePassphrase, 60)
|
err = rpcClient.WalletPassphrase(passphrase, 60)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
reportError("Failed to unlock wallet: %v", err)
|
reportError("Failed to unlock wallet: %v", err)
|
||||||
continue
|
continue
|
||||||
|
|
|
@ -265,7 +265,6 @@ func loadConfig() (*config, []string, error) {
|
||||||
ConfigFile: cfgutil.NewExplicitString(defaultConfigFile),
|
ConfigFile: cfgutil.NewExplicitString(defaultConfigFile),
|
||||||
AppDataDir: cfgutil.NewExplicitString(defaultAppDataDir),
|
AppDataDir: cfgutil.NewExplicitString(defaultAppDataDir),
|
||||||
LogDir: defaultLogDir,
|
LogDir: defaultLogDir,
|
||||||
WalletPass: wallet.InsecurePubPassphrase,
|
|
||||||
CAFile: cfgutil.NewExplicitString(""),
|
CAFile: cfgutil.NewExplicitString(""),
|
||||||
RPCKey: cfgutil.NewExplicitString(defaultRPCKeyFile),
|
RPCKey: cfgutil.NewExplicitString(defaultRPCKeyFile),
|
||||||
RPCCert: cfgutil.NewExplicitString(defaultRPCCertFile),
|
RPCCert: cfgutil.NewExplicitString(defaultRPCCertFile),
|
||||||
|
|
|
@ -52,10 +52,10 @@ func provideSeed(reader *bufio.Reader) ([]byte, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ProvidePrivPassphrase is used to prompt for the private passphrase which
|
// ProvidePassphrase is used to prompt for the passphrase which maybe required
|
||||||
// maybe required during upgrades.
|
// during upgrades.
|
||||||
func ProvidePrivPassphrase() ([]byte, error) {
|
func ProvidePassphrase() ([]byte, error) {
|
||||||
prompt := "Enter the private passphrase of your wallet: "
|
prompt := "Enter the passphrase of your wallet: "
|
||||||
for {
|
for {
|
||||||
fmt.Print(prompt)
|
fmt.Print(prompt)
|
||||||
pass, err := terminal.ReadPassword(int(os.Stdin.Fd()))
|
pass, err := terminal.ReadPassword(int(os.Stdin.Fd()))
|
||||||
|
@ -147,10 +147,10 @@ func promptUnixTimestamp(reader *bufio.Reader, prefix string,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// promptPass prompts the user for a passphrase with the given prefix. The
|
// promptPassphrase prompts the user for a passphrase with the given prefix.
|
||||||
// function will ask the user to confirm the passphrase and will repeat the
|
// The function will ask the user to confirm the passphrase and will repeat
|
||||||
// prompts until they enter a matching response.
|
// the prompts until they enter a matching response.
|
||||||
func promptPass(_ *bufio.Reader, prefix string, confirm bool) ([]byte, error) {
|
func promptPassphrase(_ *bufio.Reader, prefix string, confirm bool) ([]byte, error) {
|
||||||
// Prompt the user until they enter a passphrase.
|
// Prompt the user until they enter a passphrase.
|
||||||
prompt := fmt.Sprintf("%s: ", prefix)
|
prompt := fmt.Sprintf("%s: ", prefix)
|
||||||
for {
|
for {
|
||||||
|
@ -191,56 +191,11 @@ func birthday(reader *bufio.Reader) (time.Time, error) {
|
||||||
return promptUnixTimestamp(reader, prompt, "0")
|
return promptUnixTimestamp(reader, prompt, "0")
|
||||||
}
|
}
|
||||||
|
|
||||||
// PrivatePass prompts the user for a private passphrase. The user is prompted
|
// Passphrase prompts the user for a passphrase.
|
||||||
// for a new private passphrase. All prompts are repeated until the user
|
// All prompts are repeated until the user enters a valid response.
|
||||||
// enters a valid response.
|
func Passphrase(reader *bufio.Reader) ([]byte, error) {
|
||||||
func PrivatePass(reader *bufio.Reader) ([]byte, error) {
|
return promptPassphrase(reader, "Enter the passphrase "+
|
||||||
return promptPass(reader, "Enter the private "+
|
"for your new wallet", true)
|
||||||
"passphrase for your new wallet", true)
|
|
||||||
}
|
|
||||||
|
|
||||||
// PublicPass prompts the user whether they want to add an additional layer of
|
|
||||||
// encryption to the wallet. When the user answers yes and there is already a
|
|
||||||
// public passphrase provided via the passed config, it prompts them whether or
|
|
||||||
// not to use that configured passphrase. It will also detect when the same
|
|
||||||
// passphrase is used for the private and public passphrase and prompt the user
|
|
||||||
// if they are sure they want to use the same passphrase for both. Finally, all
|
|
||||||
// prompts are repeated until the user enters a valid response.
|
|
||||||
func PublicPass(reader *bufio.Reader, privPass []byte,
|
|
||||||
defaultPubPassphrase, configPubPassphrase []byte) ([]byte, error) {
|
|
||||||
|
|
||||||
pubPass := defaultPubPassphrase
|
|
||||||
usePubPass, err := promptListBool(reader, "Do you want "+
|
|
||||||
"to add an additional layer of encryption for public "+
|
|
||||||
"data?", "no")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if !usePubPass {
|
|
||||||
return pubPass, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if !bytes.Equal(configPubPassphrase, pubPass) {
|
|
||||||
useExisting, err := promptListBool(reader, "Use the "+
|
|
||||||
"existing configured public passphrase for encryption "+
|
|
||||||
"of public data?", "no")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if useExisting {
|
|
||||||
return configPubPassphrase, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pubPass, err = provideSeed(reader)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println("NOTE: Use the --walletpass option to configure your " +
|
|
||||||
"public passphrase.")
|
|
||||||
return pubPass, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Seed prompts the user whether they want to use an existing wallet generation
|
// Seed prompts the user whether they want to use an existing wallet generation
|
||||||
|
|
|
@ -1,30 +0,0 @@
|
||||||
// Copyright (c) 2015-2021 The btcsuite developers
|
|
||||||
// Use of this source code is governed by an ISC
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package prompt
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
func ProvideSeed() ([]byte, error) {
|
|
||||||
return nil, fmt.Errorf("prompt not supported in WebAssembly")
|
|
||||||
}
|
|
||||||
|
|
||||||
func ProvidePrivPassphrase() ([]byte, error) {
|
|
||||||
return nil, fmt.Errorf("prompt not supported in WebAssembly")
|
|
||||||
}
|
|
||||||
|
|
||||||
func PrivatePass(_ *bufio.Reader) ([]byte, error) {
|
|
||||||
return nil, fmt.Errorf("prompt not supported in WebAssembly")
|
|
||||||
}
|
|
||||||
|
|
||||||
func PublicPass(_ *bufio.Reader, _, _, _ []byte) ([]byte, error) {
|
|
||||||
return nil, fmt.Errorf("prompt not supported in WebAssembly")
|
|
||||||
}
|
|
||||||
|
|
||||||
func Seed(_ *bufio.Reader) ([]byte, error) {
|
|
||||||
return nil, fmt.Errorf("prompt not supported in WebAssembly")
|
|
||||||
}
|
|
|
@ -94,7 +94,7 @@ func walletMain() error {
|
||||||
if !cfg.NoInitialLoad {
|
if !cfg.NoInitialLoad {
|
||||||
// Load the wallet database. It must have been created already
|
// Load the wallet database. It must have been created already
|
||||||
// or this will return an appropriate error.
|
// or this will return an appropriate error.
|
||||||
_, err = loader.OpenExistingWallet([]byte(cfg.WalletPass), true)
|
_, err = loader.OpenExistingWallet(true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -2154,7 +2154,7 @@ func walletPassphrase(icmd interface{}, w *wallet.Wallet) (interface{}, error) {
|
||||||
func walletPassphraseChange(icmd interface{}, w *wallet.Wallet) (interface{}, error) {
|
func walletPassphraseChange(icmd interface{}, w *wallet.Wallet) (interface{}, error) {
|
||||||
cmd := icmd.(*btcjson.WalletPassphraseChangeCmd)
|
cmd := icmd.(*btcjson.WalletPassphraseChangeCmd)
|
||||||
|
|
||||||
err := w.ChangePrivatePassphrase([]byte(cmd.OldPassphrase),
|
err := w.ChangePassphrase([]byte(cmd.OldPassphrase),
|
||||||
[]byte(cmd.NewPassphrase))
|
[]byte(cmd.NewPassphrase))
|
||||||
if waddrmgr.IsError(err, waddrmgr.ErrWrongPassphrase) {
|
if waddrmgr.IsError(err, waddrmgr.ErrWrongPassphrase) {
|
||||||
return nil, &btcjson.RPCError{
|
return nil, &btcjson.RPCError{
|
||||||
|
|
|
@ -29,9 +29,7 @@ var (
|
||||||
|
|
||||||
rootKey, _ = hdkeychain.NewMaster(seed, &chaincfg.MainNetParams)
|
rootKey, _ = hdkeychain.NewMaster(seed, &chaincfg.MainNetParams)
|
||||||
|
|
||||||
pubPassphrase = []byte("_DJr{fL4H0O}*-0\n:V1izc)(6BomK")
|
passphrase = []byte("81lUHXnOMZ@?XXd7O9xyDIWIbXX-lj")
|
||||||
privPassphrase = []byte("81lUHXnOMZ@?XXd7O9xyDIWIbXX-lj")
|
|
||||||
pubPassphrase2 = []byte("-0NV4P~VSJBWbunw}%<Z]fuGpbN[ZI")
|
|
||||||
privPassphrase2 = []byte("~{<]08%6!-?2s<$(8$8:f(5[4/!/{Y")
|
privPassphrase2 = []byte("~{<]08%6!-?2s<$(8$8:f(5[4/!/{Y")
|
||||||
|
|
||||||
// fastScrypt are parameters used throughout the tests to speed up the
|
// fastScrypt are parameters used throughout the tests to speed up the
|
||||||
|
@ -287,13 +285,13 @@ func setupManager(t *testing.T) (tearDownFunc func(), db walletdb.DB, mgr *Manag
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = Create(
|
err = Create(
|
||||||
ns, rootKey, pubPassphrase, privPassphrase,
|
ns, rootKey, passphrase, &chaincfg.MainNetParams,
|
||||||
&chaincfg.MainNetParams, fastScrypt, time.Time{},
|
fastScrypt, time.Time{},
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
mgr, err = Open(ns, pubPassphrase, &chaincfg.MainNetParams)
|
mgr, err = Open(ns, &chaincfg.MainNetParams)
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -34,7 +34,7 @@ var (
|
||||||
|
|
||||||
// ObtainUserInputFunc is a function that reads a user input and returns it as
|
// ObtainUserInputFunc is a function that reads a user input and returns it as
|
||||||
// a byte stream. It is used to accept data required during upgrades, for e.g.
|
// a byte stream. It is used to accept data required during upgrades, for e.g.
|
||||||
// wallet seed and private passphrase.
|
// wallet seed and passphrase.
|
||||||
type ObtainUserInputFunc func() ([]byte, error)
|
type ObtainUserInputFunc func() ([]byte, error)
|
||||||
|
|
||||||
// maybeConvertDbError converts the passed error to a ManagerError with an
|
// maybeConvertDbError converts the passed error to a ManagerError with an
|
||||||
|
|
|
@ -81,7 +81,7 @@ const (
|
||||||
InternalBranch uint32 = 1
|
InternalBranch uint32 = 1
|
||||||
|
|
||||||
// saltSize is the number of bytes of the salt used when hashing
|
// saltSize is the number of bytes of the salt used when hashing
|
||||||
// private passphrases.
|
// passphrases.
|
||||||
saltSize = 32
|
saltSize = 32
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -113,11 +113,11 @@ type OpenCallbacks struct {
|
||||||
// from the user (or any other mechanism the caller deems fit).
|
// from the user (or any other mechanism the caller deems fit).
|
||||||
ObtainSeed ObtainUserInputFunc
|
ObtainSeed ObtainUserInputFunc
|
||||||
|
|
||||||
// ObtainPrivatePass is a callback function that is potentially invoked
|
// ObtainPassphrase is a callback function that is potentially invoked
|
||||||
// during upgrades. It is intended to be used to request the wallet
|
// during upgrades. It is intended to be used to request the wallet
|
||||||
// private passphrase from the user (or any other mechanism the caller
|
// passphrase from the user (or any other mechanism the caller deems
|
||||||
// deems fit).
|
// fit).
|
||||||
ObtainPrivatePass ObtainUserInputFunc
|
ObtainPassphrase ObtainUserInputFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
// DefaultScryptOptions is the default options used with scrypt.
|
// DefaultScryptOptions is the default options used with scrypt.
|
||||||
|
@ -373,11 +373,11 @@ type Manager struct {
|
||||||
cryptoKeyScriptEncrypted []byte
|
cryptoKeyScriptEncrypted []byte
|
||||||
cryptoKeyScript EncryptorDecryptor
|
cryptoKeyScript EncryptorDecryptor
|
||||||
|
|
||||||
// privPassphraseSalt and hashedPrivPassphrase allow for the secure
|
// pssphraseSalt and hashedPassphrase allow for the secure detection
|
||||||
// detection of a correct passphrase on manager unlock when the
|
// of a correct passphrase on manager unlock when the manager is already
|
||||||
// manager is already unlocked. The hash is zeroed each lock.
|
// unlocked. The hash is zeroed each lock.
|
||||||
privPassphraseSalt [saltSize]byte
|
passphraseSalt [saltSize]byte
|
||||||
hashedPrivPassphrase [sha512.Size]byte
|
hashedPassphrase [sha512.Size]byte
|
||||||
}
|
}
|
||||||
|
|
||||||
// lock performs a best try effort to remove and zero all secret keys associated
|
// lock performs a best try effort to remove and zero all secret keys associated
|
||||||
|
@ -413,7 +413,7 @@ func (m *Manager) lock() {
|
||||||
m.masterKeyPriv.Zero()
|
m.masterKeyPriv.Zero()
|
||||||
|
|
||||||
// Zero the hashed passphrase.
|
// Zero the hashed passphrase.
|
||||||
zero.Bytea64(&m.hashedPrivPassphrase)
|
zero.Bytea64(&m.hashedPassphrase)
|
||||||
|
|
||||||
// NOTE: m.cryptoKeyPub is intentionally not cleared here as the address
|
// NOTE: m.cryptoKeyPub is intentionally not cleared here as the address
|
||||||
// manager needs to be able to continue to read and decrypt public data
|
// manager needs to be able to continue to read and decrypt public data
|
||||||
|
@ -825,13 +825,12 @@ func (m *Manager) ChainParams() *chaincfg.Params {
|
||||||
return m.chainParams
|
return m.chainParams
|
||||||
}
|
}
|
||||||
|
|
||||||
// ChangePassphrase changes either the public or private passphrase to the
|
// ChangePassphrase changes passphrase to the provided value. The new
|
||||||
// provided value depending on the private flag. The new passphrase keys are
|
// passphrase keys are derived using the scrypt parameters in the options, so
|
||||||
// derived using the scrypt parameters in the options, so changing the
|
// changing the passphrase may be used to bump the computational difficulty
|
||||||
// passphrase may be used to bump the computational difficulty needed to brute
|
// needed to brute force the passphrase.
|
||||||
// force the passphrase.
|
|
||||||
func (m *Manager) ChangePassphrase(ns walletdb.ReadWriteBucket, oldPassphrase,
|
func (m *Manager) ChangePassphrase(ns walletdb.ReadWriteBucket, oldPassphrase,
|
||||||
newPassphrase []byte, private bool, config *ScryptOptions) error {
|
newPassphrase []byte, config *ScryptOptions) error {
|
||||||
|
|
||||||
m.mtx.Lock()
|
m.mtx.Lock()
|
||||||
defer m.mtx.Unlock()
|
defer m.mtx.Unlock()
|
||||||
|
@ -842,13 +841,9 @@ func (m *Manager) ChangePassphrase(ns walletdb.ReadWriteBucket, oldPassphrase,
|
||||||
// cleared when done to avoid leaving a copy in memory.
|
// cleared when done to avoid leaving a copy in memory.
|
||||||
var keyName string
|
var keyName string
|
||||||
secretKey := snacl.SecretKey{Key: &snacl.CryptoKey{}}
|
secretKey := snacl.SecretKey{Key: &snacl.CryptoKey{}}
|
||||||
if private {
|
|
||||||
keyName = "private"
|
keyName = "private"
|
||||||
secretKey.Parameters = m.masterKeyPriv.Parameters
|
secretKey.Parameters = m.masterKeyPriv.Parameters
|
||||||
} else {
|
|
||||||
keyName = "public"
|
|
||||||
secretKey.Parameters = m.masterKeyPub.Parameters
|
|
||||||
}
|
|
||||||
if err := secretKey.DeriveKey(&oldPassphrase); err != nil {
|
if err := secretKey.DeriveKey(&oldPassphrase); err != nil {
|
||||||
if err == snacl.ErrInvalidPassword {
|
if err == snacl.ErrInvalidPassword {
|
||||||
str := fmt.Sprintf("invalid passphrase for %s master "+
|
str := fmt.Sprintf("invalid passphrase for %s master "+
|
||||||
|
@ -870,7 +865,6 @@ func (m *Manager) ChangePassphrase(ns walletdb.ReadWriteBucket, oldPassphrase,
|
||||||
}
|
}
|
||||||
newKeyParams := newMasterKey.Marshal()
|
newKeyParams := newMasterKey.Marshal()
|
||||||
|
|
||||||
if private {
|
|
||||||
// Technically, the locked state could be checked here to only
|
// Technically, the locked state could be checked here to only
|
||||||
// do the decrypts when the address manager is locked as the
|
// do the decrypts when the address manager is locked as the
|
||||||
// clear text keys are already available in memory when it is
|
// clear text keys are already available in memory when it is
|
||||||
|
@ -881,7 +875,7 @@ func (m *Manager) ChangePassphrase(ns walletdb.ReadWriteBucket, oldPassphrase,
|
||||||
// Create a new salt that will be used for hashing the new
|
// Create a new salt that will be used for hashing the new
|
||||||
// passphrase each unlock.
|
// passphrase each unlock.
|
||||||
var passphraseSalt [saltSize]byte
|
var passphraseSalt [saltSize]byte
|
||||||
_, err := rand.Read(passphraseSalt[:])
|
_, err = rand.Read(passphraseSalt[:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
str := "failed to read random source for passhprase salt"
|
str := "failed to read random source for passhprase salt"
|
||||||
return managerError(ErrCrypto, str, err)
|
return managerError(ErrCrypto, str, err)
|
||||||
|
@ -947,34 +941,8 @@ func (m *Manager) ChangePassphrase(ns walletdb.ReadWriteBucket, oldPassphrase,
|
||||||
copy(m.cryptoKeyScriptEncrypted, encScript)
|
copy(m.cryptoKeyScriptEncrypted, encScript)
|
||||||
m.masterKeyPriv.Zero() // Clear the old key.
|
m.masterKeyPriv.Zero() // Clear the old key.
|
||||||
m.masterKeyPriv = newMasterKey
|
m.masterKeyPriv = newMasterKey
|
||||||
m.privPassphraseSalt = passphraseSalt
|
m.passphraseSalt = passphraseSalt
|
||||||
m.hashedPrivPassphrase = hashedPassphrase
|
m.hashedPassphrase = hashedPassphrase
|
||||||
} else {
|
|
||||||
// Re-encrypt the crypto public key using the new master public
|
|
||||||
// key.
|
|
||||||
encryptedPub, err := newMasterKey.Encrypt(m.cryptoKeyPub.Bytes())
|
|
||||||
if err != nil {
|
|
||||||
str := "failed to encrypt crypto public key"
|
|
||||||
return managerError(ErrCrypto, str, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save the new keys and params to the the db in a single
|
|
||||||
// transaction.
|
|
||||||
err = putCryptoKeys(ns, encryptedPub, nil, nil)
|
|
||||||
if err != nil {
|
|
||||||
return maybeConvertDbError(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = putMasterKeyParams(ns, newKeyParams, nil)
|
|
||||||
if err != nil {
|
|
||||||
return maybeConvertDbError(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now that the db has been successfully updated, clear the old
|
|
||||||
// key and set the new one.
|
|
||||||
m.masterKeyPub.Zero()
|
|
||||||
m.masterKeyPub = newMasterKey
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -1027,11 +995,10 @@ func (m *Manager) Unlock(ns walletdb.ReadBucket, passphrase []byte) error {
|
||||||
// Avoid actually unlocking if the manager is already unlocked
|
// Avoid actually unlocking if the manager is already unlocked
|
||||||
// and the passphrases match.
|
// and the passphrases match.
|
||||||
if !m.locked {
|
if !m.locked {
|
||||||
saltedPassphrase := append(m.privPassphraseSalt[:],
|
saltedPassphrase := append(m.passphraseSalt[:], passphrase...)
|
||||||
passphrase...)
|
|
||||||
hashedPassphrase := sha512.Sum512(saltedPassphrase)
|
hashedPassphrase := sha512.Sum512(saltedPassphrase)
|
||||||
zero.Bytes(saltedPassphrase)
|
zero.Bytes(saltedPassphrase)
|
||||||
if hashedPassphrase != m.hashedPrivPassphrase {
|
if hashedPassphrase != m.hashedPassphrase {
|
||||||
m.lock()
|
m.lock()
|
||||||
str := "invalid passphrase for master private key"
|
str := "invalid passphrase for master private key"
|
||||||
return managerError(ErrWrongPassphrase, str, nil)
|
return managerError(ErrWrongPassphrase, str, nil)
|
||||||
|
@ -1126,8 +1093,8 @@ func (m *Manager) Unlock(ns walletdb.ReadBucket, passphrase []byte) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
m.locked = false
|
m.locked = false
|
||||||
saltedPassphrase := append(m.privPassphraseSalt[:], passphrase...)
|
saltedPassphrase := append(m.passphraseSalt[:], passphrase...)
|
||||||
m.hashedPrivPassphrase = sha512.Sum512(saltedPassphrase)
|
m.hashedPassphrase = sha512.Sum512(saltedPassphrase)
|
||||||
zero.Bytes(saltedPassphrase)
|
zero.Bytes(saltedPassphrase)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -1235,7 +1202,7 @@ func (m *Manager) Decrypt(keyType CryptoKeyType, in []byte) ([]byte, error) {
|
||||||
func newManager(chainParams *chaincfg.Params, masterKeyPub *snacl.SecretKey,
|
func newManager(chainParams *chaincfg.Params, masterKeyPub *snacl.SecretKey,
|
||||||
masterKeyPriv *snacl.SecretKey, cryptoKeyPub EncryptorDecryptor,
|
masterKeyPriv *snacl.SecretKey, cryptoKeyPub EncryptorDecryptor,
|
||||||
cryptoKeyPrivEncrypted, cryptoKeyScriptEncrypted []byte, syncInfo *syncState,
|
cryptoKeyPrivEncrypted, cryptoKeyScriptEncrypted []byte, syncInfo *syncState,
|
||||||
birthday time.Time, privPassphraseSalt [saltSize]byte,
|
birthday time.Time, passphraseSalt [saltSize]byte,
|
||||||
scopedManagers map[KeyScope]*ScopedKeyManager) *Manager {
|
scopedManagers map[KeyScope]*ScopedKeyManager) *Manager {
|
||||||
|
|
||||||
m := &Manager{
|
m := &Manager{
|
||||||
|
@ -1250,7 +1217,7 @@ func newManager(chainParams *chaincfg.Params, masterKeyPub *snacl.SecretKey,
|
||||||
cryptoKeyPriv: &cryptoKey{},
|
cryptoKeyPriv: &cryptoKey{},
|
||||||
cryptoKeyScriptEncrypted: cryptoKeyScriptEncrypted,
|
cryptoKeyScriptEncrypted: cryptoKeyScriptEncrypted,
|
||||||
cryptoKeyScript: &cryptoKey{},
|
cryptoKeyScript: &cryptoKey{},
|
||||||
privPassphraseSalt: privPassphraseSalt,
|
passphraseSalt: passphraseSalt,
|
||||||
scopedManagers: scopedManagers,
|
scopedManagers: scopedManagers,
|
||||||
externalAddrSchemas: make(map[AddressType][]KeyScope),
|
externalAddrSchemas: make(map[AddressType][]KeyScope),
|
||||||
internalAddrSchemas: make(map[AddressType][]KeyScope),
|
internalAddrSchemas: make(map[AddressType][]KeyScope),
|
||||||
|
@ -1361,10 +1328,9 @@ func checkBranchKeys(acctKey *hdkeychain.ExtendedKey) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// loadManager returns a new address manager that results from loading it from
|
// loadManager returns a new address manager that results from loading it from
|
||||||
// the passed opened database. The public passphrase is required to decrypt
|
// the passed opened database.
|
||||||
// the public keys.
|
func loadManager(ns walletdb.ReadBucket, chainParams *chaincfg.Params) (
|
||||||
func loadManager(ns walletdb.ReadBucket, pubPassphrase []byte,
|
*Manager, error) {
|
||||||
chainParams *chaincfg.Params) (*Manager, error) {
|
|
||||||
|
|
||||||
// Verify the version is neither too old or too new.
|
// Verify the version is neither too old or too new.
|
||||||
version, err := fetchManagerVersion(ns)
|
version, err := fetchManagerVersion(ns)
|
||||||
|
@ -1381,7 +1347,7 @@ func loadManager(ns walletdb.ReadBucket, pubPassphrase []byte,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load the master key params from the db.
|
// Load the master key params from the db.
|
||||||
masterKeyPubParams, masterKeyPrivParams, err := fetchMasterKeyParams(ns)
|
masterKeyPubParams, masterKeyparams, err := fetchMasterKeyParams(ns)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, maybeConvertDbError(err)
|
return nil, maybeConvertDbError(err)
|
||||||
}
|
}
|
||||||
|
@ -1410,7 +1376,7 @@ func loadManager(ns walletdb.ReadBucket, pubPassphrase []byte,
|
||||||
// Set the master private key params, but don't derive it now since the
|
// Set the master private key params, but don't derive it now since the
|
||||||
// manager starts off locked.
|
// manager starts off locked.
|
||||||
var masterKeyPriv snacl.SecretKey
|
var masterKeyPriv snacl.SecretKey
|
||||||
err = masterKeyPriv.Unmarshal(masterKeyPrivParams)
|
err = masterKeyPriv.Unmarshal(masterKeyparams)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
str := "failed to unmarshal master private key"
|
str := "failed to unmarshal master private key"
|
||||||
return nil, managerError(ErrCrypto, str, err)
|
return nil, managerError(ErrCrypto, str, err)
|
||||||
|
@ -1423,6 +1389,8 @@ func loadManager(ns walletdb.ReadBucket, pubPassphrase []byte,
|
||||||
str := "failed to unmarshal master public key"
|
str := "failed to unmarshal master public key"
|
||||||
return nil, managerError(ErrCrypto, str, err)
|
return nil, managerError(ErrCrypto, str, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pubPassphrase := []byte("public") // Hardcoded salt.
|
||||||
if err := masterKeyPub.DeriveKey(&pubPassphrase); err != nil {
|
if err := masterKeyPub.DeriveKey(&pubPassphrase); err != nil {
|
||||||
str := "invalid passphrase for master public key"
|
str := "invalid passphrase for master public key"
|
||||||
return nil, managerError(ErrWrongPassphrase, str, nil)
|
return nil, managerError(ErrWrongPassphrase, str, nil)
|
||||||
|
@ -1441,9 +1409,9 @@ func loadManager(ns walletdb.ReadBucket, pubPassphrase []byte,
|
||||||
// Create the sync state struct.
|
// Create the sync state struct.
|
||||||
syncInfo := newSyncState(startBlock, syncedTo)
|
syncInfo := newSyncState(startBlock, syncedTo)
|
||||||
|
|
||||||
// Generate private passphrase salt.
|
// Generate passphrase salt.
|
||||||
var privPassphraseSalt [saltSize]byte
|
var passphraseSalt [saltSize]byte
|
||||||
_, err = rand.Read(privPassphraseSalt[:])
|
_, err = rand.Read(passphraseSalt[:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
str := "failed to read random source for passphrase salt"
|
str := "failed to read random source for passphrase salt"
|
||||||
return nil, managerError(ErrCrypto, str, err)
|
return nil, managerError(ErrCrypto, str, err)
|
||||||
|
@ -1480,7 +1448,7 @@ func loadManager(ns walletdb.ReadBucket, pubPassphrase []byte,
|
||||||
mgr := newManager(
|
mgr := newManager(
|
||||||
chainParams, &masterKeyPub, &masterKeyPriv,
|
chainParams, &masterKeyPub, &masterKeyPriv,
|
||||||
cryptoKeyPub, cryptoKeyPrivEnc, cryptoKeyScriptEnc, syncInfo,
|
cryptoKeyPub, cryptoKeyPrivEnc, cryptoKeyScriptEnc, syncInfo,
|
||||||
birthday, privPassphraseSalt, scopedManagers)
|
birthday, passphraseSalt, scopedManagers)
|
||||||
|
|
||||||
for _, scopedManager := range scopedManagers {
|
for _, scopedManager := range scopedManagers {
|
||||||
scopedManager.rootManager = mgr
|
scopedManager.rootManager = mgr
|
||||||
|
@ -1489,18 +1457,15 @@ func loadManager(ns walletdb.ReadBucket, pubPassphrase []byte,
|
||||||
return mgr, nil
|
return mgr, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open loads an existing address manager from the given namespace. The public
|
// Open loads an existing address manager from the given namespace.
|
||||||
// passphrase is required to decrypt the public keys used to protect the public
|
|
||||||
// information such as addresses. This is important since access to BIP0032
|
|
||||||
// extended keys means it is possible to generate all future addresses.
|
|
||||||
//
|
//
|
||||||
// If a config structure is passed to the function, that configuration will
|
// If a config structure is passed to the function, that configuration will
|
||||||
// override the defaults.
|
// override the defaults.
|
||||||
//
|
//
|
||||||
// A ManagerError with an error code of ErrNoExist will be returned if the
|
// A ManagerError with an error code of ErrNoExist will be returned if the
|
||||||
// passed manager does not exist in the specified namespace.
|
// passed manager does not exist in the specified namespace.
|
||||||
func Open(ns walletdb.ReadBucket, pubPassphrase []byte,
|
func Open(ns walletdb.ReadBucket, chainParams *chaincfg.Params) (
|
||||||
chainParams *chaincfg.Params) (*Manager, error) {
|
*Manager, error) {
|
||||||
|
|
||||||
// Return an error if the manager has NOT already been created in the
|
// Return an error if the manager has NOT already been created in the
|
||||||
// given database namespace.
|
// given database namespace.
|
||||||
|
@ -1510,7 +1475,7 @@ func Open(ns walletdb.ReadBucket, pubPassphrase []byte,
|
||||||
return nil, managerError(ErrNoExist, str, nil)
|
return nil, managerError(ErrNoExist, str, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
return loadManager(ns, pubPassphrase, chainParams)
|
return loadManager(ns, chainParams)
|
||||||
}
|
}
|
||||||
|
|
||||||
// createManagerKeyScope creates a new key scoped for a target manager's scope.
|
// createManagerKeyScope creates a new key scoped for a target manager's scope.
|
||||||
|
@ -1622,12 +1587,10 @@ func createManagerKeyScope(ns walletdb.ReadWriteBucket,
|
||||||
// derived. This allows all chained addresses in the address manager
|
// derived. This allows all chained addresses in the address manager
|
||||||
// to be recovered by using the same seed.
|
// to be recovered by using the same seed.
|
||||||
//
|
//
|
||||||
// All private and public keys and information are protected by secret
|
// All private keys and information are protected by secret keys derived
|
||||||
// keys derived from the provided private and public passphrases. The
|
// from the provided passphrase.
|
||||||
// public passphrase is required on subsequent opens of the address
|
// The passphrase is required to unlock the address manager in order to gain
|
||||||
// manager, and the private passphrase is required to unlock the
|
// access to any private keys and information.
|
||||||
// address manager in order to gain access to any private keys and
|
|
||||||
// information.
|
|
||||||
//
|
//
|
||||||
// If a config structure is passed to the function, that configuration
|
// If a config structure is passed to the function, that configuration
|
||||||
// will override the defaults.
|
// will override the defaults.
|
||||||
|
@ -1636,9 +1599,8 @@ func createManagerKeyScope(ns walletdb.ReadWriteBucket,
|
||||||
// returned the address manager already exists in the specified
|
// returned the address manager already exists in the specified
|
||||||
// namespace.
|
// namespace.
|
||||||
func Create(ns walletdb.ReadWriteBucket, rootKey *hdkeychain.ExtendedKey,
|
func Create(ns walletdb.ReadWriteBucket, rootKey *hdkeychain.ExtendedKey,
|
||||||
pubPassphrase, privPassphrase []byte,
|
passphrase []byte, chainParams *chaincfg.Params,
|
||||||
chainParams *chaincfg.Params, config *ScryptOptions,
|
config *ScryptOptions, birthday time.Time) error {
|
||||||
birthday time.Time) error {
|
|
||||||
|
|
||||||
// Return an error if the manager has already been created in
|
// Return an error if the manager has already been created in
|
||||||
// the given database namespace.
|
// the given database namespace.
|
||||||
|
@ -1647,9 +1609,9 @@ func Create(ns walletdb.ReadWriteBucket, rootKey *hdkeychain.ExtendedKey,
|
||||||
return managerError(ErrAlreadyExists, errAlreadyExists, nil)
|
return managerError(ErrAlreadyExists, errAlreadyExists, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure the private passphrase is not empty.
|
// Ensure the passphrase is not empty.
|
||||||
if len(privPassphrase) == 0 {
|
if len(passphrase) == 0 {
|
||||||
str := "private passphrase may not be empty"
|
str := "passphrase may not be empty"
|
||||||
return managerError(ErrEmptyPassphrase, str, nil)
|
return managerError(ErrEmptyPassphrase, str, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1664,6 +1626,7 @@ func Create(ns walletdb.ReadWriteBucket, rootKey *hdkeychain.ExtendedKey,
|
||||||
|
|
||||||
// Generate new master keys. These master keys are used to protect the
|
// Generate new master keys. These master keys are used to protect the
|
||||||
// crypto keys that will be generated next.
|
// crypto keys that will be generated next.
|
||||||
|
pubPassphrase := []byte("public") // Hardcoded salt.
|
||||||
masterKeyPub, err := newSecretKey(&pubPassphrase, config)
|
masterKeyPub, err := newSecretKey(&pubPassphrase, config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
str := "failed to master public key"
|
str := "failed to master public key"
|
||||||
|
@ -1699,22 +1662,22 @@ func Create(ns walletdb.ReadWriteBucket, rootKey *hdkeychain.ExtendedKey,
|
||||||
|
|
||||||
pubParams := masterKeyPub.Marshal()
|
pubParams := masterKeyPub.Marshal()
|
||||||
|
|
||||||
var privParams []byte
|
var params []byte
|
||||||
var masterKeyPriv *snacl.SecretKey
|
var masterKeyPriv *snacl.SecretKey
|
||||||
var cryptoKeyPrivEnc []byte
|
var cryptoKeyPrivEnc []byte
|
||||||
var cryptoKeyScriptEnc []byte
|
var cryptoKeyScriptEnc []byte
|
||||||
masterKeyPriv, err = newSecretKey(&privPassphrase, config)
|
masterKeyPriv, err = newSecretKey(&passphrase, config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
str := "failed to master private key"
|
str := "failed to master private key"
|
||||||
return managerError(ErrCrypto, str, err)
|
return managerError(ErrCrypto, str, err)
|
||||||
}
|
}
|
||||||
defer masterKeyPriv.Zero()
|
defer masterKeyPriv.Zero()
|
||||||
|
|
||||||
// Generate the private passphrase salt. This is used when
|
// Generate the passphrase salt. This is used when hashing passwords
|
||||||
// hashing passwords to detect whether an unlock can be
|
// to detect whether an unlock can be avoided when the manager is
|
||||||
// avoided when the manager is already unlocked.
|
// already unlocked.
|
||||||
var privPassphraseSalt [saltSize]byte
|
var passphraseSalt [saltSize]byte
|
||||||
_, err = rand.Read(privPassphraseSalt[:])
|
_, err = rand.Read(passphraseSalt[:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
str := "failed to read random source for passphrase salt"
|
str := "failed to read random source for passphrase salt"
|
||||||
return managerError(ErrCrypto, str, err)
|
return managerError(ErrCrypto, str, err)
|
||||||
|
@ -1786,10 +1749,10 @@ func Create(ns walletdb.ReadWriteBucket, rootKey *hdkeychain.ExtendedKey,
|
||||||
return maybeConvertDbError(err)
|
return maybeConvertDbError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
privParams = masterKeyPriv.Marshal()
|
params = masterKeyPriv.Marshal()
|
||||||
|
|
||||||
// Save the master key params to the database.
|
// Save the master key params to the database.
|
||||||
err = putMasterKeyParams(ns, pubParams, privParams)
|
err = putMasterKeyParams(ns, pubParams, params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return maybeConvertDbError(err)
|
return maybeConvertDbError(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -431,7 +431,7 @@ func testExternalAddresses(tc *testContext) bool {
|
||||||
// private information is valid as well.
|
// private information is valid as well.
|
||||||
err := walletdb.View(tc.db, func(tx walletdb.ReadTx) error {
|
err := walletdb.View(tc.db, func(tx walletdb.ReadTx) error {
|
||||||
ns := tx.ReadBucket(waddrmgrNamespaceKey)
|
ns := tx.ReadBucket(waddrmgrNamespaceKey)
|
||||||
return tc.rootManager.Unlock(ns, privPassphrase)
|
return tc.rootManager.Unlock(ns, passphrase)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tc.t.Errorf("Unlock: unexpected error: %v", err)
|
tc.t.Errorf("Unlock: unexpected error: %v", err)
|
||||||
|
@ -463,7 +463,7 @@ func testInternalAddresses(tc *testContext) bool {
|
||||||
// private information is valid as well.
|
// private information is valid as well.
|
||||||
err := walletdb.View(tc.db, func(tx walletdb.ReadTx) error {
|
err := walletdb.View(tc.db, func(tx walletdb.ReadTx) error {
|
||||||
ns := tx.ReadBucket(waddrmgrNamespaceKey)
|
ns := tx.ReadBucket(waddrmgrNamespaceKey)
|
||||||
return tc.rootManager.Unlock(ns, privPassphrase)
|
return tc.rootManager.Unlock(ns, passphrase)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tc.t.Errorf("Unlock: unexpected error: %v", err)
|
tc.t.Errorf("Unlock: unexpected error: %v", err)
|
||||||
|
@ -603,7 +603,7 @@ func testLocking(tc *testContext) bool {
|
||||||
// unexpected errors and the manager properly reports it is unlocked.
|
// unexpected errors and the manager properly reports it is unlocked.
|
||||||
err = walletdb.View(tc.db, func(tx walletdb.ReadTx) error {
|
err = walletdb.View(tc.db, func(tx walletdb.ReadTx) error {
|
||||||
ns := tx.ReadBucket(waddrmgrNamespaceKey)
|
ns := tx.ReadBucket(waddrmgrNamespaceKey)
|
||||||
return tc.rootManager.Unlock(ns, privPassphrase)
|
return tc.rootManager.Unlock(ns, passphrase)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tc.t.Errorf("Unlock: unexpected error: %v", err)
|
tc.t.Errorf("Unlock: unexpected error: %v", err)
|
||||||
|
@ -617,7 +617,7 @@ func testLocking(tc *testContext) bool {
|
||||||
// Unlocking the manager again is allowed.
|
// Unlocking the manager again is allowed.
|
||||||
err = walletdb.View(tc.db, func(tx walletdb.ReadTx) error {
|
err = walletdb.View(tc.db, func(tx walletdb.ReadTx) error {
|
||||||
ns := tx.ReadBucket(waddrmgrNamespaceKey)
|
ns := tx.ReadBucket(waddrmgrNamespaceKey)
|
||||||
return tc.rootManager.Unlock(ns, privPassphrase)
|
return tc.rootManager.Unlock(ns, passphrase)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tc.t.Errorf("Unlock: unexpected error: %v", err)
|
tc.t.Errorf("Unlock: unexpected error: %v", err)
|
||||||
|
@ -696,7 +696,7 @@ func testImportPrivateKey(tc *testContext) bool {
|
||||||
// The manager must be unlocked to import a private key.
|
// The manager must be unlocked to import a private key.
|
||||||
err := walletdb.View(tc.db, func(tx walletdb.ReadTx) error {
|
err := walletdb.View(tc.db, func(tx walletdb.ReadTx) error {
|
||||||
ns := tx.ReadBucket(waddrmgrNamespaceKey)
|
ns := tx.ReadBucket(waddrmgrNamespaceKey)
|
||||||
return tc.rootManager.Unlock(ns, privPassphrase)
|
return tc.rootManager.Unlock(ns, passphrase)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tc.t.Errorf("Unlock: unexpected error: %v", err)
|
tc.t.Errorf("Unlock: unexpected error: %v", err)
|
||||||
|
@ -913,7 +913,7 @@ func testImportScript(tc *testContext) bool {
|
||||||
// testing private data.
|
// testing private data.
|
||||||
err := walletdb.View(tc.db, func(tx walletdb.ReadTx) error {
|
err := walletdb.View(tc.db, func(tx walletdb.ReadTx) error {
|
||||||
ns := tx.ReadBucket(waddrmgrNamespaceKey)
|
ns := tx.ReadBucket(waddrmgrNamespaceKey)
|
||||||
return tc.rootManager.Unlock(ns, privPassphrase)
|
return tc.rootManager.Unlock(ns, passphrase)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tc.t.Errorf("Unlock: unexpected error: %v", err)
|
tc.t.Errorf("Unlock: unexpected error: %v", err)
|
||||||
|
@ -1122,74 +1122,11 @@ func testMarkUsed(tc *testContext, doScript bool) bool {
|
||||||
func testChangePassphrase(tc *testContext) bool {
|
func testChangePassphrase(tc *testContext) bool {
|
||||||
pfx := fmt.Sprintf("(%s) ", tc.caseName)
|
pfx := fmt.Sprintf("(%s) ", tc.caseName)
|
||||||
|
|
||||||
// Force an error when changing the passphrase due to failure to
|
testName := pfx + "ChangePassphrase with invalid old passphrase"
|
||||||
// generate a new secret key by replacing the generation function one
|
|
||||||
// that intentionally errors.
|
|
||||||
testName := pfx + "ChangePassphrase (public) with invalid new secret key"
|
|
||||||
|
|
||||||
oldKeyGen := SetSecretKeyGen(failingSecretKeyGen)
|
|
||||||
err := walletdb.Update(tc.db, func(tx walletdb.ReadWriteTx) error {
|
err := walletdb.Update(tc.db, func(tx walletdb.ReadWriteTx) error {
|
||||||
ns := tx.ReadWriteBucket(waddrmgrNamespaceKey)
|
ns := tx.ReadWriteBucket(waddrmgrNamespaceKey)
|
||||||
return tc.rootManager.ChangePassphrase(
|
return tc.rootManager.ChangePassphrase(
|
||||||
ns, pubPassphrase, pubPassphrase2, false, fastScrypt,
|
ns, []byte("bogus"), privPassphrase2, fastScrypt,
|
||||||
)
|
|
||||||
})
|
|
||||||
if !checkManagerError(tc.t, testName, err, ErrCrypto) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Attempt to change public passphrase with invalid old passphrase.
|
|
||||||
testName = pfx + "ChangePassphrase (public) with invalid old passphrase"
|
|
||||||
SetSecretKeyGen(oldKeyGen)
|
|
||||||
err = walletdb.Update(tc.db, func(tx walletdb.ReadWriteTx) error {
|
|
||||||
ns := tx.ReadWriteBucket(waddrmgrNamespaceKey)
|
|
||||||
return tc.rootManager.ChangePassphrase(
|
|
||||||
ns, []byte("bogus"), pubPassphrase2, false, fastScrypt,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
if !checkManagerError(tc.t, testName, err, ErrWrongPassphrase) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Change the public passphrase.
|
|
||||||
testName = pfx + "ChangePassphrase (public)"
|
|
||||||
err = walletdb.Update(tc.db, func(tx walletdb.ReadWriteTx) error {
|
|
||||||
ns := tx.ReadWriteBucket(waddrmgrNamespaceKey)
|
|
||||||
return tc.rootManager.ChangePassphrase(
|
|
||||||
ns, pubPassphrase, pubPassphrase2, false, fastScrypt,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
tc.t.Errorf("%s: unexpected error: %v", testName, err)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure the public passphrase was successfully changed. We do this by
|
|
||||||
// being able to re-derive the public key with the new passphrase.
|
|
||||||
secretKey := snacl.SecretKey{Key: &snacl.CryptoKey{}}
|
|
||||||
secretKey.Parameters = tc.rootManager.masterKeyPub.Parameters
|
|
||||||
if err := secretKey.DeriveKey(&pubPassphrase2); err != nil {
|
|
||||||
tc.t.Errorf("%s: passphrase does not match", testName)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Change the private passphrase back to what it was.
|
|
||||||
err = walletdb.Update(tc.db, func(tx walletdb.ReadWriteTx) error {
|
|
||||||
ns := tx.ReadWriteBucket(waddrmgrNamespaceKey)
|
|
||||||
return tc.rootManager.ChangePassphrase(
|
|
||||||
ns, pubPassphrase2, pubPassphrase, false, fastScrypt,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
tc.t.Errorf("%s: unexpected error: %v", testName, err)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
testName = pfx + "ChangePassphrase (private) with invalid old passphrase"
|
|
||||||
err = walletdb.Update(tc.db, func(tx walletdb.ReadWriteTx) error {
|
|
||||||
ns := tx.ReadWriteBucket(waddrmgrNamespaceKey)
|
|
||||||
return tc.rootManager.ChangePassphrase(
|
|
||||||
ns, []byte("bogus"), privPassphrase2, true, fastScrypt,
|
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
wantErrCode := ErrWrongPassphrase
|
wantErrCode := ErrWrongPassphrase
|
||||||
|
@ -1197,12 +1134,11 @@ func testChangePassphrase(tc *testContext) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Change the private passphrase.
|
testName = pfx + "ChangePassphrase"
|
||||||
testName = pfx + "ChangePassphrase (private)"
|
|
||||||
err = walletdb.Update(tc.db, func(tx walletdb.ReadWriteTx) error {
|
err = walletdb.Update(tc.db, func(tx walletdb.ReadWriteTx) error {
|
||||||
ns := tx.ReadWriteBucket(waddrmgrNamespaceKey)
|
ns := tx.ReadWriteBucket(waddrmgrNamespaceKey)
|
||||||
return tc.rootManager.ChangePassphrase(
|
return tc.rootManager.ChangePassphrase(
|
||||||
ns, privPassphrase, privPassphrase2, true, fastScrypt,
|
ns, passphrase, privPassphrase2, fastScrypt,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1217,8 +1153,8 @@ func testChangePassphrase(tc *testContext) bool {
|
||||||
return tc.rootManager.Unlock(ns, privPassphrase2)
|
return tc.rootManager.Unlock(ns, privPassphrase2)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tc.t.Errorf("%s: failed to unlock with new private "+
|
tc.t.Errorf("%s: failed to unlock with new passphrase: %v",
|
||||||
"passphrase: %v", testName, err)
|
testName, err)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
tc.unlocked = true
|
tc.unlocked = true
|
||||||
|
@ -1228,7 +1164,7 @@ func testChangePassphrase(tc *testContext) bool {
|
||||||
err = walletdb.Update(tc.db, func(tx walletdb.ReadWriteTx) error {
|
err = walletdb.Update(tc.db, func(tx walletdb.ReadWriteTx) error {
|
||||||
ns := tx.ReadWriteBucket(waddrmgrNamespaceKey)
|
ns := tx.ReadWriteBucket(waddrmgrNamespaceKey)
|
||||||
return tc.rootManager.ChangePassphrase(
|
return tc.rootManager.ChangePassphrase(
|
||||||
ns, privPassphrase2, privPassphrase, true, fastScrypt,
|
ns, privPassphrase2, passphrase, fastScrypt,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1269,7 +1205,7 @@ func testNewAccount(tc *testContext) bool {
|
||||||
// to derive account keys
|
// to derive account keys
|
||||||
err = walletdb.Update(tc.db, func(tx walletdb.ReadWriteTx) error {
|
err = walletdb.Update(tc.db, func(tx walletdb.ReadWriteTx) error {
|
||||||
ns := tx.ReadWriteBucket(waddrmgrNamespaceKey)
|
ns := tx.ReadWriteBucket(waddrmgrNamespaceKey)
|
||||||
err := tc.rootManager.Unlock(ns, privPassphrase)
|
err := tc.rootManager.Unlock(ns, passphrase)
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1668,7 +1604,7 @@ func _TestManager(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "created with seed",
|
name: "created with seed",
|
||||||
rootKey: rootKey,
|
rootKey: rootKey,
|
||||||
privPassphrase: privPassphrase,
|
privPassphrase: passphrase,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1688,7 +1624,7 @@ func testManagerCase(t *testing.T, caseName string,
|
||||||
// returned.
|
// returned.
|
||||||
err := walletdb.View(db, func(tx walletdb.ReadTx) error {
|
err := walletdb.View(db, func(tx walletdb.ReadTx) error {
|
||||||
ns := tx.ReadBucket(waddrmgrNamespaceKey)
|
ns := tx.ReadBucket(waddrmgrNamespaceKey)
|
||||||
_, err := Open(ns, pubPassphrase, &chaincfg.MainNetParams)
|
_, err := Open(ns, &chaincfg.MainNetParams)
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
if !checkManagerError(t, "Open non-existent", err, ErrNoExist) {
|
if !checkManagerError(t, "Open non-existent", err, ErrNoExist) {
|
||||||
|
@ -1703,13 +1639,13 @@ func testManagerCase(t *testing.T, caseName string,
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = Create(
|
err = Create(
|
||||||
ns, caseKey, pubPassphrase, casePrivPassphrase,
|
ns, caseKey, casePrivPassphrase,
|
||||||
&chaincfg.MainNetParams, fastScrypt, time.Time{},
|
&chaincfg.MainNetParams, fastScrypt, time.Time{},
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
mgr, err = Open(ns, pubPassphrase, &chaincfg.MainNetParams)
|
mgr, err = Open(ns, &chaincfg.MainNetParams)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -1729,7 +1665,7 @@ func testManagerCase(t *testing.T, caseName string,
|
||||||
err = walletdb.Update(db, func(tx walletdb.ReadWriteTx) error {
|
err = walletdb.Update(db, func(tx walletdb.ReadWriteTx) error {
|
||||||
ns := tx.ReadWriteBucket(waddrmgrNamespaceKey)
|
ns := tx.ReadWriteBucket(waddrmgrNamespaceKey)
|
||||||
return Create(
|
return Create(
|
||||||
ns, caseKey, pubPassphrase, casePrivPassphrase,
|
ns, caseKey, casePrivPassphrase,
|
||||||
&chaincfg.MainNetParams, fastScrypt, time.Time{},
|
&chaincfg.MainNetParams, fastScrypt, time.Time{},
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -1762,7 +1698,7 @@ func testManagerCase(t *testing.T, caseName string,
|
||||||
err = walletdb.View(db, func(tx walletdb.ReadTx) error {
|
err = walletdb.View(db, func(tx walletdb.ReadTx) error {
|
||||||
ns := tx.ReadBucket(waddrmgrNamespaceKey)
|
ns := tx.ReadBucket(waddrmgrNamespaceKey)
|
||||||
var err error
|
var err error
|
||||||
mgr, err = Open(ns, pubPassphrase, &chaincfg.MainNetParams)
|
mgr, err = Open(ns, &chaincfg.MainNetParams)
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1845,7 +1781,7 @@ func TestManagerHigherVersion(t *testing.T) {
|
||||||
// should expect to see the error ErrUpgrade.
|
// should expect to see the error ErrUpgrade.
|
||||||
err = walletdb.View(db, func(tx walletdb.ReadTx) error {
|
err = walletdb.View(db, func(tx walletdb.ReadTx) error {
|
||||||
ns := tx.ReadBucket(waddrmgrNamespaceKey)
|
ns := tx.ReadBucket(waddrmgrNamespaceKey)
|
||||||
_, err := Open(ns, pubPassphrase, &chaincfg.MainNetParams)
|
_, err := Open(ns, &chaincfg.MainNetParams)
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
if !checkManagerError(t, "Upgrade needed", err, ErrUpgrade) {
|
if !checkManagerError(t, "Upgrade needed", err, ErrUpgrade) {
|
||||||
|
@ -1869,7 +1805,7 @@ func TestManagerHigherVersion(t *testing.T) {
|
||||||
// ErrUpgrade.
|
// ErrUpgrade.
|
||||||
err = walletdb.View(db, func(tx walletdb.ReadTx) error {
|
err = walletdb.View(db, func(tx walletdb.ReadTx) error {
|
||||||
ns := tx.ReadBucket(waddrmgrNamespaceKey)
|
ns := tx.ReadBucket(waddrmgrNamespaceKey)
|
||||||
_, err := Open(ns, pubPassphrase, &chaincfg.MainNetParams)
|
_, err := Open(ns, &chaincfg.MainNetParams)
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
if !checkManagerError(t, "Upgrade needed", err, ErrUpgrade) {
|
if !checkManagerError(t, "Upgrade needed", err, ErrUpgrade) {
|
||||||
|
@ -1913,7 +1849,7 @@ func TestEncryptDecryptErrors(t *testing.T) {
|
||||||
// Unlock the manager for these tests
|
// Unlock the manager for these tests
|
||||||
err = walletdb.View(db, func(tx walletdb.ReadTx) error {
|
err = walletdb.View(db, func(tx walletdb.ReadTx) error {
|
||||||
ns := tx.ReadBucket(waddrmgrNamespaceKey)
|
ns := tx.ReadBucket(waddrmgrNamespaceKey)
|
||||||
return mgr.Unlock(ns, privPassphrase)
|
return mgr.Unlock(ns, passphrase)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("Attempted to unlock the manager, but failed:", err)
|
t.Fatal("Attempted to unlock the manager, but failed:", err)
|
||||||
|
@ -1945,7 +1881,7 @@ func TestEncryptDecrypt(t *testing.T) {
|
||||||
// Make sure address manager is unlocked
|
// Make sure address manager is unlocked
|
||||||
err := walletdb.View(db, func(tx walletdb.ReadTx) error {
|
err := walletdb.View(db, func(tx walletdb.ReadTx) error {
|
||||||
ns := tx.ReadBucket(waddrmgrNamespaceKey)
|
ns := tx.ReadBucket(waddrmgrNamespaceKey)
|
||||||
return mgr.Unlock(ns, privPassphrase)
|
return mgr.Unlock(ns, passphrase)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("Attempted to unlock the manager, but failed:", err)
|
t.Fatal("Attempted to unlock the manager, but failed:", err)
|
||||||
|
@ -1993,19 +1929,19 @@ func TestScopedKeyManagerManagement(t *testing.T) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = Create(
|
err = Create(
|
||||||
ns, rootKey, pubPassphrase, privPassphrase,
|
ns, rootKey, passphrase,
|
||||||
&chaincfg.MainNetParams, fastScrypt, time.Time{},
|
&chaincfg.MainNetParams, fastScrypt, time.Time{},
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
mgr, err = Open(ns, pubPassphrase, &chaincfg.MainNetParams)
|
mgr, err = Open(ns, &chaincfg.MainNetParams)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return mgr.Unlock(ns, privPassphrase)
|
return mgr.Unlock(ns, passphrase)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("create/open: unexpected error: %v", err)
|
t.Fatalf("create/open: unexpected error: %v", err)
|
||||||
|
@ -2152,12 +2088,12 @@ func TestScopedKeyManagerManagement(t *testing.T) {
|
||||||
err = walletdb.View(db, func(tx walletdb.ReadTx) error {
|
err = walletdb.View(db, func(tx walletdb.ReadTx) error {
|
||||||
ns := tx.ReadBucket(waddrmgrNamespaceKey)
|
ns := tx.ReadBucket(waddrmgrNamespaceKey)
|
||||||
var err error
|
var err error
|
||||||
mgr, err = Open(ns, pubPassphrase, &chaincfg.MainNetParams)
|
mgr, err = Open(ns, &chaincfg.MainNetParams)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return mgr.Unlock(ns, privPassphrase)
|
return mgr.Unlock(ns, passphrase)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("open: unexpected error: %v", err)
|
t.Fatalf("open: unexpected error: %v", err)
|
||||||
|
@ -2243,19 +2179,19 @@ func TestRootHDKeyNeutering(t *testing.T) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = Create(
|
err = Create(
|
||||||
ns, rootKey, pubPassphrase, privPassphrase,
|
ns, rootKey, passphrase,
|
||||||
&chaincfg.MainNetParams, fastScrypt, time.Time{},
|
&chaincfg.MainNetParams, fastScrypt, time.Time{},
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
mgr, err = Open(ns, pubPassphrase, &chaincfg.MainNetParams)
|
mgr, err = Open(ns, &chaincfg.MainNetParams)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return mgr.Unlock(ns, privPassphrase)
|
return mgr.Unlock(ns, passphrase)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("create/open: unexpected error: %v", err)
|
t.Fatalf("create/open: unexpected error: %v", err)
|
||||||
|
@ -2336,19 +2272,19 @@ func TestNewRawAccount(t *testing.T) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = Create(
|
err = Create(
|
||||||
ns, rootKey, pubPassphrase, privPassphrase,
|
ns, rootKey, passphrase,
|
||||||
&chaincfg.MainNetParams, fastScrypt, time.Time{},
|
&chaincfg.MainNetParams, fastScrypt, time.Time{},
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
mgr, err = Open(ns, pubPassphrase, &chaincfg.MainNetParams)
|
mgr, err = Open(ns, &chaincfg.MainNetParams)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return mgr.Unlock(ns, privPassphrase)
|
return mgr.Unlock(ns, passphrase)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("create/open: unexpected error: %v", err)
|
t.Fatalf("create/open: unexpected error: %v", err)
|
||||||
|
@ -2449,18 +2385,18 @@ func TestDeriveFromKeyPathCache(t *testing.T) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = Create(
|
err = Create(
|
||||||
ns, rootKey, pubPassphrase, privPassphrase,
|
ns, rootKey, passphrase,
|
||||||
&chaincfg.MainNetParams, fastScrypt, time.Time{},
|
&chaincfg.MainNetParams, fastScrypt, time.Time{},
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
mgr, err = Open(ns, pubPassphrase, &chaincfg.MainNetParams)
|
mgr, err = Open(ns, &chaincfg.MainNetParams)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return mgr.Unlock(ns, privPassphrase)
|
return mgr.Unlock(ns, passphrase)
|
||||||
})
|
})
|
||||||
require.NoError(t, err, "create/open: unexpected error: %v", err)
|
require.NoError(t, err, "create/open: unexpected error: %v", err)
|
||||||
|
|
||||||
|
|
|
@ -33,19 +33,18 @@ func testWallet(t *testing.T) (*Wallet, func()) {
|
||||||
t.Fatalf("unable to create seed: %v", err)
|
t.Fatalf("unable to create seed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
pubPass := []byte("hello")
|
passphrase := []byte("hello world")
|
||||||
privPass := []byte("world")
|
|
||||||
|
|
||||||
loader := NewLoader(
|
loader := NewLoader(
|
||||||
&chaincfg.TestNet3Params, dir, true, defaultDBTimeout, 250,
|
&chaincfg.TestNet3Params, dir, true, defaultDBTimeout, 250,
|
||||||
)
|
)
|
||||||
w, err := loader.CreateNewWallet(pubPass, privPass, seed, time.Now())
|
w, err := loader.CreateNewWallet(passphrase, seed, time.Now())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to create wallet: %v", err)
|
t.Fatalf("unable to create wallet: %v", err)
|
||||||
}
|
}
|
||||||
chainClient := &mockChainClient{}
|
chainClient := &mockChainClient{}
|
||||||
w.chainClient = chainClient
|
w.chainClient = chainClient
|
||||||
if err := w.Unlock(privPass, time.After(10*time.Minute)); err != nil {
|
if err := w.Unlock(passphrase, time.After(10*time.Minute)); err != nil {
|
||||||
t.Fatalf("unable to unlock wallet: %v", err)
|
t.Fatalf("unable to unlock wallet: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -140,10 +140,10 @@ func (l *Loader) OnWalletCreated(fn func(walletdb.ReadWriteTx) error) {
|
||||||
l.walletCreated = fn
|
l.walletCreated = fn
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateNewWallet creates a new wallet using the provided public and private
|
// CreateNewWallet creates a new wallet using the provided passphrase.
|
||||||
// passphrases. The seed is optional. If non-nil, addresses are derived from
|
// The seed is optional. If non-nil, addresses are derived from this seed.
|
||||||
// this seed. If nil, a secure random seed is generated.
|
// If nil, a secure random seed is generated.
|
||||||
func (l *Loader) CreateNewWallet(pubPassphrase, privPassphrase, seed []byte,
|
func (l *Loader) CreateNewWallet(passphrase, seed []byte,
|
||||||
bday time.Time) (*Wallet, error) {
|
bday time.Time) (*Wallet, error) {
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -168,20 +168,20 @@ func (l *Loader) CreateNewWallet(pubPassphrase, privPassphrase, seed []byte,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return l.createNewWallet(pubPassphrase, privPassphrase, rootKey, bday)
|
return l.createNewWallet(passphrase, rootKey, bday)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateNewWalletExtendedKey creates a new wallet from an extended master root
|
// CreateNewWalletExtendedKey creates a new wallet from an extended master root
|
||||||
// key using the provided public and private passphrases. The root key is
|
// key using the provided passphrase. The root key is optional.
|
||||||
// optional. If non-nil, addresses are derived from this root key. If nil, a
|
// If non-nil, addresses are derived from this root key. If nil, a
|
||||||
// secure random seed is generated and the root key is derived from that.
|
// secure random seed is generated and the root key is derived from that.
|
||||||
func (l *Loader) CreateNewWalletExtendedKey(pubPassphrase, privPassphrase []byte,
|
func (l *Loader) CreateNewWalletExtendedKey(passphrase []byte,
|
||||||
rootKey *hdkeychain.ExtendedKey, bday time.Time) (*Wallet, error) {
|
rootKey *hdkeychain.ExtendedKey, bday time.Time) (*Wallet, error) {
|
||||||
|
|
||||||
return l.createNewWallet(pubPassphrase, privPassphrase, rootKey, bday)
|
return l.createNewWallet(passphrase, rootKey, bday)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Loader) createNewWallet(pubPassphrase, privPassphrase []byte,
|
func (l *Loader) createNewWallet(passphrase []byte,
|
||||||
rootKey *hdkeychain.ExtendedKey, bday time.Time) (*Wallet, error) {
|
rootKey *hdkeychain.ExtendedKey, bday time.Time) (*Wallet, error) {
|
||||||
|
|
||||||
defer l.mu.Unlock()
|
defer l.mu.Unlock()
|
||||||
|
@ -216,16 +216,15 @@ func (l *Loader) createNewWallet(pubPassphrase, privPassphrase []byte,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize the newly created database for the wallet before opening.
|
// Initialize the newly created database for the wallet before opening.
|
||||||
err = CreateWithCallback(
|
err = CreateWithCallback(l.db, passphrase, rootKey, l.chainParams,
|
||||||
l.db, pubPassphrase, privPassphrase, rootKey,
|
bday, l.walletCreated,
|
||||||
l.chainParams, bday, l.walletCreated,
|
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open the newly-created wallet.
|
// Open the newly-created wallet.
|
||||||
w, err := Open(l.db, pubPassphrase, nil, l.chainParams, l.recoveryWindow)
|
w, err := Open(l.db, nil, l.chainParams, l.recoveryWindow)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -241,11 +240,10 @@ func noConsole() ([]byte, error) {
|
||||||
return nil, errNoConsole
|
return nil, errNoConsole
|
||||||
}
|
}
|
||||||
|
|
||||||
// OpenExistingWallet opens the wallet from the loader's wallet database path
|
// OpenExistingWallet opens the wallet from the loader's wallet database path.
|
||||||
// and the public passphrase. If the loader is being called by a context where
|
// If the loader is being called by a context where standard input prompts may
|
||||||
// standard input prompts may be used during wallet upgrades, setting
|
// be used during wallet upgrades, setting canConsolePrompt will enables these prompts.
|
||||||
// canConsolePrompt will enables these prompts.
|
func (l *Loader) OpenExistingWallet(canConsolePrompt bool) (*Wallet, error) {
|
||||||
func (l *Loader) OpenExistingWallet(pubPassphrase []byte, canConsolePrompt bool) (*Wallet, error) {
|
|
||||||
defer l.mu.Unlock()
|
defer l.mu.Unlock()
|
||||||
l.mu.Lock()
|
l.mu.Lock()
|
||||||
|
|
||||||
|
@ -275,15 +273,15 @@ func (l *Loader) OpenExistingWallet(pubPassphrase []byte, canConsolePrompt bool)
|
||||||
if canConsolePrompt {
|
if canConsolePrompt {
|
||||||
cbs = &waddrmgr.OpenCallbacks{
|
cbs = &waddrmgr.OpenCallbacks{
|
||||||
ObtainSeed: prompt.ProvideSeed(),
|
ObtainSeed: prompt.ProvideSeed(),
|
||||||
ObtainPrivatePass: prompt.ProvidePrivPassphrase,
|
ObtainPassphrase: prompt.ProvidePassphrase,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
cbs = &waddrmgr.OpenCallbacks{
|
cbs = &waddrmgr.OpenCallbacks{
|
||||||
ObtainSeed: noConsole,
|
ObtainSeed: noConsole,
|
||||||
ObtainPrivatePass: noConsole,
|
ObtainPassphrase: noConsole,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
w, err := Open(l.db, pubPassphrase, cbs, l.chainParams, l.recoveryWindow)
|
w, err := Open(l.db, cbs, l.chainParams, l.recoveryWindow)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// If opening the wallet fails (e.g. because of wrong
|
// If opening the wallet fails (e.g. because of wrong
|
||||||
// passphrase), we must close the backing database to
|
// passphrase), we must close the backing database to
|
||||||
|
|
104
wallet/wallet.go
104
wallet/wallet.go
|
@ -36,16 +36,6 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// InsecurePubPassphrase is the default outer encryption passphrase used
|
|
||||||
// for public data (everything but private keys). Using a non-default
|
|
||||||
// public passphrase can prevent an attacker without the public
|
|
||||||
// passphrase from discovering all past and future wallet addresses if
|
|
||||||
// they gain access to the wallet database.
|
|
||||||
//
|
|
||||||
// NOTE: at time of writing, public encryption only applies to public
|
|
||||||
// data in the waddrmgr namespace. Transactions are not yet encrypted.
|
|
||||||
InsecurePubPassphrase = "public"
|
|
||||||
|
|
||||||
// recoveryBatchSize is the default number of blocks that will be
|
// recoveryBatchSize is the default number of blocks that will be
|
||||||
// scanned successively by the recovery manager, in the event that the
|
// scanned successively by the recovery manager, in the event that the
|
||||||
// wallet is started in recovery mode.
|
// wallet is started in recovery mode.
|
||||||
|
@ -99,8 +89,6 @@ const (
|
||||||
// complete wallet. It contains the Armory-style key store
|
// complete wallet. It contains the Armory-style key store
|
||||||
// addresses and keys),
|
// addresses and keys),
|
||||||
type Wallet struct {
|
type Wallet struct {
|
||||||
publicPassphrase []byte
|
|
||||||
|
|
||||||
// Data stores
|
// Data stores
|
||||||
db walletdb.DB
|
db walletdb.DB
|
||||||
Manager *waddrmgr.Manager
|
Manager *waddrmgr.Manager
|
||||||
|
@ -134,7 +122,6 @@ type Wallet struct {
|
||||||
holdUnlockRequests chan chan heldUnlock
|
holdUnlockRequests chan chan heldUnlock
|
||||||
lockState chan bool
|
lockState chan bool
|
||||||
changePassphrase chan changePassphraseRequest
|
changePassphrase chan changePassphraseRequest
|
||||||
changePassphrases chan changePassphrasesRequest
|
|
||||||
|
|
||||||
NtfnServer *NotificationServer
|
NtfnServer *NotificationServer
|
||||||
|
|
||||||
|
@ -703,7 +690,7 @@ func (w *Wallet) recovery(chainClient chain.Interface,
|
||||||
// that a wallet rescan will be performed from the wallet's tip, which
|
// that a wallet rescan will be performed from the wallet's tip, which
|
||||||
// will be of bestHeight after completing the recovery process.
|
// will be of bestHeight after completing the recovery process.
|
||||||
|
|
||||||
pass, err := prompt.ProvidePrivPassphrase()
|
pass, err := prompt.ProvidePassphrase()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -1188,13 +1175,6 @@ type (
|
||||||
|
|
||||||
changePassphraseRequest struct {
|
changePassphraseRequest struct {
|
||||||
old, new []byte
|
old, new []byte
|
||||||
private bool
|
|
||||||
err chan error
|
|
||||||
}
|
|
||||||
|
|
||||||
changePassphrasesRequest struct {
|
|
||||||
publicOld, publicNew []byte
|
|
||||||
privateOld, privateNew []byte
|
|
||||||
err chan error
|
err chan error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1236,32 +1216,13 @@ out:
|
||||||
err := walletdb.Update(w.db, func(tx walletdb.ReadWriteTx) error {
|
err := walletdb.Update(w.db, func(tx walletdb.ReadWriteTx) error {
|
||||||
addrmgrNs := tx.ReadWriteBucket(waddrmgrNamespaceKey)
|
addrmgrNs := tx.ReadWriteBucket(waddrmgrNamespaceKey)
|
||||||
return w.Manager.ChangePassphrase(
|
return w.Manager.ChangePassphrase(
|
||||||
addrmgrNs, req.old, req.new, req.private,
|
addrmgrNs, req.old, req.new,
|
||||||
&waddrmgr.DefaultScryptOptions,
|
&waddrmgr.DefaultScryptOptions,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
req.err <- err
|
req.err <- err
|
||||||
continue
|
continue
|
||||||
|
|
||||||
case req := <-w.changePassphrases:
|
|
||||||
err := walletdb.Update(w.db, func(tx walletdb.ReadWriteTx) error {
|
|
||||||
addrmgrNs := tx.ReadWriteBucket(waddrmgrNamespaceKey)
|
|
||||||
err := w.Manager.ChangePassphrase(
|
|
||||||
addrmgrNs, req.publicOld, req.publicNew,
|
|
||||||
false, &waddrmgr.DefaultScryptOptions,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return w.Manager.ChangePassphrase(
|
|
||||||
addrmgrNs, req.privateOld, req.privateNew,
|
|
||||||
true, &waddrmgr.DefaultScryptOptions,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
req.err <- err
|
|
||||||
continue
|
|
||||||
|
|
||||||
case req := <-w.holdUnlockRequests:
|
case req := <-w.holdUnlockRequests:
|
||||||
if w.Manager.IsLocked() {
|
if w.Manager.IsLocked() {
|
||||||
close(req)
|
close(req)
|
||||||
|
@ -1362,44 +1323,15 @@ func (c heldUnlock) release() {
|
||||||
c <- struct{}{}
|
c <- struct{}{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ChangePrivatePassphrase attempts to change the passphrase for a wallet from
|
// ChangePassphrase attempts to change the passphrase for a wallet from
|
||||||
// old to new. Changing the passphrase is synchronized with all other address
|
// old to new. Changing the passphrase is synchronized with all other address
|
||||||
// manager locking and unlocking. The lock state will be the same as it was
|
// manager locking and unlocking. The lock state will be the same as it was
|
||||||
// before the password change.
|
// before the password change.
|
||||||
func (w *Wallet) ChangePrivatePassphrase(old, new []byte) error {
|
func (w *Wallet) ChangePassphrase(old, new []byte) error {
|
||||||
err := make(chan error, 1)
|
err := make(chan error, 1)
|
||||||
w.changePassphrase <- changePassphraseRequest{
|
w.changePassphrase <- changePassphraseRequest{
|
||||||
old: old,
|
old: old,
|
||||||
new: new,
|
new: new,
|
||||||
private: true,
|
|
||||||
err: err,
|
|
||||||
}
|
|
||||||
return <-err
|
|
||||||
}
|
|
||||||
|
|
||||||
// ChangePublicPassphrase modifies the public passphrase of the wallet.
|
|
||||||
func (w *Wallet) ChangePublicPassphrase(old, new []byte) error {
|
|
||||||
err := make(chan error, 1)
|
|
||||||
w.changePassphrase <- changePassphraseRequest{
|
|
||||||
old: old,
|
|
||||||
new: new,
|
|
||||||
private: false,
|
|
||||||
err: err,
|
|
||||||
}
|
|
||||||
return <-err
|
|
||||||
}
|
|
||||||
|
|
||||||
// ChangePassphrases modifies the public and private passphrase of the wallet
|
|
||||||
// atomically.
|
|
||||||
func (w *Wallet) ChangePassphrases(publicOld, publicNew, privateOld,
|
|
||||||
privateNew []byte) error {
|
|
||||||
|
|
||||||
err := make(chan error, 1)
|
|
||||||
w.changePassphrases <- changePassphrasesRequest{
|
|
||||||
publicOld: publicOld,
|
|
||||||
publicNew: publicNew,
|
|
||||||
privateOld: privateOld,
|
|
||||||
privateNew: privateNew,
|
|
||||||
err: err,
|
err: err,
|
||||||
}
|
}
|
||||||
return <-err
|
return <-err
|
||||||
|
@ -3659,29 +3591,23 @@ func (w *Wallet) Database() walletdb.DB {
|
||||||
|
|
||||||
// CreateWithCallback is the same as Create with an added callback that will be
|
// CreateWithCallback is the same as Create with an added callback that will be
|
||||||
// called in the same transaction the wallet structure is initialized.
|
// called in the same transaction the wallet structure is initialized.
|
||||||
func CreateWithCallback(db walletdb.DB, pubPass, privPass []byte,
|
func CreateWithCallback(db walletdb.DB, privPass []byte,
|
||||||
rootKey *hdkeychain.ExtendedKey, params *chaincfg.Params,
|
rootKey *hdkeychain.ExtendedKey, params *chaincfg.Params,
|
||||||
birthday time.Time, cb func(walletdb.ReadWriteTx) error) error {
|
birthday time.Time, cb func(walletdb.ReadWriteTx) error) error {
|
||||||
|
|
||||||
return create(
|
return create(db, privPass, rootKey, params, birthday, cb)
|
||||||
db, pubPass, privPass, rootKey, params, birthday, cb,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create creates an new wallet, writing it to an empty database. If the passed
|
// Create creates an new wallet, writing it to an empty database. If the passed
|
||||||
// root key is non-nil, it is used. Otherwise, a secure random seed of the
|
// root key is non-nil, it is used. Otherwise, a secure random seed of the
|
||||||
// recommended length is generated.
|
// recommended length is generated.
|
||||||
func Create(db walletdb.DB, pubPass, privPass []byte,
|
func Create(db walletdb.DB, privPass []byte, rootKey *hdkeychain.ExtendedKey,
|
||||||
rootKey *hdkeychain.ExtendedKey, params *chaincfg.Params,
|
params *chaincfg.Params, birthday time.Time) error {
|
||||||
birthday time.Time) error {
|
|
||||||
|
|
||||||
return create(
|
return create(db, privPass, rootKey, params, birthday, nil)
|
||||||
db, pubPass, privPass, rootKey, params, birthday, nil,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
func create(db walletdb.DB, pubPass, privPass []byte,
|
func create(db walletdb.DB, privPass []byte, rootKey *hdkeychain.ExtendedKey,
|
||||||
rootKey *hdkeychain.ExtendedKey, params *chaincfg.Params,
|
params *chaincfg.Params, birthday time.Time,
|
||||||
birthday time.Time,
|
|
||||||
cb func(walletdb.ReadWriteTx) error) error {
|
cb func(walletdb.ReadWriteTx) error) error {
|
||||||
|
|
||||||
// If no root key was provided, we create one now from a random seed.
|
// If no root key was provided, we create one now from a random seed.
|
||||||
|
@ -3718,7 +3644,7 @@ func create(db walletdb.DB, pubPass, privPass []byte,
|
||||||
}
|
}
|
||||||
|
|
||||||
err = waddrmgr.Create(
|
err = waddrmgr.Create(
|
||||||
addrmgrNs, rootKey, pubPass, privPass, params, nil,
|
addrmgrNs, rootKey, privPass, params, nil,
|
||||||
birthday,
|
birthday,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -3739,7 +3665,7 @@ func create(db walletdb.DB, pubPass, privPass []byte,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open loads an already-created wallet from the passed database and namespaces.
|
// Open loads an already-created wallet from the passed database and namespaces.
|
||||||
func Open(db walletdb.DB, pubPass []byte, cbs *waddrmgr.OpenCallbacks,
|
func Open(db walletdb.DB, cbs *waddrmgr.OpenCallbacks,
|
||||||
params *chaincfg.Params, recoveryWindow uint32) (*Wallet, error) {
|
params *chaincfg.Params, recoveryWindow uint32) (*Wallet, error) {
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -3768,7 +3694,7 @@ func Open(db walletdb.DB, pubPass []byte, cbs *waddrmgr.OpenCallbacks,
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
addrMgr, err = waddrmgr.Open(addrMgrBucket, pubPass, params)
|
addrMgr, err = waddrmgr.Open(addrMgrBucket, params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -3786,7 +3712,6 @@ func Open(db walletdb.DB, pubPass []byte, cbs *waddrmgr.OpenCallbacks,
|
||||||
log.Infof("Opened wallet") // TODO: log balance? last sync height?
|
log.Infof("Opened wallet") // TODO: log balance? last sync height?
|
||||||
|
|
||||||
w := &Wallet{
|
w := &Wallet{
|
||||||
publicPassphrase: pubPass,
|
|
||||||
db: db,
|
db: db,
|
||||||
Manager: addrMgr,
|
Manager: addrMgr,
|
||||||
TxStore: txMgr,
|
TxStore: txMgr,
|
||||||
|
@ -3803,7 +3728,6 @@ func Open(db walletdb.DB, pubPass []byte, cbs *waddrmgr.OpenCallbacks,
|
||||||
holdUnlockRequests: make(chan chan heldUnlock),
|
holdUnlockRequests: make(chan chan heldUnlock),
|
||||||
lockState: make(chan bool),
|
lockState: make(chan bool),
|
||||||
changePassphrase: make(chan changePassphraseRequest),
|
changePassphrase: make(chan changePassphraseRequest),
|
||||||
changePassphrases: make(chan changePassphrasesRequest),
|
|
||||||
chainParams: params,
|
chainParams: params,
|
||||||
quit: make(chan struct{}),
|
quit: make(chan struct{}),
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,18 +45,9 @@ func createWallet(cfg *config) error {
|
||||||
activeNet.Params, dbDir, true, cfg.DBTimeout, 250,
|
activeNet.Params, dbDir, true, cfg.DBTimeout, 250,
|
||||||
)
|
)
|
||||||
|
|
||||||
// Start by prompting for the private passphrase.
|
// Start by prompting for the passphrase.
|
||||||
reader := bufio.NewReader(os.Stdin)
|
reader := bufio.NewReader(os.Stdin)
|
||||||
privPass, err := prompt.PrivatePass(reader)
|
privPass, err := prompt.Passphrase(reader)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ascertain the public passphrase. This will either be a value
|
|
||||||
// specified by the user or the default hard-coded public passphrase if
|
|
||||||
// the user does not want the additional public data encryption.
|
|
||||||
pubPass, err := prompt.PublicPass(reader, privPass,
|
|
||||||
[]byte(wallet.InsecurePubPassphrase), []byte(cfg.WalletPass))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -70,7 +61,7 @@ func createWallet(cfg *config) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println("Creating the wallet...")
|
fmt.Println("Creating the wallet...")
|
||||||
w, err := loader.CreateNewWallet(pubPass, privPass, seed, bday)
|
w, err := loader.CreateNewWallet(privPass, seed, bday)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -88,9 +79,6 @@ func createSimulationWallet(cfg *config) error {
|
||||||
// Simulation wallet password is 'password'.
|
// Simulation wallet password is 'password'.
|
||||||
privPass := []byte("password")
|
privPass := []byte("password")
|
||||||
|
|
||||||
// Public passphrase is the default.
|
|
||||||
pubPass := []byte(wallet.InsecurePubPassphrase)
|
|
||||||
|
|
||||||
netDir := networkDir(cfg.AppDataDir.Value, activeNet.Params)
|
netDir := networkDir(cfg.AppDataDir.Value, activeNet.Params)
|
||||||
|
|
||||||
// Create the wallet.
|
// Create the wallet.
|
||||||
|
@ -105,7 +93,7 @@ func createSimulationWallet(cfg *config) error {
|
||||||
defer db.Close()
|
defer db.Close()
|
||||||
|
|
||||||
// Create the wallet.
|
// Create the wallet.
|
||||||
err = wallet.Create(db, pubPass, privPass, nil, activeNet.Params, time.Now())
|
err = wallet.Create(db, privPass, nil, activeNet.Params, time.Now())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue