The CAN write gate (page-42/43 challenge/response) is a 32-round TEA/XTEA-family
Feistel keyed by a per-session 32-bit key; REMOTE_CONTROL = 0xB16B00B5. Verified
51/51 against captured challenge/response pairs across nodes 2A/61/75/F8 (one
global key, not per-node), so the CAN path can now actuate, not just sense.
- ids_can_auth.py Python reference + self-test (51/51)
- esphome/ids_can_auth.h C++ port for the ESP32 node (host-tested 8/8)
- sniff/analyze_auth.py structural analysis (rules out affine; confirms keyed cipher)
- sniff/auth-pairs-multinode-2026-06-11.txt +9 pairs across 4 nodes
- README document the cipher, session keys, unlock sequence
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Close propane valve, force a water-heater lockout, capture + diff against
healthy baseline. Prime suspects: node 95 b1 (always FF) or node AE page-3
(always 00). Whichever flips becomes the DSI binary_sensor.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Tapped the X180T's CAN bus via CANable 2.0 at the monitor panel's terminator
port. The bus is NOT RV-C — it's Lippert's proprietary IDS-CAN (250k, 11-bit
IDs, (page<<8)|node, 1 Hz broadcasts).
Read side fully mapped from live captures:
- device classes (page-2 type byte: 0x0A tank, 0x1E switched load, 0x21 motor)
- node map for this rig (Catalina 263BHSCK): tanks 27/E2/7D/FE, lights 2A/F8,
heater 95, pump 61, awning 75 (+ direction & live motor current)
- battery voltage on 29-bit extended frames
Write side: commands are DLC-0 ext frames 0006<node><op>, but auth-gated by a
rolling challenge-response (page 42/43). Replay confirmed dead (spoofed cansend
did not actuate). Not the BLE TEA cypher. response=f(challenge) is deterministic
(no session state) so crackable offline later — seeded 42 pairs in
sniff/2A-auth-pairs.txt.
Includes raw captures (sniff/*.log, force-added past *.log ignore), a read-only
esp32_can ESPHome skeleton, and the log-can.sh sniff helper. Full writeup in
canbus/README.md.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>