Issue #13626: Add support for SSL Diffie-Hellman key exchange, through the
SSLContext.load_dh_params() method and the ssl.OP_SINGLE_DH_USE option.
This commit is contained in:
parent
5ad1af076c
commit
0e576f1f50
|
@ -428,9 +428,17 @@ Constants
|
|||
|
||||
.. versionadded:: 3.3
|
||||
|
||||
.. data:: OP_SINGLE_DH_USE
|
||||
|
||||
Prevents re-use of the same DH key for distinct SSL sessions. This
|
||||
improves forward secrecy but requires more computational resources.
|
||||
This option only applies to server sockets.
|
||||
|
||||
.. versionadded:: 3.3
|
||||
|
||||
.. data:: OP_SINGLE_ECDH_USE
|
||||
|
||||
Prevents re-use of the same ECDH key for several SSL sessions. This
|
||||
Prevents re-use of the same ECDH key for distinct SSL sessions. This
|
||||
improves forward secrecy but requires more computational resources.
|
||||
This option only applies to server sockets.
|
||||
|
||||
|
@ -707,12 +715,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.load_dh_params(dhfile)
|
||||
|
||||
Load the key generation parameters for Diffie-Helman (DH) key exchange.
|
||||
Using DH key exchange improves forward secrecy at the expense of
|
||||
computational resources (both on the server and on the client).
|
||||
The *dhfile* parameter should be the path to a file containing DH
|
||||
parameters in PEM format.
|
||||
|
||||
This setting doesn't apply to client sockets. You can also use the
|
||||
:data:`OP_SINGLE_DH_USE` option to further improve security.
|
||||
|
||||
.. versionadded:: 3.3
|
||||
|
||||
.. 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
|
||||
Set the curve name for Elliptic Curve-based Diffie-Hellman (ECDH) key
|
||||
exchange. ECDH is significantly faster than regular DH while arguably
|
||||
as secure. The *curve_name* parameter should be a string describing
|
||||
a well-known elliptic curve, for example ``prime256v1`` for a widely
|
||||
supported curve.
|
||||
|
||||
|
|
|
@ -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_SINGLE_ECDH_USE,
|
||||
OP_CIPHER_SERVER_PREFERENCE, OP_SINGLE_DH_USE, OP_SINGLE_ECDH_USE,
|
||||
)
|
||||
try:
|
||||
from _ssl import OP_NO_COMPRESSION
|
||||
|
|
|
@ -180,6 +180,8 @@ if __name__ == "__main__":
|
|||
parser.add_argument('--curve-name', dest='curve_name', type=str,
|
||||
action='store',
|
||||
help='curve name for EC-based Diffie-Hellman')
|
||||
parser.add_argument('--dh', dest='dh_file', type=str, action='store',
|
||||
help='PEM file containing DH parameters')
|
||||
args = parser.parse_args()
|
||||
|
||||
support.verbose = args.verbose
|
||||
|
@ -192,6 +194,8 @@ if __name__ == "__main__":
|
|||
context.load_cert_chain(CERTFILE)
|
||||
if args.curve_name:
|
||||
context.set_ecdh_curve(args.curve_name)
|
||||
if args.dh_file:
|
||||
context.load_dh_params(args.dh_file)
|
||||
|
||||
server = HTTPSServer(("", args.port), handler_class, context)
|
||||
if args.verbose:
|
||||
|
|
|
@ -56,6 +56,8 @@ WRONGCERT = data_file("XXXnonexisting.pem")
|
|||
BADKEY = data_file("badkey.pem")
|
||||
NOKIACERT = data_file("nokia.pem")
|
||||
|
||||
DHFILE = data_file("dh512.pem")
|
||||
BYTES_DHFILE = os.fsencode(DHFILE)
|
||||
|
||||
def handle_error(prefix):
|
||||
exc_format = ' '.join(traceback.format_exception(*sys.exc_info()))
|
||||
|
@ -99,6 +101,7 @@ class BasicSocketTests(unittest.TestCase):
|
|||
ssl.CERT_OPTIONAL
|
||||
ssl.CERT_REQUIRED
|
||||
ssl.OP_CIPHER_SERVER_PREFERENCE
|
||||
ssl.OP_SINGLE_DH_USE
|
||||
ssl.OP_SINGLE_ECDH_USE
|
||||
if ssl.OPENSSL_VERSION_INFO >= (1, 0):
|
||||
ssl.OP_NO_COMPRESSION
|
||||
|
@ -538,6 +541,19 @@ class ContextTests(unittest.TestCase):
|
|||
# Issue #10989: crash if the second argument type is invalid
|
||||
self.assertRaises(TypeError, ctx.load_verify_locations, None, True)
|
||||
|
||||
def test_load_dh_params(self):
|
||||
ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
|
||||
ctx.load_dh_params(DHFILE)
|
||||
if os.name != 'nt':
|
||||
ctx.load_dh_params(BYTES_DHFILE)
|
||||
self.assertRaises(TypeError, ctx.load_dh_params)
|
||||
self.assertRaises(TypeError, ctx.load_dh_params, None)
|
||||
with self.assertRaises(FileNotFoundError) as cm:
|
||||
ctx.load_dh_params(WRONGCERT)
|
||||
self.assertEqual(cm.exception.errno, errno.ENOENT)
|
||||
with self.assertRaisesRegex(ssl.SSLError, "PEM routines"):
|
||||
ctx.load_dh_params(CERTFILE)
|
||||
|
||||
@skip_if_broken_ubuntu_ssl
|
||||
def test_session_stats(self):
|
||||
for proto in PROTOCOLS:
|
||||
|
@ -1802,6 +1818,19 @@ else:
|
|||
chatty=True, connectionchatty=True)
|
||||
self.assertIs(stats['compression'], None)
|
||||
|
||||
def test_dh_params(self):
|
||||
# Check we can get a connection with ephemeral Diffie-Hellman
|
||||
context = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
|
||||
context.load_cert_chain(CERTFILE)
|
||||
context.load_dh_params(DHFILE)
|
||||
context.set_ciphers("kEDH")
|
||||
stats = server_params_test(context, context,
|
||||
chatty=True, connectionchatty=True)
|
||||
cipher = stats["cipher"][0]
|
||||
parts = cipher.split("-")
|
||||
if "ADH" not in parts and "EDH" not in parts and "DHE" not in parts:
|
||||
self.fail("Non-DH cipher: " + cipher[0])
|
||||
|
||||
|
||||
def test_main(verbose=False):
|
||||
if support.verbose:
|
||||
|
|
|
@ -419,6 +419,9 @@ Core and Builtins
|
|||
Library
|
||||
-------
|
||||
|
||||
- Issue #13626: Add support for SSL Diffie-Hellman key exchange, through the
|
||||
SSLContext.load_dh_params() method and the ssl.OP_SINGLE_DH_USE option.
|
||||
|
||||
- Issue #11006: Don't issue low level warning in subprocess when pipe2() fails.
|
||||
|
||||
- Issue #13620: Support for Chrome browser in webbrowser.py Patch contributed
|
||||
|
|
|
@ -1921,6 +1921,38 @@ load_verify_locations(PySSLContext *self, PyObject *args, PyObject *kwds)
|
|||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
load_dh_params(PySSLContext *self, PyObject *filepath)
|
||||
{
|
||||
FILE *f;
|
||||
DH *dh;
|
||||
|
||||
f = _Py_fopen(filepath, "rb");
|
||||
if (f == NULL) {
|
||||
if (!PyErr_Occurred())
|
||||
PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, filepath);
|
||||
return NULL;
|
||||
}
|
||||
errno = 0;
|
||||
PySSL_BEGIN_ALLOW_THREADS
|
||||
dh = PEM_read_DHparams(f, NULL, NULL, NULL);
|
||||
PySSL_END_ALLOW_THREADS
|
||||
if (dh == NULL) {
|
||||
if (errno != 0) {
|
||||
ERR_clear_error();
|
||||
PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, filepath);
|
||||
}
|
||||
else {
|
||||
_setSSLError(NULL, 0, __FILE__, __LINE__);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
if (SSL_CTX_set_tmp_dh(self->ctx, dh) == 0)
|
||||
_setSSLError(NULL, 0, __FILE__, __LINE__);
|
||||
DH_free(dh);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
context_wrap_socket(PySSLContext *self, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
|
@ -2050,6 +2082,8 @@ static struct PyMethodDef context_methods[] = {
|
|||
METH_VARARGS, NULL},
|
||||
{"load_cert_chain", (PyCFunction) load_cert_chain,
|
||||
METH_VARARGS | METH_KEYWORDS, NULL},
|
||||
{"load_dh_params", (PyCFunction) load_dh_params,
|
||||
METH_O, NULL},
|
||||
{"load_verify_locations", (PyCFunction) load_verify_locations,
|
||||
METH_VARARGS | METH_KEYWORDS, NULL},
|
||||
{"session_stats", (PyCFunction) session_stats,
|
||||
|
@ -2505,6 +2539,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_DH_USE", SSL_OP_SINGLE_DH_USE);
|
||||
PyModule_AddIntConstant(m, "OP_SINGLE_ECDH_USE", SSL_OP_SINGLE_ECDH_USE);
|
||||
#ifdef SSL_OP_NO_COMPRESSION
|
||||
PyModule_AddIntConstant(m, "OP_NO_COMPRESSION",
|
||||
|
|
|
@ -310,6 +310,12 @@ _Py_fopen(PyObject *path, const char *mode)
|
|||
wchar_t wmode[10];
|
||||
int usize;
|
||||
|
||||
if (!PyUnicode_Check(path)) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"str file path expected under Windows, got %R",
|
||||
Py_TYPE(path));
|
||||
return NULL;
|
||||
}
|
||||
wpath = PyUnicode_AsUnicode(path);
|
||||
if (wpath == NULL)
|
||||
return NULL;
|
||||
|
|
Loading…
Reference in New Issue