remove shared sender keys

This commit is contained in:
Ryan ZHAO
2021-02-18 14:14:05 +11:00
parent 568fddf91d
commit 9d0831b874
29 changed files with 120 additions and 678 deletions

View File

@@ -75,7 +75,6 @@ import org.thoughtcrime.securesms.loki.api.PublicChatManager;
import org.thoughtcrime.securesms.loki.database.LokiAPIDatabase;
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.loki.protocol.MultiDeviceProtocol;
import org.thoughtcrime.securesms.loki.protocol.SessionResetImplementation;
import org.thoughtcrime.securesms.loki.utilities.Broadcaster;
@@ -182,12 +181,10 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
LokiAPIDatabase apiDB = DatabaseFactory.getLokiAPIDatabase(this);
LokiThreadDatabase threadDB = DatabaseFactory.getLokiThreadDatabase(this);
LokiUserDatabase userDB = DatabaseFactory.getLokiUserDatabase(this);
SharedSenderKeysDatabase sskDatabase = DatabaseFactory.getSSKDatabase(this);
String userPublicKey = TextSecurePreferences.getLocalNumber(this);
SessionResetImplementation sessionResetImpl = new SessionResetImplementation(this);
MessagingConfiguration.Companion.configure(this,
DatabaseFactory.getStorage(this),
sskDatabase,
DatabaseFactory.getAttachmentProvider(this),
new SessionProtocolImpl(this));
if (userPublicKey != null) {
@@ -507,8 +504,7 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
}
return Unit.INSTANCE;
});
SharedSenderKeysDatabase sskDatabase = DatabaseFactory.getSSKDatabase(this);
ClosedGroupPoller.Companion.configureIfNeeded(this, sskDatabase);
ClosedGroupPoller.Companion.configureIfNeeded(this);
closedGroupPoller = ClosedGroupPoller.Companion.getShared();
}

View File

