session-android/app/src/main/java/org/thoughtcrime/securesms/util/AvatarPlaceholderGenerator.kt
ceokot bee287bb7e
Add Session Id blinding (#862)
* feat: Add Session Id blinding

Including modified version of lazysodium-android to expose missing libsodium functions, we could build from a fork which we still need to setup.

* Add v4 onion request handling

* Update SOGS signature construction

* Fix SOGS signature construction

* Update onion request

* Update signature data

* Keep path prefixes for v4 endpoints

* Update SOGS signature message

* Rename to remove api version suffix

* Update onion response parsing

* Refactor file download paths

* Implement request batching

* Refactor batch response handling

* Handle batch endpoint responses

* Update batch endpoint responses

* Update attachment download handling

* Handle file downloads

* Handle inbox messages

* Fix issue with file downloads

* Preserve image bytearray encoding

* Refactor

* Open group message requests

* Check id blinding in user detail bottom sheet rather

* Message validation refactor

* Cache last inbox/outbox server ids

* Update message encryption/decryption

* Refactor

* Refactor

* Bypass user details bottom sheet in open groups for blinded session ids

* Fix capabilities call auth

* Refactor

* Revert default server details

* Update sodium dependency to forked repo

* Fix attachment upload

* Revert "Update sodium dependency to forked repo"

This reverts commit c7db9529f9.

* Add signed sodium lib

* Update contact id truncation and mention logic

* Open group inbox messaging fix

* Refactor

* Update blinded id check

* Fix open group message sends

* Fix crash on open group direct message send

* Direct message refactor

* Direct message encrypt/decrypt fixes

* Use updated curve25519 version

* Updated lazysodium dependency

* Update encryption/decryption calls

* Handle direct message parse errors

* Minor refactor

* Existing chat refactor

* Update encryption & decryption parameters

* Fix authenticated ciphertext size

* Set direct message sync target

* Update direct message thread lookup

* Add blinded id mapping table

* Add blinded id mapping table

* Update threads after sends

* Update open group message timestamp handling

* Filter unblinded contacts

* Format blinded id mentions

* Add message deleted field

* Hide open group inbox id

* Update message request response handling

* Update message request response sender handling

* Fix mentions of blinded ids

* Handle open group poll failure

* fix: add log for failed open group onion request, add decoding body for blinding required error at destination

* fix: change the error check

* Persist group members

* Reschedule polling after capabilities update

* Retry on other exceptions

* Minor refactor

* Open group profile fix

* Group member db schema update

* Fix ban request key

* Update ban response type

* Ban endpoint updates

* Ban endpoint updates

* Delete messages

Co-authored-by: charles <charles@oxen.io>
Co-authored-by: jubb <hjubb@users.noreply.github.com>
2022-08-10 18:17:48 +10:00

102 lines
3.8 KiB
Kotlin

package org.thoughtcrime.securesms.util
import android.content.Context
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.Rect
import android.graphics.RectF
import android.graphics.Typeface
import android.graphics.drawable.BitmapDrawable
import android.text.TextPaint
import android.text.TextUtils
import network.loki.messenger.R
import org.session.libsignal.utilities.IdPrefix
import java.math.BigInteger
import java.security.MessageDigest
import java.util.Locale
object AvatarPlaceholderGenerator {
private const val EMPTY_LABEL = "0"
@JvmStatic
fun generate(context: Context, pixelSize: Int, hashString: String, displayName: String?): BitmapDrawable {
val hash: Long
if (hashString.length >= 12 && hashString.matches(Regex("^[0-9A-Fa-f]+\$"))) {
hash = getSha512(hashString).substring(0 until 12).toLong(16)
} else {
hash = 0
}
// Do not cache color array, it may be different depends on the current theme.
val colorArray = context.resources.getIntArray(R.array.profile_picture_placeholder_colors)
val colorPrimary = colorArray[(hash % colorArray.size).toInt()]
val labelText = when {
!TextUtils.isEmpty(displayName) -> extractLabel(displayName!!.capitalize(Locale.ROOT))
!TextUtils.isEmpty(hashString) -> extractLabel(hashString)
else -> EMPTY_LABEL
}
val bitmap = Bitmap.createBitmap(pixelSize, pixelSize, Bitmap.Config.ARGB_8888)
val canvas = Canvas(bitmap)
// Draw background/frame
val paint = Paint(Paint.ANTI_ALIAS_FLAG)
paint.color = colorPrimary
canvas.drawCircle(pixelSize.toFloat() / 2, pixelSize.toFloat() / 2, pixelSize.toFloat() / 2, paint)
// Draw text
val textPaint = TextPaint(Paint.ANTI_ALIAS_FLAG)
textPaint.typeface = Typeface.create(Typeface.DEFAULT, Typeface.NORMAL)
textPaint.textSize = pixelSize * 0.5f
textPaint.color = Color.WHITE
val areaRect = Rect(0, 0, pixelSize, pixelSize)
val textBounds = RectF(areaRect)
textBounds.right = textPaint.measureText(labelText)
textBounds.bottom = textPaint.descent() - textPaint.ascent()
textBounds.left += (areaRect.width() - textBounds.right) * 0.5f
textBounds.top += (areaRect.height() - textBounds.bottom) * 0.5f
canvas.drawText(labelText, textBounds.left, textBounds.top - textPaint.ascent(), textPaint)
return BitmapDrawable(context.resources, bitmap)
}
fun extractLabel(content: String): String {
val trimmedContent = content.trim()
if (trimmedContent.isEmpty()) return EMPTY_LABEL
return if (trimmedContent.length > 2 && IdPrefix.fromValue(trimmedContent) != null) {
trimmedContent[2].toString()
} else {
val splitWords = trimmedContent.split(Regex("\\W"))
if (splitWords.size < 2) {
trimmedContent.take(2)
} else {
splitWords.filter { word -> word.isNotEmpty() }.take(2).map { it.first() }.joinToString("")
}
}.uppercase()
}
private fun getSha512(input: String): String {
val messageDigest = MessageDigest.getInstance("SHA-512").digest(input.toByteArray())
// Convert byte array into signum representation
val no = BigInteger(1, messageDigest)
// Convert message digest into hex value
var hashText: String = no.toString(16)
// Add preceding 0s to make it 32 bytes
if (hashText.length < 128) {
val sb = StringBuilder()
for (i in 0 until 128 - hashText.length) {
sb.append('0')
}
hashText = sb.append(hashText).toString()
}
return hashText
}
}