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 2a95349..814efe7 100644 --- a/app/src/main/java/com/rosetta/messenger/crypto/MessageCrypto.kt +++ b/app/src/main/java/com/rosetta/messenger/crypto/MessageCrypto.kt @@ -736,8 +736,9 @@ object MessageCrypto { // which replaces invalid UTF-8 sequences with U+FFFD val password = bytesToJsUtf8String(plainKeyAndNonce) - // Compress with pako (deflate) - val deflater = java.util.zip.Deflater() + // Compress with raw deflate (NO zlib header) - matching pako.deflate() + // CRITICAL: nowrap=true for raw deflate (pako default) + val deflater = java.util.zip.Deflater(java.util.zip.Deflater.DEFAULT_COMPRESSION, true) deflater.setInput(replyJson.toByteArray(Charsets.UTF_8)) deflater.finish() val compressedBuffer = ByteArray(replyJson.length * 2 + 100) @@ -745,17 +746,10 @@ object MessageCrypto { deflater.end() val compressed = compressedBuffer.copyOf(compressedSize) - // 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 + // 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) // Generate random IV (16 bytes) val iv = ByteArray(16)