Skip to content

crypto: reduce EC key processing overhead - #65908

Open
panva wants to merge 6 commits into
nodejs:mainfrom
panva:crypto-ec-raw-private-deferred
Open

crypto: reduce EC key processing overhead#65908
panva wants to merge 6 commits into
nodejs:mainfrom
panva:crypto-ec-raw-private-deferred

Conversation

@panva

@panva panva commented Sep 8, 2026

Copy link
Copy Markdown
Member

Reduces unnecessary work when importing and using EC keys.

Streamlines private JWK import while preserving validation, and reads curve metadata, JWK coordinates, raw key material, and signature component sizes directly instead of reconstructing EC keys. Includes focused tests and benchmarks.

Raw private-key imports defer public-point derivation until needed, allowing signing and ECDH to avoid that cost. Operations needing the public point derive it once and cache the completed key. Dropped because this optimization does not work on OpenSSL >= 4 openssl/openssl#29054 makes provider imports derive the missing public point eagerly. rip 0ddf3c9

Avoid creating temporary EVP objects and repeating key validation while
retaining the private scalar range and public/private consistency
checks.

Assisted-by: GitHub Copilot
Signed-off-by: Filip Skokan <panva.ip@gmail.com>
Avoid reconstructing EC keys for key details and TLS ephemeral-key
curve reporting.

Assisted-by: GitHub Copilot
Signed-off-by: Filip Skokan <panva.ip@gmail.com>
Query provider coordinates together instead of serializing and
decoding the public point.

Assisted-by: GitHub Copilot
Signed-off-by: Filip Skokan <panva.ip@gmail.com>
Read private scalars and matching uncompressed provider encodings
directly.

Assisted-by: GitHub Copilot
Signed-off-by: Filip Skokan <panva.ip@gmail.com>
@panva panva added performance Issues and PRs related to the performance of Node.js. commit-queue-rebase PRs the Commit Queue should land as multiple self-contained commits. labels Sep 8, 2026
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

Review requested:

  • @nodejs/crypto
  • @nodejs/performance
  • @nodejs/security-wg

@nodejs-github-bot nodejs-github-bot added lib / src Issues and PRs involving general changes in the lib/ or src/ directories. needs-ci PRs that need a full CI run. labels Sep 8, 2026
@panva
panva marked this pull request as draft September 8, 2026 10:31
Use EVP_PKEY_bits() to determine the width of ECDSA signature
components on OpenSSL 3. This avoids reconstructing the EC group
and public point just to read the group order size.

Signed-off-by: Filip Skokan <panva.ip@gmail.com>
Assisted-by: GitHub Copilot
@nodejs nodejs deleted a comment from github-actions Bot Sep 8, 2026
@nodejs nodejs deleted a comment from github-actions Bot Sep 8, 2026
@nodejs nodejs deleted a comment from github-actions Bot Sep 8, 2026
@nodejs nodejs deleted a comment from github-actions Bot Sep 8, 2026
@panva
panva force-pushed the crypto-ec-raw-private-deferred branch from b534fc9 to b406b68 Compare September 8, 2026 10:56
@panva panva changed the title crypto: reduce EC key import and export overhead crypto: reduce EC key processing overhead Sep 8, 2026
@panva
panva marked this pull request as ready for review September 8, 2026 11:01
@github-actions

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Benchmark GHA (crypto / create-keyobject): https://github.com/nodejs/node/actions/runs/34219400423

Results

[!WARNING]
Do not take GHA benchmark results as face value, always confirm them
using a dedicated machine, e.g. Jenkins CI.

Benchmark results:

                                                                              confidence improvement accuracy (*)    (**)   (***)
crypto/create-keyobject.js n=1000 keyFormat='der-pkcs8' keyType='ec'                         -0.82 %       ±8.08% ±10.65% ±13.67%
crypto/create-keyobject.js n=1000 keyFormat='der-pkcs8' keyType='ed25519'                    -0.80 %       ±6.67%  ±8.80% ±11.29%
crypto/create-keyobject.js n=1000 keyFormat='der-pkcs8' keyType='ml-dsa-44'                   1.95 %       ±5.51%  ±7.27%  ±9.33%
crypto/create-keyobject.js n=1000 keyFormat='der-pkcs8' keyType='rsa'                        -1.72 %       ±7.75% ±10.22% ±13.12%
crypto/create-keyobject.js n=1000 keyFormat='der-spki' keyType='ec'                           1.97 %       ±8.61% ±11.35% ±14.56%
crypto/create-keyobject.js n=1000 keyFormat='der-spki' keyType='ed25519'                      0.52 %       ±8.44% ±11.13% ±14.28%
crypto/create-keyobject.js n=1000 keyFormat='der-spki' keyType='ml-dsa-44'                    0.45 %       ±9.07% ±11.96% ±15.35%
crypto/create-keyobject.js n=1000 keyFormat='der-spki' keyType='rsa'                         -0.57 %       ±8.60% ±11.33% ±14.54%
crypto/create-keyobject.js n=1000 keyFormat='jwk-private' keyType='ec'               ***     46.97 %       ±8.21% ±10.82% ±13.89%
crypto/create-keyobject.js n=1000 keyFormat='jwk-private' keyType='ed25519'                  -1.56 %       ±6.09%  ±8.02% ±10.30%
crypto/create-keyobject.js n=1000 keyFormat='jwk-private' keyType='ml-dsa-44'                -0.94 %       ±5.22%  ±6.87%  ±8.82%
crypto/create-keyobject.js n=1000 keyFormat='jwk-private' keyType='rsa'                      -0.50 %       ±6.39%  ±8.42% ±10.80%
crypto/create-keyobject.js n=1000 keyFormat='jwk-public' keyType='ec'                        -0.38 %       ±7.52%  ±9.91% ±12.72%
crypto/create-keyobject.js n=1000 keyFormat='jwk-public' keyType='ed25519'                   -0.09 %       ±8.04% ±10.60% ±13.60%
crypto/create-keyobject.js n=1000 keyFormat='jwk-public' keyType='ml-dsa-44'                  0.46 %       ±7.87% ±10.37% ±13.30%
crypto/create-keyobject.js n=1000 keyFormat='jwk-public' keyType='rsa'                       -0.19 %       ±7.71% ±10.16% ±13.04%
crypto/create-keyobject.js n=1000 keyFormat='pkcs8' keyType='ec'                             -1.33 %       ±8.14% ±10.73% ±13.76%
crypto/create-keyobject.js n=1000 keyFormat='pkcs8' keyType='ed25519'                         0.45 %       ±6.58%  ±8.68% ±11.13%
crypto/create-keyobject.js n=1000 keyFormat='pkcs8' keyType='ml-dsa-44'                       0.52 %       ±5.57%  ±7.34%  ±9.42%
crypto/create-keyobject.js n=1000 keyFormat='pkcs8' keyType='rsa'                            -1.64 %       ±7.07%  ±9.33% ±11.97%
crypto/create-keyobject.js n=1000 keyFormat='raw-private' keyType='ec'                       -1.86 %       ±6.41%  ±8.46% ±10.85%
crypto/create-keyobject.js n=1000 keyFormat='raw-private' keyType='ed25519'                  -0.65 %       ±6.43%  ±8.48% ±10.88%
crypto/create-keyobject.js n=1000 keyFormat='raw-public' keyType='ec'                        -1.35 %       ±7.30%  ±9.63% ±12.35%
crypto/create-keyobject.js n=1000 keyFormat='raw-public' keyType='ed25519'                   -1.38 %       ±8.14% ±10.73% ±13.76%
crypto/create-keyobject.js n=1000 keyFormat='raw-public' keyType='ml-dsa-44'                  0.91 %       ±9.13% ±12.03% ±15.44%
crypto/create-keyobject.js n=1000 keyFormat='raw-seed' keyType='ml-dsa-44'                   -0.30 %       ±5.49%  ±7.24%  ±9.28%
crypto/create-keyobject.js n=1000 keyFormat='spki' keyType='ec'                               0.01 %       ±8.48% ±11.18% ±14.34%
crypto/create-keyobject.js n=1000 keyFormat='spki' keyType='ed25519'                          0.39 %       ±8.56% ±11.28% ±14.48%
crypto/create-keyobject.js n=1000 keyFormat='spki' keyType='ml-dsa-44'                        0.21 %       ±7.72% ±10.18% ±13.06%
crypto/create-keyobject.js n=1000 keyFormat='spki' keyType='rsa'                              1.12 %       ±8.38% ±11.05% ±14.18%

Be aware that when doing many comparisons the risk of a false-positive
result increases. In this case, there are 30 comparisons, you can thus
expect the following amount of false-positive results:
  1.50 false positives, when considering a   5% risk acceptance (*, **, ***),
  0.30 false positives, when considering a   1% risk acceptance (**, ***),
  0.03 false positives, when considering a 0.1% risk acceptance (***)

[!WARNING]
Do not take GHA benchmark results as face value, always confirm them
using a dedicated machine, e.g. Jenkins CI.

@github-actions

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Benchmark GHA (crypto / ec-): https://github.com/nodejs/node/actions/runs/34219408729

Results

[!WARNING]
Do not take GHA benchmark results as face value, always confirm them
using a dedicated machine, e.g. Jenkins CI.

Benchmark results:

                                                                                                 confidence improvement accuracy (*)    (**)    (***)
