Skip to content

Fix RSA1_5 padding oracle: length-check the unwrapped CEK - #415

Open
RavSinghChandan wants to merge 1 commit into
mpdavis:masterfrom
RavSinghChandan:fix/rsa1_5-cek-length-check
Open

Fix RSA1_5 padding oracle: length-check the unwrapped CEK#415
RavSinghChandan wants to merge 1 commit into
mpdavis:masterfrom
RavSinghChandan:fix/rsa1_5-cek-length-check

Conversation

@RavSinghChandan

Copy link
Copy Markdown

Fixes #408.

JWE decryption with alg=RSA1_5 currently gives a remote attacker a padding
oracle. RFC 7516 §11.5 requires that format, padding and length errors of
encrypted keys are indistinguishable, and that a random CEK is substituted on
failure. jose/jwe.py does that substitution only in the except branch — a
successful unwrap_key() return is never length-checked.

That is not enough, because cryptography's PKCS1v15 decryption does not raise
for every malformed ciphertext. Its constant-time path returns bytes of an
arbitrary length instead. On 300 random 256-byte ciphertexts against a fresh
RSA-2048 key, 96 raised and 204 returned wrong-length bytes without raising.
Those 204 skip the substitution, reach the AES key constructor, and produce a
length-specific JWKError.

Against 3.5.0, 400 malformed tokens (RSA1_5 + A256GCM) give two distinct
attacker-visible responses:

297x Key must be 256 bit for alg A256GCM <- unwrap returned wrong length
103x Invalid JWE Auth Tag <- substitution worked

Same vulnerability class as Authlib's CVE-2026-28490 / GHSA-7432-952r-cw78,
fixed in Authlib 1.6.9.

The fix treats a wrong-length unwrap exactly like a raised padding error, so
both paths converge on the existing random-CEK substitution — no new code path
is introduced. The same 400-token run then collapses to a single uniform
"Invalid JWE Auth Tag".

Added a regression test that asserts malformed RSA1_5 tokens produce only one
distinct error message. It fails on master with:

AssertionError: malformed keys produced distinguishable errors:
['Invalid JWE Auth Tag', 'Key must be 256 bit for alg A256GCM']

Round-trip decryption still works for every RSA1_5 + enc combination
(A128GCM, A192GCM, A256GCM, A128CBC-HS256, A192CBC-HS384, A256CBC-HS512).

PKCS1v15 unwrapping does not raise for every malformed encrypted key; the
constant-time path can return arbitrary bytes instead. Those bypassed the
random-CEK substitution and reached the AES key constructor, which raises a
length-specific error that is distinguishable from the auth-tag failure.

RFC 7516 section 11.5 requires that format, padding and length errors are
indistinguishable, so length-check the unwrap result and let it fall into the
existing substitution path.
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.

[Security] RSA1_5 JWE decryption violates RFC 7516 §11.5: wrong-length unwrapped CEK escapes random-CEK substitution and reaches AES key constructor

1 participant