2025-06-20 23:36:37 +03:00
2025-06-20 23:36:37 +03:00
2025-06-20 23:19:45 +03:00
2025-06-20 23:19:45 +03:00
2025-06-20 23:19:45 +03:00

Анализ и реализация клиента для 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. Ключи клиента: Клиент генерирует новую, эфемерную (временную) пару ключей Curve25519client_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, токен и т.д.).

Description
No description provided
Readme 46 KiB
Languages
Python 100%