forked from arti/vt1200
dwt: runtime oracle and ground-truth vectors for the dwt_prf challenge transform
This commit is contained in:
1 parent
5645bb00b3
commit
041c82cb73
31 files changed
+7367
No files matched your search
@@ -197,3 +197,54 @@ tap, BOOTP address 192.168.99.2):
|
||||
The tap (`tap-mess-1000-0` / bridge `vt1200`) is free again as of 02:40; the
|
||||
MAME instance and the BOOTP server are stopped. Full write-up:
|
||||
`dwt/notes/EXPLOIT_ATTEMPT.md`; transcript and logs under `dwt/evidence/`.
|
||||
|
||||
## 2026-10-18 02:42 EEST — oracle agent: dwt_prf transform ground truth is captured
|
||||
|
||||
Full method and caveats: `dwt/notes/CHALLENGE_ORACLE.md`.
|
||||
|
||||
**Vectors:** `dwt/challenge_vectors.json` — a top-level JSON **array** of 180
|
||||
instances (177 distinct inputs). Load with
|
||||
`vectors = json.load(open("dwt/challenge_vectors.json"))`; each entry is
|
||||
`{"input_hex": <32 hex>, "output_hex": <32 hex>, "meta": {...}}`.
|
||||
`input_hex` is the 16-byte buffer **after** dwt_prf's per-word `ntohl` (the
|
||||
transform's actual input); `output_hex` is `f(input)` in place. The full
|
||||
128-byte key schedule and seed-block dumps are in
|
||||
`dwt/evidence/oracle/runA/stdout.log`.
|
||||
|
||||
**Direction (confirmed):** the firmware transforms the reply, not the
|
||||
challenge. With host-order challenge words `W`, wire challenge
|
||||
`C = ntohl(W)` (per-32-bit-word byte swap), wire reply `R`:
|
||||
`X = ntohl(R)`, `Y = f(X)`, and dwt_prf memcmp's `Y` against `W`.
|
||||
Pass condition: `f(ntohl(R)) == W`, i.e.
|
||||
`R = ntohl(f^-1(W)) = ntohl(f^-1(ntohl(C)))`. The vectors give `f`, so the
|
||||
implementation needs the inverse (and the outer word swap).
|
||||
|
||||
**Determinism:** yes. 177 distinct inputs give identical outputs on two
|
||||
fresh-NVRAM boots, and on a third boot that hijacks at a different frame.
|
||||
Seed block `0x1BE810` and the key schedule `@0x1C0260` are byte-identical
|
||||
across boots — the key is genuinely fixed for this image.
|
||||
|
||||
**Per-8-byte independence:** yes. Records are processed separately: changing
|
||||
only bytes 0–7 never changes output bytes 8–15 (0/64 single-bit trials), and
|
||||
32 independence-grid pairs agree. Output for a doubled record is the doubled
|
||||
output. Permutation on samples: 152 distinct inputs → 152 distinct outputs, no
|
||||
fixed points. Avalanche: one input bit flips **32.66 of 64** first-record
|
||||
output bits on average (23–42).
|
||||
|
||||
**Two reversing shortcuts found while building the oracle:**
|
||||
|
||||
1. **Argument order for a direct call.** `0x3F5A09C0` expects `(len, buf)` in
|
||||
source order, i.e. `buf` must be the **last** argument written (the callee
|
||||
reads `*a13(0xffe0)` as the pointer and `*a13(0xffc0)` as the length).
|
||||
Writing `(buf, 16)` gives a pointer of 16 and will not work.
|
||||
2. **The two "font render" calls are identity copies on this path.** dwt_prf
|
||||
calls INIT with mode **1** (`0x3F5A9620(1)`); that mode leaves
|
||||
`@0x1C0220`/`@0x1C0240` NULL, so `0x3F5ABA40`'s two render calls take their
|
||||
memcpy branch. Captured per-record intermediates
|
||||
(`dwt/evidence/oracle/stages.txt`) show `feistel_in == input` and
|
||||
`pre_final_render == output` for all six samples. The effective transform is
|
||||
the 16-round Feistel over the key schedule expanded from `0x1BE810` (+ the
|
||||
`0x1BED40/0x1BEF00/0x1BEF80` auxiliary blocks).
|
||||
|
||||
Reproduce: `python3 dwt/oracle.py` (~35 s, two boots + stage capture). No ROM,
|
||||
driver or `dwt_prf` code was modified; no socket was used for the vectors.
|
||||
Reference in new issue
Block a user