Prova di hash delle password SHA-3

2

Sono in una situazione in cui ho bisogno di indurire un hash della password, ma non mi è permesso di aggiungere alcuna dipendenza extra, quindi sono praticamente obbligato a fare l'unica cosa a cui tutti sembrano sconsigliare: distribuire la mia implementazione.

La famiglia SHA, essendo considerata hash veloce, non sembra adatta per l'hashing delle password. E c'è anche il rischio di perdita di entropia da hashing hash ripetuti.

L'intento di "hashing lento" sembra aumentare il tempo di CPU e i requisiti di memoria, quindi ho concepito la seguente strategia.

  • la password viene cancellata tramite SHA-3 512
  • il valore hash viene inviato a un PRNG mt19937 attraverso una sequenza di seed
  • viene generata una sequenza casuale lunga per il rehashed
  • recita lo stesso per n numero di volte
  • il valore di hash finale è derivato dall'annullare tutte le sequenze al loro interno

Sembra che introduca una quantità aggiuntiva di lavoro e di memoria accettabile.

Quindi la mia domanda è se questa sia una strategia ragionevole e, in tal caso, quali lunghezze della sequenza e profondità di ricorsione dovrebbero essere sufficienti.

Aggiornamento:

Ho finito per espandere un po 'l'implementazione, incorporando anche un algoritmo di hashing a "random" per ogni fase del processo, 12 in totale: SHA2, RealSHA3 e Keccak con dimensione digest di 224, 256, 384 e 512, utilizzando un doppio bloccato come selettore.

Tutto sommato, rispetto a un singolo hash SHA3 semplice, questa implementazione è circa 100k volte più lenta, e comporta un costo aggiuntivo di ~ 20 MB RAM su una ricorsione piuttosto profonda e arbitrariamente ramificata per produrre l'hash finale, che è ciò che io può permettersi pur restando entro limiti ragionevoli considerando le specifiche minime della piattaforma di destinazione. Ma forse altrettanto importante, questo comporta un'ulteriore diversità computazionale rispetto all'ingenuo approccio "rehash n times", utilizzando più algoritmi di hash, generazione PRN con uno stato interno abbastanza grande, std::seed_seq è sempre alimentato dall'output completo dell'hash e fa un ulteriore "condizionamento" "dei valori e, ultimo ma non meno importante, aggiungendo alcune operazioni in virgola mobile a doppia precisione per incollare tutto insieme, sperando di rendere scortese l'intera procedura GPU / ASIC.

L'hashing ora richiede circa 500 msec, da 5000 nsec, su una CPU i7 Ghz. Dato il totale di 95 simboli consentiti in una password e assumendo un perfetto ridimensionamento fino a 8 thread, ci vorrebbero circa 1450 anni per provare tutte le possibili combinazioni per una password di 6 caratteri o ~ 130 anni per una password di 8 caratteri usando 100k tali CPU, che sembra abbastanza impegnativo.

Questo è considerato abbastanza "hardened", e se no, come posso migliorarlo?

    
posta dtech 23.11.2018 - 23:19
fonte

3 risposte

3

C'è una soluzione a questo che non richiede di includere altre dipendenze come Argon2. È possibile implementare l'algoritmo utilizzato da OpenPGP, denominato Da stringa a chiave o S2K. Questo algoritmo funziona alimentando la password ripetuta e sale ad una funzione hash. La password e il sale sono stati ripetuti abbastanza da richiedere alla funzione hash una quantità trascurabile di tempo per elaborarlo. Questo è un KDF lento così semplice che può anche essere fatto usando le utility Linux standard (in questo esempio con SHA-256):

yes "$pass$salt" | head -c 50M | sha256sum

Se sei sempre in grado di portare le tue dipendenze, consiglio vivamente di ascoltare Future Security la cui risposta eccellente elenca alternative superiori sia a S2K sia al tuo schema personalizzato.

    
risposta data 24.11.2018 - 02:48
fonte
2

Vale la pena aggiungere Argon2 come dipendenza. Oppure bcrypt se non puoi usare Argon2 ottimizzato e riesci a evitare tutti i modi in cui bcrypt ti permette di spararti ai piedi. Se rimani senza una buona opzione, dedica maggiore attenzione a non consentire agli esseri umani di scegliere la propria password.

Perdere entropia attraverso l'hashing ripetuto non è qualcosa a cui dovresti preoccuparti a meno che non tronchi l'output hash tra le iterazioni o se usi una funzione hash veramente brutta. Non è un problema per una funzione sicura con output di grandi dimensioni. Solo quando due diverse password portano a catene di hashing si perde qualcosa. In altre parole, solo quando si verifica una collisione accidentale.

L'utilizzo di un RNG per generare nuovi input non è necessario. L'output degli hash crittografici è altrettanto casuale se si utilizza un input dall'aspetto casuale o non casuale. Potresti peggiorare le cose se qualcuno potesse utilizzare un'implementazione hardware ottimizzata mentre dovevi usare un'implementazione software più lenta. Sei decisamente che peggiora le cose se c'è qualche difetto nell'implementazione o se il seme è ridotto a dire, un numero a 64 bit.

Il tuo metodo specifico può consentire un compromesso tra memoria di tempo. Alcuni candidati alla competizione per l'hashing delle password , incluso Argon2, sono stati progettati in modo da non poter dimezzare i requisiti di memoria se si è solo disposto a dimezzare la velocità. (Scrypt ti permette di fare questi compromessi e quel problema era una delle motivazioni per cercare un algoritmo migliore.) Potresti pensare che raddoppiare il tempo di calcolo non valga la pena di risparmiare memoria, ma alla fine potresti finire con un throughput più elevato, meno potenza consumata o meno costi se puoi acquistare hardware meno costoso o più efficiente con memoria insufficiente e fare più operazioni in parallelo.

Qualunque algoritmo che potresti inventare probabilmente sarebbe, nella migliore delle ipotesi, competitivo con PBKDF2. (PBKDF2 non è eccezionale, ma è abbastanza semplice da implementare se hai già una funzione di hash.)

Se usi PBKDF2 o qualcosa del genere probabilmente non dovresti usare SHA-3. Gli hash SHA-3 possono essere calcolati in modo abbastanza efficiente ma è relativamente lento sulle CPU. Ciò potrebbe avvantaggiare i password cracker se potessero usare implementazioni più veloci ed efficienti. Faresti meglio a usare SHA-2-512, in realtà. O Blake2.

    
risposta data 24.11.2018 - 01:42
fonte
0

Se davvero non puoi includere nuove dipendenze o codice di terze parti (e in realtà, il tuo primo passo dovrebbe essere quello di respingere questa limitazione), allora è molto meglio implementare un algoritmo standard controllato che creare il tuo algoritmo . Quindi non solo trarrai beneficio dall'utilizzo di un algoritmo con sicurezza nota, riconosciuto da esperti di sicurezza e dimostrato da anni di utilizzo nel campo, ma hai anche l'opportunità di testare contro implementazioni standard o vettori di test per essere sicuro di non averlo fatto eventuali errori nell'implementazione che rovinano la sicurezza, come accidentalmente solo l'hashing fino al primo byte zero o qualcosa del genere. L'algoritmo più semplice e meno soggetto a errori da implementare è probabilmente PBKDF2, poiché per la maggior parte ha bisogno solo di un primitivo hash crittografico e di una fonte di casualità, che sembra che tu abbia già.

    
risposta data 24.11.2018 - 15:23
fonte

Leggi altre domande sui tag