Files
PotatoPassCore/test/potato_pass_core_test.dart
2026-08-09 23:47:39 +03:00

331 lines
15 KiB
Dart
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'dart:typed_data';
import 'package:potato_pass_core/potato_pass_core.dart';
import 'package:cryptography/cryptography.dart';
import 'package:test/test.dart';
void main() {
group('6-и битный колдировщик', () {
test('Кодирование и декодирование русской строки', () {
const input = 'ИВАНОВ ИВАН ИВАНОВИЧ';
// Кодируем
final Uint8List encoded = CompactTextEncoder.encode(input);
// Проверяем декодирование
final String decoded = CompactTextEncoder.decode(encoded);
expect(decoded, equals(input));
});
test('Проверка сжатия размера', () {
const input = 'ИВАНОВ ИВАН'; // 11 символов
// В UTF-8 кириллица весит 2 байта на символ и займут 22 байта
// В 6-битном кодировщике 11 символов округляются до 12 и займут 9 байт
final Uint8List encoded = CompactTextEncoder.encode(input);
expect(encoded.length, equals(9));
});
test('Обработка строчных букв (автоперевод в верхний регистр)', () {
const input = 'Петров Петр';
final Uint8List encoded = CompactTextEncoder.encode(input);
final String decoded = CompactTextEncoder.decode(encoded);
expect(decoded, equals('ПЕТРОВ ПЕТР'));
});
test('Тест некодируемых символов', () {
const input = '_.`~!@#\$%^&*()=+\\||//1234567890.';//2 точки и 29 некодирукемый символ (точки чтобы не обрезало) и _ в начале которе
final Uint8List encoded = CompactTextEncoder.encode(input);
final String decoded = CompactTextEncoder.decode(encoded);
expect(decoded, equals('. .'));//2 точки и 29 пробелов (некодируемые символы заменяются на пробелы)
});
test('Тест кодируемости всех символов', () {
const input = 'АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯABCDEFGHIJKLMNOPQRSTUVWXYZ .-,';//Все неслужебные символы
final Uint8List encoded = CompactTextEncoder.encode(input);
final String decoded = CompactTextEncoder.decode(encoded);
expect(decoded, equals('АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯABCDEFGHIJKLMNOPQRSTUVWXYZ .-,'));
});
});
group('Тест полного цикла', () {
test('Нормальные данные', () async {
// 1. Создаем ключи СБ и Клиента
final SimpleKeyPair sbKeyPair = await CryptoService.generateKeyPair();
final Uint8List sbPubKey = await CryptoService.getPublicKeyBytes(sbKeyPair);
final SimpleKeyPair clientKeyPair = await CryptoService.generateKeyPair();
final Uint8List clientPubKey = await CryptoService.getPublicKeyBytes(clientKeyPair);
// 2. СБ создает маску зон доступа (разрешаем зоны 1, 5, 10)
final Uint8List zoneMask = PassPayload.createZoneMask([1, 5, 10]);
// 3. Формируем тело пропуска для СБ
final passWithoutSignatures = PassPayload(
lastName: 'Иванов',
firstName: 'Иван',
middleName: 'Иванович',
position: 'Инженер',
department: 'Отдел ИТ',
zoneMask: zoneMask,
clientPublicKey: clientPubKey,
securityDepartmentSignature: Uint8List(64),
challengeSignature: Uint8List(64),
);
// 4. Служба Безопасности подписывает тело пропуска
final Uint8List sbSignedBodyBytes = passWithoutSignatures.getSignedBodyBytes();
final Uint8List securityDepartmentSignature = await CryptoService.sign(
message: sbSignedBodyBytes,
keyPair: sbKeyPair,
);
// 5. Контролер/Сервер дает соль
final Uint8List challengeSalt = SaltGenerator.generate(16);
// 6. Клиент подписывает (Соль + Подпись СБ) своим приватным ключом
final List<int> challengeMessage = [...challengeSalt, ...securityDepartmentSignature];
final Uint8List challengeSignature = await CryptoService.sign(
message: challengeMessage,
keyPair: clientKeyPair,
);
// 7. Собираем итоговый рабочий пропуск
final PassPayload fullPass = PassPayload(
lastName: 'Иванов',
firstName: 'Иван',
middleName: 'Иванович',
position: 'Инженер',
department: 'Отдел ИТ',
zoneMask: zoneMask,
clientPublicKey: clientPubKey,
securityDepartmentSignature: securityDepartmentSignature,
challengeSignature: challengeSignature,
);
// 8. СЕРИАЛИЗАЦИЯ В БИТЫ
final Uint8List binaryPayload = BinarySerializer.serialize(fullPass);
// 9. ДЕСЕРИАЛИЗАЦИЯ ИЗ БИТОВ
final PassPayload restoredPass = BinarySerializer.deserialize(binaryPayload);
// --- ПРОВЕРКА ДАННЫХ ---
expect(restoredPass.lastName, equals('ИВАНОВ'));
expect(restoredPass.firstName, equals('ИВАН'));
expect(restoredPass.position, equals('ИНЖЕНЕР'));
expect(restoredPass.department, equals('ОТДЕЛ ИТ'));
// --- ПРОВЕРКА ЗОН ДОСТУПА ---
expect(restoredPass.hasZoneAccess(1), isTrue);
expect(restoredPass.hasZoneAccess(5), isTrue);
expect(restoredPass.hasZoneAccess(10), isTrue);
expect(restoredPass.hasZoneAccess(2), isFalse);
// --- ПРОВЕРКА КРИПТОГРАФИИ (1): ВАЛИДНОСТЬ ПОДПИСИ СЛУЖБЫ БЕЗОПАСНОСТИ ---
final bool isSecuritySignatureValid = await CryptoService.verify(
message: restoredPass.getSignedBodyBytes(),
signatureBytes: restoredPass.securityDepartmentSignature,
publicKeyBytes: sbPubKey,
);
expect(isSecuritySignatureValid, isTrue);
// --- ПРОВЕРКА КРИПТОГРАФИИ (2): ВАЛИДНОСТЬ ПОДПИСИ СОЛИ КЛИЕНТОМ ---
final List<int> restoredChallengeMessage = [...challengeSalt, ...restoredPass.securityDepartmentSignature];
final bool isClientSignatureValid = await CryptoService.verify(
message: restoredChallengeMessage,
signatureBytes: restoredPass.challengeSignature,
publicKeyBytes: restoredPass.clientPublicKey,
);
expect(isClientSignatureValid, isTrue);
});
test('Пустые строки', () async {
// 1. Создаем ключи СБ и Клиента
final SimpleKeyPair sbKeyPair = await CryptoService.generateKeyPair();
final Uint8List sbPubKey = await CryptoService.getPublicKeyBytes(sbKeyPair);
final SimpleKeyPair clientKeyPair = await CryptoService.generateKeyPair();
final Uint8List clientPubKey = await CryptoService.getPublicKeyBytes(clientKeyPair);
// 2. СБ создает маску зон доступа (разрешаем зоны 1, 5, 10)
final Uint8List zoneMask = PassPayload.createZoneMask([1, 5, 10]);
// 3. Формируем тело пропуска для СБ
final passWithoutSignatures = PassPayload(
lastName: '',
firstName: '',
middleName: '',
position: '',
department: '',
zoneMask: zoneMask,
clientPublicKey: clientPubKey,
securityDepartmentSignature: Uint8List(64),
challengeSignature: Uint8List(64),
);
// 4. Служба Безопасности подписывает тело пропуска
final Uint8List sbSignedBodyBytes = passWithoutSignatures.getSignedBodyBytes();
final Uint8List securityDepartmentSignature = await CryptoService.sign(
message: sbSignedBodyBytes,
keyPair: sbKeyPair,
);
// 5. Контролер/Сервер дает соль
final Uint8List challengeSalt = SaltGenerator.generate(16);
// 6. Клиент подписывает (Соль + Подпись СБ) своим приватным ключом
final List<int> challengeMessage = [...challengeSalt, ...securityDepartmentSignature];
final Uint8List challengeSignature = await CryptoService.sign(
message: challengeMessage,
keyPair: clientKeyPair,
);
// 7. Собираем итоговый рабочий пропуск
final PassPayload fullPass = PassPayload(
lastName: '',
firstName: '',
middleName: '',
position: '',
department: '',
zoneMask: zoneMask,
clientPublicKey: clientPubKey,
securityDepartmentSignature: securityDepartmentSignature,
challengeSignature: challengeSignature,
);
// 8. СЕРИАЛИЗАЦИЯ В БИТЫ
final Uint8List binaryPayload = BinarySerializer.serialize(fullPass);
// 9. ДЕСЕРИАЛИЗАЦИЯ ИЗ БИТОВ
final PassPayload restoredPass = BinarySerializer.deserialize(binaryPayload);
// --- ПРОВЕРКА ДАННЫХ ---
expect(restoredPass.lastName, equals(''));
expect(restoredPass.firstName, equals(''));
expect(restoredPass.position, equals(''));
expect(restoredPass.department, equals(''));
// --- ПРОВЕРКА ЗОН ДОСТУПА ---
expect(restoredPass.hasZoneAccess(1), isTrue);
expect(restoredPass.hasZoneAccess(5), isTrue);
expect(restoredPass.hasZoneAccess(10), isTrue);
expect(restoredPass.hasZoneAccess(2), isFalse);
// --- ПРОВЕРКА КРИПТОГРАФИИ (1): ВАЛИДНОСТЬ ПОДПИСИ СЛУЖБЫ БЕЗОПАСНОСТИ ---
final bool isSecuritySignatureValid = await CryptoService.verify(
message: restoredPass.getSignedBodyBytes(),
signatureBytes: restoredPass.securityDepartmentSignature,
publicKeyBytes: sbPubKey,
);
expect(isSecuritySignatureValid, isTrue);
// --- ПРОВЕРКА КРИПТОГРАФИИ (2): ВАЛИДНОСТЬ ПОДПИСИ СОЛИ КЛИЕНТОМ ---
final List<int> restoredChallengeMessage = [...challengeSalt, ...restoredPass.securityDepartmentSignature];
final bool isClientSignatureValid = await CryptoService.verify(
message: restoredChallengeMessage,
signatureBytes: restoredPass.challengeSignature,
publicKeyBytes: restoredPass.clientPublicKey,
);
expect(isClientSignatureValid, isTrue);
});
test('Пустые зоны', () async {
// 1. Создаем ключи СБ и Клиента
final SimpleKeyPair sbKeyPair = await CryptoService.generateKeyPair();
final Uint8List sbPubKey = await CryptoService.getPublicKeyBytes(sbKeyPair);
final SimpleKeyPair clientKeyPair = await CryptoService.generateKeyPair();
final Uint8List clientPubKey = await CryptoService.getPublicKeyBytes(clientKeyPair);
// 2. Заполняем пустые зоны
final Uint8List zoneMask = PassPayload.createZoneMask([]);
// 3. Формируем тело пропуска для СБ
final passWithoutSignatures = PassPayload(
lastName: 'Иванов',
firstName: 'Иван',
middleName: 'Иванович',
position: 'Инженер',
department: 'Отдел ИТ',
zoneMask: zoneMask,
clientPublicKey: clientPubKey,
securityDepartmentSignature: Uint8List(64),
challengeSignature: Uint8List(64),
);
// 4. Служба Безопасности подписывает тело пропуска
final Uint8List sbSignedBodyBytes = passWithoutSignatures.getSignedBodyBytes();
final Uint8List securityDepartmentSignature = await CryptoService.sign(
message: sbSignedBodyBytes,
keyPair: sbKeyPair,
);
// 5. Контролер/Сервер дает соль
final Uint8List challengeSalt = SaltGenerator.generate(16);
// 6. Клиент подписывает (Соль + Подпись СБ) своим приватным ключом
final List<int> challengeMessage = [...challengeSalt, ...securityDepartmentSignature];
final Uint8List challengeSignature = await CryptoService.sign(
message: challengeMessage,
keyPair: clientKeyPair,
);
// 7. Собираем итоговый рабочий пропуск
final PassPayload fullPass = PassPayload(
lastName: 'Иванов',
firstName: 'Иван',
middleName: 'Иванович',
position: 'Инженер',
department: 'Отдел ИТ',
zoneMask: zoneMask,
clientPublicKey: clientPubKey,
securityDepartmentSignature: securityDepartmentSignature,
challengeSignature: challengeSignature,
);
// 8. СЕРИАЛИЗАЦИЯ В БИТЫ
final Uint8List binaryPayload = BinarySerializer.serialize(fullPass);
// 9. ДЕСЕРИАЛИЗАЦИЯ ИЗ БИТОВ
final PassPayload restoredPass = BinarySerializer.deserialize(binaryPayload);
// --- ПРОВЕРКА ДАННЫХ ---
expect(restoredPass.lastName, equals('ИВАНОВ'));
expect(restoredPass.firstName, equals('ИВАН'));
expect(restoredPass.position, equals('ИНЖЕНЕР'));
expect(restoredPass.department, equals('ОТДЕЛ ИТ'));
// --- ПРОВЕРКА ЗОН ДОСТУПА ---
expect(restoredPass.hasZoneAccess(1), isFalse);
expect(restoredPass.hasZoneAccess(2), isFalse);
expect(restoredPass.hasZoneAccess(3), isFalse);
expect(restoredPass.hasZoneAccess(4), isFalse);
expect(restoredPass.hasZoneAccess(5), isFalse);
expect(restoredPass.hasZoneAccess(6), isFalse);
expect(restoredPass.hasZoneAccess(7), isFalse);
// --- ПРОВЕРКА КРИПТОГРАФИИ (1): ВАЛИДНОСТЬ ПОДПИСИ СЛУЖБЫ БЕЗОПАСНОСТИ ---
final bool isSecuritySignatureValid = await CryptoService.verify(
message: restoredPass.getSignedBodyBytes(),
signatureBytes: restoredPass.securityDepartmentSignature,
publicKeyBytes: sbPubKey,
);
expect(isSecuritySignatureValid, isTrue);
// --- ПРОВЕРКА КРИПТОГРАФИИ (2): ВАЛИДНОСТЬ ПОДПИСИ СОЛИ КЛИЕНТОМ ---
final List<int> restoredChallengeMessage = [...challengeSalt, ...restoredPass.securityDepartmentSignature];
final bool isClientSignatureValid = await CryptoService.verify(
message: restoredChallengeMessage,
signatureBytes: restoredPass.challengeSignature,
publicKeyBytes: restoredPass.clientPublicKey,
);
expect(isClientSignatureValid, isTrue);
});
});
}