Rompere il codice rotto


12

Ho progettato un semplice generatore casuale che cicla due numeri in modo caotico usando un metodo di moltiplicazione e modulo. Funziona benissimo per questo.

Se dovessi usarlo come generatore di cifratura sarebbe comunque vulnerabile a un noto attacco in chiaro, dato che un aggressore può decodificare il seme da una serie di numeri casuali in modo computazionalmente efficiente.

Per provare la crittografia non trovata, trova una coppia legale di valori seed che generano 7 zeri di fila nell'intervallo [0; 255], usando il minor consumo di energia, tempo di CPU ecc.

Ecco il generatore casuale scritto in JavaScript:

function seed(state1,state2){
    //Constants
    var mod1=4294967087
    var mul1=65539
    var mod2=4294965887
    var mul2=65537
    function random(limit){
        //Cycle each state variable 1 step
        state1=(state1*mul1)%mod1
        state2=(state2*mul2)%mod2
        //Return a random variable
        return (state1+state2)%limit
    }
    //Return the random function
    return random
}

//Initiate the random generator using 2 integer values,
//they must be in the ranges [1;4294967086] and [1;4294965886]
random=seed(31337,42)
//Write 7 random values in the range [0;255] to screen
for(a=0;a<7;a++){
    document.write(random(256)+"<br>")
}

Ho creato uno strumento per testare le coppie di numeri candidati, può essere trovato qui .

Per i prossimi 3 giorni, nessun spoiler consentito , una risposta deve contenere solo un set di numeri e, naturalmente, dovrebbe essere un set diverso da quelli pubblicati dai solutori precedenti. Successivamente sei incoraggiato a pubblicare codice e spiegare il tuo approccio.

Modifica, la quarantena è terminata: le
risposte devono contenere sia un insieme univoco di numeri sia una spiegazione e un codice per documentare il metodo di risoluzione.

La soluzione più elegante vince.

Per la cronaca:
scrivere un programma in grado di trovare rapidamente una soluzione è elegante.
Realizzare un programma che utilizza le funzionalità di una GPU in modo efficiente per farlo ancora più velocemente è elegante.
Fare il lavoro su un pezzo di "museo" è elegante.
Trovare un metodo di soluzione che può essere facilmente utilizzabile usando solo carta e penna è molto elegante.
Spiegare la tua soluzione in modo istruttivo e facilmente comprensibile è elegante.

L'uso di computer multipli o molto costosi non è elegante.


Sei sicuro che esista una risposta per questo?
Wile E. Coyote,

Sì, ce ne sono ~ 256. E sono anche sicuro che è possibile trovare una risposta in pochi minuti, dato un PC moderno e la giusta programmazione.
aaaaaaaaaaaa,

Mi chiedo solo, perché le GPU sono eleganti ma non più CPU?
JB

Perché sono più difficili da programmare in modo efficiente rispetto alle CPU. Devi assicurarti di utilizzare effettivamente la GPU, nessun punto per avere la maggior parte degli shader inattivi perché qualche altro sottosistema sta colmando il collo di bottiglia. E ovviamente devi ancora implementare un algoritmo efficiente per ottenere punti importanti.
aaaaaaaaaaaa

Se invio il mio codice di lavoro, è quasi come se avessi inviato un grande gruppo di coppie di semi. La partita è già finita?
JB

Risposte:


6

C ++, 44014022/164607120

È in C ++, utilizza 1 GB di memoria e ha impiegato circa 45 secondi per trovare questa prima coppia. Aggiornerò il tempo una volta trovati tutti.

Codice sotto. Prima trova tutte le coppie che generano 4 zeri, quindi riduce quelle con una semplice prova (vedi il checkmetodo). Trova coppie che generano 4 zeri generando due array di grandi dimensioni, uno che contiene i primi 4 byte di ordine basso del generatore state1 e il secondo che contiene il negativo dei primi 4 byte di ordine basso del generatore state2. Questi array vengono quindi ordinati e ricercati per una corrispondenza, che corrisponde al generatore generale che genera 4 zeri per iniziare.

