End-to-end encrypted peer-to-peer messaging over local networks — powered by a from-scratch Blowfish cipher implementation.
A fully working encrypted chat application where every message is locked with the Blowfish block cipher before it ever leaves your machine. No cloud, no server infrastructure, no third parties. Two devices. One shared key. Zero compromise.
- Why I Built This
- Demo
- What This Is
- Architecture Diagram
- How It Works
- The Blowfish Algorithm — From Scratch
- Project Structure
- Getting Started
- Android Support
- Network Modes
- Test Cases
- Security Properties
- Threat Model — What This Does Not Protect Against
- Known Limitations
- Where This Can Be Used
- Scope and Future Direction
- What I Learned
- Impact
- Built With
- References
- About Me
- License
Most developers use encryption libraries without ever seeing what happens inside them. I wanted to understand — not just call encrypt(), but actually build the cipher, the key schedule, the authentication layer, and the network protocol myself, from the ground up, using nothing but the Python standard library.
This project started as a 6th-semester Computer Networking assignment, but it became something bigger: a way to prove to myself that I could take a 1993 academic cipher specification (Schneier's original Blowfish paper) and turn it into a working, secure, real-time messaging system — including the parts most tutorials skip, like anti-replay protection, timing-safe comparisons, and authenticated encryption.
I'm sharing it because I believe the best way to learn security is to build it, break it, and fix it yourself — and I want to keep doing that at a deeper level.
2026-04-27-02-37-48_kVKgipRV.mp4
Server and client running side by side. Every message travels as Blowfish-CBC ciphertext — decrypted only on arrival.
chat_app_mobile_clint.mp4
Terminal client running on Android (Pydroid 3), connecting to the PC server over the same WiFi router. Cross-device, fully encrypted.
Offline Secured Chat is a peer-to-peer encrypted messaging system that works entirely over a local area network — no internet required. It demonstrates how real cryptographic protocols are built by combining multiple security primitives:
- 🔒 Confidentiality — Blowfish-CBC encrypts every message
- ✅ Integrity — HMAC-SHA256 detects any tampering
- 🔁 Anti-replay — Sequence numbers block replayed packets
- 🎲 Freshness — A new random IV is generated per message
The Blowfish algorithm is implemented entirely from scratch in Python — no pycryptodome, no cryptography library, no shortcuts. Every component: the key schedule, P-array, S-boxes, Feistel rounds, CBC mode, and PKCS#7 padding is hand-written and documented.
The diagram above shows the complete message lifecycle end to end:
- Sender side — plaintext is paired with a fresh random IV, encrypted with Blowfish-CBC, tagged with an HMAC over the sequence number, IV, and ciphertext, then serialized and framed for the TCP socket.
- Key derivation (center) — both peers derive independent encryption and MAC keys from one pre-shared secret using SHA-256 with domain separation, so a compromise of one key never exposes the other.
- LAN transport — one peer listens, the other connects; no cloud, no internet, no third-party server ever touches the traffic.
- Receiver side — the packet is parsed, its version and sequence number checked, its HMAC verified in constant time, and only then decrypted back into plaintext.
(Place the diagram image at ./assets/architecture-diagram.png in your repository, or update the path above to match wherever you store it.)
User A types a message
│
▼
IV = os.urandom(8) ← Fresh random IV per message
│
▼
ciphertext = Blowfish_CBC(plaintext, enc_key, IV)
│
▼
tag = HMAC_SHA256(mac_key, seq ║ IV ║ ciphertext)
│
▼
{ version, seq, IV, ciphertext, hmac } → TCP socket
│
▼
User B verifies HMAC → checks seq → decrypts → reads message
| Layer | Mechanism | Purpose |
|---|---|---|
| Encryption | Blowfish-CBC (256-bit key) | Message confidentiality |
| Authentication | HMAC-SHA256 | Tamper detection |
| Key derivation | SHA-256 with domain separation | Independent enc/mac keys |
| IV generation | os.urandom(8) |
Prevents pattern analysis |
| Anti-replay | Strictly-increasing sequence numbers | Blocks packet replay |
| Timing safety | hmac.compare_digest() |
Timing attack resistance |
Blowfish (Bruce Schneier, 1993) is a symmetric block cipher with:
- Block size: 64 bits (8 bytes)
- Key length: 32–448 bits (4–56 bytes)
- Structure: 16-round Feistel network
The implementation in blowfish.py covers every component:
Key Schedule
├── Initialize P-array (18 subkeys) from pi digits
├── Initialize 4 S-boxes (256 entries each) from pi digits
└── XOR key into P-array → run 521 Blowfish encryptions
→ produces fully key-dependent subkeys and S-boxes
Encryption (per 64-bit block)
├── Split into 32-bit halves (xL, xR)
├── 16 rounds:
│ ├── xL = xL XOR P[i]
│ ├── xR = F(xL) XOR xR
│ └── swap xL, xR
└── Output whitening with P[16], P[17]
F-Function
├── Split x into four 8-bit pieces: a, b, c, d
└── ((S[0][a] + S[1][b]) XOR S[2][c]) + S[3][d]
CBC Mode
├── Each block XOR'd with previous ciphertext block
├── First block XOR'd with random IV
└── Decryption reverses the chain
offline-secured-chat/
│
├── blowfish.py # Blowfish cipher from scratch (ECB + CBC)
├── secure_protocol.py # Key derivation, packet framing, HMAC, anti-replay
├── server.py # GUI server — dark-themed tkinter (User B)
├── client.py # GUI client — dark-themed tkinter (User A)
├── client_mobile.py # Single-file GUI client for Android (Pydroid 3)
├── mobile_terminal_client.py # Zero-dependency terminal client (Termux/Pydroid)
├── test_cases.py # 15 automated test cases
└── assets/
└── architecture-diagram.png
- Python 3.10+ (tested on 3.12.2)
- No external libraries — everything uses the Python standard library
- Two devices on the same WiFi network (or same machine for testing)
python server.pyThe server window opens and displays your local IP address in the header. Share this IP with the other user.
python client.pyA dialog will ask for the server's IP. Enter the IP shown in the server window.
For same-machine testing, enter
127.0.0.1
python test_cases.pyAll 15 test cases should pass.
The project includes two options for running on Android:
Option 1 — GUI client (client_mobile.py) — single self-contained file with the full Blowfish implementation and tkinter UI. Paste all files into the same Pydroid 3 folder.
Option 2 — Terminal client (mobile_terminal_client.py) — zero-dependency console app. Works in Pydroid 3, Termux, or any Python terminal. Just type and press Enter.
Install Pydroid 3 from Play Store
→ Open mobile_terminal_client.py
→ Tap Run
→ Enter server IP when prompted
→ Chat securely
| Setup | Server IP to enter | Notes |
|---|---|---|
| Same machine | 127.0.0.1 |
Testing only |
| PC ↔ PC (WiFi) | Server's LAN IP | Both on same router |
| PC ↔ Android (WiFi) | Server's LAN IP | Both on same router |
| Android ↔ Android | Either device's IP | Both on same router |
Important: Both devices must be connected to the same WiFi router or hotspot. This is a LAN-only application — it does not use the internet.
15 automated tests validate correctness and security:
| # | Test | What It Validates |
|---|---|---|
| TC-01 | Basic ECB encrypt/decrypt | Core algorithm correctness |
| TC-02 | Empty string | Edge case — zero-length input |
| TC-03 | Long message (500+ chars) | Multi-block encryption |
| TC-04 | Special chars + Unicode | UTF-8 and symbol handling |
| TC-05 | Different keys → different output | Key sensitivity |
| TC-06 | Wrong key fails to decrypt | Key uniqueness enforcement |
| TC-07 | Numeric string | Data type coverage |
| TC-08 | Urdu/Arabic Unicode | Multi-byte character support |
| TC-09 | Ciphertext type is bytes | Output type contract |
| TC-10 | Ciphertext ≠ plaintext | Encryption actually changes data |
| TC-11 | Minimum key (4 bytes) | Boundary condition |
| TC-12 | Maximum key (56 bytes) | Boundary condition |
| TC-13 | CBC mode round-trip | Mode correctness |
| TC-14 | Tampered packet rejected | HMAC integrity enforcement |
| TC-15 | Replay packet rejected | Anti-replay enforcement |
| Property | Status | Implementation |
|---|---|---|
| Confidentiality | ✅ | Blowfish-CBC, 256-bit derived key |
| Integrity | ✅ | HMAC-SHA256 per packet |
| Authenticity | ✅ | HMAC verifies correct sender key |
| Anti-replay | ✅ | Strictly-increasing sequence numbers |
| IV uniqueness | ✅ | os.urandom(8) per message |
| Key separation | ✅ | SHA-256 domain-separated derivation |
| Timing safety | ✅ | hmac.compare_digest() |
Being explicit about limitations is part of responsible security engineering. This project does not protect against:
- Key compromise — if the pre-shared key leaks, all past and future messages encrypted with it can be decrypted. There is no forward secrecy.
- Endpoint compromise — if either device is compromised (malware, physical access), the attacker reads plaintext directly from memory, regardless of the encryption in transit.
- Man-in-the-middle during key exchange — because the key is shared out-of-band (manually, by the users), the protocol itself does not authenticate that the key was exchanged safely.
- Traffic analysis — an observer on the LAN can see that two devices are communicating, and roughly how much data, even though they cannot read the content.
- Denial of service — the protocol does not defend against a flood of malformed or excessive packets.
This scope was intentional: the goal was to build and understand authenticated symmetric encryption correctly, not to solve key distribution or network-layer security, which are separate, well-studied problems.
- Pre-shared key — both users must agree on the same secret key in advance. No automated key exchange is implemented.
- LAN only — no NAT traversal. Internet deployment requires a relay.
- One-to-one — current architecture is one server, one client per session.
- No persistence — chat history disappears when the window closes.
Offline-first environments: Private networks where internet connectivity is unavailable or untrusted — factory floors, field operations, internal lab networks, air-gapped environments.
Education and security research: A readable, documented, scratch implementation of a real cipher for learning how symmetric encryption, CBC mode, and authenticated encryption actually work at the bit level.
LAN communication: Any two devices on the same network — home, office, campus — that need private, tamper-evident messaging without routing traffic through a cloud service.
Embedded / IoT prototyping: Pure Python, no dependencies. Adaptable for Raspberry Pi, microcontrollers running MicroPython, or any environment with socket support.
This project is a working foundation. Natural extensions include:
- Asymmetric key exchange — Replace the pre-shared key with RSA or Diffie-Hellman so two strangers can establish a secure session without meeting first
- Group chat — A relay server that forwards encrypted messages between multiple clients
- File transfer — Encrypt and send arbitrary files over the same protocol
- Internet support — NAT traversal or a relay server to connect devices across different networks
- Native mobile app — Rebuild the client in Kotlin with Jetpack Compose for a proper Android experience
- Persistent history — Store encrypted conversation logs locally with SQLite
Building this project from scratch taught me lessons that using a library never would have:
- Symmetric cryptography, at the bit level — implementing the Feistel network, P-array/S-box key schedule, and F-function by hand gave me a real understanding of why Blowfish is secure, not just that it is.
- Authenticated encryption is not optional — encryption alone (confidentiality) does nothing to stop tampering; I learned firsthand why encrypt-then-MAC is the standard pattern, and implemented it rather than just reading about it.
- Small details break security — using
==instead ofhmac.compare_digest()for tag comparison introduces a timing side-channel; reusing an IV breaks CBC's security guarantees. Security is won or lost in details like these. - Protocol design beyond the cipher — anti-replay via sequence numbers, packet framing, and version negotiation taught me that a "secure chat app" is a systems problem, not just a math problem.
- Testing security code differently — my 15 test cases don't just check correctness (does it decrypt correctly?) but also check failure modes (does it correctly reject a tampered or replayed packet?), which is a different mindset from typical functional testing.
- Writing for other readers — documenting the cipher clearly enough that someone else could learn from the code, not just run it, was its own exercise in communication.
- Serves as a teaching resource: the from-scratch Blowfish implementation is fully documented and can be used by students studying cryptography or network security to see a real cipher built line by line, rather than as a black box.
- Provides a usable tool for environments where internet-based chat apps are unavailable or untrusted — field teams, labs, and offline facilities can communicate securely over a local network with zero setup cost.
- Demonstrates, in a single self-contained project, the full stack of an authenticated encryption system — key derivation, encryption, integrity, and anti-replay — which is a pattern directly transferable to real-world secure systems.
- Reflects my broader goal: to pursue graduate study and research in cryptography and applied security, building on projects like this one that combine theoretical understanding with working implementation.
| Technology | Role |
|---|---|
| Python 3.12 | Language |
socket |
TCP networking |
tkinter |
Desktop GUI |
hashlib |
SHA-256 key derivation |
hmac |
HMAC-SHA256 authentication |
os.urandom |
Cryptographic IV generation |
struct |
Binary packet framing |
threading |
Concurrent send/receive |
No external cryptographic libraries. All cipher logic is original.
- Schneier, B. (1993). Description of a New Variable-Length Key, 64-Bit Block Cipher (Blowfish). Fast Software Encryption, Cambridge Security Workshop.
- Python
hmacdocumentation - Python
hashlibdocumentation - Stallings, W. Cryptography and Network Security, 7th Edition.
I'm a 6th-semester Computer Networking student with a strong interest in applied cryptography and systems security. This project reflects my approach to learning: understand a concept deeply enough to build it from first principles, not just use it.
MIT License — free to use, modify, and distribute.