feat(sim): reject wrong-protocol tags in start_14a/start_15693; export tags; uid getter
- start_14a now raises TypeError for a non-Tag14443A transponder, start_15693 likewise for a non-Tag15693 (local imports to avoid the base<->sim circular import). Prevents silently starting a sim with an incompatible tag model. - Re-export the full 14a/14b/15693 tag lineup (NTAG21x, Ultralight family, NTAG I2C, ST25*, OPTIGA, ISO14443B, TI parts) from pm3py.sim for `from pm3py.sim import NTAG213`-style use. - Add a read-only `uid` property on the Transponder base (mirrors set_uid()). - Tests for both guards. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -23,6 +23,20 @@ from pm3py.transponders.hf.iso14443a.nxp.crypto1 import Crypto1
|
|||||||
from pm3py.transponders.hf.iso14443a.nxp.mifare_classic import MifareClassicTag, MifareClassicReader
|
from pm3py.transponders.hf.iso14443a.nxp.mifare_classic import MifareClassicTag, MifareClassicReader
|
||||||
from pm3py.transponders.hf.iso14443a.nxp.desfire import DesfireTag, DesfireReader
|
from pm3py.transponders.hf.iso14443a.nxp.desfire import DesfireTag, DesfireReader
|
||||||
from pm3py.transponders.hf.iso14443a.ndef import NfcType2Tag, NfcType4Tag
|
from pm3py.transponders.hf.iso14443a.ndef import NfcType2Tag, NfcType4Tag
|
||||||
|
from pm3py.transponders.hf.iso14443a.nxp.type2 import NxpType2Tag
|
||||||
|
from pm3py.transponders.hf.iso14443a.nxp.ntag21x import (
|
||||||
|
Ntag21x, NTAG210, NTAG212, NTAG213, NTAG215, NTAG216,
|
||||||
|
)
|
||||||
|
from pm3py.transponders.hf.iso14443a.nxp.ultralight import (
|
||||||
|
MifareUltralight, MifareUltralightC, MifareUltralightEV1, MF0UL11, MF0UL21,
|
||||||
|
UL_C_DEFAULT_KEY,
|
||||||
|
)
|
||||||
|
from pm3py.transponders.hf.iso14443a.nxp.ntag_i2c import NtagI2C, NT3H2111, NT3H2211
|
||||||
|
from pm3py.transponders.hf.iso14443a.st.st25tn import ST25TN, ST25TN512, ST25TN01K
|
||||||
|
from pm3py.transponders.hf.iso14443a.infineon.optiga_nbt import OptigaAuthenticateNBT
|
||||||
|
from pm3py.transponders.hf.iso14443a.st.st25ta import (
|
||||||
|
ST25TA, ST25TA512B, ST25TA02KB, ST25TA16K, ST25TA64K,
|
||||||
|
)
|
||||||
from pm3py.transponders.hf.iso15693.base import Tag15693, Reader15693, State15693
|
from pm3py.transponders.hf.iso15693.base import Tag15693, Reader15693, State15693
|
||||||
from pm3py.transponders.hf.iso15693.type5 import NfcType5Tag, ndef_text, ndef_uri, ndef_mime
|
from pm3py.transponders.hf.iso15693.type5 import NfcType5Tag, ndef_text, ndef_uri, ndef_mime
|
||||||
from pm3py.transponders.hf.iso15693.nxp.nxp_icode import NxpIcodeTag
|
from pm3py.transponders.hf.iso15693.nxp.nxp_icode import NxpIcodeTag
|
||||||
@@ -34,6 +48,20 @@ from pm3py.transponders.hf.iso15693.nxp.ntag5_platform import Ntag5PlatformTag
|
|||||||
from pm3py.transponders.hf.iso15693.nxp.ntag5_switch import Ntag5SwitchTag
|
from pm3py.transponders.hf.iso15693.nxp.ntag5_switch import Ntag5SwitchTag
|
||||||
from pm3py.transponders.hf.iso15693.nxp.ntag5_link import Ntag5LinkTag
|
from pm3py.transponders.hf.iso15693.nxp.ntag5_link import Ntag5LinkTag
|
||||||
from pm3py.transponders.hf.iso15693.nxp.ntag5_boost import Ntag5BoostTag
|
from pm3py.transponders.hf.iso15693.nxp.ntag5_boost import Ntag5BoostTag
|
||||||
|
from pm3py.transponders.hf.iso15693.st.st25tv import ST25TV, ST25TV512C, ST25TV02KC
|
||||||
|
from pm3py.transponders.hf.iso15693.st.st25dv import ST25DV, ST25DV04K, ST25DV16K, ST25DV64K
|
||||||
|
from pm3py.transponders.hf.iso15693.infineon.myd_vicinity import (
|
||||||
|
MydVicinity, SRF55V02P, SRF55V10P,
|
||||||
|
)
|
||||||
|
from pm3py.transponders.hf.iso14443b.base import Tag14443B, StateB, Tag14443B_4, StateB4
|
||||||
|
from pm3py.transponders.hf.iso14443b.st.st25tb import (
|
||||||
|
ST25TB, ST25TB512AC, ST25TB512AT, ST25TB02K, ST25TB04K,
|
||||||
|
)
|
||||||
|
from pm3py.transponders.hf.iso14443b.ti.rf430cl330h import RF430CL330H
|
||||||
|
from pm3py.transponders.hf.iso15693.ti.tagit import TagItHFIPlus
|
||||||
|
from pm3py.transponders.hf.iso15693.ti.rf430frl import (
|
||||||
|
RF430FRL15xH, RF430FRL152H, RF430FRL153H, RF430FRL154H,
|
||||||
|
)
|
||||||
from pm3py.transponders.lf.base import TagLF, ReaderLF, Modulation
|
from pm3py.transponders.lf.base import TagLF, ReaderLF, Modulation
|
||||||
from pm3py.transponders.lf.em.em4100 import EM4100Tag, EM4100Reader
|
from pm3py.transponders.lf.em.em4100 import EM4100Tag, EM4100Reader
|
||||||
from pm3py.transponders.lf.hid.hid import HIDProxTag, HIDReader
|
from pm3py.transponders.lf.hid.hid import HIDProxTag, HIDReader
|
||||||
@@ -55,6 +83,22 @@ __all__ = [
|
|||||||
"HIDProxTag", "HIDReader",
|
"HIDProxTag", "HIDReader",
|
||||||
"T5577Tag", "T5577Reader",
|
"T5577Tag", "T5577Reader",
|
||||||
"NfcType2Tag", "NfcType4Tag", "NfcType5Tag",
|
"NfcType2Tag", "NfcType4Tag", "NfcType5Tag",
|
||||||
|
"NxpType2Tag",
|
||||||
|
"Ntag21x", "NTAG210", "NTAG212", "NTAG213", "NTAG215", "NTAG216",
|
||||||
|
"MifareUltralight", "MifareUltralightC", "MifareUltralightEV1",
|
||||||
|
"MF0UL11", "MF0UL21", "UL_C_DEFAULT_KEY",
|
||||||
|
"NtagI2C", "NT3H2111", "NT3H2211",
|
||||||
|
"ST25TN", "ST25TN512", "ST25TN01K",
|
||||||
|
"ST25TA", "ST25TA512B", "ST25TA02KB", "ST25TA16K", "ST25TA64K",
|
||||||
|
"OptigaAuthenticateNBT",
|
||||||
|
"ST25TV", "ST25TV512C", "ST25TV02KC",
|
||||||
|
"ST25DV", "ST25DV04K", "ST25DV16K", "ST25DV64K",
|
||||||
|
"MydVicinity", "SRF55V02P", "SRF55V10P",
|
||||||
|
"Tag14443B", "StateB", "Tag14443B_4", "StateB4",
|
||||||
|
"ST25TB", "ST25TB512AC", "ST25TB512AT", "ST25TB02K", "ST25TB04K",
|
||||||
|
"RF430CL330H",
|
||||||
|
"TagItHFIPlus",
|
||||||
|
"RF430FRL15xH", "RF430FRL152H", "RF430FRL153H", "RF430FRL154H",
|
||||||
"ndef_text", "ndef_uri", "ndef_mime",
|
"ndef_text", "ndef_uri", "ndef_mime",
|
||||||
"NxpIcodeTag", "IcodeSlixTag", "IcodeSlix2Tag",
|
"NxpIcodeTag", "IcodeSlixTag", "IcodeSlix2Tag",
|
||||||
"Icode3Tag", "IcodeDnaTag",
|
"Icode3Tag", "IcodeDnaTag",
|
||||||
|
|||||||
@@ -140,6 +140,16 @@ class SimSession:
|
|||||||
14a-4 (ISO-DEP / WTX relay) reuses the same firmware trace but routes it
|
14a-4 (ISO-DEP / WTX relay) reuses the same firmware trace but routes it
|
||||||
through the async relay loop — see the plan's I/O model decision.
|
through the async relay loop — see the plan's I/O model decision.
|
||||||
"""
|
"""
|
||||||
|
# Local import: pm3py.transponders.hf.iso14443a.base pulls in pm3py.sim,
|
||||||
|
# so a module-level import here would be circular.
|
||||||
|
from pm3py.transponders.hf.iso14443a.base import Tag14443A
|
||||||
|
if not isinstance(tag, Tag14443A):
|
||||||
|
raise TypeError(
|
||||||
|
f"start_14a requires an ISO 14443-A transponder (a Tag14443A "
|
||||||
|
f"subclass), got {type(tag).__name__}. Use start_15693() for "
|
||||||
|
f"ISO 15693 tags."
|
||||||
|
)
|
||||||
|
|
||||||
self._tag_model = tag
|
self._tag_model = tag
|
||||||
self._on_frame = on_frame
|
self._on_frame = on_frame
|
||||||
self._quiet = quiet
|
self._quiet = quiet
|
||||||
@@ -217,6 +227,16 @@ class SimSession:
|
|||||||
dict shape). Passing it auto-enables trace streaming.
|
dict shape). Passing it auto-enables trace streaming.
|
||||||
quiet: if True, don't print frames (callback + `entries` still fill).
|
quiet: if True, don't print frames (callback + `entries` still fill).
|
||||||
"""
|
"""
|
||||||
|
# Local import: pm3py.transponders.hf.iso15693.base pulls in pm3py.sim,
|
||||||
|
# so a module-level import here would be circular.
|
||||||
|
from pm3py.transponders.hf.iso15693.base import Tag15693
|
||||||
|
if not isinstance(tag, Tag15693):
|
||||||
|
raise TypeError(
|
||||||
|
f"start_15693 requires an ISO 15693 transponder (a Tag15693 "
|
||||||
|
f"subclass), got {type(tag).__name__}. Use start_14a() for "
|
||||||
|
f"ISO 14443-A tags."
|
||||||
|
)
|
||||||
|
|
||||||
self._tag_model = tag
|
self._tag_model = tag
|
||||||
self._on_frame = on_frame
|
self._on_frame = on_frame
|
||||||
self._quiet = quiet
|
self._quiet = quiet
|
||||||
|
|||||||
@@ -128,3 +128,8 @@ class Transponder(abc.ABC):
|
|||||||
for r in self.regions.values():
|
for r in self.regions.values():
|
||||||
if r.eml_offset >= 0:
|
if r.eml_offset >= 0:
|
||||||
self._eml_push_region(r)
|
self._eml_push_region(r)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def uid(self) -> bytes:
|
||||||
|
"""Current tag UID."""
|
||||||
|
return self._parse_uid(self._uid)
|
||||||
@@ -115,3 +115,25 @@ def test_14a_entries_accumulate_and_clear():
|
|||||||
assert [e["direction"] for e in s.entries] == [0, 1]
|
assert [e["direction"] for e in s.entries] == [0, 1]
|
||||||
s.clear()
|
s.clear()
|
||||||
assert s.entries == []
|
assert s.entries == []
|
||||||
|
|
||||||
|
|
||||||
|
def test_start_14a_rejects_non_14443a_tag():
|
||||||
|
"""A 15693 (or any non-Tag14443A) transponder must be refused up front."""
|
||||||
|
import pytest
|
||||||
|
from pm3py.sim import Icode3Tag
|
||||||
|
|
||||||
|
s = SimSession()
|
||||||
|
s._port = MagicMock() # would otherwise fail on port resolution
|
||||||
|
with pytest.raises(TypeError, match="ISO 14443-A"):
|
||||||
|
s.start_14a(Icode3Tag(uid=None))
|
||||||
|
|
||||||
|
|
||||||
|
def test_start_15693_rejects_non_15693_tag():
|
||||||
|
"""Symmetric guard: a 14443-A tag must be refused by start_15693."""
|
||||||
|
import pytest
|
||||||
|
from pm3py.sim import NTAG213
|
||||||
|
|
||||||
|
s = SimSession()
|
||||||
|
s._port = MagicMock()
|
||||||
|
with pytest.raises(TypeError, match="ISO 15693"):
|
||||||
|
s.start_15693(NTAG213())
|
||||||
|
|||||||
Reference in New Issue
Block a user