docs(grpc-events): Path B — ExchangeKey ECDH clears 2 of 3 layers

Records that the pure-managed P-256 ExchangeKey works (cleared the v8 client-key
check; error advanced to 132/171 AuthenticationFailed). The remaining layer is the
26-byte credential-token KDF, which requires recovering the native key derivation.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B6mcaT2PjRFKcogzp9UkfC
This commit is contained in:
Joseph Doherty
2026-06-23 10:31:37 -04:00
parent d67f6f5e96
commit 3fd522fa10
@@ -229,3 +229,28 @@ size) — using .NET `ECDiffieHellman`, establish the client key, then reissue t
Open question for Path B: whether merely *completing* the ECDH key agreement registers the client key Open question for Path B: whether merely *completing* the ECDH key agreement registers the client key
(so the zeroed openParameters token still rides through), or whether the token must also be derived (so the zeroed openParameters token still rides through), or whether the token must also be derived
from the shared secret (full KDF/cipher RE). from the shared secret (full KDF/cipher RE).
### Path B started 2026-06-23 — ExchangeKey ECDH works; cleared 2 of 3 layers
Implemented `HistoryService.ExchangeKey` as a **pure-managed P-256 ECDH** key exchange
(`HistorianNativeHandshake.BuildExchangeKeyClientHello` / `DeriveExchangeKeySecret`, .NET
`ECDiffieHellman` over `nistP256`; wire format `"ECK1" + u32(32) + X(32) + Y(32)`) and wired it into
`HistorianGrpcHandshake.OpenSession(eventConnection: true)` ahead of the v8 `OpenConnection`,
on the same context-key handle. Live result against the server: the **`ExchangeKey` RPC succeeds**
(the server accepted our public key), and the v8 `OpenConnection` error **moved one layer deeper**:
```
Path A (no ExchangeKey): 132/34 "Failed to get client key"
Path B (ExchangeKey ECDH): 132/171 AuthenticationFailed "EstablishConnection — Authentication failed"
```
So the ECDH cleared the client-key check; the remaining blocker is **authentication**: the 26-byte
v8 credential token must be a *valid* value derived from the ECDH shared secret (not zeros). This is
the token KDF/cipher — the part that is not yet reverse-engineered and that would require analyzing
AVEVA's native ExchangeKey/credential crypto to recover the derivation (the .NET-shipped result stays
pure managed either way). The "Path B-lite" hypothesis (zeroed token rides through after key
agreement) is therefore disproven at the auth layer — 2 of 3 layers are cleared, the 3rd is the
credential-token derivation. ExchangeKey + the v8 serializer are committed and ready; the orchestrator
stays on v6 (set `eventConnection: true` to re-arm once the token KDF lands). The token-loop routing
guardrail (`HistorianGrpcHandshakeRoutingTests`) was scoped to the closure so the legitimate
ExchangeKey call is allowed while still pinning that the Negotiate token loop never routes there.