Files
Kecalek/TODO.md
2026-03-12 19:30:43 +01:00

7.5 KiB
Raw Permalink Blame History

Kecalek Android — TODO & Implementation Status

DONE — Infrastructure (Phase 0-3)

Phase 0 — Gradle + Base

  • Gradle setup (Tink, Bouncy Castle, Room/SQLCipher, Hilt, Compose, OkHttp)
  • Catppuccin Mocha dark theme + Material 3
  • Navigation (NavHost, routes)
  • Domain models (Message, Conversation, User, MessageReaction, etc.)
  • Room entities + DAOs (MessageDao, ConversationDao, UserCacheDao)
  • AppDatabase with SQLCipher

Phase 1 — Crypto (11 files)

  • AesGcm.kt — AES-256-GCM
  • Hkdf.kt — HKDF
  • ECP1.kt / KeyEncryption.kt — password-based key encryption
  • Ed25519Crypto.kt — Ed25519 sign/verify + Ed→X25519 conversion
  • X25519Crypto.kt — X25519 DH
  • RSACrypto.kt — RSA-PSS for login
  • MessagePadding.kt — bucketed padding 64B64KB
  • X3DH.kt — X3DH initiate/respond
  • DoubleRatchet.kt — Double Ratchet encrypt/decrypt/import/export
  • SenderKey.kt — group sender key protocol
  • ContactVerification.kt — safety numbers (SHA-512 × 5200, 60 digits)

Phase 2 — Network (3 files)

  • ConnectionManager.kt — raw TCP, newline-delimited JSON
  • ProtocolHandler.kt — request/response + push dispatch
  • ServerApi.kt — 50 endpoints

Phase 3 — Core + Repos + DI

  • SessionManager.kt — login/register/session persistence
  • KeyStorage.kt — encrypted key persistence
  • ChatClient.kt — messaging engine (sendDm, decrypt, new_message handler)
  • NotificationRouter.kt — routes 18 push types
  • MessageRepository.kt — message CRUD + search + reactions
  • ConversationRepository.kt — conversation CRUD
  • AppModule.kt — AppDatabase singleton
  • DatabaseModule.kt — DAO providers
  • NetworkModule.kt — placeholder (net classes auto-wired)
  • CryptoModule.kt — placeholder (crypto = Kotlin objects)

Phase 4 — Service + Feature UI

  • ChatPushService.kt (FCM/push forwarding to NotificationRouter)
  • AuthViewModel.kt
  • ConversationListViewModel.kt
  • ChatViewModel.kt — core (loadMessages, sendMessage, incoming flow)
  • ChatScreen.kt
  • ConversationListScreen.kt
  • 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)

  • deleteMessage(messageId) — api.deleteMessage() + messageRepository.markDeleted()
  • reactToMessage(messageId, reaction) — api.reactMessage() + messageRepository.updateReactions()
  • pinMessage(messageId) — api.pinMessage() + messageRepository.updatePinStatus()
  • markAsRead() — api.markConversationRead(conversationId)
  • search(query) — messageRepository.searchMessages() + update searchResults in state
  • nextSearchResult() / prevSearchResult() — navigate currentSearchIndex
  • 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()

  • Recipients per-device (not per-user)
  • Self-copy: device_id = SELF_DEVICE_ID, ratchet_header = {"self": true}
  • X3DH header fields: "ik", "ek", "opk_id" (NOT "ik_pub", "ek_pub")
  • Binary encoding: base64 via encodeBinary()/decodeBinary()
  • Ratchet header: dh_pub (hex), n, pn via header.toMap()
  • Plaintext payload: JSON {"sender", "text", "reply_to", "timestamp"}
  • Message padding: MessagePadding.pad() before encrypt, unpad() after decrypt

handleNewMessage() / decryptServerMessage()

  • Push field: "sender_id" (NOT "sender_user_id")
  • device_entries array: pick by myDeviceId or SELF_DEVICE_ID
  • Self-copy detection: ratchet_header.self == true
  • Control messages (_sender_key): import silently, don't display
  • 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)