logo
All projects
2024 · Active

tcp-chat

Multi-room TCP chat server in C. Per-client pthreads, a three-way handshake protocol with sequence numbers, room management, and an encryption layer with AES-256-GCM and hardware entropy collection.

CPOSIX SocketspthreadsAES-256-GCM
View on GitHub
Overview

tcp-chat is a multi-client, multi-room chat server written in C from scratch on top of POSIX sockets and pthreads. Each incoming TCP connection gets its own thread and a client_session_t that tracks state across three phases: CONNECTED, IN_LOBBY, and IN_ROOM. Before a session enters the lobby, it completes a custom three-way handshake (SYN → SYN-ACK → ACK) that establishes a sequence number and a nickname. The room manager assigns short 6-character room IDs and enforces a 20-client-per-room limit. A security foundation layer provides per-client encryption contexts with AES-256-GCM as primary cipher and ChaCha20-Poly1305 as backup, seeded from hardware entropy sources.

Key features
01Per-client pthreads. server_accept_clients loops on accept(), registers each connection in client_manager, then spawns a pthread for client_session_handler_thread. The main thread never blocks on a client — it returns immediately to accept the next connection.
02Custom three-way handshake. handshake_packet_t carries type (SYN/SYN-ACK/ACK), sequence number, acknowledgment number, and nickname. The server sends SYN-ACK with a generated sequence number, waits for ACK, then marks the session HANDSHAKE_ESTABLISHED before any chat protocol messages are processed.
03Room manager with 6-character IDs. Rooms support up to 20 clients and up to 50 rooms total. chat_protocol handles /create, /join, /leave, /list-rooms, and /list-users commands, updating client_session_t.current_room_id and client_state_t on each transition.
04Security foundation with dual-cipher encryption. Each client gets an encryption_context_t holding both an AES-256-GCM context and a ChaCha20-Poly1305 context with independent keys, nonces, and counters. Key rotation is tracked via key_rotation_counter.
05Hardware entropy collection. entropy_pool_t collects from three sources: hardware RNG (RDRAND via hw_security_caps_t.rdrand_available), timing jitter, and thermal data. Sources are mixed into a 2048-byte pool with a quality estimate that must exceed a minimum threshold before the pool is used.

Server & Session Model

server_t owns the listening socket, a client_manager, and a room_manager. server_accept_clients runs the accept loop: each successful accept calls client_manager_add_new_client to register the socket and allocate a client_session_t, then spawns a pthread pointing to client_session_handler_thread.

client_session_t tracks everything per connection: socket fd, peer address, nickname, thread handle, handshake state, sequence number, current state (CONNECTED / IN_LOBBY / IN_ROOM), current room ID, and security validation status. The session handler reads this struct at the start of each iteration and writes it as state transitions happen.

server/src/server.c
int server_accept_clients(server_t *server) {
    server->is_running = 1;
    while (server->is_running) {
        struct sockaddr_in client_addr;
        socklen_t len = sizeof(client_addr);

        int client_socket = accept(server->socket_fd,
                (struct sockaddr *)&client_addr, &len);
        if (client_socket < 0) { /* handle EINTR, EAGAIN, errors */ continue; }

        int client_id = client_manager_add_new_client(
                &server->client_manager, client_socket, client_addr);

        client_session_t *session =
                client_manager_get_session(&server->client_manager, client_id);

        pthread_create(&session->thread, NULL,
                client_session_handler_thread, args);
    }
}

Handshake Protocol

Before a client can enter the lobby it must complete a custom three-way handshake modelled on TCP's SYN/SYN-ACK/ACK exchange. handshake_packet_t carries the packet type, a 32-bit sequence number, a 32-bit acknowledgment number, and the client's chosen nickname in a fixed-size field.

The server side (handshake_perform_server_side) receives the client's SYN, records the client sequence number, generates its own sequence number via handshake_generate_sequence_number, sends SYN-ACK with both numbers, then waits for ACK. If the ACK matches the expected ack_num the session is marked HANDSHAKE_ESTABLISHED and the sequence number is stored in client_session_t.sequence_number for message ordering.

server/include/handshake_protocol.h
typedef enum {
    HANDSHAKE_IDLE,
    HANDSHAKE_SYN_SENT,
    HANDSHAKE_SYN_RECEIVED,
    HANDSHAKE_ESTABLISHED,
    HANDSHAKE_FAILED
} handshake_state_t;

typedef struct {
    uint8_t  type;                    // SYN=0, SYN_ACK=1, ACK=2
    uint32_t seq_num;
    uint32_t ack_num;
    char     nickname[MAX_NICKNAME_LEN];
    uint8_t  padding[7];
} handshake_packet_t;

uint32_t handshake_generate_sequence_number(void);
int      handshake_perform_server_side(client_session_t *client);

Security Foundation

Each client connection gets a security_context_t with stack canaries at both ends, an encrypted encryption_context_t pointer, and hardware entropy. encryption_context_t holds two independent cipher contexts: AES-256-GCM as primary and ChaCha20-Poly1305 as backup. Both use 32-byte keys, nonces, and 64-bit counters. The active key is rotated automatically based on key_rotation_counter.

Entropy is collected from three hardware sources into a 2048-byte entropy_pool_t: RDRAND (if hw_security_caps_t.rdrand_available is set), timing jitter from nanosecond-resolution clocks, and thermal sensor data. Sources are mixed via mix_entropy_sources before any key material is derived. The pool tracks an entropy_estimate and enforces a minimum quality threshold (MIN_ENTROPY_QUALITY = 7.9 bits per byte) before use.

server/include/security_foundation.h
typedef struct {
    aes256_gcm_context_t        primary_cipher;   // AES-256-GCM
    chacha20_poly1305_context_t backup_cipher;    // ChaCha20-Poly1305
    uint8_t  session_key[32];
    uint8_t  message_key[32];
    uint64_t key_rotation_counter;
    uint32_t sequence_number;
    pthread_mutex_t crypto_mutex;
} encryption_context_t;

typedef struct {
    uint8_t  hw_entropy[1024];     // RDRAND
    uint8_t  timing_entropy[512];  // nanosecond jitter
    uint8_t  thermal_entropy[256]; // temperature sensors
    uint8_t  mixed_pool[2048];     // combined
    uint32_t entropy_estimate;     // must exceed MIN_ENTROPY_QUALITY
    pthread_mutex_t pool_mutex;
} entropy_pool_t;

#define MIN_ENTROPY_QUALITY 7.9