feat: migrate sim framework from worktree to master
Migrates all 43 sim modules and 23 test files (~7.8k LOC, 686 tests) from .worktrees/sim-framework/ into pm3py/sim/. Import fixes: - 4 files: ..protocol/..transport → ..core.protocol/..core.transport - trace_fmt.py: pm3py.hf_15 → pm3py.trace.ndef - test_sim_pm3medium.py: flat imports → core.* - test_sim_trace_fmt.py: flat imports → core.* - Added sim Cmd entries to core/protocol.py (EML_SETMEM, SIM_TABLE_*) 751 tests passing (686 sim + 65 core). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
149
tests/test_sim_pm3medium.py
Normal file
149
tests/test_sim_pm3medium.py
Normal file
@@ -0,0 +1,149 @@
|
||||
"""Tests for PM3Medium — hardware-backed Medium using Proxmark3."""
|
||||
import asyncio
|
||||
import struct
|
||||
import pytest
|
||||
from unittest.mock import AsyncMock, MagicMock
|
||||
|
||||
from pm3py.sim.frame import RFFrame
|
||||
from pm3py.sim.pm3medium import PM3ReaderMedium
|
||||
from pm3py.core.transport import PM3Response
|
||||
from pm3py.core.protocol import Cmd, PM3Status
|
||||
|
||||
|
||||
def run(coro):
|
||||
return asyncio.get_event_loop().run_until_complete(coro)
|
||||
|
||||
|
||||
def _mock_transport():
|
||||
t = AsyncMock()
|
||||
return t
|
||||
|
||||
|
||||
def _14a_scan_response(uid=b"\x01\x02\x03\x04", atqa=b"\x04\x00", sak=0x08):
|
||||
"""Build a mock iso14a_card_select_t response."""
|
||||
# iso14a_card_select_t: uid[10] + uidlen[1] + atqa[2] + sak[1] + ats_len[1] + ats[256]
|
||||
data = uid + b"\x00" * (10 - len(uid))
|
||||
data += bytes([len(uid)])
|
||||
data += atqa
|
||||
data += bytes([sak])
|
||||
data += bytes([0]) # ats_len
|
||||
data += b"\x00" * 256 # ats
|
||||
# Select status comes from oldarg[0]: 1=OK+ATS, 2=OK no ATS
|
||||
return PM3Response(cmd=Cmd.HF_ISO14443A_READER, status=PM3Status.SUCCESS,
|
||||
reason=0, ng=False, data=data,
|
||||
oldarg=(2, 0, 0))
|
||||
|
||||
|
||||
def _14a_raw_response(data=b"\x90\x00"):
|
||||
"""Build a mock raw APDU response."""
|
||||
return PM3Response(cmd=Cmd.HF_ISO14443A_READER, status=PM3Status.SUCCESS,
|
||||
reason=0, ng=False, data=data,
|
||||
oldarg=(len(data), 0, 0))
|
||||
|
||||
|
||||
def _15693_response(data=b"\x00"):
|
||||
"""Build a mock ISO 15693 response."""
|
||||
return PM3Response(cmd=Cmd.HF_ISO15693_COMMAND, status=PM3Status.SUCCESS,
|
||||
reason=0, ng=True, data=data, oldarg=None)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# PM3ReaderMedium — 14443-A reader
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class TestPM3ReaderMedium14443A:
|
||||
def test_transmit_receive_14a_scan(self):
|
||||
t = _mock_transport()
|
||||
t.send_mix.return_value = _14a_scan_response()
|
||||
medium = PM3ReaderMedium(t, protocol="14443a")
|
||||
|
||||
# Transmit REQA (triggers scan)
|
||||
run(medium.transmit_reader(RFFrame.from_hex("26")))
|
||||
resp = run(medium.receive_reader())
|
||||
|
||||
assert resp is not None
|
||||
assert t.send_mix.called
|
||||
|
||||
def test_transmit_receive_14a_raw(self):
|
||||
t = _mock_transport()
|
||||
t.send_mix.return_value = _14a_raw_response(b"\x90\x00")
|
||||
medium = PM3ReaderMedium(t, protocol="14443a")
|
||||
|
||||
# Send a raw APDU
|
||||
apdu = b"\x00\xA4\x04\x00"
|
||||
run(medium.transmit_reader(RFFrame.from_bytes(apdu)))
|
||||
resp = run(medium.receive_reader())
|
||||
|
||||
assert resp is not None
|
||||
assert resp.data == b"\x90\x00"
|
||||
|
||||
def test_dropfield(self):
|
||||
t = _mock_transport()
|
||||
t.send_ng_no_response = AsyncMock()
|
||||
medium = PM3ReaderMedium(t, protocol="14443a")
|
||||
|
||||
run(medium.dropfield())
|
||||
t.send_ng_no_response.assert_called_once()
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# PM3ReaderMedium — 15693 reader
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class TestPM3ReaderMedium15693:
|
||||
def test_transmit_receive_15693_inventory(self):
|
||||
t = _mock_transport()
|
||||
uid_lsb = b"\x03\x1B\x6A\x5C\x50\x01\x04\xE0"
|
||||
resp_data = b"\x00\x00" + uid_lsb # flags + dsfid + uid
|
||||
t.send_ng.return_value = _15693_response(resp_data)
|
||||
medium = PM3ReaderMedium(t, protocol="15693")
|
||||
|
||||
inv = bytes([0x26, 0x01, 0x00])
|
||||
run(medium.transmit_reader(RFFrame.from_bytes(inv)))
|
||||
resp = run(medium.receive_reader())
|
||||
|
||||
assert resp is not None
|
||||
assert len(resp.data) >= 10
|
||||
|
||||
def test_transmit_receive_15693_read_block(self):
|
||||
t = _mock_transport()
|
||||
block_data = b"\x00\xDE\xAD\xBE\xEF" # flags + data
|
||||
t.send_ng.return_value = _15693_response(block_data)
|
||||
medium = PM3ReaderMedium(t, protocol="15693")
|
||||
|
||||
read_cmd = bytes([0x22, 0x20]) + b"\x01" * 8 + bytes([0x00])
|
||||
run(medium.transmit_reader(RFFrame.from_bytes(read_cmd)))
|
||||
resp = run(medium.receive_reader())
|
||||
|
||||
assert resp is not None
|
||||
assert resp.data[1:5] == b"\xDE\xAD\xBE\xEF"
|
||||
|
||||
def test_timeout_returns_none(self):
|
||||
t = _mock_transport()
|
||||
t.send_ng.side_effect = TimeoutError()
|
||||
medium = PM3ReaderMedium(t, protocol="15693")
|
||||
|
||||
inv = bytes([0x26, 0x01, 0x00])
|
||||
run(medium.transmit_reader(RFFrame.from_bytes(inv)))
|
||||
resp = run(medium.receive_reader())
|
||||
|
||||
assert resp is None
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# PM3ReaderMedium — attach/detach (not applicable for hardware)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class TestPM3ReaderMediumLimitations:
|
||||
def test_attach_raises(self):
|
||||
"""Hardware medium doesn't support attaching software transponders."""
|
||||
t = _mock_transport()
|
||||
medium = PM3ReaderMedium(t, protocol="14443a")
|
||||
with pytest.raises(NotImplementedError):
|
||||
run(medium.attach(None))
|
||||
|
||||
def test_detach_raises(self):
|
||||
t = _mock_transport()
|
||||
medium = PM3ReaderMedium(t, protocol="14443a")
|
||||
with pytest.raises(NotImplementedError):
|
||||
run(medium.detach(0))
|
||||
Reference in New Issue
Block a user