Скрепа
Защищённый неанонимный (KYC) мессенджер со сквозным шифрованием на российских алгоритмах ГОСТ. В локальной разработке авторизация сейчас идёт через mock OAuth provider; production KYC-провайдер ещё не зафиксирован.
Архитектура
┌─────────────────────────────────────────────┐
│ Flutter UI (6 платформ) │
│ ┌───────────────────────────────────────┐ │
│ │ Rust Core (flutter_rust_bridge FFI) │ │
│ │ gost-crypto + skrepa-protocol │ │
│ └───────────────────────────────────────┘ │
└──────────────┬──────────────────────────────┘
│ gRPC (tonic)
┌──────────────▼──────────────────────────────┐
│ Rust Server │
│ PostgreSQL · KYC auth provider │
└─────────────────────────────────────────────┘
Компоненты
| Компонент | Путь | Описание |
|---|---|---|
| gost-crypto | gost-crypto/ |
Pure Rust реализация ГОСТ алгоритмов (без OpenSSL) |
| skrepa-protocol | skrepa-protocol/ |
E2E протокол: X3DH, сессии, группы, хранилище |
| Сервер | server/ |
gRPC сервер: маршрутизация, авторизация, хранение |
| Клиент (Rust ядро) | client/core/ |
Bridge API для Flutter: криптография, локальная БД |
| Клиент (Flutter) | client/flutter_app/ |
UI: чаты, контакты, группы, настройки |
Криптография
Подробное описание криптографического стека, E2E-протоколов, storage encryption и multi-device вынесено в docs/crypto-architecture.md.
Для protected groups целевая архитектура и ограничения отдельно зафиксированы в docs/adr/0001-mls-gost.md.
Требования
Обязательные
- Rust 1.75+ (
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh) - Flutter 3.11+ (
brew install --cask flutter) - Docker (для
testcontainers, integration compose-стендов и локальной разработки)
Опциональные (для proptest эквивалентности с OpenSSL)
- OpenSSL 3.x с gost-engine (
brew install openssl@3) - gost-engine (собрать из исходников)
Быстрый старт
1. Клонирование и сборка
git clone <repo-url> skrepa
cd skrepa
# Сборка Rust workspace (crypto + protocol + server)
cargo build
# Сборка клиентского Rust ядра (отдельный workspace из-за конфликта sqlite)
cd client/core && cargo build && cd ../..
2. Канонический локальный запуск
scripts/dev-web-local.sh
Это основной локальный entrypoint для разработки web-клиента и backend в одной команде. Скрипт:
- поднимает PostgreSQL, Redis, Scylla и MinIO через
docker compose; - собирает и запускает локальный debug
skrepa-server; - включает mock OAuth для локальной авторизации;
- собирает и сервит web-клиент на
http://127.0.0.1:8090.
Для одноразовой сборки без watcher:
scripts/dev-web-local.sh --once
Если нужен browser launch сразу после старта:
scripts/dev-web-local.sh --open
3. Backend/infra без web
Если нужен только backend/infra для ручных проверок, cargo test или flutter test, канонический способ не поднимать сервисы руками, а обернуть команду:
scripts/with-itest-env.sh -- bash
Скрипт сам поднимет PostgreSQL, Redis, Scylla, MinIO и skrepa-server, экспортирует SKREPA_GRPC_* переменные и зачистит окружение после выхода из shell. Вместо bash можно передать любую команду, например cargo xtask test flutter-web-integration.
4. Запуск Flutter-клиента отдельно
Для web используй scripts/dev-web-local.sh из предыдущего раздела. Для desktop/mobile flutter run остаётся отдельной точкой входа, но backend уже должен быть поднят через scripts/dev-web-local.sh или scripts/with-itest-env.sh -- bash:
cd client/flutter_app
flutter pub get
flutter run # на macOS/Windows/Linux, если backend доступен по localhost
Для Android/iOS указывайте backend явно через --dart-define, иначе mobile-клиент останется на дефолтном localhost:50051 устройства:
cd client/flutter_app
# Android Emulator -> backend на хост-машине
flutter run \
--dart-define=SKREPA_GRPC_HOST=10.0.2.2 \
--dart-define=SKREPA_GRPC_PORT=50051 \
--dart-define=SKREPA_GRPC_TLS=false
# Тестовый стенд / внешний backend
flutter run \
--dart-define=SKREPA_GRPC_HOST=skrepa.fomkin.org \
--dart-define=SKREPA_GRPC_PORT=443 \
--dart-define=SKREPA_GRPC_TLS=true
Тестирование
Подробный контракт по тестам, профилям, suite’ам, prerequisites, CI и низкоуровневым runner’ам вынесен в docs/testing.md.
# Показать реестр suite'ов и профилей
cargo xtask test list
# Проверить окружение и сделать быстрый прогон
cargo xtask test doctor fast
cargo xtask test fast
# Полный канонический прогон перед merge
cargo xtask test doctor full
cargo xtask test full
# Тяжёлый CI/scheduled контракт
cargo xtask test doctor exhaustive
cargo xtask test exhaustive
Для точечных suite’ов, doctor, dry-run, device runtime, CI-контракта и низкоуровневых runner’ов см. docs/testing.md.
Конфигурация сервера
Переменные окружения
| Переменная | Описание | По умолчанию (debug) |
|---|---|---|
DATABASE_URL |
PostgreSQL connection string | postgres://localhost/skrepa |
JWT_SECRET |
Секрет для JWT токенов (минимум 32 байта) | dev-secret-... |
LISTEN_ADDR |
Адрес прослушивания gRPC | 0.0.0.0:50051 |
REDIS_URL |
Redis для Pub/Sub и состояния звонков (TLS: rediss://...) |
redis://127.0.0.1:6379 |
SCYLLA_NODES |
ScyllaDB ноды (через запятую) | 127.0.0.1:9042 |
S3_ENDPOINT |
S3-совместимое хранилище | http://127.0.0.1:9000 |
S3_BUCKET |
Имя бакета | skrepa-files |
S3_ACCESS_KEY |
S3 ключ доступа | minioadmin |
S3_SECRET_KEY |
S3 секретный ключ | minioadmin |
CDN_BASE_URL |
URL для прямой загрузки файлов | http://127.0.0.1:9000/skrepa-files |
MAX_FILE_SIZE |
Макс. размер файла (байт) | 10485760 (10 МБ) |
TURN_ENABLED |
Включить self-hosted TURN | false |
TURN_URL |
Адрес TURN-сервера | turn:turn.example.com:3478 |
TURN_SECRET |
Shared secret для coturn | (пусто) |
TURN_TTL |
Время жизни TURN-credentials (сек) | 86400 (24 часа) |
В release-сборке DATABASE_URL, JWT_SECRET, S3_ENDPOINT, S3_ACCESS_KEY, S3_SECRET_KEY, CDN_BASE_URL обязательны — сервер упадёт при запуске без них.
Настройка TURN-сервера (для звонков)
Без TURN-сервера звонки работают только при прямом P2P-соединении. За symmetric NAT или строгими firewall (~15% сетей) звонки не пройдут. Для production-развёртывания TURN обязателен.
1. Установка coturn
apt install coturn
systemctl enable coturn
2. Конфигурация /etc/turnserver.conf
realm=skrepa.example.com
listening-port=3478
tls-listening-port=443
# Публичный IP сервера
external-ip=203.0.113.10
# TLS-сертификат (для TURNS через 443 — проходит через любой firewall)
cert=/etc/letsencrypt/live/turn.skrepa.example.com/fullchain.pem
pkey=/etc/letsencrypt/live/turn.skrepa.example.com/privkey.pem
# Shared secret — тот же что TURN_SECRET в Скрепе
use-auth-secret
static-auth-secret=СГЕНЕРИРУЙТЕ_НАДЁЖНЫЙ_СЕКРЕТ
# Relay-порты
min-port=49152
max-port=65535
# Запретить relay на внутренние сети
denied-peer-ip=10.0.0.0-10.255.255.255
denied-peer-ip=172.16.0.0-172.31.255.255
denied-peer-ip=192.168.0.0-192.168.255.255
log-file=/var/log/turnserver.log
3. Открыть порты на firewall
# STUN/TURN
ufw allow 3478/tcp
ufw allow 3478/udp
# TURNS (TLS)
ufw allow 443/tcp
# Relay-порты
ufw allow 49152:65535/udp
4. Запуск сервера Скрепа с TURN
TURN_ENABLED=true \
TURN_URL=turn:turn.skrepa.example.com:3478 \
TURN_SECRET=СГЕНЕРИРУЙТЕ_НАДЁЖНЫЙ_СЕКРЕТ \
TURN_TTL=86400 \
DATABASE_URL=postgres://... \
JWT_SECRET=... \
cargo run -p skrepa-server
5. Проверка
# Проверить что coturn отвечает
turnutils_uclient -T -u test -w test turn.skrepa.example.com
# В логах сервера должно быть:
# TURN credentials generated for user ...
Как работает
- Клиент вызывает
StartCall— сервер генерирует временные TURN-credentials:username = "timestamp:userId"(expires черезTURN_TTLсекунд)credential = base64(HMAC-SHA1(TURN_SECRET, username))
- Сервер возвращает ICE-серверы: STUN + TURN/UDP + TURN/TCP + TURNS/443
- Клиент использует их для WebRTC PeerConnection
- coturn проверяет credentials тем же HMAC-SHA1 алгоритмом
- Если P2P не проходит, трафик идёт через TURN-сервер (зашифрован E2E, TURN видит только шифртекст)
Важно: TURN-credentials передаются через Redis Pub/Sub. В production Redis должен использовать TLS (
rediss://вместоredis://), чтобы credentials не передавались по сети в открытом виде. Credentials имеют ограниченный TTL (по умолчанию 24 часа).
Схема базы данных
Миграции применяются автоматически. Основные PostgreSQL таблицы:
| Таблица | Назначение |
|---|---|
users |
Пользователи (user_id, KYC subject, identity key) |
devices |
Привязанные устройства (device_id, revoked_at) |
prekeys |
Одноразовые prekeys для X3DH |
messages |
Зашифрованные сообщения (payload — opaque blob) |
groups |
Групповые чаты |
group_members |
Участники групп (role: admin/member) |
storage_keys |
Зашифрованные ключи хранилища |
device_links |
Временные токены для привязки устройств |
auth_states |
Временные OAuth состояния внешнего auth provider |
file_metadata |
Метаданные файлов (owner, size) |
Протокол gRPC
Основные proto-определения находятся в server/proto/skrepa/v1/.
| Сервис | Методы | Авторизация |
|---|---|---|
| AuthService | StartAuth, CompleteAuth, RefreshToken | Нет (pre-auth) |
| UserService | Register, GetIdentityKey, PutStorageKey, GetStorageKey, InitDeviceLink, CompleteDeviceLink, ListDevices, RevokeDevice | JWT |
| PrekeyService | UploadPrekeys, GetPrekeyBundle, GetPrekeyCount | JWT |
| MessagingService | SendMessage, ReceiveMessages (stream), AckMessages, GetMessageHistory | JWT |
| GroupService | CreateGroup, AddMember, RemoveMember, GetGroup, ListGroups | JWT |
| FileService | UploadFile (stream), DownloadFile (stream) | JWT |
| SignalingService | StartCall, AnswerCall, RejectCall, EndCall, SendIceCandidate, ReceiveSignaling (stream) | JWT |
| GroupCallService | CreateRoom, JoinRoom, LeaveRoom, SendRoomIceCandidate, ReceiveRoomEvents (stream) | JWT (SFU) |
Протокол E2E шифрования
README больше не дублирует описание криптографических потоков. Подробности по direct sessions, sender keys, PROTECTED_MLS, storage encryption и multi-device см. в docs/crypto-architecture.md.
Безопасность
Проект прошёл 6 раундов аудита безопасности:
- 0 CRITICAL, 0 HIGH на последних 4 раундах
- Proof-of-possession при регистрации Identity Key
- Device revocation проверяется на каждом запросе (включая RefreshToken и streaming)
- Валидация всех входных данных (32-byte IDs, payload 64KB, batch limits)
- Все ошибки БД не утекают наружу (ServerError::Db → generic “internal error”)
- Rate limiting и cleanup — документированы как будущие улучшения
Структура файлов
skrepa/
├── Cargo.toml # Rust workspace
├── README.md # Этот файл
│
├── gost-crypto/ # Pure Rust ГОСТ криптография
│ ├── src/
│ │ ├── pure/ # Реализации алгоритмов
│ │ │ ├── kuznyechik.rs # Блочный шифр
│ │ │ ├── streebog.rs # Хеш-функция
│ │ │ ├── mgm.rs # AEAD режим
│ │ │ ├── gost3410.rs # ЭЦП + VKO
│ │ │ ├── bigint.rs # 256-бит арифметика
│ │ │ ├── ec.rs # Эллиптические кривые
│ │ │ ├── hmac.rs # HMAC-Стрибог
│ │ │ └── drbg.rs # ГОСТ DRBG
│ │ ├── hash.rs, cipher.rs, ... # Публичный API
│ │ └── engine.rs, ffi.rs # OpenSSL (только для proptest)
│ └── tests/
│ ├── equivalence.rs # Proptest vs OpenSSL (8000+ тестов)
│ └── integration.rs # Функциональные тесты
│
├── skrepa-protocol/ # E2E протокол
│ ├── src/
│ │ ├── x3dh.rs # X3DH handshake
│ │ ├── session.rs # Сессии (encrypt/decrypt)
│ │ ├── group.rs # Sender Keys + hash-ratchet
│ │ ├── storage.rs # Storage Key wrap/unwrap
│ │ ├── keys.rs # Типы ключей
│ │ └── nonce.rs # Counter-based nonces
│ └── tests/protocol.rs # Integration тесты протокола
│
├── server/ # gRPC сервер
│ ├── proto/skrepa/v1/ # Proto definitions
│ ├── migrations/ # SQL миграции
│ ├── src/
│ │ ├── main.rs, lib.rs # Точка входа
│ │ ├── auth/ # Auth provider abstraction, JWT, interceptor
│ │ ├── db/ # PostgreSQL + Scylla access layer
│ │ ├── services/ # gRPC services
│ │ ├── validate.rs # Валидация входных данных
│ │ └── push/ # Push-уведомления (stub)
│ └── tests/e2e.rs # E2E тесты
│
└── client/
├── core/ # Rust ядро клиента
│ └── src/
│ ├── api/skrepa.rs # Bridge API для Flutter
│ └── db/ # SQLite schema
│
└── flutter_app/ # Flutter UI (6 платформ)
├── lib/
│ ├── main.dart # Точка входа (RustLib.init + Riverpod)
│ ├── app/ # Router, тема
│ ├── grpc/ # gRPC клиенты + interceptors
│ ├── providers/ # Riverpod (auth, messaging, groups, devices)
│ └── screens/ # 8 экранов
└── test/
├── screens/ # 10 widget тестов
└── integration/ # 17 integration тестов (Docker + Rust)
Лицензия
TBD