pull/218/head
nielsandriesse 5 years ago
parent 00abf4e5fe
commit 4cb1cb6b93

@ -250,11 +250,11 @@ message DataMessage {
message SenderKey {
// @required
optional bytes chainKey = 1;
optional bytes chainKey = 1;
// @required
optional uint32 keyIndex = 2;
optional uint32 keyIndex = 2;
// @required
optional string senderPublicKey = 3;
optional string publicKey = 3;
}
optional string name = 1;

@ -2,48 +2,48 @@
internal final class ClosedGroupSenderKey : NSObject, NSCoding {
internal let chainKey: Data
internal let keyIndex: UInt
internal let senderPublicKey: String
internal let publicKey: String
// MARK: Initialization
init(chainKey: Data, keyIndex: UInt, senderPublicKey: String) {
init(chainKey: Data, keyIndex: UInt, publicKey: String) {
self.chainKey = chainKey
self.keyIndex = keyIndex
self.senderPublicKey = senderPublicKey
self.publicKey = publicKey
}
// MARK: Coding
public init?(coder: NSCoder) {
guard let chainKey = coder.decodeObject(forKey: "chainKey") as? Data,
let keyIndex = coder.decodeObject(forKey: "keyIndex") as? UInt,
let senderPublicKey = coder.decodeObject(forKey: "senderPublicKey") as? String else { return nil }
let publicKey = coder.decodeObject(forKey: "publicKey") as? String else { return nil }
self.chainKey = chainKey
self.keyIndex = UInt(keyIndex)
self.senderPublicKey = senderPublicKey
self.publicKey = publicKey
super.init()
}
public func encode(with coder: NSCoder) {
coder.encode(chainKey, forKey: "chainKey")
coder.encode(keyIndex, forKey: "keyIndex")
coder.encode(senderPublicKey, forKey: "senderPublicKey")
coder.encode(publicKey, forKey: "publicKey")
}
// MARK: Proto Conversion
internal func toProto() throws -> SSKProtoDataMessageClosedGroupUpdateSenderKey {
return try SSKProtoDataMessageClosedGroupUpdateSenderKey.builder(chainKey: chainKey, keyIndex: UInt32(keyIndex), senderPublicKey: senderPublicKey).build()
return try SSKProtoDataMessageClosedGroupUpdateSenderKey.builder(chainKey: chainKey, keyIndex: UInt32(keyIndex), publicKey: publicKey).build()
}
// MARK: Equality
override public func isEqual(_ other: Any?) -> Bool {
guard let other = other as? ClosedGroupSenderKey else { return false }
return chainKey == other.chainKey && keyIndex == other.keyIndex && senderPublicKey == other.senderPublicKey
return chainKey == other.chainKey && keyIndex == other.keyIndex && publicKey == other.publicKey
}
// MARK: Hashing
override public var hash: Int { // Override NSObject.hash and not Hashable.hashValue or Hashable.hash(into:)
return chainKey.hashValue ^ keyIndex.hashValue ^ senderPublicKey.hashValue
return chainKey.hashValue ^ keyIndex.hashValue ^ publicKey.hashValue
}
// MARK: Description
override public var description: String { return "[ chainKey : \(chainKey), keyIndex : \(keyIndex), senderPublicKey: \(senderPublicKey) ]" }
override public var description: String { return "[ chainKey : \(chainKey), keyIndex : \(keyIndex), publicKey: \(publicKey) ]" }
}

@ -16,48 +16,43 @@ public final class ClosedGroupsProtocol : NSObject {
/// - Note: It's recommended to batch fetch the device links for the given set of members before invoking this, to avoid the message sending pipeline
/// making a request for each member.
public static func createClosedGroup(name: String, members membersAsSet: Set<String>, transaction: YapDatabaseReadWriteTransaction) -> TSGroupThread {
public static func createClosedGroup(name: String, members: Set<String>, transaction: YapDatabaseReadWriteTransaction) -> TSGroupThread {
// Prepare
var membersAsSet = membersAsSet
var members = members
let messageSenderJobQueue = SSKEnvironment.shared.messageSenderJobQueue
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's master device is the one that's included in the member list
membersAsSet.remove(userPublicKey)
membersAsSet.insert(UserDefaults.standard[.masterHexEncodedPublicKey] ?? userPublicKey)
// Create ratchets for all members (and their linked devices). The sorting that happens is needed because the receiving end assumes that the member
// list and sender key list are ordered the same.
var membersAndLinkedDevicesAsSet: Set<String> = []
for member in membersAsSet {
members.remove(userPublicKey)
members.insert(UserDefaults.standard[.masterHexEncodedPublicKey] ?? userPublicKey)
// Create ratchets for all members (and their linked devices)
var membersAndLinkedDevices: Set<String> = []
for member in members {
let deviceLinks = OWSPrimaryStorage.shared().getDeviceLinks(for: member, in: transaction)
membersAndLinkedDevicesAsSet.formUnion(deviceLinks.flatMap { [ $0.master.hexEncodedPublicKey, $0.slave.hexEncodedPublicKey ] })
membersAndLinkedDevices.formUnion(deviceLinks.flatMap { [ $0.master.hexEncodedPublicKey, $0.slave.hexEncodedPublicKey ] })
}
let members = [String](membersAsSet).sorted()
let membersAndLinkedDevices = [String](membersAndLinkedDevicesAsSet).sorted()
let ratchets = membersAndLinkedDevices.map {
SharedSenderKeysImplementation.shared.generateRatchet(for: groupPublicKey, senderPublicKey: $0, using: transaction)
let senderKeys: [ClosedGroupSenderKey] = membersAndLinkedDevices.map { publicKey in
let ratchet = SharedSenderKeysImplementation.shared.generateRatchet(for: groupPublicKey, senderPublicKey: publicKey, using: transaction)
return ClosedGroupSenderKey(chainKey: Data(hex: ratchet.chainKey), keyIndex: ratchet.keyIndex, publicKey: publicKey)
}
// Create the group
let admins = [ UserDefaults.standard[.masterHexEncodedPublicKey] ?? userPublicKey ]
let groupID = LKGroupUtilities.getEncodedClosedGroupIDAsData(groupPublicKey)
let group = TSGroupModel(title: name, memberIds: members, image: nil, groupId: groupID, groupType: .closedGroup, adminIds: admins)
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)
SSKEnvironment.shared.profileManager.addThread(toProfileWhitelist: thread)
// Establish sessions if needed
establishSessionsIfNeeded(with: members, using: transaction) // Not `membersAndLinkedDevices` as this internally takes care of multi device already
establishSessionsIfNeeded(with: [String](members), using: transaction) // Not `membersAndLinkedDevices` as this internally takes care of multi device already
// Send a closed group update message to all members (and their linked devices) using established channels
let senderKeys = zip(ratchets, membersAndLinkedDevices).map { ratchet, publicKey in
ClosedGroupSenderKey(chainKey: Data(hex: ratchet.chainKey), keyIndex: ratchet.keyIndex, senderPublicKey: publicKey)
}
for member in members { // Not `membersAndLinkedDevices` as this internally takes care of multi device already
let thread = TSContactThread.getOrCreateThread(withContactId: member, transaction: transaction)
thread.save(with: transaction)
let closedGroupUpdateMessageKind = ClosedGroupUpdateMessage.Kind.new(groupPublicKey: Data(hex: groupPublicKey), name: name,
groupPrivateKey: groupKeyPair.privateKey, senderKeys: senderKeys, members: members, admins: admins)
groupPrivateKey: groupKeyPair.privateKey, senderKeys: senderKeys, members: [String](members), admins: admins)
let closedGroupUpdateMessage = ClosedGroupUpdateMessage(thread: thread, kind: closedGroupUpdateMessageKind)
messageSenderJobQueue.add(message: closedGroupUpdateMessage, transaction: transaction)
}
@ -70,7 +65,7 @@ public final class ClosedGroupsProtocol : NSObject {
return thread
}
public static func addMembers(_ newMembersAsSet: Set<String>, to groupPublicKey: String, using transaction: YapDatabaseReadWriteTransaction) {
public static func addMembers(_ newMembers: Set<String>, to groupPublicKey: String, using transaction: YapDatabaseReadWriteTransaction) {
// Prepare
let messageSenderJobQueue = SSKEnvironment.shared.messageSenderJobQueue
let groupID = LKGroupUtilities.getEncodedClosedGroupID(groupPublicKey)
@ -85,32 +80,27 @@ public final class ClosedGroupsProtocol : NSObject {
}
// Add the members to the member list
var members = group.groupMemberIds
members.append(contentsOf: newMembersAsSet)
// Generate ratchets for the new members (and their linked devices). The sorting that happens is needed because the receiving end assumes that the member
// list and sender key list are ordered the same.
var newMembersAndLinkedDevicesAsSet: Set<String> = []
for member in newMembersAsSet {
members.append(contentsOf: newMembers)
// Generate ratchets for the new members (and their linked devices)
var newMembersAndLinkedDevices: Set<String> = []
for member in newMembers {
let deviceLinks = OWSPrimaryStorage.shared().getDeviceLinks(for: member, in: transaction)
newMembersAndLinkedDevicesAsSet.formUnion(deviceLinks.flatMap { [ $0.master.hexEncodedPublicKey, $0.slave.hexEncodedPublicKey ] })
newMembersAndLinkedDevices.formUnion(deviceLinks.flatMap { [ $0.master.hexEncodedPublicKey, $0.slave.hexEncodedPublicKey ] })
}
members = members.sorted()
let newMembersAndLinkedDevices = [String](newMembersAndLinkedDevicesAsSet).sorted()
let ratchets = newMembersAndLinkedDevices.map {
SharedSenderKeysImplementation.shared.generateRatchet(for: groupPublicKey, senderPublicKey: $0, using: transaction)
let senderKeys: [ClosedGroupSenderKey] = newMembersAndLinkedDevices.map { publicKey in
let ratchet = SharedSenderKeysImplementation.shared.generateRatchet(for: groupPublicKey, senderPublicKey: publicKey, using: transaction)
return ClosedGroupSenderKey(chainKey: Data(hex: ratchet.chainKey), keyIndex: ratchet.keyIndex, publicKey: publicKey)
}
// Send a closed group update message to the existing members with the new members' ratchets (this message is aimed at the group)
let senderKeys = zip(ratchets, newMembersAndLinkedDevices).map { ratchet, publicKey in
ClosedGroupSenderKey(chainKey: Data(hex: ratchet.chainKey), keyIndex: ratchet.keyIndex, senderPublicKey: publicKey)
}
let closedGroupUpdateMessageKind = ClosedGroupUpdateMessage.Kind.info(groupPublicKey: Data(hex: groupPublicKey), name: name, senderKeys: senderKeys,
members: members, admins: admins)
let closedGroupUpdateMessage = ClosedGroupUpdateMessage(thread: thread, kind: closedGroupUpdateMessageKind)
messageSenderJobQueue.add(message: closedGroupUpdateMessage, transaction: transaction)
// Establish sessions if needed
establishSessionsIfNeeded(with: [String](newMembersAsSet), using: transaction) // Not `newMembersAndLinkedDevices` as this internally takes care of multi device already
establishSessionsIfNeeded(with: [String](newMembers), using: transaction) // Not `newMembersAndLinkedDevices` as this internally takes care of multi device already
// Send closed group update messages to the new members (and their linked devices) using established channels
let allSenderKeys = [ClosedGroupSenderKey](Storage.getAllClosedGroupSenderKeys(for: groupPublicKey)) // This includes the newly generated sender keys
for member in newMembersAsSet { // Not `newMembersAndLinkedDevices` as this internally takes care of multi device already
for member in newMembers { // Not `newMembersAndLinkedDevices` as this internally takes care of multi device already
let thread = TSContactThread.getOrCreateThread(contactId: member)
thread.save(with: transaction)
let closedGroupUpdateMessageKind = ClosedGroupUpdateMessage.Kind.new(groupPublicKey: Data(hex: groupPublicKey), name: name,
@ -169,7 +159,7 @@ public final class ClosedGroupsProtocol : NSObject {
establishSessionsIfNeeded(with: members, using: transaction) // This internally takes care of multi device
// Send out the user's new ratchet to all members (minus the removed ones) and their linked devices using established channels
let userRatchet = SharedSenderKeysImplementation.shared.generateRatchet(for: groupPublicKey, senderPublicKey: userPublicKey, using: transaction)
let userSenderKey = ClosedGroupSenderKey(chainKey: Data(hex: userRatchet.chainKey), keyIndex: userRatchet.keyIndex, senderPublicKey: userPublicKey)
let userSenderKey = ClosedGroupSenderKey(chainKey: Data(hex: userRatchet.chainKey), keyIndex: userRatchet.keyIndex, publicKey: userPublicKey)
for member in members { // This internally takes care of multi device
let thread = TSContactThread.getOrCreateThread(withContactId: member, transaction: transaction)
thread.save(with: transaction)
@ -210,9 +200,9 @@ public final class ClosedGroupsProtocol : NSObject {
let members = closedGroupUpdate.members
let admins = closedGroupUpdate.admins
// Persist the ratchets
zip(members, senderKeys).forEach { (member, senderKey) in
senderKeys.forEach { senderKey in
let ratchet = ClosedGroupRatchet(chainKey: senderKey.chainKey.toHexString(), keyIndex: UInt(senderKey.keyIndex), messageKeys: [])
Storage.setClosedGroupRatchet(for: groupPublicKey, senderPublicKey: member, ratchet: ratchet, using: transaction)
Storage.setClosedGroupRatchet(for: groupPublicKey, senderPublicKey: senderKey.publicKey, ratchet: ratchet, using: transaction)
}
// Create the group
let groupID = LKGroupUtilities.getEncodedClosedGroupIDAsData(groupPublicKey)
@ -259,7 +249,7 @@ public final class ClosedGroupsProtocol : NSObject {
// Store the ratchets for any new members (it's important that this happens before the code below)
senderKeys.forEach { senderKey in
let ratchet = ClosedGroupRatchet(chainKey: senderKey.chainKey.toHexString(), keyIndex: UInt(senderKey.keyIndex), messageKeys: [])
Storage.setClosedGroupRatchet(for: groupPublicKey, senderPublicKey: senderKey.senderPublicKey, ratchet: ratchet, using: transaction)
Storage.setClosedGroupRatchet(for: groupPublicKey, senderPublicKey: senderKey.publicKey, ratchet: ratchet, using: transaction)
}
// Delete all ratchets and send out the user's new ratchet using established channels if any member of the group left or was removed
let oldMembers = group.groupMemberIds
@ -267,7 +257,7 @@ public final class ClosedGroupsProtocol : NSObject {
Storage.removeAllClosedGroupRatchets(for: groupPublicKey, using: transaction)
let userPublicKey = getUserHexEncodedPublicKey()
let userRatchet = SharedSenderKeysImplementation.shared.generateRatchet(for: groupPublicKey, senderPublicKey: userPublicKey, using: transaction)
let userSenderKey = ClosedGroupSenderKey(chainKey: Data(hex: userRatchet.chainKey), keyIndex: userRatchet.keyIndex, senderPublicKey: userPublicKey)
let userSenderKey = ClosedGroupSenderKey(chainKey: Data(hex: userRatchet.chainKey), keyIndex: userRatchet.keyIndex, publicKey: userPublicKey)
for member in members {
let thread = TSContactThread.getOrCreateThread(withContactId: member, transaction: transaction)
thread.save(with: transaction)

@ -25,8 +25,8 @@ internal extension Storage {
var result: Set<ClosedGroupSenderKey> = []
read { transaction in
transaction.enumerateRows(inCollection: collection) { key, object, _, _ in
guard let senderPublicKey = key as? String, let ratchet = object as? ClosedGroupRatchet else { return }
let senderKey = ClosedGroupSenderKey(chainKey: Data(hex: ratchet.chainKey), keyIndex: ratchet.keyIndex, senderPublicKey: senderPublicKey)
guard let publicKey = key as? String, let ratchet = object as? ClosedGroupRatchet else { return }
let senderKey = ClosedGroupSenderKey(chainKey: Data(hex: ratchet.chainKey), keyIndex: ratchet.keyIndex, publicKey: publicKey)
result.insert(senderKey)
}
}

@ -3293,13 +3293,13 @@ extension SSKProtoDataMessageLokiProfile.SSKProtoDataMessageLokiProfileBuilder {
// MARK: - SSKProtoDataMessageClosedGroupUpdateSenderKeyBuilder
@objc public class func builder(chainKey: Data, keyIndex: UInt32, senderPublicKey: String) -> SSKProtoDataMessageClosedGroupUpdateSenderKeyBuilder {
return SSKProtoDataMessageClosedGroupUpdateSenderKeyBuilder(chainKey: chainKey, keyIndex: keyIndex, senderPublicKey: senderPublicKey)
@objc public class func builder(chainKey: Data, keyIndex: UInt32, publicKey: String) -> SSKProtoDataMessageClosedGroupUpdateSenderKeyBuilder {
return SSKProtoDataMessageClosedGroupUpdateSenderKeyBuilder(chainKey: chainKey, keyIndex: keyIndex, publicKey: publicKey)
}
// asBuilder() constructs a builder that reflects the proto's contents.
@objc public func asBuilder() -> SSKProtoDataMessageClosedGroupUpdateSenderKeyBuilder {
let builder = SSKProtoDataMessageClosedGroupUpdateSenderKeyBuilder(chainKey: chainKey, keyIndex: keyIndex, senderPublicKey: senderPublicKey)
let builder = SSKProtoDataMessageClosedGroupUpdateSenderKeyBuilder(chainKey: chainKey, keyIndex: keyIndex, publicKey: publicKey)
return builder
}
@ -3309,12 +3309,12 @@ extension SSKProtoDataMessageLokiProfile.SSKProtoDataMessageLokiProfileBuilder {
@objc fileprivate override init() {}
@objc fileprivate init(chainKey: Data, keyIndex: UInt32, senderPublicKey: String) {
@objc fileprivate init(chainKey: Data, keyIndex: UInt32, publicKey: String) {
super.init()
setChainKey(chainKey)
setKeyIndex(keyIndex)
setSenderPublicKey(senderPublicKey)
setPublicKey(publicKey)
}
@objc public func setChainKey(_ valueParam: Data) {
@ -3325,8 +3325,8 @@ extension SSKProtoDataMessageLokiProfile.SSKProtoDataMessageLokiProfileBuilder {
proto.keyIndex = valueParam
}
@objc public func setSenderPublicKey(_ valueParam: String) {
proto.senderPublicKey = valueParam
@objc public func setPublicKey(_ valueParam: String) {
proto.publicKey = valueParam
}
@objc public func build() throws -> SSKProtoDataMessageClosedGroupUpdateSenderKey {
@ -3344,16 +3344,16 @@ extension SSKProtoDataMessageLokiProfile.SSKProtoDataMessageLokiProfileBuilder {
@objc public let keyIndex: UInt32
@objc public let senderPublicKey: String
@objc public let publicKey: String
private init(proto: SignalServiceProtos_DataMessage.ClosedGroupUpdate.SenderKey,
chainKey: Data,
keyIndex: UInt32,
senderPublicKey: String) {
publicKey: String) {
self.proto = proto
self.chainKey = chainKey
self.keyIndex = keyIndex
self.senderPublicKey = senderPublicKey
self.publicKey = publicKey
}
@objc
@ -3377,10 +3377,10 @@ extension SSKProtoDataMessageLokiProfile.SSKProtoDataMessageLokiProfileBuilder {
}
let keyIndex = proto.keyIndex
guard proto.hasSenderPublicKey else {
throw SSKProtoError.invalidProtobuf(description: "\(logTag) missing required field: senderPublicKey")
guard proto.hasPublicKey else {
throw SSKProtoError.invalidProtobuf(description: "\(logTag) missing required field: publicKey")
}
let senderPublicKey = proto.senderPublicKey
let publicKey = proto.publicKey
// MARK: - Begin Validation Logic for SSKProtoDataMessageClosedGroupUpdateSenderKey -
@ -3389,7 +3389,7 @@ extension SSKProtoDataMessageLokiProfile.SSKProtoDataMessageLokiProfileBuilder {
let result = SSKProtoDataMessageClosedGroupUpdateSenderKey(proto: proto,
chainKey: chainKey,
keyIndex: keyIndex,
senderPublicKey: senderPublicKey)
publicKey: publicKey)
return result
}

@ -1604,14 +1604,14 @@ struct SignalServiceProtos_DataMessage {
mutating func clearKeyIndex() {self._keyIndex = nil}
/// @required
var senderPublicKey: String {
get {return _senderPublicKey ?? String()}
set {_senderPublicKey = newValue}
var publicKey: String {
get {return _publicKey ?? String()}
set {_publicKey = 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}
/// Returns true if `publicKey` has been explicitly set.
var hasPublicKey: Bool {return self._publicKey != nil}
/// Clears the value of `publicKey`. Subsequent reads from it will return its default value.
mutating func clearPublicKey() {self._publicKey = nil}
var unknownFields = SwiftProtobuf.UnknownStorage()
@ -1619,7 +1619,7 @@ struct SignalServiceProtos_DataMessage {
fileprivate var _chainKey: Data? = nil
fileprivate var _keyIndex: UInt32? = nil
fileprivate var _senderPublicKey: String? = nil
fileprivate var _publicKey: String? = nil
}
init() {}
@ -4103,7 +4103,7 @@ extension SignalServiceProtos_DataMessage.ClosedGroupUpdate.SenderKey: SwiftProt
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "chainKey"),
2: .same(proto: "keyIndex"),
3: .same(proto: "senderPublicKey"),
3: .same(proto: "publicKey"),
]
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
@ -4111,7 +4111,7 @@ extension SignalServiceProtos_DataMessage.ClosedGroupUpdate.SenderKey: SwiftProt
switch fieldNumber {
case 1: try decoder.decodeSingularBytesField(value: &self._chainKey)
case 2: try decoder.decodeSingularUInt32Field(value: &self._keyIndex)
case 3: try decoder.decodeSingularStringField(value: &self._senderPublicKey)
case 3: try decoder.decodeSingularStringField(value: &self._publicKey)
default: break
}
}
@ -4124,7 +4124,7 @@ extension SignalServiceProtos_DataMessage.ClosedGroupUpdate.SenderKey: SwiftProt
if let v = self._keyIndex {
try visitor.visitSingularUInt32Field(value: v, fieldNumber: 2)
}
if let v = self._senderPublicKey {
if let v = self._publicKey {
try visitor.visitSingularStringField(value: v, fieldNumber: 3)
}
try unknownFields.traverse(visitor: &visitor)
@ -4133,7 +4133,7 @@ extension SignalServiceProtos_DataMessage.ClosedGroupUpdate.SenderKey: SwiftProt
static func ==(lhs: SignalServiceProtos_DataMessage.ClosedGroupUpdate.SenderKey, rhs: SignalServiceProtos_DataMessage.ClosedGroupUpdate.SenderKey) -> Bool {
if lhs._chainKey != rhs._chainKey {return false}
if lhs._keyIndex != rhs._keyIndex {return false}
if lhs._senderPublicKey != rhs._senderPublicKey {return false}
if lhs._publicKey != rhs._publicKey {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}

Loading…
Cancel
Save