[Python-checkins] cpython: Issue #16692: The ssl module now supports TLS 1.1 and TLS 1.2. Initial patch

antoine.pitrou python-checkins at python.org
Thu Mar 28 22:29:15 CET 2013


http://hg.python.org/cpython/rev/02a89bd646ca
changeset:   82992:02a89bd646ca
parent:      82990:ac0b1603a29f
user:        Antoine Pitrou <solipsis at pitrou.net>
date:        Thu Mar 28 22:24:43 2013 +0100
summary:
  Issue #16692: The ssl module now supports TLS 1.1 and TLS 1.2.  Initial patch by Michele Orrù.

files:
  Doc/library/ssl.rst  |   55 +++++++++++--
  Doc/whatsnew/3.4.rst |    1 +
  Lib/ssl.py           |   13 ++-
  Lib/test/test_ssl.py |   73 +++++++++++++-----
  Misc/NEWS            |    5 +
  Modules/_ssl.c       |  124 +++++++++++++++++++-----------
  setup.py             |    4 +-
  7 files changed, 194 insertions(+), 81 deletions(-)


diff --git a/Doc/library/ssl.rst b/Doc/library/ssl.rst
--- 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
--- 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
--- 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
--- 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
--- 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
--- 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,56 +128,15 @@
 #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
+#endif
 };
 
-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
 
 /* serves as a flag to see whether we've initialized the SSL thread support. */
@@ -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
--- 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

-- 
Repository URL: http://hg.python.org/cpython


More information about the Python-checkins mailing list