Add initial storage interfaces for kyber prekeys.

This commit is contained in:
Greyson Parrelli
2023-05-17 11:58:45 -04:00
parent c76002663f
commit e2c2ace0e3
12 changed files with 352 additions and 12 deletions

View File

@@ -0,0 +1,111 @@
package org.thoughtcrime.securesms.database
import android.content.Context
import org.signal.core.util.delete
import org.signal.core.util.exists
import org.signal.core.util.insertInto
import org.signal.core.util.readToList
import org.signal.core.util.readToSingleObject
import org.signal.core.util.requireBoolean
import org.signal.core.util.requireNonNullBlob
import org.signal.core.util.select
import org.signal.libsignal.protocol.state.KyberPreKeyRecord
import org.whispersystems.signalservice.api.push.ServiceId
/**
* A table for storing data related to [org.thoughtcrime.securesms.crypto.storage.SignalKyberPreKeyStore].
*/
class KyberPreKeyTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTable(context, databaseHelper) {
companion object {
const val TABLE_NAME = "kyber_prekey"
const val ID = "_id"
const val ACCOUNT_ID = "account_id"
const val KEY_ID = "key_id"
const val TIMESTAMP = "timestamp"
const val LAST_RESORT = "last_resort"
const val SERIALIZED = "serialized"
const val CREATE_TABLE = """
CREATE TABLE $TABLE_NAME (
$ID INTEGER PRIMARY KEY,
$ACCOUNT_ID TEXT NOT NULL,
$KEY_ID INTEGER UNIQUE NOT NULL,
$TIMESTAMP INTEGER NOT NULL,
$LAST_RESORT INTEGER NOT NULL,
$SERIALIZED BLOB NOT NULL,
UNIQUE($ACCOUNT_ID, $KEY_ID)
)
"""
private const val INDEX_ACCOUNT_KEY = "kyber_account_id_key_id"
val CREATE_INDEXES = arrayOf(
"CREATE INDEX IF NOT EXISTS $INDEX_ACCOUNT_KEY ON $TABLE_NAME ($ACCOUNT_ID, $KEY_ID, $LAST_RESORT, $SERIALIZED)"
)
}
fun get(serviceId: ServiceId, keyId: Int): KyberPreKey? {
return readableDatabase
.select(LAST_RESORT, SERIALIZED)
.from("$TABLE_NAME INDEXED BY $INDEX_ACCOUNT_KEY")
.where("$ACCOUNT_ID = ? AND $KEY_ID = ?", serviceId, keyId)
.run()
.readToSingleObject { cursor ->
KyberPreKey(
record = KyberPreKeyRecord(cursor.requireNonNullBlob(SERIALIZED)),
lastResort = cursor.requireBoolean(LAST_RESORT)
)
}
}
fun getAll(serviceId: ServiceId): List<KyberPreKey> {
return readableDatabase
.select(LAST_RESORT, SERIALIZED)
.from("$TABLE_NAME INDEXED BY $INDEX_ACCOUNT_KEY")
.where("$ACCOUNT_ID = ?", serviceId)
.run()
.readToList { cursor ->
KyberPreKey(
record = KyberPreKeyRecord(cursor.requireNonNullBlob(SERIALIZED)),
lastResort = cursor.requireBoolean(LAST_RESORT)
)
}
}
fun contains(serviceId: ServiceId, keyId: Int): Boolean {
return readableDatabase
.exists("$TABLE_NAME INDEXED BY $INDEX_ACCOUNT_KEY")
.where("$ACCOUNT_ID = ? AND $KEY_ID = ?", serviceId, keyId)
.run()
}
fun insert(serviceId: ServiceId, keyId: Int, record: KyberPreKeyRecord) {
writableDatabase
.insertInto(TABLE_NAME)
.values(
ACCOUNT_ID to serviceId.toString(),
KEY_ID to keyId,
TIMESTAMP to record.timestamp,
SERIALIZED to record.serialize()
)
.run(SQLiteDatabase.CONFLICT_REPLACE)
}
fun deleteIfNotLastResort(serviceId: ServiceId, keyId: Int) {
writableDatabase
.delete("$TABLE_NAME INDEXED BY $INDEX_ACCOUNT_KEY")
.where("$ACCOUNT_ID = ? AND $KEY_ID = ? AND $LAST_RESORT = ?", serviceId, keyId, 0)
.run()
}
fun delete(serviceId: ServiceId, keyId: Int) {
writableDatabase
.delete("$TABLE_NAME INDEXED BY $INDEX_ACCOUNT_KEY")
.where("$ACCOUNT_ID = ? AND $KEY_ID = ?", serviceId, keyId)
.run()
}
data class KyberPreKey(
val record: KyberPreKeyRecord,
val lastResort: Boolean
)
}

