[14] WebCrypto EC SPKI/PKCS8 importer bounds check
Rated High because the diff fixes a renderer-reachable parser bug: a 0xFF length byte advances the parse cursor 128 bytes past the buffer end, causing unsigned underflow on
keyData.size() - index(SPKI) or an OOBsubvector(index)that tripsRELEASE_ASSERT(PKCS8 crash).
CryptoKeyEC::platformImportSpki and platformImportPkcs8 advanced index by bytesUsedToEncodedLength(keyData[index]) + 1 without a bounds check. A long-form length byte returns up to 128, pushing index well past keyData.size().
Source/WebCore/crypto/cocoa/CryptoKeyECCocoa.cpp
Patch Details
Bounds checks gate the cursor advance in both importers. Six new EC LayoutTests mirror existing RSA tests one-for-one.
Missing post-advance bounds check on an attacker-controlled ASN.1 length byte that can advance the parse cursor past the buffer end.
Background
SubtleCrypto.importKey accepts SPKI (SubjectPublicKeyInfo) or PKCS8 (PrivateKeyInfo) bytes. ASN.1 DER length encoding: a byte < 0x80 encodes length directly; ≥ 0x80 encodes the number of subsequent bytes that form the length (0xFF means 127 extra bytes, so the length field itself can consume up to 128 bytes). bytesUsedToEncodedLength(b) returns the bytes the length field occupies. Vector<uint8_t>::subvector(offset) RELEASE_ASSERTs offset <= size().
Analysis
The commit message says it explicitly: same bug fixed for RSA in 308706@main. EC was simply missed.
PoC from the layout test: 0x3000300006072a8648ce3d020106082a8648ce3d03010703ff (25 bytes). After passing intermediate checks, index = 24 + bytesUsedToEncodedLength(0xFF) + 1 = 24 + 128 + 1 = 153. keyData.size() - index = 25 - 153 underflows to ~SIZE_MAX. PKCS8 path: keyData.subvector(index) with index > keyData.size() aborts the WebContent process via RELEASE_ASSERT. SPKI path: the underflowed length flows into key-size validation; promotion to a controlled OOB read depends on a downstream consumer trusting it before validation, which the diff does not bound.
This vulnerability weakens renderer-process availability and memory-safety hardening at a web-reachable parser. Web Crypto importers are assumed to validate ASN.1 before pointer/length arithmetic; this invariant did not hold for the final BIT STRING length byte.
Audit directions
- Every
bytesUsedToEncodedLength(b)callsite. Returns 1-128 — everyindex += bytesUsedToEncodedLength(keyData[index]) + NMUST be preceded by a bounds check. Start withCryptoKeyRSA*.cpp,CryptoKeyAES*.cpp,CryptoKeyHMAC*.cpp, and JWK importers. - Unsigned underflow on
buffer.size() - cursor. GrepSource/WebCore/crypto/for arithmetic of this shape wherecursorwas advanced by parsed length bytes. Vector::subvector(offset)/span.subspan(offset)with attacker-derived offsets. SearchSource/WebCore/crypto/and adjacent parsers (WebAuthn CBOR, certificate parsing).- Other 30870x-series RSA fixes for sibling-algorithm variants. The per-algorithm split makes cross-pollination of fixes easy to miss — sweep
CryptoKeyEC*,CryptoKeyAES*,CryptoKeyHMAC*for RSA counterparts.