From 8bd094fa66bffd9de8f7daf7270ea9c36d68918d Mon Sep 17 00:00:00 2001 From: ganfra Date: Mon, 25 Apr 2022 17:55:37 +0200 Subject: [PATCH] Do some cleanup on verification APIs --- .../android/sdk/common/CryptoTestHelper.kt | 23 +- .../crypto/crosssigning/XSigningTest.kt | 82 ++++-- .../crypto/gossiping/KeyShareTests.kt | 37 ++- .../internal/crypto/verification/SASTest.kt | 275 ++++++++++-------- .../{qrcode => }/VerificationTest.kt | 71 ++--- .../verification/qrcode/SharedSecretTest.kt | 46 --- .../IncomingSasVerificationTransaction.kt | 34 --- .../OutgoingSasVerificationTransaction.kt | 32 -- .../verification/VerificationService.kt | 63 ++-- .../internal/crypto/DefaultCryptoService.kt | 2 +- .../verification/RustVerificationService.kt | 88 ++---- .../verification/qrcode/SharedSecret.kt | 29 -- .../app/VerifySessionInteractiveTest.kt | 7 +- .../IncomingVerificationRequestHandler.kt | 5 +- .../VerificationBottomSheetViewModel.kt | 16 +- .../home/room/detail/TimelineViewModel.kt | 8 +- .../features/navigation/DefaultNavigator.kt | 12 +- .../devices/DeviceListBottomSheetViewModel.kt | 5 +- .../settings/devices/DevicesViewModel.kt | 2 +- 19 files changed, 323 insertions(+), 514 deletions(-) rename matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/{qrcode => }/VerificationTest.kt (71%) delete mode 100644 matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/qrcode/SharedSecretTest.kt delete mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/verification/IncomingSasVerificationTransaction.kt delete mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/verification/OutgoingSasVerificationTransaction.kt delete mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/qrcode/SharedSecret.kt diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestHelper.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestHelper.kt index d8546b4d8d..ba0b467122 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestHelper.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestHelper.kt @@ -30,8 +30,7 @@ import org.matrix.android.sdk.api.auth.UserPasswordAuth import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.session.Session -import org.matrix.android.sdk.api.session.crypto.verification.IncomingSasVerificationTransaction -import org.matrix.android.sdk.api.session.crypto.verification.OutgoingSasVerificationTransaction +import org.matrix.android.sdk.api.session.crypto.verification.SasVerificationTransaction import org.matrix.android.sdk.api.session.crypto.verification.VerificationMethod import org.matrix.android.sdk.api.session.crypto.verification.VerificationTxState import org.matrix.android.sdk.api.session.events.model.Event @@ -311,16 +310,16 @@ class CryptoTestHelper(private val testHelper: CommonTestHelper) { } // we should reach SHOW SAS on both - var alicePovTx: OutgoingSasVerificationTransaction? = null - var bobPovTx: IncomingSasVerificationTransaction? = null + var alicePovTx: SasVerificationTransaction? = null + var bobPovTx: SasVerificationTransaction? = null // wait for alice to get the ready testHelper.waitWithLatch { testHelper.retryPeriodicallyWithLatch(it) { - bobPovTx = bobVerificationService.getExistingTransaction(alice.myUserId, requestID) as? IncomingSasVerificationTransaction - Log.v("TEST", "== bobPovTx is ${alicePovTx?.uxState}") + bobPovTx = bobVerificationService.getExistingTransaction(alice.myUserId, requestID) as? SasVerificationTransaction + Log.v("TEST", "== bobPovTx is ${bobPovTx?.state}") if (bobPovTx?.state == VerificationTxState.OnStarted) { - bobPovTx?.performAccept() + bobPovTx?.acceptVerification() true } else { false @@ -330,18 +329,18 @@ class CryptoTestHelper(private val testHelper: CommonTestHelper) { testHelper.waitWithLatch { testHelper.retryPeriodicallyWithLatch(it) { - alicePovTx = aliceVerificationService.getExistingTransaction(bob.myUserId, requestID) as? OutgoingSasVerificationTransaction - Log.v("TEST", "== alicePovTx is ${alicePovTx?.uxState}") + alicePovTx = aliceVerificationService.getExistingTransaction(bob.myUserId, requestID) as? SasVerificationTransaction + Log.v("TEST", "== alicePovTx is ${alicePovTx?.state}") alicePovTx?.state == VerificationTxState.ShortCodeReady } } // wait for alice to get the ready testHelper.waitWithLatch { testHelper.retryPeriodicallyWithLatch(it) { - bobPovTx = bobVerificationService.getExistingTransaction(alice.myUserId, requestID) as? IncomingSasVerificationTransaction - Log.v("TEST", "== bobPovTx is ${alicePovTx?.uxState}") + bobPovTx = bobVerificationService.getExistingTransaction(alice.myUserId, requestID) as? SasVerificationTransaction + Log.v("TEST", "== bobPovTx is ${bobPovTx?.state}") if (bobPovTx?.state == VerificationTxState.OnStarted) { - bobPovTx?.performAccept() + bobPovTx?.acceptVerification() } bobPovTx?.state == VerificationTxState.ShortCodeReady } diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/crosssigning/XSigningTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/crosssigning/XSigningTest.kt index b4c8f1a22a..fc8aa57de0 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/crosssigning/XSigningTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/crosssigning/XSigningTest.kt @@ -18,6 +18,7 @@ package org.matrix.android.sdk.internal.crypto.crosssigning import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.LargeTest +import kotlinx.coroutines.runBlocking import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse import org.junit.Assert.assertNotNull @@ -53,7 +54,7 @@ class XSigningTest : InstrumentedTest { fun test_InitializeAndStoreKeys() { val aliceSession = testHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true)) - testHelper.doSync { + testHelper.runBlockingTest { aliceSession.cryptoService().crossSigningService() .initializeCrossSigning(object : UserInteractiveAuthInterceptor { override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation) { @@ -65,10 +66,12 @@ class XSigningTest : InstrumentedTest { ) ) } - }, it) + }) } - val myCrossSigningKeys = aliceSession.cryptoService().crossSigningService().getMyCrossSigningKeys() + val myCrossSigningKeys = testHelper.runBlockingTest { + aliceSession.cryptoService().crossSigningService().getMyCrossSigningKeys() + } val masterPubKey = myCrossSigningKeys?.masterKey() assertNotNull("Master key should be stored", masterPubKey?.unpaddedBase64PublicKey) val selfSigningKey = myCrossSigningKeys?.selfSigningKey() @@ -78,7 +81,10 @@ class XSigningTest : InstrumentedTest { assertTrue("Signing Keys should be trusted", myCrossSigningKeys?.isTrusted() == true) - assertTrue("Signing Keys should be trusted", aliceSession.cryptoService().crossSigningService().checkUserTrust(aliceSession.myUserId).isVerified()) + val userTrustResult = testHelper.runBlockingTest { + aliceSession.cryptoService().crossSigningService().checkUserTrust(aliceSession.myUserId) + } + assertTrue("Signing Keys should be trusted", userTrustResult.isVerified()) testHelper.signOutAndClose(aliceSession) } @@ -99,29 +105,37 @@ class XSigningTest : InstrumentedTest { password = TestConstants.PASSWORD ) - testHelper.doSync { + testHelper.runBlockingTest { aliceSession.cryptoService().crossSigningService().initializeCrossSigning(object : UserInteractiveAuthInterceptor { override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation) { promise.resume(aliceAuthParams) } - }, it) + }) + } + testHelper.runBlockingTest { + bobSession.cryptoService().crossSigningService().initializeCrossSigning(object : UserInteractiveAuthInterceptor { + override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation) { + promise.resume(bobAuthParams) + } + }) } - testHelper.doSync { bobSession.cryptoService().crossSigningService().initializeCrossSigning(object : UserInteractiveAuthInterceptor { - override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation) { - promise.resume(bobAuthParams) - } - }, it) } // Check that alice can see bob keys testHelper.runBlockingTest { aliceSession.cryptoService().downloadKeys(listOf(bobSession.myUserId), true) } - val bobKeysFromAlicePOV = aliceSession.cryptoService().crossSigningService().getUserCrossSigningKeys(bobSession.myUserId) + val bobKeysFromAlicePOV = testHelper.runBlockingTest { + aliceSession.cryptoService().crossSigningService().getUserCrossSigningKeys(bobSession.myUserId) + } assertNotNull("Alice can see bob Master key", bobKeysFromAlicePOV!!.masterKey()) assertNull("Alice should not see bob User key", bobKeysFromAlicePOV.userKey()) assertNotNull("Alice can see bob SelfSigned key", bobKeysFromAlicePOV.selfSigningKey()) - assertEquals("Bob keys from alice pov should match", bobKeysFromAlicePOV.masterKey()?.unpaddedBase64PublicKey, bobSession.cryptoService().crossSigningService().getMyCrossSigningKeys()?.masterKey()?.unpaddedBase64PublicKey) - assertEquals("Bob keys from alice pov should match", bobKeysFromAlicePOV.selfSigningKey()?.unpaddedBase64PublicKey, bobSession.cryptoService().crossSigningService().getMyCrossSigningKeys()?.selfSigningKey()?.unpaddedBase64PublicKey) + val myKeys = testHelper.runBlockingTest { + bobSession.cryptoService().crossSigningService().getMyCrossSigningKeys() + } + + assertEquals("Bob keys from alice pov should match", bobKeysFromAlicePOV.masterKey()?.unpaddedBase64PublicKey, myKeys?.masterKey()?.unpaddedBase64PublicKey) + assertEquals("Bob keys from alice pov should match", bobKeysFromAlicePOV.selfSigningKey()?.unpaddedBase64PublicKey, myKeys?.selfSigningKey()?.unpaddedBase64PublicKey) assertFalse("Bob keys from alice pov should not be trusted", bobKeysFromAlicePOV.isTrusted()) @@ -145,25 +159,33 @@ class XSigningTest : InstrumentedTest { password = TestConstants.PASSWORD ) - testHelper.doSync { aliceSession.cryptoService().crossSigningService().initializeCrossSigning(object : UserInteractiveAuthInterceptor { - override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation) { - promise.resume(aliceAuthParams) - } - }, it) } - testHelper.doSync { bobSession.cryptoService().crossSigningService().initializeCrossSigning(object : UserInteractiveAuthInterceptor { - override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation) { - promise.resume(bobAuthParams) - } - }, it) } + testHelper.runBlockingTest { + aliceSession.cryptoService().crossSigningService().initializeCrossSigning(object : UserInteractiveAuthInterceptor { + override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation) { + promise.resume(aliceAuthParams) + } + }) + } + testHelper.runBlockingTest { + bobSession.cryptoService().crossSigningService().initializeCrossSigning(object : UserInteractiveAuthInterceptor { + override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation) { + promise.resume(bobAuthParams) + } + }) + } // Check that alice can see bob keys val bobUserId = bobSession.myUserId testHelper.runBlockingTest { aliceSession.cryptoService().downloadKeys(listOf(bobUserId), true) } - val bobKeysFromAlicePOV = aliceSession.cryptoService().crossSigningService().getUserCrossSigningKeys(bobUserId) + val bobKeysFromAlicePOV = testHelper.runBlockingTest { + aliceSession.cryptoService().crossSigningService().getUserCrossSigningKeys(bobUserId) + } assertTrue("Bob keys from alice pov should not be trusted", bobKeysFromAlicePOV?.isTrusted() == false) - testHelper.doSync { aliceSession.cryptoService().crossSigningService().trustUser(bobUserId, it) } + testHelper.runBlockingTest { + aliceSession.cryptoService().crossSigningService().trustUser(bobUserId) + } // Now bobs logs in on a new device and verifies it // We will want to test that in alice POV, this new device would be trusted by cross signing @@ -180,7 +202,9 @@ class XSigningTest : InstrumentedTest { fail("Bob should see the new device") } - val bobSecondDevicePOVFirstDevice = bobSession.cryptoService().getDeviceInfo(bobUserId, bobSecondDeviceId) + val bobSecondDevicePOVFirstDevice = runBlocking { + bobSession.cryptoService().getDeviceInfo(bobUserId, bobSecondDeviceId) + } assertNotNull("Bob Second device should be known and persisted from first", bobSecondDevicePOVFirstDevice) // Manually mark it as trusted from first session @@ -198,7 +222,9 @@ class XSigningTest : InstrumentedTest { fail("Alice should see the new device") } - val result = aliceSession.cryptoService().crossSigningService().checkDeviceTrust(bobUserId, bobSecondDeviceId, null) + val result = testHelper.runBlockingTest { + aliceSession.cryptoService().crossSigningService().checkDeviceTrust(bobUserId, bobSecondDeviceId, null) + } assertTrue("Bob second device should be trusted from alice POV", result.isCrossSignedVerified()) testHelper.signOutAndClose(aliceSession) diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/gossiping/KeyShareTests.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/gossiping/KeyShareTests.kt index 8ec650a122..613a54a6b9 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/gossiping/KeyShareTests.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/gossiping/KeyShareTests.kt @@ -24,7 +24,6 @@ import junit.framework.TestCase.assertNotNull import junit.framework.TestCase.assertTrue import junit.framework.TestCase.fail import kotlinx.coroutines.delay -import kotlinx.coroutines.runBlocking import org.junit.Assert import org.junit.FixMethodOrder import org.junit.Ignore @@ -37,7 +36,6 @@ import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor import org.matrix.android.sdk.api.auth.UserPasswordAuth import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse import org.matrix.android.sdk.api.extensions.tryOrNull -import org.matrix.android.sdk.api.session.crypto.verification.IncomingSasVerificationTransaction import org.matrix.android.sdk.api.session.crypto.verification.SasVerificationTransaction import org.matrix.android.sdk.api.session.crypto.verification.VerificationMethod import org.matrix.android.sdk.api.session.crypto.verification.VerificationService @@ -250,32 +248,31 @@ class KeyShareTests : InstrumentedTest { aliceVerificationService1.addListener(object : VerificationService.Listener { override fun transactionUpdated(tx: VerificationTransaction) { + if (tx !is SasVerificationTransaction) return Log.d("#TEST", "AA: tx incoming?:${tx.isIncoming} state ${tx.state}") - if (tx is SasVerificationTransaction) { - if (tx.state == VerificationTxState.OnStarted) { - (tx as IncomingSasVerificationTransaction).performAccept() + when (tx.state) { + VerificationTxState.OnStarted -> commonTestHelper.runBlockingTest { + tx.acceptVerification() } - if (tx.state == VerificationTxState.ShortCodeReady) { + VerificationTxState.ShortCodeReady -> commonTestHelper.runBlockingTest { session1ShortCode = tx.getDecimalCodeRepresentation() - commonTestHelper.runBlockingTest { - delay(500) - tx.userHasVerifiedShortCode() - } + delay(500) + tx.userHasVerifiedShortCode() } } } - }) + } + ) aliceVerificationService2.addListener(object : VerificationService.Listener { override fun transactionUpdated(tx: VerificationTransaction) { + if (tx !is SasVerificationTransaction) return Log.d("#TEST", "BB: tx incoming?:${tx.isIncoming} state ${tx.state}") - if (tx is SasVerificationTransaction) { - if (tx.state == VerificationTxState.ShortCodeReady) { + when (tx.state) { + VerificationTxState.ShortCodeReady -> commonTestHelper.runBlockingTest { session2ShortCode = tx.getDecimalCodeRepresentation() - commonTestHelper.runBlockingTest { - delay(500) - tx.userHasVerifiedShortCode() - } + delay(500) + tx.userHasVerifiedShortCode() } } } @@ -301,7 +298,8 @@ class KeyShareTests : InstrumentedTest { // SSK and USK private keys should have been shared - commonTestHelper.waitWithLatch(60_000) { latch -> + commonTestHelper.waitWithLatch(60_000) + { latch -> commonTestHelper.retryPeriodicallyWithLatch(latch) { Log.d("#TEST", "CAN XS :${aliceSession2.cryptoService().crossSigningService().getMyCrossSigningKeys()}") aliceSession2.cryptoService().crossSigningService().canCrossSign() @@ -309,7 +307,8 @@ class KeyShareTests : InstrumentedTest { } // Test that key backup key has been shared to - commonTestHelper.waitWithLatch(60_000) { latch -> + commonTestHelper.waitWithLatch(60_000) + { latch -> val keysBackupService = aliceSession2.cryptoService().keysBackupService() commonTestHelper.retryPeriodicallyWithLatch(latch) { Log.d("#TEST", "Recovery :${keysBackupService.getKeyBackupRecoveryKeyInfo()?.recoveryKey}") diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/SASTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/SASTest.kt index 8cd725504d..395e86ac92 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/SASTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/SASTest.kt @@ -18,6 +18,7 @@ package org.matrix.android.sdk.internal.crypto.verification import android.util.Log import androidx.test.ext.junit.runners.AndroidJUnit4 +import kotlinx.coroutines.runBlocking import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse import org.junit.Assert.assertNotNull @@ -32,9 +33,7 @@ import org.junit.runners.MethodSorters import org.matrix.android.sdk.InstrumentedTest import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.crypto.verification.CancelCode -import org.matrix.android.sdk.api.session.crypto.verification.IncomingSasVerificationTransaction -import org.matrix.android.sdk.api.session.crypto.verification.OutgoingSasVerificationTransaction -import org.matrix.android.sdk.api.session.crypto.verification.SasMode +import org.matrix.android.sdk.api.session.crypto.verification.PendingVerificationRequest import org.matrix.android.sdk.api.session.crypto.verification.SasVerificationTransaction import org.matrix.android.sdk.api.session.crypto.verification.VerificationMethod import org.matrix.android.sdk.api.session.crypto.verification.VerificationService @@ -49,6 +48,8 @@ import org.matrix.android.sdk.internal.crypto.model.MXUsersDevicesMap import org.matrix.android.sdk.internal.crypto.model.rest.KeyVerificationCancel import org.matrix.android.sdk.internal.crypto.model.rest.KeyVerificationStart import org.matrix.android.sdk.internal.crypto.model.rest.toValue +import timber.log.Timber +import java.util.UUID import java.util.concurrent.CountDownLatch @RunWith(AndroidJUnit4::class) @@ -75,10 +76,15 @@ class SASTest : InstrumentedTest { } bobVerificationService.addListener(bobListener) - val txID = aliceVerificationService.beginKeyVerification(VerificationMethod.SAS, - bobSession.myUserId, - bobSession.cryptoService().getMyDevice().deviceId, - null) + val myDevice = testHelper.runBlockingTest { + bobSession.cryptoService().getMyDevice() + } + val txID = testHelper.runBlockingTest { + aliceVerificationService.beginKeyVerification(VerificationMethod.SAS, + bobSession.myUserId, + myDevice.deviceId, + null) + } assertNotNull("Alice should have a started transaction", txID) val aliceKeyTx = aliceVerificationService.getExistingTransaction(bobSession.myUserId, txID!!) @@ -90,16 +96,14 @@ class SASTest : InstrumentedTest { val bobKeyTx = bobVerificationService.getExistingTransaction(aliceSession.myUserId, txID) assertNotNull("Bob should have started verif transaction", bobKeyTx) - assertTrue(bobKeyTx is SASDefaultVerificationTransaction) + assertTrue(bobKeyTx is SasVerificationTransaction) assertNotNull("Bob should have starting a SAS transaction", bobKeyTx) - assertTrue(aliceKeyTx is SASDefaultVerificationTransaction) + assertTrue(aliceKeyTx is SasVerificationTransaction) assertEquals("Alice and Bob have same transaction id", aliceKeyTx!!.transactionId, bobKeyTx!!.transactionId) - val aliceSasTx = aliceKeyTx as SASDefaultVerificationTransaction? - val bobSasTx = bobKeyTx as SASDefaultVerificationTransaction? - assertEquals("Alice state should be started", VerificationTxState.Started, aliceSasTx!!.state) - assertEquals("Bob state should be started by alice", VerificationTxState.OnStarted, bobSasTx!!.state) + assertEquals("Alice state should be started", VerificationTxState.Started, aliceKeyTx.state) + assertEquals("Bob state should be started by alice", VerificationTxState.OnStarted, bobKeyTx.state) // Let's cancel from alice side val cancelLatch = CountDownLatch(1) @@ -107,7 +111,7 @@ class SASTest : InstrumentedTest { val bobListener2 = object : VerificationService.Listener { override fun transactionUpdated(tx: VerificationTransaction) { if (tx.transactionId == txID) { - val immutableState = (tx as SASDefaultVerificationTransaction).state + val immutableState = (tx as SasVerificationTransaction).state if (immutableState is VerificationTxState.Cancelled && !immutableState.byMe) { cancelLatch.countDown() } @@ -116,14 +120,16 @@ class SASTest : InstrumentedTest { } bobVerificationService.addListener(bobListener2) - aliceSasTx.cancel(CancelCode.User) + testHelper.runBlockingTest { + aliceKeyTx.cancel(CancelCode.User) + } testHelper.await(cancelLatch) - assertTrue("Should be cancelled on alice side", aliceSasTx.state is VerificationTxState.Cancelled) - assertTrue("Should be cancelled on bob side", bobSasTx.state is VerificationTxState.Cancelled) + 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 = aliceSasTx.state as VerificationTxState.Cancelled - val bobCancelState = bobSasTx.state as 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) @@ -177,12 +183,16 @@ class SASTest : InstrumentedTest { val aliceSession = cryptoTestData.firstSession val aliceUserID = aliceSession.myUserId - val aliceDevice = aliceSession.cryptoService().getMyDevice().deviceId + val aliceDevice = testHelper.runBlockingTest { + aliceSession.cryptoService().getMyDevice().deviceId + } val aliceListener = object : VerificationService.Listener { override fun transactionUpdated(tx: VerificationTransaction) { - if ((tx as IncomingSasVerificationTransaction).uxState === IncomingSasVerificationTransaction.UxState.SHOW_ACCEPT) { - (tx as IncomingSasVerificationTransaction).performAccept() + if (tx.state is VerificationTxState.OnStarted && tx is SasVerificationTransaction) { + testHelper.runBlockingTest { + tx.acceptVerification() + } } } } @@ -226,7 +236,9 @@ class SASTest : InstrumentedTest { val aliceSession = cryptoTestData.firstSession val aliceUserID = aliceSession.myUserId - val aliceDevice = aliceSession.cryptoService().getMyDevice().deviceId + val aliceDevice = testHelper.runBlockingTest { + aliceSession.cryptoService().getMyDevice().deviceId + } fakeBobStart(bobSession, aliceUserID, aliceDevice, tid, mac = mac) @@ -267,7 +279,9 @@ class SASTest : InstrumentedTest { val aliceSession = cryptoTestData.firstSession val aliceUserID = aliceSession.myUserId - val aliceDevice = aliceSession.cryptoService().getMyDevice().deviceId + val aliceDevice = testHelper.runBlockingTest { + aliceSession.cryptoService().getMyDevice().deviceId + } fakeBobStart(bobSession, aliceUserID, aliceDevice, tid, codes = codes) @@ -283,12 +297,15 @@ class SASTest : InstrumentedTest { aliceUserID: String?, aliceDevice: String?, tid: String, - protocols: List = SASDefaultVerificationTransaction.KNOWN_AGREEMENT_PROTOCOLS, - hashes: List = SASDefaultVerificationTransaction.KNOWN_HASHES, - mac: List = SASDefaultVerificationTransaction.KNOWN_MACS, - codes: List = SASDefaultVerificationTransaction.KNOWN_SHORT_CODES) { + protocols: List = emptyList(), + hashes: List = emptyList(), + mac: List = emptyList(), + codes: List = emptyList()) { + val deviceId = runBlocking { + bobSession.cryptoService().getMyDevice().deviceId + } val startMessage = KeyVerificationStart( - fromDevice = bobSession.cryptoService().getMyDevice().deviceId, + fromDevice = deviceId, method = VerificationMethod.SAS.toValue(), transactionId = tid, keyAgreementProtocols = protocols, @@ -324,15 +341,15 @@ class SASTest : InstrumentedTest { val aliceCreatedLatch = CountDownLatch(2) val aliceCancelledLatch = CountDownLatch(2) - val createdTx = mutableListOf() + val createdTx = mutableListOf() val aliceListener = object : VerificationService.Listener { override fun transactionCreated(tx: VerificationTransaction) { - createdTx.add(tx as SASDefaultVerificationTransaction) + createdTx.add(tx) aliceCreatedLatch.countDown() } override fun transactionUpdated(tx: VerificationTransaction) { - if ((tx as SASDefaultVerificationTransaction).state is VerificationTxState.Cancelled && !(tx.state as VerificationTxState.Cancelled).byMe) { + if (tx.state is VerificationTxState.Cancelled && !(tx.state as VerificationTxState.Cancelled).byMe) { aliceCancelledLatch.countDown() } } @@ -340,10 +357,13 @@ class SASTest : InstrumentedTest { aliceVerificationService.addListener(aliceListener) val bobUserId = bobSession!!.myUserId - val bobDeviceId = bobSession.cryptoService().getMyDevice().deviceId - aliceVerificationService.beginKeyVerification(VerificationMethod.SAS, bobUserId, bobDeviceId, null) - aliceVerificationService.beginKeyVerification(VerificationMethod.SAS, bobUserId, bobDeviceId, null) - + val bobDeviceId = testHelper.runBlockingTest { + bobSession.cryptoService().getMyDevice().deviceId + } + testHelper.runBlockingTest { + aliceVerificationService.beginKeyVerification(VerificationMethod.SAS, bobUserId, bobDeviceId, null) + aliceVerificationService.beginKeyVerification(VerificationMethod.SAS, bobUserId, bobDeviceId, null) + } testHelper.await(aliceCreatedLatch) testHelper.await(aliceCancelledLatch) @@ -366,17 +386,10 @@ class SASTest : InstrumentedTest { val aliceVerificationService = aliceSession.cryptoService().verificationService() val bobVerificationService = bobSession!!.cryptoService().verificationService() - var accepted: ValidVerificationInfoAccept? = null - var startReq: ValidVerificationInfoStart.SasVerificationInfoStart? = null - val aliceAcceptedLatch = CountDownLatch(1) val aliceListener = object : VerificationService.Listener { override fun transactionUpdated(tx: VerificationTransaction) { - Log.v("TEST", "== aliceTx state ${tx.state} => ${(tx as? OutgoingSasVerificationTransaction)?.uxState}") - if ((tx as SASDefaultVerificationTransaction).state === VerificationTxState.OnAccepted) { - val at = tx as SASDefaultVerificationTransaction - accepted = at.accepted - startReq = at.startReq + if (tx.state is VerificationTxState.OnAccepted) { aliceAcceptedLatch.countDown() } } @@ -385,32 +398,24 @@ class SASTest : InstrumentedTest { val bobListener = object : VerificationService.Listener { override fun transactionUpdated(tx: VerificationTransaction) { - Log.v("TEST", "== bobTx state ${tx.state} => ${(tx as? IncomingSasVerificationTransaction)?.uxState}") - if ((tx as IncomingSasVerificationTransaction).uxState === IncomingSasVerificationTransaction.UxState.SHOW_ACCEPT) { + if (tx.state is VerificationTxState.OnStarted && tx is SasVerificationTransaction) { bobVerificationService.removeListener(this) - val at = tx as IncomingSasVerificationTransaction - at.performAccept() + testHelper.runBlockingTest { + tx.acceptVerification() + } } } } bobVerificationService.addListener(bobListener) val bobUserId = bobSession.myUserId - val bobDeviceId = bobSession.cryptoService().getMyDevice().deviceId - aliceVerificationService.beginKeyVerification(VerificationMethod.SAS, bobUserId, bobDeviceId, null) - testHelper.await(aliceAcceptedLatch) - - assertTrue("Should have receive a commitment", accepted!!.commitment?.trim()?.isEmpty() == false) - - // check that agreement is valid - assertTrue("Agreed Protocol should be Valid", accepted != null) - assertTrue("Agreed Protocol should be known by alice", startReq!!.keyAgreementProtocols.contains(accepted!!.keyAgreementProtocol)) - assertTrue("Hash should be known by alice", startReq!!.hashes.contains(accepted!!.hash)) - assertTrue("Hash should be known by alice", startReq!!.messageAuthenticationCodes.contains(accepted!!.messageAuthenticationCode)) - - accepted!!.shortAuthenticationStrings.forEach { - assertTrue("all agreed Short Code should be known by alice", startReq!!.shortAuthenticationStrings.contains(it)) + val bobDeviceId = runBlocking { + bobSession.cryptoService().getMyDevice().deviceId } + testHelper.runBlockingTest { + aliceVerificationService.beginKeyVerification(VerificationMethod.SAS, bobUserId, bobDeviceId, null) + } + testHelper.await(aliceAcceptedLatch) cryptoTestData.cleanUp(testHelper) } @@ -422,20 +427,22 @@ class SASTest : InstrumentedTest { val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom() val aliceSession = cryptoTestData.firstSession - val bobSession = cryptoTestData.secondSession + val bobSession = cryptoTestData.secondSession!! + + cryptoTestHelper.initializeCrossSigning(aliceSession) + cryptoTestHelper.initializeCrossSigning(bobSession) val aliceVerificationService = aliceSession.cryptoService().verificationService() - val bobVerificationService = bobSession!!.cryptoService().verificationService() + val bobVerificationService = bobSession.cryptoService().verificationService() val aliceSASLatch = CountDownLatch(1) val aliceListener = object : VerificationService.Listener { override fun transactionUpdated(tx: VerificationTransaction) { - val uxState = (tx as OutgoingSasVerificationTransaction).uxState - when (uxState) { - OutgoingSasVerificationTransaction.UxState.SHOW_SAS -> { + when (tx.state) { + VerificationTxState.ShortCodeReady -> { aliceSASLatch.countDown() } - else -> Unit + else -> Unit } } } @@ -443,32 +450,42 @@ class SASTest : InstrumentedTest { val bobSASLatch = CountDownLatch(1) val bobListener = object : VerificationService.Listener { + + override fun verificationRequestCreated(pr: PendingVerificationRequest) { + + } + override fun transactionUpdated(tx: VerificationTransaction) { - val uxState = (tx as IncomingSasVerificationTransaction).uxState - when (uxState) { - IncomingSasVerificationTransaction.UxState.SHOW_ACCEPT -> { - tx.performAccept() + val sasVerification = tx as SasVerificationTransaction + when (tx.state) { + VerificationTxState.OnStarted -> testHelper.runBlockingTest { + sasVerification.acceptVerification() } - else -> Unit - } - if (uxState === IncomingSasVerificationTransaction.UxState.SHOW_SAS) { - bobSASLatch.countDown() + VerificationTxState.ShortCodeReady -> { + bobSASLatch.countDown() + } + else -> Unit } } } bobVerificationService.addListener(bobListener) val bobUserId = bobSession.myUserId - val bobDeviceId = bobSession.cryptoService().getMyDevice().deviceId - val verificationSAS = aliceVerificationService.beginKeyVerification(VerificationMethod.SAS, bobUserId, bobDeviceId, null) + testHelper.runBlockingTest { + aliceSession.cryptoService().downloadKeys(listOf(bobUserId), forceDownload = true) + aliceVerificationService.requestKeyVerificationInDMs(listOf(VerificationMethod.SAS),bobUserId, cryptoTestData.roomId) + } testHelper.await(aliceSASLatch) testHelper.await(bobSASLatch) - val aliceTx = aliceVerificationService.getExistingTransaction(bobUserId, verificationSAS!!) as SASDefaultVerificationTransaction - val bobTx = bobVerificationService.getExistingTransaction(aliceSession.myUserId, verificationSAS) as SASDefaultVerificationTransaction + /* + val aliceTx = aliceVerificationService.getExistingTransaction(bobUserId, verificationSAS!!) as SasVerificationTransaction + val bobTx = bobVerificationService.getExistingTransaction(aliceSession.myUserId, verificationSAS) as SasVerificationTransaction - assertEquals("Should have same SAS", aliceTx.getShortCodeRepresentation(SasMode.DECIMAL), - bobTx.getShortCodeRepresentation(SasMode.DECIMAL)) + + assertEquals("Should have same SAS", aliceTx.getDecimalCodeRepresentation(), bobTx.getDecimalCodeRepresentation()) + + */ cryptoTestData.cleanUp(testHelper) } @@ -489,19 +506,18 @@ class SASTest : InstrumentedTest { val aliceListener = object : VerificationService.Listener { var matchOnce = true override fun transactionUpdated(tx: VerificationTransaction) { - val uxState = (tx as OutgoingSasVerificationTransaction).uxState - Log.v("TEST", "== aliceState ${uxState.name}") - when (uxState) { - OutgoingSasVerificationTransaction.UxState.SHOW_SAS -> { + if (tx !is SasVerificationTransaction) return + when (tx.state) { + VerificationTxState.ShortCodeReady -> testHelper.runBlockingTest { tx.userHasVerifiedShortCode() } - OutgoingSasVerificationTransaction.UxState.VERIFIED -> { + VerificationTxState.Verified -> { if (matchOnce) { matchOnce = false aliceSASLatch.countDown() } } - else -> Unit + else -> Unit } } } @@ -512,40 +528,46 @@ class SASTest : InstrumentedTest { var acceptOnce = true var matchOnce = true override fun transactionUpdated(tx: VerificationTransaction) { - val uxState = (tx as IncomingSasVerificationTransaction).uxState - Log.v("TEST", "== bobState ${uxState.name}") - when (uxState) { - IncomingSasVerificationTransaction.UxState.SHOW_ACCEPT -> { + if (tx !is SasVerificationTransaction) return + when (tx.state) { + VerificationTxState.OnStarted -> testHelper.runBlockingTest { if (acceptOnce) { acceptOnce = false - tx.performAccept() + tx.acceptVerification() } } - IncomingSasVerificationTransaction.UxState.SHOW_SAS -> { + VerificationTxState.ShortCodeReady -> testHelper.runBlockingTest { if (matchOnce) { matchOnce = false tx.userHasVerifiedShortCode() } } - IncomingSasVerificationTransaction.UxState.VERIFIED -> { + VerificationTxState.Verified -> { bobSASLatch.countDown() } - else -> Unit + else -> Unit } } } bobVerificationService.addListener(bobListener) val bobUserId = bobSession.myUserId - val bobDeviceId = bobSession.cryptoService().getMyDevice().deviceId - aliceVerificationService.beginKeyVerification(VerificationMethod.SAS, bobUserId, bobDeviceId, null) + val bobDeviceId = runBlocking { + bobSession.cryptoService().getMyDevice().deviceId + } + testHelper.runBlockingTest { + aliceVerificationService.beginKeyVerification(VerificationMethod.SAS, bobUserId, bobDeviceId, null) + } testHelper.await(aliceSASLatch) testHelper.await(bobSASLatch) // Assert that devices are verified - val bobDeviceInfoFromAlicePOV: CryptoDeviceInfo? = aliceSession.cryptoService().getDeviceInfo(bobUserId, bobDeviceId) - val aliceDeviceInfoFromBobPOV: CryptoDeviceInfo? = bobSession.cryptoService().getDeviceInfo(aliceSession.myUserId, aliceSession.cryptoService().getMyDevice().deviceId) - + val bobDeviceInfoFromAlicePOV: CryptoDeviceInfo? = testHelper.runBlockingTest { + aliceSession.cryptoService().getDeviceInfo(bobUserId, bobDeviceId) + } + val aliceDeviceInfoFromBobPOV: CryptoDeviceInfo? = testHelper.runBlockingTest { + bobSession.cryptoService().getDeviceInfo(aliceSession.myUserId, aliceSession.cryptoService().getMyDevice().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) cryptoTestData.cleanUp(testHelper) @@ -563,11 +585,13 @@ class SASTest : InstrumentedTest { val aliceVerificationService = aliceSession.cryptoService().verificationService() val bobVerificationService = bobSession!!.cryptoService().verificationService() - val req = aliceVerificationService.requestKeyVerificationInDMs( - listOf(VerificationMethod.SAS, VerificationMethod.QR_CODE_SCAN, VerificationMethod.QR_CODE_SHOW), - bobSession.myUserId, - cryptoTestData.roomId - ) + val req = testHelper.runBlockingTest { + aliceVerificationService.requestKeyVerificationInDMs( + listOf(VerificationMethod.SAS, VerificationMethod.QR_CODE_SCAN, VerificationMethod.QR_CODE_SHOW), + bobSession.myUserId, + cryptoTestData.roomId + ) + } var requestID: String? = null @@ -590,11 +614,13 @@ class SASTest : InstrumentedTest { } } - bobVerificationService.readyPendingVerification( - listOf(VerificationMethod.SAS, VerificationMethod.QR_CODE_SCAN, VerificationMethod.QR_CODE_SHOW), - aliceSession.myUserId, - requestID!! - ) + testHelper.runBlockingTest { + bobVerificationService.readyPendingVerification( + listOf(VerificationMethod.SAS, VerificationMethod.QR_CODE_SCAN, VerificationMethod.QR_CODE_SHOW), + aliceSession.myUserId, + requestID!! + ) + } // wait for alice to get the ready testHelper.waitWithLatch { @@ -606,19 +632,22 @@ class SASTest : InstrumentedTest { } // Start concurrent! - aliceVerificationService.beginKeyVerificationInDMs( - VerificationMethod.SAS, - requestID!!, - cryptoTestData.roomId, - bobSession.myUserId, - bobSession.sessionParams.deviceId!!) + testHelper.runBlockingTest { + aliceVerificationService.beginKeyVerificationInDMs( + VerificationMethod.SAS, + requestID!!, + cryptoTestData.roomId, + bobSession.myUserId, + bobSession.sessionParams.deviceId!!) - bobVerificationService.beginKeyVerificationInDMs( - VerificationMethod.SAS, - requestID!!, - cryptoTestData.roomId, - aliceSession.myUserId, - aliceSession.sessionParams.deviceId!!) + bobVerificationService.beginKeyVerificationInDMs( + VerificationMethod.SAS, + requestID!!, + cryptoTestData.roomId, + aliceSession.myUserId, + aliceSession.sessionParams.deviceId!!) + + } // we should reach SHOW SAS on both var alicePovTx: SasVerificationTransaction? diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/qrcode/VerificationTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/VerificationTest.kt similarity index 71% rename from matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/qrcode/VerificationTest.kt rename to matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/VerificationTest.kt index 35c5a4dab9..ef04372ecf 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/qrcode/VerificationTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/VerificationTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2020 The Matrix.org Foundation C.I.C. + * Copyright (c) 2022 New Vector Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.matrix.android.sdk.internal.crypto.verification.qrcode +package org.matrix.android.sdk.internal.crypto.verification import androidx.test.ext.junit.runners.AndroidJUnit4 import org.amshove.kluent.shouldBe @@ -23,19 +23,13 @@ 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.auth.UIABaseAuth -import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor -import org.matrix.android.sdk.api.auth.UserPasswordAuth -import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse import org.matrix.android.sdk.api.session.crypto.verification.PendingVerificationRequest import org.matrix.android.sdk.api.session.crypto.verification.VerificationMethod import org.matrix.android.sdk.api.session.crypto.verification.VerificationService import org.matrix.android.sdk.common.CommonTestHelper import org.matrix.android.sdk.common.CryptoTestHelper -import org.matrix.android.sdk.common.TestConstants +import timber.log.Timber import java.util.concurrent.CountDownLatch -import kotlin.coroutines.Continuation -import kotlin.coroutines.resume @RunWith(AndroidJUnit4::class) @FixMethodOrder(MethodSorters.JVM) @@ -147,50 +141,19 @@ class VerificationTest : InstrumentedTest { ExpectedResult(sasIsSupported = true, otherCanShowQrCode = true, otherCanScanQrCode = true) ) - // TODO Add tests without SAS - private fun doTest(aliceSupportedMethods: List, bobSupportedMethods: List, expectedResultForAlice: ExpectedResult, expectedResultForBob: ExpectedResult) { - val testHelper = CommonTestHelper(context()) - val cryptoTestHelper = CryptoTestHelper(testHelper) + val testHelper = CommonTestHelper(context()) + val cryptoTestHelper = CryptoTestHelper(testHelper) val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom() val aliceSession = cryptoTestData.firstSession val bobSession = cryptoTestData.secondSession!! - testHelper.doSync { callback -> - aliceSession.cryptoService().crossSigningService() - .initializeCrossSigning( - object : UserInteractiveAuthInterceptor { - override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation) { - promise.resume( - UserPasswordAuth( - user = aliceSession.myUserId, - password = TestConstants.PASSWORD, - session = flowResponse.session - ) - ) - } - }, callback) - } - - testHelper.doSync { callback -> - bobSession.cryptoService().crossSigningService() - .initializeCrossSigning( - object : UserInteractiveAuthInterceptor { - override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation) { - promise.resume( - UserPasswordAuth( - user = bobSession.myUserId, - password = TestConstants.PASSWORD, - session = flowResponse.session - ) - ) - } - }, callback) - } + cryptoTestHelper.initializeCrossSigning(aliceSession) + cryptoTestHelper.initializeCrossSigning(bobSession) val aliceVerificationService = aliceSession.cryptoService().verificationService() val bobVerificationService = bobSession.cryptoService().verificationService() @@ -202,6 +165,7 @@ class VerificationTest : InstrumentedTest { val aliceListener = object : VerificationService.Listener { override fun verificationRequestUpdated(pr: PendingVerificationRequest) { // Step 4: Alice receive the ready request + Timber.v("Alice is ready: ${pr.isReady}") if (pr.isReady) { aliceReadyPendingVerificationRequest = pr latch.countDown() @@ -213,16 +177,19 @@ class VerificationTest : InstrumentedTest { val bobListener = object : VerificationService.Listener { override fun verificationRequestCreated(pr: PendingVerificationRequest) { // Step 2: Bob accepts the verification request - bobVerificationService.readyPendingVerificationInDMs( - bobSupportedMethods, - aliceSession.myUserId, - cryptoTestData.roomId, - pr.transactionId!! - ) + Timber.v("Bob accepts the verification request") + testHelper.runBlockingTest { + bobVerificationService.readyPendingVerification( + bobSupportedMethods, + aliceSession.myUserId, + pr.transactionId!! + ) + } } override fun verificationRequestUpdated(pr: PendingVerificationRequest) { // Step 3: Bob is ready + Timber.v("Bob is ready: ${pr.isReady}") if (pr.isReady) { bobReadyPendingVerificationRequest = pr latch.countDown() @@ -233,7 +200,9 @@ class VerificationTest : InstrumentedTest { val bobUserId = bobSession.myUserId // Step 1: Alice starts a verification request - aliceVerificationService.requestKeyVerificationInDMs(aliceSupportedMethods, bobUserId, cryptoTestData.roomId) + testHelper.runBlockingTest { + aliceVerificationService.requestKeyVerificationInDMs(aliceSupportedMethods, bobUserId, cryptoTestData.roomId) + } testHelper.await(latch) aliceReadyPendingVerificationRequest!!.let { pr -> diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/qrcode/SharedSecretTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/qrcode/SharedSecretTest.kt deleted file mode 100644 index 9b10f9e9af..0000000000 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/qrcode/SharedSecretTest.kt +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2020 The Matrix.org Foundation C.I.C. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.matrix.android.sdk.internal.crypto.verification.qrcode - -import androidx.test.ext.junit.runners.AndroidJUnit4 -import org.amshove.kluent.shouldBe -import org.amshove.kluent.shouldNotBeEqualTo -import org.junit.FixMethodOrder -import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters -import org.matrix.android.sdk.InstrumentedTest - -@RunWith(AndroidJUnit4::class) -@FixMethodOrder(MethodSorters.JVM) -class SharedSecretTest : InstrumentedTest { - - @Test - fun testSharedSecretLengthCase() { - repeat(100) { - generateSharedSecretV2().length shouldBe 11 - } - } - - @Test - fun testSharedDiffCase() { - val sharedSecret1 = generateSharedSecretV2() - val sharedSecret2 = generateSharedSecretV2() - - sharedSecret1 shouldNotBeEqualTo sharedSecret2 - } -} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/verification/IncomingSasVerificationTransaction.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/verification/IncomingSasVerificationTransaction.kt deleted file mode 100644 index db2ea72e88..0000000000 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/verification/IncomingSasVerificationTransaction.kt +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2020 The Matrix.org Foundation C.I.C. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.matrix.android.sdk.api.session.crypto.verification - -interface IncomingSasVerificationTransaction : SasVerificationTransaction { - val uxState: UxState - - fun performAccept() - - enum class UxState { - UNKNOWN, - SHOW_ACCEPT, - WAIT_FOR_KEY_AGREEMENT, - SHOW_SAS, - WAIT_FOR_VERIFICATION, - VERIFIED, - CANCELLED_BY_ME, - CANCELLED_BY_OTHER - } -} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/verification/OutgoingSasVerificationTransaction.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/verification/OutgoingSasVerificationTransaction.kt deleted file mode 100644 index 38ee5dc7e7..0000000000 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/verification/OutgoingSasVerificationTransaction.kt +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2020 The Matrix.org Foundation C.I.C. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.matrix.android.sdk.api.session.crypto.verification - -interface OutgoingSasVerificationTransaction : SasVerificationTransaction { - val uxState: UxState - - enum class UxState { - UNKNOWN, - WAIT_FOR_START, - WAIT_FOR_KEY_AGREEMENT, - SHOW_SAS, - WAIT_FOR_VERIFICATION, - VERIFIED, - CANCELLED_BY_ME, - CANCELLED_BY_OTHER - } -} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/verification/VerificationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/verification/VerificationService.kt index 41ba0b8b4e..8a8f80426b 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/verification/VerificationService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/verification/VerificationService.kt @@ -46,54 +46,37 @@ interface VerificationService { fun getExistingVerificationRequestInRoom(roomId: String, tid: String?): PendingVerificationRequest? - suspend fun beginKeyVerification(method: VerificationMethod, - otherUserId: String, - otherDeviceId: String, - transactionId: String?): String? - /** - * Request key verification with another user via room events (instead of the to-device API) + * Request key verification with another user via room events (instead of the to-device API). */ suspend fun requestKeyVerificationInDMs(methods: List, - otherUserId: String, - roomId: String, - localId: String? = LocalEcho.createLocalEchoId()): PendingVerificationRequest + otherUserId: String, + roomId: String, + localId: String? = LocalEcho.createLocalEchoId()): PendingVerificationRequest + + /** + * Request a self key verification using to-device API (instead of room events). + */ + suspend fun requestSelfKeyVerification(methods: List): PendingVerificationRequest + + /** + * You should call this method after receiving a verification request. + * Accept the verification request advertising the given methods as supported + * Returns false if the request is unknown or transaction is not ready. + */ + suspend fun readyPendingVerification(methods: List, + otherUserId: String, + transactionId: String): Boolean suspend fun cancelVerificationRequest(request: PendingVerificationRequest) - /** - * Request a key verification from another user using toDevice events. - */ - suspend fun requestKeyVerification(methods: List, - otherUserId: String, - otherDevices: List?): PendingVerificationRequest + suspend fun cancelVerificationRequest(otherUserId: String, transactionId: String) - suspend fun declineVerificationRequestInDMs(otherUserId: String, - transactionId: String, - roomId: String) + suspend fun beginKeyVerification(method: VerificationMethod, + otherUserId: String, + transactionId: String): String? - // Only SAS method is supported for the moment - // TODO Parameter otherDeviceId should be removed in this case - suspend fun beginKeyVerificationInDMs(method: VerificationMethod, - transactionId: String, - roomId: String, - otherUserId: String, - otherDeviceId: String): String - - /** - * Returns false if the request is unknown - */ - suspend fun readyPendingVerificationInDMs(methods: List, - otherUserId: String, - roomId: String, - transactionId: String): Boolean - - /** - * Returns false if the request is unknown - */ - suspend fun readyPendingVerification(methods: List, - otherUserId: String, - transactionId: String): Boolean + suspend fun beginDeviceVerification(otherUserId: String, otherDeviceId: String): String? interface Listener { /** diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt index c33885b925..caef4a23ed 100755 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt @@ -775,7 +775,7 @@ internal class DefaultCryptoService @Inject constructor( } } } catch (throwable: Throwable) { - Timber.tag(loggerTag.value).e(throwable, "## CRYPTO | doKeyDownloadForUsers(): error") + Timber.tag(loggerTag.value).e(throwable, "## CRYPTO doKeyDownloadForUsers(): error") } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/RustVerificationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/RustVerificationService.kt index 8ef383f193..780229f038 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/RustVerificationService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/RustVerificationService.kt @@ -229,17 +229,12 @@ internal class RustVerificationService @Inject constructor(private val olmMachin return null } - override suspend fun requestKeyVerification( - methods: List, - otherUserId: String, - otherDevices: List? - ): PendingVerificationRequest { - val verification = when (val identity = olmMachine.getIdentity(otherUserId)) { + override suspend fun requestSelfKeyVerification(methods: List): PendingVerificationRequest { + val verification = when (val identity = olmMachine.getIdentity(olmMachine.userId())) { is OwnUserIdentity -> identity.requestVerification(methods) is UserIdentity -> throw IllegalArgumentException("This method doesn't support verification of other users devices") null -> throw IllegalArgumentException("Cross signing has not been bootstrapped for our own user") } - return verification.toPendingVerificationRequest() } @@ -249,7 +244,7 @@ internal class RustVerificationService @Inject constructor(private val olmMachin roomId: String, localId: String? ): PendingVerificationRequest { - Timber.i("## SAS Requesting verification to user: $otherUserId in room $roomId") + olmMachine.ensureUsersKeys(listOf(otherUserId)) val verification = when (val identity = olmMachine.getIdentity(otherUserId)) { is UserIdentity -> identity.requestVerification(methods, roomId, localId!!) is OwnUserIdentity -> throw IllegalArgumentException("This method doesn't support verification of our own user") @@ -284,78 +279,49 @@ internal class RustVerificationService @Inject constructor(private val olmMachin } } - override suspend fun readyPendingVerificationInDMs( - methods: List, - otherUserId: String, - roomId: String, - transactionId: String - ): Boolean { - return readyPendingVerification(methods, otherUserId, transactionId) - } - override suspend fun beginKeyVerification( method: VerificationMethod, otherUserId: String, - otherDeviceId: String, - transactionId: String? + transactionId: String ): String? { return if (method == VerificationMethod.SAS) { - if (transactionId != null) { - val request = olmMachine.getVerificationRequest(otherUserId, transactionId) + val request = olmMachine.getVerificationRequest(otherUserId, transactionId) - val sas = request?.startSasVerification() + val sas = request?.startSasVerification() - if (sas != null) { - dispatcher.dispatchTxAdded(sas) - sas.transactionId - } else { - null - } + if (sas != null) { + dispatcher.dispatchTxAdded(sas) + sas.transactionId } else { - // This starts the short SAS flow, the one that doesn't start with - // a `m.key.verification.request`, Element web stopped doing this, might - // be wise do do so as well - // DeviceListBottomSheetViewModel triggers this, interestingly the method that - // triggers this is called `manuallyVerify()` - val otherDevice = olmMachine.getDevice(otherUserId, otherDeviceId) - val verification = otherDevice?.startVerification() - if (verification != null) { - dispatcher.dispatchTxAdded(verification) - verification.transactionId - } else { - null - } + null } } else { throw IllegalArgumentException("Unknown verification method") } } - override suspend fun beginKeyVerificationInDMs( - method: VerificationMethod, - transactionId: String, - roomId: String, - otherUserId: String, - otherDeviceId: String - ): String { - beginKeyVerification(method, otherUserId, otherDeviceId, transactionId) - // TODO what's the point of returning the same ID we got as an argument? - // We do this because the old verification service did so - return transactionId + override suspend fun beginDeviceVerification(otherUserId: String, otherDeviceId: String): String? { + // This starts the short SAS flow, the one that doesn't start with + // a `m.key.verification.request`, Element web stopped doing this, might + // be wise do do so as well + // DeviceListBottomSheetViewModel triggers this, interestingly the method that + // triggers this is called `manuallyVerify()` + val otherDevice = olmMachine.getDevice(otherUserId, otherDeviceId) + val verification = otherDevice?.startVerification() + return if (verification != null) { + dispatcher.dispatchTxAdded(verification) + verification.transactionId + } else { + null + } } override suspend fun cancelVerificationRequest(request: PendingVerificationRequest) { - val verificationRequest = request.transactionId?.let { - olmMachine.getVerificationRequest(request.otherUserId, it) - } - verificationRequest?.cancel() + request.transactionId ?: return + cancelVerificationRequest(request.otherUserId, request.transactionId) } - override suspend fun declineVerificationRequestInDMs( - otherUserId: String, - transactionId: String, - roomId: String - ) { + override suspend fun cancelVerificationRequest(otherUserId: String, transactionId: String) { val verificationRequest = olmMachine.getVerificationRequest(otherUserId, transactionId) verificationRequest?.cancel() } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/qrcode/SharedSecret.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/qrcode/SharedSecret.kt deleted file mode 100644 index 858c0ab6af..0000000000 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/qrcode/SharedSecret.kt +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2020 The Matrix.org Foundation C.I.C. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.matrix.android.sdk.internal.crypto.verification.qrcode - -import org.matrix.android.sdk.internal.crypto.crosssigning.toBase64NoPadding -import java.security.SecureRandom - -fun generateSharedSecretV2(): String { - val secureRandom = SecureRandom() - - // 8 bytes long - val secretBytes = ByteArray(8) - secureRandom.nextBytes(secretBytes) - return secretBytes.toBase64NoPadding() -} diff --git a/vector/src/androidTest/java/im/vector/app/VerifySessionInteractiveTest.kt b/vector/src/androidTest/java/im/vector/app/VerifySessionInteractiveTest.kt index c82b543a08..c471a59af5 100644 --- a/vector/src/androidTest/java/im/vector/app/VerifySessionInteractiveTest.kt +++ b/vector/src/androidTest/java/im/vector/app/VerifySessionInteractiveTest.kt @@ -51,6 +51,7 @@ import org.matrix.android.sdk.api.session.crypto.verification.VerificationMethod import org.matrix.android.sdk.api.session.crypto.verification.VerificationService import org.matrix.android.sdk.api.session.crypto.verification.VerificationTransaction import org.matrix.android.sdk.api.session.crypto.verification.VerificationTxState +import org.matrix.android.sdk.common.CommonTestHelper import kotlin.coroutines.Continuation import kotlin.coroutines.resume @@ -134,10 +135,8 @@ class VerifySessionInteractiveTest : VerificationTestBase() { onView(withId(R.id.bottomSheetFragmentContainer)) .check(matches(not(hasDescendant(withText(R.string.verification_cannot_access_other_session))))) - val request = existingSession!!.cryptoService().verificationService().requestKeyVerification( - listOf(VerificationMethod.SAS, VerificationMethod.QR_CODE_SCAN, VerificationMethod.QR_CODE_SHOW), - existingSession!!.myUserId, - listOf(uiSession.sessionParams.deviceId!!) + val request = existingSession!!.cryptoService().verificationService().requestSelfKeyVerification( + listOf(VerificationMethod.SAS, VerificationMethod.QR_CODE_SCAN, VerificationMethod.QR_CODE_SHOW) ) val transactionId = request.transactionId!! diff --git a/vector/src/main/java/im/vector/app/features/crypto/verification/IncomingVerificationRequestHandler.kt b/vector/src/main/java/im/vector/app/features/crypto/verification/IncomingVerificationRequestHandler.kt index 14d4b9c5d9..6b01403fe3 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/verification/IncomingVerificationRequestHandler.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/verification/IncomingVerificationRequestHandler.kt @@ -166,9 +166,8 @@ class IncomingVerificationRequestHandler @Inject constructor( } } dismissedAction = LaunchCoroutineRunnable(coroutineScope) { - session?.cryptoService()?.verificationService()?.declineVerificationRequestInDMs(pr.otherUserId, - pr.transactionId ?: "", - pr.roomId ?: "" + session?.cryptoService()?.verificationService()?.cancelVerificationRequest(pr.otherUserId, + pr.transactionId ?: "" ) } colorAttribute = R.attr.vctr_notice_secondary diff --git a/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationBottomSheetViewModel.kt b/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationBottomSheetViewModel.kt index 5fc752028f..5a338cfb45 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationBottomSheetViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationBottomSheetViewModel.kt @@ -238,7 +238,7 @@ class VerificationBottomSheetViewModel @AssistedInject constructor( handleRequestVerificationByDM(roomId, otherUserId) } is VerificationAction.StartSASVerification -> { - handleStartSASVerification(roomId, otherUserId, action) + handleStartSASVerification(otherUserId, action) } is VerificationAction.RemoteQrCodeScanned -> { handleRemoteQrCodeScanned(action) @@ -284,28 +284,16 @@ class VerificationBottomSheetViewModel @AssistedInject constructor( }.exhaustive } - private fun handleStartSASVerification(roomId: String?, otherUserId: String, action: VerificationAction.StartSASVerification) { + private fun handleStartSASVerification(otherUserId: String, action: VerificationAction.StartSASVerification) { val request = session.cryptoService().verificationService().getExistingVerificationRequest(otherUserId, action.pendingRequestTransactionId) ?: return - val otherDevice = if (request.isIncoming) request.requestInfo?.fromDevice else request.readyInfo?.fromDevice viewModelScope.launch { - if (roomId == null) { session.cryptoService().verificationService().beginKeyVerification( VerificationMethod.SAS, otherUserId = request.otherUserId, - otherDeviceId = otherDevice ?: "", transactionId = action.pendingRequestTransactionId ) - } else { - session.cryptoService().verificationService().beginKeyVerificationInDMs( - VerificationMethod.SAS, - transactionId = action.pendingRequestTransactionId, - roomId = roomId, - otherUserId = request.otherUserId, - otherDeviceId = otherDevice ?: "" - ) } - } } private fun handleSASDoNotMatchAction(action: VerificationAction.SASDoNotMatchAction) { diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineViewModel.kt index 1efed66aa5..ea89a1c017 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineViewModel.kt @@ -995,10 +995,9 @@ class TimelineViewModel @AssistedInject constructor( private fun handleAcceptVerification(action: RoomDetailAction.AcceptVerificationRequest) { Timber.v("## SAS handleAcceptVerification ${action.otherUserId}, roomId:${room.roomId}, txId:${action.transactionId}") viewModelScope.launch { - if (session.cryptoService().verificationService().readyPendingVerificationInDMs( + if (session.cryptoService().verificationService().readyPendingVerification( supportedVerificationMethodsProvider.provide(), action.otherUserId, - room.roomId, action.transactionId)) { _viewEvents.post(RoomDetailViewEvents.ActionSuccess(action)) } else { @@ -1009,10 +1008,9 @@ class TimelineViewModel @AssistedInject constructor( private fun handleDeclineVerification(action: RoomDetailAction.DeclineVerificationRequest) { viewModelScope.launch { - session.cryptoService().verificationService().declineVerificationRequestInDMs( + session.cryptoService().verificationService().cancelVerificationRequest( action.otherUserId, - action.transactionId, - room.roomId) + action.transactionId) } } diff --git a/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt b/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt index ee101be732..e4ead15183 100644 --- a/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt +++ b/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt @@ -217,10 +217,8 @@ class DefaultNavigator @Inject constructor( override fun requestSessionVerification(context: Context, otherSessionId: String) { coroutineScope.launch { val session = sessionHolder.getSafeActiveSession() ?: return@launch - val pr = session.cryptoService().verificationService().requestKeyVerification( - supportedVerificationMethodsProvider.provide(), - session.myUserId, - listOf(otherSessionId) + val pr = session.cryptoService().verificationService().requestSelfKeyVerification( + supportedVerificationMethodsProvider.provide() ) if (context is AppCompatActivity) { VerificationBottomSheet.withArgs( @@ -241,10 +239,8 @@ class DefaultNavigator @Inject constructor( .map { it.deviceId } if (context is AppCompatActivity) { if (otherSessions.isNotEmpty()) { - val pr = session.cryptoService().verificationService().requestKeyVerification( - supportedVerificationMethodsProvider.provide(), - session.myUserId, - otherSessions) + val pr = session.cryptoService().verificationService().requestSelfKeyVerification( + supportedVerificationMethodsProvider.provide()) VerificationBottomSheet.forSelfVerification(session, pr.transactionId ?: pr.localId) .show(context.supportFragmentManager, VerificationBottomSheet.WAITING_SELF_VERIF_TAG) } else { diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListBottomSheetViewModel.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListBottomSheetViewModel.kt index f7370ec439..adb0fe958f 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListBottomSheetViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListBottomSheetViewModel.kt @@ -126,11 +126,10 @@ class DeviceListBottomSheetViewModel @AssistedInject constructor(@Assisted priva private fun manuallyVerify(action: DeviceListAction.ManuallyVerify) { if (!initialState.allowDeviceAction) return viewModelScope.launch { - session.cryptoService().verificationService().beginKeyVerification( - method = VerificationMethod.SAS, + session.cryptoService().verificationService().beginDeviceVerification( otherUserId = initialState.userId, otherDeviceId = action.deviceId, - transactionId = null)?.let { txID -> + )?.let { txID -> _viewEvents.post(DeviceListBottomSheetViewEvents.Verify(initialState.userId, txID)) } } diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewModel.kt index 871109210a..768a52cfb2 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewModel.kt @@ -250,7 +250,7 @@ class DevicesViewModel @AssistedInject constructor( viewModelScope.launch { val txID = session.cryptoService() .verificationService() - .beginKeyVerification(VerificationMethod.SAS, session.myUserId, action.deviceId, null) + .beginDeviceVerification(session.myUserId, action.deviceId) _viewEvents.post(DevicesViewEvents.ShowVerifyDevice( session.myUserId, txID