В библиотеке SJCL (Stanford JavaScript Crypto Library) хэширование реализовано через набор криптографических алгоритмов, наиболее часто используемых в веб-приложениях: SHA-256, SHA-1, SHA-512. Основной принцип работы хэш-функций — преобразование произвольного объёма данных в фиксированную строку (digest), обладающую свойствами необратимости и детерминированности.
В SJCL хэш-функции находятся в пространстве
sjcl.hash:
sjcl.hash.sha256
sjcl.hash.sha1
sjcl.hash.sha512
Каждый алгоритм предоставляет унифицированный интерфейс, основанный
на работе с внутренним представлением данных —
bitArray.
Ключевая особенность SJCL заключается в том, что все входные данные
приводятся к структуре bitArray. Это не массив байтов в
привычном смысле, а специализированная структура, оптимизированная для
криптографических операций.
Пример создания bitArray:
const bits = sjcl.codec.utf8String.toBits("test");
Преобразование обратно:
const text = sjcl.codec.utf8String.fromBits(bits);
Внутренне bitArray хранит данные в виде 32-битных слов с
дополнительной информацией о длине последнего слова.
Самый простой сценарий — получение хэша строки. Для этого
используется метод hash у конкретного алгоритма.
const hashBits = sjcl.hash.sha256.hash("hello world");
const hashHex = sjcl.codec.hex.fromBits(hashBits);
В данном примере:
bitArrayЭквивалент более явного варианта:
const data = sjcl.codec.utf8String.toBits("hello world");
const hashBits = sjcl.hash.sha256.hash(data);
SJCL не работает напрямую со строками при криптографических операциях. Для преобразования используются кодеки:
sjcl.codec.utf8Stringsjcl.codec.hexsjcl.codec.base64Пример преобразования хэша в base64:
const hashBits = sjcl.hash.sha256.hash("data");
const hashBase64 = sjcl.codec.base64.fromBits(hashBits);
SJCL не поддерживает прямое хэширование JavaScript-объектов. Любая структура должна быть сериализована.
Типичный подход:
const obj = {
user: "alice",
id: 42,
roles: ["admin", "editor"]
};
const json = JSON.stringify(obj);
const bits = sjcl.codec.utf8String.toBits(json);
const hash = sjcl.hash.sha256.hash(bits);
Ключевая проблема здесь — детерминированность сериализации. Разные порядки ключей приведут к разным хэшам, поэтому для стабильного результата требуется нормализация объекта.
SJCL поддерживает потоковое хэширование через методы
update и finalize. Это важно при работе с
большими файлами или потоками данных.
Пример:
const hasher = new sjcl.hash.sha256();
hasher.update(sjcl.codec.utf8String.toBits("part1 "));
hasher.update(sjcl.codec.utf8String.toBits("part2"));
const result = hasher.finalize();
const hex = sjcl.codec.hex.fromBits(result);
Особенности:
update можно вызывать многократноfinalize завершает вычисление и возвращает
результатfinalize экземпляр хэшера нельзя использовать
повторноВ веб-приложениях данные часто приходят в виде
ArrayBuffer или Uint8Array. SJCL требует
преобразования в bitArray.
Пример конвертации Uint8Array:
function bytesToBits(bytes) {
const words = [];
let acc = 0;
let count = 0;
for (let i = 0; i < bytes.length; i++) {
acc = (acc << 8) | bytes[i];
count += 8;
if (count === 32) {
words.push(acc);
acc = 0;
count = 0;
}
}
if (count > 0) {
acc = acc << (32 - count);
words.push(acc | count);
}
return words;
}
Использование:
const buffer = new Uint8Array([1, 2, 3, 4]);
const bits = bytesToBits(buffer);
const hash = sjcl.hash.sha256.hash(bits);
На практике чаще используется sjcl.codec.hex или
сторонние утилиты для конвертации, так как SJCL не предоставляет прямого
удобного адаптера для ArrayBuffer.
При обработке файлов через <input type="file">
используется FileReader:
const file = input.files[0];
const reader = new FileReader();
reader.onl oad = function () {
const arrayBuffer = reader.result;
const bytes = new Uint8Array(arrayBuffer);
const bits = bytesToBits(bytes);
const hashBits = sjcl.hash.sha256.hash(bits);
const hashHex = sjcl.codec.hex.fromBits(hashBits);
};
reader.readAsArrayBuffer(file);
Для больших файлов предпочтительнее потоковая обработка через
update, если данные читаются чанками.
Частая задача — хэширование нескольких фрагментов данных как единого потока.
Правильный подход:
const h = new sjcl.hash.sha256();
h.update(sjcl.codec.utf8String.toBits("header"));
h.update(sjcl.codec.utf8String.toBits("|"));
h.update(sjcl.codec.utf8String.toBits("payload"));
const digest = h.finalize();
Неправильный подход — конкатенация строк до кодирования, если данные бинарные или структурированные, так как это может привести к неоднозначности.
1. Работа со строками без кодеков
sjcl.hash.sha256.hash("text"); // не гарантирует корректное преобразование
Корректный вариант:
sjcl.hash.sha256.hash(sjcl.codec.utf8String.toBits("text"));
2. Повторное использование хэшера после finalize
const h = new sjcl.hash.sha256();
h.update(...);
h.finalize();
h.update(...); // ошибка логики
3. Нестабильная сериализация объектов
JSON.stringify(obj)
Без контроля порядка ключей результат может отличаться между окружениями.
4. Потеря бинарных данных при использовании UTF-8
Использование utf8String для произвольных байтов
приводит к искажению данных.
Хэширование в SJCL часто используется не само по себе, а как основа для:
sjcl.misc.hmac)Типичный паттерн идентификатора:
const id = sjcl.codec.hex.fromBits(
sjcl.hash.sha256.hash(sjcl.codec.utf8String.toBits(json))
);