AVATAR & IMAGES ARE WORKING ON DESKTOP
This commit is contained in:
@@ -731,40 +731,35 @@ object MessageCrypto {
|
|||||||
*/
|
*/
|
||||||
fun encryptReplyBlob(replyJson: String, plainKeyAndNonce: ByteArray): String {
|
fun encryptReplyBlob(replyJson: String, plainKeyAndNonce: ByteArray): String {
|
||||||
return try {
|
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
|
// Convert plainKeyAndNonce to string - simulate JS Buffer.toString('utf-8') behavior
|
||||||
// which replaces invalid UTF-8 sequences with U+FFFD
|
// which replaces invalid UTF-8 sequences with U+FFFD
|
||||||
val password = bytesToJsUtf8String(plainKeyAndNonce)
|
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()
|
// Compress with pako (deflate)
|
||||||
// CRITICAL: nowrap=false for zlib format (pako.deflate default)
|
val deflater = java.util.zip.Deflater()
|
||||||
val deflater = java.util.zip.Deflater(java.util.zip.Deflater.DEFAULT_COMPRESSION, false)
|
|
||||||
deflater.setInput(replyJson.toByteArray(Charsets.UTF_8))
|
deflater.setInput(replyJson.toByteArray(Charsets.UTF_8))
|
||||||
deflater.finish()
|
deflater.finish()
|
||||||
val compressedBuffer = ByteArray(replyJson.length * 2 + 100)
|
val compressedBuffer = ByteArray(replyJson.length * 2 + 100)
|
||||||
val compressedSize = deflater.deflate(compressedBuffer)
|
val compressedSize = deflater.deflate(compressedBuffer)
|
||||||
deflater.end()
|
deflater.end()
|
||||||
val compressed = compressedBuffer.copyOf(compressedSize)
|
val compressed = compressedBuffer.copyOf(compressedSize)
|
||||||
android.util.Log.d("MessageCrypto", "🔐 Original size: ${replyJson.length}, Compressed size: ${compressed.size}")
|
|
||||||
|
|
||||||
// PBKDF2 key derivation (matching crypto-js)
|
// PBKDF2 key derivation (matching RN: crypto.PBKDF2(password, 'rosetta', {keySize: 256/32, iterations: 1000}))
|
||||||
// CRITICAL: Use our custom implementation that properly handles UTF-8!
|
// CRITICAL: Must use SHA256 to match React Native (not SHA1!)
|
||||||
// Java's PBEKeySpec uses UTF-16, but crypto-js uses UTF-8
|
val factory = javax.crypto.SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256")
|
||||||
val keyBytes = generatePBKDF2Key(password)
|
val spec = javax.crypto.spec.PBEKeySpec(
|
||||||
android.util.Log.d("MessageCrypto", "🔐 PBKDF2 key generated: ${keyBytes.size} bytes")
|
password.toCharArray(),
|
||||||
android.util.Log.d("MessageCrypto", "🔐 Key first 16 bytes (hex): ${keyBytes.take(16).joinToString("") { "%02x".format(it) }}")
|
"rosetta".toByteArray(Charsets.UTF_8),
|
||||||
|
1000,
|
||||||
|
256
|
||||||
|
)
|
||||||
|
val secretKey = factory.generateSecret(spec)
|
||||||
|
val keyBytes = secretKey.encoded
|
||||||
|
|
||||||
// Generate random IV (16 bytes)
|
// Generate random IV (16 bytes)
|
||||||
val iv = ByteArray(16)
|
val iv = ByteArray(16)
|
||||||
java.security.SecureRandom().nextBytes(iv)
|
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
|
// AES-CBC encryption
|
||||||
val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
|
val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
|
||||||
@@ -772,19 +767,14 @@ object MessageCrypto {
|
|||||||
val ivSpec = IvParameterSpec(iv)
|
val ivSpec = IvParameterSpec(iv)
|
||||||
cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec)
|
cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec)
|
||||||
val ciphertext = cipher.doFinal(compressed)
|
val ciphertext = cipher.doFinal(compressed)
|
||||||
android.util.Log.d("MessageCrypto", "🔐 AES encrypted: ${ciphertext.size} bytes")
|
|
||||||
|
|
||||||
// Format: "ivBase64:ciphertextBase64" (same as RN new format)
|
// Format: "ivBase64:ciphertextBase64" (same as RN new format)
|
||||||
val ivBase64 = Base64.encodeToString(iv, Base64.NO_WRAP)
|
val ivBase64 = Base64.encodeToString(iv, Base64.NO_WRAP)
|
||||||
val ctBase64 = Base64.encodeToString(ciphertext, Base64.NO_WRAP)
|
val ctBase64 = Base64.encodeToString(ciphertext, Base64.NO_WRAP)
|
||||||
val result = "$ivBase64:$ctBase64"
|
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
|
result
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
android.util.Log.e("MessageCrypto", "❌ encryptReplyBlob failed: ${e.message}", e)
|
|
||||||
// Fallback: return plaintext (for backwards compatibility)
|
// Fallback: return plaintext (for backwards compatibility)
|
||||||
replyJson
|
replyJson
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1079,11 +1079,14 @@ fun AvatarAttachment(
|
|||||||
val encryptedContent = TransportManager.downloadFile(attachment.id, downloadTag)
|
val encryptedContent = TransportManager.downloadFile(attachment.id, downloadTag)
|
||||||
downloadStatus = DownloadStatus.DECRYPTING
|
downloadStatus = DownloadStatus.DECRYPTING
|
||||||
|
|
||||||
// 🔥 КРИТИЧНО: Аватар зашифрован с AVATAR_PASSWORD (как на desktop)
|
// КРИТИЧНО: chachaKey ЗАШИФРОВАН в БД (как в Desktop)
|
||||||
// НЕ используем ChaCha ключ сообщения!
|
// Сначала расшифровываем его
|
||||||
val decrypted = CryptoManager.decryptWithPassword(
|
val decryptedKeyAndNonce = MessageCrypto.decryptKeyFromSender(chachaKey, privateKey)
|
||||||
|
val decryptKeyString = String(decryptedKeyAndNonce, Charsets.UTF_8)
|
||||||
|
|
||||||
|
val decrypted = MessageCrypto.decryptAttachmentBlobWithPassword(
|
||||||
encryptedContent,
|
encryptedContent,
|
||||||
AvatarFileManager.getAvatarPassword()
|
decryptKeyString
|
||||||
)
|
)
|
||||||
|
|
||||||
if (decrypted != null) {
|
if (decrypted != null) {
|
||||||
|
|||||||
Reference in New Issue
Block a user