From 820701511ce2ee302f622d2ea23304d6cf59a4c7 Mon Sep 17 00:00:00 2001 From: michael Date: Sun, 5 Jul 2026 00:08:40 -0700 Subject: [PATCH] fix(14a): NG struct for sim (+ tagtype, enables MFU/NTAG sim), NG for sniff 14a.sim sent a MIX frame but the handler is NG casting a packed {tagtype,u16 flags,uid[10],exitAfter,rats[20],ulauth...} struct -> whole struct garbled, flags never arrived. Rewrite to send_ng with the exact struct and expose tagtype (2=Ultralight,7=NTAG) so Ultralight/NTAG sim works. 14a.sniff sent MIX (args stripped -> empty asBytes); send NG with the 1-byte param. Pre-existing, independent of rebase. Co-Authored-By: Claude Opus 4.8 (1M context) --- pm3py/core/hf_iso14a.py | 64 ++++++++++++++++++++++++++++++----------- tests/test_hf_14a.py | 43 +++++++++++++++++++++++++++ 2 files changed, 91 insertions(+), 16 deletions(-) diff --git a/pm3py/core/hf_iso14a.py b/pm3py/core/hf_iso14a.py index ad129fc..58ec4a4 100644 --- a/pm3py/core/hf_iso14a.py +++ b/pm3py/core/hf_iso14a.py @@ -71,26 +71,58 @@ class HF14ACommands: async def sniff(self, param: int = 0) -> dict: """Sniff ISO14443A traffic.""" - resp = await self._t.send_mix( - Cmd.HF_ISO14443A_SNIFF, arg0=param, timeout=30.0, + resp = await self._t.send_ng( + Cmd.HF_ISO14443A_SNIFF, payload=bytes([param]), timeout=30.0, ) return {"status": resp.status} - async def sim(self, uid: bytes, sak: int = 0x08, atqa: bytes = b"\x04\x00", - flags: int = 0) -> dict: - """Simulate ISO14443A card.""" - if len(uid) == 4: - uid_flag = 0x0010 - elif len(uid) == 7: - uid_flag = 0x0020 - elif len(uid) == 10: - uid_flag = 0x0030 + async def sim(self, tagtype: int = 1, uid: bytes = b"", exit_after: int = 0, + flags: int = 0, rats: bytes = b"", + ulauth_1a1: bytes = b"", ulauth_1a2: bytes = b"", + ulauth_1a2_mirror: bool = False) -> dict: + """Simulate an ISO14443A tag. + + tagtype selects the emulated tag: 1=MIFARE Classic 1k, 2=Ultralight, + 3=DESFire, 4=ISO14443-4, 5=Tnp3xxx, 6=MF Mini, 7=MFU EV1/NTAG215, + 8=MFC 4k, 9=FM11RF005SH, 10=ST25TA, 11=JCOP, 12=Seos, 13=ULC, + 14=ULAES, 15=MF Plus. + + uid: 4, 7, or 10 raw bytes. Empty = take UID from emulator memory. + exit_after: stop after N reader reads (0 = infinite). + ulauth_1a1/ulauth_1a2: ULC (8 byte) / ULAES (16 byte) auth replies. + """ + # Fold the UID length into flags per FLAG_SET_UID_IN_DATA (pm3_cmd.h). + if uid: + if len(uid) == 4: + uid_flag = 0x0010 # FLAG_4B_UID_IN_DATA + elif len(uid) == 7: + uid_flag = 0x0020 # FLAG_7B_UID_IN_DATA + elif len(uid) == 10: + uid_flag = 0x0030 # FLAG_10B_UID_IN_DATA + else: + raise ValueError("UID must be 4, 7, or 10 bytes") else: - raise ValueError("UID must be 4, 7, or 10 bytes") + uid_flag = 0x0000 # FLAG_UID_IN_EMUL - flag_val = flags | uid_flag | 0x0001 # FLAG_INTERACTIVE - payload = atqa + bytes([sak]) + uid - resp = await self._t.send_mix( - Cmd.HF_ISO14443A_SIMULATE, arg0=flag_val, timeout=60.0, payload=payload, + flag_val = (flags & ~0x0030) | uid_flag # FLAG_MASK_UID = 0x0030 + + # struct { u8 tagtype; u16 flags; u8 uid[10]; u8 exitAfter; + # u8 rats[20]; u8 ulauth_1a1_len; u8 ulauth_1a2_len; + # u8 ulauth_1a1[16]; u8 ulauth_1a2[16]; bool mirror; } PACKED + payload = struct.pack( + "