Управление состоянием при потоковом шифровании

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

В основе работы лежит объектная модель CryptoJS, где ключевую роль играет WordArray — структура, представляющая данные в виде массива 32-битных слов. Любая операция шифрования или дешифрования работает именно с этим представлением, а не с «сырыми» байтами JavaScript.


При использовании потокового режима шифрования библиотека сохраняет несколько видов состояния:

  • ключевой контекст (key schedule) — развернутый ключ, используемый в каждом раунде алгоритма
  • вектор инициализации (IV) — стартовое значение для режимов CBC, CFB и аналогичных
  • счётчик или состояние цепочки блока — в CTR и потоковых режимах
  • буфер незавершённого блока — данные, не достигшие размера 128 бит (16 байт)
  • состояние паддинга — используется только в блочных режимах с дополнением

Каждый вызов обработки данных изменяет эти структуры, поэтому корректное управление состоянием критично для целостности шифрования.


Создание потокового шифра через низкоуровневый API

CryptoJS предоставляет возможность работать с шифрованием как с состоянием машины, а не как с функцией «вход → выход».

Типичный способ создания потокового шифра:

const key = CryptoJS.enc.Utf8.parse('secret key 123');
const iv = CryptoJS.enc.Utf8.parse('initial vector 123');

const encryptor = CryptoJS.algo.AES.createEncryptor(key, {
    iv: iv,
    mode: CryptoJS.mode.CBC,
    padding: CryptoJS.pad.Pkcs7
});

Объект encryptor сохраняет внутреннее состояние между вызовами process.


Обработка данных по частям

Основной механизм потокового шифрования реализуется через метод process, который принимает очередной фрагмент данных и возвращает зашифрованный результат:

const part1 = encryptor.process('Hello ');
const part2 = encryptor.process('world ');
const part3 = encryptor.process('streaming');

Каждый вызов:

  • дополняет внутренний буфер входных данных
  • обрабатывает полные 16-байтные блоки
  • сохраняет остаток в буфере состояния
  • обновляет IV или счётчик (в зависимости от режима)

Важно, что состояние не сбрасывается между вызовами.


Завершение потока и финализация

Последний этап шифрования выполняется через finalize(). Он обрабатывает остаток данных, применяет padding (если требуется) и завершает поток:

const finalPart = encryptor.finalize();

После вызова finalize объект считается завершённым. Повторное использование без пересоздания приводит к некорректному состоянию.


Управление буфером и выравниванием данных

В потоковом режиме ключевая проблема — работа с неполными блоками.

AES оперирует блоками по 16 байт, поэтому при поступлении данных:

  • если длина кратна 16 байтам — данные обрабатываются сразу
  • если нет — остаток сохраняется в буфере

CryptoJS хранит этот остаток в this._data внутри объекта cipher.

Это означает:

  • нельзя предполагать немедленную обработку всех входных данных
  • результат process может быть пустым при недостаточном размере блока
  • порядок вызовов строго влияет на итоговый поток

Сохранение и восстановление состояния шифрования

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

  • ключ
  • IV (или счётчик)
  • остаток буфера (WordArray)
  • позицию обработки

Однако CryptoJS не предоставляет прямой сериализации состояния cipher-объекта, поэтому восстановление требует ручного контроля:

const state = {
    key,
    iv,
    buffer: encryptor._data
};

При восстановлении поток должен продолжаться с тем же IV и тем же непрерывным входом.


Режимы шифрования и влияние на состояние

Разные режимы AES по-разному управляют состоянием:

CBC (Cipher Block Chaining) Каждый блок зависит от предыдущего зашифрованного блока. Состояние IV постоянно изменяется.

CTR (Counter mode) Использует счётчик, который увеличивается после каждого блока. Состояние определяется текущим значением counter.

CFB (Cipher Feedback) Работает как потоковый режим, где шифрование предыдущего выхода влияет на следующий шаг.

CTR наиболее удобен для потоковой обработки, так как не требует padding и позволяет произвольное разбиение данных.


Типичные ошибки управления состоянием

При работе с потоковым шифрованием в CryptoJS часто возникают следующие проблемы:

Повторное использование cipher-объекта

После finalize() объект нельзя использовать повторно без пересоздания.

Потеря IV

Если IV не сохраняется, расшифрование становится невозможным даже при наличии ключа.

Неправильное разбиение данных

Разделение входа без учёта блоков может привести к смещению состояния в CBC.

Игнорирование остаточного буфера

Если не вызвать finalize, последний блок может быть потерян.


Практическая модель потока данных

Потоковое шифрование в CryptoJS можно представить как конвейер:

  1. Входной поток разбивается на чанки
  2. Каждый чанк попадает в буфер состояния
  3. Полные блоки обрабатываются немедленно
  4. Остаток сохраняется
  5. IV/счётчик обновляется
  6. При завершении применяется финализация

Эта модель делает возможной обработку данных любого размера без предварительной загрузки всего массива в память.


Дешифрование с сохранением состояния

Дешифрование зеркально повторяет процесс шифрования. Важно, чтобы:

  • порядок чанков был идентичен
  • IV совпадал с исходным
  • режим шифрования был тем же
const decryptor = CryptoJS.algo.AES.createDecryptor(key, {
    iv: iv,
    mode: CryptoJS.mode.CBC,
    padding: CryptoJS.pad.Pkcs7
});

const out1 = decryptor.process(cipherPart1);
const out2 = decryptor.process(cipherPart2);
const final = decryptor.finalize();

Любое отклонение в состоянии приводит к невозможности восстановления исходного текста.


Состояние как ключевой элемент безопасности

В потоковом шифровании состояние становится частью криптографической безопасности. Ошибки в его управлении приводят к:

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

Правильное управление состоянием — это не техническая деталь, а обязательное условие корректной криптографии.


Особенности реализации в JavaScript окружении

CryptoJS работает в среде без потоков и без нативных бинарных буферов, поэтому:

  • все данные преобразуются в WordArray
  • состояние хранится в JS-объектах
  • производительность зависит от GC и копирования массивов
  • нет true streaming I/O, только имитация через process

Это накладывает ограничения на высоконагруженные сценарии, где предпочтительнее WebCrypto API или нативные реализации.


Управление жизненным циклом шифра

Корректный жизненный цикл потокового шифрования включает:

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

Любое отклонение от этого цикла приводит к неконсистентности состояния и ошибкам расшифрования.