From e5fea625a7458bbf121430d9fd1b775c5bff8fda Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith Date: Mon, 6 Mar 2023 11:37:59 +0000 Subject: [PATCH 1/2] Support for v2 of MSC3906 --- .../Rendezvous/RendezvousService.swift | 7 +++++ .../QRLogin/Common/Models/QRLoginCode.swift | 26 +++++++++++++++++-- .../Service/MatrixSDK/QRLoginService.swift | 22 +++++++++++----- .../Service/Mock/MockQRLoginService.swift | 6 ++++- 4 files changed, 52 insertions(+), 9 deletions(-) diff --git a/Riot/Modules/Rendezvous/RendezvousService.swift b/Riot/Modules/Rendezvous/RendezvousService.swift index ac9a890e1a..9a43235e23 100644 --- a/Riot/Modules/Rendezvous/RendezvousService.swift +++ b/Riot/Modules/Rendezvous/RendezvousService.swift @@ -33,6 +33,13 @@ enum RendezvousChannelAlgorithm: String { case ECDH_V2 = "org.matrix.msc3903.rendezvous.v2.curve25519-aes-sha256" } +/// Algorithm name as per MSC3906 +enum RendezvousFlow: String { + /// The v1 value never actually appears in JSON + case SETUP_ADDITIONAL_DEVICE_V1 = "org.matrix.msc3906.v1" + case SETUP_ADDITIONAL_DEVICE_V2 = "org.matrix.msc3906.setup.additional_device.v2" +} + /// Allows communication through a secure channel. Based on MSC3886 and MSC3903 @MainActor class RendezvousService { diff --git a/RiotSwiftUI/Modules/Authentication/QRLogin/Common/Models/QRLoginCode.swift b/RiotSwiftUI/Modules/Authentication/QRLogin/Common/Models/QRLoginCode.swift index b6bae67577..1ec1f02389 100644 --- a/RiotSwiftUI/Modules/Authentication/QRLogin/Common/Models/QRLoginCode.swift +++ b/RiotSwiftUI/Modules/Authentication/QRLogin/Common/Models/QRLoginCode.swift @@ -18,6 +18,7 @@ import Foundation struct QRLoginCode: Codable { let rendezvous: RendezvousDetails + let flow: String? let intent: String } @@ -42,7 +43,8 @@ struct QRLoginRendezvousPayload: Codable { var intent: Intent? var outcome: Outcome? - + var reason: FailureReason? + // swiftformat:disable:next redundantBackticks var protocols: [`Protocol`]? @@ -64,6 +66,7 @@ struct QRLoginRendezvousPayload: Codable { case type case intent case outcome + case reason case homeserver case user case protocols @@ -77,9 +80,18 @@ struct QRLoginRendezvousPayload: Codable { } enum `Type`: String, Codable { - case loginStart = "m.login.start" case loginProgress = "m.login.progress" + /** + This is only used in MSC3906 v1 and will be removed + */ case loginFinish = "m.login.finish" + case loginFailure = "m.login.failure" + case loginProtocol = "m.login.protocol" + case loginProtocols = "m.login.protocols" + case loginApproved = "m.login.approved" + case loginDeclined = "m.login.declined" + case loginSuccess = "m.login.success" + case loginVerified = "m.login.verified" } enum Intent: String, Codable { @@ -87,6 +99,9 @@ struct QRLoginRendezvousPayload: Codable { case loginReciprocate = "login.reciprocate" } + /** + This is only used in MSC306 v1 and will be removed + */ enum Outcome: String, Codable { case success case declined @@ -97,4 +112,11 @@ struct QRLoginRendezvousPayload: Codable { enum `Protocol`: String, Codable { case loginToken = "org.matrix.msc3906.login_token" } + + enum FailureReason: String, Codable { + case cancelled + case unsupported + case e2eeSecurityError = "e2ee_security_error" + case incompatibleIntent = "incompatible_intent" + } } diff --git a/RiotSwiftUI/Modules/Authentication/QRLogin/Common/Service/MatrixSDK/QRLoginService.swift b/RiotSwiftUI/Modules/Authentication/QRLogin/Common/Service/MatrixSDK/QRLoginService.swift index 05506b4bde..9c91000872 100644 --- a/RiotSwiftUI/Modules/Authentication/QRLogin/Common/Service/MatrixSDK/QRLoginService.swift +++ b/RiotSwiftUI/Modules/Authentication/QRLogin/Common/Service/MatrixSDK/QRLoginService.swift @@ -180,6 +180,12 @@ class QRLoginService: NSObject, QRLoginServiceProtocol { return } + guard let flow = code.flow != nil ? RendezvousFlow(rawValue: code.flow!) : .SETUP_ADDITIONAL_DEVICE_V1 else { + MXLog.error("[QRLoginService] Unsupported flow") + state = .failed(error: .deviceNotSupported) + return + } + // so, this is of an expected algorithm so any bad data can be considered an invalid QR code guard code.intent == QRLoginRendezvousPayload.Intent.loginReciprocate.rawValue, let uri = code.rendezvous.transport?.uri, @@ -223,7 +229,10 @@ class QRLoginService: NSObject, QRLoginServiceProtocol { } MXLog.debug("[QRLoginService] Request login with `login_token`") - guard let requestData = try? JSONEncoder().encode(QRLoginRendezvousPayload(type: .loginProgress, protocol: .loginToken)), + let protocolPayload = flow == .SETUP_ADDITIONAL_DEVICE_V1 + ? QRLoginRendezvousPayload(type: .loginProgress, protocol: .loginToken) + : QRLoginRendezvousPayload(type: .loginProtocol, protocol: .loginToken) + guard let requestData = try? JSONEncoder().encode(protocolPayload), case .success = await rendezvousService.send(data: requestData) else { MXLog.error("[QRLoginService] Failed sending continue with `login_token` request") await teardownRendezvous(state: .failed(error: .rendezvousFailed)) @@ -282,10 +291,11 @@ class QRLoginService: NSObject, QRLoginServiceProtocol { } MXLog.debug("[QRLoginService] Session created, sending device details") - guard let requestData = try? JSONEncoder().encode(QRLoginRendezvousPayload(type: .loginProgress, - outcome: .success, - deviceId: session.myDeviceId, - deviceKey: session.crypto.deviceEd25519Key)), + let successPayload = flow == .SETUP_ADDITIONAL_DEVICE_V1 + ? QRLoginRendezvousPayload(type: .loginProgress, outcome: .success, deviceId: session.myDeviceId, deviceKey: session.crypto.deviceEd25519Key) + : QRLoginRendezvousPayload(type: .loginSuccess, deviceId: session.myDeviceId, deviceKey: session.crypto.deviceEd25519Key) + + guard let requestData = try? JSONEncoder().encode(successPayload), case .success = await rendezvousService.send(data: requestData) else { MXLog.error("[QRLoginService] Failed sending session details") await teardownRendezvous(state: .failed(error: .rendezvousFailed)) @@ -307,7 +317,7 @@ class QRLoginService: NSObject, QRLoginServiceProtocol { MXLog.debug("[QRLoginService] Wait for cross-signing details") guard case let .success(data) = await rendezvousService.receive(), let responsePayload = try? JSONDecoder().decode(QRLoginRendezvousPayload.self, from: data), - responsePayload.outcome == .verified, + flow == .SETUP_ADDITIONAL_DEVICE_V1 && responsePayload.outcome == .verified || responsePayload.type == .loginVerified, let verifiyingDeviceId = responsePayload.verifyingDeviceId, let verifyingDeviceKey = responsePayload.verifyingDeviceKey else { MXLog.error("[QRLoginService] Received invalid cross-signing details") diff --git a/RiotSwiftUI/Modules/Authentication/QRLogin/Common/Service/Mock/MockQRLoginService.swift b/RiotSwiftUI/Modules/Authentication/QRLogin/Common/Service/Mock/MockQRLoginService.swift index 3f511214e7..2285c7960a 100644 --- a/RiotSwiftUI/Modules/Authentication/QRLogin/Common/Service/Mock/MockQRLoginService.swift +++ b/RiotSwiftUI/Modules/Authentication/QRLogin/Common/Service/Mock/MockQRLoginService.swift @@ -20,13 +20,16 @@ import SwiftUI class MockQRLoginService: QRLoginServiceProtocol { private let mockCanDisplayQR: Bool + private let mockFlow: String? init(withState state: QRLoginServiceState = .initial, mode: QRLoginServiceMode = .notAuthenticated, - canDisplayQR: Bool = true) { + canDisplayQR: Bool = true, + flow: String? = nil) { self.state = state self.mode = mode mockCanDisplayQR = canDisplayQR + mockFlow = flow } // MARK: - QRLoginServiceProtocol @@ -57,6 +60,7 @@ class MockQRLoginService: QRLoginServiceProtocol { uri: "https://matrix.org"), key: "some.public.key") return QRLoginCode(rendezvous: details, + flow: mockFlow, intent: "login.start") } From 014f896838539624d7f16940ac7987e6b77e4f02 Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith Date: Mon, 6 Mar 2023 19:11:04 +0000 Subject: [PATCH 2/2] Whitespace --- Riot/Modules/Rendezvous/RendezvousService.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Riot/Modules/Rendezvous/RendezvousService.swift b/Riot/Modules/Rendezvous/RendezvousService.swift index 9a43235e23..de8f079799 100644 --- a/Riot/Modules/Rendezvous/RendezvousService.swift +++ b/Riot/Modules/Rendezvous/RendezvousService.swift @@ -35,7 +35,7 @@ enum RendezvousChannelAlgorithm: String { /// Algorithm name as per MSC3906 enum RendezvousFlow: String { - /// The v1 value never actually appears in JSON + /// The v1 value never actually appears in JSON case SETUP_ADDITIONAL_DEVICE_V1 = "org.matrix.msc3906.v1" case SETUP_ADDITIONAL_DEVICE_V2 = "org.matrix.msc3906.setup.additional_device.v2" }