# Kecalek Android — TODO & Implementation Status ## ✅ DONE — Infrastructure (Phase 0-3) ### Phase 0 — Gradle + Base - [x] Gradle setup (Tink, Bouncy Castle, Room/SQLCipher, Hilt, Compose, OkHttp) - [x] Catppuccin Mocha dark theme + Material 3 - [x] Navigation (NavHost, routes) - [x] Domain models (Message, Conversation, User, MessageReaction, etc.) - [x] Room entities + DAOs (MessageDao, ConversationDao, UserCacheDao) - [x] AppDatabase with SQLCipher ### Phase 1 — Crypto (11 files) - [x] AesGcm.kt — AES-256-GCM - [x] Hkdf.kt — HKDF - [x] ECP1.kt / KeyEncryption.kt — password-based key encryption - [x] Ed25519Crypto.kt — Ed25519 sign/verify + Ed→X25519 conversion - [x] X25519Crypto.kt — X25519 DH - [x] RSACrypto.kt — RSA-PSS for login - [x] MessagePadding.kt — bucketed padding 64B–64KB - [x] X3DH.kt — X3DH initiate/respond - [x] DoubleRatchet.kt — Double Ratchet encrypt/decrypt/import/export - [x] SenderKey.kt — group sender key protocol - [x] ContactVerification.kt — safety numbers (SHA-512 × 5200, 60 digits) ### Phase 2 — Network (3 files) - [x] ConnectionManager.kt — raw TCP, newline-delimited JSON - [x] ProtocolHandler.kt — request/response + push dispatch - [x] ServerApi.kt — 50 endpoints ### Phase 3 — Core + Repos + DI - [x] SessionManager.kt — login/register/session persistence - [x] KeyStorage.kt — encrypted key persistence - [x] ChatClient.kt — messaging engine (sendDm, decrypt, new_message handler) - [x] NotificationRouter.kt — routes 18 push types - [x] MessageRepository.kt — message CRUD + search + reactions - [x] ConversationRepository.kt — conversation CRUD - [x] AppModule.kt — AppDatabase singleton - [x] DatabaseModule.kt — DAO providers - [x] NetworkModule.kt — placeholder (net classes auto-wired) - [x] CryptoModule.kt — placeholder (crypto = Kotlin objects) ### Phase 4 — Service + Feature UI - [x] ChatPushService.kt (FCM/push forwarding to NotificationRouter) - [x] AuthViewModel.kt - [x] ConversationListViewModel.kt - [x] ChatViewModel.kt — core (loadMessages, sendMessage, incoming flow) - [x] ChatScreen.kt - [x] ConversationListScreen.kt - [x] Auth screens (login, register) --- ## 🔧 READY TO BUILD & TEST ### Build - [ ] Build in Android Studio (Ctrl+F9 / Build > Make Project) - Note: Cannot build from CLI (JAVA_HOME not set on Windows) - Expected: clean build with no compilation errors ### Integration Tests - [ ] Send DM: type message on Android → verify arrives on Python/iOS client - [ ] Receive DM: send from Python/iOS → verify appears on Android - [ ] Self-copy: message I send appears in my own chat view immediately - [ ] X3DH new session: first message to new user triggers X3DH + works correctly - [ ] Group message: send in group conversation → all members receive - [ ] Reaction: add emoji reaction → reflects on all clients - [ ] Pin: pin a message → shows in pinned list - [ ] Delete: delete message → removed from all clients --- ## 🟡 TODO — ChatViewModel Secondary Features ### Simple API calls (ready to implement) - [x] deleteMessage(messageId) — `api.deleteMessage()` + `messageRepository.markDeleted()` - [x] reactToMessage(messageId, reaction) — `api.reactMessage()` + `messageRepository.updateReactions()` - [x] pinMessage(messageId) — `api.pinMessage()` + `messageRepository.updatePinStatus()` - [x] markAsRead() — `api.markConversationRead(conversationId)` - [x] search(query) — `messageRepository.searchMessages()` + update searchResults in state - [x] nextSearchResult() / prevSearchResult() — navigate currentSearchIndex - [x] forwardMessage(messageId, targetConversationId) — re-encrypt plaintext for target conv ### Complex (need encryption pipeline) - [ ] sendImage(uri) — AES encrypt image → chunked upload via uploadImageStart/Chunk/End - ImageInfo {fileId, aesKey, iv, thumbnail, filename, size} stored in message - Payload: {"sender", "text", "image": {"file_id": ..., "key": b64, "iv": b64}, "timestamp"} - [ ] sendFile(uri) — similar to sendImage but with FileInfo - [ ] downloadFile(fileId) — downloadImage() chunks → reassemble → AES decrypt - Use offset pagination: loop downloadImage(fileId, offset) until complete --- ## 🟡 TODO — ChatClient Group Messaging - [ ] sendGroupMessage() — sender key protocol - Check if each member has received sender key - If not: send `_sender_key` control message first (DM encrypted per device) - Then encrypt group message with SenderKey.encrypt() - Track which members have the key in keyStorage or DB - [ ] Group member add: resend sender key to new member via DM - [ ] Group member remove: rotate sender key, resend to remaining members - [ ] Handle `member_added` / `member_removed` push notifications in ChatClient --- ## 🟠 KNOWN ISSUES ### ✅ FIXED: SQLCipher passphrase (AppModule.kt) - **Was**: hardcoded `"TODO_REPLACE_WITH_DERIVED_KEY"` passphrase - **Now**: random 32-byte key generated once, stored in EncryptedSharedPreferences (Android Keystore) - **Migration**: if old DB exists without stored passphrase → old DB deleted, new one created - Note: Could use HKDF-derived key from identity private (like Python), but Android Keystore approach is simpler and equally secure ### ✅ FIXED: AuthUiState.useTls = true (registration bug) - **Was**: `AuthUiState.useTls = true` → registration always tried TLS → SSL error if server is plain TCP - **Now**: `AuthUiState.useTls = false` — consistent with LoginScreen local default - LoginScreen always calls `updateServerConfig(useTls=false)` before login anyway - If server requires TLS, user can toggle it in Server Configuration section on LoginScreen ### Push notification handler registration - ChatClient.setupNotificationHandlers() is called from initialize() - If ChatClient is not initialized (user not logged in), push notifications are silently dropped - Fix: Check session state before trying to decrypt; store encrypted push and decrypt on next init ### Session persistence - Sessions (DoubleRatchet state) are saved to KeyStorage per device - If app is reinstalled, all sessions are lost → first message after reinstall needs X3DH - This is expected behavior but worth testing --- ## 📋 PROTOCOL COMPATIBILITY CHECKLIST ### sendDm() - [x] Recipients per-device (not per-user) - [x] Self-copy: device_id = SELF_DEVICE_ID, ratchet_header = {"self": true} - [x] X3DH header fields: "ik", "ek", "opk_id" (NOT "ik_pub", "ek_pub") - [x] Binary encoding: base64 via encodeBinary()/decodeBinary() - [x] Ratchet header: dh_pub (hex), n, pn via header.toMap() - [x] Plaintext payload: JSON {"sender", "text", "reply_to", "timestamp"} - [x] Message padding: MessagePadding.pad() before encrypt, unpad() after decrypt ### handleNewMessage() / decryptServerMessage() - [x] Push field: "sender_id" (NOT "sender_user_id") - [x] device_entries array: pick by myDeviceId or SELF_DEVICE_ID - [x] Self-copy detection: ratchet_header.self == true - [x] Control messages (_sender_key): import silently, don't display - [x] Flat fallback fields for backward compat --- ## 🔮 FUTURE — Nice to Have - [ ] Voice messages — record, encrypt with AES, upload as file - [ ] Disappearing messages — server-side TTL - [ ] Message editing — re-encrypt + server update - [ ] Key rotation — rotate RSA/Ed25519 identity keys - [ ] Device pairing — link second device (pairingStart/pairingSend) - [ ] Contact verification — UI to display safety numbers - [ ] Backup / restore — export encrypted sessions - [ ] Push notification channels — Android notification priorities - [ ] App lock (biometric / PIN)