Environment: main (sha=738cae84, v0.7.3), includes #899 (encrypted webhook) and #916 (source-RID backfill). Single system (MMR, WACN BEE00), P25 Phase 1 + Phase 2, Airspy + RTL wideband, consuming the per-call broadcast.webhook sink into Oracle.
Summary
Follow-up to #813. algorithm_id / key_id now populate — that part is fixed, thank you. But on the completed-call webhook they populate with bit-error values on weak/partial encrypted frames: alongside the correct AES-256, the columns carry a near-uniform smear across the entire 0x00–0xFF space, with a distinct key_id for almost every garbage call. The fields are being surfaced straight from the voice-side crypto sync without any validity/confidence gate, so a mis-decoded frame emits a plausible-looking-but-wrong algid+key rather than nothing.
Same root-cause pattern as the source-RID backfill in #915/#916: a low-confidence voice-path decode is surfaced as if authoritative.
Evidence
126,329 completed-call webhook rows. Every row has algorithm_id and key_id populated (good). To separate real calls from ones whose crypto metadata is itself a mis-decode, I split on corroboration: whether the call's source_rid also appears in SDRTrunk's GRP_VCH_GRANT / affiliation log for the same system (a CRC-protected control-channel decode — trustworthy).
Corroborated encrypted calls — algorithm_id distribution:
| algorithm_id |
|
calls |
distinct key_id |
0x84 |
AES-256 |
2,250 |
18 |
0x00 |
(unresolved) |
683 |
4 |
0x01–0xFF (every other value) |
non-standard |
~4–22 each |
≈ 1 key per call |
The 0x84 row is the real system: AES-256, ~18 active key IDs — exactly what's expected on this network, and stable. Everything below it is noise. The tell is the tail: a roughly-flat ~4–22 calls at every remaining algid value, each with about as many distinct key_ids as calls. Real crypto has a small enumerated algid set and a bounded keyset; a uniform 256-wide spread with key≈call is decode error, not signalling.
Corroborated clear calls: algorithm_id = 0 for all 113,642 (GT reports 0, not the P25 0x80, for unencrypted — minor, but worth noting for consumers).
Uncorroborated encrypted calls (source_rid itself a bit-error, i.e. #915/#916 territory): 0x00 (347), 0x84 (190), then the same random tail — as expected, these are the lowest-confidence decodes and their algid is garbage too.
Ask
Validate the decoded AlgorithmID against the enumerated TIA-102.AACA algorithm-ID set (0x80 clear, 0x81 DES-OFB, 0x83 3DES, 0x84 AES-256, 0x9F DES-XL, 0xAA ADP, plus the Type-1 block) before surfacing it — and ideally gate algorithm_id/key_id on the same decode-confidence signal that #915 wants for source_rid. When the crypto sync doesn't decode cleanly, omit the fields (leave them zero-valued / omitempty) rather than emitting an out-of-set algid and a spurious key. A value outside the known set is provably wrong and is currently indistinguishable, downstream, from a real key.
Happy to share the raw split or run a targeted capture if useful.
Environment:
main (sha=738cae84, v0.7.3), includes #899 (encrypted webhook) and #916 (source-RID backfill). Single system (MMR, WACN BEE00), P25 Phase 1 + Phase 2, Airspy + RTL wideband, consuming the per-callbroadcast.webhooksink into Oracle.Summary
Follow-up to #813.
algorithm_id/key_idnow populate — that part is fixed, thank you. But on the completed-call webhook they populate with bit-error values on weak/partial encrypted frames: alongside the correct AES-256, the columns carry a near-uniform smear across the entire0x00–0xFFspace, with a distinctkey_idfor almost every garbage call. The fields are being surfaced straight from the voice-side crypto sync without any validity/confidence gate, so a mis-decoded frame emits a plausible-looking-but-wrong algid+key rather than nothing.Same root-cause pattern as the source-RID backfill in #915/#916: a low-confidence voice-path decode is surfaced as if authoritative.
Evidence
126,329 completed-call webhook rows. Every row has
algorithm_idandkey_idpopulated (good). To separate real calls from ones whose crypto metadata is itself a mis-decode, I split on corroboration: whether the call'ssource_ridalso appears in SDRTrunk'sGRP_VCH_GRANT/ affiliation log for the same system (a CRC-protected control-channel decode — trustworthy).Corroborated encrypted calls —
algorithm_iddistribution:0x840x000x01–0xFF(every other value)The
0x84row is the real system: AES-256, ~18 active key IDs — exactly what's expected on this network, and stable. Everything below it is noise. The tell is the tail: a roughly-flat ~4–22 calls at every remaining algid value, each with about as many distinctkey_ids as calls. Real crypto has a small enumerated algid set and a bounded keyset; a uniform 256-wide spread with key≈call is decode error, not signalling.Corroborated clear calls:
algorithm_id = 0for all 113,642 (GT reports0, not the P250x80, for unencrypted — minor, but worth noting for consumers).Uncorroborated encrypted calls (source_rid itself a bit-error, i.e. #915/#916 territory):
0x00(347),0x84(190), then the same random tail — as expected, these are the lowest-confidence decodes and their algid is garbage too.Ask
Validate the decoded
AlgorithmIDagainst the enumerated TIA-102.AACA algorithm-ID set (0x80clear,0x81DES-OFB,0x833DES,0x84AES-256,0x9FDES-XL,0xAAADP, plus the Type-1 block) before surfacing it — and ideally gatealgorithm_id/key_idon the same decode-confidence signal that #915 wants forsource_rid. When the crypto sync doesn't decode cleanly, omit the fields (leave them zero-valued /omitempty) rather than emitting an out-of-set algid and a spurious key. A value outside the known set is provably wrong and is currently indistinguishable, downstream, from a real key.Happy to share the raw split or run a targeted capture if useful.