# Signed-request XML fixtures Canonical `XmlSerializer` output for every `ConnectedRequest` shape that the .NET reference HMACs in `AsbSystemAuthenticator.Sign` (`src/MxAsbClient/AsbSystemAuthenticator.cs:79`). The Rust port's canonical-XML emitter (F28) must produce these exact UTF-8 bytes for the HMAC to match the server's recomputation. ## Capture procedure ```powershell dotnet run --project src\MxAsbClient.Probe -c Release -- --dump-signed-xml > capture.txt ``` The probe's `--dump-signed-xml` flag (added 2026-05-05) builds each shape with deterministic field values and prints the output of `AsbSerialization.ToXml(...)` (`src/MxAsbClient/AsbSerialization.cs:12`). ## Pinned values All shapes use the same `ConnectionValidator`: - `ConnectionId = 8cba964a-74c1-ef74-f6aa-761b3540191b` - `MessageNumber = 42` - `MessageAuthenticationCode = AAECAwQFBgcICQoLDA0ODw==` (base64 of bytes 0..15) - `SignatureInitializationVector = EBESExQVFhcYGRobHB0eHw==` (base64 of bytes 16..31) `AuthenticateMe` and `Disconnect` use `AuthenticationData` with: - `Data = "deterministic-ciphertext-bytes"` (base64-encoded) - `InitializationVector = "0123456789abcdef"` (base64-encoded) `RegisterItemsRequest` uses one `ItemIdentity` with `Type = Name (0)`, `ReferenceType = Absolute (1)`, `Name = "TestChildObject.TestInt"`, `ContextName = ""`. `UnregisterItemsRequest` uses one `ItemIdentity` with `Type = Id (1)`, `ReferenceType = Absolute (1)`, `Name = null`, `ContextName = null`, `Id = 0xCAFEBABEDEADBEEF (14627333968688430831)`, `IdSpecified = true`. ## Observed serialiser behaviour These rules were inferred from the captured output and from the .NET source for `XmlSerializer`: 1. **Element name = class name**, NOT `[MessageContract.WrapperName]`. `XmlSerializer` does not honour WCF's MessageContract attributes. 2. **Top-element xmlns ordering** (after ``): `xmlns:xsi`, then `xmlns:xsd`, then default `xmlns`. The `AsbSerialization.ToXml` post-process (`AsbSerialization.cs:36-47`) reparses with `XDocument.Load` and reorders to put `xsi` before `xsd` — `XmlSerializer`'s native order is the opposite. 3. **Field order = C# declaration order** (with inherited fields first), NOT `[MessageBodyMember.Order]`. 4. **`[XmlType(Namespace = ...)]` on a field's type** triggers an `xmlns="..."` redeclaration on EACH child element of that type's instance, NOT on the wrapper element itself. e.g. inside ``, every direct child gets `xmlns="http://asb.contracts.data/20111111"`. 5. **`byte[]` fields** serialise as base64 text content. **`Guid`** as canonical lowercase D-format (`8cba964a-74c1-...`). **`ulong`** as decimal. **`bool`** as `"true"` / `"false"`. 6. **Null reference-type fields** with `[XmlElement(IsNullable = true)]` produce ``. Empty string fields produce a self-closing ``. 7. **`*Specified` pattern**: a public bool field named `XxxSpecified` = `true` causes XmlSerializer to emit the corresponding `` element. `IdSpecified = false` (default) → `` omitted. `IdSpecified = true` → `` emitted with the int value. The `*Specified` field itself is `[XmlIgnore]` and never emitted. 8. **Self-closing elements** use ` />` (space before `/>`). 9. **Indentation**: 2 spaces, `\r\n` line endings, no trailing newline after the closing tag. 10. **XML declaration**: `` — note `utf-16` even though `AsbSystemAuthenticator.Sign` HMACs `Encoding.UTF8.GetBytes(...)` of this string. The declaration is a static .NET StringWriter default; the actual byte encoding fed to HMAC is UTF-8. ## Files - `authenticate-me.xml` — `AuthenticateMe` - `authenticate-me-empty-mac-iv.xml` — `AuthenticateMe` with the pre-signing validator (empty MAC + IV) — the actual HMAC input shape. - `disconnect.xml` — `Disconnect` - `keep-alive.xml` — `KeepAlive` - `register-items.xml` — `RegisterItemsRequest` - `unregister-items.xml` — `UnregisterItemsRequest` The eight remaining `ConnectedRequest` shapes added 2026-05-06 (F28 step 2) cover the data-plane + subscription ops: - `read-request.xml` — `ReadRequest` - `write-basic-request.xml` — `WriteBasicRequest` - `publish-write-complete-request.xml` — `PublishWriteCompleteRequest` - `create-subscription-request.xml` — `CreateSubscriptionRequest` - `delete-subscription-request.xml` — `DeleteSubscriptionRequest` - `add-monitored-items-request.xml` — `AddMonitoredItemsRequest` - `delete-monitored-items-request.xml` — `DeleteMonitoredItemsRequest` - `publish-request.xml` — `PublishRequest` Pinned values for the new shapes (in addition to the `ConnectionValidator` above): - `SubscriptionId = 0x1234_5678_9abc_def0` (decimal `1311768467463790320`) - `MaxQueueSize = 100`, `SampleInterval = 1000` - `WriteHandle = 0xDEAD_BEEF` (decimal `3735928559`) - `WriteBasicRequest` uses one `WriteValue` whose `Value` is `Variant.FromInt32(42)` (`Type=4`, `Length=4`, `Payload=[42, 0, 0, 0]`) - `AddMonitoredItemsRequest` uses one `MonitoredItem` with `Item = "TestChildObject.TestInt"` by name + `SampleInterval=1000` + `Buffered=false` (other fields default) - `DeleteMonitoredItemsRequest` uses one `MonitoredItem` with `Item.Id = 0xCAFE_BABE_DEAD_BEEF` (the same `IdSpecified` shape as `unregister-items.xml`) Each file is the verbatim UTF-8 representation of `request.ToXml()`, with literal `\r\n` line endings preserved. Treat as binary (don't let your editor reformat).