Merge branch 'master' into crl_ossl_backend
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index fdea8c3..ec27596 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -6,6 +6,8 @@
 
 .. note:: This version is not yet released and is under active development.
 
+* Added support for Elliptic Curve Diffie-Hellman with
+  :class:`~cryptography.hazmat.primitives.asymmetric.ec.ECDH`.
 * Added :class:`~cryptography.hazmat.primitives.kdf.x963kdf.X963KDF`.
 
 1.0.2 - 2015-09-27
diff --git a/docs/development/test-vectors.rst b/docs/development/test-vectors.rst
index bfe7633..0b249cc 100644
--- a/docs/development/test-vectors.rst
+++ b/docs/development/test-vectors.rst
@@ -302,6 +302,8 @@
   to "1.2.3.4". The CRL uses an unsupported MD2 signature algorithm.
 * ``crl_unsupported_reason.pem`` - Contains a CRL with one revocation which has
   an unsupported reason code.
+* ``crl_inval_cert_issuer_entry_ext.pem`` - Contains a CRL with one revocation
+  which has one entry extension for certificate issuer with an empty value.
 
 Hashes
 ~~~~~~
diff --git a/docs/hazmat/primitives/asymmetric/dsa.rst b/docs/hazmat/primitives/asymmetric/dsa.rst
index 4eb17e3..1429cb0 100644
--- a/docs/hazmat/primitives/asymmetric/dsa.rst
+++ b/docs/hazmat/primitives/asymmetric/dsa.rst
@@ -86,8 +86,14 @@
 Verification
 ~~~~~~~~~~~~
 
-Using a :class:`~cryptography.hazmat.primitives.asymmetric.dsa.DSAPublicKey`
-provider.
+Verification is performed using a
+:class:`~cryptography.hazmat.primitives.asymmetric.dsa.DSAPublicKey` provider.
+You can get a public key object with
+:func:`~cryptography.hazmat.primitives.serialization.load_pem_public_key`,
+:func:`~cryptography.hazmat.primitives.serialization.load_der_public_key`,
+:meth:`~cryptography.hazmat.primitives.asymmetric.dsa.DSAPublicNumbers.public_key`
+, or
+:meth:`~cryptography.hazmat.primitives.asymmetric.dsa.DSAPrivateKey.public_key`.
 
 .. doctest::
 
diff --git a/docs/hazmat/primitives/asymmetric/ec.rst b/docs/hazmat/primitives/asymmetric/ec.rst
index 6356c27..e4df9b1 100644
--- a/docs/hazmat/primitives/asymmetric/ec.rst
+++ b/docs/hazmat/primitives/asymmetric/ec.rst
@@ -12,7 +12,7 @@
 
     Generate a new private key on ``curve`` for use with ``backend``.
 
-    :param backend: A :class:`EllipticCurve` provider.
+    :param curve: A :class:`EllipticCurve` provider.
 
     :param backend: A
         :class:`~cryptography.hazmat.backends.interfaces.EllipticCurveBackend`
@@ -122,6 +122,32 @@
         :returns: A new instance of a :class:`EllipticCurvePublicKey`
             provider.
 
+Elliptic Curve Key Exchange algorithm
+-------------------------------------
+
+.. class:: ECDH()
+
+    .. versionadded:: 1.1
+
+    The Elliptic Curve Diffie-Hellman Key Exchange algorithm first standardized
+    in NIST publication `800-56A`_, and later in `800-56Ar2`_.
+
+    For most applications the ``shared_key`` should be passed to a key
+    derivation function.
+
+    .. doctest::
+
+        >>> from cryptography.hazmat.backends import default_backend
+        >>> from cryptography.hazmat.primitives.asymmetric import ec
+        >>> private_key = ec.generate_private_key(
+        ...     ec.SECP384R1(), default_backend()
+        ... )
+        >>> peer_public_key = ec.generate_private_key(
+        ...     ec.SECP384R1(), default_backend()
+        ... ).public_key()
+        >>> shared_key = private_key.exchange(ec.ECDH(), peer_public_key)
+
+
 Elliptic Curves
 ---------------
 
@@ -314,6 +340,22 @@
         :returns:
             :class:`~cryptography.hazmat.primitives.asymmetric.AsymmetricSignatureContext`
 
