Merge pull request #2139 from alex/remove-fast-path

Removed the fastpath from binding initialization. These aren't instantiated frequently enough for this to be worth it, and it improves our coverage.
diff --git a/.travis.yml b/.travis.yml
index 8ad514f..b8206e0 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -120,7 +120,7 @@
     - ./.travis/run.sh
 
 after_success:
-    - source ~/.venv/bin/activate && bash <(curl -s https://codecov.io/bash) -e TRAVIS_OS_NAME,TOXENV,OPENSSL
+    - ./.travis/upload_coverage.sh
 
 notifications:
     irc:
diff --git a/.travis/upload_coverage.sh b/.travis/upload_coverage.sh
new file mode 100755
index 0000000..554116f
--- /dev/null
+++ b/.travis/upload_coverage.sh
@@ -0,0 +1,10 @@
+#!/bin/bash
+
+set -e
+set -x
+
+NO_COVERAGE_TOXENVS=(pypy pypy3 pep8 py3pep8 docs)
+if ! [[ "${NO_COVERAGE_TOXENVS[*]}" =~ "${TOXENV}" ]]; then
+    source ~/.venv/bin/activate
+    bash <(curl -s https://codecov.io/bash) -e TRAVIS_OS_NAME,TOXENV,OPENSSL
+fi
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 475a2a3..85f8447 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -93,7 +93,7 @@
   Note that unsupported extensions with the critical flag raise
   :class:`~cryptography.x509.UnsupportedExtension` while unsupported extensions
   set to non-critical are silently ignored. Read the
-  :doc:`X.509 documentation</x509>` for more information.
+  :doc:`X.509 documentation</x509/index>` for more information.
 
 0.8.2 - 2015-04-10
 ~~~~~~~~~~~~~~~~~~
@@ -120,7 +120,7 @@
   from :mod:`~cryptography.hazmat.primitives.interfaces` to
   :mod:`~cryptography.hazmat.primitives.kdf`.
 * Added support for parsing X.509 names. See the
-  :doc:`X.509 documentation</x509>` for more information.
+  :doc:`X.509 documentation</x509/index>` for more information.
 * Added
   :func:`~cryptography.hazmat.primitives.serialization.load_der_private_key` to
   support loading of DER encoded private keys and
@@ -256,7 +256,7 @@
   support the loading of OpenSSH public keys (:rfc:`4253`). Only RSA and DSA
   keys are currently supported.
 * Added initial support for X.509 certificate parsing. See the
-  :doc:`X.509 documentation</x509>` for more information.
+  :doc:`X.509 documentation</x509/index>` for more information.
 
 0.6.1 - 2014-10-15
 ~~~~~~~~~~~~~~~~~~
diff --git a/docs/hazmat/primitives/asymmetric/serialization.rst b/docs/hazmat/primitives/asymmetric/serialization.rst
index 7839f34..8d51f0d 100644
--- a/docs/hazmat/primitives/asymmetric/serialization.rst
+++ b/docs/hazmat/primitives/asymmetric/serialization.rst
@@ -97,8 +97,8 @@
 .. note::
 
     A PEM block which starts with ``-----BEGIN CERTIFICATE-----`` is not a
-    public or private key, it's an :doc:`X.509 Certificate </x509>`. You can
-    load it using :func:`~cryptography.x509.load_pem_x509_certificate` and
+    public or private key, it's an :doc:`X.509 Certificate </x509/index>`. You
+    can load it using :func:`~cryptography.x509.load_pem_x509_certificate` and
     extract the public key with
     :meth:`Certificate.public_key <cryptography.x509.Certificate.public_key>`.
 
diff --git a/docs/index.rst b/docs/index.rst
index 35f80a2..5c26a75 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -63,7 +63,7 @@
     :maxdepth: 2
 
     fernet
-    x509
+    x509/index
     random-numbers
     exceptions
     faq
diff --git a/docs/x509/index.rst b/docs/x509/index.rst
new file mode 100644
index 0000000..c3fa1ed
--- /dev/null
+++ b/docs/x509/index.rst
@@ -0,0 +1,14 @@
+X.509
+=====
+
+X.509 is an ITU-T standard for a `public key infrastructure`_. X.509v3 is
+defined in :rfc:`5280` (which obsoletes :rfc:`2459` and :rfc:`3280`). X.509
+certificates are commonly used in protocols like `TLS`_.
+
+.. toctree::
+    :maxdepth: 2
+
+    reference
+
+.. _`public key infrastructure`: https://en.wikipedia.org/wiki/Public_key_infrastructure
+.. _`TLS`: https://en.wikipedia.org/wiki/Transport_Layer_Security
diff --git a/docs/x509.rst b/docs/x509/reference.rst
similarity index 98%
rename from docs/x509.rst
rename to docs/x509/reference.rst
index bcb6ee6..9179468 100644
--- a/docs/x509.rst
+++ b/docs/x509/reference.rst
@@ -1,5 +1,5 @@
-X.509
-=====
+X.509 Reference
+===============
 
 .. currentmodule:: cryptography.x509
 
@@ -86,10 +86,6 @@
     -----END CERTIFICATE-----
     """.strip()
 
-X.509 is an ITU-T standard for a `public key infrastructure`_. X.509v3 is
-defined in :rfc:`5280` (which obsoletes :rfc:`2459` and :rfc:`3280`). X.509
-certificates are commonly used in protocols like `TLS`_.
-
 Loading Certificates
 ~~~~~~~~~~~~~~~~~~~~
 
@@ -1582,7 +1578,5 @@
         types can be found in `RFC 5280 section 4.2.1.6`_.
 
 
-.. _`public key infrastructure`: https://en.wikipedia.org/wiki/Public_key_infrastructure
-.. _`TLS`: https://en.wikipedia.org/wiki/Transport_Layer_Security
 .. _`RFC 5280 section 4.2.1.1`: https://tools.ietf.org/html/rfc5280#section-4.2.1.1
 .. _`RFC 5280 section 4.2.1.6`: https://tools.ietf.org/html/rfc5280#section-4.2.1.6
diff --git a/src/_cffi_src/openssl/asn1.py b/src/_cffi_src/openssl/asn1.py
index 01d6f4c..5f8ca69 100644
--- a/src/_cffi_src/openssl/asn1.py
+++ b/src/_cffi_src/openssl/asn1.py
@@ -157,6 +157,7 @@
 int ASN1_STRING_set_default_mask_asc(char *);
 
 int i2d_ASN1_TYPE(ASN1_TYPE *, unsigned char **);
+ASN1_TYPE *d2i_ASN1_TYPE(ASN1_TYPE **, const unsigned char **, long);
 """
 
 CUSTOMIZATIONS = """
diff --git a/src/_cffi_src/openssl/x509v3.py b/src/_cffi_src/openssl/x509v3.py
index 0f5306d..8e42b65 100644
--- a/src/_cffi_src/openssl/x509v3.py
+++ b/src/_cffi_src/openssl/x509v3.py
@@ -193,6 +193,9 @@
 NAME_CONSTRAINTS *NAME_CONSTRAINTS_new(void);
 void NAME_CONSTRAINTS_free(NAME_CONSTRAINTS *);
 
+OTHERNAME *OTHERNAME_new(void);
+void OTHERNAME_free(OTHERNAME *);
+
 void *X509V3_set_ctx_nodb(X509V3_CTX *);
 
 int i2d_GENERAL_NAMES(GENERAL_NAMES *, unsigned char **);
diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py
index 7255b47..637b28c 100644
--- a/src/cryptography/hazmat/backends/openssl/backend.py
+++ b/src/cryptography/hazmat/backends/openssl/backend.py
@@ -199,6 +199,28 @@
             )
             gn.type = backend._lib.GEN_IPADD
             gn.d.iPAddress = ipaddr
+        elif isinstance(alt_name, x509.OtherName):
+            gn = backend._lib.GENERAL_NAME_new()
+            assert gn != backend._ffi.NULL
+            other_name = backend._lib.OTHERNAME_new()
+            assert other_name != backend._ffi.NULL
+
+            type_id = backend._lib.OBJ_txt2obj(
+                alt_name.type_id.dotted_string.encode('ascii'), 1
+            )
+            assert type_id != backend._ffi.NULL
+            data = backend._ffi.new("unsigned char[]", alt_name.value)
+            data_ptr_ptr = backend._ffi.new("unsigned char **")
+            data_ptr_ptr[0] = data
+            value = backend._lib.d2i_ASN1_TYPE(
+                backend._ffi.NULL, data_ptr_ptr, len(alt_name.value)
+            )
+            if value == backend._ffi.NULL:
+                raise ValueError("Invalid ASN.1 data")
+            other_name.type_id = type_id
+            other_name.value = value
+            gn.type = backend._lib.GEN_OTHERNAME
+            gn.d.otherName = other_name
         else:
             raise NotImplementedError(
                 "Only DNSName and RegisteredID supported right now"
diff --git a/src/cryptography/hazmat/backends/openssl/x509.py b/src/cryptography/hazmat/backends/openssl/x509.py
index d78c60f..096cbc9 100644
--- a/src/cryptography/hazmat/backends/openssl/x509.py
+++ b/src/cryptography/hazmat/backends/openssl/x509.py
@@ -141,11 +141,32 @@
         oid = _obj2txt(backend, gn.d.registeredID)
         return x509.RegisteredID(x509.ObjectIdentifier(oid))
     elif gn.type == backend._lib.GEN_IPADD:
-        return x509.IPAddress(
-            ipaddress.ip_address(
-                _asn1_string_to_bytes(backend, gn.d.iPAddress)
-            )
-        )
+        data = _asn1_string_to_bytes(backend, gn.d.iPAddress)
+        data_len = len(data)
+        if data_len == 8 or data_len == 32:
+            # This is an IPv4 or IPv6 Network and not a single IP. This
+            # type of data appears in Name Constraints. Unfortunately,
+            # ipaddress doesn't support packed bytes + netmask. Additionally,
+            # IPv6Network can only handle CIDR rather than the full 16 byte
+            # netmask. To handle this we convert the netmask to integer, then
+            # find the first 0 bit, which will be the prefix. If another 1
+            # bit is present after that the netmask is invalid.
+            base = ipaddress.ip_address(data[:data_len // 2])
+            netmask = ipaddress.ip_address(data[data_len // 2:])
+            bits = bin(int(netmask))[2:]
+            prefix = bits.find('0')
+            # If no 0 bits are found it is a /32 or /128
+            if prefix == -1:
+                prefix = len(bits)
+
+            if "1" in bits[prefix:]:
+                raise ValueError("Invalid netmask")
+
+            ip = ipaddress.ip_network(base.exploded + u"/{0}".format(prefix))
+        else:
+            ip = ipaddress.ip_address(data)
+
+        return x509.IPAddress(ip)
     elif gn.type == backend._lib.GEN_DIRNAME:
         return x509.DirectoryName(
             _decode_x509_name(backend, gn.d.directoryName)
diff --git a/src/cryptography/hazmat/primitives/asymmetric/rsa.py b/src/cryptography/hazmat/primitives/asymmetric/rsa.py
index 89eac4d..41b0089 100644
--- a/src/cryptography/hazmat/primitives/asymmetric/rsa.py
+++ b/src/cryptography/hazmat/primitives/asymmetric/rsa.py
@@ -307,6 +307,17 @@
     def __ne__(self, other):
         return not self == other
 
+    def __hash__(self):
+        return hash((
+            self.p,
+            self.q,
+            self.d,
+            self.dmp1,
+            self.dmq1,
+            self.iqmp,
+            self.public_numbers,
+        ))
+
 
 class RSAPublicNumbers(object):
     def __init__(self, e, n):
@@ -336,3 +347,6 @@
 
     def __ne__(self, other):
         return not self == other
+
+    def __hash__(self):
+        return hash((self.e, self.n))
diff --git a/src/cryptography/x509.py b/src/cryptography/x509.py
index 33c6416..8bed79e 100644
--- a/src/cryptography/x509.py
+++ b/src/cryptography/x509.py
@@ -6,8 +6,11 @@
 
 import abc
 import ipaddress
+from email.utils import parseaddr
 from enum import Enum
 
+import idna
+
 import six
 
 from cryptography import utils
@@ -901,7 +904,23 @@
         if not isinstance(value, six.text_type):
             raise TypeError("value must be a unicode string")
 
+        name, address = parseaddr(value)
+        parts = address.split(u"@")
+        if name or not address:
+            # parseaddr has found a name (e.g. Name <email>) or the entire
+            # value is an empty string.
+            raise ValueError("Invalid rfc822name value")
+        elif len(parts) == 1:
+            # Single label email name. This is valid for local delivery.
+            # No IDNA encoding needed since there is no domain component.
+            encoded = address.encode("ascii")
+        else:
+            # A normal email of the form user@domain.com. Let's attempt to
+            # encode the domain component and reconstruct the address.
+            encoded = parts[0].encode("ascii") + b"@" + idna.encode(parts[1])
+
         self._value = value
+        self._encoded = encoded
 
     value = utils.read_only_property("_value")
 
diff --git a/tests/conftest.py b/tests/conftest.py
index 6599a64..bdd17fb 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -12,10 +12,10 @@
 
 
 def pytest_generate_tests(metafunc):
-    names = metafunc.config.getoption("--backend")
-    selected_backends = select_backends(names, _available_backends())
-
     if "backend" in metafunc.fixturenames:
+        names = metafunc.config.getoption("--backend")
+        selected_backends = select_backends(names, _available_backends())
+
         filtered_backends = []
         required = metafunc.function.requires_backend_interface
         required_interfaces = [
diff --git a/tests/hazmat/primitives/test_rsa.py b/tests/hazmat/primitives/test_rsa.py
index bfeab8d..0c5f704 100644
--- a/tests/hazmat/primitives/test_rsa.py
+++ b/tests/hazmat/primitives/test_rsa.py
@@ -1705,6 +1705,22 @@
         )
         assert num != object()
 
+    def test_public_numbers_hash(self):
+        pub1 = RSAPublicNumbers(3, 17)
+        pub2 = RSAPublicNumbers(3, 17)
+        pub3 = RSAPublicNumbers(7, 21)
+
+        assert hash(pub1) == hash(pub2)
+        assert hash(pub1) != hash(pub3)
+
+    def test_private_numbers_hash(self):
+        priv1 = RSAPrivateNumbers(1, 2, 3, 4, 5, 6, RSAPublicNumbers(1, 2))
+        priv2 = RSAPrivateNumbers(1, 2, 3, 4, 5, 6, RSAPublicNumbers(1, 2))
+        priv3 = RSAPrivateNumbers(1, 2, 3, 4, 5, 6, RSAPublicNumbers(1, 3))
+
+        assert hash(priv1) == hash(priv2)
+        assert hash(priv1) != hash(priv3)
+
 
 class TestRSAPrimeFactorRecovery(object):
     @pytest.mark.parametrize(
diff --git a/tests/test_x509.py b/tests/test_x509.py
index 9b6b882..cb61726 100644
--- a/tests/test_x509.py
+++ b/tests/test_x509.py
@@ -1004,6 +1004,10 @@
                 ])),
                 x509.IPAddress(ipaddress.ip_address(u"127.0.0.1")),
                 x509.IPAddress(ipaddress.ip_address(u"ff::")),
+                x509.OtherName(
+                    type_id=x509.ObjectIdentifier("1.2.3.3.3.3"),
+                    value=b"0\x03\x02\x01\x05"
+                ),
             ]),
             critical=False,
         ).sign(private_key, hashes.SHA256(), backend)
@@ -1026,8 +1030,31 @@
             ])),
             x509.IPAddress(ipaddress.ip_address(u"127.0.0.1")),
             x509.IPAddress(ipaddress.ip_address(u"ff::")),
+            x509.OtherName(
+                type_id=x509.ObjectIdentifier("1.2.3.3.3.3"),
+                value=b"0\x03\x02\x01\x05"
+            ),
         ]
 
+    def test_invalid_asn1_othername(self, backend):
+        private_key = RSA_KEY_2048.private_key(backend)
+
+        builder = x509.CertificateSigningRequestBuilder().subject_name(
+            x509.Name([
+                x509.NameAttribute(x509.OID_COMMON_NAME, u"SAN"),
+            ])
+        ).add_extension(
+            x509.SubjectAlternativeName([
+                x509.OtherName(
+                    type_id=x509.ObjectIdentifier("1.2.3.3.3.3"),
+                    value=b"\x01\x02\x01\x05"
+                ),
+            ]),
+            critical=False,
+        )
+        with pytest.raises(ValueError):
+            builder.sign(private_key, hashes.SHA256(), backend)
+
     def test_subject_alt_name_unsupported_general_name(self, backend):
         private_key = RSA_KEY_2048.private_key(backend)
 
diff --git a/tests/test_x509_ext.py b/tests/test_x509_ext.py
index 993802b..84a4099 100644
--- a/tests/test_x509_ext.py
+++ b/tests/test_x509_ext.py
@@ -1087,6 +1087,24 @@
         assert gn != object()
 
 
+class TestRFC822Name(object):
+    def test_invalid_email(self):
+        with pytest.raises(ValueError):
+            x509.RFC822Name(u"Name <email>")
+
+        with pytest.raises(ValueError):
+            x509.RFC822Name(u"")
+
+    def test_single_label(self):
+        gn = x509.RFC822Name(u"administrator")
+        assert gn.value == u"administrator"
+
+    def test_idna(self):
+        gn = x509.RFC822Name(u"email@em\xe5\xefl.com")
+        assert gn.value == u"email@em\xe5\xefl.com"
+        assert gn._encoded == b"email@xn--eml-vla4c.com"
+
+
 class TestRegisteredID(object):
     def test_not_oid(self):
         with pytest.raises(TypeError):
@@ -2184,6 +2202,60 @@
             ]
         )
 
+    def test_permitted_excluded_with_ips(self, backend):
+        cert = _load_cert(
+            os.path.join(
+                "x509", "custom", "nc_permitted_excluded.pem"
+            ),
+            x509.load_pem_x509_certificate,
+            backend
+        )
+        nc = cert.extensions.get_extension_for_oid(
+            x509.OID_NAME_CONSTRAINTS
+        ).value
+        assert nc == x509.NameConstraints(
+            permitted_subtrees=[
+                x509.IPAddress(ipaddress.IPv4Network(u"192.168.0.0/24")),
+                x509.IPAddress(ipaddress.IPv6Network(u"FF:0:0:0:0:0:0:0/96")),
+            ],
+            excluded_subtrees=[
+                x509.DNSName(u".domain.com"),
+                x509.UniformResourceIdentifier(u"http://test.local"),
+            ]
+        )
+
+    def test_single_ip_netmask(self, backend):
+        cert = _load_cert(
+            os.path.join(
+                "x509", "custom", "nc_single_ip_netmask.pem"
+            ),
+            x509.load_pem_x509_certificate,
+            backend
+        )
+        nc = cert.extensions.get_extension_for_oid(
+            x509.OID_NAME_CONSTRAINTS
+        ).value
+        assert nc == x509.NameConstraints(
+            permitted_subtrees=[
+                x509.IPAddress(ipaddress.IPv6Network(u"FF:0:0:0:0:0:0:0/128")),
+                x509.IPAddress(ipaddress.IPv4Network(u"192.168.0.1/32")),
+            ],
+            excluded_subtrees=None
+        )
+
+    def test_invalid_netmask(self, backend):
+        cert = _load_cert(
+            os.path.join(
+                "x509", "custom", "nc_invalid_ip_netmask.pem"
+            ),
+            x509.load_pem_x509_certificate,
+            backend
+        )
+        with pytest.raises(ValueError):
+            cert.extensions.get_extension_for_oid(
+                x509.OID_NAME_CONSTRAINTS
+            )
+
 
 class TestDistributionPoint(object):
     def test_distribution_point_full_name_not_general_names(self):