Коротка версія
Кількість ітерацій, які дають принаймні 250 мс для обчислення
Довга версія
Коли BCrypt був вперше опублікований, у 1999 році, вони перелічили фактори вартості за замовчуванням для їх реалізації:
- звичайний користувач: 6
- суперкористувач: 8
Вартість bcrypt 6 означає 64 раунди (2 6 = 64).
Вони також зазначають:
Звичайно, яку б вартість люди не вибрали, її слід періодично переоцінювати
- На момент розгортання в 1976 р. Крипта могла гешувати менше 4 паролів на секунду. (250 мс на пароль)
- У 1977 році на VAX-11/780 крипту (MD5) можна було оцінити приблизно 3,6 рази на секунду. (277 мс на пароль)
Це надає вам смак таких затримок, які враховували оригінальні реалізатори, коли писали:
- ~ 250 мс для звичайних користувачів
- ~ 1 секунда для суперкористувачів.
Але, звичайно, чим довше ви зможете стояти, тим краще. Кожна реалізація BCrypt, яку я бачив, використовується 10
як вартість за замовчуванням. І моя реалізація використовувала це. Я вважаю, мені пора збільшити вартість за замовчуванням до 12.
Ми вирішили, що хочемо націлити не менше 250 мс на хеш.
Мій настільний ПК - це процесор Intel Core i7-2700K на частоті 3,50 ГГц. Спочатку я провів тестування реалізації BCrypt 23.01.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 |
Майбутнє випробування
Замість того, щоб мати фіксовану константу, вона повинна бути фіксованим мінімумом .
Замість того, щоб мати хеш-функцію пароля:
String HashPassword(String password)
{
return BCrypt.HashPassword(password, BCRYPT_DEFAULT_COST);
}
це має бути приблизно так:
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;
}
І в ідеалі це було б частиною бібліотеки BCrypt кожного, тож замість того, щоб покладатися на користувачів бібліотеки, щоб періодично збільшувати вартість, вартість періодично зростає сама.