Merge pull request #396 from jayschwa/nsis-branding
authorJeff Garzik <jgarzik@exmulti.com>
Wed, 13 Jul 2011 02:18:21 +0000 (19:18 -0700)
committerJeff Garzik <jgarzik@exmulti.com>
Wed, 13 Jul 2011 02:18:21 +0000 (19:18 -0700)
Add bitcoin.org logos/branding to NSIS installer.

24 files changed:
doc/README
share/uiproject.fbp
src/crypter.cpp [new file with mode: 0644]
src/crypter.h [new file with mode: 0644]
src/db.cpp
src/db.h
src/init.cpp
src/key.h
src/keystore.cpp
src/keystore.h
src/main.cpp
src/main.h
src/makefile.mingw
src/makefile.osx
src/makefile.unix
src/rpc.cpp
src/script.cpp
src/serialize.h
src/ui.cpp
src/ui.h
src/uibase.cpp
src/uibase.h
src/wallet.cpp
src/wallet.h

index a4df4c2..421c83c 100644 (file)
@@ -24,6 +24,44 @@ Unpack the files into a directory and run:
  bin/64/bitcoin (GUI, 64-bit)
  bin/64/bitcoind (headless, 64-bit)
 
+
+Wallet Encryption
+-----------------
+Bitcoin supports native wallet encryption so that people who steal your wallet
+file don't automatically get access to all of your Bitcoins.  In order to enable
+this feature, chose "Encrypt Wallet" from the Options menu.  You will be prompted
+to enter a passphrase, which will be used as the key to encrypt your wallet and
+will be needed every time you wish to send Bitcoins.  If you lose this passphrase,
+you will lose access to spend all of the bitcoins in your wallet, no one, not even
+the Bitcoin developers can recover your Bitcoins.  This means you are responsible
+for your own security, store your password in a secure location and do not forget
+it.
+
+Remember that the encryption built into bitcoin only encrypts the actual keys
+which are required to send your bitcoins, not the full wallet.  This means that
+someone who steals your wallet file will be able to see all the addresses which
+belong to you, as well as the relevant transactions, you are only protected from
+someone spending your coins.
+
+It is recommended that you backup your wallet file before you encrypt your wallet.
+To do this, close the Bitcoin client and copy the wallet.dat file from ~/.bitcoin/
+on Linux, /Users/(user name)/Application Support/Bitcoin/ on Mac OSX, and
+%APPDATA%/Bitcoin/ on Windows (that is /Users/(user name)/AppData/Roaming/Bitcoin on
+Windows Vista and 7 and /Documents and Settings/(user name)/Application Data/Bitcoin
+on Windows XP).  Once you have copied that file to a safe location, reopen the
+Bitcoin client and Encrypt your wallet.  If everything goes fine, delete the backup
+and enjoy your encrypted wallet.  Note that once you encrypt your wallet, you will
+never be able to go back to a version of the Bitcoin client older than 0.4.
+
+Keep in mind that you are always responsible for you own security.  All it takes is a
+slightly more advanced wallet-stealing trojan which installs a keylogger to steal
+your wallet passphrase as you enter it in addition to your wallet file and you have
+lost all your Bitcoins.  Wallet encryption cannot keep you safe if you do not practice
+good security, such as running up-to-date antivirus software, only entering your
+wallet passphrase in the Bitcoin client and using the same passphrase only as your
+wallet passphrase.
+
+
 See the documentation at the bitcoin wiki:
   https://en.bitcoin.it/wiki/Main_Page
 
index d9d4638..d5e6b1e 100644 (file)
                         <property name="checked">0</property>\r
                         <property name="enabled">1</property>\r
                         <property name="help"></property>\r
+                        <property name="id">wxID_ANY</property>\r
+                        <property name="kind">wxITEM_NORMAL</property>\r
+                        <property name="label">&amp;Encrypt Wallet...</property>\r
+                        <property name="name">m_menuOptionsEncryptWallet</property>\r
+                        <property name="permission">public</property>\r
+                        <property name="shortcut"></property>\r
+                        <property name="unchecked_bitmap"></property>\r
+                        <event name="OnMenuSelection">OnMenuOptionsEncryptWallet</event>\r
+                        <event name="OnUpdateUI"></event>\r
+                    </object>\r
+                    <object class="wxMenuItem" expanded="1">\r
+                        <property name="bitmap"></property>\r
+                        <property name="checked">0</property>\r
+                        <property name="enabled">1</property>\r
+                        <property name="help"></property>\r
+                        <property name="id">wxID_ANY</property>\r
+                        <property name="kind">wxITEM_NORMAL</property>\r
+                        <property name="label">&amp;Change Wallet Encryption Passphrase...</property>\r
+                        <property name="name">m_menuOptionsChangeWalletPassphrase</property>\r
+                        <property name="permission">public</property>\r
+                        <property name="shortcut"></property>\r
+                        <property name="unchecked_bitmap"></property>\r
+                        <event name="OnMenuSelection">OnMenuOptionsChangeWalletPassphrase</event>\r
+                        <event name="OnUpdateUI"></event>\r
+                    </object>\r
+                    <object class="wxMenuItem" expanded="1">\r
+                        <property name="bitmap"></property>\r
+                        <property name="checked">0</property>\r
+                        <property name="enabled">1</property>\r
+                        <property name="help"></property>\r
                         <property name="id">wxID_PREFERENCES</property>\r
                         <property name="kind">wxITEM_NORMAL</property>\r
                         <property name="label">&amp;Options...</property>\r
diff --git a/src/crypter.cpp b/src/crypter.cpp
new file mode 100644 (file)
index 0000000..9a8e6ca
--- /dev/null
@@ -0,0 +1,132 @@
+// Copyright (c) 2011 The Bitcoin Developers
+// Distributed under the MIT/X11 software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <openssl/aes.h>
+#include <openssl/evp.h>
+#include <vector>
+#include <string>
+#include "headers.h"
+#ifdef __WXMSW__
+#include <windows.h>
+#endif
+
+#include "crypter.h"
+#include "main.h"
+#include "util.h"
+
+bool CCrypter::SetKeyFromPassphrase(const std::string& strKeyData, const std::vector<unsigned char>& chSalt, const unsigned int nRounds, const unsigned int nDerivationMethod)
+{
+    if (nRounds < 1 || chSalt.size() != WALLET_CRYPTO_SALT_SIZE)
+        return false;
+
+    // Try to keep the keydata out of swap (and be a bit over-careful to keep the IV that we don't even use out of swap)
+    // Note that this does nothing about suspend-to-disk (which will put all our key data on disk)
+    // Note as well that at no point in this program is any attempt made to prevent stealing of keys by reading the memory of the running process.  
+    mlock(&chKey[0], sizeof chKey);
+    mlock(&chIV[0], sizeof chIV);
+
+    int i = 0;
+    if (nDerivationMethod == 0)
+        i = EVP_BytesToKey(EVP_aes_256_cbc(), EVP_sha512(), &chSalt[0],
+                          (unsigned char *)&strKeyData[0], strKeyData.size(), nRounds, chKey, chIV);
+
+    if (i != WALLET_CRYPTO_KEY_SIZE)
+    {
+        memset(&chKey, 0, sizeof chKey);
+        memset(&chIV, 0, sizeof chIV);
+        return false;
+    }
+
+    fKeySet = true;
+    return true;
+}
+
+bool CCrypter::SetKey(const CKeyingMaterial& chNewKey, const std::vector<unsigned char>& chNewIV)
+{
+    if (chNewKey.size() != WALLET_CRYPTO_KEY_SIZE || chNewIV.size() != WALLET_CRYPTO_KEY_SIZE)
+        return false;
+
+    // Try to keep the keydata out of swap
+    // Note that this does nothing about suspend-to-disk (which will put all our key data on disk)
+    // Note as well that at no point in this program is any attempt made to prevent stealing of keys by reading the memory of the running process.  
+    mlock(&chKey[0], sizeof chKey);
+    mlock(&chIV[0], sizeof chIV);
+
+    memcpy(&chKey[0], &chNewKey[0], sizeof chKey);
+    memcpy(&chIV[0], &chNewIV[0], sizeof chIV);
+
+    fKeySet = true;
+    return true;
+}
+
+bool CCrypter::Encrypt(const CKeyingMaterial& vchPlaintext, std::vector<unsigned char> &vchCiphertext)
+{
+    if (!fKeySet)
+        return false;
+
+    // max ciphertext len for a n bytes of plaintext is
+    // n + AES_BLOCK_SIZE - 1 bytes
+    int nLen = vchPlaintext.size();
+    int nCLen = nLen + AES_BLOCK_SIZE, nFLen = 0;
+    vchCiphertext = std::vector<unsigned char> (nCLen);
+
+    EVP_CIPHER_CTX ctx;
+
+    EVP_CIPHER_CTX_init(&ctx);
+    EVP_EncryptInit_ex(&ctx, EVP_aes_256_cbc(), NULL, chKey, chIV);
+
+    EVP_EncryptUpdate(&ctx, &vchCiphertext[0], &nCLen, &vchPlaintext[0], nLen);
+    EVP_EncryptFinal_ex(&ctx, (&vchCiphertext[0])+nCLen, &nFLen);
+
+    EVP_CIPHER_CTX_cleanup(&ctx);
+
+    vchCiphertext.resize(nCLen + nFLen);
+    return true;
+}
+
+bool CCrypter::Decrypt(const std::vector<unsigned char>& vchCiphertext, CKeyingMaterial& vchPlaintext)
+{
+    if (!fKeySet)
+        return false;
+
+    // plaintext will always be equal to or lesser than length of ciphertext
+    int nLen = vchCiphertext.size();
+    int nPLen = nLen, nFLen = 0;
+
+    vchPlaintext = CKeyingMaterial(nPLen);
+
+    EVP_CIPHER_CTX ctx;
+
+    EVP_CIPHER_CTX_init(&ctx);
+    EVP_DecryptInit_ex(&ctx, EVP_aes_256_cbc(), NULL, chKey, chIV);
+
+    EVP_DecryptUpdate(&ctx, &vchPlaintext[0], &nPLen, &vchCiphertext[0], nLen);
+    EVP_DecryptFinal_ex(&ctx, (&vchPlaintext[0])+nPLen, &nFLen);
+
+    EVP_CIPHER_CTX_cleanup(&ctx);
+
+    vchPlaintext.resize(nPLen + nFLen);
+    return true;
+}
+
+
+bool EncryptSecret(CKeyingMaterial& vMasterKey, const CSecret &vchPlaintext, const uint256& nIV, std::vector<unsigned char> &vchCiphertext)
+{
+    CCrypter cKeyCrypter;
+    std::vector<unsigned char> chIV(WALLET_CRYPTO_KEY_SIZE);
+    memcpy(&chIV[0], &nIV, WALLET_CRYPTO_KEY_SIZE);
+    if(!cKeyCrypter.SetKey(vMasterKey, chIV))
+        return false;
+    return cKeyCrypter.Encrypt((CKeyingMaterial)vchPlaintext, vchCiphertext);
+}
+
+bool DecryptSecret(const CKeyingMaterial& vMasterKey, const std::vector<unsigned char>& vchCiphertext, const uint256& nIV, CSecret& vchPlaintext)
+{
+    CCrypter cKeyCrypter;
+    std::vector<unsigned char> chIV(WALLET_CRYPTO_KEY_SIZE);
+    memcpy(&chIV[0], &nIV, WALLET_CRYPTO_KEY_SIZE);
+    if(!cKeyCrypter.SetKey(vMasterKey, chIV))
+        return false;
+    return cKeyCrypter.Decrypt(vchCiphertext, *((CKeyingMaterial*)&vchPlaintext));
+}
diff --git a/src/crypter.h b/src/crypter.h
new file mode 100644 (file)
index 0000000..5b95ea4
--- /dev/null
@@ -0,0 +1,96 @@
+// Copyright (c) 2011 The Bitcoin Developers
+// Distributed under the MIT/X11 software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#ifndef __CRYPTER_H__
+#define __CRYPTER_H__
+
+#include "key.h"
+
+const unsigned int WALLET_CRYPTO_KEY_SIZE = 32;
+const unsigned int WALLET_CRYPTO_SALT_SIZE = 8;
+
+/*
+Private key encryption is done based on a CMasterKey,
+which holds a salt and random encryption key.
+
+CMasterKeys is encrypted using AES-256-CBC using a key
+derived using derivation method nDerivationMethod
+(0 == EVP_sha512()) and derivation iterations nDeriveIterations.
+vchOtherDerivationParameters is provided for alternative algorithms
+which may require more parameters (such as scrypt).
+
+Wallet Private Keys are then encrypted using AES-256-CBC
+with the double-sha256 of the private key as the IV, and the
+master key's key as the encryption key.
+*/
+
+class CMasterKey
+{
+public:
+    std::vector<unsigned char> vchCryptedKey;
+    std::vector<unsigned char> vchSalt;
+    // 0 = EVP_sha512()
+    // 1 = scrypt()
+    unsigned int nDerivationMethod;
+    unsigned int nDeriveIterations;
+    // Use this for more parameters to key derivation,
+    // such as the various parameters to scrypt
+    std::vector<unsigned char> vchOtherDerivationParameters;
+
+    IMPLEMENT_SERIALIZE
+    (
+        READWRITE(vchCryptedKey);
+        READWRITE(vchSalt);
+        READWRITE(nDerivationMethod);
+        READWRITE(nDeriveIterations);
+        READWRITE(vchOtherDerivationParameters);
+    )
+    CMasterKey()
+    {
+        // 25000 rounds is just under 0.1 seconds on a 1.86 GHz Pentium M
+        // ie slightly lower than the lowest hardware we need bother supporting
+        nDeriveIterations = 25000;
+        nDerivationMethod = 0;
+        vchOtherDerivationParameters = std::vector<unsigned char>(0);
+    }
+};
+
+typedef std::vector<unsigned char, secure_allocator<unsigned char> > CKeyingMaterial;
+
+class CCrypter
+{
+private:
+    unsigned char chKey[WALLET_CRYPTO_KEY_SIZE];
+    unsigned char chIV[WALLET_CRYPTO_KEY_SIZE];
+    bool fKeySet;
+
+public:
+    bool SetKeyFromPassphrase(const std::string &strKeyData, const std::vector<unsigned char>& chSalt, const unsigned int nRounds, const unsigned int nDerivationMethod);
+    bool Encrypt(const CKeyingMaterial& vchPlaintext, std::vector<unsigned char> &vchCiphertext);
+    bool Decrypt(const std::vector<unsigned char>& vchCiphertext, CKeyingMaterial& vchPlaintext);
+    bool SetKey(const CKeyingMaterial& chNewKey, const std::vector<unsigned char>& chNewIV);
+
+    void CleanKey()
+    {
+        memset(&chKey, 0, sizeof chKey);
+        memset(&chIV, 0, sizeof chIV);
+        munlock(&chKey, sizeof chKey);
+        munlock(&chIV, sizeof chIV);
+        fKeySet = false;
+    }
+
+    CCrypter()
+    {
+        fKeySet = false;
+    }
+
+    ~CCrypter()
+    {
+        CleanKey();
+    }
+};
+
+bool EncryptSecret(CKeyingMaterial& vMasterKey, const CSecret &vchPlaintext, const uint256& nIV, std::vector<unsigned char> &vchCiphertext);
+bool DecryptSecret(const CKeyingMaterial& vMasterKey, const std::vector<unsigned char> &vchCiphertext, const uint256& nIV, CSecret &vchPlaintext);
+
+#endif
index f044355..ebe7e3a 100644 (file)
@@ -685,7 +685,7 @@ bool CWalletDB::LoadWallet(CWallet* pwallet)
 
     //// todo: shouldn't we catch exceptions and try to recover and continue?
     CRITICAL_BLOCK(pwallet->cs_mapWallet)
