mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2024-11-28 22:18:46 +03:00
Fix tests and better logs
This commit is contained in:
parent
3efaa8e171
commit
f07aa9f6f0
10 changed files with 567 additions and 469 deletions
|
@ -46,7 +46,7 @@ class DecryptRedactedEventTest : InstrumentedTest {
|
||||||
roomALicePOV.sendService().redactEvent(timelineEvent.root, redactionReason)
|
roomALicePOV.sendService().redactEvent(timelineEvent.root, redactionReason)
|
||||||
|
|
||||||
// get the event from bob
|
// get the event from bob
|
||||||
testHelper.retryPeriodically {
|
testHelper.retryWithBackoff {
|
||||||
bobSession.getRoom(e2eRoomID)?.getTimelineEvent(timelineEvent.eventId)?.root?.isRedacted() == true
|
bobSession.getRoom(e2eRoomID)?.getTimelineEvent(timelineEvent.eventId)?.root?.isRedacted() == true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -101,7 +101,7 @@ class E2eeSanityTests : InstrumentedTest {
|
||||||
fail("${otherSession.myUserId.take(10)} should be able to decrypt")
|
fail("${otherSession.myUserId.take(10)} should be able to decrypt")
|
||||||
}) {
|
}) {
|
||||||
val timeLineEvent = otherSession.getRoom(e2eRoomID)?.getTimelineEvent(sentEventId!!).also {
|
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 != null &&
|
||||||
timeLineEvent.isEncrypted() &&
|
timeLineEvent.isEncrypted() &&
|
||||||
|
|
|
@ -18,6 +18,7 @@ package org.matrix.android.sdk.internal.crypto
|
||||||
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.test.filters.LargeTest
|
import androidx.test.filters.LargeTest
|
||||||
|
import org.amshove.kluent.fail
|
||||||
import org.amshove.kluent.internal.assertEquals
|
import org.amshove.kluent.internal.assertEquals
|
||||||
import org.amshove.kluent.internal.assertNotEquals
|
import org.amshove.kluent.internal.assertNotEquals
|
||||||
import org.junit.Assert
|
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
|
||||||
import org.matrix.android.sdk.common.CommonTestHelper.Companion.runCryptoTest
|
import org.matrix.android.sdk.common.CommonTestHelper.Companion.runCryptoTest
|
||||||
import org.matrix.android.sdk.common.SessionTestParams
|
import org.matrix.android.sdk.common.SessionTestParams
|
||||||
import org.matrix.android.sdk.common.wrapWithTimeout
|
|
||||||
|
|
||||||
@RunWith(JUnit4::class)
|
@RunWith(JUnit4::class)
|
||||||
@FixMethodOrder(MethodSorters.JVM)
|
@FixMethodOrder(MethodSorters.JVM)
|
||||||
|
@ -99,19 +99,25 @@ class E2eeShareKeysHistoryTest : InstrumentedTest {
|
||||||
|
|
||||||
val aliceMessageId: String? = sendMessageInRoom(aliceRoomPOV, aliceMessageText, testHelper)
|
val aliceMessageId: String? = sendMessageInRoom(aliceRoomPOV, aliceMessageText, testHelper)
|
||||||
Assert.assertTrue("Message should be sent", aliceMessageId != null)
|
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
|
// Bob should be able to decrypt the message
|
||||||
testHelper.retryPeriodically {
|
testHelper.retryWithBackoff(
|
||||||
val timelineEvent = bobSession.roomService().getRoom(e2eRoomID)?.timelineService()?.getTimelineEvent(aliceMessageId!!)
|
onFail = {
|
||||||
(timelineEvent != null &&
|
fail("Bob should be able to decrypt $aliceMessageId")
|
||||||
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()}")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
) {
|
||||||
|
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
|
// Create a new user
|
||||||
|
@ -135,23 +141,31 @@ class E2eeShareKeysHistoryTest : InstrumentedTest {
|
||||||
null
|
null
|
||||||
-> {
|
-> {
|
||||||
// Aris should be able to decrypt the message
|
// Aris should be able to decrypt the message
|
||||||
testHelper.retryPeriodically {
|
testHelper.retryWithBackoff(
|
||||||
val timelineEvent = arisSession.roomService().getRoom(e2eRoomID)?.timelineService()?.getTimelineEvent(aliceMessageId!!)
|
onFail = {
|
||||||
(timelineEvent != null &&
|
fail("Aris should be able to decrypt $aliceMessageId")
|
||||||
timelineEvent.isEncrypted() &&
|
}
|
||||||
timelineEvent.root.getClearType() == EventType.MESSAGE &&
|
) {
|
||||||
timelineEvent.root.mxDecryptionResult?.isSafe == false
|
val timelineEvent = arisSession.roomService().getRoom(e2eRoomID)?.timelineService()?.getTimelineEvent(aliceMessageId!!)
|
||||||
).also {
|
(timelineEvent != null &&
|
||||||
if (it) {
|
timelineEvent.isEncrypted() &&
|
||||||
Log.v("#E2E TEST", "Aris can decrypt the message: ${timelineEvent?.root?.getDecryptedTextSummary()}")
|
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.INVITED,
|
||||||
RoomHistoryVisibility.JOINED -> {
|
RoomHistoryVisibility.JOINED -> {
|
||||||
// Aris should not even be able to get the message
|
// 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)
|
val timelineEvent = arisSession.roomService().getRoom(e2eRoomID)
|
||||||
?.timelineService()
|
?.timelineService()
|
||||||
?.getTimelineEvent(aliceMessageId!!)
|
?.getTimelineEvent(aliceMessageId!!)
|
||||||
|
@ -258,11 +272,17 @@ class E2eeShareKeysHistoryTest : InstrumentedTest {
|
||||||
|
|
||||||
// Bob should be able to decrypt the message
|
// Bob should be able to decrypt the message
|
||||||
var firstAliceMessageMegolmSessionId: String? = null
|
var firstAliceMessageMegolmSessionId: String? = null
|
||||||
val bobRoomPov = bobSession.roomService().getRoom(e2eRoomID)
|
val bobRoomPov = bobSession.roomService().getRoom(e2eRoomID)!!
|
||||||
testHelper.retryPeriodically {
|
testHelper.retryWithBackoff(
|
||||||
|
onFail = {
|
||||||
|
fail("Bob should be able to decrypt $aliceMessageId")
|
||||||
|
}
|
||||||
|
) {
|
||||||
val timelineEvent = bobRoomPov
|
val timelineEvent = bobRoomPov
|
||||||
?.timelineService()
|
.timelineService()
|
||||||
?.getTimelineEvent(aliceMessageId!!)
|
.getTimelineEvent(aliceMessageId!!)?.also {
|
||||||
|
Log.v("#E2E TEST ROTATION", "Bob sees ${it.root.getClearType()}")
|
||||||
|
}
|
||||||
(timelineEvent != null &&
|
(timelineEvent != null &&
|
||||||
timelineEvent.isEncrypted() &&
|
timelineEvent.isEncrypted() &&
|
||||||
timelineEvent.root.getClearType() == EventType.MESSAGE).also {
|
timelineEvent.root.getClearType() == EventType.MESSAGE).also {
|
||||||
|
@ -279,11 +299,17 @@ class E2eeShareKeysHistoryTest : InstrumentedTest {
|
||||||
Assert.assertNotNull("megolm session id can't be null", firstAliceMessageMegolmSessionId)
|
Assert.assertNotNull("megolm session id can't be null", firstAliceMessageMegolmSessionId)
|
||||||
|
|
||||||
var secondAliceMessageSessionId: String? = null
|
var secondAliceMessageSessionId: String? = null
|
||||||
sendMessageInRoom(aliceRoomPOV, "Other msg", testHelper)?.let { secondMessage ->
|
sendMessageInRoom(aliceRoomPOV, "Other msg", testHelper)!!.let { secondMessage ->
|
||||||
testHelper.retryPeriodically {
|
testHelper.retryWithBackoff(
|
||||||
|
onFail = {
|
||||||
|
fail("Bob should be able to decrypt the second message $secondMessage")
|
||||||
|
}
|
||||||
|
) {
|
||||||
val timelineEvent = bobRoomPov
|
val timelineEvent = bobRoomPov
|
||||||
?.timelineService()
|
.timelineService()
|
||||||
?.getTimelineEvent(secondMessage)
|
.getTimelineEvent(secondMessage)?.also {
|
||||||
|
Log.v("#E2E TEST ROTATION", "Bob sees ${it.root.getClearType()}")
|
||||||
|
}
|
||||||
(timelineEvent != null &&
|
(timelineEvent != null &&
|
||||||
timelineEvent.isEncrypted() &&
|
timelineEvent.isEncrypted() &&
|
||||||
timelineEvent.root.getClearType() == EventType.MESSAGE).also {
|
timelineEvent.root.getClearType() == EventType.MESSAGE).also {
|
||||||
|
@ -309,29 +335,44 @@ class E2eeShareKeysHistoryTest : InstrumentedTest {
|
||||||
historyVisibilityStr = nextRoomHistoryVisibility.historyVisibilityStr
|
historyVisibilityStr = nextRoomHistoryVisibility.historyVisibilityStr
|
||||||
).toContent()
|
).toContent()
|
||||||
)
|
)
|
||||||
|
Log.v("#E2E TEST ROTATION", "State update sent")
|
||||||
|
|
||||||
// ensure that the state did synced down
|
// ensure that the state did synced down
|
||||||
testHelper.retryPeriodically {
|
testHelper.retryWithBackoff(
|
||||||
aliceRoomPOV.stateService().getStateEvent(EventType.STATE_ROOM_HISTORY_VISIBILITY, QueryStringValue.IsEmpty)?.content
|
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<RoomHistoryVisibilityContent>()?.historyVisibility == nextRoomHistoryVisibility.historyVisibility
|
?.toModel<RoomHistoryVisibilityContent>()?.historyVisibility == nextRoomHistoryVisibility.historyVisibility
|
||||||
}
|
}
|
||||||
|
|
||||||
testHelper.retryPeriodically {
|
// testHelper.retryPeriodically {
|
||||||
val roomVisibility = aliceSession.getRoom(e2eRoomID)!!
|
// val roomVisibility = aliceSession.getRoom(e2eRoomID)!!
|
||||||
.stateService()
|
// .stateService()
|
||||||
.getStateEvent(EventType.STATE_ROOM_HISTORY_VISIBILITY, QueryStringValue.IsEmpty)
|
// .getStateEvent(EventType.STATE_ROOM_HISTORY_VISIBILITY, QueryStringValue.IsEmpty)
|
||||||
?.content
|
// ?.content
|
||||||
?.toModel<RoomHistoryVisibilityContent>()
|
// ?.toModel<RoomHistoryVisibilityContent>()
|
||||||
Log.v("#E2E TEST ROTATION", "Room visibility changed from: ${initRoomHistoryVisibility.name} to: ${roomVisibility?.historyVisibility?.name}")
|
// Log.v("#E2E TEST ROTATION", "Room visibility changed from: ${initRoomHistoryVisibility.name} to: ${roomVisibility?.historyVisibility?.name}")
|
||||||
roomVisibility?.historyVisibility == nextRoomHistoryVisibility.historyVisibility
|
// roomVisibility?.historyVisibility == nextRoomHistoryVisibility.historyVisibility
|
||||||
}
|
// }
|
||||||
|
|
||||||
var aliceThirdMessageSessionId: String? = null
|
var aliceThirdMessageSessionId: String? = null
|
||||||
sendMessageInRoom(aliceRoomPOV, "Message after visibility change", testHelper)?.let { thirdMessage ->
|
sendMessageInRoom(aliceRoomPOV, "Message after visibility change", testHelper)!!.let { thirdMessage ->
|
||||||
testHelper.retryPeriodically {
|
testHelper.retryWithBackoff(
|
||||||
|
onFail = {
|
||||||
|
fail("Bob should be able to decrypt $thirdMessage")
|
||||||
|
}
|
||||||
|
) {
|
||||||
val timelineEvent = bobRoomPov
|
val timelineEvent = bobRoomPov
|
||||||
?.timelineService()
|
.timelineService()
|
||||||
?.getTimelineEvent(thirdMessage)
|
.getTimelineEvent(thirdMessage)?.also {
|
||||||
|
Log.v("#E2E TEST ROTATION", "Bob sees ${it.root.getClearType()}")
|
||||||
|
}
|
||||||
(timelineEvent != null &&
|
(timelineEvent != null &&
|
||||||
timelineEvent.isEncrypted() &&
|
timelineEvent.isEncrypted() &&
|
||||||
timelineEvent.root.getClearType() == EventType.MESSAGE).also {
|
timelineEvent.root.getClearType() == EventType.MESSAGE).also {
|
||||||
|
@ -364,7 +405,7 @@ class E2eeShareKeysHistoryTest : InstrumentedTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun ensureMembersHaveJoined(aliceSession: Session, otherAccounts: List<Session>, e2eRoomID: String, testHelper: CommonTestHelper) {
|
private suspend fun ensureMembersHaveJoined(aliceSession: Session, otherAccounts: List<Session>, e2eRoomID: String, testHelper: CommonTestHelper) {
|
||||||
testHelper.retryPeriodically {
|
testHelper.retryWithBackoff {
|
||||||
otherAccounts.map {
|
otherAccounts.map {
|
||||||
aliceSession.roomService().getRoomMember(it.myUserId, e2eRoomID)?.membership
|
aliceSession.roomService().getRoomMember(it.myUserId, e2eRoomID)?.membership
|
||||||
}.all {
|
}.all {
|
||||||
|
@ -374,7 +415,7 @@ class E2eeShareKeysHistoryTest : InstrumentedTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun waitForAndAcceptInviteInRoom(otherSession: Session, e2eRoomID: String, testHelper: CommonTestHelper) {
|
private suspend fun waitForAndAcceptInviteInRoom(otherSession: Session, e2eRoomID: String, testHelper: CommonTestHelper) {
|
||||||
testHelper.retryPeriodically {
|
testHelper.retryWithBackoff {
|
||||||
val roomSummary = otherSession.roomService().getRoomSummary(e2eRoomID)
|
val roomSummary = otherSession.roomService().getRoomSummary(e2eRoomID)
|
||||||
(roomSummary != null && roomSummary.membership == Membership.INVITE).also {
|
(roomSummary != null && roomSummary.membership == Membership.INVITE).also {
|
||||||
if (it) {
|
if (it) {
|
||||||
|
@ -383,17 +424,15 @@ class E2eeShareKeysHistoryTest : InstrumentedTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
wrapWithTimeout(60_000) {
|
|
||||||
Log.v("#E2E TEST", "${otherSession.myUserId} tries to join room $e2eRoomID")
|
Log.v("#E2E TEST", "${otherSession.myUserId} tries to join room $e2eRoomID")
|
||||||
try {
|
try {
|
||||||
otherSession.roomService().joinRoom(e2eRoomID)
|
otherSession.roomService().joinRoom(e2eRoomID)
|
||||||
} catch (ex: JoinRoomFailure.JoinedWithTimeout) {
|
} catch (ex: JoinRoomFailure.JoinedWithTimeout) {
|
||||||
// it's ok we will wait after
|
// it's ok we will wait after
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Log.v("#E2E TEST", "${otherSession.myUserId} waiting for join echo ...")
|
Log.v("#E2E TEST", "${otherSession.myUserId} waiting for join echo ...")
|
||||||
testHelper.retryPeriodically {
|
testHelper.retryWithBackoff {
|
||||||
val roomSummary = otherSession.roomService().getRoomSummary(e2eRoomID)
|
val roomSummary = otherSession.roomService().getRoomSummary(e2eRoomID)
|
||||||
roomSummary != null && roomSummary.membership == Membership.JOIN
|
roomSummary != null && roomSummary.membership == Membership.JOIN
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
package org.matrix.android.sdk.internal.crypto.verification
|
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.Session
|
||||||
import org.matrix.android.sdk.api.session.crypto.verification.EVerificationState
|
import org.matrix.android.sdk.api.session.crypto.verification.EVerificationState
|
||||||
import org.matrix.android.sdk.api.session.crypto.verification.VerificationMethod
|
import org.matrix.android.sdk.api.session.crypto.verification.VerificationMethod
|
||||||
|
@ -37,7 +38,11 @@ class SasVerificationTestHelper(private val testHelper: CommonTestHelper) {
|
||||||
)
|
)
|
||||||
.transactionId
|
.transactionId
|
||||||
|
|
||||||
testHelper.retryPeriodically {
|
testHelper.retryWithBackoff(
|
||||||
|
onFail = {
|
||||||
|
fail("bob should see an incoming verification request with id $transactionId")
|
||||||
|
}
|
||||||
|
) {
|
||||||
val incomingRequest = bobVerificationService.getExistingVerificationRequest(aliceSession.myUserId, transactionId)
|
val incomingRequest = bobVerificationService.getExistingVerificationRequest(aliceSession.myUserId, transactionId)
|
||||||
if (incomingRequest != null) {
|
if (incomingRequest != null) {
|
||||||
bobVerificationService.readyPendingVerification(
|
bobVerificationService.readyPendingVerification(
|
||||||
|
@ -52,7 +57,11 @@ class SasVerificationTestHelper(private val testHelper: CommonTestHelper) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// wait for alice to see the ready
|
// 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)
|
val pendingRequest = aliceVerificationService.getExistingVerificationRequest(bobUserId, transactionId)
|
||||||
pendingRequest?.state == EVerificationState.Ready
|
pendingRequest?.state == EVerificationState.Ready
|
||||||
}
|
}
|
||||||
|
@ -67,7 +76,7 @@ class SasVerificationTestHelper(private val testHelper: CommonTestHelper) {
|
||||||
val requestID = session1VerificationService.requestSelfKeyVerification(supportedMethods).transactionId
|
val requestID = session1VerificationService.requestSelfKeyVerification(supportedMethods).transactionId
|
||||||
|
|
||||||
val myUserId = session1.myUserId
|
val myUserId = session1.myUserId
|
||||||
testHelper.retryPeriodically {
|
testHelper.retryWithBackoff {
|
||||||
val incomingRequest = session2VerificationService.getExistingVerificationRequest(myUserId, requestID)
|
val incomingRequest = session2VerificationService.getExistingVerificationRequest(myUserId, requestID)
|
||||||
if (incomingRequest != null) {
|
if (incomingRequest != null) {
|
||||||
session2VerificationService.readyPendingVerification(
|
session2VerificationService.readyPendingVerification(
|
||||||
|
|
|
@ -17,305 +17,323 @@
|
||||||
package org.matrix.android.sdk.internal.crypto.verification
|
package org.matrix.android.sdk.internal.crypto.verification
|
||||||
|
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
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.FixMethodOrder
|
||||||
import org.junit.Ignore
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
import org.junit.runners.MethodSorters
|
import org.junit.runners.MethodSorters
|
||||||
import org.matrix.android.sdk.InstrumentedTest
|
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)
|
@RunWith(AndroidJUnit4::class)
|
||||||
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
|
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
|
||||||
@Ignore
|
|
||||||
class SASTest : InstrumentedTest {
|
class SASTest : InstrumentedTest {
|
||||||
|
|
||||||
/*
|
val scope = CoroutineScope(SupervisorJob())
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun test_aliceStartThenAliceCancel() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
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
|
Timber.v("verification: doE2ETestWithAliceAndBobInARoom")
|
||||||
@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 cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
|
||||||
|
Timber.v("verification: initializeCrossSigning")
|
||||||
val bobSession = cryptoTestData.secondSession!!
|
cryptoTestData.initializeCrossSigning(cryptoTestHelper)
|
||||||
|
|
||||||
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<KeyVerificationCancel>()!!
|
|
||||||
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<KeyVerificationCancel>()!!
|
|
||||||
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<String> = SasVerificationTransaction.KNOWN_AGREEMENT_PROTOCOLS,
|
|
||||||
hashes: List<String> = SasVerificationTransaction.KNOWN_HASHES,
|
|
||||||
mac: List<String> = SasVerificationTransaction.KNOWN_MACS,
|
|
||||||
codes: List<String> = 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<Any>()
|
|
||||||
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<Void>(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 aliceSession = cryptoTestData.firstSession
|
||||||
val bobSession = cryptoTestData.secondSession
|
val bobSession = cryptoTestData.secondSession
|
||||||
|
|
||||||
val aliceVerificationService = aliceSession.cryptoService().verificationService()
|
val aliceVerificationService = aliceSession.cryptoService().verificationService()
|
||||||
|
val bobVerificationService = bobSession!!.cryptoService().verificationService()
|
||||||
|
|
||||||
val aliceCreatedLatch = CountDownLatch(2)
|
Timber.v("verification: requestVerificationAndWaitForReadyState")
|
||||||
val aliceCancelledLatch = CountDownLatch(1)
|
val txId = SasVerificationTestHelper(testHelper)
|
||||||
val createdTx = mutableListOf<VerificationTransaction>()
|
.requestVerificationAndWaitForReadyState(cryptoTestData, listOf(VerificationMethod.SAS))
|
||||||
val aliceListener = object : VerificationService.Listener {
|
|
||||||
override fun transactionCreated(tx: VerificationTransaction) {
|
Timber.v("verification: startKeyVerification")
|
||||||
createdTx.add(tx)
|
aliceVerificationService.startKeyVerification(
|
||||||
aliceCreatedLatch.countDown()
|
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<Unit>()
|
||||||
|
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) {
|
val bobCancelled = CompletableDeferred<Unit>()
|
||||||
tx as SasVerificationTransaction
|
bobVerificationService.requestEventFlow().onEach {
|
||||||
if (tx.state() is SasTransactionState.Cancelled && !(tx.state() as SasTransactionState.Cancelled).byMe) {
|
println("alice flow event $it")
|
||||||
aliceCancelledLatch.countDown()
|
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<KeyVerificationCancel>()!!
|
||||||
|
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<KeyVerificationCancel>()!!
|
||||||
|
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<String> = SasVerificationTransaction.KNOWN_AGREEMENT_PROTOCOLS,
|
||||||
|
hashes: List<String> = SasVerificationTransaction.KNOWN_HASHES,
|
||||||
|
mac: List<String> = SasVerificationTransaction.KNOWN_MACS,
|
||||||
|
codes: List<String> = 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<Any>()
|
||||||
|
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<Void>(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<VerificationTransaction>()
|
||||||
|
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)
|
// aliceVerificationService.addListener(aliceListener)
|
||||||
|
|
||||||
val bobUserId = bobSession!!.myUserId
|
val bobUserId = bobSession!!.myUserId
|
||||||
val bobDeviceId = bobSession.cryptoService().getMyCryptoDevice().deviceId
|
val bobDeviceId = bobSession.cryptoService().getMyCryptoDevice().deviceId
|
||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
// aliceSession.cryptoService().downloadKeysIfNeeded(listOf(bobUserId), forceDownload = true)
|
// aliceSession.cryptoService().downloadKeysIfNeeded(listOf(bobUserId), forceDownload = true)
|
||||||
// aliceVerificationService.beginKeyVerification(listOf(VerificationMethod.SAS), bobUserId, bobDeviceId)
|
// aliceVerificationService.beginKeyVerification(listOf(VerificationMethod.SAS), bobUserId, bobDeviceId)
|
||||||
// aliceVerificationService.beginKeyVerification(bobUserId, bobDeviceId)
|
// aliceVerificationService.beginKeyVerification(bobUserId, bobDeviceId)
|
||||||
// testHelper.await(aliceCreatedLatch)
|
// testHelper.await(aliceCreatedLatch)
|
||||||
// testHelper.await(aliceCancelledLatch)
|
// testHelper.await(aliceCancelledLatch)
|
||||||
|
|
||||||
cryptoTestData.cleanUp(testHelper)
|
cryptoTestData.cleanUp(testHelper)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test that when alice starts a 'correct' request, bob agrees.
|
* Test that when alice starts a 'correct' request, bob agrees.
|
||||||
*/
|
*/
|
||||||
// @Test
|
// @Test
|
||||||
|
@ -416,65 +434,65 @@ class SASTest : InstrumentedTest {
|
||||||
// )
|
// )
|
||||||
// }
|
// }
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun test_happyPath() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
fun test_happyPath() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
|
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
|
||||||
cryptoTestData.initializeCrossSigning(cryptoTestHelper)
|
cryptoTestData.initializeCrossSigning(cryptoTestHelper)
|
||||||
val sasVerificationTestHelper = SasVerificationTestHelper(testHelper, cryptoTestHelper)
|
val sasVerificationTestHelper = SasVerificationTestHelper(testHelper, cryptoTestHelper)
|
||||||
val transactionId = sasVerificationTestHelper.requestVerificationAndWaitForReadyState(cryptoTestData, listOf(VerificationMethod.SAS))
|
val transactionId = sasVerificationTestHelper.requestVerificationAndWaitForReadyState(cryptoTestData, listOf(VerificationMethod.SAS))
|
||||||
val aliceSession = cryptoTestData.firstSession
|
val aliceSession = cryptoTestData.firstSession
|
||||||
val bobSession = cryptoTestData.secondSession
|
val bobSession = cryptoTestData.secondSession
|
||||||
|
|
||||||
val aliceVerificationService = aliceSession.cryptoService().verificationService()
|
val aliceVerificationService = aliceSession.cryptoService().verificationService()
|
||||||
val bobVerificationService = bobSession!!.cryptoService().verificationService()
|
val bobVerificationService = bobSession!!.cryptoService().verificationService()
|
||||||
|
|
||||||
val verifiedLatch = CountDownLatch(2)
|
val verifiedLatch = CountDownLatch(2)
|
||||||
val aliceListener = object : VerificationService.Listener {
|
val aliceListener = object : VerificationService.Listener {
|
||||||
|
|
||||||
override fun verificationRequestUpdated(pr: PendingVerificationRequest) {
|
override fun verificationRequestUpdated(pr: PendingVerificationRequest) {
|
||||||
Timber.v("RequestUpdated pr=$pr")
|
Timber.v("RequestUpdated pr=$pr")
|
||||||
}
|
}
|
||||||
|
|
||||||
var matched = false
|
var matched = false
|
||||||
var verified = false
|
var verified = false
|
||||||
override fun transactionUpdated(tx: VerificationTransaction) {
|
override fun transactionUpdated(tx: VerificationTransaction) {
|
||||||
if (tx !is SasVerificationTransaction) return
|
if (tx !is SasVerificationTransaction) return
|
||||||
Timber.v("Alice transactionUpdated: ${tx.state()} on thread:${Thread.currentThread()}")
|
Timber.v("Alice transactionUpdated: ${tx.state()} on thread:${Thread.currentThread()}")
|
||||||
when (tx.state()) {
|
when (tx.state()) {
|
||||||
SasTransactionState.SasShortCodeReady -> {
|
SasTransactionState.SasShortCodeReady -> {
|
||||||
if (!matched) {
|
if (!matched) {
|
||||||
matched = true
|
matched = true
|
||||||
runBlocking {
|
runBlocking {
|
||||||
delay(500)
|
delay(500)
|
||||||
tx.userHasVerifiedShortCode()
|
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)
|
// aliceVerificationService.addListener(aliceListener)
|
||||||
|
|
||||||
val bobListener = object : VerificationService.Listener {
|
val bobListener = object : VerificationService.Listener {
|
||||||
var accepted = false
|
var accepted = false
|
||||||
var matched = false
|
var matched = false
|
||||||
var verified = false
|
var verified = false
|
||||||
|
|
||||||
override fun verificationRequestUpdated(pr: PendingVerificationRequest) {
|
override fun verificationRequestUpdated(pr: PendingVerificationRequest) {
|
||||||
Timber.v("RequestUpdated: pr=$pr")
|
Timber.v("RequestUpdated: pr=$pr")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun transactionUpdated(tx: VerificationTransaction) {
|
override fun transactionUpdated(tx: VerificationTransaction) {
|
||||||
if (tx !is SasVerificationTransaction) return
|
if (tx !is SasVerificationTransaction) return
|
||||||
Timber.v("Bob transactionUpdated: ${tx.state()} on thread: ${Thread.currentThread()}")
|
Timber.v("Bob transactionUpdated: ${tx.state()} on thread: ${Thread.currentThread()}")
|
||||||
when (tx.state()) {
|
when (tx.state()) {
|
||||||
// VerificationTxState.SasStarted -> {
|
// VerificationTxState.SasStarted -> {
|
||||||
// if (!accepted) {
|
// if (!accepted) {
|
||||||
// accepted = true
|
// accepted = true
|
||||||
|
@ -483,113 +501,113 @@ class SASTest : InstrumentedTest {
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
SasTransactionState.SasShortCodeReady -> {
|
SasTransactionState.SasShortCodeReady -> {
|
||||||
if (!matched) {
|
if (!matched) {
|
||||||
matched = true
|
matched = true
|
||||||
runBlocking {
|
runBlocking {
|
||||||
delay(500)
|
delay(500)
|
||||||
tx.userHasVerifiedShortCode()
|
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)
|
// bobVerificationService.addListener(bobListener)
|
||||||
|
|
||||||
val bobUserId = bobSession.myUserId
|
val bobUserId = bobSession.myUserId
|
||||||
val bobDeviceId = runBlocking {
|
val bobDeviceId = runBlocking {
|
||||||
bobSession.cryptoService().getMyCryptoDevice().deviceId
|
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)
|
|
||||||
}
|
}
|
||||||
|
aliceVerificationService.startKeyVerification(VerificationMethod.SAS, bobUserId, transactionId)
|
||||||
|
|
||||||
@Test
|
Timber.v("Await after beginKey ${Thread.currentThread()}")
|
||||||
fun test_ConcurrentStart() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
testHelper.await(verifiedLatch)
|
||||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
|
|
||||||
cryptoTestData.initializeCrossSigning(cryptoTestHelper)
|
|
||||||
val aliceSession = cryptoTestData.firstSession
|
|
||||||
val bobSession = cryptoTestData.secondSession!!
|
|
||||||
|
|
||||||
val aliceVerificationService = aliceSession.cryptoService().verificationService()
|
// Assert that devices are verified
|
||||||
val bobVerificationService = bobSession.cryptoService().verificationService()
|
val bobDeviceInfoFromAlicePOV: CryptoDeviceInfo? = aliceSession.cryptoService().getCryptoDeviceInfo(bobUserId, bobDeviceId)
|
||||||
|
val aliceDeviceInfoFromBobPOV: CryptoDeviceInfo? =
|
||||||
|
bobSession.cryptoService().getCryptoDeviceInfo(aliceSession.myUserId, aliceSession.cryptoService().getMyCryptoDevice().deviceId)
|
||||||
|
|
||||||
val req = aliceVerificationService.requestKeyVerificationInDMs(
|
assertTrue("alice device should be verified from bob point of view", aliceDeviceInfoFromBobPOV!!.isVerified)
|
||||||
listOf(VerificationMethod.SAS, VerificationMethod.QR_CODE_SCAN, VerificationMethod.QR_CODE_SHOW),
|
assertTrue("bob device should be verified from alice point of view", bobDeviceInfoFromAlicePOV!!.isVerified)
|
||||||
bobSession.myUserId,
|
}
|
||||||
cryptoTestData.roomId
|
|
||||||
)
|
|
||||||
|
|
||||||
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 req = aliceVerificationService.requestKeyVerificationInDMs(
|
||||||
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),
|
listOf(VerificationMethod.SAS, VerificationMethod.QR_CODE_SCAN, VerificationMethod.QR_CODE_SHOW),
|
||||||
aliceSession.myUserId,
|
bobSession.myUserId,
|
||||||
requestID
|
cryptoTestData.roomId
|
||||||
)
|
)
|
||||||
|
|
||||||
// wait for alice to get the ready
|
val requestID = req.transactionId
|
||||||
testHelper.retryPeriodically {
|
|
||||||
val prAlicePOV = aliceVerificationService.getExistingVerificationRequests(bobSession.myUserId).firstOrNull()
|
|
||||||
Log.v("TEST", "== prAlicePOV is $prAlicePOV")
|
|
||||||
prAlicePOV?.transactionId == requestID && prAlicePOV.state == EVerificationState.Ready
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start concurrent!
|
Log.v("TEST", "== requestID is $requestID")
|
||||||
aliceVerificationService.startKeyVerification(
|
|
||||||
method = VerificationMethod.SAS,
|
|
||||||
otherUserId = bobSession.myUserId,
|
|
||||||
requestId = requestID,
|
|
||||||
)
|
|
||||||
|
|
||||||
bobVerificationService.startKeyVerification(
|
testHelper.retryPeriodically {
|
||||||
method = VerificationMethod.SAS,
|
val prBobPOV = bobVerificationService.getExistingVerificationRequests(aliceSession.myUserId).firstOrNull()
|
||||||
otherUserId = aliceSession.myUserId,
|
Log.v("TEST", "== prBobPOV is $prBobPOV")
|
||||||
requestId = requestID,
|
prBobPOV?.transactionId == 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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
|
@ -263,6 +263,8 @@ internal class VerificationActor @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
is VerificationIntent.GetExistingRequestsForUser -> {
|
is VerificationIntent.GetExistingRequestsForUser -> {
|
||||||
verificationRequestsStore.getExistingRequestsForUser(msg.userId).let { requests ->
|
verificationRequestsStore.getExistingRequestsForUser(msg.userId).let { requests ->
|
||||||
|
Timber.tag(loggerTag.value)
|
||||||
|
.v("[${myUserId.take(8)}]: Found $requests")
|
||||||
msg.deferred.complete(requests.map { it.toPendingVerificationRequest() })
|
msg.deferred.complete(requests.map { it.toPendingVerificationRequest() })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -306,6 +308,8 @@ internal class VerificationActor @AssistedInject constructor(
|
||||||
private fun dispatchUpdate(update: VerificationEvent) {
|
private fun dispatchUpdate(update: VerificationEvent) {
|
||||||
// We don't want to block on emit.
|
// We don't want to block on emit.
|
||||||
// If no subscriber there is a small buffer
|
// If no subscriber there is a small buffer
|
||||||
|
Timber.tag(loggerTag.value)
|
||||||
|
.v("[${myUserId.take(8)}] Dispatch Request update ${update.transactionId}")
|
||||||
scope.launch {
|
scope.launch {
|
||||||
eventFlow.emit(update)
|
eventFlow.emit(update)
|
||||||
}
|
}
|
||||||
|
@ -565,21 +569,29 @@ internal class VerificationActor @AssistedInject constructor(
|
||||||
private suspend fun handleSasStart(msg: VerificationIntent.ActionStartSasVerification) {
|
private suspend fun handleSasStart(msg: VerificationIntent.ActionStartSasVerification) {
|
||||||
val matchingRequest = verificationRequestsStore.getExistingRequestWithRequestId(msg.requestId)
|
val matchingRequest = verificationRequestsStore.getExistingRequestWithRequestId(msg.requestId)
|
||||||
?: return Unit.also {
|
?: 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"))
|
msg.deferred.completeExceptionally(java.lang.IllegalArgumentException("Unknown request"))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (matchingRequest.state != EVerificationState.Ready) {
|
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"))
|
msg.deferred.completeExceptionally(java.lang.IllegalStateException("Can't start a non ready request"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
val otherDeviceId = matchingRequest.otherDeviceId() ?: return Unit.also {
|
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"))
|
msg.deferred.completeExceptionally(java.lang.IllegalArgumentException("Failed to find other device Id"))
|
||||||
}
|
}
|
||||||
|
|
||||||
val existingTransaction = getExistingTransaction<VerificationTransaction>(msg.otherUserId, msg.requestId)
|
val existingTransaction = getExistingTransaction<VerificationTransaction>(msg.otherUserId, msg.requestId)
|
||||||
if (existingTransaction is SasVerificationTransaction) {
|
if (existingTransaction is SasVerificationTransaction) {
|
||||||
// there is already an existing transaction??
|
// 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"))
|
msg.deferred.completeExceptionally(IllegalStateException("Already started"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -589,12 +601,17 @@ internal class VerificationActor @AssistedInject constructor(
|
||||||
requestId = msg.requestId
|
requestId = msg.requestId
|
||||||
)
|
)
|
||||||
|
|
||||||
|
Timber.tag(loggerTag.value)
|
||||||
|
.v("[${myUserId.take(8)}]:sending start to other ${msg.requestId} in room ${matchingRequest.roomId}")
|
||||||
transportLayer.sendToOther(
|
transportLayer.sendToOther(
|
||||||
matchingRequest,
|
matchingRequest,
|
||||||
EventType.KEY_VERIFICATION_START,
|
EventType.KEY_VERIFICATION_START,
|
||||||
startMessage,
|
startMessage,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
Timber.tag(loggerTag.value)
|
||||||
|
.v("[${myUserId.take(8)}]: start sent to other ${msg.requestId}")
|
||||||
|
|
||||||
// should check if already one (and cancel it)
|
// should check if already one (and cancel it)
|
||||||
val tx = KotlinSasTransaction(
|
val tx = KotlinSasTransaction(
|
||||||
channel = channel,
|
channel = channel,
|
||||||
|
@ -1262,6 +1279,9 @@ internal class VerificationActor @AssistedInject constructor(
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Timber.tag(loggerTag.value)
|
||||||
|
.v("[${myUserId.take(8)}] Request ${msg.transactionId} code is $qrCodeData")
|
||||||
|
|
||||||
val readyInfo = ValidVerificationInfoReady(
|
val readyInfo = ValidVerificationInfoReady(
|
||||||
msg.transactionId,
|
msg.transactionId,
|
||||||
verificationTrustBackend.getMyDeviceId(),
|
verificationTrustBackend.getMyDeviceId(),
|
||||||
|
@ -1274,9 +1294,14 @@ internal class VerificationActor @AssistedInject constructor(
|
||||||
methods = commonMethods,
|
methods = commonMethods,
|
||||||
fromDevice = verificationTrustBackend.getMyDeviceId()
|
fromDevice = verificationTrustBackend.getMyDeviceId()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
Timber.tag(loggerTag.value)
|
||||||
|
.v("[${myUserId.take(8)}] Request ${msg.transactionId} sending ready")
|
||||||
try {
|
try {
|
||||||
transportLayer.sendToOther(existing, EventType.KEY_VERIFICATION_READY, message)
|
transportLayer.sendToOther(existing, EventType.KEY_VERIFICATION_READY, message)
|
||||||
} catch (failure: Throwable) {
|
} catch (failure: Throwable) {
|
||||||
|
Timber.tag(loggerTag.value)
|
||||||
|
.v("[${myUserId.take(8)}] Request ${msg.transactionId} failed to send ready")
|
||||||
msg.deferred.completeExceptionally(failure)
|
msg.deferred.completeExceptionally(failure)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.crypto.verification.VerificationService
|
||||||
import org.matrix.android.sdk.api.session.events.model.Event
|
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.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.events.model.toModel
|
||||||
import org.matrix.android.sdk.api.session.room.model.message.MessageContent
|
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.MessageType
|
||||||
import org.matrix.android.sdk.api.session.room.model.message.MessageVerificationReadyContent
|
import org.matrix.android.sdk.api.session.room.model.message.MessageVerificationReadyContent
|
||||||
import org.matrix.android.sdk.internal.di.DeviceId
|
import org.matrix.android.sdk.internal.di.DeviceId
|
||||||
|
@ -55,19 +55,19 @@ internal class VerificationMessageProcessor @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun process(roomId: String, event: Event) {
|
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,
|
// 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.
|
// the message should be ignored by the receiver.
|
||||||
|
|
||||||
if (!VerificationService.isValidRequest(event.ageLocalTs, clock.epochMillis())) return Unit.also {
|
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
|
// Relates to is not encrypted
|
||||||
val relatesToEventId = event.content.toModel<MessageRelationContent>()?.relatesTo?.eventId
|
val relatesToEventId = event.getRelationContent()?.eventId
|
||||||
|
|
||||||
if (event.senderId == userId) {
|
if (event.senderId == userId) {
|
||||||
// If it's send from me, we need to keep track of Requests or Start
|
// 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<MessageVerificationRequestContent>()?.let {
|
// event.getClearContent().toModel<MessageVerificationRequestContent>()?.let {
|
||||||
// if (it.fromDevice != deviceId) {
|
// if (it.fromDevice != deviceId) {
|
||||||
// // The verification is requested from another device
|
// // 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) }
|
// event.eventId?.let { txId -> transactionsHandledByOtherDevice.add(txId) }
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
@ -87,7 +87,7 @@ internal class VerificationMessageProcessor @Inject constructor(
|
||||||
// event.getClearContent().toModel<MessageVerificationStartContent>()?.let {
|
// event.getClearContent().toModel<MessageVerificationStartContent>()?.let {
|
||||||
// if (it.fromDevice != deviceId) {
|
// if (it.fromDevice != deviceId) {
|
||||||
// // The verification is started from another device
|
// // 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) }
|
// relatesToEventId?.let { txId -> transactionsHandledByOtherDevice.add(txId) }
|
||||||
// verificationService.onRoomRequestHandledByOtherDevice(event)
|
// verificationService.onRoomRequestHandledByOtherDevice(event)
|
||||||
// }
|
// }
|
||||||
|
@ -98,14 +98,15 @@ internal class VerificationMessageProcessor @Inject constructor(
|
||||||
event.getClearContent().toModel<MessageVerificationReadyContent>()?.let {
|
event.getClearContent().toModel<MessageVerificationReadyContent>()?.let {
|
||||||
if (it.fromDevice != deviceId) {
|
if (it.fromDevice != deviceId) {
|
||||||
// The verification is started from another device
|
// 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) }
|
relatesToEventId?.let { txId -> transactionsHandledByOtherDevice.add(txId) }
|
||||||
verificationService.onRoomReadyFromOneOfMyOtherDevice(event)
|
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()) {
|
// } else if (EventType.KEY_VERIFICATION_CANCEL == event.getClearType() || EventType.KEY_VERIFICATION_DONE == event.getClearType()) {
|
||||||
// relatesToEventId?.let {
|
// relatesToEventId?.let {
|
||||||
// transactionsHandledByOtherDevice.remove(it)
|
// transactionsHandledByOtherDevice.remove(it)
|
||||||
|
@ -114,13 +115,13 @@ internal class VerificationMessageProcessor @Inject constructor(
|
||||||
// } else if (EventType.ENCRYPTED == event.getClearType()) {
|
// } else if (EventType.ENCRYPTED == event.getClearType()) {
|
||||||
// verificationService.onPotentiallyInterestingEventRoomFailToDecrypt(event)
|
// verificationService.onPotentiallyInterestingEventRoomFailToDecrypt(event)
|
||||||
// }
|
// }
|
||||||
|
Timber.v("## SAS Verification[${userId.take(5)}] discard from me msgId: ${event.eventId}")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (relatesToEventId != null && transactionsHandledByOtherDevice.contains(relatesToEventId)) {
|
if (relatesToEventId != null && transactionsHandledByOtherDevice.contains(relatesToEventId)) {
|
||||||
// Ignore this event, it is directed to another of my devices
|
// 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
|
return
|
||||||
}
|
}
|
||||||
when (event.getClearType()) {
|
when (event.getClearType()) {
|
||||||
|
|
|
@ -16,11 +16,15 @@
|
||||||
|
|
||||||
package org.matrix.android.sdk.internal.crypto
|
package org.matrix.android.sdk.internal.crypto
|
||||||
|
|
||||||
|
import org.matrix.android.sdk.BuildConfig
|
||||||
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
internal class SecretShareManager @Inject constructor() {
|
internal class SecretShareManager @Inject constructor() {
|
||||||
|
|
||||||
suspend fun requestSecretTo(deviceId: String, secretName: String) {
|
suspend fun requestSecretTo(deviceId: String, secretName: String) {
|
||||||
// nop in rust?
|
// nop in rust?
|
||||||
|
if (BuildConfig.DEBUG) TODO("requestSecretTo Not implemented in Rust")
|
||||||
|
Timber.e("SecretShareManager Not supported in rust $deviceId, $secretName")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,11 +16,13 @@
|
||||||
|
|
||||||
package org.matrix.android.sdk.internal.crypto.algorithms.megolm
|
package org.matrix.android.sdk.internal.crypto.algorithms.megolm
|
||||||
|
|
||||||
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
// empty in rust
|
// empty in rust
|
||||||
class UnRequestedForwardManager @Inject constructor() {
|
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")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -239,9 +239,9 @@ internal class RequestSender @Inject constructor(
|
||||||
val hashMap = content as? Map<*, *>
|
val hashMap = content as? Map<*, *>
|
||||||
val action = hashMap?.get("action")?.toString()
|
val action = hashMap?.get("action")?.toString()
|
||||||
if (GossipingToDeviceObject.ACTION_SHARE_REQUEST == action) {
|
if (GossipingToDeviceObject.ACTION_SHARE_REQUEST == action) {
|
||||||
val body = hashMap.get("body") as? Map<*, *>
|
val requestBody = hashMap["body"] as? Map<*, *>
|
||||||
val roomId = body?.get("room_id") as? String
|
val roomId = requestBody?.get("room_id") as? String
|
||||||
val sessionId = body?.get("session_id") as? String
|
val sessionId = requestBody?.get("session_id") as? String
|
||||||
if (roomId != null && sessionId != null) {
|
if (roomId != null && sessionId != null) {
|
||||||
rateLimiter.tryFromBackupIfPossible(sessionId, roomId)
|
rateLimiter.tryFromBackupIfPossible(sessionId, roomId)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue