Skip to content

crypto: fix various edge cases - #66237

Open
panva wants to merge 29 commits into
nodejs:mainfrom
panva:webcrypto-findings
Open

panva wants to merge 29 commits into
nodejs:mainfrom
panva:webcrypto-findings

Conversation

@panva

@panva panva commented Sep 23, 2026

Copy link
Copy Markdown
Member

Fix conversion, key handling, and algorithm edge cases found during an implementation audit.

  • Separate Web IDL conversion from algorithm validation, preserving getter receivers and validating converted values. Correct algorithm and hash normalization, importKey() union conversion, and SubtleCrypto.supports() overload resolution and unbound calls.
  • Use intrinsic buffer bounds in getRandomValues(), randomFill(), and randomFillSync(). Treat detached parameters as empty and avoid constructor/species hooks when copying parameters, derived bits, key usages, and hybrid key material.
  • Correct CryptoKey prototypes, RSA key metadata and JWK alg validation, duplicate JWK key operations, and EC key import/export. Report native JWK export failures as OperationError.
  • Allow backend-supported short AES-GCM IVs, reject undersized AES-KW inputs, isolate unrelated OpenSSL cipher errors, and correct PBKDF2 iteration-limit handling. Process Argon2 lanes serially when OpenSSL cannot use its own worker threads.
  • Use OpenSSL's cSHAKE and KMAC implementations and remove the custom Keccak framing. Non-empty cSHAKE functionName or customization requires OpenSSL 4.0; when both are empty, continue using SHAKE. Customization retains the 512-byte limit and cannot contain null bytes.
  • Reject non-byte-aligned cSHAKE/KMAC outputs and KMAC keys pending clarification of their bit-string encoding. Require KMAC keys of at least 32 bits and make zero-length KMAC verification accept an empty signature.

@panva panva added the commit-queue-rebase PRs the Commit Queue should land as multiple self-contained commits. label Sep 23, 2026
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

Review requested:

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

@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 23, 2026
@codecov

codecov Bot commented Sep 23, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 95.48872% with 18 lines in your changes missing coverage. Please review.
✅ Project coverage is 90.30%. Comparing base (a2c8da5) to head (4f26a35).
⚠️ Report is 4 commits behind head on main.

Files with missing lines Patch % Lines
lib/internal/crypto/webidl.js 92.40% 5 Missing and 1 partial ⚠️
src/crypto/crypto_keys.cc 64.70% 2 Missing and 4 partials ⚠️
lib/internal/crypto/hash.js 50.00% 4 Missing ⚠️
src/crypto/crypto_kmac.cc 81.81% 0 Missing and 2 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #66237      +/-   ##
==========================================
+ Coverage   90.28%   90.30%   +0.02%     
==========================================
  Files         789      789              
  Lines      272878   272784      -94     
  Branches    52097    52086      -11     
==========================================
- Hits       246360   246336      -24     
+ Misses      16975    16952      -23     
+ Partials     9543     9496      -47     
Files with missing lines Coverage Δ
lib/internal/crypto/aes.js 94.59% <100.00%> (+0.84%) ⬆️
lib/internal/crypto/argon2.js 98.71% <100.00%> (+0.01%) ⬆️
lib/internal/crypto/cfrg.js 95.75% <100.00%> (+0.06%) ⬆️
lib/internal/crypto/chacha20_poly1305.js 98.26% <100.00%> (+0.03%) ⬆️
lib/internal/crypto/diffiehellman.js 97.72% <100.00%> (-0.02%) ⬇️
lib/internal/crypto/ec.js 96.94% <100.00%> (+0.03%) ⬆️
lib/internal/crypto/hkdf.js 100.00% <100.00%> (ø)
lib/internal/crypto/kem_hybrids.js 98.38% <100.00%> (+<0.01%) ⬆️
lib/internal/crypto/keys.js 97.59% <100.00%> (-0.72%) ⬇️
lib/internal/crypto/mac.js 99.03% <100.00%> (+0.01%) ⬆️
... and 17 more

... and 28 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.

@panva
panva force-pushed the webcrypto-findings branch from 536fc0b to 54f9426 Compare September 23, 2026 12:39
@nodejs-github-bot

