92 lines
5.7 KiB
Markdown
92 lines
5.7 KiB
Markdown
# 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 Diffie–Hellman (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.). |