Crypto : WIP

This commit is contained in:
ganfra 2019-05-23 19:12:06 +02:00
parent bb39db3f42
commit 3519ad7c8d
14 changed files with 81 additions and 73 deletions

View File

@ -12,7 +12,7 @@ buildscript {
}
}
dependencies {
classpath 'com.android.tools.build:gradle:3.4.0'
classpath 'com.android.tools.build:gradle:3.4.1'
classpath 'com.google.gms:google-services:4.2.0'
classpath "com.airbnb.okreplay:gradle-plugin:1.4.0"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"

View File

@ -19,4 +19,4 @@ package im.vector.matrix.android
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
import kotlinx.coroutines.Dispatchers.Main

internal val testCoroutineDispatchers = MatrixCoroutineDispatchers(Main, Main, Main)
internal val testCoroutineDispatchers = MatrixCoroutineDispatchers(Main, Main, Main, Main, Main)

View File

@ -22,5 +22,5 @@ enum class RoomHistoryVisibility {
@Json(name = "shared") SHARED,
@Json(name = "invited") INVITED,
@Json(name = "joined") JOINED,
@Json(name = "word_readable") WORLD_READABLE
@Json(name = "world_readable") WORLD_READABLE
}

View File

@ -35,8 +35,10 @@ import im.vector.matrix.android.api.session.events.model.Event
import im.vector.matrix.android.api.session.events.model.EventType
import im.vector.matrix.android.api.session.events.model.toModel
import im.vector.matrix.android.api.session.room.Room
import im.vector.matrix.android.api.session.room.model.Membership
import im.vector.matrix.android.api.session.room.model.RoomHistoryVisibility
import im.vector.matrix.android.api.session.room.model.RoomHistoryVisibilityContent
import im.vector.matrix.android.api.session.room.model.RoomMember
import im.vector.matrix.android.internal.crypto.actions.EnsureOlmSessionsForDevicesAction
import im.vector.matrix.android.internal.crypto.actions.MegolmSessionDataImporter
import im.vector.matrix.android.internal.crypto.actions.SetDeviceVerificationAction
@ -44,19 +46,29 @@ import im.vector.matrix.android.internal.crypto.algorithms.IMXEncrypting
import im.vector.matrix.android.internal.crypto.algorithms.megolm.MXMegolmEncryptionFactory
import im.vector.matrix.android.internal.crypto.algorithms.olm.MXOlmEncryptionFactory
import im.vector.matrix.android.internal.crypto.keysbackup.KeysBackup
import im.vector.matrix.android.internal.crypto.model.*
import im.vector.matrix.android.internal.crypto.model.ImportRoomKeysResult
import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo
import im.vector.matrix.android.internal.crypto.model.MXEncryptEventContentResult
import im.vector.matrix.android.internal.crypto.model.MXOlmSessionResult
import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap
import im.vector.matrix.android.internal.crypto.model.event.RoomKeyContent
import im.vector.matrix.android.internal.crypto.model.rest.DevicesListResponse
import im.vector.matrix.android.internal.crypto.model.rest.KeysUploadResponse
import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyRequestBody
import im.vector.matrix.android.internal.crypto.repository.WarnOnUnknownDeviceRepository
import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
import im.vector.matrix.android.internal.crypto.tasks.*
import im.vector.matrix.android.internal.crypto.tasks.ClaimOneTimeKeysForUsersDeviceTask
import im.vector.matrix.android.internal.crypto.tasks.DeleteDeviceTask
import im.vector.matrix.android.internal.crypto.tasks.GetDevicesTask
import im.vector.matrix.android.internal.crypto.tasks.GetKeyChangesTask
import im.vector.matrix.android.internal.crypto.tasks.SendToDeviceTask
import im.vector.matrix.android.internal.crypto.tasks.SetDeviceNameTask
import im.vector.matrix.android.internal.crypto.tasks.UploadKeysTask
import im.vector.matrix.android.internal.crypto.verification.DefaultSasVerificationService
import im.vector.matrix.android.internal.di.MoshiProvider
import im.vector.matrix.android.internal.session.room.members.RoomMembers
import im.vector.matrix.android.internal.session.sync.model.SyncResponse
import im.vector.matrix.android.internal.task.TaskExecutor
import im.vector.matrix.android.internal.task.TaskThread
import im.vector.matrix.android.internal.task.configureWith
import org.matrix.olm.OlmManager
import timber.log.Timber
@ -230,15 +242,8 @@ internal class CryptoManager(
* devices.
*
* @param isInitialSync true if it starts from an initial sync
* @param aCallback the asynchronous callback
*/
fun start(isInitialSync: Boolean, aCallback: MatrixCallback<Unit>?) {
synchronized(mInitializationCallbacks) {
if (null != aCallback && mInitializationCallbacks.indexOf(aCallback) < 0) {
mInitializationCallbacks.add(aCallback)
}
}

fun start(isInitialSync: Boolean) {
if (mIsStarting) {
return
}
@ -260,11 +265,11 @@ internal class CryptoManager(
uploadDeviceKeys(object : MatrixCallback<KeysUploadResponse> {
private fun onError() {
Handler().postDelayed({
if (!isStarted()) {
mIsStarting = false
start(isInitialSync, null)
}
}, 1000)
if (!isStarted()) {
mIsStarting = false
start(isInitialSync)
}
}, 1000)
}

override fun onSuccess(data: KeysUploadResponse) {
@ -290,13 +295,6 @@ internal class CryptoManager(

mKeysBackup.checkAndStartKeysBackup()

synchronized(mInitializationCallbacks) {
for (callback in mInitializationCallbacks) {
callback.onSuccess(Unit)
}
mInitializationCallbacks.clear()
}

if (isInitialSync) {
// refresh the devices list for each known room members
deviceListManager.invalidateAllDeviceLists()
@ -605,17 +603,7 @@ internal class CryptoManager(
if (!isStarted()) {
Timber.v("## encryptEventContent() : wait after e2e init")

start(false, object : MatrixCallback<Unit> {
override fun onSuccess(data: Unit) {
encryptEventContent(eventContent, eventType, room, callback)
}

override fun onFailure(failure: Throwable) {
Timber.e(failure, "## encryptEventContent() : onNetworkError while waiting to start e2e")

callback.onFailure(failure)
}
})
start(false)

return
}
@ -669,11 +657,11 @@ internal class CryptoManager(
} else {
val algorithm = room.encryptionAlgorithm()
val reason = String.format(MXCryptoError.UNABLE_TO_ENCRYPT_REASON,
algorithm ?: MXCryptoError.NO_MORE_ALGORITHM_REASON)
algorithm ?: MXCryptoError.NO_MORE_ALGORITHM_REASON)
Timber.e("## encryptEventContent() : $reason")

callback.onFailure(Failure.CryptoError(MXCryptoError(MXCryptoError.UNABLE_TO_ENCRYPT_ERROR_CODE,
MXCryptoError.UNABLE_TO_ENCRYPT, reason)))
MXCryptoError.UNABLE_TO_ENCRYPT, reason)))
}
}

@ -703,7 +691,7 @@ internal class CryptoManager(
val reason = String.format(MXCryptoError.UNABLE_TO_DECRYPT_REASON, event.eventId, eventContent["algorithm"] as String)
Timber.e("## decryptEvent() : $reason")
exceptions.add(MXDecryptionException(MXCryptoError(MXCryptoError.UNABLE_TO_DECRYPT_ERROR_CODE,
MXCryptoError.UNABLE_TO_DECRYPT, reason)))
MXCryptoError.UNABLE_TO_DECRYPT, reason)))
} else {
try {
result = alg.decryptEvent(event, timeline)
@ -796,23 +784,15 @@ internal class CryptoManager(
// No encrypting in this room
return
}

val userId = event.stateKey!!

/* FIXME
val room = mRoomService.getRoom(roomId)

val roomMember = room?.getRoomMember(userId)

if (null != roomMember) {
val membership = roomMember.membership

event.stateKey?.let { userId ->
val roomMember: RoomMember? = event.content.toModel()
val membership = roomMember?.membership
if (membership == Membership.JOIN) {
// make sure we are tracking the deviceList for this user.
deviceListManager.startTrackingDeviceList(Arrays.asList(userId))
} else if (membership == Membership.INVITE
&& shouldEncryptForInvitedMembers(roomId)
&& mCryptoConfig.mEnableEncryptionForInvitedMembers) {
&& shouldEncryptForInvitedMembers(roomId)
&& mCryptoConfig.mEnableEncryptionForInvitedMembers) {
// track the deviceList for this invited user.
// Caution: there's a big edge case here in that federated servers do not
// know what other servers are in the room at the time they've been invited.
@ -821,7 +801,6 @@ internal class CryptoManager(
deviceListManager.startTrackingDeviceList(Arrays.asList(userId))
}
}
*/
}

private fun onRoomHistoryVisibilityEvent(roomId: String, event: Event) {
@ -851,6 +830,7 @@ internal class CryptoManager(
// same one as used in login.
mUploadKeysTask
.configureWith(UploadKeysTask.Params(getMyDevice().toDeviceKeys(), null, getMyDevice().deviceId))
.executeOn(TaskThread.ENCRYPTION)
.dispatchTo(callback)
.executeBy(mTaskExecutor)
}
@ -980,7 +960,7 @@ internal class CryptoManager(
// trigger an an unknown devices exception
callback.onFailure(
Failure.CryptoError(MXCryptoError(MXCryptoError.UNKNOWN_DEVICES_CODE,
MXCryptoError.UNABLE_TO_ENCRYPT, MXCryptoError.UNKNOWN_DEVICES_REASON, unknownDevices)))
MXCryptoError.UNABLE_TO_ENCRYPT, MXCryptoError.UNKNOWN_DEVICES_REASON, unknownDevices)))
}
}


View File

@ -23,6 +23,7 @@ import im.vector.matrix.android.internal.crypto.model.rest.KeysUploadResponse
import im.vector.matrix.android.internal.crypto.tasks.UploadKeysTask
import im.vector.matrix.android.internal.di.MoshiProvider
import im.vector.matrix.android.internal.task.TaskExecutor
import im.vector.matrix.android.internal.task.TaskThread
import im.vector.matrix.android.internal.task.configureWith
import org.matrix.olm.OlmAccount
import timber.log.Timber
@ -95,6 +96,7 @@ internal class OneTimeKeysUploader(
// ask the server how many keys we have
mUploadKeysTask
.configureWith(UploadKeysTask.Params(null, null, mCredentials.deviceId!!))
.executeOn(TaskThread.ENCRYPTION)
.dispatchTo(object : MatrixCallback<KeysUploadResponse> {

override fun onSuccess(data: KeysUploadResponse) {
@ -192,6 +194,7 @@ internal class OneTimeKeysUploader(
// same one as used in login.
mUploadKeysTask
.configureWith(UploadKeysTask.Params(null, oneTimeJson, mCredentials.deviceId!!))
.executeOn(TaskThread.ENCRYPTION)
.dispatchTo(object : MatrixCallback<KeysUploadResponse> {
override fun onSuccess(data: KeysUploadResponse) {
mLastPublishedOneTimeKeys = oneTimeKeys

View File

@ -21,6 +21,7 @@ import androidx.work.Worker
import androidx.work.WorkerParameters
import com.squareup.moshi.JsonClass
import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.internal.crypto.CryptoManager
import im.vector.matrix.android.internal.database.mapper.asDomain
import im.vector.matrix.android.internal.database.model.EventEntity
@ -50,8 +51,8 @@ internal class EnableEncryptionWorker(context: Context,


override fun doWork(): Result {
val params = WorkerParamsFactory.fromData<EnableEncryptionWorker.Params>(inputData)
?: return Result.failure()
val params = WorkerParamsFactory.fromData<Params>(inputData)
?: return Result.failure()


val events = monarchy.fetchAllMappedSync(
@ -62,9 +63,17 @@ internal class EnableEncryptionWorker(context: Context,
events.forEach {
val roomId = it.roomId!!

val callback = object : MatrixCallback<Boolean> {
override fun onSuccess(data: Boolean) {
super.onSuccess(data)

}
}

loadRoomMembersTask
.configureWith(LoadRoomMembersTask.Params(roomId))
.executeOn(TaskThread.CALLER)
.executeOn(TaskThread.ENCRYPTION)
.dispatchTo(callback)
.executeBy(taskExecutor)

var userIds: List<String> = emptyList()
@ -72,7 +81,7 @@ internal class EnableEncryptionWorker(context: Context,
monarchy.doWithRealm { realm ->
// Check whether the event content must be encrypted for the invited members.
val encryptForInvitedMembers = cryptoManager.isEncryptionEnabledForInvitedUser()
&& cryptoManager.shouldEncryptForInvitedMembers(roomId)
&& cryptoManager.shouldEncryptForInvitedMembers(roomId)


userIds = if (encryptForInvitedMembers) {

View File

@ -21,7 +21,9 @@ import im.vector.matrix.android.internal.task.TaskExecutor
import im.vector.matrix.android.internal.util.BackgroundDetectionObserver
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.asCoroutineDispatcher
import org.koin.dsl.module.module
import java.util.concurrent.Executors


class MatrixModule(private val context: Context) {
@ -33,7 +35,12 @@ class MatrixModule(private val context: Context) {
}

single {
MatrixCoroutineDispatchers(io = Dispatchers.IO, computation = Dispatchers.IO, main = Dispatchers.Main)
MatrixCoroutineDispatchers(io = Dispatchers.IO,
computation = Dispatchers.IO,
main = Dispatchers.Main,
encryption = Executors.newSingleThreadExecutor().asCoroutineDispatcher(),
decryption = Executors.newSingleThreadExecutor().asCoroutineDispatcher()
)
}

single {

View File

@ -103,18 +103,17 @@ internal class DefaultSession(override val sessionParams: SessionParams) : Sessi
val contentModule = ContentModule().definition
val cryptoModule = CryptoModule().definition
MatrixKoinHolder.instance.loadModules(listOf(sessionModule,
syncModule,
roomModule,
groupModule,
userModule,
signOutModule,
contentModule,
cryptoModule))
syncModule,
roomModule,
groupModule,
userModule,
signOutModule,
contentModule,
cryptoModule))
scope = getKoin().getOrCreateScope(SCOPE)
if (!monarchy.isMonarchyThreadOpen) {
monarchy.openManually()
}
cryptoService.start(false, null)
liveEntityUpdaters.forEach { it.start() }
}


View File

@ -56,7 +56,7 @@ internal class SyncModule {
}

scope(DefaultSession.SCOPE) {
SyncResponseHandler(get(), get(), get(), get())
SyncResponseHandler(get(), get(), get(), get(), get())
}

scope(DefaultSession.SCOPE) {

View File

@ -17,6 +17,7 @@
package im.vector.matrix.android.internal.session.sync

import arrow.core.Try
import im.vector.matrix.android.internal.crypto.CryptoManager
import im.vector.matrix.android.internal.session.sync.model.SyncResponse
import timber.log.Timber
import kotlin.system.measureTimeMillis
@ -24,7 +25,8 @@ import kotlin.system.measureTimeMillis
internal class SyncResponseHandler(private val roomSyncHandler: RoomSyncHandler,
private val userAccountDataSyncHandler: UserAccountDataSyncHandler,
private val groupSyncHandler: GroupSyncHandler,
private val cryptoSyncHandler: CryptoSyncHandler) {
private val cryptoSyncHandler: CryptoSyncHandler,
private val cryptoManager: CryptoManager) {

fun handleResponse(syncResponse: SyncResponse, fromToken: String?, isCatchingUp: Boolean): Try<SyncResponse> {
return Try {
@ -44,9 +46,10 @@ internal class SyncResponseHandler(private val roomSyncHandler: RoomSyncHandler,
if (syncResponse.accountData != null) {
userAccountDataSyncHandler.handle(syncResponse.accountData)
}

cryptoSyncHandler.onSyncCompleted(syncResponse, fromToken, isCatchingUp)
}
val isInitialSync = fromToken == null
cryptoManager.start(isInitialSync)
Timber.v("Finish handling sync in $measure ms")
syncResponse
}

View File

@ -68,6 +68,8 @@ internal class TaskExecutor(private val coroutineDispatchers: MatrixCoroutineDis
TaskThread.COMPUTATION -> coroutineDispatchers.computation
TaskThread.IO -> coroutineDispatchers.io
TaskThread.CALLER -> EmptyCoroutineContext
TaskThread.ENCRYPTION -> coroutineDispatchers.encryption
TaskThread.DECRYPTION -> coroutineDispatchers.decryption
}



View File

@ -20,5 +20,7 @@ internal enum class TaskThread {
MAIN,
COMPUTATION,
IO,
CALLER
CALLER,
ENCRYPTION,
DECRYPTION
}

View File

@ -21,5 +21,7 @@ import kotlinx.coroutines.CoroutineDispatcher
internal data class MatrixCoroutineDispatchers(
val io: CoroutineDispatcher,
val computation: CoroutineDispatcher,
val main: CoroutineDispatcher
val main: CoroutineDispatcher,
val decryption: CoroutineDispatcher,
val encryption: CoroutineDispatcher
)

View File

@ -30,6 +30,7 @@ import im.vector.matrix.android.api.session.room.model.message.*
import im.vector.matrix.android.api.session.room.timeline.Timeline
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
import im.vector.riotredesign.core.epoxy.LoadingItemModel_
import im.vector.riotredesign.core.epoxy.loadingItem
import im.vector.riotredesign.core.extensions.localDateTime
import im.vector.riotredesign.features.home.room.detail.timeline.factory.TimelineItemFactory
import im.vector.riotredesign.features.home.room.detail.timeline.helper.*