feat(rawcli): identify NTAG5 and ICODE DNA (READ_CONFIG probe)
NTAG5, ICODE DNA and SLIX2 all share E0 04 and answer READ_SIGNATURE, so name_iso15 now splits them with an addressed, retried READ_CONFIG (0xC0) probe: - session register 0xA0 (STATUS_REG) succeeds only on NTAG5 -> "NTAG 5" - 0xA0 errors but config block 0x00 succeeds (DNA has config mem) -> "ICODE DNA" - neither (SLIX2 has no READ_CONFIG) -> the 0xBD signature step -> "ICODE SLIX2" - plain SLIX answers neither -> "ICODE SLIX" Hardware-verified against a real NTAG5 (VivoKey VK Thermo, UID E0 04 01 58, 0xA0 -> 00 01400000) and a real ICODE DNA (UID E0 04 01 18, 0xA0 -> 01 0F error, 0x00 -> 00 ...). DNA was previously mislabeled "ICODE SLIX2". catalog_for routes DNA to the SLIX2 catalog for now (a dedicated DNA catalog with READ_CONFIG + AES is a follow-up). Adds a mocked unit test covering all four branches from the real captures. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -268,6 +268,34 @@ class TestIdentify:
|
||||
r = identify(s)
|
||||
assert r["found"] is False and s.transponder is None
|
||||
|
||||
def test_name_iso15_ntag5_dna_slix2_slix(self):
|
||||
# split the E0 04 family by READ_CONFIG (0xC0) probe, using the real hardware captures:
|
||||
# NTAG5 -> session reg 0xA0 succeeds; DNA -> 0xA0 errors but config 0x00 succeeds;
|
||||
# SLIX2 -> no READ_CONFIG (timeout) but READ_SIGNATURE answers; SLIX -> neither.
|
||||
from pm3py.cli.rawcli.identify import name_iso15
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
def dev_with(responses):
|
||||
dev = MagicMock()
|
||||
def _raw(frame):
|
||||
f = bytes(frame)
|
||||
if f[:2] == b"\x22\xc0": # READ_CONFIG addressed: key by block
|
||||
return responses.get(("cfg", f[-2]), {"raw": None})
|
||||
if f[:3] == b"\x02\xbd\x04": # READ_SIGNATURE
|
||||
return responses.get("sig", {"raw": None})
|
||||
return {"raw": None}
|
||||
dev.hf.iso15.raw.side_effect = _raw
|
||||
return dev
|
||||
|
||||
ntag5 = dev_with({("cfg", 0xA0): {"raw": bytes.fromhex("0001400000bad5")}})
|
||||
assert name_iso15(ntag5, "e0040158108ee802") == "NTAG 5"
|
||||
dna = dev_with({("cfg", 0xA0): {"raw": bytes.fromhex("010f68ee")},
|
||||
("cfg", 0x00): {"raw": bytes.fromhex("001e4d3235a1b8")}})
|
||||
assert name_iso15(dna, "e0040118009b4ccb") == "ICODE DNA"
|
||||
slix2 = dev_with({"sig": {"raw": bytes([0x00]) + bytes(34)}}) # 35-byte signature reply
|
||||
assert name_iso15(slix2, "e0040203040506") == "ICODE SLIX2"
|
||||
assert name_iso15(dev_with({}), "e004a2110b37c06b") == "ICODE SLIX"
|
||||
|
||||
def test_no_flat_lf(self):
|
||||
# nothing on HF and a flat LF envelope must report not-found, not a bogus "LF tag"
|
||||
assert identify(RawSession(device=_mock_device()))["found"] is False
|
||||
|
||||
Reference in New Issue
Block a user