From 23c0bbc2dc007aa507b22f407c3a0048be98a1a4 Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Fri, 25 Dec 2015 22:35:19 -0600 Subject: add invaliditydate class for crl entry extensions --- CHANGELOG.rst | 2 +- docs/x509/reference.rst | 27 ++++++++++++++++++++++- src/cryptography/hazmat/backends/openssl/x509.py | 4 +++- src/cryptography/x509/__init__.py | 3 ++- src/cryptography/x509/extensions.py | 28 ++++++++++++++++++++++++ tests/test_x509.py | 6 ++--- tests/test_x509_ext.py | 24 ++++++++++++++++++++ 7 files changed, 87 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 4052ff50..39c6f611 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -16,7 +16,7 @@ Changelog * :class:`~cryptography.x509.CertificateIssuer` * :class:`~cryptography.x509.CRLReason` - * ``InvalidityDate`` + * :class:`~cryptography.x509.InvalidityDate` * The :class:`~cryptography.x509.Certificate` class now has :attr:`~cryptography.x509.Certificate.signature` and :attr:`~cryptography.x509.Certificate.tbs_certificate_bytes` attributes. diff --git a/docs/x509/reference.rst b/docs/x509/reference.rst index 51de0747..72fd44be 100644 --- a/docs/x509/reference.rst +++ b/docs/x509/reference.rst @@ -907,7 +907,7 @@ X.509 Revoked Certificate Object >>> for ext in revoked_certificate.extensions: ... print(ext) - , critical=False, value=2015-01-01 00:00:00)> + , critical=False, value=)> , critical=False, value=)> X.509 Revoked Certificate Builder @@ -2011,6 +2011,31 @@ These extensions are only valid within a :class:`RevokedCertificate` object. :type: An element from :class:`~cryptography.x509.ReasonFlags` +.. class:: InvalidityDate(invalidity_date) + + .. versionadded:: 1.2 + + Invalidity date is an extension that is only valid inside + :class:`~cryptography.x509.RevokedCertificate` objects. It provides + the date on which it is known or suspected that the private key was + compromised or that the certificate otherwise became invalid. + This date may be earlier than the revocation date in the CRL entry, + which is the date at which the CA processed the revocation. + + :param invalidity_date: The :class:`datetime.datetime` when it is known + or suspected that the private key was compromised. + + .. attribute:: oid + + :type: :class:`ObjectIdentifier` + + Returns + :attr:`~cryptography.x509.oid.CRLEntryExtensionOID.INVALIDITY_DATE`. + + .. attribute:: invalidity_date + + :type: :class:`datetime.datetime` + Object Identifiers ~~~~~~~~~~~~~~~~~~ diff --git a/src/cryptography/hazmat/backends/openssl/x509.py b/src/cryptography/hazmat/backends/openssl/x509.py index 2650b5d4..1376ab72 100644 --- a/src/cryptography/hazmat/backends/openssl/x509.py +++ b/src/cryptography/hazmat/backends/openssl/x509.py @@ -717,7 +717,9 @@ def _decode_invalidity_date(backend, inv_date): backend._ffi.cast("ASN1_STRING *", generalized_time) ) ).decode("ascii") - return datetime.datetime.strptime(time, "%Y%m%d%H%M%SZ") + return x509.InvalidityDate( + datetime.datetime.strptime(time, "%Y%m%d%H%M%SZ") + ) def _decode_cert_issuer(backend, ext): diff --git a/src/cryptography/x509/__init__.py b/src/cryptography/x509/__init__.py index 89e7f063..dc19161e 100644 --- a/src/cryptography/x509/__init__.py +++ b/src/cryptography/x509/__init__.py @@ -18,7 +18,7 @@ from cryptography.x509.extensions import ( CRLNumber, CRLReason, CertificateIssuer, CertificatePolicies, DistributionPoint, DuplicateExtension, ExtendedKeyUsage, Extension, ExtensionNotFound, ExtensionType, Extensions, GeneralNames, - InhibitAnyPolicy, IssuerAlternativeName, KeyUsage, + InhibitAnyPolicy, InvalidityDate, IssuerAlternativeName, KeyUsage, NameConstraints, NoticeReference, OCSPNoCheck, PolicyInformation, ReasonFlags, SubjectAlternativeName, SubjectKeyIdentifier, UnsupportedExtension, UserNotice @@ -168,4 +168,5 @@ __all__ = [ "CRLExtensionOID", "CertificateIssuer", "CRLReason", + "InvalidityDate", ] diff --git a/src/cryptography/x509/extensions.py b/src/cryptography/x509/extensions.py index 6ae00927..22cba682 100644 --- a/src/cryptography/x509/extensions.py +++ b/src/cryptography/x509/extensions.py @@ -5,6 +5,7 @@ from __future__ import absolute_import, division, print_function import abc +import datetime import hashlib import ipaddress from enum import Enum @@ -1001,3 +1002,30 @@ class CRLReason(object): return not self == other reason = utils.read_only_property("_reason") + + +@utils.register_interface(ExtensionType) +class InvalidityDate(object): + oid = CRLEntryExtensionOID.INVALIDITY_DATE + + def __init__(self, invalidity_date): + if not isinstance(invalidity_date, datetime.datetime): + raise TypeError("invalidity_date must be a datetime.datetime") + + self._invalidity_date = invalidity_date + + def __repr__(self): + return "".format( + self._invalidity_date + ) + + def __eq__(self, other): + if not isinstance(other, InvalidityDate): + return NotImplemented + + return self.invalidity_date == other.invalidity_date + + def __ne__(self, other): + return not self == other + + invalidity_date = utils.read_only_property("_invalidity_date") diff --git a/tests/test_x509.py b/tests/test_x509.py index 757df442..560324b0 100644 --- a/tests/test_x509.py +++ b/tests/test_x509.py @@ -387,9 +387,9 @@ class TestRevokedCertificate(object): x509.CertificateIssuer).value assert issuer == x509.CertificateIssuer(exp_issuer) - date = rev1.extensions.get_extension_for_oid( - x509.OID_INVALIDITY_DATE).value - assert date == datetime.datetime(2015, 1, 1, 0, 0) + date = rev1.extensions.get_extension_for_class( + x509.InvalidityDate).value + assert date == x509.InvalidityDate(datetime.datetime(2015, 1, 1, 0, 0)) # Check if all reason flags can be found in the CRL. flags = set(x509.ReasonFlags) diff --git a/tests/test_x509_ext.py b/tests/test_x509_ext.py index b8105a4b..91a07654 100644 --- a/tests/test_x509_ext.py +++ b/tests/test_x509_ext.py @@ -5,6 +5,7 @@ from __future__ import absolute_import, division, print_function import binascii +import datetime import ipaddress import os @@ -135,6 +136,29 @@ class TestCRLReason(object): ) +class TestInvalidityDate(object): + def test_invalid_invalidity_date(self): + with pytest.raises(TypeError): + x509.InvalidityDate("notadate") + + def test_eq(self): + invalid1 = x509.InvalidityDate(datetime.datetime(2015, 1, 1, 1, 1)) + invalid2 = x509.InvalidityDate(datetime.datetime(2015, 1, 1, 1, 1)) + assert invalid1 == invalid2 + + def test_ne(self): + invalid1 = x509.InvalidityDate(datetime.datetime(2015, 1, 1, 1, 1)) + invalid2 = x509.InvalidityDate(datetime.datetime(2015, 1, 1, 1, 2)) + assert invalid1 != invalid2 + assert invalid1 != object() + + def test_repr(self): + invalid1 = x509.InvalidityDate(datetime.datetime(2015, 1, 1, 1, 1)) + assert repr(invalid1) == ( + "" + ) + + class TestNoticeReference(object): def test_notice_numbers_not_all_int(self): with pytest.raises(TypeError): -- cgit v1.2.3 From 67cde769bd98f701e88e051b74a9db7ae4f03f00 Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Sat, 26 Dec 2015 11:37:14 -0600 Subject: add __hash__ to InvalidityDate --- src/cryptography/x509/extensions.py | 3 +++ tests/test_x509_ext.py | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/src/cryptography/x509/extensions.py b/src/cryptography/x509/extensions.py index 22cba682..89172cb0 100644 --- a/src/cryptography/x509/extensions.py +++ b/src/cryptography/x509/extensions.py @@ -1028,4 +1028,7 @@ class InvalidityDate(object): def __ne__(self, other): return not self == other + def __hash__(self): + return hash(self.invalidity_date) + invalidity_date = utils.read_only_property("_invalidity_date") diff --git a/tests/test_x509_ext.py b/tests/test_x509_ext.py index 91a07654..d0f4cac9 100644 --- a/tests/test_x509_ext.py +++ b/tests/test_x509_ext.py @@ -158,6 +158,12 @@ class TestInvalidityDate(object): "" ) + def test_hash(self): + invalid1 = x509.InvalidityDate(datetime.datetime(2015, 1, 1, 1, 1)) + invalid2 = x509.InvalidityDate(datetime.datetime(2015, 1, 1, 1, 1)) + invalid3 = x509.InvalidityDate(datetime.datetime(2015, 1, 1, 1, 2)) + assert hash(invalid1) == hash(invalid2) + assert hash(invalid1) != hash(invalid3) class TestNoticeReference(object): def test_notice_numbers_not_all_int(self): -- cgit v1.2.3 From c0297ddb9ec205a6ee9844bde9d4e8eb0cc1009e Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Sat, 26 Dec 2015 11:37:57 -0600 Subject: ...pep8 --- tests/test_x509_ext.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_x509_ext.py b/tests/test_x509_ext.py index d0f4cac9..f7be4bbe 100644 --- a/tests/test_x509_ext.py +++ b/tests/test_x509_ext.py @@ -165,6 +165,7 @@ class TestInvalidityDate(object): assert hash(invalid1) == hash(invalid2) assert hash(invalid1) != hash(invalid3) + class TestNoticeReference(object): def test_notice_numbers_not_all_int(self): with pytest.raises(TypeError): -- cgit v1.2.3