A secure, end-to-end encrypted chat application built with React and Python Flask that ensures only the sender and intended receiver can read messages.
- π End-to-End Encryption: Messages are encrypted using AES-256-GCM on the client side
- π RSA Key Exchange: Secure session key exchange using RSA-2048 encryption
- π€ User Authentication: Secure registration and login with session tokens
- π¬ Real-time Messaging: Send and receive encrypted messages instantly
- π User Search: Find and start conversations with other users
- π± Responsive Design: Works on desktop and mobile devices
- π‘οΈ Zero Server Access: Server never sees plaintext messages or private keys
- π Automatic Key Exchange: Encrypted session keys sent with first message
- Client-side Encryption: All encryption/decryption happens in the browser using Web Crypto API
- Hybrid Cryptography: RSA-2048 for key exchange + AES-256-GCM for message encryption
- Key Management: Private keys stored only in browser localStorage, never transmitted
- Session Key Sharing: Master AES key encrypted with recipient's public RSA key
- Automatic Decryption: Receivers automatically decrypt session keys using their private RSA key
- Frontend: React 18 + Vite (Web Crypto API for encryption)
- Backend: Python Flask (message routing, authentication)
- Database: SQLite (stores only public keys and encrypted messages)
- Cryptography:
- RSA-2048 for asymmetric key exchange
- AES-256-GCM for symmetric message encryption
- SHA-256 for key derivation and hashing
- PBKDF2 for key derivation in simplified crypto
isc/
βββ backend/
β βββ app.py # Flask application entry point
β βββ database.py # SQLite database operations
β βββ requirements.txt # Python dependencies
β βββ .env.example # Environment variables template
β βββ chat.db # SQLite database (auto-generated)
β βββ routes/
β βββ auth.py # Authentication endpoints
β βββ chat.py # Chat and messaging endpoints
βββ frontend/
β βββ package.json # Node.js dependencies
β βββ vite.config.js # Vite configuration
β βββ index.html # HTML template
β βββ src/
β β βββ App.jsx # Main application component
β β βββ main.jsx # Application entry point
β β βββ index.css # Global styles
β β βββ components/ # React components
β β β βββ Login.jsx
β β β βββ Register.jsx
β β β βββ ChatWindow.jsx
β β β βββ ContactList.jsx
β β βββ utils/ # Utility functions
β β βββ simple-crypto.js # Simplified E2EE implementation (active)
β β βββ crypto.js # Complex crypto utilities
β β βββ simpleCrypto.js # Alternative crypto (for testing)
β β βββ api.js # Axios API client
β βββ public/ # Static assets
βββ README.md
- Python 3.8+ (Python 3.13.3 recommended)
- Node.js 16+ and npm
- Modern web browser with Web Crypto API support
# Clone the repository
git clone <repository-url>
cd encrypted-chat
# Or if you're starting fresh, create the directory structure as shown above# Navigate to backend directory
cd backend
# Create and activate virtual environment (optional but recommended)
python -m venv venv
# On Windows:
venv\Scripts\activate
# On Unix/MacOS:
source venv/bin/activate
# Install Python dependencies
pip install -r requirements.txt
# The database (chat.db) will be created automatically on first run# Navigate to frontend directory (from project root)
cd frontend
# Install Node.js dependencies
npm installcd backend
python app.pyThe Flask API will start on http://localhost:5000
cd frontend
npm run devThe React app will start on http://localhost:3000
Open your browser and navigate to http://localhost:3000
The application uses a simplified master key approach for easier key management:
-
User Registration:
- Client generates RSA-2048 key pair using Web Crypto API
- Client generates a random AES-256 master key
- RSA public key sent to server for storage
- RSA private key and AES master key stored only in browser localStorage
-
Starting a Conversation:
- Sender fetches recipient's public RSA key from server
- Sender encrypts their AES master key with recipient's public RSA key
- Encrypted master key sent along with the first message
-
Receiving Messages:
- Recipient receives encrypted master key from sender
- Recipient decrypts master key using their RSA private key
- Recipient stores decrypted master key for future message decryption
- All subsequent messages from that sender use the same master key
Sender Server Receiver
| | |
|-- Generate AES master key ----| |
|-- Encrypt msg with AES -------| |
|-- Encrypt master key w/ RSA --| |
|-- Send both to server ------->|-- Store encrypted ---> |
| | |-- Decrypt master key (RSA) --|
| | |-- Decrypt message (AES) -----|
-
Message Encryption (Sender):
// SimpleCrypto.encrypt(message) const masterKey = await getMasterKey(); // Get or generate AES-256 key const iv = crypto.getRandomValues(new Uint8Array(12)); const encrypted = await crypto.subtle.encrypt( { name: 'AES-GCM', iv: iv }, masterKey, messageText ); // Encrypt master key for recipient const encryptedMasterKey = await encryptKeyFor(recipientUsername);
-
Server Storage:
- Server receives:
{sender, receiver, encrypted_content, iv, encrypted_session_key} - Database stores only encrypted data
- No access to plaintext message or encryption keys
- Server receives:
-
Message Decryption (Receiver):
// SimpleCrypto.decryptAndStoreSessionKey(encryptedKey) const privateKey = await getRSAKeys(); // Get RSA private key const masterKeyBytes = await crypto.subtle.decrypt( { name: 'RSA-OAEP' }, privateKey, encryptedSessionKey ); // Store decrypted master key // SimpleCrypto.decrypt(ciphertext, iv) const decrypted = await crypto.subtle.decrypt( { name: 'AES-GCM', iv: iv }, masterKey, encryptedContent );
- AES-256-GCM: Military-grade symmetric encryption for message content
- RSA-2048: Strong asymmetric encryption for master key exchange
- Client-side Only: All encryption/decryption happens in browser
- Zero Server Knowledge: Server never has access to plaintext or private keys
- AES-GCM: Built-in authentication prevents message tampering
- Cryptographic Hashing: SHA-256 used for key derivation
- Session Management: Secure token-based authentication
- Message Authentication: GCM mode provides authenticated encryption
- RSA Key Pairs: Verify user identity through public key cryptography
- Secure Authentication: Password hashing with werkzeug security
- Session Tokens: Prevent unauthorized access
- Public Key Infrastructure: Users verified by their registered public keys
- Master Key Approach: Single AES key per user simplifies key management
- Secure Key Exchange: Master keys encrypted with RSA-2048
- Automatic Decryption: Receivers automatically decrypt and store sender's master key
- Local Key Storage: All private keys stored only in browser localStorage
- Key Isolation: Keys cleared on logout for security
POST /register- Register new user with public keyPOST /login- Authenticate user and create sessionPOST /logout- Invalidate user sessionGET /verify- Verify session token validity
GET /users- Get list of users (for contact discovery)GET /public-key/<username>- Get user's public keyPOST /send- Send encrypted messageGET /messages?with=<username>- Get encrypted messagesGET /conversations- Get list of active conversationsGET /search-users?q=<query>- Search for users
-
Register two users:
- Open two browser windows/tabs (or use incognito mode for second user)
- Register as "alice" in one window, "bob" in another
- Note: Each user gets a unique RSA key pair and AES master key
-
Start a conversation:
- In Alice's window, search for "bob" in the search bar
- Click on Bob's username to start a conversation
- This prepares the encrypted key exchange
-
Send encrypted messages:
- Alice types a message and clicks Send
- The message is encrypted with Alice's master AES key
- Alice's master key is encrypted with Bob's public RSA key
- Both encrypted data sent to server
-
Receive and decrypt:
- Bob opens the conversation with Alice
- Bob's browser automatically decrypts Alice's master key using Bob's private RSA key
- Bob's browser uses Alice's master key to decrypt all messages from Alice
- Messages appear in plaintext for Bob
-
Verify encryption:
- Open browser DevTools β Network tab
- Click on the POST request to
/api/chat/send - In Request payload, verify
encrypted_contentis base64 gibberish - Verify
encrypted_session_keyis also encrypted (base64 RSA ciphertext) - Check database (see "Viewing Encrypted Messages" section below)
You can verify messages are encrypted by viewing the SQLite database:
# Windows Command Prompt
cd backend
sqlite3 chat.dbThen run:
-- View all messages
SELECT id, sender_username, receiver_username,
substr(encrypted_content, 1, 50) as encrypted_preview,
timestamp
FROM messages;
-- Exit
.exitExpected output: You should see base64-encoded gibberish, not plaintext messages.
Alternatively, use DB Browser for SQLite (GUI tool): https://sqlitebrowser.org/
- Key Isolation: Logout and verify keys are cleared
- Network Inspection: Confirm no plaintext in network traffic
- Database Inspection: Verify encrypted storage only
- Cross-Browser: Test key exchange between different browsers
- Single Master Key: Each user has one master key (not per-conversation keys)
- No Key Rotation: Master keys don't automatically rotate
- Group Chat: Currently supports only 1:1 conversations
- File Sharing: No encrypted file transfer capability
- Message Persistence: Keys lost if localStorage is cleared
- No Message Editing: Cannot edit or delete sent messages
- Limited Forward Secrecy: Master key reused across all messages
- Signal Protocol: Implement Double Ratchet for perfect forward secrecy
- Per-Conversation Keys: Generate unique session keys per conversation
- Key Rotation: Automatic periodic key rotation
- WebRTC: Direct peer-to-peer messaging
- PWA Support: Offline capability and mobile app experience
- Key Backup: Secure key backup and recovery system (encrypted cloud backup)
- WebSocket: Real-time message delivery notifications
- File Encryption: Support for encrypted file/image sharing
- Message Status: Read receipts and delivery confirmation
- Multi-Device: Sync encrypted messages across devices
-
CORS Errors:
- Ensure backend is running on port 5000
- Check CORS is enabled in Flask app (already configured)
- Verify frontend is accessing
http://localhost:5000
-
Crypto API Not Available:
- Use HTTPS or localhost (required for Web Crypto API)
- Ensure modern browser support (Chrome 37+, Firefox 34+, Safari 11+)
- Check browser console for detailed errors
-
"Could not decrypt message" Errors:
- This is normal for old messages before key exchange
- Send a new message to establish encrypted session key
- Receiver will auto-decrypt sender's master key on first message
-
Database Issues:
- Delete
chat.dbfile and restart backend to recreate - Check Python dependencies are installed (
pip install -r requirements.txt) - Verify SQLite3 is available on your system
- Delete
-
Registration Fails (500 Error):
- Check backend terminal for Python errors
- Ensure database is writable
- Verify all required fields are provided
-
Keys Lost After Logout:
- This is expected behavior (keys cleared from localStorage)
- Re-login and send new message to re-establish encryption
- Consider implementing key backup for production use
# Check if backend is running
curl http://localhost:5000/api/health
# View database contents
cd backend
sqlite3 chat.db "SELECT * FROM users;"
# Check frontend dependencies
cd frontend
npm list
# Clear browser data
# Open DevTools (F12) β Application β Clear Storage β Clear site dataThis project is created for educational purposes to demonstrate end-to-end encryption implementation. Use and modify as needed for learning and development.
This is a prototype for learning purposes. Feel free to:
- Report bugs and issues
- Suggest security improvements
- Add new features
- Improve documentation