Add key transparency backend support.

This commit is contained in:
Michelle Tang
2026-01-30 13:17:26 -05:00
committed by Greyson Parrelli
parent 26739491a5
commit 423b8c942c
18 changed files with 355 additions and 9 deletions

View File

@@ -199,6 +199,7 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
const val SORT_NAME = "sort_name"
const val IDENTITY_STATUS = "identity_status"
const val IDENTITY_KEY = "identity_key"
const val KEY_TRANSPARENCY_DATA = "key_transparency_data"
@JvmField
val CREATE_TABLE =
@@ -267,7 +268,8 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
$NICKNAME_FAMILY_NAME TEXT DEFAULT NULL,
$NICKNAME_JOINED_NAME TEXT DEFAULT NULL,
$NOTE TEXT DEFAULT NULL,
$MESSAGE_EXPIRATION_TIME_VERSION INTEGER DEFAULT 1 NOT NULL
$MESSAGE_EXPIRATION_TIME_VERSION INTEGER DEFAULT 1 NOT NULL,
$KEY_TRANSPARENCY_DATA BLOB DEFAULT NULL
)
"""
@@ -331,7 +333,8 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
PHONE_NUMBER_SHARING,
NICKNAME_GIVEN_NAME,
NICKNAME_FAMILY_NAME,
NOTE
NOTE,
KEY_TRANSPARENCY_DATA
)
private val ID_PROJECTION = arrayOf(ID)
@@ -4026,6 +4029,25 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
.readToSingleLong(0L)
}
fun getKeyTransparencyData(aci: ACI): ByteArray? {
return readableDatabase
.select(KEY_TRANSPARENCY_DATA)
.from(TABLE_NAME)
.where("$ACI_COLUMN = ?", aci.toString())
.run()
.readToSingleObject { cursor ->
cursor.requireBlob(KEY_TRANSPARENCY_DATA)
}
}
fun setKeyTransparencyData(aci: ACI, data: ByteArray?) {
writableDatabase
.update(TABLE_NAME)
.values(KEY_TRANSPARENCY_DATA to data)
.where("$ACI_COLUMN = ?", aci.toString())
.run()
}
/**
* Will update the database with the content values you specified. It will make an intelligent
* query such that this will only return true if a row was *actually* updated.

View File

@@ -165,7 +165,8 @@ object RecipientTableCursorUtil {
callLinkRoomId = cursor.requireString(RecipientTable.CALL_LINK_ROOM_ID)?.let { CallLinkRoomId.DatabaseSerializer.deserialize(it) },
phoneNumberSharing = cursor.requireInt(RecipientTable.PHONE_NUMBER_SHARING).let { RecipientTable.PhoneNumberSharingState.fromId(it) },
nickname = ProfileName.fromParts(cursor.requireString(RecipientTable.NICKNAME_GIVEN_NAME), cursor.requireString(RecipientTable.NICKNAME_FAMILY_NAME)),
note = cursor.requireString(RecipientTable.NOTE)
note = cursor.requireString(RecipientTable.NOTE),
keyTransparencyData = cursor.requireBlob(RecipientTable.KEY_TRANSPARENCY_DATA)
)
}

View File

@@ -153,6 +153,7 @@ import org.thoughtcrime.securesms.database.helpers.migration.V296_RemovePollVote
import org.thoughtcrime.securesms.database.helpers.migration.V297_AddPinnedMessageColumns
import org.thoughtcrime.securesms.database.helpers.migration.V298_DoNotBackupReleaseNotes
import org.thoughtcrime.securesms.database.helpers.migration.V299_AddAttachmentMetadataTable
import org.thoughtcrime.securesms.database.helpers.migration.V300_AddKeyTransparencyColumn
import org.thoughtcrime.securesms.database.SQLiteDatabase as SignalSqliteDatabase
/**
@@ -312,10 +313,11 @@ object SignalDatabaseMigrations {
296 to V296_RemovePollVoteConstraint,
297 to V297_AddPinnedMessageColumns,
298 to V298_DoNotBackupReleaseNotes,
299 to V299_AddAttachmentMetadataTable
299 to V299_AddAttachmentMetadataTable,
300 to V300_AddKeyTransparencyColumn
)
const val DATABASE_VERSION = 299
const val DATABASE_VERSION = 300
@JvmStatic
fun migrate(context: Application, db: SignalSqliteDatabase, oldVersion: Int, newVersion: Int) {

View File

@@ -0,0 +1,15 @@
package org.thoughtcrime.securesms.database.helpers.migration
import android.app.Application
import org.thoughtcrime.securesms.database.SQLiteDatabase
/**
* Adds column to recipient to track key transparency data
*/
@Suppress("ClassName")
object V300_AddKeyTransparencyColumn : SignalDatabaseMigration {
override fun migrate(context: Application, db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
db.execSQL("ALTER TABLE recipient ADD COLUMN key_transparency_data BLOB DEFAULT NULL")
}
}

View File

@@ -0,0 +1,34 @@
package org.thoughtcrime.securesms.database.model
import org.signal.core.util.logging.Log.tag
import org.signal.libsignal.keytrans.Store
import org.signal.libsignal.protocol.ServiceId
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.keyvalue.SignalStore
import java.util.Optional
/**
* Store used by [org.signal.libsignal.net.KeyTransparencyClient] during key transparency
*/
data object KeyTransparencyStore : Store {
private val TAG: String = tag(KeyTransparencyStore::class.java)
override fun getLastDistinguishedTreeHead(): Optional<ByteArray> {
return Optional.ofNullable(SignalStore.account.distinguishedHead)
}
override fun setLastDistinguishedTreeHead(lastDistinguishedTreeHead: ByteArray) {
SignalStore.account.distinguishedHead = lastDistinguishedTreeHead
}
override fun getAccountData(libsignalAci: ServiceId.Aci): Optional<ByteArray> {
val aci = org.signal.core.models.ServiceId.ACI.fromLibSignal(libsignalAci)
return Optional.ofNullable(SignalDatabase.recipients.getKeyTransparencyData(aci))
}
override fun setAccountData(libsignalAci: ServiceId.Aci, data: ByteArray) {
val aci = org.signal.core.models.ServiceId.ACI.fromLibSignal(libsignalAci)
SignalDatabase.recipients.setKeyTransparencyData(aci, data)
}
}

View File

@@ -81,7 +81,8 @@ data class RecipientRecord(
val callLinkRoomId: CallLinkRoomId?,
val phoneNumberSharing: PhoneNumberSharingState,
val nickname: ProfileName,
val note: String?
val note: String?,
val keyTransparencyData: ByteArray? = null
) {
fun e164Only(): Boolean {