+    .. method:: exchange(algorithm, peer_public_key)
+
+        Perform's a key exchange operation using the provided algorithm with
+        the peer's public key.
+
+        For most applications the result should be passed to a key derivation
+        function.
+
+        :param algorithm: The key exchange algorithm, currently only
+            :class:`~cryptography.hazmat.primitives.asymmetric.ec.ECDH` is
+            supported.
+        :param EllipticCurvePublicKey peer_public_key: The public key for the
+            peer.
+
+        :returns bytes: A shared key.
+
     .. method:: public_key()
 
         :return: :class:`EllipticCurvePublicKey`
@@ -419,6 +461,8 @@
 
 .. _`FIPS 186-3`: http://csrc.nist.gov/publications/fips/fips186-3/fips_186-3.pdf
 .. _`FIPS 186-4`: http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf
+.. _`800-56A`: http://csrc.nist.gov/publications/nistpubs/800-56A/SP800-56A_Revision1_Mar08-2007.pdf
+.. _`800-56Ar2`: http://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-56Ar2.pdf
 .. _`some concern`: https://crypto.stackexchange.com/questions/10263/should-we-trust-the-nist-recommended-ecc-parameters
 .. _`less than 224 bits`: http://www.ecrypt.eu.org/ecrypt2/documents/D.SPA.20.pdf
 .. _`elliptic curve diffie-hellman is faster than diffie-hellman`: http://digitalcommons.unl.edu/cgi/viewcontent.cgi?article=1100&context=cseconfwork
diff --git a/docs/hazmat/primitives/asymmetric/rsa.rst b/docs/hazmat/primitives/asymmetric/rsa.rst
index f88750c..bc2402d 100644
--- a/docs/hazmat/primitives/asymmetric/rsa.rst
+++ b/docs/hazmat/primitives/asymmetric/rsa.rst
@@ -163,9 +163,15 @@
 ~~~~~~~~~~~~
 
 The previous section describes what to do if you have a private key and want to
-sign something. If you have a public key, a message, and a signature, you can
-check that the public key genuinely was used to sign that specific message. You
-also need to know which signing algorithm was used:
+sign something. If you have a public key, a message, a signature, and the
+signing algorithm that was used you can check that the private key associated
+with a given public key was used to sign that specific message.  You can obtain
+a public key to use in verification using
+:func:`~cryptography.hazmat.primitives.serialization.load_pem_public_key`,
+:func:`~cryptography.hazmat.primitives.serialization.load_der_public_key`,
+:meth:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPublicNumbers.public_key`
+, or
+:meth:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKey.public_key`.
 
 .. doctest::
 
diff --git a/docs/hazmat/primitives/asymmetric/serialization.rst b/docs/hazmat/primitives/asymmetric/serialization.rst
index 8d51f0d..f14f403 100644
--- a/docs/hazmat/primitives/asymmetric/serialization.rst
+++ b/docs/hazmat/primitives/asymmetric/serialization.rst
@@ -337,8 +337,6 @@
 
 .. class:: Encoding
 
