fix(sdk): emit RFC 7518 raw ECDSA signatures in JWS (DSPX-3397) - #987
fix(sdk): emit RFC 7518 raw ECDSA signatures in JWS (DSPX-3397)#987dmihalcik-virtru wants to merge 1 commit 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>
756b4b3 to
b220a91
Compare
|



Stack 1/8, split out of #939. Base:
main.What
cryptoService.sign()returns DER-encoded ECDSA signatures, but JWS requires raw IEEE P1363 (R || S) per RFC 7518 §3.4. Both JWS emitters were shipping DER:tdf3/src/crypto/jwt.ts— the KAS rewrap request tokensrc/auth/dpop.ts— the DPoP proofso any EC-keyed token was rejected by conformant verifiers. This is the "Invalid token signature" / "unable to verify request token" failure seen against Keycloak.
verifyJwthad the mirror bug: it fed a raw JWS signature to a verifier that expects DER.Separately,
reqSignaturedefaulted toRS256for 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.Why it wasn't caught
The SDK's own sign/verify pair is symmetric — both sides speak DER — so it round-trips internally even when the wire format is wrong. The mock test server only
decodeJwts the rewrap token and never checks the signature. Both passed while the real platform failed.The new tests verify against
jose.jwtVerify, an independent RFC-conformant verifier (the same library Keycloak uses), which is what closes that gap.Changes
tdf3/src/crypto/jwt.ts: convert to raw on sign, back to DER on verify, forES*src/auth/dpop.ts: same conversion in the proof signertdf3/src/tdf.ts:signingAlgForKeyAlgorithm()picks the rewrap token alg from the dpop keytdf3/src/crypto/core/signing.ts: exportieeeP1363ToDer/derToIeeeP1363tdf3/src/crypto/declarations.ts: addisAsymmetricSigningAlgorithm()so the JWSalgheader is validated rather than blind-cast (the JWS alg space includesPS256/EdDSA, which we cannot sign with)Tests
tests/mocha/dpop-proof.spec.ts— DPoP proofs (ES256/384/512, RS256) verified withjose.jwtVerify, plus tamper and wrong-key casestests/mocha/reqsignature-jws.spec.ts— rewrap request token, same treatmenttests/mocha/helpers/jws-keys.ts— shared WebCrypto→PEM→SDK key fixtures for both suitestests/mocha/encrypt-decrypt.spec.ts— end-to-end decrypt with an EC dpop keyFollow-up
The conversion creates a round-trip:
sign()encodes raw→DER, and both callers immediately decode DER→raw. Removing it (makingsign()/verify()speak raw) is DSPX-3634 and is stack 8/8, kept separate because it changes the publicCryptoServicecontract.How to test
cd lib && npm test