Files
pm3py/pm3py/hf_15.py
2026-03-15 22:18:11 -07:00

94 lines
3.2 KiB
Python

"""ISO 15693 commands: hf.15.*"""
import struct
from .protocol import Cmd
from .transport import PM3Transport, PM3Error
# ISO15 PM3 flags
ISO15_CONNECT = 0x01
ISO15_NO_DISCONNECT = 0x02
ISO15_RAW = 0x04
ISO15_APPEND_CRC = 0x08
ISO15_READ_RESPONSE = 0x10
class HF15Commands:
"""ISO 15693 commands."""
def __init__(self, transport: PM3Transport):
self._t = transport
async def scan(self) -> dict:
"""Scan for ISO15693 tags using inventory command."""
# ISO15693 inventory: flags=0x06 (high data rate + inventory), cmd=0x01
iso_cmd = bytes([0x06, 0x01, 0x00]) # flags, inventory cmd, mask_len=0
flags = ISO15_CONNECT | ISO15_READ_RESPONSE | ISO15_APPEND_CRC
payload = struct.pack("<BH", flags, len(iso_cmd)) + iso_cmd
resp = await self._t.send_ng(Cmd.HF_ISO15693_COMMAND, payload, timeout=5.0)
if resp.status != 0 or len(resp.data) < 10:
return {"found": False, "uid": None}
resp_flags = resp.data[0]
dsfid = resp.data[1]
uid = resp.data[2:10]
return {
"found": True,
"uid": uid[::-1].hex(), # ISO15693 UIDs are reversed
"dsfid": dsfid,
"response_flags": resp_flags,
}
async def rdbl(self, block: int, uid: str | None = None) -> dict:
"""Read a single block."""
if uid:
uid_bytes = bytes.fromhex(uid)[::-1] # reverse for wire
iso_cmd = bytes([0x22, 0x20]) + uid_bytes + bytes([block])
else:
iso_cmd = bytes([0x02, 0x20, block]) # addressed without UID
flags = ISO15_CONNECT | ISO15_READ_RESPONSE | ISO15_APPEND_CRC
payload = struct.pack("<BH", flags, len(iso_cmd)) + 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:
return {"success": False, "error": resp.status}
resp_flags = resp.data[0]
if resp_flags != 0:
return {"success": False, "error_flag": resp_flags}
lock_status = resp.data[1]
block_data = resp.data[2:]
return {
"success": True,
"block": block,
"locked": bool(lock_status),
"data": block_data.hex(),
"raw": block_data,
}
async def wrbl(self, block: int, data: bytes | str, uid: str | None = None) -> dict:
"""Write a single block."""
if isinstance(data, str):
data = bytes.fromhex(data)
if uid:
uid_bytes = bytes.fromhex(uid)[::-1]
iso_cmd = bytes([0x22, 0x21]) + uid_bytes + bytes([block]) + data
else:
iso_cmd = bytes([0x02, 0x21, block]) + data
flags = ISO15_CONNECT | ISO15_READ_RESPONSE | ISO15_APPEND_CRC
payload = struct.pack("<BH", flags, len(iso_cmd)) + iso_cmd
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
return {"success": success, "block": block}
async def sniff(self) -> dict:
"""Sniff ISO15693 traffic."""
resp = await self._t.send_ng(Cmd.HF_ISO15693_SNIFF, timeout=30.0)
return {"status": resp.status}