diff options
Diffstat (limited to 'src/main.cpp')
| -rw-r--r-- | src/main.cpp | 334 |
1 files changed, 332 insertions, 2 deletions
diff --git a/src/main.cpp b/src/main.cpp index efece6c..0342650 100644 --- a/src/main.cpp +++ b/src/main.cpp | |||
| @@ -72,7 +72,15 @@ vector<pair<uint256, bool> > vWalletUpdated; | |||
| 72 | CCriticalSection cs_mapWallet; | 72 | CCriticalSection cs_mapWallet; |
| 73 | 73 | ||
| 74 | map<vector<unsigned char>, CPrivKey> mapKeys; | 74 | map<vector<unsigned char>, CPrivKey> mapKeys; |
| 75 | map<vector<unsigned char>, vector<unsigned char> > mapCryptedKeys; | ||
| 75 | map<uint160, vector<unsigned char> > mapPubKeys; | 76 | map<uint160, vector<unsigned char> > mapPubKeys; |
| 77 | map<unsigned int, CWalletMasterKey> mapMasterKeys; | ||
| 78 | unsigned int nWalletMasterKeyMaxID = 0; | ||
| 79 | CKeyingMaterial vWalletMasterKey; | ||
| 80 | vector<unsigned char> vchCryptedHDMaster; | ||
| 81 | vector<unsigned char> vchCryptedHDChainCode; | ||
| 82 | bool fWalletEncrypted = false; | ||
| 83 | bool fWalletLocked = true; | ||
| 76 | CCriticalSection cs_mapKeys; | 84 | CCriticalSection cs_mapKeys; |
| 77 | CKey keyUser; | 85 | CKey keyUser; |
| 78 | 86 | ||
| @@ -159,6 +167,21 @@ int MinerThreadCount() | |||
| 159 | 167 | ||
| 160 | bool AddKey(const CKey& key) | 168 | bool AddKey(const CKey& key) |
| 161 | { | 169 | { |
| 170 | if (IsWalletEncrypted()) | ||
| 171 | { | ||
| 172 | string strError; | ||
| 173 | CPrivKey vchPrivKey = key.GetPrivKey(); | ||
| 174 | vector<unsigned char> vchCryptedSecret; | ||
| 175 | if (IsWalletLocked()) | ||
| 176 | return error("AddKey() : wallet is locked\n"); | ||
| 177 | if (!EncryptSecret(vWalletMasterKey, vector<unsigned char>(vchPrivKey.begin(), vchPrivKey.end()), | ||
| 178 | WalletKeyIV(key.GetPubKey()), vchCryptedSecret)) | ||
| 179 | return error("AddKey() : encrypting key failed\n"); | ||
| 180 | if (!AddCryptedKey(key.GetPubKey(), vchCryptedSecret)) | ||
| 181 | return false; | ||
| 182 | return CWalletDB().WriteCryptedKey(key.GetPubKey(), vchCryptedSecret); | ||
| 183 | } | ||
| 184 | |||
| 162 | CRITICAL_BLOCK(cs_mapKeys) | 185 | CRITICAL_BLOCK(cs_mapKeys) |
| 163 | { | 186 | { |
| 164 | mapKeys[key.GetPubKey()] = key.GetPrivKey(); | 187 | mapKeys[key.GetPubKey()] = key.GetPrivKey(); |
| @@ -167,10 +190,287 @@ bool AddKey(const CKey& key) | |||
| 167 | return CWalletDB().WriteKey(key.GetPubKey(), key.GetPrivKey()); | 190 | return CWalletDB().WriteKey(key.GetPubKey(), key.GetPrivKey()); |
| 168 | } | 191 | } |
| 169 | 192 | ||
| 193 | bool IsWalletEncrypted() | ||
| 194 | { | ||
| 195 | return fWalletEncrypted; | ||
| 196 | } | ||
| 197 | |||
| 198 | bool IsWalletLocked() | ||
| 199 | { | ||
| 200 | return fWalletEncrypted && fWalletLocked; | ||
| 201 | } | ||
| 202 | |||
| 203 | void LockWallet() | ||
| 204 | { | ||
| 205 | if (!fWalletEncrypted) | ||
| 206 | return; | ||
| 207 | CRITICAL_BLOCK(cs_mapKeys) | ||
| 208 | { | ||
| 209 | vWalletMasterKey.clear(); | ||
| 210 | if (!vchCryptedHDMaster.empty()) | ||
| 211 | vchHDMaster.clear(); | ||
| 212 | if (!vchCryptedHDChainCode.empty()) | ||
| 213 | vchHDChainCode.clear(); | ||
| 214 | fWalletLocked = true; | ||
| 215 | } | ||
| 216 | } | ||
| 217 | |||
| 218 | bool DeriveWalletPassphraseKey(const string& strPassphrase, | ||
| 219 | const vector<unsigned char>& vchSalt, | ||
| 220 | unsigned int nDeriveIterations, | ||
| 221 | CKeyingMaterial& vchKeyRet, | ||
| 222 | vector<unsigned char>& vchIVRet) | ||
| 223 | { | ||
| 224 | vchKeyRet.clear(); | ||
| 225 | vchIVRet.clear(); | ||
| 226 | if (vchSalt.size() != 8 || nDeriveIterations < 1) | ||
| 227 | return false; | ||
| 228 | |||
| 229 | unsigned char chKey[32]; | ||
| 230 | unsigned char chIV[16]; | ||
| 231 | int n = EVP_BytesToKey(EVP_aes_256_cbc(), EVP_sha512(), &vchSalt[0], | ||
| 232 | (const unsigned char*)strPassphrase.data(), | ||
| 233 | (int)strPassphrase.size(), | ||
| 234 | (int)nDeriveIterations, | ||
| 235 | chKey, chIV); | ||
| 236 | if (n != 32) | ||
| 237 | { | ||
| 238 | memset(chKey, 0, sizeof(chKey)); | ||
| 239 | memset(chIV, 0, sizeof(chIV)); | ||
| 240 | return false; | ||
| 241 | } | ||
| 242 | vchKeyRet.assign(chKey, chKey + sizeof(chKey)); | ||
| 243 | vchIVRet.assign(chIV, chIV + sizeof(chIV)); | ||
| 244 | memset(chKey, 0, sizeof(chKey)); | ||
| 245 | memset(chIV, 0, sizeof(chIV)); | ||
| 246 | return true; | ||
| 247 | } | ||
| 248 | |||
| 249 | bool EncryptSecret(const CKeyingMaterial& vchKey, | ||
| 250 | const vector<unsigned char>& vchPlaintext, | ||
| 251 | const vector<unsigned char>& vchIV, | ||
| 252 | vector<unsigned char>& vchCiphertextRet) | ||
| 253 | { | ||
| 254 | vchCiphertextRet.clear(); | ||
| 255 | if (vchKey.size() != 32 || vchIV.size() != 16) | ||
| 256 | return false; | ||
| 257 | |||
| 258 | EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new(); | ||
| 259 | if (!ctx) | ||
| 260 | return false; | ||
| 261 | |||
| 262 | bool fOk = false; | ||
| 263 | int nLen = 0; | ||
| 264 | int nFinal = 0; | ||
| 265 | vector<unsigned char> vchOut(vchPlaintext.size() + EVP_CIPHER_block_size(EVP_aes_256_cbc())); | ||
| 266 | if (EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, &vchKey[0], &vchIV[0]) && | ||
| 267 | EVP_EncryptUpdate(ctx, &vchOut[0], &nLen, | ||
| 268 | vchPlaintext.empty() ? NULL : &vchPlaintext[0], | ||
| 269 | (int)vchPlaintext.size()) && | ||
| 270 | EVP_EncryptFinal_ex(ctx, &vchOut[0] + nLen, &nFinal)) | ||
| 271 | { | ||
| 272 | vchOut.resize(nLen + nFinal); | ||
| 273 | vchCiphertextRet.swap(vchOut); | ||
| 274 | fOk = true; | ||
| 275 | } | ||
| 276 | EVP_CIPHER_CTX_free(ctx); | ||
| 277 | return fOk; | ||
| 278 | } | ||
| 279 | |||
| 280 | bool DecryptSecret(const CKeyingMaterial& vchKey, | ||
| 281 | const vector<unsigned char>& vchCiphertext, | ||
| 282 | const vector<unsigned char>& vchIV, | ||
| 283 | vector<unsigned char>& vchPlaintextRet) | ||
| 284 | { | ||
| 285 | vchPlaintextRet.clear(); | ||
| 286 | if (vchKey.size() != 32 || vchIV.size() != 16 || vchCiphertext.empty()) | ||
| 287 | return false; | ||
| 288 | |||
| 289 | EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new(); | ||
| 290 | if (!ctx) | ||
| 291 | return false; | ||
| 292 | |||
| 293 | bool fOk = false; | ||
| 294 | int nLen = 0; | ||
| 295 | int nFinal = 0; | ||
| 296 | vector<unsigned char> vchOut(vchCiphertext.size()); | ||
| 297 | if (EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, &vchKey[0], &vchIV[0]) && | ||
| 298 | EVP_DecryptUpdate(ctx, &vchOut[0], &nLen, &vchCiphertext[0], | ||
| 299 | (int)vchCiphertext.size()) && | ||
| 300 | EVP_DecryptFinal_ex(ctx, &vchOut[0] + nLen, &nFinal)) | ||
| 301 | { | ||
| 302 | vchOut.resize(nLen + nFinal); | ||
| 303 | vchPlaintextRet.swap(vchOut); | ||
| 304 | fOk = true; | ||
| 305 | } | ||
| 306 | EVP_CIPHER_CTX_free(ctx); | ||
| 307 | return fOk; | ||
| 308 | } | ||
| 309 | |||
| 310 | vector<unsigned char> WalletKeyIV(const vector<unsigned char>& vchPubKey) | ||
| 311 | { | ||
| 312 | uint256 hash = Hash(vchPubKey.begin(), vchPubKey.end()); | ||
| 313 | vector<unsigned char> vchIV(16); | ||
| 314 | memcpy(&vchIV[0], hash.begin(), vchIV.size()); | ||
| 315 | return vchIV; | ||
| 316 | } | ||
| 317 | |||
| 318 | vector<unsigned char> WalletSecretIV(const string& strLabel) | ||
| 319 | { | ||
| 320 | uint256 hash = Hash(strLabel.begin(), strLabel.end()); | ||
| 321 | vector<unsigned char> vchIV(16); | ||
| 322 | memcpy(&vchIV[0], hash.begin(), vchIV.size()); | ||
| 323 | return vchIV; | ||
| 324 | } | ||
| 325 | |||
| 326 | bool AddCryptedKey(const vector<unsigned char>& vchPubKey, | ||
| 327 | const vector<unsigned char>& vchCryptedSecret) | ||
| 328 | { | ||
| 329 | if (vchPubKey.empty() || vchCryptedSecret.empty()) | ||
| 330 | return false; | ||
| 331 | CRITICAL_BLOCK(cs_mapKeys) | ||
| 332 | { | ||
| 333 | fWalletEncrypted = true; | ||
| 334 | mapCryptedKeys[vchPubKey] = vchCryptedSecret; | ||
| 335 | mapPubKeys[Hash160(vchPubKey)] = vchPubKey; | ||
| 336 | } | ||
| 337 | return true; | ||
| 338 | } | ||
| 339 | |||
| 340 | bool WalletCanSpendKey(const vector<unsigned char>& vchPubKey) | ||
| 341 | { | ||
| 342 | CRITICAL_BLOCK(cs_mapKeys) | ||
| 343 | return mapKeys.count(vchPubKey) > 0 || mapCryptedKeys.count(vchPubKey) > 0; | ||
| 344 | return false; | ||
| 345 | } | ||
| 346 | |||
| 347 | bool GetWalletPrivKey(const vector<unsigned char>& vchPubKey, | ||
| 348 | CPrivKey& vchPrivKeyRet, | ||
| 349 | string& strErrorRet) | ||
| 350 | { | ||
| 351 | vchPrivKeyRet.clear(); | ||
| 352 | CRITICAL_BLOCK(cs_mapKeys) | ||
| 353 | { | ||
| 354 | map<vector<unsigned char>, CPrivKey>::iterator mi = mapKeys.find(vchPubKey); | ||
| 355 | if (mi != mapKeys.end()) | ||
| 356 | { | ||
| 357 | vchPrivKeyRet = (*mi).second; | ||
| 358 | return true; | ||
| 359 | } | ||
| 360 | |||
| 361 | map<vector<unsigned char>, vector<unsigned char> >::iterator mci = mapCryptedKeys.find(vchPubKey); | ||
| 362 | if (mci == mapCryptedKeys.end()) | ||
| 363 | { | ||
| 364 | strErrorRet = "private key not found"; | ||
| 365 | return false; | ||
| 366 | } | ||
| 367 | if (IsWalletLocked()) | ||
| 368 | { | ||
| 369 | strErrorRet = "wallet is locked"; | ||
| 370 | return false; | ||
| 371 | } | ||
| 372 | |||
| 373 | vector<unsigned char> vchPlaintext; | ||
| 374 | if (!DecryptSecret(vWalletMasterKey, (*mci).second, WalletKeyIV(vchPubKey), vchPlaintext)) | ||
| 375 | { | ||
| 376 | strErrorRet = "could not decrypt private key"; | ||
| 377 | return false; | ||
| 378 | } | ||
| 379 | |||
| 380 | CPrivKey vchPrivKey(vchPlaintext.begin(), vchPlaintext.end()); | ||
| 381 | CKey key; | ||
| 382 | if (!key.SetPrivKey(vchPrivKey) || key.GetPubKey() != vchPubKey) | ||
| 383 | { | ||
| 384 | strErrorRet = "decrypted private key does not match its public key"; | ||
| 385 | return false; | ||
| 386 | } | ||
| 387 | vchPrivKeyRet = vchPrivKey; | ||
| 388 | return true; | ||
| 389 | } | ||
| 390 | return false; | ||
| 391 | } | ||
| 392 | |||
| 393 | bool UnlockWallet(const string& strPassphrase, string& strErrorRet) | ||
| 394 | { | ||
| 395 | strErrorRet.clear(); | ||
| 396 | if (!IsWalletEncrypted()) | ||
| 397 | return true; | ||
| 398 | |||
| 399 | CRITICAL_BLOCK(cs_mapKeys) | ||
| 400 | { | ||
| 401 | for (map<unsigned int, CWalletMasterKey>::iterator mi = mapMasterKeys.begin(); | ||
| 402 | mi != mapMasterKeys.end(); ++mi) | ||
| 403 | { | ||
| 404 | const CWalletMasterKey& kMasterKey = (*mi).second; | ||
| 405 | CKeyingMaterial vchPassKey; | ||
| 406 | vector<unsigned char> vchPassIV; | ||
| 407 | if (!DeriveWalletPassphraseKey(strPassphrase, kMasterKey.vchSalt, | ||
| 408 | kMasterKey.nDeriveIterations, | ||
| 409 | vchPassKey, vchPassIV)) | ||
| 410 | continue; | ||
| 411 | |||
| 412 | vector<unsigned char> vchMasterPlain; | ||
| 413 | if (!DecryptSecret(vchPassKey, kMasterKey.vchCryptedKey, | ||
| 414 | vchPassIV, vchMasterPlain)) | ||
| 415 | continue; | ||
| 416 | if (vchMasterPlain.size() != 32) | ||
| 417 | continue; | ||
| 418 | |||
| 419 | CKeyingMaterial vchCandidate(vchMasterPlain.begin(), vchMasterPlain.end()); | ||
| 420 | bool fValid = mapCryptedKeys.empty(); | ||
| 421 | for (map<vector<unsigned char>, vector<unsigned char> >::iterator mci = mapCryptedKeys.begin(); | ||
| 422 | mci != mapCryptedKeys.end(); ++mci) | ||
| 423 | { | ||
| 424 | vector<unsigned char> vchPlaintext; | ||
| 425 | if (!DecryptSecret(vchCandidate, (*mci).second, WalletKeyIV((*mci).first), vchPlaintext)) | ||
| 426 | break; | ||
| 427 | CPrivKey vchPrivKey(vchPlaintext.begin(), vchPlaintext.end()); | ||
| 428 | CKey key; | ||
| 429 | if (key.SetPrivKey(vchPrivKey) && key.GetPubKey() == (*mci).first) | ||
| 430 | fValid = true; | ||
| 431 | break; | ||
| 432 | } | ||
| 433 | if (fValid) | ||
| 434 | { | ||
| 435 | vWalletMasterKey = vchCandidate; | ||
| 436 | if (!vchCryptedHDMaster.empty()) | ||
| 437 | { | ||
| 438 | vector<unsigned char> vchPlainHDMaster; | ||
| 439 | if (!DecryptSecret(vWalletMasterKey, vchCryptedHDMaster, | ||
| 440 | WalletSecretIV("hdmaster"), vchPlainHDMaster) || | ||
| 441 | vchPlainHDMaster.size() != 32) | ||
| 442 | { | ||
| 443 | strErrorRet = "could not decrypt HD master key"; | ||
| 444 | return false; | ||
| 445 | } | ||
| 446 | vchHDMaster = vchPlainHDMaster; | ||
| 447 | } | ||
| 448 | if (!vchCryptedHDChainCode.empty()) | ||
| 449 | { | ||
| 450 | vector<unsigned char> vchPlainHDChainCode; | ||
| 451 | if (!DecryptSecret(vWalletMasterKey, vchCryptedHDChainCode, | ||
| 452 | WalletSecretIV("hdchaincode"), vchPlainHDChainCode) || | ||
| 453 | vchPlainHDChainCode.size() != 32) | ||
| 454 | { | ||
| 455 | strErrorRet = "could not decrypt HD chain code"; | ||
| 456 | return false; | ||
| 457 | } | ||
| 458 | vchHDChainCode = vchPlainHDChainCode; | ||
| 459 | } | ||
| 460 | fWalletLocked = false; | ||
| 461 | return true; | ||
| 462 | } | ||
| 463 | } | ||
| 464 | } | ||
| 465 | |||
| 466 | strErrorRet = "passphrase did not unlock the wallet"; | ||
| 467 | return false; | ||
| 468 | } | ||
| 469 | |||
| 170 | static bool WalletAlreadyHasKey(const vector<unsigned char>& vchPubKey) | 470 | static bool WalletAlreadyHasKey(const vector<unsigned char>& vchPubKey) |
| 171 | { | 471 | { |
| 172 | CRITICAL_BLOCK(cs_mapKeys) | 472 | CRITICAL_BLOCK(cs_mapKeys) |
| 173 | return mapKeys.count(vchPubKey) > 0; | 473 | return mapKeys.count(vchPubKey) > 0 || mapCryptedKeys.count(vchPubKey) > 0; |
| 174 | return false; | 474 | return false; |
| 175 | } | 475 | } |
| 176 | 476 | ||
| @@ -509,6 +809,12 @@ bool SetHDSeedFromMnemonic(const string& strMnemonic, string& strErrorRet) | |||
| 509 | 809 | ||
| 510 | void TopUpKeyPool() | 810 | void TopUpKeyPool() |
| 511 | { | 811 | { |
| 812 | if (IsWalletLocked()) | ||
| 813 | { | ||
| 814 | printf("TopUpKeyPool() : wallet is encrypted and locked\n"); | ||
| 815 | return; | ||
| 816 | } | ||
| 817 | |||
| 512 | CRITICAL_BLOCK(cs_keyPool) | 818 | CRITICAL_BLOCK(cs_keyPool) |
| 513 | { | 819 | { |
| 514 | while ((int)mapKeyPool.size() < KEYPOOL_SIZE) | 820 | while ((int)mapKeyPool.size() < KEYPOOL_SIZE) |
| @@ -562,6 +868,12 @@ void TopUpKeyPool() | |||
| 562 | 868 | ||
| 563 | vector<unsigned char> GetKeyFromPool() | 869 | vector<unsigned char> GetKeyFromPool() |
| 564 | { | 870 | { |
| 871 | if (IsWalletLocked()) | ||
| 872 | { | ||
| 873 | printf("GetKeyFromPool() : wallet is encrypted and locked\n"); | ||
| 874 | return vector<unsigned char>(); | ||
| 875 | } | ||
| 876 | |||
| 565 | CRITICAL_BLOCK(cs_keyPool) | 877 | CRITICAL_BLOCK(cs_keyPool) |
| 566 | { | 878 | { |
| 567 | TopUpKeyPool(); | 879 | TopUpKeyPool(); |
| @@ -584,7 +896,15 @@ vector<unsigned char> GetKeyFromPool() | |||
| 584 | // locked open elsewhere. Generating on demand is what this code did for | 896 | // locked open elsewhere. Generating on demand is what this code did for |
| 585 | // its whole life, so fall back to it rather than refuse to mine. | 897 | // its whole life, so fall back to it rather than refuse to mine. |
| 586 | printf("GetKeyFromPool() : pool empty, generating a key on demand\n"); | 898 | printf("GetKeyFromPool() : pool empty, generating a key on demand\n"); |
| 587 | return GenerateNewKey(); | 899 | try |
| 900 | { | ||
| 901 | return GenerateNewKey(); | ||
| 902 | } | ||
| 903 | catch (const std::exception& e) | ||
| 904 | { | ||
| 905 | printf("GetKeyFromPool() : failed to generate a key: %s\n", e.what()); | ||
| 906 | return vector<unsigned char>(); | ||
| 907 | } | ||
| 588 | } | 908 | } |
| 589 | 909 | ||
| 590 | 910 | ||
| @@ -3581,6 +3901,11 @@ bool BitcoinMiner(int nThreadId) | |||
| 3581 | // thread happened to win a block, which meant a backup taken while mining | 3901 | // thread happened to win a block, which meant a backup taken while mining |
| 3582 | // could not spend what the very next block paid. | 3902 | // could not spend what the very next block paid. |
| 3583 | vector<unsigned char> vchPubKey = GetKeyFromPool(); | 3903 | vector<unsigned char> vchPubKey = GetKeyFromPool(); |
| 3904 | if (vchPubKey.empty()) | ||
| 3905 | { | ||
| 3906 | printf("BitcoinMiner: no wallet key available for coinbase; mining stopped\n"); | ||
| 3907 | return false; | ||
| 3908 | } | ||
| 3584 | CBigNum bnExtraNonce = 0; | 3909 | CBigNum bnExtraNonce = 0; |
| 3585 | while (fGenerateBitcoins) | 3910 | while (fGenerateBitcoins) |
| 3586 | { | 3911 | { |
| @@ -3759,6 +4084,11 @@ bool BitcoinMiner(int nThreadId) | |||
| 3759 | // them out one at a time under cs_keyPool, which is | 4084 | // them out one at a time under cs_keyPool, which is |
| 3760 | // what keeps concurrent coinbases distinct (#58). | 4085 | // what keeps concurrent coinbases distinct (#58). |
| 3761 | vchPubKey = GetKeyFromPool(); | 4086 | vchPubKey = GetKeyFromPool(); |
| 4087 | if (vchPubKey.empty()) | ||
| 4088 | { | ||
| 4089 | printf("BitcoinMiner: no replacement wallet key available; mining stopped\n"); | ||
| 4090 | fGenerateBitcoins = false; | ||
| 4091 | } | ||
| 3762 | 4092 | ||
| 3763 | // Process this block the same as if we had received it from another node | 4093 | // Process this block the same as if we had received it from another node |
| 3764 | if (!ProcessBlock(NULL, pblock.release())) | 4094 | if (!ProcessBlock(NULL, pblock.release())) |