Fix RSA1_5 padding oracle: length-check the unwrapped CEK - #415
Open
RavSinghChandan wants to merge 1 commit into
Open
Fix RSA1_5 padding oracle: length-check the unwrapped CEK#415RavSinghChandan wants to merge 1 commit into
RavSinghChandan wants to merge 1 commit into
Conversation
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.
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.
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).