diff --git a/changelog.d/6146.feature b/changelog.d/6146.feature new file mode 100644 index 0000000000..9d1e117ddb --- /dev/null +++ b/changelog.d/6146.feature @@ -0,0 +1 @@ +Allow .well-known configuration to override key sharing mode diff --git a/vector/src/main/java/im/vector/app/features/crypto/keysrequest/OutboundSessionKeySharingStrategy.kt b/vector/src/main/java/im/vector/app/features/crypto/keysrequest/OutboundSessionKeySharingStrategy.kt index 2018a5b053..5396ce908e 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/keysrequest/OutboundSessionKeySharingStrategy.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/keysrequest/OutboundSessionKeySharingStrategy.kt @@ -19,6 +19,7 @@ package im.vector.app.features.crypto.keysrequest enum class OutboundSessionKeySharingStrategy { /** * Keys will be sent for the first time when the first message is sent. + * This is handled by the Matrix SDK so there's no need to do it in Vector. */ WhenSendingEvent, diff --git a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt index 9fe8a1f60e..5d441d4b59 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt @@ -33,6 +33,7 @@ import im.vector.app.features.login.ReAuthHelper import im.vector.app.features.raw.wellknown.ElementWellKnown import im.vector.app.features.raw.wellknown.getElementWellknown import im.vector.app.features.raw.wellknown.isSecureBackupRequired +import im.vector.app.features.raw.wellknown.withElementWellKnown import im.vector.app.features.session.coroutineScope import im.vector.app.features.settings.VectorPreferences import kotlinx.coroutines.Dispatchers @@ -134,9 +135,8 @@ class HomeActivityViewModel @AssistedInject constructor( .onEach { info -> val isVerified = info.getOrNull()?.isTrusted() ?: false if (!isVerified && onceTrusted) { - viewModelScope.launch(Dispatchers.IO) { - val elementWellKnown = rawService.getElementWellknown(safeActiveSession.sessionParams) - sessionHasBeenUnverified(elementWellKnown) + rawService.withElementWellKnown(viewModelScope, safeActiveSession.sessionParams) { + sessionHasBeenUnverified(it) } } onceTrusted = isVerified diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineViewModel.kt index 86137b89f1..35ba710fe5 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineViewModel.kt @@ -29,7 +29,6 @@ import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import im.vector.app.AppStateHandler -import im.vector.app.BuildConfig import im.vector.app.R import im.vector.app.core.di.MavericksAssistedViewModelFactory import im.vector.app.core.di.hiltMavericksViewModelFactory @@ -56,6 +55,8 @@ import im.vector.app.features.home.room.typing.TypingHelper import im.vector.app.features.location.LocationSharingServiceConnection import im.vector.app.features.notifications.NotificationDrawerManager import im.vector.app.features.powerlevel.PowerLevelsFlowFactory +import im.vector.app.features.raw.wellknown.getOutboundSessionKeySharingStrategyOrDefault +import im.vector.app.features.raw.wellknown.withElementWellKnown import im.vector.app.features.session.coroutineScope import im.vector.app.features.settings.VectorDataStore import im.vector.app.features.settings.VectorPreferences @@ -76,6 +77,7 @@ import kotlinx.coroutines.withContext import org.matrix.android.sdk.api.MatrixPatterns import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.query.QueryStringValue +import org.matrix.android.sdk.api.raw.RawService import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.crypto.MXCryptoError import org.matrix.android.sdk.api.session.events.model.EventType @@ -118,6 +120,7 @@ class TimelineViewModel @AssistedInject constructor( private val vectorDataStore: VectorDataStore, private val stringProvider: StringProvider, private val session: Session, + private val rawService: RawService, private val supportedVerificationMethodsProvider: SupportedVerificationMethodsProvider, private val stickerPickerActionHandler: StickerPickerActionHandler, private val typingHelper: TypingHelper, @@ -196,8 +199,13 @@ class TimelineViewModel @AssistedInject constructor( chatEffectManager.delegate = this // Ensure to share the outbound session keys with all members - if (OutboundSessionKeySharingStrategy.WhenEnteringRoom == BuildConfig.outboundSessionKeySharingStrategy && room.roomCryptoService().isEncrypted()) { - prepareForEncryption() + if (room.roomCryptoService().isEncrypted()) { + rawService.withElementWellKnown(viewModelScope, session.sessionParams) { + val strategy = it.getOutboundSessionKeySharingStrategyOrDefault() + if (strategy == OutboundSessionKeySharingStrategy.WhenEnteringRoom) { + prepareForEncryption() + } + } } // If the user had already accepted the invitation in the room list @@ -667,10 +675,13 @@ class TimelineViewModel @AssistedInject constructor( private fun handleComposerFocusChange(action: RoomDetailAction.ComposerFocusChange) { // Ensure outbound session keys - if (OutboundSessionKeySharingStrategy.WhenTyping == BuildConfig.outboundSessionKeySharingStrategy && room.roomCryptoService().isEncrypted()) { - if (action.focused) { - // Should we add some rate limit here, or do it only once per model lifecycle? - prepareForEncryption() + if (room.roomCryptoService().isEncrypted()) { + rawService.withElementWellKnown(viewModelScope, session.sessionParams) { + val strategy = it.getOutboundSessionKeySharingStrategyOrDefault() + if (strategy == OutboundSessionKeySharingStrategy.WhenTyping && action.focused) { + // Should we add some rate limit here, or do it only once per model lifecycle? + prepareForEncryption() + } } } } diff --git a/vector/src/main/java/im/vector/app/features/raw/wellknown/ElementWellKnown.kt b/vector/src/main/java/im/vector/app/features/raw/wellknown/ElementWellKnown.kt index 3c4d514e47..0df5f0e9cf 100644 --- a/vector/src/main/java/im/vector/app/features/raw/wellknown/ElementWellKnown.kt +++ b/vector/src/main/java/im/vector/app/features/raw/wellknown/ElementWellKnown.kt @@ -65,7 +65,14 @@ data class E2EWellKnownConfig( * clients should fallback to the default value of: ["key", "passphrase"]. */ @Json(name = "secure_backup_setup_methods") - val secureBackupSetupMethods: List? = null + val secureBackupSetupMethods: List? = null, + + /** + * Configuration for sharing keys strategy which should be used instead of [im.vector.app.BuildConfig.outboundSessionKeySharingStrategy]. + * One of on_room_opening, on_typing or disabled. + */ + @Json(name = "outbound_keys_pre_sharing_mode") + val outboundsKeyPreSharingMode: String? = null, ) @JsonClass(generateAdapter = true) diff --git a/vector/src/main/java/im/vector/app/features/raw/wellknown/ElementWellKnownExt.kt b/vector/src/main/java/im/vector/app/features/raw/wellknown/ElementWellKnownExt.kt index fce91b8f15..73662613f7 100644 --- a/vector/src/main/java/im/vector/app/features/raw/wellknown/ElementWellKnownExt.kt +++ b/vector/src/main/java/im/vector/app/features/raw/wellknown/ElementWellKnownExt.kt @@ -16,6 +16,11 @@ package im.vector.app.features.raw.wellknown +import im.vector.app.BuildConfig +import im.vector.app.features.crypto.keysrequest.OutboundSessionKeySharingStrategy +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch import org.matrix.android.sdk.api.MatrixPatterns.getServerName import org.matrix.android.sdk.api.auth.data.SessionParams import org.matrix.android.sdk.api.extensions.tryOrNull @@ -30,6 +35,25 @@ suspend fun RawService.getElementWellknown(sessionParams: SessionParams): Elemen fun ElementWellKnown.isE2EByDefault() = elementE2E?.e2eDefault ?: riotE2E?.e2eDefault ?: true +fun ElementWellKnown?.getOutboundSessionKeySharingStrategyOrDefault(): OutboundSessionKeySharingStrategy { + return when (this?.elementE2E?.outboundsKeyPreSharingMode) { + "on_room_opening" -> OutboundSessionKeySharingStrategy.WhenEnteringRoom + "on_typing" -> OutboundSessionKeySharingStrategy.WhenTyping + "disabled" -> OutboundSessionKeySharingStrategy.WhenSendingEvent + else -> BuildConfig.outboundSessionKeySharingStrategy + } +} + +fun RawService.withElementWellKnown( + coroutineScope: CoroutineScope, + sessionParams: SessionParams, + block: ((ElementWellKnown?) -> Unit) +) = with(coroutineScope) { + launch(Dispatchers.IO) { + block(getElementWellknown(sessionParams)) + } +} + fun ElementWellKnown.isSecureBackupRequired() = elementE2E?.secureBackupRequired ?: riotE2E?.secureBackupRequired ?: false