diff --git a/SignalServiceKit/src/Loki/API/LokiAPITarget.swift b/SignalServiceKit/src/Loki/API/LokiAPITarget.swift index f97e8b684..6801e4e38 100644 --- a/SignalServiceKit/src/Loki/API/LokiAPITarget.swift +++ b/SignalServiceKit/src/Loki/API/LokiAPITarget.swift @@ -29,7 +29,18 @@ internal final class LokiAPITarget : NSObject, NSCoding { coder.encode(address, forKey: "address") coder.encode(port, forKey: "port") } + + // MARK: Equality + override internal func isEqual(_ other: Any?) -> Bool { + guard let other = other as? LokiAPITarget else { return false } + return address == other.address && port == other.port + } + + // MARK: Hashing + override internal var hash: Int { // Override NSObject.hash and not Hashable.hashValue or Hashable.hash(into:) + return address.hashValue ^ port.hashValue + } // MARK: Description - override var description: String { return "\(address):\(port)" } + override internal var description: String { return "\(address):\(port)" } } diff --git a/SignalServiceKit/src/Loki/API/Multi Device/LokiDeviceLink.swift b/SignalServiceKit/src/Loki/API/Multi Device/LokiDeviceLink.swift index 034036f7b..c30af47a7 100644 --- a/SignalServiceKit/src/Loki/API/Multi Device/LokiDeviceLink.swift +++ b/SignalServiceKit/src/Loki/API/Multi Device/LokiDeviceLink.swift @@ -1,12 +1,14 @@ -public struct LokiDeviceLink : Hashable { +@objc(LKDeviceLink) +public final class LokiDeviceLink : NSObject, NSCoding { public let master: Device public let slave: Device public var isAuthorized: Bool { return master.signature != nil } // MARK: Types - public struct Device : Hashable { + @objc(LKDevice) + public final class Device : NSObject, NSCoding { public let hexEncodedPublicKey: String public let signature: Data? @@ -15,10 +17,28 @@ public struct LokiDeviceLink : Hashable { self.signature = signature } - public func hash(into hasher: inout Hasher) { - hexEncodedPublicKey.hash(into: &hasher) - signature?.hash(into: &hasher) + public init?(coder: NSCoder) { + hexEncodedPublicKey = coder.decodeObject(forKey: "hexEncodedPublicKey") as! String + signature = coder.decodeObject(forKey: "signature") as! Data? } + + public func encode(with coder: NSCoder) { + coder.encode(hexEncodedPublicKey, forKey: "hexEncodedPublicKey") + if let signature = signature { coder.encode(signature, forKey: "signature") } + } + + public override func isEqual(_ other: Any?) -> Bool { + guard let other = other as? Device else { return false } + return hexEncodedPublicKey == other.hexEncodedPublicKey && signature == other.signature + } + + override public var hash: Int { // Override NSObject.hash and not Hashable.hashValue or Hashable.hash(into:) + var result = hexEncodedPublicKey.hashValue + if let signature = signature { result = result ^ signature.hashValue } + return result + } + + override public var description: String { return hexEncodedPublicKey } } // MARK: Lifecycle @@ -27,9 +47,29 @@ public struct LokiDeviceLink : Hashable { self.slave = slave } + // MARK: Coding + public init?(coder: NSCoder) { + master = coder.decodeObject(forKey: "master") as! Device + slave = coder.decodeObject(forKey: "slave") as! Device + super.init() + } + + public func encode(with coder: NSCoder) { + coder.encode(master, forKey: "master") + coder.encode(slave, forKey: "slave") + } + + // MARK: Equality + override public func isEqual(_ other: Any?) -> Bool { + guard let other = other as? LokiDeviceLink else { return false } + return master == other.master && slave == other.slave + } + // MARK: Hashing - public func hash(into hasher: inout Hasher) { - master.hash(into: &hasher) - slave.hash(into: &hasher) + override public var hash: Int { // Override NSObject.hash and not Hashable.hashValue or Hashable.hash(into:) + return master.hash ^ slave.hash } + + // MARK: Description + override public var description: String { return "\(master) - \(slave)" } } diff --git a/SignalServiceKit/src/Loki/API/Multi Device/LokiDeviceLinkingSession.swift b/SignalServiceKit/src/Loki/API/Multi Device/LokiDeviceLinkingSession.swift index d0dd9fa8d..73f640d21 100644 --- a/SignalServiceKit/src/Loki/API/Multi Device/LokiDeviceLinkingSession.swift +++ b/SignalServiceKit/src/Loki/API/Multi Device/LokiDeviceLinkingSession.swift @@ -29,7 +29,12 @@ final class LokiDeviceLinkingSession : NSObject { let master = LokiDeviceLink.Device(hexEncodedPublicKey: OWSIdentityManager.shared().identityKeyPair()!.hexEncodedPublicKey) let slave = LokiDeviceLink.Device(hexEncodedPublicKey: slaveHexEncodedPublicKey, signature: slaveSignature) let deviceLink = LokiDeviceLink(between: master, and: slave) - delegate.authorizeDeviceLinkIfValid(deviceLink) + guard isValid(deviceLink) else { return } + delegate.requestUserAuthorization(for: deviceLink) + } + + public func authorizeDeviceLink(_ deviceLink: LokiDeviceLink) { + // TODO: Send a device link authorized message } public func stopListeningForLinkingRequests() { @@ -37,4 +42,9 @@ final class LokiDeviceLinkingSession : NSObject { timer = nil isListeningForLinkingRequests = false } + + // MARK: Private API + private func isValid(_ deviceLink: LokiDeviceLink) -> Bool { + return true // TODO: Implement + } } diff --git a/SignalServiceKit/src/Loki/API/Multi Device/LokiDeviceLinkingSessionDelegate.swift b/SignalServiceKit/src/Loki/API/Multi Device/LokiDeviceLinkingSessionDelegate.swift index 20f387b3a..23505a973 100644 --- a/SignalServiceKit/src/Loki/API/Multi Device/LokiDeviceLinkingSessionDelegate.swift +++ b/SignalServiceKit/src/Loki/API/Multi Device/LokiDeviceLinkingSessionDelegate.swift @@ -1,6 +1,6 @@ public protocol LokiDeviceLinkingSessionDelegate { - func authorizeDeviceLinkIfValid(_ deviceLink: LokiDeviceLink) + func requestUserAuthorization(for deviceLink: LokiDeviceLink) func handleDeviceLinkingSessionTimeout() } diff --git a/SignalServiceKit/src/Loki/Database/OWSPrimaryStorage+Loki.swift b/SignalServiceKit/src/Loki/Database/OWSPrimaryStorage+Loki.swift index 68ac4733e..405eba4ec 100644 --- a/SignalServiceKit/src/Loki/Database/OWSPrimaryStorage+Loki.swift +++ b/SignalServiceKit/src/Loki/Database/OWSPrimaryStorage+Loki.swift @@ -1,33 +1,22 @@ extension OWSPrimaryStorage { - private func getCollection(for primaryDevice: String) -> String { - return "LokiMultiDevice-\(primaryDevice)" - } - - public func getAuthorisation(forSecondaryDevice secondaryDevice: String, with transaction: YapDatabaseReadTransaction) -> LokiPairingAuthorisation? { - let query = YapDatabaseQuery(string: "WHERE \(PairingAuthorisationsIndex.secondaryDevicePubKey) = ?", parameters: [secondaryDevice]) - let authorisations = PairingAuthorisationsIndex.getPairingAuthorisations(with: query, transaction: transaction) - - // This should never be the case - if (authorisations.count > 1) { owsFailDebug("[Loki][Multidevice] Found multiple authorisations for secondary device: \(secondaryDevice)") } - - return authorisations.first + public func storeDeviceLink(_ deviceLink: LokiDeviceLink, in transaction: YapDatabaseReadWriteTransaction) { + let collection = getCollection(for: deviceLink.master.hexEncodedPublicKey) + transaction.setObject(deviceLink, forKey: deviceLink.slave.hexEncodedPublicKey, inCollection: collection) } - public func createOrUpdatePairingAuthorisation(_ authorisation: LokiPairingAuthorisation, with transaction: YapDatabaseReadWriteTransaction) { - // iOS makes this easy, we can group all authorizations into the primary device collection - // Then we associate an authorisation with the secondary device key - transaction.setObject(authorisation, forKey: authorisation.secondaryDevicePubKey, inCollection: getCollection(for: authorisation.primaryDevicePubKey)) + public func getDeviceLinks(for masterHexEncodedPublicKey: String, in transaction: YapDatabaseReadTransaction) -> [LokiDeviceLink] { + let collection = getCollection(for: masterHexEncodedPublicKey) + var result: [LokiDeviceLink] = [] + transaction.enumerateRows(inCollection: collection) { _, object, _, _ in + guard let deviceLink = object as? LokiDeviceLink else { return } + result.append(deviceLink) + } + return result } - public func getSecondaryDevices(forPrimaryDevice primaryDevice: String, with transaction: YapDatabaseReadTransaction) -> [String] { - // primary device collection should have secondary devices as its keys - return transaction.allKeys(inCollection: getCollection(for: primaryDevice)) - } - - public func getPrimaryDevice(forSecondaryDevice secondaryDevice: String, with transaction: YapDatabaseReadTransaction) -> String? { - let authorisation = getAuthorisation(forSecondaryDevice: secondaryDevice, with: transaction) - return authorisation?.primaryDevicePubKey + private func getCollection(for primaryDevice: String) -> String { + return "LokiDeviceLinkCollection-\(primaryDevice)" } }