mirror of
https://github.com/vector-im/element-ios.git
synced 2024-09-28 23:32:41 +00:00
Support for MSC3906 v2 (#7407)
This commit is contained in:
parent
de5ece66b7
commit
54800f33e2
4 changed files with 52 additions and 9 deletions
|
@ -33,6 +33,13 @@ enum RendezvousChannelAlgorithm: String {
|
||||||
case ECDH_V2 = "org.matrix.msc3903.rendezvous.v2.curve25519-aes-sha256"
|
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
|
/// Allows communication through a secure channel. Based on MSC3886 and MSC3903
|
||||||
@MainActor
|
@MainActor
|
||||||
class RendezvousService {
|
class RendezvousService {
|
||||||
|
|
|
@ -18,6 +18,7 @@ import Foundation
|
||||||
|
|
||||||
struct QRLoginCode: Codable {
|
struct QRLoginCode: Codable {
|
||||||
let rendezvous: RendezvousDetails
|
let rendezvous: RendezvousDetails
|
||||||
|
let flow: String?
|
||||||
let intent: String
|
let intent: String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,6 +43,7 @@ struct QRLoginRendezvousPayload: Codable {
|
||||||
|
|
||||||
var intent: Intent?
|
var intent: Intent?
|
||||||
var outcome: Outcome?
|
var outcome: Outcome?
|
||||||
|
var reason: FailureReason?
|
||||||
|
|
||||||
// swiftformat:disable:next redundantBackticks
|
// swiftformat:disable:next redundantBackticks
|
||||||
var protocols: [`Protocol`]?
|
var protocols: [`Protocol`]?
|
||||||
|
@ -64,6 +66,7 @@ struct QRLoginRendezvousPayload: Codable {
|
||||||
case type
|
case type
|
||||||
case intent
|
case intent
|
||||||
case outcome
|
case outcome
|
||||||
|
case reason
|
||||||
case homeserver
|
case homeserver
|
||||||
case user
|
case user
|
||||||
case protocols
|
case protocols
|
||||||
|
@ -77,9 +80,18 @@ struct QRLoginRendezvousPayload: Codable {
|
||||||
}
|
}
|
||||||
|
|
||||||
enum `Type`: String, Codable {
|
enum `Type`: String, Codable {
|
||||||
case loginStart = "m.login.start"
|
|
||||||
case loginProgress = "m.login.progress"
|
case loginProgress = "m.login.progress"
|
||||||
|
/**
|
||||||
|
This is only used in MSC3906 v1 and will be removed
|
||||||
|
*/
|
||||||
case loginFinish = "m.login.finish"
|
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 {
|
enum Intent: String, Codable {
|
||||||
|
@ -87,6 +99,9 @@ struct QRLoginRendezvousPayload: Codable {
|
||||||
case loginReciprocate = "login.reciprocate"
|
case loginReciprocate = "login.reciprocate"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
This is only used in MSC306 v1 and will be removed
|
||||||
|
*/
|
||||||
enum Outcome: String, Codable {
|
enum Outcome: String, Codable {
|
||||||
case success
|
case success
|
||||||
case declined
|
case declined
|
||||||
|
@ -97,4 +112,11 @@ struct QRLoginRendezvousPayload: Codable {
|
||||||
enum `Protocol`: String, Codable {
|
enum `Protocol`: String, Codable {
|
||||||
case loginToken = "org.matrix.msc3906.login_token"
|
case loginToken = "org.matrix.msc3906.login_token"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum FailureReason: String, Codable {
|
||||||
|
case cancelled
|
||||||
|
case unsupported
|
||||||
|
case e2eeSecurityError = "e2ee_security_error"
|
||||||
|
case incompatibleIntent = "incompatible_intent"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -180,6 +180,12 @@ class QRLoginService: NSObject, QRLoginServiceProtocol {
|
||||||
return
|
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
|
// 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,
|
guard code.intent == QRLoginRendezvousPayload.Intent.loginReciprocate.rawValue,
|
||||||
let uri = code.rendezvous.transport?.uri,
|
let uri = code.rendezvous.transport?.uri,
|
||||||
|
@ -223,7 +229,10 @@ class QRLoginService: NSObject, QRLoginServiceProtocol {
|
||||||
}
|
}
|
||||||
|
|
||||||
MXLog.debug("[QRLoginService] Request login with `login_token`")
|
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 {
|
case .success = await rendezvousService.send(data: requestData) else {
|
||||||
MXLog.error("[QRLoginService] Failed sending continue with `login_token` request")
|
MXLog.error("[QRLoginService] Failed sending continue with `login_token` request")
|
||||||
await teardownRendezvous(state: .failed(error: .rendezvousFailed))
|
await teardownRendezvous(state: .failed(error: .rendezvousFailed))
|
||||||
|
@ -282,10 +291,11 @@ class QRLoginService: NSObject, QRLoginServiceProtocol {
|
||||||
}
|
}
|
||||||
|
|
||||||
MXLog.debug("[QRLoginService] Session created, sending device details")
|
MXLog.debug("[QRLoginService] Session created, sending device details")
|
||||||
guard let requestData = try? JSONEncoder().encode(QRLoginRendezvousPayload(type: .loginProgress,
|
let successPayload = flow == .SETUP_ADDITIONAL_DEVICE_V1
|
||||||
outcome: .success,
|
? QRLoginRendezvousPayload(type: .loginProgress, outcome: .success, deviceId: session.myDeviceId, deviceKey: session.crypto.deviceEd25519Key)
|
||||||
deviceId: session.myDeviceId,
|
: QRLoginRendezvousPayload(type: .loginSuccess, deviceId: session.myDeviceId, deviceKey: session.crypto.deviceEd25519Key)
|
||||||
deviceKey: session.crypto.deviceEd25519Key)),
|
|
||||||
|
guard let requestData = try? JSONEncoder().encode(successPayload),
|
||||||
case .success = await rendezvousService.send(data: requestData) else {
|
case .success = await rendezvousService.send(data: requestData) else {
|
||||||
MXLog.error("[QRLoginService] Failed sending session details")
|
MXLog.error("[QRLoginService] Failed sending session details")
|
||||||
await teardownRendezvous(state: .failed(error: .rendezvousFailed))
|
await teardownRendezvous(state: .failed(error: .rendezvousFailed))
|
||||||
|
@ -307,7 +317,7 @@ class QRLoginService: NSObject, QRLoginServiceProtocol {
|
||||||
MXLog.debug("[QRLoginService] Wait for cross-signing details")
|
MXLog.debug("[QRLoginService] Wait for cross-signing details")
|
||||||
guard case let .success(data) = await rendezvousService.receive(),
|
guard case let .success(data) = await rendezvousService.receive(),
|
||||||
let responsePayload = try? JSONDecoder().decode(QRLoginRendezvousPayload.self, from: data),
|
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 verifiyingDeviceId = responsePayload.verifyingDeviceId,
|
||||||
let verifyingDeviceKey = responsePayload.verifyingDeviceKey else {
|
let verifyingDeviceKey = responsePayload.verifyingDeviceKey else {
|
||||||
MXLog.error("[QRLoginService] Received invalid cross-signing details")
|
MXLog.error("[QRLoginService] Received invalid cross-signing details")
|
||||||
|
|
|
@ -20,13 +20,16 @@ import SwiftUI
|
||||||
|
|
||||||
class MockQRLoginService: QRLoginServiceProtocol {
|
class MockQRLoginService: QRLoginServiceProtocol {
|
||||||
private let mockCanDisplayQR: Bool
|
private let mockCanDisplayQR: Bool
|
||||||
|
private let mockFlow: String?
|
||||||
|
|
||||||
init(withState state: QRLoginServiceState = .initial,
|
init(withState state: QRLoginServiceState = .initial,
|
||||||
mode: QRLoginServiceMode = .notAuthenticated,
|
mode: QRLoginServiceMode = .notAuthenticated,
|
||||||
canDisplayQR: Bool = true) {
|
canDisplayQR: Bool = true,
|
||||||
|
flow: String? = nil) {
|
||||||
self.state = state
|
self.state = state
|
||||||
self.mode = mode
|
self.mode = mode
|
||||||
mockCanDisplayQR = canDisplayQR
|
mockCanDisplayQR = canDisplayQR
|
||||||
|
mockFlow = flow
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - QRLoginServiceProtocol
|
// MARK: - QRLoginServiceProtocol
|
||||||
|
@ -57,6 +60,7 @@ class MockQRLoginService: QRLoginServiceProtocol {
|
||||||
uri: "https://matrix.org"),
|
uri: "https://matrix.org"),
|
||||||
key: "some.public.key")
|
key: "some.public.key")
|
||||||
return QRLoginCode(rendezvous: details,
|
return QRLoginCode(rendezvous: details,
|
||||||
|
flow: mockFlow,
|
||||||
intent: "login.start")
|
intent: "login.start")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue