diff --git a/pm3py/sim/__init__.py b/pm3py/sim/__init__.py index c77ec2d..b699fe5 100644 --- a/pm3py/sim/__init__.py +++ b/pm3py/sim/__init__.py @@ -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.icode_slix import IcodeSlixTag 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.auth_aes import NxpKeyPrivileges from pm3py.transponders.hf.iso15693.nxp.ntag5_platform import ( @@ -115,7 +115,7 @@ __all__ = [ "Icode3Tag", "IcodeDnaTag", "Ntag5PlatformTag", "Ntag5SwitchTag", "Ntag5LinkTag", "Ntag5BoostTag", "Ntag5StatusReg", "Ntag5ConfigReg", - "NxpKeyPrivileges", + "NxpKeyPrivileges", "IcodeFeatureFlags", "DesfireTag", "DesfireReader", "xEM", "xNT", "xM1", "FlexDF", "NExT", "MagicMifareClassicTag", "MutationFuzzer", "GrammarFuzzer", diff --git a/pm3py/transponders/hf/iso15693/nxp/icode3.py b/pm3py/transponders/hf/iso15693/nxp/icode3.py index d1a8ade..f48402c 100644 --- a/pm3py/transponders/hf/iso15693/nxp/icode3.py +++ b/pm3py/transponders/hf/iso15693/nxp/icode3.py @@ -16,6 +16,7 @@ import os import struct from pm3py.sim.frame import RFFrame +from pm3py.transponders.bitfield import BitField, Register from .icode_slix2 import IcodeSlix2Tag, PWD_READ, PWD_WRITE from .nxp_icode import NXP_MFG @@ -44,6 +45,33 @@ _FEATURE_WRITE_CID = 1 << 14 _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): """ICODE 3 transponder (SL2S3003 / SL2S3003TT). diff --git a/tests/test_bitfield.py b/tests/test_bitfield.py index ad37f7d..b387bf3 100644 --- a/tests/test_bitfield.py +++ b/tests/test_bitfield.py @@ -10,6 +10,7 @@ from pm3py.sim import ( NtagI2CConfig, NtagI2CSession, NtagI2CPtI2C, NT3H2111, NT3H2211, Ntag5StatusReg, Ntag5ConfigReg, Ntag5LinkTag, NxpKeyPrivileges, IcodeDnaTag, + IcodeFeatureFlags, ) @@ -467,3 +468,26 @@ class TestNxpKeyPrivileges: text = repr(NxpKeyPrivileges.build(read=True, destroy=True)) assert "NXP key privileges" 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