Radix 16 (live-verified), universal reply splitter, golden capture vectors, host_probe6
This commit is contained in:
1 parent
0cd6b34a2e
commit
62e0fd649b
10 files changed
+418
-82
No files matched your search
+33
-12
@@ -161,14 +161,20 @@ unpadded. E.g. SN `A03X2516013210060005` → byte sum `1052 & 0xFF = 28` → `"1
|
||||
2. `S1 = parts[0] + "," + parts[1] + ","` (header, *with* trailing comma);
|
||||
`a = UTF-8(S1)`.
|
||||
3. `S2 = join(",", parts[2..])` (the rest); `b = UTF-8(S2)`.
|
||||
4. `keyInt = int.tryParse(key, radix: 32)` (literal `0x20` at the call site;
|
||||
null → 0).
|
||||
4. `keyInt = int.tryParse(key)` (default radix **16** — two live frames
|
||||
decrypt cleanly only with radix 16 and then verify byte-exact on
|
||||
`len`/CRC; an earlier radix-32 reading of a nearby `0x20` literal is
|
||||
superseded).
|
||||
5. `out[i] = (b[i] + i + len(a) + keyInt) & 0xFF` for all i (so each tail
|
||||
byte is shifted by its *absolute* JSON index plus the key).
|
||||
6. Result = `a ++ out` — the plaintext header goes on the wire **first**,
|
||||
transformed tail after (`addAll` onto the header copy).
|
||||
|
||||
**`decryptData(bytes)`** (single arg, the accumulated receive buffer):
|
||||
as recovered from the disassembly (two-`0x58`-marker scan, `s + "}"`
|
||||
equipNo probe, `s + "," + T` return). NOTE (2026-10-16): live socket
|
||||
replies decrypt with the *request-side* split instead (see below); the
|
||||
marker model is kept for the unverified Bluetooth path.
|
||||
1. `i = indexWhere(byte == 0x58)`, `j = indexWhere(byte == 0x58, start: i+1)`;
|
||||
either missing → return `""`.
|
||||
2. `s = utf8(wire[0..j])`; `probe = jsonDecode(s + "}")`, null → `""`.
|
||||
@@ -273,17 +279,32 @@ Full TCP sweep of `10.100.1.1` (ports 1–65535): exactly **two** ports open —
|
||||
for *every* probe (TLS ClientHello, HTTP GET, MQTT CONNECT, exact-spec
|
||||
envelopes raw/split/length-prefixed, greeting-first reads).
|
||||
|
||||
### Update (2026-10-15) — socket cmd space + live capture
|
||||
### Update (2026-10-16) — binary capture fully decoded
|
||||
|
||||
All pre-2026-10-15 probes used `off_8` ids (`25/0/29/30`) and were dropped
|
||||
silently — the socket transport speaks the `off_14` id space (§4), so the
|
||||
device saw unknown commands. A PCAPdroid capture of the working app shows
|
||||
real socket frames: `{"cmdType":1101,…}` (queryDeviceInfo, empty SN) and
|
||||
`{"cmdType":1102,"equipNo":"1006005A03X25160132",…}` (922 B). Open:
|
||||
dst IP:port + direction per frame (need capture metadata / full `.pcap`),
|
||||
the meaning of `1102` (no pool cmd maps to `0x44E`), and the 17-char
|
||||
`equipNo` (sub-device? battery unit? reply convention?). Current probes
|
||||
use socket-space ids (`1101/1307/1705/1707`) via `scripts/host_probe3.py`.
|
||||
Raw `.bin` payloads (no text mangling) decrypt completely offline with
|
||||
radix 16 (`tests/fixtures/*.bin` are golden vectors):
|
||||
|
||||
- Discovery request (153 B): `{"cmdType":1101,"equipNo":"00000000",…,
|
||||
"dataArea":{"opt":0,"minProtocolVer":"V1.00.00"}}`, key `"80"`/n=128.
|
||||
- Device-info reply (1154 B): `cmdType` **1102**, `equipNo`
|
||||
`10060005A03X25160132` (the unit's IBG SN — label SN
|
||||
`A03X2516013210060005` shares its trailing 12 chars), `len` 1024 exact,
|
||||
CRC exact, `dataArea.result == 0` with the full version inventory
|
||||
(`FHP_NUM: 2`, two `FHP_SN`, two `BMS_SN`, `protocolVer V1.12.36`).
|
||||
Encrypted with the **gateway** key (`"1c"`/n=28) while addressed to the
|
||||
IBG SN — the gateway is the crypto endpoint; sub-unit addressing rides
|
||||
inside.
|
||||
|
||||
Reply rules learned: same `S1 || transform(S2)` framing both directions;
|
||||
reply id observed as request + 1 (`1101 → 1102`); universal split at the
|
||||
2nd comma needs no ids up front (`decrypt_wire`). The remaining unknown
|
||||
is the live endpoint (dst IP:port + direction per capture).
|
||||
|
||||
Superseded notes: pre-2026-10-16 probes additionally used the wrong key
|
||||
radix (32 instead of 16) and mostly omitted the `opt`-seeded payload, so
|
||||
every silent run is fully explained by bytes, with no need for exotic
|
||||
causes. Current probes use socket-space ids + radix 16 + `opt` payloads
|
||||
via `scripts/host_probe6.py`.
|
||||
|
||||
## 14. Direct-connect flow (manual AP join → return to app)
|
||||
|
||||
|
||||
Reference in new issue
Block a user