344 lines
17 KiB
Markdown
344 lines
17 KiB
Markdown
<<<<<<< HEAD
|
|
# Kecalek_python
|
|
|
|
=======
|
|
# Encrypted Chat
|
|
|
|
End-to-end encrypted chat s forward secrecy (X3DH + Double Ratchet, Signal Protocol).
|
|
Server ukládá a přeposílá šifrované bloby — nikdy nevidí plaintext.
|
|
|
|
## Soubory
|
|
|
|
### Server
|
|
| Soubor | Účel |
|
|
|--------|------|
|
|
| `server.py` | Asyncio TCP server, handler dispatch, rate limiting, notifikace |
|
|
| `db.py` | MySQL CRUD, jedna connection na volání |
|
|
| `schema.sql` | MySQL schéma (users, conversations, messages, ...) |
|
|
|
|
### Klient
|
|
| Soubor | Účel |
|
|
|--------|------|
|
|
| `gui_client.py` | PyQt6 GUI |
|
|
| `client.py` | CLI klient |
|
|
| `chat_core.py` | Logika klienta — session management, šifrování, lokální klíče |
|
|
|
|
### Sdílené (server + klient)
|
|
| Soubor | Účel |
|
|
|--------|------|
|
|
| `crypto_utils.py` | Ed25519, X25519, AES-256-GCM, HKDF, PBKDF2, X3DH, Double Ratchet (state rollback), Sender Keys (state rollback), ECP1 key encryption |
|
|
| `protocol.py` | Newline-delimited JSON protokol, base64 encoding |
|
|
|
|
## Quick Start
|
|
|
|
1. `pip install -r requirements.txt`
|
|
2. Spustit `schema.sql` v MySQL (kompletní clean start). Pro migraci existující DB: `migration_multi_device.sql`.
|
|
3. `python server.py`
|
|
4. Klient: `python client.py` (CLI) nebo `python gui_client.py` (GUI, PyQt6)
|
|
|
|
## Jak funguje šifrování
|
|
|
|
### Klíče na uživatele
|
|
| Klíč | Typ | Účel |
|
|
|------|-----|------|
|
|
| RSA-4096 | Asymetrický | Pouze login challenge-response. Šifrovaný PBKDF2 (600k iterací) + AES-256-GCM. |
|
|
| Identity Key (IK) | Ed25519 | Podpisy, konverze na X25519 pro X3DH. Šifrovaný PBKDF2 (600k iterací) + AES-256-GCM. |
|
|
| Signed Pre-Key (SPK) | X25519 | DH v X3DH, podepsaný IK. **Rotuje se každých 7 dní** s grace periodem pro in-flight X3DH. |
|
|
| One-Time Pre-Keys (OPK) | X25519 | Jednorázové, spotřebuje se při X3DH, automaticky doplňované (< 20 → +50) |
|
|
|
|
### DM (1:1 zprávy) — X3DH + Double Ratchet
|
|
1. Alice chce napsat Bobovi poprvé → stáhne jeho key bundle (IK, SPK, OPK) ze serveru.
|
|
2. X3DH: 4 DH výpočty → shared secret.
|
|
3. Double Ratchet inicializován ze shared secret.
|
|
4. Každá zpráva: symmetric ratchet (HMAC chain) → message key → AES-256-GCM.
|
|
5. Každá odpověď: DH ratchet (nový X25519 keypair) → nový root key + chain key.
|
|
6. Per-recipient ciphertext — každý recipient má vlastní šifrovaný blob.
|
|
7. Při selhání dešifrování: automatický rollback stavu ratchetu (snapshot/restore).
|
|
|
|
### Skupiny — Sender Keys
|
|
1. Každý člen má vlastní sender key chain pro skupinu.
|
|
2. Sender key se distribuuje ostatním členům přes pairwise Double Ratchet (jako DM).
|
|
3. Skupinové zprávy: symmetric ratchet na sender key → AES-256-GCM.
|
|
4. Jeden ciphertext pro celou skupinu (efektivní).
|
|
|
|
### Lokální úložiště klíčů
|
|
```
|
|
~/.encrypted_chat/{email}/
|
|
private.pem # RSA (login) — ECP1 formát s heslem, PEM bez hesla
|
|
public.pem # RSA (login)
|
|
identity_private.bin # Ed25519 — ECP1 formát s heslem, 32B raw bez hesla
|
|
identity_public.bin # Ed25519
|
|
device_id.txt # UUID tohoto zařízení
|
|
spk_private.bin # Aktuální signed prekey (šifrovaný AES-256-GCM)
|
|
spk_id.txt
|
|
prev_spk_private.bin # Předchozí SPK, grace period (šifrovaný AES-256-GCM)
|
|
prev_spk_id.txt
|
|
opk_private/ # One-time prekeys (šifrované AES-256-GCM)
|
|
{opk_id}.bin
|
|
login_lockout.json # Brute-force lockout stav (failed_attempts, locked_until)
|
|
sessions/ # Double Ratchet stavy (šifrované AES-256-GCM)
|
|
{user_id}_{device_id}.bin
|
|
sender_keys/ # Vlastní sender keys pro skupiny
|
|
{conv_id}.bin
|
|
sender_keys_recv/ # Přijaté sender keys od ostatních
|
|
{conv_id}_{sender_id}_{device_id}.bin
|
|
```
|
|
|
|
## Bezpečnostní hardening
|
|
|
|
### Šifrování privátních klíčů na disku (ECP1 formát)
|
|
RSA a Ed25519 privátní klíče šifrované heslem používají vlastní formát ECP1 (Encrypted Chat PBKDF v1):
|
|
- **PBKDF2-HMAC-SHA256** s 600 000 iteracemi (OWASP 2023 doporučuje 480k+)
|
|
- **AES-256-GCM** pro šifrování, magic bytes "ECP1" jako AAD
|
|
- **Formát:** `ECP1(4B) + salt(16B) + nonce(12B) + ciphertext+tag`
|
|
- **Zpětná kompatibilita:** Staré PEM soubory (z `BestAvailableEncryption`) se načtou automaticky a při dalším uložení se přešifrují do ECP1.
|
|
|
|
### Šifrování SPK/OPK na disku
|
|
SPK a OPK privátní klíče jsou šifrované AES-256-GCM klíčem `_local_key` (HKDF z Ed25519 identity key):
|
|
- Při save: `_encrypt_local(raw, local_key)` → `nonce(12B) + tag(16B) + ciphertext`
|
|
- Při load: `_decrypt_local()` s transparentní migrací — pokud dešifrování selže, načte jako plaintext a uloží šifrovaně
|
|
- Aplikováno na `spk_private.bin`, `prev_spk_private.bin`, `opk_private/*.bin`
|
|
|
|
### Brute-force ochrana (client-side lockout)
|
|
Po chybném zadání hesla se prodlužuje čas do dalšího pokusu:
|
|
- **Vzorec:** `min(2^N, 300)` sekund, kde N = počet neúspěšných pokusů (2s, 4s, 8s, ... až 5 min)
|
|
- **Stav:** `login_lockout.json` v adresáři klíčů (`failed_attempts`, `locked_until`)
|
|
- **Aplikováno na:** `ChatClient.login()` (síťový login) + GUI privacy overlay unlock (`_on_unlock_attempt`)
|
|
- **Reset:** Úspěšné přihlášení smaže lockout soubor
|
|
- **Defense-in-depth:** Smazání souboru resetuje počítadlo, ale PBKDF2-600k stále zpomaluje každý pokus (~0.5s/pokus)
|
|
|
|
### SPK rotace (7 dní)
|
|
Signed Pre-Key se rotuje periodicky:
|
|
- Po přihlášení `_ensure_prekeys()` zjistí stáří SPK ze serveru (`spk_created_at`)
|
|
- Pokud je SPK starší než 7 dní → vygeneruje nový, starý uloží jako grace period
|
|
- **Grace period:** `prev_spk_private.bin` — pokud příchozí X3DH selže s aktuálním SPK, zkusí předchozí
|
|
- Omezuje dopad kompromitace SPK — útočník může vytvářet nové sessions max 7 dní
|
|
|
|
### Odolnost ratchetu (state rollback)
|
|
Double Ratchet i Sender Keys automaticky rollbackují stav při selhání dešifrování:
|
|
- Před modifikací chain keys/counters se vytvoří snapshot
|
|
- Pokud AES-GCM dešifrování selže (corrupted data, wrong key), stav se obnoví
|
|
- Session zůstane funkční i po zpracování poškozené zprávy
|
|
|
|
## Registrace
|
|
|
|
1. `register` → server pošle 6-místný kód na email (nebo vrátí přímo v dev módu bez SMTP).
|
|
2. `register_confirm` → potvrzení kódu.
|
|
3. Automaticky se vygenerují a uploadnou prekeys (1 SPK + 50 OPKs).
|
|
4. Login.
|
|
|
|
## Multi-Device Support
|
|
|
|
Pravý multi-device (Signal-like) — každé zařízení má nezávislé Double Ratchet sessions.
|
|
Při posílání DM se zpráva šifruje zvlášť pro každé zařízení příjemce.
|
|
Všechna zařízení uživatele sdílejí Ed25519 identity key (pro self-encryption kompatibilitu).
|
|
|
|
### Architektura
|
|
- **Devices tabulka** — každé přihlášení registruje device (UUID), server mapuje writer→device
|
|
- **Per-device prekeys** — každé zařízení má vlastní SPK + OPKs, server vrací `device_bundles` pole
|
|
- **Per-device sessions** — sessions klíčované `"user_id:device_id"`, nezávislé Double Ratchet instance
|
|
- **Self-encryption** — odesílatel šifruje vlastní kopii statickým klíčem z identity key (čitelné všemi vlastními zařízeními)
|
|
- **Notifikace** — `device_entries` pole, klient vybere záznam odpovídající svému device_id
|
|
|
|
### Device Pairing (zjednodušený)
|
|
|
|
Nové zařízení získá RSA + Ed25519 identity klíče od existujícího zařízení.
|
|
Přenos šifrovaný RSA-OAEP + AES-GCM přes server (server nevidí klíče).
|
|
Nové zařízení si po přihlášení automaticky vygeneruje vlastní SPK + OPKs.
|
|
|
|
1. Nové zařízení: `Link Device` → dostane 8-místný kód.
|
|
2. Existující zařízení: `Authorize Device` → zadá kód → odešle RSA + identity klíče.
|
|
3. Nové zařízení importuje klíče, přihlásí se, vygeneruje vlastní prekeys.
|
|
|
|
### Migrace
|
|
- Existující DB: spustit `migration_multi_device.sql` (nebo `migration_multi_device_resume.sql` pro idempotentní re-run)
|
|
- Čistá DB: `schema.sql` již obsahuje všechny multi-device sloupce
|
|
|
|
## Device Revocation (Key Rotation)
|
|
|
|
Rotuje RSA login klíč. Odpojí ostatní sessions. Forward secrecy zajišťuje, že kompromitace
|
|
jednoho session klíče neodhalí historii — není potřeba re-encryption.
|
|
|
|
## Konfigurace
|
|
|
|
### Server + DB
|
|
- `SERVER_HOST` (default `127.0.0.1`), `SERVER_PORT` (default `9999`)
|
|
- `MYSQL_HOST`, `MYSQL_PORT`, `MYSQL_USER`, `MYSQL_PASSWORD`, `MYSQL_DATABASE`
|
|
|
|
### TLS
|
|
- `TLS_ENABLED` — zapne TLS (default `false`)
|
|
- `TLS_REQUIRED` — vyžaduje TLS_ENABLED, jinak server odmítne start
|
|
- `TLS_CERT_FILE`, `TLS_KEY_FILE` — cesty k certifikátu a privátnímu klíči (PEM)
|
|
- `TLS_AUTOGEN` — auto-generuje self-signed cert (**jen s `ENVIRONMENT=dev`**)
|
|
- `TLS_CA_FILE` (klient) — vlastní CA certifikát pro ověření serveru
|
|
- `TLS_INSECURE` (klient) — vypne ověření certifikátu (**jen s `ENVIRONMENT=dev`**)
|
|
- `ENVIRONMENT` — `dev`/`development` povolí TLS_INSECURE a TLS_AUTOGEN
|
|
|
|
#### Produkční nasazení s Let's Encrypt
|
|
```bash
|
|
# 1. Nainstalovat certbot
|
|
sudo apt install certbot
|
|
|
|
# 2. Získat certifikát (port 80 musí být volný pro ověření)
|
|
sudo certbot certonly --standalone -d chat.example.com
|
|
|
|
# 3. V .env nastavit:
|
|
TLS_ENABLED=true
|
|
TLS_CERT_FILE=/etc/letsencrypt/live/chat.example.com/fullchain.pem
|
|
TLS_KEY_FILE=/etc/letsencrypt/live/chat.example.com/privkey.pem
|
|
|
|
# 4. Klient — stačí zapnout TLS (Let's Encrypt je v systémovém trust store):
|
|
TLS_ENABLED=true
|
|
```
|
|
Certifikát funguje na jakémkoliv portu (9999, 443, ...) — je vázaný na doménu, ne port. Certbot automaticky obnovuje certifikát každých 90 dní.
|
|
|
|
#### Dev/testování (self-signed)
|
|
```bash
|
|
ENVIRONMENT=dev
|
|
TLS_ENABLED=true
|
|
TLS_AUTOGEN=true # server auto-generuje self-signed cert
|
|
TLS_INSECURE=true # klient přeskočí ověření certifikátu
|
|
```
|
|
|
|
### SMTP
|
|
- `SMTP_HOST`, `SMTP_PORT`, `SMTP_USER`, `SMTP_PASS`, `SMTP_FROM`
|
|
- Bez SMTP = dev mód (kód se vrací přímo klientovi).
|
|
|
|
### Obrázky
|
|
- `UPLOAD_DIR` (default `uploads`), `MAX_IMAGE_BYTES` (default 5 MB, `0` = bez limitu)
|
|
|
|
### Limity
|
|
- `MAX_MESSAGE_BYTES` (default `65536`), `MAX_INPUT_CHARS` (GUI, default `2000`)
|
|
- Rate limity: register 3/min, login 10/min, send_message 20/min, pairing_poll 10/min
|
|
- Connection: 20 req/s per connection, max 10 per IP, 200 global
|
|
- Pairing TTL: 120s, max 5 failed poll pokusů
|
|
|
|
### Logging
|
|
- `LOG_LEVEL` (default `INFO`)
|
|
|
|
## Features
|
|
|
|
- Registrace (2-step, SMTP), login (RSA challenge-response), key rotation
|
|
- **Multi-device** — per-device sessions (Signal-like), device pairing (RSA + identity key transfer), automatické prekey generování na novém zařízení
|
|
- DM s forward secrecy (X3DH + Double Ratchet) — per-device šifrování
|
|
- Skupiny se Sender Keys (distribuované přes pairwise ratchet)
|
|
- Skupinové pozvánky — přidání do skupiny vyžaduje souhlas (accept/decline)
|
|
- Odpovědi na zprávy (reply_to)
|
|
- Mazání zpráv (soft-delete pro všechny, real-time notifikace)
|
|
- Mazání konverzací (pravý klik → smaže pro uživatele, pokud nezbývají členové smaže celou konverzaci)
|
|
- Šifrované obrázky (AES-256-GCM, chunked upload, thumbnail v bublině)
|
|
- Šifrované soubory (PDF, ZIP, atd. až 50 MB, chunked upload)
|
|
- Read receipts (real-time, client-side resoluce)
|
|
- Prekey replenishment (automatické doplňování OPKs po loginu + SPK rotace každých 7 dní)
|
|
- Silné šifrování klíčů na disku (PBKDF2 600k iterací + AES-256-GCM, ECP1 formát)
|
|
- Odolný ratchet — automatický rollback stavu při selhání dešifrování
|
|
- TLS (volitelný, auto-gen self-signed)
|
|
- Real-time notifikace konverzací — nové konverzace, přidání/odebrání členů se zobrazí okamžitě bez re-loginu
|
|
- Connection state indicator — zelená/červená/oranžová tečka, automatický reconnect s exponential backoff
|
|
- Online/offline status — zelená tečka na avataru v seznamu konverzací + v group info
|
|
- User profily — telefon, lokace, avatar, nastavení viditelnosti (email, telefon, lokace)
|
|
- Phantom users — anti user-enumeration: konverzace s neregistrovaným emailem funguje normálně (odesílatel vidí své zprávy), zprávy pro phantom příjemce se neukládají, phantom se smaže při skutečné registraci
|
|
- Clickable links — HTTPS modré, HTTP oranžové s ikonou zámku + potvrzovací dialog
|
|
|
|
### GUI (PyQt6)
|
|
- Dark theme (Catppuccin Mocha)
|
|
- Seznam konverzací s kulatými avatary a online indikátorem (zelená tečka)
|
|
- Unread count badge na konverzacích (číselný počet nepřečtených zpráv)
|
|
- Message bubliny s barevným left border, timestamp vedle jména
|
|
- Read receipts (checkmarks), group info dialog, add/remove member
|
|
- Context menu: reply, delete, view image, download file
|
|
- Attach button pro obrázky a soubory, thumbnail v bublině, full-size viewer + save
|
|
- Pagination ("Load older messages")
|
|
- Connection indicator (zelená=online, červená=offline, oranžová=reconnecting)
|
|
- Auto-reconnect s exponential backoff (1s → 2s → 4s → ... → max 30s)
|
|
- Tlačítko "My Profile" — editace vlastního profilu (telefon, lokace, avatar, viditelnost)
|
|
- User profil dialog — klik na info tlačítko v group info → read-only profil uživatele
|
|
- Avatar upload/download (JPEG/PNG, max 2 MB, kruhový výřez)
|
|
- Leave group (červené tlačítko v group info, přenos creatora)
|
|
- Pozvánky do skupin — seznam pending pozvánek nad konverzacemi, pravý klik → accept/decline
|
|
- Periodický refresh avatarů a pozvánek (každé 2 minuty)
|
|
|
|
### CLI
|
|
- Základní funkcionalita (DM, skupiny, šifrování). Profily a soubory pouze přes GUI.
|
|
|
|
## Závislosti
|
|
|
|
- `cryptography` — Ed25519, X25519, AES-GCM, RSA, HKDF, PBKDF2
|
|
- `mysql-connector-python` — MySQL
|
|
- `python-dotenv` — env vars
|
|
- `PyQt6` — GUI
|
|
- `Pillow` — resize/thumbnail obrázků
|
|
|
|
## Known Issues
|
|
|
|
- Sender Keys pro skupiny se nedistribuují znovu při přidání nového člena (nový člen neuvidí staré skupinové zprávy).
|
|
|
|
## TODO
|
|
|
|
### Security — Zbývající
|
|
- [ ] **H9: Self-encryption key** — statický/deterministický klíč (by-design pro cross-device, architektonické omezení)
|
|
- [ ] M1: Nekonzistentní Ed25519 serializace (částečně vyřešeno M3 — ECP1 formát, ale 3 legacy formáty)
|
|
- [ ] M6: TOCTOU race v membership checks
|
|
- [ ] M7: MySQL spojení bez TLS
|
|
- [ ] L1-L8: Low-priority hardening
|
|
- [ ] **Penetrační testy** — manuální + automatizované
|
|
|
|
### Features — High Priority
|
|
- [ ] Redistribuce sender keys při přidání nového člena do skupiny
|
|
- [ ] Typing indicators
|
|
|
|
### Features — Medium Priority
|
|
- [ ] Hledání zpráv v konverzacích
|
|
- [ ] Group admin roles (více adminů)
|
|
- [ ] Edit sent messages
|
|
|
|
### Features — Low Priority
|
|
- [ ] Dark/light theme toggle
|
|
- [ ] Desktop notifications (system tray)
|
|
- [ ] Database connection pooling
|
|
- [ ] Image gallery view
|
|
- [ ] Systemd + Docker deployment
|
|
|
|
### Monetizace
|
|
Oddělený platební server (Stripe, KYC/AML compliant) od chat serveru (anonymní). Platba → premium kód → aktivace na chat serveru. Žádný přímý link platba↔chat identita.
|
|
|
|
- **Free tier:** 5 konverzací, 10 MB soubory, 1 zařízení, 30 dní retence, max 10 členů/skupina
|
|
- **Premium:** neomezeno — aktivace jednorázovým kódem z platebního serveru
|
|
- **Enterprise:** self-hosted, LDAP/SSO, admin dashboard, SLA — faktura na firmu
|
|
- Detaily implementace viz `CLAUDE.md`
|
|
|
|
### Hotovo — Security
|
|
- [x] **C1-C6: Všechny CRITICAL opraveny** — readuntil DoS, sender key fast-forward, OPK permissions, upload size check, path traversal (UUID validace + is_relative_to)
|
|
- [x] **H1-H8, H10-H14: Většina HIGH opravena** — lokální šifrování dat (AES-256-GCM), TLS hardening (INSECURE/AUTOGEN jen v dev), anti-enumeration, race conditions (asyncio.Lock), protokol error handling, avatar path traversal, hesla v paměti (bytearray+zero), image validace, filename sanitizace, OPK race condition (SELECT FOR UPDATE)
|
|
- [x] **M2-M5+M8-M13: Většina MEDIUM opravena** — HKDF salt, PBKDF2 600k iterací (ECP1 formát), SPK rotace 7 dní s grace periodem, rate limit cleanup, UUID validace, ratchet state rollback, message_ids cap, pairing poll token, upload check, chmod 0o700/0o600
|
|
- [x] **SPK/OPK šifrování + brute-force lockout** — všechny privátní klíče na disku šifrované (ECP1 nebo AES-256-GCM), exponenciální backoff po chybném hesle (2^N s, max 5 min)
|
|
|
|
### Hotovo — Features
|
|
- [x] **Multi-device support** — per-device sessions (Signal-like), device pairing, automatické prekey generování
|
|
- [x] Unread counts pro offline uživatele
|
|
- [x] Clickable HTTP links — HTTPS modré, HTTP oranžové s varováním
|
|
- [x] User profily (telefon, lokace, avatar, viditelnost)
|
|
- [x] Connection state indicator + auto-reconnect
|
|
- [x] Encrypted file sharing (až 50 MB)
|
|
- [x] Leave group + přenos creatora
|
|
- [x] Unread count badge
|
|
- [x] User avatars (upload/download, kruhový výřez)
|
|
- [x] Online/offline status (zelená tečka na avataru)
|
|
- [x] Mazání konverzací
|
|
- [x] Skupinové pozvánky (accept/decline)
|
|
- [x] Graceful server shutdown
|
|
|
|
## Bezpečnostní audit
|
|
|
|
Dva bezpečnostní audity provedeny (kód review). Nalezeno 6 CRITICAL, 12 HIGH, 12 MEDIUM, 8 LOW nálezů.
|
|
|
|
| Závažnost | Celkem | Opraveno | Zbývá |
|
|
|-----------|--------|----------|-------|
|
|
| CRITICAL | 6 | **6** | 0 |
|
|
| HIGH | 12 | **11** | 1 (H9 — by-design) |
|
|
| MEDIUM | 12 | **10** | 2 (M1 částečně, M6, M7) |
|
|
| LOW | 8 | 0 | 8 |
|
|
|
|
Detaily viz `CLAUDE.md`.
|
|
>>>>>>> d506e65 (initial commit)
|