Sync from SUSE:ALP:Source:Standard:1.0 python-python-jose revision 5c75de7a8f5df54fdec3def24093b464
This commit is contained in:
commit
af3cf74a7d
23
.gitattributes
vendored
Normal file
23
.gitattributes
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
## Default LFS
|
||||
*.7z filter=lfs diff=lfs merge=lfs -text
|
||||
*.bsp filter=lfs diff=lfs merge=lfs -text
|
||||
*.bz2 filter=lfs diff=lfs merge=lfs -text
|
||||
*.gem filter=lfs diff=lfs merge=lfs -text
|
||||
*.gz filter=lfs diff=lfs merge=lfs -text
|
||||
*.jar filter=lfs diff=lfs merge=lfs -text
|
||||
*.lz filter=lfs diff=lfs merge=lfs -text
|
||||
*.lzma filter=lfs diff=lfs merge=lfs -text
|
||||
*.obscpio filter=lfs diff=lfs merge=lfs -text
|
||||
*.oxt filter=lfs diff=lfs merge=lfs -text
|
||||
*.pdf filter=lfs diff=lfs merge=lfs -text
|
||||
*.png filter=lfs diff=lfs merge=lfs -text
|
||||
*.rpm filter=lfs diff=lfs merge=lfs -text
|
||||
*.tbz filter=lfs diff=lfs merge=lfs -text
|
||||
*.tbz2 filter=lfs diff=lfs merge=lfs -text
|
||||
*.tgz filter=lfs diff=lfs merge=lfs -text
|
||||
*.ttf filter=lfs diff=lfs merge=lfs -text
|
||||
*.txz filter=lfs diff=lfs merge=lfs -text
|
||||
*.whl filter=lfs diff=lfs merge=lfs -text
|
||||
*.xz filter=lfs diff=lfs merge=lfs -text
|
||||
*.zip filter=lfs diff=lfs merge=lfs -text
|
||||
*.zst filter=lfs diff=lfs merge=lfs -text
|
599
CVE-2024-33663.patch
Normal file
599
CVE-2024-33663.patch
Normal file
@ -0,0 +1,599 @@
|
||||
From 34bd82c43ea31da5b9deaa25ff591905a180bdf7 Mon Sep 17 00:00:00 2001
|
||||
From: Daniel Garcia Moreno <daniel.garcia@suse.com>
|
||||
Date: Thu, 2 May 2024 09:29:54 +0200
|
||||
Subject: [PATCH 1/4] Improve asymmetric key check in CryptographyHMACKey
|
||||
|
||||
This change should fix https://github.com/mpdavis/python-jose/issues/346
|
||||
security issue.
|
||||
|
||||
The code is based on pyjwt change:
|
||||
https://github.com/jpadilla/pyjwt/commit/9c528670c455b8d948aff95ed50e22940d1ad3fc
|
||||
---
|
||||
jose/backends/cryptography_backend.py | 72 ++++++++++++++++++++++++---
|
||||
tests/test_jwt.py | 35 ++++++++++++-
|
||||
2 files changed, 98 insertions(+), 9 deletions(-)
|
||||
|
||||
Index: python-jose-3.3.0/jose/backends/cryptography_backend.py
|
||||
===================================================================
|
||||
--- python-jose-3.3.0.orig/jose/backends/cryptography_backend.py
|
||||
+++ python-jose-3.3.0/jose/backends/cryptography_backend.py
|
||||
@@ -17,6 +17,7 @@ from cryptography.x509 import load_pem_x
|
||||
from ..constants import ALGORITHMS
|
||||
from ..exceptions import JWEError, JWKError
|
||||
from ..utils import base64_to_long, base64url_decode, base64url_encode, ensure_binary, long_to_base64
|
||||
+from ..utils import is_pem_format, is_ssh_key
|
||||
from .base import Key
|
||||
|
||||
_binding = None
|
||||
@@ -552,14 +553,7 @@ class CryptographyHMACKey(Key):
|
||||
if isinstance(key, str):
|
||||
key = key.encode("utf-8")
|
||||
|
||||
- invalid_strings = [
|
||||
- b"-----BEGIN PUBLIC KEY-----",
|
||||
- b"-----BEGIN RSA PUBLIC KEY-----",
|
||||
- b"-----BEGIN CERTIFICATE-----",
|
||||
- b"ssh-rsa",
|
||||
- ]
|
||||
-
|
||||
- if any(string_value in key for string_value in invalid_strings):
|
||||
+ if is_pem_format(key) or is_ssh_key(key):
|
||||
raise JWKError(
|
||||
"The specified key is an asymmetric key or x509 certificate and"
|
||||
" should not be used as an HMAC secret."
|
||||
Index: python-jose-3.3.0/tests/test_jwt.py
|
||||
===================================================================
|
||||
--- python-jose-3.3.0.orig/tests/test_jwt.py
|
||||
+++ python-jose-3.3.0/tests/test_jwt.py
|
||||
@@ -5,7 +5,8 @@ from datetime import datetime, timedelta
|
||||
import pytest
|
||||
|
||||
from jose import jws, jwt
|
||||
-from jose.exceptions import JWTError
|
||||
+from jose.constants import ALGORITHMS
|
||||
+from jose.exceptions import JWTError, JWKError
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@@ -56,7 +57,7 @@ class TestJWT:
|
||||
],
|
||||
)
|
||||
def test_numeric_key(self, key, token):
|
||||
- token_info = jwt.decode(token, key)
|
||||
+ token_info = jwt.decode(token, key, algorithms=ALGORITHMS.SUPPORTED)
|
||||
assert token_info == {"name": "test"}
|
||||
|
||||
def test_invalid_claims_json(self):
|
||||
@@ -108,7 +109,7 @@ class TestJWT:
|
||||
|
||||
def test_non_default_headers(self, claims, key, headers):
|
||||
encoded = jwt.encode(claims, key, headers=headers)
|
||||
- decoded = jwt.decode(encoded, key)
|
||||
+ decoded = jwt.decode(encoded, key, algorithms=ALGORITHMS.HS256)
|
||||
assert claims == decoded
|
||||
all_headers = jwt.get_unverified_headers(encoded)
|
||||
for k, v in headers.items():
|
||||
@@ -161,7 +162,7 @@ class TestJWT:
|
||||
|
||||
token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9" ".eyJhIjoiYiJ9" ".jiMyrsmD8AoHWeQgmxZ5yq8z0lXS67_QGs52AzC8Ru8"
|
||||
|
||||
- decoded = jwt.decode(token, key)
|
||||
+ decoded = jwt.decode(token, key, algorithms=ALGORITHMS.SUPPORTED)
|
||||
|
||||
assert decoded == claims
|
||||
|
||||
@@ -193,7 +194,7 @@ class TestJWT:
|
||||
options = {"leeway": leeway}
|
||||
|
||||
token = jwt.encode(claims, key)
|
||||
- jwt.decode(token, key, options=options)
|
||||
+ jwt.decode(token, key, options=options, algorithms=ALGORITHMS.HS256)
|
||||
|
||||
def test_iat_not_int(self, key):
|
||||
|
||||
@@ -202,7 +203,7 @@ class TestJWT:
|
||||
token = jwt.encode(claims, key)
|
||||
|
||||
with pytest.raises(JWTError):
|
||||
- jwt.decode(token, key)
|
||||
+ jwt.decode(token, key, algorithms=ALGORITHMS.HS256)
|
||||
|
||||
def test_nbf_not_int(self, key):
|
||||
|
||||
@@ -211,7 +212,7 @@ class TestJWT:
|
||||
token = jwt.encode(claims, key)
|
||||
|
||||
with pytest.raises(JWTError):
|
||||
- jwt.decode(token, key)
|
||||
+ jwt.decode(token, key, algorithms=ALGORITHMS.HS256)
|
||||
|
||||
def test_nbf_datetime(self, key):
|
||||
|
||||
@@ -220,7 +221,7 @@ class TestJWT:
|
||||
claims = {"nbf": nbf}
|
||||
|
||||
token = jwt.encode(claims, key)
|
||||
- jwt.decode(token, key)
|
||||
+ jwt.decode(token, key, algorithms=ALGORITHMS.HS256)
|
||||
|
||||
def test_nbf_with_leeway(self, key):
|
||||
|
||||
@@ -233,7 +234,7 @@ class TestJWT:
|
||||
options = {"leeway": 10}
|
||||
|
||||
token = jwt.encode(claims, key)
|
||||
- jwt.decode(token, key, options=options)
|
||||
+ jwt.decode(token, key, options=options, algorithms=ALGORITHMS.HS256)
|
||||
|
||||
def test_nbf_in_future(self, key):
|
||||
|
||||
@@ -244,7 +245,7 @@ class TestJWT:
|
||||
token = jwt.encode(claims, key)
|
||||
|
||||
with pytest.raises(JWTError):
|
||||
- jwt.decode(token, key)
|
||||
+ jwt.decode(token, key, algorithms=ALGORITHMS.HS256)
|
||||
|
||||
def test_nbf_skip(self, key):
|
||||
|
||||
@@ -255,11 +256,11 @@ class TestJWT:
|
||||
token = jwt.encode(claims, key)
|
||||
|
||||
with pytest.raises(JWTError):
|
||||
- jwt.decode(token, key)
|
||||
+ jwt.decode(token, key, algorithms=ALGORITHMS.HS256)
|
||||
|
||||
options = {"verify_nbf": False}
|
||||
|
||||
- jwt.decode(token, key, options=options)
|
||||
+ jwt.decode(token, key, options=options, algorithms=ALGORITHMS.HS256)
|
||||
|
||||
def test_exp_not_int(self, key):
|
||||
|
||||
@@ -268,7 +269,7 @@ class TestJWT:
|
||||
token = jwt.encode(claims, key)
|
||||
|
||||
with pytest.raises(JWTError):
|
||||
- jwt.decode(token, key)
|
||||
+ jwt.decode(token, key, algorithms=ALGORITHMS.HS256)
|
||||
|
||||
def test_exp_datetime(self, key):
|
||||
|
||||
@@ -277,7 +278,7 @@ class TestJWT:
|
||||
claims = {"exp": exp}
|
||||
|
||||
token = jwt.encode(claims, key)
|
||||
- jwt.decode(token, key)
|
||||
+ jwt.decode(token, key, algorithms=ALGORITHMS.HS256)
|
||||
|
||||
def test_exp_with_leeway(self, key):
|
||||
|
||||
@@ -290,7 +291,7 @@ class TestJWT:
|
||||
options = {"leeway": 10}
|
||||
|
||||
token = jwt.encode(claims, key)
|
||||
- jwt.decode(token, key, options=options)
|
||||
+ jwt.decode(token, key, options=options, algorithms=ALGORITHMS.HS256)
|
||||
|
||||
def test_exp_in_past(self, key):
|
||||
|
||||
@@ -301,7 +302,7 @@ class TestJWT:
|
||||
token = jwt.encode(claims, key)
|
||||
|
||||
with pytest.raises(JWTError):
|
||||
- jwt.decode(token, key)
|
||||
+ jwt.decode(token, key, algorithms=ALGORITHMS.HS256)
|
||||
|
||||
def test_exp_skip(self, key):
|
||||
|
||||
@@ -312,11 +313,11 @@ class TestJWT:
|
||||
token = jwt.encode(claims, key)
|
||||
|
||||
with pytest.raises(JWTError):
|
||||
- jwt.decode(token, key)
|
||||
+ jwt.decode(token, key, algorithms=ALGORITHMS.HS256)
|
||||
|
||||
options = {"verify_exp": False}
|
||||
|
||||
- jwt.decode(token, key, options=options)
|
||||
+ jwt.decode(token, key, options=options, algorithms=ALGORITHMS.HS256)
|
||||
|
||||
def test_aud_string(self, key):
|
||||
|
||||
@@ -325,7 +326,7 @@ class TestJWT:
|
||||
claims = {"aud": aud}
|
||||
|
||||
token = jwt.encode(claims, key)
|
||||
- jwt.decode(token, key, audience=aud)
|
||||
+ jwt.decode(token, key, audience=aud, algorithms=ALGORITHMS.HS256)
|
||||
|
||||
def test_aud_list(self, key):
|
||||
|
||||
@@ -334,7 +335,7 @@ class TestJWT:
|
||||
claims = {"aud": [aud]}
|
||||
|
||||
token = jwt.encode(claims, key)
|
||||
- jwt.decode(token, key, audience=aud)
|
||||
+ jwt.decode(token, key, audience=aud, algorithms=ALGORITHMS.HS256)
|
||||
|
||||
def test_aud_list_multiple(self, key):
|
||||
|
||||
@@ -343,7 +344,7 @@ class TestJWT:
|
||||
claims = {"aud": [aud, "another"]}
|
||||
|
||||
token = jwt.encode(claims, key)
|
||||
- jwt.decode(token, key, audience=aud)
|
||||
+ jwt.decode(token, key, audience=aud, algorithms=ALGORITHMS.HS256)
|
||||
|
||||
def test_aud_list_is_strings(self, key):
|
||||
|
||||
@@ -353,7 +354,7 @@ class TestJWT:
|
||||
|
||||
token = jwt.encode(claims, key)
|
||||
with pytest.raises(JWTError):
|
||||
- jwt.decode(token, key, audience=aud)
|
||||
+ jwt.decode(token, key, audience=aud, algorithms=ALGORITHMS.HS256)
|
||||
|
||||
def test_aud_case_sensitive(self, key):
|
||||
|
||||
@@ -363,14 +364,14 @@ class TestJWT:
|
||||
|
||||
token = jwt.encode(claims, key)
|
||||
with pytest.raises(JWTError):
|
||||
- jwt.decode(token, key, audience="AUDIENCE")
|
||||
+ jwt.decode(token, key, audience="AUDIENCE", algorithms=ALGORITHMS.HS256)
|
||||
|
||||
def test_aud_empty_claim(self, claims, key):
|
||||
|
||||
aud = "audience"
|
||||
|
||||
token = jwt.encode(claims, key)
|
||||
- jwt.decode(token, key, audience=aud)
|
||||
+ jwt.decode(token, key, audience=aud, algorithms=ALGORITHMS.HS256)
|
||||
|
||||
def test_aud_not_string_or_list(self, key):
|
||||
|
||||
@@ -380,7 +381,7 @@ class TestJWT:
|
||||
|
||||
token = jwt.encode(claims, key)
|
||||
with pytest.raises(JWTError):
|
||||
- jwt.decode(token, key)
|
||||
+ jwt.decode(token, key, algorithms=ALGORITHMS.HS256)
|
||||
|
||||
def test_aud_given_number(self, key):
|
||||
|
||||
@@ -390,7 +391,7 @@ class TestJWT:
|
||||
|
||||
token = jwt.encode(claims, key)
|
||||
with pytest.raises(JWTError):
|
||||
- jwt.decode(token, key, audience=1)
|
||||
+ jwt.decode(token, key, audience=1, algorithms=ALGORITHMS.HS256)
|
||||
|
||||
def test_iss_string(self, key):
|
||||
|
||||
@@ -399,7 +400,7 @@ class TestJWT:
|
||||
claims = {"iss": iss}
|
||||
|
||||
token = jwt.encode(claims, key)
|
||||
- jwt.decode(token, key, issuer=iss)
|
||||
+ jwt.decode(token, key, issuer=iss, algorithms=ALGORITHMS.HS256)
|
||||
|
||||
def test_iss_list(self, key):
|
||||
|
||||
@@ -408,7 +409,7 @@ class TestJWT:
|
||||
claims = {"iss": iss}
|
||||
|
||||
token = jwt.encode(claims, key)
|
||||
- jwt.decode(token, key, issuer=["https://issuer", "issuer"])
|
||||
+ jwt.decode(token, key, issuer=["https://issuer", "issuer"], algorithms=ALGORITHMS.HS256)
|
||||
|
||||
def test_iss_tuple(self, key):
|
||||
|
||||
@@ -417,7 +418,7 @@ class TestJWT:
|
||||
claims = {"iss": iss}
|
||||
|
||||
token = jwt.encode(claims, key)
|
||||
- jwt.decode(token, key, issuer=("https://issuer", "issuer"))
|
||||
+ jwt.decode(token, key, issuer=("https://issuer", "issuer"), algorithms=ALGORITHMS.HS256)
|
||||
|
||||
def test_iss_invalid(self, key):
|
||||
|
||||
@@ -427,7 +428,7 @@ class TestJWT:
|
||||
|
||||
token = jwt.encode(claims, key)
|
||||
with pytest.raises(JWTError):
|
||||
- jwt.decode(token, key, issuer="another")
|
||||
+ jwt.decode(token, key, issuer="another", algorithms=ALGORITHMS.HS256)
|
||||
|
||||
def test_sub_string(self, key):
|
||||
|
||||
@@ -436,7 +437,7 @@ class TestJWT:
|
||||
claims = {"sub": sub}
|
||||
|
||||
token = jwt.encode(claims, key)
|
||||
- jwt.decode(token, key)
|
||||
+ jwt.decode(token, key, algorithms=ALGORITHMS.HS256)
|
||||
|
||||
def test_sub_invalid(self, key):
|
||||
|
||||
@@ -446,7 +447,7 @@ class TestJWT:
|
||||
|
||||
token = jwt.encode(claims, key)
|
||||
with pytest.raises(JWTError):
|
||||
- jwt.decode(token, key)
|
||||
+ jwt.decode(token, key, algorithms=ALGORITHMS.HS256)
|
||||
|
||||
def test_sub_correct(self, key):
|
||||
|
||||
@@ -455,7 +456,7 @@ class TestJWT:
|
||||
claims = {"sub": sub}
|
||||
|
||||
token = jwt.encode(claims, key)
|
||||
- jwt.decode(token, key, subject=sub)
|
||||
+ jwt.decode(token, key, subject=sub, algorithms=ALGORITHMS.HS256)
|
||||
|
||||
def test_sub_incorrect(self, key):
|
||||
|
||||
@@ -465,7 +466,7 @@ class TestJWT:
|
||||
|
||||
token = jwt.encode(claims, key)
|
||||
with pytest.raises(JWTError):
|
||||
- jwt.decode(token, key, subject="another")
|
||||
+ jwt.decode(token, key, subject="another", algorithms=ALGORITHMS.HS256)
|
||||
|
||||
def test_jti_string(self, key):
|
||||
|
||||
@@ -474,7 +475,7 @@ class TestJWT:
|
||||
claims = {"jti": jti}
|
||||
|
||||
token = jwt.encode(claims, key)
|
||||
- jwt.decode(token, key)
|
||||
+ jwt.decode(token, key, algorithms=ALGORITHMS.HS256)
|
||||
|
||||
def test_jti_invalid(self, key):
|
||||
|
||||
@@ -484,33 +485,33 @@ class TestJWT:
|
||||
|
||||
token = jwt.encode(claims, key)
|
||||
with pytest.raises(JWTError):
|
||||
- jwt.decode(token, key)
|
||||
+ jwt.decode(token, key, algorithms=ALGORITHMS.HS256)
|
||||
|
||||
def test_at_hash(self, claims, key):
|
||||
access_token = "<ACCESS_TOKEN>"
|
||||
token = jwt.encode(claims, key, access_token=access_token)
|
||||
- payload = jwt.decode(token, key, access_token=access_token)
|
||||
+ payload = jwt.decode(token, key, access_token=access_token, algorithms=ALGORITHMS.HS256)
|
||||
assert "at_hash" in payload
|
||||
|
||||
def test_at_hash_invalid(self, claims, key):
|
||||
token = jwt.encode(claims, key, access_token="<ACCESS_TOKEN>")
|
||||
with pytest.raises(JWTError):
|
||||
- jwt.decode(token, key, access_token="<OTHER_TOKEN>")
|
||||
+ jwt.decode(token, key, access_token="<OTHER_TOKEN>", algorithms=ALGORITHMS.HS256)
|
||||
|
||||
def test_at_hash_missing_access_token(self, claims, key):
|
||||
token = jwt.encode(claims, key, access_token="<ACCESS_TOKEN>")
|
||||
with pytest.raises(JWTError):
|
||||
- jwt.decode(token, key)
|
||||
+ jwt.decode(token, key, algorithms=ALGORITHMS.HS256)
|
||||
|
||||
def test_at_hash_missing_claim(self, claims, key):
|
||||
token = jwt.encode(claims, key)
|
||||
- payload = jwt.decode(token, key, access_token="<ACCESS_TOKEN>")
|
||||
+ payload = jwt.decode(token, key, access_token="<ACCESS_TOKEN>", algorithms=ALGORITHMS.HS256)
|
||||
assert "at_hash" not in payload
|
||||
|
||||
def test_at_hash_unable_to_calculate(self, claims, key):
|
||||
token = jwt.encode(claims, key, access_token="<ACCESS_TOKEN>")
|
||||
with pytest.raises(JWTError):
|
||||
- jwt.decode(token, key, access_token="\xe2")
|
||||
+ jwt.decode(token, key, access_token="\xe2", algorithms=ALGORITHMS.HS256)
|
||||
|
||||
def test_bad_claims(self):
|
||||
bad_token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.iOJ5SiNfaNO_pa2J4Umtb3b3zmk5C18-mhTCVNsjnck"
|
||||
@@ -548,9 +549,48 @@ class TestJWT:
|
||||
|
||||
token = jwt.encode(claims, key)
|
||||
with pytest.raises(JWTError):
|
||||
- jwt.decode(token, key, options=options, audience=str(value))
|
||||
+ jwt.decode(token, key, options=options, audience=str(value), algorithms=ALGORITHMS.HS256)
|
||||
|
||||
new_claims = dict(claims)
|
||||
new_claims[claim] = value
|
||||
token = jwt.encode(new_claims, key)
|
||||
- jwt.decode(token, key, options=options, audience=str(value))
|
||||
+ jwt.decode(token, key, options=options, audience=str(value), algorithms=ALGORITHMS.HS256)
|
||||
+
|
||||
+ def test_CVE_2024_33663(self):
|
||||
+ """Test based on https://github.com/mpdavis/python-jose/issues/346"""
|
||||
+ try:
|
||||
+ from Crypto.PublicKey import ECC
|
||||
+ from Crypto.Hash import HMAC, SHA256
|
||||
+ except ModuleNotFoundError:
|
||||
+ pytest.skip("pycryptodome module not installed")
|
||||
+
|
||||
+ # ----- SETUP -----
|
||||
+ # generate an asymmetric ECC keypair
|
||||
+ # !! signing should only be possible with the private key !!
|
||||
+ KEY = ECC.generate(curve='P-256')
|
||||
+
|
||||
+ # PUBLIC KEY, AVAILABLE TO USER
|
||||
+ # CAN BE RECOVERED THROUGH E.G. PUBKEY RECOVERY WITH TWO SIGNATURES:
|
||||
+ # https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm#Public_key_recovery
|
||||
+ # https://github.com/FlorianPicca/JWT-Key-Recovery
|
||||
+ PUBKEY = KEY.public_key().export_key(format='OpenSSH').encode()
|
||||
+
|
||||
+ # ---- CLIENT SIDE -----
|
||||
+ # without knowing the private key, a valid token can be constructed
|
||||
+ # YIKES!!
|
||||
+
|
||||
+ b64 = lambda x:base64.urlsafe_b64encode(x).replace(b'=',b'')
|
||||
+ payload = b64(b'{"alg":"HS256"}') + b'.' + b64(b'{"pwned":true}')
|
||||
+ hasher = HMAC.new(PUBKEY, digestmod=SHA256)
|
||||
+ hasher.update(payload)
|
||||
+ evil_token = payload + b'.' + b64(hasher.digest())
|
||||
+
|
||||
+ # ---- SERVER SIDE -----
|
||||
+ # verify and decode the token using the public key, as is custom
|
||||
+ # algorithm field is left unspecified
|
||||
+ # but the library will happily still verify without warning, trusting the user-controlled alg field of the token header
|
||||
+ with pytest.raises(JWKError):
|
||||
+ data = jwt.decode(evil_token, PUBKEY, algorithms=ALGORITHMS.HS256)
|
||||
+
|
||||
+ with pytest.raises(JWTError, match='.*required.*"algorithms".*'):
|
||||
+ data = jwt.decode(evil_token, PUBKEY)
|
||||
Index: python-jose-3.3.0/jose/jwt.py
|
||||
===================================================================
|
||||
--- python-jose-3.3.0.orig/jose/jwt.py
|
||||
+++ python-jose-3.3.0/jose/jwt.py
|
||||
@@ -138,6 +138,14 @@ def decode(token, key, algorithms=None,
|
||||
|
||||
verify_signature = defaults.get("verify_signature", True)
|
||||
|
||||
+ # Forbid the usage of the jwt.decode without alogrightms parameter
|
||||
+ # See https://github.com/mpdavis/python-jose/issues/346 for more
|
||||
+ # information CVE-2024-33663
|
||||
+ if verify_signature and algorithms is None:
|
||||
+ raise JWTError("It is required that you pass in a value for "
|
||||
+ 'the "algorithms" argument when calling '
|
||||
+ "decode().")
|
||||
+
|
||||
try:
|
||||
payload = jws.verify(token, key, algorithms, verify=verify_signature)
|
||||
except JWSError as e:
|
||||
Index: python-jose-3.3.0/jose/backends/native.py
|
||||
===================================================================
|
||||
--- python-jose-3.3.0.orig/jose/backends/native.py
|
||||
+++ python-jose-3.3.0/jose/backends/native.py
|
||||
@@ -6,6 +6,7 @@ from jose.backends.base import Key
|
||||
from jose.constants import ALGORITHMS
|
||||
from jose.exceptions import JWKError
|
||||
from jose.utils import base64url_decode, base64url_encode
|
||||
+from jose.utils import is_pem_format, is_ssh_key
|
||||
|
||||
|
||||
def get_random_bytes(num_bytes):
|
||||
@@ -36,14 +37,7 @@ class HMACKey(Key):
|
||||
if isinstance(key, str):
|
||||
key = key.encode("utf-8")
|
||||
|
||||
- invalid_strings = [
|
||||
- b"-----BEGIN PUBLIC KEY-----",
|
||||
- b"-----BEGIN RSA PUBLIC KEY-----",
|
||||
- b"-----BEGIN CERTIFICATE-----",
|
||||
- b"ssh-rsa",
|
||||
- ]
|
||||
-
|
||||
- if any(string_value in key for string_value in invalid_strings):
|
||||
+ if is_pem_format(key) or is_ssh_key(key):
|
||||
raise JWKError(
|
||||
"The specified key is an asymmetric key or x509 certificate and"
|
||||
" should not be used as an HMAC secret."
|
||||
Index: python-jose-3.3.0/jose/utils.py
|
||||
===================================================================
|
||||
--- python-jose-3.3.0.orig/jose/utils.py
|
||||
+++ python-jose-3.3.0/jose/utils.py
|
||||
@@ -1,3 +1,4 @@
|
||||
+import re
|
||||
import base64
|
||||
import struct
|
||||
|
||||
@@ -106,3 +107,75 @@ def ensure_binary(s):
|
||||
if isinstance(s, str):
|
||||
return s.encode("utf-8", "strict")
|
||||
raise TypeError(f"not expecting type '{type(s)}'")
|
||||
+
|
||||
+
|
||||
+# Based on https://github.com/jpadilla/pyjwt/commit/9c528670c455b8d948aff95ed50e22940d1ad3fc
|
||||
+# Based on https://github.com/hynek/pem/blob/7ad94db26b0bc21d10953f5dbad3acfdfacf57aa/src/pem/_core.py#L224-L252
|
||||
+_PEMS = {
|
||||
+ b"CERTIFICATE",
|
||||
+ b"TRUSTED CERTIFICATE",
|
||||
+ b"PRIVATE KEY",
|
||||
+ b"PUBLIC KEY",
|
||||
+ b"ENCRYPTED PRIVATE KEY",
|
||||
+ b"OPENSSH PRIVATE KEY",
|
||||
+ b"DSA PRIVATE KEY",
|
||||
+ b"RSA PRIVATE KEY",
|
||||
+ b"RSA PUBLIC KEY",
|
||||
+ b"EC PRIVATE KEY",
|
||||
+ b"DH PARAMETERS",
|
||||
+ b"NEW CERTIFICATE REQUEST",
|
||||
+ b"CERTIFICATE REQUEST",
|
||||
+ b"SSH2 PUBLIC KEY",
|
||||
+ b"SSH2 ENCRYPTED PRIVATE KEY",
|
||||
+ b"X509 CRL",
|
||||
+}
|
||||
+
|
||||
+
|
||||
+_PEM_RE = re.compile(
|
||||
+ b"----[- ]BEGIN ("
|
||||
+ + b"|".join(_PEMS)
|
||||
+ + b""")[- ]----\r?
|
||||
+.+?\r?
|
||||
+----[- ]END \\1[- ]----\r?\n?""",
|
||||
+ re.DOTALL,
|
||||
+)
|
||||
+
|
||||
+
|
||||
+def is_pem_format(key):
|
||||
+ """
|
||||
+ Return True if the key is PEM format
|
||||
+ This function uses the list of valid PEM headers defined in
|
||||
+ _PEMS dict.
|
||||
+ """
|
||||
+ return bool(_PEM_RE.search(key))
|
||||
+
|
||||
+
|
||||
+# Based on https://github.com/pyca/cryptography/blob/bcb70852d577b3f490f015378c75cba74986297b/src/cryptography/hazmat/primitives/serialization/ssh.py#L40-L46
|
||||
+_CERT_SUFFIX = b"-cert-v01@openssh.com"
|
||||
+_SSH_PUBKEY_RC = re.compile(br"\A(\S+)[ \t]+(\S+)")
|
||||
+_SSH_KEY_FORMATS = [
|
||||
+ b"ssh-ed25519",
|
||||
+ b"ssh-rsa",
|
||||
+ b"ssh-dss",
|
||||
+ b"ecdsa-sha2-nistp256",
|
||||
+ b"ecdsa-sha2-nistp384",
|
||||
+ b"ecdsa-sha2-nistp521",
|
||||
+]
|
||||
+
|
||||
+
|
||||
+def is_ssh_key(key):
|
||||
+ """
|
||||
+ Return True if the key is a SSH key
|
||||
+ This function uses the list of valid SSH key format defined in
|
||||
+ _SSH_KEY_FORMATS dict.
|
||||
+ """
|
||||
+ if any(string_value in key for string_value in _SSH_KEY_FORMATS):
|
||||
+ return True
|
||||
+
|
||||
+ ssh_pubkey_match = _SSH_PUBKEY_RC.match(key)
|
||||
+ if ssh_pubkey_match:
|
||||
+ key_type = ssh_pubkey_match.group(1)
|
||||
+ if _CERT_SUFFIX == key_type[-len(_CERT_SUFFIX) :]:
|
||||
+ return True
|
||||
+
|
||||
+ return False
|
||||
Index: python-jose-3.3.0/tests/algorithms/test_HMAC.py
|
||||
===================================================================
|
||||
--- python-jose-3.3.0.orig/tests/algorithms/test_HMAC.py
|
||||
+++ python-jose-3.3.0/tests/algorithms/test_HMAC.py
|
||||
@@ -14,14 +14,17 @@ class TestHMACAlgorithm:
|
||||
|
||||
def test_RSA_key(self):
|
||||
key = "-----BEGIN PUBLIC KEY-----"
|
||||
+ key += "\n\n\n-----END PUBLIC KEY-----"
|
||||
with pytest.raises(JOSEError):
|
||||
HMACKey(key, ALGORITHMS.HS256)
|
||||
|
||||
key = "-----BEGIN RSA PUBLIC KEY-----"
|
||||
+ key += "\n\n\n-----END RSA PUBLIC KEY-----"
|
||||
with pytest.raises(JOSEError):
|
||||
HMACKey(key, ALGORITHMS.HS256)
|
||||
|
||||
key = "-----BEGIN CERTIFICATE-----"
|
||||
+ key += "\n\n\n-----END CERTIFICATE-----"
|
||||
with pytest.raises(JOSEError):
|
||||
HMACKey(key, ALGORITHMS.HS256)
|
||||
|
135
CVE-2024-33664.patch
Normal file
135
CVE-2024-33664.patch
Normal file
@ -0,0 +1,135 @@
|
||||
From ff3357d9f91b93bc957aac9bc5a447c5c0bb74da Mon Sep 17 00:00:00 2001
|
||||
From: "alistair.watts@groupbc.com" <alistair.watts@groupbc.com>
|
||||
Date: Tue, 7 May 2024 14:50:53 +0100
|
||||
Subject: [PATCH] Fix for CVE-2024-33664. JWE limited to 250K
|
||||
|
||||
---
|
||||
jose/constants.py | 2 ++
|
||||
jose/jwe.py | 24 ++++++++++++++++++------
|
||||
tests/test_jwe.py | 34 +++++++++++++++++++++++++++++++++-
|
||||
3 files changed, 53 insertions(+), 7 deletions(-)
|
||||
|
||||
diff --git a/jose/constants.py b/jose/constants.py
|
||||
index ab4d74d3..58787d46 100644
|
||||
--- a/jose/constants.py
|
||||
+++ b/jose/constants.py
|
||||
@@ -96,3 +96,5 @@ class Zips:
|
||||
|
||||
|
||||
ZIPS = Zips()
|
||||
+
|
||||
+JWE_SIZE_LIMIT = 250 * 1024
|
||||
diff --git a/jose/jwe.py b/jose/jwe.py
|
||||
index 2c387ff4..04923873 100644
|
||||
--- a/jose/jwe.py
|
||||
+++ b/jose/jwe.py
|
||||
@@ -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
|
||||
|
||||
@@ -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")
|
||||
+
|
||||
header, encoded_header, encrypted_key, iv, cipher_text, auth_tag = _jwe_compact_deserialize(jwe_str)
|
||||
|
||||
# Verify that the implementation understands and can process all
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
diff --git a/tests/test_jwe.py b/tests/test_jwe.py
|
||||
index f089d565..8c5ff387 100644
|
||||
--- a/tests/test_jwe.py
|
||||
+++ b/tests/test_jwe.py
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
4
_multibuild
Normal file
4
_multibuild
Normal file
@ -0,0 +1,4 @@
|
||||
<multibuild>
|
||||
<flavor>test-backend-cryptography</flavor>
|
||||
<flavor>test-backend-native</flavor>
|
||||
</multibuild>
|
60
fix-tests-ecdsa-019.patch
Normal file
60
fix-tests-ecdsa-019.patch
Normal file
@ -0,0 +1,60 @@
|
||||
From ec5c62249b4f67b15376d3cbc96d2b1d272d0552 Mon Sep 17 00:00:00 2001
|
||||
From: Daniel Garcia Moreno <daniel.garcia@suse.com>
|
||||
Date: Thu, 2 May 2024 18:47:43 +0200
|
||||
Subject: [PATCH] test: Fix tests with ecdsa 0.19.0
|
||||
|
||||
Fix https://github.com/mpdavis/python-jose/issues/348
|
||||
---
|
||||
tests/algorithms/test_EC_compat.py | 8 ++++----
|
||||
1 file changed, 4 insertions(+), 4 deletions(-)
|
||||
|
||||
Index: python-jose-3.3.0/tests/algorithms/test_EC_compat.py
|
||||
===================================================================
|
||||
--- python-jose-3.3.0.orig/tests/algorithms/test_EC_compat.py
|
||||
+++ python-jose-3.3.0/tests/algorithms/test_EC_compat.py
|
||||
@@ -37,7 +37,7 @@ class TestBackendEcdsaCompatibility:
|
||||
key = BackendFrom(private_key, ALGORITHMS.ES256)
|
||||
key2 = BackendTo(private_key, ALGORITHMS.ES256)
|
||||
|
||||
- assert key.public_key().to_pem().strip() == key2.public_key().to_pem().strip()
|
||||
+ assert key.public_key().to_pem().strip().replace(b"\n", b"") == key2.public_key().to_pem().strip().replace(b"\n", b"")
|
||||
|
||||
@pytest.mark.parametrize("BackendFrom", [ECDSAECKey, CryptographyECKey])
|
||||
@pytest.mark.parametrize("BackendTo", [ECDSAECKey, CryptographyECKey])
|
||||
@@ -45,7 +45,7 @@ class TestBackendEcdsaCompatibility:
|
||||
key = BackendFrom(private_key, ALGORITHMS.ES256)
|
||||
key2 = BackendTo(private_key, ALGORITHMS.ES256)
|
||||
|
||||
- assert key.to_pem().strip() == key2.to_pem().strip()
|
||||
+ assert key.to_pem().strip().replace(b"\n", b"") == key2.to_pem().strip().replace(b"\n", b"")
|
||||
|
||||
@pytest.mark.parametrize("BackendFrom", [ECDSAECKey, CryptographyECKey])
|
||||
@pytest.mark.parametrize("BackendTo", [ECDSAECKey, CryptographyECKey])
|
||||
@@ -57,7 +57,7 @@ class TestBackendEcdsaCompatibility:
|
||||
|
||||
pub_target = BackendTo(pub_pem_source, ALGORITHMS.ES256)
|
||||
|
||||
- assert pub_pem_source == pub_target.to_pem().strip()
|
||||
+ assert pub_pem_source.replace(b"\n", b"") == pub_target.to_pem().strip().replace(b"\n", b"")
|
||||
|
||||
@pytest.mark.parametrize("BackendFrom", [ECDSAECKey, CryptographyECKey])
|
||||
@pytest.mark.parametrize("BackendTo", [ECDSAECKey, CryptographyECKey])
|
||||
@@ -68,4 +68,4 @@ class TestBackendEcdsaCompatibility:
|
||||
|
||||
target = BackendTo(pem_source, ALGORITHMS.ES256)
|
||||
|
||||
- assert pem_source == target.to_pem().strip()
|
||||
+ assert pem_source.replace(b"\n", b"") == target.to_pem().strip().replace(b"\n", b"")
|
||||
Index: python-jose-3.3.0/tests/algorithms/test_EC.py
|
||||
===================================================================
|
||||
--- python-jose-3.3.0.orig/tests/algorithms/test_EC.py
|
||||
+++ python-jose-3.3.0/tests/algorithms/test_EC.py
|
||||
@@ -104,7 +104,7 @@ class TestECAlgorithm:
|
||||
def test_to_pem(self):
|
||||
key = ECKey(private_key, ALGORITHMS.ES256)
|
||||
assert not key.is_public()
|
||||
- assert key.to_pem().strip() == private_key.strip().encode("utf-8")
|
||||
+ assert key.to_pem().strip().replace(b"\n", b"") == private_key.strip().encode("utf-8").replace(b"\n", b"")
|
||||
|
||||
public_pem = key.public_key().to_pem()
|
||||
assert ECKey(public_pem, ALGORITHMS.ES256).is_public()
|
BIN
python-jose-3.3.0.tar.gz
(Stored with Git LFS)
Normal file
BIN
python-jose-3.3.0.tar.gz
(Stored with Git LFS)
Normal file
Binary file not shown.
86
python-python-jose.changes
Normal file
86
python-python-jose.changes
Normal file
@ -0,0 +1,86 @@
|
||||
-------------------------------------------------------------------
|
||||
Mon Jun 3 07:38:00 UTC 2024 - Daniel Garcia <daniel.garcia@suse.com>
|
||||
|
||||
- Update CVE-2024-33664.patch with upstream
|
||||
https://github.com/mpdavis/python-jose/pull/352
|
||||
bsc#1223422
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Tue May 7 09:58:08 UTC 2024 - Daniel Garcia <daniel.garcia@suse.com>
|
||||
|
||||
- Make python-pycryptodome dependency only required for Factory.
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Mon May 6 07:11:18 UTC 2024 - Daniel Garcia <daniel.garcia@suse.com>
|
||||
|
||||
- Add upstream patches:
|
||||
* CVE-2024-33663.patch, bsc#1223417, gh#mpdavis/python-jose#349
|
||||
* CVE-2024-33664.patch, bsc#1223422, gh#mpdavis/python-jose#345
|
||||
* fix-tests-ecdsa-019.patch, gh#mpdavis/python-jose#350
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Tue Jun 13 12:18:28 UTC 2023 - ecsos <ecsos@opensuse.org>
|
||||
|
||||
- Add %{?sle15_python_module_pythons}
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Sun May 29 19:45:02 UTC 2022 - Ben Greiner <code@bnavigator.de>
|
||||
|
||||
- Update to 3.3.0
|
||||
* Remove support for python 2.7 & 3.5
|
||||
* Add support for Python 3.9
|
||||
* Remove PyCrypto backend
|
||||
* Fix deprecation warning from cryptography backend
|
||||
- Add rpm subpackages for the extra backend selection. The missing
|
||||
requires were only discovered because other packages started to
|
||||
fail.
|
||||
* setup.py and README still mention pycrypto and pycryptodome, but
|
||||
it was removed from the code.
|
||||
* Test in flavors
|
||||
- Refresh unpin-deps.patch
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Wed Aug 5 12:58:25 UTC 2020 - Marketa Calabkova <mcalabkova@suse.com>
|
||||
|
||||
- Update to 3.2.0
|
||||
* This will be the last release supporting Python 2.7, 3.5, and the PyCrypto
|
||||
backend.
|
||||
* Use hmac.compare_digest instead of our own constant_time_string_compare #163
|
||||
* Fix `to_dict` output, which should always be JSON encodeable. #139 and #165
|
||||
(fixes #127 and #137)
|
||||
* Require setuptools >= 39.2.0 #167 (fixes #161)
|
||||
* Emit a warning when verifying with a private key #168 (fixes #53 and #142)
|
||||
* Avoid loading python-ecdsa when using the cryptography backend, and pinned
|
||||
python-ecdsa dependency to <0.15 #178
|
||||
- Rebase patch unpin-deps.patch
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Tue Mar 10 09:47:42 UTC 2020 - Tomáš Chvátal <tchvatal@suse.com>
|
||||
|
||||
- Update to 3.1.0:
|
||||
* Improve JWT.decode() #76 (fixes #75)
|
||||
* ort headers when serializing to allow for headless JWT #136 (fixes #80)
|
||||
* djust dependency handling
|
||||
* se PyCryptodome instead of PyCrypto #83
|
||||
* pdate package dependencies #124 (fixes #158)
|
||||
* void using deprecated methods #85
|
||||
* upport X509 certificates #107
|
||||
* solate and flesh out cryptographic backends to enable independent operation #129 (fixes #114)
|
||||
* emove pyca/cryptography backend's dependency on python-ecdsa #117
|
||||
* Remove pycrypto/dome backends' dependency on python-rsa #121
|
||||
* Make pyca/cryptography backend the preferred backend if multiple backends are present #122
|
||||
- Rebase patch unpin-deps.patch
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Thu Apr 11 05:11:28 UTC 2019 - John Vandenberg <jayvdb@gmail.com>
|
||||
|
||||
- Activate test suite, using GitHub archive
|
||||
- Add unpin-deps.patch to fix broken installed egg-info,
|
||||
and remove unused dependency on python-future
|
||||
- Remove undesirable < comparator in build and runtime dependencies,
|
||||
and remove duplicated dependencies
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Thu Nov 15 00:12:07 UTC 2018 - Todd R <toddrme2178@gmail.com>
|
||||
|
||||
- Initial version
|
129
python-python-jose.spec
Normal file
129
python-python-jose.spec
Normal file
@ -0,0 +1,129 @@
|
||||
#
|
||||
# spec file for package python-python-jose
|
||||
#
|
||||
# Copyright (c) 2024 SUSE LLC
|
||||
#
|
||||
# All modifications and additions to the file contributed by third parties
|
||||
# remain the property of their copyright owners, unless otherwise agreed
|
||||
# upon. The license for this file, and modifications and additions to the
|
||||
# file, is the same license as for the pristine package itself (unless the
|
||||
# license for the pristine package is not an Open Source License, in which
|
||||
# case the license is the MIT License). An "Open Source License" is a
|
||||
# license that conforms to the Open Source Definition (Version 1.9)
|
||||
# published by the Open Source Initiative.
|
||||
|
||||
# Please submit bugfixes or comments via https://bugs.opensuse.org/
|
||||
#
|
||||
|
||||
|
||||
%global flavor @BUILD_FLAVOR@%{nil}
|
||||
%if "%{flavor}" == "test-backend-cryptography"
|
||||
%define psuffix -%{flavor}
|
||||
%bcond_without test
|
||||
%bcond_without testcryptography
|
||||
%bcond_with testnative
|
||||
%endif
|
||||
%if "%{flavor}" == "test-backend-native"
|
||||
%define psuffix -%{flavor}
|
||||
%bcond_without test
|
||||
%bcond_with testcryptography
|
||||
%bcond_without testnative
|
||||
%endif
|
||||
%if "%{flavor}" == ""
|
||||
%define psuffix %{nil}
|
||||
%bcond_with test
|
||||
%bcond_with testcryptography
|
||||
%bcond_with testnative
|
||||
%endif
|
||||
|
||||
%{?sle15_python_module_pythons}
|
||||
Name: python-python-jose%{psuffix}
|
||||
Version: 3.3.0
|
||||
Release: 0
|
||||
Summary: JOSE implementation in Python
|
||||
License: MIT
|
||||
URL: https://github.com/mpdavis/python-jose
|
||||
Source: https://files.pythonhosted.org/packages/source/p/python-jose/python-jose-%{version}.tar.gz
|
||||
Patch0: unpin-deps.patch
|
||||
# PATCH-FIX-UPSTREAM CVE-2024-33664.patch gh#mpdavis/python-jose#352
|
||||
Patch1: CVE-2024-33664.patch
|
||||
# PATCH-FIX-UPSTREAM CVE-2024-33663.patch gh#mpdavis/python-jose#349
|
||||
Patch2: CVE-2024-33663.patch
|
||||
# PATCH-FIX-UPSTREAM fix-tests-ecdsa-019.patch gh#mpdavis/python-jose#350
|
||||
Patch3: fix-tests-ecdsa-019.patch
|
||||
BuildRequires: %{python_module setuptools >= 39.2.0}
|
||||
BuildRequires: fdupes
|
||||
BuildRequires: python-rpm-macros
|
||||
Requires: python-ecdsa >= 0.16
|
||||
Requires: python-pyasn1
|
||||
Requires: python-rsa
|
||||
BuildArch: noarch
|
||||
%if %{with test}
|
||||
# pycryptodome is needed just for one test added in CVE-2024-33663.
|
||||
# This package is not in Leap, so do not require for other versions.
|
||||
%if 0%{?suse_version} > 1600
|
||||
BuildRequires: %{python_module pycryptodome}
|
||||
%endif
|
||||
BuildRequires: %{python_module pytest}
|
||||
%if %{with testcryptography}
|
||||
BuildRequires: %{python_module python-jose-cryptography = %{version}}
|
||||
%endif
|
||||
%if %{with testnative}
|
||||
BuildRequires: %{python_module python-jose = %{version}}
|
||||
%endif
|
||||
%endif
|
||||
# /SECTION
|
||||
%python_subpackages
|
||||
|
||||
%description
|
||||
A JavaScript Object Signing and Encryption (JOSE) technologies
|
||||
implementation in Python.
|
||||
|
||||
python-jose implements different cryptographic backends.
|
||||
Consuming python packages must select the backend as an extra
|
||||
when installing python-jose. RPM packages must select the
|
||||
corresponding rpm subpackage. If no backend is selected, the
|
||||
main package uses the native-python backend.
|
||||
|
||||
%package cryptography
|
||||
Summary: JOSE implementation in Python, cryptography extra
|
||||
Requires: %{name} = %{version}-%{release}
|
||||
Requires: python-cryptography >= 3.4.0
|
||||
|
||||
%description cryptography
|
||||
A JavaScript Object Signing and Encryption (JOSE) technologies
|
||||
implementation in Python.
|
||||
|
||||
python-jose implements three different cryptographic backends.
|
||||
This package provides the python-jose[cryptography] extra.
|
||||
|
||||
%prep
|
||||
%autosetup -p1 -n python-jose-%{version}
|
||||
|
||||
%if ! %{with test}
|
||||
%build
|
||||
%python_build
|
||||
|
||||
%install
|
||||
%python_install
|
||||
%python_expand %fdupes %{buildroot}%{$python_sitelib}
|
||||
%endif
|
||||
|
||||
%if %{with test}
|
||||
%check
|
||||
%pytest -rsEf
|
||||
%endif
|
||||
|
||||
%if ! %{with test}
|
||||
%files %{python_files}
|
||||
%doc README.rst
|
||||
%license LICENSE
|
||||
%{python_sitelib}/python_jose-%{version}*-info
|
||||
%{python_sitelib}/jose
|
||||
|
||||
%files %{python_files cryptography}
|
||||
%doc README.rst
|
||||
%license LICENSE
|
||||
%endif
|
||||
|
||||
%changelog
|
32
unpin-deps.patch
Normal file
32
unpin-deps.patch
Normal file
@ -0,0 +1,32 @@
|
||||
Index: python-jose-3.3.0/setup.py
|
||||
===================================================================
|
||||
--- python-jose-3.3.0.orig/setup.py
|
||||
+++ python-jose-3.3.0/setup.py
|
||||
@@ -23,11 +23,9 @@ def get_packages(package):
|
||||
pyasn1 = ["pyasn1"]
|
||||
extras_require = {
|
||||
"cryptography": ["cryptography>=3.4.0"],
|
||||
- "pycrypto": ["pycrypto >=2.6.0, <2.7.0"] + pyasn1,
|
||||
- "pycryptodome": ["pycryptodome >=3.3.1, <4.0.0"] + pyasn1,
|
||||
}
|
||||
# TODO: work this into the extras selection instead.
|
||||
-install_requires = ["ecdsa != 0.15", "rsa"] + pyasn1
|
||||
+install_requires = ["ecdsa >= 0.16", "rsa"] + pyasn1
|
||||
|
||||
|
||||
setup(
|
||||
@@ -63,14 +61,11 @@ setup(
|
||||
],
|
||||
extras_require=extras_require,
|
||||
setup_requires=[
|
||||
- "pytest-runner",
|
||||
"setuptools>=39.2.0",
|
||||
],
|
||||
tests_require=[
|
||||
"ecdsa != 0.15",
|
||||
"pytest",
|
||||
- "pytest-cov",
|
||||
- "pytest-runner",
|
||||
],
|
||||
install_requires=install_requires,
|
||||
)
|
Loading…
Reference in New Issue
Block a user