feat: MIFARE stored-value awareness — identify, verify --deep, fidelity, value command - #2
Merged
Merged
Conversation
Decodes value blocks (laundry/vending balances, with USD-cents interpretation), surfaces printable ASCII markers, and emits a compact hex dump with zero-runs collapsed. Locates dumps by UID via tag.dumpFile, ~/hf-mf-<UID>-dump.bin, or CWD — so existing tags saved before the dumpFile field will still work. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
`verify` only compares one chosen identity by UID, so it green-lights a UID-only clone (correct UID, empty data sectors, factory keys) that a stored-value reader rejects as unformatted. `identify` reads the tag on the antenna and cross-references every saved identity at once, and for MIFARE Classic probes data fidelity — reading a data block with the default key to tell a full clone (custom sector keys) from a UID-only clone (blank/default). - add probeMifareDataFidelity() + parseReadBlock() with the default-key probe - add DEFAULT_MIFARE_KEY constant - register `identify` in the CLI and interactive menu - tests for the parser, the probe, and all identify branches - README: document the command and the full-vs-UID-only distinction Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
`verify` matched on UID alone, so a UID-only clone (right UID, empty data) passed. `--deep` reads the live card's value blocks with the keys from the saved dump, reports the current on-card balances, and fails when nothing is readable under those keys — the signature of a UID-only clone. - readLiveValueBlocks() reads live value blocks using the saved dump's keys - sectorKeyA() / decodeValueBlockBytes() helpers in mf-data - --deep flag on the verify CLI command - tests for the helpers, the op, and the deep-verify pass/fail paths Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A MIFARE Classic identity with no saved dump can only be written as a UID-only clone — which `verify` passed but a stored-value reader rejects. Nothing in the tool flagged that ambiguity, so two same-UID identities looked identical. - tagFidelity() classifies an identity as full / uid-only / n/a - `show` reports the data fidelity and warns on UID-only identities - `list` adds a Data column for MIFARE Classic tags - `write` warns before writing a UID-only MIFARE identity - tests for the classifier and each surfaced warning/column Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds `keyfabe value <name> --block <n> [--get|--set|--inc|--dec]` to read or write a MIFARE Classic value block (a stored-value balance), wrapping pm3's `hf mf value`. The sector key comes from the named identity's saved dump or an explicit --key. Writes warn, confirm, and read back to verify. - readValueBlock() / writeValueBlock() ops + HF_MF_VALUE command - value command with key resolution, confirmation, and read-back - docs/stored-value-cards.md: how balance-on-card systems work, value-block format, full-vs-UID-only clones, why hollow clones format-error, and the real limits of writing a balance (MAC / counter / server reconciliation) - README + CLAUDE.md reference the new command and doc - tests for the ops and every value command branch Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
🎉 This PR is included in version 1.1.0 🎉 The release is available on: Your semantic-release bot 📦🚀 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Motivation
Debugging a building laundry card exposed a class of gaps around MIFARE Classic stored-value cards. A UID-only clone (correct UID, factory-default keys, empty data) passed
keyfabe verify— which only compared the UID — yet a stored-value reader rejects it as unformatted. Two physically-identical same-UID cards were indistinguishable by hand and by the tool. This PR makes keyfabe understand the difference between a UID and the data a stored-value card actually needs.What's in it
identify— read the tag and match it against all saved identities at once; for MIFARE Classic, probe data fidelity to distinguish a full clone (custom keys → real data) from a UID-only clone (default keys → blank).verify --deep— go beyond the UID: read the live card's value blocks with the saved dump's keys, report the current on-card balance, and fail on a UID-only clone that would otherwise pass.tagFidelity()classifies identities as full / uid-only / n/a;listshows a Data column,showandwritewarn on UID-only identities.value— read or set a MIFARE value block (a stored-value balance) viahf mf value, with saved-key resolution, confirmation, and read-back. Honest about its limits (MAC / counter / server reconciliation).docs/stored-value-cards.mdexplains how balance-on-card systems work, the value-block format, the full-vs-UID-only distinction, and why hollow clones format-error.inspectcommand along.Verification
identifyverified live against the real laundry card: matched both saved identities and reported "custom keys — carries real data."identify/verify --deep/valuesmoke-tested via--help.🤖 Generated with Claude Code