-    CRITICAL_BLOCK(pwallet->cs_mapKeys)
+    CRITICAL_BLOCK(pwallet->cs_KeyStore)
     {
         // Get cursor
         Dbc* pcursor = GetCursor();
@@ -765,14 +765,42 @@ bool CWalletDB::LoadWallet(CWallet* pwallet)
             {
                 vector<unsigned char> vchPubKey;
                 ssKey >> vchPubKey;
-                CWalletKey wkey;
+                CKey key;
                 if (strType == "key")
-                    ssValue >> wkey.vchPrivKey;
+                {
+                    CPrivKey pkey;
+                    ssValue >> pkey;
+                    key.SetPrivKey(pkey);
+                }
                 else
+                {
+                    CWalletKey wkey;
                     ssValue >> wkey;
-
-                pwallet->mapKeys[vchPubKey] = wkey.vchPrivKey;
-                mapPubKeys[Hash160(vchPubKey)] = vchPubKey;
+                    key.SetPrivKey(wkey.vchPrivKey);
+                }
+                if (!pwallet->LoadKey(key))
+                    return false;
+            }
+            else if (strType == "mkey")
+            {
+                unsigned int nID;
+                ssKey >> nID;
+                CMasterKey kMasterKey;
+                ssValue >> kMasterKey;
+                if(pwallet->mapMasterKeys.count(nID) != 0)
+                    return false;
+                pwallet->mapMasterKeys[nID] = kMasterKey;
+                if (pwallet->nMasterKeyMaxID < nID)
+                    pwallet->nMasterKeyMaxID = nID;
+            }
+            else if (strType == "ckey")
+            {
+                vector<unsigned char> vchPubKey;
+                ssKey >> vchPubKey;
+                vector<unsigned char> vchPrivKey;
+                ssValue >> vchPrivKey;
+                if (!pwallet->LoadCryptedKey(vchPubKey, vchPrivKey))
+                    return false;
             }
             else if (strType == "defaultkey")
             {
@@ -800,7 +828,6 @@ bool CWalletDB::LoadWallet(CWallet* pwallet)
                 if (strKey == "fGenerateBitcoins")  ssValue >> fGenerateBitcoins;
 #endif
                 if (strKey == "nTransactionFee")    ssValue >> nTransactionFee;
-                if (strKey == "addrIncoming")       ssValue >> addrIncoming;
                 if (strKey == "fLimitProcessors")   ssValue >> fLimitProcessors;
                 if (strKey == "nLimitProcessors")   ssValue >> nLimitProcessors;
                 if (strKey == "fMinimizeToTray")    ssValue >> fMinimizeToTray;
@@ -819,7 +846,6 @@ bool CWalletDB::LoadWallet(CWallet* pwallet)
     printf("nFileVersion = %d\n", nFileVersion);
     printf("fGenerateBitcoins = %d\n", fGenerateBitcoins);
     printf("nTransactionFee = %"PRI64d"\n", nTransactionFee);
-    printf("addrIncoming = %s\n", addrIncoming.ToString().c_str());
     printf("fMinimizeToTray = %d\n", fMinimizeToTray);
     printf("fMinimizeOnClose = %d\n", fMinimizeOnClose);
     printf("fUseProxy = %d\n", fUseProxy);
index b89b34e..c8d46d8 100644 (file)
--- a/src/db.h
+++ b/src/db.h
@@ -391,6 +391,25 @@ public:
         return Write(std::make_pair(std::string("key"), vchPubKey), vchPrivKey, false);
     }
 
+    bool WriteCryptedKey(const std::vector<unsigned char>& vchPubKey, const std::vector<unsigned char>& vchCryptedSecret, bool fEraseUnencryptedKey = true)
+    {
+        nWalletDBUpdated++;
+        if (!Write(std::make_pair(std::string("ckey"), vchPubKey), vchCryptedSecret, false))
+            return false;
+        if (fEraseUnencryptedKey)
+        {
+            Erase(std::make_pair(std::string("key"), vchPubKey));
+            Erase(std::make_pair(std::string("wkey"), vchPubKey));
+        }
+        return true;
+    }
+
+    bool WriteMasterKey(unsigned int nID, const CMasterKey& kMasterKey)
+    {
+        nWalletDBUpdated++;
+        return Write(std::make_pair(std::string("mkey"), nID), kMasterKey, true);
+    }
+
     bool WriteBestBlock(const CBlockLocator& locator)
     {
         nWalletDBUpdated++;
index 635799c..21b40d5 100644 (file)
@@ -416,7 +416,6 @@ bool AppInit2(int argc, char* argv[])
         //// debug print
         printf("mapBlockIndex.size() = %d\n",   mapBlockIndex.size());
         printf("nBestHeight = %d\n",            nBestHeight);
-        printf("mapKeys.size() = %d\n",         pwalletMain->mapKeys.size());
         printf("setKeyPool.size() = %d\n",      pwalletMain->setKeyPool.size());
         printf("mapPubKeys.size() = %d\n",      mapPubKeys.size());
         printf("mapWallet.size() = %d\n",       pwalletMain->mapWallet.size());
index c973d6e..c43e4ee 100644 (file)
--- a/src/key.h
+++ b/src/key.h
 // see www.keylength.com
 // script supports up to 75 for single byte push
 
+int static inline EC_KEY_regenerate_key(EC_KEY *eckey, BIGNUM *priv_key)
+{
+    int ok = 0;
+    BN_CTX *ctx = NULL;
+    EC_POINT *pub_key = NULL;
+
+    if (!eckey) return 0;
+
+    const EC_GROUP *group = EC_KEY_get0_group(eckey);
+
+    if ((ctx = BN_CTX_new()) == NULL)
+        goto err;
+
+    pub_key = EC_POINT_new(group);
+
+    if (pub_key == NULL)
+        goto err;
+
+    if (!EC_POINT_mul(group, pub_key, priv_key, NULL, NULL, ctx))
+        goto err;
+
+    EC_KEY_set_private_key(eckey,priv_key);
+    EC_KEY_set_public_key(eckey,pub_key);
+
+    ok = 1;
+
+err:
+
+    if (pub_key)
+        EC_POINT_free(pub_key);
+    if (ctx != NULL)
+        BN_CTX_free(ctx);
+
+    return(ok);
+}
 
 
 class key_error : public std::runtime_error
@@ -42,8 +77,7 @@ public:
 
 // secure_allocator is defined in serialize.h
 typedef std::vector<unsigned char, secure_allocator<unsigned char> > CPrivKey;
-
-
+typedef std::vector<unsigned char, secure_allocator<unsigned char> > CSecret;
 
 class CKey
 {
@@ -102,6 +136,38 @@ public:
         return true;
     }
 
+    bool SetSecret(const CSecret& vchSecret)
+    {
+        EC_KEY_free(pkey);
+        pkey = EC_KEY_new_by_curve_name(NID_secp256k1);
+        if (pkey == NULL)
+            throw key_error("CKey::SetSecret() : EC_KEY_new_by_curve_name failed");
+        if (vchSecret.size() != 32)
+            throw key_error("CKey::SetSecret() : secret must be 32 bytes");
+        BIGNUM *bn = BN_bin2bn(&vchSecret[0],32,BN_new());
+        if (bn == NULL) 
+            throw key_error("CKey::SetSecret() : BN_bin2bn failed");
+        if (!EC_KEY_regenerate_key(pkey,bn))
+            throw key_error("CKey::SetSecret() : EC_KEY_regenerate_key failed");
+        BN_clear_free(bn);
+        fSet = true;
+        return true;
+    }
+
+    CSecret GetSecret() const
+    {
+        CSecret vchRet;
+        vchRet.resize(32);
+        const BIGNUM *bn = EC_KEY_get0_private_key(pkey);
+        int nBytes = BN_num_bytes(bn);
+        if (bn == NULL)
+            throw key_error("CKey::GetSecret() : EC_KEY_get0_private_key failed");
+        int n=BN_bn2bin(bn,&vchRet[32 - nBytes]);
+        if (n != nBytes) 
+            throw key_error("CKey::GetSecret(): BN_bn2bin failed");
+        return vchRet;
+    }
+
     CPrivKey GetPrivKey() const
     {
         unsigned int nSize = i2d_ECPrivateKey(pkey, NULL);
@@ -154,22 +220,6 @@ public:
             return false;
         return true;
     }
-
-    static bool Sign(const CPrivKey& vchPrivKey, uint256 hash, std::vector<unsigned char>& vchSig)
-    {
-        CKey key;
-        if (!key.SetPrivKey(vchPrivKey))
-            return false;
-        return key.Sign(hash, vchSig);
-    }
-
-    static bool Verify(const std::vector<unsigned char>& vchPubKey, uint256 hash, const std::vector<unsigned char>& vchSig)
-    {
-        CKey key;
-        if (!key.SetPubKey(vchPubKey))
-            return false;
-        return key.Verify(hash, vchSig);
-    }
 };
 
 #endif
index 7dd045f..de13958 100644 (file)
@@ -4,13 +4,7 @@
 
 #include "headers.h"
 #include "db.h"
-
-
-
-//////////////////////////////////////////////////////////////////////////////
-//
-// mapKeys
-//
+#include "crypter.h"
 
 std::vector<unsigned char> CKeyStore::GenerateNewKey()
 {
@@ -18,16 +12,136 @@ std::vector<unsigned char> CKeyStore::GenerateNewKey()
     CKey key;
     key.MakeNewKey();
     if (!AddKey(key))
-        throw std::runtime_error("GenerateNewKey() : AddKey failed");
+        throw std::runtime_error("CKeyStore::GenerateNewKey() : AddKey failed");
     return key.GetPubKey();
 }
 
-bool CKeyStore::AddKey(const CKey& key)
+bool CBasicKeyStore::AddKey(const CKey& key)
 {
-    CRITICAL_BLOCK(cs_mapKeys)
+    CRITICAL_BLOCK(cs_mapPubKeys)
+    CRITICAL_BLOCK(cs_KeyStore)
     {
         mapKeys[key.GetPubKey()] = key.GetPrivKey();
         mapPubKeys[Hash160(key.GetPubKey())] = key.GetPubKey();
     }
+    return true;
+}
+
+std::vector<unsigned char> CCryptoKeyStore::GenerateNewKey()
+{
+    RandAddSeedPerfmon();
+    CKey key;
+    key.MakeNewKey();
+    if (!AddKey(key))
+        throw std::runtime_error("CCryptoKeyStore::GenerateNewKey() : AddKey failed");
+    return key.GetPubKey();
+}
+
+bool CCryptoKeyStore::Unlock(const CKeyingMaterial& vMasterKeyIn)
+{
+    CRITICAL_BLOCK(cs_vMasterKey)
+    {
+        if (!SetCrypted())
+            return false;
+
+        std::map<std::vector<unsigned char>, std::vector<unsigned char> >::const_iterator mi = mapCryptedKeys.begin();
+        for (; mi != mapCryptedKeys.end(); ++mi)
+        {
+            const std::vector<unsigned char> &vchPubKey = (*mi).first;
+            const std::vector<unsigned char> &vchCryptedSecret = (*mi).second;
+            CSecret vchSecret;
+            if(!DecryptSecret(vMasterKeyIn, vchCryptedSecret, Hash(vchPubKey.begin(), vchPubKey.end()), vchSecret))
+                return false;
+            CKey key;
+            key.SetSecret(vchSecret);
+            if (key.GetPubKey() == vchPubKey)
+                break;
+            return false;
+        }
+        vMasterKey = vMasterKeyIn;
+    }
+    return true;
 }
 
+bool CCryptoKeyStore::AddKey(const CKey& key)
+{
+    CRITICAL_BLOCK(cs_KeyStore)
+    CRITICAL_BLOCK(cs_vMasterKey)
+    {
+        if (!IsCrypted())
+            return CBasicKeyStore::AddKey(key);
+
+        if (IsLocked())
+            return false;
+
+        std::vector<unsigned char> vchCryptedSecret;
+        std::vector<unsigned char> vchPubKey = key.GetPubKey();
+        if (!EncryptSecret(vMasterKey, key.GetSecret(), Hash(vchPubKey.begin(), vchPubKey.end()), vchCryptedSecret))
+            return false;
+
+        if (!AddCryptedKey(key.GetPubKey(), vchCryptedSecret))
+            return false;
+    }
+    return true;
+}
+
+
+bool CCryptoKeyStore::AddCryptedKey(const std::vector<unsigned char> &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret)
+{
+    CRITICAL_BLOCK(cs_mapPubKeys)
+    CRITICAL_BLOCK(cs_KeyStore)
+    {
+        if (!SetCrypted())
+            return false;
+
+        mapCryptedKeys[vchPubKey] = vchCryptedSecret;
+        mapPubKeys[Hash160(vchPubKey)] = vchPubKey;
+    }
+    return true;
+}
+
+bool CCryptoKeyStore::GetPrivKey(const std::vector<unsigned char> &vchPubKey, CKey& keyOut) const
+{
+    CRITICAL_BLOCK(cs_vMasterKey)
+    {
+        if (!IsCrypted())
+            return CBasicKeyStore::GetPrivKey(vchPubKey, keyOut);
+
+        std::map<std::vector<unsigned char>, std::vector<unsigned char> >::const_iterator mi = mapCryptedKeys.find(vchPubKey);
+        if (mi != mapCryptedKeys.end())
+        {
+            const std::vector<unsigned char> &vchCryptedSecret = (*mi).second;
+            CSecret vchSecret;
+            if (!DecryptSecret(vMasterKey, (*mi).second, Hash((*mi).first.begin(), (*mi).first.end()), vchSecret))
+                return false;
+            keyOut.SetSecret(vchSecret);
+            return true;
+        }
+    }
+    return false;
+}
+
+bool CCryptoKeyStore::EncryptKeys(CKeyingMaterial& vMasterKeyIn)
+{
+    CRITICAL_BLOCK(cs_KeyStore)
+    CRITICAL_BLOCK(cs_vMasterKey)
+    {
+        if (!mapCryptedKeys.empty() || IsCrypted())
+            return false;
+
+        fUseCrypto = true;
+        CKey key;
+        BOOST_FOREACH(KeyMap::value_type& mKey, mapKeys)
+        {
+            if (!key.SetPrivKey(mKey.second))
+                return false;
+            std::vector<unsigned char> vchCryptedSecret;
+            if (!EncryptSecret(vMasterKeyIn, key.GetSecret(), Hash(mKey.first.begin(), mKey.first.end()), vchCryptedSecret))
+                return false;
+            if (!AddCryptedKey(mKey.first, vchCryptedSecret))
+                return false;
+        }
+        mapKeys.clear();
+    }
+    return true;
+}
index 6080d7d..0dc09f0 100644 (file)
 #ifndef BITCOIN_KEYSTORE_H
 #define BITCOIN_KEYSTORE_H
 
+#include "crypter.h"
+
 class CKeyStore
 {
 public:
-    std::map<std::vector<unsigned char>, CPrivKey> mapKeys;
-    mutable CCriticalSection cs_mapKeys;
-    virtual bool AddKey(const CKey& key);
+    mutable CCriticalSection cs_KeyStore;
+
+    virtual bool AddKey(const CKey& key) =0;
+    virtual bool HaveKey(const std::vector<unsigned char> &vchPubKey) const =0;
+    virtual bool GetPrivKey(const std::vector<unsigned char> &vchPubKey, CKey& keyOut) const =0;
+    virtual std::vector<unsigned char> GenerateNewKey();
+};
+
+typedef std::map<std::vector<unsigned char>, CPrivKey> KeyMap;
+
+class CBasicKeyStore : public CKeyStore
+{
+protected:
+    KeyMap mapKeys;
+
+public:
+    bool AddKey(const CKey& key);
     bool HaveKey(const std::vector<unsigned char> &vchPubKey) const
     {
         return (mapKeys.count(vchPubKey) > 0);
     }
-    bool GetPrivKey(const std::vector<unsigned char> &vchPubKey, CPrivKey& keyOut) const
+    bool GetPrivKey(const std::vector<unsigned char> &vchPubKey, CKey& keyOut) const
     {
         std::map<std::vector<unsigned char>, CPrivKey>::const_iterator mi = mapKeys.find(vchPubKey);
         if (mi != mapKeys.end())
         {
-            keyOut = (*mi).second;
+            keyOut.SetPrivKey((*mi).second);
             return true;
         }
         return false;
     }
+};
+
+class CCryptoKeyStore : public CBasicKeyStore
+{
+private:
+    std::map<std::vector<unsigned char>, std::vector<unsigned char> > mapCryptedKeys;
+
+    CKeyingMaterial vMasterKey;
+
+    // if fUseCrypto is true, mapKeys must be empty
+    // if fUseCrypto is false, vMasterKey must be empty
+    bool fUseCrypto;
+
+protected:
+    bool SetCrypted()
+    {
+        if (fUseCrypto)
+            return true;
+        if (!mapKeys.empty())
+            return false;
+        fUseCrypto = true;
+        return true;
+    }
+
+    // will encrypt previously unencrypted keys
+    bool EncryptKeys(CKeyingMaterial& vMasterKeyIn);
+
+    bool Unlock(const CKeyingMaterial& vMasterKeyIn);
+
+public:
+    mutable CCriticalSection cs_vMasterKey; //No guarantees master key wont get locked before you can use it, so lock this first
+
+    CCryptoKeyStore() : fUseCrypto(false)
+    {
+    }
+
+    bool IsCrypted() const
+    {
+        return fUseCrypto;
+    }
+
+    bool IsLocked() const
+    {
+        if (!IsCrypted())
+            return false;
+        return vMasterKey.empty();
+    }
+
+    bool Lock()
+    {
+        CRITICAL_BLOCK(cs_vMasterKey)
+        {
+            if (!SetCrypted())
+                return false;
+
+            vMasterKey.clear();
+        }
+        return true;
+    }
+
+    virtual bool AddCryptedKey(const std::vector<unsigned char> &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret);
     std::vector<unsigned char> GenerateNewKey();
+    bool AddKey(const CKey& key);
+    bool HaveKey(const std::vector<unsigned char> &vchPubKey) const
+    {
+        if (!IsCrypted())
+            return CBasicKeyStore::HaveKey(vchPubKey);
+        return mapCryptedKeys.count(vchPubKey) > 0;
+    }
+    bool GetPrivKey(const std::vector<unsigned char> &vchPubKey, CKey& keyOut) const;
 };
 
 #endif
index 594f1d3..6e97349 100644 (file)
@@ -55,7 +55,6 @@ int64 nHPSTimerStart;
 // Settings
 int fGenerateBitcoins = false;
 int64 nTransactionFee = 0;
-CAddress addrIncoming;
 int fLimitProcessors = false;
 int nLimitProcessors = 1;
 int fMinimizeToTray = true;
@@ -2213,7 +2212,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
 
         // Keep giving the same key to the same ip until they use it
         if (!mapReuseKey.count(pfrom->addr.ip))
-            mapReuseKey[pfrom->addr.ip] = pwalletMain->GetKeyFromKeyPool();
+            mapReuseKey[pfrom->addr.ip] = pwalletMain->GetOrReuseKeyFromPool();
 
         // Send back approval of order and pubkey to use
         CScript scriptPubKey;
index 124c7c2..d34f68f 100644 (file)
@@ -70,7 +70,6 @@ extern std::set<CWallet*> setpwalletRegistered;
 // Settings
 extern int fGenerateBitcoins;
 extern int64 nTransactionFee;
-extern CAddress addrIncoming;
 extern int fLimitProcessors;
 extern int nLimitProcessors;
 extern int fMinimizeToTray;
index 507833b..16a0540 100644 (file)
@@ -33,7 +33,8 @@ DEFS=-DWIN32 -D__WXMSW__ -D_WINDOWS -DNOPCH -DUSE_SSL
 DEBUGFLAGS=-g -D__WXDEBUG__
 CFLAGS=-mthreads -O2 -w -Wno-invalid-offsetof -Wformat $(DEBUGFLAGS) $(DEFS) $(INCLUDEPATHS)
 HEADERS=headers.h strlcpy.h serialize.h uint256.h util.h key.h bignum.h base58.h \
-    script.h db.h net.h irc.h keystore.h main.h wallet.h rpc.h uibase.h ui.h noui.h init.h
+    script.h db.h net.h irc.h keystore.h main.h wallet.h rpc.h uibase.h ui.h noui.h \
+    init.h crypter.h
 
 ifdef USE_UPNP
  INCLUDEPATHS += -I"C:\upnpc-exe-win32-20110215"
@@ -55,6 +56,7 @@ OBJS= \
     obj/wallet.o \
     obj/rpc.o \
     obj/init.o \
+    obj/crypter.o \
     cryptopp/obj/sha.o \
     cryptopp/obj/cpu.o
 
index 784596b..8978856 100644 (file)
@@ -33,7 +33,8 @@ DEBUGFLAGS=-g -DwxDEBUG_LEVEL=0
 # ppc doesn't work because we don't support big-endian
 CFLAGS=-mmacosx-version-min=10.5 -arch i386 -arch x86_64 -O3 -Wno-invalid-offsetof -Wformat $(DEBUGFLAGS) $(DEFS) $(INCLUDEPATHS)
 HEADERS=headers.h strlcpy.h serialize.h uint256.h util.h key.h bignum.h base58.h \
-    script.h db.h net.h irc.h keystore.h main.h wallet.h rpc.h uibase.h ui.h noui.h init.h
+    script.h db.h net.h irc.h keystore.h main.h wallet.h rpc.h uibase.h ui.h noui.h \
+    init.h crypter.h
 
 OBJS= \
     obj/util.o \
@@ -46,6 +47,7 @@ OBJS= \
     obj/wallet.o \
     obj/rpc.o \
     obj/init.o \
+    obj/crypter.o \
     cryptopp/obj/sha.o \
     cryptopp/obj/cpu.o
 
index bb26bf5..0567f8b 100644 (file)
@@ -39,7 +39,8 @@ LIBS+= \
 DEBUGFLAGS=-g -D__WXDEBUG__
 CXXFLAGS=-O2 -Wno-invalid-offsetof -Wformat $(DEBUGFLAGS) $(DEFS)
 HEADERS=headers.h strlcpy.h serialize.h uint256.h util.h key.h bignum.h base58.h \
-    script.h db.h net.h irc.h keystore.h main.h wallet.h rpc.h uibase.h ui.h noui.h init.h
+    script.h db.h net.h irc.h keystore.h main.h wallet.h rpc.h uibase.h ui.h noui.h \
+    init.h crypter.h
 
 OBJS= \
     obj/util.o \
@@ -52,6 +53,7 @@ OBJS= \
     obj/wallet.o \
     obj/rpc.o \
     obj/init.o \
+    obj/crypter.o \
     cryptopp/obj/sha.o \
     cryptopp/obj/cpu.o
 
index 6f951b7..fbed626 100644 (file)
@@ -36,6 +36,9 @@ void ThreadRPCServer2(void* parg);
 typedef Value(*rpcfn_type)(const Array& params, bool fHelp);
 extern map<string, rpcfn_type> mapCallTable;
 
+static int64 nWalletUnlockTime;
+static CCriticalSection cs_nWalletUnlockTime;
+
 
 Object JSONRPCError(int code, const string& message)
 {
@@ -309,7 +312,10 @@ Value getinfo(const Array& params, bool fHelp)
     obj.push_back(Pair("hashespersec",  gethashespersec(params, false)));
     obj.push_back(Pair("testnet",       fTestNet));
     obj.push_back(Pair("keypoololdest", (boost::int64_t)pwalletMain->GetOldestKeyPoolTime()));
+    obj.push_back(Pair("keypoolsize",   pwalletMain->GetKeyPoolSize()));
     obj.push_back(Pair("paytxfee",      ValueFromAmount(nTransactionFee)));
+    if (pwalletMain->IsCrypted())
+        obj.push_back(Pair("unlocked_until", (boost::int64_t)nWalletUnlockTime));
     obj.push_back(Pair("errors",        GetWarnings("statusbar")));
     return obj;
 }
@@ -324,13 +330,19 @@ Value getnewaddress(const Array& params, bool fHelp)
             "If [account] is specified (recommended), it is added to the address book "
             "so payments received with the address will be credited to [account].");
 
+    if (!pwalletMain->IsLocked())
+        pwalletMain->TopUpKeyPool();
+
+    if (pwalletMain->GetKeyPoolSize() < 1)
+        throw JSONRPCError(-12, "Error: Keypool ran out, please call keypoolrefill first");
+
     // Parse the account first so we don't generate a key if there's an error
     string strAccount;
     if (params.size() > 0)
         strAccount = AccountFromValue(params[0]);
 
     // Generate a new key that is added to wallet
-    string strAddress = PubKeyToAddress(pwalletMain->GetKeyFromKeyPool());
+    string strAddress = PubKeyToAddress(pwalletMain->GetOrReuseKeyFromPool());
 
     // This could be done in the same main CS as GetKeyFromKeyPool.
     CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook)
@@ -346,37 +358,48 @@ string GetAccountAddress(string strAccount, bool bForceNew=false)
     string strAddress;
 
     CWalletDB walletdb(pwalletMain->strWalletFile);
-    walletdb.TxnBegin();
 
     CAccount account;
-    walletdb.ReadAccount(strAccount, account);
-
-    // Check if the current key has been used
-    if (!account.vchPubKey.empty())
+    CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook)
     {
-        CScript scriptPubKey;
-        scriptPubKey.SetBitcoinAddress(account.vchPubKey);
-        for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin();
-             it != pwalletMain->mapWallet.end() && !account.vchPubKey.empty();
-             ++it)
+        walletdb.ReadAccount(strAccount, account);
+
+        bool bKeyUsed = false;
+
+        // Check if the current key has been used
+        if (!account.vchPubKey.empty())
         {
-            const CWalletTx& wtx = (*it).second;
-            BOOST_FOREACH(const CTxOut& txout, wtx.vout)
-                if (txout.scriptPubKey == scriptPubKey)
-                    account.vchPubKey.clear();
+            CScript scriptPubKey;
+            scriptPubKey.SetBitcoinAddress(account.vchPubKey);
+            for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin();
+                 it != pwalletMain->mapWallet.end() && !account.vchPubKey.empty();
+                 ++it)
+            {
+                const CWalletTx& wtx = (*it).second;
+                BOOST_FOREACH(const CTxOut& txout, wtx.vout)
+                    if (txout.scriptPubKey == scriptPubKey)
+                        bKeyUsed = true;
+            }
         }
-    }
 
-    // Generate a new key
-    if (account.vchPubKey.empty() || bForceNew)
-    {
-        account.vchPubKey = pwalletMain->GetKeyFromKeyPool();
-        string strAddress = PubKeyToAddress(account.vchPubKey);
-        pwalletMain->SetAddressBookName(strAddress, strAccount);
-        walletdb.WriteAccount(strAccount, account);
+        // Generate a new key
+        if (account.vchPubKey.empty() || bForceNew || bKeyUsed)
+        {
+            if (pwalletMain->GetKeyPoolSize() < 1)
+            {
+                if (bKeyUsed || bForceNew)
+                    throw JSONRPCError(-12, "Error: Keypool ran out, please call topupkeypool first");
+            }
+            else
+            {
+                account.vchPubKey = pwalletMain->GetOrReuseKeyFromPool();
+                string strAddress = PubKeyToAddress(account.vchPubKey);
+                pwalletMain->SetAddressBookName(strAddress, strAccount);
+                walletdb.WriteAccount(strAccount, account);
+            }
+        }
     }
 
-    walletdb.TxnCommit();
     strAddress = PubKeyToAddress(account.vchPubKey);
 
     return strAddress;
@@ -510,7 +533,12 @@ Value settxfee(const Array& params, bool fHelp)
 
 Value sendtoaddress(const Array& params, bool fHelp)
 {
-    if (fHelp || params.size() < 2 || params.size() > 4)
+    if (pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 4))
+        throw runtime_error(
+            "sendtoaddress <bitcoinaddress> <amount> [comment] [comment-to]\n"
+            "<amount> is a real and is rounded to the nearest 0.00000001\n"
+            "requires wallet passphrase to be set with walletpassphrase first");
+    if (!pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 4))
         throw runtime_error(
             "sendtoaddress <bitcoinaddress> <amount> [comment] [comment-to]\n"
             "<amount> is a real and is rounded to the nearest 0.00000001");
@@ -528,7 +556,11 @@ Value sendtoaddress(const Array& params, bool fHelp)
         wtx.mapValue["to"]      = params[3].get_str();
 
     CRITICAL_BLOCK(cs_main)
