- 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>
6.0 KiB
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 #defines 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
CheckCrc154-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 ourCheckCrc15vs upstream'sCalculateCrc15at ~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)
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)
make -C "$WT" clean && make -C "$WT" PLATFORM=PM3GENERIC— ARM compiles.make -C "$WT" client— validatescmdhf15.c.python -m pytest tests/in pm3py — sim tests are transport-mocked, so they stay green; confirms no Python-side regression.- Hardware smoke test (user, has PM3): flash, then
hf 15scan + 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.cdiff for review before anything touchesmain. - Fast-forward
firmwaremain torebase-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 --forceare blocked — use-d/plainrm/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— pinUPSTREAM_TESTED_COMMIT=87388b389.- A real rebase-check CI (the existing
firmware/.github/workflows/ rebase.ymlis upstream's Changelog Reminder, not ours).