Юнит-тесты для операций шифрования и расшифровки выполняют несколько ключевых задач:
Важно учитывать, что криптографические функции часто включают случайность (например, IV), поэтому тестирование требует либо фиксации параметров, либо проверки свойств, а не точных значений.
Для примеров используется библиотека Crypto-js и тестовый фреймворк (например, Jest или Mocha). Основные сценарии:
const CryptoJS = require('crypto-js');
function encryptAES(plaintext, key) {
return CryptoJS.AES.encrypt(plaintext, key).toString();
}
function decryptAES(ciphertext, key) {
const bytes = CryptoJS.AES.decrypt(ciphertext, key);
return bytes.toString(CryptoJS.enc.Utf8);
}
test('AES: шифрование и расшифровка возвращают исходную строку', () => {
const key = 'secret-key';
const message = 'Hello, Crypto!';
const encrypted = encryptAES(message, key);
const decrypted = decryptAES(encrypted, key);
expect(decrypted).toBe(message);
});
По умолчанию Crypto-js генерирует случайный IV, что делает результат шифрования недетерминированным. Для точного сравнения используется фиксированный IV.
function encryptWithIV(plaintext, key, iv) {
return CryptoJS.AES.encrypt(plaintext, key, { iv }).toString();
}
test('AES: детерминированное шифрование с фиксированным IV', () => {
const key = CryptoJS.enc.Utf8.parse('1234567890123456');
const iv = CryptoJS.enc.Utf8.parse('6543210987654321');
const message = 'Test message';
const encrypted1 = encryptWithIV(message, key, iv);
const encrypted2 = encryptWithIV(message, key, iv);
expect(encrypted1).toBe(encrypted2);
});
test('AES: расшифровка с неверным ключом не совпадает', () => {
const message = 'Sensitive data';
const encrypted = encryptAES(message, 'correct-key');
const decrypted = decryptAES(encrypted, 'wrong-key');
expect(decrypted).not.toBe(message);
});
test('AES: пустая строка', () => {
const encrypted = encryptAES('', 'key');
const decrypted = decryptAES(encrypted, 'key');
expect(decrypted).toBe('');
});
test('AES: длинная строка', () => {
const message = 'A'.repeat(10000);
const encrypted = encryptAES(message, 'key');
const decrypted = decryptAES(encrypted, 'key');
expect(decrypted).toBe(message);
});
Crypto-js работает с различными кодировками, что важно учитывать в тестах.
test('UTF-8 кодировка сохраняется корректно', () => {
const message = 'Привет, мир!';
const encrypted = encryptAES(message, 'key');
const decrypted = decryptAES(encrypted, 'key');
expect(decrypted).toBe(message);
});
Хеш-функции должны быть детерминированными: одинаковый ввод — одинаковый результат.
function hashSHA256(message) {
return CryptoJS.SHA256(message).toString();
}
test('SHA256: одинаковые входные данные дают одинаковый хеш', () => {
const hash1 = hashSHA256('data');
const hash2 = hashSHA256('data');
expect(hash1).toBe(hash2);
});
test('SHA256: разные входные данные дают разные хеши', () => {
const hash1 = hashSHA256('data1');
const hash2 = hashSHA256('data2');
expect(hash1).not.toBe(hash2);
});
function hmacSHA256(message, key) {
return CryptoJS.HmacSHA256(message, key).toString();
}
test('HMAC: одинаковые входные данные и ключ дают одинаковый результат', () => {
const result1 = hmacSHA256('data', 'key');
const result2 = hmacSHA256('data', 'key');
expect(result1).toBe(result2);
});
test('HMAC: изменение данных меняет результат', () => {
const result1 = hmacSHA256('data', 'key');
const result2 = hmacSHA256('data-modified', 'key');
expect(result1).not.toBe(result2);
});
Для тестирования функций с генерацией случайных значений используется подмена генератора.
jest.spyOn(CryptoJS.lib.WordArray, 'random').mockReturnValue(
CryptoJS.enc.Hex.parse('00000000000000000000000000000000')
);
Это позволяет:
test('Результат шифрования является строкой', () => {
const encrypted = encryptAES('data', 'key');
expect(typeof encrypted).toBe('string');
});
test('Расшифровка некорректного ciphertext', () => {
const result = decryptAES('invalid-data', 'key');
expect(result).toBe('');
});
Рекомендуемая структура:
describe('AES Encryption', () => {
test('...', () => {});
});
describe('Hashing', () => {
test('...', () => {});
});
Разделение облегчает:
Ключевые аспекты покрытия:
Инструменты:
Вместо проверки конкретного значения:
decrypt(encrypt(x)) === xhash(x) !== hash(y) при x !== yПример:
test('Свойство обратимости', () => {
const message = 'random text';
const key = 'key';
expect(decryptAES(encryptAES(message, key), key)).toBe(message);
});
Юнит-тесты интегрируются в CI/CD:
Каждый найденный баг сопровождается тестом:
test('Регрессия: некорректная расшифровка UTF-8', () => {
const message = 'тест';
const encrypted = encryptAES(message, 'key');
const decrypted = decryptAES(encrypted, 'key');
expect(decrypted).toBe(message);
});
Комплексный подход к тестированию Crypto-js включает:
Такой подход обеспечивает надежность криптографических операций и снижает риск критических уязвимостей в приложении.