Типичные ошибки при преобразовании данных

Одна из самых частых проблем при работе с SJCL связана с тем, что большинство разработчиков ожидают, что библиотека оперирует строками или Uint8Array, тогда как базовый тип данных в SJCL — это bitArray.

bitArray представляет собой массив 32-битных слов с дополнительным указанием длины в битах. Это критически важно:

  • последний элемент массива хранит длину данных
  • данные могут быть не кратны 8 битам
  • прямое преобразование через стандартные JS-функции приводит к потере данных

Ошибка возникает, когда bitArray воспринимается как обычный массив чисел:

const hash = sjcl.hash.sha256.hash("test");
console.log(hash.length); // не длина в байтах!

Правильная интерпретация требует использования кодеков:

const hex = sjcl.codec.hex.fromBits(hash);

Игнорирование кодеков sjcl.codec

SJCL строго разделяет внутренний формат и представление данных. Любая попытка вручную преобразовать данные приводит к ошибкам.

Распространённые ошибки:

  • использование toString() вместо sjcl.codec.*
  • смешивание hex/base64/utf8 без явного кодирования
  • передача строк напрямую в криптографические функции

Пример неправильного подхода

const encrypted = sjcl.encrypt("password", "data");
console.log(encrypted.toString()); // бессмысленный результат

Правильный подход

const encrypted = sjcl.encrypt("password", "data");
const parsed = JSON.parse(encrypted);

Ошибки при работе с UTF-8 строками

SJCL не предполагает автоматической нормализации строк. Любая строка преобразуется в битовый массив через utf8String.

Проблемы возникают в следующих случаях:

  • различие между NFC и NFD (актуально для Unicode)
  • символы за пределами BMP (эмодзи, редкие иероглифы)
  • смешивание строк и байтовых представлений

Ошибка:

sjcl.hash.sha256.hash("café");
sjcl.hash.sha256.hash("cafe\u0301"); // другой результат

Хотя визуально строки одинаковы, байтовое представление различается.


Потеря данных при преобразовании hex и base64

При работе с hex и base64 кодеками часто возникает ошибка обрезки ведущих нулей.

Типичная проблема

const bits = sjcl.codec.hex.toBits("0a");
const back = sjcl.codec.hex.fromBits(bits);

Результат может неожиданно стать "a" вместо "0a".

Это связано с тем, что hex-строка интерпретируется как число, а не как фиксированная длина байтового массива.


Ошибки при конкатенации bitArray

Обычная операция:

const a = sjcl.codec.utf8String.toBits("hello");
const b = sjcl.codec.utf8String.toBits("world");

const c = a.concat(b);

Проблема заключается в том, что concat в SJCL:

  • не копирует данные глубоко
  • может привести к мутации исходных массивов
  • требует корректной работы с длиной битов

Ошибка проявляется при повторном использовании a или b после конкатенации.


Неверное использование длины битов

Каждый bitArray хранит длину в последнем элементе. Игнорирование этого приводит к:

  • обрезке данных
  • лишним нулевым битам
  • ошибкам при дешифровке

Типичная ошибка:

const bits = sjcl.codec.utf8String.toBits("data");
bits.pop(); // разрушение структуры

Любая модификация массива вручную приводит к некорректной работе криптографических функций.


Проблемы с AES и размером блока

SJCL использует блочные шифры, где входные данные должны быть корректно выровнены.

Распространённые ошибки:

  • передача строк вместо bitArray
  • отсутствие padding при ручной обработке
  • смешивание режимов (CBC, CTR)

Пример некорректного использования:

sjcl.encrypt("key", "short");

Хотя API допускает строки, при низкоуровневом использовании возникает рассинхронизация форматов.


Ошибки при хранении данных JSON

bitArray не сериализуется напрямую:

JSON.stringify(sjcl.hash.sha256.hash("test")); // потеря структуры

После десериализации восстановить исходные данные невозможно без кодека.

Правильный способ:

const hex = sjcl.codec.hex.fromBits(bits);
JSON.stringify(hex);

Неправильное использование случайных чисел

SJCL предоставляет криптографически стойкий генератор:

sjcl.random.randomWords(4);

Ошибка возникает, когда:

  • результат трактуется как обычный массив чисел
  • игнорируется необходимость преобразования в байты
  • используется без инициализации энтропии

Несовместимость форматов между функциями

Разные компоненты SJCL ожидают разные представления:

  • sjcl.hashbitArray
  • sjcl.encrypt → строка или bitArray
  • sjcl.codec.* → конвертеры

Ошибка возникает при смешивании:

const hash = sjcl.hash.sha256.hash("data");
sjcl.encrypt("key", hash); // некорректное использование

Правильный путь:

const hex = sjcl.codec.hex.fromBits(hash);
sjcl.encrypt("key", hex);

Потеря совместимости из-за base64url и base64

SJCL использует стандартный base64, но часто данные приходят в base64url.

Отличия:

  • + и / vs - и _
  • отсутствие =

Ошибка возникает при прямой передаче:

sjcl.codec.base64.toBits(base64url); // некорректно

Необходима предварительная нормализация строки.


Ошибки копирования и мутации объектов

bitArray передаётся по ссылке. Любая операция над ним может изменить исходные данные:

const a = sjcl.codec.utf8String.toBits("test");
const b = a;
b[0] = 0; // изменяет a

Это приводит к:

  • некорректным хэшам
  • расхождению данных при проверке подписи
  • невозможности воспроизвести результат

Несовпадение ожиданий между JS и SJCL

JavaScript разработчики часто ожидают:

  • строки как основной формат
  • байтовые массивы Uint8Array
  • автоматическое преобразование типов

SJCL же работает строго:

  • только bitArray
  • явные кодеки
  • отсутствие неявных преобразований

Любая попытка “упростить” приводит к скрытым ошибкам, которые проявляются только на этапе дешифрования или проверки подписи.