From 214da18779110b5fbc25edced69b631086c0e292 Mon Sep 17 00:00:00 2001 From: Filip Date: Sat, 14 Mar 2026 12:43:56 +0100 Subject: [PATCH] ios_client --- AGENTS.md | 42 + README.md | 441 +- TODO.md | 147 +- chat_core.py | 27 +- ios_client 0.8.5/ARCHITECTURE.md | 346 ++ .../Kecalek.xcodeproj/project.pbxproj | 351 ++ .../contents.xcworkspacedata | 7 + .../xcshareddata/WorkspaceSettings.xcsettings | 5 + .../UserInterfaceState.xcuserstate | Bin 0 -> 37791 bytes .../WorkspaceSettings.xcsettings | 16 + .../xcschemes/xcschememanagement.plist | 14 + ios_client 0.8.5/Kecalek/.DS_Store | Bin 0 -> 8196 bytes ios_client 0.8.5/Kecalek/AppState.swift | 161 + .../AccentColor.colorset/Contents.json | 11 + .../AppIcon.appiconset/AppIcon.png | Bin 0 -> 718842 bytes .../AppIcon.appiconset/Contents.json | 38 + .../Kecalek/Assets.xcassets/Contents.json | 6 + .../Kecalek/Core/ChatClient.swift | 3666 +++++++++++++++++ .../Kecalek/Core/KeyStorage.swift | 485 +++ .../Kecalek/Core/KeychainService.swift | 132 + .../Kecalek/Core/MessageCache.swift | 200 + .../Kecalek/Crypto/ContactVerification.swift | 162 + .../Kecalek/Crypto/CryptoErrors.swift | 95 + .../Kecalek/Crypto/CryptoUtils.swift | 196 + .../Kecalek/Crypto/DoubleRatchet.swift | 393 ++ .../Kecalek/Crypto/Ed25519Crypto.swift | 73 + .../Kecalek/Crypto/FieldArithmetic.swift | 231 ++ .../Kecalek/Crypto/KeyEncryption.swift | 106 + .../Kecalek/Crypto/MessagePadding.swift | 59 + .../Kecalek/Crypto/RSACrypto.swift | 356 ++ .../Kecalek/Crypto/SenderKeyState.swift | 175 + .../Kecalek/Crypto/X25519Crypto.swift | 77 + ios_client 0.8.5/Kecalek/Crypto/X3DH.swift | 139 + ios_client 0.8.5/Kecalek/KecalekApp.swift | 47 + .../Kecalek/Models/Conversation.swift | 54 + .../Kecalek/Models/DeviceBundle.swift | 69 + .../Kecalek/Models/Invitation.swift | 9 + ios_client 0.8.5/Kecalek/Models/Message.swift | 210 + ios_client 0.8.5/Kecalek/Models/User.swift | 19 + .../Kecalek/Network/ConnectionManager.swift | 191 + .../Kecalek/Network/ProtocolHandler.swift | 88 + .../Kecalek/Utilities/Constants.swift | 38 + .../Kecalek/Utilities/Extensions.swift | 168 + .../Kecalek/ViewModels/AuthViewModel.swift | 192 + .../Kecalek/ViewModels/ChatViewModel.swift | 356 ++ .../ViewModels/ConversationListVM.swift | 246 ++ .../Kecalek/ViewModels/ProfileViewModel.swift | 98 + .../Kecalek/ViewModels/VerificationVM.swift | 60 + .../Views/Auth/AuthorizeDeviceView.swift | 82 + .../Kecalek/Views/Auth/LoginView.swift | 179 + .../Kecalek/Views/Auth/PairingView.swift | 175 + .../Kecalek/Views/Auth/RegisterView.swift | 4 + .../Kecalek/Views/Chat/ChatView.swift | 382 ++ .../Views/Chat/ForwardPickerView.swift | 77 + .../Kecalek/Views/Chat/ImageViewerView.swift | 113 + .../Views/Chat/MessageBubbleView.swift | 558 +++ .../Kecalek/Views/Chat/MessageInputView.swift | 215 + .../Views/Chat/PinnedMessagesView.swift | 62 + .../Views/Chat/SearchOverlayView.swift | 46 + .../Views/Components/CircularAvatarView.swift | 46 + .../Components/ConnectionIndicator.swift | 36 + .../Views/Components/OnlineDotOverlay.swift | 15 + .../Conversations/ConversationListView.swift | 101 + .../Conversations/ConversationRowView.swift | 60 + .../Conversations/NewConversationSheet.swift | 100 + .../Views/Groups/CreateGroupSheet.swift | 4 + .../Kecalek/Views/Groups/GroupInfoView.swift | 301 ++ .../Views/Groups/InvitationBanner.swift | 41 + .../Views/Profile/EditProfileView.swift | 4 + .../Kecalek/Views/Profile/ProfileView.swift | 277 ++ .../Verification/QRCodeScannerView.swift | 149 + .../Views/Verification/SafetyNumberView.swift | 144 + .../Verification/VerificationStatusView.swift | 43 + ios_client 0.8.5/SECURITY_REVIEW.md | 204 + 74 files changed, 13136 insertions(+), 284 deletions(-) create mode 100644 AGENTS.md create mode 100644 ios_client 0.8.5/ARCHITECTURE.md create mode 100644 ios_client 0.8.5/Kecalek.xcodeproj/project.pbxproj create mode 100644 ios_client 0.8.5/Kecalek.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 ios_client 0.8.5/Kecalek.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings create mode 100644 ios_client 0.8.5/Kecalek.xcodeproj/project.xcworkspace/xcuserdata/filip.xcuserdatad/UserInterfaceState.xcuserstate create mode 100644 ios_client 0.8.5/Kecalek.xcodeproj/project.xcworkspace/xcuserdata/filip.xcuserdatad/WorkspaceSettings.xcsettings create mode 100644 ios_client 0.8.5/Kecalek.xcodeproj/xcuserdata/filip.xcuserdatad/xcschemes/xcschememanagement.plist create mode 100644 ios_client 0.8.5/Kecalek/.DS_Store create mode 100644 ios_client 0.8.5/Kecalek/AppState.swift create mode 100644 ios_client 0.8.5/Kecalek/Assets.xcassets/AccentColor.colorset/Contents.json create mode 100644 ios_client 0.8.5/Kecalek/Assets.xcassets/AppIcon.appiconset/AppIcon.png create mode 100644 ios_client 0.8.5/Kecalek/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 ios_client 0.8.5/Kecalek/Assets.xcassets/Contents.json create mode 100644 ios_client 0.8.5/Kecalek/Core/ChatClient.swift create mode 100644 ios_client 0.8.5/Kecalek/Core/KeyStorage.swift create mode 100644 ios_client 0.8.5/Kecalek/Core/KeychainService.swift create mode 100644 ios_client 0.8.5/Kecalek/Core/MessageCache.swift create mode 100644 ios_client 0.8.5/Kecalek/Crypto/ContactVerification.swift create mode 100644 ios_client 0.8.5/Kecalek/Crypto/CryptoErrors.swift create mode 100644 ios_client 0.8.5/Kecalek/Crypto/CryptoUtils.swift create mode 100644 ios_client 0.8.5/Kecalek/Crypto/DoubleRatchet.swift create mode 100644 ios_client 0.8.5/Kecalek/Crypto/Ed25519Crypto.swift create mode 100644 ios_client 0.8.5/Kecalek/Crypto/FieldArithmetic.swift create mode 100644 ios_client 0.8.5/Kecalek/Crypto/KeyEncryption.swift create mode 100644 ios_client 0.8.5/Kecalek/Crypto/MessagePadding.swift create mode 100644 ios_client 0.8.5/Kecalek/Crypto/RSACrypto.swift create mode 100644 ios_client 0.8.5/Kecalek/Crypto/SenderKeyState.swift create mode 100644 ios_client 0.8.5/Kecalek/Crypto/X25519Crypto.swift create mode 100644 ios_client 0.8.5/Kecalek/Crypto/X3DH.swift create mode 100644 ios_client 0.8.5/Kecalek/KecalekApp.swift create mode 100644 ios_client 0.8.5/Kecalek/Models/Conversation.swift create mode 100644 ios_client 0.8.5/Kecalek/Models/DeviceBundle.swift create mode 100644 ios_client 0.8.5/Kecalek/Models/Invitation.swift create mode 100644 ios_client 0.8.5/Kecalek/Models/Message.swift create mode 100644 ios_client 0.8.5/Kecalek/Models/User.swift create mode 100644 ios_client 0.8.5/Kecalek/Network/ConnectionManager.swift create mode 100644 ios_client 0.8.5/Kecalek/Network/ProtocolHandler.swift create mode 100644 ios_client 0.8.5/Kecalek/Utilities/Constants.swift create mode 100644 ios_client 0.8.5/Kecalek/Utilities/Extensions.swift create mode 100644 ios_client 0.8.5/Kecalek/ViewModels/AuthViewModel.swift create mode 100644 ios_client 0.8.5/Kecalek/ViewModels/ChatViewModel.swift create mode 100644 ios_client 0.8.5/Kecalek/ViewModels/ConversationListVM.swift create mode 100644 ios_client 0.8.5/Kecalek/ViewModels/ProfileViewModel.swift create mode 100644 ios_client 0.8.5/Kecalek/ViewModels/VerificationVM.swift create mode 100644 ios_client 0.8.5/Kecalek/Views/Auth/AuthorizeDeviceView.swift create mode 100644 ios_client 0.8.5/Kecalek/Views/Auth/LoginView.swift create mode 100644 ios_client 0.8.5/Kecalek/Views/Auth/PairingView.swift create mode 100644 ios_client 0.8.5/Kecalek/Views/Auth/RegisterView.swift create mode 100644 ios_client 0.8.5/Kecalek/Views/Chat/ChatView.swift create mode 100644 ios_client 0.8.5/Kecalek/Views/Chat/ForwardPickerView.swift create mode 100644 ios_client 0.8.5/Kecalek/Views/Chat/ImageViewerView.swift create mode 100644 ios_client 0.8.5/Kecalek/Views/Chat/MessageBubbleView.swift create mode 100644 ios_client 0.8.5/Kecalek/Views/Chat/MessageInputView.swift create mode 100644 ios_client 0.8.5/Kecalek/Views/Chat/PinnedMessagesView.swift create mode 100644 ios_client 0.8.5/Kecalek/Views/Chat/SearchOverlayView.swift create mode 100644 ios_client 0.8.5/Kecalek/Views/Components/CircularAvatarView.swift create mode 100644 ios_client 0.8.5/Kecalek/Views/Components/ConnectionIndicator.swift create mode 100644 ios_client 0.8.5/Kecalek/Views/Components/OnlineDotOverlay.swift create mode 100644 ios_client 0.8.5/Kecalek/Views/Conversations/ConversationListView.swift create mode 100644 ios_client 0.8.5/Kecalek/Views/Conversations/ConversationRowView.swift create mode 100644 ios_client 0.8.5/Kecalek/Views/Conversations/NewConversationSheet.swift create mode 100644 ios_client 0.8.5/Kecalek/Views/Groups/CreateGroupSheet.swift create mode 100644 ios_client 0.8.5/Kecalek/Views/Groups/GroupInfoView.swift create mode 100644 ios_client 0.8.5/Kecalek/Views/Groups/InvitationBanner.swift create mode 100644 ios_client 0.8.5/Kecalek/Views/Profile/EditProfileView.swift create mode 100644 ios_client 0.8.5/Kecalek/Views/Profile/ProfileView.swift create mode 100644 ios_client 0.8.5/Kecalek/Views/Verification/QRCodeScannerView.swift create mode 100644 ios_client 0.8.5/Kecalek/Views/Verification/SafetyNumberView.swift create mode 100644 ios_client 0.8.5/Kecalek/Views/Verification/VerificationStatusView.swift create mode 100644 ios_client 0.8.5/SECURITY_REVIEW.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..fbcf12d --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,42 @@ +# Repository Guidelines + +## Project Structure & Module Organization + +The main Python modules live in the repository root. `server.py` contains the asyncio TCP server, request handlers, rate limiting, and upload flows. `chat_core.py` holds shared client logic, crypto workflows, and local key handling. `client.py` is the CLI, `gui_client.py` is the PyQt6 GUI, `db.py` is the MySQL layer, `protocol.py` defines the newline-delimited JSON protocol, and `crypto_utils.py` contains X3DH, Double Ratchet, Sender Keys, and local encryption helpers. Use `schema.sql` for a clean database bootstrap. Security and architecture notes are tracked in `SECURITY_AUDIT.md`, `README.md`, `scaling.md`, and `CLAUDE.md`. Put new test tooling under `tests/`. Treat `zaloha/` as archive code, not an active source directory. + +## Build, Test, and Development Commands + +Use the project virtualenv and MySQL schema: + +```bash +.venv/bin/pip install -r requirements.txt +mysql -u -p < schema.sql +.venv/bin/python server.py +.venv/bin/python client.py +.venv/bin/python gui_client.py +``` + +For quick validation, run: + +```bash +.venv/bin/python -m py_compile server.py chat_core.py client.py gui_client.py db.py +.venv/bin/python tests/pentest_client.py --server-host --member-email ... --peer-email ... --outsider-email ... +``` + +There is no full `pytest` suite yet; current regression coverage is mainly protocol-level through `tests/pentest_client.py`. + +## Coding Style & Naming Conventions + +Follow existing Python conventions: 4-space indentation, `snake_case` for functions and variables, `PascalCase` for classes, and type hints on new or changed code. Keep handlers non-blocking: DB, file, or SMTP work that can block should be moved behind async helpers or `asyncio.to_thread()`. Reuse central validation helpers instead of duplicating checks, and keep logs free of secrets, emails, or raw user-controlled text where possible. + +## Testing Guidelines + +Add tests in `tests/` with descriptive names. Prefer `test_.py` for focused checks and `_client.py` for protocol or penetration probes. Every security fix should include a regression path that covers malformed input, authorization, replay, rate limiting, or multi-device behavior. + +## Commit & Pull Request Guidelines + +Git history is not available in this workspace snapshot, so use short imperative commit messages. Conventional Commit style is preferred, for example `fix: reject invalid ratchet headers`. PRs should summarize behavior changes, mention schema or `.env` updates, link related issues, and include CLI or GUI evidence for user-visible changes. + +## Security & Configuration Tips + +Do not commit `.env`, TLS private keys, uploaded files, or local key material from `~/.encrypted_chat/`. When testing TLS, remember that `0.0.0.0` is a server bind address, not a valid client hostname. Use a host or IP that matches the certificate SAN or CN. diff --git a/README.md b/README.md index c0221cf..d015ff1 100644 --- a/README.md +++ b/README.md @@ -1,333 +1,246 @@ -<<<<<<< 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. +## Architektura + +``` +┌─────────────┐ TLS/TCP ┌─────────────┐ MySQL ┌─────────┐ +│ GUI/CLI │◄───────────────►│ Server │◄──────────────►│ DB │ +│ klient │ JSON + base64 │ (asyncio) │ │ │ +└─────────────┘ └─────────────┘ └─────────┘ + │ │ + │ X3DH + Double Ratchet │ Opaque blobs + │ Sender Keys (skupiny) │ (server nevidí plaintext) + ▼ ▼ + Lokální klíče Šifrované zprávy + (~/.encrypted_chat/) + metadata +``` + ## 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, ...) | +| Soubor | Řádky | Účel | +|--------|-------|------| +| `server.py` | ~2 900 | Asyncio TCP server, 45 handlerů, rate limiting, 5 asyncio.Lock guardů, real-time notifikace | +| `db.py` | ~1 700 | MySQL CRUD, connection pooling (pool_size=10), phantom users, reactions/pins CRUD | +| `schema.sql` | ~190 | MySQL schéma (14 tabulek) | ### 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 | +| Soubor | Řádky | Účel | +|--------|-------|------| +| `gui_client.py` | ~6 300 | PyQt6 GUI — dark/light téma, widget-based message bubbles, verifikace kontaktů, privacy overlay | +| `client.py` | ~900 | CLI klient — 23 menu opcí | +| `chat_core.py` | ~3 500 | Sdílená logika — session management, X3DH/ratchet šifrování, lokální klíče, multi-device | +| `theme.py` | ~540 | Catppuccin dark + Signal-inspired light téma, live switching | ### 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 | +| `crypto_utils.py` (~935 ř.) | Ed25519, X25519, AES-256-GCM, HKDF, PBKDF2, X3DH, Double Ratchet (state rollback), Sender Keys (state rollback), ECP1 key encryption, contact verification (fingerprints, safety numbers, QR), message padding | +| `protocol.py` (~140 ř.) | Newline-delimited JSON protokol, base64 encoding, verze (0.8.4) | + +### iOS klient +| Složka | Účel | +|--------|------| +| `ios_client/` (47 Swift souborů, ~5 000 ř.) | Nativní iOS port — CryptoKit + pure Swift GF(2^255-19) + Security.framework RSA, SwiftUI views, wire-kompatibilní s Python serverem | + +### Testy +| Soubor | Účel | +|--------|------| +| `tests/pentest_client.py` (~340 ř.) | Automatizované security regresní testy (AuthZ, malformed headers, session reset, rate limits) | ## 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`. +2. Spustit `schema.sql` v MySQL 3. `python server.py` -4. Klient: `python client.py` (CLI) nebo `python gui_client.py` (GUI, PyQt6) +4. Klient: `python gui_client.py` (GUI) nebo `python client.py` (CLI) ## 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) | +| RSA-4096 | Asymetrický | Pouze login challenge-response. Šifrovaný ECP1 (PBKDF2 600k + AES-256-GCM). | +| Identity Key (IK) | Ed25519 | Podpisy, konverze na X25519 pro X3DH. Šifrovaný ECP1. | +| Signed Pre-Key (SPK) | X25519 | DH v X3DH, podepsaný IK. **Rotuje se každých 7 dní** s grace periodem. | +| 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). +1. Alice stáhne Bobovy per-device key bundles (IK, SPK, OPK) → X3DH per device → shared secret per device. +2. Double Ratchet inicializován ze shared secret — jedna session per (user, device). +3. Každá zpráva: symmetric ratchet (HMAC chain) → message key → AES-256-GCM. +4. Každá odpověď: DH ratchet (nový X25519 keypair) → nový root key + chain key. +5. Per-device ciphertext — každé zařízení příjemce dostane individuálně šifrovaný blob. +6. Self-encrypted kopie s SELF_DEVICE_ID sentinel, čitelná všemi vlastními zařízeními. ### 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). +1. Každý odesílatel má vlastní SenderKeyState per group. +2. Sender key distribuován členům přes pairwise Double Ratchet (jako control DM). 3. Skupinové zprávy: symmetric ratchet na sender key → AES-256-GCM. -4. Jeden ciphertext pro celou skupinu (efektivní). +4. Stejný ciphertext pro všechny příjemce (efektivní). + +### Kontaktní verifikace (Signal-style) +- **Safety numbers** — 60-digit číslo (12 skupin × 5 číslic), deterministické pro každý pár. +- **QR kódy** — binární payload zakódovaný jako base64. +- **Fingerprints** — 30-digit per-user číslo. +- **TOFU** — Trust On First Use + explicit verification + key change warning. ### 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 + private.pem / public.pem — RSA (login, ECP1 formát) + identity_private.bin / _public.bin — Ed25519 (ECP1 formát) + device_id.txt — UUID tohoto zařízení + spk_private.bin / spk_id.txt — Aktuální SPK (AES-256-GCM) + prev_spk_private.bin / prev_spk_id.txt — Předchozí SPK, grace period + opk_private/{opk_id}.bin — One-time prekeys (AES-256-GCM) + sessions/{uid}_{did}.bin — Double Ratchet stavy (AES-256-GCM) + sender_keys/{conv_id}.bin — Vlastní sender keys + sender_keys_recv/{conv}_{uid}_{did}.bin — Přijaté sender keys + known_identity_keys.bin — TOFU registr (AES-256-GCM) + verified_contacts.bin — Explicitní verifikace (AES-256-GCM) + message_cache/{conv_id}.bin — Šifrovaný message cache + login_lockout.json — Brute-force lockout stav ``` ## 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 +### Šifrování privátních klíčů (ECP1 formát) +- **PBKDF2-HMAC-SHA256** s 600 000 iteracemi (OWASP 2023) +- **AES-256-GCM**, 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. +- Zpětná kompatibilita: staré PEM se migrují automaticky -### Š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` +### Lokální šifrování dat +- Session/sender key soubory, OPK, SPK, message cache, verifikační soubory — AES-256-GCM klíčem z HKDF(identity_key) +- `chmod 0o700` na adresáře, `0o600` na soubory -### 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) +### Brute-force ochrana +- Exponenciální backoff: `min(2^N, 300)` sekund po N chybných pokusech +- Aplikováno na login + privacy overlay unlock ### 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í +- Automatická rotace s grace periodem pro in-flight X3DH +- Omezuje dopad kompromitace SPK -### 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 +### Ratchet state rollback +- Snapshot/restore při selhání dešifrování (DoubleRatchet + SenderKeyState) -## Registrace +### Secure deletion +- Overwrite `os.urandom()` + `fsync` + `unlink` na smazané citlivé soubory -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. +### Message padding +- Bucketed padding (64B–64KB) maskuje délku zpráv + +### Metadata privacy +- Log sanitizace (žádná PII), metadata retention (90 dní), sender chain minimalizace + +### Anti-enumeration +- Phantom users pro neregistrované emaily +- Generické odpovědi na register/login/get_user_info ## 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 +- **Devices tabulka** — každé přihlášení registruje device (UUID) +- **Per-device prekeys** — každé zařízení má vlastní SPK + OPKs +- **Per-device sessions** — klíčované `"user_id:device_id"` +- **Self-encryption** — statický klíč z identity key (čitelné všemi vlastními zařízeními) +- **Pairing** — přenos RSA + Ed25519, nové zařízení generuje vlastní SPK + OPKs -### Device Pairing (zjednodušený) +## Features -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. +### Protokol & šifrování +- X3DH + Double Ratchet (DM) s forward secrecy +- Sender Keys (skupiny) s distribucí přes pairwise ratchet +- Per-device šifrování (multi-device) +- SPK rotace (7 dní) + grace period +- Ratchet state rollback při selhání +- ECP1 šifrování klíčů (PBKDF2 600k) +- Message padding (bucketed 64B–64KB) +- Kontaktní verifikace (safety numbers, fingerprints, QR kódy) -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. +### Komunikace +- DM + skupinové konverzace +- Reakce na zprávy (thumbsup, heart, laugh, surprised, sad, thumbsdown) +- Přeposílání zpráv (text, obrázky, soubory) +- Připnuté zprávy (pin/unpin + dialog) +- @Mentions s autocomplete +- Odpovědi na zprávy (reply_to) +- Hledání zpráv (client-side, Ctrl+F) +- Šifrované obrázky (AES-256-GCM, chunked upload, thumbnail) +- Šifrované soubory (až 50 MB, chunked upload) +- Read receipts (real-time) -### 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 +### Skupiny +- Skupinové pozvánky (accept/decline) +- Leave group + přenos creatora +- Rename group (creator only) +- Delete conversation (DMs per-user, groups creator-only) +- Group avatar -## Device Revocation (Key Rotation) +### Správa +- Multi-device support (per-device sessions, pairing) +- User profily (telefon, lokace, avatar, viditelnost) +- Online/offline status +- Session reset (při poškození ratchetu) +- Key rotation (revokace zařízení) +- Brute-force lockout -Rotuje RSA login klíč. Odpojí ostatní sessions. Forward secrecy zajišťuje, že kompromitace -jednoho session klíče neodhalí historii — není potřeba re-encryption. +### GUI (PyQt6) +- Dark (Catppuccin Mocha) + Light (Signal) téma s live switching +- Widget-based message bubbles s ConversationDelegate +- Cirkulární avatary + online zelená tečka +- Unread count badges +- Privacy overlay / lock screen (30s timeout + heslo) +- Drag & drop souborů +- Frameless dialogy +- Connection indicator (green/red/orange) + auto-reconnect +- VerificationDialog (safety numbers, QR, fingerprints) +- Key change warning dialog + +### CLI +- 23 menu opcí (DM, skupiny, soubory, reakce, piny, forwarding, verifikace, zařízení, search) + +### iOS (SwiftUI) +- Wire-kompatibilní s Python serverem +- Kompletní Signal Protocol (X3DH, Double Ratchet, Sender Keys) +- CryptoKit + pure Swift field arithmetic + Security.framework RSA +- SwiftUI views (login, chat, groups, profiles, search) ## Konfigurace ### Server + DB - `SERVER_HOST` (default `127.0.0.1`), `SERVER_PORT` (default `9999`) - `MYSQL_HOST`, `MYSQL_PORT`, `MYSQL_USER`, `MYSQL_PASSWORD`, `MYSQL_DATABASE` +- `DB_POOL_SIZE` (default `10`) ### 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_REQUIRED` — vyžaduje TLS_ENABLED +- `TLS_CERT_FILE`, `TLS_KEY_FILE` — cesty k certifikátu (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_CA_FILE` (klient) — vlastní CA certifikát - `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) +- Bez SMTP = dev mód (kód se vrací přímo klientovi) ### 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ů +- `MAX_MESSAGE_BYTES` (default `65536`), `MAX_IMAGE_BYTES` (5 MB), `MAX_FILE_BYTES` (50 MB) +- `MAX_INPUT_CHARS` (GUI, default `2000`) +- `METADATA_RETENTION_DAYS` (default `90`) +- Rate limity: register 3/min, login 10/min, send_message 20/min +- Connection: 20 req/s, max 10/IP, 200 global ### 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ů. @@ -336,8 +249,22 @@ Dva bezpečnostní audity provedeny (kód review). Nalezeno 6 CRITICAL, 12 HIGH, |-----------|--------|----------|-------| | CRITICAL | 6 | **6** | 0 | | HIGH | 12 | **11** | 1 (H9 — by-design) | -| MEDIUM | 12 | **10** | 2 (M1 částečně, M6, M7) | -| LOW | 8 | 0 | 8 | +| MEDIUM | 12 | **11** | 1 (M7) | +| LOW | 8 | **1** | 7 | -Detaily viz `CLAUDE.md`. ->>>>>>> d506e65 (initial commit) +Detaily viz `SECURITY_AUDIT.md` a `CLAUDE.md`. + +## Known Issues + +- **Sender Key Redistribution:** Nový člen skupiny nedešifruje staré skupinové zprávy (sender keys se nedistribuují znovu při přidání). +- **iOS: Contact Key Verification** — safety numbers, QR kódy, TOFU zatím neimplementovány v iOS klientu. + +## Závislosti + +- `cryptography` — Ed25519, X25519, AES-GCM, RSA, HKDF, PBKDF2 +- `mysql-connector-python` — MySQL s connection pooling +- `python-dotenv` — env vars +- `PyQt6` — GUI +- `Pillow` — resize/thumbnail obrázků +- `qrcode` — generování QR kódů +- `pyzbar` (volitelné) — skenování QR kódů diff --git a/TODO.md b/TODO.md index 302a5f5..5f35b01 100644 --- a/TODO.md +++ b/TODO.md @@ -1,22 +1,131 @@ # TODO -## Distributed global cap for phantom users (multi-process safe) +## Zbývající bezpečnostní nálezy -1. Add DB-backed quota as source of truth (`system_quotas` table, row `phantom_users` with `used` and `limit`). -2. Move cap enforcement into one DB transaction: - - lock quota row with `SELECT ... FOR UPDATE` - - check `used < limit` - - create phantom user - - increment `used` - - commit (or rollback on failure). -3. Handle same-email races using `UNIQUE(email)`: - - on duplicate key, do not increment quota - - return existing user (or unified error response). -4. Add periodic reconciliation job: - - recalculate phantom count from `users` - - repair `system_quotas.used` if drift is detected. -5. Move phantom creation rate-limits to shared backend (Redis or DB atomic counters), so all server processes enforce the same limits. -6. Add concurrency tests: - - multi-process create storm near cap boundary (499/500) - - duplicate-email storm - - assert `used <= limit` always holds. +### HIGH +- [ ] **H9: Self-encryption key** — statický/deterministický klíč z identity key (by-design pro cross-device čtení, architektonické omezení — žádná forward secrecy pro self-copies) + +### MEDIUM +- [ ] **M7: MySQL TLS** — `db.get_connection()` nepředává SSL parametry. Na vzdáleném serveru jdou DB credentials v plaintextu. Přidat `ssl_ca`, `ssl_cert`, `ssl_key`. + +### LOW (nízké riziko) +- [ ] L1: Hex string keys v skipped messages dict — timing side-channel (post-auth) +- [ ] L2: RatchetHeader redundantní type konverze +- [ ] L3: `notif_label.setText()` vs `setHtml()` křehkost +- [ ] L4: SQL column interpolation v `update_user_profile` (whitelist chrání) +- [ ] L5: TLS cipher suite hardening (Python defaults rozumné, ne explicitní) +- [ ] L6: Temporary pairing key cleanup z paměti +- [ ] L7: `_user_cache` indefinite growth + +## Funkční TODO + +### High Priority +- [ ] **Sender Key Redistribution** — při `add_member` redistribuovat sender keys všem členům včetně nového. Nový člen skupiny momentálně nedešifruje staré zprávy. + +### Medium Priority +- [ ] **iOS: Contact Key Verification** — safety numbers, fingerprints, QR kódy, TOFU registr. Spec viz CLAUDE.md (iOS implementation spec). +- [ ] Typing indicators (`typing_start`/`typing_stop` + 3s timeout, debounce) +- [ ] Delivery receipts (`message_delivered` notifikace — 1 fajfka odesláno, 2 fajfky doručeno, modré přečteno) +- [ ] Group admin roles (více adminů) +- [ ] Edit sent messages + +### Low Priority +- [ ] Desktop notifications (system tray) +- [ ] Image gallery view +- [ ] Systemd + Docker deployment + +## Před nasazením do produkce + +- [ ] **TLS certifikáty** — Let's Encrypt nebo vlastní CA. `TLS_ENABLED=true`, `TLS_CERT_FILE`, `TLS_KEY_FILE`. +- [ ] **SMTP** — reálný SMTP server pro registrační kódy. +- [ ] **MySQL TLS** — SSL parametry v `db.get_connection()` pokud DB na jiném stroji. +- [ ] **UPLOAD_DIR** — persistentní disk, dostatečná kapacita, správná práva (0o700). +- [ ] **Backup** — pravidelný backup MySQL + UPLOAD_DIR. +- [ ] **Packaging** — pyinstaller / cx_Freeze pro distribuci klientů. +- [ ] **Penetrační testy** — manuální + automatizované (path traversal, DoS, race conditions, enumeration, TLS downgrade, pairing hijacking). + +## Budoucí plány + +- [ ] WebSocket upgrade (nahradit raw TCP pro lepší kompatibilitu) +- [ ] Mobilní push notifikace (APNs + FCM) +- [ ] Auto-update klientů (po packagingu) +- [ ] Monetizace — oddělený platební server (Stripe), premium kódy, free/premium tier. Detaily viz CLAUDE.md. + +## Phantom Users — Distributed Cap + +Pro multi-process deployment: +1. DB-backed quota (`system_quotas` tabulka, `SELECT ... FOR UPDATE`) +2. Same-email races přes `UNIQUE(email)` +3. Periodic reconciliation job +4. Shared rate-limits (Redis nebo DB atomic counters) +5. Concurrency testy + +## Hotovo + +### Security (všechny CRITICAL + většina HIGH/MEDIUM opraveny) +- [x] C1: readuntil DoS → LimitOverrunError handling +- [x] C2: SenderKeyState fast-forward DoS → MAX_SENDER_KEY_SKIP=256 +- [x] C3: Plaintext message cache → AES-256-GCM šifrování +- [x] C4: OPK file permissions → chmod 0o600 +- [x] C5: Upload size validation → received_bytes == file_size check +- [x] C6: Path traversal → UUID validace + is_relative_to +- [x] H1: Session/sender key šifrování → AES-256-GCM via _local_key +- [x] H2+H14: TLS hardening → ENVIRONMENT=dev guard +- [x] H3+H13: Anti-enumeration → generické odpovědi, auth pro get_user_info +- [x] H4: Race conditions → 5 asyncio.Lock guardů +- [x] H5+H6: Protocol error handling → base64/JSON exception handling +- [x] H7: Avatar path traversal → _safe_avatar_path +- [x] H8: Password memory → bytearray + zero-out +- [x] H10: Image validation → size + dimensions check +- [x] H11: Filename sanitization → os.path.basename +- [x] H12: OPK race condition → SELECT FOR UPDATE +- [x] M2: HKDF salt → b"\x00"*32 +- [x] M3: PBKDF2 600k iterations (ECP1 formát) +- [x] M4: SPK rotace 7 dní + grace period +- [x] M5: Rate limit cleanup +- [x] M6: TOCTOU → remove_conversation_member_atomic +- [x] M8: UUID validace všech handlerů +- [x] M9: Ratchet state rollback (snapshot/restore) +- [x] M10: message_ids cap (500) +- [x] M11: Pairing poll token (secrets.token_hex) +- [x] M12: Upload end size validation +- [x] M13: chmod 0o700/0o600 na klíčové adresáře/soubory +- [x] L8: Phantom user cleanup (30 dní + email validace) +- [x] SPK/OPK šifrování na disku +- [x] Brute-force lockout (exponenciální backoff) + +### Features +- [x] X3DH + Double Ratchet (Signal Protocol) +- [x] Sender Keys pro skupiny +- [x] Multi-device support (per-device sessions, pairing) +- [x] Kontaktní verifikace (safety numbers, fingerprints, QR kódy) — Python klienti +- [x] Message padding (bucketed 64B–64KB) +- [x] Metadata privacy (log sanitizace, retention, sender chain minimalizace) +- [x] Secure deletion (overwrite + fsync + unlink) +- [x] Reakce na zprávy (6 emoji typů) +- [x] Přeposílání zpráv (text, obrázky, soubory) +- [x] Připnuté zprávy (pin/unpin + dialog) +- [x] @Mentions s autocomplete +- [x] Hledání zpráv (client-side, Ctrl+F) +- [x] Šifrované obrázky + soubory (chunked upload, až 50 MB) +- [x] Skupinové pozvánky (accept/decline) +- [x] Leave group + přenos creatora +- [x] Rename group (creator only) +- [x] Delete conversation +- [x] Group avatar +- [x] User profily (telefon, lokace, avatar, viditelnost) +- [x] Online/offline status +- [x] Unread count badges (server-side pro offline uživatele) +- [x] Privacy overlay / lock screen +- [x] Dark/light téma (Catppuccin + Signal) s live switching +- [x] Session recovery (reset + auto X3DH) +- [x] Connection indicator + auto-reconnect +- [x] Drag & drop souborů +- [x] Favorites (GUI) +- [x] Phantom users (anti-enumeration) +- [x] DB connection pooling (pool_size=10) +- [x] Version negotiation (0.8.4, MIN_CLIENT_VERSION=0.8.3) +- [x] Graceful server shutdown +- [x] iOS klient (47 Swift souborů, ~5 000 řádků) +- [x] CLI klient (23 menu opcí) +- [x] Pentest harness (4 test kategorií) diff --git a/chat_core.py b/chat_core.py index 7f19d07..d5dd0cc 100644 --- a/chat_core.py +++ b/chat_core.py @@ -1044,30 +1044,34 @@ class ChatClient: # ------------------------------------------------------------------ async def register(self, username: str, password: str, email: str) -> tuple[bool, str]: - """Register user. Generates RSA + Ed25519 + prekeys.""" + """Register user. Generates RSA + Ed25519 in memory (saved to disk + only after server confirms registration via confirm_registration).""" self.username = username self.email = email pwd_bytes = bytearray(password.encode("utf-8")) if password else None try: - # RSA keys for login - priv, pub, err = load_keys(email, password=bytes(pwd_bytes) if pwd_bytes else None) + pwd = bytes(pwd_bytes) if pwd_bytes else None + # Try loading existing keys (previous successful registration) + priv, pub, err = load_keys(email, password=pwd) if priv is None: priv, pub = generate_rsa_keypair() - save_keys(email, priv, pub, password=bytes(pwd_bytes) if pwd_bytes else None) self.private_key = priv self.public_key = pub - # Ed25519 identity keys - ed_priv, ed_pub = _load_identity_keys(email, password=bytes(pwd_bytes) if pwd_bytes else None) + try: + ed_priv, ed_pub = _load_identity_keys(email, password=pwd) + except Exception: + ed_priv, ed_pub = None, None if ed_priv is None: ed_priv, ed_pub = generate_identity_keypair() - _save_identity_keys(email, ed_priv, ed_pub, password=bytes(pwd_bytes) if pwd_bytes else None) self.identity_private = ed_priv self.identity_public = ed_pub self._cache_key = derive_self_encryption_key(ed_priv) self._local_key = derive_local_storage_key(ed_priv) - self._load_verification_stores() + + # Store password for saving keys after confirm + self._reg_password = pwd finally: if pwd_bytes: pwd_bytes[:] = b'\x00' * len(pwd_bytes) @@ -1100,6 +1104,7 @@ class ChatClient: **extra_fields, ) if start["status"] != "ok": + self._reg_password = None return False, start["data"]["message"] code = start["data"].get("code") if code: @@ -1109,6 +1114,12 @@ class ChatClient: async def confirm_registration(self, email: str, username: str, code: str) -> tuple[bool, str]: confirm = await self.send_and_recv("register_confirm", email=email, code=code) if confirm["status"] == "ok": + # Registration confirmed — NOW save keys to disk + pwd = getattr(self, "_reg_password", None) + save_keys(email, self.private_key, self.public_key, password=pwd) + _save_identity_keys(email, self.identity_private, self.identity_public, password=pwd) + self._reg_password = None + self._load_verification_stores() # Upload prekeys immediately after registration await self._generate_and_upload_prekeys() return True, f"Registered as '{username}' (ID: {confirm['data']['user_id']})" diff --git a/ios_client 0.8.5/ARCHITECTURE.md b/ios_client 0.8.5/ARCHITECTURE.md new file mode 100644 index 0000000..0715af1 --- /dev/null +++ b/ios_client 0.8.5/ARCHITECTURE.md @@ -0,0 +1,346 @@ +# Kecalek iOS — Architecture & Features + +**Version:** 0.8.5 +**Platform:** iOS 26+ / Swift 6 +**Files:** 57 Swift source files + +--- + +## Project Structure + +``` +Kecalek/ +├── KecalekApp.swift # App entry point, tab navigation +├── AppState.swift # Login state, connection monitoring, reconnection +├── Core/ +│ ├── ChatClient.swift # Main actor — all server communication & crypto (3400+ lines) +│ ├── KeyStorage.swift # Persistent key storage (RSA, Ed25519, sessions, TOFU) +│ ├── KeychainService.swift # Secure credential storage (biometric auth) +│ └── MessageCache.swift # Encrypted message cache (per-conversation) +├── Crypto/ +│ ├── CryptoUtils.swift # AES-256-GCM, HKDF, chain KDF, local encryption +│ ├── DoubleRatchet.swift # Signal Double Ratchet (DM encryption) +│ ├── X3DH.swift # Extended Triple Diffie-Hellman (session init) +│ ├── SenderKeyState.swift # Sender Key chains (group encryption) +│ ├── Ed25519Crypto.swift # Identity key generation & signing +│ ├── X25519Crypto.swift # DH key agreement & Ed25519↔X25519 conversion +│ ├── RSACrypto.swift # RSA-2048 key generation, PKCS#1/PKCS#8 +│ ├── KeyEncryption.swift # ECP1 format: PBKDF2 600K + AES-GCM key encryption +│ ├── FieldArithmetic.swift # GF(2^255-19) for Ed25519→X25519 conversion +│ ├── MessagePadding.swift # Bucket-based padding (64B–64KB) for metadata privacy +│ ├── ContactVerification.swift # Fingerprints, safety numbers, QR codes +│ └── CryptoErrors.swift # Error types +├── Network/ +│ ├── ConnectionManager.swift # TCP/TLS via Network.framework (actor) +│ └── ProtocolHandler.swift # Newline-delimited JSON encoding/decoding +├── Models/ +│ ├── Message.swift # Message, reactions, replies, pins, files, images +│ ├── Conversation.swift # Conversation, members, group detection +│ ├── User.swift # User, UserProfile +│ ├── DeviceBundle.swift # X3DH key bundle per device +│ └── Invitation.swift # Group invitation +├── ViewModels/ +│ ├── AuthViewModel.swift # Login, register, pairing, biometrics +│ ├── ChatViewModel.swift # Messages, sending, search, reactions, pins +│ ├── ConversationListVM.swift # Conversations, online users, favorites, avatars +│ ├── ProfileViewModel.swift # Profile editing, avatar upload +│ └── VerificationVM.swift # Safety numbers, QR verification +├── Views/ +│ ├── Auth/ # LoginView, RegisterView, PairingView, AuthorizeDeviceView +│ ├── Chat/ # ChatView, MessageBubbleView, MessageInputView, etc. +│ ├── Components/ # CircularAvatarView, ConnectionIndicator, OnlineDotOverlay +│ ├── Conversations/ # ConversationListView, ConversationRowView, NewConversationSheet +│ ├── Groups/ # GroupInfoView, InvitationBanner, CreateGroupSheet +│ ├── Profile/ # ProfileView, EditProfileView +│ └── Verification/ # SafetyNumberView, QRCodeScannerView, VerificationStatusView +└── Utilities/ + ├── Constants.swift # Version, limits, timeouts, server defaults, crypto params + └── Extensions.swift # Data hex/base64, DateParsing, Dictionary helpers +``` + +--- + +## Architecture + +### Pattern: MVVM + Actor Isolation + +- **Views** — SwiftUI, declarative UI, bind to `@Observable` ViewModels +- **ViewModels** — `@Observable final class`, business logic, async operations +- **ChatClient** — `actor`, single source of truth for all crypto & network ops +- **Models** — plain `struct`s with `Identifiable`, `Codable` + +### Concurrency Model + +- `ChatClient` is an **actor** — all crypto state (keys, sessions, ratchets) is thread-safe +- All network calls use `async/await` +- Real-time notifications via `AsyncStream` (multiple subscribers) +- Background tasks: avatar loading, reconnection, notification listening + +### Connection Lifecycle + +``` +App Launch → Login (RSA challenge-response) → TCP/TLS connected + → Background listener loop reads messages continuously + → Notifications broadcast via AsyncStream to all subscribers + → On disconnect: exponential backoff reconnect (1s → 30s, 5 attempts) + → On auth failure: immediate logout (keys rotated) + → On foreground: check connection health, reconnect if stale (>30s) +``` + +--- + +## Encryption (Signal Protocol) + +### Key Types + +| Key | Algorithm | Size | Purpose | +|-----|-----------|------|---------| +| RSA | RSA-2048 | 256B | Login authentication (challenge-response) | +| Identity Key (IK) | Ed25519 | 32B | Long-term identity, signs SPK | +| Signed Pre-Key (SPK) | X25519 | 32B | Medium-term, rotated every 7 days | +| One-Time Pre-Keys (OPKs) | X25519 | 32B each | Single-use, batch of 50, replenish at 20 | +| Ratchet Keys | X25519 | 32B | Ephemeral per DH ratchet step | +| Sender Keys | Random | 32B | Per-group, per-sender chain key | + +### DM Encryption (X3DH + Double Ratchet) + +1. **Session Init (X3DH):** + - Alice computes: DH(IK_A, SPK_B) || DH(EK_A, IK_B) || DH(EK_A, SPK_B) || DH(EK_A, OPK_B) + - HKDF-SHA256 derives 32-byte shared secret + - Double Ratchet initialized + +2. **Message Encryption (Double Ratchet):** + - Root key → chain key → message key (HKDF chain) + - DH ratchet step on each direction change + - AES-256-GCM with derived message key + - AAD: ratchet header (dh_pub, n, pn) + - Max skip: 256 messages + +3. **Message Format:** + ``` + plaintext → MessagePadding.pad() → AES-256-GCM encrypt → base64 → JSON + ``` + +### Group Encryption (Sender Keys) + +1. Each member maintains own sender key chain +2. Sender key distributed to all members via pairwise Double Ratchet DMs +3. Messages encrypted with AES-256-GCM using derived chain key +4. Chain ID = SHA-256(sender_key) for verification +5. Max skip: 256 messages per chain + +### Message Padding + +Bucket sizes: `64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536` bytes + +``` +Format: 0x01 | plaintext | random_padding | pad_length (4B big-endian) +``` + +All messages padded to nearest bucket size — prevents metadata analysis of message lengths. + +### Self-Encryption + +- Derived from identity private key via HKDF +- Encrypts own message copies for multi-device access +- Static key — same across all user's devices + +### Contact Verification (TOFU) + +- **Fingerprint:** Iterated SHA-512 (5200 rounds) over identity key +- **Safety Number:** 60 digits (12 groups of 5), deterministic ordering by userId +- **QR Code:** Binary `version(1B) + uid_len(1B) + uid + identity_key(32B)` +- **TOFU Registry:** Track first-seen identity keys, alert on change +- **Verification Status:** unverified → trusted (TOFU) → verified (manual/QR) + +--- + +## Features + +### Authentication & Accounts + +- [x] **Registration** — email + password + verification code +- [x] **Login** — RSA challenge-response authentication +- [x] **Biometric Login** — Face ID / Touch ID via Keychain +- [x] **PoW Challenge** — SHA-256 proof-of-work during registration surge +- [x] **Brute-Force Lockout** — exponential backoff (2^n seconds, max 300s) +- [x] **Change Username** — update display name +- [x] **Change Password** — re-encrypt RSA + Ed25519 keys with new PBKDF2 password +- [x] **Key Rotation** — regenerate all keys with grace period for in-flight sessions +- [x] **Logout** — clean disconnect, clear session + +### Multi-Device + +- [x] **Device Pairing** — authorize new device via pairing flow +- [x] **Device List** — view all authorized devices +- [x] **Device Removal** — revoke device authorization +- [x] **Self-Encryption** — own messages readable on all devices + +### Messaging + +- [x] **Text Messages** — encrypted DM and group messages +- [x] **Message Replies** — reply-to with visual indicator +- [x] **Reactions** — 6 emoji reactions (👍❤️😂😮😢👎) with toggle +- [x] **Message Pinning** — pin/unpin with pinned messages sheet +- [x] **Message Deletion** — soft delete with "Message deleted" indicator +- [x] **Message Forwarding** — forward to any conversation with source attribution +- [x] **Message Search** — full-text search with result navigation (prev/next) +- [x] **Read Receipts** — track who read each message +- [x] **Delivery Receipts** — sent → delivered → read indicators (checkmarks) +- [x] **Incremental Sync** — fetch only new messages via `after_ts` +- [x] **Deleted Sync** — `get_deleted_since` for incremental deletion sync +- [x] **Message Padding** — metadata privacy via bucket-based padding + +### Media & Files + +- [x] **Image Upload** — encrypt + chunked upload (24KB chunks) +- [x] **Image Thumbnails** — base64 JPEG preview inline +- [x] **Image Viewer** — full-screen with pinch zoom +- [x] **File Upload** — any file type with mime detection +- [x] **File Download** — decrypt + share via system share sheet +- [x] **File Icons** — type-based system icons (PDF, DOC, ZIP, etc.) + +### Conversations + +- [x] **Direct Messages** — 1-on-1 encrypted chat +- [x] **Group Conversations** — multi-member with Sender Keys +- [x] **Create Conversation** — new DM or group +- [x] **Rename Conversation** — group rename (creator only) +- [x] **Delete Conversation** — remove DM or delete group +- [x] **Favorites** — pin conversations to top with star icon +- [x] **Unread Counts** — per-conversation badge +- [x] **Online Status** — real-time presence (green dot) + +### Group Management + +- [x] **Add Member** — by email +- [x] **Remove Member** — creator only +- [x] **Leave Group** — with confirmation +- [x] **Group Avatar** — upload/change group photo +- [x] **Group Rename** — change group name +- [x] **Group Invitations** — accept/decline with banner UI + +### Profile + +- [x] **User Profile** — username, email, phone, location +- [x] **Avatar** — upload/change profile photo +- [x] **Field Visibility** — toggle phone/location visibility +- [x] **View Other Profiles** — see other users' info (respects visibility) + +### Contact Verification + +- [x] **Safety Numbers** — 60-digit verification code per contact pair +- [x] **Fingerprints** — identity key fingerprints +- [x] **QR Code Generation** — generate scannable verification QR +- [x] **QR Code Scanning** — camera-based QR scan for verification +- [x] **Verification Status** — verified (green) / trusted (blue) / unverified (gray) +- [x] **TOFU Registry** — track identity key first-seen, detect changes +- [x] **Shield Icon** — verification badge in chat toolbar + +### Connection & Reliability + +- [x] **TCP/TLS** — Network.framework with optional TLS +- [x] **Configurable Server** — host, port, TLS toggle in login screen +- [x] **Connection Indicator** — visual status (disconnected/connecting/connected) +- [x] **Auto-Reconnect** — exponential backoff (1s → 30s, 5 attempts) +- [x] **Background/Foreground Handling** — reconnect when returning from background +- [x] **Auth Failure Detection** — immediate logout on key rotation + +### Caching & Storage + +- [x] **Message Cache** — encrypted per-conversation cache on disk +- [x] **Avatar Cache** — disk + in-memory cache +- [x] **Conversation Cache** — cached list for instant UI +- [x] **Session Persistence** — Double Ratchet states saved encrypted +- [x] **Sender Key Persistence** — group key chains saved encrypted +- [x] **Device Bundle Cache** — 5-minute TTL in-memory +- [x] **Keychain Storage** — biometric-protected credentials + +--- + +## Network Protocol + +### Transport + +``` +TCP → optional TLS → Newline-delimited JSON (\n terminated) +``` + +### Message Format + +```json +{"type": "send_message", "request_id": "uuid", "conversation_id": "...", "ciphertext": "base64..."} +``` + +### API Methods (36 endpoints) + +**Auth:** `register`, `register_confirm`, `login_start`, `login_finish`, `change_username`, `change_password` + +**Keys:** `get_key_bundle`, `ensure_prekeys`, `get_prekey_count`, `rotate_keys`, `reset_session` + +**Messaging:** `send_message`, `get_messages`, `delete_message`, `mark_read`, `mark_conversation_read`, `react_message`, `pin_message`, `get_pinned_messages`, `get_deleted_since`, `forward_message`, `confirm_delivery`, `search_messages` + +**Conversations:** `list_conversations`, `create_conversation`, `find_conversation`, `delete_conversation`, `rename_conversation`, `add_member`, `remove_member`, `leave_group`, `accept_invitation`, `decline_invitation`, `list_invitations` + +**Profiles:** `get_profile`, `update_profile`, `update_avatar`, `get_avatar`, `update_group_avatar`, `get_group_avatar` + +**Files:** `upload_file`, `download_file` + +**Devices:** `list_devices`, `remove_device`, `pairing_start`, `pairing_wait`, `authorize_device` + +### Notification Types (17 real-time events) + +``` +new_message, messages_read, message_deleted, message_reacted, +message_pinned, message_unpinned, message_delivered, +conversation_created, conversation_renamed, conversation_deleted, +member_added, member_removed, group_invitation, +user_online, user_offline, online_users, +session_reset, keys_updated +``` + +--- + +## Storage Layout + +``` +~/Library/Application Support/EncryptedChat/{email}/ +├── private.pem # RSA private key (password-protected) +├── public.pem # RSA public key +├── identity_private.bin # Ed25519 private (ECP1: PBKDF2 + AES-GCM) +├── identity_public.bin # Ed25519 public +├── spk_private.bin # Current signed pre-key (X25519) +├── spk_id.txt # SPK ID +├── prevspk_private.bin # Previous SPK (grace period) +├── prevspk_id.txt +├── opk_{id}.bin # One-time pre-keys +├── sessions/ +│ └── {userId}_{deviceId}.bin # Double Ratchet state (encrypted) +├── sender_keys/ +│ └── {convId}_{senderId}_{deviceId}.bin # Sender Key chain (encrypted) +├── message_cache/ +│ └── {convId}.json # Message cache (encrypted) +├── conversations_cache.json # Conversation list cache (encrypted) +├── avatars/ +│ └── {convId}.bin # Avatar image data (encrypted) +├── known_identity_keys.bin # TOFU registry (encrypted) +├── verified_contacts.bin # Verified contacts (encrypted) +└── favorites.bin # Favorite conversation IDs (encrypted) +``` + +--- + +## Build + +```bash +# Xcode build +open /Users/filip/Desktop/kecalek_ios/Kecalek/Kecalek.xcodeproj + +# Command-line build +DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer \ +xcodebuild -scheme Kecalek \ + -destination 'platform=iOS Simulator,name=iPhone 17 Pro' \ + build +``` + +**Result:** 57 files, 0 errors, 0 warnings. diff --git a/ios_client 0.8.5/Kecalek.xcodeproj/project.pbxproj b/ios_client 0.8.5/Kecalek.xcodeproj/project.pbxproj new file mode 100644 index 0000000..1943678 --- /dev/null +++ b/ios_client 0.8.5/Kecalek.xcodeproj/project.pbxproj @@ -0,0 +1,351 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 77; + objects = { + +/* Begin PBXFileReference section */ + DC2D11D32F3CE6FD009F93FA /* Kecalek.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Kecalek.app; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFileSystemSynchronizedRootGroup section */ + DC2D11D52F3CE6FD009F93FA /* Kecalek */ = { + isa = PBXFileSystemSynchronizedRootGroup; + path = Kecalek; + sourceTree = ""; + }; +/* End PBXFileSystemSynchronizedRootGroup section */ + +/* Begin PBXFrameworksBuildPhase section */ + DC2D11D02F3CE6FD009F93FA /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + DC2D11CA2F3CE6FD009F93FA = { + isa = PBXGroup; + children = ( + DC2D11D52F3CE6FD009F93FA /* Kecalek */, + DC2D11D42F3CE6FD009F93FA /* Products */, + ); + sourceTree = ""; + }; + DC2D11D42F3CE6FD009F93FA /* Products */ = { + isa = PBXGroup; + children = ( + DC2D11D32F3CE6FD009F93FA /* Kecalek.app */, + ); + name = Products; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + DC2D11D22F3CE6FD009F93FA /* Kecalek */ = { + isa = PBXNativeTarget; + buildConfigurationList = DC2D11DE2F3CE6FF009F93FA /* Build configuration list for PBXNativeTarget "Kecalek" */; + buildPhases = ( + DC2D11CF2F3CE6FD009F93FA /* Sources */, + DC2D11D02F3CE6FD009F93FA /* Frameworks */, + DC2D11D12F3CE6FD009F93FA /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + fileSystemSynchronizedGroups = ( + DC2D11D52F3CE6FD009F93FA /* Kecalek */, + ); + name = Kecalek; + packageProductDependencies = ( + ); + productName = Kecalek; + productReference = DC2D11D32F3CE6FD009F93FA /* Kecalek.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + DC2D11CB2F3CE6FD009F93FA /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 2600; + LastUpgradeCheck = 2620; + TargetAttributes = { + DC2D11D22F3CE6FD009F93FA = { + CreatedOnToolsVersion = 26.0.1; + }; + }; + }; + buildConfigurationList = DC2D11CE2F3CE6FD009F93FA /* Build configuration list for PBXProject "Kecalek" */; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = DC2D11CA2F3CE6FD009F93FA; + minimizedProjectReferenceProxies = 1; + preferredProjectObjectVersion = 77; + productRefGroup = DC2D11D42F3CE6FD009F93FA /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + DC2D11D22F3CE6FD009F93FA /* Kecalek */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + DC2D11D12F3CE6FD009F93FA /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + DC2D11CF2F3CE6FD009F93FA /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + DC2D11DC2F3CE6FF009F93FA /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + DEVELOPMENT_TEAM = J26GZ5AW57; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 26.0; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + STRING_CATALOG_GENERATE_SYMBOLS = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + DC2D11DD2F3CE6FF009F93FA /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = J26GZ5AW57; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 26.0; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + STRING_CATALOG_GENERATE_SYMBOLS = YES; + SWIFT_COMPILATION_MODE = wholemodule; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + DC2D11DF2F3CE6FF009F93FA /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = J26GZ5AW57; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_CFBundleDisplayName = Kecalek; + INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.social-networking"; + INFOPLIST_KEY_NSCameraUsageDescription = "Scan QR codes for contact verification"; + INFOPLIST_KEY_NSFaceIDUsageDescription = "Sign in with Face ID"; + INFOPLIST_KEY_NSPhotoLibraryUsageDescription = "Share photos in chat"; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = Kecalek.lockmseg.com2; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + STRING_CATALOG_GENERATE_SYMBOLS = YES; + SWIFT_APPROACHABLE_CONCURRENCY = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + DC2D11E02F3CE6FF009F93FA /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = J26GZ5AW57; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_CFBundleDisplayName = Kecalek; + INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.social-networking"; + INFOPLIST_KEY_NSCameraUsageDescription = "Scan QR codes for contact verification"; + INFOPLIST_KEY_NSFaceIDUsageDescription = "Sign in with Face ID"; + INFOPLIST_KEY_NSPhotoLibraryUsageDescription = "Share photos in chat"; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = Kecalek.lockmseg.com2; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + STRING_CATALOG_GENERATE_SYMBOLS = YES; + SWIFT_APPROACHABLE_CONCURRENCY = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + DC2D11CE2F3CE6FD009F93FA /* Build configuration list for PBXProject "Kecalek" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + DC2D11DC2F3CE6FF009F93FA /* Debug */, + DC2D11DD2F3CE6FF009F93FA /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + DC2D11DE2F3CE6FF009F93FA /* Build configuration list for PBXNativeTarget "Kecalek" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + DC2D11DF2F3CE6FF009F93FA /* Debug */, + DC2D11E02F3CE6FF009F93FA /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = DC2D11CB2F3CE6FD009F93FA /* Project object */; +} diff --git a/ios_client 0.8.5/Kecalek.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/ios_client 0.8.5/Kecalek.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/ios_client 0.8.5/Kecalek.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/ios_client 0.8.5/Kecalek.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/ios_client 0.8.5/Kecalek.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..0c67376 --- /dev/null +++ b/ios_client 0.8.5/Kecalek.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,5 @@ + + + + + diff --git a/ios_client 0.8.5/Kecalek.xcodeproj/project.xcworkspace/xcuserdata/filip.xcuserdatad/UserInterfaceState.xcuserstate b/ios_client 0.8.5/Kecalek.xcodeproj/project.xcworkspace/xcuserdata/filip.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000000000000000000000000000000000000..ab8d6793a500d4ff4469a0c865ade70b6a0ce571 GIT binary patch literal 37791 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 literal 0 HcmV?d00001 diff --git a/ios_client 0.8.5/Kecalek.xcodeproj/project.xcworkspace/xcuserdata/filip.xcuserdatad/WorkspaceSettings.xcsettings b/ios_client 0.8.5/Kecalek.xcodeproj/project.xcworkspace/xcuserdata/filip.xcuserdatad/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..723a561 --- /dev/null +++ b/ios_client 0.8.5/Kecalek.xcodeproj/project.xcworkspace/xcuserdata/filip.xcuserdatad/WorkspaceSettings.xcsettings @@ -0,0 +1,16 @@ + + + + + BuildLocationStyle + UseAppPreferences + CompilationCachingSetting + Default + CustomBuildLocationType + RelativeToDerivedData + DerivedDataLocationStyle + Default + ShowSharedSchemesAutomaticallyEnabled + + + diff --git a/ios_client 0.8.5/Kecalek.xcodeproj/xcuserdata/filip.xcuserdatad/xcschemes/xcschememanagement.plist b/ios_client 0.8.5/Kecalek.xcodeproj/xcuserdata/filip.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..8619ae5 --- /dev/null +++ b/ios_client 0.8.5/Kecalek.xcodeproj/xcuserdata/filip.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,14 @@ + + + + + SchemeUserState + + Kecalek.xcscheme_^#shared#^_ + + orderHint + 0 + + + + diff --git a/ios_client 0.8.5/Kecalek/.DS_Store b/ios_client 0.8.5/Kecalek/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..142e921e20cdd0e1cc0b849199aec76a74d39978 GIT binary patch literal 8196 zcmeHMziSjh7=3d&8&jc}kkjsBdv@7q-w z#Q|~PzjQ!9A0l>*vBT7&d310vB>=I^Y14SjI>5w44r7O@MH9+%+Uh~MP~}PtWy851 zX*w}>m|C>qP&OROWmc|2Q6@X%B14CYEy^ekhyz&%B2FE~EbI0jnnC^EpJ`v2Sy)Xv zT^l@B{c}Vosnu56$qJd3^NSzu^t;8uhe{@2 zWEsAe&5bjY&#acA^Z2fOdw*wF@enK2WA2LsSme&M=t|3AbJ(5gHEPOX`MRFhA5S@5 zxy*r6CoVLPKC)Mk!_3il5SOT|iS^?B+iX)|F3;Ea{YQ1@sm)x#9jJV_we$MHZ@8h( zZKlV*zRvLOQeU5htG=ewr*0j0_m&s$?`1wC$Y0tvKSr2E-v4?hq=^HgJ7C{hn9%kA zx#I8tqbHC^aX=i{^A4zTZJ{? + private var notificationTask: Task? + private var isReconnecting = false + private var backgroundedAt: Date? + + /// Start listening for connection state changes (call after login) + func startConnectionMonitor() { + notificationTask?.cancel() + notificationTask = Task { [weak self] in + guard let self else { return } + let stream = await chatClient.makeNotificationStream() + for await notification in stream { + guard !Task.isCancelled else { break } + if case .connectionStateChanged(let connected) = notification { + await MainActor.run { + if connected { + self.connectionStatus = .connected + self.isReconnecting = false + self.reconnectTask?.cancel() + self.reconnectTask = nil + } else if self.isLoggedIn, !self.isReconnecting { + // Only start reconnect if not already reconnecting + // (reconnect() internally calls disconnect() which fires this) + self.connectionStatus = .disconnected + self.attemptReconnect() + } + } + } + } + } + } + + /// Attempt reconnect with exponential backoff; immediate logout on auth failure + @MainActor + private func attemptReconnect() { + reconnectTask?.cancel() + isReconnecting = true + reconnectTask = Task { [weak self] in + guard let self else { return } + let maxAttempts = 5 + var delay: TimeInterval = Constants.reconnectBaseDelay + + for attempt in 1...maxAttempts { + guard !Task.isCancelled, self.isLoggedIn else { return } + + self.connectionStatus = .reconnecting + #if DEBUG + print("DEBUG AppState: reconnect attempt \(attempt)/\(maxAttempts), delay=\(delay)s") + #endif + + try? await Task.sleep(nanoseconds: UInt64(delay * 1_000_000_000)) + guard !Task.isCancelled, self.isLoggedIn else { return } + + let result = await self.chatClient.reconnect() + switch result { + case .success: + self.connectionStatus = .connected + self.isReconnecting = false + #if DEBUG + print("DEBUG AppState: reconnected on attempt \(attempt)") + #endif + return + case .authFailed: + // Keys rotated or invalid — logout immediately, don't retry + self.isReconnecting = false + #if DEBUG + print("DEBUG AppState: auth failed (keys likely rotated), logging out immediately") + #endif + await self.logout() + return + case .networkError: + // Network issue — retry with backoff + delay = min(delay * 2, Constants.reconnectMaxDelay) + } + } + + // All network retries exhausted → force logout + self.isReconnecting = false + guard !Task.isCancelled, self.isLoggedIn else { return } + #if DEBUG + print("DEBUG AppState: reconnect failed after \(maxAttempts) attempts, logging out") + #endif + await self.logout() + } + } + + // MARK: - App Lifecycle + + func handleEnteredBackground() { + backgroundedAt = Date() + } + + @MainActor + func handleBecameActive() { + guard isLoggedIn, !isReconnecting else { return } + let wasInBackground = backgroundedAt.map { Date().timeIntervalSince($0) } ?? 0 + backgroundedAt = nil + + Task { + let alive = await chatClient.isConnectionAlive() + if !alive { + #if DEBUG + print("DEBUG AppState: foreground — connection dead, reconnecting") + #endif + await MainActor.run { + guard !self.isReconnecting else { return } + self.connectionStatus = .reconnecting + self.attemptReconnect() + } + } else if wasInBackground > 30 { + // Connection appears alive but was backgrounded a long time — + // force reconnect to ensure fresh state + #if DEBUG + print("DEBUG AppState: foreground — stale connection (\(Int(wasInBackground))s), reconnecting") + #endif + await MainActor.run { + guard !self.isReconnecting else { return } + self.connectionStatus = .reconnecting + self.attemptReconnect() + } + } else { + #if DEBUG + print("DEBUG AppState: foreground — connection alive (\(Int(wasInBackground))s in bg)") + #endif + } + } + } + + func logout() async { + isReconnecting = false + reconnectTask?.cancel() + reconnectTask = nil + notificationTask?.cancel() + notificationTask = nil + await chatClient.disconnect() + KeychainService.deleteCredentials() + isLoggedIn = false + currentUser = nil + connectionStatus = .disconnected + email = "" + } +} diff --git a/ios_client 0.8.5/Kecalek/Assets.xcassets/AccentColor.colorset/Contents.json b/ios_client 0.8.5/Kecalek/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/ios_client 0.8.5/Kecalek/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ios_client 0.8.5/Kecalek/Assets.xcassets/AppIcon.appiconset/AppIcon.png b/ios_client 0.8.5/Kecalek/Assets.xcassets/AppIcon.appiconset/AppIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..bcf9dd0343ffd7c0ddcf28dc834f621337d32122 GIT binary patch literal 718842 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�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%0A&QtM8gNv~~>@ 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�k1Y1q5uawOq<<{!!Dt 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∾t#B)!?zghwZZ=M3c4zAa1BP4yXTwtvU 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 literal 0 HcmV?d00001 diff --git a/ios_client 0.8.5/Kecalek/Assets.xcassets/AppIcon.appiconset/Contents.json b/ios_client 0.8.5/Kecalek/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..87d4015 --- /dev/null +++ b/ios_client 0.8.5/Kecalek/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,38 @@ +{ + "images" : [ + { + "filename" : "AppIcon.png", + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "AppIcon.png", + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "filename" : "AppIcon.png", + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ios_client 0.8.5/Kecalek/Assets.xcassets/Contents.json b/ios_client 0.8.5/Kecalek/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/ios_client 0.8.5/Kecalek/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ios_client 0.8.5/Kecalek/Core/ChatClient.swift b/ios_client 0.8.5/Kecalek/Core/ChatClient.swift new file mode 100644 index 0000000..8a0c4c6 --- /dev/null +++ b/ios_client 0.8.5/Kecalek/Core/ChatClient.swift @@ -0,0 +1,3666 @@ +import Foundation +import CryptoKit +import UIKit + +/// Notification types from the server +enum ChatNotification { + case newMessage(data: [String: Any]) + case messagesRead(data: [String: Any]) + case messageDeleted(data: [String: Any]) + case conversationCreated(data: [String: Any]) + case memberAdded(data: [String: Any]) + case memberRemoved(data: [String: Any]) + case userOnline(userId: String) + case userOffline(userId: String) + case onlineUsers(userIds: [String]) + case groupInvitation(data: [String: Any]) + case conversationRenamed(data: [String: Any]) + case sessionReset(data: [String: Any]) + case messageReacted(data: [String: Any]) + case messagePinned(data: [String: Any]) + case messageUnpinned(data: [String: Any]) + case messageDelivered(data: [String: Any]) + case conversationDeleted(data: [String: Any]) + case connectionStateChanged(connected: Bool) + case reconnected +} + +/// Result of a reconnect attempt +enum ReconnectResult { + case success + case authFailed // Keys invalid (e.g. rotated) — should logout immediately + case networkError // TCP/connection issue — can retry +} + +/// Main chat client — handles all server communication and crypto operations. +/// Thread-safe via Swift actor isolation. +/// Port of Python ChatClient class from chat_core.py +actor ChatClient { + + // MARK: - Connection + + let connectionManager = ConnectionManager() + private(set) var isConnected = false + private var lastHost = Constants.defaultHost + private var lastPort = Constants.defaultPort + private(set) var sessionToken: String? + private(set) var userId: String? + private(set) var username: String = "" + private(set) var email: String = "" + private(set) var loginRejected = false + private(set) var onlineUserIds: Set = [] + + // MARK: - Keys + + private var rsaPrivate: SecKey? + private var rsaPublic: SecKey? + private(set) var identityPrivate: Curve25519.Signing.PrivateKey? + private(set) var identityPublic: Curve25519.Signing.PublicKey? + private var spkPrivate: Curve25519.KeyAgreement.PrivateKey? + private var spkId: String = "" + private var prevSpkPrivate: Curve25519.KeyAgreement.PrivateKey? + private var prevSpkId: String = "" + private var opkPrivates: [String: Curve25519.KeyAgreement.PrivateKey] = [:] + + // MARK: - Sessions & Sender Keys + + private var sessions: [String: DoubleRatchet] = [:] // "userId:deviceId" -> ratchet + private var senderKeyStates: [String: SenderKeyState] = [:] // convId -> own sender key + private var recvSenderKeys: [String: SenderKeyState] = [:] // "convId:senderId:deviceId" -> their key + + // MARK: - Derived Keys + + private(set) var cacheKey: Data? // for encrypting message cache + private(set) var localKey: Data? // for encrypting session/sender key files + + // MARK: - Multi-Device + + private(set) var deviceId: String? + + // MARK: - Caches + + private var userCache: [String: User] = [:] + private var deviceBundleCache: [String: (timestamp: Date, bundles: [DeviceBundle])] = [:] + + // MARK: - Self-Encrypt Queue + + private var pendingSelfEncrypt: [(messageId: String, plaintext: Data)] = [] + + // MARK: - TOFU / Contact Verification + + private var knownIdentityKeys: [String: [String: String]] = [:] // userId -> {identity_key, first_seen, last_seen} + private var verifiedContacts: [String: [String: String]] = [:] // userId -> {identity_key, verified_at, method} + + // MARK: - Brute-Force Lockout + + private static let lockoutBaseSeconds: TimeInterval = 2 + private static let lockoutMaxSeconds: TimeInterval = 300 + + // MARK: - Request/Response Tracking + + private var pendingRequests: [String: CheckedContinuation<[String: Any], Error>] = [:] + private var listenerTask: Task? + + // MARK: - Notification Broadcast (supports multiple consumers) + + private var notificationSubscribers: [UUID: AsyncStream.Continuation] = [:] + + /// Create a new notification stream. Each subscriber gets ALL notifications. + func makeNotificationStream() -> AsyncStream { + let id = UUID() + var captured: AsyncStream.Continuation! + let stream = AsyncStream { cont in + captured = cont + } + captured.onTermination = { [weak self] _ in + Task { await self?.removeSubscriber(id: id) } + } + notificationSubscribers[id] = captured + return stream + } + + private func removeSubscriber(id: UUID) { + notificationSubscribers.removeValue(forKey: id) + } + + private func broadcastNotification(_ notification: ChatNotification) { + for (_, continuation) in notificationSubscribers { + continuation.yield(notification) + } + } + + // MARK: - Init + + init() {} + + // MARK: - Connection + + func connect(host: String = Constants.defaultHost, port: UInt16 = Constants.defaultPort) async throws { + try await connectionManager.connect(host: host, port: port) + lastHost = host + lastPort = port + isConnected = true + startBackgroundListener() + broadcastNotification(.connectionStateChanged(connected: true)) + } + + /// Check if the underlying TCP connection is actually alive + func isConnectionAlive() async -> Bool { + guard isConnected else { return false } + return await connectionManager.isConnected + } + + func disconnect() async { + listenerTask?.cancel() + listenerTask = nil + await connectionManager.disconnect() + isConnected = false + // Fail all pending requests + let pending = pendingRequests + pendingRequests.removeAll() + for (_, cont) in pending { + cont.resume(throwing: NetworkError.notConnected) + } + broadcastNotification(.connectionStateChanged(connected: false)) + } + + // MARK: - Send and Receive + + /// Send a request and wait for the matching response. + func sendAndReceive(type: String, timeout: TimeInterval = 30, params: [String: Any] = [:]) async -> [String: Any] { + let requestId = ProtocolHandler.newRequestId() + #if DEBUG + print("DEBUG sendAndReceive: type=\(type), requestId=\(requestId)") + #endif + + // Timeout task — resumes continuation with error if server doesn't respond + let timeoutTask = Task { + try await Task.sleep(nanoseconds: UInt64(timeout * 1_000_000_000)) + if let cont = pendingRequests.removeValue(forKey: requestId) { + cont.resume(throwing: NetworkError.timeout) + } + } + + do { + let response: [String: Any] = try await withCheckedThrowingContinuation { continuation in + pendingRequests[requestId] = continuation + + Task { + do { + try await connectionManager.sendMessage(type: type, requestId: requestId, params: params) + } catch { + if let cont = pendingRequests.removeValue(forKey: requestId) { + cont.resume(throwing: error) + } + } + } + } + timeoutTask.cancel() + return response + } catch { + timeoutTask.cancel() + pendingRequests.removeValue(forKey: requestId) + return [ + "type": type, + "status": "error", + "data": ["message": error.localizedDescription] + ] + } + } + + // MARK: - Background Listener + + func startBackgroundListener() { + listenerTask?.cancel() + listenerTask = Task { [weak self] in + guard let self = self else { return } + await self.backgroundListenerLoop() + } + } + + private func backgroundListenerLoop() async { + while !Task.isCancelled { + do { + guard let msg = try await connectionManager.readMessage() else { + // EOF — connection closed + handleDisconnect() + break + } + routeMessage(msg) + } catch { + handleDisconnect() + break + } + } + } + + private func handleDisconnect() { + isConnected = false + // Fail all pending futures + let pending = pendingRequests + pendingRequests.removeAll() + for (_, cont) in pending { + cont.resume(throwing: NetworkError.notConnected) + } + broadcastNotification(.connectionStateChanged(connected: false)) + } + + private func routeMessage(_ msg: [String: Any]) { + #if DEBUG + print("DEBUG routeMessage received: \(msg)") + #endif + let msgType = msg["type"] as? String ?? "" + + // Notification types (no request_id expected from client) + let notificationTypes = Set([ + "new_message", "messages_read", "message_deleted", + "conversation_created", "member_added", "member_removed", + "user_online", "user_offline", "online_users", + "group_invitation", "conversation_renamed", "session_reset", + "keys_updated", "message_reacted", "message_pinned", "message_unpinned", + "message_delivered", "conversation_deleted" + ]) + + if notificationTypes.contains(msgType) { + let data = msg["data"] as? [String: Any] ?? msg + switch msgType { + case "new_message": + broadcastNotification(.newMessage(data: data)) + case "messages_read": + broadcastNotification(.messagesRead(data: data)) + case "message_deleted": + broadcastNotification(.messageDeleted(data: data)) + case "conversation_created": + broadcastNotification(.conversationCreated(data: data)) + case "member_added": + broadcastNotification(.memberAdded(data: data)) + case "member_removed": + broadcastNotification(.memberRemoved(data: data)) + case "user_online": + if let uid = data["user_id"] as? String { + onlineUserIds.insert(uid) + broadcastNotification(.userOnline(userId: uid)) + } + case "user_offline": + if let uid = data["user_id"] as? String { + onlineUserIds.remove(uid) + broadcastNotification(.userOffline(userId: uid)) + } + case "online_users": + if let uids = data["user_ids"] as? [String] { + onlineUserIds = Set(uids) + broadcastNotification(.onlineUsers(userIds: uids)) + } + case "group_invitation": + broadcastNotification(.groupInvitation(data: data)) + case "conversation_renamed": + broadcastNotification(.conversationRenamed(data: data)) + case "session_reset": + broadcastNotification(.sessionReset(data: data)) + case "message_reacted": + broadcastNotification(.messageReacted(data: data)) + case "message_pinned": + broadcastNotification(.messagePinned(data: data)) + case "message_unpinned": + broadcastNotification(.messageUnpinned(data: data)) + case "message_delivered": + broadcastNotification(.messageDelivered(data: data)) + case "conversation_deleted": + broadcastNotification(.conversationDeleted(data: data)) + case "keys_updated": + // Peer uploaded new prekeys (new device or rotation) — invalidate bundle cache + if let uid = data["user_id"] as? String { + deviceBundleCache.removeValue(forKey: uid) + #if DEBUG + print("DEBUG keys_updated: invalidated bundle cache for \(uid)") + #endif + } + default: + break + } + } else { + // Response to a pending request + if let requestId = msg["request_id"] as? String, + let cont = pendingRequests.removeValue(forKey: requestId) { + cont.resume(returning: msg) + } + } + } + + // MARK: - User Info Cache + + func getUserInfo(userId: String = "", userEmail: String = "") async -> User? { + if !userId.isEmpty, let cached = userCache[userId] { + return cached + } + var params: [String: Any] = [:] + if !userId.isEmpty { params["user_id"] = userId } + else if !userEmail.isEmpty { params["email"] = userEmail } + else { return nil } + + let resp = await sendAndReceive(type: "get_user_info", params: params) + guard resp.string(for: "status") == "ok", + let data = resp.dict(for: "data") else { return nil } + + var ikData: Data? + if let ikB64 = data["identity_key"] as? String { + ikData = try? ProtocolHandler.decodeBinary(ikB64) + } + + let user = User( + id: data.string(for: "user_id") ?? "", + username: data.string(for: "username") ?? "", + email: data.string(for: "email") ?? "", + identityKey: ikData + ) + userCache[user.id] = user + return user + } + + // MARK: - Registration + + func register(username: String, password: String, email: String) async -> (success: Bool, message: String) { + self.username = username + self.email = email + var pwdBytes = Array(password.utf8) + defer { pwdBytes.withUnsafeMutableBytes { ptr in _ = memset(ptr.baseAddress!, 0, ptr.count) } } + + let pwdData = Data(pwdBytes) + + do { + // RSA keys + let (rsaPriv, rsaPub, _) = KeyStorage.loadRSAKeys(email: email, password: pwdData) + if let rsaPriv = rsaPriv, let rsaPub = rsaPub { + self.rsaPrivate = rsaPriv + self.rsaPublic = rsaPub + } else { + let (newPriv, newPub) = try RSACrypto.generateKeypair() + try KeyStorage.saveRSAKeys(email: email, privateKey: newPriv, publicKey: newPub, password: pwdData) + self.rsaPrivate = newPriv + self.rsaPublic = newPub + } + + // Ed25519 identity keys + let (edPriv, edPub) = KeyStorage.loadIdentityKeys(email: email, password: pwdData) + if let edPriv = edPriv, let edPub = edPub { + self.identityPrivate = edPriv + self.identityPublic = edPub + } else { + let (newPriv, newPub) = Ed25519Crypto.generateKeypair() + try KeyStorage.saveIdentityKeys(email: email, privateKey: newPriv, publicKey: newPub, password: pwdData) + self.identityPrivate = newPriv + self.identityPublic = newPub + } + + self.cacheKey = CryptoUtils.deriveSelfEncryptionKey(identityPrivateRaw: identityPrivate!.rawData) + self.localKey = CryptoUtils.deriveLocalStorageKey(identityPrivateRaw: identityPrivate!.rawData) + } catch { + return (false, "Key generation failed: \(error.localizedDescription)") + } + + // Send registration request + let pubPem = String(data: try! RSACrypto.serializePublicKey(rsaPublic!), encoding: .utf8)! + let ikB64 = ProtocolHandler.encodeBinary(Ed25519Crypto.serializePublic(identityPublic!)) + + let resp = await sendAndReceive(type: "register", params: [ + "username": username, + "public_key": pubPem, + "email": email, + "identity_key": ikB64, + ]) + + #if DEBUG + print("DEBUG register response: \(resp)") + #endif + + // Handle PoW challenge if required by server + if resp.string(for: "status") == "pow_required", + let powData = resp.dict(for: "data"), + let powChallenge = powData.string(for: "challenge"), + let powMac = powData.string(for: "mac"), + let powDifficulty = powData.int(for: "difficulty") { + #if DEBUG + print("DEBUG register: PoW required, difficulty=\(powDifficulty)") + #endif + let powNonce = ChatClient.solvePow(challenge: powChallenge, difficulty: powDifficulty) + #if DEBUG + print("DEBUG register: PoW solved, nonce=\(powNonce)") + #endif + + // Retry with PoW solution + let retryResp = await sendAndReceive(type: "register", params: [ + "username": username, + "public_key": pubPem, + "email": email, + "identity_key": ikB64, + "pow_challenge": powChallenge, + "pow_mac": powMac, + "pow_nonce": powNonce, + ]) + + guard retryResp.string(for: "status") == "ok" else { + let msg = retryResp.dict(for: "data")?.string(for: "message") ?? "Registration failed after PoW" + return (false, msg) + } + let retryData = retryResp.dict(for: "data") ?? [:] + if let code = retryData.string(for: "code") { + return (true, code) + } + return (true, retryData.string(for: "message") ?? "Check your email for the code.") + } + + guard resp.string(for: "status") == "ok" else { + let msg = resp.dict(for: "data")?.string(for: "message") ?? "Registration failed" + return (false, msg) + } + + let data = resp.dict(for: "data") ?? [:] + if let code = data.string(for: "code") { + return (true, code) + } + return (true, data.string(for: "message") ?? "Check your email for the code.") + } + + func confirmRegistration(email: String, username: String, code: String) async -> (success: Bool, message: String) { + #if DEBUG + print("DEBUG confirmRegistration: email=\(email), code=\(code)") + #endif + let resp = await sendAndReceive(type: "register_confirm", params: [ + "email": email, + "code": code, + ]) + + guard resp.string(for: "status") == "ok" else { + let msg = resp.dict(for: "data")?.string(for: "message") ?? "Confirmation failed" + return (false, msg) + } + + // NOTE: Don't upload prekeys here - user isn't logged in yet. + // ensurePrekeys() will be called after login. + + let uid = resp.dict(for: "data")?.string(for: "user_id") ?? "" + return (true, "Registered as '\(username)' (ID: \(uid))") + } + + // MARK: - Prekeys + + private func generateAndUploadPrekeys(keepSPK: Bool = false) async { + guard let identityPrivate = identityPrivate else { return } + + do { + let spkData: [String: Any] + + if keepSPK, let spkPriv = spkPrivate, !spkId.isEmpty { + let spkPubBytes = X25519Crypto.serializePublic(spkPriv.publicKey) + let sig = try Ed25519Crypto.sign(identityPrivate, data: spkPubBytes) + spkData = [ + "id": spkId, + "public_key": ProtocolHandler.encodeBinary(spkPubBytes), + "signature": ProtocolHandler.encodeBinary(sig), + ] + } else { + // Save current as previous (grace period) + if let spkPriv = spkPrivate, !spkId.isEmpty { + prevSpkPrivate = spkPriv + prevSpkId = spkId + try? KeyStorage.savePrevSPK(email: email, privateKey: spkPriv, spkId: spkId) + } + + let spk = try X3DH.generateSignedPrekey(identityPrivate: identityPrivate) + self.spkPrivate = spk.privateKey + self.spkId = spk.id + try? KeyStorage.saveSPK(email: email, privateKey: spk.privateKey, spkId: spk.id) + + spkData = [ + "id": spk.id, + "public_key": ProtocolHandler.encodeBinary(X25519Crypto.serializePublic(spk.publicKey)), + "signature": ProtocolHandler.encodeBinary(spk.signature), + ] + } + + // Generate OPKs + let opks = X3DH.generateOneTimePrekeys(count: Constants.opkBatchSize) + #if DEBUG + print("DEBUG generatePrekeys: generating \(opks.count) OPKs") + #endif + for opk in opks { + opkPrivates[opk.id] = opk.privateKey + do { + try KeyStorage.saveOPKPrivate(email: email, opkId: opk.id, privateKey: opk.privateKey) + } catch { + #if DEBUG + print("DEBUG generatePrekeys: FAILED to save OPK \(opk.id): \(error)") + #endif + } + } + #if DEBUG + print("DEBUG generatePrekeys: OPK IDs = \(opks.map { $0.id })") + #endif + + let otpData = opks.map { opk -> [String: Any] in + [ + "id": opk.id, + "public_key": ProtocolHandler.encodeBinary(X25519Crypto.serializePublic(opk.publicKey)), + ] + } + + let uploadResp = await sendAndReceive(type: "ensure_prekeys", params: [ + "signed_prekey": spkData, + "one_time_prekeys": otpData, + ]) + let uploadStatus = uploadResp.string(for: "status") ?? "nil" + #if DEBUG + print("DEBUG generatePrekeys: ensure_prekeys response status=\(uploadStatus) deviceId=\(deviceId ?? "nil")") + #endif + if uploadStatus != "ok" { + let errMsg = uploadResp.dict(for: "data")?.string(for: "message") ?? "unknown" + #if DEBUG + print("DEBUG generatePrekeys: upload FAILED: \(errMsg)") + #endif + } + } catch { + // Log error but don't fail + #if DEBUG + print("Prekey generation error: \(error)") + #endif + } + } + + private func ensurePrekeys() async { + let resp = await sendAndReceive(type: "get_prekey_count") + guard resp.string(for: "status") == "ok", + let data = resp.dict(for: "data") else { return } + + let count = data.int(for: "count") ?? 0 + let spkCreatedAt = data.string(for: "spk_created_at") ?? "" + + var needNewSPK = false + if !spkCreatedAt.isEmpty { + if let created = DateParsing.parse(spkCreatedAt) { + let ageDays = Calendar.current.dateComponents([.day], from: created, to: Date()).day ?? 0 + if ageDays >= Constants.spkRotationDays { + needNewSPK = true + } + } + } + + if count < Constants.opkReplenishThreshold || needNewSPK { + await generateAndUploadPrekeys() + } + } + + // MARK: - Login + + func login(email: String, password: String) async -> (success: Bool, message: String) { + self.email = email + + // Check brute-force lockout + let lockoutRemaining = ChatClient.checkLockout(email: email) + if lockoutRemaining > 0 { + return (false, "Too many failed attempts. Wait \(Int(lockoutRemaining))s.") + } + + var pwdBytes = Array(password.utf8) + defer { pwdBytes.withUnsafeMutableBytes { ptr in _ = memset(ptr.baseAddress!, 0, ptr.count) } } + let pwdData = Data(pwdBytes) + + // Load RSA keys + let (rsaPriv, rsaPub, err) = KeyStorage.loadRSAKeys(email: email, password: pwdData) + guard let rsaPriv = rsaPriv, let rsaPub = rsaPub else { + return (false, err ?? "No local keys found. Register first.") + } + self.rsaPrivate = rsaPriv + self.rsaPublic = rsaPub + + // Load identity keys + let (edPriv, edPub) = KeyStorage.loadIdentityKeys(email: email, password: pwdData) + if let edPriv = edPriv, let edPub = edPub { + self.identityPrivate = edPriv + self.identityPublic = edPub + self.cacheKey = CryptoUtils.deriveSelfEncryptionKey(identityPrivateRaw: edPriv.rawData) + self.localKey = CryptoUtils.deriveLocalStorageKey(identityPrivateRaw: edPriv.rawData) + } + + // Load SPK + let (spkP, spkI) = KeyStorage.loadSPK(email: email) + if let spkP = spkP { + self.spkPrivate = spkP + self.spkId = spkI ?? "" + } + + // Load previous SPK (grace period) + let (prevP, prevI) = KeyStorage.loadPrevSPK(email: email) + if let prevP = prevP { + self.prevSpkPrivate = prevP + self.prevSpkId = prevI ?? "" + } + + // Load device ID + self.deviceId = KeyStorage.loadDeviceId(email: email) + + // RSA challenge-response login + let startResp = await sendAndReceive(type: "login_start", params: ["email": email]) + guard startResp.string(for: "status") == "ok", + let startData = startResp.dict(for: "data"), + let challengeB64 = startData.string(for: "challenge") else { + let msg = startResp.dict(for: "data")?.string(for: "message") ?? "Login failed" + return (false, msg) + } + + let challengeData: Data + do { + challengeData = try ProtocolHandler.decodeBinary(challengeB64) + } catch { + return (false, "Invalid challenge data") + } + + let signature: Data + do { + signature = try RSACrypto.sign(rsaPriv, data: challengeData) + } catch { + return (false, "RSA signing failed: \(error.localizedDescription)") + } + + var finishParams: [String: Any] = [ + "email": email, + "signature": ProtocolHandler.encodeBinary(signature), + "client_version": Constants.version, + ] + if let deviceId = deviceId { + finishParams["device_id"] = deviceId + } + + let finishResp = await sendAndReceive(type: "login_finish", params: finishParams) + guard finishResp.string(for: "status") == "ok", + let finishData = finishResp.dict(for: "data") else { + let msg = finishResp.dict(for: "data")?.string(for: "message") ?? "Login failed" + loginRejected = true + ChatClient.recordFailedAttempt(email: email) + return (false, msg) + } + + self.userId = finishData.string(for: "user_id") + self.username = finishData.string(for: "username") ?? "" + self.sessionToken = finishData.string(for: "session_token") + + // Save device ID from server + if let newDeviceId = finishData.string(for: "device_id") { + self.deviceId = newDeviceId + try? KeyStorage.saveDeviceId(email: email, deviceId: newDeviceId) + } + + // Handle online_users if included + if let onlineUserIds = finishData["online_user_ids"] as? [String] { + self.onlineUserIds = Set(onlineUserIds) + broadcastNotification(.onlineUsers(userIds: onlineUserIds)) + } + + // Check if we have local OPK private keys. + // After device pairing, the new device has no local OPKs — must generate fresh ones. + let hasLocalOPKs: Bool + if let dir = try? KeyStorage.getKeyDir(email: email) { + let opkDir = dir.appendingPathComponent("opk_private") + let files = (try? FileManager.default.contentsOfDirectory(atPath: opkDir.path)) ?? [] + hasLocalOPKs = !files.isEmpty + } else { + hasLocalOPKs = false + } + + if hasLocalOPKs { + // Existing device — check/replenish prekeys in background + Task { await ensurePrekeys() } + } else { + // New device — MUST upload prekeys before returning so other clients + // can encrypt for this device immediately. Fire-and-forget would create + // a race where senders fetch bundles before prekeys exist on server. + #if DEBUG + print("DEBUG login: no local OPKs (likely new device). Generating fresh prekeys (synchronous).") + #endif + await generateAndUploadPrekeys(keepSPK: true) + } + + // Load previous SPK for grace period (M4) + let (prevSPK, prevSPKId) = KeyStorage.loadPrevSPK(email: email) + if let prevSPK = prevSPK { + prevSpkPrivate = prevSPK + prevSpkId = prevSPKId ?? "" + } + + // Load TOFU and verification stores + loadVerificationStores() + + // Clear lockout on successful login + ChatClient.clearLockout(email: email) + + return (true, "Logged in as \(username)") + } + + // MARK: - Reconnect + + func reconnect() async -> ReconnectResult { + guard rsaPrivate != nil else { return .authFailed } + + let host = lastHost + let port = lastPort + + await disconnect() + + do { + try await connect(host: host, port: port) + } catch { + return .networkError + } + + // TCP connected — try RSA challenge-response with in-memory keys + let startResp = await sendAndReceive(type: "login_start", params: ["email": email]) + guard startResp.string(for: "status") == "ok", + let startData = startResp.dict(for: "data"), + let challengeB64 = startData.string(for: "challenge"), + let challengeData = try? ProtocolHandler.decodeBinary(challengeB64), + let signature = try? RSACrypto.sign(rsaPrivate!, data: challengeData) else { + // TCP was fine but login_start failed — auth issue + return .authFailed + } + + var finishParams: [String: Any] = [ + "email": email, + "signature": ProtocolHandler.encodeBinary(signature), + "client_version": Constants.version, + ] + if let deviceId = deviceId { + finishParams["device_id"] = deviceId + } + + let finishResp = await sendAndReceive(type: "login_finish", params: finishParams) + guard finishResp.string(for: "status") == "ok", + let finishData = finishResp.dict(for: "data") else { + // TCP connected, got challenge, but signature rejected — keys rotated + return .authFailed + } + + // Update session token if server returned a new one + if let token = finishData.string(for: "session_token") { + self.sessionToken = token + } + + // Refresh online users (matches Python: self.session = finish["data"]) + if let onlineUserIds = finishData["online_user_ids"] as? [String] { + self.onlineUserIds = Set(onlineUserIds) + broadcastNotification(.onlineUsers(userIds: onlineUserIds)) + } + + // Replenish prekeys if needed (matches Python: asyncio.create_task(self._ensure_prekeys())) + Task { await ensurePrekeys() } + + broadcastNotification(.reconnected) + return .success + } + + // MARK: - Device Bundles + + private func getDeviceBundles(userId: String) async throws -> [DeviceBundle] { + // Check cache (5-min TTL) + if let cached = deviceBundleCache[userId], + Date().timeIntervalSince(cached.timestamp) < Constants.deviceBundleCacheTTL { + return cached.bundles + } + + let resp = await sendAndReceive(type: "get_key_bundle", params: ["user_id": userId]) + guard resp.string(for: "status") == "ok", + let data = resp.dict(for: "data") else { + throw ChatError.operationFailed("Failed to get key bundle") + } + + var bundles: [DeviceBundle] = [] + + // Get identity key from top level (shared across all device bundles) + var identityKey: Data? + if let ikB64 = data["identity_key"] as? String { + identityKey = Data(base64Encoded: ikB64) + } + + // Per-device bundles (new format) + if let deviceBundlesRaw = data["device_bundles"] as? [[String: Any]] { + for bundleDict in deviceBundlesRaw { + if let bundle = try? DeviceBundle.fromDict(bundleDict, identityKey: identityKey) { + bundles.append(bundle) + } + } + } + // Legacy single bundle + else if identityKey != nil { + let bundle = try DeviceBundle.fromDict(data, identityKey: identityKey) + bundles.append(bundle) + } + + deviceBundleCache[userId] = (Date(), bundles) + return bundles + } + + // MARK: - Session Management + + private func getOrCreateSession( + peerUserId: String, + peerDeviceId: String, + bundle: DeviceBundle + ) async throws -> DoubleRatchet { + let sessionKey = "\(peerUserId):\(peerDeviceId)" + #if DEBUG + print("DEBUG getOrCreateSession: looking for session \(sessionKey)") + #endif + + // Check memory + if let session = sessions[sessionKey] { + #if DEBUG + print("DEBUG getOrCreateSession: found in memory") + #endif + return session + } + + // Check disk + if let session = KeyStorage.loadSession( + email: email, + peerUserId: peerUserId, + localKey: localKey, + peerDeviceId: peerDeviceId + ) { + #if DEBUG + print("DEBUG getOrCreateSession: found on disk") + #endif + sessions[sessionKey] = session + return session + } + + #if DEBUG + print("DEBUG getOrCreateSession: creating new session via X3DH") + #endif + + // Create new via X3DH + let remoteIkEd = try Ed25519Crypto.loadPublic(bundle.identityKey) + let spkRemote = try X25519Crypto.loadPublic(bundle.spk) + var opkRemote: Curve25519.KeyAgreement.PublicKey? + if let opkData = bundle.opk { + opkRemote = try X25519Crypto.loadPublic(opkData) + } + + let (sharedSecret, _, ekPub) = try X3DH.initiate( + ikPrivateEd: identityPrivate!, + ikPublicRemoteEd: remoteIkEd, + spkRemote: spkRemote, + spkSignature: bundle.spkSignature, + opkRemote: opkRemote + ) + + let ratchet = try DoubleRatchet.initAlice(sharedSecret: sharedSecret, bobSpkPub: spkRemote) + + // Build X3DH header for first message + // Keys must be base64 encoded (not hex) to match Python server + let myIkBytes = Ed25519Crypto.serializePublic(identityPublic!) + let ekPubBytes = X25519Crypto.serializePublic(ekPub) + #if DEBUG + print("DEBUG getOrCreateSession: sending ik len=\(myIkBytes.count)") + print("DEBUG getOrCreateSession: sending ek len=\(ekPubBytes.count)") + #endif + var x3dhHeader: [String: Any] = [ + "ik": ProtocolHandler.encodeBinary(myIkBytes), + "ek": ProtocolHandler.encodeBinary(ekPubBytes), + "spk_id": bundle.spkId, + ] + if let opkId = bundle.opkId { + x3dhHeader["opk_id"] = opkId + #if DEBUG + print("DEBUG getOrCreateSession: using opk_id = \(opkId)") + #endif + } + ratchet.x3dhHeader = x3dhHeader + + sessions[sessionKey] = ratchet + try? KeyStorage.saveSession(email: email, peerUserId: peerUserId, ratchet: ratchet, localKey: localKey, peerDeviceId: peerDeviceId) + + return ratchet + } + + // MARK: - X3DH Response (Bob Side) + + private func processX3DHHeader( + senderId: String, + x3dhHeader: [String: Any], + senderDeviceId: String, + spkOverride: Curve25519.KeyAgreement.PrivateKey? = nil + ) throws -> DoubleRatchet { + guard let ikB64 = x3dhHeader["ik"] as? String, + let ikData = try? ProtocolHandler.decodeBinary(ikB64), + let ekB64 = x3dhHeader["ek"] as? String, + let ekData = try? ProtocolHandler.decodeBinary(ekB64) else { + throw CryptoError.x3dhFailed("Invalid X3DH header - missing ik or ek") + } + + // spk_id is optional - if missing, use current SPK + let spkIdStr = x3dhHeader["spk_id"] as? String + #if DEBUG + print("DEBUG processX3DHHeader: ik=\(ikData.count)B, ek=\(ekData.count)B, spk_id=\(spkIdStr ?? "nil")") + #endif + + let remoteIkEd = try Ed25519Crypto.loadPublic(ikData) + let ekRemote = try X25519Crypto.loadPublic(ekData) + + // Determine which SPK to use + let spkToUse: Curve25519.KeyAgreement.PrivateKey + if let override = spkOverride { + #if DEBUG + print("DEBUG processX3DHHeader: using spkOverride") + #endif + spkToUse = override + } else if let spkIdStr = spkIdStr { + // spk_id provided - match against known SPKs + #if DEBUG + print("DEBUG processX3DHHeader: spk_id provided, mySpkId=\(spkId), prevSpkId=\(prevSpkId)") + #endif + if spkIdStr == spkId, let spk = spkPrivate { + spkToUse = spk + } else if spkIdStr == prevSpkId, let prevSpk = prevSpkPrivate { + spkToUse = prevSpk + } else { + throw CryptoError.x3dhFailed("SPK \(spkIdStr) not found") + } + } else { + // No spk_id provided - use current SPK (for backward compatibility) + #if DEBUG + print("DEBUG processX3DHHeader: no spk_id, using current SPK") + #endif + guard let spk = spkPrivate else { + throw CryptoError.x3dhFailed("No SPK available") + } + spkToUse = spk + } + + // OPK + var opkPriv: Curve25519.KeyAgreement.PrivateKey? + if let opkIdStr = x3dhHeader["opk_id"] as? String { + #if DEBUG + print("DEBUG processX3DHHeader: looking for OPK \(opkIdStr)") + print("DEBUG processX3DHHeader: opkPrivates has \(opkPrivates.count) keys: \(Array(opkPrivates.keys).prefix(5))...") + #endif + + // Check file system + if let dir = try? KeyStorage.getKeyDir(email: email) { + let opkDir = dir.appendingPathComponent("opk_private") + let opkFile = opkDir.appendingPathComponent("\(opkIdStr).bin") + #if DEBUG + print("DEBUG processX3DHHeader: checking file \(opkFile.path)") + print("DEBUG processX3DHHeader: file exists = \(FileManager.default.fileExists(atPath: opkFile.path))") + #endif + if let files = try? FileManager.default.contentsOfDirectory(atPath: opkDir.path) { + #if DEBUG + print("DEBUG processX3DHHeader: opk_private dir has \(files.count) files: \(files.prefix(5))...") + #endif + } + } + + opkPriv = opkPrivates[opkIdStr] ?? KeyStorage.loadOPKPrivate(email: email, opkId: opkIdStr) + if opkPriv != nil { + #if DEBUG + print("DEBUG processX3DHHeader: OPK found") + #endif + opkPrivates.removeValue(forKey: opkIdStr) + KeyStorage.deleteOPKPrivate(email: email, opkId: opkIdStr) + } else { + #if DEBUG + print("DEBUG processX3DHHeader: OPK NOT found - continuing without OPK") + #endif + } + } + + #if DEBUG + print("DEBUG processX3DHHeader: performing X3DH respond") + #endif + let sharedSecret = try X3DH.respond( + ikPrivateEd: identityPrivate!, + spkPrivate: spkToUse, + ikRemoteEd: remoteIkEd, + ekRemote: ekRemote, + opkPrivate: opkPriv + ) + #if DEBUG + print("DEBUG processX3DHHeader: X3DH respond success, sharedSecret=\(sharedSecret.count) bytes") + #endif + + let ratchet = DoubleRatchet.initBob( + sharedSecret: sharedSecret, + spkPair: (spkToUse, spkToUse.publicKey) + ) + + let sessionKey = "\(senderId):\(senderDeviceId)" + sessions[sessionKey] = ratchet + try? KeyStorage.saveSession(email: email, peerUserId: senderId, ratchet: ratchet, localKey: localKey, peerDeviceId: senderDeviceId) + #if DEBUG + print("DEBUG processX3DHHeader: session created for \(sessionKey)") + #endif + + return ratchet + } + + // MARK: - Send Message + + func sendMessage(convId: String, text: String, members: [ConversationMember], replyTo: String? = nil, + extraPayload: [String: Any]? = nil, imageFileId: String? = nil) async -> (success: Bool, message: String, sentMessage: Message?) { + let isGroup = members.count > 2 + + if isGroup { + return await sendGroupMessage(convId: convId, text: text, members: members, replyTo: replyTo, extraPayload: extraPayload, imageFileId: imageFileId) + } else { + return await sendDM(convId: convId, text: text, members: members, replyTo: replyTo, extraPayload: extraPayload, imageFileId: imageFileId) + } + } + + // MARK: - Send DM + + private func sendDM(convId: String, text: String, members: [ConversationMember], replyTo: String? = nil, + extraPayload: [String: Any]? = nil, imageFileId: String? = nil) async -> (success: Bool, message: String, sentMessage: Message?) { + #if DEBUG + print("DEBUG sendDM: convId=\(convId), members=\(members.map { $0.userId })") + #endif + guard let identityPrivate = identityPrivate else { + #if DEBUG + print("DEBUG sendDM: Identity key not loaded") + #endif + return (false, "Identity key not loaded", nil) + } + + // Build JSON payload matching Python format for cross-client compatibility + var payload: [String: Any] = [ + "sender": username, + "text": text, + "reply_to": (replyTo as Any?) ?? NSNull(), + "timestamp": ISO8601DateFormatter().string(from: Date()), + ] + // Merge extra fields (image, file) at top level + if let extra = extraPayload { + for (key, value) in extra { payload[key] = value } + } + let rawPlaintext: Data + if let jsonData = try? JSONSerialization.data(withJSONObject: payload, options: [.sortedKeys]) { + rawPlaintext = jsonData + } else { + rawPlaintext = Data(text.utf8) + } + let plaintext = MessagePadding.pad(rawPlaintext) + + var recipients: [[String: Any]] = [] + + // Ensure we have the other member(s) — refetch if members only contains self + var actualMembers = members + let otherMembers = members.filter { $0.userId != userId } + if otherMembers.isEmpty { + #if DEBUG + print("DEBUG sendDM: WARNING — no other members in conversation, refetching from server") + #endif + let allConvs = await listConversations() + if let conv = allConvs.first(where: { $0.id == convId }) { + actualMembers = conv.members + #if DEBUG + print("DEBUG sendDM: refetched \(actualMembers.count) members for conv \(convId)") + #endif + } + } + + // Encrypt for each member's devices + #if DEBUG + print("DEBUG sendDM: encrypting for members, my userId=\(userId ?? "nil")") + #endif + for member in actualMembers where member.userId != userId { + #if DEBUG + print("DEBUG sendDM: processing member \(member.userId)") + #endif + do { + let bundles = try await getDeviceBundles(userId: member.userId) + #if DEBUG + print("DEBUG sendDM: got \(bundles.count) device bundles for \(member.userId)") + #endif + for (index, bundle) in bundles.enumerated() { + #if DEBUG + print("DEBUG sendDM: encrypting for bundle \(index + 1)/\(bundles.count), deviceId=\(bundle.deviceId)") + #endif + let ratchet = try await getOrCreateSession( + peerUserId: member.userId, + peerDeviceId: bundle.deviceId, + bundle: bundle + ) + + // Consume X3DH header if present (first message only) + let x3dhHeader = ratchet.x3dhHeader + ratchet.x3dhHeader = nil + + let encrypted = try ratchet.encrypt(plaintext) + try? KeyStorage.saveSession(email: email, peerUserId: member.userId, ratchet: ratchet, localKey: localKey, peerDeviceId: bundle.deviceId) + + var recipientEntry: [String: Any] = [ + "user_id": member.userId, + "device_id": bundle.deviceId, + "encrypted_content": ProtocolHandler.encodeBinary(encrypted.ciphertext), + "nonce": ProtocolHandler.encodeBinary(encrypted.nonce), + "ratchet_header": encrypted.header, + ] + if let x3dh = x3dhHeader { + recipientEntry["x3dh_header"] = x3dh + #if DEBUG + print("DEBUG sendDM: x3dh_header for \(bundle.deviceId) = \(x3dh)") + #endif + } + #if DEBUG + print("DEBUG sendDM: ratchet_header for \(bundle.deviceId) = \(encrypted.header)") + #endif + recipients.append(recipientEntry) + } + } catch { + #if DEBUG + print("DEBUG sendDM: ERROR encrypting for member \(member.userId): \(error)") + #endif + return (false, "Encryption failed for \(member.username): \(error.localizedDescription)", nil) + } + } + + // Self-encrypted copy (readable by all own devices via shared identity key) + let selfKey = CryptoUtils.deriveSelfEncryptionKey(identityPrivateRaw: identityPrivate.rawData) + if let (_, nonce, ct, tag) = try? CryptoUtils.aesEncrypt(plaintext, key: selfKey) { + let selfCiphertext = ct + tag + recipients.append([ + "user_id": userId!, + "device_id": Constants.selfDeviceId, + "encrypted_content": ProtocolHandler.encodeBinary(selfCiphertext), + "nonce": ProtocolHandler.encodeBinary(nonce), + "ratchet_header": ["self": true], + ]) + } + + // Build ratchet header for message table (use first recipient's or dummy) + let ratchetHeader: [String: Any] + if let first = recipients.first { + ratchetHeader = first["ratchet_header"] as? [String: Any] ?? [:] + } else { + ratchetHeader = ["dh_pub": String(repeating: "00", count: 32), "n": 0, "pn": 0] + } + + var params: [String: Any] = [ + "conversation_id": convId, + "ratchet_header": ratchetHeader, + "recipients": recipients, + ] + if let replyTo = replyTo { + params["reply_to"] = replyTo + } + if let imageFileId = imageFileId { + params["image_file_id"] = imageFileId + } + + #if DEBUG + print("DEBUG sendDM: sending with \(recipients.count) recipients") + #endif + let resp = await sendAndReceive(type: "send_message", params: params) + #if DEBUG + print("DEBUG sendDM: response status=\(resp.string(for: "status") ?? "nil")") + #endif + guard resp.string(for: "status") == "ok" else { + let msg = resp.dict(for: "data")?.string(for: "message") ?? "Send failed" + return (false, msg, nil) + } + + // Build sent Message and cache the plaintext + var sentMessage: Message? + if let msgData = resp.dict(for: "data"), let messageId = msgData.string(for: "message_id") { + // Cache plaintext for future getMessages calls + MessageCache.cacheDecryptedMessage( + email: email, convId: convId, messageId: messageId, + plaintext: plaintext, cacheKey: cacheKey + ) + + var forwardedFrom: ForwardedFrom? + if let fwd = extraPayload?["forwarded_from"] as? [String: Any], + let fwdSender = fwd["sender"] as? String { + forwardedFrom = ForwardedFrom(sender: fwdSender, + conversationId: fwd["conversation_id"] as? String ?? "", + messageId: fwd["message_id"] as? String ?? "") + } + + // Parse image/file info from extraPayload so the sent message displays correctly + var sentImage: ImageInfo? + if let imgDict = extraPayload?["image"] as? [String: Any], + let imgFileId = imgDict["file_id"] as? String, !imgFileId.isEmpty { + sentImage = ImageInfo( + fileId: imgFileId, + aesKey: imgDict["aes_key"] as? String ?? "", + iv: imgDict["iv"] as? String ?? "", + thumbnail: imgDict["thumbnail"] as? String, + filename: imgDict["filename"] as? String ?? "image.jpg", + size: imgDict["size"] as? Int ?? 0 + ) + } + var sentFile: FileInfo? + if let fileDict = extraPayload?["file"] as? [String: Any], + let fFileId = fileDict["file_id"] as? String, !fFileId.isEmpty { + sentFile = FileInfo( + fileId: fFileId, + aesKey: fileDict["aes_key"] as? String ?? "", + iv: fileDict["iv"] as? String ?? "", + filename: fileDict["filename"] as? String ?? "", + size: fileDict["size"] as? Int ?? 0, + mimeType: fileDict["mime_type"] as? String ?? "" + ) + } + + let createdAt = msgData.string(for: "created_at").flatMap { DateParsing.parse($0) } ?? Date() + sentMessage = Message( + id: messageId, conversationId: convId, senderId: userId!, + senderUsername: username, createdAt: createdAt, + text: text.isEmpty ? nil : text, replyTo: replyTo, + imageFileId: imageFileId, file: sentFile, image: sentImage, + isDeleted: false, readBy: [], + reactions: [], forwardedFrom: forwardedFrom, pinnedAt: nil, pinnedBy: nil + ) + } + + return (true, "Message sent", sentMessage) + } + + // MARK: - Send Group Message + + private func sendGroupMessage(convId: String, text: String, members: [ConversationMember], replyTo: String? = nil, + extraPayload: [String: Any]? = nil, imageFileId: String? = nil) async -> (success: Bool, message: String, sentMessage: Message?) { + guard let identityPrivate = identityPrivate, let userId = userId, deviceId != nil else { + return (false, "Not properly logged in", nil) + } + + // Ensure we have all members — refetch if only self is present + var actualMembers = members + let otherMembers = members.filter { $0.userId != userId } + if otherMembers.isEmpty { + #if DEBUG + print("DEBUG sendGroupMessage: WARNING — no other members, refetching from server") + #endif + let allConvs = await listConversations() + if let conv = allConvs.first(where: { $0.id == convId }) { + actualMembers = conv.members + } + } + + // Get or create sender key for this group + var senderKeyState = senderKeyStates[convId] + if senderKeyState == nil { + senderKeyState = KeyStorage.loadSenderKeyState(email: email, convId: convId, localKey: localKey) + } + + var needDistribute = false + if senderKeyState == nil { + senderKeyState = SenderKeyState() + needDistribute = true + } + + senderKeyStates[convId] = senderKeyState + + // Distribute sender key if new + if needDistribute { + await distributeSenderKey(convId: convId, members: actualMembers) + } + + // Encrypt with sender key — JSON payload for cross-client compatibility + var groupPayload: [String: Any] = [ + "sender": username, + "text": text, + "reply_to": (replyTo as Any?) ?? NSNull(), + "timestamp": ISO8601DateFormatter().string(from: Date()), + ] + if let extra = extraPayload { + for (key, value) in extra { groupPayload[key] = value } + } + let rawPlaintext: Data + if let jsonData = try? JSONSerialization.data(withJSONObject: groupPayload, options: [.sortedKeys]) { + rawPlaintext = jsonData + } else { + rawPlaintext = Data(text.utf8) + } + let plaintext = MessagePadding.pad(rawPlaintext) + do { + let encrypted = try senderKeyState!.encrypt(plaintext) + try? KeyStorage.saveSenderKeyState(email: email, convId: convId, state: senderKeyState!, localKey: localKey) + + // Build recipients (same ciphertext for all) + var recipients: [[String: Any]] = [] + for member in actualMembers where member.userId != userId { + recipients.append([ + "user_id": member.userId, + "device_id": Constants.selfDeviceId, // group messages use sentinel + "encrypted_content": ProtocolHandler.encodeBinary(encrypted.ciphertext), + "nonce": ProtocolHandler.encodeBinary(encrypted.nonce), + ]) + } + + // Self copy (readable by all own devices via shared identity key) + let selfKey = CryptoUtils.deriveSelfEncryptionKey(identityPrivateRaw: identityPrivate.rawData) + if let (_, nonce, ct, tag) = try? CryptoUtils.aesEncrypt(plaintext, key: selfKey) { + recipients.append([ + "user_id": userId, + "device_id": Constants.selfDeviceId, + "encrypted_content": ProtocolHandler.encodeBinary(ct + tag), + "nonce": ProtocolHandler.encodeBinary(nonce), + "ratchet_header": ["self": true], + ]) + } + + let dummyHeader: [String: Any] = [ + "dh_pub": String(repeating: "00", count: 32), + "n": 0, + "pn": 0, + ] + + var params: [String: Any] = [ + "conversation_id": convId, + "ratchet_header": dummyHeader, + "recipients": recipients, + ] + + // Include sender key metadata for group routing (base64-encoded chain ID bytes) + if let chainIdBytes = Data(hexString: encrypted.chainIdHex) { + params["sender_chain_id"] = ProtocolHandler.encodeBinary(chainIdBytes) + } + params["sender_chain_n"] = encrypted.n + + if let replyTo = replyTo { + params["reply_to"] = replyTo + } + if let imageFileId = imageFileId { + params["image_file_id"] = imageFileId + } + + let resp = await sendAndReceive(type: "send_message", params: params) + guard resp.string(for: "status") == "ok" else { + let msg = resp.dict(for: "data")?.string(for: "message") ?? "Send failed" + return (false, msg, nil) + } + + // Build sent Message and cache the plaintext + var sentMessage: Message? + if let msgData = resp.dict(for: "data"), let messageId = msgData.string(for: "message_id") { + MessageCache.cacheDecryptedMessage( + email: email, convId: convId, messageId: messageId, + plaintext: plaintext, cacheKey: cacheKey + ) + + var forwardedFrom: ForwardedFrom? + if let fwd = extraPayload?["forwarded_from"] as? [String: Any], + let fwdSender = fwd["sender"] as? String { + forwardedFrom = ForwardedFrom(sender: fwdSender, + conversationId: fwd["conversation_id"] as? String ?? "", + messageId: fwd["message_id"] as? String ?? "") + } + + // Parse image/file info from extraPayload so the sent message displays correctly + var sentImage: ImageInfo? + if let imgDict = extraPayload?["image"] as? [String: Any], + let imgFileId = imgDict["file_id"] as? String, !imgFileId.isEmpty { + sentImage = ImageInfo( + fileId: imgFileId, + aesKey: imgDict["aes_key"] as? String ?? "", + iv: imgDict["iv"] as? String ?? "", + thumbnail: imgDict["thumbnail"] as? String, + filename: imgDict["filename"] as? String ?? "image.jpg", + size: imgDict["size"] as? Int ?? 0 + ) + } + var sentFile: FileInfo? + if let fileDict = extraPayload?["file"] as? [String: Any], + let fFileId = fileDict["file_id"] as? String, !fFileId.isEmpty { + sentFile = FileInfo( + fileId: fFileId, + aesKey: fileDict["aes_key"] as? String ?? "", + iv: fileDict["iv"] as? String ?? "", + filename: fileDict["filename"] as? String ?? "", + size: fileDict["size"] as? Int ?? 0, + mimeType: fileDict["mime_type"] as? String ?? "" + ) + } + + let createdAt = msgData.string(for: "created_at").flatMap { DateParsing.parse($0) } ?? Date() + sentMessage = Message( + id: messageId, conversationId: convId, senderId: userId, + senderUsername: username, createdAt: createdAt, + text: text.isEmpty ? nil : text, replyTo: replyTo, + imageFileId: imageFileId, file: sentFile, image: sentImage, + isDeleted: false, readBy: [], + reactions: [], forwardedFrom: forwardedFrom, pinnedAt: nil, pinnedBy: nil + ) + } + + return (true, "Message sent", sentMessage) + } catch { + return (false, "Encryption failed: \(error.localizedDescription)", nil) + } + } + + // MARK: - Distribute Sender Key + + private func distributeSenderKey(convId: String, members: [ConversationMember]) async { + guard let senderKeyState = senderKeyStates[convId], + let userId = userId, + let deviceId = deviceId else { return } + + let exportedKey = senderKeyState.exportKey() + + for member in members where member.userId != userId { + do { + let bundles = try await getDeviceBundles(userId: member.userId) + for bundle in bundles { + let ratchet = try await getOrCreateSession( + peerUserId: member.userId, + peerDeviceId: bundle.deviceId, + bundle: bundle + ) + + let x3dhHeader = ratchet.x3dhHeader + ratchet.x3dhHeader = nil + + // Payload includes sender key + metadata + let controlPayload: [String: Any] = [ + "_sender_key": [ + "conv_id": convId, + "key": ProtocolHandler.encodeBinary(exportedKey), + "sender_device_id": deviceId, + ] + ] + let controlData = try JSONSerialization.data(withJSONObject: controlPayload) + let encrypted = try ratchet.encrypt(controlData) + try? KeyStorage.saveSession(email: email, peerUserId: member.userId, ratchet: ratchet, localKey: localKey, peerDeviceId: bundle.deviceId) + + var recipientEntry: [String: Any] = [ + "user_id": member.userId, + "device_id": bundle.deviceId, + "encrypted_content": ProtocolHandler.encodeBinary(encrypted.ciphertext), + "nonce": ProtocolHandler.encodeBinary(encrypted.nonce), + "ratchet_header": encrypted.header, + ] + if let x3dh = x3dhHeader { + recipientEntry["x3dh_header"] = x3dh + } + + let dummyHeader: [String: Any] = [ + "dh_pub": String(repeating: "00", count: 32), + "n": 0, + "pn": 0, + ] + + _ = await sendAndReceive(type: "send_message", params: [ + "conversation_id": convId, + "ratchet_header": dummyHeader, + "recipients": [recipientEntry], + ]) + } + } catch { + #if DEBUG + print("Failed to distribute sender key to \(member.userId): \(error)") + #endif + } + } + } + + // MARK: - Decrypt + + /// Decrypt DM using Double Ratchet, or static key for self-copies. + /// Matches Python: _decrypt_dm() lines 1547-1652 + func decryptDMRecipientData( + senderData: [String: Any], + senderId: String, + senderDeviceId: String + ) -> Data? { + // Server uses "encrypted_content", fallback to "ciphertext" for compatibility + let ctB64 = senderData["encrypted_content"] as? String ?? senderData["ciphertext"] as? String + guard let ctB64 = ctB64, + let nonceB64 = senderData["nonce"] as? String, + let ciphertext = try? ProtocolHandler.decodeBinary(ctB64), + let nonce = try? ProtocolHandler.decodeBinary(nonceB64) else { + #if DEBUG + print("DEBUG decryptDM: missing encrypted_content or nonce") + #endif + return nil + } + #if DEBUG + print("DEBUG decryptDM: ct=\(ciphertext.count)B nonce=\(nonce.count)B sender=\(senderId) senderDev=\(senderDeviceId)") + #endif + + let ratchetHeader = senderData["ratchet_header"] as? [String: Any] ?? [:] + + // Self-encrypted message (ratchet_header.self == true) + // Matches Python lines 1562-1566 + let isSelf = (ratchetHeader["self"] as? Bool) == true || (ratchetHeader["self"] as? Int) == 1 + if isSelf { + #if DEBUG + print("DEBUG decryptDM: self-encrypted copy") + #endif + guard let cacheKey = cacheKey, ciphertext.count >= 16 else { + #if DEBUG + print("DEBUG decryptDM: no cacheKey or ct too short") + #endif + return nil + } + let ct = ciphertext.prefix(ciphertext.count - 16) + let tag = ciphertext.suffix(16) + let result = try? CryptoUtils.aesDecrypt(key: cacheKey, nonce: nonce, ciphertext: Data(ct), tag: Data(tag)) + #if DEBUG + print("DEBUG decryptDM: self-decrypt \(result != nil ? "OK" : "FAILED")") + #endif + return result + } + + // Regular DM — Double Ratchet decryption + // Matches Python lines 1568-1631 + let x3dhHeader = senderData["x3dh_header"] as? [String: Any] + let sessionKey = "\(senderId):\(senderDeviceId)" + + // Try to load existing session (memory → disk) + let ratchet = sessions[sessionKey] + ?? KeyStorage.loadSession(email: email, peerUserId: senderId, localKey: localKey, peerDeviceId: senderDeviceId) + if ratchet != nil { + sessions[sessionKey] = ratchet + } + #if DEBUG + print("DEBUG decryptDM: sessionKey=\(sessionKey) existingSession=\(ratchet != nil) hasX3DH=\(x3dhHeader != nil)") + #endif + + var plaintext: Data? + + if ratchet != nil && x3dhHeader == nil { + // Normal case: existing session, no X3DH header (Python line 1581-1585) + do { + plaintext = try ratchet!.decrypt(headerDict: ratchetHeader, ciphertext: ciphertext, nonce: nonce) + try? KeyStorage.saveSession(email: email, peerUserId: senderId, ratchet: ratchet!, localKey: localKey, peerDeviceId: senderDeviceId) + #if DEBUG + print("DEBUG decryptDM: normal decrypt OK") + #endif + } catch { + #if DEBUG + print("DEBUG decryptDM: normal decrypt FAILED: \(error)") + #endif + return nil + } + } else if let x3dh = x3dhHeader, let existingRatchet = ratchet { + // Existing session + X3DH: sender may have reset. Try existing first, fallback to new X3DH + // Matches Python lines 1587-1613 + #if DEBUG + print("DEBUG decryptDM: existing session + X3DH header, trying existing first") + #endif + let backup = try? existingRatchet.exportState() + do { + plaintext = try existingRatchet.decrypt(headerDict: ratchetHeader, ciphertext: ciphertext, nonce: nonce) + try? KeyStorage.saveSession(email: email, peerUserId: senderId, ratchet: existingRatchet, localKey: localKey, peerDeviceId: senderDeviceId) + #if DEBUG + print("DEBUG decryptDM: existing session decrypt OK (X3DH ignored)") + #endif + } catch { + // Restore from backup and try X3DH + #if DEBUG + print("DEBUG decryptDM: existing session failed, trying X3DH") + #endif + if let backup = backup, let restored = try? DoubleRatchet.importState(backup) { + sessions[sessionKey] = restored + try? KeyStorage.saveSession(email: email, peerUserId: senderId, ratchet: restored, localKey: localKey, peerDeviceId: senderDeviceId) + } + plaintext = tryX3DHDecrypt(x3dh: x3dh, ratchetHeader: ratchetHeader, ciphertext: ciphertext, nonce: nonce, senderId: senderId, senderDeviceId: senderDeviceId) + } + } else if let x3dh = x3dhHeader { + // No existing session, process X3DH + // Matches Python lines 1614-1629 + #if DEBUG + print("DEBUG decryptDM: no existing session, processing X3DH") + #endif + plaintext = tryX3DHDecrypt(x3dh: x3dh, ratchetHeader: ratchetHeader, ciphertext: ciphertext, nonce: nonce, senderId: senderId, senderDeviceId: senderDeviceId) + } else { + // No session and no X3DH + #if DEBUG + print("DEBUG decryptDM: no session and no X3DH for \(senderId)") + #endif + return nil + } + + guard let rawDecrypted = plaintext else { return nil } + let unpadded = MessagePadding.unpad(rawDecrypted) + + sessions[sessionKey] = ratchet ?? sessions[sessionKey] + #if DEBUG + print("DEBUG decryptDM: decrypt OK, \(unpadded.count) bytes") + #endif + + // Check for sender key distribution (control message) + if let jsonObj = try? JSONSerialization.jsonObject(with: unpadded) as? [String: Any], + let senderKeyInfo = jsonObj["_sender_key"] as? [String: Any] { + handleSenderKeyDistribution(senderKeyInfo, senderId: senderId) + return nil // Control message + } + + return unpadded + } + + /// Helper: try X3DH header processing + decrypt, with prev SPK fallback. + /// Matches Python's try/except pattern in _decrypt_dm lines 1615-1629. + private func tryX3DHDecrypt( + x3dh: [String: Any], ratchetHeader: [String: Any], + ciphertext: Data, nonce: Data, + senderId: String, senderDeviceId: String + ) -> Data? { + let sessionKey = "\(senderId):\(senderDeviceId)" + do { + let ratchet = try processX3DHHeader( + senderId: senderId, x3dhHeader: x3dh, senderDeviceId: senderDeviceId) + let plaintext = try ratchet.decrypt(headerDict: ratchetHeader, ciphertext: ciphertext, nonce: nonce) + sessions[sessionKey] = ratchet + try? KeyStorage.saveSession(email: email, peerUserId: senderId, ratchet: ratchet, localKey: localKey, peerDeviceId: senderDeviceId) + #if DEBUG + print("DEBUG tryX3DHDecrypt: OK with current SPK") + #endif + return plaintext + } catch { + #if DEBUG + print("DEBUG tryX3DHDecrypt: failed with current SPK: \(error)") + #endif + // Try with previous SPK (grace period, M4) + if let prevSpk = prevSpkPrivate { + do { + let ratchet = try processX3DHHeader( + senderId: senderId, x3dhHeader: x3dh, senderDeviceId: senderDeviceId, + spkOverride: prevSpk) + let plaintext = try ratchet.decrypt(headerDict: ratchetHeader, ciphertext: ciphertext, nonce: nonce) + sessions[sessionKey] = ratchet + try? KeyStorage.saveSession(email: email, peerUserId: senderId, ratchet: ratchet, localKey: localKey, peerDeviceId: senderDeviceId) + #if DEBUG + print("DEBUG tryX3DHDecrypt: OK with prev SPK") + #endif + return plaintext + } catch { + #if DEBUG + print("DEBUG tryX3DHDecrypt: failed with prev SPK too: \(error)") + #endif + } + } + return nil + } + } + + private func handleSenderKeyDistribution(_ info: [String: Any], senderId: String) { + guard let convId = info["conv_id"] as? String, + let keyB64 = info["key"] as? String, + let keyData = try? ProtocolHandler.decodeBinary(keyB64) else { return } + + let senderDeviceId = info["sender_device_id"] as? String ?? Constants.selfDeviceId + + do { + let senderKey = try SenderKeyState.fromKey(keyData) + let stateKey = "\(convId):\(senderId):\(senderDeviceId)" + recvSenderKeys[stateKey] = senderKey + try? KeyStorage.saveRecvSenderKey( + email: email, + convId: convId, + senderId: senderId, + senderDeviceId: senderDeviceId, + state: senderKey, + localKey: localKey + ) + } catch { + #if DEBUG + print("Failed to import sender key: \(error)") + #endif + } + } + + // MARK: - Group Decrypt + + /// Decrypt a group message using the sender's distributed Sender Key. + /// Matches Python: _decrypt_group() in chat_core.py + func decryptGroupMessage( + msgDict: [String: Any], + senderId: String, + senderDeviceId: String, + convId: String + ) -> Data? { + guard let chainIdB64 = msgDict["sender_chain_id"] as? String, + let chainN = msgDict["sender_chain_n"] as? Int, + let chainIdData = try? ProtocolHandler.decodeBinary(chainIdB64) else { + #if DEBUG + print("DEBUG decryptGroupMessage: missing sender_chain_id or sender_chain_n") + #endif + return nil + } + let chainIdHex = chainIdData.hexString + + let ctB64 = msgDict["encrypted_content"] as? String ?? msgDict["ciphertext"] as? String + guard let ctB64 = ctB64, + let nonceB64 = msgDict["nonce"] as? String, + let ciphertext = try? ProtocolHandler.decodeBinary(ctB64), + let nonce = try? ProtocolHandler.decodeBinary(nonceB64) else { + #if DEBUG + print("DEBUG decryptGroupMessage: missing encrypted_content or nonce") + #endif + return nil + } + + // Look up received sender key — try with sender_device_id first + var sk: SenderKeyState? + let keyWithDevice = "\(convId):\(senderId):\(senderDeviceId)" + sk = recvSenderKeys[keyWithDevice] + if sk == nil { + sk = KeyStorage.loadRecvSenderKey( + email: email, convId: convId, senderId: senderId, + senderDeviceId: senderDeviceId, localKey: localKey + ) + if let loaded = sk { + recvSenderKeys[keyWithDevice] = loaded + } + } + + // Fallback: try with self device ID (legacy/default) + if sk == nil && senderDeviceId != Constants.selfDeviceId { + let keyLegacy = "\(convId):\(senderId):\(Constants.selfDeviceId)" + sk = recvSenderKeys[keyLegacy] + if sk == nil { + sk = KeyStorage.loadRecvSenderKey( + email: email, convId: convId, senderId: senderId, + senderDeviceId: Constants.selfDeviceId, localKey: localKey + ) + if let loaded = sk { + recvSenderKeys[keyLegacy] = loaded + } + } + } + + guard let senderKey = sk else { + #if DEBUG + print("DEBUG decryptGroupMessage: no sender key for \(senderId) in conv \(convId)") + #endif + return nil + } + + do { + let rawPlaintext = try senderKey.decrypt(chainIdHex: chainIdHex, n: chainN, ciphertext: ciphertext, nonce: nonce) + let plaintext = MessagePadding.unpad(rawPlaintext) + // Save updated state (chain has advanced) + try? KeyStorage.saveRecvSenderKey( + email: email, convId: convId, senderId: senderId, + senderDeviceId: senderDeviceId, state: senderKey, localKey: localKey + ) + #if DEBUG + print("DEBUG decryptGroupMessage: success, \(plaintext.count) bytes") + #endif + return plaintext + } catch { + #if DEBUG + print("DEBUG decryptGroupMessage: decrypt failed: \(error)") + #endif + return nil + } + } + + /// Decrypt a new_message notification. Matches Python: decrypt_notification(). + /// Supports multi-device format (device_entries array) and legacy flat format. + func decryptNotification(_ data: [String: Any]) -> Message? { + guard let senderId = data.string(for: "sender_id"), + let conversationId = data.string(for: "conversation_id"), + let messageId = data.string(for: "message_id") else { + #if DEBUG + print("DEBUG decryptNotification: missing sender_id/conversation_id/message_id") + #endif + return nil + } + + let senderDeviceId = data.string(for: "sender_device_id") ?? Constants.selfDeviceId + let myUserId = userId ?? "" + #if DEBUG + print("DEBUG decryptNotification: msgId=\(messageId) senderId=\(senderId) senderDeviceId=\(senderDeviceId) convId=\(conversationId)") + #endif + + // --- Step 1: Extract per-device encrypted content --- + // Matches Python decrypt_notification lines 1862-1897 + var encryptedContent: String = "" + var nonce: String = "" + var ratchetHeader: [String: Any] = [:] + var x3dhHeader: [String: Any]? + + if let deviceEntries = data["device_entries"] as? [[String: Any]] { + // Multi-device format: pick entry matching our device_id or SELF_DEVICE_ID + let entryDeviceIds = deviceEntries.compactMap { $0["device_id"] as? String } + #if DEBUG + print("DEBUG decryptNotification: myDeviceId=\(deviceId ?? "nil") device_entries=\(entryDeviceIds)") + #endif + + var chosen: [String: Any]? + var selfEntry: [String: Any]? + for entry in deviceEntries { + let eid = entry["device_id"] as? String ?? "" + if eid == deviceId { + chosen = entry + break + } + if eid == Constants.selfDeviceId { + selfEntry = entry + } + } + + // If sender is us, prefer self-encrypted entry (matches Python lines 1878-1882) + if senderId == myUserId { + chosen = selfEntry ?? chosen + } else if chosen == nil { + chosen = selfEntry + } + + guard let chosen = chosen else { + #if DEBUG + print("DEBUG decryptNotification: NO matching device_entry for device \(deviceId ?? "nil")") + #endif + return nil + } + + encryptedContent = chosen["encrypted_content"] as? String ?? "" + nonce = chosen["nonce"] as? String ?? "" + ratchetHeader = (chosen["ratchet_header"] as? [String: Any]) ?? (data["ratchet_header"] as? [String: Any]) ?? [:] + x3dhHeader = (chosen["x3dh_header"] as? [String: Any]) ?? (data["x3dh_header"] as? [String: Any]) + } else { + // Legacy flat format (no device_entries) + encryptedContent = data["encrypted_content"] as? String ?? data["ciphertext"] as? String ?? "" + nonce = data["nonce"] as? String ?? "" + ratchetHeader = data["ratchet_header"] as? [String: Any] ?? [:] + x3dhHeader = data["x3dh_header"] as? [String: Any] + } + + guard !encryptedContent.isEmpty, !nonce.isEmpty else { + #if DEBUG + print("DEBUG decryptNotification: empty encrypted_content or nonce") + #endif + return nil + } + + // --- Step 2: Build msg_data and decrypt --- + // Matches Python decrypt_notification lines 1899-1910 + let msgData: [String: Any] = [ + "sender_id": senderId, + "sender_device_id": senderDeviceId, + "conversation_id": conversationId, + "ratchet_header": ratchetHeader, + "encrypted_content": encryptedContent, + "nonce": nonce, + "x3dh_header": x3dhHeader as Any, + "sender_chain_id": data["sender_chain_id"] as Any, + "sender_chain_n": data["sender_chain_n"] as Any, + ] + + // Dispatch: matches Python _decrypt_message() lines 1533-1545 + // Check self-encrypted FIRST (before group check) — after re-encryption, + // group messages have {"self": true} ratchet_header but still sender_chain_id. + var plaintext: Data? + let isSelfEncrypted = (ratchetHeader["self"] as? Bool) == true + || (ratchetHeader["self"] as? Int) == 1 + + if isSelfEncrypted { + // Self-encrypted copy → static self-key (same path as _decrypt_dm self check) + #if DEBUG + print("DEBUG decryptNotification: self-encrypted message") + #endif + if let cacheKey = cacheKey { + if let ct = try? ProtocolHandler.decodeBinary(encryptedContent), + let n = try? ProtocolHandler.decodeBinary(nonce), + ct.count >= 16 { + let ciphertext = ct.prefix(ct.count - 16) + let tag = ct.suffix(16) + plaintext = try? CryptoUtils.aesDecrypt(key: cacheKey, nonce: n, ciphertext: Data(ciphertext), tag: Data(tag)) + #if DEBUG + print("DEBUG decryptNotification: self-decrypt result=\(plaintext != nil ? "OK" : "FAILED")") + #endif + } + } + } else if let chainId = msgData["sender_chain_id"] as? String, !chainId.isEmpty, senderId != myUserId { + // Group message from someone else → sender key decryption + #if DEBUG + print("DEBUG decryptNotification: group message") + #endif + plaintext = decryptGroupMessage( + msgDict: msgData, senderId: senderId, + senderDeviceId: senderDeviceId, convId: conversationId + ) + } else { + // DM → ratchet decryption + #if DEBUG + print("DEBUG decryptNotification: DM message, calling decryptDMRecipientData") + #endif + plaintext = decryptDMRecipientData( + senderData: msgData, + senderId: senderId, + senderDeviceId: senderDeviceId + ) + } + + guard let plaintext = plaintext else { + #if DEBUG + print("DEBUG decryptNotification: decrypt returned nil for msgId=\(messageId)") + #endif + return nil + } + + // --- Step 3: Cache and parse --- + MessageCache.cacheDecryptedMessage( + email: email, convId: conversationId, messageId: messageId, + plaintext: plaintext, cacheKey: cacheKey + ) + + // Queue for self-encryption if from another user (so other devices can read it) + if senderId != myUserId && !messageId.isEmpty { + pendingSelfEncrypt.append((messageId: messageId, plaintext: plaintext)) + } + + var messageText: String? = String(data: plaintext, encoding: .utf8) + var replyTo: String? + var file: FileInfo? + var image: ImageInfo? + var forwardedFrom: ForwardedFrom? + var senderName = data.string(for: "sender_username") ?? userCache[senderId]?.username ?? "Unknown" + + if let jsonObj = try? JSONSerialization.jsonObject(with: plaintext) as? [String: Any] { + messageText = jsonObj["text"] as? String + replyTo = jsonObj["reply_to"] as? String + if senderName == "Unknown", let s = jsonObj["sender"] as? String { + senderName = s + } + if let fileDict = jsonObj["file"] as? [String: Any] { + file = FileInfo( + fileId: fileDict["file_id"] as? String ?? "", + aesKey: fileDict["aes_key"] as? String ?? "", + iv: fileDict["iv"] as? String ?? "", + filename: fileDict["filename"] as? String ?? "", + size: fileDict["size"] as? Int ?? 0, + mimeType: fileDict["mime_type"] as? String ?? "" + ) + } + image = parseImageInfo(from: jsonObj) + if let fwd = jsonObj["forwarded_from"] as? [String: Any], + let fwdSender = fwd["sender"] as? String { + forwardedFrom = ForwardedFrom(sender: fwdSender, + conversationId: fwd["conversation_id"] as? String ?? "", + messageId: fwd["message_id"] as? String ?? "") + } + + // Check for sender key distribution (control message) + if jsonObj["_sender_key"] != nil { + #if DEBUG + print("DEBUG decryptNotification: control message (sender key distribution)") + #endif + return nil + } + } + + if messageText == nil && file == nil && image == nil { + #if DEBUG + print("DEBUG decryptNotification: control message (no text/file/image)") + #endif + return nil + } + + let createdAt = data.string(for: "created_at").flatMap { DateParsing.parse($0) } ?? Date() + + return Message( + id: messageId, + conversationId: conversationId, + senderId: senderId, + senderUsername: senderName, + createdAt: createdAt, + text: messageText, + replyTo: replyTo, + imageFileId: data.string(for: "image_file_id"), + file: file, + image: image, + isDeleted: false, + readBy: [], + reactions: [], + forwardedFrom: forwardedFrom, + pinnedAt: nil, + pinnedBy: nil + ) + } + + // MARK: - Self-Encrypt Flush + + /// Encrypt received messages with self-encryption key and upload to server. + /// Allows other devices of the same user to read received messages. + /// Matches Python: _flush_self_encrypt() + func flushSelfEncrypt() async { + guard !pendingSelfEncrypt.isEmpty, let edPriv = identityPrivate else { return } + + let selfKey = CryptoUtils.deriveSelfEncryptionKey(identityPrivateRaw: edPriv.rawData) + var updates: [[String: Any]] = [] + + for item in pendingSelfEncrypt { + guard let (_, nonce, ct, tag) = try? CryptoUtils.aesEncrypt(item.plaintext, key: selfKey) else { + continue + } + let selfCiphertext = ct + tag + updates.append([ + "message_id": item.messageId, + "encrypted_content": ProtocolHandler.encodeBinary(selfCiphertext), + "nonce": ProtocolHandler.encodeBinary(nonce), + ]) + } + pendingSelfEncrypt.removeAll() + + // Send in batches of 500 (matches Python batch size) + let batchSize = 500 + for batchStart in stride(from: 0, to: updates.count, by: batchSize) { + let batchEnd = min(batchStart + batchSize, updates.count) + let batch = Array(updates[batchStart.. [Conversation] { + let resp = await sendAndReceive(type: "list_conversations") + guard resp.string(for: "status") == "ok", + let data = resp.dict(for: "data"), + let convList = data["conversations"] as? [[String: Any]] else { + return [] + } + + return convList.compactMap { dict -> Conversation? in + guard let id = dict.string(for: "conversation_id") else { return nil } + + let membersRaw = dict["members"] as? [[String: Any]] ?? [] + #if DEBUG + print("DEBUG listConversations: conv \(id) raw members count=\(membersRaw.count), raw=\(membersRaw)") + #endif + let members = membersRaw.compactMap { m -> ConversationMember? in + guard let uid = m.string(for: "user_id"), + let uname = m.string(for: "username"), + let uemail = m.string(for: "email") else { + #if DEBUG + print("DEBUG listConversations: FAILED to parse member: \(m)") + #endif + return nil + } + return ConversationMember(userId: uid, username: uname, email: uemail) + } + #if DEBUG + print("DEBUG listConversations: conv \(id) parsed members count=\(members.count)") + #endif + + let unreadCount = dict.int(for: "unread_count") ?? 0 + + return Conversation( + id: id, + name: dict.string(for: "name"), + members: members, + createdBy: dict.string(for: "created_by"), + avatarFile: dict.string(for: "avatar_file"), + unreadCount: unreadCount, + isFavorite: false, + lastMessageTime: nil + ) + } + } + + func createConversation(emails: [String], name: String? = nil) async -> (convId: String?, message: String) { + var params: [String: Any] = ["members": emails] + if let name = name { + params["name"] = name + } + + let resp = await sendAndReceive(type: "create_conversation", params: params) + guard resp.string(for: "status") == "ok", + let data = resp.dict(for: "data"), + let convId = data.string(for: "conversation_id") else { + let msg = resp.dict(for: "data")?.string(for: "message") ?? "Failed to create conversation" + return (nil, msg) + } + + return (convId, "Conversation created") + } + + func findConversation(email: String) async -> String? { + let resp = await sendAndReceive(type: "find_conversation", params: ["email": email]) + guard resp.string(for: "status") == "ok", + let data = resp.dict(for: "data") else { return nil } + return data.string(for: "conversation_id") + } + + // MARK: - Payload Parsing Helpers + + private func parseImageInfo(from jsonObj: [String: Any]) -> ImageInfo? { + guard let imgDict = jsonObj["image"] as? [String: Any], + let fileId = imgDict["file_id"] as? String, !fileId.isEmpty else { return nil } + return ImageInfo( + fileId: fileId, + aesKey: imgDict["aes_key"] as? String ?? "", + iv: imgDict["iv"] as? String ?? "", + thumbnail: imgDict["thumbnail"] as? String, + filename: imgDict["filename"] as? String ?? "image.jpg", + size: imgDict["size"] as? Int ?? 0 + ) + } + + // MARK: - Messages + + func getMessages(convId: String, limit: Int = 50, offset: Int = 0, afterTs: String? = nil) async -> [Message] { + var params: [String: Any] = [ + "conversation_id": convId, + "limit": limit, + "offset": offset, + ] + if let afterTs = afterTs { + params["after_ts"] = afterTs + } + let resp = await sendAndReceive(type: "get_messages", params: params) + guard resp.string(for: "status") == "ok", + let data = resp.dict(for: "data"), + let messagesRaw = data["messages"] as? [[String: Any]] else { + return [] + } + + // Server returns DESC (newest first), reverse to ASC (oldest first) — matches Python client + let sortedMessages = Array(messagesRaw.reversed()) + + // Deduplicate: server can return both device-specific and SELF_DEVICE_ID rows + // for the same message. Prefer self-encrypted rows (new devices after pairing + // can't decrypt old device-specific ratchet data, but CAN decrypt self-encrypted). + var dedupedMessages: [[String: Any]] = [] + var msgIndex: [String: Int] = [:] + for msgDict in sortedMessages { + guard let msgId = msgDict.string(for: "message_id") else { continue } + let rh = msgDict["ratchet_header"] as? [String: Any] + let isSelf = (rh?["self"] as? Bool) == true || (rh?["self"] as? Int) == 1 + if let idx = msgIndex[msgId] { + if isSelf { dedupedMessages[idx] = msgDict } + } else { + msgIndex[msgId] = dedupedMessages.count + dedupedMessages.append(msgDict) + } + } + + var messages: [Message] = [] + #if DEBUG + print("DEBUG getMessages: processing \(dedupedMessages.count) messages (from \(sortedMessages.count) rows)") + #endif + for msgDict in dedupedMessages { + guard let msgId = msgDict.string(for: "message_id"), + let senderId = msgDict.string(for: "sender_id") else { + #if DEBUG + print("DEBUG getMessages: skipping message without message_id or sender_id") + #endif + continue + } + + let senderDeviceId = msgDict.string(for: "sender_device_id") ?? Constants.selfDeviceId + let isDeleted = msgDict["deleted_at"] != nil && !(msgDict["deleted_at"] is NSNull) + #if DEBUG + print("DEBUG getMessages: msgId=\(msgId), senderId=\(senderId), senderDeviceId=\(senderDeviceId), myUserId=\(userId ?? "nil")") + print("DEBUG getMessages: msgDict keys=\(Array(msgDict.keys))") + #endif + + // Parse read_by from server response (matches Python: m.get("read_by", [])) + let readBy = Set(msgDict["read_by"] as? [String] ?? []) + + // Parse reactions from server response + var reactions: [MessageReaction] = [] + if let reactionsArr = msgDict["reactions"] as? [[String: Any]] { + reactions = reactionsArr.compactMap { r in + guard let rUserId = r["user_id"] as? String, + let reaction = r["reaction"] as? String else { return nil } + let rCreatedAt = (r["created_at"] as? String).flatMap { DateParsing.parse($0) } ?? Date() + return MessageReaction(userId: rUserId, reaction: reaction, createdAt: rCreatedAt) + } + } + + // Parse pin info from server response + let pinnedAt = (msgDict["pinned_at"] as? String).flatMap { DateParsing.parse($0) } + let pinnedBy = msgDict["pinned_by"] as? String + + if isDeleted { + let createdAt = msgDict.string(for: "created_at").flatMap { DateParsing.parse($0) } ?? Date() + messages.append(Message( + id: msgId, conversationId: convId, senderId: senderId, + senderUsername: "", + createdAt: createdAt, text: nil, isDeleted: true, readBy: readBy, + reactions: [], forwardedFrom: nil, pinnedAt: pinnedAt, pinnedBy: pinnedBy + )) + continue + } + + // Check per-message cache first (ratchet keys are one-time — cannot re-decrypt) + if let cachedPlaintext = MessageCache.getCachedMessage( + email: email, convId: convId, messageId: msgId, cacheKey: cacheKey + ) { + var messageText: String? = String(data: cachedPlaintext, encoding: .utf8) + var replyTo: String? + var file: FileInfo? + var image: ImageInfo? + var forwardedFrom: ForwardedFrom? + var senderName = userCache[senderId]?.username ?? "" + if let jsonObj = try? JSONSerialization.jsonObject(with: cachedPlaintext) as? [String: Any] { + messageText = jsonObj["text"] as? String + replyTo = jsonObj["reply_to"] as? String + if senderName.isEmpty, let s = jsonObj["sender"] as? String { + senderName = s + } + if let fileDict = jsonObj["file"] as? [String: Any] { + file = FileInfo( + fileId: fileDict["file_id"] as? String ?? "", + aesKey: fileDict["aes_key"] as? String ?? "", + iv: fileDict["iv"] as? String ?? "", + filename: fileDict["filename"] as? String ?? "", + size: fileDict["size"] as? Int ?? 0, + mimeType: fileDict["mime_type"] as? String ?? "" + ) + } + image = parseImageInfo(from: jsonObj) + if let fwd = jsonObj["forwarded_from"] as? [String: Any], + let fwdSender = fwd["sender"] as? String { + forwardedFrom = ForwardedFrom(sender: fwdSender, + conversationId: fwd["conversation_id"] as? String ?? "", + messageId: fwd["message_id"] as? String ?? "") + } + } + if messageText == nil && file == nil && image == nil { continue } // Control message + let createdAt = msgDict.string(for: "created_at").flatMap { DateParsing.parse($0) } ?? Date() + messages.append(Message( + id: msgId, conversationId: convId, senderId: senderId, + senderUsername: senderName, + createdAt: createdAt, text: messageText, replyTo: replyTo, + imageFileId: msgDict.string(for: "image_file_id"), file: file, + image: image, + isDeleted: false, readBy: readBy, + reactions: reactions, forwardedFrom: forwardedFrom, + pinnedAt: pinnedAt, pinnedBy: pinnedBy + )) + #if DEBUG + print("DEBUG getMessages: loaded from cache msgId=\(msgId)") + #endif + continue + } + + // Try to decrypt (only for messages not in cache) + // Dispatch: self-encrypted (ratchet_header.self) → static key + // group messages (sender_chain_id present, from others) → sender key decrypt + // DM or self-copy → ratchet / self-key decrypt + var plaintext: Data? + let ratchetHeader = msgDict["ratchet_header"] as? [String: Any] + let isSelfEncrypted = (ratchetHeader?["self"] as? Bool) == true + || (ratchetHeader?["self"] as? Int) == 1 + + #if DEBUG + print("DEBUG getMessages: decrypt msgId=\(msgId) senderId=\(senderId) isSelfEncrypted=\(isSelfEncrypted) ratchetHeader=\(ratchetHeader as Any) hasCacheKey=\(cacheKey != nil)") + #endif + + if isSelfEncrypted, let cacheKey = cacheKey { + // Re-encrypted message (from reencryptHistory) — decrypt with self-key + if let ctB64 = (msgDict["encrypted_content"] as? String ?? msgDict["ciphertext"] as? String), + let nonceB64 = msgDict["nonce"] as? String, + let ct = try? ProtocolHandler.decodeBinary(ctB64), + let nonce = try? ProtocolHandler.decodeBinary(nonceB64), + ct.count >= 16 { + let ciphertext = ct.prefix(ct.count - 16) + let tag = ct.suffix(16) + plaintext = try? CryptoUtils.aesDecrypt(key: cacheKey, nonce: nonce, ciphertext: Data(ciphertext), tag: Data(tag)) + #if DEBUG + print("DEBUG getMessages: self-decrypt msgId=\(msgId) ctLen=\(ct.count) nonceLen=\(nonce.count) result=\(plaintext != nil ? "OK(\(plaintext!.count)B)" : "FAILED")") + #endif + } else { + #if DEBUG + print("DEBUG getMessages: self-decrypt msgId=\(msgId) SKIPPED (missing ct/nonce)") + #endif + } + } else if let chainId = msgDict["sender_chain_id"] as? String, !chainId.isEmpty, senderId != userId { + plaintext = decryptGroupMessage( + msgDict: msgDict, senderId: senderId, + senderDeviceId: senderDeviceId, convId: convId + ) + } else { + plaintext = decryptDMRecipientData( + senderData: msgDict, senderId: senderId, + senderDeviceId: senderDeviceId + ) + } + + if let plaintext = plaintext { + // Cache the decrypted plaintext for future use + MessageCache.cacheDecryptedMessage( + email: email, convId: convId, messageId: msgId, + plaintext: plaintext, cacheKey: cacheKey + ) + + // Queue for self-encryption if from another user + if senderId != userId && !msgId.isEmpty { + pendingSelfEncrypt.append((messageId: msgId, plaintext: plaintext)) + } + + let text = String(data: plaintext, encoding: .utf8) + var messageText = text + var replyTo: String? + var file: FileInfo? + var image: ImageInfo? + var forwardedFrom: ForwardedFrom? + + var senderName = userCache[senderId]?.username ?? "" + if let jsonObj = try? JSONSerialization.jsonObject(with: plaintext) as? [String: Any] { + messageText = jsonObj["text"] as? String + replyTo = jsonObj["reply_to"] as? String + if senderName.isEmpty, let s = jsonObj["sender"] as? String { + senderName = s + } + if let fileDict = jsonObj["file"] as? [String: Any] { + file = FileInfo( + fileId: fileDict["file_id"] as? String ?? "", + aesKey: fileDict["aes_key"] as? String ?? "", + iv: fileDict["iv"] as? String ?? "", + filename: fileDict["filename"] as? String ?? "", + size: fileDict["size"] as? Int ?? 0, + mimeType: fileDict["mime_type"] as? String ?? "" + ) + } + image = parseImageInfo(from: jsonObj) + if let fwd = jsonObj["forwarded_from"] as? [String: Any], + let fwdSender = fwd["sender"] as? String { + forwardedFrom = ForwardedFrom(sender: fwdSender, + conversationId: fwd["conversation_id"] as? String ?? "", + messageId: fwd["message_id"] as? String ?? "") + } + } + + if messageText == nil && file == nil && image == nil { continue } // Control message + + let createdAt = msgDict.string(for: "created_at").flatMap { DateParsing.parse($0) } ?? Date() + messages.append(Message( + id: msgId, conversationId: convId, senderId: senderId, + senderUsername: senderName, + createdAt: createdAt, text: messageText, replyTo: replyTo, + imageFileId: msgDict.string(for: "image_file_id"), file: file, + image: image, + isDeleted: false, readBy: readBy, + reactions: reactions, forwardedFrom: forwardedFrom, + pinnedAt: pinnedAt, pinnedBy: pinnedBy + )) + #if DEBUG + print("DEBUG getMessages: decrypted and cached msgId=\(msgId)") + #endif + } else { + #if DEBUG + print("DEBUG getMessages: decryption failed for msgId=\(msgId)") + #endif + } + } + + // Confirm delivery for messages from others (fire-and-forget) + let deliverIds = messages.filter { $0.senderId != userId && !$0.isDeleted }.map(\.id) + if !deliverIds.isEmpty { + Task { await confirmDelivery(convId: convId, messageIds: deliverIds) } + } + + // Flush any queued self-encryptions in background + if !pendingSelfEncrypt.isEmpty { + Task { await flushSelfEncrypt() } + } + + #if DEBUG + print("DEBUG getMessages: returning \(messages.count) messages") + #endif + return messages + } + + func markRead(convId: String, messageIds: [String]) async { + _ = await sendAndReceive(type: "mark_read", params: [ + "conversation_id": convId, + "message_ids": messageIds, + ]) + } + + func markConversationRead(convId: String) async { + _ = await sendAndReceive(type: "mark_conversation_read", params: [ + "conversation_id": convId, + ]) + } + + func deleteMessage(messageId: String, convId: String) async -> Bool { + let resp = await sendAndReceive(type: "delete_message", params: [ + "message_id": messageId, + "conversation_id": convId, + ]) + return resp.string(for: "status") == "ok" + } + + func getDeletedSince(convId: String, sinceTs: String) async -> [String] { + let resp = await sendAndReceive(type: "get_deleted_since", params: [ + "conversation_id": convId, + "since_ts": sinceTs, + ]) + guard resp.string(for: "status") == "ok", + let data = resp.dict(for: "data"), + let ids = data["deleted_ids"] as? [String] else { return [] } + return ids + } + + func reactMessage(messageId: String, conversationId: String, + reaction: String, action: String) async -> Bool { + let resp = await sendAndReceive(type: "react_message", params: [ + "message_id": messageId, + "conversation_id": conversationId, + "reaction": reaction, + "action": action, + ]) + return resp.string(for: "status") == "ok" + } + + func pinMessage(messageId: String, conversationId: String, + action: String) async -> Bool { + let resp = await sendAndReceive(type: "pin_message", params: [ + "message_id": messageId, + "conversation_id": conversationId, + "action": action, + ]) + return resp.string(for: "status") == "ok" + } + + func getPinnedMessages(conversationId: String) async -> [[String: Any]] { + let resp = await sendAndReceive(type: "get_pinned_messages", params: [ + "conversation_id": conversationId, + ]) + guard resp.string(for: "status") == "ok", + let data = resp.dict(for: "data"), + let messages = data["messages"] as? [[String: Any]] else { return [] } + return messages + } + + // MARK: - Group Operations + + func addMember(convId: String, email: String) async -> (success: Bool, message: String) { + let resp = await sendAndReceive(type: "add_member", params: [ + "conversation_id": convId, + "email": email, + ]) + let msg = resp.dict(for: "data")?.string(for: "message") ?? "" + return (resp.string(for: "status") == "ok", msg) + } + + func removeMember(convId: String, userId: String) async -> (success: Bool, message: String) { + let resp = await sendAndReceive(type: "remove_member", params: [ + "conversation_id": convId, + "user_id": userId, + ]) + let msg = resp.dict(for: "data")?.string(for: "message") ?? "" + return (resp.string(for: "status") == "ok", msg) + } + + func leaveGroup(convId: String) async -> (success: Bool, message: String) { + let resp = await sendAndReceive(type: "leave_group", params: [ + "conversation_id": convId, + ]) + if resp.string(for: "status") == "ok" { + // Clean up local sender keys + senderKeyStates.removeValue(forKey: convId) + KeyStorage.deleteSenderKeyState(email: email, convId: convId) + KeyStorage.deleteRecvSenderKeys(email: email, convId: convId) + return (true, "Left group") + } + let msg = resp.dict(for: "data")?.string(for: "message") ?? "Failed" + return (false, msg) + } + + func renameConversation(convId: String, name: String) async -> (success: Bool, message: String) { + let resp = await sendAndReceive(type: "rename_conversation", params: [ + "conversation_id": convId, + "name": name, + ]) + let msg = resp.dict(for: "data")?.string(for: "message") ?? "" + return (resp.string(for: "status") == "ok", msg) + } + + func deleteConversation(convId: String) async -> (success: Bool, message: String) { + let resp = await sendAndReceive(type: "delete_conversation", params: [ + "conversation_id": convId, + ]) + if resp.string(for: "status") == "ok" { + senderKeyStates.removeValue(forKey: convId) + KeyStorage.deleteSenderKeyState(email: email, convId: convId) + KeyStorage.deleteRecvSenderKeys(email: email, convId: convId) + return (true, "Deleted") + } + let msg = resp.dict(for: "data")?.string(for: "message") ?? "Failed" + return (false, msg) + } + + // MARK: - Invitations + + func acceptInvitation(convId: String) async -> (success: Bool, message: String) { + let resp = await sendAndReceive(type: "accept_invitation", params: [ + "conversation_id": convId, + ]) + let msg = resp.dict(for: "data")?.string(for: "message") ?? "" + return (resp.string(for: "status") == "ok", msg) + } + + func declineInvitation(convId: String) async -> (success: Bool, message: String) { + let resp = await sendAndReceive(type: "decline_invitation", params: [ + "conversation_id": convId, + ]) + let msg = resp.dict(for: "data")?.string(for: "message") ?? "" + return (resp.string(for: "status") == "ok", msg) + } + + func listInvitations() async -> [Invitation] { + let resp = await sendAndReceive(type: "list_invitations") + guard resp.string(for: "status") == "ok", + let data = resp.dict(for: "data"), + let invList = data["invitations"] as? [[String: Any]] else { + return [] + } + + return invList.compactMap { dict -> Invitation? in + guard let convId = dict.string(for: "conversation_id") else { return nil } + return Invitation( + id: dict.string(for: "id") ?? convId, + conversationId: convId, + conversationName: dict.string(for: "conversation_name") ?? "Group", + invitedBy: dict.string(for: "invited_by") ?? "", + invitedByUsername: dict.string(for: "invited_by_username") ?? "" + ) + } + } + + // MARK: - Profile + + func getProfile(userId: String? = nil) async -> UserProfile? { + var params: [String: Any] = [:] + if let userId = userId { + params["user_id"] = userId + } + let resp = await sendAndReceive(type: "get_profile", params: params) + guard resp.string(for: "status") == "ok", + let data = resp.dict(for: "data") else { return nil } + + return UserProfile( + userId: data.string(for: "user_id") ?? userId ?? self.userId ?? "", + username: data.string(for: "username"), + email: data.string(for: "email"), + phone: data.string(for: "phone"), + phoneVisible: data.bool(for: "phone_visible") ?? false, + location: data.string(for: "location"), + locationVisible: data.bool(for: "location_visible") ?? false, + avatarFile: data.string(for: "avatar_file") + ) + } + + func updateProfile(phone: String? = nil, phoneVisible: Bool? = nil, + location: String? = nil, locationVisible: Bool? = nil) async -> Bool { + var params: [String: Any] = [:] + if let phone = phone { params["phone"] = phone } + if let phoneVisible = phoneVisible { params["phone_visible"] = phoneVisible } + if let location = location { params["location"] = location } + if let locationVisible = locationVisible { params["location_visible"] = locationVisible } + + let resp = await sendAndReceive(type: "update_profile", params: params) + return resp.string(for: "status") == "ok" + } + + func updateAvatar(imageData: Data) async -> (Bool, String) { + // Resize avatar to fit within protocol message limit (64KB total) + guard let uiImage = UIImage(data: imageData) else { return (false, "Invalid image") } + + let maxDim: CGFloat = 256 + let scale = min(1.0, maxDim / max(uiImage.size.width, uiImage.size.height)) + let newSize = CGSize(width: uiImage.size.width * scale, height: uiImage.size.height * scale) + let renderer = UIGraphicsImageRenderer(size: newSize) + let processed = renderer.image { _ in + uiImage.draw(in: CGRect(origin: .zero, size: newSize)) + } + + guard let jpegData = processed.jpegData(compressionQuality: 0.6) else { return (false, "JPEG encode failed") } + #if DEBUG + print("DEBUG updateAvatar: jpegData=\(jpegData.count) bytes, base64=\(jpegData.count * 4 / 3) bytes approx") + #endif + + let resp = await sendAndReceive(type: "update_avatar", params: [ + "data": ProtocolHandler.encodeBinary(jpegData), + ]) + #if DEBUG + print("DEBUG updateAvatar: resp=\(resp)") + #endif + if resp.string(for: "status") == "ok" { + return (true, "") + } + let errMsg = resp.dict(for: "data")?.string(for: "message") ?? "Upload failed" + return (false, errMsg) + } + + func getAvatar(userId: String) async -> Data? { + let resp = await sendAndReceive(type: "get_avatar", params: ["user_id": userId]) + guard resp.string(for: "status") == "ok", + let data = resp.dict(for: "data"), + let avatarB64 = data.string(for: "data"), + let avatarData = try? ProtocolHandler.decodeBinary(avatarB64) else { + return nil + } + return avatarData + } + + // MARK: - Group Avatar + + func updateGroupAvatar(convId: String, imageData: Data) async -> Bool { + guard let uiImage = UIImage(data: imageData) else { return false } + + let maxDim: CGFloat = 256 + let scale = min(1.0, maxDim / max(uiImage.size.width, uiImage.size.height)) + let newSize = CGSize(width: uiImage.size.width * scale, height: uiImage.size.height * scale) + let renderer = UIGraphicsImageRenderer(size: newSize) + let processed = renderer.image { _ in + uiImage.draw(in: CGRect(origin: .zero, size: newSize)) + } + + guard let jpegData = processed.jpegData(compressionQuality: 0.6) else { return false } + + let resp = await sendAndReceive(type: "update_group_avatar", params: [ + "conversation_id": convId, + "data": ProtocolHandler.encodeBinary(jpegData), + ]) + return resp.string(for: "status") == "ok" + } + + func getGroupAvatar(convId: String) async -> Data? { + let resp = await sendAndReceive(type: "get_group_avatar", params: ["conversation_id": convId]) + guard resp.string(for: "status") == "ok", + let data = resp.dict(for: "data"), + let avatarB64 = data.string(for: "data"), + let avatarData = try? ProtocolHandler.decodeBinary(avatarB64) else { + return nil + } + return avatarData + } + + // MARK: - Key Rotation + + func rotateKeys(password: String) async -> (success: Bool, message: String) { + guard rsaPrivate != nil, userId != nil else { + return (false, "Not logged in.") + } + + // Generate new RSA-4096 keypair + let newPriv: SecKey + let newPub: SecKey + do { + (newPriv, newPub) = try RSACrypto.generateKeypair() + } catch { + return (false, "Key generation failed: \(error.localizedDescription)") + } + + // Save locally (password-encrypted) + var pwdBytes = Array(password.utf8) + defer { pwdBytes.withUnsafeMutableBytes { ptr in _ = memset(ptr.baseAddress!, 0, ptr.count) } } + do { + try KeyStorage.saveRSAKeys(email: email, privateKey: newPriv, publicKey: newPub, password: Data(pwdBytes)) + } catch { + return (false, "Failed to save keys: \(error.localizedDescription)") + } + + // Update in-memory + self.rsaPrivate = newPriv + self.rsaPublic = newPub + + // Serialize public key to PEM and send to server + guard let pubPEM = try? RSACrypto.serializePublicKey(newPub), + let pubPEMStr = String(data: pubPEM, encoding: .utf8) else { + return (false, "Failed to serialize public key") + } + + let resp = await sendAndReceive(type: "rotate_keys", params: ["public_key": pubPEMStr]) + if resp.string(for: "status") == "ok" { + return (true, "Keys rotated. Other devices will be disconnected.") + } + let errMsg = resp.dict(for: "data")?.string(for: "message") ?? "Rotation failed" + return (false, errMsg) + } + + // MARK: - Device Pairing + + private var pairingTempPrivateKey: SecKey? + private var pairingPollToken: String = "" + + /// Start pairing on NEW device: generates temp RSA-2048 key, sends to server, returns 8-digit code + func pairingStart(email: String) async -> (success: Bool, codeOrMessage: String) { + // Generate ephemeral RSA-2048 keypair + let tempPriv: SecKey + let tempPub: SecKey + do { + (tempPriv, tempPub) = try RSACrypto.generateKeypair2048() + } catch { + return (false, "Temp key generation failed") + } + self.pairingTempPrivateKey = tempPriv + + // Serialize temp public key to PEM + guard let tempPubPEM = try? RSACrypto.serializePublicKey(tempPub), + let tempPubStr = String(data: tempPubPEM, encoding: .utf8) else { + return (false, "Failed to serialize temp key") + } + + let resp = await sendAndReceive(type: "pairing_start", params: [ + "email": email, + "temp_public_key": tempPubStr, + ]) + + guard resp.string(for: "status") == "ok", + let data = resp.dict(for: "data"), + let code = data.string(for: "code") else { + let msg = resp.dict(for: "data")?.string(for: "message") ?? "Pairing start failed" + return (false, msg) + } + + self.pairingPollToken = data.string(for: "poll_token") ?? "" + return (true, code) + } + + /// Wait for pairing on NEW device: polls server every 2s, decrypts keys when ready + func pairingWait(code: String, email: String, password: String, timeout: TimeInterval = 300) async -> (success: Bool, message: String) { + guard pairingTempPrivateKey != nil else { + return (false, "Pairing not started.") + } + + let deadline = Date().addingTimeInterval(timeout) + + while Date() < deadline { + let resp = await sendAndReceive(type: "pairing_poll", params: [ + "code": code, + "poll_token": pairingPollToken, + ]) + + guard resp.string(for: "status") == "ok", + let data = resp.dict(for: "data") else { + let msg = resp.dict(for: "data")?.string(for: "message") ?? "Poll failed" + return (false, msg) + } + + guard data.bool(for: "ready") == true else { + try? await Task.sleep(for: .seconds(2)) + continue + } + + // Payload received — decrypt keys + guard let payload = data["payload"] as? [String: Any], + let encKeyB64 = payload.string(for: "encrypted_key"), + let ivB64 = payload.string(for: "iv"), + let ctB64 = payload.string(for: "ciphertext"), + let tagB64 = payload.string(for: "tag") else { + return (false, "Invalid payload format") + } + + do { + let encAESKey = try ProtocolHandler.decodeBinary(encKeyB64) + let iv = try ProtocolHandler.decodeBinary(ivB64) + let ct = try ProtocolHandler.decodeBinary(ctB64) + let tag = try ProtocolHandler.decodeBinary(tagB64) + + // RSA-OAEP decrypt the AES key + let aesKey = try RSACrypto.decrypt(pairingTempPrivateKey!, ciphertext: encAESKey) + + // AES-GCM decrypt the keys JSON + let keysJSON = try CryptoUtils.aesDecrypt(key: aesKey, nonce: iv, ciphertext: ct, tag: tag) + + guard let keysDict = try JSONSerialization.jsonObject(with: keysJSON) as? [String: String], + let rsaPrivPEM = keysDict["rsa_private"], + let identityHex = keysDict["identity_private"] else { + return (false, "Invalid keys JSON") + } + + // Import RSA key + var pwdBytes = Array(password.utf8) + defer { pwdBytes.withUnsafeMutableBytes { ptr in _ = memset(ptr.baseAddress!, 0, ptr.count) } } + let pwdData = Data(pwdBytes) + + let rsaPriv = try RSACrypto.loadPrivateKey(Data(rsaPrivPEM.utf8), password: nil) + let rsaPub = SecKeyCopyPublicKey(rsaPriv)! + + // Import Ed25519 identity key + guard let identityRaw = Data(hexString: identityHex), identityRaw.count == 32 else { + return (false, "Invalid identity key format") + } + let edPriv = try Curve25519.Signing.PrivateKey(rawRepresentation: identityRaw) + let edPub = edPriv.publicKey + + // Save to disk (encrypted with password) + try KeyStorage.saveRSAKeys(email: email, privateKey: rsaPriv, publicKey: rsaPub, password: pwdData) + try KeyStorage.saveIdentityKeys(email: email, privateKey: edPriv, publicKey: edPub, password: pwdData) + + // Update in-memory state + self.email = email + self.rsaPrivate = rsaPriv + self.rsaPublic = rsaPub + self.identityPrivate = edPriv + self.identityPublic = edPub + self.cacheKey = CryptoUtils.deriveSelfEncryptionKey(identityPrivateRaw: edPriv.rawData) + self.localKey = CryptoUtils.deriveLocalStorageKey(identityPrivateRaw: edPriv.rawData) + + // Clear pairing state + self.pairingTempPrivateKey = nil + self.pairingPollToken = "" + + return (true, "Pairing complete.") + } catch { + return (false, "Failed to import keys: \(error.localizedDescription)") + } + } + + return (false, "Pairing timed out.") + } + + /// Re-encrypt all cached messages with self-encryption key so a paired device can read them. + /// Called by authorizeDevice() before sending keys. + func reencryptHistory() async { + guard let edPriv = identityPrivate else { return } + let selfKey = CryptoUtils.deriveSelfEncryptionKey(identityPrivateRaw: edPriv.rawData) + + // Phase 1: Fetch all messages to populate cache (decrypts and caches them) + let convs = await listConversations() + for conv in convs { + var offset = 0 + while true { + let msgs = await getMessages(convId: conv.id, limit: 200, offset: offset) + if msgs.count < 200 { break } + offset += msgs.count + } + } + + // Phase 2: Read per-message cache and re-encrypt with self-key + var allUpdates: [[String: String]] = [] + for conv in convs { + let cachedMessages = MessageCache.loadAllCachedMessages(email: email, convId: conv.id, cacheKey: cacheKey) + for (msgId, plaintext) in cachedMessages { + // Skip control messages + if let jsonObj = try? JSONSerialization.jsonObject(with: plaintext) as? [String: Any] { + if jsonObj["_control"] != nil { continue } + let text = jsonObj["text"] as? String + let image = jsonObj["image"] + let file = jsonObj["file"] + if text == nil && image == nil && file == nil { continue } + } + + // Re-encrypt with self-encryption key + guard let (_, nonce, ct, tag) = try? CryptoUtils.aesEncrypt(plaintext, key: selfKey) else { continue } + + allUpdates.append([ + "message_id": msgId, + "encrypted_content": ProtocolHandler.encodeBinary(ct + tag), + "nonce": ProtocolHandler.encodeBinary(nonce), + ]) + } + } + + if allUpdates.isEmpty { return } + + // Send in batches of 500 + let batchSize = 500 + for start in stride(from: 0, to: allUpdates.count, by: batchSize) { + let end = min(start + batchSize, allUpdates.count) + let batch = Array(allUpdates[start.. (success: Bool, message: String) { + guard let rsaPriv = rsaPrivate, let edPriv = identityPrivate else { + return (false, "Not logged in.") + } + + // Claim the pairing code — get temp public key + let claimResp = await sendAndReceive(type: "pairing_claim", params: ["code": code]) + guard claimResp.string(for: "status") == "ok", + let claimData = claimResp.dict(for: "data"), + let tempPubPEM = claimData.string(for: "temp_public_key") else { + let msg = claimResp.dict(for: "data")?.string(for: "message") ?? "Claim failed" + return (false, msg) + } + + // Load temp public key + let tempPub: SecKey + do { + tempPub = try RSACrypto.loadPublicKey(Data(tempPubPEM.utf8)) + } catch { + return (false, "Invalid temp public key: \(error.localizedDescription)") + } + + // Re-encrypt message history so new device can read old messages + await reencryptHistory() + + // Build keys payload (RSA private unencrypted PEM + Ed25519 raw hex) + let rsaPrivPEM: String + do { + let pemData = try RSACrypto.serializePrivateKey(rsaPriv, password: nil) + rsaPrivPEM = String(data: pemData, encoding: .utf8) ?? "" + } catch { + return (false, "Failed to serialize RSA key") + } + + let identityHex = edPriv.rawRepresentation.map { String(format: "%02x", $0) }.joined() + + let keysDict: [String: String] = [ + "rsa_private": rsaPrivPEM, + "identity_private": identityHex, + ] + + guard let keysJSON = try? JSONSerialization.data(withJSONObject: keysDict) else { + return (false, "JSON serialization failed") + } + + // Encrypt: AES-GCM for payload, RSA-OAEP for AES key + do { + let (aesKey, nonce, ct, tag) = try CryptoUtils.aesEncrypt(keysJSON) + let encAESKey = try RSACrypto.encrypt(tempPub, plaintext: aesKey) + + let payload: [String: String] = [ + "encrypted_key": ProtocolHandler.encodeBinary(encAESKey), + "iv": ProtocolHandler.encodeBinary(nonce), + "ciphertext": ProtocolHandler.encodeBinary(ct), + "tag": ProtocolHandler.encodeBinary(tag), + ] + + let resp = await sendAndReceive(type: "pairing_send", params: [ + "code": code, + "payload": payload, + ]) + + if resp.string(for: "status") == "ok" { + return (true, "Device authorized.") + } + let msg = resp.dict(for: "data")?.string(for: "message") ?? "Send failed" + return (false, msg) + } catch { + return (false, "Encryption failed: \(error.localizedDescription)") + } + } + + // MARK: - File Sharing + + func sendFile(convId: String, fileData: Data, filename: String, mimeType: String, + members: [ConversationMember], replyTo: String? = nil) async -> (success: Bool, message: String, sentMessage: Message?) { + // Encrypt file with AES-GCM + guard let (aesKey, nonce, ct, tag) = try? CryptoUtils.aesEncrypt(fileData) else { + return (false, "File encryption failed", nil) + } + + let encryptedData = ct + tag + let fileType = mimeType.hasPrefix("image/") ? "image" : "file" + + // Start upload + let fileId = UUID().uuidString.lowercased() + let startResp = await sendAndReceive(type: "upload_image_start", params: [ + "conversation_id": convId, + "file_id": fileId, + "file_size": encryptedData.count, + "file_type": fileType, + ]) + guard startResp.string(for: "status") == "ok" else { + let msg = startResp.dict(for: "data")?.string(for: "message") ?? "Upload start failed" + return (false, msg, nil) + } + + // Upload chunks + var offset = 0 + while offset < encryptedData.count { + let end = min(offset + Constants.imageChunkSize, encryptedData.count) + let chunk = encryptedData[offset.. (success: Bool, message: String, sentMessage: Message?) { + + // Step 1: Process image on MainActor (UIKit requires main thread) + let processed = await MainActor.run { () -> (imageBytes: Data, thumbnailB64: String, originalSize: Int)? in + guard let uiImage = UIImage(data: imageData) else { return nil } + + var finalBytes = imageData + var img = uiImage + // AES-GCM overhead: 12 nonce + 16 tag = 28 bytes + let maxPlaintextSize = Constants.maxImageBytes - 28 + + // If too large, progressively compress + if finalBytes.count > maxPlaintextSize { + let qualities: [CGFloat] = [0.92, 0.85, 0.75, 0.60] + var fits = false + for quality in qualities { + if let compressed = img.jpegData(compressionQuality: quality) { + finalBytes = compressed + if compressed.count <= maxPlaintextSize { fits = true; break } + } + } + + if !fits { + // Downscale dimensions + for maxDim: CGFloat in [3840, 2560, 1920, 1280] { + if max(img.size.width, img.size.height) > maxDim { + let scale = maxDim / max(img.size.width, img.size.height) + let newSize = CGSize(width: img.size.width * scale, height: img.size.height * scale) + let renderer = UIGraphicsImageRenderer(size: newSize) + img = renderer.image { _ in img.draw(in: CGRect(origin: .zero, size: newSize)) } + } + if let compressed = img.jpegData(compressionQuality: 0.75) { + finalBytes = compressed + if compressed.count <= maxPlaintextSize { break } + } + } + } + } + + // Generate thumbnail (200x200, quality 60) + let thumbScale = min(200.0 / max(img.size.width, img.size.height), 1.0) + let thumbDim = CGSize(width: img.size.width * thumbScale, height: img.size.height * thumbScale) + let thumbRenderer = UIGraphicsImageRenderer(size: thumbDim) + let thumbImg = thumbRenderer.image { _ in img.draw(in: CGRect(origin: .zero, size: thumbDim)) } + let thumbB64 = thumbImg.jpegData(compressionQuality: 0.6)?.base64EncodedString() ?? "" + + return (finalBytes, thumbB64, imageData.count) + } + + guard let processed = processed else { + return (false, "Invalid image data", nil) + } + + // Step 2: Encrypt on background (actor isolates this automatically) + guard let (aesKey, nonce, ct, tag) = try? CryptoUtils.aesEncrypt(processed.imageBytes) else { + return (false, "Image encryption failed", nil) + } + let encryptedData = ct + tag + + // Step 3: Chunked upload + let fileId = UUID().uuidString.lowercased() + let startResp = await sendAndReceive(type: "upload_image_start", params: [ + "conversation_id": convId, + "file_id": fileId, + "file_size": encryptedData.count, + "file_type": "image", + ]) + guard startResp.string(for: "status") == "ok" else { + let msg = startResp.dict(for: "data")?.string(for: "message") ?? "Upload start failed" + return (false, msg, nil) + } + + var offset = 0 + while offset < encryptedData.count { + let end = min(offset + Constants.imageChunkSize, encryptedData.count) + let chunk = encryptedData[offset.. Data? { + #if DEBUG + print("DEBUG downloadFile: starting download for fileId=\(fileId) convId=\(conversationId ?? "nil") aesKeyLen=\(aesKey.count) ivLen=\(iv.count)") + #endif + var allData = Data() + var offset = 0 + + while true { + var params: [String: Any] = [ + "file_id": fileId, + "offset": offset, + ] + if let conversationId = conversationId { + params["conversation_id"] = conversationId + } + let resp = await sendAndReceive(type: "download_image", params: params) + + let status = resp.string(for: "status") + guard status == "ok" else { + let errMsg = resp.dict(for: "data")?.string(for: "message") ?? "unknown" + #if DEBUG + print("DEBUG downloadFile: server error status=\(status ?? "nil") msg=\(errMsg)") + #endif + break + } + guard let data = resp.dict(for: "data") else { + #if DEBUG + print("DEBUG downloadFile: missing 'data' dict in response, keys=\(Array(resp.keys))") + #endif + break + } + guard let chunkB64 = data.string(for: "data") else { + #if DEBUG + print("DEBUG downloadFile: missing 'data' string in data dict, keys=\(Array(data.keys))") + #endif + break + } + guard let chunk = try? ProtocolHandler.decodeBinary(chunkB64) else { + #if DEBUG + print("DEBUG downloadFile: base64 decode failed, chunkB64 length=\(chunkB64.count)") + #endif + break + } + + if chunk.isEmpty { + #if DEBUG + print("DEBUG downloadFile: empty chunk received, done") + #endif + break + } + allData.append(chunk) + offset += chunk.count + #if DEBUG + print("DEBUG downloadFile: received chunk \(chunk.count) bytes, total=\(allData.count)") + #endif + + if data.bool(for: "done") == true { + #if DEBUG + print("DEBUG downloadFile: server signaled done") + #endif + break + } + } + + guard !allData.isEmpty else { + #if DEBUG + print("DEBUG downloadFile: no data downloaded for fileId=\(fileId)") + #endif + return nil + } + + // Decrypt: allData = ciphertext + tag(16) + guard allData.count >= 16 else { + #if DEBUG + print("DEBUG downloadFile: data too short (\(allData.count) bytes) for fileId=\(fileId)") + #endif + return nil + } + let ct = allData.prefix(allData.count - 16) + let tag = allData.suffix(16) + #if DEBUG + print("DEBUG downloadFile: decrypting \(ct.count) bytes ciphertext + 16 bytes tag") + #endif + do { + let result = try CryptoUtils.aesDecrypt(key: aesKey, nonce: iv, ciphertext: Data(ct), tag: Data(tag)) + #if DEBUG + print("DEBUG downloadFile: decryption success, \(result.count) bytes") + #endif + return result + } catch { + #if DEBUG + print("DEBUG downloadFile: decryption FAILED: \(error)") + #endif + return nil + } + } + + // MARK: - Devices + + func listDevices() async -> [[String: Any]] { + let resp = await sendAndReceive(type: "list_devices") + guard resp.string(for: "status") == "ok", + let data = resp.dict(for: "data") else { return [] } + return data["devices"] as? [[String: Any]] ?? [] + } + + func removeDevice(deviceIdToRemove: String) async -> Bool { + let resp = await sendAndReceive(type: "remove_device", params: [ + "device_id": deviceIdToRemove, + ]) + return resp.string(for: "status") == "ok" + } + + // MARK: - Session Reset + + func resetSession(peerUserId: String, peerDeviceId: String? = nil) async { + if let peerDeviceId = peerDeviceId { + let sessionKey = "\(peerUserId):\(peerDeviceId)" + sessions.removeValue(forKey: sessionKey) + KeyStorage.deleteSession(email: email, peerUserId: peerUserId, peerDeviceId: peerDeviceId) + } else { + // Delete all sessions for this user + for key in sessions.keys where key.hasPrefix(peerUserId) { + sessions.removeValue(forKey: key) + } + KeyStorage.deleteSession(email: email, peerUserId: peerUserId) + } + + _ = await sendAndReceive(type: "session_reset", params: [ + "peer_user_id": peerUserId, + "peer_device_id": peerDeviceId ?? "", + ]) + } + + func handleSessionResetNotification(fromUserId: String, fromDeviceId: String?) { + if let deviceId = fromDeviceId { + let sessionKey = "\(fromUserId):\(deviceId)" + sessions.removeValue(forKey: sessionKey) + KeyStorage.deleteSession(email: email, peerUserId: fromUserId, peerDeviceId: deviceId) + } else { + for key in sessions.keys where key.hasPrefix(fromUserId) { + sessions.removeValue(forKey: key) + } + KeyStorage.deleteSession(email: email, peerUserId: fromUserId) + } + } + + // MARK: - Search + + func searchMessages(convId: String, query: String) -> [[String: Any]] { + MessageCache.search(email: email, convId: convId, query: query, cacheKey: cacheKey) + } + + // MARK: - Change Username + + func changeUsername(newUsername: String) async -> (success: Bool, message: String) { + let trimmed = newUsername.trimmingCharacters(in: .whitespacesAndNewlines) + guard !trimmed.isEmpty, trimmed.count <= 100 else { + return (false, "Username must be 1-100 characters.") + } + let resp = await sendAndReceive(type: "change_username", params: ["username": trimmed]) + if resp.string(for: "status") == "ok" { + if let data = resp.dict(for: "data"), let newName = data.string(for: "username") { + username = newName + } else { + username = trimmed + } + return (true, "Username changed.") + } + let msg = resp.dict(for: "data")?.string(for: "message") ?? "Unknown error" + return (false, msg) + } + + // MARK: - Change Password + + func changePassword(oldPassword: String, newPassword: String) -> (success: Bool, message: String) { + guard !email.isEmpty else { + return (false, "Not logged in.") + } + + let oldPwd = Data(oldPassword.utf8) + let newPwd = Data(newPassword.utf8) + + // 1. Verify old password by loading keys + let (rsaPriv, rsaPub, err) = KeyStorage.loadRSAKeys(email: email, password: oldPwd) + guard let rsaPriv = rsaPriv, let rsaPub = rsaPub else { + return (false, err ?? "Wrong current password.") + } + + let (edPriv, edPub) = KeyStorage.loadIdentityKeys(email: email, password: oldPwd) + guard let edPriv = edPriv, let edPub = edPub else { + return (false, "Failed to load identity key.") + } + + // 2. Re-save with new password + do { + try KeyStorage.saveRSAKeys(email: email, privateKey: rsaPriv, publicKey: rsaPub, password: newPwd) + try KeyStorage.saveIdentityKeys(email: email, privateKey: edPriv, publicKey: edPub, password: newPwd) + return (true, "Password changed successfully.") + } catch { + return (false, "Failed to save keys: \(error.localizedDescription)") + } + } + + // MARK: - Confirm Delivery + + func confirmDelivery(convId: String, messageIds: [String]) async { + guard !messageIds.isEmpty else { return } + // Fire-and-forget, non-critical + _ = await sendAndReceive(type: "confirm_delivery", params: [ + "conversation_id": convId, + "message_ids": messageIds, + ]) + } + + // MARK: - Forward Message + + func forwardMessage(targetConvId: String, originalMsg: [String: Any], + targetMembers: [ConversationMember]) async -> (success: Bool, message: String, sentMessage: Message?) { + let text = originalMsg["text"] as? String ?? "" + + var payload: [String: Any] = [ + "sender": username, + "text": text, + "forwarded_from": [ + "sender": originalMsg["sender"] as? String ?? "", + "conversation_id": originalMsg["conversation_id"] as? String ?? "", + "message_id": originalMsg["message_id"] as? String ?? "", + ] as [String: Any], + "timestamp": ISO8601DateFormatter().string(from: Date()), + ] + + // Forward image/file metadata (the encrypted blob is already on the server) + if let image = originalMsg["image"] { + payload["image"] = image + if text.isEmpty { payload["text"] = "" } + } + if let file = originalMsg["file"] { + payload["file"] = file + if text.isEmpty { payload["text"] = "" } + } + + var extraPayload: [String: Any] = [:] + if let fwd = payload["forwarded_from"] { + extraPayload["forwarded_from"] = fwd + } + if let image = payload["image"] { + extraPayload["image"] = image + } + if let file = payload["file"] { + extraPayload["file"] = file + } + + return await sendMessage( + convId: targetConvId, text: text, members: targetMembers, + extraPayload: extraPayload + ) + } + + // MARK: - Proof of Work + + static func solvePow(challenge: String, difficulty: Int) -> String { + let targetBytes = difficulty / 8 + let targetBits = difficulty % 8 + let mask: UInt8 = targetBits > 0 ? UInt8((0xFF << (8 - targetBits)) & 0xFF) : 0 + + var nonce: UInt64 = 0 + while true { + let input = "\(challenge)\(nonce)" + let inputData = Data(input.utf8) + let digest = SHA256.hash(data: inputData) + let digestBytes = Array(digest) + + var ok = true + for i in 0.. 0 { + if digestBytes[targetBytes] & mask != 0 { + ok = false + } + } + if ok { + return String(nonce) + } + nonce += 1 + } + } + + // MARK: - TOFU / Contact Verification + + /// Load TOFU and verification stores from disk + private func loadVerificationStores() { + guard !email.isEmpty else { return } + knownIdentityKeys = KeyStorage.loadKnownIdentityKeys(email: email, localKey: localKey) + verifiedContacts = KeyStorage.loadVerifiedContacts(email: email, localKey: localKey) + } + + /// Check a user's identity key against TOFU registry + func checkIdentityKey(userId: String, identityKeyBytes: Data) -> String { + let ikHex = identityKeyBytes.hexString + let now = ISO8601DateFormatter().string(from: Date()) + let known = knownIdentityKeys[userId] + + if known == nil { + // TOFU: trust on first use + knownIdentityKeys[userId] = [ + "identity_key": ikHex, + "first_seen": now, + "last_seen": now, + ] + try? KeyStorage.saveKnownIdentityKeys(email: email, keys: knownIdentityKeys, localKey: localKey) + return "new" + } + + if known?["identity_key"] == ikHex { + knownIdentityKeys[userId]?["last_seen"] = now + try? KeyStorage.saveKnownIdentityKeys(email: email, keys: knownIdentityKeys, localKey: localKey) + let verified = verifiedContacts[userId] + if verified != nil && verified?["identity_key"] == ikHex { + return "verified" + } + return "trusted" + } + + // Key has CHANGED + let wasVerified = verifiedContacts[userId] != nil + return wasVerified ? "changed_verified" : "changed" + } + + /// Mark a contact's identity key as explicitly verified + func verifyContact(userId: String, identityKey: Data, method: String = "manual") { + let ikHex = identityKey.hexString + let now = ISO8601DateFormatter().string(from: Date()) + verifiedContacts[userId] = [ + "identity_key": ikHex, + "verified_at": now, + "method": method, + ] + if knownIdentityKeys[userId] == nil { + knownIdentityKeys[userId] = [ + "identity_key": ikHex, + "first_seen": now, + "last_seen": now, + ] + } else { + knownIdentityKeys[userId]?["last_seen"] = now + } + try? KeyStorage.saveVerifiedContacts(email: email, contacts: verifiedContacts, localKey: localKey) + try? KeyStorage.saveKnownIdentityKeys(email: email, keys: knownIdentityKeys, localKey: localKey) + } + + /// Remove explicit verification for a contact + func unverifyContact(userId: String) { + verifiedContacts.removeValue(forKey: userId) + try? KeyStorage.saveVerifiedContacts(email: email, contacts: verifiedContacts, localKey: localKey) + } + + /// Accept a changed identity key + func acceptKeyChange(userId: String, newIdentityKey: Data) { + let ikHex = newIdentityKey.hexString + let now = ISO8601DateFormatter().string(from: Date()) + knownIdentityKeys[userId] = [ + "identity_key": ikHex, + "first_seen": now, + "last_seen": now, + ] + verifiedContacts.removeValue(forKey: userId) + try? KeyStorage.saveKnownIdentityKeys(email: email, keys: knownIdentityKeys, localKey: localKey) + try? KeyStorage.saveVerifiedContacts(email: email, contacts: verifiedContacts, localKey: localKey) + } + + /// Get verification status for a user + func getVerificationStatus(userId: String) -> String { + if let verified = verifiedContacts[userId] { + let known = knownIdentityKeys[userId] + if known?["identity_key"] == verified["identity_key"] { + return "verified" + } + } + if knownIdentityKeys[userId] != nil { + return "trusted" + } + return "unverified" + } + + /// Get formatted safety number for a peer + func getSafetyNumber(peerUserId: String) -> String? { + guard let idPub = identityPublic, let myId = userId else { return nil } + let myIK = idPub.rawData + guard let peerIK = getPeerIdentityKeySync(userId: peerUserId) else { return nil } + return ContactVerification.computeSafetyNumber( + myUserId: myId, myIdentityKey: myIK, + theirUserId: peerUserId, theirIdentityKey: peerIK + ) + } + + /// Get formatted fingerprint for own identity key + func getMyFingerprint() -> String? { + guard let idPub = identityPublic, let myId = userId else { return nil } + let fp = ContactVerification.computeFingerprint(userId: myId, identityKey: idPub.rawData) + return ContactVerification.formatFingerprint(fp) + } + + /// Get formatted fingerprint for a peer's identity key + func getPeerFingerprint(peerUserId: String) -> String? { + guard let peerIK = getPeerIdentityKeySync(userId: peerUserId) else { return nil } + let fp = ContactVerification.computeFingerprint(userId: peerUserId, identityKey: peerIK) + return ContactVerification.formatFingerprint(fp) + } + + /// Get QR code payload bytes for own identity + func getVerificationQRData() -> Data? { + guard let idPub = identityPublic, let myId = userId else { return nil } + return ContactVerification.encodeVerificationQR(userId: myId, identityKey: idPub.rawData) + } + + /// Verify a scanned QR code against known identity keys + func verifyQRCode(qrData: Data) -> (success: Bool, userId: String, message: String) { + let decoded: (userId: String, identityKey: Data) + do { + decoded = try ContactVerification.decodeVerificationQR(qrData) + } catch { + return (false, "", "Invalid QR code: \(error.localizedDescription)") + } + guard let peerIK = getPeerIdentityKeySync(userId: decoded.userId) else { + return (false, decoded.userId, "Unknown user — not in your contacts.") + } + guard peerIK == decoded.identityKey else { + return (false, decoded.userId, "Identity key MISMATCH — verification failed!") + } + verifyContact(userId: decoded.userId, identityKey: decoded.identityKey, method: "qr_code") + let name = userCache[decoded.userId]?.username ?? String(decoded.userId.prefix(8)) + return (true, decoded.userId, "Verified \(name) via QR code.") + } + + /// Get a peer's identity key bytes (from cache, TOFU registry, or server) + func getPeerIdentityKey(userId: String) async -> Data? { + // 1. Check local caches first + if let ik = getPeerIdentityKeySync(userId: userId) { + return ik + } + // 2. Fetch from server + if let user = await getUserInfo(userId: userId) { + return user.identityKey + } + return nil + } + + /// Sync lookup: user cache → TOFU registry + private func getPeerIdentityKeySync(userId: String) -> Data? { + if let ik = userCache[userId]?.identityKey { + return ik + } + if let ikHex = knownIdentityKeys[userId]?["identity_key"], + let ikData = Data(hexString: ikHex) { + return ikData + } + return nil + } + + // MARK: - Brute-Force Lockout + + static func checkLockout(email: String) -> TimeInterval { + guard let dir = try? KeyStorage.getKeyDir(email: email) else { return 0 } + let path = dir.appendingPathComponent("login_lockout.json") + guard let data = try? Data(contentsOf: path), + let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any], + let lockedUntil = json["locked_until"] as? TimeInterval else { return 0 } + return max(0, lockedUntil - Date().timeIntervalSince1970) + } + + static func recordFailedAttempt(email: String) { + guard let dir = try? KeyStorage.getKeyDir(email: email) else { return } + let path = dir.appendingPathComponent("login_lockout.json") + var failed = 0 + if let data = try? Data(contentsOf: path), + let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any] { + failed = json["failed_attempts"] as? Int ?? 0 + } + failed += 1 + let delay = min(pow(lockoutBaseSeconds, Double(failed)), lockoutMaxSeconds) + let lockedUntil = Date().timeIntervalSince1970 + delay + let json: [String: Any] = ["failed_attempts": failed, "locked_until": lockedUntil] + if let data = try? JSONSerialization.data(withJSONObject: json) { + try? data.write(to: path, options: .completeFileProtection) + } + } + + static func clearLockout(email: String) { + guard let dir = try? KeyStorage.getKeyDir(email: email) else { return } + let path = dir.appendingPathComponent("login_lockout.json") + try? FileManager.default.removeItem(at: path) + } +} diff --git a/ios_client 0.8.5/Kecalek/Core/KeyStorage.swift b/ios_client 0.8.5/Kecalek/Core/KeyStorage.swift new file mode 100644 index 0000000..dbbd3c9 --- /dev/null +++ b/ios_client 0.8.5/Kecalek/Core/KeyStorage.swift @@ -0,0 +1,485 @@ +import Foundation +import CryptoKit + +/// Local file storage for keys, sessions, and sender keys. +/// Matches Python: chat_core.py key storage functions. +/// +/// Base directory: Application Support / EncryptedChat / {email} +/// Same file names as Python client for cross-platform compatibility. +enum KeyStorage { + + // MARK: - Base Directory + + /// Get or create the key storage directory for a user + static func getKeyDir(email: String) throws -> URL { + let appSupport = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask).first! + let dir = appSupport.appendingPathComponent("EncryptedChat").appendingPathComponent(email) + try FileManager.default.createDirectory(at: dir, withIntermediateDirectories: true) + // iOS file protection + try (dir as NSURL).setResourceValue(URLFileProtection.complete, forKey: .fileProtectionKey) + return dir + } + + // MARK: - RSA Keys + + /// Save RSA keypair + static func saveRSAKeys(email: String, privateKey: SecKey, publicKey: SecKey, password: Data? = nil) throws { + let dir = try getKeyDir(email: email) + let privData = try RSACrypto.serializePrivateKey(privateKey, password: password) + let pubData = try RSACrypto.serializePublicKey(publicKey) + try writeProtected(privData, to: dir.appendingPathComponent("private.pem")) + try writeProtected(pubData, to: dir.appendingPathComponent("public.pem")) + } + + /// Load RSA keypair. Returns (private, public, error). + static func loadRSAKeys(email: String, password: Data? = nil) -> (SecKey?, SecKey?, String?) { + guard let dir = try? getKeyDir(email: email) else { + return (nil, nil, "Cannot access key directory") + } + let privPath = dir.appendingPathComponent("private.pem") + let pubPath = dir.appendingPathComponent("public.pem") + + guard FileManager.default.fileExists(atPath: privPath.path) else { + return (nil, nil, "No local keys found.") + } + + guard let privData = try? Data(contentsOf: privPath), + let pubData = try? Data(contentsOf: pubPath) else { + return (nil, nil, "Cannot read key files.") + } + + do { + let privateKey = try RSACrypto.loadPrivateKey(privData, password: password) + let publicKey = try RSACrypto.loadPublicKey(pubData) + return (privateKey, publicKey, nil) + } catch { + // Try without password (unencrypted) + do { + let privateKey = try RSACrypto.loadPrivateKey(privData, password: nil) + let publicKey = try RSACrypto.loadPublicKey(pubData) + // Re-save with password if provided + if let password = password { + try? saveRSAKeys(email: email, privateKey: privateKey, publicKey: publicKey, password: password) + } + return (privateKey, publicKey, nil) + } catch { + return (nil, nil, "Invalid or missing password.") + } + } + } + + // MARK: - Identity Keys (Ed25519) + + static func saveIdentityKeys( + email: String, + privateKey: Curve25519.Signing.PrivateKey, + publicKey: Curve25519.Signing.PublicKey, + password: Data? = nil + ) throws { + let dir = try getKeyDir(email: email) + let privData = try Ed25519Crypto.serializePrivate(privateKey, password: password) + let pubData = Ed25519Crypto.serializePublic(publicKey) + try writeProtected(privData, to: dir.appendingPathComponent("identity_private.bin")) + try writeProtected(pubData, to: dir.appendingPathComponent("identity_public.bin")) + } + + static func loadIdentityKeys( + email: String, + password: Data? = nil + ) -> (Curve25519.Signing.PrivateKey?, Curve25519.Signing.PublicKey?) { + guard let dir = try? getKeyDir(email: email) else { return (nil, nil) } + let privPath = dir.appendingPathComponent("identity_private.bin") + let pubPath = dir.appendingPathComponent("identity_public.bin") + + guard FileManager.default.fileExists(atPath: privPath.path), + let privData = try? Data(contentsOf: privPath), + let pubData = try? Data(contentsOf: pubPath) else { + return (nil, nil) + } + + do { + let priv = try Ed25519Crypto.loadPrivate(privData, password: password) + let pub = try Ed25519Crypto.loadPublic(pubData) + return (priv, pub) + } catch { + return (nil, nil) + } + } + + // MARK: - Signed Pre-Key + + static func saveSPK(email: String, privateKey: Curve25519.KeyAgreement.PrivateKey, spkId: String) throws { + let dir = try getKeyDir(email: email) + try writeProtected(X25519Crypto.serializePrivate(privateKey), to: dir.appendingPathComponent("spk_private.bin")) + try writeProtected(Data(spkId.utf8), to: dir.appendingPathComponent("spk_id.txt")) + } + + static func loadSPK(email: String) -> (Curve25519.KeyAgreement.PrivateKey?, String?) { + guard let dir = try? getKeyDir(email: email) else { return (nil, nil) } + let privPath = dir.appendingPathComponent("spk_private.bin") + let idPath = dir.appendingPathComponent("spk_id.txt") + + guard FileManager.default.fileExists(atPath: privPath.path), + let privData = try? Data(contentsOf: privPath), + let priv = try? X25519Crypto.loadPrivate(privData) else { + return (nil, nil) + } + let spkId = (try? String(data: Data(contentsOf: idPath), encoding: .utf8))?.trimmed ?? "" + return (priv, spkId) + } + + // MARK: - Previous SPK (Grace Period) + + static func savePrevSPK(email: String, privateKey: Curve25519.KeyAgreement.PrivateKey, spkId: String) throws { + let dir = try getKeyDir(email: email) + try writeProtected(X25519Crypto.serializePrivate(privateKey), to: dir.appendingPathComponent("prev_spk_private.bin")) + try writeProtected(Data(spkId.utf8), to: dir.appendingPathComponent("prev_spk_id.txt")) + } + + static func loadPrevSPK(email: String) -> (Curve25519.KeyAgreement.PrivateKey?, String?) { + guard let dir = try? getKeyDir(email: email) else { return (nil, nil) } + let privPath = dir.appendingPathComponent("prev_spk_private.bin") + let idPath = dir.appendingPathComponent("prev_spk_id.txt") + + guard FileManager.default.fileExists(atPath: privPath.path), + let privData = try? Data(contentsOf: privPath), + let priv = try? X25519Crypto.loadPrivate(privData) else { + return (nil, nil) + } + let spkId = (try? String(data: Data(contentsOf: idPath), encoding: .utf8))?.trimmed ?? "" + return (priv, spkId) + } + + // MARK: - One-Time Pre-Keys + + static func saveOPKPrivate(email: String, opkId: String, privateKey: Curve25519.KeyAgreement.PrivateKey) throws { + let dir = try getKeyDir(email: email).appendingPathComponent("opk_private") + try FileManager.default.createDirectory(at: dir, withIntermediateDirectories: true) + try writeProtected(X25519Crypto.serializePrivate(privateKey), to: dir.appendingPathComponent("\(opkId).bin")) + } + + static func loadOPKPrivate(email: String, opkId: String) -> Curve25519.KeyAgreement.PrivateKey? { + guard let dir = try? getKeyDir(email: email) else { return nil } + let path = dir.appendingPathComponent("opk_private").appendingPathComponent("\(opkId).bin") + guard let data = try? Data(contentsOf: path) else { return nil } + return try? X25519Crypto.loadPrivate(data) + } + + static func deleteOPKPrivate(email: String, opkId: String) { + guard let dir = try? getKeyDir(email: email) else { return } + let path = dir.appendingPathComponent("opk_private").appendingPathComponent("\(opkId).bin") + try? FileManager.default.removeItem(at: path) + } + + // MARK: - Device ID + + static func saveDeviceId(email: String, deviceId: String) throws { + let dir = try getKeyDir(email: email) + try writeProtected(Data(deviceId.utf8), to: dir.appendingPathComponent("device_id.txt")) + } + + static func loadDeviceId(email: String) -> String? { + guard let dir = try? getKeyDir(email: email) else { return nil } + let path = dir.appendingPathComponent("device_id.txt") + guard let data = try? Data(contentsOf: path) else { return nil } + let str = String(data: data, encoding: .utf8)?.trimmed + return (str?.isEmpty ?? true) ? nil : str + } + + // MARK: - Sessions (Double Ratchet) + + static func saveSession( + email: String, + peerUserId: String, + ratchet: DoubleRatchet, + localKey: Data? = nil, + peerDeviceId: String? = nil + ) throws { + let dir = try getKeyDir(email: email).appendingPathComponent("sessions") + try FileManager.default.createDirectory(at: dir, withIntermediateDirectories: true) + + let filename: String + if let deviceId = peerDeviceId { + filename = "\(peerUserId)_\(deviceId).bin" + } else { + filename = "\(peerUserId).bin" + } + + let exported = try ratchet.exportState() + guard let localKey = localKey else { + throw CryptoError.encryptionFailed("localKey required for session storage") + } + let data = try CryptoUtils.encryptLocal(exported, key: localKey) + try writeProtected(data, to: dir.appendingPathComponent(filename)) + } + + static func loadSession( + email: String, + peerUserId: String, + localKey: Data? = nil, + peerDeviceId: String? = nil + ) -> DoubleRatchet? { + guard let dir = try? getKeyDir(email: email) else { return nil } + let sessionsDir = dir.appendingPathComponent("sessions") + + let filename: String + if let deviceId = peerDeviceId { + filename = "\(peerUserId)_\(deviceId).bin" + } else { + filename = "\(peerUserId).bin" + } + + let path = sessionsDir.appendingPathComponent(filename) + return loadSessionFile(path, localKey: localKey) + } + + static func deleteSession(email: String, peerUserId: String, peerDeviceId: String? = nil) { + guard let dir = try? getKeyDir(email: email) else { return } + let sessionsDir = dir.appendingPathComponent("sessions") + + if let deviceId = peerDeviceId { + let path = sessionsDir.appendingPathComponent("\(peerUserId)_\(deviceId).bin") + try? FileManager.default.removeItem(at: path) + } else { + // Delete all sessions for this user + if let files = try? FileManager.default.contentsOfDirectory(atPath: sessionsDir.path) { + for file in files where file.hasPrefix(peerUserId) { + try? FileManager.default.removeItem(at: sessionsDir.appendingPathComponent(file)) + } + } + } + } + + private static func loadSessionFile(_ path: URL, localKey: Data?) -> DoubleRatchet? { + guard let raw = try? Data(contentsOf: path) else { return nil } + + if let localKey = localKey { + // Try encrypted first + if let decrypted = try? CryptoUtils.decryptLocal(raw, key: localKey) { + return try? DoubleRatchet.importState(decrypted) + } + // Migration: try plaintext, immediately re-encrypt + if let ratchet = try? DoubleRatchet.importState(raw) { + try? writeProtected(CryptoUtils.encryptLocal(try ratchet.exportState(), key: localKey), to: path) + return ratchet + } + // Corrupted — delete + try? FileManager.default.removeItem(at: path) + return nil + } + + // No localKey — refuse to load plaintext sessions + return nil + } + + // MARK: - Sender Keys + + static func saveSenderKeyState( + email: String, + convId: String, + state: SenderKeyState, + localKey: Data? = nil + ) throws { + let dir = try getKeyDir(email: email).appendingPathComponent("sender_keys") + try FileManager.default.createDirectory(at: dir, withIntermediateDirectories: true) + + guard let localKey = localKey else { + throw CryptoError.encryptionFailed("localKey required for sender key storage") + } + let data = try CryptoUtils.encryptLocal(state.exportState(), key: localKey) + try writeProtected(data, to: dir.appendingPathComponent("\(convId).bin")) + } + + static func loadSenderKeyState( + email: String, + convId: String, + localKey: Data? = nil + ) -> SenderKeyState? { + guard let dir = try? getKeyDir(email: email) else { return nil } + let path = dir.appendingPathComponent("sender_keys").appendingPathComponent("\(convId).bin") + guard let raw = try? Data(contentsOf: path) else { return nil } + + if let localKey = localKey { + if let decrypted = try? CryptoUtils.decryptLocal(raw, key: localKey) { + return try? SenderKeyState.importState(decrypted) + } + // Migration: try plaintext, immediately re-encrypt + if let state = try? SenderKeyState.importState(raw) { + try? writeProtected(CryptoUtils.encryptLocal(state.exportState(), key: localKey), to: path) + return state + } + try? FileManager.default.removeItem(at: path) + return nil + } + + return nil + } + + static func deleteSenderKeyState(email: String, convId: String) { + guard let dir = try? getKeyDir(email: email) else { return } + let path = dir.appendingPathComponent("sender_keys").appendingPathComponent("\(convId).bin") + try? FileManager.default.removeItem(at: path) + } + + // MARK: - Received Sender Keys + + static func saveRecvSenderKey( + email: String, + convId: String, + senderId: String, + senderDeviceId: String, + state: SenderKeyState, + localKey: Data? = nil + ) throws { + let dir = try getKeyDir(email: email).appendingPathComponent("sender_keys_recv") + try FileManager.default.createDirectory(at: dir, withIntermediateDirectories: true) + + guard let localKey = localKey else { + throw CryptoError.encryptionFailed("localKey required for sender key storage") + } + let data = try CryptoUtils.encryptLocal(state.exportState(), key: localKey) + try writeProtected(data, to: dir.appendingPathComponent("\(convId)_\(senderId)_\(senderDeviceId).bin")) + } + + static func loadRecvSenderKey( + email: String, + convId: String, + senderId: String, + senderDeviceId: String, + localKey: Data? = nil + ) -> SenderKeyState? { + guard let dir = try? getKeyDir(email: email) else { return nil } + let path = dir.appendingPathComponent("sender_keys_recv").appendingPathComponent("\(convId)_\(senderId)_\(senderDeviceId).bin") + guard let raw = try? Data(contentsOf: path) else { return nil } + + if let localKey = localKey { + if let decrypted = try? CryptoUtils.decryptLocal(raw, key: localKey) { + return try? SenderKeyState.importState(decrypted) + } + // Migration: try plaintext, immediately re-encrypt + if let state = try? SenderKeyState.importState(raw) { + try? writeProtected(CryptoUtils.encryptLocal(state.exportState(), key: localKey), to: path) + return state + } + try? FileManager.default.removeItem(at: path) + return nil + } + + return nil + } + + static func deleteRecvSenderKeys(email: String, convId: String) { + guard let dir = try? getKeyDir(email: email) else { return } + let recvDir = dir.appendingPathComponent("sender_keys_recv") + guard let files = try? FileManager.default.contentsOfDirectory(atPath: recvDir.path) else { return } + for file in files where file.hasPrefix(convId) { + try? FileManager.default.removeItem(at: recvDir.appendingPathComponent(file)) + } + } + + // MARK: - Favorites + + static func saveFavorites(email: String, favorites: Set, localKey: Data? = nil) throws { + let dir = try getKeyDir(email: email) + let jsonData = try JSONSerialization.data(withJSONObject: Array(favorites)) + let dataToWrite: Data + if let localKey = localKey { + dataToWrite = try CryptoUtils.encryptLocal(jsonData, key: localKey) + } else { + dataToWrite = jsonData + } + try writeProtected(dataToWrite, to: dir.appendingPathComponent("favorites.json")) + } + + static func loadFavorites(email: String, localKey: Data? = nil) -> Set { + guard let dir = try? getKeyDir(email: email) else { return [] } + let path = dir.appendingPathComponent("favorites.json") + guard let raw = try? Data(contentsOf: path) else { return [] } + let jsonData: Data + if let localKey = localKey { + if let decrypted = try? CryptoUtils.decryptLocal(raw, key: localKey) { + jsonData = decrypted + } else { + jsonData = raw // migration fallback + } + } else { + jsonData = raw + } + guard let array = try? JSONSerialization.jsonObject(with: jsonData) as? [String] else { + return [] + } + return Set(array) + } + + // MARK: - TOFU Identity Key Registry + + static func saveKnownIdentityKeys(email: String, keys: [String: [String: String]], localKey: Data?) throws { + let dir = try getKeyDir(email: email) + let jsonObj: [String: Any] = ["version": 1, "keys": keys] + let jsonData = try JSONSerialization.data(withJSONObject: jsonObj) + guard let localKey = localKey else { + try writeProtected(jsonData, to: dir.appendingPathComponent("known_identity_keys.bin")) + return + } + let encrypted = try CryptoUtils.encryptLocal(jsonData, key: localKey) + try writeProtected(encrypted, to: dir.appendingPathComponent("known_identity_keys.bin")) + } + + static func loadKnownIdentityKeys(email: String, localKey: Data?) -> [String: [String: String]] { + guard let dir = try? getKeyDir(email: email) else { return [:] } + let path = dir.appendingPathComponent("known_identity_keys.bin") + guard let raw = try? Data(contentsOf: path) else { return [:] } + do { + let jsonData: Data + if let localKey = localKey { + jsonData = try CryptoUtils.decryptLocal(raw, key: localKey) + } else { + jsonData = raw + } + guard let obj = try JSONSerialization.jsonObject(with: jsonData) as? [String: Any], + let keys = obj["keys"] as? [String: [String: String]] else { return [:] } + return keys + } catch { + return [:] + } + } + + // MARK: - Verified Contacts + + static func saveVerifiedContacts(email: String, contacts: [String: [String: String]], localKey: Data?) throws { + let dir = try getKeyDir(email: email) + let jsonObj: [String: Any] = ["version": 1, "contacts": contacts] + let jsonData = try JSONSerialization.data(withJSONObject: jsonObj) + guard let localKey = localKey else { + try writeProtected(jsonData, to: dir.appendingPathComponent("verified_contacts.bin")) + return + } + let encrypted = try CryptoUtils.encryptLocal(jsonData, key: localKey) + try writeProtected(encrypted, to: dir.appendingPathComponent("verified_contacts.bin")) + } + + static func loadVerifiedContacts(email: String, localKey: Data?) -> [String: [String: String]] { + guard let dir = try? getKeyDir(email: email) else { return [:] } + let path = dir.appendingPathComponent("verified_contacts.bin") + guard let raw = try? Data(contentsOf: path) else { return [:] } + do { + let jsonData: Data + if let localKey = localKey { + jsonData = try CryptoUtils.decryptLocal(raw, key: localKey) + } else { + jsonData = raw + } + guard let obj = try JSONSerialization.jsonObject(with: jsonData) as? [String: Any], + let contacts = obj["contacts"] as? [String: [String: String]] else { return [:] } + return contacts + } catch { + return [:] + } + } + + // MARK: - Helpers + + private static func writeProtected(_ data: Data, to url: URL) throws { + try data.write(to: url, options: .completeFileProtection) + } +} diff --git a/ios_client 0.8.5/Kecalek/Core/KeychainService.swift b/ios_client 0.8.5/Kecalek/Core/KeychainService.swift new file mode 100644 index 0000000..b5e78fb --- /dev/null +++ b/ios_client 0.8.5/Kecalek/Core/KeychainService.swift @@ -0,0 +1,132 @@ +import Foundation +import Security +import LocalAuthentication + +enum KeychainService { + private static let service = "com.encryptedchat.credentials" + private static let account = "userCredentials" + + struct Credentials: Codable { + let email: String + let password: String + let host: String + let port: UInt16 + } + + /// Check if saved credentials exist without triggering biometric prompt. + static func hasSavedCredentials() -> Bool { + let context = LAContext() + context.interactionNotAllowed = true + + let query: [String: Any] = [ + kSecClass as String: kSecClassGenericPassword, + kSecAttrService as String: service, + kSecAttrAccount as String: account, + kSecMatchLimit as String: kSecMatchLimitOne, + kSecReturnAttributes as String: true, + kSecUseAuthenticationContext as String: context + ] + var result: AnyObject? + let status = SecItemCopyMatching(query as CFDictionary, &result) + // errSecInteractionNotAllowed means item exists but needs biometric + return status == errSecSuccess || status == errSecInteractionNotAllowed + } + + /// Save credentials to Keychain with biometric protection. + static func saveCredentials(email: String, password: String, host: String, port: UInt16) throws { + // Delete any existing entry first + deleteCredentials() + + let credentials = Credentials(email: email, password: password, host: host, port: port) + let data = try JSONEncoder().encode(credentials) + + var accessError: Unmanaged? + guard let accessControl = SecAccessControlCreateWithFlags( + kCFAllocatorDefault, + kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly, + .biometryAny, + &accessError + ) else { + throw KeychainError.accessControlCreationFailed + } + + let query: [String: Any] = [ + kSecClass as String: kSecClassGenericPassword, + kSecAttrService as String: service, + kSecAttrAccount as String: account, + kSecValueData as String: data, + kSecAttrAccessControl as String: accessControl + ] + + let status = SecItemAdd(query as CFDictionary, nil) + guard status == errSecSuccess else { + throw KeychainError.saveFailed(status) + } + } + + /// Load credentials from Keychain. Triggers biometric prompt. + static func loadCredentials() throws -> Credentials { + let context = LAContext() + context.localizedReason = "Unlock to log in" + + let query: [String: Any] = [ + kSecClass as String: kSecClassGenericPassword, + kSecAttrService as String: service, + kSecAttrAccount as String: account, + kSecReturnData as String: true, + kSecMatchLimit as String: kSecMatchLimitOne, + kSecUseAuthenticationContext as String: context + ] + + var result: AnyObject? + let status = SecItemCopyMatching(query as CFDictionary, &result) + + guard status == errSecSuccess, let data = result as? Data else { + if status == errSecUserCanceled || status == errSecAuthFailed { + throw KeychainError.biometricFailed + } + throw KeychainError.loadFailed(status) + } + + return try JSONDecoder().decode(Credentials.self, from: data) + } + + /// Delete stored credentials from Keychain. + @discardableResult + static func deleteCredentials() -> Bool { + let query: [String: Any] = [ + kSecClass as String: kSecClassGenericPassword, + kSecAttrService as String: service, + kSecAttrAccount as String: account + ] + let status = SecItemDelete(query as CFDictionary) + return status == errSecSuccess || status == errSecItemNotFound + } + + /// Check if biometric authentication is available on this device. + static func isBiometricAvailable() -> Bool { + let context = LAContext() + var error: NSError? + return context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) + } + + enum KeychainError: LocalizedError { + case accessControlCreationFailed + case saveFailed(OSStatus) + case loadFailed(OSStatus) + case biometricFailed + + var errorDescription: String? { + switch self { + case .accessControlCreationFailed: + return "Failed to create biometric access control" + case .saveFailed(let status): + return "Failed to save credentials (error \(status))" + case .loadFailed(let status): + return "Failed to load credentials (error \(status))" + case .biometricFailed: + return "Biometric authentication failed or was cancelled" + } + } + } +} diff --git a/ios_client 0.8.5/Kecalek/Core/MessageCache.swift b/ios_client 0.8.5/Kecalek/Core/MessageCache.swift new file mode 100644 index 0000000..792d9ab --- /dev/null +++ b/ios_client 0.8.5/Kecalek/Core/MessageCache.swift @@ -0,0 +1,200 @@ +import Foundation + +/// Encrypted local message cache. +/// Matches Python: chat_core.py message cache (message_cache/{conv_id}.json) +enum MessageCache { + + /// Save messages for a conversation (encrypted with local storage key) + static func save(email: String, convId: String, messages: [[String: Any]], cacheKey: Data?) throws { + let dir = try KeyStorage.getKeyDir(email: email).appendingPathComponent("message_cache") + try FileManager.default.createDirectory(at: dir, withIntermediateDirectories: true) + + let jsonData = try JSONSerialization.data(withJSONObject: messages) + + guard let cacheKey = cacheKey else { + return // Refuse to save plaintext message cache + } + let dataToWrite = try CryptoUtils.encryptLocal(jsonData, key: cacheKey) + try dataToWrite.write(to: dir.appendingPathComponent("\(convId).json"), options: .completeFileProtection) + } + + /// Load messages for a conversation + static func load(email: String, convId: String, cacheKey: Data?) -> [[String: Any]]? { + guard let dir = try? KeyStorage.getKeyDir(email: email) else { return nil } + let path = dir.appendingPathComponent("message_cache").appendingPathComponent("\(convId).json") + guard let raw = try? Data(contentsOf: path) else { return nil } + + let jsonData: Data + if let cacheKey = cacheKey { + if let decrypted = try? CryptoUtils.decryptLocal(raw, key: cacheKey) { + jsonData = decrypted + } else if let parsed = try? JSONSerialization.jsonObject(with: raw) as? [[String: Any]] { + // Migration: re-encrypt plaintext cache and return + try? save(email: email, convId: convId, messages: parsed, cacheKey: cacheKey) + return parsed + } else { + // Corrupted — delete stale cache + try? FileManager.default.removeItem(at: path) + return nil + } + } else { + jsonData = raw + } + + return try? JSONSerialization.jsonObject(with: jsonData) as? [[String: Any]] + } + + /// Search messages in a conversation + static func search(email: String, convId: String, query: String, cacheKey: Data?) -> [[String: Any]] { + guard let messages = load(email: email, convId: convId, cacheKey: cacheKey) else { + return [] + } + let lowerQuery = query.lowercased() + return messages.filter { msg in + if let text = msg["text"] as? String, text.lowercased().contains(lowerQuery) { + return true + } + return false + } + } + + /// Delete cache for a conversation + static func delete(email: String, convId: String) { + guard let dir = try? KeyStorage.getKeyDir(email: email) else { return } + let path = dir.appendingPathComponent("message_cache").appendingPathComponent("\(convId).json") + try? FileManager.default.removeItem(at: path) + } + + // MARK: - Per-Message Cache (for Double Ratchet - messages can only be decrypted once) + + /// Cache a decrypted message by its ID + static func cacheDecryptedMessage(email: String, convId: String, messageId: String, plaintext: Data, cacheKey: Data?) { + guard let dir = try? KeyStorage.getKeyDir(email: email) else { return } + let cacheDir = dir.appendingPathComponent("decrypted_messages").appendingPathComponent(convId) + try? FileManager.default.createDirectory(at: cacheDir, withIntermediateDirectories: true) + + let path = cacheDir.appendingPathComponent("\(messageId).bin") + do { + guard let cacheKey = cacheKey else { return } // Refuse plaintext + let dataToWrite = try CryptoUtils.encryptLocal(plaintext, key: cacheKey) + try dataToWrite.write(to: path, options: .completeFileProtection) + } catch { + #if DEBUG + print("DEBUG MessageCache: failed to cache message \(messageId): \(error)") + #endif + } + } + + /// Load all cached decrypted messages for a conversation. + /// Returns array of (messageId, plaintext) tuples. + static func loadAllCachedMessages(email: String, convId: String, cacheKey: Data?) -> [(String, Data)] { + guard let dir = try? KeyStorage.getKeyDir(email: email) else { return [] } + let cacheDir = dir.appendingPathComponent("decrypted_messages").appendingPathComponent(convId) + guard let files = try? FileManager.default.contentsOfDirectory(at: cacheDir, includingPropertiesForKeys: nil) else { return [] } + + var result: [(String, Data)] = [] + for file in files where file.pathExtension == "bin" { + let messageId = file.deletingPathExtension().lastPathComponent + guard let raw = try? Data(contentsOf: file) else { continue } + if let cacheKey = cacheKey, + let decrypted = try? CryptoUtils.decryptLocal(raw, key: cacheKey) { + result.append((messageId, decrypted)) + } else if cacheKey == nil { + result.append((messageId, raw)) + } + } + return result + } + + /// Get a cached decrypted message by ID + static func getCachedMessage(email: String, convId: String, messageId: String, cacheKey: Data?) -> Data? { + guard let dir = try? KeyStorage.getKeyDir(email: email) else { return nil } + let path = dir.appendingPathComponent("decrypted_messages").appendingPathComponent(convId).appendingPathComponent("\(messageId).bin") + guard let raw = try? Data(contentsOf: path) else { return nil } + + if let cacheKey = cacheKey { + if let decrypted = try? CryptoUtils.decryptLocal(raw, key: cacheKey) { + return decrypted + } + // Migration: try as plaintext, re-encrypt + if let _ = try? JSONSerialization.jsonObject(with: raw) { + cacheDecryptedMessage(email: email, convId: convId, messageId: messageId, plaintext: raw, cacheKey: cacheKey) + return raw + } + // Corrupted — delete + try? FileManager.default.removeItem(at: path) + return nil + } + return raw + } + + // MARK: - Conversation List Cache + + /// Save conversation list to disk (encrypted with local key) + static func saveConversations(email: String, conversations: [Conversation], cacheKey: Data?) { + guard let cacheKey = cacheKey else { return } + guard let dir = try? KeyStorage.getKeyDir(email: email) else { return } + do { + let jsonData = try JSONEncoder().encode(conversations) + let encrypted = try CryptoUtils.encryptLocal(jsonData, key: cacheKey) + try encrypted.write(to: dir.appendingPathComponent("conversation_cache.json"), options: .completeFileProtection) + } catch { + #if DEBUG + print("DEBUG MessageCache: failed to save conversations: \(error)") + #endif + } + } + + /// Load conversation list from disk + static func loadConversations(email: String, cacheKey: Data?) -> [Conversation]? { + guard let cacheKey = cacheKey else { return nil } + guard let dir = try? KeyStorage.getKeyDir(email: email) else { return nil } + let path = dir.appendingPathComponent("conversation_cache.json") + guard let raw = try? Data(contentsOf: path) else { return nil } + guard let decrypted = try? CryptoUtils.decryptLocal(raw, key: cacheKey) else { return nil } + return try? JSONDecoder().decode([Conversation].self, from: decrypted) + } + + // MARK: - Avatar Disk Cache + + /// Save avatar data to disk (encrypted with local key) + static func saveAvatar(email: String, key: String, data: Data, cacheKey: Data?) { + guard let cacheKey = cacheKey else { return } + guard let dir = try? KeyStorage.getKeyDir(email: email) else { return } + let cacheDir = dir.appendingPathComponent("avatar_cache") + try? FileManager.default.createDirectory(at: cacheDir, withIntermediateDirectories: true) + do { + let encrypted = try CryptoUtils.encryptLocal(data, key: cacheKey) + try encrypted.write(to: cacheDir.appendingPathComponent("\(key).dat"), options: .completeFileProtection) + } catch { + #if DEBUG + print("DEBUG MessageCache: failed to save avatar \(key): \(error)") + #endif + } + } + + /// Load avatar data from disk + static func loadAvatar(email: String, key: String, cacheKey: Data?) -> Data? { + guard let cacheKey = cacheKey else { return nil } + guard let dir = try? KeyStorage.getKeyDir(email: email) else { return nil } + let path = dir.appendingPathComponent("avatar_cache").appendingPathComponent("\(key).dat") + guard let raw = try? Data(contentsOf: path) else { return nil } + return try? CryptoUtils.decryptLocal(raw, key: cacheKey) + } + + /// Load all cached avatars from disk + static func loadAllAvatars(email: String, cacheKey: Data?) -> [String: Data] { + guard let cacheKey = cacheKey else { return [:] } + guard let dir = try? KeyStorage.getKeyDir(email: email) else { return [:] } + let cacheDir = dir.appendingPathComponent("avatar_cache") + guard let files = try? FileManager.default.contentsOfDirectory(at: cacheDir, includingPropertiesForKeys: nil) else { return [:] } + var result: [String: Data] = [:] + for file in files where file.pathExtension == "dat" { + let key = file.deletingPathExtension().lastPathComponent + guard let raw = try? Data(contentsOf: file), + let decrypted = try? CryptoUtils.decryptLocal(raw, key: cacheKey) else { continue } + result[key] = decrypted + } + return result + } +} diff --git a/ios_client 0.8.5/Kecalek/Crypto/ContactVerification.swift b/ios_client 0.8.5/Kecalek/Crypto/ContactVerification.swift new file mode 100644 index 0000000..65ca40b --- /dev/null +++ b/ios_client 0.8.5/Kecalek/Crypto/ContactVerification.swift @@ -0,0 +1,162 @@ +import Foundation +import CryptoKit + +/// Contact key verification: fingerprints, safety numbers, QR codes. +/// Matches Python: crypto_utils.py compute_fingerprint, compute_safety_number, etc. +enum ContactVerification { + + /// Version byte for fingerprint computation (Signal's NumericFingerprint). + private static let fingerprintVersion: UInt16 = 0 + + /// Number of SHA-512 iterations for fingerprint computation. + private static let fingerprintIterations = 5200 + + // MARK: - Fingerprint + + /// Compute a 32-byte fingerprint for a user's identity key. + /// + /// Uses iterated SHA-512 (Signal's NumericFingerprint algorithm). + /// Seed: version(2B big-endian) + identity_key(32B) + user_id(UTF-8). + /// Each iteration: SHA-512(previous_hash + identity_key). + /// Output: first 32 bytes of final hash. + static func computeFingerprint(userId: String, identityKey: Data, iterations: Int = fingerprintIterations) -> Data { + let versionBytes = fingerprintVersion.bigEndianData + var data = versionBytes + identityKey + Data(userId.utf8) + for _ in 0.. String { + var groups: [String] = [] + for i in 0..<6 { + let start = i * 5 + let end = min(start + 5, fpBytes.count) + let slice = fpBytes[fpBytes.startIndex + start ..< fpBytes.startIndex + end] + let num = bigEndianUInt64(slice) % 100000 + groups.append(String(format: "%05d", num)) + } + return groups[0..<3].joined(separator: " ") + "\n" + groups[3..<6].joined(separator: " ") + } + + // MARK: - Safety Number + + /// Compute a 60-digit safety number for a pair of users. + /// + /// Both users see the same number regardless of who computes it. + /// Lower user_id's fingerprint comes first (deterministic ordering). + /// Output: 12 groups of 5 digits, formatted as 3 lines of 4 groups. + static func computeSafetyNumber( + myUserId: String, myIdentityKey: Data, + theirUserId: String, theirIdentityKey: Data + ) -> String { + let fpMine = computeFingerprint(userId: myUserId, identityKey: myIdentityKey) + let fpTheirs = computeFingerprint(userId: theirUserId, identityKey: theirIdentityKey) + + let combined: Data + if myUserId < theirUserId { + combined = fpMine + fpTheirs + } else { + combined = fpTheirs + fpMine + } + + // 64 bytes -> 12 groups of 5 digits + var groups: [String] = [] + for i in 0..<12 { + let start = i * 5 + let end = min(start + 5, combined.count) + let slice = combined[combined.startIndex + start ..< combined.startIndex + end] + let num = bigEndianUInt64(slice) % 100000 + groups.append(String(format: "%05d", num)) + } + + return [ + groups[0..<4].joined(separator: " "), + groups[4..<8].joined(separator: " "), + groups[8..<12].joined(separator: " "), + ].joined(separator: "\n") + } + + // MARK: - QR Code + + /// Encode user identity for QR code verification. + /// + /// Format: version(1B=0x01) + uid_len(1B) + uid(UTF-8) + identity_key(32B). + static func encodeVerificationQR(userId: String, identityKey: Data) -> Data { + let uidBytes = Data(userId.utf8) + var data = Data([0x01, UInt8(uidBytes.count)]) + data.append(uidBytes) + data.append(identityKey) + return data + } + + /// Decode QR code verification payload. + /// + /// Returns (userId, identityKey). + /// Throws on invalid format. + static func decodeVerificationQR(_ data: Data) throws -> (userId: String, identityKey: Data) { + guard data.count >= 3 else { + throw VerificationError.qrDataTooShort + } + guard data[data.startIndex] == 0x01 else { + throw VerificationError.unknownQRVersion(data[data.startIndex]) + } + let uidLen = Int(data[data.startIndex + 1]) + guard data.count >= 2 + uidLen + 32 else { + throw VerificationError.qrDataTruncated + } + let uidData = data[data.startIndex + 2 ..< data.startIndex + 2 + uidLen] + guard let userId = String(data: uidData, encoding: .utf8) else { + throw VerificationError.invalidUTF8 + } + let identityKey = Data(data[data.startIndex + 2 + uidLen ..< data.startIndex + 2 + uidLen + 32]) + return (userId, identityKey) + } + + // MARK: - Helpers + + /// Convert up to 8 bytes to UInt64, big-endian. + private static func bigEndianUInt64(_ data: Data) -> UInt64 { + var result: UInt64 = 0 + for byte in data { + result = result << 8 | UInt64(byte) + } + return result + } +} + +// MARK: - UInt16 Big-Endian + +private extension UInt16 { + var bigEndianData: Data { + var value = self.bigEndian + return Data(bytes: &value, count: 2) + } +} + +// MARK: - Verification Errors + +enum VerificationError: Error, LocalizedError { + case qrDataTooShort + case unknownQRVersion(UInt8) + case qrDataTruncated + case invalidUTF8 + + var errorDescription: String? { + switch self { + case .qrDataTooShort: return "QR data too short" + case .unknownQRVersion(let v): return "Unknown QR version: \(v)" + case .qrDataTruncated: return "QR data truncated" + case .invalidUTF8: return "Invalid UTF-8 in QR data" + } + } +} diff --git a/ios_client 0.8.5/Kecalek/Crypto/CryptoErrors.swift b/ios_client 0.8.5/Kecalek/Crypto/CryptoErrors.swift new file mode 100644 index 0000000..bf45320 --- /dev/null +++ b/ios_client 0.8.5/Kecalek/Crypto/CryptoErrors.swift @@ -0,0 +1,95 @@ +import Foundation + +enum CryptoError: Error, LocalizedError { + case invalidBase64 + case invalidHex + case invalidKeyData(String) + case invalidSignature + case signatureVerificationFailed + case encryptionFailed(String) + case decryptionFailed(String) + case invalidECP1Format + case pbkdf2Failed + case rsaKeyGenerationFailed + case rsaOperationFailed(String) + case x3dhFailed(String) + case ratchetError(String) + case senderKeyError(String) + case maxSkipExceeded + case duplicateMessage + case invalidHeader(String) + case stateImportFailed(String) + case keyConversionFailed(String) + + var errorDescription: String? { + switch self { + case .invalidBase64: return "Invalid base64 encoding" + case .invalidHex: return "Invalid hex encoding" + case .invalidKeyData(let msg): return "Invalid key data: \(msg)" + case .invalidSignature: return "Invalid signature format" + case .signatureVerificationFailed: return "Signature verification failed" + case .encryptionFailed(let msg): return "Encryption failed: \(msg)" + case .decryptionFailed(let msg): return "Decryption failed: \(msg)" + case .invalidECP1Format: return "Invalid ECP1 key format" + case .pbkdf2Failed: return "PBKDF2 key derivation failed" + case .rsaKeyGenerationFailed: return "RSA key generation failed" + case .rsaOperationFailed(let msg): return "RSA operation failed: \(msg)" + case .x3dhFailed(let msg): return "X3DH failed: \(msg)" + case .ratchetError(let msg): return "Ratchet error: \(msg)" + case .senderKeyError(let msg): return "Sender key error: \(msg)" + case .maxSkipExceeded: return "Maximum message skip exceeded" + case .duplicateMessage: return "Duplicate message detected" + case .invalidHeader(let msg): return "Invalid header: \(msg)" + case .stateImportFailed(let msg): return "State import failed: \(msg)" + case .keyConversionFailed(let msg): return "Key conversion failed: \(msg)" + } + } +} + +enum NetworkError: Error, LocalizedError { + case notConnected + case connectionFailed(String) + case timeout + case serverError(String) + case protocolError(String) + case messageTooLarge + case invalidResponse(String) + case authenticationFailed(String) + case alreadyConnected + + var errorDescription: String? { + switch self { + case .notConnected: return "Not connected to server" + case .connectionFailed(let msg): return "Connection failed: \(msg)" + case .timeout: return "Request timed out" + case .serverError(let msg): return "Server error: \(msg)" + case .protocolError(let msg): return "Protocol error: \(msg)" + case .messageTooLarge: return "Message exceeds maximum size" + case .invalidResponse(let msg): return "Invalid response: \(msg)" + case .authenticationFailed(let msg): return "Authentication failed: \(msg)" + case .alreadyConnected: return "Already connected" + } + } +} + +enum ChatError: Error, LocalizedError { + case notLoggedIn + case conversationNotFound + case membershipRequired + case permissionDenied(String) + case operationFailed(String) + case fileError(String) + case invalidData(String) + + var errorDescription: String? { + switch self { + case .notLoggedIn: return "Not logged in" + case .conversationNotFound: return "Conversation not found" + case .membershipRequired: return "Must be a member of this conversation" + case .permissionDenied(let msg): return "Permission denied: \(msg)" + case .operationFailed(let msg): return "Operation failed: \(msg)" + case .fileError(let msg): return "File error: \(msg)" + case .invalidData(let msg): return "Invalid data: \(msg)" + } + } +} diff --git a/ios_client 0.8.5/Kecalek/Crypto/CryptoUtils.swift b/ios_client 0.8.5/Kecalek/Crypto/CryptoUtils.swift new file mode 100644 index 0000000..60e5abb --- /dev/null +++ b/ios_client 0.8.5/Kecalek/Crypto/CryptoUtils.swift @@ -0,0 +1,196 @@ +import Foundation +import CryptoKit + +/// Core cryptographic utilities: AES-GCM, HKDF, KDF helpers +enum CryptoUtils { + + // MARK: - AES-256-GCM + + /// Encrypt with AES-256-GCM. Returns (key, nonce, ciphertext, tag) — all as Data. + /// If key is nil, generates a random 256-bit key. + /// Matches Python: aes_encrypt(plaintext, key=None) + static func aesEncrypt(_ plaintext: Data, key: Data? = nil) throws -> (key: Data, nonce: Data, ciphertext: Data, tag: Data) { + let keyData = key ?? Data.randomBytes(32) + let symmetricKey = SymmetricKey(data: keyData) + let nonceData = Data.randomBytes(12) + let gcmNonce = try AES.GCM.Nonce(data: nonceData) + + let sealedBox = try AES.GCM.seal(plaintext, using: symmetricKey, nonce: gcmNonce) + + return ( + key: keyData, + nonce: nonceData, + ciphertext: Data(sealedBox.ciphertext), + tag: Data(sealedBox.tag) + ) + } + + /// Decrypt with AES-256-GCM. + /// Matches Python: aes_decrypt(key, nonce, ciphertext, tag) + static func aesDecrypt(key: Data, nonce: Data, ciphertext: Data, tag: Data) throws -> Data { + let symmetricKey = SymmetricKey(data: key) + let gcmNonce = try AES.GCM.Nonce(data: nonce) + + let sealedBox = try AES.GCM.SealedBox( + nonce: gcmNonce, + ciphertext: ciphertext, + tag: tag + ) + + do { + return try AES.GCM.open(sealedBox, using: symmetricKey) + } catch { + throw CryptoError.decryptionFailed("AES-GCM decryption failed") + } + } + + /// Encrypt with AES-256-GCM using AAD. Returns ciphertext with tag appended. + /// Used by Double Ratchet and Sender Keys. + static func aesGcmEncrypt(_ plaintext: Data, key: Data, nonce: Data, aad: Data) throws -> Data { + let symmetricKey = SymmetricKey(data: key) + let gcmNonce = try AES.GCM.Nonce(data: nonce) + + let sealedBox = try AES.GCM.seal( + plaintext, + using: symmetricKey, + nonce: gcmNonce, + authenticating: aad + ) + + // Return ciphertext + tag concatenated (matches Python AESGCM.encrypt) + return Data(sealedBox.ciphertext) + Data(sealedBox.tag) + } + + /// Decrypt AES-256-GCM with AAD. Input ciphertext has tag appended (last 16 bytes). + static func aesGcmDecrypt(_ ctWithTag: Data, key: Data, nonce: Data, aad: Data) throws -> Data { + guard ctWithTag.count >= 16 else { + throw CryptoError.decryptionFailed("Ciphertext too short") + } + + let ct = ctWithTag.prefix(ctWithTag.count - 16) + let tag = ctWithTag.suffix(16) + + let symmetricKey = SymmetricKey(data: key) + let gcmNonce = try AES.GCM.Nonce(data: nonce) + + let sealedBox = try AES.GCM.SealedBox( + nonce: gcmNonce, + ciphertext: ct, + tag: tag + ) + + do { + return try AES.GCM.open(sealedBox, using: symmetricKey, authenticating: aad) + } catch { + throw CryptoError.decryptionFailed("AES-GCM decryption with AAD failed") + } + } + + // MARK: - HKDF + + /// HKDF-SHA256 key derivation. + /// Matches Python: hkdf_derive(input_key, salt, info, length=32) + static func hkdfDerive(inputKey: Data, salt: Data, info: Data, length: Int = 32) -> Data { + let symmetricKey = SymmetricKey(data: inputKey) + let derived = HKDF.deriveKey( + inputKeyMaterial: symmetricKey, + salt: salt, + info: info, + outputByteCount: length + ) + return derived.withUnsafeBytes { Data($0) } + } + + // MARK: - KDF for Double Ratchet + + /// Root key KDF. Returns (newRootKey, chainKey). + /// HKDF with rootKey as salt and DH output as input. Derives 64 bytes, split in half. + /// Matches Python: kdf_rk(root_key, dh_output) + static func kdfRK(rootKey: Data, dhOutput: Data) -> (newRootKey: Data, chainKey: Data) { + let derived = hkdfDerive( + inputKey: dhOutput, + salt: rootKey, + info: Data(Constants.rootKeyInfo.utf8), + length: 64 + ) + return (derived.prefix(32), Data(derived.suffix(32))) + } + + /// Chain key KDF. Returns (newChainKey, messageKey). + /// HMAC-SHA256: messageKey = HMAC(chainKey, 0x01), newChainKey = HMAC(chainKey, 0x02) + /// Matches Python: kdf_ck(chain_key) + static func kdfCK(chainKey: Data) -> (newChainKey: Data, messageKey: Data) { + let symmetricKey = SymmetricKey(data: chainKey) + let messageKey = Data(HMAC.authenticationCode(for: Data([0x01]), using: symmetricKey)) + let newChainKey = Data(HMAC.authenticationCode(for: Data([0x02]), using: symmetricKey)) + return (newChainKey, messageKey) + } + + // MARK: - Self-Encryption Key + + /// Derive static AES-256 key from identity key for self-encrypted message copies. + /// Matches Python: derive_self_encryption_key(identity_private) + static func deriveSelfEncryptionKey(identityPrivateRaw: Data) -> Data { + hkdfDerive( + inputKey: identityPrivateRaw, + salt: Data(Constants.selfEncryptionSalt.utf8), + info: Data(Constants.selfEncryptionInfo.utf8), + length: 32 + ) + } + + // MARK: - Local Storage Key + + /// Derive AES-256 key for encrypting local session/sender key files. + /// Matches Python: derive_local_storage_key(identity_private) + static func deriveLocalStorageKey(identityPrivateRaw: Data) -> Data { + hkdfDerive( + inputKey: identityPrivateRaw, + salt: Data(Constants.localStorageSalt.utf8), + info: Data(Constants.localStorageInfo.utf8), + length: 32 + ) + } + + // MARK: - Local File Encryption + + /// Encrypt data for local storage. Format: nonce(12) + tag(16) + ciphertext + /// Matches Python: _encrypt_local(data, key) + static func encryptLocal(_ data: Data, key: Data) throws -> Data { + let symmetricKey = SymmetricKey(data: key) + let sealedBox = try AES.GCM.seal(data, using: symmetricKey) + + var result = Data() + result.append(Data(sealedBox.nonce)) // 12 bytes + result.append(Data(sealedBox.tag)) // 16 bytes + result.append(Data(sealedBox.ciphertext)) // N bytes + return result + } + + /// Decrypt locally stored data. Format: nonce(12) + tag(16) + ciphertext + /// Matches Python: _decrypt_local(raw, key) + static func decryptLocal(_ raw: Data, key: Data) throws -> Data { + guard raw.count >= 28 else { // 12 + 16 minimum + throw CryptoError.decryptionFailed("Local encrypted data too short") + } + + let nonce = raw[0..<12] + let tag = raw[12..<28] + let ct = raw[28...] + + let symmetricKey = SymmetricKey(data: key) + let gcmNonce = try AES.GCM.Nonce(data: nonce) + + let sealedBox = try AES.GCM.SealedBox( + nonce: gcmNonce, + ciphertext: ct, + tag: tag + ) + + do { + return try AES.GCM.open(sealedBox, using: symmetricKey) + } catch { + throw CryptoError.decryptionFailed("Local storage decryption failed") + } + } +} diff --git a/ios_client 0.8.5/Kecalek/Crypto/DoubleRatchet.swift b/ios_client 0.8.5/Kecalek/Crypto/DoubleRatchet.swift new file mode 100644 index 0000000..7609498 --- /dev/null +++ b/ios_client 0.8.5/Kecalek/Crypto/DoubleRatchet.swift @@ -0,0 +1,393 @@ +import Foundation +import CryptoKit + +/// Ratchet header sent with each message +struct RatchetHeader { + let dhPub: Data // sender's current ratchet public key (32 bytes) + let n: Int // message number in current sending chain + let pn: Int // number of messages in previous sending chain + + /// Serialize header to JSON bytes for use as AAD. + /// Matches Python: RatchetHeader.serialize() + /// IMPORTANT: Must match Python's json.dumps() format exactly (with spaces after : and ,) + func serialize() -> Data { + // Python json.dumps produces: {"dh_pub": "...", "n": 0, "pn": 0} + // Note the spaces after colons and commas - this is critical for AAD matching + let jsonString = "{\"dh_pub\": \"\(dhPub.hexString)\", \"n\": \(n), \"pn\": \(pn)}" + return jsonString.data(using: .utf8)! + } + + /// Convert to dictionary for protocol. + /// Matches Python: RatchetHeader.to_dict() + func toDict() -> [String: Any] { + [ + "dh_pub": dhPub.hexString, + "n": n, + "pn": pn, + ] + } + + /// Parse from dictionary. + /// Matches Python: RatchetHeader.from_dict(d) + static func fromDict(_ d: [String: Any]) throws -> RatchetHeader { + guard let dhPubHex = d["dh_pub"] as? String, + let dhPub = Data(hexString: dhPubHex), + let n = d["n"] as? Int, + let pn = d["pn"] as? Int else { + throw CryptoError.invalidHeader("Missing or invalid header fields") + } + return RatchetHeader(dhPub: dhPub, n: n, pn: pn) + } +} + +/// Signal Double Ratchet implementation. +/// Matches Python: DoubleRatchet class in crypto_utils.py +class DoubleRatchet { + + private(set) var dhPair: (privateKey: Curve25519.KeyAgreement.PrivateKey, + publicKey: Curve25519.KeyAgreement.PublicKey)? + private(set) var dhRemote: Curve25519.KeyAgreement.PublicKey? + private(set) var rootKey: Data = Data() + private(set) var sendChainKey: Data? + private(set) var recvChainKey: Data? + private(set) var sendN: Int = 0 + private(set) var recvN: Int = 0 + private(set) var prevSendN: Int = 0 + // Skipped message keys: "dh_pub_hex:n" → message_key + private(set) var skipped: [String: Data] = [:] + + /// Attached X3DH header — set when creating a new session, consumed on first send. + /// Matches Python: ratchet._x3dh_header + var x3dhHeader: [String: Any]? + + init() {} + + // MARK: - Initialization + + /// Initialize as initiator (Alice) after X3DH. + /// Matches Python: DoubleRatchet.init_alice(shared_secret, bob_spk_pub) + static func initAlice(sharedSecret: Data, bobSpkPub: Curve25519.KeyAgreement.PublicKey) throws -> DoubleRatchet { + let ratchet = DoubleRatchet() + let (priv, pub) = X25519Crypto.generateKeypair() + ratchet.dhPair = (priv, pub) + ratchet.dhRemote = bobSpkPub + + // Debug: print ratchet inputs (matching Python _dh_ratchet) + #if DEBUG + print("DEBUG initAlice: shared_secret (root_key) = \(sharedSecret.hexString)") + print("DEBUG initAlice: my_dh_pub = \(X25519Crypto.serializePublic(pub).hexString)") + print("DEBUG initAlice: remote_dh_pub (bob_spk) = \(X25519Crypto.serializePublic(bobSpkPub).hexString)") + #endif + + // Perform DH ratchet to derive send chain + let dhOutput = try X25519Crypto.dh(priv, bobSpkPub) + let (newRK, sendCK) = CryptoUtils.kdfRK(rootKey: sharedSecret, dhOutput: dhOutput) + #if DEBUG + print("DEBUG initAlice: dh_output = \(dhOutput.hexString)") + print("DEBUG initAlice: new_root_key = \(newRK.hexString)") + print("DEBUG initAlice: send_chain_key = \(sendCK.hexString)") + #endif + ratchet.rootKey = newRK + ratchet.sendChainKey = sendCK + ratchet.recvChainKey = nil + ratchet.sendN = 0 + ratchet.recvN = 0 + ratchet.prevSendN = 0 + return ratchet + } + + /// Initialize as responder (Bob) after X3DH. + /// Matches Python: DoubleRatchet.init_bob(shared_secret, spk_pair) + static func initBob( + sharedSecret: Data, + spkPair: (privateKey: Curve25519.KeyAgreement.PrivateKey, publicKey: Curve25519.KeyAgreement.PublicKey) + ) -> DoubleRatchet { + let ratchet = DoubleRatchet() + ratchet.dhPair = spkPair + ratchet.rootKey = sharedSecret + ratchet.sendChainKey = nil + ratchet.recvChainKey = nil + ratchet.sendN = 0 + ratchet.recvN = 0 + ratchet.prevSendN = 0 + return ratchet + } + + // MARK: - Encrypt + + /// Encrypt a message. + /// Returns (header dict, ciphertext with tag, nonce). + /// Matches Python: DoubleRatchet.encrypt(plaintext) + func encrypt(_ plaintext: Data) throws -> (header: [String: Any], ciphertext: Data, nonce: Data) { + guard sendChainKey != nil else { + throw CryptoError.ratchetError("Send chain not initialized") + } + guard let dhPair = dhPair else { + throw CryptoError.ratchetError("DH pair not set") + } + + let (newCK, messageKey) = CryptoUtils.kdfCK(chainKey: sendChainKey!) + sendChainKey = newCK + + let header = RatchetHeader( + dhPub: X25519Crypto.serializePublic(dhPair.publicKey), + n: sendN, + pn: prevSendN + ) + + let nonce = Data.randomBytes(12) + let aad = header.serialize() + + // Debug: print encrypt values (matching Python decrypt) + #if DEBUG + print("DEBUG encrypt: message_key = \(messageKey.hexString)") + print("DEBUG encrypt: aad = \(aad.hexString)") + print("DEBUG encrypt: aad_str = \(String(data: aad, encoding: .utf8) ?? "nil")") + print("DEBUG encrypt: nonce = \(nonce.hexString)") + #endif + + let ctWithTag = try CryptoUtils.aesGcmEncrypt(plaintext, key: messageKey, nonce: nonce, aad: aad) + #if DEBUG + print("DEBUG encrypt: ciphertext_len = \(ctWithTag.count)") + #endif + + sendN += 1 + + return (header.toDict(), ctWithTag, nonce) + } + + // MARK: - Decrypt + + /// Decrypt a message. Handles DH ratchet step if new dh_pub. + /// State is snapshotted before modification and restored on failure (M9 fix). + /// Matches Python: DoubleRatchet.decrypt(header_dict, ciphertext, nonce) + func decrypt(headerDict: [String: Any], ciphertext: Data, nonce: Data) throws -> Data { + let header = try RatchetHeader.fromDict(headerDict) + let remoteDhPubBytes = header.dhPub + + // Check if this is from a skipped message + let skipKey = "\(remoteDhPubBytes.hexString):\(header.n)" + if let mk = skipped[skipKey] { + skipped.removeValue(forKey: skipKey) + let aad = header.serialize() + do { + return try CryptoUtils.aesGcmDecrypt(ciphertext, key: mk, nonce: nonce, aad: aad) + } catch { + // Restore skipped key on failure + skipped[skipKey] = mk + throw error + } + } + + // Snapshot state before modifications + let snap = snapshot() + + do { + let remoteDhPub = try X25519Crypto.loadPublic(remoteDhPubBytes) + let currentRemoteBytes: Data? = dhRemote.map { X25519Crypto.serializePublic($0) } + + if currentRemoteBytes == nil || remoteDhPubBytes != currentRemoteBytes { + // New DH ratchet step + try skipMessages(until: header.pn) + try dhRatchet(remoteDhPub: remoteDhPub) + } + + try skipMessages(until: header.n) + + // Derive message key from receive chain + guard recvChainKey != nil else { + throw CryptoError.ratchetError("Receive chain key is nil") + } + let (newCK, mk) = CryptoUtils.kdfCK(chainKey: recvChainKey!) + recvChainKey = newCK + recvN += 1 + + let aad = header.serialize() + return try CryptoUtils.aesGcmDecrypt(ciphertext, key: mk, nonce: nonce, aad: aad) + } catch { + restore(snap) + throw error + } + } + + // MARK: - State Snapshot/Restore (M9) + + private struct Snapshot { + let dhPairPriv: Data? + let dhPairPub: Data? + let dhRemote: Data? + let rootKey: Data + let sendChainKey: Data? + let recvChainKey: Data? + let sendN: Int + let recvN: Int + let prevSendN: Int + let skipped: [String: Data] + } + + private func snapshot() -> Snapshot { + Snapshot( + dhPairPriv: dhPair.map { X25519Crypto.serializePrivate($0.privateKey) }, + dhPairPub: dhPair.map { X25519Crypto.serializePublic($0.publicKey) }, + dhRemote: dhRemote.map { X25519Crypto.serializePublic($0) }, + rootKey: rootKey, + sendChainKey: sendChainKey, + recvChainKey: recvChainKey, + sendN: sendN, + recvN: recvN, + prevSendN: prevSendN, + skipped: skipped + ) + } + + private func restore(_ snap: Snapshot) { + if let privData = snap.dhPairPriv, let pubData = snap.dhPairPub, + let priv = try? X25519Crypto.loadPrivate(privData), + let pub = try? X25519Crypto.loadPublic(pubData) { + dhPair = (priv, pub) + } else { + dhPair = nil + } + if let remoteData = snap.dhRemote, let remote = try? X25519Crypto.loadPublic(remoteData) { + dhRemote = remote + } else { + dhRemote = nil + } + rootKey = snap.rootKey + sendChainKey = snap.sendChainKey + recvChainKey = snap.recvChainKey + sendN = snap.sendN + recvN = snap.recvN + prevSendN = snap.prevSendN + skipped = snap.skipped + } + + // MARK: - Internal Ratchet Operations + + private func skipMessages(until: Int) throws { + guard recvChainKey != nil else { return } + if until - recvN > Constants.maxSkip { + throw CryptoError.maxSkipExceeded + } + while recvN < until { + let (newCK, mk) = CryptoUtils.kdfCK(chainKey: recvChainKey!) + recvChainKey = newCK + let remoteHex = dhRemote.map { X25519Crypto.serializePublic($0).hexString } ?? "" + skipped["\(remoteHex):\(recvN)"] = mk + recvN += 1 + } + } + + private func dhRatchet(remoteDhPub: Curve25519.KeyAgreement.PublicKey) throws { + prevSendN = sendN + sendN = 0 + recvN = 0 + dhRemote = remoteDhPub + + // Derive new receive chain key + guard let dhPair = dhPair else { + throw CryptoError.ratchetError("DH pair not set") + } + let dhOutput1 = try X25519Crypto.dh(dhPair.privateKey, remoteDhPub) + let (newRK1, recvCK) = CryptoUtils.kdfRK(rootKey: rootKey, dhOutput: dhOutput1) + rootKey = newRK1 + recvChainKey = recvCK + + // Generate new DH pair and derive new send chain key + let (newPriv, newPub) = X25519Crypto.generateKeypair() + self.dhPair = (newPriv, newPub) + let dhOutput2 = try X25519Crypto.dh(newPriv, remoteDhPub) + let (newRK2, sendCK) = CryptoUtils.kdfRK(rootKey: rootKey, dhOutput: dhOutput2) + rootKey = newRK2 + sendChainKey = sendCK + } + + // MARK: - State Export/Import + + /// Serialize full ratchet state for persistent storage. + /// Produces JSON matching Python's DoubleRatchet.export_state() exactly. + func exportState() throws -> Data { + var state: [String: Any] = [:] + + if let pair = dhPair { + state["dh_priv"] = X25519Crypto.serializePrivate(pair.privateKey).hexString + state["dh_pub"] = X25519Crypto.serializePublic(pair.publicKey).hexString + } else { + state["dh_priv"] = NSNull() + state["dh_pub"] = NSNull() + } + + if let remote = dhRemote { + state["dh_remote"] = X25519Crypto.serializePublic(remote).hexString + } else { + state["dh_remote"] = NSNull() + } + + state["root_key"] = rootKey.hexString + state["send_ck"] = sendChainKey?.hexString ?? NSNull() + state["recv_ck"] = recvChainKey?.hexString ?? NSNull() + state["send_n"] = sendN + state["recv_n"] = recvN + state["prev_send_n"] = prevSendN + + // Skipped keys: Python format is "dh_pub_hex:n" -> message_key_hex + var skippedDict: [String: String] = [:] + for (key, value) in skipped { + skippedDict[key] = value.hexString + } + state["skipped"] = skippedDict + + return try JSONSerialization.data(withJSONObject: state) + } + + /// Deserialize ratchet state. + /// Matches Python: DoubleRatchet.import_state(data) + static func importState(_ data: Data) throws -> DoubleRatchet { + guard let state = try JSONSerialization.jsonObject(with: data) as? [String: Any] else { + throw CryptoError.stateImportFailed("Invalid JSON") + } + + let r = DoubleRatchet() + + if let dhPrivHex = state["dh_priv"] as? String, + let dhPubHex = state["dh_pub"] as? String, + let privData = Data(hexString: dhPrivHex), + let pubData = Data(hexString: dhPubHex) { + let priv = try X25519Crypto.loadPrivate(privData) + let pub = try X25519Crypto.loadPublic(pubData) + r.dhPair = (priv, pub) + } + + if let dhRemoteHex = state["dh_remote"] as? String, + let remoteData = Data(hexString: dhRemoteHex) { + r.dhRemote = try X25519Crypto.loadPublic(remoteData) + } + + guard let rootKeyHex = state["root_key"] as? String, + let rootKey = Data(hexString: rootKeyHex) else { + throw CryptoError.stateImportFailed("Missing root_key") + } + r.rootKey = rootKey + + if let sendCKHex = state["send_ck"] as? String, let ck = Data(hexString: sendCKHex) { + r.sendChainKey = ck + } + if let recvCKHex = state["recv_ck"] as? String, let ck = Data(hexString: recvCKHex) { + r.recvChainKey = ck + } + + r.sendN = state["send_n"] as? Int ?? 0 + r.recvN = state["recv_n"] as? Int ?? 0 + r.prevSendN = state["prev_send_n"] as? Int ?? 0 + + if let skippedDict = state["skipped"] as? [String: String] { + for (key, valueHex) in skippedDict { + if let value = Data(hexString: valueHex) { + r.skipped[key] = value + } + } + } + + return r + } +} diff --git a/ios_client 0.8.5/Kecalek/Crypto/Ed25519Crypto.swift b/ios_client 0.8.5/Kecalek/Crypto/Ed25519Crypto.swift new file mode 100644 index 0000000..a71ad96 --- /dev/null +++ b/ios_client 0.8.5/Kecalek/Crypto/Ed25519Crypto.swift @@ -0,0 +1,73 @@ +import Foundation +import CryptoKit + +/// Ed25519 signing operations — Identity Key management +enum Ed25519Crypto { + + // MARK: - Key Generation + + /// Generate Ed25519 keypair + static func generateKeypair() -> (privateKey: Curve25519.Signing.PrivateKey, publicKey: Curve25519.Signing.PublicKey) { + let privateKey = Curve25519.Signing.PrivateKey() + return (privateKey, privateKey.publicKey) + } + + // MARK: - Serialization + + /// Serialize Ed25519 private key. With password: raw 32B → ECP1. Without: raw 32B. + /// Matches Python: serialize_ed25519_private(key, password=None) + static func serializePrivate(_ key: Curve25519.Signing.PrivateKey, password: Data? = nil) throws -> Data { + let raw = key.rawData // 32 bytes + if let password = password { + return try KeyEncryption.encrypt(raw, password: password) + } + return raw + } + + /// Serialize Ed25519 public key to 32 raw bytes. + /// Matches Python: serialize_ed25519_public(key) + static func serializePublic(_ key: Curve25519.Signing.PublicKey) -> Data { + key.rawData // 32 bytes + } + + // MARK: - Loading + + /// Load Ed25519 private key. Auto-detects ECP1 / raw 32B. + /// Matches Python: load_ed25519_private(data, password=None) + static func loadPrivate(_ data: Data, password: Data? = nil) throws -> Curve25519.Signing.PrivateKey { + if KeyEncryption.isECP1Format(data) { + guard let pwd = password else { + throw CryptoError.invalidKeyData("ECP1 key requires password") + } + let raw = try KeyEncryption.decrypt(data, password: pwd) + return try Curve25519.Signing.PrivateKey(rawRepresentation: raw) + } + if data.count == 32 { + return try Curve25519.Signing.PrivateKey(rawRepresentation: data) + } + throw CryptoError.invalidKeyData("Cannot parse Ed25519 private key (\(data.count) bytes)") + } + + /// Load Ed25519 public key from 32 raw bytes. + /// Matches Python: load_ed25519_public(data) + static func loadPublic(_ data: Data) throws -> Curve25519.Signing.PublicKey { + guard data.count == 32 else { + throw CryptoError.invalidKeyData("Ed25519 public key must be 32 bytes, got \(data.count)") + } + return try Curve25519.Signing.PublicKey(rawRepresentation: data) + } + + // MARK: - Sign / Verify + + /// Sign data with Ed25519. Returns 64-byte signature. + /// Matches Python: ed25519_sign(private_key, data) + static func sign(_ privateKey: Curve25519.Signing.PrivateKey, data: Data) throws -> Data { + Data(try privateKey.signature(for: data)) + } + + /// Verify Ed25519 signature. + /// Matches Python: ed25519_verify(public_key, signature, data) + static func verify(_ publicKey: Curve25519.Signing.PublicKey, signature: Data, data: Data) -> Bool { + publicKey.isValidSignature(signature, for: data) + } +} diff --git a/ios_client 0.8.5/Kecalek/Crypto/FieldArithmetic.swift b/ios_client 0.8.5/Kecalek/Crypto/FieldArithmetic.swift new file mode 100644 index 0000000..c1f6ead --- /dev/null +++ b/ios_client 0.8.5/Kecalek/Crypto/FieldArithmetic.swift @@ -0,0 +1,231 @@ +import Foundation + +/// Pure Swift GF(2^255-19) arithmetic for Ed25519 → X25519 public key conversion. +/// +/// The conversion formula is: u = (1 + y) / (1 - y) mod p +/// where p = 2^255 - 19, and y is the Ed25519 public key's y-coordinate. +/// +/// Uses 4-limb UInt64 representation (little-endian). +enum FieldArithmetic { + + // p = 2^255 - 19 + static let p: [UInt64] = [ + 0xFFFF_FFFF_FFFF_FFED, // limb 0 (least significant) + 0xFFFF_FFFF_FFFF_FFFF, // limb 1 + 0xFFFF_FFFF_FFFF_FFFF, // limb 2 + 0x7FFF_FFFF_FFFF_FFFF, // limb 3 (most significant, 2^63 - 1 accounting for -19) + ] + + /// Load a 256-bit little-endian byte array into 4 UInt64 limbs + static func load(_ bytes: Data) -> [UInt64] { + precondition(bytes.count == 32) + var limbs = [UInt64](repeating: 0, count: 4) + for i in 0..<4 { + var val: UInt64 = 0 + for j in 0..<8 { + val |= UInt64(bytes[i * 8 + j]) << (j * 8) + } + limbs[i] = val + } + return limbs + } + + /// Store 4 UInt64 limbs as 32 little-endian bytes + static func store(_ limbs: [UInt64]) -> Data { + var bytes = Data(count: 32) + for i in 0..<4 { + for j in 0..<8 { + bytes[i * 8 + j] = UInt8((limbs[i] >> (j * 8)) & 0xFF) + } + } + return bytes + } + + /// a + b mod p + static func add(_ a: [UInt64], _ b: [UInt64]) -> [UInt64] { + var result = [UInt64](repeating: 0, count: 4) + var carry: UInt64 = 0 + for i in 0..<4 { + let (sum1, c1) = a[i].addingReportingOverflow(b[i]) + let (sum2, c2) = sum1.addingReportingOverflow(carry) + result[i] = sum2 + carry = (c1 ? 1 : 0) + (c2 ? 1 : 0) + } + // Reduce mod p + return reduceOnce(result, carry: carry) + } + + /// a - b mod p + static func sub(_ a: [UInt64], _ b: [UInt64]) -> [UInt64] { + var result = [UInt64](repeating: 0, count: 4) + var borrow: UInt64 = 0 + for i in 0..<4 { + let (diff1, b1) = a[i].subtractingReportingOverflow(b[i]) + let (diff2, b2) = diff1.subtractingReportingOverflow(borrow) + result[i] = diff2 + borrow = (b1 ? 1 : 0) + (b2 ? 1 : 0) + } + if borrow > 0 { + // Add p back + var c: UInt64 = 0 + for i in 0..<4 { + let (s1, c1) = result[i].addingReportingOverflow(p[i]) + let (s2, c2) = s1.addingReportingOverflow(c) + result[i] = s2 + c = (c1 ? 1 : 0) + (c2 ? 1 : 0) + } + } + return result + } + + /// Multiply two 256-bit numbers mod p using schoolbook multiplication + static func mul(_ a: [UInt64], _ b: [UInt64]) -> [UInt64] { + // Full 512-bit product in 8 limbs + var product = [UInt64](repeating: 0, count: 8) + + for i in 0..<4 { + var carry: UInt64 = 0 + for j in 0..<4 { + let (hi, lo) = a[i].multipliedFullWidth(by: b[j]) + let (sum1, c1) = product[i + j].addingReportingOverflow(lo) + let (sum2, c2) = sum1.addingReportingOverflow(carry) + product[i + j] = sum2 + carry = hi + (c1 ? 1 : 0) + (c2 ? 1 : 0) + } + product[i + 4] = carry + } + + // Reduce mod p using Barrett-like reduction + // Since p = 2^255 - 19, for a 512-bit number we can use: + // x mod p = (x_low + x_high * 2^256) mod p + // Since 2^255 ≡ 19 (mod p), 2^256 ≡ 38 (mod p) + return reduceFull(product) + } + + /// Reduce 512-bit product mod p using 2^256 ≡ 38 (mod p) + private static func reduceFull(_ product: [UInt64]) -> [UInt64] { + // Split: low = product[0..3], high = product[4..7] + // result = low + high * 38 + var result = [UInt64](repeating: 0, count: 5) + + // Start with low part + for i in 0..<4 { + result[i] = product[i] + } + + // Add high * 38 + var carry: UInt64 = 0 + for i in 0..<4 { + let (hi, lo) = product[i + 4].multipliedFullWidth(by: 38) + let (sum1, c1) = result[i].addingReportingOverflow(lo) + let (sum2, c2) = sum1.addingReportingOverflow(carry) + result[i] = sum2 + carry = hi + (c1 ? 1 : 0) + (c2 ? 1 : 0) + } + result[4] = carry + + // The result might still be >= p, so reduce once more + // result[4] * 2^256 ≡ result[4] * 38 (mod p) + let extra: UInt64 = result[4] + result[4] = 0 + if extra > 0 { + let (hi, lo) = extra.multipliedFullWidth(by: 38) + let (sum1, c1) = result[0].addingReportingOverflow(lo) + result[0] = sum1 + var c = hi + (c1 ? 1 : 0) + for i in 1..<4 { + let (s, cf) = result[i].addingReportingOverflow(c) + result[i] = s + c = cf ? 1 : 0 + } + // One more round if carry + if c > 0 { + let (s, _) = result[0].addingReportingOverflow(c * 38) + result[0] = s + } + } + + var out = Array(result[0..<4]) + // Final reduction: if >= p, subtract p + out = reduceOnce(out, carry: 0) + return out + } + + /// If the number >= p, subtract p + private static func reduceOnce(_ val: [UInt64], carry: UInt64) -> [UInt64] { + if carry > 0 || isGreaterOrEqual(val, p) { + var result = [UInt64](repeating: 0, count: 4) + var borrow: UInt64 = 0 + for i in 0..<4 { + let (diff1, b1) = val[i].subtractingReportingOverflow(p[i]) + let (diff2, b2) = diff1.subtractingReportingOverflow(borrow) + result[i] = diff2 + borrow = (b1 ? 1 : 0) + (b2 ? 1 : 0) + } + // If borrow after subtracting p, the original was fine (shouldn't happen with carry) + if borrow > 0 && carry == 0 { + return val + } + return result + } + return val + } + + /// Compare a >= b + private static func isGreaterOrEqual(_ a: [UInt64], _ b: [UInt64]) -> Bool { + for i in stride(from: 3, through: 0, by: -1) { + if a[i] > b[i] { return true } + if a[i] < b[i] { return false } + } + return true // equal + } + + /// Modular inverse using Fermat's little theorem: a^(-1) = a^(p-2) mod p + static func inverse(_ a: [UInt64]) -> [UInt64] { + // p - 2 = 2^255 - 21 + let pMinus2 = sub(p, [2, 0, 0, 0]) + return power(a, pMinus2) + } + + /// Modular exponentiation using square-and-multiply + static func power(_ base: [UInt64], _ exp: [UInt64]) -> [UInt64] { + var result: [UInt64] = [1, 0, 0, 0] // 1 + var b = base + + for i in 0..<4 { + var limb = exp[i] + let bits = (i == 3) ? 63 : 64 // top limb has 63 bits for p-2 + for _ in 0..>= 1 + } + } + return result + } + + // MARK: - Ed25519 → X25519 Public Key Conversion + + /// Convert Ed25519 public key (32 bytes) to X25519 public key (32 bytes). + /// Formula: u = (1 + y) * inverse(1 - y) mod p + static func ed25519PublicToX25519(_ ed25519Pub: Data) -> Data { + precondition(ed25519Pub.count == 32) + + // Ed25519 public key is the y-coordinate with sign bit in the top bit of byte 31 + var keyBytes = ed25519Pub + // Clear the sign bit + keyBytes[31] &= 0x7F + + let y = load(keyBytes) + let one: [UInt64] = [1, 0, 0, 0] + + let onePlusY = add(one, y) + let oneMinusY = sub(one, y) + let inv = inverse(oneMinusY) + let u = mul(onePlusY, inv) + + return store(u) + } +} diff --git a/ios_client 0.8.5/Kecalek/Crypto/KeyEncryption.swift b/ios_client 0.8.5/Kecalek/Crypto/KeyEncryption.swift new file mode 100644 index 0000000..16e8dd2 --- /dev/null +++ b/ios_client 0.8.5/Kecalek/Crypto/KeyEncryption.swift @@ -0,0 +1,106 @@ +import Foundation +import CryptoKit +import CommonCrypto + +/// ECP1 key encryption format: PBKDF2-HMAC-SHA256 (600k iterations) + AES-256-GCM +/// Wire format: magic(4) + salt(16) + nonce(12) + ciphertext_with_tag(N+16) +enum KeyEncryption { + + /// Encrypt raw key bytes with password using ECP1 format + static func encrypt(_ rawBytes: Data, password: Data) throws -> Data { + let salt = Data.randomBytes(16) + let derivedKey = try pbkdf2(password: password, salt: salt) + + let nonce = Data.randomBytes(12) + let symmetricKey = SymmetricKey(data: derivedKey) + let gcmNonce = try AES.GCM.Nonce(data: nonce) + + // AAD = ECP1 magic bytes (matching Python) + let sealedBox = try AES.GCM.seal( + rawBytes, + using: symmetricKey, + nonce: gcmNonce, + authenticating: Constants.ecp1Magic + ) + + // ciphertext + tag concatenated (matches Python's AESGCM.encrypt output) + var result = Data() + result.append(Constants.ecp1Magic) // 4 bytes + result.append(salt) // 16 bytes + result.append(nonce) // 12 bytes + result.append(sealedBox.ciphertext) // N bytes + result.append(sealedBox.tag) // 16 bytes + return result + } + + /// Decrypt ECP1-encrypted key bytes with password + static func decrypt(_ data: Data, password: Data) throws -> Data { + guard data.count >= 48 else { // 4 + 16 + 12 + 16 minimum + throw CryptoError.invalidECP1Format + } + guard data.prefix(4) == Constants.ecp1Magic else { + throw CryptoError.invalidECP1Format + } + + let salt = data[4..<20] + let nonce = data[20..<32] + let ctWithTag = data[32...] + + guard ctWithTag.count >= 16 else { + throw CryptoError.invalidECP1Format + } + + let derivedKey = try pbkdf2(password: password, salt: Data(salt)) + let symmetricKey = SymmetricKey(data: derivedKey) + let gcmNonce = try AES.GCM.Nonce(data: nonce) + + // Split ciphertext and tag + let ct = ctWithTag.prefix(ctWithTag.count - 16) + let tag = ctWithTag.suffix(16) + + let sealedBox = try AES.GCM.SealedBox( + nonce: gcmNonce, + ciphertext: ct, + tag: tag + ) + + do { + return try AES.GCM.open(sealedBox, using: symmetricKey, authenticating: Constants.ecp1Magic) + } catch { + throw CryptoError.decryptionFailed("ECP1 decryption failed - wrong password?") + } + } + + /// Check if data starts with ECP1 magic + static func isECP1Format(_ data: Data) -> Bool { + data.count >= 4 && data.prefix(4) == Constants.ecp1Magic + } + + // MARK: - PBKDF2 + + /// Derive 32-byte key using PBKDF2-HMAC-SHA256 with 600k iterations + static func pbkdf2(password: Data, salt: Data) throws -> Data { + var derivedKey = Data(count: 32) + let status = derivedKey.withUnsafeMutableBytes { derivedKeyPtr in + password.withUnsafeBytes { passwordPtr in + salt.withUnsafeBytes { saltPtr in + CCKeyDerivationPBKDF( + CCPBKDFAlgorithm(kCCPBKDF2), + passwordPtr.baseAddress?.assumingMemoryBound(to: Int8.self), + password.count, + saltPtr.baseAddress?.assumingMemoryBound(to: UInt8.self), + salt.count, + CCPseudoRandomAlgorithm(kCCPRFHmacAlgSHA256), + Constants.pbkdf2Iterations, + derivedKeyPtr.baseAddress?.assumingMemoryBound(to: UInt8.self), + 32 + ) + } + } + } + guard status == kCCSuccess else { + throw CryptoError.pbkdf2Failed + } + return derivedKey + } +} diff --git a/ios_client 0.8.5/Kecalek/Crypto/MessagePadding.swift b/ios_client 0.8.5/Kecalek/Crypto/MessagePadding.swift new file mode 100644 index 0000000..6e06c4f --- /dev/null +++ b/ios_client 0.8.5/Kecalek/Crypto/MessagePadding.swift @@ -0,0 +1,59 @@ +import Foundation + +/// Message padding for metadata privacy — hides plaintext length. +/// Matches Python: crypto_utils.py pad_plaintext / unpad_plaintext +enum MessagePadding { + + /// Magic byte prefix to distinguish padded from legacy unpadded messages. + private static let padMagic: UInt8 = 0x01 + + /// Bucket sizes for length hiding (64B to 64KB). + private static let padBuckets = [64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536] + + /// Pad plaintext to nearest bucket size to hide message length. + /// + /// Format: `0x01 + plaintext + random_padding + pad_length(4B big-endian)` + /// Prefix 0x01 distinguishes padded messages from legacy unpadded (which start with '{'). + static func pad(_ plaintext: Data) -> Data { + var content = Data([padMagic]) + content.append(plaintext) + + // +4 for the length suffix + let minSize = content.count + 4 + let target = padBuckets.first(where: { $0 >= minSize }) ?? minSize + let padLen = target - content.count + + // random_padding (padLen - 4 bytes) + pad_length (4 bytes big-endian) + var result = content + result.append(Data.randomBytes(padLen - 4)) + result.append(UInt32(padLen).bigEndianData) + return result + } + + /// Remove padding. Returns raw plaintext for both padded and legacy unpadded messages. + static func unpad(_ data: Data) -> Data { + guard !data.isEmpty else { return data } + + // Legacy unpadded message (starts with '{' for JSON) + guard data[data.startIndex] == padMagic else { return data } + + // Too short to be validly padded (magic + at least 4 bytes for length) + guard data.count >= 5 else { return data } + + // Read pad_length from last 4 bytes (big-endian UInt32) + let padLenOffset = data.count - 4 + let padLen = data.withUnsafeBytes { ptr -> UInt32 in + var value: UInt32 = 0 + withUnsafeMutableBytes(of: &value) { dest in + dest.copyBytes(from: UnsafeRawBufferPointer(rebasing: ptr[padLenOffset...])) + } + return UInt32(bigEndian: value) + } + + // Validate padding metadata + guard padLen >= 4, padLen <= data.count - 1 else { return data } + + // Strip: skip magic byte (index 0), take up to (data.count - padLen) + return data[data.startIndex + 1 ..< data.startIndex + data.count - Int(padLen)] + } +} diff --git a/ios_client 0.8.5/Kecalek/Crypto/RSACrypto.swift b/ios_client 0.8.5/Kecalek/Crypto/RSACrypto.swift new file mode 100644 index 0000000..2e23c07 --- /dev/null +++ b/ios_client 0.8.5/Kecalek/Crypto/RSACrypto.swift @@ -0,0 +1,356 @@ +import Foundation +import Security + +/// RSA-4096 operations — used for login challenge-response ONLY +enum RSACrypto { + + // MARK: - Key Generation + + /// Generate RSA-4096 keypair + static func generateKeypair() throws -> (privateKey: SecKey, publicKey: SecKey) { + let attributes: [String: Any] = [ + kSecAttrKeyType as String: kSecAttrKeyTypeRSA, + kSecAttrKeySizeInBits as String: 4096, + ] + + var error: Unmanaged? + guard let privateKey = SecKeyCreateRandomKey(attributes as CFDictionary, &error) else { + throw CryptoError.rsaKeyGenerationFailed + } + guard let publicKey = SecKeyCopyPublicKey(privateKey) else { + throw CryptoError.rsaKeyGenerationFailed + } + return (privateKey, publicKey) + } + + // MARK: - Serialization + + /// Serialize RSA private key. With password: DER → ECP1. Without: PEM PKCS#8. + static func serializePrivateKey(_ key: SecKey, password: Data? = nil) throws -> Data { + var error: Unmanaged? + guard let derData = SecKeyCopyExternalRepresentation(key, &error) as Data? else { + throw CryptoError.rsaOperationFailed("Failed to export private key") + } + + // SecKey exports in PKCS#1 format on iOS — wrap in PKCS#8 for Python compat + let pkcs8 = wrapRSAPrivateKeyPKCS8(derData) + + if let password = password { + return try KeyEncryption.encrypt(pkcs8, password: password) + } + + // PEM encode for Python compatibility + return pemEncode(pkcs8, label: "PRIVATE KEY") + } + + /// Serialize RSA public key as PEM SubjectPublicKeyInfo (Python-compatible) + static func serializePublicKey(_ key: SecKey) throws -> Data { + var error: Unmanaged? + guard let derData = SecKeyCopyExternalRepresentation(key, &error) as Data? else { + throw CryptoError.rsaOperationFailed("Failed to export public key") + } + + // SecKey exports PKCS#1 on iOS — wrap in SubjectPublicKeyInfo + let spki = wrapRSAPublicKeySPKI(derData) + return pemEncode(spki, label: "PUBLIC KEY") + } + + /// Load RSA private key. Auto-detects ECP1 vs PEM format. + static func loadPrivateKey(_ data: Data, password: Data? = nil) throws -> SecKey { + let derData: Data + + if KeyEncryption.isECP1Format(data) { + guard let pwd = password else { + throw CryptoError.invalidKeyData("ECP1 key requires password") + } + let raw = try KeyEncryption.decrypt(data, password: pwd) + derData = unwrapPKCS8ToRSAPrivateKey(raw) + } else { + // PEM format + let pem = String(data: data, encoding: .utf8) ?? "" + derData = try pemDecode(pem, label: "PRIVATE KEY") + .flatMap { unwrapPKCS8ToRSAPrivateKey($0) } + ?? pemDecode(pem, label: "RSA PRIVATE KEY") + ?? { throw CryptoError.invalidKeyData("Cannot parse RSA private key PEM") }() + } + + let attributes: [String: Any] = [ + kSecAttrKeyType as String: kSecAttrKeyTypeRSA, + kSecAttrKeyClass as String: kSecAttrKeyClassPrivate, + ] + + var error: Unmanaged? + guard let key = SecKeyCreateWithData(derData as CFData, attributes as CFDictionary, &error) else { + throw CryptoError.invalidKeyData("Failed to create RSA private key from DER") + } + return key + } + + /// Load RSA public key from PEM + static func loadPublicKey(_ pemData: Data) throws -> SecKey { + let pem = String(data: pemData, encoding: .utf8) ?? "" + + // Try SubjectPublicKeyInfo (PUBLIC KEY), unwrap to PKCS#1 + let derData: Data + if let spki = pemDecode(pem, label: "PUBLIC KEY") { + derData = unwrapSPKIToRSAPublicKey(spki) + } else if let pkcs1 = pemDecode(pem, label: "RSA PUBLIC KEY") { + derData = pkcs1 + } else { + throw CryptoError.invalidKeyData("Cannot parse RSA public key PEM") + } + + let attributes: [String: Any] = [ + kSecAttrKeyType as String: kSecAttrKeyTypeRSA, + kSecAttrKeyClass as String: kSecAttrKeyClassPublic, + ] + + var error: Unmanaged? + guard let key = SecKeyCreateWithData(derData as CFData, attributes as CFDictionary, &error) else { + throw CryptoError.invalidKeyData("Failed to create RSA public key from DER") + } + return key + } + + // MARK: - Sign / Verify + + /// Sign data with RSA-PSS SHA-256. + /// Note: iOS uses salt_length = hash_length (32). Server must use PSS.AUTO to verify. + static func sign(_ privateKey: SecKey, data: Data) throws -> Data { + var error: Unmanaged? + guard let signature = SecKeyCreateSignature( + privateKey, + .rsaSignatureMessagePSSSHA256, + data as CFData, + &error + ) as Data? else { + throw CryptoError.rsaOperationFailed("RSA signing failed") + } + return signature + } + + /// Verify RSA-PSS SHA-256 signature + static func verify(_ publicKey: SecKey, signature: Data, data: Data) -> Bool { + SecKeyVerifySignature( + publicKey, + .rsaSignatureMessagePSSSHA256, + data as CFData, + signature as CFData, + nil + ) + } + + // MARK: - RSA-OAEP Encrypt / Decrypt (for device pairing) + + /// Encrypt data with RSA-OAEP SHA-256 using a public key + static func encrypt(_ publicKey: SecKey, plaintext: Data) throws -> Data { + var error: Unmanaged? + guard let encrypted = SecKeyCreateEncryptedData( + publicKey, + .rsaEncryptionOAEPSHA256, + plaintext as CFData, + &error + ) as Data? else { + throw CryptoError.rsaOperationFailed("RSA-OAEP encryption failed") + } + return encrypted + } + + /// Decrypt data with RSA-OAEP SHA-256 using a private key + static func decrypt(_ privateKey: SecKey, ciphertext: Data) throws -> Data { + var error: Unmanaged? + guard let decrypted = SecKeyCreateDecryptedData( + privateKey, + .rsaEncryptionOAEPSHA256, + ciphertext as CFData, + &error + ) as Data? else { + throw CryptoError.rsaOperationFailed("RSA-OAEP decryption failed") + } + return decrypted + } + + /// Generate RSA-2048 keypair (for pairing temp keys — smaller for OAEP payload) + static func generateKeypair2048() throws -> (privateKey: SecKey, publicKey: SecKey) { + let attributes: [String: Any] = [ + kSecAttrKeyType as String: kSecAttrKeyTypeRSA, + kSecAttrKeySizeInBits as String: 2048, + ] + + var error: Unmanaged? + guard let privateKey = SecKeyCreateRandomKey(attributes as CFDictionary, &error) else { + throw CryptoError.rsaKeyGenerationFailed + } + guard let publicKey = SecKeyCopyPublicKey(privateKey) else { + throw CryptoError.rsaKeyGenerationFailed + } + return (privateKey, publicKey) + } + + // MARK: - PEM Helpers + + private static func pemEncode(_ der: Data, label: String) -> Data { + let base64 = der.base64EncodedString(options: .lineLength64Characters) + let pem = "-----BEGIN \(label)-----\n\(base64)\n-----END \(label)-----\n" + return Data(pem.utf8) + } + + private static func pemDecode(_ pem: String, label: String) -> Data? { + let beginMarker = "-----BEGIN \(label)-----" + let endMarker = "-----END \(label)-----" + + guard let beginRange = pem.range(of: beginMarker), + let endRange = pem.range(of: endMarker) else { + return nil + } + + let base64String = pem[beginRange.upperBound.. Data { + // PrivateKeyInfo ::= SEQUENCE { + // version INTEGER (0), + // algorithm AlgorithmIdentifier, + // privateKey OCTET STRING (containing PKCS#1 key) + // } + let version = Data([0x02, 0x01, 0x00]) // INTEGER 0 + let algorithmSeq = asn1Sequence(Data(rsaOID) + Data(nullParam)) + let privateKeyOctet = asn1OctetString(pkcs1) + return asn1Sequence(version + algorithmSeq + privateKeyOctet) + } + + /// Unwrap PKCS#8 to get PKCS#1 RSA private key + private static func unwrapPKCS8ToRSAPrivateKey(_ pkcs8: Data) -> Data { + // Parse SEQUENCE, skip version + algorithm, extract OCTET STRING + guard pkcs8.count > 2 else { return pkcs8 } + + var offset = 0 + // Outer SEQUENCE + guard pkcs8[offset] == 0x30 else { return pkcs8 } + offset += 1 + offset = skipASN1Length(pkcs8, offset: offset) + + // Version INTEGER + guard offset < pkcs8.count, pkcs8[offset] == 0x02 else { return pkcs8 } + offset += 1 + let versionLen = readASN1Length(pkcs8, offset: &offset) + offset += versionLen + + // Algorithm SEQUENCE + guard offset < pkcs8.count, pkcs8[offset] == 0x30 else { return pkcs8 } + offset += 1 + let algoLen = readASN1Length(pkcs8, offset: &offset) + offset += algoLen + + // Private key OCTET STRING + guard offset < pkcs8.count, pkcs8[offset] == 0x04 else { return pkcs8 } + offset += 1 + let keyLen = readASN1Length(pkcs8, offset: &offset) + guard offset + keyLen <= pkcs8.count else { return pkcs8 } + return Data(pkcs8[offset..<(offset + keyLen)]) + } + + /// Wrap PKCS#1 RSA public key in SubjectPublicKeyInfo + private static func wrapRSAPublicKeySPKI(_ pkcs1: Data) -> Data { + // SubjectPublicKeyInfo ::= SEQUENCE { + // algorithm AlgorithmIdentifier, + // subjectPublicKey BIT STRING (containing PKCS#1 key) + // } + let algorithmSeq = asn1Sequence(Data(rsaOID) + Data(nullParam)) + let bitString = asn1BitString(pkcs1) + return asn1Sequence(algorithmSeq + bitString) + } + + /// Unwrap SubjectPublicKeyInfo to get PKCS#1 RSA public key + private static func unwrapSPKIToRSAPublicKey(_ spki: Data) -> Data { + guard spki.count > 2 else { return spki } + + var offset = 0 + // Outer SEQUENCE + guard spki[offset] == 0x30 else { return spki } + offset += 1 + offset = skipASN1Length(spki, offset: offset) + + // Algorithm SEQUENCE + guard offset < spki.count, spki[offset] == 0x30 else { return spki } + offset += 1 + let algoLen = readASN1Length(spki, offset: &offset) + offset += algoLen + + // BIT STRING + guard offset < spki.count, spki[offset] == 0x03 else { return spki } + offset += 1 + let bitLen = readASN1Length(spki, offset: &offset) + // Skip the unused bits byte + guard offset < spki.count, spki[offset] == 0x00 else { return spki } + offset += 1 + let keyLen = bitLen - 1 + guard offset + keyLen <= spki.count else { return spki } + return Data(spki[offset..<(offset + keyLen)]) + } + + // MARK: - ASN.1 Primitives + + private static func asn1Length(_ length: Int) -> Data { + if length < 0x80 { + return Data([UInt8(length)]) + } else if length <= 0xFF { + return Data([0x81, UInt8(length)]) + } else if length <= 0xFFFF { + return Data([0x82, UInt8(length >> 8), UInt8(length & 0xFF)]) + } else { + return Data([0x83, UInt8(length >> 16), UInt8((length >> 8) & 0xFF), UInt8(length & 0xFF)]) + } + } + + private static func asn1Sequence(_ content: Data) -> Data { + Data([0x30]) + asn1Length(content.count) + content + } + + private static func asn1OctetString(_ content: Data) -> Data { + Data([0x04]) + asn1Length(content.count) + content + } + + private static func asn1BitString(_ content: Data) -> Data { + // BIT STRING: tag + length + unused_bits(0) + content + Data([0x03]) + asn1Length(content.count + 1) + Data([0x00]) + content + } + + private static func readASN1Length(_ data: Data, offset: inout Int) -> Int { + guard offset < data.count else { return 0 } + let first = data[offset] + offset += 1 + if first < 0x80 { + return Int(first) + } + let numBytes = Int(first & 0x7F) + var length = 0 + for _ in 0.. Int { + var off = offset + _ = readASN1Length(data, offset: &off) + return off + } +} diff --git a/ios_client 0.8.5/Kecalek/Crypto/SenderKeyState.swift b/ios_client 0.8.5/Kecalek/Crypto/SenderKeyState.swift new file mode 100644 index 0000000..63cc53d --- /dev/null +++ b/ios_client 0.8.5/Kecalek/Crypto/SenderKeyState.swift @@ -0,0 +1,175 @@ +import Foundation +import CryptoKit + +/// Sender key chain for group messaging. +/// Each sender in a group has their own chain. Others receive the initial key via pairwise ratchet. +/// Matches Python: SenderKeyState class in crypto_utils.py +class SenderKeyState { + + let senderKey: Data + let chainId: Data + private(set) var chainKey: Data + private(set) var n: Int + private var knownKeys: [Int: Data] + + /// Initialize with optional sender key (generates random 32B if nil). + /// Matches Python: SenderKeyState.__init__(sender_key=None) + init(senderKey: Data? = nil) { + let key = senderKey ?? Data.randomBytes(32) + self.senderKey = key + self.chainId = Data(SHA256.hash(data: key)) + self.chainKey = CryptoUtils.hkdfDerive( + inputKey: key, + salt: Data(repeating: 0x00, count: 32), + info: Data(Constants.senderKeyChainInfo.utf8), + length: 32 + ) + self.n = 0 + self.knownKeys = [:] + } + + /// Private init for import + private init(senderKey: Data, chainId: Data, chainKey: Data, n: Int, knownKeys: [Int: Data]) { + self.senderKey = senderKey + self.chainId = chainId + self.chainKey = chainKey + self.n = n + self.knownKeys = knownKeys + } + + // MARK: - Encrypt + + /// Encrypt with current chain key. + /// Returns (chainId hex, n, ciphertext with tag, nonce). + /// Matches Python: SenderKeyState.encrypt(plaintext) + func encrypt(_ plaintext: Data) throws -> (chainIdHex: String, n: Int, ciphertext: Data, nonce: Data) { + let (newCK, messageKey) = CryptoUtils.kdfCK(chainKey: chainKey) + chainKey = newCK + + let nonce = Data.randomBytes(12) + // AAD = chainId + bigEndian(UInt32(n)) + let aad = chainId + UInt32(n).bigEndianData + let ctWithTag = try CryptoUtils.aesGcmEncrypt(plaintext, key: messageKey, nonce: nonce, aad: aad) + + let result = (chainIdHex: chainId.hexString, n: n, ciphertext: ctWithTag, nonce: nonce) + n += 1 + return result + } + + // MARK: - Decrypt + + /// Decrypt a group message. Fast-forwards the chain if needed. + /// State is snapshotted before modification and restored on failure. + /// Matches Python: SenderKeyState.decrypt(chain_id_hex, n, ciphertext, nonce) + func decrypt(chainIdHex: String, n: Int, ciphertext: Data, nonce: Data) throws -> Data { + guard let expectedChainId = Data(hexString: chainIdHex) else { + throw CryptoError.senderKeyError("Invalid chain ID hex") + } + guard expectedChainId == chainId else { + throw CryptoError.senderKeyError("Chain ID mismatch") + } + + if n - self.n > Constants.maxSenderKeySkip { + throw CryptoError.senderKeyError("Sender key skip too large (\(n - self.n) > \(Constants.maxSenderKeySkip))") + } + + // Snapshot before fast-forward + let snapChainKey = chainKey + let snapN = self.n + let snapKnown = knownKeys + + do { + // Fast-forward the chain to reach message n + while self.n <= n { + let (newCK, mk) = CryptoUtils.kdfCK(chainKey: chainKey) + chainKey = newCK + knownKeys[self.n] = mk + self.n += 1 + } + + guard let mk = knownKeys.removeValue(forKey: n) else { + throw CryptoError.senderKeyError("Message key for n=\(n) not available") + } + + let aad = chainId + UInt32(n).bigEndianData + return try CryptoUtils.aesGcmDecrypt(ciphertext, key: mk, nonce: nonce, aad: aad) + } catch { + // Restore state on failure + chainKey = snapChainKey + self.n = snapN + knownKeys = snapKnown + throw error + } + } + + // MARK: - Key Export/Import + + /// Export sender key for distribution to group members. + /// Matches Python: SenderKeyState.export_key() + func exportKey() -> Data { + let dict: [String: Any] = ["sender_key": senderKey.hexString] + return try! JSONSerialization.data(withJSONObject: dict) + } + + /// Initialize a receiving SenderKeyState from an exported key. + /// Matches Python: SenderKeyState.from_key(exported_key) + static func fromKey(_ exportedKey: Data) throws -> SenderKeyState { + guard let dict = try JSONSerialization.jsonObject(with: exportedKey) as? [String: Any], + let senderKeyHex = dict["sender_key"] as? String, + let senderKey = Data(hexString: senderKeyHex) else { + throw CryptoError.stateImportFailed("Invalid sender key export") + } + return SenderKeyState(senderKey: senderKey) + } + + // MARK: - Full State Export/Import + + /// Serialize full state for persistent storage. + /// Matches Python: SenderKeyState.export_state() + func exportState() -> Data { + var knownKeysDict: [String: String] = [:] + for (k, v) in knownKeys { + knownKeysDict[String(k)] = v.hexString + } + let state: [String: Any] = [ + "sender_key": senderKey.hexString, + "chain_id": chainId.hexString, + "chain_key": chainKey.hexString, + "n": n, + "known_keys": knownKeysDict, + ] + return try! JSONSerialization.data(withJSONObject: state) + } + + /// Deserialize full state. + /// Matches Python: SenderKeyState.import_state(data) + static func importState(_ data: Data) throws -> SenderKeyState { + guard let state = try JSONSerialization.jsonObject(with: data) as? [String: Any], + let senderKeyHex = state["sender_key"] as? String, + let senderKey = Data(hexString: senderKeyHex), + let chainIdHex = state["chain_id"] as? String, + let chainId = Data(hexString: chainIdHex), + let chainKeyHex = state["chain_key"] as? String, + let chainKey = Data(hexString: chainKeyHex), + let n = state["n"] as? Int else { + throw CryptoError.stateImportFailed("Invalid sender key state") + } + + var knownKeys: [Int: Data] = [:] + if let knownKeysDict = state["known_keys"] as? [String: String] { + for (k, v) in knownKeysDict { + if let idx = Int(k), let data = Data(hexString: v) { + knownKeys[idx] = data + } + } + } + + return SenderKeyState( + senderKey: senderKey, + chainId: chainId, + chainKey: chainKey, + n: n, + knownKeys: knownKeys + ) + } +} diff --git a/ios_client 0.8.5/Kecalek/Crypto/X25519Crypto.swift b/ios_client 0.8.5/Kecalek/Crypto/X25519Crypto.swift new file mode 100644 index 0000000..5431489 --- /dev/null +++ b/ios_client 0.8.5/Kecalek/Crypto/X25519Crypto.swift @@ -0,0 +1,77 @@ +import Foundation +import CryptoKit + +/// X25519 Diffie-Hellman key agreement +enum X25519Crypto { + + // MARK: - Key Generation + + /// Generate X25519 keypair + static func generateKeypair() -> (privateKey: Curve25519.KeyAgreement.PrivateKey, publicKey: Curve25519.KeyAgreement.PublicKey) { + let privateKey = Curve25519.KeyAgreement.PrivateKey() + return (privateKey, privateKey.publicKey) + } + + // MARK: - Serialization + + /// Serialize X25519 private key to 32 raw bytes + static func serializePrivate(_ key: Curve25519.KeyAgreement.PrivateKey) -> Data { + key.rawData // 32 bytes + } + + /// Serialize X25519 public key to 32 raw bytes + static func serializePublic(_ key: Curve25519.KeyAgreement.PublicKey) -> Data { + key.rawData // 32 bytes + } + + /// Load X25519 private key from 32 raw bytes + static func loadPrivate(_ data: Data) throws -> Curve25519.KeyAgreement.PrivateKey { + guard data.count == 32 else { + throw CryptoError.invalidKeyData("X25519 private key must be 32 bytes") + } + return try Curve25519.KeyAgreement.PrivateKey(rawRepresentation: data) + } + + /// Load X25519 public key from 32 raw bytes + static func loadPublic(_ data: Data) throws -> Curve25519.KeyAgreement.PublicKey { + guard data.count == 32 else { + throw CryptoError.invalidKeyData("X25519 public key must be 32 bytes") + } + return try Curve25519.KeyAgreement.PublicKey(rawRepresentation: data) + } + + // MARK: - Diffie-Hellman + + /// Perform X25519 DH key agreement. Returns 32-byte shared secret. + /// Matches Python: x25519_dh(private_key, public_key) + static func dh(_ privateKey: Curve25519.KeyAgreement.PrivateKey, _ publicKey: Curve25519.KeyAgreement.PublicKey) throws -> Data { + let sharedSecret = try privateKey.sharedSecretFromKeyAgreement(with: publicKey) + // Extract raw bytes from SharedSecret + return sharedSecret.withUnsafeBytes { Data($0) } + } + + // MARK: - Ed25519 → X25519 Key Conversion + + /// Convert Ed25519 private key to X25519 private key. + /// SHA-512(seed) → take first 32 bytes → clamp per RFC 7748 + /// Matches Python: ed25519_private_to_x25519(ed_private) + static func fromEd25519Private(_ edPrivate: Curve25519.Signing.PrivateKey) throws -> Curve25519.KeyAgreement.PrivateKey { + let raw = edPrivate.rawData // 32 bytes seed + // SHA-512 of the seed + let hash = SHA512.hash(data: raw) + var clamped = Data(hash.prefix(32)) + // Clamp per RFC 7748 + clamped[0] &= 248 + clamped[31] &= 127 + clamped[31] |= 64 + return try Curve25519.KeyAgreement.PrivateKey(rawRepresentation: clamped) + } + + /// Convert Ed25519 public key to X25519 public key. + /// Uses Montgomery birational map: u = (1+y)/(1-y) mod p + /// Matches Python: ed25519_public_to_x25519(ed_public) + static func fromEd25519Public(_ edPublic: Curve25519.Signing.PublicKey) throws -> Curve25519.KeyAgreement.PublicKey { + let x25519Bytes = FieldArithmetic.ed25519PublicToX25519(edPublic.rawData) + return try Curve25519.KeyAgreement.PublicKey(rawRepresentation: x25519Bytes) + } +} diff --git a/ios_client 0.8.5/Kecalek/Crypto/X3DH.swift b/ios_client 0.8.5/Kecalek/Crypto/X3DH.swift new file mode 100644 index 0000000..31202fa --- /dev/null +++ b/ios_client 0.8.5/Kecalek/Crypto/X3DH.swift @@ -0,0 +1,139 @@ +import Foundation +import CryptoKit + +/// X3DH key agreement protocol (Signal Protocol) +enum X3DH { + + // MARK: - Pre-Key Generation + + /// Generate a signed pre-key (SPK). + /// Returns (private, public, signature, id). + /// Matches Python: generate_signed_prekey(identity_private) + static func generateSignedPrekey( + identityPrivate: Curve25519.Signing.PrivateKey + ) throws -> (privateKey: Curve25519.KeyAgreement.PrivateKey, + publicKey: Curve25519.KeyAgreement.PublicKey, + signature: Data, + id: String) { + let (spkPriv, spkPub) = X25519Crypto.generateKeypair() + let spkPubBytes = X25519Crypto.serializePublic(spkPub) + let signature = try Ed25519Crypto.sign(identityPrivate, data: spkPubBytes) + return (spkPriv, spkPub, signature, UUID().uuidString) + } + + /// Generate a batch of one-time pre-keys. + /// Matches Python: generate_one_time_prekeys(count=50) + static func generateOneTimePrekeys(count: Int = 50) -> [(privateKey: Curve25519.KeyAgreement.PrivateKey, + publicKey: Curve25519.KeyAgreement.PublicKey, + id: String)] { + (0.. (sharedSecret: Data, + ephemeralPrivate: Curve25519.KeyAgreement.PrivateKey, + ephemeralPublic: Curve25519.KeyAgreement.PublicKey) { + // Verify SPK signature + let spkRemoteBytes = X25519Crypto.serializePublic(spkRemote) + guard Ed25519Crypto.verify(ikPublicRemoteEd, signature: spkSignature, data: spkRemoteBytes) else { + throw CryptoError.x3dhFailed("Invalid SPK signature") + } + + // Convert identity keys to X25519 + let ikX25519Private = try X25519Crypto.fromEd25519Private(ikPrivateEd) + let ikX25519Remote = try X25519Crypto.fromEd25519Public(ikPublicRemoteEd) + + // Generate ephemeral keypair + let (ekPriv, ekPub) = X25519Crypto.generateKeypair() + + // Debug: print key inputs (matching Python x3dh_respond) + #if DEBUG + print("DEBUG x3dh_initiate: ik_remote_ed = \(Ed25519Crypto.serializePublic(ikPublicRemoteEd).hexString)") + print("DEBUG x3dh_initiate: ik_x25519_remote = \(X25519Crypto.serializePublic(ikX25519Remote).hexString)") + print("DEBUG x3dh_initiate: ek_pub = \(X25519Crypto.serializePublic(ekPub).hexString)") + print("DEBUG x3dh_initiate: spk_remote = \(spkRemoteBytes.hexString)") + #endif + + // DH computations + let dh1 = try X25519Crypto.dh(ikX25519Private, spkRemote) // IK_A, SPK_B + let dh2 = try X25519Crypto.dh(ekPriv, ikX25519Remote) // EK_A, IK_B + let dh3 = try X25519Crypto.dh(ekPriv, spkRemote) // EK_A, SPK_B + + // Debug: print DH outputs + #if DEBUG + print("DEBUG x3dh_initiate: dh1 = \(dh1.hexString)") + print("DEBUG x3dh_initiate: dh2 = \(dh2.hexString)") + print("DEBUG x3dh_initiate: dh3 = \(dh3.hexString)") + #endif + + var dhConcat = dh1 + dh2 + dh3 + if let opk = opkRemote { + let dh4 = try X25519Crypto.dh(ekPriv, opk) // EK_A, OPK_B + #if DEBUG + print("DEBUG x3dh_initiate: dh4 = \(dh4.hexString)") + #endif + dhConcat += dh4 + } + + // Derive shared secret + let sharedSecret = CryptoUtils.hkdfDerive( + inputKey: dhConcat, + salt: Data(repeating: 0x00, count: 32), + info: Data(Constants.x3dhInfo.utf8), + length: 32 + ) + #if DEBUG + print("DEBUG x3dh_initiate: shared_secret = \(sharedSecret.hexString)") + #endif + + return (sharedSecret, ekPriv, ekPub) + } + + // MARK: - X3DH Respond (Bob) + + /// Responder side of X3DH. + /// Returns sharedSecret. + /// Matches Python: x3dh_respond(ik_private_ed, spk_private, ik_remote_ed, ek_remote, opk_private?) + static func respond( + ikPrivateEd: Curve25519.Signing.PrivateKey, + spkPrivate: Curve25519.KeyAgreement.PrivateKey, + ikRemoteEd: Curve25519.Signing.PublicKey, + ekRemote: Curve25519.KeyAgreement.PublicKey, + opkPrivate: Curve25519.KeyAgreement.PrivateKey? = nil + ) throws -> Data { + let ikX25519Private = try X25519Crypto.fromEd25519Private(ikPrivateEd) + let ikX25519Remote = try X25519Crypto.fromEd25519Public(ikRemoteEd) + + let dh1 = try X25519Crypto.dh(spkPrivate, ikX25519Remote) // SPK_B, IK_A + let dh2 = try X25519Crypto.dh(ikX25519Private, ekRemote) // IK_B, EK_A + let dh3 = try X25519Crypto.dh(spkPrivate, ekRemote) // SPK_B, EK_A + + var dhConcat = dh1 + dh2 + dh3 + if let opk = opkPrivate { + let dh4 = try X25519Crypto.dh(opk, ekRemote) // OPK_B, EK_A + dhConcat += dh4 + } + + let sharedSecret = CryptoUtils.hkdfDerive( + inputKey: dhConcat, + salt: Data(repeating: 0x00, count: 32), + info: Data(Constants.x3dhInfo.utf8), + length: 32 + ) + + return sharedSecret + } +} diff --git a/ios_client 0.8.5/Kecalek/KecalekApp.swift b/ios_client 0.8.5/Kecalek/KecalekApp.swift new file mode 100644 index 0000000..95a97e8 --- /dev/null +++ b/ios_client 0.8.5/Kecalek/KecalekApp.swift @@ -0,0 +1,47 @@ +import SwiftUI + +@main +struct KecalekApp: App { + @State private var appState = AppState() + @State private var authViewModel = AuthViewModel() + @Environment(\.scenePhase) private var scenePhase + + var body: some Scene { + WindowGroup { + if appState.isLoggedIn { + MainTabView(appState: appState) + } else { + LoginView(viewModel: authViewModel, appState: appState) + } + } + .onChange(of: scenePhase) { _, newPhase in + switch newPhase { + case .background: + appState.handleEnteredBackground() + case .active: + appState.handleBecameActive() + default: + break + } + } + } +} + +struct MainTabView: View { + var appState: AppState + @State private var convListVM = ConversationListVM() + + var body: some View { + TabView { + ConversationListView(appState: appState, viewModel: convListVM) + .tabItem { + Label("Chats", systemImage: "bubble.left.and.bubble.right.fill") + } + + ProfileView(appState: appState, isOwnProfile: true) + .tabItem { + Label("Profile", systemImage: "person.fill") + } + } + } +} diff --git a/ios_client 0.8.5/Kecalek/Models/Conversation.swift b/ios_client 0.8.5/Kecalek/Models/Conversation.swift new file mode 100644 index 0000000..e179d09 --- /dev/null +++ b/ios_client 0.8.5/Kecalek/Models/Conversation.swift @@ -0,0 +1,54 @@ +import Foundation + +struct Conversation: Identifiable, Equatable, Hashable, Codable { + let id: String + var name: String? + var members: [ConversationMember] + var createdBy: String? + var avatarFile: String? + var unreadCount: Int + var isFavorite: Bool + var lastMessageTime: Date? + + var isGroup: Bool { + name != nil || members.count > 2 + } + + /// Display name: group name, or DM partner username + func displayName(currentUserId: String) -> String { + if let name = name, !name.isEmpty { + return name + } + // DM: show the other person's name + if let other = members.first(where: { $0.userId != currentUserId }) { + return other.username + } + return "Unknown" + } + + /// DM partner user ID (nil for groups) + func dmPartnerId(currentUserId: String) -> String? { + guard !isGroup else { return nil } + return members.first(where: { $0.userId != currentUserId })?.userId + } + + static func == (lhs: Conversation, rhs: Conversation) -> Bool { + lhs.id == rhs.id + && lhs.name == rhs.name + && lhs.members == rhs.members + && lhs.avatarFile == rhs.avatarFile + && lhs.unreadCount == rhs.unreadCount + } + + func hash(into hasher: inout Hasher) { + hasher.combine(id) + } +} + +struct ConversationMember: Identifiable, Equatable, Codable { + let userId: String + var username: String + var email: String + + var id: String { userId } +} diff --git a/ios_client 0.8.5/Kecalek/Models/DeviceBundle.swift b/ios_client 0.8.5/Kecalek/Models/DeviceBundle.swift new file mode 100644 index 0000000..be520bb --- /dev/null +++ b/ios_client 0.8.5/Kecalek/Models/DeviceBundle.swift @@ -0,0 +1,69 @@ +import Foundation + +/// Key bundle for one device, used in X3DH +struct DeviceBundle { + let deviceId: String + let identityKey: Data // Ed25519 public key (32 bytes) + let spk: Data // X25519 public key (32 bytes) + let spkSignature: Data // Ed25519 signature (64 bytes) + let spkId: String + let opk: Data? // X25519 public key (32 bytes), optional + let opkId: String? + + /// Parse from server response dictionary + /// Server uses: signed_prekey, signed_prekey_id, one_time_prekey, one_time_prekey_id (base64) + static func fromDict(_ dict: [String: Any], identityKey: Data? = nil) throws -> DeviceBundle { + guard let deviceId = dict["device_id"] as? String else { + throw ChatError.invalidData("Missing device_id") + } + + // Identity key can be passed in (from parent) or in dict + let ik: Data + if let passedIk = identityKey { + ik = passedIk + } else if let ikB64 = dict["identity_key"] as? String, + let ikData = Data(base64Encoded: ikB64) { + ik = ikData + } else { + throw ChatError.invalidData("Missing identity_key") + } + + // SPK - try both naming conventions, base64 encoded + let spkB64 = dict["signed_prekey"] as? String ?? dict["spk"] as? String + guard let spkB64 = spkB64, + let spk = Data(base64Encoded: spkB64) else { + throw ChatError.invalidData("Missing signed_prekey") + } + + // SPK signature - base64 encoded + guard let spkSigB64 = dict["spk_signature"] as? String, + let spkSig = Data(base64Encoded: spkSigB64) else { + throw ChatError.invalidData("Missing spk_signature") + } + + // SPK ID - try both naming conventions + let spkId = dict["signed_prekey_id"] as? String ?? dict["spk_id"] as? String + guard let spkId = spkId else { + throw ChatError.invalidData("Missing signed_prekey_id") + } + + // OPK - optional, base64 encoded + var opk: Data? + var opkId: String? + let opkB64 = dict["one_time_prekey"] as? String ?? dict["opk"] as? String + if let opkB64 = opkB64, let opkData = Data(base64Encoded: opkB64) { + opk = opkData + opkId = dict["one_time_prekey_id"] as? String ?? dict["opk_id"] as? String + } + + return DeviceBundle( + deviceId: deviceId, + identityKey: ik, + spk: spk, + spkSignature: spkSig, + spkId: spkId, + opk: opk, + opkId: opkId + ) + } +} diff --git a/ios_client 0.8.5/Kecalek/Models/Invitation.swift b/ios_client 0.8.5/Kecalek/Models/Invitation.swift new file mode 100644 index 0000000..6b0631c --- /dev/null +++ b/ios_client 0.8.5/Kecalek/Models/Invitation.swift @@ -0,0 +1,9 @@ +import Foundation + +struct Invitation: Identifiable { + let id: String // invitation id (from server) or conversationId + let conversationId: String + let conversationName: String + let invitedBy: String + let invitedByUsername: String +} diff --git a/ios_client 0.8.5/Kecalek/Models/Message.swift b/ios_client 0.8.5/Kecalek/Models/Message.swift new file mode 100644 index 0000000..4eb18eb --- /dev/null +++ b/ios_client 0.8.5/Kecalek/Models/Message.swift @@ -0,0 +1,210 @@ +import Foundation + +struct MessageReaction: Equatable { + let userId: String + let reaction: String + let createdAt: Date +} + +struct ForwardedFrom: Equatable { + let sender: String + let conversationId: String + let messageId: String +} + +enum ReactionEmoji { + static let allowed = ["thumbsup", "heart", "laugh", "surprised", "sad", "thumbsdown"] + static let display: [String: String] = [ + "thumbsup": "👍", "heart": "❤️", "laugh": "😂", + "surprised": "😮", "sad": "😢", "thumbsdown": "👎", + ] +} + +struct Message: Identifiable, Equatable { + let id: String + let conversationId: String + let senderId: String + var senderUsername: String + let createdAt: Date + var text: String? + var replyTo: String? + var imageFileId: String? + var file: FileInfo? + var image: ImageInfo? + var isDeleted: Bool + var readBy: Set + var reactions: [MessageReaction] + var forwardedFrom: ForwardedFrom? + var pinnedAt: Date? + var pinnedBy: String? + + /// Whether this is a self-sent message + func isMine(currentUserId: String) -> Bool { + senderId == currentUserId + } + + static func == (lhs: Message, rhs: Message) -> Bool { + lhs.id == rhs.id + } +} + +struct FileInfo: Equatable, Codable { + let fileId: String + let aesKey: String // base64 + let iv: String // base64 + let filename: String + let size: Int + let mimeType: String +} + +struct ImageInfo: Equatable { + let fileId: String + let aesKey: String // base64 + let iv: String // base64 + let thumbnail: String? // base64 JPEG thumbnail + let filename: String + let size: Int +} + +// MARK: - Cache Dictionary Conversion + +extension Message { + /// Convert to dictionary matching server JSON format for MessageCache storage + func toCacheDict() -> [String: Any] { + var dict: [String: Any] = [ + "message_id": id, + "conversation_id": conversationId, + "sender_id": senderId, + "sender_username": senderUsername, + "created_at": DateParsing.format(createdAt), + "is_deleted": isDeleted, + ] + if let text = text { dict["text"] = text } + if let replyTo = replyTo { dict["reply_to"] = replyTo } + if let imageFileId = imageFileId { dict["image_file_id"] = imageFileId } + if let file = file { + dict["file"] = [ + "file_id": file.fileId, + "aes_key": file.aesKey, + "iv": file.iv, + "filename": file.filename, + "size": file.size, + "mime_type": file.mimeType, + ] as [String: Any] + } + if let image = image { + var imgDict: [String: Any] = [ + "file_id": image.fileId, + "aes_key": image.aesKey, + "iv": image.iv, + "filename": image.filename, + "size": image.size, + ] + if let thumbnail = image.thumbnail { imgDict["thumbnail"] = thumbnail } + dict["image"] = imgDict + } + if !readBy.isEmpty { dict["read_by"] = Array(readBy) } + if !reactions.isEmpty { + dict["reactions"] = reactions.map { + ["user_id": $0.userId, "reaction": $0.reaction, + "created_at": DateParsing.format($0.createdAt)] as [String: Any] + } + } + if let fwd = forwardedFrom { + dict["forwarded_from"] = ["sender": fwd.sender, + "conversation_id": fwd.conversationId, + "message_id": fwd.messageId] as [String: Any] + } + if let pinnedAt { dict["pinned_at"] = DateParsing.format(pinnedAt) } + if let pinnedBy { dict["pinned_by"] = pinnedBy } + return dict + } + + /// Create Message from cache dictionary (server JSON format) + static func fromCacheDict(_ dict: [String: Any]) -> Message? { + guard let id = dict["message_id"] as? String, + let conversationId = dict["conversation_id"] as? String, + let senderId = dict["sender_id"] as? String, + let createdAtStr = dict["created_at"] as? String, + let createdAt = DateParsing.parse(createdAtStr) else { + return nil + } + + let senderUsername = dict["sender_username"] as? String ?? "" + + var file: FileInfo? + if let fileDict = dict["file"] as? [String: Any], + let fileId = fileDict["file_id"] as? String { + file = FileInfo( + fileId: fileId, + aesKey: fileDict["aes_key"] as? String ?? "", + iv: fileDict["iv"] as? String ?? "", + filename: fileDict["filename"] as? String ?? "", + size: fileDict["size"] as? Int ?? 0, + mimeType: fileDict["mime_type"] as? String ?? "" + ) + } + + var image: ImageInfo? + if let imgDict = dict["image"] as? [String: Any], + let imgFileId = imgDict["file_id"] as? String { + image = ImageInfo( + fileId: imgFileId, + aesKey: imgDict["aes_key"] as? String ?? "", + iv: imgDict["iv"] as? String ?? "", + thumbnail: imgDict["thumbnail"] as? String, + filename: imgDict["filename"] as? String ?? "image.jpg", + size: imgDict["size"] as? Int ?? 0 + ) + } + + let readBy: Set + if let readByArray = dict["read_by"] as? [String] { + readBy = Set(readByArray) + } else { + readBy = [] + } + + var reactions: [MessageReaction] = [] + if let reactionsArr = dict["reactions"] as? [[String: Any]] { + reactions = reactionsArr.compactMap { r in + guard let userId = r["user_id"] as? String, + let reaction = r["reaction"] as? String else { return nil } + let createdAt = (r["created_at"] as? String).flatMap { DateParsing.parse($0) } ?? Date() + return MessageReaction(userId: userId, reaction: reaction, createdAt: createdAt) + } + } + + var forwardedFrom: ForwardedFrom? + if let fwd = dict["forwarded_from"] as? [String: Any], + let sender = fwd["sender"] as? String { + forwardedFrom = ForwardedFrom( + sender: sender, + conversationId: fwd["conversation_id"] as? String ?? "", + messageId: fwd["message_id"] as? String ?? "" + ) + } + + let pinnedAt = (dict["pinned_at"] as? String).flatMap { DateParsing.parse($0) } + let pinnedBy = dict["pinned_by"] as? String + + return Message( + id: id, + conversationId: conversationId, + senderId: senderId, + senderUsername: senderUsername, + createdAt: createdAt, + text: dict["text"] as? String, + replyTo: dict["reply_to"] as? String, + imageFileId: dict["image_file_id"] as? String, + file: file, + image: image, + isDeleted: dict["is_deleted"] as? Bool ?? false, + readBy: readBy, + reactions: reactions, + forwardedFrom: forwardedFrom, + pinnedAt: pinnedAt, + pinnedBy: pinnedBy + ) + } +} diff --git a/ios_client 0.8.5/Kecalek/Models/User.swift b/ios_client 0.8.5/Kecalek/Models/User.swift new file mode 100644 index 0000000..0e429b3 --- /dev/null +++ b/ios_client 0.8.5/Kecalek/Models/User.swift @@ -0,0 +1,19 @@ +import Foundation + +struct User: Identifiable, Equatable { + let id: String + var username: String + var email: String + var identityKey: Data? // Ed25519 public key (32 bytes) +} + +struct UserProfile: Equatable { + var userId: String + var username: String? + var email: String? + var phone: String? + var phoneVisible: Bool + var location: String? + var locationVisible: Bool + var avatarFile: String? +} diff --git a/ios_client 0.8.5/Kecalek/Network/ConnectionManager.swift b/ios_client 0.8.5/Kecalek/Network/ConnectionManager.swift new file mode 100644 index 0000000..745a71e --- /dev/null +++ b/ios_client 0.8.5/Kecalek/Network/ConnectionManager.swift @@ -0,0 +1,191 @@ +import Foundation +import Network + +/// TCP connection manager using Network.framework. +/// Handles connection lifecycle, TLS, buffered reading (newline-delimited), and writing. +actor ConnectionManager { + + enum ConnectionState: Equatable { + case disconnected + case connecting + case connected + case failed(String) + } + + private var connection: NWConnection? + private var receiveBuffer = Data() + private(set) var state: ConnectionState = .disconnected + private var stateCallback: ((ConnectionState) -> Void)? + private var messageStream: AsyncStream<[String: Any]>.Continuation? + + /// Set a callback for connection state changes + func onStateChange(_ callback: @escaping (ConnectionState) -> Void) { + stateCallback = callback + } + + // MARK: - Connect / Disconnect + + /// Connect to server + func connect(host: String, port: UInt16) async throws { + guard state == .disconnected || state != .connected else { + throw NetworkError.alreadyConnected + } + + updateState(.connecting) + + let nwHost = NWEndpoint.Host(host) + let nwPort = NWEndpoint.Port(rawValue: port)! + + let tlsOptions = NWProtocolTLS.Options() + let params = NWParameters(tls: tlsOptions, tcp: .init()) + + let conn = NWConnection(host: nwHost, port: nwPort, using: params) + self.connection = conn + self.receiveBuffer = Data() + + return try await withCheckedThrowingContinuation { continuation in + // nonisolated flag — accessed only from the stateUpdateHandler serial queue + // Use a class wrapper so the closure can mutate it + final class ResumedFlag: @unchecked Sendable { + var value = false + } + let resumed = ResumedFlag() + + conn.stateUpdateHandler = { [weak self] newState in + Task { [weak self] in + guard let self = self else { return } + switch newState { + case .ready: + await self.updateState(.connected) + guard !resumed.value else { return } + resumed.value = true + continuation.resume() + case .failed(let error): + await self.updateState(.failed(error.localizedDescription)) + guard !resumed.value else { return } + resumed.value = true + continuation.resume(throwing: NetworkError.connectionFailed(error.localizedDescription)) + case .cancelled: + await self.updateState(.disconnected) + guard !resumed.value else { return } + resumed.value = true + continuation.resume(throwing: NetworkError.connectionFailed("Connection cancelled")) + case .waiting(let error): + await self.updateState(.failed(error.localizedDescription)) + guard !resumed.value else { return } + resumed.value = true + continuation.resume(throwing: NetworkError.connectionFailed("Waiting: \(error.localizedDescription)")) + default: + break + } + } + } + conn.start(queue: .global(qos: .userInitiated)) + } + } + + /// Disconnect from server + func disconnect() { + connection?.cancel() + connection = nil + receiveBuffer = Data() + updateState(.disconnected) + messageStream?.finish() + messageStream = nil + } + + // MARK: - Send + + /// Send raw data over the connection + func send(_ data: Data) async throws { + guard let connection = connection, state == .connected else { + throw NetworkError.notConnected + } + + return try await withCheckedThrowingContinuation { continuation in + connection.send(content: data, completion: .contentProcessed { error in + if let error = error { + continuation.resume(throwing: NetworkError.connectionFailed(error.localizedDescription)) + } else { + continuation.resume() + } + }) + } + } + + /// Send a protocol message (builds JSON + newline, sends) + func sendMessage(type: String, requestId: String? = nil, params: [String: Any] = [:]) async throws { + let data = try ProtocolHandler.buildRequest(type: type, requestId: requestId, params: params) + try await send(data) + } + + // MARK: - Receive + + /// Read one newline-delimited JSON message. + /// Returns nil on EOF / connection close. + func readMessage() async throws -> [String: Any]? { + while true { + // Check buffer for a complete line + if let newlineIndex = receiveBuffer.firstIndex(of: 0x0A) { + let lineData = receiveBuffer.prefix(through: newlineIndex) + receiveBuffer.removeSubrange(...newlineIndex) + + // Check size + if lineData.count > Constants.maxMessageBytes { + throw NetworkError.messageTooLarge + } + + return try ProtocolHandler.parseMessage(Data(lineData)) + } + + // Buffer doesn't have a complete line — read more from the connection + guard let connection = connection else { + return nil + } + + let chunk = try await receiveChunk(connection: connection) + guard let chunk = chunk else { + return nil // EOF + } + + receiveBuffer.append(chunk) + + // Safety: if buffer exceeds max without a newline, drop it + if receiveBuffer.count > Constants.maxMessageBytes * 2 { + receiveBuffer = Data() + throw NetworkError.messageTooLarge + } + } + } + + /// Read a chunk of data from the connection + private func receiveChunk(connection: NWConnection) async throws -> Data? { + return try await withCheckedThrowingContinuation { continuation in + connection.receive(minimumIncompleteLength: 1, maximumLength: 65536) { content, _, isComplete, error in + if let error = error { + continuation.resume(throwing: NetworkError.connectionFailed(error.localizedDescription)) + return + } + if let content = content, !content.isEmpty { + continuation.resume(returning: content) + } else if isComplete { + continuation.resume(returning: nil) + } else { + // No data and not complete — shouldn't happen but return nil + continuation.resume(returning: nil) + } + } + } + } + + // MARK: - State + + var isConnected: Bool { + state == .connected + } + + private func updateState(_ newState: ConnectionState) { + state = newState + stateCallback?(newState) + } +} diff --git a/ios_client 0.8.5/Kecalek/Network/ProtocolHandler.swift b/ios_client 0.8.5/Kecalek/Network/ProtocolHandler.swift new file mode 100644 index 0000000..5e1bae5 --- /dev/null +++ b/ios_client 0.8.5/Kecalek/Network/ProtocolHandler.swift @@ -0,0 +1,88 @@ +import Foundation + +/// Newline-delimited JSON protocol handler. +/// Matches Python: protocol.py build_request, build_response, parse_message, encode_binary, decode_binary +enum ProtocolHandler: Sendable { + + /// Build a request message (newline-terminated JSON). + /// Matches Python: build_request(msg_type, request_id=None, **kwargs) + nonisolated static func buildRequest(type: String, requestId: String? = nil, params: [String: Any] = [:]) throws -> Data { + var msg: [String: Any] = ["type": type] + if let requestId = requestId { + msg["request_id"] = requestId + } + // Merge params into msg + for (key, value) in params { + msg[key] = value + } + + let jsonData = try JSONSerialization.data(withJSONObject: msg) + guard jsonData.count < Constants.maxMessageBytes else { + throw NetworkError.messageTooLarge + } + return jsonData + Data([0x0A]) // newline + } + + /// Build a response message (newline-terminated JSON). + nonisolated static func buildResponse(type: String, status: String, data: [String: Any]? = nil, requestId: String? = nil) throws -> Data { + var msg: [String: Any] = ["type": type, "status": status] + if let data = data { + msg["data"] = data + } + if let requestId = requestId { + msg["request_id"] = requestId + } + + let jsonData = try JSONSerialization.data(withJSONObject: msg) + guard jsonData.count < Constants.maxMessageBytes else { + throw NetworkError.messageTooLarge + } + return jsonData + Data([0x0A]) + } + + /// Parse a single protocol message from bytes. + /// Matches Python: parse_message(line) + nonisolated static func parseMessage(_ data: Data) throws -> [String: Any] { + let trimmed = Self.trimmingNewlines(data) + guard !trimmed.isEmpty else { + throw NetworkError.protocolError("Empty message") + } + guard let obj = try JSONSerialization.jsonObject(with: trimmed) as? [String: Any] else { + throw NetworkError.protocolError("Message is not a JSON object") + } + return obj + } + + /// Encode bytes to base64 string. + /// Matches Python: encode_binary(data) + nonisolated static func encodeBinary(_ data: Data) -> String { + data.base64EncodedString(options: []) + } + + /// Decode base64 string to bytes. + /// Matches Python: decode_binary(data) + nonisolated static func decodeBinary(_ string: String) throws -> Data { + guard let data = Data(base64Encoded: string, options: .ignoreUnknownCharacters) else { + throw CryptoError.invalidBase64 + } + return data + } + + /// Generate a new request ID (UUID string). + nonisolated static func newRequestId() -> String { + UUID().uuidString + } + + // MARK: - Helpers + + private nonisolated static func trimmingNewlines(_ data: Data) -> Data { + var result = data + while let last = result.last, last == 0x0A || last == 0x0D { + result.removeLast() + } + while let first = result.first, first == 0x0A || first == 0x0D { + result.removeFirst() + } + return result + } +} diff --git a/ios_client 0.8.5/Kecalek/Utilities/Constants.swift b/ios_client 0.8.5/Kecalek/Utilities/Constants.swift new file mode 100644 index 0000000..d82e722 --- /dev/null +++ b/ios_client 0.8.5/Kecalek/Utilities/Constants.swift @@ -0,0 +1,38 @@ +import Foundation + +enum Constants: Sendable { + nonisolated static let version = "0.8.5" + nonisolated static let maxMessageBytes = 65536 + nonisolated static let maxImageBytes = 5 * 1024 * 1024 // 5 MB + nonisolated static let maxFileBytes = 50 * 1024 * 1024 // 50 MB + nonisolated static let imageChunkSize = 32768 // 32 KB (matches Python IMAGE_CHUNK_SIZE) + nonisolated static let selfDeviceId = "00000000-0000-0000-0000-000000000000" + + nonisolated static let opkReplenishThreshold = 20 + nonisolated static let opkBatchSize = 50 + nonisolated static let spkRotationDays = 7 + + nonisolated static let maxSkip = 256 + nonisolated static let maxSenderKeySkip = 256 + + nonisolated static let deviceBundleCacheTTL: TimeInterval = 300 // 5 minutes + nonisolated static let sendReceiveTimeout: TimeInterval = 30 + nonisolated static let reconnectBaseDelay: TimeInterval = 1 + nonisolated static let reconnectMaxDelay: TimeInterval = 30 + + nonisolated static let pbkdf2Iterations: UInt32 = 600_000 + nonisolated static let ecp1Magic = Data([0x45, 0x43, 0x50, 0x31]) // "ECP1" + + // HKDF info/salt strings matching Python + nonisolated static let x3dhInfo = "EncryptedChat_X3DH" + nonisolated static let rootKeyInfo = "EncryptedChat_RootKey" + nonisolated static let selfEncryptionSalt = "self_encryption" + nonisolated static let selfEncryptionInfo = "EncryptedChat_SelfKey" + nonisolated static let localStorageSalt = "local_storage" + nonisolated static let localStorageInfo = "EncryptedChat_LocalStorage" + nonisolated static let senderKeyChainInfo = "SenderKeyChain" + + // Server connection defaults + nonisolated static let defaultHost = "chat.ai-tech.news" + nonisolated static let defaultPort: UInt16 = 9999 +} diff --git a/ios_client 0.8.5/Kecalek/Utilities/Extensions.swift b/ios_client 0.8.5/Kecalek/Utilities/Extensions.swift new file mode 100644 index 0000000..8cc2add --- /dev/null +++ b/ios_client 0.8.5/Kecalek/Utilities/Extensions.swift @@ -0,0 +1,168 @@ +import Foundation +import CryptoKit + +// MARK: - Data ↔ Hex + +extension Data { + /// Convert data to lowercase hex string + var hexString: String { + map { String(format: "%02x", $0) }.joined() + } + + /// Initialize Data from a hex string + init?(hexString: String) { + let hex = hexString.lowercased() + guard hex.count % 2 == 0 else { return nil } + var data = Data(capacity: hex.count / 2) + var index = hex.startIndex + while index < hex.endIndex { + let nextIndex = hex.index(index, offsetBy: 2) + guard let byte = UInt8(hex[index.. Data { + var data = Data(count: count) + data.withUnsafeMutableBytes { ptr in + _ = SecRandomCopyBytes(kSecRandomDefault, count, ptr.baseAddress!) + } + return data + } +} + +// MARK: - Data ↔ Base64 (Protocol Wire Format) + +extension Data { + /// Encode to standard base64 string (matching Python's base64.b64encode) + func base64EncodedString() -> String { + self.base64EncodedString(options: []) + } + + /// Decode from base64 string + static func fromBase64(_ string: String) throws -> Data { + // Try standard base64 first, then URL-safe + if let data = Data(base64Encoded: string, options: .ignoreUnknownCharacters) { + return data + } + throw CryptoError.invalidBase64 + } +} + +// MARK: - UInt32 Big-Endian + +extension UInt32 { + var bigEndianData: Data { + var value = self.bigEndian + return Data(bytes: &value, count: 4) + } +} + +// MARK: - CryptoKit Key → Data + +extension Curve25519.KeyAgreement.PublicKey { + nonisolated var rawData: Data { + Data(rawRepresentation) + } +} + +extension Curve25519.KeyAgreement.PrivateKey { + nonisolated var rawData: Data { + Data(rawRepresentation) + } +} + +extension Curve25519.Signing.PublicKey { + nonisolated var rawData: Data { + Data(rawRepresentation) + } +} + +extension Curve25519.Signing.PrivateKey { + nonisolated var rawData: Data { + Data(rawRepresentation) + } +} + +// MARK: - String helpers + +extension String { + /// Trim whitespace and newlines + var trimmed: String { + trimmingCharacters(in: .whitespacesAndNewlines) + } +} + +// MARK: - Date Parsing (server sends ISO8601 with or without timezone) + +enum DateParsing { + private static let iso8601WithTZ: ISO8601DateFormatter = { + let f = ISO8601DateFormatter() + f.formatOptions = [.withInternetDateTime, .withFractionalSeconds] + return f + }() + + private static let iso8601Basic: ISO8601DateFormatter = { + let f = ISO8601DateFormatter() + f.formatOptions = [.withInternetDateTime] + return f + }() + + private static let noTZ: DateFormatter = { + let f = DateFormatter() + f.dateFormat = "yyyy-MM-dd'T'HH:mm:ss" + f.timeZone = TimeZone(identifier: "UTC") + f.locale = Locale(identifier: "en_US_POSIX") + return f + }() + + /// Parse ISO8601 date string — handles with/without timezone, with/without fractional seconds + static func parse(_ string: String) -> Date? { + iso8601WithTZ.date(from: string) + ?? iso8601Basic.date(from: string) + ?? noTZ.date(from: string) + } + + /// Format Date to ISO8601 string (for after_ts / since_ts parameters) + static func format(_ date: Date) -> String { + iso8601WithTZ.string(from: date) + } +} + +// MARK: - Dictionary merge helper + +extension Dictionary where Key == String, Value == Any { + nonisolated func string(for key: String) -> String? { + self[key] as? String + } + + nonisolated func int(for key: String) -> Int? { + if let i = self[key] as? Int { return i } + if let s = self[key] as? String, let i = Int(s) { return i } + return nil + } + + nonisolated func dict(for key: String) -> [String: Any]? { + self[key] as? [String: Any] + } + + nonisolated func array(for key: String) -> [[String: Any]]? { + self[key] as? [[String: Any]] + } + + nonisolated func data(for key: String) -> Data? { + if let hex = self[key] as? String { + return Data(hexString: hex) + } + return nil + } + + nonisolated func bool(for key: String) -> Bool? { + if let b = self[key] as? Bool { return b } + if let i = self[key] as? Int { return i != 0 } + return nil + } +} diff --git a/ios_client 0.8.5/Kecalek/ViewModels/AuthViewModel.swift b/ios_client 0.8.5/Kecalek/ViewModels/AuthViewModel.swift new file mode 100644 index 0000000..fb7edf9 --- /dev/null +++ b/ios_client 0.8.5/Kecalek/ViewModels/AuthViewModel.swift @@ -0,0 +1,192 @@ +import Foundation +import SwiftUI + +@Observable +final class AuthViewModel { + var email = "" + var password = "" + var confirmPassword = "" + var username = "" + var confirmationCode = "" + + var isLoading = false + var errorMessage: String? + var showConfirmation = false + var registrationMessage: String? + + var serverHost = Constants.defaultHost + var serverPort = String(Constants.defaultPort) + + var hasSavedCredentials = false + var isBiometricLoading = false + + enum AuthMode { + case login, register, pairing + } + var mode: AuthMode = .login + + func checkSavedCredentials() { + hasSavedCredentials = KeychainService.hasSavedCredentials() && KeychainService.isBiometricAvailable() + } + + func login(appState: AppState) async { + guard !email.isEmpty, !password.isEmpty else { + errorMessage = "Email and password are required" + return + } + + isLoading = true + errorMessage = nil + + // Only connect if not already connected + if await !appState.chatClient.isConnected { + do { + let port = UInt16(serverPort) ?? Constants.defaultPort + try await appState.chatClient.connect(host: serverHost, port: port) + } catch { + isLoading = false + errorMessage = "Connection failed: \(error.localizedDescription)" + return + } + } + + let (success, message) = await appState.chatClient.login(email: email, password: password) + isLoading = false + + if success { + appState.email = email + appState.isLoggedIn = true + appState.connectionStatus = .connected + appState.startConnectionMonitor() + if let userId = await appState.chatClient.userId { + appState.currentUser = User(id: userId, username: await appState.chatClient.username, email: email) + } + + // Save credentials for biometric login next time + if KeychainService.isBiometricAvailable() { + let port = UInt16(serverPort) ?? Constants.defaultPort + try? KeychainService.saveCredentials( + email: email, password: password, + host: serverHost, port: port + ) + } + + // Clear password from memory after successful login + password = "" + confirmPassword = "" + } else { + errorMessage = message + } + } + + func biometricLogin(appState: AppState) async { + isBiometricLoading = true + errorMessage = nil + + do { + let creds = try KeychainService.loadCredentials() + email = creds.email + password = creds.password + serverHost = creds.host + serverPort = String(creds.port) + isBiometricLoading = false + + await login(appState: appState) + + // If login failed, reset to defaults so the form isn't stuck on stale values + if !appState.isLoggedIn { + serverHost = Constants.defaultHost + serverPort = String(Constants.defaultPort) + password = "" + KeychainService.deleteCredentials() + hasSavedCredentials = false + } + } catch KeychainService.KeychainError.biometricFailed { + isBiometricLoading = false + // User cancelled — just let them type manually + } catch { + isBiometricLoading = false + errorMessage = error.localizedDescription + } + } + + func register(appState: AppState) async { + guard !email.isEmpty, !password.isEmpty, !username.isEmpty else { + errorMessage = "All fields are required" + return + } + guard password == confirmPassword else { + errorMessage = "Passwords don't match" + return + } + + isLoading = true + errorMessage = nil + + #if DEBUG + print("DEBUG register: connecting to \(serverHost):\(serverPort)") + #endif + if await !appState.chatClient.isConnected { + do { + let port = UInt16(serverPort) ?? Constants.defaultPort + try await appState.chatClient.connect(host: serverHost, port: port) + #if DEBUG + print("DEBUG register: connected successfully") + #endif + } catch { + isLoading = false + #if DEBUG + print("DEBUG register: connection failed - \(error)") + #endif + errorMessage = "Connection failed: \(error.localizedDescription)" + return + } + } + + #if DEBUG + print("DEBUG register: calling chatClient.register") + #endif + let (success, message) = await appState.chatClient.register(username: username, password: password, email: email) + isLoading = false + + #if DEBUG + print("DEBUG AuthViewModel: register returned success=\(success), message=\(message)") + #endif + + if success { + registrationMessage = message + showConfirmation = true + #if DEBUG + print("DEBUG AuthViewModel: showConfirmation set to true") + #endif + } else { + errorMessage = message + #if DEBUG + print("DEBUG AuthViewModel: errorMessage set to \(message)") + #endif + } + } + + func confirmRegistration(appState: AppState) async { + guard !confirmationCode.isEmpty else { + errorMessage = "Enter the confirmation code" + return + } + + isLoading = true + errorMessage = nil + + let (success, message) = await appState.chatClient.confirmRegistration( + email: email, username: username, code: confirmationCode + ) + isLoading = false + + if success { + registrationMessage = message + // Auto-login after registration + await login(appState: appState) + } else { + errorMessage = message + } + } +} diff --git a/ios_client 0.8.5/Kecalek/ViewModels/ChatViewModel.swift b/ios_client 0.8.5/Kecalek/ViewModels/ChatViewModel.swift new file mode 100644 index 0000000..19b11e9 --- /dev/null +++ b/ios_client 0.8.5/Kecalek/ViewModels/ChatViewModel.swift @@ -0,0 +1,356 @@ +import Foundation +import SwiftUI + +@Observable +final class ChatViewModel { + var messages: [Message] = [] + var isLoading = false + var isSending = false + var errorMessage: String? + var searchQuery = "" + var searchResults: [String] = [] // message IDs matching search + var currentSearchIndex = 0 + + private var notificationTask: Task? + + func loadMessages(convId: String, chatClient: ChatClient) async { + let email = await chatClient.email + let cacheKey = await chatClient.cacheKey + + // 1. Load from cache + let cachedDicts = MessageCache.load(email: email, convId: convId, cacheKey: cacheKey) + let cached = cachedDicts?.compactMap { Message.fromCacheDict($0) } ?? [] + + if !cached.isEmpty { + // Cache hit — show immediately, no spinner + messages = cached.sorted { $0.createdAt < $1.createdAt } + } else { + // No cache — show spinner (first open) + isLoading = true + } + + // 2. Determine after_ts from newest cached message + let newestCached = messages.last + + // 3. Fetch from server + let serverMessages: [Message] + if let newest = newestCached { + let afterTs = DateParsing.format(newest.createdAt) + #if DEBUG + print("DEBUG getMessages after_ts=\(afterTs)") + #endif + serverMessages = await chatClient.getMessages(convId: convId, limit: 50, afterTs: afterTs) + } else { + serverMessages = await chatClient.getMessages(convId: convId, limit: 50) + } + + // 4. Merge + if newestCached != nil { + // Incremental: dedup by ID, append new, sort + let existingIds = Set(messages.map(\.id)) + let newMessages = serverMessages.filter { !existingIds.contains($0.id) } + if !newMessages.isEmpty { + messages.append(contentsOf: newMessages) + messages.sort { $0.createdAt < $1.createdAt } + } + } else { + // Full fetch: replace + messages = serverMessages + } + + // 5. Sync deleted (only for incremental) + if let newest = newestCached { + let afterTs = DateParsing.format(newest.createdAt) + #if DEBUG + print("DEBUG get_deleted_since since_ts=\(afterTs)") + #endif + let deletedIds = await chatClient.getDeletedSince(convId: convId, sinceTs: afterTs) + if !deletedIds.isEmpty { + messages.removeAll { deletedIds.contains($0.id) } + } + } + + // 6. Loading done + isLoading = false + + // 7. Save to cache + await saveCache(convId: convId, chatClient: chatClient) + + // 8. Mark entire conversation as read (server-side bulk mark) + // This handles messages not in cache (e.g. failed to decrypt or never fetched) + await chatClient.markConversationRead(convId: convId) + // Update local readBy for cached messages so cache reflects read state + let currentUserId = await chatClient.userId ?? "" + var anyUpdated = false + for i in messages.indices { + if !messages[i].isMine(currentUserId: currentUserId) && !messages[i].readBy.contains(currentUserId) { + messages[i].readBy.insert(currentUserId) + anyUpdated = true + } + } + if anyUpdated { + await saveCache(convId: convId, chatClient: chatClient) + } + } + + func loadOlderMessages(convId: String, chatClient: ChatClient) async { + let older = await chatClient.getMessages(convId: convId, limit: 50, offset: messages.count) + messages.insert(contentsOf: older, at: 0) + await saveCache(convId: convId, chatClient: chatClient) + } + + func sendMessage(convId: String, text: String, members: [ConversationMember], + chatClient: ChatClient, replyTo: String? = nil) async { + guard !text.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty else { return } + + isSending = true + errorMessage = nil + + let (success, msg, sentMessage) = await chatClient.sendMessage( + convId: convId, text: text, members: members, replyTo: replyTo + ) + + isSending = false + + if !success { + errorMessage = msg + } else if let sentMessage = sentMessage { + // Append locally — don't reload from server (ratchet keys are one-time) + if !messages.contains(where: { $0.id == sentMessage.id }) { + messages.append(sentMessage) + } + await saveCache(convId: convId, chatClient: chatClient) + } + } + + func deleteMessage(messageId: String, convId: String, chatClient: ChatClient) async { + let success = await chatClient.deleteMessage(messageId: messageId, convId: convId) + if success { + messages.removeAll { $0.id == messageId } + await saveCache(convId: convId, chatClient: chatClient) + } + } + + func saveCache(convId: String, chatClient: ChatClient) async { + let email = await chatClient.email + let cacheKey = await chatClient.cacheKey + let dicts = messages.map { $0.toCacheDict() } + try? MessageCache.save(email: email, convId: convId, messages: dicts, cacheKey: cacheKey) + } + + func search(query: String) { + searchQuery = query + if query.isEmpty { + searchResults = [] + currentSearchIndex = 0 + return + } + let lower = query.lowercased() + searchResults = messages.filter { $0.text?.lowercased().contains(lower) == true }.map(\.id) + currentSearchIndex = searchResults.isEmpty ? 0 : searchResults.count - 1 + } + + func nextSearchResult() { + guard !searchResults.isEmpty else { return } + currentSearchIndex = (currentSearchIndex + 1) % searchResults.count + } + + func prevSearchResult() { + guard !searchResults.isEmpty else { return } + currentSearchIndex = (currentSearchIndex - 1 + searchResults.count) % searchResults.count + } + + func startNotificationListener(convId: String, chatClient: ChatClient) { + notificationTask?.cancel() + notificationTask = Task { + for await notification in await chatClient.makeNotificationStream() { + await handleNotification(notification, convId: convId, chatClient: chatClient) + } + } + } + + @MainActor + private func handleNotification(_ notification: ChatNotification, convId: String, chatClient: ChatClient) { + switch notification { + case .newMessage(let data): + if data["conversation_id"] as? String == convId { + Task { + if let message = await chatClient.decryptNotification(data) { + await MainActor.run { + // Deduplicate — sent messages are already appended locally + if !messages.contains(where: { $0.id == message.id }) { + messages.append(message) + } + } + await saveCache(convId: convId, chatClient: chatClient) + // Only mark as read if from someone else + let myId = await chatClient.userId ?? "" + if message.senderId != myId { + await chatClient.markRead(convId: convId, messageIds: [message.id]) + } + await chatClient.flushSelfEncrypt() + } + } + } + case .messageDeleted(let data): + if let msgId = data["message_id"] as? String { + messages.removeAll { $0.id == msgId } + Task { + await saveCache(convId: convId, chatClient: chatClient) + } + } + case .messagesRead(let data): + if let readUserId = data["user_id"] as? String, + let msgIds = data["message_ids"] as? [String] { + for i in messages.indices { + if msgIds.contains(messages[i].id) { + messages[i].readBy.insert(readUserId) + } + } + } + case .messageReacted(let data): + if let msgId = data["message_id"] as? String, + let reactUserId = data["user_id"] as? String, + let reaction = data["reaction"] as? String, + let action = data["action"] as? String, + let idx = messages.firstIndex(where: { $0.id == msgId }) { + if action == "add" { + let newReaction = MessageReaction(userId: reactUserId, reaction: reaction, createdAt: Date()) + if !messages[idx].reactions.contains(where: { $0.userId == reactUserId && $0.reaction == reaction }) { + messages[idx].reactions.append(newReaction) + } + } else { + messages[idx].reactions.removeAll { $0.userId == reactUserId && $0.reaction == reaction } + } + Task { await saveCache(convId: convId, chatClient: chatClient) } + } + case .messagePinned(let data): + if let msgId = data["message_id"] as? String, + let pinUserId = data["user_id"] as? String, + let idx = messages.firstIndex(where: { $0.id == msgId }) { + messages[idx].pinnedAt = Date() + messages[idx].pinnedBy = pinUserId + Task { await saveCache(convId: convId, chatClient: chatClient) } + } + case .messageUnpinned(let data): + if let msgId = data["message_id"] as? String, + let idx = messages.firstIndex(where: { $0.id == msgId }) { + messages[idx].pinnedAt = nil + messages[idx].pinnedBy = nil + Task { await saveCache(convId: convId, chatClient: chatClient) } + } + case .messageDelivered(let data): + // Delivery receipt — message was successfully received by recipient + if let msgId = data["message_id"] as? String, + let idx = messages.firstIndex(where: { $0.id == msgId }) { + messages[idx].readBy.insert("__delivered__") + Task { await saveCache(convId: convId, chatClient: chatClient) } + } + default: + break + } + } + + func reactToMessage(messageId: String, convId: String, reaction: String, + currentUserId: String, chatClient: ChatClient) async { + guard let idx = messages.firstIndex(where: { $0.id == messageId }) else { return } + let existingReaction = messages[idx].reactions.first { $0.userId == currentUserId } + let hasSameReaction = existingReaction?.reaction == reaction + let savedReactions = messages[idx].reactions + + // Optimistic update + if hasSameReaction { + // Tapping same emoji — remove it + messages[idx].reactions.removeAll { $0.userId == currentUserId } + } else { + // Remove any previous reaction from this user, then add new one + messages[idx].reactions.removeAll { $0.userId == currentUserId } + messages[idx].reactions.append(MessageReaction(userId: currentUserId, reaction: reaction, createdAt: Date())) + } + + // If user had a different reaction, remove it on server first + if let old = existingReaction, old.reaction != reaction { + let _ = await chatClient.reactMessage(messageId: messageId, conversationId: convId, + reaction: old.reaction, action: "remove") + } + + // Add or remove the target reaction on server + let action = hasSameReaction ? "remove" : "add" + let success = await chatClient.reactMessage(messageId: messageId, conversationId: convId, + reaction: reaction, action: action) + if !success { + // Revert on failure + messages[idx].reactions = savedReactions + } + await saveCache(convId: convId, chatClient: chatClient) + } + + func pinMessage(messageId: String, convId: String, pin: Bool, + chatClient: ChatClient) async { + guard let idx = messages.firstIndex(where: { $0.id == messageId }) else { return } + + // Optimistic update + if pin { + messages[idx].pinnedAt = Date() + messages[idx].pinnedBy = await chatClient.userId + } else { + messages[idx].pinnedAt = nil + messages[idx].pinnedBy = nil + } + + let success = await chatClient.pinMessage(messageId: messageId, conversationId: convId, + action: pin ? "pin" : "unpin") + if !success { + // Revert on failure + if pin { + messages[idx].pinnedAt = nil + messages[idx].pinnedBy = nil + } + } + await saveCache(convId: convId, chatClient: chatClient) + } + + // MARK: - Forward Message + + func forwardMessage(message: Message, targetConvId: String, + targetMembers: [ConversationMember], chatClient: ChatClient) async -> Bool { + var originalMsg: [String: Any] = [ + "text": message.text ?? "", + "sender": message.senderUsername, + "conversation_id": message.conversationId, + "message_id": message.id, + ] + if let file = message.file { + originalMsg["file"] = [ + "file_id": file.fileId, + "aes_key": file.aesKey, + "iv": file.iv, + "filename": file.filename, + "size": file.size, + "mime_type": file.mimeType, + ] as [String: Any] + } + if let image = message.image { + var imgDict: [String: Any] = [ + "file_id": image.fileId, + "aes_key": image.aesKey, + "iv": image.iv, + "filename": image.filename, + "size": image.size, + ] + if let thumb = image.thumbnail { imgDict["thumbnail"] = thumb } + originalMsg["image"] = imgDict + } + + let (success, _, _) = await chatClient.forwardMessage( + targetConvId: targetConvId, originalMsg: originalMsg, + targetMembers: targetMembers + ) + return success + } + + func stop() { + notificationTask?.cancel() + notificationTask = nil + } +} diff --git a/ios_client 0.8.5/Kecalek/ViewModels/ConversationListVM.swift b/ios_client 0.8.5/Kecalek/ViewModels/ConversationListVM.swift new file mode 100644 index 0000000..d20c846 --- /dev/null +++ b/ios_client 0.8.5/Kecalek/ViewModels/ConversationListVM.swift @@ -0,0 +1,246 @@ +import Foundation +import SwiftUI + +@Observable +final class ConversationListVM { + var conversations: [Conversation] = [] + var invitations: [Invitation] = [] + var onlineUsers: Set = [] + var unreadCounts: [String: Int] = [:] + var favorites: Set = [] + var avatarCache: [String: Data] = [:] // convId -> avatar image data + var isLoading = false + + private var notificationTask: Task? + private var avatarTask: Task? + private var refreshTask: Task? + private var localKey: Data? + private var email: String = "" + private var lastRefreshTime: Date = .distantPast + + func load(chatClient: ChatClient, email: String) async { + isLoading = true + self.email = email + + // Load favorites from disk (encrypted with localKey) + localKey = await chatClient.localKey + favorites = KeyStorage.loadFavorites(email: email, localKey: localKey) + + let currentUserId = await chatClient.userId ?? "" + + // Load cached conversations immediately (show while fetching from server) + if let cached = MessageCache.loadConversations(email: email, cacheKey: localKey) { + conversations = sortConversations(cached, currentUserId: currentUserId) + for conv in conversations where conv.unreadCount > 0 { + unreadCounts[conv.id] = conv.unreadCount + } + } + + // Load cached avatars from disk + let diskAvatars = MessageCache.loadAllAvatars(email: email, cacheKey: localKey) + if !diskAvatars.isEmpty { + avatarCache = diskAvatars + } + + // Fetch conversations from server + let convs = await chatClient.listConversations() + if !convs.isEmpty { + // Sync unread counts from server (authoritative source) + for conv in convs { + unreadCounts[conv.id] = conv.unreadCount + } + conversations = sortConversations(convs, currentUserId: currentUserId) + // Save to cache + MessageCache.saveConversations(email: email, conversations: convs, cacheKey: localKey) + } + + // Fetch invitations + invitations = await chatClient.listInvitations() + + isLoading = false + lastRefreshTime = Date() + + // Start notification listener + startNotificationListener(chatClient: chatClient, email: email) + + // Read initial online users stored in ChatClient + // (online_users notification arrives during login before any subscriber exists) + onlineUsers = await chatClient.onlineUserIds + + // Load avatars in background (non-blocking) + avatarTask?.cancel() + avatarTask = Task { await loadAvatars(chatClient: chatClient, currentUserId: currentUserId) } + } + + func refresh(chatClient: ChatClient) async { + // Debounce: skip if refreshed < 2s ago + guard Date().timeIntervalSince(lastRefreshTime) > 2 else { + #if DEBUG + print("DEBUG ConversationListVM: refresh debounced") + #endif + return + } + lastRefreshTime = Date() + + let currentUserId = await chatClient.userId ?? "" + let convs = await chatClient.listConversations() + if !convs.isEmpty { + // Sync unread counts from server (authoritative source) + for conv in convs { + unreadCounts[conv.id] = conv.unreadCount + } + conversations = sortConversations(convs, currentUserId: currentUserId) + // Save to cache + MessageCache.saveConversations(email: email, conversations: convs, cacheKey: localKey) + } + invitations = await chatClient.listInvitations() + + // Refresh avatars in background + avatarTask?.cancel() + avatarTask = Task { await loadAvatars(chatClient: chatClient, currentUserId: currentUserId) } + } + + func toggleFavorite(convId: String, email: String) { + if favorites.contains(convId) { + favorites.remove(convId) + } else { + favorites.insert(convId) + } + try? KeyStorage.saveFavorites(email: email, favorites: favorites, localKey: localKey) + + // Re-sort + let userId = conversations.first?.createdBy ?? "" + conversations = sortConversations(conversations, currentUserId: userId) + } + + func forceRefresh(chatClient: ChatClient) async { + lastRefreshTime = .distantPast + await refresh(chatClient: chatClient) + } + + func updateAvatar(convId: String, data: Data) { + avatarCache[convId] = data + // Persist to disk so it survives load() re-reads + MessageCache.saveAvatar(email: email, key: convId, data: data, cacheKey: localKey) + } + + func markConversationRead(convId: String) { + unreadCounts[convId] = 0 + } + + func incrementUnread(convId: String) { + unreadCounts[convId, default: 0] += 1 + } + + private func sortConversations(_ convs: [Conversation], currentUserId: String) -> [Conversation] { + var result = convs.map { conv -> Conversation in + var c = conv + c.isFavorite = favorites.contains(conv.id) + c.unreadCount = unreadCounts[conv.id] ?? conv.unreadCount + return c + } + + result.sort { a, b in + // Favorites first + if a.isFavorite != b.isFavorite { return a.isFavorite } + // Online DMs next + let aOnline = a.dmPartnerId(currentUserId: currentUserId).map { onlineUsers.contains($0) } ?? false + let bOnline = b.dmPartnerId(currentUserId: currentUserId).map { onlineUsers.contains($0) } ?? false + if aOnline != bOnline { return aOnline } + // Alphabetical + return a.displayName(currentUserId: currentUserId).lowercased() < b.displayName(currentUserId: currentUserId).lowercased() + } + + return result + } + + private func startNotificationListener(chatClient: ChatClient, email: String) { + notificationTask?.cancel() + notificationTask = Task { + for await notification in await chatClient.makeNotificationStream() { + await handleNotification(notification, chatClient: chatClient, email: email) + } + } + } + + @MainActor + private func handleNotification(_ notification: ChatNotification, chatClient: ChatClient, email: String) { + switch notification { + case .newMessage(let data): + if let convId = data["conversation_id"] as? String { + incrementUnread(convId: convId) + } + case .onlineUsers(let userIds): + onlineUsers = Set(userIds) + case .userOnline(let userId): + onlineUsers.insert(userId) + case .userOffline(let userId): + onlineUsers.remove(userId) + case .conversationCreated, .memberAdded, .memberRemoved, .conversationRenamed, .conversationDeleted: + refreshTask?.cancel() + refreshTask = Task { await refresh(chatClient: chatClient) } + case .groupInvitation: + Task { invitations = await chatClient.listInvitations() } + case .reconnected: + #if DEBUG + print("DEBUG ConversationListVM: reconnected — refreshing") + #endif + refreshTask?.cancel() + refreshTask = Task { await refresh(chatClient: chatClient) } + case .connectionStateChanged(let connected): + if !connected { + #if DEBUG + print("DEBUG ConversationListVM: disconnected") + #endif + } + default: + break + } + } + + private func loadAvatars(chatClient: ChatClient, currentUserId: String) async { + await withTaskGroup(of: (String, Data?).self) { group in + for conv in conversations { + let convId = conv.id + // Skip if already cached in memory + if avatarCache[convId] != nil { continue } + if conv.isGroup { + // Only fetch if group has an avatar file + if conv.avatarFile != nil { + group.addTask { + let data = await chatClient.getGroupAvatar(convId: convId) + return (convId, data) + } + } + } else { + // DM: fetch partner's avatar + if let partnerId = conv.dmPartnerId(currentUserId: currentUserId) { + group.addTask { + let data = await chatClient.getAvatar(userId: partnerId) + return (convId, data) + } + } + } + } + + let emailCapture = email + let keyCapture = localKey + for await (convId, data) in group { + if let data = data { + avatarCache[convId] = data + // Save to disk cache + MessageCache.saveAvatar(email: emailCapture, key: convId, data: data, cacheKey: keyCapture) + } + } + } + } + + func stop() { + notificationTask?.cancel() + notificationTask = nil + avatarTask?.cancel() + avatarTask = nil + refreshTask?.cancel() + refreshTask = nil + } +} diff --git a/ios_client 0.8.5/Kecalek/ViewModels/ProfileViewModel.swift b/ios_client 0.8.5/Kecalek/ViewModels/ProfileViewModel.swift new file mode 100644 index 0000000..43ad02a --- /dev/null +++ b/ios_client 0.8.5/Kecalek/ViewModels/ProfileViewModel.swift @@ -0,0 +1,98 @@ +import Foundation +import SwiftUI + +@Observable +final class ProfileViewModel { + var profile: UserProfile? + var avatarData: Data? + var isLoading = false + var isSaving = false + var errorMessage: String? + + // Editable fields + var phone = "" + var phoneVisible = false + var location = "" + var locationVisible = false + + func loadProfile(userId: String? = nil, chatClient: ChatClient) async { + isLoading = true + profile = await chatClient.getProfile(userId: userId) + isLoading = false + + if let p = profile { + phone = p.phone ?? "" + phoneVisible = p.phoneVisible + location = p.location ?? "" + locationVisible = p.locationVisible + } + + // Load avatar + let clientUserId = await chatClient.userId + let uid = userId ?? clientUserId ?? "" + if !uid.isEmpty { + avatarData = await chatClient.getAvatar(userId: uid) + } + } + + @discardableResult + func saveProfile(chatClient: ChatClient) async -> Bool { + isSaving = true + errorMessage = nil + + let success = await chatClient.updateProfile( + phone: phone.isEmpty ? nil : phone, + phoneVisible: phoneVisible, + location: location.isEmpty ? nil : location, + locationVisible: locationVisible + ) + + isSaving = false + + if !success { + errorMessage = "Failed to update profile" + } + return success + } + + func uploadAvatar(imageData: Data, chatClient: ChatClient) async { + isSaving = true + errorMessage = nil + let (success, msg) = await chatClient.updateAvatar(imageData: imageData) + isSaving = false + + if success { + // Reload avatar from server (it was resized/compressed) + let clientUserId = await chatClient.userId ?? "" + avatarData = await chatClient.getAvatar(userId: clientUserId) + } else { + errorMessage = msg.isEmpty ? "Failed to upload avatar" : msg + } + } + + // MARK: - Username Change + + func changeUsername(newUsername: String, chatClient: ChatClient) async -> Bool { + isSaving = true + errorMessage = nil + let (success, msg) = await chatClient.changeUsername(newUsername: newUsername) + isSaving = false + if !success { + errorMessage = msg + } + return success + } + + // MARK: - Password Change + + func changePassword(oldPassword: String, newPassword: String, chatClient: ChatClient) async -> Bool { + isSaving = true + errorMessage = nil + let (success, msg) = await chatClient.changePassword(oldPassword: oldPassword, newPassword: newPassword) + isSaving = false + if !success { + errorMessage = msg + } + return success + } +} diff --git a/ios_client 0.8.5/Kecalek/ViewModels/VerificationVM.swift b/ios_client 0.8.5/Kecalek/ViewModels/VerificationVM.swift new file mode 100644 index 0000000..fd78d2f --- /dev/null +++ b/ios_client 0.8.5/Kecalek/ViewModels/VerificationVM.swift @@ -0,0 +1,60 @@ +import Foundation +import SwiftUI + +@Observable +final class VerificationVM { + var safetyNumber: String? + var myFingerprint: String? + var peerFingerprint: String? + var verificationStatus: String = "unverified" // "verified", "trusted", "unverified" + var qrCodeData: Data? + var scanResult: String? + var scanSuccess: Bool? + var isLoading = false + var errorMessage: String? + + func loadVerification(peerUserId: String, chatClient: ChatClient) async { + isLoading = true + + // Ensure peer's identity key is fetched (needed for safety number & verification) + _ = await chatClient.getPeerIdentityKey(userId: peerUserId) + + // Get safety number + safetyNumber = await chatClient.getSafetyNumber(peerUserId: peerUserId) + + // Get fingerprints + myFingerprint = await chatClient.getMyFingerprint() + peerFingerprint = await chatClient.getPeerFingerprint(peerUserId: peerUserId) + + // Get verification status + verificationStatus = await chatClient.getVerificationStatus(userId: peerUserId) + + // Get QR code data for display + qrCodeData = await chatClient.getVerificationQRData() + + isLoading = false + } + + func verifyContact(peerUserId: String, chatClient: ChatClient) async { + guard let peerIK = await chatClient.getPeerIdentityKey(userId: peerUserId) else { + errorMessage = "No identity key on record for this user." + return + } + await chatClient.verifyContact(userId: peerUserId, identityKey: peerIK, method: "manual") + verificationStatus = "verified" + } + + func unverifyContact(peerUserId: String, chatClient: ChatClient) async { + await chatClient.unverifyContact(userId: peerUserId) + verificationStatus = "trusted" + } + + func verifyQRCode(data: Data, chatClient: ChatClient) async { + let (success, _, message) = await chatClient.verifyQRCode(qrData: data) + scanSuccess = success + scanResult = message + if success { + verificationStatus = "verified" + } + } +} diff --git a/ios_client 0.8.5/Kecalek/Views/Auth/AuthorizeDeviceView.swift b/ios_client 0.8.5/Kecalek/Views/Auth/AuthorizeDeviceView.swift new file mode 100644 index 0000000..fa1ff20 --- /dev/null +++ b/ios_client 0.8.5/Kecalek/Views/Auth/AuthorizeDeviceView.swift @@ -0,0 +1,82 @@ +import SwiftUI + +struct AuthorizeDeviceView: View { + var appState: AppState + @State private var code = "" + @State private var isAuthorizing = false + @State private var statusMessage: String? + @State private var isError = false + @State private var isDone = false + @Environment(\.dismiss) private var dismiss + + var body: some View { + NavigationStack { + ScrollView { + VStack(spacing: 24) { + Image(systemName: "iphone.badge.checkmark") + .font(.system(size: 48)) + .foregroundStyle(.blue) + + Text("Authorize New Device") + .font(.title2.bold()) + + Text("Enter the 8-digit pairing code shown on the new device.") + .font(.subheadline) + .foregroundStyle(.secondary) + .multilineTextAlignment(.center) + + TextField("Pairing Code", text: $code) + .font(.system(size: 24, weight: .bold, design: .monospaced)) + .multilineTextAlignment(.center) + .keyboardType(.numberPad) + .textFieldStyle(.roundedBorder) + + Button("Authorize") { + Task { await authorize() } + } + .buttonStyle(.borderedProminent) + .disabled(code.count < 8 || isAuthorizing || isDone) + + if isAuthorizing { + ProgressView("Preparing history & sending keys...") + } + + if let status = statusMessage { + Text(status) + .font(.caption) + .foregroundStyle(isError ? .red : .green) + .multilineTextAlignment(.center) + } + + if isDone { + Button("Done") { dismiss() } + .buttonStyle(.bordered) + } + } + .padding(32) + } + .navigationTitle("Authorize Device") + .navigationBarTitleDisplayMode(.inline) + .toolbar { + ToolbarItem(placement: .topBarLeading) { + Button("Cancel") { dismiss() } + } + } + } + } + + private func authorize() async { + isAuthorizing = true + isError = false + statusMessage = nil + + let (success, msg) = await appState.chatClient.authorizeDevice(code: code) + isAuthorizing = false + + statusMessage = msg + isError = !success + if success { + isDone = true + } + } +} diff --git a/ios_client 0.8.5/Kecalek/Views/Auth/LoginView.swift b/ios_client 0.8.5/Kecalek/Views/Auth/LoginView.swift new file mode 100644 index 0000000..321bbf3 --- /dev/null +++ b/ios_client 0.8.5/Kecalek/Views/Auth/LoginView.swift @@ -0,0 +1,179 @@ +import SwiftUI + +struct LoginView: View { + @Bindable var viewModel: AuthViewModel + var appState: AppState + @State private var showPairing = false + @State private var didAttemptBiometric = false + + var body: some View { + NavigationStack { + ScrollView { + VStack(spacing: 20) { + Image(systemName: "lock.shield.fill") + .font(.system(size: 60)) + .foregroundStyle(.blue) + .padding(.top, 40) + + Text("Encrypted Chat") + .font(.largeTitle.bold()) + + Text("End-to-end encrypted messaging") + .font(.subheadline) + .foregroundStyle(.secondary) + + VStack(spacing: 16) { + // Server config + DisclosureGroup("Server") { + TextField("Host", text: $viewModel.serverHost) + .textContentType(.URL) + .autocapitalization(.none) + TextField("Port", text: $viewModel.serverPort) + .keyboardType(.numberPad) + } + .padding(.horizontal) + + if viewModel.mode == .register { + TextField("Username", text: $viewModel.username) + .textContentType(.username) + .autocapitalization(.none) + .textFieldStyle(.roundedBorder) + } + + TextField("Email", text: $viewModel.email) + .textContentType(.emailAddress) + .keyboardType(.emailAddress) + .autocapitalization(.none) + .textFieldStyle(.roundedBorder) + + SecureField("Password", text: $viewModel.password) + .textContentType(viewModel.mode == .login ? .password : .oneTimeCode) + .autocorrectionDisabled() + .textInputAutocapitalization(.never) + .textFieldStyle(.roundedBorder) + + if viewModel.mode == .register { + SecureField("Confirm Password", text: $viewModel.confirmPassword) + .textContentType(.oneTimeCode) + .autocorrectionDisabled() + .textInputAutocapitalization(.never) + .textFieldStyle(.roundedBorder) + } + + if let error = viewModel.errorMessage { + Text(error) + .foregroundStyle(.red) + .font(.caption) + .multilineTextAlignment(.center) + } + + Button(action: { + Task { + if viewModel.mode == .login { + await viewModel.login(appState: appState) + } else { + await viewModel.register(appState: appState) + } + } + }) { + if viewModel.isLoading { + ProgressView() + .frame(maxWidth: .infinity) + } else { + Text(viewModel.mode == .login ? "Login" : "Register") + .frame(maxWidth: .infinity) + } + } + .buttonStyle(.borderedProminent) + .disabled(viewModel.isLoading) + + Button(viewModel.mode == .login ? "Don't have an account? Register" : "Already have an account? Login") { + viewModel.mode = viewModel.mode == .login ? .register : .login + viewModel.errorMessage = nil + } + .font(.caption) + + if viewModel.hasSavedCredentials && viewModel.mode == .login { + Divider() + .padding(.vertical, 4) + + Button { + Task { await viewModel.biometricLogin(appState: appState) } + } label: { + if viewModel.isBiometricLoading { + ProgressView() + .frame(maxWidth: .infinity) + } else { + Label("Sign in with Face ID", systemImage: "faceid") + .frame(maxWidth: .infinity) + } + } + .buttonStyle(.bordered) + .disabled(viewModel.isLoading || viewModel.isBiometricLoading) + } + + Divider() + .padding(.vertical, 4) + + Button("Pair from existing device") { + showPairing = true + } + .font(.caption) + } + .padding(.horizontal, 32) + } + } + .task { + viewModel.checkSavedCredentials() + if viewModel.hasSavedCredentials && !didAttemptBiometric { + didAttemptBiometric = true + await viewModel.biometricLogin(appState: appState) + } + } + .sheet(isPresented: $viewModel.showConfirmation) { + ConfirmationSheet(viewModel: viewModel, appState: appState) + } + .sheet(isPresented: $showPairing) { + PairingView(appState: appState, authViewModel: viewModel) + } + } + } +} + +struct ConfirmationSheet: View { + @Bindable var viewModel: AuthViewModel + var appState: AppState + + var body: some View { + VStack(spacing: 20) { + Text("Confirm Registration") + .font(.title2.bold()) + + if let msg = viewModel.registrationMessage { + Text(msg) + .font(.subheadline) + .foregroundStyle(.secondary) + .multilineTextAlignment(.center) + } + + TextField("Confirmation Code", text: $viewModel.confirmationCode) + .textFieldStyle(.roundedBorder) + .keyboardType(.numberPad) + + if let error = viewModel.errorMessage { + Text(error) + .foregroundStyle(.red) + .font(.caption) + } + + Button("Confirm") { + Task { + await viewModel.confirmRegistration(appState: appState) + } + } + .buttonStyle(.borderedProminent) + .disabled(viewModel.isLoading) + } + .padding(32) + } +} diff --git a/ios_client 0.8.5/Kecalek/Views/Auth/PairingView.swift b/ios_client 0.8.5/Kecalek/Views/Auth/PairingView.swift new file mode 100644 index 0000000..489f067 --- /dev/null +++ b/ios_client 0.8.5/Kecalek/Views/Auth/PairingView.swift @@ -0,0 +1,175 @@ +import SwiftUI + +struct PairingView: View { + var appState: AppState + @Bindable var authViewModel: AuthViewModel + @State private var email = "" + @State private var password = "" + @State private var pairingCode: String? + @State private var isStarting = false + @State private var isWaiting = false + @State private var statusMessage: String? + @State private var isError = false + @Environment(\.dismiss) private var dismiss + + var body: some View { + NavigationStack { + ScrollView { + VStack(spacing: 24) { + Image(systemName: "iphone.and.arrow.forward") + .font(.system(size: 48)) + .foregroundStyle(.blue) + + Text("Device Pairing") + .font(.title2.bold()) + + Text("Transfer your keys from an existing device to this one.") + .font(.subheadline) + .foregroundStyle(.secondary) + .multilineTextAlignment(.center) + + if pairingCode == nil { + // Phase 1: Enter email and start pairing + VStack(spacing: 16) { + // Server config + DisclosureGroup("Server") { + TextField("Host", text: $authViewModel.serverHost) + .textContentType(.URL) + .autocapitalization(.none) + TextField("Port", text: $authViewModel.serverPort) + .keyboardType(.numberPad) + } + + TextField("Email", text: $email) + .textContentType(.emailAddress) + .keyboardType(.emailAddress) + .autocapitalization(.none) + .textFieldStyle(.roundedBorder) + + SecureField("Password (for key encryption)", text: $password) + .textContentType(.password) + .autocorrectionDisabled() + .textInputAutocapitalization(.never) + .textFieldStyle(.roundedBorder) + + Button("Start Pairing") { + Task { await startPairing() } + } + .buttonStyle(.borderedProminent) + .disabled(email.isEmpty || password.isEmpty || isStarting) + + if isStarting { + ProgressView("Connecting...") + } + } + } else { + // Phase 2: Show code and wait for authorization + VStack(spacing: 16) { + Text("Pairing Code") + .font(.headline) + + Text(pairingCode!) + .font(.system(size: 36, weight: .bold, design: .monospaced)) + .padding() + .background(Color(.systemGray6)) + .clipShape(RoundedRectangle(cornerRadius: 12)) + + Text("Enter this code on your already logged-in device\nto authorize this device.") + .font(.caption) + .foregroundStyle(.secondary) + .multilineTextAlignment(.center) + + if isWaiting { + ProgressView("Waiting for authorization...") + .padding() + } + } + } + + if let status = statusMessage { + Text(status) + .font(.caption) + .foregroundStyle(isError ? .red : .green) + .multilineTextAlignment(.center) + } + } + .padding(32) + } + .navigationTitle("Pair Device") + .navigationBarTitleDisplayMode(.inline) + .toolbar { + ToolbarItem(placement: .topBarLeading) { + Button("Cancel") { dismiss() } + } + } + } + } + + private func startPairing() async { + isStarting = true + isError = false + statusMessage = nil + + // Connect to server + if await !appState.chatClient.isConnected { + do { + let port = UInt16(authViewModel.serverPort) ?? Constants.defaultPort + try await appState.chatClient.connect( + host: authViewModel.serverHost, port: port + ) + } catch { + isStarting = false + statusMessage = "Connection failed: \(error.localizedDescription)" + isError = true + return + } + } + + let (success, codeOrMsg) = await appState.chatClient.pairingStart(email: email) + isStarting = false + + if success { + pairingCode = codeOrMsg + // Start waiting for authorization + isWaiting = true + Task { await waitForAuthorization() } + } else { + statusMessage = codeOrMsg + isError = true + } + } + + private func waitForAuthorization() async { + let (success, msg) = await appState.chatClient.pairingWait( + code: pairingCode!, email: email, password: password + ) + isWaiting = false + + if success { + statusMessage = msg + isError = false + // Auto-login + let (loginOk, loginMsg) = await appState.chatClient.login(email: email, password: password) + if loginOk { + appState.email = email + appState.isLoggedIn = true + appState.connectionStatus = .connected + appState.startConnectionMonitor() + if let userId = await appState.chatClient.userId { + appState.currentUser = User( + id: userId, + username: await appState.chatClient.username, + email: email + ) + } + dismiss() + } else { + statusMessage = "Keys imported but login failed: \(loginMsg)" + isError = true + } + } else { + statusMessage = msg + isError = true + } + } +} diff --git a/ios_client 0.8.5/Kecalek/Views/Auth/RegisterView.swift b/ios_client 0.8.5/Kecalek/Views/Auth/RegisterView.swift new file mode 100644 index 0000000..04c0b57 --- /dev/null +++ b/ios_client 0.8.5/Kecalek/Views/Auth/RegisterView.swift @@ -0,0 +1,4 @@ +import SwiftUI + +// Registration is handled within LoginView via mode toggle. +// This file exists for potential future separation. diff --git a/ios_client 0.8.5/Kecalek/Views/Chat/ChatView.swift b/ios_client 0.8.5/Kecalek/Views/Chat/ChatView.swift new file mode 100644 index 0000000..8c7fd32 --- /dev/null +++ b/ios_client 0.8.5/Kecalek/Views/Chat/ChatView.swift @@ -0,0 +1,382 @@ +import SwiftUI + +struct ChatView: View { + @State private var conversation: Conversation + var appState: AppState + var conversationListVM: ConversationListVM? + + init(conversation: Conversation, appState: AppState, conversationListVM: ConversationListVM? = nil) { + self._conversation = State(initialValue: conversation) + self.appState = appState + self.conversationListVM = conversationListVM + } + @State private var viewModel = ChatViewModel() + @State private var inputText = "" + @State private var replyTo: Message? + @State private var showGroupInfo = false + @State private var showDMInfo = false + @State private var showSearch = false + @State private var showDeleteConfirm = false + @State private var showError = false + @State private var memberListenerTask: Task? + @State private var forwardingMessage: Message? + @State private var showForwardPicker = false + @State private var showPinnedMessages = false + @State private var scrollTarget: String? + @State private var showVerification = false + @State private var verificationStatus: String = "unverified" + + private var currentUserId: String { + appState.currentUser?.id ?? "" + } + + private var isPartnerOnline: Bool { + guard !conversation.isGroup, + let partnerId = conversation.dmPartnerId(currentUserId: currentUserId), + let listVM = conversationListVM else { + return false + } + return listVM.onlineUsers.contains(partnerId) + } + + var body: some View { + VStack(spacing: 0) { + searchBar + messagesScrollView + replyPreview + inputView + } + .navigationBarTitleDisplayMode(.inline) + .toolbar { toolbarContent } + .alert("Delete Conversation?", isPresented: $showDeleteConfirm) { + Button("Cancel", role: .cancel) {} + Button("Delete", role: .destructive) { + Task { await appState.chatClient.deleteConversation(convId: conversation.id) } + } + } message: { + Text(conversation.isGroup + ? "This will remove all members and delete the conversation." + : "This will remove you from the conversation.") + } + .alert("Error", isPresented: $showError) { + Button("OK", role: .cancel) {} + } message: { + Text(viewModel.errorMessage ?? "Unknown error") + } + .sheet(isPresented: $showGroupInfo) { + GroupInfoView(conversation: $conversation, appState: appState, conversationListVM: conversationListVM) + } + .sheet(isPresented: $showDMInfo) { + if let partnerId = conversation.dmPartnerId(currentUserId: currentUserId) { + ProfileView(appState: appState, isOwnProfile: false, userId: partnerId) + } + } + .sheet(isPresented: $showForwardPicker) { + if let msg = forwardingMessage { + ForwardPickerView(message: msg, appState: appState) + } + } + .sheet(isPresented: $showVerification) { + if let partnerId = conversation.dmPartnerId(currentUserId: currentUserId) { + NavigationStack { + SafetyNumberView( + peerUserId: partnerId, + peerUsername: conversation.displayName(currentUserId: currentUserId), + chatClient: appState.chatClient + ) + .toolbar { + ToolbarItem(placement: .topBarLeading) { + Button("Done") { showVerification = false } + } + } + } + } + } + .sheet(isPresented: $showPinnedMessages) { + PinnedMessagesView( + messages: viewModel.messages.filter { $0.pinnedAt != nil }, + onScrollTo: { scrollTarget = $0 } + ) + } + .task { + // Use already-loaded data from conversation list (avoid redundant list_conversations call) + if let updated = conversationListVM?.conversations.first(where: { $0.id == conversation.id }) { + conversation = updated + } + conversationListVM?.markConversationRead(convId: conversation.id) + await viewModel.loadMessages(convId: conversation.id, chatClient: appState.chatClient) + viewModel.startNotificationListener(convId: conversation.id, chatClient: appState.chatClient) + + // Load verification status for DM partner + if !conversation.isGroup, + let partnerId = conversation.dmPartnerId(currentUserId: currentUserId) { + verificationStatus = await appState.chatClient.getVerificationStatus(userId: partnerId) + } + + memberListenerTask = Task { + for await notification in await appState.chatClient.makeNotificationStream() { + switch notification { + case .memberAdded, .memberRemoved, .conversationRenamed: + let refreshed = await appState.chatClient.listConversations() + if let updated = refreshed.first(where: { $0.id == conversation.id }) { + await MainActor.run { conversation = updated } + } + default: + break + } + } + } + } + .onDisappear { + viewModel.stop() + memberListenerTask?.cancel() + memberListenerTask = nil + } + } + + // MARK: - Search Bar + + @ViewBuilder + private var searchBar: some View { + if showSearch { + SearchOverlayView( + query: $viewModel.searchQuery, + matchCount: viewModel.searchResults.count, + currentIndex: viewModel.currentSearchIndex, + onSearch: { viewModel.search(query: $0) }, + onNext: { viewModel.nextSearchResult() }, + onPrev: { viewModel.prevSearchResult() }, + onClose: { showSearch = false; viewModel.search(query: "") } + ) + } + } + + // MARK: - Messages + + private var messagesScrollView: some View { + ScrollViewReader { proxy in + ScrollView { + LazyVStack(spacing: 8) { + if viewModel.messages.count >= 50 { + Button("Load older messages") { + Task { + await viewModel.loadOlderMessages(convId: conversation.id, chatClient: appState.chatClient) + } + } + .font(.caption) + .padding() + } + + ForEach(viewModel.messages) { message in + messageBubble(for: message) + .id(message.id) + } + } + .padding(.horizontal) + .padding(.vertical, 8) + } + .onChange(of: viewModel.messages.count) { + if let lastId = viewModel.messages.last?.id { + withAnimation { proxy.scrollTo(lastId, anchor: .bottom) } + } + } + .onChange(of: scrollTarget) { + if let target = scrollTarget { + withAnimation { proxy.scrollTo(target, anchor: .center) } + scrollTarget = nil + } + } + } + } + + private func messageBubble(for message: Message) -> some View { + let isCurrentSearch = viewModel.searchResults.indices.contains(viewModel.currentSearchIndex) + && viewModel.searchResults[viewModel.currentSearchIndex] == message.id + return MessageBubbleView( + message: message, + isMine: message.isMine(currentUserId: currentUserId), + isGroup: conversation.isGroup, + isHighlighted: viewModel.searchResults.contains(message.id), + isCurrentSearchResult: isCurrentSearch, + chatClient: appState.chatClient, + currentUserId: currentUserId, + onReply: { replyTo = message }, + onReact: { reaction in + Task { + await viewModel.reactToMessage( + messageId: message.id, convId: conversation.id, + reaction: reaction, currentUserId: currentUserId, + chatClient: appState.chatClient + ) + } + }, + onForward: { + forwardingMessage = message + showForwardPicker = true + }, + onPin: { pin in + Task { + await viewModel.pinMessage( + messageId: message.id, convId: conversation.id, + pin: pin, chatClient: appState.chatClient + ) + } + }, + onDelete: { + Task { + await viewModel.deleteMessage( + messageId: message.id, convId: conversation.id, + chatClient: appState.chatClient + ) + } + } + ) + } + + // MARK: - Reply Preview + + @ViewBuilder + private var replyPreview: some View { + if let reply = replyTo { + HStack { + Rectangle() + .fill(.blue) + .frame(width: 3) + VStack(alignment: .leading) { + Text(reply.senderUsername) + .font(.caption.bold()) + Text(reply.text ?? "") + .font(.caption) + .lineLimit(1) + } + Spacer() + Button(action: { replyTo = nil }) { + Image(systemName: "xmark.circle.fill") + .foregroundStyle(.secondary) + } + } + .padding(.horizontal) + .padding(.vertical, 6) + .background(.ultraThinMaterial) + } + } + + // MARK: - Input + + private var inputView: some View { + MessageInputView( + text: $inputText, + isSending: viewModel.isSending, + onSend: { + Task { + let text = inputText + inputText = "" + let reply = replyTo?.id + replyTo = nil + await viewModel.sendMessage( + convId: conversation.id, text: text, + members: conversation.members, + chatClient: appState.chatClient, replyTo: reply + ) + } + }, + onImageSelected: { imageData in + Task { + viewModel.isSending = true + let (success, msg, sentMessage) = await appState.chatClient.sendImage( + convId: conversation.id, imageData: imageData, + members: conversation.members + ) + viewModel.isSending = false + if success, let sentMessage { + if !viewModel.messages.contains(where: { $0.id == sentMessage.id }) { + viewModel.messages.append(sentMessage) + } + await viewModel.saveCache(convId: conversation.id, chatClient: appState.chatClient) + } else if !success { + viewModel.errorMessage = msg + showError = true + } + } + }, + onFileSelected: { fileData, filename, mimeType in + Task { + viewModel.isSending = true + let (success, msg, sentMessage) = await appState.chatClient.sendFile( + convId: conversation.id, fileData: fileData, + filename: filename, mimeType: mimeType, + members: conversation.members + ) + viewModel.isSending = false + if success, let sentMessage { + if !viewModel.messages.contains(where: { $0.id == sentMessage.id }) { + viewModel.messages.append(sentMessage) + } + await viewModel.saveCache(convId: conversation.id, chatClient: appState.chatClient) + } else if !success { + viewModel.errorMessage = msg + showError = true + } + } + }, + members: conversation.members + ) + } + + // MARK: - Toolbar + + @ToolbarContentBuilder + private var toolbarContent: some ToolbarContent { + ToolbarItem(placement: .principal) { + HStack(spacing: 8) { + CircularAvatarView( + name: conversation.displayName(currentUserId: currentUserId), + imageData: conversationListVM?.avatarCache[conversation.id], + size: 28, + isGroup: conversation.isGroup + ) + Text(conversation.displayName(currentUserId: currentUserId)) + .font(.headline) + if !conversation.isGroup && verificationStatus == "verified" { + Image(systemName: "checkmark.shield.fill") + .font(.caption) + .foregroundStyle(.green) + } + if isPartnerOnline { + Circle().fill(.green).frame(width: 8, height: 8) + } + } + } + + ToolbarItem(placement: .topBarTrailing) { + HStack(spacing: 16) { + if !conversation.isGroup { + Button(action: { showVerification = true }) { + Image(systemName: verificationStatus == "verified" ? "checkmark.shield.fill" : "shield") + .foregroundStyle(verificationStatus == "verified" ? .green : .secondary) + } + } + Button(action: { showPinnedMessages = true }) { + Image(systemName: "pin") + } + Button(action: { showSearch.toggle() }) { + Image(systemName: "magnifyingglass") + } + if conversation.isGroup { + Button(action: { showGroupInfo = true }) { + Image(systemName: "info.circle") + } + } else { + Button(action: { showDMInfo = true }) { + Image(systemName: "info.circle") + } + } + if !conversation.isGroup || conversation.createdBy == currentUserId { + Button(action: { showDeleteConfirm = true }) { + Image(systemName: "trash").foregroundStyle(.red) + } + } + } + } + } +} diff --git a/ios_client 0.8.5/Kecalek/Views/Chat/ForwardPickerView.swift b/ios_client 0.8.5/Kecalek/Views/Chat/ForwardPickerView.swift new file mode 100644 index 0000000..cf2eeda --- /dev/null +++ b/ios_client 0.8.5/Kecalek/Views/Chat/ForwardPickerView.swift @@ -0,0 +1,77 @@ +import SwiftUI + +struct ForwardPickerView: View { + let message: Message + let appState: AppState + + @Environment(\.dismiss) private var dismiss + @State private var conversations: [Conversation] = [] + @State private var isLoading = true + @State private var isSending = false + + var body: some View { + NavigationStack { + Group { + if isLoading { + ProgressView("Loading conversations...") + } else if conversations.isEmpty { + Text("No conversations available") + .foregroundStyle(.secondary) + } else { + List(conversations) { conv in + Button { + forwardTo(conv) + } label: { + HStack { + CircularAvatarView( + name: conv.displayName(currentUserId: appState.currentUser?.id ?? ""), + size: 36, + isGroup: conv.isGroup + ) + Text(conv.displayName(currentUserId: appState.currentUser?.id ?? "")) + Spacer() + } + } + .disabled(isSending) + } + } + } + .navigationTitle("Forward to...") + .navigationBarTitleDisplayMode(.inline) + .toolbar { + ToolbarItem(placement: .cancellationAction) { + Button("Cancel") { dismiss() } + } + } + } + .task { + conversations = await appState.chatClient.listConversations() + isLoading = false + } + } + + private func forwardTo(_ conv: Conversation) { + isSending = true + Task { + let forwardPayload: [String: Any] = [ + "forwarded_from": [ + "sender": message.senderUsername, + "conversation_id": message.conversationId, + "message_id": message.id, + ] as [String: Any] + ] + let (success, _, _) = await appState.chatClient.sendMessage( + convId: conv.id, + text: message.text ?? "", + members: conv.members, + extraPayload: forwardPayload + ) + await MainActor.run { + isSending = false + if success { + dismiss() + } + } + } + } +} diff --git a/ios_client 0.8.5/Kecalek/Views/Chat/ImageViewerView.swift b/ios_client 0.8.5/Kecalek/Views/Chat/ImageViewerView.swift new file mode 100644 index 0000000..ab7ad19 --- /dev/null +++ b/ios_client 0.8.5/Kecalek/Views/Chat/ImageViewerView.swift @@ -0,0 +1,113 @@ +import SwiftUI +import UIKit +import Photos + +struct ImageViewerView: View { + let imageData: Data + @State private var scale: CGFloat = 1.0 + @State private var saved = false + @State private var saveError: String? + @Environment(\.dismiss) private var dismiss + + var body: some View { + NavigationStack { + GeometryReader { geo in + if let uiImage = UIImage(data: imageData) { + Image(uiImage: uiImage) + .resizable() + .aspectRatio(contentMode: .fit) + .scaleEffect(scale) + .gesture( + MagnifyGesture() + .onChanged { value in + scale = value.magnification + } + .onEnded { _ in + withAnimation { + scale = max(1.0, min(scale, 5.0)) + } + } + ) + .onTapGesture(count: 2) { + withAnimation { + scale = scale > 1 ? 1 : 2 + } + } + .frame(width: geo.size.width, height: geo.size.height) + } + } + .overlay(alignment: .bottom) { + if let error = saveError { + Text(error) + .font(.caption) + .foregroundStyle(.white) + .padding(8) + .background(Capsule().fill(.red.opacity(0.8))) + .padding(.bottom, 40) + .transition(.move(edge: .bottom).combined(with: .opacity)) + } + } + .toolbar { + ToolbarItem(placement: .topBarLeading) { + Button { + dismiss() + } label: { + Image(systemName: "xmark") + .foregroundStyle(.white) + } + } + ToolbarItem(placement: .topBarTrailing) { + HStack(spacing: 16) { + // Share + if let uiImage = UIImage(data: imageData) { + ShareLink(item: Image(uiImage: uiImage), preview: SharePreview("Image", image: Image(uiImage: uiImage))) { + Image(systemName: "square.and.arrow.up") + .foregroundStyle(.white) + } + } + + // Save to Photos + Button { + saveToPhotos() + } label: { + Image(systemName: saved ? "checkmark.circle.fill" : "arrow.down.to.line") + .foregroundStyle(saved ? .green : .white) + } + } + } + } + .toolbarBackground(.hidden, for: .navigationBar) + .background(.black) + } + } + + private func saveToPhotos() { + guard let uiImage = UIImage(data: imageData) else { + withAnimation { saveError = "Invalid image data" } + return + } + + PHPhotoLibrary.requestAuthorization(for: .addOnly) { status in + DispatchQueue.main.async { + switch status { + case .authorized, .limited: + PHPhotoLibrary.shared().performChanges { + PHAssetChangeRequest.creationRequestForAsset(from: uiImage) + } completionHandler: { success, error in + DispatchQueue.main.async { + if success { + withAnimation { saved = true; saveError = nil } + } else { + withAnimation { saveError = error?.localizedDescription ?? "Save failed" } + } + } + } + case .denied, .restricted: + withAnimation { saveError = "Photo library access denied. Check Settings." } + default: + withAnimation { saveError = "Photo library access required" } + } + } + } + } +} diff --git a/ios_client 0.8.5/Kecalek/Views/Chat/MessageBubbleView.swift b/ios_client 0.8.5/Kecalek/Views/Chat/MessageBubbleView.swift new file mode 100644 index 0000000..b8747c5 --- /dev/null +++ b/ios_client 0.8.5/Kecalek/Views/Chat/MessageBubbleView.swift @@ -0,0 +1,558 @@ +import SwiftUI +import UIKit + +struct MessageBubbleView: View { + let message: Message + let isMine: Bool + var isGroup: Bool = false + var isHighlighted: Bool = false + var isCurrentSearchResult: Bool = false + var chatClient: ChatClient? + var currentUserId: String = "" + var onReply: (() -> Void)? + var onReact: ((String) -> Void)? + var onForward: (() -> Void)? + var onPin: ((Bool) -> Void)? + var onDelete: (() -> Void)? + + @State private var fullImageData: Data? + @State private var showFullImage = false + @State private var isLoadingImage = false + @State private var isLoadingFile = false + @State private var downloadedFileURL: URL? + @State private var showShareSheet = false + @State private var imageError: String? + + var body: some View { + HStack { + if isMine { Spacer(minLength: 60) } + + VStack(alignment: isMine ? .trailing : .leading, spacing: 4) { + if !isMine && isGroup { + Text(message.senderUsername) + .font(.caption.bold()) + .foregroundStyle(.secondary) + } + + if message.isDeleted { + Text("Message deleted") + .font(.body.italic()) + .foregroundStyle(.secondary) + .padding(12) + .background(Color(.systemGray6)) + .clipShape(RoundedRectangle(cornerRadius: 16)) + } else { + // Forwarded header + if let fwd = message.forwardedFrom { + HStack(spacing: 4) { + Rectangle().fill(.cyan).frame(width: 3) + VStack(alignment: .leading, spacing: 1) { + Text("Forwarded from").font(.caption2).foregroundStyle(.secondary) + Text(fwd.sender).font(.caption.bold()).foregroundStyle(.cyan) + } + } + .padding(.horizontal, 8).padding(.top, 4) + } + + // Reply reference + if message.replyTo != nil { + HStack(spacing: 4) { + Rectangle() + .fill(.blue.opacity(0.5)) + .frame(width: 2) + Text("Reply to message") + .font(.caption) + .foregroundStyle(.secondary) + } + .padding(.horizontal, 8) + } + + // Image thumbnail + if let imageInfo = message.image { + imageView(imageInfo: imageInfo) + } + + // File card + if let file = message.file { + VStack(alignment: .leading, spacing: 4) { + HStack { + if isLoadingFile { + ProgressView() + .scaleEffect(0.8) + } else { + Image(systemName: fileIcon(for: file.filename)) + } + Text(file.filename) + .lineLimit(1) + } + .font(.subheadline) + + Text(formatFileSize(file.size)) + .font(.caption) + .foregroundStyle(.secondary) + } + .padding(12) + .background(Color(.systemGray5)) + .clipShape(RoundedRectangle(cornerRadius: 12)) + .onTapGesture { + downloadAndShareFile(file: file) + } + } + + // Text content with link detection + if let text = message.text, !text.isEmpty { + LinkText(text: text, isMine: isMine) + .padding(12) + .background( + isMine ? Color.blue : Color(.systemGray5) + ) + .clipShape(RoundedRectangle(cornerRadius: 16)) + } + + // Timestamp + checkmarks + reactions — all on one line + HStack(spacing: 4) { + if message.pinnedAt != nil { + Image(systemName: "pin.fill").font(.caption2).foregroundStyle(.orange) + } + Text(formatTime(message.createdAt)).font(.caption2).foregroundStyle(.secondary) + if isMine { + deliveryIndicator + } + if !message.reactions.isEmpty { + inlineReactionBadges + } + } + .frame(maxWidth: .infinity, alignment: isMine ? .trailing : .leading) + } + } + .padding(2) + .background( + RoundedRectangle(cornerRadius: 16) + .fill(isCurrentSearchResult ? Color.orange.opacity(0.3) : + isHighlighted ? Color.yellow.opacity(0.2) : Color.clear) + ) + .contextMenu { + if !message.isDeleted { + Button(action: { onReply?() }) { + Label("Reply", systemImage: "arrowshape.turn.up.left") + } + + Menu { + ForEach(ReactionEmoji.allowed, id: \.self) { key in + Button("\(ReactionEmoji.display[key] ?? "") \(key)") { onReact?(key) } + } + } label: { + Label("React", systemImage: "face.smiling") + } + + Button(action: { + UIPasteboard.general.string = message.text ?? "" + // Auto-clear clipboard after 30 seconds + DispatchQueue.main.asyncAfter(deadline: .now() + 30) { + if UIPasteboard.general.string == message.text { + UIPasteboard.general.string = "" + } + } + }) { + Label("Copy", systemImage: "doc.on.doc") + } + + Button(action: { onForward?() }) { + Label("Forward", systemImage: "arrowshape.turn.up.right") + } + + Button(action: { onPin?(message.pinnedAt == nil) }) { + Label(message.pinnedAt == nil ? "Pin" : "Unpin", + systemImage: message.pinnedAt == nil ? "pin" : "pin.slash") + } + + if isMine { + Button(role: .destructive, action: { onDelete?() }) { + Label("Delete", systemImage: "trash") + } + } + } + } + + if !isMine { Spacer(minLength: 60) } + } + .sheet(isPresented: $showFullImage) { + if let data = fullImageData { + ImageViewerView(imageData: data) + } + } + .sheet(isPresented: $showShareSheet, onDismiss: { + // Clean up decrypted temp file after sharing + if let fileURL = downloadedFileURL { + try? FileManager.default.removeItem(at: fileURL) + downloadedFileURL = nil + } + }) { + if let fileURL = downloadedFileURL { + ActivityViewController(activityItems: [fileURL]) + } + } + } + + // MARK: - Reaction Badges (inline — used in timestamp row) + + private var inlineReactionBadges: some View { + let grouped = Dictionary(grouping: message.reactions, by: \.reaction) + return HStack(spacing: 2) { + ForEach(grouped.sorted(by: { $0.key < $1.key }), id: \.key) { reaction, users in + Button { + onReact?(reaction) + } label: { + Text(ReactionEmoji.display[reaction] ?? reaction) + .font(.caption) + } + .buttonStyle(.plain) + } + } + } + + // MARK: - Delivery Indicator (checkmarks) + + @ViewBuilder + private var deliveryIndicator: some View { + let isRead = message.readBy.contains(where: { $0 != "__delivered__" && $0 != currentUserId }) + let isDelivered = message.readBy.contains("__delivered__") + + if isRead { + // Read: 2 green checkmarks + HStack(spacing: -4) { + Image(systemName: "checkmark").font(.system(size: 9, weight: .bold)) + Image(systemName: "checkmark").font(.system(size: 9, weight: .bold)) + } + .foregroundStyle(.green) + } else if isDelivered { + // Delivered: 2 gray checkmarks + HStack(spacing: -4) { + Image(systemName: "checkmark").font(.system(size: 9, weight: .bold)) + Image(systemName: "checkmark").font(.system(size: 9, weight: .bold)) + } + .foregroundStyle(.secondary) + } else { + // Sent: 1 gray checkmark + Image(systemName: "checkmark").font(.system(size: 9, weight: .bold)) + .foregroundStyle(.secondary) + } + } + + // MARK: - Image View + + @ViewBuilder + private func imageView(imageInfo: ImageInfo) -> some View { + VStack(spacing: 4) { + if let thumbB64 = imageInfo.thumbnail, + let thumbData = Data(base64Encoded: thumbB64), + let uiImage = UIImage(data: thumbData) { + Image(uiImage: uiImage) + .resizable() + .aspectRatio(contentMode: .fit) + .frame(maxWidth: 220, maxHeight: 220) + .clipShape(RoundedRectangle(cornerRadius: 12)) + .overlay { + if isLoadingImage { + RoundedRectangle(cornerRadius: 12) + .fill(.black.opacity(0.4)) + ProgressView() + .tint(.white) + } + } + .onTapGesture { + downloadAndShowFullImage(imageInfo: imageInfo) + } + } else { + // No thumbnail available — show placeholder + RoundedRectangle(cornerRadius: 12) + .fill(Color(.systemGray5)) + .frame(width: 160, height: 120) + .overlay { + if isLoadingImage { + ProgressView() + } else { + VStack(spacing: 6) { + Image(systemName: "photo") + .font(.title2) + Text(imageInfo.filename) + .font(.caption) + .lineLimit(1) + } + .foregroundStyle(.secondary) + } + } + .onTapGesture { + downloadAndShowFullImage(imageInfo: imageInfo) + } + } + + if let error = imageError { + Text(error) + .font(.caption2) + .foregroundStyle(.red) + .onTapGesture { + imageError = nil + downloadAndShowFullImage(imageInfo: imageInfo) + } + } + } + } + + private func downloadAndShowFullImage(imageInfo: ImageInfo) { + guard !isLoadingImage, let client = chatClient else { return } + // If already downloaded, show immediately + if fullImageData != nil { + showFullImage = true + return + } + imageError = nil + isLoadingImage = true + Task { + guard let aesKey = try? ProtocolHandler.decodeBinary(imageInfo.aesKey), + let iv = try? ProtocolHandler.decodeBinary(imageInfo.iv) else { + await MainActor.run { + isLoadingImage = false + imageError = "Failed to decode image keys" + } + return + } + let data = await client.downloadFile(fileId: imageInfo.fileId, aesKey: aesKey, iv: iv, conversationId: message.conversationId) + await MainActor.run { + isLoadingImage = false + if let data = data { + fullImageData = data + showFullImage = true + } else { + imageError = "Download failed, tap to retry" + } + } + } + } + + // MARK: - File Download + + private func downloadAndShareFile(file: FileInfo) { + guard !isLoadingFile, let client = chatClient else { return } + // If already downloaded, show share sheet immediately + if downloadedFileURL != nil { + showShareSheet = true + return + } + isLoadingFile = true + Task { + guard let aesKey = try? ProtocolHandler.decodeBinary(file.aesKey), + let iv = try? ProtocolHandler.decodeBinary(file.iv) else { + await MainActor.run { isLoadingFile = false } + return + } + let data = await client.downloadFile(fileId: file.fileId, aesKey: aesKey, iv: iv, conversationId: message.conversationId) + await MainActor.run { + isLoadingFile = false + if let data = data { + // Save to temp with file protection, clean up on dismiss + let tempDir = FileManager.default.temporaryDirectory + let fileURL = tempDir.appendingPathComponent(file.filename) + try? data.write(to: fileURL, options: .completeFileProtection) + downloadedFileURL = fileURL + showShareSheet = true + } + } + } + } + + private func fileIcon(for filename: String) -> String { + let ext = (filename as NSString).pathExtension.lowercased() + switch ext { + case "pdf": return "doc.richtext" + case "doc", "docx": return "doc.text" + case "xls", "xlsx": return "tablecells" + case "ppt", "pptx": return "rectangle.on.rectangle" + case "zip", "rar", "7z": return "doc.zipper" + case "mp3", "wav", "m4a": return "music.note" + case "mp4", "mov", "avi": return "film" + case "txt": return "doc.plaintext" + default: return "paperclip" + } + } + + // MARK: - Helpers + + private func formatTime(_ date: Date) -> String { + let formatter = DateFormatter() + if Calendar.current.isDateInToday(date) { + formatter.dateFormat = "HH:mm" + } else { + formatter.dateFormat = "MMM d, HH:mm" + } + return formatter.string(from: date) + } + + private func formatFileSize(_ bytes: Int) -> String { + if bytes < 1024 { return "\(bytes) B" } + if bytes < 1024 * 1024 { return "\(bytes / 1024) KB" } + return String(format: "%.1f MB", Double(bytes) / (1024 * 1024)) + } +} + +// MARK: - Link Text + +struct LinkText: View { + let text: String + let isMine: Bool + + var body: some View { + Text(buildAttributedString()) + .environment(\.openURL, OpenURLAction { url in + UIApplication.shared.open(url) + return .handled + }) + } + + private func buildAttributedString() -> AttributedString { + var result = AttributedString() + + guard let detector = try? NSDataDetector(types: NSTextCheckingResult.CheckingType.link.rawValue) else { + return appendPlainWithMentions(text[text.startIndex.. AttributedString { + let str = String(substring) + let nsRange = NSRange(str.startIndex..., in: str) + let matches = Self.mentionRegex.matches(in: str, range: nsRange) + + if matches.isEmpty { + var plain = AttributedString(str) + plain.foregroundColor = isMine ? .white : .primary + result.append(plain) + return result + } + + let mentionColor = Color(red: 0.537, green: 0.706, blue: 0.980) + var lastEnd = str.startIndex + + for match in matches { + guard let matchRange = Range(match.range, in: str) else { continue } + + if lastEnd < matchRange.lowerBound { + var plain = AttributedString(str[lastEnd.. CGSize { + let maxWidth = proposal.width ?? .infinity + var x: CGFloat = 0 + var y: CGFloat = 0 + var rowHeight: CGFloat = 0 + + for subview in subviews { + let size = subview.sizeThatFits(.unspecified) + if x + size.width > maxWidth && x > 0 { + x = 0 + y += rowHeight + spacing + rowHeight = 0 + } + x += size.width + spacing + rowHeight = max(rowHeight, size.height) + } + return CGSize(width: maxWidth, height: y + rowHeight) + } + + func placeSubviews(in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) { + var x = bounds.minX + var y = bounds.minY + var rowHeight: CGFloat = 0 + + for subview in subviews { + let size = subview.sizeThatFits(.unspecified) + if x + size.width > bounds.maxX && x > bounds.minX { + x = bounds.minX + y += rowHeight + spacing + rowHeight = 0 + } + subview.place(at: CGPoint(x: x, y: y), proposal: .unspecified) + x += size.width + spacing + rowHeight = max(rowHeight, size.height) + } + } +} + +// MARK: - Share Sheet + +struct ActivityViewController: UIViewControllerRepresentable { + let activityItems: [Any] + + func makeUIViewController(context: Context) -> UIActivityViewController { + UIActivityViewController(activityItems: activityItems, applicationActivities: nil) + } + + func updateUIViewController(_ uiViewController: UIActivityViewController, context: Context) {} +} diff --git a/ios_client 0.8.5/Kecalek/Views/Chat/MessageInputView.swift b/ios_client 0.8.5/Kecalek/Views/Chat/MessageInputView.swift new file mode 100644 index 0000000..c682256 --- /dev/null +++ b/ios_client 0.8.5/Kecalek/Views/Chat/MessageInputView.swift @@ -0,0 +1,215 @@ +import SwiftUI +import PhotosUI +import UniformTypeIdentifiers +import UIKit + +struct MessageInputView: View { + @Binding var text: String + let isSending: Bool + let onSend: () -> Void + var onImageSelected: ((Data) -> Void)? + var onFileSelected: ((Data, String, String) -> Void)? // data, filename, mimeType + var members: [ConversationMember] = [] + + @State private var isProcessing = false + @State private var showFilePicker = false + @State private var showPhotoPicker = false + @State private var showMentionPopup = false + @State private var mentionCandidates: [ConversationMember] = [] + + var body: some View { + VStack(spacing: 0) { + // Mention autocomplete popup + if showMentionPopup && !mentionCandidates.isEmpty { + ScrollView { + VStack(alignment: .leading, spacing: 0) { + ForEach(mentionCandidates) { member in + Button { + completeMention(member: member) + } label: { + Text("@\(member.username)") + .padding(.horizontal, 12) + .padding(.vertical, 8) + .frame(maxWidth: .infinity, alignment: .leading) + } + .buttonStyle(.plain) + Divider() + } + } + } + .frame(maxHeight: 150) + .background(.ultraThinMaterial) + .clipShape(RoundedRectangle(cornerRadius: 8)) + .padding(.horizontal) + } + + HStack(spacing: 8) { + // Attach button + Menu { + Button { + showPhotoPicker = true + } label: { + Label("Photo", systemImage: "photo") + } + Button { + showFilePicker = true + } label: { + Label("File", systemImage: "doc") + } + } label: { + if isProcessing { + ProgressView() + .scaleEffect(0.8) + } else { + Image(systemName: "plus.circle.fill") + .font(.title2) + .foregroundStyle(.blue) + } + } + .disabled(isProcessing || isSending) + + // Text field + TextField("Message", text: $text, axis: .vertical) + .textFieldStyle(.roundedBorder) + .lineLimit(1...5) + .onSubmit { + if !text.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty { + onSend() + } + } + + // Send button + Button(action: onSend) { + if isSending { + ProgressView() + .scaleEffect(0.8) + } else { + Image(systemName: "arrow.up.circle.fill") + .font(.title2) + .foregroundStyle(.blue) + } + } + .disabled(text.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty || isSending) + } + .padding(.horizontal) + .padding(.vertical, 8) + .background(.ultraThinMaterial) + .sheet(isPresented: $showPhotoPicker) { + ImagePickerView { data in + isProcessing = true + onImageSelected?(data) + isProcessing = false + } + } + .sheet(isPresented: $showFilePicker) { + DocumentPickerView { url in + guard url.startAccessingSecurityScopedResource() else { return } + defer { url.stopAccessingSecurityScopedResource() } + guard let data = try? Data(contentsOf: url) else { return } + let filename = url.lastPathComponent + let mimeType = UTType(filenameExtension: url.pathExtension)?.preferredMIMEType ?? "application/octet-stream" + onFileSelected?(data, filename, mimeType) + } + } + .onChange(of: text) { + updateMentionCandidates() + } + } // end VStack + } + + private func updateMentionCandidates() { + // Look for @prefix at end of text + guard let atRange = text.range(of: "@\\w*$", options: .regularExpression) else { + showMentionPopup = false + mentionCandidates = [] + return + } + let prefix = String(text[atRange]).dropFirst().lowercased() // remove @ + mentionCandidates = members.filter { member in + prefix.isEmpty || member.username.lowercased().hasPrefix(prefix) + } + showMentionPopup = !mentionCandidates.isEmpty + } + + private func completeMention(member: ConversationMember) { + if let atRange = text.range(of: "@\\w*$", options: .regularExpression) { + text.replaceSubrange(atRange, with: "@\(member.username) ") + } + showMentionPopup = false + mentionCandidates = [] + } +} + +// MARK: - Image Picker (UIKit PHPicker wrapper) + +struct ImagePickerView: UIViewControllerRepresentable { + let onImagePicked: (Data) -> Void + + func makeCoordinator() -> Coordinator { + Coordinator(onImagePicked: onImagePicked) + } + + func makeUIViewController(context: Context) -> PHPickerViewController { + var config = PHPickerConfiguration() + config.filter = .images + config.selectionLimit = 1 + let picker = PHPickerViewController(configuration: config) + picker.delegate = context.coordinator + return picker + } + + func updateUIViewController(_ uiViewController: PHPickerViewController, context: Context) {} + + class Coordinator: NSObject, PHPickerViewControllerDelegate { + let onImagePicked: (Data) -> Void + + init(onImagePicked: @escaping (Data) -> Void) { + self.onImagePicked = onImagePicked + } + + func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) { + picker.dismiss(animated: true) + guard let provider = results.first?.itemProvider, + provider.canLoadObject(ofClass: UIImage.self) else { return } + provider.loadObject(ofClass: UIImage.self) { [weak self] image, _ in + guard let uiImage = image as? UIImage, + let data = uiImage.jpegData(compressionQuality: 0.9) else { return } + DispatchQueue.main.async { + self?.onImagePicked(data) + } + } + } + } +} + +// MARK: - Document Picker (UIKit wrapper) + +struct DocumentPickerView: UIViewControllerRepresentable { + let onPick: (URL) -> Void + + func makeCoordinator() -> Coordinator { + Coordinator(onPick: onPick) + } + + func makeUIViewController(context: Context) -> UIDocumentPickerViewController { + let picker = UIDocumentPickerViewController(forOpeningContentTypes: [.item]) + picker.delegate = context.coordinator + picker.allowsMultipleSelection = false + return picker + } + + func updateUIViewController(_ uiViewController: UIDocumentPickerViewController, context: Context) {} + + class Coordinator: NSObject, UIDocumentPickerDelegate { + let onPick: (URL) -> Void + + init(onPick: @escaping (URL) -> Void) { + self.onPick = onPick + } + + func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) { + guard let url = urls.first else { return } + onPick(url) + } + } +} diff --git a/ios_client 0.8.5/Kecalek/Views/Chat/PinnedMessagesView.swift b/ios_client 0.8.5/Kecalek/Views/Chat/PinnedMessagesView.swift new file mode 100644 index 0000000..17d1dfa --- /dev/null +++ b/ios_client 0.8.5/Kecalek/Views/Chat/PinnedMessagesView.swift @@ -0,0 +1,62 @@ +import SwiftUI + +struct PinnedMessagesView: View { + let messages: [Message] + var onScrollTo: ((String) -> Void)? + + @Environment(\.dismiss) private var dismiss + + var body: some View { + NavigationStack { + Group { + if messages.isEmpty { + Text("No pinned messages") + .foregroundStyle(.secondary) + } else { + List(messages) { message in + Button { + dismiss() + onScrollTo?(message.id) + } label: { + VStack(alignment: .leading, spacing: 4) { + HStack { + Image(systemName: "pin.fill") + .font(.caption) + .foregroundStyle(.orange) + Text(message.senderUsername) + .font(.caption.bold()) + Spacer() + Text(formatTime(message.createdAt)) + .font(.caption2) + .foregroundStyle(.secondary) + } + Text(message.text ?? "") + .font(.body) + .lineLimit(3) + } + .padding(.vertical, 4) + } + .buttonStyle(.plain) + } + } + } + .navigationTitle("Pinned Messages") + .navigationBarTitleDisplayMode(.inline) + .toolbar { + ToolbarItem(placement: .cancellationAction) { + Button("Done") { dismiss() } + } + } + } + } + + private func formatTime(_ date: Date) -> String { + let formatter = DateFormatter() + if Calendar.current.isDateInToday(date) { + formatter.dateFormat = "HH:mm" + } else { + formatter.dateFormat = "MMM d, HH:mm" + } + return formatter.string(from: date) + } +} diff --git a/ios_client 0.8.5/Kecalek/Views/Chat/SearchOverlayView.swift b/ios_client 0.8.5/Kecalek/Views/Chat/SearchOverlayView.swift new file mode 100644 index 0000000..98bc3b4 --- /dev/null +++ b/ios_client 0.8.5/Kecalek/Views/Chat/SearchOverlayView.swift @@ -0,0 +1,46 @@ +import SwiftUI + +struct SearchOverlayView: View { + @Binding var query: String + let matchCount: Int + let currentIndex: Int + let onSearch: (String) -> Void + let onNext: () -> Void + let onPrev: () -> Void + let onClose: () -> Void + + var body: some View { + HStack(spacing: 8) { + Image(systemName: "magnifyingglass") + .foregroundStyle(.secondary) + + TextField("Search messages", text: $query) + .textFieldStyle(.roundedBorder) + .onChange(of: query) { _, newValue in + onSearch(newValue) + } + + if matchCount > 0 { + Text("\(currentIndex + 1)/\(matchCount)") + .font(.caption) + .foregroundStyle(.secondary) + .fixedSize() + + Button(action: onPrev) { + Image(systemName: "chevron.up") + } + Button(action: onNext) { + Image(systemName: "chevron.down") + } + } + + Button(action: onClose) { + Image(systemName: "xmark.circle.fill") + .foregroundStyle(.secondary) + } + } + .padding(.horizontal) + .padding(.vertical, 6) + .background(.ultraThinMaterial) + } +} diff --git a/ios_client 0.8.5/Kecalek/Views/Components/CircularAvatarView.swift b/ios_client 0.8.5/Kecalek/Views/Components/CircularAvatarView.swift new file mode 100644 index 0000000..787a349 --- /dev/null +++ b/ios_client 0.8.5/Kecalek/Views/Components/CircularAvatarView.swift @@ -0,0 +1,46 @@ +import SwiftUI + +struct CircularAvatarView: View { + let name: String + var imageData: Data? + var size: CGFloat = 32 + var isGroup: Bool = false + + var body: some View { + if let imageData = imageData, let uiImage = UIImage(data: imageData) { + Image(uiImage: uiImage) + .resizable() + .aspectRatio(contentMode: .fill) + .frame(width: size, height: size) + .clipShape(Circle()) + } else { + // Default: colored circle with initial letter + ZStack { + Circle() + .fill(avatarColor) + .frame(width: size, height: size) + + Text(initial) + .font(.system(size: size * 0.4, weight: .semibold)) + .foregroundStyle(.white) + } + } + } + + private var initial: String { + String(name.prefix(1)).uppercased() + } + + /// Deterministic color from name hash (matching Python gui_client behavior) + private var avatarColor: Color { + let colors: [Color] = [ + .red, .orange, .yellow, .green, .mint, + .teal, .cyan, .blue, .indigo, .purple, .pink + ] + var hash = 0 + for char in name.unicodeScalars { + hash = hash &* 31 &+ Int(char.value) + } + return colors[abs(hash) % colors.count] + } +} diff --git a/ios_client 0.8.5/Kecalek/Views/Components/ConnectionIndicator.swift b/ios_client 0.8.5/Kecalek/Views/Components/ConnectionIndicator.swift new file mode 100644 index 0000000..bfa0fec --- /dev/null +++ b/ios_client 0.8.5/Kecalek/Views/Components/ConnectionIndicator.swift @@ -0,0 +1,36 @@ +import SwiftUI + +struct ConnectionIndicator: View { + let status: ConnectionStatus + + var body: some View { + HStack(spacing: 4) { + Circle() + .fill(statusColor) + .frame(width: 8, height: 8) + + if status != .connected { + Text(statusText) + .font(.caption2) + .foregroundStyle(.secondary) + } + } + } + + private var statusColor: Color { + switch status { + case .connected: return .green + case .connecting, .reconnecting: return .orange + case .disconnected: return .red + } + } + + private var statusText: String { + switch status { + case .connected: return "" + case .connecting: return "Connecting..." + case .reconnecting: return "Reconnecting..." + case .disconnected: return "Disconnected" + } + } +} diff --git a/ios_client 0.8.5/Kecalek/Views/Components/OnlineDotOverlay.swift b/ios_client 0.8.5/Kecalek/Views/Components/OnlineDotOverlay.swift new file mode 100644 index 0000000..4a2b645 --- /dev/null +++ b/ios_client 0.8.5/Kecalek/Views/Components/OnlineDotOverlay.swift @@ -0,0 +1,15 @@ +import SwiftUI + +struct OnlineDotOverlay: View { + var size: CGFloat = 12 + + var body: some View { + Circle() + .fill(.green) + .frame(width: size, height: size) + .overlay( + Circle() + .stroke(.white, lineWidth: 2) + ) + } +} diff --git a/ios_client 0.8.5/Kecalek/Views/Conversations/ConversationListView.swift b/ios_client 0.8.5/Kecalek/Views/Conversations/ConversationListView.swift new file mode 100644 index 0000000..5ebb5c7 --- /dev/null +++ b/ios_client 0.8.5/Kecalek/Views/Conversations/ConversationListView.swift @@ -0,0 +1,101 @@ +import SwiftUI + +struct ConversationListView: View { + var appState: AppState + @Bindable var viewModel: ConversationListVM + @State private var showNewConversation = false + @State private var showProfile = false + @State private var selectedConversation: Conversation? + + var body: some View { + NavigationStack { + List { + // Invitations section + if !viewModel.invitations.isEmpty { + Section { + ForEach(viewModel.invitations) { invitation in + InvitationBanner( + invitation: invitation, + onAccept: { + Task { + let (success, _) = await appState.chatClient.acceptInvitation(convId: invitation.conversationId) + if success { + await viewModel.refresh(chatClient: appState.chatClient) + } + } + }, + onDecline: { + Task { + _ = await appState.chatClient.declineInvitation(convId: invitation.conversationId) + await viewModel.refresh(chatClient: appState.chatClient) + } + } + ) + } + } header: { + Text("Invitations") + } + } + + // Conversations section + Section { + ForEach(viewModel.conversations) { conversation in + NavigationLink(value: conversation) { + ConversationRowView( + conversation: conversation, + currentUserId: appState.currentUser?.id ?? "", + isOnline: conversation.dmPartnerId(currentUserId: appState.currentUser?.id ?? "") + .map { viewModel.onlineUsers.contains($0) } ?? false, + unreadCount: viewModel.unreadCounts[conversation.id] ?? 0, + avatarData: viewModel.avatarCache[conversation.id] + ) + } + .contextMenu { + Button(conversation.isFavorite ? "Remove from Favorites" : "Add to Favorites") { + viewModel.toggleFavorite(convId: conversation.id, email: appState.email) + } + } + } + } + } + .navigationTitle("Chats") + .navigationDestination(for: Conversation.self) { conversation in + ChatView( + conversation: conversation, + appState: appState, + conversationListVM: viewModel + ) + } + .toolbar { + ToolbarItem(placement: .topBarLeading) { + ConnectionIndicator(status: appState.connectionStatus) + } + ToolbarItem(placement: .topBarTrailing) { + HStack { + Button(action: { showProfile = true }) { + Image(systemName: "person.circle") + } + Button(action: { showNewConversation = true }) { + Image(systemName: "square.and.pencil") + } + } + } + } + .refreshable { + await viewModel.refresh(chatClient: appState.chatClient) + } + .sheet(isPresented: $showNewConversation) { + NewConversationSheet(appState: appState) { convId in + showNewConversation = false + await viewModel.refresh(chatClient: appState.chatClient) + } + } + .sheet(isPresented: $showProfile) { + ProfileView(appState: appState, isOwnProfile: true) + } + .task { + await viewModel.load(chatClient: appState.chatClient, email: appState.email) + } + } + } +} diff --git a/ios_client 0.8.5/Kecalek/Views/Conversations/ConversationRowView.swift b/ios_client 0.8.5/Kecalek/Views/Conversations/ConversationRowView.swift new file mode 100644 index 0000000..b6f1df2 --- /dev/null +++ b/ios_client 0.8.5/Kecalek/Views/Conversations/ConversationRowView.swift @@ -0,0 +1,60 @@ +import SwiftUI + +struct ConversationRowView: View { + let conversation: Conversation + let currentUserId: String + let isOnline: Bool + let unreadCount: Int + var avatarData: Data? + + var body: some View { + HStack(spacing: 12) { + // Avatar + ZStack(alignment: .bottomTrailing) { + CircularAvatarView( + name: conversation.displayName(currentUserId: currentUserId), + imageData: avatarData, + size: 44, + isGroup: conversation.isGroup + ) + + if isOnline && !conversation.isGroup { + OnlineDotOverlay(size: 12) + } + } + + VStack(alignment: .leading, spacing: 2) { + HStack { + if conversation.isFavorite { + Image(systemName: "star.fill") + .font(.caption2) + .foregroundStyle(.yellow) + } + + Text(conversation.displayName(currentUserId: currentUserId)) + .font(unreadCount > 0 ? .body.bold() : .body) + .lineLimit(1) + } + + if conversation.isGroup { + Text("\(conversation.members.count) members") + .font(.caption) + .foregroundStyle(.secondary) + } + } + + Spacer() + + if unreadCount > 0 { + Text("\(unreadCount)") + .font(.caption2.bold()) + .foregroundStyle(.white) + .padding(.horizontal, 8) + .padding(.vertical, 2) + .background(Color.blue) + .clipShape(Capsule()) + } + } + .padding(.vertical, 4) + } +} diff --git a/ios_client 0.8.5/Kecalek/Views/Conversations/NewConversationSheet.swift b/ios_client 0.8.5/Kecalek/Views/Conversations/NewConversationSheet.swift new file mode 100644 index 0000000..c0ffb31 --- /dev/null +++ b/ios_client 0.8.5/Kecalek/Views/Conversations/NewConversationSheet.swift @@ -0,0 +1,100 @@ +import SwiftUI + +struct NewConversationSheet: View { + var appState: AppState + var onCreated: (String) async -> Void + + @State private var email = "" + @State private var groupName = "" + @State private var isGroup = false + @State private var memberEmails: [String] = [""] + @State private var isLoading = false + @State private var errorMessage: String? + @Environment(\.dismiss) private var dismiss + + var body: some View { + NavigationStack { + Form { + Section { + Toggle("Create Group", isOn: $isGroup) + + if isGroup { + TextField("Group Name", text: $groupName) + } + } + + Section(isGroup ? "Members" : "Recipient") { + if isGroup { + ForEach(memberEmails.indices, id: \.self) { index in + TextField("Email", text: $memberEmails[index]) + .textContentType(.emailAddress) + .keyboardType(.emailAddress) + .autocapitalization(.none) + } + Button("Add Member") { + memberEmails.append("") + } + } else { + TextField("Email", text: $email) + .textContentType(.emailAddress) + .keyboardType(.emailAddress) + .autocapitalization(.none) + } + } + + if let error = errorMessage { + Section { + Text(error) + .foregroundStyle(.red) + } + } + } + .navigationTitle("New Conversation") + .navigationBarTitleDisplayMode(.inline) + .toolbar { + ToolbarItem(placement: .cancellationAction) { + Button("Cancel") { dismiss() } + } + ToolbarItem(placement: .confirmationAction) { + Button("Create") { + Task { await create() } + } + .disabled(isLoading) + } + } + } + } + + private func create() async { + isLoading = true + errorMessage = nil + + let emails: [String] + if isGroup { + emails = memberEmails.map { $0.trimmed }.filter { !$0.isEmpty } + guard !emails.isEmpty else { + errorMessage = "Add at least one member" + isLoading = false + return + } + } else { + guard !email.trimmed.isEmpty else { + errorMessage = "Enter an email address" + isLoading = false + return + } + emails = [email.trimmed] + } + + let name = isGroup && !groupName.trimmed.isEmpty ? groupName.trimmed : nil + let (convId, message) = await appState.chatClient.createConversation(emails: emails, name: name) + + isLoading = false + + if let convId = convId { + await onCreated(convId) + } else { + errorMessage = message + } + } +} diff --git a/ios_client 0.8.5/Kecalek/Views/Groups/CreateGroupSheet.swift b/ios_client 0.8.5/Kecalek/Views/Groups/CreateGroupSheet.swift new file mode 100644 index 0000000..cbeaa75 --- /dev/null +++ b/ios_client 0.8.5/Kecalek/Views/Groups/CreateGroupSheet.swift @@ -0,0 +1,4 @@ +import SwiftUI + +// Group creation is handled within NewConversationSheet via the isGroup toggle. +// This file exists for potential future separation. diff --git a/ios_client 0.8.5/Kecalek/Views/Groups/GroupInfoView.swift b/ios_client 0.8.5/Kecalek/Views/Groups/GroupInfoView.swift new file mode 100644 index 0000000..da1fb3c --- /dev/null +++ b/ios_client 0.8.5/Kecalek/Views/Groups/GroupInfoView.swift @@ -0,0 +1,301 @@ +import SwiftUI +import PhotosUI + +struct GroupInfoView: View { + @Binding var conversation: Conversation + var appState: AppState + var conversationListVM: ConversationListVM? + @State private var showRenameSheet = false + @State private var showLeaveConfirm = false + @State private var showAddMember = false + @State private var showRemoveConfirm = false + @State private var showAvatarPicker = false + @State private var newName = "" + @State private var addMemberEmail = "" + @State private var memberToRemove: ConversationMember? + @State private var errorMessage: String? + @State private var showError = false + @State private var isUploadingAvatar = false + @State private var groupAvatarData: Data? + @Environment(\.dismiss) private var dismiss + + private var isCreator: Bool { + conversation.createdBy == appState.currentUser?.id + } + + private func refreshConversation() async { + let convs = await appState.chatClient.listConversations() + if let updated = convs.first(where: { $0.id == conversation.id }) { + conversation = updated + } + await conversationListVM?.forceRefresh(chatClient: appState.chatClient) + } + + var body: some View { + NavigationStack { + List { + // Avatar section + Section { + HStack { + Spacer() + VStack(spacing: 8) { + CircularAvatarView( + name: conversation.name ?? "Group", + imageData: groupAvatarData ?? conversationListVM?.avatarCache[conversation.id], + size: 64, + isGroup: true + ) + + Text(conversation.name ?? "Group") + .font(.title2.bold()) + + Text("\(conversation.members.count) members") + .font(.subheadline) + .foregroundStyle(.secondary) + } + Spacer() + } + .listRowBackground(Color.clear) + } + + // Actions + if isCreator { + Section { + Button("Add Member") { + addMemberEmail = "" + showAddMember = true + } + + Button("Rename Group") { + newName = conversation.name ?? "" + showRenameSheet = true + } + + Button { + showAvatarPicker = true + } label: { + HStack { + Text("Change Avatar") + if isUploadingAvatar { + Spacer() + ProgressView() + .scaleEffect(0.8) + } + } + } + .disabled(isUploadingAvatar) + } + } + + // Members + Section("Members") { + ForEach(conversation.members) { member in + HStack { + CircularAvatarView(name: member.username, size: 32, isGroup: false) + + VStack(alignment: .leading) { + Text(member.username) + .font(.body) + Text(member.email) + .font(.caption) + .foregroundStyle(.secondary) + } + + Spacer() + + if member.userId == conversation.createdBy { + Text("Admin") + .font(.caption) + .foregroundStyle(.blue) + } + } + .contextMenu { + if isCreator && member.userId != appState.currentUser?.id { + Button("Remove from Group", role: .destructive) { + memberToRemove = member + showRemoveConfirm = true + } + } + } + } + } + + // Leave / Delete + Section { + Button("Leave Group", role: .destructive) { + showLeaveConfirm = true + } + + if isCreator { + Button("Delete Group", role: .destructive) { + Task { + _ = await appState.chatClient.deleteConversation(convId: conversation.id) + dismiss() + } + } + } + } + } + .navigationTitle("Group Info") + .navigationBarTitleDisplayMode(.inline) + .toolbar { + ToolbarItem(placement: .topBarTrailing) { + Button("Done") { dismiss() } + } + } + .alert("Leave Group?", isPresented: $showLeaveConfirm) { + Button("Cancel", role: .cancel) {} + Button("Leave", role: .destructive) { + Task { + _ = await appState.chatClient.leaveGroup(convId: conversation.id) + dismiss() + } + } + } + .alert("Remove Member?", isPresented: $showRemoveConfirm) { + Button("Cancel", role: .cancel) {} + Button("Remove", role: .destructive) { + if let member = memberToRemove { + Task { + let (success, msg) = await appState.chatClient.removeMember( + convId: conversation.id, userId: member.userId + ) + if success { + await refreshConversation() + } else { + errorMessage = msg + showError = true + } + } + } + } + } message: { + if let member = memberToRemove { + Text("Remove \(member.username) from the group?") + } + } + .alert("Add Member", isPresented: $showAddMember) { + TextField("Email", text: $addMemberEmail) + .textInputAutocapitalization(.never) + .keyboardType(.emailAddress) + Button("Cancel", role: .cancel) {} + Button("Add") { + let email = addMemberEmail.trimmingCharacters(in: .whitespacesAndNewlines) + guard !email.isEmpty else { return } + Task { + let (success, msg) = await appState.chatClient.addMember( + convId: conversation.id, email: email + ) + if success { + await refreshConversation() + } else { + errorMessage = msg + showError = true + } + } + } + } + .alert("Rename Group", isPresented: $showRenameSheet) { + TextField("Group Name", text: $newName) + Button("Cancel", role: .cancel) {} + Button("Rename") { + let trimmedName = newName.trimmingCharacters(in: .whitespacesAndNewlines) + guard !trimmedName.isEmpty else { return } + // Optimistic update - immediately reflect in UI + conversation.name = trimmedName + Task { + let (success, _) = await appState.chatClient.renameConversation(convId: conversation.id, name: trimmedName) + if success { + await refreshConversation() + } else { + // Revert on failure + await refreshConversation() + } + } + } + } + .alert("Error", isPresented: $showError) { + Button("OK") {} + } message: { + Text(errorMessage ?? "") + } + .sheet(isPresented: $showAvatarPicker) { + AvatarPickerView { imageData in + isUploadingAvatar = true + Task { + let success = await appState.chatClient.updateGroupAvatar( + convId: conversation.id, imageData: imageData + ) + isUploadingAvatar = false + if success { + // Update local avatar cache (memory + disk) + groupAvatarData = imageData + conversationListVM?.updateAvatar(convId: conversation.id, data: imageData) + await refreshConversation() + } else { + errorMessage = "Failed to update avatar" + showError = true + } + } + } + } + .task { + // Load current group avatar + if groupAvatarData == nil, let cached = conversationListVM?.avatarCache[conversation.id] { + groupAvatarData = cached + } else if groupAvatarData == nil { + groupAvatarData = await appState.chatClient.getGroupAvatar(convId: conversation.id) + } + await refreshConversation() + } + } + } +} + +// MARK: - Avatar Picker (PHPicker wrapper for avatar selection) + +private struct AvatarPickerView: UIViewControllerRepresentable { + let onImagePicked: (Data) -> Void + + func makeCoordinator() -> Coordinator { + Coordinator(onImagePicked: onImagePicked) + } + + func makeUIViewController(context: Context) -> PHPickerViewController { + var config = PHPickerConfiguration() + config.filter = .images + config.selectionLimit = 1 + let picker = PHPickerViewController(configuration: config) + picker.delegate = context.coordinator + return picker + } + + func updateUIViewController(_ uiViewController: PHPickerViewController, context: Context) {} + + class Coordinator: NSObject, PHPickerViewControllerDelegate { + let onImagePicked: (Data) -> Void + + init(onImagePicked: @escaping (Data) -> Void) { + self.onImagePicked = onImagePicked + } + + func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) { + guard let provider = results.first?.itemProvider, + provider.canLoadObject(ofClass: UIImage.self) else { + picker.dismiss(animated: true) + return + } + provider.loadObject(ofClass: UIImage.self) { [weak self] image, _ in + guard let uiImage = image as? UIImage, + let data = uiImage.jpegData(compressionQuality: 0.8) else { + DispatchQueue.main.async { picker.dismiss(animated: true) } + return + } + DispatchQueue.main.async { + self?.onImagePicked(data) + picker.dismiss(animated: true) + } + } + } + } +} diff --git a/ios_client 0.8.5/Kecalek/Views/Groups/InvitationBanner.swift b/ios_client 0.8.5/Kecalek/Views/Groups/InvitationBanner.swift new file mode 100644 index 0000000..5f9c877 --- /dev/null +++ b/ios_client 0.8.5/Kecalek/Views/Groups/InvitationBanner.swift @@ -0,0 +1,41 @@ +import SwiftUI + +struct InvitationBanner: View { + let invitation: Invitation + let onAccept: () -> Void + let onDecline: () -> Void + + var body: some View { + VStack(alignment: .leading, spacing: 8) { + HStack { + Image(systemName: "envelope.badge") + .foregroundStyle(.orange) + + VStack(alignment: .leading) { + Text(invitation.conversationName) + .font(.body.bold()) + Text("Invited by \(invitation.invitedByUsername)") + .font(.caption) + .foregroundStyle(.secondary) + } + + Spacer() + } + + HStack(spacing: 12) { + Button("Accept") { + onAccept() + } + .buttonStyle(.borderedProminent) + .controlSize(.small) + + Button("Decline") { + onDecline() + } + .buttonStyle(.bordered) + .controlSize(.small) + } + } + .padding(.vertical, 4) + } +} diff --git a/ios_client 0.8.5/Kecalek/Views/Profile/EditProfileView.swift b/ios_client 0.8.5/Kecalek/Views/Profile/EditProfileView.swift new file mode 100644 index 0000000..b993ca0 --- /dev/null +++ b/ios_client 0.8.5/Kecalek/Views/Profile/EditProfileView.swift @@ -0,0 +1,4 @@ +import SwiftUI + +// Profile editing is handled within ProfileView when isOwnProfile = true. +// This file exists for potential future separation. diff --git a/ios_client 0.8.5/Kecalek/Views/Profile/ProfileView.swift b/ios_client 0.8.5/Kecalek/Views/Profile/ProfileView.swift new file mode 100644 index 0000000..542c64f --- /dev/null +++ b/ios_client 0.8.5/Kecalek/Views/Profile/ProfileView.swift @@ -0,0 +1,277 @@ +import SwiftUI +import PhotosUI +import UIKit + +struct ProfileView: View { + var appState: AppState + var isOwnProfile: Bool + var userId: String? + @State private var viewModel = ProfileViewModel() + @State private var showLogoutConfirm = false + @State private var showAvatarPicker = false + @State private var showAuthorizeDevice = false + @State private var showRotateKeys = false + @State private var rotatePassword = "" + @State private var isRotating = false + @State private var rotateMessage: String? + @State private var rotateIsError = false + @State private var showChangeUsername = false + @State private var newUsername = "" + @State private var showChangePassword = false + @State private var oldPassword = "" + @State private var newPassword = "" + @State private var confirmNewPassword = "" + @State private var showVerification = false + @Environment(\.dismiss) private var dismiss + + var body: some View { + NavigationStack { + Form { + // Avatar + Section { + HStack { + Spacer() + VStack(spacing: 8) { + if let avatarData = viewModel.avatarData, + let uiImage = UIImage(data: avatarData) { + Image(uiImage: uiImage) + .resizable() + .aspectRatio(contentMode: .fill) + .frame(width: 80, height: 80) + .clipShape(Circle()) + } else { + CircularAvatarView( + name: viewModel.profile?.username ?? "?", + size: 80, + isGroup: false + ) + } + + if isOwnProfile { + Button("Change Photo") { + showAvatarPicker = true + } + .font(.caption) + } + } + Spacer() + } + .listRowBackground(Color.clear) + } + + // Info + Section("Info") { + if let username = viewModel.profile?.username { + LabeledContent("Username", value: username) + } + if let email = viewModel.profile?.email { + LabeledContent("Email", value: email) + } + } + + if isOwnProfile { + // Editable fields + Section("Contact") { + TextField("Phone", text: $viewModel.phone) + .keyboardType(.phonePad) + Toggle("Phone visible to contacts", isOn: $viewModel.phoneVisible) + + TextField("Location", text: $viewModel.location) + Toggle("Location visible to contacts", isOn: $viewModel.locationVisible) + } + } else { + // Read-only view + if let phone = viewModel.profile?.phone, viewModel.profile?.phoneVisible == true { + Section("Contact") { + LabeledContent("Phone", value: phone) + } + } + if let location = viewModel.profile?.location, viewModel.profile?.locationVisible == true { + Section("Location") { + LabeledContent("Location", value: location) + } + } + } + + if !isOwnProfile, let uid = userId { + Section("Security") { + NavigationLink { + SafetyNumberView( + peerUserId: uid, + peerUsername: viewModel.profile?.username ?? "User", + chatClient: appState.chatClient + ) + } label: { + Label("Verify Identity", systemImage: "checkmark.shield") + } + } + } + + if let error = viewModel.errorMessage { + Section { + Text(error) + .foregroundStyle(.red) + } + } + + if isOwnProfile { + Section("Account") { + Button { + newUsername = viewModel.profile?.username ?? "" + showChangeUsername = true + } label: { + Label("Change Username", systemImage: "person.text.rectangle") + } + + Button { + showChangePassword = true + } label: { + Label("Change Password", systemImage: "key") + } + } + + Section("Security") { + Button { + showAuthorizeDevice = true + } label: { + Label("Authorize New Device", systemImage: "iphone.badge.checkmark") + } + + Button { + showRotateKeys = true + } label: { + Label("Rotate Keys", systemImage: "arrow.triangle.2.circlepath") + } + } + + Section { + Button(role: .destructive) { + showLogoutConfirm = true + } label: { + HStack { + Spacer() + Text("Logout") + Spacer() + } + } + } + } + } + .navigationTitle(isOwnProfile ? "My Profile" : "Profile") + .navigationBarTitleDisplayMode(.inline) + .toolbar { + ToolbarItem(placement: .topBarTrailing) { + if isOwnProfile { + Button("Save") { + Task { + let success = await viewModel.saveProfile(chatClient: appState.chatClient) + if success { + dismiss() + } + } + } + .disabled(viewModel.isSaving) + } + } + ToolbarItem(placement: .topBarLeading) { + if !isOwnProfile { + Button { + dismiss() + } label: { + Image(systemName: "xmark") + } + } + } + } + .alert("Logout", isPresented: $showLogoutConfirm) { + Button("Cancel", role: .cancel) {} + Button("Logout", role: .destructive) { + Task { + await appState.logout() + } + } + } message: { + Text("Are you sure you want to logout?") + } + .sheet(isPresented: $showAvatarPicker) { + ImagePickerView { data in + Task { + await viewModel.uploadAvatar(imageData: data, chatClient: appState.chatClient) + } + } + } + .sheet(isPresented: $showAuthorizeDevice) { + AuthorizeDeviceView(appState: appState) + } + .alert("Rotate Keys", isPresented: $showRotateKeys) { + SecureField("Password", text: $rotatePassword) + Button("Cancel", role: .cancel) { rotatePassword = "" } + Button("Rotate") { + Task { + isRotating = true + let (success, msg) = await appState.chatClient.rotateKeys(password: rotatePassword) + rotatePassword = "" + isRotating = false + rotateMessage = msg + rotateIsError = !success + } + } + } message: { + Text("Enter your password to generate new keys. All other devices will be disconnected.") + } + .alert(rotateIsError ? "Error" : "Success", isPresented: Binding( + get: { rotateMessage != nil }, + set: { if !$0 { rotateMessage = nil } } + )) { + Button("OK") { rotateMessage = nil } + } message: { + Text(rotateMessage ?? "") + } + .alert("Change Username", isPresented: $showChangeUsername) { + TextField("New username", text: $newUsername) + Button("Cancel", role: .cancel) { newUsername = "" } + Button("Change") { + Task { + let success = await viewModel.changeUsername(newUsername: newUsername, chatClient: appState.chatClient) + if success { + await viewModel.loadProfile(chatClient: appState.chatClient) + } + newUsername = "" + } + } + } message: { + Text("Enter a new display name.") + } + .alert("Change Password", isPresented: $showChangePassword) { + SecureField("Current password", text: $oldPassword) + SecureField("New password", text: $newPassword) + SecureField("Confirm new password", text: $confirmNewPassword) + Button("Cancel", role: .cancel) { + oldPassword = "" + newPassword = "" + confirmNewPassword = "" + } + Button("Change") { + Task { + guard newPassword == confirmNewPassword else { + viewModel.errorMessage = "Passwords don't match" + return + } + _ = await viewModel.changePassword( + oldPassword: oldPassword, newPassword: newPassword, + chatClient: appState.chatClient + ) + oldPassword = "" + newPassword = "" + confirmNewPassword = "" + } + } + } message: { + Text("Enter your current password and a new password.") + } + .task { + await viewModel.loadProfile(userId: userId, chatClient: appState.chatClient) + } + } + } +} diff --git a/ios_client 0.8.5/Kecalek/Views/Verification/QRCodeScannerView.swift b/ios_client 0.8.5/Kecalek/Views/Verification/QRCodeScannerView.swift new file mode 100644 index 0000000..899cf39 --- /dev/null +++ b/ios_client 0.8.5/Kecalek/Views/Verification/QRCodeScannerView.swift @@ -0,0 +1,149 @@ +import SwiftUI +import AVFoundation + +struct QRCodeScannerView: View { + let onScan: (Data) -> Void + @Environment(\.dismiss) private var dismiss + @State private var cameraPermission: CameraPermission = .unknown + + enum CameraPermission { + case unknown, granted, denied + } + + var body: some View { + NavigationStack { + ZStack { + switch cameraPermission { + case .unknown: + ProgressView("Requesting camera access...") + case .denied: + VStack(spacing: 16) { + Image(systemName: "camera.fill") + .font(.system(size: 48)) + .foregroundStyle(.secondary) + Text("Camera access is required to scan QR codes.") + .multilineTextAlignment(.center) + Button("Open Settings") { + if let url = URL(string: UIApplication.openSettingsURLString) { + UIApplication.shared.open(url) + } + } + .buttonStyle(.borderedProminent) + } + .padding() + case .granted: + ScannerRepresentable(onScan: { data in + onScan(data) + dismiss() + }) + .ignoresSafeArea() + } + } + .navigationTitle("Scan QR Code") + .navigationBarTitleDisplayMode(.inline) + .toolbar { + ToolbarItem(placement: .topBarLeading) { + Button("Cancel") { dismiss() } + } + } + } + .task { + await checkCameraPermission() + } + } + + private func checkCameraPermission() async { + let status = AVCaptureDevice.authorizationStatus(for: .video) + switch status { + case .authorized: + cameraPermission = .granted + case .notDetermined: + let granted = await AVCaptureDevice.requestAccess(for: .video) + cameraPermission = granted ? .granted : .denied + default: + cameraPermission = .denied + } + } +} + +// MARK: - Scanner UIKit wrapper + +private struct ScannerRepresentable: UIViewControllerRepresentable { + let onScan: (Data) -> Void + + func makeUIViewController(context: Context) -> ScannerViewController { + let vc = ScannerViewController() + vc.onScan = onScan + return vc + } + + func updateUIViewController(_ uiViewController: ScannerViewController, context: Context) {} +} + +final class ScannerViewController: UIViewController, AVCaptureMetadataOutputObjectsDelegate { + var onScan: ((Data) -> Void)? + private var captureSession: AVCaptureSession? + private var previewLayer: AVCaptureVideoPreviewLayer? + private var hasScanned = false + + override func viewDidLoad() { + super.viewDidLoad() + view.backgroundColor = .black + setupCamera() + } + + override func viewDidLayoutSubviews() { + super.viewDidLayoutSubviews() + previewLayer?.frame = view.bounds + } + + override func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) + captureSession?.stopRunning() + } + + private func setupCamera() { + let session = AVCaptureSession() + captureSession = session + + guard let device = AVCaptureDevice.default(for: .video), + let input = try? AVCaptureDeviceInput(device: device), + session.canAddInput(input) else { + return + } + session.addInput(input) + + let output = AVCaptureMetadataOutput() + guard session.canAddOutput(output) else { return } + session.addOutput(output) + output.setMetadataObjectsDelegate(self, queue: .main) + output.metadataObjectTypes = [.qr] + + let layer = AVCaptureVideoPreviewLayer(session: session) + layer.videoGravity = .resizeAspectFill + layer.frame = view.bounds + view.layer.addSublayer(layer) + previewLayer = layer + + DispatchQueue.global(qos: .userInitiated).async { + session.startRunning() + } + } + + func metadataOutput(_ output: AVCaptureMetadataOutput, + didOutput metadataObjects: [AVMetadataObject], + from connection: AVCaptureConnection) { + guard !hasScanned, + let object = metadataObjects.first as? AVMetadataMachineReadableCodeObject, + object.type == .qr else { return } + + hasScanned = true + captureSession?.stopRunning() + + // QR codes contain base64-encoded binary data (matching Python client) + if let stringValue = object.stringValue, + let data = Data(base64Encoded: stringValue) { + onScan?(data) + } + } +} diff --git a/ios_client 0.8.5/Kecalek/Views/Verification/SafetyNumberView.swift b/ios_client 0.8.5/Kecalek/Views/Verification/SafetyNumberView.swift new file mode 100644 index 0000000..389e51b --- /dev/null +++ b/ios_client 0.8.5/Kecalek/Views/Verification/SafetyNumberView.swift @@ -0,0 +1,144 @@ +import SwiftUI +import CoreImage.CIFilterBuiltins + +struct SafetyNumberView: View { + let peerUserId: String + let peerUsername: String + var chatClient: ChatClient + @State private var vm = VerificationVM() + @State private var showQRScanner = false + + var body: some View { + ScrollView { + VStack(spacing: 24) { + // Verification status badge + VerificationStatusView(status: vm.verificationStatus) + .padding(.top) + + // Safety number + if let safetyNumber = vm.safetyNumber { + VStack(spacing: 8) { + Text("Safety Number") + .font(.headline) + + Text("If both you and \(peerUsername) see the same number, your communication is secure.") + .font(.caption) + .foregroundStyle(.secondary) + .multilineTextAlignment(.center) + .padding(.horizontal) + + Text(safetyNumber) + .font(.system(.title2, design: .monospaced)) + .padding() + .background(.quaternary) + .clipShape(RoundedRectangle(cornerRadius: 12)) + } + } + + // QR Code + if let qrData = vm.qrCodeData { + VStack(spacing: 8) { + Text("Your QR Code") + .font(.headline) + + if let qrImage = generateQRCode(from: qrData) { + Image(uiImage: qrImage) + .interpolation(.none) + .resizable() + .scaledToFit() + .frame(width: 200, height: 200) + .padding() + .background(.white) + .clipShape(RoundedRectangle(cornerRadius: 12)) + } + } + } + + // Fingerprints + VStack(spacing: 12) { + if let myFP = vm.myFingerprint { + VStack(spacing: 4) { + Text("Your Fingerprint") + .font(.subheadline.bold()) + Text(myFP) + .font(.system(.caption, design: .monospaced)) + } + } + + if let peerFP = vm.peerFingerprint { + VStack(spacing: 4) { + Text("\(peerUsername)'s Fingerprint") + .font(.subheadline.bold()) + Text(peerFP) + .font(.system(.caption, design: .monospaced)) + } + } + } + + // Actions + VStack(spacing: 12) { + if vm.verificationStatus != "verified" { + Button { + Task { await vm.verifyContact(peerUserId: peerUserId, chatClient: chatClient) } + } label: { + Label("Mark as Verified", systemImage: "checkmark.shield.fill") + .frame(maxWidth: .infinity) + } + .buttonStyle(.borderedProminent) + + Button { + showQRScanner = true + } label: { + Label("Scan QR Code", systemImage: "qrcode.viewfinder") + .frame(maxWidth: .infinity) + } + .buttonStyle(.bordered) + } else { + Button(role: .destructive) { + Task { await vm.unverifyContact(peerUserId: peerUserId, chatClient: chatClient) } + } label: { + Label("Remove Verification", systemImage: "xmark.shield") + .frame(maxWidth: .infinity) + } + .buttonStyle(.bordered) + } + } + .padding(.horizontal) + + // Scan result + if let result = vm.scanResult { + Text(result) + .font(.callout) + .foregroundStyle(vm.scanSuccess == true ? .green : .red) + .padding() + } + } + .padding() + } + .navigationTitle("Verify \(peerUsername)") + .navigationBarTitleDisplayMode(.inline) + .sheet(isPresented: $showQRScanner) { + QRCodeScannerView { scannedData in + showQRScanner = false + Task { await vm.verifyQRCode(data: scannedData, chatClient: chatClient) } + } + } + .task { + await vm.loadVerification(peerUserId: peerUserId, chatClient: chatClient) + } + } + + private func generateQRCode(from data: Data) -> UIImage? { + let context = CIContext() + let filter = CIFilter.qrCodeGenerator() + // Base64-encode binary data — raw binary gets corrupted by QR readers (UTF-8 re-encoding) + let b64String = data.base64EncodedString() + filter.setValue(b64String.data(using: .ascii), forKey: "inputMessage") + filter.setValue("M", forKey: "inputCorrectionLevel") + guard let outputImage = filter.outputImage else { return nil } + let scale = 200.0 / outputImage.extent.width + let scaledImage = outputImage.transformed(by: CGAffineTransform(scaleX: scale, y: scale)) + guard let cgImage = context.createCGImage(scaledImage, from: scaledImage.extent) else { return nil } + return UIImage(cgImage: cgImage) + } +} diff --git a/ios_client 0.8.5/Kecalek/Views/Verification/VerificationStatusView.swift b/ios_client 0.8.5/Kecalek/Views/Verification/VerificationStatusView.swift new file mode 100644 index 0000000..a54f276 --- /dev/null +++ b/ios_client 0.8.5/Kecalek/Views/Verification/VerificationStatusView.swift @@ -0,0 +1,43 @@ +import SwiftUI + +struct VerificationStatusView: View { + let status: String // "verified", "trusted", "unverified" + + var body: some View { + HStack(spacing: 6) { + Image(systemName: iconName) + .foregroundStyle(iconColor) + Text(displayText) + .font(.subheadline.bold()) + .foregroundStyle(iconColor) + } + .padding(.horizontal, 12) + .padding(.vertical, 6) + .background(iconColor.opacity(0.12)) + .clipShape(Capsule()) + } + + private var iconName: String { + switch status { + case "verified": return "checkmark.shield.fill" + case "trusted": return "shield.fill" + default: return "shield.slash" + } + } + + private var iconColor: Color { + switch status { + case "verified": return .green + case "trusted": return .blue + default: return .secondary + } + } + + private var displayText: String { + switch status { + case "verified": return "Verified" + case "trusted": return "Trusted" + default: return "Unverified" + } + } +} diff --git a/ios_client 0.8.5/SECURITY_REVIEW.md b/ios_client 0.8.5/SECURITY_REVIEW.md new file mode 100644 index 0000000..ca2d455 --- /dev/null +++ b/ios_client 0.8.5/SECURITY_REVIEW.md @@ -0,0 +1,204 @@ +# Security Review: Kecalek iOS v0.8.5 + +**Datum:** 14. března 2026 +**Platforma:** iOS 26+ / Swift 6 +**Architektura:** MVVM + Actor-based concurrency +**Rozsah:** 57 Swift souborů, ~11 500 řádků kódu +**Typ aplikace:** End-to-end encrypted messaging (Signal Protocol) + +--- + +## 1. Shrnutí + +Kecalek je kryptograficky vyspělá messaging aplikace postavená na Signal Protocol (Double Ratchet, X3DH, Sender Keys). Využívá výhradně nativní Apple frameworky bez externích závislostí. Celková bezpečnostní úroveň je vysoká — během review byly identifikovány převážně provozní nedostatky, nikoli fundamentální architekturální chyby. + +--- + +## 2. Kryptografická architektura + +### 2.1 Protokoly a algoritmy + +| Komponenta | Implementace | Hodnocení | +|---|---|---| +| Key Exchange | X3DH (Extended Triple Diffie-Hellman) | Odpovídá Signal specifikaci | +| DM šifrování | Double Ratchet (AES-256-GCM) | Forward secrecy zajištěna | +| Skupinové šifrování | Sender Keys (AES-256-GCM) | Distribuce přes pairwise DM | +| Autentizace | RSA-4096 challenge-response (PSS-SHA256) | Silné | +| Úložiště klíčů | PBKDF2-HMAC-SHA256 (600 000 iterací) + AES-256-GCM | Odpovídající | +| Identity keys | Ed25519 (podepisování) + X25519 (key agreement) | Standard | +| KDF | HKDF-SHA256 | Standard | +| RNG | SecRandomCopyBytes | Kryptograficky bezpečný | +| Message padding | Bucket-based (64B–64KB) | Skrývá délku zpráv | + +### 2.2 Správa klíčů + +- **Signed Pre-Key (SPK):** rotace každých 7 dní s grace periodem pro předchozí SPK +- **One-Time Pre-Keys (OPK):** batch 50 kusů, doplnění při prahu 20 +- **Max skip:** 256 zpráv na chain (ochrana proti DoS přes vynucené přeskakování) +- **TOFU registry:** sledování identity klíčů kontaktů s možností manuální verifikace +- **Safety numbers:** 60místné číslo (SHA-512, 5 200 iterací) + QR kód verifikace + +### 2.3 Pozitivní nálezy + +- Žádné použití zastaralých algoritmů (MD5, SHA1, DES, RC4) +- Žádné vlastní kryptografické primitivy — vše přes CryptoKit a Security framework +- Správná implementace AAD (Associated Authenticated Data) v AES-GCM +- Snapshot/restore mechanismus pro atomické ratchet operace (M9 fix) +- Self-encryption pro multi-device synchronizaci vlastních zpráv + +--- + +## 3. Nalezené zranitelnosti a nápravná opatření + +### 3.1 KRITICKÉ — Opraveno + +#### 3.1.1 Únik kryptografických klíčů přes debug výpisy + +**Popis:** 160 `print()` volání v produkčním kódu vypisovalo citlivý kryptografický materiál do systémových logů — root keys, chain keys, message keys, identity keys, shared secrets, DH výstupy, nonce hodnoty. + +**Riziko:** Na iOS jsou systémové logy čitelné přes USB (Console.app), diagnostické profily a potenciálně dalšími aplikacemi. Útočník s fyzickým přístupem k zařízení nebo se schopností číst logy mohl získat kompletní kryptografický stav relace. + +**Dotčené soubory:** + +- `Core/ChatClient.swift` — 118 výskytů (session data, OPK IDs, decryption debug) +- `Crypto/DoubleRatchet.swift` — 11 výskytů (root keys, DH public keys, message keys, nonce) +- `Crypto/X3DH.swift` — 9 výskytů (identity keys, ephemeral keys, DH outputs, shared secrets) +- `ViewModels/AuthViewModel.swift` — 7 výskytů +- `AppState.swift` — 7 výskytů +- `Core/MessageCache.swift` — 3 výskyty +- `ViewModels/ChatViewModel.swift` — 2 výskyty +- `ViewModels/ConversationListVM.swift` — 3 výskyty + +**Náprava:** Všech 160 print statements zabaleno do `#if DEBUG` / `#endif` bloků. V release buildech nebude žádný kryptografický materiál logován. + +#### 3.1.2 Insecure TLS — bypass ověření certifikátu a volitelné TLS + +**Popis:** Parametr `tlsInsecure` umožňoval kompletní vypnutí TLS certificate verification. Navíc bylo TLS volitelné — uživatel mohl v UI vypnout šifrování transportní vrstvy přes toggle "Use TLS". + +**Riziko:** MitM útok — útočník na síti mohl odposlouchávat a modifikovat veškerou komunikaci, včetně challenge-response autentizace a metadat. + +**Dotčené soubory:** + +- `Network/ConnectionManager.swift` +- `Core/ChatClient.swift` +- `ViewModels/AuthViewModel.swift` +- `Views/Auth/LoginView.swift` +- `Views/Auth/PairingView.swift` +- `Core/KeychainService.swift` + +**Náprava:** TLS je nyní povinné bez výjimek. Parametry `useTLS` a `tlsInsecure` kompletně odstraněny z celého codebase. Toggle "Use TLS" odstraněn z UI. `ConnectionManager.connect()` vždy navazuje TLS spojení. Credentials v Keychainu již neukládají `useTLS` flag. + +--- + +### 3.2 VYSOKÉ — Opraveno + +#### 3.2.1 Heslo v paměti po úspěšném přihlášení + +**Popis:** `AuthViewModel` uchovával heslo jako `String` property i po úspěšném loginu. Swift String je immutable a garbage collector jej může držet v paměti neomezeně dlouho. + +**Riziko:** Memory dump útok — při fyzickém přístupu k zařízení nebo exploitu s přístupem do paměti procesu mohl útočník extrahovat heslo. + +**Dotčený soubor:** `ViewModels/AuthViewModel.swift` + +**Náprava:** Properties `password` a `confirmPassword` jsou vynulovány ihned po úspěšném přihlášení a uložení do Keychainu. + +**Poznámka:** Swift `String` neumožňuje bezpečné přepisování paměti (na rozdíl od `UnsafeMutableBufferPointer`). Kompletní mitigace by vyžadovala vlastní typ pro citlivé řetězce. Aktuální řešení minimalizuje dobu expozice. + +--- + +### 3.3 STŘEDNÍ — Opraveno + +#### 3.3.1 Clipboard bez automatického vymazání + +**Popis:** Funkce kopírování zprávy zapisovala text do systémového clipboardu (`UIPasteboard.general`) bez časového omezení. + +**Riziko:** Jiné aplikace mohou číst obsah clipboardu (iOS 14+ zobrazuje notifikaci, ale nezabraňuje přístupu). Citlivý obsah zpráv mohl zůstat v clipboardu neomezeně. + +**Dotčený soubor:** `Views/Chat/MessageBubbleView.swift` + +**Náprava:** Přidán automatický clear clipboardu po 30 sekundách s kontrolou, že obsah nebyl mezitím uživatelem přepsán. + +#### 3.3.2 Komentované vývojové IP adresy + +**Popis:** `Constants.swift` obsahoval komentované dev server adresy (`192.168.88.65`, `85.71.71.188`), které odhalovaly interní síťovou infrastrukturu. + +**Dotčený soubor:** `Utilities/Constants.swift` + +**Náprava:** Komentované IP adresy odstraněny. + +--- + +### 3.4 STŘEDNÍ — Neřešeno (doporučení) + +#### 3.4.1 Chybějící certificate pinning + +**Popis:** Aplikace se spoléhá výhradně na systémovou validaci TLS certifikátů. Neimplementuje certificate pinning ani SPKI pinning. + +**Riziko:** Při kompromitaci certifikační autority, na enterprise-managed zařízeních s vlastním root CA, nebo při state-level útoku může útočník provést MitM. Dopady jsou omezené díky E2EE (obsah zpráv zůstává chráněn), ale metadata (kdo s kým komunikuje, timing) by byla vystavena. + +**Doporučení:** Implementovat SPKI pinning pro produkční server `chat.ai-tech.news` pomocí Network.framework `sec_protocol_options_set_verify_block` s vlastní validací veřejného klíče serveru. + +#### 3.4.2 Chybějící jailbreak detekce + +**Popis:** Aplikace nedetekuje jailbreaknutá zařízení a nevaruje uživatele. + +**Riziko:** Na jailbreaknutém zařízení jsou oslabeny iOS sandbox protekce — jiné aplikace mohou přistupovat k souborům aplikace, Keychain items mohou být extrahovány, a iOS file protection je částečně neúčinná. + +**Doporučení:** Implementovat detekci (existence `/Applications/Cydia.app`, zápis mimo sandbox, dynamické knihovny) a zobrazit varování uživateli. Neblokovat použití — pouze informovat o riziku. + +--- + +## 4. Pozitivní bezpečnostní nálezy + +### 4.1 Keychain + +Implementace v `KeychainService.swift` je správná: + +- Přístupnost: `kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly` +- Biometrická ochrana: `.biometryAny` (Face ID / Touch ID) +- Credentials nejsou exportovatelné na jiná zařízení +- Správné error handling pro biometrické selhání + +### 4.2 File protection + +Všechny soubory na disku používají `.completeFileProtection` — jsou šifrované iOS file protection a přístupné pouze když je zařízení odemčené. + +### 4.3 Šifrování lokálních dat + +Všechny persistentní soubory (sessions, sender keys, message cache, conversation cache, avatary, TOFU registry) jsou šifrované AES-256-GCM s klíčem derivovaným přes HKDF z identity private key. + +### 4.4 Žádné externí závislosti + +Aplikace nepoužívá žádné third-party knihovny (CocoaPods, SPM, Carthage). Veškerá kryptografie běží přes nativní Apple frameworky (CryptoKit, Security, CommonCrypto). To eliminuje supply chain rizika. + +### 4.5 Žádné WebView + +Celé UI je nativní SwiftUI/UIKit. Absence WebView eliminuje kategorii XSS a JavaScript injection zranitelností. + +### 4.6 Brute-force ochrana + +Login implementuje exponenciální backoff (2^n sekund, max 300s) při neúspěšných pokusech. Server může vyžadovat PoW challenge při registračních surge. + +### 4.7 Bezpečná registrace + +Registrační flow zahrnuje email verifikaci a volitelný SHA-256 Proof-of-Work challenge jako ochranu proti automatizovaným registracím. + +### 4.8 Actor isolation + +`ChatClient` je implementován jako Swift actor, což garantuje thread-safe přístup ke kryptografickému stavu bez možnosti race conditions. + +--- + +## 5. Shrnutí změn + +| # | Závažnost | Nález | Stav | +|---|---|---|---| +| 3.1.1 | Kritická | Debug výpisy kryptografických klíčů (160×) | **Opraveno** | +| 3.1.2 | Kritická | TLS insecure bypass + volitelné TLS | **Opraveno** | +| 3.2.1 | Vysoká | Heslo zůstává v paměti po loginu | **Opraveno** | +| 3.3.1 | Střední | Clipboard bez auto-clear | **Opraveno** | +| 3.3.2 | Střední | Dev IP adresy v kódu | **Opraveno** | +| 3.4.1 | Střední | Chybějící certificate pinning | Doporučení | +| 3.4.2 | Střední | Chybějící jailbreak detekce | Doporučení | + +**Celkové hodnocení po opravách:** Aplikace splňuje vysoké bezpečnostní standardy pro E2EE messaging. Kryptografická architektura je solidní a odpovídá Signal Protocol specifikaci. Zbývající doporučení se týkají defense-in-depth opatření.