# 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
  spk_id.txt
  prev_spk_private.bin      # Předchozí SPK (grace period pro in-flight X3DH)
  prev_spk_id.txt
  opk_private/              # One-time prekeys
    {opk_id}.bin
  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.

### 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

### 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

### 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`.
