Files
pm3py/pm3py/transponders/hf/iso15693/nxp/nxp_icode.py
michael c1ab980036 refactor: remove all backward-compat shims from sim/
Deletes 24 shim files from sim/. All test imports now point directly
to canonical transponders/ and trace/ paths. sim/ contains only
infrastructure (15 files): frame, memory, transponder ABC, reader ABC,
medium, sim_session, table_compiler, replay, relay, fuzzer, pm3medium,
mcu_bridge, mcu_protocol, dual_session, __init__.

751 tests passing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 20:56:40 -07:00

87 lines
3.1 KiB
Python

"""NXP ICODE/NTAG5 platform — shared NXP custom commands."""
from __future__ import annotations
import os
import struct
from pm3py.sim.frame import RFFrame
from pm3py.trace.decode_iso15 import decode_15693_nxp
from ..type5 import NfcType5Tag
# NXP manufacturer code
NXP_MFG = 0x04
# NXP custom commands per SL2S2602 datasheet
CMD_INVENTORY_READ = 0xA0
CMD_FAST_INVENTORY_READ = 0xA1
CMD_SET_EAS = 0xA2
CMD_RESET_EAS = 0xA3
CMD_LOCK_EAS = 0xA4
CMD_EAS_ALARM = 0xA5
CMD_PASSWORD_PROTECT_EAS_AFI = 0xA6
CMD_WRITE_EAS_ID = 0xA7
CMD_GET_NXP_SYSTEM_INFO = 0xAB
CMD_GET_RANDOM = 0xB2
CMD_SET_PASSWORD = 0xB3
CMD_WRITE_PASSWORD = 0xB4
CMD_LOCK_PASSWORD = 0xB5
CMD_PROTECT_PAGE = 0xB6
CMD_LOCK_PAGE_PROTECTION = 0xB7
CMD_DESTROY = 0xB9
CMD_ENABLE_PRIVACY = 0xBA
CMD_64BIT_PASSWORD_PROTECTION = 0xBB
CMD_STAY_QUIET_PERSISTENT = 0xBC
CMD_READ_SIGNATURE = 0xBD
CMD_READ_CONFIG = 0xC0
CMD_WRITE_CONFIG = 0xC1
CMD_READ_TT = 0xC4
class NxpIcodeTag(NfcType5Tag):
"""NXP ICODE/NTAG5 shared platform features.
Provides: originality signature, NXP system info, GET_RANDOM, SET_PASSWORD.
Base for ICODE SLIX, SLIX2, DNA, NTAG 5 Link, etc.
"""
_uid_prefix = b"\xE0\x04" # E0 + NXP manufacturer code
def __init__(self, uid: bytes, signature: bytes | None = None, **kwargs):
super().__init__(uid=uid, **kwargs)
# 32-byte ECC originality signature (from factory, read-only)
self._signature = signature or bytes(32)
def decode_trace(self, direction: int, payload: bytes) -> str | None:
return decode_15693_nxp(direction, payload) or super().decode_trace(direction, payload)
def _handle_custom_command(self, cmd: int, frame: RFFrame) -> RFFrame | None:
"""Handle NXP custom commands (0xA0-0xDF)."""
# Verify NXP manufacturer code
if len(frame.data) < 3 or frame.data[2] != NXP_MFG:
return None
match cmd:
case 0xB2: # GET RANDOM NUMBER
return self._handle_get_random(frame)
case 0xB3: # SET PASSWORD
return self._nxp_handle_set_password(frame)
return None
def _handle_get_nxp_system_info(self, frame: RFFrame) -> RFFrame | None:
"""GET NXP SYSTEM INFO (0xAB): returns PP byte, PP conditions, lock bits, feature flags."""
# Base implementation — subclasses override with actual PP/feature data
# Response: flags(1) + PP(1) + PP_cond(1) + lock_bits(1) + feature_flags(4)
return RFFrame.from_bytes(bytes([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]))
def _handle_get_random(self, frame: RFFrame) -> RFFrame | None:
random_bytes = os.urandom(2)
return RFFrame.from_bytes(bytes([0x00]) + random_bytes)
def _handle_read_signature(self, frame: RFFrame) -> RFFrame | None:
"""READ SIGNATURE (0xBD): returns 32-byte ECC originality signature."""
return RFFrame.from_bytes(bytes([0x00]) + self._signature)
def _nxp_handle_set_password(self, frame: RFFrame) -> RFFrame | None:
"""Base SET_PASSWORD handler. Override in subclasses for specific password types."""
return None