Блочные алгоритмы шифрования оперируют фиксированными размерами блоков данных. Например, AES работает с блоками по 16 байт. Если входные данные не кратны размеру блока, их необходимо дополнить до нужной длины. Эта операция называется дополнением (padding).
Без корректного дополнения шифрование становится невозможным или приводит к неоднозначному восстановлению исходных данных при расшифровке.
Основные требования к схеме дополнения:
В библиотеке CryptoJS данные представлены в виде структуры
WordArray. Она хранит:
sigBytes)Перед шифрованием данные проходят этапы:
Padding в CryptoJS реализован как объект с двумя методами:
pad(wordArray, blockSize)unpad(wordArray)Любая кастомная схема дополнения должна соответствовать структуре:
pad: добавляет байты к данным до кратности блокаunpad: удаляет добавленные байты после расшифровкиconst CustomPadding = {
pad: function (data, blockSize) {
// модификация data (WordArray)
},
unpad: function (data) {
// удаление добавленных байтов
}
};
blockSize передаётся в 32-битных словах, поэтому
реальный размер блока в байтах:
blockSizeBytes = blockSize * 4
Одна из простейших схем — заполнение нулями.
const ZeroPadding = {
pad: function (data, blockSize) {
const blockSizeBytes = blockSize * 4;
const padding = blockSizeBytes - (data.sigBytes % blockSizeBytes);
for (let i = 0; i < padding; i++) {
data.concat(CryptoJS.lib.WordArray.create([0x00000000], 1));
}
},
unpad: function (data) {
while (data.sigBytes > 0 && (data.words[(data.sigBytes - 1) >>> 2] & 0xff) === 0) {
data.sigBytes--;
}
}
};
Недостаток этой схемы — невозможность различить реальные нулевые байты и добавленные.
ANSI X.923 использует заполнение нулями и хранит длину padding в последнем байте.
const AnsiX923 = {
pad: function (data, blockSize) {
const blockSizeBytes = blockSize * 4;
const padding = blockSizeBytes - (data.sigBytes % blockSizeBytes);
// добавляем нули
for (let i = 0; i < padding - 1; i++) {
data.concat(CryptoJS.lib.WordArray.create([0x00000000], 1));
}
// последний байт — длина padding
data.concat(CryptoJS.lib.WordArray.create([padding], 1));
},
unpad: function (data) {
const padding = data.words[(data.sigBytes - 1) >>> 2] & 0xff;
data.sigBytes -= padding;
}
};
После определения объекта padding его можно зарегистрировать:
CryptoJS.pad.Custom = CustomPadding;
И использовать в параметрах шифрования:
const encrypted = CryptoJS.AES.encrypt("Secret data", "key123", {
padding: CryptoJS.pad.Custom
});
Расшифрование автоматически использует unpad.
При реализации padding важно учитывать особенности
WordArray:
sigBytes определяет реальную длинуsigBytesПрямое изменение массива words без корректировки
sigBytes приводит к повреждению данных.
Наиболее частые проблемы:
1. Несоответствие sigBytes и words Неверное обновление длины приводит к утечке лишних байтов.
2. Неверный расчёт блока CryptoJS использует 32-битные слова, а не байты.
3. Отсутствие обработки полного блока Если длина уже кратна блоку, padding всё равно должен добавляться.
4. Небезопасные схемы Некоторые кастомные padding-подходы могут приводить к уязвимостям при анализе длины сообщений.
const blockSizeBytes = blockSize * 4;
const remainder = data.sigBytes % blockSizeBytes;
const padding = blockSizeBytes - remainder;
Если remainder === 0, padding должен быть равен размеру
блока.
Схема добавляет единичный бит и заполняет нулями:
const BitPadding = {
pad: function (data, blockSize) {
const blockSizeBytes = blockSize * 4;
data.concat(CryptoJS.lib.WordArray.create([0x80000000], 1));
const paddingBytes = blockSizeBytes - (data.sigBytes % blockSizeBytes);
for (let i = 0; i < paddingBytes; i++) {
data.concat(CryptoJS.lib.WordArray.create([0x00000000], 1));
}
},
unpad: function (data) {
let i = data.sigBytes - 1;
while (i >= 0) {
const byte = (data.words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff;
if (byte === 0x80) {
data.sigBytes = i;
break;
}
i--;
}
}
};
Padding используется только в режимах, где требуется заполнение:
В потоковых режимах (CTR, OFB) padding не применяется, так как данные шифруются побайтно.
При использовании AES в CryptoJS порядок операций фиксирован:
Любая кастомная схема должна работать до этапа шифрования и после расшифрования, не вмешиваясь в криптографический процесс.
Если unpad реализован неправильно:
Особенно критично это при работе с бинарными данными, где нет текстовой структуры.
sigBytes