Блочные алгоритмы шифрования в JavaScript-библиотеке CryptoJS работают с фиксированными размерами блоков данных. AES, TripleDES и другие алгоритмы принимают входные данные строго кратные размеру блока (например, 16 байт для AES). Любое отклонение от этого требования приводит к невозможности корректного шифрования.
Для устранения этой проблемы применяется дополнение (padding) — механизм, который приводит длину входного сообщения к необходимому размеру.
При шифровании данных текст разбивается на блоки фиксированной длины. Если последний блок оказывается неполным, его необходимо дополнить байтами до требуемого размера.
Без механизма дополнения шифрование невозможно, поскольку алгоритм не определяет, где заканчиваются реальные данные, а где добавленные байты.
Дополнение решает две задачи:
В CryptoJS дополнение выполняется автоматически при вызове
encrypt, если явно не указано иное.
Библиотека предоставляет несколько встроенных реализаций
padding-алгоритмов, реализованных в пространстве
CryptoJS.pad.
Наиболее распространённая схема. В конец добавляется N байт, каждый из которых равен значению N.
Пример:
Исходные данные:
"HELLO"
Размер блока: 8 байт Длина строки: 5 байт Не хватает: 3 байта
Результат:
HELLO\x03\x03\x03
PKCS7 используется по умолчанию в AES:
const encrypted = CryptoJS.AES.encrypt("Hello world", "secret key");
// padding: CryptoJS.pad.Pkcs7 применяется автоматически
Дополнение нулевыми байтами. Простой, но менее безопасный метод, поскольку невозможно отличить реальные нули от добавленных.
const encrypted = CryptoJS.AES.encrypt("Hello", "key", {
padding: CryptoJS.pad.ZeroPadding
});
Особенность:
Дополнение случайными байтами, последний байт содержит длину дополнения.
Пример:
HELLO + random + \x03
Используется реже, но повышает устойчивость к анализу структуры данных.
Внутри CryptoJS padding реализован как объект с двумя методами:
pad(data, blockSize)unpad(data)При шифровании вызывается pad, при расшифровании —
unpad.
Упрощённая структура:
CryptoJS.pad.Pkcs7 = {
pad: function (data, blockSize) {
// добавление байтов
},
unpad: function (data) {
// удаление байтов
}
};
Библиотека ожидает, что padding изменяет объект
WordArray, используемый внутри CryptoJS для хранения
бинарных данных.
CryptoJS позволяет полностью заменить механизм padding, что полезно в следующих случаях:
Кастомная схема должна реализовывать два метода: pad и
unpad.
Основная идея — контроль добавляемых байтов.
Пример простой схемы, где добавляется фиксированный байт
0x80, а затем нули:
const CustomPadding = {
pad: function (data, blockSize) {
const blockSizeBytes = blockSize * 4;
const paddingBytes = blockSizeBytes - (data.sigBytes % blockSizeBytes);
const paddingWord = (0x80 << 24);
data.concat(CryptoJS.lib.WordArray.create([paddingWord]));
for (let i = 1; i < paddingBytes; i++) {
data.concat(CryptoJS.lib.WordArray.create([0x00000000]));
}
},
unpad: function (data) {
const words = data.words;
let i = data.sigBytes - 1;
while (i >= 0) {
const byte = (words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff;
if (byte === 0x80) {
break;
}
i--;
}
data.sigBytes = i;
}
};
После определения схемы она передаётся через параметр
padding:
const encrypted = CryptoJS.AES.encrypt("Sensitive data", "secret key", {
padding: CustomPadding
});
При расшифровке используется тот же механизм автоматически:
const decrypted = CryptoJS.AES.decrypt(encrypted, "secret key", {
padding: CustomPadding
});
Несовпадение padding на этапах шифрования и дешифрования приводит к повреждению данных.
Для более удобного использования можно встроить padding в
пространство CryptoJS.pad:
CryptoJS.pad.Custom = CustomPadding;
После этого использование становится аналогичным стандартным схемам:
const encrypted = CryptoJS.AES.encrypt("Data", "key", {
padding: CryptoJS.pad.Custom
});
Иногда требуется добавить длину сообщения в начало блока дополнения.
Пример реализации:
const LengthPrefixPadding = {
pad: function (data, blockSize) {
const len = data.sigBytes;
data.concat(CryptoJS.lib.WordArray.create([len << 24]));
const blockSizeBytes = blockSize * 4;
const padding = blockSizeBytes - (data.sigBytes % blockSizeBytes);
for (let i = 0; i < padding; i++) {
data.concat(CryptoJS.lib.WordArray.create([0x00000000]));
}
},
unpad: function (data) {
const words = data.words;
const lengthByte = words[0] >>> 24;
data.sigBytes = lengthByte;
}
};
Такая схема может использоваться в протоколах, где длина сообщения является частью структуры данных.
CryptoJS хранит данные в формате 32-битных слов. Каждый элемент
массива words содержит 4 байта.
При реализации padding важно учитывать:
sigBytesОшибки в этих операциях приводят к невозможности расшифрования.
Использование нестандартного padding накладывает ряд ограничений:
Стандартные схемы предпочтительнее при обмене данными между разными платформами.
Кастомные реализации применяются в случаях, когда требуется полный контроль над бинарным форматом шифртекста или интеграция с нестандартными криптографическими протоколами.