Skip to content

Latest commit

 

History

13 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ShadowChat

ShadowChat is a multi-client TCP chat and file sharing system implemented in Python. The project uses a classic Client/Server architecture, where multiple clients connect to one central server through TCP sockets.

The main idea of the project is to send both control messages and real data messages over the same TCP socket. This is implemented using a logical separation called Shadow Control Channel.


Features

  • Multi-client TCP server
  • Thread-based client handling
  • User registration and login
  • Online user management
  • End-to-end encrypted chat
  • Offline encrypted messages
  • Unread message notifications after login
  • Chat history with a specific user
  • File upload to server storage
  • Local file sharing after receiver acceptance
  • Chunk-based binary file transfer
  • Persistent storage using JSON files
  • Better terminal UI using rich and prompt-toolkit
  • Single TCP socket per client

Project Screenshots

Main Client Page

Main Page

Register and Login

Register and Login

Online Users and Encrypted Chat

Send Text

Correct and Wrong Passphrase Behavior

Wrong Passphrase

Offline Message Notification

Offline Text

Server File Upload

Server Upload Start

Server Upload

File Share Accept / Reject

Accept Reject

File Sharing After Acceptance

Share Upload

Architecture Diagram

Architecture

Message Protocol Format

Protocol Message


System Architecture

ShadowChat has three main parts:

  1. Client
  2. Server
  3. Persistent Storage

The client is responsible for user commands, terminal UI, encryption/decryption, sending chat messages, uploading files, and receiving files.

The server is responsible for accepting clients, managing users, routing messages, storing encrypted chat history, coordinating file sharing, and managing file uploads.

Persistent storage is used to save users, uploaded file metadata, and encrypted chat history.


Simplified Architecture

Client  <---- one TCP socket ---->  Server  <---- read/write ---->  Persistent Storage

Inside the same TCP socket:
    - CTRL messages
    - DATA messages

The project does not create a separate socket for control messages. Instead, each message has a type field in its JSON header.

Example:

{
    "type": "CTRL",
    "command": "LOGIN"
}

or:

{
    "type": "DATA",
    "command": "CHAT"
}

This is the Shadow Control Channel mechanism.


Message Frame Format

Every message is sent using a custom frame format:

[4-byte header length][JSON header][optional binary body]

The first 4 bytes specify the size of the JSON header. The JSON header describes the message type, command, body length, and metadata. The optional binary body is used for file chunks.

Example frame header for login:

{
    "type": "CTRL",
    "command": "LOGIN",
    "username": "esta",
    "password": "123"
}

Example frame header for encrypted chat:

{
    "type": "DATA",
    "command": "CHAT",
    "target": "dudeA",
    "ciphertext": "...",
    "encrypted": true
}

Example frame header for file chunk:

{
    "type": "DATA",
    "command": "FILE_CHUNK",
    "filename": "report.pdf",
    "chunk_number": 1,
    "chunk_size": 1024
}

Project Structure

ShadowChat/
│
├── server.py
├── client.py
│
├── utils/
│   ├── protocol.py
│   └── crypto_utils.py
│
├── server_core/
│   ├── user_manager.py
│   ├── chat_manager.py
│   ├── file_manager.py
│   └── share_manager.py
│
├── storage/
│   ├── users.json
│   ├── chats.json
│   ├── files.json
│   └── uploaded_files/
│
├── downloads/
│
└── images/
    ├── main_page.png
    ├── register_login.png
    ├── send_text.png
    ├── wrong_passphrase.png
    ├── offline_text.png
    ├── server_upload_start.png
    ├── server_upload.png
    ├── accept_reject.png
    ├── share_upload.png
    ├── architecture.png
    └── protocol_msg.png

Requirements

This project uses Python 3.

Install the required packages:

pip install rich prompt-toolkit cryptography

Used libraries:

  • socket
  • threading
  • json
  • struct
  • os
  • uuid
  • rich
  • prompt-toolkit
  • cryptography

How to Run

First, start the server:

python3 server.py

Then open one or more new terminals and start clients:

python3 client.py

Each client connects to the server using TCP.

Default host and port:

HOST = "127.0.0.1"
PORT = 5000

Client Commands

Register

REGISTER <username> <password>

Example:

REGISTER esta 123

Login

LOGIN <username> <password>

Example:

LOGIN esta 123

Logout

LOGOUT

Logs out the current user but keeps the client program open.


List Online Users

LIST_USERS

Shows users that are currently online.


Set Chat Encryption Key

SET_KEY <username> <secret>

Example:

SET_KEY dudeA esta_dudeA_passphrase

Both users must set the same shared secret for their conversation.

Example:

esta:  SET_KEY dudeA esta_dudeA_passphrase
dudeA: SET_KEY esta esta_dudeA_passphrase

Send Encrypted Chat Message

CHAT <target_user> <message>

Example:

CHAT dudeA salam doost

Before sending a chat message, the user must set an encryption key for the target user using SET_KEY.


View Chat History

LIST_CHAT <username>

Example:

LIST_CHAT dudeA

