Skip to content

Commit c49f63c

Browse files
stratakistiran
authored andcommitted
[2.7] bpo-33570: TLS 1.3 ciphers for OpenSSL 1.1.1 (GH-6976) (GH-8760) (GH-10607)
Change TLS 1.3 cipher suite settings for compatibility with OpenSSL 1.1.1-pre6 and newer. OpenSSL 1.1.1 will have TLS 1.3 cipers enabled by default. Also update multissltests to test with latest OpenSSL. Signed-off-by: Christian Heimes <[email protected]>. (cherry picked from commit 3e630c5) Co-authored-by: Christian Heimes <[email protected]>
1 parent 826a8b7 commit c49f63c

File tree

4 files changed

+121
-80
lines changed

4 files changed

+121
-80
lines changed

Doc/library/ssl.rst

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -294,11 +294,6 @@ purposes.
294294

295295
3DES was dropped from the default cipher string.
296296

297-
.. versionchanged:: 2.7.15
298-
299-
TLS 1.3 cipher suites TLS_AES_128_GCM_SHA256, TLS_AES_256_GCM_SHA384,
300-
and TLS_CHACHA20_POLY1305_SHA256 were added to the default cipher string.
301-
302297
.. function:: _https_verify_certificates(enable=True)
303298

304299
Specifies whether or not server certificates are verified when creating
@@ -1179,6 +1174,9 @@ to speed up repeated connections from the same clients.
11791174
when connected, the :meth:`SSLSocket.cipher` method of SSL sockets will
11801175
give the currently selected cipher.
11811176

1177+
OpenSSL 1.1.1 has TLS 1.3 cipher suites enabled by default. The suites
1178+
cannot be disabled with :meth:`~SSLContext.set_ciphers`.
1179+
11821180
.. method:: SSLContext.set_alpn_protocols(protocols)
11831181

11841182
Specify which protocols the socket should advertise during the SSL/TLS

Lib/test/test_ssl.py

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2796,19 +2796,24 @@ def test_do_handshake_enotconn(self):
27962796
sock.do_handshake()
27972797
self.assertEqual(cm.exception.errno, errno.ENOTCONN)
27982798

2799-
def test_default_ciphers(self):
2800-
context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
2801-
try:
2802-
# Force a set of weak ciphers on our client context
2803-
context.set_ciphers("DES")
2804-
except ssl.SSLError:
2805-
self.skipTest("no DES cipher available")
2806-
with ThreadedEchoServer(CERTFILE,
2807-
ssl_version=ssl.PROTOCOL_SSLv23,
2808-
chatty=False) as server:
2809-
with closing(context.wrap_socket(socket.socket())) as s:
2810-
with self.assertRaises(ssl.SSLError):
2811-
s.connect((HOST, server.port))
2799+
def test_no_shared_ciphers(self):
2800+
server_context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
2801+
server_context.load_cert_chain(SIGNED_CERTFILE)
2802+
client_context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
2803+
client_context.verify_mode = ssl.CERT_REQUIRED
2804+
client_context.check_hostname = True
2805+
2806+
# OpenSSL enables all TLS 1.3 ciphers, enforce TLS 1.2 for test
2807+
client_context.options |= ssl.OP_NO_TLSv1_3
2808+
# Force different suites on client and master
2809+
client_context.set_ciphers("AES128")
2810+
server_context.set_ciphers("AES256")
2811+
with ThreadedEchoServer(context=server_context) as server:
2812+
s = client_context.wrap_socket(
2813+
socket.socket(),
2814+
server_hostname="localhost")
2815+
with self.assertRaises(ssl.SSLError):
2816+
s.connect((HOST, server.port))
28122817
self.assertIn("no shared cipher", str(server.conn_errors[0]))
28132818

28142819
def test_version_basic(self):
@@ -2839,9 +2844,9 @@ def test_tls1_3(self):
28392844
with context.wrap_socket(socket.socket()) as s:
28402845
s.connect((HOST, server.port))
28412846
self.assertIn(s.cipher()[0], [
2842-
'TLS13-AES-256-GCM-SHA384',
2843-
'TLS13-CHACHA20-POLY1305-SHA256',
2844-
'TLS13-AES-128-GCM-SHA256',
2847+
'TLS_AES_256_GCM_SHA384',
2848+
'TLS_CHACHA20_POLY1305_SHA256',
2849+
'TLS_AES_128_GCM_SHA256',
28452850
])
28462851

