Библиотека CryptoJS построена вокруг модульной системы криптографических примитивов, где каждый алгоритм реализуется как расширение базового класса хеширования. Такой подход позволяет внедрять собственные алгоритмы, сохраняя совместимость с внутренними механизмами обработки данных, потокового обновления и финализации результата.
В основе лежит объектная модель CryptoJS.lib, где
ключевую роль играют:
Base — базовый класс для всех объектов библиотекиWordArray — представление бинарных данныхHasher — абстракция для всех хеш-функцийBufferedBlockAlgorithm — обработка данных блокамиХеш-алгоритм в CryptoJS представляет собой класс с переопределением трёх основных методов:
_doReset — инициализация состояния_doProcessBlock — обработка блока данных_doFinalize — завершение вычисленияСоздание собственного алгоритма начинается с расширения
CryptoJS.lib.Hasher.
const MyHash = CryptoJS.lib.Hasher.extend({
_doReset: function () {
this._state = 0x12345678;
},
_doProcessBlock: function (words, offset) {
let block = words[offset];
this._state ^= block;
this._state = (this._state << 5) | (this._state >>> 27);
},
_doFinalize: function () {
return CryptoJS.lib.WordArray.create([this._state]);
}
});
Такая структура уже является полноценным хеш-алгоритмом, интегрированным в систему CryptoJS.
Любой хеш-алгоритм опирается на внутреннее состояние. В CryptoJS оно обычно представляется числовыми регистрами или массивами 32-битных слов.
Метод _doReset вызывается при создании экземпляра и
повторной инициализации:
_doReset: function () {
this._a = 0x67452301;
this._b = 0xEFCDAB89;
this._c = 0x98BADCFE;
this._d = 0x10325476;
}
Подобный подход характерен для алгоритмов семейства MD5 и SHA-подобных конструкций.
В CryptoJS входные данные автоматически преобразуются в массив
32-битных слов (WordArray). Каждый блок данных передаётся в
_doProcessBlock.
_doProcessBlock: function (words, offset) {
let w0 = words[offset];
let w1 = words[offset + 1];
this._a = this._a + (w0 ^ this._b);
this._b = this._b + (w1 ^ this._c);
this._a = (this._a << 7) | (this._a >>> 25);
}
Ключевая особенность модели заключается в том, что данные не обрабатываются целиком, а поступают по блокам фиксированного размера (обычно 512 бит).
Метод _doFinalize отвечает за финальную стадию
алгоритма. Здесь выполняется:
_doFinalize: function () {
let bitLength = this._nDataBytes * 8;
this._data.words.push(0x80 << 24);
while (this._data.words.length % 16 !== 14) {
this._data.words.push(0);
}
this._data.words.push(0);
this._data.words.push(bitLength);
this._process();
return CryptoJS.lib.WordArray.create([
this._a, this._b, this._c, this._d
]);
}
Финализация тесно связана с внутренним буфером
BufferedBlockAlgorithm, который управляет накоплением
данных.
После определения класса алгоритм интегрируется в пространство
CryptoJS.algo.
CryptoJS.algo.MyHash = MyHash;
Теперь алгоритм становится доступным через стандартный интерфейс:
const hash = CryptoJS.MyHash("data").toString();
WordArray — фундаментальная структура представления
бинарных данных.
let wa = CryptoJS.lib.WordArray.create([0xDEADBEEF, 0xCAFEBABE]);
Основные операции:
Внутри хеш-алгоритма WordArray используется для:
CryptoJS полностью опирается на 32-битные операции, так как JavaScript не имеет нативной поддержки низкоуровневой битовой арифметики.
Типовые операции:
(a + b) >>> 0
(a << 5) | (a >>> 27)
a ^ b
Особое внимание требуется к переполнению, поскольку оно имитирует поведение беззнаковых 32-битных регистров.
Ниже приведён минимальный рабочий пример линейного хеша:
const SimpleHash = CryptoJS.lib.Hasher.extend({
_doReset: function () {
this._state = 0;
},
_doProcessBlock: function (words, offset) {
for (let i = 0; i < 16; i++) {
this._state += words[offset + i];
this._state = this._state ^ (this._state << 3);
}
},
_doFinalize: function () {
return CryptoJS.lib.WordArray.create([this._state >>> 0]);
}
});
CryptoJS.algo.SimpleHash = SimpleHash;
Одно из ключевых преимуществ CryptoJS — поддержка потоковой обработки данных.
Методы:
update(data)finalize(data)clone()Пример внутреннего поведения:
let hasher = CryptoJS.algo.MyHash.create();
hasher.update("part1");
hasher.update("part2");
let result = hasher.finalize();
Каждый вызов update вызывает буферизацию и частичную
обработку блоков.
Клонирование необходимо для параллельных вычислений:
clone: function () {
let clone = this.constructor.create();
clone._state = this._state;
return clone;
}
Это позволяет сохранять промежуточное состояние и продолжать вычисления независимо.
При создании собственного хеша необходимо учитывать:
Типичный алгоритм в CryptoJS можно представить как конвейер:
WordArray_doProcessBlock_doFinalizeWordArrayCryptoJS позволяет расширять не только хеши, но и вспомогательные компоненты:
Пример изменения размера блока:
blockSize: 512/32
BufferedBlockAlgorithm управляет буфером данных:
_doProcessBlock при заполнении блокаЭто скрытый слой, обеспечивающий потоковую обработку.
Хотя хеши и шифры различаются, архитектура CryptoJS унифицирована:
HasherCipherWordArrayЭто позволяет комбинировать алгоритмы в цепочки обработки данных.