-
Notifications
You must be signed in to change notification settings - Fork 38.8k
crypto: Use secure_allocator for AES256_ctx
#31774
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
base: master
Are you sure you want to change the base?
crypto: Use secure_allocator for AES256_ctx
#31774
Conversation
|
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers. Code Coverage & BenchmarksFor details see: https://corecheck.dev/bitcoin/bitcoin/pulls/31774. ReviewsSee the guideline for information on the review process.
If your review is incorrectly listed, please copy-paste 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. |
ab21aa1 to
68f1370
Compare
|
🚧 At least one of the CI tasks failed. HintsTry to run the tests locally, according to the documentation. However, a CI failure may still
Leave a comment here, if you need help tracking down a confusing failure. |
|
Concept ACK on clearing out the ctx/iv members I'm wondering if a minimum-diff fix which simply replaces the bitcoin/src/wallet/crypter.cpp Lines 85 to 91 in 94ca99a
bitcoin/src/wallet/crypter.cpp Lines 102 to 108 in 94ca99a
The reasoning might be a bit loose as there might be other uses of these classes in the future where the situation is different. Curious about other opinions. |
|
I think the approach here might have an unacceptable performance impact, as it is allocating and deallocating a secure area for every individual key being encrypted/decrypted. It may be better to:
|
046c5e4 to
a3be398
Compare
|
🚧 At least one of the CI tasks failed. HintsTry to run the tests locally, according to the documentation. However, a CI failure may still
Leave a comment here, if you need help tracking down a confusing failure. |
fda2ec1 to
15d8500
Compare
It seems right to me that this structure is always short lived, but I also felt in #31166
I added a benchmark for
But if the short-lived nature of the key material makes just using |
This may still be true, though FWIW the idea behind the But agree that for short-lived key material, it's less important, there is little to no chance of it ending up in swap anyway. |
15d8500 to
95d266f
Compare
|
🚧 At least one of the CI tasks failed. HintsTry to run the tests locally, according to the documentation. However, a CI failure may still
Leave a comment here, if you need help tracking down a confusing failure. |
95d266f to
591764f
Compare
|
Rebased to address merge conflict, dropped legacy wallet encryption benchmark, and reduced number of keys since the benchmark was taking an unreasonably long time. |
sipa
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.
utACK 591764f6170d6f74d7eebb5fec1cbf5b912098a4, just coding style nits
591764f to
3d7d2c3
Compare
|
🚧 At least one of the CI tasks failed. HintsTry to run the tests locally, according to the documentation. However, a CI failure may still
Leave a comment here, if you need help tracking down a confusing failure. |
90528e7 to
8bdcd12
Compare
|
Refactored to address @furszy feedback to avoid adding a test-only parameter to I don't like the idea of using a |
sipa
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.
utACK 8bdcd12d3bcbeaf922fa10dc2a261848e3900cfd
theStack
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 8bdcd12d3bcbeaf922fa10dc2a261848e3900cfd
furszy
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.
ACK 8bdcd12d3bcbeaf922fa10dc2a261848e3900cfd
No need to tackle the nano nits I left.
| CCrypter crypter; | ||
|
|
||
| crypter.SetKeyFromPassphrase(wallet_passphrase, master_key.vchSalt, master_key.nDeriveIterations, master_key.nDerivationMethod); | ||
| master_key.nDeriveIterations = static_cast<unsigned int>(master_key.nDeriveIterations * target / (SteadyClock::now() - start)); | ||
| // Get the weighted average of iterations we can do in 100ms over 2 runs. |
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 7176b26:
Pedantic ultra-nano nit:
We finish comments with a dot only if they span more than one line.
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.
Really? I couldn't find this in doc/ and I find a lot of counter-examples when doing:
git grep -n -C 2 "// .*\." '*.cpp' '*.h'
| master_key.nDeriveIterations = (master_key.nDeriveIterations + static_cast<unsigned int>(master_key.nDeriveIterations * target / (SteadyClock::now() - start))) / 2; | ||
| // target_iterations : elapsed_iterations :: target_time : elapsed_time | ||
| unsigned int target_iterations = master_key.nDeriveIterations * target_time / elapsed_time; | ||
| // Get the weighted average with previous runs. |
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 7176b26:
nano nit: you are already mentioning this above the for-loop line.
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.
Fair enough, I think this is adding a little more information than the comment above the for loop, but I'll remove when/if retouching.
| start = SteadyClock::now(); | ||
| crypter.SetKeyFromPassphrase(wallet_passphrase, master_key.vchSalt, master_key.nDeriveIterations, master_key.nDerivationMethod); | ||
| master_key.nDeriveIterations = (master_key.nDeriveIterations + static_cast<unsigned int>(master_key.nDeriveIterations * target / (SteadyClock::now() - start))) / 2; | ||
| // target_iterations : elapsed_iterations :: target_time : elapsed_time |
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 7176b26cde7cbaffdd92af9c25f85f8e5233e78a "refactor: Generalize derivation target calculation"
This comment does not seem helpful, I'm not sure what information it is trying to convey.
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.
https://en.wikipedia.org/wiki/Ratio#Notation_and_terminology (archive link)
This comment explains why the assignment below is correct. This can be read as "target_iterations is to elapsed_iterations as target_time is to elapsed_time", this is mostly equivalent to:
| // Skip actually encrypting wallet on the overhead measuring run, so we | ||
| // can subtract the overhead from the full benchmark results to get | ||
| // encryption performance. | ||
| if (!measure_overhead) { |
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 26442fec572da263765c576c080186c84cee1615 "bench: Add wallet encryption benchmark"
We've generally not had "measure overhead" in the benchmarks. If this is because encryption is actually a very small amount of time, I would suggest generating significantly more keys that need to actually be encrypted.
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.
Unfortunately, even after generating more keys, there is still significant overhead, and this overhead is not fixed, it scales with the number of keys...
Even though total - overhead isn't something we've measured in any existing benchmarks, it's probably a good idea for some benchmarks. It won't make a difference for catching extreme regressions, but for smaller performance regressions / improvements, the setup overhead in each run adds noise that makes changes harder to measure. Ideally there would be some way to achieve this with nanobench, but for now this hasn't been implemented and the maintainer of nanobench has suggested this approach: martinus/nanobench#86 (comment)
|
IIRC we're waiting on @davidgumberg to address the comments from @achow101 's reviews #31774 (comment) here? |
07497f0 to
b8048b1
Compare
|
🚧 At least one of the CI tasks failed. HintsTry to run the tests locally, according to the documentation. However, a CI failure may still
Leave a comment here, if you need help tracking down a confusing failure. |
Adds a special case where if the elapsed time during measurement of DKF performance is 0, the default derive iterations are used so that behavior is stable for testing and benchmarks.
Allows `crypto` functions and classes to use `secure_allocator`.
b8048b1 to
e54690a
Compare
Rebased against master to fix a silent merge conflict with
To set up, I cleaned my build dir and used pyperf.
```bash
rm -rf ./build
pip install pyperf && sudo pyperf system tune
```
|
| ns/key | key/s | err% | ins/key | cyc/key | IPC | bra/key | miss% | total | benchmark |
|---|---|---|---|---|---|---|---|---|---|
| 293,553.20 | 3,406.54 | 0.1% | 6,848,134.51 | 1,258,326.47 | 5.442 | 151,429.70 | 0.9% | 12.91 | WalletEncryptDescriptors |
| 104,682.68 | 9,552.68 | 0.1% | 2,329,994.28 | 448,813.08 | 5.191 | 74,084.65 | 0.9% | 13.02 | WalletEncryptDescriptorsBenchOverhead |
Branch (e54690a)
git checkout e54690a
cmake -B build -DBUILD_BENCH=ON && cmake --build build -j $(nproc)
./build/bin/bench_bitcoin --filter="WalletEncrypt.*" -min-time=12000| ns/key | key/s | err% | ins/key | cyc/key | IPC | bra/key | miss% | total | benchmark |
|---|---|---|---|---|---|---|---|---|---|
| 294,794.46 | 3,392.19 | 0.3% | 6,855,246.26 | 1,264,321.32 | 5.422 | 153,125.52 | 0.9% | 12.97 | WalletEncryptDescriptors |
| 104,329.09 | 9,585.05 | 0.1% | 2,330,778.18 | 447,414.99 | 5.209 | 74,203.06 | 0.8% | 12.94 | WalletEncryptDescriptorsBenchOverhead |
Fixes #31744
Reuse
secure_allocatorforAES256_ctxin the aes 256 encrypters and decrypters and theivofAES256CBCencrypters and decrypters. These classes are relevant toCCrypter, used for encrypting wallets, and my understanding is that if an attacker knows some or all of the contents of these data structures (AES256_ctx&iv) they might be able to decrypt a user's wallet.Presently the
secure_allocatortries to protect sensitive data withmlock()on POSIX systems andVirtualLock()on Windows to prevent memory being paged to disk, and by zero'ing out memory contents on deallocation withmemory_cleanse()which is similar toOPENSSL_cleanse()by scaring compilers away from optimizingmemsetcalls on non-Windows systems, and usingSecureZeroMemory()on Windows.