feat: exact signature-DB match for decoded KEY .sub files

A Flipper KEY file names an already-decoded protocol but carries no pulses.
Previously _decode_from_key ignored the signature database entirely: it
synthesized a fake signature from the category router's guess, so an exact,
known protocol scored like a fuzzy guess (~0.45) and surfaced the router's
coarser category (e.g. Princeton -> "Remote Control") while discarding the
real catalog manufacturer (e.g. HCS301 lost "Microchip").

Now a decoded protocol name is looked up (case-insensitive) in the signature
DB. On a hit we surface the REAL catalog signature -- true manufacturer and
category (Princeton -> "Garage Door Opener") -- with high, frequency-aware
confidence (0.95 when the band is consistent, 0.90 otherwise): an exact
database match, not a timing guess. Unknown names keep the router fallback so
uploads are still identified by family rather than silently dropped.

Advances the "database matching" + "confidence scoring (exact/partial)" goals.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
leetcrypt
2026-07-21 12:07:57 -07:00
parent 37c9f7d67d
commit f5b0983044
+58 -3
View File
@@ -146,10 +146,41 @@ class PatternDecoder:
Identify a decoded (KEY) .sub file from its Protocol name.
KEY files have no RAW pulses but carry a decoded `Protocol:` name,
frequency and key. We route the protocol name to a device category and
emit a match surfacing the real Flipper protocol + routed category, so
decoded uploads are no longer silently unidentifiable.
frequency and key. Two cases:
1. The named protocol is in our signature database. This is the
strongest possible identification — the capture device already
demodulated and *named* the protocol, and we recognise it — so we
surface the REAL catalog signature (true manufacturer + category)
with high, frequency-aware confidence. This is an exact "database
match", not a timing guess, and it keeps the surfaced category
consistent with the catalog (e.g. Princeton -> Garage Door Opener)
instead of the router's coarser guess.
2. The name is unknown to us. Fall back to category routing so the
upload is still identified by family rather than silently dropped.
"""
known = self._lookup_known_protocol(metadata.protocol, metadata.frequency)
if known is not None:
freq_ok = bool(metadata.frequency) and known.matches_frequency(metadata.frequency)
# Exact, already-decoded match against a known protocol. Confidence
# is high but never a literal 1.0 (the key payload itself is not
# cryptographically verified); a consistent frequency lifts it.
confidence = 0.95 if freq_ok else 0.90
details = {
"predicted_category": known.category,
"source": "decoded_key_exact",
"bit_length": metadata.bit_length,
"frequency_match": freq_ok,
"manufacturer": known.manufacturer,
}
return [DeviceMatch(
protocol=known,
confidence=confidence,
match_method="decoded_key_exact",
details=details,
)]
pred = self.category_router.route_by_protocol(
metadata.protocol, metadata.frequency
)
@@ -172,6 +203,30 @@ class PatternDecoder:
details=details,
)]
def _lookup_known_protocol(
self, name: Optional[str], frequency: Optional[int]
) -> Optional[ProtocolSignature]:
"""Exact (case-insensitive) protocol-name lookup in the signature DB.
When several catalog entries share a name, prefer one whose frequency
band matches the capture; otherwise return the first. Returns None if
the name is unknown.
"""
if not name:
return None
target = name.strip().lower()
candidates = [
p for p in self.protocol_db.get_all()
if p.name.strip().lower() == target
]
if not candidates:
return None
if frequency:
for cand in candidates:
if cand.matches_frequency(frequency):
return cand
return candidates[0]
def _identify_pulse_widths(self, pulses: List[int]) -> Tuple[int, int, int, int]:
"""
Identify SHORT/LONG pulse and gap durations using robust timing analyzer