Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix for CVE-2024-33664. JWE limited to 250K #352

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions jose/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,3 +96,5 @@ class Zips:


ZIPS = Zips()

JWE_SIZE_LIMIT = 250 * 1024
24 changes: 18 additions & 6 deletions jose/jwe.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

from . import jwk
from .backends import get_random_bytes
from .constants import ALGORITHMS, ZIPS
from .constants import ALGORITHMS, ZIPS, JWE_SIZE_LIMIT
from .exceptions import JWEError, JWEParseError
from .utils import base64url_decode, base64url_encode, ensure_binary

Expand Down Expand Up @@ -76,6 +76,13 @@ def decrypt(jwe_str, key):
>>> jwe.decrypt(jwe_string, 'asecret128bitkey')
'Hello, World!'
"""

# Limit the token size - if the data is compressed then decompressing the
# data could lead to large memory usage. This helps address This addresses
# CVE-2024-33664. Also see _decompress()
if len(jwe_str) > JWE_SIZE_LIMIT:
raise JWEError("JWE string exceeds {JWE_SIZE_LIMIT} bytes")
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be an f-string.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
raise JWEError("JWE string exceeds {JWE_SIZE_LIMIT} bytes")
raise JWEError(f"JWE string exceeds {JWE_SIZE_LIMIT} bytes")


header, encoded_header, encrypted_key, iv, cipher_text, auth_tag = _jwe_compact_deserialize(jwe_str)

# Verify that the implementation understands and can process all
Expand Down Expand Up @@ -424,13 +431,13 @@ def _compress(zip, plaintext):
(bytes): Compressed plaintext
"""
if zip not in ZIPS.SUPPORTED:
raise NotImplementedError("ZIP {} is not supported!")
raise NotImplementedError(f"ZIP {zip} is not supported!")
if zip is None:
compressed = plaintext
elif zip == ZIPS.DEF:
compressed = zlib.compress(plaintext)
else:
raise NotImplementedError("ZIP {} is not implemented!")
raise NotImplementedError(f"ZIP {zip} is not implemented!")
return compressed


Expand All @@ -446,13 +453,18 @@ def _decompress(zip, compressed):
(bytes): Compressed plaintext
"""
if zip not in ZIPS.SUPPORTED:
raise NotImplementedError("ZIP {} is not supported!")
raise NotImplementedError(f"ZIP {zip} is not supported!")
if zip is None:
decompressed = compressed
elif zip == ZIPS.DEF:
decompressed = zlib.decompress(compressed)
# If, during decompression, there is more data than expected, the
# decompression halts and raise an error. This addresses CVE-2024-33664
decompressor = zlib.decompressobj()
decompressed = decompressor.decompress(compressed, max_length=JWE_SIZE_LIMIT)
if decompressor.unconsumed_tail:
raise JWEError(f"Decompressed JWE string exceeds {JWE_SIZE_LIMIT} bytes")
else:
raise NotImplementedError("ZIP {} is not implemented!")
raise NotImplementedError(f"ZIP {zip} is not implemented!")
return decompressed


Expand Down
34 changes: 33 additions & 1 deletion tests/test_jwe.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import jose.backends
from jose import jwe
from jose.constants import ALGORITHMS, ZIPS
from jose.exceptions import JWEParseError
from jose.exceptions import JWEParseError, JWEError
from jose.jwk import AESKey, RSAKey
from jose.utils import base64url_decode

Expand Down Expand Up @@ -525,3 +525,35 @@ def test_kid_header_not_present_when_not_provided(self):
encrypted = jwe.encrypt("Text", PUBLIC_KEY_PEM, enc, alg)
header = json.loads(base64url_decode(encrypted.split(b".")[0]))
assert "kid" not in header

@pytest.mark.skipif(AESKey is None, reason="No AES backend")
def test_jwe_with_excessive_data(self):
enc = ALGORITHMS.A256CBC_HS512
alg = ALGORITHMS.RSA_OAEP_256
import jose.constants
old_limit = jose.constants.JWE_SIZE_LIMIT
try:
jose.constants.JWE_SIZE_LIMIT = 1024
encrypted = jwe.encrypt(b"Text"*64*1024, PUBLIC_KEY_PEM, enc, alg)
header = json.loads(base64url_decode(encrypted.split(b".")[0]))
with pytest.raises(JWEError):
actual = jwe.decrypt(encrypted, PRIVATE_KEY_PEM)
finally:
jose.constants.JWE_SIZE_LIMIT = old_limit

@pytest.mark.skipif(AESKey is None, reason="No AES backend")
def test_jwe_zip_with_excessive_data(self):
# Test that a fix for CVE-2024-33664 is in place.
enc = ALGORITHMS.A256CBC_HS512
alg = ALGORITHMS.RSA_OAEP_256
import jose.constants
old_limit = jose.constants.JWE_SIZE_LIMIT
try:
jose.constants.JWE_SIZE_LIMIT = 1024
encrypted = jwe.encrypt(b"Text"*64*1024, PUBLIC_KEY_PEM, enc, alg, zip=ZIPS.DEF)
assert len(encrypted) < jose.constants.JWE_SIZE_LIMIT
header = json.loads(base64url_decode(encrypted.split(b".")[0]))
with pytest.raises(JWEError):
actual = jwe.decrypt(encrypted, PRIVATE_KEY_PEM)
finally:
jose.constants.JWE_SIZE_LIMIT = old_limit
Loading