consolidate: remove passphrase support for public keys #37

Merged
roylee17 merged 1 commit from roylee/remove-passphrase-support-for-public-keys into master 2022-09-29 07:12:39 +02:00
14 changed files with 243 additions and 513 deletions

View file

@ -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

View file

@ -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),

View file

@ -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

View file

@ -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")
}

View file

@ -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

View file

@ -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{

View file

@ -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 {

View file

@ -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

View file

@ -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)
} }

View file

@ -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)

View file

@ -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)
} }

View file

@ -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

View file

@ -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{}),
} }

View file

@ -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
} }