Files
pm3py/tests/test_integration.py
michael 75bf0e19cb feat(hf): rename mfc -> mf (MIFARE Classic), add mfu (Ultralight/NTAG)
hf.mfc renamed to hf.mf to match the C client's 'hf mf'. New hf.mfu (HFMFUCommands) provides rdbl/wrbl/dump over the dedicated CMD_HF_MIFAREU_READBL/WRITEBL using the firmware mful_readblock_t / mful_writeblock_t structs; a READ returns 4 pages (16 bytes) and the firmware replies them directly. keytype selects auth (0=none, 1=UL-C, 2=EV1/NTAG pwd, 3=UL-AES). Wire formats verified against firmware structs; needs a bench pass for the auth/write-length paths.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-05 00:40:07 -07:00

126 lines
4.3 KiB
Python

"""End-to-end test using mocked transport to verify full API surface."""
import struct
import asyncio
from unittest.mock import AsyncMock, MagicMock
from pm3py import Proxmark3, Cmd
from pm3py.core.client import FirmwareInfo
def test_full_api_surface():
"""Verify all nested attributes exist."""
pm3 = Proxmark3("/dev/null")
assert hasattr(pm3, "hw")
assert hasattr(pm3, "lf")
assert hasattr(pm3, "hf")
assert hasattr(pm3.lf, "t55")
assert hasattr(pm3.hf, "iso14a")
assert hasattr(pm3.hf, "iso15")
assert hasattr(pm3.hf, "mf")
assert hasattr(pm3, "send_ng")
assert hasattr(pm3, "send_mix")
assert hasattr(pm3, "firmware")
assert isinstance(pm3.firmware, FirmwareInfo)
def test_hw_ping_roundtrip():
"""Test ping returns structured dict."""
pm3 = Proxmark3("/dev/null")
mock_transport = AsyncMock()
from pm3py.core.transport import PM3Response
ping_data = bytes(range(32))
mock_transport.send_ng.return_value = PM3Response(
cmd=Cmd.PING, status=0, reason=0, ng=True, data=ping_data)
pm3.hw._t = mock_transport
result = asyncio.get_event_loop().run_until_complete(pm3.hw.ping(32))
assert isinstance(result, dict)
assert result["success"] is True
def test_mf_rdbl_returns_hex():
"""Verify MIFARE read returns hex string data."""
pm3 = Proxmark3("/dev/null")
mock_transport = AsyncMock()
from pm3py.core.transport import PM3Response
block = bytes(range(16))
mock_transport.send_ng.return_value = PM3Response(
cmd=Cmd.HF_MIFARE_READBL, status=0, reason=0, ng=True, data=block)
pm3.hf.mf._t = mock_transport
result = asyncio.get_event_loop().run_until_complete(
pm3.hf.mf.rdbl(block=4, key="FFFFFFFFFFFF"))
assert result["data"] == "000102030405060708090a0b0c0d0e0f"
assert result["raw"] == block
def test_sync_proxy():
"""Verify sync proxy wraps async calls."""
from pm3py.core.transport import PM3Response
from pm3py.core.client import _SyncProxy
pm3 = Proxmark3("/dev/null")
loop = asyncio.new_event_loop()
proxy = _SyncProxy(pm3, loop)
# Mock the transport on the command object
mock_transport = AsyncMock()
proxy._target.hw._t = mock_transport
ping_data = bytes(range(32))
mock_transport.send_ng.return_value = PM3Response(
cmd=Cmd.PING, status=0, reason=0, ng=True, data=ping_data)
# Call synchronously — no await needed
result = proxy.hw.ping(32)
assert result["success"] is True
assert result["length"] == 32
loop.close()
def test_firmware_probe_compatible():
"""Verify firmware probe populates FirmwareInfo on successful connect."""
pm3 = Proxmark3("/dev/null")
mock_transport = AsyncMock()
pm3._transport = mock_transport
pm3.hw._t = mock_transport
from pm3py.core.transport import PM3Response
ping_data = bytes(range(32))
vstr = b"Proxmark3 RDV4.0 FW v4.18000\x00"
version_payload = struct.pack("<III", 0x270B0A40, 512*1024, len(vstr)) + vstr
# send_ng called twice: once for ping, once for version
mock_transport.send_ng.side_effect = [
PM3Response(cmd=Cmd.PING, status=0, reason=0, ng=True, data=ping_data),
PM3Response(cmd=Cmd.VERSION, status=0, reason=0, ng=True, data=version_payload),
]
asyncio.get_event_loop().run_until_complete(pm3._probe_firmware())
assert pm3.firmware.compatible is True
assert pm3.firmware.ng_protocol is True
assert "Proxmark3 RDV4.0" in pm3.firmware.version_string
assert pm3.firmware.warnings == []
def test_firmware_probe_ping_fails():
"""Verify firmware probe marks incompatible when ping fails."""
pm3 = Proxmark3("/dev/null")
mock_transport = AsyncMock()
pm3._transport = mock_transport
pm3.hw._t = mock_transport
from pm3py.core.transport import PM3Response, PM3Error
vstr = b"Proxmark3 OLD\x00"
version_payload = struct.pack("<III", 0x270B0A40, 256*1024, len(vstr)) + vstr
mock_transport.send_ng.side_effect = [
PM3Error("Ping failed"),
PM3Response(cmd=Cmd.VERSION, status=0, reason=0, ng=True, data=version_payload),
]
asyncio.get_event_loop().run_until_complete(pm3._probe_firmware())
assert pm3.firmware.compatible is False
assert pm3.firmware.ng_protocol is False
assert len(pm3.firmware.warnings) > 0