SessionKey handshake for V1-initial S7-1200 PLCs#775
Conversation
Rebased onto the s7commplus/ package split (#774). All session_auth code now lives under s7commplus/session_auth/ instead of s7/session_auth/. Adds the full session_auth package for V1-initial S7-1200 PLCs that require a SessionKey handshake before data operations: - s7commplus/session_auth/: Public-key-based session authentication (Family 0 key derivation, fingerprint matching, SecurityKeyEncryptedKey blob generation, legitimation challenge solver, HMAC key derivation) - V3 HMAC framing for post-auth data operations - TIA-style CreateObject attributes (required by V1-initial firmware) - PAOM string stripping in ServerSessionVersion echo - Post-auth legitimation flow (challenge read + solved blob write) - Connection.connect(password=) parameter for auth flow Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ation - Add type annotations for hashmod parameters in HKDF helpers - Fix frame_version type annotation (int vs ProtocolVersion) - Remove duplicated _skip_typed_value type-dispatch block (dead code after return) - Guard auth_result unpacking with explicit None check for mypy - Add None guards for session_key and challenge in _post_auth_legitimation - Add assert/guard for Optional connection in substreamed client methods Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Analysis of TIA Portal V19 pcap captures (xBiggs, GH-710) revealed the root cause of the ConnectionReset after SessionKey auth: TIA Portal's post-SetupSession sequence: 1. SET_VARIABLE (0x04F2) — write attribute 323 on session 2. GET_VAR_SUBSTREAMED — read from InObj=50, addr 7920 (finalize) 3. Data reads work immediately (no password needed) 4. Legitimation (addr 303/1846) happens later, optionally Our code was skipping steps 1-2 and jumping straight to legitimation (read challenge → write blob → finalize), which caused the PLC to return V254 SYSTEM_EVENT error 0xE9 and RST subsequent requests. Changes: - Add _session_activate() that mirrors TIA Portal's activation: SET_VARIABLE attr 323 + GET_VAR_SUBSTREAMED InObj=50 addr 7920 - Only call _post_auth_legitimation() when a password is provided (previously ran unconditionally, even with empty password) - Reorder: activate first, then legitimation (if password given) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Replace duplicate _skip_typed_value (73 lines) with codec.skip_typed_value import; fixes DWORD bug (was VLQ instead of fixed 4 bytes) - Initialize _session_auth_public_key/_session_auth_family in __init__ instead of using hasattr() guards - Remove redundant lazy imports (DataType, LegitimationId already at top level) - Make SessionKey and TLS auth mutually exclusive in client.connect() Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
3a5660f to
6a3b019
Compare
Move the 95K+ lines of machine-transpiled monolith transforms, constant tables, and binary data into family0/_generated/ to clearly separate opaque generated code from the human-readable orchestration layer. Add ARCHITECTURE.md documenting: - When SessionKey auth is needed (V1-initial vs TLS) - The full authentication flow (diagram) - Module map: which file does what - How the 180-byte SecurityKeyEncryptedKey blob is built - References to upstream HarpoS7, Black Hat papers Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Data2Collection table 1 had 2111 entries (4222 bytes) but its 33 operations require 2112 entries (4224 bytes). The missing final word 0x4C28 caused IndexError during session key renewal when a challenge selected the last lookup block. Backported from HarpoS7 commit 22b9dc0 (bonk-dev/HarpoS7#24). Adds: - The missing 0x4C28 entry in fp_data2.bin - Lookup table size invariant test - Regression test with captured renewal challenge 5B15B469... Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Analysis of all 4 TIA Portal pcaps (GH-710 + GH-728) shows TIA consistently sends 4 requests after SetupSession, not 2: 1. SET_VARIABLE attr 323 = USINT(5) 2. GET_VAR_SUBSTREAMED InObj=50, addr 7920 3. GET_VAR_SUBSTREAMED InObj=session, addr 1842 4. GET_VAR_SUBSTREAMED InObj=50, addr 7920 (again) Previously we only did steps 1-2. Add steps 3-4 and extract a _build_get_var_substreamed helper to reduce boilerplate in both _session_activate and _post_auth_legitimation. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Move _parse_create_object_response (120 lines, connection.py) into codec.py as parse_create_object_attributes — a reusable function that returns ServerSessionVersion, public key fingerprint, and session challenge in a CreateObjectAttributes dataclass. Both sync connection and async_client can use it. - Keep parse_server_session_version as a thin wrapper for backward compat. - Fix: add session_auth/**/*.bin to pyproject.toml package-data so binary data files (fp_data, transform12 metadata) ship in wheels. Net: -69 lines. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
Hi, I'm the creator of HarpoS7. I see that you're porting it to Python which is awesome! However, could you please include the original license text somewhere in your repository?: LICENSE-HarpoS7 |
|
hey! thanks for making HarpoS7! But i don't fully understand your request. the licenses we put on our software is the same, it's both MIT right? https://github.com/gijzelaerr/python-snap7/blob/master/LICENSE Or are you talking about credits? I think the parts that are ported from HaroS7 should indicate it comes from your source code, but i happily do another iteration and check credit is given where applicable. I'll aso add you to the credits file/section. Also, i'm not 100% convinced yet that the SessionKey handshake for V1 s7commplus should land in this package, since it is very reverse-engineery with tons of binary blobs and magic. I've been trying to polish it, but there is still quite some magic. Maybe my cleanups can be helpful on your side again :) Let me know if i can help you out in another way. |
The session_auth package is a Python port of HarpoS7 by bonk-dev. Include the original MIT license text as requested in #775, and add credits in the changelog and docs. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replaces #761, rebased onto the s7commplus/ package split (#774).
Summary
Adds the full
session_authpackage for V1-initial S7-1200 PLCs that require a SessionKey handshake before data operations:s7commplus/session_auth/: Public-key-based session authentication (Family 0 key derivation, fingerprint matching, SecurityKeyEncryptedKey blob generation, legitimation challenge solver, HMAC key derivation)Connection.connect(password=)parameter for auth flowTest plan
Known limitations
01:prefix) public keys are includedsend_requestneeds V3 HMAC framing wired in for data operations🤖 Generated with Claude Code