QR login + E2EE set up
This commit is contained in:
parent
0111b932de
commit
1ed082d3cb
@ -16,8 +16,11 @@
|
|||||||
|
|
||||||
package org.matrix.android.sdk.internal.rendezvous
|
package org.matrix.android.sdk.internal.rendezvous
|
||||||
|
|
||||||
|
import android.net.Uri
|
||||||
import com.squareup.moshi.Json
|
import com.squareup.moshi.Json
|
||||||
import com.squareup.moshi.JsonClass
|
import com.squareup.moshi.JsonClass
|
||||||
|
import org.matrix.android.sdk.api.auth.AuthenticationService
|
||||||
|
import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig
|
||||||
import org.matrix.android.sdk.api.logger.LoggerTag
|
import org.matrix.android.sdk.api.logger.LoggerTag
|
||||||
import org.matrix.android.sdk.api.session.Session
|
import org.matrix.android.sdk.api.session.Session
|
||||||
import org.matrix.android.sdk.api.session.crypto.crosssigning.DeviceTrustLevel
|
import org.matrix.android.sdk.api.session.crypto.crosssigning.DeviceTrustLevel
|
||||||
@ -56,9 +59,12 @@ internal data class Payload(
|
|||||||
|
|
||||||
private val TAG = LoggerTag(Rendezvous::class.java.simpleName, LoggerTag.RENDEZVOUS).value
|
private val TAG = LoggerTag(Rendezvous::class.java.simpleName, LoggerTag.RENDEZVOUS).value
|
||||||
|
|
||||||
data class Rendezvous(
|
/**
|
||||||
|
* Implementation of MSC3906 to sign in + E2EE set up using a QR code.
|
||||||
|
*/
|
||||||
|
class Rendezvous(
|
||||||
val channel: RendezvousChannel,
|
val channel: RendezvousChannel,
|
||||||
val theirIntent: RendezvousIntent
|
val theirIntent: RendezvousIntent,
|
||||||
) {
|
) {
|
||||||
companion object {
|
companion object {
|
||||||
fun buildChannelFromCode(code: String, onCancelled: (reason: RendezvousFailureReason) -> Unit): Rendezvous {
|
fun buildChannelFromCode(code: String, onCancelled: (reason: RendezvousFailureReason) -> Unit): Rendezvous {
|
||||||
@ -116,7 +122,7 @@ data class Rendezvous(
|
|||||||
return checksum
|
return checksum
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun completeOnNewDevice(): Session? {
|
suspend fun waitForLoginOnNewDevice(authenticationService: AuthenticationService): Session? {
|
||||||
Timber.tag(TAG).i("Waiting for login_token");
|
Timber.tag(TAG).i("Waiting for login_token");
|
||||||
|
|
||||||
val loginToken = receive()
|
val loginToken = receive()
|
||||||
@ -143,22 +149,11 @@ data class Rendezvous(
|
|||||||
|
|
||||||
Timber.tag(TAG).i("Got login_token: $login_token for $homeserver");
|
Timber.tag(TAG).i("Got login_token: $login_token for $homeserver");
|
||||||
|
|
||||||
// TODO: set view to be state logging in?
|
val hsConfig = HomeServerConnectionConfig(homeServerUri = Uri.parse(homeserver))
|
||||||
|
return authenticationService.loginUsingQrLoginToken(hsConfig, login_token)
|
||||||
|
}
|
||||||
|
|
||||||
// use token to login
|
suspend fun completeVerificationOnNewDevice(session: Session) {
|
||||||
// const login = await sendLoginRequest(homeserver, undefined, "m.login.token", { token: login_token });
|
|
||||||
//
|
|
||||||
// await setLoggedIn(login);
|
|
||||||
//
|
|
||||||
// const { deviceId, userId } = login;
|
|
||||||
//
|
|
||||||
// const client = MatrixClientPeg.get();
|
|
||||||
//
|
|
||||||
|
|
||||||
val newSession: Session? = null
|
|
||||||
|
|
||||||
newSession ?.let {
|
|
||||||
session ->
|
|
||||||
val userId = session.myUserId
|
val userId = session.myUserId
|
||||||
val crypto = session.cryptoService()
|
val crypto = session.cryptoService()
|
||||||
val deviceId = crypto.getMyDevice().deviceId
|
val deviceId = crypto.getMyDevice().deviceId
|
||||||
@ -170,32 +165,30 @@ data class Rendezvous(
|
|||||||
val verificationResponse = receive()
|
val verificationResponse = receive()
|
||||||
val verifyingDeviceId = verificationResponse?.verifying_device_id ?: throw RuntimeException("No verifying device id returned")
|
val verifyingDeviceId = verificationResponse?.verifying_device_id ?: throw RuntimeException("No verifying device id returned")
|
||||||
val verifyingDeviceFromServer = crypto.getCryptoDeviceInfo(userId, verifyingDeviceId)
|
val verifyingDeviceFromServer = crypto.getCryptoDeviceInfo(userId, verifyingDeviceId)
|
||||||
if (verifyingDeviceFromServer?.fingerprint() == verificationResponse.verifying_device_key) {
|
if (verifyingDeviceFromServer?.fingerprint() != verificationResponse.verifying_device_key) {
|
||||||
|
Timber.tag(TAG).w("Verifying device $verifyingDeviceId doesn't match: $verifyingDeviceFromServer")
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// set other device as verified
|
// set other device as verified
|
||||||
Timber.tag(TAG).i("Setting device $verifyingDeviceId as verified");
|
Timber.tag(TAG).i("Setting device $verifyingDeviceId as verified");
|
||||||
crypto.setDeviceVerification(DeviceTrustLevel(locallyVerified = true, crossSigningVerified = false), userId, verifyingDeviceId)
|
crypto.setDeviceVerification(DeviceTrustLevel(locallyVerified = true, crossSigningVerified = false), userId, verifyingDeviceId)
|
||||||
|
|
||||||
verificationResponse.master_key ?.let {
|
// TODO: what do we do with the master key?
|
||||||
// set master key as trusted
|
// verificationResponse.master_key ?.let {
|
||||||
crypto.setDeviceVerification(DeviceTrustLevel(locallyVerified = true, crossSigningVerified = false), userId, it)
|
// // set master key as trusted
|
||||||
|
// crypto.setDeviceVerification(DeviceTrustLevel(locallyVerified = true, crossSigningVerified = false), userId, it)
|
||||||
}
|
// }
|
||||||
|
|
||||||
// request secrets from the verifying device
|
// request secrets from the verifying device
|
||||||
Timber.tag(TAG).i("Requesting secrets from $verifyingDeviceId")
|
Timber.tag(TAG).i("Requesting secrets from $verifyingDeviceId")
|
||||||
|
|
||||||
session.sharedSecretStorageService() .let {
|
session.sharedSecretStorageService() .let {
|
||||||
it.requestSecret(verifyingDeviceId, MASTER_KEY_SSSS_NAME)
|
it.requestSecret(MASTER_KEY_SSSS_NAME, verifyingDeviceId)
|
||||||
it.requestSecret(verifyingDeviceId, SELF_SIGNING_KEY_SSSS_NAME)
|
it.requestSecret(SELF_SIGNING_KEY_SSSS_NAME, verifyingDeviceId)
|
||||||
it.requestSecret(verifyingDeviceId, USER_SIGNING_KEY_SSSS_NAME)
|
it.requestSecret(USER_SIGNING_KEY_SSSS_NAME, verifyingDeviceId)
|
||||||
it.requestSecret(verifyingDeviceId, KEYBACKUP_SECRET_SSSS_NAME)
|
it.requestSecret(KEYBACKUP_SECRET_SSSS_NAME, verifyingDeviceId)
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
Timber.tag(TAG).i("Verifying device $verifyingDeviceId doesn't match: $verifyingDeviceFromServer")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return newSession
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun receive(): Payload? {
|
private suspend fun receive(): Payload? {
|
||||||
|
@ -16,24 +16,31 @@
|
|||||||
|
|
||||||
package im.vector.app.features.login.qr
|
package im.vector.app.features.login.qr
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
import com.airbnb.mvrx.MavericksViewModelFactory
|
import com.airbnb.mvrx.MavericksViewModelFactory
|
||||||
import dagger.assisted.Assisted
|
import dagger.assisted.Assisted
|
||||||
import dagger.assisted.AssistedFactory
|
import dagger.assisted.AssistedFactory
|
||||||
import dagger.assisted.AssistedInject
|
import dagger.assisted.AssistedInject
|
||||||
|
import im.vector.app.core.di.ActiveSessionHolder
|
||||||
import im.vector.app.core.di.MavericksAssistedViewModelFactory
|
import im.vector.app.core.di.MavericksAssistedViewModelFactory
|
||||||
import im.vector.app.core.di.hiltMavericksViewModelFactory
|
import im.vector.app.core.di.hiltMavericksViewModelFactory
|
||||||
|
import im.vector.app.core.extensions.configureAndStart
|
||||||
import im.vector.app.core.platform.VectorViewModel
|
import im.vector.app.core.platform.VectorViewModel
|
||||||
|
import im.vector.app.features.home.HomeActivity
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import org.matrix.android.sdk.api.auth.AuthenticationService
|
||||||
import org.matrix.android.sdk.api.session.Session
|
import org.matrix.android.sdk.api.session.Session
|
||||||
import org.matrix.android.sdk.internal.rendezvous.Rendezvous
|
import org.matrix.android.sdk.internal.rendezvous.Rendezvous
|
||||||
import org.matrix.android.sdk.internal.rendezvous.RendezvousFailureReason
|
import org.matrix.android.sdk.internal.rendezvous.RendezvousFailureReason
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
class QrCodeLoginViewModel @AssistedInject constructor(
|
class QrCodeLoginViewModel @AssistedInject constructor(
|
||||||
@Assisted private val initialState: QrCodeLoginViewState
|
@Assisted private val initialState: QrCodeLoginViewState,
|
||||||
|
private val applicationContext: Context,
|
||||||
|
private val authenticationService: AuthenticationService,
|
||||||
|
private val activeSessionHolder: ActiveSessionHolder,
|
||||||
) : VectorViewModel<QrCodeLoginViewState, QrCodeLoginAction, QrCodeLoginViewEvents>(initialState) {
|
) : VectorViewModel<QrCodeLoginViewState, QrCodeLoginAction, QrCodeLoginViewEvents>(initialState) {
|
||||||
|
|
||||||
val TAG: String = QrCodeLoginViewModel::class.java.simpleName
|
val TAG: String = QrCodeLoginViewModel::class.java.simpleName
|
||||||
|
|
||||||
@AssistedFactory
|
@AssistedFactory
|
||||||
@ -81,43 +88,30 @@ class QrCodeLoginViewModel @AssistedInject constructor(
|
|||||||
_viewEvents.post(QrCodeLoginViewEvents.NavigateToStatusScreen)
|
_viewEvents.post(QrCodeLoginViewEvents.NavigateToStatusScreen)
|
||||||
|
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
|
try {
|
||||||
val confirmationCode = rendezvous.startAfterScanningCode()
|
val confirmationCode = rendezvous.startAfterScanningCode()
|
||||||
Timber.tag(TAG).i("Established secure channel with checksum: $confirmationCode")
|
Timber.tag(TAG).i("Established secure channel with checksum: $confirmationCode")
|
||||||
confirmationCode ?.let {
|
confirmationCode?.let {
|
||||||
onConnectionEstablished(it)
|
onConnectionEstablished(it)
|
||||||
rendezvous.completeOnNewDevice()
|
val session = rendezvous.waitForLoginOnNewDevice(authenticationService)
|
||||||
}
|
onSigningIn()
|
||||||
}
|
session?.let {
|
||||||
// if (isValidQrCode(action.qrCode)) {
|
activeSessionHolder.setActiveSession(session)
|
||||||
// setState {
|
authenticationService.reset()
|
||||||
// copy(
|
|
||||||
// connectionStatus = QrCodeLoginConnectionStatus.ConnectingToDevice
|
|
||||||
// )
|
|
||||||
// }
|
|
||||||
// _viewEvents.post(QrCodeLoginViewEvents.NavigateToStatusScreen)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
|
|
||||||
// // TODO. UI test purpose. Fixme remove!
|
session.configureAndStart(applicationContext)
|
||||||
// viewModelScope.launch {
|
|
||||||
// delay(3000)
|
|
||||||
// onFailed(QrCodeLoginErrorType.TIMEOUT, true)
|
|
||||||
// delay(3000)
|
|
||||||
// onConnectionEstablished("1234-ABCD-5678-EFGH")
|
|
||||||
// delay(3000)
|
|
||||||
// onSigningIn()
|
|
||||||
// delay(3000)
|
|
||||||
// onFailed(QrCodeLoginErrorType.DEVICE_IS_NOT_SUPPORTED, false)
|
|
||||||
// }
|
|
||||||
// // TODO. UI test purpose. Fixme remove!
|
|
||||||
// viewModelScope.launch {
|
|
||||||
// delay(3000)
|
|
||||||
// onConnectionEstablished("1234-ABCD-5678-EFGH")
|
|
||||||
// delay(3000)
|
|
||||||
// onSigningIn()
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
|
rendezvous.completeVerificationOnNewDevice(session)
|
||||||
|
|
||||||
|
_viewEvents.post(QrCodeLoginViewEvents.NavigateToHomeScreen)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (failure: Throwable) {
|
||||||
|
Timber.tag(TAG).e(failure, "Error occurred during sign in")
|
||||||
|
onFailed(RendezvousFailureReason.Unknown)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun onFailed(reason: RendezvousFailureReason) {
|
private fun onFailed(reason: RendezvousFailureReason) {
|
||||||
setState {
|
setState {
|
||||||
@ -144,13 +138,6 @@ class QrCodeLoginViewModel @AssistedInject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO. UI test purpose. Fixme accordingly.
|
|
||||||
*/
|
|
||||||
private fun isValidQrCode(qrCode: String): Boolean {
|
|
||||||
return qrCode.startsWith("http")
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO. UI test purpose. Fixme accordingly.
|
* TODO. UI test purpose. Fixme accordingly.
|
||||||
*/
|
*/
|
||||||
|
Loading…
x
Reference in New Issue
Block a user