crypto: Move the sendToDevice logic to a common class and use it for verifications

This commit is contained in:
Damir Jelić 2021-06-24 13:58:59 +02:00
parent 6a79d022c3
commit aad18ebec7
4 changed files with 272 additions and 244 deletions

View file

@ -20,25 +20,16 @@ import android.content.Context
import androidx.annotation.VisibleForTesting import androidx.annotation.VisibleForTesting
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.paging.PagedList import androidx.paging.PagedList
import com.squareup.moshi.Types
import dagger.Lazy
import java.io.File
import java.util.concurrent.atomic.AtomicBoolean
import java.util.concurrent.ConcurrentHashMap
import javax.inject.Inject
import kotlin.jvm.Throws
import kotlin.math.max
import kotlinx.coroutines.async
import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.async
import kotlinx.coroutines.cancelChildren import kotlinx.coroutines.cancelChildren
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.joinAll import kotlinx.coroutines.joinAll
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext
import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.MatrixCallback
import org.matrix.android.sdk.api.NoOpMatrixCallback import org.matrix.android.sdk.api.NoOpMatrixCallback
import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
@ -59,14 +50,11 @@ import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibility
import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibilityContent import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibilityContent
import org.matrix.android.sdk.api.session.room.model.RoomMemberContent import org.matrix.android.sdk.api.session.room.model.RoomMemberContent
import org.matrix.android.sdk.api.util.JsonDict import org.matrix.android.sdk.api.util.JsonDict
import org.matrix.android.sdk.internal.crypto.OlmMachine
import org.matrix.android.sdk.internal.crypto.setRustLogger
import org.matrix.android.sdk.internal.crypto.crosssigning.DefaultCrossSigningService import org.matrix.android.sdk.internal.crypto.crosssigning.DefaultCrossSigningService
import org.matrix.android.sdk.internal.crypto.crosssigning.DeviceTrustLevel import org.matrix.android.sdk.internal.crypto.crosssigning.DeviceTrustLevel
import org.matrix.android.sdk.internal.crypto.keysbackup.DefaultKeysBackupService import org.matrix.android.sdk.internal.crypto.keysbackup.DefaultKeysBackupService
import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo 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.ImportRoomKeysResult
import org.matrix.android.sdk.internal.crypto.model.MXDeviceInfo
import org.matrix.android.sdk.internal.crypto.model.MXEncryptEventContentResult import org.matrix.android.sdk.internal.crypto.model.MXEncryptEventContentResult
import org.matrix.android.sdk.internal.crypto.model.MXUsersDevicesMap import org.matrix.android.sdk.internal.crypto.model.MXUsersDevicesMap
import org.matrix.android.sdk.internal.crypto.model.event.RoomKeyContent import org.matrix.android.sdk.internal.crypto.model.event.RoomKeyContent
@ -74,19 +62,19 @@ import org.matrix.android.sdk.internal.crypto.model.event.RoomKeyWithHeldContent
import org.matrix.android.sdk.internal.crypto.model.rest.DeviceInfo import org.matrix.android.sdk.internal.crypto.model.rest.DeviceInfo
import org.matrix.android.sdk.internal.crypto.model.rest.DevicesListResponse import org.matrix.android.sdk.internal.crypto.model.rest.DevicesListResponse
import org.matrix.android.sdk.internal.crypto.model.rest.ForwardedRoomKeyContent import org.matrix.android.sdk.internal.crypto.model.rest.ForwardedRoomKeyContent
import org.matrix.android.sdk.internal.crypto.model.rest.KeysUploadResponse
import org.matrix.android.sdk.internal.crypto.model.rest.KeysClaimResponse import org.matrix.android.sdk.internal.crypto.model.rest.KeysClaimResponse
import org.matrix.android.sdk.internal.crypto.model.rest.KeysQueryResponse import org.matrix.android.sdk.internal.crypto.model.rest.KeysQueryResponse
import org.matrix.android.sdk.internal.crypto.model.rest.KeysUploadResponse
import org.matrix.android.sdk.internal.crypto.repository.WarnOnUnknownDeviceRepository import org.matrix.android.sdk.internal.crypto.repository.WarnOnUnknownDeviceRepository
import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
import org.matrix.android.sdk.internal.crypto.tasks.ClaimOneTimeKeysForUsersDeviceTask
import org.matrix.android.sdk.internal.crypto.tasks.DeleteDeviceTask import org.matrix.android.sdk.internal.crypto.tasks.DeleteDeviceTask
import org.matrix.android.sdk.internal.crypto.tasks.DownloadKeysForUsersTask import org.matrix.android.sdk.internal.crypto.tasks.DownloadKeysForUsersTask
import org.matrix.android.sdk.internal.crypto.tasks.GetDeviceInfoTask import org.matrix.android.sdk.internal.crypto.tasks.GetDeviceInfoTask
import org.matrix.android.sdk.internal.crypto.tasks.GetDevicesTask import org.matrix.android.sdk.internal.crypto.tasks.GetDevicesTask
import org.matrix.android.sdk.internal.crypto.tasks.UploadKeysTask
import org.matrix.android.sdk.internal.crypto.tasks.SetDeviceNameTask
import org.matrix.android.sdk.internal.crypto.tasks.SendToDeviceTask import org.matrix.android.sdk.internal.crypto.tasks.SendToDeviceTask
import org.matrix.android.sdk.internal.crypto.tasks.ClaimOneTimeKeysForUsersDeviceTask import org.matrix.android.sdk.internal.crypto.tasks.SetDeviceNameTask
import org.matrix.android.sdk.internal.crypto.tasks.UploadKeysTask
import org.matrix.android.sdk.internal.crypto.verification.RustVerificationService import org.matrix.android.sdk.internal.crypto.verification.RustVerificationService
import org.matrix.android.sdk.internal.di.DeviceId import org.matrix.android.sdk.internal.di.DeviceId
import org.matrix.android.sdk.internal.di.MoshiProvider import org.matrix.android.sdk.internal.di.MoshiProvider
@ -95,7 +83,6 @@ import org.matrix.android.sdk.internal.di.UserId
import org.matrix.android.sdk.internal.extensions.foldToCallback import org.matrix.android.sdk.internal.extensions.foldToCallback
import org.matrix.android.sdk.internal.session.SessionScope import org.matrix.android.sdk.internal.session.SessionScope
import org.matrix.android.sdk.internal.session.room.membership.LoadRoomMembersTask import org.matrix.android.sdk.internal.session.room.membership.LoadRoomMembersTask
import org.matrix.android.sdk.internal.session.sync.model.SyncResponse
import org.matrix.android.sdk.internal.session.sync.model.DeviceListResponse import org.matrix.android.sdk.internal.session.sync.model.DeviceListResponse
import org.matrix.android.sdk.internal.session.sync.model.DeviceOneTimeKeysCountSyncResponse import org.matrix.android.sdk.internal.session.sync.model.DeviceOneTimeKeysCountSyncResponse
import org.matrix.android.sdk.internal.session.sync.model.ToDeviceSyncResponse import org.matrix.android.sdk.internal.session.sync.model.ToDeviceSyncResponse
@ -107,6 +94,30 @@ import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers
import timber.log.Timber import timber.log.Timber
import uniffi.olm.Request import uniffi.olm.Request
import uniffi.olm.RequestType import uniffi.olm.RequestType
import java.io.File
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.atomic.AtomicBoolean
import javax.inject.Inject
import kotlin.math.max
internal class RequestSender(
private val sendToDeviceTask: SendToDeviceTask,
) {
suspend fun sendToDevice(eventType: String, body: String) {
// TODO this produces floats for the Olm type fields, which
// are integers originally.
val adapter = MoshiProvider
.providesMoshi()
.adapter<Map<String, HashMap<String, Any>>>(Map::class.java)
val jsonBody = adapter.fromJson(body)!!
val userMap = MXUsersDevicesMap<Any>()
userMap.join(jsonBody)
val sendToDeviceParams = SendToDeviceTask.Params(eventType, userMap)
sendToDeviceTask.execute(sendToDeviceParams)
}
}
/** /**
* A `CryptoService` class instance manages the end-to-end crypto for a session. * A `CryptoService` class instance manages the end-to-end crypto for a session.
@ -153,6 +164,8 @@ internal class DefaultCryptoService @Inject constructor(
private val isStarting = AtomicBoolean(false) private val isStarting = AtomicBoolean(false)
private val isStarted = AtomicBoolean(false) private val isStarted = AtomicBoolean(false)
private val sender = RequestSender(this.sendToDeviceTask)
private var olmMachine: OlmMachine? = null private var olmMachine: OlmMachine? = null
// The verification service. // The verification service.
private var verificationService: RustVerificationService? = null private var verificationService: RustVerificationService? = null
@ -320,7 +333,7 @@ internal class DefaultCryptoService @Inject constructor(
val machine = OlmMachine(userId, deviceId!!, dataDir, deviceObserver) val machine = OlmMachine(userId, deviceId!!, dataDir, deviceObserver)
this.olmMachine = machine this.olmMachine = machine
this.verificationService = this.verificationService =
RustVerificationService(this.taskExecutor, machine, this.sendToDeviceTask) RustVerificationService(machine, this.sender)
Timber.v( Timber.v(
"## CRYPTO | Successfully started up an Olm machine for " + "## CRYPTO | Successfully started up an Olm machine for " +
"${userId}, ${deviceId}, identity keys: ${this.olmMachine?.identityKeys()}") "${userId}, ${deviceId}, identity keys: ${this.olmMachine?.identityKeys()}")
@ -373,7 +386,7 @@ internal class DefaultCryptoService @Inject constructor(
// `ActiveSessionHolder` class in the `setActiveSession()` method. In // `ActiveSessionHolder` class in the `setActiveSession()` method. In
// the `setActiveSession()` method we call the `start()` method of the // the `setActiveSession()` method we call the `start()` method of the
// handlers without first calling the `start()` method of the // handlers without first calling the `start()` method of the
// `DefaultCrytpoService`. // `DefaultCryptoService`.
// //
// The start method of the crypto service isn't part of the // The start method of the crypto service isn't part of the
// `CryptoService` interface so it currently can't be called there. I'm // `CryptoService` interface so it currently can't be called there. I'm
@ -666,7 +679,7 @@ internal class DefaultCryptoService @Inject constructor(
} }
} }
private fun notifyRoomKeyReceival( private fun notifyRoomKeyReceived(
roomId: String, roomId: String,
sessionId: String, sessionId: String,
) { ) {
@ -791,18 +804,7 @@ internal class DefaultCryptoService @Inject constructor(
} }
private suspend fun sendToDevice(request: Request.ToDevice) { private suspend fun sendToDevice(request: Request.ToDevice) {
// TODO this produces floats for the Olm type fields, which this.sender.sendToDevice(request.eventType, request.body)
// are integers originally.
val adapter = MoshiProvider
.providesMoshi()
.adapter<Map<String, HashMap<String, Any>>>(Map::class.java)
val body = adapter.fromJson(request.body)!!
val userMap = MXUsersDevicesMap<Any>()
userMap.join(body)
val sendToDeviceParams = SendToDeviceTask.Params(request.eventType, userMap)
sendToDeviceTask.execute(sendToDeviceParams)
olmMachine!!.markRequestAsSent(request.requestId, RequestType.TO_DEVICE, "{}") olmMachine!!.markRequestAsSent(request.requestId, RequestType.TO_DEVICE, "{}")
} }

View file

@ -27,13 +27,10 @@ import kotlinx.coroutines.withContext
import org.matrix.android.sdk.api.listeners.ProgressListener 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.MXCryptoError
import org.matrix.android.sdk.api.session.crypto.verification.CancelCode import org.matrix.android.sdk.api.session.crypto.verification.CancelCode
import org.matrix.android.sdk.api.session.crypto.verification.EmojiRepresentation
import org.matrix.android.sdk.api.session.crypto.verification.PendingVerificationRequest import org.matrix.android.sdk.api.session.crypto.verification.PendingVerificationRequest
import org.matrix.android.sdk.api.session.crypto.verification.SasVerificationTransaction
import org.matrix.android.sdk.api.session.crypto.verification.ValidVerificationInfoReady 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.ValidVerificationInfoRequest
import org.matrix.android.sdk.api.session.crypto.verification.VerificationMethod import org.matrix.android.sdk.api.session.crypto.verification.VerificationMethod
import org.matrix.android.sdk.api.session.crypto.verification.VerificationTxState
import org.matrix.android.sdk.api.session.events.model.Content 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.session.events.model.Event
import org.matrix.android.sdk.api.util.JsonDict import org.matrix.android.sdk.api.util.JsonDict
@ -46,7 +43,6 @@ import org.matrix.android.sdk.internal.crypto.model.rest.VERIFICATION_METHOD_QR_
import org.matrix.android.sdk.internal.crypto.model.rest.VERIFICATION_METHOD_QR_CODE_SHOW import org.matrix.android.sdk.internal.crypto.model.rest.VERIFICATION_METHOD_QR_CODE_SHOW
import org.matrix.android.sdk.internal.crypto.model.rest.VERIFICATION_METHOD_RECIPROCATE import org.matrix.android.sdk.internal.crypto.model.rest.VERIFICATION_METHOD_RECIPROCATE
import org.matrix.android.sdk.internal.crypto.model.rest.VERIFICATION_METHOD_SAS import org.matrix.android.sdk.internal.crypto.model.rest.VERIFICATION_METHOD_SAS
import org.matrix.android.sdk.internal.crypto.verification.getEmojiForCode
import org.matrix.android.sdk.internal.di.MoshiProvider import org.matrix.android.sdk.internal.di.MoshiProvider
import org.matrix.android.sdk.internal.session.sync.model.DeviceListResponse import org.matrix.android.sdk.internal.session.sync.model.DeviceListResponse
import org.matrix.android.sdk.internal.session.sync.model.DeviceOneTimeKeysCountSyncResponse import org.matrix.android.sdk.internal.session.sync.model.DeviceOneTimeKeysCountSyncResponse
@ -64,7 +60,7 @@ import uniffi.olm.OutgoingVerificationRequest
import uniffi.olm.ProgressListener as RustProgressListener import uniffi.olm.ProgressListener as RustProgressListener
import uniffi.olm.Request import uniffi.olm.Request
import uniffi.olm.RequestType import uniffi.olm.RequestType
import uniffi.olm.Sas as InnerSas import uniffi.olm.Sas
import uniffi.olm.StartSasResult import uniffi.olm.StartSasResult
import uniffi.olm.VerificationRequest as InnerRequest import uniffi.olm.VerificationRequest as InnerRequest
import uniffi.olm.setLogger import uniffi.olm.setLogger
@ -149,7 +145,7 @@ internal class VerificationRequest(
return return
} }
fun accept_with_methods(methods: List<VerificationMethod>): OutgoingVerificationRequest? { fun acceptWithMethods(methods: List<VerificationMethod>): OutgoingVerificationRequest? {
val stringMethods: MutableList<String> = val stringMethods: MutableList<String> =
methods methods
.map { .map {
@ -185,17 +181,11 @@ internal class VerificationRequest(
return this.inner.isReady return this.inner.isReady
} }
suspend fun startSasVerification(): Pair<OutgoingVerificationRequest, SasVerification>? { suspend fun startSasVerification(): StartSasResult? {
refreshData() refreshData()
return withContext(Dispatchers.IO) { return withContext(Dispatchers.IO) {
val response = machine.startSasVerification(inner.otherUserId, inner.flowId) machine.startSasVerification(inner.otherUserId, inner.flowId)
if (response != null) {
Pair(response.request, SasVerification(machine, response.sas))
} else {
null
}
} }
} }
@ -280,7 +270,7 @@ internal class VerificationRequest(
} }
} }
private fun toCancelCode(cancelCode: RustCancelCode): CancelCode { internal fun toCancelCode(cancelCode: RustCancelCode): CancelCode {
return when (cancelCode) { return when (cancelCode) {
RustCancelCode.USER -> CancelCode.User RustCancelCode.USER -> CancelCode.User
RustCancelCode.TIMEOUT -> CancelCode.Timeout RustCancelCode.TIMEOUT -> CancelCode.Timeout
@ -290,162 +280,11 @@ private fun toCancelCode(cancelCode: RustCancelCode): CancelCode {
RustCancelCode.KEY_MISMATCH -> CancelCode.MismatchedKeys RustCancelCode.KEY_MISMATCH -> CancelCode.MismatchedKeys
RustCancelCode.USER_MISMATCH -> CancelCode.MismatchedKeys RustCancelCode.USER_MISMATCH -> CancelCode.MismatchedKeys
RustCancelCode.INVALID_MESSAGE -> CancelCode.InvalidMessage RustCancelCode.INVALID_MESSAGE -> CancelCode.InvalidMessage
// TODO why don't the ruma codes match what's in EA? // TODO why don't the Ruma codes match what's in EA?
RustCancelCode.ACCEPTED -> CancelCode.User RustCancelCode.ACCEPTED -> CancelCode.User
} }
} }
public class SasVerification(private val machine: InnerMachine, private var inner: InnerSas) :
SasVerificationTransaction {
private var stateField: VerificationTxState = VerificationTxState.OnStarted
private fun refreshData() {
val sas = this.machine.getVerification(this.inner.otherUserId, this.inner.flowId)
if (sas != null) {
this.inner = sas
}
return
}
override val isIncoming: Boolean
get() {
return false
}
override var otherDeviceId: String?
get() {
return this.inner.otherDeviceId
}
set(value) {
if (value != null) {
this.inner.otherDeviceId = value
}
}
override val otherUserId: String
get() {
return this.inner.otherUserId
}
override var state: VerificationTxState
get() {
refreshData()
return when {
this.inner.canBePresented -> {
VerificationTxState.ShortCodeReady
}
this.inner.isCancelled -> {
// TODO fetch the cancel code from the rust side
VerificationTxState.Cancelled(CancelCode.User, false)
}
this.inner.isDone -> {
VerificationTxState.Verified
}
else -> {
VerificationTxState.Started
}
}
}
set(v) {
this.stateField = v
}
override val transactionId: String
get() {
return this.inner.flowId
}
override fun cancel() {
TODO()
}
override fun cancel(code: CancelCode) {
TODO()
}
override fun shortCodeDoesNotMatch() {
TODO()
}
override fun isToDeviceTransport(): Boolean {
return false
}
override fun supportsDecimal(): Boolean {
// This is ignored anyways, throw it away?
// The spec also mandates that devices support
// at least decimal and the rust-sdk cancels if
// devices don't support it
return true
}
override fun supportsEmoji(): Boolean {
refreshData()
return this.inner.supportsEmoji
}
override fun userHasVerifiedShortCode() {
// This is confirm
TODO()
}
fun isCanceled(): Boolean {
refreshData()
return this.inner.isCancelled
}
fun isDone(): Boolean {
refreshData()
return this.inner.isDone
}
fun timedOut(): Boolean {
refreshData()
return this.inner.timedOut
}
fun canBePresented(): Boolean {
refreshData()
return this.inner.canBePresented
}
fun accept(): OutgoingVerificationRequest? {
return this.machine.acceptSasVerification(this.inner.otherUserId, inner.flowId)
}
@Throws(CryptoStoreErrorException::class)
suspend fun confirm(): OutgoingVerificationRequest? =
withContext(Dispatchers.IO) {
machine.confirmVerification(inner.otherUserId, inner.flowId)
}
fun cancelHelper(): OutgoingVerificationRequest? {
return this.machine.cancelVerification(this.inner.otherUserId, inner.flowId)
}
override fun getEmojiCodeRepresentation(): List<EmojiRepresentation> {
val emojiIndex = this.machine.getEmojiIndex(this.inner.otherUserId, this.inner.flowId)
return if (emojiIndex != null) {
emojiIndex.map { getEmojiForCode(it) }
} else {
listOf()
}
}
override fun getDecimalCodeRepresentation(): String {
val decimals = this.machine.getDecimals(this.inner.otherUserId, this.inner.flowId)
return if (decimals != null) {
decimals.joinToString(" ")
} else {
""
}
}
}
internal class OlmMachine( internal class OlmMachine(
user_id: String, user_id: String,
device_id: String, device_id: String,
@ -470,6 +309,10 @@ internal class OlmMachine(
return this.inner.identityKeys() return this.inner.identityKeys()
} }
fun inner(): InnerMachine {
return this.inner
}
fun ownDevice(): CryptoDeviceInfo { fun ownDevice(): CryptoDeviceInfo {
val deviceId = this.deviceId() val deviceId = this.deviceId()
@ -528,7 +371,7 @@ internal class OlmMachine(
* This needs to be called after every sync, ideally before processing any other sync changes. * This needs to be called after every sync, ideally before processing any other sync changes.
* *
* @param toDevice A serialized array of to-device events we received in the current sync * @param toDevice A serialized array of to-device events we received in the current sync
* resposne. * response.
* *
* @param deviceChanges The list of devices that have changed in some way since the previous * @param deviceChanges The list of devices that have changed in some way since the previous
* sync. * sync.
@ -613,7 +456,7 @@ internal class OlmMachine(
* **Note**: A room key needs to be shared with the group of users that are members in the given * **Note**: A room key needs to be shared with the group of users that are members in the given
* room. If this is not done this method will panic. * room. If this is not done this method will panic.
* *
* The usual flow to encrypt an evnet using this state machine is as follows: * The usual flow to encrypt an event using this state machine is as follows:
* *
* 1. Get the one-time key claim request to establish 1:1 Olm sessions for * 1. Get the one-time key claim request to establish 1:1 Olm sessions for
* ``` * ```
@ -629,7 +472,7 @@ internal class OlmMachine(
* *
* 4. Send the encrypted event to the server. * 4. Send the encrypted event to the server.
* *
* After the room key is shared steps 1 and 2 will become noops, unless there's some changes in * After the room key is shared steps 1 and 2 will become no-ops, unless there's some changes in
* the room membership or in the list of devices a member has. * the room membership or in the list of devices a member has.
* *
* @param roomId the ID of the room where the encrypted event will be sent to * @param roomId the ID of the room where the encrypted event will be sent to
@ -865,13 +708,7 @@ internal class OlmMachine(
} }
/** Get an active verification */ /** Get an active verification */
fun getVerification(userId: String, flowId: String): SasVerification? { fun getVerification(userId: String, flowId: String): Sas? {
val sas = this.inner.getVerification(userId, flowId) return this.inner.getVerification(userId, flowId)
return if (sas == null) {
null
} else {
SasVerification(this.inner, sas)
}
} }
} }

View file

@ -1,2 +1,208 @@
package org.matrix.android.sdk.internal.crypto /*
* Copyright (c) 2021 New Vector Ltd
*
* 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.internal.crypto
import android.os.Handler
import android.os.Looper
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import org.matrix.android.sdk.api.session.crypto.verification.CancelCode
import org.matrix.android.sdk.api.session.crypto.verification.EmojiRepresentation
import org.matrix.android.sdk.api.session.crypto.verification.SasVerificationTransaction
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.internal.crypto.verification.getEmojiForCode
import timber.log.Timber
import uniffi.olm.CryptoStoreErrorException
import uniffi.olm.OlmMachine
import uniffi.olm.OutgoingVerificationRequest
import uniffi.olm.Sas
internal class SasVerification(
private val machine: OlmMachine,
private var inner: Sas,
private val sender: RequestSender,
private val listeners: ArrayList<VerificationService.Listener>,
) :
SasVerificationTransaction {
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")
}
}
}
}
private fun refreshData() {
val sas = this.machine.getVerification(this.inner.otherUserId, this.inner.flowId)
if (sas != null) {
this.inner = sas
}
return
}
override val isIncoming: Boolean
get() {
return false
}
override var otherDeviceId: String?
get() {
return this.inner.otherDeviceId
}
set(value) {
if (value != null) {
this.inner.otherDeviceId = value
}
}
override val otherUserId: String
get() {
return this.inner.otherUserId
}
override var state: VerificationTxState
get() {
refreshData()
return when {
this.inner.isDone -> VerificationTxState.Verified
this.inner.haveWeConfirmed -> VerificationTxState.ShortCodeAccepted
this.inner.canBePresented -> VerificationTxState.ShortCodeReady
this.inner.isCancelled -> {
val rustCancelCode = this.inner.cancelCode
val cancelCode =
if (rustCancelCode != null) {
toCancelCode(rustCancelCode)
} else {
CancelCode.UnexpectedMessage
}
// TODO get byMe from the rust side
VerificationTxState.Cancelled(cancelCode, false)
}
else -> {
VerificationTxState.Started
}
}
}
set(v) {
this.stateField = v
}
override val transactionId: String
get() {
return this.inner.flowId
}
override fun cancel() {
TODO()
}
override fun cancel(code: CancelCode) {
TODO()
}
override fun shortCodeDoesNotMatch() {
TODO()
}
override fun isToDeviceTransport(): Boolean {
return false
}
override fun supportsDecimal(): Boolean {
// This is ignored anyways, throw it away?
// The spec also mandates that devices support
// at least decimal and the rust-sdk cancels if
// devices don't support it
return true
}
override fun supportsEmoji(): Boolean {
refreshData()
return this.inner.supportsEmoji
}
override fun userHasVerifiedShortCode() {
runBlocking {
when (val request = confirm()) {
is OutgoingVerificationRequest.ToDevice -> {
sender.sendToDevice(request.eventType, request.body)
}
else -> {}
}
}
refreshData()
dispatchTxUpdated()
}
fun isCanceled(): Boolean {
refreshData()
return this.inner.isCancelled
}
fun isDone(): Boolean {
refreshData()
return this.inner.isDone
}
fun timedOut(): Boolean {
refreshData()
return this.inner.timedOut
}
fun canBePresented(): Boolean {
refreshData()
return this.inner.canBePresented
}
fun accept(): OutgoingVerificationRequest? {
return this.machine.acceptSasVerification(this.inner.otherUserId, inner.flowId)
}
@Throws(CryptoStoreErrorException::class)
suspend fun confirm(): OutgoingVerificationRequest? =
withContext(Dispatchers.IO) {
machine.confirmVerification(inner.otherUserId, inner.flowId)
}
fun cancelHelper(): OutgoingVerificationRequest? {
return this.machine.cancelVerification(this.inner.otherUserId, inner.flowId)
}
override fun getEmojiCodeRepresentation(): List<EmojiRepresentation> {
val emojiIndex = this.machine.getEmojiIndex(this.inner.otherUserId, this.inner.flowId)
return emojiIndex?.map { getEmojiForCode(it) } ?: listOf()
}
override fun getDecimalCodeRepresentation(): String {
val decimals = this.machine.getDecimals(this.inner.otherUserId, this.inner.flowId)
return decimals?.joinToString(" ") ?: ""
}
}

View file

@ -18,9 +18,7 @@ package org.matrix.android.sdk.internal.crypto.verification
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
import kotlinx.coroutines.flow.flow
import javax.inject.Inject import javax.inject.Inject
import kotlin.collections.set
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import org.matrix.android.sdk.api.session.crypto.verification.PendingVerificationRequest import org.matrix.android.sdk.api.session.crypto.verification.PendingVerificationRequest
import org.matrix.android.sdk.api.session.crypto.verification.VerificationMethod import org.matrix.android.sdk.api.session.crypto.verification.VerificationMethod
@ -31,14 +29,12 @@ 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.events.model.toModel
import org.matrix.android.sdk.api.session.room.model.message.MessageType 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.OlmMachine
import org.matrix.android.sdk.internal.crypto.model.MXUsersDevicesMap import org.matrix.android.sdk.internal.crypto.RequestSender
import org.matrix.android.sdk.internal.crypto.SasVerification
import org.matrix.android.sdk.internal.crypto.model.rest.KeyVerificationDone 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.KeyVerificationKey
import org.matrix.android.sdk.internal.crypto.model.rest.KeyVerificationRequest import org.matrix.android.sdk.internal.crypto.model.rest.KeyVerificationRequest
import org.matrix.android.sdk.internal.crypto.tasks.SendToDeviceTask
import org.matrix.android.sdk.internal.di.MoshiProvider
import org.matrix.android.sdk.internal.session.SessionScope import org.matrix.android.sdk.internal.session.SessionScope
import org.matrix.android.sdk.internal.task.TaskExecutor
import timber.log.Timber import timber.log.Timber
import uniffi.olm.OutgoingVerificationRequest import uniffi.olm.OutgoingVerificationRequest
@ -46,9 +42,8 @@ import uniffi.olm.OutgoingVerificationRequest
internal class RustVerificationService internal class RustVerificationService
@Inject @Inject
constructor( constructor(
private val taskExecutor: TaskExecutor,
private val olmMachine: OlmMachine, private val olmMachine: OlmMachine,
private val sendToDeviceTask: SendToDeviceTask, private val requestSender: RequestSender,
) : DefaultVerificationTransaction.Listener, VerificationService { ) : DefaultVerificationTransaction.Listener, VerificationService {
private val uiHandler = Handler(Looper.getMainLooper()) private val uiHandler = Handler(Looper.getMainLooper())
@ -91,7 +86,7 @@ constructor(
} }
private fun dispatchRequestAdded(tx: PendingVerificationRequest) { private fun dispatchRequestAdded(tx: PendingVerificationRequest) {
Timber.v("## SAS dispatchRequestAdded txId:${tx.transactionId} ${tx}") Timber.v("## SAS dispatchRequestAdded txId:${tx.transactionId} $tx")
uiHandler.post { uiHandler.post {
listeners.forEach { listeners.forEach {
try { try {
@ -188,7 +183,9 @@ constructor(
otherUserId: String, otherUserId: String,
tid: String, tid: String,
): VerificationTransaction? { ): VerificationTransaction? {
return this.olmMachine.getVerification(otherUserId, tid) val verification = this.olmMachine.getVerification(otherUserId, tid) ?: return null
return SasVerification(this.olmMachine.inner(), verification, this.requestSender, this.listeners)
} }
override fun getExistingVerificationRequests( override fun getExistingVerificationRequests(
@ -204,13 +201,7 @@ constructor(
tid: String? tid: String?
): PendingVerificationRequest? { ): PendingVerificationRequest? {
return if (tid != null) { return if (tid != null) {
val request = this.olmMachine.getVerificationRequest(otherUserId, tid) olmMachine.getVerificationRequest(otherUserId, tid)?.toPendingVerificationRequest()
if (request != null) {
request.toPendingVerificationRequest()
} else {
null
}
} else { } else {
null null
} }
@ -236,9 +227,10 @@ constructor(
runBlocking { runBlocking {
val response = request?.startSasVerification() val response = request?.startSasVerification()
if (response != null) { if (response != null) {
sendRequest(response.first) sendRequest(response.request)
dispatchTxAdded(response.second) val sas = SasVerification(olmMachine.inner(), response.sas, requestSender, listeners)
response.second.transactionId dispatchTxAdded(sas)
sas.transactionId
} else { } else {
null null
} }
@ -315,7 +307,7 @@ constructor(
val request = this.olmMachine.getVerificationRequest(otherUserId, transactionId) val request = this.olmMachine.getVerificationRequest(otherUserId, transactionId)
return if (request != null) { return if (request != null) {
val outgoingRequest = request.accept_with_methods(methods) val outgoingRequest = request.acceptWithMethods(methods)
if (outgoingRequest != null) { if (outgoingRequest != null) {
runBlocking { sendRequest(outgoingRequest) } runBlocking { sendRequest(outgoingRequest) }
@ -335,16 +327,7 @@ constructor(
suspend fun sendRequest(request: OutgoingVerificationRequest) { suspend fun sendRequest(request: OutgoingVerificationRequest) {
when (request) { when (request) {
is OutgoingVerificationRequest.ToDevice -> { is OutgoingVerificationRequest.ToDevice -> {
val adapter = this.requestSender.sendToDevice(request.eventType, request.body)
MoshiProvider.providesMoshi()
.adapter<Map<String, HashMap<String, Any>>>(Map::class.java)
val body = adapter.fromJson(request.body)!!
val userMap = MXUsersDevicesMap<Any>()
userMap.join(body)
val sendToDeviceParams = SendToDeviceTask.Params(request.eventType, userMap)
sendToDeviceTask.execute(sendToDeviceParams)
} }
else -> {} else -> {}
} }