Quando BCrypt fu pubblicato per la prima volta, nel 1999, elencarono i fattori di costo di default della loro implementazione:
- utente normale: 6
- super utente: 8
Notano anche:
Of course, whatever cost people choose should be reevaluated
from time to time
Un costo in bcrypt di 6 significa 64 round (2 6 = 64).
Se usiamo quel valore iniziale "utente normale" , vogliamo provare ad aggiustare il calcolo dell'inflazione della potenza ( supponendo che in media raddoppi ogni 18 mesi ).
R = R0 × 2(months/18)
R = 64 × 2(months/18)
Oggi (9 marzo 2015) è di 171 mesi dal 31/12/1999 (o usa 1/1/2000 per semplicità), il numero di giri dovrebbe essere raddoppiato un po 'più di 9 volte:
R = 64 × 2(171/18)
R = 64 × 29.5
R = 64 × 724.1
R = 46,341.0
Alla fine, vogliamo riconvertirlo in un fattore di costo
cost = ln(R) / ln(2)
cost = ln(46,341.0) / ln(2)
cost = 15.5
La praticità di un fattore di costo o 15 dipende dalla potenza di calcolo del tuo server. Ad esempio, il mio PC desktop è una CPU Intel Core i7-2700K a 3.50 GHz. Inizialmente ho eseguito il benchmarking di un'implementazione di BCrypt il 1/23/2014:
1/23/2014 Intel Core i7-2700K CPU @ 3.50 GHz
| Cost | Iterations | Duration |
|------|-------------------|-------------|
| 8 | 256 iterations | 38.2 ms | <-- minimum allowed by BCrypt
| 9 | 512 iterations | 74.8 ms |
| 10 | 1,024 iterations | 152.4 ms | <-- current default (BCRYPT_COST=10)
| 11 | 2,048 iterations | 296.6 ms |
| 12 | 4,096 iterations | 594.3 ms |
| 13 | 8,192 iterations | 1,169.5 ms |
| 14 | 16,384 iterations | 2,338.8 ms |
| 15 | 32,768 iterations | 4,656.0 ms |
| 16 | 65,536 iterations | 9,302.2 ms |
Ma era il 2014
Questi tempi sono stati originariamente calcolati all'inizio del 2014. Nei miei calcoli avrebbero dovuto essere utilizzati solo 156 mesi (anziché 171):
R = 64 × 2(156/18)
R = 64 × 28.66
R = 64 × 406.8
R = 26,035.2
cost = ln(R) / ln(2)
cost = ln(26,035.2) / ln(2)
cost = 14.7
Ma l'i7-2700K era già stato interrotto
L'i7-2700K era già stato interrotto (primo trimestre 2013) quando ho eseguito i miei benchmark. È stato rilasciato ed è stato all'avanguardia nel quarto trimestre del 2011. Se eseguo i numeri per il quarto trimestre del 2011:
R = 64 × 2(129/18)
R = 64 × 27.16
R = 64 × 143.7
R = 9,196.8
cost = ln(R) / ln(2)
cost = ln(9,196.8) / ln(2)
cost = 13.2
Un costo di 13 è, sul mio desktop, quasi 2 secondi in un secondo.
Per quanto tempo puoi sopportarlo?
Questo ti dà il sapore del tipo di ritardi che gli implementatori originali stavano prendendo in considerazione quando lo hanno scritto: ~ 0.5-1 secondo.
Ma, naturalmente, più a lungo puoi stare, meglio è. Ogni implementazione di BCrypt che ho visto utilizzava 10
come costo predefinito. E la mia implementazione l'ha usata. Credo che sia tempo per me di aumentare il costo predefinito a 12.
Proofing futuro
Potrei anche cambiare la funzione di hash:
hash = HashPassword("correct battery horse stapler");
vale a dire. quello in cui si fa affidamento sul costo predefinito, per utilizzare invece un costo scorrevole automaticamente. In questo modo il costo è auto-aumentante nel tempo. Modifica:
String HashPassword(String password)
{
return BCrypt.HashPassword(password, BCRYPT_DEFAULT_COST);
}
in qualcosa di simile:
String HashPassword(String password)
{
/*
Rather than using a fixed default cost, run a micro-benchmark
to figure out how fast the CPU is.
Use that to make sure that it takes **at least** 250ms to calculate
the hash
*/
Int32 costFactor = this.CalculateIdealCost();
//Never use a cost lower than the default hard-coded cost
if (costFactor < BCRYPT_DEFAULT_COST)
costFactor = BCRYPT_DEFAULT_COST;
return BCrypt.HashPassword(password, costFactor);
}
Int32 CalculateIdealCost()
{
//Benchmark using a cost of 5 (the second-lowest allowed)
Int32 cost = 5;
var sw = new Stopwatch();
sw.Start();
this.HashPassword("microbenchmark", cost);
sw.Stop();
Double durationMS = sw.Elapsed.TotalMilliseconds;
//Increasing cost by 1 would double the run time.
//Keep increasing cost until the estimated duration is over 250 ms
while (durationMS < 250)
{
cost += 1;
durationMS *= 2;
}
return cost;
}
Modifica 3/12/2015 : numeri di velocità aggiornati. Il compilatore Delphi XE6 a 32 bit (c.2013) genera un codice di un ordine di grandezza più veloce di quello di Delphi 5 (c.1999) per lo stesso processore. Il compilatore Delphi XE6 a 64 bit genera il codice più lento del 20% rispetto al compilatore a 32 bit.