Do some cleanup on verification APIs

This commit is contained in:
ganfra 2022-04-25 17:55:37 +02:00
parent 309a290cb8
commit 8bd094fa66
19 changed files with 323 additions and 514 deletions

View File

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

View File

@ -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<Unit> {
testHelper.runBlockingTest {
aliceSession.cryptoService().crossSigningService()
.initializeCrossSigning(object : UserInteractiveAuthInterceptor {
override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) {
@ -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<Unit> {
testHelper.runBlockingTest {
aliceSession.cryptoService().crossSigningService().initializeCrossSigning(object : UserInteractiveAuthInterceptor {
override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) {
promise.resume(aliceAuthParams)
}
}, it)
})
}
testHelper.runBlockingTest {
bobSession.cryptoService().crossSigningService().initializeCrossSigning(object : UserInteractiveAuthInterceptor {
override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) {
promise.resume(bobAuthParams)
}
})
}
testHelper.doSync<Unit> { bobSession.cryptoService().crossSigningService().initializeCrossSigning(object : UserInteractiveAuthInterceptor {
override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) {
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<Unit> { aliceSession.cryptoService().crossSigningService().initializeCrossSigning(object : UserInteractiveAuthInterceptor {
override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) {
promise.resume(aliceAuthParams)
}
}, it) }
testHelper.doSync<Unit> { bobSession.cryptoService().crossSigningService().initializeCrossSigning(object : UserInteractiveAuthInterceptor {
override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) {
promise.resume(bobAuthParams)
}
}, it) }
testHelper.runBlockingTest {
aliceSession.cryptoService().crossSigningService().initializeCrossSigning(object : UserInteractiveAuthInterceptor {
override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) {
promise.resume(aliceAuthParams)
}
})
}
testHelper.runBlockingTest {
bobSession.cryptoService().crossSigningService().initializeCrossSigning(object : UserInteractiveAuthInterceptor {
override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) {
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<Unit> { 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)

View File

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

View File

@ -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<String> = SASDefaultVerificationTransaction.KNOWN_AGREEMENT_PROTOCOLS,
hashes: List<String> = SASDefaultVerificationTransaction.KNOWN_HASHES,
mac: List<String> = SASDefaultVerificationTransaction.KNOWN_MACS,
codes: List<String> = SASDefaultVerificationTransaction.KNOWN_SHORT_CODES) {
protocols: List<String> = emptyList(),
hashes: List<String> = emptyList(),
mac: List<String> = emptyList(),
codes: List<String> = 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<SASDefaultVerificationTransaction>()
val createdTx = mutableListOf<VerificationTransaction>()
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?

View File

@ -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<VerificationMethod>,
bobSupportedMethods: List<VerificationMethod>,
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<Unit> { callback ->
aliceSession.cryptoService().crossSigningService()
.initializeCrossSigning(
object : UserInteractiveAuthInterceptor {
override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) {
promise.resume(
UserPasswordAuth(
user = aliceSession.myUserId,
password = TestConstants.PASSWORD,
session = flowResponse.session
)
)
}
}, callback)
}
testHelper.doSync<Unit> { callback ->
bobSession.cryptoService().crossSigningService()
.initializeCrossSigning(
object : UserInteractiveAuthInterceptor {
override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) {
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 ->

View File

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

View File

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

View File

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

View File

@ -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<VerificationMethod>,
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<VerificationMethod>): 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<VerificationMethod>,
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<VerificationMethod>,
otherUserId: String,
otherDevices: List<String>?): 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<VerificationMethod>,
otherUserId: String,
roomId: String,
transactionId: String): Boolean
/**
* Returns false if the request is unknown
*/
suspend fun readyPendingVerification(methods: List<VerificationMethod>,
otherUserId: String,
transactionId: String): Boolean
suspend fun beginDeviceVerification(otherUserId: String, otherDeviceId: String): String?
interface Listener {
/**

View File

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

View File

@ -229,17 +229,12 @@ internal class RustVerificationService @Inject constructor(private val olmMachin
return null
}
override suspend fun requestKeyVerification(
methods: List<VerificationMethod>,
otherUserId: String,
otherDevices: List<String>?
): PendingVerificationRequest {
val verification = when (val identity = olmMachine.getIdentity(otherUserId)) {
override suspend fun requestSelfKeyVerification(methods: List<VerificationMethod>): 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<VerificationMethod>,
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()
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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