View File

@@ -74,6 +74,7 @@ open class SignalDatabase(private val context: Application, databaseSecret: Data
val remoteMegaphoneTable: RemoteMegaphoneTable = RemoteMegaphoneTable(context, this)
val pendingPniSignatureMessageTable: PendingPniSignatureMessageTable = PendingPniSignatureMessageTable(context, this)
val callTable: CallTable = CallTable(context, this)
val kyberPreKeyTable: KyberPreKeyTable = KyberPreKeyTable(context, this)
override fun onOpen(db: net.zetetic.database.sqlcipher.SQLiteDatabase) {
db.setForeignKeyConstraintsEnabled(true)
@@ -110,6 +111,7 @@ open class SignalDatabase(private val context: Application, databaseSecret: Data
db.execSQL(PendingPniSignatureMessageTable.CREATE_TABLE)
db.execSQL(CallLinkTable.CREATE_TABLE)
db.execSQL(CallTable.CREATE_TABLE)
db.execSQL(KyberPreKeyTable.CREATE_TABLE)
executeStatements(db, SearchTable.CREATE_TABLE)
executeStatements(db, RemappedRecordTables.CREATE_TABLE)
executeStatements(db, MessageSendLogTables.CREATE_TABLE)
@@ -135,6 +137,7 @@ open class SignalDatabase(private val context: Application, databaseSecret: Data
executeStatements(db, PendingPniSignatureMessageTable.CREATE_INDEXES)
executeStatements(db, CallTable.CREATE_INDEXES)
executeStatements(db, ReactionTable.CREATE_INDEXES)
executeStatements(db, KyberPreKeyTable.CREATE_INDEXES)
executeStatements(db, SearchTable.CREATE_TRIGGERS)
executeStatements(db, MessageSendLogTables.CREATE_TRIGGERS)
@@ -403,6 +406,11 @@ open class SignalDatabase(private val context: Application, databaseSecret: Data
val identities: IdentityTable
get() = instance!!.identityTable
@get:JvmStatic
@get:JvmName("kyberPreKeys")
val kyberPreKeys: KyberPreKeyTable
get() = instance!!.kyberPreKeyTable
@get:JvmStatic
@get:JvmName("media")
val media: MediaTable

View File

@@ -49,6 +49,7 @@ import org.thoughtcrime.securesms.database.helpers.migration.V190_UniqueMessageM
import org.thoughtcrime.securesms.database.helpers.migration.V191_UniqueMessageMigrationV2
import org.thoughtcrime.securesms.database.helpers.migration.V192_CallLinkTableNullableRootKeys
import org.thoughtcrime.securesms.database.helpers.migration.V193_BackCallLinksWithRecipient
import org.thoughtcrime.securesms.database.helpers.migration.V194_KyberPreKeyMigration
/**
* Contains all of the database migrations for [SignalDatabase]. Broken into a separate file for cleanliness.
@@ -57,7 +58,7 @@ object SignalDatabaseMigrations {
val TAG: String = Log.tag(SignalDatabaseMigrations.javaClass)
const val DATABASE_VERSION = 193
const val DATABASE_VERSION = 194
@JvmStatic
fun migrate(context: Application, db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
@@ -240,6 +241,10 @@ object SignalDatabaseMigrations {
if (oldVersion < 193) {
V193_BackCallLinksWithRecipient.migrate(context, db, oldVersion, newVersion)
}
if (oldVersion < 194) {
V194_KyberPreKeyMigration.migrate(context, db, oldVersion, newVersion)
}
}
@JvmStatic

View File

@@ -0,0 +1,32 @@
/*
* Copyright 2023 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.thoughtcrime.securesms.database.helpers.migration
import android.app.Application
import net.zetetic.database.sqlcipher.SQLiteDatabase
/**
* Introduces [org.thoughtcrime.securesms.database.KyberPreKeyTable].
*/
object V194_KyberPreKeyMigration : SignalDatabaseMigration {
override fun migrate(context: Application, db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
db.execSQL(
"""
CREATE TABLE kyber_prekey (
_id INTEGER PRIMARY KEY,
account_id TEXT NOT NULL,
key_id INTEGER UNIQUE NOT NULL,
timestamp INTEGER NOT NULL,
last_resort INTEGER NOT NULL,
serialized BLOB NOT NULL,
UNIQUE(account_id, key_id)
)
"""
)
db.execSQL("CREATE INDEX IF NOT EXISTS kyber_account_id_key_id ON kyber_prekey (account_id, key_id, last_resort, serialized)")
}
}