mirror of
https://github.com/element-hq/element-android
synced 2024-12-21 08:54:12 +03:00
crypto: Move the sendToDevice logic to a common class and use it for verifications
This commit is contained in:
parent
6a79d022c3
commit
aad18ebec7
4 changed files with 272 additions and 244 deletions
|
@ -20,25 +20,16 @@ import android.content.Context
|
|||
import androidx.annotation.VisibleForTesting
|
||||
import androidx.lifecycle.LiveData
|
||||
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.CoroutineScope
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.cancelChildren
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.joinAll
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.matrix.android.sdk.api.MatrixCallback
|
||||
import org.matrix.android.sdk.api.NoOpMatrixCallback
|
||||
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.RoomMemberContent
|
||||
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.DeviceTrustLevel
|
||||
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.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.MXUsersDevicesMap
|
||||
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.DevicesListResponse
|
||||
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.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.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.DownloadKeysForUsersTask
|
||||
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.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.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.di.DeviceId
|
||||
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.session.SessionScope
|
||||
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.DeviceOneTimeKeysCountSyncResponse
|
||||
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 uniffi.olm.Request
|
||||
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.
|
||||
|
@ -153,6 +164,8 @@ internal class DefaultCryptoService @Inject constructor(
|
|||
|
||||
private val isStarting = AtomicBoolean(false)
|
||||
private val isStarted = AtomicBoolean(false)
|
||||
private val sender = RequestSender(this.sendToDeviceTask)
|
||||
|
||||
private var olmMachine: OlmMachine? = null
|
||||
// The verification service.
|
||||
private var verificationService: RustVerificationService? = null
|
||||
|
@ -320,7 +333,7 @@ internal class DefaultCryptoService @Inject constructor(
|
|||
val machine = OlmMachine(userId, deviceId!!, dataDir, deviceObserver)
|
||||
this.olmMachine = machine
|
||||
this.verificationService =
|
||||
RustVerificationService(this.taskExecutor, machine, this.sendToDeviceTask)
|
||||
RustVerificationService(machine, this.sender)
|
||||
Timber.v(
|
||||
"## CRYPTO | Successfully started up an Olm machine for " +
|
||||
"${userId}, ${deviceId}, identity keys: ${this.olmMachine?.identityKeys()}")
|
||||
|
@ -373,7 +386,7 @@ internal class DefaultCryptoService @Inject constructor(
|
|||
// `ActiveSessionHolder` class in the `setActiveSession()` method. In
|
||||
// the `setActiveSession()` method we call 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
|
||||
// `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,
|
||||
sessionId: String,
|
||||
) {
|
||||
|
@ -791,18 +804,7 @@ internal class DefaultCryptoService @Inject constructor(
|
|||
}
|
||||
|
||||
private suspend fun sendToDevice(request: Request.ToDevice) {
|
||||
// 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 body = adapter.fromJson(request.body)!!
|
||||
|
||||
val userMap = MXUsersDevicesMap<Any>()
|
||||
userMap.join(body)
|
||||
|
||||
val sendToDeviceParams = SendToDeviceTask.Params(request.eventType, userMap)
|
||||
sendToDeviceTask.execute(sendToDeviceParams)
|
||||
this.sender.sendToDevice(request.eventType, request.body)
|
||||
olmMachine!!.markRequestAsSent(request.requestId, RequestType.TO_DEVICE, "{}")
|
||||
}
|
||||
|
||||
|
|
|
@ -27,13 +27,10 @@ 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.EmojiRepresentation
|
||||
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.ValidVerificationInfoRequest
|
||||
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.Event
|
||||
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_RECIPROCATE
|
||||
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.session.sync.model.DeviceListResponse
|
||||
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.Request
|
||||
import uniffi.olm.RequestType
|
||||
import uniffi.olm.Sas as InnerSas
|
||||
import uniffi.olm.Sas
|
||||
import uniffi.olm.StartSasResult
|
||||
import uniffi.olm.VerificationRequest as InnerRequest
|
||||
import uniffi.olm.setLogger
|
||||
|
@ -149,7 +145,7 @@ internal class VerificationRequest(
|
|||
return
|
||||
}
|
||||
|
||||
fun accept_with_methods(methods: List<VerificationMethod>): OutgoingVerificationRequest? {
|
||||
fun acceptWithMethods(methods: List<VerificationMethod>): OutgoingVerificationRequest? {
|
||||
val stringMethods: MutableList<String> =
|
||||
methods
|
||||
.map {
|
||||
|
@ -185,17 +181,11 @@ internal class VerificationRequest(
|
|||
return this.inner.isReady
|
||||
}
|
||||
|
||||
suspend fun startSasVerification(): Pair<OutgoingVerificationRequest, SasVerification>? {
|
||||
suspend fun startSasVerification(): StartSasResult? {
|
||||
refreshData()
|
||||
|
||||
return withContext(Dispatchers.IO) {
|
||||
val response = machine.startSasVerification(inner.otherUserId, inner.flowId)
|
||||
|
||||
if (response != null) {
|
||||
Pair(response.request, SasVerification(machine, response.sas))
|
||||
} else {
|
||||
null
|
||||
}
|
||||
machine.startSasVerification(inner.otherUserId, inner.flowId)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -280,7 +270,7 @@ internal class VerificationRequest(
|
|||
}
|
||||
}
|
||||
|
||||
private fun toCancelCode(cancelCode: RustCancelCode): CancelCode {
|
||||
internal fun toCancelCode(cancelCode: RustCancelCode): CancelCode {
|
||||
return when (cancelCode) {
|
||||
RustCancelCode.USER -> CancelCode.User
|
||||
RustCancelCode.TIMEOUT -> CancelCode.Timeout
|
||||
|
@ -290,162 +280,11 @@ private fun toCancelCode(cancelCode: RustCancelCode): CancelCode {
|
|||
RustCancelCode.KEY_MISMATCH -> CancelCode.MismatchedKeys
|
||||
RustCancelCode.USER_MISMATCH -> CancelCode.MismatchedKeys
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
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(
|
||||
user_id: String,
|
||||
device_id: String,
|
||||
|
@ -470,6 +309,10 @@ internal class OlmMachine(
|
|||
return this.inner.identityKeys()
|
||||
}
|
||||
|
||||
fun inner(): InnerMachine {
|
||||
return this.inner
|
||||
}
|
||||
|
||||
fun ownDevice(): CryptoDeviceInfo {
|
||||
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.
|
||||
*
|
||||
* @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
|
||||
* 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
|
||||
* 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
|
||||
* ```
|
||||
|
@ -629,7 +472,7 @@ internal class OlmMachine(
|
|||
*
|
||||
* 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.
|
||||
*
|
||||
* @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 */
|
||||
fun getVerification(userId: String, flowId: String): SasVerification? {
|
||||
val sas = this.inner.getVerification(userId, flowId)
|
||||
|
||||
return if (sas == null) {
|
||||
null
|
||||
} else {
|
||||
SasVerification(this.inner, sas)
|
||||
}
|
||||
fun getVerification(userId: String, flowId: String): Sas? {
|
||||
return this.inner.getVerification(userId, flowId)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,2 +1,208 @@
|
|||
/*
|
||||
* 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(" ") ?: ""
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,9 +18,7 @@ package org.matrix.android.sdk.internal.crypto.verification
|
|||
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import javax.inject.Inject
|
||||
import kotlin.collections.set
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.matrix.android.sdk.api.session.crypto.verification.PendingVerificationRequest
|
||||
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.room.model.message.MessageType
|
||||
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.KeyVerificationKey
|
||||
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.task.TaskExecutor
|
||||
import timber.log.Timber
|
||||
import uniffi.olm.OutgoingVerificationRequest
|
||||
|
||||
|
@ -46,9 +42,8 @@ import uniffi.olm.OutgoingVerificationRequest
|
|||
internal class RustVerificationService
|
||||
@Inject
|
||||
constructor(
|
||||
private val taskExecutor: TaskExecutor,
|
||||
private val olmMachine: OlmMachine,
|
||||
private val sendToDeviceTask: SendToDeviceTask,
|
||||
private val requestSender: RequestSender,
|
||||
) : DefaultVerificationTransaction.Listener, VerificationService {
|
||||
|
||||
private val uiHandler = Handler(Looper.getMainLooper())
|
||||
|
@ -91,7 +86,7 @@ constructor(
|
|||
}
|
||||
|
||||
private fun dispatchRequestAdded(tx: PendingVerificationRequest) {
|
||||
Timber.v("## SAS dispatchRequestAdded txId:${tx.transactionId} ${tx}")
|
||||
Timber.v("## SAS dispatchRequestAdded txId:${tx.transactionId} $tx")
|
||||
uiHandler.post {
|
||||
listeners.forEach {
|
||||
try {
|
||||
|
@ -188,7 +183,9 @@ constructor(
|
|||
otherUserId: String,
|
||||
tid: String,
|
||||
): 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(
|
||||
|
@ -204,13 +201,7 @@ constructor(
|
|||
tid: String?
|
||||
): PendingVerificationRequest? {
|
||||
return if (tid != null) {
|
||||
val request = this.olmMachine.getVerificationRequest(otherUserId, tid)
|
||||
|
||||
if (request != null) {
|
||||
request.toPendingVerificationRequest()
|
||||
} else {
|
||||
null
|
||||
}
|
||||
olmMachine.getVerificationRequest(otherUserId, tid)?.toPendingVerificationRequest()
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
@ -236,9 +227,10 @@ constructor(
|
|||
runBlocking {
|
||||
val response = request?.startSasVerification()
|
||||
if (response != null) {
|
||||
sendRequest(response.first)
|
||||
dispatchTxAdded(response.second)
|
||||
response.second.transactionId
|
||||
sendRequest(response.request)
|
||||
val sas = SasVerification(olmMachine.inner(), response.sas, requestSender, listeners)
|
||||
dispatchTxAdded(sas)
|
||||
sas.transactionId
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
@ -315,7 +307,7 @@ constructor(
|
|||
val request = this.olmMachine.getVerificationRequest(otherUserId, transactionId)
|
||||
|
||||
return if (request != null) {
|
||||
val outgoingRequest = request.accept_with_methods(methods)
|
||||
val outgoingRequest = request.acceptWithMethods(methods)
|
||||
|
||||
if (outgoingRequest != null) {
|
||||
runBlocking { sendRequest(outgoingRequest) }
|
||||
|
@ -335,16 +327,7 @@ constructor(
|
|||
suspend fun sendRequest(request: OutgoingVerificationRequest) {
|
||||
when (request) {
|
||||
is OutgoingVerificationRequest.ToDevice -> {
|
||||
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)
|
||||
this.requestSender.sendToDevice(request.eventType, request.body)
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue