diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt index 1a006c51c5..dffc100e7a 100755 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt @@ -134,6 +134,7 @@ internal class DefaultCryptoService @Inject constructor( private val crossSigningService: CrossSigningService, private val verificationService: RustVerificationService, private val keysBackupService: RustKeyBackupService, + private val megolmSessionImportManager: MegolmSessionImportManager, private val olmMachineProvider: OlmMachineProvider ) : CryptoService { @@ -152,9 +153,6 @@ internal class DefaultCryptoService @Inject constructor( private val outgoingRequestsLock: Mutex = Mutex() private val roomKeyShareLocks: ConcurrentHashMap = ConcurrentHashMap() - // TODO does this need to be concurrent? - private val newSessionListeners = ArrayList() - fun onStateEvent(roomId: String, event: Event) { when (event.getClearType()) { EventType.STATE_ROOM_ENCRYPTION -> onRoomEncryptionEvent(roomId, event) @@ -675,17 +673,7 @@ internal class DefaultCryptoService @Inject constructor( roomId: String, sessionId: String, ) { - // The sender key is actually unused since it's unimportant for megolm - // Our events don't contain the info so pass an empty string until we - // change the listener definition - val senderKey = "" - - newSessionListeners.forEach { - try { - it.onNewSession(roomId, senderKey, sessionId) - } catch (e: Throwable) { - } - } + megolmSessionImportManager.dispatchNewSession(roomId, sessionId) } suspend fun receiveSyncChanges( @@ -886,7 +874,9 @@ internal class DefaultCryptoService @Inject constructor( override suspend fun importRoomKeys(roomKeysAsArray: ByteArray, password: String, progressListener: ProgressListener?): ImportRoomKeysResult { - val result = olmMachine.importKeys(roomKeysAsArray, password, progressListener) + val result = olmMachine.importKeys(roomKeysAsArray, password, progressListener).also { + megolmSessionImportManager.dispatchKeyImportResults(it) + } keysBackupService.maybeBackupKeys() return result @@ -1030,11 +1020,11 @@ internal class DefaultCryptoService @Inject constructor( } override fun addNewSessionListener(newSessionListener: NewSessionListener) { - if (!newSessionListeners.contains(newSessionListener)) newSessionListeners.add(newSessionListener) + megolmSessionImportManager.addListener(newSessionListener) } override fun removeSessionListener(listener: NewSessionListener) { - newSessionListeners.remove(listener) + megolmSessionImportManager.removeListener(listener) } /* ========================================================================================== * DEBUG INFO diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/IncomingGossipingRequestManager.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/IncomingGossipingRequestManager.kt index 220f25ec80..cf7a373258 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/IncomingGossipingRequestManager.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/IncomingGossipingRequestManager.kt @@ -44,6 +44,7 @@ import timber.log.Timber import java.util.concurrent.Executors import javax.inject.Inject +@Deprecated("rust") @SessionScope internal class IncomingGossipingRequestManager @Inject constructor( @SessionId private val sessionId: String, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/MegolmSessionImportManager.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/MegolmSessionImportManager.kt new file mode 100644 index 0000000000..93f0c12caf --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/MegolmSessionImportManager.kt @@ -0,0 +1,77 @@ +/* + * Copyright 2021 The Matrix.org Foundation C.I.C. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.android.sdk.internal.crypto + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch +import org.matrix.android.sdk.api.MatrixCoroutineDispatchers +import org.matrix.android.sdk.api.extensions.tryOrNull +import org.matrix.android.sdk.internal.crypto.model.ImportRoomKeysResult +import org.matrix.android.sdk.internal.session.SessionScope +import javax.inject.Inject + +/** + * Helper that allows listeners to be notified when a new megolm session + * has been added to the crypto layer (could be via room keys or forward keys via sync + * or after importing keys from key backup or manual import). + * Can be used to refresh display when the keys are received after the message + */ +@SessionScope +internal class MegolmSessionImportManager @Inject constructor( + private val coroutineDispatchers: MatrixCoroutineDispatchers, + private val cryptoCoroutineScope: CoroutineScope +) { + + private val newSessionsListeners = mutableListOf() + + fun addListener(listener: NewSessionListener) { + synchronized(newSessionsListeners) { + if (!newSessionsListeners.contains(listener)) { + newSessionsListeners.add(listener) + } + } + } + + fun removeListener(listener: NewSessionListener) { + synchronized(newSessionsListeners) { + newSessionsListeners.remove(listener) + } + } + + fun dispatchNewSession(roomId: String?, sessionId: String) { + val copy = synchronized(newSessionsListeners) { + newSessionsListeners.toList() + } + cryptoCoroutineScope.launch(coroutineDispatchers.computation) { + copy.forEach { + tryOrNull("Failed to dispatch new session import") { + it.onNewSession(roomId, sessionId) + } + } + } + } + + fun dispatchKeyImportResults(result: ImportRoomKeysResult) { + result.importedSessionInfo.forEach { (roomId, senderToSessionIdMap) -> + senderToSessionIdMap.values.forEach { sessionList -> + sessionList.forEach { sessionId -> + dispatchNewSession(roomId, sessionId) + } + } + } + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/NewSessionListener.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/NewSessionListener.kt index 301729680c..19f89b2f1e 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/NewSessionListener.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/NewSessionListener.kt @@ -16,5 +16,5 @@ package org.matrix.android.sdk.internal.crypto interface NewSessionListener { - fun onNewSession(roomId: String?, senderKey: String, sessionId: String) + fun onNewSession(roomId: String?, sessionId: String) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/OlmMachine.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/OlmMachine.kt index cfe12901f5..2d09d000a9 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/OlmMachine.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/OlmMachine.kt @@ -510,8 +510,7 @@ internal class OlmMachine( val result = inner.importKeys(decodedKeys, passphrase, rustListener) - // TODO do we want to remove the cast here? - ImportRoomKeysResult(result.total.toInt(), result.imported.toInt()) + ImportRoomKeysResult.fromOlm(result) } @Throws(CryptoStoreException::class) @@ -520,13 +519,14 @@ internal class OlmMachine( listener: ProgressListener? ): ImportRoomKeysResult = withContext(Dispatchers.IO) { - val adapter = MoshiProvider.providesMoshi().adapter(List::class.java) // If the key backup is too big we take the risk of causing OOM + // when serializing to json // so let's chunk to avoid it var totalImported = 0L var accTotal = 0L + val details = mutableMapOf>>() keys.chunked(500) .forEach { keysSlice -> val encodedKeys = adapter.toJson(keysSlice) @@ -540,9 +540,10 @@ internal class OlmMachine( inner.importDecryptedKeys(encodedKeys, rustListener).let { totalImported += it.imported accTotal += it.total + details.putAll(it.keys) } } - ImportRoomKeysResult(totalImported.toInt(), accTotal.toInt()) + ImportRoomKeysResult(totalImported.toInt(), accTotal.toInt(), details) } @Throws(CryptoStoreException::class) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/RoomDecryptorProvider.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/RoomDecryptorProvider.kt index 89fb43ef2e..dd81eae99a 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/RoomDecryptorProvider.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/RoomDecryptorProvider.kt @@ -72,11 +72,11 @@ internal class RoomDecryptorProvider @Inject constructor( val alg = when (algorithm) { MXCRYPTO_ALGORITHM_MEGOLM -> megolmDecryptionFactory.create().apply { this.newSessionListener = object : NewSessionListener { - override fun onNewSession(roomId: String?, senderKey: String, sessionId: String) { + override fun onNewSession(roomId: String?, sessionId: String) { // PR reviewer: the parameter has been renamed so is now in conflict with the parameter of getOrCreateRoomDecryptor newSessionListeners.forEach { try { - it.onNewSession(roomId, senderKey, sessionId) + it.onNewSession(roomId, sessionId) } catch (e: Throwable) { } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/actions/MegolmSessionDataImporter.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/actions/MegolmSessionDataImporter.kt index e0748a0d1f..6c8c1c4637 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/actions/MegolmSessionDataImporter.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/actions/MegolmSessionDataImporter.kt @@ -103,6 +103,6 @@ internal class MegolmSessionDataImporter @Inject constructor(private val olmDevi Timber.v("## importMegolmSessionsData : sessions import " + (t1 - t0) + " ms (" + megolmSessionsData.size + " sessions)") - return ImportRoomKeysResult(totalNumbersOfKeys, totalNumbersOfImportedKeys) + return ImportRoomKeysResult(totalNumbersOfKeys, totalNumbersOfImportedKeys, emptyMap()) } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt index ea239dad53..20658f7db4 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt @@ -317,7 +317,7 @@ internal class MXMegolmDecryption(private val userId: String, */ override fun onNewSession(senderKey: String, sessionId: String) { Timber.tag(loggerTag.value).v("ON NEW SESSION $sessionId - $senderKey") - newSessionListener?.onNewSession(null, senderKey, sessionId) + newSessionListener?.onNewSession(null, sessionId) } override fun hasKeysForKeyRequest(request: IncomingRoomKeyRequest): Boolean { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/RustKeyBackupService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/RustKeyBackupService.kt index 3320ec089a..5410ab07df 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/RustKeyBackupService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/RustKeyBackupService.kt @@ -39,6 +39,7 @@ import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysBackupState import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysBackupStateListener import org.matrix.android.sdk.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM_BACKUP import org.matrix.android.sdk.internal.crypto.MegolmSessionData +import org.matrix.android.sdk.internal.crypto.MegolmSessionImportManager import org.matrix.android.sdk.internal.crypto.OlmMachineProvider import org.matrix.android.sdk.internal.crypto.RequestSender import org.matrix.android.sdk.internal.crypto.keysbackup.model.KeysBackupVersionTrust @@ -74,6 +75,7 @@ internal class RustKeyBackupService @Inject constructor( olmMachineProvider: OlmMachineProvider, private val sender: RequestSender, private val coroutineDispatchers: MatrixCoroutineDispatchers, + private val megolmSessionImportManager: MegolmSessionImportManager, private val cryptoCoroutineScope: CoroutineScope, ) : KeysBackupService { companion object { @@ -545,7 +547,9 @@ internal class RustKeyBackupService @Inject constructor( null } - val result = olmMachine.importDecryptedKeys(sessionsData, progressListener) + val result = olmMachine.importDecryptedKeys(sessionsData, progressListener).also { + megolmSessionImportManager.dispatchKeyImportResults(it) + } // Do not back up the key if it comes from a backup recovery if (backUp) { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/ImportRoomKeysResult.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/ImportRoomKeysResult.kt index e9d2a1bcd8..d04936bdde 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/ImportRoomKeysResult.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/ImportRoomKeysResult.kt @@ -16,5 +16,21 @@ package org.matrix.android.sdk.internal.crypto.model +import uniffi.olm.KeysImportResult + data class ImportRoomKeysResult(val totalNumberOfKeys: Int, - val successfullyNumberOfImportedKeys: Int) + val successfullyNumberOfImportedKeys: Int, + /**It's a map from room id to a map of the sender key to a list of session*/ + val importedSessionInfo: Map>> +) { + + companion object { + fun fromOlm(result: KeysImportResult): ImportRoomKeysResult { + return ImportRoomKeysResult( + result.total.toInt(), + result.imported.toInt(), + result.keys + ) + } + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TimelineEventDecryptor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TimelineEventDecryptor.kt index 75d02dfd98..f93f72d312 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TimelineEventDecryptor.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TimelineEventDecryptor.kt @@ -40,7 +40,7 @@ internal class TimelineEventDecryptor @Inject constructor( ) { private val newSessionListener = object : NewSessionListener { - override fun onNewSession(roomId: String?, senderKey: String, sessionId: String) { + override fun onNewSession(roomId: String?, sessionId: String) { synchronized(unknownSessionsFailure) { unknownSessionsFailure[sessionId] ?.toList()