Gli array sono troppo grandi per essere archiviati in memoria, quindi fa il lavoro in batch dimensionati per adattarsi alla memoria.

Sembra che la corsa completa durerà ~ 12 ore.

Modifica : Migliorato il codice in modo da richiedere solo ~ 1 ora per ottenere tutti i semi possibili. Ora genera le tabelle in 256 file diversi, uno per ogni primo byte di output. Possiamo quindi elaborare ogni file in modo indipendente, quindi non è necessario rigenerare i dati.

Modifica : si scopre che è possibile generare i 256 sottotitoli singolarmente anziché tutti contemporaneamente, quindi non è necessario alcun disco. Tempo di esecuzione fino a ~ 15 minuti con 256 MB.

#include <stdio.h>
#include <stdint.h>
#include <algorithm>
using namespace std;

#define M1 65539
#define N1 4294967087
#define M2 65537
#define N2 4294965887
#define MATCHES 7

// M^-1 mod N                                                                                                                                                        
#define M1_INV 3027952124
#define M2_INV 1949206866

int npairs = 0;

void check(uint32_t seed1, uint32_t seed2) {
  uint32_t s1 = seed1;
  uint32_t s2 = seed2;
  for (int i = 0; i < MATCHES; i++) {
    s1 = (uint64_t)s1 * M1 % N1;
    s2 = (uint64_t)s2 * M2 % N2;
    if (((s1 + s2) & 0xff) != 0) return;
  }
  printf("%d %u %u\n", npairs++, seed1, seed2);
}

struct Record {
  uint32_t signature; // 2nd through 5th generated bytes                                                                                                             
  uint32_t seed;      // seed that generated them                                                                                                                    
};
// for sorting Records                                                                                                                                               
bool operator<(const Record &a, const Record &b) {
  return a.signature < b.signature;
}

int main(int argc, char *argv[]) {
  Record *table1 = (Record*)malloc((N1/256+1)*sizeof(*table1));
  Record *table2 = (Record*)malloc((N2/256+1)*sizeof(*table2));

  for (int i = 0; i < 256; i++) {  // iterate over first byte                                                                                                        
    printf("first byte %x\n", i);

    // generate signatures (bytes 2 through 5) for all states of generator 1                                                                                         
    // that generate i as the first byte.                                                                                                                            
    Record *r = table1;
    for (uint64_t k = i; k < N1; k += 256) {
      uint32_t sig = 0;
      uint32_t v = k;
      for (int j = 0; j < 4; j++) {
        v = (uint64_t)v * M1 % N1;
        sig = (sig << 8) + (v & 0xff);
      }
      r->signature = sig;
      r->seed = k;
      r++;
    }
    Record *endtable1 = r;

    // generate signatures (bytes 2 through 5) for all states of generator 2                                                                                         
    // that generate -i as the first byte.                                                                                                                           
    r = table2;
    for (uint64_t k = (-i & 0xff); k < N2; k += 256) {
      uint32_t sig = 0;
      uint32_t v = k;
      for (int j = 0; j < 4; j++) {
        v = (uint64_t)v * M2 % N2;
        sig = (sig << 8) + (-v & 0xff);
      }
      r->signature = sig;
      r->seed = k;
      r++;
    }
    Record *endtable2 = r;

    sort(table1, endtable1);
    sort(table2, endtable2);

    // iterate through looking for matches                                                                                                                           
    const Record *p1 = table1;
    const Record *p2 = table2;
    while (p1 < endtable1  && p2 < endtable2) {
      if (p1->signature < p2->signature) p1++;
      else if (p1->signature > p2->signature) p2++;
      else {
        check((uint64_t)p1->seed * M1_INV % N1, (uint64_t)p2->seed * M2_INV % N2);
        // NOTE: may skip some potential matches, if p1->signature==(p1+1)->signature or p2->signature==(p2+1)->signature                                            
        p1++;
      }
    }
  }
}

Non pensavo che un disco rigido fosse abbastanza veloce da essere efficiente per l'attività. Interessante.
aaaaaaaaaaaa
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.