Skip to content

About

NFC tag emulator for M5StickC + GroveNFC v1.2 (Flipper/Proxmark .nfc files, Mifare Classic + ISO15693/SLIX-L)

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

2 Commits

Folders and files

Repository files navigation

EmuGrooveTag

NFC tag emulator for the M5StickC Plus 1.1 / PLUS2 + GroveNFC v1.2.

Loads dump files exported from Flipper Zero and Proxmark3 and has the GroveNFC module emulate them: Mifare Classic, NTAG / Ultralight, ISO15693 (SLIX / SLIX-L including Privacy Mode, SLI, Tag-it, ...).

One module firmware for everything. Mifare Classic (including Auth/Crypto1), SLIX-L Privacy Mode and the NTAG extensions now all run together in module-fw/GroveNFC_EXT_fw0x6931.uf2. The vendor's official firmware (Mifare emulation closed-source, no sources available) is no longer needed for this - this firmware ships its own Mifare Classic emulation including a Crypto1 cipher, see "Mifare Classic + Privacy: the custom Crypto1 implementation" below, in particular what has only been simulated on a Mac and what has not been verified against real hardware. License is therefore GPLv3 instead of MIT, see "License" at the end.

Usage

pio run -e m5stick-c-plus -t upload     # flash the Plus 1.1 (4 MB flash)
pio run -e m5stick-c-plus2 -t upload    # flash the PLUS2 (8 MB flash, more storage for tag files)
pio run -t uploadfs                     # copy data/ into LittleFS

B = scroll file list up, power button = scroll down, short press A = start/stop emulation. (The StickC Plus has no real third "BtnC" button - the power button takes on that role; M5Unified exposes it as BtnPWR.) Loading a tag takes about 1 s (one flash sector on the module).

While a tag is emulating, B/power directly switch to the previous/next tag file in the same folder (skipping subfolders) and start it immediately - handy for stepping through a whole collection without returning to the menu each time.

Uploading files (no cable needed)

Hold A (~1 s) starts WiFi upload mode: the StickC opens its own WiFi network (EmuGrooveTag-xxxx, WPA2, fixed password shown on the display). Connect with a laptop or phone, open http://192.168.4.1, and drag files onto the page (multiple at once; listing, download and delete all work too, including folder creation/navigation). B exits the mode, after which the file list is refreshed. Only the file extensions listed below are accepted, max. 300 KB per file. A real USB mass-storage mode isn't possible on this hardware (ESP32-PICO-D4 without native USB, no SD card slot). Alternative: drop files into data/ and run pio run -t uploadfs (overwrites the entire filesystem).

Supported file formats

Extension Source Contents Verified against
.nfc Flipper Zero Mifare Classic 1K/4K, NTAG/Ultralight (UL, UL-C, NTAG203/213/215/216, UL EV1, NTAG I2C), SLIX/SLIX-L/SLIX2/ISO15693-3 Flipper source code
.json Proxmark3 mfc v2/mfc v3/mfcard, mfu, 15693 v4/v5 client/src/fileutils.c
.eml Proxmark3 / emulators Classic (16-byte lines), NTAG/UL (4-byte lines) fileutils.c
.mct Mifare Classic Tool Classic fileutils.c
.bin both Classic 1024/4096 raw, NTAG/UL raw, PM3 mfu (56-byte header), PM3 ISO15693 (2332/2235 bytes) include/{mifare,iso15}.h

Rejected with an error message: Mifare Mini, Classic 2K, DESFire, FeliCa, iCLASS, Legic, Hitag, ISO14443-4/-3B, ...

What the one firmware can do

Type Support
Mifare Classic 1K/4K, 4-byte/7-byte UID custom Crypto1 emulation (auth + encrypted read/write), see below
NTAG213/215/216 (read/write) yes
NTAG with password, signature, counter; Ultralight EV1/UL/NTAG203 yes (from the file, via the extension-record mechanism)
SLIX-L / SLIX with Privacy Mode yes
ISO15693 with non-default block count/IC reference taken exactly from the file

Flashing: put the module into UF2 mode and copy module-fw/GroveNFC_EXT_fw0x6931.uf2 onto it, or build it yourself from source (see "Building" below). Back to the vendor firmware: UF2 from Firmware/ in the NFC-funs/GroveNFC repo (Mifare emulation stays closed-source there, but without the limitations listed below). The patch lives in module-fw/ext-features.patch (8 changed/new files), the source in module-fw/src/ (Pico SDK 2.3.0, cmake + make, PICO_BOARD=pico). Ideally the vendor would upstream at least the Mifare Classic emulation as a callable function - then this custom addition wouldn't be needed. Nothing was sent back to them.

Mifare Classic + Privacy: the custom Crypto1 implementation

The GroveNFC API demo this project builds on has no Mifare Classic emulation at all - authentication was completely missing (Mifare1_Emul(...) was just a comment in the source). To get Mifare Classic and SLIX-L Privacy into the same firmware, module-fw/src/libNFC/Crypto1/ now contains a custom Crypto1 cipher implementation (Mifare Classic's stream cipher used for card authentication), ported from the public reference implementation common/crapto1 in the RfidResearchGroup/proxmark3 repo (GPLv3, © bla/Proxmark3 contributors) - only the core cipher functions (crypto1_init/_bit/_byte/_word, prng_successor), none of the key-recovery/cryptanalysis functions. Built on top of that, in module-fw/src/2-NFC/ISO14A/Mifare1/X_Mifare1.c: anticollision (4-byte/7-byte UID), auth (60/61) against the KeyA/KeyB already present in the dump file, encrypted read (30) and write (A0).

What it supports: 1K (16×4 blocks) and 4K (32×4 + 8×16 blocks) sector geometry; keys are never read back (always 0x00 when reading a trailer block); either key (A or B) unlocks both read and write for the whole sector (no fine-grained access-bit evaluation). What's missing: value-block arithmetic (increment/decrement/restore/transfer), proper per-key-type access rights, every other Mifare Classic variant (Mini, Plus, DESFire).

Verified: a host-side test harness (tests/module_mifare1_test.c, part of ./tests/run.sh) simulates a complete reader (PCD) using the same cipher code and drives the real tag code (X_Mifare1.c) through REQA → anticollision → auth → encrypted read/write → halt, for both 4-byte and 7-byte UIDs and both 1K and 4K geometry, including wrong-key and key-masking cases (25 checks, all pass). This proves internal consistency (tag and simulated reader agree on the same cipher state), not bit-level compatibility with a real external reader.

Not verified, most likely to have issues:

  1. Encrypted parity bits. After auth, each transmitted byte is expected to carry one additional, co-encrypted parity bit. Which exact extra cipher clock real NXP chips use for this couldn't be conclusively determined from publicly available material; the implementation uses the most plausible variant, the one most commonly described in the literature (one extra clock per byte, fed unencrypted). If a real reader can't read the card: look here first (mf1Encrypt/mf1Decrypt in X_Mifare1.c).
  2. Whether the module's low-level PIO capture (precompiled, no source available) passes encrypted frames with non-standard parity through unchanged.
  3. Everything else listed under "Not verified" further below regarding RF timing/I2C.

What Flipper Zero emulates - and what makes it onto the GroveNFC

Reference: nfc_listener_defs.c - Flipper emulates exactly these 7 protocols:

Flipper emulation GroveNFC Here
Mifare Classic 4-byte/7-byte × 1K/4K, auth + write (custom Crypto1 emulation) implemented
Mifare Ultralight / NTAG (all variants) NTAG213/215/216 natively; Ultralight family via the ext firmware (version/signature/password/counter from the file) implemented
SLIX / SLIX-L / SLIX2 / SLIX-S ISO15693 inventory/read/write; Privacy added via the patch implemented (Privacy: SLIX-L tested)
ISO15693-3 generic as above implemented (block size 4 only)
ISO14443-3A (UID only) via the NTAG/Mifare type as a workaround
ISO14443-4A (APDU, NDEF Type 4) not in the module not feasible without module firmware work
FeliCa the register knows FeliCa, but there's no emulation logic not feasible without module firmware work

Not emulated by Flipper at all: ISO14443-3B/-4B, DESFire, ST25TB, MIFARE Plus.

What Proxmark3 simulates - and what works from that

From the source code (RfidResearchGroup/proxmark3, hf ... sim):

Proxmark simulation (HF) GroveNFC
hf mf sim / hf 14a sim -t 1,8 Classic 1K/4K yes (custom Crypto1 emulation)
hf 14a sim -t 6 Classic Mini no
hf mfu sim / hf 14a sim -t 2,7,13,14 Ultralight, EV1, NTAG215, UL-C, UL-AES NTAG/UL EV1 yes; UL-C without 3DES auth only; UL-AES no
hf 15 sim ISO15693 (including SLIX privacy password) yes
hf 14a sim -t 3,4,5,9,10,11,12,15 DESFire, ISO14443-4, TNP3xxx, FM11RF005SH, ST25TA, JCOP, Seos, MIFARE Plus no
hf 14b sim, hf felica sim, hf iclass sim, hf legic sim, hf topaz sim, hf st25ta sim, hf mfdes sim, hf seos sim, hf cryptorf/epa/jooki/secc/texkom/thinfilm sim no
lf ... sim (EM410x, HID, Indala, ...) no - the module is 13.56 MHz only

Bottom line: everything the GroveNFC can emulate (Mifare Classic, NTAG/Ultralight, ISO15693) can be loaded from either Flipper or Proxmark files. Both sources produce bit-identical images for the same card (tested: Classic bin = eml = mct = json; NTAG Flipper = raw = eml = PM3 bin = PM3 json; ISO15693 PM3 json = PM3 bin). Anything the module can't emulate simply isn't loaded.

SLIX-L Privacy Mode behaviour

Follows Flipper (slix_listener_i.c) and - confirmed against the Proxmark source (armsrc/iso15693.c) - NXP:

  • Privacy on: the tag is silent, only Get Random Number (B2) and Set Password (B3) are answered.
  • Set Password (type Privacy, password XOR'd with RN|RN) correct → privacy off; Enable Privacy (BA) → on.
  • Wrong password/wrong ID: no response. Initial state comes from the file's Privacy Mode: field - but since many dump collections export tags as unlocked even though real readers only treat the lock/unlock handshake as "a new tag was presented", a tag is always started locked whenever it carries a privacy password at all, regardless of what the file says.
  • Proxmark JSON/BIN carry the password but no explicit "privacy on" flag; such tags start visible and can be muted via Enable Privacy.
  • Addressed commands (Flags bit 0x20 set) are only answered when the UID in the command matches the tag's own current UID, as required by the ISO15693 standard - otherwise the tag stays silent. This was missing from the original code (every addressed command was answered regardless of UID), which could leave a reader stuck addressing a stale UID from a previously-loaded tag after a switch; found via real-world hardware testing with a logic-level sniff of a commercial SLIX-L privacy reader.

NTAG / Ultralight - what the ext firmware adds

Stock firmware answers GET_VERSION only from the CC byte, READ_SIG with zeros, and PWD_AUTH with a NAK. The ext firmware supplies, from the file: a real version (or NAK for UL/NTAG203), signature, PWD_AUTH (password + PACK from the config pages) and READ_CNT. Write protection/AUTH0 isn't enforced (everything stays readable). If the password page is blank in the dump (real tags return it masked), reader authentication will fail - the host flags this.

What's been verified - and what hasn't

Verified (against source code or by test, no hardware needed) - ./tests/run.sh, 6 suites:

  • Module registers, layouts and type codes: against the register PDF and the vendor API source (X_RegPara.h, X_ISO15_App.c, X_Ntag.c).
  • All file formats: against Flipper and Proxmark source code; cross-checked identical cards across every format.
  • The real module sources (X_ISO15_App.c, X_Ntag.c, X_Mifare1.c + the ported crapto1.c) run on the host against simulated reader frames: SLIX-L privacy sequence (including addressed commands and UID matching), NTAG anticollision/read/write/version/signature/PWD_AUTH/counter, Mifare Classic anticollision/auth/encrypted read+write/1K+4K geometry/4-byte+7-byte UID/wrong-key case - each also covering the stock case without an extension record or without Crypto1 interaction.
  • Host and module firmware both build without errors (one firmware, every feature combined).

Verified against real hardware (Proxmark3 + a real SLIX-L privacy reader):

  • UID and full tag-content switching between different loaded files (RF level, not just host-side state).
  • SLIX-L privacy password byte order (found via a real capture, documented in the code).
  • The addressed-command UID-matching fix described above.

Not verified - please test on your own device/reader:

  1. Mifare Classic against a real reader - in particular the encrypted parity bits (see "Mifare Classic + Privacy" above). This is the least certain part of the whole firmware.
  2. Privacy password byte order for Flipper files with an asymmetric password (Proxmark files are unambiguous). If unlocking doesn't work: try PRIVACY_PWD_REVERSED in src/config.h set to 1.

Limitations

  • Mifare: sectors with ??/- (unknown keys) can't be authenticated; the number of unknown blocks is shown. Mini, 2K and 10-byte UID aren't supported. No value-block arithmetic, no fine-grained access-bit evaluation (see above).
  • SLIX: read/write/EAS/destroy passwords, lock bits, stay-quiet/select and AFI filtering are missing.
  • ISO14B "China ID" (UID only) and FeliCa: not implemented.
  • The module's tag buffer can't be read back → writes from a reader can't be saved back out to a file.
  • Every emulation writes one flash sector on the module (limited erase cycles, not a concern in normal use).

A note on use

For your own tags/cards only, or ones you have permission to use (testing, development, backups). You need to already know any passwords involved; nothing here cracks them for you.

Hardware

  • StickC Plus Grove port: SDA = G32, SCL = G33, I2C address 0x48, 100 kHz (reduced from the GroveNFC spec's 400 kHz on the PLUS2 for more margin against weaker pull-ups on that board's Grove port).
  • The host uses M5.Ex_I2C (I2C_NUM_0). Wire1 is off-limits: M5Unified runs the internal bus (power IC, backlight, IMU) on it.
  • Plus 1.1: 4 MB flash, ~1.4 MB usable for tag files. PLUS2: 8 MB flash with a custom partition table (partitions_plus2_8mb.csv), ~6.4 MB usable for tag files.

Building

Host firmware (ESP32, PlatformIO):

pio run -e m5stick-c-plus     # Plus 1.1
pio run -e m5stick-c-plus2    # PLUS2

Module firmware (RP2040, Pico SDK 2.3.0 + CMake):

cd module-fw/src
mkdir build && cd build
PICO_SDK_PATH=/path/to/pico-sdk cmake -G "Unix Makefiles" ..
make -j4

This produces Pico_NFC.uf2. Flash it by putting the module into UF2 bootloader mode (hold BOOTSEL while connecting) and either copying the file onto the drive that appears, or using picotool load -u.

Prebuilt binaries: see the Releases page for ready-to-flash images - .bin files for the ESP32 host (flash with esptool.py write_flash ..., see the release notes for the exact command) and a .uf2 for the RP2040 module (drag-and-drop in UF2 bootloader mode, or picotool load -u).

License

The host code (src/) and the vendor-derived module firmware base (module-fw/src/, minus the files below) are MIT licensed, same as the original upstream repo. The ported Crypto1 implementation (module-fw/src/libNFC/Crypto1/crapto1.c + crapto1.h, © 2008-2014 bla, © Proxmark3 contributors) is GPLv3. This makes the compiled module firmware (GroveNFC_EXT_fw0x6931.uf2) GPLv3 as a whole, not MIT - anyone redistributing it must also offer the complete corresponding source (already the case here: module-fw/src/). parity.h (evenparity32) is an independent, trivial reimplementation, not a port.

See LICENSE for the full GPLv3 text.

Credits / sources

  • NFC-funs/GroveNFC - vendor hardware, register documentation and the original (closed-Mifare) API demo this module firmware is built on.
  • RfidResearchGroup/proxmark3 - common/crapto1 (Crypto1 cipher core, GPLv3) and file-format reference (client/src/fileutils.c, include/mifare.h, include/iso15.h).
  • flipperdevices/flipperzero-firmware - .nfc file format and nfc_listener_defs.c protocol/feature reference.
  • whywilson/GroveNFC - an independent host driver that helped confirm the correct REG_MODE value for starting emulation.
  • M5Unified - M5Stack's hardware abstraction library used for the host firmware.

Project layout

src/            Host firmware: main.cpp, grovenfc.* (I2C driver), tagfile.* + loader_*.cpp (all formats)
data/           Sample files (SLIX-L, Mifare 1K, NTAG215)
module-fw/      Firmware (UF2), patch, source - including libNFC/Crypto1 (GPLv3, see "License")
tests/          run.sh, samples in every format (make_samples.py), module simulations (including Mifare1)

About

NFC tag emulator for M5StickC + GroveNFC v1.2 (Flipper/Proxmark .nfc files, Mifare Classic + ISO15693/SLIX-L)

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages