Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add tbsCertList and signature interfaces to CRLs #2489

Merged
merged 2 commits into from
Nov 19, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions docs/x509/reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -504,6 +504,26 @@ X.509 CRL (Certificate Revocation List) Object

The extensions encoded in the CRL.

.. attribute:: signature

.. versionadded:: 1.2

:type: bytes

The bytes of the CRL's signature.

.. attribute:: tbs_certlist_bytes

.. versionadded:: 1.2

:type: bytes

The DER encoded bytes payload (as defined by :rfc:`5280`) that is hashed
and then signed by the private key of the CRL's issuer. This data may be
used to validate a signature, but use extreme caution as CRL validation
is a complex problem that involves much more than just signature checks.


X.509 Certificate Builder
~~~~~~~~~~~~~~~~~~~~~~~~~

Expand Down
3 changes: 3 additions & 0 deletions src/_cffi_src/openssl/x509.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
typedef struct {
X509_CRL_INFO *crl;
X509_ALGOR *sig_alg;
ASN1_BIT_STRING *signature;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this should block, but we'll need to alter this for 1.1.0 using the new function X509_CRL_get0_signature. I'll deal with that in the 1.1.0 WIP PR though.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't realize that. Thanks for taking care of it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, OpenSSL 1.1.0 is opaquing almost every struct. In most cases there are already accessors available, but for this (and the tbs payload) we'll have to define some functions since cffi won't know what the struct looks like.

...;
} X509_CRL;

Expand Down Expand Up @@ -259,6 +260,8 @@

MACROS = """
int i2d_X509_CINF(X509_CINF *, unsigned char **);
int i2d_X509_CRL_INFO(X509_CRL_INFO *, unsigned char **);

long X509_get_version(X509 *);

ASN1_TIME *X509_get_notBefore(X509 *);
Expand Down
15 changes: 15 additions & 0 deletions src/cryptography/hazmat/backends/openssl/x509.py
Original file line number Diff line number Diff line change
Expand Up @@ -818,6 +818,21 @@ def last_update(self):
self._backend.openssl_assert(lu != self._backend._ffi.NULL)
return self._backend._parse_asn1_time(lu)

@property
def signature(self):
return self._backend._asn1_string_to_bytes(self._x509_crl.signature)

@property
def tbs_certlist_bytes(self):
pp = self._backend._ffi.new("unsigned char **")
# the X509_CRL_INFO struct holds the tbsCertList data
res = self._backend._lib.i2d_X509_CRL_INFO(self._x509_crl.crl, pp)
self._backend.openssl_assert(res > 0)
pp = self._backend._ffi.gc(
pp, lambda pointer: self._backend._lib.OPENSSL_free(pointer[0])
)
return self._backend._ffi.buffer(pp[0], res)[:]

def _revoked_certificates(self):
revoked = self._backend._lib.X509_CRL_get_REVOKED(self._x509_crl)
self._backend.openssl_assert(revoked != self._backend._ffi.NULL)
Expand Down
12 changes: 12 additions & 0 deletions src/cryptography/x509/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,18 @@ def extensions(self):
Returns an Extensions object containing a list of CRL extensions.
"""

@abc.abstractproperty
def signature(self):
"""
Returns the signature bytes.
"""

@abc.abstractproperty
def tbs_certlist_bytes(self):
"""
Returns the tbsCertList payload bytes as defined in RFC 5280.
"""

@abc.abstractmethod
def __eq__(self, other):
"""
Expand Down
38 changes: 38 additions & 0 deletions tests/test_x509.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,44 @@ def test_extensions(self, backend):
with pytest.raises(NotImplementedError):
crl.extensions

def test_signature(self, backend):
crl = _load_cert(
os.path.join("x509", "custom", "crl_all_reasons.pem"),
x509.load_pem_x509_crl,
backend
)

assert crl.signature == binascii.unhexlify(
b"536a5a0794f68267361e7bc2f19167a3e667a2ab141535616855d8deb2ba1af"
b"9fd4546b1fe76b454eb436af7b28229fedff4634dfc9dd92254266219ae0ea8"
b"75d9ff972e9a2da23d5945f073da18c50a4265bfed9ca16586347800ef49dd1"
b"6856d7265f4f3c498a57f04dc04404e2bd2e2ada1f5697057aacef779a18371"
b"c621edc9a5c2b8ec1716e8fa22feeb7fcec0ce9156c8d344aa6ae8d1a5d99d0"
b"9386df36307df3b63c83908f4a61a0ff604c1e292ad63b349d1082ddd7ae1b7"
b"c178bba995523ec6999310c54da5706549797bfb1230f5593ba7b4353dade4f"
b"d2be13a57580a6eb20b5c4083f000abac3bf32cd8b75f23e4c8f4b3a79e1e2d"
b"58a472b0"
)

def test_tbs_certlist_bytes(self, backend):
crl = _load_cert(
os.path.join("x509", "PKITS_data", "crls", "GoodCACRL.crl"),
x509.load_der_x509_crl,
backend
)

ca_cert = _load_cert(
os.path.join("x509", "PKITS_data", "certs", "GoodCACert.crt"),
x509.load_der_x509_certificate,
backend
)

verifier = ca_cert.public_key().verifier(
crl.signature, padding.PKCS1v15(), crl.signature_hash_algorithm
)
verifier.update(crl.tbs_certlist_bytes)
verifier.verify()


@pytest.mark.requires_backend_interface(interface=X509Backend)
class TestRevokedCertificate(object):
Expand Down