Криптографическое семейство SHA-3 (Keccak) основано на принципиально иной конструкции хеш-функций по сравнению с SHA-2. Вместо классической схемы Меркла–Дамгора используется конструкция sponge construction (губка), в которой входные данные “впитываются” в внутреннее состояние фиксированного размера, после чего из него “выжимаются” выходные биты требуемой длины.
В контексте JavaScript-библиотеки Stanford JS Crypto Library (SJCL) важно учитывать архитектурные особенности: библиотека исторически ориентирована на компактность, предсказуемость и минимализм зависимостей. Основной набор хеш-функций в SJCL включает SHA-256 и HMAC на его основе, тогда как SHA-3/Keccak не является частью базового ядра.
SHA-3 стандартизирован NIST и основан на алгоритме Keccak. Его ключевая особенность — внутренняя перестановка состояния (permutation) и разделение состояния на две части:
Общее состояние:
state = r + c
Хеширование происходит в два этапа:
Absorbing phase (поглощение) Входные данные разбиваются на блоки и XOR’ятся с частью состояния rate, после чего применяется перестановка Keccak-f.
Squeezing phase (выжимание) Из состояния последовательно извлекаются блоки выходного хеша, при необходимости с повторным применением перестановки.
Ключевое свойство: безопасность зависит от capacity, а не от длины выходного хеша напрямую.
В SJCL криптографические примитивы реализуются через унифицированные интерфейсы. Хеш-функции обычно реализуют API вида:
var hash = new sjcl.hash.sha256();
hash.update("data");
var result = hash.finalize();
или через статические методы:
sjcl.hash.sha256.hash("data");
Внутри библиотека ожидает, что хеш-объект поддерживает:
update(data)finalize()reset()Поскольку SJCL не включает SHA-3 по умолчанию, возникает задача
адаптации внешней реализации Keccak к интерфейсу
sjcl.hash.
Сложность заключается в различии моделей:
Поэтому интеграция требует либо:
На практике чаще используется готовая реализация SHA-3, например из
npm-пакетов (js-sha3, keccak,
crypto-js с расширениями). Пример с использованием
абстрактной функции Keccak:
function KeccakWrapper(bits) {
this._keccak = new Keccak(bits);
}
KeccakWrapper.prototype.update = function (data) {
this._keccak.update(data);
return this;
};
KeccakWrapper.prototype.finalize = function () {
return this._keccak.digest();
};
Далее создаётся адаптер под SJCL:
sjcl.hash.sha3_256 = function () {
this._impl = new KeccakWrapper(256);
};
sjcl.hash.sha3_256.prototype.update = function (data) {
this._impl.update(data);
};
sjcl.hash.sha3_256.prototype.finalize = function () {
return this._impl.finalize();
};
Теперь API становится совместимым с остальными хешами SJCL:
var h = new sjcl.hash.sha3_256();
h.update("hello");
var digest = h.finalize();
SJCL активно использует собственный тип sjcl.bitArray,
представляющий данные как массив 32-битных слов.
Keccak-реализации, напротив, часто работают с:
Поэтому критически важен слой преобразования:
function bitArrayToBytes(arr) {
return sjcl.codec.bytes.fromBits(arr);
}
function bytesToBitArray(bytes) {
return sjcl.codec.bytes.toBits(bytes);
}
Обёртка должна обеспечивать согласованность форматов:
KeccakWrapper.prototype.update = function (bitArray) {
var bytes = bitArrayToBytes(bitArray);
this._keccak.update(bytes);
};
В отличие от SHA-256, SHA-3 (Keccak) имеет другие характеристики производительности:
При этом он обеспечивает:
В семействе Keccak присутствуют функции SHAKE:
Они позволяют получать произвольную длину выхода:
var shake = new KeccakWrapper(256);
shake.update("data");
var out = shake.finalize(64); // 64 байта
Для SJCL это требует расширения интерфейса
finalize(length).
SJCL реализует HMAC через универсальный интерфейс хеш-функции:
var mac = new sjcl.misc.hmac(key, sjcl.hash.sha256);
mac.update("message");
var tag = mac.digest();
Для SHA-3 адаптер должен быть совместим:
var mac = new sjcl.misc.hmac(key, sjcl.hash.sha3_256);
Это возможно только если SHA-3-класс реализует идентичный API.
PBKDF2 в SJCL зависит от хеш-функции как параметра:
sjcl.misc.pbkdf2(password, salt, iterations, keyLength, sjcl.hash.sha3_256);
Критически важно, чтобы SHA-3:
1. Несовместимость форматов данных Keccak ожидает байты, SJCL работает с bitArray.
2. Повторное использование экземпляра hash Некорректный reset приводит к утечке состояния.
3. Несоответствие выходного формата SHA-3 возвращает Uint8Array, SJCL ожидает bitArray.
4. Игнорирование padding-логики Keccak использует собственное padding rule (multi-rate padding), несовместимое с SHA-2.
sjcl.hash.sha3_256 = function () {
this._state = new Keccak(256);
};
sjcl.hash.sha3_256.prototype.update = function (bits) {
this._state.update(sjcl.codec.bytes.fromBits(bits));
};
sjcl.hash.sha3_256.prototype.finalize = function () {
var out = this._state.digest();
return sjcl.codec.bytes.toBits(out);
};
sjcl.hash.sha3_256.prototype.reset = function () {
this._state = new Keccak(256);
};
При использовании SHA-3 в SJCL в реальных приложениях важно учитывать:
Для оптимизации применяют:
SHA-256 (SJCL native):
SHA-3 (external integration):
Архитектурная гибкость SJCL позволяет подключать SHA-3 как внешнюю
реализацию при условии строгого соблюдения интерфейса
sjcl.hash, корректного преобразования данных и аккуратной
работы с состоянием хеш-функции.