Wallet Private Key Encryption (on CWallet) #352

pull TheBlueMatt wants to merge 12 commits into bitcoin:master from TheBlueMatt:newenc changing 24 files +1588 −160
  1. TheBlueMatt commented at 12:56 AM on June 27, 2011: member

    This commit adds support for ckeys, or enCrypted private keys, to the wallet.

    See commit message for a more detailed description.

  2. TheBlueMatt commented at 12:58 AM on June 27, 2011: member
  3. TheBlueMatt commented at 1:05 AM on June 27, 2011: member

    Changed topupkeypool to keypoolrefill at tcatm's suggestion

  4. TheBlueMatt commented at 1:46 AM on June 27, 2011: member

    A note for users of the previous version - Ill write a converter tomorrow that will try to convert the old encrypted wallets to this new format.

  5. TheBlueMatt commented at 9:34 PM on June 27, 2011: member

    TODO List: Anything marked with //TODO s/password/passphrase/ write better text for first encryption ie "DONT LOSE THIS PASSWORD" and such An option to decrypt the wallet? (Decided against this one as noone seemed to care if it got implemented, if others here disagree, please respond) Figure out why walletphassphrase is lagging (top up keypool?)

  6. gavinandresen commented at 1:57 AM on June 28, 2011: contributor

    Looks OK, I like the feature set, I haven't had a chance to compile and test it.

  7. jgarzik commented at 7:40 PM on June 28, 2011: contributor

    Looks OK at first glance. Will give it an in-depth look and test RSN.

    One nit: mlock() can definitely fail based variable conditions such as ulimit. The code never checks for mlock failure, and seems to assume you may munlock even if mlock failed.

  8. TheBlueMatt commented at 8:22 PM on June 28, 2011: member

    If it fails, it should continue, yes we are potentially not as secure, but we should stop just because we cant mlock(). Also, since munlock() is called implicitly when we deallocate, its unnecessary, I pulled it out. Oh got Im stupid, sorry, readded that.

  9. gmaxwell commented at 6:50 PM on June 30, 2011: contributor

    I spent a couple days beating the hell out of this implementation and Matt has quickly fixed all the bugs I've encountered.

    In particular I've now sent thousands of transactions and thousands of getaddresses in an abusive testing rig which is constantly unlocking/locking/password changing/account moving, etc. and not lost any coin. I've basically run out of obvious automated testing at this point.

    I am, however, unable to do GUI testing, so I hope and assume other people are beating on that.

    To the best of my ability to determine this is now ready for mainline and testing by a broader audience.

    I think that before release the wallettools probably need to be updated to support this: both as an additional validation of the implementation, but also so that there isn't a gap in recovery tools, but I don't see any reason that should block mainlining it.

  10. joric commented at 8:31 AM on July 4, 2011: none

    Just tried to build 499334e on win32 using mingw, works fine, except a couple of things.

    1. In crypter.cpp you have to move headers.h to the very top otherwise you'll get a whole bunch of "Cannot convert from 'const wchar_t *' to '_TCHAR *'" (more precisely, wx.h should be included before windows.h).
    2. Cancel button in the encryption dialog does not work as expected (buggy wxGetPasswordFromUser). P.S. Also note encryption can't be undone yet, so don't try it on your wallets. )
  11. joric commented at 9:14 AM on July 4, 2011: none

    Just figured out if you're trying to read encrypted wallet with official client (0.3.24rc1 in my case) wallet gets destroyed and not readable by anything. It's not very nice.

  12. TheBlueMatt commented at 10:51 AM on July 4, 2011: member

    If you try to read it with the old client, the client won't (shouldn't) touch it and just complain that it is "Unable to read wallet.dat". However, if you reopen it with this client, it should open fine. Also, thanks for the mingw heads-up, I suppose I must've changed the header order at some point after I tested on mingw... Oh well, should be fixed now.

  13. joric commented at 11:22 AM on July 4, 2011: none

    Well, not really. Official 0.3.23 says basically the same as 0.3.24rc1: EXCEPTION: St13runtime_error ReserveKeyFromKeyPool() : unknown key in key pool (...)bitcoin.exe in AppInit(), EXCEPTION: St13runtime_error ReserveKeyFromKeyPool() : unknown key in key pool (...)bitcoin.exe in CMyApp::OnUnhandledException(), Runtime error!

    After that wallet.dat becomes completely inaccesible, 499334e says "Error loading wallet.dat". If someone is interested I've tried that only on a new wallet (with no transactions).

  14. TheBlueMatt commented at 12:27 PM on July 4, 2011: member

    Ah, shit you are totally right, and here I was thinking the client was smarter than that...Ill make sure a fix goes into 0.3.24 so at least people can downgrade to that safely. Oh, forgot to mention, I changed master key format last night, so wallet encrypted with this previously will not load.

  15. TheBlueMatt commented at 12:30 PM on July 4, 2011: member

    See #378

  16. graingert commented at 8:13 AM on July 5, 2011: contributor

    would it be possible to encrypt the wallet with a null/default pass-phrase by default rather than leaving the wallet unencrypted?

  17. TheBlueMatt commented at 11:33 AM on July 5, 2011: member

    null/default passphrase is 100% identical to no encryption, if an attacker gets the wallet, they can still just dump it in their .bitcoin folder and send coins, its better to be backward compatible if users dont encrypt than just break that too for nothing.

  18. Prepare codebase for Encrypted Keys. acd6501610
  19. mlock() all private keys in memory
    Inline comment and idea come from the encprivkeys branch
    by Matt Corallo <matt@bluematt.me>.
    c1aacf0be3
  20. sipa commented at 3:32 PM on July 9, 2011: member

    I am not sure the mlock() wrappers are correct. ((void *)(((size_t)(a)) & ((size_t)(((PAGESIZE)<<1)-1)))) seems to throw away the high bits of the address, instead of the low ones.

    I suggest using:

    mlock((void*)((size_t)a & ~(PAGESIZE - 1)), ((((size_t)a + b - 1) | (PAGESIZE - 1)) + 1) - ((size_t)a & ~(PAGESIZE - 1)));
    

    Here is a test program:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <assert.h>
    
    #define PAGESIZE 4096
    
    int my_mlock(void *a, size_t b)
    {
        void *start = (void*)((size_t)a & ~(PAGESIZE - 1));
        void *stop  = (void*)((((size_t)a + b - 1) | (PAGESIZE - 1)) + 1);
        size_t range = stop - start;
        assert(start <= a);
        assert(start+range >= (a+b));
        assert((((size_t)start) & (PAGESIZE-1)) == 0);
        assert((((size_t)start+range) & (PAGESIZE-1)) == 0);
        // return mlock(start, range);
        return 0;
    }
    
    int main(void)
    {
        size_t as[] = {0x400000, 0x3FFFFF, 0x3FFF00, 0x400100};
        size_t bs[] = {0,1,2,3,4,7,15,127,128,129,255,256,257,4000,4095,4096,4097,4100,4200,8000,8190,8300};
        for (int na=0; na<sizeof(as)/sizeof(as[0]); na++) {
          for (int nb=0; nb<sizeof(bs)/sizeof(bs[0]); nb++) {
            my_mlock((void*)(as[na]),bs[nb]);
          }
        }
        return 0;
    }
    
  21. sipa commented at 3:36 PM on July 9, 2011: member

    Apart from the above comment: ACK.

  22. Make mlock() and munlock() portable to systems that require the address to be on a page boundary. a48c671957
  23. TheBlueMatt commented at 11:24 PM on July 12, 2011: member

    re gavin's comment "This needs a comment explaining what it is for-- "This class writes an addrIncoming entry that causes pre-0.4 clients to crash on startup if reading a private-key-encrypted wallet."" done.

  24. Add wallet privkey encryption.
    This commit adds support for ckeys, or enCrypted private keys, to the wallet.
    All keys are stored in memory in their encrypted form and thus the passphrase
    is required from the user to spend coins, or to create new addresses.
    
    Keys are encrypted with AES-256-CBC using OpenSSL's EVP library. The key is
    calculated via EVP_BytesToKey using SHA512 with (by default) 25000 rounds and
    a random salt.
    
    By default, the user's wallet remains unencrypted until they call the RPC
    command encryptwallet <passphrase> or, from the GUI menu, Options->
    Encrypt Wallet.
    
    When the user is attempting to call RPC functions which require the password
    to unlock the wallet, an error will be returned unless they call
    walletpassphrase <passphrase> <time to keep key in memory> first.
    
    A keypoolrefill command has been added which tops up the users keypool
    (requiring the passphrase via walletpassphrase first).
    keypoolsize has been added to the output of getinfo to show the user the
    number of keys left before they need to specify their passphrase (and call
    keypoolrefill).
    
    Note that walletpassphrase will automatically fill keypool in a separate
    thread which it spawns when the passphrase is set. This could cause some
    delays in other threads waiting for locks on the wallet passphrase, including
    one which could cause the passphrase to be stored longer than expected,
    however it will not allow the passphrase to be used longer than expected as
    ThreadCleanWalletPassphrase will attempt to get a lock on the key as soon
    as the specified lock time has arrived.
    
    When the keypool runs out (and wallet is locked) GetOrReuseKeyFromPool
    returns vchDefaultKey, meaning miners may start to generate many blocks to
    vchDefaultKey instead of a new key each time.
    
    A walletpassphrasechange <oldpassphrase> <newpassphrase> has been added to
    allow the user to change their password via RPC.
    
    Whenever keying material (unencrypted private keys, the user's passphrase,
    the wallet's AES key) is stored unencrypted in memory, any reasonable attempt
    is made to mlock/VirtualLock that memory before storing the keying material.
    This is not true in several (commented) cases where mlock/VirtualLocking the
    memory is not possible.
    
    Although encryption of private keys in memory can be very useful on desktop
    systems (as some small amount of protection against stupid viruses), on an
    RPC server, the password is entered fairly insecurely. Thus, the only main
    advantage encryption has for RPC servers is for RPC servers that do not spend
    coins, except in rare cases, eg. a webserver of a merchant which only receives
    payment except for cases of manual intervention.
    
    Thanks to jgarzik for the original patch and sipa, gmaxwell and many others
    for all their input.
    
    Conflicts:
    
    	src/wallet.cpp
    4e87d341f7
  25. Set the number of SHA512 rounds based on the speed of the computer. ddebdd9a8f
  26. Push unlocked_until in getinfo. 98545d2cdf
  27. Dynamically remove/insert the Options for encryption in the menus. 81598083e7
  28. Add the walletlock RPC method to lock the wallet manually. fbeb5fb483
  29. Add Wallet Encryption section to README b6b039d84e
  30. Do not use obsolete CPrivKey for passing keys around 0efda1a79e
  31. Use DB Transactions when encrypting wallet.
    This speeds up the encryption process significantly.
    96f34cd5c4
  32. Make an invalid addrIncoming so that old clients crash.
    This prevents old clients from opening, and thus corrupting
    or otherwise causing harm to encrypted wallets.
    7414733bea
  33. gavinandresen commented at 1:34 AM on July 13, 2011: contributor

    ACK, looks ready-for-prime-time to me.

  34. jgarzik referenced this in commit 0bad8e4237 on Jul 13, 2011
  35. jgarzik merged this on Jul 13, 2011
  36. jgarzik closed this on Jul 13, 2011

  37. groffer commented at 2:29 AM on July 16, 2011: none

    Unit tests?

  38. ptschip referenced this in commit 7f83f8df3b on Mar 11, 2017
  39. classesjack referenced this in commit f6bdd363f6 on Jan 2, 2018
  40. NickyYangYang commented at 3:38 AM on July 16, 2018: none

    Why the unencrypted master key is stored in memory? Why not unencrypted private key?

  41. fjahr referenced this in commit 1086fda4c1 on Jul 24, 2019
  42. 0xartem referenced this in commit 91ca83d913 on Feb 29, 2020
  43. 0xartem referenced this in commit 55929a99a5 on Feb 29, 2020
  44. DrahtBot locked this on Sep 8, 2021

github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin/bitcoin. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2026-04-24 15:16 UTC

This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me