fix(tls): derive OMS exporter secret from TLS key log (stdlib only)#773
Merged
gijzelaerr merged 2 commits intoJul 15, 2026
Merged
Conversation
CPython's ssl module has no export_keying_material, so _activate_tls always logged "Could not extract OMS exporter secret" and left oms_secret=None — leaving password legitimation / HMAC key derivation as dead code against real hardware (see gijzelaerr#718). Capture the TLS 1.3 exporter_master_secret via the session key log (SSLContext.keylog_filename, written to a private 0600 temp file that is removed right after the handshake) and run the RFC 8446 exporter derivation ourselves with stdlib hmac/hashlib. No new dependency, no ctypes / external shared libraries. Validated on real hardware (S7-1200 CPU 1211C, FW V4.6): the derived secret is byte-for-byte identical to pyOpenSSL's export_keying_material on the same session, and oms_secret is now populated (32 bytes). Scope: this unblocks the exporter-derived path only; the legitimation challenge exchange itself still needs work (challenge fetch is rejected by the PLC — tracked in gijzelaerr#718). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
3 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
CPython's
sslmodule doesn't exposeexport_keying_material(RFC 5705), so_activate_tlsalways fell intoCould not extract OMS exporter secretand setoms_secret = None— leaving password legitimation / HMAC key derivation as dead code against real PLCs (#718).This derives the secret using only the standard library — no new dependency:
SSLContext.keylog_filename) on a private0600temp file, removed immediately after the handshake.exporter_master_secretfrom it and run the RFC 8446 exporter derivation with stdlibhmac/hashlib(_tls13_exporter).Relationship to #772
This is an alternative to #772, which solves the same problem by adding a
pyOpenSSL(+cryptography) dependency. The two touch the same_activate_tls, so only one is needed. The trade-offs:pyOpenSSL.set_ecdh_curverestriction that they accept.(No "ctypes" angle here —
pyOpenSSLuses cffi, not ctypes; the only real difference is the added dependency.)Validation
Tested over TLS 1.3 against two real PLCs:
On both:
oms_secretis now populated (32 bytes) instead ofNone, and the derived value is byte-for-byte identical topyOpenSSL'sexport_keying_material(b"EXPERIMENTAL_OMS", 32)on the same session (cross-checked separately). Anonymous V2 reads still work; falls back tooms_secret=Nonefor non-TLS-1.3 sessions.Scope / not included
This only unblocks the exporter. The legitimation challenge fetch (
GetVarSubStreamedonSERVER_SESSION_REQUEST=303) is still rejected by the 1211C (return_value=0xC318890001E1FFFE), so full password legitimation needs further protocol work — tracked in #718.Happy to add a unit test for
_tls13_exporter(fixed vector) if you'd like.🤖 Generated with Claude Code