This comment was marked as outdated.

@jasnell jasnell left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think some of the additional validations would technically qualify as semver-major but I'd prefer to not have to land it as such. I think we could probably get away with classifying them as bug fixes.

@panva panva added the author ready PRs with CI started, the required approvals, and no outstanding review comments. label Sep 23, 2026
@panva

panva commented Sep 23, 2026

Copy link
Copy Markdown
Member Author

I think some of the additional validations would technically qualify as semver-major but I'd prefer to not have to land it as such. I think we could probably get away with classifying them as bug fixes.

Thank you James, some of the added validations are just fail earlies that would fall into OperationError, some of them are for [experimental] algorithms or methods, some are really edge case bug fixes that, I concur, I don't feel are needed to be treated as [semver-major].

@nodejs-github-bot

This comment was marked as outdated.

@nodejs-github-bot

This comment was marked as outdated.

Read buffer lengths and element sizes from internal slots in
randomFillSync() and randomFill(). Shadowed properties can otherwise
skip filling, change the selected range, or fail a native bounds check.

Use the intrinsic byte length for the getRandomValues() quota check and
keep its delegation to randomFillSync().

Signed-off-by: Filip Skokan <panva.ip@gmail.com>
Assisted-by: Codex
Reject non-ASCII equivalents of registered Web Crypto algorithm names.
Skip the character scan when the input already uses the canonical name.

Signed-off-by: Filip Skokan <panva.ip@gmail.com>
Assisted-by: Codex
Enforce the minimum key-wrap input lengths before the empty-input
shortcut, including the integrity-check block when unwrapping.

Signed-off-by: Filip Skokan <panva.ip@gmail.com>
Assisted-by: Codex
Use only errors captured by the cipher job when converting its result.
Unrelated errors on the event loop thread must not turn a successful
operation into a failure or trigger an assertion.

Signed-off-by: Filip Skokan <panva.ip@gmail.com>
Assisted-by: Codex
Apply Web IDL view conversion before checking the integer array type,
including shared and resizable backing-buffer restrictions.

Signed-off-by: Filip Skokan <panva.ip@gmail.com>
Assisted-by: Codex
Check the normalized SHA algorithm instead of reading the caller hash
name before conversion. This prevents changing getters from bypassing
the supported hash check.

Signed-off-by: Filip Skokan <panva.ip@gmail.com>
Assisted-by: Codex
Allow nonempty GCM IVs shorter than the default length and let the
underlying cipher enforce its supported range.

Signed-off-by: Filip Skokan <panva.ip@gmail.com>
Assisted-by: Codex
Static Web IDL operations do not require an interface receiver.

Signed-off-by: Filip Skokan <panva.ip@gmail.com>
Assisted-by: Codex
Convert the third argument before checking the operation, and apply
additional-algorithm checks only for that overload.

Signed-off-by: Filip Skokan <panva.ip@gmail.com>
Assisted-by: Codex
Treat detached BufferSource parameters as empty byte sequences during
algorithm normalization, including detached DataViews.

Signed-off-by: Filip Skokan <panva.ip@gmail.com>
Assisted-by: Codex
Choose the BufferSource or JsonWebKey branch from the value, then
check the requested format after algorithm normalization.

Signed-off-by: Filip Skokan <panva.ip@gmail.com>
Assisted-by: Codex
Allocate byte copies directly so typed-array species cannot replace
normalized parameters or bit-truncated key material.

Signed-off-by: Filip Skokan <panva.ip@gmail.com>
Assisted-by: Codex
Reject a supplied JWK alg when no matching identifier exists for the
requested RSA algorithm and hash.

Signed-off-by: Filip Skokan <panva.ip@gmail.com>
Assisted-by: Codex
Check all JWK key_ops entries for duplicates before ignoring unknown
usage names.

Signed-off-by: Filip Skokan <panva.ip@gmail.com>
Assisted-by: Codex
Report OperationError for unsupported iteration counts and return an
empty result before invoking the backend when no output is requested.

