encode IssuingDistributionPoint (#4618)

diff --git a/src/cryptography/hazmat/backends/openssl/encode_asn1.py b/src/cryptography/hazmat/backends/openssl/encode_asn1.py
index 8bc8cb4..3557576 100644
--- a/src/cryptography/hazmat/backends/openssl/encode_asn1.py
+++ b/src/cryptography/hazmat/backends/openssl/encode_asn1.py
@@ -136,6 +136,28 @@
     return _encode_asn1_int_gc(backend, ext.crl_number)
 
 
+def _encode_issuing_dist_point(backend, ext):
+    idp = backend._lib.ISSUING_DIST_POINT_new()
+    backend.openssl_assert(idp != backend._ffi.NULL)
+    idp = backend._ffi.gc(idp, backend._lib.ISSUING_DIST_POINT_free)
+    idp.onlyuser = 255 if ext.only_contains_user_certs else 0
+    idp.onlyCA = 255 if ext.only_contains_ca_certs else 0
+    idp.indirectCRL = 255 if ext.indirect_crl else 0
+    idp.onlyattr = 255 if ext.only_contains_attribute_certs else 0
+    if ext.only_some_reasons:
+        idp.onlysomereasons = _encode_reasonflags(
+            backend, ext.only_some_reasons
+        )
+
+    if ext.full_name:
+        idp.distpoint = _encode_full_name(backend, ext.full_name)
+
+    if ext.relative_name:
+        idp.distpoint = _encode_relative_name(backend, ext.relative_name)
+
+    return idp
+
+
 def _encode_crl_reason(backend, crl_reason):
     asn1enum = backend._lib.ASN1_ENUMERATED_new()
     backend.openssl_assert(asn1enum != backend._ffi.NULL)
@@ -614,6 +636,7 @@
     ),
     ExtensionOID.CRL_NUMBER: _encode_crl_number_delta_crl_indicator,
     ExtensionOID.DELTA_CRL_INDICATOR: _encode_crl_number_delta_crl_indicator,
