Add is_signature_valid method on CertificateRevocationList (#3849)

diff --git a/docs/development/test-vectors.rst b/docs/development/test-vectors.rst
index eb95a62..ec6a1d0 100644
--- a/docs/development/test-vectors.rst
+++ b/docs/development/test-vectors.rst
@@ -369,6 +369,11 @@
 * ``crl_ian_aia_aki.pem`` - Contains a CRL with ``IssuerAlternativeName``,
   ``AuthorityInformationAccess``, ``AuthorityKeyIdentifier`` and ``CRLNumber``
   extensions.
+* ``valid_signature.pem`` - Contains a CRL with the public key which was used
+  to generate it.
+* ``invalid_signature.pem`` - Contains a CRL with the last signature byte
+  incremented by 1 to produce an invalid signature, and the public key which
+  was used to generate it.
 
 Hashes
 ~~~~~~
diff --git a/docs/x509/reference.rst b/docs/x509/reference.rst
index 8b97611..47f7625 100644
--- a/docs/x509/reference.rst
+++ b/docs/x509/reference.rst
@@ -563,6 +563,12 @@
             over the network and used as part of a certificate verification
             process.
 
+    .. method:: is_signature_valid(public_key)
+
+        .. versionadded:: 2.1
+
+        Returns True if the CRL signature is correct for given public key,
+        False otherwise.
 
 X.509 Certificate Builder
 ~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/src/cryptography/hazmat/backends/openssl/x509.py b/src/cryptography/hazmat/backends/openssl/x509.py
index 5bf0438..9637fc0 100644
--- a/src/cryptography/hazmat/backends/openssl/x509.py
+++ b/src/cryptography/hazmat/backends/openssl/x509.py
@@ -17,6 +17,7 @@
     _asn1_string_to_bytes, _decode_x509_name, _obj2txt, _parse_asn1_time
 )
 from cryptography.hazmat.primitives import hashes, serialization
+from cryptography.hazmat.primitives.asymmetric import dsa, ec, rsa
 
 
 @utils.register_interface(x509.Certificate)
@@ -338,6 +339,21 @@
     def extensions(self):
         return _CRL_EXTENSION_PARSER.parse(self._backend, self._x509_crl)
 
+    def is_signature_valid(self, public_key):
+        if not isinstance(public_key, (dsa.DSAPublicKey, rsa.RSAPublicKey,
+                                       ec.EllipticCurvePublicKey)):
+            raise TypeError('Expecting one of DSAPublicKey, RSAPublicKey,'
+                            ' or EllipticCurvePublicKey.')
+        res = self._backend._lib.X509_CRL_verify(
+            self._x509_crl, public_key._evp_pkey
+        )
+
+        if res != 1:
+            self._backend._consume_errors()
+            return False
+
+        return True
+
 
 @utils.register_interface(x509.CertificateSigningRequest)
 class _CertificateSigningRequest(object):
diff --git a/src/cryptography/x509/base.py b/src/cryptography/x509/base.py
index ffa7191..2c96c5b 100644
--- a/src/cryptography/x509/base.py
+++ b/src/cryptography/x509/base.py
@@ -250,6 +250,12 @@
         Checks not equal.
         """
 
+    @abc.abstractmethod
+    def is_signature_valid(self, public_key):
+        """
+        Verifies signature of revocation list against given public key.
+        """
+
 
 @six.add_metaclass(abc.ABCMeta)
 class CertificateSigningRequest(object):
diff --git a/tests/test_x509.py b/tests/test_x509.py
index 58d3e54..5285436 100644
--- a/tests/test_x509.py
+++ b/tests/test_x509.py
@@ -336,6 +336,47 @@
         with pytest.raises(TypeError):
             crl.public_bytes('NotAnEncoding')
 
+    def test_verify_bad(self, backend):
+        crl = _load_cert(
+            os.path.join("x509", "custom", "invalid_signature.pem"),
+            x509.load_pem_x509_crl,
+            backend
+        )
+        crt = _load_cert(
+            os.path.join("x509", "custom", "invalid_signature.pem"),
+            x509.load_pem_x509_certificate,
+            backend
+        )
+
+        assert not crl.is_signature_valid(crt.public_key())
+
+    def test_verify_good(self, backend):
+        crl = _load_cert(
+            os.path.join("x509", "custom", "valid_signature.pem"),
+            x509.load_pem_x509_crl,
+            backend
+        )
+        crt = _load_cert(
+            os.path.join("x509", "custom", "valid_signature.pem"),
+            x509.load_pem_x509_certificate,
+            backend
+        )
+
+        assert crl.is_signature_valid(crt.public_key())
+
+    def test_verify_argument_must_be_a_public_key(self, backend):
+        crl = _load_cert(
+            os.path.join("x509", "custom", "valid_signature.pem"),
+            x509.load_pem_x509_crl,
+            backend
+        )
+
+        with pytest.raises(TypeError):
+            crl.is_signature_valid("not a public key")
+
+        with pytest.raises(TypeError):
+            crl.is_signature_valid(object)
+
 
 @pytest.mark.requires_backend_interface(interface=X509Backend)
 class TestRevokedCertificate(object):
diff --git a/vectors/cryptography_vectors/x509/custom/invalid_signature.pem b/vectors/cryptography_vectors/x509/custom/invalid_signature.pem
new file mode 100644
index 0000000..2fc483d
--- /dev/null
+++ b/vectors/cryptography_vectors/x509/custom/invalid_signature.pem
@@ -0,0 +1,28 @@
+-----BEGIN X509 CRL-----
+MIIBfTBnAgEBMA0GCSqGSIb3DQEBCwUAMCUxIzAhBgNVBAMMGmludmFsaWRfc2ln
+bmF0dXJlIENSTCB0ZXN0Fw0xNzA4MDYwMTQ4MjVaFw0xNzA5MDUwMTQ4MjVaoA4w
+DDAKBgNVHRQEAwIBAzANBgkqhkiG9w0BAQsFAAOCAQEAFgGnFwwqviPvA0bfmnvI
+c6oGIlq9Bmx/vSH6gwLCuGWn2BrKCWCIJNEtK4hrTfQRASb/uywHvhnByAE2lQlY
+9FiefdvXgF5zEah/gV/2A0azvqfvOlPBLzreeoW3Q1fizmip3XN1fXiq8cXBpEYt
+SRTJPzgbHvIu50EB2J0hs+rGo1hPTDtZn/r63hcQzUhIWQVmwP+NOzhpUcdnQj3/
+pn6BAJcxyYO2xDoUIncq586k8XVqshEl9xVwJMKhDDk84m/WQZg8i8szgI/muFsm
+3vilMgIISrTMYeFIZWAy8rYfKLDMlmAtPRXYqyqOdTsLqz2X3RDMRHMXf1Vf8V31
+vA==
+-----END X509 CRL-----
+-----BEGIN CERTIFICATE-----
+MIICxjCCAa4CCQCETsDmKRzISDANBgkqhkiG9w0BAQsFADAlMSMwIQYDVQQDDBpp
+bnZhbGlkX3NpZ25hdHVyZSBDUkwgdGVzdDAeFw0xNzA4MDYwMTM5MzRaFw0xNzA5
+MDUwMTM5MzRaMCUxIzAhBgNVBAMMGmludmFsaWRfc2lnbmF0dXJlIENSTCB0ZXN0
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwUMEv2zCY/YMUrmuTqF0
+mWvRTB5aU9YQFOT882jTHeFvb4ZKIQDUBz+B6UObGPcdwJv/S1srhcLa4dWEEkVh
+GrFRXzPxZOXS/NbMgqnlxtkP3SjiINmYVSUY4+zXpneM32QXbEoQQoYkHHLiHg4l
+L2hQHGYE47cRFzJ9IqIIFGx5Sh+fAWm40CzCDTaVWd7C4IsamOdYhvflpXJZcKtQ
+ni1vQl5IEunsGP7nHdOcOBSi6LkNj2jGhflPwuOlWEXeqbHxAfd7We6fMPXDjZVR
+TRTa+MHpfA1yCZgpr9NOmu8h115zWx+/pDjsNf9PqNSGgfRTsayT8AYFi5SzfORm
+xQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQA0xIWlIp0LmyqAEASHppuDFbKLVlRb
+H1oSQRbWiZpRpBHIdPEtEqp8+2KOQXyZEWzkGwuo46++Zt/aepGOBEbSAljvyJV9
+P4JqH/jJeHkZSC+/CYcegMh14xr6X3OYe+go+huwPSGULYbDguhgzAzpU+0LHWsF
+Q/JCKZOjDutLJekzbvNeUgxkNFtsL1OhWYvSzngAph0OJ0QsDTyUhHv2iigHHv/I
+Y83lNYi6AriqE2L42leHBcvG6Gnc8Ipx+su6r2a/KnHt8XeWXf/OK/HbqKiCG1AV
+Xzp8dgfQjXvDdTLl9yL+jjeOcMdOemY3x2EzQPX1God0rl1pvZFIRYih
+-----END CERTIFICATE-----
diff --git a/vectors/cryptography_vectors/x509/custom/valid_signature.pem b/vectors/cryptography_vectors/x509/custom/valid_signature.pem
new file mode 100644
index 0000000..9c21809
--- /dev/null
+++ b/vectors/cryptography_vectors/x509/custom/valid_signature.pem
@@ -0,0 +1,28 @@
+-----BEGIN X509 CRL-----
+MIIBfTBnAgEBMA0GCSqGSIb3DQEBCwUAMCUxIzAhBgNVBAMMGmludmFsaWRfc2ln
+bmF0dXJlIENSTCB0ZXN0Fw0xNzA4MDYwMTQ4MjVaFw0xNzA5MDUwMTQ4MjVaoA4w
+DDAKBgNVHRQEAwIBAzANBgkqhkiG9w0BAQsFAAOCAQEAFgGnFwwqviPvA0bfmnvI
+c6oGIlq9Bmx/vSH6gwLCuGWn2BrKCWCIJNEtK4hrTfQRASb/uywHvhnByAE2lQlY
+9FiefdvXgF5zEah/gV/2A0azvqfvOlPBLzreeoW3Q1fizmip3XN1fXiq8cXBpEYt
+SRTJPzgbHvIu50EB2J0hs+rGo1hPTDtZn/r63hcQzUhIWQVmwP+NOzhpUcdnQj3/
+pn6BAJcxyYO2xDoUIncq586k8XVqshEl9xVwJMKhDDk84m/WQZg8i8szgI/muFsm
+3vilMgIISrTMYeFIZWAy8rYfKLDMlmAtPRXYqyqOdTsLqz2X3RDMRHMXf1Vf8V31
+ug==
+-----END X509 CRL-----
+-----BEGIN CERTIFICATE-----
+MIICxjCCAa4CCQCETsDmKRzISDANBgkqhkiG9w0BAQsFADAlMSMwIQYDVQQDDBpp
+bnZhbGlkX3NpZ25hdHVyZSBDUkwgdGVzdDAeFw0xNzA4MDYwMTM5MzRaFw0xNzA5
+MDUwMTM5MzRaMCUxIzAhBgNVBAMMGmludmFsaWRfc2lnbmF0dXJlIENSTCB0ZXN0
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwUMEv2zCY/YMUrmuTqF0
+mWvRTB5aU9YQFOT882jTHeFvb4ZKIQDUBz+B6UObGPcdwJv/S1srhcLa4dWEEkVh
+GrFRXzPxZOXS/NbMgqnlxtkP3SjiINmYVSUY4+zXpneM32QXbEoQQoYkHHLiHg4l
+L2hQHGYE47cRFzJ9IqIIFGx5Sh+fAWm40CzCDTaVWd7C4IsamOdYhvflpXJZcKtQ
+ni1vQl5IEunsGP7nHdOcOBSi6LkNj2jGhflPwuOlWEXeqbHxAfd7We6fMPXDjZVR
+TRTa+MHpfA1yCZgpr9NOmu8h115zWx+/pDjsNf9PqNSGgfRTsayT8AYFi5SzfORm
+xQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQA0xIWlIp0LmyqAEASHppuDFbKLVlRb
+H1oSQRbWiZpRpBHIdPEtEqp8+2KOQXyZEWzkGwuo46++Zt/aepGOBEbSAljvyJV9
+P4JqH/jJeHkZSC+/CYcegMh14xr6X3OYe+go+huwPSGULYbDguhgzAzpU+0LHWsF
+Q/JCKZOjDutLJekzbvNeUgxkNFtsL1OhWYvSzngAph0OJ0QsDTyUhHv2iigHHv/I
+Y83lNYi6AriqE2L42leHBcvG6Gnc8Ipx+su6r2a/KnHt8XeWXf/OK/HbqKiCG1AV
+Xzp8dgfQjXvDdTLl9yL+jjeOcMdOemY3x2EzQPX1God0rl1pvZFIRYih
+-----END CERTIFICATE-----