mirror of
https://github.com/vector-im/riotX-android
synced 2025-10-06 00:02:48 +02:00
Compare commits
4 Commits
develop
...
hughns/msc
Author | SHA1 | Date | |
---|---|---|---|
|
fb3b8aabf6 | ||
|
8e416cdaee | ||
|
49f740514a | ||
|
3b98245669 |
1
changelog.d/8212.feature
Normal file
1
changelog.d/8212.feature
Normal file
@@ -0,0 +1 @@
|
||||
Updates to protocol used for Sign in with QR code
|
@@ -25,27 +25,53 @@ import org.junit.Test
|
||||
import org.matrix.android.sdk.InstrumentedTest
|
||||
import org.matrix.android.sdk.api.rendezvous.channels.ECDHRendezvousChannel
|
||||
import org.matrix.android.sdk.api.rendezvous.model.RendezvousError
|
||||
import org.matrix.android.sdk.api.rendezvous.model.RendezvousFlow
|
||||
import org.matrix.android.sdk.common.CommonTestHelper
|
||||
|
||||
class RendezvousTest : InstrumentedTest {
|
||||
|
||||
@Test
|
||||
fun shouldSuccessfullyBuildChannels() = CommonTestHelper.runCryptoTest(context()) { _, _ ->
|
||||
fun shouldSuccessfullyBuildMSC3906V1Channels() = CommonTestHelper.runCryptoTest(context()) { _, _ ->
|
||||
val cases = listOf(
|
||||
// v1:
|
||||
// MSC3903 v1 + MSC3906 v1:
|
||||
"{\"rendezvous\":{\"algorithm\":\"org.matrix.msc3903.rendezvous.v1.curve25519-aes-sha256\"," +
|
||||
"\"key\":\"aeSGwYTV1IUhikUyCapzC6p2xG5NpJ4Lwj2UgUMlcTk\",\"transport\":" +
|
||||
"{\"type\":\"org.matrix.msc3886.http.v1\",\"uri\":\"https://rendezvous.lab.element.dev/bcab62cd-3e34-48b4-bc39-90895da8f6fe\"}}," +
|
||||
"\"intent\":\"login.reciprocate\"}",
|
||||
// v2:
|
||||
// MSC3903 v2 + MSC3906 v1:
|
||||
"{\"rendezvous\":{\"algorithm\":\"org.matrix.msc3903.rendezvous.v2.curve25519-aes-sha256\"," +
|
||||
"\"key\":\"aeSGwYTV1IUhikUyCapzC6p2xG5NpJ4Lwj2UgUMlcTk\",\"transport\":" +
|
||||
"{\"type\":\"org.matrix.msc3886.http.v1\",\"uri\":\"https://rendezvous.lab.element.dev/bcab62cd-3e34-48b4-bc39-90895da8f6fe\"}}," +
|
||||
"\"intent\":\"login.reciprocate\"}",
|
||||
"\"intent\":\"login.reciprocate\"}"
|
||||
)
|
||||
|
||||
cases.forEach { input ->
|
||||
Rendezvous.buildChannelFromCode(input).channel shouldBeInstanceOf ECDHRendezvousChannel::class
|
||||
val rz = Rendezvous.buildChannelFromCode(input)
|
||||
rz.channel shouldBeInstanceOf ECDHRendezvousChannel::class
|
||||
rz.flow shouldBeEqualTo RendezvousFlow.SETUP_ADDITIONAL_DEVICE_V1
|
||||
}
|
||||
}
|
||||
|
||||
fun shouldSuccessfullyBuildMSC3906V2Channels() = CommonTestHelper.runCryptoTest(context()) { _, _ ->
|
||||
val cases = listOf(
|
||||
// MSC3903 v1:
|
||||
"{\"rendezvous\":{\"algorithm\":\"org.matrix.msc3903.rendezvous.v1.curve25519-aes-sha256\"," +
|
||||
"\"key\":\"aeSGwYTV1IUhikUyCapzC6p2xG5NpJ4Lwj2UgUMlcTk\",\"transport\":" +
|
||||
"{\"type\":\"org.matrix.msc3886.http.v1\",\"uri\":\"https://rendezvous.lab.element.dev/bcab62cd-3e34-48b4-bc39-90895da8f6fe\"}}," +
|
||||
"\"flow\":\"org.matrix.msc3906.setup.additional_device.v2\"," +
|
||||
"\"intent\":\"login.reciprocate\"}",
|
||||
// MSC3903 v2:
|
||||
"{\"rendezvous\":{\"algorithm\":\"org.matrix.msc3903.rendezvous.v2.curve25519-aes-sha256\"," +
|
||||
"\"key\":\"aeSGwYTV1IUhikUyCapzC6p2xG5NpJ4Lwj2UgUMlcTk\",\"transport\":" +
|
||||
"{\"type\":\"org.matrix.msc3886.http.v1\",\"uri\":\"https://rendezvous.lab.element.dev/bcab62cd-3e34-48b4-bc39-90895da8f6fe\"}}," +
|
||||
"\"flow\":\"org.matrix.msc3906.setup.additional_device.v2\"," +
|
||||
"\"intent\":\"login.reciprocate\"}"
|
||||
)
|
||||
|
||||
cases.forEach { input ->
|
||||
val rz = Rendezvous.buildChannelFromCode(input)
|
||||
rz.channel shouldBeInstanceOf ECDHRendezvousChannel::class
|
||||
rz.flow shouldBeEqualTo RendezvousFlow.SETUP_ADDITIONAL_DEVICE_V2
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,6 +82,7 @@ class RendezvousTest : InstrumentedTest {
|
||||
"{\"rendezvous\":{\"algorithm\":\"bad algo\"," +
|
||||
"\"key\":\"aeSGwYTV1IUhikUyCapzC6p2xG5NpJ4Lwj2UgUMlcTk\",\"transport\":" +
|
||||
"{\"type\":\"org.matrix.msc3886.http.v1\",\"uri\":\"https://rendezvous.lab.element.dev/bcab62cd-3e34-48b4-bc39-90895da8f6fe\"}}," +
|
||||
"\"flow\":\"org.matrix.msc3906.setup.additional_device.v2\"," +
|
||||
"\"intent\":\"login.reciprocate\"}"
|
||||
)
|
||||
} shouldThrow RendezvousError::class with {
|
||||
@@ -70,6 +97,7 @@ class RendezvousTest : InstrumentedTest {
|
||||
"{\"rendezvous\":{\"algorithm\":\"org.matrix.msc3903.rendezvous.v1.curve25519-aes-sha256\"," +
|
||||
"\"key\":\"aeSGwYTV1IUhikUyCapzC6p2xG5NpJ4Lwj2UgUMlcTk\",\"transport\":" +
|
||||
"{\"type\":\"bad transport\",\"uri\":\"https://rendezvous.lab.element.dev/bcab62cd-3e34-48b4-bc39-90895da8f6fe\"}}," +
|
||||
"\"flow\":\"org.matrix.msc3906.setup.additional_device.v2\"," +
|
||||
"\"intent\":\"login.reciprocate\"}"
|
||||
)
|
||||
} shouldThrow RendezvousError::class with {
|
||||
@@ -84,6 +112,7 @@ class RendezvousTest : InstrumentedTest {
|
||||
"{\"rendezvous\":{\"algorithm\":\"org.matrix.msc3903.rendezvous.v1.curve25519-aes-sha256\"," +
|
||||
"\"key\":\"aeSGwYTV1IUhikUyCapzC6p2xG5NpJ4Lwj2UgUMlcTk\",\"transport\":" +
|
||||
"{\"type\":\"org.matrix.msc3886.http.v1\",\"uri\":\"https://rendezvous.lab.element.dev/bcab62cd-3e34-48b4-bc39-90895da8f6fe\"}}," +
|
||||
"\"flow\":\"org.matrix.msc3906.setup.additional_device.v2\"," +
|
||||
"\"intent\":\"foo\"}"
|
||||
)
|
||||
} shouldThrow RendezvousError::class with {
|
||||
|
@@ -22,12 +22,14 @@ import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig
|
||||
import org.matrix.android.sdk.api.logger.LoggerTag
|
||||
import org.matrix.android.sdk.api.rendezvous.channels.ECDHRendezvousChannel
|
||||
import org.matrix.android.sdk.api.rendezvous.model.ECDHRendezvousCode
|
||||
import org.matrix.android.sdk.api.rendezvous.model.FailureReason
|
||||
import org.matrix.android.sdk.api.rendezvous.model.Outcome
|
||||
import org.matrix.android.sdk.api.rendezvous.model.Payload
|
||||
import org.matrix.android.sdk.api.rendezvous.model.PayloadType
|
||||
import org.matrix.android.sdk.api.rendezvous.model.Protocol
|
||||
import org.matrix.android.sdk.api.rendezvous.model.RendezvousCode
|
||||
import org.matrix.android.sdk.api.rendezvous.model.RendezvousError
|
||||
import org.matrix.android.sdk.api.rendezvous.model.RendezvousFlow
|
||||
import org.matrix.android.sdk.api.rendezvous.model.RendezvousIntent
|
||||
import org.matrix.android.sdk.api.rendezvous.model.RendezvousTransportType
|
||||
import org.matrix.android.sdk.api.rendezvous.model.SecureRendezvousChannelAlgorithm
|
||||
@@ -42,11 +44,14 @@ import org.matrix.android.sdk.api.util.MatrixJsonParser
|
||||
import timber.log.Timber
|
||||
|
||||
/**
|
||||
* Implementation of MSC3906 to sign in + E2EE set up using a QR code.
|
||||
* Implementation of MSC3906 to sign in + E2EE set up using a QR code: https://github.com/matrix-org/matrix-spec-proposals/pull/3906
|
||||
*
|
||||
* @alpha This is an experimental API, and is subject to change until MSC3906 is stabilised and accepted.
|
||||
*/
|
||||
class Rendezvous(
|
||||
val channel: RendezvousChannel,
|
||||
val theirIntent: RendezvousIntent,
|
||||
val flow: RendezvousFlow,
|
||||
) {
|
||||
companion object {
|
||||
private val TAG = LoggerTag(Rendezvous::class.java.simpleName, LoggerTag.RENDEZVOUS).value
|
||||
@@ -61,6 +66,11 @@ class Rendezvous(
|
||||
throw RendezvousError("Malformed code", RendezvousFailureReason.InvalidCode)
|
||||
} ?: throw RendezvousError("Code is null", RendezvousFailureReason.InvalidCode)
|
||||
|
||||
// then we check that flow is supported
|
||||
if (genericParsed.flow != null && !RendezvousFlow.values().map { it.value }.contains(genericParsed.flow)) {
|
||||
throw RendezvousError("Unsupported flow", RendezvousFailureReason.UnsupportedAlgorithm)
|
||||
}
|
||||
|
||||
// then we check that algorithm is supported
|
||||
if (!SecureRendezvousChannelAlgorithm.values().map { it.value }.contains(genericParsed.rendezvous.algorithm)) {
|
||||
throw RendezvousError("Unsupported algorithm", RendezvousFailureReason.UnsupportedAlgorithm)
|
||||
@@ -83,25 +93,33 @@ class Rendezvous(
|
||||
|
||||
return Rendezvous(
|
||||
ECDHRendezvousChannel(transport, supportedParsed.rendezvous.algorithm, supportedParsed.rendezvous.key),
|
||||
supportedParsed.intent
|
||||
supportedParsed.intent,
|
||||
// default to v1 if not specified:
|
||||
supportedParsed.flow ?: RendezvousFlow.SETUP_ADDITIONAL_DEVICE_V1
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private val adapter = MatrixJsonParser.getMoshi().adapter(Payload::class.java)
|
||||
|
||||
// not yet implemented: RendezvousIntent.RECIPROCATE_LOGIN_ON_EXISTING_DEVICE
|
||||
val ourIntent: RendezvousIntent = RendezvousIntent.LOGIN_ON_NEW_DEVICE
|
||||
fun isUsingV1(): Boolean = flow == RendezvousFlow.SETUP_ADDITIONAL_DEVICE_V1
|
||||
|
||||
@Throws(RendezvousError::class)
|
||||
private suspend fun checkCompatibility() {
|
||||
// not yet implemented: RendezvousIntent.RECIPROCATE_LOGIN_ON_EXISTING_DEVICE
|
||||
val ourIntent: RendezvousIntent = RendezvousIntent.LOGIN_ON_NEW_DEVICE
|
||||
|
||||
val incompatible = theirIntent == ourIntent
|
||||
|
||||
Timber.tag(TAG).d("ourIntent: $ourIntent, theirIntent: $theirIntent, incompatible: $incompatible")
|
||||
Timber.tag(TAG).d("ourIntent: $ourIntent, theirIntent: $theirIntent, incompatible: $incompatible, flow: $flow")
|
||||
|
||||
if (incompatible) {
|
||||
// inform the other side
|
||||
send(Payload(PayloadType.FINISH, intent = ourIntent))
|
||||
if (isUsingV1()) {
|
||||
send(Payload(PayloadType.FINISH, intent = ourIntent))
|
||||
} else {
|
||||
send(Payload(PayloadType.FAILURE, intent = ourIntent))
|
||||
}
|
||||
if (ourIntent == RendezvousIntent.LOGIN_ON_NEW_DEVICE) {
|
||||
throw RendezvousError("The other device isn't signed in", RendezvousFailureReason.OtherDeviceNotSignedIn)
|
||||
} else {
|
||||
@@ -123,11 +141,19 @@ class Rendezvous(
|
||||
val protocolsResponse = receive()
|
||||
|
||||
if (protocolsResponse?.protocols == null || !protocolsResponse.protocols.contains(Protocol.LOGIN_TOKEN)) {
|
||||
send(Payload(PayloadType.FINISH, outcome = Outcome.UNSUPPORTED))
|
||||
if (isUsingV1()) {
|
||||
send(Payload(PayloadType.FINISH, outcome = Outcome.UNSUPPORTED))
|
||||
} else {
|
||||
send(Payload(PayloadType.FAILURE, reason = FailureReason.UNSUPPORTED))
|
||||
}
|
||||
throw RendezvousError("Unsupported protocols", RendezvousFailureReason.UnsupportedHomeserver)
|
||||
}
|
||||
|
||||
send(Payload(PayloadType.PROGRESS, protocol = Protocol.LOGIN_TOKEN))
|
||||
if (isUsingV1()) {
|
||||
send(Payload(PayloadType.PROGRESS, protocol = Protocol.LOGIN_TOKEN))
|
||||
} else {
|
||||
send(Payload(PayloadType.PROTOCOL, protocol = Protocol.LOGIN_TOKEN))
|
||||
}
|
||||
|
||||
return checksum
|
||||
}
|
||||
@@ -138,6 +164,7 @@ class Rendezvous(
|
||||
|
||||
val loginToken = receive()
|
||||
|
||||
// v1:
|
||||
if (loginToken?.type == PayloadType.FINISH) {
|
||||
when (loginToken.outcome) {
|
||||
Outcome.DECLINED -> {
|
||||
@@ -152,6 +179,28 @@ class Rendezvous(
|
||||
}
|
||||
}
|
||||
|
||||
// v2:
|
||||
if (loginToken?.type == PayloadType.DECLINED) {
|
||||
throw RendezvousError("Login declined by other device", RendezvousFailureReason.UserDeclined)
|
||||
}
|
||||
if (loginToken?.type == PayloadType.FAILURE) {
|
||||
when (loginToken.reason) {
|
||||
FailureReason.UNSUPPORTED -> {
|
||||
throw RendezvousError("Homeserver lacks support", RendezvousFailureReason.UnsupportedHomeserver)
|
||||
}
|
||||
FailureReason.CANCELLED -> {
|
||||
throw RendezvousError("Login cancelled by other device", RendezvousFailureReason.UserDeclined)
|
||||
}
|
||||
FailureReason.E2EE_SECURITY_ERROR -> {
|
||||
throw RendezvousError("E2EE security error", RendezvousFailureReason.E2EESecurityIssue)
|
||||
}
|
||||
// incompatible intent shouldn't be received at this stage
|
||||
else -> {
|
||||
throw RendezvousError("Unknown error", RendezvousFailureReason.Unknown)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val homeserver = loginToken?.homeserver ?: throw RendezvousError("No homeserver returned", RendezvousFailureReason.ProtocolError)
|
||||
val token = loginToken.loginToken ?: throw RendezvousError("No login token returned", RendezvousFailureReason.ProtocolError)
|
||||
|
||||
@@ -167,8 +216,11 @@ class Rendezvous(
|
||||
val crypto = session.cryptoService()
|
||||
val deviceId = crypto.getMyCryptoDevice().deviceId
|
||||
val deviceKey = crypto.getMyCryptoDevice().fingerprint()
|
||||
send(Payload(PayloadType.PROGRESS, outcome = Outcome.SUCCESS, deviceId = deviceId, deviceKey = deviceKey))
|
||||
|
||||
if (isUsingV1()) {
|
||||
send(Payload(PayloadType.PROGRESS, outcome = Outcome.SUCCESS, deviceId = deviceId, deviceKey = deviceKey))
|
||||
} else {
|
||||
send(Payload(PayloadType.SUCCESS, deviceId = deviceId, deviceKey = deviceKey))
|
||||
}
|
||||
try {
|
||||
// explicitly download keys for ourself rather than racing with initial sync which might not complete in time
|
||||
crypto.downloadKeysIfNeeded(listOf(userId), false)
|
||||
@@ -179,63 +231,78 @@ class Rendezvous(
|
||||
|
||||
// await confirmation of verification
|
||||
val verificationResponse = receive()
|
||||
if (verificationResponse?.outcome == Outcome.VERIFIED) {
|
||||
if (verificationResponse?.outcome == Outcome.VERIFIED || verificationResponse?.type == PayloadType.VERIFIED) {
|
||||
val verifyingDeviceId = verificationResponse.verifyingDeviceId
|
||||
?: throw RendezvousError("No verifying device id returned", RendezvousFailureReason.ProtocolError)
|
||||
val verifyingDeviceFromServer = crypto.getCryptoDeviceInfo(userId, verifyingDeviceId)
|
||||
if (verifyingDeviceFromServer?.fingerprint() != verificationResponse.verifyingDeviceKey) {
|
||||
Timber.tag(TAG).w(
|
||||
"Verifying device $verifyingDeviceId key doesn't match: ${
|
||||
verifyingDeviceFromServer?.fingerprint()
|
||||
} vs ${verificationResponse.verifyingDeviceKey})"
|
||||
)
|
||||
// inform the other side
|
||||
send(Payload(PayloadType.FINISH, outcome = Outcome.E2EE_SECURITY_ERROR))
|
||||
throw RendezvousError("Key from verifying device doesn't match", RendezvousFailureReason.E2EESecurityIssue)
|
||||
}
|
||||
|
||||
verificationResponse.masterKey?.let { masterKeyFromVerifyingDevice ->
|
||||
// verifying device provided us with a master key, so use it to check integrity
|
||||
|
||||
// see what the homeserver told us
|
||||
val localMasterKey = crypto.crossSigningService().getMyCrossSigningKeys()?.masterKey()
|
||||
|
||||
// n.b. if no local master key this is a problem, as well as it not matching
|
||||
if (localMasterKey?.unpaddedBase64PublicKey != masterKeyFromVerifyingDevice) {
|
||||
Timber.tag(TAG).w("Master key from verifying device doesn't match: $masterKeyFromVerifyingDevice vs $localMasterKey")
|
||||
// inform the other side
|
||||
send(Payload(PayloadType.FINISH, outcome = Outcome.E2EE_SECURITY_ERROR))
|
||||
throw RendezvousError("Master key from verifying device doesn't match", RendezvousFailureReason.E2EESecurityIssue)
|
||||
}
|
||||
|
||||
// set other device as verified
|
||||
Timber.tag(TAG).i("Setting device $verifyingDeviceId as verified")
|
||||
crypto.setDeviceVerification(DeviceTrustLevel(locallyVerified = true, crossSigningVerified = false), userId, verifyingDeviceId)
|
||||
|
||||
Timber.tag(TAG).i("Setting master key as trusted")
|
||||
crypto.crossSigningService().markMyMasterKeyAsTrusted()
|
||||
} ?: run {
|
||||
// set other device as verified anyway
|
||||
Timber.tag(TAG).i("Setting device $verifyingDeviceId as verified")
|
||||
crypto.setDeviceVerification(DeviceTrustLevel(locallyVerified = true, crossSigningVerified = false), userId, verifyingDeviceId)
|
||||
|
||||
Timber.tag(TAG).i("No master key given by verifying device")
|
||||
}
|
||||
|
||||
// request secrets from the verifying device
|
||||
Timber.tag(TAG).i("Requesting secrets from $verifyingDeviceId")
|
||||
|
||||
session.sharedSecretStorageService().let {
|
||||
it.requestSecret(MASTER_KEY_SSSS_NAME, verifyingDeviceId)
|
||||
it.requestSecret(SELF_SIGNING_KEY_SSSS_NAME, verifyingDeviceId)
|
||||
it.requestSecret(USER_SIGNING_KEY_SSSS_NAME, verifyingDeviceId)
|
||||
it.requestSecret(KEYBACKUP_SECRET_SSSS_NAME, verifyingDeviceId)
|
||||
}
|
||||
handleVerification(session, verifyingDeviceId, verificationResponse.verifyingDeviceKey, verificationResponse.masterKey)
|
||||
} else {
|
||||
Timber.tag(TAG).i("Not doing verification")
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(RendezvousError::class)
|
||||
private suspend fun handleVerification(session: Session, verifyingDeviceId: String, verifyingDeviceKey: String?, masterKey: String?) {
|
||||
var crypto = session.cryptoService()
|
||||
var userId = session.myUserId
|
||||
val verifyingDeviceFromServer = crypto.getCryptoDeviceInfo(userId, verifyingDeviceId)
|
||||
if (verifyingDeviceFromServer?.fingerprint() != verifyingDeviceKey) {
|
||||
Timber.tag(TAG).w(
|
||||
"Verifying device $verifyingDeviceId key doesn't match: ${
|
||||
verifyingDeviceFromServer?.fingerprint()
|
||||
} vs $verifyingDeviceKey)"
|
||||
)
|
||||
// inform the other side
|
||||
if (isUsingV1()) {
|
||||
send(Payload(PayloadType.FINISH, outcome = Outcome.E2EE_SECURITY_ERROR))
|
||||
} else {
|
||||
send(Payload(PayloadType.FAILURE, reason = FailureReason.E2EE_SECURITY_ERROR))
|
||||
}
|
||||
throw RendezvousError("Key from verifying device doesn't match", RendezvousFailureReason.E2EESecurityIssue)
|
||||
}
|
||||
|
||||
masterKey?.let { masterKeyFromVerifyingDevice ->
|
||||
// verifying device provided us with a master key, so use it to check integrity
|
||||
|
||||
// see what the homeserver told us
|
||||
val localMasterKey = crypto.crossSigningService().getMyCrossSigningKeys()?.masterKey()
|
||||
|
||||
// n.b. if no local master key this is a problem, as well as it not matching
|
||||
if (localMasterKey?.unpaddedBase64PublicKey != masterKeyFromVerifyingDevice) {
|
||||
Timber.tag(TAG).w("Master key from verifying device doesn't match: $masterKeyFromVerifyingDevice vs $localMasterKey")
|
||||
// inform the other side
|
||||
if (isUsingV1()) {
|
||||
send(Payload(PayloadType.FINISH, outcome = Outcome.E2EE_SECURITY_ERROR))
|
||||
} else {
|
||||
send(Payload(PayloadType.FAILURE, reason = FailureReason.E2EE_SECURITY_ERROR))
|
||||
}
|
||||
throw RendezvousError("Master key from verifying device doesn't match", RendezvousFailureReason.E2EESecurityIssue)
|
||||
}
|
||||
|
||||
// set other device as verified
|
||||
Timber.tag(TAG).i("Setting device $verifyingDeviceId as verified")
|
||||
crypto.setDeviceVerification(DeviceTrustLevel(locallyVerified = true, crossSigningVerified = false), userId, verifyingDeviceId)
|
||||
|
||||
Timber.tag(TAG).i("Setting master key as trusted")
|
||||
crypto.crossSigningService().markMyMasterKeyAsTrusted()
|
||||
} ?: run {
|
||||
// set other device as verified anyway
|
||||
Timber.tag(TAG).i("Setting device $verifyingDeviceId as verified")
|
||||
crypto.setDeviceVerification(DeviceTrustLevel(locallyVerified = true, crossSigningVerified = false), userId, verifyingDeviceId)
|
||||
|
||||
Timber.tag(TAG).i("No master key given by verifying device")
|
||||
}
|
||||
|
||||
// request secrets from the verifying device
|
||||
Timber.tag(TAG).i("Requesting secrets from $verifyingDeviceId")
|
||||
|
||||
session.sharedSecretStorageService().let {
|
||||
it.requestSecret(MASTER_KEY_SSSS_NAME, verifyingDeviceId)
|
||||
it.requestSecret(SELF_SIGNING_KEY_SSSS_NAME, verifyingDeviceId)
|
||||
it.requestSecret(USER_SIGNING_KEY_SSSS_NAME, verifyingDeviceId)
|
||||
it.requestSecret(KEYBACKUP_SECRET_SSSS_NAME, verifyingDeviceId)
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(RendezvousError::class)
|
||||
private suspend fun receive(): Payload? {
|
||||
val data = channel.receive() ?: return null
|
||||
|
@@ -40,6 +40,8 @@ import javax.crypto.spec.SecretKeySpec
|
||||
/**
|
||||
* Implements X25519 ECDH key agreement and AES-256-GCM encryption channel as per MSC3903:
|
||||
* https://github.com/matrix-org/matrix-spec-proposals/pull/3903
|
||||
*
|
||||
* @alpha This is an experimental API, and is subject to change until MSC3903 is stabilised and accepted.
|
||||
*/
|
||||
class ECDHRendezvousChannel(
|
||||
override var transport: RendezvousTransport,
|
||||
|
@@ -21,5 +21,6 @@ import com.squareup.moshi.JsonClass
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class ECDHRendezvousCode(
|
||||
val intent: RendezvousIntent,
|
||||
val flow: RendezvousFlow?,
|
||||
val rendezvous: ECDHRendezvous
|
||||
)
|
||||
|
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.matrix.android.sdk.api.rendezvous.model
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
@JsonClass(generateAdapter = false)
|
||||
enum class FailureReason(val value: String) {
|
||||
@Json(name = "cancelled")
|
||||
CANCELLED("cancelled"),
|
||||
|
||||
@Json(name = "unsupported")
|
||||
UNSUPPORTED("unsupported"),
|
||||
|
||||
@Json(name = "e2ee_security_error")
|
||||
E2EE_SECURITY_ERROR("e2ee_security_error"),
|
||||
|
||||
@Json(name = "incompatible_intent")
|
||||
INCOMPATIBLE_INTENT("incompatible_intent")
|
||||
}
|
@@ -19,6 +19,9 @@ package org.matrix.android.sdk.api.rendezvous.model
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
/**
|
||||
* This is only used for v1 of MSC3906 and will be removed in future. FailureReason is used in v2.
|
||||
*/
|
||||
@JsonClass(generateAdapter = false)
|
||||
enum class Outcome(val value: String) {
|
||||
@Json(name = "success")
|
||||
|
@@ -23,7 +23,11 @@ import com.squareup.moshi.JsonClass
|
||||
internal data class Payload(
|
||||
val type: PayloadType,
|
||||
val intent: RendezvousIntent? = null,
|
||||
/**
|
||||
* This is only used in v1 of MSC3906 and will be removed in future.
|
||||
*/
|
||||
val outcome: Outcome? = null,
|
||||
val reason: FailureReason? = null,
|
||||
val protocols: List<Protocol>? = null,
|
||||
val protocol: Protocol? = null,
|
||||
val homeserver: String? = null,
|
||||
@@ -32,5 +36,5 @@ internal data class Payload(
|
||||
@Json(name = "device_key") val deviceKey: String? = null,
|
||||
@Json(name = "verifying_device_id") val verifyingDeviceId: String? = null,
|
||||
@Json(name = "verifying_device_key") val verifyingDeviceKey: String? = null,
|
||||
@Json(name = "master_key") val masterKey: String? = null
|
||||
@Json(name = "master_key") val masterKey: String? = null,
|
||||
)
|
||||
|
@@ -21,12 +21,33 @@ import com.squareup.moshi.JsonClass
|
||||
|
||||
@JsonClass(generateAdapter = false)
|
||||
internal enum class PayloadType(val value: String) {
|
||||
@Json(name = "m.login.start")
|
||||
START("m.login.start"),
|
||||
|
||||
/**
|
||||
* This is only used in v1 of MSC3906 and will be removed in future.
|
||||
*/
|
||||
@Json(name = "m.login.finish")
|
||||
FINISH("m.login.finish"),
|
||||
|
||||
@Json(name = "m.login.progress")
|
||||
PROGRESS("m.login.progress")
|
||||
PROGRESS("m.login.progress"),
|
||||
|
||||
@Json(name = "m.login.protocol")
|
||||
PROTOCOL("m.login.protocol"),
|
||||
|
||||
@Json(name = "m.login.protocols")
|
||||
PROTOCOLS("m.login.protocols"),
|
||||
|
||||
@Json(name = "m.login.approved")
|
||||
APPROVED("m.login.approved"),
|
||||
|
||||
@Json(name = "m.login.success")
|
||||
SUCCESS("m.login.success"),
|
||||
|
||||
@Json(name = "m.login.verified")
|
||||
VERIFIED("m.login.verified"),
|
||||
|
||||
@Json(name = "m.login.failure")
|
||||
FAILURE("m.login.failure"),
|
||||
|
||||
@Json(name = "m.login.declined")
|
||||
DECLINED("m.login.declined"),
|
||||
}
|
||||
|
@@ -21,5 +21,6 @@ import com.squareup.moshi.JsonClass
|
||||
@JsonClass(generateAdapter = true)
|
||||
open class RendezvousCode(
|
||||
open val intent: RendezvousIntent,
|
||||
open val flow: String?,
|
||||
open val rendezvous: Rendezvous
|
||||
)
|
||||
|
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.matrix.android.sdk.api.rendezvous.model
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
@JsonClass(generateAdapter = false)
|
||||
enum class RendezvousFlow(val value: String) {
|
||||
// v1 is never represented in JSON so we don't annotate it
|
||||
SETUP_ADDITIONAL_DEVICE_V1("org.matrix.msc3906.v1"),
|
||||
@Json(name = "org.matrix.msc3906.setup.additional_device.v2")
|
||||
SETUP_ADDITIONAL_DEVICE_V2("org.matrix.msc3906.setup.additional_device.v2"),
|
||||
}
|
@@ -33,6 +33,8 @@ import java.util.Locale
|
||||
|
||||
/**
|
||||
* Implementation of the Simple HTTP transport MSC3886: https://github.com/matrix-org/matrix-spec-proposals/pull/3886
|
||||
*
|
||||
* @alpha This is an experimental API, and is subject to change until MSC3886 is stabilised and accepted.
|
||||
*/
|
||||
class SimpleHttpRendezvousTransport(rendezvousUri: String?) : RendezvousTransport {
|
||||
companion object {
|
||||
|
Reference in New Issue
Block a user