-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Make SHA256 compression runtime pluggable #1777
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?
Make SHA256 compression runtime pluggable #1777
Conversation
16cd02b to
7fefa9c
Compare
|
I guess this is more of a draft for initial review but I will spell it out anyway: It's currently missing some CI coverage for building with an external library as well as some docs. Curious what people think in terms of docs for something like this: Should there be extensive guidance in which context this is safe to use or would users be left on their own to try it out? Or are we assuming all users that go to this length are competent enough to judge if it's a good idea to bring their own sha256 or use the systems one? |
|
Thanks for the feedback fjahr!
Yeah, can create a CI job testing the compile-time pluggable compression function very easily. Thanks for reminding that. And about the missing docs; yeah. I didn't add it because we currently don't have much documentation outside
It’s not that we’re letting people plug in whatever they want. Both introduced features have guardrails:
So you can bring your own compression function, but it still has to prove it behaves exactly like ours bit-for-bit. Also, the target user here is someone who's actually written a hardware-optimized SHA256. It’s not like they stumbled into this by accident. |
real-or-random
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.
Concept ACK, great to see a PR for this!
configure.ac
Outdated
| ### Define config arguments | ||
| ### | ||
|
|
||
| AC_ARG_WITH([external-sha256], |
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.
nit: I think this one should also be an enable-style configure option instead of a with-style configure options; with is for compiling with external packages e.g., with-libxyz. And what the user provides here is not a package.
hebasto
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.
Concept ACK.
7fefa9c to
3683e07
Compare
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.
Update pushed, thanks for the deep review real_or_random!
I haven’t fully finished addressing all the comments yet, but I'll hopefully finish tomorrow. So many good topics.
|
A few high-level comments:
|
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.
Per feedback, this is how it would look if we pass the function everywhere furszy@8149fc1 vs how it looks when the function lives in the sha256 struct (an updated version of what we currently have here): furszy@eb57082
Let me know what you guys think.
|
@furszy From a quick glance, the passing of the function everywhere is fine I think. I'd suggest passing a pointer to |
3683e07 to
2413e92
Compare
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.
Updated per feedback. We now pass the hash context to all functions that need it, without storing any state inside the sha256 struct. The API arg "rounds" has also been renamed to "blocks" to prevent confusion with existing terms. Also, the commit introducing the compile-time feature has been removed for inclusion at a later stage (see #1777 (comment) for more details).
This comment was marked as outdated.
This comment was marked as outdated.
2413e92 to
041a621
Compare
This comment was marked as outdated.
This comment was marked as outdated.
real-or-random
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.
Approach ACK
041a621 to
fd7bf49
Compare
|
Updated per feedback. Thanks @real-or-random! |
fd7bf49 to
8b45a8f
Compare
real-or-random
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.
two nits on the last commit (I like that one!)
8b45a8f to
8251957
Compare
| test_ecdh_generator_basepoint_impl(ctx); | ||
| CHECK(sha256_ecdh_called); | ||
| secp256k1_context_destroy(ctx); | ||
| } |
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.
What is the point of this test? The baseline run makes sense, of course, but it already exists on master.
For the re-run, I'm not entirely sure what it is supposed to test.
- The first thing it tests is that
test_ecdh_generator_basepoint_implstill passes when an external SHA256 compression function is set. But that's not very surprising given that the "external" function simply delegates to the internal one. I don't think that this is a very meaningful test. - The other thing it tests is that the custom function was actually called. This is not entirely a meaningful test because the test function itself
test_ecdh_generator_basepoint_implhassecp256k1_sha256_writeandsecp256k1_finalizecalls. So we're testing the test function here and notsecp256k1_ecdh.
I suspect you had the second item in mind, but simply overlooked the internal sha256 calls?
Anyway, I believe a test that this PR should add is:
- If an external compression function is set, then
secp256k1_ecdh(..., NULL, ...)calls the external compression.
We could also add this: - If the external compression function is removed (set to NULL) afterwards, then
secp256k1_ecdh(..., NULL, ...)won't call the external compression again. Not sure if it adds much.
And I suspect that's enough in the case of ECDH. Another new thing is the branch on hashfp == NULL and we want to make sure to test both sides of it. The desired test 1 I described above exercises the "then" branch. And the "else" branch should already be covered by existing tests (and hashfp function won't have access to a ctx, so whether it uses an external or internal compression is not a meaningful question).
And I guess what I said here will apply more or less to the tests in the other modules as well.
edit: I still have to look at your most recent updates, but let me say that this one here is my last comment, so everything else is ACK and we're almost ready to go from my side.
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.
Done. Ended up skipping the SHA256 output sanity check during setup so that we can modify the compression function and assert that the results from the default compression versus the ctx-provided one are different.
I know I could have simplified DEFINE_SHA256_TRANSFORM_PROBE even more by not calling the internal function at all and always returning a random or fixed value, but I think behaving like a real one-way compression function is better for testing.
This is purely a mechanical change with no behavior change. It introduces a secp256k1_hash_ctx struct inside secp256k1_context and propagates it to all SHA256-related operations. This sets up the ability to provide a hardware-optimized SHA256 compression function at runtime in a follow-up commit.
8251957 to
0182e3d
Compare
|
Updated per feedback regarding the tests. Thanks, real-or-random! I also ended up reorganizing the PR slightly differently:
|
|
CI has some segfaults unfortunately. Here's the relevant snippet from valgrind: |
yep, was working on it. Nothing serious. See #1806. |
0182e3d to
406d905
Compare
This introduces `secp256k1_context_set_sha256_compression()`, which allows users to provide their own SHA256 block-compression function at runtime. This is useful in setups where the optimal implementation is detected dynamically, where rebuilding the library is not possible, or when the compression function is not written in bare C89. The callback is installed on the `secp256k1_context` and is then used by all operations that compute SHA256 hashes. As part of the setup, the library performs sanity checks to ensure that the supplied function is equivalent to the default transform. Passing NULL to the callback setter restores the built-in implementation.
Multiple 64-byte blocks can now be compressed directly from the input buffer, without copying them into the internal buffer.
406d905 to
4c0b80e
Compare
|
CI green again. Ready to go. |
Tackling the long-standing request #702.
Right now we ship our own SHA256 implementation, a standard baseline version that does not take advantage of any hardware-optimized instruction, and it cannot be accessed by the embedding application - it is for internal usage only.
This means embedding applications often have to implement or include a different version for their use cases, wasting space on constrained environments, and in performance-sensitive setups it forces them to use a slower path than what the platform provides. Many projects already rely on tuned SHA-NI / ARMv8 / or other hardware-optimized code, so always using the baseline implementation we ship within the library is not ideal.
These changes allow users to supply their own SHA256 compression function at runtime, while preserving the existing default behavior for everyone else. This is primarily intended for environments where the available SHA256 implementation is detected dynamically and recompiling the library with a different implementation is not feasible (equivalent build-time functionality will come in a follow-up PR).
It introduces a new API:
This function installs the optimized SHA256 compression into the
secp256k1_context, which is then used by all internal computations. Important: The provided function is verified to be output-equivalent to the original one.As a quick example, using this functionality in Bitcoin-Core will be very straightforward: furszy/bitcoin-core@f68bef0