From d46920675b6f8bc1970b4160a4e897ca1c5886e6 Mon Sep 17 00:00:00 2001 From: k1ngsterr1 Date: Sun, 11 Jan 2026 03:15:45 +0500 Subject: [PATCH] feat: Enhance compression and decompression methods for compatibility with pako library by using RAW deflate/inflate --- .../rosetta/messenger/crypto/CryptoManager.kt | 24 +- .../rosetta/messenger/crypto/MessageCrypto.kt | 285 +++++++++++++++--- 2 files changed, 257 insertions(+), 52 deletions(-) diff --git a/app/src/main/java/com/rosetta/messenger/crypto/CryptoManager.kt b/app/src/main/java/com/rosetta/messenger/crypto/CryptoManager.kt index 6be22f2..6e3dbc9 100644 --- a/app/src/main/java/com/rosetta/messenger/crypto/CryptoManager.kt +++ b/app/src/main/java/com/rosetta/messenger/crypto/CryptoManager.kt @@ -216,8 +216,17 @@ object CryptoManager { } } + /** + * RAW Deflate сжатие (без zlib header) + * + * ⚠️ ВАЖНО: nowrap=true для совместимости с pako.deflate() в JS! + * - pako.deflate() создаёт RAW deflate поток (без 2-byte zlib header) + * - Java Deflater() по умолчанию создаёт zlib поток (с header 78 9C) + * - Поэтому используем Deflater(level, true) где true = nowrap + */ private fun compress(data: ByteArray): ByteArray { - val deflater = Deflater() + // nowrap=true = RAW deflate (совместимо с pako.deflate) + val deflater = Deflater(Deflater.DEFAULT_COMPRESSION, true) deflater.setInput(data) deflater.finish() @@ -227,12 +236,22 @@ object CryptoManager { val count = deflater.deflate(buffer) outputStream.write(buffer, 0, count) } + deflater.end() // Освобождаем ресурсы outputStream.close() return outputStream.toByteArray() } + /** + * RAW Inflate декомпрессия (без zlib header) + * + * ⚠️ ВАЖНО: nowrap=true для совместимости с pako.inflate() в JS! + * - pako.inflate() ожидает RAW deflate поток + * - Java Inflater() по умолчанию ожидает zlib поток (с header) + * - Поэтому используем Inflater(true) где true = nowrap + */ private fun decompress(data: ByteArray): ByteArray { - val inflater = Inflater() + // nowrap=true = RAW inflate (совместимо с pako.inflate) + val inflater = Inflater(true) inflater.setInput(data) val outputStream = ByteArrayOutputStream() @@ -241,6 +260,7 @@ object CryptoManager { val count = inflater.inflate(buffer) outputStream.write(buffer, 0, count) } + inflater.end() // Освобождаем ресурсы outputStream.close() return outputStream.toByteArray() } 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 77683bf..ea0e3cb 100644 --- a/app/src/main/java/com/rosetta/messenger/crypto/MessageCrypto.kt +++ b/app/src/main/java/com/rosetta/messenger/crypto/MessageCrypto.kt @@ -275,112 +275,241 @@ object MessageCrypto { * ECDH шифрование ключа для получателя * Использует secp256k1 + AES как в RN версии * Формат: Base64(iv:ciphertext:ephemeralPrivateKeyHex) + * + * JS эквивалент: + * const key = Buffer.concat([keyBytes, nonceBytes]); + * const encryptedKey = await encrypt(key.toString('binary'), publicKey); + * + * КРИТИЧНО: ephemeralKey.getPrivate('hex') в JS может быть БЕЗ ведущих нулей! */ fun encryptKeyForRecipient(keyAndNonce: ByteArray, recipientPublicKeyHex: String): String { + android.util.Log.d("MessageCrypto", "\n" + "━".repeat(80)) + android.util.Log.d("MessageCrypto", "🔐 ECDH ENCRYPT KEY FOR RECIPIENT") + android.util.Log.d("MessageCrypto", "━".repeat(80)) + val secureRandom = SecureRandom() val ecSpec = ECNamedCurveTable.getParameterSpec("secp256k1") // Генерируем эфемерный приватный ключ (32 байта) + android.util.Log.d("MessageCrypto", "📍 Step 1: Generate ephemeral key pair") val ephemeralPrivateKeyBytes = ByteArray(32) secureRandom.nextBytes(ephemeralPrivateKeyBytes) val ephemeralPrivateKey = BigInteger(1, ephemeralPrivateKeyBytes) - val ephemeralPrivateKeyHex = ephemeralPrivateKeyBytes.toHex() + + // ⚠️ КРИТИЧНО: JS elliptic.js может вернуть hex БЕЗ ведущих нулей! + // ephemeralKey.getPrivate('hex') - это BigInteger.toString(16) + // Не добавляет ведущие нули если первый байт < 0x10 + val ephemeralPrivateKeyHex = ephemeralPrivateKey.toString(16).let { + // Но если нечётная длина, добавим ведущий 0 для правильного парсинга + if (it.length % 2 != 0) { + android.util.Log.d("MessageCrypto", "⚠️ Padded ephemeral private key (was odd length)") + "0$it" + } else it + } + + android.util.Log.d("MessageCrypto", " ✓ Ephemeral private key: $ephemeralPrivateKeyHex") + android.util.Log.d("MessageCrypto", " ✓ Ephemeral private length: ${ephemeralPrivateKeyHex.length} hex chars") // Получаем эфемерный публичный ключ val ephemeralPublicKey = ecSpec.g.multiply(ephemeralPrivateKey) + val ephemeralPublicKeyHex = ephemeralPublicKey.getEncoded(false).toHex() + android.util.Log.d("MessageCrypto", " ✓ Ephemeral public key: $ephemeralPublicKeyHex") // Парсим публичный ключ получателя + android.util.Log.d("MessageCrypto", "\n📍 Step 2: Parse recipient public key") + android.util.Log.d("MessageCrypto", " ✓ Recipient public key: $recipientPublicKeyHex") val recipientPublicKeyBytes = recipientPublicKeyHex.hexToBytes() + android.util.Log.d("MessageCrypto", " ✓ Recipient key bytes: ${recipientPublicKeyBytes.size}") val recipientPublicKey = ecSpec.curve.decodePoint(recipientPublicKeyBytes) // ECDH: ephemeralPrivate * recipientPublic = sharedSecret + android.util.Log.d("MessageCrypto", "\n📍 Step 3: Compute ECDH shared secret") + android.util.Log.d("MessageCrypto", " • Computing: ephemeral_private × recipient_public") val sharedPoint = recipientPublicKey.multiply(ephemeralPrivateKey) - val sharedSecret = sharedPoint.normalize().xCoord.encoded - val sharedSecretHex = sharedSecret.toHex() + + // ⚠️ КРИТИЧНО: Эмулируем JS поведение! + // JS: BN.toString(16) НЕ добавляет ведущие нули + // crypto.enc.Hex.parse(hex) парсит как есть + // Если X coordinate = 0x00abc..., JS получит "abc..." (меньше 32 байт) + val xCoordBigInt = sharedPoint.normalize().xCoord.toBigInteger() + android.util.Log.d("MessageCrypto", " • X coordinate (BigInt): $xCoordBigInt") + var sharedSecretHex = xCoordBigInt.toString(16) + android.util.Log.d("MessageCrypto", " • toString(16) result: $sharedSecretHex (${sharedSecretHex.length} chars)") + + // JS: если hex нечётной длины, crypto.enc.Hex.parse добавит ведущий 0 + if (sharedSecretHex.length % 2 != 0) { + android.util.Log.d("MessageCrypto", " ⚠️ Padding to even length for hex parsing") + sharedSecretHex = "0$sharedSecretHex" + } + val sharedSecret = sharedSecretHex.hexToBytes() + android.util.Log.d("MessageCrypto", " ✓ SHARED SECRET hex: $sharedSecretHex") + android.util.Log.d("MessageCrypto", " ✓ Shared secret bytes: ${sharedSecret.size} bytes") // Генерируем IV для AES (16 байт) + android.util.Log.d("MessageCrypto", "\n📍 Step 4: Generate AES IV") val iv = ByteArray(16) secureRandom.nextBytes(iv) val ivHex = iv.toHex() + android.util.Log.d("MessageCrypto", " ✓ IV: $ivHex") - // КРИТИЧНО: Эмулируем поведение crypto-js! - // React Native делает: key.toString('binary') → crypto.AES.encrypt(string, ...) - // crypto-js интерпретирует string как UTF-8! - // Поэтому: ByteArray → Latin1 String → UTF-8 bytes → encrypt - val latin1String = String(keyAndNonce, Charsets.ISO_8859_1) - val utf8Bytes = latin1String.toByteArray(Charsets.UTF_8) + // ⚠️ КРИТИЧНО: Эмулируем поведение JS! + android.util.Log.d("MessageCrypto", "\n📍 Step 5: Latin1 → UTF-8 conversion (JS compatibility)") + android.util.Log.d("MessageCrypto", " • Input key+nonce bytes: ${keyAndNonce.size}") + android.util.Log.d("MessageCrypto", " • Input hex: ${keyAndNonce.toHex()}") - android.util.Log.d("MessageCrypto", "📝 Key encoding:") - android.util.Log.d("MessageCrypto", " - Original bytes: ${keyAndNonce.size}") - android.util.Log.d("MessageCrypto", " - Latin1 string chars: ${latin1String.length}") - android.util.Log.d("MessageCrypto", " - UTF-8 bytes: ${utf8Bytes.size}") + val latin1String = String(keyAndNonce, Charsets.ISO_8859_1) // bytes → Latin1 string + android.util.Log.d("MessageCrypto", " • Latin1 string length: ${latin1String.length} chars") + android.util.Log.d("MessageCrypto", " • Latin1 char codes (first 20): ${latin1String.take(20).map { it.code }}") + + val utf8Bytes = latin1String.toByteArray(Charsets.UTF_8) // Latin1 → UTF-8 bytes + android.util.Log.d("MessageCrypto", " ✓ UTF-8 bytes length: ${utf8Bytes.size}") + android.util.Log.d("MessageCrypto", " ✓ UTF-8 hex: ${utf8Bytes.toHex()}") + + // AES шифрование + android.util.Log.d("MessageCrypto", "\n📍 Step 6: AES-CBC encryption") + android.util.Log.d("MessageCrypto", " • AES key (shared secret): ${sharedSecret.size} bytes") + android.util.Log.d("MessageCrypto", " • IV: ${iv.size} bytes") + android.util.Log.d("MessageCrypto", " • Plaintext: ${utf8Bytes.size} bytes") val aesKey = SecretKeySpec(sharedSecret, "AES") val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding") cipher.init(Cipher.ENCRYPT_MODE, aesKey, IvParameterSpec(iv)) - val encryptedKey = cipher.doFinal(utf8Bytes) + val encryptedKey = cipher.doFinal(utf8Bytes) // Шифруем UTF-8 bytes val encryptedKeyHex = encryptedKey.toHex() + android.util.Log.d("MessageCrypto", " ✓ CIPHERTEXT: $encryptedKeyHex") + android.util.Log.d("MessageCrypto", " ✓ Ciphertext length: ${encryptedKey.size} bytes") + // Формат как в RN: btoa(ivHex:encryptedHex:ephemeralPrivateHex) + android.util.Log.d("MessageCrypto", "\n📍 Step 7: Format as iv:ciphertext:ephemeralPrivate") val combined = "$ivHex:$encryptedKeyHex:$ephemeralPrivateKeyHex" + android.util.Log.d("MessageCrypto", " • Combined string: ${combined.take(160)}...") + android.util.Log.d("MessageCrypto", " • Combined length: ${combined.length} chars") - android.util.Log.d("MessageCrypto", "🔐 ECDH Encrypt:") - android.util.Log.d("MessageCrypto", " - Shared secret: ${sharedSecretHex.take(40)}...") - android.util.Log.d("MessageCrypto", " - IV: ${ivHex.take(32)}...") - android.util.Log.d("MessageCrypto", " - Ephemeral private: ${ephemeralPrivateKeyHex.take(40)}...") - android.util.Log.d("MessageCrypto", " - Recipient public: ${recipientPublicKeyHex.take(40)}...") - - return Base64.encodeToString(combined.toByteArray(), Base64.NO_WRAP) + val result = Base64.encodeToString(combined.toByteArray(), Base64.NO_WRAP) + android.util.Log.d("MessageCrypto", "\n ✅ FINAL ENCRYPTED KEY (Base64): $result") + android.util.Log.d("MessageCrypto", " ✅ Base64 length: ${result.length} chars") + android.util.Log.d("MessageCrypto", "━".repeat(80) + "\n") + return result } /** * ECDH расшифровка ключа * Формат: Base64(ivHex:encryptedHex:ephemeralPrivateHex) + * + * КРИТИЧНО: ephemeralPrivateKeyHex может иметь нечётную длину! */ fun decryptKeyFromSender(encryptedKeyBase64: String, myPrivateKeyHex: String): ByteArray { + android.util.Log.d("MessageCrypto", "\n" + "━".repeat(80)) + android.util.Log.d("MessageCrypto", "🔓 ECDH DECRYPT KEY FROM SENDER") + android.util.Log.d("MessageCrypto", "━".repeat(80)) + android.util.Log.d("MessageCrypto", "📥 Encrypted key (Base64): $encryptedKeyBase64") + android.util.Log.d("MessageCrypto", "📥 Base64 length: ${encryptedKeyBase64.length} chars") + android.util.Log.d("MessageCrypto", "🔑 My private key: $myPrivateKeyHex") + + android.util.Log.d("MessageCrypto", "\n📍 Step 1: Decode Base64 and parse format") val combined = String(Base64.decode(encryptedKeyBase64, Base64.NO_WRAP)) + android.util.Log.d("MessageCrypto", " • Decoded string: ${combined.take(160)}...") + android.util.Log.d("MessageCrypto", " • Decoded length: ${combined.length} chars") + val parts = combined.split(":") - if (parts.size != 3) throw IllegalArgumentException("Invalid encrypted key format: expected 3 parts, got ${parts.size}") + if (parts.size != 3) { + android.util.Log.e("MessageCrypto", "❌ Invalid format: expected 3 parts, got ${parts.size}") + throw IllegalArgumentException("Invalid encrypted key format: expected 3 parts, got ${parts.size}") + } + android.util.Log.d("MessageCrypto", " ✓ Parsed into 3 parts") val ivHex = parts[0] val encryptedKeyHex = parts[1] - val ephemeralPrivateKeyHex = parts[2] + var ephemeralPrivateKeyHex = parts[2] + + android.util.Log.d("MessageCrypto", " • Part 1 (IV): $ivHex (${ivHex.length} chars)") + android.util.Log.d("MessageCrypto", " • Part 2 (ciphertext): ${encryptedKeyHex.take(80)}... (${encryptedKeyHex.length} chars)") + android.util.Log.d("MessageCrypto", " • Part 3 (ephemeral private): $ephemeralPrivateKeyHex") + + // ⚠️ КРИТИЧНО: JS toString(16) может вернуть hex нечётной длины! + // Добавляем ведущий 0 если нужно для правильного парсинга + if (ephemeralPrivateKeyHex.length % 2 != 0) { + android.util.Log.d("MessageCrypto", " ⚠️ Padding ephemeral key from ${ephemeralPrivateKeyHex.length} to ${ephemeralPrivateKeyHex.length + 1}") + ephemeralPrivateKeyHex = "0$ephemeralPrivateKeyHex" + } val iv = ivHex.hexToBytes() val encryptedKey = encryptedKeyHex.hexToBytes() + android.util.Log.d("MessageCrypto", " ✓ IV bytes: ${iv.size}") + android.util.Log.d("MessageCrypto", " ✓ Ciphertext bytes: ${encryptedKey.size}") val ecSpec = ECNamedCurveTable.getParameterSpec("secp256k1") // Парсим эфемерный приватный ключ + android.util.Log.d("MessageCrypto", "\n📍 Step 2: Parse ephemeral key pair") val ephemeralPrivateKey = BigInteger(ephemeralPrivateKeyHex, 16) val ephemeralPublicKey = ecSpec.g.multiply(ephemeralPrivateKey) + val ephemeralPublicKeyHex = ephemeralPublicKey.getEncoded(false).toHex() + android.util.Log.d("MessageCrypto", " ✓ Ephemeral private: $ephemeralPrivateKeyHex") + android.util.Log.d("MessageCrypto", " ✓ Ephemeral public: $ephemeralPublicKeyHex") // Парсим мой приватный ключ + android.util.Log.d("MessageCrypto", "\n📍 Step 3: Parse my key pair") val myPrivateKey = BigInteger(myPrivateKeyHex, 16) val myPublicKey = ecSpec.g.multiply(myPrivateKey) + val myPublicKeyHex = myPublicKey.getEncoded(false).toHex() + android.util.Log.d("MessageCrypto", " ✓ My private: ${myPrivateKeyHex.take(32)}...") + android.util.Log.d("MessageCrypto", " ✓ My public: $myPublicKeyHex") // ECDH: ephemeralPrivate * myPublic = sharedSecret + android.util.Log.d("MessageCrypto", "\n📍 Step 4: Compute ECDH shared secret") + android.util.Log.d("MessageCrypto", " • Computing: ephemeral_private × my_public") val sharedPoint = myPublicKey.multiply(ephemeralPrivateKey) - val sharedSecret = sharedPoint.normalize().xCoord.encoded + + // ⚠️ КРИТИЧНО: Эмулируем JS поведение! + // JS: BN.toString(16) НЕ добавляет ведущие нули + val xCoordBigInt = sharedPoint.normalize().xCoord.toBigInteger() + android.util.Log.d("MessageCrypto", " • X coordinate (BigInt): $xCoordBigInt") + var sharedSecretHex = xCoordBigInt.toString(16) + android.util.Log.d("MessageCrypto", " • toString(16) result: $sharedSecretHex (${sharedSecretHex.length} chars)") + + if (sharedSecretHex.length % 2 != 0) { + android.util.Log.d("MessageCrypto", " ⚠️ Padding to even length") + sharedSecretHex = "0$sharedSecretHex" + } + val sharedSecret = sharedSecretHex.hexToBytes() + android.util.Log.d("MessageCrypto", " ✓ SHARED SECRET hex: $sharedSecretHex") + android.util.Log.d("MessageCrypto", " ✓ Shared secret bytes: ${sharedSecret.size} bytes") // Расшифровываем используя sharedSecret как AES ключ + android.util.Log.d("MessageCrypto", "\n📍 Step 5: AES-CBC decryption") + android.util.Log.d("MessageCrypto", " • AES key (shared secret): ${sharedSecret.size} bytes") + android.util.Log.d("MessageCrypto", " • IV: ${iv.size} bytes") + android.util.Log.d("MessageCrypto", " • Ciphertext: ${encryptedKey.size} bytes") + val aesKey = SecretKeySpec(sharedSecret, "AES") val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding") cipher.init(Cipher.DECRYPT_MODE, aesKey, IvParameterSpec(iv)) val decryptedUtf8Bytes = cipher.doFinal(encryptedKey) + android.util.Log.d("MessageCrypto", " ✓ Decrypted UTF-8 bytes: ${decryptedUtf8Bytes.size}") + android.util.Log.d("MessageCrypto", " ✓ Decrypted hex: ${decryptedUtf8Bytes.toHex()}") - // КРИТИЧНО: Обратная конвертация как в crypto-js! - // crypto-js делает: decrypt → WordArray → .toString(Utf8) → string - // Потом: Buffer.from(string, 'binary') → bytes - // Эквивалент: UTF-8 bytes → UTF-8 string → Latin1 bytes - val utf8String = String(decryptedUtf8Bytes, Charsets.UTF_8) - val originalBytes = utf8String.toByteArray(Charsets.ISO_8859_1) + // ⚠️ КРИТИЧНО: Обратная конвертация как в JS! + android.util.Log.d("MessageCrypto", "\n📍 Step 6: UTF-8 → Latin1 conversion (JS compatibility)") + // JS делает: + // 1. AES decrypt → UTF-8 bytes + // 2. decrypted.toString(crypto.enc.Utf8) → UTF-8 decode → string + // 3. Buffer.from(string, 'binary') → берёт charCode каждого символа как байт + // + // Kotlin эквивалент: + val utf8String = String(decryptedUtf8Bytes, Charsets.UTF_8) // UTF-8 decode + android.util.Log.d("MessageCrypto", " • UTF-8 string length: ${utf8String.length} chars") + android.util.Log.d("MessageCrypto", " • UTF-8 char codes (first 20): ${utf8String.take(20).map { it.code }}") - android.util.Log.d("MessageCrypto", "📝 Key decoding:") - android.util.Log.d("MessageCrypto", " - Decrypted UTF-8 bytes: ${decryptedUtf8Bytes.size}") - android.util.Log.d("MessageCrypto", " - UTF-8 string chars: ${utf8String.length}") - android.util.Log.d("MessageCrypto", " - Original bytes: ${originalBytes.size}") + val originalBytes = utf8String.toByteArray(Charsets.ISO_8859_1) // charCode → bytes + android.util.Log.d("MessageCrypto", " ✓ RESULT bytes: ${originalBytes.size}") + android.util.Log.d("MessageCrypto", " ✓ Result hex: ${originalBytes.toHex()}") + + android.util.Log.d("MessageCrypto", "\n ✅ DECRYPTION COMPLETE") + android.util.Log.d("MessageCrypto", " ✅ Returned ${originalBytes.size} bytes") + android.util.Log.d("MessageCrypto", "━".repeat(80) + "\n") return originalBytes } @@ -389,31 +518,52 @@ object MessageCrypto { * Полное шифрование сообщения для отправки */ fun encryptForSending(plaintext: String, recipientPublicKey: String): Pair { - 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)}...") + android.util.Log.d("MessageCrypto", "=".repeat(100)) + android.util.Log.d("MessageCrypto", "🚀🚀🚀 START ENCRYPTION FOR SENDING 🚀🚀🚀") + android.util.Log.d("MessageCrypto", "=".repeat(100)) + android.util.Log.d("MessageCrypto", "📝 PLAINTEXT: '$plaintext'") + android.util.Log.d("MessageCrypto", "📝 Plaintext length: ${plaintext.length} chars") + android.util.Log.d("MessageCrypto", "📝 Plaintext bytes: ${plaintext.toByteArray().size} bytes") + android.util.Log.d("MessageCrypto", "📝 Plaintext hex (first 128): ${plaintext.toByteArray().take(64).toByteArray().toHex()}") + android.util.Log.d("MessageCrypto", "🔑 RECIPIENT PUBLIC KEY: $recipientPublicKey") + android.util.Log.d("MessageCrypto", "🔑 Public key length: ${recipientPublicKey.length} chars") // 1. Шифруем текст - android.util.Log.d("MessageCrypto", "⚡ Step 1: Encrypting message with XChaCha20-Poly1305...") + android.util.Log.d("MessageCrypto", "\n" + "─".repeat(80)) + android.util.Log.d("MessageCrypto", "⚡ STEP 1: XCHACHA20-POLY1305 ENCRYPTION") + android.util.Log.d("MessageCrypto", "─".repeat(80)) 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)}...") + android.util.Log.d("MessageCrypto", "✅ CIPHERTEXT (hex): ${encrypted.ciphertext}") + android.util.Log.d("MessageCrypto", "✅ Ciphertext length: ${encrypted.ciphertext.length} chars") + android.util.Log.d("MessageCrypto", "✅ KEY (hex): ${encrypted.key}") + android.util.Log.d("MessageCrypto", "✅ Key length: ${encrypted.key.length} chars (should be 64)") + android.util.Log.d("MessageCrypto", "✅ NONCE (hex): ${encrypted.nonce}") + android.util.Log.d("MessageCrypto", "✅ Nonce length: ${encrypted.nonce.length} chars (should be 48)") // 2. Собираем key + nonce - android.util.Log.d("MessageCrypto", "⚡ Step 2: Combining key + nonce...") + android.util.Log.d("MessageCrypto", "\n" + "─".repeat(80)) + android.util.Log.d("MessageCrypto", "⚡ STEP 2: COMBINE KEY + NONCE") + android.util.Log.d("MessageCrypto", "─".repeat(80)) val keyAndNonce = encrypted.key.hexToBytes() + encrypted.nonce.hexToBytes() - android.util.Log.d("MessageCrypto", "✅ Combined keyAndNonce length: ${keyAndNonce.size} bytes") + android.util.Log.d("MessageCrypto", "✅ KEY+NONCE combined: ${keyAndNonce.size} bytes (should be 56)") + android.util.Log.d("MessageCrypto", "✅ Combined hex: ${keyAndNonce.toHex()}") // 3. Шифруем ключ для получателя - android.util.Log.d("MessageCrypto", "⚡ Step 3: Encrypting key with ECDH + AES...") + android.util.Log.d("MessageCrypto", "\n" + "─".repeat(80)) + android.util.Log.d("MessageCrypto", "⚡ STEP 3: ECDH + AES ENCRYPTION OF KEY") + android.util.Log.d("MessageCrypto", "─".repeat(80)) 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", "✅ ENCRYPTED KEY (Base64): $encryptedKey") + android.util.Log.d("MessageCrypto", "✅ Encrypted key length: ${encryptedKey.length} chars") + + android.util.Log.d("MessageCrypto", "\n" + "=".repeat(100)) + android.util.Log.d("MessageCrypto", "✅✅✅ ENCRYPTION COMPLETE ✅✅✅") + android.util.Log.d("MessageCrypto", "=".repeat(100)) + android.util.Log.d("MessageCrypto", "FINAL OUTPUTS:") + android.util.Log.d("MessageCrypto", " • Ciphertext: ${encrypted.ciphertext.take(60)}... (${encrypted.ciphertext.length} chars)") + android.util.Log.d("MessageCrypto", " • Encrypted key: ${encryptedKey.take(60)}... (${encryptedKey.length} chars)") + android.util.Log.d("MessageCrypto", "=".repeat(100) + "\n") - android.util.Log.d("MessageCrypto", "🔐 === ENCRYPTION COMPLETE ===") return kotlin.Pair(encrypted.ciphertext, encryptedKey) } @@ -425,15 +575,50 @@ object MessageCrypto { encryptedKey: String, myPrivateKey: String ): String { + android.util.Log.d("MessageCrypto", "=".repeat(100)) + android.util.Log.d("MessageCrypto", "🔓🔓🔓 START DECRYPTION OF INCOMING MESSAGE 🔓🔓🔓") + android.util.Log.d("MessageCrypto", "=".repeat(100)) + android.util.Log.d("MessageCrypto", "📩 CIPHERTEXT (hex): ${ciphertext.take(100)}...") + android.util.Log.d("MessageCrypto", "📩 Ciphertext length: ${ciphertext.length} chars") + android.util.Log.d("MessageCrypto", "🔐 ENCRYPTED KEY: ${encryptedKey.take(100)}...") + android.util.Log.d("MessageCrypto", "🔐 Encrypted key length: ${encryptedKey.length} chars") + android.util.Log.d("MessageCrypto", "🔑 MY PRIVATE KEY: ${myPrivateKey.take(32)}... (${myPrivateKey.length} chars)") + // 1. Расшифровываем ключ + android.util.Log.d("MessageCrypto", "\n" + "─".repeat(80)) + android.util.Log.d("MessageCrypto", "⚡ STEP 1: ECDH + AES DECRYPTION OF KEY") + android.util.Log.d("MessageCrypto", "─".repeat(80)) val keyAndNonce = decryptKeyFromSender(encryptedKey, myPrivateKey) + android.util.Log.d("MessageCrypto", "✅ DECRYPTED KEY+NONCE: ${keyAndNonce.size} bytes (should be 56)") + android.util.Log.d("MessageCrypto", "✅ Key+Nonce hex: ${keyAndNonce.toHex()}") // 2. Разделяем key и nonce + android.util.Log.d("MessageCrypto", "\n" + "─".repeat(80)) + android.util.Log.d("MessageCrypto", "⚡ STEP 2: SPLIT KEY AND NONCE") + android.util.Log.d("MessageCrypto", "─".repeat(80)) val key = keyAndNonce.slice(0 until 32).toByteArray() val nonce = keyAndNonce.slice(32 until keyAndNonce.size).toByteArray() + android.util.Log.d("MessageCrypto", "✅ KEY (32 bytes): ${key.toHex()}") + android.util.Log.d("MessageCrypto", "✅ NONCE (24 bytes): ${nonce.toHex()}") + android.util.Log.d("MessageCrypto", "✅ Key length: ${key.size} bytes") + android.util.Log.d("MessageCrypto", "✅ Nonce length: ${nonce.size} bytes") // 3. Расшифровываем сообщение - return decryptMessage(ciphertext, key.toHex(), nonce.toHex()) + android.util.Log.d("MessageCrypto", "\n" + "─".repeat(80)) + android.util.Log.d("MessageCrypto", "⚡ STEP 3: XCHACHA20-POLY1305 DECRYPTION") + android.util.Log.d("MessageCrypto", "─".repeat(80)) + val plaintext = decryptMessage(ciphertext, key.toHex(), nonce.toHex()) + android.util.Log.d("MessageCrypto", "✅ PLAINTEXT: '$plaintext'") + android.util.Log.d("MessageCrypto", "✅ Plaintext length: ${plaintext.length} chars") + android.util.Log.d("MessageCrypto", "✅ Plaintext bytes: ${plaintext.toByteArray().size} bytes") + + android.util.Log.d("MessageCrypto", "\n" + "=".repeat(100)) + android.util.Log.d("MessageCrypto", "✅✅✅ DECRYPTION COMPLETE ✅✅✅") + android.util.Log.d("MessageCrypto", "=".repeat(100)) + android.util.Log.d("MessageCrypto", "FINAL OUTPUT: '$plaintext'") + android.util.Log.d("MessageCrypto", "=".repeat(100) + "\n") + + return plaintext } }