fix(lf): T55xx config via rotation-to-preset match; label the emitted credential

Hardware-validated. Two real bugs in the T55xx config decode, both fixed by requiring a
bit-rotation of the recovered repeating word to match a known preset:

- Rotation ambiguity: a block read recovers the 32-bit config at an arbitrary bit offset.
  A real capture came back 0x000A4020 = the EM4100 word 0x00148040 rotated by one; the old
  "looks sane" scorer accepted the rotation verbatim and mislabeled it FSK1/RF-6. Now every
  rotation is tried and only a preset match (EM4100/HID/Indala/FDX-B/Viking/default) is
  accepted -> resolves to 0x00148040 = EM4100.
- False positives: garbage config captures (all-0, all-1, a rotation of the tag's own
  emission) no longer pass -> decode returns None and the reliable emitted-stream decode
  carries the result.

identify_lf label now names the chip + the actual decoded credential:
"T5577 (EM4100 00FFFFFFFF)" instead of the config's guess. On real hardware this is now
stable 4/4: config 00148040, emitted EM4100 00FFFFFFFF.

Tests: rotation recovery (0x000A4020 -> EM4100), no-false-positive on all-1s/random,
updated label expectation.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
michael
2026-07-14 21:26:56 -07:00
parent e82b94a490
commit 6c5409c111
3 changed files with 52 additions and 48 deletions

View File

@@ -300,6 +300,19 @@ class TestT55xxConfig:
assert r["modulation"] == "ASK"
assert r["emulating"] == "EM4100 / EM4102"
def test_config_rotation_recovers_preset(self):
# a block read recovers the config at some bit rotation (hardware gave 0x000A4020 =
# 0x00148040 >> 1); rotate-matching must still resolve it to the EM4100 preset
rotated = ((CONFIG_EM4100 >> 1) | ((CONFIG_EM4100 & 1) << 31)) & 0xFFFFFFFF
assert rotated == 0x000A4020
r = protocols.decode_t55xx_config(_t55xx_config_env(rotated))
assert r and r["config"] == CONFIG_EM4100 and r["emulating"] == "EM4100 / EM4102"
def test_config_no_false_positive(self):
# repeating words that aren't any preset rotation (all-1s, random) -> None, not a bogus config
assert protocols.decode_t55xx_config(_t55xx_config_env(0xFFFFFFFF)) is None
assert protocols.decode_t55xx_config(_t55xx_config_env(0x12345678)) is None
def test_preset_labels(self):
assert protocols.emulation_name(T5577Config._PRESETS["hid"]) == "HID Prox"
assert protocols.emulation_name(T5577Config._PRESETS["indala"]) == "Indala"
@@ -332,7 +345,8 @@ class TestIdentifyLF:
assert r["chip"] == "T5577"
assert r["emulating"] == "EM4100 / EM4102"
assert r["emitted"]["id_hex"] == "0102030405"
assert r["label"] == "T5577 (EM4100 / EM4102)"
# label names the chip + the actual decoded credential (not just the config's guess)
assert r["label"] == "T5577 (EM4100 0102030405)"
assert any("T55xx block 0" in l for l in lines)
def test_native_em4100_no_config(self):