Skip to content

Commit

Permalink
Fix for CVE-2024-33664. JWE limited to 250K (#352)
Browse files Browse the repository at this point in the history
* Fix for CVE-2024-33664. JWE limited to 250K

* Removed Py3.7 from Ubuntu latest. Linting on Py3.10

* Changed exception message

* Test now uses monkeypatch and checks error message text

* Update .github/workflows/ci.yml

* Update .github/workflows/ci.yml

* Update jose/jwe.py

* Update tests/test_jwe.py

* fmt

---------

Co-authored-by: Asher Foa <[email protected]>
  • Loading branch information
alistairwatts and asherf authored Feb 6, 2025
1 parent c9403b5 commit 8e1f521
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 9 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", "pypy3.9"]
python-version: ["3.8", "3.9", "3.10", "3.11", "pypy3.9"]
os: [ubuntu-latest, macos-latest, windows-latest]
exclude:
- os: macos-latest
Expand Down Expand Up @@ -56,7 +56,7 @@ jobs:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: 3.9
python-version: "3.10"
- name: Install dependencies
run: |
pip install -U setuptools
Expand Down
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, JWE_SIZE_LIMIT, ZIPS
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(f"JWE string {len(jwe_str)} bytes 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
27 changes: 26 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 JWEError, JWEParseError
from jose.jwk import AESKey, RSAKey
from jose.utils import base64url_decode

Expand Down Expand Up @@ -525,3 +525,28 @@ 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, monkeypatch):
enc = ALGORITHMS.A256CBC_HS512
alg = ALGORITHMS.RSA_OAEP_256
monkeypatch.setattr("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) as excinfo:
actual = jwe.decrypt(encrypted, PRIVATE_KEY_PEM)
assert "JWE string" in str(excinfo.value)
assert "bytes exceeds" in str(excinfo.value)

@pytest.mark.skipif(AESKey is None, reason="No AES backend")
def test_jwe_zip_with_excessive_data(self, monkeypatch):
# Test that a fix for CVE-2024-33664 is in place.
enc = ALGORITHMS.A256CBC_HS512
alg = ALGORITHMS.RSA_OAEP_256
monkeypatch.setattr("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) as excinfo:
actual = jwe.decrypt(encrypted, PRIVATE_KEY_PEM)
assert "Decompressed JWE string exceeds" in str(excinfo.value)

0 comments on commit 8e1f521

Please sign in to comment.