merge master
diff --git a/.gitignore b/.gitignore
index 867434b..7b60bb0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,7 @@
 build
 dist
-*.egg-info
\ No newline at end of file
+*.egg-info
+*.pyc
+*.pyo
+__pycache__
+.tox
diff --git a/.travis.yml b/.travis.yml
index 52f1032..f359de1 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -2,7 +2,6 @@
 
 os:
   - linux
-  - osx
 
 python:
   - "pypy"
@@ -10,21 +9,42 @@
   - "2.7"
   - "3.2"
   - "3.3"
+  - "3.4"
 
 matrix:
   include:
+  # Also run the tests against cryptography master.
   - python: "2.6"
-    env: CRYPTOGRAPHY_GIT_MASTER=true
+    env:
+      CRYPTOGRAPHY_GIT_MASTER=true
   - python: "2.7"
-    env: CRYPTOGRAPHY_GIT_MASTER=true
+    env:
+      CRYPTOGRAPHY_GIT_MASTER=true
   - python: "3.2"
-    env: CRYPTOGRAPHY_GIT_MASTER=true
+    env:
+      CRYPTOGRAPHY_GIT_MASTER=true
   - python: "3.3"
-    env: CRYPTOGRAPHY_GIT_MASTER=true
+    env:
+      CRYPTOGRAPHY_GIT_MASTER=true
+  - python: "3.4"
+    env:
+      CRYPTOGRAPHY_GIT_MASTER=true
   - python: "pypy"
-    env: CRYPTOGRAPHY_GIT_MASTER=true
+    env:
+      CRYPTOGRAPHY_GIT_MASTER=true
+
+  # Also run at least a little bit against an older version of OpenSSL.
+  - python: "2.7"
+    env:
+      OPENSSL=0.9.8
+
+  # Let the cryptography master builds fail because they might be triggered by
+  # cryptography changes beyond our control.
   allow_failures:
-  - env: CRYPTOGRAPHY_GIT_MASTER=true
+  - env:
+      CRYPTOGRAPHY_GIT_MASTER=true
+  - env:
+      OPENSSL=0.9.8
 
 before_install:
   - if [ -n "$CRYPTOGRAPHY_GIT_MASTER" ]; then pip install git+https://github.com/pyca/cryptography.git;fi
@@ -36,5 +56,28 @@
   # travis.
   - pip install wheel
 
+  # Also install some tools for measuring code coverage and sending the results
+  # to coveralls.
+  - pip install coveralls coverage
+
 script:
-  - python setup.py bdist_wheel test
+  - |
+    if [[ "${OPENSSL}" == "0.9.8" ]]; then
+      sudo add-apt-repository "deb http://archive.ubuntu.com/ubuntu/ lucid main"
+      sudo apt-get -y update
+      sudo apt-get install -y --force-yes libssl-dev/lucid
+    fi
+  - |
+    pip install -e .
+  - |
+    coverage run --branch --source=OpenSSL setup.py bdist_wheel test
+  - |
+    coverage report -m
+  - |
+    python -c "import OpenSSL.SSL; print(OpenSSL.SSL.SSLeay_version(OpenSSL.SSL.SSLEAY_VERSION))"
+
+after_success:
+    - coveralls
+
+notifications:
+  email: false
diff --git a/ChangeLog b/ChangeLog
index 86e644f..c22aaae 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,4 +1,4 @@
-2014-04-02  Jean-Paul Calderone  <exarkun@twistedmatrix.com>
+2014-05-05  Jean-Paul Calderone  <exarkun@twistedmatrix.com>
 
 	* OpenSSL/SSL.py: Fix a regression in which the first argument of
 	  the "verify" callback was incorrectly a ``Context`` instance
@@ -6,6 +6,25 @@
 	* OpenSSL/test/test_ssl.py: Add a test for the value passed as the
 	  first argument of the "verify" callback.
 
+2014-04-19  Jean-Paul Calderone  <exarkun@twistedmatrix.com>
+
+	* OpenSSL/crypto.py: Based on work from Alex Gaynor, Andrew
+	  Lutomirski, Tobias Oberstein, Laurens Van Houtven, and Hynek
+	  Schlawack, add ``get_elliptic_curve`` and ``get_elliptic_curves``
+	  to support TLS ECDHE modes.
+	* OpenSSL/SSL.py: Add ``Context.set_tmp_ecdh`` to configure a TLS
+	  context with a particular elliptic curve for ECDHE modes.
+
+2014-04-19  Markus Unterwaditzer <markus@unterwaditzer.net>
+
+	* OpenSSL/SSL.py: ``Connection.send`` and ``Connection.sendall``
+	  now also accept the ``buffer`` type as data.
+
+2014-04-05  Stephen Holsapple <sholsapp@gmail.com>
+
+	* OpenSSL/crypto.py: Make ``load_pkcs12`` backwards compatible with
+	  pyOpenSSL 0.13 by making passphrase optional.
+
 2014-03-30  Fedor Brunner  <fedor.brunner@azet.sk>
 
 	* OpenSSL/SSL.py: Add ``get_finished``, ``get_peer_finished``
@@ -25,6 +44,13 @@
 	* OpenSSL/tsafe.py: Replace the use of ``apply`` (which has been
 	  removed in Python 3) with the equivalent syntax.
 
+2014-03-28  Jonathan Giannuzzi  <jonathan@giannuzzi.be>
+
+	* OpenSSL/crypto.py: Fix memory leak in _X509_REVOKED_dup.
+	* leakcheck/crypto.py: Add checks for _X509_REVOKED_dup, CRL.add_revoked
+	  and CRL.get_revoked.
+	* setup.py: Require cryptography 0.3 to have the ASN1_TIME_free binding.
+
 2014-03-02  Stephen Holsapple  <sholsapp@gmail.com>
 
 	* OpenSSL/crypto.py: Add ``get_extensions`` method to ``X509Req``.
diff --git a/OpenSSL/SSL.py b/OpenSSL/SSL.py
index 40fe5a6..7b1cbc1 100644
--- a/OpenSSL/SSL.py
+++ b/OpenSSL/SSL.py
@@ -24,6 +24,12 @@
     class _memoryview(object):
         pass
 
+try:
+    _buffer = buffer
+except NameError:
+    class _buffer(object):
+        pass
+
 OPENSSL_VERSION_NUMBER = _lib.OPENSSL_VERSION_NUMBER
 SSLEAY_VERSION = _lib.SSLEAY_VERSION
 SSLEAY_CFLAGS = _lib.SSLEAY_CFLAGS
@@ -124,7 +130,6 @@
 SSL_CB_HANDSHAKE_START = _lib.SSL_CB_HANDSHAKE_START
 SSL_CB_HANDSHAKE_DONE = _lib.SSL_CB_HANDSHAKE_DONE
 
-
 class Error(Exception):
     """
     An error occurred in an `OpenSSL.SSL` API.
@@ -602,6 +607,19 @@
         _lib.SSL_CTX_set_tmp_dh(self._context, dh)
 
 
+    def set_tmp_ecdh(self, curve):
+        """
+        Select a curve to use for ECDHE key exchange.
+
+        :param curve: A curve object to use as returned by either
+            :py:meth:`OpenSSL.crypto.get_elliptic_curve` or
+            :py:meth:`OpenSSL.crypto.get_elliptic_curves`.
+
+        :return: None
+        """
+        _lib.SSL_CTX_set_tmp_ecdh(self._context, curve._to_EC_KEY())
+
+
     def set_cipher_list(self, cipher_list):
         """
         Change the cipher list
@@ -944,15 +962,17 @@
         WantWrite or WantX509Lookup exceptions on this, you have to call the
         method again with the SAME buffer.
 
-        :param buf: The string to send
+        :param buf: The string, buffer or memoryview to send
         :param flags: (optional) Included for compatibility with the socket
                       API, the value is ignored
         :return: The number of bytes written
         """
         if isinstance(buf, _memoryview):
             buf = buf.tobytes()
+        if isinstance(buf, _buffer):
+            buf = str(buf)
         if not isinstance(buf, bytes):
-            raise TypeError("data must be a byte string")
+            raise TypeError("data must be a memoryview, buffer or byte string")
 
         result = _lib.SSL_write(self._ssl, buf, len(buf))
         self._raise_ssl_error(self._ssl, result)
@@ -966,15 +986,17 @@
         all data is sent. If an error occurs, it's impossible to tell how much
         data has been sent.
 
-        :param buf: The string to send
+        :param buf: The string, buffer or memoryview to send
         :param flags: (optional) Included for compatibility with the socket
                       API, the value is ignored
         :return: The number of bytes written
         """
         if isinstance(buf, _memoryview):
             buf = buf.tobytes()
+        if isinstance(buf, _buffer):
+            buf = str(buf)
         if not isinstance(buf, bytes):
-            raise TypeError("buf must be a byte string")
+            raise TypeError("buf must be a memoryview, buffer or byte string")
 
         left_to_send = len(buf)
         total_sent = 0
@@ -1218,7 +1240,7 @@
         The makefile() method is not implemented, since there is no dup semantics
         for SSL connections
 
-        :raise NotImplementedError
+        :raise: NotImplementedError
         """
         raise NotImplementedError("Cannot make file object of OpenSSL.SSL.Connection")
 
diff --git a/OpenSSL/crypto.py b/OpenSSL/crypto.py
index ed0b629..54569ea 100644
--- a/OpenSSL/crypto.py
+++ b/OpenSSL/crypto.py
@@ -5,7 +5,8 @@
 
 from six import (
     integer_types as _integer_types,
-    text_type as _text_type)
+    text_type as _text_type,
+    PY3 as _PY3)
 
 from OpenSSL._util import (
     ffi as _ffi,
@@ -263,6 +264,156 @@
 
 
 
+class _EllipticCurve(object):
+    """
+    A representation of a supported elliptic curve.
+
+    @cvar _curves: :py:obj:`None` until an attempt is made to load the curves.
+        Thereafter, a :py:type:`set` containing :py:type:`_EllipticCurve`
+        instances each of which represents one curve supported by the system.
+    @type _curves: :py:type:`NoneType` or :py:type:`set`
+    """
+    _curves = None
+
+    if _PY3:
+        # This only necessary on Python 3.  Morever, it is broken on Python 2.
+        def __ne__(self, other):
+            """
+            Implement cooperation with the right-hand side argument of ``!=``.
+
+            Python 3 seems to have dropped this cooperation in this very narrow
+            circumstance.
+            """
+            if isinstance(other, _EllipticCurve):
+                return super(_EllipticCurve, self).__ne__(other)
+            return NotImplemented
+
+
+    @classmethod
+    def _load_elliptic_curves(cls, lib):
+        """
+        Get the curves supported by OpenSSL.
+
+        :param lib: The OpenSSL library binding object.
+
+        :return: A :py:type:`set` of ``cls`` instances giving the names of the
+            elliptic curves the underlying library supports.
+        """
+        if lib.Cryptography_HAS_EC:
+            num_curves = lib.EC_get_builtin_curves(_ffi.NULL, 0)
+            builtin_curves = _ffi.new('EC_builtin_curve[]', num_curves)
+            # The return value on this call should be num_curves again.  We could
+            # check it to make sure but if it *isn't* then.. what could we do?
+            # Abort the whole process, I suppose...?  -exarkun
+            lib.EC_get_builtin_curves(builtin_curves, num_curves)
+            return set(
+                cls.from_nid(lib, c.nid)
+                for c in builtin_curves)
+        return set()
+
+
+    @classmethod
+    def _get_elliptic_curves(cls, lib):
+        """
+        Get, cache, and return the curves supported by OpenSSL.
+
+        :param lib: The OpenSSL library binding object.
+
+        :return: A :py:type:`set` of ``cls`` instances giving the names of the
+            elliptic curves the underlying library supports.
+        """
+        if cls._curves is None:
+            cls._curves = cls._load_elliptic_curves(lib)
+        return cls._curves
+
+
+    @classmethod
+    def from_nid(cls, lib, nid):
+        """
+        Instantiate a new :py:class:`_EllipticCurve` associated with the given
+        OpenSSL NID.
+
+        :param lib: The OpenSSL library binding object.
+
+        :param nid: The OpenSSL NID the resulting curve object will represent.
+            This must be a curve NID (and not, for example, a hash NID) or
+            subsequent operations will fail in unpredictable ways.
+        :type nid: :py:class:`int`
+
+        :return: The curve object.
+        """
+        return cls(lib, nid, _ffi.string(lib.OBJ_nid2sn(nid)).decode("ascii"))
+
+
+    def __init__(self, lib, nid, name):
+        """
+        :param _lib: The :py:mod:`cryptography` binding instance used to
+            interface with OpenSSL.
+
+        :param _nid: The OpenSSL NID identifying the curve this object
+            represents.
+        :type _nid: :py:class:`int`
+
+        :param name: The OpenSSL short name identifying the curve this object
+            represents.
+        :type name: :py:class:`unicode`
+        """
+        self._lib = lib
+        self._nid = nid
+        self.name = name
+
+
+    def __repr__(self):
+        return "<Curve %r>" % (self.name,)
+
+
+    def _to_EC_KEY(self):
+        """
+        Create a new OpenSSL EC_KEY structure initialized to use this curve.
+
+        The structure is automatically garbage collected when the Python object
+        is garbage collected.
+        """
+        key = self._lib.EC_KEY_new_by_curve_name(self._nid)
+        return _ffi.gc(key, _lib.EC_KEY_free)
+
+
+
+def get_elliptic_curves():
+    """
+    Return a set of objects representing the elliptic curves supported in the
+    OpenSSL build in use.
+
+    The curve objects have a :py:class:`unicode` ``name`` attribute by which
+    they identify themselves.
+
+    The curve objects are useful as values for the argument accepted by
+    :py:meth:`Context.set_tmp_ecdh` to specify which elliptical curve should be
+    used for ECDHE key exchange.
+    """
+    return _EllipticCurve._get_elliptic_curves(_lib)
+
+
+
+def get_elliptic_curve(name):
+    """
+    Return a single curve object selected by name.
+
+    See :py:func:`get_elliptic_curves` for information about curve objects.
+
+    :param name: The OpenSSL short name identifying the curve object to
+        retrieve.
+    :type name: :py:class:`unicode`
+
+    If the named curve is not supported then :py:class:`ValueError` is raised.
+    """
+    for curve in get_elliptic_curves():
+        if curve.name == name:
+            return curve
+    raise ValueError("unknown curve name", name)
+
+
+
 class X509Name(object):
     def __init__(self, name):
         """
@@ -1323,9 +1474,11 @@
         _raise_current_error()
 
     if original.serialNumber != _ffi.NULL:
+        _lib.ASN1_INTEGER_free(copy.serialNumber)
         copy.serialNumber = _lib.ASN1_INTEGER_dup(original.serialNumber)
 
     if original.revocationDate != _ffi.NULL:
+        _lib.ASN1_TIME_free(copy.revocationDate)
         copy.revocationDate = _lib.M_ASN1_TIME_dup(original.revocationDate)
 
     if original.extensions != _ffi.NULL:
@@ -2213,7 +2366,7 @@
 
 
 
-def load_pkcs12(buffer, passphrase):
+def load_pkcs12(buffer, passphrase=None):
     """
     Load a PKCS12 object from a buffer
 
@@ -2226,6 +2379,13 @@
 
     bio = _new_mem_buf(buffer)
 
+    # Use null passphrase if passphrase is None or empty string. With PKCS#12
+    # password based encryption no password and a zero length password are two
+    # different things, but OpenSSL implementation will try both to figure out
+    # which one works.
+    if not passphrase:
+        passphrase = _ffi.NULL
+
     p12 = _lib.d2i_PKCS12_bio(bio, _ffi.NULL)
     if p12 == _ffi.NULL:
         _raise_current_error()
diff --git a/OpenSSL/test/test_crypto.py b/OpenSSL/test/test_crypto.py
index a3685a9..bbe5d05 100644
--- a/OpenSSL/test/test_crypto.py
+++ b/OpenSSL/test/test_crypto.py
@@ -11,7 +11,7 @@
 from subprocess import PIPE, Popen
 from datetime import datetime, timedelta
 
-from six import binary_type
+from six import u, b, binary_type
 
 from OpenSSL.crypto import TYPE_RSA, TYPE_DSA, Error, PKey, PKeyType
 from OpenSSL.crypto import X509, X509Type, X509Name, X509NameType
@@ -25,9 +25,10 @@
 from OpenSSL.crypto import PKCS12, PKCS12Type, load_pkcs12
 from OpenSSL.crypto import CRL, Revoked, load_crl
 from OpenSSL.crypto import NetscapeSPKI, NetscapeSPKIType
-from OpenSSL.crypto import sign, verify
-from OpenSSL.test.util import TestCase, b
-from OpenSSL._util import native
+from OpenSSL.crypto import (
+    sign, verify, get_elliptic_curve, get_elliptic_curves)
+from OpenSSL.test.util import EqualityTestsMixin, TestCase
+from OpenSSL._util import native, lib
 
 def normalize_certificate_pem(pem):
     return dump_certificate(FILETYPE_PEM, load_certificate(FILETYPE_PEM, pem))
@@ -1940,6 +1941,21 @@
             self.assertEqual(recovered_cert[-len(ca):], ca)
 
 
+    def verify_pkcs12_container(self, p12):
+        """
+        Verify that the PKCS#12 container contains the correct client
+        certificate and private key.
+
+        :param p12: The PKCS12 instance to verify.
+        :type p12: :py:class:`PKCS12`
+        """
+        cert_pem = dump_certificate(FILETYPE_PEM, p12.get_certificate())
+        key_pem = dump_privatekey(FILETYPE_PEM, p12.get_privatekey())
+        self.assertEqual(
+            (client_cert_pem, client_key_pem, None),
+            (cert_pem, key_pem, p12.get_ca_certificates()))
+
+
     def test_load_pkcs12(self):
         """
         A PKCS12 string generated using the openssl command line can be loaded
@@ -1949,14 +1965,70 @@
         pem = client_key_pem + client_cert_pem
         p12_str = _runopenssl(
             pem, b"pkcs12", b"-export", b"-clcerts", b"-passout", b"pass:" + passwd)
-        p12 = load_pkcs12(p12_str, passwd)
-        # verify
-        self.assertTrue(isinstance(p12, PKCS12))
-        cert_pem = dump_certificate(FILETYPE_PEM, p12.get_certificate())
-        self.assertEqual(cert_pem, client_cert_pem)
-        key_pem = dump_privatekey(FILETYPE_PEM, p12.get_privatekey())
-        self.assertEqual(key_pem, client_key_pem)
-        self.assertEqual(None, p12.get_ca_certificates())
+        p12 = load_pkcs12(p12_str, passphrase=passwd)
+        self.verify_pkcs12_container(p12)
+
+
+    def test_load_pkcs12_no_passphrase(self):
+        """
+        A PKCS12 string generated using openssl command line can be loaded with
+        :py:obj:`load_pkcs12` without a passphrase and its components extracted
+        and examined.
+        """
+        pem = client_key_pem + client_cert_pem
+        p12_str = _runopenssl(
+            pem, b"pkcs12", b"-export", b"-clcerts", b"-passout", b"pass:")
+        p12 = load_pkcs12(p12_str)
+        self.verify_pkcs12_container(p12)
+
+
+    def _dump_and_load(self, dump_passphrase, load_passphrase):
+        """
+        A helper method to dump and load a PKCS12 object.
+        """
+        p12 = self.gen_pkcs12(client_cert_pem, client_key_pem)
+        dumped_p12 = p12.export(passphrase=dump_passphrase, iter=2, maciter=3)
+        return load_pkcs12(dumped_p12, passphrase=load_passphrase)
+
+
+    def test_load_pkcs12_null_passphrase_load_empty(self):
+        """
+        A PKCS12 string can be dumped with a null passphrase, loaded with an
+        empty passphrase with :py:obj:`load_pkcs12`, and its components
+        extracted and examined.
+        """
+        self.verify_pkcs12_container(
+            self._dump_and_load(dump_passphrase=None, load_passphrase=b''))
+
+
+    def test_load_pkcs12_null_passphrase_load_null(self):
+        """
+        A PKCS12 string can be dumped with a null passphrase, loaded with a
+        null passphrase with :py:obj:`load_pkcs12`, and its components
+        extracted and examined.
+        """
+        self.verify_pkcs12_container(
+            self._dump_and_load(dump_passphrase=None, load_passphrase=None))
+
+
+    def test_load_pkcs12_empty_passphrase_load_empty(self):
+        """
+        A PKCS12 string can be dumped with an empty passphrase, loaded with an
+        empty passphrase with :py:obj:`load_pkcs12`, and its components
+        extracted and examined.
+        """
+        self.verify_pkcs12_container(
+            self._dump_and_load(dump_passphrase=b'', load_passphrase=b''))
+
+
+    def test_load_pkcs12_empty_passphrase_load_null(self):
+        """
+        A PKCS12 string can be dumped with an empty passphrase, loaded with a
+        null passphrase with :py:obj:`load_pkcs12`, and its components
+        extracted and examined.
+        """
+        self.verify_pkcs12_container(
+            self._dump_and_load(dump_passphrase=b'', load_passphrase=None))
 
 
     def test_load_pkcs12_garbage(self):
@@ -3058,5 +3130,154 @@
         verify(good_cert, sig, content, "sha1")
 
 
+
+class EllipticCurveTests(TestCase):
+    """
+    Tests for :py:class:`_EllipticCurve`, :py:obj:`get_elliptic_curve`, and
+    :py:obj:`get_elliptic_curves`.
+    """
+    def test_set(self):
+        """
+        :py:obj:`get_elliptic_curves` returns a :py:obj:`set`.
+        """
+        self.assertIsInstance(get_elliptic_curves(), set)
+
+
+    def test_some_curves(self):
+        """
+        If :py:mod:`cryptography` has elliptic curve support then the set
+        returned by :py:obj:`get_elliptic_curves` has some elliptic curves in
+        it.
+
+        There could be an OpenSSL that violates this assumption.  If so, this
+        test will fail and we'll find out.
+        """
+        curves = get_elliptic_curves()
+        if lib.Cryptography_HAS_EC:
+            self.assertTrue(curves)
+        else:
+            self.assertFalse(curves)
+
+
+    def test_a_curve(self):
+        """
+        :py:obj:`get_elliptic_curve` can be used to retrieve a particular
+        supported curve.
+        """
+        curves = get_elliptic_curves()
+        if curves:
+            curve = next(iter(curves))
+            self.assertEqual(curve.name, get_elliptic_curve(curve.name).name)
+        else:
+            self.assertRaises(ValueError, get_elliptic_curve, u("prime256v1"))
+
+
+    def test_not_a_curve(self):
+        """
+        :py:obj:`get_elliptic_curve` raises :py:class:`ValueError` if called
+        with a name which does not identify a supported curve.
+        """
+        self.assertRaises(
+            ValueError, get_elliptic_curve, u("this curve was just invented"))
+
+
+    def test_repr(self):
+        """
+        The string representation of a curve object includes simply states the
+        object is a curve and what its name is.
+        """
+        curves = get_elliptic_curves()
+        if curves:
+            curve = next(iter(curves))
+            self.assertEqual("<Curve %r>" % (curve.name,), repr(curve))
+
+
+    def test_to_EC_KEY(self):
+        """
+        The curve object can export a version of itself as an EC_KEY* via the
+        private :py:meth:`_EllipticCurve._to_EC_KEY`.
+        """
+        curves = get_elliptic_curves()
+        if curves:
+            curve = next(iter(curves))
+            # It's not easy to assert anything about this object.  However, see
+            # leakcheck/crypto.py for a test that demonstrates it at least does
+            # not leak memory.
+            curve._to_EC_KEY()
+
+
+
+class EllipticCurveFactory(object):
+    """
+    A helper to get the names of two curves.
+    """
+    def __init__(self):
+        curves = iter(get_elliptic_curves())
+        try:
+            self.curve_name = next(curves).name
+            self.another_curve_name = next(curves).name
+        except StopIteration:
+            self.curve_name = self.another_curve_name = None
+
+
+
+class EllipticCurveEqualityTests(TestCase, EqualityTestsMixin):
+    """
+    Tests :py:type:`_EllipticCurve`\ 's implementation of ``==`` and ``!=``.
+    """
+    curve_factory = EllipticCurveFactory()
+
+    if curve_factory.curve_name is None:
+        skip = "There are no curves available there can be no curve objects."
+
+
+    def anInstance(self):
+        """
+        Get the curve object for an arbitrary curve supported by the system.
+        """
+        return get_elliptic_curve(self.curve_factory.curve_name)
+
+
+    def anotherInstance(self):
+        """
+        Get the curve object for an arbitrary curve supported by the system -
+        but not the one returned by C{anInstance}.
+        """
+        return get_elliptic_curve(self.curve_factory.another_curve_name)
+
+
+
+class EllipticCurveHashTests(TestCase):
+    """
+    Tests for :py:type:`_EllipticCurve`\ 's implementation of hashing (thus use
+    as an item in a :py:type:`dict` or :py:type:`set`).
+    """
+    curve_factory = EllipticCurveFactory()
+
+    if curve_factory.curve_name is None:
+        skip = "There are no curves available there can be no curve objects."
+
+
+    def test_contains(self):
+        """
+        The ``in`` operator reports that a :py:type:`set` containing a curve
+        does contain that curve.
+        """
+        curve = get_elliptic_curve(self.curve_factory.curve_name)
+        curves = set([curve])
+        self.assertIn(curve, curves)
+
+
+    def test_does_not_contain(self):
+        """
+        The ``in`` operator reports that a :py:type:`set` not containing a
+        curve does not contain that curve.
+        """
+        curve = get_elliptic_curve(self.curve_factory.curve_name)
+        curves = set([get_elliptic_curve(self.curve_factory.another_curve_name)])
+        self.assertNotIn(curve, curves)
+
+
+
 if __name__ == '__main__':
     main()
diff --git a/OpenSSL/test/test_ssl.py b/OpenSSL/test/test_ssl.py
index 369b1b6..b30bed4 100644
--- a/OpenSSL/test/test_ssl.py
+++ b/OpenSSL/test/test_ssl.py
@@ -20,7 +20,9 @@
 from OpenSSL.crypto import PKey, X509, X509Extension, X509Store
 from OpenSSL.crypto import dump_privatekey, load_privatekey
 from OpenSSL.crypto import dump_certificate, load_certificate
+from OpenSSL.crypto import get_elliptic_curves
 
+from OpenSSL.SSL import _lib
 from OpenSSL.SSL import OPENSSL_VERSION_NUMBER, SSLEAY_VERSION, SSLEAY_CFLAGS
 from OpenSSL.SSL import SSLEAY_PLATFORM, SSLEAY_DIR, SSLEAY_BUILT_ON
 from OpenSSL.SSL import SENT_SHUTDOWN, RECEIVED_SHUTDOWN
@@ -1213,6 +1215,18 @@
         # XXX What should I assert here? -exarkun
 
 
+    def test_set_tmp_ecdh(self):
+        """
+        :py:obj:`Context.set_tmp_ecdh` sets the elliptic curve for
+        Diffie-Hellman to the specified curve.
+        """
+        context = Context(TLSv1_METHOD)
+        for curve in get_elliptic_curves():
+            # The only easily "assertable" thing is that it does not raise an
+            # exception.
+            context.set_tmp_ecdh(curve)
+
+
     def test_set_cipher_list_bytes(self):
         """
         :py:obj:`Context.set_cipher_list` accepts a :py:obj:`bytes` naming the
@@ -1952,9 +1966,13 @@
         """
         client_socket, server_socket = socket_pair()
         # Fill up the client's send buffer so Connection won't be able to write
-        # anything.
-        msg = b"x" * 512
-        for i in range(2048):
+        # anything.  Only write a single byte at a time so we can be sure we
+        # completely fill the buffer.  Even though the socket API is allowed to
+        # signal a short write via its return value it seems this doesn't
+        # always happen on all platforms (FreeBSD and OS X particular) for the
+        # very last bit of available buffer space.
+        msg = b"x"
+        for i in range(1024 * 1024 * 4):
             try:
                 client_socket.send(msg)
             except error as e:
@@ -2177,6 +2195,23 @@
             self.assertEquals(client.recv(2), b('xy'))
 
 
+    try:
+        buffer
+    except NameError:
+        "cannot test sending buffer without buffer"
+    else:
+        def test_short_buffer(self):
+            """
+            When passed a buffer containing a small number of bytes,
+            :py:obj:`Connection.send` transmits all of them and returns the number of
+            bytes sent.
+            """
+            server, client = self._loopback()
+            count = server.send(buffer(b('xy')))
+            self.assertEquals(count, 2)
+            self.assertEquals(client.recv(2), b('xy'))
+
+
 
 class ConnectionSendallTests(TestCase, _LoopbackMixin):
     """
@@ -2220,6 +2255,21 @@
             self.assertEquals(client.recv(1), b('x'))
 
 
+    try:
+        buffer
+    except NameError:
+        "cannot test sending buffers without buffers"
+    else:
+        def test_short_buffers(self):
+            """
+            When passed a buffer containing a small number of bytes,
+            :py:obj:`Connection.sendall` transmits all of them.
+            """
+            server, client = self._loopback()
+            server.sendall(buffer(b('x')))
+            self.assertEquals(client.recv(1), b('x'))
+
+
     def test_long(self):
         """
         :py:obj:`Connection.sendall` transmits all of the bytes in the string passed to
diff --git a/OpenSSL/test/util.py b/OpenSSL/test/util.py
index 4e4d812..21bbdc4 100644
--- a/OpenSSL/test/util.py
+++ b/OpenSSL/test/util.py
@@ -210,6 +210,23 @@
         return containee
     assertIn = failUnlessIn
 
+    def assertNotIn(self, containee, container, msg=None):
+        """
+        Fail the test if C{containee} is found in C{container}.
+
+        @param containee: the value that should not be in C{container}
+        @param container: a sequence type, or in the case of a mapping type,
+                          will follow semantics of 'if key in dict.keys()'
+        @param msg: if msg is None, then the failure message will be
+                    '%r in %r' % (first, second)
+        """
+        if containee in container:
+            raise self.failureException(msg or "%r in %r"
+                                        % (containee, container))
+        return containee
+    failIfIn = assertNotIn
+
+
     def failUnlessIdentical(self, first, second, msg=None):
         """
         Fail the test if :py:data:`first` is not :py:data:`second`.  This is an
@@ -300,3 +317,133 @@
         self.assertTrue(isinstance(theType, type))
         instance = theType(*constructionArgs)
         self.assertIdentical(type(instance), theType)
+
+
+
+class EqualityTestsMixin(object):
+    """
+    A mixin defining tests for the standard implementation of C{==} and C{!=}.
+    """
+    def anInstance(self):
+        """
+        Return an instance of the class under test.  Each call to this method
+        must return a different object.  All objects returned must be equal to
+        each other.
+        """
+        raise NotImplementedError()
+
+
+    def anotherInstance(self):
+        """
+        Return an instance of the class under test.  Each call to this method
+        must return a different object.  The objects must not be equal to the
+        objects returned by C{anInstance}.  They may or may not be equal to
+        each other (they will not be compared against each other).
+        """
+        raise NotImplementedError()
+
+
+    def test_identicalEq(self):
+        """
+        An object compares equal to itself using the C{==} operator.
+        """
+        o = self.anInstance()
+        self.assertTrue(o == o)
+
+
+    def test_identicalNe(self):
+        """
+        An object doesn't compare not equal to itself using the C{!=} operator.
+        """
+        o = self.anInstance()
+        self.assertFalse(o != o)
+
+
+    def test_sameEq(self):
+        """
+        Two objects that are equal to each other compare equal to each other
+        using the C{==} operator.
+        """
+        a = self.anInstance()
+        b = self.anInstance()
+        self.assertTrue(a == b)
+
+
+    def test_sameNe(self):
+        """
+        Two objects that are equal to each other do not compare not equal to
+        each other using the C{!=} operator.
+        """
+        a = self.anInstance()
+        b = self.anInstance()
+        self.assertFalse(a != b)
+
+
+    def test_differentEq(self):
+        """
+        Two objects that are not equal to each other do not compare equal to
+        each other using the C{==} operator.
+        """
+        a = self.anInstance()
+        b = self.anotherInstance()
+        self.assertFalse(a == b)
+
+
+    def test_differentNe(self):
+        """
+        Two objects that are not equal to each other compare not equal to each
+        other using the C{!=} operator.
+        """
+        a = self.anInstance()
+        b = self.anotherInstance()
+        self.assertTrue(a != b)
+
+
+    def test_anotherTypeEq(self):
+        """
+        The object does not compare equal to an object of an unrelated type
+        (which does not implement the comparison) using the C{==} operator.
+        """
+        a = self.anInstance()
+        b = object()
+        self.assertFalse(a == b)
+
+
+    def test_anotherTypeNe(self):
+        """
+        The object compares not equal to an object of an unrelated type (which
+        does not implement the comparison) using the C{!=} operator.
+        """
+        a = self.anInstance()
+        b = object()
+        self.assertTrue(a != b)
+
+
+    def test_delegatedEq(self):
+        """
+        The result of comparison using C{==} is delegated to the right-hand
+        operand if it is of an unrelated type.
+        """
+        class Delegate(object):
+            def __eq__(self, other):
+                # Do something crazy and obvious.
+                return [self]
+
+        a = self.anInstance()
+        b = Delegate()
+        self.assertEqual(a == b, [b])
+
+
+    def test_delegateNe(self):
+        """
+        The result of comparison using C{!=} is delegated to the right-hand
+        operand if it is of an unrelated type.
+        """
+        class Delegate(object):
+            def __ne__(self, other):
+                # Do something crazy and obvious.
+                return [self]
+
+        a = self.anInstance()
+        b = Delegate()
+        self.assertEqual(a != b, [b])
diff --git a/README b/README.rst
similarity index 74%
rename from README
rename to README.rst
index 1b2a093..de9aa4e 100644
--- a/README
+++ b/README.rst
@@ -7,3 +7,6 @@
 See http://github.com/pyca/pyopenssl for development.
 
 See https://mail.python.org/mailman/listinfo/pyopenssl-users for the discussion mailing list.
+
+.. image:: https://coveralls.io/repos/pyca/pyopenssl/badge.png
+  :target: https://coveralls.io/r/pyca/pyopenssl
diff --git a/doc/api/crypto.rst b/doc/api/crypto.rst
index ee93cfb..b360e89 100644
--- a/doc/api/crypto.rst
+++ b/doc/api/crypto.rst
@@ -119,6 +119,28 @@
     Generic exception used in the :py:mod:`.crypto` module.
 
 
+.. py:function:: get_elliptic_curves
+
+    Return a set of objects representing the elliptic curves supported in the
+    OpenSSL build in use.
+
+    The curve objects have a :py:class:`unicode` ``name`` attribute by which
+    they identify themselves.
+
+    The curve objects are useful as values for the argument accepted by
+    :py:meth:`Context.set_tmp_ecdh` to specify which elliptical curve should be
+    used for ECDHE key exchange.
+
+
+.. py:function:: get_elliptic_curve
+
+    Return a single curve object selected by name.
+
+    See :py:func:`get_elliptic_curves` for information about curve objects.
+
+    If the named curve is not supported then :py:class:`ValueError` is raised.
+
+
 .. py:function:: dump_certificate(type, cert)
 
     Dump the certificate *cert* into a buffer string encoded with the type
diff --git a/doc/api/ssl.rst b/doc/api/ssl.rst
index e1c1d8a..a75af1f 100644
--- a/doc/api/ssl.rst
+++ b/doc/api/ssl.rst
@@ -317,6 +317,15 @@
     Load parameters for Ephemeral Diffie-Hellman from *dhfile*.
 
 
+.. py:method:: Context.set_tmp_ecdh(curve)
+
+   Select a curve to use for ECDHE key exchange.
+
+   The valid values of *curve* are the objects returned by
+   :py:func:`OpenSSL.crypto.get_elliptic_curves` or
+   :py:func:`OpenSSL.crypto.get_elliptic_curve`.
+
+
 .. py:method:: Context.set_app_data(data)
 
     Associate *data* with this Context object. *data* can be retrieved
diff --git a/leakcheck/crypto.py b/leakcheck/crypto.py
index 6a9af92..ca79b7c 100644
--- a/leakcheck/crypto.py
+++ b/leakcheck/crypto.py
@@ -4,7 +4,10 @@
 import sys
 
 from OpenSSL.crypto import (
-    FILETYPE_PEM, TYPE_DSA, Error, PKey, X509, load_privatekey)
+    FILETYPE_PEM, TYPE_DSA, Error, PKey, X509, load_privatekey, CRL, Revoked,
+    get_elliptic_curves, _X509_REVOKED_dup)
+
+from OpenSSL._util import lib as _lib
 
 
 
@@ -101,6 +104,63 @@
                 pass
 
 
+
+class Checker_CRL(BaseChecker):
+    """
+    Leak checks for L{CRL.add_revoked} and L{CRL.get_revoked}.
+    """
+    def check_add_revoked(self):
+        """
+        Call the add_revoked method repeatedly on an empty CRL.
+        """
+        for i in xrange(self.iterations * 200):
+            CRL().add_revoked(Revoked())
+
+
+    def check_get_revoked(self):
+        """
+        Create a CRL object with 100 Revoked objects, then call the
+        get_revoked method repeatedly.
+        """
+        crl = CRL()
+        for i in xrange(100):
+            crl.add_revoked(Revoked())
+        for i in xrange(self.iterations):
+            crl.get_revoked()
+
+
+
+class Checker_X509_REVOKED_dup(BaseChecker):
+    """
+    Leak checks for :py:obj:`_X509_REVOKED_dup`.
+    """
+    def check_X509_REVOKED_dup(self):
+        """
+        Copy an empty Revoked object repeatedly. The copy is not garbage
+        collected, therefore it needs to be manually freed.
+        """
+        for i in xrange(self.iterations * 100):
+            revoked_copy = _X509_REVOKED_dup(Revoked()._revoked)
+            _lib.X509_REVOKED_free(revoked_copy)
+
+
+
+class Checker_EllipticCurve(BaseChecker):
+    """
+    Leak checks for :py:obj:`_EllipticCurve`.
+    """
+    def check_to_EC_KEY(self):
+        """
+        Repeatedly create an EC_KEY* from an :py:obj:`_EllipticCurve`.  The
+        structure should be automatically garbage collected.
+        """
+        curves = get_elliptic_curves()
+        if curves:
+            curve = next(iter(curves))
+            for i in xrange(self.iterations * 1000):
+                curve._to_EC_KEY()
+
+
 def vmsize():
     return [x for x in file('/proc/self/status').readlines() if 'VmSize' in x]