Start preview

This commit is contained in:
filip
2026-03-12 19:30:43 +01:00
parent 3204bd6605
commit c73820b9ce
2 changed files with 395 additions and 0 deletions

164
TODO.md Normal file
View File

@@ -0,0 +1,164 @@
# 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 64B64KB
- [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)