Fix tests and better logs

This commit is contained in:
valere 2022-12-15 18:47:48 +01:00
parent 3efaa8e171
commit f07aa9f6f0
10 changed files with 567 additions and 469 deletions

View file

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

View file

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

View file

@ -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<RoomHistoryVisibilityContent>()?.historyVisibility == nextRoomHistoryVisibility.historyVisibility
}
testHelper.retryPeriodically {
val roomVisibility = aliceSession.getRoom(e2eRoomID)!!
.stateService()
.getStateEvent(EventType.STATE_ROOM_HISTORY_VISIBILITY, QueryStringValue.IsEmpty)
?.content
?.toModel<RoomHistoryVisibilityContent>()
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<RoomHistoryVisibilityContent>()
// 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<Session>, 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
}

View file

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

View file

@ -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<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()
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<VerificationTransaction>()
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<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) {
tx as SasVerificationTransaction
if (tx.state() is SasTransactionState.Cancelled && !(tx.state() as SasTransactionState.Cancelled).byMe) {
aliceCancelledLatch.countDown()
val bobCancelled = CompletableDeferred<Unit>()
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<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)
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
}
}
*/
}

View file

@ -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<VerificationTransaction>(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
}

View file

@ -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<MessageRelationContent>()?.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<MessageVerificationRequestContent>()?.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<MessageVerificationStartContent>()?.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<MessageVerificationReadyContent>()?.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()) {

View file

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

View file

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

View file

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