Dingtian: close password-less string-protocol relay-fire hole

The string protocol (UDP 60001) has no password field but can fire relays
("11" = relay 1 on), bypassing relay_pw entirely. Proven on hardware: an
unauthenticated packet opened a relay. harden() had left it enabled "for
status reads".

- #status() now reads via the authenticated binary command (relay cmd 0x00)
  instead of the string protocol, so the string protocol is no longer needed.
- harden() disables the string protocol (udp2.p=255). BEST-EFFORT: firmware
  V3.6J's config API silently refuses to disable udp2 (the device web UI can),
  so it's not part of the blocking verify -- harden() re-checks and returns a
  warning instead of throwing. After a web-UI disable, the attack is dead and
  binary control/status still work (verified on hardware).
- HardenResult gains an optional `warnings[]`; the assign route surfaces them
  to the admin and logs them.
- Corrected the false comment claiming relay_pw stops an attacker (it is
  defence-in-depth on plaintext UDP, not a boundary).
- Thread localAddress through the driver's UDP/HTTP calls so a multi-homed
  host sources device traffic from the device-facing NIC.
- Device web login (webUser/webPassword) is no longer redacted from setup
  state -- it's an operational credential for the admin-only device area;
  pushPassword/relayPassword stay machine-only.

Wiki: document the vuln + fix, the firmware caveat, and the out-of-band
actuation gap (the log captures host actions only; reconciliation vs. an
independent witness is the real control and is not yet built).
This commit is contained in:
2026-06-15 11:29:55 +02:00
parent add5fc0166
commit 7db5cfa0e4
6 changed files with 270 additions and 79 deletions
+36
View File
@@ -238,3 +238,39 @@ guarantee. Recorded in [[dingtian-relay]] (new Hardening section).
- Verified via Fastify inject: 2 printers assigned to one lane -> both listed, no secret leak,
delete -> 204, delete unknown -> 404, count drops to 1. Full repo typechecks (8/8).
- Updated [[first-run-setup]].
## [2026-06-15] ingest | Append-only signed event log (Dingtian input pushes persist)
- Q: does the Dingtian push events? -> inputs YES (input_link_url), relay opens NO (device keeps
no log). Host is the source of truth; a relay open w/o matching signed event is the anomaly.
- Implemented EventLog (apps/server/event-log.ts): serialized append, monotonic index, prevHash
chain, signature; verifyChain() detects tamper/reorder/delete. Read: GET /api/events;
integrity: GET /api/events/verify (admin).
- Signer abstraction (packages/shared) over the ATECC608; SoftwareSigner (HMAC, EVENT_SIGNING_KEY)
shipped now since chip wiring is open-question #6. Caveat documented: software signer is
tamper-evident but NOT unforgeable-by-owner.
- Wired bus -> log: Dingtian input pushes become input_received events (lane mapping TODO).
- Added ParkingEventType 'input_received'.
- Verified via inject: push w/o digest -> 401; pushes -> 2 signed+chained events; verify -> ok;
direct DB tamper -> verifyChain catches at the right index; deleted row -> index gap. 5 concurrent
appends -> indices 1..5 intact. Full repo typechecks.
- Updated [[append-only-event-chain]], [[dingtian-relay]].
## [2026-06-15] ingest | Event log + Dingtian string-protocol security fix
- Append-only signed event log shipped (EventLog, Signer abstraction over ATECC608 w/ SoftwareSigner
HMAC; GET /api/events + /api/events/verify). Dingtian input pushes persist as input_received.
Verified on hardware: shorting I1-I4 -> 8 signed+chained events, verifyChain ok.
- SECURITY (verified on hardware): the password-less string protocol (udp2) can fire relays
("11" -> relay1 on) with NO auth, bypassing relay_pw. Fixes: status reads moved to authenticated
binary read (cmd 0x00); harden() disables udp2 BEST-EFFORT (firmware V3.6J config API refuses,
but web UI works) and returns a warning instead of throwing. After web-UI disable, the "11" attack
is dead and binary control/status still work.
- GAP (user-identified): event log captures host-originated actions only; out-of-band relay
actuation (sniffed relay_pw, string protocol, ip_watchdog) produces NO event — proven on hardware.
Real control is reconciliation vs. an independent witness; witness+reconciliation NOT yet built.
- Device web login (webUser/webPassword) now un-redacted in setup state (admin-only device area);
pushPassword/relayPassword stay machine-only.
- harden() warnings surfaced via the assign response.
- localAddress threaded through the Dingtian driver (device-facing-IP foundation; multi-homed hosts).
- INCIDENT: probing default.cgi factory-reset the bench device (now at 192.168.1.100, defaults).
Re-provisioning is the ADMIN's job via First-run setup (app must not hardcode site IPs).
- Updated [[append-only-event-chain]], [[dingtian-relay]].