fix(sdk): bounds-check the DER ECDSA signature parser (DSPX-3397) - #988
fix(sdk): bounds-check the DER ECDSA signature parser (DSPX-3397)#988dmihalcik-virtru wants to merge 2 commits into
Conversation
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
cryptoService.sign() returns DER-encoded ECDSA signatures, but JWS requires
raw IEEE P1363 (R || S) per RFC 7518 section 3.4. Both JWS emitters -- the KAS
rewrap request token (tdf3/src/crypto/jwt.ts) and the DPoP proof signer
(src/auth/dpop.ts) -- were shipping DER, so any EC-keyed token was rejected by
conformant verifiers (Keycloak, panva-jose). verifyJwt had the mirror bug: it
fed a raw JWS signature to a verifier expecting DER.
Also fixes reqSignature defaulting to RS256 for the rewrap request token.
WebCrypto rejects signing an EC private key with RSA params ("Unable to use
this key to sign"), so an EC dpop key could not produce a rewrap token at all;
the alg is now derived from the key's algorithm.
Supporting changes:
- export ieeeP1363ToDer / derToIeeeP1363 for these callers
- add isAsymmetricSigningAlgorithm() so the JWS `alg` header is validated
rather than blind-cast to the narrower set CryptoService can actually sign
with (the JWS alg space includes PS256/EdDSA, which we do not support)
The round-trip this creates (sign encodes to DER, caller decodes back to raw)
is tracked for removal in DSPX-3634.
Signed-off-by: Dave Mihalcik <dmihalcik@virtru.com>
derToIeeeP1363 indexed into the signature buffer without checking bounds: a truncated or malformed DER blob read `undefined` length bytes, sliced empty components, and then computed a negative offset in `result.set()`. It also only stripped a single leading zero, and only when the component was already over-long, so a validly-padded value could survive at the wrong width. ieeeP1363ToDer had the mirror gap: it derived the component length by halving the input, so an odd-length or wrong-curve input silently produced a fractional split rather than an error. Both now derive the fixed component width from the algorithm (getEcdsaComponentLength) and validate every read. Malformed input raises ConfigurationError with a specific message instead of producing a garbage signature. Signed-off-by: Dave Mihalcik <dmihalcik@virtru.com>
756b4b3 to
b220a91
Compare
4f53ae5 to
5722f0b
Compare
|
X-Test Failure Report |
b220a91 to
0f1935a
Compare



Stack 2/8, split out of #939. Base: #987.
What
derToIeeeP1363indexed into the signature buffer without checking bounds. A truncated or malformed DER blob would:undefinedas a length byte,result.set(r, componentLen - r.length).It also stripped only a single leading zero, and only when the component was already over-long — so a validly zero-padded value could survive at the wrong width and silently produce a garbage signature.
ieeeP1363ToDerhad the mirror gap: it derived the component length by halving the input, so an odd-length or wrong-curve input produced a fractional split instead of an error.Changes
Both directions now derive the fixed component width from the algorithm (
getEcdsaComponentLength: ES256→32, ES384→48, ES512→66) and validate every read. Malformed input raisesConfigurationErrorwith a message naming the specific problem instead of returning a bad signature.trimLeadingZerosis hoisted to module scope and now used by both directions.Tests
tests/mocha/unit/crypto/der-signature.spec.ts— round-trip across all three curves, plus truncated, over-long, wrong-tag, zero-length-INTEGER, and wrong-curve inputs.tests/mocha/reqsignature-jws.spec.tsgains the truncated-signature case, which asserts the specific error rather than a generic verification failure.How to test
cd lib && npm test