Signed-off-by: Filip Skokan <panva.ip@gmail.com>
Assisted-by: Codex
Convert algorithm dictionaries on the original receiver, with name
read once. Validate normalized parameters in their operation steps
after the method-level key checks and generation usage checks.

This also makes Argon2 validation use converted parallelism and keeps
later dictionary conversion errors ahead of semantic parameter errors.

Signed-off-by: Filip Skokan <panva.ip@gmail.com>
Assisted-by: Codex
Use the byte-copy helper for truncated ECDH results so ArrayBuffer
species cannot replace or resize the returned key material.

Signed-off-by: Filip Skokan <panva.ip@gmail.com>
Assisted-by: Codex
Omit leading zero octets from generated CryptoKey publicExponent
values, matching the BigInteger representation.

Signed-off-by: Filip Skokan <panva.ip@gmail.com>
Assisted-by: Codex
Build public usages and JWK key_ops from the native usage mask so
array species cannot replace or mutate internal usage arrays.

Signed-off-by: Filip Skokan <panva.ip@gmail.com>
Assisted-by: Codex
Keep Argon2 lane count independent from backend worker availability.
When the OpenSSL thread pool is unavailable, compute lanes serially.

Signed-off-by: Filip Skokan <panva.ip@gmail.com>
Assisted-by: Codex
Use the generated key's modulus size for both CryptoKey algorithm
objects. Preserve successful backend generation when it rounds the
requested size, keeping metadata consistent across export and cloning.

Signed-off-by: Filip Skokan <panva.ip@gmail.com>
Assisted-by: Codex
Create byte views directly when splitting hybrid KEM keys and seeds,
so typed-array species cannot alter the component key material.

Signed-off-by: Filip Skokan <panva.ip@gmail.com>
Assisted-by: Codex
Give generated, imported, converted, and transferred keys the public
interface prototype directly, without an intermediate internal object.

Signed-off-by: Filip Skokan <panva.ip@gmail.com>
Assisted-by: Codex
Export Web Crypto EC private keys from a clone with the public point
included, even when the imported encoding omitted it. Preserve the
original KeyObject encoding state.

Signed-off-by: Filip Skokan <panva.ip@gmail.com>
Assisted-by: Codex
Map inaccessible key material to the Web Crypto error type, including
seedless ML-DSA and ML-KEM keys converted from KeyObjects.

Assisted-by: Codex
Signed-off-by: Filip Skokan <panva.ip@gmail.com>
Propagate coordinate conversion failures instead of importing an
oversized JWK coordinate as zero. Keep equivalent short and zero-padded
integer encodings accepted by the native decoder.

Signed-off-by: Filip Skokan <panva.ip@gmail.com>
Assisted-by: Codex
Signed-off-by: Filip Skokan <panva.ip@gmail.com>
Assisted-by: Codex
Require cSHAKE and KMAC output lengths and KMAC key lengths to be
multiples of 8 bits. KMAC keys must be at least 32 bits. Share these
restrictions between operations and supports.

Use OpenSSL's KMAC provider for all supported inputs and its cSHAKE
implementation for non-empty function names or customization strings.
Keep using SHAKE when both cSHAKE parameters are empty. Remove the
custom Keccak framing, partial-bit handling, and short-key fallback.

Document the OpenSSL 4.0 requirement for non-empty cSHAKE parameters and
reject customization strings containing null bytes. Keep the documented
512-byte customization limit and let backend failures reach callers.

Signed-off-by: Filip Skokan <panva.ip@gmail.com>
Assisted-by: Codex
Compare empty KMAC outputs with empty signatures as equal, as required
by the verification algorithm. Skip CRYPTO_memcmp for zero-length
buffers while retaining the constant-time comparison for other MACs.

Signed-off-by: Filip Skokan <panva.ip@gmail.com>
Assisted-by: Codex
@panva
panva force-pushed the webcrypto-findings branch from 54f9426 to 4f26a35 Compare September 24, 2026 07:21
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

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

Labels

author ready PRs with CI started, the required approvals, and no outstanding review comments. commit-queue-rebase PRs the Commit Queue should land as multiple self-contained commits. lib / src Issues and PRs involving general changes in the lib/ or src/ directories. needs-ci PRs that need a full CI run.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants