Repository: pyca/cryptography

Issue:
Overly loose None checking in x509 extensions causing ValueError to be thrown
When parsing some certificates I hit the following error:

ValueError: authority_cert_issuer and authority_cert_serial_number must both be present or both None

It turns out that the cert at fault has authority_cert_serial_number equal to 0, after a little digging i found that this chokes on the following code:

``` python
if authority_cert_issuer or authority_cert_serial_number:
    if not authority_cert_issuer or not authority_cert_serial_number:
        raise ValueError(
            "authority_cert_issuer and authority_cert_serial_number "
            "must both be present or both None"
        )
```

found at https://github.com/pyca/cryptography/blob/5660526e6baadb10a1969a32a2dda3bc14c78133/src/cryptography/x509/extensions.py#L159

as authority_cert_serial_number can be falsey should it not be checked with is None / is not None?



Discussion:
Yup, this is a bug, I've tagged it for our next release.

thank you ^_^


Resolving patch:
diff --git a/src/cryptography/x509/extensions.py b/src/cryptography/x509/extensions.py
--- a/src/cryptography/x509/extensions.py
+++ b/src/cryptography/x509/extensions.py
@@ -155,25 +155,28 @@ class AuthorityKeyIdentifier(object):
 
     def __init__(self, key_identifier, authority_cert_issuer,
                  authority_cert_serial_number):
-        if authority_cert_issuer or authority_cert_serial_number:
-            if not authority_cert_issuer or not authority_cert_serial_number:
-                raise ValueError(
-                    "authority_cert_issuer and authority_cert_serial_number "
-                    "must both be present or both None"
-                )
+        if (authority_cert_issuer is None) != (
+            authority_cert_serial_number is None
+        ):
+            raise ValueError(
+                "authority_cert_issuer and authority_cert_serial_number "
+                "must both be present or both None"
+            )
 
-            if not all(
-                isinstance(x, GeneralName) for x in authority_cert_issuer
-            ):
-                raise TypeError(
-                    "authority_cert_issuer must be a list of GeneralName "
-                    "objects"
-                )
+        if authority_cert_issuer is not None and not all(
+            isinstance(x, GeneralName) for x in authority_cert_issuer
+        ):
+            raise TypeError(
+                "authority_cert_issuer must be a list of GeneralName "
+                "objects"
+            )
 
-            if not isinstance(authority_cert_serial_number, six.integer_types):
-                raise TypeError(
-                    "authority_cert_serial_number must be an integer"
-                )
+        if authority_cert_serial_number is not None and not isinstance(
+            authority_cert_serial_number, six.integer_types
+        ):
+            raise TypeError(
+                "authority_cert_serial_number must be an integer"
+            )
 
         self._key_identifier = key_identifier
         self._authority_cert_issuer = authority_cert_issuer


Test patch:
diff --git a/tests/test_x509_ext.py b/tests/test_x509_ext.py
--- a/tests/test_x509_ext.py
+++ b/tests/test_x509_ext.py
@@ -835,6 +835,13 @@ def test_authority_cert_serial_and_issuer_none(self):
         assert aki.authority_cert_issuer is None
         assert aki.authority_cert_serial_number is None
 
+    def test_authority_cert_serial_zero(self):
+        dns = x509.DNSName(u"SomeIssuer")
+        aki = x509.AuthorityKeyIdentifier(b"id", [dns], 0)
+        assert aki.key_identifier == b"id"
+        assert aki.authority_cert_issuer == [dns]
+        assert aki.authority_cert_serial_number == 0
+
     def test_repr(self):
         dirname = x509.DirectoryName(
             x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, u'myCN')])