Improve detection of encrypted rooms
This commit is contained in:
parent
9dc1034891
commit
db1f129034
@ -0,0 +1,111 @@
|
|||||||
|
/*
|
||||||
|
* 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.internal.crypto.encryption
|
||||||
|
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
|
import org.amshove.kluent.shouldBe
|
||||||
|
import org.junit.FixMethodOrder
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.junit.runners.MethodSorters
|
||||||
|
import org.matrix.android.sdk.InstrumentedTest
|
||||||
|
import org.matrix.android.sdk.api.NoOpMatrixCallback
|
||||||
|
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||||
|
import org.matrix.android.sdk.api.session.events.model.toContent
|
||||||
|
import org.matrix.android.sdk.api.session.room.Room
|
||||||
|
import org.matrix.android.sdk.api.session.room.send.SendState
|
||||||
|
import org.matrix.android.sdk.api.session.room.timeline.Timeline
|
||||||
|
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
||||||
|
import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings
|
||||||
|
import org.matrix.android.sdk.common.CommonTestHelper
|
||||||
|
import org.matrix.android.sdk.common.CryptoTestHelper
|
||||||
|
import org.matrix.android.sdk.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM
|
||||||
|
import org.matrix.android.sdk.internal.crypto.model.event.EncryptionEventContent
|
||||||
|
import java.util.concurrent.CountDownLatch
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4::class)
|
||||||
|
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
|
||||||
|
class EncryptionTest : InstrumentedTest {
|
||||||
|
private val mTestHelper = CommonTestHelper(context())
|
||||||
|
private val mCryptoTestHelper = CryptoTestHelper(mTestHelper)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun test_EncryptionEvent() {
|
||||||
|
performTest(roomShouldBeEncrypted = false) { room ->
|
||||||
|
// Send an encryption Event as an Event (and not as a state event)
|
||||||
|
room.sendEvent(
|
||||||
|
eventType = EventType.STATE_ROOM_ENCRYPTION,
|
||||||
|
content = EncryptionEventContent(algorithm = MXCRYPTO_ALGORITHM_MEGOLM).toContent()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun test_EncryptionStateEvent() {
|
||||||
|
performTest(roomShouldBeEncrypted = true) { room ->
|
||||||
|
// Send an encryption Event as a State Event
|
||||||
|
room.sendStateEvent(
|
||||||
|
eventType = EventType.STATE_ROOM_ENCRYPTION,
|
||||||
|
stateKey = null,
|
||||||
|
body = EncryptionEventContent(algorithm = MXCRYPTO_ALGORITHM_MEGOLM).toContent(),
|
||||||
|
callback = NoOpMatrixCallback()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun performTest(roomShouldBeEncrypted: Boolean, action: (Room) -> Unit) {
|
||||||
|
val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceInARoom(encryptedRoom = false)
|
||||||
|
|
||||||
|
val aliceSession = cryptoTestData.firstSession
|
||||||
|
val room = aliceSession.getRoom(cryptoTestData.roomId)!!
|
||||||
|
|
||||||
|
room.isEncrypted() shouldBe false
|
||||||
|
|
||||||
|
val timeline = room.createTimeline(null, TimelineSettings(10))
|
||||||
|
val latch = CountDownLatch(1)
|
||||||
|
val timelineListener = object : Timeline.Listener {
|
||||||
|
override fun onTimelineFailure(throwable: Throwable) {
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onNewTimelineEvents(eventIds: List<String>) {
|
||||||
|
// noop
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onTimelineUpdated(snapshot: List<TimelineEvent>) {
|
||||||
|
val newMessages = snapshot
|
||||||
|
.filter { it.root.sendState == SendState.SYNCED }
|
||||||
|
.filter { it.root.getClearType() == EventType.STATE_ROOM_ENCRYPTION }
|
||||||
|
|
||||||
|
if (newMessages.isNotEmpty()) {
|
||||||
|
timeline.removeListener(this)
|
||||||
|
latch.countDown()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
timeline.start()
|
||||||
|
timeline.addListener(timelineListener)
|
||||||
|
|
||||||
|
action.invoke(room)
|
||||||
|
|
||||||
|
mTestHelper.await(latch)
|
||||||
|
timeline.dispose()
|
||||||
|
|
||||||
|
room.isEncrypted() shouldBe roomShouldBeEncrypted
|
||||||
|
|
||||||
|
cryptoTestData.cleanUp(mTestHelper)
|
||||||
|
}
|
||||||
|
}
|
@ -615,6 +615,7 @@ internal class DefaultCryptoService @Inject constructor(
|
|||||||
val encryptionEvent = monarchy.fetchCopied { realm ->
|
val encryptionEvent = monarchy.fetchCopied { realm ->
|
||||||
EventEntity.whereType(realm, roomId = roomId, type = EventType.STATE_ROOM_ENCRYPTION)
|
EventEntity.whereType(realm, roomId = roomId, type = EventType.STATE_ROOM_ENCRYPTION)
|
||||||
.contains(EventEntityFields.CONTENT, "\"algorithm\":\"$MXCRYPTO_ALGORITHM_MEGOLM\"")
|
.contains(EventEntityFields.CONTENT, "\"algorithm\":\"$MXCRYPTO_ALGORITHM_MEGOLM\"")
|
||||||
|
.isNotNull(EventEntityFields.STATE_KEY)
|
||||||
.findFirst()
|
.findFirst()
|
||||||
}
|
}
|
||||||
return encryptionEvent != null
|
return encryptionEvent != null
|
||||||
@ -915,6 +916,11 @@ internal class DefaultCryptoService @Inject constructor(
|
|||||||
* @param event the encryption event.
|
* @param event the encryption event.
|
||||||
*/
|
*/
|
||||||
private fun onRoomEncryptionEvent(roomId: String, event: Event) {
|
private fun onRoomEncryptionEvent(roomId: String, event: Event) {
|
||||||
|
if (!event.isStateEvent()) {
|
||||||
|
// Ignore
|
||||||
|
Timber.w("Invalid encryption event")
|
||||||
|
return
|
||||||
|
}
|
||||||
cryptoCoroutineScope.launch(coroutineDispatchers.crypto) {
|
cryptoCoroutineScope.launch(coroutineDispatchers.crypto) {
|
||||||
val params = LoadRoomMembersTask.Params(roomId)
|
val params = LoadRoomMembersTask.Params(roomId)
|
||||||
try {
|
try {
|
||||||
|
@ -94,6 +94,7 @@ internal class RoomSummaryUpdater @Inject constructor(
|
|||||||
// Don't use current state for this one as we are only interested in having MXCRYPTO_ALGORITHM_MEGOLM event in the room
|
// Don't use current state for this one as we are only interested in having MXCRYPTO_ALGORITHM_MEGOLM event in the room
|
||||||
val encryptionEvent = EventEntity.whereType(realm, roomId = roomId, type = EventType.STATE_ROOM_ENCRYPTION)
|
val encryptionEvent = EventEntity.whereType(realm, roomId = roomId, type = EventType.STATE_ROOM_ENCRYPTION)
|
||||||
.contains(EventEntityFields.CONTENT, "\"algorithm\":\"$MXCRYPTO_ALGORITHM_MEGOLM\"")
|
.contains(EventEntityFields.CONTENT, "\"algorithm\":\"$MXCRYPTO_ALGORITHM_MEGOLM\"")
|
||||||
|
.isNotNull(EventEntityFields.STATE_KEY)
|
||||||
.findFirst()
|
.findFirst()
|
||||||
|
|
||||||
val latestPreviewableEvent = RoomSummaryEventsHelper.getLatestPreviewableEvent(realm, roomId)
|
val latestPreviewableEvent = RoomSummaryEventsHelper.getLatestPreviewableEvent(realm, roomId)
|
||||||
|
@ -44,6 +44,9 @@ class EncryptionItemFactory @Inject constructor(
|
|||||||
fun create(event: TimelineEvent,
|
fun create(event: TimelineEvent,
|
||||||
highlight: Boolean,
|
highlight: Boolean,
|
||||||
callback: TimelineEventController.Callback?): StatusTileTimelineItem? {
|
callback: TimelineEventController.Callback?): StatusTileTimelineItem? {
|
||||||
|
if (!event.root.isStateEvent()) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
val algorithm = event.root.getClearContent().toModel<EncryptionEventContent>()?.algorithm
|
val algorithm = event.root.getClearContent().toModel<EncryptionEventContent>()?.algorithm
|
||||||
val informationData = informationDataFactory.create(event, null)
|
val informationData = informationDataFactory.create(event, null)
|
||||||
val attributes = messageItemAttributesFactory.create(null, informationData, callback)
|
val attributes = messageItemAttributesFactory.create(null, informationData, callback)
|
||||||
|
@ -148,7 +148,7 @@ class MergedHeaderItemFactory @Inject constructor(private val activeSessionHolde
|
|||||||
var hasEncryption = false
|
var hasEncryption = false
|
||||||
var encryptionAlgorithm: String? = null
|
var encryptionAlgorithm: String? = null
|
||||||
while (prevEvent != null && prevEvent.isRoomConfiguration(null)) {
|
while (prevEvent != null && prevEvent.isRoomConfiguration(null)) {
|
||||||
if (prevEvent.root.getClearType() == EventType.STATE_ROOM_ENCRYPTION) {
|
if (prevEvent.root.isStateEvent() && prevEvent.root.getClearType() == EventType.STATE_ROOM_ENCRYPTION) {
|
||||||
hasEncryption = true
|
hasEncryption = true
|
||||||
encryptionAlgorithm = prevEvent.root.getClearContent()?.toModel<EncryptionEventContent>()?.algorithm
|
encryptionAlgorithm = prevEvent.root.getClearContent()?.toModel<EncryptionEventContent>()?.algorithm
|
||||||
}
|
}
|
||||||
|
@ -428,6 +428,9 @@ class NoticeEventFormatter @Inject constructor(private val activeSessionDataSour
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun formatRoomEncryptionEvent(event: Event, senderName: String?): CharSequence? {
|
private fun formatRoomEncryptionEvent(event: Event, senderName: String?): CharSequence? {
|
||||||
|
if (!event.isStateEvent()) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
val content = event.content.toModel<EncryptionEventContent>() ?: return null
|
val content = event.content.toModel<EncryptionEventContent>() ?: return null
|
||||||
return when (content.algorithm) {
|
return when (content.algorithm) {
|
||||||
MXCRYPTO_ALGORITHM_MEGOLM ->
|
MXCRYPTO_ALGORITHM_MEGOLM ->
|
||||||
|
@ -55,7 +55,7 @@ fun TimelineEvent.canBeMerged(): Boolean {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun TimelineEvent.isRoomConfiguration(roomCreatorUserId: String?): Boolean {
|
fun TimelineEvent.isRoomConfiguration(roomCreatorUserId: String?): Boolean {
|
||||||
return when (root.getClearType()) {
|
return root.isStateEvent() && when (root.getClearType()) {
|
||||||
EventType.STATE_ROOM_GUEST_ACCESS,
|
EventType.STATE_ROOM_GUEST_ACCESS,
|
||||||
EventType.STATE_ROOM_HISTORY_VISIBILITY,
|
EventType.STATE_ROOM_HISTORY_VISIBILITY,
|
||||||
EventType.STATE_ROOM_JOIN_RULES,
|
EventType.STATE_ROOM_JOIN_RULES,
|
||||||
|
Loading…
Reference in New Issue
Block a user