feat: Enhance chat functionality by updating dialog handling and adding ChatsListViewModel for database integration

This commit is contained in:
k1ngsterr1
2026-01-10 23:06:41 +05:00
parent 7216cc0d0b
commit 219158ac7d
5 changed files with 403 additions and 51 deletions

View File

@@ -274,83 +274,89 @@ object MessageCrypto {
/**
* ECDH шифрование ключа для получателя
* Использует secp256k1 + AES как в RN версии
* Формат: Base64(iv:ciphertext:ephemeralPrivateKeyHex)
*/
fun encryptKeyForRecipient(keyAndNonce: ByteArray, recipientPublicKeyHex: String): String {
val secureRandom = SecureRandom()
val ecSpec = ECNamedCurveTable.getParameterSpec("secp256k1")
// Генерируем эфемерный приватный ключ
// Генерируем эфемерный приватный ключ (32 байта)
val ephemeralPrivateKeyBytes = ByteArray(32)
secureRandom.nextBytes(ephemeralPrivateKeyBytes)
val ephemeralPrivateKey = BigInteger(1, ephemeralPrivateKeyBytes)
val ephemeralPrivateKeyHex = ephemeralPrivateKeyBytes.toHex()
// Получаем эфемерный публичный ключ
val ephemeralPublicKey = ecSpec.g.multiply(ephemeralPrivateKey)
val ephemeralPublicKeyHex = ephemeralPublicKey.getEncoded(false).toHex()
// Парсим публичный ключ получателя
val recipientPublicKeyBytes = recipientPublicKeyHex.hexToBytes()
val recipientPublicKey = ecSpec.curve.decodePoint(recipientPublicKeyBytes)
// ECDH: получаем общий секрет
// ECDH: ephemeralPrivate * recipientPublic = sharedSecret
val sharedPoint = recipientPublicKey.multiply(ephemeralPrivateKey)
val sharedSecret = sharedPoint.normalize().xCoord.encoded
val sharedSecretHex = sharedSecret.toHex()
// Derive AES key from shared secret
val aesKey = MessageDigest.getInstance("SHA-256").digest(sharedSecret)
// Генерируем IV для AES
// Генерируем IV для AES (16 байт)
val iv = ByteArray(16)
secureRandom.nextBytes(iv)
val ivHex = iv.toHex()
// Шифруем keyAndNonce с AES-CBC
val secretKey = SecretKeySpec(aesKey, "AES")
// Шифруем keyAndNonce с AES-CBC используя sharedSecret как ключ
// ВАЖНО: Используем сам sharedSecret напрямую (32 байта) без SHA-256!
val aesKey = SecretKeySpec(sharedSecret, "AES")
val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
cipher.init(Cipher.ENCRYPT_MODE, secretKey, IvParameterSpec(iv))
cipher.init(Cipher.ENCRYPT_MODE, aesKey, IvParameterSpec(iv))
val encryptedKey = cipher.doFinal(keyAndNonce)
val encryptedKeyHex = encryptedKey.toHex()
// Формат: iv:ciphertext:ephemeralPublicKey (Base64)
val result = ByteArray(iv.size + encryptedKey.size + recipientPublicKeyBytes.size)
System.arraycopy(iv, 0, result, 0, iv.size)
System.arraycopy(encryptedKey, 0, result, iv.size, encryptedKey.size)
// Формат как в RN: btoa(ivHex:encryptedHex:ephemeralPrivateHex)
val combined = "$ivHex:$encryptedKeyHex:$ephemeralPrivateKeyHex"
// Возвращаем как Base64 с ephemeral public key в конце
return Base64.encodeToString(iv, Base64.NO_WRAP) + ":" +
Base64.encodeToString(encryptedKey, Base64.NO_WRAP) + ":" +
ephemeralPublicKeyHex
ProtocolManager.addLog("🔐 ECDH Encrypt:")
ProtocolManager.addLog(" - Shared secret: ${sharedSecretHex.take(40)}...")
ProtocolManager.addLog(" - IV: ${ivHex.take(32)}...")
ProtocolManager.addLog(" - Ephemeral private: ${ephemeralPrivateKeyHex.take(40)}...")
ProtocolManager.addLog(" - Recipient public: ${recipientPublicKeyHex.take(40)}...")
return Base64.encodeToString(combined.toByteArray(), Base64.NO_WRAP)
}
/**
* ECDH расшифровка ключа
* Формат: Base64(ivHex:encryptedHex:ephemeralPrivateHex)
*/
fun decryptKeyFromSender(encryptedKeyBase64: String, myPrivateKeyHex: String): ByteArray {
val parts = encryptedKeyBase64.split(":")
if (parts.size != 3) throw IllegalArgumentException("Invalid encrypted key format")
val combined = String(Base64.decode(encryptedKeyBase64, Base64.NO_WRAP))
val parts = combined.split(":")
if (parts.size != 3) throw IllegalArgumentException("Invalid encrypted key format: expected 3 parts, got ${parts.size}")
val iv = Base64.decode(parts[0], Base64.NO_WRAP)
val encryptedKey = Base64.decode(parts[1], Base64.NO_WRAP)
val ephemeralPublicKeyHex = parts[2]
val ivHex = parts[0]
val encryptedKeyHex = parts[1]
val ephemeralPrivateKeyHex = parts[2]
val iv = ivHex.hexToBytes()
val encryptedKey = encryptedKeyHex.hexToBytes()
val ecSpec = ECNamedCurveTable.getParameterSpec("secp256k1")
// Парсим эфемерный приватный ключ
val ephemeralPrivateKey = BigInteger(ephemeralPrivateKeyHex, 16)
val ephemeralPublicKey = ecSpec.g.multiply(ephemeralPrivateKey)
// Парсим мой приватный ключ
val myPrivateKey = BigInteger(myPrivateKeyHex, 16)
val myPublicKey = ecSpec.g.multiply(myPrivateKey)
// Парсим эфемерный публичный ключ отправителя
val ephemeralPublicKeyBytes = ephemeralPublicKeyHex.hexToBytes()
val ephemeralPublicKey = ecSpec.curve.decodePoint(ephemeralPublicKeyBytes)
// ECDH: получаем общий секрет
val sharedPoint = ephemeralPublicKey.multiply(myPrivateKey)
// ECDH: ephemeralPrivate * myPublic = sharedSecret
val sharedPoint = myPublicKey.multiply(ephemeralPrivateKey)
val sharedSecret = sharedPoint.normalize().xCoord.encoded
// Derive AES key from shared secret
val aesKey = MessageDigest.getInstance("SHA-256").digest(sharedSecret)
// Расшифровываем
val secretKey = SecretKeySpec(aesKey, "AES")
// Расшифровываем используя sharedSecret как AES ключ
val aesKey = SecretKeySpec(sharedSecret, "AES")
val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
cipher.init(Cipher.DECRYPT_MODE, secretKey, IvParameterSpec(iv))
cipher.init(Cipher.DECRYPT_MODE, aesKey, IvParameterSpec(iv))
return cipher.doFinal(encryptedKey)
}
@@ -359,15 +365,31 @@ object MessageCrypto {
* Полное шифрование сообщения для отправки
*/
fun encryptForSending(plaintext: String, recipientPublicKey: String): Pair<String, String> {
android.util.Log.d("MessageCrypto", "🔐 === START ENCRYPTION ===")
android.util.Log.d("MessageCrypto", "📝 Plaintext: '$plaintext'")
android.util.Log.d("MessageCrypto", "📝 Plaintext length: ${plaintext.length}")
android.util.Log.d("MessageCrypto", "🔑 Recipient public key: ${recipientPublicKey.take(20)}...")
// 1. Шифруем текст
android.util.Log.d("MessageCrypto", "⚡ Step 1: Encrypting message with XChaCha20-Poly1305...")
val encrypted = encryptMessage(plaintext)
android.util.Log.d("MessageCrypto", "✅ Ciphertext: ${encrypted.ciphertext.take(40)}...")
android.util.Log.d("MessageCrypto", "✅ Ciphertext length: ${encrypted.ciphertext.length}")
android.util.Log.d("MessageCrypto", "✅ Key: ${encrypted.key.take(20)}...")
android.util.Log.d("MessageCrypto", "✅ Nonce: ${encrypted.nonce.take(20)}...")
// 2. Собираем key + nonce
android.util.Log.d("MessageCrypto", "⚡ Step 2: Combining key + nonce...")
val keyAndNonce = encrypted.key.hexToBytes() + encrypted.nonce.hexToBytes()
android.util.Log.d("MessageCrypto", "✅ Combined keyAndNonce length: ${keyAndNonce.size} bytes")
// 3. Шифруем ключ для получателя
android.util.Log.d("MessageCrypto", "⚡ Step 3: Encrypting key with ECDH + AES...")
val encryptedKey = encryptKeyForRecipient(keyAndNonce, recipientPublicKey)
android.util.Log.d("MessageCrypto", "✅ Encrypted key: ${encryptedKey.take(40)}...")
android.util.Log.d("MessageCrypto", "✅ Encrypted key length: ${encryptedKey.length}")
android.util.Log.d("MessageCrypto", "🔐 === ENCRYPTION COMPLETE ===")
return Pair(encrypted.ciphertext, encryptedKey)
}