Implement sender key requesting

pull/223/head
nielsandriesse 4 years ago
parent ed9ac3a60c
commit 88aaebefaa

@ -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
}

@ -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() ])

@ -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<String>, transaction: YapDatabaseReadWriteTransaction) -> Promise<TSGroupThread> {
@ -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

@ -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..<Int(SharedSenderKeysImplementation.ivSize)]
let ciphertext = ivAndCiphertext[Int(SharedSenderKeysImplementation.ivSize)...]
let gcm = GCM(iv: iv.bytes, tagLength: Int(SharedSenderKeysImplementation.gcmTagSize), mode: .combined)
guard let messageKey = ratchet.messageKeys.last else { throw RatchetingError.messageKeyMissing(targetKeyIndex: keyIndex, groupPublicKey: groupPublicKey, senderPublicKey: senderPublicKey) }
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))
}

Loading…
Cancel
Save