Allow admins to leave & fix remaining issues
This commit is contained in:
parent
c7d3f3e32d
commit
d5e1237b0c
|
@ -931,7 +931,7 @@ static CGRect oldframe;
|
|||
if (gThread.usesSharedSenderKeys) {
|
||||
NSString *groupPublicKey = [LKGroupUtilities getDecodedGroupID:gThread.groupModel.groupId];
|
||||
[LKStorage writeSyncWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) {
|
||||
[[SNMessageSender leaveGroupWithPublicKey:groupPublicKey transaction:transaction] retainUntilComplete];
|
||||
[SNMessageSender leaveClosedGroupWithPublicKey:groupPublicKey using:transaction error:nil];
|
||||
}];
|
||||
}
|
||||
|
||||
|
|
|
@ -252,28 +252,22 @@ final class EditClosedGroupVC : BaseVC, UITableViewDataSource, UITableViewDelega
|
|||
guard members != Set(thread.groupModel.groupMemberIds) || name != thread.groupModel.groupName else {
|
||||
return popToConversationVC(self)
|
||||
}
|
||||
|
||||
/*
|
||||
ModalActivityIndicatorViewController.present(fromViewController: navigationController!, canCancel: false) { [weak self] _ in
|
||||
Storage.writeSync { [weak self] transaction in
|
||||
MessageSender.update(groupPublicKey, with: members, name: name, transaction: transaction).done(on: DispatchQueue.main) {
|
||||
guard let self = self else { return }
|
||||
self.dismiss(animated: true, completion: nil) // Dismiss the loader
|
||||
popToConversationVC(self)
|
||||
}.catch(on: DispatchQueue.main) { error in
|
||||
guard let self = self else { return }
|
||||
self.dismiss(animated: true, completion: nil) // Dismiss the loader
|
||||
self.showError(title: "Couldn't Update Group", message: "Please check your internet connection and try again.")
|
||||
}
|
||||
if !members.contains(getUserHexEncodedPublicKey()) {
|
||||
guard members.intersection(Set(thread.groupModel.groupMemberIds).subtracting([ getUserHexEncodedPublicKey() ])) == members else {
|
||||
return showError(title: "Couldn't Update Group", message: "Can't leave while adding or removing other members.")
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
Storage.write(with: { [weak self] transaction in
|
||||
do {
|
||||
try MessageSender.updateV2(groupPublicKey, with: members, name: name, transaction: transaction)
|
||||
if !members.contains(getUserHexEncodedPublicKey()) {
|
||||
try MessageSender.leaveV2(groupPublicKey, using: transaction)
|
||||
} else {
|
||||
try MessageSender.updateV2(groupPublicKey, with: members, name: name, transaction: transaction)
|
||||
}
|
||||
} catch {
|
||||
self?.showError(title: "Couldn't Update Group", message: "Please check your internet connection and try again.")
|
||||
DispatchQueue.main.async {
|
||||
self?.showError(title: "Couldn't Update Group", message: "Please check your internet connection and try again.")
|
||||
}
|
||||
}
|
||||
}, completion: { [weak self] in
|
||||
guard let self = self else { return }
|
||||
|
@ -289,7 +283,7 @@ final class EditClosedGroupVC : BaseVC, UITableViewDataSource, UITableViewDelega
|
|||
}
|
||||
|
||||
private func canBeRemoved(_ publicKey: String) -> Bool {
|
||||
return !isAdmin(publicKey) && !isCurrentUser(publicKey)
|
||||
return !isAdmin(publicKey) || isCurrentUser(publicKey)
|
||||
}
|
||||
|
||||
private func isAdmin(_ publicKey: String) -> Bool {
|
||||
|
|
|
@ -402,7 +402,11 @@ final class HomeVC : BaseVC, UITableViewDataSource, UITableViewDelegate, UIScrol
|
|||
guard let thread = self.thread(at: indexPath.row) else { return [] }
|
||||
let openGroup = Storage.shared.getOpenGroup(for: thread.uniqueId!)
|
||||
let delete = UITableViewRowAction(style: .destructive, title: NSLocalizedString("TXT_DELETE_TITLE", comment: "")) { [weak self] _, _ in
|
||||
let alert = UIAlertController(title: NSLocalizedString("CONVERSATION_DELETE_CONFIRMATION_ALERT_TITLE", comment: ""), message: NSLocalizedString("CONVERSATION_DELETE_CONFIRMATION_ALERT_MESSAGE", comment: ""), preferredStyle: .alert)
|
||||
var message = NSLocalizedString("CONVERSATION_DELETE_CONFIRMATION_ALERT_MESSAGE", comment: "")
|
||||
if let thread = thread as? TSGroupThread, thread.usesSharedSenderKeys, thread.groupModel.groupAdminIds.contains(getUserHexEncodedPublicKey()) {
|
||||
message = "Because you are the creator of this group it will be deleted for everyone. This cannot be undone."
|
||||
}
|
||||
let alert = UIAlertController(title: NSLocalizedString("CONVERSATION_DELETE_CONFIRMATION_ALERT_TITLE", comment: ""), message: message, preferredStyle: .alert)
|
||||
alert.addAction(UIAlertAction(title: NSLocalizedString("TXT_DELETE_TITLE", comment: ""), style: .destructive) { _ in
|
||||
Storage.write { transaction in
|
||||
Storage.shared.cancelPendingMessageSendJobs(for: thread.uniqueId!, using: transaction)
|
||||
|
@ -420,11 +424,14 @@ final class HomeVC : BaseVC, UITableViewDataSource, UITableViewDelegate, UIScrol
|
|||
} else if let thread = thread as? TSGroupThread, thread.usesSharedSenderKeys == true {
|
||||
let groupID = thread.groupModel.groupId
|
||||
let groupPublicKey = LKGroupUtilities.getDecodedGroupID(groupID)
|
||||
let _ = MessageSender.leave(groupPublicKey, using: transaction).ensure {
|
||||
do {
|
||||
try MessageSender.leaveV2(groupPublicKey, using: transaction)
|
||||
Storage.write { transaction in
|
||||
thread.removeAllThreadInteractions(with: transaction)
|
||||
thread.remove(with: transaction)
|
||||
}
|
||||
} catch {
|
||||
// TODO: Handle
|
||||
}
|
||||
} else {
|
||||
thread.removeAllThreadInteractions(with: transaction)
|
||||
|
|
|
@ -170,11 +170,6 @@ final class NewClosedGroupVC : BaseVC, UITableViewDataSource, UITableViewDelegat
|
|||
ModalActivityIndicatorViewController.present(fromViewController: navigationController!, canCancel: false) { [weak self] _ in
|
||||
var promise: Promise<TSGroupThread>!
|
||||
Storage.writeSync { transaction in
|
||||
|
||||
/*
|
||||
promise = MessageSender.createClosedGroup(name: name, members: selectedContacts, transaction: transaction)
|
||||
*/
|
||||
|
||||
promise = MessageSender.createV2ClosedGroup(name: name, members: selectedContacts, transaction: transaction)
|
||||
}
|
||||
let _ = promise.done(on: DispatchQueue.main) { thread in
|
||||
|
|
|
@ -122,7 +122,6 @@ extension Storage {
|
|||
var result: Set<String> = []
|
||||
Storage.read { transaction in
|
||||
result = result.union(Set(transaction.allKeys(inCollection: Storage.closedGroupPublicKeyCollection)))
|
||||
result = result.union(Set(transaction.allKeys(inCollection: Storage.closedGroupPrivateKeyCollection)))
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
|
|
@ -80,8 +80,8 @@ public final class ClosedGroupUpdateV2 : ControlMessage {
|
|||
case .new(let publicKey, let name, let encryptionKeyPair, let members, let admins):
|
||||
return !publicKey.isEmpty && !name.isEmpty && !encryptionKeyPair.publicKey.isEmpty
|
||||
&& !encryptionKeyPair.privateKey.isEmpty && !members.isEmpty && !admins.isEmpty
|
||||
case .update(let name, let members):
|
||||
return !name.isEmpty && !members.isEmpty
|
||||
case .update(let name, _):
|
||||
return !name.isEmpty
|
||||
case .encryptionKeyPair: return true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ FOUNDATION_EXPORT const unsigned char SessionMessagingKitVersionString[];
|
|||
#import <SessionMessagingKit/AppReadiness.h>
|
||||
#import <SessionMessagingKit/Environment.h>
|
||||
#import <SessionMessagingKit/NotificationsProtocol.h>
|
||||
#import <SessionMessagingKit/NSData+messagePadding.h>
|
||||
#import <SessionMessagingKit/OWSAudioPlayer.h>
|
||||
#import <SessionMessagingKit/OWSBackgroundTask.h>
|
||||
#import <SessionMessagingKit/OWSBackupFragment.h>
|
||||
|
|
|
@ -12,7 +12,6 @@ extension MessageReceiver {
|
|||
case let message as ReadReceipt: handleReadReceipt(message, using: transaction)
|
||||
case let message as TypingIndicator: handleTypingIndicator(message, using: transaction)
|
||||
case let message as ClosedGroupUpdateV2: handleClosedGroupUpdateV2(message, using: transaction)
|
||||
case let message as ClosedGroupUpdate: handleClosedGroupUpdate(message, using: transaction)
|
||||
case let message as ExpirationTimerUpdate: handleExpirationTimerUpdate(message, using: transaction)
|
||||
case let message as VisibleMessage: try handleVisibleMessage(message, associatedWithProto: proto, openGroupID: openGroupID, isBackgroundPoll: isBackgroundPoll, using: transaction)
|
||||
default: fatalError()
|
||||
|
@ -224,17 +223,6 @@ extension MessageReceiver {
|
|||
case .encryptionKeyPair: handleGroupEncryptionKeyPair(message, using: transaction)
|
||||
}
|
||||
}
|
||||
|
||||
private static func handleClosedGroupUpdate(_ message: ClosedGroupUpdate, using transaction: Any) {
|
||||
switch message.kind! {
|
||||
case .new: handleNewGroup(message, using: transaction)
|
||||
case .info: handleGroupUpdate(message, using: transaction)
|
||||
case .senderKeyRequest: handleSenderKeyRequest(message, using: transaction)
|
||||
case .senderKey: handleSenderKey(message, using: transaction)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - V2
|
||||
|
||||
private static func handleNewGroupV2(_ message: ClosedGroupUpdateV2, using transaction: Any) {
|
||||
// Prepare
|
||||
|
@ -286,8 +274,8 @@ extension MessageReceiver {
|
|||
guard Set(group.groupMemberIds).contains(message.sender!) else {
|
||||
return SNLog("Ignoring closed group update message from non-member.")
|
||||
}
|
||||
// Check that the admin wasn't removed
|
||||
guard members.contains(group.groupAdminIds.first!) else {
|
||||
// Check that the admin wasn't removed unless the group was destroyed entirely
|
||||
if !members.contains(group.groupAdminIds.first!) && !members.isEmpty {
|
||||
return SNLog("Ignoring invalid closed group update message.")
|
||||
}
|
||||
// Remove the group from the user's set of public keys to poll for if the current user was removed
|
||||
|
@ -363,164 +351,4 @@ extension MessageReceiver {
|
|||
Storage.shared.addClosedGroupEncryptionKeyPair(keyPair, for: groupPublicKey, using: transaction)
|
||||
SNLog("Received a new closed group encryption key pair.")
|
||||
}
|
||||
|
||||
|
||||
|
||||
// MARK: - V1
|
||||
|
||||
private static func handleNewGroup(_ message: ClosedGroupUpdate, using transaction: Any) {
|
||||
guard case let .new(groupPublicKeyAsData, name, groupPrivateKey, senderKeys, membersAsData, adminsAsData) = message.kind else { return }
|
||||
let transaction = transaction as! YapDatabaseReadWriteTransaction
|
||||
let groupPublicKey = groupPublicKeyAsData.toHexString()
|
||||
let members = membersAsData.map { $0.toHexString() }
|
||||
let admins = adminsAsData.map { $0.toHexString() }
|
||||
// Persist the ratchets
|
||||
senderKeys.forEach { senderKey in
|
||||
guard members.contains(senderKey.publicKey.toHexString()) else { return }
|
||||
let ratchet = ClosedGroupRatchet(chainKey: senderKey.chainKey.toHexString(), keyIndex: UInt(senderKey.keyIndex), messageKeys: [])
|
||||
Storage.shared.setClosedGroupRatchet(for: groupPublicKey, senderPublicKey: senderKey.publicKey.toHexString(), ratchet: ratchet, using: transaction)
|
||||
}
|
||||
// Sort out any discrepancies between the provided sender keys and what's required
|
||||
let missingSenderKeys = Set(members).subtracting(senderKeys.map { $0.publicKey.toHexString() })
|
||||
let userPublicKey = getUserHexEncodedPublicKey()
|
||||
if missingSenderKeys.contains(userPublicKey) {
|
||||
let userRatchet = SharedSenderKeys.generateRatchet(for: groupPublicKey, senderPublicKey: userPublicKey, using: transaction)
|
||||
let userSenderKey = ClosedGroupSenderKey(chainKey: Data(hex: userRatchet.chainKey), keyIndex: userRatchet.keyIndex, publicKey: Data(hex: userPublicKey))
|
||||
members.forEach { member in
|
||||
guard member != userPublicKey else { return }
|
||||
let thread = TSContactThread.getOrCreateThread(withContactId: member, transaction: transaction)
|
||||
thread.save(with: transaction)
|
||||
let closedGroupUpdateKind = ClosedGroupUpdate.Kind.senderKey(groupPublicKey: Data(hex: groupPublicKey), senderKey: userSenderKey)
|
||||
let closedGroupUpdate = ClosedGroupUpdate()
|
||||
closedGroupUpdate.kind = closedGroupUpdateKind
|
||||
MessageSender.send(closedGroupUpdate, in: thread, using: transaction)
|
||||
}
|
||||
}
|
||||
missingSenderKeys.subtracting([ userPublicKey ]).forEach { publicKey in
|
||||
MessageSender.shared.requestSenderKey(for: groupPublicKey, senderPublicKey: publicKey, using: transaction)
|
||||
}
|
||||
// Create the group
|
||||
let groupID = LKGroupUtilities.getEncodedClosedGroupIDAsData(groupPublicKey)
|
||||
let group = TSGroupModel(title: name, memberIds: members, image: nil, groupId: groupID, groupType: .closedGroup, adminIds: admins)
|
||||
let thread: TSGroupThread
|
||||
if let t = TSGroupThread.fetch(uniqueId: TSGroupThread.threadId(fromGroupId: groupID), transaction: transaction) {
|
||||
thread = t
|
||||
thread.setGroupModel(group, with: transaction)
|
||||
} else {
|
||||
thread = TSGroupThread.getOrCreateThread(with: group, transaction: transaction)
|
||||
thread.usesSharedSenderKeys = true
|
||||
thread.save(with: transaction)
|
||||
}
|
||||
// Add the group to the user's set of public keys to poll for
|
||||
Storage.shared.setClosedGroupPrivateKey(groupPrivateKey.toHexString(), for: groupPublicKey, using: transaction)
|
||||
// Notify the PN server
|
||||
let _ = PushNotificationAPI.performOperation(.subscribe, for: groupPublicKey, publicKey: getUserHexEncodedPublicKey())
|
||||
// Notify the user
|
||||
let infoMessage = TSInfoMessage(timestamp: NSDate.ows_millisecondTimeStamp(), in: thread, messageType: .typeGroupUpdate)
|
||||
infoMessage.save(with: transaction)
|
||||
}
|
||||
|
||||
private static func handleGroupUpdate(_ message: ClosedGroupUpdate, using transaction: Any) {
|
||||
guard case let .info(groupPublicKeyAsData, name, senderKeys, membersAsData, adminsAsData) = message.kind else { return }
|
||||
let transaction = transaction as! YapDatabaseReadWriteTransaction
|
||||
let groupPublicKey = groupPublicKeyAsData.toHexString()
|
||||
let members = membersAsData.map { $0.toHexString() }
|
||||
let admins = adminsAsData.map { $0.toHexString() }
|
||||
// Get the group
|
||||
let groupID = LKGroupUtilities.getEncodedClosedGroupIDAsData(groupPublicKey)
|
||||
guard let thread = TSGroupThread.fetch(uniqueId: TSGroupThread.threadId(fromGroupId: groupID), transaction: transaction) else {
|
||||
return SNLog("Ignoring closed group info message for nonexistent group.")
|
||||
}
|
||||
let group = thread.groupModel
|
||||
// Check that the sender is a member of the group (before the update)
|
||||
guard Set(group.groupMemberIds).contains(message.sender!) else {
|
||||
return SNLog("Ignoring closed group info message from non-member.")
|
||||
}
|
||||
// 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.shared.setClosedGroupRatchet(for: groupPublicKey, senderPublicKey: senderKey.publicKey.toHexString(), ratchet: ratchet, using: transaction)
|
||||
}
|
||||
// Delete all ratchets and either:
|
||||
// • Send out the user's new ratchet using established channels if other members of the group left or were removed
|
||||
// • Remove the group from the user's set of public keys to poll for if the current user was among the members that were removed
|
||||
let oldMembers = group.groupMemberIds
|
||||
let userPublicKey = getUserHexEncodedPublicKey()
|
||||
let wasUserRemoved = !members.contains(userPublicKey)
|
||||
if Set(members).intersection(oldMembers) != Set(oldMembers) {
|
||||
let allOldRatchets = Storage.shared.getAllClosedGroupRatchets(for: groupPublicKey)
|
||||
for (senderPublicKey, oldRatchet) in allOldRatchets {
|
||||
let collection = ClosedGroupRatchetCollectionType.old
|
||||
Storage.shared.setClosedGroupRatchet(for: groupPublicKey, senderPublicKey: senderPublicKey, ratchet: oldRatchet, in: collection, using: transaction)
|
||||
}
|
||||
Storage.shared.removeAllClosedGroupRatchets(for: groupPublicKey, using: transaction)
|
||||
if wasUserRemoved {
|
||||
Storage.shared.removeClosedGroupPrivateKey(for: groupPublicKey, using: transaction)
|
||||
// Notify the PN server
|
||||
let _ = PushNotificationAPI.performOperation(.unsubscribe, for: groupPublicKey, publicKey: userPublicKey)
|
||||
} else {
|
||||
let userRatchet = SharedSenderKeys.generateRatchet(for: groupPublicKey, senderPublicKey: userPublicKey, using: transaction)
|
||||
let userSenderKey = ClosedGroupSenderKey(chainKey: Data(hex: userRatchet.chainKey), keyIndex: userRatchet.keyIndex, publicKey: Data(hex: userPublicKey))
|
||||
members.forEach { member in
|
||||
guard member != userPublicKey else { return }
|
||||
let thread = TSContactThread.getOrCreateThread(withContactId: member, transaction: transaction)
|
||||
thread.save(with: transaction)
|
||||
let closedGroupUpdateKind = ClosedGroupUpdate.Kind.senderKey(groupPublicKey: Data(hex: groupPublicKey), senderKey: userSenderKey)
|
||||
let closedGroupUpdate = ClosedGroupUpdate()
|
||||
closedGroupUpdate.kind = closedGroupUpdateKind
|
||||
MessageSender.send(closedGroupUpdate, in: thread, using: transaction)
|
||||
}
|
||||
}
|
||||
}
|
||||
// Update the group
|
||||
let newGroupModel = TSGroupModel(title: name, memberIds: members, image: nil, groupId: groupID, groupType: .closedGroup, adminIds: admins)
|
||||
thread.setGroupModel(newGroupModel, with: transaction)
|
||||
// Notify the user if needed
|
||||
if Set(members) != Set(oldMembers) || Set(admins) != Set(group.groupAdminIds) || name != group.groupName {
|
||||
let infoMessageType: TSInfoMessageType = wasUserRemoved ? .typeGroupQuit : .typeGroupUpdate
|
||||
let updateInfo = group.getInfoStringAboutUpdate(to: newGroupModel)
|
||||
let infoMessage = TSInfoMessage(timestamp: NSDate.ows_millisecondTimeStamp(), in: thread, messageType: infoMessageType, customMessage: updateInfo)
|
||||
infoMessage.save(with: transaction)
|
||||
}
|
||||
}
|
||||
|
||||
private static func handleSenderKeyRequest(_ message: ClosedGroupUpdate, using transaction: Any) {
|
||||
guard case let .senderKeyRequest(groupPublicKeyAsData) = message.kind else { return }
|
||||
let transaction = transaction as! YapDatabaseReadWriteTransaction
|
||||
let userPublicKey = getUserHexEncodedPublicKey()
|
||||
let groupPublicKey = groupPublicKeyAsData.toHexString()
|
||||
let groupID = LKGroupUtilities.getEncodedClosedGroupIDAsData(groupPublicKey)
|
||||
guard let groupThread = TSGroupThread.fetch(uniqueId: TSGroupThread.threadId(fromGroupId: groupID), transaction: transaction) else {
|
||||
return SNLog("Ignoring closed group sender key request for nonexistent group.")
|
||||
}
|
||||
let group = groupThread.groupModel
|
||||
// Check that the requesting user is a member of the group
|
||||
let members = Set(group.groupMemberIds)
|
||||
guard members.contains(message.sender!) else {
|
||||
return SNLog("Ignoring closed group sender key request from non-member.")
|
||||
}
|
||||
// Respond to the request
|
||||
SNLog("Responding to sender key request from: \(message.sender!).")
|
||||
let userRatchet = Storage.shared.getClosedGroupRatchet(for: groupPublicKey, senderPublicKey: userPublicKey)
|
||||
?? SharedSenderKeys.generateRatchet(for: groupPublicKey, senderPublicKey: userPublicKey, using: transaction)
|
||||
let userSenderKey = ClosedGroupSenderKey(chainKey: Data(hex: userRatchet.chainKey), keyIndex: userRatchet.keyIndex, publicKey: Data(hex: userPublicKey))
|
||||
let thread = TSContactThread.getOrCreateThread(withContactId: message.sender!, transaction: transaction)
|
||||
thread.save(with: transaction)
|
||||
let closedGroupUpdateKind = ClosedGroupUpdate.Kind.senderKey(groupPublicKey: Data(hex: groupPublicKey), senderKey: userSenderKey)
|
||||
let closedGroupUpdate = ClosedGroupUpdate()
|
||||
closedGroupUpdate.kind = closedGroupUpdateKind
|
||||
MessageSender.send(closedGroupUpdate, in: thread, using: transaction)
|
||||
}
|
||||
|
||||
private static func handleSenderKey(_ message: ClosedGroupUpdate, using transaction: Any) {
|
||||
guard case let .senderKey(groupPublicKeyAsData, senderKey) = message.kind else { return }
|
||||
let groupPublicKey = groupPublicKeyAsData.toHexString()
|
||||
guard senderKey.publicKey.toHexString() == message.sender! else {
|
||||
return SNLog("Ignoring invalid closed group sender key.")
|
||||
}
|
||||
// Store the sender key
|
||||
SNLog("Received a sender key from: \(message.sender!).")
|
||||
let ratchet = ClosedGroupRatchet(chainKey: senderKey.chainKey.toHexString(), keyIndex: UInt(senderKey.keyIndex), messageKeys: [])
|
||||
Storage.shared.setClosedGroupRatchet(for: groupPublicKey, senderPublicKey: message.sender!, ratchet: ratchet, using: transaction)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -71,31 +71,24 @@ public enum MessageReceiver {
|
|||
(plaintext, sender) = try decryptWithSessionProtocol(ciphertext: ciphertext, using: userX25519KeyPair)
|
||||
case .closedGroupCiphertext:
|
||||
guard let hexEncodedGroupPublicKey = envelope.source, SNMessagingKitConfiguration.shared.storage.isClosedGroup(hexEncodedGroupPublicKey) else { throw Error.invalidGroupPublicKey }
|
||||
do {
|
||||
var encryptionKeyPairs = Storage.shared.getClosedGroupEncryptionKeyPairs(for: hexEncodedGroupPublicKey)
|
||||
guard !encryptionKeyPairs.isEmpty else { throw Error.noGroupKeyPair }
|
||||
// Loop through all known group key pairs in reverse order (i.e. try the latest key pair first (which'll more than
|
||||
// likely be the one we want) but try older ones in case that didn't work)
|
||||
var encryptionKeyPair = encryptionKeyPairs.removeLast()
|
||||
func decrypt() throws {
|
||||
do {
|
||||
(plaintext, sender) = try decryptWithSessionProtocol(ciphertext: ciphertext, using: encryptionKeyPair)
|
||||
} catch {
|
||||
if !encryptionKeyPairs.isEmpty {
|
||||
encryptionKeyPair = encryptionKeyPairs.removeLast()
|
||||
try decrypt()
|
||||
} else {
|
||||
throw error
|
||||
}
|
||||
var encryptionKeyPairs = Storage.shared.getClosedGroupEncryptionKeyPairs(for: hexEncodedGroupPublicKey)
|
||||
guard !encryptionKeyPairs.isEmpty else { throw Error.noGroupKeyPair }
|
||||
// Loop through all known group key pairs in reverse order (i.e. try the latest key pair first (which'll more than
|
||||
// likely be the one we want) but try older ones in case that didn't work)
|
||||
var encryptionKeyPair = encryptionKeyPairs.removeLast()
|
||||
func decrypt() throws {
|
||||
do {
|
||||
(plaintext, sender) = try decryptWithSessionProtocol(ciphertext: ciphertext, using: encryptionKeyPair)
|
||||
} catch {
|
||||
if !encryptionKeyPairs.isEmpty {
|
||||
encryptionKeyPair = encryptionKeyPairs.removeLast()
|
||||
try decrypt()
|
||||
} else {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
try decrypt()
|
||||
} catch {
|
||||
// Fall back on the V1 method
|
||||
guard let privateKey = SNMessagingKitConfiguration.shared.storage.getClosedGroupPrivateKey(for: hexEncodedGroupPublicKey) else { throw Error.noGroupKeyPair }
|
||||
let keyPair = try ECKeyPair(publicKeyData: Data(hex: hexEncodedGroupPublicKey.removing05PrefixIfNeeded()), privateKeyData: Data(hex: privateKey))
|
||||
(plaintext, sender) = try decryptWithSessionProtocol(ciphertext: ciphertext, using: keyPair)
|
||||
}
|
||||
try decrypt()
|
||||
groupPublicKey = envelope.source
|
||||
default: throw Error.unknownEnvelopeType
|
||||
}
|
||||
|
@ -117,7 +110,6 @@ public enum MessageReceiver {
|
|||
if let readReceipt = ReadReceipt.fromProto(proto) { return readReceipt }
|
||||
if let typingIndicator = TypingIndicator.fromProto(proto) { return typingIndicator }
|
||||
if let closedGroupUpdate = ClosedGroupUpdateV2.fromProto(proto) { return closedGroupUpdate }
|
||||
if let closedGroupUpdate = ClosedGroupUpdate.fromProto(proto) { return closedGroupUpdate }
|
||||
if let expirationTimerUpdate = ExpirationTimerUpdate.fromProto(proto) { return expirationTimerUpdate }
|
||||
if let visibleMessage = VisibleMessage.fromProto(proto) { return visibleMessage }
|
||||
return nil
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
import PromiseKit
|
||||
import SessionProtocolKit
|
||||
|
||||
extension MessageSender : SharedSenderKeysDelegate {
|
||||
|
||||
// MARK: - V2
|
||||
extension MessageSender {
|
||||
|
||||
public static func createV2ClosedGroup(name: String, members: Set<String>, transaction: YapDatabaseReadWriteTransaction) -> Promise<TSGroupThread> {
|
||||
// Prepare
|
||||
|
@ -62,28 +60,33 @@ extension MessageSender : SharedSenderKeysDelegate {
|
|||
let oldMembers = Set(group.groupMemberIds)
|
||||
let newMembers = members.subtracting(oldMembers)
|
||||
let membersAsData = members.map { Data(hex: $0) }
|
||||
let removedMembers = oldMembers.subtracting(members)
|
||||
let admins = group.groupAdminIds
|
||||
let adminsAsData = admins.map { Data(hex: $0) }
|
||||
let isUserLeaving = !members.contains(userPublicKey)
|
||||
let wasAnyUserRemoved = !removedMembers.isEmpty
|
||||
let isCurrentUserAdmin = group.groupAdminIds.contains(getUserHexEncodedPublicKey())
|
||||
// Check preconditions
|
||||
guard let encryptionKeyPair = Storage.shared.getLatestClosedGroupEncryptionKeyPair(for: groupPublicKey) else {
|
||||
SNLog("Couldn't get key pair for closed group.")
|
||||
throw Error.noKeyPair
|
||||
}
|
||||
let removedMembers = oldMembers.subtracting(members)
|
||||
guard !removedMembers.contains(admins.first!) else {
|
||||
SNLog("Can't remove admin from closed group.")
|
||||
if removedMembers.contains(admins.first!) && !members.isEmpty {
|
||||
SNLog("Can't remove admin from closed group without removing everyone.")
|
||||
throw Error.invalidClosedGroupUpdate
|
||||
}
|
||||
let isUserLeaving = removedMembers.contains(userPublicKey)
|
||||
if isUserLeaving && (removedMembers.count != 1 || !newMembers.isEmpty) {
|
||||
SNLog("Can't remove self and add or remove others simultaneously.")
|
||||
throw Error.invalidClosedGroupUpdate
|
||||
if isUserLeaving && !members.isEmpty {
|
||||
guard removedMembers.count == 1 && newMembers.isEmpty else {
|
||||
SNLog("Can't remove self and add or remove others simultaneously.")
|
||||
throw Error.invalidClosedGroupUpdate
|
||||
}
|
||||
}
|
||||
// Send the update to the group
|
||||
let mainClosedGroupUpdate = ClosedGroupUpdateV2(kind: .update(name: name, members: membersAsData))
|
||||
if isUserLeaving {
|
||||
let _ = MessageSender.sendNonDurably(mainClosedGroupUpdate, in: thread, using: transaction).done {
|
||||
SNMessagingKitConfiguration.shared.storage.write { transaction in
|
||||
// Remove the group private key and unsubscribe from PNs
|
||||
// Remove the group from the database and unsubscribe from PNs
|
||||
Storage.shared.removeAllClosedGroupEncryptionKeyPairs(for: groupPublicKey, using: transaction)
|
||||
Storage.shared.removeClosedGroupPublicKey(groupPublicKey, using: transaction)
|
||||
let _ = PushNotificationAPI.performOperation(.unsubscribe, for: groupPublicKey, publicKey: userPublicKey)
|
||||
|
@ -92,8 +95,6 @@ extension MessageSender : SharedSenderKeysDelegate {
|
|||
} else {
|
||||
MessageSender.send(mainClosedGroupUpdate, in: thread, using: transaction)
|
||||
// Generate and distribute a new encryption key pair if needed
|
||||
let wasAnyUserRemoved = !removedMembers.isEmpty
|
||||
let isCurrentUserAdmin = group.groupAdminIds.contains(getUserHexEncodedPublicKey())
|
||||
if wasAnyUserRemoved && isCurrentUserAdmin {
|
||||
try generateAndSendNewEncryptionKeyPair(for: groupPublicKey, to: members.subtracting(newMembers), using: transaction)
|
||||
}
|
||||
|
@ -115,7 +116,8 @@ extension MessageSender : SharedSenderKeysDelegate {
|
|||
let infoMessage = TSInfoMessage(timestamp: NSDate.ows_millisecondTimeStamp(), in: thread, messageType: .typeGroupUpdate, customMessage: updateInfo)
|
||||
infoMessage.save(with: transaction)
|
||||
}
|
||||
|
||||
|
||||
@objc(leaveClosedGroupWithPublicKey:using:error:)
|
||||
public static func leaveV2(_ groupPublicKey: String, using transaction: YapDatabaseReadWriteTransaction) throws {
|
||||
let groupID = LKGroupUtilities.getEncodedClosedGroupIDAsData(groupPublicKey)
|
||||
let threadID = TSGroupThread.threadId(fromGroupId: groupID)
|
||||
|
@ -124,8 +126,14 @@ extension MessageSender : SharedSenderKeysDelegate {
|
|||
throw Error.noThread
|
||||
}
|
||||
let group = thread.groupModel
|
||||
var newMembers = Set(group.groupMemberIds)
|
||||
newMembers.remove(getUserHexEncodedPublicKey())
|
||||
let isCurrentUserAdmin = group.groupAdminIds.contains(getUserHexEncodedPublicKey())
|
||||
var newMembers: Set<String>
|
||||
if !isCurrentUserAdmin {
|
||||
newMembers = Set(group.groupMemberIds)
|
||||
newMembers.remove(getUserHexEncodedPublicKey())
|
||||
} else {
|
||||
newMembers = [] // If the admin leaves the group is destroyed
|
||||
}
|
||||
return try updateV2(groupPublicKey, with: newMembers, name: group.groupName!, transaction: transaction)
|
||||
}
|
||||
|
||||
|
@ -160,213 +168,4 @@ extension MessageSender : SharedSenderKeysDelegate {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// MARK: - V1
|
||||
|
||||
public static func createClosedGroup(name: String, members: Set<String>, transaction: YapDatabaseReadWriteTransaction) -> Promise<TSGroupThread> {
|
||||
// Prepare
|
||||
var members = members
|
||||
let userPublicKey = getUserHexEncodedPublicKey()
|
||||
// Generate a key pair for the group
|
||||
let groupKeyPair = Curve25519.generateKeyPair()
|
||||
let groupPublicKey = groupKeyPair.hexEncodedPublicKey // Includes the "05" prefix
|
||||
// Ensure the current user is included in the member list
|
||||
members.insert(userPublicKey)
|
||||
let membersAsData = members.map { Data(hex: $0) }
|
||||
// Create ratchets for all members
|
||||
let senderKeys: [ClosedGroupSenderKey] = members.map { publicKey in
|
||||
let ratchet = SharedSenderKeys.generateRatchet(for: groupPublicKey, senderPublicKey: publicKey, using: transaction)
|
||||
return ClosedGroupSenderKey(chainKey: Data(hex: ratchet.chainKey), keyIndex: ratchet.keyIndex, publicKey: Data(hex: publicKey))
|
||||
}
|
||||
// Create the group
|
||||
let admins = [ userPublicKey ]
|
||||
let adminsAsData = admins.map { Data(hex: $0) }
|
||||
let groupID = LKGroupUtilities.getEncodedClosedGroupIDAsData(groupPublicKey)
|
||||
let group = TSGroupModel(title: name, memberIds: [String](members), image: nil, groupId: groupID, groupType: .closedGroup, adminIds: admins)
|
||||
let thread = TSGroupThread.getOrCreateThread(with: group, transaction: transaction)
|
||||
thread.usesSharedSenderKeys = true
|
||||
thread.save(with: transaction)
|
||||
// Send a closed group update message to all members using established channels
|
||||
var promises: [Promise<Void>] = []
|
||||
for member in members {
|
||||
guard member != userPublicKey else { continue }
|
||||
let thread = TSContactThread.getOrCreateThread(withContactId: member, transaction: transaction)
|
||||
thread.save(with: transaction)
|
||||
let closedGroupUpdateKind = ClosedGroupUpdate.Kind.new(groupPublicKey: Data(hex: groupPublicKey), name: name,
|
||||
groupPrivateKey: groupKeyPair.privateKey, senderKeys: senderKeys, members: membersAsData, admins: adminsAsData)
|
||||
let closedGroupUpdate = ClosedGroupUpdate()
|
||||
closedGroupUpdate.kind = closedGroupUpdateKind
|
||||
let promise = MessageSender.sendNonDurably(closedGroupUpdate, in: thread, using: transaction)
|
||||
promises.append(promise)
|
||||
}
|
||||
// Add the group to the user's set of public keys to poll for
|
||||
Storage.shared.setClosedGroupPrivateKey(groupKeyPair.privateKey.toHexString(), for: groupPublicKey, using: transaction)
|
||||
// Notify the PN server
|
||||
promises.append(PushNotificationAPI.performOperation(.subscribe, for: groupPublicKey, publicKey: userPublicKey))
|
||||
// Notify the user
|
||||
let infoMessage = TSInfoMessage(timestamp: NSDate.ows_millisecondTimeStamp(), in: thread, messageType: .typeGroupUpdate)
|
||||
infoMessage.save(with: transaction)
|
||||
// Return
|
||||
return when(fulfilled: promises).map2 { thread }
|
||||
}
|
||||
|
||||
/// - Note: The returned promise is only relevant for group leaving.
|
||||
public static func update(_ groupPublicKey: String, with members: Set<String>, name: String, transaction: YapDatabaseReadWriteTransaction) -> Promise<Void> {
|
||||
let (promise, seal) = Promise<Void>.pending()
|
||||
let userPublicKey = getUserHexEncodedPublicKey()
|
||||
let groupID = LKGroupUtilities.getEncodedClosedGroupIDAsData(groupPublicKey)
|
||||
guard let thread = TSGroupThread.fetch(uniqueId: TSGroupThread.threadId(fromGroupId: groupID), transaction: transaction) else {
|
||||
SNLog("Can't update nonexistent closed group.")
|
||||
return Promise(error: Error.noThread)
|
||||
}
|
||||
let group = thread.groupModel
|
||||
let oldMembers = Set(group.groupMemberIds)
|
||||
let newMembers = members.subtracting(oldMembers)
|
||||
let membersAsData = members.map { Data(hex: $0) }
|
||||
let admins = group.groupAdminIds
|
||||
let adminsAsData = admins.map { Data(hex: $0) }
|
||||
guard let groupPrivateKey = Storage.shared.getClosedGroupPrivateKey(for: groupPublicKey) else {
|
||||
SNLog("Couldn't get private key for closed group.")
|
||||
return Promise(error: Error.noKeyPair)
|
||||
}
|
||||
let wasAnyUserRemoved = Set(members).intersection(oldMembers) != oldMembers
|
||||
let removedMembers = oldMembers.subtracting(members)
|
||||
let isUserLeaving = removedMembers.contains(userPublicKey)
|
||||
var newSenderKeys: [ClosedGroupSenderKey] = []
|
||||
if wasAnyUserRemoved {
|
||||
if isUserLeaving && removedMembers.count != 1 {
|
||||
SNLog("Can't remove self and others simultaneously.")
|
||||
return Promise(error: Error.invalidClosedGroupUpdate)
|
||||
}
|
||||
// Send the update to the existing members using established channels (don't include new ratchets as everyone should regenerate new ratchets individually)
|
||||
let promises: [Promise<Void>] = oldMembers.map { member in
|
||||
let thread = TSContactThread.getOrCreateThread(withContactId: member, transaction: transaction)
|
||||
thread.save(with: transaction)
|
||||
let closedGroupUpdateKind = ClosedGroupUpdate.Kind.info(groupPublicKey: Data(hex: groupPublicKey), name: name, senderKeys: [],
|
||||
members: membersAsData, admins: adminsAsData)
|
||||
let closedGroupUpdate = ClosedGroupUpdate()
|
||||
closedGroupUpdate.kind = closedGroupUpdateKind
|
||||
return MessageSender.sendNonDurably(closedGroupUpdate, in: thread, using: transaction)
|
||||
}
|
||||
when(resolved: promises).done2 { _ in seal.fulfill(()) }.catch2 { seal.reject($0) }
|
||||
let _ = promise.done {
|
||||
SNMessagingKitConfiguration.shared.storage.writeSync { transaction in
|
||||
let allOldRatchets = Storage.shared.getAllClosedGroupRatchets(for: groupPublicKey)
|
||||
for (senderPublicKey, oldRatchet) in allOldRatchets {
|
||||
let collection = ClosedGroupRatchetCollectionType.old
|
||||
Storage.shared.setClosedGroupRatchet(for: groupPublicKey, senderPublicKey: senderPublicKey, ratchet: oldRatchet, in: collection, using: transaction)
|
||||
}
|
||||
// Delete all ratchets (it's important that this happens * after * sending out the update)
|
||||
Storage.shared.removeAllClosedGroupRatchets(for: groupPublicKey, using: transaction)
|
||||
// Remove the group from the user's set of public keys to poll for if the user is leaving. Otherwise generate a new ratchet and
|
||||
// send it out to all members (minus the removed ones) using established channels.
|
||||
if isUserLeaving {
|
||||
Storage.shared.removeClosedGroupPrivateKey(for: groupPublicKey, using: transaction)
|
||||
// Notify the PN server
|
||||
let _ = PushNotificationAPI.performOperation(.unsubscribe, for: groupPublicKey, publicKey: userPublicKey)
|
||||
} else {
|
||||
// Send closed group update messages to any new members using established channels
|
||||
for member in newMembers {
|
||||
let transaction = transaction as! YapDatabaseReadWriteTransaction
|
||||
let thread = TSContactThread.getOrCreateThread(withContactId: member, transaction: transaction)
|
||||
thread.save(with: transaction)
|
||||
let closedGroupUpdateKind = ClosedGroupUpdate.Kind.new(groupPublicKey: Data(hex: groupPublicKey), name: name,
|
||||
groupPrivateKey: Data(hex: groupPrivateKey), senderKeys: [], members: membersAsData, admins: adminsAsData)
|
||||
let closedGroupUpdate = ClosedGroupUpdate()
|
||||
closedGroupUpdate.kind = closedGroupUpdateKind
|
||||
MessageSender.send(closedGroupUpdate, in: thread, using: transaction)
|
||||
}
|
||||
// Send out the user's new ratchet to all members (minus the removed ones) using established channels
|
||||
let userRatchet = SharedSenderKeys.generateRatchet(for: groupPublicKey, senderPublicKey: userPublicKey, using: transaction)
|
||||
let userSenderKey = ClosedGroupSenderKey(chainKey: Data(hex: userRatchet.chainKey), keyIndex: userRatchet.keyIndex, publicKey: Data(hex: userPublicKey))
|
||||
for member in members {
|
||||
let transaction = transaction as! YapDatabaseReadWriteTransaction
|
||||
guard member != userPublicKey else { continue }
|
||||
let thread = TSContactThread.getOrCreateThread(withContactId: member, transaction: transaction)
|
||||
thread.save(with: transaction)
|
||||
let closedGroupUpdateKind = ClosedGroupUpdate.Kind.senderKey(groupPublicKey: Data(hex: groupPublicKey), senderKey: userSenderKey)
|
||||
let closedGroupUpdate = ClosedGroupUpdate()
|
||||
closedGroupUpdate.kind = closedGroupUpdateKind
|
||||
MessageSender.send(closedGroupUpdate, in: thread, using: transaction)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if !newMembers.isEmpty {
|
||||
seal.fulfill(())
|
||||
// Generate ratchets for any new members
|
||||
newSenderKeys = newMembers.map { publicKey in
|
||||
let ratchet = SharedSenderKeys.generateRatchet(for: groupPublicKey, senderPublicKey: publicKey, using: transaction)
|
||||
return ClosedGroupSenderKey(chainKey: Data(hex: ratchet.chainKey), keyIndex: ratchet.keyIndex, publicKey: Data(hex: publicKey))
|
||||
}
|
||||
// Send a closed group update message to the existing members with the new members' ratchets (this message is aimed at the group)
|
||||
let closedGroupUpdateKind = ClosedGroupUpdate.Kind.info(groupPublicKey: Data(hex: groupPublicKey), name: name, senderKeys: newSenderKeys,
|
||||
members: membersAsData, admins: adminsAsData)
|
||||
let closedGroupUpdate = ClosedGroupUpdate()
|
||||
closedGroupUpdate.kind = closedGroupUpdateKind
|
||||
MessageSender.send(closedGroupUpdate, in: thread, using: transaction)
|
||||
// Send closed group update messages to the new members using established channels
|
||||
var allSenderKeys = Storage.shared.getAllClosedGroupSenderKeys(for: groupPublicKey)
|
||||
allSenderKeys.formUnion(newSenderKeys)
|
||||
for member in newMembers {
|
||||
let thread = TSContactThread.getOrCreateThread(withContactId: member, transaction: transaction)
|
||||
thread.save(with: transaction)
|
||||
let closedGroupUpdateKind = ClosedGroupUpdate.Kind.new(groupPublicKey: Data(hex: groupPublicKey), name: name,
|
||||
groupPrivateKey: Data(hex: groupPrivateKey), senderKeys: [ClosedGroupSenderKey](allSenderKeys), members: membersAsData, admins: adminsAsData)
|
||||
let closedGroupUpdate = ClosedGroupUpdate()
|
||||
closedGroupUpdate.kind = closedGroupUpdateKind
|
||||
MessageSender.send(closedGroupUpdate, in: thread, using: transaction)
|
||||
}
|
||||
} else {
|
||||
seal.fulfill(())
|
||||
let allSenderKeys = Storage.shared.getAllClosedGroupSenderKeys(for: groupPublicKey)
|
||||
let closedGroupUpdateKind = ClosedGroupUpdate.Kind.info(groupPublicKey: Data(hex: groupPublicKey), name: name,
|
||||
senderKeys: [ClosedGroupSenderKey](allSenderKeys), members: membersAsData, admins: adminsAsData)
|
||||
let closedGroupUpdate = ClosedGroupUpdate()
|
||||
closedGroupUpdate.kind = closedGroupUpdateKind
|
||||
MessageSender.send(closedGroupUpdate, in: thread, using: transaction)
|
||||
}
|
||||
// Update the group
|
||||
let newGroupModel = TSGroupModel(title: name, memberIds: [String](members), image: nil, groupId: groupID, groupType: .closedGroup, adminIds: admins)
|
||||
thread.setGroupModel(newGroupModel, with: transaction)
|
||||
// Notify the user
|
||||
let updateInfo = group.getInfoStringAboutUpdate(to: newGroupModel)
|
||||
let infoMessage = TSInfoMessage(timestamp: NSDate.ows_millisecondTimeStamp(), in: thread, messageType: .typeGroupUpdate, customMessage: updateInfo)
|
||||
infoMessage.save(with: transaction)
|
||||
// Return
|
||||
return promise
|
||||
}
|
||||
|
||||
/// The returned promise is fulfilled when the group update message has been sent. It doesn't wait for the user's new ratchet to be distributed.
|
||||
@objc(leaveGroupWithPublicKey:transaction:)
|
||||
public static func objc_leave(_ groupPublicKey: String, using transaction: YapDatabaseReadWriteTransaction) -> AnyPromise {
|
||||
return AnyPromise.from(leave(groupPublicKey, using: transaction))
|
||||
}
|
||||
|
||||
/// The returned promise is fulfilled when the group update message has been sent. It doesn't wait for the user's new ratchet to be distributed.
|
||||
public static func leave(_ groupPublicKey: String, using transaction: YapDatabaseReadWriteTransaction) -> Promise<Void> {
|
||||
let userPublicKey = getUserHexEncodedPublicKey()
|
||||
let groupID = LKGroupUtilities.getEncodedClosedGroupIDAsData(groupPublicKey)
|
||||
guard let thread = TSGroupThread.fetch(uniqueId: TSGroupThread.threadId(fromGroupId: groupID), transaction: transaction) else {
|
||||
SNLog("Can't leave nonexistent closed group.")
|
||||
return Promise(error: Error.noThread)
|
||||
}
|
||||
let group = thread.groupModel
|
||||
var newMembers = Set(group.groupMemberIds)
|
||||
newMembers.remove(userPublicKey)
|
||||
return update(groupPublicKey, with: newMembers, name: group.groupName!, transaction: transaction)
|
||||
}
|
||||
|
||||
public func requestSenderKey(for groupPublicKey: String, senderPublicKey: String, using transaction: Any) { // FIXME: This should be static
|
||||
SNLog("Requesting sender key for group public key: \(groupPublicKey), sender public key: \(senderPublicKey).")
|
||||
let transaction = transaction as! YapDatabaseReadWriteTransaction
|
||||
let thread = TSContactThread.getOrCreateThread(withContactId: senderPublicKey, transaction: transaction)
|
||||
thread.save(with: transaction)
|
||||
let closedGroupUpdateKind = ClosedGroupUpdate.Kind.senderKeyRequest(groupPublicKey: Data(hex: groupPublicKey))
|
||||
let closedGroupUpdate = ClosedGroupUpdate()
|
||||
closedGroupUpdate.kind = closedGroupUpdateKind
|
||||
MessageSender.send(closedGroupUpdate, in: thread, using: transaction)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -176,11 +176,6 @@ public final class MessageSender : NSObject {
|
|||
switch destination {
|
||||
case .contact(let publicKey): ciphertext = try encryptWithSessionProtocol(plaintext, for: publicKey)
|
||||
case .closedGroup(let groupPublicKey):
|
||||
|
||||
/*
|
||||
ciphertext = try encryptWithSessionProtocol(plaintext, for: groupPublicKey)
|
||||
*/
|
||||
|
||||
guard let encryptionKeyPair = Storage.shared.getLatestClosedGroupEncryptionKeyPair(for: groupPublicKey) else { throw Error.noKeyPair }
|
||||
ciphertext = try encryptWithSessionProtocol(plaintext, for: encryptionKeyPair.hexEncodedPublicKey)
|
||||
case .openGroup(_, _): preconditionFailure()
|
||||
|
|
|
@ -2,5 +2,3 @@
|
|||
|
||||
FOUNDATION_EXPORT double SessionProtocolKitVersionNumber;
|
||||
FOUNDATION_EXPORT const unsigned char SessionProtocolKitVersionString[];
|
||||
|
||||
#import <SessionProtocolKit/NSData+messagePadding.h>
|
||||
|
|
|
@ -317,6 +317,11 @@
|
|||
C31FFE57254A5FFE00F19441 /* KeyPairUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = C31FFE56254A5FFE00F19441 /* KeyPairUtilities.swift */; };
|
||||
C329FEEC24F7277900B1C64C /* LightModeSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = C329FEEB24F7277900B1C64C /* LightModeSheet.swift */; };
|
||||
C32A025A25A7FC55000ED5D4 /* ClosedGroupsV2Migration.swift in Sources */ = {isa = PBXBuildFile; fileRef = C32A025925A7FC55000ED5D4 /* ClosedGroupsV2Migration.swift */; };
|
||||
C32A026325A801AA000ED5D4 /* NSData+messagePadding.m in Sources */ = {isa = PBXBuildFile; fileRef = C3A71D4825589FF20043A11F /* NSData+messagePadding.m */; };
|
||||
C32A026C25A801AF000ED5D4 /* NSData+messagePadding.h in Headers */ = {isa = PBXBuildFile; fileRef = C3A71D4E25589FF30043A11F /* NSData+messagePadding.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
C32A027D25A80423000ED5D4 /* SessionProtocolKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C3C2A8622553B41A00C340D1 /* SessionProtocolKit.framework */; };
|
||||
C32A027E25A80428000ED5D4 /* SessionProtocolKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C3C2A8622553B41A00C340D1 /* SessionProtocolKit.framework */; };
|
||||
C32A027F25A80432000ED5D4 /* SessionProtocolKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C3C2A8622553B41A00C340D1 /* SessionProtocolKit.framework */; };
|
||||
C32C598A256D0664003C73A2 /* SNProtoEnvelope+Conversion.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EEF09255B49A8007E1867 /* SNProtoEnvelope+Conversion.swift */; };
|
||||
C32C599E256DB02B003C73A2 /* TypingIndicators.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDA87255A57FC00E217F9 /* TypingIndicators.swift */; };
|
||||
C32C59C0256DB41F003C73A2 /* TSThread.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDAD3255A580300E217F9 /* TSThread.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
|
@ -453,7 +458,6 @@
|
|||
C33FD9AF255A548A00E217F9 /* SignalUtilitiesKit.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FD9AD255A548A00E217F9 /* SignalUtilitiesKit.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
C33FD9B3255A548A00E217F9 /* SignalUtilitiesKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = C33FD9AB255A548A00E217F9 /* SignalUtilitiesKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
C33FD9C2255A54EF00E217F9 /* SessionMessagingKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C3C2A6F025539DE700C340D1 /* SessionMessagingKit.framework */; };
|
||||
C33FD9C3255A54EF00E217F9 /* SessionProtocolKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C3C2A8622553B41A00C340D1 /* SessionProtocolKit.framework */; };
|
||||
C33FD9C4255A54EF00E217F9 /* SessionSnodeKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C3C2A59F255385C100C340D1 /* SessionSnodeKit.framework */; };
|
||||
C33FD9C5255A54EF00E217F9 /* SessionUtilitiesKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C3C2A679255388CC00C340D1 /* SessionUtilitiesKit.framework */; };
|
||||
C33FDC27255A581F00E217F9 /* YapDatabase+Promise.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDA6D255A57FA00E217F9 /* YapDatabase+Promise.swift */; };
|
||||
|
@ -545,7 +549,6 @@
|
|||
C37F5385255B94F6002AEA92 /* SelectRecipientViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = C38EF34E255B6DC8007E1867 /* SelectRecipientViewController.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
C37F5396255B95BD002AEA92 /* OWSAnyTouchGestureRecognizer.h in Headers */ = {isa = PBXBuildFile; fileRef = C38EF302255B6DBE007E1867 /* OWSAnyTouchGestureRecognizer.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
C37F5414255BAFA7002AEA92 /* SignalUtilitiesKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C33FD9AB255A548A00E217F9 /* SignalUtilitiesKit.framework */; };
|
||||
C37F54CB255BB53F002AEA92 /* SessionProtocolKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C3C2A8622553B41A00C340D1 /* SessionProtocolKit.framework */; };
|
||||
C37F54DC255BB84A002AEA92 /* SessionSnodeKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C3C2A59F255385C100C340D1 /* SessionSnodeKit.framework */; };
|
||||
C38D5E8D2575011E00B6A65C /* MessageSender+ClosedGroups.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38D5E8C2575011E00B6A65C /* MessageSender+ClosedGroups.swift */; };
|
||||
C38EF00C255B61CC007E1867 /* SignalUtilitiesKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C33FD9AB255A548A00E217F9 /* SignalUtilitiesKit.framework */; };
|
||||
|
@ -705,8 +708,6 @@
|
|||
C3A71D0B2558989C0043A11F /* MessageWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3A71D0A2558989C0043A11F /* MessageWrapper.swift */; };
|
||||
C3A71D1E25589AC30043A11F /* WebSocketProto.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3A71D1C25589AC30043A11F /* WebSocketProto.swift */; };
|
||||
C3A71D1F25589AC30043A11F /* WebSocketResources.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3A71D1D25589AC30043A11F /* WebSocketResources.pb.swift */; };
|
||||
C3A71D5425589FF30043A11F /* NSData+messagePadding.m in Sources */ = {isa = PBXBuildFile; fileRef = C3A71D4825589FF20043A11F /* NSData+messagePadding.m */; };
|
||||
C3A71D5A25589FF30043A11F /* NSData+messagePadding.h in Headers */ = {isa = PBXBuildFile; fileRef = C3A71D4E25589FF30043A11F /* NSData+messagePadding.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
C3A71F892558BA9F0043A11F /* Mnemonic.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3A71F882558BA9F0043A11F /* Mnemonic.swift */; };
|
||||
C3A7211A2558BCA10043A11F /* DiffieHellman.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3A71D662558A0170043A11F /* DiffieHellman.swift */; };
|
||||
C3A721382558BDFA0043A11F /* OpenGroupMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3A721342558BDF90043A11F /* OpenGroupMessage.swift */; };
|
||||
|
@ -758,7 +759,6 @@
|
|||
C3C2A7852553AAF300C340D1 /* SessionProtos.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3C2A7832553AAF300C340D1 /* SessionProtos.pb.swift */; };
|
||||
C3C2A8662553B41A00C340D1 /* SessionProtocolKit.h in Headers */ = {isa = PBXBuildFile; fileRef = C3C2A8642553B41A00C340D1 /* SessionProtocolKit.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
C3C2A86A2553B41A00C340D1 /* SessionProtocolKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = C3C2A8622553B41A00C340D1 /* SessionProtocolKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
C3C2AAC82553C25300C340D1 /* SessionProtocolKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C3C2A8622553B41A00C340D1 /* SessionProtocolKit.framework */; };
|
||||
C3C2ABD22553C6C900C340D1 /* Data+SecureRandom.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3C2ABD12553C6C900C340D1 /* Data+SecureRandom.swift */; };
|
||||
C3C2AC2E2553CBEB00C340D1 /* String+Trimming.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3C2AC2D2553CBEB00C340D1 /* String+Trimming.swift */; };
|
||||
C3C2AC372553CCE600C340D1 /* SessionUtilitiesKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C3C2A679255388CC00C340D1 /* SessionUtilitiesKit.framework */; };
|
||||
|
@ -1877,9 +1877,9 @@
|
|||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
C32A027E25A80428000ED5D4 /* SessionProtocolKit.framework in Frameworks */,
|
||||
C38EF48A255B7E3F007E1867 /* SessionUIKit.framework in Frameworks */,
|
||||
C33FD9C2255A54EF00E217F9 /* SessionMessagingKit.framework in Frameworks */,
|
||||
C33FD9C3255A54EF00E217F9 /* SessionProtocolKit.framework in Frameworks */,
|
||||
C33FD9C4255A54EF00E217F9 /* SessionSnodeKit.framework in Frameworks */,
|
||||
C33FD9C5255A54EF00E217F9 /* SessionUtilitiesKit.framework in Frameworks */,
|
||||
B3E0C9C6F1633B1ABCE5AD0B /* Pods_SignalUtilitiesKit.framework in Frameworks */,
|
||||
|
@ -1907,8 +1907,8 @@
|
|||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
C32A027D25A80423000ED5D4 /* SessionProtocolKit.framework in Frameworks */,
|
||||
5DF9AB212C6DB1E8BE70EFF6 /* Pods_SessionMessagingKit.framework in Frameworks */,
|
||||
C3C2AAC82553C25300C340D1 /* SessionProtocolKit.framework in Frameworks */,
|
||||
C3C2A70B25539E1E00C340D1 /* SessionSnodeKit.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
@ -1926,8 +1926,8 @@
|
|||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
C32A027F25A80432000ED5D4 /* SessionProtocolKit.framework in Frameworks */,
|
||||
C37F54DC255BB84A002AEA92 /* SessionSnodeKit.framework in Frameworks */,
|
||||
C37F54CB255BB53F002AEA92 /* SessionProtocolKit.framework in Frameworks */,
|
||||
C37F5414255BAFA7002AEA92 /* SignalUtilitiesKit.framework in Frameworks */,
|
||||
455A16DD1F1FEA0000F86704 /* Metal.framework in Frameworks */,
|
||||
455A16DE1F1FEA0000F86704 /* MetalKit.framework in Frameworks */,
|
||||
|
@ -3162,6 +3162,8 @@
|
|||
C33FDB7F255A581100E217F9 /* FullTextSearchFinder.swift */,
|
||||
C33FDBC1255A581700E217F9 /* General.swift */,
|
||||
C3A71D0A2558989C0043A11F /* MessageWrapper.swift */,
|
||||
C3A71D4E25589FF30043A11F /* NSData+messagePadding.h */,
|
||||
C3A71D4825589FF20043A11F /* NSData+messagePadding.m */,
|
||||
C38EF2F5255B6DBC007E1867 /* OWSAudioPlayer.h */,
|
||||
C38EF2F7255B6DBC007E1867 /* OWSAudioPlayer.m */,
|
||||
C38EF281255B6D84007E1867 /* OWSAudioSession.swift */,
|
||||
|
@ -3324,8 +3326,6 @@
|
|||
B8CA010A25A293530091AF73 /* ClosedGroupRatchet.swift */,
|
||||
B8CA010025A293260091AF73 /* ClosedGroupSenderKey.swift */,
|
||||
B8CA011425A293800091AF73 /* Configuration.swift */,
|
||||
C3A71D4E25589FF30043A11F /* NSData+messagePadding.h */,
|
||||
C3A71D4825589FF20043A11F /* NSData+messagePadding.m */,
|
||||
B8CA011E25A2939F0091AF73 /* SharedSenderKeys.swift */,
|
||||
B8CA014025A293EE0091AF73 /* Storage.swift */,
|
||||
);
|
||||
|
@ -3669,6 +3669,7 @@
|
|||
C32C5EF7256DF567003C73A2 /* TSDatabaseView.h in Headers */,
|
||||
C3A3A0B3256E17F2004D228D /* SSKJobRecord.h in Headers */,
|
||||
B8856ED7256F1EB4001CE70E /* OWSPreferences.h in Headers */,
|
||||
C32A026C25A801AF000ED5D4 /* NSData+messagePadding.h in Headers */,
|
||||
C32C5BE6256DC891003C73A2 /* OWSReadReceiptManager.h in Headers */,
|
||||
C32C5EC3256DE133003C73A2 /* OWSQuotedReplyModel.h in Headers */,
|
||||
C32C5BF8256DC8F6003C73A2 /* OWSDisappearingMessagesJob.h in Headers */,
|
||||
|
@ -3702,7 +3703,6 @@
|
|||
isa = PBXHeadersBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
C3A71D5A25589FF30043A11F /* NSData+messagePadding.h in Headers */,
|
||||
C3C2A8662553B41A00C340D1 /* SessionProtocolKit.h in Headers */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
@ -4740,6 +4740,7 @@
|
|||
C3C2A74D2553A39700C340D1 /* VisibleMessage.swift in Sources */,
|
||||
C32C5AAD256DBE8F003C73A2 /* TSInfoMessage.m in Sources */,
|
||||
C32C5A13256DB7A5003C73A2 /* PushNotificationAPI.swift in Sources */,
|
||||
C32A026325A801AA000ED5D4 /* NSData+messagePadding.m in Sources */,
|
||||
C352A3932557883D00338F3E /* JobDelegate.swift in Sources */,
|
||||
C32C5B84256DC54F003C73A2 /* SSKEnvironment.m in Sources */,
|
||||
C3A3A108256E1A5C004D228D /* OWSIncomingMessageFinder.m in Sources */,
|
||||
|
@ -4868,7 +4869,6 @@
|
|||
files = (
|
||||
B8CA010125A293260091AF73 /* ClosedGroupSenderKey.swift in Sources */,
|
||||
B8CA011525A293800091AF73 /* Configuration.swift in Sources */,
|
||||
C3A71D5425589FF30043A11F /* NSData+messagePadding.m in Sources */,
|
||||
B8CA011F25A2939F0091AF73 /* SharedSenderKeys.swift in Sources */,
|
||||
B8CA010B25A293530091AF73 /* ClosedGroupRatchet.swift in Sources */,
|
||||
B8CA014125A293EE0091AF73 /* Storage.swift in Sources */,
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import SessionMessagingKit
|
||||
import SessionProtocolKit
|
||||
import SessionSnodeKit
|
||||
|
||||
extension OWSPrimaryStorage : OWSPrimaryStorageProtocol { }
|
||||
|
@ -10,7 +9,6 @@ public final class Configuration : NSObject {
|
|||
@objc public static func performMainSetup() {
|
||||
SNMessagingKit.configure(storage: Storage.shared)
|
||||
SNSnodeKit.configure(storage: Storage.shared)
|
||||
SNProtocolKit.configure(storage: Storage.shared, sharedSenderKeysDelegate: MessageSender.shared)
|
||||
SNUtilitiesKit.configure(owsPrimaryStorage: OWSPrimaryStorage.shared(), maxFileSize: UInt(Double(FileServerAPI.maxFileSize) / FileServerAPI.fileSizeORMultiplier))
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue