Передача IV вместе с шифротекстом

Симметричные алгоритмы шифрования в режиме CBC, CFB или CTR требуют использования вектора инициализации. В CryptoJS этот параметр задаётся явно и при этом остаётся критически важным для корректного и безопасного расшифрования. Основная проблема практической реализации заключается не в генерации IV, а в его корректной передаче вместе с шифротекстом.

Вектор инициализации обеспечивает случайность первого блока шифрования. При одинаковом открытом тексте и одинаковом ключе без IV результат шифрования будет полностью идентичным, что делает систему уязвимой к анализу повторов.

y = E_{K}(P IV)

В режиме CBC каждый блок шифротекста зависит от предыдущего, а первый — от IV. Поэтому повторное использование IV с тем же ключом приводит к утечке структуры данных.

Ключевые свойства IV:

  • не обязан быть секретным
  • должен быть уникальным для каждой операции шифрования
  • должен иметь фиксированную длину (для AES — 16 байт)

Особенности CryptoJS при работе с IV

В CryptoJS IV не добавляется автоматически к результату шифрования при использовании низкоуровневого API CryptoJS.AES.encrypt с явной передачей параметров. Результат операции содержит только шифротекст, тогда как IV остаётся отдельным объектом.

const CryptoJS = require("crypto-js");

const key = CryptoJS.enc.Utf8.parse("1234567890123456");
const iv = CryptoJS.lib.WordArray.random(16);

const encrypted = CryptoJS.AES.encrypt("секретное сообщение", key, {
  iv: iv,
  mode: CryptoJS.mode.CBC,
  padding: CryptoJS.pad.Pkcs7
});

console.log(encrypted.toString());
console.log(iv.toString());

В данном случае шифротекст и IV существуют как два независимых значения, что создаёт задачу их совместной упаковки.

Проблема передачи IV

Шифротекст без IV невозможно корректно расшифровать. Однако IV не является секретом, поэтому его допустимо передавать вместе с данными. Основная задача заключается в выборе структуры упаковки.

Существуют несколько распространённых подходов:

1. Конкатенация IV и шифротекста

IV добавляется в начало или конец итоговой строки после кодирования.

const ivBase64 = CryptoJS.enc.Base64.stringify(iv);
const ciphertextBase64 = encrypted.toString();

const payload = ivBase64 + ":" + ciphertextBase64;

При расшифровке строка разделяется на две части, после чего IV восстанавливается:

const parts = payload.split(":");

const iv = CryptoJS.enc.Base64.parse(parts[0]);
const ciphertext = parts[1];

const decrypted = CryptoJS.AES.decrypt(ciphertext, key, {
  iv: iv,
  mode: CryptoJS.mode.CBC,
  padding: CryptoJS.pad.Pkcs7
});

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

2. Упаковка в JSON

Более структурированный вариант — использование JSON-объекта.

const payload = JSON.stringify({
  iv: CryptoJS.enc.Base64.stringify(iv),
  data: encrypted.toString()
});

Расшифрование:

const obj = JSON.parse(payload);

const iv = CryptoJS.enc.Base64.parse(obj.iv);
const ciphertext = obj.data;

const decrypted = CryptoJS.AES.decrypt(ciphertext, key, {
  iv: iv,
  mode: CryptoJS.mode.CBC,
  padding: CryptoJS.pad.Pkcs7
});

Данный подход обеспечивает расширяемость структуры (например, добавление версии протокола или алгоритма).

3. Префиксация бинарных данных

В некоторых реализациях используется объединение в бинарном виде: IV + ciphertext → единый WordArray → Base64.

const combined = iv.clone().concat(encrypted.ciphertext);

const result = CryptoJS.enc.Base64.stringify(combined);

При расшифровке:

const raw = CryptoJS.enc.Base64.parse(result);

const iv = CryptoJS.lib.WordArray.create(raw.words.slice(0, 4), 16);
const ciphertext = CryptoJS.lib.WordArray.create(raw.words.slice(4));

Такой способ минимизирует накладные расходы, но требует точного контроля длины IV.

Важность уникальности IV

Повторное использование IV с одним и тем же ключом приводит к криптографической уязвимости. В режиме CBC это позволяет вычислять XOR между двумя открытыми текстами:

C_1 C_2 = (P_1 IV) (P_2 IV)

IV сокращается, и структура сообщений становится частично восстановимой.

Поэтому стандартная практика:

  • генерация IV через CryptoJS.lib.WordArray.random(16)
  • запрет повторного использования IV при одном ключе
  • хранение IV вместе с шифротекстом, но не отдельно от него

Передача IV в формате Base64 и Hex

CryptoJS оперирует объектом WordArray, поэтому преобразование в строковые форматы обязательно.

Base64:

CryptoJS.enc.Base64.stringify(iv)

Hex:

CryptoJS.enc.Hex.stringify(iv)

Base64 используется чаще из-за меньшего размера и совместимости с JSON и HTTP.

Особенности режима AES-GCM

В отличие от CBC, режим GCM включает аутентификацию данных и использует nonce вместо классического IV. Однако в CryptoJS поддержка GCM ограничена, поэтому часто используется CBC с внешней реализацией контроля целостности.

В системах, где GCM доступен, структура обычно включает:

  • nonce (аналог IV)
  • ciphertext
  • authentication tag

Все три компонента передаются вместе, аналогично IV в CBC.

Ошибки при передаче IV

Типичные проблемы при реализации:

  • повторное использование IV при одном ключе
  • потеря IV при сериализации
  • неверное кодирование (UTF-8 вместо Base64/Hex)
  • несогласованная длина IV при декодировании
  • попытка хранить IV внутри строки шифротекста без явного формата

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

Практическая структура защищённого сообщения

В реальных системах чаще всего применяется комбинированный формат:

{
  "v": 1,
  "iv": "Base64",
  "ct": "Base64"
}

Такой подход позволяет:

  • версионировать формат
  • заменять алгоритмы шифрования
  • сохранять совместимость при обновлениях

CryptoJS остаётся на уровне криптографического примитива, поэтому вся логика упаковки данных реализуется на уровне приложения, а не библиотеки.

Итоговые принципы работы с IV

  • IV генерируется случайно для каждого шифрования
  • IV не является секретом, но является обязательным для расшифрования
  • IV всегда передаётся вместе с шифротекстом
  • формат передачи выбирается на уровне протокола приложения
  • повторное использование IV недопустимо при одном ключе
  • корректная сериализация определяет надёжность всей схемы шифрования