--- Begin Message ---
Package: release.debian.org
Severity: normal
X-Debbugs-Cc: python-josepy@packages.debian.org, hlieberman@debian.org
Control: affects -1 + src:python-josepy
User: release.debian.org@packages.debian.org
Usertags: unblock
User: hlieberman@debian.org
Usertags: trixie-certbot
Please unblock package python-josepy
[ Reason ]
The certbot 2.x series is end of life and will not receive further updates or
backports of changes.
(https://github.com/certbot/certbot/wiki/Architectural-Decision-Records-2025#-update-to-certbots-version-policy-and-end-of-life-support-on-previous-major-versions)
By far and away, the primary purpose of certbot is to receive certificates from
Let's Encrypt, and the Let's Encrypt team are planning API changes in 2025 which
will break the issuance of TLS certificates for people using the Certbot 2.x
series.
[ Impact ]
If the unblock is not granted, certbot will suddenly stop working at
some point in the next year and users' TLS certificates will expire. Because
certbot tends to be used as a set-it-and-forget-it system, and Let's Encrypt has
recently disabled their email notifications, users' websites and applications
may suddenly be unavailable to users and/or vulnerable to MitM.
[ Tests ]
Certbot's two primary plugins (python-certbot-apache, python-certbot-nginx) and
the main utility (python-certbot) have a test harness which exercises the entire
process of getting a certificate against a test environment. This provides very
high confidence that those packages are still working, and that the libraries
which they depend on (python-josepy, python-acme) are in good health. These
tests pass cleanly on ci.d.n for all three invocations.
The dns plugin packages (python-certbot-dns-*) are substantially less
complicated than the other certbot packages and primarily handle communication
with various companies' API layers. Those are unlikely to have broken because of
the changes to certbot's internals; the primary way in which those packages
break are due to API changes on the providers' ends.
[ Risks ]
Upgrading the packages across major versions comes with risks, certainly, but
there is little in the way of alternative. The changes are too complex for me to
be willing to attempt to backport, and in a security critical application, I am
even more reticent than I normally would be. I recognize the late application
introduces even more risk --- and rightfully, I'm sure no small amount of
annoyance --- but it is where we've ended up.
[ Checklist ]
[X] all changes are documented in the d/changelog
[X] I reviewed all changes and I approve them
[X] attach debdiff against the package in testing
[ Other info ]
This is one of a series of identical unblock requests with only
package names and debdiffs differing.
unblock python-josepy/2.0.0-1
diff -Nru python-josepy-1.14.0/CHANGELOG.rst python-josepy-2.0.0/CHANGELOG.rst
--- python-josepy-1.14.0/CHANGELOG.rst 2023-11-01 09:36:41.805958700 -0400
+++ python-josepy-2.0.0/CHANGELOG.rst 1969-12-31 19:00:00.000000000 -0500
@@ -1,6 +1,38 @@
Changelog
=========
+2.0.0 (2025-02-10)
+------------------
+
+* Breaking Change: PyOpenSSL has been fully removed.
+ - Dropped objects:
+ `josepy.util.ComparableX509`
+ - Functions now expect `cryptography.x509` objects:
+ `josepy.json_util.encode_cert`
+ `josepy.json_util.encode_csr`
+ `josepy.jws.Header.x5c.encoder`
+ - Functions now return `cryptography.x509` objects:
+ `josepy.json_util.decode_cert`
+ `josepy.json_util.decode_csr`
+ `josepy.jws.Header.x5c.decoder`
+* Dropped support for Python 3.8.
+
+
+1.15.0 (2025-01-22)
+-------------------
+
+* Added a deprecation warning about future backwards incompatible changes. The
+ text of that warning is "The next major version of josepy will remove
+ josepy.util.ComparableX509 and all uses of it as part of removing our
+ dependency on PyOpenSSL. This includes modifying any functions with
+ ComparableX509 parameters or return values. This will be a breaking change.
+ To avoid breakage, we recommend pinning josepy < 2.0.0 until josepy 2.0.0 is
+ out and you've had time to update your code."
+* Added support for Python 3.13.
+* Dropped support for Python 3.7.
+* Support for Python 3.8 has been deprecated and will be removed in the next
+ scheduled release.
+
1.14.0 (2023-11-01)
-------------------
diff -Nru python-josepy-1.14.0/CONTRIBUTING.md python-josepy-2.0.0/CONTRIBUTING.md
--- python-josepy-1.14.0/CONTRIBUTING.md 2023-11-01 09:36:41.806066000 -0400
+++ python-josepy-2.0.0/CONTRIBUTING.md 1969-12-31 19:00:00.000000000 -0500
@@ -28,7 +28,7 @@
If you're a developer, we have some helpful information in our
[Developer's Guide](https://certbot.eff.org/docs/contributing.html) to get you
-started. In particular, we recommend you read these sections
+started. In particular, we recommend you read these sections
- [Finding issues to work on](https://certbot.eff.org/docs/contributing.html#find-issues-to-work-on)
- [Coding style](https://certbot.eff.org/docs/contributing.html#coding-style)
@@ -71,3 +71,10 @@
```bash
$ tox -l
```
+
+## Updating dependencies
+
+Our poetry.lock file is only used during development so security
+vulnerabilities in the pinned packages are rarely relevant. With that said, if
+you want to update package versions, you can use the [`poetry update`
+command](https://python-poetry.org/docs/cli/#update).
diff -Nru python-josepy-1.14.0/debian/changelog python-josepy-2.0.0/debian/changelog
--- python-josepy-1.14.0/debian/changelog 2024-08-31 16:11:35.000000000 -0400
+++ python-josepy-2.0.0/debian/changelog 2025-05-24 14:47:42.000000000 -0400
@@ -1,3 +1,20 @@
+python-josepy (2.0.0-1) unstable; urgency=medium
+
+ * New upstream version 2.0.0 (Closes: #1106463)
+ * Drop patches applied upstream; refresh remaining
+ * Drop dependency on openssl, following upstream (Closes: #1080132)
+ * Drop dependency on importlib.resources (Closes: #1104684)
+ * Bump S-V; no changes needed
+ * Touch all files at build time
+
+ -- Harlan Lieberman-Berg <hlieberman@debian.org> Sat, 24 May 2025 14:47:42 -0400
+
+python-josepy (1.14.0-3) unstable; urgency=medium
+
+ * Add patch to fix intersphinx mapping (Closes: #1090148)
+
+ -- Harlan Lieberman-Berg <hlieberman@debian.org> Wed, 25 Dec 2024 14:54:15 -0500
+
python-josepy (1.14.0-2) unstable; urgency=medium
[ Janitor ]
diff -Nru python-josepy-1.14.0/debian/control python-josepy-2.0.0/debian/control
--- python-josepy-1.14.0/debian/control 2024-08-31 15:52:57.000000000 -0400
+++ python-josepy-2.0.0/debian/control 2025-05-24 14:46:31.000000000 -0400
@@ -8,14 +8,12 @@
pybuild-plugin-pyproject,
python3,
python3-cryptography,
- python3-importlib-resources,
- python3-openssl,
python3-poetry-core,
python3-pytest,
python3-setuptools,
python3-sphinx,
python3-sphinx-rtd-theme
-Standards-Version: 4.6.2
+Standards-Version: 4.7.2
Homepage: https://certbot.eff.org/
Vcs-Git: https://salsa.debian.org/letsencrypt-team/certbot/josepy.git
Vcs-Browser: https://salsa.debian.org/letsencrypt-team/certbot/josepy
diff -Nru python-josepy-1.14.0/debian/patches/0001-remove-files.patch python-josepy-2.0.0/debian/patches/0001-remove-files.patch
--- python-josepy-1.14.0/debian/patches/0001-remove-files.patch 2024-08-31 15:48:55.000000000 -0400
+++ python-josepy-2.0.0/debian/patches/0001-remove-files.patch 2025-05-24 14:25:13.000000000 -0400
@@ -5,7 +5,7 @@
===================================================================
--- josepy.orig/pyproject.toml
+++ josepy/pyproject.toml
-@@ -30,8 +30,6 @@ homepage = "https://github.com/certbot/j
+@@ -29,8 +29,6 @@ homepage = "https://github.com/certbot/j
authors = ["Certbot Project <certbot-dev@eff.org>"]
readme = "README.rst"
include = [
diff -Nru python-josepy-1.14.0/debian/patches/0002-disable-depreciation.patch python-josepy-2.0.0/debian/patches/0002-disable-depreciation.patch
--- python-josepy-1.14.0/debian/patches/0002-disable-depreciation.patch 2024-08-31 16:03:16.000000000 -0400
+++ python-josepy-2.0.0/debian/patches/0002-disable-depreciation.patch 1969-12-31 19:00:00.000000000 -0500
@@ -1,18 +0,0 @@
-Description: Disable depreciation warning relating to pyOpenSSL
-Author: Harlan Lieberman-Berg <hlieberman@debian.org>
-Bug: https://github.com/certbot/josepy/issues/181
-Bug-Debian: https://bugs.debian.org/1080132
-Forwarded: not-needed
-Index: josepy/pyproject.toml
-===================================================================
---- josepy.orig/pyproject.toml
-+++ josepy/pyproject.toml
-@@ -94,7 +94,7 @@ disallow_untyped_defs = true
-
- [tool.pytest.ini_options]
- # We also ignore our own deprecation warning about dropping Python 3.7 support.
--filterwarnings = ["error", "ignore:Python 3.7 support will be dropped:DeprecationWarning"]
-+filterwarnings = ["error", "ignore:Python 3.7 support will be dropped:DeprecationWarning", "ignore:CSR support in pyOpenSSL is deprecated:DeprecationWarning"]
- norecursedirs = "*.egg .eggs dist build docs .tox"
-
- # Isort tooling configuration
diff -Nru python-josepy-1.14.0/debian/patches/0003-fix-intersphinx.patch python-josepy-2.0.0/debian/patches/0003-fix-intersphinx.patch
--- python-josepy-1.14.0/debian/patches/0003-fix-intersphinx.patch 2024-08-31 16:11:35.000000000 -0400
+++ python-josepy-2.0.0/debian/patches/0003-fix-intersphinx.patch 1969-12-31 19:00:00.000000000 -0500
@@ -1,28 +0,0 @@
-From 65849c52c96f4b09806ca600e4845abfd30795c2 Mon Sep 17 00:00:00 2001
-From: Harlan Lieberman-Berg <hlieberman@setec.io>
-Date: Wed, 25 Dec 2024 14:43:33 -0500
-Subject: [PATCH] Switch intersphinx_mapping to new format
-Bug-Debian: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1090148
-Forwarded: https://github.com/certbot/josepy/pull/198
-Author: Harlan Lieberman-Berg <hlieberman@debian.org>
-
-Sphinx v8 no longer supports intersphinx_mapping being a direct map;
-it now must be a map with identifiers and tuples. This fixes a FTBFS
-downstream in Debian, bug #1090148.
----
- docs/conf.py | 4 ++--
- 1 file changed, 2 insertions(+), 2 deletions(-)
-
-Index: josepy/docs/conf.py
-===================================================================
---- josepy.orig/docs/conf.py
-+++ josepy/docs/conf.py
-@@ -185,6 +185,6 @@ texinfo_documents = [
-
- # Example configuration for intersphinx: refer to the Python standard library.
- intersphinx_mapping = {
-- "https://docs.python.org/": None,
-- "https://cryptography.io/en/latest/": None,
-+ 'python': ("https://docs.python.org/", None),
-+ 'cryptography': ("https://cryptography.io/en/latest/", None),
- }
diff -Nru python-josepy-1.14.0/debian/patches/series python-josepy-2.0.0/debian/patches/series
--- python-josepy-1.14.0/debian/patches/series 2024-08-31 16:11:35.000000000 -0400
+++ python-josepy-2.0.0/debian/patches/series 2025-05-24 14:24:45.000000000 -0400
@@ -1,3 +1 @@
0001-remove-files.patch
-0002-disable-depreciation.patch
-0003-fix-intersphinx.patch
diff -Nru python-josepy-1.14.0/debian/rules python-josepy-2.0.0/debian/rules
--- python-josepy-1.14.0/debian/rules 2024-08-31 15:59:04.000000000 -0400
+++ python-josepy-2.0.0/debian/rules 2025-05-24 14:44:41.000000000 -0400
@@ -6,6 +6,7 @@
dh $@ --with python3,sphinxdoc --buildsystem=pybuild
override_dh_auto_build:
+ find $(CURDIR) -print0 | xargs -0 touch -d@${SOURCE_DATE_EPOCH} '{}' \;
dh_auto_build
PYTHONPATH=$(CURDIR)/src \
http_proxy='127.0.0.1:9' \
diff -Nru python-josepy-1.14.0/docs/conf.py python-josepy-2.0.0/docs/conf.py
--- python-josepy-1.14.0/docs/conf.py 2023-11-01 09:36:41.833833500 -0400
+++ python-josepy-2.0.0/docs/conf.py 1969-12-31 19:00:00.000000000 -0500
@@ -63,9 +63,9 @@
# built documents.
#
# The short X.Y version.
-version = u'1.14'
+version = "2.0"
# The full version, including alpha/beta/rc tags.
-release = u'1.14.0'
+release = "2.0.0"
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
@@ -185,6 +185,6 @@
# Example configuration for intersphinx: refer to the Python standard library.
intersphinx_mapping = {
- "https://docs.python.org/": None,
- "https://cryptography.io/en/latest/": None,
+ "python": ("https://docs.python.org/", None),
+ "cryptography": ("https://cryptography.io/en/latest/", None),
}
diff -Nru python-josepy-1.14.0/docs/requirements.txt python-josepy-2.0.0/docs/requirements.txt
--- python-josepy-1.14.0/docs/requirements.txt 2023-11-01 09:36:41.807566200 -0400
+++ python-josepy-2.0.0/docs/requirements.txt 1969-12-31 19:00:00.000000000 -0500
@@ -1,4 +0,0 @@
-# We pin our dependencies with a constraints file for increased stability.
-
--c ../constraints.txt
--e .[docs]
diff -Nru python-josepy-1.14.0/PKG-INFO python-josepy-2.0.0/PKG-INFO
--- python-josepy-1.14.0/PKG-INFO 1969-12-31 19:00:00.000000000 -0500
+++ python-josepy-2.0.0/PKG-INFO 1969-12-31 19:00:00.000000000 -0500
@@ -1,30 +1,28 @@
-Metadata-Version: 2.1
+Metadata-Version: 2.3
Name: josepy
-Version: 1.14.0
+Version: 2.0.0
Summary: JOSE protocol implementation in Python
-Home-page: https://github.com/certbot/josepy
License: Apache-2.0
Author: Certbot Project
Author-email: certbot-dev@eff.org
-Requires-Python: >=3.7,<4.0
+Requires-Python: >=3.9,<4.0
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3.7
-Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
+Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Topic :: Security
Provides-Extra: docs
Requires-Dist: cryptography (>=1.5)
-Requires-Dist: pyopenssl (>=0.13)
Requires-Dist: sphinx (>=4.3.0) ; extra == "docs"
Requires-Dist: sphinx-rtd-theme (>=1.0) ; extra == "docs"
+Project-URL: Homepage, https://github.com/certbot/josepy
Description-Content-Type: text/x-rst
JOSE protocol implementation in Python using cryptography
@@ -38,11 +36,7 @@
.. image:: https://img.shields.io/badge/code%20style-black-000000.svg
:target: https://github.com/psf/black
-Originally developed as part of the ACME_ protocol implementation.
-
-.. _ACME: https://pypi.python.org/pypi/acme
-
-To learn how to contribute to this project, see CONTRIBUTING.md_.
+For more information about contributing to this project, see CONTRIBUTING.md_.
.. _CONTRIBUTING.md: CONTRIBUTING.md
diff -Nru python-josepy-1.14.0/pyproject.toml python-josepy-2.0.0/pyproject.toml
--- python-josepy-1.14.0/pyproject.toml 2023-11-01 09:36:42.032093000 -0400
+++ python-josepy-2.0.0/pyproject.toml 1969-12-31 19:00:00.000000000 -0500
@@ -8,7 +8,7 @@
[tool.poetry]
name = "josepy"
-version = "1.14.0"
+version = "2.0.0"
description = "JOSE protocol implementation in Python"
license = "Apache License 2.0"
classifiers = [
@@ -17,12 +17,11 @@
"License :: OSI Approved :: Apache Software License",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
- "Programming Language :: Python :: 3.7",
- "Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
+ "Programming Language :: Python :: 3.13",
"Topic :: Internet :: WWW/HTTP",
"Topic :: Security",
]
@@ -38,26 +37,19 @@
[tool.poetry.dependencies]
# This should be kept in sync with the value of target-version in our
# configuration for black below.
-python = "^3.7"
+python = "^3.9"
# load_pem_private/public_key (>=0.6)
# rsa_recover_prime_factors (>=0.8)
# add sign() and verify() to asymetric keys (RSA >=1.4, ECDSA >=1.5)
cryptography = ">=1.5"
-# Connection.set_tlsext_host_name (>=0.13)
-pyopenssl = ">=0.13"
# >=4.3.0 is needed for Python 3.10 support
sphinx = {version = ">=4.3.0", optional = true}
sphinx-rtd-theme = {version = ">=1.0", optional = true}
-[tool.poetry.dev-dependencies]
+[tool.poetry.group.dev.dependencies]
# coverage[toml] extra is required to read the coverage config from pyproject.toml
coverage = {version = ">=4.0", extras = ["toml"]}
-# importlib_resources 1.3 was the version included in Python 3.9 which
-# introduced the functionality we are using. See
-# https://github.com/python/importlib_resources/tree/7f4fbb5ee026d7610636d5ece18b09c64aa0c893#compatibility.
-importlib_resources = {version = ">=1.3", python = "<3.9"}
mypy = "*"
-types-pyOpenSSL = "*"
types-pyRFC3339 = "*"
types-requests = "*"
types-setuptools = "*"
@@ -82,7 +74,7 @@
line-length = 100
# This should be kept in sync with the version of Python specified in poetry's
# dependencies above.
-target-version = ['py37', 'py38', 'py39', 'py310', 'py311', 'py312']
+target-version = ['py39', 'py310', 'py311', 'py312', 'py313']
# Mypy tooling configuration
@@ -95,8 +87,9 @@
# Pytest tooling configuration
[tool.pytest.ini_options]
-# We also ignore our own deprecation warning about dropping Python 3.7 support.
-filterwarnings = ["error", "ignore:Python 3.7 support will be dropped:DeprecationWarning"]
+filterwarnings = [
+ "error",
+]
norecursedirs = "*.egg .eggs dist build docs .tox"
# Isort tooling configuration
diff -Nru python-josepy-1.14.0/README.rst python-josepy-2.0.0/README.rst
--- python-josepy-1.14.0/README.rst 2023-11-01 09:36:41.806271000 -0400
+++ python-josepy-2.0.0/README.rst 1969-12-31 19:00:00.000000000 -0500
@@ -9,10 +9,6 @@
.. image:: https://img.shields.io/badge/code%20style-black-000000.svg
:target: https://github.com/psf/black
-Originally developed as part of the ACME_ protocol implementation.
-
-.. _ACME: https://pypi.python.org/pypi/acme
-
-To learn how to contribute to this project, see CONTRIBUTING.md_.
+For more information about contributing to this project, see CONTRIBUTING.md_.
.. _CONTRIBUTING.md: CONTRIBUTING.md
diff -Nru python-josepy-1.14.0/src/josepy/b64.py python-josepy-2.0.0/src/josepy/b64.py
--- python-josepy-1.14.0/src/josepy/b64.py 2023-11-01 09:36:41.808879100 -0400
+++ python-josepy-2.0.0/src/josepy/b64.py 1969-12-31 19:00:00.000000000 -0500
@@ -10,6 +10,7 @@
standard library.
"""
+
import base64
from typing import Union
diff -Nru python-josepy-1.14.0/src/josepy/errors.py python-josepy-2.0.0/src/josepy/errors.py
--- python-josepy-1.14.0/src/josepy/errors.py 2023-11-01 09:36:41.808996700 -0400
+++ python-josepy-2.0.0/src/josepy/errors.py 1969-12-31 19:00:00.000000000 -0500
@@ -1,4 +1,5 @@
"""JOSE errors."""
+
from typing import Any
diff -Nru python-josepy-1.14.0/src/josepy/__init__.py python-josepy-2.0.0/src/josepy/__init__.py
--- python-josepy-1.14.0/src/josepy/__init__.py 2023-11-01 09:36:41.808747000 -0400
+++ python-josepy-2.0.0/src/josepy/__init__.py 1969-12-31 19:00:00.000000000 -0500
@@ -25,8 +25,6 @@
.. _ACME: https://pypi.python.org/pypi/acme
"""
-import sys
-import warnings
# flake8: noqa
from josepy.b64 import b64decode, b64encode
@@ -72,13 +70,5 @@
ComparableECKey,
ComparableKey,
ComparableRSAKey,
- ComparableX509,
ImmutableMap,
)
-
-if sys.version_info[:2] == (3, 7):
- warnings.warn(
- "Python 3.7 support will be dropped in the next scheduled release of "
- "josepy. Please upgrade your Python version.",
- DeprecationWarning,
- )
diff -Nru python-josepy-1.14.0/src/josepy/interfaces.py python-josepy-2.0.0/src/josepy/interfaces.py
--- python-josepy-1.14.0/src/josepy/interfaces.py 2023-11-01 09:36:41.809141400 -0400
+++ python-josepy-2.0.0/src/josepy/interfaces.py 1969-12-31 19:00:00.000000000 -0500
@@ -1,4 +1,5 @@
"""JOSE interfaces."""
+
import abc
import json
from collections.abc import Mapping, Sequence
diff -Nru python-josepy-1.14.0/src/josepy/json_util.py python-josepy-2.0.0/src/josepy/json_util.py
--- python-josepy-1.14.0/src/josepy/json_util.py 2023-11-01 09:36:41.809327800 -0400
+++ python-josepy-2.0.0/src/josepy/json_util.py 1969-12-31 19:00:00.000000000 -0500
@@ -6,6 +6,7 @@
.. _`Go's "json" package`: http://golang.org/pkg/encoding/json/
"""
+
import abc
import binascii
import logging
@@ -21,7 +22,8 @@
TypeVar,
)
-from OpenSSL import crypto
+from cryptography import x509
+from cryptography.hazmat.primitives.serialization import Encoding
from josepy import b64, errors, interfaces, util
@@ -425,59 +427,65 @@
raise errors.DeserializationError(error)
-def encode_cert(cert: util.ComparableX509) -> str:
+def encode_cert(cert: x509.Certificate) -> str:
"""Encode certificate as JOSE Base-64 DER.
- :type cert: `OpenSSL.crypto.X509` wrapped in `.ComparableX509`
+ :type cert: `cryptography.x509.Certificate`
:rtype: unicode
+ .. versionchanged:: 2.0.0
+ The `cert` parameter is now `cryptography.x509.Certificate`.
+ Previously this was an `josepy.util.ComparableX509` object, which wrapped
+ an `OpenSSL.crypto.X509` object.
"""
- if isinstance(cert.wrapped, crypto.X509Req):
- raise ValueError("Error input is actually a certificate request.")
-
- return encode_b64jose(crypto.dump_certificate(crypto.FILETYPE_ASN1, cert.wrapped))
+ return encode_b64jose(cert.public_bytes(Encoding.DER))
-def decode_cert(b64der: str) -> util.ComparableX509:
+def decode_cert(b64der: str) -> x509.Certificate:
"""Decode JOSE Base-64 DER-encoded certificate.
:param unicode b64der:
- :rtype: `OpenSSL.crypto.X509` wrapped in `.ComparableX509`
+ :rtype: `cryptography.x509.Certificate`
+ .. versionchanged:: 2.0.0
+ The returned object is now a `cryptography.x509.Certificate`.
+ Previously this was an `josepy.util.ComparableX509` object, which wrapped
+ an `OpenSSL.crypto.X509` object.
"""
try:
- return util.ComparableX509(
- crypto.load_certificate(crypto.FILETYPE_ASN1, decode_b64jose(b64der))
- )
- except crypto.Error as error:
+ return x509.load_der_x509_certificate(decode_b64jose(b64der))
+ except ValueError as error:
raise errors.DeserializationError(error)
-def encode_csr(csr: util.ComparableX509) -> str:
+def encode_csr(csr: x509.CertificateSigningRequest) -> str:
"""Encode CSR as JOSE Base-64 DER.
- :type csr: `OpenSSL.crypto.X509Req` wrapped in `.ComparableX509`
+ :type csr: `cryptography.x509.CertificateSigningRequest`
:rtype: unicode
+ .. versionchanged:: 2.0.0
+ The `cert` parameter is now `cryptography.x509.CertificateSigningRequest`.
+ Previously this was an `josepy.util.ComparableX509` object, which wrapped
+ an `OpenSSL.crypto.X509Req` object.
"""
- if isinstance(csr.wrapped, crypto.X509):
- raise ValueError("Error input is actually a certificate.")
-
- return encode_b64jose(crypto.dump_certificate_request(crypto.FILETYPE_ASN1, csr.wrapped))
+ return encode_b64jose(csr.public_bytes(Encoding.DER))
-def decode_csr(b64der: str) -> util.ComparableX509:
+def decode_csr(b64der: str) -> x509.CertificateSigningRequest:
"""Decode JOSE Base-64 DER-encoded CSR.
:param unicode b64der:
- :rtype: `OpenSSL.crypto.X509Req` wrapped in `.ComparableX509`
+ :rtype: `cryptography.x509.CertificateSigningRequest`
+ .. versionchanged:: 2.0.0
+ The returned object is now a `cryptography.x509.CertificateSigningRequest`.
+ Previously this was an `josepy.util.ComparableX509` object, which wrapped
+ an `OpenSSL.crypto.X509Req` object.
"""
try:
- return util.ComparableX509(
- crypto.load_certificate_request(crypto.FILETYPE_ASN1, decode_b64jose(b64der))
- )
- except crypto.Error as error:
+ return x509.load_der_x509_csr(decode_b64jose(b64der))
+ except ValueError as error:
raise errors.DeserializationError(error)
diff -Nru python-josepy-1.14.0/src/josepy/jwa.py python-josepy-2.0.0/src/josepy/jwa.py
--- python-josepy-1.14.0/src/josepy/jwa.py 2023-11-01 09:36:41.809478800 -0400
+++ python-josepy-2.0.0/src/josepy/jwa.py 1969-12-31 19:00:00.000000000 -0500
@@ -3,6 +3,7 @@
https://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-40
"""
+
import abc
import logging
from collections.abc import Hashable
diff -Nru python-josepy-1.14.0/src/josepy/jwk.py python-josepy-2.0.0/src/josepy/jwk.py
--- python-josepy-1.14.0/src/josepy/jwk.py 2023-11-01 09:36:41.809641400 -0400
+++ python-josepy-2.0.0/src/josepy/jwk.py 1969-12-31 19:00:00.000000000 -0500
@@ -1,4 +1,5 @@
"""JSON Web Key."""
+
import abc
import json
import logging
diff -Nru python-josepy-1.14.0/src/josepy/jws.py python-josepy-2.0.0/src/josepy/jws.py
--- python-josepy-1.14.0/src/josepy/jws.py 2023-11-01 09:36:41.809802000 -0400
+++ python-josepy-2.0.0/src/josepy/jws.py 1969-12-31 19:00:00.000000000 -0500
@@ -1,4 +1,5 @@
"""JSON Web Signature."""
+
import argparse
import base64
import sys
@@ -14,12 +15,12 @@
cast,
)
-from OpenSSL import crypto
+from cryptography import x509
+from cryptography.hazmat.primitives.serialization import Encoding
import josepy
from josepy import b64, errors, json_util, jwa
from josepy import jwk as jwk_mod
-from josepy import util
class MediaType:
@@ -79,7 +80,7 @@
)
kid: Optional[str] = json_util.field("kid", omitempty=True)
x5u: Optional[bytes] = json_util.field("x5u", omitempty=True)
- x5c: Tuple[util.ComparableX509, ...] = json_util.field("x5c", omitempty=True, default=())
+ x5c: Tuple[x509.Certificate, ...] = json_util.field("x5c", omitempty=True, default=())
x5t: Optional[bytes] = json_util.field("x5t", decoder=json_util.decode_b64jose, omitempty=True)
x5tS256: Optional[bytes] = json_util.field(
"x5t#S256", decoder=json_util.decode_b64jose, omitempty=True
@@ -137,21 +138,25 @@
@x5c.encoder # type: ignore
def x5c(value):
- return [
- base64.b64encode(crypto.dump_certificate(crypto.FILETYPE_ASN1, cert.wrapped))
- for cert in value
- ]
+ """
+ .. versionchanged:: 2.0.0
+ The values are now `cryptography.x509.Certificate` objects.
+ Previously these were `josepy.util.ComparableX509` objects, which wrapped
+ `OpenSSL.crypto.X509` objects.
+ """
+ return [base64.b64encode(cert.public_bytes(Encoding.DER)) for cert in value]
@x5c.decoder # type: ignore
def x5c(value):
+ """
+ .. versionchanged:: 2.0.0
+ The values are now `cryptography.x509.Certificate` objects.
+ Previously these were `josepy.util.ComparableX509` objects, which wrapped
+ `OpenSSL.crypto.X509` objects.
+ """
try:
- return tuple(
- util.ComparableX509(
- crypto.load_certificate(crypto.FILETYPE_ASN1, base64.b64decode(cert))
- )
- for cert in value
- )
- except crypto.Error as error:
+ return tuple(x509.load_der_x509_certificate(base64.b64decode(cert)) for cert in value)
+ except ValueError as error:
raise errors.DeserializationError(error)
diff -Nru python-josepy-1.14.0/src/josepy/magic_typing.py python-josepy-2.0.0/src/josepy/magic_typing.py
--- python-josepy-1.14.0/src/josepy/magic_typing.py 2023-11-01 09:36:41.809918900 -0400
+++ python-josepy-2.0.0/src/josepy/magic_typing.py 1969-12-31 19:00:00.000000000 -0500
@@ -1,4 +1,5 @@
"""Shim class to not have to depend on typing module in prod."""
+
# mypy: ignore-errors
import sys
import warnings
diff -Nru python-josepy-1.14.0/src/josepy/util.py python-josepy-2.0.0/src/josepy/util.py
--- python-josepy-1.14.0/src/josepy/util.py 2023-11-01 09:36:41.810105600 -0400
+++ python-josepy-2.0.0/src/josepy/util.py 1969-12-31 19:00:00.000000000 -0500
@@ -1,4 +1,5 @@
"""JOSE utilities."""
+
import abc
import sys
import warnings
@@ -7,7 +8,6 @@
from typing import Any, Callable, Iterator, List, Tuple, TypeVar, Union, cast
from cryptography.hazmat.primitives.asymmetric import ec, rsa
-from OpenSSL import crypto
# Deprecated. Please use built-in decorators @classmethod and abc.abstractmethod together instead.
@@ -15,51 +15,6 @@
return classmethod(abc.abstractmethod(func))
-class ComparableX509:
- """Wrapper for OpenSSL.crypto.X509** objects that supports __eq__.
-
- :ivar wrapped: Wrapped certificate or certificate request.
- :type wrapped: `OpenSSL.crypto.X509` or `OpenSSL.crypto.X509Req`.
-
- """
-
- def __init__(self, wrapped: Union[crypto.X509, crypto.X509Req]) -> None:
- assert isinstance(wrapped, crypto.X509) or isinstance(wrapped, crypto.X509Req)
- self.wrapped = wrapped
-
- def __getattr__(self, name: str) -> Any:
- return getattr(self.wrapped, name)
-
- def _dump(self, filetype: int = crypto.FILETYPE_ASN1) -> bytes:
- """Dumps the object into a buffer with the specified encoding.
-
- :param int filetype: The desired encoding. Should be one of
- `OpenSSL.crypto.FILETYPE_ASN1`,
- `OpenSSL.crypto.FILETYPE_PEM`, or
- `OpenSSL.crypto.FILETYPE_TEXT`.
-
- :returns: Encoded X509 object.
- :rtype: bytes
-
- """
- if isinstance(self.wrapped, crypto.X509):
- return crypto.dump_certificate(filetype, self.wrapped)
-
- # assert in __init__ makes sure this is X509Req
- return crypto.dump_certificate_request(filetype, self.wrapped)
-
- def __eq__(self, other: Any) -> bool:
- if not isinstance(other, self.__class__):
- return NotImplemented
- return self._dump() == other._dump()
-
- def __hash__(self) -> int:
- return hash((self.__class__, self._dump()))
-
- def __repr__(self) -> str:
- return "<{0}({1!r})>".format(self.__class__.__name__, self.wrapped)
-
-
class ComparableKey:
"""Comparable wrapper for ``cryptography`` keys.
diff -Nru python-josepy-1.14.0/tests/b64_test.py python-josepy-2.0.0/tests/b64_test.py
--- python-josepy-1.14.0/tests/b64_test.py 2023-11-01 09:36:41.810268000 -0400
+++ python-josepy-2.0.0/tests/b64_test.py 1969-12-31 19:00:00.000000000 -0500
@@ -1,4 +1,5 @@
"""Tests for josepy.b64."""
+
import sys
from typing import Union
diff -Nru python-josepy-1.14.0/tests/errors_test.py python-josepy-2.0.0/tests/errors_test.py
--- python-josepy-1.14.0/tests/errors_test.py 2023-11-01 09:36:41.810395700 -0400
+++ python-josepy-2.0.0/tests/errors_test.py 1969-12-31 19:00:00.000000000 -0500
@@ -1,4 +1,5 @@
"""Tests for josepy.errors."""
+
import sys
import unittest
diff -Nru python-josepy-1.14.0/tests/init_test.py python-josepy-2.0.0/tests/init_test.py
--- python-josepy-1.14.0/tests/init_test.py 2023-11-01 09:36:41.810521000 -0400
+++ python-josepy-2.0.0/tests/init_test.py 1969-12-31 19:00:00.000000000 -0500
@@ -1,25 +0,0 @@
-import importlib
-import re
-import sys
-import warnings
-
-import pytest
-
-import josepy
-
-
-@pytest.mark.skipif(sys.version_info[:2] != (3, 7), reason="requires Python 3.7")
-def test_warns() -> None:
- with pytest.warns(DeprecationWarning, match=re.escape(r"Python 3.7 support")):
- importlib.reload(josepy)
-
-
-@pytest.mark.skipif(sys.version_info[:2] == (3, 7), reason="requires Python != 3.7")
-def test_does_not_warn() -> None:
- with warnings.catch_warnings():
- warnings.simplefilter("error")
- importlib.reload(josepy)
-
-
-if __name__ == "__main__":
- sys.exit(pytest.main(sys.argv[1:] + [__file__])) # pragma: no cover
diff -Nru python-josepy-1.14.0/tests/interfaces_test.py python-josepy-2.0.0/tests/interfaces_test.py
--- python-josepy-1.14.0/tests/interfaces_test.py 2023-11-01 09:36:41.810647000 -0400
+++ python-josepy-2.0.0/tests/interfaces_test.py 1969-12-31 19:00:00.000000000 -0500
@@ -1,4 +1,5 @@
"""Tests for josepy.interfaces."""
+
import sys
import unittest
from typing import Any, Dict, List
diff -Nru python-josepy-1.14.0/tests/json_util_test.py python-josepy-2.0.0/tests/json_util_test.py
--- python-josepy-1.14.0/tests/json_util_test.py 2023-11-01 09:36:41.810800800 -0400
+++ python-josepy-2.0.0/tests/json_util_test.py 1969-12-31 19:00:00.000000000 -0500
@@ -1,4 +1,5 @@
"""Tests for josepy.json_util."""
+
import itertools
import sys
import unittest
@@ -7,11 +8,12 @@
import pytest
import test_util
+from cryptography import x509
from josepy import errors, interfaces, util
-CERT = test_util.load_comparable_cert("cert.pem")
-CSR = test_util.load_comparable_csr("csr.pem")
+CERT = test_util.load_cert("cert.pem")
+CSR = test_util.load_csr("csr.pem")
class FieldTest(unittest.TestCase):
@@ -326,7 +328,7 @@
from josepy.json_util import decode_cert
cert = decode_cert(self.b64_cert)
- assert isinstance(cert, util.ComparableX509)
+ assert isinstance(cert, x509.Certificate)
assert cert == CERT
with pytest.raises(errors.DeserializationError):
decode_cert("")
@@ -340,7 +342,7 @@
from josepy.json_util import decode_csr
csr = decode_csr(self.b64_csr)
- assert isinstance(csr, util.ComparableX509)
+ assert isinstance(csr, x509.CertificateSigningRequest)
assert csr == CSR
with pytest.raises(errors.DeserializationError):
decode_csr("")
diff -Nru python-josepy-1.14.0/tests/jwa_test.py python-josepy-2.0.0/tests/jwa_test.py
--- python-josepy-1.14.0/tests/jwa_test.py 2023-11-01 09:36:41.810944800 -0400
+++ python-josepy-2.0.0/tests/jwa_test.py 1969-12-31 19:00:00.000000000 -0500
@@ -1,4 +1,5 @@
"""Tests for josepy.jwa."""
+
import sys
import unittest
from typing import Any
diff -Nru python-josepy-1.14.0/tests/jwk_test.py python-josepy-2.0.0/tests/jwk_test.py
--- python-josepy-1.14.0/tests/jwk_test.py 2023-11-01 09:36:41.811095200 -0400
+++ python-josepy-2.0.0/tests/jwk_test.py 1969-12-31 19:00:00.000000000 -0500
@@ -1,4 +1,5 @@
"""Tests for josepy.jwk."""
+
import binascii
import sys
import unittest
diff -Nru python-josepy-1.14.0/tests/jws_test.py python-josepy-2.0.0/tests/jws_test.py
--- python-josepy-1.14.0/tests/jws_test.py 2023-11-01 09:36:41.811241600 -0400
+++ python-josepy-2.0.0/tests/jws_test.py 1969-12-31 19:00:00.000000000 -0500
@@ -1,16 +1,18 @@
"""Tests for josepy.jws."""
+
import base64
import sys
import unittest
from unittest import mock
-import OpenSSL
import pytest
import test_util
+from cryptography import x509
+from cryptography.hazmat.primitives.serialization import Encoding
from josepy import errors, json_util, jwa, jwk
-CERT = test_util.load_comparable_cert("cert.pem")
+CERT = test_util.load_cert("cert.pem")
KEY = jwk.JWKRSA.load(test_util.load_vector("rsa512_key.pem"))
@@ -71,8 +73,8 @@
header = Header(x5c=(CERT, CERT))
jobj = header.to_partial_json()
- assert isinstance(CERT.wrapped, OpenSSL.crypto.X509)
- cert_asn1 = OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_ASN1, CERT.wrapped)
+ assert isinstance(CERT, x509.Certificate)
+ cert_asn1 = CERT.public_bytes(Encoding.DER)
cert_b64 = base64.b64encode(cert_asn1)
assert jobj == {"x5c": [cert_b64, cert_b64]}
assert header == Header.from_json(jobj)
diff -Nru python-josepy-1.14.0/tests/magic_typing_test.py python-josepy-2.0.0/tests/magic_typing_test.py
--- python-josepy-1.14.0/tests/magic_typing_test.py 2023-11-01 09:36:41.811362300 -0400
+++ python-josepy-2.0.0/tests/magic_typing_test.py 1969-12-31 19:00:00.000000000 -0500
@@ -1,4 +1,5 @@
"""Tests for josepy.magic_typing."""
+
import sys
import warnings
from unittest import mock
diff -Nru python-josepy-1.14.0/tests/test_util.py python-josepy-2.0.0/tests/test_util.py
--- python-josepy-1.14.0/tests/test_util.py 2023-11-01 09:36:41.811495300 -0400
+++ python-josepy-2.0.0/tests/test_util.py 1969-12-31 19:00:00.000000000 -0500
@@ -1,26 +1,20 @@
"""Test utilities."""
+
import atexit
import contextlib
+import importlib.resources
import os
-import sys
from typing import Any
+from cryptography import x509
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization
-from OpenSSL import crypto
import josepy.util
-from josepy import ComparableRSAKey, ComparableX509
+from josepy import ComparableRSAKey
from josepy.util import ComparableECKey
-# This approach is based on the recommendation at
-# https://github.com/python/mypy/issues/1153#issuecomment-1207333806.
-if sys.version_info >= (3, 9):
- import importlib.resources as importlib_resources
-else:
- import importlib_resources
-
-TESTDATA = importlib_resources.files("testdata")
+TESTDATA = importlib.resources.files("testdata")
def vector_path(*names: str) -> str:
@@ -32,7 +26,7 @@
ref = TESTDATA.joinpath(*names)
# We convert the value to str here because some of the calling code doesn't
# work with pathlib objects.
- return str(file_manager.enter_context(importlib_resources.as_file(ref)))
+ return str(file_manager.enter_context(importlib.resources.as_file(ref)))
def load_vector(*names: str) -> bytes:
@@ -50,26 +44,18 @@
raise ValueError("Loader could not be recognized based on extension")
-def load_cert(*names: str) -> crypto.X509:
+def load_cert(*names: str) -> x509.Certificate:
"""Load certificate."""
- loader = _guess_loader(names[-1], crypto.FILETYPE_PEM, crypto.FILETYPE_ASN1)
- return crypto.load_certificate(loader, load_vector(*names))
-
-
-def load_comparable_cert(*names: str) -> josepy.util.ComparableX509:
- """Load ComparableX509 cert."""
- return ComparableX509(load_cert(*names))
+ loader = _guess_loader(
+ names[-1], x509.load_pem_x509_certificate, x509.load_der_x509_certificate
+ )
+ return loader(load_vector(*names))
-def load_csr(*names: str) -> crypto.X509Req:
+def load_csr(*names: str) -> x509.CertificateSigningRequest:
"""Load certificate request."""
- loader = _guess_loader(names[-1], crypto.FILETYPE_PEM, crypto.FILETYPE_ASN1)
- return crypto.load_certificate_request(loader, load_vector(*names))
-
-
-def load_comparable_csr(*names: str) -> josepy.util.ComparableX509:
- """Load ComparableX509 certificate request."""
- return ComparableX509(load_csr(*names))
+ loader = _guess_loader(names[-1], x509.load_pem_x509_csr, x509.load_der_x509_csr)
+ return loader(load_vector(*names))
def load_rsa_private_key(*names: str) -> josepy.util.ComparableRSAKey:
@@ -86,9 +72,3 @@
names[-1], serialization.load_pem_private_key, serialization.load_der_private_key
)
return ComparableECKey(loader(load_vector(*names), password=None, backend=default_backend()))
-
-
-def load_pyopenssl_private_key(*names: str) -> crypto.PKey:
- """Load pyOpenSSL private key."""
- loader = _guess_loader(names[-1], crypto.FILETYPE_PEM, crypto.FILETYPE_ASN1)
- return crypto.load_privatekey(loader, load_vector(*names))
diff -Nru python-josepy-1.14.0/tests/util_test.py python-josepy-2.0.0/tests/util_test.py
--- python-josepy-1.14.0/tests/util_test.py 2023-11-01 09:36:41.813426700 -0400
+++ python-josepy-2.0.0/tests/util_test.py 1969-12-31 19:00:00.000000000 -0500
@@ -1,4 +1,5 @@
"""Tests for josepy.util."""
+
import functools
import sys
import unittest
@@ -7,46 +8,6 @@
import test_util
-class ComparableX509Test(unittest.TestCase):
- """Tests for josepy.util.ComparableX509."""
-
- def setUp(self) -> None:
- # test_util.load_comparable_{csr,cert} return ComparableX509
- self.req1 = test_util.load_comparable_csr("csr.pem")
- self.req2 = test_util.load_comparable_csr("csr.pem")
- self.req_other = test_util.load_comparable_csr("csr-san.pem")
-
- self.cert1 = test_util.load_comparable_cert("cert.pem")
- self.cert2 = test_util.load_comparable_cert("cert.pem")
- self.cert_other = test_util.load_comparable_cert("cert-san.pem")
-
- def test_getattr_proxy(self) -> None:
- assert self.cert1.has_expired() is True
-
- def test_eq(self) -> None:
- assert self.req1 == self.req2
- assert self.cert1 == self.cert2
-
- def test_ne(self) -> None:
- assert self.req1 != self.req_other
- assert self.cert1 != self.cert_other
-
- def test_ne_wrong_types(self) -> None:
- assert self.req1 != 5
- assert self.cert1 != 5
-
- def test_hash(self) -> None:
- assert hash(self.req1) == hash(self.req2)
- assert hash(self.req1) != hash(self.req_other)
-
- assert hash(self.cert1) == hash(self.cert2)
- assert hash(self.cert1) != hash(self.cert_other)
-
- def test_repr(self) -> None:
- for x509 in self.req1, self.cert1:
- assert repr(x509) == "<ComparableX509({0!r})>".format(x509.wrapped)
-
-
class ComparableRSAKeyTest(unittest.TestCase):
"""Tests for josepy.util.ComparableRSAKey."""
--- End Message ---