# PotatoPassCore Core библиотека на Dart для экосистемы **PotatoPass** (электронные пропуска и системы контроля доступа). Содержит реализацию бинарного протокола v1.0, 6-битную оптимизацию текста, генерацию асимметричных ключей и подписей Ed25519, а также средства сериализации/десериализации. --- ## Подключение к проектам Добавление заывисимости в `pubspec.yaml`: ```yaml dependencies: potato_pass_core: git: url: https://git.mr-potato.ru/PotatoPass/PotatoPassCore.git ref: stable ``` --- ## Спецификация Бинарного Протокола Формат разработан для сверхплотной упаковки данных пропуска в 2D-коды ### Структура Байтового Массива (Binary Layout) | Секция | Длина (Байты) | Описание | | :--- | :--- | :--- | | **1. Текстовый блок** | Динамическая (~20–45B) | 5 полей текста с 1-байтовым префиксом длины (6-битное кодирование) | | **2. Блок доступа** | Динамическая (~3–33B) | `1B` длина маски $N$ + $N$ байт битовой маски зон ($1 \text{ бит} = 1 \text{ зона}$) | | **3. Публичный ключ** | `32B` | 32 байта Ed25519 Public Key клиента (уникальный ID пропуска) | | **4. Подпись СБ** | `64B` | Подпись Службы Безопасности (подписывает секции с 1 по 3) | | **5. Подпись Соли** | `64B` | Подпись Клиента (подписывает `Соль (16B)` + `Подпись СБ (64B)`) | --- ## 6-битный кодировщик текста (`CompactTextEncoder`) Для кириллицы стандартный UTF-8 использует 2 байта (16 бит) на символ. `CompactTextEncoder` сжимает 4 символа в 3 байта (6 бит на символ), давая **62% экономии места**. ### Словарь символов (64 значения = $2^6$): ```text 0..32 : А Б В Г Д Е Ё Ж З И Й К Л М Н О П Р С Т У Ф Х Ц Ч Ш Щ Ъ Ы Ь Э Ю Я 33..58 : A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 59..63 : [Пробел] - . , _ (подчеркивание/заполнитель) ``` --- ## Криптографическая модель подписей (Ed25519) В системе используются две цифровые подписи для защиты от подделки и копирования: ```text 1. [ Текст + Зоны + PubKey Клиента ] ──────────────► Подпись СБ (64B) 2. [ Challenge Salt (16B) + Подпись СБ (64B) ] ───────────────► Подпись Соли Клиентом (64B) ``` 1. **`securityDepartmentSignature` (Подпись СБ):** Доказывает, что ФИО, должность и зоны доступа утверждены Службой Безопасности и не изменены владельцем. 2. **`challengeSignature` (Подпись Соли):** Доказывает, что пропуск предъявляет именно его владелец с помощью своего приватного ключа. Одноразовая соль (16B) сгорает каждые 10 секунд, делая скриншоты и фото кода бесполезными. --- ## Примеры использования API ### 1. Работа с 6-битным кодировщиком текста ```dart import 'dart:typed_data'; import 'package:potato_pass_core/potato_pass_core.dart'; void main() { // Упаковка строки в 6-битные байты final Uint8List encoded = CompactTextEncoder.encode('Иванов Иван'); // Распаковка обратно в строку final String decoded = CompactTextEncoder.decode(encoded); // "ИВАНОВ ИВАН" } ``` ### 2. Генерация ключей и подпись (Ed25519) ```dart import 'dart:typed_data'; import 'package:cryptography/cryptography.dart'; import 'package:potato_pass_core/potato_pass_core.dart'; void main() async { // Генерация пары ключей final SimpleKeyPair keyPair = await CryptoService.generateKeyPair(); final Uint8List pubKeyBytes = await CryptoService.getPublicKeyBytes(keyPair); // Подпись сообщения final List message = [1, 2, 3, 4, 5]; final Uint8List signature = await CryptoService.sign( message: message, keyPair: keyPair, ); // Проверка подписи final bool isValid = await CryptoService.verify( message: message, signatureBytes: signature, publicKeyBytes: pubKeyBytes, ); } ``` ### 3. Полный цикл сериализации и проверки пропуска ```dart import 'dart:typed_data'; import 'package:potato_pass_core/potato_pass_core.dart'; void main() async { // Генерация маски зон доступа (разрешаем зоны 1, 5, 10) final Uint8List zoneMask = PassPayload.createZoneMask([1, 5, 10]); // Создание объекта пропуска final pass = PassPayload( lastName: 'Иванов', firstName: 'Иван', middleName: 'Иванович', position: 'Инженер', department: 'Отдел ИТ', zoneMask: zoneMask, clientPublicKey: clientPubKeyBytes, securityDepartmentSignature: sbSignatureBytes, challengeSignature: clientChallengeSigBytes, ); // Упаковка в байты для DataMatrix / BLE final Uint8List binaryPayload = BinarySerializer.serialize(pass); // Распаковка из байтов на стороне Контролера final PassPayload restoredPass = BinarySerializer.deserialize(binaryPayload); // Проверка прав доступа в зону 5 if (restoredPass.hasZoneAccess(5)) { print('Доступ разрешен!'); } } ```