diff --git a/src/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java b/src/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java index 2dc877906d..14708f9f38 100644 --- a/src/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java +++ b/src/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java @@ -41,6 +41,7 @@ import org.thoughtcrime.securesms.loki.database.LokiPreKeyBundleDatabase; import org.thoughtcrime.securesms.loki.database.LokiPreKeyRecordDatabase; import org.thoughtcrime.securesms.loki.database.LokiThreadDatabase; import org.thoughtcrime.securesms.loki.database.LokiUserDatabase; +import org.thoughtcrime.securesms.loki.database.SharedSenderKeysDatabase; import org.thoughtcrime.securesms.notifications.NotificationChannels; import org.thoughtcrime.securesms.service.KeyCachingService; import org.thoughtcrime.securesms.util.GroupUtil; @@ -85,8 +86,9 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper { private static final int lokiV9 = 30; private static final int lokiV10 = 31; private static final int lokiV11 = 32; + private static final int lokiV12 = 33; - private static final int DATABASE_VERSION = lokiV11; // Loki - onUpgrade(...) must be updated to use Loki version numbers if Signal makes any database changes + private static final int DATABASE_VERSION = lokiV12; // Loki - onUpgrade(...) must be updated to use Loki version numbers if Signal makes any database changes private static final String DATABASE_NAME = "signal.db"; private final Context context; @@ -157,6 +159,8 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper { db.execSQL(LokiThreadDatabase.getCreatePublicChatTableCommand()); db.execSQL(LokiUserDatabase.getCreateDisplayNameTableCommand()); db.execSQL(LokiUserDatabase.getCreateServerDisplayNameTableCommand()); + db.execSQL(SharedSenderKeysDatabase.getCreateClosedGroupRatchetsTableCommand()); + db.execSQL(SharedSenderKeysDatabase.getCreateClosedGroupPrivateKeysTableCommand()); executeStatements(db, SmsDatabase.CREATE_INDEXS); executeStatements(db, MmsDatabase.CREATE_INDEXS); @@ -603,6 +607,11 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper { db.execSQL(LokiAPIDatabase.getCreateOpenGroupPublicKeyDBCommand()); } + if (oldVersion < lokiV12) { + db.execSQL(SharedSenderKeysDatabase.getCreateClosedGroupRatchetsTableCommand()); + db.execSQL(SharedSenderKeysDatabase.getCreateClosedGroupPrivateKeysTableCommand()); + } + db.setTransactionSuccessful(); } finally { db.endTransaction(); diff --git a/src/org/thoughtcrime/securesms/loki/database/SharedSenderKeysDatabase.kt b/src/org/thoughtcrime/securesms/loki/database/SharedSenderKeysDatabase.kt new file mode 100644 index 0000000000..749f4ba02f --- /dev/null +++ b/src/org/thoughtcrime/securesms/loki/database/SharedSenderKeysDatabase.kt @@ -0,0 +1,105 @@ +package org.thoughtcrime.securesms.loki.database + +import android.content.ContentValues +import android.content.Context +import org.thoughtcrime.securesms.database.Database +import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper +import org.thoughtcrime.securesms.loki.utilities.* +import org.thoughtcrime.securesms.util.Hex +import org.whispersystems.signalservice.loki.protocol.closedgroups.ClosedGroupRatchet +import org.whispersystems.signalservice.loki.protocol.closedgroups.ClosedGroupSenderKey +import org.whispersystems.signalservice.loki.protocol.closedgroups.SharedSenderKeysDatabaseProtocol + +class SharedSenderKeysDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper), SharedSenderKeysDatabaseProtocol { + + companion object { + // Shared + private val closedGroupPublicKey = "closed_group_public_key" + // Ratchets + private val closedGroupRatchetsTable = "closed_group_ratchets" + private val senderPublicKey = "sender_public_key" + private val chainKey = "chain_key" + private val keyIndex = "key_index" + private val messageKeys = "message_keys" + @JvmStatic val createClosedGroupRatchetsTableCommand + = "CREATE TABLE $closedGroupRatchetsTable (PRIMARY KEY ($closedGroupPublicKey, $senderPublicKey), $chainKey STRING, $keyIndex INTEGER DEFAULT 0, $messageKeys STRING);" + // Private keys + private val closedGroupPrivateKeysTable = "closed_group_private_keys" + private val closedGroupPrivateKey = "closed_group_private_key" + @JvmStatic val createClosedGroupPrivateKeysTableCommand + = "CREATE TABLE $closedGroupPrivateKeysTable ($closedGroupPublicKey STRING PRIMARY KEY, $closedGroupPrivateKey STRING);" + } + + // region Ratchets & Sender Keys + override fun getClosedGroupRatchet(groupPublicKey: String, senderPublicKey: String): ClosedGroupRatchet? { + val database = databaseHelper.readableDatabase + val query = "${Companion.closedGroupPublicKey} = ? AND ${Companion.senderPublicKey} = ?" + return database.get(closedGroupRatchetsTable, query, arrayOf( groupPublicKey, senderPublicKey )) { cursor -> + val chainKey = cursor.getString(Companion.chainKey) + val keyIndex = cursor.getInt(Companion.keyIndex) + val messageKeys = cursor.getString(Companion.messageKeys).split("-") + ClosedGroupRatchet(chainKey, keyIndex, messageKeys) + } + } + + override fun setClosedGroupRatchet(groupPublicKey: String, senderPublicKey: String, ratchet: ClosedGroupRatchet) { + val database = databaseHelper.writableDatabase + val values = ContentValues() + values.put(Companion.closedGroupPublicKey, groupPublicKey) + values.put(Companion.senderPublicKey, senderPublicKey) + values.put(Companion.chainKey, ratchet.chainKey) + values.put(Companion.keyIndex, ratchet.keyIndex) + values.put(Companion.messageKeys, ratchet.messageKeys.joinToString("-")) + val query = "${Companion.closedGroupPublicKey} = ? AND ${Companion.senderPublicKey} = ?" + database.insertOrUpdate(closedGroupRatchetsTable, values, query, arrayOf( groupPublicKey, senderPublicKey )) + } + + override fun removeAllClosedGroupRatchets(groupPublicKey: String) { + val database = databaseHelper.writableDatabase + database.delete(closedGroupRatchetsTable, null, null) + } + + override fun getAllClosedGroupSenderKeys(groupPublicKey: String): Set { + val database = databaseHelper.readableDatabase + val query = "${Companion.closedGroupPublicKey} = ? AND ${Companion.senderPublicKey} = ?" + return database.getAll(closedGroupRatchetsTable, query, arrayOf( groupPublicKey, senderPublicKey )) { cursor -> + val chainKey = cursor.getString(Companion.chainKey) + val keyIndex = cursor.getInt(Companion.keyIndex) + val senderPublicKey = cursor.getString(Companion.senderPublicKey) + ClosedGroupSenderKey(Hex.fromStringCondensed(chainKey), keyIndex, Hex.fromStringCondensed(senderPublicKey)) + }.toSet() + } + // endregion + + // region Public & Private Keys + override fun getClosedGroupPrivateKey(groupPublicKey: String): String? { + val database = databaseHelper.readableDatabase + val query = "${Companion.closedGroupPublicKey} = ?" + return database.get(closedGroupPrivateKeysTable, query, arrayOf( groupPublicKey )) { cursor -> + cursor.getString(Companion.closedGroupPrivateKey) + } + } + + override fun setClosedGroupPrivateKey(groupPublicKey: String, groupPrivateKey: String) { + val database = databaseHelper.writableDatabase + val values = ContentValues() + values.put(Companion.closedGroupPublicKey, groupPublicKey) + values.put(Companion.closedGroupPrivateKey, groupPrivateKey) + val query = "${Companion.closedGroupPublicKey} = ?" + database.insertOrUpdate(closedGroupPrivateKeysTable, values, query, arrayOf( groupPublicKey )) + } + + override fun removeClosedGroupPrivateKey(groupPublicKey: String) { + val database = databaseHelper.writableDatabase + val query = "${Companion.closedGroupPublicKey} = ?" + database.delete(closedGroupPrivateKeysTable, query, arrayOf( groupPublicKey )) + } + + override fun getAllClosedGroupPublicKeys(): Set { + val database = databaseHelper.readableDatabase + return database.getAll(closedGroupPrivateKeysTable, null, null) { cursor -> + cursor.getString(Companion.closedGroupPublicKey) + }.toSet() + } + // endregion +} diff --git a/src/org/thoughtcrime/securesms/loki/utilities/DatabaseUtilities.kt b/src/org/thoughtcrime/securesms/loki/utilities/DatabaseUtilities.kt index 8d3112939b..33d8997f02 100644 --- a/src/org/thoughtcrime/securesms/loki/utilities/DatabaseUtilities.kt +++ b/src/org/thoughtcrime/securesms/loki/utilities/DatabaseUtilities.kt @@ -5,7 +5,7 @@ import net.sqlcipher.Cursor import net.sqlcipher.database.SQLiteDatabase import org.whispersystems.signalservice.internal.util.Base64 -fun SQLiteDatabase.get(table: String, query: String, arguments: Array, get: (Cursor) -> T): T? { +fun SQLiteDatabase.get(table: String, query: String?, arguments: Array?, get: (Cursor) -> T): T? { var cursor: Cursor? = null try { cursor = query(table, null, query, arguments, null, null, null) @@ -18,7 +18,7 @@ fun SQLiteDatabase.get(table: String, query: String, arguments: Array SQLiteDatabase.getAll(table: String, query: String, arguments: Array, get: (Cursor) -> T): List { +fun SQLiteDatabase.getAll(table: String, query: String?, arguments: Array?, get: (Cursor) -> T): List { val result = mutableListOf() var cursor: Cursor? = null try {