From e51b1ee23a725ce8cfc25f0a277ab6065a532081 Mon Sep 17 00:00:00 2001 From: AsamK Date: Thu, 25 Jul 2024 16:24:41 +0200 Subject: [PATCH] Update libsignal-service --- graalvm-config-dir/jni-config.json | 6 + .../manager/api/ProofRequiredException.java | 1 - .../signal/manager/config/ServiceConfig.java | 13 +- .../signal/manager/helper/GroupHelper.java | 131 +++++++++++++----- .../signal/manager/helper/GroupV2Helper.java | 80 ++++++----- .../signal/manager/helper/ProfileHelper.java | 15 +- .../signal/manager/helper/SendHelper.java | 34 +++-- .../helper/UnidentifiedAccessHelper.java | 54 +++----- settings.gradle.kts | 2 +- .../java/org/asamk/signal/BaseConfig.java | 2 +- 10 files changed, 201 insertions(+), 137 deletions(-) diff --git a/graalvm-config-dir/jni-config.json b/graalvm-config-dir/jni-config.json index 4050838c..381dca9e 100644 --- a/graalvm-config-dir/jni-config.json +++ b/graalvm-config-dir/jni-config.json @@ -1,7 +1,13 @@ [ +{ + "name":"[B" +}, { "name":"[Z" }, +{ + "name":"[[B" +}, { "name":"com.sun.security.auth.module.UnixSystem", "fields":[{"name":"gid"}, {"name":"groups"}, {"name":"uid"}, {"name":"username"}] diff --git a/lib/src/main/java/org/asamk/signal/manager/api/ProofRequiredException.java b/lib/src/main/java/org/asamk/signal/manager/api/ProofRequiredException.java index 277406cb..21b4034d 100644 --- a/lib/src/main/java/org/asamk/signal/manager/api/ProofRequiredException.java +++ b/lib/src/main/java/org/asamk/signal/manager/api/ProofRequiredException.java @@ -36,7 +36,6 @@ public class ProofRequiredException extends Exception { static Option from(org.whispersystems.signalservice.api.push.exceptions.ProofRequiredException.Option option) { return switch (option) { - case RECAPTCHA -> CAPTCHA; case CAPTCHA -> CAPTCHA; case PUSH_CHALLENGE -> PUSH_CHALLENGE; }; diff --git a/lib/src/main/java/org/asamk/signal/manager/config/ServiceConfig.java b/lib/src/main/java/org/asamk/signal/manager/config/ServiceConfig.java index e827e3f5..62aedaf6 100644 --- a/lib/src/main/java/org/asamk/signal/manager/config/ServiceConfig.java +++ b/lib/src/main/java/org/asamk/signal/manager/config/ServiceConfig.java @@ -27,19 +27,8 @@ public class ServiceConfig { public static final long UNREGISTERED_LIFESPAN = TimeUnit.DAYS.toMillis(30); public static AccountAttributes.Capabilities getCapabilities(boolean isPrimaryDevice) { - final var giftBadges = !isPrimaryDevice; - final var pni = !isPrimaryDevice; - final var paymentActivation = !isPrimaryDevice; final var deleteSync = !isPrimaryDevice; - return new AccountAttributes.Capabilities(true, - true, - true, - true, - true, - giftBadges, - pni, - paymentActivation, - deleteSync); + return new AccountAttributes.Capabilities(true, deleteSync); } public static ServiceEnvironmentConfig getServiceEnvironmentConfig( diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/GroupHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/GroupHelper.java index 7cc9fef8..62291a3a 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/GroupHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/GroupHelper.java @@ -33,13 +33,16 @@ import org.signal.libsignal.zkgroup.groups.GroupMasterKey; import org.signal.libsignal.zkgroup.groups.GroupSecretParams; import org.signal.libsignal.zkgroup.profiles.ProfileKey; import org.signal.storageservice.protos.groups.GroupChange; +import org.signal.storageservice.protos.groups.GroupChangeResponse; import org.signal.storageservice.protos.groups.local.DecryptedGroup; import org.signal.storageservice.protos.groups.local.DecryptedGroupChange; import org.signal.storageservice.protos.groups.local.DecryptedGroupJoinInfo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.whispersystems.signalservice.api.groupsv2.DecryptedGroupChangeLog; +import org.whispersystems.signalservice.api.groupsv2.DecryptedGroupResponse; import org.whispersystems.signalservice.api.groupsv2.GroupLinkNotActiveException; +import org.whispersystems.signalservice.api.groupsv2.ReceivedGroupSendEndorsements; import org.whispersystems.signalservice.api.messages.SignalServiceAttachment; import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentStream; import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage; @@ -133,9 +136,10 @@ public class GroupHelper { } if (group == null) { try { - group = context.getGroupV2Helper().getDecryptedGroup(groupSecretParams); + final var response = context.getGroupV2Helper().getDecryptedGroup(groupSecretParams); - if (group != null) { + if (response != null) { + group = handleDecryptedGroupResponse(groupInfoV2, response); storeProfileKeysFromHistory(groupSecretParams, groupInfoV2, group); } } catch (NotAGroupMemberException ignored) { @@ -156,6 +160,35 @@ public class GroupHelper { return groupInfoV2; } + private DecryptedGroup handleDecryptedGroupResponse( + GroupInfoV2 groupInfoV2, final DecryptedGroupResponse decryptedGroupResponse + ) { + final var groupSecretParams = GroupSecretParams.deriveFromMasterKey(groupInfoV2.getMasterKey()); + ReceivedGroupSendEndorsements groupSendEndorsements = dependencies.getGroupsV2Operations() + .forGroup(groupSecretParams) + .receiveGroupSendEndorsements(account.getAci(), + decryptedGroupResponse.getGroup(), + decryptedGroupResponse.getGroupSendEndorsementsResponse()); + + // TODO save group endorsements + + return decryptedGroupResponse.getGroup(); + } + + private GroupChange handleGroupChangeResponse( + final GroupInfoV2 groupInfoV2, final GroupChangeResponse groupChangeResponse + ) { + ReceivedGroupSendEndorsements groupSendEndorsements = dependencies.getGroupsV2Operations() + .forGroup(GroupSecretParams.deriveFromMasterKey(groupInfoV2.getMasterKey())) + .receiveGroupSendEndorsements(account.getAci(), + groupInfoV2.getGroup(), + groupChangeResponse.groupSendEndorsementsResponse); + + // TODO save group endorsements + + return groupChangeResponse.groupChange; + } + public Pair createGroup( String name, Set members, String avatarFile ) throws IOException, AttachmentInvalidException { @@ -181,7 +214,7 @@ public class GroupHelper { final var gv2 = gv2Pair.first(); final var decryptedGroup = gv2Pair.second(); - gv2.setGroup(decryptedGroup); + gv2.setGroup(handleDecryptedGroupResponse(gv2, decryptedGroup)); gv2.setProfileSharingEnabled(true); if (avatarBytes != null) { context.getAvatarStore() @@ -277,7 +310,7 @@ public class GroupHelper { var group = getGroupForUpdating(groupId); if (group instanceof GroupInfoV2 groupInfoV2) { - Pair groupChangePair; + Pair groupChangePair; try { groupChangePair = context.getGroupV2Helper().updateSelfProfileKey(groupInfoV2); } catch (ConflictException e) { @@ -286,7 +319,9 @@ public class GroupHelper { groupChangePair = context.getGroupV2Helper().updateSelfProfileKey(groupInfoV2); } if (groupChangePair != null) { - sendUpdateGroupV2Message(groupInfoV2, groupChangePair.first(), groupChangePair.second()); + sendUpdateGroupV2Message(groupInfoV2, + groupChangePair.first(), + handleGroupChangeResponse(groupInfoV2, groupChangePair.second())); } } } @@ -304,11 +339,12 @@ public class GroupHelper { if (groupJoinInfo.pendingAdminApproval) { throw new PendingAdminApprovalException("You have already requested to join the group."); } - final var groupChange = context.getGroupV2Helper() + final var changeResponse = context.getGroupV2Helper() .joinGroup(inviteLinkUrl.getGroupMasterKey(), inviteLinkUrl.getPassword(), groupJoinInfo); final var group = getOrMigrateGroup(inviteLinkUrl.getGroupMasterKey(), groupJoinInfo.revision + 1, - groupChange.encode()); + changeResponse.groupChange == null ? null : changeResponse.groupChange.encode()); + final var groupChange = handleGroupChangeResponse(group, changeResponse); if (group.getGroup() == null) { // Only requested member, can't send update to group members @@ -404,17 +440,17 @@ public class GroupHelper { final var groupSecretParams = GroupSecretParams.deriveFromMasterKey(groupInfoV2.getMasterKey()); DecryptedGroup decryptedGroup; try { - decryptedGroup = context.getGroupV2Helper().getDecryptedGroup(groupSecretParams); + final var response = context.getGroupV2Helper().getDecryptedGroup(groupSecretParams); + if (response == null) { + return; + } + decryptedGroup = handleDecryptedGroupResponse(groupInfoV2, response); } catch (NotAGroupMemberException e) { groupInfoV2.setPermissionDenied(true); account.getGroupStore().updateGroup(group); return; } - if (decryptedGroup == null) { - return; - } - try { storeProfileKeysFromHistory(groupSecretParams, groupInfoV2, decryptedGroup); } catch (NotAGroupMemberException ignored) { @@ -496,10 +532,12 @@ public class GroupHelper { ) throws NotAGroupMemberException { final var revisionWeWereAdded = context.getGroupV2Helper().findRevisionWeWereAdded(newDecryptedGroup); final var localRevision = localGroup.getGroup() == null ? 0 : localGroup.getGroup().revision; + final var sendEndorsementsExpirationMs = 0L;// TODO store expiration localGroup.getGroup() == null ? 0 : localGroup.getGroup().revision; var fromRevision = Math.max(revisionWeWereAdded, localRevision); final var newProfileKeys = new HashMap(); while (true) { - final var page = context.getGroupV2Helper().getDecryptedGroupHistoryPage(groupSecretParams, fromRevision); + final var page = context.getGroupV2Helper() + .getDecryptedGroupHistoryPage(groupSecretParams, fromRevision, sendEndorsementsExpirationMs); page.getChangeLogs() .stream() .map(DecryptedGroupChangeLog::getChange) @@ -606,7 +644,9 @@ public class GroupHelper { final var groupV2Helper = context.getGroupV2Helper(); if (group.isPendingMember(account.getSelfRecipientId())) { var groupGroupChangePair = groupV2Helper.acceptInvite(group); - result = sendUpdateGroupV2Message(group, groupGroupChangePair.first(), groupGroupChangePair.second()); + result = sendUpdateGroupV2Message(group, + groupGroupChangePair.first(), + handleGroupChangeResponse(group, groupGroupChangePair.second())); } if (members != null) { @@ -614,14 +654,18 @@ public class GroupHelper { requestingMembers.retainAll(group.getRequestingMembers()); if (!requestingMembers.isEmpty()) { var groupGroupChangePair = groupV2Helper.approveJoinRequestMembers(group, requestingMembers); - result = sendUpdateGroupV2Message(group, groupGroupChangePair.first(), groupGroupChangePair.second()); + result = sendUpdateGroupV2Message(group, + groupGroupChangePair.first(), + handleGroupChangeResponse(group, groupGroupChangePair.second())); } final var newMembers = new HashSet<>(members); newMembers.removeAll(group.getMembers()); newMembers.removeAll(group.getRequestingMembers()); if (!newMembers.isEmpty()) { var groupGroupChangePair = groupV2Helper.addMembers(group, newMembers); - result = sendUpdateGroupV2Message(group, groupGroupChangePair.first(), groupGroupChangePair.second()); + result = sendUpdateGroupV2Message(group, + groupGroupChangePair.first(), + handleGroupChangeResponse(group, groupGroupChangePair.second())); } } @@ -637,20 +681,26 @@ public class GroupHelper { existingRemoveMembers.remove(account.getSelfRecipientId());// self can be removed with sendQuitGroupMessage if (!existingRemoveMembers.isEmpty()) { var groupGroupChangePair = groupV2Helper.removeMembers(group, existingRemoveMembers); - result = sendUpdateGroupV2Message(group, groupGroupChangePair.first(), groupGroupChangePair.second()); + result = sendUpdateGroupV2Message(group, + groupGroupChangePair.first(), + handleGroupChangeResponse(group, groupGroupChangePair.second())); } var pendingRemoveMembers = new HashSet<>(removeMembers); pendingRemoveMembers.retainAll(group.getPendingMembers()); if (!pendingRemoveMembers.isEmpty()) { var groupGroupChangePair = groupV2Helper.revokeInvitedMembers(group, pendingRemoveMembers); - result = sendUpdateGroupV2Message(group, groupGroupChangePair.first(), groupGroupChangePair.second()); + result = sendUpdateGroupV2Message(group, + groupGroupChangePair.first(), + handleGroupChangeResponse(group, groupGroupChangePair.second())); } var requestingRemoveMembers = new HashSet<>(removeMembers); requestingRemoveMembers.retainAll(group.getRequestingMembers()); if (!requestingRemoveMembers.isEmpty()) { var groupGroupChangePair = groupV2Helper.refuseJoinRequestMembers(group, requestingRemoveMembers); - result = sendUpdateGroupV2Message(group, groupGroupChangePair.first(), groupGroupChangePair.second()); + result = sendUpdateGroupV2Message(group, + groupGroupChangePair.first(), + handleGroupChangeResponse(group, groupGroupChangePair.second())); } } @@ -663,7 +713,7 @@ public class GroupHelper { var groupGroupChangePair = groupV2Helper.setMemberAdmin(group, admin, true); result = sendUpdateGroupV2Message(group, groupGroupChangePair.first(), - groupGroupChangePair.second()); + handleGroupChangeResponse(group, groupGroupChangePair.second())); } } } @@ -676,7 +726,7 @@ public class GroupHelper { var groupGroupChangePair = groupV2Helper.setMemberAdmin(group, admin, false); result = sendUpdateGroupV2Message(group, groupGroupChangePair.first(), - groupGroupChangePair.second()); + handleGroupChangeResponse(group, groupGroupChangePair.second())); } } } @@ -686,7 +736,9 @@ public class GroupHelper { newlyBannedMembers.removeAll(group.getBannedMembers()); if (!newlyBannedMembers.isEmpty()) { var groupGroupChangePair = groupV2Helper.banMembers(group, newlyBannedMembers); - result = sendUpdateGroupV2Message(group, groupGroupChangePair.first(), groupGroupChangePair.second()); + result = sendUpdateGroupV2Message(group, + groupGroupChangePair.first(), + handleGroupChangeResponse(group, groupGroupChangePair.second())); } } @@ -695,38 +747,52 @@ public class GroupHelper { existingUnbanMembers.retainAll(group.getBannedMembers()); if (!existingUnbanMembers.isEmpty()) { var groupGroupChangePair = groupV2Helper.unbanMembers(group, existingUnbanMembers); - result = sendUpdateGroupV2Message(group, groupGroupChangePair.first(), groupGroupChangePair.second()); + result = sendUpdateGroupV2Message(group, + groupGroupChangePair.first(), + handleGroupChangeResponse(group, groupGroupChangePair.second())); } } if (resetGroupLink) { var groupGroupChangePair = groupV2Helper.resetGroupLinkPassword(group); - result = sendUpdateGroupV2Message(group, groupGroupChangePair.first(), groupGroupChangePair.second()); + result = sendUpdateGroupV2Message(group, + groupGroupChangePair.first(), + handleGroupChangeResponse(group, groupGroupChangePair.second())); } if (groupLinkState != null) { var groupGroupChangePair = groupV2Helper.setGroupLinkState(group, groupLinkState); - result = sendUpdateGroupV2Message(group, groupGroupChangePair.first(), groupGroupChangePair.second()); + result = sendUpdateGroupV2Message(group, + groupGroupChangePair.first(), + handleGroupChangeResponse(group, groupGroupChangePair.second())); } if (addMemberPermission != null) { var groupGroupChangePair = groupV2Helper.setAddMemberPermission(group, addMemberPermission); - result = sendUpdateGroupV2Message(group, groupGroupChangePair.first(), groupGroupChangePair.second()); + result = sendUpdateGroupV2Message(group, + groupGroupChangePair.first(), + handleGroupChangeResponse(group, groupGroupChangePair.second())); } if (editDetailsPermission != null) { var groupGroupChangePair = groupV2Helper.setEditDetailsPermission(group, editDetailsPermission); - result = sendUpdateGroupV2Message(group, groupGroupChangePair.first(), groupGroupChangePair.second()); + result = sendUpdateGroupV2Message(group, + groupGroupChangePair.first(), + handleGroupChangeResponse(group, groupGroupChangePair.second())); } if (expirationTimer != null) { var groupGroupChangePair = groupV2Helper.setMessageExpirationTimer(group, expirationTimer); - result = sendUpdateGroupV2Message(group, groupGroupChangePair.first(), groupGroupChangePair.second()); + result = sendUpdateGroupV2Message(group, + groupGroupChangePair.first(), + handleGroupChangeResponse(group, groupGroupChangePair.second())); } if (isAnnouncementGroup != null) { var groupGroupChangePair = groupV2Helper.setIsAnnouncementGroup(group, isAnnouncementGroup); - result = sendUpdateGroupV2Message(group, groupGroupChangePair.first(), groupGroupChangePair.second()); + result = sendUpdateGroupV2Message(group, + groupGroupChangePair.first(), + handleGroupChangeResponse(group, groupGroupChangePair.second())); } if (name != null || description != null || avatarFile != null) { @@ -735,7 +801,9 @@ public class GroupHelper { context.getAvatarStore() .storeGroupAvatar(group.getGroupId(), outputStream -> outputStream.write(avatarFile)); } - result = sendUpdateGroupV2Message(group, groupGroupChangePair.first(), groupGroupChangePair.second()); + result = sendUpdateGroupV2Message(group, + groupGroupChangePair.first(), + handleGroupChangeResponse(group, groupGroupChangePair.second())); } return result; @@ -771,7 +839,8 @@ public class GroupHelper { groupInfoV2.setGroup(groupGroupChangePair.first()); account.getGroupStore().updateGroup(groupInfoV2); - var messageBuilder = getGroupUpdateMessageBuilder(groupInfoV2, groupGroupChangePair.second().encode()); + var messageBuilder = getGroupUpdateMessageBuilder(groupInfoV2, + handleGroupChangeResponse(groupInfoV2, groupGroupChangePair.second()).encode()); return sendGroupMessage(messageBuilder, groupInfoV2.getMembersIncludingPendingWithout(account.getSelfRecipientId()), groupInfoV2.getDistributionId()); diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/GroupV2Helper.java b/lib/src/main/java/org/asamk/signal/manager/helper/GroupV2Helper.java index 83f49926..6c6acd65 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/GroupV2Helper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/GroupV2Helper.java @@ -19,6 +19,7 @@ import org.signal.libsignal.zkgroup.groups.UuidCiphertext; import org.signal.libsignal.zkgroup.profiles.ProfileKey; import org.signal.storageservice.protos.groups.AccessControl; import org.signal.storageservice.protos.groups.GroupChange; +import org.signal.storageservice.protos.groups.GroupChangeResponse; import org.signal.storageservice.protos.groups.Member; import org.signal.storageservice.protos.groups.local.DecryptedGroup; import org.signal.storageservice.protos.groups.local.DecryptedGroupChange; @@ -27,6 +28,7 @@ import org.signal.storageservice.protos.groups.local.DecryptedMember; import org.signal.storageservice.protos.groups.local.DecryptedPendingMember; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.whispersystems.signalservice.api.groupsv2.DecryptedGroupResponse; import org.whispersystems.signalservice.api.groupsv2.DecryptedGroupUtil; import org.whispersystems.signalservice.api.groupsv2.GroupCandidate; import org.whispersystems.signalservice.api.groupsv2.GroupHistoryPage; @@ -75,7 +77,7 @@ class GroupV2Helper { groupApiCredentials = null; } - DecryptedGroup getDecryptedGroup(final GroupSecretParams groupSecretParams) throws NotAGroupMemberException { + DecryptedGroupResponse getDecryptedGroup(final GroupSecretParams groupSecretParams) throws NotAGroupMemberException { try { final var groupsV2AuthorizationString = getGroupAuthForToday(groupSecretParams); return dependencies.getGroupsV2Api().getGroup(groupSecretParams, groupsV2AuthorizationString); @@ -85,7 +87,7 @@ class GroupV2Helper { } logger.warn("Failed to retrieve Group V2 info, ignoring: {}", e.getMessage()); return null; - } catch (IOException | VerificationFailedException | InvalidGroupStateException e) { + } catch (IOException | VerificationFailedException | InvalidGroupStateException | InvalidInputException e) { logger.warn("Failed to retrieve Group V2 info, ignoring: {}", e.getMessage()); return null; } @@ -103,19 +105,23 @@ class GroupV2Helper { } GroupHistoryPage getDecryptedGroupHistoryPage( - final GroupSecretParams groupSecretParams, int fromRevision + final GroupSecretParams groupSecretParams, int fromRevision, long sendEndorsementsExpirationMs ) throws NotAGroupMemberException { try { final var groupsV2AuthorizationString = getGroupAuthForToday(groupSecretParams); return dependencies.getGroupsV2Api() - .getGroupHistoryPage(groupSecretParams, fromRevision, groupsV2AuthorizationString, false); + .getGroupHistoryPage(groupSecretParams, + fromRevision, + groupsV2AuthorizationString, + false, + sendEndorsementsExpirationMs); } catch (NonSuccessfulResponseCodeException e) { if (e.getCode() == 403) { throw new NotAGroupMemberException(GroupUtils.getGroupIdV2(groupSecretParams), null); } logger.warn("Failed to retrieve Group V2 history, ignoring: {}", e.getMessage()); return null; - } catch (IOException | VerificationFailedException | InvalidGroupStateException e) { + } catch (IOException | VerificationFailedException | InvalidGroupStateException | InvalidInputException e) { logger.warn("Failed to retrieve Group V2 history, ignoring: {}", e.getMessage()); return null; } @@ -132,7 +138,7 @@ class GroupV2Helper { return partialDecryptedGroup.revision; } - Pair createGroup( + Pair createGroup( String name, Set members, byte[] avatarFile ) { final var newGroup = buildNewGroup(name, members, avatarFile); @@ -143,16 +149,16 @@ class GroupV2Helper { final var groupSecretParams = newGroup.getGroupSecretParams(); final GroupsV2AuthorizationString groupAuthForToday; - final DecryptedGroup decryptedGroup; + final DecryptedGroupResponse response; try { groupAuthForToday = getGroupAuthForToday(groupSecretParams); dependencies.getGroupsV2Api().putNewGroup(newGroup, groupAuthForToday); - decryptedGroup = dependencies.getGroupsV2Api().getGroup(groupSecretParams, groupAuthForToday); - } catch (IOException | VerificationFailedException | InvalidGroupStateException e) { + response = dependencies.getGroupsV2Api().getGroup(groupSecretParams, groupAuthForToday); + } catch (IOException | VerificationFailedException | InvalidGroupStateException | InvalidInputException e) { logger.warn("Failed to create V2 group: {}", e.getMessage()); return null; } - if (decryptedGroup == null) { + if (response == null) { logger.warn("Failed to create V2 group, unknown error!"); return null; } @@ -161,7 +167,7 @@ class GroupV2Helper { final var masterKey = groupSecretParams.getMasterKey(); var g = new GroupInfoV2(groupId, masterKey, context.getAccount().getRecipientResolver()); - return new Pair<>(g, decryptedGroup); + return new Pair<>(g, response); } private GroupsV2Operations.NewGroup buildNewGroup( @@ -195,7 +201,7 @@ class GroupV2Helper { 0); } - Pair updateGroup( + Pair updateGroup( GroupInfoV2 groupInfoV2, String name, String description, byte[] avatarFile ) throws IOException { final var groupSecretParams = GroupSecretParams.deriveFromMasterKey(groupInfoV2.getMasterKey()); @@ -218,7 +224,7 @@ class GroupV2Helper { return commitChange(groupInfoV2, change); } - Pair addMembers( + Pair addMembers( GroupInfoV2 groupInfoV2, Set newMembers ) throws IOException { GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2); @@ -244,7 +250,7 @@ class GroupV2Helper { return commitChange(groupInfoV2, change); } - Pair leaveGroup( + Pair leaveGroup( GroupInfoV2 groupInfoV2, Set membersToMakeAdmin ) throws IOException { var pendingMembersList = groupInfoV2.getGroup().pendingMembers; @@ -264,7 +270,7 @@ class GroupV2Helper { return commitChange(groupInfoV2, groupOperations.createLeaveAndPromoteMembersToAdmin(selfAci, adminUuids)); } - Pair removeMembers( + Pair removeMembers( GroupInfoV2 groupInfoV2, Set members ) throws IOException { final var memberUuids = members.stream() @@ -276,7 +282,7 @@ class GroupV2Helper { return ejectMembers(groupInfoV2, memberUuids); } - Pair approveJoinRequestMembers( + Pair approveJoinRequestMembers( GroupInfoV2 groupInfoV2, Set members ) throws IOException { final var memberUuids = members.stream() @@ -287,7 +293,7 @@ class GroupV2Helper { return approveJoinRequest(groupInfoV2, memberUuids); } - Pair refuseJoinRequestMembers( + Pair refuseJoinRequestMembers( GroupInfoV2 groupInfoV2, Set members ) throws IOException { final var memberUuids = members.stream() @@ -297,7 +303,7 @@ class GroupV2Helper { return refuseJoinRequest(groupInfoV2, memberUuids); } - Pair revokeInvitedMembers( + Pair revokeInvitedMembers( GroupInfoV2 groupInfoV2, Set members ) throws IOException { var pendingMembersList = groupInfoV2.getGroup().pendingMembers; @@ -311,7 +317,7 @@ class GroupV2Helper { return revokeInvites(groupInfoV2, memberUuids); } - Pair banMembers( + Pair banMembers( GroupInfoV2 groupInfoV2, Set block ) throws IOException { GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2); @@ -329,7 +335,7 @@ class GroupV2Helper { return commitChange(groupInfoV2, change); } - Pair unbanMembers( + Pair unbanMembers( GroupInfoV2 groupInfoV2, Set block ) throws IOException { GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2); @@ -345,14 +351,14 @@ class GroupV2Helper { return commitChange(groupInfoV2, change); } - Pair resetGroupLinkPassword(GroupInfoV2 groupInfoV2) throws IOException { + Pair resetGroupLinkPassword(GroupInfoV2 groupInfoV2) throws IOException { final GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2); final var newGroupLinkPassword = GroupLinkPassword.createNew().serialize(); final var change = groupOperations.createModifyGroupLinkPasswordChange(newGroupLinkPassword); return commitChange(groupInfoV2, change); } - Pair setGroupLinkState( + Pair setGroupLinkState( GroupInfoV2 groupInfoV2, GroupLinkState state ) throws IOException { final GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2); @@ -367,7 +373,7 @@ class GroupV2Helper { return commitChange(groupInfoV2, change); } - Pair setEditDetailsPermission( + Pair setEditDetailsPermission( GroupInfoV2 groupInfoV2, GroupPermission permission ) throws IOException { final GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2); @@ -377,7 +383,7 @@ class GroupV2Helper { return commitChange(groupInfoV2, change); } - Pair setAddMemberPermission( + Pair setAddMemberPermission( GroupInfoV2 groupInfoV2, GroupPermission permission ) throws IOException { final GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2); @@ -387,7 +393,7 @@ class GroupV2Helper { return commitChange(groupInfoV2, change); } - Pair updateSelfProfileKey(GroupInfoV2 groupInfoV2) throws IOException { + Pair updateSelfProfileKey(GroupInfoV2 groupInfoV2) throws IOException { Optional selfInGroup = groupInfoV2.getGroup() == null ? Optional.empty() : DecryptedGroupUtil.findMemberByAci(groupInfoV2.getGroup().members, getSelfAci()); @@ -417,7 +423,7 @@ class GroupV2Helper { return commitChange(groupInfoV2, change); } - GroupChange joinGroup( + GroupChangeResponse joinGroup( GroupMasterKey groupMasterKey, GroupLinkPassword groupLinkPassword, DecryptedGroupJoinInfo decryptedGroupJoinInfo @@ -444,7 +450,7 @@ class GroupV2Helper { return commitChange(groupSecretParams, decryptedGroupJoinInfo.revision, change, groupLinkPassword); } - Pair acceptInvite(GroupInfoV2 groupInfoV2) throws IOException { + Pair acceptInvite(GroupInfoV2 groupInfoV2) throws IOException { final GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2); final var selfRecipientId = context.getAccount().getSelfRecipientId(); @@ -461,7 +467,7 @@ class GroupV2Helper { return commitChange(groupInfoV2, change); } - Pair setMemberAdmin( + Pair setMemberAdmin( GroupInfoV2 groupInfoV2, RecipientId recipientId, boolean admin ) throws IOException { final GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2); @@ -475,7 +481,7 @@ class GroupV2Helper { } } - Pair setMessageExpirationTimer( + Pair setMessageExpirationTimer( GroupInfoV2 groupInfoV2, int messageExpirationTimer ) throws IOException { final GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2); @@ -483,7 +489,7 @@ class GroupV2Helper { return commitChange(groupInfoV2, change); } - Pair setIsAnnouncementGroup( + Pair setIsAnnouncementGroup( GroupInfoV2 groupInfoV2, boolean isAnnouncementGroup ) throws IOException { final GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2); @@ -511,7 +517,7 @@ class GroupV2Helper { return dependencies.getGroupsV2Operations().forGroup(groupSecretParams); } - private Pair revokeInvites( + private Pair revokeInvites( GroupInfoV2 groupInfoV2, Set pendingMembers ) throws IOException { final GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2); @@ -525,28 +531,28 @@ class GroupV2Helper { return commitChange(groupInfoV2, groupOperations.createRemoveInvitationChange(uuidCipherTexts)); } - private Pair approveJoinRequest( + private Pair approveJoinRequest( GroupInfoV2 groupInfoV2, Set uuids ) throws IOException { final GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2); return commitChange(groupInfoV2, groupOperations.createApproveGroupJoinRequest(uuids)); } - private Pair refuseJoinRequest( + private Pair refuseJoinRequest( GroupInfoV2 groupInfoV2, Set serviceIds ) throws IOException { final GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2); return commitChange(groupInfoV2, groupOperations.createRefuseGroupJoinRequest(serviceIds, false, List.of())); } - private Pair ejectMembers( + private Pair ejectMembers( GroupInfoV2 groupInfoV2, Set members ) throws IOException { final GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2); return commitChange(groupInfoV2, groupOperations.createRemoveMembersChange(members, false, List.of())); } - private Pair commitChange( + private Pair commitChange( GroupInfoV2 groupInfoV2, GroupChange.Actions.Builder change ) throws IOException { final var groupSecretParams = GroupSecretParams.deriveFromMasterKey(groupInfoV2.getMasterKey()); @@ -567,10 +573,12 @@ class GroupV2Helper { var signedGroupChange = dependencies.getGroupsV2Api() .patchGroup(changeActions, getGroupAuthForToday(groupSecretParams), Optional.empty()); + groupInfoV2.setGroup(decryptedGroupState); + return new Pair<>(decryptedGroupState, signedGroupChange); } - private GroupChange commitChange( + private GroupChangeResponse commitChange( GroupSecretParams groupSecretParams, int currentRevision, GroupChange.Actions.Builder change, diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/ProfileHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/ProfileHelper.java index 4a2b79bd..03d277f9 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/ProfileHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/ProfileHelper.java @@ -16,13 +16,14 @@ import org.asamk.signal.manager.util.KeyUtils; import org.asamk.signal.manager.util.PaymentUtils; import org.asamk.signal.manager.util.ProfileUtils; import org.asamk.signal.manager.util.Utils; +import org.jetbrains.annotations.Nullable; import org.signal.libsignal.protocol.IdentityKey; import org.signal.libsignal.protocol.InvalidKeyException; import org.signal.libsignal.zkgroup.profiles.ExpiringProfileKeyCredential; import org.signal.libsignal.zkgroup.profiles.ProfileKey; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.whispersystems.signalservice.api.crypto.UnidentifiedAccess; +import org.whispersystems.signalservice.api.crypto.SealedSenderAccess; import org.whispersystems.signalservice.api.profiles.AvatarUploadParams; import org.whispersystems.signalservice.api.profiles.ProfileAndCredential; import org.whispersystems.signalservice.api.profiles.SignalServiceProfile; @@ -387,7 +388,7 @@ public final class ProfileHelper { private Single retrieveProfile( SignalServiceAddress address, Optional profileKey, - Optional unidentifiedAccess, + @Nullable SealedSenderAccess unidentifiedAccess, SignalServiceProfile.RequestType requestType ) { final var profileService = dependencies.getProfileService(); @@ -450,13 +451,7 @@ public final class ProfileHelper { } } - private Optional getUnidentifiedAccess(RecipientId recipientId) { - var unidentifiedAccess = context.getUnidentifiedAccessHelper().getAccessFor(recipientId, true); - - if (unidentifiedAccess.isPresent()) { - return unidentifiedAccess.get().getTargetUnidentifiedAccess(); - } - - return Optional.empty(); + private @Nullable SealedSenderAccess getUnidentifiedAccess(RecipientId recipientId) { + return context.getUnidentifiedAccessHelper().getSealedSenderAccessFor(recipientId, true); } } diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/SendHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/SendHelper.java index 0ff7fd89..2ac0fc9c 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/SendHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/SendHelper.java @@ -13,6 +13,7 @@ import org.asamk.signal.manager.storage.SignalAccount; import org.asamk.signal.manager.storage.groups.GroupInfo; import org.asamk.signal.manager.storage.recipients.RecipientId; import org.asamk.signal.manager.storage.sendLog.MessageSendLogEntry; +import org.jetbrains.annotations.Nullable; import org.signal.libsignal.protocol.InvalidKeyException; import org.signal.libsignal.protocol.InvalidRegistrationIdException; import org.signal.libsignal.protocol.NoSessionException; @@ -22,9 +23,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.whispersystems.signalservice.api.SignalServiceMessageSender; import org.whispersystems.signalservice.api.crypto.ContentHint; +import org.whispersystems.signalservice.api.crypto.SealedSenderAccess; import org.whispersystems.signalservice.api.crypto.UnidentifiedAccess; -import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair; import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException; +import org.whispersystems.signalservice.api.groupsv2.GroupSendEndorsements; import org.whispersystems.signalservice.api.messages.SendMessageResult; import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage; import org.whispersystems.signalservice.api.messages.SignalServiceEditMessage; @@ -199,7 +201,7 @@ public class SendHelper { return SendMessageResult.success(account.getSelfAddress(), List.of(), false, false, 0, Optional.empty()); } try { - return messageSender.sendSyncMessage(message, context.getUnidentifiedAccessHelper().getAccessForSync()); + return messageSender.sendSyncMessage(message); } catch (UnregisteredUserException e) { var address = context.getRecipientHelper().resolveSignalServiceAddress(account.getSelfRecipientId()); return SendMessageResult.unregisteredFailure(address); @@ -380,10 +382,11 @@ public class SendHelper { () -> false, urgent, editTargetTimestamp.get()); - final SenderKeySenderHandler senderKeySender = (distId, recipients, unidentifiedAccess, isRecipientUpdate) -> messageSender.sendGroupDataMessage( + final SenderKeySenderHandler senderKeySender = (distId, recipients, unidentifiedAccess, groupSendEndorsements, isRecipientUpdate) -> messageSender.sendGroupDataMessage( distId, recipients, unidentifiedAccess, + groupSendEndorsements, isRecipientUpdate, contentHint, message, @@ -436,9 +439,11 @@ public class SendHelper { unidentifiedAccess, message, () -> false), - (distId, recipients, unidentifiedAccess, isRecipientUpdate) -> messageSender.sendGroupTyping(distId, + (distId, recipients, unidentifiedAccess, groupSendEndorsements, isRecipientUpdate) -> messageSender.sendGroupTyping( + distId, recipients, unidentifiedAccess, + groupSendEndorsements, message), recipientIds, distributionId); @@ -526,8 +531,8 @@ public class SendHelper { final var senderKeyTargets = new HashSet(); final var recipientList = new ArrayList<>(recipientIds); for (final var recipientId : recipientList) { - final var access = context.getUnidentifiedAccessHelper().getAccessFor(recipientId); - if (access.isEmpty() || access.get().getTargetUnidentifiedAccess().isEmpty()) { + final var access = context.getUnidentifiedAccessHelper().getSealedSenderAccessFor(recipientId); + if (access != null) { continue; } @@ -562,7 +567,8 @@ public class SendHelper { final var addresses = recipientIdList.stream() .map(context.getRecipientHelper()::resolveSignalServiceAddress) .toList(); - final var unidentifiedAccesses = context.getUnidentifiedAccessHelper().getAccessFor(recipientIdList); + final var unidentifiedAccesses = context.getUnidentifiedAccessHelper() + .getSealedSenderAccessFor(recipientIdList); try { final var results = sender.send(addresses, unidentifiedAccesses, isRecipientUpdate); @@ -601,15 +607,14 @@ public class SendHelper { List unidentifiedAccesses = context.getUnidentifiedAccessHelper() .getAccessFor(recipientIdList) .stream() - .map(Optional::get) - .map(UnidentifiedAccessPair::getTargetUnidentifiedAccess) - .map(Optional::get) .toList(); + final GroupSendEndorsements groupSendEndorsements = null;//TODO try { List results = sender.send(distributionId, addresses, unidentifiedAccesses, + groupSendEndorsements, isRecipientUpdate); final var successCount = results.stream().filter(SendMessageResult::isSuccess).count(); @@ -684,7 +689,7 @@ public class SendHelper { try { return s.send(messageSender, address, - context.getUnidentifiedAccessHelper().getAccessFor(recipientId), + context.getUnidentifiedAccessHelper().getSealedSenderAccessFor(recipientId), includePniSignature); } catch (UnregisteredUserException e) { final RecipientId newRecipientId; @@ -696,7 +701,7 @@ public class SendHelper { address = context.getRecipientHelper().resolveSignalServiceAddress(newRecipientId); return s.send(messageSender, address, - context.getUnidentifiedAccessHelper().getAccessFor(newRecipientId), + context.getUnidentifiedAccessHelper().getSealedSenderAccessFor(newRecipientId), includePniSignature); } } catch (UnregisteredUserException e) { @@ -772,7 +777,7 @@ public class SendHelper { SendMessageResult send( SignalServiceMessageSender messageSender, SignalServiceAddress address, - Optional unidentifiedAccess, + @Nullable SealedSenderAccess unidentifiedAccess, boolean includePniSignature ) throws IOException, UnregisteredUserException, ProofRequiredException, RateLimitException, org.whispersystems.signalservice.api.crypto.UntrustedIdentityException; } @@ -783,6 +788,7 @@ public class SendHelper { DistributionId distributionId, List recipients, List unidentifiedAccess, + GroupSendEndorsements groupSendEndorsements, boolean isRecipientUpdate ) throws IOException, UntrustedIdentityException, NoSessionException, InvalidKeyException, InvalidRegistrationIdException; } @@ -791,7 +797,7 @@ public class SendHelper { List send( List recipients, - List> unidentifiedAccess, + List unidentifiedAccess, boolean isRecipientUpdate ) throws IOException, UntrustedIdentityException; } diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/UnidentifiedAccessHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/UnidentifiedAccessHelper.java index dacc97ba..3899179c 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/UnidentifiedAccessHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/UnidentifiedAccessHelper.java @@ -5,17 +5,17 @@ import org.asamk.signal.manager.api.Profile; import org.asamk.signal.manager.internal.SignalDependencies; import org.asamk.signal.manager.storage.SignalAccount; import org.asamk.signal.manager.storage.recipients.RecipientId; +import org.jetbrains.annotations.Nullable; import org.signal.libsignal.metadata.certificate.InvalidCertificateException; import org.signal.libsignal.metadata.certificate.SenderCertificate; import org.signal.libsignal.zkgroup.profiles.ProfileKey; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.whispersystems.signalservice.api.crypto.SealedSenderAccess; import org.whispersystems.signalservice.api.crypto.UnidentifiedAccess; -import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair; import java.io.IOException; import java.util.List; -import java.util.Optional; import java.util.concurrent.TimeUnit; public class UnidentifiedAccessHelper { @@ -42,57 +42,49 @@ public class UnidentifiedAccessHelper { senderCertificate = null; } - public List> getAccessFor(List recipients) { + public List getSealedSenderAccessFor(List recipients) { + return recipients.stream().map(this::getAccessFor).map(SealedSenderAccess::forIndividual).toList(); + } + + public @Nullable SealedSenderAccess getSealedSenderAccessFor(RecipientId recipient) { + return getSealedSenderAccessFor(recipient, false); + } + + public @Nullable SealedSenderAccess getSealedSenderAccessFor(RecipientId recipient, boolean noRefresh) { + return SealedSenderAccess.forIndividual(getAccessFor(recipient, noRefresh)); + } + + public List getAccessFor(List recipients) { return recipients.stream().map(this::getAccessFor).toList(); } - public Optional getAccessFor(RecipientId recipient) { + private @Nullable UnidentifiedAccess getAccessFor(RecipientId recipient) { return getAccessFor(recipient, false); } - public Optional getAccessFor(RecipientId recipientId, boolean noRefresh) { + private @Nullable UnidentifiedAccess getAccessFor(RecipientId recipientId, boolean noRefresh) { var recipientUnidentifiedAccessKey = getTargetUnidentifiedAccessKey(recipientId, noRefresh); if (recipientUnidentifiedAccessKey == null) { logger.trace("Unidentified access not available for {}", recipientId); - return Optional.empty(); + return null; } var selfUnidentifiedAccessKey = getSelfUnidentifiedAccessKey(noRefresh); if (selfUnidentifiedAccessKey == null) { logger.trace("Unidentified access not available for self"); - return Optional.empty(); + return null; } var senderCertificate = getSenderCertificateFor(recipientId); if (senderCertificate == null) { logger.trace("Unidentified access not available due to missing sender certificate"); - return Optional.empty(); - } - - try { - return Optional.of(new UnidentifiedAccessPair(new UnidentifiedAccess(recipientUnidentifiedAccessKey, - senderCertificate, - false), new UnidentifiedAccess(selfUnidentifiedAccessKey, senderCertificate, false))); - } catch (InvalidCertificateException e) { - return Optional.empty(); - } - } - - public Optional getAccessForSync() { - var selfUnidentifiedAccessKey = getSelfUnidentifiedAccessKey(false); - var selfUnidentifiedAccessCertificate = getSenderCertificate(); - - if (selfUnidentifiedAccessKey == null || selfUnidentifiedAccessCertificate == null) { - return Optional.empty(); + return null; } try { - return Optional.of(new UnidentifiedAccessPair(new UnidentifiedAccess(selfUnidentifiedAccessKey, - selfUnidentifiedAccessCertificate, - false), - new UnidentifiedAccess(selfUnidentifiedAccessKey, selfUnidentifiedAccessCertificate, false))); + return new UnidentifiedAccess(recipientUnidentifiedAccessKey, senderCertificate, false); } catch (InvalidCertificateException e) { - return Optional.empty(); + return null; } } @@ -121,7 +113,7 @@ public class UnidentifiedAccessHelper { privacySenderCertificate = new SenderCertificate(certificate); return certificate; } catch (IOException | InvalidCertificateException e) { - logger.warn("Failed to get sender certificate, ignoring: {}", e.getMessage()); + logger.warn("Failed to get sender certificate (pnp), ignoring: {}", e.getMessage()); return null; } } diff --git a/settings.gradle.kts b/settings.gradle.kts index d185c417..99018552 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -15,7 +15,7 @@ dependencyResolutionManagement { library("slf4j.jul", "org.slf4j", "jul-to-slf4j").versionRef("slf4j") library("logback", "ch.qos.logback", "logback-classic").version("1.5.6") - library("signalservice", "com.github.turasa", "signal-service-java").version("2.15.3_unofficial_104") + library("signalservice", "com.github.turasa", "signal-service-java").version("2.15.3_unofficial_105") library("sqlite", "org.xerial", "sqlite-jdbc").version("3.46.0.0") library("hikari", "com.zaxxer", "HikariCP").version("5.1.0") library("junit.jupiter", "org.junit.jupiter", "junit-jupiter").version("5.10.2") diff --git a/src/main/java/org/asamk/signal/BaseConfig.java b/src/main/java/org/asamk/signal/BaseConfig.java index e234ed2f..43db0531 100644 --- a/src/main/java/org/asamk/signal/BaseConfig.java +++ b/src/main/java/org/asamk/signal/BaseConfig.java @@ -8,7 +8,7 @@ public class BaseConfig { public static final String PROJECT_VERSION = BaseConfig.class.getPackage().getImplementationVersion(); static final String USER_AGENT_SIGNAL_ANDROID = Optional.ofNullable(System.getenv("SIGNAL_CLI_USER_AGENT")) - .orElse("Signal-Android/7.9.0"); + .orElse("Signal-Android/7.12.1"); static final String USER_AGENT_SIGNAL_CLI = PROJECT_NAME == null ? "signal-cli" : PROJECT_NAME + "/" + PROJECT_VERSION; -- 2.50.1