BSDecrypter-Colette/README-en.md
2025-06-20 23:19:45 +03:00

5.7 KiB
Raw Permalink Blame History

Analysis and Client Implementation for Project Colette V53

This document provides a detailed technical breakdown of the connection process to a Project Colette V53 server. It dissects the cryptographic handshake, packet structure, and message exchange logic as implemented in the main.py Python client.

[*] Core Concepts

The data exchange protocol is a custom protocol layered on top of standard TCP. The encryption is built using the NaCl cryptographic library (referenced in the C# code as TweetNaCl), which provides:

  • Asymmetric Encryption: Curve25519 for key exchange via Elliptic-curve DiffieHellman (ECDH).
  • Symmetric Encryption: XSalsa20-Poly1305 for stream encryption after the session is established.
  • Hashing: Blake2b for nonce generation.

Each message (packet) in the protocol is prefixed with a 7-byte header:

  • Message ID (2 bytes): The identifier for the message type.
  • Payload Length (3 bytes): The length of the upcoming payload.
  • Version (2 bytes): The version of the message.

[!] Step-by-Step Breakdown of the Connection Process

The process from the initial connection to a fully established encrypted session involves the exchange of four key packets.


Step 1: ClientHello (ID: 10100)

  • Direction: Client -> Server
  • Encryption: None

This is the very first packet the client sends after establishing a TCP connection. Its sole purpose is to initiate a session on the server and signal readiness to begin the handshake. The payload of this packet is irrelevant to the cryptography and can be filled with zeros.


Step 2: LoginMessage (ID: 10101)

  • Direction: Client -> Server
  • Encryption: Asymmetric (Box)

This is the key packet from the client, where the core of the key exchange magic happens.

Client-Side Preparation (Before Sending):

  1. Server's Key: The client uses the server's static server_public_key, which is known beforehand (extracted from the server's source code).
  2. Client's Keys: The client generates a new, ephemeral (temporary) Curve25519 key pair—client_public_key and client_private_key. This pair will only be used for the current session.
  3. Shared Secret (s): The client immediately computes the pre-master shared secret (shared_secret or s) using its new private key and the server's public key. This is a standard ECDH operation. In PyNaCl, this is done with Box.beforenm(server_public_key, client_private_key). This secret s will be needed to decrypt the server's response.
  4. Client Nonce (RNonce): The client generates 24 random bytes, the client_nonce. The server refers to this as the RNonce (Receiver's Nonce). It is a critical part of the handshake.

Packet Structure and Transmission:

The 10101 packet consists of two parts:

  1. Client's Public Key (32 bytes): Sent in cleartext.
  2. Encrypted Payload:
    • Data to Encrypt: The plaintext to be encrypted contains:
      1. A temporary session key (24 bytes, generated by the client but ignored by the server).
      2. The client_nonce (RNonce) (24 bytes).
      3. The actual login data (account ID, client version, etc.), serialized into a bytestream.
    • Nonce for Encryption: The nonce for this operation is calculated as a hash of the public keys: blake2b(client_public_key + server_public_key).
    • Encryption Process: The data above is encrypted using Box(client_private_key, server_public_key).encrypt(...).

Step 3: ServerHello (ID: 20100)

  • Direction: Server -> Client
  • Encryption: Asymmetric (Box)

This is the server's response, which completes the asymmetric phase of the handshake.

Client-Side Decryption Process:

  1. Nonce Calculation: To decrypt this packet, the client must use the same nonce the server used. The server calculates it with the formula: blake2b(RNonce + client_public_key + server_public_key). The client already has all three components: it generated RNonce itself, and it knows both public keys.
  2. Decryption Process: Decryption is performed using the previously computed shared secret s. Formula: Box.open_afternm(payload, nonce, shared_secret).

Contents of the Decrypted Packet:

Inside are two key elements for all future communication:

  1. Final Session Key (session_key) (32 bytes): This is the symmetric key that will be used to encrypt all subsequent messages.
  2. Server Nonce (SNonce) (24 bytes): This nonce will be the base for encrypting messages flowing from the server to the client.

Step 4: Transition to Symmetric Encryption & LoginOk (ID: 20104)

  • Direction: Server -> Client
  • Encryption: Symmetric (SecretBox)

After receiving ServerHello, the cryptographic session is fully established. All subsequent packets are encrypted symmetrically using SecretBox and the session_key.

Nonce Logic (NextNonce)

A key feature of the protocol is that the nonce is incremented by 2 before every encryption or decryption operation. The next_nonce function in the code precisely replicates this server-side logic.

LoginOk

This is the first packet the client receives that is symmetrically encrypted.

  1. Get Nonce: The client takes the SNonce it received in the previous step.
  2. Increment Nonce: It applies the next_nonce logic to it (increasing it by 2).
  3. Decrypt: It decrypts the packet using SecretBox(session_key).decrypt(payload, nonce).

Successfully decrypting LoginOk confirms that the entire process has worked correctly, and the client is now in a fully encrypted session with the server. This packet contains the details of the newly created account (ID, token, etc.).