Patchwork [v4] sslutil-add tls 1.3 support - done during IETF101 Hackathon

login
register
mail settings
Submitter Codarren Velvindron
Date April 8, 2018, 5:51 a.m.
Message ID <20180408055145.GA17010@hackers.mu>
Download mbox | patch
Permalink /patch/30551/
State Superseded
Headers show

Comments

Codarren Velvindron - April 8, 2018, 5:51 a.m.
# HG changeset patch
# User Codarren Velvindron <codarren@hackers.mu>
# Date 1523166519 -14400
#      Sun Apr 08 09:48:39 2018 +0400
# Node ID 5df15ef67ce1674a8f408058cd953de5ab9601cf
# Parent  632b928992039afe96df8f99a8dec6127ff983f1
slutil: add tls 1.3 support

Done during the IETF101 Hackathon.
Yuya Nishihara - April 8, 2018, 9:25 a.m.
On Sun, 8 Apr 2018 09:51:45 +0400, Codarren Velvindron wrote:
> # HG changeset patch
> # User Codarren Velvindron <codarren@hackers.mu>
> # Date 1523166519 -14400
> #      Sun Apr 08 09:48:39 2018 +0400
> # Node ID 5df15ef67ce1674a8f408058cd953de5ab9601cf
> # Parent  632b928992039afe96df8f99a8dec6127ff983f1
> slutil: add tls 1.3 support

> -# TLS 1.1 and 1.2 may not be supported if the OpenSSL Python is compiled
> +# TLS 1.1,1.2 and 1.3 may not be supported if the OpenSSL Python is compiled
>  # against doesn't support them.
>  supportedprotocols = {'tls1.0'}
>  if util.safehasattr(ssl, 'PROTOCOL_TLSv1_1'):
>      supportedprotocols.add('tls1.1')
>  if util.safehasattr(ssl, 'PROTOCOL_TLSv1_2'):
>      supportedprotocols.add('tls1.2')
> +if util.safehasattr(ssl, 'PROTOCOL_TLS'):
> +    supportedprotocols.add('tls1.3')

PROTOCOL_TLS doesn't mean the Python supports TLS 1.3.

https://docs.python.org/2.7/library/ssl.html#ssl.PROTOCOL_TLS

Perhaps HAS_TLSv1_3 can be used instead.

  if getattr(ssl, 'HAS_TLSv1_3', False)

https://docs.python.org/2.7/library/ssl.html#ssl.HAS_TLSv1_3

> @@ -542,6 +547,10 @@
>          if 'tls1.2' not in supportedprotocols:
>              raise error.Abort(_('TLS 1.2 not supported by this Python'))
>          protocol = ssl.PROTOCOL_TLSv1_2
> +    elif exactprotocol == 'tls1.3':
> +        if 'tls1.3' not in supportedprotocols:
> +            raise error.Abort(_('TLS 1.3 not supported by this Python'))
> +        protocol = ssl.PROTOCOL_TLSv1_3

Undefined. I have no idea how to enforce the TLS 1.3 here.

Did you run tests? Since you're adding feature depending on unreleased
Python, you'll have to build Python from source.

Patch

diff -r 632b92899203 -r 5df15ef67ce1 mercurial/help/config.txt
--- a/mercurial/help/config.txt	Sat Apr 07 21:26:37 2018 +0900
+++ b/mercurial/help/config.txt	Sun Apr 08 09:48:39 2018 +0400
@@ -1163,7 +1163,7 @@ 
     By default, the highest version of TLS supported by both client and server
     is used.
 
-    Allowed values are: ``tls1.0``, ``tls1.1``, ``tls1.2``.
+    Allowed values are: ``tls1.0``, ``tls1.1``, ``tls1.2``, ``tls1.3``.
 
     When running on an old Python version, only ``tls1.0`` is allowed since
     old versions of Python only support up to TLS 1.0.
diff -r 632b92899203 -r 5df15ef67ce1 mercurial/sslutil.py
--- a/mercurial/sslutil.py	Sat Apr 07 21:26:37 2018 +0900
+++ b/mercurial/sslutil.py	Sun Apr 08 09:48:39 2018 +0400
@@ -38,17 +38,20 @@ 
     'tls1.0',
     'tls1.1',
     'tls1.2',
+    'tls1.3',
 }
 
 hassni = getattr(ssl, 'HAS_SNI', False)
 
-# TLS 1.1 and 1.2 may not be supported if the OpenSSL Python is compiled
+# TLS 1.1,1.2 and 1.3 may not be supported if the OpenSSL Python is compiled
 # against doesn't support them.
 supportedprotocols = {'tls1.0'}
 if util.safehasattr(ssl, 'PROTOCOL_TLSv1_1'):
     supportedprotocols.add('tls1.1')
 if util.safehasattr(ssl, 'PROTOCOL_TLSv1_2'):
     supportedprotocols.add('tls1.2')
+if util.safehasattr(ssl, 'PROTOCOL_TLS'):
+    supportedprotocols.add('tls1.3')
 
 try:
     # ssl.SSLContext was added in 2.7.9 and presence indicates modern
@@ -293,7 +296,7 @@ 
     # Despite its name, PROTOCOL_SSLv23 selects the highest protocol
     # that both ends support, including TLS protocols. On legacy stacks,
     # the highest it likely goes is TLS 1.0. On modern stacks, it can
-    # support TLS 1.2.
+    # support TLS 1.2 or TLS 1.3.
     #
     # The PROTOCOL_TLSv* constants select a specific TLS version
     # only (as opposed to multiple versions). So the method for
