Implement syncing
This commit is contained in:
parent
2ef26ab915
commit
9de2cc210d
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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..<Int(SharedSenderKeysImplementation.ivSize)]
|
||||
let ciphertext = ivAndCiphertext[Int(SharedSenderKeysImplementation.ivSize)...]
|
||||
let gcm = GCM(iv: iv.bytes, tagLength: Int(SharedSenderKeysImplementation.gcmTagSize), mode: .combined)
|
||||
let messageKey = ratchet.messageKeys.last!
|
||||
guard let messageKey = ratchet.messageKeys.last else { throw RatchetingError.messageKeyMissing(targetKeyIndex: keyIndex, groupPublicKey: groupPublicKey, senderPublicKey: senderPublicKey) }
|
||||
let aes = try AES(key: Data(hex: messageKey).bytes, blockMode: gcm, padding: .noPadding)
|
||||
return Data(try aes.decrypt(ciphertext.bytes))
|
||||
}
|
||||
|
|
|
@ -17,6 +17,14 @@ public final class SyncMessagesProtocol : NSObject {
|
|||
|
||||
internal static var storage: OWSPrimaryStorage { OWSPrimaryStorage.shared() }
|
||||
|
||||
// MARK: - Error
|
||||
|
||||
@objc(LKSyncMessagesProtocolError)
|
||||
public class SyncMessagesProtocolError : NSError { // Not called `Error` for Obj-C interoperablity
|
||||
|
||||
@objc public static let privateKeyMissing = SyncMessagesProtocolError(domain: "SyncMessagesProtocolErrorDomain", code: 1, userInfo: [ NSLocalizedDescriptionKey : "Couldn't get private key for SSK based closed group." ])
|
||||
}
|
||||
|
||||
// MARK: - Sending
|
||||
|
||||
@objc public static func syncProfile() {
|
||||
|
@ -59,6 +67,51 @@ public final class SyncMessagesProtocol : NSObject {
|
|||
return AnyPromise.from(when(fulfilled: promises))
|
||||
}
|
||||
|
||||
@objc(syncClosedGroup:transaction:)
|
||||
public static func syncClosedGroup(_ thread: TSGroupThread, using transaction: YapDatabaseReadWriteTransaction) -> 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<Void>(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<Void> { $0.fulfill(()) })
|
||||
}
|
||||
|
||||
@objc public static func syncAllClosedGroups() -> AnyPromise {
|
||||
var closedGroups: [TSGroupThread] = []
|
||||
TSGroupThread.enumerateCollectionObjects { object, _ in
|
||||
|
|
|
@ -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];
|
||||
|
|
Loading…
Reference in New Issue