Include the sender public key in sender key messages

This commit is contained in:
nielsandriesse 2020-07-06 14:39:55 +10:00
parent b12937ef00
commit 00abf4e5fe
7 changed files with 85 additions and 44 deletions

View file

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

View file

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

View file

@ -103,7 +103,7 @@ internal final class ClosedGroupUpdateMessage : TSOutgoingMessage {
closedGroupUpdate.setMembers(members)
closedGroupUpdate.setAdmins(admins)
case .senderKey(let groupPublicKey, let senderKey):
closedGroupUpdate = SSKProtoDataMessageClosedGroupUpdate.builder(groupPublicKey: groupPublicKey, type: .chainKey)
closedGroupUpdate = SSKProtoDataMessageClosedGroupUpdate.builder(groupPublicKey: groupPublicKey, type: .senderKey)
closedGroupUpdate.setSenderKeys([ try senderKey.toProto() ])
}
builder.setClosedGroupUpdate(try closedGroupUpdate.build())

View file

@ -50,7 +50,9 @@ public final class ClosedGroupsProtocol : NSObject {
// Establish sessions if needed
establishSessionsIfNeeded(with: members, using: transaction) // Not `membersAndLinkedDevices` as this internally takes care of multi device already
// Send a closed group update message to all members (and their linked devices) using established channels
let senderKeys = ratchets.map { ClosedGroupSenderKey(chainKey: Data(hex: $0.chainKey), keyIndex: $0.keyIndex) }
let senderKeys = zip(ratchets, membersAndLinkedDevices).map { ratchet, publicKey in
ClosedGroupSenderKey(chainKey: Data(hex: ratchet.chainKey), keyIndex: ratchet.keyIndex, senderPublicKey: publicKey)
}
for member in members { // Not `membersAndLinkedDevices` as this internally takes care of multi device already
let thread = TSContactThread.getOrCreateThread(withContactId: member, transaction: transaction)
thread.save(with: transaction)
@ -97,7 +99,9 @@ public final class ClosedGroupsProtocol : NSObject {
SharedSenderKeysImplementation.shared.generateRatchet(for: groupPublicKey, senderPublicKey: $0, using: transaction)
}
// Send a closed group update message to the existing members with the new members' ratchets (this message is aimed at the group)
let senderKeys = ratchets.map { ClosedGroupSenderKey(chainKey: Data(hex: $0.chainKey), keyIndex: $0.keyIndex) }
let senderKeys = zip(ratchets, newMembersAndLinkedDevices).map { ratchet, publicKey in
ClosedGroupSenderKey(chainKey: Data(hex: ratchet.chainKey), keyIndex: ratchet.keyIndex, senderPublicKey: publicKey)
}
let closedGroupUpdateMessageKind = ClosedGroupUpdateMessage.Kind.info(groupPublicKey: Data(hex: groupPublicKey), name: name, senderKeys: senderKeys,
members: members, admins: admins)
let closedGroupUpdateMessage = ClosedGroupUpdateMessage(thread: thread, kind: closedGroupUpdateMessageKind)
@ -105,9 +109,7 @@ public final class ClosedGroupsProtocol : NSObject {
// Establish sessions if needed
establishSessionsIfNeeded(with: [String](newMembersAsSet), using: transaction) // Not `newMembersAndLinkedDevices` as this internally takes care of multi device already
// Send closed group update messages to the new members (and their linked devices) using established channels
let allSenderKeys = Storage.getAllClosedGroupRatchets(for: groupPublicKey).map { // This includes the newly generated ratchets
ClosedGroupSenderKey(chainKey: Data(hex: $0.chainKey), keyIndex: $0.keyIndex)
}
let allSenderKeys = [ClosedGroupSenderKey](Storage.getAllClosedGroupSenderKeys(for: groupPublicKey)) // This includes the newly generated sender keys
for member in newMembersAsSet { // Not `newMembersAndLinkedDevices` as this internally takes care of multi device already
let thread = TSContactThread.getOrCreateThread(contactId: member)
thread.save(with: transaction)
@ -167,7 +169,7 @@ public final class ClosedGroupsProtocol : NSObject {
establishSessionsIfNeeded(with: members, using: transaction) // This internally takes care of multi device
// Send out the user's new ratchet to all members (minus the removed ones) and their linked devices using established channels
let userRatchet = SharedSenderKeysImplementation.shared.generateRatchet(for: groupPublicKey, senderPublicKey: userPublicKey, using: transaction)
let userSenderKey = ClosedGroupSenderKey(chainKey: Data(hex: userRatchet.chainKey), keyIndex: userRatchet.keyIndex)
let userSenderKey = ClosedGroupSenderKey(chainKey: Data(hex: userRatchet.chainKey), keyIndex: userRatchet.keyIndex, senderPublicKey: userPublicKey)
for member in members { // This internally takes care of multi device
let thread = TSContactThread.getOrCreateThread(withContactId: member, transaction: transaction)
thread.save(with: transaction)
@ -195,7 +197,7 @@ public final class ClosedGroupsProtocol : NSObject {
switch closedGroupUpdate.type {
case .new: handleNewGroupMessage(closedGroupUpdate, using: transaction)
case .info: handleInfoMessage(closedGroupUpdate, from: publicKey, using: transaction)
case .chainKey: handleChainKeyMessage(closedGroupUpdate, from: publicKey, using: transaction)
case .senderKey: handleSenderKeyMessage(closedGroupUpdate, from: publicKey, using: transaction)
}
}
@ -254,19 +256,18 @@ public final class ClosedGroupsProtocol : NSObject {
}
// Establish sessions if needed (it's important that this happens before the code below)
establishSessionsIfNeeded(with: members, using: transaction) // This internally takes care of multi device
// Parse out any new members and store their ratchets (it's important that this happens before the code below)
let oldMembers = group.groupMemberIds
let newMembers = members.filter { !oldMembers.contains($0) }
zip(newMembers, senderKeys).forEach { (member, senderKey) in
// Store the ratchets for any new members (it's important that this happens before the code below)
senderKeys.forEach { senderKey in
let ratchet = ClosedGroupRatchet(chainKey: senderKey.chainKey.toHexString(), keyIndex: UInt(senderKey.keyIndex), messageKeys: [])
Storage.setClosedGroupRatchet(for: groupPublicKey, senderPublicKey: member, ratchet: ratchet, using: transaction)
Storage.setClosedGroupRatchet(for: groupPublicKey, senderPublicKey: senderKey.senderPublicKey, ratchet: ratchet, using: transaction)
}
// Delete all ratchets and send out the user's new ratchet using established channels if any member of the group left or was removed
let oldMembers = group.groupMemberIds
if Set(members).intersection(oldMembers) != Set(oldMembers) {
Storage.removeAllClosedGroupRatchets(for: groupPublicKey, using: transaction)
let userPublicKey = getUserHexEncodedPublicKey()
let userRatchet = SharedSenderKeysImplementation.shared.generateRatchet(for: groupPublicKey, senderPublicKey: userPublicKey, using: transaction)
let userSenderKey = ClosedGroupSenderKey(chainKey: Data(hex: userRatchet.chainKey), keyIndex: userRatchet.keyIndex)
let userSenderKey = ClosedGroupSenderKey(chainKey: Data(hex: userRatchet.chainKey), keyIndex: userRatchet.keyIndex, senderPublicKey: userPublicKey)
for member in members {
let thread = TSContactThread.getOrCreateThread(withContactId: member, transaction: transaction)
thread.save(with: transaction)
@ -286,7 +287,7 @@ public final class ClosedGroupsProtocol : NSObject {
}
/// Invoked upon receiving a chain key from another user.
private static func handleChainKeyMessage(_ closedGroupUpdate: SSKProtoDataMessageClosedGroupUpdate, from senderPublicKey: String, using transaction: YapDatabaseReadWriteTransaction) {
private static func handleSenderKeyMessage(_ closedGroupUpdate: SSKProtoDataMessageClosedGroupUpdate, from senderPublicKey: String, using transaction: YapDatabaseReadWriteTransaction) {
let groupPublicKey = closedGroupUpdate.groupPublicKey.toHexString()
guard let senderKey = closedGroupUpdate.senderKeys.first else {
return print("[Loki] Ignoring invalid closed group update.")

View file

@ -20,13 +20,14 @@ internal extension Storage {
transaction.setObject(ratchet, forKey: senderPublicKey, inCollection: collection)
}
internal static func getAllClosedGroupRatchets(for groupPublicKey: String) -> Set<ClosedGroupRatchet> {
internal static func getAllClosedGroupSenderKeys(for groupPublicKey: String) -> Set<ClosedGroupSenderKey> {
let collection = getClosedGroupRatchetCollection(for: groupPublicKey)
var result: Set<ClosedGroupRatchet> = []
var result: Set<ClosedGroupSenderKey> = []
read { transaction in
transaction.enumerateRows(inCollection: collection) { _, object, _, _ in
guard let ratchet = object as? ClosedGroupRatchet else { return }
result.insert(ratchet)
transaction.enumerateRows(inCollection: collection) { key, object, _, _ in
guard let senderPublicKey = key as? String, let ratchet = object as? ClosedGroupRatchet else { return }
let senderKey = ClosedGroupSenderKey(chainKey: Data(hex: ratchet.chainKey), keyIndex: ratchet.keyIndex, senderPublicKey: senderPublicKey)
result.insert(senderKey)
}
}
return result

View file

@ -3293,13 +3293,13 @@ extension SSKProtoDataMessageLokiProfile.SSKProtoDataMessageLokiProfileBuilder {
// MARK: - SSKProtoDataMessageClosedGroupUpdateSenderKeyBuilder
@objc public class func builder(chainKey: Data, keyIndex: UInt32) -> SSKProtoDataMessageClosedGroupUpdateSenderKeyBuilder {
return SSKProtoDataMessageClosedGroupUpdateSenderKeyBuilder(chainKey: chainKey, keyIndex: keyIndex)
@objc public class func builder(chainKey: Data, keyIndex: UInt32, senderPublicKey: String) -> SSKProtoDataMessageClosedGroupUpdateSenderKeyBuilder {
return SSKProtoDataMessageClosedGroupUpdateSenderKeyBuilder(chainKey: chainKey, keyIndex: keyIndex, senderPublicKey: senderPublicKey)
}
// asBuilder() constructs a builder that reflects the proto's contents.
@objc public func asBuilder() -> SSKProtoDataMessageClosedGroupUpdateSenderKeyBuilder {
let builder = SSKProtoDataMessageClosedGroupUpdateSenderKeyBuilder(chainKey: chainKey, keyIndex: keyIndex)
let builder = SSKProtoDataMessageClosedGroupUpdateSenderKeyBuilder(chainKey: chainKey, keyIndex: keyIndex, senderPublicKey: senderPublicKey)
return builder
}
@ -3309,11 +3309,12 @@ extension SSKProtoDataMessageLokiProfile.SSKProtoDataMessageLokiProfileBuilder {
@objc fileprivate override init() {}
@objc fileprivate init(chainKey: Data, keyIndex: UInt32) {
@objc fileprivate init(chainKey: Data, keyIndex: UInt32, senderPublicKey: String) {
super.init()
setChainKey(chainKey)
setKeyIndex(keyIndex)
setSenderPublicKey(senderPublicKey)
}
@objc public func setChainKey(_ valueParam: Data) {
@ -3324,6 +3325,10 @@ extension SSKProtoDataMessageLokiProfile.SSKProtoDataMessageLokiProfileBuilder {
proto.keyIndex = valueParam
}
@objc public func setSenderPublicKey(_ valueParam: String) {
proto.senderPublicKey = valueParam
}
@objc public func build() throws -> SSKProtoDataMessageClosedGroupUpdateSenderKey {
return try SSKProtoDataMessageClosedGroupUpdateSenderKey.parseProto(proto)
}
@ -3339,12 +3344,16 @@ extension SSKProtoDataMessageLokiProfile.SSKProtoDataMessageLokiProfileBuilder {
@objc public let keyIndex: UInt32
@objc public let senderPublicKey: String
private init(proto: SignalServiceProtos_DataMessage.ClosedGroupUpdate.SenderKey,
chainKey: Data,
keyIndex: UInt32) {
keyIndex: UInt32,
senderPublicKey: String) {
self.proto = proto
self.chainKey = chainKey
self.keyIndex = keyIndex
self.senderPublicKey = senderPublicKey
}
@objc
@ -3368,13 +3377,19 @@ extension SSKProtoDataMessageLokiProfile.SSKProtoDataMessageLokiProfileBuilder {
}
let keyIndex = proto.keyIndex
guard proto.hasSenderPublicKey else {
throw SSKProtoError.invalidProtobuf(description: "\(logTag) missing required field: senderPublicKey")
}
let senderPublicKey = proto.senderPublicKey
// MARK: - Begin Validation Logic for SSKProtoDataMessageClosedGroupUpdateSenderKey -
// MARK: - End Validation Logic for SSKProtoDataMessageClosedGroupUpdateSenderKey -
let result = SSKProtoDataMessageClosedGroupUpdateSenderKey(proto: proto,
chainKey: chainKey,
keyIndex: keyIndex)
keyIndex: keyIndex,
senderPublicKey: senderPublicKey)
return result
}
@ -3408,14 +3423,14 @@ extension SSKProtoDataMessageClosedGroupUpdateSenderKey.SSKProtoDataMessageClose
@objc public enum SSKProtoDataMessageClosedGroupUpdateType: Int32 {
case new = 0
case info = 1
case chainKey = 2
case senderKey = 2
}
private class func SSKProtoDataMessageClosedGroupUpdateTypeWrap(_ value: SignalServiceProtos_DataMessage.ClosedGroupUpdate.TypeEnum) -> SSKProtoDataMessageClosedGroupUpdateType {
switch value {
case .new: return .new
case .info: return .info
case .chainKey: return .chainKey
case .senderKey: return .senderKey
}
}
@ -3423,7 +3438,7 @@ extension SSKProtoDataMessageClosedGroupUpdateSenderKey.SSKProtoDataMessageClose
switch value {
case .new: return .new
case .info: return .info
case .chainKey: return .chainKey
case .senderKey: return .senderKey
}
}

View file

@ -1546,14 +1546,14 @@ struct SignalServiceProtos_DataMessage {
enum TypeEnum: SwiftProtobuf.Enum {
typealias RawValue = Int
/// groupPublicKey, name, groupPrivateKey, chainKeys, members, admins
/// groupPublicKey, name, groupPrivateKey, senderKeys, members, admins
case new // = 0
/// groupPublicKey, name, chainKeys, members, admins
/// groupPublicKey, name, senderKeys, members, admins
case info // = 1
/// groupPublicKey, chainKeys
case chainKey // = 2
/// groupPublicKey, senderKeys
case senderKey // = 2
init() {
self = .new
@ -1563,7 +1563,7 @@ struct SignalServiceProtos_DataMessage {
switch rawValue {
case 0: self = .new
case 1: self = .info
case 2: self = .chainKey
case 2: self = .senderKey
default: return nil
}
}
@ -1572,7 +1572,7 @@ struct SignalServiceProtos_DataMessage {
switch self {
case .new: return 0
case .info: return 1
case .chainKey: return 2
case .senderKey: return 2
}
}
@ -1603,12 +1603,23 @@ struct SignalServiceProtos_DataMessage {
/// Clears the value of `keyIndex`. Subsequent reads from it will return its default value.
mutating func clearKeyIndex() {self._keyIndex = nil}
/// @required
var senderPublicKey: String {
get {return _senderPublicKey ?? String()}
set {_senderPublicKey = newValue}
}
/// Returns true if `senderPublicKey` has been explicitly set.
var hasSenderPublicKey: Bool {return self._senderPublicKey != nil}
/// Clears the value of `senderPublicKey`. Subsequent reads from it will return its default value.
mutating func clearSenderPublicKey() {self._senderPublicKey = nil}
var unknownFields = SwiftProtobuf.UnknownStorage()
init() {}
fileprivate var _chainKey: Data? = nil
fileprivate var _keyIndex: UInt32? = nil
fileprivate var _senderPublicKey: String? = nil
}
init() {}
@ -4083,7 +4094,7 @@ extension SignalServiceProtos_DataMessage.ClosedGroupUpdate.TypeEnum: SwiftProto
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
0: .same(proto: "NEW"),
1: .same(proto: "INFO"),
2: .same(proto: "CHAIN_KEY"),
2: .same(proto: "SENDER_KEY"),
]
}
@ -4092,6 +4103,7 @@ extension SignalServiceProtos_DataMessage.ClosedGroupUpdate.SenderKey: SwiftProt
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "chainKey"),
2: .same(proto: "keyIndex"),
3: .same(proto: "senderPublicKey"),
]
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
@ -4099,6 +4111,7 @@ extension SignalServiceProtos_DataMessage.ClosedGroupUpdate.SenderKey: SwiftProt
switch fieldNumber {
case 1: try decoder.decodeSingularBytesField(value: &self._chainKey)
case 2: try decoder.decodeSingularUInt32Field(value: &self._keyIndex)
case 3: try decoder.decodeSingularStringField(value: &self._senderPublicKey)
default: break
}
}
@ -4111,12 +4124,16 @@ extension SignalServiceProtos_DataMessage.ClosedGroupUpdate.SenderKey: SwiftProt
if let v = self._keyIndex {
try visitor.visitSingularUInt32Field(value: v, fieldNumber: 2)
}
if let v = self._senderPublicKey {
try visitor.visitSingularStringField(value: v, fieldNumber: 3)
}
try unknownFields.traverse(visitor: &visitor)
}
static func ==(lhs: SignalServiceProtos_DataMessage.ClosedGroupUpdate.SenderKey, rhs: SignalServiceProtos_DataMessage.ClosedGroupUpdate.SenderKey) -> Bool {
if lhs._chainKey != rhs._chainKey {return false}
if lhs._keyIndex != rhs._keyIndex {return false}
if lhs._senderPublicKey != rhs._senderPublicKey {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}