Ports the proven subset of `[MC-NBFX]` to `mxaccess-asb-nettcp::nbfx`.
Token model: Element { prefix, name } / EndElement / Attribute /
DefaultNamespace / NamespaceDeclaration / Text. Element + attribute
names can be inline UTF-8, an `[MC-NBFS]` static-dictionary id (via
F22's `lookup_static`), or a per-session `DynamicDictionary` id.
Text records covered: Empty (0xA8), Zero (0x80), One (0x82), Bool
(0x84/0x86 + 0xB4), Int8 (0x88), Int16 (0x8A), Int32 (0x8C), Int64
(0x8E), Chars (0x98/0x9A/0x9C — width variant chosen automatically by
payload length), DictionaryText (0xAA — both static and dynamic refs).
`*WithEndElement` collapse is automatic: a `Text → EndElement` pair
encodes as the `+1` record byte (e.g. `EmptyTextWithEndElement = 0xA9`,
`TrueTextWithEndElement = 0x87`). The decoder splits the implicit
EndElement back out so consumers see the same token stream regardless
of which wire form was used.
Element variants covered: ShortElement (0x40), Element (0x41 with
prefix string), ShortDictionaryElement (0x42), DictionaryElement
(0x43). Prefix-letter family (0x44-0x77) deferred — emit the long
form for now.
Attribute variants covered: ShortAttribute (0x04), Attribute (0x05),
ShortDictionaryAttribute (0x06), DictionaryAttribute (0x07), plus
xmlns variants (0x08/0x09).
15 new unit tests cover the dynamic dictionary, every supported
element/attribute/xmlns/text record form (including round-trip),
explicit byte pinning for the collapse behavior, Chars width-variant
selection, unknown-record rejection, and truncated-payload rejection.
Records left for follow-up: Decimal, UniqueId, TimeSpan, Float/Double
text, DateTime text, Bytes8/16/32, QNameDictionary, the 0x0C-0x25
prefix-dict-attribute / 0x26-0x3F prefix-attribute / 0x44-0x77
prefix-element families. None of these are on the proven ASB path.
With F21 landed, the M5 framing + encoder layer (streams A+B+C+D and
the F24 codec) is complete. F25 (mxaccess-asb IASBIDataV2 client) and
F26 (Session over AsbTransport) remain.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Ports the curated subset of the `[MC-NBFS]` §2.2 static dictionary to
`mxaccess-asb-nettcp::nbfs`. Approximately 80 entries covering SOAP 1.2
envelope tokens, WS-Addressing 1.0 tokens, WS-RM, WS-Security,
WS-Trust/SecureConversation, XML Schema Instance primitives, plus the
common XML element / attribute names captured in
`analysis/proxy/mxasbclient-*` traces.
API:
* `STATIC_ENTRIES: &[StaticEntry]` — sorted-by-id table; one-line
extension when wire captures show new IDs.
* `lookup_static(id) -> Option<&'static str>` — binary-search lookup
for the F21 NBFX decoder.
* `position_of_static(value) -> Option<u32>` — `OnceLock`-cached
reverse lookup for the F21 NBFX encoder.
Lookups outside the curated subset return `None`. The NBFX decoder
will surface that as a typed `UnknownStaticDictionaryId` error so the
caller knows to either extend the table or fall through to the
inline-string path. The full 487-entry table is bounded but tedious;
the deliberate subset keeps source size down while remaining
extensible.
ASB-specific contract strings (`http://ASB.IDataV2`,
`http://asb.contracts/20111111`, the IASBIDataV2 operation actions,
etc.) are intentionally **not** in the static dictionary — they live
in the per-session dynamic dictionary that the F21 NBFX codec builds
up via `DictionaryString` records.
6 unit tests cover monotonic-id invariant, known-id lookup,
unknown-id rejection, round-trip lookup consistency, and the
empty-string slot at id=142.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Implements the 13 record types from `[MS-NMF]` §2.2 (Version, Mode, Via,
KnownEncoding, ExtensibleEncoding, Unsized/SizedEnvelope, End, Fault,
UpgradeRequest/Response, PreambleAck, PreambleEnd) over a `net.tcp` channel.
Includes the `Multibyte Int31` length codec (LEB128-style 7-bit groups
over a 31-bit unsigned range, max 5 bytes; rejects negative input and
overflow), plus an `encode_preamble` helper that emits the canonical ASB
connect record sequence (`Version 1.0 → Duplex → Via $uri →
BinaryWithDictionary → PreambleEnd`).
Pure codec — no I/O. Encoders write into a `Vec<u8>`; decoders parse
from a `&[u8]` slice and return the consumed-byte count alongside the
record. Higher-level connect/request/response orchestration stays in the
M5 ASB client (`mxaccess-asb`, F25).
24 new unit tests cover round-trip for every record type, multibyte-int31
boundary cases (0, 1, 127, 128, 16383, 16384, 200, i32::MAX), preamble
emission against the canonical ASB sequence, byte-layout pinning for
Version/Mode/KnownEncoding, and rejection of unknown record/mode/encoding
bytes plus truncated sized-envelope frames.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
F18 plans M5 as 9 sub-followups (F18-F26 + F27 constant-time DH) per
design/dependencies.md:73-89. Wave-1 streams F20-F23+F24 are parallel-safe
after F19 (workspace deps). F25 (ASB client) is sequential after the
framing/encoder streams. F26 (Session over AsbTransport) is sequential
after F25.
F19 — workspace deps for the M5 crypto + framing surface: hmac, md-5,
sha1, sha2, aes, cbc, pbkdf2, flate2, rand, num-bigint, num-traits,
num-integer, quick-xml, tokio-util, zeroize. Pinned to the digest 0.10 /
cipher 0.4 generation matching mxaccess-rpc.
F23 — ports `AsbSystemAuthenticator.cs` (167 LoC) to
`mxaccess-asb-nettcp::auth`. Wire-byte parity points: .NET BigInteger
little-endian two's-complement byte order with optional 0x00 sign-byte
suffix; AES-128-CBC with PKCS7 padding; PBKDF2-SHA1 1000 iterations
over `Convert.ToBase64String(crypto_key)` with ASCII salt
"ArchestrAService"; deflate-then-AES (Baktun) vs raw-AES (Apollo)
selected by `:V2` lifetime suffix; HMAC-MD5/SHA1/SHA512 negotiated per
`AsbSolutionCryptoParameters.HashAlgorithm` (with `force_hmac=true`
fallback to HMAC-SHA1 for unrecognised algorithms).
13 unit tests cover the cryptographic primitives + DH peer agreement +
.NET byte-order round-trip + Apollo lifetime dispatch.
F27 — filed for the `num-bigint` → `crypto-bigint::BoxedUint` swap once
the latter exposes a stable heap-allocated `pow_mod`. Currently at
parity with the .NET reference (also not constant-time).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>