diff --git a/SignalMessaging/contacts/OWSSyncManager.m b/SignalMessaging/contacts/OWSSyncManager.m index d11584242..d76fed195 100644 --- a/SignalMessaging/contacts/OWSSyncManager.m +++ b/SignalMessaging/contacts/OWSSyncManager.m @@ -302,20 +302,28 @@ NSString *const kSyncManagerLastContactSyncKey = @"kTSStorageManagerOWSSyncManag - (AnyPromise *)syncGroupForThread:(TSGroupThread *)thread { - OWSSyncGroupsMessage *syncGroupsMessage = [[OWSSyncGroupsMessage alloc] initWithGroupThread:thread]; - AnyPromise *promise = [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) { - [self.messageSender sendMessage:syncGroupsMessage - success:^{ - OWSLogInfo(@"Successfully sent group sync message."); - resolve(@(1)); - } - failure:^(NSError *error) { - OWSLogError(@"Failed to send group sync message due to error: %@.", error); - resolve(error); - }]; - }]; - [promise retainUntilComplete]; - return promise; + if (thread.usesSharedSenderKeys) { + __block AnyPromise *promise; + [LKStorage writeSyncWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { + promise = [LKSyncMessagesProtocol syncClosedGroup:thread transaction:transaction]; + } error:nil]; + return promise; + } else { + OWSSyncGroupsMessage *syncGroupsMessage = [[OWSSyncGroupsMessage alloc] initWithGroupThread:thread]; + AnyPromise *promise = [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) { + [self.messageSender sendMessage:syncGroupsMessage + success:^{ + OWSLogInfo(@"Successfully sent group sync message."); + resolve(@(1)); + } + failure:^(NSError *error) { + OWSLogError(@"Failed to send group sync message due to error: %@.", error); + resolve(error); + }]; + }]; + [promise retainUntilComplete]; + return promise; + } } - (AnyPromise *)syncAllOpenGroups diff --git a/SignalServiceKit/src/Loki/Protocol/Closed Groups/ClosedGroupsProtocol.swift b/SignalServiceKit/src/Loki/Protocol/Closed Groups/ClosedGroupsProtocol.swift index 4d38c00ed..a11de6f86 100644 --- a/SignalServiceKit/src/Loki/Protocol/Closed Groups/ClosedGroupsProtocol.swift +++ b/SignalServiceKit/src/Loki/Protocol/Closed Groups/ClosedGroupsProtocol.swift @@ -101,7 +101,7 @@ public final class ClosedGroupsProtocol : NSObject { // Send closed group update messages to the new members (and their linked devices) using established channels let allSenderKeys = [ClosedGroupSenderKey](Storage.getAllClosedGroupSenderKeys(for: groupPublicKey)) // This includes the newly generated sender keys for member in newMembers { // Not `newMembersAndLinkedDevices` as this internally takes care of multi device already - let thread = TSContactThread.getOrCreateThread(contactId: member) + let thread = TSContactThread.getOrCreateThread(withContactId: member, transaction: transaction) thread.save(with: transaction) let closedGroupUpdateMessageKind = ClosedGroupUpdateMessage.Kind.new(groupPublicKey: Data(hex: groupPublicKey), name: name, groupPrivateKey: Data(hex: groupPrivateKey), senderKeys: allSenderKeys, members: members, admins: admins) diff --git a/SignalServiceKit/src/Loki/Protocol/Closed Groups/SharedSenderKeysImplementation.swift b/SignalServiceKit/src/Loki/Protocol/Closed Groups/SharedSenderKeysImplementation.swift index ac27f0f7b..75026cdf5 100644 --- a/SignalServiceKit/src/Loki/Protocol/Closed Groups/SharedSenderKeysImplementation.swift +++ b/SignalServiceKit/src/Loki/Protocol/Closed Groups/SharedSenderKeysImplementation.swift @@ -124,6 +124,7 @@ public final class SharedSenderKeysImplementation : NSObject, SharedSenderKeysPr } } + // MARK: Public API @objc(encrypt:forGroupWithPublicKey:senderPublicKey:protocolContext:error:) public func encrypt(_ plaintext: Data, forGroupWithPublicKey groupPublicKey: String, senderPublicKey: String, protocolContext: Any) throws -> [Any] { let transaction = protocolContext as! YapDatabaseReadWriteTransaction @@ -152,7 +153,7 @@ public final class SharedSenderKeysImplementation : NSObject, SharedSenderKeysPr let iv = ivAndCiphertext[0.. AnyPromise { + // Prepare + let messageSenderJobQueue = SSKEnvironment.shared.messageSenderJobQueue + let group = thread.groupModel + let groupPublicKey = LKGroupUtilities.getDecodedGroupID(group.groupId) + let name = group.groupName! + let members = group.groupMemberIds + let admins = group.groupAdminIds + guard let groupPrivateKey = Storage.getClosedGroupPrivateKey(for: groupPublicKey) else { + print("[Loki] Couldn't get private key for SSK based closed group.") + return AnyPromise.from(Promise(error: SyncMessagesProtocolError.privateKeyMissing)) + } + // Generate ratchets for the user's linked devices + let userPublicKey = getUserHexEncodedPublicKey() + let masterPublicKey = UserDefaults.standard[.masterHexEncodedPublicKey] ?? userPublicKey + let deviceLinks = storage.getDeviceLinks(for: userPublicKey, in: transaction) + let linkedDevices = deviceLinks.flatMap { [ $0.master.hexEncodedPublicKey, $0.slave.hexEncodedPublicKey ] }.filter { $0 != userPublicKey } + let senderKeys: [ClosedGroupSenderKey] = linkedDevices.map { publicKey in + let ratchet = SharedSenderKeysImplementation.shared.generateRatchet(for: groupPublicKey, senderPublicKey: publicKey, using: transaction) + return ClosedGroupSenderKey(chainKey: Data(hex: ratchet.chainKey), keyIndex: ratchet.keyIndex, publicKey: publicKey) + } + // Send a closed group update message to the existing members with the linked devices' ratchets (this message is aimed at the group) + func sendMessageToGroup() { + let closedGroupUpdateMessageKind = ClosedGroupUpdateMessage.Kind.info(groupPublicKey: Data(hex: groupPublicKey), name: name, senderKeys: senderKeys, + members: members, admins: admins) + let closedGroupUpdateMessage = ClosedGroupUpdateMessage(thread: thread, kind: closedGroupUpdateMessageKind) + messageSenderJobQueue.add(message: closedGroupUpdateMessage, transaction: transaction) + } + sendMessageToGroup() + // Send closed group update messages to the linked devices using established channels + func sendMessageToLinkedDevices() { + let allSenderKeys = [ClosedGroupSenderKey](Storage.getAllClosedGroupSenderKeys(for: groupPublicKey)) // This includes the newly generated sender keys + let thread = TSContactThread.getOrCreateThread(withContactId: masterPublicKey, transaction: transaction) + thread.save(with: transaction) + let closedGroupUpdateMessageKind = ClosedGroupUpdateMessage.Kind.new(groupPublicKey: Data(hex: groupPublicKey), name: name, + groupPrivateKey: Data(hex: groupPrivateKey), senderKeys: allSenderKeys, members: members, admins: admins) + let closedGroupUpdateMessage = ClosedGroupUpdateMessage(thread: thread, kind: closedGroupUpdateMessageKind) + messageSenderJobQueue.add(message: closedGroupUpdateMessage, transaction: transaction) // This internally takes care of multi device + } + sendMessageToLinkedDevices() + // Return a dummy promise + return AnyPromise.from(Promise { $0.fulfill(()) }) + } + @objc public static func syncAllClosedGroups() -> AnyPromise { var closedGroups: [TSGroupThread] = [] TSGroupThread.enumerateCollectionObjects { object, _ in diff --git a/SignalServiceKit/src/Messages/OWSMessageSender.m b/SignalServiceKit/src/Messages/OWSMessageSender.m index 14c2a8130..f74096a02 100644 --- a/SignalServiceKit/src/Messages/OWSMessageSender.m +++ b/SignalServiceKit/src/Messages/OWSMessageSender.m @@ -543,10 +543,6 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; } [recipientIds addObject:recipientContactId]; - - if ([recipientIds containsObject:self.tsAccountManager.localNumber]) { - OWSFailDebug(@"Message send recipients should not include self."); - } } else { OWSFailDebug(@"Unknown message type: %@", [message class]); NSError *error = OWSErrorMakeFailedToSendOutgoingMessageError(); @@ -692,7 +688,8 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; // In the "self-send" special case, we ony need to send a sync message with a delivery receipt // Loki: Take into account multi device - if ([LKSessionMetaProtocol isThreadNoteToSelf:thread] && !([message isKindOfClass:LKDeviceLinkMessage.class])) { + if ([LKSessionMetaProtocol isThreadNoteToSelf:thread] + && !([message isKindOfClass:LKDeviceLinkMessage.class]) && !([message isKindOfClass:LKClosedGroupUpdateMessage.class])) { // Don't mark self-sent messages as read (or sent) until the sync transcript is sent successHandler(); return; @@ -1135,7 +1132,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; if ([messageSend.thread isKindOfClass:TSGroupThread.class] && ((TSGroupThread *)messageSend.thread).usesSharedSenderKeys) { senderID = [LKGroupUtilities getDecodedGroupID:((TSGroupThread *)messageSend.thread).groupModel.groupId]; } else { - [LKLogger print:@"Non-UD send"]; + [LKLogger print:@"[Loki] Non-UD send"]; } uint32_t senderDeviceID = type == SSKProtoEnvelopeTypeUnidentifiedSender ? 0 : OWSDevicePrimaryDeviceId; NSString *content = signalMessageInfo[@"content"]; @@ -1436,7 +1433,8 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; // Don't mark self-sent messages as read (or sent) until the sync transcript is sent // Loki: Take into account multi device BOOL isNoteToSelf = [LKSessionMetaProtocol isThreadNoteToSelf:message.thread]; - if (isNoteToSelf && !([message isKindOfClass:LKDeviceLinkMessage.class])) { + if (isNoteToSelf && !([message isKindOfClass:LKDeviceLinkMessage.class]) + && ![message isKindOfClass:LKClosedGroupUpdateMessage.class]) { [LKStorage writeSyncWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { for (NSString *recipientId in message.sendingRecipientIds) { [message updateWithReadRecipientId:recipientId readTimestamp:message.timestamp transaction:transaction];