From ae36846aaf834c1e4f579e4f03732dca688efaf9 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 29 Jan 2020 16:11:23 +0100 Subject: [PATCH] Negotiate E2E by default for DMs (#907) --- CHANGES.md | 1 + .../matrix/android/api/extensions/Booleans.kt | 22 +++++++++++ .../room/model/create/CreateRoomParams.kt | 9 ++++- .../session/room/create/CreateRoomTask.kt | 38 +++++++++++++++++-- .../createdirect/CreateDirectRoomViewModel.kt | 12 +++--- .../VerificationBottomSheetViewModel.kt | 4 +- 6 files changed, 74 insertions(+), 12 deletions(-) create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/api/extensions/Booleans.kt diff --git a/CHANGES.md b/CHANGES.md index 5af9a2ba54..f71a4f8c22 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -3,6 +3,7 @@ Changes in RiotX 0.14.0 (2020-XX-XX) Features ✨: - Enable encryption in unencrypted rooms, from the room settings (#212) + - Negotiate E2E by default for DMs (#907) Improvements 🙌: - Sharing things to RiotX: sort list by recent room first (#771) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/extensions/Booleans.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/extensions/Booleans.kt new file mode 100644 index 0000000000..50b6758305 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/extensions/Booleans.kt @@ -0,0 +1,22 @@ +/* + * Copyright 2020 New Vector Ltd + * + * 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 im.vector.matrix.android.api.extensions + +fun Boolean?.orTrue() = this ?: true + +fun Boolean?.orFalse() = this ?: false + diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/create/CreateRoomParams.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/create/CreateRoomParams.kt index f3069e0de5..f9eba02e4d 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/create/CreateRoomParams.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/create/CreateRoomParams.kt @@ -120,12 +120,19 @@ data class CreateRoomParams( @Json(name = "power_level_content_override") val powerLevelContentOverride: PowerLevelsContent? = null ) { + /** + * Set to true means that if cross-signing is enabled and we can get keys for every invited users, + * the encryption will be enabled on the created room + */ + @Transient + var enableEncryptionIfInvitedUsersSupportIt: Boolean = false + /** * Add the crypto algorithm to the room creation parameters. * * @param algorithm the algorithm */ - fun enableEncryptionWithAlgorithm(algorithm: String): CreateRoomParams { + fun enableEncryptionWithAlgorithm(algorithm: String = MXCRYPTO_ALGORITHM_MEGOLM): CreateRoomParams { return if (algorithm == MXCRYPTO_ALGORITHM_MEGOLM) { val contentMap = mapOf("algorithm" to algorithm) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/create/CreateRoomTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/create/CreateRoomTask.kt index 6567b7ad97..8576c71e96 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/create/CreateRoomTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/create/CreateRoomTask.kt @@ -17,9 +17,12 @@ package im.vector.matrix.android.internal.session.room.create import com.zhuinden.monarchy.Monarchy +import im.vector.matrix.android.api.extensions.orTrue +import im.vector.matrix.android.api.session.crypto.crosssigning.CrossSigningService import im.vector.matrix.android.api.session.room.failure.CreateRoomFailure import im.vector.matrix.android.api.session.room.model.create.CreateRoomParams import im.vector.matrix.android.api.session.room.model.create.CreateRoomResponse +import im.vector.matrix.android.internal.crypto.DeviceListManager import im.vector.matrix.android.internal.database.awaitNotEmptyResult import im.vector.matrix.android.internal.database.model.RoomEntity import im.vector.matrix.android.internal.database.model.RoomEntityFields @@ -49,12 +52,41 @@ internal class DefaultCreateRoomTask @Inject constructor( private val readMarkersTask: SetReadMarkersTask, @SessionDatabase private val realmConfiguration: RealmConfiguration, + private val crossSigningService: CrossSigningService, + private val deviceListManager: DeviceListManager, private val eventBus: EventBus ) : CreateRoomTask { override suspend fun execute(params: CreateRoomParams): String { + val createRoomParams = params + .takeIf { it.enableEncryptionIfInvitedUsersSupportIt } + .takeIf { crossSigningService.isCrossSigningEnabled() } + ?.takeIf { it.invite3pids.isNullOrEmpty() } + ?.invitedUserIds + ?.let { userIds -> + val keys = deviceListManager.downloadKeys(userIds, forceDownload = false) + + userIds.any { userId -> + if (keys.map[userId].isNullOrEmpty()) { + // A user has no device, so do not enable encryption + true + } else { + // Check that every user's device have at least one key + keys.map[userId]?.values?.any { it.keys.isNullOrEmpty() } ?: true + } + } + } + .orTrue() + .let { cannotEnableEncryption -> + if (!cannotEnableEncryption) { + params.enableEncryptionWithAlgorithm() + } else { + params + } + } + val createRoomResponse = executeRequest(eventBus) { - apiCall = roomAPI.createRoom(params) + apiCall = roomAPI.createRoom(createRoomParams) } val roomId = createRoomResponse.roomId!! // Wait for room to come back from the sync (but it can maybe be in the DB if the sync response is received before) @@ -66,8 +98,8 @@ internal class DefaultCreateRoomTask @Inject constructor( } catch (exception: TimeoutCancellationException) { throw CreateRoomFailure.CreatedWithTimeout } - if (params.isDirect()) { - handleDirectChatCreation(params, roomId) + if (createRoomParams.isDirect()) { + handleDirectChatCreation(createRoomParams, roomId) } setReadMarkers(roomId) return roomId diff --git a/vector/src/main/java/im/vector/riotx/features/createdirect/CreateDirectRoomViewModel.kt b/vector/src/main/java/im/vector/riotx/features/createdirect/CreateDirectRoomViewModel.kt index 313e5459e9..b32dec395f 100644 --- a/vector/src/main/java/im/vector/riotx/features/createdirect/CreateDirectRoomViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/createdirect/CreateDirectRoomViewModel.kt @@ -92,14 +92,12 @@ class CreateDirectRoomViewModel @AssistedInject constructor(@Assisted } private fun createRoomAndInviteSelectedUsers() = withState { currentState -> - val isDirect = currentState.selectedUsers.size == 1 val roomParams = CreateRoomParams( - invitedUserIds = ArrayList(currentState.selectedUsers.map { it.userId }) - ).apply { - if (isDirect) { - setDirectMessage() - } - } + invitedUserIds = currentState.selectedUsers.map { it.userId } + ) + .setDirectMessage() + .also { it.enableEncryptionIfInvitedUsersSupportIt = true } + session.rx() .createRoom(roomParams) .execute { diff --git a/vector/src/main/java/im/vector/riotx/features/crypto/verification/VerificationBottomSheetViewModel.kt b/vector/src/main/java/im/vector/riotx/features/crypto/verification/VerificationBottomSheetViewModel.kt index 5db0ef2284..e6eed7db5a 100644 --- a/vector/src/main/java/im/vector/riotx/features/crypto/verification/VerificationBottomSheetViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/crypto/verification/VerificationBottomSheetViewModel.kt @@ -128,9 +128,11 @@ class VerificationBottomSheetViewModel @AssistedInject constructor(@Assisted ini ) } val roomParams = CreateRoomParams( - invitedUserIds = listOf(otherUserId).toMutableList() + invitedUserIds = listOf(otherUserId) ) .setDirectMessage() + .also { it.enableEncryptionIfInvitedUsersSupportIt = true } + session.createRoom(roomParams, object : MatrixCallback { override fun onSuccess(data: String) { setState {