Field Guide · algorithm

Also known as: NXDN scrambler, NXDN encryption, NXDN privacy

The NXDN scrambler is the optional privacy mode NXDN radios expose — the setting sometimes labelled “encryption” — that obscures the voice or data field by XORing a pseudo-random keystream over it.1 The keystream comes from a 15-bit linear-feedback shift register (LFSR) seeded by a 15-bit scrambling key in the range 0 to 32767. It is not cryptography in any meaningful sense: the key space is only 2¹⁵ = 32768 values, small enough to try exhaustively in a fraction of a second, so the “privacy” it offers is defeated by brute force alone.2

Status — synthetic model, not hardware-confirmed. GopherTrunk’s tap polynomial and seed mapping are an internally-consistent working model — a maximal-length 15-bit Fibonacci LFSR, x¹⁵ + x¹⁴ + 1 — chosen so the scrambler is its own inverse and emits a balanced m-sequence of period 32767. They have not been confirmed bit-exact against a known-key capture. The spec-level facts the tooling relies on are the XOR-symmetric (self-inverse) structure and the 15-bit key space; the exact feedback is provisional and should be adjusted when a known-key capture is available.

15-bit shift register bit14 bit0 feedback keystream bit = MSB data bit scrambled bit
The scrambler advances a 15-stage LFSR — the top two stages exclusive-ored back into the input — and emits its most significant stage as the keystream bit, which is XORed onto each data bit. Because XOR is symmetric, the identical operation both scrambles and descrambles.

How the keystream is generated

Each step the register outputs its most significant stage, then shifts left and inserts the XOR of the top two stages as the new least significant bit. Seeding the register with the key and running it over the information field produces the additive keystream:

func (s *Scrambler) Next() byte {
    out := byte((s.state >> 14) & 1)
    fb := ((s.state >> 14) ^ (s.state >> 13)) & 1
    s.state = ((s.state << 1) | fb) & ScramblerKeyMax // mask to 15 bits
    return out
}

Key 0 seeds an all-zero register, which produces an all-zero keystream — that is the “clear” (unscrambled) case, so key 0 means no privacy at all. Every other key produces a distinct maximal-length sequence. Because scrambling is a bare XOR, Descramble is literally the same routine as Scramble; the receive side reuses it with the same key.

Why it does not provide security

Two facts collapse the scrambler’s protection. First, the key is only 15 bits, so there are just 32768 possible keys — an attacker tries them all. Second, NXDN frames carry CRCs over their decoded contents, which hand the attacker a free correctness oracle: descramble a captured field under a candidate key, check whether the frame’s CRC now validates, and the one key in 32768 that produces consistently valid CRCs is the answer. GopherTrunk’s offline cryptolab nxdn tool does exactly this — it brute-forces the full key space using this LFSR primitive plus the frame CRCs as the oracle. An additive stream obscured by a 15-bit register is a scrambler in the historical sense, not an encryption scheme; it stops a casual listener with a stock radio, nothing more.

Relevance to SDR

internal/radio/nxdn/scramble.go implements the Scrambler (a 15-bit LFSR state), the Scramble / Descramble XOR helpers, and the ScramblerKeyMax bound the brute-forcer sweeps. The design deliberately separates the spec-level facts it is confident in — XOR symmetry and the 2¹⁵ key space, on which the brute-force loop and CRC oracles depend — from the provisional feedback polynomial, which is a plausible working model pending a bit-exact capture. That separation is what lets the tooling be useful today (the key-recovery approach is correct regardless of the exact taps) while being honest that the keystream itself is not yet verified against real hardware.

Sources

  1. Scrambler — Wikipedia, on additive/multiplicative scramblers and their distinction from encryption. 

  2. Linear-feedback shift register — Wikipedia, on the LFSR construction and maximal-length sequences the keystream is built from. 

See also