[M5] mxaccess-asb: F30 read-side dict-id resolution + matching .NET CV xmlns
**F30 (read side):** post-pass over `body_tokens` in `decode_envelope` substitutes `NbfxName::Static(id)` → `NbfxName::Inline(name)` and `NbfxText::DictionaryStatic(id)` → `NbfxText::Chars(name)` whenever the dict id resolves. Lookup tries the per-message binary header strings first (`(id-1)/2` slot), then falls back to the cumulative session dynamic dict, then the `[MC-NBFS]` static table for even ids. Tokens with unresolvable ids stay opaque so trace output still reveals them. This unblocks reading the live Register response: previously every field came back as `<b:Static(43)>false</…>` and we couldn't tell what the server actually said. Now we see `<b:successField>false</>` and `<b:resultCodeField>1</>` clearly. resultCode 1 maps to `AsbErrorCode.InvalidConnectionId` (`AsbResultMapping.cs:6`) — which means AuthenticateMe failed silently and the server discarded our connection state, even though the crypto stack is proven byte-equal to .NET. **Wire CV xmlns parity:** `<h:ConnectionValidator>` for the `XmlSerializer` mode (AuthenticateMe / Disconnect / KeepAlive) now emits all four xmlns declarations .NET writes, in the same order: `xmlns:h`, default `xmlns` (same value), `xmlns:xsi`, `xmlns:xsd`. .NET emits the default xmlns redundantly even though the `h` prefix is bound to the same URL — captured against the .NET probe via asb-relay. This was suspected to be the AuthenticateMe HMAC blocker but the live test still returns `InvalidConnectionId`, so the bug is elsewhere. **F31 updated** with the surviving hypotheses for the `InvalidConnectionId` mystery: server-side `XmlSerializer` constructor mismatch, subtle byte-level wire difference affecting deserialization, or unused `ServiceAuthenticationData` from the ConnectResponse. Resolution probably requires server-side instrumentation or controlled-scenario byte-level HMAC diff. Workspace: 710 unit tests pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+13
-5
@@ -141,12 +141,20 @@ F25 (`mxaccess-asb` IASBIDataV2 client) and F26 (`mxaccess::Session` over `AsbTr
|
||||
|
||||
**Resolves when:** `decode_tokens` (or a post-pass over the token stream) substitutes `NbfxName::Static(id)` with `NbfxName::Inline(name)` whenever the dict id resolves to a known string. The dynamic dict (`read_dictionary`) accumulates session strings via `intern`; the read-path needs the parallel session counter to map wire ids to slots — wire ids are odd and session-cumulative across messages, mirroring the F28 fix on the write side. **Resolves**: F25 live data path (Read/Write/Subscribe responses are all dict-encoded too).
|
||||
|
||||
### F31 — Wire-byte server response says `successField = false` for AuthenticateMe-then-Register
|
||||
**Severity:** P1 — visible failure mode.
|
||||
**Source:** `relay-rust-decode.log` + `MX_ASB_TRACE_REPLY` dump.
|
||||
**Why deferred:** With every wire-format fix from F28 landed (canonical XML signing, registry-driven DH params, dynamic-dict id management, ConnectionValidator wire-format-per-action, chunked ASBIData decode, `0x0A ShortDictionaryXmlnsAttribute` decode), `AuthenticateMe` is accepted by the server and a real `RegisterItemsResponse` returns. The response body decodes structurally but the visible `<b:Static(43)>false</b:Static(43)>` element matches the `successField` slot in the response binary header pre-pop, so Register returned success=false with empty Status array. Hypothesis (highest-likelihood first): (a) `AuthenticateMe` reaches the server but its HMAC is silently invalid → server treats subsequent requests as unauthenticated → register returns "no items processed"; (b) wire-byte difference between our Register and .NET's that the server still accepts but interprets as a 0-item registration; (c) tag `TestChildObject.TestInt` doesn't resolve in the live Galaxy state during this run.
|
||||
### F31 — `AuthenticateMe` HMAC silently invalid on the server (resultCode = `InvalidConnectionId`)
|
||||
**Severity:** P1 — gates every signed and unsigned operation after Connect.
|
||||
**Source:** Live capture + F30 dict-id resolution exposing the response `<b:resultCodeField>1</b:resultCodeField>` (= `AsbErrorCode.InvalidConnectionId` per `AsbResultMapping.cs:6`) plus `<b:successField>false</b:successField>`.
|
||||
|
||||
**Resolves when:** F30 lands (so we can read the actual Status array + error codes from the response), AND we confirm against a side-by-side .NET probe wire diff which interpretation applies. If (a), AuthenticateMe HMAC needs further investigation despite the deterministic-HMAC fixture parity (commit `ce27b63`) — a session-state mismatch between client+server view of next-message-number could explain it. If (b), expect a structural delta in the request bytes the server tolerates but interprets differently. If (c), pick a different known-resolvable tag.
|
||||
**Why this is mysterious:** the entire crypto stack is proven byte-equal to .NET (commit `ce27b63` deterministic HMAC fixture covers DH, crypto_key, HMAC-SHA1, PBKDF2-SHA1, AES-CBC PKCS7), the canonical XML emitter is fixture-validated against `request.ToXml()` (commit `f14580e`), the registry DH params are honoured (commit `f14580e`), and the wire-level `<h:ConnectionValidator>` now carries the same four xmlns declarations .NET emits (`xmlns:h`, default `xmlns`, `xmlns:xsi`, `xmlns:xsd` all in this commit). Yet the server reports `InvalidConnectionId` on Register, indicating that AuthenticateMe's HMAC failed to verify and the server discarded the connection state.
|
||||
|
||||
**Investigation done:** side-by-side `MX_ASB_TRACE_DERIVE` confirms passphrase bytes [96..176] of the crypto_key match .NET (commit `fd38189`); shared_secret bytes diverge per session because each peer chooses its own DH random, but the client+server pair derives the same value by construction.
|
||||
|
||||
**Hypotheses still standing:**
|
||||
- The server's canonical-XML reconstruction uses `new XmlSerializer(type)` without the `"urn:invensys.schemas"` default namespace that the client passes in `AsbSerialization.cs:27` — would produce different bytes, mismatching HMAC. Untestable from outside the server.
|
||||
- A subtle byte-level wire difference that affects deserialization (e.g. an attribute the server's XmlSerializer requires but XmlBinaryReader normalizes differently). Hard to find without server logs.
|
||||
- Some other state the server tracks per-connection that we're not setting (e.g. a session token from `ServiceAuthenticationData` we ignore). The `ConnectResponse.ServiceAuthenticationData` is currently parsed but not fed back into anything; .NET's `AsbSystemAuthenticator` may use it for a downstream verification we're missing.
|
||||
|
||||
**Resolves when:** Either (a) the server is instrumented (`IncludeExceptionDetailInFaults` on the WCF service config, or a TraceListener on `System.ServiceModel.MessageLogging`) to surface the actual deserialization / HMAC mismatch reason; or (b) we capture .NET probe HMAC bytes alongside Rust HMAC bytes for a controlled scenario (fixed DH private key on both ends) and identify the byte-level divergence.
|
||||
|
||||
### F28 — Canonical XML serialiser for `ConnectedRequest` signing (matches `XmlSerializer.Serialize` byte-for-byte)
|
||||
**Severity:** P0 — blocks every signed ASB operation (AuthenticateMe, RegisterItems, all data-plane RPCs).
|
||||
|
||||
Reference in New Issue
Block a user