Skip to content

Commit b91c69c

Browse files
committed
Fix for CVE-2024-33664. JWE limited to 250K
1 parent 58e543e commit b91c69c

File tree

3 files changed

+53
-7
lines changed

3 files changed

+53
-7
lines changed

jose/constants.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,3 +96,5 @@ class Zips:
9696

9797

9898
ZIPS = Zips()
99+
100+
JWE_SIZE_LIMIT = 250 * 1024

jose/jwe.py

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
from . import jwk
88
from .backends import get_random_bytes
9-
from .constants import ALGORITHMS, ZIPS
9+
from .constants import ALGORITHMS, ZIPS, JWE_SIZE_LIMIT
1010
from .exceptions import JWEError, JWEParseError
1111
from .utils import base64url_decode, base64url_encode, ensure_binary
1212

@@ -76,6 +76,13 @@ def decrypt(jwe_str, key):
7676
>>> jwe.decrypt(jwe_string, 'asecret128bitkey')
7777
'Hello, World!'
7878
"""
79+
80+
# Limit the token size - if the data is compressed then decompressing the
81+
# data could lead to large memory usage. This helps address This addresses
82+
# CVE-2024-33664. Also see _decompress()
83+
if len(jwe_str) > JWE_SIZE_LIMIT:
84+
raise JWEError(f"JWE string exceeds {JWE_SIZE_LIMIT} bytes")
85+
7986
header, encoded_header, encrypted_key, iv, cipher_text, auth_tag = _jwe_compact_deserialize(jwe_str)
8087

8188
# Verify that the implementation understands and can process all
@@ -424,13 +431,13 @@ def _compress(zip, plaintext):
424431
(bytes): Compressed plaintext
425432
"""
426433
if zip not in ZIPS.SUPPORTED:
427-
raise NotImplementedError("ZIP {} is not supported!")
434+
raise NotImplementedError(f"ZIP {zip} is not supported!")
428435
if zip is None:
429436
compressed = plaintext
430437
elif zip == ZIPS.DEF:
431438
compressed = zlib.compress(plaintext)
432439
else:
433-
raise NotImplementedError("ZIP {} is not implemented!")
440+
raise NotImplementedError(f"ZIP {zip} is not implemented!")
434441
return compressed
435442

436443

@@ -446,13 +453,18 @@ def _decompress(zip, compressed):
446453
(bytes): Compressed plaintext
447454
"""
448455
if zip not in ZIPS.SUPPORTED:
449-
raise NotImplementedError("ZIP {} is not supported!")
456+
raise NotImplementedError(f"ZIP {zip} is not supported!")
450457
if zip is None:
451458
decompressed = compressed
452459
elif zip == ZIPS.DEF:
453-
decompressed = zlib.decompress(compressed)
460+
# If, during decompression, there is more data than expected, the
461+
# decompression halts and raise an error. This addresses CVE-2024-33664
462+
decompressor = zlib.decompressobj()
463+
decompressed = decompressor.decompress(compressed, max_length=JWE_SIZE_LIMIT)
464+
if decompressor.unconsumed_tail:
465+
raise JWEError(f"Decompressed JWE string exceeds {JWE_SIZE_LIMIT} bytes")
454466
else:
455-
raise NotImplementedError("ZIP {} is not implemented!")
467+
raise NotImplementedError(f"ZIP {zip} is not implemented!")
456468
return decompressed
457469

458470

tests/test_jwe.py

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import jose.backends
66
from jose import jwe
77
from jose.constants import ALGORITHMS, ZIPS
8-
from jose.exceptions import JWEParseError
8+
from jose.exceptions import JWEParseError, JWEError
99
from jose.jwk import AESKey, RSAKey
1010
from jose.utils import base64url_decode
1111

@@ -525,3 +525,35 @@ def test_kid_header_not_present_when_not_provided(self):
525525
encrypted = jwe.encrypt("Text", PUBLIC_KEY_PEM, enc, alg)
526526
header = json.loads(base64url_decode(encrypted.split(b".")[0]))
527527
assert "kid" not in header
528+
529+
@pytest.mark.skipif(AESKey is None, reason="No AES backend")
530+
def test_jwe_with_excessive_data(self):
531+
enc = ALGORITHMS.A256CBC_HS512
532+
alg = ALGORITHMS.RSA_OAEP_256
533+
import jose.constants
534+
old_limit = jose.constants.JWE_SIZE_LIMIT
535+
try:
536+
jose.constants.JWE_SIZE_LIMIT = 1024
537+
encrypted = jwe.encrypt(b"Text"*64*1024, PUBLIC_KEY_PEM, enc, alg)
538+
header = json.loads(base64url_decode(encrypted.split(b".")[0]))
539+
with pytest.raises(JWEError):
540+
actual = jwe.decrypt(encrypted, PRIVATE_KEY_PEM)
541+
finally:
542+
jose.constants.JWE_SIZE_LIMIT = old_limit
543+
544+
@pytest.mark.skipif(AESKey is None, reason="No AES backend")
545+
def test_jwe_zip_with_excessive_data(self):
546+
# Test that a fix for CVE-2024-33664 is in place.
547+
enc = ALGORITHMS.A256CBC_HS512
548+
alg = ALGORITHMS.RSA_OAEP_256
549+
import jose.constants
550+
old_limit = jose.constants.JWE_SIZE_LIMIT
551+
try:
552+
jose.constants.JWE_SIZE_LIMIT = 1024
553+
encrypted = jwe.encrypt(b"Text"*64*1024, PUBLIC_KEY_PEM, enc, alg, zip=ZIPS.DEF)
554+
assert len(encrypted) < jose.constants.JWE_SIZE_LIMIT
555+
header = json.loads(base64url_decode(encrypted.split(b".")[0]))
556+
with pytest.raises(JWEError):
557+
actual = jwe.decrypt(encrypted, PRIVATE_KEY_PEM)
558+
finally:
559+
jose.constants.JWE_SIZE_LIMIT = old_limit

0 commit comments

Comments
 (0)