Port SpawnedTestServer to Python 3

Porting tlslite was more straightforward than it seemed, probably
because they already internally used bytearray everywhere, rather
than str.

One complication is getting a copy of Python 3 onto one of the
CrOS builders. It runs out of space if we ship both Python 2
and Python 3, so I've switched it to only Python 3. This requires
disabling one test, but that test only exists to test Python 2
works.
https://groups.google.com/a/chromium.org/g/python/c/KK60bQcwoCw/m/VctNoDYHCAAJ

We probably should remove GetPythonCommand from //net, since we
don't use it anymore. There's some code in //chrome/browser/media.
But that will take a bit more work to disentangle, so leave it
alone for this CL.

Bug: 1248530
Change-Id: I16b6d077648a436f0f2b42194b19d3104d198614
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3156548
Reviewed-by: Ryan Sleevi <rsleevi@chromium.org>
Reviewed-by: Amr Aboelkher <amraboelkher@chromium.org>
Reviewed-by: Dirk Pranke <dpranke@google.com>
Reviewed-by: Pavol Marko <pmarko@chromium.org>
Reviewed-by: Ben Pastene <bpastene@chromium.org>
Commit-Queue: David Benjamin <davidben@chromium.org>
Cr-Commit-Position: refs/heads/main@{#922697}
NOKEYCHECK=True
GitOrigin-RevId: 5af020cfe8e17f1d24ce85ee6585d79df0664c6f
diff --git a/README.chromium b/README.chromium
index b189e12..1cd7d32 100644
--- a/README.chromium
+++ b/README.chromium
@@ -64,3 +64,4 @@
   inspired by tlslite-ng implementation.
 - patches/signature_algorithms.patch: Add basic signature algorithms
   negotiation.
+- patches/python3.patch: Fix with Python 3.
diff --git a/patches/python3.patch b/patches/python3.patch
new file mode 100644
index 0000000..12cffa1
--- /dev/null
+++ b/patches/python3.patch
@@ -0,0 +1,272 @@
+diff --git a/third_party/tlslite/tlslite/integration/asyncstatemachine.py b/third_party/tlslite/tlslite/integration/asyncstatemachine.py
+index 50a6f4a355c14..9faf84f966a31 100644
+--- a/third_party/tlslite/tlslite/integration/asyncstatemachine.py
++++ b/third_party/tlslite/tlslite/integration/asyncstatemachine.py
+@@ -192,7 +192,7 @@ class AsyncStateMachine:
+         @type handshaker: generator
+         @param handshaker: A generator created by using one of the
+         asynchronous handshake functions (i.e. handshakeServerAsync, or
+-        handshakeClientxxx(..., async=True).
++        handshakeClientxxx(..., is_async=True).
+         """
+         try:
+             self._checkAssert(0)
+diff --git a/third_party/tlslite/tlslite/messages.py b/third_party/tlslite/tlslite/messages.py
+index ce4f1a6e15eb2..5df50d34cd3ad 100644
+--- a/third_party/tlslite/tlslite/messages.py
++++ b/third_party/tlslite/tlslite/messages.py
+@@ -9,6 +9,8 @@
+ 
+ """Classes representing TLS messages."""
+ 
++from __future__ import division
++
+ from .utils.compat import *
+ from .utils.cryptomath import *
+ from .errors import *
+@@ -262,7 +264,7 @@ class ClientHello(HandshakeMsg):
+                         numBytes = p.get(2)
+                         if numBytes + 2 != extLength or numBytes % 2 != 0:
+                             raise SyntaxError()
+-                        for _ in range(numBytes / 2):
++                        for _ in range(numBytes // 2):
+                             hashAlg = p.get(1)
+                             sigAlg = p.get(1)
+                             self.signature_algorithms.append((hashAlg, sigAlg))
+diff --git a/third_party/tlslite/tlslite/tlsconnection.py b/third_party/tlslite/tlslite/tlsconnection.py
+index 6d7c859884a41..f64c848a511c0 100644
+--- a/third_party/tlslite/tlslite/tlsconnection.py
++++ b/third_party/tlslite/tlslite/tlsconnection.py
+@@ -202,7 +202,7 @@ class TLSConnection(TLSRecordLayer):
+ 
+     def handshakeClientAnonymous(self, session=None, settings=None, 
+                                 checker=None, serverName="",
+-                                async=False):
++                                is_async=False):
+         """Perform an anonymous handshake in the role of client.
+ 
+         This function performs an SSL or TLS handshake using an
+@@ -236,8 +236,8 @@ class TLSConnection(TLSRecordLayer):
+         @type serverName: string
+         @param serverName: The ServerNameIndication TLS Extension.
+ 
+-        @type async: bool
+-        @param async: If False, this function will block until the
++        @type is_async: bool
++        @param is_async: If False, this function will block until the
+         handshake is completed.  If True, this function will return a
+         generator.  Successive invocations of the generator will
+         return 0 if it is waiting to read from the socket, 1 if it is
+@@ -245,7 +245,7 @@ class TLSConnection(TLSRecordLayer):
+         the handshake operation is completed.
+ 
+         @rtype: None or an iterable
+-        @return: If 'async' is True, a generator object will be
++        @return: If 'is_async' is True, a generator object will be
+         returned.
+ 
+         @raise socket.error: If a socket error occurs.
+@@ -260,7 +260,7 @@ class TLSConnection(TLSRecordLayer):
+                                                 settings=settings,
+                                                 checker=checker,
+                                                 serverName=serverName)
+-        if async:
++        if is_async:
+             return handshaker
+         for result in handshaker:
+             pass
+@@ -268,7 +268,7 @@ class TLSConnection(TLSRecordLayer):
+     def handshakeClientSRP(self, username, password, session=None,
+                            settings=None, checker=None, 
+                            reqTack=True, serverName="",
+-                           async=False):
++                           is_async=False):
+         """Perform an SRP handshake in the role of client.
+ 
+         This function performs a TLS/SRP handshake.  SRP mutually
+@@ -313,8 +313,8 @@ class TLSConnection(TLSRecordLayer):
+         @type serverName: string
+         @param serverName: The ServerNameIndication TLS Extension.
+ 
+-        @type async: bool
+-        @param async: If False, this function will block until the
++        @type is_async: bool
++        @param is_async: If False, this function will block until the
+         handshake is completed.  If True, this function will return a
+         generator.  Successive invocations of the generator will
+         return 0 if it is waiting to read from the socket, 1 if it is
+@@ -322,7 +322,7 @@ class TLSConnection(TLSRecordLayer):
+         the handshake operation is completed.
+ 
+         @rtype: None or an iterable
+-        @return: If 'async' is True, a generator object will be
++        @return: If 'is_async' is True, a generator object will be
+         returned.
+ 
+         @raise socket.error: If a socket error occurs.
+@@ -340,9 +340,9 @@ class TLSConnection(TLSRecordLayer):
+         # fashion, returning 1 when it is waiting to able to write, 0 when
+         # it is waiting to read.
+         #
+-        # If 'async' is True, the generator is returned to the caller, 
+-        # otherwise it is executed to completion here.  
+-        if async:
++        # If 'is_async' is True, the generator is returned to the caller,
++        # otherwise it is executed to completion here.
++        if is_async:
+             return handshaker
+         for result in handshaker:
+             pass
+@@ -350,7 +350,7 @@ class TLSConnection(TLSRecordLayer):
+     def handshakeClientCert(self, certChain=None, privateKey=None,
+                             session=None, settings=None, checker=None,
+                             nextProtos=None, reqTack=True, serverName="",
+-                            async=False):
++                            is_async=False):
+         """Perform a certificate-based handshake in the role of client.
+ 
+         This function performs an SSL or TLS handshake.  The server
+@@ -407,8 +407,8 @@ class TLSConnection(TLSRecordLayer):
+         @type serverName: string
+         @param serverName: The ServerNameIndication TLS Extension.
+ 
+-        @type async: bool
+-        @param async: If False, this function will block until the
++        @type is_async: bool
++        @param is_async: If False, this function will block until the
+         handshake is completed.  If True, this function will return a
+         generator.  Successive invocations of the generator will
+         return 0 if it is waiting to read from the socket, 1 if it is
+@@ -416,7 +416,7 @@ class TLSConnection(TLSRecordLayer):
+         the handshake operation is completed.
+ 
+         @rtype: None or an iterable
+-        @return: If 'async' is True, a generator object will be
++        @return: If 'is_async' is True, a generator object will be
+         returned.
+ 
+         @raise socket.error: If a socket error occurs.
+@@ -435,9 +435,9 @@ class TLSConnection(TLSRecordLayer):
+         # fashion, returning 1 when it is waiting to able to write, 0 when
+         # it is waiting to read.
+         #
+-        # If 'async' is True, the generator is returned to the caller, 
+-        # otherwise it is executed to completion here.                        
+-        if async:
++        # If 'is_async' is True, the generator is returned to the caller,
++        # otherwise it is executed to completion here.
++        if is_async:
+             return handshaker
+         for result in handshaker:
+             pass
+@@ -1368,10 +1368,10 @@ class TLSConnection(TLSRecordLayer):
+         # See https://tools.ietf.org/html/rfc8446#section-4.1.3
+         if settings.simulateTLS13Downgrade:
+             serverRandom = serverRandom[:24] + \
+-                bytearray("\x44\x4f\x57\x4e\x47\x52\x44\x01")
++                bytearray(b"\x44\x4f\x57\x4e\x47\x52\x44\x01")
+         elif settings.simulateTLS12Downgrade:
+             serverRandom = serverRandom[:24] + \
+-                bytearray("\x44\x4f\x57\x4e\x47\x52\x44\x00")
++                bytearray(b"\x44\x4f\x57\x4e\x47\x52\x44\x00")
+         serverHello = ServerHello()
+         serverHello.create(self.version, serverRandom, sessionID, \
+                             cipherSuite, CertificateType.x509, tackExt,
+diff --git a/third_party/tlslite/tlslite/utils/aesgcm.py b/third_party/tlslite/tlslite/utils/aesgcm.py
+index 7319c268536a3..c887f2f518e77 100644
+--- a/third_party/tlslite/tlslite/utils/aesgcm.py
++++ b/third_party/tlslite/tlslite/utils/aesgcm.py
+@@ -13,6 +13,8 @@
+ # x^127 term. This bit reversal also applies to polynomials used as indices in a
+ # look-up table.
+ 
++from __future__ import division
++
+ from .cryptomath import bytesToNumber, numberToByteArray
+ 
+ class AESGCM(object):
+@@ -47,7 +49,7 @@ class AESGCM(object):
+         self._productTable[_reverseBits(1)] = h
+         for i in range(2, 16, 2):
+             self._productTable[_reverseBits(i)] = \
+-                _gcmShift(self._productTable[_reverseBits(i/2)])
++                _gcmShift(self._productTable[_reverseBits(i//2)])
+             self._productTable[_reverseBits(i+1)] = \
+                 _gcmAdd(self._productTable[_reverseBits(i)], h)
+ 
+diff --git a/third_party/tlslite/tlslite/utils/p256.py b/third_party/tlslite/tlslite/utils/p256.py
+index 6eb9a7799accc..45159000fa499 100644
+--- a/third_party/tlslite/tlslite/utils/p256.py
++++ b/third_party/tlslite/tlslite/utils/p256.py
+@@ -2,6 +2,7 @@
+ # See the LICENSE file for legal information regarding use of this file.
+ 
+ import os
++import six
+ 
+ p = (
+     115792089210356248762697446949407573530086143415290314195533631308867097853951)
+@@ -90,25 +91,27 @@ def _scalarBaseMult(k):
+ 
+ 
+ def _decodeBigEndian(b):
+-    return sum([ord(b[len(b) - i - 1]) << 8 * i for i in range(len(b))])
++    # TODO(davidben): Replace with int.from_bytes when removing Python 2.
++    return sum([six.indexbytes(b, len(b) - i - 1) << 8 * i
++                for i in range(len(b))])
+ 
+ 
+ def _encodeBigEndian(n):
+-    b = []
++    b = bytearray()
+     while n != 0:
+-        b.append(chr(n & 0xff))
++        b.append(n & 0xff)
+         n >>= 8
+ 
+     if len(b) == 0:
+         b.append(0)
+     b.reverse()
+ 
+-    return "".join(b)
++    return bytes(b)
+ 
+ 
+ def _zeroPad(b, length):
+     if len(b) < length:
+-        return ("\x00" * (length - len(b))) + b
++        return (b"\x00" * (length - len(b))) + b
+     return b
+ 
+ 
+@@ -117,12 +120,12 @@ def _encodePoint(point):
+     y = point[1]
+     if (y * y) % p != (x * x * x - 3 * x + p256B) % p:
+         raise "point not on curve"
+-    return "\x04" + _zeroPad(_encodeBigEndian(point[0]), 32) + _zeroPad(
++    return b"\x04" + _zeroPad(_encodeBigEndian(point[0]), 32) + _zeroPad(
+         _encodeBigEndian(point[1]), 32)
+ 
+ 
+ def _decodePoint(b):
+-    if len(b) != 1 + 32 + 32 or ord(b[0]) != 4:
++    if len(b) != 1 + 32 + 32 or six.indexbytes(b, 0) != 4:
+         raise "invalid encoded ec point"
+     x = _decodeBigEndian(b[1:33])
+     y = _decodeBigEndian(b[33:65])
+diff --git a/third_party/tlslite/tlslite/utils/pycrypto_rsakey.py b/third_party/tlslite/tlslite/utils/pycrypto_rsakey.py
+index d76ea2d1b13a6..453785664565c 100644
+--- a/third_party/tlslite/tlslite/utils/pycrypto_rsakey.py
++++ b/third_party/tlslite/tlslite/utils/pycrypto_rsakey.py
+@@ -15,9 +15,9 @@ if pycryptoLoaded:
+     class PyCrypto_RSAKey(RSAKey):
+         def __init__(self, n=0, e=0, d=0, p=0, q=0, dP=0, dQ=0, qInv=0):
+             if not d:
+-                self.rsa = RSA.construct( (long(n), long(e)) )
++                self.rsa = RSA.construct( (n, e) )
+             else:
+-                self.rsa = RSA.construct( (long(n), long(e), long(d), long(p), long(q)) )
++                self.rsa = RSA.construct( (n, e, d, p, q) )
+ 
+         def __getattr__(self, name):
+             return getattr(self.rsa, name)
diff --git a/tlslite/integration/asyncstatemachine.py b/tlslite/integration/asyncstatemachine.py
index 50a6f4a..9faf84f 100644
--- a/tlslite/integration/asyncstatemachine.py
+++ b/tlslite/integration/asyncstatemachine.py
@@ -192,7 +192,7 @@
         @type handshaker: generator
         @param handshaker: A generator created by using one of the
         asynchronous handshake functions (i.e. handshakeServerAsync, or
-        handshakeClientxxx(..., async=True).
+        handshakeClientxxx(..., is_async=True).
         """
         try:
             self._checkAssert(0)
diff --git a/tlslite/messages.py b/tlslite/messages.py
index ce4f1a6..5df50d3 100644
--- a/tlslite/messages.py
+++ b/tlslite/messages.py
@@ -9,6 +9,8 @@
 
 """Classes representing TLS messages."""
 
+from __future__ import division
+
 from .utils.compat import *
 from .utils.cryptomath import *
 from .errors import *
@@ -262,7 +264,7 @@
                         numBytes = p.get(2)
                         if numBytes + 2 != extLength or numBytes % 2 != 0:
                             raise SyntaxError()
-                        for _ in range(numBytes / 2):
+                        for _ in range(numBytes // 2):
                             hashAlg = p.get(1)
                             sigAlg = p.get(1)
                             self.signature_algorithms.append((hashAlg, sigAlg))
diff --git a/tlslite/tlsconnection.py b/tlslite/tlsconnection.py
index 6d7c859..f64c848 100644
--- a/tlslite/tlsconnection.py
+++ b/tlslite/tlsconnection.py
@@ -202,7 +202,7 @@
 
     def handshakeClientAnonymous(self, session=None, settings=None, 
                                 checker=None, serverName="",
-                                async=False):
+                                is_async=False):
         """Perform an anonymous handshake in the role of client.
 
         This function performs an SSL or TLS handshake using an
@@ -236,8 +236,8 @@
         @type serverName: string
         @param serverName: The ServerNameIndication TLS Extension.
 
-        @type async: bool
-        @param async: If False, this function will block until the
+        @type is_async: bool
+        @param is_async: If False, this function will block until the
         handshake is completed.  If True, this function will return a
         generator.  Successive invocations of the generator will
         return 0 if it is waiting to read from the socket, 1 if it is
@@ -245,7 +245,7 @@
         the handshake operation is completed.
 
         @rtype: None or an iterable
-        @return: If 'async' is True, a generator object will be
+        @return: If 'is_async' is True, a generator object will be
         returned.
 
         @raise socket.error: If a socket error occurs.
@@ -260,7 +260,7 @@
                                                 settings=settings,
                                                 checker=checker,
                                                 serverName=serverName)
-        if async:
+        if is_async:
             return handshaker
         for result in handshaker:
             pass
@@ -268,7 +268,7 @@
     def handshakeClientSRP(self, username, password, session=None,
                            settings=None, checker=None, 
                            reqTack=True, serverName="",
-                           async=False):
+                           is_async=False):
         """Perform an SRP handshake in the role of client.
 
         This function performs a TLS/SRP handshake.  SRP mutually
@@ -313,8 +313,8 @@
         @type serverName: string
         @param serverName: The ServerNameIndication TLS Extension.
 
-        @type async: bool
-        @param async: If False, this function will block until the
+        @type is_async: bool
+        @param is_async: If False, this function will block until the
         handshake is completed.  If True, this function will return a
         generator.  Successive invocations of the generator will
         return 0 if it is waiting to read from the socket, 1 if it is
@@ -322,7 +322,7 @@
         the handshake operation is completed.
 
         @rtype: None or an iterable
-        @return: If 'async' is True, a generator object will be
+        @return: If 'is_async' is True, a generator object will be
         returned.
 
         @raise socket.error: If a socket error occurs.
@@ -340,9 +340,9 @@
         # fashion, returning 1 when it is waiting to able to write, 0 when
         # it is waiting to read.
         #
-        # If 'async' is True, the generator is returned to the caller, 
-        # otherwise it is executed to completion here.  
-        if async:
+        # If 'is_async' is True, the generator is returned to the caller,
+        # otherwise it is executed to completion here.
+        if is_async:
             return handshaker
         for result in handshaker:
             pass
@@ -350,7 +350,7 @@
     def handshakeClientCert(self, certChain=None, privateKey=None,
                             session=None, settings=None, checker=None,
                             nextProtos=None, reqTack=True, serverName="",
-                            async=False):
+                            is_async=False):
         """Perform a certificate-based handshake in the role of client.
 
         This function performs an SSL or TLS handshake.  The server
@@ -407,8 +407,8 @@
         @type serverName: string
         @param serverName: The ServerNameIndication TLS Extension.
 
-        @type async: bool
-        @param async: If False, this function will block until the
+        @type is_async: bool
+        @param is_async: If False, this function will block until the
         handshake is completed.  If True, this function will return a
         generator.  Successive invocations of the generator will
         return 0 if it is waiting to read from the socket, 1 if it is
@@ -416,7 +416,7 @@
         the handshake operation is completed.
 
         @rtype: None or an iterable
-        @return: If 'async' is True, a generator object will be
+        @return: If 'is_async' is True, a generator object will be
         returned.
 
         @raise socket.error: If a socket error occurs.
@@ -435,9 +435,9 @@
         # fashion, returning 1 when it is waiting to able to write, 0 when
         # it is waiting to read.
         #
-        # If 'async' is True, the generator is returned to the caller, 
-        # otherwise it is executed to completion here.                        
-        if async:
+        # If 'is_async' is True, the generator is returned to the caller,
+        # otherwise it is executed to completion here.
+        if is_async:
             return handshaker
         for result in handshaker:
             pass
@@ -1368,10 +1368,10 @@
         # See https://tools.ietf.org/html/rfc8446#section-4.1.3
         if settings.simulateTLS13Downgrade:
             serverRandom = serverRandom[:24] + \
-                bytearray("\x44\x4f\x57\x4e\x47\x52\x44\x01")
+                bytearray(b"\x44\x4f\x57\x4e\x47\x52\x44\x01")
         elif settings.simulateTLS12Downgrade:
             serverRandom = serverRandom[:24] + \
-                bytearray("\x44\x4f\x57\x4e\x47\x52\x44\x00")
+                bytearray(b"\x44\x4f\x57\x4e\x47\x52\x44\x00")
         serverHello = ServerHello()
         serverHello.create(self.version, serverRandom, sessionID, \
                             cipherSuite, CertificateType.x509, tackExt,
diff --git a/tlslite/utils/aesgcm.py b/tlslite/utils/aesgcm.py
index 7319c26..c887f2f 100644
--- a/tlslite/utils/aesgcm.py
+++ b/tlslite/utils/aesgcm.py
@@ -13,6 +13,8 @@
 # x^127 term. This bit reversal also applies to polynomials used as indices in a
 # look-up table.
 
+from __future__ import division
+
 from .cryptomath import bytesToNumber, numberToByteArray
 
 class AESGCM(object):
@@ -47,7 +49,7 @@
         self._productTable[_reverseBits(1)] = h
         for i in range(2, 16, 2):
             self._productTable[_reverseBits(i)] = \
-                _gcmShift(self._productTable[_reverseBits(i/2)])
+                _gcmShift(self._productTable[_reverseBits(i//2)])
             self._productTable[_reverseBits(i+1)] = \
                 _gcmAdd(self._productTable[_reverseBits(i)], h)
 
diff --git a/tlslite/utils/p256.py b/tlslite/utils/p256.py
index 6eb9a77..4515900 100644
--- a/tlslite/utils/p256.py
+++ b/tlslite/utils/p256.py
@@ -2,6 +2,7 @@
 # See the LICENSE file for legal information regarding use of this file.
 
 import os
+import six
 
 p = (
     115792089210356248762697446949407573530086143415290314195533631308867097853951)
@@ -90,25 +91,27 @@
 
 
 def _decodeBigEndian(b):
-    return sum([ord(b[len(b) - i - 1]) << 8 * i for i in range(len(b))])
+    # TODO(davidben): Replace with int.from_bytes when removing Python 2.
+    return sum([six.indexbytes(b, len(b) - i - 1) << 8 * i
+                for i in range(len(b))])
 
 
 def _encodeBigEndian(n):
-    b = []
+    b = bytearray()
     while n != 0:
-        b.append(chr(n & 0xff))
+        b.append(n & 0xff)
         n >>= 8
 
     if len(b) == 0:
         b.append(0)
     b.reverse()
 
-    return "".join(b)
+    return bytes(b)
 
 
 def _zeroPad(b, length):
     if len(b) < length:
-        return ("\x00" * (length - len(b))) + b
+        return (b"\x00" * (length - len(b))) + b
     return b
 
 
@@ -117,12 +120,12 @@
     y = point[1]
     if (y * y) % p != (x * x * x - 3 * x + p256B) % p:
         raise "point not on curve"
-    return "\x04" + _zeroPad(_encodeBigEndian(point[0]), 32) + _zeroPad(
+    return b"\x04" + _zeroPad(_encodeBigEndian(point[0]), 32) + _zeroPad(
         _encodeBigEndian(point[1]), 32)
 
 
 def _decodePoint(b):
-    if len(b) != 1 + 32 + 32 or ord(b[0]) != 4:
+    if len(b) != 1 + 32 + 32 or six.indexbytes(b, 0) != 4:
         raise "invalid encoded ec point"
     x = _decodeBigEndian(b[1:33])
     y = _decodeBigEndian(b[33:65])
diff --git a/tlslite/utils/pycrypto_rsakey.py b/tlslite/utils/pycrypto_rsakey.py
index d76ea2d..4537856 100644
--- a/tlslite/utils/pycrypto_rsakey.py
+++ b/tlslite/utils/pycrypto_rsakey.py
@@ -15,9 +15,9 @@
     class PyCrypto_RSAKey(RSAKey):
         def __init__(self, n=0, e=0, d=0, p=0, q=0, dP=0, dQ=0, qInv=0):
             if not d:
-                self.rsa = RSA.construct( (long(n), long(e)) )
+                self.rsa = RSA.construct( (n, e) )
             else:
-                self.rsa = RSA.construct( (long(n), long(e), long(d), long(p), long(q)) )
+                self.rsa = RSA.construct( (n, e, d, p, q) )
 
         def __getattr__(self, name):
             return getattr(self.rsa, name)