]> granicus.if.org Git - python/commitdiff
Issue #13627: Add support for SSL Elliptic Curve-based Diffie-Hellman
authorAntoine Pitrou <solipsis@pitrou.net>
Mon, 19 Dec 2011 16:16:51 +0000 (17:16 +0100)
committerAntoine Pitrou <solipsis@pitrou.net>
Mon, 19 Dec 2011 16:16:51 +0000 (17:16 +0100)
key exchange, through the SSLContext.set_ecdh_curve() method and the
ssl.OP_SINGLE_ECDH_USE option.

Doc/library/ssl.rst
Lib/ssl.py
Lib/test/ssl_servers.py
Lib/test/test_ssl.py
Misc/NEWS
Modules/_ssl.c

index 69eaf8b9302bbdb03cdb312efcd9059accfa2fc3..7017b8f4f41ef9325663a8bd3796a25f42d41539 100644 (file)
@@ -428,6 +428,14 @@ Constants
 
    .. versionadded:: 3.3
 
+.. data:: OP_SINGLE_ECDH_USE
+
+   Prevents re-use of the same ECDH key for several SSL sessions.  This
+   improves forward secrecy but requires more computational resources.
+   This option only applies to server sockets.
+
+   .. versionadded:: 3.3
+
 .. data:: HAS_SNI
 
    Whether the OpenSSL library has built-in support for the *Server Name
@@ -672,6 +680,24 @@ to speed up repeated connections from the same clients.
       when connected, the :meth:`SSLSocket.cipher` method of SSL sockets will
       give the currently selected cipher.
 
+.. method:: SSLContext.set_ecdh_curve(curve_name)
+
+   Set the curve name for Elliptic Curve-based Diffie-Hellman (abbreviated
+   ECDH) key exchange.  Using Diffie-Hellman key exchange improves forward
+   secrecy at the expense of computational resources (both on the server and
+   on the client).  The *curve_name* parameter should be a string describing
+   a well-known elliptic curve, for example ``prime256v1`` for a widely
+   supported curve.
+
+   This setting doesn't apply to client sockets.  You can also use the
+   :data:`OP_SINGLE_ECDH_USE` option to further improve security.
+
+   .. versionadded:: 3.3
+
+   .. seealso::
+      `SSL/TLS & Perfect Forward Secrecy <http://vincent.bernat.im/en/blog/2011-ssl-perfect-forward-secrecy.html>`_
+         Vincent Bernat.
+
 .. method:: SSLContext.wrap_socket(sock, server_side=False, \
       do_handshake_on_connect=True, suppress_ragged_eofs=True, \
       server_hostname=None)
index 0cf2fae63362805aa6b7609aada8ff36ee607efb..d2441042a5ef6dbda03fd34db231cf6065e8e47a 100644 (file)
@@ -68,7 +68,7 @@ from _ssl import (
 from _ssl import CERT_NONE, CERT_OPTIONAL, CERT_REQUIRED
 from _ssl import (
     OP_ALL, OP_NO_SSLv2, OP_NO_SSLv3, OP_NO_TLSv1,
-    OP_CIPHER_SERVER_PREFERENCE,
+    OP_CIPHER_SERVER_PREFERENCE, OP_SINGLE_ECDH_USE,
     )
 from _ssl import RAND_status, RAND_egd, RAND_add, RAND_bytes, RAND_pseudo_bytes
 from _ssl import (
index 77be3814e85a804bdd93928e523e912b342e4873..86bc95052e709fe3c2136628bc34a17df98716ab 100644 (file)
@@ -176,6 +176,9 @@ if __name__ == "__main__":
                         action='store_false', help='be less verbose')
     parser.add_argument('-s', '--stats', dest='use_stats_handler', default=False,
                         action='store_true', help='always return stats page')
+    parser.add_argument('--curve-name', dest='curve_name', type=str,
+                        action='store',
+                        help='curve name for EC-based Diffie-Hellman')
     args = parser.parse_args()
 
     support.verbose = args.verbose
@@ -186,6 +189,8 @@ if __name__ == "__main__":
         handler_class.root = os.getcwd()
     context = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
     context.load_cert_chain(CERTFILE)
+    if args.curve_name:
+        context.set_ecdh_curve(args.curve_name)
 
     server = HTTPSServer(("", args.port), handler_class, context)
     if args.verbose:
index 288b714cebc4e83c5c574a2cb3760d8fc66bb219..505550f269fe1cc8790f49883211bc31a8aa097e 100644 (file)
@@ -99,6 +99,7 @@ class BasicSocketTests(unittest.TestCase):
         ssl.CERT_OPTIONAL
         ssl.CERT_REQUIRED
         ssl.OP_CIPHER_SERVER_PREFERENCE
+        ssl.OP_SINGLE_ECDH_USE
         self.assertIn(ssl.HAS_SNI, {True, False})
 
     def test_random(self):
@@ -558,6 +559,15 @@ class ContextTests(unittest.TestCase):
         ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
         ctx.set_default_verify_paths()
 
+    def test_set_ecdh_curve(self):
+        ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
+        ctx.set_ecdh_curve("prime256v1")
+        ctx.set_ecdh_curve(b"prime256v1")
+        self.assertRaises(TypeError, ctx.set_ecdh_curve)
+        self.assertRaises(TypeError, ctx.set_ecdh_curve, None)
+        self.assertRaises(ValueError, ctx.set_ecdh_curve, "foo")
+        self.assertRaises(ValueError, ctx.set_ecdh_curve, b"foo")
+
 
 class NetworkedTests(unittest.TestCase):
 
index aeb1c899664287a609f9414c9242357f22bb72da..926184d47fe767ce1061344d07f553906d1856ef 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -419,6 +419,10 @@ Core and Builtins
 Library
 -------
 
+- Issue #13627: Add support for SSL Elliptic Curve-based Diffie-Hellman
+  key exchange, through the SSLContext.set_ecdh_curve() method and the
+  ssl.OP_SINGLE_ECDH_USE option.
+
 - Issue #13635: Add ssl.OP_CIPHER_SERVER_PREFERENCE, so that SSL servers
   choose the cipher based on their own preferences, rather than on the
   client's.
index 0f3d2c139e68323483e61e0d5298716605946e4d..725f1485b4c96899caeb45143d2997b1385c69cb 100644 (file)
@@ -1986,6 +1986,33 @@ set_default_verify_paths(PySSLContext *self, PyObject *unused)
     Py_RETURN_NONE;
 }
 
+static PyObject *
+set_ecdh_curve(PySSLContext *self, PyObject *name)
+{
+    PyObject *name_bytes;
+    int nid;
+    EC_KEY *key;
+
+    if (!PyUnicode_FSConverter(name, &name_bytes))
+        return NULL;
+    assert(PyBytes_Check(name_bytes));
+    nid = OBJ_sn2nid(PyBytes_AS_STRING(name_bytes));
+    Py_DECREF(name_bytes);
+    if (nid == 0) {
+        PyErr_Format(PyExc_ValueError,
+                     "unknown elliptic curve name %R", name);
+        return NULL;
+    }
+    key = EC_KEY_new_by_curve_name(nid);
+    if (key == NULL) {
+        _setSSLError(NULL, 0, __FILE__, __LINE__);
+        return NULL;
+    }
+    SSL_CTX_set_tmp_ecdh(self->ctx, key);
+    EC_KEY_free(key);
+    Py_RETURN_NONE;
+}
+
 static PyGetSetDef context_getsetlist[] = {
     {"options", (getter) get_options,
                 (setter) set_options, NULL},
@@ -2007,6 +2034,8 @@ static struct PyMethodDef context_methods[] = {
                       METH_NOARGS, NULL},
     {"set_default_verify_paths", (PyCFunction) set_default_verify_paths,
                                  METH_NOARGS, NULL},
+    {"set_ecdh_curve", (PyCFunction) set_ecdh_curve,
+                       METH_O, NULL},
     {NULL, NULL}        /* sentinel */
 };
 
@@ -2452,6 +2481,7 @@ PyInit__ssl(void)
     PyModule_AddIntConstant(m, "OP_NO_TLSv1", SSL_OP_NO_TLSv1);
     PyModule_AddIntConstant(m, "OP_CIPHER_SERVER_PREFERENCE",
                             SSL_OP_CIPHER_SERVER_PREFERENCE);
+    PyModule_AddIntConstant(m, "OP_SINGLE_ECDH_USE", SSL_OP_SINGLE_ECDH_USE);
 
 #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
     r = Py_True;