A multi-room, end-to-end encrypted chat server application written in C. Features password-protected rooms, client-side encryption, secure message broadcasting, and ngrok support for remote connections.
Huge thanks to STUDevLantern on Youtube for making this course on Socket Programming.
ChatSocket is a TCP-based chat application that enables multiple users to communicate in separate chat rooms with end-to-end encryption. The server handles room management and message routing, while encryption/decryption occurs entirely on the client side, ensuring that messages remain encrypted during transmission.
- Multi-room Support: Create and join multiple chat rooms
- End-to-End Encryption: Messages encrypted client-side using AES-256-CTR
- Password-Protected Rooms: Secure rooms with salted and hashed passwords
- Room-Based Key Derivation: Each room derives encryption keys from user-provided passwords
- Concurrent Connections: Multi-threaded server supporting up to 32 simultaneous clients
- Command Interface: Simple command-based interaction for room management
- Automatic Cleanup: Inactive rooms automatically cleaned up after 10 minutes
- Remote Access: Built-in ngrok support for exposing server to the internet
- Raw Mode Terminal: Real-time character input without waiting for Enter key
- Room Entry Notifications: Automatic broadcast when users enter rooms
- Color-Coded Interface: Visual distinction between messages, errors, and notifications
- Algorithm: AES-256-CTR (Counter Mode)
- Key Derivation: SHA-256 hash of room password
- IV Generation: Random 16-byte initialization vector per message
- Message Format:
ENC:<base64-encoded-ciphertext>
- Hashing: SHA-256 with 10,000 iterations
- Salt: 16-byte random salt per room
- Format:
<salt_hex>:<hash_hex> - Constant-Time Comparison: Protection against timing attacks
The SHA-256 implementation is based on the work from Lulu's Blog on Lucidar, with modifications to suit the specific requirements of this application.
- Protocol: TCP/IPv4
- Default Port: 2077 (configurable via
PORTenvironment variable) - Message Size: 2048 bytes maximum (MSG_SIZE), configurable by changing memory allocation
- Connection Model: One thread per client connection on the server side. On the client side 2 threads are active: 1 for sending and 1 for receiving messages.
- Maximum 50 concurrent rooms
- Maximum 32 members per room
- Automatic cleanup of inactive rooms (1 hour timeout)
- Password verification with constant-time comparison
- Broadcast notifications for room entry/exit
- Thread-per-client model with detached threads
- Mutex-protected shared state
- Command parsing and routing
- Message broadcasting within rooms
- Support for both encrypted and plaintext messages
- Raw mode terminal input for real-time character processing
- Backspace support for editing input
- Color-coded output (cyan for messages, red for errors, yellow for notifications, green for prompts)
- Clear screen functionality
- Input line preservation during message reception
- Separate receive thread for asynchronous message handling
- Automatic encryption detection and decryption
- Base64 encoding/decoding for binary data transmission
- Proper handling of encrypted and plaintext messages
- Username extraction and display
- Automatic disconnect detection
- Graceful connection loss handling
- Reconnection support via address specification
- Support for both local and remote server connections
- Per-room encryption context
- Key derivation on room entry
- Automatic encryption for messages in protected rooms
- Separate handling for password-protected and public rooms
ChatSocket/
├── Client/
│ ├── client.c # Client implementation with raw mode terminal
│ ├── client # Compiled client binary
│ └── run.sh # Client build and run script
├── Server/
│ ├── server.c # Server implementation with room notifications
│ ├── server # Compiled server binary
│ └── run.sh # Server build and run script with ngrok support
├── Utils/
│ ├── aes.c # AES encryption implementation
│ ├── aes.h # AES header
│ ├── sha256.c # SHA-256 implementation
│ ├── sha256.h # SHA-256 header
│ ├── socketUtil.c # Socket utilities and room management
│ └── socketUtil.h # Socket utilities header
├── Dockerfile # Container configuration
└── README.md # This file
- POSIX threads (pthread)
- Standard C library
- POSIX sockets
- POSIX threads (pthread)
- OpenSSL (libssl, libcrypto) - for AES operations
- Standard C library
- POSIX sockets
- termios - for raw mode terminal
cd Server
gcc server.c ../Utils/socketUtil.c ../Utils/sha256.c -o server -lpthread
./serverOr use the provided script:
cd Server
./run.shThe server listens on 0.0.0.0:2077 by default.
cd Server
./run.sh ngrok [port]This will:
- Compile and start the server
- Launch ngrok to expose the server
- Display the public address for clients to connect to
- Show the ngrok dashboard URL (http://localhost:4040)
Example:
./run.sh ngrok 2077Output:
================================================
Server Address: 0.tcp.ngrok.io:12345
ngrok Dashboard: http://localhost:4040
================================================
cd Client
gcc client.c ../Utils/socketUtil.c ../Utils/sha256.c ../Utils/aes.c -o client -lpthread -lssl -lcrypto
./clientOr use the provided script:
cd Client
./run.shcd Client
./run.sh <address:port>Example:
./run.sh 0.tcp.ngrok.io:12345The client connects to 127.0.0.1:2077 by default. Specify an address to connect to a remote server.
/help- Display available commands/name <username>- Set your display name/create <room> -p <password>- Create a password-protected room/create <room>- Create a public room (no encryption)/enter <room>- Enter a room (prompts for password if protected)/leave- Leave current room/clear- Clear the terminal screen/exit- Disconnect from server
-
Start the server (with ngrok for remote access):
cd Server && ./run.sh ngrok 2077
Note the public address displayed.
-
Start a client and set your name:
cd Client && ./run.sh 0.tcp.ngrok.io:12345 >>> /name Alice
-
Create an encrypted room:
>>> /create secure-room -p mypassword123 [*] Room 'secure-room' created -
Enter the room (same client or another client):
>>> /enter secure-room [*] Password: mypassword123 [*] Entered room 'secure-room' -
Send encrypted messages:
>>> Hello, this message is encrypted! << Alice: Hello, this message is encrypted! -
Leave the room:
>>> /leave [*] Left Room
- Type messages in real-time without pressing Enter to submit each character
- Use backspace to edit your message before sending
- Press Enter to send the complete message
- The prompt
>>>indicates you're ready to type
- Green (
>>>): Input prompt - Cyan (
<<): Incoming messages - Yellow (
[*]): System notifications (room entry/exit, status messages) - Red (
[!]): Errors and warnings
- End-to-end encryption ensures server cannot read message content
- AES-256-CTR provides strong encryption
- Salted password hashing with 10,000 iterations
- Constant-time password comparison prevents timing attacks
- Random IV generation for each message
- Encryption keys never transmitted over the network
- Password input hidden in terminal
- Shared room passwords mean all room members have the same decryption key
- No authentication of message origin
- No key exchange mechanism - clients must pre-agree on password
- No protection against replay attacks
- Server can log encrypted messages
- Metadata (usernames, timestamps, room membership) not protected
- ngrok traffic is tunneled through third-party servers
This project is provided as-is for educational purposes.
- SHA-256 implementation based on work by Lucidar
- AES implementation using OpenSSL EVP interface
- Socket programming concepts from STUDevLantern
- C standard library and Berkeley sockets
- ngrok for secure tunneling
Contributions are welcome. Please ensure code follows the existing style and includes appropriate error handling. Key areas for contribution:
- Security enhancements
- Performance optimizations
- Additional features
- Bug fixes
- Documentation improvements
- Test coverage
For issues, questions, or suggestions, please open an issue on the project repository.