Repository: pyca/cryptography

Issue:
Implement __eq__ for x509.Certificate objects
This will add it to the interface as well as add an OpenSSL implementation. How should we document classes that implement equality operations?



Discussion:
\o/


Resolving patch:
diff --git a/src/cryptography/hazmat/backends/openssl/x509.py b/src/cryptography/hazmat/backends/openssl/x509.py
--- a/src/cryptography/hazmat/backends/openssl/x509.py
+++ b/src/cryptography/hazmat/backends/openssl/x509.py
@@ -123,6 +123,16 @@ def __init__(self, backend, x509):
         self._backend = backend
         self._x509 = x509
 
+    def __eq__(self, other):
+        if not isinstance(other, x509.Certificate):
+            return NotImplemented
+
+        res = self._backend._lib.X509_cmp(self._x509, other._x509)
+        return res == 0
+
+    def __ne__(self, other):
+        return not self == other
+
     def fingerprint(self, algorithm):
         h = hashes.Hash(algorithm, self._backend)
         bio = self._backend._create_mem_bio()
diff --git a/src/cryptography/hazmat/bindings/openssl/x509.py b/src/cryptography/hazmat/bindings/openssl/x509.py
--- a/src/cryptography/hazmat/bindings/openssl/x509.py
+++ b/src/cryptography/hazmat/bindings/openssl/x509.py
@@ -115,6 +115,7 @@
 X509 *X509_new(void);
 void X509_free(X509 *);
 X509 *X509_dup(X509 *);
+int X509_cmp(const X509 *, const X509 *);
 
 int X509_print_ex(BIO *, X509 *, unsigned long, unsigned long);
 
diff --git a/src/cryptography/x509.py b/src/cryptography/x509.py
--- a/src/cryptography/x509.py
+++ b/src/cryptography/x509.py
@@ -730,6 +730,18 @@ def signature_hash_algorithm(self):
         in the certificate.
         """
 
+    @abc.abstractmethod
+    def __eq__(self, other):
+        """
+        Checks equality.
+        """
+
+    @abc.abstractmethod
+    def __ne__(self, other):
+        """
+        Checks not equal.
+        """
+
 
 @six.add_metaclass(abc.ABCMeta)
 class CertificateSigningRequest(object):


Test patch:
diff --git a/tests/test_x509.py b/tests/test_x509.py
--- a/tests/test_x509.py
+++ b/tests/test_x509.py
@@ -313,6 +313,36 @@ def test_invalid_version_cert(self, backend):
 
         assert exc.value.parsed_version == 7
 
+    def test_eq(self, backend):
+        cert = _load_cert(
+            os.path.join("x509", "custom", "post2000utctime.pem"),
+            x509.load_pem_x509_certificate,
+            backend
+        )
+        cert2 = _load_cert(
+            os.path.join("x509", "custom", "post2000utctime.pem"),
+            x509.load_pem_x509_certificate,
+            backend
+        )
+        assert cert == cert2
+
+    def test_ne(self, backend):
+        cert = _load_cert(
+            os.path.join("x509", "custom", "post2000utctime.pem"),
+            x509.load_pem_x509_certificate,
+            backend
+        )
+        cert2 = _load_cert(
+            os.path.join(
+                "x509", "PKITS_data", "certs",
+                "ValidGeneralizedTimenotAfterDateTest8EE.crt"
+            ),
+            x509.load_der_x509_certificate,
+            backend
+        )
+        assert cert != cert2
+        assert cert != object()
+
     def test_version_1_cert(self, backend):
         cert = _load_cert(
             os.path.join("x509", "v1_cert.pem"),