feat(pm3py): soft firmware compatibility check on connect (ping + version probe)
This commit is contained in:
@@ -1,5 +1,8 @@
|
||||
"""Main Proxmark3 client with nested API."""
|
||||
from .transport import PM3Transport, PM3Response, ProgressCallback
|
||||
import logging
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
from .transport import PM3Transport, PM3Response, PM3Error, ProgressCallback
|
||||
from .hw import HWCommands
|
||||
from .lf import LFCommands
|
||||
from .hf import HFCommands
|
||||
@@ -7,12 +10,27 @@ from .hf_14a import HF14ACommands
|
||||
from .hf_15 import HF15Commands
|
||||
from .hf_mf import HFMFCommands
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@dataclass
|
||||
class FirmwareInfo:
|
||||
"""Firmware version and compatibility information populated on connect."""
|
||||
version_string: str = ""
|
||||
chip_id: str = ""
|
||||
section_size: int = 0
|
||||
ng_protocol: bool = False
|
||||
compatible: bool = False
|
||||
warnings: list[str] = field(default_factory=list)
|
||||
|
||||
|
||||
class Proxmark3:
|
||||
"""Proxmark3 device client with structured Python API.
|
||||
|
||||
Usage:
|
||||
async with Proxmark3("/dev/ttyACM0") as pm3:
|
||||
if not pm3.firmware.compatible:
|
||||
print("Warnings:", pm3.firmware.warnings)
|
||||
status = await pm3.hw.version()
|
||||
print(status)
|
||||
|
||||
@@ -28,9 +46,11 @@ class Proxmark3:
|
||||
self.hf.a14 = HF14ACommands(self._transport)
|
||||
self.hf.iso15 = HF15Commands(self._transport)
|
||||
self.hf.mf = HFMFCommands(self._transport)
|
||||
self.firmware = FirmwareInfo()
|
||||
|
||||
async def connect(self) -> None:
|
||||
await self._transport.connect()
|
||||
await self._probe_firmware()
|
||||
|
||||
async def disconnect(self) -> None:
|
||||
await self._transport.disconnect()
|
||||
@@ -42,6 +62,36 @@ class Proxmark3:
|
||||
async def __aexit__(self, *args) -> None:
|
||||
await self.disconnect()
|
||||
|
||||
async def _probe_firmware(self) -> None:
|
||||
"""Ping device and fetch firmware version after connecting."""
|
||||
fw = self.firmware
|
||||
fw.warnings = []
|
||||
|
||||
# Step 1: Ping to verify NG protocol support
|
||||
try:
|
||||
ping_result = await self.hw.ping(32)
|
||||
fw.ng_protocol = ping_result.get("success", False)
|
||||
if not fw.ng_protocol:
|
||||
fw.warnings.append("Ping echo mismatch — device may not support NG protocol")
|
||||
except PM3Error:
|
||||
fw.ng_protocol = False
|
||||
fw.warnings.append("Ping failed — device may not support NG protocol or may be in bootloader mode")
|
||||
|
||||
# Step 2: Fetch firmware version
|
||||
try:
|
||||
ver = await self.hw.version()
|
||||
fw.version_string = ver.get("version_string", "")
|
||||
fw.chip_id = ver.get("chip_id", "")
|
||||
fw.section_size = ver.get("section_size", 0)
|
||||
except PM3Error:
|
||||
fw.warnings.append("Could not read firmware version — device may be in bootloader mode")
|
||||
|
||||
# Step 3: Assess compatibility
|
||||
fw.compatible = fw.ng_protocol and len(fw.warnings) == 0
|
||||
|
||||
for w in fw.warnings:
|
||||
log.warning("pm3py: %s", w)
|
||||
|
||||
async def send_ng(self, cmd: int, payload: bytes = b"",
|
||||
timeout: float = 2.0) -> PM3Response:
|
||||
"""Raw NG command -- escape hatch for unwrapped commands."""
|
||||
|
||||
Reference in New Issue
Block a user