diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b436298..10d0612 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -23,11 +23,11 @@ jobs: strategy: matrix: python-version: - - "3.9" - "3.10" - "3.11" - "3.12" - "3.13" + - "3.14" steps: - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} 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 diff --git a/pyproject.toml b/pyproject.toml index bd8b719..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" @@ -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" ] @@ -74,7 +74,7 @@ select = [ # isort "I", ] -ignore = ["E501", "I001", "SIM102", "UP006", "UP035"] +ignore = ["E501", "SIM102"] 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/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/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/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 diff --git a/src/cryptojwt/jwt.py b/src/cryptojwt/jwt.py index 19e70e7..e96fb2e 100755 --- a/src/cryptojwt/jwt.py +++ b/src/cryptojwt/jwt.py @@ -5,8 +5,8 @@ import logging import time import uuid +from collections.abc import MutableMapping from json import JSONDecodeError -from typing import Dict, List, MutableMapping, Optional from .exception import HeaderError, VerificationError from .jwe.jwe import JWE, factory as jwe_factory @@ -83,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 @@ -208,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 f27f3f8..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 List, 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 80b7abd..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 List, 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 6c541d3..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 List, Optional from requests import request @@ -55,7 +54,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 @@ -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 @@ -159,7 +158,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 +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: 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"])