mirror of
https://github.com/vector-im/element-android.git
synced 2024-11-16 02:05:06 +08:00
Add share test + fix
Crypto config to only request to own device. Only cancel request if ratchet index is low enough
This commit is contained in:
parent
9177cb11d5
commit
9747eb2432
@ -19,6 +19,7 @@ package org.matrix.android.sdk.common
|
||||
import android.os.SystemClock
|
||||
import android.util.Log
|
||||
import androidx.lifecycle.Observer
|
||||
import org.amshove.kluent.fail
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertNotNull
|
||||
import org.junit.Assert.assertNull
|
||||
@ -31,6 +32,7 @@ import org.matrix.android.sdk.api.crypto.MXCRYPTO_ALGORITHM_MEGOLM
|
||||
import org.matrix.android.sdk.api.crypto.MXCRYPTO_ALGORITHM_MEGOLM_BACKUP
|
||||
import org.matrix.android.sdk.api.extensions.orFalse
|
||||
import org.matrix.android.sdk.api.session.Session
|
||||
import org.matrix.android.sdk.api.session.crypto.MXCryptoError
|
||||
import org.matrix.android.sdk.api.session.crypto.crosssigning.KEYBACKUP_SECRET_SSSS_NAME
|
||||
import org.matrix.android.sdk.api.session.crypto.crosssigning.MASTER_KEY_SSSS_NAME
|
||||
import org.matrix.android.sdk.api.session.crypto.crosssigning.SELF_SIGNING_KEY_SSSS_NAME
|
||||
@ -39,6 +41,7 @@ import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysVersion
|
||||
import org.matrix.android.sdk.api.session.crypto.keysbackup.MegolmBackupAuthData
|
||||
import org.matrix.android.sdk.api.session.crypto.keysbackup.MegolmBackupCreationInfo
|
||||
import org.matrix.android.sdk.api.session.crypto.keysbackup.extractCurveKeyFromRecoveryKey
|
||||
import org.matrix.android.sdk.api.session.crypto.model.OlmDecryptionResult
|
||||
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.VerificationMethod
|
||||
@ -46,11 +49,13 @@ import org.matrix.android.sdk.api.session.crypto.verification.VerificationTxStat
|
||||
import org.matrix.android.sdk.api.session.events.model.Event
|
||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||
import org.matrix.android.sdk.api.session.events.model.toContent
|
||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||
import org.matrix.android.sdk.api.session.getRoom
|
||||
import org.matrix.android.sdk.api.session.room.Room
|
||||
import org.matrix.android.sdk.api.session.room.model.Membership
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageContent
|
||||
import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams
|
||||
import org.matrix.android.sdk.api.session.securestorage.EmptyKeySigner
|
||||
import org.matrix.android.sdk.api.session.securestorage.SharedSecretStorageService
|
||||
@ -299,7 +304,8 @@ class CryptoTestHelper(private val testHelper: CommonTestHelper) {
|
||||
)
|
||||
)
|
||||
}
|
||||
}, it)
|
||||
}, it
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -308,7 +314,7 @@ class CryptoTestHelper(private val testHelper: CommonTestHelper) {
|
||||
*/
|
||||
fun bootstrapSecurity(session: Session) {
|
||||
initializeCrossSigning(session)
|
||||
val ssssService = session.sharedSecretStorageService
|
||||
val ssssService = session.sharedSecretStorageService()
|
||||
testHelper.runBlockingTest {
|
||||
val keyInfo = ssssService.generateKey(
|
||||
UUID.randomUUID().toString(),
|
||||
@ -369,7 +375,8 @@ class CryptoTestHelper(private val testHelper: CommonTestHelper) {
|
||||
requestID,
|
||||
roomId,
|
||||
bob.myUserId,
|
||||
bob.sessionParams.credentials.deviceId!!)
|
||||
bob.sessionParams.credentials.deviceId!!
|
||||
)
|
||||
|
||||
// we should reach SHOW SAS on both
|
||||
var alicePovTx: OutgoingSasVerificationTransaction? = null
|
||||
@ -451,4 +458,50 @@ class CryptoTestHelper(private val testHelper: CommonTestHelper) {
|
||||
|
||||
return CryptoTestData(roomId, sessions)
|
||||
}
|
||||
|
||||
fun ensureCanDecrypt(sentEventIds: List<String>, session: Session, e2eRoomID: String, messagesText: List<String>) {
|
||||
sentEventIds.forEachIndexed { index, sentEventId ->
|
||||
testHelper.waitWithLatch { latch ->
|
||||
testHelper.retryPeriodicallyWithLatch(latch) {
|
||||
val event = session.getRoom(e2eRoomID)!!.getTimelineEvent(sentEventId)!!.root
|
||||
testHelper.runBlockingTest {
|
||||
try {
|
||||
session.cryptoService().decryptEvent(event, "").let { result ->
|
||||
event.mxDecryptionResult = OlmDecryptionResult(
|
||||
payload = result.clearEvent,
|
||||
senderKey = result.senderCurve25519Key,
|
||||
keysClaimed = result.claimedEd25519Key?.let { mapOf("ed25519" to it) },
|
||||
forwardingCurve25519KeyChain = result.forwardingCurve25519KeyChain
|
||||
)
|
||||
}
|
||||
} catch (error: MXCryptoError) {
|
||||
// nop
|
||||
}
|
||||
}
|
||||
Log.v("TEST", "ensureCanDecrypt ${event.getClearType()} is ${event.getClearContent()}")
|
||||
event.getClearType() == EventType.MESSAGE &&
|
||||
messagesText[index] == event.getClearContent()?.toModel<MessageContent>()?.body
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun ensureCannotDecrypt(sentEventIds: List<String>, session: Session, e2eRoomID: String, expectedError: MXCryptoError.ErrorType? = null) {
|
||||
sentEventIds.forEach { sentEventId ->
|
||||
val event = session.getRoom(e2eRoomID)!!.getTimelineEvent(sentEventId)!!.root
|
||||
testHelper.runBlockingTest {
|
||||
try {
|
||||
session.cryptoService().decryptEvent(event, "")
|
||||
fail("Should not be able to decrypt event")
|
||||
} catch (error: MXCryptoError) {
|
||||
val errorType = (error as? MXCryptoError.Base)?.errorType
|
||||
if (expectedError == null) {
|
||||
assertNotNull(errorType)
|
||||
} else {
|
||||
assertEquals("Unexpected reason", expectedError, errorType)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -34,7 +34,6 @@ import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysVersion
|
||||
import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysVersionResult
|
||||
import org.matrix.android.sdk.api.session.crypto.keysbackup.MegolmBackupCreationInfo
|
||||
import org.matrix.android.sdk.api.session.crypto.model.ImportRoomKeysResult
|
||||
import org.matrix.android.sdk.api.session.crypto.model.OlmDecryptionResult
|
||||
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.PendingVerificationRequest
|
||||
@ -296,7 +295,7 @@ class E2eeSanityTests : InstrumentedTest {
|
||||
}
|
||||
}
|
||||
// after initial sync events are not decrypted, so we have to try manually
|
||||
ensureCannotDecrypt(sentEventIds, newBobSession, e2eRoomID, MXCryptoError.ErrorType.UNKNOWN_INBOUND_SESSION_ID)
|
||||
cryptoTestHelper.ensureCannotDecrypt(sentEventIds, newBobSession, e2eRoomID, MXCryptoError.ErrorType.UNKNOWN_INBOUND_SESSION_ID)
|
||||
|
||||
// Let's now import keys from backup
|
||||
|
||||
@ -317,7 +316,7 @@ class E2eeSanityTests : InstrumentedTest {
|
||||
}
|
||||
|
||||
// ensure bob can now decrypt
|
||||
ensureCanDecrypt(sentEventIds, newBobSession, e2eRoomID, messagesText)
|
||||
cryptoTestHelper.ensureCanDecrypt(sentEventIds, newBobSession, e2eRoomID, messagesText)
|
||||
|
||||
testHelper.signOutAndClose(newBobSession)
|
||||
}
|
||||
@ -368,7 +367,7 @@ class E2eeSanityTests : InstrumentedTest {
|
||||
// check that new bob can't currently decrypt
|
||||
Log.v("#E2E TEST", "check that new bob can't currently decrypt")
|
||||
|
||||
ensureCannotDecrypt(sentEventIds, newBobSession, e2eRoomID, null)
|
||||
cryptoTestHelper.ensureCannotDecrypt(sentEventIds, newBobSession, e2eRoomID, null)
|
||||
// newBobSession.cryptoService().getOutgoingRoomKeyRequests()
|
||||
// .firstOrNull {
|
||||
// it.sessionId ==
|
||||
@ -408,7 +407,7 @@ class E2eeSanityTests : InstrumentedTest {
|
||||
}
|
||||
}
|
||||
|
||||
ensureCannotDecrypt(sentEventIds, newBobSession, e2eRoomID, null)
|
||||
cryptoTestHelper.ensureCannotDecrypt(sentEventIds, newBobSession, e2eRoomID, null)
|
||||
|
||||
// Now mark new bob session as verified
|
||||
|
||||
@ -421,7 +420,7 @@ class E2eeSanityTests : InstrumentedTest {
|
||||
newBobSession.cryptoService().reRequestRoomKeyForEvent(event)
|
||||
}
|
||||
|
||||
ensureCanDecrypt(sentEventIds, newBobSession, e2eRoomID, messagesText)
|
||||
cryptoTestHelper.ensureCanDecrypt(sentEventIds, newBobSession, e2eRoomID, messagesText)
|
||||
|
||||
cryptoTestData.cleanUp(testHelper)
|
||||
testHelper.signOutAndClose(newBobSession)
|
||||
@ -467,7 +466,7 @@ class E2eeSanityTests : InstrumentedTest {
|
||||
|
||||
// check that new bob can't currently decrypt
|
||||
Log.v("#E2E TEST", "check that new bob can't currently decrypt")
|
||||
ensureCannotDecrypt(listOf(firstEventId), newBobSession, e2eRoomID, MXCryptoError.ErrorType.UNKNOWN_INBOUND_SESSION_ID)
|
||||
cryptoTestHelper.ensureCannotDecrypt(listOf(firstEventId), newBobSession, e2eRoomID, MXCryptoError.ErrorType.UNKNOWN_INBOUND_SESSION_ID)
|
||||
|
||||
// Now let alice send a new message. this time the new bob session will be able to decrypt
|
||||
var secondEventId: String
|
||||
@ -686,8 +685,13 @@ class E2eeSanityTests : InstrumentedTest {
|
||||
// wait for secret gossiping to happen
|
||||
testHelper.waitWithLatch { latch ->
|
||||
testHelper.retryPeriodicallyWithLatch(latch) {
|
||||
aliceNewSession.cryptoService().crossSigningService().allPrivateKeysKnown() &&
|
||||
aliceNewSession.cryptoService().keysBackupService().getKeyBackupRecoveryKeyInfo() != null
|
||||
aliceNewSession.cryptoService().crossSigningService().allPrivateKeysKnown()
|
||||
}
|
||||
}
|
||||
|
||||
testHelper.waitWithLatch { latch ->
|
||||
testHelper.retryPeriodicallyWithLatch(latch) {
|
||||
aliceNewSession.cryptoService().keysBackupService().getKeyBackupRecoveryKeyInfo() != null
|
||||
}
|
||||
}
|
||||
|
||||
@ -765,32 +769,6 @@ class E2eeSanityTests : InstrumentedTest {
|
||||
}
|
||||
}
|
||||
|
||||
private fun ensureCanDecrypt(sentEventIds: MutableList<String>, session: Session, e2eRoomID: String, messagesText: List<String>) {
|
||||
sentEventIds.forEachIndexed { index, sentEventId ->
|
||||
testHelper.waitWithLatch { latch ->
|
||||
testHelper.retryPeriodicallyWithLatch(latch) {
|
||||
val event = session.getRoom(e2eRoomID)!!.getTimelineEvent(sentEventId)!!.root
|
||||
testHelper.runBlockingTest {
|
||||
try {
|
||||
session.cryptoService().decryptEvent(event, "").let { result ->
|
||||
event.mxDecryptionResult = OlmDecryptionResult(
|
||||
payload = result.clearEvent,
|
||||
senderKey = result.senderCurve25519Key,
|
||||
keysClaimed = result.claimedEd25519Key?.let { mapOf("ed25519" to it) },
|
||||
forwardingCurve25519KeyChain = result.forwardingCurve25519KeyChain
|
||||
)
|
||||
}
|
||||
} catch (error: MXCryptoError) {
|
||||
// nop
|
||||
}
|
||||
}
|
||||
event.getClearType() == EventType.MESSAGE &&
|
||||
messagesText[index] == event.getClearContent()?.toModel<MessageContent>()?.body
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun ensureIsDecrypted(sentEventIds: List<String>, session: Session, e2eRoomID: String) {
|
||||
testHelper.waitWithLatch { latch ->
|
||||
sentEventIds.forEach { sentEventId ->
|
||||
@ -803,23 +781,4 @@ class E2eeSanityTests : InstrumentedTest {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun ensureCannotDecrypt(sentEventIds: List<String>, newBobSession: Session, e2eRoomID: String, expectedError: MXCryptoError.ErrorType?) {
|
||||
sentEventIds.forEach { sentEventId ->
|
||||
val event = newBobSession.getRoom(e2eRoomID)!!.getTimelineEvent(sentEventId)!!.root
|
||||
testHelper.runBlockingTest {
|
||||
try {
|
||||
newBobSession.cryptoService().decryptEvent(event, "")
|
||||
fail("Should not be able to decrypt event")
|
||||
} catch (error: MXCryptoError) {
|
||||
val errorType = (error as? MXCryptoError.Base)?.errorType
|
||||
if (expectedError == null) {
|
||||
Assert.assertNotNull(errorType)
|
||||
} else {
|
||||
assertEquals(expectedError, errorType, "Unexpected reason")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,45 +19,31 @@ package org.matrix.android.sdk.internal.crypto.gossiping
|
||||
import android.util.Log
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.filters.LargeTest
|
||||
import junit.framework.TestCase.assertEquals
|
||||
import junit.framework.TestCase.assertNotNull
|
||||
import junit.framework.TestCase.assertTrue
|
||||
import junit.framework.TestCase.fail
|
||||
import org.amshove.kluent.internal.assertEquals
|
||||
import org.junit.Assert
|
||||
import org.junit.Assert.assertNull
|
||||
import org.junit.FixMethodOrder
|
||||
import org.junit.Ignore
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.junit.runners.MethodSorters
|
||||
import org.matrix.android.sdk.InstrumentedTest
|
||||
import org.matrix.android.sdk.api.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.extensions.tryOrNull
|
||||
import org.matrix.android.sdk.api.session.crypto.crosssigning.DeviceTrustLevel
|
||||
import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysVersion
|
||||
import org.matrix.android.sdk.api.session.crypto.keysbackup.MegolmBackupCreationInfo
|
||||
import org.matrix.android.sdk.api.session.crypto.model.CryptoDeviceInfo
|
||||
import org.matrix.android.sdk.api.session.crypto.model.GossipingRequestState
|
||||
import org.matrix.android.sdk.api.session.crypto.model.MXUsersDevicesMap
|
||||
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
|
||||
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.api.session.events.model.content.EncryptedEventContent
|
||||
import org.matrix.android.sdk.api.session.events.model.content.WithHeldCode
|
||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||
import org.matrix.android.sdk.api.session.getRoom
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomDirectoryVisibility
|
||||
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageContent
|
||||
import org.matrix.android.sdk.api.session.room.timeline.getLastMessageContent
|
||||
import org.matrix.android.sdk.common.CommonTestHelper
|
||||
import org.matrix.android.sdk.common.CryptoTestHelper
|
||||
import org.matrix.android.sdk.common.SessionTestParams
|
||||
import org.matrix.android.sdk.common.TestConstants
|
||||
import kotlin.coroutines.Continuation
|
||||
import kotlin.coroutines.resume
|
||||
import org.matrix.android.sdk.internal.crypto.OutgoingRoomKeyRequestState
|
||||
import org.matrix.android.sdk.internal.crypto.RequestResult
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@FixMethodOrder(MethodSorters.JVM)
|
||||
@ -65,11 +51,12 @@ import kotlin.coroutines.resume
|
||||
class KeyShareTests : InstrumentedTest {
|
||||
|
||||
private val commonTestHelper = CommonTestHelper(context())
|
||||
private val cryptoTestHelper = CryptoTestHelper(commonTestHelper)
|
||||
|
||||
@Test
|
||||
@Ignore("This test will be ignored until it is fixed")
|
||||
fun test_DoNotSelfShareIfNotTrusted() {
|
||||
val aliceSession = commonTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
|
||||
Log.v("TEST", "=======> AliceSession 1 is ${aliceSession.sessionParams.deviceId}")
|
||||
|
||||
// Create an encrypted room and add a message
|
||||
val roomId = commonTestHelper.runBlockingTest {
|
||||
@ -84,11 +71,14 @@ class KeyShareTests : InstrumentedTest {
|
||||
assertNotNull(room)
|
||||
Thread.sleep(4_000)
|
||||
assertTrue(room?.isEncrypted() == true)
|
||||
val sentEventId = commonTestHelper.sendTextMessage(room!!, "My Message", 1).first().eventId
|
||||
val sentEvent = commonTestHelper.sendTextMessage(room!!, "My Message", 1).first()
|
||||
val sentEventId = sentEvent.eventId
|
||||
val sentEventText = sentEvent.getLastMessageContent()?.body
|
||||
|
||||
// Open a new sessionx
|
||||
|
||||
val aliceSession2 = commonTestHelper.logIntoAccount(aliceSession.myUserId, SessionTestParams(true))
|
||||
Log.v("TEST", "=======> AliceSession 2 is ${aliceSession2.sessionParams.deviceId}")
|
||||
|
||||
val roomSecondSessionPOV = aliceSession2.getRoom(roomId)
|
||||
|
||||
@ -139,17 +129,34 @@ class KeyShareTests : InstrumentedTest {
|
||||
commonTestHelper.waitWithLatch { latch ->
|
||||
commonTestHelper.retryPeriodicallyWithLatch(latch) {
|
||||
// DEBUG LOGS
|
||||
aliceSession.cryptoService().getIncomingRoomKeyRequests().let {
|
||||
Log.v("TEST", "Incoming request Session 1 (looking for $outGoingRequestId)")
|
||||
// aliceSession.cryptoService().getIncomingRoomKeyRequests().let {
|
||||
// Log.v("TEST", "Incoming request Session 1 (looking for $outGoingRequestId)")
|
||||
// Log.v("TEST", "=========================")
|
||||
// it.forEach { keyRequest ->
|
||||
// Log.v("TEST", "[ts${keyRequest.localCreationTimestamp}] requestId ${keyRequest.requestId}, for sessionId ${keyRequest.requestBody?.sessionId}")
|
||||
// }
|
||||
// Log.v("TEST", "=========================")
|
||||
// }
|
||||
|
||||
val incoming = aliceSession.cryptoService().getIncomingRoomKeyRequests().firstOrNull { it.requestId == outGoingRequestId }
|
||||
incoming != null
|
||||
}
|
||||
}
|
||||
|
||||
commonTestHelper.waitWithLatch { latch ->
|
||||
commonTestHelper.retryPeriodicallyWithLatch(latch) {
|
||||
// DEBUG LOGS
|
||||
aliceSession2.cryptoService().getOutgoingRoomKeyRequests().forEach { keyRequest ->
|
||||
Log.v("TEST", "=========================")
|
||||
it.forEach { keyRequest ->
|
||||
Log.v("TEST", "[ts${keyRequest.localCreationTimestamp}] requestId ${keyRequest.requestId}, for sessionId ${keyRequest.requestBody?.sessionId} is ${keyRequest.state}")
|
||||
}
|
||||
Log.v("TEST", "requestId ${keyRequest.requestId}, for sessionId ${keyRequest.requestBody?.sessionId}")
|
||||
Log.v("TEST", "replies -> ${keyRequest.results.joinToString { it.toString() }}")
|
||||
Log.v("TEST", "=========================")
|
||||
}
|
||||
|
||||
val incoming = aliceSession.cryptoService().getIncomingRoomKeyRequests().firstOrNull { it.requestId == outGoingRequestId }
|
||||
incoming?.state == GossipingRequestState.REJECTED
|
||||
val outgoing = aliceSession2.cryptoService().getOutgoingRoomKeyRequests().firstOrNull { it.requestId == outGoingRequestId }
|
||||
val reply = outgoing?.results?.firstOrNull { it.userId == aliceSession.myUserId && it.fromDevice == aliceSession.sessionParams.deviceId }
|
||||
val resultCode = (reply?.result as? RequestResult.Failure)?.code
|
||||
resultCode == WithHeldCode.UNAUTHORISED
|
||||
}
|
||||
}
|
||||
|
||||
@ -168,249 +175,279 @@ class KeyShareTests : InstrumentedTest {
|
||||
// Re request
|
||||
aliceSession2.cryptoService().reRequestRoomKeyForEvent(receivedEvent.root)
|
||||
|
||||
commonTestHelper.waitWithLatch { latch ->
|
||||
commonTestHelper.retryPeriodicallyWithLatch(latch) {
|
||||
aliceSession.cryptoService().getIncomingRoomKeyRequests().let {
|
||||
Log.v("TEST", "Incoming request Session 1")
|
||||
Log.v("TEST", "=========================")
|
||||
it.forEach {
|
||||
Log.v("TEST", "requestId ${it.requestId}, for sessionId ${it.requestBody?.sessionId} is ${it.state}")
|
||||
}
|
||||
Log.v("TEST", "=========================")
|
||||
|
||||
it.any { it.requestBody?.sessionId == eventMegolmSessionId && it.state == GossipingRequestState.ACCEPTED }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Thread.sleep(6_000)
|
||||
commonTestHelper.waitWithLatch { latch ->
|
||||
commonTestHelper.retryPeriodicallyWithLatch(latch) {
|
||||
// It should have been deleted from store
|
||||
val outgoingRoomKeyRequests = aliceSession2.cryptoService().getOutgoingRoomKeyRequests()
|
||||
outgoingRoomKeyRequests.isEmpty()
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
commonTestHelper.runBlockingTest {
|
||||
aliceSession2.cryptoService().decryptEvent(receivedEvent.root, "foo")
|
||||
}
|
||||
} catch (failure: Throwable) {
|
||||
fail("should have been able to decrypt")
|
||||
}
|
||||
cryptoTestHelper.ensureCanDecrypt(listOf(receivedEvent.eventId), aliceSession2, roomId, listOf(sentEventText ?: ""))
|
||||
|
||||
commonTestHelper.signOutAndClose(aliceSession)
|
||||
commonTestHelper.signOutAndClose(aliceSession2)
|
||||
}
|
||||
|
||||
// See E2ESanityTest for a test regarding secret sharing
|
||||
|
||||
/**
|
||||
* Test that the sender of a message accepts to re-share to another user
|
||||
* if the key was originally shared with him
|
||||
*/
|
||||
@Test
|
||||
@Ignore("This test will be ignored until it is fixed")
|
||||
fun test_ShareSSSSSecret() {
|
||||
val aliceSession1 = commonTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
|
||||
fun test_reShareIfWasIntendedToBeShared() {
|
||||
val testData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(true)
|
||||
val aliceSession = testData.firstSession
|
||||
val roomFromAlice = aliceSession.getRoom(testData.roomId)!!
|
||||
val bobSession = testData.secondSession!!
|
||||
|
||||
commonTestHelper.doSync<Unit> {
|
||||
aliceSession1.cryptoService().crossSigningService()
|
||||
.initializeCrossSigning(
|
||||
object : UserInteractiveAuthInterceptor {
|
||||
override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) {
|
||||
promise.resume(
|
||||
UserPasswordAuth(
|
||||
user = aliceSession1.myUserId,
|
||||
password = TestConstants.PASSWORD
|
||||
)
|
||||
)
|
||||
}
|
||||
}, it)
|
||||
}
|
||||
val sentEvent = commonTestHelper.sendTextMessage(roomFromAlice, "Hello", 1).first()
|
||||
val sentEventMegolmSession = sentEvent.root.content.toModel<EncryptedEventContent>()!!.sessionId!!
|
||||
|
||||
// Also bootstrap keybackup on first session
|
||||
val creationInfo = commonTestHelper.doSync<MegolmBackupCreationInfo> {
|
||||
aliceSession1.cryptoService().keysBackupService().prepareKeysBackupVersion(null, null, it)
|
||||
}
|
||||
val version = commonTestHelper.doSync<KeysVersion> {
|
||||
aliceSession1.cryptoService().keysBackupService().createKeysBackupVersion(creationInfo, it)
|
||||
}
|
||||
// Save it for gossiping
|
||||
aliceSession1.cryptoService().keysBackupService().saveBackupRecoveryKey(creationInfo.recoveryKey, version = version.version)
|
||||
// bob should be able to decrypt
|
||||
cryptoTestHelper.ensureCanDecrypt(listOf(sentEvent.eventId), bobSession, testData.roomId, listOf(sentEvent.getLastMessageContent()?.body ?: ""))
|
||||
|
||||
val aliceSession2 = commonTestHelper.logIntoAccount(aliceSession1.myUserId, SessionTestParams(true))
|
||||
|
||||
val aliceVerificationService1 = aliceSession1.cryptoService().verificationService()
|
||||
val aliceVerificationService2 = aliceSession2.cryptoService().verificationService()
|
||||
|
||||
// force keys download
|
||||
commonTestHelper.doSync<MXUsersDevicesMap<CryptoDeviceInfo>> {
|
||||
aliceSession1.cryptoService().downloadKeys(listOf(aliceSession1.myUserId), true, it)
|
||||
}
|
||||
commonTestHelper.doSync<MXUsersDevicesMap<CryptoDeviceInfo>> {
|
||||
aliceSession2.cryptoService().downloadKeys(listOf(aliceSession2.myUserId), true, it)
|
||||
}
|
||||
|
||||
var session1ShortCode: String? = null
|
||||
var session2ShortCode: String? = null
|
||||
|
||||
aliceVerificationService1.addListener(object : VerificationService.Listener {
|
||||
override fun transactionUpdated(tx: VerificationTransaction) {
|
||||
Log.d("#TEST", "AA: tx incoming?:${tx.isIncoming} state ${tx.state}")
|
||||
if (tx is SasVerificationTransaction) {
|
||||
if (tx.state == VerificationTxState.OnStarted) {
|
||||
(tx as IncomingSasVerificationTransaction).performAccept()
|
||||
}
|
||||
if (tx.state == VerificationTxState.ShortCodeReady) {
|
||||
session1ShortCode = tx.getDecimalCodeRepresentation()
|
||||
Thread.sleep(500)
|
||||
tx.userHasVerifiedShortCode()
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
aliceVerificationService2.addListener(object : VerificationService.Listener {
|
||||
override fun transactionUpdated(tx: VerificationTransaction) {
|
||||
Log.d("#TEST", "BB: tx incoming?:${tx.isIncoming} state ${tx.state}")
|
||||
if (tx is SasVerificationTransaction) {
|
||||
if (tx.state == VerificationTxState.ShortCodeReady) {
|
||||
session2ShortCode = tx.getDecimalCodeRepresentation()
|
||||
Thread.sleep(500)
|
||||
tx.userHasVerifiedShortCode()
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
val txId = "m.testVerif12"
|
||||
aliceVerificationService2.beginKeyVerification(VerificationMethod.SAS, aliceSession1.myUserId, aliceSession1.sessionParams.deviceId
|
||||
?: "", txId)
|
||||
// Let's try to request any how.
|
||||
// As it was share previously alice should accept to reshare
|
||||
bobSession.cryptoService().reRequestRoomKeyForEvent(sentEvent.root)
|
||||
|
||||
commonTestHelper.waitWithLatch { latch ->
|
||||
commonTestHelper.retryPeriodicallyWithLatch(latch) {
|
||||
aliceSession1.cryptoService().getDeviceInfo(aliceSession1.myUserId, aliceSession2.sessionParams.deviceId ?: "")?.isVerified == true
|
||||
val outgoing = bobSession.cryptoService().getOutgoingRoomKeyRequests().firstOrNull { it.sessionId == sentEventMegolmSession }
|
||||
val aliceReply = outgoing?.results?.firstOrNull { it.userId == aliceSession.myUserId && it.fromDevice == aliceSession.sessionParams.deviceId }
|
||||
aliceReply != null && aliceReply.result is RequestResult.Success
|
||||
}
|
||||
}
|
||||
|
||||
assertNotNull(session1ShortCode)
|
||||
Log.d("#TEST", "session1ShortCode: $session1ShortCode")
|
||||
assertNotNull(session2ShortCode)
|
||||
Log.d("#TEST", "session2ShortCode: $session2ShortCode")
|
||||
assertEquals(session1ShortCode, session2ShortCode)
|
||||
|
||||
// SSK and USK private keys should have been shared
|
||||
|
||||
commonTestHelper.waitWithLatch(60_000) { latch ->
|
||||
commonTestHelper.retryPeriodicallyWithLatch(latch) {
|
||||
Log.d("#TEST", "CAN XS :${aliceSession2.cryptoService().crossSigningService().getMyCrossSigningKeys()}")
|
||||
aliceSession2.cryptoService().crossSigningService().canCrossSign()
|
||||
}
|
||||
}
|
||||
|
||||
// Test that key backup key has been shared to
|
||||
commonTestHelper.waitWithLatch(60_000) { latch ->
|
||||
val keysBackupService = aliceSession2.cryptoService().keysBackupService()
|
||||
commonTestHelper.retryPeriodicallyWithLatch(latch) {
|
||||
Log.d("#TEST", "Recovery :${keysBackupService.getKeyBackupRecoveryKeyInfo()?.recoveryKey}")
|
||||
keysBackupService.getKeyBackupRecoveryKeyInfo()?.recoveryKey == creationInfo.recoveryKey
|
||||
}
|
||||
}
|
||||
|
||||
commonTestHelper.signOutAndClose(aliceSession1)
|
||||
commonTestHelper.signOutAndClose(aliceSession2)
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that our own devices accept to reshare to unverified device if it was shared initialy
|
||||
* if the key was originally shared with him
|
||||
*/
|
||||
@Test
|
||||
@Ignore("This test will be ignored until it is fixed")
|
||||
fun test_ImproperKeyShareBug() {
|
||||
val aliceSession = commonTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
|
||||
fun test_reShareToUnverifiedIfWasIntendedToBeShared() {
|
||||
val testData = cryptoTestHelper.doE2ETestWithAliceInARoom(true)
|
||||
val aliceSession = testData.firstSession
|
||||
val roomFromAlice = aliceSession.getRoom(testData.roomId)!!
|
||||
|
||||
commonTestHelper.doSync<Unit> {
|
||||
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
|
||||
)
|
||||
)
|
||||
}
|
||||
}, it)
|
||||
val aliceNewSession = commonTestHelper.logIntoAccount(aliceSession.myUserId, SessionTestParams(true))
|
||||
|
||||
// we wait for alice first session to be aware of that session?
|
||||
commonTestHelper.waitWithLatch { latch ->
|
||||
commonTestHelper.retryPeriodicallyWithLatch(latch) {
|
||||
val newSession = aliceSession.cryptoService().getUserDevices(aliceSession.myUserId)
|
||||
.firstOrNull { it.deviceId == aliceNewSession.sessionParams.deviceId }
|
||||
newSession != null
|
||||
}
|
||||
}
|
||||
val sentEvent = commonTestHelper.sendTextMessage(roomFromAlice, "Hello", 1).first()
|
||||
val sentEventMegolmSession = sentEvent.root.content.toModel<EncryptedEventContent>()!!.sessionId!!
|
||||
|
||||
// Create an encrypted room and send a couple of messages
|
||||
val roomId = commonTestHelper.runBlockingTest {
|
||||
aliceSession.roomService().createRoom(
|
||||
CreateRoomParams().apply {
|
||||
visibility = RoomDirectoryVisibility.PRIVATE
|
||||
enableEncryption()
|
||||
}
|
||||
)
|
||||
// Let's try to request any how.
|
||||
// As it was share previously alice should accept to reshare
|
||||
aliceNewSession.cryptoService().reRequestRoomKeyForEvent(sentEvent.root)
|
||||
|
||||
commonTestHelper.waitWithLatch { latch ->
|
||||
commonTestHelper.retryPeriodicallyWithLatch(latch) {
|
||||
val outgoing = aliceNewSession.cryptoService().getOutgoingRoomKeyRequests().firstOrNull { it.sessionId == sentEventMegolmSession }
|
||||
val ownDeviceReply = outgoing?.results?.firstOrNull { it.userId == aliceSession.myUserId && it.fromDevice == aliceSession.sessionParams.deviceId }
|
||||
ownDeviceReply != null && ownDeviceReply.result is RequestResult.Success
|
||||
}
|
||||
}
|
||||
val roomAlicePov = aliceSession.getRoom(roomId)
|
||||
assertNotNull(roomAlicePov)
|
||||
Thread.sleep(1_000)
|
||||
assertTrue(roomAlicePov?.isEncrypted() == true)
|
||||
val secondEventId = commonTestHelper.sendTextMessage(roomAlicePov!!, "Message", 3)[1].eventId
|
||||
}
|
||||
|
||||
// Create bob session
|
||||
/**
|
||||
* Tests that keys reshared with own verified session are done from the earliest known index
|
||||
*/
|
||||
@Test
|
||||
fun test_reShareFromTheEarliestKnownIndexWithOwnVerifiedSession() {
|
||||
val testData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(true)
|
||||
val aliceSession = testData.firstSession
|
||||
val bobSession = testData.secondSession!!
|
||||
val roomFromBob = bobSession.getRoom(testData.roomId)!!
|
||||
|
||||
val bobSession = commonTestHelper.createAccount(TestConstants.USER_BOB, SessionTestParams(true))
|
||||
commonTestHelper.doSync<Unit> {
|
||||
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
|
||||
)
|
||||
)
|
||||
}
|
||||
}, it)
|
||||
}
|
||||
val sentEvents = commonTestHelper.sendTextMessage(roomFromBob, "Hello", 3)
|
||||
val sentEventMegolmSession = sentEvents.first().root.content.toModel<EncryptedEventContent>()!!.sessionId!!
|
||||
|
||||
// Let alice invite bob
|
||||
commonTestHelper.runBlockingTest {
|
||||
roomAlicePov.invite(bobSession.myUserId, null)
|
||||
}
|
||||
// Let alice now add a new session
|
||||
val aliceNewSession = commonTestHelper.logIntoAccount(aliceSession.myUserId, SessionTestParams(true))
|
||||
|
||||
commonTestHelper.runBlockingTest {
|
||||
bobSession.roomService().joinRoom(roomAlicePov.roomId, null, emptyList())
|
||||
}
|
||||
|
||||
// we want to discard alice outbound session
|
||||
aliceSession.cryptoService().discardOutboundSession(roomAlicePov.roomId)
|
||||
|
||||
// and now resend a new message to reset index to 0
|
||||
commonTestHelper.sendTextMessage(roomAlicePov, "After", 1)
|
||||
|
||||
val roomRoomBobPov = aliceSession.getRoom(roomId)
|
||||
val beforeJoin = roomRoomBobPov!!.getTimelineEvent(secondEventId)
|
||||
|
||||
var dRes = tryOrNull {
|
||||
commonTestHelper.runBlockingTest {
|
||||
bobSession.cryptoService().decryptEvent(beforeJoin!!.root, "")
|
||||
// we wait bob first session to be aware of that session?
|
||||
commonTestHelper.waitWithLatch { latch ->
|
||||
commonTestHelper.retryPeriodicallyWithLatch(latch) {
|
||||
val newSession = bobSession.cryptoService().getUserDevices(aliceSession.myUserId)
|
||||
.firstOrNull { it.deviceId == aliceNewSession.sessionParams.deviceId }
|
||||
newSession != null
|
||||
}
|
||||
}
|
||||
|
||||
assert(dRes == null)
|
||||
val newEvent = commonTestHelper.sendTextMessage(roomFromBob, "The New", 1).first()
|
||||
val newEventId = newEvent.eventId
|
||||
val newEventText = newEvent.getLastMessageContent()!!.body
|
||||
|
||||
// Try to re-ask the keys
|
||||
// alice should be able to decrypt the new one
|
||||
cryptoTestHelper.ensureCanDecrypt(listOf(newEventId), aliceNewSession, testData.roomId, listOf(newEventText))
|
||||
// but not the first one!
|
||||
cryptoTestHelper.ensureCannotDecrypt(sentEvents.map { it.eventId }, aliceNewSession, testData.roomId)
|
||||
|
||||
bobSession.cryptoService().reRequestRoomKeyForEvent(beforeJoin!!.root)
|
||||
// All should be using the same session id
|
||||
sentEvents.forEach {
|
||||
assertEquals(sentEventMegolmSession, it.root.content.toModel<EncryptedEventContent>()!!.sessionId)
|
||||
}
|
||||
assertEquals(sentEventMegolmSession, newEvent.root.content.toModel<EncryptedEventContent>()!!.sessionId)
|
||||
|
||||
Thread.sleep(3_000)
|
||||
// Request a first time, bob and alice should reply with unauthorized
|
||||
aliceNewSession.cryptoService().reRequestRoomKeyForEvent(newEvent.root)
|
||||
|
||||
// With the bug the first session would have improperly reshare that key :/
|
||||
dRes = tryOrNull {
|
||||
commonTestHelper.runBlockingTest {
|
||||
bobSession.cryptoService().decryptEvent(beforeJoin.root, "")
|
||||
commonTestHelper.waitWithLatch { latch ->
|
||||
commonTestHelper.retryPeriodicallyWithLatch(latch) {
|
||||
val outgoing = aliceNewSession.cryptoService().getOutgoingRoomKeyRequests().firstOrNull { it.sessionId == sentEventMegolmSession }
|
||||
val ownDeviceReply = outgoing?.results
|
||||
?.firstOrNull { it.userId == aliceSession.myUserId && it.fromDevice == aliceSession.sessionParams.deviceId }
|
||||
val result = ownDeviceReply?.result
|
||||
Log.v("TEST", "own device result is $result")
|
||||
result != null && result is RequestResult.Failure && result.code == WithHeldCode.UNVERIFIED
|
||||
}
|
||||
}
|
||||
Log.d("#TEST", "KS: sgould not decrypt that ${beforeJoin.root.getClearContent().toModel<MessageContent>()?.body}")
|
||||
assert(dRes?.clearEvent == null)
|
||||
|
||||
commonTestHelper.waitWithLatch { latch ->
|
||||
commonTestHelper.retryPeriodicallyWithLatch(latch) {
|
||||
val outgoing = aliceNewSession.cryptoService().getOutgoingRoomKeyRequests().firstOrNull { it.sessionId == sentEventMegolmSession }
|
||||
val bobDeviceReply = outgoing?.results
|
||||
?.firstOrNull { it.userId == bobSession.myUserId && it.fromDevice == bobSession.sessionParams.deviceId }
|
||||
val result = bobDeviceReply?.result
|
||||
result != null && result is RequestResult.Success && result.chainIndex > 0
|
||||
}
|
||||
}
|
||||
|
||||
// it's a success but still can't decrypt first message
|
||||
cryptoTestHelper.ensureCannotDecrypt(sentEvents.map { it.eventId }, aliceNewSession, testData.roomId)
|
||||
|
||||
// Mark the new session as verified
|
||||
aliceSession.cryptoService()
|
||||
.verificationService()
|
||||
.markedLocallyAsManuallyVerified(aliceNewSession.myUserId, aliceNewSession.sessionParams.deviceId!!)
|
||||
|
||||
// Let's now try to request
|
||||
aliceNewSession.cryptoService().reRequestRoomKeyForEvent(newEvent.root)
|
||||
|
||||
commonTestHelper.waitWithLatch { latch ->
|
||||
commonTestHelper.retryPeriodicallyWithLatch(latch) {
|
||||
// DEBUG LOGS
|
||||
aliceNewSession.cryptoService().getOutgoingRoomKeyRequests().forEach { keyRequest ->
|
||||
Log.v("TEST", "=========================")
|
||||
Log.v("TEST", "requestId ${keyRequest.requestId}, for sessionId ${keyRequest.requestBody?.sessionId}")
|
||||
Log.v("TEST", "replies -> ${keyRequest.results.joinToString { it.toString() }}")
|
||||
Log.v("TEST", "=========================")
|
||||
}
|
||||
val outgoing = aliceNewSession.cryptoService().getOutgoingRoomKeyRequests().firstOrNull { it.sessionId == sentEventMegolmSession }
|
||||
val ownDeviceReply = outgoing?.results?.firstOrNull { it.userId == aliceSession.myUserId && it.fromDevice == aliceSession.sessionParams.deviceId }
|
||||
val result = ownDeviceReply?.result
|
||||
result != null && result is RequestResult.Success && result.chainIndex == 0
|
||||
}
|
||||
}
|
||||
|
||||
// now the new session should be able to decrypt all!
|
||||
cryptoTestHelper.ensureCanDecrypt(
|
||||
sentEvents.map { it.eventId },
|
||||
aliceNewSession,
|
||||
testData.roomId,
|
||||
sentEvents.map { it.getLastMessageContent()!!.body }
|
||||
)
|
||||
|
||||
// Additional test, can we check that bob replied successfully but with a ratcheted key
|
||||
commonTestHelper.waitWithLatch { latch ->
|
||||
commonTestHelper.retryPeriodicallyWithLatch(latch) {
|
||||
val outgoing = aliceNewSession.cryptoService().getOutgoingRoomKeyRequests().firstOrNull { it.sessionId == sentEventMegolmSession }
|
||||
val bobReply = outgoing?.results?.firstOrNull { it.userId == bobSession.myUserId }
|
||||
val result = bobReply?.result
|
||||
result != null && result is RequestResult.Success && result.chainIndex == 3
|
||||
}
|
||||
}
|
||||
|
||||
commonTestHelper.signOutAndClose(aliceNewSession)
|
||||
commonTestHelper.signOutAndClose(aliceSession)
|
||||
commonTestHelper.signOutAndClose(bobSession)
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that we don't cancel a request to early on first forward if the index is not good enough
|
||||
*/
|
||||
@Test
|
||||
fun test_dontCancelToEarly() {
|
||||
val testData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(true)
|
||||
val aliceSession = testData.firstSession
|
||||
val bobSession = testData.secondSession!!
|
||||
val roomFromBob = bobSession.getRoom(testData.roomId)!!
|
||||
|
||||
val sentEvents = commonTestHelper.sendTextMessage(roomFromBob, "Hello", 3)
|
||||
val sentEventMegolmSession = sentEvents.first().root.content.toModel<EncryptedEventContent>()!!.sessionId!!
|
||||
|
||||
// Let alice now add a new session
|
||||
val aliceNewSession = commonTestHelper.logIntoAccount(aliceSession.myUserId, SessionTestParams(true))
|
||||
|
||||
// we wait bob first session to be aware of that session?
|
||||
commonTestHelper.waitWithLatch { latch ->
|
||||
commonTestHelper.retryPeriodicallyWithLatch(latch) {
|
||||
val newSession = bobSession.cryptoService().getUserDevices(aliceSession.myUserId)
|
||||
.firstOrNull { it.deviceId == aliceNewSession.sessionParams.deviceId }
|
||||
newSession != null
|
||||
}
|
||||
}
|
||||
|
||||
val newEvent = commonTestHelper.sendTextMessage(roomFromBob, "The New", 1).first()
|
||||
val newEventId = newEvent.eventId
|
||||
val newEventText = newEvent.getLastMessageContent()!!.body
|
||||
|
||||
// alice should be able to decrypt the new one
|
||||
cryptoTestHelper.ensureCanDecrypt(listOf(newEventId), aliceNewSession, testData.roomId, listOf(newEventText))
|
||||
// but not the first one!
|
||||
cryptoTestHelper.ensureCannotDecrypt(sentEvents.map { it.eventId }, aliceNewSession, testData.roomId)
|
||||
|
||||
// All should be using the same session id
|
||||
sentEvents.forEach {
|
||||
assertEquals(sentEventMegolmSession, it.root.content.toModel<EncryptedEventContent>()!!.sessionId)
|
||||
}
|
||||
assertEquals(sentEventMegolmSession, newEvent.root.content.toModel<EncryptedEventContent>()!!.sessionId)
|
||||
|
||||
// Mark the new session as verified
|
||||
aliceSession.cryptoService()
|
||||
.verificationService()
|
||||
.markedLocallyAsManuallyVerified(aliceNewSession.myUserId, aliceNewSession.sessionParams.deviceId!!)
|
||||
|
||||
// /!\ Stop initial alice session syncing so that it can't reply
|
||||
aliceSession.stopSync()
|
||||
|
||||
// Let's now try to request
|
||||
aliceNewSession.cryptoService().reRequestRoomKeyForEvent(sentEvents.first().root)
|
||||
|
||||
// Should get a reply from bob and not from alice
|
||||
commonTestHelper.waitWithLatch { latch ->
|
||||
commonTestHelper.retryPeriodicallyWithLatch(latch) {
|
||||
val outgoing = aliceNewSession.cryptoService().getOutgoingRoomKeyRequests().firstOrNull { it.sessionId == sentEventMegolmSession }
|
||||
val bobReply = outgoing?.results?.firstOrNull { it.userId == bobSession.myUserId }
|
||||
val result = bobReply?.result
|
||||
result != null && result is RequestResult.Success && result.chainIndex == 3
|
||||
}
|
||||
}
|
||||
|
||||
val outgoingReq = aliceNewSession.cryptoService().getOutgoingRoomKeyRequests().firstOrNull { it.sessionId == sentEventMegolmSession }
|
||||
|
||||
assertNull("We should not have a reply from first session", outgoingReq!!.results.firstOrNull { it.fromDevice == aliceSession.sessionParams.deviceId })
|
||||
assertEquals("The request should not be canceled", OutgoingRoomKeyRequestState.SENT, outgoingReq.state)
|
||||
|
||||
// let's wake up alice
|
||||
aliceSession.startSync(true)
|
||||
|
||||
// We should now get a reply from first session
|
||||
commonTestHelper.waitWithLatch { latch ->
|
||||
commonTestHelper.retryPeriodicallyWithLatch(latch) {
|
||||
val outgoing = aliceNewSession.cryptoService().getOutgoingRoomKeyRequests().firstOrNull { it.sessionId == sentEventMegolmSession }
|
||||
val ownDeviceReply = outgoing?.results?.firstOrNull { it.userId == aliceSession.myUserId && it.fromDevice == aliceSession.sessionParams.deviceId }
|
||||
val result = ownDeviceReply?.result
|
||||
result != null && result is RequestResult.Success && result.chainIndex == 0
|
||||
}
|
||||
}
|
||||
|
||||
// It should be in sent then cancel
|
||||
val outgoing = aliceNewSession.cryptoService().getOutgoingRoomKeyRequests().firstOrNull { it.sessionId == sentEventMegolmSession }
|
||||
assertEquals("The request should be canceled", OutgoingRoomKeyRequestState.SENT_THEN_CANCELED, outgoing!!.state)
|
||||
|
||||
commonTestHelper.signOutAndClose(aliceNewSession)
|
||||
commonTestHelper.signOutAndClose(aliceSession)
|
||||
commonTestHelper.signOutAndClose(bobSession)
|
||||
}
|
||||
}
|
||||
|
@ -31,5 +31,11 @@ data class MXCryptoConfig constructor(
|
||||
* If set to false, the request will be forwarded to the application layer; in this
|
||||
* case the application can decide to prompt the user.
|
||||
*/
|
||||
val discardRoomKeyRequestsFromUntrustedDevices: Boolean = true
|
||||
val discardRoomKeyRequestsFromUntrustedDevices: Boolean = true,
|
||||
|
||||
/**
|
||||
* Currently megolm keys are requested to the sender device and to all of our devices.
|
||||
* You can limit request only to your sessions by turning this setting to `true`
|
||||
*/
|
||||
val limitRoomKeyRequestsToMyDevices: Boolean = false
|
||||
)
|
||||
|
@ -146,6 +146,12 @@ interface CryptoService {
|
||||
fun getIncomingRoomKeyRequests(): List<IncomingRoomKeyRequest>
|
||||
fun getIncomingRoomKeyRequestsPaged(): LiveData<PagedList<IncomingRoomKeyRequest>>
|
||||
|
||||
/**
|
||||
* Can be called by the app layer to accept a request manually
|
||||
* Use carefully as it is prone to social attacks
|
||||
*/
|
||||
suspend fun manuallyAcceptRoomKeyRequest(request: IncomingRoomKeyRequest)
|
||||
|
||||
fun getGossipingEventsTrail(): LiveData<PagedList<AuditTrail>>
|
||||
fun getGossipingEvents(): List<AuditTrail>
|
||||
|
||||
|
@ -16,9 +16,8 @@
|
||||
|
||||
package org.matrix.android.sdk.api.session.crypto.keyshare
|
||||
|
||||
import org.matrix.android.sdk.api.session.crypto.model.IncomingRequestCancellation
|
||||
import org.matrix.android.sdk.api.session.crypto.model.IncomingRoomKeyRequest
|
||||
import org.matrix.android.sdk.api.session.crypto.model.IncomingSecretShareRequest
|
||||
import org.matrix.android.sdk.api.session.crypto.model.SecretShareRequest
|
||||
|
||||
/**
|
||||
* Room keys events listener
|
||||
@ -35,12 +34,12 @@ interface GossipingRequestListener {
|
||||
* Returns the secret value to be shared
|
||||
* @return true if is handled
|
||||
*/
|
||||
fun onSecretShareRequest(request: IncomingSecretShareRequest): Boolean
|
||||
fun onSecretShareRequest(request: SecretShareRequest): Boolean
|
||||
|
||||
/**
|
||||
* A room key request cancellation has been received.
|
||||
*
|
||||
* @param request the cancellation request
|
||||
*/
|
||||
fun onRoomKeyRequestCancellation(request: IncomingRequestCancellation)
|
||||
fun onRequestCancelled(requestId: IncomingRoomKeyRequest)
|
||||
}
|
||||
|
@ -1,63 +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.model
|
||||
|
||||
import org.matrix.android.sdk.api.session.events.model.Event
|
||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||
import org.matrix.android.sdk.internal.crypto.IncomingShareRequestCommon
|
||||
import org.matrix.android.sdk.internal.crypto.model.rest.ShareRequestCancellation
|
||||
|
||||
/**
|
||||
* IncomingRequestCancellation describes the incoming room key cancellation.
|
||||
*/
|
||||
data class IncomingRequestCancellation(
|
||||
/**
|
||||
* The user id
|
||||
*/
|
||||
override val userId: String? = null,
|
||||
|
||||
/**
|
||||
* The device id
|
||||
*/
|
||||
override val deviceId: String? = null,
|
||||
|
||||
/**
|
||||
* The request id
|
||||
*/
|
||||
override val requestId: String? = null,
|
||||
override val localCreationTimestamp: Long?
|
||||
) : IncomingShareRequestCommon {
|
||||
companion object {
|
||||
/**
|
||||
* Factory
|
||||
*
|
||||
* @param event the event
|
||||
*/
|
||||
fun fromEvent(event: Event): IncomingRequestCancellation? {
|
||||
return event.getClearContent()
|
||||
.toModel<ShareRequestCancellation>()
|
||||
?.let {
|
||||
IncomingRequestCancellation(
|
||||
userId = event.senderId,
|
||||
deviceId = it.requestingDeviceId,
|
||||
requestId = it.requestId,
|
||||
localCreationTimestamp = event.ageLocalTs ?: System.currentTimeMillis()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -16,9 +16,9 @@
|
||||
|
||||
package org.matrix.android.sdk.api.session.crypto.model
|
||||
|
||||
import org.matrix.android.sdk.api.session.events.model.Event
|
||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||
import org.matrix.android.sdk.internal.crypto.IncomingShareRequestCommon
|
||||
import org.matrix.android.sdk.internal.crypto.model.AuditTrail
|
||||
import org.matrix.android.sdk.internal.crypto.model.IncomingKeyRequestInfo
|
||||
import org.matrix.android.sdk.internal.crypto.model.TrailType
|
||||
|
||||
/**
|
||||
* IncomingRoomKeyRequest class defines the incoming room keys request.
|
||||
@ -27,56 +27,61 @@ data class IncomingRoomKeyRequest(
|
||||
/**
|
||||
* The user id
|
||||
*/
|
||||
override val userId: String? = null,
|
||||
val userId: String? = null,
|
||||
|
||||
/**
|
||||
* The device id
|
||||
*/
|
||||
override val deviceId: String? = null,
|
||||
val deviceId: String? = null,
|
||||
|
||||
/**
|
||||
* The request id
|
||||
*/
|
||||
override val requestId: String? = null,
|
||||
val requestId: String? = null,
|
||||
|
||||
/**
|
||||
* The request body
|
||||
*/
|
||||
val requestBody: RoomKeyRequestBody? = null,
|
||||
|
||||
val state: GossipingRequestState = GossipingRequestState.NONE,
|
||||
|
||||
/**
|
||||
* The runnable to call to accept to share the keys
|
||||
*/
|
||||
@Transient
|
||||
var share: Runnable? = null,
|
||||
|
||||
/**
|
||||
* The runnable to call to ignore the key share request.
|
||||
*/
|
||||
@Transient
|
||||
var ignore: Runnable? = null,
|
||||
override val localCreationTimestamp: Long?
|
||||
) : IncomingShareRequestCommon {
|
||||
val localCreationTimestamp: Long?
|
||||
) {
|
||||
companion object {
|
||||
/**
|
||||
* Factory
|
||||
*
|
||||
* @param event the event
|
||||
*/
|
||||
fun fromEvent(event: Event): IncomingRoomKeyRequest? {
|
||||
return event.getClearContent()
|
||||
.toModel<RoomKeyShareRequest>()
|
||||
fun fromEvent(trail: AuditTrail): IncomingRoomKeyRequest? {
|
||||
return trail
|
||||
.takeIf { it.type == TrailType.IncomingKeyRequest }
|
||||
?.let {
|
||||
it.info as? IncomingKeyRequestInfo
|
||||
}
|
||||
?.let {
|
||||
IncomingRoomKeyRequest(
|
||||
userId = event.senderId,
|
||||
deviceId = it.requestingDeviceId,
|
||||
userId = it.userId,
|
||||
deviceId = it.deviceId,
|
||||
requestId = it.requestId,
|
||||
requestBody = it.body ?: RoomKeyRequestBody(),
|
||||
localCreationTimestamp = event.ageLocalTs ?: System.currentTimeMillis()
|
||||
requestBody = RoomKeyRequestBody(
|
||||
algorithm = it.alg,
|
||||
roomId = it.roomId,
|
||||
senderKey = it.senderKey,
|
||||
sessionId = it.sessionId
|
||||
),
|
||||
localCreationTimestamp = trail.ageLocalTs
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun fromRestRequest(senderId: String, request: RoomKeyShareRequest): IncomingRoomKeyRequest? {
|
||||
return IncomingRoomKeyRequest(
|
||||
userId = senderId,
|
||||
deviceId = request.requestingDeviceId,
|
||||
requestId = request.requestId,
|
||||
requestBody = request.body,
|
||||
localCreationTimestamp = System.currentTimeMillis()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,82 +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.model
|
||||
|
||||
import org.matrix.android.sdk.api.session.events.model.Event
|
||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||
import org.matrix.android.sdk.internal.crypto.IncomingShareRequestCommon
|
||||
|
||||
/**
|
||||
* IncomingSecretShareRequest class defines the incoming secret keys request.
|
||||
*/
|
||||
data class IncomingSecretShareRequest(
|
||||
/**
|
||||
* The user id
|
||||
*/
|
||||
override val userId: String? = null,
|
||||
|
||||
/**
|
||||
* The device id
|
||||
*/
|
||||
override val deviceId: String? = null,
|
||||
|
||||
/**
|
||||
* The request id
|
||||
*/
|
||||
override val requestId: String? = null,
|
||||
|
||||
/**
|
||||
* The request body
|
||||
*/
|
||||
val secretName: String? = null,
|
||||
|
||||
/**
|
||||
* The runnable to call to accept to share the keys
|
||||
*/
|
||||
@Transient
|
||||
var share: ((String) -> Unit)? = null,
|
||||
|
||||
/**
|
||||
* The runnable to call to ignore the key share request.
|
||||
*/
|
||||
@Transient
|
||||
var ignore: Runnable? = null,
|
||||
|
||||
override val localCreationTimestamp: Long?
|
||||
|
||||
) : IncomingShareRequestCommon {
|
||||
companion object {
|
||||
/**
|
||||
* Factory
|
||||
*
|
||||
* @param event the event
|
||||
*/
|
||||
fun fromEvent(event: Event): IncomingSecretShareRequest? {
|
||||
return event.getClearContent()
|
||||
.toModel<SecretShareRequest>()
|
||||
?.let {
|
||||
IncomingSecretShareRequest(
|
||||
userId = event.senderId,
|
||||
deviceId = it.requestingDeviceId,
|
||||
requestId = it.requestId,
|
||||
secretName = it.secretName,
|
||||
localCreationTimestamp = event.ageLocalTs ?: System.currentTimeMillis()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -81,6 +81,13 @@ import org.matrix.android.sdk.internal.crypto.crosssigning.DefaultCrossSigningSe
|
||||
import org.matrix.android.sdk.internal.crypto.keysbackup.DefaultKeysBackupService
|
||||
import org.matrix.android.sdk.internal.crypto.model.AuditTrail
|
||||
import org.matrix.android.sdk.internal.crypto.model.MXKey.Companion.KEY_SIGNED_CURVE_25519_TYPE
|
||||
import org.matrix.android.sdk.internal.crypto.model.MXUsersDevicesMap
|
||||
import org.matrix.android.sdk.internal.crypto.model.TrailType
|
||||
import org.matrix.android.sdk.internal.crypto.model.event.RoomKeyContent
|
||||
import org.matrix.android.sdk.internal.crypto.model.event.RoomKeyWithHeldContent
|
||||
import org.matrix.android.sdk.internal.crypto.model.rest.DeviceInfo
|
||||
import org.matrix.android.sdk.internal.crypto.model.rest.DevicesListResponse
|
||||
import org.matrix.android.sdk.internal.crypto.model.rest.RoomKeyShareRequest
|
||||
import org.matrix.android.sdk.internal.crypto.model.toRest
|
||||
import org.matrix.android.sdk.internal.crypto.repository.WarnOnUnknownDeviceRepository
|
||||
import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
|
||||
@ -156,7 +163,7 @@ internal class DefaultCryptoService @Inject constructor(
|
||||
private val incomingKeyRequestManager: IncomingKeyRequestManager,
|
||||
private val secretShareManager: SecretShareManager,
|
||||
//
|
||||
private val outgoingGossipingRequestManager: OutgoingGossipingRequestManager,
|
||||
private val outgoingKeyRequestManager: OutgoingKeyRequestManager,
|
||||
// Actions
|
||||
private val setDeviceVerificationAction: SetDeviceVerificationAction,
|
||||
private val megolmSessionDataImporter: MegolmSessionDataImporter,
|
||||
@ -387,7 +394,7 @@ internal class DefaultCryptoService @Inject constructor(
|
||||
fun close() = runBlocking(coroutineDispatchers.crypto) {
|
||||
cryptoCoroutineScope.coroutineContext.cancelChildren(CancellationException("Closing crypto module"))
|
||||
incomingKeyRequestManager.close()
|
||||
outgoingGossipingRequestManager.close()
|
||||
outgoingKeyRequestManager.close()
|
||||
olmDevice.release()
|
||||
cryptoStore.close()
|
||||
}
|
||||
@ -458,7 +465,7 @@ internal class DefaultCryptoService @Inject constructor(
|
||||
try {
|
||||
if (toDevices.isEmpty()) {
|
||||
// this is not blocking
|
||||
outgoingGossipingRequestManager.requireProcessAllPendingKeyRequests()
|
||||
outgoingKeyRequestManager.requireProcessAllPendingKeyRequests()
|
||||
} else {
|
||||
Timber.tag(loggerTag.value)
|
||||
.w("Don't process key requests yet as their might be more to_device to catchup")
|
||||
@ -778,26 +785,18 @@ internal class DefaultCryptoService @Inject constructor(
|
||||
cryptoCoroutineScope.launch(coroutineDispatchers.crypto) {
|
||||
when (event.getClearType()) {
|
||||
EventType.ROOM_KEY, EventType.FORWARDED_ROOM_KEY -> {
|
||||
// gossipingBuffer.add(event)
|
||||
// Keys are imported directly, not waiting for end of sync
|
||||
onRoomKeyEvent(event)
|
||||
}
|
||||
EventType.REQUEST_SECRET -> {
|
||||
secretShareManager.handleSecretRequest(event)
|
||||
// incomingGossipingRequestManager.onGossipingRequestEvent(event)
|
||||
}
|
||||
EventType.ROOM_KEY_REQUEST -> {
|
||||
Timber.w("VALR: key request ${event.getClearContent()}")
|
||||
// save audit trail
|
||||
// gossipingBuffer.add(event)
|
||||
// Requests are stacked, and will be handled one by one at the end of the sync (onSyncComplete)
|
||||
Timber.w("VALR: sender Id is ${event.senderId} full ev $event")
|
||||
event.getClearContent().toModel<RoomKeyShareRequest>()?.let { req ->
|
||||
event.senderId?.let { incomingKeyRequestManager.addNewIncomingRequest(it, req) }
|
||||
}
|
||||
}
|
||||
EventType.SEND_SECRET -> {
|
||||
// gossipingBuffer.add(event)
|
||||
onSecretSendReceived(event)
|
||||
}
|
||||
EventType.ROOM_KEY_WITHHELD -> {
|
||||
@ -842,7 +841,7 @@ internal class DefaultCryptoService @Inject constructor(
|
||||
withHeldContent.algorithm ?: return
|
||||
withHeldContent.roomId ?: return
|
||||
withHeldContent.senderKey ?: return
|
||||
outgoingGossipingRequestManager.onRoomKeyWithHeld(
|
||||
outgoingKeyRequestManager.onRoomKeyWithHeld(
|
||||
sessionId = withHeldContent.sessionId,
|
||||
algorithm = withHeldContent.algorithm,
|
||||
roomId = withHeldContent.roomId,
|
||||
@ -854,14 +853,6 @@ internal class DefaultCryptoService @Inject constructor(
|
||||
content = event.getClearContent()
|
||||
)
|
||||
)
|
||||
// Timber.tag(loggerTag.value).i("onKeyWithHeldReceived() received from:${event.senderId}, content <$withHeldContent>")
|
||||
// val alg = roomDecryptorProvider.getOrCreateRoomDecryptor(withHeldContent.roomId, withHeldContent.algorithm)
|
||||
// if (alg is IMXWithHeldExtension) {
|
||||
// alg.onRoomKeyWithHeldEvent(senderId, withHeldContent)
|
||||
// } else {
|
||||
// Timber.tag(loggerTag.value).e("onKeyWithHeldReceived() from:${event.senderId}: Unable to handle WithHeldContent for ${withHeldContent.algorithm}")
|
||||
// return
|
||||
// }
|
||||
}
|
||||
|
||||
private suspend fun onSecretSendReceived(event: Event) {
|
||||
@ -1158,52 +1149,11 @@ internal class DefaultCryptoService @Inject constructor(
|
||||
* @param event the event to decrypt again.
|
||||
*/
|
||||
override fun reRequestRoomKeyForEvent(event: Event) {
|
||||
val sender = event.senderId ?: return
|
||||
val wireContent = event.content.toModel<EncryptedEventContent>() ?: return Unit.also {
|
||||
Timber.tag(loggerTag.value).e("reRequestRoomKeyForEvent Failed to re-request key, null content")
|
||||
}
|
||||
|
||||
val recipients = if (event.senderId == userId) {
|
||||
mapOf(
|
||||
userId to listOf("*")
|
||||
)
|
||||
} else {
|
||||
// for the case where you share the key with a device that has a broken olm session
|
||||
// The other user might Re-shares a megolm session key with devices if the key has already been
|
||||
// sent to them.
|
||||
mapOf(
|
||||
userId to listOf("*"),
|
||||
// TODO we might not have deviceId in the future due to https://github.com/matrix-org/matrix-spec-proposals/pull/3700
|
||||
// so in this case query to all
|
||||
sender to listOf(wireContent.deviceId ?: "*")
|
||||
)
|
||||
}
|
||||
val requestBody = RoomKeyRequestBody(
|
||||
algorithm = wireContent.algorithm,
|
||||
roomId = event.roomId,
|
||||
senderKey = wireContent.senderKey,
|
||||
sessionId = wireContent.sessionId
|
||||
)
|
||||
|
||||
outgoingGossipingRequestManager.postRoomKeyRequest(requestBody, recipients, true)
|
||||
outgoingKeyRequestManager.requestKeyForEvent(event, true)
|
||||
}
|
||||
|
||||
override fun requestRoomKeyForEvent(event: Event) {
|
||||
val wireContent = event.content.toModel<EncryptedEventContent>() ?: return Unit.also {
|
||||
Timber.tag(loggerTag.value).e("requestRoomKeyForEvent Failed to request key, null content eventId: ${event.eventId}")
|
||||
}
|
||||
|
||||
cryptoCoroutineScope.launch(coroutineDispatchers.crypto) {
|
||||
// if (!isStarted()) {
|
||||
// Timber.v("## CRYPTO | requestRoomKeyForEvent() : wait after e2e init")
|
||||
// internalStart(false)
|
||||
// }
|
||||
roomDecryptorProvider
|
||||
.getOrCreateRoomDecryptor(event.roomId, wireContent.algorithm)
|
||||
?.requestKeysForEvent(event, false) ?: run {
|
||||
Timber.tag(loggerTag.value).v("requestRoomKeyForEvent() : No room decryptor for roomId:${event.roomId} algorithm:${wireContent.algorithm}")
|
||||
}
|
||||
}
|
||||
outgoingKeyRequestManager.requestKeyForEvent(event, false)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1213,7 +1163,7 @@ internal class DefaultCryptoService @Inject constructor(
|
||||
*/
|
||||
override fun addRoomKeysRequestListener(listener: GossipingRequestListener) {
|
||||
incomingKeyRequestManager.addRoomKeysRequestListener(listener)
|
||||
// TODO same for secret manager
|
||||
secretShareManager.addListener(listener)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1223,42 +1173,9 @@ internal class DefaultCryptoService @Inject constructor(
|
||||
*/
|
||||
override fun removeRoomKeysRequestListener(listener: GossipingRequestListener) {
|
||||
incomingKeyRequestManager.removeRoomKeysRequestListener(listener)
|
||||
// TODO same for secret manager
|
||||
secretShareManager.addListener(listener)
|
||||
}
|
||||
|
||||
// private fun markOlmSessionForUnwedging(senderId: String, deviceInfo: CryptoDeviceInfo) {
|
||||
// val deviceKey = deviceInfo.identityKey()
|
||||
//
|
||||
// val lastForcedDate = lastNewSessionForcedDates.getObject(senderId, deviceKey) ?: 0
|
||||
// val now = System.currentTimeMillis()
|
||||
// if (now - lastForcedDate < CRYPTO_MIN_FORCE_SESSION_PERIOD_MILLIS) {
|
||||
// Timber.d("## CRYPTO | markOlmSessionForUnwedging: New session already forced with device at $lastForcedDate. Not forcing another")
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// Timber.d("## CRYPTO | markOlmSessionForUnwedging from $senderId:${deviceInfo.deviceId}")
|
||||
// lastNewSessionForcedDates.setObject(senderId, deviceKey, now)
|
||||
//
|
||||
// cryptoCoroutineScope.launch(coroutineDispatchers.crypto) {
|
||||
// ensureOlmSessionsForDevicesAction.handle(mapOf(senderId to listOf(deviceInfo)), force = true)
|
||||
//
|
||||
// // Now send a blank message on that session so the other side knows about it.
|
||||
// // (The keyshare request is sent in the clear so that won't do)
|
||||
// // We send this first such that, as long as the toDevice messages arrive in the
|
||||
// // same order we sent them, the other end will get this first, set up the new session,
|
||||
// // then get the keyshare request and send the key over this new session (because it
|
||||
// // is the session it has most recently received a message on).
|
||||
// val payloadJson = mapOf<String, Any>("type" to EventType.DUMMY)
|
||||
//
|
||||
// val encodedPayload = messageEncrypter.encryptMessage(payloadJson, listOf(deviceInfo))
|
||||
// val sendToDeviceMap = MXUsersDevicesMap<Any>()
|
||||
// sendToDeviceMap.setObject(senderId, deviceInfo.deviceId, encodedPayload)
|
||||
// Timber.v("## CRYPTO | markOlmSessionForUnwedging() : sending to $senderId:${deviceInfo.deviceId}")
|
||||
// val sendToDeviceParams = SendToDeviceTask.Params(EventType.ENCRYPTED, sendToDeviceMap)
|
||||
// sendToDeviceTask.execute(sendToDeviceParams)
|
||||
// }
|
||||
// }
|
||||
|
||||
/**
|
||||
* Provides the list of unknown devices
|
||||
*
|
||||
@ -1312,12 +1229,26 @@ internal class DefaultCryptoService @Inject constructor(
|
||||
return cryptoStore.getOutgoingRoomKeyRequestsPaged()
|
||||
}
|
||||
|
||||
override fun getIncomingRoomKeyRequestsPaged(): LiveData<PagedList<IncomingRoomKeyRequest>> {
|
||||
return cryptoStore.getIncomingRoomKeyRequestsPaged()
|
||||
override fun getIncomingRoomKeyRequests(): List<IncomingRoomKeyRequest> {
|
||||
return cryptoStore.getGossipingEvents()
|
||||
.mapNotNull {
|
||||
IncomingRoomKeyRequest.fromEvent(it)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getIncomingRoomKeyRequests(): List<IncomingRoomKeyRequest> {
|
||||
return cryptoStore.getIncomingRoomKeyRequests()
|
||||
override fun getIncomingRoomKeyRequestsPaged(): LiveData<PagedList<IncomingRoomKeyRequest>> {
|
||||
return cryptoStore.getGossipingEventsTrail(TrailType.IncomingKeyRequest) {
|
||||
IncomingRoomKeyRequest.fromEvent(it)
|
||||
?: IncomingRoomKeyRequest(localCreationTimestamp = 0L)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If you registered a `GossipingRequestListener`, you will be notified of key request
|
||||
* that was not accepted by the SDK. You can call back this manually to accept anyhow.
|
||||
*/
|
||||
override suspend fun manuallyAcceptRoomKeyRequest(request: IncomingRoomKeyRequest) {
|
||||
incomingKeyRequestManager.manuallyAcceptRoomKeyRequest(request)
|
||||
}
|
||||
|
||||
override fun getGossipingEventsTrail(): LiveData<PagedList<AuditTrail>> {
|
||||
|
@ -311,10 +311,19 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM
|
||||
} else {
|
||||
Timber.v("## CRYPTO | downloadKeys() : starts")
|
||||
val t0 = System.currentTimeMillis()
|
||||
val result = doKeyDownloadForUsers(downloadUsers)
|
||||
Timber.v("## CRYPTO | downloadKeys() : doKeyDownloadForUsers succeeds after ${System.currentTimeMillis() - t0} ms")
|
||||
result.also {
|
||||
it.addEntriesFromMap(stored)
|
||||
try {
|
||||
val result = doKeyDownloadForUsers(downloadUsers)
|
||||
Timber.v("## CRYPTO | downloadKeys() : doKeyDownloadForUsers succeeds after ${System.currentTimeMillis() - t0} ms")
|
||||
result.also {
|
||||
it.addEntriesFromMap(stored)
|
||||
}
|
||||
} catch (failure: Throwable) {
|
||||
Timber.w(failure, "## CRYPTO | downloadKeys() : doKeyDownloadForUsers failed after ${System.currentTimeMillis() - t0} ms")
|
||||
if (forceDownload) {
|
||||
throw failure
|
||||
} else {
|
||||
stored
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -26,10 +26,14 @@ import kotlinx.coroutines.withContext
|
||||
import org.matrix.android.sdk.api.MatrixCoroutineDispatchers
|
||||
import org.matrix.android.sdk.api.auth.data.Credentials
|
||||
import org.matrix.android.sdk.api.crypto.MXCRYPTO_ALGORITHM_MEGOLM
|
||||
import org.matrix.android.sdk.api.crypto.MXCryptoConfig
|
||||
import org.matrix.android.sdk.api.extensions.tryOrNull
|
||||
import org.matrix.android.sdk.api.logger.LoggerTag
|
||||
import org.matrix.android.sdk.api.session.crypto.keyshare.GossipingRequestListener
|
||||
import org.matrix.android.sdk.api.session.crypto.model.CryptoDeviceInfo
|
||||
import org.matrix.android.sdk.api.session.crypto.model.IncomingRoomKeyRequest
|
||||
import org.matrix.android.sdk.api.session.crypto.model.MXUsersDevicesMap
|
||||
import org.matrix.android.sdk.api.session.crypto.model.RoomKeyRequestBody
|
||||
import org.matrix.android.sdk.api.session.crypto.model.RoomKeyShareRequest
|
||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||
import org.matrix.android.sdk.api.session.events.model.content.RoomKeyWithHeldContent
|
||||
@ -53,6 +57,7 @@ internal class IncomingKeyRequestManager @Inject constructor(
|
||||
private val cryptoStore: IMXCryptoStore,
|
||||
private val ensureOlmSessionsForDevicesAction: EnsureOlmSessionsForDevicesAction,
|
||||
private val olmDevice: MXOlmDevice,
|
||||
private val cryptoConfig: MXCryptoConfig,
|
||||
private val messageEncrypter: MessageEncrypter,
|
||||
private val coroutineDispatchers: MatrixCoroutineDispatchers,
|
||||
private val sendToDeviceTask: SendToDeviceTask) {
|
||||
@ -71,6 +76,7 @@ internal class IncomingKeyRequestManager @Inject constructor(
|
||||
}
|
||||
|
||||
data class ValidMegolmRequestBody(
|
||||
val requestId: String,
|
||||
val requestingUserId: String,
|
||||
val requestingDeviceId: String,
|
||||
val roomId: String,
|
||||
@ -87,6 +93,7 @@ internal class IncomingKeyRequestManager @Inject constructor(
|
||||
val roomId = body.roomId ?: return null
|
||||
val sessionId = body.sessionId ?: return null
|
||||
val senderKey = body.senderKey ?: return null
|
||||
val requestId = this.requestId ?: return null
|
||||
if (body.algorithm != MXCRYPTO_ALGORITHM_MEGOLM) return null
|
||||
val action = when (this.action) {
|
||||
"request" -> MegolmRequestAction.Request
|
||||
@ -94,6 +101,7 @@ internal class IncomingKeyRequestManager @Inject constructor(
|
||||
else -> null
|
||||
} ?: return null
|
||||
return ValidMegolmRequestBody(
|
||||
requestId = requestId,
|
||||
requestingUserId = senderId,
|
||||
requestingDeviceId = deviceId,
|
||||
roomId = roomId,
|
||||
@ -121,6 +129,21 @@ internal class IncomingKeyRequestManager @Inject constructor(
|
||||
}
|
||||
MegolmRequestAction.Cancel -> {
|
||||
// ignore, we can't cancel as it's not known (probably already processed)
|
||||
// still notify app layer if it was passed up previously
|
||||
IncomingRoomKeyRequest.fromRestRequest(senderId, request)?.let { iReq ->
|
||||
outgoingRequestScope.launch(coroutineDispatchers.computation) {
|
||||
val listenersCopy = synchronized(gossipingRequestListeners) {
|
||||
gossipingRequestListeners.toList()
|
||||
}
|
||||
listenersCopy.onEach {
|
||||
tryOrNull {
|
||||
withContext(coroutineDispatchers.main) {
|
||||
it.onRequestCancelled(iReq)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -131,6 +154,18 @@ internal class IncomingKeyRequestManager @Inject constructor(
|
||||
MegolmRequestAction.Cancel -> {
|
||||
// discard the request in buffer
|
||||
incomingRequestBuffer.remove(existing)
|
||||
outgoingRequestScope.launch(coroutineDispatchers.computation) {
|
||||
val listenersCopy = synchronized(gossipingRequestListeners) {
|
||||
gossipingRequestListeners.toList()
|
||||
}
|
||||
listenersCopy.onEach {
|
||||
IncomingRoomKeyRequest.fromRestRequest(senderId, request)?.let { iReq ->
|
||||
withContext(coroutineDispatchers.main) {
|
||||
tryOrNull { it.onRequestCancelled(iReq) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -170,6 +205,7 @@ internal class IncomingKeyRequestManager @Inject constructor(
|
||||
}
|
||||
|
||||
cryptoStore.saveIncomingKeyRequestAuditTrail(
|
||||
request.requestId,
|
||||
request.roomId,
|
||||
request.sessionId,
|
||||
request.senderKey,
|
||||
@ -205,6 +241,10 @@ internal class IncomingKeyRequestManager @Inject constructor(
|
||||
shareIfItWasPreviouslyShared(request, requestingDevice)
|
||||
}
|
||||
} else {
|
||||
if (cryptoConfig.limitRoomKeyRequestsToMyDevices) {
|
||||
Timber.tag(loggerTag.value).v("Ignore request from other user as per crypto config: ${request.shortDbgString()}")
|
||||
return
|
||||
}
|
||||
Timber.tag(loggerTag.value).v("handling request from other user: megolm session ${request.sessionId}")
|
||||
if (requestingDevice.isBlocked) {
|
||||
// it's blocked, so send a withheld code
|
||||
@ -224,12 +264,39 @@ internal class IncomingKeyRequestManager @Inject constructor(
|
||||
// we share from the index it was previously shared with
|
||||
shareMegolmKey(request, requestingDevice, wasSessionSharedWithUser.chainIndex.toLong())
|
||||
} else {
|
||||
sendWithheldForRequest(request, WithHeldCode.UNAUTHORISED)
|
||||
// TODO if it's our device we could delegate to the app layer to decide?
|
||||
val isOwnDevice = requestingDevice.userId == credentials.userId
|
||||
sendWithheldForRequest(request, if (isOwnDevice) WithHeldCode.UNVERIFIED else WithHeldCode.UNAUTHORISED)
|
||||
// if it's our device we could delegate to the app layer to decide
|
||||
if (isOwnDevice) {
|
||||
outgoingRequestScope.launch(coroutineDispatchers.computation) {
|
||||
val listenersCopy = synchronized(gossipingRequestListeners) {
|
||||
gossipingRequestListeners.toList()
|
||||
}
|
||||
val iReq = IncomingRoomKeyRequest(
|
||||
userId = requestingDevice.userId,
|
||||
deviceId = requestingDevice.deviceId,
|
||||
requestId = request.requestId,
|
||||
requestBody = RoomKeyRequestBody(
|
||||
algorithm = MXCRYPTO_ALGORITHM_MEGOLM,
|
||||
senderKey = request.senderKey,
|
||||
sessionId = request.sessionId,
|
||||
roomId = request.roomId
|
||||
),
|
||||
localCreationTimestamp = System.currentTimeMillis()
|
||||
)
|
||||
listenersCopy.onEach {
|
||||
withContext(coroutineDispatchers.main) {
|
||||
tryOrNull { it.onRoomKeyRequest(iReq) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun sendWithheldForRequest(request: ValidMegolmRequestBody, code: WithHeldCode) {
|
||||
Timber.tag(loggerTag.value)
|
||||
.w("Send withheld $code for req: ${request.shortDbgString()}")
|
||||
val withHeldContent = RoomKeyWithHeldContent(
|
||||
roomId = request.roomId,
|
||||
senderKey = request.senderKey,
|
||||
@ -253,13 +320,13 @@ internal class IncomingKeyRequestManager @Inject constructor(
|
||||
}
|
||||
|
||||
cryptoStore.saveWithheldAuditTrail(
|
||||
request.roomId,
|
||||
request.sessionId,
|
||||
request.senderKey,
|
||||
MXCRYPTO_ALGORITHM_MEGOLM,
|
||||
code,
|
||||
request.requestingUserId,
|
||||
request.requestingDeviceId
|
||||
roomId = request.roomId,
|
||||
sessionId = request.sessionId,
|
||||
senderKey = request.senderKey,
|
||||
algorithm = MXCRYPTO_ALGORITHM_MEGOLM,
|
||||
code = code,
|
||||
userId = request.requestingUserId,
|
||||
deviceId = request.requestingDeviceId
|
||||
)
|
||||
} catch (failure: Throwable) {
|
||||
// Ignore it's not that important?
|
||||
@ -269,6 +336,31 @@ internal class IncomingKeyRequestManager @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun manuallyAcceptRoomKeyRequest(request: IncomingRoomKeyRequest) {
|
||||
request.requestId ?: return
|
||||
request.deviceId ?: return
|
||||
request.userId ?: return
|
||||
request.requestBody?.roomId ?: return
|
||||
request.requestBody.senderKey ?: return
|
||||
request.requestBody.sessionId ?: return
|
||||
val validReq = ValidMegolmRequestBody(
|
||||
requestId = request.requestId,
|
||||
requestingDeviceId = request.deviceId,
|
||||
requestingUserId = request.userId,
|
||||
roomId = request.requestBody.roomId,
|
||||
senderKey = request.requestBody.senderKey,
|
||||
sessionId = request.requestBody.sessionId,
|
||||
action = MegolmRequestAction.Request
|
||||
)
|
||||
val requestingDevice =
|
||||
cryptoStore.getUserDevice(request.userId, request.deviceId)
|
||||
?: return Unit.also {
|
||||
Timber.tag(loggerTag.value).d("Ignoring key request: ${validReq.shortDbgString()}")
|
||||
}
|
||||
|
||||
shareMegolmKey(validReq, requestingDevice, null)
|
||||
}
|
||||
|
||||
private suspend fun shareMegolmKey(validRequest: ValidMegolmRequestBody,
|
||||
requestingDevice: CryptoDeviceInfo,
|
||||
chainIndex: Long?): Boolean {
|
||||
@ -341,14 +433,12 @@ internal class IncomingKeyRequestManager @Inject constructor(
|
||||
|
||||
fun addRoomKeysRequestListener(listener: GossipingRequestListener) {
|
||||
synchronized(gossipingRequestListeners) {
|
||||
// TODO
|
||||
gossipingRequestListeners.add(listener)
|
||||
}
|
||||
}
|
||||
|
||||
fun removeRoomKeysRequestListener(listener: GossipingRequestListener) {
|
||||
synchronized(gossipingRequestListeners) {
|
||||
// TODO
|
||||
gossipingRequestListeners.remove(listener)
|
||||
}
|
||||
}
|
||||
|
@ -1,36 +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
|
||||
|
||||
internal interface IncomingShareRequestCommon {
|
||||
/**
|
||||
* The user id
|
||||
*/
|
||||
val userId: String?
|
||||
|
||||
/**
|
||||
* The device id
|
||||
*/
|
||||
val deviceId: String?
|
||||
|
||||
/**
|
||||
* The request id
|
||||
*/
|
||||
val requestId: String?
|
||||
|
||||
val localCreationTimestamp: Long?
|
||||
}
|
@ -585,6 +585,13 @@ internal class MXOlmDevice @Inject constructor(
|
||||
|
||||
// Inbound group session
|
||||
|
||||
sealed class AddSessionResult {
|
||||
data class Imported(val ratchetIndex: Int) : AddSessionResult()
|
||||
abstract class Failure : AddSessionResult()
|
||||
object NotImported : Failure()
|
||||
data class NotImportedHigherIndex(val newIndex: Int) : AddSessionResult()
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an inbound group session to the session store.
|
||||
*
|
||||
@ -603,7 +610,7 @@ internal class MXOlmDevice @Inject constructor(
|
||||
senderKey: String,
|
||||
forwardingCurve25519KeyChain: List<String>,
|
||||
keysClaimed: Map<String, String>,
|
||||
exportFormat: Boolean): Boolean {
|
||||
exportFormat: Boolean): AddSessionResult {
|
||||
val candidateSession = OlmInboundGroupSessionWrapper2(sessionKey, exportFormat)
|
||||
val existingSessionHolder = tryOrNull { getInboundGroupSession(sessionId, senderKey, roomId) }
|
||||
val existingSession = existingSessionHolder?.wrapper
|
||||
@ -611,7 +618,7 @@ internal class MXOlmDevice @Inject constructor(
|
||||
if (existingSession != null) {
|
||||
Timber.tag(loggerTag.value).d("## addInboundGroupSession() check if known session is better than candidate session")
|
||||
try {
|
||||
val existingFirstKnown = existingSession.firstKnownIndex ?: return false.also {
|
||||
val existingFirstKnown = existingSession.firstKnownIndex ?: return AddSessionResult.NotImported.also {
|
||||
// This is quite unexpected, could throw if native was released?
|
||||
Timber.tag(loggerTag.value).e("## addInboundGroupSession() null firstKnownIndex on existing session")
|
||||
candidateSession.olmInboundGroupSession?.releaseSession()
|
||||
@ -622,12 +629,12 @@ internal class MXOlmDevice @Inject constructor(
|
||||
if (newKnownFirstIndex != null && existingFirstKnown <= newKnownFirstIndex) {
|
||||
Timber.tag(loggerTag.value).d("## addInboundGroupSession() : ignore session our is better $senderKey/$sessionId")
|
||||
candidateSession.olmInboundGroupSession?.releaseSession()
|
||||
return false
|
||||
return AddSessionResult.NotImportedHigherIndex(newKnownFirstIndex.toInt())
|
||||
}
|
||||
} catch (failure: Throwable) {
|
||||
Timber.tag(loggerTag.value).e("## addInboundGroupSession() Failed to add inbound: ${failure.localizedMessage}")
|
||||
candidateSession.olmInboundGroupSession?.releaseSession()
|
||||
return false
|
||||
return AddSessionResult.NotImported
|
||||
}
|
||||
}
|
||||
|
||||
@ -637,19 +644,19 @@ internal class MXOlmDevice @Inject constructor(
|
||||
val candidateOlmInboundSession = candidateSession.olmInboundGroupSession
|
||||
if (null == candidateOlmInboundSession) {
|
||||
Timber.tag(loggerTag.value).e("## addInboundGroupSession : invalid session <null>")
|
||||
return false
|
||||
return AddSessionResult.NotImported
|
||||
}
|
||||
|
||||
try {
|
||||
if (candidateOlmInboundSession.sessionIdentifier() != sessionId) {
|
||||
Timber.tag(loggerTag.value).e("## addInboundGroupSession : ERROR: Mismatched group session ID from senderKey: $senderKey")
|
||||
candidateOlmInboundSession.releaseSession()
|
||||
return false
|
||||
return AddSessionResult.NotImported
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
candidateOlmInboundSession.releaseSession()
|
||||
Timber.tag(loggerTag.value).e(e, "## addInboundGroupSession : sessionIdentifier() failed")
|
||||
return false
|
||||
return AddSessionResult.NotImported
|
||||
}
|
||||
|
||||
candidateSession.senderKey = senderKey
|
||||
@ -663,7 +670,7 @@ internal class MXOlmDevice @Inject constructor(
|
||||
inboundGroupSessionStore.storeInBoundGroupSession(InboundGroupSessionHolder(candidateSession), sessionId, senderKey)
|
||||
}
|
||||
|
||||
return true
|
||||
return AddSessionResult.Imported(candidateSession.firstKnownIndex?.toInt() ?: 0)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2022 New Vector Ltd
|
||||
* Copyright (c) 2022 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.
|
||||
@ -27,7 +27,7 @@ data class RequestReply(
|
||||
)
|
||||
|
||||
sealed class RequestResult {
|
||||
object Success : RequestResult()
|
||||
data class Success(val chainIndex: Int) : RequestResult()
|
||||
data class Failure(val code: WithHeldCode) : RequestResult()
|
||||
}
|
||||
|
||||
@ -35,6 +35,7 @@ data class OutgoingKeyRequest(
|
||||
var requestBody: RoomKeyRequestBody?,
|
||||
// recipients for the request map of users to list of deviceId
|
||||
val recipients: Map<String, List<String>>,
|
||||
val fromIndex: Int,
|
||||
// Unique id for this request. Used for both
|
||||
// an id within the request for later pairing with a cancellation, and for
|
||||
// the transaction id when sending the to_device messages to our local
|
||||
|
@ -23,19 +23,23 @@ import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.matrix.android.sdk.api.MatrixCoroutineDispatchers
|
||||
import org.matrix.android.sdk.api.crypto.MXCRYPTO_ALGORITHM_MEGOLM
|
||||
import org.matrix.android.sdk.api.crypto.MXCryptoConfig
|
||||
import org.matrix.android.sdk.api.extensions.tryOrNull
|
||||
import org.matrix.android.sdk.api.logger.LoggerTag
|
||||
import org.matrix.android.sdk.api.session.crypto.model.GossipingToDeviceObject
|
||||
import org.matrix.android.sdk.api.session.crypto.model.MXUsersDevicesMap
|
||||
import org.matrix.android.sdk.api.session.crypto.model.OutgoingRoomKeyRequestState
|
||||
import org.matrix.android.sdk.api.session.crypto.model.RoomKeyRequestBody
|
||||
import org.matrix.android.sdk.api.session.crypto.model.RoomKeyShareRequest
|
||||
import org.matrix.android.sdk.api.session.events.model.Event
|
||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||
import org.matrix.android.sdk.internal.crypto.crosssigning.fromBase64
|
||||
import org.matrix.android.sdk.internal.crypto.model.MXUsersDevicesMap
|
||||
import org.matrix.android.sdk.internal.crypto.model.event.EncryptedEventContent
|
||||
import org.matrix.android.sdk.internal.crypto.model.rest.GossipingToDeviceObject
|
||||
import org.matrix.android.sdk.internal.crypto.model.rest.RoomKeyRequestBody
|
||||
import org.matrix.android.sdk.internal.crypto.model.rest.RoomKeyShareRequest
|
||||
import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
|
||||
import org.matrix.android.sdk.internal.crypto.tasks.DefaultSendToDeviceTask
|
||||
import org.matrix.android.sdk.internal.crypto.tasks.SendToDeviceTask
|
||||
import org.matrix.android.sdk.internal.di.SessionId
|
||||
import org.matrix.android.sdk.internal.di.UserId
|
||||
import org.matrix.android.sdk.internal.session.SessionScope
|
||||
import org.matrix.android.sdk.internal.task.SemaphoreCoroutineSequencer
|
||||
import timber.log.Timber
|
||||
@ -44,7 +48,7 @@ import java.util.concurrent.Executors
|
||||
import javax.inject.Inject
|
||||
import kotlin.system.measureTimeMillis
|
||||
|
||||
private val loggerTag = LoggerTag("OutgoingGossipingRequestManager", LoggerTag.CRYPTO)
|
||||
private val loggerTag = LoggerTag("OutgoingKeyRequestManager", LoggerTag.CRYPTO)
|
||||
|
||||
/**
|
||||
* This class is responsible for sending key requests to other devices when a message failed to decrypt.
|
||||
@ -54,10 +58,13 @@ private val loggerTag = LoggerTag("OutgoingGossipingRequestManager", LoggerTag.C
|
||||
* If a request failed it will be retried at the end of the next sync
|
||||
*/
|
||||
@SessionScope
|
||||
internal class OutgoingGossipingRequestManager @Inject constructor(
|
||||
internal class OutgoingKeyRequestManager @Inject constructor(
|
||||
@SessionId private val sessionId: String,
|
||||
@UserId private val myUserId: String,
|
||||
private val cryptoStore: IMXCryptoStore,
|
||||
private val coroutineDispatchers: MatrixCoroutineDispatchers,
|
||||
private val cryptoConfig: MXCryptoConfig,
|
||||
private val inboundGroupSessionStore: InboundGroupSessionStore,
|
||||
private val sendToDeviceTask: DefaultSendToDeviceTask,
|
||||
private val perSessionBackupQueryRateLimiter: PerSessionBackupQueryRateLimiter) {
|
||||
|
||||
@ -70,10 +77,71 @@ internal class OutgoingGossipingRequestManager @Inject constructor(
|
||||
// We keep a stack as we consider that the key requested last is more likely to be on screen?
|
||||
private val requestDiscardedBecauseAlreadySentThatCouldBeTriedWithBackup = Stack<String>()
|
||||
|
||||
fun postRoomKeyRequest(requestBody: RoomKeyRequestBody, recipients: Map<String, List<String>>, force: Boolean = false) {
|
||||
fun requestKeyForEvent(event: Event, force: Boolean) {
|
||||
val (targets, body) = getRoomKeyRequestTargetForEvent(event) ?: return
|
||||
val index = ratchetIndexForMessage(event) ?: 0
|
||||
postRoomKeyRequest(body, targets, index, force)
|
||||
}
|
||||
|
||||
private fun getRoomKeyRequestTargetForEvent(event: Event): Pair<Map<String, List<String>>, RoomKeyRequestBody>? {
|
||||
val sender = event.senderId ?: return null
|
||||
val encryptedEventContent = event.content.toModel<EncryptedEventContent>() ?: return null.also {
|
||||
Timber.tag(loggerTag.value).e("getRoomKeyRequestTargetForEvent Failed to re-request key, null content")
|
||||
}
|
||||
if (encryptedEventContent.algorithm != MXCRYPTO_ALGORITHM_MEGOLM) return null
|
||||
|
||||
val senderDevice = encryptedEventContent.deviceId
|
||||
val recipients = if (cryptoConfig.limitRoomKeyRequestsToMyDevices) {
|
||||
mapOf(
|
||||
myUserId to listOf("*")
|
||||
)
|
||||
} else {
|
||||
if (event.senderId == myUserId) {
|
||||
mapOf(
|
||||
myUserId to listOf("*")
|
||||
)
|
||||
} else {
|
||||
// for the case where you share the key with a device that has a broken olm session
|
||||
// The other user might Re-shares a megolm session key with devices if the key has already been
|
||||
// sent to them.
|
||||
mapOf(
|
||||
myUserId to listOf("*"),
|
||||
|
||||
// TODO we might not have deviceId in the future due to https://github.com/matrix-org/matrix-spec-proposals/pull/3700
|
||||
// so in this case query to all
|
||||
sender to listOf(senderDevice ?: "*")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val requestBody = RoomKeyRequestBody(
|
||||
roomId = event.roomId,
|
||||
algorithm = encryptedEventContent.algorithm,
|
||||
senderKey = encryptedEventContent.senderKey,
|
||||
sessionId = encryptedEventContent.sessionId
|
||||
)
|
||||
return recipients to requestBody
|
||||
}
|
||||
|
||||
private fun ratchetIndexForMessage(event: Event): Int? {
|
||||
val encryptedContent = event.content.toModel<EncryptedEventContent>() ?: return null
|
||||
if (encryptedContent.algorithm != MXCRYPTO_ALGORITHM_MEGOLM) return null
|
||||
return encryptedContent.ciphertext?.fromBase64()?.inputStream()?.reader()?.let {
|
||||
tryOrNull {
|
||||
val megolmVersion = it.read()
|
||||
if (megolmVersion != 3) return@tryOrNull null
|
||||
/** Int tag */
|
||||
/** Int tag */
|
||||
if (it.read() != 8) return@tryOrNull null
|
||||
it.read()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun postRoomKeyRequest(requestBody: RoomKeyRequestBody, recipients: Map<String, List<String>>, fromIndex: Int, force: Boolean = false) {
|
||||
outgoingRequestScope.launch {
|
||||
sequencer.post {
|
||||
internalQueueRequest(requestBody, recipients, force)
|
||||
internalQueueRequest(requestBody, recipients, fromIndex, force)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -81,10 +149,10 @@ internal class OutgoingGossipingRequestManager @Inject constructor(
|
||||
/**
|
||||
* Typically called when we the session as been imported or received meanwhile
|
||||
*/
|
||||
fun postCancelRequestForSessionIfNeeded(sessionId: String, roomId: String, senderKey: String) {
|
||||
fun postCancelRequestForSessionIfNeeded(sessionId: String, roomId: String, senderKey: String, fromIndex: Int) {
|
||||
outgoingRequestScope.launch {
|
||||
sequencer.post {
|
||||
internalQueueCancelRequest(sessionId, roomId, senderKey)
|
||||
internalQueueCancelRequest(sessionId, roomId, senderKey, fromIndex)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -94,17 +162,24 @@ internal class OutgoingGossipingRequestManager @Inject constructor(
|
||||
roomId: String,
|
||||
senderKey: String,
|
||||
fromDevice: String?,
|
||||
fromIndex: Int,
|
||||
event: Event) {
|
||||
Timber.tag(loggerTag.value).v("Key forwarded for $sessionId from ${event.senderId}|$fromDevice")
|
||||
Timber.tag(loggerTag.value).d("Key forwarded for $sessionId from ${event.senderId}|$fromDevice at index $fromIndex")
|
||||
outgoingRequestScope.launch {
|
||||
sequencer.post {
|
||||
cryptoStore.updateOutgoingRoomKeyReply(
|
||||
roomId,
|
||||
sessionId,
|
||||
algorithm,
|
||||
senderKey,
|
||||
fromDevice,
|
||||
event
|
||||
roomId = roomId,
|
||||
sessionId = sessionId,
|
||||
algorithm = algorithm,
|
||||
senderKey = senderKey,
|
||||
fromDevice = fromDevice,
|
||||
// strip out encrypted stuff as it's just a trail?
|
||||
event = event.copy(
|
||||
type = event.getClearType(),
|
||||
content = mapOf(
|
||||
"chain_index" to fromIndex
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -120,12 +195,12 @@ internal class OutgoingGossipingRequestManager @Inject constructor(
|
||||
sequencer.post {
|
||||
Timber.tag(loggerTag.value).d("Withheld received for $sessionId from ${event.senderId}|$fromDevice")
|
||||
cryptoStore.updateOutgoingRoomKeyReply(
|
||||
roomId,
|
||||
sessionId,
|
||||
algorithm,
|
||||
senderKey,
|
||||
fromDevice,
|
||||
event
|
||||
roomId = roomId,
|
||||
sessionId = sessionId,
|
||||
algorithm = algorithm,
|
||||
senderKey = senderKey,
|
||||
fromDevice = fromDevice,
|
||||
event = event
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -142,7 +217,7 @@ internal class OutgoingGossipingRequestManager @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
private fun internalQueueCancelRequest(sessionId: String, roomId: String, senderKey: String) {
|
||||
private fun internalQueueCancelRequest(sessionId: String, roomId: String, senderKey: String, localKnownChainIndex: Int) {
|
||||
// do we have known requests for that session??
|
||||
Timber.tag(loggerTag.value).v("Cancel Key Request if needed for $sessionId")
|
||||
val knownRequest = cryptoStore.getOutgoingRoomKeyRequest(
|
||||
@ -161,18 +236,29 @@ internal class OutgoingGossipingRequestManager @Inject constructor(
|
||||
knownRequest.forEach { request ->
|
||||
when (request.state) {
|
||||
OutgoingRoomKeyRequestState.UNSENT -> {
|
||||
cryptoStore.deleteOutgoingRoomKeyRequest(request.requestId)
|
||||
if (request.fromIndex >= localKnownChainIndex) {
|
||||
// we have a good index we can cancel
|
||||
cryptoStore.deleteOutgoingRoomKeyRequest(request.requestId)
|
||||
}
|
||||
}
|
||||
OutgoingRoomKeyRequestState.SENT -> {
|
||||
// It was already sent, so cancel
|
||||
cryptoStore.updateOutgoingRoomKeyRequestState(request.requestId, OutgoingRoomKeyRequestState.CANCELLATION_PENDING)
|
||||
// It was already sent, and index satisfied we can cancel
|
||||
if (request.fromIndex >= localKnownChainIndex) {
|
||||
cryptoStore.updateOutgoingRoomKeyRequestState(request.requestId, OutgoingRoomKeyRequestState.CANCELLATION_PENDING)
|
||||
}
|
||||
}
|
||||
OutgoingRoomKeyRequestState.CANCELLATION_PENDING -> {
|
||||
// It is already marked to be cancelled
|
||||
}
|
||||
OutgoingRoomKeyRequestState.CANCELLATION_PENDING_AND_WILL_RESEND -> {
|
||||
// we just want to cancel now
|
||||
cryptoStore.updateOutgoingRoomKeyRequestState(request.requestId, OutgoingRoomKeyRequestState.CANCELLATION_PENDING)
|
||||
if (request.fromIndex >= localKnownChainIndex) {
|
||||
// we just want to cancel now
|
||||
cryptoStore.updateOutgoingRoomKeyRequestState(request.requestId, OutgoingRoomKeyRequestState.CANCELLATION_PENDING)
|
||||
}
|
||||
}
|
||||
OutgoingRoomKeyRequestState.SENT_THEN_CANCELED -> {
|
||||
// was already canceled
|
||||
// if we need a better index, should we resend?
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -187,19 +273,24 @@ internal class OutgoingGossipingRequestManager @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
private fun internalQueueRequest(requestBody: RoomKeyRequestBody, recipients: Map<String, List<String>>, force: Boolean) {
|
||||
Timber.tag(loggerTag.value).d("Queueing key request for ${requestBody.sessionId}")
|
||||
val existing = cryptoStore.getOrAddOutgoingRoomKeyRequest(requestBody, recipients)
|
||||
when (existing.state) {
|
||||
private fun internalQueueRequest(requestBody: RoomKeyRequestBody, recipients: Map<String, List<String>>, fromIndex: Int, force: Boolean) {
|
||||
Timber.tag(loggerTag.value).d("Queueing key request for ${requestBody.sessionId} force:$force")
|
||||
val existing = cryptoStore.getOutgoingRoomKeyRequest(requestBody)
|
||||
Timber.tag(loggerTag.value).v("Queueing key request exiting is ${existing?.state}")
|
||||
when (existing?.state) {
|
||||
null -> {
|
||||
// create a new one
|
||||
cryptoStore.getOrAddOutgoingRoomKeyRequest(requestBody, recipients, fromIndex)
|
||||
}
|
||||
OutgoingRoomKeyRequestState.UNSENT -> {
|
||||
// nothing it's new or not yet handled
|
||||
}
|
||||
OutgoingRoomKeyRequestState.SENT -> {
|
||||
// it was already requested
|
||||
Timber.tag(loggerTag.value).w("The session ${requestBody.sessionId} is already requested")
|
||||
Timber.tag(loggerTag.value).d("The session ${requestBody.sessionId} is already requested")
|
||||
if (force) {
|
||||
// update to UNSENT
|
||||
Timber.tag(loggerTag.value).w(".. force to request ${requestBody.sessionId}")
|
||||
Timber.tag(loggerTag.value).d(".. force to request ${requestBody.sessionId}")
|
||||
cryptoStore.updateOutgoingRoomKeyRequestState(existing.requestId, OutgoingRoomKeyRequestState.CANCELLATION_PENDING_AND_WILL_RESEND)
|
||||
} else {
|
||||
requestDiscardedBecauseAlreadySentThatCouldBeTriedWithBackup.push(existing.requestId)
|
||||
@ -214,6 +305,17 @@ internal class OutgoingGossipingRequestManager @Inject constructor(
|
||||
OutgoingRoomKeyRequestState.CANCELLATION_PENDING_AND_WILL_RESEND -> {
|
||||
// It's already going to resend
|
||||
}
|
||||
OutgoingRoomKeyRequestState.SENT_THEN_CANCELED -> {
|
||||
if (force) {
|
||||
cryptoStore.deleteOutgoingRoomKeyRequest(existing.requestId)
|
||||
cryptoStore.getOrAddOutgoingRoomKeyRequest(requestBody, recipients, fromIndex)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (existing != null && existing.fromIndex >= fromIndex) {
|
||||
// update the required index
|
||||
cryptoStore.updateOutgoingRoomKeyRequiredIndex(existing.requestId, fromIndex)
|
||||
}
|
||||
}
|
||||
|
||||
@ -227,6 +329,7 @@ internal class OutgoingGossipingRequestManager @Inject constructor(
|
||||
OutgoingRoomKeyRequestState.UNSENT -> handleUnsentRequest(it)
|
||||
OutgoingRoomKeyRequestState.CANCELLATION_PENDING -> handleRequestToCancel(it)
|
||||
OutgoingRoomKeyRequestState.CANCELLATION_PENDING_AND_WILL_RESEND -> handleRequestToCancelWillResend(it)
|
||||
OutgoingRoomKeyRequestState.SENT_THEN_CANCELED,
|
||||
OutgoingRoomKeyRequestState.SENT -> {
|
||||
// these are filtered out
|
||||
}
|
||||
@ -260,10 +363,16 @@ internal class OutgoingGossipingRequestManager @Inject constructor(
|
||||
val sessionId = request.sessionId ?: return
|
||||
val roomId = request.roomId ?: return
|
||||
if (perSessionBackupQueryRateLimiter.tryFromBackupIfPossible(sessionId, roomId)) {
|
||||
// we found the key in backup, so we can just mark as cancelled, no need to send request
|
||||
Timber.tag(loggerTag.value).v("Megolm session $sessionId successfully restored from backup, do not send request")
|
||||
cryptoStore.deleteOutgoingRoomKeyRequest(request.requestId)
|
||||
return
|
||||
// let's see what's the index
|
||||
val knownIndex = tryOrNull {
|
||||
inboundGroupSessionStore.getInboundGroupSession(sessionId, request.requestBody?.senderKey ?: "")?.wrapper?.firstKnownIndex
|
||||
}
|
||||
if (knownIndex != null && knownIndex <= request.fromIndex) {
|
||||
// we found the key in backup with good enough index, so we can just mark as cancelled, no need to send request
|
||||
Timber.tag(loggerTag.value).v("Megolm session $sessionId successfully restored from backup, do not send request")
|
||||
cryptoStore.deleteOutgoingRoomKeyRequest(request.requestId)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// we need to send the request
|
||||
@ -322,9 +431,9 @@ internal class OutgoingGossipingRequestManager @Inject constructor(
|
||||
withContext(coroutineDispatchers.io) {
|
||||
sendToDeviceTask.executeRetry(params, 3)
|
||||
}
|
||||
// The request cancellation was sent, we can forget about it
|
||||
cryptoStore.deleteOutgoingRoomKeyRequest(request.requestId)
|
||||
// TODO update the audit trail
|
||||
// The request cancellation was sent, we don't delete yet because we want
|
||||
// to keep trace of the sent replies
|
||||
cryptoStore.updateOutgoingRoomKeyRequestState(request.requestId, OutgoingRoomKeyRequestState.SENT_THEN_CANCELED)
|
||||
true
|
||||
} catch (failure: Throwable) {
|
||||
Timber.tag(loggerTag.value).v("Failed to cancel request ${request.requestId} for session $sessionId targets:${request.recipients}")
|
||||
@ -334,10 +443,9 @@ internal class OutgoingGossipingRequestManager @Inject constructor(
|
||||
|
||||
private suspend fun handleRequestToCancelWillResend(request: OutgoingKeyRequest) {
|
||||
if (handleRequestToCancel(request)) {
|
||||
// we have to create a new unsent one
|
||||
val body = request.requestBody ?: return
|
||||
// this will create a new unsent request that will be process in the following call
|
||||
cryptoStore.getOrAddOutgoingRoomKeyRequest(body, request.recipients)
|
||||
// this will create a new unsent request with no replies that will be process in the following call
|
||||
cryptoStore.deleteOutgoingRoomKeyRequest(request.requestId)
|
||||
request.requestBody?.let { cryptoStore.getOrAddOutgoingRoomKeyRequest(it, request.recipients, request.fromIndex) }
|
||||
}
|
||||
}
|
||||
}
|
@ -14,11 +14,12 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.matrix.android.sdk.api.session.crypto.model
|
||||
package org.matrix.android.sdk.internal.crypto
|
||||
|
||||
enum class OutgoingRoomKeyRequestState {
|
||||
UNSENT,
|
||||
SENT,
|
||||
SENT_THEN_CANCELED,
|
||||
CANCELLATION_PENDING,
|
||||
CANCELLATION_PENDING_AND_WILL_RESEND;
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2022 New Vector Ltd
|
||||
* Copyright (c) 2022 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.
|
||||
@ -28,6 +28,7 @@ import org.matrix.android.sdk.api.session.crypto.crosssigning.KEYBACKUP_SECRET_S
|
||||
import org.matrix.android.sdk.api.session.crypto.crosssigning.MASTER_KEY_SSSS_NAME
|
||||
import org.matrix.android.sdk.api.session.crypto.crosssigning.SELF_SIGNING_KEY_SSSS_NAME
|
||||
import org.matrix.android.sdk.api.session.crypto.crosssigning.USER_SIGNING_KEY_SSSS_NAME
|
||||
import org.matrix.android.sdk.api.session.crypto.keyshare.GossipingRequestListener
|
||||
import org.matrix.android.sdk.api.session.events.model.Event
|
||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||
@ -75,6 +76,21 @@ internal class SecretShareManager @Inject constructor(
|
||||
*/
|
||||
private val outgoingSecretRequests = mutableListOf<SecretShareRequest>()
|
||||
|
||||
// the listeners
|
||||
private val gossipingRequestListeners: MutableSet<GossipingRequestListener> = HashSet()
|
||||
|
||||
fun addListener(listener: GossipingRequestListener) {
|
||||
synchronized(gossipingRequestListeners) {
|
||||
gossipingRequestListeners.add(listener)
|
||||
}
|
||||
}
|
||||
|
||||
fun removeRoomKeysRequestListener(listener: GossipingRequestListener) {
|
||||
synchronized(gossipingRequestListeners) {
|
||||
gossipingRequestListeners.remove(listener)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a session has been verified.
|
||||
* This information can be used by the manager to decide whether or not to fullfill gossiping requests.
|
||||
@ -140,7 +156,11 @@ internal class SecretShareManager @Inject constructor(
|
||||
else -> null
|
||||
}
|
||||
if (secretValue == null) {
|
||||
Timber.e("The secret is unknown $secretName")
|
||||
Timber.i("The secret is unknown $secretName, passing to app layer")
|
||||
val toList = synchronized(gossipingRequestListeners) { gossipingRequestListeners.toList() }
|
||||
toList.onEach { listener ->
|
||||
listener.onSecretShareRequest(request)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -17,12 +17,13 @@
|
||||
package org.matrix.android.sdk.internal.crypto.actions
|
||||
|
||||
import androidx.annotation.WorkerThread
|
||||
import org.matrix.android.sdk.api.extensions.tryOrNull
|
||||
import org.matrix.android.sdk.api.listeners.ProgressListener
|
||||
import org.matrix.android.sdk.api.session.crypto.model.ImportRoomKeysResult
|
||||
import org.matrix.android.sdk.api.logger.LoggerTag
|
||||
import org.matrix.android.sdk.internal.crypto.MXOlmDevice
|
||||
import org.matrix.android.sdk.internal.crypto.MegolmSessionData
|
||||
import org.matrix.android.sdk.internal.crypto.OutgoingGossipingRequestManager
|
||||
import org.matrix.android.sdk.internal.crypto.OutgoingKeyRequestManager
|
||||
import org.matrix.android.sdk.internal.crypto.RoomDecryptorProvider
|
||||
import org.matrix.android.sdk.internal.crypto.algorithms.megolm.MXMegolmDecryption
|
||||
import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
|
||||
@ -33,7 +34,7 @@ private val loggerTag = LoggerTag("MegolmSessionDataImporter", LoggerTag.CRYPTO)
|
||||
|
||||
internal class MegolmSessionDataImporter @Inject constructor(private val olmDevice: MXOlmDevice,
|
||||
private val roomDecryptorProvider: RoomDecryptorProvider,
|
||||
private val outgoingGossipingRequestManager: OutgoingGossipingRequestManager,
|
||||
private val outgoingKeyRequestManager: OutgoingKeyRequestManager,
|
||||
private val cryptoStore: IMXCryptoStore) {
|
||||
|
||||
/**
|
||||
@ -71,10 +72,15 @@ internal class MegolmSessionDataImporter @Inject constructor(private val olmDevi
|
||||
// cancel any outstanding room key requests for this session
|
||||
|
||||
Timber.tag(loggerTag.value).d("Imported megolm session $sessionId from backup=$fromBackup in ${megolmSessionData.roomId}")
|
||||
outgoingGossipingRequestManager.postCancelRequestForSessionIfNeeded(
|
||||
outgoingKeyRequestManager.postCancelRequestForSessionIfNeeded(
|
||||
megolmSessionData.sessionId ?: "",
|
||||
megolmSessionData.roomId ?: "",
|
||||
megolmSessionData.senderKey ?: ""
|
||||
megolmSessionData.senderKey ?: "",
|
||||
tryOrNull {
|
||||
olmInboundGroupSessionWrappers
|
||||
.firstOrNull { it.olmInboundGroupSession?.sessionIdentifier() == megolmSessionData.sessionId }
|
||||
?.firstKnownIndex?.toInt()
|
||||
} ?: 0
|
||||
)
|
||||
|
||||
// Have another go at decrypting events sent with this session
|
||||
|
@ -17,8 +17,6 @@
|
||||
package org.matrix.android.sdk.internal.crypto.algorithms
|
||||
|
||||
import org.matrix.android.sdk.api.session.crypto.MXCryptoError
|
||||
import org.matrix.android.sdk.api.session.crypto.model.IncomingRoomKeyRequest
|
||||
import org.matrix.android.sdk.api.session.crypto.model.IncomingSecretShareRequest
|
||||
import org.matrix.android.sdk.api.session.crypto.model.MXEventDecryptionResult
|
||||
import org.matrix.android.sdk.api.session.events.model.Event
|
||||
import org.matrix.android.sdk.internal.crypto.keysbackup.DefaultKeysBackupService
|
||||
@ -44,23 +42,4 @@ internal interface IMXDecrypting {
|
||||
* @param event the key event.
|
||||
*/
|
||||
fun onRoomKeyEvent(event: Event, defaultKeysBackupService: DefaultKeysBackupService) {}
|
||||
|
||||
/**
|
||||
* Determine if we have the keys necessary to respond to a room key request
|
||||
*
|
||||
* @param request keyRequest
|
||||
* @return true if we have the keys and could (theoretically) share
|
||||
*/
|
||||
fun hasKeysForKeyRequest(request: IncomingRoomKeyRequest): Boolean = false
|
||||
|
||||
/**
|
||||
* Send the response to a room key request.
|
||||
*
|
||||
* @param request keyRequest
|
||||
*/
|
||||
fun shareKeysWithDevice(request: IncomingRoomKeyRequest) {}
|
||||
|
||||
fun shareSecretWithDevice(request: IncomingSecretShareRequest, secretValue: String) {}
|
||||
|
||||
fun requestKeysForEvent(event: Event, withHeld: Boolean)
|
||||
}
|
||||
|
@ -17,48 +17,31 @@
|
||||
package org.matrix.android.sdk.internal.crypto.algorithms.megolm
|
||||
|
||||
import dagger.Lazy
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import org.matrix.android.sdk.api.MatrixCoroutineDispatchers
|
||||
import org.matrix.android.sdk.api.logger.LoggerTag
|
||||
import org.matrix.android.sdk.api.session.crypto.MXCryptoError
|
||||
import org.matrix.android.sdk.api.session.crypto.NewSessionListener
|
||||
import org.matrix.android.sdk.api.session.crypto.model.ForwardedRoomKeyContent
|
||||
import org.matrix.android.sdk.api.session.crypto.model.IncomingRoomKeyRequest
|
||||
import org.matrix.android.sdk.api.session.crypto.model.MXEventDecryptionResult
|
||||
import org.matrix.android.sdk.api.session.crypto.model.MXUsersDevicesMap
|
||||
import org.matrix.android.sdk.api.session.crypto.model.RoomKeyRequestBody
|
||||
import org.matrix.android.sdk.api.session.events.model.Event
|
||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||
import org.matrix.android.sdk.api.session.events.model.content.EncryptedEventContent
|
||||
import org.matrix.android.sdk.api.session.events.model.content.RoomKeyContent
|
||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||
import org.matrix.android.sdk.internal.crypto.DeviceListManager
|
||||
import org.matrix.android.sdk.internal.crypto.MXOlmDevice
|
||||
import org.matrix.android.sdk.internal.crypto.OutgoingGossipingRequestManager
|
||||
import org.matrix.android.sdk.internal.crypto.actions.EnsureOlmSessionsForDevicesAction
|
||||
import org.matrix.android.sdk.internal.crypto.actions.MessageEncrypter
|
||||
import org.matrix.android.sdk.internal.crypto.algorithms.IMXDecrypting
|
||||
import org.matrix.android.sdk.internal.crypto.keysbackup.DefaultKeysBackupService
|
||||
import org.matrix.android.sdk.internal.crypto.OutgoingKeyRequestManager
|
||||
import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
|
||||
import org.matrix.android.sdk.internal.crypto.tasks.SendToDeviceTask
|
||||
import org.matrix.android.sdk.internal.session.StreamEventsManager
|
||||
import timber.log.Timber
|
||||
|
||||
private val loggerTag = LoggerTag("MXMegolmDecryption", LoggerTag.CRYPTO)
|
||||
|
||||
internal class MXMegolmDecryption(private val userId: String,
|
||||
private val olmDevice: MXOlmDevice,
|
||||
private val deviceListManager: DeviceListManager,
|
||||
private val outgoingGossipingRequestManager: OutgoingGossipingRequestManager,
|
||||
private val messageEncrypter: MessageEncrypter,
|
||||
private val ensureOlmSessionsForDevicesAction: EnsureOlmSessionsForDevicesAction,
|
||||
private val cryptoStore: IMXCryptoStore,
|
||||
private val sendToDeviceTask: SendToDeviceTask,
|
||||
private val coroutineDispatchers: MatrixCoroutineDispatchers,
|
||||
private val cryptoCoroutineScope: CoroutineScope,
|
||||
private val liveEventManager: Lazy<StreamEventsManager>
|
||||
internal class MXMegolmDecryption(
|
||||
private val olmDevice: MXOlmDevice,
|
||||
private val outgoingKeyRequestManager: OutgoingKeyRequestManager,
|
||||
private val cryptoStore: IMXCryptoStore,
|
||||
private val liveEventManager: Lazy<StreamEventsManager>
|
||||
) : IMXDecrypting {
|
||||
|
||||
var newSessionListener: NewSessionListener? = null
|
||||
@ -122,23 +105,9 @@ internal class MXMegolmDecryption(private val userId: String,
|
||||
if (throwable is MXCryptoError.OlmError) {
|
||||
// TODO Check the value of .message
|
||||
if (throwable.olmException.message == "UNKNOWN_MESSAGE_INDEX") {
|
||||
// addEventToPendingList(event, timeline)
|
||||
// The session might has been partially withheld (and only pass ratcheted)
|
||||
val withHeldInfo = cryptoStore.getWithHeldMegolmSession(event.roomId, encryptedEventContent.sessionId)
|
||||
if (withHeldInfo != null) {
|
||||
if (requestKeysOnFail) {
|
||||
requestKeysForEvent(event, true)
|
||||
}
|
||||
// Encapsulate as withHeld exception
|
||||
throw MXCryptoError.Base(MXCryptoError.ErrorType.KEYS_WITHHELD,
|
||||
withHeldInfo.code?.value ?: "",
|
||||
withHeldInfo.reason)
|
||||
}
|
||||
|
||||
if (requestKeysOnFail) {
|
||||
requestKeysForEvent(event, false)
|
||||
requestKeysForEvent(event)
|
||||
}
|
||||
|
||||
throw MXCryptoError.Base(
|
||||
MXCryptoError.ErrorType.UNKNOWN_MESSAGE_INDEX,
|
||||
"UNKNOWN_MESSAGE_INDEX",
|
||||
@ -154,25 +123,9 @@ internal class MXMegolmDecryption(private val userId: String,
|
||||
detailedReason)
|
||||
}
|
||||
if (throwable is MXCryptoError.Base) {
|
||||
if (
|
||||
/** if the session is unknown*/
|
||||
throwable.errorType == MXCryptoError.ErrorType.UNKNOWN_INBOUND_SESSION_ID
|
||||
) {
|
||||
val withHeldInfo = cryptoStore.getWithHeldMegolmSession(event.roomId, encryptedEventContent.sessionId)
|
||||
if (withHeldInfo != null) {
|
||||
if (requestKeysOnFail) {
|
||||
requestKeysForEvent(event, true)
|
||||
}
|
||||
// Encapsulate as withHeld exception
|
||||
throw MXCryptoError.Base(MXCryptoError.ErrorType.KEYS_WITHHELD,
|
||||
withHeldInfo.code?.value ?: "",
|
||||
withHeldInfo.reason)
|
||||
} else {
|
||||
// This is un-used in Matrix Android SDK2, not sure if needed
|
||||
// addEventToPendingList(event, timeline)
|
||||
if (requestKeysOnFail) {
|
||||
requestKeysForEvent(event, false)
|
||||
}
|
||||
if (throwable.errorType == MXCryptoError.ErrorType.UNKNOWN_INBOUND_SESSION_ID) {
|
||||
if (requestKeysOnFail) {
|
||||
requestKeysForEvent(event)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -188,60 +141,10 @@ internal class MXMegolmDecryption(private val userId: String,
|
||||
*
|
||||
* @param event the event
|
||||
*/
|
||||
override fun requestKeysForEvent(event: Event, withHeld: Boolean) {
|
||||
val sender = event.senderId ?: return
|
||||
val encryptedEventContent = event.content.toModel<EncryptedEventContent>() ?: return Unit.also {
|
||||
Timber.tag(loggerTag.value).e("requestRoomKeyForEvent Failed to re-request key, null content")
|
||||
}
|
||||
val senderDevice = encryptedEventContent.deviceId
|
||||
|
||||
val recipients = if (event.senderId == userId || withHeld) {
|
||||
mapOf(
|
||||
userId to listOf("*")
|
||||
)
|
||||
} else {
|
||||
// for the case where you share the key with a device that has a broken olm session
|
||||
// The other user might Re-shares a megolm session key with devices if the key has already been
|
||||
// sent to them.
|
||||
mapOf(
|
||||
userId to listOf("*"),
|
||||
|
||||
// TODO we might not have deviceId in the future due to https://github.com/matrix-org/matrix-spec-proposals/pull/3700
|
||||
// so in this case query to all
|
||||
sender to listOf(senderDevice ?: "*")
|
||||
)
|
||||
}
|
||||
|
||||
val requestBody = RoomKeyRequestBody(
|
||||
roomId = event.roomId,
|
||||
algorithm = encryptedEventContent.algorithm,
|
||||
senderKey = encryptedEventContent.senderKey,
|
||||
sessionId = encryptedEventContent.sessionId
|
||||
)
|
||||
|
||||
outgoingGossipingRequestManager.postRoomKeyRequest(requestBody, recipients, force = withHeld)
|
||||
private fun requestKeysForEvent(event: Event) {
|
||||
outgoingKeyRequestManager.requestKeyForEvent(event, false)
|
||||
}
|
||||
|
||||
// /**
|
||||
// * Add an event to the list of those we couldn't decrypt the first time we
|
||||
// * saw them.
|
||||
// *
|
||||
// * @param event the event to try to decrypt later
|
||||
// * @param timelineId the timeline identifier
|
||||
// */
|
||||
// private fun addEventToPendingList(event: Event, timelineId: String) {
|
||||
// val encryptedEventContent = event.content.toModel<EncryptedEventContent>() ?: return
|
||||
// val pendingEventsKey = "${encryptedEventContent.senderKey}|${encryptedEventContent.sessionId}"
|
||||
//
|
||||
// val timeline = pendingEvents.getOrPut(pendingEventsKey) { HashMap() }
|
||||
// val events = timeline.getOrPut(timelineId) { ArrayList() }
|
||||
//
|
||||
// if (event !in events) {
|
||||
// Timber.v("## CRYPTO | addEventToPendingList() : add Event ${event.eventId} in room id ${event.roomId}")
|
||||
// events.add(event)
|
||||
// }
|
||||
// }
|
||||
|
||||
/**
|
||||
* Handle a key event.
|
||||
*
|
||||
@ -289,16 +192,6 @@ internal class MXMegolmDecryption(private val userId: String,
|
||||
}
|
||||
|
||||
keysClaimed["ed25519"] = forwardedRoomKeyContent.senderClaimedEd25519Key
|
||||
|
||||
val fromDevice = event.getSenderKey()?.let { cryptoStore.deviceWithIdentityKey(it) }?.deviceId
|
||||
|
||||
outgoingGossipingRequestManager.onRoomKeyForwarded(
|
||||
sessionId = roomKeyContent.sessionId,
|
||||
algorithm = roomKeyContent.algorithm ?: "",
|
||||
roomId = roomKeyContent.roomId,
|
||||
senderKey = forwardedRoomKeyContent.senderKey ?: "",
|
||||
fromDevice = fromDevice,
|
||||
event = event)
|
||||
} else {
|
||||
Timber.tag(loggerTag.value).i("onRoomKeyEvent(), Adding key : ${roomKeyContent.roomId}|${roomKeyContent.sessionId}")
|
||||
if (null == senderKey) {
|
||||
@ -319,13 +212,38 @@ internal class MXMegolmDecryption(private val userId: String,
|
||||
keysClaimed,
|
||||
exportFormat)
|
||||
|
||||
if (added) {
|
||||
when (added) {
|
||||
is MXOlmDevice.AddSessionResult.Imported -> added.ratchetIndex
|
||||
is MXOlmDevice.AddSessionResult.NotImportedHigherIndex -> added.newIndex
|
||||
else -> null
|
||||
}?.let { index ->
|
||||
if (event.getClearType() == EventType.FORWARDED_ROOM_KEY) {
|
||||
val fromDevice = (event.content?.get("sender_key") as? String)?.let { senderDeviceIdentityKey ->
|
||||
cryptoStore.getUserDeviceList(event.senderId ?: "")
|
||||
?.firstOrNull {
|
||||
it.identityKey() == senderDeviceIdentityKey
|
||||
}
|
||||
}?.deviceId
|
||||
|
||||
outgoingKeyRequestManager.onRoomKeyForwarded(
|
||||
sessionId = roomKeyContent.sessionId,
|
||||
algorithm = roomKeyContent.algorithm ?: "",
|
||||
roomId = roomKeyContent.roomId,
|
||||
senderKey = senderKey,
|
||||
fromIndex = index,
|
||||
fromDevice = fromDevice,
|
||||
event = event)
|
||||
|
||||
// The index is used to decide if we cancel sent request or if we wait for a better key
|
||||
outgoingKeyRequestManager.postCancelRequestForSessionIfNeeded(roomKeyContent.sessionId, roomKeyContent.roomId, senderKey, index)
|
||||
}
|
||||
}
|
||||
|
||||
if (added is MXOlmDevice.AddSessionResult.Imported) {
|
||||
Timber.tag(loggerTag.value)
|
||||
.d("onRoomKeyEvent(${event.getClearType()}) : Added megolm session ${roomKeyContent.sessionId} in ${roomKeyContent.roomId}")
|
||||
defaultKeysBackupService.maybeBackupKeys()
|
||||
|
||||
outgoingGossipingRequestManager.postCancelRequestForSessionIfNeeded(roomKeyContent.sessionId, roomKeyContent.roomId, senderKey)
|
||||
|
||||
onNewSession(roomKeyContent.roomId, senderKey, roomKeyContent.sessionId)
|
||||
}
|
||||
}
|
||||
@ -341,72 +259,4 @@ internal class MXMegolmDecryption(private val userId: String,
|
||||
Timber.tag(loggerTag.value).v("ON NEW SESSION $sessionId - $senderKey")
|
||||
newSessionListener?.onNewSession(roomId, senderKey, sessionId)
|
||||
}
|
||||
|
||||
override fun hasKeysForKeyRequest(request: IncomingRoomKeyRequest): Boolean {
|
||||
val roomId = request.requestBody?.roomId ?: return false
|
||||
val senderKey = request.requestBody.senderKey ?: return false
|
||||
val sessionId = request.requestBody.sessionId ?: return false
|
||||
return olmDevice.hasInboundSessionKeys(roomId, senderKey, sessionId)
|
||||
}
|
||||
|
||||
override fun shareKeysWithDevice(request: IncomingRoomKeyRequest) {
|
||||
// sanity checks
|
||||
if (request.requestBody == null) {
|
||||
return
|
||||
}
|
||||
val userId = request.userId ?: return
|
||||
|
||||
cryptoCoroutineScope.launch(coroutineDispatchers.crypto) {
|
||||
val body = request.requestBody
|
||||
val sessionHolder = try {
|
||||
olmDevice.getInboundGroupSession(body.sessionId, body.senderKey, body.roomId)
|
||||
} catch (failure: Throwable) {
|
||||
Timber.tag(loggerTag.value).e(failure, "shareKeysWithDevice: failed to get session for request $body")
|
||||
return@launch
|
||||
}
|
||||
|
||||
val export = sessionHolder.mutex.withLock {
|
||||
sessionHolder.wrapper.exportKeys()
|
||||
} ?: return@launch Unit.also {
|
||||
Timber.tag(loggerTag.value).e("shareKeysWithDevice: failed to export group session ${body.sessionId}")
|
||||
}
|
||||
|
||||
runCatching { deviceListManager.downloadKeys(listOf(userId), false) }
|
||||
.mapCatching {
|
||||
val deviceId = request.deviceId
|
||||
val deviceInfo = cryptoStore.getUserDevice(userId, deviceId ?: "")
|
||||
if (deviceInfo == null) {
|
||||
throw RuntimeException()
|
||||
} else {
|
||||
val devicesByUser = mapOf(userId to listOf(deviceInfo))
|
||||
val usersDeviceMap = ensureOlmSessionsForDevicesAction.handle(devicesByUser)
|
||||
val olmSessionResult = usersDeviceMap.getObject(userId, deviceId)
|
||||
if (olmSessionResult?.sessionId == null) {
|
||||
// no session with this device, probably because there
|
||||
// were no one-time keys.
|
||||
Timber.tag(loggerTag.value).e("no session with this device $deviceId, probably because there were no one-time keys.")
|
||||
return@mapCatching
|
||||
}
|
||||
Timber.tag(loggerTag.value).i("shareKeysWithDevice() : sharing session ${body.sessionId} with device $userId:$deviceId")
|
||||
|
||||
val payloadJson = mapOf(
|
||||
"type" to EventType.FORWARDED_ROOM_KEY,
|
||||
"content" to export
|
||||
)
|
||||
|
||||
val encodedPayload = messageEncrypter.encryptMessage(payloadJson, listOf(deviceInfo))
|
||||
val sendToDeviceMap = MXUsersDevicesMap<Any>()
|
||||
sendToDeviceMap.setObject(userId, deviceId, encodedPayload)
|
||||
Timber.tag(loggerTag.value).i("shareKeysWithDevice() : sending ${body.sessionId} to $userId:$deviceId")
|
||||
val sendToDeviceParams = SendToDeviceTask.Params(EventType.ENCRYPTED, sendToDeviceMap)
|
||||
try {
|
||||
sendToDeviceTask.execute(sendToDeviceParams)
|
||||
} catch (failure: Throwable) {
|
||||
Timber.tag(loggerTag.value).e(failure, "shareKeysWithDevice() : Failed to send ${body.sessionId} to $userId:$deviceId")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -17,45 +17,24 @@
|
||||
package org.matrix.android.sdk.internal.crypto.algorithms.megolm
|
||||
|
||||
import dagger.Lazy
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import org.matrix.android.sdk.api.MatrixCoroutineDispatchers
|
||||
import org.matrix.android.sdk.internal.crypto.DeviceListManager
|
||||
import org.matrix.android.sdk.internal.crypto.MXOlmDevice
|
||||
import org.matrix.android.sdk.internal.crypto.OutgoingGossipingRequestManager
|
||||
import org.matrix.android.sdk.internal.crypto.actions.EnsureOlmSessionsForDevicesAction
|
||||
import org.matrix.android.sdk.internal.crypto.actions.MessageEncrypter
|
||||
import org.matrix.android.sdk.internal.crypto.OutgoingKeyRequestManager
|
||||
import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
|
||||
import org.matrix.android.sdk.internal.crypto.tasks.SendToDeviceTask
|
||||
import org.matrix.android.sdk.internal.di.UserId
|
||||
import org.matrix.android.sdk.internal.session.StreamEventsManager
|
||||
import javax.inject.Inject
|
||||
|
||||
internal class MXMegolmDecryptionFactory @Inject constructor(
|
||||
@UserId private val userId: String,
|
||||
private val olmDevice: MXOlmDevice,
|
||||
private val deviceListManager: DeviceListManager,
|
||||
private val outgoingGossipingRequestManager: OutgoingGossipingRequestManager,
|
||||
private val messageEncrypter: MessageEncrypter,
|
||||
private val ensureOlmSessionsForDevicesAction: EnsureOlmSessionsForDevicesAction,
|
||||
private val outgoingKeyRequestManager: OutgoingKeyRequestManager,
|
||||
private val cryptoStore: IMXCryptoStore,
|
||||
private val sendToDeviceTask: SendToDeviceTask,
|
||||
private val coroutineDispatchers: MatrixCoroutineDispatchers,
|
||||
private val cryptoCoroutineScope: CoroutineScope,
|
||||
private val eventsManager: Lazy<StreamEventsManager>
|
||||
) {
|
||||
|
||||
fun create(): MXMegolmDecryption {
|
||||
return MXMegolmDecryption(
|
||||
userId,
|
||||
olmDevice,
|
||||
deviceListManager,
|
||||
outgoingGossipingRequestManager,
|
||||
messageEncrypter,
|
||||
ensureOlmSessionsForDevicesAction,
|
||||
outgoingKeyRequestManager,
|
||||
cryptoStore,
|
||||
sendToDeviceTask,
|
||||
coroutineDispatchers,
|
||||
cryptoCoroutineScope,
|
||||
eventsManager)
|
||||
}
|
||||
}
|
||||
|
@ -241,8 +241,4 @@ internal class MXOlmDecryption(
|
||||
|
||||
return res["payload"]
|
||||
}
|
||||
|
||||
override fun requestKeysForEvent(event: Event, withHeld: Boolean) {
|
||||
// nop
|
||||
}
|
||||
}
|
||||
|
@ -65,7 +65,8 @@ data class IncomingKeyRequestInfo(
|
||||
override val senderKey: String,
|
||||
override val alg: String,
|
||||
override val userId: String,
|
||||
override val deviceId: String
|
||||
override val deviceId: String,
|
||||
val requestId: String
|
||||
) : AuditInfo
|
||||
|
||||
data class AuditTrail(
|
||||
|
@ -25,22 +25,19 @@ import org.matrix.android.sdk.api.session.crypto.crosssigning.PrivateKeysInfo
|
||||
import org.matrix.android.sdk.api.session.crypto.keysbackup.SavedKeyBackupKeyInfo
|
||||
import org.matrix.android.sdk.api.session.crypto.model.CryptoDeviceInfo
|
||||
import org.matrix.android.sdk.api.session.crypto.model.DeviceInfo
|
||||
import org.matrix.android.sdk.api.session.crypto.model.GossipingRequestState
|
||||
import org.matrix.android.sdk.api.session.crypto.model.IncomingRoomKeyRequest
|
||||
import org.matrix.android.sdk.api.session.crypto.model.MXUsersDevicesMap
|
||||
import org.matrix.android.sdk.api.session.crypto.model.OutgoingRoomKeyRequestState
|
||||
import org.matrix.android.sdk.api.session.crypto.model.RoomKeyRequestBody
|
||||
import org.matrix.android.sdk.api.session.events.model.Event
|
||||
import org.matrix.android.sdk.api.session.events.model.content.RoomKeyWithHeldContent
|
||||
import org.matrix.android.sdk.api.session.events.model.content.WithHeldCode
|
||||
import org.matrix.android.sdk.api.util.Optional
|
||||
import org.matrix.android.sdk.internal.crypto.IncomingShareRequestCommon
|
||||
import org.matrix.android.sdk.internal.crypto.OutgoingKeyRequest
|
||||
import org.matrix.android.sdk.internal.crypto.OutgoingSecretRequest
|
||||
import org.matrix.android.sdk.api.session.crypto.model.OutgoingRoomKeyRequestState
|
||||
import org.matrix.android.sdk.internal.crypto.model.AuditTrail
|
||||
import org.matrix.android.sdk.internal.crypto.model.OlmInboundGroupSessionWrapper2
|
||||
import org.matrix.android.sdk.internal.crypto.model.OlmSessionWrapper
|
||||
import org.matrix.android.sdk.internal.crypto.model.OutboundGroupSessionWrapper
|
||||
import org.matrix.android.sdk.internal.crypto.model.TrailType
|
||||
import org.matrix.android.sdk.internal.crypto.store.db.model.KeysBackupDataEntity
|
||||
import org.matrix.olm.OlmAccount
|
||||
import org.matrix.olm.OlmOutboundGroupSession
|
||||
@ -127,18 +124,6 @@ internal interface IMXCryptoStore {
|
||||
*/
|
||||
fun getDeviceTrackingStatuses(): Map<String, Int>
|
||||
|
||||
/**
|
||||
* @return the pending IncomingRoomKeyRequest requests
|
||||
*/
|
||||
fun getPendingIncomingRoomKeyRequests(): List<IncomingRoomKeyRequest>
|
||||
|
||||
fun getPendingIncomingGossipingRequests(): List<IncomingShareRequestCommon>
|
||||
|
||||
fun storeIncomingGossipingRequest(request: IncomingShareRequestCommon, ageLocalTS: Long?)
|
||||
|
||||
fun storeIncomingGossipingRequests(requests: List<IncomingShareRequestCommon>)
|
||||
// fun getPendingIncomingSecretShareRequests(): List<IncomingSecretShareRequest>
|
||||
|
||||
/**
|
||||
* Indicate if the store contains data for the passed account.
|
||||
*
|
||||
@ -389,25 +374,21 @@ internal interface IMXCryptoStore {
|
||||
* @param request the request
|
||||
* @return either the same instance as passed in, or the existing one.
|
||||
*/
|
||||
fun getOrAddOutgoingRoomKeyRequest(requestBody: RoomKeyRequestBody, recipients: Map<String, List<String>>): OutgoingKeyRequest
|
||||
fun getOrAddOutgoingRoomKeyRequest(requestBody: RoomKeyRequestBody, recipients: Map<String, List<String>>, fromIndex: Int): OutgoingKeyRequest
|
||||
fun updateOutgoingRoomKeyRequestState(requestId: String, newState: OutgoingRoomKeyRequestState)
|
||||
fun updateOutgoingRoomKeyRequiredIndex(requestId: String, newIndex: Int)
|
||||
fun updateOutgoingRoomKeyReply(
|
||||
roomId: String,
|
||||
sessionId: String,
|
||||
algorithm: String,
|
||||
senderKey: String, fromDevice: String?, event: Event)
|
||||
senderKey: String,
|
||||
fromDevice: String?,
|
||||
event: Event)
|
||||
|
||||
fun deleteOutgoingRoomKeyRequest(requestId: String)
|
||||
|
||||
fun getOrAddOutgoingSecretShareRequest(secretName: String, recipients: Map<String, List<String>>): OutgoingSecretRequest?
|
||||
|
||||
@Deprecated("TODO")
|
||||
fun saveGossipingEvent(event: Event) = saveGossipingEvents(listOf(event))
|
||||
|
||||
@Deprecated("TODO")
|
||||
fun saveGossipingEvents(events: List<Event>)
|
||||
|
||||
fun saveIncomingKeyRequestAuditTrail(
|
||||
requestId: String,
|
||||
roomId: String,
|
||||
sessionId: String,
|
||||
senderKey: String,
|
||||
@ -436,32 +417,6 @@ internal interface IMXCryptoStore {
|
||||
chainIndex: Long?
|
||||
)
|
||||
|
||||
fun updateGossipingRequestState(request: IncomingShareRequestCommon, state: GossipingRequestState) {
|
||||
updateGossipingRequestState(
|
||||
requestUserId = request.userId,
|
||||
requestDeviceId = request.deviceId,
|
||||
requestId = request.requestId,
|
||||
state = state
|
||||
)
|
||||
}
|
||||
|
||||
fun updateGossipingRequestState(requestUserId: String?,
|
||||
requestDeviceId: String?,
|
||||
requestId: String?,
|
||||
state: GossipingRequestState)
|
||||
|
||||
/**
|
||||
* Search an IncomingRoomKeyRequest
|
||||
*
|
||||
* @param userId the user id
|
||||
* @param deviceId the device id
|
||||
* @param requestId the request id
|
||||
* @return an IncomingRoomKeyRequest if it exists, else null
|
||||
*/
|
||||
fun getIncomingRoomKeyRequest(userId: String, deviceId: String, requestId: String): IncomingRoomKeyRequest?
|
||||
|
||||
fun updateOutgoingGossipingRequestState(requestId: String, state: OutgoingRoomKeyRequestState)
|
||||
|
||||
fun addNewSessionListener(listener: NewSessionListener)
|
||||
|
||||
fun removeSessionListener(listener: NewSessionListener)
|
||||
@ -522,11 +477,8 @@ internal interface IMXCryptoStore {
|
||||
|
||||
fun getOutgoingRoomKeyRequests(): List<OutgoingKeyRequest>
|
||||
fun getOutgoingRoomKeyRequestsPaged(): LiveData<PagedList<OutgoingKeyRequest>>
|
||||
fun getOutgoingSecretKeyRequests(): List<OutgoingSecretRequest>
|
||||
fun getOutgoingSecretRequest(secretName: String): OutgoingSecretRequest?
|
||||
fun getIncomingRoomKeyRequests(): List<IncomingRoomKeyRequest>
|
||||
fun getIncomingRoomKeyRequestsPaged(): LiveData<PagedList<IncomingRoomKeyRequest>>
|
||||
fun getGossipingEventsTrail(): LiveData<PagedList<AuditTrail>>
|
||||
fun <T> getGossipingEventsTrail(type: TrailType, mapper: ((AuditTrail) -> T)): LiveData<PagedList<T>>
|
||||
fun getGossipingEvents(): List<AuditTrail>
|
||||
|
||||
fun setDeviceKeysUploaded(uploaded: Boolean)
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -22,8 +22,6 @@ import org.matrix.android.sdk.internal.crypto.store.db.model.CrossSigningInfoEnt
|
||||
import org.matrix.android.sdk.internal.crypto.store.db.model.CryptoMetadataEntity
|
||||
import org.matrix.android.sdk.internal.crypto.store.db.model.CryptoRoomEntity
|
||||
import org.matrix.android.sdk.internal.crypto.store.db.model.DeviceInfoEntity
|
||||
import org.matrix.android.sdk.internal.crypto.store.db.model.GossipingEventEntity
|
||||
import org.matrix.android.sdk.internal.crypto.store.db.model.IncomingGossipingRequestEntity
|
||||
import org.matrix.android.sdk.internal.crypto.store.db.model.KeyInfoEntity
|
||||
import org.matrix.android.sdk.internal.crypto.store.db.model.KeyRequestReplyEntity
|
||||
import org.matrix.android.sdk.internal.crypto.store.db.model.KeysBackupDataEntity
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2022 New Vector Ltd
|
||||
* Copyright (c) 2022 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.
|
||||
@ -45,7 +45,6 @@ class MigrateCryptoTo016(realm: DynamicRealm) : RealmMigrator(realm, 15) {
|
||||
.addField(OutgoingKeyRequestEntityFields.CREATION_TIME_STAMP, Long::class.java)
|
||||
.setNullable(OutgoingKeyRequestEntityFields.CREATION_TIME_STAMP, true)
|
||||
|
||||
|
||||
realm.schema.create("AuditTrailEntity")
|
||||
.addField(AuditTrailEntityFields.AGE_LOCAL_TS, Long::class.java)
|
||||
.setNullable(AuditTrailEntityFields.AGE_LOCAL_TS, true)
|
||||
@ -56,6 +55,5 @@ class MigrateCryptoTo016(realm: DynamicRealm) : RealmMigrator(realm, 15) {
|
||||
.addField(KeyRequestReplyEntityFields.SENDER_ID, String::class.java)
|
||||
.addField(KeyRequestReplyEntityFields.FROM_DEVICE, String::class.java)
|
||||
.addField(KeyRequestReplyEntityFields.EVENT_JSON, String::class.java)
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -18,13 +18,6 @@ package org.matrix.android.sdk.internal.crypto.store.db.model
|
||||
|
||||
import io.realm.RealmObject
|
||||
import io.realm.annotations.Index
|
||||
import org.matrix.android.sdk.api.extensions.tryOrNull
|
||||
import org.matrix.android.sdk.api.session.crypto.model.GossipingRequestState
|
||||
import org.matrix.android.sdk.api.session.crypto.model.IncomingRoomKeyRequest
|
||||
import org.matrix.android.sdk.api.session.crypto.model.IncomingSecretShareRequest
|
||||
import org.matrix.android.sdk.api.session.crypto.model.RoomKeyRequestBody
|
||||
import org.matrix.android.sdk.internal.crypto.GossipRequestType
|
||||
import org.matrix.android.sdk.internal.crypto.IncomingShareRequestCommon
|
||||
|
||||
// not used anymore, just here for db migration
|
||||
internal open class IncomingGossipingRequestEntity(@Index var requestId: String? = "",
|
||||
@ -35,56 +28,7 @@ internal open class IncomingGossipingRequestEntity(@Index var requestId: String?
|
||||
var localCreationTimestamp: Long? = null
|
||||
) : RealmObject() {
|
||||
|
||||
fun getRequestedSecretName(): String? = if (type == GossipRequestType.SECRET) {
|
||||
requestedInfoStr
|
||||
} else null
|
||||
|
||||
fun getRequestedKeyInfo(): RoomKeyRequestBody? = if (type == GossipRequestType.KEY) {
|
||||
RoomKeyRequestBody.fromJson(requestedInfoStr)
|
||||
} else null
|
||||
|
||||
var type: GossipRequestType
|
||||
get() {
|
||||
return tryOrNull { typeStr?.let { GossipRequestType.valueOf(it) } } ?: GossipRequestType.KEY
|
||||
}
|
||||
set(value) {
|
||||
typeStr = value.name
|
||||
}
|
||||
|
||||
private var requestStateStr: String = GossipingRequestState.NONE.name
|
||||
|
||||
var requestState: GossipingRequestState
|
||||
get() {
|
||||
return tryOrNull { GossipingRequestState.valueOf(requestStateStr) }
|
||||
?: GossipingRequestState.NONE
|
||||
}
|
||||
set(value) {
|
||||
requestStateStr = value.name
|
||||
}
|
||||
private var requestStateStr: String = ""
|
||||
|
||||
companion object
|
||||
|
||||
fun toIncomingGossipingRequest(): IncomingShareRequestCommon {
|
||||
return when (type) {
|
||||
GossipRequestType.KEY -> {
|
||||
IncomingRoomKeyRequest(
|
||||
requestBody = getRequestedKeyInfo(),
|
||||
deviceId = otherDeviceId,
|
||||
userId = otherUserId,
|
||||
requestId = requestId,
|
||||
state = requestState,
|
||||
localCreationTimestamp = localCreationTimestamp
|
||||
)
|
||||
}
|
||||
GossipRequestType.SECRET -> {
|
||||
IncomingSecretShareRequest(
|
||||
secretName = getRequestedSecretName(),
|
||||
deviceId = otherDeviceId,
|
||||
userId = otherUserId,
|
||||
requestId = requestId,
|
||||
localCreationTimestamp = localCreationTimestamp
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,6 @@ package org.matrix.android.sdk.internal.crypto.store.db.model
|
||||
|
||||
import io.realm.RealmObject
|
||||
import io.realm.annotations.Index
|
||||
import org.matrix.android.sdk.internal.crypto.OutgoingGossipingRequestState
|
||||
|
||||
// not used anymore, just here for db migration
|
||||
internal open class OutgoingGossipingRequestEntity(
|
||||
@ -28,5 +27,5 @@ internal open class OutgoingGossipingRequestEntity(
|
||||
@Index var typeStr: String? = null
|
||||
) : RealmObject() {
|
||||
|
||||
private var requestStateStr: String = OutgoingGossipingRequestState.UNSENT.name
|
||||
private var requestStateStr: String = ""
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2022 New Vector Ltd
|
||||
* Copyright (c) 2022 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.
|
||||
@ -32,10 +32,10 @@ import org.matrix.android.sdk.internal.crypto.OutgoingKeyRequest
|
||||
import org.matrix.android.sdk.internal.crypto.RequestReply
|
||||
import org.matrix.android.sdk.internal.crypto.RequestResult
|
||||
import org.matrix.android.sdk.internal.di.MoshiProvider
|
||||
import timber.log.Timber
|
||||
|
||||
internal open class OutgoingKeyRequestEntity(
|
||||
@Index var requestId: String? = null,
|
||||
var requestedIndex: Int? = null,
|
||||
var recipientsData: String? = null,
|
||||
var requestedInfoStr: String? = null,
|
||||
var creationTimeStamp: Long? = null,
|
||||
@ -84,7 +84,6 @@ internal open class OutgoingKeyRequestEntity(
|
||||
}
|
||||
|
||||
fun addReply(userId: String, fromDevice: String?, event: Event) {
|
||||
Timber.w("##VALR: add reply $userId / $fromDevice / $event")
|
||||
val newReply = KeyRequestReplyEntity(
|
||||
senderId = userId,
|
||||
fromDevice = fromDevice,
|
||||
@ -98,6 +97,7 @@ internal open class OutgoingKeyRequestEntity(
|
||||
requestBody = getRequestedKeyInfo(),
|
||||
recipients = getRecipients().orEmpty(),
|
||||
requestId = requestId ?: "",
|
||||
fromIndex = requestedIndex ?: 0,
|
||||
state = requestState,
|
||||
results = replies.mapNotNull { entity ->
|
||||
val userId = entity.senderId ?: return@mapNotNull null
|
||||
@ -107,9 +107,9 @@ internal open class OutgoingKeyRequestEntity(
|
||||
eventToResult(event)
|
||||
} ?: return@mapNotNull null
|
||||
RequestReply(
|
||||
userId,
|
||||
entity.fromDevice,
|
||||
result
|
||||
userId = userId,
|
||||
fromDevice = entity.fromDevice,
|
||||
result = result
|
||||
)
|
||||
}
|
||||
)
|
||||
@ -123,7 +123,7 @@ internal open class OutgoingKeyRequestEntity(
|
||||
}
|
||||
}
|
||||
EventType.FORWARDED_ROOM_KEY -> {
|
||||
RequestResult.Success
|
||||
RequestResult.Success((event.content?.get("chain_index") as? Number)?.toInt() ?: 0)
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ import org.matrix.android.sdk.api.session.crypto.verification.IncomingSasVerific
|
||||
import org.matrix.android.sdk.api.session.crypto.verification.SasMode
|
||||
import org.matrix.android.sdk.api.session.crypto.verification.VerificationTxState
|
||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||
import org.matrix.android.sdk.internal.crypto.OutgoingGossipingRequestManager
|
||||
import org.matrix.android.sdk.internal.crypto.OutgoingKeyRequestManager
|
||||
import org.matrix.android.sdk.internal.crypto.SecretShareManager
|
||||
import org.matrix.android.sdk.internal.crypto.actions.SetDeviceVerificationAction
|
||||
import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
|
||||
@ -35,7 +35,7 @@ internal class DefaultIncomingSASDefaultVerificationTransaction(
|
||||
override val deviceId: String?,
|
||||
private val cryptoStore: IMXCryptoStore,
|
||||
crossSigningService: CrossSigningService,
|
||||
outgoingGossipingRequestManager: OutgoingGossipingRequestManager,
|
||||
outgoingKeyRequestManager: OutgoingKeyRequestManager,
|
||||
secretShareManager: SecretShareManager,
|
||||
deviceFingerprint: String,
|
||||
transactionId: String,
|
||||
@ -47,7 +47,7 @@ internal class DefaultIncomingSASDefaultVerificationTransaction(
|
||||
deviceId,
|
||||
cryptoStore,
|
||||
crossSigningService,
|
||||
outgoingGossipingRequestManager,
|
||||
outgoingKeyRequestManager,
|
||||
secretShareManager,
|
||||
deviceFingerprint,
|
||||
transactionId,
|
||||
|
@ -20,7 +20,7 @@ import org.matrix.android.sdk.api.session.crypto.verification.CancelCode
|
||||
import org.matrix.android.sdk.api.session.crypto.verification.OutgoingSasVerificationTransaction
|
||||
import org.matrix.android.sdk.api.session.crypto.verification.VerificationTxState
|
||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||
import org.matrix.android.sdk.internal.crypto.OutgoingGossipingRequestManager
|
||||
import org.matrix.android.sdk.internal.crypto.OutgoingKeyRequestManager
|
||||
import org.matrix.android.sdk.internal.crypto.SecretShareManager
|
||||
import org.matrix.android.sdk.internal.crypto.actions.SetDeviceVerificationAction
|
||||
import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
|
||||
@ -32,7 +32,7 @@ internal class DefaultOutgoingSASDefaultVerificationTransaction(
|
||||
deviceId: String?,
|
||||
cryptoStore: IMXCryptoStore,
|
||||
crossSigningService: CrossSigningService,
|
||||
outgoingGossipingRequestManager: OutgoingGossipingRequestManager,
|
||||
outgoingKeyRequestManager: OutgoingKeyRequestManager,
|
||||
secretShareManager: SecretShareManager,
|
||||
deviceFingerprint: String,
|
||||
transactionId: String,
|
||||
@ -44,7 +44,7 @@ internal class DefaultOutgoingSASDefaultVerificationTransaction(
|
||||
deviceId,
|
||||
cryptoStore,
|
||||
crossSigningService,
|
||||
outgoingGossipingRequestManager,
|
||||
outgoingKeyRequestManager,
|
||||
secretShareManager,
|
||||
deviceFingerprint,
|
||||
transactionId,
|
||||
|
@ -60,7 +60,7 @@ import org.matrix.android.sdk.api.session.room.model.message.MessageVerification
|
||||
import org.matrix.android.sdk.api.session.room.model.message.ValidVerificationDone
|
||||
import org.matrix.android.sdk.internal.crypto.DeviceListManager
|
||||
import org.matrix.android.sdk.internal.crypto.MyDeviceInfoHolder
|
||||
import org.matrix.android.sdk.internal.crypto.OutgoingGossipingRequestManager
|
||||
import org.matrix.android.sdk.internal.crypto.OutgoingKeyRequestManager
|
||||
import org.matrix.android.sdk.internal.crypto.SecretShareManager
|
||||
import org.matrix.android.sdk.internal.crypto.actions.SetDeviceVerificationAction
|
||||
import org.matrix.android.sdk.internal.crypto.model.rest.KeyVerificationAccept
|
||||
@ -94,7 +94,7 @@ internal class DefaultVerificationService @Inject constructor(
|
||||
@UserId private val userId: String,
|
||||
@DeviceId private val deviceId: String?,
|
||||
private val cryptoStore: IMXCryptoStore,
|
||||
private val outgoingGossipingRequestManager: OutgoingGossipingRequestManager,
|
||||
private val outgoingKeyRequestManager: OutgoingKeyRequestManager,
|
||||
private val secretShareManager: SecretShareManager,
|
||||
private val myDeviceInfoHolder: Lazy<MyDeviceInfoHolder>,
|
||||
private val deviceListManager: DeviceListManager,
|
||||
@ -547,7 +547,7 @@ internal class DefaultVerificationService @Inject constructor(
|
||||
deviceId,
|
||||
cryptoStore,
|
||||
crossSigningService,
|
||||
outgoingGossipingRequestManager,
|
||||
outgoingKeyRequestManager,
|
||||
secretShareManager,
|
||||
myDeviceInfoHolder.get().myDevice.fingerprint()!!,
|
||||
startReq.transactionId,
|
||||
@ -909,7 +909,7 @@ internal class DefaultVerificationService @Inject constructor(
|
||||
otherUserId = senderId,
|
||||
otherDeviceId = readyReq.fromDevice,
|
||||
crossSigningService = crossSigningService,
|
||||
outgoingGossipingRequestManager = outgoingGossipingRequestManager,
|
||||
outgoingKeyRequestManager = outgoingKeyRequestManager,
|
||||
secretShareManager = secretShareManager,
|
||||
cryptoStore = cryptoStore,
|
||||
qrCodeData = qrCodeData,
|
||||
@ -1108,7 +1108,7 @@ internal class DefaultVerificationService @Inject constructor(
|
||||
deviceId,
|
||||
cryptoStore,
|
||||
crossSigningService,
|
||||
outgoingGossipingRequestManager,
|
||||
outgoingKeyRequestManager,
|
||||
secretShareManager,
|
||||
myDeviceInfoHolder.get().myDevice.fingerprint()!!,
|
||||
txID,
|
||||
@ -1300,7 +1300,7 @@ internal class DefaultVerificationService @Inject constructor(
|
||||
deviceId,
|
||||
cryptoStore,
|
||||
crossSigningService,
|
||||
outgoingGossipingRequestManager,
|
||||
outgoingKeyRequestManager,
|
||||
secretShareManager,
|
||||
myDeviceInfoHolder.get().myDevice.fingerprint()!!,
|
||||
transactionId,
|
||||
@ -1438,7 +1438,7 @@ internal class DefaultVerificationService @Inject constructor(
|
||||
otherUserId = otherUserId,
|
||||
otherDeviceId = otherDeviceId,
|
||||
crossSigningService = crossSigningService,
|
||||
outgoingGossipingRequestManager = outgoingGossipingRequestManager,
|
||||
outgoingKeyRequestManager = outgoingKeyRequestManager,
|
||||
secretShareManager = secretShareManager,
|
||||
cryptoStore = cryptoStore,
|
||||
qrCodeData = qrCodeData,
|
||||
|
@ -20,7 +20,7 @@ import org.matrix.android.sdk.api.session.crypto.crosssigning.CrossSigningServic
|
||||
import org.matrix.android.sdk.api.session.crypto.crosssigning.DeviceTrustLevel
|
||||
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.internal.crypto.OutgoingGossipingRequestManager
|
||||
import org.matrix.android.sdk.internal.crypto.OutgoingKeyRequestManager
|
||||
import org.matrix.android.sdk.internal.crypto.SecretShareManager
|
||||
import org.matrix.android.sdk.internal.crypto.actions.SetDeviceVerificationAction
|
||||
import timber.log.Timber
|
||||
@ -31,7 +31,7 @@ import timber.log.Timber
|
||||
internal abstract class DefaultVerificationTransaction(
|
||||
private val setDeviceVerificationAction: SetDeviceVerificationAction,
|
||||
private val crossSigningService: CrossSigningService,
|
||||
private val outgoingGossipingRequestManager: OutgoingGossipingRequestManager,
|
||||
private val outgoingKeyRequestManager: OutgoingKeyRequestManager,
|
||||
private val secretShareManager: SecretShareManager,
|
||||
private val userId: String,
|
||||
override val transactionId: String,
|
||||
|
@ -23,7 +23,7 @@ import org.matrix.android.sdk.api.session.crypto.verification.SasMode
|
||||
import org.matrix.android.sdk.api.session.crypto.verification.SasVerificationTransaction
|
||||
import org.matrix.android.sdk.api.session.crypto.verification.VerificationTxState
|
||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||
import org.matrix.android.sdk.internal.crypto.OutgoingGossipingRequestManager
|
||||
import org.matrix.android.sdk.internal.crypto.OutgoingKeyRequestManager
|
||||
import org.matrix.android.sdk.internal.crypto.SecretShareManager
|
||||
import org.matrix.android.sdk.internal.crypto.actions.SetDeviceVerificationAction
|
||||
import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
|
||||
@ -42,7 +42,7 @@ internal abstract class SASDefaultVerificationTransaction(
|
||||
open val deviceId: String?,
|
||||
private val cryptoStore: IMXCryptoStore,
|
||||
crossSigningService: CrossSigningService,
|
||||
outgoingGossipingRequestManager: OutgoingGossipingRequestManager,
|
||||
outgoingKeyRequestManager: OutgoingKeyRequestManager,
|
||||
secretShareManager: SecretShareManager,
|
||||
private val deviceFingerprint: String,
|
||||
transactionId: String,
|
||||
@ -52,7 +52,7 @@ internal abstract class SASDefaultVerificationTransaction(
|
||||
) : DefaultVerificationTransaction(
|
||||
setDeviceVerificationAction,
|
||||
crossSigningService,
|
||||
outgoingGossipingRequestManager,
|
||||
outgoingKeyRequestManager,
|
||||
secretShareManager,
|
||||
userId,
|
||||
transactionId,
|
||||
|
@ -21,8 +21,7 @@ import org.matrix.android.sdk.api.session.crypto.verification.CancelCode
|
||||
import org.matrix.android.sdk.api.session.crypto.verification.QrCodeVerificationTransaction
|
||||
import org.matrix.android.sdk.api.session.crypto.verification.VerificationTxState
|
||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||
import org.matrix.android.sdk.api.util.fromBase64
|
||||
import org.matrix.android.sdk.internal.crypto.OutgoingGossipingRequestManager
|
||||
import org.matrix.android.sdk.internal.crypto.OutgoingKeyRequestManager
|
||||
import org.matrix.android.sdk.internal.crypto.SecretShareManager
|
||||
import org.matrix.android.sdk.internal.crypto.actions.SetDeviceVerificationAction
|
||||
import org.matrix.android.sdk.internal.crypto.crosssigning.fromBase64Safe
|
||||
@ -37,7 +36,7 @@ internal class DefaultQrCodeVerificationTransaction(
|
||||
override val otherUserId: String,
|
||||
override var otherDeviceId: String?,
|
||||
private val crossSigningService: CrossSigningService,
|
||||
outgoingGossipingRequestManager: OutgoingGossipingRequestManager,
|
||||
outgoingKeyRequestManager: OutgoingKeyRequestManager,
|
||||
secretShareManager: SecretShareManager,
|
||||
private val cryptoStore: IMXCryptoStore,
|
||||
// Not null only if other user is able to scan QR code
|
||||
@ -48,7 +47,7 @@ internal class DefaultQrCodeVerificationTransaction(
|
||||
) : DefaultVerificationTransaction(
|
||||
setDeviceVerificationAction,
|
||||
crossSigningService,
|
||||
outgoingGossipingRequestManager,
|
||||
outgoingKeyRequestManager,
|
||||
secretShareManager,
|
||||
userId,
|
||||
transactionId,
|
||||
|
@ -22,6 +22,8 @@ import im.vector.app.core.date.DateFormatKind
|
||||
import im.vector.app.core.date.VectorDateFormatter
|
||||
import im.vector.app.features.popup.DefaultVectorAlert
|
||||
import im.vector.app.features.popup.PopupAlertManager
|
||||
import im.vector.app.features.session.coroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
import org.matrix.android.sdk.api.MatrixCallback
|
||||
import org.matrix.android.sdk.api.session.Session
|
||||
import org.matrix.android.sdk.api.session.crypto.crosssigning.DeviceTrustLevel
|
||||
@ -36,6 +38,12 @@ import org.matrix.android.sdk.api.session.crypto.verification.SasVerificationTra
|
||||
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.internal.crypto.IncomingRoomKeyRequest
|
||||
import org.matrix.android.sdk.internal.crypto.crosssigning.DeviceTrustLevel
|
||||
import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo
|
||||
import org.matrix.android.sdk.internal.crypto.model.MXUsersDevicesMap
|
||||
import org.matrix.android.sdk.internal.crypto.model.rest.DeviceInfo
|
||||
import org.matrix.android.sdk.internal.crypto.model.rest.SecretShareRequest
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
@ -72,10 +80,9 @@ class KeyRequestHandler @Inject constructor(
|
||||
session = null
|
||||
}
|
||||
|
||||
override fun onSecretShareRequest(request: IncomingSecretShareRequest): Boolean {
|
||||
override fun onSecretShareRequest(request: SecretShareRequest): Boolean {
|
||||
// By default Element will not prompt if the SDK has decided that the request should not be fulfilled
|
||||
Timber.v("## onSecretShareRequest() : Ignoring $request")
|
||||
request.ignore?.run()
|
||||
return true
|
||||
}
|
||||
|
||||
@ -195,15 +202,14 @@ class KeyRequestHandler @Inject constructor(
|
||||
}
|
||||
|
||||
private fun denyAllRequests(mappingKey: String) {
|
||||
alertsToRequests[mappingKey]?.forEach {
|
||||
it.ignore?.run()
|
||||
}
|
||||
alertsToRequests.remove(mappingKey)
|
||||
}
|
||||
|
||||
private fun shareAllSessions(mappingKey: String) {
|
||||
alertsToRequests[mappingKey]?.forEach {
|
||||
it.share?.run()
|
||||
session?.coroutineScope?.launch {
|
||||
session?.cryptoService()?.manuallyAcceptRoomKeyRequest(it)
|
||||
}
|
||||
}
|
||||
alertsToRequests.remove(mappingKey)
|
||||
}
|
||||
@ -213,7 +219,7 @@ class KeyRequestHandler @Inject constructor(
|
||||
*
|
||||
* @param request the cancellation request.
|
||||
*/
|
||||
override fun onRoomKeyRequestCancellation(request: IncomingRequestCancellation) {
|
||||
override fun onRequestCancelled(request: IncomingRoomKeyRequest) {
|
||||
// see if we can find the request in the queue
|
||||
val userId = request.userId
|
||||
val deviceId = request.deviceId
|
||||
|
@ -62,10 +62,6 @@ class IncomingKeyRequestPagedController @Inject constructor(
|
||||
textStyle = "bold"
|
||||
}
|
||||
+"${roomKeyRequest.deviceId}"
|
||||
span("\nstate: ") {
|
||||
textStyle = "bold"
|
||||
}
|
||||
+roomKeyRequest.state.name
|
||||
}.toEpoxyCharSequence()
|
||||
)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user