Skip to content

fix: required-arg checks, validation ordering, and length=null in subtle#1024

Merged
boorad merged 2 commits into
mainfrom
fix/subtle-arg-validation-ordering
May 6, 2026
Merged

fix: required-arg checks, validation ordering, and length=null in subtle#1024
boorad merged 2 commits into
mainfrom
fix/subtle-arg-validation-ordering

Conversation

@boorad
Copy link
Copy Markdown
Collaborator

@boorad boorad commented May 6, 2026

Summary

Aligns SubtleCrypto with Node's webcrypto.js (commits 856231e8c40, 4cb1f284136) for argument-count validation, validation-step ordering, and length=null handling. Closes #1003.

Changes

  • B.6 — Required-arg TypeErrors. New requireArgs(actual, required, method) helper throws plain TypeError (matching webidl) when a subtle.* call is missing required positional args. Wired into importKey, exportKey, encrypt, decrypt, sign, verify, digest, deriveBits, deriveKey, wrapKey, unwrapKey, generateKey, getPublicKey, and the KEM methods.
  • B.8 — Validation ordering. Algorithm-mismatch and usage checks moved out of cipherOrWrap / signVerify and into the public methods (encrypt/decrypt/sign/verify/wrapKey/unwrapKey) so the spec ordering is preserved: algorithm-mismatch first ('Key algorithm mismatch'), then usage ('Unable to use this key to <op>'). Both throw InvalidAccessError.
  • B.9 — wrapKey/unwrapKey algorithm match. wrapKey/unwrapKey now reject when wrappingKey.algorithm.name doesn't match the (normalized) wrap algorithm name.
  • B.10 — deriveBits length=null. length is now number | null with default null. ECDH/X25519/X448 with null returns the full natural shared secret; PBKDF2/HKDF/Argon2 throw OperationError ("length cannot be null").
  • B.11 — getKeyLength hardening. Returns number | null (null for HKDF/PBKDF2/Argon2 KDF targets), throws OperationError on invalid AES (not 128/192/256) and HMAC (length === 0) inputs instead of silently coercing to 256. New getHmacBlockSize helper for the HMAC default (block size of the named hash).
  • subtle.deriveBits usage gate. Now requires 'deriveBits' specifically — the old deriveBits || deriveKey accept-either branch silently promoted deriveKey-only keys.
  • Replaces plain throw new Error(...) in deriveBits / deriveKey with lazyDOMException carrying the right error name (InvalidAccessError, NotSupportedError, OperationError).
  • Cleanup: dropped unreachable try/catch fallback in wrapKey/unwrapKey and the unused _op parameter from cipherOrWrap.

Test plan

  • bun tsc --noEmit clean across both packages
  • New example/src/tests/subtle/validation_ordering.ts (19 tests):
    • importKey/generateKey/sign/verify/encrypt/decrypt/deriveBits/deriveKey/wrapKey/unwrapKey/exportKey/digest/getPublicKey all throw TypeError when called with too few args
    • sign with mismatched algorithm throws 'Key algorithm mismatch' (not the usage error) — confirms ordering
    • sign with correct algorithm but missing usage throws the usage error
    • encrypt / wrapKey / unwrapKey mismatched-algorithm cases all throw 'Key algorithm mismatch' first
    • deriveBits ECDH with length=null returns full 32-byte P-256 shared secret
    • deriveBits ECDH with length omitted defaults to null (full secret)
    • deriveBits HKDF / PBKDF2 with length=null throw OperationError
    • deriveKey to AES-GCM with invalid length 100 throws OperationError("Invalid key length")
    • deriveKey to HMAC with length=0 throws OperationError("Invalid key length")
  • Existing encrypt_decrypt assertions migrated from English-message snippets to InvalidAccessError (the DOMException.name)

boorad added 2 commits May 5, 2026 20:05
Aligns subtle.* with WebCrypto spec and Node webcrypto.js commits
856231e8c40 (required-arg counts) and 4cb1f284136 (validation ordering).

- B.6: every public Subtle method throws TypeError when called with
  fewer than the spec-required arguments.
- B.8: sign/verify and encrypt/decrypt now check algorithm-mismatch
  before usage, throwing 'Key algorithm mismatch' first; the combined
  check moved out of cipherOrWrap so each call site enforces order.
- B.9: wrapKey/unwrapKey gain the missing algorithm-mismatch check
  (with 'wrapKey'→'encrypt' / 'unwrapKey'→'decrypt' fallback).
- B.10: deriveBits accepts length=null (default), per-algorithm gating
  rejects null for HKDF/PBKDF2/Argon2 with OperationError; ECDH and
  X25519/X448 return the full shared secret.
- B.11: getKeyLength throws OperationError on invalid AES (non
  128/192/256) and HMAC (length === 0) lengths instead of silently
  defaulting; HMAC without length now uses the hash block size.

Issue #1003.
- wrapKey/unwrapKey: remove try/catch fallback to 'encrypt'/'decrypt' op
  during normalizeAlgorithm — the operation parameter is unused inside
  normalizeAlgorithm, so the catch was unreachable.
- cipherOrWrap: drop the unused _op parameter (and the four pass-through
  args at the call sites). Algorithm/usage validation already lives at
  the public-method boundaries.
- subtle.deriveBits: use baseKey.usages (matches the rest of the file)
  instead of the keyUsages alias.

No behavior change.
@boorad boorad self-assigned this May 6, 2026
@vercel
Copy link
Copy Markdown

vercel Bot commented May 6, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
react-native-quick-crypto Ready Ready Preview, Comment May 6, 2026 1:04am

Request Review

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 6, 2026

🤖 End-to-End Test Results - Android

Status: ✅ Passed
Platform: Android
Run: 25410925037

📸 Final Test Screenshot

Maestro Test Results - android

Screenshot automatically captured from End-to-End tests and will expire in 30 days


This comment is automatically updated on each test run.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 6, 2026

🤖 End-to-End Test Results - iOS

Status: ✅ Passed
Platform: iOS
Run: 25410925050

📸 Final Test Screenshot

Maestro Test Results - ios

Screenshot automatically captured from End-to-End tests and will expire in 30 days


This comment is automatically updated on each test run.

@boorad boorad merged commit 5b23b02 into main May 6, 2026
9 checks passed
@boorad boorad deleted the fix/subtle-arg-validation-ordering branch May 6, 2026 01:27
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.

subtle: required-arg checks, validation step ordering, and length=null handling

1 participant