diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/OlmMachine.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/OlmMachine.kt index 3bbd7b4524..6868d5b997 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/OlmMachine.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/OlmMachine.kt @@ -16,6 +16,8 @@ package org.matrix.android.sdk.internal.crypto +import android.os.Handler +import android.os.Looper import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import java.io.File @@ -26,10 +28,16 @@ import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext import org.matrix.android.sdk.api.listeners.ProgressListener import org.matrix.android.sdk.api.session.crypto.MXCryptoError +import org.matrix.android.sdk.api.session.crypto.verification.CancelCode +import org.matrix.android.sdk.api.session.crypto.verification.QrCodeVerificationTransaction +import org.matrix.android.sdk.api.session.crypto.verification.VerificationService +import org.matrix.android.sdk.api.session.crypto.verification.VerificationTxState +import org.matrix.android.sdk.api.session.crypto.verification.safeValueOf import org.matrix.android.sdk.api.session.events.model.Content import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.util.JsonDict import org.matrix.android.sdk.internal.crypto.crosssigning.DeviceTrustLevel +import org.matrix.android.sdk.internal.crypto.crosssigning.fromBase64 import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo import org.matrix.android.sdk.internal.crypto.model.ImportRoomKeysResult import org.matrix.android.sdk.internal.crypto.model.MXUsersDevicesMap @@ -45,11 +53,14 @@ import uniffi.olm.Device import uniffi.olm.DeviceLists import uniffi.olm.KeyRequestPair import uniffi.olm.Logger +import uniffi.olm.OutgoingVerificationRequest +import uniffi.olm.QrCode import uniffi.olm.OlmMachine as InnerMachine import uniffi.olm.ProgressListener as RustProgressListener import uniffi.olm.Request import uniffi.olm.RequestType -import uniffi.olm.Sas +import uniffi.olm.Verification +import uniffi.olm.VerificationRequest import uniffi.olm.setLogger class CryptoLogger : Logger { @@ -117,6 +128,136 @@ internal class DeviceUpdateObserver { } } +internal class QrCodeVerification( + private val machine: uniffi.olm.OlmMachine, + private var inner: QrCode, + private val sender: RequestSender, + private val listeners: ArrayList, +) : QrCodeVerificationTransaction { + private val uiHandler = Handler(Looper.getMainLooper()) + private var stateField: VerificationTxState = VerificationTxState.OnStarted + + private fun dispatchTxUpdated() { + uiHandler.post { + listeners.forEach { + try { + it.transactionUpdated(this) + } catch (e: Throwable) { + Timber.e(e, "## Error while notifying listeners") + } + } + } + } + + override val qrCodeText: String? + get() { + val data = this.machine.generateQrCode(this.inner.otherUserId, this.inner.flowId) + + // TODO Why are we encoding this to ISO_8859_1? If we're going to encode, why not base64? + return data?.fromBase64()?.toString(Charsets.ISO_8859_1) + } + + override fun userHasScannedOtherQrCode(otherQrCodeText: String) { + TODO("Not yet implemented") + } + + override fun otherUserScannedMyQrCode() { + val request = runBlocking { confirm() } ?: return + sendRequest(request) + } + + override fun otherUserDidNotScannedMyQrCode() { + // TODO Is this code correct here? The old code seems to do this + cancelHelper(CancelCode.MismatchedKeys) + } + + override var state: VerificationTxState + get() { + refreshData() + val state = when { + this.inner.isDone -> VerificationTxState.Verified + this.inner.otherSideScanned -> VerificationTxState.QrScannedByOther + this.inner.isCancelled -> { + val cancelCode = safeValueOf(this.inner.cancelCode) + val byMe = this.inner.cancelledByUs ?: false + VerificationTxState.Cancelled(cancelCode, byMe) + } + else -> { + VerificationTxState.None + } + } + + return state + } + @Suppress("UNUSED_PARAMETER") + set(value) {} + + override val transactionId: String + get() = this.inner.flowId + + override val otherUserId: String + get() = this.inner.otherUserId + + override var otherDeviceId: String? + get() = this.inner.otherDeviceId + @Suppress("UNUSED_PARAMETER") + set(value) {} + + override val isIncoming: Boolean + get() = !this.inner.weStarted + + override fun cancel() { + cancelHelper(CancelCode.User) + } + + override fun cancel(code: CancelCode) { + cancelHelper(code) + } + + override fun isToDeviceTransport(): Boolean { + return this.inner.roomId == null + } + + @Throws(CryptoStoreErrorException::class) + suspend fun confirm(): OutgoingVerificationRequest? = + withContext(Dispatchers.IO) { + machine.confirmVerification(inner.otherUserId, inner.flowId) + } + + private fun sendRequest(request: OutgoingVerificationRequest) { + runBlocking { + when (request) { + is OutgoingVerificationRequest.ToDevice -> { + sender.sendToDevice(request.eventType, request.body) + } + is OutgoingVerificationRequest.InRoom -> TODO() + } + } + + refreshData() + dispatchTxUpdated() + } + + private fun cancelHelper(code: CancelCode) { + val request = this.machine.cancelVerification(this.inner.otherUserId, inner.flowId, code.value) + + if (request != null) { + sendRequest(request) + } + } + + private fun refreshData() { + when (val verification = this.machine.getVerification(this.inner.otherUserId, this.inner.flowId)) { + is Verification.QrCodeV1 -> { + this.inner = verification.qrcode + } + else -> {} + } + + return + } +} + internal class OlmMachine( user_id: String, device_id: String, @@ -523,24 +664,16 @@ internal class OlmMachine( runBlocking { inner.discardRoomKey(roomId) } } - fun getVerificationRequests(userId: String): List { - return this.inner.getVerificationRequests(userId).map { - VerificationRequest(this.inner, it) - } + fun getVerificationRequests(userId: String): List { + return this.inner.getVerificationRequests(userId) } fun getVerificationRequest(userId: String, flowId: String): VerificationRequest? { - val request = this.inner.getVerificationRequest(userId, flowId) - - return if (request == null) { - null - } else { - VerificationRequest(this.inner, request) - } + return this.inner.getVerificationRequest(userId, flowId) } /** Get an active verification */ - fun getVerification(userId: String, flowId: String): Sas? { + fun getVerification(userId: String, flowId: String): Verification? { return this.inner.getVerification(userId, flowId) } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/RustSasVerification.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/RustSasVerification.kt index 86be1b9cc6..9c4ca62e9f 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/RustSasVerification.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/RustSasVerification.kt @@ -33,6 +33,7 @@ import uniffi.olm.CryptoStoreErrorException import uniffi.olm.OlmMachine import uniffi.olm.OutgoingVerificationRequest import uniffi.olm.Sas +import uniffi.olm.Verification internal class SasVerification( private val machine: OlmMachine, @@ -57,10 +58,11 @@ internal class SasVerification( } private fun refreshData() { - val sas = this.machine.getVerification(this.inner.otherUserId, this.inner.flowId) - - if (sas != null) { - this.inner = sas + when (val verification = this.machine.getVerification(this.inner.otherUserId, this.inner.flowId)) { + is Verification.SasV1 -> { + this.inner = verification.sas + } + else -> {} } return diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/VerificationRequest.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/VerificationRequest.kt index 10b23ff0ad..78df72a928 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/VerificationRequest.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/VerificationRequest.kt @@ -22,6 +22,7 @@ import org.matrix.android.sdk.api.session.crypto.verification.PendingVerificatio import org.matrix.android.sdk.api.session.crypto.verification.ValidVerificationInfoReady import org.matrix.android.sdk.api.session.crypto.verification.ValidVerificationInfoRequest import org.matrix.android.sdk.api.session.crypto.verification.VerificationMethod +import org.matrix.android.sdk.api.session.crypto.verification.VerificationService import org.matrix.android.sdk.api.session.crypto.verification.safeValueOf import org.matrix.android.sdk.internal.crypto.model.rest.VERIFICATION_METHOD_QR_CODE_SCAN import org.matrix.android.sdk.internal.crypto.model.rest.VERIFICATION_METHOD_QR_CODE_SHOW @@ -34,7 +35,9 @@ import uniffi.olm.VerificationRequest internal class VerificationRequest( private val machine: OlmMachine, - private var inner: VerificationRequest + private var inner: VerificationRequest, + private val sender: RequestSender, + private val listeners: ArrayList, ) { private fun refreshData() { val request = this.machine.getVerificationRequest(this.inner.otherUserId, this.inner.flowId) @@ -46,6 +49,21 @@ internal class VerificationRequest( return } + internal fun startQrVerification(): QrCodeVerification? { + val qrcode = this.machine.startQrVerification(this.inner.otherUserId, this.inner.flowId) + + return if (qrcode != null) { + QrCodeVerification( + this.machine, + qrcode, + this.sender, + this.listeners, + ) + } else { + null + } + } + fun acceptWithMethods(methods: List): OutgoingVerificationRequest? { val stringMethods: MutableList = methods diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/RustVerificationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/RustVerificationService.kt index b103607c78..cbf8ca32ca 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/RustVerificationService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/RustVerificationService.kt @@ -29,14 +29,17 @@ import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.room.model.message.MessageType import org.matrix.android.sdk.internal.crypto.OlmMachine +import org.matrix.android.sdk.internal.crypto.QrCodeVerification import org.matrix.android.sdk.internal.crypto.RequestSender import org.matrix.android.sdk.internal.crypto.SasVerification +import org.matrix.android.sdk.internal.crypto.VerificationRequest import org.matrix.android.sdk.internal.crypto.model.rest.KeyVerificationDone import org.matrix.android.sdk.internal.crypto.model.rest.KeyVerificationKey import org.matrix.android.sdk.internal.crypto.model.rest.KeyVerificationRequest import org.matrix.android.sdk.internal.session.SessionScope import timber.log.Timber import uniffi.olm.OutgoingVerificationRequest +import uniffi.olm.Verification @SessionScope internal class RustVerificationService @@ -183,13 +186,16 @@ constructor( tid: String, ): VerificationTransaction? { val verification = this.olmMachine.getVerification(otherUserId, tid) ?: return null - return SasVerification(this.olmMachine.inner(), verification, this.requestSender, this.listeners) + return when (verification) { + is Verification.QrCodeV1 -> QrCodeVerification(this.olmMachine.inner(), verification.qrcode, this.requestSender, this.listeners) + is Verification.SasV1 -> SasVerification(this.olmMachine.inner(), verification.sas, this.requestSender, this.listeners) + } } override fun getExistingVerificationRequests( otherUserId: String ): List { - return this.olmMachine.getVerificationRequests(otherUserId).map { + return this.getVerificationRequests(otherUserId).map { it.toPendingVerificationRequest() } } @@ -199,7 +205,7 @@ constructor( tid: String? ): PendingVerificationRequest? { return if (tid != null) { - olmMachine.getVerificationRequest(otherUserId, tid)?.toPendingVerificationRequest() + this.getVerificationRequest(otherUserId, tid)?.toPendingVerificationRequest() } else { null } @@ -221,7 +227,7 @@ constructor( // should check if already one (and cancel it) return if (method == VerificationMethod.SAS) { val flowId = transactionId ?: return null - val request = this.olmMachine.getVerificationRequest(otherUserId, flowId) + val request = this.getVerificationRequest(otherUserId, flowId) runBlocking { val response = request?.startSasVerification() if (response != null) { @@ -297,12 +303,38 @@ constructor( return true } + private fun getVerificationRequest(otherUserId: String, transactionId: String): VerificationRequest? { + val request = this.olmMachine.getVerificationRequest(otherUserId, transactionId) + + return if (request != null) { + VerificationRequest( + this.olmMachine.inner(), + request, + requestSender, + listeners, + ) + } else { + null + } + } + + private fun getVerificationRequests(userId: String): List { + return this.olmMachine.getVerificationRequests(userId).map { + VerificationRequest( + this.olmMachine.inner(), + it, + this.requestSender, + this.listeners, + ) + } + } + override fun readyPendingVerification( methods: List, otherUserId: String, transactionId: String ): Boolean { - val request = this.olmMachine.getVerificationRequest(otherUserId, transactionId) + val request = this.getVerificationRequest(otherUserId, transactionId) return if (request != null) { val outgoingRequest = request.acceptWithMethods(methods) @@ -310,6 +342,10 @@ constructor( if (outgoingRequest != null) { runBlocking { sendRequest(outgoingRequest) } dispatchRequestUpdated(request.toPendingVerificationRequest()) + val qrcode = request.startQrVerification() + if (qrcode != null) { + dispatchTxAdded(qrcode) + } true } else { false