ISO/IEC 9797-1 описывает семейство алгоритмов формирования кода аутентификации сообщения (MAC — Message Authentication Code) на основе блочных шифров. Эти алгоритмы используются для проверки целостности и подлинности данных, передаваемых или хранимых в небезопасной среде.
В отличие от HMAC, который основан на хеш-функциях, ISO9797-1 MAC строится вокруг симметричного шифрования (чаще всего AES или 3DES). В контексте JavaScript и библиотеки CryptoJS реализация таких схем обычно выполняется вручную, так как прямой функции ISO9797-1 MAC в библиотеке нет.
Стандарт определяет несколько вариантов алгоритма (MAC Algorithm 1, 2, 3 и 4). На практике чаще всего используются первые три.
Использует блочный шифр в режиме CBC без дополнительной постобработки.
MAC = E_K(M_n C_{n-1})
Где:
Особенности:
Добавляется дополнительное шифрование финального блока для повышения стойкости.
MAC = E_K(E_K(M_n C_{n-1}))
Отличие:
Использует промежуточное значение CBC, затем применяет дополнительное шифрование и усечение результата.
MAC = Truncate(E_K(C_n))
Где:
Особенности:
Перед обработкой сообщение должно быть выровнено по размеру блока.
Используются варианты:
0x80 и нулей)Наиболее распространён метод 2:
... | DATA | 0x80 | 0x00 | 0x00 ...
CryptoJS не предоставляет готового ISO9797-1 MAC, но позволяет собрать его вручную через AES или 3DES.
import CryptoJS from "crypto-js";
const key = CryptoJS.enc.Hex.parse("00112233445566778899aabbccddeeff");
const message = "Hello ISO9797-1 MAC";
const data = CryptoJS.enc.Utf8.parse(message);
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
});
const mac = encrypted.ciphertext.toString(CryptoJS.enc.Hex);
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;
}
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);
}
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 | HMAC |
|---|---|---|
| Основа | блочный шифр | хеш-функция |
| Алгоритм | AES / 3DES | SHA-256 / SHA-1 |
| Скорость | высокая на аппаратном шифровании | стабильная |
| Безопасность | зависит от режима | математически обоснована |
| Стандарт применения | банковские системы | интернет-протоколы |
ISO9797-1 MAC встречается в:
== вместо constant-time
сравнения