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 index 4e1efbb700..4e447af098 100644 --- 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 @@ -46,7 +46,7 @@ class DecryptRedactedEventTest : InstrumentedTest { roomALicePOV.sendService().redactEvent(timelineEvent.root, redactionReason) // get the event from bob - testHelper.retryPeriodically { + testHelper.retryWithBackoff { bobSession.getRoom(e2eRoomID)?.getTimelineEvent(timelineEvent.eventId)?.root?.isRedacted() == true } diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/E2eeSanityTests.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/E2eeSanityTests.kt index f3a795822e..b3cc1f20e8 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/E2eeSanityTests.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/E2eeSanityTests.kt @@ -101,7 +101,7 @@ class E2eeSanityTests : InstrumentedTest { fail("${otherSession.myUserId.take(10)} should be able to decrypt") }) { val timeLineEvent = otherSession.getRoom(e2eRoomID)?.getTimelineEvent(sentEventId!!).also { - Log.v("#E2E TEST", "Event seen by new user ${it?.root?.getClearType()}|${it?.root?.mCryptoError}") + Log.v("#E2E TEST", "Event seen by new user ${it?.root?.getClearType()}|${it?.root?.mCryptoError}|${it?.root?.mxDecryptionResult?.isSafe}") } timeLineEvent != null && timeLineEvent.isEncrypted() && diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/E2eeShareKeysHistoryTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/E2eeShareKeysHistoryTest.kt index 91e0026c93..3e57a56e18 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/E2eeShareKeysHistoryTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/E2eeShareKeysHistoryTest.kt @@ -18,6 +18,7 @@ package org.matrix.android.sdk.internal.crypto import android.util.Log import androidx.test.filters.LargeTest +import org.amshove.kluent.fail import org.amshove.kluent.internal.assertEquals import org.amshove.kluent.internal.assertNotEquals import org.junit.Assert @@ -42,7 +43,6 @@ import org.matrix.android.sdk.api.session.room.model.shouldShareHistory import org.matrix.android.sdk.common.CommonTestHelper import org.matrix.android.sdk.common.CommonTestHelper.Companion.runCryptoTest import org.matrix.android.sdk.common.SessionTestParams -import org.matrix.android.sdk.common.wrapWithTimeout @RunWith(JUnit4::class) @FixMethodOrder(MethodSorters.JVM) @@ -99,19 +99,25 @@ class E2eeShareKeysHistoryTest : InstrumentedTest { val aliceMessageId: String? = sendMessageInRoom(aliceRoomPOV, aliceMessageText, testHelper) Assert.assertTrue("Message should be sent", aliceMessageId != null) - Log.v("#E2E TEST", "Alice sent message to roomId: $e2eRoomID") + Log.v("#E2E TEST", "Alice has sent message to roomId: $e2eRoomID") // Bob should be able to decrypt the message - testHelper.retryPeriodically { - val timelineEvent = bobSession.roomService().getRoom(e2eRoomID)?.timelineService()?.getTimelineEvent(aliceMessageId!!) - (timelineEvent != null && - timelineEvent.isEncrypted() && - timelineEvent.root.getClearType() == EventType.MESSAGE && - timelineEvent.root.mxDecryptionResult?.isSafe == true).also { - if (it) { - Log.v("#E2E TEST", "Bob can decrypt the message: ${timelineEvent?.root?.getDecryptedTextSummary()}") - } + testHelper.retryWithBackoff( + onFail = { + fail("Bob should be able to decrypt $aliceMessageId") } + ) { + val timelineEvent = bobSession.roomService().getRoom(e2eRoomID)?.timelineService()?.getTimelineEvent(aliceMessageId!!)?.also { + Log.v("#E2E TEST", "Bob sees ${it.root.getClearType()}") + } + (timelineEvent != null && + timelineEvent.isEncrypted() && + timelineEvent.root.getClearType() == EventType.MESSAGE && + timelineEvent.root.mxDecryptionResult?.isSafe == true).also { + if (it) { + Log.v("#E2E TEST", "Bob can decrypt the message: ${timelineEvent?.root?.getDecryptedTextSummary()}") + } + } } // Create a new user @@ -135,23 +141,31 @@ class E2eeShareKeysHistoryTest : InstrumentedTest { null -> { // Aris should be able to decrypt the message - testHelper.retryPeriodically { - val timelineEvent = arisSession.roomService().getRoom(e2eRoomID)?.timelineService()?.getTimelineEvent(aliceMessageId!!) - (timelineEvent != null && - timelineEvent.isEncrypted() && - timelineEvent.root.getClearType() == EventType.MESSAGE && - timelineEvent.root.mxDecryptionResult?.isSafe == false - ).also { - if (it) { - Log.v("#E2E TEST", "Aris can decrypt the message: ${timelineEvent?.root?.getDecryptedTextSummary()}") - } + testHelper.retryWithBackoff( + onFail = { + fail("Aris should be able to decrypt $aliceMessageId") + } + ) { + val timelineEvent = arisSession.roomService().getRoom(e2eRoomID)?.timelineService()?.getTimelineEvent(aliceMessageId!!) + (timelineEvent != null && + timelineEvent.isEncrypted() && + timelineEvent.root.getClearType() == EventType.MESSAGE && + timelineEvent.root.mxDecryptionResult?.isSafe == false + ).also { + if (it) { + Log.v("#E2E TEST", "Aris can decrypt the message: ${timelineEvent?.root?.getDecryptedTextSummary()}") } + } } } RoomHistoryVisibility.INVITED, RoomHistoryVisibility.JOINED -> { // Aris should not even be able to get the message - testHelper.retryPeriodically { + testHelper.retryWithBackoff( + onFail = { + fail("Aris should not even be able to get the message") + } + ) { val timelineEvent = arisSession.roomService().getRoom(e2eRoomID) ?.timelineService() ?.getTimelineEvent(aliceMessageId!!) @@ -258,11 +272,17 @@ class E2eeShareKeysHistoryTest : InstrumentedTest { // Bob should be able to decrypt the message var firstAliceMessageMegolmSessionId: String? = null - val bobRoomPov = bobSession.roomService().getRoom(e2eRoomID) - testHelper.retryPeriodically { + val bobRoomPov = bobSession.roomService().getRoom(e2eRoomID)!! + testHelper.retryWithBackoff( + onFail = { + fail("Bob should be able to decrypt $aliceMessageId") + } + ) { val timelineEvent = bobRoomPov - ?.timelineService() - ?.getTimelineEvent(aliceMessageId!!) + .timelineService() + .getTimelineEvent(aliceMessageId!!)?.also { + Log.v("#E2E TEST ROTATION", "Bob sees ${it.root.getClearType()}") + } (timelineEvent != null && timelineEvent.isEncrypted() && timelineEvent.root.getClearType() == EventType.MESSAGE).also { @@ -279,11 +299,17 @@ class E2eeShareKeysHistoryTest : InstrumentedTest { Assert.assertNotNull("megolm session id can't be null", firstAliceMessageMegolmSessionId) var secondAliceMessageSessionId: String? = null - sendMessageInRoom(aliceRoomPOV, "Other msg", testHelper)?.let { secondMessage -> - testHelper.retryPeriodically { + sendMessageInRoom(aliceRoomPOV, "Other msg", testHelper)!!.let { secondMessage -> + testHelper.retryWithBackoff( + onFail = { + fail("Bob should be able to decrypt the second message $secondMessage") + } + ) { val timelineEvent = bobRoomPov - ?.timelineService() - ?.getTimelineEvent(secondMessage) + .timelineService() + .getTimelineEvent(secondMessage)?.also { + Log.v("#E2E TEST ROTATION", "Bob sees ${it.root.getClearType()}") + } (timelineEvent != null && timelineEvent.isEncrypted() && timelineEvent.root.getClearType() == EventType.MESSAGE).also { @@ -309,29 +335,44 @@ class E2eeShareKeysHistoryTest : InstrumentedTest { historyVisibilityStr = nextRoomHistoryVisibility.historyVisibilityStr ).toContent() ) + Log.v("#E2E TEST ROTATION", "State update sent") // ensure that the state did synced down - testHelper.retryPeriodically { - aliceRoomPOV.stateService().getStateEvent(EventType.STATE_ROOM_HISTORY_VISIBILITY, QueryStringValue.IsEmpty)?.content + testHelper.retryWithBackoff( + onFail = { + fail("Alice state should be updated to ${nextRoomHistoryVisibility.historyVisibilityStr}") + } + ) { + aliceRoomPOV.stateService().getStateEvent(EventType.STATE_ROOM_HISTORY_VISIBILITY, QueryStringValue.IsEmpty) + ?.content + ?.also { + Log.v("#E2E TEST ROTATION", "Alice sees state as $it") + } ?.toModel()?.historyVisibility == nextRoomHistoryVisibility.historyVisibility } - testHelper.retryPeriodically { - val roomVisibility = aliceSession.getRoom(e2eRoomID)!! - .stateService() - .getStateEvent(EventType.STATE_ROOM_HISTORY_VISIBILITY, QueryStringValue.IsEmpty) - ?.content - ?.toModel() - Log.v("#E2E TEST ROTATION", "Room visibility changed from: ${initRoomHistoryVisibility.name} to: ${roomVisibility?.historyVisibility?.name}") - roomVisibility?.historyVisibility == nextRoomHistoryVisibility.historyVisibility - } +// testHelper.retryPeriodically { +// val roomVisibility = aliceSession.getRoom(e2eRoomID)!! +// .stateService() +// .getStateEvent(EventType.STATE_ROOM_HISTORY_VISIBILITY, QueryStringValue.IsEmpty) +// ?.content +// ?.toModel() +// Log.v("#E2E TEST ROTATION", "Room visibility changed from: ${initRoomHistoryVisibility.name} to: ${roomVisibility?.historyVisibility?.name}") +// roomVisibility?.historyVisibility == nextRoomHistoryVisibility.historyVisibility +// } var aliceThirdMessageSessionId: String? = null - sendMessageInRoom(aliceRoomPOV, "Message after visibility change", testHelper)?.let { thirdMessage -> - testHelper.retryPeriodically { + sendMessageInRoom(aliceRoomPOV, "Message after visibility change", testHelper)!!.let { thirdMessage -> + testHelper.retryWithBackoff( + onFail = { + fail("Bob should be able to decrypt $thirdMessage") + } + ) { val timelineEvent = bobRoomPov - ?.timelineService() - ?.getTimelineEvent(thirdMessage) + .timelineService() + .getTimelineEvent(thirdMessage)?.also { + Log.v("#E2E TEST ROTATION", "Bob sees ${it.root.getClearType()}") + } (timelineEvent != null && timelineEvent.isEncrypted() && timelineEvent.root.getClearType() == EventType.MESSAGE).also { @@ -364,7 +405,7 @@ class E2eeShareKeysHistoryTest : InstrumentedTest { } private suspend fun ensureMembersHaveJoined(aliceSession: Session, otherAccounts: List, e2eRoomID: String, testHelper: CommonTestHelper) { - testHelper.retryPeriodically { + testHelper.retryWithBackoff { otherAccounts.map { aliceSession.roomService().getRoomMember(it.myUserId, e2eRoomID)?.membership }.all { @@ -374,7 +415,7 @@ class E2eeShareKeysHistoryTest : InstrumentedTest { } private suspend fun waitForAndAcceptInviteInRoom(otherSession: Session, e2eRoomID: String, testHelper: CommonTestHelper) { - testHelper.retryPeriodically { + testHelper.retryWithBackoff { val roomSummary = otherSession.roomService().getRoomSummary(e2eRoomID) (roomSummary != null && roomSummary.membership == Membership.INVITE).also { if (it) { @@ -383,17 +424,15 @@ class E2eeShareKeysHistoryTest : InstrumentedTest { } } - wrapWithTimeout(60_000) { Log.v("#E2E TEST", "${otherSession.myUserId} tries to join room $e2eRoomID") try { otherSession.roomService().joinRoom(e2eRoomID) } catch (ex: JoinRoomFailure.JoinedWithTimeout) { // it's ok we will wait after } - } Log.v("#E2E TEST", "${otherSession.myUserId} waiting for join echo ...") - testHelper.retryPeriodically { + testHelper.retryWithBackoff { val roomSummary = otherSession.roomService().getRoomSummary(e2eRoomID) roomSummary != null && roomSummary.membership == Membership.JOIN } diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/SasVerificationTestHelper.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/SasVerificationTestHelper.kt index eea2ed35a9..05031e8c13 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/SasVerificationTestHelper.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/SasVerificationTestHelper.kt @@ -16,6 +16,7 @@ package org.matrix.android.sdk.internal.crypto.verification +import org.amshove.kluent.fail import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.crypto.verification.EVerificationState import org.matrix.android.sdk.api.session.crypto.verification.VerificationMethod @@ -37,7 +38,11 @@ class SasVerificationTestHelper(private val testHelper: CommonTestHelper) { ) .transactionId - testHelper.retryPeriodically { + testHelper.retryWithBackoff( + onFail = { + fail("bob should see an incoming verification request with id $transactionId") + } + ) { val incomingRequest = bobVerificationService.getExistingVerificationRequest(aliceSession.myUserId, transactionId) if (incomingRequest != null) { bobVerificationService.readyPendingVerification( @@ -52,7 +57,11 @@ class SasVerificationTestHelper(private val testHelper: CommonTestHelper) { } // wait for alice to see the ready - testHelper.retryPeriodically { + testHelper.retryWithBackoff( + onFail = { + fail("Alice request whould be ready $transactionId") + } + ) { val pendingRequest = aliceVerificationService.getExistingVerificationRequest(bobUserId, transactionId) pendingRequest?.state == EVerificationState.Ready } @@ -67,7 +76,7 @@ class SasVerificationTestHelper(private val testHelper: CommonTestHelper) { val requestID = session1VerificationService.requestSelfKeyVerification(supportedMethods).transactionId val myUserId = session1.myUserId - testHelper.retryPeriodically { + testHelper.retryWithBackoff { val incomingRequest = session2VerificationService.getExistingVerificationRequest(myUserId, requestID) if (incomingRequest != null) { session2VerificationService.readyPendingVerification( diff --git a/matrix-sdk-android/src/androidTestKotlinCrypto/java/org/matrix/android/sdk/internal/crypto/verification/SASTest.kt b/matrix-sdk-android/src/androidTestKotlinCrypto/java/org/matrix/android/sdk/internal/crypto/verification/SASTest.kt index 7fbce118c6..4d5a937b5d 100644 --- a/matrix-sdk-android/src/androidTestKotlinCrypto/java/org/matrix/android/sdk/internal/crypto/verification/SASTest.kt +++ b/matrix-sdk-android/src/androidTestKotlinCrypto/java/org/matrix/android/sdk/internal/crypto/verification/SASTest.kt @@ -17,305 +17,323 @@ package org.matrix.android.sdk.internal.crypto.verification import androidx.test.ext.junit.runners.AndroidJUnit4 +import kotlinx.coroutines.CompletableDeferred +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import org.amshove.kluent.internal.assertEquals +import org.junit.Assert.assertNotNull +import org.junit.Assert.assertNull +import org.junit.Assert.assertTrue import org.junit.FixMethodOrder -import org.junit.Ignore +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.crypto.verification.CancelCode +import org.matrix.android.sdk.api.session.crypto.verification.EVerificationState +import org.matrix.android.sdk.api.session.crypto.verification.SasTransactionState +import org.matrix.android.sdk.api.session.crypto.verification.SasVerificationTransaction +import org.matrix.android.sdk.api.session.crypto.verification.VerificationEvent +import org.matrix.android.sdk.api.session.crypto.verification.VerificationMethod +import org.matrix.android.sdk.common.CommonTestHelper.Companion.runCryptoTest +import timber.log.Timber @RunWith(AndroidJUnit4::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -@Ignore class SASTest : InstrumentedTest { - /* + val scope = CoroutineScope(SupervisorJob()) @Test fun test_aliceStartThenAliceCancel() = runCryptoTest(context()) { cryptoTestHelper, testHelper -> - // TODO -// val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom() -// cryptoTestData.initializeCrossSigning(cryptoTestHelper) -// val aliceSession = cryptoTestData.firstSession -// val bobSession = cryptoTestData.secondSession -// -// val aliceVerificationService = aliceSession.cryptoService().verificationService() -// val bobVerificationService = bobSession!!.cryptoService().verificationService() -// -// val bobTxCreatedLatch = CountDownLatch(1) -// val bobListener = object : VerificationService.Listener { -// override fun transactionUpdated(tx: VerificationTransaction) { -// bobTxCreatedLatch.countDown() -// } -// } -// bobVerificationService.addListener(bobListener) -// -// val bobDevice = bobSession.cryptoService().getMyCryptoDevice() -// -// aliceSession.cryptoService().downloadKeysIfNeeded(listOf(bobSession.myUserId), forceDownload = true) -// val txID = aliceVerificationService.beginKeyVerification(bobSession.myUserId, bobDevice.deviceId) -// -// assertNotNull("Alice should have a started transaction", txID) -// -// val aliceKeyTx = aliceVerificationService.getExistingTransaction(bobSession.myUserId, txID!!) -// assertNotNull("Alice should have a started transaction", aliceKeyTx) -// -// testHelper.await(bobTxCreatedLatch) -// bobVerificationService.removeListener(bobListener) -// -// val bobKeyTx = bobVerificationService.getExistingTransaction(aliceSession.myUserId, txID) -// -// assertNotNull("Bob should have started verif transaction", bobKeyTx) -// assertTrue(bobKeyTx is SasVerificationTransaction) -// assertNotNull("Bob should have starting a SAS transaction", bobKeyTx) -// assertTrue(aliceKeyTx is SasVerificationTransaction) -// assertEquals("Alice and Bob have same transaction id", aliceKeyTx!!.transactionId, bobKeyTx!!.transactionId) -// -// assertEquals("Alice state should be started", VerificationTxState.OnStarted, aliceKeyTx.state) -// assertEquals("Bob state should be started by alice", VerificationTxState.OnStarted, bobKeyTx.state) -// -// // Let's cancel from alice side -// val cancelLatch = CountDownLatch(1) -// -// val bobListener2 = object : VerificationService.Listener { -// override fun transactionUpdated(tx: VerificationTransaction) { -// if (tx.transactionId == txID) { -// val immutableState = (tx as SasVerificationTransaction).state -// if (immutableState is VerificationTxState.Cancelled && !immutableState.byMe) { -// cancelLatch.countDown() -// } -// } -// } -// } -// bobVerificationService.addListener(bobListener2) -// -// aliceKeyTx.cancel(CancelCode.User) -// -// testHelper.await(cancelLatch) -// -// assertTrue("Should be cancelled on alice side", aliceKeyTx.state is VerificationTxState.Cancelled) -// assertTrue("Should be cancelled on bob side", bobKeyTx.state is VerificationTxState.Cancelled) -// -// val aliceCancelState = aliceKeyTx.state as VerificationTxState.Cancelled -// val bobCancelState = bobKeyTx.state as VerificationTxState.Cancelled -// -// assertTrue("Should be cancelled by me on alice side", aliceCancelState.byMe) -// assertFalse("Should be cancelled by other on bob side", bobCancelState.byMe) -// -// assertEquals("Should be User cancelled on alice side", CancelCode.User, aliceCancelState.cancelCode) -// assertEquals("Should be User cancelled on bob side", CancelCode.User, bobCancelState.cancelCode) -// -// assertNull(bobVerificationService.getExistingTransaction(aliceSession.myUserId, txID)) -// assertNull(aliceVerificationService.getExistingTransaction(bobSession.myUserId, txID)) - } - @Test - @Ignore("This test will be ignored until it is fixed") - fun test_key_agreement_protocols_must_include_curve25519() = runCryptoTest(context()) { cryptoTestHelper, testHelper -> - fail("Not passing for the moment") + Timber.v("verification: doE2ETestWithAliceAndBobInARoom") val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom() - - val bobSession = cryptoTestData.secondSession!! - - val protocols = listOf("meh_dont_know") - val tid = "00000000" - - // Bob should receive a cancel - var cancelReason: CancelCode? = null - val cancelLatch = CountDownLatch(1) - - val bobListener = object : VerificationService.Listener { - override fun transactionUpdated(tx: VerificationTransaction) { - tx as SasVerificationTransaction - if (tx.transactionId == tid && tx.state() is SasTransactionState.Cancelled) { - cancelReason = (tx.state() as SasTransactionState.Cancelled).cancelCode - cancelLatch.countDown() - } - } - } -// bobSession.cryptoService().verificationService().addListener(bobListener) - - // TODO bobSession!!.dataHandler.addListener(object : MXEventListener() { - // TODO override fun onToDeviceEvent(event: Event?) { - // TODO if (event!!.getType() == CryptoEvent.EVENT_TYPE_KEY_VERIFICATION_CANCEL) { - // TODO if (event.contentAsJsonObject?.get("transaction_id")?.asString == tid) { - // TODO canceledToDeviceEvent = event - // TODO cancelLatch.countDown() - // TODO } - // TODO } - // TODO } - // TODO }) - - val aliceSession = cryptoTestData.firstSession - val aliceUserID = aliceSession.myUserId - val aliceDevice = aliceSession.cryptoService().getMyCryptoDevice().deviceId - - val aliceListener = object : VerificationService.Listener { - override fun transactionUpdated(tx: VerificationTransaction) { - tx as SasVerificationTransaction - if (tx.state() is SasTransactionState.SasStarted) { - runBlocking { - tx.acceptVerification() - } - } - } - } -// aliceSession.cryptoService().verificationService().addListener(aliceListener) - - fakeBobStart(bobSession, aliceUserID, aliceDevice, tid, protocols = protocols) - - testHelper.await(cancelLatch) - - assertEquals("Request should be cancelled with m.unknown_method", CancelCode.UnknownMethod, cancelReason) - } - - @Test - @Ignore("This test will be ignored until it is fixed") - fun test_key_agreement_macs_Must_include_hmac_sha256() = runCryptoTest(context()) { cryptoTestHelper, testHelper -> - fail("Not passing for the moment") - val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom() - - val bobSession = cryptoTestData.secondSession!! - - val mac = listOf("shaBit") - val tid = "00000000" - - // Bob should receive a cancel - val canceledToDeviceEvent: Event? = null - val cancelLatch = CountDownLatch(1) - // TODO bobSession!!.dataHandler.addListener(object : MXEventListener() { - // TODO override fun onToDeviceEvent(event: Event?) { - // TODO if (event!!.getType() == CryptoEvent.EVENT_TYPE_KEY_VERIFICATION_CANCEL) { - // TODO if (event.contentAsJsonObject?.get("transaction_id")?.asString == tid) { - // TODO canceledToDeviceEvent = event - // TODO cancelLatch.countDown() - // TODO } - // TODO } - // TODO } - // TODO }) - - val aliceSession = cryptoTestData.firstSession - val aliceUserID = aliceSession.myUserId - val aliceDevice = aliceSession.cryptoService().getMyCryptoDevice().deviceId - - fakeBobStart(bobSession, aliceUserID, aliceDevice, tid, mac = mac) - - testHelper.await(cancelLatch) - val cancelReq = canceledToDeviceEvent!!.content.toModel()!! - assertEquals("Request should be cancelled with m.unknown_method", CancelCode.UnknownMethod.value, cancelReq.code) - } - - @Test - @Ignore("This test will be ignored until it is fixed") - fun test_key_agreement_short_code_include_decimal() = runCryptoTest(context()) { cryptoTestHelper, testHelper -> - fail("Not passing for the moment") - val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom() - - val bobSession = cryptoTestData.secondSession!! - - val codes = listOf("bin", "foo", "bar") - val tid = "00000000" - - // Bob should receive a cancel - var canceledToDeviceEvent: Event? = null - val cancelLatch = CountDownLatch(1) - // TODO bobSession!!.dataHandler.addListener(object : MXEventListener() { - // TODO override fun onToDeviceEvent(event: Event?) { - // TODO if (event!!.getType() == CryptoEvent.EVENT_TYPE_KEY_VERIFICATION_CANCEL) { - // TODO if (event.contentAsJsonObject?.get("transaction_id")?.asString == tid) { - // TODO canceledToDeviceEvent = event - // TODO cancelLatch.countDown() - // TODO } - // TODO } - // TODO } - // TODO }) - - val aliceSession = cryptoTestData.firstSession - val aliceUserID = aliceSession.myUserId - val aliceDevice = aliceSession.cryptoService().getMyCryptoDevice().deviceId - - fakeBobStart(bobSession, aliceUserID, aliceDevice, tid, codes = codes) - - testHelper.await(cancelLatch) - - val cancelReq = canceledToDeviceEvent!!.content.toModel()!! - assertEquals("Request should be cancelled with m.unknown_method", CancelCode.UnknownMethod.value, cancelReq.code) - } - - private suspend fun fakeBobStart( - bobSession: Session, - aliceUserID: String?, - aliceDevice: String?, - tid: String, - protocols: List = SasVerificationTransaction.KNOWN_AGREEMENT_PROTOCOLS, - hashes: List = SasVerificationTransaction.KNOWN_HASHES, - mac: List = SasVerificationTransaction.KNOWN_MACS, - codes: List = SasVerificationTransaction.KNOWN_SHORT_CODES - ) { - val startMessage = KeyVerificationStart( - fromDevice = bobSession.cryptoService().getMyCryptoDevice().deviceId, - method = VerificationMethod.SAS.toValue(), - transactionId = tid, - keyAgreementProtocols = protocols, - hashes = hashes, - messageAuthenticationCodes = mac, - shortAuthenticationStrings = codes - ) - - val contentMap = MXUsersDevicesMap() - contentMap.setObject(aliceUserID, aliceDevice, startMessage) - - // TODO val sendLatch = CountDownLatch(1) - // TODO bobSession.cryptoRestClient.sendToDevice( - // TODO EventType.KEY_VERIFICATION_START, - // TODO contentMap, - // TODO tid, - // TODO TestMatrixCallback(sendLatch) - // TODO ) - } - - // any two devices may only have at most one key verification in flight at a time. - // If a device has two verifications in progress with the same device, then it should cancel both verifications. - @Test - fun test_aliceStartTwoRequests() = runCryptoTest(context()) { cryptoTestHelper, testHelper -> - val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom() - + Timber.v("verification: initializeCrossSigning") + cryptoTestData.initializeCrossSigning(cryptoTestHelper) val aliceSession = cryptoTestData.firstSession val bobSession = cryptoTestData.secondSession val aliceVerificationService = aliceSession.cryptoService().verificationService() + val bobVerificationService = bobSession!!.cryptoService().verificationService() - val aliceCreatedLatch = CountDownLatch(2) - val aliceCancelledLatch = CountDownLatch(1) - val createdTx = mutableListOf() - val aliceListener = object : VerificationService.Listener { - override fun transactionCreated(tx: VerificationTransaction) { - createdTx.add(tx) - aliceCreatedLatch.countDown() + Timber.v("verification: requestVerificationAndWaitForReadyState") + val txId = SasVerificationTestHelper(testHelper) + .requestVerificationAndWaitForReadyState(cryptoTestData, listOf(VerificationMethod.SAS)) + + Timber.v("verification: startKeyVerification") + aliceVerificationService.startKeyVerification( + VerificationMethod.SAS, + bobSession.myUserId, + txId + ) + + Timber.v("verification: ensure bob has received starete") + testHelper.retryWithBackoff { + bobVerificationService.getExistingVerificationRequest(aliceSession.myUserId, txId)?.state == EVerificationState.Started + } + + val bobKeyTx = bobVerificationService.getExistingTransaction(aliceSession.myUserId, txId) + + assertNotNull("Bob should have started verif transaction", bobKeyTx) + assertTrue(bobKeyTx is SasVerificationTransaction) + + val aliceKeyTx = aliceVerificationService.getExistingTransaction(bobSession.myUserId, txId) + assertTrue(aliceKeyTx is SasVerificationTransaction) + + assertEquals("Alice and Bob have same transaction id", aliceKeyTx!!.transactionId, bobKeyTx!!.transactionId) + + val aliceCancelled = CompletableDeferred() + aliceVerificationService.requestEventFlow().onEach { + println("alice flow event $it") + if (it is VerificationEvent.TransactionUpdated && it.transactionId == txId) { + val sasVerificationTransaction = it.transaction as SasVerificationTransaction + if (sasVerificationTransaction.state() is SasTransactionState.Cancelled) { + aliceCancelled.complete(Unit) + } } + }.launchIn(scope) - override fun transactionUpdated(tx: VerificationTransaction) { - tx as SasVerificationTransaction - if (tx.state() is SasTransactionState.Cancelled && !(tx.state() as SasTransactionState.Cancelled).byMe) { - aliceCancelledLatch.countDown() + val bobCancelled = CompletableDeferred() + bobVerificationService.requestEventFlow().onEach { + println("alice flow event $it") + if (it is VerificationEvent.TransactionUpdated && it.transactionId == txId) { + val sasVerificationTransaction = it.transaction as SasVerificationTransaction + if (sasVerificationTransaction.state() is SasTransactionState.Cancelled) { + bobCancelled.complete(Unit) + } + } + }.launchIn(scope) + + aliceVerificationService.cancelVerificationRequest(bobSession.myUserId, txId) + + aliceCancelled.await() + bobCancelled.await() + + val cancelledAlice = aliceVerificationService.getExistingVerificationRequest(bobSession.myUserId, txId)!! + val cancelledBob = aliceVerificationService.getExistingVerificationRequest(aliceSession.myUserId, txId)!! + + assertEquals("Should be cancelled on alice side", cancelledAlice.state, EVerificationState.Cancelled) + assertEquals("Should be cancelled on alice side", cancelledBob.state, EVerificationState.Cancelled) + + assertEquals("Should be User cancelled on alice side", CancelCode.User, cancelledAlice.cancelConclusion) + assertEquals("Should be User cancelled on bob side", CancelCode.User, cancelledBob.cancelConclusion) + + assertNull(bobVerificationService.getExistingTransaction(aliceSession.myUserId, txId)) + assertNull(aliceVerificationService.getExistingTransaction(bobSession.myUserId, txId)) + } + + /* +@Test +@Ignore("This test will be ignored until it is fixed") +fun test_key_agreement_protocols_must_include_curve25519() = runCryptoTest(context()) { cryptoTestHelper, testHelper -> + fail("Not passing for the moment") + val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom() + + val bobSession = cryptoTestData.secondSession!! + + val protocols = listOf("meh_dont_know") + val tid = "00000000" + + // Bob should receive a cancel + var cancelReason: CancelCode? = null + val cancelLatch = CountDownLatch(1) + + val bobListener = object : VerificationService.Listener { + override fun transactionUpdated(tx: VerificationTransaction) { + tx as SasVerificationTransaction + if (tx.transactionId == tid && tx.state() is SasTransactionState.Cancelled) { + cancelReason = (tx.state() as SasTransactionState.Cancelled).cancelCode + cancelLatch.countDown() + } + } + } +// bobSession.cryptoService().verificationService().addListener(bobListener) + + // TODO bobSession!!.dataHandler.addListener(object : MXEventListener() { + // TODO override fun onToDeviceEvent(event: Event?) { + // TODO if (event!!.getType() == CryptoEvent.EVENT_TYPE_KEY_VERIFICATION_CANCEL) { + // TODO if (event.contentAsJsonObject?.get("transaction_id")?.asString == tid) { + // TODO canceledToDeviceEvent = event + // TODO cancelLatch.countDown() + // TODO } + // TODO } + // TODO } + // TODO }) + + val aliceSession = cryptoTestData.firstSession + val aliceUserID = aliceSession.myUserId + val aliceDevice = aliceSession.cryptoService().getMyCryptoDevice().deviceId + + val aliceListener = object : VerificationService.Listener { + override fun transactionUpdated(tx: VerificationTransaction) { + tx as SasVerificationTransaction + if (tx.state() is SasTransactionState.SasStarted) { + runBlocking { + tx.acceptVerification() } } } + } +// aliceSession.cryptoService().verificationService().addListener(aliceListener) + + fakeBobStart(bobSession, aliceUserID, aliceDevice, tid, protocols = protocols) + + testHelper.await(cancelLatch) + + assertEquals("Request should be cancelled with m.unknown_method", CancelCode.UnknownMethod, cancelReason) +} + +@Test +@Ignore("This test will be ignored until it is fixed") +fun test_key_agreement_macs_Must_include_hmac_sha256() = runCryptoTest(context()) { cryptoTestHelper, testHelper -> + fail("Not passing for the moment") + val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom() + + val bobSession = cryptoTestData.secondSession!! + + val mac = listOf("shaBit") + val tid = "00000000" + + // Bob should receive a cancel + val canceledToDeviceEvent: Event? = null + val cancelLatch = CountDownLatch(1) + // TODO bobSession!!.dataHandler.addListener(object : MXEventListener() { + // TODO override fun onToDeviceEvent(event: Event?) { + // TODO if (event!!.getType() == CryptoEvent.EVENT_TYPE_KEY_VERIFICATION_CANCEL) { + // TODO if (event.contentAsJsonObject?.get("transaction_id")?.asString == tid) { + // TODO canceledToDeviceEvent = event + // TODO cancelLatch.countDown() + // TODO } + // TODO } + // TODO } + // TODO }) + + val aliceSession = cryptoTestData.firstSession + val aliceUserID = aliceSession.myUserId + val aliceDevice = aliceSession.cryptoService().getMyCryptoDevice().deviceId + + fakeBobStart(bobSession, aliceUserID, aliceDevice, tid, mac = mac) + + testHelper.await(cancelLatch) + val cancelReq = canceledToDeviceEvent!!.content.toModel()!! + assertEquals("Request should be cancelled with m.unknown_method", CancelCode.UnknownMethod.value, cancelReq.code) +} + +@Test +@Ignore("This test will be ignored until it is fixed") +fun test_key_agreement_short_code_include_decimal() = runCryptoTest(context()) { cryptoTestHelper, testHelper -> + fail("Not passing for the moment") + val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom() + + val bobSession = cryptoTestData.secondSession!! + + val codes = listOf("bin", "foo", "bar") + val tid = "00000000" + + // Bob should receive a cancel + var canceledToDeviceEvent: Event? = null + val cancelLatch = CountDownLatch(1) + // TODO bobSession!!.dataHandler.addListener(object : MXEventListener() { + // TODO override fun onToDeviceEvent(event: Event?) { + // TODO if (event!!.getType() == CryptoEvent.EVENT_TYPE_KEY_VERIFICATION_CANCEL) { + // TODO if (event.contentAsJsonObject?.get("transaction_id")?.asString == tid) { + // TODO canceledToDeviceEvent = event + // TODO cancelLatch.countDown() + // TODO } + // TODO } + // TODO } + // TODO }) + + val aliceSession = cryptoTestData.firstSession + val aliceUserID = aliceSession.myUserId + val aliceDevice = aliceSession.cryptoService().getMyCryptoDevice().deviceId + + fakeBobStart(bobSession, aliceUserID, aliceDevice, tid, codes = codes) + + testHelper.await(cancelLatch) + + val cancelReq = canceledToDeviceEvent!!.content.toModel()!! + assertEquals("Request should be cancelled with m.unknown_method", CancelCode.UnknownMethod.value, cancelReq.code) +} + +private suspend fun fakeBobStart( + bobSession: Session, + aliceUserID: String?, + aliceDevice: String?, + tid: String, + protocols: List = SasVerificationTransaction.KNOWN_AGREEMENT_PROTOCOLS, + hashes: List = SasVerificationTransaction.KNOWN_HASHES, + mac: List = SasVerificationTransaction.KNOWN_MACS, + codes: List = SasVerificationTransaction.KNOWN_SHORT_CODES +) { + val startMessage = KeyVerificationStart( + fromDevice = bobSession.cryptoService().getMyCryptoDevice().deviceId, + method = VerificationMethod.SAS.toValue(), + transactionId = tid, + keyAgreementProtocols = protocols, + hashes = hashes, + messageAuthenticationCodes = mac, + shortAuthenticationStrings = codes + ) + + val contentMap = MXUsersDevicesMap() + contentMap.setObject(aliceUserID, aliceDevice, startMessage) + + // TODO val sendLatch = CountDownLatch(1) + // TODO bobSession.cryptoRestClient.sendToDevice( + // TODO EventType.KEY_VERIFICATION_START, + // TODO contentMap, + // TODO tid, + // TODO TestMatrixCallback(sendLatch) + // TODO ) +} + +// any two devices may only have at most one key verification in flight at a time. +// If a device has two verifications in progress with the same device, then it should cancel both verifications. +@Test +fun test_aliceStartTwoRequests() = runCryptoTest(context()) { cryptoTestHelper, testHelper -> + val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom() + + val aliceSession = cryptoTestData.firstSession + val bobSession = cryptoTestData.secondSession + + val aliceVerificationService = aliceSession.cryptoService().verificationService() + + val aliceCreatedLatch = CountDownLatch(2) + val aliceCancelledLatch = CountDownLatch(1) + val createdTx = mutableListOf() + val aliceListener = object : VerificationService.Listener { + override fun transactionCreated(tx: VerificationTransaction) { + createdTx.add(tx) + aliceCreatedLatch.countDown() + } + + override fun transactionUpdated(tx: VerificationTransaction) { + tx as SasVerificationTransaction + if (tx.state() is SasTransactionState.Cancelled && !(tx.state() as SasTransactionState.Cancelled).byMe) { + aliceCancelledLatch.countDown() + } + } + } // aliceVerificationService.addListener(aliceListener) - val bobUserId = bobSession!!.myUserId - val bobDeviceId = bobSession.cryptoService().getMyCryptoDevice().deviceId + val bobUserId = bobSession!!.myUserId + val bobDeviceId = bobSession.cryptoService().getMyCryptoDevice().deviceId - // TODO + // TODO // aliceSession.cryptoService().downloadKeysIfNeeded(listOf(bobUserId), forceDownload = true) // aliceVerificationService.beginKeyVerification(listOf(VerificationMethod.SAS), bobUserId, bobDeviceId) // aliceVerificationService.beginKeyVerification(bobUserId, bobDeviceId) // testHelper.await(aliceCreatedLatch) // testHelper.await(aliceCancelledLatch) - cryptoTestData.cleanUp(testHelper) - } + cryptoTestData.cleanUp(testHelper) +} - /** +/** * Test that when alice starts a 'correct' request, bob agrees. */ // @Test @@ -416,65 +434,65 @@ class SASTest : InstrumentedTest { // ) // } - @Test - fun test_happyPath() = runCryptoTest(context()) { cryptoTestHelper, testHelper -> - val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom() - cryptoTestData.initializeCrossSigning(cryptoTestHelper) - val sasVerificationTestHelper = SasVerificationTestHelper(testHelper, cryptoTestHelper) - val transactionId = sasVerificationTestHelper.requestVerificationAndWaitForReadyState(cryptoTestData, listOf(VerificationMethod.SAS)) - val aliceSession = cryptoTestData.firstSession - val bobSession = cryptoTestData.secondSession +@Test +fun test_happyPath() = runCryptoTest(context()) { cryptoTestHelper, testHelper -> + val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom() + cryptoTestData.initializeCrossSigning(cryptoTestHelper) + val sasVerificationTestHelper = SasVerificationTestHelper(testHelper, cryptoTestHelper) + val transactionId = sasVerificationTestHelper.requestVerificationAndWaitForReadyState(cryptoTestData, listOf(VerificationMethod.SAS)) + val aliceSession = cryptoTestData.firstSession + val bobSession = cryptoTestData.secondSession - val aliceVerificationService = aliceSession.cryptoService().verificationService() - val bobVerificationService = bobSession!!.cryptoService().verificationService() + val aliceVerificationService = aliceSession.cryptoService().verificationService() + val bobVerificationService = bobSession!!.cryptoService().verificationService() - val verifiedLatch = CountDownLatch(2) - val aliceListener = object : VerificationService.Listener { + val verifiedLatch = CountDownLatch(2) + val aliceListener = object : VerificationService.Listener { - override fun verificationRequestUpdated(pr: PendingVerificationRequest) { - Timber.v("RequestUpdated pr=$pr") - } + override fun verificationRequestUpdated(pr: PendingVerificationRequest) { + Timber.v("RequestUpdated pr=$pr") + } - var matched = false - var verified = false - override fun transactionUpdated(tx: VerificationTransaction) { - if (tx !is SasVerificationTransaction) return - Timber.v("Alice transactionUpdated: ${tx.state()} on thread:${Thread.currentThread()}") - when (tx.state()) { - SasTransactionState.SasShortCodeReady -> { - if (!matched) { - matched = true - runBlocking { - delay(500) - tx.userHasVerifiedShortCode() - } + var matched = false + var verified = false + override fun transactionUpdated(tx: VerificationTransaction) { + if (tx !is SasVerificationTransaction) return + Timber.v("Alice transactionUpdated: ${tx.state()} on thread:${Thread.currentThread()}") + when (tx.state()) { + SasTransactionState.SasShortCodeReady -> { + if (!matched) { + matched = true + runBlocking { + delay(500) + tx.userHasVerifiedShortCode() } } - is SasTransactionState.Done -> { - if (!verified) { - verified = true - verifiedLatch.countDown() - } - } - else -> Unit } + is SasTransactionState.Done -> { + if (!verified) { + verified = true + verifiedLatch.countDown() + } + } + else -> Unit } } + } // aliceVerificationService.addListener(aliceListener) - val bobListener = object : VerificationService.Listener { - var accepted = false - var matched = false - var verified = false + val bobListener = object : VerificationService.Listener { + var accepted = false + var matched = false + var verified = false - override fun verificationRequestUpdated(pr: PendingVerificationRequest) { - Timber.v("RequestUpdated: pr=$pr") - } + override fun verificationRequestUpdated(pr: PendingVerificationRequest) { + Timber.v("RequestUpdated: pr=$pr") + } - override fun transactionUpdated(tx: VerificationTransaction) { - if (tx !is SasVerificationTransaction) return - Timber.v("Bob transactionUpdated: ${tx.state()} on thread: ${Thread.currentThread()}") - when (tx.state()) { + override fun transactionUpdated(tx: VerificationTransaction) { + if (tx !is SasVerificationTransaction) return + Timber.v("Bob transactionUpdated: ${tx.state()} on thread: ${Thread.currentThread()}") + when (tx.state()) { // VerificationTxState.SasStarted -> { // if (!accepted) { // accepted = true @@ -483,113 +501,113 @@ class SASTest : InstrumentedTest { // } // } // } - SasTransactionState.SasShortCodeReady -> { - if (!matched) { - matched = true - runBlocking { - delay(500) - tx.userHasVerifiedShortCode() - } + SasTransactionState.SasShortCodeReady -> { + if (!matched) { + matched = true + runBlocking { + delay(500) + tx.userHasVerifiedShortCode() } } - is SasTransactionState.Done -> { - if (!verified) { - verified = true - verifiedLatch.countDown() - } - } - else -> Unit } + is SasTransactionState.Done -> { + if (!verified) { + verified = true + verifiedLatch.countDown() + } + } + else -> Unit } } + } // bobVerificationService.addListener(bobListener) - val bobUserId = bobSession.myUserId - val bobDeviceId = runBlocking { - bobSession.cryptoService().getMyCryptoDevice().deviceId - } - aliceVerificationService.startKeyVerification(VerificationMethod.SAS, bobUserId, transactionId) - - Timber.v("Await after beginKey ${Thread.currentThread()}") - testHelper.await(verifiedLatch) - - // Assert that devices are verified - val bobDeviceInfoFromAlicePOV: CryptoDeviceInfo? = aliceSession.cryptoService().getCryptoDeviceInfo(bobUserId, bobDeviceId) - val aliceDeviceInfoFromBobPOV: CryptoDeviceInfo? = - bobSession.cryptoService().getCryptoDeviceInfo(aliceSession.myUserId, aliceSession.cryptoService().getMyCryptoDevice().deviceId) - - assertTrue("alice device should be verified from bob point of view", aliceDeviceInfoFromBobPOV!!.isVerified) - assertTrue("bob device should be verified from alice point of view", bobDeviceInfoFromAlicePOV!!.isVerified) + val bobUserId = bobSession.myUserId + val bobDeviceId = runBlocking { + bobSession.cryptoService().getMyCryptoDevice().deviceId } + aliceVerificationService.startKeyVerification(VerificationMethod.SAS, bobUserId, transactionId) - @Test - fun test_ConcurrentStart() = runCryptoTest(context()) { cryptoTestHelper, testHelper -> - val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom() - cryptoTestData.initializeCrossSigning(cryptoTestHelper) - val aliceSession = cryptoTestData.firstSession - val bobSession = cryptoTestData.secondSession!! + Timber.v("Await after beginKey ${Thread.currentThread()}") + testHelper.await(verifiedLatch) - val aliceVerificationService = aliceSession.cryptoService().verificationService() - val bobVerificationService = bobSession.cryptoService().verificationService() + // Assert that devices are verified + val bobDeviceInfoFromAlicePOV: CryptoDeviceInfo? = aliceSession.cryptoService().getCryptoDeviceInfo(bobUserId, bobDeviceId) + val aliceDeviceInfoFromBobPOV: CryptoDeviceInfo? = + bobSession.cryptoService().getCryptoDeviceInfo(aliceSession.myUserId, aliceSession.cryptoService().getMyCryptoDevice().deviceId) - val req = aliceVerificationService.requestKeyVerificationInDMs( - listOf(VerificationMethod.SAS, VerificationMethod.QR_CODE_SCAN, VerificationMethod.QR_CODE_SHOW), - bobSession.myUserId, - cryptoTestData.roomId - ) + assertTrue("alice device should be verified from bob point of view", aliceDeviceInfoFromBobPOV!!.isVerified) + assertTrue("bob device should be verified from alice point of view", bobDeviceInfoFromAlicePOV!!.isVerified) +} - val requestID = req.transactionId +@Test +fun test_ConcurrentStart() = runCryptoTest(context()) { cryptoTestHelper, testHelper -> + val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom() + cryptoTestData.initializeCrossSigning(cryptoTestHelper) + val aliceSession = cryptoTestData.firstSession + val bobSession = cryptoTestData.secondSession!! - Log.v("TEST", "== requestID is $requestID") + val aliceVerificationService = aliceSession.cryptoService().verificationService() + val bobVerificationService = bobSession.cryptoService().verificationService() - testHelper.retryPeriodically { - val prBobPOV = bobVerificationService.getExistingVerificationRequests(aliceSession.myUserId).firstOrNull() - Log.v("TEST", "== prBobPOV is $prBobPOV") - prBobPOV?.transactionId == requestID - } - - bobVerificationService.readyPendingVerification( + val req = aliceVerificationService.requestKeyVerificationInDMs( listOf(VerificationMethod.SAS, VerificationMethod.QR_CODE_SCAN, VerificationMethod.QR_CODE_SHOW), - aliceSession.myUserId, - requestID + bobSession.myUserId, + cryptoTestData.roomId ) - // wait for alice to get the ready - testHelper.retryPeriodically { - val prAlicePOV = aliceVerificationService.getExistingVerificationRequests(bobSession.myUserId).firstOrNull() - Log.v("TEST", "== prAlicePOV is $prAlicePOV") - prAlicePOV?.transactionId == requestID && prAlicePOV.state == EVerificationState.Ready - } + val requestID = req.transactionId - // Start concurrent! - aliceVerificationService.startKeyVerification( - method = VerificationMethod.SAS, - otherUserId = bobSession.myUserId, - requestId = requestID, - ) + Log.v("TEST", "== requestID is $requestID") - bobVerificationService.startKeyVerification( - method = VerificationMethod.SAS, - otherUserId = aliceSession.myUserId, - requestId = requestID, - ) - - // we should reach SHOW SAS on both - var alicePovTx: SasVerificationTransaction? - var bobPovTx: SasVerificationTransaction? - - testHelper.retryPeriodically { - alicePovTx = aliceVerificationService.getExistingTransaction(bobSession.myUserId, requestID) as? SasVerificationTransaction - Log.v("TEST", "== alicePovTx is $alicePovTx") - alicePovTx?.state() == SasTransactionState.SasShortCodeReady - } - // wait for alice to get the ready - testHelper.retryPeriodically { - bobPovTx = bobVerificationService.getExistingTransaction(aliceSession.myUserId, requestID) as? SasVerificationTransaction - Log.v("TEST", "== bobPovTx is $bobPovTx") - bobPovTx?.state() == SasTransactionState.SasShortCodeReady - } + testHelper.retryPeriodically { + val prBobPOV = bobVerificationService.getExistingVerificationRequests(aliceSession.myUserId).firstOrNull() + Log.v("TEST", "== prBobPOV is $prBobPOV") + prBobPOV?.transactionId == requestID } + bobVerificationService.readyPendingVerification( + listOf(VerificationMethod.SAS, VerificationMethod.QR_CODE_SCAN, VerificationMethod.QR_CODE_SHOW), + aliceSession.myUserId, + requestID + ) + + // wait for alice to get the ready + testHelper.retryPeriodically { + val prAlicePOV = aliceVerificationService.getExistingVerificationRequests(bobSession.myUserId).firstOrNull() + Log.v("TEST", "== prAlicePOV is $prAlicePOV") + prAlicePOV?.transactionId == requestID && prAlicePOV.state == EVerificationState.Ready + } + + // Start concurrent! + aliceVerificationService.startKeyVerification( + method = VerificationMethod.SAS, + otherUserId = bobSession.myUserId, + requestId = requestID, + ) + + bobVerificationService.startKeyVerification( + method = VerificationMethod.SAS, + otherUserId = aliceSession.myUserId, + requestId = requestID, + ) + + // we should reach SHOW SAS on both + var alicePovTx: SasVerificationTransaction? + var bobPovTx: SasVerificationTransaction? + + testHelper.retryPeriodically { + alicePovTx = aliceVerificationService.getExistingTransaction(bobSession.myUserId, requestID) as? SasVerificationTransaction + Log.v("TEST", "== alicePovTx is $alicePovTx") + alicePovTx?.state() == SasTransactionState.SasShortCodeReady + } + // wait for alice to get the ready + testHelper.retryPeriodically { + bobPovTx = bobVerificationService.getExistingTransaction(aliceSession.myUserId, requestID) as? SasVerificationTransaction + Log.v("TEST", "== bobPovTx is $bobPovTx") + bobPovTx?.state() == SasTransactionState.SasShortCodeReady + } +} + */ } diff --git a/matrix-sdk-android/src/kotlinCrypto/java/org/matrix/android/sdk/internal/crypto/verification/VerificationActor.kt b/matrix-sdk-android/src/kotlinCrypto/java/org/matrix/android/sdk/internal/crypto/verification/VerificationActor.kt index 994fc2f33b..66a54bfdc5 100644 --- a/matrix-sdk-android/src/kotlinCrypto/java/org/matrix/android/sdk/internal/crypto/verification/VerificationActor.kt +++ b/matrix-sdk-android/src/kotlinCrypto/java/org/matrix/android/sdk/internal/crypto/verification/VerificationActor.kt @@ -263,6 +263,8 @@ internal class VerificationActor @AssistedInject constructor( } is VerificationIntent.GetExistingRequestsForUser -> { verificationRequestsStore.getExistingRequestsForUser(msg.userId).let { requests -> + Timber.tag(loggerTag.value) + .v("[${myUserId.take(8)}]: Found $requests") msg.deferred.complete(requests.map { it.toPendingVerificationRequest() }) } } @@ -306,6 +308,8 @@ internal class VerificationActor @AssistedInject constructor( private fun dispatchUpdate(update: VerificationEvent) { // We don't want to block on emit. // If no subscriber there is a small buffer + Timber.tag(loggerTag.value) + .v("[${myUserId.take(8)}] Dispatch Request update ${update.transactionId}") scope.launch { eventFlow.emit(update) } @@ -565,21 +569,29 @@ internal class VerificationActor @AssistedInject constructor( private suspend fun handleSasStart(msg: VerificationIntent.ActionStartSasVerification) { val matchingRequest = verificationRequestsStore.getExistingRequestWithRequestId(msg.requestId) ?: return Unit.also { + Timber.tag(loggerTag.value) + .v("[${myUserId.take(8)}]: Can't start unknown request ${msg.requestId}") msg.deferred.completeExceptionally(java.lang.IllegalArgumentException("Unknown request")) } if (matchingRequest.state != EVerificationState.Ready) { + Timber.tag(loggerTag.value) + .v("[${myUserId.take(8)}]: Can't start a non ready request ${msg.requestId}") msg.deferred.completeExceptionally(java.lang.IllegalStateException("Can't start a non ready request")) return } val otherDeviceId = matchingRequest.otherDeviceId() ?: return Unit.also { + Timber.tag(loggerTag.value) + .v("[${myUserId.take(8)}]: Can't start null other device id ${msg.requestId}") msg.deferred.completeExceptionally(java.lang.IllegalArgumentException("Failed to find other device Id")) } val existingTransaction = getExistingTransaction(msg.otherUserId, msg.requestId) if (existingTransaction is SasVerificationTransaction) { // there is already an existing transaction?? + Timber.tag(loggerTag.value) + .v("[${myUserId.take(8)}]: Can't start, already started ${msg.requestId}") msg.deferred.completeExceptionally(IllegalStateException("Already started")) return } @@ -589,12 +601,17 @@ internal class VerificationActor @AssistedInject constructor( requestId = msg.requestId ) + Timber.tag(loggerTag.value) + .v("[${myUserId.take(8)}]:sending start to other ${msg.requestId} in room ${matchingRequest.roomId}") transportLayer.sendToOther( matchingRequest, EventType.KEY_VERIFICATION_START, startMessage, ) + Timber.tag(loggerTag.value) + .v("[${myUserId.take(8)}]: start sent to other ${msg.requestId}") + // should check if already one (and cancel it) val tx = KotlinSasTransaction( channel = channel, @@ -1262,6 +1279,9 @@ internal class VerificationActor @AssistedInject constructor( null } + Timber.tag(loggerTag.value) + .v("[${myUserId.take(8)}] Request ${msg.transactionId} code is $qrCodeData") + val readyInfo = ValidVerificationInfoReady( msg.transactionId, verificationTrustBackend.getMyDeviceId(), @@ -1274,9 +1294,14 @@ internal class VerificationActor @AssistedInject constructor( methods = commonMethods, fromDevice = verificationTrustBackend.getMyDeviceId() ) + + Timber.tag(loggerTag.value) + .v("[${myUserId.take(8)}] Request ${msg.transactionId} sending ready") try { transportLayer.sendToOther(existing, EventType.KEY_VERIFICATION_READY, message) } catch (failure: Throwable) { + Timber.tag(loggerTag.value) + .v("[${myUserId.take(8)}] Request ${msg.transactionId} failed to send ready") msg.deferred.completeExceptionally(failure) return } diff --git a/matrix-sdk-android/src/kotlinCrypto/java/org/matrix/android/sdk/internal/crypto/verification/VerificationMessageProcessor.kt b/matrix-sdk-android/src/kotlinCrypto/java/org/matrix/android/sdk/internal/crypto/verification/VerificationMessageProcessor.kt index db9ecf31ff..b15dc60bbf 100644 --- a/matrix-sdk-android/src/kotlinCrypto/java/org/matrix/android/sdk/internal/crypto/verification/VerificationMessageProcessor.kt +++ b/matrix-sdk-android/src/kotlinCrypto/java/org/matrix/android/sdk/internal/crypto/verification/VerificationMessageProcessor.kt @@ -18,9 +18,9 @@ package org.matrix.android.sdk.internal.crypto.verification import org.matrix.android.sdk.api.session.crypto.verification.VerificationService 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.getRelationContent import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.room.model.message.MessageContent -import org.matrix.android.sdk.api.session.room.model.message.MessageRelationContent import org.matrix.android.sdk.api.session.room.model.message.MessageType import org.matrix.android.sdk.api.session.room.model.message.MessageVerificationReadyContent import org.matrix.android.sdk.internal.di.DeviceId @@ -55,19 +55,19 @@ internal class VerificationMessageProcessor @Inject constructor( } suspend fun process(roomId: String, event: Event) { - Timber.v("## SAS Verification live observer: received msgId: ${event.eventId} msgtype: ${event.getClearType()} from ${event.senderId}") + Timber.v("## SAS Verification[${userId.take(5)}] live observer: received msgId: ${event.eventId} msgtype: ${event.getClearType()} from ${event.senderId}") // If the request is in the future by more than 5 minutes or more than 10 minutes in the past, // the message should be ignored by the receiver. if (!VerificationService.isValidRequest(event.ageLocalTs, clock.epochMillis())) return Unit.also { - Timber.d("## SAS Verification live observer: msgId: ${event.eventId} is outdated age:${event.ageLocalTs} ms") + Timber.d("## SAS Verification[${userId.take(5)}] live observer: msgId: ${event.eventId} is outdated age:${event.ageLocalTs} ms") } - Timber.v("## SAS Verification live observer: received msgId: ${event.eventId} type: ${event.getClearType()}") + Timber.v("## SAS Verification[${userId.take(5)}] live observer: received msgId: ${event.eventId} type: ${event.getClearType()}") // Relates to is not encrypted - val relatesToEventId = event.content.toModel()?.relatesTo?.eventId + val relatesToEventId = event.getRelationContent()?.eventId if (event.senderId == userId) { // If it's send from me, we need to keep track of Requests or Start @@ -78,7 +78,7 @@ internal class VerificationMessageProcessor @Inject constructor( // event.getClearContent().toModel()?.let { // if (it.fromDevice != deviceId) { // // The verification is requested from another device -// Timber.v("## SAS Verification live observer: Transaction requested from other device tid:${event.eventId} ") +// Timber.v("## SAS Verification[$userItakeng5 live observer: Transaction requested from other device tid:${event.eventId} ") // event.eventId?.let { txId -> transactionsHandledByOtherDevice.add(txId) } // } // } @@ -87,7 +87,7 @@ internal class VerificationMessageProcessor @Inject constructor( // event.getClearContent().toModel()?.let { // if (it.fromDevice != deviceId) { // // The verification is started from another device -// Timber.v("## SAS Verification live observer: Transaction started by other device tid:$relatesToEventId ") +// Timber.v("## SAS Verification[$userItakeng5 live observer: Transaction started by other device tid:$relatesToEventId ") // relatesToEventId?.let { txId -> transactionsHandledByOtherDevice.add(txId) } // verificationService.onRoomRequestHandledByOtherDevice(event) // } @@ -98,14 +98,15 @@ internal class VerificationMessageProcessor @Inject constructor( event.getClearContent().toModel()?.let { if (it.fromDevice != deviceId) { // The verification is started from another device - Timber.v("## SAS Verification live observer: Transaction started by other device tid:$relatesToEventId ") + Timber.v("## SAS Verification[${userId.take(5)}] live observer: Transaction started by other device tid:$relatesToEventId ") relatesToEventId?.let { txId -> transactionsHandledByOtherDevice.add(txId) } verificationService.onRoomReadyFromOneOfMyOtherDevice(event) } } - } else { - Timber.v("## SAS Verification ignoring message sent by me: ${event.eventId} type: ${event.getClearType()}") } +// else { +// Timber.v("## SAS Verification[${userId.take(5)}] ignoring message sent by me: ${event.eventId} type: ${event.getClearType()}") +// } // } else if (EventType.KEY_VERIFICATION_CANCEL == event.getClearType() || EventType.KEY_VERIFICATION_DONE == event.getClearType()) { // relatesToEventId?.let { // transactionsHandledByOtherDevice.remove(it) @@ -114,13 +115,13 @@ internal class VerificationMessageProcessor @Inject constructor( // } else if (EventType.ENCRYPTED == event.getClearType()) { // verificationService.onPotentiallyInterestingEventRoomFailToDecrypt(event) // } - + Timber.v("## SAS Verification[${userId.take(5)}] discard from me msgId: ${event.eventId}") return } if (relatesToEventId != null && transactionsHandledByOtherDevice.contains(relatesToEventId)) { // Ignore this event, it is directed to another of my devices - Timber.v("## SAS Verification live observer: Ignore Transaction handled by other device tid:$relatesToEventId ") + Timber.v("## SAS Verification[${userId.take(5)}] live observer: Ignore Transaction handled by other device tid:$relatesToEventId ") return } when (event.getClearType()) { diff --git a/matrix-sdk-android/src/rustCrypto/java/org/matrix/android/sdk/internal/crypto/SecretShareManager.kt b/matrix-sdk-android/src/rustCrypto/java/org/matrix/android/sdk/internal/crypto/SecretShareManager.kt index 93c297ca3c..b4fab22677 100644 --- a/matrix-sdk-android/src/rustCrypto/java/org/matrix/android/sdk/internal/crypto/SecretShareManager.kt +++ b/matrix-sdk-android/src/rustCrypto/java/org/matrix/android/sdk/internal/crypto/SecretShareManager.kt @@ -16,11 +16,15 @@ package org.matrix.android.sdk.internal.crypto +import org.matrix.android.sdk.BuildConfig +import timber.log.Timber import javax.inject.Inject internal class SecretShareManager @Inject constructor() { suspend fun requestSecretTo(deviceId: String, secretName: String) { // nop in rust? + if (BuildConfig.DEBUG) TODO("requestSecretTo Not implemented in Rust") + Timber.e("SecretShareManager Not supported in rust $deviceId, $secretName") } } diff --git a/matrix-sdk-android/src/rustCrypto/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/UnRequestedForwardManager.kt b/matrix-sdk-android/src/rustCrypto/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/UnRequestedForwardManager.kt index 38440b41c0..85c48ce28a 100644 --- a/matrix-sdk-android/src/rustCrypto/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/UnRequestedForwardManager.kt +++ b/matrix-sdk-android/src/rustCrypto/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/UnRequestedForwardManager.kt @@ -16,11 +16,13 @@ package org.matrix.android.sdk.internal.crypto.algorithms.megolm +import timber.log.Timber import javax.inject.Inject // empty in rust class UnRequestedForwardManager @Inject constructor() { - fun onInviteReceived(roomId: String, orEmpty: String, epochMillis: Long) { + fun onInviteReceived(roomId: String, inviterId: String, epochMillis: Long) { + Timber.e("UnRequestedForwardManager not yet implemented $roomId, $inviterId, $epochMillis") } } diff --git a/matrix-sdk-android/src/rustCrypto/java/org/matrix/android/sdk/internal/crypto/network/RequestSender.kt b/matrix-sdk-android/src/rustCrypto/java/org/matrix/android/sdk/internal/crypto/network/RequestSender.kt index 982f711a2f..cef0629971 100644 --- a/matrix-sdk-android/src/rustCrypto/java/org/matrix/android/sdk/internal/crypto/network/RequestSender.kt +++ b/matrix-sdk-android/src/rustCrypto/java/org/matrix/android/sdk/internal/crypto/network/RequestSender.kt @@ -239,9 +239,9 @@ internal class RequestSender @Inject constructor( val hashMap = content as? Map<*, *> val action = hashMap?.get("action")?.toString() if (GossipingToDeviceObject.ACTION_SHARE_REQUEST == action) { - val body = hashMap.get("body") as? Map<*, *> - val roomId = body?.get("room_id") as? String - val sessionId = body?.get("session_id") as? String + val requestBody = hashMap["body"] as? Map<*, *> + val roomId = requestBody?.get("room_id") as? String + val sessionId = requestBody?.get("session_id") as? String if (roomId != null && sessionId != null) { rateLimiter.tryFromBackupIfPossible(sessionId, roomId) }