diff -r f5e3f2f0fe79 Doc/library/ssl.rst --- a/Doc/library/ssl.rst Thu Feb 07 22:17:21 2013 -0800 +++ b/Doc/library/ssl.rst Wed Feb 13 23:12:04 2013 +0100 @@ -26,7 +26,8 @@ Some behavior may be platform dependent, since calls are made to the operating system socket APIs. The installed version of OpenSSL may also - cause variations in behavior. + cause variations in behavior. For example, TLSv1.1 and TLSv1.2 come with + openssl version 1.0.1. This section documents the objects and functions in the ``ssl`` module; for more general information about TLS, SSL, and certificates, the reader is referred to @@ -177,14 +178,16 @@ .. table:: - ======================== ========= ========= ========== ========= - *client* / **server** **SSLv2** **SSLv3** **SSLv23** **TLSv1** - ------------------------ --------- --------- ---------- --------- - *SSLv2* yes no yes no - *SSLv3* no yes yes no - *SSLv23* yes no yes no - *TLSv1* no no yes yes - ======================== ========= ========= ========== ========= + ======================== ========= ========= ========== ========= =========== =========== + *client* / **server** **SSLv2** **SSLv3** **SSLv23** **TLSv1** **TLSv1.1** **TLSv1.2** + ------------------------ --------- --------- ---------- --------- ----------- ----------- + *SSLv2* yes no yes no no no + *SSLv3* no yes yes no no no + *SSLv23* yes no yes no no no + *TLSv1* no no yes yes no no + *TLSv1.1* no no yes no yes no + *TLSv1.2* no no yes no no yes + ======================== ========= ========= ========== ========= =========== =========== .. note:: @@ -401,10 +404,26 @@ .. data:: PROTOCOL_TLSv1 - Selects TLS version 1 as the channel encryption protocol. This is the most + Selects TLS version 1.0 as the channel encryption protocol. + +.. data:: PROTOCOL_TLSv1_1 + + .. warning:: requires at least openssl version 1.0.1 + + Selects TLS version 1.1 as the channel encryption protocol. + + .. versionadded:: 3.4 + +.. data:: PROTCOL_TLSv1_2 + + .. warning:: requires at least openssl version 1.0.1 + + Selects TLS version 1.2 as the channel encryption protocol. This is the most modern version, and probably the best choice for maximum protection, if both sides can speak it. + .. versionadded:: 3.4 + .. data:: OP_ALL Enables workarounds for various bugs present in other SSL implementations. @@ -437,6 +456,22 @@ .. versionadded:: 3.2 +.. data:: OP_NO_TLSv1_1 + + Prevents a TLSv1.1 connection. This option is only applicable in conjunction + with :const:`PROCOL_SSLv23`. It prevents the peers from choosing TLSv1.1 as + the protocol version. Available only with openssl version 1.0.1+. + + .. versionadded:: 3.4 + +.. data:: OP_NO_TLSv1_2 + + Prevents a TLSv1.2 connection. This option is only applicable in conjunction + with :const:`PROCOL_SSLv23`. It prevents the peers from choosing TLSv1.2 as + the protocol version. Available only with openssl version 1.0.1+. + + .. versionadded:: 3.4 + .. data:: OP_CIPHER_SERVER_PREFERENCE Use the server's cipher ordering preference, rather than the client's. diff -r f5e3f2f0fe79 Doc/whatsnew/3.4.rst --- a/Doc/whatsnew/3.4.rst Thu Feb 07 22:17:21 2013 -0800 +++ b/Doc/whatsnew/3.4.rst Wed Feb 13 23:12:04 2013 +0100 @@ -103,6 +103,7 @@ Significantly Improved Library Modules: * SHA-3 (Keccak) support for :mod:`hashlib`. +* TLSv1.1 and TLSv1.2 support for :mod:`ssl`. Security improvements: diff -r f5e3f2f0fe79 Lib/ssl.py --- a/Lib/ssl.py Thu Feb 07 22:17:21 2013 -0800 +++ b/Lib/ssl.py Wed Feb 13 23:12:04 2013 +0100 @@ -52,6 +52,8 @@ PROTOCOL_SSLv3 PROTOCOL_SSLv23 PROTOCOL_TLSv1 +PROTOCOL_TLSv1_1 +PROTOCOL_TLSv1_2 The following constants identify various SSL alert message descriptions as per http://www.iana.org/assignments/tls-parameters/tls-parameters.xml#tls-parameters-6 @@ -110,8 +112,7 @@ from _ssl import HAS_SNI, HAS_ECDH, HAS_NPN -from _ssl import (PROTOCOL_SSLv3, PROTOCOL_SSLv23, - PROTOCOL_TLSv1) +from _ssl import PROTOCOL_SSLv3, PROTOCOL_SSLv23, PROTOCOL_TLSv1 from _ssl import _OPENSSL_API_VERSION @@ -128,6 +129,14 @@ else: _PROTOCOL_NAMES[PROTOCOL_SSLv2] = "SSLv2" +try: + from _ssl import PROTOCOL_TLSv1_1, PROTOCOL_TLSv1_2 +except ImportError: + pass +else: + _PROTOCOL_NAMES[PROTOCOL_TLSv1_1] = "TLSv1.1" + _PROTOCOL_NAMES[PROTOCOL_TLSv1_2] = "TLSv1.2" + from socket import getnameinfo as _getnameinfo from socket import socket, AF_INET, SOCK_STREAM, create_connection import base64 # for DER-to-PEM translation diff -r f5e3f2f0fe79 Lib/test/test_ssl.py --- a/Lib/test/test_ssl.py Thu Feb 07 22:17:21 2013 -0800 +++ b/Lib/test/test_ssl.py Wed Feb 13 23:12:04 2013 +0100 @@ -22,10 +22,11 @@ PROTOCOLS = [ ssl.PROTOCOL_SSLv3, - ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_TLSv1 + ssl.PROTOCOL_SSLv23, + ssl.PROTOCOL_TLSv1, ] -if hasattr(ssl, 'PROTOCOL_SSLv2'): - PROTOCOLS.append(ssl.PROTOCOL_SSLv2) +optional_protos = ('PROTOCOL_SSLv2', 'PROTOCOL_TLSv1_1', 'PROTOCOL_TLSv_1_2') +PROTOCOLS.extend(getattr(ssl, proto) for proto in optional_protos if hasattr(ssl, proto)) HOST = support.HOST @@ -95,6 +96,17 @@ else: return func + +def skip_if_unsupported_tlsv1_1(func): + """Skip test if openssl version does not support TLSv1.1, TLSv1.2.""" + def unsupported(*args, **kwargs): + raise unittest.SkipTest("TLS version > 1.0 not supported.") + + if 'PROTOCOL_TLSv1_1' not in PROTOCOLS and 'PROTOCOL_TLSv1_2' not in PROTOCOLS: + return unsupported + else: + return func + needs_sni = unittest.skipUnless(ssl.HAS_SNI, "SNI support needed for this test") @@ -102,6 +114,8 @@ def test_constants(self): #ssl.PROTOCOL_SSLv2 + #ssl.PROTOCOL_TLSv1_1 + #ssl.PROTOCOL_TLSv1_2 ssl.PROTOCOL_SSLv23 ssl.PROTOCOL_SSLv3 ssl.PROTOCOL_TLSv1 @@ -396,11 +410,8 @@ @skip_if_broken_ubuntu_ssl def test_constructor(self): - if hasattr(ssl, 'PROTOCOL_SSLv2'): - ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv2) - ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23) - ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv3) - ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1) + for protocol in PROTOCOLS: + ssl.SSLContext(protocol) self.assertRaises(TypeError, ssl.SSLContext) self.assertRaises(ValueError, ssl.SSLContext, -1) self.assertRaises(ValueError, ssl.SSLContext, 42) @@ -1356,12 +1367,15 @@ client_context.options = ssl.OP_ALL | client_options server_context = ssl.SSLContext(server_protocol) server_context.options = ssl.OP_ALL | server_options + + # NOTE: we must enable "ALL" ciphers on the server, otherwise an + # SSLv23 client will send an SSLv3 hello (rather than SSLv2) + # starting from OpenSSL 1.0.0 (see issue #8322). + if server_context.protocol == ssl.PROTOCOL_SSLv23: + server_context.set_ciphers("ALL") + for ctx in (client_context, server_context): ctx.verify_mode = certsreqs - # NOTE: we must enable "ALL" ciphers, otherwise an SSLv23 client - # will send an SSLv3 hello (rather than SSLv2) starting from - # OpenSSL 1.0.0 (see issue #8322). - ctx.set_ciphers("ALL") ctx.load_cert_chain(CERTFILE) ctx.load_verify_locations(CERTFILE) try: @@ -1577,6 +1591,45 @@ try_protocol_combo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_SSLv23, False, client_options=ssl.OP_NO_TLSv1) + @skip_if_unsupported_tlsv1_1 + @skip_if_broken_ubuntu_ssl + def test_protocol_tlsv1_1(self): + """Connecting to a TLSv1.1 server with various client options. + Testing against older TLS versions.""" + if support.verbose: + sys.stdout.write("\n") + try_protocol_combo(ssl.PROTOCOL_TLSv1_1, ssl.PROTOCOL_TLSv1_1, True) + if hasattr(ssl, 'PROTOCOL_SSLv2'): + try_protocol_combo(ssl.PROTOCOL_TLSv1_1, ssl.PROTOCOL_SSLv2, False) + try_protocol_combo(ssl.PROTOCOL_TLSv1_1, ssl.PROTOCOL_SSLv3, False) + try_protocol_combo(ssl.PROTOCOL_TLSv1_1, ssl.PROTOCOL_SSLv23, False, + client_options=ssl.OP_NO_TLSv1_1) + + try_protocol_combo(ssl.PROTOCOL_TLSv1_1, ssl.PROTOCOL_TLSv1, False) + try_protocol_combo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_TLSv1_1, False) + + + @skip_if_unsupported_tlsv1_1 + @skip_if_broken_ubuntu_ssl + def test_protocol_tlsv1_2(self): + """Connecting to a TLSv1.2 server with various client options. + Testing against older TLS versions.""" + if support.verbose: + sys.stdout.write("\n") + try_protocol_combo(ssl.PROTOCOL_TLSv1_2, ssl.PROTOCOL_TLSv1_2, True, + server_options=ssl.OP_NO_SSLv3|ssl.OP_NO_SSLv2, + client_options=ssl.OP_NO_SSLv3|ssl.OP_NO_SSLv2,) + if hasattr(ssl, 'PROTOCOL_SSLv2'): + try_protocol_combo(ssl.PROTOCOL_TLSv1_2, ssl.PROTOCOL_SSLv2, False) + try_protocol_combo(ssl.PROTOCOL_TLSv1_2, ssl.PROTOCOL_SSLv3, False) + try_protocol_combo(ssl.PROTOCOL_TLSv1_2, ssl.PROTOCOL_SSLv23, False, + client_options=ssl.OP_NO_TLSv1_2) + + try_protocol_combo(ssl.PROTOCOL_TLSv1_2, ssl.PROTOCOL_TLSv1, False) + try_protocol_combo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_TLSv1_2, False) + try_protocol_combo(ssl.PROTOCOL_TLSv1_2, ssl.PROTOCOL_TLSv1_1, False) + try_protocol_combo(ssl.PROTOCOL_TLSv1_1, ssl.PROTOCOL_TLSv1_2, False) + def test_starttls(self): """Switching from clear text to encrypted and back again.""" msgs = (b"msg 1", b"MSG 2", b"STARTTLS", b"MSG 3", b"msg 4", b"ENDTLS", b"msg 5", b"msg 6") diff -r f5e3f2f0fe79 Misc/NEWS --- a/Misc/NEWS Thu Feb 07 22:17:21 2013 -0800 +++ b/Misc/NEWS Wed Feb 13 23:12:04 2013 +0100 @@ -758,6 +758,8 @@ `sha3_256`, `sha3_384` and `sha3_512`. As part of the patch some common code was moved from _hashopenssl.c to hashlib.h. +- Issue #16692: Added TLSv1.1 and TLSv1.2 support for the ssl modules. + Extension Modules ----------------- diff -r f5e3f2f0fe79 Modules/_ssl.c --- a/Modules/_ssl.c Thu Feb 07 22:17:21 2013 -0800 +++ b/Modules/_ssl.c Wed Feb 13 23:12:04 2013 +0100 @@ -40,6 +40,11 @@ #endif +/* openssl comes with TLSv1.1 and TLSv1.2 between 1.0.0h version 1.0.1 + http://www.openssl.org/news/changelog.html + */ +#define SSL_SUPPORT_TLSv1_2 (OPENSSL_VERSION_NUMBER > 0x1000007fL) + enum py_ssl_error { /* these mirror ssl.h */ PY_SSL_ERROR_NONE, @@ -73,7 +78,13 @@ #endif PY_SSL_VERSION_SSL3=1, PY_SSL_VERSION_SSL23, +#ifdef SSL_SUPPORT_TLSv1_2 + PY_SSL_VERSION_TLS1, + PY_SSL_VERSION_TLS1_1, + PY_SSL_VERSION_TLS1_2, +#else PY_SSL_VERSION_TLS1 +#endif }; struct py_ssl_error_code { @@ -182,7 +193,7 @@ int npn_protocols_len; #endif #ifndef OPENSSL_NO_TLSEXT - PyObject *set_hostname; + PyObject *set_hostname; #endif } PySSLContext; @@ -1144,7 +1155,7 @@ const unsigned char *out; unsigned int outlen; - SSL_get0_next_proto_negotiated(self->ssl, + SSL_get0_next_proto_negotiated(self->ssl, &out, &outlen); if (out == NULL) @@ -1732,6 +1743,12 @@ PySSL_BEGIN_ALLOW_THREADS if (proto_version == PY_SSL_VERSION_TLS1) ctx = SSL_CTX_new(TLSv1_method()); +#if SSL_SUPPORT_TLSv1_2 + else if (proto_version == PY_SSL_VERSION_TLS1_1) + ctx = SSL_CTX_new(TLSv1_1_method()); + else if (proto_version == PY_SSL_VERSION_TLS1_2) + ctx = SSL_CTX_new(TLSv1_2_method()); +#endif else if (proto_version == PY_SSL_VERSION_SSL3) ctx = SSL_CTX_new(SSLv3_method()); #ifndef OPENSSL_NO_SSL2 @@ -1766,7 +1783,7 @@ self->npn_protocols = NULL; #endif #ifndef OPENSSL_NO_TLSEXT - self->set_hostname = NULL; + self->set_hostname = NULL; #endif /* Defaults */ SSL_CTX_set_verify(self->ctx, SSL_VERIFY_NONE, NULL); @@ -1834,8 +1851,8 @@ #ifdef OPENSSL_NPN_NEGOTIATED /* this callback gets passed to SSL_CTX_set_next_protos_advertise_cb */ static int -_advertiseNPN_cb(SSL *s, - const unsigned char **data, unsigned int *len, +_advertiseNPN_cb(SSL *s, + const unsigned char **data, unsigned int *len, void *args) { PySSLContext *ssl_ctx = (PySSLContext *) args; @@ -1852,7 +1869,7 @@ } /* this callback gets passed to SSL_CTX_set_next_proto_select_cb */ static int -_selectNPN_cb(SSL *s, +_selectNPN_cb(SSL *s, unsigned char **out, unsigned char *outlen, const unsigned char *server, unsigned int server_len, void *args) @@ -2413,7 +2430,7 @@ if (ssl_socket == Py_None) { goto error; } - + servername_o = PyBytes_FromString(servername); if (servername_o == NULL) { PyErr_WriteUnraisable((PyObject *) ssl_ctx); @@ -3004,6 +3021,12 @@ PY_SSL_VERSION_SSL23); PyModule_AddIntConstant(m, "PROTOCOL_TLSv1", PY_SSL_VERSION_TLS1); +#if SSL_SUPPORT_TLSv1_2 + PyModule_AddIntConstant(m, "PROTOCOL_TLSv1_1", + PY_SSL_VERSION_TLS1_1); + PyModule_AddIntConstant(m, "PROTOCOL_TLSv1_2", + PY_SSL_VERSION_TLS1_2); +#endif /* protocol options */ PyModule_AddIntConstant(m, "OP_ALL", @@ -3011,6 +3034,10 @@ PyModule_AddIntConstant(m, "OP_NO_SSLv2", SSL_OP_NO_SSLv2); PyModule_AddIntConstant(m, "OP_NO_SSLv3", SSL_OP_NO_SSLv3); PyModule_AddIntConstant(m, "OP_NO_TLSv1", SSL_OP_NO_TLSv1); +#if SSL_SUPPORT_TLSv1_2 + PyModule_AddIntConstant(m, "OP_NO_TLSv1_1", SSL_OP_NO_TLSv1_1); + PyModule_AddIntConstant(m, "OP_NO_TLSv1_2", SSL_OP_NO_TLSv1_2); +#endif PyModule_AddIntConstant(m, "OP_CIPHER_SERVER_PREFERENCE", SSL_OP_CIPHER_SERVER_PREFERENCE); PyModule_AddIntConstant(m, "OP_SINGLE_DH_USE", SSL_OP_SINGLE_DH_USE); @@ -3097,7 +3124,7 @@ } if (PyModule_AddObject(m, "lib_codes_to_names", lib_codes_to_names)) return NULL; - + /* OpenSSL version */ /* SSLeay() gives us the version of the library linked against, which could be different from the headers version. diff -r f5e3f2f0fe79 setup.py --- a/setup.py Thu Feb 07 22:17:21 2013 -0800 +++ b/setup.py Wed Feb 13 23:12:04 2013 +0100 @@ -792,10 +792,10 @@ for line in incfile: m = openssl_ver_re.match(line) if m: - openssl_ver = eval(m.group(1)) + openssl_ver = int(m.group(1), 16) + break except IOError as msg: print("IOError while reading opensshv.h:", msg) - pass #print('openssl_ver = 0x%08x' % openssl_ver) min_openssl_ver = 0x00907000