upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/src/main.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/main.cpp')
-rw-r--r--src/main.cpp334
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;
72CCriticalSection cs_mapWallet; 72CCriticalSection cs_mapWallet;
73 73
74map<vector<unsigned char>, CPrivKey> mapKeys; 74map<vector<unsigned char>, CPrivKey> mapKeys;
75map<vector<unsigned char>, vector<unsigned char> > mapCryptedKeys;
75map<uint160, vector<unsigned char> > mapPubKeys; 76map<uint160, vector<unsigned char> > mapPubKeys;
77map<unsigned int, CWalletMasterKey> mapMasterKeys;
78unsigned int nWalletMasterKeyMaxID = 0;
79CKeyingMaterial vWalletMasterKey;
80vector<unsigned char> vchCryptedHDMaster;
81vector<unsigned char> vchCryptedHDChainCode;
82bool fWalletEncrypted = false;
83bool fWalletLocked = true;
76CCriticalSection cs_mapKeys; 84CCriticalSection cs_mapKeys;
77CKey keyUser; 85CKey keyUser;
78 86
@@ -159,6 +167,21 @@ int MinerThreadCount()
159 167
160bool AddKey(const CKey& key) 168bool 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
193bool IsWalletEncrypted()
194{
195 return fWalletEncrypted;
196}
197
198bool IsWalletLocked()
199{
200 return fWalletEncrypted && fWalletLocked;
201}
202
203void 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
218bool 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
249bool 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
280bool 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
310vector<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
318vector<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
326bool 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
340bool 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
347bool 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
393bool 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
170static bool WalletAlreadyHasKey(const vector<unsigned char>& vchPubKey) 470static 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
510void TopUpKeyPool() 810void 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
563vector<unsigned char> GetKeyFromPool() 869vector<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()))