diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/GroupManager.java b/app/src/main/java/org/thoughtcrime/securesms/groups/GroupManager.java index 5b16323c3b..9d981ce8f9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/GroupManager.java +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/GroupManager.java @@ -9,6 +9,7 @@ import androidx.annotation.WorkerThread; import org.signal.core.util.logging.Log; import org.signal.libsignal.zkgroup.VerificationFailedException; import org.signal.libsignal.zkgroup.groups.GroupMasterKey; +import org.signal.libsignal.zkgroup.groups.GroupSecretParams; import org.signal.libsignal.zkgroup.groups.UuidCiphertext; import org.signal.storageservice.protos.groups.GroupExternalCredential; import org.signal.storageservice.protos.groups.local.DecryptedGroup; @@ -185,9 +186,21 @@ public final class GroupManager { long timestamp, @Nullable byte[] signedGroupChange) throws GroupChangeBusyException, IOException, GroupNotAMemberException + { + return updateGroupFromServer(context, groupMasterKey, null, revision, timestamp, signedGroupChange); + } + + @WorkerThread + public static GroupsV2StateProcessor.GroupUpdateResult updateGroupFromServer(@NonNull Context context, + @NonNull GroupMasterKey groupMasterKey, + @Nullable GroupSecretParams groupSecretParams, + int revision, + long timestamp, + @Nullable byte[] signedGroupChange) + throws GroupChangeBusyException, IOException, GroupNotAMemberException { try (GroupManagerV2.GroupUpdater updater = new GroupManagerV2(context).updater(groupMasterKey)) { - return updater.updateLocalToServerRevision(revision, timestamp, signedGroupChange); + return updater.updateLocalToServerRevision(revision, timestamp, groupSecretParams, signedGroupChange); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/GroupManagerV2.java b/app/src/main/java/org/thoughtcrime/securesms/groups/GroupManagerV2.java index cd30d80518..e2d772f7b4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/GroupManagerV2.java +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/GroupManagerV2.java @@ -797,10 +797,10 @@ final class GroupManagerV2 { } @WorkerThread - GroupsV2StateProcessor.GroupUpdateResult updateLocalToServerRevision(int revision, long timestamp, @Nullable byte[] signedGroupChange) + GroupsV2StateProcessor.GroupUpdateResult updateLocalToServerRevision(int revision, long timestamp, @Nullable GroupSecretParams groupSecretParams, @Nullable byte[] signedGroupChange) throws IOException, GroupNotAMemberException { - return new GroupsV2StateProcessor(context).forGroup(serviceIds, groupMasterKey) + return new GroupsV2StateProcessor(context).forGroup(serviceIds, groupMasterKey, groupSecretParams) .updateLocalGroupToRevision(revision, timestamp, getDecryptedGroupChange(signedGroupChange)); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/v2/processing/GroupsV2StateProcessor.java b/app/src/main/java/org/thoughtcrime/securesms/groups/v2/processing/GroupsV2StateProcessor.java index 82b8b66365..98ecb1fb32 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/v2/processing/GroupsV2StateProcessor.java +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/v2/processing/GroupsV2StateProcessor.java @@ -107,9 +107,15 @@ public class GroupsV2StateProcessor { } public StateProcessorForGroup forGroup(@NonNull ServiceIds serviceIds, @NonNull GroupMasterKey groupMasterKey) { - ProfileAndMessageHelper profileAndMessageHelper = new ProfileAndMessageHelper(context, serviceIds.getAci(), groupMasterKey, GroupId.v2(groupMasterKey), recipientTable); + return forGroup(serviceIds, groupMasterKey, null); + } - return new StateProcessorForGroup(serviceIds, context, groupDatabase, groupsV2Api, groupsV2Authorization, groupMasterKey, profileAndMessageHelper); + public StateProcessorForGroup forGroup(@NonNull ServiceIds serviceIds, @NonNull GroupMasterKey groupMasterKey, @Nullable GroupSecretParams groupSecretParams) { + if (groupSecretParams == null) { + return new StateProcessorForGroup(serviceIds, context, groupDatabase, groupsV2Api, groupsV2Authorization, groupMasterKey, recipientTable); + } else { + return new StateProcessorForGroup(serviceIds, context, groupDatabase, groupsV2Api, groupsV2Authorization, groupMasterKey, groupSecretParams, recipientTable); + } } public enum GroupState { @@ -153,6 +159,37 @@ public class GroupsV2StateProcessor { private final GroupSecretParams groupSecretParams; private final ProfileAndMessageHelper profileAndMessageHelper; + private StateProcessorForGroup(@NonNull ServiceIds serviceIds, + @NonNull Context context, + @NonNull GroupTable groupDatabase, + @NonNull GroupsV2Api groupsV2Api, + @NonNull GroupsV2Authorization groupsV2Authorization, + @NonNull GroupMasterKey groupMasterKey, + @NonNull RecipientTable recipientTable) + { + this(serviceIds, context, groupDatabase, groupsV2Api, groupsV2Authorization, groupMasterKey, GroupSecretParams.deriveFromMasterKey(groupMasterKey), recipientTable); + } + + private StateProcessorForGroup(@NonNull ServiceIds serviceIds, + @NonNull Context context, + @NonNull GroupTable groupDatabase, + @NonNull GroupsV2Api groupsV2Api, + @NonNull GroupsV2Authorization groupsV2Authorization, + @NonNull GroupMasterKey groupMasterKey, + @NonNull GroupSecretParams groupSecretParams, + @NonNull RecipientTable recipientTable) + { + this.serviceIds = serviceIds; + this.context = context; + this.groupDatabase = groupDatabase; + this.groupsV2Api = groupsV2Api; + this.groupsV2Authorization = groupsV2Authorization; + this.masterKey = groupMasterKey; + this.groupSecretParams = groupSecretParams; + this.groupId = GroupId.v2(groupSecretParams.getPublicParams().getGroupIdentifier()); + this.profileAndMessageHelper = new ProfileAndMessageHelper(context, serviceIds.getAci(), groupMasterKey, groupId, recipientTable); + } + @VisibleForTesting StateProcessorForGroup(@NonNull ServiceIds serviceIds, @NonNull Context context, @NonNull GroupTable groupDatabase, @@ -167,8 +204,8 @@ public class GroupsV2StateProcessor { this.groupsV2Api = groupsV2Api; this.groupsV2Authorization = groupsV2Authorization; this.masterKey = groupMasterKey; - this.groupId = GroupId.v2(masterKey); this.groupSecretParams = GroupSecretParams.deriveFromMasterKey(groupMasterKey); + this.groupId = GroupId.v2(groupSecretParams.getPublicParams().getGroupIdentifier()); this.profileAndMessageHelper = profileAndMessageHelper; } diff --git a/app/src/main/java/org/thoughtcrime/securesms/messages/DataMessageProcessor.kt b/app/src/main/java/org/thoughtcrime/securesms/messages/DataMessageProcessor.kt index e4ab171358..35bf8b0827 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/messages/DataMessageProcessor.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/messages/DataMessageProcessor.kt @@ -9,6 +9,7 @@ import org.signal.core.util.concurrent.SignalExecutors import org.signal.core.util.logging.Log import org.signal.core.util.orNull import org.signal.core.util.toOptional +import org.signal.libsignal.zkgroup.groups.GroupSecretParams import org.signal.libsignal.zkgroup.receipts.ReceiptCredentialPresentation import org.thoughtcrime.securesms.attachments.Attachment import org.thoughtcrime.securesms.attachments.DatabaseAttachment @@ -120,11 +121,12 @@ object DataMessageProcessor { earlyMessageCacheEntry: EarlyMessageCacheEntry? ) { val message: DataMessage = content.dataMessage - val groupId: GroupId.V2? = if (message.hasGroupContext) GroupId.v2(message.groupV2.groupMasterKey) else null + val groupSecretParams = if (message.hasGroupContext) GroupSecretParams.deriveFromMasterKey(message.groupV2.groupMasterKey) else null + val groupId: GroupId.V2? = if (groupSecretParams != null) GroupId.v2(groupSecretParams.publicParams.groupIdentifier) else null var groupProcessResult: MessageContentProcessorV2.Gv2PreProcessResult? = null if (groupId != null) { - groupProcessResult = MessageContentProcessorV2.handleGv2PreProcessing(context, envelope.timestamp, content, metadata, groupId, message.groupV2, senderRecipient) + groupProcessResult = MessageContentProcessorV2.handleGv2PreProcessing(context, envelope.timestamp, content, metadata, groupId, message.groupV2, senderRecipient, groupSecretParams) if (groupProcessResult == MessageContentProcessorV2.Gv2PreProcessResult.IGNORE) { return } diff --git a/app/src/main/java/org/thoughtcrime/securesms/messages/MessageContentProcessorV2.kt b/app/src/main/java/org/thoughtcrime/securesms/messages/MessageContentProcessorV2.kt index 206b4ac81b..25b5d95899 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/messages/MessageContentProcessorV2.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/messages/MessageContentProcessorV2.kt @@ -6,6 +6,7 @@ import org.signal.core.util.orNull import org.signal.libsignal.protocol.SignalProtocolAddress import org.signal.libsignal.protocol.ecc.ECPublicKey import org.signal.libsignal.protocol.message.DecryptionErrorMessage +import org.signal.libsignal.zkgroup.groups.GroupSecretParams import org.thoughtcrime.securesms.database.SignalDatabase import org.thoughtcrime.securesms.database.model.MessageLogEntry import org.thoughtcrime.securesms.database.model.MessageRecord @@ -219,7 +220,8 @@ open class MessageContentProcessorV2(private val context: Context) { metadata: EnvelopeMetadata, groupId: GroupId.V2, groupV2: SignalServiceProtos.GroupContextV2, - senderRecipient: Recipient + senderRecipient: Recipient, + groupSecretParams: GroupSecretParams? = null ): Gv2PreProcessResult { val possibleV1OrV2Group = SignalDatabase.groups.getGroupV1OrV2ByExpectedV2(groupId) val needsV1Migration = possibleV1OrV2Group.isPresent && possibleV1OrV2Group.get().isV1Group @@ -227,7 +229,7 @@ open class MessageContentProcessorV2(private val context: Context) { GroupsV1MigrationUtil.performLocalMigration(context, possibleV1OrV2Group.get().id.requireV1()) } - val groupUpdateResult = updateGv2GroupFromServerOrP2PChange(context, timestamp, groupV2) + val groupUpdateResult = updateGv2GroupFromServerOrP2PChange(context, timestamp, groupV2, groupSecretParams) if (groupUpdateResult == null) { log(timestamp, "Ignoring GV2 message for group we are not currently in $groupId") return Gv2PreProcessResult.IGNORE @@ -266,12 +268,13 @@ open class MessageContentProcessorV2(private val context: Context) { fun updateGv2GroupFromServerOrP2PChange( context: Context, timestamp: Long, - groupV2: SignalServiceProtos.GroupContextV2 + groupV2: SignalServiceProtos.GroupContextV2, + groupSecretParams: GroupSecretParams? = null ): GroupsV2StateProcessor.GroupUpdateResult? { return try { val signedGroupChange: ByteArray? = if (groupV2.hasSignedGroupChange) groupV2.signedGroupChange else null val updatedTimestamp = if (signedGroupChange != null) timestamp else timestamp - 1 - GroupManager.updateGroupFromServer(context, groupV2.groupMasterKey, groupV2.revision, updatedTimestamp, signedGroupChange) + GroupManager.updateGroupFromServer(context, groupV2.groupMasterKey, groupSecretParams, groupV2.revision, updatedTimestamp, signedGroupChange) } catch (e: GroupNotAMemberException) { warn(timestamp, "Ignoring message for a group we're not in") null