Skip to content

Make SHA256 compression runtime pluggable#1777

Merged
real-or-random merged 3 commits intobitcoin-core:masterfrom
furszy:2025_pluggable_sha256_transform
Mar 4, 2026
Merged

Make SHA256 compression runtime pluggable#1777
real-or-random merged 3 commits intobitcoin-core:masterfrom
furszy:2025_pluggable_sha256_transform

Conversation

@furszy
Copy link
Member

@furszy furszy commented Nov 27, 2025

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:

secp256k1_context_set_sha256_transform_callback(ctx, fn_transform)

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

@furszy furszy force-pushed the 2025_pluggable_sha256_transform branch from 16cd02b to 7fefa9c Compare November 28, 2025 15:54
@fjahr
Copy link
Contributor

fjahr commented Dec 1, 2025

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?

@furszy
Copy link
Member Author

furszy commented Dec 1, 2025

Thanks for the feedback fjahr!

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.

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 configure.ac / CMakeLists.txt. Happy to create a file for it.

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?

It’s not that we’re letting people plug in whatever they want. Both introduced features have guardrails:

  1. Compile-time: we run all current tests against the provided implementation, plus a runtime self-test.
  2. Runtime: besides the runtime self-test, have introduced an "equivalence check" that hashes known inputs and compares them against the library’s internal implementation before accepting the external function.

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.

Copy link
Contributor

@real-or-random real-or-random left a 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],
Copy link
Contributor

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.

Copy link
Member

@hebasto hebasto left a comment

Choose a reason for hiding this comment

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

Concept ACK.

@furszy furszy force-pushed the 2025_pluggable_sha256_transform branch from 7fefa9c to 3683e07 Compare December 16, 2025 21:45
Copy link
Member Author

@furszy furszy left a 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.

@sipa
Copy link
Contributor

sipa commented Dec 17, 2025

A few high-level comments:

  • I don't think we should do link-time plugging in the same PR as runtime plugging, and when we do, I don't think it should be done through the "module" abstractions. We use modules for exporting functionality from the library, not for offering replacement of internal dependencies (I find it strange to have base functionality depend on a module!).
  • "round" is a term of art in cipher design, and SHA-256 has exactly 64 rounds. Using the same term with another meaning is confusing.
  • I would try hard to avoid storing the transformation function pointer inside the secp256k1_sha256 struct. Reviewing many lines that just add an extra parameter being pass through is not hard.

Copy link
Member Author

@furszy furszy left a 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.

@sipa
Copy link
Contributor

sipa commented Dec 18, 2025

@furszy From a quick glance, the passing of the function everywhere is fine I think.

I'd suggest passing a pointer to secp256k1_hash_context to the sha256 functions rather than the function pointer itself. That makes it easier to (if ever) add additional fields to that context.

@furszy furszy force-pushed the 2025_pluggable_sha256_transform branch from 3683e07 to 2413e92 Compare December 19, 2025 17:52
Copy link
Member Author

@furszy furszy left a 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).

@real-or-random

This comment was marked as outdated.

@furszy furszy force-pushed the 2025_pluggable_sha256_transform branch from 2413e92 to 041a621 Compare December 19, 2025 20:08
@real-or-random

This comment was marked as outdated.

Copy link
Contributor

@real-or-random real-or-random left a comment

Choose a reason for hiding this comment

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

Approach ACK

@furszy furszy force-pushed the 2025_pluggable_sha256_transform branch from 041a621 to fd7bf49 Compare January 13, 2026 20:01
@furszy
Copy link
Member Author

furszy commented Jan 13, 2026

Updated per feedback. Thanks @real-or-random!

@furszy furszy force-pushed the 2025_pluggable_sha256_transform branch from fd7bf49 to 8b45a8f Compare January 14, 2026 17:19
Copy link
Contributor

@real-or-random real-or-random left a 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!)

@furszy furszy deleted the 2025_pluggable_sha256_transform branch March 4, 2026 12:44
Copy link
Contributor

@sipa sipa left a comment

Choose a reason for hiding this comment

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

Posthumous reACK 4d92a08

real-or-random added a commit to BlockstreamResearch/secp256k1-zkp that referenced this pull request Mar 5, 2026
656c7cc sync-upstream: Clarify that we merge a *single* upstream ref (Tim Ruffing)
349a94b sync-upstream: Remove "select" mode and simplify (Tim Ruffing)

Pull request description:

  Please see individual commit messages for details.

  ---

  Here's an example session that shows why `select` doesn't make sense:

  ```
  ### Let's find some PRs to sync (master is at 5a67b63)

  ❯ ./contrib/sync-upstream.sh -b master range
  Merging b9cb1cb 1aafe15 . Continue with y
  n

  ### Let's look at them in the order they have been merged

  ❯ git show b9cb1cb
  commit b9cb1cb
  Merge: c0a2aba 921b971
  Author: merge-script <me@real-or-random.org>
  Date:   Tue Mar 3 15:31:46 2026 +0100

      Merge bitcoin-core/secp256k1#1824: util: introduce and use `ARRAY_SIZE` macro

  [...]

  ❯ git --no-pager show 1aafe15

  commit 1aafe15 (upstream/master, upstream/HEAD)
  Merge: b9cb1cb 4d92a08
  Author: merge-script <me@real-or-random.org>
  Date:   Wed Mar 4 08:43:07 2026 +0100

      Merge bitcoin-core/secp256k1#1777: Make SHA256 compression runtime pluggable

  [...]

  ### Let's assume I want to cherry-pick 1aafe15 but not sync b9cb1bcf

  ❯ ./contrib/sync-upstream.sh -b master select 1aafe15
  -----------------------------------
  Upstream PRs 1777
  -----------------------------------

  [bitcoin-core/secp256k1#1777]: Make SHA256 compression runtime pluggable

  This PR can be recreated with `./contrib/sync-upstream.sh -b master select 1aafe15`.

  [...]

  ### Let's check if it's really only PR #1777

  ❯ git diff | grep ARRAY_SIZE
  + #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))

  ### Ah damn, we also got the other PR #1824...
  ```

ACKs for top commit:
  mllwchrry:
    ACK 656c7cc
  jonasnick:
    ACK 656c7cc

Tree-SHA512: 27cdfe7c6decce840ef4f2d12fa0a5fd829b35ac24403272c4b34c0a3b0da2303cee36bcb419d0608c6b1297fd5bf8a6cd6d41672850ac092814c140638f9d83
fanquake added a commit to fanquake/bitcoin that referenced this pull request Mar 10, 2026
1aafe15139 Merge bitcoin-core/secp256k1#1777: Make SHA256 compression runtime pluggable
b9cb1cbfd7 Merge bitcoin-core/secp256k1#1824: util: introduce and use `ARRAY_SIZE` macro
4d92a083bc sha256: speed up writes using multi-block compression
0753f8b909 Add API to override SHA256 compression at runtime
fdb6a91a5e Introduce hash context to support pluggable SHA256 compression
c0a2aba088 Merge bitcoin-core/secp256k1#1811: bench: Update help functions in bench and bench_internal
10f546a2c0 Merge bitcoin-core/secp256k1#1832: testrand: Remove testrand_finish
8d0eda07e9 testrand: Remove testrand_finish
95e6815843 Merge bitcoin-core/secp256k1#1825: hash: remove redundant `secp256k1_sha256_initialize` in tagged hash midstate functions
f48b1bfa5d hash: add midstate initializer and use it for tagged hashes
3019186a6d Merge bitcoin-core/secp256k1#1829: ci: Fix leftover use of old ECMULTGENPRECISION
79e9f25237 ci: Fix leftover use of old ECMULTGENPRECISION
dfe042feb2 Merge bitcoin-core/secp256k1#1828: Revert "ci, docker: Fix LLVM repository signature failure"
76e92cfeea Revert "ci, docker: Fix LLVM repository signature failure"
ac561601b8 Merge bitcoin-core/secp256k1#1760: cmake: Add dynamic test discovery to improve parallelism
c7a7f732bd Merge bitcoin-core/secp256k1#1821: ellswift: fix overflow flag handling in secp256k1_ellswift_xdh
921b9711ea util: introduce and use `ARRAY_SIZE` macro
b99a94c382 Add tests for bad scalar inputs in ellswift XDH
307b49f1b9 ellswift: fix overflow flag handling in secp256k1_ellswift_xdh
322d0a4358 Merge bitcoin-core/secp256k1#1823: ci: Load Docker image by ID from builder step
ed02466d3f ci: Load Docker image by ID from builder step
c49c9be504 bench: Update help functions in bench and bench_internal
1d146ac3ed Merge bitcoin-core/secp256k1#1819: tests: Improve secp256k1_scalar_check_overflow tests (Issue bitcoin#1812)
f47bbc07f0 test: add unit tests for secp256k1_scalar_check_overflow
d071aa56d5 Merge bitcoin-core/secp256k1#1815: refactor: remove unnecessary `malloc` result casts
99ab4a105e Merge bitcoin-core/secp256k1#1817: ci: Disable Docker build summary generation
c5da3bde9c Merge bitcoin-core/secp256k1#1818: ci: Enforce base-10 evaluation
97de5120cf Merge bitcoin-core/secp256k1#1804: test: show both CMake and Autotools usage for ctime_tests
4fb7ccf5d4 ci: Enforce base-10 evaluation
3ae72e7867 ci: Disable Docker build summary generation
97b3c47849 refactor: remove unnecessary `malloc` result casts
1bc74a22f8 test: show both Autotools and CMake usage for ctime_tests
8354618e02 cmake: Set `LABELS` property for tests
29f26ec3cf cmake: Integrate DiscoverTests and normalize test names
f95b263f23 cmake: Add DiscoverTests module
4ac651144b cmake, refactor: Deduplicate test-related code

git-subtree-dir: src/secp256k1
git-subtree-split: 1aafe15139976b0142d791aaf4963de3fc1ff736
sedited added a commit to bitcoin/bitcoin that referenced this pull request Mar 11, 2026
…ath spends

d339884 bench: add script verification benchmark for P2TR key path spends (Sebastian Falbesoner)
dd93362 bench: simplify script verification benchmark, generalize signing (Sebastian Falbesoner)

Pull request description:

  We currently benchmark Schnorr signature verification only in the context of block validation ([`ConectBlock*`](https://github.com/bitcoin/bitcoin/blob/8bb77f348ef390b7f7c7fb6b57fdc7e86ddb4ce7/src/bench/connectblock.cpp#L107) benchmarks), but not individually for single inputs [1]. This PR adds a script verification benchmark for P2TR key path spends accordingly, by generalizing the already existing one for P2WPKH spends.

  This should make it easier to quantify potential performance improvements like e.g. bitcoin-core/secp256k1#1777, which allows to plug in our HW-optimized SHA256 functions to be used in libsecp256k1 (see the linked example commit furszy@f68bef0). IIRC from last CoreDev, the main speedup from this is expected for ECDSA signing though (as this involves quite a lot of hashing), but it still makes sense to have verification benchmarks available for both signature types as well.

  (An alternative way could be to add benchmarks for the signing/verifying member functions `CKey::Sign{,Schnorr}`, `CPubKey::Verify` and `XOnlyPubKey::VerifySchnorr` directly, if we prefer that.)

  [1] this claim can be practically verified by putting an `assert(false);` into `XOnlyPubKey::VerifySchnorr`: the three benchmarks crashing are `ConnectBlockAllSchnorr`, `ConnectBlockMixedEcdsaSchnorr` and `SignTransactionSchnorr` (as signing includes verification)

ACKs for top commit:
  furszy:
    ACK d339884
  sedited:
    Re-ACK d339884
  w0xlt:
    ACK d339884

Tree-SHA512: efd20444984bdf1dab4d3d876fdbe2a3a838d7cebc0e31e26683009b81afe4dab8611e2c28c87e46fe8b7e305895c93e461b7934a5aaf293f72b19488b9cec60
fanquake added a commit to fanquake/bitcoin that referenced this pull request Mar 17, 2026
ffc25a2731 Merge bitcoin-core/secp256k1#1834: ecmult: Document and test ng=NULL in ecmult
3a403639dc eckey: Call ecmult with NULL instead of zero scalar
7e68c0c88b ecmult: Document and test ng=NULL in ecmult
1aafe15139 Merge bitcoin-core/secp256k1#1777: Make SHA256 compression runtime pluggable
b9cb1cbfd7 Merge bitcoin-core/secp256k1#1824: util: introduce and use `ARRAY_SIZE` macro
4d92a083bc sha256: speed up writes using multi-block compression
0753f8b909 Add API to override SHA256 compression at runtime
fdb6a91a5e Introduce hash context to support pluggable SHA256 compression
c0a2aba088 Merge bitcoin-core/secp256k1#1811: bench: Update help functions in bench and bench_internal
10f546a2c0 Merge bitcoin-core/secp256k1#1832: testrand: Remove testrand_finish
8d0eda07e9 testrand: Remove testrand_finish
95e6815843 Merge bitcoin-core/secp256k1#1825: hash: remove redundant `secp256k1_sha256_initialize` in tagged hash midstate functions
f48b1bfa5d hash: add midstate initializer and use it for tagged hashes
3019186a6d Merge bitcoin-core/secp256k1#1829: ci: Fix leftover use of old ECMULTGENPRECISION
79e9f25237 ci: Fix leftover use of old ECMULTGENPRECISION
dfe042feb2 Merge bitcoin-core/secp256k1#1828: Revert "ci, docker: Fix LLVM repository signature failure"
76e92cfeea Revert "ci, docker: Fix LLVM repository signature failure"
ac561601b8 Merge bitcoin-core/secp256k1#1760: cmake: Add dynamic test discovery to improve parallelism
c7a7f732bd Merge bitcoin-core/secp256k1#1821: ellswift: fix overflow flag handling in secp256k1_ellswift_xdh
921b9711ea util: introduce and use `ARRAY_SIZE` macro
b99a94c382 Add tests for bad scalar inputs in ellswift XDH
307b49f1b9 ellswift: fix overflow flag handling in secp256k1_ellswift_xdh
322d0a4358 Merge bitcoin-core/secp256k1#1823: ci: Load Docker image by ID from builder step
ed02466d3f ci: Load Docker image by ID from builder step
c49c9be504 bench: Update help functions in bench and bench_internal
1d146ac3ed Merge bitcoin-core/secp256k1#1819: tests: Improve secp256k1_scalar_check_overflow tests (Issue bitcoin#1812)
f47bbc07f0 test: add unit tests for secp256k1_scalar_check_overflow
d071aa56d5 Merge bitcoin-core/secp256k1#1815: refactor: remove unnecessary `malloc` result casts
99ab4a105e Merge bitcoin-core/secp256k1#1817: ci: Disable Docker build summary generation
c5da3bde9c Merge bitcoin-core/secp256k1#1818: ci: Enforce base-10 evaluation
97de5120cf Merge bitcoin-core/secp256k1#1804: test: show both CMake and Autotools usage for ctime_tests
4fb7ccf5d4 ci: Enforce base-10 evaluation
3ae72e7867 ci: Disable Docker build summary generation
97b3c47849 refactor: remove unnecessary `malloc` result casts
1bc74a22f8 test: show both Autotools and CMake usage for ctime_tests
8354618e02 cmake: Set `LABELS` property for tests
29f26ec3cf cmake: Integrate DiscoverTests and normalize test names
f95b263f23 cmake: Add DiscoverTests module
4ac651144b cmake, refactor: Deduplicate test-related code

git-subtree-dir: src/secp256k1
git-subtree-split: ffc25a2731fd277e056c6f62aa94eb0fb78e031d
fanquake added a commit to fanquake/bitcoin that referenced this pull request Mar 21, 2026
ffc25a2731 Merge bitcoin-core/secp256k1#1834: ecmult: Document and test ng=NULL in ecmult
3a403639dc eckey: Call ecmult with NULL instead of zero scalar
7e68c0c88b ecmult: Document and test ng=NULL in ecmult
1aafe15139 Merge bitcoin-core/secp256k1#1777: Make SHA256 compression runtime pluggable
b9cb1cbfd7 Merge bitcoin-core/secp256k1#1824: util: introduce and use `ARRAY_SIZE` macro
4d92a083bc sha256: speed up writes using multi-block compression
0753f8b909 Add API to override SHA256 compression at runtime
fdb6a91a5e Introduce hash context to support pluggable SHA256 compression
c0a2aba088 Merge bitcoin-core/secp256k1#1811: bench: Update help functions in bench and bench_internal
10f546a2c0 Merge bitcoin-core/secp256k1#1832: testrand: Remove testrand_finish
8d0eda07e9 testrand: Remove testrand_finish
95e6815843 Merge bitcoin-core/secp256k1#1825: hash: remove redundant `secp256k1_sha256_initialize` in tagged hash midstate functions
f48b1bfa5d hash: add midstate initializer and use it for tagged hashes
3019186a6d Merge bitcoin-core/secp256k1#1829: ci: Fix leftover use of old ECMULTGENPRECISION
79e9f25237 ci: Fix leftover use of old ECMULTGENPRECISION
dfe042feb2 Merge bitcoin-core/secp256k1#1828: Revert "ci, docker: Fix LLVM repository signature failure"
76e92cfeea Revert "ci, docker: Fix LLVM repository signature failure"
ac561601b8 Merge bitcoin-core/secp256k1#1760: cmake: Add dynamic test discovery to improve parallelism
c7a7f732bd Merge bitcoin-core/secp256k1#1821: ellswift: fix overflow flag handling in secp256k1_ellswift_xdh
921b9711ea util: introduce and use `ARRAY_SIZE` macro
b99a94c382 Add tests for bad scalar inputs in ellswift XDH
307b49f1b9 ellswift: fix overflow flag handling in secp256k1_ellswift_xdh
322d0a4358 Merge bitcoin-core/secp256k1#1823: ci: Load Docker image by ID from builder step
ed02466d3f ci: Load Docker image by ID from builder step
c49c9be504 bench: Update help functions in bench and bench_internal
1d146ac3ed Merge bitcoin-core/secp256k1#1819: tests: Improve secp256k1_scalar_check_overflow tests (Issue bitcoin#1812)
f47bbc07f0 test: add unit tests for secp256k1_scalar_check_overflow
d071aa56d5 Merge bitcoin-core/secp256k1#1815: refactor: remove unnecessary `malloc` result casts
99ab4a105e Merge bitcoin-core/secp256k1#1817: ci: Disable Docker build summary generation
c5da3bde9c Merge bitcoin-core/secp256k1#1818: ci: Enforce base-10 evaluation
97de5120cf Merge bitcoin-core/secp256k1#1804: test: show both CMake and Autotools usage for ctime_tests
4fb7ccf5d4 ci: Enforce base-10 evaluation
3ae72e7867 ci: Disable Docker build summary generation
97b3c47849 refactor: remove unnecessary `malloc` result casts
1bc74a22f8 test: show both Autotools and CMake usage for ctime_tests
8354618e02 cmake: Set `LABELS` property for tests
29f26ec3cf cmake: Integrate DiscoverTests and normalize test names
f95b263f23 cmake: Add DiscoverTests module
4ac651144b cmake, refactor: Deduplicate test-related code

git-subtree-dir: src/secp256k1
git-subtree-split: ffc25a2731fd277e056c6f62aa94eb0fb78e031d
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants