Прогрессивное вычисление HMAC

HMAC (Hash-based Message Authentication Code) строится поверх криптографической хеш-функции и используется для проверки целостности и подлинности сообщений. В библиотеке CryptoJS поддерживаются как одноразовые вычисления HMAC, так и прогрессивный (потоковый) режим, при котором данные поступают частями.

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


Архитектура HMAC в CryptoJS

HMAC основан на двух стадиях хеширования:

  • внутренний хеш: hash(key ⊕ ipad || message)
  • внешний хеш: hash(key ⊕ opad || inner_hash_result)

где:

  • ipad — повторяющийся байт 0x36
  • opad — повторяющийся байт 0x5c
  • — побитовое XOR

CryptoJS реализует эту схему через алгоритмический конструктор CryptoJS.algo.HMAC.


Базовая модель работы HMAC

При однократном вычислении используется простая форма:

const hmac = CryptoJS.HmacSHA256("message", "secret");

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


Прогрессивный режим через HMAC.create

Для потоковой обработки используется создание экземпляра HMAC:

const hmac = CryptoJS.algo.HMAC.create(CryptoJS.algo.SHA256, "secret");

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


Инициализация внутреннего состояния

При вызове create происходит подготовка:

  • нормализация ключа под размер блока хеша
  • создание inner/outer padded ключей
  • инициализация внутреннего состояния SHA-256

Внутренний механизм эквивалентен следующей логике:

key ⊕ ipad → внутренний стартовый блок
key ⊕ opad → внешний блок

Постепенная подача данных через update

Основной метод прогрессивного режима — update.

hmac.update("first chunk");
hmac.update("second chunk");
hmac.update("third chunk");

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

  • добавляет данные в буфер SHA-256
  • обрабатывает полные блоки (64 байта для SHA-256)
  • сохраняет остаток для следующего обновления

Ключевая особенность: состояние хеша не сбрасывается между вызовами.


Внутренний механизм обработки блоков

SHA-256 внутри CryptoJS работает на уровне 512-битных блоков.

При поступлении данных:

  1. данные накапливаются в WordArray

  2. когда размер ≥ 512 бит:

    • запускается compression function
  3. промежуточное состояние сохраняется

Это позволяет обрабатывать поток без хранения полного сообщения.


Завершение вычисления через finalize

После передачи всех частей вызывается:

const result = hmac.finalize();

На этом этапе выполняются:

  • добивание (padding) последнего блока
  • завершение внутреннего SHA-256
  • запуск внешнего хеширования (outer hash)

Результат возвращается как WordArray.


Полный пример потокового HMAC

const hmac = CryptoJS.algo.HMAC.create(CryptoJS.algo.SHA256, "secret-key");

hmac.update("chunk-1");
hmac.update("chunk-2");
hmac.update("chunk-3");

const digest = hmac.finalize();

console.log(digest.toString(CryptoJS.enc.Hex));

Сравнение с одноразовым вычислением

Одноразовый режим:

  • проще
  • быстрее для малых данных
  • требует полного сообщения в памяти

Прогрессивный режим:

  • работает с потоками
  • подходит для файлов и сетевых данных
  • снижает пиковое потребление памяти

Использование с бинарными данными

CryptoJS оперирует объектом WordArray, поэтому данные могут поступать не только строками:

hmac.update(CryptoJS.enc.Hex.parse("deadbeef"));
hmac.update(CryptoJS.lib.WordArray.create([0x12345678]));

Это важно при работе с:

  • бинарными протоколами
  • файлами
  • WebSocket потоками

Особенности ключа в HMAC

Ключ проходит нормализацию:

  • если длина больше блока — хешируется
  • если меньше — дополняется нулями
  • затем применяется XOR с ipad/opad

Это обеспечивает устойчивость к атаке на длину ключа.


Внутренний state machine HMAC

Объект HMAC в CryptoJS можно рассматривать как конечный автомат:

  1. INIT

    • подготовка ключей
  2. INNER UPDATE

    • накопление данных
  3. INNER FINALIZE

    • завершение SHA-256
  4. OUTER HASH

    • финальное хеширование
  5. DONE

    • результат готов

Повторное использование экземпляра

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

Для нового сообщения требуется создание нового экземпляра:

const hmac1 = CryptoJS.algo.HMAC.create(CryptoJS.algo.SHA256, key);
hmac1.update("data").finalize();

const hmac2 = CryptoJS.algo.HMAC.create(CryptoJS.algo.SHA256, key);
hmac2.update("other").finalize();

Потоковая обработка в реальных сценариях

Прогрессивное HMAC используется в:

  • проверке целостности файлов при загрузке
  • потоковой передаче видео и аудио
  • криптографических протоколах поверх TCP/WebSocket
  • обработке больших JSON/логов по частям

Совместимость с другими алгоритмами CryptoJS

HMAC можно строить поверх любых хеш-функций CryptoJS:

CryptoJS.algo.HMAC.create(CryptoJS.algo.MD5, key);
CryptoJS.algo.HMAC.create(CryptoJS.algo.SHA1, key);
CryptoJS.algo.HMAC.create(CryptoJS.algo.SHA256, key);

Выбор алгоритма влияет на:

  • размер выхода
  • криптостойкость
  • скорость вычисления

Представление результата

Результат HMAC — это WordArray, который может быть преобразован:

digest.toString(CryptoJS.enc.Hex);
digest.toString(CryptoJS.enc.Base64);

Внутренние оптимизации CryptoJS

Прогрессивная модель снижает:

  • копирование строк
  • создание промежуточных объектов
  • нагрузку на GC

Особенно заметно при обработке данных > десятков мегабайт.


Работа с частичными блоками

Если входные данные не кратны размеру блока:

  • остаток сохраняется в буфере
  • обрабатывается при следующем update
  • padding применяется только в finalize

Поведение при разных типах входных данных

Тип данных Поведение
строка UTF-8 преобразование
WordArray прямое использование
hex/base64 декодирование через enc

Потоковая модель как основа криптографического дизайна

Прогрессивное вычисление HMAC в CryptoJS демонстрирует классическую потоковую модель:

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

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