-    .. versionadded:: 0.8
-
     An enumeration for encoding types. Used with the ``private_bytes`` method
     available on
     :class:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKeyWithSerialization`
@@ -353,10 +351,14 @@
 
     .. attribute:: PEM
 
+        .. versionadded:: 0.8
+
         For PEM format. This is a base64 format with delimiters.
 
     .. attribute:: DER
 
+        .. versionadded:: 0.9
+
         For DER format. This is a binary format.
 
 
diff --git a/docs/hazmat/primitives/index.rst b/docs/hazmat/primitives/index.rst
index 675111b..a9ab38a 100644
--- a/docs/hazmat/primitives/index.rst
+++ b/docs/hazmat/primitives/index.rst
@@ -15,4 +15,3 @@
     constant-time
     interfaces
     twofactor
-    key-exchange-agreements
diff --git a/docs/hazmat/primitives/key-exchange-agreements.rst b/docs/hazmat/primitives/key-exchange-agreements.rst
deleted file mode 100644
index 8d79fba..0000000
--- a/docs/hazmat/primitives/key-exchange-agreements.rst
+++ /dev/null
@@ -1,23 +0,0 @@
-.. hazmat::
-
-Key Exchange agreements
-=======================
-
-.. module:: cryptography.hazmat.primitives.asymmetric.key_exchange
-
-Key exchange agreements are cryptographic operations, like Diffie-Hellman
-key exchanges, that allow two parties to use their public-private key pairs
-to establish a shared secret key over an insecure channel. Usually the
-negotiated key is further derived before using it for symmetric operations.
-
-Interfaces
-~~~~~~~~~~
-
-.. class:: KeyExchangeContext
-
-    .. versionadded:: 1.1
-
-    .. method:: agree(public_key)
-
-        :param public_key: The peer public key, the type depends on the
-            crypto system used, for example :class:`~cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePublicKey`
diff --git a/docs/installation.rst b/docs/installation.rst
index 5d629e9..61f9348 100644
--- a/docs/installation.rst
+++ b/docs/installation.rst
@@ -46,7 +46,9 @@
 
 If you prefer to compile it yourself you'll need to have OpenSSL installed.
 You can compile OpenSSL yourself as well or use the binaries we build for our
-release infrastructure (`32-bit`_ and `64-bit`_). Wherever you place your copy
+release infrastructure (`openssl-release`_). Be sure to download the proper
+version for your architecture and Python (2010 works for Python 2.6, 2.7, 3.3,
+and 3.4 while 2015 is required for 3.5). Wherever you place your copy
 of OpenSSL you'll need to set the ``LIB`` and ``INCLUDE`` environment variables
 to include the proper locations. For example:
 
@@ -166,7 +168,7 @@
     ./config no-shared no-ssl2 -fPIC --prefix=${CWD}/openssl
     make && make install
     cd ..
-    CFLAGS="-I${CWD}/openssl/include" LDFLAGS="-L${CWD}/openssl/lib" pip wheel cryptography
+    CFLAGS="-I${CWD}/openssl/include" LDFLAGS="-L${CWD}/openssl/lib" pip wheel --no-use-wheel cryptography
 
 Building cryptography on OS X
 -----------------------------
@@ -250,8 +252,7 @@
 
 .. _`Homebrew`: http://brew.sh
 .. _`MacPorts`: https://www.macports.org
-.. _`32-bit`: https://jenkins.cryptography.io/job/openssl-win32-release/
-.. _`64-bit`: https://jenkins.cryptography.io/job/openssl-win64-release/
+.. _`openssl-release`: https://jenkins.cryptography.io/job/openssl-release/
 .. _`bug in conda`: https://github.com/conda/conda-recipes/issues/110
 .. _`Greg Wilson's blog post`: http://software-carpentry.org/blog/2014/04/mr-biczo-was-right.html
 .. _virtualenv: https://virtualenv.pypa.io/en/latest/
diff --git a/docs/x509/reference.rst b/docs/x509/reference.rst
index eec0cbc..5ab6caa 100644
--- a/docs/x509/reference.rst
+++ b/docs/x509/reference.rst
@@ -287,13 +287,13 @@
 
     .. method:: public_key()
 
-        :type:
+        The public key associated with the certificate.
+
+        :returns:
             :class:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPublicKey` or
             :class:`~cryptography.hazmat.primitives.asymmetric.dsa.DSAPublicKey` or
             :class:`~cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePublicKey`
 
-        The public key associated with the certificate.
-
         .. doctest::
 
             >>> from cryptography.hazmat.primitives.asymmetric import rsa
@@ -617,6 +617,8 @@
             :class:`~cryptography.hazmat.backends.interfaces.X509Backend`
             interface.
 
+        :returns: :class:`~cryptography.x509.Certificate`
+
 
 X.509 CSR (Certificate Signing Request) Object
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -627,13 +629,13 @@
 
     .. method:: public_key()
 
-        :type:
+        The public key associated with the request.
+
+        :returns:
             :class:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPublicKey` or
             :class:`~cryptography.hazmat.primitives.asymmetric.dsa.DSAPublicKey` or
             :class:`~cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePublicKey`
 
-        The public key associated with the request.
-
         .. doctest::
 
             >>> from cryptography.hazmat.primitives.asymmetric import rsa
diff --git a/setup.py b/setup.py
index 7afa84c..9c97e1d 100644
--- a/setup.py
+++ b/setup.py
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
 # This file is dual licensed under the terms of the Apache License, Version
 # 2.0, and the BSD License. See the LICENSE file in the root of this repository
 # for complete details.
diff --git a/src/_cffi_src/build_openssl.py b/src/_cffi_src/build_openssl.py
index defa69d..c856e3d 100644
--- a/src/_cffi_src/build_openssl.py
+++ b/src/_cffi_src/build_openssl.py
@@ -79,7 +79,6 @@
         "objects",
         "opensslv",
         "pem",
-        "pkcs7",
         "pkcs12",
         "rand",
         "rsa",
@@ -87,7 +86,8 @@
         "x509",
         "x509name",
         "x509v3",
-        "x509_vfy"
+        "x509_vfy",
+        "pkcs7",
     ],
     pre_include=_OSX_PRE_INCLUDE,
     post_include=_OSX_POST_INCLUDE,
diff --git a/src/_cffi_src/openssl/pkcs7.py b/src/_cffi_src/openssl/pkcs7.py
index 5d6ee45..0dd8958 100644
--- a/src/_cffi_src/openssl/pkcs7.py
+++ b/src/_cffi_src/openssl/pkcs7.py
@@ -10,7 +10,33 @@
 
 TYPES = """
 typedef struct {
+    Cryptography_STACK_OF_X509 *cert;
+    Cryptography_STACK_OF_X509_CRL *crl;
+    ...;
+} PKCS7_SIGNED;
+
+typedef struct {
+    Cryptography_STACK_OF_X509 *cert;
+    Cryptography_STACK_OF_X509_CRL *crl;
+    ...;
+} PKCS7_SIGN_ENVELOPE;
+
+typedef ... PKCS7_DIGEST;
+typedef ... PKCS7_ENCRYPT;
+typedef ... PKCS7_ENVELOPE;
+
+typedef struct {
     ASN1_OBJECT *type;
+    union {
+        char *ptr;
+        ASN1_OCTET_STRING *data;
+        PKCS7_SIGNED *sign;
+        PKCS7_ENVELOPE *enveloped;
+        PKCS7_SIGN_ENVELOPE *signed_and_enveloped;
+        PKCS7_DIGEST *digest;
+        PKCS7_ENCRYPT *encrypted;
+        ASN1_TYPE *other;
+     } d;
     ...;
 } PKCS7;
 
@@ -44,13 +70,17 @@
 PKCS7 *PKCS7_encrypt(Cryptography_STACK_OF_X509 *, BIO *,
                      const EVP_CIPHER *, int);
 int PKCS7_decrypt(PKCS7 *, EVP_PKEY *, X509 *, BIO *, int);
+
+BIO *PKCS7_dataInit(PKCS7 *, BIO *);
 """
 
 MACROS = """
+int PKCS7_type_is_encrypted(PKCS7 *);
 int PKCS7_type_is_signed(PKCS7 *);
 int PKCS7_type_is_enveloped(PKCS7 *);
 int PKCS7_type_is_signedAndEnveloped(PKCS7 *);
 int PKCS7_type_is_data(PKCS7 *);
+int PKCS7_type_is_digest(PKCS7 *);
 """
 
 CUSTOMIZATIONS = ""
diff --git a/src/_cffi_src/openssl/x509v3.py b/src/_cffi_src/openssl/x509v3.py
index 51cac62..22406c4 100644
--- a/src/_cffi_src/openssl/x509v3.py
+++ b/src/_cffi_src/openssl/x509v3.py
@@ -202,6 +202,8 @@
 void *X509V3_set_ctx_nodb(X509V3_CTX *);
 
 int i2d_GENERAL_NAMES(GENERAL_NAMES *, unsigned char **);
+GENERAL_NAMES *d2i_GENERAL_NAMES(GENERAL_NAMES **, const unsigned char **,
+                                 long);
 
 int i2d_EXTENDED_KEY_USAGE(EXTENDED_KEY_USAGE *, unsigned char **);
 
diff --git a/src/cryptography/exceptions.py b/src/cryptography/exceptions.py
index 29be22b..3bf8a75 100644
--- a/src/cryptography/exceptions.py
+++ b/src/cryptography/exceptions.py
@@ -20,6 +20,7 @@
     UNSUPPORTED_ELLIPTIC_CURVE = 6
     UNSUPPORTED_SERIALIZATION = 7
     UNSUPPORTED_X509 = 8
+    UNSUPPORTED_EXCHANGE_ALGORITHM = 9
 
 
 class UnsupportedAlgorithm(Exception):
diff --git a/src/cryptography/hazmat/backends/interfaces.py b/src/cryptography/hazmat/backends/interfaces.py
index a43621a..92d9653 100644
--- a/src/cryptography/hazmat/backends/interfaces.py
+++ b/src/cryptography/hazmat/backends/interfaces.py
@@ -212,7 +212,13 @@
     @abc.abstractmethod
     def load_elliptic_curve_private_numbers(self, numbers):
         """
-        Return an EllipticCurvePublicKey provider using the given numbers.
+        Return an EllipticCurvePrivateKey provider using the given numbers.
+        """
+
+    @abc.abstractmethod
+    def elliptic_curve_exchange_algorithm_supported(self, algorithm, curve):
+        """
+        Returns whether the exchange algorithm is supported by this backend.
         """
 
 
diff --git a/src/cryptography/hazmat/backends/multibackend.py b/src/cryptography/hazmat/backends/multibackend.py
index cda3314..bbaaf42 100644
--- a/src/cryptography/hazmat/backends/multibackend.py
+++ b/src/cryptography/hazmat/backends/multibackend.py
@@ -271,6 +271,12 @@
             _Reasons.UNSUPPORTED_ELLIPTIC_CURVE
         )
 
+    def elliptic_curve_exchange_algorithm_supported(self, algorithm, curve):
+        return any(
+            b.elliptic_curve_exchange_algorithm_supported(algorithm, curve)
+            for b in self._filtered_backends(EllipticCurveBackend)
+        )
+
     def load_pem_private_key(self, data, password):
         for b in self._filtered_backends(PEMSerializationBackend):
             return b.load_pem_private_key(data, password)
diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py
index 06db6f2..58587b9 100644
--- a/src/cryptography/hazmat/backends/openssl/backend.py
+++ b/src/cryptography/hazmat/backends/openssl/backend.py
@@ -1693,6 +1693,13 @@
 
         return _EllipticCurvePublicKey(self, ec_cdata, evp_pkey)
 
+    def elliptic_curve_exchange_algorithm_supported(self, algorithm, curve):
+        return (
+            self.elliptic_curve_supported(curve) and
+            self._lib.Cryptography_HAS_ECDH == 1 and
+            isinstance(algorithm, ec.ECDH)
+        )
+
     def _ec_cdata_to_evp_pkey(self, ec_cdata):
         evp_pkey = self._lib.EVP_PKEY_new()
         self.openssl_assert(evp_pkey != self._ffi.NULL)
@@ -1798,9 +1805,13 @@
             self.openssl_assert(res == 1)
 
             res = self._lib.BN_cmp(bn_x, check_x)
-            self.openssl_assert(res == 0)
+            if res != 0:
+                self._consume_errors()
+                raise ValueError("Invalid EC Key X point.")
             res = self._lib.BN_cmp(bn_y, check_y)
-            self.openssl_assert(res == 0)
+            if res != 0:
+                self._consume_errors()
+                raise ValueError("Invalid EC Key Y point.")
 
         res = self._lib.EC_KEY_set_public_key(ctx, point)
         self.openssl_assert(res == 1)
diff --git a/src/cryptography/hazmat/backends/openssl/ec.py b/src/cryptography/hazmat/backends/openssl/ec.py
index 939a3f9..cfd559a 100644
--- a/src/cryptography/hazmat/backends/openssl/ec.py
+++ b/src/cryptography/hazmat/backends/openssl/ec.py
@@ -171,6 +171,31 @@
                 "Unsupported elliptic curve signature algorithm.",
                 _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM)
 
+    def exchange(self, algorithm, peer_public_key):
+        if not (
+            self._backend.elliptic_curve_exchange_algorithm_supported(
+                algorithm, self.curve
+            )
+        ):
+            raise UnsupportedAlgorithm(
+                "This backend does not support the ECDH algorithm.",
+                _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM
+            )
+
+        group = self._backend._lib.EC_KEY_get0_group(self._ec_key)
+        z_len = (self._backend._lib.EC_GROUP_get_degree(group) + 7) // 8
+        self._backend.openssl_assert(z_len > 0)
+        z_buf = self._backend._ffi.new("uint8_t[]", z_len)
+        peer_key = self._backend._lib.EC_KEY_get0_public_key(
+            peer_public_key._ec_key
+        )
+
+        r = self._backend._lib.ECDH_compute_key(
+            z_buf, z_len, peer_key, self._ec_key, self._backend._ffi.NULL
+        )
+        self._backend.openssl_assert(r > 0)
+        return self._backend._ffi.buffer(z_buf)[:z_len]
+
     def public_key(self):
         group = self._backend._lib.EC_KEY_get0_group(self._ec_key)
         self._backend.openssl_assert(group != self._backend._ffi.NULL)
diff --git a/src/cryptography/hazmat/primitives/asymmetric/ec.py b/src/cryptography/hazmat/primitives/asymmetric/ec.py
index f1d39ee..c6f8366 100644
--- a/src/cryptography/hazmat/primitives/asymmetric/ec.py
+++ b/src/cryptography/hazmat/primitives/asymmetric/ec.py
@@ -44,6 +44,13 @@
         """
 
     @abc.abstractmethod
+    def exchange(self, algorithm, peer_public_key):
+        """
+        Performs a key exchange operation using the provided algorithm with the
+        provided peer's public key.
+        """
+
+    @abc.abstractmethod
     def public_key(self):
         """
         The EllipticCurvePublicKey for this private key.
