From 3247f2b284f60d1999c4519d037f8135ccd064dc Mon Sep 17 00:00:00 2001 From: k1ngsterr1 Date: Tue, 27 Jan 2026 18:16:14 +0500 Subject: [PATCH] AVATAR & IMAGES ARE WORKING ON DESKTOP --- .../rosetta/messenger/crypto/MessageCrypto.kt | 36 +++++++------------ .../chats/components/AttachmentComponents.kt | 11 +++--- 2 files changed, 20 insertions(+), 27 deletions(-) diff --git a/app/src/main/java/com/rosetta/messenger/crypto/MessageCrypto.kt b/app/src/main/java/com/rosetta/messenger/crypto/MessageCrypto.kt index 1c17957..2a95349 100644 --- a/app/src/main/java/com/rosetta/messenger/crypto/MessageCrypto.kt +++ b/app/src/main/java/com/rosetta/messenger/crypto/MessageCrypto.kt @@ -731,40 +731,35 @@ object MessageCrypto { */ fun encryptReplyBlob(replyJson: String, plainKeyAndNonce: ByteArray): String { return try { - android.util.Log.d("MessageCrypto", "🔐 encryptReplyBlob START") - android.util.Log.d("MessageCrypto", "🔐 Input length: ${replyJson.length} chars") - android.util.Log.d("MessageCrypto", "🔐 Input first 100: ${replyJson.take(100)}") - android.util.Log.d("MessageCrypto", "🔐 plainKeyAndNonce length: ${plainKeyAndNonce.size} bytes") // Convert plainKeyAndNonce to string - simulate JS Buffer.toString('utf-8') behavior // which replaces invalid UTF-8 sequences with U+FFFD val password = bytesToJsUtf8String(plainKeyAndNonce) - android.util.Log.d("MessageCrypto", "🔐 Password length: ${password.length} chars") - android.util.Log.d("MessageCrypto", "🔐 Password first 50 chars: ${password.take(50)}") - // Compress with zlib format (WITH header) - matching pako.deflate() - // CRITICAL: nowrap=false for zlib format (pako.deflate default) - val deflater = java.util.zip.Deflater(java.util.zip.Deflater.DEFAULT_COMPRESSION, false) + // Compress with pako (deflate) + val deflater = java.util.zip.Deflater() deflater.setInput(replyJson.toByteArray(Charsets.UTF_8)) deflater.finish() val compressedBuffer = ByteArray(replyJson.length * 2 + 100) val compressedSize = deflater.deflate(compressedBuffer) deflater.end() val compressed = compressedBuffer.copyOf(compressedSize) - android.util.Log.d("MessageCrypto", "🔐 Original size: ${replyJson.length}, Compressed size: ${compressed.size}") - // PBKDF2 key derivation (matching crypto-js) - // CRITICAL: Use our custom implementation that properly handles UTF-8! - // Java's PBEKeySpec uses UTF-16, but crypto-js uses UTF-8 - val keyBytes = generatePBKDF2Key(password) - android.util.Log.d("MessageCrypto", "🔐 PBKDF2 key generated: ${keyBytes.size} bytes") - android.util.Log.d("MessageCrypto", "🔐 Key first 16 bytes (hex): ${keyBytes.take(16).joinToString("") { "%02x".format(it) }}") + // PBKDF2 key derivation (matching RN: crypto.PBKDF2(password, 'rosetta', {keySize: 256/32, iterations: 1000})) + // CRITICAL: Must use SHA256 to match React Native (not SHA1!) + val factory = javax.crypto.SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256") + val spec = javax.crypto.spec.PBEKeySpec( + password.toCharArray(), + "rosetta".toByteArray(Charsets.UTF_8), + 1000, + 256 + ) + val secretKey = factory.generateSecret(spec) + val keyBytes = secretKey.encoded // Generate random IV (16 bytes) val iv = ByteArray(16) java.security.SecureRandom().nextBytes(iv) - android.util.Log.d("MessageCrypto", "🔐 IV generated: ${iv.size} bytes") - android.util.Log.d("MessageCrypto", "🔐 IV (hex): ${iv.joinToString("") { "%02x".format(it) }}") // AES-CBC encryption val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding") @@ -772,19 +767,14 @@ object MessageCrypto { val ivSpec = IvParameterSpec(iv) cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec) val ciphertext = cipher.doFinal(compressed) - android.util.Log.d("MessageCrypto", "🔐 AES encrypted: ${ciphertext.size} bytes") // Format: "ivBase64:ciphertextBase64" (same as RN new format) val ivBase64 = Base64.encodeToString(iv, Base64.NO_WRAP) val ctBase64 = Base64.encodeToString(ciphertext, Base64.NO_WRAP) val result = "$ivBase64:$ctBase64" - android.util.Log.d("MessageCrypto", "🔐 Final encrypted length: ${result.length} chars") - android.util.Log.d("MessageCrypto", "🔐 Final format check - contains colon: ${result.contains(":")}") - android.util.Log.d("MessageCrypto", "🔐 encryptReplyBlob END") result } catch (e: Exception) { - android.util.Log.e("MessageCrypto", "❌ encryptReplyBlob failed: ${e.message}", e) // Fallback: return plaintext (for backwards compatibility) replyJson } diff --git a/app/src/main/java/com/rosetta/messenger/ui/chats/components/AttachmentComponents.kt b/app/src/main/java/com/rosetta/messenger/ui/chats/components/AttachmentComponents.kt index f4c65ed..f4519a9 100644 --- a/app/src/main/java/com/rosetta/messenger/ui/chats/components/AttachmentComponents.kt +++ b/app/src/main/java/com/rosetta/messenger/ui/chats/components/AttachmentComponents.kt @@ -1079,11 +1079,14 @@ fun AvatarAttachment( val encryptedContent = TransportManager.downloadFile(attachment.id, downloadTag) downloadStatus = DownloadStatus.DECRYPTING - // 🔥 КРИТИЧНО: Аватар зашифрован с AVATAR_PASSWORD (как на desktop) - // НЕ используем ChaCha ключ сообщения! - val decrypted = CryptoManager.decryptWithPassword( + // КРИТИЧНО: chachaKey ЗАШИФРОВАН в БД (как в Desktop) + // Сначала расшифровываем его + val decryptedKeyAndNonce = MessageCrypto.decryptKeyFromSender(chachaKey, privateKey) + val decryptKeyString = String(decryptedKeyAndNonce, Charsets.UTF_8) + + val decrypted = MessageCrypto.decryptAttachmentBlobWithPassword( encryptedContent, - AvatarFileManager.getAvatarPassword() + decryptKeyString ) if (decrypted != null) {