+    ExtensionOID.ISSUING_DISTRIBUTION_POINT: _encode_issuing_dist_point,
 }
 
 _CRL_ENTRY_EXTENSION_ENCODE_HANDLERS = {
diff --git a/tests/hazmat/backends/test_openssl_memleak.py b/tests/hazmat/backends/test_openssl_memleak.py
index 6f42ed7..1db103b 100644
--- a/tests/hazmat/backends/test_openssl_memleak.py
+++ b/tests/hazmat/backends/test_openssl_memleak.py
@@ -325,3 +325,48 @@
                     f.read(), b"cryptography", backend
                 )
         """), [path])
+
+    def test_create_crl_with_idp(self):
+        assert_no_memory_leaks(textwrap.dedent("""
+        def func():
+            import datetime
+            from cryptography import x509
+            from cryptography.hazmat.backends.openssl import backend
+            from cryptography.hazmat.primitives import hashes
+            from cryptography.hazmat.primitives.asymmetric import ec
+            from cryptography.x509.oid import NameOID
+
+            key = ec.generate_private_key(ec.SECP256R1(), backend)
+            last_update = datetime.datetime(2002, 1, 1, 12, 1)
+            next_update = datetime.datetime(2030, 1, 1, 12, 1)
+            idp = x509.IssuingDistributionPoint(
+                full_name=None,
+                relative_name=x509.RelativeDistinguishedName([
+                    x509.NameAttribute(
+                        oid=x509.NameOID.ORGANIZATION_NAME, value=u"PyCA")
+                ]),
+                only_contains_user_certs=False,
+                only_contains_ca_certs=True,
+                only_some_reasons=None,
+                indirect_crl=False,
+                only_contains_attribute_certs=False,
+            )
+            builder = x509.CertificateRevocationListBuilder().issuer_name(
+                x509.Name([
+                    x509.NameAttribute(
+                        NameOID.COMMON_NAME, u"cryptography.io CA"
+                    )
+                ])
+            ).last_update(
+                last_update
+            ).next_update(
+                next_update
+            ).add_extension(
+                idp, True
+            )
+
+            crl = builder.sign(key, hashes.SHA256(), backend)
+            crl.extensions.get_extension_for_class(
+                x509.IssuingDistributionPoint
+            )
+        """))
diff --git a/tests/x509/test_x509_ext.py b/tests/x509/test_x509_ext.py
index 5ff3bdd..bfa3784 100644
--- a/tests/x509/test_x509_ext.py
+++ b/tests/x509/test_x509_ext.py
@@ -4727,6 +4727,143 @@
         assert hash(idp1) == hash(idp2)
         assert hash(idp1) != hash(idp3)
 
+    @pytest.mark.requires_backend_interface(interface=RSABackend)
+    @pytest.mark.requires_backend_interface(interface=X509Backend)
+    @pytest.mark.parametrize(
+        "idp",
+        [
+            x509.IssuingDistributionPoint(
+                full_name=[
+                    x509.UniformResourceIdentifier(
+                        u"http://myhost.com/myca.crl"
+                    )
+                ],
+                relative_name=None,
+                only_contains_user_certs=False,
+                only_contains_ca_certs=False,
+                only_some_reasons=None,
+                indirect_crl=True,
+                only_contains_attribute_certs=False,
+            ),
+            x509.IssuingDistributionPoint(
+                full_name=[
+                    x509.UniformResourceIdentifier(
+                        u"http://myhost.com/myca.crl"
+                    )
+                ],
+                relative_name=None,
+                only_contains_user_certs=False,
+                only_contains_ca_certs=False,
+                only_some_reasons=None,
+                indirect_crl=False,
+                only_contains_attribute_certs=False,
+            ),
+            x509.IssuingDistributionPoint(
+                full_name=[
+                    x509.UniformResourceIdentifier(
+                        u"http://myhost.com/myca.crl"
+                    )
+                ],
+                relative_name=None,
+                only_contains_user_certs=False,
+                only_contains_ca_certs=False,
+                only_some_reasons=None,
+                indirect_crl=False,
+                only_contains_attribute_certs=True,
+            ),
+            x509.IssuingDistributionPoint(
+                full_name=[
+                    x509.UniformResourceIdentifier(
+                        u"http://myhost.com/myca.crl"
+                    )
+                ],
+                relative_name=None,
+                only_contains_user_certs=True,
+                only_contains_ca_certs=False,
+                only_some_reasons=None,
+                indirect_crl=False,
+                only_contains_attribute_certs=False,
+            ),
+            x509.IssuingDistributionPoint(
+                full_name=None,
+                relative_name=x509.RelativeDistinguishedName([
+                    x509.NameAttribute(
+                        oid=x509.NameOID.ORGANIZATION_NAME, value=u"PyCA"
+                    )
+                ]),
+                only_contains_user_certs=False,
+                only_contains_ca_certs=True,
+                only_some_reasons=None,
+                indirect_crl=False,
+                only_contains_attribute_certs=False,
+            ),
+            x509.IssuingDistributionPoint(
+                full_name=None,
+                relative_name=None,
+                only_contains_user_certs=False,
+                only_contains_ca_certs=True,
+                only_some_reasons=frozenset([x509.ReasonFlags.key_compromise]),
+                indirect_crl=False,
+                only_contains_attribute_certs=False,
+            ),
+            x509.IssuingDistributionPoint(
+                full_name=None,
+                relative_name=x509.RelativeDistinguishedName([
+                    x509.NameAttribute(
+                        oid=x509.NameOID.ORGANIZATION_NAME, value=u"PyCA"),
+                    x509.NameAttribute(
+                        oid=x509.NameOID.COMMON_NAME, value=u"cryptography")
+                ]),
+                only_contains_user_certs=True,
+                only_contains_ca_certs=False,
+                only_some_reasons=frozenset([
+                    x509.ReasonFlags.key_compromise,
+                    x509.ReasonFlags.ca_compromise,
+                    x509.ReasonFlags.affiliation_changed,
+                    x509.ReasonFlags.privilege_withdrawn,
+                    x509.ReasonFlags.aa_compromise,
+                ]),
+                indirect_crl=False,
+                only_contains_attribute_certs=False,
+            ),
+            x509.IssuingDistributionPoint(
+                full_name=None,
+                relative_name=x509.RelativeDistinguishedName([
+                    x509.NameAttribute(
+                        oid=x509.NameOID.ORGANIZATION_NAME, value=u"PyCA"
+                    )
+                ]),
+                only_contains_user_certs=False,
+                only_contains_ca_certs=False,
+                only_some_reasons=None,
+                indirect_crl=False,
+                only_contains_attribute_certs=False,
+            ),
+        ]
+    )
+    def test_generate(self, idp, backend):
+        key = RSA_KEY_2048.private_key(backend)
+        last_update = datetime.datetime(2002, 1, 1, 12, 1)
+        next_update = datetime.datetime(2030, 1, 1, 12, 1)
+        builder = x509.CertificateRevocationListBuilder().issuer_name(
+            x509.Name([
+                x509.NameAttribute(NameOID.COMMON_NAME, u"cryptography.io CA")
+            ])
+        ).last_update(
+            last_update
+        ).next_update(
+            next_update
+        ).add_extension(
+            idp, True
+        )
+
+        crl = builder.sign(key, hashes.SHA256(), backend)
+        ext = crl.extensions.get_extension_for_class(
+            x509.IssuingDistributionPoint
+        )
+        assert ext.critical is True
+        assert ext.value == idp
+
 
 @pytest.mark.requires_backend_interface(interface=RSABackend)
 @pytest.mark.requires_backend_interface(interface=X509Backend)