feat(pm3py): soft firmware compatibility check on connect (ping + version probe)

This commit is contained in:
michael
2026-03-15 22:25:37 -07:00
parent fec04acc23
commit d9ec661d06
2 changed files with 103 additions and 1 deletions

View File

@@ -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."""