From 5dd8e086cc259c11e6948ab37f8e0b6e0e4a1770 Mon Sep 17 00:00:00 2001 From: michael Date: Tue, 14 Jul 2026 16:00:14 -0700 Subject: [PATCH] fix(lf): per-protocol identify label (native HID/AWID/FDX-B no longer KeyError) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit identify_lf built its label from emitted['id_hex'], which only EM4100 provides — a native HID/AWID/FDX-B tag (no T5577 config) would KeyError. Add credential_label() to format each decoder's own fields: "HID Prox H10301 FC.. Card..", "AWID-26 FC.. Card..", "FDX-B 124-000000000555 (animal)", "EM4100 ". Tests for native FDX-B + HID labels. Co-Authored-By: Claude Opus 4.8 --- pm3py/lf/capture.py | 18 ++++++++++++++++-- tests/test_lf_demod.py | 13 +++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/pm3py/lf/capture.py b/pm3py/lf/capture.py index 030d163..6994ccb 100644 --- a/pm3py/lf/capture.py +++ b/pm3py/lf/capture.py @@ -22,6 +22,20 @@ _DECODERS = ( ) +def credential_label(c: dict) -> str: + """One-line name for a decoded credential, per protocol (each decoder returns its own fields).""" + p = c.get("protocol", "?") + if p == "EM4100": + return f"EM4100 {c['id_hex']}" + if p == "HID Prox": + return f"HID Prox {c['format']} FC {c['facility_code']} Card {c['card_number']}" + if p == "AWID": + return f"{c['format']} FC {c['facility_code']} Card {c['card_number']}" + if p == "FDX-B": + return f"FDX-B {c['id']}" + (" (animal)" if c.get("animal") else "") + return p + + def demod_samples(samples) -> dict | None: """Decode a raw LF envelope to a credential by trying each protocol demod in turn. @@ -87,13 +101,13 @@ def identify_lf(device, emit=None) -> dict | None: f"({config['modulation']}, RF/{config['data_bit_rate']}, {config['max_block']} blocks)" f" -> {emulating}") if emitted is not None: - emit(f" emitted: {emitted['protocol']} id {emitted['id_hex']}") + emit(f" emitted: {credential_label(emitted)}") # label: prefer the physical chip + what it emulates; else the emitted credential if chip: label = f"{chip} ({emulating})" else: - label = f"{emitted['protocol']} {emitted['id_hex']}" + label = credential_label(emitted) return { "found": True, diff --git a/tests/test_lf_demod.py b/tests/test_lf_demod.py index ed4397b..6aa969e 100644 --- a/tests/test_lf_demod.py +++ b/tests/test_lf_demod.py @@ -343,6 +343,19 @@ class TestIdentifyLF: assert r["found"] and r["chip"] is None assert r["label"] == "EM4100 1122334455" + def test_native_fdxb_label(self): + # a native FDX-B tag (no T55xx) must label per-protocol, not KeyError on id_hex + dev = _mock_lf_device(config_env=bytes([128] * 4000), + emitted_env=_fdxb_env(124, 555, animal=1)) + r = identify_lf(dev) + assert r["found"] and r["chip"] is None + assert r["label"].startswith("FDX-B 124-000000000555") + + def test_native_hid_label(self): + dev = _mock_lf_device(config_env=bytes([128] * 4000), emitted_env=_hid_env(12, 3456)) + r = identify_lf(dev) + assert r["label"] == "HID Prox H10301 FC 12 Card 3456" + def test_nothing_on_antenna(self): dev = _mock_lf_device(config_env=bytes([128] * 4000), emitted_env=bytes([128] * 4000)) assert identify_lf(dev) is None