A couple of users have requested that we support wallets that encrypt everything, even if the wallet is watch-only, in order to have better privacy if the wallet is stolen. This PR introduces an EncryptedDatabase
backend for the wallet which encrypts/decrypts each key-value record individually before reading from or writing to the database.
EncryptedDatabase
is only supported for SQLite databases and descriptor wallets. This was done in order to have an easier way to get downgrade protection that also does not involve writing an existing record in plaintext (e.g. minversion
or flags
). SQLite has a fixed field “application id” that we already use for cross-network protection. This is reused to detect if a sqlite database is an encrypted wallet, and thus it prevents older software from attempting to open such wallets.
In order to read records from the database, EncryptedDatabase
will cache the decrypted key in memory so that it can lookup the encrypted key in the database. Values will always be decrypted when read.
The encryption scheme is the same one that we use for encrypting private keys. It’s not that great, but I didn’t feel like re-implementing it, and it seems good enough. The encryption key itself is encrypted with the passphrase and stored in a new record.
Wallets with encrypted databases cannot be loaded on start. They can only be loaded by explicit user action through loadwallet
which now has a db_passphrase
parameter to allow the decryption of these wallets. createwallet
also has a db_passphrase
to create these wallets. For now, there is no way to encrypt the database after it has been created, nor is there a way to change the passphrase.