Fixes #4333 -- added support for precert poison extension (#4442)

* Fixes #4333 -- added support for precert poison extension

* Make work on all OpenSSL versions

* fixed flake8 + docs

* fix for older OpenSSLs

* document this

* spell
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index a614df8..8c08fcb 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -13,6 +13,7 @@
   support, however we strongly encourage all users to upgrade or install
   ``cryptography`` from a wheel.
 * Added initial :doc:`OCSP </x509/ocsp>` support.
+* Added support for :class:`~cryptography.x509.PrecertPoison`.
 
 .. _v2-3-1:
 
diff --git a/docs/x509/reference.rst b/docs/x509/reference.rst
index 5fa8471..ede08aa 100644
--- a/docs/x509/reference.rst
+++ b/docs/x509/reference.rst
@@ -1944,6 +1944,23 @@
         :attr:`~cryptography.x509.oid.ExtensionOID.PRECERT_SIGNED_CERTIFICATE_TIMESTAMPS`.
 
 
+.. class:: PrecertPoison()
+
+    .. versionadded:: 2.4
+
+    This extension indicates that the certificate should not be treated as a
+    certificate for the purposes of validation, but is instead for submission
+    to a certificate transparency log in order to obtain SCTs which will be
+    embedded in a :class:`PrecertificateSignedCertificateTimestamps` extension
+    on the final certificate.
+
+    .. attribute:: oid
+
+        :type: :class:`ObjectIdentifier`
+
+        Returns :attr:`~cryptography.x509.oid.ExtensionOID.PRECERT_POISON`.
+
+
 .. class:: DeltaCRLIndicator(crl_number)
 
     .. versionadded:: 2.1
@@ -2804,6 +2821,12 @@
 
         Corresponds to the dotted string ``"1.3.6.1.4.1.11129.2.4.2"``.
 
+    .. attribute:: PRECERT_POISON
+
+        .. versionadded:: 2.4
+
+        Corresponds to the dotted string ``"1.3.6.1.4.1.11129.2.4.3"``.
+
     .. attribute:: POLICY_CONSTRAINTS
 
         Corresponds to the dotted string ``"2.5.29.36"``. The identifier for the
diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py
index 64d26af..58fe492 100644
--- a/src/cryptography/hazmat/backends/openssl/backend.py
+++ b/src/cryptography/hazmat/backends/openssl/backend.py
@@ -11,6 +11,8 @@
 import itertools
 from contextlib import contextmanager
 
+import asn1crypto.core
+
 import six
 
 from cryptography import utils, x509
@@ -965,6 +967,10 @@
             asn1 = _Integers([x.value for x in extension.value]).dump()
             value = _encode_asn1_str_gc(self, asn1, len(asn1))
             return self._create_raw_x509_extension(extension, value)
+        elif isinstance(extension.value, x509.PrecertPoison):
+            asn1 = asn1crypto.core.Null().dump()
+            value = _encode_asn1_str_gc(self, asn1, len(asn1))
+            return self._create_raw_x509_extension(extension, value)
         else:
             try:
                 encode = handlers[extension.oid]
diff --git a/src/cryptography/hazmat/backends/openssl/decode_asn1.py b/src/cryptography/hazmat/backends/openssl/decode_asn1.py
index 31fb8cf..47fa911 100644
--- a/src/cryptography/hazmat/backends/openssl/decode_asn1.py
+++ b/src/cryptography/hazmat/backends/openssl/decode_asn1.py
@@ -7,7 +7,7 @@
 import datetime
 import ipaddress
 
-from asn1crypto.core import Integer, SequenceOf
+import asn1crypto.core
 
 from cryptography import x509
 from cryptography.x509.extensions import _TLS_FEATURE_TYPE_TO_ENUM
@@ -17,8 +17,8 @@
 )
 
 
-class _Integers(SequenceOf):
-    _child_spec = Integer
+class _Integers(asn1crypto.core.SequenceOf):
+    _child_spec = asn1crypto.core.Integer
 
 
 def _obj2txt(backend, obj):
@@ -202,8 +202,8 @@
                     "Duplicate {0} extension found".format(oid), oid
                 )
 
-            # This OID is only supported in OpenSSL 1.1.0+ but we want
-            # to support it in all versions of OpenSSL so we decode it
+            # These OIDs are only supported in OpenSSL 1.1.0+ but we want
+            # to support them in all versions of OpenSSL so we decode them
             # ourselves.
             if oid == ExtensionOID.TLS_FEATURE:
                 data = backend._lib.X509_EXTENSION_get_data(ext)
@@ -214,6 +214,17 @@
                 extensions.append(x509.Extension(oid, critical, value))
                 seen_oids.add(oid)
                 continue
+            elif oid == ExtensionOID.PRECERT_POISON:
+                data = backend._lib.X509_EXTENSION_get_data(ext)
+                parsed = asn1crypto.core.Null.load(
+                    _asn1_string_to_bytes(backend, data)
+                )
+                assert parsed == asn1crypto.core.Null()
+                extensions.append(x509.Extension(
+                    oid, critical, x509.PrecertPoison()
+                ))
+                seen_oids.add(oid)
+                continue
 
             try:
                 handler = self.handlers[oid]
diff --git a/src/cryptography/x509/__init__.py b/src/cryptography/x509/__init__.py
index d2f9b04..15459a1 100644
--- a/src/cryptography/x509/__init__.py
+++ b/src/cryptography/x509/__init__.py
@@ -22,7 +22,8 @@
     Extension, ExtensionNotFound, ExtensionType, Extensions, FreshestCRL,
     GeneralNames, InhibitAnyPolicy, InvalidityDate, IssuerAlternativeName,
     KeyUsage, NameConstraints, NoticeReference, OCSPNoCheck, PolicyConstraints,
-    PolicyInformation, PrecertificateSignedCertificateTimestamps, ReasonFlags,
+    PolicyInformation, PrecertPoison,
+    PrecertificateSignedCertificateTimestamps, ReasonFlags,
     SubjectAlternativeName, SubjectKeyIdentifier, TLSFeature, TLSFeatureType,
     UnrecognizedExtension, UserNotice
 )
@@ -182,4 +183,5 @@
     "UnrecognizedExtension",
     "PolicyConstraints",
     "PrecertificateSignedCertificateTimestamps",
+    "PrecertPoison",
 ]
diff --git a/src/cryptography/x509/extensions.py b/src/cryptography/x509/extensions.py
index eb4b927..08af03c 100644
--- a/src/cryptography/x509/extensions.py
+++ b/src/cryptography/x509/extensions.py
@@ -855,6 +855,11 @@
 
 
 @utils.register_interface(ExtensionType)
+class PrecertPoison(object):
+    oid = ExtensionOID.PRECERT_POISON
+
+
+@utils.register_interface(ExtensionType)
 class TLSFeature(object):
     oid = ExtensionOID.TLS_FEATURE
 
diff --git a/src/cryptography/x509/oid.py b/src/cryptography/x509/oid.py
index 90003d7..77e3fa6 100644
--- a/src/cryptography/x509/oid.py
+++ b/src/cryptography/x509/oid.py
@@ -91,6 +91,9 @@
     PRECERT_SIGNED_CERTIFICATE_TIMESTAMPS = (
         ObjectIdentifier("1.3.6.1.4.1.11129.2.4.2")
     )
+    PRECERT_POISON = (
+        ObjectIdentifier("1.3.6.1.4.1.11129.2.4.3")
+    )
 
 
 class CRLEntryExtensionOID(object):
diff --git a/tests/x509/test_x509_ext.py b/tests/x509/test_x509_ext.py
index c052f85..7e0ae22 100644
--- a/tests/x509/test_x509_ext.py
+++ b/tests/x509/test_x509_ext.py
@@ -4442,6 +4442,35 @@
 
 @pytest.mark.requires_backend_interface(interface=RSABackend)
 @pytest.mark.requires_backend_interface(interface=X509Backend)
+class TestPrecertPoisonExtension(object):
+    def test_load(self, backend):
+        cert = _load_cert(
+            os.path.join("x509", "cryptography.io.precert.pem"),
+            x509.load_pem_x509_certificate,
+            backend
+        )
+        poison = cert.extensions.get_extension_for_oid(
+            ExtensionOID.PRECERT_POISON
+        ).value
+        assert isinstance(poison, x509.PrecertPoison)
+        poison = cert.extensions.get_extension_for_class(
+            x509.PrecertPoison
+        ).value
+        assert isinstance(poison, x509.PrecertPoison)
+
+    def test_generate(self, backend):
+        private_key = RSA_KEY_2048.private_key(backend)
+        cert = _make_certbuilder(private_key).add_extension(
+            x509.PrecertPoison(), critical=True
+        ).sign(private_key, hashes.SHA256(), backend)
+        poison = cert.extensions.get_extension_for_oid(
+            ExtensionOID.PRECERT_POISON
+        ).value
+        assert isinstance(poison, x509.PrecertPoison)
+
+
+@pytest.mark.requires_backend_interface(interface=RSABackend)
+@pytest.mark.requires_backend_interface(interface=X509Backend)
 class TestPrecertificateSignedCertificateTimestampsExtension(object):
     def test_init(self):
         with pytest.raises(TypeError):