From 679b29d3f0c69935ae2c4fb8afed46078455b96b Mon Sep 17 00:00:00 2001 From: Jakob Schlyter Date: Fri, 14 Nov 2025 15:25:51 +0100 Subject: [PATCH 01/11] update --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index be211e7..5696c1b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v5.0.0 + rev: v6.0.0 hooks: - id: check-merge-conflict - id: debug-statements @@ -9,7 +9,7 @@ repos: - id: check-yaml - id: check-json - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.9.9 + rev: v0.14.4 hooks: - id: ruff - id: ruff-format From d84873d8b5599097ca44146b7637d1a7eed5bee9 Mon Sep 17 00:00:00 2001 From: Jakob Schlyter Date: Fri, 14 Nov 2025 15:29:02 +0100 Subject: [PATCH 02/11] Upgrade ruff requirement --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index bd8b719..d98a7c5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -43,7 +43,7 @@ dev = [ "sphinx>=3.5.2", "sphinx-autobuild>=2021.3.14", "coverage>=7", - "ruff>=0.9.9", + "ruff>=0.14.4", "pytest-ruff>=0.3.2" ] From 6c7d0a4199244f47752d74a3a2a583200f5b8c72 Mon Sep 17 00:00:00 2001 From: Jakob Schlyter Date: Fri, 14 Nov 2025 15:33:53 +0100 Subject: [PATCH 03/11] Fix UP035 --- pyproject.toml | 2 +- src/cryptojwt/__init__.py | 2 +- src/cryptojwt/jwk/__init__.py | 16 ++++++++++++---- src/cryptojwt/jwt.py | 15 ++++++++------- src/cryptojwt/key_bundle.py | 4 ++-- src/cryptojwt/key_issuer.py | 4 ++-- src/cryptojwt/key_jar.py | 16 ++++++++-------- src/cryptojwt/utils.py | 5 ++--- tests/test_21_pss.py | 2 +- 9 files changed, 37 insertions(+), 29 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index d98a7c5..56b6473 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -74,7 +74,7 @@ select = [ # isort "I", ] -ignore = ["E501", "I001", "SIM102", "UP006", "UP035"] +ignore = ["E501", "I001", "SIM102", "UP006"] exclude = ["examples/*"] [tool.ruff.lint.isort] diff --git a/src/cryptojwt/__init__.py b/src/cryptojwt/__init__.py index cd515ac..f880d79 100644 --- a/src/cryptojwt/__init__.py +++ b/src/cryptojwt/__init__.py @@ -1,7 +1,7 @@ """JSON Web Token""" import logging -from importlib.metadata import version, PackageNotFoundError +from importlib.metadata import PackageNotFoundError, version from cryptojwt.jwe.jwe import JWE from cryptojwt.jwk import JWK diff --git a/src/cryptojwt/jwk/__init__.py b/src/cryptojwt/jwk/__init__.py index 4294f50..8dfd1b6 100644 --- a/src/cryptojwt/jwk/__init__.py +++ b/src/cryptojwt/jwk/__init__.py @@ -2,7 +2,6 @@ import hashlib import json import ssl -from typing import List from ..exception import UnsupportedAlgorithm from ..utils import as_bytes, as_unicode, b64e, base64url_to_long @@ -21,12 +20,21 @@ class JWK: """ members = ["kty", "alg", "use", "kid", "x5c", "x5t", "x5u", "key_ops"] - longs: List[str] = [] + longs: list[str] = [] public_members = ["kty", "alg", "use", "kid", "x5c", "x5t", "x5u", "key_ops"] required = ["kty"] def __init__( - self, kty="", alg="", use="", kid="", x5c=None, x5t="", x5u="", key_ops=None, **kwargs + self, + kty="", + alg="", + use="", + kid="", + x5c=None, + x5t="", + x5u="", + key_ops=None, + **kwargs, ): self.extra_args = kwargs @@ -121,7 +129,7 @@ def __init__( self.kid = as_unicode(kid) if key_ops: - self.key_ops: List[str] = [] + self.key_ops: list[str] = [] for ops in key_ops: if isinstance(ops, str): self.key_ops.append(ops) diff --git a/src/cryptojwt/jwt.py b/src/cryptojwt/jwt.py index 19e70e7..b397e3d 100755 --- a/src/cryptojwt/jwt.py +++ b/src/cryptojwt/jwt.py @@ -5,8 +5,9 @@ import logging import time import uuid +from collections.abc import MutableMapping from json import JSONDecodeError -from typing import Dict, List, MutableMapping, Optional +from typing import Optional from .exception import HeaderError, VerificationError from .jwe.jwe import JWE, factory as jwe_factory @@ -84,14 +85,14 @@ def __init__( enc_enc: str = "A128GCM", enc_alg: str = "RSA-OAEP-256", msg_cls: Optional[MutableMapping] = None, - iss2msg_cls: Optional[Dict[str, str]] = None, + iss2msg_cls: Optional[dict[str, str]] = None, skew: Optional[int] = 15, - allowed_sign_algs: Optional[List[str]] = None, - allowed_enc_algs: Optional[List[str]] = None, - allowed_enc_encs: Optional[List[str]] = None, + allowed_sign_algs: Optional[list[str]] = None, + allowed_enc_algs: Optional[list[str]] = None, + allowed_enc_encs: Optional[list[str]] = None, allowed_max_lifetime: Optional[int] = None, zip: Optional[str] = "", - typ2msg_cls: Optional[Dict] = None, + typ2msg_cls: Optional[dict] = None, ): self.key_jar = key_jar # KeyJar instance self.iss = iss # My identifier @@ -214,7 +215,7 @@ def pack( recv: Optional[str] = "", aud: Optional[str] = None, iat: Optional[int] = None, - jws_headers: Optional[Dict[str, str]] = None, + jws_headers: Optional[dict[str, str]] = None, **kwargs, ) -> str: """ diff --git a/src/cryptojwt/key_bundle.py b/src/cryptojwt/key_bundle.py index f27f3f8..d931ad4 100755 --- a/src/cryptojwt/key_bundle.py +++ b/src/cryptojwt/key_bundle.py @@ -9,7 +9,7 @@ import time from datetime import datetime from functools import cmp_to_key -from typing import List, Optional +from typing import Optional import requests @@ -808,7 +808,7 @@ def difference(self, bundle): return [k for k in self.keys() if k not in bundle] - def dump(self, exclude_attributes: Optional[List[str]] = None): + def dump(self, exclude_attributes: Optional[list[str]] = None): if exclude_attributes is None: exclude_attributes = [] diff --git a/src/cryptojwt/key_issuer.py b/src/cryptojwt/key_issuer.py index 80b7abd..14a808b 100755 --- a/src/cryptojwt/key_issuer.py +++ b/src/cryptojwt/key_issuer.py @@ -1,7 +1,7 @@ import json import logging import os -from typing import List, Optional +from typing import Optional from requests import request @@ -345,7 +345,7 @@ def __len__(self): nr += len(kb) return nr - def dump(self, exclude_attributes: Optional[List[str]] = None) -> dict: + def dump(self, exclude_attributes: Optional[list[str]] = None) -> dict: """ Returns the content as a dictionary. diff --git a/src/cryptojwt/key_jar.py b/src/cryptojwt/key_jar.py index 6c541d3..907ee88 100755 --- a/src/cryptojwt/key_jar.py +++ b/src/cryptojwt/key_jar.py @@ -1,7 +1,7 @@ import contextlib import json import logging -from typing import List, Optional +from typing import Optional from requests import request @@ -55,7 +55,7 @@ def __init__( if not self.httpc_params: # backward compatibility self.httpc_params["verify"] = verify_ssl - def _issuer_ids(self) -> List[str]: + def _issuer_ids(self) -> list[str]: """ Returns a list of issuer identifiers @@ -159,7 +159,7 @@ def add_kb(self, issuer_id, kb): issuer.add_kb(kb) self._issuers[issuer_id] = issuer - def add_keys(self, issuer_id: str, keys: List[JWK], **kwargs): + def add_keys(self, issuer_id: str, keys: list[JWK], **kwargs): _kb = KeyBundle(**kwargs) _kb.extend(keys) self.add_kb(issuer_id, _kb) @@ -623,8 +623,8 @@ def __len__(self): def _dump_issuers( self, - exclude_issuers: Optional[List[str]] = None, - exclude_attributes: Optional[List[str]] = None, + exclude_issuers: Optional[list[str]] = None, + exclude_attributes: Optional[list[str]] = None, ): _issuers = {} for _id, _issuer in self._issuers.items(): @@ -635,8 +635,8 @@ def _dump_issuers( def dump( self, - exclude_issuers: Optional[List[str]] = None, - exclude_attributes: Optional[List[str]] = None, + exclude_issuers: Optional[list[str]] = None, + exclude_attributes: Optional[list[str]] = None, ) -> dict: """ Returns the key jar content as dictionary @@ -667,7 +667,7 @@ def dump( return info - def dumps(self, exclude_issuers: Optional[List[str]] = None): + def dumps(self, exclude_issuers: Optional[list[str]] = None): """ Returns a JSON representation of the key jar diff --git a/src/cryptojwt/utils.py b/src/cryptojwt/utils.py index 4fa5250..1ee1f34 100644 --- a/src/cryptojwt/utils.py +++ b/src/cryptojwt/utils.py @@ -8,7 +8,6 @@ import warnings from binascii import unhexlify from email.message import EmailMessage -from typing import List, Set, Union from cryptojwt.exception import BadSyntax @@ -32,7 +31,7 @@ def intarr2str(arr): def long2intarr(long_int): - _bytes: List[int] = [] + _bytes: list[int] = [] while long_int: long_int, r = divmod(long_int, 256) _bytes.insert(0, r) @@ -262,7 +261,7 @@ def httpc_params_loader(httpc_params): return httpc_params -def check_content_type(content_type: str, mime_type: Union[str, List[str], Set[str]]): +def check_content_type(content_type: str, mime_type: str | list[str] | set[str]): """Return True if the content type contains the MIME type""" msg = EmailMessage() msg["content-type"] = content_type diff --git a/tests/test_21_pss.py b/tests/test_21_pss.py index 7f99cac..eb14fe7 100644 --- a/tests/test_21_pss.py +++ b/tests/test_21_pss.py @@ -1,10 +1,10 @@ import json import pytest +import test_vector from cryptojwt.jwk.jwk import key_from_jwk_dict from cryptojwt.jws.jws import JWS -import test_vector @pytest.mark.parametrize("alg", ["RS256", "RS384", "RS512", "PS256", "PS384", "PS512"]) From 8edee46c0b6d9e050fbbf7c4f408791ff9f097a8 Mon Sep 17 00:00:00 2001 From: Jakob Schlyter Date: Fri, 14 Nov 2025 15:34:47 +0100 Subject: [PATCH 04/11] Remove unneeded ignores --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 56b6473..f5b7389 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -74,7 +74,7 @@ select = [ # isort "I", ] -ignore = ["E501", "I001", "SIM102", "UP006"] +ignore = ["E501", "SIM102"] exclude = ["examples/*"] [tool.ruff.lint.isort] From f6a68eb328a589d34f170be9fb2dde4259a4786c Mon Sep 17 00:00:00 2001 From: Jakob Schlyter Date: Fri, 14 Nov 2025 15:38:35 +0100 Subject: [PATCH 05/11] Remove Optional and Union, fix Type --- src/cryptojwt/jwe/fernet.py | 17 ++++++++--------- src/cryptojwt/jwt.py | 33 ++++++++++++++++----------------- src/cryptojwt/key_bundle.py | 3 +-- src/cryptojwt/key_issuer.py | 3 +-- src/cryptojwt/key_jar.py | 17 ++++++++--------- src/cryptojwt/tools/keyconv.py | 25 ++++++++++++------------- 6 files changed, 46 insertions(+), 52 deletions(-) diff --git a/src/cryptojwt/jwe/fernet.py b/src/cryptojwt/jwe/fernet.py index 4fee8c3..20ab716 100644 --- a/src/cryptojwt/jwe/fernet.py +++ b/src/cryptojwt/jwe/fernet.py @@ -1,6 +1,5 @@ import base64 import os -from typing import Optional, Union from cryptography.fernet import Fernet from cryptography.hazmat.primitives import hashes @@ -15,12 +14,12 @@ class FernetEncrypter(Encrypter): def __init__( self, - password: Optional[str] = None, - salt: Optional[bytes] = "", - key: Optional[bytes] = None, - hash_alg: Optional[str] = "SHA256", - digest_size: Optional[int] = 0, - iterations: Optional[int] = DEFAULT_ITERATIONS, + password: str | None = None, + salt: bytes | None = "", + key: bytes | None = None, + hash_alg: str | None = "SHA256", + digest_size: int | None = 0, + iterations: int | None = DEFAULT_ITERATIONS, ): Encrypter.__init__(self) @@ -45,14 +44,14 @@ def __init__( self.core = Fernet(self.key) - def encrypt(self, msg: Union[str, bytes], **kwargs) -> bytes: + def encrypt(self, msg: str | bytes, **kwargs) -> bytes: text = as_bytes(msg) # Padding to block size of AES if len(text) % 16: text += b" " * (16 - len(text) % 16) return self.core.encrypt(as_bytes(text)) - def decrypt(self, msg: Union[str, bytes], **kwargs) -> bytes: + def decrypt(self, msg: str | bytes, **kwargs) -> bytes: dec_text = self.core.decrypt(as_bytes(msg)) dec_text = dec_text.rstrip(b" ") return dec_text diff --git a/src/cryptojwt/jwt.py b/src/cryptojwt/jwt.py index b397e3d..e96fb2e 100755 --- a/src/cryptojwt/jwt.py +++ b/src/cryptojwt/jwt.py @@ -7,7 +7,6 @@ import uuid from collections.abc import MutableMapping from json import JSONDecodeError -from typing import Optional from .exception import HeaderError, VerificationError from .jwe.jwe import JWE, factory as jwe_factory @@ -84,15 +83,15 @@ def __init__( encrypt: bool = False, enc_enc: str = "A128GCM", enc_alg: str = "RSA-OAEP-256", - msg_cls: Optional[MutableMapping] = None, - iss2msg_cls: Optional[dict[str, str]] = None, - skew: Optional[int] = 15, - allowed_sign_algs: Optional[list[str]] = None, - allowed_enc_algs: Optional[list[str]] = None, - allowed_enc_encs: Optional[list[str]] = None, - allowed_max_lifetime: Optional[int] = None, - zip: Optional[str] = "", - typ2msg_cls: Optional[dict] = None, + msg_cls: type[MutableMapping] | None = None, + iss2msg_cls: dict[str, str] | None = None, + skew: int | None = 15, + allowed_sign_algs: list[str] | None = None, + allowed_enc_algs: list[str] | None = None, + allowed_enc_encs: list[str] | None = None, + allowed_max_lifetime: int | None = None, + zip: str | None = "", + typ2msg_cls: dict | None = None, ): self.key_jar = key_jar # KeyJar instance self.iss = iss # My identifier @@ -209,13 +208,13 @@ def message(self, signing_key, **kwargs): def pack( self, - payload: Optional[dict] = None, - kid: Optional[str] = "", - issuer_id: Optional[str] = "", - recv: Optional[str] = "", - aud: Optional[str] = None, - iat: Optional[int] = None, - jws_headers: Optional[dict[str, str]] = None, + payload: dict | None = None, + kid: str | None = "", + issuer_id: str | None = "", + recv: str | None = "", + aud: str | None = None, + iat: int | None = None, + jws_headers: dict[str, str] | None = None, **kwargs, ) -> str: """ diff --git a/src/cryptojwt/key_bundle.py b/src/cryptojwt/key_bundle.py index d931ad4..94be23a 100755 --- a/src/cryptojwt/key_bundle.py +++ b/src/cryptojwt/key_bundle.py @@ -9,7 +9,6 @@ import time from datetime import datetime from functools import cmp_to_key -from typing import Optional import requests @@ -808,7 +807,7 @@ def difference(self, bundle): return [k for k in self.keys() if k not in bundle] - def dump(self, exclude_attributes: Optional[list[str]] = None): + def dump(self, exclude_attributes: list[str] | None = None): if exclude_attributes is None: exclude_attributes = [] diff --git a/src/cryptojwt/key_issuer.py b/src/cryptojwt/key_issuer.py index 14a808b..fcfdbc1 100755 --- a/src/cryptojwt/key_issuer.py +++ b/src/cryptojwt/key_issuer.py @@ -1,7 +1,6 @@ import json import logging import os -from typing import Optional from requests import request @@ -345,7 +344,7 @@ def __len__(self): nr += len(kb) return nr - def dump(self, exclude_attributes: Optional[list[str]] = None) -> dict: + def dump(self, exclude_attributes: list[str] | None = None) -> dict: """ Returns the content as a dictionary. diff --git a/src/cryptojwt/key_jar.py b/src/cryptojwt/key_jar.py index 907ee88..f54da42 100755 --- a/src/cryptojwt/key_jar.py +++ b/src/cryptojwt/key_jar.py @@ -1,7 +1,6 @@ import contextlib import json import logging -from typing import Optional from requests import request @@ -64,7 +63,7 @@ def _issuer_ids(self) -> list[str]: return list(self._issuers.keys()) @deprecated_alias(issuer="issuer_id", owner="issuer_id") - def _get_issuer(self, issuer_id: str) -> Optional[KeyIssuer]: + def _get_issuer(self, issuer_id: str) -> KeyIssuer | None: """ Return the KeyIssuer instance that has name == issuer_id @@ -623,8 +622,8 @@ def __len__(self): def _dump_issuers( self, - exclude_issuers: Optional[list[str]] = None, - exclude_attributes: Optional[list[str]] = None, + exclude_issuers: list[str] | None = None, + exclude_attributes: list[str] | None = None, ): _issuers = {} for _id, _issuer in self._issuers.items(): @@ -635,8 +634,8 @@ def _dump_issuers( def dump( self, - exclude_issuers: Optional[list[str]] = None, - exclude_attributes: Optional[list[str]] = None, + exclude_issuers: list[str] | None = None, + exclude_attributes: list[str] | None = None, ) -> dict: """ Returns the key jar content as dictionary @@ -667,7 +666,7 @@ def dump( return info - def dumps(self, exclude_issuers: Optional[list[str]] = None): + def dumps(self, exclude_issuers: list[str] | None = None): """ Returns a JSON representation of the key jar @@ -680,8 +679,8 @@ def dumps(self, exclude_issuers: Optional[list[str]] = None): def load( self, info: dict, - init_args: Optional[dict] = None, - load_args: Optional[dict] = None, + init_args: dict | None = None, + load_args: dict | None = None, ): """ diff --git a/src/cryptojwt/tools/keyconv.py b/src/cryptojwt/tools/keyconv.py index fc1b966..bf69be8 100644 --- a/src/cryptojwt/tools/keyconv.py +++ b/src/cryptojwt/tools/keyconv.py @@ -6,7 +6,6 @@ import json from binascii import hexlify from getpass import getpass -from typing import Optional from cryptography.hazmat.primitives import serialization @@ -35,9 +34,9 @@ def jwk_from_file(filename: str, private: bool = True) -> JWK: def pem2rsa( filename: str, - kid: Optional[str] = None, + kid: str | None = None, private: bool = False, - passphrase: Optional[str] = None, + passphrase: str | None = None, ) -> JWK: """Convert RSA key from PEM to JWK""" if private: @@ -51,9 +50,9 @@ def pem2rsa( def pem2ec( filename: str, - kid: Optional[str] = None, + kid: str | None = None, private: bool = False, - passphrase: Optional[str] = None, + passphrase: str | None = None, ) -> JWK: """Convert EC key from PEM to JWK""" if private: @@ -67,9 +66,9 @@ def pem2ec( def pem2okp( filename: str, - kid: Optional[str] = None, + kid: str | None = None, private: bool = False, - passphrase: Optional[str] = None, + passphrase: str | None = None, ) -> JWK: """Convert OKP key from PEM to JWK""" if private: @@ -90,10 +89,10 @@ def bin2jwk(filename: str, kid: str) -> JWK: def pem2jwk( filename: str, - kid: Optional[str] = None, - kty: Optional[str] = None, + kid: str | None = None, + kty: str | None = None, private: bool = False, - passphrase: Optional[str] = None, + passphrase: str | None = None, ) -> JWK: """Read PEM from filename and return JWK""" with open(filename) as file: @@ -144,7 +143,7 @@ def export_jwk( jwk: JWK, private: bool = False, encrypt: bool = False, - passphrase: Optional[str] = None, + passphrase: str | None = None, ) -> bytes: """Export JWK as PEM/bin""" @@ -177,7 +176,7 @@ def export_jwk( return serialized -def output_jwk(jwk: JWK, private: bool = False, filename: Optional[str] = None) -> None: +def output_jwk(jwk: JWK, private: bool = False, filename: str | None = None) -> None: """Output JWK to file""" serialized = jwk.serialize(private=private) if filename is not None: @@ -187,7 +186,7 @@ def output_jwk(jwk: JWK, private: bool = False, filename: Optional[str] = None) print(json.dumps(serialized, indent=4)) -def output_bytes(data: bytes, binary: bool = False, filename: Optional[str] = None) -> None: +def output_bytes(data: bytes, binary: bool = False, filename: str | None = None) -> None: """Output data to file""" if filename is not None: with open(filename, mode="wb") as file: From 6a0ff8fb79a96c293a05baacec95df00fe19ddb6 Mon Sep 17 00:00:00 2001 From: Jakob Schlyter Date: Fri, 14 Nov 2025 15:39:32 +0100 Subject: [PATCH 06/11] Test on Python 3.14 --- .github/workflows/test.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b436298..1ef906d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -28,6 +28,7 @@ jobs: - "3.11" - "3.12" - "3.13" + - "3.14" steps: - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} From 909fbdd72befb670c902143677b7319af4712e23 Mon Sep 17 00:00:00 2001 From: Jakob Schlyter Date: Fri, 14 Nov 2025 15:41:38 +0100 Subject: [PATCH 07/11] Python 3.9 is EOL --- .github/workflows/test.yml | 1 - pyproject.toml | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 1ef906d..10d0612 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -23,7 +23,6 @@ jobs: strategy: matrix: python-version: - - "3.9" - "3.10" - "3.11" - "3.12" diff --git a/pyproject.toml b/pyproject.toml index f5b7389..3fe41e4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,7 +13,7 @@ readme = "README.md" packages = [ { include = "cryptojwt", from = "src" } ] -requires-python = ">=3.9,<4.0" +requires-python = ">=3.10,<4.0" dependencies = [ "cryptography>=3.4.6", "requests>=2.25.1" From 3615fe8f7e55d6ccd12097071202d31e12301886 Mon Sep 17 00:00:00 2001 From: Jakob Schlyter Date: Fri, 14 Nov 2025 15:43:37 +0100 Subject: [PATCH 08/11] Remove one more union --- src/cryptojwt/jwk/okp.py | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/src/cryptojwt/jwk/okp.py b/src/cryptojwt/jwk/okp.py index 1425f90..b8255c1 100644 --- a/src/cryptojwt/jwk/okp.py +++ b/src/cryptojwt/jwk/okp.py @@ -1,5 +1,3 @@ -from typing import Union - from cryptography.hazmat.primitives import serialization from cryptography.hazmat.primitives.asymmetric import ed448, ed25519, x448, x25519 @@ -14,12 +12,16 @@ import_public_key_from_pem_file, ) -OKPPublicKey = Union[ - ed25519.Ed25519PublicKey, ed448.Ed448PublicKey, x25519.X25519PublicKey, x448.X448PublicKey -] -OKPPrivateKey = Union[ - ed25519.Ed25519PrivateKey, ed448.Ed448PrivateKey, x25519.X25519PrivateKey, x448.X448PrivateKey -] +OKPPublicKey = ( + ed25519.Ed25519PublicKey | ed448.Ed448PublicKey | x25519.X25519PublicKey | x448.X448PublicKey +) + +OKPPrivateKey = ( + ed25519.Ed25519PrivateKey + | ed448.Ed448PrivateKey + | x25519.X25519PrivateKey + | x448.X448PrivateKey +) OKP_CRV2PUBLIC = { "Ed25519": ed25519.Ed25519PublicKey, @@ -155,7 +157,8 @@ def deserialize(self): def _serialize_public(self, key): self.x = b64e( key.public_bytes( - encoding=serialization.Encoding.Raw, format=serialization.PublicFormat.Raw + encoding=serialization.Encoding.Raw, + format=serialization.PublicFormat.Raw, ) ).decode("ascii") @@ -304,9 +307,11 @@ def cmp_keys(a, b, key_type): return False else: if a.public_bytes( - encoding=serialization.Encoding.Raw, format=serialization.PublicFormat.Raw + encoding=serialization.Encoding.Raw, + format=serialization.PublicFormat.Raw, ) != b.public_bytes( - encoding=serialization.Encoding.Raw, format=serialization.PublicFormat.Raw + encoding=serialization.Encoding.Raw, + format=serialization.PublicFormat.Raw, ): return False return True From cd11b752680c715662dcfe7312addb94b5bc87be Mon Sep 17 00:00:00 2001 From: Jakob Schlyter Date: Sun, 18 Jan 2026 11:42:44 +0100 Subject: [PATCH 09/11] Reformat --- tests/test_02_jwk.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/tests/test_02_jwk.py b/tests/test_02_jwk.py index 6e83f3d..ac5eda8 100755 --- a/tests/test_02_jwk.py +++ b/tests/test_02_jwk.py @@ -9,11 +9,7 @@ import pytest from cryptography.hazmat.primitives.asymmetric import ec, ed25519, rsa -from cryptojwt.exception import ( - DeSerializationNotPossible, - UnsupportedAlgorithm, - WrongUsage, -) +from cryptojwt.exception import DeSerializationNotPossible, UnsupportedAlgorithm, WrongUsage from cryptojwt.jwk import JWK, certificate_fingerprint, pem_hash, pems_to_x5c from cryptojwt.jwk.ec import ECKey, new_ec_key from cryptojwt.jwk.hmac import SYMKey, new_sym_key, sha256_digest From 95bf0be6b089e0646af0e05ed4513bbcfb79f3b5 Mon Sep 17 00:00:00 2001 From: Jakob Schlyter Date: Sun, 18 Jan 2026 11:44:45 +0100 Subject: [PATCH 10/11] Remove Union --- src/cryptojwt/jwk/okp.py | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/src/cryptojwt/jwk/okp.py b/src/cryptojwt/jwk/okp.py index fedd0fa..8889e20 100644 --- a/src/cryptojwt/jwk/okp.py +++ b/src/cryptojwt/jwk/okp.py @@ -12,18 +12,16 @@ import_public_key_from_pem_file, ) -OKPPublicKey = Union[ - ed25519.Ed25519PublicKey, - ed448.Ed448PublicKey, - x25519.X25519PublicKey, - x448.X448PublicKey, -] -OKPPrivateKey = Union[ - ed25519.Ed25519PrivateKey, - ed448.Ed448PrivateKey, - x25519.X25519PrivateKey, - x448.X448PrivateKey, -] +OKPPublicKey = ( + ed25519.Ed25519PublicKey | ed448.Ed448PublicKey | x25519.X25519PublicKey | x448.X448PublicKey +) + +OKPPrivateKey = ( + ed25519.Ed25519PrivateKey + | ed448.Ed448PrivateKey + | x25519.X25519PrivateKey + | x448.X448PrivateKey +) OKP_CRV2PUBLIC = { "Ed25519": ed25519.Ed25519PublicKey, From d27016d60355032e3f4dff1acb183ec22fb61408 Mon Sep 17 00:00:00 2001 From: Jakob Schlyter Date: Mon, 16 Mar 2026 10:35:37 +0100 Subject: [PATCH 11/11] Fix imports --- CONTRIBUTING.md | 10 +++++----- doc/Makefile | 2 +- src/cryptojwt/key_jar.py | 1 - src/cryptojwt/tools/jwtpeek.py | 4 ++-- tests/ec-public.pem | 1 - tests/ec.pem | 2 +- tests/jwk_private_ec_key.json | 2 +- tests/jwk_private_key.json | 2 +- tests/rsa-public.pem | 1 - tests/size2048.key | 2 +- tests/test_keys/ec-p256-private.pem | 1 - tests/test_keys/ec-p256-public.pem | 1 - tests/test_keys/ec-p384-private.pem | 1 - tests/test_keys/ec-p384-public.pem | 1 - tests/test_keys/jwk.json | 2 +- tests/test_keys/rsa-1024-private.pem | 1 - tests/test_keys/rsa-1024-public.pem | 1 - tests/test_keys/rsa-1280-private.pem | 1 - tests/test_keys/rsa-1280-public.pem | 1 - tests/test_keys/rsa-2048-private.pem | 1 - tests/test_keys/rsa-2048-public.pem | 1 - tests/test_keys/rsa-3072-private.pem | 1 - tests/test_keys/rsa-3072-public.pem | 1 - tests/test_keys/rsa-4096-private.pem | 1 - tests/test_keys/rsa-4096-public.pem | 1 - 25 files changed, 13 insertions(+), 30 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index fa39d2b..07be810 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,7 +4,7 @@ All contributions to the Python JwtConnect packages are welcome! Note that as this library is planned to be used in high-profile production code, we insist on a very high standards for the code and design, but don't feel shy: -discuss your plans over +discuss your plans over [GitHub Issues](https://github.com/openid/JWTConnect-Python-OidcMsg/issues) and the [mailing list](http://lists.openid.net/mailman/listinfo/openid-specs-ab), and send in those pull requests! @@ -42,14 +42,14 @@ requests). Before you work on a big new feature, get in touch to make sure that your work is inline with the direction of the project and get input on your architecture. You can file an [Issue](https://github.com/openid/JWTConnect-Python-OidcMsg/issues) -discussing your proposal, or email the -[list](http://lists.openid.net/mailman/listinfo/openid-specs-ab). +discussing your proposal, or email the +[list](http://lists.openid.net/mailman/listinfo/openid-specs-ab). ## Coding Standards The JWTCOnnect-Python-OidcMsg library follows the -[PEP8](https://www.python.org/dev/peps/pep-0008/) -coding style for Python implementations. Please review your own code +[PEP8](https://www.python.org/dev/peps/pep-0008/) +coding style for Python implementations. Please review your own code for adherence to the standard. ## Pull Request Reviews diff --git a/doc/Makefile b/doc/Makefile index 7421c93..2a619e4 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -17,4 +17,4 @@ help: # Catch-all target: route all unknown targets to Sphinx using the new # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). %: Makefile - @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) \ No newline at end of file + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/src/cryptojwt/key_jar.py b/src/cryptojwt/key_jar.py index b722193..889c112 100755 --- a/src/cryptojwt/key_jar.py +++ b/src/cryptojwt/key_jar.py @@ -2,7 +2,6 @@ import json import logging from collections import defaultdict -from typing import List, Optional from requests import request diff --git a/src/cryptojwt/tools/jwtpeek.py b/src/cryptojwt/tools/jwtpeek.py index 7dfaa96..471010c 100755 --- a/src/cryptojwt/tools/jwtpeek.py +++ b/src/cryptojwt/tools/jwtpeek.py @@ -37,12 +37,12 @@ ./jwtpeek.py -f idtoken -J keys.jwks -or +or (3) JWT from stdin, no keys echo json.web.token | ./jwtpeek.py - + """ diff --git a/tests/ec-public.pem b/tests/ec-public.pem index 8384a6a..58b003f 100644 --- a/tests/ec-public.pem +++ b/tests/ec-public.pem @@ -2,4 +2,3 @@ MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEBNVkO23bySJFBEg/YXNxlL9fkBrf gHCr7s8X4z0aqsy7b5VOZ1CjjNq4CwjFT9IYruMLYERClGC8mx9VD8BLrw== -----END PUBLIC KEY----- - diff --git a/tests/ec.pem b/tests/ec.pem index d9d9015..547c1e2 100644 --- a/tests/ec.pem +++ b/tests/ec.pem @@ -2,4 +2,4 @@ MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEAb97f3Sw5anaTuSuP/QrP27wtN4tkewd \nbFJelq3pCBB0xpe1SjU5waxfnyrlMKG1oj0gm3sIhDGfyFFM2n65KIPMPl+q9V VS\nWvb9mSCrRZ7FDRrTTYAC5PsrrtYoIATf ------END PUBLIC KEY----- \ No newline at end of file +-----END PUBLIC KEY----- diff --git a/tests/jwk_private_ec_key.json b/tests/jwk_private_ec_key.json index 8d73d31..4e1a4f8 100644 --- a/tests/jwk_private_ec_key.json +++ b/tests/jwk_private_ec_key.json @@ -5,4 +5,4 @@ "x": "Ijdv9ZAD7QaEYnEqY5als5NFbNP_LsyZgJMTcQhNsYo", "y": "Hpc9vdz77lQG0NJf5FZAaeOJTR-bMjIw9kkCE1duxKE", "d": "omnOYCv3UxQRIogRcd2VIHhaj46FTZU8GfdYS4qX2_w" -} \ No newline at end of file +} diff --git a/tests/jwk_private_key.json b/tests/jwk_private_key.json index 35b2d62..3b1c0a0 100644 --- a/tests/jwk_private_key.json +++ b/tests/jwk_private_key.json @@ -11,4 +11,4 @@ "p": "6MEg5Di_IFiPGKvMFRjyx2t7YAOQ4KfdIkU_Khny1t1eCG5O07omPe_jLU8I5fPaD5F5HhWExLNureHD4K6LB18JPE3VE8chQROiRSNPZo1-faUvHu-Dy0pr7I-TS8pl_P3vop1KelIbGwXhzPIRKQMqCEKi3tLJt4R_MQ18Dx0", "q": "1cZVPpUbf4p5n4cMv_kERCPh3cieMs4aVojgh3feAiJiLwWWL9Pc43oJUekK44aWMnbs68Y4kqXtc52PMtBDzVp0Gjt0lCY3M7MYRVI4JhtknqvQynMKQ2nKs3VldvVfY2SxyUmnRyEolQUGRA7rRMUyPb4AXhSR7oroRrJD59s", "qi": "50PhyaqbLSczhipWiYy149sLsGlx9cX0tnGMswy1JLam7nBvH4-MWB2oGwD2hmG-YN66q-xXBS9CVDLZZrj1sonRTQPtWE-zuZqds6_NVlk2Ge4_IAA3TZ9tvIfM5FZVTOQsExu3_LX8FGCspWC1R-zDqT45Y9bpaCwxekluO7Q" -} \ No newline at end of file +} diff --git a/tests/rsa-public.pem b/tests/rsa-public.pem index d8cd0dc..97d4540 100644 --- a/tests/rsa-public.pem +++ b/tests/rsa-public.pem @@ -4,4 +4,3 @@ rbVBGf2Pk7PwiKAep3nko0CWds4MUIRlioCiigE6KSj2eExStNAFDf3jcjOfYpEV kaUsyGmwclKXxAPNRMr6GAJeKlbJqTs5eYd5tpBjdeW2rjkD/W6R77zp+EtuU4jY /zR2YWeqB6GdTnduKwIDAQAB -----END PUBLIC KEY----- - diff --git a/tests/size2048.key b/tests/size2048.key index 1afec60..bd029ff 100644 --- a/tests/size2048.key +++ b/tests/size2048.key @@ -24,4 +24,4 @@ Z86amGtk20CrNNFpQSlam8qWhQAvesFpQA2uqMHlnbxuHhsEC35qbVbFwdtzW3hK MeaO1uECgYBJgvilqYno7Stxu0nbK5GoBYqMSnJiLuJ83+6aTcFw7IryZFdaff24 iKfRJ0mvbJoklJuBGDjoRYE6AT20nGZTc+Wbx2cMR3HNGqlPKRbUjViwo8xkR7fx mBmyncZCL2HldD8gPo5SvLEKcIwYg8foXCM/tNLcmYgdf6amY+MyXQ== ------END RSA PRIVATE KEY----- \ No newline at end of file +-----END RSA PRIVATE KEY----- diff --git a/tests/test_keys/ec-p256-private.pem b/tests/test_keys/ec-p256-private.pem index b5459f2..fd0b4f2 100644 --- a/tests/test_keys/ec-p256-private.pem +++ b/tests/test_keys/ec-p256-private.pem @@ -3,4 +3,3 @@ MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgrp4E4eaFhHonpP/U 8QRaFDTvCGy8mQrSfQod+QxmViehRANCAAQE1WQ7bdvJIkUESD9hc3GUv1+QGt+A cKvuzxfjPRqqzLtvlU5nUKOM2rgLCMVP0hiu4wtgREKUYLybH1UPwEuv -----END PRIVATE KEY----- - diff --git a/tests/test_keys/ec-p256-public.pem b/tests/test_keys/ec-p256-public.pem index 8384a6a..58b003f 100644 --- a/tests/test_keys/ec-p256-public.pem +++ b/tests/test_keys/ec-p256-public.pem @@ -2,4 +2,3 @@ MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEBNVkO23bySJFBEg/YXNxlL9fkBrf gHCr7s8X4z0aqsy7b5VOZ1CjjNq4CwjFT9IYruMLYERClGC8mx9VD8BLrw== -----END PUBLIC KEY----- - diff --git a/tests/test_keys/ec-p384-private.pem b/tests/test_keys/ec-p384-private.pem index dda46f0..7677a08 100644 --- a/tests/test_keys/ec-p384-private.pem +++ b/tests/test_keys/ec-p384-private.pem @@ -4,4 +4,3 @@ iVTFrhF1hVzR57YiMld16H7HcMzv6HNF/nHkiaITetKxCaKhZANiAATpTb3pcZ+C E9WYGiBho6391NUd5wx15qhGTWT0/2hl2DEvfxHEN/rXJUbG+mzCpvDSvz5IfDDF ryLtXb3fgSYxGA8VLPC24wT0PImF5L7u3Z4vjSmO38qkrIwBMkUKyTU= -----END PRIVATE KEY----- - diff --git a/tests/test_keys/ec-p384-public.pem b/tests/test_keys/ec-p384-public.pem index fe92a0f..1c150b3 100644 --- a/tests/test_keys/ec-p384-public.pem +++ b/tests/test_keys/ec-p384-public.pem @@ -3,4 +3,3 @@ MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE6U296XGfghPVmBogYaOt/dTVHecMdeao Rk1k9P9oZdgxL38RxDf61yVGxvpswqbw0r8+SHwwxa8i7V2934EmMRgPFSzwtuME 9DyJheS+7t2eL40pjt/KpKyMATJFCsk1 -----END PUBLIC KEY----- - diff --git a/tests/test_keys/jwk.json b/tests/test_keys/jwk.json index 790a3c4..70c8a35 100755 --- a/tests/test_keys/jwk.json +++ b/tests/test_keys/jwk.json @@ -9,4 +9,4 @@ "n": "pKybs0WaHU_y4cHxWbm8Wzj66HtcyFn7Fh3n-99qTXu5yNa30MRYIYfSDwe9JVc1JUoGw41yq2StdGBJ40HxichjE-Yopfu3B58QlgJvToUbWD4gmTDGgMGxQxtv1En2yedaynQ73sDpIK-12JJDY55pvf-PCiSQ9OjxZLiVGKlClDus44_uv2370b9IN2JiEOF-a7JBqaTEYLPpXaoKWDSnJNonr79tL0T7iuJmO1l705oO3Y0TQ-INLY6jnKG_RpsvyvGNnwP9pMvcP1phKsWZ10ofuuhJGRp8IxQL9RfzT87OvF0RBSO1U73h09YP-corWDsnKIi6TbzRpN5YDw" } ] -} \ No newline at end of file +} diff --git a/tests/test_keys/rsa-1024-private.pem b/tests/test_keys/rsa-1024-private.pem index e26a5ba..8e50c83 100644 --- a/tests/test_keys/rsa-1024-private.pem +++ b/tests/test_keys/rsa-1024-private.pem @@ -14,4 +14,3 @@ Pixzn7QPPAqeA4txREeckslmhtc+VU7iqSFO1JDqLxmhmdfT5aReOeECQGKfe9gJ oiTy02VzeMQ/2FSdkHG7ZjZYl1+rwA1Y+2eKstg88xpk456ePp4PBJ5MQLEQKvyq wXuCdEjuK8ipOaQ= -----END PRIVATE KEY----- - diff --git a/tests/test_keys/rsa-1024-public.pem b/tests/test_keys/rsa-1024-public.pem index d8cd0dc..97d4540 100644 --- a/tests/test_keys/rsa-1024-public.pem +++ b/tests/test_keys/rsa-1024-public.pem @@ -4,4 +4,3 @@ rbVBGf2Pk7PwiKAep3nko0CWds4MUIRlioCiigE6KSj2eExStNAFDf3jcjOfYpEV kaUsyGmwclKXxAPNRMr6GAJeKlbJqTs5eYd5tpBjdeW2rjkD/W6R77zp+EtuU4jY /zR2YWeqB6GdTnduKwIDAQAB -----END PUBLIC KEY----- - diff --git a/tests/test_keys/rsa-1280-private.pem b/tests/test_keys/rsa-1280-private.pem index 0f8812b..05369ce 100644 --- a/tests/test_keys/rsa-1280-private.pem +++ b/tests/test_keys/rsa-1280-private.pem @@ -17,4 +17,3 @@ xGfx23wjBBY1QGVuE64o5cQn4/mf/qkCUQDpjF2GH+EqEinqTGAHlcEpKomp8cXH ZxVZ7l9KB3sk17aN58GUnuPaIda4D9dbqQjAzqRx6OcQf80oxEE4XUP6nbXkHHZO 0lakd66lHnHikQ== -----END PRIVATE KEY----- - diff --git a/tests/test_keys/rsa-1280-public.pem b/tests/test_keys/rsa-1280-public.pem index 19944cd..ceeb25f 100644 --- a/tests/test_keys/rsa-1280-public.pem +++ b/tests/test_keys/rsa-1280-public.pem @@ -5,4 +5,3 @@ MTQ8VD8/kT5OF1uf8/g7PX87FfSSJwhICgD/jZ5w5jwY9UNngMPLKEE81w5KRr92 895QU7ssSCySZ8LVFK5Wd5wKOSsyqLfZ1WahzaQm8lRiSbgf0dcqLz0H7y59AgMB AAE= -----END PUBLIC KEY----- - diff --git a/tests/test_keys/rsa-2048-private.pem b/tests/test_keys/rsa-2048-private.pem index 39c77b6..bf709e5 100644 --- a/tests/test_keys/rsa-2048-private.pem +++ b/tests/test_keys/rsa-2048-private.pem @@ -26,4 +26,3 @@ d2oejX6A2HSlnsCf1QHOzxpp1j05ZC6JrSFqxZEmNNeqYcahjdBjYXQYd0MaGqIu 42Bj0JRO2Z3K1Udlb0shIt3fCEuTCoOQLPwmHm0cqP97A21ISLfg7BPiI25nbE7V Ip9YGido3N9hQycoqkETeYI= -----END PRIVATE KEY----- - diff --git a/tests/test_keys/rsa-2048-public.pem b/tests/test_keys/rsa-2048-public.pem index e53a9db..880d752 100644 --- a/tests/test_keys/rsa-2048-public.pem +++ b/tests/test_keys/rsa-2048-public.pem @@ -7,4 +7,3 @@ JnUResdM2TfsneTHjh1XI6TZu8UYghHSr5QRNwSAr4NgNzYVU3XPgm9n4QoAeJFy xkcNtwpszeKR4hYeeYmdV6jt3kHZN/VC+W/XqqB9sJp9q20mBFKaddxr/FhaEy4J fQIDAQAB -----END PUBLIC KEY----- - diff --git a/tests/test_keys/rsa-3072-private.pem b/tests/test_keys/rsa-3072-private.pem index f42c0e5..07e00e0 100644 --- a/tests/test_keys/rsa-3072-private.pem +++ b/tests/test_keys/rsa-3072-private.pem @@ -38,4 +38,3 @@ kgW+6uqke4yB603Gj3m1eqxS2bV3BcR7+ZaR5hcu46dTv/p2MZ+LnS3MMhc+43YK 1H60+frQ79QUAiKJm73U6o9al7NZBaTJZrAwfJGygOucprd0RmreCg4LtbSL3nt0 wWHrbeVLAmn0Dlw2zyBc47B9 -----END PRIVATE KEY----- - diff --git a/tests/test_keys/rsa-3072-public.pem b/tests/test_keys/rsa-3072-public.pem index ae6b4b6..6945382 100644 --- a/tests/test_keys/rsa-3072-public.pem +++ b/tests/test_keys/rsa-3072-public.pem @@ -9,4 +9,3 @@ tD85yVC9SSWE/ku9IwCfQ4anvUaYF1CgDA2WuJx4I3q3iKtham1+9q5s5Xt9SO8H VZ4aR26j6eP2ndzILfj5+kw3QOkCPjIWISAtHBr83PnNlvqoxdElN3tfFLfxxSf8 /xqGtbsicV/ZOhn7bni7vq84JpdKCD9HxeCvnLIwQyv5AgMBAAE= -----END PUBLIC KEY----- - diff --git a/tests/test_keys/rsa-4096-private.pem b/tests/test_keys/rsa-4096-private.pem index e36699a..418de08 100644 --- a/tests/test_keys/rsa-4096-private.pem +++ b/tests/test_keys/rsa-4096-private.pem @@ -50,4 +50,3 @@ dfajuukiLycB4Bl3ercPyDLDmPicUEizSO1LW+N5nk7Y2m3tGwrrncoEks19L36A H0Kua1AIg8wrLsE1eoRHaKalCYEo2J5jB8ji5gEnlvqDg4yjZ2NpTRm2HciKsQUL lyJo0JRs+DrjD7/jYUSzLyIODs3bQgk= -----END PRIVATE KEY----- - diff --git a/tests/test_keys/rsa-4096-public.pem b/tests/test_keys/rsa-4096-public.pem index 434e947..329e7c7 100644 --- a/tests/test_keys/rsa-4096-public.pem +++ b/tests/test_keys/rsa-4096-public.pem @@ -12,4 +12,3 @@ mc2wT5xzUrwslbmFPhGA27FGPOt2S1ufZzZPdXJPF0XZkI1IHtHPyWP4oGJBgLsK zBgWD9TzW4zXGlLVnlsdPDeu1P8CeFX1skb6rgiGW7JyciKMqIBd3d7NMuEOxPJe VXLU9LZV6zI7I+Eqc8gz15kCAwEAAQ== -----END PUBLIC KEY----- -