diff --git a/changelog.d/6148.bugfix b/changelog.d/6148.bugfix new file mode 100644 index 0000000000..3aa623315a --- /dev/null +++ b/changelog.d/6148.bugfix @@ -0,0 +1 @@ +Fix decrypting redacted event from sending errors diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/DecryptRedactedEventTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/DecryptRedactedEventTest.kt new file mode 100644 index 0000000000..de5fa41581 --- /dev/null +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/DecryptRedactedEventTest.kt @@ -0,0 +1,79 @@ +/* + * Copyright 2022 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 + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import org.junit.Assert +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.session.events.model.Event +import org.matrix.android.sdk.api.session.events.model.toModel +import org.matrix.android.sdk.api.session.getRoom +import org.matrix.android.sdk.api.session.room.getTimelineEvent +import org.matrix.android.sdk.common.CommonTestHelper +import org.matrix.android.sdk.common.CryptoTestHelper + +@RunWith(AndroidJUnit4::class) +@FixMethodOrder(MethodSorters.JVM) +class DecryptRedactedEventTest : InstrumentedTest { + + @Test + fun doNotFailToDecryptRedactedEvent() { + val testHelper = CommonTestHelper(context()) + val cryptoTestHelper = CryptoTestHelper(testHelper) + + val testData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(true) + val e2eRoomID = testData.roomId + val aliceSession = testData.firstSession + val bobSession = testData.secondSession!! + + val roomALicePOV = aliceSession.getRoom(e2eRoomID)!! + val timelineEvent = testHelper.sendTextMessage(roomALicePOV, "Hello", 1).first() + val redactionReason = "Wrong Room" + roomALicePOV.sendService().redactEvent(timelineEvent.root, redactionReason) + + // get the event from bob + testHelper.waitWithLatch { + testHelper.retryPeriodicallyWithLatch(it) { + bobSession.getRoom(e2eRoomID)?.getTimelineEvent(timelineEvent.eventId)?.root?.isRedacted() == true + } + } + + val eventBobPov = bobSession.getRoom(e2eRoomID)?.getTimelineEvent(timelineEvent.eventId)!! + + testHelper.runBlockingTest { + try { + val result = bobSession.cryptoService().decryptEvent(eventBobPov.root, "") + Assert.assertEquals( + "Unexpected redacted reason", + redactionReason, + result.clearEvent.toModel()?.unsignedData?.redactedEvent?.content?.get("reason") + ) + Assert.assertEquals( + "Unexpected Redacted event id", + timelineEvent.eventId, + result.clearEvent.toModel()?.unsignedData?.redactedEvent?.redacts + ) + } catch (failure: Throwable) { + Assert.fail("Should not throw when decrypting a redacted event") + } + } + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/EventDecryptor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/EventDecryptor.kt index cb61bbe1de..c1d04eb22b 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/EventDecryptor.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/EventDecryptor.kt @@ -29,6 +29,7 @@ import org.matrix.android.sdk.api.session.crypto.model.MXUsersDevicesMap import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.content.OlmEventContent +import org.matrix.android.sdk.api.session.events.model.toContent import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.internal.crypto.actions.EnsureOlmSessionsForDevicesAction import org.matrix.android.sdk.internal.crypto.actions.MessageEncrypter @@ -42,7 +43,7 @@ import javax.inject.Inject private const val SEND_TO_DEVICE_RETRY_COUNT = 3 -private val loggerTag = LoggerTag("CryptoSyncHandler", LoggerTag.CRYPTO) +private val loggerTag = LoggerTag("EventDecryptor", LoggerTag.CRYPTO) @SessionScope internal class EventDecryptor @Inject constructor( @@ -110,6 +111,16 @@ internal class EventDecryptor @Inject constructor( if (eventContent == null) { Timber.tag(loggerTag.value).e("decryptEvent : empty event content") throw MXCryptoError.Base(MXCryptoError.ErrorType.BAD_ENCRYPTED_MESSAGE, MXCryptoError.BAD_ENCRYPTED_MESSAGE_REASON) + } else if (event.isRedacted()) { + // we shouldn't attempt to decrypt a redacted event because the content is cleared and decryption will fail because of null algorithm + return MXEventDecryptionResult( + clearEvent = mapOf( + "room_id" to event.roomId.orEmpty(), + "type" to EventType.MESSAGE, + "content" to emptyMap(), + "unsigned" to event.unsignedData.toContent() + ) + ) } else { val algorithm = eventContent["algorithm"]?.toString() val alg = roomDecryptorProvider.getOrCreateRoomDecryptor(event.roomId, algorithm)