Skip to content

Secure SunSpec Modbus TLS test PKI (ECDSA P-256) + pymodbus 3.x update#117

Open
dersecure wants to merge 6 commits into
sunspec:masterfrom
dersecure:master
Open

Secure SunSpec Modbus TLS test PKI (ECDSA P-256) + pymodbus 3.x update#117
dersecure wants to merge 6 commits into
sunspec:masterfrom
dersecure:master

Conversation

@dersecure
Copy link
Copy Markdown
Contributor

Summary

Regenerates the Modbus TLS test PKI under sunspec2/tests/tls_data/ so it is Secure SunSpec Modbus compliant, reorganises it into a structured layout, and updates the TLS test to a current pymodbus.

What changed

Secure SunSpec Modbus test PKI (ECDSA P-256)

The previous PKI used RSA-2048 keys, which can only negotiate the non-conformant ECDHE-RSA cipher suites. The mandatory Secure SunSpec Modbus suites are all ECDHE-ECDSA on the P-256 curve, so every key (root, intermediates, leaves) is now ECDSA P-256. Client certificates carry the SunSpec role extension at OID 1.3.6.1.4.1.50316.802.1 as an ASN.1 UTF8String holding exactly one role.

create_tls_certs.sh now generates the full certificate set the Secure SunSpec Modbus conformance tests need — valid and deliberately-invalid — in a structured tree:

tls_data/
  create_tls_certs.sh   openssl/   README.md
  ca/        root_ca, server_inter_ca, client_inter_ca, *_ca_chain
  server/
    tls1_2/  server_valid          tls1_3/  server_valid
    invalid/ server_expired, server_self_signed, server_foreign_ca, server_no_san
  client/
    tls1_2/  client_readonly, client_gridservice, client_netadmin, client_superadmin
    tls1_3/  (same four mandatory SunSpec roles)
    invalid/ client_no_role, client_malformed_role (IA5String), client_self_signed,
             client_foreign_ca, client_expired
  foreign_pki/  foreign_root_ca   (untrusted CA for multiple-PKI tests)

tls_data/README.md documents the layout and trust relationships. modbus.py (CAFILE / CLIENT_CERTFILE / CLIENT_KEYFILE) is updated to the new paths, and setup.py package_data now ships the full nested tree.

pymodbus update

pymodbus was pinned at 2.5.3, several major versions behind; its test-only API (pymodbus.server.sync) was removed upstream, so test_tls_client.py could not be collected. requirements.txt now requires pymodbus>=3.13.0,<4.0, and test_tls_client.py is rewritten for the pymodbus 3.x API. pymodbus is only used by that test, not the sunspec2 library.

Verification

  • create_tls_certs.sh runs clean on OpenSSL 3.1.2 and produces the full tree; openssl verify confirms the valid leaves chain to the root.
  • A mutual-TLS handshake with the regenerated certs negotiates the mandatory ECDHE-ECDSA-AES128-GCM-SHA256 at TLS 1.2.
  • test_tls_client.py passes against pymodbus 3.13; the full test suite passes (234 tests).

This was developed and reviewed in the dersecure/pysunspec2 fork; it is offered upstream for the SunSpec Alliance repository.

dersecure added 6 commits May 20, 2026 13:46
…iance

Replace the flat RSA test PKI with a Secure SunSpec Modbus compliant
certificate set and reorganise tls_data into a structured layout.

  * All keys are now ECDSA on the NIST P-256 curve, required by the
    mandatory Secure SunSpec Modbus cipher suites (ECDHE-ECDSA, P-256).
  * Client certificates carry the SunSpec role extension at OID
    1.3.6.1.4.1.50316.802.1 as an ASN.1 UTF8String with a single role.
  * create_tls_certs.sh now generates every certificate the Secure
    SunSpec Modbus conformance tests need:
      - ca/                 shared root + server/client intermediate CAs
      - server/tls1_2|tls1_3 valid server certificates
      - server/invalid/     expired, self-signed, foreign-CA, no-SAN
      - client/tls1_2|tls1_3 one cert per mandatory SunSpec role
      - client/invalid/     no-role, malformed-role (IA5String),
                            self-signed, foreign-CA, expired
      - foreign_pki/        an untrusted CA for multiple-PKI tests
  * tls_data/README.md documents the layout and trust relationships.

modbus.py and test_tls_client.py are updated to the new cert paths.
This PKI is the shared source of truth for the Secure SunSpec Modbus
TLS work in DERSim and the LabTest Modbus client.
pymodbus 2.5.3 is several major versions behind and its test-only API
(pymodbus.server.sync) was removed upstream, so test_tls_client.py
could not even be collected.

  * requirements.txt: pymodbus==2.5.3 -> pymodbus>=3.9.0. pymodbus is
    only used by the TLS test, not the sunspec2 library, so the bump
    has no effect on the package itself.
  * test_tls_client.py: import StartTlsServer from pymodbus.server and
    build the slave context with ModbusSequentialDataBlock, per the
    pymodbus 3.x API. Also fix the server to verify connecting clients
    against the client CA chain (it previously used the server chain).

Verified: test_tls_client.py passes against the regenerated ECDSA
P-256 Secure SunSpec Modbus PKI.
  * setup.py: package_data only globbed tls_data/* (non-recursive), so
    installed distributions would omit the nested ca/, server/, client/
    and foreign_pki/ certificates that modbus.py now references by
    default. Add tls_data/*/* and tls_data/*/*/* so the whole PKI tree
    ships; verified all 55 tls_data files are matched.
  * test_tls_client.py: the parametrized cafile was unused (the device
    hardcoded CAFILE_SERVER). Use the parameter and set it to the
    server CA chain so the client correctly validates the server.

The related server-side trust fix (verify clients against the client
CA chain) already landed in the previous commit.
CI installed pymodbus 3.13 (requirements allowed >=3.9.0), where the
test-only API has moved on again and collection failed:

  * ModbusSlaveContext was renamed ModbusDeviceContext; import either.
  * ModbusServerContext takes 'devices' instead of 'slaves'; try both.
  * ModbusSequentialDataBlock must start at address 1, not 0, or
    pymodbus 3.13 raises "0 <= address < 65535" building SimData.
  * requirements.txt: pin pymodbus>=3.13.0,<4.0 so CI resolves a
    version whose API matches this test.

Full suite: 234 passed.
…s-pki

Regenerate Modbus TLS test PKI for Secure SunSpec Modbus compliance
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant