ce27b63010
Adds end-to-end byte-equality test against a `.NET reference fixture captured via the new `MxAsbClient.Probe --dump-deterministic-hmac` flag. All inputs are pinned (passphrase, prime, generator, private- key bytes, remote-pub bytes, message number, connection ID, AES IV, consumer-data + IV bytes), so the test reproduces .NET's exact output for every crypto step: 1. shared = remote_pub^private_key mod prime — ✅ matches 2. crypto_key = shared || passphrase_utf8 — ✅ matches 3. hmac = HMAC-SHA1(crypto_key, xml_utf8) — ✅ matches 4. aes_key = PBKDF2-SHA1(base64(crypto_key), salt, 1000, 16) — ✅ 5. encrypted_mac = AES-CBC(aes_key, iv=zeros, hmac, PKCS7) — ✅ This conclusively rules out the entire crypto stack as the source of the live AuthenticateMe `dispatcher/fault`. Our DH math, HMAC engine, PBKDF2 derivation, AES-CBC PKCS7, and crypto_key concatenation are byte-equal to .NET. The remaining live failure must come from one of: (a) wire-level ConnectionValidator NBFX shape (DataContract field names, mustUnderstand attribute, namespace), (b) WCF binary message header (action+to dict pre-pop), or (c) a subtle XmlSerializer quirk for live values that the hardcoded fixtures don't exercise (Guid format edge case, base64 line wrapping, ulong text rendering). Fixture lives at `crates/mxaccess-asb-nettcp/tests/fixtures/ deterministic-hmac/authenticate-me.kv` (KV format, ASCII hex, lines trim CRLF/LF transparently). The companion `README.md` documents the capture procedure and the per-step decomposition. The test consumes the .NET-supplied canonical XML directly from the fixture's `xml_utf8_b64` so a Rust XML emitter bug would not mask a Rust crypto bug — XML byte-equality is verified separately by `mxaccess-asb::xml_canonical::tests` against the `signed-xml/*.xml` fixtures. Workspace: 710 unit tests pass (was 709 + 1 new). Clippy clean. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
45 lines
1.8 KiB
Markdown
45 lines
1.8 KiB
Markdown
# Deterministic HMAC fixture
|
|
|
|
Pinned input/output triple for the `AsbSystemAuthenticator.Sign`
|
|
crypto path, captured from the .NET reference. Used by the Rust
|
|
parity test in `crates/mxaccess-asb-nettcp/tests/deterministic_hmac.rs`
|
|
to assert byte-equality of crypto_key derivation, canonical XML
|
|
emission, HMAC-SHA1, PBKDF2-SHA1 AES key derivation, and AES-CBC
|
|
encryption — independent of session randomness (DH private key,
|
|
remote public key, and AES IV are all pinned to deterministic values
|
|
so a single `cargo test` run can reproduce the .NET output).
|
|
|
|
## Capture procedure
|
|
|
|
```powershell
|
|
dotnet run --project src\MxAsbClient.Probe -c Release -- --dump-deterministic-hmac > capture.txt
|
|
```
|
|
|
|
The probe's `--dump-deterministic-hmac` flag (added 2026-05-05)
|
|
inlines the per-step decomposition of `Sign` (`AsbSystemAuthenticator
|
|
.cs:62-82`):
|
|
|
|
1. `shared = remote_pub^private_key mod prime` (.NET `BigInteger.ModPow`)
|
|
2. `crypto_key = shared || passphrase_utf8`
|
|
3. `xml = AuthenticateMe.ToXml()` with empty MAC + IV
|
|
4. `hmac = HMAC-SHA1(crypto_key, utf8(xml))`
|
|
5. `aes_key = PBKDF2-SHA1(base64(crypto_key), "ArchestrAService", 1000, 16)`
|
|
6. `encrypted_mac = AES-CBC(aes_key, iv=zeros, hmac, padding=PKCS7)`
|
|
|
|
Step 6 uses an all-zero IV to make the test reproducible — the real
|
|
wire path uses a random IV per call, but the Rust test bypasses the
|
|
random IV path by calling the AES primitive directly with the same
|
|
zero IV.
|
|
|
|
## File format
|
|
|
|
Plain-ASCII `key=value` lines, one per line. Hex values are
|
|
upper-case (matching .NET's `Convert.ToHexString`). The `xml_utf8_b64`
|
|
field encodes the canonical XML as base64 of the UTF-8 bytes.
|
|
|
|
## Files
|
|
|
|
- `authenticate-me.kv` — fixture for the `AuthenticateMe` shape with
|
|
the `[XmlType(Namespace="http://asb.contracts.data/20111111")]`
|
|
ConsumerAuthenticationData wrapper.
|