[RFC] Wallet: Store unencrypted keys as encrypted with a default or plaintext passphrase #18889

issue achow101 opened this issue on May 5, 2020
  1. achow101 commented at 4:54 PM on May 5, 2020: member

    Currently we store unencrypted private keys in a key record, and encrypted private keys in a ckey record. During encryption, all key records are deleted and the keys encrypted and written into new ckey records. However database systems and filesystems do not always remove data when it is deleted, many simply mark the data as deleted for it to be overwritten later. In databases like BDB, some deleted records are kept around to help keep the structure of the b-tree. This fact has resulted in a notable issue in the original wallet encryption implementation where the unencrypted private keys were kept in the wallet.dat file and in the database transaction logs after the encryption was done. If/when we move to a new storage method, this same issue will likely crop up again and another workaround developed for that specific storage method. However modifying records is usually done in place and overwrites existing records.

    I would like to suggest a different way that this could be handled that is independent of the storage method. Instead of storing private keys unencrypted, we can always encrypt the private keys using either a default passphrase or a passphrase that is stored in plaintext in the wallet file. So all private keys would be stored in ckey records. "Unencrypted" then means that the passphrase is either publicly known (a default passphrase) or easily found within the wallet.dat file itself. Encrypting the wallet would then be our typical passphrase changing mechanism.

    Specifically, each ckey is encrypted with a randomly generated CMasterKey. The CMasterKey itself is encrypted with a passphrase. An "unencrypted" wallet encrypts the CMasterKey with some default passphrase. During encryption, as we do now for passphrase changing, the CMasterKey is decrypted in memory and encrypted with the new passphrase. The original CMasterKey record is overwritten. Of course we would still regenerate the keypool and set a new HD seed during this process.

    Alternatively, instead of using a default passphrase, a randomly generated passphrase can be used to encrypt the CMasterKey. This randomly generated passphrase can be stored on disk in a new record. When the wallet is actually encrypted by the user, a new CMasterKey can be generated and each key re-encrypted with the new CMasterKey along with the CMasterKey itself being encrypted with the new passphrase. Then the passphrase record can be deleted. Since all of these modifications are updates to existing records, there shouldn't be anything that could remain in slack space that reveals the private keys. By rotating the CMasterKey, the old plaintext passphrase and its CMasterKey are not useful even if they are left behind due to deletion not always deleting. This approach could also be used with a default passphrase too.

    There are a few benefits from doing this: we avoid having to ensure that unencrypted private keys never get left behind when encrypting, and ckey records are way smaller than key records so we get the space savings too. Some downsides are that it is possible that this could make wallet recovery much more difficult. If a wallet becomes corrupted and the CMasterKey encrypting the keys is lost, the keys would be lost too. But perhaps that could be resolved by using a default CMasterKey instead of a default passphrase.

    Thoughts?

  2. achow101 added the label Feature on May 5, 2020
  3. maflcko commented at 5:41 PM on May 5, 2020: member

    Concept ACK

  4. maflcko added the label Brainstorming on May 5, 2020
  5. maflcko added the label Wallet on May 5, 2020
  6. ryanofsky commented at 6:50 PM on May 5, 2020: contributor

    Then the passphrase record can be deleted. Since all of these modifications are updates to existing records, there shouldn't be anything that could remain in slack space that reveals the private keys

    If you are satisfied overwriting the passphrase record effectively deletes it, it seems like you could also be satisfied that overwriting key records would effectively delete them, and just make a small code change to overwrite them instead of adding a new encryption scheme.

    Support for this does sound like a good idea either way. Probably would improve encryption performance and make likelihood of old keys being unintentionally recoverable from the database or filesystem smaller. Not the best thing for backwards compatibility if a new wallet by default can't be opened by previous versions of the software, but maybe this could be rolled out gradually and not turned on by default at first.

  7. bitcoin deleted a comment on May 11, 2020
  8. tryphe commented at 3:05 AM on May 19, 2020: contributor

    Concept ACK for using a default passphrase and gradual rollout, nACK at using a random passphrase as it seems unnecessary here to increase the surface area of data which could lead to coin loss.

  9. maflcko referenced this in commit 7418169364 on May 21, 2020
  10. sidhujag referenced this in commit 30cd7e0406 on May 21, 2020
  11. willcl-ark closed this on Sep 21, 2023

  12. knst referenced this in commit b07e008c56 on Oct 19, 2023
  13. PastaPastaPasta referenced this in commit 01adc81ebf on Oct 23, 2023
  14. bitcoin locked this on Sep 20, 2024

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-17 06:14 UTC

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