feat(rawcli): identify + persistent connection (Phase 3)
- identify.py: probe HF (14a -> 15693) then LF; set session field / protocol / transponder. The 14a scan uses NO_DISCONNECT so the tag stays selected (the persistent connection). SAK -> coarse family name for the breadcrumb. - session: add `protocol` (drives raw-exchange routing) - app: connect via Proxmark3.sync() (scan/raw become plain calls); wire identify / transponder / close control commands; protocol-aware _raw_exchange; breadcrumb fills in field + transponder after identify 8 new tests (identify 14a/15693/none, clear, control commands). Device probes are mocked; live identify is the user's hardware test. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -12,35 +12,45 @@ import sys
|
||||
from .session import RawSession
|
||||
from .parser import parse_line
|
||||
from .entry import install_key_bindings
|
||||
from .identify import identify, clear, format_summary
|
||||
from .trace_view import render_exchange
|
||||
|
||||
_HELP = """\
|
||||
rawcli — raw command interface
|
||||
|
||||
help show this help
|
||||
identify probe the field; sets HF/LF + transponder, keeps the connection
|
||||
transponder show the identified transponder
|
||||
close drop the tag connection
|
||||
quit | exit | q leave rawcli
|
||||
<hex ...> send raw bytes to the tag; the annotated exchange prints above
|
||||
|
||||
Type hex as "30 04" or "3004". Entry-mode toggle (0x/0b), identify, and function-style
|
||||
transponder commands (READ(4), GET_DATA) land in later phases."""
|
||||
Type hex as "30 04" or "3004"; Ctrl-/ toggles hex/binary entry (0x/0b). Function-style
|
||||
transponder commands (READ(4), GET_DATA) land in the next phase."""
|
||||
|
||||
|
||||
def _connect(port):
|
||||
"""Best-effort device connection; returns the client or None (offline)."""
|
||||
try:
|
||||
from pm3py import Proxmark3
|
||||
return Proxmark3(port)
|
||||
return Proxmark3.sync(port) # sync proxy: scan()/raw() are plain calls
|
||||
except Exception as exc: # no device / connect failure — run offline
|
||||
print(f"rawcli: no device ({type(exc).__name__}: {exc}); running offline", file=sys.stderr)
|
||||
return None
|
||||
|
||||
|
||||
def _raw_exchange(device, payload: bytes) -> bytes | None:
|
||||
"""Issue one raw 14a exchange, returning the response bytes (or None)."""
|
||||
def _raw_exchange(device, payload: bytes, protocol: str = "hf14a") -> bytes | None:
|
||||
"""Issue one raw exchange over the identified protocol, returning the response bytes."""
|
||||
if device is None:
|
||||
return None
|
||||
try:
|
||||
resp = device.hf.iso14a.raw(payload)
|
||||
if protocol == "hf14a":
|
||||
resp = device.hf.iso14a.raw(payload)
|
||||
elif protocol == "hf15":
|
||||
raw = getattr(device.hf.iso15, "raw", None)
|
||||
resp = raw(payload) if raw else None
|
||||
else:
|
||||
return None # lf raw exchange lands in a later phase
|
||||
return resp.get("data") if isinstance(resp, dict) else None
|
||||
except Exception as exc:
|
||||
print(f"rawcli: exchange failed ({type(exc).__name__}: {exc})", file=sys.stderr)
|
||||
@@ -61,9 +71,13 @@ def dispatch(state: RawSession, line: str) -> bool:
|
||||
return False
|
||||
if cmd.name == "help":
|
||||
print(_HELP)
|
||||
else:
|
||||
# identify / transponder / close land in Phase 3
|
||||
print(f"rawcli: '{cmd.name}' is not wired yet (coming in a later phase)")
|
||||
elif cmd.name == "identify":
|
||||
print(format_summary(identify(state)))
|
||||
elif cmd.name == "transponder":
|
||||
print(state.transponder or "no transponder identified — run 'identify'")
|
||||
elif cmd.name == "close":
|
||||
clear(state)
|
||||
print("connection closed")
|
||||
return True
|
||||
|
||||
if cmd.kind == "call":
|
||||
@@ -74,8 +88,8 @@ def dispatch(state: RawSession, line: str) -> bool:
|
||||
# raw payload
|
||||
if not cmd.payload:
|
||||
return True
|
||||
response = _raw_exchange(state.device, cmd.payload)
|
||||
print(render_exchange(cmd.payload, response, protocol="hf14a",
|
||||
response = _raw_exchange(state.device, cmd.payload, protocol=state.protocol)
|
||||
print(render_exchange(cmd.payload, response, protocol=state.protocol,
|
||||
is_tty=sys.stdout.isatty()))
|
||||
return True
|
||||
|
||||
|
||||
Reference in New Issue
Block a user