From 65f1de1408174ed186878d9d395ba70964fb1457 Mon Sep 17 00:00:00 2001 From: neeraj netrakar Date: Thu, 22 Oct 2026 20:42:17 +0530 Subject: [PATCH] Treat a wrong-length unwrapped CEK like an unwrap failure in jwe.decrypt --- jose/jwe.py | 20 +++++++++++++------- tests/test_jwe.py | 19 ++++++++++++++++++- 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/jose/jwe.py b/jose/jwe.py index 09e5c32..faa8de9 100644 --- a/jose/jwe.py +++ b/jose/jwe.py @@ -150,13 +150,19 @@ def decrypt(jwe_str, key): # recipient or not. cek_valid = False - # To mitigate the attacks described in RFC 3218 [RFC3218], the - # recipient MUST NOT distinguish between format, padding, and length - # errors of encrypted keys. It is strongly recommended, in the event - # of receiving an improperly formatted key, that the recipient - # substitute a randomly generated CEK and proceed to the next step, to - # mitigate timing attacks. - cek_bytes = _get_random_cek_bytes_for_enc(enc) + # To mitigate the attacks described in RFC 3218 [RFC3218], the + # recipient MUST NOT distinguish between format, padding, and length + # errors of encrypted keys. It is strongly recommended, in the event + # of receiving an improperly formatted key, that the recipient + # substitute a randomly generated CEK and proceed to the next step, to + # mitigate timing attacks. + try: + random_cek_bytes = _get_random_cek_bytes_for_enc(enc) + except NotImplementedError: + raise JWEError(f"enc {enc} is not implemented") + if not cek_valid or len(cek_bytes) != len(random_cek_bytes): + cek_valid = False + cek_bytes = random_cek_bytes # Compute the Encoded Protected Header value BASE64URL(UTF8(JWE # Protected Header)). If the JWE Protected Header is not present diff --git a/tests/test_jwe.py b/tests/test_jwe.py index 6ab9971..fd2c274 100644 --- a/tests/test_jwe.py +++ b/tests/test_jwe.py @@ -7,7 +7,7 @@ from jose.constants import ALGORITHMS, ZIPS from jose.exceptions import JWEError, JWEParseError from jose.jwk import AESKey, RSAKey -from jose.utils import base64url_decode +from jose.utils import base64url_decode, base64url_encode backends = [] try: @@ -336,6 +336,23 @@ def test_decrypt_oct_256_key_wrap(self, jwe_package): actual = jwe.decrypt(jwe_package, key) assert actual == b"Live long and prosper." + def test_wrong_length_cek_is_not_distinguishable(self): + # A CEK that unwraps fine but has the wrong length for enc must fail + # the same way as a bad padding (RFC 7516 section 11.5). + encrypted_key = RSAKey(PUBLIC_KEY_PEM, ALGORITHMS.RSA1_5).wrap_key(b"\x00" * 16) + header = base64url_encode(json.dumps({"alg": "RSA1_5", "enc": "A256GCM"}).encode("utf-8")) + jwe_str = b".".join( + [ + header, + base64url_encode(encrypted_key), + base64url_encode(b"\x00" * 12), + b"AAAA", + base64url_encode(b"\x00" * 16), + ] + ) + with pytest.raises(JWEError, match="Invalid JWE Auth Tag"): + jwe.decrypt(jwe_str, PRIVATE_KEY_PEM) + def test_invalid_jwe_is_parse_error(self): with pytest.raises(JWEParseError): jwe.decrypt("invalid", "key")