ver 0.0.1
This commit is contained in:
@@ -1,4 +1,7 @@
|
||||
library;
|
||||
|
||||
// Экспортируем наш 6-битный кодировщик
|
||||
export 'src/encoding/compact_text_encoder.dart';
|
||||
export 'src/encoding/compact_text_encoder.dart'; //Кодировщик
|
||||
export 'src/crypto/salt_generator.dart'; //Генератор солей
|
||||
export 'src/crypto/crypto_service.dart'; //Создание и проверка подписей
|
||||
export 'src/models/pass_payload.dart'; //Модель данных пропуска
|
||||
export 'src/serializer/binary_serializer.dart'; //Упаковщик данных в модель и распаковщик данных из нее
|
||||
58
lib/src/crypto/crypto_service.dart
Normal file
58
lib/src/crypto/crypto_service.dart
Normal file
@@ -0,0 +1,58 @@
|
||||
import 'dart:typed_data';
|
||||
import 'package:cryptography/cryptography.dart';
|
||||
|
||||
/// Сервис асимметричной криптографии Ed25519
|
||||
class CryptoService {
|
||||
static final Ed25519 _algorithm = Ed25519();
|
||||
|
||||
/// Генерация новой пары ключей
|
||||
static Future<SimpleKeyPair> generateKeyPair() async {
|
||||
return await _algorithm.newKeyPair();
|
||||
}
|
||||
|
||||
/// Получение сырых 32 байт публичного ключа из объекта SimpleKeyPair
|
||||
static Future<Uint8List> getPublicKeyBytes(SimpleKeyPair keyPair) async {
|
||||
|
||||
final SimplePublicKey publicKey = await keyPair.extractPublicKey();
|
||||
return Uint8List.fromList(publicKey.bytes);
|
||||
}
|
||||
|
||||
/// Создание цифровой подписи (64 байта)
|
||||
static Future<Uint8List> sign({
|
||||
required List<int> message,
|
||||
required SimpleKeyPair keyPair,
|
||||
}) async {
|
||||
final Signature signature = await _algorithm.sign(
|
||||
message,
|
||||
keyPair: keyPair,
|
||||
);
|
||||
return Uint8List.fromList(signature.bytes);
|
||||
}
|
||||
|
||||
/// Проверка цифровой подписи [signatureBytes] (64 байта)
|
||||
/// для сообщения [message] с помощью публичного ключа [publicKeyBytes] (32 байта)
|
||||
static Future<bool> verify({
|
||||
required List<int> message,
|
||||
required List<int> signatureBytes,
|
||||
required List<int> publicKeyBytes,
|
||||
}) async {
|
||||
if (signatureBytes.length != 64 || publicKeyBytes.length != 32) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final SimplePublicKey publicKey = SimplePublicKey(
|
||||
publicKeyBytes,
|
||||
type: KeyPairType.ed25519,
|
||||
);
|
||||
|
||||
final Signature signature = Signature(
|
||||
signatureBytes,
|
||||
publicKey: publicKey,
|
||||
);
|
||||
|
||||
return await _algorithm.verify(
|
||||
message,
|
||||
signature: signature,
|
||||
);
|
||||
}
|
||||
}
|
||||
20
lib/src/crypto/salt_generator.dart
Normal file
20
lib/src/crypto/salt_generator.dart
Normal file
@@ -0,0 +1,20 @@
|
||||
import 'dart:math';
|
||||
import 'dart:typed_data';
|
||||
|
||||
|
||||
abstract class SaltGenerator {
|
||||
// Стандартный размер соли: 16 байт (128 бит)
|
||||
static const int defaultSize = 16;
|
||||
|
||||
// Генерирует случайный массив байт указанной длины
|
||||
static Uint8List generate([int length = defaultSize]) {
|
||||
final Random secureRandom = Random.secure();
|
||||
final Uint8List salt = Uint8List(length);
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
salt[i] = secureRandom.nextInt(256);
|
||||
}
|
||||
|
||||
return salt;
|
||||
}
|
||||
}
|
||||
79
lib/src/models/pass_payload.dart
Normal file
79
lib/src/models/pass_payload.dart
Normal file
@@ -0,0 +1,79 @@
|
||||
import 'dart:typed_data';
|
||||
import 'package:potato_pass_core/src/encoding/compact_text_encoder.dart';
|
||||
|
||||
/// Модель данных пропуска PotatoPass
|
||||
class PassPayload {
|
||||
final String lastName;
|
||||
final String firstName;
|
||||
final String middleName;
|
||||
final String position;
|
||||
final String department;
|
||||
|
||||
/// Битовая маска зон доступа
|
||||
final Uint8List zoneMask;
|
||||
|
||||
/// 32 байта публичного ключа клиента (он же уникальный ID пропуска)
|
||||
final Uint8List clientPublicKey;
|
||||
|
||||
/// 64 байта подписи Службы Безопасности (Security Department Signature)
|
||||
final Uint8List securityDepartmentSignature;
|
||||
|
||||
/// 64 байта подписи Соли
|
||||
final Uint8List challengeSignature;
|
||||
|
||||
PassPayload({
|
||||
required this.lastName,
|
||||
required this.firstName,
|
||||
required this.middleName,
|
||||
required this.position,
|
||||
required this.department,
|
||||
required this.zoneMask,
|
||||
required this.clientPublicKey,
|
||||
required this.securityDepartmentSignature,
|
||||
required this.challengeSignature,
|
||||
});
|
||||
|
||||
/// Проверяет, разрешен ли проход в конкретную зону по её индексу
|
||||
bool hasZoneAccess(int zoneIndex) {
|
||||
final int byteIndex = zoneIndex ~/ 8;
|
||||
final int bitIndex = zoneIndex % 8;
|
||||
if (byteIndex >= zoneMask.length) return false;
|
||||
return (zoneMask[byteIndex] & (1 << bitIndex)) != 0;
|
||||
}
|
||||
|
||||
/// Возвращает байты тела пропуска (Текст + Зоны + PubKey)
|
||||
Uint8List getSignedBodyBytes() {
|
||||
final List<int> bytes = [];
|
||||
|
||||
void writeField(String text) {
|
||||
final Uint8List encoded = CompactTextEncoder.encode(text);
|
||||
bytes.add(encoded.length);
|
||||
bytes.addAll(encoded);
|
||||
}
|
||||
|
||||
writeField(lastName);
|
||||
writeField(firstName);
|
||||
writeField(middleName);
|
||||
writeField(position);
|
||||
writeField(department);
|
||||
|
||||
bytes.add(zoneMask.length);
|
||||
bytes.addAll(zoneMask);
|
||||
bytes.addAll(clientPublicKey);
|
||||
|
||||
return Uint8List.fromList(bytes);
|
||||
}
|
||||
|
||||
/// Вспомогательный статический метод для генерации битовой маски зон
|
||||
static Uint8List createZoneMask(List<int> activeZoneIndices, {int byteCount = 16}) {
|
||||
final mask = Uint8List(byteCount);
|
||||
for (final zone in activeZoneIndices) {
|
||||
final int byteIndex = zone ~/ 8;
|
||||
final int bitIndex = zone % 8;
|
||||
if (byteIndex < byteCount) {
|
||||
mask[byteIndex] |= (1 << bitIndex);
|
||||
}
|
||||
}
|
||||
return mask;
|
||||
}
|
||||
}
|
||||
65
lib/src/serializer/binary_serializer.dart
Normal file
65
lib/src/serializer/binary_serializer.dart
Normal file
@@ -0,0 +1,65 @@
|
||||
import 'dart:typed_data';
|
||||
import 'package:potato_pass_core/src/encoding/compact_text_encoder.dart';
|
||||
import 'package:potato_pass_core/src/models/pass_payload.dart';
|
||||
|
||||
/// Утилита для упаковки PassPayload в бинарный массив и обратно
|
||||
abstract class BinarySerializer {
|
||||
/// Упаковывает модель PassPayload в бинарный массив Uint8List
|
||||
static Uint8List serialize(PassPayload pass) {
|
||||
final List<int> bytes = [];
|
||||
|
||||
//Добавляем тело (Текст + Зоны + PubKey)
|
||||
bytes.addAll(pass.getSignedBodyBytes());
|
||||
|
||||
//Добавляем 64 байта подписи Службы Безопасности
|
||||
bytes.addAll(pass.securityDepartmentSignature);
|
||||
|
||||
//Добавляем 64 байта подписи Соли
|
||||
bytes.addAll(pass.challengeSignature);
|
||||
|
||||
return Uint8List.fromList(bytes);
|
||||
}
|
||||
|
||||
/// Распаковывает байтовый массив обратно в объект PassPayload
|
||||
static PassPayload deserialize(Uint8List bytes) {
|
||||
int offset = 0;
|
||||
|
||||
String readField() {
|
||||
final int length = bytes[offset++];
|
||||
final Uint8List fieldBytes = Uint8List.sublistView(bytes, offset, offset + length);
|
||||
offset += length;
|
||||
return CompactTextEncoder.decode(fieldBytes);
|
||||
}
|
||||
|
||||
final String lastName = readField();
|
||||
final String firstName = readField();
|
||||
final String middleName = readField();
|
||||
final String position = readField();
|
||||
final String department = readField();
|
||||
|
||||
final int zoneMaskLength = bytes[offset++];
|
||||
final Uint8List zoneMask = Uint8List.sublistView(bytes, offset, offset + zoneMaskLength);
|
||||
offset += zoneMaskLength;
|
||||
|
||||
final Uint8List clientPublicKey = Uint8List.sublistView(bytes, offset, offset + 32);
|
||||
offset += 32;
|
||||
|
||||
final Uint8List securityDepartmentSignature = Uint8List.sublistView(bytes, offset, offset + 64);
|
||||
offset += 64;
|
||||
|
||||
final Uint8List challengeSignature = Uint8List.sublistView(bytes, offset, offset + 64);
|
||||
offset += 64;
|
||||
|
||||
return PassPayload(
|
||||
lastName: lastName,
|
||||
firstName: firstName,
|
||||
middleName: middleName,
|
||||
position: position,
|
||||
department: department,
|
||||
zoneMask: zoneMask,
|
||||
clientPublicKey: clientPublicKey,
|
||||
securityDepartmentSignature: securityDepartmentSignature,
|
||||
challengeSignature: challengeSignature,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,10 @@
|
||||
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('CompactTextEncoder Tests', () {
|
||||
group('6-и битный колдировщик', () {
|
||||
test('Кодирование и декодирование русской строки', () {
|
||||
const input = 'ИВАНОВ ИВАН ИВАНОВИЧ';
|
||||
|
||||
@@ -50,4 +51,281 @@ void main() {
|
||||
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);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user