I want to share my side project — SecureShare. It's a desktop app for direct file transfer between two computers with end-to-end encryption.
This post isn't about how great it is. It's more about the technical decisions, the pitfalls, and why certain things are built the way they are. I'd love to hear your thoughts and criticism.
The Problem
I needed to send a confidential document to someone in another city. My options:
- Google Drive / Dropbox — the file sits on someone else's servers, the company has access
- Telegram — convenient, but files are stored on their servers, and "secret chats" don't handle files well
- Email — no comment
- WeTransfer — the file is temporarily stored on their servers
All of these have the same issue: my file ends up on someone else's server, and I don't control what happens to it next.
I wanted something simple: send a file directly, encrypted on my device, decrypted only on the recipient's device. No sign-ups, no accounts.
How It Works
The architecture is fairly straightforward:
Sender ←—WSS—→ Relay Server ←—WSS—→ Receiver
- Both clients connect to the relay server via WebSocket (TLS)
- X25519 key exchange (Elliptic Curve Diffie-Hellman)
- Both sides see a verification code — like Signal's safety numbers, to confirm no one is intercepting
- The file is encrypted with AES-256-GCM and sent in chunks
- SHA-256 hash verification at the end
The relay server is literally a pipe. It receives encrypted bytes from the sender and forwards them to the receiver. The server doesn't know the file name, its contents, or even its size (because metadata is also encrypted).
Why These Choices
X25519 + AES-256-GCM
I thought about this for a while. XChaCha20-Poly1305 is often recommended for new projects — it's simpler and less sensitive to nonce misuse. But I went with AES-256-GCM for a few reasons:
- Broad support in Python's
cryptographylibrary - Hardware acceleration via AES-NI on most CPUs
- Secure enough with proper nonce management
To prevent nonce reuse, I built a prefix system: each side gets its own 4-byte prefix (determined by comparing public keys), plus an 8-byte counter. This guarantees nonces never repeat.
Honestly, if I were starting over — I might choose XChaCha20. Less headache with nonces. But AES-GCM works, and I'm not planning to change it.
Verification Code
One thing that frustrated me about other solutions — no MITM protection. If the relay server is compromised, it could swap keys. So both users see a short code (first 8 chars of SHA-256 of the shared secret) and must compare it. Like Signal's safety number verification.
It's not perfect — users can ignore the verification. But at least the mechanism exists.
WebSocket Relay Instead of P2P
The first version tried to work peer-to-peer via MQTT. It was a nightmare: NAT traversal, STUN/TURN, firewalls... Eventually I gave up and built a simple WebSocket relay on a VPS. Yes, this adds a single point of failure, but:
- Works through any NAT and firewall
- No network configuration needed
- The relay is fully self-hostable — spin up your own in 5 minutes via Docker
# docker-compose.yml — the entire server
services:
relay:
build: .
restart: unless-stopped
caddy:
image: caddy:2
ports: ["80:80", "443:443"]
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
Auto-reconnect and Resume
This is a feature I didn't plan but turned out to be critical. Imagine: you're transferring a 3 GB file, and at 80% your Wi-Fi drops for 10 seconds.
Without resume — start over. With resume — the client automatically reconnects, re-does the key exchange, and continues from where it left off. The receiver stores a .resume manifest with information about received chunks.
This was tricky to get right. Especially the part where a reconnect needs a new key exchange (because the connection is new), but verification can be skipped (because a reconnect_token proves identity).
The Stack
| Component | Technology |
|---|---|
| Client | Python + CustomTkinter |
| Encryption | cryptography (X25519, AES-256-GCM, HKDF) |
| Relay server | Python asyncio + websockets |
| TLS | Caddy + Let's Encrypt |
| DNS | DuckDNS |
| Build | PyInstaller (.exe / Linux binary) |
Why Python? Because it's a side project, and I wanted to move fast. CustomTkinter gives a decent-looking GUI with minimal code. Yes, the .exe is ~30 MB (PyInstaller), and it doesn't start instantly. If this were a commercial product — I'd pick something else.
What I Don't Like
I'll be honest — there are things that annoy me too:
- One file per session. Want to send 5 files? Pack them into an archive. It's a protocol limitation, and changing it basically means rewriting the transfer logic
- No macOS build. PyInstaller + CustomTkinter + macOS = suffering. It runs from source, but I don't build a .app
- 5 GB limit. Server-side limit to prevent one user from hogging the channel. Enough for most tasks, but not all
- GUI could be better. CustomTkinter is a compromise. Looks okay, but it's not Electron and not a native UI
What It Looks Like
The interface supports three languages (Ukrainian, English, German) with live switching — no restart needed. Dark theme by default.
Here's a real transfer of a 2.9 GB file between two devices:
The sender generates a session code (ooxv-zhef) and shares it with the receiver. After connecting, both see a verification code (FD79-FDBB) — if it matches, no one has intercepted the connection. Then the file flies through as an encrypted stream at ~15 MB/s.
At the bottom of the window — a log showing every step: file hash, relay connection, key exchange, verification, transfer. Full transparency.
What's Next
I have a few ideas, but I'm not sure about priorities:
- Web version (so you don't need to download .exe)
- Folder transfer support
- Better verification UX (maybe QR codes?)
- macOS build
If you have thoughts on what matters most — I'd love to hear it.
Links
- Website: https://secureshare-relay.duckdns.org
- GitHub: https://github.com/artmarchenko/SecureShare
- License: MIT
I'd appreciate any feedback — especially on the cryptography and architecture. If you see holes in the security model — please tell me, that's the most valuable input.
Thanks for reading!




Top comments (0)