Commit 923df6f2 authored by Antoine Pitrou's avatar Antoine Pitrou

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.
parent d1301953
...@@ -428,6 +428,14 @@ Constants ...@@ -428,6 +428,14 @@ Constants
.. versionadded:: 3.3 .. 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 .. data:: HAS_SNI
Whether the OpenSSL library has built-in support for the *Server Name 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. ...@@ -672,6 +680,24 @@ to speed up repeated connections from the same clients.
when connected, the :meth:`SSLSocket.cipher` method of SSL sockets will when connected, the :meth:`SSLSocket.cipher` method of SSL sockets will
give the currently selected cipher. 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, \ .. method:: SSLContext.wrap_socket(sock, server_side=False, \
do_handshake_on_connect=True, suppress_ragged_eofs=True, \ do_handshake_on_connect=True, suppress_ragged_eofs=True, \
server_hostname=None) server_hostname=None)
......
...@@ -68,7 +68,7 @@ from _ssl import ( ...@@ -68,7 +68,7 @@ from _ssl import (
from _ssl import CERT_NONE, CERT_OPTIONAL, CERT_REQUIRED from _ssl import CERT_NONE, CERT_OPTIONAL, CERT_REQUIRED
from _ssl import ( from _ssl import (
OP_ALL, OP_NO_SSLv2, OP_NO_SSLv3, OP_NO_TLSv1, 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 RAND_status, RAND_egd, RAND_add, RAND_bytes, RAND_pseudo_bytes
from _ssl import ( from _ssl import (
......
...@@ -176,6 +176,9 @@ if __name__ == "__main__": ...@@ -176,6 +176,9 @@ if __name__ == "__main__":
action='store_false', help='be less verbose') action='store_false', help='be less verbose')
parser.add_argument('-s', '--stats', dest='use_stats_handler', default=False, parser.add_argument('-s', '--stats', dest='use_stats_handler', default=False,
action='store_true', help='always return stats page') 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() args = parser.parse_args()
support.verbose = args.verbose support.verbose = args.verbose
...@@ -186,6 +189,8 @@ if __name__ == "__main__": ...@@ -186,6 +189,8 @@ if __name__ == "__main__":
handler_class.root = os.getcwd() handler_class.root = os.getcwd()
context = ssl.SSLContext(ssl.PROTOCOL_TLSv1) context = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
context.load_cert_chain(CERTFILE) context.load_cert_chain(CERTFILE)
if args.curve_name:
context.set_ecdh_curve(args.curve_name)
server = HTTPSServer(("", args.port), handler_class, context) server = HTTPSServer(("", args.port), handler_class, context)
if args.verbose: if args.verbose:
......
...@@ -99,6 +99,7 @@ class BasicSocketTests(unittest.TestCase): ...@@ -99,6 +99,7 @@ class BasicSocketTests(unittest.TestCase):
ssl.CERT_OPTIONAL ssl.CERT_OPTIONAL
ssl.CERT_REQUIRED ssl.CERT_REQUIRED
ssl.OP_CIPHER_SERVER_PREFERENCE ssl.OP_CIPHER_SERVER_PREFERENCE
ssl.OP_SINGLE_ECDH_USE
self.assertIn(ssl.HAS_SNI, {True, False}) self.assertIn(ssl.HAS_SNI, {True, False})
def test_random(self): def test_random(self):
...@@ -558,6 +559,15 @@ class ContextTests(unittest.TestCase): ...@@ -558,6 +559,15 @@ class ContextTests(unittest.TestCase):
ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1) ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
ctx.set_default_verify_paths() 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): class NetworkedTests(unittest.TestCase):
......
...@@ -419,6 +419,10 @@ Core and Builtins ...@@ -419,6 +419,10 @@ Core and Builtins
Library 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 - Issue #13635: Add ssl.OP_CIPHER_SERVER_PREFERENCE, so that SSL servers
choose the cipher based on their own preferences, rather than on the choose the cipher based on their own preferences, rather than on the
client's. client's.
......
...@@ -1986,6 +1986,33 @@ set_default_verify_paths(PySSLContext *self, PyObject *unused) ...@@ -1986,6 +1986,33 @@ set_default_verify_paths(PySSLContext *self, PyObject *unused)
Py_RETURN_NONE; 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[] = { static PyGetSetDef context_getsetlist[] = {
{"options", (getter) get_options, {"options", (getter) get_options,
(setter) set_options, NULL}, (setter) set_options, NULL},
...@@ -2007,6 +2034,8 @@ static struct PyMethodDef context_methods[] = { ...@@ -2007,6 +2034,8 @@ static struct PyMethodDef context_methods[] = {
METH_NOARGS, NULL}, METH_NOARGS, NULL},
{"set_default_verify_paths", (PyCFunction) set_default_verify_paths, {"set_default_verify_paths", (PyCFunction) set_default_verify_paths,
METH_NOARGS, NULL}, METH_NOARGS, NULL},
{"set_ecdh_curve", (PyCFunction) set_ecdh_curve,
METH_O, NULL},
{NULL, NULL} /* sentinel */ {NULL, NULL} /* sentinel */
}; };
...@@ -2452,6 +2481,7 @@ PyInit__ssl(void) ...@@ -2452,6 +2481,7 @@ PyInit__ssl(void)
PyModule_AddIntConstant(m, "OP_NO_TLSv1", SSL_OP_NO_TLSv1); PyModule_AddIntConstant(m, "OP_NO_TLSv1", SSL_OP_NO_TLSv1);
PyModule_AddIntConstant(m, "OP_CIPHER_SERVER_PREFERENCE", PyModule_AddIntConstant(m, "OP_CIPHER_SERVER_PREFERENCE",
SSL_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 #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
r = Py_True; r = Py_True;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment