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 #353

Closed
wants to merge 2 commits into from
Closed
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

20 changes: 14 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 @@ -78,6 +78,10 @@ def decrypt(jwe_str, key):
"""
header, encoded_header, encrypted_key, iv, cipher_text, auth_tag = _jwe_compact_deserialize(jwe_str)

# Addresses CWE-2024-33664
if len(jwe_str) > JWE_SIZE_LIMIT:
raise JWEError(f"JWE size exceeds {JWE_SIZE_LIMIT} bytes")

# Verify that the implementation understands and can process all
# fields that it is required to support, whether required by this
# specification, by the algorithms being used, or by the "crit"
Expand Down Expand Up @@ -424,13 +428,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 +450,17 @@ 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)
# Addresses CWE-2024-33664
decompressor = zlib.decompressobj()
decompressed = decompressor.decompress(compressed, max_length=JWE_SIZE_LIMIT)
if decompressor.unconsumed_tail:
raise JWEError(f"Decompressed data exceeds {JWE_SIZE_LIMIT} bytes")
else:
raise NotImplementedError("ZIP {} is not implemented!")
raise NotImplementedError(f"ZIP {zip} is not implemented!")
return decompressed


Expand Down
35 changes: 33 additions & 2 deletions tests/test_jwe.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@

import jose.backends
from jose import jwe
from jose.constants import ALGORITHMS, ZIPS
from jose.exceptions import JWEParseError
from jose.constants import ALGORITHMS, ZIPS, JWE_SIZE_LIMIT
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,34 @@ 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):
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