From c54af23849395ea31bd1eeb3e5261223cc85a56a Mon Sep 17 00:00:00 2001 From: Josh Rickmar Date: Tue, 3 Dec 2013 12:33:37 -0500 Subject: [PATCH] Perform signature verifiction when generating addresses. This change adds an additional check when creating a new wallet or extending the keypool. All public and private keypairs are parsed from their serialized forms, and an ecdsa signature is created and verified using the keypairs. If the verifiction fails at any point, the wallet creation or keypool extension is aborted to prevent any errors where an address is returned to a user, but any funds send to that address are unspendable due to a mismatched keypair. --- account.go | 4 +++- cmd.go | 3 +-- wallet/wallet.go | 42 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 46 insertions(+), 3 deletions(-) diff --git a/account.go b/account.go index 357a09b..9d56008 100644 --- a/account.go +++ b/account.go @@ -444,7 +444,6 @@ func (a *Account) ActivePaymentAddresses() map[string]struct{} { // NewAddress returns a new payment address for an account. func (a *Account) NewAddress() (string, error) { a.mtx.Lock() - defer a.mtx.Unlock() // Get current block's height and hash. bs, err := GetCurBlock() @@ -460,6 +459,7 @@ func (a *Account) NewAddress() (string, error) { // Write updated wallet to disk. a.dirty = true + a.mtx.Unlock() if err = a.writeDirtyToDisk(); err != nil { log.Errorf("cannot sync dirty wallet: %v", err) } @@ -467,6 +467,8 @@ func (a *Account) NewAddress() (string, error) { // Request updates from btcd for new transactions sent to this address. a.ReqNewTxsForAddress(addr) + fmt.Println("after") + return addr, nil } diff --git a/cmd.go b/cmd.go index e71b568..de03fa7 100644 --- a/cmd.go +++ b/cmd.go @@ -207,8 +207,7 @@ func main() { // Open default account err = accountstore.OpenAccount("", cfg) if err != nil { - log.Errorf("cannot open account: %v", err) - os.Exit(1) + log.Warnf("cannot open default account: %v", err) } // Read CA file to verify a btcd TLS connection. diff --git a/wallet/wallet.go b/wallet/wallet.go index 0479e57..ddfd1ae 100644 --- a/wallet/wallet.go +++ b/wallet/wallet.go @@ -402,6 +402,11 @@ func NewWallet(name, desc string, passphrase []byte, net btcwire.BitcoinNet, return nil, err } + // Verify root address keypairs. + if err := root.verifyKeypairs(); err != nil { + return nil, err + } + // Compute AES key and encrypt root address. kdfp, err := computeKdfParameters(defaultKdfComputeTime, defaultKdfMaxMem) if err != nil { @@ -754,6 +759,9 @@ func (w *Wallet) extendKeypool(n uint, aeskey []byte, bs *BlockStamp) error { if err != nil { return err } + if err := newaddr.verifyKeypairs(); err != nil { + return err + } if err = newaddr.encrypt(aeskey); err != nil { return err } @@ -1238,6 +1246,40 @@ func newRootBtcAddress(privKey, iv, chaincode []byte, return addr, err } +// verifyKeypairs creates a signature using the parsed private key and +// verifies the signature with the parsed public key. If either of these +// steps fail, the keypair generation failed and any funds sent to this +// address will be unspendable. This step requires an unencrypted or +// unlocked btcAddress. +func (a *btcAddress) verifyKeypairs() error { + // Parse public key. + pubkey, err := btcec.ParsePubKey(a.pubKey, btcec.S256()) + if err != nil { + return err + } + + if len(a.privKeyCT.key) != 32 { + return errors.New("private key unavailable") + } + + privkey := &ecdsa.PrivateKey{ + PublicKey: *pubkey, + D: new(big.Int).SetBytes(a.privKeyCT.key), + } + + data := "String to sign." + r, s, err := ecdsa.Sign(rand.Reader, privkey, []byte(data)) + if err != nil { + return err + } + + ok := ecdsa.Verify(&privkey.PublicKey, []byte(data), r, s) + if !ok { + return errors.New("ecdsa verification failed") + } + return nil +} + // ReadFrom reads an encrypted address from an io.Reader. func (a *btcAddress) ReadFrom(r io.Reader) (n int64, err error) { var read int64