+    CRITICAL_BLOCK(pwalletMain->cs_vMasterKey)
     {
+        if(pwalletMain->IsLocked())
+            throw JSONRPCError(-14, "Error: The wallet passphrase entered was incorrect.");
+
         string strError = pwalletMain->SendMoneyToBitcoinAddress(strAddress, nAmount, wtx);
         if (strError != "")
             throw JSONRPCError(-4, strError);
@@ -773,7 +805,12 @@ Value movecmd(const Array& params, bool fHelp)
 
 Value sendfrom(const Array& params, bool fHelp)
 {
-    if (fHelp || params.size() < 3 || params.size() > 6)
+    if (pwalletMain->IsCrypted() && (fHelp || params.size() < 3 || params.size() > 6))
+        throw runtime_error(
+            "sendfrom <fromaccount> <tobitcoinaddress> <amount> [minconf=1] [comment] [comment-to]\n"
+            "<amount> is a real and is rounded to the nearest 0.00000001\n"
+            "requires wallet passphrase to be set with walletpassphrase first");
+    if (!pwalletMain->IsCrypted() && (fHelp || params.size() < 3 || params.size() > 6))
         throw runtime_error(
             "sendfrom <fromaccount> <tobitcoinaddress> <amount> [minconf=1] [comment] [comment-to]\n"
             "<amount> is a real and is rounded to the nearest 0.00000001");
@@ -794,7 +831,11 @@ Value sendfrom(const Array& params, bool fHelp)
 
     CRITICAL_BLOCK(cs_main)
     CRITICAL_BLOCK(pwalletMain->cs_mapWallet)
+    CRITICAL_BLOCK(pwalletMain->cs_vMasterKey)
     {
+        if(pwalletMain->IsLocked())
+            throw JSONRPCError(-14, "Error: The wallet passphrase entered was incorrect.");
+
         // Check funds
         int64 nBalance = GetAccountBalance(strAccount, nMinDepth);
         if (nAmount > nBalance)
@@ -809,9 +850,15 @@ Value sendfrom(const Array& params, bool fHelp)
     return wtx.GetHash().GetHex();
 }
 
+
 Value sendmany(const Array& params, bool fHelp)
 {
-    if (fHelp || params.size() < 2 || params.size() > 4)
+    if (pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 4))
+        throw runtime_error(
+            "sendmany <fromaccount> {address:amount,...} [minconf=1] [comment]\n"
+            "amounts are double-precision floating point numbers\n"
+            "requires wallet passphrase to be set with walletpassphrase first");
+    if (!pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 4))
         throw runtime_error(
             "sendmany <fromaccount> {address:amount,...} [minconf=1] [comment]\n"
             "amounts are double-precision floating point numbers");
@@ -851,7 +898,11 @@ Value sendmany(const Array& params, bool fHelp)
 
     CRITICAL_BLOCK(cs_main)
     CRITICAL_BLOCK(pwalletMain->cs_mapWallet)
+    CRITICAL_BLOCK(pwalletMain->cs_vMasterKey)
     {
+        if(pwalletMain->IsLocked())
+            throw JSONRPCError(-14, "Error: The wallet passphrase entered was incorrect.");
+
         // Check funds
         int64 nBalance = GetAccountBalance(strAccount, nMinDepth);
         if (totalAmount > nBalance)
@@ -1281,6 +1332,219 @@ Value backupwallet(const Array& params, bool fHelp)
 }
 
 
+Value keypoolrefill(const Array& params, bool fHelp)
+{
+    if (pwalletMain->IsCrypted() && (fHelp || params.size() > 0))
+        throw runtime_error(
+            "keypoolrefill\n"
+            "Fills the keypool, requires wallet passphrase to be set.");
+    if (!pwalletMain->IsCrypted() && (fHelp || params.size() > 0))
+        throw runtime_error(
+            "keypoolrefill\n"
+            "Fills the keypool.");
+
+    CRITICAL_BLOCK(pwalletMain->cs_vMasterKey)
+    {
+        if (pwalletMain->IsLocked())
+            throw JSONRPCError(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.");
+
+        pwalletMain->TopUpKeyPool();
+    }
+
+    if (pwalletMain->GetKeyPoolSize() < GetArg("-keypool", 100))
+        throw JSONRPCError(-4, "Error refreshing keypool.");
+
+    return Value::null;
+}
+
+
+void ThreadTopUpKeyPool(void* parg)
+{
+    pwalletMain->TopUpKeyPool();
+}
+
+void ThreadCleanWalletPassphrase(void* parg)
+{
+    int64 nMyWakeTime = GetTime() + *((int*)parg);
+
+    if (nWalletUnlockTime == 0)
+    {
+        CRITICAL_BLOCK(cs_nWalletUnlockTime)
+        {
+            nWalletUnlockTime = nMyWakeTime;
+        }
+
+        while (GetTime() < nWalletUnlockTime)
+            Sleep(GetTime() - nWalletUnlockTime);
+
+        CRITICAL_BLOCK(cs_nWalletUnlockTime)
+        {
+            nWalletUnlockTime = 0;
+        }
+    }
+    else
+    {
+        CRITICAL_BLOCK(cs_nWalletUnlockTime)
+        {
+            if (nWalletUnlockTime < nMyWakeTime)
+                nWalletUnlockTime = nMyWakeTime;
+        }
+        free(parg);
+        return;
+    }
+
+    pwalletMain->Lock();
+
+    delete (int*)parg;
+}
+
+Value walletpassphrase(const Array& params, bool fHelp)
+{
+    if (pwalletMain->IsCrypted() && (fHelp || params.size() != 2))
+        throw runtime_error(
+            "walletpassphrase <passphrase> <timeout>\n"
+            "Stores the wallet decryption key in memory for <timeout> seconds.");
+    if (fHelp)
+        return true;
+    if (!pwalletMain->IsCrypted())
+        throw JSONRPCError(-15, "Error: running with an unencrypted wallet, but walletpassphrase was called.");
+
+    if (!pwalletMain->IsLocked())
+        throw JSONRPCError(-17, "Error: Wallet is already unlocked.");
+
+    // Note that the walletpassphrase is stored in params[0] which is not mlock()ed
+    string strWalletPass;
+    strWalletPass.reserve(100);
+    mlock(&strWalletPass[0], strWalletPass.capacity());
+    strWalletPass = params[0].get_str();
+
+    CRITICAL_BLOCK(pwalletMain->cs_vMasterKey)
+    {
+        if (strWalletPass.length() > 0)
+        {
+            if (!pwalletMain->Unlock(strWalletPass))
+            {
+                fill(strWalletPass.begin(), strWalletPass.end(), '\0');
+                munlock(&strWalletPass[0], strWalletPass.capacity());
+                throw JSONRPCError(-14, "Error: The wallet passphrase entered was incorrect.");
+            }
+            fill(strWalletPass.begin(), strWalletPass.end(), '\0');
+            munlock(&strWalletPass[0], strWalletPass.capacity());
+        }
+        else
+            throw runtime_error(
+                "walletpassphrase <passphrase> <timeout>\n"
+                "Stores the wallet decryption key in memory for <timeout> seconds.");
+    }
+
+    CreateThread(ThreadTopUpKeyPool, NULL);
+    int* pnSleepTime = new int(params[1].get_int());
+    CreateThread(ThreadCleanWalletPassphrase, pnSleepTime);
+
+    return Value::null;
+}
+
+
+Value walletpassphrasechange(const Array& params, bool fHelp)
+{
+    if (pwalletMain->IsCrypted() && (fHelp || params.size() != 2))
+        throw runtime_error(
+            "walletpassphrasechange <oldpassphrase> <newpassphrase>\n"
+            "Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>.");
+    if (fHelp)
+        return true;
+    if (!pwalletMain->IsCrypted())
+        throw JSONRPCError(-15, "Error: running with an unencrypted wallet, but walletpassphrasechange was called.");
+
+    string strOldWalletPass;
+    strOldWalletPass.reserve(100);
+    mlock(&strOldWalletPass[0], strOldWalletPass.capacity());
+    strOldWalletPass = params[0].get_str();
+
+    string strNewWalletPass;
+    strNewWalletPass.reserve(100);
+    mlock(&strNewWalletPass[0], strNewWalletPass.capacity());
+    strNewWalletPass = params[1].get_str();
+
+    if (strOldWalletPass.length() < 1 || strNewWalletPass.length() < 1)
+        throw runtime_error(
+            "walletpassphrasechange <oldpassphrase> <newpassphrase>\n"
+            "Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>.");
+
+    if (!pwalletMain->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass))
+    {
+        fill(strOldWalletPass.begin(), strOldWalletPass.end(), '\0');
+        fill(strNewWalletPass.begin(), strNewWalletPass.end(), '\0');
+        munlock(&strOldWalletPass[0], strOldWalletPass.capacity());
+        munlock(&strNewWalletPass[0], strNewWalletPass.capacity());
+        throw JSONRPCError(-14, "Error: The wallet passphrase entered was incorrect.");
+    }
+    fill(strNewWalletPass.begin(), strNewWalletPass.end(), '\0');
+    fill(strOldWalletPass.begin(), strOldWalletPass.end(), '\0');
+    munlock(&strOldWalletPass[0], strOldWalletPass.capacity());
+    munlock(&strNewWalletPass[0], strNewWalletPass.capacity());
+
+    return Value::null;
+}
+
+
+Value walletlock(const Array& params, bool fHelp)
+{
+    if (pwalletMain->IsCrypted() && (fHelp || params.size() != 0))
+        throw runtime_error(
+            "walletlock\n"
+            "Removes the wallet encryption key from memory, locking the wallet.\n"
+            "After calling this method, you will need to call walletpassphrase again\n"
+            "before being able to call any methods which require the wallet to be unlocked.");
+    if (fHelp)
+        return true;
+    if (!pwalletMain->IsCrypted())
+        throw JSONRPCError(-15, "Error: running with an unencrypted wallet, but walletlock was called.");
+
+    pwalletMain->Lock();
+    CRITICAL_BLOCK(cs_nWalletUnlockTime)
+    {
+        nWalletUnlockTime = 0;
+    }
+
+    return Value::null;
+}
+
+
+Value encryptwallet(const Array& params, bool fHelp)
+{
+    if (!pwalletMain->IsCrypted() && (fHelp || params.size() != 1))
+        throw runtime_error(
+            "encryptwallet <passphrase>\n"
+            "Encrypts the wallet with <passphrase>.");
+    if (fHelp)
+        return true;
+    if (pwalletMain->IsCrypted())
+        throw JSONRPCError(-15, "Error: running with an encrypted wallet, but encryptwallet was called.");
+
+    string strWalletPass;
+    strWalletPass.reserve(100);
+    mlock(&strWalletPass[0], strWalletPass.capacity());
+    strWalletPass = params[0].get_str();
+
+    if (strWalletPass.length() < 1)
+        throw runtime_error(
+            "encryptwallet <passphrase>\n"
+            "Encrypts the wallet with <passphrase>.");
+
+    if (!pwalletMain->EncryptWallet(strWalletPass))
+    {
+        fill(strWalletPass.begin(), strWalletPass.end(), '\0');
+        munlock(&strWalletPass[0], strWalletPass.capacity());
+        throw JSONRPCError(-16, "Error: Failed to encrypt the wallet.");
+    }
+    fill(strWalletPass.begin(), strWalletPass.end(), '\0');
+    munlock(&strWalletPass[0], strWalletPass.capacity());
+
+    return Value::null;
+}
+
+
 Value validateaddress(const Array& params, bool fHelp)
 {
     if (fHelp || params.size() != 1)
@@ -1432,44 +1696,49 @@ Value getwork(const Array& params, bool fHelp)
 
 pair<string, rpcfn_type> pCallTable[] =
 {
-    make_pair("help",                  &help),
-    make_pair("stop",                  &stop),
-    make_pair("getblockcount",         &getblockcount),
-    make_pair("getblocknumber",        &getblocknumber),
-    make_pair("getconnectioncount",    &getconnectioncount),
-    make_pair("getdifficulty",         &getdifficulty),
-    make_pair("getgenerate",           &getgenerate),
-    make_pair("setgenerate",           &setgenerate),
-    make_pair("gethashespersec",       &gethashespersec),
-    make_pair("getinfo",               &getinfo),
-    make_pair("getnewaddress",         &getnewaddress),
-    make_pair("getaccountaddress",     &getaccountaddress),
-    make_pair("setaccount",            &setaccount),
-    make_pair("setlabel",              &setaccount), // deprecated
-    make_pair("getaccount",            &getaccount),
-    make_pair("getlabel",              &getaccount), // deprecated
-    make_pair("getaddressesbyaccount", &getaddressesbyaccount),
-    make_pair("getaddressesbylabel",   &getaddressesbyaccount), // deprecated
-    make_pair("sendtoaddress",         &sendtoaddress),
-    make_pair("getamountreceived",     &getreceivedbyaddress), // deprecated, renamed to getreceivedbyaddress
-    make_pair("getallreceived",        &listreceivedbyaddress), // deprecated, renamed to listreceivedbyaddress
-    make_pair("getreceivedbyaddress",  &getreceivedbyaddress),
-    make_pair("getreceivedbyaccount",  &getreceivedbyaccount),
-    make_pair("getreceivedbylabel",    &getreceivedbyaccount), // deprecated
-    make_pair("listreceivedbyaddress", &listreceivedbyaddress),
-    make_pair("listreceivedbyaccount", &listreceivedbyaccount),
-    make_pair("listreceivedbylabel",   &listreceivedbyaccount), // deprecated
-    make_pair("backupwallet",          &backupwallet),
-    make_pair("validateaddress",       &validateaddress),
-    make_pair("getbalance",            &getbalance),
-    make_pair("move",                  &movecmd),
-    make_pair("sendfrom",              &sendfrom),
-    make_pair("sendmany",              &sendmany),
-    make_pair("gettransaction",        &gettransaction),
-    make_pair("listtransactions",      &listtransactions),
-    make_pair("getwork",               &getwork),
-    make_pair("listaccounts",          &listaccounts),
-    make_pair("settxfee",              &settxfee),
+    make_pair("help",                   &help),
+    make_pair("stop",                   &stop),
+    make_pair("getblockcount",          &getblockcount),
+    make_pair("getblocknumber",         &getblocknumber),
+    make_pair("getconnectioncount",     &getconnectioncount),
+    make_pair("getdifficulty",          &getdifficulty),
+    make_pair("getgenerate",            &getgenerate),
+    make_pair("setgenerate",            &setgenerate),
+    make_pair("gethashespersec",        &gethashespersec),
+    make_pair("getinfo",                &getinfo),
+    make_pair("getnewaddress",          &getnewaddress),
+    make_pair("getaccountaddress",      &getaccountaddress),
+    make_pair("setaccount",             &setaccount),
+    make_pair("setlabel",               &setaccount), // deprecated
+    make_pair("getaccount",             &getaccount),
+    make_pair("getlabel",               &getaccount), // deprecated
+    make_pair("getaddressesbyaccount",  &getaddressesbyaccount),
+    make_pair("getaddressesbylabel",    &getaddressesbyaccount), // deprecated
+    make_pair("sendtoaddress",          &sendtoaddress),
+    make_pair("getamountreceived",      &getreceivedbyaddress), // deprecated, renamed to getreceivedbyaddress
+    make_pair("getallreceived",         &listreceivedbyaddress), // deprecated, renamed to listreceivedbyaddress
+    make_pair("getreceivedbyaddress",   &getreceivedbyaddress),
+    make_pair("getreceivedbyaccount",   &getreceivedbyaccount),
+    make_pair("getreceivedbylabel",     &getreceivedbyaccount), // deprecated
+    make_pair("listreceivedbyaddress",  &listreceivedbyaddress),
+    make_pair("listreceivedbyaccount",  &listreceivedbyaccount),
+    make_pair("listreceivedbylabel",    &listreceivedbyaccount), // deprecated
+    make_pair("backupwallet",           &backupwallet),
+    make_pair("keypoolrefill",          &keypoolrefill),
+    make_pair("walletpassphrase",       &walletpassphrase),
+    make_pair("walletpassphrasechange", &walletpassphrasechange),
+    make_pair("walletlock",             &walletlock),
+    make_pair("encryptwallet",          &encryptwallet),
+    make_pair("validateaddress",        &validateaddress),
+    make_pair("getbalance",             &getbalance),
+    make_pair("move",                   &movecmd),
+    make_pair("sendfrom",               &sendfrom),
+    make_pair("sendmany",               &sendmany),
+    make_pair("gettransaction",         &gettransaction),
+    make_pair("listtransactions",       &listtransactions),
+    make_pair("getwork",                &getwork),
+    make_pair("listaccounts",           &listaccounts),
+    make_pair("settxfee",               &settxfee),
 };
 map<string, rpcfn_type> mapCallTable(pCallTable, pCallTable + sizeof(pCallTable)/sizeof(pCallTable[0]));
 
@@ -1493,6 +1762,9 @@ string pAllowInSafeMode[] =
     "getaddressesbyaccount",
     "getaddressesbylabel", // deprecated
     "backupwallet",
+    "keypoolrefill",
+    "walletpassphrase",
+    "walletlock",
     "validateaddress",
     "getwork",
 };
@@ -2130,6 +2402,7 @@ int CommandLineRPC(int argc, char *argv[])
         if (strMethod == "listtransactions"       && n > 1) ConvertTo<boost::int64_t>(params[1]);
         if (strMethod == "listtransactions"       && n > 2) ConvertTo<boost::int64_t>(params[2]);
         if (strMethod == "listaccounts"           && n > 0) ConvertTo<boost::int64_t>(params[0]);
+        if (strMethod == "walletpassphrase"       && n > 1) ConvertTo<boost::int64_t>(params[1]);
         if (strMethod == "sendmany"               && n > 1)
         {
             string s = params[1].get_str();
index bd1b5b3..aa7f1f5 100644 (file)
@@ -1030,7 +1030,7 @@ bool Solver(const CKeyStore& keystore, const CScript& scriptPubKey, uint256 hash
         return false;
 
     // Compile solution
-    CRITICAL_BLOCK(keystore.cs_mapKeys)
+    CRITICAL_BLOCK(keystore.cs_KeyStore)
     {
         BOOST_FOREACH(PAIRTYPE(opcodetype, valtype)& item, vSolution)
         {
@@ -1038,13 +1038,13 @@ bool Solver(const CKeyStore& keystore, const CScript& scriptPubKey, uint256 hash
             {
                 // Sign
                 const valtype& vchPubKey = item.second;
-                CPrivKey privkey;
-                if (!keystore.GetPrivKey(vchPubKey, privkey))
+                CKey key;
+                if (!keystore.GetPrivKey(vchPubKey, key))
                     return false;
                 if (hash != 0)
                 {
                     vector<unsigned char> vchSig;
-                    if (!CKey::Sign(privkey, hash, vchSig))
+                    if (!key.Sign(hash, vchSig))
                         return false;
                     vchSig.push_back((unsigned char)nHashType);
                     scriptSigRet << vchSig;
@@ -1057,13 +1057,13 @@ bool Solver(const CKeyStore& keystore, const CScript& scriptPubKey, uint256 hash
                 if (mi == mapPubKeys.end())
                     return false;
                 const vector<unsigned char>& vchPubKey = (*mi).second;
-                CPrivKey privkey;
-                if (!keystore.GetPrivKey(vchPubKey, privkey))
+                CKey key;
+                if (!keystore.GetPrivKey(vchPubKey, key))
                     return false;
                 if (hash != 0)
                 {
                     vector<unsigned char> vchSig;
-                    if (!CKey::Sign(privkey, hash, vchSig))
+                    if (!key.Sign(hash, vchSig))
                         return false;
                     vchSig.push_back((unsigned char)nHashType);
                     scriptSigRet << vchSig << vchPubKey;
@@ -1089,8 +1089,40 @@ bool IsStandard(const CScript& scriptPubKey)
 
 bool IsMine(const CKeyStore &keystore, const CScript& scriptPubKey)
 {
-    CScript scriptSig;
-    return Solver(keystore, scriptPubKey, 0, 0, scriptSig);
+    vector<pair<opcodetype, valtype> > vSolution;
+    if (!Solver(scriptPubKey, vSolution))
+        return false;
+
+    // Compile solution
+    CRITICAL_BLOCK(keystore.cs_KeyStore)
+    {
+        BOOST_FOREACH(PAIRTYPE(opcodetype, valtype)& item, vSolution)
+        {
+            if (item.first == OP_PUBKEY)
+            {
+                // Sign
+                const valtype& vchPubKey = item.second;
+                if (!keystore.HaveKey(vchPubKey))
+                    return false;
+            }
+            else if (item.first == OP_PUBKEYHASH)
+            {
+                // Sign and give pubkey
+                map<uint160, valtype>::iterator mi = mapPubKeys.find(uint160(item.second));
+                if (mi == mapPubKeys.end())
+                    return false;
+                const vector<unsigned char>& vchPubKey = (*mi).second;
+                if (!keystore.HaveKey(vchPubKey))
+                    return false;
+            }
+            else
+            {
+                return false;
+            }
+        }
+    }
+
+    return true;
 }
 
 
index 31862a7..38c533d 100644 (file)
@@ -28,6 +28,30 @@ typedef unsigned long long  uint64;
 #if defined(_MSC_VER) && _MSC_VER < 1300
 #define for  if (false) ; else for
 #endif
+
+#ifdef __WXMSW__
+// This is used to attempt to keep keying material out of swap
+// Note that VirtualLock does not provide this as a guarantee on Windows,
+// but, in practice, memory that has been VirtualLock'd almost never gets written to
+// the pagefile except in rare circumstances where memory is extremely low.
+#define mlock(p, n) VirtualLock((p), (n));
+#define munlock(p, n) VirtualUnlock((p), (n));
+#else
+#include <sys/mman.h>
+#include <limits.h>
+/* This comes from limits.h if it's not defined there set a sane default */
+#ifndef PAGESIZE
+#include <unistd.h>
+#define PAGESIZE sysconf(_SC_PAGESIZE)
+#endif
+#define mlock(a,b) \
+  mlock(((void *)(((size_t)(a)) & (~((PAGESIZE)-1)))),\
+  (((((size_t)(a)) + (b) - 1) | ((PAGESIZE) - 1)) + 1) - (((size_t)(a)) & (~((PAGESIZE) - 1))))
+#define munlock(a,b) \
+  munlock(((void *)(((size_t)(a)) & (~((PAGESIZE)-1)))),\
+  (((((size_t)(a)) + (b) - 1) | ((PAGESIZE) - 1)) + 1) - (((size_t)(a)) & (~((PAGESIZE) - 1))))
+#endif
+
 class CScript;
 class CDataStream;
 class CAutoFile;
@@ -755,7 +779,8 @@ struct ser_streamplaceholder
 
 
 //
-// Allocator that clears its contents before deletion
+// Allocator that locks its contents from being paged
+// out of memory and clears its contents before deletion.
 //
 template<typename T>
 struct secure_allocator : public std::allocator<T>
@@ -777,10 +802,22 @@ struct secure_allocator : public std::allocator<T>
     template<typename _Other> struct rebind
     { typedef secure_allocator<_Other> other; };
 
+    T* allocate(std::size_t n, const void *hint = 0)
+    {
+        T *p;
+        p = std::allocator<T>::allocate(n, hint);
+        if (p != NULL)
+            mlock(p, sizeof(T) * n);
+        return p;
+    }
+
     void deallocate(T* p, std::size_t n)
     {
         if (p != NULL)
+        {
             memset(p, 0, sizeof(T) * n);
+            munlock(p, sizeof(T) * n);
+        }
         std::allocator<T>::deallocate(p, n);
     }
 };
index ff0b4af..eae0a4f 100644 (file)
@@ -245,6 +245,41 @@ void SetDefaultReceivingAddress(const string& strAddress)
     }
 }
 
+bool GetWalletPassphrase()
+{
+    if (pwalletMain->IsLocked())
+    {
+        string strWalletPass;
+        strWalletPass.reserve(100);
+        mlock(&strWalletPass[0], strWalletPass.capacity());
+
+        // obtain current wallet encrypt/decrypt key, from passphrase
+        // Note that the passphrase is not mlock()d during this entry and could potentially
+        // be obtained from disk long after bitcoin has run.
+        strWalletPass = wxGetPasswordFromUser(_("Enter the current passphrase to the wallet."),
+                                              _("Passphrase")).ToStdString();
+
+        if (!strWalletPass.size())
+        {
+            fill(strWalletPass.begin(), strWalletPass.end(), '\0');
+            munlock(&strWalletPass[0], strWalletPass.capacity());
+            wxMessageBox(_("Please supply the current wallet decryption passphrase."), "Bitcoin");
+            return false;
+        }
+
+        if (!pwalletMain->Unlock(strWalletPass))
+        {
+            fill(strWalletPass.begin(), strWalletPass.end(), '\0');
+            munlock(&strWalletPass[0], strWalletPass.capacity());
+            wxMessageBox(_("The passphrase entered for the wallet decryption was incorrect."), "Bitcoin");
+            return false;
+        }
+        fill(strWalletPass.begin(), strWalletPass.end(), '\0');
+        munlock(&strWalletPass[0], strWalletPass.capacity());
+    }
+    return true;
+}
+
 
 
 
@@ -333,6 +368,11 @@ CMainFrame::CMainFrame(wxWindow* parent) : CMainFrameBase(parent)
     if (CWalletDB(pwalletMain->strWalletFile,"r").ReadDefaultKey(vchPubKey))
         m_textCtrlAddress->SetValue(PubKeyToAddress(vchPubKey));
 
+    if (pwalletMain->IsCrypted())
+        m_menuOptions->Remove(m_menuOptionsEncryptWallet);
+    else
+        m_menuOptions->Remove(m_menuOptionsChangeWalletPassphrase);
+
     // Fill listctrl with wallet transactions
     RefreshListCtrl();
 }
@@ -1122,6 +1162,169 @@ void CMainFrame::OnMenuOptionsChangeYourAddress(wxCommandEvent& event)
         return;
 }
 
+void CMainFrame::OnMenuOptionsEncryptWallet(wxCommandEvent& event)
+{
+    // Options->Encrypt Wallet
+    if (pwalletMain->IsCrypted())
+    {
+        wxMessageBox(_("Wallet already encrypted."), "Bitcoin", wxOK | wxICON_ERROR);
+        return;
+    }
+
+    string strWalletPass;
+    strWalletPass.reserve(100);
+    mlock(&strWalletPass[0], strWalletPass.capacity());
+
+    // obtain current wallet encrypt/decrypt key, from passphrase
+    // Note that the passphrase is not mlock()d during this entry and could potentially
+    // be obtained from disk long after bitcoin has run.
+    strWalletPass = wxGetPasswordFromUser(_("Enter the new passphrase to the wallet.\nPlease use a passphrase of 10 or more random characters, or eight or more words."),
+                                          _("Passphrase")).ToStdString();
+
+    if (!strWalletPass.size())
+    {
+        fill(strWalletPass.begin(), strWalletPass.end(), '\0');
+        munlock(&strWalletPass[0], strWalletPass.capacity());
+        wxMessageBox(_("Error: The supplied passphrase was too short."), "Bitcoin", wxOK | wxICON_ERROR);
+        return;
+    }
+
+    if(wxMessageBox(_("WARNING: If you encrypt your wallet and lose your passphrase, you will LOSE ALL OF YOUR BITCOINS!\nAre you sure you wish to encrypt your wallet?"), "Bitcoin", wxYES_NO) != wxYES)
+        return;
+
+    string strWalletPassTest;
+    strWalletPassTest.reserve(100);
+    mlock(&strWalletPassTest[0], strWalletPassTest.capacity());
+    strWalletPassTest = wxGetPasswordFromUser(_("Please re-enter your new wallet passphrase."),
+                                              _("Passphrase")).ToStdString();
+
+    if (strWalletPassTest != strWalletPass)
+    {
+        fill(strWalletPass.begin(), strWalletPass.end(), '\0');
+        fill(strWalletPassTest.begin(), strWalletPassTest.end(), '\0');
+        munlock(&strWalletPass[0], strWalletPass.capacity());
+        munlock(&strWalletPassTest[0], strWalletPassTest.capacity());
+        wxMessageBox(_("Error: the supplied passphrases didn't match."), "Bitcoin", wxOK | wxICON_ERROR);
+        return;
+    }
+
+    if (!pwalletMain->EncryptWallet(strWalletPass))
+    {
+        fill(strWalletPass.begin(), strWalletPass.end(), '\0');
+        fill(strWalletPassTest.begin(), strWalletPassTest.end(), '\0');
+        munlock(&strWalletPass[0], strWalletPass.capacity());
+        munlock(&strWalletPassTest[0], strWalletPassTest.capacity());
+        wxMessageBox(_("Wallet encryption failed."), "Bitcoin", wxOK | wxICON_ERROR);
+        return;
+    }
+    fill(strWalletPass.begin(), strWalletPass.end(), '\0');
+    fill(strWalletPassTest.begin(), strWalletPassTest.end(), '\0');
+    munlock(&strWalletPass[0], strWalletPass.capacity());
+    munlock(&strWalletPassTest[0], strWalletPassTest.capacity());
+    wxMessageBox(_("Wallet Encrypted.\nRemember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer."), "Bitcoin");
+
+    m_menuOptions->Remove(m_menuOptionsEncryptWallet);
+    m_menuOptions->Insert(m_menuOptions->GetMenuItemCount() - 1, m_menuOptionsChangeWalletPassphrase);
+}
+
+void CMainFrame::OnMenuOptionsChangeWalletPassphrase(wxCommandEvent& event)
+{
+    // Options->Change Wallet Encryption Passphrase
+    if (!pwalletMain->IsCrypted())
+    {
+        wxMessageBox(_("Wallet is unencrypted, please encrypt it first."), "Bitcoin", wxOK | wxICON_ERROR);
+        return;
+    }
+
+    string strOldWalletPass;
+    strOldWalletPass.reserve(100);
+    mlock(&strOldWalletPass[0], strOldWalletPass.capacity());
+
+    // obtain current wallet encrypt/decrypt key, from passphrase
+    // Note that the passphrase is not mlock()d during this entry and could potentially
+    // be obtained from disk long after bitcoin has run.
+    strOldWalletPass = wxGetPasswordFromUser(_("Enter the current passphrase to the wallet."),
+                                             _("Passphrase")).ToStdString();
+
+    CRITICAL_BLOCK(pwalletMain->cs_vMasterKey)
+    {
+        bool fWasLocked = pwalletMain->IsLocked();
+        pwalletMain->Lock();
+
+        if (!strOldWalletPass.size() || !pwalletMain->Unlock(strOldWalletPass))
+        {
+            fill(strOldWalletPass.begin(), strOldWalletPass.end(), '\0');
+            munlock(&strOldWalletPass[0], strOldWalletPass.capacity());
+            wxMessageBox(_("The passphrase entered for the wallet decryption was incorrect."), "Bitcoin", wxOK | wxICON_ERROR);
+            return;
+        }
+
+        if (fWasLocked)
+            pwalletMain->Lock();
+
+        string strNewWalletPass;
+        strNewWalletPass.reserve(100);
+        mlock(&strNewWalletPass[0], strNewWalletPass.capacity());
+
+        // obtain new wallet encrypt/decrypt key, from passphrase
+        // Note that the passphrase is not mlock()d during this entry and could potentially
+        // be obtained from disk long after bitcoin has run.
+        strNewWalletPass = wxGetPasswordFromUser(_("Enter the new passphrase for the wallet."),
+                                                 _("Passphrase")).ToStdString();
+
+        if (!strNewWalletPass.size())
+        {
+            fill(strOldWalletPass.begin(), strOldWalletPass.end(), '\0');
+            fill(strNewWalletPass.begin(), strNewWalletPass.end(), '\0');
+            munlock(&strOldWalletPass[0], strOldWalletPass.capacity());
+            munlock(&strNewWalletPass[0], strNewWalletPass.capacity());
+            wxMessageBox(_("Error: The supplied passphrase was too short."), "Bitcoin", wxOK | wxICON_ERROR);
+            return;
+        }
+
+        string strNewWalletPassTest;
+        strNewWalletPassTest.reserve(100);
+        mlock(&strNewWalletPassTest[0], strNewWalletPassTest.capacity());
+
+        // obtain new wallet encrypt/decrypt key, from passphrase
+        // Note that the passphrase is not mlock()d during this entry and could potentially
+        // be obtained from disk long after bitcoin has run.
+        strNewWalletPassTest = wxGetPasswordFromUser(_("Re-enter the new passphrase for the wallet."),
+                                                     _("Passphrase")).ToStdString();
+
+        if (strNewWalletPassTest != strNewWalletPass)
+        {
+            fill(strOldWalletPass.begin(), strOldWalletPass.end(), '\0');
+            fill(strNewWalletPass.begin(), strNewWalletPass.end(), '\0');
+            fill(strNewWalletPassTest.begin(), strNewWalletPassTest.end(), '\0');
+            munlock(&strOldWalletPass[0], strOldWalletPass.capacity());
+            munlock(&strNewWalletPass[0], strNewWalletPass.capacity());
+            munlock(&strNewWalletPassTest[0], strNewWalletPassTest.capacity());
+            wxMessageBox(_("Error: the supplied passphrases didn't match."), "Bitcoin", wxOK | wxICON_ERROR);
+            return;
+        }
+
+        if (!pwalletMain->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass))
+        {
+            fill(strOldWalletPass.begin(), strOldWalletPass.end(), '\0');
+            fill(strNewWalletPass.begin(), strNewWalletPass.end(), '\0');
+            fill(strNewWalletPassTest.begin(), strNewWalletPassTest.end(), '\0');
+            munlock(&strOldWalletPass[0], strOldWalletPass.capacity());
+            munlock(&strNewWalletPass[0], strNewWalletPass.capacity());
+            munlock(&strNewWalletPassTest[0], strNewWalletPassTest.capacity());
+            wxMessageBox(_("The passphrase entered for the wallet decryption was incorrect."), "Bitcoin", wxOK | wxICON_ERROR);
+            return;
+        }
+        fill(strOldWalletPass.begin(), strOldWalletPass.end(), '\0');
+        fill(strNewWalletPass.begin(), strNewWalletPass.end(), '\0');
+        fill(strNewWalletPassTest.begin(), strNewWalletPassTest.end(), '\0');
+        munlock(&strOldWalletPass[0], strOldWalletPass.capacity());
+        munlock(&strNewWalletPass[0], strNewWalletPass.capacity());
+        munlock(&strNewWalletPassTest[0], strNewWalletPassTest.capacity());
+        wxMessageBox(_("Wallet Passphrase Changed."), "Bitcoin");
+    }
+}
+
 void CMainFrame::OnMenuOptionsOptions(wxCommandEvent& event)
 {
     // Options->Options
@@ -1182,8 +1385,19 @@ void CMainFrame::OnButtonNew(wxCommandEvent& event)
         return;
     string strName = dialog.GetValue();
 
-    // Generate new key
-    string strAddress = PubKeyToAddress(pwalletMain->GetKeyFromKeyPool());
+    string strAddress;
+    CRITICAL_BLOCK(pwalletMain->cs_vMasterKey)
+    {
+        bool fWasLocked = pwalletMain->IsLocked();
+        if (!GetWalletPassphrase())
+            return;
+
+        // Generate new key
+        strAddress = PubKeyToAddress(pwalletMain->GetOrReuseKeyFromPool());
+
+        if (fWasLocked)
+            pwalletMain->Lock();
+    }
 
     // Save
     CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook)
@@ -1947,7 +2161,12 @@ void CSendDialog::OnButtonSend(wxCommandEvent& event)
         if (fBitcoinAddress)
         {
            CRITICAL_BLOCK(cs_main)
+            CRITICAL_BLOCK(pwalletMain->cs_vMasterKey)
            {
+                bool fWasLocked = pwalletMain->IsLocked();
+                if (!GetWalletPassphrase())
+                    return;
+
                 // Send to bitcoin address
                 CScript scriptPubKey;
                 scriptPubKey << OP_DUP << OP_HASH160 << hash160 << OP_EQUALVERIFY << OP_CHECKSIG;
@@ -1956,13 +2175,22 @@ void CSendDialog::OnButtonSend(wxCommandEvent& event)
                 if (strError == "")
                     wxMessageBox(_("Payment sent  "), _("Sending..."));
                 else if (strError == "ABORTED")
+                {
+                    if (fWasLocked)
+                        pwalletMain->Lock();
                     return; // leave send dialog open
+                }
                 else
                 {
                     wxMessageBox(strError + "  ", _("Sending..."));
                     EndModal(false);
+                    if (fWasLocked)
+                        pwalletMain->Lock();
                     return;
                 }
+
+                if (fWasLocked)
+                    pwalletMain->Lock();
            }
         }
         else
@@ -2246,16 +2474,27 @@ void CSendingDialog::OnReply2(CDataStream& vRecv)
             Error(_("Insufficient funds"));
             return;
         }
+
         CReserveKey reservekey(pwalletMain);
         int64 nFeeRequired;
-        if (!pwalletMain->CreateTransaction(scriptPubKey, nPrice, wtx, reservekey, nFeeRequired))
+        CRITICAL_BLOCK(pwalletMain->cs_vMasterKey)
         {
-            if (nPrice + nFeeRequired > pwalletMain->GetBalance())
-                Error(strprintf(_("This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds"), FormatMoney(nFeeRequired).c_str()));
-            else
-                Error(_("Transaction creation failed"));
-            return;
-        }
+            bool fWasLocked = pwalletMain->IsLocked();
+            if (!GetWalletPassphrase())
+                return;
+
+            if (!pwalletMain->CreateTransaction(scriptPubKey, nPrice, wtx, reservekey, nFeeRequired))
+            {
+                if (nPrice + nFeeRequired > pwalletMain->GetBalance())
+                    Error(strprintf(_("This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds"), FormatMoney(nFeeRequired).c_str()));
+                else
+                    Error(_("Transaction creation failed"));
+                return;
+            }
+
+            if (fWasLocked)
+                pwalletMain->Lock();
+       }
 
         // Transaction fee
         if (!ThreadSafeAskFee(nFeeRequired, _("Sending..."), this))
@@ -2382,7 +2621,7 @@ CAddressBookDialog::CAddressBookDialog(wxWindow* parent, const wxString& strInit
     m_listCtrlReceiving->SetFocus();
 
     // Fill listctrl with address book data
-    CRITICAL_BLOCK(pwalletMain->cs_mapKeys)
+    CRITICAL_BLOCK(pwalletMain->cs_KeyStore)
     CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook)
     {
         string strDefaultReceiving = (string)pframeMain->m_textCtrlAddress->GetValue();
@@ -2581,8 +2820,18 @@ void CAddressBookDialog::OnButtonNew(wxCommandEvent& event)
             return;
         strName = dialog.GetValue();
 
-        // Generate new key
-        strAddress = PubKeyToAddress(pwalletMain->GetKeyFromKeyPool());
+        CRITICAL_BLOCK(pwalletMain->cs_vMasterKey)
+        {
+            bool fWasLocked = pwalletMain->IsLocked();
+            if (!GetWalletPassphrase())
+                return;
+
+            // Generate new key
+            strAddress = PubKeyToAddress(pwalletMain->GetOrReuseKeyFromPool());
+
+            if (fWasLocked)
+                pwalletMain->Lock();
+        }
     }
 
     // Add to list and select it
index 3f06ad9..3bf7415 100644 (file)
--- a/src/ui.h
+++ b/src/ui.h
@@ -59,6 +59,8 @@ protected:
     void OnMenuFileExit(wxCommandEvent& event);
     void OnUpdateUIOptionsGenerate(wxUpdateUIEvent& event);
     void OnMenuOptionsChangeYourAddress(wxCommandEvent& event);
+    void OnMenuOptionsEncryptWallet(wxCommandEvent& event);
+    void OnMenuOptionsChangeWalletPassphrase(wxCommandEvent& event);
     void OnMenuOptionsOptions(wxCommandEvent& event);
     void OnMenuHelpAbout(wxCommandEvent& event);
     void OnButtonSend(wxCommandEvent& event);
index 1b901a1..18eec44 100644 (file)
@@ -32,6 +32,12 @@ CMainFrameBase::CMainFrameBase( wxWindow* parent, wxWindowID id, const wxString&
        m_menuOptionsChangeYourAddress = new wxMenuItem( m_menuOptions, wxID_ANY, wxString( _("&Your Receiving Addresses...") ) , wxEmptyString, wxITEM_NORMAL );
        m_menuOptions->Append( m_menuOptionsChangeYourAddress );
        
+       m_menuOptionsEncryptWallet = new wxMenuItem( m_menuOptions, wxID_ANY, wxString( _("&Encrypt Wallet...") ) , wxEmptyString, wxITEM_NORMAL );
+       m_menuOptions->Append( m_menuOptionsEncryptWallet );
+       
+       m_menuOptionsChangeWalletPassphrase = new wxMenuItem( m_menuOptions, wxID_ANY, wxString( _("&Change Wallet Encryption Passphrase...") ) , wxEmptyString, wxITEM_NORMAL );
+       m_menuOptions->Append( m_menuOptionsChangeWalletPassphrase );
+       
        wxMenuItem* m_menuOptionsOptions;
        m_menuOptionsOptions = new wxMenuItem( m_menuOptions, wxID_PREFERENCES, wxString( _("&Options...") ) , wxEmptyString, wxITEM_NORMAL );
        m_menuOptions->Append( m_menuOptionsOptions );
@@ -187,6 +193,8 @@ CMainFrameBase::CMainFrameBase( wxWindow* parent, wxWindowID id, const wxString&
        this->Connect( wxEVT_PAINT, wxPaintEventHandler( CMainFrameBase::OnPaint ) );
        this->Connect( m_menuFileExit->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( CMainFrameBase::OnMenuFileExit ) );
        this->Connect( m_menuOptionsChangeYourAddress->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( CMainFrameBase::OnMenuOptionsChangeYourAddress ) );
+       this->Connect( m_menuOptionsEncryptWallet->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( CMainFrameBase::OnMenuOptionsEncryptWallet ) );
+       this->Connect( m_menuOptionsChangeWalletPassphrase->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( CMainFrameBase::OnMenuOptionsChangeWalletPassphrase ) );
        this->Connect( m_menuOptionsOptions->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( CMainFrameBase::OnMenuOptionsOptions ) );
        this->Connect( m_menuHelpAbout->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( CMainFrameBase::OnMenuHelpAbout ) );
        this->Connect( wxID_BUTTONSEND, wxEVT_COMMAND_TOOL_CLICKED, wxCommandEventHandler( CMainFrameBase::OnButtonSend ) );
@@ -245,6 +253,8 @@ CMainFrameBase::~CMainFrameBase()
        this->Disconnect( wxEVT_PAINT, wxPaintEventHandler( CMainFrameBase::OnPaint ) );
        this->Disconnect( wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( CMainFrameBase::OnMenuFileExit ) );
        this->Disconnect( wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( CMainFrameBase::OnMenuOptionsChangeYourAddress ) );
+       this->Disconnect( wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( CMainFrameBase::OnMenuOptionsEncryptWallet ) );
+       this->Disconnect( wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( CMainFrameBase::OnMenuOptionsChangeWalletPassphrase ) );
        this->Disconnect( wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( CMainFrameBase::OnMenuOptionsOptions ) );
        this->Disconnect( wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( CMainFrameBase::OnMenuHelpAbout ) );
        this->Disconnect( wxID_BUTTONSEND, wxEVT_COMMAND_TOOL_CLICKED, wxCommandEventHandler( CMainFrameBase::OnButtonSend ) );
index 78f3d1b..ca0730b 100644 (file)
@@ -98,6 +98,8 @@ class CMainFrameBase : public wxFrame
                virtual void OnPaint( wxPaintEvent& event ) { event.Skip(); }
                virtual void OnMenuFileExit( wxCommandEvent& event ) { event.Skip(); }
                virtual void OnMenuOptionsChangeYourAddress( wxCommandEvent& event ) { event.Skip(); }
+               virtual void OnMenuOptionsEncryptWallet( wxCommandEvent& event ) { event.Skip(); }
+               virtual void OnMenuOptionsChangeWalletPassphrase( wxCommandEvent& event ) { event.Skip(); }
                virtual void OnMenuOptionsOptions( wxCommandEvent& event ) { event.Skip(); }
                virtual void OnMenuHelpAbout( wxCommandEvent& event ) { event.Skip(); }
                virtual void OnButtonSend( wxCommandEvent& event ) { event.Skip(); }
@@ -115,6 +117,8 @@ class CMainFrameBase : public wxFrame
        
        public:
                wxMenu* m_menuOptions;
+               wxMenuItem* m_menuOptionsEncryptWallet;
+               wxMenuItem* m_menuOptionsChangeWalletPassphrase;
                wxStatusBar* m_statusBar;
                wxTextCtrl* m_textCtrlAddress;
                wxListCtrl* m_listCtrlAll;
index 6ef75ef..93313e7 100644 (file)
@@ -5,11 +5,11 @@
 #include "headers.h"
 #include "db.h"
 #include "cryptopp/sha.h"
+#include "crypter.h"
 
 using namespace std;
 
 
-
 //////////////////////////////////////////////////////////////////////////////
 //
 // mapWallet
@@ -17,10 +17,180 @@ using namespace std;
 
 bool CWallet::AddKey(const CKey& key)
 {
-    this->CKeyStore::AddKey(key);
+    if (!CCryptoKeyStore::AddKey(key))
+        return false;
+    if (!fFileBacked)
+        return true;
+    if (!IsCrypted())
+        return CWalletDB(strWalletFile).WriteKey(key.GetPubKey(), key.GetPrivKey());
+}
+
+bool CWallet::AddCryptedKey(const vector<unsigned char> &vchPubKey, const vector<unsigned char> &vchCryptedSecret)
+{
+    if (!CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret))
+        return false;
     if (!fFileBacked)
         return true;
-    return CWalletDB(strWalletFile).WriteKey(key.GetPubKey(), key.GetPrivKey());
+    CRITICAL_BLOCK(cs_pwalletdbEncryption)
+    {
+        if (pwalletdbEncryption)
+            return pwalletdbEncryption->WriteCryptedKey(vchPubKey, vchCryptedSecret);
+        else
+            return CWalletDB(strWalletFile).WriteCryptedKey(vchPubKey, vchCryptedSecret);
+    }
+}
+
+bool CWallet::Unlock(const string& strWalletPassphrase)
+{
+    CRITICAL_BLOCK(cs_vMasterKey)
+    {
+        if (!IsLocked())
+            return false;
+
+        CCrypter crypter;
+        CKeyingMaterial vMasterKey;
+
+        BOOST_FOREACH(const MasterKeyMap::value_type& pMasterKey, mapMasterKeys)
+        {
+            if(!crypter.SetKeyFromPassphrase(strWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod))
+                return false;
+            if (!crypter.Decrypt(pMasterKey.second.vchCryptedKey, vMasterKey))
+                return false;
+            if (CCryptoKeyStore::Unlock(vMasterKey))
+                return true;
+        }
+    }
+    return false;
+}
+
+bool CWallet::ChangeWalletPassphrase(const string& strOldWalletPassphrase, const string& strNewWalletPassphrase)
+{
+    CRITICAL_BLOCK(cs_vMasterKey)
+    {
+        bool fWasLocked = IsLocked();
+
+        Lock();
+
+        CCrypter crypter;
+        CKeyingMaterial vMasterKey;
+        BOOST_FOREACH(MasterKeyMap::value_type& pMasterKey, mapMasterKeys)
+        {
+            if(!crypter.SetKeyFromPassphrase(strOldWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod))
+                return false;
+            if(!crypter.Decrypt(pMasterKey.second.vchCryptedKey, vMasterKey))
+                return false;
+            if (CCryptoKeyStore::Unlock(vMasterKey))
+            {
+                int64 nStartTime = GetTimeMillis();
+                crypter.SetKeyFromPassphrase(strNewWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod);
+                pMasterKey.second.nDeriveIterations = pMasterKey.second.nDeriveIterations * (100 / ((double)(GetTimeMillis() - nStartTime)));
+
+                nStartTime = GetTimeMillis();
+                crypter.SetKeyFromPassphrase(strNewWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod);
+                pMasterKey.second.nDeriveIterations = (pMasterKey.second.nDeriveIterations + pMasterKey.second.nDeriveIterations * 100 / ((double)(GetTimeMillis() - nStartTime))) / 2;
+
+                if (pMasterKey.second.nDeriveIterations < 25000)
+                    pMasterKey.second.nDeriveIterations = 25000;
+
+                printf("Wallet passphrase changed to an nDeriveIterations of %i\n", pMasterKey.second.nDeriveIterations);
+
+                if (!crypter.SetKeyFromPassphrase(strNewWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod))
+                    return false;
+                if (!crypter.Encrypt(vMasterKey, pMasterKey.second.vchCryptedKey))
+                    return false;
+                CWalletDB(strWalletFile).WriteMasterKey(pMasterKey.first, pMasterKey.second);
+                if (fWasLocked)
+                    Lock();
+                return true;
+            }
+        }
+    }
+    return false;
+}
+
+
+// This class implements an addrIncoming entry that causes pre-0.4
+// clients to crash on startup if reading a private-key-encrypted wallet.
+class CCorruptAddress
+{
+public:
+    IMPLEMENT_SERIALIZE
+    (
+        if (nType & SER_DISK)
+            READWRITE(nVersion);
+    )
+};
+
+bool CWallet::EncryptWallet(const string& strWalletPassphrase)
+{
+    CRITICAL_BLOCK(cs_mapPubKeys)
+    CRITICAL_BLOCK(cs_KeyStore)
+    CRITICAL_BLOCK(cs_vMasterKey)
+    CRITICAL_BLOCK(cs_pwalletdbEncryption)
+    {
+        if (IsCrypted())
+            return false;
+
+        CKeyingMaterial vMasterKey;
+        RandAddSeedPerfmon();
+
+        vMasterKey.resize(WALLET_CRYPTO_KEY_SIZE);
+        RAND_bytes(&vMasterKey[0], WALLET_CRYPTO_KEY_SIZE);
+
+        CMasterKey kMasterKey;
+
+        RandAddSeedPerfmon();
+        kMasterKey.vchSalt.resize(WALLET_CRYPTO_SALT_SIZE);
+        RAND_bytes(&kMasterKey.vchSalt[0], WALLET_CRYPTO_SALT_SIZE);
+
+        CCrypter crypter;
+        int64 nStartTime = GetTimeMillis();
+        crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, 25000, kMasterKey.nDerivationMethod);
+        kMasterKey.nDeriveIterations = 2500000 / ((double)(GetTimeMillis() - nStartTime));
+
+        nStartTime = GetTimeMillis();
+        crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, kMasterKey.nDeriveIterations, kMasterKey.nDerivationMethod);
+        kMasterKey.nDeriveIterations = (kMasterKey.nDeriveIterations + kMasterKey.nDeriveIterations * 100 / ((double)(GetTimeMillis() - nStartTime))) / 2;
+
+        if (kMasterKey.nDeriveIterations < 25000)
+            kMasterKey.nDeriveIterations = 25000;
+
+        printf("Encrypting Wallet with an nDeriveIterations of %i\n", kMasterKey.nDeriveIterations);
+
+        if (!crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, kMasterKey.nDeriveIterations, kMasterKey.nDerivationMethod))
+            return false;
+        if (!crypter.Encrypt(vMasterKey, kMasterKey.vchCryptedKey))
+            return false;
+
+        mapMasterKeys[++nMasterKeyMaxID] = kMasterKey;
+        if (fFileBacked)
+        {
+            pwalletdbEncryption = new CWalletDB(strWalletFile);
+            pwalletdbEncryption->TxnBegin();
+            pwalletdbEncryption->WriteMasterKey(nMasterKeyMaxID, kMasterKey);
+        }
+
+        if (!EncryptKeys(vMasterKey))
+        {
+            if (fFileBacked)
+                pwalletdbEncryption->TxnAbort();
+            exit(1); //We now probably have half of our keys encrypted in memory, and half not...die and let the user reload their unencrypted wallet.
+        }
+
+        if (fFileBacked)
+        {
+            CCorruptAddress corruptAddress;
+            pwalletdbEncryption->WriteSetting("addrIncoming", corruptAddress);
+            if (!pwalletdbEncryption->TxnCommit())
+                exit(1); //We now have keys encrypted in memory, but no on disk...die to avoid confusion and let the user reload their unencrypted wallet.
+
+            pwalletdbEncryption->Close();
+            pwalletdbEncryption = NULL;
+        }
+
+        Lock();
+    }
+    return true;
 }
 
 void CWallet::WalletUpdateSpent(const CTransaction &tx)
