Skip to content

Upstream PR 1058#1

Closed
DarkWindman wants to merge 21 commits intotemp-merge-1515from
temp-merge-1058
Closed

Upstream PR 1058#1
DarkWindman wants to merge 21 commits intotemp-merge-1515from
temp-merge-1058

Conversation

@DarkWindman
Copy link
Copy Markdown
Collaborator

Merge bitcoin-core/secp256k1#1058: Signed-digit multi-comb ecmult_gen algorithm

Stacked on top of branch temp-merge-1515.
This PR can be recreated with ./contrib/sync-upstream-stacked.sh temp-merge-1515 select da515074e3ebc8abc85a4fff3a31d7694ecf897b.
Tip: Use git show --remerge-diff to show the changes manually added to the merge commit.' --base 'temp-merge-1515' --web

real-or-random and others added 20 commits April 15, 2024 13:18
Instead of having the starting point of the ecmult_gen computation be
offset, do it with the final point. This enables reasoning over the
set of points reachable in intermediary computations, which can be
leveraged by potential future optimization.

Because the final point is in affine coordinates, its projective
blinding is no longer possible. It will be reintroduced again in
a different way, in a later commit.

Also introduce some more comments and more descriptive names.
The old code overwrote the input at the start of the function,
making a call like secp256k1_scalar_inverse(&x,&x) always fail.
This introduces the signed-digit multi-comb multiplication algorithm
for constant-time G multiplications (ecmult_gen). It is based on
section 3.3 of "Fast and compact elliptic-curve cryptography" by
Mike Hamburg (see https://eprint.iacr.org/2012/309).

Original implementation by Peter Dettman, with changes by Pieter Wuille
to use scalars for recoding, and additional comments.
It is unnecessary to recompute this term needed by the SDMC algorithm
for every multiplication; move it into the context scalar_offset value
instead.
The old code would trigger UB when count=32.
The existing code needs to deal with the edge case that bit_pos >= 256,
which would lead to an out-of-bounds read from secp256k1_scalar.

Instead, recode the scalar into an array of uint32_t with enough zero
padding at the end to alleviate the issue. This also simplifies the
code, and is necessary for a security improvement in a follow-up
commit.

Original code by Peter Dettman, with modifications by Pieter Wuille.
Co-authored-by: Tim Ruffing <crypto@timruffing.de>
… algorithm

4c341f8 Add changelog entry for SDMC (Pieter Wuille)
a043940 Permit COMB_BITS < 256 for exhaustive tests (Pieter Wuille)
39b2f2a Add test case for ecmult_gen recoded = {-1,0,1} (Pieter Wuille)
644e86d Reintroduce projective blinding (Pieter Wuille)
07810d9 Reduce side channels from single-bit reads (Peter Dettman)
a0d32b5 Optimization: use Nx32 representation for recoded bits (Peter Dettman)
e03dcc4 Make secp256k1_scalar_get_bits support 32-bit reads (Pieter Wuille)
5005abe Rename scalar_get_bits -> scalar_get_bits_limb32; return uint32_t (Pieter Wuille)
6247f48 Optimization: avoid unnecessary doublings in precomputation (Peter Dettman)
15d0cca Optimization: first table lookup needs no point addition (Pieter Wuille)
7a33db3 Optimization: move (2^COMB_BITS-1)/2 term into ctx->scalar_offset (Pieter Wuille)
ed2a056 Provide 3 configurations accessible through ./configure (Pieter Wuille)
5f7be9f Always generate tables for current (blocks,teeth) config (Pieter Wuille)
fde1dfc Signed-digit multi-comb ecmult_gen algorithm (Peter Dettman)
486518b Make exhaustive tests's scalar_inverse(&x,&x) work (Pieter Wuille)
ab45c3e Initial gej blinding -> final ge blinding (Pieter Wuille)
aa00a6b Introduce CEIL_DIV macro and use it (Tim Ruffing)

Pull request description:

ACKs for top commit:
  real-or-random:
    reACK 4c341f8
  jonasnick:
    ACK 4c341f8
  stratospher:
    ACK 4c341f8. Did [these benchmarks](bitcoin-core/secp256k1#1058 (comment)) and saw a 12.4% on gcc 13.2.0 and 11.5% on clang 15.0.0. Also summarised how the precomputed table generation works [here](https://github.com/stratospher/blogosphere/blob/main/sdmc.md) for future me :)

Tree-SHA512: 9a11138e4fb98b98e85c82cd46ed78b29fbe63d6efe61654ef519a64b1e175d63395a8a931c1646f9df8c7daacd796d5fe2384899d5a13a2c7ed2ded696ceed5
@mllwchrry mllwchrry closed this Jan 29, 2026
@mllwchrry mllwchrry deleted the temp-merge-1058 branch January 29, 2026 14:14
mllwchrry pushed a commit that referenced this pull request Mar 3, 2026
…to improve parallelism

8354618 cmake: Set `LABELS` property for tests (Hennadii Stepanov)
29f26ec cmake: Integrate DiscoverTests and normalize test names (Hennadii Stepanov)
f95b263 cmake: Add DiscoverTests module (Hennadii Stepanov)
4ac6511 cmake, refactor: Deduplicate test-related code (Hennadii Stepanov)

Pull request description:

  This PR implements the idea suggested in bitcoin-core/secp256k1#1734 (review) and is based on the work from bitcoin/bitcoin#33483.

  Here is an example of the `ctest` output:
  ```
  $ ctest --test-dir build -j $(nproc)
  Test project /home/hebasto/dev/secp256k1/secp256k1/build
          Start   1: secp256k1.noverify_tests.selftest_tests
          Start   2: secp256k1.noverify_tests.all_proper_context_tests
          Start   3: secp256k1.noverify_tests.all_static_context_tests
          Start   4: secp256k1.noverify_tests.deprecated_context_flags_test
  <snip>
  193/196 Test  BlockstreamResearch#31: secp256k1.noverify_tests.ecmult_constants .........................   Passed    5.32 sec
  194/196 Test BlockstreamResearch#184: secp256k1.tests.ellswift_xdh_correctness_tests ....................   Passed    5.62 sec
  195/196 Test BlockstreamResearch#191: secp256k1.exhaustive_tests ........................................   Passed    6.97 sec
  196/196 Test BlockstreamResearch#126: secp256k1.tests.ecmult_constants ..................................   Passed    9.60 sec

  100% tests passed, 0 tests failed out of 196

  Label Time Summary:
  secp256k1_example           =   0.02 sec*proc (5 tests)
  secp256k1_exhaustive        =   6.97 sec*proc (1 test)
  secp256k1_noverify_tests    =  23.77 sec*proc (95 tests)
  secp256k1_tests             =  43.67 sec*proc (95 tests)

  Total Test time (real) =  10.21 sec
  ```

  For comparison, here is the output for the master branch on the same machine:
  ```
  $ ctest --test-dir build -j $(nproc)
  Test project /home/hebasto/dev/secp256k1/secp256k1/build
      Start 1: secp256k1_noverify_tests
      Start 2: secp256k1_tests
      Start 3: secp256k1_exhaustive_tests
      Start 4: secp256k1_ecdsa_example
      Start 5: secp256k1_ecdh_example
      Start 6: secp256k1_schnorr_example
      Start 7: secp256k1_ellswift_example
      Start 8: secp256k1_musig_example
  1/8 Test #4: secp256k1_ecdsa_example ..........   Passed    0.00 sec
  2/8 Test #5: secp256k1_ecdh_example ...........   Passed    0.00 sec
  3/8 Test #6: secp256k1_schnorr_example ........   Passed    0.00 sec
  4/8 Test #7: secp256k1_ellswift_example .......   Passed    0.00 sec
  5/8 Test #8: secp256k1_musig_example ..........   Passed    0.00 sec
  6/8 Test #3: secp256k1_exhaustive_tests .......   Passed    6.26 sec
  7/8 Test #1: secp256k1_noverify_tests .........   Passed   14.31 sec
  8/8 Test #2: secp256k1_tests ..................   Passed   31.65 sec

  100% tests passed, 0 tests failed out of 8

  Total Test time (real) =  31.65 sec
  ```

  ---

  **New Feature:** As the number of tests has grown, the _labels_ have been introduced to simplify test management. Now, one can run:
  ```
  $ ctest --test-dir build -j $(nproc) -L example
  Test project /home/hebasto/dev/secp256k1/secp256k1/build
      Start 192: secp256k1.example.ecdsa
      Start 193: secp256k1.example.ecdh
      Start 194: secp256k1.example.schnorr
      Start 195: secp256k1.example.ellswift
      Start 196: secp256k1.example.musig
  1/5 Test BlockstreamResearch#192: secp256k1.example.ecdsa ..........   Passed    0.00 sec
  2/5 Test BlockstreamResearch#193: secp256k1.example.ecdh ...........   Passed    0.00 sec
  3/5 Test BlockstreamResearch#194: secp256k1.example.schnorr ........   Passed    0.00 sec
  4/5 Test BlockstreamResearch#195: secp256k1.example.ellswift .......   Passed    0.00 sec
  5/5 Test BlockstreamResearch#196: secp256k1.example.musig ..........   Passed    0.00 sec

  100% tests passed, 0 tests failed out of 5

  Label Time Summary:
  secp256k1_example    =   0.01 sec*proc (5 tests)

  Total Test time (real) =   0.01 sec
  ```
  or
  ```
  $ ctest --test-dir build -j $(nproc) -LE tests
  Test project /home/hebasto/dev/secp256k1/secp256k1/build
      Start 192: secp256k1.example.ecdsa
      Start 193: secp256k1.example.ecdh
      Start 194: secp256k1.example.schnorr
      Start 195: secp256k1.example.ellswift
      Start 196: secp256k1.example.musig
      Start 191: secp256k1.exhaustive_tests
  1/6 Test BlockstreamResearch#192: secp256k1.example.ecdsa ..........   Passed    0.00 sec
  2/6 Test BlockstreamResearch#193: secp256k1.example.ecdh ...........   Passed    0.00 sec
  3/6 Test BlockstreamResearch#194: secp256k1.example.schnorr ........   Passed    0.00 sec
  4/6 Test BlockstreamResearch#195: secp256k1.example.ellswift .......   Passed    0.00 sec
  5/6 Test BlockstreamResearch#196: secp256k1.example.musig ..........   Passed    0.00 sec
  6/6 Test BlockstreamResearch#191: secp256k1.exhaustive_tests .......   Passed    6.19 sec

  100% tests passed, 0 tests failed out of 6

  Label Time Summary:
  secp256k1_example       =   0.01 sec*proc (5 tests)
  secp256k1_exhaustive    =   6.19 sec*proc (1 test)

  Total Test time (real) =   6.20 sec
  ```

ACKs for top commit:
  purpleKarrot:
    ACK 8354618
  furszy:
    Tested ACK 8354618

Tree-SHA512: 8c506ab08491aba4836b3058a8a09c929c6dd097c11e4e6f4deb20cf602285e73c3fd8a2c2040f7e92a058c7f8fc09752fa9de2ce80f7673adbdd505237ed262
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants