Improve detection of encrypted rooms

This commit is contained in:
Benoit Marty 2020-10-13 11:27:30 +02:00
parent 9dc1034891
commit db1f129034
7 changed files with 126 additions and 2 deletions

View File

@ -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)
}
}

View File

@ -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 {

View File

@ -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)

View File

@ -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<EncryptionEventContent>()?.algorithm
val informationData = informationDataFactory.create(event, null)
val attributes = messageItemAttributesFactory.create(null, informationData, callback)

View File

@ -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<EncryptionEventContent>()?.algorithm
}

View File

@ -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<EncryptionEventContent>() ?: return null
return when (content.algorithm) {
MXCRYPTO_ALGORITHM_MEGOLM ->

View File

@ -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,