# Анализ и реализация клиента для Project Colette V53 Этот документ представляет подробный технический разбор процесса подключения к серверу Project Colette V53. Он анализирует криптографическое рукопожатие, структуру пакетов и логику обмена сообщениями, реализованную в Python-клиенте `main.py`. ## [*] Основные концепции Протокол обмена данными — это пользовательский протокол, работающий поверх стандартного TCP. Шифрование построено с использованием криптографической библиотеки **NaCl** (в коде C# упоминается как `TweetNaCl`), которая предоставляет: - **Асимметричное шифрование**: `Curve25519` для обмена ключами через эллиптическую кривую Диффи-Хеллмана (ECDH). - **Симметричное шифрование**: `XSalsa20-Poly1305` для потокового шифрования после установления сессии. - **Хеширование**: `Blake2b` для генерации одноразовых номеров (nonce). Каждое сообщение (пакет) в протоколе предваряется 7-байтным заголовком: - **ID сообщения** (2 байта): Идентификатор типа сообщения. - **Длина полезной нагрузки** (3 байта): Длина предстоящих данных. - **Версия** (2 байта): Версия сообщения. ## [!] Пошаговый разбор процесса подключения Процесс от первоначального подключения до полностью установленной зашифрованной сессии включает обмен четырьмя ключевыми пакетами. --- ### **Шаг 1: `ClientHello` (ID: 10100)** - **Направление**: Клиент -> Сервер - **Шифрование**: Нет Это самый первый пакет, который клиент отправляет после установления TCP-соединения. Его единственная цель — инициировать сессию на сервере и сообщить о готовности начать рукопожатие. Полезная нагрузка этого пакета не имеет значения для криптографии и может быть заполнена нулями. --- ### **Шаг 2: `LoginMessage` (ID: 10101)** - **Направление**: Клиент -> Сервер - **Шифрование**: Асимметричное (`Box`) Это ключевой пакет от клиента, где происходит основная магия обмена ключами. #### **Подготовка на стороне клиента (перед отправкой):** 1. **Ключ сервера**: Клиент использует статический `server_public_key` сервера, который известен заранее (извлечен из исходного кода сервера). 2. **Ключи клиента**: Клиент генерирует новую, **эфемерную (временную)** пару ключей `Curve25519` — `client_public_key` и `client_private_key`. Эта пара будет использоваться только для текущей сессии. 3. **Общий секрет (`s`)**: Клиент немедленно вычисляет предварительный общий секрет (`shared_secret` или `s`), используя свой новый приватный ключ и публичный ключ сервера. Это стандартная операция ECDH. В `PyNaCl` это делается с помощью `Box.beforenm(server_public_key, client_private_key)`. Этот секрет `s` понадобится для расшифровки ответа сервера. 4. **Nonce клиента (`RNonce`)**: Клиент генерирует 24 случайных байта, `client_nonce`. Сервер называет это `RNonce` (Nonce получателя). Это критически важная часть рукопожатия. #### **Структура и передача пакета:** Пакет `10101` состоит из двух частей: 1. **Публичный ключ клиента** (32 байта): Отправляется в открытом виде. 2. **Зашифрованная полезная нагрузка**: - **Данные для шифрования**: Открытый текст для шифрования содержит: 1. Временный сеансовый ключ (24 байта, генерируется клиентом, но игнорируется сервером). 2. `client_nonce` (`RNonce`) (24 байта). 3. Фактические данные для входа (ID аккаунта, версия клиента и т.д.), сериализованные в байтовый поток. - **Nonce для шифрования**: Nonce для этой операции вычисляется как хеш публичных ключей: `blake2b(client_public_key + server_public_key)`. - **Процесс шифрования**: Вышеуказанные данные шифруются с помощью `Box(client_private_key, server_public_key).encrypt(...)`. --- ### **Шаг 3: `ServerHello` (ID: 20100)** - **Направление**: Сервер -> Клиент - **Шифрование**: Асимметричное (`Box`) Это ответ сервера, который завершает асимметричную фазу рукопожатия. #### **Процесс расшифровки на стороне клиента:** 1. **Вычисление Nonce**: Чтобы расшифровать этот пакет, клиент должен использовать тот же `nonce`, который использовал сервер. Сервер вычисляет его по формуле: `blake2b(RNonce + client_public_key + server_public_key)`. У клиента уже есть все три компонента: он сам сгенерировал `RNonce` и знает оба публичных ключа. 2. **Процесс расшифровки**: Расшифровка выполняется с использованием ранее вычисленного общего секрета `s`. Формула: `Box.open_afternm(payload, nonce, shared_secret)`. #### **Содержимое расшифрованного пакета:** Внутри находятся два ключевых элемента для всей будущей коммуникации: 1. **Финальный сеансовый ключ (`session_key`)** (32 байта): Это симметричный ключ, который будет использоваться для шифрования всех последующих сообщений. 2. **Nonce сервера (`SNonce`)** (24 байта): Этот nonce будет основой для шифрования сообщений, идущих **от сервера к клиенту**. --- ### **Шаг 4: Переход к симметричному шифрованию и `LoginOk` (ID: 20104)** - **Направление**: Сервер -> Клиент - **Шифрование**: Симметричное (`SecretBox`) После получения `ServerHello` криптографическая сессия считается полностью установленной. Все последующие пакеты шифруются симметрично с использованием `SecretBox` и `session_key`. #### **Логика Nonce (`NextNonce`)** Ключевой особенностью протокола является то, что nonce увеличивается **на 2** перед каждой операцией шифрования или дешифрования. Функция `next_nonce` в коде точно воспроизводит эту логику на стороне сервера. #### **`LoginOk`** Это первый пакет, который клиент получает в зашифрованном виде. 1. **Получить Nonce**: Клиент берет `SNonce`, полученный на предыдущем шаге. 2. **Увеличить Nonce**: Он применяет к нему логику `next_nonce` (увеличивая его на 2). 3. **Расшифровать**: Он расшифровывает пакет с помощью `SecretBox(session_key).decrypt(payload, nonce)`. Успешная расшифровка `LoginOk` подтверждает, что весь процесс прошел корректно, и клиент теперь находится в полностью зашифрованной сессии с сервером. Этот пакет содержит данные о созданном аккаунте (ID, токен и т.д.).