Issue #16692: The ssl module now supports TLS 1.1 and TLS 1.2. Initial patch by Michele OrrĂ¹.
diff --git a/Doc/library/ssl.rst b/Doc/library/ssl.rst
index 02a626a..aa0b393 100644
--- a/Doc/library/ssl.rst
+++ b/Doc/library/ssl.rst
@@ -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,9 +404,25 @@
.. 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
+
+
+ Selects TLS version 1.1 as the channel encryption protocol.
+ Available only with openssl version 1.0.1+.
+
+ .. versionadded:: 3.4
+
+.. data:: PROTOCOL_TLSv1_2
+
+
+ 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.
+ Available only with openssl version 1.0.1+.
+
+ .. versionadded:: 3.4
.. data:: OP_ALL
@@ -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:`PROTOCOL_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:`PROTOCOL_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 --git a/Doc/whatsnew/3.4.rst b/Doc/whatsnew/3.4.rst
index d2b826f..26dc2fa 100644
--- a/Doc/whatsnew/3.4.rst
+++ b/Doc/whatsnew/3.4.rst
@@ -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 --git a/Lib/ssl.py b/Lib/ssl.py
index c8311bc..021ae35 100644
--- a/Lib/ssl.py
+++ b/Lib/ssl.py
@@ -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 --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py
index 8735832..6318360 100644
--- a/Lib/test/test_ssl.py
+++ b/Lib/test/test_ssl.py
@@ -20,13 +20,7 @@
ssl = support.import_module("ssl")
-PROTOCOLS = [
- ssl.PROTOCOL_SSLv3,
- ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_TLSv1
-]
-if hasattr(ssl, 'PROTOCOL_SSLv2'):
- PROTOCOLS.append(ssl.PROTOCOL_SSLv2)
-
+PROTOCOLS = sorted(ssl._PROTOCOL_NAMES)
HOST = support.HOST
data_file = lambda name: os.path.join(os.path.dirname(__file__), name)
@@ -101,10 +95,6 @@
class BasicSocketTests(unittest.TestCase):
def test_constants(self):
- #ssl.PROTOCOL_SSLv2
- ssl.PROTOCOL_SSLv23
- ssl.PROTOCOL_SSLv3
- ssl.PROTOCOL_TLSv1
ssl.CERT_NONE
ssl.CERT_OPTIONAL
ssl.CERT_REQUIRED
@@ -396,11 +386,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)
@@ -1360,12 +1347,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 client, otherwise an
+ # SSLv23 client will send an SSLv3 hello (rather than SSLv2)
+ # starting from OpenSSL 1.0.0 (see issue #8322).
+ if client_context.protocol == ssl.PROTOCOL_SSLv23:
+ client_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:
@@ -1581,6 +1571,49 @@
try_protocol_combo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_SSLv23, False,
client_options=ssl.OP_NO_TLSv1)
+ @skip_if_broken_ubuntu_ssl
+ @unittest.skipUnless(hasattr(ssl, "PROTOCOL_TLSv1_1"),
+ "TLS version 1.1 not supported.")
+ 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_SSLv23, ssl.PROTOCOL_TLSv1_1, True)
+ try_protocol_combo(ssl.PROTOCOL_TLSv1_1, ssl.PROTOCOL_TLSv1, False)
+ try_protocol_combo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_TLSv1_1, False)
+
+
+ @skip_if_broken_ubuntu_ssl
+ @unittest.skipUnless(hasattr(ssl, "PROTOCOL_TLSv1_2"),
+ "TLS version 1.2 not supported.")
+ 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_SSLv23, ssl.PROTOCOL_TLSv1_2, True)
+ 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 --git a/Misc/NEWS b/Misc/NEWS
index 55080c2..de80e5f 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -297,6 +297,9 @@
Library
-------
+- Issue #16692: The ssl module now supports TLS 1.1 and TLS 1.2. Initial
+ patch by Michele Orrù.
+
- Issue #17025: multiprocessing: Reduce Queue and SimpleQueue contention.
- Issue #17536: Add to webbrowser's browser list: www-browser, x-www-browser,
@@ -1005,6 +1008,8 @@
- ctypes.call_commethod was removed, since its only usage was in the defunct
samples directory.
+- Issue #16692: Added TLSv1.1 and TLSv1.2 support for the ssl modules.
+
Extension Modules
-----------------
diff --git a/Modules/_ssl.c b/Modules/_ssl.c
index 268ae93..88525c8 100644
--- a/Modules/_ssl.c
+++ b/Modules/_ssl.c
@@ -40,6 +40,61 @@
#endif
+/* Include symbols from _socket module */
+#include "socketmodule.h"
+
+static PySocketModule_APIObject PySocketModule;
+
+#if defined(HAVE_POLL_H)
+#include <poll.h>
+#elif defined(HAVE_SYS_POLL_H)
+#include <sys/poll.h>
+#endif
+
+/* Include OpenSSL header files */
+#include "openssl/rsa.h"
+#include "openssl/crypto.h"
+#include "openssl/x509.h"
+#include "openssl/x509v3.h"
+#include "openssl/pem.h"
+#include "openssl/ssl.h"
+#include "openssl/err.h"
+#include "openssl/rand.h"
+
+/* SSL error object */
+static PyObject *PySSLErrorObject;
+static PyObject *PySSLZeroReturnErrorObject;
+static PyObject *PySSLWantReadErrorObject;
+static PyObject *PySSLWantWriteErrorObject;
+static PyObject *PySSLSyscallErrorObject;
+static PyObject *PySSLEOFErrorObject;
+
+/* Error mappings */
+static PyObject *err_codes_to_names;
+static PyObject *err_names_to_codes;
+static PyObject *lib_codes_to_names;
+
+struct py_ssl_error_code {
+ const char *mnemonic;
+ int library, reason;
+};
+struct py_ssl_library_code {
+ const char *library;
+ int code;
+};
+
+/* Include generated data (error codes) */
+#include "_ssl_data.h"
+
+/* Openssl comes with TLSv1.1 and TLSv1.2 between 1.0.0h and 1.0.1
+ http://www.openssl.org/news/changelog.html
+ */
+#if OPENSSL_VERSION_NUMBER >= 0x10001000L
+# define HAVE_TLSv1_2 1
+#else
+# define HAVE_TLSv1_2 0
+#endif
+
enum py_ssl_error {
/* these mirror ssl.h */
PY_SSL_ERROR_NONE,
@@ -73,55 +128,14 @@
#endif
PY_SSL_VERSION_SSL3=1,
PY_SSL_VERSION_SSL23,
+#if HAVE_TLSv1_2
+ PY_SSL_VERSION_TLS1,
+ PY_SSL_VERSION_TLS1_1,
+ PY_SSL_VERSION_TLS1_2
+#else
PY_SSL_VERSION_TLS1
-};
-
-struct py_ssl_error_code {
- const char *mnemonic;
- int library, reason;
-};
-
-struct py_ssl_library_code {
- const char *library;
- int code;
-};
-
-/* Include symbols from _socket module */
-#include "socketmodule.h"
-
-static PySocketModule_APIObject PySocketModule;
-
-#if defined(HAVE_POLL_H)
-#include <poll.h>
-#elif defined(HAVE_SYS_POLL_H)
-#include <sys/poll.h>
#endif
-
-/* Include OpenSSL header files */
-#include "openssl/rsa.h"
-#include "openssl/crypto.h"
-#include "openssl/x509.h"
-#include "openssl/x509v3.h"
-#include "openssl/pem.h"
-#include "openssl/ssl.h"
-#include "openssl/err.h"
-#include "openssl/rand.h"
-
-/* Include generated data (error codes) */
-#include "_ssl_data.h"
-
-/* SSL error object */
-static PyObject *PySSLErrorObject;
-static PyObject *PySSLZeroReturnErrorObject;
-static PyObject *PySSLWantReadErrorObject;
-static PyObject *PySSLWantWriteErrorObject;
-static PyObject *PySSLSyscallErrorObject;
-static PyObject *PySSLEOFErrorObject;
-
-/* Error mappings */
-static PyObject *err_codes_to_names;
-static PyObject *err_names_to_codes;
-static PyObject *lib_codes_to_names;
+};
#ifdef WITH_THREAD
@@ -1732,6 +1746,12 @@
PySSL_BEGIN_ALLOW_THREADS
if (proto_version == PY_SSL_VERSION_TLS1)
ctx = SSL_CTX_new(TLSv1_method());
+#if HAVE_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
@@ -3004,6 +3024,12 @@
PY_SSL_VERSION_SSL23);
PyModule_AddIntConstant(m, "PROTOCOL_TLSv1",
PY_SSL_VERSION_TLS1);
+#if HAVE_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 +3037,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 HAVE_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);
diff --git a/setup.py b/setup.py
index 020772b..e6f3b53 100644
--- a/setup.py
+++ b/setup.py
@@ -786,10 +786,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