Files
mxaccess/rust/crates/mxaccess-rpc/Cargo.toml
T
Joseph Doherty 1de049e114
rust / build / test / clippy / fmt (push) Has been cancelled
[F2] mxaccess-rpc: NTLM verify_signature (server-to-client) with constant-time MAC compare
Closes F2. Structural port from [MS-NLMP] §3.4.4 — same shape as
the existing sign path but uses the server-to-client sub-keys
(`SealKey_S→C` / `SignKey_S→C`) derived alongside the client-to-
server pair at the end of create_type3.

NtlmClientContext gained four new fields populated during
create_type3:
  - server_signing_key
  - server_sealing_key
  - server_sealing_state (independent RC4 stream)
  - server_sequence (independent counter)

The S→C key derivation already existed in auth.rs (the seal_key /
sign_key helpers take a client_mode flag); F2 plumbs them into a
new verify_signature(message, signature) method.

The verify path:
  1. Validates signature.len() == 16 + leading version word 0x01.
  2. Reads trailing seq num, compares against self.server_sequence
     (mismatch ⇒ InvalidSignature, no state change).
  3. Computes expected_mac = HMAC_MD5(server_signing_key,
     seq || message)[0..8] then RC4 transform.
  4. Constant-time compares expected_mac against wire bytes 4..12
     via subtle::ConstantTimeEq.
  5. On success: commits cipher-state advance + ++server_sequence.
     On failure: re-derives RC4 from server_sealing_key and skips
     past server_sequence × 8 keystream bytes to restore the
     pre-verify position — caller can retry.

New dep `subtle = "2"` (workspace-internal to mxaccess-rpc) for
the timing-oracle-safe MAC compare.

6 new tests:
  - verify_signature_round_trip_against_sign (3-message sequence
    via paired_authed_context helper that aliases server-side keys
    onto client-side for self-validating round-trip)
  - verify_signature_rejects_corrupted_mac (with
    server_sequence-non-advance assertion)
  - verify_signature_rejects_wrong_sequence_number
  - verify_signature_rejects_wrong_version_field
  - verify_signature_rejects_wrong_length
  - verify_signature_before_authenticate_errors

mxaccess-rpc 188 → 194 tests; default-feature clippy clean.

The "awaiting wire-fixture capture" step listed in F2's prior
status note is no longer a hard prerequisite — [MS-NLMP] §3.4.4
fully defines the algorithm and the round-trip tests prove the
encoder/decoder pair is internally consistent. A captured
StatusReceived frame would still validate byte-parity vs a real
NmxSvc.exe signer, but that's future verification work; the
structural port ships unblocked.

design/followups.md F2 moved to Resolved.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 03:30:48 -04:00

48 lines
1.5 KiB
TOML

[package]
name = "mxaccess-rpc"
description = "DCE/RPC PDU codec + NTLMv2 + OBJREF + OXID resolution + RemQI for the NMX transport."
version.workspace = true
edition.workspace = true
license.workspace = true
repository.workspace = true
rust-version.workspace = true
authors.workspace = true
[dependencies]
thiserror = { workspace = true }
tokio = { workspace = true }
hmac = "0.12"
md-5 = "0.10"
md4 = "0.10"
rc4 = "0.2"
rand = "0.8"
# F2 — constant-time MAC compare for verify_signature (server-to-client
# direction). subtle::ConstantTimeEq prevents timing oracles on the
# 8-byte MAC field of inbound NTLM-signed PDUs.
subtle = "2"
# F6 — Win32 OBJREF emitter via CoMarshalInterface. Optional, gated by the
# `windows-com` feature so the default footprint stays slim. windows-rs
# pulls a small set of submodules — Win32_System_Com for IUnknown / IStream
# / CoCreateInstance / CoMarshalInterface, Win32_System_Memory for
# GlobalLock / GlobalSize, Win32_System_Ole for the historical
# CreateStreamOnHGlobal / GetHGlobalFromStream re-exports.
windows = { version = "0.59", features = [
"Win32_Foundation",
"Win32_System_Com",
"Win32_System_Com_Marshal",
"Win32_System_Com_StructuredStorage",
"Win32_System_Memory",
], optional = true }
[features]
default = []
# Gates the Win32 OBJREF emitter port (`com_objref_provider` module). The
# module itself is `cfg(windows)`-gated so non-Windows builds with the
# feature on stay green (the `windows` crate compiles to stubs on
# non-Windows targets).
windows-com = ["dep:windows"]
[lints]
workspace = true