The server returns encrypted messages, and the client decrypts them locally if the correct key is set.


Upload File to Server

UPLOAD <filepath>

Example:

UPLOAD report.pdf

The file is uploaded to the server in chunks and stored in:

storage/uploaded_files/

List Uploaded Files

LIST_FILES

Shows files uploaded to server storage.


Share Local File With Another User

SHARE <filepath> <target_user>

Example:

SHARE report.pdf dudeA

The file is not sent immediately. First, the receiver gets a share request. If the receiver accepts, the upload starts.


Accept File Request

ACCEPT_FILE <filename>

Example:

ACCEPT_FILE report.pdf

Reject File Request

REJECT_FILE <filename>

Example:

REJECT_FILE report.pdf

Help

HELP

Shows available commands.


Exit

EXIT

Closes the client connection.


End-to-End Encryption

Chat messages are encrypted on the client side before being sent to the server.

The server stores only ciphertext in:

storage/chats.json

The server does not know the original plaintext message.

The encryption key is derived from:

sorted usernames + shared passphrase

This means both users must use the same shared secret for the same conversation.

Example:

esta  -> SET_KEY dudeA my_secret
dudeA -> SET_KEY esta my_secret

If the wrong key is used, the client cannot decrypt the message.


Offline Messages

If a user sends a chat message to an offline user, the message is still saved by the server.

The server stores the encrypted message and marks it as unread.

When the receiver logs in, the server sends an unread notification.

The user can then run:

LIST_CHAT <username>

to view and decrypt the saved conversation.


File Upload

File upload is done in chunks.

Default chunk size:

CHUNK_SIZE = 1024

The client reads a file in binary mode and sends each chunk separately.

The server writes the chunks to a file and checks whether the final received size matches the expected file size.


File Sharing

There are two different file-related features:

1. Upload to Server

UPLOAD <filepath>

This stores the file on the server.

2. Share Local File With User

SHARE <filepath> <target_user>

This sends a share request first. The file is only transferred if the receiver accepts the request.

Flow:

Sender -> SHARE file.txt receiver
Server -> sends share request to receiver
Receiver -> ACCEPT_FILE file.txt
Server -> tells sender to start upload
Sender -> sends file chunks
Receiver -> saves file in downloads/

Received shared files are saved in:

downloads/

Shadow Control Channel

The project uses only one TCP socket between each client and the server.

Instead of creating separate sockets for control and data messages, the protocol separates messages logically.

Control messages:

CTRL

Examples:

LOGIN
LOGOUT
LIST_USERS
LIST_FILES
SHARE_REQUEST
ACCEPT_FILE
REJECT_FILE

Data messages:

DATA

Examples:

CHAT
FILE_CHUNK
SHARE_FILE_CHUNK
INCOMING_FILE_CHUNK

This design keeps one physical TCP connection but still allows the program to distinguish different message types.


Multi-Client Handling

The server uses one thread per connected client.

When a new client connects, the server creates a new thread:

client_thread = threading.Thread(
    target=handle_client,
    args=(client_socket, client_address),
    daemon=True,
)
client_thread.start()

This allows several clients to use the system at the same time.

Locks are used to protect shared data such as:

  • online users
  • registered users
  • uploaded files
  • active file transfers
  • chat history

Each online user also has a send_lock to prevent multiple threads from writing to the same socket at the same time.


Persistent Storage

The server stores data in JSON files.

storage/users.json

Stores registered users.

storage/chats.json

Stores encrypted chat messages.

storage/files.json

Stores metadata for uploaded files.

storage/uploaded_files/

Stores uploaded binary files.


Error Handling

The system handles common errors such as:

  • duplicate username
  • wrong password
  • user already logged in
  • command without authentication
  • target user not found
  • missing encryption key
  • wrong encryption key
  • invalid file path
  • incomplete file transfer
  • offline file share target
  • unknown command
  • unexpected client disconnect

Example server response:

ERROR authentication required

Another example:

ERROR target user not found

Example Usage Scenario

Terminal 1:

python3 server.py

Terminal 2:

python3 client.py

Client A:

REGISTER esta 123
LOGIN esta 123

Terminal 3:

python3 client.py

Client B:

REGISTER dudeA 123
LOGIN dudeA 123

Set encryption keys:

esta:  SET_KEY dudeA shared_secret
dudeA: SET_KEY esta shared_secret

Send message:

esta: CHAT dudeA hello

View chat history:

dudeA: LIST_CHAT esta

Upload file:

esta: UPLOAD report.pdf

Share local file:

esta: SHARE report.pdf dudeA
dudeA: ACCEPT_FILE report.pdf

Notes

This project was developed for a computer networks course and focuses on:

  • TCP socket programming
  • client/server architecture
  • message protocol design
  • multi-client concurrency
  • logical control/data separation
  • file transfer using chunks
  • encrypted chat messages
  • offline message storage

The project is intentionally terminal-based to focus on socket programming and protocol design rather than GUI development.


License

This project is for educational use.

About

ShadowChat is Midterm project of Computer Networks course at AmirKabir University.

Topics

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages