ISO97971

ISO/IEC 9797-1 описывает семейство алгоритмов формирования кода аутентификации сообщения (MAC — Message Authentication Code) на основе блочных шифров. Эти алгоритмы используются для проверки целостности и подлинности данных, передаваемых или хранимых в небезопасной среде.

В отличие от HMAC, который основан на хеш-функциях, ISO9797-1 MAC строится вокруг симметричного шифрования (чаще всего AES или 3DES). В контексте JavaScript и библиотеки CryptoJS реализация таких схем обычно выполняется вручную, так как прямой функции ISO9797-1 MAC в библиотеке нет.


Общая структура ISO/IEC 9797-1 MAC

Стандарт определяет несколько вариантов алгоритма (MAC Algorithm 1, 2, 3 и 4). На практике чаще всего используются первые три.

Базовый принцип работы

  1. Сообщение разбивается на блоки фиксированного размера (например, 64 или 128 бит).
  2. Используется блочный шифр (AES, DES, 3DES).
  3. Применяется режим цепочки (CBC-like processing).
  4. Финальный блок шифрования формирует MAC-значение.

Основные варианты алгоритма

MAC Algorithm 1 (CBC-MAC классический)

Использует блочный шифр в режиме CBC без дополнительной постобработки.

MAC = E_K(M_n C_{n-1})

Где:

  • (E_K) — шифрование ключом K
  • (M_n) — последний блок сообщения
  • (C_{n-1}) — предыдущий шифроблок

Особенности:

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

MAC Algorithm 2 (с дополнительным шифрованием)

Добавляется дополнительное шифрование финального блока для повышения стойкости.

MAC = E_K(E_K(M_n C_{n-1}))

Отличие:

  • двойное шифрование финального блока
  • повышенная устойчивость к некоторым атакам CBC-MAC

MAC Algorithm 3 (с усечением)

Использует промежуточное значение CBC, затем применяет дополнительное шифрование и усечение результата.

MAC = Truncate(E_K(C_n))

Где:

  • (C_n) — финальный блок CBC-цепочки
  • Truncate — обрезка до нужной длины (например, 64 бита)

Особенности:

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

Паддинг (дополнение данных)

Перед обработкой сообщение должно быть выровнено по размеру блока.

Используются варианты:

  • ISO/IEC 9797-1 Padding Method 1 (нулевые байты)
  • Padding Method 2 (добавление 0x80 и нулей)
  • Padding Method 3 (длина сообщения в конце)

Наиболее распространён метод 2:

... | DATA | 0x80 | 0x00 | 0x00 ...

Реализация ISO9797-1 MAC в CryptoJS

CryptoJS не предоставляет готового ISO9797-1 MAC, но позволяет собрать его вручную через AES или 3DES.

Подключение библиотеки

import CryptoJS from "crypto-js";

Реализация CBC-MAC (база ISO9797-1 Algorithm 1)

Шаг 1: подготовка данных

const key = CryptoJS.enc.Hex.parse("00112233445566778899aabbccddeeff");
const message = "Hello ISO9797-1 MAC";

const data = CryptoJS.enc.Utf8.parse(message);

Шаг 2: разбиение и шифрование

CryptoJS не даёт CBC-MAC напрямую, поэтому используется AES-CBC с нулевым IV.

const iv = CryptoJS.enc.Hex.parse("0000000000000000");

const encrypted = CryptoJS.AES.encrypt(data, key, {
  iv: iv,
  mode: CryptoJS.mode.CBC,
  padding: CryptoJS.pad.NoPadding
});

Шаг 3: извлечение MAC

const mac = encrypted.ciphertext.toString(CryptoJS.enc.Hex);

Корректная реализация ISO9797-1 MAC (с паддингом)

Метод 2 паддинга

function iso9797Pad(data) {
  const blockSize = 16;
  const padded = CryptoJS.lib.WordArray.create(data.words.slice());

  padded.concat(CryptoJS.enc.Hex.parse("80"));

  while (padded.sigBytes % blockSize !== 0) {
    padded.concat(CryptoJS.enc.Hex.parse("00"));
  }

  return padded;
}

Полная MAC-функция

function iso9797Mac(message, key) {
  const iv = CryptoJS.enc.Hex.parse("0000000000000000");
  let data = CryptoJS.enc.Utf8.parse(message);

  data = iso9797Pad(data);

  const encrypted = CryptoJS.AES.encrypt(data, key, {
    iv: iv,
    mode: CryptoJS.mode.CBC,
    padding: CryptoJS.pad.NoPadding
  });

  const ciphertext = encrypted.ciphertext;

  const lastBlock = ciphertext.words.slice(-4);

  const macWordArray = CryptoJS.lib.WordArray.create(lastBlock);

  return macWordArray.toString(CryptoJS.enc.Hex);
}

Использование 3DES вместо AES

ISO9797-1 исторически часто применяется с 3DES.

const key = CryptoJS.enc.Hex.parse("0123456789ABCDEF0123456789ABCDEF");

const encrypted = CryptoJS.TripleDES.encrypt(data, key, {
  mode: CryptoJS.mode.CBC,
  iv: iv,
  padding: CryptoJS.pad.NoPadding
});

Особенности безопасности ISO9797-1 MAC

Критические моменты:

  • CBC-MAC без модификаций небезопасен при переменной длине сообщений
  • необходимо строго контролировать паддинг
  • ключ должен быть уникальным для MAC-операций
  • предпочтительно использовать усечённые MAC (8–16 байт)

Отличия от HMAC

Характеристика ISO9797-1 MAC HMAC
Основа блочный шифр хеш-функция
Алгоритм AES / 3DES SHA-256 / SHA-1
Скорость высокая на аппаратном шифровании стабильная
Безопасность зависит от режима математически обоснована
Стандарт применения банковские системы интернет-протоколы

Применение в реальных системах

ISO9797-1 MAC встречается в:

  • банковских EMV-картах
  • терминалах оплаты
  • смарт-картах
  • защищённых API протоколах
  • legacy криптосистемах

Практические замечания при работе с CryptoJS

  • WordArray требует внимательной работы с байтами
  • неправильный padding ломает совместимость с другими реализациями
  • результаты MAC могут отличаться между библиотеками из-за endian-особенностей
  • всегда фиксируется длина выходного MAC

Типичные ошибки реализации

  • использование случайного IV (в CBC-MAC IV должен быть нулевым)
  • применение CBC-MAC к сообщениям переменной длины без модификации алгоритма
  • отсутствие паддинга или некорректное выравнивание
  • сравнение MAC через обычное == вместо constant-time сравнения