@@ -1098,16 +1098,16 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
builder.setPositiveButton(R.string.yes, (dialog, which) -> {
Recipient groupRecipient = getRecipient();
String groupPublicKey;
boolean isSSKBasedClosedGroup;
boolean isClosedGroup;
try {
groupPublicKey = HexEncodingKt.toHexString(GroupUtil.doubleDecodeGroupID(groupRecipient.getAddress().toString()));
isSSKBasedClosedGroup = DatabaseFactory.getSSKDatabase(this).isSSKBasedClosedGroup(groupPublicKey);
isClosedGroup = DatabaseFactory.getLokiAPIDatabase(this).isClosedGroup(groupPublicKey);
} catch (IOException e) {
groupPublicKey = null;
isSSKBasedClosedGroup = false;
isClosedGroup = false;
}
try {
if (isSSKBasedClosedGroup) {
if (isClosedGroup) {
ClosedGroupsProtocolV2.explicitLeave(this, groupPublicKey);
initializeEnabledCheck();
} else {

View File

@@ -34,7 +34,6 @@ import org.thoughtcrime.securesms.loki.database.LokiMessageDatabase;
import org.thoughtcrime.securesms.loki.database.LokiThreadDatabase;
import org.thoughtcrime.securesms.loki.database.LokiUserDatabase;
import org.thoughtcrime.securesms.loki.database.SessionJobDatabase;
import org.thoughtcrime.securesms.loki.database.SharedSenderKeysDatabase;
public class DatabaseFactory {
@@ -67,7 +66,6 @@ public class DatabaseFactory {
private final LokiThreadDatabase lokiThreadDatabase;
private final LokiUserDatabase lokiUserDatabase;
private final LokiBackupFilesDatabase lokiBackupFilesDatabase;
private final SharedSenderKeysDatabase sskDatabase;
private final SessionJobDatabase sessionJobDatabase;
// Refactor
@@ -176,10 +174,6 @@ public class DatabaseFactory {
return getInstance(context).lokiBackupFilesDatabase;
}
public static SharedSenderKeysDatabase getSSKDatabase(Context context) {
return getInstance(context).sskDatabase;
}
public static SessionJobDatabase getSessionJobDatabase(Context context) {
return getInstance(context).sessionJobDatabase;
}
@@ -229,7 +223,6 @@ public class DatabaseFactory {
this.lokiThreadDatabase = new LokiThreadDatabase(context, databaseHelper);
this.lokiUserDatabase = new LokiUserDatabase(context, databaseHelper);
this.lokiBackupFilesDatabase = new LokiBackupFilesDatabase(context, databaseHelper);
this.sskDatabase = new SharedSenderKeysDatabase(context, databaseHelper);
this.storage = new Storage(context, databaseHelper);
this.attachmentProvider = new DatabaseAttachmentProvider(context, databaseHelper);
this.sessionJobDatabase = new SessionJobDatabase(context, databaseHelper);

View File

@@ -417,9 +417,9 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context,
}
override fun isClosedGroup(publicKey: String): Boolean {
val isSSKBasedClosedGroup = DatabaseFactory.getSSKDatabase(context).isSSKBasedClosedGroup(publicKey)
val isClosedGroup = DatabaseFactory.getLokiAPIDatabase(context).isClosedGroup(publicKey)
val address = Address.fromSerialized(publicKey)
return address.isClosedGroup || isSSKBasedClosedGroup
return address.isClosedGroup || isClosedGroup
}
override fun getClosedGroupEncryptionKeyPairs(groupPublicKey: String): MutableList<ECKeyPair> {
@@ -431,7 +431,7 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context,
}
override fun getAllClosedGroupPublicKeys(): Set<String> {
return DatabaseFactory.getSSKDatabase(context).getAllClosedGroupPublicKeys()
return DatabaseFactory.getLokiAPIDatabase(context).getAllClosedGroupPublicKeys()
}
override fun addClosedGroupPublicKey(groupPublicKey: String) {

View File

@@ -32,7 +32,6 @@ import org.thoughtcrime.securesms.loki.database.LokiBackupFilesDatabase;
import org.thoughtcrime.securesms.loki.database.LokiMessageDatabase;
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.loki.protocol.ClosedGroupsMigration;
public class SQLCipherOpenHelper extends SQLiteOpenHelper {
@@ -131,9 +130,6 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
db.execSQL(LokiUserDatabase.getCreateDisplayNameTableCommand());
db.execSQL(LokiUserDatabase.getCreateServerDisplayNameTableCommand());
db.execSQL(LokiBackupFilesDatabase.getCreateTableCommand());
db.execSQL(SharedSenderKeysDatabase.getCreateOldClosedGroupRatchetTableCommand());
db.execSQL(SharedSenderKeysDatabase.getCreateCurrentClosedGroupRatchetTableCommand());
db.execSQL(SharedSenderKeysDatabase.getCreateClosedGroupPrivateKeyTableCommand());
executeStatements(db, SmsDatabase.CREATE_INDEXS);
executeStatements(db, MmsDatabase.CREATE_INDEXS);
@@ -186,8 +182,8 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
if (oldVersion < lokiV12) {
db.execSQL(LokiAPIDatabase.getCreateLastMessageHashValueTable2Command());
db.execSQL(SharedSenderKeysDatabase.getCreateCurrentClosedGroupRatchetTableCommand());
db.execSQL(SharedSenderKeysDatabase.getCreateClosedGroupPrivateKeyTableCommand());
db.execSQL(ClosedGroupsMigration.getCreateCurrentClosedGroupRatchetTableCommand());
db.execSQL(ClosedGroupsMigration.getCreateClosedGroupPrivateKeyTableCommand());
}
if (oldVersion < lokiV13) {
@@ -199,7 +195,7 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
}
if (oldVersion < lokiV15) {
db.execSQL(SharedSenderKeysDatabase.getCreateOldClosedGroupRatchetTableCommand());
db.execSQL(ClosedGroupsMigration.getCreateOldClosedGroupRatchetTableCommand());
}
if (oldVersion < lokiV16) {

View File

@@ -98,7 +98,6 @@ public class SignalCommunicationModule {
Optional.of(new MessageSenderEventListener(context)),
TextSecurePreferences.getLocalNumber(context),
DatabaseFactory.getLokiAPIDatabase(context),
DatabaseFactory.getSSKDatabase(context),
DatabaseFactory.getLokiThreadDatabase(context),
DatabaseFactory.getLokiMessageDatabase(context),
null, // DatabaseFactory.getLokiPreKeyBundleDatabase(context)

View File

@@ -248,7 +248,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
SessionResetProtocol sessionResetProtocol = new SessionResetImplementation(context);
SignalServiceAddress localAddress = new SignalServiceAddress(TextSecurePreferences.getLocalNumber(context));
LokiAPIDatabase apiDB = DatabaseFactory.getLokiAPIDatabase(context);
LokiServiceCipher cipher = new LokiServiceCipher(localAddress, axolotlStore, DatabaseFactory.getSSKDatabase(context), new SessionProtocolImpl(context), sessionResetProtocol, apiDB, UnidentifiedAccessUtil.getCertificateValidator());
LokiServiceCipher cipher = new LokiServiceCipher(localAddress, axolotlStore, new SessionProtocolImpl(context), sessionResetProtocol, apiDB, UnidentifiedAccessUtil.getCertificateValidator());
SignalServiceContent content = cipher.decrypt(envelope);

View File

@@ -246,21 +246,21 @@ class EditClosedGroupActivity : PassphraseRequiredActionBarActivity() {
val admins = members.toSet() //TODO For now, consider all the users to be admins.
var isSSKBasedClosedGroup: Boolean
var isClosedGroup: Boolean
var groupPublicKey: String?
try {
groupPublicKey = GroupUtil.doubleDecodeGroupID(groupID).toHexString()
isSSKBasedClosedGroup = DatabaseFactory.getSSKDatabase(this).isSSKBasedClosedGroup(groupPublicKey)
isClosedGroup = DatabaseFactory.getLokiAPIDatabase(this).isClosedGroup(groupPublicKey)
} catch (e: IOException) {
groupPublicKey = null
isSSKBasedClosedGroup = false
isClosedGroup = false
}
if (members.isEmpty()) {
return Toast.makeText(this, R.string.activity_edit_closed_group_not_enough_group_members_error, Toast.LENGTH_LONG).show()
}
val maxGroupMembers = if (isSSKBasedClosedGroup) ClosedGroupsProtocolV2.groupSizeLimit else legacyGroupSizeLimit
val maxGroupMembers = if (isClosedGroup) ClosedGroupsProtocolV2.groupSizeLimit else legacyGroupSizeLimit
if (members.size >= maxGroupMembers) {
return Toast.makeText(this, R.string.activity_create_closed_group_too_many_group_members_error, Toast.LENGTH_LONG).show()
}
@@ -273,7 +273,7 @@ class EditClosedGroupActivity : PassphraseRequiredActionBarActivity() {
return Toast.makeText(this@EditClosedGroupActivity, message, Toast.LENGTH_LONG).show()
}
if (isSSKBasedClosedGroup) {
if (isClosedGroup) {
isLoading = true
loaderContainer.fadeIn()
val promise: Promise<Any, Exception> = if (!members.contains(Recipient.from(this, Address.fromSerialized(userPublicKey), false))) {

View File

@@ -167,7 +167,6 @@ class HomeActivity : PassphraseRequiredActionBarActivity, ConversationClickListe
val apiDB = DatabaseFactory.getLokiAPIDatabase(this)
val threadDB = DatabaseFactory.getLokiThreadDatabase(this)
val userDB = DatabaseFactory.getLokiUserDatabase(this)
val sskDatabase = DatabaseFactory.getSSKDatabase(this)
val userPublicKey = TextSecurePreferences.getLocalNumber(this)
val sessionResetImpl = SessionResetImplementation(this)
if (userPublicKey != null) {
@@ -315,7 +314,6 @@ class HomeActivity : PassphraseRequiredActionBarActivity, ConversationClickListe
val threadID = thread.threadId
val recipient = thread.recipient
val threadDB = DatabaseFactory.getThreadDatabase(this)
val isClosedGroup = recipient.address.isClosedGroup
val dialogMessage: String
if (recipient.isGroupRecipient) {
val group = DatabaseFactory.getGroupDatabase(this).getGroup(recipient.address.toString()).orNull()
@@ -333,17 +331,17 @@ class HomeActivity : PassphraseRequiredActionBarActivity, ConversationClickListe
val context = this@HomeActivity as Context
// Send a leave group message if this is an active closed group
if (isClosedGroup && DatabaseFactory.getGroupDatabase(context).isActive(recipient.address.toGroupString())) {
var isSSKBasedClosedGroup: Boolean
if (recipient.address.isClosedGroup && DatabaseFactory.getGroupDatabase(context).isActive(recipient.address.toGroupString())) {
var isClosedGroup: Boolean
var groupPublicKey: String?
try {
groupPublicKey = GroupUtil.doubleDecodeGroupID(recipient.address.toString()).toHexString()
isSSKBasedClosedGroup = DatabaseFactory.getSSKDatabase(context).isSSKBasedClosedGroup(groupPublicKey)
isClosedGroup = DatabaseFactory.getLokiAPIDatabase(context).isClosedGroup(groupPublicKey)
} catch (e: IOException) {
groupPublicKey = null
isSSKBasedClosedGroup = false
isClosedGroup = false
}
if (isSSKBasedClosedGroup) {
if (isClosedGroup) {
ClosedGroupsProtocolV2.explicitLeave(context, groupPublicKey!!)
} else {
Toast.makeText(context, R.string.activity_home_leaving_group_failed_message, Toast.LENGTH_LONG).show()

View File

@@ -76,8 +76,7 @@ class BackgroundPollWorker(val context: Context, params: WorkerParameters) : Wor
promises.add(privateChatsPromise)
// Closed groups
val sskDatabase = DatabaseFactory.getSSKDatabase(context)
ClosedGroupPoller.configureIfNeeded(context, sskDatabase)
ClosedGroupPoller.configureIfNeeded(context)
promises.addAll(ClosedGroupPoller.shared.pollOnce())
// Open Groups

View File

@@ -7,14 +7,14 @@ import nl.komponents.kovenant.functional.bind
import nl.komponents.kovenant.functional.map
import org.thoughtcrime.securesms.jobs.PushContentReceiveJob
import org.session.libsignal.utilities.logging.Log
import org.thoughtcrime.securesms.loki.database.SharedSenderKeysDatabase
import org.session.libsignal.utilities.successBackground
import org.session.libsignal.service.api.messages.SignalServiceEnvelope
import org.session.libsignal.service.loki.api.SnodeAPI
import org.session.libsignal.service.loki.api.SwarmAPI
import org.session.libsignal.service.loki.utilities.getRandomElementOrNull
import org.thoughtcrime.securesms.database.DatabaseFactory
class ClosedGroupPoller private constructor(private val context: Context, private val database: SharedSenderKeysDatabase) {
class ClosedGroupPoller private constructor(private val context: Context) {
private var isPolling = false
private val handler: Handler by lazy { Handler() }
@@ -32,9 +32,9 @@ class ClosedGroupPoller private constructor(private val context: Context, privat
public lateinit var shared: ClosedGroupPoller
public fun configureIfNeeded(context: Context, sskDatabase: SharedSenderKeysDatabase) {
public fun configureIfNeeded(context: Context) {
if (::shared.isInitialized) { return; }
shared = ClosedGroupPoller(context, sskDatabase)
shared = ClosedGroupPoller(context)
}
}
// endregion
@@ -66,7 +66,7 @@ class ClosedGroupPoller private constructor(private val context: Context, privat
// region Private API
private fun poll(): List<Promise<Unit, Exception>> {
if (!isPolling) { return listOf() }
val publicKeys = database.getAllClosedGroupPublicKeys()
val publicKeys = DatabaseFactory.getLokiAPIDatabase(context).getAllClosedGroupPublicKeys()
return publicKeys.map { publicKey ->
val promise = SwarmAPI.shared.getSwarm(publicKey).bind { swarm ->
val snode = swarm.getRandomElementOrNull() ?: throw InsufficientSnodesException() // Should be cryptographically secure

View File

@@ -53,7 +53,7 @@ object LokiPushNotificationManager {
}
}
// Unsubscribe from all closed groups
val allClosedGroupPublicKeys = DatabaseFactory.getSSKDatabase(context).getAllClosedGroupPublicKeys()
val allClosedGroupPublicKeys = DatabaseFactory.getLokiAPIDatabase(context).getAllClosedGroupPublicKeys()
val userPublicKey = TextSecurePreferences.getLocalNumber(context)!!
allClosedGroupPublicKeys.forEach { closedGroup ->
performOperation(context, ClosedGroupOperation.Unsubscribe, closedGroup, userPublicKey)
@@ -84,7 +84,7 @@ object LokiPushNotificationManager {
}
}
// Subscribe to all closed groups
val allClosedGroupPublicKeys = DatabaseFactory.getSSKDatabase(context).getAllClosedGroupPublicKeys()
val allClosedGroupPublicKeys = DatabaseFactory.getLokiAPIDatabase(context).getAllClosedGroupPublicKeys()
allClosedGroupPublicKeys.forEach { closedGroup ->
performOperation(context, ClosedGroupOperation.Subscribe, closedGroup, publicKey)
}

View File

@@ -16,6 +16,7 @@ import org.session.libsignal.service.loki.utilities.toHexString
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil
import org.session.libsignal.utilities.Hex
import org.session.libsession.utilities.TextSecurePreferences
import org.session.libsignal.service.loki.utilities.PublicKeyValidation
import java.util.*
class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper), LokiAPIDatabaseProtocol {
@@ -454,6 +455,11 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(
}.toSet()
}
override fun isClosedGroup(groupPublicKey: String): Boolean {
if (!PublicKeyValidation.isValid(groupPublicKey)) { return false }
return getAllClosedGroupPublicKeys().contains(groupPublicKey)
}
fun removeClosedGroupPublicKey(groupPublicKey: String) {
val database = databaseHelper.writableDatabase
database.delete(closedGroupPublicKeysTable, "${Companion.groupPublicKey} = ?", wrap(groupPublicKey))

View File

@@ -1,143 +0,0 @@
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.session.libsignal.utilities.Hex
import org.session.libsignal.service.loki.protocol.closedgroups.ClosedGroupRatchet
import org.session.libsignal.service.loki.protocol.closedgroups.ClosedGroupRatchetCollectionType
import org.session.libsignal.service.loki.protocol.closedgroups.ClosedGroupSenderKey
import org.session.libsignal.service.loki.protocol.closedgroups.SharedSenderKeysDatabaseProtocol
import org.session.libsignal.service.loki.utilities.PublicKeyValidation
import org.thoughtcrime.securesms.database.DatabaseFactory
class SharedSenderKeysDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper), SharedSenderKeysDatabaseProtocol {
companion object {
// Shared
public val closedGroupPublicKey = "closed_group_public_key"
// Ratchets
private val oldClosedGroupRatchetTable = "old_closed_group_ratchet_table"
private val currentClosedGroupRatchetTable = "closed_group_ratchet_table"
private val senderPublicKey = "sender_public_key"
private val chainKey = "chain_key"
private val keyIndex = "key_index"
private val messageKeys = "message_keys"
@JvmStatic val createOldClosedGroupRatchetTableCommand
= "CREATE TABLE $oldClosedGroupRatchetTable ($closedGroupPublicKey STRING, $senderPublicKey STRING, $chainKey STRING, " +
"$keyIndex INTEGER DEFAULT 0, $messageKeys TEXT, PRIMARY KEY ($closedGroupPublicKey, $senderPublicKey));"
// Private keys
@JvmStatic val createCurrentClosedGroupRatchetTableCommand
= "CREATE TABLE $currentClosedGroupRatchetTable ($closedGroupPublicKey STRING, $senderPublicKey STRING, $chainKey STRING, " +
"$keyIndex INTEGER DEFAULT 0, $messageKeys TEXT, PRIMARY KEY ($closedGroupPublicKey, $senderPublicKey));"
// Private keys
public val closedGroupPrivateKeyTable = "closed_group_private_key_table"
public val closedGroupPrivateKey = "closed_group_private_key"
@JvmStatic val createClosedGroupPrivateKeyTableCommand
= "CREATE TABLE $closedGroupPrivateKeyTable ($closedGroupPublicKey STRING PRIMARY KEY, $closedGroupPrivateKey STRING);"
}
private fun getTable(collection: ClosedGroupRatchetCollectionType): String {
return when (collection) {
ClosedGroupRatchetCollectionType.Old -> oldClosedGroupRatchetTable
ClosedGroupRatchetCollectionType.Current -> currentClosedGroupRatchetTable
}
}
// region Ratchets & Sender Keys
override fun getClosedGroupRatchet(groupPublicKey: String, senderPublicKey: String, collection: ClosedGroupRatchetCollectionType): ClosedGroupRatchet? {
val database = databaseHelper.readableDatabase
val query = "${Companion.closedGroupPublicKey} = ? AND ${Companion.senderPublicKey} = ?"
return database.get(getTable(collection), 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, collection: ClosedGroupRatchetCollectionType) {
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(getTable(collection), values, query, arrayOf( groupPublicKey, senderPublicKey ))
}
override fun removeAllClosedGroupRatchets(groupPublicKey: String, collection: ClosedGroupRatchetCollectionType) {
val database = databaseHelper.writableDatabase
val query = "${Companion.closedGroupPublicKey} = ?"
database.delete(getTable(collection), query, arrayOf( groupPublicKey ))
}
override fun getAllClosedGroupRatchets(groupPublicKey: String, collection: ClosedGroupRatchetCollectionType): Set<Pair<String, ClosedGroupRatchet>> {
val database = databaseHelper.readableDatabase
val query = "${Companion.closedGroupPublicKey} = ?"
return database.getAll(getTable(collection), query, arrayOf( groupPublicKey )) { cursor ->
val chainKey = cursor.getString(Companion.chainKey)
val keyIndex = cursor.getInt(Companion.keyIndex)
val messageKeys = cursor.getString(Companion.messageKeys).split("-")
val senderPublicKey = cursor.getString(Companion.senderPublicKey)
val ratchet = ClosedGroupRatchet(chainKey, keyIndex, messageKeys)
Pair(senderPublicKey, ratchet)
}.toSet()
}
override fun getAllClosedGroupSenderKeys(groupPublicKey: String, collection: ClosedGroupRatchetCollectionType): Set<ClosedGroupSenderKey> {
return getAllClosedGroupRatchets(groupPublicKey, collection).map { pair ->
val senderPublicKey = pair.first
val ratchet = pair.second
ClosedGroupSenderKey(Hex.fromStringCondensed(ratchet.chainKey), ratchet.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(closedGroupPrivateKeyTable, 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(closedGroupPrivateKeyTable, values, query, arrayOf( groupPublicKey ))
}
override fun removeClosedGroupPrivateKey(groupPublicKey: String) {
val database = databaseHelper.writableDatabase
val query = "${Companion.closedGroupPublicKey} = ?"
database.delete(closedGroupPrivateKeyTable, query, arrayOf( groupPublicKey ))
}
override fun getAllClosedGroupPublicKeys(): Set<String> {
val database = databaseHelper.readableDatabase
val result = mutableSetOf<String>()
result.addAll(database.getAll(closedGroupPrivateKeyTable, null, null) { cursor ->
cursor.getString(Companion.closedGroupPublicKey)
}.filter {
PublicKeyValidation.isValid(it)
})
result.addAll(DatabaseFactory.getLokiAPIDatabase(context).getAllClosedGroupPublicKeys())
return result
}
// endregion
override fun isSSKBasedClosedGroup(groupPublicKey: String): Boolean {
if (!PublicKeyValidation.isValid(groupPublicKey)) { return false }
return getAllClosedGroupPublicKeys().contains(groupPublicKey)
}
// endregion
}

View File

@@ -2,7 +2,6 @@ package org.thoughtcrime.securesms.loki.protocol
import android.content.ContentValues
import org.thoughtcrime.securesms.loki.database.LokiAPIDatabase
import org.thoughtcrime.securesms.loki.database.SharedSenderKeysDatabase
import org.thoughtcrime.securesms.loki.utilities.get
import org.thoughtcrime.securesms.loki.utilities.getAll
import org.thoughtcrime.securesms.loki.utilities.getString
@@ -18,17 +17,39 @@ import java.util.*
object ClosedGroupsMigration {
fun perform(database: net.sqlcipher.database.SQLiteDatabase) {
val publicKeys = database.getAll(SharedSenderKeysDatabase.closedGroupPrivateKeyTable, null, null) { cursor ->
cursor.getString(SharedSenderKeysDatabase.closedGroupPublicKey)
public val closedGroupPublicKey = "closed_group_public_key"
// Ratchets
private val oldClosedGroupRatchetTable = "old_closed_group_ratchet_table"
private val currentClosedGroupRatchetTable = "closed_group_ratchet_table"
private val senderPublicKey = "sender_public_key"
private val chainKey = "chain_key"
private val keyIndex = "key_index"
private val messageKeys = "message_keys"
@JvmStatic val createOldClosedGroupRatchetTableCommand
= "CREATE TABLE $oldClosedGroupRatchetTable ($closedGroupPublicKey STRING, $senderPublicKey STRING, $chainKey STRING, " +
"$keyIndex INTEGER DEFAULT 0, $messageKeys TEXT, PRIMARY KEY ($closedGroupPublicKey, $senderPublicKey));"
// Private keys
@JvmStatic val createCurrentClosedGroupRatchetTableCommand
= "CREATE TABLE $currentClosedGroupRatchetTable ($closedGroupPublicKey STRING, $senderPublicKey STRING, $chainKey STRING, " +
"$keyIndex INTEGER DEFAULT 0, $messageKeys TEXT, PRIMARY KEY ($closedGroupPublicKey, $senderPublicKey));"
// Private keys
public val closedGroupPrivateKeyTable = "closed_group_private_key_table"
public val closedGroupPrivateKey = "closed_group_private_key"
@JvmStatic val createClosedGroupPrivateKeyTableCommand
= "CREATE TABLE $closedGroupPrivateKeyTable ($closedGroupPublicKey STRING PRIMARY KEY, $closedGroupPrivateKey STRING);"
fun perform(database: net.sqlcipher.database.SQLiteDatabase) {
val publicKeys = database.getAll(closedGroupPrivateKeyTable, null, null) { cursor ->
cursor.getString(closedGroupPublicKey)
}.filter {
PublicKeyValidation.isValid(it)
}
val keyPairs = mutableListOf<ECKeyPair>()
for (publicKey in publicKeys) {
val query = "${SharedSenderKeysDatabase.closedGroupPublicKey} = ?"
val privateKey = database.get(SharedSenderKeysDatabase.closedGroupPrivateKeyTable, query, arrayOf( publicKey )) { cursor ->
cursor.getString(SharedSenderKeysDatabase.closedGroupPrivateKey)
val query = "${closedGroupPublicKey} = ?"
val privateKey = database.get(closedGroupPrivateKeyTable, query, arrayOf( publicKey )) { cursor ->
cursor.getString(closedGroupPrivateKey)
}
val keyPair = ECKeyPair(DjbECPublicKey(Hex.fromStringCondensed(publicKey.removing05PrefixIfNeeded())), DjbECPrivateKey(Hex.fromStringCondensed(privateKey)))
keyPairs.add(keyPair)