feat: enhance chat and requests screens with avatar handling, pinning, and user blocking functionalities
This commit is contained in:
@@ -811,16 +811,8 @@ object MessageCrypto {
|
||||
val compressed = compressedBuffer.copyOf(compressedSize)
|
||||
|
||||
// PBKDF2 key derivation (matching crypto-js: crypto.PBKDF2(password, 'rosetta', {keySize: 256/32, iterations: 1000}))
|
||||
// CRITICAL: crypto-js PBKDF2 uses SHA256 by default (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
|
||||
// Используем generatePBKDF2Key() для совместимости с crypto-js (UTF-8 encoding)
|
||||
val keyBytes = generatePBKDF2Key(password)
|
||||
|
||||
// Generate random IV (16 bytes)
|
||||
val iv = ByteArray(16)
|
||||
@@ -1030,28 +1022,17 @@ object MessageCrypto {
|
||||
|
||||
// Password from plainKeyAndNonce - use same JS-like UTF-8 conversion
|
||||
val password = bytesToJsUtf8String(plainKeyAndNonce)
|
||||
|
||||
// PBKDF2 key derivation
|
||||
// CRITICAL: Must use SHA1 to match Desktop crypto-js (not SHA256!)
|
||||
val factory = javax.crypto.SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1")
|
||||
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 — SHA256 (совместимо с crypto-js и encryptReplyBlob)
|
||||
val keyBytes = generatePBKDF2Key(password)
|
||||
|
||||
// AES-CBC decryption
|
||||
val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
|
||||
val keySpec = SecretKeySpec(keyBytes, "AES")
|
||||
val ivSpec = IvParameterSpec(iv)
|
||||
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec)
|
||||
val decompressed = cipher.doFinal(ciphertext)
|
||||
|
||||
|
||||
|
||||
// Decompress with inflate
|
||||
val inflater = java.util.zip.Inflater()
|
||||
inflater.setInput(decompressed)
|
||||
@@ -1059,12 +1040,41 @@ object MessageCrypto {
|
||||
val outputSize = inflater.inflate(outputBuffer)
|
||||
inflater.end()
|
||||
val plaintext = String(outputBuffer, 0, outputSize, Charsets.UTF_8)
|
||||
|
||||
|
||||
|
||||
plaintext
|
||||
} catch (e: Exception) {
|
||||
// Return as-is, might be plain JSON
|
||||
encryptedBlob
|
||||
// Fallback: пробуем SHA1 для обратной совместимости со старыми сообщениями
|
||||
try {
|
||||
val parts = encryptedBlob.split(':')
|
||||
if (parts.size != 2) return encryptedBlob
|
||||
|
||||
val iv = Base64.decode(parts[0], Base64.DEFAULT)
|
||||
val ciphertext = Base64.decode(parts[1], Base64.DEFAULT)
|
||||
val password = bytesToJsUtf8String(plainKeyAndNonce)
|
||||
|
||||
val factory = javax.crypto.SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1")
|
||||
val spec = javax.crypto.spec.PBEKeySpec(
|
||||
password.toCharArray(),
|
||||
"rosetta".toByteArray(Charsets.UTF_8),
|
||||
1000,
|
||||
256
|
||||
)
|
||||
val keyBytesSha1 = factory.generateSecret(spec).encoded
|
||||
|
||||
val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
|
||||
cipher.init(Cipher.DECRYPT_MODE, SecretKeySpec(keyBytesSha1, "AES"), IvParameterSpec(iv))
|
||||
val decompressed = cipher.doFinal(ciphertext)
|
||||
|
||||
val inflater = java.util.zip.Inflater()
|
||||
inflater.setInput(decompressed)
|
||||
val outputBuffer = ByteArray(decompressed.size * 10)
|
||||
val outputSize = inflater.inflate(outputBuffer)
|
||||
inflater.end()
|
||||
String(outputBuffer, 0, outputSize, Charsets.UTF_8)
|
||||
} catch (e2: Exception) {
|
||||
// Return as-is, might be plain JSON
|
||||
encryptedBlob
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user