aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--docs/x509.rst72
-rw-r--r--src/cryptography/x509.py144
-rw-r--r--tests/test_x509_ext.py282
3 files changed, 498 insertions, 0 deletions
diff --git a/docs/x509.rst b/docs/x509.rst
index f4ea2a52..9ef8e149 100644
--- a/docs/x509.rst
+++ b/docs/x509.rst
@@ -781,6 +781,8 @@ X.509 Extensions
.. class:: AccessDescription
+ .. versionadded:: 0.9
+
.. attribute:: access_method
:type: :class:`ObjectIdentifier`
@@ -798,6 +800,76 @@ X.509 Extensions
Where to access the information defined by the access method.
+.. class:: CRLDistributionPoints
+
+ .. versionadded:: 0.9
+
+ The CRL distribution points extension identifies how CRL information is
+ obtained. It is an iterable, containing one or more
+ :class:`DistributionPoint` instances.
+
+.. class:: DistributionPoint
+
+ .. versionadded:: 0.9
+
+ .. attribute:: distribution_point
+
+ :type: list of :class:`GeneralName` instances, :class:`Name`, or None
+
+ This field describes methods to retrieve the CRL.
+
+ .. attribute:: crl_issuer
+
+ :type: list of :class:`GeneralName` instances or None
+
+ Information about the issuer of the CRL.
+
+ .. attribute:: reasons
+
+ :type: :class:`ReasonFlags` or None
+
+ The reasons a given distribution point may be used for when performing
+ revocation checks.
+
+.. class:: ReasonFlags
+
+ .. versionadded:: 0.9
+
+ This class holds reasons a distribution point may be used for when
+ performing revocation checks.
+
+ .. attribute:: key_compromise
+
+ :type: bool
+
+ .. attribute:: ca_compromise
+
+ :type: bool
+
+ .. attribute:: affiliation_changed
+
+ :type: bool
+
+ .. attribute:: superseded
+
+ :type: bool
+
+ .. attribute:: cessation_of_operation
+
+ :type: bool
+
+ .. attribute:: certificate_hold
+
+ :type: bool
+
+ .. attribute:: privilege_withdrawn
+
+ :type: bool
+
+ .. attribute:: aa_compromise
+
+ :type: bool
+
Object Identifiers
~~~~~~~~~~~~~~~~~~
diff --git a/src/cryptography/x509.py b/src/cryptography/x509.py
index 0d87cd51..671294e2 100644
--- a/src/cryptography/x509.py
+++ b/src/cryptography/x509.py
@@ -481,6 +481,150 @@ class SubjectKeyIdentifier(object):
return not self == other
+class CRLDistributionPoints(object):
+ def __init__(self, distribution_points):
+ if not all(
+ isinstance(x, DistributionPoint) for x in distribution_points
+ ):
+ raise TypeError(
+ "distribution_points must be a list of DistributionPoint "
+ "objects"
+ )
+
+ self._distribution_points = distribution_points
+
+ def __iter__(self):
+ return iter(self._distribution_points)
+
+ def __len__(self):
+ return len(self._distribution_points)
+
+ def __repr__(self):
+ return "<CRLDistributionPoints({0})>".format(self._distribution_points)
+
+ def __eq__(self, other):
+ if not isinstance(other, CRLDistributionPoints):
+ return NotImplemented
+
+ return self._distribution_points == other._distribution_points
+
+ def __ne__(self, other):
+ return not self == other
+
+
+class DistributionPoint(object):
+ def __init__(self, distribution_point, reasons, crl_issuer):
+ if distribution_point:
+ if (
+ (
+ isinstance(distribution_point, list) and
+ not all(
+ isinstance(x, GeneralName) for x in distribution_point
+ )
+ ) or not isinstance(distribution_point, (list, Name))
+ ):
+ raise TypeError(
+ "distribution_point must be None, a list of general names"
+ ", or a Name"
+ )
+
+ if crl_issuer and not all(
+ isinstance(x, GeneralName) for x in crl_issuer
+ ):
+ raise TypeError(
+ "crl_issuer must be None or a list of general names"
+ )
+
+ if reasons and not isinstance(reasons, ReasonFlags):
+ raise TypeError("reasons must be None or ReasonFlags")
+
+ if reasons and not crl_issuer and not distribution_point:
+ raise ValueError(
+ "You must supply crl_issuer or distribution_point when "
+ "reasons is not None"
+ )
+
+ self._distribution_point = distribution_point
+ self._reasons = reasons
+ self._crl_issuer = crl_issuer
+
+ def __repr__(self):
+ return (
+ "<DistributionPoint(distribution_point={0.distribution_point}, rea"
+ "sons={0.reasons}, crl_issuer={0.crl_issuer})>".format(self)
+ )
+
+ def __eq__(self, other):
+ if not isinstance(other, DistributionPoint):
+ return NotImplemented
+
+ return (
+ self.distribution_point == other.distribution_point and
+ self.reasons == other.reasons and
+ self.crl_issuer == other.crl_issuer
+ )
+
+ def __ne__(self, other):
+ return not self == other
+
+ distribution_point = utils.read_only_property("_distribution_point")
+ reasons = utils.read_only_property("_reasons")
+ crl_issuer = utils.read_only_property("_crl_issuer")
+
+
+class ReasonFlags(object):
+ def __init__(self, key_compromise, ca_compromise, affiliation_changed,
+ superseded, cessation_of_operation, certificate_hold,
+ privilege_withdrawn, aa_compromise):
+ self._key_compromise = key_compromise
+ self._ca_compromise = ca_compromise
+ self._affiliation_changed = affiliation_changed
+ self._superseded = superseded
+ self._cessation_of_operation = cessation_of_operation
+ self._certificate_hold = certificate_hold
+ self._privilege_withdrawn = privilege_withdrawn
+ self._aa_compromise = aa_compromise
+
+ def __repr__(self):
+ return (
+ "<ReasonFlags(key_compromise={0.key_compromise}, ca_compromise"
+ "={0.ca_compromise}, affiliation_changed={0.affiliation_changed},"
+ "superseded={0.superseded}, cessation_of_operation={0.cessation_o"
+ "f_operation}, certificate_hold={0.certificate_hold}, privilege_w"
+ "ithdrawn={0.privilege_withdrawn}, aa_compromise={0.aa_compromise"
+ "})>".format(self)
+ )
+
+ def __eq__(self, other):
+ if not isinstance(other, ReasonFlags):
+ return NotImplemented
+
+ return (
+ self.key_compromise == other.key_compromise and
+ self.ca_compromise == other.ca_compromise and
+ self.affiliation_changed == other.affiliation_changed and
+ self.superseded == other.superseded and
+ self.cessation_of_operation == other.cessation_of_operation and
+ self.certificate_hold == other.certificate_hold and
+ self.privilege_withdrawn == other.privilege_withdrawn and
+ self.aa_compromise == other.aa_compromise
+ )
+
+ def __ne__(self, other):
+ return not self == other
+
+ key_compromise = utils.read_only_property("_key_compromise")
+ ca_compromise = utils.read_only_property("_ca_compromise")
+ affiliation_changed = utils.read_only_property("_affiliation_changed")
+ superseded = utils.read_only_property("_superseded")
+ cessation_of_operation = utils.read_only_property(
+ "_cessation_of_operation"
+ )
+ certificate_hold = utils.read_only_property("_certificate_hold")
+ privilege_withdrawn = utils.read_only_property("_privilege_withdrawn")
+ aa_compromise = utils.read_only_property("_aa_compromise")
+
+
@six.add_metaclass(abc.ABCMeta)
class GeneralName(object):
@abc.abstractproperty
diff --git a/tests/test_x509_ext.py b/tests/test_x509_ext.py
index 8a227953..1ccb361b 100644
--- a/tests/test_x509_ext.py
+++ b/tests/test_x509_ext.py
@@ -1318,3 +1318,285 @@ class TestAuthorityKeyIdentifierExtension(object):
)
]
assert ext.value.authority_cert_serial_number == 3
+
+
+class TestReasonFlags(object):
+ def test_flags(self):
+ flags = x509.ReasonFlags(
+ True, True, False, False, True, True, False, False
+ )
+ assert flags.key_compromise is True
+ assert flags.ca_compromise is True
+ assert flags.affiliation_changed is False
+ assert flags.superseded is False
+ assert flags.cessation_of_operation is True
+ assert flags.certificate_hold is True
+ assert flags.privilege_withdrawn is False
+ assert flags.aa_compromise is False
+
+ def test_eq(self):
+ flags = x509.ReasonFlags(
+ True, True, False, False, True, True, False, False
+ )
+ flags2 = x509.ReasonFlags(
+ True, True, False, False, True, True, False, False
+ )
+ assert flags == flags2
+
+ def test_ne(self):
+ flags = x509.ReasonFlags(
+ True, True, False, False, True, True, False, False
+ )
+ flags2 = x509.ReasonFlags(
+ True, True, False, False, True, True, False, True
+ )
+ assert flags != flags2
+ assert flags != object()
+
+ def test_repr(self):
+ flags = x509.ReasonFlags(
+ True, True, False, False, True, True, False, False
+ )
+ assert repr(flags) == (
+ "<ReasonFlags(key_compromise=True, ca_compromise=True, affiliatio"
+ "n_changed=False,superseded=False, cessation_of_operation=True, c"
+ "ertificate_hold=True, privilege_withdrawn=False, aa_compromise=F"
+ "alse)>"
+ )
+
+
+class TestDistributionPoint(object):
+ def test_distribution_point_list_not_general_names(self):
+ with pytest.raises(TypeError):
+ x509.DistributionPoint(["notgn"], None, None)
+
+ def test_distribution_point_not_name(self):
+ with pytest.raises(TypeError):
+ x509.DistributionPoint("notname", None, None)
+
+ def test_crl_issuer_not_general_names(self):
+ with pytest.raises(TypeError):
+ x509.DistributionPoint(None, None, ["notgn"])
+
+ def test_reason_not_reasonflags(self):
+ with pytest.raises(TypeError):
+ x509.DistributionPoint(
+ [x509.UniformResourceIdentifier(u"http://crypt.og/crl")],
+ "notreasonflags",
+ None
+ )
+
+ def test_reason_only(self):
+ with pytest.raises(ValueError):
+ x509.DistributionPoint(
+ None,
+ x509.ReasonFlags(
+ True, True, False, False, True, True, False, False
+ ),
+ None
+ )
+
+ def test_eq(self):
+ dp = x509.DistributionPoint(
+ [x509.UniformResourceIdentifier(u"http://crypt.og/crl")],
+ x509.ReasonFlags(
+ False, False, False, False, False, True, False, False
+ ),
+ [
+ x509.DirectoryName(
+ x509.Name([
+ x509.NameAttribute(
+ x509.OID_COMMON_NAME, "Important CA"
+ )
+ ])
+ )
+ ],
+ )
+ dp2 = x509.DistributionPoint(
+ [x509.UniformResourceIdentifier(u"http://crypt.og/crl")],
+ x509.ReasonFlags(
+ False, False, False, False, False, True, False, False
+ ),
+ [
+ x509.DirectoryName(
+ x509.Name([
+ x509.NameAttribute(
+ x509.OID_COMMON_NAME, "Important CA"
+ )
+ ])
+ )
+ ],
+ )
+ assert dp == dp2
+
+ def test_ne(self):
+ dp = x509.DistributionPoint(
+ [x509.UniformResourceIdentifier(u"http://crypt.og/crl")],
+ x509.ReasonFlags(
+ False, False, False, False, False, True, False, False
+ ),
+ [
+ x509.DirectoryName(
+ x509.Name([
+ x509.NameAttribute(
+ x509.OID_COMMON_NAME, "Important CA"
+ )
+ ])
+ )
+ ],
+ )
+ dp2 = x509.DistributionPoint(
+ [x509.UniformResourceIdentifier(u"http://crypt.og/crl")],
+ None,
+ None
+ )
+ assert dp != dp2
+ assert dp != object()
+
+ def test_repr(self):
+ dp = x509.DistributionPoint(
+ x509.Name([
+ x509.NameAttribute(x509.OID_COMMON_NAME, "myCN")
+ ]),
+ x509.ReasonFlags(
+ False, False, False, False, False, True, False, False
+ ),
+ [
+ x509.DirectoryName(
+ x509.Name([
+ x509.NameAttribute(
+ x509.OID_COMMON_NAME, "Important CA"
+ )
+ ])
+ )
+ ],
+ )
+ assert repr(dp) == (
+ "<DistributionPoint(distribution_point=<Name([<NameAttribute(oid="
+ "<ObjectIdentifier(oid=2.5.4.3, name=commonName)>, value='myCN')>"
+ "])>, reasons=<ReasonFlags(key_compromise=False, ca_compromise=Fa"
+ "lse, affiliation_changed=False,superseded=False, cessation_of_op"
+ "eration=False, certificate_hold=True, privilege_withdrawn=False,"
+ " aa_compromise=False)>, crl_issuer=[<DirectoryName(value=<Name(["
+ "<NameAttribute(oid=<ObjectIdentifier(oid=2.5.4.3, name=commonNam"
+ "e)>, value='Important CA')>])>)>])>"
+ )
+
+
+class TestCRLDistributionPoints(object):
+ def test_invalid_distribution_points(self):
+ with pytest.raises(TypeError):
+ x509.CRLDistributionPoints(["notadistributionpoint"])
+
+ def test_iter_len(self):
+ cdp = x509.CRLDistributionPoints([
+ x509.DistributionPoint(
+ [x509.UniformResourceIdentifier(u"http://domain")],
+ None,
+ None
+ ),
+ x509.DistributionPoint(
+ [x509.UniformResourceIdentifier(u"ftp://domain")],
+ x509.ReasonFlags(
+ True, True, True, True, True, True, True, True
+ ),
+ None
+ ),
+ ])
+ assert len(cdp) == 2
+ assert list(cdp) == [
+ x509.DistributionPoint(
+ [x509.UniformResourceIdentifier(u"http://domain")],
+ None,
+ None
+ ),
+ x509.DistributionPoint(
+ [x509.UniformResourceIdentifier(u"ftp://domain")],
+ x509.ReasonFlags(
+ True, True, True, True, True, True, True, True
+ ),
+ None
+ ),
+ ]
+
+ def test_repr(self):
+ cdp = x509.CRLDistributionPoints([
+ x509.DistributionPoint(
+ [x509.UniformResourceIdentifier(u"ftp://domain")],
+ x509.ReasonFlags(
+ True, True, True, True, True, True, True, True
+ ),
+ None
+ ),
+ ])
+ assert repr(cdp) == (
+ "<CRLDistributionPoints([<DistributionPoint(distribution_point=[<"
+ "UniformResourceIdentifier(value=ftp://domain)>], reasons=<Reason"
+ "Flags(key_compromise=True, ca_compromise=True, affiliation_chang"
+ "ed=True,superseded=True, cessation_of_operation=True, certificat"
+ "e_hold=True, privilege_withdrawn=True, aa_compromise=True)>, crl"
+ "_issuer=None)>])>"
+ )
+
+ def test_eq(self):
+ cdp = x509.CRLDistributionPoints([
+ x509.DistributionPoint(
+ [x509.UniformResourceIdentifier(u"ftp://domain")],
+ x509.ReasonFlags(
+ True, True, True, True, True, True, True, True
+ ),
+ [x509.UniformResourceIdentifier(u"uri://thing")],
+ ),
+ ])
+ cdp2 = x509.CRLDistributionPoints([
+ x509.DistributionPoint(
+ [x509.UniformResourceIdentifier(u"ftp://domain")],
+ x509.ReasonFlags(
+ True, True, True, True, True, True, True, True
+ ),
+ [x509.UniformResourceIdentifier(u"uri://thing")],
+ ),
+ ])
+ assert cdp == cdp2
+
+ def test_ne(self):
+ cdp = x509.CRLDistributionPoints([
+ x509.DistributionPoint(
+ [x509.UniformResourceIdentifier(u"ftp://domain")],
+ x509.ReasonFlags(
+ True, True, True, True, True, True, True, True
+ ),
+ [x509.UniformResourceIdentifier(u"uri://thing")],
+ ),
+ ])
+ cdp2 = x509.CRLDistributionPoints([
+ x509.DistributionPoint(
+ [x509.UniformResourceIdentifier(u"ftp://domain2")],
+ x509.ReasonFlags(
+ True, True, True, True, True, True, True, True
+ ),
+ [x509.UniformResourceIdentifier(u"uri://thing")],
+ ),
+ ])
+ cdp3 = x509.CRLDistributionPoints([
+ x509.DistributionPoint(
+ [x509.UniformResourceIdentifier(u"ftp://domain")],
+ x509.ReasonFlags(
+ True, True, True, True, True, True, True, False
+ ),
+ [x509.UniformResourceIdentifier(u"uri://thing")],
+ ),
+ ])
+ cdp4 = x509.CRLDistributionPoints([
+ x509.DistributionPoint(
+ [x509.UniformResourceIdentifier(u"ftp://domain")],
+ x509.ReasonFlags(
+ True, True, True, True, True, True, True, True
+ ),
+ [x509.UniformResourceIdentifier(u"uri://thing2")],
+ ),
+ ])
+ assert cdp != cdp2
+ assert cdp != cdp3
+ assert cdp != cdp4
+ assert cdp != object()