@@ -98,7 +268,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn)
         BOOST_FOREACH(const CTxOut& txout, wtx.vout)
         {
             if (txout.scriptPubKey == scriptDefaultKey)
-                SetDefaultKey(GetKeyFromKeyPool());
+                SetDefaultKey(GetOrReuseKeyFromPool());
         }
 
         // Notify UI
@@ -783,7 +953,7 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, int64> >& vecSend, CW
 
                     // Reserve a new key pair from key pool
                     vector<unsigned char> vchPubKey = reservekey.GetReservedKey();
-                    assert(mapKeys.count(vchPubKey));
+                    // assert(mapKeys.count(vchPubKey));
 
                     // Fill a vout to ourself, using same address type as the payment
                     CScript scriptChange;
@@ -903,15 +1073,24 @@ string CWallet::SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew,
 {
     CReserveKey reservekey(this);
     int64 nFeeRequired;
-    if (!CreateTransaction(scriptPubKey, nValue, wtxNew, reservekey, nFeeRequired))
+    CRITICAL_BLOCK(cs_vMasterKey)
     {
-        string strError;
-        if (nValue + nFeeRequired > GetBalance())
-            strError = strprintf(_("Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds  "), FormatMoney(nFeeRequired).c_str());
-        else
-            strError = _("Error: Transaction creation failed  ");
-        printf("SendMoney() : %s", strError.c_str());
-        return strError;
+        if (IsLocked())
+        {
+            string strError = _("Error: Wallet locked, unable to create transaction  ");
+            printf("SendMoney() : %s", strError.c_str());
+            return strError;
+        }
+        if (!CreateTransaction(scriptPubKey, nValue, wtxNew, reservekey, nFeeRequired))
+        {
+            string strError;
+            if (nValue + nFeeRequired > GetBalance())
+                strError = strprintf(_("Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds  "), FormatMoney(nFeeRequired).c_str());
+            else
+                strError = _("Error: Transaction creation failed  ");
+            printf("SendMoney() : %s", strError.c_str());
+            return strError;
+        }
     }
 
     if (fAskFee && !ThreadSafeAskFee(nFeeRequired, _("Sending..."), NULL))
@@ -955,12 +1134,12 @@ bool CWallet::LoadWallet(bool& fFirstRunRet)
         return false;
     fFirstRunRet = vchDefaultKey.empty();
 
-    if (!mapKeys.count(vchDefaultKey))
+    if (!HaveKey(vchDefaultKey))
     {
-        // Create new default key
+        // Create new keyUser and set as default key
         RandAddSeedPerfmon();
 
-        SetDefaultKey(GetKeyFromKeyPool());
+        SetDefaultKey(GetOrReuseKeyFromPool());
         if (!SetAddressBookName(PubKeyToAddress(vchDefaultKey), ""))
             return false;
     }
@@ -1033,14 +1212,16 @@ bool GetWalletFile(CWallet* pwallet, string &strWalletFileOut)
     return true;
 }
 
