Autenticación de desafío / respuesta para abridor de puerta de garaje

20

Estoy construyendo un abridor de puerta de garaje de radio basado en Arduino, y para protegerlo de los ataques de repetición, he creado este algoritmo:

  1. el remitente inicia la comunicación
  2. el receptor envía un número aleatorio de 32 bits XORed con una clave secreta
  3. el remitente invierte la operación XOR usando la misma clave secreta, resuelve el desafío (por ejemplo: desafío ^ 2 - 5), XORes y envía el resultado al receptor junto con el comando (puerta arriba o puerta abajo)
  4. el receptor compara la respuesta con su propio cálculo del desafío y ejecuta el comando si coinciden.

Tengo curiosidad, ¿los pasos descritos anteriormente aseguran que la respuesta sea imposible de adivinar para un atacante?

    
pregunta Denis Denisovici 21.04.2016 - 13:06
fuente

2 respuestas

58

No.

Vamos a escribirlo:

  • C es el mensaje de desafío
  • R es el mensaje de respuesta
  • K es la clave secreta
  • C = q ⊕ K donde q es un número aleatorio.
  • R = ((C ⊕ K) 2 - 5) ⊕ K , o si saltamos el paso de descifrado: R = (q 2 - 5) ⊕ K .

El atacante puede ver tanto C como R . Si el atacante entonces xors los dos valores:

C ⊕ R = (q 2 - 5) ⊕ K ⊕ q ⊕ K

Ya que estamos xoring con K dos veces, solo podemos abandonar esas operaciones. Esto le da al atacante lo siguiente:

C ⊕ R = (q 2 - 5) ⊕ q

Solo hay unos pocos valores para q para cualquier valor dado de C ⊕ R en el espacio de 32 bits. De hecho, es una operación lo suficientemente fácil como para simplemente forzar la fuerza bruta: solo intente todos los valores de q hasta que encuentre todos los valores que coincidan.

Esto le da un valor posible de q , el número aleatorio. Dado que C = q ⊕ K , simplemente calcule C ⊕ q para cada candidato q para obtener un pequeño número de K valores. Repita este proceso por segunda vez y vea qué valor candidato de K apareció en ambas ejecuciones: esto le da a usted K .

Incluso escribí un PoC!

// our key!
int k = 0xBAD1DEA;

void Main()
{
    // output the key just so we can see it in the output
    Console.WriteLine("Key is: 0x{0:X8}", k);

    int challenge = GenerateChallenge();
    int response = GenerateResponse(challenge);

    Console.WriteLine();
    Console.WriteLine("Cracking the challenge and response...");

    // this is the attacker: they know only the challenge and respoonse!
    Crack(challenge, response);
}

int GenerateChallenge()
{
    Random rng = new Random();

    // I'm keeping the random number small-ish to avoid the c^2 operation from overflowing
    // this is just a limitation of the fact that .NET has sane integer types that don't wrap on multiplication overflows
    int q = rng.Next(0, 10000000);
    Console.WriteLine("I picked q={0}", q);

    int challenge = k ^ q;
    return challenge;
}

int GenerateResponse(int c)
{
    c ^= k;
    return ((c * c) - 5) ^ k;
}

void Crack(int c, int r)
{
    int c_r = c ^ r;

    // try all possible 'q' values.
    for (int q = 1; q < int.MaxValue; q++)
    {
        if ((((q * q) - 5) ^ q) == c_r)
        {
            // got a match, output it
            Console.WriteLine("q candidate: {0}", q);
            Console.WriteLine("k candidate: 0x{0:X8}", q ^ c);
        }
    }
}

Salida de muestra:

Key is: 0x0BAD1DEA
I picked q=2847555

Cracking the challenge and response...
q candidate: 2847555
k candidate: 0x0BAD1DEA

El proceso de "cracking" tomó menos de un segundo en mi sistema.

EDITAR: ya que aparentemente esto no estaba claro: no estás forzando nada contra el sistema real. Este enfoque no implica que envíes ningún dato al receptor en absoluto. Simplemente, siéntese allí con una radio definida por software (SDR) y capture las señales producidas cuando el propietario abre la puerta de su garaje. Luego extrae los valores de desafío y respuesta de esas señales, que son C y R . Dados C y R puede usar el proceso anterior para calcular algunos valores posibles de q para ese par de desafío / respuesta en particular. En algunos casos solo obtendrá uno, en otros casos puede obtener 2 o 3. Compute q ⊕ C para cada candidato q para obtener una lista de candidatos K valores. Si obtiene más de uno, espere a que vuelvan a abrir su garaje y capture otro par C y R , vuelva a ejecutar el proceso y vea qué candidato Los valores de K coinciden con la primera vez; esto le dará el valor real de K . Una vez que tienes eso, tienes todo lo que necesitas para personificar el dispositivo de apertura real. Puede responder correctamente cada vez que conozca el valor de K .

    
respondido por el Polynomial 21.04.2016 - 13:44
fuente
1

En primer lugar, necesita una función criptográfica cuya relación entre entrada y salida no sea fácilmente perceptible. XOR simplemente no lo cortará. Una forma lenta pero efectiva de cifrar los datos sería dividir el valor de 32 bits en dos mitades, H y L, y repetir el siguiente varias veces:

  • Rellene H con suficientes bytes para hacer un bloque criptográfico para DES, AES o algún otro esquema decente (y una clave secreta) y cifrelo.
  • Establezca H en el valor antiguo de L, y establezca L en el valor anterior de H xor'ed con 16 bits del bloque cifrado.

Si uno utiliza el enfoque anterior con un método de cifrado decente en su núcleo, el resultado será una asignación segura de 32 bits a 32 bits. El mapeo inverso se puede obtener intercambiando H y L, realizando el procedimiento anterior y volviéndolos a intercambiar.

Una carga útil de 32 bits es algo pequeña, pero podría proporcionar un nivel razonable de seguridad si los desafíos nunca se repiten. Esto podría lograrse haciendo que el remitente lleve un recuento de por vida de cuántos desafíos se han enviado, y usándolo como datos de desafío para cifrar y validar.

    
respondido por el supercat 21.04.2016 - 18:29
fuente

Lea otras preguntas en las etiquetas