В основе работы Stanford JavaScript Crypto Library лежит единый тип
данных — bitArray. Любая криптографическая операция внутри
библиотеки работает не со строками и байтами напрямую, а с массивами
32-битных слов, дополненными информацией о длине значимой части
последнего элемента.
Такое представление позволяет:
Любой внешний формат (hex, base64, JSON, бинарные строки) в sjcl
реализуется через слой кодеков (sjcl.codec), который
отвечает за преобразование:
bitArray ⇄ внешний формат
Все кодеки в sjcl находятся в пространстве:
sjcl.codec
Каждый кодек представляет собой объект с двумя ключевыми функциями:
encode(bitArray) — преобразование битового массива в
строку или структуруdecode(string) — обратное преобразование в
bitArrayТиповая сигнатура:
sjcl.codec.custom = {
encode: function (arr) { ... },
decode: function (str) { ... }
};
Некоторые кодеки дополнительно реализуют вспомогательные методы, но базовый контракт ограничивается этими двумя функциями.
Понимание структуры bitArray критично для создания
корректного кодека.
Пример структуры:
[
0x12345678,
0x9abcdef0,
0x13579b00, // последний элемент содержит "хвост"
100 // длина значимых бит
]
Особенности:
Используется для человекочитаемого представления байтов:
sjcl.codec.hex
sjcl.codec.base64
Используется для компактной передачи бинарных данных:
=sjcl.codec.utf8String
Пользовательские кодеки в sjcl применяются в случаях, когда требуется:
Корректный codec должен соблюдать несколько инвариантов:
Полная обратимость преобразования
decode(encode(x)) ≡ xУчет битовой длины bitArray
Отсутствие зависимости от внешнего состояния
Предсказуемая работа с неполными байтами
Рассматривается пример кодека на основе пользовательского алфавита (base-N).
var ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
var BASE = ALPHABET.length;
Для кодирования требуется преобразовать битовый массив в последовательность чисел.
Принцип:
sjcl.codec.baseN = {
encode: function (bitArray) {
var out = "";
var value = 0;
var bits = 0;
for (var i = 0; i < bitArray.length - 1; i++) {
value = (value << 32) | bitArray[i];
bits += 32;
while (bits >= 6) {
bits -= 6;
out += ALPHABET[(value >>> bits) & 63];
}
}
var last = bitArray[bitArray.length - 1];
value = (value << last) >>> 0;
while (bits > 0) {
if (bits < 6) {
out += ALPHABET[(value << (6 - bits)) & 63];
break;
}
bits -= 6;
out += ALPHABET[(value >>> bits) & 63];
}
return out;
}
};
Обратное преобразование требует восстановления битового массива:
sjcl.codec.baseN.decode = function (str) {
var out = [];
var value = 0;
var bits = 0;
for (var i = 0; i < str.length; i++) {
value = (value << 6) | ALPHABET.indexOf(str[i]);
bits += 6;
if (bits >= 32) {
bits -= 32;
out.push(value >>> bits);
value &= (1 << bits) - 1;
}
}
if (bits > 0) {
out.push(value << (32 - bits));
}
out.push(bits);
return out;
};
Все пользовательские кодеки добавляются в пространство:
sjcl.codec.baseN = baseN;
После этого они становятся доступными как стандартные:
var encoded = sjcl.codec.baseN.encode(bits);
var decoded = sjcl.codec.baseN.decode(encoded);
Ключевая сложность кодеков sjcl заключается в корректной обработке “хвоста” битового массива.
Особенности:
Типовая ошибка — потеря значимых бит при сдвигах:
value << (32 - bits)
Некоторые кастомные кодеки расширяются до потокового режима:
В таких случаях кодек разделяется на:
bitArray[length - 1]Кодек не является криптографическим компонентом.
Он:
Ошибочная интерпретация кодека как средства защиты приводит к ложному чувству безопасности.
+, /, =Кодеки используются во всех ключевых модулях:
sjcl.encryptsjcl.decryptsjcl.hashsjcl.randomЛюбое изменение кодека влияет только на внешний слой представления, но не на криптографическое ядро.