Fix make verif scope as a child of crypto scope

This commit is contained in:
Valere 2022-04-29 09:42:56 +02:00
parent 0f06368027
commit de580cc997
2 changed files with 322 additions and 318 deletions

View File

@ -1,316 +1,316 @@
/* /*
* Copyright 2020 The Matrix.org Foundation C.I.C. * Copyright 2020 The Matrix.org Foundation C.I.C.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.matrix.android.sdk.internal.crypto.verification package org.matrix.android.sdk.internal.crypto.verification
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.asCoroutineDispatcher
import kotlinx.coroutines.asCoroutineDispatcher import kotlinx.coroutines.launch
import kotlinx.coroutines.launch 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.ValidVerificationInfoRequest
import org.matrix.android.sdk.api.session.crypto.verification.ValidVerificationInfoRequest import org.matrix.android.sdk.api.session.crypto.verification.VerificationTxState
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.session.events.model.EventType
import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.LocalEcho
import org.matrix.android.sdk.api.session.events.model.LocalEcho import org.matrix.android.sdk.api.session.events.model.RelationType
import org.matrix.android.sdk.api.session.events.model.RelationType import org.matrix.android.sdk.api.session.events.model.UnsignedData
import org.matrix.android.sdk.api.session.events.model.UnsignedData import org.matrix.android.sdk.api.session.events.model.toContent
import org.matrix.android.sdk.api.session.events.model.toContent import org.matrix.android.sdk.api.session.room.model.message.MessageVerificationAcceptContent
import org.matrix.android.sdk.api.session.room.model.message.MessageVerificationAcceptContent import org.matrix.android.sdk.api.session.room.model.message.MessageVerificationCancelContent
import org.matrix.android.sdk.api.session.room.model.message.MessageVerificationCancelContent import org.matrix.android.sdk.api.session.room.model.message.MessageVerificationDoneContent
import org.matrix.android.sdk.api.session.room.model.message.MessageVerificationDoneContent import org.matrix.android.sdk.api.session.room.model.message.MessageVerificationKeyContent
import org.matrix.android.sdk.api.session.room.model.message.MessageVerificationKeyContent import org.matrix.android.sdk.api.session.room.model.message.MessageVerificationMacContent
import org.matrix.android.sdk.api.session.room.model.message.MessageVerificationMacContent import org.matrix.android.sdk.api.session.room.model.message.MessageVerificationReadyContent
import org.matrix.android.sdk.api.session.room.model.message.MessageVerificationReadyContent import org.matrix.android.sdk.api.session.room.model.message.MessageVerificationRequestContent
import org.matrix.android.sdk.api.session.room.model.message.MessageVerificationRequestContent import org.matrix.android.sdk.api.session.room.model.message.MessageVerificationStartContent
import org.matrix.android.sdk.api.session.room.model.message.MessageVerificationStartContent import org.matrix.android.sdk.api.session.room.model.relation.RelationDefaultContent
import org.matrix.android.sdk.api.session.room.model.relation.RelationDefaultContent 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.tasks.SendVerificationMessageTask
import org.matrix.android.sdk.internal.crypto.tasks.SendVerificationMessageTask import org.matrix.android.sdk.internal.session.room.send.LocalEchoEventFactory
import org.matrix.android.sdk.internal.session.room.send.LocalEchoEventFactory import org.matrix.android.sdk.internal.task.SemaphoreCoroutineSequencer
import org.matrix.android.sdk.internal.task.SemaphoreCoroutineSequencer import timber.log.Timber
import timber.log.Timber import java.util.concurrent.Executors
import java.util.concurrent.Executors
internal class VerificationTransportRoomMessage(
internal class VerificationTransportRoomMessage( private val sendVerificationMessageTask: SendVerificationMessageTask,
private val sendVerificationMessageTask: SendVerificationMessageTask, private val userId: String,
private val userId: String, private val userDeviceId: String?,
private val userDeviceId: String?, private val roomId: String,
private val roomId: String, private val localEchoEventFactory: LocalEchoEventFactory,
private val localEchoEventFactory: LocalEchoEventFactory, private val tx: DefaultVerificationTransaction?,
private val tx: DefaultVerificationTransaction? cryptoCoroutineScope: CoroutineScope,
) : VerificationTransport { ) : VerificationTransport {
private val dispatcher = Executors.newSingleThreadExecutor().asCoroutineDispatcher() private val dispatcher = Executors.newSingleThreadExecutor().asCoroutineDispatcher()
private val verificationSenderScope = CoroutineScope(SupervisorJob() + dispatcher) private val verificationSenderScope = CoroutineScope(cryptoCoroutineScope.coroutineContext + dispatcher)
private val sequencer = SemaphoreCoroutineSequencer() private val sequencer = SemaphoreCoroutineSequencer()
override fun <T> sendToOther(type: String, override fun <T> sendToOther(type: String,
verificationInfo: VerificationInfo<T>, verificationInfo: VerificationInfo<T>,
nextState: VerificationTxState, nextState: VerificationTxState,
onErrorReason: CancelCode, onErrorReason: CancelCode,
onDone: (() -> Unit)?) { onDone: (() -> Unit)?) {
Timber.d("## SAS sending msg type $type") Timber.d("## SAS sending msg type $type")
Timber.v("## SAS sending msg info $verificationInfo") Timber.v("## SAS sending msg info $verificationInfo")
val event = createEventAndLocalEcho( val event = createEventAndLocalEcho(
type = type, type = type,
roomId = roomId, roomId = roomId,
content = verificationInfo.toEventContent()!! content = verificationInfo.toEventContent()!!
) )
verificationSenderScope.launch { verificationSenderScope.launch {
sequencer.post { sequencer.post {
try { try {
val params = SendVerificationMessageTask.Params(event) val params = SendVerificationMessageTask.Params(event)
sendVerificationMessageTask.executeRetry(params, 5) sendVerificationMessageTask.executeRetry(params, 5)
// Do I need to update local echo state to sent? // Do I need to update local echo state to sent?
if (onDone != null) { if (onDone != null) {
onDone() onDone()
} else { } else {
tx?.state = nextState tx?.state = nextState
} }
} catch (failure: Throwable) { } catch (failure: Throwable) {
tx?.cancel(onErrorReason) tx?.cancel(onErrorReason)
} }
} }
} }
} }
override fun sendVerificationRequest(supportedMethods: List<String>, override fun sendVerificationRequest(supportedMethods: List<String>,
localId: String, localId: String,
otherUserId: String, otherUserId: String,
roomId: String?, roomId: String?,
toDevices: List<String>?, toDevices: List<String>?,
callback: (String?, ValidVerificationInfoRequest?) -> Unit) { callback: (String?, ValidVerificationInfoRequest?) -> Unit) {
Timber.d("## SAS sending verification request with supported methods: $supportedMethods") Timber.d("## SAS sending verification request with supported methods: $supportedMethods")
// This transport requires a room // This transport requires a room
requireNotNull(roomId) requireNotNull(roomId)
val validInfo = ValidVerificationInfoRequest( val validInfo = ValidVerificationInfoRequest(
transactionId = "", transactionId = "",
fromDevice = userDeviceId ?: "", fromDevice = userDeviceId ?: "",
methods = supportedMethods, methods = supportedMethods,
timestamp = System.currentTimeMillis() timestamp = System.currentTimeMillis()
) )
val info = MessageVerificationRequestContent( val info = MessageVerificationRequestContent(
body = "$userId is requesting to verify your key, but your client does not support in-chat key verification." + body = "$userId is requesting to verify your key, but your client does not support in-chat key verification." +
" You will need to use legacy key verification to verify keys.", " You will need to use legacy key verification to verify keys.",
fromDevice = validInfo.fromDevice, fromDevice = validInfo.fromDevice,
toUserId = otherUserId, toUserId = otherUserId,
timestamp = validInfo.timestamp, timestamp = validInfo.timestamp,
methods = validInfo.methods methods = validInfo.methods
) )
val content = info.toContent() val content = info.toContent()
val event = createEventAndLocalEcho( val event = createEventAndLocalEcho(
localId = localId, localId = localId,
type = EventType.MESSAGE, type = EventType.MESSAGE,
roomId = roomId, roomId = roomId,
content = content content = content
) )
verificationSenderScope.launch { verificationSenderScope.launch {
val params = SendVerificationMessageTask.Params(event) val params = SendVerificationMessageTask.Params(event)
sequencer.post { sequencer.post {
try { try {
val eventId = sendVerificationMessageTask.executeRetry(params, 5) val eventId = sendVerificationMessageTask.executeRetry(params, 5)
// Do I need to update local echo state to sent? // Do I need to update local echo state to sent?
callback(eventId, validInfo) callback(eventId, validInfo)
} catch (failure: Throwable) { } catch (failure: Throwable) {
callback(null, null) callback(null, null)
} }
} }
} }
} }
override fun cancelTransaction(transactionId: String, otherUserId: String, otherUserDeviceId: String?, code: CancelCode) { override fun cancelTransaction(transactionId: String, otherUserId: String, otherUserDeviceId: String?, code: CancelCode) {
Timber.d("## SAS canceling transaction $transactionId for reason $code") Timber.d("## SAS canceling transaction $transactionId for reason $code")
val event = createEventAndLocalEcho( val event = createEventAndLocalEcho(
type = EventType.KEY_VERIFICATION_CANCEL, type = EventType.KEY_VERIFICATION_CANCEL,
roomId = roomId, roomId = roomId,
content = MessageVerificationCancelContent.create(transactionId, code).toContent() content = MessageVerificationCancelContent.create(transactionId, code).toContent()
) )
verificationSenderScope.launch { verificationSenderScope.launch {
sequencer.post { sequencer.post {
try { try {
val params = SendVerificationMessageTask.Params(event) val params = SendVerificationMessageTask.Params(event)
sendVerificationMessageTask.executeRetry(params, 5) sendVerificationMessageTask.executeRetry(params, 5)
} catch (failure: Throwable) { } catch (failure: Throwable) {
Timber.w(failure, "Failed to cancel verification transaction") Timber.w(failure, "Failed to cancel verification transaction")
} }
} }
} }
} }
override fun done(transactionId: String, override fun done(transactionId: String,
onDone: (() -> Unit)?) { onDone: (() -> Unit)?) {
Timber.d("## SAS sending done for $transactionId") Timber.d("## SAS sending done for $transactionId")
val event = createEventAndLocalEcho( val event = createEventAndLocalEcho(
type = EventType.KEY_VERIFICATION_DONE, type = EventType.KEY_VERIFICATION_DONE,
roomId = roomId, roomId = roomId,
content = MessageVerificationDoneContent( content = MessageVerificationDoneContent(
relatesTo = RelationDefaultContent( relatesTo = RelationDefaultContent(
RelationType.REFERENCE, RelationType.REFERENCE,
transactionId transactionId
) )
).toContent() ).toContent()
) )
verificationSenderScope.launch { verificationSenderScope.launch {
sequencer.post { sequencer.post {
try { try {
val params = SendVerificationMessageTask.Params(event) val params = SendVerificationMessageTask.Params(event)
sendVerificationMessageTask.executeRetry(params, 5) sendVerificationMessageTask.executeRetry(params, 5)
} catch (failure: Throwable) { } catch (failure: Throwable) {
Timber.w(failure, "Failed to complete (done) verification") Timber.w(failure, "Failed to complete (done) verification")
// should we call onDone? // should we call onDone?
} finally { } finally {
onDone?.invoke() onDone?.invoke()
} }
} }
} }
// val workerParams = WorkerParamsFactory.toData(SendVerificationMessageWorker.Params( // val workerParams = WorkerParamsFactory.toData(SendVerificationMessageWorker.Params(
// sessionId = sessionId, // sessionId = sessionId,
// eventId = event.eventId ?: "" // eventId = event.eventId ?: ""
// )) // ))
// val enqueueInfo = enqueueSendWork(workerParams) // val enqueueInfo = enqueueSendWork(workerParams)
// //
// val workLiveData = workManagerProvider.workManager // val workLiveData = workManagerProvider.workManager
// .getWorkInfosForUniqueWorkLiveData(uniqueQueueName()) // .getWorkInfosForUniqueWorkLiveData(uniqueQueueName())
// val observer = object : Observer<List<WorkInfo>> { // val observer = object : Observer<List<WorkInfo>> {
// override fun onChanged(workInfoList: List<WorkInfo>?) { // override fun onChanged(workInfoList: List<WorkInfo>?) {
// workInfoList // workInfoList
// ?.filter { it.state == WorkInfo.State.SUCCEEDED } // ?.filter { it.state == WorkInfo.State.SUCCEEDED }
// ?.firstOrNull { it.id == enqueueInfo.second } // ?.firstOrNull { it.id == enqueueInfo.second }
// ?.let { _ -> // ?.let { _ ->
// onDone?.invoke() // onDone?.invoke()
// workLiveData.removeObserver(this) // workLiveData.removeObserver(this)
// } // }
// } // }
// } // }
// //
// // TODO listen to DB to get synced info // // TODO listen to DB to get synced info
// coroutineScope.launch(Dispatchers.Main) { // coroutineScope.launch(Dispatchers.Main) {
// workLiveData.observeForever(observer) // workLiveData.observeForever(observer)
// } // }
} }
// private fun enqueueSendWork(workerParams: Data): Pair<Operation, UUID> { // private fun enqueueSendWork(workerParams: Data): Pair<Operation, UUID> {
// val workRequest = workManagerProvider.matrixOneTimeWorkRequestBuilder<SendVerificationMessageWorker>() // val workRequest = workManagerProvider.matrixOneTimeWorkRequestBuilder<SendVerificationMessageWorker>()
// .setConstraints(WorkManagerProvider.workConstraints) // .setConstraints(WorkManagerProvider.workConstraints)
// .setInputData(workerParams) // .setInputData(workerParams)
// .setBackoffCriteria(BackoffPolicy.LINEAR, WorkManagerProvider.BACKOFF_DELAY_MILLIS, TimeUnit.MILLISECONDS) // .setBackoffCriteria(BackoffPolicy.LINEAR, WorkManagerProvider.BACKOFF_DELAY_MILLIS, TimeUnit.MILLISECONDS)
// .build() // .build()
// return workManagerProvider.workManager // return workManagerProvider.workManager
// .beginUniqueWork(uniqueQueueName(), ExistingWorkPolicy.APPEND_OR_REPLACE, workRequest) // .beginUniqueWork(uniqueQueueName(), ExistingWorkPolicy.APPEND_OR_REPLACE, workRequest)
// .enqueue() to workRequest.id // .enqueue() to workRequest.id
// } // }
// private fun uniqueQueueName() = "${roomId}_VerificationWork" // private fun uniqueQueueName() = "${roomId}_VerificationWork"
override fun createAccept(tid: String, override fun createAccept(tid: String,
keyAgreementProtocol: String, keyAgreementProtocol: String,
hash: String, hash: String,
commitment: String, commitment: String,
messageAuthenticationCode: String, messageAuthenticationCode: String,
shortAuthenticationStrings: List<String>): VerificationInfoAccept = shortAuthenticationStrings: List<String>): VerificationInfoAccept =
MessageVerificationAcceptContent.create( MessageVerificationAcceptContent.create(
tid, tid,
keyAgreementProtocol, keyAgreementProtocol,
hash, hash,
commitment, commitment,
messageAuthenticationCode, messageAuthenticationCode,
shortAuthenticationStrings shortAuthenticationStrings
) )
override fun createKey(tid: String, pubKey: String): VerificationInfoKey = MessageVerificationKeyContent.create(tid, pubKey) override fun createKey(tid: String, pubKey: String): VerificationInfoKey = MessageVerificationKeyContent.create(tid, pubKey)
override fun createMac(tid: String, mac: Map<String, String>, keys: String) = MessageVerificationMacContent.create(tid, mac, keys) override fun createMac(tid: String, mac: Map<String, String>, keys: String) = MessageVerificationMacContent.create(tid, mac, keys)
override fun createStartForSas(fromDevice: String, override fun createStartForSas(fromDevice: String,
transactionId: String, transactionId: String,
keyAgreementProtocols: List<String>, keyAgreementProtocols: List<String>,
hashes: List<String>, hashes: List<String>,
messageAuthenticationCodes: List<String>, messageAuthenticationCodes: List<String>,
shortAuthenticationStrings: List<String>): VerificationInfoStart { shortAuthenticationStrings: List<String>): VerificationInfoStart {
return MessageVerificationStartContent( return MessageVerificationStartContent(
fromDevice, fromDevice,
hashes, hashes,
keyAgreementProtocols, keyAgreementProtocols,
messageAuthenticationCodes, messageAuthenticationCodes,
shortAuthenticationStrings, shortAuthenticationStrings,
VERIFICATION_METHOD_SAS, VERIFICATION_METHOD_SAS,
RelationDefaultContent( RelationDefaultContent(
type = RelationType.REFERENCE, type = RelationType.REFERENCE,
eventId = transactionId eventId = transactionId
), ),
null null
) )
} }
override fun createStartForQrCode(fromDevice: String, override fun createStartForQrCode(fromDevice: String,
transactionId: String, transactionId: String,
sharedSecret: String): VerificationInfoStart { sharedSecret: String): VerificationInfoStart {
return MessageVerificationStartContent( return MessageVerificationStartContent(
fromDevice, fromDevice,
null, null,
null, null,
null, null,
null, null,
VERIFICATION_METHOD_RECIPROCATE, VERIFICATION_METHOD_RECIPROCATE,
RelationDefaultContent( RelationDefaultContent(
type = RelationType.REFERENCE, type = RelationType.REFERENCE,
eventId = transactionId eventId = transactionId
), ),
sharedSecret sharedSecret
) )
} }
override fun createReady(tid: String, fromDevice: String, methods: List<String>): VerificationInfoReady { override fun createReady(tid: String, fromDevice: String, methods: List<String>): VerificationInfoReady {
return MessageVerificationReadyContent( return MessageVerificationReadyContent(
fromDevice = fromDevice, fromDevice = fromDevice,
relatesTo = RelationDefaultContent( relatesTo = RelationDefaultContent(
type = RelationType.REFERENCE, type = RelationType.REFERENCE,
eventId = tid eventId = tid
), ),
methods = methods methods = methods
) )
} }
private fun createEventAndLocalEcho(localId: String = LocalEcho.createLocalEchoId(), type: String, roomId: String, content: Content): Event { private fun createEventAndLocalEcho(localId: String = LocalEcho.createLocalEchoId(), type: String, roomId: String, content: Content): Event {
return Event( return Event(
roomId = roomId, roomId = roomId,
originServerTs = System.currentTimeMillis(), originServerTs = System.currentTimeMillis(),
senderId = userId, senderId = userId,
eventId = localId, eventId = localId,
type = type, type = type,
content = content, content = content,
unsignedData = UnsignedData(age = null, transactionId = localId) unsignedData = UnsignedData(age = null, transactionId = localId)
).also { ).also {
localEchoEventFactory.createLocalEcho(it) localEchoEventFactory.createLocalEcho(it)
} }
} }
override fun sendVerificationReady(keyReq: VerificationInfoReady, override fun sendVerificationReady(keyReq: VerificationInfoReady,
otherUserId: String, otherUserId: String,
otherDeviceId: String?, otherDeviceId: String?,
callback: (() -> Unit)?) { callback: (() -> Unit)?) {
// Not applicable (send event is called directly) // Not applicable (send event is called directly)
Timber.w("## SAS ignored verification ready with methods: ${keyReq.methods}") Timber.w("## SAS ignored verification ready with methods: ${keyReq.methods}")
} }
} }

View File

@ -16,6 +16,7 @@
package org.matrix.android.sdk.internal.crypto.verification package org.matrix.android.sdk.internal.crypto.verification
import kotlinx.coroutines.CoroutineScope
import org.matrix.android.sdk.internal.crypto.tasks.SendVerificationMessageTask import org.matrix.android.sdk.internal.crypto.tasks.SendVerificationMessageTask
import org.matrix.android.sdk.internal.di.DeviceId import org.matrix.android.sdk.internal.di.DeviceId
import org.matrix.android.sdk.internal.di.UserId import org.matrix.android.sdk.internal.di.UserId
@ -28,7 +29,8 @@ internal class VerificationTransportRoomMessageFactory @Inject constructor(
private val userId: String, private val userId: String,
@DeviceId @DeviceId
private val deviceId: String?, private val deviceId: String?,
private val localEchoEventFactory: LocalEchoEventFactory private val localEchoEventFactory: LocalEchoEventFactory,
private val cryptoCoroutineScope: CoroutineScope,
) { ) {
fun createTransport(roomId: String, tx: DefaultVerificationTransaction?): VerificationTransportRoomMessage { fun createTransport(roomId: String, tx: DefaultVerificationTransaction?): VerificationTransportRoomMessage {
@ -38,6 +40,8 @@ internal class VerificationTransportRoomMessageFactory @Inject constructor(
userDeviceId = deviceId, userDeviceId = deviceId,
roomId = roomId, roomId = roomId,
localEchoEventFactory = localEchoEventFactory, localEchoEventFactory = localEchoEventFactory,
tx = tx) tx = tx,
cryptoCoroutineScope
)
} }
} }