Introducción

NXDN (Next Generation Digital Narrowband) es un estándar de radio digital desarrollado conjuntamente por Icom y Kenwood en Japón. Fue diseñado para comunicaciones de voz y datos en entornos de radio bidireccionales, como sistemas de comunicaciones de radio móviles y portátiles utilizados por organizaciones como servicios de emergencia, empresas, y agencias gubernamentales.

Estas comunicaciones pueden enviarse sin cifrar o usando un cifrado Scramble, DES, o AES, según consta en la especificación NXDN TS 1-D Version 1.3. En este caso nos vamos a centrar en el cifrado Scramble, que se define así.

  • El cifrado Scramble es un algoritmo de cifrado que consiste en una inversión de bits aleatoria mediante una operación XOR a nivel de bits entre una secuencia de bits de voz u otros datos y una secuencia de bits PN. La secuencia PN utiliza el polinomio P(x) = X^15 + X + 1, que tiene un período de repetición de 32,767 bits, y se utiliza una clave de cifrado como predeterminada para la secuencia PN. Dado que la secuencia PN es generada por un registro de desplazamiento de 15 etapas, la clave de cifrado se puede seleccionar entre 32,767 claves distintas, excepto todas las claves en cero. En resumen, estos son los pasos del cifrado, el cual se trata de un registro de desplazamiento con retroalimentación lineal (LFSR).
    1. Se cargan los bits de la clave de cifrado de 15 bits en los registros S14...S0
    2. Se realiza una operación XOR con el registro S0 y un bit del texto en claro generando un bit cifrado
    3. Se realiza una operación XOR con el registro S0 y el registro S1, llamada X
    4. Se desplazan los registros hacia la derecha, pasando a ser S14 igual a X, S13 igual a S14, y así sucesivamente
    5. Se repiten los pasos 2-4 hasta que ya no existan más bits del texto en claro a cifrar

Se ha implementado este algoritmo usando el lenguaje de programación C, siendo configurables en la función main(), el array key_15_bit con la clave de cifrado, el array list_bits con el texto en claro (o cifrado, si queremos descifrarlo), y el array bits_to_xor con los índices de los bits a realizar la operación XOR (en este caso 13 y 14 siendo S1 y S0). Este es un ejemplo de ejecución para el texto en claro “ABCDEFGHIJKLMNOPQRST” y la clave de cifrado “1”, devolviéndose el texto cifrado en binario.

$ ./lfsr
NXDN-LFSR
1100000101000011010000110100001001000101010100100100011100110000010010000101101001001101001011000101100000001110001100001101000101010000010101000101010101000000

Código fuente

#include <stdio.h>

int lfsr(int *register_lfsr, int *bits_to_xor, int *result, int size_register, int iterations)
{
    int i = 0;

    while (i < iterations)
    {
        // Save last bit register and XOR operation
        result[i] = result[i] ^ register_lfsr[size_register - 1];

        // Xor bits
        int xored = register_lfsr[bits_to_xor[0]] ^ register_lfsr[bits_to_xor[1]];

        // Shift bits [1,0]-->[0,1]
        int prev = 0;
        for (int j = 0; j < size_register; j++)
        {
            if (j == 0)
            {
                prev = register_lfsr[j];
                // Save XORed bit
                register_lfsr[0] = xored;
            }
            else if (j > 0)
            {
                int tmp = prev;
                prev = register_lfsr[j];
                register_lfsr[j] = tmp;
            }
        }

        // Up
        i++;
    }

    return 0;
}

int main(int argc, char const *argv[])
{
    // Key 1 0x1
    int key_15_bit[15] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,1};
    // Text ABCDEFGHIJKLMNOPQRST
    int list_bits[160] = {0,1,0,0,0,0,0,1,0,1,0,0,0,0,1,0,0,1,0,0,0,0,1,1,0,1,0,0,0,1,0,0,
                          0,1,0,0,0,1,0,1,0,1,0,0,0,1,1,0,0,1,0,0,0,1,1,1,0,1,0,0,1,0,0,0,
                          0,1,0,0,1,0,0,1,0,1,0,0,1,0,1,0,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,
                          0,1,0,0,1,1,0,1,0,1,0,0,1,1,1,0,0,1,0,0,1,1,1,1,0,1,0,1,0,0,0,0,
                          0,1,0,1,0,0,0,1,0,1,0,1,0,0,1,0,0,1,0,1,0,0,1,1,0,1,0,1,0,1,0,0};
    // Bits to XOR S1(13)^S0(14)
    int bits_to_xor[2] = {13, 14};
    int size_array = sizeof(list_bits)/sizeof(list_bits[0]);

    lfsr(key_15_bit, bits_to_xor, list_bits, 15, size_array);

    printf("NXDN-LFSR\n");

    for (int i = 0; i < size_array; i++)
    {
        printf("%d", list_bits[i]);
    }

    return 0;
}

Conclusión

Como se ha observado en el artículo el cifrado utilizado en NXDN (Scramble) es débil ya que únicamente puede utilizar 32767 claves lo que lo hace un objetivo de ataques ya que en menos de un segundo de un procesador moderno se puede generar el descifrado de un texto cifrado para todas las claves.