Skip to content

Commit

Permalink
Merge pull request pyca#2579 from reaperhulk/crlentry-crlreason
Browse files Browse the repository at this point in the history
switch CRLReason to use a class
  • Loading branch information
alex committed Dec 26, 2015
2 parents 0860ef6 + 7058ece commit d67d77f
Show file tree
Hide file tree
Showing 7 changed files with 80 additions and 9 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Changelog
are:

* :class:`~cryptography.x509.CertificateIssuer`
* ``CRLReason``
* :class:`~cryptography.x509.CRLReason`
* ``InvalidityDate``
* The :class:`~cryptography.x509.Certificate` class now has
:attr:`~cryptography.x509.Certificate.signature` and
Expand Down
24 changes: 23 additions & 1 deletion docs/x509/reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -908,7 +908,7 @@ X.509 Revoked Certificate Object
>>> for ext in revoked_certificate.extensions:
... print(ext)
<Extension(oid=<ObjectIdentifier(oid=2.5.29.24, name=invalidityDate)>, critical=False, value=2015-01-01 00:00:00)>
<Extension(oid=<ObjectIdentifier(oid=2.5.29.21, name=cRLReason)>, critical=False, value=ReasonFlags.key_compromise)>
<Extension(oid=<ObjectIdentifier(oid=2.5.29.21, name=cRLReason)>, critical=False, value=<CRLReason(reason=ReasonFlags.key_compromise)>)>

X.509 Revoked Certificate Builder
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Expand Down Expand Up @@ -1989,6 +1989,28 @@ These extensions are only valid within a :class:`RevokedCertificate` object.
:returns: A list of values extracted from the matched general names.
The type of the returned values depends on the :class:`GeneralName`.

.. class:: CRLReason(reason)

.. versionadded:: 1.2

CRL reason (also known as ``reasonCode``) is an extension that is only
valid inside :class:`~cryptography.x509.RevokedCertificate` objects. It
identifies a reason for the certificate revocation.

:param reason: A value from the
:class:`~cryptography.x509.oid.CRLEntryExtensionOID` enum.

.. attribute:: oid

:type: :class:`ObjectIdentifier`

Returns
:attr:`~cryptography.x509.oid.CRLEntryExtensionOID.CRL_REASON`.

.. attribute:: reason

:type: An element from :class:`~cryptography.x509.ReasonFlags`


Object Identifiers
~~~~~~~~~~~~~~~~~~
Expand Down
2 changes: 1 addition & 1 deletion src/cryptography/hazmat/backends/openssl/x509.py
Original file line number Diff line number Diff line change
Expand Up @@ -700,7 +700,7 @@ def _decode_crl_reason(backend, enum):
code = backend._lib.ASN1_ENUMERATED_get(enum)

try:
return _CRL_REASON_CODE_TO_ENUM[code]
return x509.CRLReason(_CRL_REASON_CODE_TO_ENUM[code])
except KeyError:
raise ValueError("Unsupported reason code: {0}".format(code))

Expand Down
3 changes: 2 additions & 1 deletion src/cryptography/x509/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from cryptography.x509.extensions import (
AccessDescription, AuthorityInformationAccess,
AuthorityKeyIdentifier, BasicConstraints, CRLDistributionPoints,
CRLNumber, CertificateIssuer, CertificatePolicies,
CRLNumber, CRLReason, CertificateIssuer, CertificatePolicies,
DistributionPoint, DuplicateExtension, ExtendedKeyUsage, Extension,
ExtensionNotFound, ExtensionType, Extensions, GeneralNames,
InhibitAnyPolicy, IssuerAlternativeName, KeyUsage,
Expand Down Expand Up @@ -167,4 +167,5 @@
"_GENERAL_NAMES",
"CRLExtensionOID",
"CertificateIssuer",
"CRLReason",
]
25 changes: 25 additions & 0 deletions src/cryptography/x509/extensions.py
Original file line number Diff line number Diff line change
Expand Up @@ -976,3 +976,28 @@ def __eq__(self, other):

def __ne__(self, other):
return not self == other


@utils.register_interface(ExtensionType)
class CRLReason(object):
oid = CRLEntryExtensionOID.CRL_REASON

def __init__(self, reason):
if not isinstance(reason, ReasonFlags):
raise TypeError("reason must be an element from ReasonFlags")

self._reason = reason

def __repr__(self):
return "<CRLReason(reason={0})>".format(self._reason)

def __eq__(self, other):
if not isinstance(other, CRLReason):
return NotImplemented

return self.reason == other.reason

def __ne__(self, other):
return not self == other

reason = utils.read_only_property("_reason")
10 changes: 5 additions & 5 deletions tests/test_x509.py
Original file line number Diff line number Diff line change
Expand Up @@ -379,9 +379,9 @@ def test_revoked_extensions(self, backend):
rev1 = crl[1]
assert isinstance(rev1.extensions, x509.Extensions)

reason = rev1.extensions.get_extension_for_oid(
x509.OID_CRL_REASON).value
assert reason == x509.ReasonFlags.unspecified
reason = rev1.extensions.get_extension_for_class(
x509.CRLReason).value
assert reason == x509.CRLReason(x509.ReasonFlags.unspecified)

issuer = rev1.extensions.get_extension_for_class(
x509.CertificateIssuer).value
Expand All @@ -395,12 +395,12 @@ def test_revoked_extensions(self, backend):
flags = set(x509.ReasonFlags)
for rev in crl:
try:
r = rev.extensions.get_extension_for_oid(x509.OID_CRL_REASON)
r = rev.extensions.get_extension_for_class(x509.CRLReason)
except x509.ExtensionNotFound:
# Not all revoked certs have a reason extension.
pass
else:
flags.discard(r.value)
flags.discard(r.value.reason)

assert len(flags) == 0

Expand Down
23 changes: 23 additions & 0 deletions tests/test_x509_ext.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,29 @@ def test_get_values_for_type(self):
assert names == [u"cryptography.io"]


class TestCRLReason(object):
def test_invalid_reason_flags(self):
with pytest.raises(TypeError):
x509.CRLReason("notareason")

def test_eq(self):
reason1 = x509.CRLReason(x509.ReasonFlags.unspecified)
reason2 = x509.CRLReason(x509.ReasonFlags.unspecified)
assert reason1 == reason2

def test_ne(self):
reason1 = x509.CRLReason(x509.ReasonFlags.unspecified)
reason2 = x509.CRLReason(x509.ReasonFlags.ca_compromise)
assert reason1 != reason2
assert reason1 != object()

def test_repr(self):
reason1 = x509.CRLReason(x509.ReasonFlags.unspecified)
assert repr(reason1) == (
"<CRLReason(reason=ReasonFlags.unspecified)>"
)


class TestNoticeReference(object):
def test_notice_numbers_not_all_int(self):
with pytest.raises(TypeError):
Expand Down

0 comments on commit d67d77f

Please sign in to comment.