28472852
@unittest.skipUnless(ssl.HAS_ECDH, "test requires ECDH-enabled OpenSSL")
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Change TLS 1.3 cipher suite settings for compatibility with OpenSSL
2+
1.1.1-pre6 and newer. OpenSSL 1.1.1 will have TLS 1.3 cipers enabled by
3+
default.

Tools/ssl/multissltests.py

Lines changed: 94 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -41,15 +41,14 @@
4141
log = logging.getLogger("multissl")
4242

4343
OPENSSL_OLD_VERSIONS = [
44-
"0.9.8zc",
45-
"0.9.8zh",
4644
"1.0.1u",
45+
"1.0.2o",
4746
]
4847

4948
OPENSSL_RECENT_VERSIONS = [
50-
"1.0.2",
51-
"1.0.2l",
52-
"1.1.0f",
49+
"1.0.2p",
50+
"1.1.0i",
51+
# "1.1.1",
5352
]
5453

5554
LIBRESSL_OLD_VERSIONS = [
@@ -59,13 +58,15 @@
5958

6059
LIBRESSL_RECENT_VERSIONS = [
6160
"2.5.5",
62-
"2.6.4",
63-
"2.7.1",
61+
"2.6.5",
62+
"2.7.4",
6463
]
6564

6665
# store files in ../multissl
67-
HERE = os.path.abspath(os.getcwd())
68-
MULTISSL_DIR = os.path.abspath(os.path.join(HERE, '..', 'multissl'))
66+
HERE = os.path.dirname(os.path.abspath(__file__))
67+
PYTHONROOT = os.path.abspath(os.path.join(HERE, '..', '..'))
68+
MULTISSL_DIR = os.path.abspath(os.path.join(PYTHONROOT, '..', 'multissl'))
69+
6970

7071
parser = argparse.ArgumentParser(
7172
prog='multissl',
@@ -77,7 +78,7 @@
7778
parser.add_argument(
7879
'--debug',
7980
action='store_true',
80-
help="Enable debug mode",
81+
help="Enable debug logging",
8182
)
8283
parser.add_argument(
8384
'--disable-ancient',
@@ -120,32 +121,49 @@
120121
help="Disable network tests."
121122
)
122123
parser.add_argument(
123-
'--compile-only',
124-
action='store_true',
125-
help="Don't run tests, only compile _ssl.c and _hashopenssl.c."
124+
'--steps',
125+
choices=['library', 'modules', 'tests'],
126+
default='tests',
127+
help=(
128+
"Which steps to perform. 'library' downloads and compiles OpenSSL "
129+
"or LibreSSL. 'module' also compiles Python modules. 'tests' builds "
130+
"all and runs the test suite."
131+
)
126132
)
127133

134+
parser.add_argument(
135+
'--force',
136+
action='store_true',
137+
dest='force',
138+
help="Force build and installation."
139+
)
140+
parser.add_argument(
141+
'--keep-sources',
142+
action='store_true',
143+
dest='keep_sources',
144+
help="Keep original sources for debugging."
145+
)
128146

129147
class AbstractBuilder(object):
130148
library = None
131149
url_template = None
132150
src_template = None
133151
build_template = None
152+
install_target = 'install'
134153

135154
module_files = ("Modules/_ssl.c",
136155
"Modules/_hashopenssl.c")
137156
module_libs = ("_ssl", "_hashlib")
138157

139-
def __init__(self, version, compile_args=(),
140-
basedir=MULTISSL_DIR):
158+
def __init__(self, version, args):
141159
self.version = version
142-
self.compile_args = compile_args
160+
self.args = args
143161
# installation directory
144162
self.install_dir = os.path.join(
145-
os.path.join(basedir, self.library.lower()), version
163+
os.path.join(args.base_directory, self.library.lower()), version
146164
)
147165
# source file
148-
self.src_dir = os.path.join(basedir, 'src')
166+
self.src_dir = os.path.join(args.base_directory, 'src')
149167
self.src_file = os.path.join(
150168
self.src_dir, self.src_template.format(version))
151169
# build directory (removed after install)
@@ -252,21 +270,29 @@ def _unpack_src(self):
252270
def _build_src(self):
253271
"""Now build openssl"""
254272
log.info("Running build in {}".format(self.build_dir))
255-
cwd = self.build_dir
256-
cmd = ["./config", "shared", "--prefix={}".format(self.install_dir)]
257-
cmd.extend(self.compile_args)
273+
cmd = [
274+
"./config",
275+
"shared", "--debug",
276+
"--prefix={}".format(self.install_dir)
277+
]
278+
env = os.environ.copy()
279+
# set rpath
280+
env["LD_RUN_PATH"] = self.lib_dir
258281
self._subprocess_call(cmd, cwd=cwd)
259282
# Old OpenSSL versions do not support parallel builds.
260283
self._subprocess_call(["make", "-j1"], cwd=cwd)
261284

262-
def _make_install(self, remove=True):
263-
self._subprocess_call(["make", "-j1", "install"], cwd=self.build_dir)
264-
if remove:
285+
def _make_install(self):
286+
self._subprocess_call(
287+
["make", "-j1", self.install_target],
288+
cwd=self.build_dir
289+
)
290+
if not self.args.keep_sources:
265291
shutil.rmtree(self.build_dir)
266292

267293
def install(self):
268294
log.info(self.openssl_cli)
269-
if not self.has_openssl:
295+
if not self.has_openssl or self.args.force:
270296
if not self.has_src:
271297
self._download_src()
272298
else:
@@ -332,6 +358,8 @@ class BuildOpenSSL(AbstractBuilder):
332358
url_template = "https://www.openssl.org/source/openssl-{}.tar.gz"
333359
src_template = "openssl-{}.tar.gz"
334360
build_template = "openssl-{}"
361+
# only install software, skip docs
362+
install_target = 'install_sw'
335363

336364

337365
class BuildLibreSSL(AbstractBuilder):
@@ -370,57 +398,64 @@ def main():
370398

371399
start = datetime.now()
372400

373-
for name in ['python', 'setup.py', 'Modules/_ssl.c']:
374-
if not os.path.isfile(name):
401+
if args.steps in {'modules', 'tests'}:
402+
for name in ['setup.py', 'Modules/_ssl.c']:
403+
if not os.path.isfile(os.path.join(PYTHONROOT, name)):
404+
parser.error(
405+
"Must be executed with ./python from CPython build dir"
406+
)
407+
if not os.path.samefile('python', sys.executable):
375408
parser.error(
376-
"Must be executed from CPython build dir"
377-
)
378-
if not os.path.samefile('python', sys.executable):
379-
parser.error(
380409
"Must be executed with ./python from CPython build dir"
381410
)
382411

383-
# check for configure and run make
384-
configure_make()
412+
# check for configure and run make
413+
configure_make()
385414

386415
# download and register builder
387416
builds = []
388417

389418
for version in args.openssl:
390-
build = BuildOpenSSL(version)
419+
build = BuildOpenSSL(
420+
version,
421+
args
422+
)
391423
build.install()
392424
builds.append(build)
393425

394426
for version in args.libressl:
395-
build = BuildLibreSSL(version)
427+
build = BuildLibreSSL(
428+
version,
429+
args
430+
)
396431
build.install()
397432
builds.append(build)
398433

399-
for build in builds:
400-
try:
401-
build.recompile_pymods()
402-
build.check_pyssl()
403-
if not args.compile_only:
404-
build.run_python_tests(
405-
tests=args.tests,
406-
network=args.network,
407-
)
408-
except Exception as e:
409-
log.exception("%s failed", build)
410-
print("{} failed: {}".format(build, e), file=sys.stderr)
411-
sys.exit(2)
412-
413-
print("\n{} finished in {}".format(
414-
"Tests" if not args.compile_only else "Builds",
415-
datetime.now() - start
416-
))
434+
if args.steps in {'modules', 'tests'}:
435+
for build in builds:
436+
try:
437+
build.recompile_pymods()
438+
build.check_pyssl()
439+
if args.steps == 'tests':
440+
build.run_python_tests(
441+
tests=args.tests,
442+
network=args.network,
443+
)
444+
except Exception as e:
445+
log.exception("%s failed", build)
446+
print("{} failed: {}".format(build, e), file=sys.stderr)
447+
sys.exit(2)
448+
449+
log.info("\n{} finished in {}".format(
450+
args.steps.capitalize(),
451+
datetime.now() - start
452+
))
417453
print('Python: ', sys.version)
418-
if args.compile_only:
419-
print('Build only')
420-
elif args.tests:
421-
print('Executed Tests:', ' '.join(args.tests))
422-
else:
423-
print('Executed all SSL tests.')
454+
if args.steps == 'tests':
455+
if args.tests:
456+
print('Executed Tests:', ' '.join(args.tests))
457+
else:
458+
print('Executed all SSL tests.')
424459

425460
print('OpenSSL / LibreSSL versions:')
426461
for build in builds:

0 commit comments

Comments
 (0)