Add support for call link epochs.

This commit is contained in:
emir-signal
2025-07-03 15:07:34 -04:00
committed by Alex Hart
parent 5d0f71e02c
commit b42dcece48
34 changed files with 211 additions and 71 deletions

View File

@@ -21,6 +21,7 @@ import org.signal.core.util.requireNonNullString
import org.signal.core.util.select
import org.signal.core.util.update
import org.signal.core.util.withinTransaction
import org.signal.ringrtc.CallLinkEpoch
import org.signal.ringrtc.CallLinkRootKey
import org.signal.ringrtc.CallLinkState.Restrictions
import org.thoughtcrime.securesms.calls.log.CallLogRow
@@ -47,6 +48,7 @@ class CallLinkTable(context: Context, databaseHelper: SignalDatabase) : Database
const val TABLE_NAME = "call_link"
const val ID = "_id"
const val ROOT_KEY = "root_key"
const val EPOCH = "epoch"
const val ROOM_ID = "room_id"
const val ADMIN_KEY = "admin_key"
const val NAME = "name"
@@ -61,6 +63,7 @@ class CallLinkTable(context: Context, databaseHelper: SignalDatabase) : Database
CREATE TABLE $TABLE_NAME (
$ID INTEGER PRIMARY KEY,
$ROOT_KEY BLOB,
$EPOCH BLOB,
$ROOM_ID TEXT NOT NULL UNIQUE,
$ADMIN_KEY BLOB,
$NAME TEXT NOT NULL,
@@ -128,6 +131,7 @@ class CallLinkTable(context: Context, databaseHelper: SignalDatabase) : Database
.values(
contentValuesOf(
ROOT_KEY to credentials.linkKeyBytes,
EPOCH to credentials.epochBytes,
ADMIN_KEY to credentials.adminPassBytes
)
)
@@ -188,7 +192,8 @@ class CallLinkTable(context: Context, databaseHelper: SignalDatabase) : Database
}
fun getOrCreateCallLinkByRootKey(
callLinkRootKey: CallLinkRootKey
callLinkRootKey: CallLinkRootKey,
callLinkEpoch: CallLinkEpoch?
): CallLink {
val roomId = CallLinkRoomId.fromBytes(callLinkRootKey.deriveRoomId())
val callLink = getCallLinkByRoomId(roomId)
@@ -198,6 +203,7 @@ class CallLinkTable(context: Context, databaseHelper: SignalDatabase) : Database
roomId = roomId,
credentials = CallLinkCredentials(
linkKeyBytes = callLinkRootKey.keyBytes,
epochBytes = callLinkEpoch?.bytes,
adminPassBytes = null
),
state = SignalCallLinkState(),
@@ -207,12 +213,26 @@ class CallLinkTable(context: Context, databaseHelper: SignalDatabase) : Database
insertCallLink(link)
return getCallLinkByRoomId(roomId)!!
} else {
callLink
if (callLink.credentials?.epoch != callLinkEpoch) {
overwriteEpoch(callLink, callLinkEpoch)
} else {
callLink
}
}
}
private fun overwriteEpoch(callLink: CallLink, callLinkEpoch: CallLinkEpoch?): CallLink {
val modifiedCallLink = callLink.copy(
deletionTimestamp = 0,
credentials = callLink.credentials!!.copy(epochBytes = callLinkEpoch?.bytes)
)
updateCallLinkCredentials(modifiedCallLink.roomId, modifiedCallLink.credentials!!)
return modifiedCallLink
}
fun insertOrUpdateCallLinkByRootKey(
callLinkRootKey: CallLinkRootKey,
callLinkEpoch: CallLinkEpoch?,
adminPassKey: ByteArray?,
deletionTimestamp: Long = 0L,
storageId: StorageId? = null
@@ -227,6 +247,7 @@ class CallLinkTable(context: Context, databaseHelper: SignalDatabase) : Database
roomId = roomId,
credentials = CallLinkCredentials(
linkKeyBytes = callLinkRootKey.keyBytes,
epochBytes = callLinkEpoch?.bytes,
adminPassBytes = adminPassKey
),
state = SignalCallLinkState(),
@@ -253,7 +274,8 @@ class CallLinkTable(context: Context, databaseHelper: SignalDatabase) : Database
writableDatabase.update(TABLE_NAME)
.values(
ADMIN_KEY to adminPassKey,
ROOT_KEY to callLinkRootKey.keyBytes
ROOT_KEY to callLinkRootKey.keyBytes,
EPOCH to callLinkEpoch?.bytes
)
.where("$ROOM_ID = ?", callLink.roomId.serialize())
.run()
@@ -464,6 +486,7 @@ class CallLinkTable(context: Context, databaseHelper: SignalDatabase) : Database
RECIPIENT_ID to data.recipientId.takeIf { it != RecipientId.UNKNOWN }?.toLong(),
ROOM_ID to data.roomId.serialize(),
ROOT_KEY to data.credentials?.linkKeyBytes,
EPOCH to data.credentials?.epochBytes,
ADMIN_KEY to data.credentials?.adminPassBytes
).apply {
putAll(data.state.serialize())
@@ -487,6 +510,7 @@ class CallLinkTable(context: Context, databaseHelper: SignalDatabase) : Database
credentials = data.requireBlob(ROOT_KEY)?.let { linkKey ->
CallLinkCredentials(
linkKeyBytes = linkKey,
epochBytes = data.requireBlob(EPOCH),
adminPassBytes = data.requireBlob(ADMIN_KEY)
)
},

View File

@@ -139,6 +139,7 @@ import org.thoughtcrime.securesms.database.helpers.migration.V281_RemoveArchiveT
import org.thoughtcrime.securesms.database.helpers.migration.V282_AddSnippetMessageIdColumnToThreadTable
import org.thoughtcrime.securesms.database.helpers.migration.V283_ViewOnceRemoteDataCleanup
import org.thoughtcrime.securesms.database.helpers.migration.V284_SetPlaceholderGroupFlag
import org.thoughtcrime.securesms.database.helpers.migration.V285_AddEpochToCallLinksTable
import org.thoughtcrime.securesms.database.SQLiteDatabase as SignalSqliteDatabase
/**
@@ -283,10 +284,11 @@ object SignalDatabaseMigrations {
281 to V281_RemoveArchiveTransferFile,
282 to V282_AddSnippetMessageIdColumnToThreadTable,
283 to V283_ViewOnceRemoteDataCleanup,
284 to V284_SetPlaceholderGroupFlag
284 to V284_SetPlaceholderGroupFlag,
285 to V285_AddEpochToCallLinksTable
)
const val DATABASE_VERSION = 284
const val DATABASE_VERSION = 285
@JvmStatic
fun migrate(context: Application, db: SignalSqliteDatabase, oldVersion: Int, newVersion: Int) {

View File

@@ -0,0 +1,16 @@
/*
* Copyright 2025 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.thoughtcrime.securesms.database.helpers.migration
import android.app.Application
import org.thoughtcrime.securesms.database.SQLiteDatabase
@Suppress("ClassName")
object V285_AddEpochToCallLinksTable : SignalDatabaseMigration {
override fun migrate(context: Application, db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
db.execSQL("ALTER TABLE call_link ADD COLUMN epoch BLOB DEFAULT NULL")
}
}