-
Notifications
You must be signed in to change notification settings - Fork 38.7k
Refactor: Move encryption code between KeyMan and Wallet #17369
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers. ConflictsReviewers, this pull request conflicts with the following ones:
If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first. |
ryanofsky
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code review ACK d96925bde5c103693e44787503dc3e45d1248da8
This PR does a lot of different things:
-
Adds new
LegacyScriptPubKeyMan::CheckDecryptionKey()method containing most of the code that was previously part ofCWallet::Unlock(CKeyingMaterial).CWallet::Unlock(CKeyingMaterial)now callsLegacyScriptPubKeyMan::CheckDecryptionKey()so there is no change in behavior.-
Sidebar 1: A little confusingly, there are two
CWallet::Unlockmethods:CWallet::Unlock(CKeyingMaterial)andCWallet::Unlock(SecureString)and the latter unlock calls the former unlock after finding and decrypting a "master key" inCWallet::mapMasterKeysassociated with theSecureStringpassphrase. -
Sidebar 2: "master key" throughout this code does not refer to a BIP32 master key, but to a symmetric encryption key used to encrypt the BIP32 master key and other keys. The BIP32 master key is only kept in memory as a hashed "seed id" in
LegacyScriptPubKeyMan::hdChain.seed_id, and is generally referred to in wallet code as an "hd seed" or "hd chain" instead of as a master key.
-
-
Moves
fDecryptionThoroughlyCheckedmember variable fromCWalletclass toLegacyScriptPubKeyManclass. This has no effect on behavior and probably should have happened in #17260, but at the time code there was put together, encryption code was still in flux.LegacyScriptPubKeyMan::fDecryptionThoroughlyCheckedis only used internally byLegacyScriptPubKeyMan::CheckDecryptionKey()to avoid repeating the same checks every time the wallet is unlocked. -
Adds new
CWallet::HasEncryptionKeys()method.HasEncyptionKeys()returns true ifCWallet::mapMasterKeysis not empty, which is true if a wallet passphrase has been ever been set, even if the wallet doesn't have any seeds or keys or address data. Again,mapMasterKeysdoes NOT hold BIP32 master keys, but symmetric encryption keys (see Sidebar 2 above) encrypted with passphrases. -
Removes the
CWallet::fUseCryptomember variable andCWallet::SetCrypted()method and changesCWallet::IsCrypted()to returnHasEncryptionKeys()instead offUseCrypto. The meaning offUseCryptowas not describable in any simple terms: It was false by default and true ifCWallet::LockorCWallet::Unlock(CKeyingMaterial)orLegacyScriptPubKeyMan::AddCryptedKeyInnerhad previously been called AND ifFillableSigningProvider::mapKeyswas empty. I wasn't able to determine if or why there aren't bugs in the current wallet code caused byIsCrypted()returning false when it would more correctly return true, but the new implementation ofIsCrypted()in this PR seems vastly simpler and safer. -
Replaces
if (!IsCrypted())checks withif (!m_storage.HasEncryptionKeys())checks. After the change described in the previous point, this is equivalent, just replacing indirect calls with direct ones. -
Replaces two
if (!SetCrypted())checks withif (!mapKeys.empty()checks. These checks are equivalent becauseSetCrypted()always returned true unlessFillableSigningProvider::mapKeyshad (unencrypted) keys. I believe these checks are impossible to trigger, and should probably be asserts instead. Otherwise they could use comments explaining what their purpose is. -
Replaces
CWallet::vMasterKeyaccesses with calls to a new methodCWallet::GetEncryptionKey()returning the same value. This is just to avoid the key manager accessing a wallet variable.CWallet::vMasterKeyholds a master decryption key decrypted with the passphrase and is only set while the wallet is unlocked. -
Renames
LegacyScriptPubKeyMan::EncryptKeys()toLegacyScriptPubKeyMan::Encrypt()for fun and entertainment. -
Passes a new
WalletBatch* batchargument toLegacyScriptPubKeyMan::Encrypt()which is used to set theLegacyScriptPubKeyMan::encrypted_batchmember while the function is running. This is equivalent to the previous code because the previous caller,CWallet::EncryptWallet(), was setting theLegacyScriptPubKeyMan::encrypted_batchmember itself, but is no longer doing that because as a wallet method, it shouldn't have access to key manager internals. -
Tweaks
LegacyScriptPubKeyMan::Encryptto clearFillableSigningProvider::mapKeys/before/ encrypting keys rather than after. This is equivalent to previous behavior, and the change was only made because (as a new comment there states) it is necessary to makeAddCryptedKeyInner()work now thatSetCrypted()is gone and it is checking forFillableSigningProvider::mapKeysto be empty.
src/wallet/scriptpubkeyman.cpp
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In commit "Refactor: Move encryption code between KeyMan and Wallet" (d96925bde5c103693e44787503dc3e45d1248da8)
Would be great if this check and the same check in LegacyScriptPubKeyMan::AddCryptedKeyInner could be made into asserts or CHECK_NONFATAL's, because it seems like something would have to be very wrong for these to trigger. Or it would be helpful to have a comment saying when this is expected, if it is ever expected.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed to asserts.
src/wallet/wallet.h
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In commit "Refactor: Move encryption code between KeyMan and Wallet" (d96925bde5c103693e44787503dc3e45d1248da8)
Note: Future cleanup could delete this method and just have callers call HasEncryptionKeys() directly instead.
d96925b to
4b5c54b
Compare
ryanofsky
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code review ACK 4b5c54b5be86890c0f46c2b3139014c8a88011a1. No change since last review other than rebase after trivial conflict with #15931
|
Approach ACKs for this PR? Does it look good and reviewable? (Recommend to just look at the code and ignore my overlong description above which is mostly just notes for myself about previous behavior.) I think the changes here are great and make wallet encryption state much easier to understand than before. This PR could potentially be split up into multiple commits based on the bullet points in #17369 (review), but IMO the current single commit is not hard to review from top to bottom once you basically understand what the code is doing. |
|
@instagibbs and @meshcollider, you may be interested to review this. This is code you previously acked when it was part of #16341 |
|
I think splitting up into separate commits would be beneficial for the current and future readers. |
4b5c54b to
49e1fbd
Compare
ryanofsky
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code review ACK 49e1fbd016cc0ba4ad32ddd88f8ca0b38c41f3e9. No changes since last review other than rebase after a trivial conflict with #17371
49e1fbd to
c079263
Compare
|
I've split this up into several commits each that does one or two bullets from the breakdown earlier. The final diff is identical. (Accidentally screwed up the number of commits I was editing during the rebase and now the force push history is messed up, sorry) |
3a55e9e to
5c8f0f8
Compare
|
much more understandable split up, thank you. utACK 5c8f0f8 |
Adds functions in WalletStorage that allow ScriptPubKeyMans to check and get encryption keys from the wallet.
CWallet::Unlock is changed to call ScriptPubKeyMan::CheckDecryptionKey and the original implementation of Unlock is renamed to CheckDecryptionKey.
Does not change behavior. Needed to make AddCryptedKeyInner() work with SetCrypted() being gone.
Removes SetCrypted() and fUseCrypto as we don't need them anymore. SetCrypted calls in LegacyScriptPubKeyMan are replaced with mapKeys.empty() IsCrypted() is changed to just call HasEncryptionKeys()
…HasEncryptionKeys
5c8f0f8 to
7cecf10
Compare
|
ACK 7cecf10 |
7cecf10 Replace LegacyScriptPubKeyMan::IsCrypted with LegacyScriptPubKeyMan::HasEncryptionKeys (Andrew Chow) bf64171 Remove SetCrypted() and fUseCrypto; Change IsCrypted()'s implementation (Andrew Chow) 77a7771 Rename EncryptKeys to Encrypt and pass in the encrypted batch to use (Andrew Chow) 35f962f Clear mapKeys before encrypting (Andrew Chow) 14b5efd Move fDecryptionThoroughlyChecked from CWallet to LegacyScriptPubKeyMan (Andrew Chow) 97c0374 Move Unlock implementation to LegacyScriptPubKeyMan (Andrew Chow) e576b13 Replace LegacyScriptPubKeyMan::vMasterKey with GetDecryptionKey() (Andrew Chow) fd9d6ee Add GetEncryptionKey() and HasEncryptionKeys() to WalletStorage (Andrew Chow) Pull request description: Let wallet class handle locked/unlocked status and master key, and let keyman handle encrypting its data and determining whether there is encrypted data. There should be no change in behavior, but state is tracked differently. The fUseCrypto atomic bool is eliminated and replaced with equivalent HasEncryptionKeys checks. Split from #17261 ACKs for top commit: laanwj: ACK 7cecf10 Tree-SHA512: 95a997c366ca539abba0c0a7a0015f39d27b55220683d8d86344ff2d926db4724da67700d2c8ec2d82ed75d07404318c6cb81544af8aadeefab312167257e673
…d Wallet 7cecf10 Replace LegacyScriptPubKeyMan::IsCrypted with LegacyScriptPubKeyMan::HasEncryptionKeys (Andrew Chow) bf64171 Remove SetCrypted() and fUseCrypto; Change IsCrypted()'s implementation (Andrew Chow) 77a7771 Rename EncryptKeys to Encrypt and pass in the encrypted batch to use (Andrew Chow) 35f962f Clear mapKeys before encrypting (Andrew Chow) 14b5efd Move fDecryptionThoroughlyChecked from CWallet to LegacyScriptPubKeyMan (Andrew Chow) 97c0374 Move Unlock implementation to LegacyScriptPubKeyMan (Andrew Chow) e576b13 Replace LegacyScriptPubKeyMan::vMasterKey with GetDecryptionKey() (Andrew Chow) fd9d6ee Add GetEncryptionKey() and HasEncryptionKeys() to WalletStorage (Andrew Chow) Pull request description: Let wallet class handle locked/unlocked status and master key, and let keyman handle encrypting its data and determining whether there is encrypted data. There should be no change in behavior, but state is tracked differently. The fUseCrypto atomic bool is eliminated and replaced with equivalent HasEncryptionKeys checks. Split from bitcoin#17261 ACKs for top commit: laanwj: ACK 7cecf10 Tree-SHA512: 95a997c366ca539abba0c0a7a0015f39d27b55220683d8d86344ff2d926db4724da67700d2c8ec2d82ed75d07404318c6cb81544af8aadeefab312167257e673
…llet Summary: 7cecf10ac32af0fca206ac5f24f482bdec88cb7d Replace LegacyScriptPubKeyMan::IsCrypted with LegacyScriptPubKeyMan::HasEncryptionKeys (Andrew Chow) bf6417142f36a2f75b3a11368bd73fe788ae1ccb Remove SetCrypted() and fUseCrypto; Change IsCrypted()'s implementation (Andrew Chow) 77a777118eaf78f10a439810d1c08d510a539aa0 Rename EncryptKeys to Encrypt and pass in the encrypted batch to use (Andrew Chow) 35f962fcf0d5107ae6a3a9348e249a9b18ff7106 Clear mapKeys before encrypting (Andrew Chow) 14b5efd66ff0afbf3bf9158a724534a9090fc7fc Move fDecryptionThoroughlyChecked from CWallet to LegacyScriptPubKeyMan (Andrew Chow) 97c0374a46943b2ed38ea24eeeff1f1568dd55b3 Move Unlock implementation to LegacyScriptPubKeyMan (Andrew Chow) e576b135d6451101d6a8219f55d80aefa216dc38 Replace LegacyScriptPubKeyMan::vMasterKey with GetDecryptionKey() (Andrew Chow) fd9d6eebc1eabb4675a118d19d38283da2dead39 Add GetEncryptionKey() and HasEncryptionKeys() to WalletStorage (Andrew Chow) Pull request description: Let wallet class handle locked/unlocked status and master key, and let keyman handle encrypting its data and determining whether there is encrypted data. There should be no change in behavior, but state is tracked differently. The fUseCrypto atomic bool is eliminated and replaced with equivalent HasEncryptionKeys checks. Split from #17261 --- Depends on D7697 Backport of Core [[bitcoin/bitcoin#17369 | PR17369]] Test Plan: ninja check check-functional Reviewers: #bitcoin_abc, deadalnix Reviewed By: #bitcoin_abc, deadalnix Subscribers: deadalnix Differential Revision: https://reviews.bitcoinabc.org/D7698
…d Wallet 7cecf10 Replace LegacyScriptPubKeyMan::IsCrypted with LegacyScriptPubKeyMan::HasEncryptionKeys (Andrew Chow) bf64171 Remove SetCrypted() and fUseCrypto; Change IsCrypted()'s implementation (Andrew Chow) 77a7771 Rename EncryptKeys to Encrypt and pass in the encrypted batch to use (Andrew Chow) 35f962f Clear mapKeys before encrypting (Andrew Chow) 14b5efd Move fDecryptionThoroughlyChecked from CWallet to LegacyScriptPubKeyMan (Andrew Chow) 97c0374 Move Unlock implementation to LegacyScriptPubKeyMan (Andrew Chow) e576b13 Replace LegacyScriptPubKeyMan::vMasterKey with GetDecryptionKey() (Andrew Chow) fd9d6ee Add GetEncryptionKey() and HasEncryptionKeys() to WalletStorage (Andrew Chow) Pull request description: Let wallet class handle locked/unlocked status and master key, and let keyman handle encrypting its data and determining whether there is encrypted data. There should be no change in behavior, but state is tracked differently. The fUseCrypto atomic bool is eliminated and replaced with equivalent HasEncryptionKeys checks. Split from bitcoin#17261 ACKs for top commit: laanwj: ACK 7cecf10 Tree-SHA512: 95a997c366ca539abba0c0a7a0015f39d27b55220683d8d86344ff2d926db4724da67700d2c8ec2d82ed75d07404318c6cb81544af8aadeefab312167257e673
Let wallet class handle locked/unlocked status and master key, and let keyman
handle encrypting its data and determining whether there is encrypted data.
There should be no change in behavior, but state is tracked differently. The
fUseCrypto atomic bool is eliminated and replaced with equivalent
HasEncryptionKeys checks.
Split from #17261