From 79a02e84c20dfdcef07cdeac7ebadb356eeb2bf9 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 18 Jun 2026 20:49:16 +0200 Subject: [PATCH] docs(s7commplus): configure OPENSSL_CONF for TLS on OpenSSL 3.5+ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On OpenSSL >= 3.5 the default TLS 1.3 ClientHello advertises the X25519MLKEM768 post-quantum group, whose ~1.2 KB key share the S7-1500 rejects — it drops the handshake, so connect(use_tls=True) fails with a connection reset. The PLC mandates TLS 1.3 (TLS 1.2 is refused outright) and CPython's ssl exposes no API for the TLS 1.3 supported_groups list, so document restricting them to classic ECDHE curves via an OPENSSL_CONF file. Follow-up to #746 (the in-code ctypes group restriction was declined). Co-Authored-By: Claude Opus 4.8 (1M context) --- doc/connecting.rst | 57 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/doc/connecting.rst b/doc/connecting.rst index 39fb3a86..740ff7fb 100644 --- a/doc/connecting.rst +++ b/doc/connecting.rst @@ -152,6 +152,63 @@ with the ``s7commplus`` extra: ``browse()`` and other CommPlus-only operations are not yet supported on those firmwares — see issue #710. +TLS handshake fails on OpenSSL 3.5+ (post-quantum key share) +------------------------------------------------------------- + +On systems with **OpenSSL ≥ 3.5** (e.g. Debian 13 and other recent +distributions) the TLS 1.3 ``ClientHello`` advertises a post-quantum +hybrid key-exchange group, ``X25519MLKEM768``, by default. Its key +share is roughly 1.2 KB, and the S7-1500's TLS stack rejects the +oversized ``ClientHello`` — it drops the connection mid-handshake, so +``connect(use_tls=True)`` fails with a *connection reset by peer*. + +The PLC mandates TLS 1.3 (it refuses TLS 1.2 outright), and CPython's +``ssl`` module exposes no API to restrict the TLS 1.3 +``supported_groups`` list. The fix is to restrict the offered groups +through OpenSSL's own configuration — via the ``OPENSSL_CONF`` +environment variable — to the classic ECDHE curves that every +S7-1200/1500 supports. + +1. Create an OpenSSL configuration file, e.g. ``s7-openssl.cnf``: + + .. code-block:: ini + + # Restrict the TLS key-exchange groups to classic ECDHE curves so the + # ClientHello stays small enough for the S7-1500 to accept. + openssl_conf = openssl_init + + [openssl_init] + ssl_conf = ssl_configuration + + [ssl_configuration] + system_default = system_default_sect + + [system_default_sect] + Groups = x25519:secp256r1:secp384r1 + +2. Point ``OPENSSL_CONF`` at it **before** the Python process starts. + OpenSSL reads this configuration once, when it initialises, so + setting the variable from inside Python (e.g. via ``os.environ``) + after ``ssl`` is imported is too late — it must be set in the + environment: + + .. code-block:: bash + + # for a single run + OPENSSL_CONF=/path/to/s7-openssl.cnf python your_script.py + + # or for the whole shell session + export OPENSSL_CONF=/path/to/s7-openssl.cnf + +With the groups restricted to classic curves, the TLS 1.3 handshake no +longer offers ``X25519MLKEM768`` and the S7-1500 completes it normally. + +.. note:: + + This only affects OpenSSL ≥ 3.5. On older OpenSSL releases the + default key-exchange groups are already classic ECDHE curves, so no + extra configuration is needed. + PLC Password Authentication ----------------------------