The client and the server share a lot of the same code, especially when it comes to the encryption and decryption of messages. However, since the server handles the majority of the processing, we’ll start there.

I’m only going to be showing the important bits so therefore some trivial code will be omitted (indicated by …). The full code can be found here.

We start by creating a new socket that the server will communicate on.

self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

We indicate the use of the IPv4 address family (e.g. 0x00sec.org, 74.125.136.94) with socket.AF_INET, and the use of TCP with socket.SOCK_STREAM.

Next, we generate the parameters for the Diffie-Hellman key exchange with the DH module from m2crypto.

self.dh_params = M2DH.gen_params(DH_SIZE, 2)

Remember that we needed to create two public values: a prime modulus p and prime base g. When we indicate the size (in bits) of the desired prime (a minimum of 2048 bits for the shared prime is recommended) and which generator we want to use, this function returns a class containing p and g. It does this using OpenSSL, a “robust, commercial-grade, and full-featured toolkit for the Transport Layer Security (TLS) and Secure Sockets Layer (SSL) protocols” and a “general-purpose cryptography library.” You’ll find many other programs use this library, such as OpenVPN.

We can now wait for new clients to connect, with which we will attempt Diffie-Hellman key exchange and listen for their incoming encrypted messages.

First, the method converts the p and g parameters from bytes to integers with DH.b2i, located in dhke.py. It then generates a random private key a (also an integer). The server’s public key for this exchange is calculated by raising g to the power of a and modulo-ing the result by p with DH.gen_public_key().

Since we need a standardized message format for the client to unpack, the first 1024 bytes belong to p, the following 16 to g, and the last 1024 to the public key. The package method just converts the integer variable to bytes and adds padding until it hits the desired length:

In a new thread we listen for income encrypted messages from the client, decrypt them, and broadcast them (re-encrypted) to every other client on the server with self.listen.

This post is already long enough as-is, so I’ll stop here for now. Check in next time when we’ll look at how the server processes messages and maybe see what’s going on with the client. Thanks for reading!