X-Git-Url: https://git.nmode.ca/signal-cli/blobdiff_plain/3643d57d0eaef73d1aa375ad95e97b4cc2dcc12e..81ee2c2d2bb80660d85cf9d1b7ecdf001983b3d6:/lib/src/main/java/org/asamk/signal/manager/Manager.java diff --git a/lib/src/main/java/org/asamk/signal/manager/Manager.java b/lib/src/main/java/org/asamk/signal/manager/Manager.java index 4abe1260..f285e2ef 100644 --- a/lib/src/main/java/org/asamk/signal/manager/Manager.java +++ b/lib/src/main/java/org/asamk/signal/manager/Manager.java @@ -23,10 +23,13 @@ import org.asamk.signal.manager.config.ServiceEnvironmentConfig; import org.asamk.signal.manager.groups.GroupId; import org.asamk.signal.manager.groups.GroupIdV1; import org.asamk.signal.manager.groups.GroupInviteLinkUrl; +import org.asamk.signal.manager.groups.GroupLinkState; import org.asamk.signal.manager.groups.GroupNotFoundException; +import org.asamk.signal.manager.groups.GroupPermission; import org.asamk.signal.manager.groups.GroupUtils; +import org.asamk.signal.manager.groups.LastGroupAdminException; import org.asamk.signal.manager.groups.NotAGroupMemberException; -import org.asamk.signal.manager.helper.GroupHelper; +import org.asamk.signal.manager.helper.GroupV2Helper; import org.asamk.signal.manager.helper.PinHelper; import org.asamk.signal.manager.helper.ProfileHelper; import org.asamk.signal.manager.helper.UnidentifiedAccessHelper; @@ -83,6 +86,7 @@ import org.whispersystems.signalservice.api.SignalServiceAccountManager; import org.whispersystems.signalservice.api.SignalServiceMessagePipe; import org.whispersystems.signalservice.api.SignalServiceMessageReceiver; import org.whispersystems.signalservice.api.SignalServiceMessageSender; +import org.whispersystems.signalservice.api.SignalSessionLock; import org.whispersystems.signalservice.api.crypto.SignalServiceCipher; import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException; import org.whispersystems.signalservice.api.groupsv2.ClientZkOperations; @@ -161,6 +165,7 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import java.util.concurrent.locks.ReentrantLock; import java.util.function.Function; import java.util.stream.Collectors; @@ -189,10 +194,19 @@ public class Manager implements Closeable { private final UnidentifiedAccessHelper unidentifiedAccessHelper; private final ProfileHelper profileHelper; - private final GroupHelper groupHelper; + private final GroupV2Helper groupV2Helper; private final PinHelper pinHelper; private final AvatarStore avatarStore; private final AttachmentStore attachmentStore; + private final SignalSessionLock sessionLock = new SignalSessionLock() { + private final ReentrantLock LEGACY_LOCK = new ReentrantLock(); + + @Override + public Lock acquire() { + LEGACY_LOCK.lock(); + return LEGACY_LOCK::unlock; + } + }; Manager( SignalAccount account, @@ -248,7 +262,7 @@ public class Manager implements Closeable { unidentified -> unidentified ? getOrCreateUnidentifiedMessagePipe() : getOrCreateMessagePipe(), () -> messageReceiver, this::resolveSignalServiceAddress); - this.groupHelper = new GroupHelper(this::getRecipientProfileKeyCredential, + this.groupV2Helper = new GroupV2Helper(this::getRecipientProfileKeyCredential, this::getRecipientProfile, account::getSelfRecipientId, groupsV2Operations, @@ -389,6 +403,7 @@ public class Manager implements Closeable { newProfile.getInternalServiceName(), newProfile.getAbout() == null ? "" : newProfile.getAbout(), newProfile.getAboutEmoji() == null ? "" : newProfile.getAboutEmoji(), + Optional.absent(), streamDetails); } @@ -534,6 +549,7 @@ public class Manager implements Closeable { account.getPassword(), account.getDeviceId(), account.getSignalProtocolStore(), + sessionLock, userAgent, account.isMultiDevice(), Optional.fromNullable(messagePipe), @@ -556,14 +572,6 @@ public class Manager implements Closeable { Profile getRecipientProfile( RecipientId recipientId, boolean force ) { - var profileKey = account.getProfileStore().getProfileKey(recipientId); - if (profileKey == null) { - if (force) { - // retrieve profile to get identity key - retrieveEncryptedProfile(recipientId); - } - return null; - } var profile = account.getProfileStore().getProfile(recipientId); var now = new Date().getTime(); @@ -590,7 +598,18 @@ public class Manager implements Closeable { return null; } - profile = decryptProfileAndDownloadAvatar(recipientId, profileKey, encryptedProfile); + var profileKey = account.getProfileStore().getProfileKey(recipientId); + if (profileKey == null) { + profile = new Profile(new Date().getTime(), + null, + null, + null, + null, + ProfileUtils.getUnidentifiedAccessMode(encryptedProfile, null), + ProfileUtils.getCapabilities(encryptedProfile)); + } else { + profile = decryptProfileAndDownloadAvatar(recipientId, profileKey, encryptedProfile); + } account.getProfileStore().storeProfile(recipientId, profile); return profile; @@ -745,7 +764,9 @@ public class Manager implements Closeable { return sendMessage(messageBuilder, g.getMembersWithout(account.getSelfRecipientId())); } - public Pair> sendQuitGroupMessage(GroupId groupId) throws GroupNotFoundException, IOException, NotAGroupMemberException { + public Pair> sendQuitGroupMessage( + GroupId groupId, Set groupAdmins + ) throws GroupNotFoundException, IOException, NotAGroupMemberException, InvalidNumberException, LastGroupAdminException { SignalServiceDataMessage.Builder messageBuilder; final var g = getGroupForUpdating(groupId); @@ -757,7 +778,18 @@ public class Manager implements Closeable { account.getGroupStore().updateGroup(groupInfoV1); } else { final var groupInfoV2 = (GroupInfoV2) g; - final var groupGroupChangePair = groupHelper.leaveGroup(groupInfoV2); + final var currentAdmins = g.getAdminMembers(); + final var newAdmins = getSignalServiceAddresses(groupAdmins); + newAdmins.removeAll(currentAdmins); + newAdmins.retainAll(g.getMembers()); + if (currentAdmins.contains(getSelfRecipientId()) + && currentAdmins.size() == 1 + && g.getMembers().size() > 1 + && newAdmins.size() == 0) { + // Last admin can't leave the group, unless she's also the last member + throw new LastGroupAdminException(g.getGroupId(), g.getTitle()); + } + final var groupGroupChangePair = groupV2Helper.leaveGroup(groupInfoV2, newAdmins); groupInfoV2.setGroup(groupGroupChangePair.first(), this::resolveRecipient); messageBuilder = getGroupUpdateMessageBuilder(groupInfoV2, groupGroupChangePair.second().toByteArray()); account.getGroupStore().updateGroup(groupInfoV2); @@ -766,94 +798,132 @@ public class Manager implements Closeable { return sendMessage(messageBuilder, g.getMembersWithout(account.getSelfRecipientId())); } - public Pair> updateGroup( - GroupId groupId, String name, List members, File avatarFile - ) throws IOException, GroupNotFoundException, AttachmentInvalidException, InvalidNumberException, NotAGroupMemberException { - return sendUpdateGroupMessage(groupId, - name, - members == null ? null : getSignalServiceAddresses(members), - avatarFile); + public Pair> createGroup( + String name, List members, File avatarFile + ) throws IOException, AttachmentInvalidException, InvalidNumberException { + return createGroup(name, members == null ? null : getSignalServiceAddresses(members), avatarFile); } - private Pair> sendUpdateGroupMessage( - GroupId groupId, String name, Set members, File avatarFile - ) throws IOException, GroupNotFoundException, AttachmentInvalidException, NotAGroupMemberException { - GroupInfo g; - SignalServiceDataMessage.Builder messageBuilder; - if (groupId == null) { - // Create new group - var gv2Pair = groupHelper.createGroupV2(name == null ? "" : name, - members == null ? Set.of() : members, - avatarFile); - if (gv2Pair == null) { - var gv1 = new GroupInfoV1(GroupIdV1.createRandom()); - gv1.addMembers(List.of(account.getSelfRecipientId())); - updateGroupV1(gv1, name, members, avatarFile); - messageBuilder = getGroupUpdateMessageBuilder(gv1); - g = gv1; - } else { - final var gv2 = gv2Pair.first(); - final var decryptedGroup = gv2Pair.second(); + private Pair> createGroup( + String name, Set members, File avatarFile + ) throws IOException, AttachmentInvalidException { + final var selfRecipientId = account.getSelfRecipientId(); + if (members != null && members.contains(selfRecipientId)) { + members = new HashSet<>(members); + members.remove(selfRecipientId); + } - gv2.setGroup(decryptedGroup, this::resolveRecipient); - if (avatarFile != null) { - avatarStore.storeGroupAvatar(gv2.getGroupId(), - outputStream -> IOUtils.copyFileToStream(avatarFile, outputStream)); - } - messageBuilder = getGroupUpdateMessageBuilder(gv2, null); - g = gv2; - } - } else { - var group = getGroupForUpdating(groupId); - if (group instanceof GroupInfoV2) { - final var groupInfoV2 = (GroupInfoV2) group; - - Pair> result = null; - if (groupInfoV2.isPendingMember(account.getSelfRecipientId())) { - var groupGroupChangePair = groupHelper.acceptInvite(groupInfoV2); - result = sendUpdateGroupMessage(groupInfoV2, - groupGroupChangePair.first(), - groupGroupChangePair.second()); - } + var gv2Pair = groupV2Helper.createGroup(name == null ? "" : name, + members == null ? Set.of() : members, + avatarFile); - if (members != null) { - final var newMembers = new HashSet<>(members); - newMembers.removeAll(group.getMembers()); - if (newMembers.size() > 0) { - var groupGroupChangePair = groupHelper.updateGroupV2(groupInfoV2, newMembers); - result = sendUpdateGroupMessage(groupInfoV2, - groupGroupChangePair.first(), - groupGroupChangePair.second()); - } - } - if (result == null || name != null || avatarFile != null) { - var groupGroupChangePair = groupHelper.updateGroupV2(groupInfoV2, name, avatarFile); - if (avatarFile != null) { - avatarStore.storeGroupAvatar(groupInfoV2.getGroupId(), - outputStream -> IOUtils.copyFileToStream(avatarFile, outputStream)); - } - result = sendUpdateGroupMessage(groupInfoV2, - groupGroupChangePair.first(), - groupGroupChangePair.second()); - } + SignalServiceDataMessage.Builder messageBuilder; + if (gv2Pair == null) { + // Failed to create v2 group, creating v1 group instead + var gv1 = new GroupInfoV1(GroupIdV1.createRandom()); + gv1.addMembers(List.of(selfRecipientId)); + final var result = updateGroupV1(gv1, name, members, avatarFile); + return new Pair<>(gv1.getGroupId(), result.second()); + } - return new Pair<>(group.getGroupId(), result.second()); - } else { - var gv1 = (GroupInfoV1) group; - updateGroupV1(gv1, name, members, avatarFile); - messageBuilder = getGroupUpdateMessageBuilder(gv1); - g = gv1; - } + final var gv2 = gv2Pair.first(); + final var decryptedGroup = gv2Pair.second(); + + gv2.setGroup(decryptedGroup, this::resolveRecipient); + if (avatarFile != null) { + avatarStore.storeGroupAvatar(gv2.getGroupId(), + outputStream -> IOUtils.copyFileToStream(avatarFile, outputStream)); } + messageBuilder = getGroupUpdateMessageBuilder(gv2, null); + account.getGroupStore().updateGroup(gv2); + + final var result = sendMessage(messageBuilder, gv2.getMembersIncludingPendingWithout(selfRecipientId)); + return new Pair<>(gv2.getGroupId(), result.second()); + } + + public Pair> updateGroup( + GroupId groupId, + String name, + String description, + List members, + List removeMembers, + List admins, + List removeAdmins, + boolean resetGroupLink, + GroupLinkState groupLinkState, + GroupPermission addMemberPermission, + GroupPermission editDetailsPermission, + File avatarFile, + Integer expirationTimer + ) throws IOException, GroupNotFoundException, AttachmentInvalidException, InvalidNumberException, NotAGroupMemberException { + return updateGroup(groupId, + name, + description, + members == null ? null : getSignalServiceAddresses(members), + removeMembers == null ? null : getSignalServiceAddresses(removeMembers), + admins == null ? null : getSignalServiceAddresses(admins), + removeAdmins == null ? null : getSignalServiceAddresses(removeAdmins), + resetGroupLink, + groupLinkState, + addMemberPermission, + editDetailsPermission, + avatarFile, + expirationTimer); + } + + private Pair> updateGroup( + final GroupId groupId, + final String name, + final String description, + final Set members, + final Set removeMembers, + final Set admins, + final Set removeAdmins, + final boolean resetGroupLink, + final GroupLinkState groupLinkState, + final GroupPermission addMemberPermission, + final GroupPermission editDetailsPermission, + final File avatarFile, + final Integer expirationTimer + ) throws IOException, GroupNotFoundException, AttachmentInvalidException, NotAGroupMemberException { + var group = getGroupForUpdating(groupId); + + if (group instanceof GroupInfoV2) { + return updateGroupV2((GroupInfoV2) group, + name, + description, + members, + removeMembers, + admins, + removeAdmins, + resetGroupLink, + groupLinkState, + addMemberPermission, + editDetailsPermission, + avatarFile, + expirationTimer); + } + + final var gv1 = (GroupInfoV1) group; + final var result = updateGroupV1(gv1, name, members, avatarFile); + if (expirationTimer != null) { + setExpirationTimer(gv1, expirationTimer); + } + return result; + } + + private Pair> updateGroupV1( + final GroupInfoV1 gv1, final String name, final Set members, final File avatarFile + ) throws IOException, AttachmentInvalidException { + updateGroupV1Details(gv1, name, members, avatarFile); + var messageBuilder = getGroupUpdateMessageBuilder(gv1); - account.getGroupStore().updateGroup(g); + account.getGroupStore().updateGroup(gv1); - final var result = sendMessage(messageBuilder, - g.getMembersIncludingPendingWithout(account.getSelfRecipientId())); - return new Pair<>(g.getGroupId(), result.second()); + return sendMessage(messageBuilder, gv1.getMembersIncludingPendingWithout(account.getSelfRecipientId())); } - private void updateGroupV1( + private void updateGroupV1Details( final GroupInfoV1 g, final String name, final Collection members, final File avatarFile ) throws IOException { if (name != null) { @@ -891,18 +961,123 @@ public class Manager implements Closeable { } } - public Pair> joinGroup( - GroupInviteLinkUrl inviteLinkUrl - ) throws IOException, GroupLinkNotActiveException { - return sendJoinGroupMessage(inviteLinkUrl); + private Pair> updateGroupV2( + final GroupInfoV2 group, + final String name, + final String description, + final Set members, + final Set removeMembers, + final Set admins, + final Set removeAdmins, + final boolean resetGroupLink, + final GroupLinkState groupLinkState, + final GroupPermission addMemberPermission, + final GroupPermission editDetailsPermission, + final File avatarFile, + Integer expirationTimer + ) throws IOException { + Pair> result = null; + if (group.isPendingMember(account.getSelfRecipientId())) { + var groupGroupChangePair = groupV2Helper.acceptInvite(group); + result = sendUpdateGroupV2Message(group, groupGroupChangePair.first(), groupGroupChangePair.second()); + } + + if (members != null) { + final var newMembers = new HashSet<>(members); + newMembers.removeAll(group.getMembers()); + if (newMembers.size() > 0) { + var groupGroupChangePair = groupV2Helper.addMembers(group, newMembers); + result = sendUpdateGroupV2Message(group, groupGroupChangePair.first(), groupGroupChangePair.second()); + } + } + + if (removeMembers != null) { + var existingRemoveMembers = new HashSet<>(removeMembers); + existingRemoveMembers.retainAll(group.getMembers()); + existingRemoveMembers.remove(getSelfRecipientId());// self can be removed with sendQuitGroupMessage + if (existingRemoveMembers.size() > 0) { + var groupGroupChangePair = groupV2Helper.removeMembers(group, existingRemoveMembers); + result = sendUpdateGroupV2Message(group, groupGroupChangePair.first(), groupGroupChangePair.second()); + } + + var pendingRemoveMembers = new HashSet<>(removeMembers); + pendingRemoveMembers.retainAll(group.getPendingMembers()); + if (pendingRemoveMembers.size() > 0) { + var groupGroupChangePair = groupV2Helper.revokeInvitedMembers(group, pendingRemoveMembers); + result = sendUpdateGroupV2Message(group, groupGroupChangePair.first(), groupGroupChangePair.second()); + } + } + + if (admins != null) { + final var newAdmins = new HashSet<>(admins); + newAdmins.retainAll(group.getMembers()); + newAdmins.removeAll(group.getAdminMembers()); + if (newAdmins.size() > 0) { + for (var admin : newAdmins) { + var groupGroupChangePair = groupV2Helper.setMemberAdmin(group, admin, true); + result = sendUpdateGroupV2Message(group, + groupGroupChangePair.first(), + groupGroupChangePair.second()); + } + } + } + + if (removeAdmins != null) { + final var existingRemoveAdmins = new HashSet<>(removeAdmins); + existingRemoveAdmins.retainAll(group.getAdminMembers()); + if (existingRemoveAdmins.size() > 0) { + for (var admin : existingRemoveAdmins) { + var groupGroupChangePair = groupV2Helper.setMemberAdmin(group, admin, false); + result = sendUpdateGroupV2Message(group, + groupGroupChangePair.first(), + groupGroupChangePair.second()); + } + } + } + + if (resetGroupLink) { + var groupGroupChangePair = groupV2Helper.resetGroupLinkPassword(group); + result = sendUpdateGroupV2Message(group, groupGroupChangePair.first(), groupGroupChangePair.second()); + } + + if (groupLinkState != null) { + var groupGroupChangePair = groupV2Helper.setGroupLinkState(group, groupLinkState); + result = sendUpdateGroupV2Message(group, groupGroupChangePair.first(), groupGroupChangePair.second()); + } + + if (addMemberPermission != null) { + var groupGroupChangePair = groupV2Helper.setAddMemberPermission(group, addMemberPermission); + result = sendUpdateGroupV2Message(group, groupGroupChangePair.first(), groupGroupChangePair.second()); + } + + if (editDetailsPermission != null) { + var groupGroupChangePair = groupV2Helper.setEditDetailsPermission(group, editDetailsPermission); + result = sendUpdateGroupV2Message(group, groupGroupChangePair.first(), groupGroupChangePair.second()); + } + + if (expirationTimer != null) { + var groupGroupChangePair = groupV2Helper.setMessageExpirationTimer(group, expirationTimer); + result = sendUpdateGroupV2Message(group, groupGroupChangePair.first(), groupGroupChangePair.second()); + } + + if (name != null || description != null || avatarFile != null) { + var groupGroupChangePair = groupV2Helper.updateGroup(group, name, description, avatarFile); + if (avatarFile != null) { + avatarStore.storeGroupAvatar(group.getGroupId(), + outputStream -> IOUtils.copyFileToStream(avatarFile, outputStream)); + } + result = sendUpdateGroupV2Message(group, groupGroupChangePair.first(), groupGroupChangePair.second()); + } + + return result; } - private Pair> sendJoinGroupMessage( + public Pair> joinGroup( GroupInviteLinkUrl inviteLinkUrl ) throws IOException, GroupLinkNotActiveException { - final var groupJoinInfo = groupHelper.getDecryptedGroupJoinInfo(inviteLinkUrl.getGroupMasterKey(), + final var groupJoinInfo = groupV2Helper.getDecryptedGroupJoinInfo(inviteLinkUrl.getGroupMasterKey(), inviteLinkUrl.getPassword()); - final var groupChange = groupHelper.joinGroup(inviteLinkUrl.getGroupMasterKey(), + final var groupChange = groupV2Helper.joinGroup(inviteLinkUrl.getGroupMasterKey(), inviteLinkUrl.getPassword(), groupJoinInfo); final var group = getOrMigrateGroup(inviteLinkUrl.getGroupMasterKey(), @@ -914,11 +1089,24 @@ public class Manager implements Closeable { return new Pair<>(group.getGroupId(), List.of()); } - final var result = sendUpdateGroupMessage(group, group.getGroup(), groupChange); + final var result = sendUpdateGroupV2Message(group, group.getGroup(), groupChange); return new Pair<>(group.getGroupId(), result.second()); } + private Pair> sendUpdateGroupV2Message( + GroupInfoV2 group, DecryptedGroup newDecryptedGroup, GroupChange groupChange + ) throws IOException { + final var selfRecipientId = account.getSelfRecipientId(); + final var members = group.getMembersIncludingPendingWithout(selfRecipientId); + group.setGroup(newDecryptedGroup, this::resolveRecipient); + members.addAll(group.getMembersIncludingPendingWithout(selfRecipientId)); + + final var messageBuilder = getGroupUpdateMessageBuilder(group, groupChange.toByteArray()); + account.getGroupStore().updateGroup(group); + return sendMessage(messageBuilder, members); + } + private static int currentTimeDays() { return (int) TimeUnit.MILLISECONDS.toDays(System.currentTimeMillis()); } @@ -941,15 +1129,6 @@ public class Manager implements Closeable { } } - private Pair> sendUpdateGroupMessage( - GroupInfoV2 group, DecryptedGroup newDecryptedGroup, GroupChange groupChange - ) throws IOException { - group.setGroup(newDecryptedGroup, this::resolveRecipient); - final var messageBuilder = getGroupUpdateMessageBuilder(group, groupChange.toByteArray()); - account.getGroupStore().updateGroup(group); - return sendMessage(messageBuilder, group.getMembersIncludingPendingWithout(account.getSelfRecipientId())); - } - Pair> sendGroupInfoMessage( GroupIdV1 groupId, SignalServiceAddress recipient ) throws IOException, NotAGroupMemberException, GroupNotFoundException, AttachmentInvalidException { @@ -1101,9 +1280,11 @@ public class Manager implements Closeable { } } - SendMessageResult renewSession(RecipientId recipientId) throws IOException { + void renewSession(RecipientId recipientId) throws IOException { account.getSessionStore().archiveSessions(recipientId); - return sendNullMessage(recipientId); + if (!recipientId.equals(getSelfRecipientId())) { + sendNullMessage(recipientId); + } } public String getContactName(String number) throws InvalidNumberException { @@ -1175,15 +1356,17 @@ public class Manager implements Closeable { /** * Change the expiration timer for a group */ - public void setExpirationTimer(GroupId groupId, int messageExpirationTimer) { - var g = getGroup(groupId); - if (g instanceof GroupInfoV1) { - var groupInfoV1 = (GroupInfoV1) g; - groupInfoV1.messageExpirationTime = messageExpirationTimer; - account.getGroupStore().updateGroup(groupInfoV1); - } else { - throw new RuntimeException("TODO Not implemented!"); - } + private void setExpirationTimer( + GroupInfoV1 groupInfoV1, int messageExpirationTimer + ) throws NotAGroupMemberException, GroupNotFoundException, IOException { + groupInfoV1.messageExpirationTime = messageExpirationTimer; + account.getGroupStore().updateGroup(groupInfoV1); + sendExpirationTimerUpdate(groupInfoV1.getGroupId()); + } + + private void sendExpirationTimerUpdate(GroupIdV1 groupId) throws IOException, NotAGroupMemberException, GroupNotFoundException { + final var messageBuilder = SignalServiceDataMessage.newBuilder().asExpirationUpdate(); + sendGroupMessage(messageBuilder, groupId); } /** @@ -1505,18 +1688,12 @@ public class Manager implements Closeable { } } - private SignalServiceContent decryptMessage(SignalServiceEnvelope envelope) throws InvalidMetadataMessageException, ProtocolInvalidMessageException, ProtocolDuplicateMessageException, ProtocolLegacyMessageException, ProtocolInvalidKeyIdException, InvalidMetadataVersionException, ProtocolInvalidVersionException, ProtocolNoSessionException, ProtocolInvalidKeyException, SelfSendException, UnsupportedDataMessageException, org.whispersystems.libsignal.UntrustedIdentityException { + private SignalServiceContent decryptMessage(SignalServiceEnvelope envelope) throws InvalidMetadataMessageException, ProtocolInvalidMessageException, ProtocolDuplicateMessageException, ProtocolLegacyMessageException, ProtocolInvalidKeyIdException, InvalidMetadataVersionException, ProtocolInvalidVersionException, ProtocolNoSessionException, ProtocolInvalidKeyException, SelfSendException, UnsupportedDataMessageException, ProtocolUntrustedIdentityException { var cipher = new SignalServiceCipher(account.getSelfAddress(), account.getSignalProtocolStore(), + sessionLock, certificateValidator); - try { - return cipher.decrypt(envelope); - } catch (ProtocolUntrustedIdentityException e) { - if (e.getCause() instanceof org.whispersystems.libsignal.UntrustedIdentityException) { - throw (org.whispersystems.libsignal.UntrustedIdentityException) e.getCause(); - } - throw new AssertionError(e); - } + return cipher.decrypt(envelope); } private void handleEndSession(RecipientId recipientId) { @@ -1700,10 +1877,12 @@ public class Manager implements Closeable { if (signedGroupChange != null && groupInfoV2.getGroup() != null && groupInfoV2.getGroup().getRevision() + 1 == revision) { - group = groupHelper.getUpdatedDecryptedGroup(groupInfoV2.getGroup(), signedGroupChange, groupMasterKey); + group = groupV2Helper.getUpdatedDecryptedGroup(groupInfoV2.getGroup(), + signedGroupChange, + groupMasterKey); } if (group == null) { - group = groupHelper.getDecryptedGroup(groupSecretParams); + group = groupV2Helper.getDecryptedGroup(groupSecretParams); } if (group != null) { storeProfileKeysFromMembers(group); @@ -1760,9 +1939,9 @@ public class Manager implements Closeable { if (!envelope.isReceipt()) { try { content = decryptMessage(envelope); - } catch (org.whispersystems.libsignal.UntrustedIdentityException e) { + } catch (ProtocolUntrustedIdentityException e) { if (!envelope.hasSource()) { - final var identifier = ((org.whispersystems.libsignal.UntrustedIdentityException) e).getName(); + final var identifier = e.getSender(); final var recipientId = resolveRecipient(identifier); try { account.getMessageCache().replaceSender(cachedMessage, recipientId); @@ -1839,6 +2018,7 @@ public class Manager implements Closeable { if (envelope.hasSource()) { // Store uuid if we don't have it already + // address/uuid in envelope is sent by server resolveRecipientTrusted(envelope.getSourceAddress()); } final var notAGroupMember = isNotAGroupMember(envelope, content); @@ -1848,6 +2028,11 @@ public class Manager implements Closeable { } catch (Exception e) { exception = e; } + if (!envelope.hasSource() && content != null) { + // Store uuid if we don't have it already + // address/uuid is validated by unidentified sender certificate + resolveRecipientTrusted(content.getSender()); + } var actions = handleMessage(envelope, content, ignoreAttachments); if (exception instanceof ProtocolInvalidMessageException) { final var sender = resolveRecipient(((ProtocolInvalidMessageException) exception).getSender()); @@ -1877,8 +2062,8 @@ public class Manager implements Closeable { handler.handleMessage(envelope, content, exception); } if (cachedMessage[0] != null) { - if (exception instanceof org.whispersystems.libsignal.UntrustedIdentityException) { - final var identifier = ((org.whispersystems.libsignal.UntrustedIdentityException) exception).getName(); + if (exception instanceof ProtocolUntrustedIdentityException) { + final var identifier = ((ProtocolUntrustedIdentityException) exception).getSender(); final var recipientId = resolveRecipient(identifier); queuedActions.add(new RetrieveProfileAction(recipientId)); if (!envelope.hasSource()) { @@ -2509,7 +2694,7 @@ public class Manager implements Closeable { final var group = account.getGroupStore().getGroup(groupId); if (group instanceof GroupInfoV2 && ((GroupInfoV2) group).getGroup() == null) { final var groupSecretParams = GroupSecretParams.deriveFromMasterKey(((GroupInfoV2) group).getMasterKey()); - ((GroupInfoV2) group).setGroup(groupHelper.getDecryptedGroup(groupSecretParams), this::resolveRecipient); + ((GroupInfoV2) group).setGroup(groupV2Helper.getDecryptedGroup(groupSecretParams), this::resolveRecipient); account.getGroupStore().updateGroup(group); } return group;