Control: tags 1133006 + patch
Control: tags 1133006 + pending

Dear maintainer,

I've prepared an NMU for python-jwcrypto (versioned as 1.5.6-1.1)
and uploaded it to DELAYED/2. Please feel free to tell me if I
should cancel it.

cu
Adrian
diffstat for python-jwcrypto-1.5.6 python-jwcrypto-1.5.6

 changelog                                                         |    7 
 patches/0001-Limit-max-plaintext-size-for-JWE-decompression.patch |  156 ++++++++++
 patches/series                                                    |    1 
 3 files changed, 164 insertions(+)

diff -Nru python-jwcrypto-1.5.6/debian/changelog python-jwcrypto-1.5.6/debian/changelog
--- python-jwcrypto-1.5.6/debian/changelog	2024-05-02 09:03:21.000000000 +0300
+++ python-jwcrypto-1.5.6/debian/changelog	2026-06-23 17:17:46.000000000 +0300
@@ -1,3 +1,10 @@
+python-jwcrypto (1.5.6-1.1) unstable; urgency=medium
+
+  * Non-maintainer upload.
+  * CVE-2026-39373: JWT bomb Attack in deserialize (Closes: #1133006)
+
+ -- Adrian Bunk <[email protected]>  Tue, 23 Jun 2026 17:17:46 +0300
+
 python-jwcrypto (1.5.6-1) unstable; urgency=medium
 
   * New upstream release.
diff -Nru python-jwcrypto-1.5.6/debian/patches/0001-Limit-max-plaintext-size-for-JWE-decompression.patch python-jwcrypto-1.5.6/debian/patches/0001-Limit-max-plaintext-size-for-JWE-decompression.patch
--- python-jwcrypto-1.5.6/debian/patches/0001-Limit-max-plaintext-size-for-JWE-decompression.patch	1970-01-01 02:00:00.000000000 +0200
+++ python-jwcrypto-1.5.6/debian/patches/0001-Limit-max-plaintext-size-for-JWE-decompression.patch	2026-06-23 17:17:46.000000000 +0300
@@ -0,0 +1,156 @@
+From 4c4740f2c8f94793f27c0bfc5689dc28d131da45 Mon Sep 17 00:00:00 2001
+From: Simo Sorce <[email protected]>
+Date: Mon, 6 Apr 2026 10:37:20 -0400
+Subject: Limit max plaintext size for JWE decompression
+
+This change introduces a maximum plaintext size limit (defaulting to 100MB)
+during JWE decryption and updates the decompression logic to enforce it safely
+using zlib.decompressobj. The decrypt method now accepts a max_plaintext
+parameter to allow overriding the default limit.
+
+This mitigates memory exhaustion and decompression bomb attacks when
+processing highly compressed malicious JWE payloads.
+
+Fixes CVE-2026-39373
+
+Signed-off-by: Simo Sorce <[email protected]>
+---
+ jwcrypto/jwe.py   | 27 +++++++++++++++++++++------
+ jwcrypto/tests.py | 34 ++++++++++++++++++++++++++--------
+ 2 files changed, 47 insertions(+), 14 deletions(-)
+
+diff --git a/jwcrypto/jwe.py b/jwcrypto/jwe.py
+index 5df500b..613699e 100644
+--- a/jwcrypto/jwe.py
++++ b/jwcrypto/jwe.py
+@@ -12,7 +12,8 @@ from jwcrypto.jwk import JWKSet
+ 
+ # Limit the amount of data we are willing to decompress by default.
+ default_max_compressed_size = 256 * 1024
+-
++# Limit the maximum plaintext size to 100MB by default.
++default_max_plaintext_size = 100 * 1024 * 1024
+ 
+ # RFC 7516 - 4.1
+ # name: (description, supported?)
+@@ -371,7 +372,7 @@ class JWE:
+         return data
+ 
+     # FIXME: allow to specify which algorithms to accept as valid
+-    def _decrypt(self, key, ppe):
++    def _decrypt(self, key, ppe, max_plaintext=default_max_plaintext_size):
+ 
+         jh = self._get_jose_header(ppe.get('header', None))
+ 
+@@ -429,19 +430,29 @@ class JWE:
+                 raise InvalidJWEData(
+                     'Compressed data exceeds maximum allowed'
+                     'size' + f' ({default_max_compressed_size})')
+-            self.plaintext = zlib.decompress(data, -zlib.MAX_WBITS)
++            do = zlib.decompressobj(wbits=-zlib.MAX_WBITS)
++            self.plaintext = do.decompress(data, max_plaintext)
++            if do.unconsumed_tail or not do.eof:
++                self.plaintext = None
++                raise InvalidJWEData(
++                    'Compressed data exceeds maximum allowed'
++                    'output size' + f' ({max_plaintext})')
+         elif compress is None:
+             self.plaintext = data
+         else:
+             raise ValueError('Unknown compression')
+ 
+-    def decrypt(self, key):
++    def decrypt(self, key, max_plaintext=0):
+         """Decrypt a JWE token.
+ 
+         :param key: The (:class:`jwcrypto.jwk.JWK`) decryption key.
+         :param key: A (:class:`jwcrypto.jwk.JWK`) decryption key,
+          or a (:class:`jwcrypto.jwk.JWKSet`) that contains a key indexed
+          by the 'kid' header or (deprecated) a string containing a password.
++        :param max_plaintext: Maximum plaintext size allowed, 0 means
++         the library default applies. Application writers are recommended
++         to set a limit here if they know what is the max plaintext size
++         for their application.
+ 
+         :raises InvalidJWEOperation: if the key is not a JWK object.
+         :raises InvalidJWEData: if the ciphertext can't be decrypted or
+@@ -449,6 +460,10 @@ class JWE:
+         :raises JWKeyNotFound: if key is a JWKSet and the key is not found.
+         """
+ 
++        self.plaintext = None
++        if max_plaintext == 0:
++            max_plaintext = default_max_plaintext_size
++
+         if 'ciphertext' not in self.objects:
+             raise InvalidJWEOperation("No available ciphertext")
+         self.decryptlog = []
+@@ -457,14 +472,14 @@ class JWE:
+         if 'recipients' in self.objects:
+             for rec in self.objects['recipients']:
+                 try:
+-                    self._decrypt(key, rec)
++                    self._decrypt(key, rec, max_plaintext=max_plaintext)
+                 except Exception as e:  # pylint: disable=broad-except
+                     if isinstance(e, JWKeyNotFound):
+                         missingkey = True
+                     self.decryptlog.append('Failed: [%s]' % repr(e))
+         else:
+             try:
+-                self._decrypt(key, self.objects)
++                self._decrypt(key, self.objects, max_plaintext=max_plaintext)
+             except Exception as e:  # pylint: disable=broad-except
+                 if isinstance(e, JWKeyNotFound):
+                     missingkey = True
+diff --git a/jwcrypto/tests.py b/jwcrypto/tests.py
+index 59049f8..d75761a 100644
+--- a/jwcrypto/tests.py
++++ b/jwcrypto/tests.py
+@@ -2124,18 +2124,36 @@ class ConformanceTests(unittest.TestCase):
+         enc = jwe.JWE(payload.encode('utf-8'),
+                       recipient=key,
+                       protected=protected_header).serialize(compact=True)
++        check = jwe.JWE()
++        check.deserialize(enc)
+         with self.assertRaises(jwe.InvalidJWEData):
+-            check = jwe.JWE()
+-            check.deserialize(enc)
+             check.decrypt(key)
+ 
+-        defmax = jwe.default_max_compressed_size
+-        jwe.default_max_compressed_size = 1000000000
+-        # ensure we can eraise the limit and decrypt
+-        check = jwe.JWE()
+-        check.deserialize(enc)
++        # raise the limit on compressed token size so we can decrypt
++        defcmax = jwe.default_max_compressed_size
++        jwe.default_max_compressed_size = 10 * 1024 * 1024
++
++        # this passes if we explicitly allow larger plaintext via API
++        check.decrypt(key, max_plaintext=1000000000)
++
++        # this will still fail because the max plaintext length clamps this
++        with self.assertRaises(jwe.InvalidJWEData):
++            check.decrypt(key)
++
++        # ensure that now this can work with changed defaults
++        defpmax = jwe.default_max_plaintext_size
++        jwe.default_max_plaintext_size = 1000000000
+         check.decrypt(key)
+-        jwe.default_max_compressed_size = defmax
++
++        # restore limits
++        jwe.default_max_compressed_size = defcmax
++
++        # check that this fails the max compressed header limits
++        with self.assertRaises(jwe.InvalidJWEData):
++            check.decrypt(key)
++
++        # restore plaintext limits
++        jwe.default_max_plaintext_size = defpmax
+ 
+ 
+ class JWATests(unittest.TestCase):
+-- 
+2.47.3
+
diff -Nru python-jwcrypto-1.5.6/debian/patches/series python-jwcrypto-1.5.6/debian/patches/series
--- python-jwcrypto-1.5.6/debian/patches/series	1970-01-01 02:00:00.000000000 +0200
+++ python-jwcrypto-1.5.6/debian/patches/series	2026-06-23 17:17:46.000000000 +0300
@@ -0,0 +1 @@
+0001-Limit-max-plaintext-size-for-JWE-decompression.patch

Reply via email to