Тестовые векторы и эталонные значения

Тестовые векторы представляют собой заранее определённые наборы входных данных и соответствующих им выходных значений, которые используются для проверки корректности реализации криптографических алгоритмов. В контексте JavaScript-библиотеки CryptoJS они выполняют критически важную функцию: позволяют убедиться, что реализация шифрования, хэширования и вычисления HMAC совпадает с эталонными результатами, опубликованными в спецификациях алгоритмов.

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


Источники эталонных значений

Эталонные значения формируются из официальных стандартов:

  • NIST (например, FIPS-197 для AES)
  • RFC-спецификации (например, RFC 2104 для HMAC)
  • Документы ISO/IEC
  • Reference implementations (OpenSSL, Bouncy Castle)

Каждый тестовый вектор обычно включает:

  • исходный текст (plaintext)
  • ключ (key)
  • вектор инициализации (IV), если применимо
  • режим работы алгоритма
  • ожидаемый результат (ciphertext, hash, mac)

Базовая структура тестового вектора

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

plaintext:  "HelloCrypto"
key:        "00112233445566778899aabbccddeeff"
iv:         "0102030405060708"
ciphertext: "..."

Для хэш-функций структура проще:

input:      "message"
sha256:     "2c26b46b68ffc68ff99b453c1d304134..."

Проверка хэш-функций в CryptoJS

CryptoJS предоставляет API для вычисления хэшей через модуль CryptoJS.SHA256, CryptoJS.SHA1, CryptoJS.MD5 и другие.

Пример использования тестового вектора для SHA-256:

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

const input = "message";
const expected = "ab530a13e45914982b79f9b7e3fba994c2a8f2c4...";

const hash = CryptoJS.SHA256(input).toString();

if (hash === expected) {
    console.log("SHA-256 соответствует тестовому вектору");
} else {
    console.log("Несовпадение результата");
}

Особое внимание требуется уделять кодировке входных данных. CryptoJS по умолчанию интерпретирует строки как UTF-8. Несоответствие кодировки — одна из наиболее частых причин расхождения с эталонными значениями.


Тестовые векторы для AES

AES является наиболее часто проверяемым алгоритмом. Векторы для AES включают разные режимы: ECB, CBC, CFB, OFB.

Пример для AES-CBC:

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

const key = CryptoJS.enc.Hex.parse("000102030405060708090a0b0c0d0e0f");
const iv = CryptoJS.enc.Hex.parse("101112131415161718191a1b1c1d1e1f");
const plaintext = "CryptoJS test vector";

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

Эталонные значения AES зависят от:

  • режима шифрования
  • режима паддинга
  • формата ключа (WordArray vs строка)
  • способа кодирования результата (Base64, Hex)

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

Несовпадения часто связаны не с алгоритмом, а с окружением выполнения:

1. Неверная кодировка ключа

CryptoJS.AES.encrypt("data", "00112233")

Строковый ключ интерпретируется как UTF-8, а не как hex. Эталонные векторы обычно используют бинарное представление.

Правильный вариант:

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

2. Отсутствие явного IV

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


3. Различия в padding

CryptoJS по умолчанию использует PKCS7:

padding: CryptoJS.pad.Pkcs7

Но некоторые тестовые векторы используют no-padding или ANSI X.923.


4. Формат вывода

Результат CryptoJS по умолчанию — Base64:

encrypted.toString()

Для сравнения с hex-эталонами необходимо:

encrypted.ciphertext.toString(CryptoJS.enc.Hex);

HMAC и эталонные значения

HMAC часто используется для проверки целостности сообщений. Тестовые векторы RFC 2104 включают различные ключи и сообщения.

Пример HMAC-SHA256:

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

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

const hmac = CryptoJS.HmacSHA256(message, key).toString();

При сравнении с эталоном необходимо учитывать:

  • тип хэш-функции (SHA1, SHA256, SHA512)
  • формат ключа
  • порядок байтов

PBKDF2 и тестовые векторы

PBKDF2 используется для получения ключей из паролей. Тестовые векторы RFC 6070 включают параметры:

  • password
  • salt
  • iterations
  • dkLen

Пример:

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

const key = CryptoJS.PBKDF2("password", "salt", {
    keySize: 256 / 32,
    iterations: 1000
}).toString();

Расхождения с эталоном чаще всего возникают из-за:

  • неправильного количества итераций
  • различий в keySize (в словах, а не в байтах)
  • неверной интерпретации salt

Приведение данных к WordArray

CryptoJS оперирует внутренним типом WordArray. Многие тестовые векторы требуют явного преобразования:

const key = CryptoJS.enc.Hex.parse("0f0e0d0c0b0a09080706050403020100");
const data = CryptoJS.enc.Utf8.parse("test");

Без этого преобразования результаты не совпадают с эталонными реализациями OpenSSL или NIST test suite.


Интероперабельность с другими реализациями

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

  • OpenSSL
  • Java javax.crypto
  • .NET System.Security.Cryptography
  • Python cryptography

Пример расхождения:

  • OpenSSL возвращает hex
  • CryptoJS возвращает Base64
  • Java использует Big-Endian представление блоков

Для корректного сравнения необходимо унифицировать представление данных.


Работа с многочастными тестовыми векторами

Некоторые стандарты предоставляют наборы последовательных тестов:

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

Пример генерации серии проверок:

for (let i = 0; i < 10; i++) {
    const input = "A".repeat(i + 1);
    const hash = CryptoJS.SHA256(input).toString();
    console.log(i + 1, hash);
}

Влияние внутреннего представления байтов

CryptoJS использует 32-битные слова (WordArray), что влияет на:

  • порядок байтов (endianness)
  • упаковку строк в блоки
  • интерпретацию hex-строк

При сравнении с тестовыми векторами важно учитывать, что многие эталонные реализации оперируют байтовыми массивами напрямую.


Практическая схема верификации реализации

При разработке криптографического слоя на CryptoJS тестовые векторы используются как фиксированная база:

  1. Подготовка входных данных в точном формате стандарта
  2. Явное преобразование через CryptoJS.enc.*
  3. Вычисление результата
  4. Сравнение с эталонным значением
  5. Проверка чувствительности к изменениям одного параметра

Причины расхождения с эталонами при корректной реализации

Даже при правильной реализации алгоритма возможны расхождения:

  • неверная интерпретация строк (UTF-8 vs ASCII)
  • различие в обработке нулевых байтов
  • автоматическое добавление padding
  • отличие в сериализации результата (hex/base64/raw)
  • использование браузерного и Node.js окружения с разной кодировкой

Значение тестовых векторов в долгосрочной поддержке кода

При обновлении CryptoJS или миграции между версиями библиотек тестовые векторы выступают гарантом неизменности поведения. Они позволяют:

  • выявлять регрессии
  • проверять совместимость после рефакторинга
  • контролировать корректность кастомных расширений алгоритмов
  • подтверждать соответствие стандартам после оптимизаций

Систематизация набора тестов

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

  • базовые векторы (официальные RFC/NIST)
  • краевые случаи (пустые строки, длинные сообщения)
  • случайные векторы (fuzz testing)
  • кросс-реализационные проверки

Такая организация позволяет поддерживать предсказуемость криптографического слоя при любых изменениях реализации CryptoJS-кода.