- Add docs/plans/README.md as the roadmap index cataloguing all design/plan docs. - Add the custom ISO14443-A command handling design + plan (L3 NTAG I2C SECTOR_SELECT / cross-sector reads + native auth; L4 static APDUs + WTX relay), produced from a multi-agent design workflow. - Backfill previously-uncommitted plan docs (trace-formatter, ndef-trace-decode, live-sniff, firmware-upstream-rebase, 14a-live-trace) and NTAG5_SECURITY.md. - Sync CLAUDE.md package structure with the committed transponder models. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
132 lines
6.0 KiB
Markdown
132 lines
6.0 KiB
Markdown
# Firmware Upstream Rebase Plan (2026-07-03)
|
||
|
||
Rebase the `firmware/` submodule (fork `dangerous-tac0s/proxmark3-pm3py`)
|
||
onto current RRG `upstream/master` **without losing our sim / sniff /
|
||
inventory patch**. Backed by a non-destructive dry-run (worktree + merge-tree).
|
||
|
||
## Current state (measured)
|
||
|
||
- **merge-base:** `c7086c227` (2026-02-20) — where we branched.
|
||
- **our patch:** 33 commits on top of merge-base (main == `35db99fa5`).
|
||
- **upstream ahead:** 1049 commits (tip `87388b389`, 2026-07-03) — ~4.5 months.
|
||
- Firmware is on `main`; ALM branch already scrapped.
|
||
|
||
## Dry-run findings — the conflict surface is small and concentrated
|
||
|
||
A **squash-then-rebase** (all 33 commits applied as one combined diff, per
|
||
`git merge-tree`) conflicts in **exactly 3 files**; everything else
|
||
auto-merges or is untouched:
|
||
|
||
| File | Verdict | ours vs upstream (lines) |
|
||
|------|---------|--------------------------|
|
||
| `armsrc/iso15693.c` | ⚠️ real work — 13 hunks | +599/−146 vs +523/−214 |
|
||
| `client/src/cmdhf15.c` | ⚠️ moderate | +372/−7 vs +544/−45 |
|
||
| `include/pm3_cmd.h` | ✅ trivial (textual re-append) | +21/−0 vs +94/−113 |
|
||
| `appmain.c`, `iclass.c`, `iso15693.h`, `iso15.h`, `Makefile` | ✅ auto-merge | — |
|
||
| `sim_table.c/h`, `sim_crypto.c/h`, `Standalone/hf_15sniff.c`, `Standalone/hf_unisniff.c` | ✅ upstream never touched | — |
|
||
|
||
A **sequential** rebase (replay 33 commits) is worse: it conflicts on
|
||
commit #1 and, because **24 of the 33 commits touch `iso15693.c`**, it
|
||
re-conflicts that file many times. → **Squash first, resolve once.**
|
||
|
||
### CMD-ID collision check — CLEAR ✅
|
||
|
||
All six IDs we added are free in upstream (upstream's `0x033x` block only
|
||
reaches `CSETUID_V2 0x0333`; `0x095x` is empty):
|
||
|
||
```
|
||
0x0336 CMD_HF_ISO15693_SIM_TRACE 0x0950 CMD_SIM_TABLE_UPLOAD
|
||
0x0337 CMD_HF_SNIFF_STREAM 0x0951 CMD_SIM_TABLE_CLEAR
|
||
0x0338 CMD_HF_SNIFF_STATUS 0x0952 CMD_SIM_TABLE_UPDATE
|
||
```
|
||
|
||
So `pm3_cmd.h` is a pure textual re-append (drop our `#define`s at a clean
|
||
spot in the churned region), **not** a semantic collision.
|
||
|
||
### `iso15693.c` hunk map (13 hunks, 2 functions)
|
||
|
||
- **15693 sniff loop (~L1598–1853):** our streaming-sniff feature (ring
|
||
buffer, button-toggle, idle flush) vs upstream sniff edits. Biggest hunk
|
||
~53 lines (our idle/flush block) — mostly *keep-ours-add-both*.
|
||
- **SimTag / sim loop (~L2475–3372):** our sim handler (table lookup,
|
||
access control, random UID, periodic field report) + the `CheckCrc15`
|
||
4-byte-frame fix vs upstream. ~10 hunks: three large (span 36–43, our
|
||
inserted blocks → take-ours), the rest small (≤16) needing genuine
|
||
line-level reconciliation — notably **our `CheckCrc15` vs upstream's
|
||
`CalculateCrc15`** at ~L2591 (keep ours; it fixes 4-byte frames).
|
||
|
||
Effort estimate: **~half a day** of focused merge work, dominated by
|
||
`iso15693.c`; `cmdhf15.c` moderate; `pm3_cmd.h` ~10 min.
|
||
|
||
## Strategy: squash-to-one → rebase (single 3-file resolution) → validate → re-split
|
||
|
||
Resolve conflicts exactly once (matching the merge-tree preview), validate
|
||
the build, then restore clean per-file commits for cheap future rebases
|
||
(the design-doc "atomic single-file commit" model).
|
||
|
||
### Procedure (all in an isolated worktree — real checkout never moves)
|
||
|
||
```bash
|
||
FW=/home/work/pm3py/firmware
|
||
WT=<scratchpad>/fw-rebase
|
||
git -C $FW fetch upstream master
|
||
git -C $FW worktree add -b rebase-wip "$WT" 35db99fa5
|
||
|
||
cd "$WT"
|
||
# 1. collapse 33 commits into one diff on our base (no upstream yet → no conflicts)
|
||
MB=$(git merge-base HEAD upstream/master)
|
||
git reset --soft "$MB"
|
||
git commit -m "pm3py sim+sniff+inventory patch (squashed for rebase)"
|
||
|
||
# 2. rebase the single commit → conflicts in the 3 known files, resolved once
|
||
git rebase upstream/master # resolve iso15693.c, cmdhf15.c, pm3_cmd.h
|
||
# ... resolve, git add, git rebase --continue
|
||
|
||
# 3. re-split into logical per-file commits (soft reset + staged adds)
|
||
git reset --soft upstream/master
|
||
# commit A: sim_table.c/h + sim_crypto.c/h (new modules)
|
||
# commit B: pm3_cmd.h + iso15.h + iso15693.h (IDs + struct fields)
|
||
# commit C: appmain.c (dispatch)
|
||
# commit D: iso15693.c (sim + sniff + inventory)
|
||
# commit E: cmdhf15.c (client reader/inventory)
|
||
# commit F: Makefile + Standalone/* (build glue)
|
||
```
|
||
|
||
### Validation (all local per build-workflow — ARM builds here, not the Mac)
|
||
|
||
1. `make -C "$WT" clean && make -C "$WT" PLATFORM=PM3GENERIC` — ARM compiles.
|
||
2. `make -C "$WT" client` — validates `cmdhf15.c`.
|
||
3. `python -m pytest tests/` in pm3py — sim tests are transport-mocked, so
|
||
they stay green; confirms no Python-side regression.
|
||
4. **Hardware smoke test (user, has PM3):** flash, then `hf 15` scan +
|
||
15693 sim + a phone read (the known-good sim path). This is the only
|
||
check the dry-run can't cover — upstream may have altered 15693 wire
|
||
behavior around our insertions.
|
||
|
||
### Landing
|
||
|
||
- Show resolved `iso15693.c` diff for review **before** anything touches
|
||
`main`.
|
||
- Fast-forward `firmware` main to `rebase-wip`, bump the submodule pointer
|
||
in a pm3py commit, force-push the fork (`--force-with-lease`).
|
||
- Remove the worktree (`git worktree remove`, no `--force`).
|
||
|
||
## Risks & rollback
|
||
|
||
- **Semantic drift in `iso15693.c`:** upstream may have changed shared
|
||
helpers our sim path calls. Compile catches signature breaks; behavior
|
||
needs the hardware smoke test.
|
||
- **Rollback:** old main is preserved. Recovery ref for pre-rebase tip:
|
||
`35db99fa5`. Nothing is force-pushed until build + review pass.
|
||
- **Safety Net:** `branch -D`, `rm -rf`, `git clean -f`, `worktree remove
|
||
--force` are blocked — use `-d`/plain `rm`/`worktree remove` (no force),
|
||
or hand force-ops to the user.
|
||
|
||
## Go-forward (close the gap that let us drift 1049 commits)
|
||
|
||
From the earlier maintenance discussion, still unbuilt:
|
||
- `pm3py/firmware_compat.py` — pin `UPSTREAM_TESTED_COMMIT` = `87388b389`.
|
||
- A **real** rebase-check CI (the existing `firmware/.github/workflows/
|
||
rebase.yml` is upstream's Changelog Reminder, not ours).
|
||
```
|