-void CWallet::ReserveKeyFromKeyPool(int64& nIndex, CKeyPool& keypool)
+bool CWallet::TopUpKeyPool()
 {
-    nIndex = -1;
-    keypool.vchPubKey.clear();
     CRITICAL_BLOCK(cs_main)
     CRITICAL_BLOCK(cs_mapWallet)
     CRITICAL_BLOCK(cs_setKeyPool)
+    CRITICAL_BLOCK(cs_vMasterKey)
     {
+        if (IsLocked())
+            return false;
+
         CWalletDB walletdb(strWalletFile);
 
         // Top up key pool
@@ -1051,18 +1232,36 @@ void CWallet::ReserveKeyFromKeyPool(int64& nIndex, CKeyPool& keypool)
             if (!setKeyPool.empty())
                 nEnd = *(--setKeyPool.end()) + 1;
             if (!walletdb.WritePool(nEnd, CKeyPool(GenerateNewKey())))
-                throw runtime_error("ReserveKeyFromKeyPool() : writing generated key failed");
+                throw runtime_error("TopUpKeyPool() : writing generated key failed");
             setKeyPool.insert(nEnd);
             printf("keypool added key %"PRI64d", size=%d\n", nEnd, setKeyPool.size());
         }
+    }
+    return true;
+}
+
+void CWallet::ReserveKeyFromKeyPool(int64& nIndex, CKeyPool& keypool)
+{
+    nIndex = -1;
+    keypool.vchPubKey.clear();
+    CRITICAL_BLOCK(cs_main)
+    CRITICAL_BLOCK(cs_mapWallet)
+    CRITICAL_BLOCK(cs_setKeyPool)
+    {
+        if (!IsLocked())
+            TopUpKeyPool();
 
         // Get the oldest key
-        assert(!setKeyPool.empty());
+        if(setKeyPool.empty())
+            return;
+
+        CWalletDB walletdb(strWalletFile);
+
         nIndex = *(setKeyPool.begin());
         setKeyPool.erase(setKeyPool.begin());
         if (!walletdb.ReadPool(nIndex, keypool))
             throw runtime_error("ReserveKeyFromKeyPool() : read failed");
-        if (!mapKeys.count(keypool.vchPubKey))
+        if (!HaveKey(keypool.vchPubKey))
             throw runtime_error("ReserveKeyFromKeyPool() : unknown key in key pool");
         assert(!keypool.vchPubKey.empty());
         printf("keypool reserve %"PRI64d"\n", nIndex);
@@ -1091,11 +1290,13 @@ void CWallet::ReturnKey(int64 nIndex)
     printf("keypool return %"PRI64d"\n", nIndex);
 }
 
-vector<unsigned char> CWallet::GetKeyFromKeyPool()
+vector<unsigned char> CWallet::GetOrReuseKeyFromPool()
 {
     int64 nIndex = 0;
     CKeyPool keypool;
     ReserveKeyFromKeyPool(nIndex, keypool);
+    if(nIndex == -1)
+        return vchDefaultKey;
     KeepKey(nIndex);
     return keypool.vchPubKey;
 }
@@ -1105,6 +1306,8 @@ int64 CWallet::GetOldestKeyPoolTime()
     int64 nIndex = 0;
     CKeyPool keypool;
     ReserveKeyFromKeyPool(nIndex, keypool);
+    if (nIndex == -1)
+        return GetTime();
     ReturnKey(nIndex);
     return keypool.nTime;
 }
