RFID
Fantasi supports RFID on every device it runs on. A capable RFID frontend is one of the criteria for a board becoming a Fantasi target. Proxmark3, Proxmark5, Flipper Zero, Kiisu, and Chameleon Ultra use the same commands and dump formats, so supported operations can move between devices.
Devices and frontends
Each platform carries its own driver in platforms/*/rfid.c. They meet at a logical HAL (hal/hal_rfid.h): HF is only a framed transceive, LF is a raw acquire plus modulate. Everything above that line (anticollision, EM4100 decode, CRC, Crypto1) is written once in core/rfid/ and shared. Platforms advertise their abilities through capability bits.
| Device | RFID frontend |
|---|---|
| Proxmark3 | Xilinx Spartan-II XC2S30 FPGA driven by AT91SAM7S512 |
| Proxmark5 | GOWIN GW1N-4B FPGA driven by AT32F435 |
| Flipper / Kiisu | ST25R3916 in transparent mode |
| Chameleon Ultra | nRF52840 NFCT, MFRC522 |
NB: On the Proxmark3, FPGA bitstreams are stored compressed under /fpga to conserve internal flash. They stream from the host the first time a band is used. The Proxmark5's GW1N-4B is different: its gateware lives in the FPGA's own internal config flash and autoboots at power-on (LF and HF logic are both resident), so there is no host-streamed bitstream and no /fpga store. The AT32F435 reaches the frontend over a bit-banged command channel plus an SPI4/SSC sample bus; capability bits currently advertise LF and HF read (hal_rfid_caps() = LF_READ | HF_READ).
Quickstart
rfid is a command inside the Fantasi host CLI. Running it launches the on-device app and drops you into an interactive rfid> prompt with history and TAB completion:
fantasi> rfid
rfid> read mfc
rfid> read t5577
rfid> exit
The command requires a protobuf connection: use fantasi --usb or fantasi --ble. It is not available over the legacy --serial transport. The web Launcher exposes the same inline terminal, including command history, completion, selectable output, stop keys, and the device's live status output.
For scripting, pass a single command with -c and it runs once and exits:
fantasi --usb -c "rfid read mfc"
fantasi --usb -c "rfid emulate lf-t55xx-1A2B3C4D-dump.json"
Only one application can run at a time. If an earlier RFID session did not exit cleanly, return to the main prompt and run kill before launching it again.
Example:
read mfc reads a MIFARE Classic card end to end: it profiles the PRNG, tries known keys from the dictionaries, then authenticates and dumps every block. read t5577 gives you a complete dump of an LF T5577; it decodes the config block to learn how many data blocks to expect. A bare read <proto> reads all blocks, and read <proto> -b <n> reads one. Type list any time to see the protocol tree, or help for the verbs.
Commands
Type help for the command list, help <command> for its options and examples, or use -h / --help after a command. TAB completion covers commands, protocols, and applicable subcommands.
| Command | Description |
|---|---|
search [band] | Scan for nearby tags; bare search scans every band |
read <protocol> [-b <block>] [-k <key>] [-s [file]] | Read one block or dump a whole tag |
write <protocol> -b <block> -d <hex> [-k <key>] | Write one tag block |
emulate <dump.json> | Emulate a card from a saved dump |
sniff <protocol> | Passively capture a reader-to-card exchange |
trace [clear] | Show or clear the captured frame trace |
collect <protocol> <card/reader/sniff> [-u <UID>] [-k <key>] | Capture nonces or sniffed keys |
raw <protocol> [-c] [-k] [-s] <hex> | Send a raw protocol frame |
field on/off/status | Control or inspect the reader carrier |
list [band/subcategory] | List protocols and available operations |
help [command] | Show general or command-specific help |
exit | Leave the RFID app |
read, write, emulate, collect, list, and help are host-side abstractions. The remaining commands dispatch to the on-device RFID app.
Protocols
list
list presents a band > technology > protocol tree and shows which operations are available for the connected target. Pass a band or subcategory to filter it, such as list lf, list hf, or list nfca.
rfid> list
LF
125-500 kHz
t5577 T5577 read write
HF · 13.56 MHz
NFC A · ISO14443-A
nfca generic sniff raw
mfc MIFARE Classic read sniff raw emulate collect
mfp MIFARE Plus sniff raw
ul Ultralight read sniff raw
ulc Ultralight C read sniff raw
ulaes Ultralight AES sniff raw
mfdes DESFire sniff raw
NFC B · ISO14443-B
(none yet)
NFC F · FeliCa
(none yet)
NFC V · ISO15693
(none yet)
UHF
860-960 MHz
(none yet)
The client colours built operations green and adds * to operations whose module was not built for the connected target architecture. Hardware without a required frontend capability reports that when the command is run.
search
search [band] looks for nearby tags and prints each result. Bare search checks every implemented band in order; search hf or search lf limits the scan.
read
read <protocol> dumps an entire tag. Add -b <block> to read one block, -k <key> to provide an authentication key or password, and -s [file] to save the result as JSON. When no filename follows -s, the client generates one.
rfid> read mfc
rfid> read mfc -b 4 -k FFFFFFFFFFFF
rfid> read t5577 -b 4
rfid> read mfc -s dump.json
read t5577 first decodes the configuration block, then reads the complete tag. read mfc profiles the card's PRNG, tries the bundled and card-specific key dictionaries, authenticates each sector, and reads all 64 blocks. Long reads show live phase and block/sector status in place instead of appearing to hang.
write
write <protocol> -b <block> -d <hex> [-k <key>] writes one block. The data width is validated for the selected protocol.
rfid> write t5577 -b 4 -d 1A2B3C4D
raw
raw <protocol> [-c] [-k] [-s] <hex> sends a raw frame and prints the decoded response and trace.
| Flag | Effect |
|---|---|
-c | Append the protocol CRC |
-k | Keep the field on for a follow-up exchange |
-s | Select the tag before sending the frame |
rfid> raw mfc -s -c 3000
field
field on enables the reader carrier and leaves it on, field off disables it, and field status reports its current state.
trace
trace prints the raw frames accumulated during the current session. trace clear empties the trace.
The protocol tree reserves three bands - LF, HF, and UHF - even when a band does not yet contain an implemented protocol.
Sniffing
sniff <protocol> passively watches a live reader-to-card exchange with the device's own field off. Traffic scrolls as it arrives. Press any key to finish the capture, or ^C (Control-C) to abort the app.
rfid> sniff mfc
Each captured frame is annotated. The host recognises the protocol you named and labels the commands inline (SELECT, ANTICOLL, READ blk 4, AUTH-A blk 7, WRITE page 6, and so on), turning a hex trace into something readable. Bad CRCs and parity errors are flagged in place. For MIFARE Classic, mfkey64 runs inline against the trace: a captured authentication is enough to recover the sector key, with no separate cracking step.
Where the frontend can measure it, a capture opens with a coupling reading that grades the sniff quality as strong, fair or weak (green, yellow, red). On the Flipper this comes from the ST25R3916's External Field Detector.
That same measurement feeds an adaptive first-stage gain, so a reader stays in range as its distance drifts; without it the signal would degrade up close and fade out further away. A device that cannot sense field strength reports the coupling as unknown.
While a capture is running, the terminal deliberately withholds the next rfid> prompt. It prints the module's stopped/completion message first, then shows the prompt when the command is actually ready for more input.
Emulation
emulate <dump.json> plays a saved card back on targets whose list output marks emulation available. The current implementation supports MIFARE Classic 1K, including reader authentication and block reads, and runs until you press a key or ^C.
rfid> emulate hf-mfc-A1B2C3D4-dump.json
The client parses the JSON dump, uploads the 1 KiB card image, and reports its UID, populated block count, and PRNG profile before starting. Its frame delay time is close to that of a physical card. For background on the timing, see the Chameleon Ultra technical whitepaper.
Key collection
collect mfc card gathers a Hardnested nonce set from a presented MIFARE Classic card and saves it as mfc.log for the host-side solver. -u <UID> can target a 4- or 7-byte UID, while -k <key> seeds a known six-byte key.
collect mfc sniff watches live reader/card authentications, runs mfkey64 as they arrive, and stores recovered keys in /nfc/mfc.dict for later read mfc commands. Press a key to stop the capture and finalize the dictionary.
rfid> collect mfc card
rfid> collect mfc sniff
The reader collection mode is reserved for card impersonation and is not yet implemented.
Dump files
read -s writes dump files when you read a card. It stays close enough to Proxmark3's own JSON format that dumps interchange for common card types. With no explicit filename the client names the dump from its band, protocol, and UID. MIFARE Classic dumps include all block data, recovered trailer keys, and the detected PRNG class; emulate reads that JSON back.
Modularity
The resident RFID app is a small loader. Readers, writers, sniffers, collectors, and emulators are separate ELF modules loaded only when their command needs them. The app first looks for installed modules on external storage and under /modules; when the host or web Launcher supplies one on demand, it is uploaded to /ramfs, run, and removed afterward. Only the active feature module occupies RAM.
FPGA resources are different. Only the Proxmark3 requests compressed bitstreams from the host and caches them under /fpga. Proxmark5 gateware is already in the FPGA's configuration flash and is never uploaded by the RFID client.