Skip to content

Conversation

@achow101
Copy link
Member

@achow101 achow101 commented Nov 4, 2019

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

@DrahtBot
Copy link
Contributor

DrahtBot commented Nov 4, 2019

The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

Conflicts

Reviewers, this pull request conflicts with the following ones:

  • #16910 (wallet: reduce loading time by using unordered maps by achow101)

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.

Copy link
Contributor

@ryanofsky ryanofsky left a 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 of CWallet::Unlock(CKeyingMaterial). CWallet::Unlock(CKeyingMaterial) now calls LegacyScriptPubKeyMan::CheckDecryptionKey() so there is no change in behavior.

    • Sidebar 1: A little confusingly, there are two CWallet::Unlock methods: CWallet::Unlock(CKeyingMaterial) and CWallet::Unlock(SecureString) and the latter unlock calls the former unlock after finding and decrypting a "master key" in CWallet::mapMasterKeys associated with the SecureString passphrase.

    • 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 fDecryptionThoroughlyChecked member variable from CWallet class to LegacyScriptPubKeyMan class. 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::fDecryptionThoroughlyChecked is only used internally by LegacyScriptPubKeyMan::CheckDecryptionKey() to avoid repeating the same checks every time the wallet is unlocked.

  • Adds new CWallet::HasEncryptionKeys() method. HasEncyptionKeys() returns true if CWallet::mapMasterKeys is 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, mapMasterKeys does NOT hold BIP32 master keys, but symmetric encryption keys (see Sidebar 2 above) encrypted with passphrases.

  • Removes the CWallet::fUseCrypto member variable and CWallet::SetCrypted() method and changes CWallet::IsCrypted() to return HasEncryptionKeys() instead of fUseCrypto. The meaning of fUseCrypto was not describable in any simple terms: It was false by default and true if CWallet::Lock or CWallet::Unlock(CKeyingMaterial) or LegacyScriptPubKeyMan::AddCryptedKeyInner had previously been called AND if FillableSigningProvider::mapKeys was empty. I wasn't able to determine if or why there aren't bugs in the current wallet code caused by IsCrypted() returning false when it would more correctly return true, but the new implementation of IsCrypted() in this PR seems vastly simpler and safer.

  • Replaces if (!IsCrypted()) checks with if (!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 with if (!mapKeys.empty() checks. These checks are equivalent because SetCrypted() always returned true unless FillableSigningProvider::mapKeys had (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::vMasterKey accesses with calls to a new method CWallet::GetEncryptionKey() returning the same value. This is just to avoid the key manager accessing a wallet variable. CWallet::vMasterKey holds a master decryption key decrypted with the passphrase and is only set while the wallet is unlocked.

  • Renames LegacyScriptPubKeyMan::EncryptKeys() to LegacyScriptPubKeyMan::Encrypt() for fun and entertainment.

  • Passes a new WalletBatch* batch argument to LegacyScriptPubKeyMan::Encrypt() which is used to set the LegacyScriptPubKeyMan::encrypted_batch member while the function is running. This is equivalent to the previous code because the previous caller, CWallet::EncryptWallet(), was setting the LegacyScriptPubKeyMan::encrypted_batch member itself, but is no longer doing that because as a wallet method, it shouldn't have access to key manager internals.

  • Tweaks LegacyScriptPubKeyMan::Encrypt to clear FillableSigningProvider::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 make AddCryptedKeyInner() work now that SetCrypted() is gone and it is checking for FillableSigningProvider::mapKeys to be empty.

Copy link
Contributor

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.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed to asserts.

Copy link
Contributor

@ryanofsky ryanofsky Nov 7, 2019

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.

Copy link
Contributor

@ryanofsky ryanofsky left a 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

@ryanofsky
Copy link
Contributor

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.

@ryanofsky
Copy link
Contributor

@instagibbs and @meshcollider, you may be interested to review this. This is code you previously acked when it was part of #16341

@instagibbs
Copy link
Member

I think splitting up into separate commits would be beneficial for the current and future readers.

Copy link
Contributor

@ryanofsky ryanofsky left a 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

@achow101
Copy link
Member Author

achow101 commented Dec 5, 2019

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)

@achow101 achow101 force-pushed the wallet-box-pr-4 branch 3 times, most recently from 3a55e9e to 5c8f0f8 Compare December 6, 2019 00:30
@instagibbs
Copy link
Member

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()
@laanwj
Copy link
Member

laanwj commented Dec 12, 2019

ACK 7cecf10

laanwj added a commit that referenced this pull request Dec 12, 2019
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
@laanwj laanwj merged commit 7cecf10 into bitcoin:master Dec 12, 2019
sidhujag pushed a commit to syscoin/syscoin that referenced this pull request Dec 12, 2019
…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
jasonbcox pushed a commit to Bitcoin-ABC/bitcoin-abc that referenced this pull request Oct 1, 2020
…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
sidhujag pushed a commit to syscoin-core/syscoin that referenced this pull request Nov 10, 2020
…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
@bitcoin bitcoin locked as resolved and limited conversation to collaborators Dec 16, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants