Skip to content

Commit

Permalink
Make X509Store.add_crl() accept cryptography CRLs
Browse files Browse the repository at this point in the history
  • Loading branch information
facutuesca committed Oct 23, 2023
1 parent 1bed18f commit fd7de13
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 10 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ Deprecations:
Changes:
^^^^^^^^

- Changed `X509Store.add_crl()` to also accept
`X509.CertificateRevocationList` arguments in addition to the now
deprecated `OpenSSL.crypto.CRL` arguments.

23.2.0 (2023-05-30)
-------------------

Expand Down
26 changes: 23 additions & 3 deletions src/OpenSSL/crypto.py
Original file line number Diff line number Diff line change
Expand Up @@ -1720,7 +1720,9 @@ def add_cert(self, cert: X509) -> None:
res = _lib.X509_STORE_add_cert(self._store, cert._x509)
_openssl_assert(res == 1)

def add_crl(self, crl: "_CRLInternal") -> None:
def add_crl(
self, crl: Union["_CRLInternal", x509.CertificateRevocationList]
) -> None:
"""
Add a certificate revocation list to this store.
Expand All @@ -1730,11 +1732,29 @@ def add_crl(self, crl: "_CRLInternal") -> None:
.. versionadded:: 16.1.0
:param CRL crl: The certificate revocation list to add to this store.
:param crl: The certificate revocation list to add to this store.
:type crl: ``Union[CRL, cryptography.x509.CertificateRevocationList]``
:return: ``None`` if the certificate revocation list was added
successfully.
"""
_openssl_assert(_lib.X509_STORE_add_crl(self._store, crl._crl) != 0)
if isinstance(crl, x509.CertificateRevocationList):
from cryptography.hazmat.primitives.serialization import Encoding

bio = _new_mem_buf(crl.public_bytes(Encoding.DER))
openssl_crl = _lib.d2i_X509_CRL_bio(bio, _ffi.NULL)
if openssl_crl == _ffi.NULL:
_raise_current_error()

crl = _ffi.gc(openssl_crl, _lib.X509_CRL_free)
elif isinstance(crl, _CRLInternal):
crl = crl._crl
else:
raise TypeError(
"CRL must be of type OpenSSL.crypto.CRL or "
"cryptography.x509.CertificateRevocationList"
)

_openssl_assert(_lib.X509_STORE_add_crl(self._store, crl) != 0)

def set_flags(self, flags: int) -> None:
"""
Expand Down
76 changes: 69 additions & 7 deletions tests/test_crypto.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
import flaky
import pytest
from cryptography import x509
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import ec, ed448, ed25519, rsa

from OpenSSL._util import ffi as _ffi
Expand Down Expand Up @@ -3505,7 +3505,8 @@ def test_dump_crl(self):
buf = dump_crl(FILETYPE_PEM, crl)
assert buf == crlData

def _make_test_crl(self, issuer_cert, issuer_key, certs=()):
@staticmethod
def _make_test_crl(issuer_cert, issuer_key, certs=()):
"""
Create a CRL.
Expand All @@ -3531,18 +3532,66 @@ def _make_test_crl(self, issuer_cert, issuer_key, certs=()):
crl.sign(issuer_cert, issuer_key, digest=b"sha512")
return crl

def test_verify_with_revoked(self):
@staticmethod
def _make_test_crl_cryptography(issuer_cert, issuer_key, certs=()):
"""
Create a CRL using cryptography's API.
:param list[X509] certs: A list of certificates to revoke.
:rtype: ``cryptography.x509.CertificateRevocationList``
"""
from cryptography.x509.extensions import CRLReason, ReasonFlags

builder = x509.CertificateRevocationListBuilder()
builder = builder.issuer_name(
X509.to_cryptography(issuer_cert).subject
)
for cert in certs:
revoked = (
x509.RevokedCertificateBuilder()
.serial_number(cert.get_serial_number())
.revocation_date(datetime(2014, 6, 1, 0, 0, 0))
.add_extension(CRLReason(ReasonFlags.unspecified), False)
.build()
)
builder = builder.add_revoked_certificate(revoked)

builder = builder.last_update(datetime(2014, 6, 1, 0, 0, 0))
# The year 5000 is far into the future so that this CRL isn't
# considered to have expired.
builder = builder.next_update(datetime(5000, 6, 1, 0, 0, 0))

crl = builder.sign(
private_key=PKey.to_cryptography_key(issuer_key),
algorithm=hashes.SHA512(),
)
return crl

@pytest.mark.parametrize(
"create_crl",
[
pytest.param(
_make_test_crl.__func__,
id="pyOpenSSL CRL",
),
pytest.param(
_make_test_crl_cryptography.__func__,
id="cryptography CRL",
),
],
)
def test_verify_with_revoked(self, create_crl):
"""
`verify_certificate` raises error when an intermediate certificate is
revoked.
"""
store = X509Store()
store.add_cert(self.root_cert)
store.add_cert(self.intermediate_cert)
root_crl = self._make_test_crl(
root_crl = create_crl(
self.root_cert, self.root_key, certs=[self.intermediate_cert]
)
intermediate_crl = self._make_test_crl(
intermediate_crl = create_crl(
self.intermediate_cert, self.intermediate_key, certs=[]
)
store.add_crl(root_crl)
Expand All @@ -3555,15 +3604,28 @@ def test_verify_with_revoked(self):
store_ctx.verify_certificate()
assert str(err.value) == "certificate revoked"

def test_verify_with_missing_crl(self):
@pytest.mark.parametrize(
"create_crl",
[
pytest.param(
_make_test_crl.__func__,
id="pyOpenSSL CRL",
),
pytest.param(
_make_test_crl_cryptography.__func__,
id="cryptography CRL",
),
],
)
def test_verify_with_missing_crl(self, create_crl):
"""
`verify_certificate` raises error when an intermediate certificate's
CRL is missing.
"""
store = X509Store()
store.add_cert(self.root_cert)
store.add_cert(self.intermediate_cert)
root_crl = self._make_test_crl(
root_crl = create_crl(
self.root_cert, self.root_key, certs=[self.intermediate_cert]
)
store.add_crl(root_crl)
Expand Down

0 comments on commit fd7de13

Please sign in to comment.