Remove unnecessary mutex around wallet secret.

The wallet package was using a sync.Mutex around the saved decryption
key (kept in memory for an unlocked wallet).  As the wallet package is
designed to use no internal locking, and correct synchronization is
provided by the importers of the package, this mutex has been removed.
This commit is contained in:
Josh Rickmar 2014-01-17 09:35:52 -05:00
parent 311d6176a8
commit 82f2067ac4

View file

@ -34,7 +34,6 @@ import (
"github.com/conformal/btcwire" "github.com/conformal/btcwire"
"io" "io"
"math/big" "math/big"
"sync"
"time" "time"
) )
@ -487,10 +486,7 @@ type Wallet struct {
txCommentMap map[transactionHashKey]comment txCommentMap map[transactionHashKey]comment
// The rest of the fields in this struct are not serialized. // The rest of the fields in this struct are not serialized.
secret struct { secret []byte
sync.Mutex
key []byte
}
chainIdxMap map[int64]*btcutil.AddressPubKeyHash chainIdxMap map[int64]*btcutil.AddressPubKeyHash
importedAddrs []*btcAddress importedAddrs []*btcAddress
lastChainIdx int64 lastChainIdx int64
@ -571,6 +567,7 @@ func NewWallet(name, desc string, passphrase []byte, net btcwire.BitcoinNet,
txCommentMap: make(map[transactionHashKey]comment), txCommentMap: make(map[transactionHashKey]comment),
chainIdxMap: make(map[int64]*btcutil.AddressPubKeyHash), chainIdxMap: make(map[int64]*btcutil.AddressPubKeyHash),
lastChainIdx: rootKeyChainIdx, lastChainIdx: rootKeyChainIdx,
secret: aeskey,
} }
copy(w.name[:], []byte(name)) copy(w.name[:], []byte(name))
copy(w.desc[:], []byte(desc)) copy(w.desc[:], []byte(desc))
@ -580,7 +577,12 @@ func NewWallet(name, desc string, passphrase []byte, net btcwire.BitcoinNet,
w.chainIdxMap[rootKeyChainIdx] = w.keyGenerator.address(net) w.chainIdxMap[rootKeyChainIdx] = w.keyGenerator.address(net)
// Fill keypool. // Fill keypool.
if err := w.extendKeypool(keypoolSize, aeskey, createdAt); err != nil { if err := w.extendKeypool(keypoolSize, createdAt); err != nil {
return nil, err
}
// Wallet must be returned locked.
if err := w.Lock(); err != nil {
return nil, err return nil, err
} }
@ -787,29 +789,22 @@ func (w *Wallet) Unlock(passphrase []byte) error {
return err return err
} }
// If unlock was successful, create a copy for below and save the // If unlock was successful, save the secret key.
// secret key. w.secret = key
keycopy := make([]byte, len(key))
copy(keycopy, key)
w.secret.Lock()
w.secret.key = key
w.secret.Unlock()
return w.createMissingPrivateKeys(keycopy) return w.createMissingPrivateKeys()
} }
// Lock performs a best try effort to remove and zero all secret keys // Lock performs a best try effort to remove and zero all secret keys
// associated with the wallet. // associated with the wallet.
func (w *Wallet) Lock() (err error) { func (w *Wallet) Lock() (err error) {
// Remove clear text passphrase from wallet. // Remove clear text passphrase from wallet.
w.secret.Lock() if len(w.secret) != 32 {
if w.secret.key == nil {
err = ErrWalletLocked err = ErrWalletLocked
} else { } else {
zero(w.secret.key) zero(w.secret)
w.secret.key = nil w.secret = nil
} }
w.secret.Unlock()
// Remove clear text private keys from all address entries. // Remove clear text private keys from all address entries.
for _, addr := range w.addrMap { for _, addr := range w.addrMap {
@ -828,11 +823,8 @@ func zero(b []byte) {
// IsLocked returns whether a wallet is unlocked (in which case the // IsLocked returns whether a wallet is unlocked (in which case the
// key is saved in memory), or locked. // key is saved in memory), or locked.
func (w *Wallet) IsLocked() (locked bool) { func (w *Wallet) IsLocked() bool {
w.secret.Lock() return len(w.secret) != 32
locked = w.secret.key == nil
w.secret.Unlock()
return locked
} }
// Version returns a wallet's version as a string and int. // Version returns a wallet's version as a string and int.
@ -852,24 +844,13 @@ func (w *Wallet) NextChainedAddress(bs *BlockStamp,
nextAPKH, ok := w.chainIdxMap[w.highestUsed+1] nextAPKH, ok := w.chainIdxMap[w.highestUsed+1]
if !ok { if !ok {
// Extending the keypool requires an unlocked wallet. // Extending the keypool requires an unlocked wallet.
var aeskey []byte if len(w.secret) == 32 {
w.secret.Lock() // Key is available, extend keypool.
if len(w.secret.key) == 32 { if err := w.extendKeypool(keypoolSize, bs); err != nil {
// Key is available, make a copy and extend
// keypool.
aeskey = make([]byte, 32)
copy(aeskey, w.secret.key)
w.secret.Unlock()
err := w.extendKeypool(keypoolSize, aeskey, bs)
if err != nil {
return nil, err return nil, err
} }
} else { } else {
w.secret.Unlock() if err := w.extendLockedWallet(bs); err != nil {
err := w.extendLockedWallet(bs)
if err != nil {
return nil, err return nil, err
} }
} }
@ -901,7 +882,7 @@ func (w *Wallet) LastChainedAddress() btcutil.Address {
} }
// extendKeypool grows the keypool by n addresses. // extendKeypool grows the keypool by n addresses.
func (w *Wallet) extendKeypool(n uint, aeskey []byte, bs *BlockStamp) error { func (w *Wallet) extendKeypool(n uint, bs *BlockStamp) error {
// Get last chained address. New chained addresses will be // Get last chained address. New chained addresses will be
// chained off of this address's chaincode and private key. // chained off of this address's chaincode and private key.
a := w.chainIdxMap[w.lastChainIdx] a := w.chainIdxMap[w.lastChainIdx]
@ -909,7 +890,7 @@ func (w *Wallet) extendKeypool(n uint, aeskey []byte, bs *BlockStamp) error {
if !ok { if !ok {
return errors.New("expected last chained address not found") return errors.New("expected last chained address not found")
} }
privkey, err := addr.unlock(aeskey) privkey, err := addr.unlock(w.secret)
if err != nil { if err != nil {
return err return err
} }
@ -929,7 +910,7 @@ func (w *Wallet) extendKeypool(n uint, aeskey []byte, bs *BlockStamp) error {
if err := newaddr.verifyKeypairs(); err != nil { if err := newaddr.verifyKeypairs(); err != nil {
return err return err
} }
if err = newaddr.encrypt(aeskey); err != nil { if err = newaddr.encrypt(w.secret); err != nil {
return err return err
} }
a := newaddr.address(w.net) a := newaddr.address(w.net)
@ -983,7 +964,7 @@ func (w *Wallet) extendLockedWallet(bs *BlockStamp) error {
return nil return nil
} }
func (w *Wallet) createMissingPrivateKeys(aeskey []byte) error { func (w *Wallet) createMissingPrivateKeys() error {
idx := w.missingKeysStart idx := w.missingKeysStart
if idx == 0 { if idx == 0 {
return nil return nil
@ -995,7 +976,7 @@ func (w *Wallet) createMissingPrivateKeys(aeskey []byte) error {
return errors.New("missing previous chained address") return errors.New("missing previous chained address")
} }
prevAddr := w.addrMap[*apkh] prevAddr := w.addrMap[*apkh]
prevPrivKey, err := prevAddr.unlock(aeskey) prevPrivKey, err := prevAddr.unlock(w.secret)
if err != nil { if err != nil {
return err return err
} }
@ -1017,7 +998,7 @@ func (w *Wallet) createMissingPrivateKeys(aeskey []byte) error {
} }
addr := w.addrMap[*apkh] addr := w.addrMap[*apkh]
addr.privKeyCT = ithPrivKey addr.privKeyCT = ithPrivKey
if err := addr.encrypt(aeskey); err != nil { if err := addr.encrypt(w.secret); err != nil {
return err return err
} }
@ -1065,21 +1046,15 @@ func (w *Wallet) AddressKey(a btcutil.Address) (key *ecdsa.PrivateKey, err error
return nil, err return nil, err
} }
// The wallet's secret will be zeroed on lock, so make a local // Wallet must be unlocked to decrypt the private key.
// copy. if len(w.secret) != 32 {
localSecret := make([]byte, 32)
w.secret.Lock()
if len(w.secret.key) != 32 {
w.secret.Unlock()
return nil, ErrWalletLocked return nil, ErrWalletLocked
} }
copy(localSecret, w.secret.key)
w.secret.Unlock()
// Unlock address with wallet secret. unlock returns a copy of the // Unlock address with wallet secret. unlock returns a copy of the
// clear text private key, and may be used safely even during an address // clear text private key, and may be used safely even during an address
// lock. // lock.
privKeyCT, err := btcaddr.unlock(localSecret) privKeyCT, err := btcaddr.unlock(w.secret)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -1226,15 +1201,10 @@ func (w *Wallet) ImportPrivateKey(privkey []byte, compressed bool, bs *BlockStam
return "", ErrDuplicate return "", ErrDuplicate
} }
// The wallet's secret will be zeroed on lock, so make a local copy. // The wallet must be unlocked to encrypt the imported private key.
w.secret.Lock() if len(w.secret) != 32 {
if len(w.secret.key) != 32 {
w.secret.Unlock()
return "", ErrWalletLocked return "", ErrWalletLocked
} }
localSecret := make([]byte, 32)
copy(localSecret, w.secret.key)
w.secret.Unlock()
// Create new address with this private key. // Create new address with this private key.
btcaddr, err := newBtcAddress(privkey, nil, bs, compressed) btcaddr, err := newBtcAddress(privkey, nil, bs, compressed)
@ -1244,7 +1214,7 @@ func (w *Wallet) ImportPrivateKey(privkey []byte, compressed bool, bs *BlockStam
btcaddr.chainIndex = importedKeyChainIdx btcaddr.chainIndex = importedKeyChainIdx
// Encrypt imported address with the derived AES key. // Encrypt imported address with the derived AES key.
if err = btcaddr.encrypt(localSecret); err != nil { if err = btcaddr.encrypt(w.secret); err != nil {
return "", err return "", err
} }