feat: enhance chat and requests screens with avatar handling, pinning, and user blocking functionalities

This commit is contained in:
2026-02-11 05:50:08 +05:00
parent a0ef378909
commit 8c8a651500
13 changed files with 1244 additions and 451 deletions

View File

@@ -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
}
}
}
}