fix(deno_crypto): return errors instead of aborting on malformed WebCrypto keys - #727
Merged
Merged
Conversation
…rypto keys
Two `crypto.subtle` paths in deno_crypto 0.196.0 panic on user-supplied key
material. Because the panics happen inside op functions that cannot unwind,
they abort the whole process rather than surfacing a JS exception, so a single
edge function can take the runtime down.
1. `importKey("pkcs8", ...)` for EC keys unwraps the conversion of the PKCS#8
AlgorithmIdentifier parameters into a named-curve OID. A key that encodes
explicit EC parameters (a SEQUENCE) instead of a curve OID aborts with:
called `Result::unwrap()` on an `Err` value: Error { kind: TagUnexpected {
expected: Some(Tag(0x06: OBJECT IDENTIFIER)), actual: Tag(0x30: SEQUENCE) } }
2. `importKey("raw", ...)` for X25519/X448/Ed25519 never validates the key
length, so a shorter key (e.g. an empty Uint8Array) is stored as-is and the
subsequent `deriveBits` aborts in `op_crypto_derive_bits_{x25519,x448}` when
converting the slice to a fixed-size array.
Both are fixed upstream (denoland/deno#32410 and denoland/deno#33944), but the
releases carrying them require a deno_core bump we cannot take here, so vendor
deno_crypto 0.196.0 and cherry-pick the two fixes. The vendored tree is
otherwise byte-identical to the published crate.
Malformed input now raises a DataError DOMException and the worker survives.
Valid keys are unaffected.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
laktek
approved these changes
Aug 31, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Two
crypto.subtlepaths indeno_crypto0.196.0 panic on user-supplied key material. The panics happen inside op functions that cannot unwind, so they abort the whole process instead of surfacing a JS exception — a single edge function can take the runtime down.1. EC
importKey("pkcs8", ...)—import_key.rs:683unwraps the conversion of the PKCS#8AlgorithmIdentifierparameters into a named-curve OID. A key that encodes explicit EC parameters (aSEQUENCE) instead of a curve OID aborts:2.
importKey("raw", ...)for X25519/X448/Ed25519 — the key length is never validated, so a shorter key (e.g. an emptyUint8Array) is stored as-is and the subsequentderiveBitsaborts inop_crypto_derive_bits_{x25519,x448}when converting the slice to a fixed-size array. Same shape, found while auditing for the pattern above.How
Both are fixed upstream — denoland/deno#32410 and denoland/deno#33944 — but the releases carrying them (deno v2.7.10+) require a
deno_corebump we can't take here. So this vendorsdeno_crypto0.196.0 undervendor/(same pattern asdeno_fetch/deno_http/deno_telemetry) and cherry-picks just those two fixes.The vendored tree is otherwise byte-identical to the published crate. The Rust diff against pristine 0.196.0 is:
import_key.rs— one line,.unwrap()→.map_err(|_| ImportKeyError::MalformedParameters)?x25519.rs/x448.rs—derive_bitsreturnsResultinstead ofexpect-ing, plus a newInvalidKeyLengthvariant00_crypto.js— length validation on the raw/jwk import paths (X448's jwk path was also missing theop_crypto_base64url_decodetry/catch that X25519/Ed25519 already had)deno/runtime/errors.rsmaps the new variants toDOMExceptionDataError.The remaining 26 panic sites in the crate were checked and are unreachable: the ECDH
pk.unwrap()s inlib.rssit behindis_some()guards, and PBKDF2'sNonZeroU32::new().unwrap()/assert!(length % 8 == 0)are guarded in JS, which throwsOperationErrorfirst.Verification
Built locally and driven through a function that hits each path.
Before (fix reverted, everything else identical) — one request kills the process:
After — DOMExceptions, process survives repeated hits, valid keys unaffected:
cargo check --workspaceis clean.🤖 Generated with Claude Code