crypto/ec-jwk-export.js n=10000 type='private' namedCurve='prime256v1'                                  ***    134.04 %      ±11.18% ±14.76%  ±18.98%
crypto/ec-jwk-export.js n=10000 type='private' namedCurve='secp256k1'                                   ***    238.46 %      ±19.90% ±26.29%  ±33.85%
crypto/ec-jwk-export.js n=10000 type='private' namedCurve='secp384r1'                                   ***    224.90 %      ±45.03% ±59.48%  ±76.57%
crypto/ec-jwk-export.js n=10000 type='private' namedCurve='secp521r1'                                   ***    243.52 %      ±44.67% ±59.02%  ±75.99%
crypto/ec-jwk-export.js n=10000 type='public' namedCurve='prime256v1'                                   ***    161.24 %      ±12.20% ±16.10%  ±20.71%
crypto/ec-jwk-export.js n=10000 type='public' namedCurve='secp256k1'                                    ***    301.64 %      ±24.63% ±32.54%  ±41.90%
crypto/ec-jwk-export.js n=10000 type='public' namedCurve='secp384r1'                                    ***    282.24 %      ±54.68% ±72.24%  ±93.03%
crypto/ec-jwk-export.js n=10000 type='public' namedCurve='secp521r1'                                    ***    326.33 %      ±57.90% ±76.51%  ±98.55%
crypto/ec-key-details.js n=10000 type='private' namedCurve='P-256'                                      ***    318.72 %      ±18.62% ±24.60%  ±31.68%
crypto/ec-key-details.js n=10000 type='private' namedCurve='P-384'                                      ***    516.83 %      ±29.72% ±39.23%  ±50.47%
crypto/ec-key-details.js n=10000 type='private' namedCurve='P-521'                                      ***    568.32 %      ±32.94% ±43.52%  ±56.02%
crypto/ec-key-details.js n=10000 type='public' namedCurve='P-256'                                       ***    318.14 %      ±19.15% ±25.30%  ±32.59%
crypto/ec-key-details.js n=10000 type='public' namedCurve='P-384'                                       ***    507.42 %      ±29.64% ±39.13%  ±50.34%
crypto/ec-key-details.js n=10000 type='public' namedCurve='P-521'                                       ***    562.96 %      ±31.38% ±41.45%  ±53.35%
crypto/ec-raw-export.js n=10000 type='compressed' format='raw-public' namedCurve='prime256v1'                    0.37 %       ±4.75%  ±6.26%   ±8.03%
crypto/ec-raw-export.js n=10000 type='compressed' format='raw-public' namedCurve='secp384r1'                     0.96 %      ±17.89% ±23.58%  ±30.26%
crypto/ec-raw-export.js n=10000 type='compressed' format='raw-public' namedCurve='secp521r1'                    -0.40 %      ±15.80% ±20.83%  ±26.73%
crypto/ec-raw-export.js n=10000 type='uncompressed' format='raw-private' namedCurve='prime256v1'        ***    117.92 %       ±7.90% ±10.43%  ±13.40%
crypto/ec-raw-export.js n=10000 type='uncompressed' format='raw-private' namedCurve='secp384r1'         ***    216.74 %      ±14.69% ±19.37%  ±24.86%
crypto/ec-raw-export.js n=10000 type='uncompressed' format='raw-private' namedCurve='secp521r1'         ***    252.17 %      ±15.10% ±19.91%  ±25.55%
crypto/ec-raw-export.js n=10000 type='uncompressed' format='raw-public' namedCurve='prime256v1'         ***     93.30 %       ±5.70%  ±7.51%   ±9.65%
crypto/ec-raw-export.js n=10000 type='uncompressed' format='raw-public' namedCurve='secp384r1'          ***    287.38 %      ±62.04% ±81.99% ±105.60%
crypto/ec-raw-export.js n=10000 type='uncompressed' format='raw-public' namedCurve='secp521r1'          ***    302.08 %      ±63.06% ±83.33% ±107.35%

Be aware that when doing many comparisons the risk of a false-positive
result increases. In this case, there are 23 comparisons, you can thus
expect the following amount of false-positive results:
  1.15 false positives, when considering a   5% risk acceptance (*, **, ***),
  0.23 false positives, when considering a   1% risk acceptance (**, ***),
  0.02 false positives, when considering a 0.1% risk acceptance (***)

[!WARNING]
Do not take GHA benchmark results as face value, always confirm them
using a dedicated machine, e.g. Jenkins CI.

@github-actions

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Benchmark GHA (crypto / webcrypto-sign): https://github.com/nodejs/node/actions/runs/34219404291

Results

[!WARNING]
Do not take GHA benchmark results as face value, always confirm them
using a dedicated machine, e.g. Jenkins CI.

Benchmark results:

                                                                                              confidence improvement accuracy (*)    (**)   (***)
crypto/webcrypto-sign.js n=1000 keyReuse='shared' mode='parallel' keyType='ec'                       ***     18.15 %       ±8.92% ±11.76% ±15.10%
crypto/webcrypto-sign.js n=1000 keyReuse='shared' mode='parallel' keyType='ed25519'                           0.21 %       ±7.50%  ±9.89% ±12.69%
crypto/webcrypto-sign.js n=1000 keyReuse='shared' mode='parallel' keyType='ml-dsa-44'                         1.17 %       ±8.24% ±10.86% ±13.94%
crypto/webcrypto-sign.js n=1000 keyReuse='shared' mode='parallel' keyType='rsa-pss'                          -1.83 %       ±6.62%  ±8.73% ±11.20%
crypto/webcrypto-sign.js n=1000 keyReuse='shared' mode='parallel' keyType='rsassa-pkcs1-v1_5'                -1.32 %       ±6.91%  ±9.11% ±11.68%
crypto/webcrypto-sign.js n=1000 keyReuse='shared' mode='serial' keyType='ec'                         ***     17.72 %       ±9.00% ±11.86% ±15.22%
crypto/webcrypto-sign.js n=1000 keyReuse='shared' mode='serial' keyType='ed25519'                            -2.12 %       ±7.46%  ±9.83% ±12.62%
crypto/webcrypto-sign.js n=1000 keyReuse='shared' mode='serial' keyType='ml-dsa-44'                           0.47 %       ±6.88%  ±9.06% ±11.63%
crypto/webcrypto-sign.js n=1000 keyReuse='shared' mode='serial' keyType='rsa-pss'                            -2.23 %       ±3.79%  ±5.00%  ±6.42%
crypto/webcrypto-sign.js n=1000 keyReuse='shared' mode='serial' keyType='rsassa-pkcs1-v1_5'                  -0.33 %       ±3.62%  ±4.77%  ±6.13%
crypto/webcrypto-sign.js n=1000 keyReuse='unique' mode='parallel' keyType='ec'                       ***     15.75 %       ±8.80% ±11.60% ±14.90%
crypto/webcrypto-sign.js n=1000 keyReuse='unique' mode='parallel' keyType='ed25519'                           1.41 %       ±7.24%  ±9.54% ±12.24%
crypto/webcrypto-sign.js n=1000 keyReuse='unique' mode='parallel' keyType='ml-dsa-44'                         0.15 %       ±8.00% ±10.54% ±13.52%
crypto/webcrypto-sign.js n=1000 keyReuse='unique' mode='parallel' keyType='rsa-pss'                          -2.76 %       ±6.05%  ±7.98% ±10.24%
crypto/webcrypto-sign.js n=1000 keyReuse='unique' mode='parallel' keyType='rsassa-pkcs1-v1_5'                -2.97 %       ±6.05%  ±7.98% ±10.24%

Be aware that when doing many comparisons the risk of a false-positive
result increases. In this case, there are 15 comparisons, you can thus
expect the following amount of false-positive results:
  0.75 false positives, when considering a   5% risk acceptance (*, **, ***),
  0.15 false positives, when considering a   1% risk acceptance (**, ***),
  0.01 false positives, when considering a 0.1% risk acceptance (***)

[!WARNING]
Do not take GHA benchmark results as face value, always confirm them
using a dedicated machine, e.g. Jenkins CI.

@panva panva added the crypto Issues and PRs related to the crypto subsystem. label Sep 8, 2026
@codecov

codecov Bot commented Sep 8, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 81.08108% with 7 lines in your changes missing coverage. Please review.
✅ Project coverage is 90.19%. Comparing base (70458d5) to head (f349ae1).
⚠️ Report is 5 commits behind head on main.

Files with missing lines Patch % Lines
src/crypto/crypto_ec.cc 71.42% 2 Missing and 2 partials ⚠️
src/crypto/crypto_keys.cc 86.36% 1 Missing and 2 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #65908      +/-   ##
==========================================
+ Coverage   90.18%   90.19%   +0.01%     
==========================================
  Files         771      771              
  Lines      265103   265039      -64     
  Branches    50353    50333      -20     
==========================================
- Hits       239082   239061      -21     
+ Misses      16968    16943      -25     
+ Partials     9053     9035      -18     
Files with missing lines Coverage Δ
src/crypto/crypto_common.cc 78.12% <100.00%> (+0.80%) ⬆️
src/crypto/crypto_keys.cc 70.47% <86.36%> (+0.81%) ⬆️
src/crypto/crypto_ec.cc 69.75% <71.42%> (+1.33%) ⬆️

... and 27 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

commit-queue-rebase PRs the Commit Queue should land as multiple self-contained commits. crypto Issues and PRs related to the crypto subsystem. lib / src Issues and PRs involving general changes in the lib/ or src/ directories. needs-ci PRs that need a full CI run. performance Issues and PRs related to the performance of Node.js.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants