From db1f129034eb1192e5c012e55af067c2dd73a3dc Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 13 Oct 2020 11:27:30 +0200 Subject: [PATCH] Improve detection of encrypted rooms --- .../crypto/encryption/EncryptionTest.kt | 111 ++++++++++++++++++ .../internal/crypto/DefaultCryptoService.kt | 6 + .../room/summary/RoomSummaryUpdater.kt | 1 + .../timeline/factory/EncryptionItemFactory.kt | 3 + .../factory/MergedHeaderItemFactory.kt | 2 +- .../timeline/format/NoticeEventFormatter.kt | 3 + .../helper/TimelineDisplayableEvents.kt | 2 +- 7 files changed, 126 insertions(+), 2 deletions(-) create mode 100644 matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/encryption/EncryptionTest.kt diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/encryption/EncryptionTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/encryption/EncryptionTest.kt new file mode 100644 index 0000000000..e42059c639 --- /dev/null +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/encryption/EncryptionTest.kt @@ -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) { + // noop + } + + override fun onTimelineUpdated(snapshot: List) { + 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) + } +} 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 21aafda9ef..b78afe6d41 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 @@ -615,6 +615,7 @@ internal class DefaultCryptoService @Inject constructor( val encryptionEvent = monarchy.fetchCopied { realm -> EventEntity.whereType(realm, roomId = roomId, type = EventType.STATE_ROOM_ENCRYPTION) .contains(EventEntityFields.CONTENT, "\"algorithm\":\"$MXCRYPTO_ALGORITHM_MEGOLM\"") + .isNotNull(EventEntityFields.STATE_KEY) .findFirst() } return encryptionEvent != null @@ -915,6 +916,11 @@ internal class DefaultCryptoService @Inject constructor( * @param event the encryption event. */ private fun onRoomEncryptionEvent(roomId: String, event: Event) { + if (!event.isStateEvent()) { + // Ignore + Timber.w("Invalid encryption event") + return + } cryptoCoroutineScope.launch(coroutineDispatchers.crypto) { val params = LoadRoomMembersTask.Params(roomId) try { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryUpdater.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryUpdater.kt index f58059d6f2..f9a27c367c 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryUpdater.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryUpdater.kt @@ -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 val encryptionEvent = EventEntity.whereType(realm, roomId = roomId, type = EventType.STATE_ROOM_ENCRYPTION) .contains(EventEntityFields.CONTENT, "\"algorithm\":\"$MXCRYPTO_ALGORITHM_MEGOLM\"") + .isNotNull(EventEntityFields.STATE_KEY) .findFirst() val latestPreviewableEvent = RoomSummaryEventsHelper.getLatestPreviewableEvent(realm, roomId) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/EncryptionItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/EncryptionItemFactory.kt index 4f2c8ea63a..1eb09f2e7a 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/EncryptionItemFactory.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/EncryptionItemFactory.kt @@ -44,6 +44,9 @@ class EncryptionItemFactory @Inject constructor( fun create(event: TimelineEvent, highlight: Boolean, callback: TimelineEventController.Callback?): StatusTileTimelineItem? { + if (!event.root.isStateEvent()) { + return null + } val algorithm = event.root.getClearContent().toModel()?.algorithm val informationData = informationDataFactory.create(event, null) val attributes = messageItemAttributesFactory.create(null, informationData, callback) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MergedHeaderItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MergedHeaderItemFactory.kt index 35db7fe469..e7a911ceb1 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MergedHeaderItemFactory.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MergedHeaderItemFactory.kt @@ -148,7 +148,7 @@ class MergedHeaderItemFactory @Inject constructor(private val activeSessionHolde var hasEncryption = false var encryptionAlgorithm: String? = 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 encryptionAlgorithm = prevEvent.root.getClearContent()?.toModel()?.algorithm } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/NoticeEventFormatter.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/NoticeEventFormatter.kt index e26472feb0..1a5f820831 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/NoticeEventFormatter.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/NoticeEventFormatter.kt @@ -428,6 +428,9 @@ class NoticeEventFormatter @Inject constructor(private val activeSessionDataSour } private fun formatRoomEncryptionEvent(event: Event, senderName: String?): CharSequence? { + if (!event.isStateEvent()) { + return null + } val content = event.content.toModel() ?: return null return when (content.algorithm) { MXCRYPTO_ALGORITHM_MEGOLM -> diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineDisplayableEvents.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineDisplayableEvents.kt index b1b1109580..14b8c12fee 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineDisplayableEvents.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineDisplayableEvents.kt @@ -55,7 +55,7 @@ fun TimelineEvent.canBeMerged(): 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_HISTORY_VISIBILITY, EventType.STATE_ROOM_JOIN_RULES,