From 88aaebefaad9506007d0d66f19859febe51298df Mon Sep 17 00:00:00 2001 From: nielsandriesse Date: Thu, 9 Jul 2020 10:26:10 +1000 Subject: [PATCH] Implement sender key requesting --- SignalServiceKit/protobuf/SignalService.proto | 2 +- .../ClosedGroupUpdateMessage.swift | 13 +++++------ .../Closed Groups/ClosedGroupsProtocol.swift | 22 +++++++++++++++---- .../SharedSenderKeysImplementation.swift | 16 ++++++++++++-- 4 files changed, 38 insertions(+), 15 deletions(-) diff --git a/SignalServiceKit/protobuf/SignalService.proto b/SignalServiceKit/protobuf/SignalService.proto index d8d993c25..de4cb009e 100644 --- a/SignalServiceKit/protobuf/SignalService.proto +++ b/SignalServiceKit/protobuf/SignalService.proto @@ -245,7 +245,7 @@ message DataMessage { enum Type { NEW = 0; // groupPublicKey, name, groupPrivateKey, senderKeys, members, admins INFO = 1; // groupPublicKey, name, senderKeys, members, admins - SENDER_KEY_REQUEST = 2; // groupPublicKey, members + SENDER_KEY_REQUEST = 2; // groupPublicKey SENDER_KEY = 3; // groupPublicKey, senderKeys } diff --git a/SignalServiceKit/src/Loki/Protocol/Closed Groups/ClosedGroupUpdateMessage.swift b/SignalServiceKit/src/Loki/Protocol/Closed Groups/ClosedGroupUpdateMessage.swift index 0d560f824..a4488ca53 100644 --- a/SignalServiceKit/src/Loki/Protocol/Closed Groups/ClosedGroupUpdateMessage.swift +++ b/SignalServiceKit/src/Loki/Protocol/Closed Groups/ClosedGroupUpdateMessage.swift @@ -13,7 +13,7 @@ internal final class ClosedGroupUpdateMessage : TSOutgoingMessage { internal enum Kind { case new(groupPublicKey: Data, name: String, groupPrivateKey: Data, senderKeys: [ClosedGroupSenderKey], members: [Data], admins: [Data]) case info(groupPublicKey: Data, name: String, senderKeys: [ClosedGroupSenderKey], members: [Data], admins: [Data]) - case senderKeyRequest(groupPublicKey: Data, members: [Data]) + case senderKeyRequest(groupPublicKey: Data) case senderKey(groupPublicKey: Data, senderKey: ClosedGroupSenderKey) } @@ -49,9 +49,8 @@ internal final class ClosedGroupUpdateMessage : TSOutgoingMessage { let admins = coder.decodeObject(forKey: "admins") as? [Data] else { return nil } self.kind = .info(groupPublicKey: groupPublicKey, name: name, senderKeys: senderKeys, members: members, admins: admins) case "senderKeyRequest": - guard let name = coder.decodeObject(forKey: "name") as? String, - let members = coder.decodeObject(forKey: "members") as? [Data] else { return nil } - self.kind = .senderKeyRequest(groupPublicKey: groupPublicKey, members: members) + guard let name = coder.decodeObject(forKey: "name") as? String else { return nil } + self.kind = .senderKeyRequest(groupPublicKey: groupPublicKey) case "senderKey": guard let senderKey = senderKeys.first else { return nil } self.kind = .senderKey(groupPublicKey: groupPublicKey, senderKey: senderKey) @@ -81,9 +80,8 @@ internal final class ClosedGroupUpdateMessage : TSOutgoingMessage { coder.encode(senderKeys, forKey: "senderKeys") coder.encode(members, forKey: "members") coder.encode(admins, forKey: "admins") - case .senderKeyRequest(let groupPublicKey, let members): + case .senderKeyRequest(let groupPublicKey): coder.encode(groupPublicKey, forKey: "groupPublicKey") - coder.encode(members, forKey: "members") case .senderKey(let groupPublicKey, let senderKey): coder.encode("senderKey", forKey: "kind") coder.encode(groupPublicKey, forKey: "groupPublicKey") @@ -110,9 +108,8 @@ internal final class ClosedGroupUpdateMessage : TSOutgoingMessage { closedGroupUpdate.setSenderKeys(try senderKeys.map { try $0.toProto() }) closedGroupUpdate.setMembers(members) closedGroupUpdate.setAdmins(admins) - case .senderKeyRequest(let groupPublicKey, let members): + case .senderKeyRequest(let groupPublicKey): closedGroupUpdate = SSKProtoDataMessageClosedGroupUpdate.builder(groupPublicKey: groupPublicKey, type: .senderKeyRequest) - closedGroupUpdate.setMembers(members) case .senderKey(let groupPublicKey, let senderKey): closedGroupUpdate = SSKProtoDataMessageClosedGroupUpdate.builder(groupPublicKey: groupPublicKey, type: .senderKey) closedGroupUpdate.setSenderKeys([ try senderKey.toProto() ]) diff --git a/SignalServiceKit/src/Loki/Protocol/Closed Groups/ClosedGroupsProtocol.swift b/SignalServiceKit/src/Loki/Protocol/Closed Groups/ClosedGroupsProtocol.swift index 8b75d8466..5bc658d6d 100644 --- a/SignalServiceKit/src/Loki/Protocol/Closed Groups/ClosedGroupsProtocol.swift +++ b/SignalServiceKit/src/Loki/Protocol/Closed Groups/ClosedGroupsProtocol.swift @@ -14,6 +14,8 @@ import PromiseKit public final class ClosedGroupsProtocol : NSObject { public static let isSharedSenderKeysEnabled = false + // MARK: - Sending + /// - Note: It's recommended to batch fetch the device links for the given set of members before invoking this, to avoid the message sending pipeline /// making a request for each member. public static func createClosedGroup(name: String, members: Set, transaction: YapDatabaseReadWriteTransaction) -> Promise { @@ -186,6 +188,20 @@ public final class ClosedGroupsProtocol : NSObject { infoMessage.save(with: transaction) } + public static func requestSenderKey(for groupPublicKey: String, senderPublicKey: String, using transaction: YapDatabaseReadWriteTransaction) { + // Establish session if needed + SessionManagementProtocol.establishSessionIfNeeded(with: senderPublicKey, using: transaction) + // Send the request + let thread = TSContactThread.getOrCreateThread(withContactId: senderPublicKey, transaction: transaction) + thread.save(with: transaction) + let closedGroupUpdateMessageKind = ClosedGroupUpdateMessage.Kind.senderKeyRequest(groupPublicKey: Data(hex: groupPublicKey)) + let closedGroupUpdateMessage = ClosedGroupUpdateMessage(thread: thread, kind: closedGroupUpdateMessageKind) + let messageSenderJobQueue = SSKEnvironment.shared.messageSenderJobQueue + messageSenderJobQueue.add(message: closedGroupUpdateMessage, transaction: transaction) + } + + // MARK: - Receiving + @objc(handleSharedSenderKeysUpdateIfNeeded:from:transaction:) public static func handleSharedSenderKeysUpdateIfNeeded(_ dataMessage: SSKProtoDataMessage, from publicKey: String, using transaction: YapDatabaseReadWriteTransaction) { // Note that `publicKey` is either the public key of the group or the public key of the @@ -312,10 +328,6 @@ public final class ClosedGroupsProtocol : NSObject { guard membersAndLinkedDevices.contains(senderPublicKey) else { return print("[Loki] Ignoring closed group sender key request from non-member.") } - // Check that the current user is one of the members that the sender is requesting the sender key of - guard closedGroupUpdate.members.map({ $0.toHexString() }).contains(userPublicKey) else { - return print("[Loki] Ignoring closed group sender key request aimed at other members.") - } // Respond to the request SessionManagementProtocol.establishSessionIfNeeded(with: senderPublicKey, using: transaction) // This internally takes care of multi device let userRatchet = SharedSenderKeysImplementation.shared.generateRatchet(for: groupPublicKey, senderPublicKey: userPublicKey, using: transaction) @@ -353,6 +365,8 @@ public final class ClosedGroupsProtocol : NSObject { Storage.setClosedGroupRatchet(for: groupPublicKey, senderPublicKey: senderPublicKey, ratchet: ratchet, using: transaction) } + // MARK: - General + @objc(establishSessionsIfNeededWithClosedGroupMembers:transaction:) public static func establishSessionsIfNeeded(with closedGroupMembers: [String], using transaction: YapDatabaseReadWriteTransaction) { closedGroupMembers.forEach { publicKey in diff --git a/SignalServiceKit/src/Loki/Protocol/Closed Groups/SharedSenderKeysImplementation.swift b/SignalServiceKit/src/Loki/Protocol/Closed Groups/SharedSenderKeysImplementation.swift index 75026cdf5..9f988f976 100644 --- a/SignalServiceKit/src/Loki/Protocol/Closed Groups/SharedSenderKeysImplementation.swift +++ b/SignalServiceKit/src/Loki/Protocol/Closed Groups/SharedSenderKeysImplementation.swift @@ -149,11 +149,23 @@ public final class SharedSenderKeysImplementation : NSObject, SharedSenderKeysPr } public func decrypt(_ ivAndCiphertext: Data, for groupPublicKey: String, senderPublicKey: String, keyIndex: UInt, using transaction: YapDatabaseReadWriteTransaction) throws -> Data { - let ratchet = try stepRatchet(for: groupPublicKey, senderPublicKey: senderPublicKey, until: keyIndex, using: transaction) + let ratchet: ClosedGroupRatchet + do { + ratchet = try stepRatchet(for: groupPublicKey, senderPublicKey: senderPublicKey, until: keyIndex, using: transaction) + } catch { + // FIXME: It'd be cleaner to handle this in OWSMessageDecrypter (where all the other decryption errors are handled), but this was a lot more + // convenient because there's an easy way to get the sender public key from here. + if case RatchetingError.loadingFailed(_, _) = error { + ClosedGroupsProtocol.requestSenderKey(for: groupPublicKey, senderPublicKey: senderPublicKey, using: transaction) + } + throw error + } let iv = ivAndCiphertext[0..