support delta crl indicator extension (#3936)

This is an extension for CRLs
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 51488ce..d239d75 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -41,6 +41,7 @@
 * Added support for using labels with
   :class:`~cryptography.hazmat.primitives.asymmetric.padding.OAEP` when using
   OpenSSL 1.0.2 or greater.
+* Add support for the :class:`~cryptography.x509.DeltaCRLIndicator` extension.
 * Add support for the :class:`~cryptography.x509.TLSFeature`
   extension. This is commonly used for enabling ``OCSP Must-Staple`` in
   certificates.
diff --git a/docs/x509/reference.rst b/docs/x509/reference.rst
index 71ac9d9..dea7ee3 100644
--- a/docs/x509/reference.rst
+++ b/docs/x509/reference.rst
@@ -1954,6 +1954,30 @@
         :attr:`~cryptography.x509.oid.ExtensionOID.PRECERT_SIGNED_CERTIFICATE_TIMESTAMPS`.
 
 
+.. class:: DeltaCRLIndicator(crl_number)
+
+    .. versionadded:: 2.1
+
+    The delta CRL indicator is a CRL extension that identifies a CRL as being
+    a delta CRL. Delta CRLs contain updates to revocation information
+    previously distributed, rather than all the information that would appear
+    in a complete CRL.
+
+    :param int crl_number: The CRL number of the complete CRL that the
+        delta CRL is updating.
+
+    .. attribute:: oid
+
+        :type: :class:`ObjectIdentifier`
+
+        Returns
+        :attr:`~cryptography.x509.oid.ExtensionOID.DELTA_CRL_INDICATOR`.
+
+    .. attribute:: crl_number
+
+        :type: int
+
+
 .. class:: AuthorityInformationAccess(descriptions)
 
     .. versionadded:: 0.9
@@ -2749,6 +2773,14 @@
         the ``CRLNumber`` extension type. This extension only has meaning
         for certificate revocation lists.
 
+    .. attribute:: DELTA_CRL_INDICATOR
+
+        .. versionadded:: 2.1
+
+        Corresponds to the dotted string ``"2.5.29.27"``. The identifier for
+        the ``DeltaCRLIndicator`` extension type. This extension only has
+        meaning for certificate revocation lists.
+
     .. attribute:: PRECERT_SIGNED_CERTIFICATE_TIMESTAMPS
 
         .. versionadded:: 1.9
diff --git a/src/cryptography/hazmat/backends/openssl/decode_asn1.py b/src/cryptography/hazmat/backends/openssl/decode_asn1.py
index 6ed4aeb..1326a94 100644
--- a/src/cryptography/hazmat/backends/openssl/decode_asn1.py
+++ b/src/cryptography/hazmat/backends/openssl/decode_asn1.py
@@ -152,6 +152,12 @@
     return x509.CRLNumber(_asn1_integer_to_int(backend, asn1_int))
 
 
+def _decode_delta_crl_indicator(backend, ext):
+    asn1_int = backend._ffi.cast("ASN1_INTEGER *", ext)
+    asn1_int = backend._ffi.gc(asn1_int, backend._lib.ASN1_INTEGER_free)
+    return x509.DeltaCRLIndicator(_asn1_integer_to_int(backend, asn1_int))
+
+
 class _X509ExtensionParser(object):
     def __init__(self, ext_count, get_ext, handlers):
         self.ext_count = ext_count
@@ -742,6 +748,7 @@
 
 _CRL_EXTENSION_HANDLERS = {
     ExtensionOID.CRL_NUMBER: _decode_crl_number,
+    ExtensionOID.DELTA_CRL_INDICATOR: _decode_delta_crl_indicator,
     ExtensionOID.AUTHORITY_KEY_IDENTIFIER: _decode_authority_key_identifier,
     ExtensionOID.ISSUER_ALTERNATIVE_NAME: _decode_issuer_alt_name,
     ExtensionOID.AUTHORITY_INFORMATION_ACCESS: (
diff --git a/src/cryptography/hazmat/backends/openssl/encode_asn1.py b/src/cryptography/hazmat/backends/openssl/encode_asn1.py
index 6d9f956..5ceb29c 100644
--- a/src/cryptography/hazmat/backends/openssl/encode_asn1.py
+++ b/src/cryptography/hazmat/backends/openssl/encode_asn1.py
@@ -133,8 +133,8 @@
     return name_entry
 
 
-def _encode_crl_number(backend, crl_number):
-    return _encode_asn1_int_gc(backend, crl_number.crl_number)
+def _encode_crl_number_delta_crl_indicator(backend, ext):
+    return _encode_asn1_int_gc(backend, ext.crl_number)
 
 
 def _encode_crl_reason(backend, crl_reason):
@@ -598,7 +598,8 @@
     ExtensionOID.AUTHORITY_INFORMATION_ACCESS: (
         _encode_authority_information_access
     ),
-    ExtensionOID.CRL_NUMBER: _encode_crl_number,
+    ExtensionOID.CRL_NUMBER: _encode_crl_number_delta_crl_indicator,
+    ExtensionOID.DELTA_CRL_INDICATOR: _encode_crl_number_delta_crl_indicator,
 }
 
 _CRL_ENTRY_EXTENSION_ENCODE_HANDLERS = {
diff --git a/src/cryptography/x509/__init__.py b/src/cryptography/x509/__init__.py
index 176ed8c..e168adb 100644
--- a/src/cryptography/x509/__init__.py
+++ b/src/cryptography/x509/__init__.py
@@ -18,8 +18,8 @@
     AccessDescription, AuthorityInformationAccess,
     AuthorityKeyIdentifier, BasicConstraints, CRLDistributionPoints,
     CRLNumber, CRLReason, CertificateIssuer, CertificatePolicies,
-    DistributionPoint, DuplicateExtension, ExtendedKeyUsage, Extension,
-    ExtensionNotFound, ExtensionType, Extensions, GeneralNames,
+    DeltaCRLIndicator, DistributionPoint, DuplicateExtension, ExtendedKeyUsage,
+    Extension, ExtensionNotFound, ExtensionType, Extensions, GeneralNames,
     InhibitAnyPolicy, InvalidityDate, IssuerAlternativeName, KeyUsage,
     NameConstraints, NoticeReference, OCSPNoCheck, PolicyConstraints,
     PolicyInformation, PrecertificateSignedCertificateTimestamps, ReasonFlags,
@@ -119,6 +119,7 @@
     "load_der_x509_crl",
     "random_serial_number",
     "InvalidVersion",
+    "DeltaCRLIndicator",
     "DuplicateExtension",
     "ExtensionNotFound",
     "UnsupportedGeneralNameType",
diff --git a/src/cryptography/x509/extensions.py b/src/cryptography/x509/extensions.py
index 442000e..beb20ba 100644
--- a/src/cryptography/x509/extensions.py
+++ b/src/cryptography/x509/extensions.py
@@ -376,6 +376,34 @@
 
 
 @utils.register_interface(ExtensionType)
+class DeltaCRLIndicator(object):
+    oid = ExtensionOID.DELTA_CRL_INDICATOR
+
+    def __init__(self, crl_number):
+        if not isinstance(crl_number, six.integer_types):
+            raise TypeError("crl_number must be an integer")
+
+        self._crl_number = crl_number
+
+    crl_number = utils.read_only_property("_crl_number")
+
+    def __eq__(self, other):
+        if not isinstance(other, DeltaCRLIndicator):
+            return NotImplemented
+
+        return self.crl_number == other.crl_number
+
+    def __ne__(self, other):
+        return not self == other
+
+    def __hash__(self):
+        return hash(self.crl_number)
+
+    def __repr__(self):
+        return "<DeltaCRLIndicator(crl_number={0.crl_number})>".format(self)
+
+
+@utils.register_interface(ExtensionType)
 class CRLDistributionPoints(object):
     oid = ExtensionOID.CRL_DISTRIBUTION_POINTS
 
diff --git a/src/cryptography/x509/oid.py b/src/cryptography/x509/oid.py
index 7f8c903..fedea31 100644
--- a/src/cryptography/x509/oid.py
+++ b/src/cryptography/x509/oid.py
@@ -87,6 +87,7 @@
     OCSP_NO_CHECK = ObjectIdentifier("1.3.6.1.5.5.7.48.1.5")
     TLS_FEATURE = ObjectIdentifier("1.3.6.1.5.5.7.1.24")
     CRL_NUMBER = ObjectIdentifier("2.5.29.20")
+    DELTA_CRL_INDICATOR = ObjectIdentifier("2.5.29.27")
     PRECERT_SIGNED_CERTIFICATE_TIMESTAMPS = (
         ObjectIdentifier("1.3.6.1.4.1.11129.2.4.2")
     )
@@ -256,6 +257,7 @@
     ExtensionOID.SUBJECT_INFORMATION_ACCESS: "subjectInfoAccess",
     ExtensionOID.OCSP_NO_CHECK: "OCSPNoCheck",
     ExtensionOID.CRL_NUMBER: "cRLNumber",
+    ExtensionOID.DELTA_CRL_INDICATOR: "deltaCRLIndicator",
     ExtensionOID.TLS_FEATURE: "TLSFeature",
     AuthorityInformationAccessOID.OCSP: "OCSP",
     AuthorityInformationAccessOID.CA_ISSUERS: "caIssuers",
diff --git a/tests/x509/test_x509.py b/tests/x509/test_x509.py
index 1833a4a..d0ce46d 100644
--- a/tests/x509/test_x509.py
+++ b/tests/x509/test_x509.py
@@ -232,6 +232,19 @@
             x509.UniformResourceIdentifier(b"https://cryptography.io"),
         ])
 
+    def test_delta_crl_indicator(self, backend):
+        crl = _load_cert(
+            os.path.join("x509", "custom", "crl_delta_crl_indicator.pem"),
+            x509.load_pem_x509_crl,
+            backend
+        )
+
+        dci = crl.extensions.get_extension_for_oid(
+            ExtensionOID.DELTA_CRL_INDICATOR
+        )
+        assert dci.value == x509.DeltaCRLIndicator(12345678901234567890)
+        assert dci.critical is False
+
     def test_signature(self, backend):
         crl = _load_cert(
             os.path.join("x509", "custom", "crl_all_reasons.pem"),
diff --git a/tests/x509/test_x509_crlbuilder.py b/tests/x509/test_x509_crlbuilder.py
index b90805f..e90fd3f 100644
--- a/tests/x509/test_x509_crlbuilder.py
+++ b/tests/x509/test_x509_crlbuilder.py
@@ -209,6 +209,7 @@
         "extension",
         [
             x509.CRLNumber(13),
+            x509.DeltaCRLIndicator(12345678901234567890),
             x509.AuthorityKeyIdentifier(
                 b"\xc3\x9c\xf3\xfc\xd3F\x084\xbb\xceF\x7f\xa0|[\xf3\xe2\x08"
                 b"\xcbY",
diff --git a/tests/x509/test_x509_ext.py b/tests/x509/test_x509_ext.py
index b9400b6..9f0b1b0 100644
--- a/tests/x509/test_x509_ext.py
+++ b/tests/x509/test_x509_ext.py
@@ -326,6 +326,36 @@
         )
 
 
+class TestDeltaCRLIndicator(object):
+    def test_not_int(self):
+        with pytest.raises(TypeError):
+            x509.DeltaCRLIndicator("notanint")
+
+    def test_eq(self):
+        delta1 = x509.DeltaCRLIndicator(1)
+        delta2 = x509.DeltaCRLIndicator(1)
+        assert delta1 == delta2
+
+    def test_ne(self):
+        delta1 = x509.DeltaCRLIndicator(1)
+        delta2 = x509.DeltaCRLIndicator(2)
+        assert delta1 != delta2
+        assert delta1 != object()
+
+    def test_repr(self):
+        delta1 = x509.DeltaCRLIndicator(2)
+        assert repr(delta1) == (
+            "<DeltaCRLIndicator(crl_number=2)>"
+        )
+
+    def test_hash(self):
+        delta1 = x509.DeltaCRLIndicator(1)
+        delta2 = x509.DeltaCRLIndicator(1)
+        delta3 = x509.DeltaCRLIndicator(2)
+        assert hash(delta1) == hash(delta2)
+        assert hash(delta1) != hash(delta3)
+
+
 class TestInvalidityDate(object):
     def test_invalid_invalidity_date(self):
         with pytest.raises(TypeError):