@@ -302,3 +309,7 @@
 
     def __ne__(self, other):
         return not self == other
+
+
+class ECDH(object):
+    pass
diff --git a/src/cryptography/hazmat/primitives/asymmetric/key_exchange.py b/src/cryptography/hazmat/primitives/asymmetric/key_exchange.py
deleted file mode 100644
index a9846e2..0000000
--- a/src/cryptography/hazmat/primitives/asymmetric/key_exchange.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# This file is dual licensed under the terms of the Apache License, Version
-# 2.0, and the BSD License. See the LICENSE file in the root of this repository
-# for complete details.
-
-from __future__ import absolute_import, division, print_function
-
-import abc
-
-import six
-
-
-@six.add_metaclass(abc.ABCMeta)
-class KeyExchangeContext(object):
-    @abc.abstractmethod
-    def agree(self, public_key):
-        """
-        Returns the agreed key material.
-        """
diff --git a/src/cryptography/x509/extensions.py b/src/cryptography/x509/extensions.py
index cd75ecd..46ba5a2 100644
--- a/src/cryptography/x509/extensions.py
+++ b/src/cryptography/x509/extensions.py
@@ -104,6 +104,11 @@
     def __len__(self):
         return len(self._extensions)
 
+    def __repr__(self):
+        return (
+            "<Extensions({0})>".format(self._extensions)
+        )
+
 
 @utils.register_interface(ExtensionType)
 class AuthorityKeyIdentifier(object):
diff --git a/tests/hazmat/backends/test_multibackend.py b/tests/hazmat/backends/test_multibackend.py
index 618d21b..81a64ce 100644
--- a/tests/hazmat/backends/test_multibackend.py
+++ b/tests/hazmat/backends/test_multibackend.py
@@ -152,10 +152,7 @@
     ):
         return (
             isinstance(signature_algorithm, ec.ECDSA) and
-            any(
-                isinstance(curve, curve_type)
-                for curve_type in self._curves
-            )
+            self.elliptic_curve_supported(curve)
         )
 
     def generate_elliptic_curve_private_key(self, curve):
@@ -170,6 +167,12 @@
         if not self.elliptic_curve_supported(numbers.curve):
             raise UnsupportedAlgorithm(_Reasons.UNSUPPORTED_ELLIPTIC_CURVE)
 
+    def elliptic_curve_exchange_algorithm_supported(self, algorithm, curve):
+        return (
+            isinstance(algorithm, ec.ECDH) and
+            self.elliptic_curve_supported(curve)
+        )
+
 
 @utils.register_interface(PEMSerializationBackend)
 class DummyPEMSerializationBackend(object):
@@ -468,6 +471,14 @@
                 )
             )
 
+        assert backend.elliptic_curve_exchange_algorithm_supported(
+            ec.ECDH(), ec.SECT283K1()
+        )
+        backend2 = MultiBackend([DummyEllipticCurveBackend([])])
+        assert not backend2.elliptic_curve_exchange_algorithm_supported(
+            ec.ECDH(), ec.SECT163K1()
+        )
+
     def test_pem_serialization_backend(self):
         backend = MultiBackend([DummyPEMSerializationBackend()])
 
diff --git a/tests/hazmat/backends/test_openssl.py b/tests/hazmat/backends/test_openssl.py
index 8fd0d71..8533159 100644
--- a/tests/hazmat/backends/test_openssl.py
+++ b/tests/hazmat/backends/test_openssl.py
@@ -551,6 +551,12 @@
         with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_ELLIPTIC_CURVE):
             _sn_to_elliptic_curve(backend, b"fake")
 
+    def test_elliptic_curve_exchange_algorithm_supported(self, monkeypatch):
+        monkeypatch.setattr(backend, "_lib", DummyLibrary())
+        assert not backend.elliptic_curve_exchange_algorithm_supported(
+            ec.ECDH(), ec.SECP256R1()
+        )
+
 
 @pytest.mark.requires_backend_interface(interface=RSABackend)
 class TestRSAPEMSerialization(object):
diff --git a/tests/hazmat/primitives/test_ec.py b/tests/hazmat/primitives/test_ec.py
index 59bdc52..4c4d5b9 100644
--- a/tests/hazmat/primitives/test_ec.py
+++ b/tests/hazmat/primitives/test_ec.py
@@ -7,6 +7,8 @@
 import itertools
 import os
 
+from binascii import hexlify
+
 import pytest
 
 from cryptography import exceptions, utils
@@ -21,7 +23,8 @@
 
 from ...utils import (
     load_fips_ecdsa_key_pair_vectors, load_fips_ecdsa_signing_vectors,
-    load_vectors_from_file, raises_unsupported_algorithm
+    load_kasvs_ecdh_vectors, load_vectors_from_file,
+    raises_unsupported_algorithm
 )
 
 _HASH_TYPES = {
@@ -54,6 +57,17 @@
         )
 
 
+def _skip_exchange_algorithm_unsupported(backend, algorithm, curve):
+    if not backend.elliptic_curve_exchange_algorithm_supported(
+        algorithm, curve
+    ):
+        pytest.skip(
+            "Exchange algorithm is not supported by this backend {0}".format(
+                backend
+            )
+        )
+
+
 @utils.register_interface(ec.EllipticCurve)
 class DummyCurve(object):
     name = "dummy-curve"
@@ -76,6 +90,12 @@
         _skip_curve_unsupported(backend, DummyCurve())
 
 
+@pytest.mark.requires_backend_interface(interface=EllipticCurveBackend)
+def test_skip_exchange_algorithm_unsupported(backend):
+    with pytest.raises(pytest.skip.Exception):
+        _skip_exchange_algorithm_unsupported(backend, ec.ECDH(), DummyCurve())
+
+
 def test_ec_numbers():
     numbers = ec.EllipticCurvePrivateNumbers(
         1,
@@ -285,6 +305,35 @@
         with pytest.raises(ValueError):
             numbers.private_key(backend)
 
+    def test_load_invalid_public_ec_key_from_numbers(self, backend):
+        _skip_curve_unsupported(backend, ec.SECP521R1())
+
+        # Bad X coordinate
+        numbers = ec.EllipticCurvePublicNumbers(
+            int("000003647356b91f8ace114c7247ecf4f4a622553fc025e04a178f179ef27"
+                "9090c184af678a4c78f635483bdd8aa544851c6ef291c1f0d6a241ebfd145"
+                "77d1d30d9903ce", 16),
+            int("000001499bc7e079322ea0fcfbd6b40103fa6a1536c2257b182db0df4b369"
+                "6ec643adf100eb4f2025d1b873f82e5a475d6e4400ba777090eeb4563a115"
+                "09e4c87319dc26", 16),
+            ec.SECP521R1()
+        )
+        with pytest.raises(ValueError):
+            numbers.public_key(backend)
+
+        # Bad Y coordinate
+        numbers = ec.EllipticCurvePublicNumbers(
+            int("0000019aadc221cc0525118ab6d5aa1f64720603de0be128cbfea0b381ad8"
+                "02a2facc6370bb58cf88b3f0c692bc654ee19d6cad198f10d4b681b396f20"
+                "d2e40603fa945b", 16),
+            int("0000025da392803a320717a08d4cb3dea932039badff363b71bdb8064e726"
+                "6c7f4f4b748d4d425347fc33e3885d34b750fa7fcd5691f4d90c89522ce33"
+                "feff5db10088a5", 16),
+            ec.SECP521R1()
+        )
+        with pytest.raises(ValueError):
+            numbers.public_key(backend)
+
     @pytest.mark.parametrize(
         "vector",
         itertools.chain(
@@ -720,3 +769,78 @@
         public_key = key.public_key()
         with pytest.raises(TypeError):
             public_key.verifier(1234, ec.ECDSA(hashes.SHA256()))
+
+
+@pytest.mark.requires_backend_interface(interface=EllipticCurveBackend)
+class TestECDHVectors(object):
+    @pytest.mark.parametrize(
+        "vector",
+        load_vectors_from_file(
+            os.path.join(
+                "asymmetric", "ECDH",
+                "KASValidityTest_ECCStaticUnified_NOKC_ZZOnly_init.fax"),
+            load_kasvs_ecdh_vectors
+        )
+    )
+    def test_key_exchange_with_vectors(self, backend, vector):
+        _skip_exchange_algorithm_unsupported(
+            backend, ec.ECDH(), ec._CURVE_TYPES[vector['curve']]
+        )
+
+        key_numbers = vector['IUT']
+        private_numbers = ec.EllipticCurvePrivateNumbers(
+            key_numbers['d'],
+            ec.EllipticCurvePublicNumbers(
+                key_numbers['x'],
+                key_numbers['y'],
+                ec._CURVE_TYPES[vector['curve']]()
+            )
+        )
+        # Errno 5 and 6 indicates a bad public key, this doesn't test the ECDH
+        # code at all
+        if vector['fail'] and vector['errno'] in [5, 6]:
+            with pytest.raises(ValueError):
+                private_numbers.private_key(backend)
+            return
+        else:
+            private_key = private_numbers.private_key(backend)
+
+        peer_numbers = vector['CAVS']
+        public_numbers = ec.EllipticCurvePublicNumbers(
+            peer_numbers['x'],
+            peer_numbers['y'],
+            ec._CURVE_TYPES[vector['curve']]()
+        )
+        # Errno 1 and 2 indicates a bad public key, this doesn't test the ECDH
+        # code at all
+        if vector['fail'] and vector['errno'] in [1, 2]:
+            with pytest.raises(ValueError):
+                public_numbers.public_key(backend)
+            return
+        else:
+            peer_pubkey = public_numbers.public_key(backend)
+
+        z = private_key.exchange(ec.ECDH(), peer_pubkey)
+        z = int(hexlify(z).decode('ascii'), 16)
+        # At this point fail indicates that one of the underlying keys was
+        # changed. This results in a non-matching derived key.
+        if vector['fail']:
+            assert z != vector['Z']
+        else:
+            assert z == vector['Z']
+
+    def test_exchange_unsupported_algorithm(self, backend):
+        _skip_curve_unsupported(backend, ec.SECP256R1())
+
+        key = load_vectors_from_file(
+            os.path.join(
+                "asymmetric", "PKCS8", "ec_private_key.pem"),
+            lambda pemfile: serialization.load_pem_private_key(
+                pemfile.read().encode(), None, backend
+            )
+        )
+
+        with raises_unsupported_algorithm(
+            exceptions._Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM
+        ):
+            key.exchange(None, key.public_key())
diff --git a/tests/test_utils.py b/tests/test_utils.py
index 04182a0..72e2072 100644
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -3363,7 +3363,7 @@
     [EB - SHA224]
 
 
-    COUNT = 0
+    COUNT = 50
     dsCAVS = 540904b67b3716823dd621ed72ad3dbc615887b4f56f910b78a57199
     QsCAVSx = 28e5f3a72d8f6b8499dd1bcdfceafcecec68a0d715789bcf4b55fe15
     QsCAVSy = 8c8006a7da7c1a19f5328d7e865522b0c0dfb9a29b2c46dc96590d2a
@@ -3385,7 +3385,7 @@
     expected = [
         {'errno': 12,
          'fail': True,
-         'COUNT': 0,
+         'COUNT': 50,
          'CAVS': {
              'd': int("540904b67b3716823dd621ed72ad3dbc615887b4f56f910b"
                       "78a57199", 16),
diff --git a/tests/test_x509_ext.py b/tests/test_x509_ext.py
index 1bc1462..8f46936 100644
--- a/tests/test_x509_ext.py
+++ b/tests/test_x509_ext.py
@@ -857,6 +857,20 @@
         assert ext is not None
         assert isinstance(ext.value, x509.BasicConstraints)
 
+    def test_repr(self, backend):
+        cert = _load_cert(
+            os.path.join(
+                "x509", "custom", "basic_constraints_not_critical.pem"
+            ),
+            x509.load_pem_x509_certificate,
+            backend
+        )
+        assert repr(cert.extensions) == (
+            "<Extensions([<Extension(oid=<ObjectIdentifier(oid=2.5.29.19, name"
+            "=basicConstraints)>, critical=False, value=<BasicConstraints(ca=F"
+            "alse, path_length=None)>)>])>"
+        )
+
 
 @pytest.mark.requires_backend_interface(interface=RSABackend)
 @pytest.mark.requires_backend_interface(interface=X509Backend)
diff --git a/tests/utils.py b/tests/utils.py
index cc3f9fc..3970109 100644
--- a/tests/utils.py
+++ b/tests/utils.py
@@ -722,7 +722,7 @@
         if line.startswith("["):
             tag = line.split()[0][1:]
         elif line.startswith("COUNT = "):
-            data["COUNT"] = int(line.split("=")[1], 16)
+            data["COUNT"] = int(line.split("=")[1])
         elif line.startswith("dsCAVS = "):
             data["CAVS"]["d"] = int(line.split("=")[1], 16)
         elif line.startswith("QsCAVSx = "):
diff --git a/vectors/setup.py b/vectors/setup.py
index 53ec82e..bf02e38 100644
--- a/vectors/setup.py
+++ b/vectors/setup.py
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
 # This file is dual licensed under the terms of the Apache License, Version
 # 2.0, and the BSD License. See the LICENSE file in the root of this repository
 # for complete details.