diff --git a/app/src/main/java/org/thoughtcrime/securesms/crypto/SenderKeyUtil.java b/app/src/main/java/org/thoughtcrime/securesms/crypto/SenderKeyUtil.java index d0a1a0fc87..a0114e6452 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/crypto/SenderKeyUtil.java +++ b/app/src/main/java/org/thoughtcrime/securesms/crypto/SenderKeyUtil.java @@ -8,6 +8,7 @@ import org.thoughtcrime.securesms.crypto.storage.SignalSenderKeyStore; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; import org.thoughtcrime.securesms.recipients.Recipient; +import org.whispersystems.libsignal.SignalProtocolAddress; import org.whispersystems.signalservice.api.SignalSessionLock; import org.whispersystems.signalservice.api.push.DistributionId; import org.whispersystems.signalservice.api.push.SignalServiceAddress; @@ -20,7 +21,7 @@ public final class SenderKeyUtil { */ public static void rotateOurKey(@NonNull Context context, @NonNull DistributionId distributionId) { try (SignalSessionLock.Lock unused = ReentrantSessionLock.INSTANCE.acquire()) { - ApplicationDependencies.getSenderKeyStore().deleteAllFor(Recipient.self().getId(), distributionId); + ApplicationDependencies.getSenderKeyStore().deleteAllFor(Recipient.self().requireServiceId(), distributionId); DatabaseFactory.getSenderKeySharedDatabase(context).deleteAllFor(distributionId); } } @@ -29,7 +30,8 @@ public final class SenderKeyUtil { * Gets when the sender key session was created, or -1 if it doesn't exist. */ public static long getCreateTimeForOurKey(@NonNull Context context, @NonNull DistributionId distributionId) { - return DatabaseFactory.getSenderKeyDatabase(context).getCreatedTime(Recipient.self().getId(), SignalServiceAddress.DEFAULT_DEVICE_ID, distributionId); + SignalProtocolAddress address = new SignalProtocolAddress(Recipient.self().requireServiceId(), SignalServiceAddress.DEFAULT_DEVICE_ID); + return DatabaseFactory.getSenderKeyDatabase(context).getCreatedTime(address, distributionId); } /** diff --git a/app/src/main/java/org/thoughtcrime/securesms/crypto/storage/SignalSenderKeyStore.java b/app/src/main/java/org/thoughtcrime/securesms/crypto/storage/SignalSenderKeyStore.java index 6a8b9cf33f..8eb79e554d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/crypto/storage/SignalSenderKeyStore.java +++ b/app/src/main/java/org/thoughtcrime/securesms/crypto/storage/SignalSenderKeyStore.java @@ -34,16 +34,14 @@ public final class SignalSenderKeyStore implements SignalServiceSenderKeyStore { @Override public void storeSenderKey(@NonNull SignalProtocolAddress sender, @NonNull UUID distributionId, @NonNull SenderKeyRecord record) { synchronized (LOCK) { - RecipientId recipientId = RecipientId.fromExternalPush(sender.getName()); - DatabaseFactory.getSenderKeyDatabase(context).store(recipientId, sender.getDeviceId(), DistributionId.from(distributionId), record); + DatabaseFactory.getSenderKeyDatabase(context).store(sender, DistributionId.from(distributionId), record); } } @Override public @Nullable SenderKeyRecord loadSenderKey(@NonNull SignalProtocolAddress sender, @NonNull UUID distributionId) { synchronized (LOCK) { - RecipientId recipientId = RecipientId.fromExternalPush(sender.getName()); - return DatabaseFactory.getSenderKeyDatabase(context).load(recipientId, sender.getDeviceId(), DistributionId.from(distributionId)); + return DatabaseFactory.getSenderKeyDatabase(context).load(sender, DistributionId.from(distributionId)); } } @@ -71,9 +69,9 @@ public final class SignalSenderKeyStore implements SignalServiceSenderKeyStore { /** * Removes all sender key session state for all devices for the provided recipient-distributionId pair. */ - public void deleteAllFor(@NonNull RecipientId recipientId, @NonNull DistributionId distributionId) { + public void deleteAllFor(@NonNull String addressName, @NonNull DistributionId distributionId) { synchronized (LOCK) { - DatabaseFactory.getSenderKeyDatabase(context).deleteAllFor(recipientId, distributionId); + DatabaseFactory.getSenderKeyDatabase(context).deleteAllFor(addressName, distributionId); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/MessageSendLogDatabase.kt b/app/src/main/java/org/thoughtcrime/securesms/database/MessageSendLogDatabase.kt index 2399eaf5d5..993e01ea63 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/MessageSendLogDatabase.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/MessageSendLogDatabase.kt @@ -362,5 +362,17 @@ class MessageSendLogDatabase constructor(context: Context?, databaseHelper: SQLC db.delete(PayloadTable.TABLE_NAME, query, args) } + fun remapRecipient(oldRecipientId: RecipientId, newRecipientId: RecipientId) { + val values = ContentValues().apply { + put(RecipientTable.RECIPIENT_ID, newRecipientId.serialize()) + } + + val db = databaseHelper.signalWritableDatabase + val query = "${RecipientTable.RECIPIENT_ID} = ?" + val args = SqlUtil.buildArgs(oldRecipientId.serialize()) + + db.update(RecipientTable.TABLE_NAME, values, query, args) + } + private data class RecipientDevice(val recipientId: RecipientId, val devices: List) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/RecipientDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/RecipientDatabase.java index 149f926f3f..475b16f5b8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/RecipientDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/RecipientDatabase.java @@ -3111,6 +3111,9 @@ public class RecipientDatabase extends Database { Log.w(TAG, "Had no sessions. No action necessary.", true); } + // MSL + DatabaseFactory.getMessageLogDatabase(context).remapRecipient(byE164, byUuid); + // Mentions ContentValues mentionRecipientValues = new ContentValues(); mentionRecipientValues.put(MentionDatabase.RECIPIENT_ID, byUuid.serialize()); diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/SenderKeyDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/SenderKeyDatabase.java index 7d9bdfecc2..443af82693 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/SenderKeyDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/SenderKeyDatabase.java @@ -10,8 +10,8 @@ import androidx.annotation.Nullable; import org.signal.core.util.logging.Log; import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper; +import org.whispersystems.libsignal.SignalProtocolAddress; import org.whispersystems.signalservice.api.push.DistributionId; -import org.thoughtcrime.securesms.recipients.RecipientId; import org.thoughtcrime.securesms.util.CursorUtil; import org.thoughtcrime.securesms.util.SqlUtil; import org.whispersystems.libsignal.groups.state.SenderKeyRecord; @@ -31,30 +31,30 @@ public class SenderKeyDatabase extends Database { public static final String TABLE_NAME = "sender_keys"; private static final String ID = "_id"; - public static final String RECIPIENT_ID = "recipient_id"; + public static final String ADDRESS = "address"; public static final String DEVICE = "device"; public static final String DISTRIBUTION_ID = "distribution_id"; public static final String RECORD = "record"; public static final String CREATED_AT = "created_at"; public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + "(" + ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " + - RECIPIENT_ID + " INTEGER NOT NULL, " + + ADDRESS + " TEXT NOT NULL, " + DEVICE + " INTEGER NOT NULL, " + DISTRIBUTION_ID + " TEXT NOT NULL, " + RECORD + " BLOB NOT NULL, " + CREATED_AT + " INTEGER NOT NULL, " + - "UNIQUE(" + RECIPIENT_ID + "," + DEVICE + ", " + DISTRIBUTION_ID + ") ON CONFLICT REPLACE);"; + "UNIQUE(" + ADDRESS + "," + DEVICE + ", " + DISTRIBUTION_ID + ") ON CONFLICT REPLACE);"; SenderKeyDatabase(Context context, SQLCipherOpenHelper databaseHelper) { super(context, databaseHelper); } - public void store(@NonNull RecipientId recipientId, int deviceId, @NonNull DistributionId distributionId, @NonNull SenderKeyRecord record) { + public void store(@NonNull SignalProtocolAddress address, @NonNull DistributionId distributionId, @NonNull SenderKeyRecord record) { SQLiteDatabase db = databaseHelper.getSignalWritableDatabase(); ContentValues values = new ContentValues(); - values.put(RECIPIENT_ID, recipientId.serialize()); - values.put(DEVICE, deviceId); + values.put(ADDRESS, address.getName()); + values.put(DEVICE, address.getDeviceId()); values.put(DISTRIBUTION_ID, distributionId.toString()); values.put(RECORD, record.serialize()); values.put(CREATED_AT, System.currentTimeMillis()); @@ -62,11 +62,11 @@ public class SenderKeyDatabase extends Database { db.insertWithOnConflict(TABLE_NAME, null, values, SQLiteDatabase.CONFLICT_REPLACE); } - public @Nullable SenderKeyRecord load(@NonNull RecipientId recipientId, int deviceId, @NonNull DistributionId distributionId) { + public @Nullable SenderKeyRecord load(@NonNull SignalProtocolAddress address, @NonNull DistributionId distributionId) { SQLiteDatabase db = databaseHelper.getSignalReadableDatabase(); - String query = RECIPIENT_ID + " = ? AND " + DEVICE + " = ? AND " + DISTRIBUTION_ID + " = ?"; - String[] args = SqlUtil.buildArgs(recipientId, deviceId, distributionId); + String query = ADDRESS + " = ? AND " + DEVICE + " = ? AND " + DISTRIBUTION_ID + " = ?"; + String[] args = SqlUtil.buildArgs(address.getName(), address.getDeviceId(), distributionId); try (Cursor cursor = db.query(TABLE_NAME, new String[]{ RECORD }, query, args, null, null, null)) { if (cursor.moveToFirst()) { @@ -84,11 +84,11 @@ public class SenderKeyDatabase extends Database { /** * Gets when the sender key session was created, or -1 if it doesn't exist. */ - public long getCreatedTime(@NonNull RecipientId recipientId, int deviceId, @NonNull DistributionId distributionId) { + public long getCreatedTime(@NonNull SignalProtocolAddress address, @NonNull DistributionId distributionId) { SQLiteDatabase db = databaseHelper.getSignalReadableDatabase(); - String query = RECIPIENT_ID + " = ? AND " + DEVICE + " = ? AND " + DISTRIBUTION_ID + " = ?"; - String[] args = SqlUtil.buildArgs(recipientId, deviceId, distributionId); + String query = ADDRESS + " = ? AND " + DEVICE + " = ? AND " + DISTRIBUTION_ID + " = ?"; + String[] args = SqlUtil.buildArgs(address.getName(), address.getDeviceId(), distributionId); try (Cursor cursor = db.query(TABLE_NAME, new String[]{ CREATED_AT }, query, args, null, null, null)) { if (cursor.moveToFirst()) { @@ -102,10 +102,10 @@ public class SenderKeyDatabase extends Database { /** * Removes all sender key session state for all devices for the provided recipient-distributionId pair. */ - public void deleteAllFor(@NonNull RecipientId recipientId, @NonNull DistributionId distributionId) { + public void deleteAllFor(@NonNull String addressName, @NonNull DistributionId distributionId) { SQLiteDatabase db = databaseHelper.getSignalWritableDatabase(); - String query = RECIPIENT_ID + " = ? AND " + DISTRIBUTION_ID + " = ?"; - String[] args = SqlUtil.buildArgs(recipientId, distributionId); + String query = ADDRESS + " = ? AND " + DISTRIBUTION_ID + " = ?"; + String[] args = SqlUtil.buildArgs(addressName, distributionId); db.delete(TABLE_NAME, query, args); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java index 0e3501cfeb..2ba95f4f1e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java @@ -217,8 +217,9 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper implements SignalDatab private static final int CLEANUP_SESSION_MIGRATION = 116; private static final int RECEIPT_TIMESTAMP = 117; private static final int BADGES = 118; + private static final int SENDER_KEY_UUID = 119; - private static final int DATABASE_VERSION = 118; + private static final int DATABASE_VERSION = 119; private static final String DATABASE_NAME = "signal.db"; private final Context context; @@ -2048,6 +2049,32 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper implements SignalDatab db.execSQL("ALTER TABLE recipient ADD COLUMN badges BLOB DEFAULT NULL"); } + if (oldVersion < SENDER_KEY_UUID) { + long start = System.currentTimeMillis(); + + db.execSQL("CREATE TABLE sender_keys_tmp (_id INTEGER PRIMARY KEY AUTOINCREMENT, " + + "address TEXT NOT NULL, " + + "device INTEGER NOT NULL, " + + "distribution_id TEXT NOT NULL, " + + "record BLOB NOT NULL, " + + "created_at INTEGER NOT NULL, " + + "UNIQUE(address, device, distribution_id) ON CONFLICT REPLACE)"); + + db.execSQL("INSERT INTO sender_keys_tmp (address, device, distribution_id, record, created_at) " + + "SELECT recipient.uuid AS new_address, " + + "sender_keys.device, " + + "sender_keys.distribution_id, " + + "sender_keys.record, " + + "sender_keys.created_at " + + "FROM sender_keys INNER JOIN recipient ON sender_keys.recipient_id = recipient._id " + + "WHERE new_address NOT NULL"); + + db.execSQL("DROP TABLE sender_keys"); + db.execSQL("ALTER TABLE sender_keys_tmp RENAME TO sender_keys"); + + Log.d(TAG, "Sender key migration took " + (System.currentTimeMillis() - start) + " ms"); + } + db.setTransactionSuccessful(); } finally { db.endTransaction();