From 2705f166702243295b5aee0110def84e7d59bd43 Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Thu, 12 Nov 2026 22:22:45 -0500 Subject: [PATCH] test(validation): ScadaBridge consumer matrix uses a production-length API key; SDK 2 consumer run (ScadaBridge#125) The consumer matrix minted a 55-character otua_matrix_ key, which stays under SDK 2's 64-byte RsaEncryptedSecret threshold and so could not show lmxopcua#880 from the consumer side. It now uses the ApiKeySecret.Generate shape (65 characters), as opcua-sdk-compatibility already does. README records the 2026-11-12 run with ScadaBridge on SDK 2.0.1 (a2e9d718): matrix 11/11 x3 modes on 83ed880e and 412da27c, ACL off and on; 8/8 trust controls fail closed; failover 8/8; a mutation with the SDK default UserName handler fails every baseline leg with BadIdentityTokenInvalid. Claude-Session: https://claude.ai/code/session_01GT67ACmpMR7b9LGRPLijMi --- .../scadabridge-sdk-compatibility/README.md | 19 ++++++++++++++++++- .../scadabridge-sdk-compatibility/run.py | 6 +++++- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/scripts/validation/scadabridge-sdk-compatibility/README.md b/scripts/validation/scadabridge-sdk-compatibility/README.md index 62a012776..5206ff21d 100644 --- a/scripts/validation/scadabridge-sdk-compatibility/README.md +++ b/scripts/validation/scadabridge-sdk-compatibility/README.md @@ -45,7 +45,11 @@ roundtrips, durable hierarchical browse including the native alarm, data quality and timestamps on raw and UNS subscriptions, alarm filter/ConditionRefresh, disconnect/reconnect, and (ScadaBridge#125) a Server-object alarm subscription on the reconnected session whose ConditionRefresh must replay the active native -condition (11 cases per trusted leg). History +condition (11 cases per trusted leg). The API key has the production +`ApiKeySecret.Generate` shape and length, 65 characters (lmxopcua#880): the +55-character `otua_matrix_` key used before stayed under SDK 2's 64-byte +`RsaEncryptedSecret` threshold, so it could not show whether an SDK 2 consumer +still sends the password in a format an SDK 1.5 server decrypts. History and Part 9 acknowledgment are not exposed by the inspected consumer interfaces; the parent Shared/CLI matrix covers those application services instead. @@ -97,3 +101,16 @@ legs (11/11 cases each). Loss-to-backup timings are the same on both versions: waits out a connect timeout against the silent primary. `rig-failover.sh` passed 9/9 against the docker-dev pair (image SDK 2.0.1), central-1 killed, about 20 s to central-2. + +Run 2026-11-12 (ScadaBridge#125) with the consumer itself on SDK **2.0.1** +(ScadaBridge `731d1ed7`, on main as `a2e9d718`; the DCL sends UserName passwords in the +legacy RSA format), 65-character key, against the SDK 1.5.378 baseline +(`83ed880e`) and master `412da27c` (SDK 2.0.1): matrix 11/11 in None, Sign and +SignAndEncrypt on both with the ACL off and on, all 8 trust controls failed +closed (0x80130000 / 0x801A0000), failover 8/8 legs. `kill` reaches the backup +in about 20 s as before; `freeze` is now detected in about 15 s (SDK 2 +keep-alive; about 20 s on the SDK 1.5 client) and reaches the backup in 81–125 s. +A mutation run with the consumer's UserName identity reverted to the SDK 2 +default handler fails every trusted leg against the baseline with +`BadIdentityTokenInvalid` (0x80200000) and passes against master, which is +lmxopcua#880 reproduced from the consumer side. diff --git a/scripts/validation/scadabridge-sdk-compatibility/run.py b/scripts/validation/scadabridge-sdk-compatibility/run.py index 1de2e7e45..535ae8961 100644 --- a/scripts/validation/scadabridge-sdk-compatibility/run.py +++ b/scripts/validation/scadabridge-sdk-compatibility/run.py @@ -16,7 +16,11 @@ SECURITY = {"None": "none", "Sign": "sign", "SignAndEncrypt": None} def leg_env(mode): env = os.environ.copy() - env["OPCUA_MATRIX_PASSWORD"] = "otua_matrix_" + secrets.token_urlsafe(32) + # The production ApiKeySecret.Generate shape and length, 65 characters (lmxopcua#880, ScadaBridge#125): the + # 55-character key used before stayed under SDK 2's 64-byte RsaEncryptedSecret threshold, so it could not show + # whether an SDK 2 consumer still sends the password in a format an SDK 1.5 server decrypts. + env["OPCUA_MATRIX_PASSWORD"] = "otua_" + secrets.token_hex(8) + "_" + secrets.token_urlsafe(32) + assert len(env["OPCUA_MATRIX_PASSWORD"]) == 65 for name in ("SCADABRIDGE_PROBE_PKI", "SCADABRIDGE_PROBE_SERVER_CERTS", "OPCUA_MATRIX_SERVER_SECURITY"): env.pop(name, None) if SECURITY[mode]: