Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions Riot/Modules/Rendezvous/RendezvousService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import Foundation

struct QRLoginCode: Codable {
let rendezvous: RendezvousDetails
let flow: String?
let intent: String
}

Expand All @@ -42,7 +43,8 @@ struct QRLoginRendezvousPayload: Codable {

var intent: Intent?
var outcome: Outcome?

var reason: FailureReason?

// swiftformat:disable:next redundantBackticks
var protocols: [`Protocol`]?

Expand All @@ -64,6 +66,7 @@ struct QRLoginRendezvousPayload: Codable {
case type
case intent
case outcome
case reason
case homeserver
case user
case protocols
Expand All @@ -77,16 +80,28 @@ 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 {
case loginStart = "login.start"
case loginReciprocate = "login.reciprocate"
}

/**
This is only used in MSC306 v1 and will be removed
*/
enum Outcome: String, Codable {
case success
case declined
Expand All @@ -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"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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))
Expand All @@ -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")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -57,6 +60,7 @@ class MockQRLoginService: QRLoginServiceProtocol {
uri: "https://matrix.org"),
key: "some.public.key")
return QRLoginCode(rendezvous: details,
flow: mockFlow,
intent: "login.start")
}

Expand Down