Field Guide · algorithm

Also known as: Motorola alias cipher, per-byte alias cipher

The Motorola talker-alias cipher is the proprietary per-byte obfuscation Motorola applies to a radio’s talker alias — the human-readable display name — before it is fragmented across the air.1 It is not encryption for confidentiality in the AES sense; it is a lightweight, undocumented scrambling of the alias string that a receiver must reverse to read the name. Its structure is a length-seeded 16-bit additive accumulator, a 256-byte substitution table, and an odd-multiplier (modular-inverse-mod-256) step, with the decoded bytes read as UTF-16 BE.

This cipher was recovered by clean-room reverse engineering and is verified. It was reconstructed from black-box (input → output) observations of an existing decoder driven as an opaque oracle — no third-party source or table was read or copied — and it decodes a real over-the-air capture (RID 200062 → “CRIO 0062”) with a valid CRC. GopherTrunk decodes it by default (CipherVerified = true). For the full recovery method, reproducible validation, and the clean-room provenance/licensing record, see the clean-room provenance document.

W += 293 × (byte+1) LUT[byte] − (W≫8) × inv((W&0xFF)|1) decoded byte seed W = 0xC433 + 586·(n−1); repeat per byte — clean-room recovered, verified
The transform chains an additive accumulator, a table lookup, and an odd-multiplier per byte; the 256-byte table and constants were recovered clean-room and verified against real over-the-air data, so the path is enabled.

The per-byte transform

The decode-side transform lives in internal/radio/p25/motorola/alias.go. There is no vendor spec — the algorithm below was recovered from behaviour — so it is worth showing:

// W seeded from the character count n = len(encoded)/2; LUT is a 256-byte table.
n := len(encoded) / 2
w := uint16(0xC433 + 586*(n-1))                     // length seed (586 = 2*293)
for i := 0; i < 2*n; i++ {
    // per-byte substitution: odd-multiplier × (table lookup − accumulator high byte)
    out[i] = motorolaAliasInvOdd[byte(w)|1] * (motorolaAliasLUT[encoded[i]] - byte(w>>8))
    w += uint16(293 * (int(encoded[i]) + 1))        // additive accumulator update
}
// pack pairs into UTF-16BE chars; force the high byte to 0xFF when the low byte is >= 0x80.

motorolaAliasInvOdd[(W&0xFF)|1] is the multiplicative inverse of the accumulator’s low byte (forced odd) modulo 256, and the subtraction mixes in the accumulator’s high byte W>>8. The accumulator update is additive (W += 293·(byte+1)) — an earlier inferred multiplicative form (accum×293+0x72E9) was wrong and decoded nothing; the oracle recovery corrected it. The decoded byte stream is read as UTF-16 BE and rendered to printable ASCII.

Verification status

Both the SUID framing (WACN / System / Radio ID prefix) and the cipher itself are verified. The cipher was recovered by clean-room reverse engineering — reconstructed from ~22,000 black-box (input → output) observations of an existing decoder driven as an opaque oracle — and it reproduces that oracle byte-for-byte on a 300-row held-out set it never trained on (1242/1242 characters). Independently, it decodes a real over-the-air capture (RID 200062) to “CRIO 0062” — clean ASCII whose “0062” is the tail of the radio’s own ID — with a CRC-16/GSM that matches the on-air bytes (0x6A96). The earlier worry that the one #376 sample was “underdetermined” was a framing artifact: the cipher length must be taken from the self-delimiting CRC boundary (stripping the trailing FACCH pad), after which it decodes cleanly.

The decode is gated behind the CipherVerified constant, now true, standing on a committed regression fixture (the held-out oracle vectors). While true, a clean-ASCII decode is reported reliable and the name surfaces; a garbled decode is still flagged unreliable. See the clean-room provenance record for the method and reproducible validation.

Licensing

A working implementation also exists in SDRTrunk (GPLv3); GopherTrunk is Apache-2.0, so that source and table were not ported. The cipher was instead recovered by clean-room reverse engineering: an existing decoder was driven as an opaque black-box oracle — its public decode accessor only, with no source, table, or algorithm read or copied — and the cipher’s structure and 256-byte table were reconstructed as functional facts from the observed input/output behaviour, then re-implemented independently in Go. The GPL-linked measurement instruments were quarantined and never committed. The full provenance — the exact boundary of what was and was not read, and the reproducible validation — is documented in the clean-room provenance record.

Sources

  1. Project 25 — Wikipedia, on the P25 standard. The cipher is a proprietary Motorola extension with no vendor specification; it was recovered by clean-room reverse engineering and verified against real over-the-air data per GopherTrunk issue #773. 

See also