index 7d9db97..d336d38 100644 (file)
@@ -12,12 +12,14 @@ class CWalletTx;
 class CReserveKey;
 class CWalletDB;
 
-class CWallet : public CKeyStore
+class CWallet : public CCryptoKeyStore
 {
 private:
     bool SelectCoinsMinConf(int64 nTargetValue, int nConfMine, int nConfTheirs, std::set<std::pair<const CWalletTx*,unsigned int> >& setCoinsRet, int64& nValueRet) const;
     bool SelectCoins(int64 nTargetValue, std::set<std::pair<const CWalletTx*,unsigned int> >& setCoinsRet, int64& nValueRet) const;
 
+    CWalletDB *pwalletdbEncryption;
+    CCriticalSection cs_pwalletdbEncryption;
 
 public:
     bool fFileBacked;
@@ -26,14 +28,22 @@ public:
     std::set<int64> setKeyPool;
     CCriticalSection cs_setKeyPool;
 
+    typedef std::map<unsigned int, CMasterKey> MasterKeyMap;
+    MasterKeyMap mapMasterKeys;
+    unsigned int nMasterKeyMaxID;
+
     CWallet()
     {
         fFileBacked = false;
+        nMasterKeyMaxID = 0;
+        pwalletdbEncryption = NULL;
     }
     CWallet(std::string strWalletFileIn)
     {
         strWalletFile = strWalletFileIn;
         fFileBacked = true;
+        nMasterKeyMaxID = 0;
+        pwalletdbEncryption = NULL;
     }
 
     mutable CCriticalSection cs_mapWallet;
@@ -48,7 +58,16 @@ public:
 
     std::vector<unsigned char> vchDefaultKey;
 
+    // keystore implementation
     bool AddKey(const CKey& key);
+    bool LoadKey(const CKey& key) { return CCryptoKeyStore::AddKey(key); }
+    bool AddCryptedKey(const std::vector<unsigned char> &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret);
+    bool LoadCryptedKey(const std::vector<unsigned char> &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret) { return CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret); }
+
+    bool Unlock(const std::string& strWalletPassphrase);
+    bool ChangeWalletPassphrase(const std::string& strOldWalletPassphrase, const std::string& strNewWalletPassphrase);
+    bool EncryptWallet(const std::string& strWalletPassphrase);
+
     bool AddToWallet(const CWalletTx& wtxIn);
     bool AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pblock, bool fUpdate = false);
     bool EraseFromWallet(uint256 hash);
@@ -64,10 +83,11 @@ public:
     std::string SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, bool fAskFee=false);
     std::string SendMoneyToBitcoinAddress(std::string strAddress, int64 nValue, CWalletTx& wtxNew, bool fAskFee=false);
 
+    bool TopUpKeyPool();
     void ReserveKeyFromKeyPool(int64& nIndex, CKeyPool& keypool);
     void KeepKey(int64 nIndex);
     void ReturnKey(int64 nIndex);
-    std::vector<unsigned char> GetKeyFromKeyPool();
+    std::vector<unsigned char> GetOrReuseKeyFromPool();
     int64 GetOldestKeyPoolTime();
 
     bool IsMine(const CTxIn& txin) const;
@@ -174,6 +194,11 @@ public:
         }
     }
 
+    int GetKeyPoolSize()
+    {
+        return setKeyPool.size();
+    }
+
     bool GetTransaction(const uint256 &hashTx, CWalletTx& wtx);
 
     bool SetDefaultKey(const std::vector<unsigned char> &vchPubKey);