From 5b04b5ed3bf01357c1e51e5b390ea57f713da233 Mon Sep 17 00:00:00 2001 From: Niels Andriesse Date: Tue, 24 Sep 2019 15:05:59 +1000 Subject: [PATCH] Further build out device link authorization --- Signal.xcodeproj/project.pbxproj | 8 ++-- .../src/Loki/Group Chat/GroupChatPoller.swift | 34 ++++++------- .../src/Loki/Group Chat/RSSFeedPoller.swift | 26 +++++----- Signal/src/Loki/Onboarding/SeedVC.swift | 4 +- .../Loki/Settings/DeviceLinkingModal.swift | 13 ++++- .../Settings/DeviceLinkingModalDelegate.swift | 11 +++-- .../Multi Device/DeviceLinkingSession.swift | 48 +++++++++++++++---- .../DeviceLinkingSessionDelegate.swift | 4 +- .../Notification+DeviceLinking.swift | 4 ++ .../src/Messages/OWSMessageManager.m | 7 +-- 10 files changed, 104 insertions(+), 55 deletions(-) create mode 100644 SignalServiceKit/src/Loki/API/Multi Device/Notification+DeviceLinking.swift diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index cd0322bd8..5574af731 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -570,12 +570,12 @@ B86BD08123399883000F5AE3 /* QRCodeModal.swift in Sources */ = {isa = PBXBuildFile; fileRef = B86BD08023399883000F5AE3 /* QRCodeModal.swift */; }; B86BD08423399ACF000F5AE3 /* Modal.swift in Sources */ = {isa = PBXBuildFile; fileRef = B86BD08323399ACF000F5AE3 /* Modal.swift */; }; B86BD08623399CEF000F5AE3 /* SeedModal.swift in Sources */ = {isa = PBXBuildFile; fileRef = B86BD08523399CEF000F5AE3 /* SeedModal.swift */; }; - B86BD08C2339AE43000F5AE3 /* DeviceLinkingModalDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B86BD08B2339AE43000F5AE3 /* DeviceLinkingModalDelegate.swift */; }; B885D5F4233491AB00EE0D8E /* DeviceLinkingModal.swift in Sources */ = {isa = PBXBuildFile; fileRef = B885D5F3233491AB00EE0D8E /* DeviceLinkingModal.swift */; }; B885D5F62334A32100EE0D8E /* UIView+Constraint.swift in Sources */ = {isa = PBXBuildFile; fileRef = B885D5F52334A32100EE0D8E /* UIView+Constraint.swift */; }; B891105C2320872800F15FCC /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = B891105B2320872800F15FCC /* GoogleService-Info.plist */; }; B891105E2320872800F15FCC /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = B891105B2320872800F15FCC /* GoogleService-Info.plist */; }; B891105F2320872800F15FCC /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = B891105B2320872800F15FCC /* GoogleService-Info.plist */; }; + B894D0712339D6F300B4D94D /* DeviceLinkingModalDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B894D0702339D6F300B4D94D /* DeviceLinkingModalDelegate.swift */; }; B89841E322B7579F00B1BDC6 /* NewConversationVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B89841E222B7579F00B1BDC6 /* NewConversationVC.swift */; }; B90418E6183E9DD40038554A /* DateUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = B90418E5183E9DD40038554A /* DateUtil.m */; }; B9EB5ABD1884C002007CBB57 /* MessageUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B9EB5ABC1884C002007CBB57 /* MessageUI.framework */; }; @@ -1378,10 +1378,10 @@ B86BD08023399883000F5AE3 /* QRCodeModal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRCodeModal.swift; sourceTree = ""; }; B86BD08323399ACF000F5AE3 /* Modal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Modal.swift; sourceTree = ""; }; B86BD08523399CEF000F5AE3 /* SeedModal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeedModal.swift; sourceTree = ""; }; - B86BD08B2339AE43000F5AE3 /* DeviceLinkingModalDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceLinkingModalDelegate.swift; sourceTree = ""; }; B885D5F3233491AB00EE0D8E /* DeviceLinkingModal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceLinkingModal.swift; sourceTree = ""; }; B885D5F52334A32100EE0D8E /* UIView+Constraint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+Constraint.swift"; sourceTree = ""; }; B891105B2320872800F15FCC /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; + B894D0702339D6F300B4D94D /* DeviceLinkingModalDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceLinkingModalDelegate.swift; sourceTree = ""; }; B89841E222B7579F00B1BDC6 /* NewConversationVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewConversationVC.swift; sourceTree = ""; }; B90418E4183E9DD40038554A /* DateUtil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DateUtil.h; sourceTree = ""; }; B90418E5183E9DD40038554A /* DateUtil.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DateUtil.m; sourceTree = ""; }; @@ -2668,7 +2668,7 @@ isa = PBXGroup; children = ( B885D5F3233491AB00EE0D8E /* DeviceLinkingModal.swift */, - B86BD08B2339AE43000F5AE3 /* DeviceLinkingModalDelegate.swift */, + B894D0702339D6F300B4D94D /* DeviceLinkingModalDelegate.swift */, B86BD08023399883000F5AE3 /* QRCodeModal.swift */, B86BD08523399CEF000F5AE3 /* SeedModal.swift */, ); @@ -3913,7 +3913,7 @@ 45E5A6991F61E6DE001E4A8A /* MarqueeLabel.swift in Sources */, 34D1F0B01F867BFC0066283D /* OWSSystemMessageCell.m in Sources */, 45A663C51F92EC760027B59E /* GroupTableViewCell.swift in Sources */, - B86BD08C2339AE43000F5AE3 /* DeviceLinkingModalDelegate.swift in Sources */, + B894D0712339D6F300B4D94D /* DeviceLinkingModalDelegate.swift in Sources */, 34CA631B2097806F00E526A0 /* OWSContactShareView.m in Sources */, B86BD08423399ACF000F5AE3 /* Modal.swift in Sources */, 34D1F0861F8678AA0066283D /* ConversationViewController.m in Sources */, diff --git a/Signal/src/Loki/Group Chat/GroupChatPoller.swift b/Signal/src/Loki/Group Chat/GroupChatPoller.swift index d712c317c..fbaa3c136 100644 --- a/Signal/src/Loki/Group Chat/GroupChatPoller.swift +++ b/Signal/src/Loki/Group Chat/GroupChatPoller.swift @@ -51,32 +51,32 @@ public final class GroupChatPoller : NSObject { let cutoffIndex = senderHexEncodedPublicKey.index(endIndex, offsetBy: -8) let senderDisplayName = "\(message.displayName) (...\(senderHexEncodedPublicKey[cutoffIndex.. DeviceLinkingSession { + let session = DeviceLinkingSession(delegate: delegate) + session.isListeningForLinkingAuthorization = true + DeviceLinkingSession.current = session + return session + } + + @objc public func processLinkingRequest(from slaveHexEncodedPublicKey: String, to masterHexEncodedPublicKey: String, with slaveSignature: Data) { + guard isListeningForLinkingRequests, masterHexEncodedPublicKey == OWSIdentityManager.shared().identityKeyPair()!.hexEncodedPublicKey else { return } + let master = DeviceLink.Device(hexEncodedPublicKey: masterHexEncodedPublicKey) let slave = DeviceLink.Device(hexEncodedPublicKey: slaveHexEncodedPublicKey, signature: slaveSignature) let deviceLink = DeviceLink(between: master, and: slave) - guard isValidLinkingRequest(deviceLink) else { return } + guard hasValidSlaveSignature(deviceLink) else { return } delegate.requestUserAuthorization(for: deviceLink) } + @objc public func processLinkingAuthorization(from masterHexEncodedPublicKey: String, for slaveHexEncodedPublicKey: String, masterSignature: Data, slaveSignature: Data) { + guard isListeningForLinkingAuthorization, slaveHexEncodedPublicKey == OWSIdentityManager.shared().identityKeyPair()!.hexEncodedPublicKey else { return } + let master = DeviceLink.Device(hexEncodedPublicKey: masterHexEncodedPublicKey, signature: masterSignature) + let slave = DeviceLink.Device(hexEncodedPublicKey: slaveHexEncodedPublicKey, signature: slaveSignature) + let deviceLink = DeviceLink(between: master, and: slave) + guard hasValidSlaveSignature(deviceLink) && hasValidMasterSignature(deviceLink) else { return } + delegate.handleDeviceLinkAuthorized(deviceLink) + } + public func stopListeningForLinkingRequests() { DeviceLinkingSession.current = nil isListeningForLinkingRequests = false } + public func stopListeningForLinkingAuthorization() { + DeviceLinkingSession.current = nil + isListeningForLinkingAuthorization = false + } + // MARK: Private API - private func isValidLinkingRequest(_ deviceLink: DeviceLink) -> Bool { - // When requesting a device link, the slave device signs the master device's public key. When authorizing - // a device link, the master device signs the slave device's public key. - let slaveSignature = deviceLink.slave.signature! + + // When requesting a device link, the slave device signs the master device's public key. When authorizing + // a device link, the master device signs the slave device's public key. + + private func hasValidSlaveSignature(_ deviceLink: DeviceLink) -> Bool { + guard let slaveSignature = deviceLink.slave.signature else { return false } let slavePublicKey = Data(hex: deviceLink.slave.hexEncodedPublicKey) let masterPublicKey = Data(hex: deviceLink.master.hexEncodedPublicKey) return (try? Ed25519.verifySignature(slaveSignature, publicKey: slavePublicKey, data: masterPublicKey)) ?? false } + + private func hasValidMasterSignature(_ deviceLink: DeviceLink) -> Bool { + guard let masterSignature = deviceLink.master.signature else { return false } + let slavePublicKey = Data(hex: deviceLink.slave.hexEncodedPublicKey) + let masterPublicKey = Data(hex: deviceLink.master.hexEncodedPublicKey) + return (try? Ed25519.verifySignature(masterSignature, publicKey: masterPublicKey, data: slavePublicKey)) ?? false + } } diff --git a/SignalServiceKit/src/Loki/API/Multi Device/DeviceLinkingSessionDelegate.swift b/SignalServiceKit/src/Loki/API/Multi Device/DeviceLinkingSessionDelegate.swift index bca2c4939..ab806251c 100644 --- a/SignalServiceKit/src/Loki/API/Multi Device/DeviceLinkingSessionDelegate.swift +++ b/SignalServiceKit/src/Loki/API/Multi Device/DeviceLinkingSessionDelegate.swift @@ -1,6 +1,6 @@ -@objc(LKDeviceLinkingSessionDelegate) public protocol DeviceLinkingSessionDelegate { - @objc func requestUserAuthorization(for deviceLink: DeviceLink) + func requestUserAuthorization(for deviceLink: DeviceLink) + func handleDeviceLinkAuthorized(_ deviceLink: DeviceLink) } diff --git a/SignalServiceKit/src/Loki/API/Multi Device/Notification+DeviceLinking.swift b/SignalServiceKit/src/Loki/API/Multi Device/Notification+DeviceLinking.swift new file mode 100644 index 000000000..d9a7a4bb3 --- /dev/null +++ b/SignalServiceKit/src/Loki/API/Multi Device/Notification+DeviceLinking.swift @@ -0,0 +1,4 @@ + +public extension Notification.Name { + public static let linkingRequestAuthorized = Notification.Name("linkingRequestAuthorized") +} diff --git a/SignalServiceKit/src/Messages/OWSMessageManager.m b/SignalServiceKit/src/Messages/OWSMessageManager.m index c1f17b9c7..1ec9851cc 100644 --- a/SignalServiceKit/src/Messages/OWSMessageManager.m +++ b/SignalServiceKit/src/Messages/OWSMessageManager.m @@ -431,11 +431,12 @@ NS_ASSUME_NONNULL_BEGIN // Loki: Handle device linking message if (contentProto.lokiDeviceLinkingMessage != nil && contentProto.lokiDeviceLinkingMessage.type == SSKProtoLokiDeviceLinkingMessageTypeRequest) { OWSLogInfo(@"[Loki] Received a device linking request from: %@", envelope.source); - NSData *signature = contentProto.lokiDeviceLinkingMessage.slaveSignature; - if (signature == nil) { + NSData *slaveSignature = contentProto.lokiDeviceLinkingMessage.slaveSignature; + if (slaveSignature == nil) { OWSFailDebug(@"Received a device linking request without an attached slave signature."); } - [LKDeviceLinkingSession.current processLinkingRequestFrom:envelope.source with:signature]; + NSString *masterHexEncodedPublicKey = contentProto.lokiDeviceLinkingMessage.masterHexEncodedPublicKey; + [LKDeviceLinkingSession.current processLinkingRequestFrom:envelope.source to:masterHexEncodedPublicKey with:slaveSignature]; } // Loki: Handle pre key bundle message