From 01357e4e787122cb93b802c0bad3949968748aab Mon Sep 17 00:00:00 2001 From: michael Date: Sat, 4 Jul 2026 23:03:06 -0700 Subject: [PATCH] fix(iso15): rdbl needs OPTION bit + CRC strip; wrbl needs LONG_WAIT rdbl omitted the ISO15693 OPTION flag (0x40) so the tag returned [flags][data] without the lock byte the parser assumes (shifting data one byte) and included the 2-byte CRC in block_data. wrbl reused the read flags (no LONG_WAIT) so the firmware used the short RX timeout and the ~20ms write ack arrived too late -> false failure. rdbl: set OPTION (0x62/0x42), strip CRC (data[2:-2]). wrbl: CONNECT|READ_RESPONSE|LONG_WAIT (0x61). Mirrors C client CmdHF15Readblock / hf_15_write_blk. Pre-existing, independent of the rebase. Co-Authored-By: Claude Opus 4.8 (1M context) --- pm3py/core/hf_iso15.py | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/pm3py/core/hf_iso15.py b/pm3py/core/hf_iso15.py index 090b4ed..bb10f56 100644 --- a/pm3py/core/hf_iso15.py +++ b/pm3py/core/hf_iso15.py @@ -21,17 +21,22 @@ ISO15_READ_RESPONSE = 0x20 ISO15_LONG_WAIT = 0x40 -def _iso15_payload(iso_cmd: bytes) -> bytes: +# Default transport flags for read/inventory (C client CmdHF15Info/getUID). +_READ_FLAGS = ISO15_CONNECT | ISO15_HIGH_SPEED | ISO15_READ_RESPONSE # 0x31 +# Write needs the long (~22ms) tag-ack timeout and does not set HIGH_SPEED +# (mirrors the C client's hf_15_write_blk). +_WRITE_FLAGS = ISO15_CONNECT | ISO15_READ_RESPONSE | ISO15_LONG_WAIT # 0x61 + + +def _iso15_payload(iso_cmd: bytes, flags: int = _READ_FLAGS) -> bytes: """Build a HF_ISO15693_COMMAND payload from a raw ISO15693 command. Appends the ISO15693 CRC host-side (the ARM firmware ignores ISO15_APPEND_CRC — the client must add it, like the C client's AddCrc15) and prefixes the PM3 transport - flags. HIGH_SPEED + READ_RESPONSE make the firmware receive at high data rate and - actually listen for the tag's answer — without READ_RESPONSE (0x20) the firmware - passes a NULL rx buffer and replies empty. + flags. READ_RESPONSE (0x20) makes the firmware actually listen for the tag's answer; + without it the firmware passes a NULL rx buffer and replies empty. """ raw = iso_cmd + crc15_bytes(iso_cmd) - flags = ISO15_CONNECT | ISO15_HIGH_SPEED | ISO15_READ_RESPONSE return struct.pack(" dict: """Read a single block.""" + # OPTION (0x40) makes the tag return the block-security (lock) byte this parser + # expects: addressed 0x22|0x40=0x62, unaddressed 0x02|0x40=0x42. if uid: uid_bytes = bytes.fromhex(uid)[::-1] # reverse for wire - iso_cmd = bytes([0x22, 0x20]) + uid_bytes + bytes([block]) + iso_cmd = bytes([0x62, 0x20]) + uid_bytes + bytes([block]) else: - iso_cmd = bytes([0x02, 0x20, block]) # addressed without UID + iso_cmd = bytes([0x42, 0x20, block]) # unaddressed payload = _iso15_payload(iso_cmd) resp = await self._t.send_ng(Cmd.HF_ISO15693_COMMAND, payload, timeout=5.0) - if resp.status != 0 or len(resp.data) < 2: + if resp.status != 0 or len(resp.data) < 4: return {"success": False, "error": resp.status} resp_flags = resp.data[0] @@ -96,7 +103,7 @@ class HF15Commands: return {"success": False, "error_flag": resp_flags} lock_status = resp.data[1] - block_data = resp.data[2:] + block_data = resp.data[2:-2] # strip the 2-byte ISO15693 CRC the firmware relays return { "success": True, @@ -117,7 +124,7 @@ class HF15Commands: else: iso_cmd = bytes([0x02, 0x21, block]) + data - payload = _iso15_payload(iso_cmd) + payload = _iso15_payload(iso_cmd, flags=_WRITE_FLAGS) # LONG_WAIT for the ~22ms write ack resp = await self._t.send_ng(Cmd.HF_ISO15693_COMMAND, payload, timeout=5.0) success = resp.status == 0 and len(resp.data) > 0 and resp.data[0] == 0