feat(registers): ICODE feature-flags decoder (GET NXP SYSTEM INFO)

Add IcodeFeatureFlags (Register) for the 16-bit ICODE feature word
reported by GET NXP SYSTEM INFO (ICODE 3 Table 113; DNA/SLIX2 report a
subset). Read-only capability decoder — decode a captured word to see
what a tag supports. Fields mirror the existing _FEATURE_* constants
one-to-one (asserted in a test). 3 new tests.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
michael
2026-07-14 09:01:52 -07:00
parent e795b5bb1d
commit f9e56e32ef
3 changed files with 54 additions and 2 deletions

View File

@@ -46,7 +46,7 @@ from pm3py.transponders.hf.iso15693.type5 import NfcType5Tag, ndef_text, ndef_ur
from pm3py.transponders.hf.iso15693.nxp.nxp_icode import NxpIcodeTag from pm3py.transponders.hf.iso15693.nxp.nxp_icode import NxpIcodeTag
from pm3py.transponders.hf.iso15693.nxp.icode_slix import IcodeSlixTag from pm3py.transponders.hf.iso15693.nxp.icode_slix import IcodeSlixTag
from pm3py.transponders.hf.iso15693.nxp.icode_slix2 import IcodeSlix2Tag from pm3py.transponders.hf.iso15693.nxp.icode_slix2 import IcodeSlix2Tag
from pm3py.transponders.hf.iso15693.nxp.icode3 import Icode3Tag from pm3py.transponders.hf.iso15693.nxp.icode3 import Icode3Tag, IcodeFeatureFlags
from pm3py.transponders.hf.iso15693.nxp.icode_dna import IcodeDnaTag from pm3py.transponders.hf.iso15693.nxp.icode_dna import IcodeDnaTag
from pm3py.transponders.hf.iso15693.nxp.auth_aes import NxpKeyPrivileges from pm3py.transponders.hf.iso15693.nxp.auth_aes import NxpKeyPrivileges
from pm3py.transponders.hf.iso15693.nxp.ntag5_platform import ( from pm3py.transponders.hf.iso15693.nxp.ntag5_platform import (
@@ -115,7 +115,7 @@ __all__ = [
"Icode3Tag", "IcodeDnaTag", "Icode3Tag", "IcodeDnaTag",
"Ntag5PlatformTag", "Ntag5SwitchTag", "Ntag5LinkTag", "Ntag5BoostTag", "Ntag5PlatformTag", "Ntag5SwitchTag", "Ntag5LinkTag", "Ntag5BoostTag",
"Ntag5StatusReg", "Ntag5ConfigReg", "Ntag5StatusReg", "Ntag5ConfigReg",
"NxpKeyPrivileges", "NxpKeyPrivileges", "IcodeFeatureFlags",
"DesfireTag", "DesfireReader", "DesfireTag", "DesfireReader",
"xEM", "xNT", "xM1", "FlexDF", "NExT", "MagicMifareClassicTag", "xEM", "xNT", "xM1", "FlexDF", "NExT", "MagicMifareClassicTag",
"MutationFuzzer", "GrammarFuzzer", "MutationFuzzer", "GrammarFuzzer",

View File

@@ -16,6 +16,7 @@ import os
import struct import struct
from pm3py.sim.frame import RFFrame from pm3py.sim.frame import RFFrame
from pm3py.transponders.bitfield import BitField, Register
from .icode_slix2 import IcodeSlix2Tag, PWD_READ, PWD_WRITE from .icode_slix2 import IcodeSlix2Tag, PWD_READ, PWD_WRITE
from .nxp_icode import NXP_MFG from .nxp_icode import NXP_MFG
@@ -44,6 +45,33 @@ _FEATURE_WRITE_CID = 1 << 14
_FEATURE_HIGH_DATA_RATES = 1 << 15 _FEATURE_HIGH_DATA_RATES = 1 << 15
class IcodeFeatureFlags(Register):
"""The NXP ICODE feature-flags word reported by GET NXP SYSTEM INFO (ICODE 3 Table 113;
ICODE DNA / SLIX2 report a subset). Read-only capability bits — decode a word you captured
to see what a tag supports. 16-bit little-endian on the wire, but treated here as one word
(bit N is feature flag N)."""
_width_bits = 16
_label = "ICODE feature flags"
um_pp = BitField(0, doc="user-memory password protection")
counter = BitField(1, doc="single-block read counter")
eas_id = BitField(2, doc="EAS ID")
eas_pp = BitField(3, doc="EAS password protection")
afi_pp = BitField(4, doc="AFI password protection")
inventory_read_ext = BitField(5, doc="extended INVENTORY READ")
eas_ir = BitField(6, doc="EAS in INVENTORY READ")
cid = BitField(7, doc="configurable customer ID")
originality_sig = BitField(8, doc="ECC originality signature")
tagtamper = BitField(9, doc="tag-tamper feature")
p_quiet = BitField(10, doc="persistent QUIET state")
nfc_mirror = BitField(11, doc="NFC ASCII mirror")
privacy = BitField(12, doc="privacy mode")
destroy = BitField(13, doc="DESTROY command")
write_cid = BitField(14, doc="writeable customer ID")
high_data_rates = BitField(15, doc="high data-rate support")
class Icode3Tag(IcodeSlix2Tag): class Icode3Tag(IcodeSlix2Tag):
"""ICODE 3 transponder (SL2S3003 / SL2S3003TT). """ICODE 3 transponder (SL2S3003 / SL2S3003TT).

View File

@@ -10,6 +10,7 @@ from pm3py.sim import (
NtagI2CConfig, NtagI2CSession, NtagI2CPtI2C, NT3H2111, NT3H2211, NtagI2CConfig, NtagI2CSession, NtagI2CPtI2C, NT3H2111, NT3H2211,
Ntag5StatusReg, Ntag5ConfigReg, Ntag5LinkTag, Ntag5StatusReg, Ntag5ConfigReg, Ntag5LinkTag,
NxpKeyPrivileges, IcodeDnaTag, NxpKeyPrivileges, IcodeDnaTag,
IcodeFeatureFlags,
) )
@@ -467,3 +468,26 @@ class TestNxpKeyPrivileges:
text = repr(NxpKeyPrivileges.build(read=True, destroy=True)) text = repr(NxpKeyPrivileges.build(read=True, destroy=True))
assert "NXP key privileges" in text assert "NXP key privileges" in text
assert "read" in text and "destroy" in text assert "read" in text and "destroy" in text
class TestIcodeFeatureFlags:
def test_bits_match_module_constants(self):
import pm3py.transponders.hf.iso15693.nxp.icode3 as ic3
# every declared field must line up with the _FEATURE_* constant of the same name
for name, bf in IcodeFeatureFlags.fields():
const = getattr(ic3, f"_FEATURE_{name.upper()}")
assert (1 << bf.lo) == const, name
def test_decode(self):
# um_pp(0) + counter(1) + originality_sig(8) + high_data_rates(15)
w = (1 << 0) | (1 << 1) | (1 << 8) | (1 << 15)
f = IcodeFeatureFlags(w)
assert f.um_pp is True and f.counter is True
assert f.originality_sig is True and f.high_data_rates is True
assert f.privacy is False
assert int(f) == w
def test_repr(self):
text = repr(IcodeFeatureFlags(0x0001))
assert "ICODE feature flags" in text
assert "um_pp" in text