В библиотеке Stanford JavaScript Crypto Library (SJCL) режим шифрования представляет собой слой поверх блочного шифра (обычно AES), который определяет, как именно обрабатываются данные больше одного блока, как используется вектор инициализации, и каким образом обеспечивается целостность и конфиденциальность сообщения.
Базовый принцип: режим шифрования = алгоритм управления потоками блоков + обработка IV + (опционально) аутентификация
Внутри SJCL режимы реализуются как объекты в пространстве
sjcl.mode, где каждый режим обязан предоставлять единый
интерфейс шифрования и расшифрования.
Любой пользовательский режим должен соответствовать ожидаемому
интерфейсу, который используется функциями sjcl.encrypt и
sjcl.decrypt.
Минимальный набор требований:
encrypt(prf, plaintext, iv, adata)decrypt(prf, ciphertext, iv, adata)sjcl.cipher.aesПараметры:
prf — псевдослучайная функция, обычно экземпляр AES
(new sjcl.cipher.aes(key))plaintext / ciphertext — массивы слов SJCL
(sjcl.bitArray)iv — вектор инициализацииadata — дополнительные аутентифицированные данные (если
режим поддерживает AEAD)Режим в SJCL обычно оформляется как функция-конструктор:
sjcl.mode.MyMode = function (cipher) {
this.cipher = cipher;
};
И набор методов:
sjcl.mode.MyMode.prototype.encrypt = function (prf, plaintext, iv, adata) {
...
};
sjcl.mode.MyMode.prototype.decrypt = function (prf, ciphertext, iv, adata) {
...
};
После этого режим регистрируется в системе:
sjcl.mode.myMode = function (aes, plaintext, iv, adata) {
return new sjcl.mode.MyMode(aes).encrypt(aes, plaintext, iv, adata);
};
и аналогично для decrypt.
Для понимания механики полезно рассмотреть упрощённый потоковый режим, который генерирует псевдослучайный поток на основе AES и XOR-ит его с данными.
sjcl.mode.XorStream = function (aes) {
this.cipher = aes;
};
SJCL работает с sjcl.bitArray, поэтому используется:
function xor(a, b) {
return sjcl.bitArray.bitxor(a, b);
}
sjcl.mode.XorStream.prototype.encrypt = function (aes, plaintext, iv) {
var blockSize = 4; // 128 бит = 4 слова
var counter = iv.slice(0);
var output = [];
var i, keystream;
for (i = 0; i < plaintext.length; i += blockSize) {
keystream = aes.encrypt(counter);
var chunk = plaintext.slice(i, i + blockSize);
output = output.concat(xor(chunk, keystream));
counter[3]++; // инкремент младшего слова счётчика
}
return output;
};
Для потоковых режимов шифрование и дешифрование идентичны:
sjcl.mode.XorStream.prototype.decrypt = sjcl.mode.XorStream.prototype.encrypt;
Чтобы режим стал доступен через стандартный API SJCL, его необходимо зарегистрировать.
sjcl.mode.xorStream = {
encrypt: function (aes, plaintext, iv, adata) {
return new sjcl.mode.XorStream(aes).encrypt(aes, plaintext, iv, adata);
},
decrypt: function (aes, ciphertext, iv, adata) {
return new sjcl.mode.XorStream(aes).decrypt(aes, ciphertext, iv, adata);
}
};
После этого режим можно использовать через стандартные функции:
sjcl.encrypt("password", "data", { mode: "xorStream" });
sjcl.decrypt("password", json, { mode: "xorStream" });
Внутри SJCL выбор режима происходит по строковому идентификатору:
options.mode
При вызове:
sjcl.encrypt(password, data, { mode: "ccm" });
библиотека выполняет:
sjcl.mode[mode]encryptПоэтому пользовательский режим должен быть доступен именно через:
sjcl.mode["имя"]
IV не должен генерироваться внутри режима без необходимости. SJCL ожидает:
sjcl.encryptТипичная ошибка при реализации:
Корректная практика:
var ivCopy = iv.slice(0);
Если требуется AEAD-подобное поведение, режим должен:
adata в хэшПростейшая схема:
var mac = sjcl.hash.sha256.hash(adata + ciphertext);
и сравнение:
if (!sjcl.bitArray.equal(mac, expectedMac)) {
throw new sjcl.exception.corrupt("tag mismatch");
}
После регистрации режима он становится частью стандартного набора:
sjcl.encryptsjcl.decryptsjcl.json.encryptsjcl.json.decryptПример использования:
var encrypted = sjcl.encrypt("key", "secret data", {
mode: "xorStream",
ts: 64
});
var decrypted = sjcl.decrypt("key", encrypted, {
mode: "xorStream"
});
Сам факт корректной реализации интерфейса не означает криптографическую безопасность режима.
Критически важные требования:
Самодельные режимы допустимы только для:
SJCL проектировался как расширяемая система:
sjcl.cipher — блочные шифрыsjcl.mode — режимы работыsjcl.misc — вспомогательные функцииsjcl.codec — кодирование форматовРежим шифрования является связующим звеном между криптографическим
ядром и форматом данных верхнего уровня, поэтому корректная регистрация
требует соблюдения интерфейсной совместимости и строгой работы с
bitArray.
sjcl.mode.custom = {
encrypt: function (aes, plaintext, iv, adata) {
return new sjcl.mode.XorStream(aes).encrypt(aes, plaintext, iv);
},
decrypt: function (aes, ciphertext, iv, adata) {
return new sjcl.mode.XorStream(aes).decrypt(aes, ciphertext, iv);
}
};