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 inferred from public protocol notes: a length-seeded 16-bit 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 is reverse-engineered and unverified. GopherTrunk does not decode it by default and never presents its output as a confirmed name — the caveats below are not incidental, they are the whole point of the page.

accum × 293 + 0x72E9 LUT[byte+128] × odd-multiplier m2 decoded byte accum advances by (byte + 1); repeat per byte — UNVERIFIED table & constants
The transform chains a stepped accumulator, a table lookup, and an odd-multiplier per byte; both the 256-byte table and the constants are unconfirmed placeholders, so the whole path is gated off.

The per-byte transform

The decode-side transform lives in internal/radio/p25/motorola/alias.go. It exists only in reverse-engineering notes — there is no spec table to point at — so the exact algorithm is worth showing:

// accum seeded with the encoded length; LUT is a 256-byte signed table.
accum = uint16(len(encoded))
for i, raw := range encoded {
    accum = uint16(uint32(accum)*293 + 0x72E9)      // stepped accumulator
    lut := motorolaAliasLUT[int(int8(raw))+128]     // signed table lookup
    m1 := int8(int(lut) - int(int8(accum>>8)))

    var m2 int8 = 1                                 // odd-multiplier search
    stop := int8(accum | 1)
    increment := stop << 1
    for stop != 1 && m2 != -1 {
        stop += increment
        m2 += 2
    }
    decoded[i] = byte(int8(int(m1) * int(m2)))
    accum = uint16(uint32(accum) + uint32(raw) + 1) // advance
}

The m2 loop finds the multiplicative inverse of accum | 1 modulo 256 (odd values are units mod 256), and m1 mixes the table output with the high byte of the accumulator. The decoded byte stream is then read as UTF-16 BE and rendered to printable ASCII.

Verification status — read this

The SUID framing around the cipher (the WACN / System / Radio ID prefix) is verified against real traffic. The cipher itself is not. Per GopherTrunk issue #773: the 256-byte table (motorolaAliasLUT) is a placeholder permutation of unconfirmed provenance, the accumulator constants (293, 0x72E9) are inferred, and the routine decodes nothing on live traffic. The one partial capture available (RID 200062) is mathematically underdetermined — dozens of distinct constant-sets reproduce its known bytes while disagreeing on the unknown ones, and one alias character is unrecoverable from that sample — so it cannot pin the table. This mirrors GopherTrunk’s own house rule of labelling reverse-engineered work as best-effort and not hardware-confirmed.

Because a wrong table could fabricate a plausible name, the decode is gated behind the CipherVerified constant, which is false. While it is false, DecodeMessage never reports an alias as reliable, and callers must not surface the output as a confirmed name. The constant is to be flipped to true only together with a committed regression fixture mapping real encoded bytes to the correct plaintext for the same RID — never on inference alone.

Licensing

A working implementation exists in SDRTrunk, but SDRTrunk is GPLv3 and GopherTrunk is Apache-2.0, so that table and decode must not be ported into GopherTrunk. A verified table therefore requires either ground-truth captures GopherTrunk can solve independently or a separate licensing path. Until then the cipher stays off, and GopherTrunk instead logs the raw encoded region so the cryptanalysis can be finished from committed data.

Sources

  1. Project 25 — Wikipedia, on the P25 standard. The cipher is a proprietary Motorola extension, reverse-engineered and unverified per GopherTrunk issue #773; no vendor specification exists. 

See also