From 00abf4e5feeceb9f6f702c1a042eea89d84000dc Mon Sep 17 00:00:00 2001 From: nielsandriesse Date: Mon, 6 Jul 2020 14:39:55 +1000 Subject: [PATCH] Include the sender public key in sender key messages --- SignalServiceKit/protobuf/SignalService.proto | 6 ++-- .../Closed Groups/ClosedGroupSenderKey.swift | 17 ++++++---- .../ClosedGroupUpdateMessage.swift | 2 +- .../Closed Groups/ClosedGroupsProtocol.swift | 29 ++++++++-------- .../Closed Groups/Storage+ClosedGroups.swift | 11 ++++--- .../src/Protos/Generated/SSKProto.swift | 33 ++++++++++++++----- .../Protos/Generated/SignalService.pb.swift | 31 +++++++++++++---- 7 files changed, 85 insertions(+), 44 deletions(-) diff --git a/SignalServiceKit/protobuf/SignalService.proto b/SignalServiceKit/protobuf/SignalService.proto index ba737af42..8aea62453 100644 --- a/SignalServiceKit/protobuf/SignalService.proto +++ b/SignalServiceKit/protobuf/SignalService.proto @@ -250,9 +250,11 @@ message DataMessage { message SenderKey { // @required - optional bytes chainKey = 1; + optional bytes chainKey = 1; // @required - optional uint32 keyIndex = 2; + optional uint32 keyIndex = 2; + // @required + optional string senderPublicKey = 3; } optional string name = 1; diff --git a/SignalServiceKit/src/Loki/Protocol/Closed Groups/ClosedGroupSenderKey.swift b/SignalServiceKit/src/Loki/Protocol/Closed Groups/ClosedGroupSenderKey.swift index 26ee6a8c3..57e8bf0af 100644 --- a/SignalServiceKit/src/Loki/Protocol/Closed Groups/ClosedGroupSenderKey.swift +++ b/SignalServiceKit/src/Loki/Protocol/Closed Groups/ClosedGroupSenderKey.swift @@ -2,43 +2,48 @@ internal final class ClosedGroupSenderKey : NSObject, NSCoding { internal let chainKey: Data internal let keyIndex: UInt + internal let senderPublicKey: String // MARK: Initialization - init(chainKey: Data, keyIndex: UInt) { + init(chainKey: Data, keyIndex: UInt, senderPublicKey: String) { self.chainKey = chainKey self.keyIndex = keyIndex + self.senderPublicKey = senderPublicKey } // MARK: Coding public init?(coder: NSCoder) { guard let chainKey = coder.decodeObject(forKey: "chainKey") as? Data, - let keyIndex = coder.decodeObject(forKey: "keyIndex") as? UInt else { return nil } + let keyIndex = coder.decodeObject(forKey: "keyIndex") as? UInt, + let senderPublicKey = coder.decodeObject(forKey: "senderPublicKey") as? String else { return nil } self.chainKey = chainKey self.keyIndex = UInt(keyIndex) + self.senderPublicKey = senderPublicKey super.init() } public func encode(with coder: NSCoder) { coder.encode(chainKey, forKey: "chainKey") coder.encode(keyIndex, forKey: "keyIndex") + coder.encode(senderPublicKey, forKey: "senderPublicKey") } // MARK: Proto Conversion internal func toProto() throws -> SSKProtoDataMessageClosedGroupUpdateSenderKey { - return try SSKProtoDataMessageClosedGroupUpdateSenderKey.builder(chainKey: chainKey, keyIndex: UInt32(keyIndex)).build() + return try SSKProtoDataMessageClosedGroupUpdateSenderKey.builder(chainKey: chainKey, keyIndex: UInt32(keyIndex), senderPublicKey: senderPublicKey).build() } // MARK: Equality override public func isEqual(_ other: Any?) -> Bool { guard let other = other as? ClosedGroupSenderKey else { return false } - return chainKey == other.chainKey && keyIndex == other.keyIndex + return chainKey == other.chainKey && keyIndex == other.keyIndex && senderPublicKey == other.senderPublicKey } // MARK: Hashing override public var hash: Int { // Override NSObject.hash and not Hashable.hashValue or Hashable.hash(into:) - return chainKey.hashValue ^ keyIndex.hashValue + return chainKey.hashValue ^ keyIndex.hashValue ^ senderPublicKey.hashValue } // MARK: Description - override public var description: String { return "[ chainKey : \(chainKey), keyIndex : \(keyIndex) ]" } + override public var description: String { return "[ chainKey : \(chainKey), keyIndex : \(keyIndex), senderPublicKey: \(senderPublicKey) ]" } } diff --git a/SignalServiceKit/src/Loki/Protocol/Closed Groups/ClosedGroupUpdateMessage.swift b/SignalServiceKit/src/Loki/Protocol/Closed Groups/ClosedGroupUpdateMessage.swift index f4e00d3aa..61867f103 100644 --- a/SignalServiceKit/src/Loki/Protocol/Closed Groups/ClosedGroupUpdateMessage.swift +++ b/SignalServiceKit/src/Loki/Protocol/Closed Groups/ClosedGroupUpdateMessage.swift @@ -103,7 +103,7 @@ internal final class ClosedGroupUpdateMessage : TSOutgoingMessage { closedGroupUpdate.setMembers(members) closedGroupUpdate.setAdmins(admins) case .senderKey(let groupPublicKey, let senderKey): - closedGroupUpdate = SSKProtoDataMessageClosedGroupUpdate.builder(groupPublicKey: groupPublicKey, type: .chainKey) + closedGroupUpdate = SSKProtoDataMessageClosedGroupUpdate.builder(groupPublicKey: groupPublicKey, type: .senderKey) closedGroupUpdate.setSenderKeys([ try senderKey.toProto() ]) } builder.setClosedGroupUpdate(try closedGroupUpdate.build()) diff --git a/SignalServiceKit/src/Loki/Protocol/Closed Groups/ClosedGroupsProtocol.swift b/SignalServiceKit/src/Loki/Protocol/Closed Groups/ClosedGroupsProtocol.swift index da710d3d3..fb303a161 100644 --- a/SignalServiceKit/src/Loki/Protocol/Closed Groups/ClosedGroupsProtocol.swift +++ b/SignalServiceKit/src/Loki/Protocol/Closed Groups/ClosedGroupsProtocol.swift @@ -50,7 +50,9 @@ public final class ClosedGroupsProtocol : NSObject { // Establish sessions if needed establishSessionsIfNeeded(with: members, using: transaction) // Not `membersAndLinkedDevices` as this internally takes care of multi device already // Send a closed group update message to all members (and their linked devices) using established channels - let senderKeys = ratchets.map { ClosedGroupSenderKey(chainKey: Data(hex: $0.chainKey), keyIndex: $0.keyIndex) } + let senderKeys = zip(ratchets, membersAndLinkedDevices).map { ratchet, publicKey in + ClosedGroupSenderKey(chainKey: Data(hex: ratchet.chainKey), keyIndex: ratchet.keyIndex, senderPublicKey: publicKey) + } for member in members { // Not `membersAndLinkedDevices` as this internally takes care of multi device already let thread = TSContactThread.getOrCreateThread(withContactId: member, transaction: transaction) thread.save(with: transaction) @@ -97,7 +99,9 @@ public final class ClosedGroupsProtocol : NSObject { SharedSenderKeysImplementation.shared.generateRatchet(for: groupPublicKey, senderPublicKey: $0, using: transaction) } // Send a closed group update message to the existing members with the new members' ratchets (this message is aimed at the group) - let senderKeys = ratchets.map { ClosedGroupSenderKey(chainKey: Data(hex: $0.chainKey), keyIndex: $0.keyIndex) } + let senderKeys = zip(ratchets, newMembersAndLinkedDevices).map { ratchet, publicKey in + ClosedGroupSenderKey(chainKey: Data(hex: ratchet.chainKey), keyIndex: ratchet.keyIndex, senderPublicKey: publicKey) + } let closedGroupUpdateMessageKind = ClosedGroupUpdateMessage.Kind.info(groupPublicKey: Data(hex: groupPublicKey), name: name, senderKeys: senderKeys, members: members, admins: admins) let closedGroupUpdateMessage = ClosedGroupUpdateMessage(thread: thread, kind: closedGroupUpdateMessageKind) @@ -105,9 +109,7 @@ public final class ClosedGroupsProtocol : NSObject { // Establish sessions if needed establishSessionsIfNeeded(with: [String](newMembersAsSet), using: transaction) // Not `newMembersAndLinkedDevices` as this internally takes care of multi device already // Send closed group update messages to the new members (and their linked devices) using established channels - let allSenderKeys = Storage.getAllClosedGroupRatchets(for: groupPublicKey).map { // This includes the newly generated ratchets - ClosedGroupSenderKey(chainKey: Data(hex: $0.chainKey), keyIndex: $0.keyIndex) - } + let allSenderKeys = [ClosedGroupSenderKey](Storage.getAllClosedGroupSenderKeys(for: groupPublicKey)) // This includes the newly generated sender keys for member in newMembersAsSet { // Not `newMembersAndLinkedDevices` as this internally takes care of multi device already let thread = TSContactThread.getOrCreateThread(contactId: member) thread.save(with: transaction) @@ -167,7 +169,7 @@ public final class ClosedGroupsProtocol : NSObject { establishSessionsIfNeeded(with: members, using: transaction) // This internally takes care of multi device // Send out the user's new ratchet to all members (minus the removed ones) and their linked devices using established channels let userRatchet = SharedSenderKeysImplementation.shared.generateRatchet(for: groupPublicKey, senderPublicKey: userPublicKey, using: transaction) - let userSenderKey = ClosedGroupSenderKey(chainKey: Data(hex: userRatchet.chainKey), keyIndex: userRatchet.keyIndex) + let userSenderKey = ClosedGroupSenderKey(chainKey: Data(hex: userRatchet.chainKey), keyIndex: userRatchet.keyIndex, senderPublicKey: userPublicKey) for member in members { // This internally takes care of multi device let thread = TSContactThread.getOrCreateThread(withContactId: member, transaction: transaction) thread.save(with: transaction) @@ -195,7 +197,7 @@ public final class ClosedGroupsProtocol : NSObject { switch closedGroupUpdate.type { case .new: handleNewGroupMessage(closedGroupUpdate, using: transaction) case .info: handleInfoMessage(closedGroupUpdate, from: publicKey, using: transaction) - case .chainKey: handleChainKeyMessage(closedGroupUpdate, from: publicKey, using: transaction) + case .senderKey: handleSenderKeyMessage(closedGroupUpdate, from: publicKey, using: transaction) } } @@ -254,19 +256,18 @@ public final class ClosedGroupsProtocol : NSObject { } // Establish sessions if needed (it's important that this happens before the code below) establishSessionsIfNeeded(with: members, using: transaction) // This internally takes care of multi device - // Parse out any new members and store their ratchets (it's important that this happens before the code below) - let oldMembers = group.groupMemberIds - let newMembers = members.filter { !oldMembers.contains($0) } - zip(newMembers, senderKeys).forEach { (member, senderKey) in + // Store the ratchets for any new members (it's important that this happens before the code below) + senderKeys.forEach { senderKey in let ratchet = ClosedGroupRatchet(chainKey: senderKey.chainKey.toHexString(), keyIndex: UInt(senderKey.keyIndex), messageKeys: []) - Storage.setClosedGroupRatchet(for: groupPublicKey, senderPublicKey: member, ratchet: ratchet, using: transaction) + Storage.setClosedGroupRatchet(for: groupPublicKey, senderPublicKey: senderKey.senderPublicKey, ratchet: ratchet, using: transaction) } // Delete all ratchets and send out the user's new ratchet using established channels if any member of the group left or was removed + let oldMembers = group.groupMemberIds if Set(members).intersection(oldMembers) != Set(oldMembers) { Storage.removeAllClosedGroupRatchets(for: groupPublicKey, using: transaction) let userPublicKey = getUserHexEncodedPublicKey() let userRatchet = SharedSenderKeysImplementation.shared.generateRatchet(for: groupPublicKey, senderPublicKey: userPublicKey, using: transaction) - let userSenderKey = ClosedGroupSenderKey(chainKey: Data(hex: userRatchet.chainKey), keyIndex: userRatchet.keyIndex) + let userSenderKey = ClosedGroupSenderKey(chainKey: Data(hex: userRatchet.chainKey), keyIndex: userRatchet.keyIndex, senderPublicKey: userPublicKey) for member in members { let thread = TSContactThread.getOrCreateThread(withContactId: member, transaction: transaction) thread.save(with: transaction) @@ -286,7 +287,7 @@ public final class ClosedGroupsProtocol : NSObject { } /// Invoked upon receiving a chain key from another user. - private static func handleChainKeyMessage(_ closedGroupUpdate: SSKProtoDataMessageClosedGroupUpdate, from senderPublicKey: String, using transaction: YapDatabaseReadWriteTransaction) { + private static func handleSenderKeyMessage(_ closedGroupUpdate: SSKProtoDataMessageClosedGroupUpdate, from senderPublicKey: String, using transaction: YapDatabaseReadWriteTransaction) { let groupPublicKey = closedGroupUpdate.groupPublicKey.toHexString() guard let senderKey = closedGroupUpdate.senderKeys.first else { return print("[Loki] Ignoring invalid closed group update.") diff --git a/SignalServiceKit/src/Loki/Protocol/Closed Groups/Storage+ClosedGroups.swift b/SignalServiceKit/src/Loki/Protocol/Closed Groups/Storage+ClosedGroups.swift index 7a00d049b..e96b4a1ad 100644 --- a/SignalServiceKit/src/Loki/Protocol/Closed Groups/Storage+ClosedGroups.swift +++ b/SignalServiceKit/src/Loki/Protocol/Closed Groups/Storage+ClosedGroups.swift @@ -20,13 +20,14 @@ internal extension Storage { transaction.setObject(ratchet, forKey: senderPublicKey, inCollection: collection) } - internal static func getAllClosedGroupRatchets(for groupPublicKey: String) -> Set { + internal static func getAllClosedGroupSenderKeys(for groupPublicKey: String) -> Set { let collection = getClosedGroupRatchetCollection(for: groupPublicKey) - var result: Set = [] + var result: Set = [] read { transaction in - transaction.enumerateRows(inCollection: collection) { _, object, _, _ in - guard let ratchet = object as? ClosedGroupRatchet else { return } - result.insert(ratchet) + transaction.enumerateRows(inCollection: collection) { key, object, _, _ in + guard let senderPublicKey = key as? String, let ratchet = object as? ClosedGroupRatchet else { return } + let senderKey = ClosedGroupSenderKey(chainKey: Data(hex: ratchet.chainKey), keyIndex: ratchet.keyIndex, senderPublicKey: senderPublicKey) + result.insert(senderKey) } } return result diff --git a/SignalServiceKit/src/Protos/Generated/SSKProto.swift b/SignalServiceKit/src/Protos/Generated/SSKProto.swift index 7ba70cb45..7c91cc5f7 100644 --- a/SignalServiceKit/src/Protos/Generated/SSKProto.swift +++ b/SignalServiceKit/src/Protos/Generated/SSKProto.swift @@ -3293,13 +3293,13 @@ extension SSKProtoDataMessageLokiProfile.SSKProtoDataMessageLokiProfileBuilder { // MARK: - SSKProtoDataMessageClosedGroupUpdateSenderKeyBuilder - @objc public class func builder(chainKey: Data, keyIndex: UInt32) -> SSKProtoDataMessageClosedGroupUpdateSenderKeyBuilder { - return SSKProtoDataMessageClosedGroupUpdateSenderKeyBuilder(chainKey: chainKey, keyIndex: keyIndex) + @objc public class func builder(chainKey: Data, keyIndex: UInt32, senderPublicKey: String) -> SSKProtoDataMessageClosedGroupUpdateSenderKeyBuilder { + return SSKProtoDataMessageClosedGroupUpdateSenderKeyBuilder(chainKey: chainKey, keyIndex: keyIndex, senderPublicKey: senderPublicKey) } // asBuilder() constructs a builder that reflects the proto's contents. @objc public func asBuilder() -> SSKProtoDataMessageClosedGroupUpdateSenderKeyBuilder { - let builder = SSKProtoDataMessageClosedGroupUpdateSenderKeyBuilder(chainKey: chainKey, keyIndex: keyIndex) + let builder = SSKProtoDataMessageClosedGroupUpdateSenderKeyBuilder(chainKey: chainKey, keyIndex: keyIndex, senderPublicKey: senderPublicKey) return builder } @@ -3309,11 +3309,12 @@ extension SSKProtoDataMessageLokiProfile.SSKProtoDataMessageLokiProfileBuilder { @objc fileprivate override init() {} - @objc fileprivate init(chainKey: Data, keyIndex: UInt32) { + @objc fileprivate init(chainKey: Data, keyIndex: UInt32, senderPublicKey: String) { super.init() setChainKey(chainKey) setKeyIndex(keyIndex) + setSenderPublicKey(senderPublicKey) } @objc public func setChainKey(_ valueParam: Data) { @@ -3324,6 +3325,10 @@ extension SSKProtoDataMessageLokiProfile.SSKProtoDataMessageLokiProfileBuilder { proto.keyIndex = valueParam } + @objc public func setSenderPublicKey(_ valueParam: String) { + proto.senderPublicKey = valueParam + } + @objc public func build() throws -> SSKProtoDataMessageClosedGroupUpdateSenderKey { return try SSKProtoDataMessageClosedGroupUpdateSenderKey.parseProto(proto) } @@ -3339,12 +3344,16 @@ extension SSKProtoDataMessageLokiProfile.SSKProtoDataMessageLokiProfileBuilder { @objc public let keyIndex: UInt32 + @objc public let senderPublicKey: String + private init(proto: SignalServiceProtos_DataMessage.ClosedGroupUpdate.SenderKey, chainKey: Data, - keyIndex: UInt32) { + keyIndex: UInt32, + senderPublicKey: String) { self.proto = proto self.chainKey = chainKey self.keyIndex = keyIndex + self.senderPublicKey = senderPublicKey } @objc @@ -3368,13 +3377,19 @@ extension SSKProtoDataMessageLokiProfile.SSKProtoDataMessageLokiProfileBuilder { } let keyIndex = proto.keyIndex + guard proto.hasSenderPublicKey else { + throw SSKProtoError.invalidProtobuf(description: "\(logTag) missing required field: senderPublicKey") + } + let senderPublicKey = proto.senderPublicKey + // MARK: - Begin Validation Logic for SSKProtoDataMessageClosedGroupUpdateSenderKey - // MARK: - End Validation Logic for SSKProtoDataMessageClosedGroupUpdateSenderKey - let result = SSKProtoDataMessageClosedGroupUpdateSenderKey(proto: proto, chainKey: chainKey, - keyIndex: keyIndex) + keyIndex: keyIndex, + senderPublicKey: senderPublicKey) return result } @@ -3408,14 +3423,14 @@ extension SSKProtoDataMessageClosedGroupUpdateSenderKey.SSKProtoDataMessageClose @objc public enum SSKProtoDataMessageClosedGroupUpdateType: Int32 { case new = 0 case info = 1 - case chainKey = 2 + case senderKey = 2 } private class func SSKProtoDataMessageClosedGroupUpdateTypeWrap(_ value: SignalServiceProtos_DataMessage.ClosedGroupUpdate.TypeEnum) -> SSKProtoDataMessageClosedGroupUpdateType { switch value { case .new: return .new case .info: return .info - case .chainKey: return .chainKey + case .senderKey: return .senderKey } } @@ -3423,7 +3438,7 @@ extension SSKProtoDataMessageClosedGroupUpdateSenderKey.SSKProtoDataMessageClose switch value { case .new: return .new case .info: return .info - case .chainKey: return .chainKey + case .senderKey: return .senderKey } } diff --git a/SignalServiceKit/src/Protos/Generated/SignalService.pb.swift b/SignalServiceKit/src/Protos/Generated/SignalService.pb.swift index 89ef5776a..226e74999 100644 --- a/SignalServiceKit/src/Protos/Generated/SignalService.pb.swift +++ b/SignalServiceKit/src/Protos/Generated/SignalService.pb.swift @@ -1546,14 +1546,14 @@ struct SignalServiceProtos_DataMessage { enum TypeEnum: SwiftProtobuf.Enum { typealias RawValue = Int - /// groupPublicKey, name, groupPrivateKey, chainKeys, members, admins + /// groupPublicKey, name, groupPrivateKey, senderKeys, members, admins case new // = 0 - /// groupPublicKey, name, chainKeys, members, admins + /// groupPublicKey, name, senderKeys, members, admins case info // = 1 - /// groupPublicKey, chainKeys - case chainKey // = 2 + /// groupPublicKey, senderKeys + case senderKey // = 2 init() { self = .new @@ -1563,7 +1563,7 @@ struct SignalServiceProtos_DataMessage { switch rawValue { case 0: self = .new case 1: self = .info - case 2: self = .chainKey + case 2: self = .senderKey default: return nil } } @@ -1572,7 +1572,7 @@ struct SignalServiceProtos_DataMessage { switch self { case .new: return 0 case .info: return 1 - case .chainKey: return 2 + case .senderKey: return 2 } } @@ -1603,12 +1603,23 @@ struct SignalServiceProtos_DataMessage { /// Clears the value of `keyIndex`. Subsequent reads from it will return its default value. mutating func clearKeyIndex() {self._keyIndex = nil} + /// @required + var senderPublicKey: String { + get {return _senderPublicKey ?? String()} + set {_senderPublicKey = newValue} + } + /// Returns true if `senderPublicKey` has been explicitly set. + var hasSenderPublicKey: Bool {return self._senderPublicKey != nil} + /// Clears the value of `senderPublicKey`. Subsequent reads from it will return its default value. + mutating func clearSenderPublicKey() {self._senderPublicKey = nil} + var unknownFields = SwiftProtobuf.UnknownStorage() init() {} fileprivate var _chainKey: Data? = nil fileprivate var _keyIndex: UInt32? = nil + fileprivate var _senderPublicKey: String? = nil } init() {} @@ -4083,7 +4094,7 @@ extension SignalServiceProtos_DataMessage.ClosedGroupUpdate.TypeEnum: SwiftProto static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ 0: .same(proto: "NEW"), 1: .same(proto: "INFO"), - 2: .same(proto: "CHAIN_KEY"), + 2: .same(proto: "SENDER_KEY"), ] } @@ -4092,6 +4103,7 @@ extension SignalServiceProtos_DataMessage.ClosedGroupUpdate.SenderKey: SwiftProt static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ 1: .same(proto: "chainKey"), 2: .same(proto: "keyIndex"), + 3: .same(proto: "senderPublicKey"), ] mutating func decodeMessage(decoder: inout D) throws { @@ -4099,6 +4111,7 @@ extension SignalServiceProtos_DataMessage.ClosedGroupUpdate.SenderKey: SwiftProt switch fieldNumber { case 1: try decoder.decodeSingularBytesField(value: &self._chainKey) case 2: try decoder.decodeSingularUInt32Field(value: &self._keyIndex) + case 3: try decoder.decodeSingularStringField(value: &self._senderPublicKey) default: break } } @@ -4111,12 +4124,16 @@ extension SignalServiceProtos_DataMessage.ClosedGroupUpdate.SenderKey: SwiftProt if let v = self._keyIndex { try visitor.visitSingularUInt32Field(value: v, fieldNumber: 2) } + if let v = self._senderPublicKey { + try visitor.visitSingularStringField(value: v, fieldNumber: 3) + } try unknownFields.traverse(visitor: &visitor) } static func ==(lhs: SignalServiceProtos_DataMessage.ClosedGroupUpdate.SenderKey, rhs: SignalServiceProtos_DataMessage.ClosedGroupUpdate.SenderKey) -> Bool { if lhs._chainKey != rhs._chainKey {return false} if lhs._keyIndex != rhs._keyIndex {return false} + if lhs._senderPublicKey != rhs._senderPublicKey {return false} if lhs.unknownFields != rhs.unknownFields {return false} return true }