From 6b0ab10231a3f50607a2bbd02c90e2e4ee3f74a6 Mon Sep 17 00:00:00 2001 From: ganfra Date: Wed, 5 Jun 2019 22:18:16 +0200 Subject: [PATCH] Crypto: continue threading rework. WIP to shash --- .../api/session/crypto/MXCryptoError.kt | 3 +- .../android/internal/crypto/CryptoManager.kt | 158 +++---- .../android/internal/crypto/CryptoModule.kt | 4 +- .../crypto/IncomingRoomKeyRequestManager.kt | 79 ++-- .../internal/crypto/OneTimeKeysUploader.kt | 81 ++-- .../internal/crypto/OutgoingRoomKeyRequest.kt | 18 +- .../crypto/OutgoingRoomKeyRequestManager.kt | 54 ++- .../internal/crypto/RoomDecryptorProvider.kt | 48 +- .../EnsureOlmSessionsForDevicesAction.kt | 93 ++-- .../EnsureOlmSessionsForUsersAction.kt | 11 +- .../crypto/algorithms/IMXDecrypting.kt | 2 +- .../crypto/algorithms/IMXEncrypting.kt | 5 +- .../algorithms/megolm/MXMegolmDecryption.kt | 136 +++--- .../megolm/MXMegolmDecryptionFactory.kt | 4 +- .../algorithms/megolm/MXMegolmEncryption.kt | 432 +++++------------- .../megolm/MXMegolmEncryptionFactory.kt | 1 - .../megolm/MXOutboundSessionInfo.kt | 18 +- .../crypto/algorithms/olm/MXOlmDecryption.kt | 2 +- .../crypto/algorithms/olm/MXOlmEncryption.kt | 52 +-- .../algorithms/olm/MXOlmEncryptionFactory.kt | 1 - .../crypto/store/db/RealmCryptoStore.kt | 24 +- .../db/model/OutgoingRoomKeyRequestEntity.kt | 2 +- .../android/internal/di/MatrixModule.kt | 11 +- .../room/timeline/TimelineEventFactory.kt | 5 + .../internal/session/sync/RoomSyncHandler.kt | 1 - .../session/sync/SyncResponseHandler.kt | 4 +- .../timeline/factory/EncryptedItemFactory.kt | 23 +- 27 files changed, 472 insertions(+), 800 deletions(-) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/MXCryptoError.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/MXCryptoError.kt index 3e4f70287d..c4ffd8bdb8 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/MXCryptoError.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/MXCryptoError.kt @@ -41,7 +41,7 @@ class MXCryptoError(var code: String, * @return true if the current error is an olm one. */ val isOlmError: Boolean - get() = TextUtils.equals(OLM_ERROR_CODE, code) + get() = OLM_ERROR_CODE == code /** @@ -98,6 +98,7 @@ class MXCryptoError(var code: String, const val MISSING_PROPERTY_ERROR_CODE = "MISSING_PROPERTY" const val OLM_ERROR_CODE = "OLM_ERROR_CODE" const val UNKNOWN_DEVICES_CODE = "UNKNOWN_DEVICES_CODE" + const val UNKNOWN_MESSAGE_INDEX = "UNKNOWN_MESSAGE_INDEX" /** * short error reasons diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/CryptoManager.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/CryptoManager.kt index 84885e9e51..621f846309 100755 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/CryptoManager.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/CryptoManager.kt @@ -19,10 +19,8 @@ package im.vector.matrix.android.internal.crypto import android.content.Context -import android.os.Handler import android.text.TextUtils import arrow.core.Try -import arrow.instances.`try`.applicativeError.handleError import com.zhuinden.monarchy.Monarchy import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.auth.data.Credentials @@ -70,10 +68,7 @@ import im.vector.matrix.android.internal.session.sync.model.SyncResponse import im.vector.matrix.android.internal.task.TaskExecutor import im.vector.matrix.android.internal.task.configureWith import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch -import kotlinx.coroutines.runBlocking -import kotlinx.coroutines.withContext +import kotlinx.coroutines.* import org.matrix.olm.OlmManager import timber.log.Timber import java.util.* @@ -245,56 +240,41 @@ internal class CryptoManager( * @param isInitialSync true if it starts from an initial sync */ fun start(isInitialSync: Boolean) { - if (isStarting.get()) { + CoroutineScope(coroutineDispatchers.crypto).launch { + internalStart(isInitialSync) + } + } + + private suspend fun internalStart(isInitialSync: Boolean) { + if (isStarted.get() || isStarting.get()) { return } - - // do not start if there is not network connection - // TODO - //if (null != mNetworkConnectivityReceiver && !mNetworkConnectivityReceiver!!.isConnected()) { - // // wait that a valid network connection is retrieved - // mNetworkConnectivityReceiver!!.removeEventListener(mNetworkListener) - // mNetworkConnectivityReceiver!!.addEventListener(mNetworkListener) - // return - //} - isStarting.set(true) // Open the store cryptoStore.open() - CoroutineScope(coroutineDispatchers.crypto).launch { - uploadDeviceKeys() - .flatMap { - oneTimeKeysUploader.maybeUploadOneTimeKeys() - } - .handleError { - Handler().postDelayed( - { - if (!isStarted()) { - isStarting.set(false) - start(isInitialSync) - } - }, 1000 - ) - } - .fold( - { - Timber.e("Start failed: $it") - }, - { - isStarting.set(false) - isStarted.set(true) - outgoingRoomKeyRequestManager.start() - keysBackup.checkAndStartKeysBackup() - if (isInitialSync) { - // refresh the devices list for each known room members - deviceListManager.invalidateAllDeviceLists() - deviceListManager.refreshOutdatedDeviceLists() - } else { - incomingRoomKeyRequestManager.processReceivedRoomKeyRequests() - } + uploadDeviceKeys() + .flatMap { oneTimeKeysUploader.maybeUploadOneTimeKeys() } + .fold( + { + Timber.e("Start failed: $it") + delay(1000) + isStarting.set(false) + internalStart(isInitialSync) + }, + { + isStarting.set(false) + isStarted.set(true) + outgoingRoomKeyRequestManager.start() + keysBackup.checkAndStartKeysBackup() + if (isInitialSync) { + // refresh the devices list for each known room members + deviceListManager.invalidateAllDeviceLists() + deviceListManager.refreshOutdatedDeviceLists() + } else { + incomingRoomKeyRequestManager.processReceivedRoomKeyRequests() } - ) - } + } + ) } /** @@ -447,7 +427,10 @@ internal class CryptoManager( * @param membersId list of members to start tracking their devices * @return true if the operation succeeds. */ - private suspend fun setEncryptionInRoom(roomId: String, algorithm: String?, inhibitDeviceQuery: Boolean, membersId: List): Boolean { + private suspend fun setEncryptionInRoom(roomId: String, + algorithm: String?, + inhibitDeviceQuery: Boolean, + membersId: List): Boolean { // If we already have encryption in this room, we should ignore this event // (for now at least. Maybe we should alert the user somehow?) val existingAlgorithm = cryptoStore.getRoomAlgorithm(roomId) @@ -555,13 +538,11 @@ internal class CryptoManager( eventType: String, roomId: String, callback: MatrixCallback) { - // wait that the crypto is really started - if (!isStarted()) { - Timber.v("## encryptEventContent() : wait after e2e init") - start(false) - return - } CoroutineScope(coroutineDispatchers.crypto).launch { + if (!isStarted()) { + Timber.v("## encryptEventContent() : wait after e2e init") + internalStart(false) + } val userIds = getRoomUserIds(roomId) var alg = synchronized(roomEncryptors) { roomEncryptors[roomId] @@ -580,24 +561,21 @@ internal class CryptoManager( if (safeAlgorithm != null) { val t0 = System.currentTimeMillis() Timber.v("## encryptEventContent() starts") - safeAlgorithm.encryptEventContent(eventContent, eventType, userIds, object : MatrixCallback { - override fun onSuccess(data: Content) { - Timber.v("## encryptEventContent() : succeeds after " + (System.currentTimeMillis() - t0) + " ms") - callback.onSuccess(MXEncryptEventContentResult(data, EventType.ENCRYPTED)) - } - - override fun onFailure(failure: Throwable) { - callback.onFailure(failure) - } - }) + safeAlgorithm.encryptEventContent(eventContent, eventType, userIds) + .fold( + { callback.onFailure(it) }, + { + Timber.v("## encryptEventContent() : succeeds after " + (System.currentTimeMillis() - t0) + " ms") + callback.onSuccess(MXEncryptEventContentResult(it, EventType.ENCRYPTED)) + } + ) } else { val algorithm = getEncryptionAlgorithm(roomId) val reason = String.format(MXCryptoError.UNABLE_TO_ENCRYPT_REASON, - algorithm ?: MXCryptoError.NO_MORE_ALGORITHM_REASON) + algorithm ?: MXCryptoError.NO_MORE_ALGORITHM_REASON) Timber.e("## encryptEventContent() : $reason") - callback.onFailure(Failure.CryptoError(MXCryptoError(MXCryptoError.UNABLE_TO_ENCRYPT_ERROR_CODE, - MXCryptoError.UNABLE_TO_ENCRYPT, reason))) + MXCryptoError.UNABLE_TO_ENCRYPT, reason))) } } } @@ -616,14 +594,14 @@ internal class CryptoManager( Timber.e("## decryptEvent : empty event content") return null } - val alg = roomDecryptorProvider.getOrCreateRoomDecryptor(event.roomId, eventContent["algorithm"] as String) - if (alg == null) { - val reason = String.format(MXCryptoError.UNABLE_TO_DECRYPT_REASON, event.eventId, eventContent["algorithm"] as String) - Timber.e("## decryptEvent() : $reason") - throw MXDecryptionException(MXCryptoError(MXCryptoError.UNABLE_TO_DECRYPT_ERROR_CODE, MXCryptoError.UNABLE_TO_DECRYPT, reason)) - } else { - return runBlocking { - withContext(coroutineDispatchers.crypto) { + return runBlocking { + withContext(coroutineDispatchers.crypto) { + val alg = roomDecryptorProvider.getOrCreateRoomDecryptor(event.roomId, eventContent["algorithm"] as String) + if (alg == null) { + val reason = String.format(MXCryptoError.UNABLE_TO_DECRYPT_REASON, event.eventId, eventContent["algorithm"] as String) + Timber.e("## decryptEvent() : $reason") + throw MXDecryptionException(MXCryptoError(MXCryptoError.UNABLE_TO_DECRYPT_ERROR_CODE, MXCryptoError.UNABLE_TO_DECRYPT, reason)) + } else { alg.decryptEvent(event, timeline) } } @@ -661,19 +639,15 @@ internal class CryptoManager( */ private fun onRoomKeyEvent(event: Event) { val roomKeyContent = event.getClearContent().toModel()!! - if (TextUtils.isEmpty(roomKeyContent.roomId) || TextUtils.isEmpty(roomKeyContent.algorithm)) { Timber.e("## onRoomKeyEvent() : missing fields") return } - val alg = roomDecryptorProvider.getOrCreateRoomDecryptor(roomKeyContent.roomId, roomKeyContent.algorithm) - - if (null == alg) { + if (alg == null) { Timber.e("## onRoomKeyEvent() : Unable to handle keys for " + roomKeyContent.algorithm) return } - alg.onRoomKeyEvent(event, keysBackup) } @@ -699,7 +673,7 @@ internal class CryptoManager( monarchy.doWithRealm { realm -> // Check whether the event content must be encrypted for the invited members. val encryptForInvitedMembers = isEncryptionEnabledForInvitedUser() - && shouldEncryptForInvitedMembers(roomId) + && shouldEncryptForInvitedMembers(roomId) userIds = if (encryptForInvitedMembers) { RoomMembers(realm, roomId).getActiveRoomMemberIds() @@ -734,8 +708,8 @@ internal class CryptoManager( // make sure we are tracking the deviceList for this user. deviceListManager.startTrackingDeviceList(Arrays.asList(userId)) } else if (membership == Membership.INVITE - && shouldEncryptForInvitedMembers(roomId) - && cryptoConfig.mEnableEncryptionForInvitedMembers) { + && shouldEncryptForInvitedMembers(roomId) + && cryptoConfig.mEnableEncryptionForInvitedMembers) { // track the deviceList for this invited user. // Caution: there's a big edge case here in that federated servers do not // know what other servers are in the room at the time they've been invited. @@ -898,7 +872,7 @@ internal class CryptoManager( // trigger an an unknown devices exception callback.onFailure( Failure.CryptoError(MXCryptoError(MXCryptoError.UNKNOWN_DEVICES_CODE, - MXCryptoError.UNABLE_TO_ENCRYPT, MXCryptoError.UNKNOWN_DEVICES_REASON, unknownDevices))) + MXCryptoError.UNABLE_TO_ENCRYPT, MXCryptoError.UNKNOWN_DEVICES_REASON, unknownDevices))) } } ) @@ -936,7 +910,7 @@ internal class CryptoManager( * @param roomId the room id * @return true if the client should encrypt messages only for the verified devices. */ - // TODO add this info in CryptoRoomEntity? +// TODO add this info in CryptoRoomEntity? override fun isRoomBlacklistUnverifiedDevices(roomId: String?): Boolean { return if (null != roomId) { cryptoStore.getRoomsListBlacklistUnverifiedDevices().contains(roomId) @@ -984,7 +958,7 @@ internal class CryptoManager( setRoomBlacklistUnverifiedDevices(roomId, false) } - // TODO Check if this method is still necessary +// TODO Check if this method is still necessary /** * Cancel any earlier room key request * @@ -1057,9 +1031,9 @@ internal class CryptoManager( return unknownDevices } - /* ========================================================================================== - * DEBUG INFO - * ========================================================================================== */ +/* ========================================================================================== + * DEBUG INFO + * ========================================================================================== */ override fun toString(): String { return "CryptoManager of " + credentials.userId + " (" + credentials.deviceId + ")" diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/CryptoModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/CryptoModule.kt index 6a55914fe9..3b729eca01 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/CryptoModule.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/CryptoModule.kt @@ -169,7 +169,7 @@ internal class CryptoModule { } scope(DefaultSession.SCOPE) { - EnsureOlmSessionsForDevicesAction(get(), get(), get(), get()) + EnsureOlmSessionsForDevicesAction(get(), get()) } scope(DefaultSession.SCOPE) { @@ -192,7 +192,7 @@ internal class CryptoModule { // Factories scope(DefaultSession.SCOPE) { MXMegolmDecryptionFactory( - get(), get(), get(), get(), get(), get(), get(), get(), get(), get() + get(), get(), get(), get(), get(), get(), get(), get(), get() ) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingRoomKeyRequestManager.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingRoomKeyRequestManager.kt index 7444eaaf33..de53f34f7d 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingRoomKeyRequestManager.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingRoomKeyRequestManager.kt @@ -27,21 +27,21 @@ import timber.log.Timber import java.util.* internal class IncomingRoomKeyRequestManager( - private val mCredentials: Credentials, - private val mCryptoStore: IMXCryptoStore, - private val mRoomDecryptorProvider: RoomDecryptorProvider) { + private val credentials: Credentials, + private val cryptoStore: IMXCryptoStore, + private val roomDecryptorProvider: RoomDecryptorProvider) { // list of IncomingRoomKeyRequests/IncomingRoomKeyRequestCancellations // we received in the current sync. - private val mReceivedRoomKeyRequests = ArrayList() - private val mReceivedRoomKeyRequestCancellations = ArrayList() + private val receivedRoomKeyRequests = ArrayList() + private val receivedRoomKeyRequestCancellations = ArrayList() // the listeners - private val mRoomKeysRequestListeners: MutableSet = HashSet() + private val roomKeysRequestListeners: MutableSet = HashSet() init { - mReceivedRoomKeyRequests.addAll(mCryptoStore.getPendingIncomingRoomKeyRequests()) + receivedRoomKeyRequests.addAll(cryptoStore.getPendingIncomingRoomKeyRequests()) } /** @@ -52,13 +52,12 @@ internal class IncomingRoomKeyRequestManager( */ fun onRoomKeyRequestEvent(event: Event) { val roomKeyShare = event.getClearContent().toModel() - when (roomKeyShare?.action) { - RoomKeyShare.ACTION_SHARE_REQUEST -> synchronized(mReceivedRoomKeyRequests) { - mReceivedRoomKeyRequests.add(IncomingRoomKeyRequest(event)) + RoomKeyShare.ACTION_SHARE_REQUEST -> synchronized(receivedRoomKeyRequests) { + receivedRoomKeyRequests.add(IncomingRoomKeyRequest(event)) } - RoomKeyShare.ACTION_SHARE_CANCELLATION -> synchronized(mReceivedRoomKeyRequestCancellations) { - mReceivedRoomKeyRequestCancellations.add(IncomingRoomKeyRequestCancellation(event)) + RoomKeyShare.ACTION_SHARE_CANCELLATION -> synchronized(receivedRoomKeyRequestCancellations) { + receivedRoomKeyRequestCancellations.add(IncomingRoomKeyRequestCancellation(event)) } else -> Timber.e("## onRoomKeyRequestEvent() : unsupported action " + roomKeyShare?.action) } @@ -71,10 +70,10 @@ internal class IncomingRoomKeyRequestManager( fun processReceivedRoomKeyRequests() { var receivedRoomKeyRequests: List? = null - synchronized(mReceivedRoomKeyRequests) { - if (!mReceivedRoomKeyRequests.isEmpty()) { - receivedRoomKeyRequests = ArrayList(mReceivedRoomKeyRequests) - mReceivedRoomKeyRequests.clear() + synchronized(this.receivedRoomKeyRequests) { + if (this.receivedRoomKeyRequests.isNotEmpty()) { + receivedRoomKeyRequests = ArrayList(this.receivedRoomKeyRequests) + this.receivedRoomKeyRequests.clear() } } @@ -88,7 +87,7 @@ internal class IncomingRoomKeyRequestManager( Timber.v("m.room_key_request from " + userId + ":" + deviceId + " for " + roomId + " / " + body.sessionId + " id " + request.requestId) - if (!TextUtils.equals(mCredentials.userId, userId)) { + if (!TextUtils.equals(credentials.userId, userId)) { // TODO: determine if we sent this device the keys already: in Timber.e("## processReceivedRoomKeyRequests() : Ignoring room key request from other user for now") return @@ -100,7 +99,7 @@ internal class IncomingRoomKeyRequestManager( // if we don't have a decryptor for this room/alg, we don't have // the keys for the requested events, and can drop the requests. - val decryptor = mRoomDecryptorProvider.getRoomDecryptor(roomId, alg) + val decryptor = roomDecryptorProvider.getRoomDecryptor(roomId, alg) if (null == decryptor) { Timber.e("## processReceivedRoomKeyRequests() : room key request for unknown $alg in room $roomId") @@ -109,52 +108,52 @@ internal class IncomingRoomKeyRequestManager( if (!decryptor.hasKeysForKeyRequest(request)) { Timber.e("## processReceivedRoomKeyRequests() : room key request for unknown session " + body.sessionId!!) - mCryptoStore.deleteIncomingRoomKeyRequest(request) + cryptoStore.deleteIncomingRoomKeyRequest(request) continue } - if (TextUtils.equals(deviceId, mCredentials.deviceId) && TextUtils.equals(mCredentials.userId, userId)) { + if (TextUtils.equals(deviceId, credentials.deviceId) && TextUtils.equals(credentials.userId, userId)) { Timber.v("## processReceivedRoomKeyRequests() : oneself device - ignored") - mCryptoStore.deleteIncomingRoomKeyRequest(request) + cryptoStore.deleteIncomingRoomKeyRequest(request) continue } request.share = Runnable { decryptor.shareKeysWithDevice(request) - mCryptoStore.deleteIncomingRoomKeyRequest(request) + cryptoStore.deleteIncomingRoomKeyRequest(request) } - request.ignore = Runnable { mCryptoStore.deleteIncomingRoomKeyRequest(request) } + request.ignore = Runnable { cryptoStore.deleteIncomingRoomKeyRequest(request) } // if the device is verified already, share the keys - val device = mCryptoStore.getUserDevice(deviceId!!, userId) + val device = cryptoStore.getUserDevice(deviceId!!, userId) if (null != device) { if (device.isVerified) { Timber.v("## processReceivedRoomKeyRequests() : device is already verified: sharing keys") - mCryptoStore.deleteIncomingRoomKeyRequest(request) + cryptoStore.deleteIncomingRoomKeyRequest(request) request.share?.run() continue } if (device.isBlocked) { Timber.v("## processReceivedRoomKeyRequests() : device is blocked -> ignored") - mCryptoStore.deleteIncomingRoomKeyRequest(request) + cryptoStore.deleteIncomingRoomKeyRequest(request) continue } } - mCryptoStore.storeIncomingRoomKeyRequest(request) + cryptoStore.storeIncomingRoomKeyRequest(request) onRoomKeyRequest(request) } } var receivedRoomKeyRequestCancellations: List? = null - synchronized(mReceivedRoomKeyRequestCancellations) { - if (!mReceivedRoomKeyRequestCancellations.isEmpty()) { - receivedRoomKeyRequestCancellations = mReceivedRoomKeyRequestCancellations.toList() - mReceivedRoomKeyRequestCancellations.clear() + synchronized(this.receivedRoomKeyRequestCancellations) { + if (!this.receivedRoomKeyRequestCancellations.isEmpty()) { + receivedRoomKeyRequestCancellations = this.receivedRoomKeyRequestCancellations.toList() + this.receivedRoomKeyRequestCancellations.clear() } } @@ -167,7 +166,7 @@ internal class IncomingRoomKeyRequestManager( // about, but we don't currently have a record of that, so we just pass // everything through. onRoomKeyRequestCancellation(request) - mCryptoStore.deleteIncomingRoomKeyRequest(request) + cryptoStore.deleteIncomingRoomKeyRequest(request) } } } @@ -178,8 +177,8 @@ internal class IncomingRoomKeyRequestManager( * @param request the request */ private fun onRoomKeyRequest(request: IncomingRoomKeyRequest) { - synchronized(mRoomKeysRequestListeners) { - for (listener in mRoomKeysRequestListeners) { + synchronized(roomKeysRequestListeners) { + for (listener in roomKeysRequestListeners) { try { listener.onRoomKeyRequest(request) } catch (e: Exception) { @@ -197,8 +196,8 @@ internal class IncomingRoomKeyRequestManager( * @param request the cancellation request */ private fun onRoomKeyRequestCancellation(request: IncomingRoomKeyRequestCancellation) { - synchronized(mRoomKeysRequestListeners) { - for (listener in mRoomKeysRequestListeners) { + synchronized(roomKeysRequestListeners) { + for (listener in roomKeysRequestListeners) { try { listener.onRoomKeyRequestCancellation(request) } catch (e: Exception) { @@ -210,14 +209,14 @@ internal class IncomingRoomKeyRequestManager( } fun addRoomKeysRequestListener(listener: RoomKeysRequestListener) { - synchronized(mRoomKeysRequestListeners) { - mRoomKeysRequestListeners.add(listener) + synchronized(roomKeysRequestListeners) { + roomKeysRequestListeners.add(listener) } } fun removeRoomKeysRequestListener(listener: RoomKeysRequestListener) { - synchronized(mRoomKeysRequestListeners) { - mRoomKeysRequestListeners.remove(listener) + synchronized(roomKeysRequestListeners) { + roomKeysRequestListeners.remove(listener) } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/OneTimeKeysUploader.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/OneTimeKeysUploader.kt index 5ec8e84a4f..20c6800eaf 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/OneTimeKeysUploader.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/OneTimeKeysUploader.kt @@ -28,20 +28,19 @@ import timber.log.Timber import java.util.* internal class OneTimeKeysUploader( - private val mCredentials: Credentials, - private val mOlmDevice: MXOlmDevice, - private val mObjectSigner: ObjectSigner, - private val mUploadKeysTask: UploadKeysTask + private val credentials: Credentials, + private val olmDevice: MXOlmDevice, + private val objectSigner: ObjectSigner, + private val uploadKeysTask: UploadKeysTask ) { // tell if there is a OTK check in progress - private var mOneTimeKeyCheckInProgress = false + private var oneTimeKeyCheckInProgress = false // last OTK check timestamp - private var mLastOneTimeKeyCheck: Long = 0 - private var mOneTimeKeyCount: Int? = null + private var lastOneTimeKeyCheck: Long = 0 + private var oneTimeKeyCount: Int? = null - var mLastPublishedOneTimeKeys: Map>? = null - private set + private var lastPublishedOneTimeKeys: Map>? = null /** * Stores the current one_time_key count which will be handled later (in a call of @@ -50,7 +49,7 @@ internal class OneTimeKeysUploader( * @param currentCount the new count */ fun updateOneTimeKeyCount(currentCount: Int) { - mOneTimeKeyCount = currentCount + oneTimeKeyCount = currentCount } @@ -58,19 +57,19 @@ internal class OneTimeKeysUploader( * Check if the OTK must be uploaded. */ suspend fun maybeUploadOneTimeKeys(): Try { - if (mOneTimeKeyCheckInProgress) { + if (oneTimeKeyCheckInProgress) { return Try.just(Unit) } - if (System.currentTimeMillis() - mLastOneTimeKeyCheck < ONE_TIME_KEY_UPLOAD_PERIOD) { + if (System.currentTimeMillis() - lastOneTimeKeyCheck < ONE_TIME_KEY_UPLOAD_PERIOD) { // we've done a key upload recently. return Try.just(Unit) } - mLastOneTimeKeyCheck = System.currentTimeMillis() - mOneTimeKeyCheckInProgress = true + lastOneTimeKeyCheck = System.currentTimeMillis() + oneTimeKeyCheckInProgress = true // We then check how many keys we can store in the Account object. - val maxOneTimeKeys = mOlmDevice.getMaxNumberOfOneTimeKeys() + val maxOneTimeKeys = olmDevice.getMaxNumberOfOneTimeKeys() // Try to keep at most half that number on the server. This leaves the // rest of the slots free to hold keys that have been claimed from the @@ -79,12 +78,12 @@ internal class OneTimeKeysUploader( // discard the oldest private keys first. This will eventually clean // out stale private keys that won't receive a message. val keyLimit = Math.floor(maxOneTimeKeys / 2.0).toInt() - if (mOneTimeKeyCount != null) { - return uploadOTK(mOneTimeKeyCount!!, keyLimit) + val result = if (oneTimeKeyCount != null) { + uploadOTK(oneTimeKeyCount!!, keyLimit) } else { // ask the server how many keys we have - val uploadKeysParams = UploadKeysTask.Params(null, null, mCredentials.deviceId!!) - return mUploadKeysTask.execute(uploadKeysParams) + val uploadKeysParams = UploadKeysTask.Params(null, null, credentials.deviceId!!) + uploadKeysTask.execute(uploadKeysParams) .flatMap { // We need to keep a pool of one time public keys on the server so that // other devices can start conversations with us. But we can only store @@ -97,17 +96,23 @@ internal class OneTimeKeysUploader( // private keys clogging up our local storage. // So we need some kind of engineering compromise to balance all of // these factors. - // TODO Why we do not set mOneTimeKeyCount here? + // TODO Why we do not set oneTimeKeyCount here? // TODO This is not needed anymore, see https://github.com/matrix-org/matrix-js-sdk/pull/493 (TODO on iOS also) val keyCount = it.oneTimeKeyCountsForAlgorithm(MXKey.KEY_SIGNED_CURVE_25519_TYPE) uploadOTK(keyCount, keyLimit) } - .handleError { - Timber.e(it, "## uploadKeys() : failed") - mOneTimeKeyCount = null - mOneTimeKeyCheckInProgress = false - } } + return result + .map { + Timber.v("## uploadKeys() : success") + oneTimeKeyCount = null + oneTimeKeyCheckInProgress = false + } + .handleError { + Timber.e(it, "## uploadKeys() : failed") + oneTimeKeyCount = null + oneTimeKeyCheckInProgress = false + } } /** @@ -117,27 +122,17 @@ internal class OneTimeKeysUploader( * @param keyLimit the limit */ private suspend fun uploadOTK(keyCount: Int, keyLimit: Int): Try { - return uploadLoop(keyCount, keyLimit) - } - - /** - * OTK upload loop - * - * @param keyCount the number of key to generate - * @param keyLimit the limit - */ - private suspend fun uploadLoop(keyCount: Int, keyLimit: Int): Try { if (keyLimit <= keyCount) { // If we don't need to generate any more keys then we are done. return Try.just(Unit) } val keysThisLoop = Math.min(keyLimit - keyCount, ONE_TIME_KEY_GENERATION_MAX_NUMBER) - mOlmDevice.generateOneTimeKeys(keysThisLoop) + olmDevice.generateOneTimeKeys(keysThisLoop) return uploadOneTimeKeys() .flatMap { if (it.hasOneTimeKeyCountsForAlgorithm(MXKey.KEY_SIGNED_CURVE_25519_TYPE)) { - uploadLoop(it.oneTimeKeyCountsForAlgorithm(MXKey.KEY_SIGNED_CURVE_25519_TYPE), keyLimit) + uploadOTK(it.oneTimeKeyCountsForAlgorithm(MXKey.KEY_SIGNED_CURVE_25519_TYPE), keyLimit) } else { Timber.e("## uploadLoop() : response for uploading keys does not contain one_time_key_counts.signed_curve25519") Try.raise(Exception("response for uploading keys does not contain one_time_key_counts.signed_curve25519")) @@ -149,7 +144,7 @@ internal class OneTimeKeysUploader( * Upload my user's one time keys. */ private suspend fun uploadOneTimeKeys(): Try { - val oneTimeKeys = mOlmDevice.getOneTimeKeys() + val oneTimeKeys = olmDevice.getOneTimeKeys() val oneTimeJson = HashMap() val curve25519Map = oneTimeKeys!![OlmAccount.JSON_KEY_ONE_TIME_KEY] @@ -162,7 +157,7 @@ internal class OneTimeKeysUploader( // the key is also signed val canonicalJson = MoshiProvider.getCanonicalJson(Map::class.java, k) - k["signatures"] = mObjectSigner.signObject(canonicalJson) + k["signatures"] = objectSigner.signObject(canonicalJson) oneTimeJson["signed_curve25519:$key_id"] = k } @@ -170,12 +165,12 @@ internal class OneTimeKeysUploader( // For now, we set the device id explicitly, as we may not be using the // same one as used in login. - val uploadParams = UploadKeysTask.Params(null, oneTimeJson, mCredentials.deviceId!!) - return mUploadKeysTask + val uploadParams = UploadKeysTask.Params(null, oneTimeJson, credentials.deviceId!!) + return uploadKeysTask .execute(uploadParams) .map { - mLastPublishedOneTimeKeys = oneTimeKeys - mOlmDevice.markKeysAsPublished() + lastPublishedOneTimeKeys = oneTimeKeys + olmDevice.markKeysAsPublished() it } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/OutgoingRoomKeyRequest.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/OutgoingRoomKeyRequest.kt index a439fdddc6..89de5a078b 100755 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/OutgoingRoomKeyRequest.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/OutgoingRoomKeyRequest.kt @@ -24,15 +24,15 @@ import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyRequestBody */ class OutgoingRoomKeyRequest( // RequestBody - var mRequestBody: RoomKeyRequestBody?, // list of recipients for the request - var mRecipients: List>, // Unique id for this request. Used for both + var requestBody: RoomKeyRequestBody?, // list of recipients for the request + var recipients: List>, // Unique id for this request. Used for both // an id within the request for later pairing with a cancellation, and for // the transaction id when sending the to_device messages to our local - var mRequestId: String, // current state of this request - var mState: RequestState) { + var requestId: String, // current state of this request + var state: RequestState) { // transaction id for the cancellation, if any - var mCancellationTxnId: String? = null + var cancellationTxnId: String? = null /** * Used only for log. @@ -40,8 +40,8 @@ class OutgoingRoomKeyRequest( * @return the room id. */ val roomId: String? - get() = if (null != mRequestBody) { - mRequestBody!!.roomId + get() = if (null != requestBody) { + requestBody!!.roomId } else null /** @@ -50,8 +50,8 @@ class OutgoingRoomKeyRequest( * @return the session id */ val sessionId: String? - get() = if (null != mRequestBody) { - mRequestBody!!.sessionId + get() = if (null != requestBody) { + requestBody!!.sessionId } else null /** diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/OutgoingRoomKeyRequestManager.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/OutgoingRoomKeyRequestManager.kt index c87c798fc1..4c68e905da 100755 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/OutgoingRoomKeyRequestManager.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/OutgoingRoomKeyRequestManager.kt @@ -27,7 +27,6 @@ import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyShareRequest import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore import im.vector.matrix.android.internal.crypto.tasks.SendToDeviceTask import im.vector.matrix.android.internal.task.TaskExecutor -import im.vector.matrix.android.internal.task.TaskThread import im.vector.matrix.android.internal.task.configureWith import timber.log.Timber import java.util.* @@ -88,7 +87,7 @@ internal class OutgoingRoomKeyRequestManager( OutgoingRoomKeyRequest(requestBody, recipients, makeTxnId(), OutgoingRoomKeyRequest.RequestState.UNSENT)) - if (req?.mState == OutgoingRoomKeyRequest.RequestState.UNSENT) { + if (req?.state == OutgoingRoomKeyRequest.RequestState.UNSENT) { startTimer() } } @@ -122,20 +121,20 @@ internal class OutgoingRoomKeyRequestManager( ?: // no request was made for this key return - Timber.v("cancelRoomKeyRequest: requestId: " + req.mRequestId + " state: " + req.mState + " andResend: " + andResend) + Timber.v("cancelRoomKeyRequest: requestId: " + req.requestId + " state: " + req.state + " andResend: " + andResend) - if (req.mState === OutgoingRoomKeyRequest.RequestState.CANCELLATION_PENDING || req.mState === OutgoingRoomKeyRequest.RequestState.CANCELLATION_PENDING_AND_WILL_RESEND) { + if (req.state === OutgoingRoomKeyRequest.RequestState.CANCELLATION_PENDING || req.state === OutgoingRoomKeyRequest.RequestState.CANCELLATION_PENDING_AND_WILL_RESEND) { // nothing to do here - } else if (req.mState === OutgoingRoomKeyRequest.RequestState.UNSENT || req.mState === OutgoingRoomKeyRequest.RequestState.FAILED) { + } else if (req.state === OutgoingRoomKeyRequest.RequestState.UNSENT || req.state === OutgoingRoomKeyRequest.RequestState.FAILED) { Timber.v("## cancelRoomKeyRequest() : deleting unnecessary room key request for $requestBody") - cryptoStore.deleteOutgoingRoomKeyRequest(req.mRequestId) - } else if (req.mState === OutgoingRoomKeyRequest.RequestState.SENT) { + cryptoStore.deleteOutgoingRoomKeyRequest(req.requestId) + } else if (req.state === OutgoingRoomKeyRequest.RequestState.SENT) { if (andResend) { - req.mState = OutgoingRoomKeyRequest.RequestState.CANCELLATION_PENDING_AND_WILL_RESEND + req.state = OutgoingRoomKeyRequest.RequestState.CANCELLATION_PENDING_AND_WILL_RESEND } else { - req.mState = OutgoingRoomKeyRequest.RequestState.CANCELLATION_PENDING + req.state = OutgoingRoomKeyRequest.RequestState.CANCELLATION_PENDING } - req.mCancellationTxnId = makeTxnId() + req.cancellationTxnId = makeTxnId() cryptoStore.updateOutgoingRoomKeyRequest(req) sendOutgoingRoomKeyRequestCancellation(req) } @@ -149,7 +148,6 @@ internal class OutgoingRoomKeyRequestManager( if (sendOutgoingRoomKeyRequestsRunning) { return } - Handler().postDelayed(Runnable { if (sendOutgoingRoomKeyRequestsRunning) { Timber.v("## startTimer() : RoomKeyRequestSend already in progress!") @@ -182,7 +180,7 @@ internal class OutgoingRoomKeyRequestManager( return } - if (OutgoingRoomKeyRequest.RequestState.UNSENT === outgoingRoomKeyRequest.mState) { + if (OutgoingRoomKeyRequest.RequestState.UNSENT === outgoingRoomKeyRequest.state) { sendOutgoingRoomKeyRequest(outgoingRoomKeyRequest) } else { sendOutgoingRoomKeyRequestCancellation(outgoingRoomKeyRequest) @@ -195,20 +193,20 @@ internal class OutgoingRoomKeyRequestManager( * @param request the request */ private fun sendOutgoingRoomKeyRequest(request: OutgoingRoomKeyRequest) { - Timber.v("## sendOutgoingRoomKeyRequest() : Requesting keys " + request.mRequestBody - + " from " + request.mRecipients + " id " + request.mRequestId) + Timber.v("## sendOutgoingRoomKeyRequest() : Requesting keys " + request.requestBody + + " from " + request.recipients + " id " + request.requestId) val requestMessage = RoomKeyShareRequest() requestMessage.requestingDeviceId = cryptoStore.getDeviceId() - requestMessage.requestId = request.mRequestId - requestMessage.body = request.mRequestBody + requestMessage.requestId = request.requestId + requestMessage.body = request.requestBody - sendMessageToDevices(requestMessage, request.mRecipients, request.mRequestId, object : MatrixCallback { + sendMessageToDevices(requestMessage, request.recipients, request.requestId, object : MatrixCallback { private fun onDone(state: OutgoingRoomKeyRequest.RequestState) { - if (request.mState !== OutgoingRoomKeyRequest.RequestState.UNSENT) { - Timber.v("## sendOutgoingRoomKeyRequest() : Cannot update room key request from UNSENT as it was already updated to " + request.mState) + if (request.state !== OutgoingRoomKeyRequest.RequestState.UNSENT) { + Timber.v("## sendOutgoingRoomKeyRequest() : Cannot update room key request from UNSENT as it was already updated to " + request.state) } else { - request.mState = state + request.state = state cryptoStore.updateOutgoingRoomKeyRequest(request) } @@ -234,17 +232,17 @@ internal class OutgoingRoomKeyRequestManager( * @param request the request */ private fun sendOutgoingRoomKeyRequestCancellation(request: OutgoingRoomKeyRequest) { - Timber.v("## sendOutgoingRoomKeyRequestCancellation() : Sending cancellation for key request for " + request.mRequestBody - + " to " + request.mRecipients - + " cancellation id " + request.mCancellationTxnId) + Timber.v("## sendOutgoingRoomKeyRequestCancellation() : Sending cancellation for key request for " + request.requestBody + + " to " + request.recipients + + " cancellation id " + request.cancellationTxnId) val roomKeyShareCancellation = RoomKeyShareCancellation() roomKeyShareCancellation.requestingDeviceId = cryptoStore.getDeviceId() - roomKeyShareCancellation.requestId = request.mCancellationTxnId + roomKeyShareCancellation.requestId = request.cancellationTxnId - sendMessageToDevices(roomKeyShareCancellation, request.mRecipients, request.mCancellationTxnId, object : MatrixCallback { + sendMessageToDevices(roomKeyShareCancellation, request.recipients, request.cancellationTxnId, object : MatrixCallback { private fun onDone() { - cryptoStore.deleteOutgoingRoomKeyRequest(request.mRequestId) + cryptoStore.deleteOutgoingRoomKeyRequest(request.requestId) sendOutgoingRoomKeyRequestsRunning = false startTimer() } @@ -252,13 +250,13 @@ internal class OutgoingRoomKeyRequestManager( override fun onSuccess(data: Unit) { Timber.v("## sendOutgoingRoomKeyRequestCancellation() : done") - val resend = request.mState === OutgoingRoomKeyRequest.RequestState.CANCELLATION_PENDING_AND_WILL_RESEND + val resend = request.state === OutgoingRoomKeyRequest.RequestState.CANCELLATION_PENDING_AND_WILL_RESEND onDone() // Resend the request with a new ID if (resend) { - sendRoomKeyRequest(request.mRequestBody, request.mRecipients) + sendRoomKeyRequest(request.requestBody, request.recipients) } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/RoomDecryptorProvider.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/RoomDecryptorProvider.kt index 4561428b96..e75885a3cb 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/RoomDecryptorProvider.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/RoomDecryptorProvider.kt @@ -43,37 +43,35 @@ internal class RoomDecryptorProvider( */ fun getOrCreateRoomDecryptor(roomId: String?, algorithm: String?): IMXDecrypting? { // sanity check - if (algorithm.isNullOrEmpty() || roomId.isNullOrEmpty()) { + if (algorithm.isNullOrEmpty()) { Timber.e("## getRoomDecryptor() : null algorithm") return null } - - var alg: IMXDecrypting? - synchronized(roomDecryptors) { - if (!roomDecryptors.containsKey(roomId)) { - roomDecryptors[roomId!!] = HashMap() - } - - alg = roomDecryptors[roomId]!![algorithm] - } - if (alg != null) { - return alg - } - val decryptingClass = MXCryptoAlgorithms.hasDecryptorClassForAlgorithm(algorithm) - if (decryptingClass) { - alg = when (algorithm) { - MXCRYPTO_ALGORITHM_MEGOLM -> megolmDecryptionFactory.create() - else -> olmDecryptionFactory.create() - } - if (null != alg) { - if (!TextUtils.isEmpty(roomId)) { - synchronized(roomDecryptors) { - roomDecryptors[roomId]!!.put(algorithm!!, alg!!) - } + if(roomId != null && roomId.isNotEmpty()) { + synchronized(roomDecryptors) { + if (!roomDecryptors.containsKey(roomId)) { + roomDecryptors[roomId] = HashMap() + } + val alg = roomDecryptors[roomId]?.get(algorithm) + if (alg != null) { + return alg } } } - return alg + val decryptingClass = MXCryptoAlgorithms.hasDecryptorClassForAlgorithm(algorithm) + if (decryptingClass) { + val alg = when (algorithm) { + MXCRYPTO_ALGORITHM_MEGOLM -> megolmDecryptionFactory.create() + else -> olmDecryptionFactory.create() + } + if (roomId != null && !TextUtils.isEmpty(roomId)) { + synchronized(roomDecryptors) { + roomDecryptors[roomId]?.put(algorithm, alg) + } + } + return alg + } + return null } fun getRoomDecryptor(roomId: String?, algorithm: String?): IMXDecrypting? { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/EnsureOlmSessionsForDevicesAction.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/EnsureOlmSessionsForDevicesAction.kt index 4481a92f14..f452e6067a 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/EnsureOlmSessionsForDevicesAction.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/EnsureOlmSessionsForDevicesAction.kt @@ -17,6 +17,7 @@ package im.vector.matrix.android.internal.crypto.actions import android.text.TextUtils +import arrow.core.Try import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.internal.crypto.MXOlmDevice import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo @@ -31,12 +32,10 @@ import timber.log.Timber import java.util.* internal class EnsureOlmSessionsForDevicesAction(private val olmDevice: MXOlmDevice, - private val oneTimeKeysForUsersDeviceTask: ClaimOneTimeKeysForUsersDeviceTask, - private val coroutineDispatchers: MatrixCoroutineDispatchers, - private val taskExecutor: TaskExecutor) { + private val oneTimeKeysForUsersDeviceTask: ClaimOneTimeKeysForUsersDeviceTask) { - fun handle(devicesByUser: Map>, callback: MatrixCallback>?) { + suspend fun handle(devicesByUser: Map>): Try> { val devicesWithoutSession = ArrayList() val results = MXUsersDevicesMap() @@ -62,8 +61,7 @@ internal class EnsureOlmSessionsForDevicesAction(private val olmDevice: MXOlmDev } if (devicesWithoutSession.size == 0) { - callback?.onSuccess(results) - return + return Try.just(results) } // Prepare the request for claiming one-time keys @@ -83,67 +81,42 @@ internal class EnsureOlmSessionsForDevicesAction(private val olmDevice: MXOlmDev Timber.v("## claimOneTimeKeysForUsersDevices() : $usersDevicesToClaim") - - oneTimeKeysForUsersDeviceTask - .configureWith(ClaimOneTimeKeysForUsersDeviceTask.Params(usersDevicesToClaim)) - .dispatchTo(object : MatrixCallback> { - override fun onSuccess(data: MXUsersDevicesMap) { - try { - Timber.v("## claimOneTimeKeysForUsersDevices() : keysClaimResponse.oneTimeKeys: $data") - - for (userId in userIds) { - val deviceInfos = devicesByUser[userId] - - for (deviceInfo in deviceInfos!!) { - - var oneTimeKey: MXKey? = null - - val deviceIds = data.getUserDeviceIds(userId) - - if (null != deviceIds) { - for (deviceId in deviceIds) { - val olmSessionResult = results.getObject(deviceId, userId) - - if (null != olmSessionResult!!.mSessionId) { - // We already have a result for this device - continue - } - - val key = data.getObject(deviceId, userId) - - if (TextUtils.equals(key!!.type, oneTimeKeyAlgorithm)) { - oneTimeKey = key - } - - if (null == oneTimeKey) { - Timber.v("## ensureOlmSessionsForDevices() : No one-time keys " + oneTimeKeyAlgorithm - + " for device " + userId + " : " + deviceId) - continue - } - - // Update the result for this device in results - olmSessionResult.mSessionId = verifyKeyAndStartSession(oneTimeKey, userId, deviceInfo) - } + val claimParams = ClaimOneTimeKeysForUsersDeviceTask.Params(usersDevicesToClaim) + return oneTimeKeysForUsersDeviceTask + .execute(claimParams) + .map { + Timber.v("## claimOneTimeKeysForUsersDevices() : keysClaimResponse.oneTimeKeys: $it") + for (userId in userIds) { + val deviceInfos = devicesByUser[userId] + for (deviceInfo in deviceInfos!!) { + var oneTimeKey: MXKey? = null + val deviceIds = it.getUserDeviceIds(userId) + if (null != deviceIds) { + for (deviceId in deviceIds) { + val olmSessionResult = results.getObject(deviceId, userId) + if (olmSessionResult!!.mSessionId != null) { + // We already have a result for this device + continue } + val key = it.getObject(deviceId, userId) + if (key?.type == oneTimeKeyAlgorithm) { + oneTimeKey = key + } + if (oneTimeKey == null) { + Timber.v("## ensureOlmSessionsForDevices() : No one-time keys " + oneTimeKeyAlgorithm + + " for device " + userId + " : " + deviceId) + continue + } + // Update the result for this device in results + olmSessionResult.mSessionId = verifyKeyAndStartSession(oneTimeKey, userId, deviceInfo) } } - } catch (e: Exception) { - Timber.e(e, "## ensureOlmSessionsForDevices() " + e.message) } - - callback?.onSuccess(results) } - - override fun onFailure(failure: Throwable) { - Timber.e(failure, "## ensureOlmSessionsForUsers(): claimOneTimeKeysForUsersDevices request failed") - - callback?.onFailure(failure) - } - }) - .executeBy(taskExecutor) + results + } } - private fun verifyKeyAndStartSession(oneTimeKey: MXKey, userId: String, deviceInfo: MXDeviceInfo): String? { var sessionId: String? = null diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/EnsureOlmSessionsForUsersAction.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/EnsureOlmSessionsForUsersAction.kt index eb3f44a132..6b7e28be1b 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/EnsureOlmSessionsForUsersAction.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/EnsureOlmSessionsForUsersAction.kt @@ -17,6 +17,7 @@ package im.vector.matrix.android.internal.crypto.actions import android.text.TextUtils +import arrow.core.Try import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.internal.crypto.MXOlmDevice import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo @@ -32,15 +33,10 @@ internal class EnsureOlmSessionsForUsersAction(private val olmDevice: MXOlmDevic /** * Try to make sure we have established olm sessions for the given users. - * It must be called in getEncryptingThreadHandler() thread. - * The callback is called in the UI thread. - * * @param users a list of user ids. - * @param callback the asynchronous callback */ - fun handle(users: List, callback: MatrixCallback>) { + suspend fun handle(users: List) : Try> { Timber.v("## ensureOlmSessionsForUsers() : ensureOlmSessionsForUsers $users") - val devicesByUser = HashMap>() for (userId in users) { @@ -64,7 +60,6 @@ internal class EnsureOlmSessionsForUsersAction(private val olmDevice: MXOlmDevic devicesByUser[userId]!!.add(device) } } - - ensureOlmSessionsForDevicesAction.handle(devicesByUser, callback) + return ensureOlmSessionsForDevicesAction.handle(devicesByUser) } } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/IMXDecrypting.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/IMXDecrypting.kt index b6a911c812..a44cd61e70 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/IMXDecrypting.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/IMXDecrypting.kt @@ -37,7 +37,7 @@ internal interface IMXDecrypting { * @throws MXDecryptionException the decryption failure reason */ @Throws(MXDecryptionException::class) - fun decryptEvent(event: Event, timeline: String): MXEventDecryptionResult? + suspend fun decryptEvent(event: Event, timeline: String): MXEventDecryptionResult? /** * Handle a key event. diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/IMXEncrypting.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/IMXEncrypting.kt index baa5fb640e..5e9d305e0e 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/IMXEncrypting.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/IMXEncrypting.kt @@ -17,6 +17,7 @@ package im.vector.matrix.android.internal.crypto.algorithms +import arrow.core.Try import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.session.events.model.Content @@ -31,7 +32,7 @@ internal interface IMXEncrypting { * @param eventContent the content of the event. * @param eventType the type of the event. * @param userIds the room members the event will be sent to. - * @param callback the asynchronous callback + * @return the encrypted content wrapped by [Try] */ - fun encryptEventContent(eventContent: Content, eventType: String, userIds: List, callback: MatrixCallback) + suspend fun encryptEventContent(eventContent: Content, eventType: String, userIds: List): Try } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt index 016eccf74f..b39728e454 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt @@ -18,26 +18,19 @@ package im.vector.matrix.android.internal.crypto.algorithms.megolm import android.text.TextUtils -import im.vector.matrix.android.api.MatrixCallback +import arrow.core.Try import im.vector.matrix.android.api.auth.data.Credentials import im.vector.matrix.android.api.session.crypto.MXCryptoError import im.vector.matrix.android.api.session.events.model.Event import im.vector.matrix.android.api.session.events.model.EventType import im.vector.matrix.android.api.session.events.model.toModel -import im.vector.matrix.android.internal.crypto.CryptoAsyncHelper -import im.vector.matrix.android.internal.crypto.DeviceListManager -import im.vector.matrix.android.internal.crypto.IncomingRoomKeyRequest -import im.vector.matrix.android.internal.crypto.MXDecryptionException -import im.vector.matrix.android.internal.crypto.MXEventDecryptionResult -import im.vector.matrix.android.internal.crypto.MXOlmDevice -import im.vector.matrix.android.internal.crypto.OutgoingRoomKeyRequestManager +import im.vector.matrix.android.internal.crypto.* import im.vector.matrix.android.internal.crypto.actions.EnsureOlmSessionsForDevicesAction import im.vector.matrix.android.internal.crypto.actions.MessageEncrypter import im.vector.matrix.android.internal.crypto.algorithms.IMXDecrypting import im.vector.matrix.android.internal.crypto.algorithms.MXDecryptionResult import im.vector.matrix.android.internal.crypto.keysbackup.KeysBackup import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo -import im.vector.matrix.android.internal.crypto.model.MXOlmSessionResult import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap import im.vector.matrix.android.internal.crypto.model.event.EncryptedEventContent import im.vector.matrix.android.internal.crypto.model.event.RoomKeyContent @@ -69,7 +62,7 @@ internal class MXMegolmDecryption(private val credentials: Credentials, private var pendingEvents: MutableMap>> = HashMap() @Throws(MXDecryptionException::class) - override fun decryptEvent(event: Event, timeline: String): MXEventDecryptionResult? { + override suspend fun decryptEvent(event: Event, timeline: String): MXEventDecryptionResult? { return decryptEvent(event, timeline, true) } @@ -78,7 +71,7 @@ internal class MXMegolmDecryption(private val credentials: Credentials, val encryptedEventContent = event.content.toModel()!! if (TextUtils.isEmpty(encryptedEventContent.senderKey) || TextUtils.isEmpty(encryptedEventContent.sessionId) || TextUtils.isEmpty(encryptedEventContent.ciphertext)) { throw MXDecryptionException(MXCryptoError(MXCryptoError.MISSING_FIELDS_ERROR_CODE, - MXCryptoError.UNABLE_TO_DECRYPT, MXCryptoError.MISSING_FIELDS_REASON)) + MXCryptoError.UNABLE_TO_DECRYPT, MXCryptoError.MISSING_FIELDS_REASON)) } var eventDecryptionResult: MXEventDecryptionResult? = null @@ -90,7 +83,6 @@ internal class MXMegolmDecryption(private val credentials: Credentials, } catch (e: MXDecryptionException) { cryptoError = e.cryptoError } - // the decryption succeeds if (decryptGroupMessageResult?.payload != null && cryptoError == null) { eventDecryptionResult = MXEventDecryptionResult() @@ -105,9 +97,8 @@ internal class MXMegolmDecryption(private val credentials: Credentials, eventDecryptionResult.forwardingCurve25519KeyChain = decryptGroupMessageResult.forwardingCurve25519KeyChain!! } else if (cryptoError != null) { if (cryptoError.isOlmError) { - if (TextUtils.equals("UNKNOWN_MESSAGE_INDEX", cryptoError.message)) { + if (MXCryptoError.UNKNOWN_MESSAGE_INDEX == cryptoError.message) { addEventToPendingList(event, timeline) - if (requestKeysOnFail) { requestKeysForEvent(event) } @@ -176,20 +167,19 @@ internal class MXMegolmDecryption(private val credentials: Credentials, */ private fun addEventToPendingList(event: Event, timelineId: String) { val encryptedEventContent = event.content.toModel()!! + val pendingEventsKey = "${encryptedEventContent.senderKey}|${encryptedEventContent.sessionId}" - val k = "${encryptedEventContent.senderKey}|${encryptedEventContent.sessionId}" - - if (!pendingEvents.containsKey(k)) { - pendingEvents[k] = HashMap() + if (!pendingEvents.containsKey(pendingEventsKey)) { + pendingEvents[pendingEventsKey] = HashMap() } - if (!pendingEvents[k]!!.containsKey(timelineId)) { - pendingEvents[k]!!.put(timelineId, ArrayList()) + if (!pendingEvents[pendingEventsKey]!!.containsKey(timelineId)) { + pendingEvents[pendingEventsKey]!![timelineId] = ArrayList() } - if (pendingEvents[k]!![timelineId]!!.indexOf(event) < 0) { + if (pendingEvents[pendingEventsKey]!![timelineId]!!.indexOf(event) < 0) { Timber.v("## addEventToPendingList() : add Event " + event.eventId + " in room id " + event.roomId) - pendingEvents[k]!![timelineId]!!.add(event) + pendingEvents[pendingEventsKey]!![timelineId]!!.add(event) } } @@ -213,7 +203,7 @@ internal class MXMegolmDecryption(private val credentials: Credentials, if (event.getClearType() == EventType.FORWARDED_ROOM_KEY) { Timber.v("## onRoomKeyEvent(), forward adding key : roomId " + roomKeyContent.roomId + " sessionId " + roomKeyContent.sessionId - + " sessionKey " + roomKeyContent.sessionKey) // from " + event); + + " sessionKey " + roomKeyContent.sessionKey) // from " + event); val forwardedRoomKeyContent = event.getClearContent().toModel()!! if (null == forwardedRoomKeyContent.forwardingCurve25519KeyChain) { @@ -239,7 +229,7 @@ internal class MXMegolmDecryption(private val credentials: Credentials, keysClaimed["ed25519"] = forwardedRoomKeyContent.senderClaimedEd25519Key!! } else { Timber.v("## onRoomKeyEvent(), Adding key : roomId " + roomKeyContent.roomId + " sessionId " + roomKeyContent.sessionId - + " sessionKey " + roomKeyContent.sessionKey) // from " + event); + + " sessionKey " + roomKeyContent.sessionKey) // from " + event); if (null == senderKey) { Timber.e("## onRoomKeyEvent() : key event has no sender key (not encrypted?)") @@ -275,7 +265,8 @@ internal class MXMegolmDecryption(private val credentials: Credentials, * @param sessionId the session id */ override fun onNewSession(senderKey: String, sessionId: String) { - val k = "$senderKey|$sessionId" + //TODO see how to handle this + /*val k = "$senderKey|$sessionId" val pending = pendingEvents[k] @@ -309,6 +300,7 @@ internal class MXMegolmDecryption(private val credentials: Credentials, } } } + */ } override fun hasKeysForKeyRequest(request: IncomingRoomKeyRequest): Boolean { @@ -323,70 +315,46 @@ internal class MXMegolmDecryption(private val credentials: Credentials, } val userId = request.userId!! CoroutineScope(coroutineDispatchers.crypto).launch { - deviceListManager.downloadKeys(listOf(userId), false, object : MatrixCallback> { - override fun onSuccess(data: MXUsersDevicesMap) { - val deviceId = request.deviceId - val deviceInfo = cryptoStore.getUserDevice(deviceId!!, userId) + deviceListManager + .downloadKeys(listOf(userId), false) + .flatMap { + val deviceId = request.deviceId + val deviceInfo = cryptoStore.getUserDevice(deviceId!!, userId) + if (deviceInfo == null) { + throw RuntimeException() + } else { + val devicesByUser = HashMap>() + devicesByUser[userId] = ArrayList(Arrays.asList(deviceInfo)) + ensureOlmSessionsForDevicesAction + .handle(devicesByUser) + .flatMap { + val body = request.requestBody + val olmSessionResult = it.getObject(deviceId, userId) + if (olmSessionResult?.mSessionId == null) { + // no session with this device, probably because there + // were no one-time keys. + Try.just(Unit) + } + Timber.v("## shareKeysWithDevice() : sharing keys for session " + body!!.senderKey + "|" + body.sessionId + + " with device " + userId + ":" + deviceId) + val inboundGroupSession = olmDevice.getInboundGroupSession(body.sessionId, body.senderKey, body.roomId) - if (null != deviceInfo) { - val body = request.requestBody + val payloadJson = HashMap() + payloadJson["type"] = EventType.FORWARDED_ROOM_KEY + payloadJson["content"] = inboundGroupSession!!.exportKeys()!! - val devicesByUser = HashMap>() - devicesByUser[userId] = ArrayList(Arrays.asList(deviceInfo)) + val encodedPayload = messageEncrypter.encryptMessage(payloadJson, Arrays.asList(deviceInfo)) + val sendToDeviceMap = MXUsersDevicesMap() + sendToDeviceMap.setObject(encodedPayload, userId, deviceId) + Timber.v("## shareKeysWithDevice() : sending to $userId:$deviceId") + val sendToDeviceParams = SendToDeviceTask.Params(EventType.ENCRYPTED, sendToDeviceMap) + sendToDeviceTask.execute(sendToDeviceParams) + } - ensureOlmSessionsForDevicesAction.handle(devicesByUser, object : MatrixCallback> { - override fun onSuccess(data: MXUsersDevicesMap) { - val olmSessionResult = data.getObject(deviceId, userId) - if (olmSessionResult?.mSessionId == null) { - // no session with this device, probably because there - // were no one-time keys. - // - // ensureOlmSessionsForUsers has already done the logging, - // so just skip it. - return - } - Timber.v("## shareKeysWithDevice() : sharing keys for session " + body!!.senderKey + "|" + body.sessionId - + " with device " + userId + ":" + deviceId) - val inboundGroupSession = olmDevice.getInboundGroupSession(body.sessionId, body.senderKey, body.roomId) - - val payloadJson = HashMap() - payloadJson["type"] = EventType.FORWARDED_ROOM_KEY - payloadJson["content"] = inboundGroupSession!!.exportKeys()!! - - val encodedPayload = messageEncrypter.encryptMessage(payloadJson, Arrays.asList(deviceInfo)) - val sendToDeviceMap = MXUsersDevicesMap() - sendToDeviceMap.setObject(encodedPayload, userId, deviceId) - Timber.v("## shareKeysWithDevice() : sending to $userId:$deviceId") - - val sendToDeviceParams = SendToDeviceTask.Params(EventType.ENCRYPTED, sendToDeviceMap) - sendToDeviceTask - .execute(sendToDeviceParams) - .fold( - { - Timber.e(it, "## shareKeysWithDevice() : sendToDevice $userId:$deviceId failed") - } - , { - Timber.v("## shareKeysWithDevice() : sent to $userId:$deviceId") - } - ) - } - - override fun onFailure(failure: Throwable) { - Timber.e(failure, "## shareKeysWithDevice() : ensureOlmSessionsForDevices $userId:$deviceId failed") - } - }) - } else { - Timber.e("## shareKeysWithDevice() : ensureOlmSessionsForDevices $userId:$deviceId not found") + } } - } - - override fun onFailure(failure: Throwable) { - Timber.e(failure, "## shareKeysWithDevice() : downloadKeys $userId failed") - } - }) } - - } + } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmDecryptionFactory.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmDecryptionFactory.kt index e6b6b7f0f3..f5b91c6bf8 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmDecryptionFactory.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmDecryptionFactory.kt @@ -24,7 +24,6 @@ import im.vector.matrix.android.internal.crypto.actions.EnsureOlmSessionsForDevi import im.vector.matrix.android.internal.crypto.actions.MessageEncrypter import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore import im.vector.matrix.android.internal.crypto.tasks.SendToDeviceTask -import im.vector.matrix.android.internal.task.TaskExecutor import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers internal class MXMegolmDecryptionFactory(private val mCredentials: Credentials, @@ -35,8 +34,7 @@ internal class MXMegolmDecryptionFactory(private val mCredentials: Credentials, private val mEnsureOlmSessionsForDevicesAction: EnsureOlmSessionsForDevicesAction, private val mCryptoStore: IMXCryptoStore, private val mSendToDeviceTask: SendToDeviceTask, - private val coroutineDispatchers: MatrixCoroutineDispatchers, - private val mTaskExecutor: TaskExecutor) { + private val coroutineDispatchers: MatrixCoroutineDispatchers) { fun create(): MXMegolmDecryption { return MXMegolmDecryption( diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmEncryption.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmEncryption.kt index 29a3c05c1d..6515dd1cea 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmEncryption.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmEncryption.kt @@ -19,13 +19,10 @@ package im.vector.matrix.android.internal.crypto.algorithms.megolm import android.text.TextUtils -import im.vector.matrix.android.api.MatrixCallback +import arrow.core.Try import im.vector.matrix.android.api.auth.data.Credentials -import im.vector.matrix.android.api.failure.Failure -import im.vector.matrix.android.api.session.crypto.MXCryptoError import im.vector.matrix.android.api.session.events.model.Content import im.vector.matrix.android.api.session.events.model.EventType -import im.vector.matrix.android.internal.crypto.CryptoAsyncHelper import im.vector.matrix.android.internal.crypto.DeviceListManager import im.vector.matrix.android.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM import im.vector.matrix.android.internal.crypto.MXOlmDevice @@ -34,15 +31,11 @@ import im.vector.matrix.android.internal.crypto.actions.MessageEncrypter import im.vector.matrix.android.internal.crypto.algorithms.IMXEncrypting import im.vector.matrix.android.internal.crypto.keysbackup.KeysBackup import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo -import im.vector.matrix.android.internal.crypto.model.MXOlmSessionResult -import im.vector.matrix.android.internal.crypto.model.MXQueuedEncryption import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap import im.vector.matrix.android.internal.crypto.repository.WarnOnUnknownDeviceRepository import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore import im.vector.matrix.android.internal.crypto.tasks.SendToDeviceTask import im.vector.matrix.android.internal.di.MoshiProvider -import im.vector.matrix.android.internal.task.TaskExecutor -import im.vector.matrix.android.internal.task.configureWith import im.vector.matrix.android.internal.util.convertToUTF8 import timber.log.Timber import java.util.* @@ -50,7 +43,6 @@ import java.util.* internal class MXMegolmEncryption( // The id of the room we will be sending to. private var roomId: String, - private val olmDevice: MXOlmDevice, private val keysBackup: KeysBackup, private val cryptoStore: IMXCryptoStore, @@ -58,7 +50,6 @@ internal class MXMegolmEncryption( private val ensureOlmSessionsForDevicesAction: EnsureOlmSessionsForDevicesAction, private val credentials: Credentials, private val sendToDeviceTask: SendToDeviceTask, - private val taskExecutor: TaskExecutor, private val messageEncrypter: MessageEncrypter, private val warnOnUnknownDevicesRepository: WarnOnUnknownDeviceRepository ) : IMXEncrypting { @@ -69,84 +60,20 @@ internal class MXMegolmEncryption( // case outboundSession.shareOperation will be non-null.) private var outboundSession: MXOutboundSessionInfo? = null - // true when there is an HTTP operation in progress - private var shareOperationIsProgress: Boolean = false - - private val _pendingEncryptions = ArrayList() - // Default rotation periods // TODO: Make it configurable via parameters // Session rotation periods private var sessionRotationPeriodMsgs: Int = 100 private var sessionRotationPeriodMs: Int = 7 * 24 * 3600 * 1000 - /** - * @return a snapshot of the pending encryptions - */ - private val pendingEncryptions: List - get() { - val list = ArrayList() - synchronized(_pendingEncryptions) { - list.addAll(_pendingEncryptions) - } - return list - } - - override fun encryptEventContent(eventContent: Content, - eventType: String, - userIds: List, - callback: MatrixCallback) { - // Queue the encryption request - // It will be processed when everything is set up - val queuedEncryption = MXQueuedEncryption() - - queuedEncryption.eventContent = eventContent - queuedEncryption.eventType = eventType - queuedEncryption.apiCallback = callback - - synchronized(_pendingEncryptions) { - _pendingEncryptions.add(queuedEncryption) - } - - val t0 = System.currentTimeMillis() - Timber.v("## encryptEventContent () starts") - - getDevicesInRoom(userIds, object : MatrixCallback> { - - /** - * A network error has been received while encrypting - * @param failure the exception - */ - private fun dispatchFailure(failure: Throwable) { - Timber.e(failure, "## encryptEventContent() : failure") - val queuedEncryptions = pendingEncryptions - - for (queuedEncryption in queuedEncryptions) { - queuedEncryption.apiCallback?.onFailure(failure) + override suspend fun encryptEventContent(eventContent: Content, + eventType: String, + userIds: List): Try { + return getDevicesInRoom(userIds) + .flatMap { ensureOutboundSession(it) } + .flatMap { + encryptContent(it, eventType, eventContent) } - - synchronized(_pendingEncryptions) { - _pendingEncryptions.removeAll(queuedEncryptions) - } - } - - override fun onSuccess(devicesInRoom: MXUsersDevicesMap) { - ensureOutboundSession(devicesInRoom, object : MatrixCallback { - override fun onSuccess(data: MXOutboundSessionInfo) { - Timber.v("## encryptEventContent () processPendingEncryptions after " + (System.currentTimeMillis() - t0) + "ms") - processPendingEncryptions(data) - } - - override fun onFailure(failure: Throwable) { - dispatchFailure(failure) - } - }) - } - - override fun onFailure(failure: Throwable) { - dispatchFailure(failure) - } - }) } /** @@ -161,7 +88,7 @@ internal class MXMegolmEncryption( keysClaimedMap["ed25519"] = olmDevice.deviceEd25519Key!! olmDevice.addInboundGroupSession(sessionId!!, olmDevice.getSessionKey(sessionId)!!, roomId, olmDevice.deviceCurve25519Key!!, - ArrayList(), keysClaimedMap, false) + ArrayList(), keysClaimedMap, false) keysBackup.maybeBackupKeys() @@ -172,62 +99,33 @@ internal class MXMegolmEncryption( * Ensure the outbound session * * @param devicesInRoom the devices list - * @param callback the asynchronous callback. */ - private fun ensureOutboundSession(devicesInRoom: MXUsersDevicesMap, callback: MatrixCallback?) { + private suspend fun ensureOutboundSession(devicesInRoom: MXUsersDevicesMap): Try { var session = outboundSession - - if (null == session - // Need to make a brand new session? - || session.needsRotation(sessionRotationPeriodMsgs, sessionRotationPeriodMs) - // Determine if we have shared with anyone we shouldn't have - || session.sharedWithTooManyDevices(devicesInRoom)) { + if (session == null + // Need to make a brand new session? + || session.needsRotation(sessionRotationPeriodMsgs, sessionRotationPeriodMs) + // Determine if we have shared with anyone we shouldn't have + || session.sharedWithTooManyDevices(devicesInRoom)) { session = prepareNewSessionInRoom() outboundSession = session } - - if (shareOperationIsProgress) { - Timber.v("## ensureOutboundSessionInRoom() : already in progress") - // Key share already in progress - return - } - - val fSession = session - + val safeSession = session val shareMap = HashMap>()/* userId */ - val userIds = devicesInRoom.userIds - for (userId in userIds) { val deviceIds = devicesInRoom.getUserDeviceIds(userId) - for (deviceId in deviceIds!!) { val deviceInfo = devicesInRoom.getObject(deviceId, userId) - - if (null == fSession.mSharedWithDevices.getObject(deviceId, userId)) { + if (null == safeSession.sharedWithDevices.getObject(deviceId, userId)) { if (!shareMap.containsKey(userId)) { shareMap[userId] = ArrayList() } - shareMap[userId]!!.add(deviceInfo) } } } - - shareKey(fSession, shareMap, object : MatrixCallback { - override fun onSuccess(data: Unit) { - shareOperationIsProgress = false - callback?.onSuccess(fSession) - } - - override fun onFailure(failure: Throwable) { - Timber.e("## ensureOutboundSessionInRoom() : shareKey onFailure") - - callback?.onFailure(failure) - shareOperationIsProgress = false - } - }) - + return shareKey(safeSession, shareMap).map { safeSession!! } } /** @@ -235,55 +133,33 @@ internal class MXMegolmEncryption( * * @param session the session info * @param devicesByUsers the devices map - * @param callback the asynchronous callback */ - private fun shareKey(session: MXOutboundSessionInfo, - devicesByUsers: MutableMap>, - callback: MatrixCallback?) { + private suspend fun shareKey(session: MXOutboundSessionInfo, + devicesByUsers: Map>): Try { // nothing to send, the task is done - if (0 == devicesByUsers.size) { + if (devicesByUsers.isEmpty()) { Timber.v("## shareKey() : nothing more to do") - - if (null != callback) { - CryptoAsyncHelper.getUiHandler().post { callback.onSuccess(Unit) } - } - - return + return Try.just(Unit) } - // reduce the map size to avoid request timeout when there are too many devices (Users size * devices per user) val subMap = HashMap>() - val userIds = ArrayList() var devicesCount = 0 - for (userId in devicesByUsers.keys) { val devicesList = devicesByUsers[userId] - userIds.add(userId) subMap[userId] = devicesList!! - devicesCount += devicesList.size - if (devicesCount > 100) { break } } - Timber.v("## shareKey() ; userId $userIds") - shareUserDevicesKey(session, subMap, object : MatrixCallback { - override fun onSuccess(data: Unit) { - for (userId in userIds) { - devicesByUsers.remove(userId) + return shareUserDevicesKey(session, subMap) + .flatMap { + val remainingDevices = devicesByUsers.filterKeys { userIds.contains(it) } + shareKey(session, remainingDevices) } - shareKey(session, devicesByUsers, callback) - } - - override fun onFailure(failure: Throwable) { - Timber.e(failure, "## shareKey() ; userIds " + userIds + " failed") - callback?.onFailure(failure) - } - }) } /** @@ -293,16 +169,15 @@ internal class MXMegolmEncryption( * @param devicesByUser the devices map * @param callback the asynchronous callback */ - private fun shareUserDevicesKey(session: MXOutboundSessionInfo, - devicesByUser: Map>, - callback: MatrixCallback?) { - val sessionKey = olmDevice.getSessionKey(session.mSessionId) - val chainIndex = olmDevice.getMessageIndex(session.mSessionId) + private suspend fun shareUserDevicesKey(session: MXOutboundSessionInfo, + devicesByUser: Map>): Try { + val sessionKey = olmDevice.getSessionKey(session.sessionId) + val chainIndex = olmDevice.getMessageIndex(session.sessionId) val submap = HashMap() submap["algorithm"] = MXCRYPTO_ALGORITHM_MEGOLM submap["room_id"] = roomId - submap["session_id"] = session.mSessionId + submap["session_id"] = session.sessionId submap["session_key"] = sessionKey!! submap["chain_index"] = chainIndex @@ -310,56 +185,48 @@ internal class MXMegolmEncryption( payload["type"] = EventType.ROOM_KEY payload["content"] = submap - val t0 = System.currentTimeMillis() + var t0 = System.currentTimeMillis() Timber.v("## shareUserDevicesKey() : starts") - ensureOlmSessionsForDevicesAction.handle(devicesByUser, object : MatrixCallback> { - override fun onSuccess(data: MXUsersDevicesMap) { - Timber.v("## shareUserDevicesKey() : ensureOlmSessionsForDevices succeeds after " - + (System.currentTimeMillis() - t0) + " ms") - val contentMap = MXUsersDevicesMap() - - var haveTargets = false - val userIds = data.userIds - - for (userId in userIds) { - val devicesToShareWith = devicesByUser[userId] - - for ((deviceID) in devicesToShareWith!!) { - - val sessionResult = data.getObject(deviceID, userId) - - if (null == sessionResult || null == sessionResult.mSessionId) { - // no session with this device, probably because there - // were no one-time keys. - // - // we could send them a to_device message anyway, as a - // signal that they have missed out on the key sharing - // message because of the lack of keys, but there's not - // much point in that really; it will mostly serve to clog - // up to_device inboxes. - // - // ensureOlmSessionsForUsers has already done the logging, - // so just skip it. - continue + return ensureOlmSessionsForDevicesAction.handle(devicesByUser) + .flatMap { + Timber.v("## shareUserDevicesKey() : ensureOlmSessionsForDevices succeeds after " + + (System.currentTimeMillis() - t0) + " ms") + val contentMap = MXUsersDevicesMap() + var haveTargets = false + val userIds = it.userIds + for (userId in userIds) { + val devicesToShareWith = devicesByUser[userId] + for ((deviceID) in devicesToShareWith!!) { + val sessionResult = it.getObject(deviceID, userId) + if (sessionResult?.mSessionId == null) { + // no session with this device, probably because there + // were no one-time keys. + // + // we could send them a to_device message anyway, as a + // signal that they have missed out on the key sharing + // message because of the lack of keys, but there's not + // much point in that really; it will mostly serve to clog + // up to_device inboxes. + // + // ensureOlmSessionsForUsers has already done the logging, + // so just skip it. + continue + } + Timber.v("## shareUserDevicesKey() : Sharing keys with device $userId:$deviceID") + //noinspection ArraysAsListWithZeroOrOneArgument,ArraysAsListWithZeroOrOneArgument + contentMap.setObject(messageEncrypter.encryptMessage(payload, Arrays.asList(sessionResult.mDevice)), userId, deviceID) + haveTargets = true } - - Timber.v("## shareUserDevicesKey() : Sharing keys with device $userId:$deviceID") - //noinspection ArraysAsListWithZeroOrOneArgument,ArraysAsListWithZeroOrOneArgument - contentMap.setObject(messageEncrypter.encryptMessage(payload, Arrays.asList(sessionResult.mDevice)), userId, deviceID) - haveTargets = true } - } - - if (haveTargets) { - val t0 = System.currentTimeMillis() - Timber.v("## shareUserDevicesKey() : has target") - - sendToDeviceTask.configureWith(SendToDeviceTask.Params(EventType.ENCRYPTED, contentMap)) - .dispatchTo(object : MatrixCallback { - override fun onSuccess(data: Unit) { + if (haveTargets) { + t0 = System.currentTimeMillis() + Timber.v("## shareUserDevicesKey() : has target") + val sendToDeviceParams = SendToDeviceTask.Params(EventType.ENCRYPTED, contentMap) + sendToDeviceTask.execute(sendToDeviceParams) + .map { Timber.v("## shareUserDevicesKey() : sendToDevice succeeds after " - + (System.currentTimeMillis() - t0) + " ms") + + (System.currentTimeMillis() - t0) + " ms") // Add the devices we have shared with to session.sharedWithDevices. // we deliberately iterate over devicesByUser (ie, the devices we @@ -368,80 +235,45 @@ internal class MXMegolmEncryption( // for dead devices on every message. for (userId in devicesByUser.keys) { val devicesToShareWith = devicesByUser[userId] - for ((deviceId) in devicesToShareWith!!) { - session.mSharedWithDevices.setObject(chainIndex, userId, deviceId) + session.sharedWithDevices.setObject(chainIndex, userId, deviceId) } } - - CryptoAsyncHelper.getUiHandler().post { - callback?.onSuccess(Unit) - } + Unit } - - override fun onFailure(failure: Throwable) { - Timber.e(failure, "## shareUserDevicesKey() : sendToDevice") - - callback?.onFailure(failure) - } - }) - .executeBy(taskExecutor) - } else { - Timber.v("## shareUserDevicesKey() : no need to sharekey") - - if (null != callback) { - CryptoAsyncHelper.getUiHandler().post { callback.onSuccess(Unit) } + } else { + Timber.v("## shareUserDevicesKey() : no need to sharekey") + Try.just(Unit) } } - } - - override fun onFailure(failure: Throwable) { - Timber.e(failure, "## shareUserDevicesKey() : ensureOlmSessionsForDevices failed") - - callback?.onFailure(failure) - } - }) } /** * process the pending encryptions */ - private fun processPendingEncryptions(session: MXOutboundSessionInfo?) { - if (null != session) { - val queuedEncryptions = pendingEncryptions + private suspend fun encryptContent(session: MXOutboundSessionInfo, eventType: String, eventContent: Content) = Try { + // Everything is in place, encrypt all pending events + val payloadJson = HashMap() + payloadJson["room_id"] = roomId + payloadJson["type"] = eventType + payloadJson["content"] = eventContent - // Everything is in place, encrypt all pending events - for (queuedEncryption in queuedEncryptions) { - val payloadJson = HashMap() + // Get canonical Json from - payloadJson["room_id"] = roomId - payloadJson["type"] = queuedEncryption.eventType!! - payloadJson["content"] = queuedEncryption.eventContent!! + val payloadString = convertToUTF8(MoshiProvider.getCanonicalJson(Map::class.java, payloadJson)) + val ciphertext = olmDevice.encryptGroupMessage(session.sessionId, payloadString!!) - // Get canonical Json from + val map = HashMap() + map["algorithm"] = MXCRYPTO_ALGORITHM_MEGOLM + map["sender_key"] = olmDevice.deviceCurve25519Key!! + map["ciphertext"] = ciphertext!! + map["session_id"] = session.sessionId - val payloadString = convertToUTF8(MoshiProvider.getCanonicalJson(Map::class.java, payloadJson)) - val ciphertext = olmDevice.encryptGroupMessage(session.mSessionId, payloadString!!) - - val map = HashMap() - map["algorithm"] = MXCRYPTO_ALGORITHM_MEGOLM - map["sender_key"] = olmDevice.deviceCurve25519Key!! - map["ciphertext"] = ciphertext!! - map["session_id"] = session.mSessionId - - // Include our device ID so that recipients can send us a - // m.new_device message if they don't have our session key. - map["device_id"] = credentials.deviceId!! - - CryptoAsyncHelper.getUiHandler().post { queuedEncryption.apiCallback?.onSuccess(map) } - - session.mUseCount++ - } - - synchronized(_pendingEncryptions) { - _pendingEncryptions.removeAll(queuedEncryptions) - } - } + // Include our device ID so that recipients can send us a + // m.new_device message if they don't have our session key. + map["device_id"] = credentials.deviceId!! + session.useCount++ + map } /** @@ -451,65 +283,47 @@ internal class MXMegolmEncryption( * @param userIds the user ids whose devices must be checked. * @param callback the asynchronous callback */ - private fun getDevicesInRoom(userIds: List, callback: MatrixCallback>) { + private suspend fun getDevicesInRoom(userIds: List): Try> { // We are happy to use a cached version here: we assume that if we already // have a list of the user's devices, then we already share an e2e room // with them, which means that they will have announced any new devices via // an m.new_device. - deviceListManager.downloadKeys(userIds, false, object : MatrixCallback> { - override fun onSuccess(data: MXUsersDevicesMap) { - val encryptToVerifiedDevicesOnly = cryptoStore.getGlobalBlacklistUnverifiedDevices() - || cryptoStore.getRoomsListBlacklistUnverifiedDevices().contains(roomId) + return deviceListManager + .downloadKeys(userIds, false) + .map { + val encryptToVerifiedDevicesOnly = cryptoStore.getGlobalBlacklistUnverifiedDevices() + || cryptoStore.getRoomsListBlacklistUnverifiedDevices().contains(roomId) - val devicesInRoom = MXUsersDevicesMap() - val unknownDevices = MXUsersDevicesMap() + val devicesInRoom = MXUsersDevicesMap() + val unknownDevices = MXUsersDevicesMap() - for (userId in data.userIds) { - val deviceIds = data.getUserDeviceIds(userId) + for (userId in it.userIds) { + val deviceIds = it.getUserDeviceIds(userId) - for (deviceId in deviceIds!!) { - val deviceInfo = data.getObject(deviceId, userId) + for (deviceId in deviceIds!!) { + val deviceInfo = it.getObject(deviceId, userId) + if (warnOnUnknownDevicesRepository.warnOnUnknownDevices() && deviceInfo!!.isUnknown) { + // The device is not yet known by the user + unknownDevices.setObject(deviceInfo, userId, deviceId) + continue + } + if (deviceInfo!!.isBlocked) { + // Remove any blocked devices + continue + } - if (warnOnUnknownDevicesRepository.warnOnUnknownDevices() && deviceInfo!!.isUnknown) { - // The device is not yet known by the user - unknownDevices.setObject(deviceInfo, userId, deviceId) - continue + if (!deviceInfo.isVerified && encryptToVerifiedDevicesOnly) { + continue + } + + if (TextUtils.equals(deviceInfo.identityKey(), olmDevice.deviceCurve25519Key)) { + // Don't bother sending to ourself + continue + } + devicesInRoom.setObject(deviceInfo, userId, deviceId) } - - if (deviceInfo!!.isBlocked) { - // Remove any blocked devices - continue - } - - if (!deviceInfo.isVerified && encryptToVerifiedDevicesOnly) { - continue - } - - if (TextUtils.equals(deviceInfo.identityKey(), olmDevice.deviceCurve25519Key)) { - // Don't bother sending to ourself - continue - } - - devicesInRoom.setObject(deviceInfo, userId, deviceId) } + devicesInRoom } - - CryptoAsyncHelper.getUiHandler().post { - // Check if any of these devices are not yet known to the user. - // if so, warn the user so they can verify or ignore. - if (unknownDevices.map.isNotEmpty()) { - callback.onFailure(Failure.CryptoError(MXCryptoError(MXCryptoError.UNKNOWN_DEVICES_CODE, - MXCryptoError.UNABLE_TO_ENCRYPT, MXCryptoError.UNKNOWN_DEVICES_REASON, unknownDevices))) - } else { - callback.onSuccess(devicesInRoom) - } - - } - } - - override fun onFailure(failure: Throwable) { - callback.onFailure(failure) - } - }) } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmEncryptionFactory.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmEncryptionFactory.kt index 4e67069524..9463d6ac42 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmEncryptionFactory.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmEncryptionFactory.kt @@ -50,7 +50,6 @@ internal class MXMegolmEncryptionFactory( mEnsureOlmSessionsForDevicesAction, mCredentials, mSendToDeviceTask, - mTaskExecutor, mMessageEncrypter, mWarnOnUnknownDevicesRepository) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXOutboundSessionInfo.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXOutboundSessionInfo.kt index 6a2fcec9a1..5280ae0dfe 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXOutboundSessionInfo.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXOutboundSessionInfo.kt @@ -23,23 +23,23 @@ import timber.log.Timber internal class MXOutboundSessionInfo( // The id of the session - val mSessionId: String) { + val sessionId: String) { // When the session was created - private val mCreationTime = System.currentTimeMillis() + private val creationTime = System.currentTimeMillis() // Number of times this session has been used - var mUseCount: Int = 0 + var useCount: Int = 0 // Devices with which we have shared the session key // userId -> {deviceId -> msgindex} - val mSharedWithDevices: MXUsersDevicesMap = MXUsersDevicesMap() + val sharedWithDevices: MXUsersDevicesMap = MXUsersDevicesMap() fun needsRotation(rotationPeriodMsgs: Int, rotationPeriodMs: Int): Boolean { var needsRotation = false - val sessionLifetime = System.currentTimeMillis() - mCreationTime + val sessionLifetime = System.currentTimeMillis() - creationTime - if (mUseCount >= rotationPeriodMsgs || sessionLifetime >= rotationPeriodMs) { - Timber.v("## needsRotation() : Rotating megolm session after " + mUseCount + ", " + sessionLifetime + "ms") + if (useCount >= rotationPeriodMsgs || sessionLifetime >= rotationPeriodMs) { + Timber.v("## needsRotation() : Rotating megolm session after " + useCount + ", " + sessionLifetime + "ms") needsRotation = true } @@ -53,7 +53,7 @@ internal class MXOutboundSessionInfo( * @return true if we have shared the session with devices which aren't in devicesInRoom. */ fun sharedWithTooManyDevices(devicesInRoom: MXUsersDevicesMap): Boolean { - val userIds = mSharedWithDevices.userIds + val userIds = sharedWithDevices.userIds for (userId in userIds) { if (null == devicesInRoom.getUserDeviceIds(userId)) { @@ -61,7 +61,7 @@ internal class MXOutboundSessionInfo( return true } - val deviceIds = mSharedWithDevices.getUserDeviceIds(userId) + val deviceIds = sharedWithDevices.getUserDeviceIds(userId) for (deviceId in deviceIds!!) { if (null == devicesInRoom.getObject(deviceId, userId)) { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmDecryption.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmDecryption.kt index c1f6b40a05..1630062922 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmDecryption.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmDecryption.kt @@ -44,7 +44,7 @@ internal class MXOlmDecryption( : IMXDecrypting { @Throws(MXDecryptionException::class) - override fun decryptEvent(event: Event, timeline: String): MXEventDecryptionResult? { + override suspend fun decryptEvent(event: Event, timeline: String): MXEventDecryptionResult? { val olmEventContent = event.content.toModel()!! if (null == olmEventContent.ciphertext) { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmEncryption.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmEncryption.kt index b35ec6df2e..ad0680a424 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmEncryption.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmEncryption.kt @@ -19,7 +19,7 @@ package im.vector.matrix.android.internal.crypto.algorithms.olm import android.text.TextUtils -import im.vector.matrix.android.api.MatrixCallback +import arrow.core.Try import im.vector.matrix.android.api.session.events.model.Content import im.vector.matrix.android.api.session.events.model.toContent import im.vector.matrix.android.internal.crypto.DeviceListManager @@ -28,12 +28,7 @@ import im.vector.matrix.android.internal.crypto.actions.EnsureOlmSessionsForUser import im.vector.matrix.android.internal.crypto.actions.MessageEncrypter import im.vector.matrix.android.internal.crypto.algorithms.IMXEncrypting import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo -import im.vector.matrix.android.internal.crypto.model.MXOlmSessionResult -import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore -import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch import java.util.* internal class MXOlmEncryption( @@ -42,38 +37,28 @@ internal class MXOlmEncryption( private val cryptoStore: IMXCryptoStore, private val messageEncrypter: MessageEncrypter, private val deviceListManager: DeviceListManager, - private val coroutineDispatchers: MatrixCoroutineDispatchers, private val ensureOlmSessionsForUsersAction: EnsureOlmSessionsForUsersAction) : IMXEncrypting { - override fun encryptEventContent(eventContent: Content, - eventType: String, - userIds: List, - callback: MatrixCallback) { + override suspend fun encryptEventContent(eventContent: Content, eventType: String, userIds: List): Try { // pick the list of recipients based on the membership list. // // TODO: there is a race condition here! What if a new user turns up - CoroutineScope(coroutineDispatchers.crypto).launch { - ensureSession(userIds, object : MatrixCallback { - override fun onSuccess(data: Unit) { + return ensureSession(userIds) + .map { val deviceInfos = ArrayList() - for (userId in userIds) { val devices = cryptoStore.getUserDevices(userId)?.values ?: emptyList() - for (device in devices) { val key = device.identityKey() - if (TextUtils.equals(key, olmDevice.deviceCurve25519Key)) { // Don't bother setting up session to ourself continue } - if (device.isBlocked) { // Don't bother setting up sessions with blocked users continue } - deviceInfos.add(device) } } @@ -84,37 +69,22 @@ internal class MXOlmEncryption( messageMap["content"] = eventContent messageEncrypter.encryptMessage(messageMap, deviceInfos) - - callback.onSuccess(messageMap.toContent()!!) + messageMap.toContent()!! } - }) - } } + /** * Ensure that the session * * @param users the user ids list * @param callback the asynchronous callback */ - private fun ensureSession(users: List, callback: MatrixCallback?) { - deviceListManager.downloadKeys(users, false, object : MatrixCallback> { + private suspend fun ensureSession(users: List): Try { + return deviceListManager + .downloadKeys(users, false) + .flatMap { ensureOlmSessionsForUsersAction.handle(users) } + .map { Unit } - override fun onSuccess(data: MXUsersDevicesMap) { - ensureOlmSessionsForUsersAction.handle(users, object : MatrixCallback> { - override fun onSuccess(data: MXUsersDevicesMap) { - callback?.onSuccess(Unit) - } - - override fun onFailure(failure: Throwable) { - callback?.onFailure(failure) - } - }) - } - - override fun onFailure(failure: Throwable) { - callback?.onFailure(failure) - } - }) } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmEncryptionFactory.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmEncryptionFactory.kt index eda5fc3788..c7df56cda9 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmEncryptionFactory.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmEncryptionFactory.kt @@ -37,7 +37,6 @@ internal class MXOlmEncryptionFactory(private val mOlmDevice: MXOlmDevice, mCryptoStore, mMessageEncrypter, mDeviceListManager, - coroutineDispatchers, mEnsureOlmSessionsForUsersAction) } } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/RealmCryptoStore.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/RealmCryptoStore.kt index 72fa628bad..78fce2e264 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/RealmCryptoStore.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/RealmCryptoStore.kt @@ -603,11 +603,11 @@ internal class RealmCryptoStore(private val enableFileEncryption: Boolean = fals } override fun getOrAddOutgoingRoomKeyRequest(request: OutgoingRoomKeyRequest): OutgoingRoomKeyRequest? { - if (request.mRequestBody == null) { + if (request.requestBody == null) { return null } - val existingOne = getOutgoingRoomKeyRequest(request.mRequestBody!!) + val existingOne = getOutgoingRoomKeyRequest(request.requestBody!!) if (existingOne != null) { return existingOne @@ -615,11 +615,11 @@ internal class RealmCryptoStore(private val enableFileEncryption: Boolean = fals // Insert the request and return the one passed in parameter doRealmTransaction(realmConfiguration) { - it.createObject(OutgoingRoomKeyRequestEntity::class.java, request.mRequestId).apply { - putRequestBody(request.mRequestBody) - putRecipients(request.mRecipients) - cancellationTxnId = request.mCancellationTxnId - state = request.mState.ordinal + it.createObject(OutgoingRoomKeyRequestEntity::class.java, request.requestId).apply { + putRequestBody(request.requestBody) + putRecipients(request.recipients) + cancellationTxnId = request.cancellationTxnId + state = request.state.ordinal } } @@ -638,11 +638,11 @@ internal class RealmCryptoStore(private val enableFileEncryption: Boolean = fals override fun updateOutgoingRoomKeyRequest(request: OutgoingRoomKeyRequest) { doRealmTransaction(realmConfiguration) { val obj = OutgoingRoomKeyRequestEntity().apply { - requestId = request.mRequestId - cancellationTxnId = request.mCancellationTxnId - state = request.mState.ordinal - putRecipients(request.mRecipients) - putRequestBody(request.mRequestBody) + requestId = request.requestId + cancellationTxnId = request.cancellationTxnId + state = request.state.ordinal + putRecipients(request.recipients) + putRequestBody(request.requestBody) } it.insertOrUpdate(obj) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/OutgoingRoomKeyRequestEntity.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/OutgoingRoomKeyRequestEntity.kt index f4cc732ec6..cbbc2028d3 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/OutgoingRoomKeyRequestEntity.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/OutgoingRoomKeyRequestEntity.kt @@ -52,7 +52,7 @@ internal open class OutgoingRoomKeyRequestEntity( requestId!!, OutgoingRoomKeyRequest.RequestState.from(state) ).apply { - this.mCancellationTxnId = cancellationTxnId + this.cancellationTxnId = cancellationTxnId } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/MatrixModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/MatrixModule.kt index 638695992c..1400fc3862 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/MatrixModule.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/MatrixModule.kt @@ -17,13 +17,13 @@ package im.vector.matrix.android.internal.di import android.content.Context +import im.vector.matrix.android.internal.crypto.CryptoAsyncHelper import im.vector.matrix.android.internal.task.TaskExecutor import im.vector.matrix.android.internal.util.BackgroundDetectionObserver import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.asCoroutineDispatcher +import kotlinx.coroutines.android.asCoroutineDispatcher import org.koin.dsl.module.module -import java.util.concurrent.Executors class MatrixModule(private val context: Context) { @@ -35,10 +35,11 @@ class MatrixModule(private val context: Context) { } single { + val cryptoHandler = CryptoAsyncHelper.getDecryptBackgroundHandler() MatrixCoroutineDispatchers(io = Dispatchers.IO, - computation = Dispatchers.IO, - main = Dispatchers.Main, - crypto = Executors.newSingleThreadExecutor().asCoroutineDispatcher() + computation = Dispatchers.IO, + main = Dispatchers.Main, + crypto = cryptoHandler.asCoroutineDispatcher("crypto") ) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/TimelineEventFactory.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/TimelineEventFactory.kt index f8aae7adfa..1045399220 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/TimelineEventFactory.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/TimelineEventFactory.kt @@ -16,9 +16,11 @@ package im.vector.matrix.android.internal.session.room.timeline +import im.vector.matrix.android.api.failure.Failure import im.vector.matrix.android.api.session.crypto.CryptoService import im.vector.matrix.android.api.session.events.model.EventType import im.vector.matrix.android.api.session.room.timeline.TimelineEvent +import im.vector.matrix.android.internal.crypto.MXDecryptionException import im.vector.matrix.android.internal.database.mapper.asDomain import im.vector.matrix.android.internal.database.model.EventEntity import im.vector.matrix.android.internal.session.room.members.SenderRoomMemberExtractor @@ -44,6 +46,9 @@ internal class TimelineEventFactory(private val roomMemberExtractor: SenderRoomM event.setClearData(result) } catch (e: Exception) { Timber.e(e) + if (e is MXDecryptionException) { + event.setCryptoError(e.cryptoError) + } } } return TimelineEvent( diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/RoomSyncHandler.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/RoomSyncHandler.kt index 7963527c3e..102eabc1ee 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/RoomSyncHandler.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/RoomSyncHandler.kt @@ -97,7 +97,6 @@ internal class RoomSyncHandler(private val monarchy: Monarchy, roomEntity.addStateEvents(roomSync.state.events, filterDuplicates = true, stateIndex = untimelinedStateIndex) // Give info to crypto module - // TODO Remove roomSync.state.events.forEach { mCrypto.onStateEvent(roomId, it) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncResponseHandler.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncResponseHandler.kt index ba5e6ab661..1beccb6e50 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncResponseHandler.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncResponseHandler.kt @@ -49,7 +49,9 @@ internal class SyncResponseHandler(private val roomSyncHandler: RoomSyncHandler, cryptoSyncHandler.onSyncCompleted(syncResponse, fromToken, isCatchingUp) } val isInitialSync = fromToken == null - cryptoManager.start(isInitialSync) + if (!cryptoManager.isStarted()) { + cryptoManager.start(isInitialSync) + } Timber.v("Finish handling sync in $measure ms") syncResponse } diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/factory/EncryptedItemFactory.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/factory/EncryptedItemFactory.kt index 2040408afa..6010578af4 100644 --- a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/factory/EncryptedItemFactory.kt +++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/factory/EncryptedItemFactory.kt @@ -45,15 +45,12 @@ class EncryptedItemFactory( return when { EventType.ENCRYPTED == timelineEvent.root.getClearType() -> { - val decrypted: MXEventDecryptionResult? - try { - decrypted = session.decryptEvent(timelineEvent.root, "TODO") - } catch (e: MXDecryptionException) { + val cryptoError = timelineEvent.root.mCryptoError val errorDescription = - if (e.cryptoError?.code == MXCryptoError.UNKNOWN_INBOUND_SESSION_ID_ERROR_CODE) { + if (cryptoError?.code == MXCryptoError.UNKNOWN_INBOUND_SESSION_ID_ERROR_CODE) { stringProvider.getString(R.string.notice_crypto_error_unkwown_inbound_session_id) } else { - e.localizedMessage + cryptoError?.message } val message = stringProvider.getString(R.string.notice_crypto_unable_to_decrypt, errorDescription) @@ -65,20 +62,6 @@ class EncryptedItemFactory( .noticeText(spannableStr) .avatarUrl(timelineEvent.senderAvatar) .memberName(timelineEvent.senderName) - } - - if (decrypted == null) { - return null - } - if (decrypted.clearEvent == null) { - return null - } - val adapter = MoshiProvider.providesMoshi().adapter(Event::class.java) - val clearEvent = adapter.fromJsonValue(decrypted.clearEvent) ?: return null - val decryptedTimelineEvent = timelineEvent.copy(root = clearEvent) - - // Success - return messageItemFactory.create(decryptedTimelineEvent, nextEvent, callback) } else -> null }