Skip to content

Commit ec7c8c8

Browse files
authored
Merge pull request #6726 from vector-im/feature/bca/block_unverified
Per room block unverified devices
2 parents aad2eed + b567fc5 commit ec7c8c8

File tree

16 files changed

+386
-48
lines changed

16 files changed

+386
-48
lines changed

changelog.d/6725.bugfix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add option to only send to verified devices per room (web parity)

library/ui-strings/src/main/res/values/strings.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1234,6 +1234,9 @@
12341234
<string name="encryption_import_import">Import</string>
12351235
<string name="encryption_never_send_to_unverified_devices_title">Encrypt to verified sessions only</string>
12361236
<string name="encryption_never_send_to_unverified_devices_summary">Never send encrypted messages to unverified sessions from this session.</string>
1237+
<string name="encryption_never_send_to_unverified_devices_in_room">Never send encrypted messages to unverified sessions in this room.</string>
1238+
<string name="some_devices_will_not_be_able_to_decrypt">⚠ There are unverified devices in this room, they won’t be able to decrypt messages you send.</string>
1239+
<string name="room_settings_global_block_unverified_info_text">🔒 You have enabled encrypt to verified sessions only for all rooms in Security Settings.</string>
12371240
<plurals name="encryption_import_room_keys_success">
12381241
<item quantity="one">%1$d/%2$d key imported with success.</item>
12391242
<item quantity="other">%1$d/%2$d keys imported with success.</item>
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
/*
2+
* Copyright 2022 The Matrix.org Foundation C.I.C.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.matrix.android.sdk.internal.crypto
18+
19+
import androidx.test.filters.LargeTest
20+
import org.amshove.kluent.shouldBe
21+
import org.junit.FixMethodOrder
22+
import org.junit.Test
23+
import org.junit.runner.RunWith
24+
import org.junit.runners.JUnit4
25+
import org.junit.runners.MethodSorters
26+
import org.matrix.android.sdk.InstrumentedTest
27+
import org.matrix.android.sdk.api.session.crypto.MXCryptoError
28+
import org.matrix.android.sdk.api.session.getRoom
29+
import org.matrix.android.sdk.api.session.room.timeline.getLastMessageContent
30+
import org.matrix.android.sdk.common.CommonTestHelper.Companion.runCryptoTest
31+
32+
@RunWith(JUnit4::class)
33+
@FixMethodOrder(MethodSorters.JVM)
34+
@LargeTest
35+
class E2eeConfigTest : InstrumentedTest {
36+
37+
@Test
38+
fun testBlacklistUnverifiedDefault() = runCryptoTest(context()) { cryptoTestHelper, _ ->
39+
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(true)
40+
41+
cryptoTestData.firstSession.cryptoService().getGlobalBlacklistUnverifiedDevices() shouldBe false
42+
cryptoTestData.firstSession.cryptoService().isRoomBlacklistUnverifiedDevices(cryptoTestData.roomId) shouldBe false
43+
cryptoTestData.secondSession!!.cryptoService().getGlobalBlacklistUnverifiedDevices() shouldBe false
44+
cryptoTestData.secondSession!!.cryptoService().isRoomBlacklistUnverifiedDevices(cryptoTestData.roomId) shouldBe false
45+
}
46+
47+
@Test
48+
fun testCantDecryptIfGlobalUnverified() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
49+
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(true)
50+
51+
cryptoTestData.firstSession.cryptoService().setGlobalBlacklistUnverifiedDevices(true)
52+
53+
val roomAlicePOV = cryptoTestData.firstSession.roomService().getRoom(cryptoTestData.roomId)!!
54+
55+
val sentMessage = testHelper.sendTextMessage(roomAlicePOV, "you are blocked", 1).first()
56+
57+
val roomBobPOV = cryptoTestData.secondSession!!.roomService().getRoom(cryptoTestData.roomId)!!
58+
// ensure other received
59+
testHelper.retryPeriodically {
60+
roomBobPOV.timelineService().getTimelineEvent(sentMessage.eventId) != null
61+
}
62+
63+
cryptoTestHelper.ensureCannotDecrypt(listOf(sentMessage.eventId), cryptoTestData.secondSession!!, cryptoTestData.roomId)
64+
}
65+
66+
@Test
67+
fun testCanDecryptIfGlobalUnverifiedAndUserTrusted() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
68+
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(true)
69+
70+
cryptoTestHelper.initializeCrossSigning(cryptoTestData.firstSession)
71+
cryptoTestHelper.initializeCrossSigning(cryptoTestData.secondSession!!)
72+
73+
cryptoTestHelper.verifySASCrossSign(cryptoTestData.firstSession, cryptoTestData.secondSession!!, cryptoTestData.roomId)
74+
75+
cryptoTestData.firstSession.cryptoService().setGlobalBlacklistUnverifiedDevices(true)
76+
77+
val roomAlicePOV = cryptoTestData.firstSession.roomService().getRoom(cryptoTestData.roomId)!!
78+
79+
val sentMessage = testHelper.sendTextMessage(roomAlicePOV, "you can read", 1).first()
80+
81+
val roomBobPOV = cryptoTestData.secondSession!!.roomService().getRoom(cryptoTestData.roomId)!!
82+
// ensure other received
83+
testHelper.retryPeriodically {
84+
roomBobPOV.timelineService().getTimelineEvent(sentMessage.eventId) != null
85+
}
86+
87+
cryptoTestHelper.ensureCanDecrypt(
88+
listOf(sentMessage.eventId),
89+
cryptoTestData.secondSession!!,
90+
cryptoTestData.roomId,
91+
listOf(sentMessage.getLastMessageContent()!!.body)
92+
)
93+
}
94+
95+
@Test
96+
fun testCantDecryptIfPerRoomUnverified() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
97+
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(true)
98+
99+
val roomAlicePOV = cryptoTestData.firstSession.roomService().getRoom(cryptoTestData.roomId)!!
100+
101+
val beforeMessage = testHelper.sendTextMessage(roomAlicePOV, "you can read", 1).first()
102+
103+
val roomBobPOV = cryptoTestData.secondSession!!.roomService().getRoom(cryptoTestData.roomId)!!
104+
// ensure other received
105+
testHelper.retryPeriodically {
106+
roomBobPOV.timelineService().getTimelineEvent(beforeMessage.eventId) != null
107+
}
108+
109+
cryptoTestHelper.ensureCanDecrypt(
110+
listOf(beforeMessage.eventId),
111+
cryptoTestData.secondSession!!,
112+
cryptoTestData.roomId,
113+
listOf(beforeMessage.getLastMessageContent()!!.body)
114+
)
115+
116+
cryptoTestData.firstSession.cryptoService().setRoomBlockUnverifiedDevices(cryptoTestData.roomId, true)
117+
118+
val afterMessage = testHelper.sendTextMessage(roomAlicePOV, "you are blocked", 1).first()
119+
120+
// ensure received
121+
testHelper.retryPeriodically {
122+
cryptoTestData.secondSession?.getRoom(cryptoTestData.roomId)?.timelineService()?.getTimelineEvent(afterMessage.eventId)?.root != null
123+
}
124+
125+
cryptoTestHelper.ensureCannotDecrypt(
126+
listOf(afterMessage.eventId),
127+
cryptoTestData.secondSession!!,
128+
cryptoTestData.roomId,
129+
MXCryptoError.ErrorType.KEYS_WITHHELD
130+
)
131+
}
132+
}

matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/CryptoService.kt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ interface CryptoService {
6161

6262
fun isRoomBlacklistUnverifiedDevices(roomId: String?): Boolean
6363

64+
fun getLiveBlockUnverifiedDevices(roomId: String): LiveData<Boolean>
65+
6466
fun setWarnOnUnknownDevices(warn: Boolean)
6567

6668
fun setDeviceVerification(trustLevel: DeviceTrustLevel, userId: String, deviceId: String)
@@ -77,6 +79,8 @@ interface CryptoService {
7779

7880
fun setGlobalBlacklistUnverifiedDevices(block: Boolean)
7981

82+
fun getLiveGlobalCryptoConfig(): LiveData<GlobalCryptoConfig>
83+
8084
/**
8185
* Enable or disable key gossiping.
8286
* Default is true.
@@ -100,7 +104,7 @@ interface CryptoService {
100104
*/
101105
fun isShareKeysOnInviteEnabled(): Boolean
102106

103-
fun setRoomUnBlacklistUnverifiedDevices(roomId: String)
107+
fun setRoomUnBlockUnverifiedDevices(roomId: String)
104108

105109
fun getDeviceTrackingStatus(userId: String): Int
106110

@@ -112,7 +116,7 @@ interface CryptoService {
112116

113117
suspend fun exportRoomKeys(password: String): ByteArray
114118

115-
fun setRoomBlacklistUnverifiedDevices(roomId: String)
119+
fun setRoomBlockUnverifiedDevices(roomId: String, block: Boolean)
116120

117121
fun getCryptoDeviceInfo(userId: String, deviceId: String?): CryptoDeviceInfo?
118122

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/*
2+
* Copyright (c) 2022 The Matrix.org Foundation C.I.C.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.matrix.android.sdk.api.session.crypto
18+
19+
data class GlobalCryptoConfig(
20+
val globalBlockUnverifiedDevices: Boolean,
21+
val globalEnableKeyGossiping: Boolean,
22+
val enableKeyForwardingOnInvite: Boolean,
23+
)

matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt

Lines changed: 15 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ import org.matrix.android.sdk.api.failure.Failure
4040
import org.matrix.android.sdk.api.listeners.ProgressListener
4141
import org.matrix.android.sdk.api.logger.LoggerTag
4242
import org.matrix.android.sdk.api.session.crypto.CryptoService
43+
import org.matrix.android.sdk.api.session.crypto.GlobalCryptoConfig
4344
import org.matrix.android.sdk.api.session.crypto.MXCryptoError
4445
import org.matrix.android.sdk.api.session.crypto.NewSessionListener
4546
import org.matrix.android.sdk.api.session.crypto.OutgoingKeyRequest
@@ -1163,6 +1164,10 @@ internal class DefaultCryptoService @Inject constructor(
11631164
return cryptoStore.getGlobalBlacklistUnverifiedDevices()
11641165
}
11651166

1167+
override fun getLiveGlobalCryptoConfig(): LiveData<GlobalCryptoConfig> {
1168+
return cryptoStore.getLiveGlobalCryptoConfig()
1169+
}
1170+
11661171
/**
11671172
* Tells whether the client should encrypt messages only for the verified devices
11681173
* in this room.
@@ -1171,48 +1176,37 @@ internal class DefaultCryptoService @Inject constructor(
11711176
* @param roomId the room id
11721177
* @return true if the client should encrypt messages only for the verified devices.
11731178
*/
1174-
// TODO add this info in CryptoRoomEntity?
11751179
override fun isRoomBlacklistUnverifiedDevices(roomId: String?): Boolean {
1176-
return roomId?.let { cryptoStore.getRoomsListBlacklistUnverifiedDevices().contains(it) }
1180+
return roomId?.let { cryptoStore.getBlockUnverifiedDevices(roomId) }
11771181
?: false
11781182
}
11791183

11801184
/**
1181-
* Manages the room black-listing for unverified devices.
1185+
* A live status regarding sharing keys for unverified devices in this room.
11821186
*
1183-
* @param roomId the room id
1184-
* @param add true to add the room id to the list, false to remove it.
1187+
* @return Live status
11851188
*/
1186-
private fun setRoomBlacklistUnverifiedDevices(roomId: String, add: Boolean) {
1187-
val roomIds = cryptoStore.getRoomsListBlacklistUnverifiedDevices().toMutableList()
1188-
1189-
if (add) {
1190-
if (roomId !in roomIds) {
1191-
roomIds.add(roomId)
1192-
}
1193-
} else {
1194-
roomIds.remove(roomId)
1195-
}
1196-
1197-
cryptoStore.setRoomsListBlacklistUnverifiedDevices(roomIds)
1189+
override fun getLiveBlockUnverifiedDevices(roomId: String): LiveData<Boolean> {
1190+
return cryptoStore.getLiveBlockUnverifiedDevices(roomId)
11981191
}
11991192

12001193
/**
12011194
* Add this room to the ones which don't encrypt messages to unverified devices.
12021195
*
12031196
* @param roomId the room id
1197+
* @param block if true will block sending keys to unverified devices
12041198
*/
1205-
override fun setRoomBlacklistUnverifiedDevices(roomId: String) {
1206-
setRoomBlacklistUnverifiedDevices(roomId, true)
1199+
override fun setRoomBlockUnverifiedDevices(roomId: String, block: Boolean) {
1200+
cryptoStore.blockUnverifiedDevicesInRoom(roomId, block)
12071201
}
12081202

12091203
/**
12101204
* Remove this room to the ones which don't encrypt messages to unverified devices.
12111205
*
12121206
* @param roomId the room id
12131207
*/
1214-
override fun setRoomUnBlacklistUnverifiedDevices(roomId: String) {
1215-
setRoomBlacklistUnverifiedDevices(roomId, false)
1208+
override fun setRoomUnBlockUnverifiedDevices(roomId: String) {
1209+
setRoomBlockUnverifiedDevices(roomId, false)
12161210
}
12171211

12181212
/**

matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmEncryption.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -424,7 +424,7 @@ internal class MXMegolmEncryption(
424424
// an m.new_device.
425425
val keys = deviceListManager.downloadKeys(userIds, false)
426426
val encryptToVerifiedDevicesOnly = cryptoStore.getGlobalBlacklistUnverifiedDevices() ||
427-
cryptoStore.getRoomsListBlacklistUnverifiedDevices().contains(roomId)
427+
cryptoStore.getBlockUnverifiedDevices(roomId)
428428

429429
val devicesInRoom = DeviceInRoomInfo()
430430
val unknownDevices = MXUsersDevicesMap<CryptoDeviceInfo>()

matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/IMXCryptoStore.kt

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package org.matrix.android.sdk.internal.crypto.store
1818

1919
import androidx.lifecycle.LiveData
2020
import androidx.paging.PagedList
21+
import org.matrix.android.sdk.api.session.crypto.GlobalCryptoConfig
2122
import org.matrix.android.sdk.api.session.crypto.NewSessionListener
2223
import org.matrix.android.sdk.api.session.crypto.OutgoingKeyRequest
2324
import org.matrix.android.sdk.api.session.crypto.OutgoingRoomKeyRequestState
@@ -120,11 +121,26 @@ internal interface IMXCryptoStore {
120121
fun getRoomsListBlacklistUnverifiedDevices(): List<String>
121122

122123
/**
123-
* Updates the rooms ids list in which the messages are not encrypted for the unverified devices.
124+
* A live status regarding sharing keys for unverified devices in this room.
124125
*
125-
* @param roomIds the room ids list
126+
* @return Live status
126127
*/
127-
fun setRoomsListBlacklistUnverifiedDevices(roomIds: List<String>)
128+
fun getLiveBlockUnverifiedDevices(roomId: String): LiveData<Boolean>
129+
130+
/**
131+
* Tell if unverified devices should be blacklisted when sending keys.
132+
*
133+
* @return true if should not send keys to unverified devices
134+
*/
135+
fun getBlockUnverifiedDevices(roomId: String): Boolean
136+
137+
/**
138+
* Define if encryption keys should be sent to unverified devices in this room.
139+
*
140+
* @param roomId the roomId
141+
* @param block if true will not send keys to unverified devices
142+
*/
143+
fun blockUnverifiedDevicesInRoom(roomId: String, block: Boolean)
128144

129145
/**
130146
* Get the current keys backup version.
@@ -516,6 +532,9 @@ internal interface IMXCryptoStore {
516532
fun getCrossSigningPrivateKeys(): PrivateKeysInfo?
517533
fun getLiveCrossSigningPrivateKeys(): LiveData<Optional<PrivateKeysInfo>>
518534

535+
fun getGlobalCryptoConfig(): GlobalCryptoConfig
536+
fun getLiveGlobalCryptoConfig(): LiveData<GlobalCryptoConfig>
537+
519538
fun saveBackupRecoveryKey(recoveryKey: String?, version: String?)
520539
fun getKeyBackupRecoveryKeyInfo(): SavedKeyBackupKeyInfo?
521540

0 commit comments

Comments
 (0)