diff --git a/CHANGES.md b/CHANGES.md index ba3ad08037..a3eb4bfdf9 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -41,6 +41,7 @@ Improvements 🙌: - Improve room profile UX - Upgrade Jitsi library from 2.9.3 to 3.1.0 - a11y improvements + - Pre-share session keys when opening a room or start typing (#2771) Bugfix 🐛: - VoIP : fix audio devices output diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/MatrixConfiguration.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/MatrixConfiguration.kt index 93a1b962ed..c01aeab6dd 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/MatrixConfiguration.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/MatrixConfiguration.kt @@ -17,6 +17,7 @@ package org.matrix.android.sdk.api import org.matrix.android.sdk.api.crypto.MXCryptoConfig +import org.matrix.android.sdk.api.crypto.OutboundSessionKeySharingStrategy import java.net.Proxy data class MatrixConfiguration( @@ -40,6 +41,7 @@ data class MatrixConfiguration( * True to advertise support for call transfers to other parties on Matrix calls. */ val supportsCallTransfer: Boolean = false + val outboundSessionKeySharingStrategy: OutboundSessionKeySharingStrategy = OutboundSessionKeySharingStrategy.WhenSendingEvent ) { /** diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/crypto/OutboundSessionKeySharingStrategy.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/crypto/OutboundSessionKeySharingStrategy.kt new file mode 100644 index 0000000000..9ef1f538b8 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/crypto/OutboundSessionKeySharingStrategy.kt @@ -0,0 +1,34 @@ +/* + * Copyright 2020 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.api.crypto + +enum class OutboundSessionKeySharingStrategy { + /** + * Keys will be sent for the first time when the first message is sent + */ + WhenSendingEvent, + + /** + * Keys will be sent for the first time when the timeline displayed + */ + WhenEnteringRoom, + + /** + * Keys will be sent for the first time when a typing started + */ + WhenTyping +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/CryptoService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/CryptoService.kt index eead9b4ab7..1c5bfb1a7b 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/CryptoService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/CryptoService.kt @@ -156,4 +156,6 @@ interface CryptoService { fun getWithHeldMegolmSession(roomId: String, sessionId: String): RoomKeyWithHeldContent? fun logDbUsageInfo() + + fun ensureOutboundSession(roomId: String) } 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 67229a5eae..821ac18e3a 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 @@ -1290,6 +1290,21 @@ internal class DefaultCryptoService @Inject constructor( cryptoStore.logDbUsageInfo() } + override fun ensureOutboundSession(roomId: String) { + cryptoCoroutineScope.launch(coroutineDispatchers.crypto) { + roomEncryptorsStore + .get(roomId) + ?.let { + getEncryptionAlgorithm(roomId)?.let { safeAlgorithm -> + val userIds = getRoomUserIds(roomId) + if (setEncryptionInRoom(roomId, safeAlgorithm, false, userIds)) { + roomEncryptorsStore.get(roomId)?.ensureOutboundSession(getRoomUserIds(roomId)) + } + } + } + } + } + /* ========================================================================================== * For test only * ========================================================================================== */ diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/IMXEncrypting.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/IMXEncrypting.kt index fc3ea08a21..008ef1b084 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/IMXEncrypting.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/IMXEncrypting.kt @@ -62,4 +62,11 @@ internal interface IMXEncrypting { userId: String, deviceId: String, senderKey: String): Boolean + + /** + * Ensure the outbound session + * + * @param usersInRoom the users in the room + */ + suspend fun ensureOutboundSession(usersInRoom: List) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmEncryption.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmEncryption.kt index fa8acafb83..3a07dbcfb6 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmEncryption.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmEncryption.kt @@ -137,11 +137,10 @@ internal class MXMegolmEncryption( return MXOutboundSessionInfo(sessionId, SharedWithHelper(roomId, sessionId, cryptoStore)) } - /** - * Ensure the outbound session - * - * @param devicesInRoom the devices list - */ + override suspend fun ensureOutboundSession(usersInRoom: List) { + getDevicesInRoom(usersInRoom).allowedDevices + } + private suspend fun ensureOutboundSession(devicesInRoom: MXUsersDevicesMap): MXOutboundSessionInfo { Timber.v("## CRYPTO | ensureOutboundSession start") var session = outboundSession diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/olm/MXOlmEncryption.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/olm/MXOlmEncryption.kt index 9acc9bc1b2..acea6ff3fe 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/olm/MXOlmEncryption.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/olm/MXOlmEncryption.kt @@ -85,4 +85,8 @@ internal class MXOlmEncryption( // No need for olm return false } + + override suspend fun ensureOutboundSession(usersInRoom: List) { + // NOOP + } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimeline.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimeline.kt index ca272a3520..e75beb4b21 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimeline.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimeline.kt @@ -24,9 +24,13 @@ import io.realm.RealmQuery import io.realm.RealmResults import io.realm.Sort import org.matrix.android.sdk.api.MatrixCallback +import org.matrix.android.sdk.api.MatrixConfiguration import org.matrix.android.sdk.api.NoOpMatrixCallback +import org.matrix.android.sdk.api.crypto.OutboundSessionKeySharingStrategy import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.extensions.tryOrNull +import org.matrix.android.sdk.api.session.Session +import org.matrix.android.sdk.api.session.crypto.CryptoService import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.RelationType import org.matrix.android.sdk.api.session.events.model.toModel @@ -80,8 +84,11 @@ internal class DefaultTimeline( private val timelineInput: TimelineInput, private val eventDecryptor: TimelineEventDecryptor, private val realmSessionProvider: RealmSessionProvider, - private val loadRoomMembersTask: LoadRoomMembersTask -) : Timeline, + private val loadRoomMembersTask: LoadRoomMembersTask, + private val session: Session, + private val matrixConfiguration: MatrixConfiguration, + private val cryptoService: CryptoService + ) : Timeline, TimelineHiddenReadReceipts.Delegate, TimelineInput.Listener { @@ -188,6 +195,11 @@ internal class DefaultTimeline( } .executeBy(taskExecutor) + if (session.getRoom(roomId)?.isEncrypted().orFalse() + && matrixConfiguration.outboundSessionKeySharingStrategy == OutboundSessionKeySharingStrategy.WhenEnteringRoom) { + cryptoService.ensureOutboundSession(roomId) + } + isReady.set(true) } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/typing/SendTypingTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/typing/SendTypingTask.kt index 3b56d04872..aa8dcf6013 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/typing/SendTypingTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/typing/SendTypingTask.kt @@ -21,6 +21,11 @@ import org.matrix.android.sdk.internal.network.executeRequest import org.matrix.android.sdk.internal.session.room.RoomAPI import org.matrix.android.sdk.internal.task.Task import kotlinx.coroutines.delay +import org.matrix.android.sdk.api.MatrixConfiguration +import org.matrix.android.sdk.api.crypto.OutboundSessionKeySharingStrategy +import org.matrix.android.sdk.api.extensions.orFalse +import org.matrix.android.sdk.api.session.Session +import org.matrix.android.sdk.api.session.crypto.CryptoService import org.matrix.android.sdk.internal.network.GlobalErrorReceiver import javax.inject.Inject @@ -38,12 +43,21 @@ internal interface SendTypingTask : Task { internal class DefaultSendTypingTask @Inject constructor( private val roomAPI: RoomAPI, @UserId private val userId: String, - private val globalErrorReceiver: GlobalErrorReceiver + private val globalErrorReceiver: GlobalErrorReceiver, + private val matrixConfiguration: MatrixConfiguration, + private val session: Session, + private val cryptoService: CryptoService ) : SendTypingTask { override suspend fun execute(params: SendTypingTask.Params) { delay(params.delay ?: -1) + if (params.isTyping + && session.getRoom(params.roomId)?.isEncrypted().orFalse() + && matrixConfiguration.outboundSessionKeySharingStrategy == OutboundSessionKeySharingStrategy.WhenTyping) { + cryptoService.ensureOutboundSession(params.roomId) + } + executeRequest(globalErrorReceiver) { apiCall = roomAPI.sendTypingState( params.roomId, diff --git a/vector/build.gradle b/vector/build.gradle index 5543d27d95..a257f6aaf6 100644 --- a/vector/build.gradle +++ b/vector/build.gradle @@ -136,6 +136,8 @@ android { buildConfigField "String", "BUILD_NUMBER", "\"${buildNumber}\"" resValue "string", "build_number", "\"${buildNumber}\"" + buildConfigField "org.matrix.android.sdk.api.crypto.OutboundSessionKeySharingStrategy", "OutboundSessionKeySharingStrategy", "org.matrix.android.sdk.api.crypto.OutboundSessionKeySharingStrategy.WhenTyping" + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" // Keep abiFilter for the universalApk diff --git a/vector/src/main/java/im/vector/app/VectorApplication.kt b/vector/src/main/java/im/vector/app/VectorApplication.kt index f8cda2c417..1926a35c9c 100644 --- a/vector/src/main/java/im/vector/app/VectorApplication.kt +++ b/vector/src/main/java/im/vector/app/VectorApplication.kt @@ -205,7 +205,7 @@ class VectorApplication : } } - override fun providesMatrixConfiguration() = MatrixConfiguration(BuildConfig.FLAVOR_DESCRIPTION) + override fun providesMatrixConfiguration() = MatrixConfiguration(applicationFlavor = BuildConfig.FLAVOR_DESCRIPTION, outboundSessionKeySharingStrategy = BuildConfig.OutboundSessionKeySharingStrategy) override fun getWorkManagerConfiguration(): WorkConfiguration { return WorkConfiguration.Builder()