@@ -323,6 +326,8 @@ 
         options |= ssl.OP_NO_TLSv1
     elif protocol == 'tls1.2':
         options |= ssl.OP_NO_TLSv1 | ssl.OP_NO_TLSv1_1
+    elif protocol == 'tls1.3':
+        options |= ssl.OP_NO_TLSv1 | ssl.OP_NO_TLSv1_1 | ssl.OP_NO_TLSv1_2
     else:
         raise error.Abort(_('this should not happen'))
 
@@ -542,6 +547,10 @@ 
         if 'tls1.2' not in supportedprotocols:
             raise error.Abort(_('TLS 1.2 not supported by this Python'))
         protocol = ssl.PROTOCOL_TLSv1_2
+    elif exactprotocol == 'tls1.3':
+        if 'tls1.3' not in supportedprotocols:
+            raise error.Abort(_('TLS 1.3 not supported by this Python'))
+        protocol = ssl.PROTOCOL_TLSv1_3
     elif exactprotocol:
         raise error.Abort(_('invalid value for serverexactprotocol: %s') %
                           exactprotocol)
diff -r 632b92899203 -r 5df15ef67ce1 tests/hghave.py
--- a/tests/hghave.py	Sat Apr 07 21:26:37 2018 +0900
+++ b/tests/hghave.py	Sun Apr 08 09:48:39 2018 +0400
@@ -523,6 +523,11 @@ 
     from mercurial import sslutil
     return 'tls1.2' in sslutil.supportedprotocols
 
+@check("tls1.3", "TLS 1.3 protocol support")
+def has_tls1_3():
+    from mercurial import sslutil
+    return 'tls1.3' in sslutil.supportedprotocols
+
 @check("windows", "Windows")
 def has_windows():
     return os.name == 'nt'
diff -r 632b92899203 -r 5df15ef67ce1 tests/test-https.t
--- a/tests/test-https.t	Sat Apr 07 21:26:37 2018 +0900
+++ b/tests/test-https.t	Sun Apr 08 09:48:39 2018 +0400
@@ -536,6 +536,61 @@ 
   $ killdaemons.py hg2.pid
 #endif
 
+#if sslcontext tls1.3
+Start servers running supported TLS versions
+
+  $ cd test
+  $ hg serve -p $HGPORT -d --pid-file=../hg0.pid --certificate=$PRIV \
+  > --config devel.serverexactprotocol=tls1.0
+  $ cat ../hg0.pid >> $DAEMON_PIDS
+  $ hg serve -p $HGPORT1 -d --pid-file=../hg1.pid --certificate=$PRIV \
+  > --config devel.serverexactprotocol=tls1.3
+  $ cat ../hg1.pid >> $DAEMON_PIDS
+  $ cd ..
+
+Clients talking same TLS versions work
+
+  $ P="$CERTSDIR" hg --config hostsecurity.minimumprotocol=tls1.0 id https://localhost:$HGPORT/
+  5fed3813f7f5
+  $ P="$CERTSDIR" hg --config hostsecurity.minimumprotocol=tls1.3 id https://localhost:$HGPORT1/
+  5fed3813f7f5
+
+Clients requiring newer TLS version than what server supports fail
+
+  $ P="$CERTSDIR" hg --config hostsecurity.minimumprotocol=tls1.3 id https://localhost:$HGPORT/
+  (could not negotiate a common security protocol (tls1.3+) with localhost; the likely cause is Mercurial is configured to be more secure than the server can support)
+  (consider contacting the operator of this server and ask them to support modern TLS protocol versions; or, set hostsecurity.localhost:minimumprotocol=tls1.0 to allow use of legacy, less secure protocols when communicating with this server)
+  (see https://mercurial-scm.org/wiki/SecureConnections for more info)
+  abort: error: *unsupported protocol* (glob)
+  [255]
+
+--insecure will allow TLS 1.0 connections and override configs
+
+  $ hg --config hostsecurity.minimumprotocol=tls1.3 id --insecure https://localhost:$HGPORT/
+  warning: connection security to localhost is disabled per current settings; communication is susceptible to eavesdropping and tampering
+  5fed3813f7f5
+
+The per-host config option overrides the default
+
+  $ P="$CERTSDIR" hg id https://localhost:$HGPORT/ \
+  > --config hostsecurity.minimumprotocol=tls1.3 \
+  > --config hostsecurity.localhost:minimumprotocol=tls1.0
+  5fed3813f7f5
+
+The per-host config option by itself works
+
+  $ P="$CERTSDIR" hg id https://localhost:$HGPORT/ \
+  > --config hostsecurity.localhost:minimumprotocol=tls1.3
+  (could not negotiate a common security protocol (tls1.3+) with localhost; the likely cause is Mercurial is configured to be more secure than the server can support)
+  (consider contacting the operator of this server and ask them to support modern TLS protocol versions; or, set hostsecurity.localhost:minimumprotocol=tls1.0 to allow use of legacy, less secure protocols when communicating with this server)
+  (see https://mercurial-scm.org/wiki/SecureConnections for more info)
+  abort: error: *unsupported protocol* (glob)
+  [255]
+
+  $ killdaemons.py hg0.pid
+  $ killdaemons.py hg1.pid
+#endif
+
 Prepare for connecting through proxy
 
   $ hg serve -R test -p $HGPORT -d --pid-file=hg0.pid --certificate=$PRIV