Flutter Client / User Flows

Пути пользователя и диаграммы вызовов

Пошаговое описание процессов регистрации, шифрования, обмена ключами и WebRTC-звонков.

Сценарий А: Первичная регистрация и настройка E2EE

Этот путь выполняется при первом входе пользователя в аккаунт после регистрации или при сбросе ключей шифрования.

Последовательность действий
Пользователь                Клиент (Flutter)                Бэкенд (FastAPI)
     │                             │                               │
     ├─ Вводит логин/пароль ──────>│                               │
     │  и нажимает "Войти"         ├─ POST /auth/login ───────────>│
     │                             │<─ Возвращает JWT токены ──────┤
     │                             │                               │
     │  (Перенаправление на экран настройки аккаунта)              │
     ├─ Вводит мастер-пароль ─────>│                               │
     │                             ├─ Генерирует пару X25519       │
     │                             │  ключей (CryptoService)       │
     │                             ├─ Производит KDF (PBKDF2)      │
     │                             │  мастер-пароля                │
     │                             ├─ Шифрует приватный ключ       │
     │                             │  с AES-256-GCM                │
     │                             │                               │
     │                             ├─ POST /auth/setup-account ───>│ (Сохраняет
     │                             │  (Имя, Публичный ключ,        │  публичный ключ
     │                             │   зашифрованный приватник)    │  и зашифрованный
     │                             │                               │  приватник)
     │                             │<─ Статус 200 OK ──────────────┤
     │                             │                               │
     │                             ├─ Сохраняет приватный ключ в   │
     │                             │  SecureStorage и ОЗУ-кэш      │
     ▼                             ▼                               ▼

Сценарий Б: Начало чата и обмен ключами (Диффи-Хеллман)

Диаграмма показывает, как два пользователя устанавливают защищенный канал связи без передачи секретного ключа через сервер.

Схема обмена ключами
Алиса (Отправитель)               Сервер (FastAPI)               Боб (Получатель)
     │                                   │                               │
     ├─ Создает чат с Бобом              │                               │
     ├─ GET /users/by-username/bob ─────>│                               │
     │<─ Возвращает PubKey_Bob (X25519) ─┤                               │
     │                                   │                               │
     ├─ Вычисляет общий секрет:          │                               │
     │  ECDH(PrivKey_Alice, PubKey_Bob)  │                               │
     ├─ Кэширует общий ключ в ОЗУ        │                               │
     │                                   │                               │
     ├─ Шифрует текст с AES-GCM (shared) │                               │
     ├─ Отправляет по сокету: ──────────>│                               │
     │  {"type": "private_message",      ├─ Пересылает по сокету ───────>│
     │   "content": "Base64(cipher)"}    │                               ├─ Получает событие
     │                                   │                               ├─ Проверяет локальный кэш
     │                                   │                               ├─ Если ключа нет:
     │                                   │                               │  скачивает PubKey_Alice
     │                                   │                               ├─ Вычисляет общий секрет:
     │                                   │                               │  ECDH(PrivKey_Bob, PubKey_Alice)
     │                                   │                               ├─ Дешифрует сообщение
     ▼                                   ▼                               ▼

Сценарий В: WebRTC P2P видеозвонок

Связь между устройствами устанавливается через сигнальные сообщения по WebSockets.

Установление WebRTC соединения
Вызывающий (Алиса)                WebSocket Сервер               Вызываемый (Боб)
     │                                   │                               │
     ├─ Инициирует звонок                │                               │
     ├─ WS: {"type": "call_init"} ──────>│                               │
     │                                   ├─ Отправляет high-priority     │
     │                                   │  FCM пуш для пробуждения      │
     │                                   ├─ WS: Forward call_init ──────>│
     │                                   │                               ├─ Звонит телефон
     │                                   │                               ├─ Нажимает "Принять"
     │                                   │<─ WS: {"type": "call_accept"}─┤
     │<─ WS: Forward call_accepted ──────┤                               │
     │                                   │                               │
     │  (Алиса и Боб создают RTCPeerConnection и захватывают камеры)     │
     ├─ Создает SDP Offer                │                               │
     ├─ WS: {"type": "offer"} ──────────>│                               │
     │                                   ├─ WS: Forward offer ──────────>│
     │                                   │                               ├─ Устанавливает Offer
     │                                   │                               ├─ Создает SDP Answer
     │                                   │<─ WS: {"type": "answer"} ─────┤
     │<─ WS: Forward answer ─────────────┤                               │
     │                                   │                               │
     │  (Параллельный обмен кандидатами ICE для пробития NAT)            │
     ├─ WS: {"type": "ice_candidate"} ──>│                               │
     │                                   ├─ WS: Forward candidate ──────>│
     │                                   │<─ WS: {"type": "ice_candidate"}
     │<─ WS: Forward candidate ──────────┤                               │
     │                                   │                               │
     │<═════════════ УСТАНОВЛЕНО ПРЯМОЕ P2P СОЕДИНЕНИЕ ═════════════════>│
     ▼                                   ▼                               ▼

Сценарий Г: Сопряжение десктопного приложения через QR-код

Диаграмма авторизации устройства-сателлита (ПК) с помощью основного телефона.

Схема сопряжения
Новое устройство (Десктоп)         WebSocket Сервер                Смартфон (Авторизован)
     │                                   │                               │
     ├─ Подключается к /ws/qr            │                               │
     │  с room_id = UUID                 │                               │
     ├─ Рисует на экране QR-код ─────────┼──────────────────────────────>│ Сканирует QR-код
     │                                   │                               ├─ Читает room_id
     │                                   │                               ├─ Расшифровывает свой
     │                                   │                               │  X25519 приватный ключ
     │                                   │                               ├─ Шифрует приватный ключ
     │                                   │                               │  ключом ПК (из QR-кода)
     │                                   │                               ├─ POST /auth/qr/approve
     │                                   │<──────────────────────────────┤  (передает room_id, JWT,
     │                                   │                               │   зашифрованный приватник)
     │                                   ├─ Находит комнату room_id      │
     │<─ WS: Отправляет JWT-токен ───────┤                               │
     │   и E2EE-ключи                    │                               │
     ├─ Сохраняет ключи,                 │                               │
     │  закрывает WebSocket сопряжения   │                               │
     ▼                                   ▼                               ▼