Implement rough group modification logic
This commit is contained in:
parent
79655169bd
commit
c604b40632
2
Pods
2
Pods
|
@ -1 +1 @@
|
|||
Subproject commit c33c86d927843d7383dde6b4c9350052e0f3f482
|
||||
Subproject commit 776821513e5208897eb79150f2801b62653ec5b7
|
|
@ -244,6 +244,8 @@ message DataMessage {
|
|||
message ClosedGroupUpdate { // Loki
|
||||
enum Type {
|
||||
NEW = 0; // groupPublicKey, name, groupPrivateKey, chainKeys, members, admins
|
||||
INFO = 1; // groupPublicKey, name, chainKeys, members, admins
|
||||
CHAIN_KEY = 2; // groupPublicKey, chainKeys
|
||||
}
|
||||
|
||||
optional string name = 1;
|
||||
|
|
|
@ -39,14 +39,14 @@ extern NSString *const TSGroupThread_NotificationKey_UniqueId;
|
|||
|
||||
- (BOOL)isLocalUserInGroup;
|
||||
- (BOOL)isCurrentUserInGroupWithTransaction:(YapDatabaseReadTransaction *)transaction;
|
||||
- (BOOL)isUserInGroup:(NSString *)hexEncodedPublicKey transaction:(YapDatabaseReadTransaction *)transaction;
|
||||
- (BOOL)isUserMemberInGroup:(NSString *)hexEncodedPublicKey transaction:(YapDatabaseReadTransaction *)transaction;
|
||||
- (BOOL)isUserAdminInGroup:(NSString *)hexEncodedPublicKey transaction:(YapDatabaseReadTransaction *)transaction;
|
||||
|
||||
// all group threads containing recipient as a member
|
||||
+ (NSArray<TSGroupThread *> *)groupThreadsWithRecipientId:(NSString *)recipientId
|
||||
transaction:(YapDatabaseReadWriteTransaction *)transaction;
|
||||
|
||||
- (void)setGroupModel:(TSGroupModel *)newGroupModel withTransaction:(YapDatabaseReadTransaction *)transaction;
|
||||
- (void)setGroupModel:(TSGroupModel *)newGroupModel withTransaction:(YapDatabaseReadWriteTransaction *)transaction;
|
||||
- (void)leaveGroupWithSneakyTransaction;
|
||||
- (void)leaveGroupWithTransaction:(YapDatabaseReadWriteTransaction *)transaction;
|
||||
|
||||
|
|
|
@ -218,10 +218,10 @@ NSString *const TSGroupThread_NotificationKey_UniqueId = @"TSGroupThread_Notific
|
|||
- (BOOL)isCurrentUserInGroupWithTransaction:(YapDatabaseReadTransaction *)transaction
|
||||
{
|
||||
NSString *userHexEncodedPublicKey = TSAccountManager.localNumber;
|
||||
return [self isUserInGroup:userHexEncodedPublicKey transaction:transaction];
|
||||
return [self isUserMemberInGroup:userHexEncodedPublicKey transaction:transaction];
|
||||
}
|
||||
|
||||
- (BOOL)isUserInGroup:(NSString *)hexEncodedPublicKey transaction:(YapDatabaseReadTransaction *)transaction
|
||||
- (BOOL)isUserMemberInGroup:(NSString *)hexEncodedPublicKey transaction:(YapDatabaseReadTransaction *)transaction
|
||||
{
|
||||
if (hexEncodedPublicKey == nil) { return NO; }
|
||||
NSSet<NSString *> *linkedDeviceHexEncodedPublicKeys = [LKDatabaseUtilities getLinkedDeviceHexEncodedPublicKeysFor:hexEncodedPublicKey in:transaction];
|
||||
|
|
|
@ -8,7 +8,7 @@ public final class Poller : NSObject {
|
|||
private var pollCount = 0
|
||||
|
||||
// MARK: Settings
|
||||
private static let pollInterval: TimeInterval = 2
|
||||
private static let pollInterval: TimeInterval = 1
|
||||
private static let retryInterval: TimeInterval = 0.25
|
||||
/// After polling a given snode this many times we always switch to a new one.
|
||||
///
|
||||
|
|
|
@ -6,7 +6,7 @@ public final class ClosedGroupPoller : NSObject {
|
|||
private var timer: Timer?
|
||||
|
||||
// MARK: Settings
|
||||
private static let pollInterval: TimeInterval = 4
|
||||
private static let pollInterval: TimeInterval = 2
|
||||
|
||||
// MARK: Error
|
||||
private enum Error : LocalizedError {
|
||||
|
|
|
@ -3,10 +3,6 @@
|
|||
internal final class ClosedGroupUpdateMessage : TSOutgoingMessage {
|
||||
private let kind: Kind
|
||||
|
||||
@objc internal var isGroupCreationMessage: Bool {
|
||||
if case .new = kind { return true } else { return false }
|
||||
}
|
||||
|
||||
// MARK: Settings
|
||||
@objc internal override var ttl: UInt32 { return UInt32(TTLUtilities.getTTL(for: .closedGroupUpdate)) }
|
||||
|
||||
|
@ -16,6 +12,8 @@ internal final class ClosedGroupUpdateMessage : TSOutgoingMessage {
|
|||
// MARK: Kind
|
||||
internal enum Kind {
|
||||
case new(groupPublicKey: Data, name: String, groupPrivateKey: Data, chainKeys: [Data], members: [String], admins: [String])
|
||||
case info(groupPublicKey: Data, name: String, chainKeys: [Data], members: [String], admins: [String])
|
||||
case chainKey(groupPublicKey: Data, chainKey: Data)
|
||||
}
|
||||
|
||||
// MARK: Initialization
|
||||
|
@ -47,6 +45,15 @@ internal final class ClosedGroupUpdateMessage : TSOutgoingMessage {
|
|||
closedGroupUpdate.setChainKeys(chainKeys)
|
||||
closedGroupUpdate.setMembers(members)
|
||||
closedGroupUpdate.setAdmins(admins)
|
||||
case .info(let groupPublicKey, let name, let chainKeys, let members, let admins):
|
||||
closedGroupUpdate = SSKProtoDataMessageClosedGroupUpdate.builder(groupPublicKey: groupPublicKey, type: .info)
|
||||
closedGroupUpdate.setName(name)
|
||||
closedGroupUpdate.setChainKeys(chainKeys)
|
||||
closedGroupUpdate.setMembers(members)
|
||||
closedGroupUpdate.setAdmins(admins)
|
||||
case .chainKey(let groupPublicKey, let chainKey):
|
||||
closedGroupUpdate = SSKProtoDataMessageClosedGroupUpdate.builder(groupPublicKey: groupPublicKey, type: .chainKey)
|
||||
closedGroupUpdate.setChainKeys([ chainKey ])
|
||||
}
|
||||
builder.setClosedGroupUpdate(try closedGroupUpdate.build())
|
||||
} catch {
|
||||
|
|
|
@ -58,57 +58,232 @@ public final class ClosedGroupsProtocol : NSObject {
|
|||
return thread
|
||||
}
|
||||
|
||||
@objc(handleSharedSenderKeysUpdateIfNeeded:transaction:)
|
||||
public static func handleSharedSenderKeysUpdateIfNeeded(_ dataMessage: SSKProtoDataMessage, using transaction: YapDatabaseReadWriteTransaction) -> Bool {
|
||||
guard let closedGroupUpdate = dataMessage.closedGroupUpdate else { return false }
|
||||
switch closedGroupUpdate.type {
|
||||
case .new:
|
||||
// Unwrap the message
|
||||
let groupPublicKey = closedGroupUpdate.groupPublicKey.toHexString()
|
||||
let name = closedGroupUpdate.name
|
||||
let groupPrivateKey = closedGroupUpdate.groupPrivateKey!
|
||||
let chainKeys = closedGroupUpdate.chainKeys
|
||||
let members = closedGroupUpdate.members
|
||||
let admins = closedGroupUpdate.admins
|
||||
// Persist the ratchets
|
||||
zip(members, chainKeys).forEach { (member, chainKey) in
|
||||
let ratchet = ClosedGroupRatchet(chainKey: chainKey.toHexString(), keyIndex: 0, messageKeys: [])
|
||||
Storage.setClosedGroupRatchet(groupPublicKey: groupPublicKey, senderPublicKey: member, ratchet: ratchet, 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.getOrCreateThread(with: group, transaction: transaction)
|
||||
thread.usesSharedSenderKeys = true
|
||||
public static func addUser(_ publicKey: String, to groupPublicKey: String, using transaction: YapDatabaseReadWriteTransaction) {
|
||||
// Prepare
|
||||
let messageSenderJobQueue = SSKEnvironment.shared.messageSenderJobQueue
|
||||
let groupID = LKGroupUtilities.getEncodedClosedGroupID(groupPublicKey)
|
||||
guard let thread1 = TSGroupThread.fetch(uniqueId: groupID, transaction: transaction) else {
|
||||
return print("[Loki] Can't add user to nonexistent closed group.")
|
||||
}
|
||||
let group = thread1.groupModel
|
||||
let name = group.groupName!
|
||||
let admins = group.groupAdminIds
|
||||
// Add the user
|
||||
var members = group.groupMemberIds
|
||||
members.append(publicKey)
|
||||
// Establish sessions if needed (it's important that this happens before the code below)
|
||||
establishSessionsIfNeeded(with: members, using: transaction)
|
||||
// Generate a ratchet for the new member
|
||||
let ratchet = SharedSenderKeysImplementation.shared.generateRatchet(for: groupPublicKey, senderPublicKey: publicKey, using: transaction)
|
||||
let chainKey = Data(hex: ratchet.chainKey)
|
||||
// Send the update to the group
|
||||
let closedGroupUpdateMessageKind1 = ClosedGroupUpdateMessage.Kind.info(groupPublicKey: Data(hex: groupPublicKey), name: name, chainKeys: [ chainKey ], members: members, admins: admins)
|
||||
let closedGroupUpdateMessage1 = ClosedGroupUpdateMessage(thread: thread1, kind: closedGroupUpdateMessageKind1)
|
||||
messageSenderJobQueue.add(message: closedGroupUpdateMessage1, transaction: transaction)
|
||||
// Notify the added user
|
||||
let allChainKeys = Storage.getAllClosedGroupRatchets(for: groupPublicKey).map { Data(hex: $0.chainKey) } + [ chainKey ] // TODO: I think we need to include the key index here as well
|
||||
let closedGroupUpdateMessageKind2 = ClosedGroupUpdateMessage.Kind.info(groupPublicKey: Data(hex: groupPublicKey), name: name, chainKeys: allChainKeys, members: members, admins: admins)
|
||||
let thread2 = TSContactThread.getOrCreateThread(contactId: publicKey)
|
||||
thread2.save(with: transaction)
|
||||
let closedGroupUpdateMessage2 = ClosedGroupUpdateMessage(thread: thread2, kind: closedGroupUpdateMessageKind2)
|
||||
messageSenderJobQueue.add(message: closedGroupUpdateMessage2, transaction: transaction)
|
||||
// Notify the user
|
||||
let infoMessage = TSInfoMessage(timestamp: NSDate.ows_millisecondTimeStamp(), in: thread1, messageType: .typeGroupUpdate)
|
||||
infoMessage.save(with: transaction)
|
||||
}
|
||||
|
||||
public static func removeUser(_ publicKey: String, from groupPublicKey: String, using transaction: YapDatabaseReadWriteTransaction) {
|
||||
// Prepare
|
||||
let groupID = LKGroupUtilities.getEncodedClosedGroupID(groupPublicKey)
|
||||
guard let thread = TSGroupThread.fetch(uniqueId: groupID, transaction: transaction) else {
|
||||
return print("[Loki] Can't remove user from nonexistent closed group.")
|
||||
}
|
||||
let group = thread.groupModel
|
||||
let name = group.groupName!
|
||||
let admins = group.groupAdminIds
|
||||
// Remove the user
|
||||
var members = group.groupMemberIds
|
||||
guard let indexOfUser = members.firstIndex(of: publicKey) else {
|
||||
return print("[Loki] Can't remove user from group.")
|
||||
}
|
||||
members.remove(at: indexOfUser)
|
||||
// Establish sessions if needed (it's important that this happens before the code below)
|
||||
establishSessionsIfNeeded(with: members, using: transaction)
|
||||
// Generate new ratchets for everyone except the member that was removed
|
||||
let ratchets = members.map {
|
||||
SharedSenderKeysImplementation.shared.generateRatchet(for: groupPublicKey, senderPublicKey: $0, using: transaction)
|
||||
}
|
||||
let chainKeys = ratchets.map { Data(hex: $0.chainKey) }
|
||||
// Send a closed group update message to all members involved
|
||||
for member in members {
|
||||
let thread = TSContactThread.getOrCreateThread(withContactId: member, transaction: transaction)
|
||||
thread.save(with: transaction)
|
||||
SSKEnvironment.shared.profileManager.addThread(toProfileWhitelist: thread)
|
||||
// Add the group to the user's set of public keys to poll for
|
||||
Storage.setClosedGroupPrivateKey(groupPrivateKey.toHexString(), for: groupPublicKey, using: transaction)
|
||||
// Notify the user
|
||||
let infoMessage = TSInfoMessage(timestamp: NSDate.ows_millisecondTimeStamp(), in: thread, messageType: .typeGroupUpdate)
|
||||
infoMessage.save(with: transaction)
|
||||
// Establish sessions if needed
|
||||
establishSessionsIfNeeded(with: members, in: thread, using: transaction)
|
||||
// Return
|
||||
return true
|
||||
let closedGroupUpdateMessageKind = ClosedGroupUpdateMessage.Kind.info(groupPublicKey: Data(hex: groupPublicKey), name: name, chainKeys: chainKeys, members: members, admins: admins)
|
||||
let closedGroupUpdateMessage = ClosedGroupUpdateMessage(thread: thread, kind: closedGroupUpdateMessageKind)
|
||||
let messageSenderJobQueue = SSKEnvironment.shared.messageSenderJobQueue
|
||||
messageSenderJobQueue.add(message: closedGroupUpdateMessage, transaction: transaction)
|
||||
}
|
||||
// Notify the removed user
|
||||
SessionManagementProtocol.establishSessionIfNeeded(with: publicKey, using: transaction)
|
||||
let closedGroupUpdateMessageKind = ClosedGroupUpdateMessage.Kind.info(groupPublicKey: Data(hex: groupPublicKey), name: name, chainKeys: [], members: members, admins: admins)
|
||||
let closedGroupUpdateMessage = ClosedGroupUpdateMessage(thread: thread, kind: closedGroupUpdateMessageKind)
|
||||
let messageSenderJobQueue = SSKEnvironment.shared.messageSenderJobQueue
|
||||
messageSenderJobQueue.add(message: closedGroupUpdateMessage, transaction: transaction)
|
||||
// Notify the user
|
||||
let infoMessage = TSInfoMessage(timestamp: NSDate.ows_millisecondTimeStamp(), in: thread, messageType: .typeGroupUpdate)
|
||||
infoMessage.save(with: transaction)
|
||||
}
|
||||
|
||||
public static func leave(_ groupPublicKey: String, using transaction: YapDatabaseReadWriteTransaction) {
|
||||
// Prepare
|
||||
let groupID = LKGroupUtilities.getEncodedClosedGroupID(groupPublicKey)
|
||||
guard let thread = TSGroupThread.fetch(uniqueId: groupID, transaction: transaction) else {
|
||||
return print("[Loki] Can't leave nonexistent closed group.")
|
||||
}
|
||||
let group = thread.groupModel
|
||||
let name = group.groupName!
|
||||
// Leave the group
|
||||
var members = group.groupMemberIds
|
||||
guard let indexOfSelf = members.firstIndex(of: getUserHexEncodedPublicKey()) else {
|
||||
return print("[Loki] Can't leave group.")
|
||||
}
|
||||
members.remove(at: indexOfSelf)
|
||||
let admins = group.groupAdminIds
|
||||
// Send the update to the group (don't include new ratchets as everyone should generate new ratchets
|
||||
// individually in this case)
|
||||
let closedGroupUpdateMessageKind = ClosedGroupUpdateMessage.Kind.info(groupPublicKey: Data(hex: groupPublicKey), name: name, chainKeys: [], members: members, admins: admins)
|
||||
let closedGroupUpdateMessage = ClosedGroupUpdateMessage(thread: thread, kind: closedGroupUpdateMessageKind)
|
||||
let messageSenderJobQueue = SSKEnvironment.shared.messageSenderJobQueue
|
||||
messageSenderJobQueue.add(message: closedGroupUpdateMessage, transaction: transaction)
|
||||
// Delete all ratchets
|
||||
Storage.removeAllClosedGroupRatchets(for: groupPublicKey, using: transaction)
|
||||
}
|
||||
|
||||
@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
|
||||
// sender, depending on how the message was sent
|
||||
guard let closedGroupUpdate = dataMessage.closedGroupUpdate else { return }
|
||||
switch closedGroupUpdate.type {
|
||||
case .new: handleNewGroupMessage(closedGroupUpdate, using: transaction)
|
||||
case .info: handleInfoMessage(closedGroupUpdate, using: transaction)
|
||||
case .chainKey: handleChainKeyMessage(closedGroupUpdate, from: publicKey, using: transaction)
|
||||
}
|
||||
}
|
||||
|
||||
private static func handleNewGroupMessage(_ closedGroupUpdate: SSKProtoDataMessageClosedGroupUpdate, using transaction: YapDatabaseReadWriteTransaction) {
|
||||
// Unwrap the message
|
||||
let groupPublicKey = closedGroupUpdate.groupPublicKey.toHexString()
|
||||
let name = closedGroupUpdate.name
|
||||
let groupPrivateKey = closedGroupUpdate.groupPrivateKey!
|
||||
let chainKeys = closedGroupUpdate.chainKeys
|
||||
let members = closedGroupUpdate.members
|
||||
let admins = closedGroupUpdate.admins
|
||||
// Persist the ratchets
|
||||
zip(members, chainKeys).forEach { (member, chainKey) in
|
||||
let ratchet = ClosedGroupRatchet(chainKey: chainKey.toHexString(), keyIndex: 0, messageKeys: [])
|
||||
Storage.setClosedGroupRatchet(for: groupPublicKey, senderPublicKey: member, ratchet: ratchet, 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.getOrCreateThread(with: group, transaction: transaction)
|
||||
thread.usesSharedSenderKeys = true
|
||||
thread.save(with: transaction)
|
||||
SSKEnvironment.shared.profileManager.addThread(toProfileWhitelist: thread)
|
||||
// Add the group to the user's set of public keys to poll for
|
||||
Storage.setClosedGroupPrivateKey(groupPrivateKey.toHexString(), for: groupPublicKey, using: transaction)
|
||||
// Notify the user
|
||||
let infoMessage = TSInfoMessage(timestamp: NSDate.ows_millisecondTimeStamp(), in: thread, messageType: .typeGroupUpdate)
|
||||
infoMessage.save(with: transaction)
|
||||
// Establish sessions if needed
|
||||
establishSessionsIfNeeded(with: members, using: transaction)
|
||||
}
|
||||
|
||||
/// Invoked upon receiving a group update. A group update is sent out when a group's name is changed, when new users
|
||||
/// are added, when users leave or are kicked, or if the group admins are changed.
|
||||
private static func handleInfoMessage(_ closedGroupUpdate: SSKProtoDataMessageClosedGroupUpdate, using transaction: YapDatabaseReadWriteTransaction) {
|
||||
// TODO: Check that the sender was an admin
|
||||
// Unwrap the message
|
||||
let groupPublicKey = closedGroupUpdate.groupPublicKey.toHexString()
|
||||
let name = closedGroupUpdate.name
|
||||
let chainKeys = closedGroupUpdate.chainKeys
|
||||
let members = closedGroupUpdate.members
|
||||
let admins = closedGroupUpdate.admins
|
||||
// Get the group
|
||||
let groupID = LKGroupUtilities.getEncodedClosedGroupID(groupPublicKey)
|
||||
guard let thread = TSGroupThread.fetch(uniqueId: groupID, transaction: transaction) else {
|
||||
return print("[Loki] Ignoring closed group update for nonexistent group.")
|
||||
}
|
||||
let group = thread.groupModel
|
||||
// Establish sessions if needed (it's important that this happens before the code below)
|
||||
establishSessionsIfNeeded(with: members, using: transaction)
|
||||
// Parse out any new members and store their ratchets (it's important that
|
||||
// this happens before handling removed members)
|
||||
let oldMembers = group.groupMemberIds
|
||||
let newMembers = members.filter { !oldMembers.contains($0) }
|
||||
if newMembers.count == chainKeys.count { // If someone was kicked the message won't have any chain keys
|
||||
zip(newMembers, chainKeys).forEach { (member, chainKey) in
|
||||
let ratchet = ClosedGroupRatchet(chainKey: chainKey.toHexString(), keyIndex: 0, messageKeys: [])
|
||||
Storage.setClosedGroupRatchet(for: groupPublicKey, senderPublicKey: member, 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
|
||||
if Set(members).intersection(oldMembers) != Set(oldMembers) {
|
||||
Storage.removeAllClosedGroupRatchets(for: groupPublicKey, using: transaction)
|
||||
let userPublicKey = getUserHexEncodedPublicKey()
|
||||
let newRatchet = SharedSenderKeysImplementation.shared.generateRatchet(for: groupPublicKey, senderPublicKey: userPublicKey, using: transaction)
|
||||
Storage.setClosedGroupRatchet(for: groupPublicKey, senderPublicKey: userPublicKey, ratchet: newRatchet, using: transaction)
|
||||
for member in members {
|
||||
let thread = TSContactThread.getOrCreateThread(withContactId: member, transaction: transaction)
|
||||
thread.save(with: transaction)
|
||||
let closedGroupUpdateMessageKind = ClosedGroupUpdateMessage.Kind.chainKey(groupPublicKey: Data(hex: groupPublicKey), chainKey: Data(hex: newRatchet.chainKey))
|
||||
let closedGroupUpdateMessage = ClosedGroupUpdateMessage(thread: thread, kind: closedGroupUpdateMessageKind)
|
||||
let messageSenderJobQueue = SSKEnvironment.shared.messageSenderJobQueue
|
||||
messageSenderJobQueue.add(message: closedGroupUpdateMessage, transaction: transaction)
|
||||
}
|
||||
}
|
||||
// Update the group
|
||||
let groupIDAsData = groupID.data(using: String.Encoding.utf8)!
|
||||
let newGroupModel = TSGroupModel(title: name, memberIds: members, image: nil, groupId: groupIDAsData, groupType: .closedGroup, adminIds: admins)
|
||||
thread.setGroupModel(newGroupModel, with: transaction)
|
||||
// Notify the user
|
||||
let infoMessage = TSInfoMessage(timestamp: NSDate.ows_millisecondTimeStamp(), in: thread, messageType: .typeGroupUpdate)
|
||||
infoMessage.save(with: transaction)
|
||||
}
|
||||
|
||||
/// Invoked upon receiving a chain key from another user.
|
||||
private static func handleChainKeyMessage(_ closedGroupUpdate: SSKProtoDataMessageClosedGroupUpdate, from senderPublicKey: String, using transaction: YapDatabaseReadWriteTransaction) {
|
||||
let groupPublicKey = closedGroupUpdate.groupPublicKey.toHexString()
|
||||
guard let chainKey = closedGroupUpdate.chainKeys.first else {
|
||||
return print("[Loki] Ignoring invalid closed group update.")
|
||||
}
|
||||
let ratchet = ClosedGroupRatchet(chainKey: chainKey.toHexString(), keyIndex: 0, messageKeys: [])
|
||||
Storage.setClosedGroupRatchet(for: groupPublicKey, senderPublicKey: senderPublicKey, ratchet: ratchet, using: transaction)
|
||||
}
|
||||
|
||||
@objc(establishSessionsIfNeededWithClosedGroupMembers:transaction:)
|
||||
public static func establishSessionsIfNeeded(with closedGroupMembers: [String], using transaction: YapDatabaseReadWriteTransaction) {
|
||||
closedGroupMembers.forEach { publicKey in
|
||||
SessionManagementProtocol.establishSessionIfNeeded(with: publicKey, using: transaction)
|
||||
}
|
||||
}
|
||||
|
||||
@objc(shouldIgnoreClosedGroupMessage:inThread:wrappedIn:)
|
||||
public static func shouldIgnoreClosedGroupMessage(_ dataMessage: SSKProtoDataMessage, in thread: TSThread, wrappedIn envelope: SSKProtoEnvelope) -> Bool {
|
||||
guard let thread = thread as? TSGroupThread, thread.groupModel.groupType == .closedGroup,
|
||||
dataMessage.group?.type == .deliver else { return false }
|
||||
public static func shouldIgnoreClosedGroupMessage(_ dataMessage: SSKProtoDataMessage, in thread: TSGroupThread, wrappedIn envelope: SSKProtoEnvelope) -> Bool {
|
||||
guard thread.groupModel.groupType == .closedGroup else { return true }
|
||||
let publicKey = envelope.source! // Set during UD decryption
|
||||
var result = false
|
||||
Storage.read { transaction in
|
||||
result = !thread.isUser(inGroup: publicKey, transaction: transaction)
|
||||
result = !thread.isUserMember(inGroup: publicKey, transaction: transaction)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
@objc(shouldIgnoreClosedGroupUpdateMessage:inThread:wrappedIn:)
|
||||
public static func shouldIgnoreClosedGroupUpdateMessage(_ dataMessage: SSKProtoDataMessage, in thread: TSGroupThread?, wrappedIn envelope: SSKProtoEnvelope) -> Bool {
|
||||
guard let thread = thread else { return false }
|
||||
public static func shouldIgnoreClosedGroupUpdateMessage(_ dataMessage: SSKProtoDataMessage, in thread: TSGroupThread, wrappedIn envelope: SSKProtoEnvelope) -> Bool {
|
||||
guard thread.groupModel.groupType == .closedGroup else { return true }
|
||||
let publicKey = envelope.source! // Set during UD decryption
|
||||
var result = false
|
||||
Storage.read { transaction in
|
||||
|
@ -116,13 +291,5 @@ public final class ClosedGroupsProtocol : NSObject {
|
|||
}
|
||||
return result
|
||||
}
|
||||
|
||||
@objc(establishSessionsIfNeededWithClosedGroupMembers:inThread:transaction:)
|
||||
public static func establishSessionsIfNeeded(with closedGroupMembers: [String], in thread: TSGroupThread, using transaction: YapDatabaseReadWriteTransaction) {
|
||||
guard thread.groupModel.groupType == .closedGroup else { return }
|
||||
closedGroupMembers.forEach { member in
|
||||
SessionManagementProtocol.establishSessionIfNeeded(with: member, using: transaction)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -61,7 +61,7 @@ public final class SharedSenderKeysImplementation : NSObject, SharedSenderKeysPr
|
|||
internal func generateRatchet(for groupPublicKey: String, senderPublicKey: String, using transaction: YapDatabaseReadWriteTransaction) -> ClosedGroupRatchet {
|
||||
let rootChainKey = Data.getSecureRandomData(ofSize: 32)!.toHexString()
|
||||
let ratchet = ClosedGroupRatchet(chainKey: rootChainKey, keyIndex: 0, messageKeys: [])
|
||||
Storage.setClosedGroupRatchet(groupPublicKey: groupPublicKey, senderPublicKey: senderPublicKey, ratchet: ratchet, using: transaction)
|
||||
Storage.setClosedGroupRatchet(for: groupPublicKey, senderPublicKey: senderPublicKey, ratchet: ratchet, using: transaction)
|
||||
return ratchet
|
||||
}
|
||||
|
||||
|
@ -77,14 +77,14 @@ public final class SharedSenderKeysImplementation : NSObject, SharedSenderKeysPr
|
|||
#if DEBUG
|
||||
assert(!Thread.isMainThread)
|
||||
#endif
|
||||
guard let ratchet = Storage.getClosedGroupRatchet(groupPublicKey: groupPublicKey, senderPublicKey: senderPublicKey) else {
|
||||
guard let ratchet = Storage.getClosedGroupRatchet(for: groupPublicKey, senderPublicKey: senderPublicKey) else {
|
||||
let error = RatchetingError.loadingFailed(groupPublicKey: groupPublicKey, senderPublicKey: senderPublicKey)
|
||||
print("[Loki] \(error.errorDescription!)")
|
||||
throw error
|
||||
}
|
||||
do {
|
||||
let result = try step(ratchet)
|
||||
Storage.setClosedGroupRatchet(groupPublicKey: groupPublicKey, senderPublicKey: senderPublicKey, ratchet: result, using: transaction)
|
||||
Storage.setClosedGroupRatchet(for: groupPublicKey, senderPublicKey: senderPublicKey, ratchet: result, using: transaction)
|
||||
return result
|
||||
} catch {
|
||||
print("[Loki] Couldn't step ratchet due to error: \(error).")
|
||||
|
@ -97,7 +97,7 @@ public final class SharedSenderKeysImplementation : NSObject, SharedSenderKeysPr
|
|||
#if DEBUG
|
||||
assert(!Thread.isMainThread)
|
||||
#endif
|
||||
guard let ratchet = Storage.getClosedGroupRatchet(groupPublicKey: groupPublicKey, senderPublicKey: senderPublicKey) else {
|
||||
guard let ratchet = Storage.getClosedGroupRatchet(for: groupPublicKey, senderPublicKey: senderPublicKey) else {
|
||||
let error = RatchetingError.loadingFailed(groupPublicKey: groupPublicKey, senderPublicKey: senderPublicKey)
|
||||
print("[Loki] \(error.errorDescription!)")
|
||||
throw error
|
||||
|
@ -122,7 +122,7 @@ public final class SharedSenderKeysImplementation : NSObject, SharedSenderKeysPr
|
|||
throw error
|
||||
}
|
||||
}
|
||||
Storage.setClosedGroupRatchet(groupPublicKey: groupPublicKey, senderPublicKey: senderPublicKey, ratchet: result, using: transaction)
|
||||
Storage.setClosedGroupRatchet(for: groupPublicKey, senderPublicKey: senderPublicKey, ratchet: result, using: transaction)
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,20 +2,39 @@
|
|||
internal extension Storage {
|
||||
|
||||
// MARK: Ratchets
|
||||
internal static let closedGroupRatchetCollection = "LokiClosedGroupRatchetCollection"
|
||||
internal static func getClosedGroupRatchetCollection(for groupPublicKey: String) -> String {
|
||||
return "LokiClosedGroupRatchetCollection.\(groupPublicKey)"
|
||||
}
|
||||
|
||||
internal static func getClosedGroupRatchet(groupPublicKey: String, senderPublicKey: String) -> ClosedGroupRatchet? {
|
||||
let key = "\(groupPublicKey).\(senderPublicKey)"
|
||||
internal static func getClosedGroupRatchet(for groupPublicKey: String, senderPublicKey: String) -> ClosedGroupRatchet? {
|
||||
let collection = getClosedGroupRatchetCollection(for: groupPublicKey)
|
||||
var result: ClosedGroupRatchet?
|
||||
read { transaction in
|
||||
result = transaction.object(forKey: key, inCollection: closedGroupRatchetCollection) as? ClosedGroupRatchet
|
||||
result = transaction.object(forKey: senderPublicKey, inCollection: collection) as? ClosedGroupRatchet
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
internal static func setClosedGroupRatchet(groupPublicKey: String, senderPublicKey: String, ratchet: ClosedGroupRatchet, using transaction: YapDatabaseReadWriteTransaction) {
|
||||
let key = "\(groupPublicKey).\(senderPublicKey)"
|
||||
transaction.setObject(ratchet, forKey: key, inCollection: closedGroupRatchetCollection)
|
||||
internal static func setClosedGroupRatchet(for groupPublicKey: String, senderPublicKey: String, ratchet: ClosedGroupRatchet, using transaction: YapDatabaseReadWriteTransaction) {
|
||||
let collection = getClosedGroupRatchetCollection(for: groupPublicKey)
|
||||
transaction.setObject(ratchet, forKey: senderPublicKey, inCollection: collection)
|
||||
}
|
||||
|
||||
internal static func getAllClosedGroupRatchets(for groupPublicKey: String) -> [ClosedGroupRatchet] {
|
||||
let collection = getClosedGroupRatchetCollection(for: groupPublicKey)
|
||||
var result: [ClosedGroupRatchet] = []
|
||||
read { transaction in
|
||||
transaction.enumerateRows(inCollection: collection) { _, object, _, _ in
|
||||
guard let ratchet = object as? ClosedGroupRatchet else { return }
|
||||
result.append(ratchet)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
internal static func removeAllClosedGroupRatchets(for groupPublicKey: String, using transaction: YapDatabaseReadWriteTransaction) {
|
||||
let collection = getClosedGroupRatchetCollection(for: groupPublicKey)
|
||||
transaction.removeAllObjects(inCollection: collection)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -104,7 +104,7 @@ public final class SessionManagementProtocol : NSObject {
|
|||
let thread = TSContactThread.getOrCreateThread(withContactId: publicKey, transaction: transaction)
|
||||
thread.save(with: transaction)
|
||||
// Send the session request
|
||||
print("[Loki] Establishing session with: \(publicKey).")
|
||||
print("[Loki] Sending session request to: \(publicKey).")
|
||||
storage.setSessionRequestTimestamp(for: publicKey, to: Date(), in: transaction)
|
||||
let sessionRequestMessage = SessionRequestMessage(thread: thread)
|
||||
let messageSenderJobQueue = SSKEnvironment.shared.messageSenderJobQueue
|
||||
|
|
|
@ -137,7 +137,7 @@ public final class SyncMessagesProtocol : NSObject {
|
|||
newGroupThread.setGroupModel(newGroupModel, with: transaction)
|
||||
OWSDisappearingMessagesJob.shared().becomeConsistent(withDisappearingDuration: transcript.dataMessage.expireTimer, thread: newGroupThread, createdByRemoteRecipientId: nil, createdInExistingGroup: true, transaction: transaction)
|
||||
// Try to establish sessions with all members for which none exists yet when a group is created or updated
|
||||
ClosedGroupsProtocol.establishSessionsIfNeeded(with: members, in: newGroupThread, using: transaction)
|
||||
ClosedGroupsProtocol.establishSessionsIfNeeded(with: members, using: transaction)
|
||||
// Notify the user
|
||||
let contactsManager = SSKEnvironment.shared.contactsManager
|
||||
let infoMessageText = newGroupThread.groupModel.getInfoStringAboutUpdate(to: newGroupModel, contactsManager: contactsManager)
|
||||
|
@ -221,7 +221,7 @@ public final class SyncMessagesProtocol : NSObject {
|
|||
thread.shouldThreadBeVisible = true
|
||||
thread.save(with: transaction)
|
||||
}
|
||||
ClosedGroupsProtocol.establishSessionsIfNeeded(with: closedGroup.groupMemberIds, in: thread, using: transaction)
|
||||
ClosedGroupsProtocol.establishSessionsIfNeeded(with: closedGroup.groupMemberIds, using: transaction)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -502,14 +502,6 @@ NSError *EnsureDecryptError(NSError *_Nullable error, NSString *fallbackErrorDes
|
|||
return failureBlock(cipherError);
|
||||
}
|
||||
|
||||
ECKeyPair *keyPair = nil; // Loki: SMKSecretSessionCipher will fall back on the user's key pair if this is nil
|
||||
if (envelope.type == SSKProtoEnvelopeTypeClosedGroupCiphertext) {
|
||||
NSString *groupPrivateKey = [LKStorage getPrivateKeyForClosedGroupWithPublicKey:envelope.source];
|
||||
if (groupPrivateKey != nil) {
|
||||
keyPair = [[ECKeyPair alloc] initWithPublicKey:[NSData dataFromHexString:[envelope.source removing05PrefixIfNeeded]] privateKey:[NSData dataFromHexString:groupPrivateKey]];
|
||||
}
|
||||
}
|
||||
|
||||
NSError *decryptError;
|
||||
SMKDecryptResult *_Nullable decryptResult =
|
||||
[cipher throwswrapped_decryptMessageWithCertificateValidator:certificateValidator
|
||||
|
|
|
@ -547,7 +547,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
}
|
||||
}
|
||||
|
||||
[LKClosedGroupsProtocol handleSharedSenderKeysUpdateIfNeeded:dataMessage transaction:transaction];
|
||||
[LKClosedGroupsProtocol handleSharedSenderKeysUpdateIfNeeded:dataMessage from:envelope.source transaction:transaction];
|
||||
|
||||
if (dataMessage.group) {
|
||||
TSGroupThread *_Nullable groupThread =
|
||||
|
@ -1192,7 +1192,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
}
|
||||
|
||||
// Ensure sender is in the group.
|
||||
if (![gThread isUserInGroup:envelope.source transaction:transaction]) {
|
||||
if (![gThread isUserMemberInGroup:envelope.source transaction:transaction]) {
|
||||
OWSLogWarn(@"Ignoring 'Request Group Info' message for non-member of group. %@ not in %@",
|
||||
envelope.source,
|
||||
gThread.groupModel.groupMemberIds);
|
||||
|
@ -1321,7 +1321,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
switch (dataMessage.group.type) {
|
||||
case SSKProtoGroupContextTypeUpdate: {
|
||||
// Loki: Ignore updates from non-admins
|
||||
if ([LKClosedGroupsProtocol shouldIgnoreClosedGroupUpdateMessage:dataMessage inThread:oldGroupThread wrappedIn:envelope]) {
|
||||
if (oldGroupThread != nil && [LKClosedGroupsProtocol shouldIgnoreClosedGroupUpdateMessage:dataMessage inThread:oldGroupThread wrappedIn:envelope]) {
|
||||
return nil;
|
||||
}
|
||||
// Ensures that the thread exists but don't update it.
|
||||
|
@ -1343,7 +1343,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
BOOL wasCurrentUserRemovedFromGroup = [removedMemberIds containsObject:userMasterPublicKey];
|
||||
if (!wasCurrentUserRemovedFromGroup) {
|
||||
// Loki: Try to establish sessions with all members involved when a group is created or updated
|
||||
[LKClosedGroupsProtocol establishSessionsIfNeededWithClosedGroupMembers:newMemberIds.allObjects inThread:newGroupThread transaction:transaction];
|
||||
[LKClosedGroupsProtocol establishSessionsIfNeededWithClosedGroupMembers:newMemberIds.allObjects transaction:transaction];
|
||||
}
|
||||
|
||||
[[OWSDisappearingMessagesJob sharedJob] becomeConsistentWithDisappearingDuration:dataMessage.expireTimer
|
||||
|
|
|
@ -1710,133 +1710,6 @@ extension SSKProtoCallMessage.SSKProtoCallMessageBuilder {
|
|||
|
||||
#endif
|
||||
|
||||
// MARK: - SSKProtoClosedGroupCiphertext
|
||||
|
||||
@objc public class SSKProtoClosedGroupCiphertext: NSObject {
|
||||
|
||||
// MARK: - SSKProtoClosedGroupCiphertextBuilder
|
||||
|
||||
@objc public class func builder(ciphertext: Data, senderPublicKey: String, keyIndex: UInt32) -> SSKProtoClosedGroupCiphertextBuilder {
|
||||
return SSKProtoClosedGroupCiphertextBuilder(ciphertext: ciphertext, senderPublicKey: senderPublicKey, keyIndex: keyIndex)
|
||||
}
|
||||
|
||||
// asBuilder() constructs a builder that reflects the proto's contents.
|
||||
@objc public func asBuilder() -> SSKProtoClosedGroupCiphertextBuilder {
|
||||
let builder = SSKProtoClosedGroupCiphertextBuilder(ciphertext: ciphertext, senderPublicKey: senderPublicKey, keyIndex: keyIndex)
|
||||
return builder
|
||||
}
|
||||
|
||||
@objc public class SSKProtoClosedGroupCiphertextBuilder: NSObject {
|
||||
|
||||
private var proto = SignalServiceProtos_ClosedGroupCiphertext()
|
||||
|
||||
@objc fileprivate override init() {}
|
||||
|
||||
@objc fileprivate init(ciphertext: Data, senderPublicKey: String, keyIndex: UInt32) {
|
||||
super.init()
|
||||
|
||||
setCiphertext(ciphertext)
|
||||
setSenderPublicKey(senderPublicKey)
|
||||
setKeyIndex(keyIndex)
|
||||
}
|
||||
|
||||
@objc public func setCiphertext(_ valueParam: Data) {
|
||||
proto.ciphertext = valueParam
|
||||
}
|
||||
|
||||
@objc public func setSenderPublicKey(_ valueParam: String) {
|
||||
proto.senderPublicKey = valueParam
|
||||
}
|
||||
|
||||
@objc public func setKeyIndex(_ valueParam: UInt32) {
|
||||
proto.keyIndex = valueParam
|
||||
}
|
||||
|
||||
@objc public func build() throws -> SSKProtoClosedGroupCiphertext {
|
||||
return try SSKProtoClosedGroupCiphertext.parseProto(proto)
|
||||
}
|
||||
|
||||
@objc public func buildSerializedData() throws -> Data {
|
||||
return try SSKProtoClosedGroupCiphertext.parseProto(proto).serializedData()
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate let proto: SignalServiceProtos_ClosedGroupCiphertext
|
||||
|
||||
@objc public let ciphertext: Data
|
||||
|
||||
@objc public let senderPublicKey: String
|
||||
|
||||
@objc public let keyIndex: UInt32
|
||||
|
||||
private init(proto: SignalServiceProtos_ClosedGroupCiphertext,
|
||||
ciphertext: Data,
|
||||
senderPublicKey: String,
|
||||
keyIndex: UInt32) {
|
||||
self.proto = proto
|
||||
self.ciphertext = ciphertext
|
||||
self.senderPublicKey = senderPublicKey
|
||||
self.keyIndex = keyIndex
|
||||
}
|
||||
|
||||
@objc
|
||||
public func serializedData() throws -> Data {
|
||||
return try self.proto.serializedData()
|
||||
}
|
||||
|
||||
@objc public class func parseData(_ serializedData: Data) throws -> SSKProtoClosedGroupCiphertext {
|
||||
let proto = try SignalServiceProtos_ClosedGroupCiphertext(serializedData: serializedData)
|
||||
return try parseProto(proto)
|
||||
}
|
||||
|
||||
fileprivate class func parseProto(_ proto: SignalServiceProtos_ClosedGroupCiphertext) throws -> SSKProtoClosedGroupCiphertext {
|
||||
guard proto.hasCiphertext else {
|
||||
throw SSKProtoError.invalidProtobuf(description: "\(logTag) missing required field: ciphertext")
|
||||
}
|
||||
let ciphertext = proto.ciphertext
|
||||
|
||||
guard proto.hasSenderPublicKey else {
|
||||
throw SSKProtoError.invalidProtobuf(description: "\(logTag) missing required field: senderPublicKey")
|
||||
}
|
||||
let senderPublicKey = proto.senderPublicKey
|
||||
|
||||
guard proto.hasKeyIndex else {
|
||||
throw SSKProtoError.invalidProtobuf(description: "\(logTag) missing required field: keyIndex")
|
||||
}
|
||||
let keyIndex = proto.keyIndex
|
||||
|
||||
// MARK: - Begin Validation Logic for SSKProtoClosedGroupCiphertext -
|
||||
|
||||
// MARK: - End Validation Logic for SSKProtoClosedGroupCiphertext -
|
||||
|
||||
let result = SSKProtoClosedGroupCiphertext(proto: proto,
|
||||
ciphertext: ciphertext,
|
||||
senderPublicKey: senderPublicKey,
|
||||
keyIndex: keyIndex)
|
||||
return result
|
||||
}
|
||||
|
||||
@objc public override var debugDescription: String {
|
||||
return "\(proto)"
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
|
||||
extension SSKProtoClosedGroupCiphertext {
|
||||
@objc public func serializedDataIgnoringErrors() -> Data? {
|
||||
return try! self.serializedData()
|
||||
}
|
||||
}
|
||||
|
||||
extension SSKProtoClosedGroupCiphertext.SSKProtoClosedGroupCiphertextBuilder {
|
||||
@objc public func buildIgnoringErrors() -> SSKProtoClosedGroupCiphertext? {
|
||||
return try! self.build()
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// MARK: - SSKProtoDataMessageQuoteQuotedAttachment
|
||||
|
||||
@objc public class SSKProtoDataMessageQuoteQuotedAttachment: NSObject {
|
||||
|
@ -3422,17 +3295,23 @@ extension SSKProtoDataMessageLokiProfile.SSKProtoDataMessageLokiProfileBuilder {
|
|||
|
||||
@objc public enum SSKProtoDataMessageClosedGroupUpdateType: Int32 {
|
||||
case new = 0
|
||||
case info = 1
|
||||
case chainKey = 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
|
||||
}
|
||||
}
|
||||
|
||||
private class func SSKProtoDataMessageClosedGroupUpdateTypeUnwrap(_ value: SSKProtoDataMessageClosedGroupUpdateType) -> SignalServiceProtos_DataMessage.ClosedGroupUpdate.TypeEnum {
|
||||
switch value {
|
||||
case .new: return .new
|
||||
case .info: return .info
|
||||
case .chainKey: return .chainKey
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -729,50 +729,6 @@ struct SignalServiceProtos_CallMessage {
|
|||
fileprivate var _profileKey: Data? = nil
|
||||
}
|
||||
|
||||
struct SignalServiceProtos_ClosedGroupCiphertext {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
||||
/// @required
|
||||
var ciphertext: Data {
|
||||
get {return _ciphertext ?? SwiftProtobuf.Internal.emptyData}
|
||||
set {_ciphertext = newValue}
|
||||
}
|
||||
/// Returns true if `ciphertext` has been explicitly set.
|
||||
var hasCiphertext: Bool {return self._ciphertext != nil}
|
||||
/// Clears the value of `ciphertext`. Subsequent reads from it will return its default value.
|
||||
mutating func clearCiphertext() {self._ciphertext = 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}
|
||||
|
||||
/// @required
|
||||
var keyIndex: UInt32 {
|
||||
get {return _keyIndex ?? 0}
|
||||
set {_keyIndex = newValue}
|
||||
}
|
||||
/// Returns true if `keyIndex` has been explicitly set.
|
||||
var hasKeyIndex: Bool {return self._keyIndex != nil}
|
||||
/// Clears the value of `keyIndex`. Subsequent reads from it will return its default value.
|
||||
mutating func clearKeyIndex() {self._keyIndex = nil}
|
||||
|
||||
var unknownFields = SwiftProtobuf.UnknownStorage()
|
||||
|
||||
init() {}
|
||||
|
||||
fileprivate var _ciphertext: Data? = nil
|
||||
fileprivate var _senderPublicKey: String? = nil
|
||||
fileprivate var _keyIndex: UInt32? = nil
|
||||
}
|
||||
|
||||
struct SignalServiceProtos_DataMessage {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
|
@ -1593,6 +1549,12 @@ struct SignalServiceProtos_DataMessage {
|
|||
/// groupPublicKey, name, groupPrivateKey, chainKeys, members, admins
|
||||
case new // = 0
|
||||
|
||||
/// groupPublicKey, name, chainKeys, members, admins
|
||||
case info // = 1
|
||||
|
||||
/// groupPublicKey, chainKeys
|
||||
case chainKey // = 2
|
||||
|
||||
init() {
|
||||
self = .new
|
||||
}
|
||||
|
@ -1600,6 +1562,8 @@ struct SignalServiceProtos_DataMessage {
|
|||
init?(rawValue: Int) {
|
||||
switch rawValue {
|
||||
case 0: self = .new
|
||||
case 1: self = .info
|
||||
case 2: self = .chainKey
|
||||
default: return nil
|
||||
}
|
||||
}
|
||||
|
@ -1607,6 +1571,8 @@ struct SignalServiceProtos_DataMessage {
|
|||
var rawValue: Int {
|
||||
switch self {
|
||||
case .new: return 0
|
||||
case .info: return 1
|
||||
case .chainKey: return 2
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3389,47 +3355,6 @@ extension SignalServiceProtos_CallMessage.Hangup: SwiftProtobuf.Message, SwiftPr
|
|||
}
|
||||
}
|
||||
|
||||
extension SignalServiceProtos_ClosedGroupCiphertext: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
|
||||
static let protoMessageName: String = _protobuf_package + ".ClosedGroupCiphertext"
|
||||
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
1: .same(proto: "ciphertext"),
|
||||
2: .same(proto: "senderPublicKey"),
|
||||
3: .same(proto: "keyIndex"),
|
||||
]
|
||||
|
||||
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
while let fieldNumber = try decoder.nextFieldNumber() {
|
||||
switch fieldNumber {
|
||||
case 1: try decoder.decodeSingularBytesField(value: &self._ciphertext)
|
||||
case 2: try decoder.decodeSingularStringField(value: &self._senderPublicKey)
|
||||
case 3: try decoder.decodeSingularUInt32Field(value: &self._keyIndex)
|
||||
default: break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
|
||||
if let v = self._ciphertext {
|
||||
try visitor.visitSingularBytesField(value: v, fieldNumber: 1)
|
||||
}
|
||||
if let v = self._senderPublicKey {
|
||||
try visitor.visitSingularStringField(value: v, fieldNumber: 2)
|
||||
}
|
||||
if let v = self._keyIndex {
|
||||
try visitor.visitSingularUInt32Field(value: v, fieldNumber: 3)
|
||||
}
|
||||
try unknownFields.traverse(visitor: &visitor)
|
||||
}
|
||||
|
||||
static func ==(lhs: SignalServiceProtos_ClosedGroupCiphertext, rhs: SignalServiceProtos_ClosedGroupCiphertext) -> Bool {
|
||||
if lhs._ciphertext != rhs._ciphertext {return false}
|
||||
if lhs._senderPublicKey != rhs._senderPublicKey {return false}
|
||||
if lhs._keyIndex != rhs._keyIndex {return false}
|
||||
if lhs.unknownFields != rhs.unknownFields {return false}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
extension SignalServiceProtos_DataMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
|
||||
static let protoMessageName: String = _protobuf_package + ".DataMessage"
|
||||
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
|
@ -4124,6 +4049,8 @@ extension SignalServiceProtos_DataMessage.ClosedGroupUpdate: SwiftProtobuf.Messa
|
|||
extension SignalServiceProtos_DataMessage.ClosedGroupUpdate.TypeEnum: SwiftProtobuf._ProtoNameProviding {
|
||||
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
0: .same(proto: "NEW"),
|
||||
1: .same(proto: "INFO"),
|
||||
2: .same(proto: "CHAIN_KEY"),
|
||||
]
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue