No.
Scriviamolo:
-
C è il messaggio di sfida
-
R è il messaggio di risposta
-
K è la chiave segreta
-
C = q ⊕ K dove q è un numero casuale.
-
R = ((C ⊕ K) 2 - 5) ⊕ K , oppure saltando il passo di decrittazione: R = (q 2 - 5) ⊕ K .
L'attaccante può vedere sia C sia R . Se l'utente malintenzionato esegue quindi il xors dei due valori:
C ⊕ R = (q 2 - 5) ⊕ K ⊕ q ⊕ K
Poiché eseguiamo il xoring con K due volte, possiamo semplicemente eliminare queste operazioni. Ciò fornisce all'attaccante quanto segue:
C ⊕ R = (q 2 - 5) ⊕ q
Ci sono solo alcuni valori per q per ogni dato valore di C ⊕ R nello spazio a 32 bit. In effetti, è un'operazione abbastanza semplice da fare solo forza bruta: prova tutti i valori q finché non trovi tutti i valori corrispondenti.
Questo ti dà qualche possibile valore di q , il numero casuale. Poiché C = q ⊕ K , basta calcolare C ⊕ q per ciascun candidato q per ottenere un piccolo numero di K valori. Ripeti questo processo per la seconda volta e vedi quale valore K candidato è apparso in entrambe le esecuzioni: questo ti dà K .
Ho persino scritto 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);
}
}
}
Output di esempio:
Key is: 0x0BAD1DEA
I picked q=2847555
Cracking the challenge and response...
q candidate: 2847555
k candidate: 0x0BAD1DEA
Il processo di "cracking" ha richiesto meno di un secondo sul mio sistema.
EDIT: Dal momento che questo non è stato chiaro: non sei brutoforcing di nulla contro il sistema reale. Questo approccio non implica affatto l'invio di dati al destinatario. Ti siedi semplicemente con una radio definita dal software (SDR) e acquisisci i segnali prodotti quando il proprietario apre la porta del garage. Quindi estrai i valori di sfida e risposta da quei segnali: questi sono C e R . Dato C e R puoi usare il processo sopra riportato per calcolare alcuni possibili valori q per quella particolare coppia sfida / risposta. In alcuni casi ne riceverai solo uno, in alcuni casi potresti ottenere 2 o 3. Calcolare q ⊕ C per ciascun candidato q per ottenere una lista di candidati K valori. Se ne ottieni più di uno, attendi che aprano nuovamente il loro garage e catturi un'altra coppia C e R , riesegui il processo e vedi quale candidato I valori di K corrispondono per la prima volta - questo ti darà il vero valore K . Una volta ottenuto ciò, hai tutto ciò che ti serve per impersonare il vero dispositivo di apertura. Puoi rispondere correttamente ogni volta poiché conosci il valore di K .