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?