Files
PotatoPassCore/README.md
2026-08-10 00:08:27 +03:00

149 lines
6.3 KiB
Markdown
Raw 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.
# 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<int> 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('Доступ разрешен!');
}
}
```