Библиотека bcrypt.js представляет собой чистую JavaScript-реализацию алгоритма хеширования паролей bcrypt, основанного на блочном шифре Blowfish. В отличие от нативных модулей, использующих C/C++ bindings, bcrypt.js полностью выполняется в среде JavaScript, что делает её переносимой, но одновременно накладывает ограничения на производительность.
Внутреннее устройство библиотеки можно условно разделить на несколько уровней:
В основе bcrypt лежит не стандартный Blowfish, а его модификация — eksBlowfish (expensive key schedule Blowfish). Именно «дорогая» стадия и делает алгоритм устойчивым к перебору.
Классический Blowfish состоит из:
В bcrypt.js структура полностью повторяет эту модель, но инициализация проходит через многократное переобучение ключевого расписания.
Изначально P-массив и S-блоки заполняются фиксированными константами, производными от дробной части числа π. Это важно для детерминированного старта.
P, S ← initial_state(pi_constants)
Соль в bcrypt.js играет ключевую роль в защите от радужных таблиц. Она всегда имеет фиксированный размер — 16 байт.
Внутренне процесс выглядит так:
crypto.randomBytes)./A-Za-z0-9)Формат соли:
$2a$<cost>$<22 символа соли>
Где cost — показатель сложности (например, 10, 12, 14).
Самая важная часть реализации — процедура расширения ключа:
В псевдокоде:
EksBlowfishSetup(cost, salt, password):
state = InitState()
state = ExpandKey(state, salt, password)
repeat 2^cost times:
state = ExpandKey(state, 0, password)
state = ExpandKey(state, 0, salt)
return state
Функция ExpandKey выполняет:
Именно здесь происходит основная вычислительная нагрузка.
bcrypt использует Blowfish в режиме ECB для шифрования фиксированного блока:
"OrpheanBeholderScryDoubt"
Этот 24-байтовый блок проходит 64 раунда шифрования, результат которого становится частью итогового хеша.
После завершения EksBlowfishSetup выполняется:
Финальный формат:
$2a$<cost>$<salt><hash>
Где:
Внутри библиотеки можно выделить несколько ключевых модулей:
Отвечает за основную функцию:
hashSynchashcompareSynccompareСодержит реализацию:
Реализует:
bcrypt.js поддерживает две модели выполнения:
Выполняется в основном потоке:
bcrypt.hashSync(password, saltRounds)
Минус — блокировка event loop.
Реализована через setTimeout разбиение нагрузки:
bcrypt.hash(password, saltRounds, callback)
Внутри используется разбиение EksBlowfish на чанки, чтобы избежать долгой блокировки.
Cost factor определяет количество итераций:
iterations = 2^cost
Примеры:
Каждое увеличение на 1 удваивает время вычисления.
bcrypt.js вынужден обходить ограничения JS:
Используются:
>>> 0 для приведения к uint32Blowfish требует быстрых операций, но JS использует:
Все арифметические операции приводятся к 32 битам:
a = (a + b) >>> 0;
Функция compare не просто сравнивает строки, а:
Важно: используется сравнение без раннего выхода, чтобы снизить риск timing attacks.
safeEqual(hash1, hash2)
bcrypt.js реализует:
Это снижает вероятность утечки информации через side-channel анализ.
Поскольку bcrypt.js написан на чистом JS:
Основное ограничение — блокировка event loop при высоких cost значениях.
Итоговая строка всегда кодируется в модифицированном base64:
./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789Это необходимо для совместимости с оригинальным bcrypt.
Полный цикл работы:
bcrypt.js построен вокруг одной идеи: искусственное замедление вычислений через дорогостоящую инициализацию ключей Blowfish, где основная стоимость перенесена в фазу setup, а не в сам процесс шифрования.
Это делает алгоритм устойчивым к перебору, поскольку каждая попытка проверки пароля требует полного пересчёта всей цепочки EksBlowfish.