Crypto: continue cleaning. Need threading refactoring

This commit is contained in:
ganfra 2019-06-03 18:39:37 +02:00
parent 784d55c16c
commit 3d50393b33
30 changed files with 462 additions and 466 deletions

View File

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


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

View File

@ -135,20 +135,20 @@ data class Event(
internal fun setClearData(decryptionResult: MXEventDecryptionResult?) { internal fun setClearData(decryptionResult: MXEventDecryptionResult?) {
mClearEvent = null mClearEvent = null
if (decryptionResult != null) { if (decryptionResult != null) {
if (decryptionResult.mClearEvent != null) { if (decryptionResult.clearEvent != null) {
val adapter = MoshiProvider.providesMoshi().adapter(Event::class.java) val adapter = MoshiProvider.providesMoshi().adapter(Event::class.java)
mClearEvent = adapter.fromJsonValue(decryptionResult.mClearEvent) mClearEvent = adapter.fromJsonValue(decryptionResult.clearEvent)


} }
mClearEvent?.apply { mClearEvent?.apply {
mSenderCurve25519Key = decryptionResult.mSenderCurve25519Key mSenderCurve25519Key = decryptionResult.senderCurve25519Key
mClaimedEd25519Key = decryptionResult.mClaimedEd25519Key mClaimedEd25519Key = decryptionResult.claimedEd25519Key
mForwardingCurve25519KeyChain = decryptionResult.mForwardingCurve25519KeyChain mForwardingCurve25519KeyChain = decryptionResult.forwardingCurve25519KeyChain
try { try {
// Add "m.relates_to" data from e2e event to the unencrypted event // Add "m.relates_to" data from e2e event to the unencrypted event
// TODO // TODO
//if (getWireContent().getAsJsonObject().has("m.relates_to")) { //if (getWireContent().getAsJsonObject().has("m.relates_to")) {
// mClearEvent!!.getContentAsJsonObject() // clearEvent!!.getContentAsJsonObject()
// .add("m.relates_to", getWireContent().getAsJsonObject().get("m.relates_to")) // .add("m.relates_to", getWireContent().getAsJsonObject().get("m.relates_to"))
//} //}
} catch (e: Exception) { } catch (e: Exception) {
@ -193,7 +193,7 @@ data class Event(
} }


/** /**
* @return the event type * @return the event content
*/ */
fun getClearContent(): Content? { fun getClearContent(): Content? {
return mClearEvent?.content ?: content return mClearEvent?.content ?: content

View File

@ -22,44 +22,40 @@ import android.os.Handler
import android.os.HandlerThread import android.os.HandlerThread
import android.os.Looper import android.os.Looper


private const val THREAD_ENCRYPT_NAME = "Crypto_Encrypt_Thread" private const val THREAD_CRYPTO_NAME = "Crypto_Thread"
private const val THREAD_DECRYPT_NAME = "Crypto_Decrypt_Thread"


// TODO Remove and replace by Task // TODO Remove and replace by Task
internal object CryptoAsyncHelper { internal object CryptoAsyncHelper {


private var uiHandler: Handler? = null private var uiHandler: Handler? = null
private var decryptBackgroundHandler: Handler? = null private var cryptoBackgroundHandler: Handler? = null
private var encryptBackgroundHandler: Handler? = null


fun getUiHandler(): Handler { fun getUiHandler(): Handler {
return uiHandler return uiHandler
?: Handler(Looper.getMainLooper()) ?: Handler(Looper.getMainLooper())
.also { uiHandler = it } .also { uiHandler = it }
} }



fun getDecryptBackgroundHandler(): Handler { fun getDecryptBackgroundHandler(): Handler {
return decryptBackgroundHandler return getCryptoBackgroundHandler()
?: createDecryptBackgroundHandler()
.also { decryptBackgroundHandler = it }
} }


fun getEncryptBackgroundHandler(): Handler { fun getEncryptBackgroundHandler(): Handler {
return encryptBackgroundHandler return getCryptoBackgroundHandler()
?: createEncryptBackgroundHandler()
.also { encryptBackgroundHandler = it }
} }


private fun createDecryptBackgroundHandler(): Handler { private fun getCryptoBackgroundHandler(): Handler {
val handlerThread = HandlerThread(THREAD_DECRYPT_NAME) return cryptoBackgroundHandler
?: createCryptoBackgroundHandler()
.also { cryptoBackgroundHandler = it }
}

private fun createCryptoBackgroundHandler(): Handler {
val handlerThread = HandlerThread(THREAD_CRYPTO_NAME)
handlerThread.start() handlerThread.start()
return Handler(handlerThread.looper) return Handler(handlerThread.looper)
} }


private fun createEncryptBackgroundHandler(): Handler {
val handlerThread = HandlerThread(THREAD_ENCRYPT_NAME)
handlerThread.start()
return Handler(handlerThread.looper)
}


} }

View File

@ -49,7 +49,6 @@ import im.vector.matrix.android.internal.crypto.keysbackup.KeysBackup
import im.vector.matrix.android.internal.crypto.model.ImportRoomKeysResult 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.MXDeviceInfo
import im.vector.matrix.android.internal.crypto.model.MXEncryptEventContentResult 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.MXUsersDevicesMap
import im.vector.matrix.android.internal.crypto.model.event.RoomKeyContent 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.DevicesListResponse
@ -488,8 +487,8 @@ internal class CryptoManager(
cryptoStore.storeRoomAlgorithm(roomId, algorithm!!) cryptoStore.storeRoomAlgorithm(roomId, algorithm!!)


val alg: IMXEncrypting = when (algorithm) { val alg: IMXEncrypting = when (algorithm) {
MXCRYPTO_ALGORITHM_MEGOLM -> megolmEncryptionFactory.instantiate(roomId) MXCRYPTO_ALGORITHM_MEGOLM -> megolmEncryptionFactory.create(roomId)
else -> olmEncryptionFactory.instantiate(roomId) else -> olmEncryptionFactory.create(roomId)
} }


synchronized(roomEncryptors) { synchronized(roomEncryptors) {
@ -544,20 +543,6 @@ internal class CryptoManager(
return if (null != map) ArrayList(map.values) else ArrayList() return if (null != map) ArrayList(map.values) else ArrayList()
} }


// TODO Remove ?
/**
* Try to make sure we have established olm sessions for the given devices.
* It must be called in getCryptoHandler() thread.
* The callback is called in the UI thread.
*
* @param devicesByUser a map from userid to list of devices.
* @param callback the asynchronous callback
*/
fun ensureOlmSessionsForDevices(devicesByUser: Map<String, List<MXDeviceInfo>>,
callback: MatrixCallback<MXUsersDevicesMap<MXOlmSessionResult>>?) {
ensureOlmSessionsForDevicesAction.handle(devicesByUser, callback)
}

fun isEncryptionEnabledForInvitedUser(): Boolean { fun isEncryptionEnabledForInvitedUser(): Boolean {
return cryptoConfig.mEnableEncryptionForInvitedMembers return cryptoConfig.mEnableEncryptionForInvitedMembers
} }
@ -602,7 +587,7 @@ internal class CryptoManager(
var alg = synchronized(roomEncryptors) { var alg = synchronized(roomEncryptors) {
roomEncryptors[roomId] roomEncryptors[roomId]
} }
if (null == alg) { if (alg == null) {
val algorithm = getEncryptionAlgorithm(roomId) val algorithm = getEncryptionAlgorithm(roomId)
if (null != algorithm) { if (null != algorithm) {
if (setEncryptionInRoom(roomId, algorithm, false, userIds)) { if (setEncryptionInRoom(roomId, algorithm, false, userIds)) {
@ -613,7 +598,7 @@ internal class CryptoManager(
} }
} }


if (null != alg) { if (alg != null) {
val t0 = System.currentTimeMillis() val t0 = System.currentTimeMillis()
Timber.v("## encryptEventContent() starts") Timber.v("## encryptEventContent() starts")


@ -739,7 +724,7 @@ internal class CryptoManager(
* @param event the encryption event. * @param event the encryption event.
*/ */
private fun onRoomEncryptionEvent(roomId: String, event: Event) { private fun onRoomEncryptionEvent(roomId: String, event: Event) {
CoroutineScope(coroutineDispatchers.encryption).launch { CoroutineScope(coroutineDispatchers.crypto).launch {
val params = LoadRoomMembersTask.Params(roomId) val params = LoadRoomMembersTask.Params(roomId)
loadRoomMembersTask loadRoomMembersTask
.execute(params) .execute(params)

View File

@ -69,8 +69,8 @@ internal class CryptoModule {
// CryptoStore // CryptoStore
scope(DefaultSession.SCOPE) { scope(DefaultSession.SCOPE) {
RealmCryptoStore(false /* TODO*/, RealmCryptoStore(false /* TODO*/,
get("CryptoRealmConfiguration"), get("CryptoRealmConfiguration"),
get()) as IMXCryptoStore get()) as IMXCryptoStore
} }


scope(DefaultSession.SCOPE) { scope(DefaultSession.SCOPE) {
@ -123,7 +123,7 @@ internal class CryptoModule {
} }


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


scope(DefaultSession.SCOPE) { scope(DefaultSession.SCOPE) {
@ -146,7 +146,7 @@ internal class CryptoModule {
// Factories // Factories
scope(DefaultSession.SCOPE) { scope(DefaultSession.SCOPE) {
MXMegolmDecryptionFactory( MXMegolmDecryptionFactory(
get(), get(), get(), get(), get(), get(), get(), get(), get() get(), get(), get(), get(), get(), get(), get(), get(), get(), get()
) )
} }


@ -164,7 +164,7 @@ internal class CryptoModule {


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


@ -217,7 +217,7 @@ internal class CryptoModule {


// Device list // Device list
scope(DefaultSession.SCOPE) { scope(DefaultSession.SCOPE) {
DeviceListManager(get(), get(), get(), get(), get(), get()) DeviceListManager(get(), get(), get(), get(), get(), get(), get())
} }


// Crypto tasks // Crypto tasks

View File

@ -28,7 +28,9 @@ import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
import im.vector.matrix.android.internal.crypto.tasks.DownloadKeysForUsersTask import im.vector.matrix.android.internal.crypto.tasks.DownloadKeysForUsersTask
import im.vector.matrix.android.internal.session.sync.SyncTokenStore import im.vector.matrix.android.internal.session.sync.SyncTokenStore
import im.vector.matrix.android.internal.task.TaskExecutor 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 im.vector.matrix.android.internal.task.configureWith
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
import timber.log.Timber import timber.log.Timber
import java.util.* import java.util.*


@ -38,6 +40,7 @@ internal class DeviceListManager(private val cryptoStore: IMXCryptoStore,
private val syncTokenStore: SyncTokenStore, private val syncTokenStore: SyncTokenStore,
private val credentials: Credentials, private val credentials: Credentials,
private val downloadKeysForUsersTask: DownloadKeysForUsersTask, private val downloadKeysForUsersTask: DownloadKeysForUsersTask,
private val coroutineDispatchers: MatrixCoroutineDispatchers,
private val taskExecutor: TaskExecutor) { private val taskExecutor: TaskExecutor) {


// keys in progress // keys in progress
@ -457,6 +460,7 @@ internal class DeviceListManager(private val cryptoStore: IMXCryptoStore,


downloadKeysForUsersTask downloadKeysForUsersTask
.configureWith(DownloadKeysForUsersTask.Params(filteredUsers, syncTokenStore.getLastToken())) .configureWith(DownloadKeysForUsersTask.Params(filteredUsers, syncTokenStore.getLastToken()))
.executeOn(TaskThread.ENCRYPTION)
.dispatchTo(object : MatrixCallback<KeysQueryResponse> { .dispatchTo(object : MatrixCallback<KeysQueryResponse> {
override fun onSuccess(data: KeysQueryResponse) { override fun onSuccess(data: KeysQueryResponse) {
CryptoAsyncHelper.getEncryptBackgroundHandler().post { CryptoAsyncHelper.getEncryptBackgroundHandler().post {

View File

@ -30,34 +30,34 @@ open class IncomingRoomKeyRequest {
/** /**
* The user id * The user id
*/ */
var mUserId: String? = null var userId: String? = null


/** /**
* The device id * The device id
*/ */
var mDeviceId: String? = null var deviceId: String? = null


/** /**
* The request id * The request id
*/ */
var mRequestId: String? = null var requestId: String? = null


/** /**
* The request body * The request body
*/ */
var mRequestBody: RoomKeyRequestBody? = null var requestBody: RoomKeyRequestBody? = null


/** /**
* The runnable to call to accept to share the keys * The runnable to call to accept to share the keys
*/ */
@Transient @Transient
var mShare: Runnable? = null var share: Runnable? = null


/** /**
* The runnable to call to ignore the key share request. * The runnable to call to ignore the key share request.
*/ */
@Transient @Transient
var mIgnore: Runnable? = null var ignore: Runnable? = null


/** /**
* Constructor * Constructor
@ -65,11 +65,11 @@ open class IncomingRoomKeyRequest {
* @param event the event * @param event the event
*/ */
constructor(event: Event) { constructor(event: Event) {
mUserId = event.sender userId = event.sender
val roomKeyShareRequest = event.getClearContent().toModel<RoomKeyShareRequest>()!! val roomKeyShareRequest = event.getClearContent().toModel<RoomKeyShareRequest>()!!
mDeviceId = roomKeyShareRequest.requestingDeviceId deviceId = roomKeyShareRequest.requestingDeviceId
mRequestId = roomKeyShareRequest.requestId requestId = roomKeyShareRequest.requestId
mRequestBody = if (null != roomKeyShareRequest.body) roomKeyShareRequest.body else RoomKeyRequestBody() requestBody = if (null != roomKeyShareRequest.body) roomKeyShareRequest.body else RoomKeyRequestBody()
} }


/** /**

View File

@ -24,6 +24,6 @@ import im.vector.matrix.android.api.session.events.model.Event
class IncomingRoomKeyRequestCancellation(event: Event) : IncomingRoomKeyRequest(event) { class IncomingRoomKeyRequestCancellation(event: Event) : IncomingRoomKeyRequest(event) {


init { init {
mRequestBody = null requestBody = null
} }
} }

View File

@ -80,13 +80,13 @@ internal class IncomingRoomKeyRequestManager(


if (null != receivedRoomKeyRequests) { if (null != receivedRoomKeyRequests) {
for (request in receivedRoomKeyRequests!!) { for (request in receivedRoomKeyRequests!!) {
val userId = request.mUserId!! val userId = request.userId!!
val deviceId = request.mDeviceId val deviceId = request.deviceId
val body = request.mRequestBody val body = request.requestBody
val roomId = body!!.roomId val roomId = body!!.roomId
val alg = body.algorithm val alg = body.algorithm


Timber.v("m.room_key_request from " + userId + ":" + deviceId + " for " + roomId + " / " + body.sessionId + " id " + request.mRequestId) Timber.v("m.room_key_request from " + userId + ":" + deviceId + " for " + roomId + " / " + body.sessionId + " id " + request.requestId)


if (!TextUtils.equals(mCredentials.userId, userId)) { if (!TextUtils.equals(mCredentials.userId, userId)) {
// TODO: determine if we sent this device the keys already: in // TODO: determine if we sent this device the keys already: in
@ -119,12 +119,12 @@ internal class IncomingRoomKeyRequestManager(
continue continue
} }


request.mShare = Runnable { request.share = Runnable {
decryptor.shareKeysWithDevice(request) decryptor.shareKeysWithDevice(request)
mCryptoStore.deleteIncomingRoomKeyRequest(request) mCryptoStore.deleteIncomingRoomKeyRequest(request)
} }


request.mIgnore = Runnable { mCryptoStore.deleteIncomingRoomKeyRequest(request) } request.ignore = Runnable { mCryptoStore.deleteIncomingRoomKeyRequest(request) }


// if the device is verified already, share the keys // if the device is verified already, share the keys
val device = mCryptoStore.getUserDevice(deviceId!!, userId) val device = mCryptoStore.getUserDevice(deviceId!!, userId)
@ -133,7 +133,7 @@ internal class IncomingRoomKeyRequestManager(
if (device.isVerified) { if (device.isVerified) {
Timber.v("## processReceivedRoomKeyRequests() : device is already verified: sharing keys") Timber.v("## processReceivedRoomKeyRequests() : device is already verified: sharing keys")
mCryptoStore.deleteIncomingRoomKeyRequest(request) mCryptoStore.deleteIncomingRoomKeyRequest(request)
request.mShare!!.run() request.share!!.run()
continue continue
} }


@ -160,8 +160,8 @@ internal class IncomingRoomKeyRequestManager(


if (null != receivedRoomKeyRequestCancellations) { if (null != receivedRoomKeyRequestCancellations) {
for (request in receivedRoomKeyRequestCancellations!!) { for (request in receivedRoomKeyRequestCancellations!!) {
Timber.v("## ## processReceivedRoomKeyRequests() : m.room_key_request cancellation for " + request.mUserId Timber.v("## ## processReceivedRoomKeyRequests() : m.room_key_request cancellation for " + request.userId
+ ":" + request.mDeviceId + " id " + request.mRequestId) + ":" + request.deviceId + " id " + request.requestId)


// we should probably only notify the app of cancellations we told it // we should probably only notify the app of cancellations we told it
// about, but we don't currently have a record of that, so we just pass // about, but we don't currently have a record of that, so we just pass

View File

@ -28,23 +28,23 @@ data class MXEventDecryptionResult(
/** /**
* The plaintext payload for the event (typically containing "type" and "content" fields). * The plaintext payload for the event (typically containing "type" and "content" fields).
*/ */
var mClearEvent: JsonDict? = null, var clearEvent: JsonDict? = null,


/** /**
* Key owned by the sender of this event. * Key owned by the sender of this event.
* See MXEvent.senderKey. * See MXEvent.senderKey.
*/ */
var mSenderCurve25519Key: String? = null, var senderCurve25519Key: String? = null,


/** /**
* Ed25519 key claimed by the sender of this event. * Ed25519 key claimed by the sender of this event.
* See MXEvent.claimedEd25519Key. * See MXEvent.claimedEd25519Key.
*/ */
var mClaimedEd25519Key: String? = null, var claimedEd25519Key: String? = null,


/** /**
* List of curve25519 keys involved in telling us about the senderCurve25519Key and * List of curve25519 keys involved in telling us about the senderCurve25519Key and
* claimedEd25519Key. See MXEvent.forwardingCurve25519KeyChain. * claimedEd25519Key. See MXEvent.forwardingCurve25519KeyChain.
*/ */
var mForwardingCurve25519KeyChain: List<String> = ArrayList() var forwardingCurve25519KeyChain: List<String> = ArrayList()
) )

View File

@ -32,25 +32,25 @@ import timber.log.Timber
import java.util.* import java.util.*


internal class OutgoingRoomKeyRequestManager( internal class OutgoingRoomKeyRequestManager(
private val mCryptoStore: IMXCryptoStore, private val cryptoStore: IMXCryptoStore,
private val mSendToDeviceTask: SendToDeviceTask, private val sendToDeviceTask: SendToDeviceTask,
private val mTaskExecutor: TaskExecutor) { private val taskExecutor: TaskExecutor) {


// running // running
private var mClientRunning: Boolean = false private var isClientRunning: Boolean = false


// transaction counter // transaction counter
private var mTxnCtr: Int = 0 private var txnCtr: Int = 0


// sanity check to ensure that we don't end up with two concurrent runs // sanity check to ensure that we don't end up with two concurrent runs
// of mSendOutgoingRoomKeyRequestsTimer // of sendOutgoingRoomKeyRequestsTimer
private var mSendOutgoingRoomKeyRequestsRunning: Boolean = false private var sendOutgoingRoomKeyRequestsRunning: Boolean = false


/** /**
* Called when the client is started. Sets background processes running. * Called when the client is started. Sets background processes running.
*/ */
fun start() { fun start() {
mClientRunning = true isClientRunning = true
startTimer() startTimer()
} }


@ -58,7 +58,7 @@ internal class OutgoingRoomKeyRequestManager(
* Called when the client is stopped. Stops any running background processes. * Called when the client is stopped. Stops any running background processes.
*/ */
fun stop() { fun stop() {
mClientRunning = false isClientRunning = false
} }


/** /**
@ -67,7 +67,7 @@ internal class OutgoingRoomKeyRequestManager(
* @return {string} a new, unique, transaction id * @return {string} a new, unique, transaction id
*/ */
private fun makeTxnId(): String { private fun makeTxnId(): String {
return "m" + System.currentTimeMillis() + "." + mTxnCtr++ return "m" + System.currentTimeMillis() + "." + txnCtr++
} }


/** /**
@ -83,7 +83,7 @@ internal class OutgoingRoomKeyRequestManager(
* @param recipients recipients * @param recipients recipients
*/ */
fun sendRoomKeyRequest(requestBody: RoomKeyRequestBody?, recipients: List<Map<String, String>>) { fun sendRoomKeyRequest(requestBody: RoomKeyRequestBody?, recipients: List<Map<String, String>>) {
val req = mCryptoStore.getOrAddOutgoingRoomKeyRequest( val req = cryptoStore.getOrAddOutgoingRoomKeyRequest(
OutgoingRoomKeyRequest(requestBody, recipients, makeTxnId(), OutgoingRoomKeyRequest.RequestState.UNSENT)) OutgoingRoomKeyRequest(requestBody, recipients, makeTxnId(), OutgoingRoomKeyRequest.RequestState.UNSENT))




@ -117,9 +117,9 @@ internal class OutgoingRoomKeyRequestManager(
* @param andResend true to resend the key request * @param andResend true to resend the key request
*/ */
private fun cancelRoomKeyRequest(requestBody: RoomKeyRequestBody, andResend: Boolean) { private fun cancelRoomKeyRequest(requestBody: RoomKeyRequestBody, andResend: Boolean) {
val req = mCryptoStore.getOutgoingRoomKeyRequest(requestBody) val req = cryptoStore.getOutgoingRoomKeyRequest(requestBody)
?: // no request was made for this key ?: // no request was made for this key
return return


Timber.v("cancelRoomKeyRequest: requestId: " + req.mRequestId + " state: " + req.mState + " andResend: " + andResend) Timber.v("cancelRoomKeyRequest: requestId: " + req.mRequestId + " state: " + req.mState + " andResend: " + andResend)


@ -127,7 +127,7 @@ internal class OutgoingRoomKeyRequestManager(
// nothing to do here // nothing to do here
} else if (req.mState === OutgoingRoomKeyRequest.RequestState.UNSENT || req.mState === OutgoingRoomKeyRequest.RequestState.FAILED) { } else if (req.mState === OutgoingRoomKeyRequest.RequestState.UNSENT || req.mState === OutgoingRoomKeyRequest.RequestState.FAILED) {
Timber.v("## cancelRoomKeyRequest() : deleting unnecessary room key request for $requestBody") Timber.v("## cancelRoomKeyRequest() : deleting unnecessary room key request for $requestBody")
mCryptoStore.deleteOutgoingRoomKeyRequest(req.mRequestId) cryptoStore.deleteOutgoingRoomKeyRequest(req.mRequestId)
} else if (req.mState === OutgoingRoomKeyRequest.RequestState.SENT) { } else if (req.mState === OutgoingRoomKeyRequest.RequestState.SENT) {
if (andResend) { if (andResend) {
req.mState = OutgoingRoomKeyRequest.RequestState.CANCELLATION_PENDING_AND_WILL_RESEND req.mState = OutgoingRoomKeyRequest.RequestState.CANCELLATION_PENDING_AND_WILL_RESEND
@ -135,7 +135,7 @@ internal class OutgoingRoomKeyRequestManager(
req.mState = OutgoingRoomKeyRequest.RequestState.CANCELLATION_PENDING req.mState = OutgoingRoomKeyRequest.RequestState.CANCELLATION_PENDING
} }
req.mCancellationTxnId = makeTxnId() req.mCancellationTxnId = makeTxnId()
mCryptoStore.updateOutgoingRoomKeyRequest(req) cryptoStore.updateOutgoingRoomKeyRequest(req)
sendOutgoingRoomKeyRequestCancellation(req) sendOutgoingRoomKeyRequestCancellation(req)
} }
} }
@ -145,17 +145,17 @@ internal class OutgoingRoomKeyRequestManager(
* Start the background timer to send queued requests, if the timer isn't already running. * Start the background timer to send queued requests, if the timer isn't already running.
*/ */
private fun startTimer() { private fun startTimer() {
if (mSendOutgoingRoomKeyRequestsRunning) { if (sendOutgoingRoomKeyRequestsRunning) {
return return
} }


Handler().postDelayed(Runnable { Handler().postDelayed(Runnable {
if (mSendOutgoingRoomKeyRequestsRunning) { if (sendOutgoingRoomKeyRequestsRunning) {
Timber.v("## startTimer() : RoomKeyRequestSend already in progress!") Timber.v("## startTimer() : RoomKeyRequestSend already in progress!")
return@Runnable return@Runnable
} }


mSendOutgoingRoomKeyRequestsRunning = true sendOutgoingRoomKeyRequestsRunning = true
sendOutgoingRoomKeyRequests() sendOutgoingRoomKeyRequests()
}, SEND_KEY_REQUESTS_DELAY_MS.toLong()) }, SEND_KEY_REQUESTS_DELAY_MS.toLong())
} }
@ -164,20 +164,20 @@ internal class OutgoingRoomKeyRequestManager(
// there are no more requests, or there is an error (in which case, the // there are no more requests, or there is an error (in which case, the
// timer will be restarted before the promise resolves). // timer will be restarted before the promise resolves).
private fun sendOutgoingRoomKeyRequests() { private fun sendOutgoingRoomKeyRequests() {
if (!mClientRunning) { if (!isClientRunning) {
mSendOutgoingRoomKeyRequestsRunning = false sendOutgoingRoomKeyRequestsRunning = false
return return
} }


Timber.v("## sendOutgoingRoomKeyRequests() : Looking for queued outgoing room key requests") Timber.v("## sendOutgoingRoomKeyRequests() : Looking for queued outgoing room key requests")
val outgoingRoomKeyRequest = mCryptoStore.getOutgoingRoomKeyRequestByState( val outgoingRoomKeyRequest = cryptoStore.getOutgoingRoomKeyRequestByState(
HashSet<OutgoingRoomKeyRequest.RequestState>(Arrays.asList<OutgoingRoomKeyRequest.RequestState>(OutgoingRoomKeyRequest.RequestState.UNSENT, HashSet<OutgoingRoomKeyRequest.RequestState>(Arrays.asList<OutgoingRoomKeyRequest.RequestState>(OutgoingRoomKeyRequest.RequestState.UNSENT,
OutgoingRoomKeyRequest.RequestState.CANCELLATION_PENDING, OutgoingRoomKeyRequest.RequestState.CANCELLATION_PENDING,
OutgoingRoomKeyRequest.RequestState.CANCELLATION_PENDING_AND_WILL_RESEND))) OutgoingRoomKeyRequest.RequestState.CANCELLATION_PENDING_AND_WILL_RESEND)))


if (null == outgoingRoomKeyRequest) { if (null == outgoingRoomKeyRequest) {
Timber.e("## sendOutgoingRoomKeyRequests() : No more outgoing room key requests") Timber.e("## sendOutgoingRoomKeyRequests() : No more outgoing room key requests")
mSendOutgoingRoomKeyRequestsRunning = false sendOutgoingRoomKeyRequestsRunning = false
return return
} }


@ -198,7 +198,7 @@ internal class OutgoingRoomKeyRequestManager(
+ " from " + request.mRecipients + " id " + request.mRequestId) + " from " + request.mRecipients + " id " + request.mRequestId)


val requestMessage = RoomKeyShareRequest() val requestMessage = RoomKeyShareRequest()
requestMessage.requestingDeviceId = mCryptoStore.getDeviceId() requestMessage.requestingDeviceId = cryptoStore.getDeviceId()
requestMessage.requestId = request.mRequestId requestMessage.requestId = request.mRequestId
requestMessage.body = request.mRequestBody requestMessage.body = request.mRequestBody


@ -208,10 +208,10 @@ internal class OutgoingRoomKeyRequestManager(
Timber.v("## sendOutgoingRoomKeyRequest() : Cannot update room key request from UNSENT as it was already updated to " + request.mState) Timber.v("## sendOutgoingRoomKeyRequest() : Cannot update room key request from UNSENT as it was already updated to " + request.mState)
} else { } else {
request.mState = state request.mState = state
mCryptoStore.updateOutgoingRoomKeyRequest(request) cryptoStore.updateOutgoingRoomKeyRequest(request)
} }


mSendOutgoingRoomKeyRequestsRunning = false sendOutgoingRoomKeyRequestsRunning = false
startTimer() startTimer()
} }


@ -238,13 +238,13 @@ internal class OutgoingRoomKeyRequestManager(
+ " cancellation id " + request.mCancellationTxnId) + " cancellation id " + request.mCancellationTxnId)


val roomKeyShareCancellation = RoomKeyShareCancellation() val roomKeyShareCancellation = RoomKeyShareCancellation()
roomKeyShareCancellation.requestingDeviceId = mCryptoStore.getDeviceId() roomKeyShareCancellation.requestingDeviceId = cryptoStore.getDeviceId()
roomKeyShareCancellation.requestId = request.mCancellationTxnId roomKeyShareCancellation.requestId = request.mCancellationTxnId


sendMessageToDevices(roomKeyShareCancellation, request.mRecipients, request.mCancellationTxnId, object : MatrixCallback<Unit> { sendMessageToDevices(roomKeyShareCancellation, request.mRecipients, request.mCancellationTxnId, object : MatrixCallback<Unit> {
private fun onDone() { private fun onDone() {
mCryptoStore.deleteOutgoingRoomKeyRequest(request.mRequestId) cryptoStore.deleteOutgoingRoomKeyRequest(request.mRequestId)
mSendOutgoingRoomKeyRequestsRunning = false sendOutgoingRoomKeyRequestsRunning = false
startTimer() startTimer()
} }


@ -286,9 +286,9 @@ internal class OutgoingRoomKeyRequestManager(
contentMap.setObject(message, recipient["userId"], recipient["deviceId"]) // TODO Change this two hard coded key to something better contentMap.setObject(message, recipient["userId"], recipient["deviceId"]) // TODO Change this two hard coded key to something better
} }


mSendToDeviceTask.configureWith(SendToDeviceTask.Params(EventType.ROOM_KEY_REQUEST, contentMap, transactionId)) sendToDeviceTask.configureWith(SendToDeviceTask.Params(EventType.ROOM_KEY_REQUEST, contentMap, transactionId))
.dispatchTo(callback) .dispatchTo(callback)
.executeBy(mTaskExecutor) .executeBy(taskExecutor)
} }


companion object { companion object {

View File

@ -68,8 +68,8 @@ internal class RoomDecryptorProvider(


if (decryptingClass) { if (decryptingClass) {
alg = when (algorithm) { alg = when (algorithm) {
MXCRYPTO_ALGORITHM_MEGOLM -> mMXMegolmDecryptionFactory.instantiate() MXCRYPTO_ALGORITHM_MEGOLM -> mMXMegolmDecryptionFactory.create()
else -> mMXOlmDecryptionFactory.instantiate() else -> mMXOlmDecryptionFactory.create()
} }


if (null != alg) { if (null != alg) {

View File

@ -26,12 +26,14 @@ import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap
import im.vector.matrix.android.internal.crypto.tasks.ClaimOneTimeKeysForUsersDeviceTask import im.vector.matrix.android.internal.crypto.tasks.ClaimOneTimeKeysForUsersDeviceTask
import im.vector.matrix.android.internal.task.TaskExecutor import im.vector.matrix.android.internal.task.TaskExecutor
import im.vector.matrix.android.internal.task.configureWith import im.vector.matrix.android.internal.task.configureWith
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
import timber.log.Timber import timber.log.Timber
import java.util.* import java.util.*


internal class EnsureOlmSessionsForDevicesAction(private val mOlmDevice: MXOlmDevice, internal class EnsureOlmSessionsForDevicesAction(private val olmDevice: MXOlmDevice,
private val mClaimOneTimeKeysForUsersDeviceTask: ClaimOneTimeKeysForUsersDeviceTask, private val oneTimeKeysForUsersDeviceTask: ClaimOneTimeKeysForUsersDeviceTask,
private val mTaskExecutor: TaskExecutor) { private val coroutineDispatchers: MatrixCoroutineDispatchers,
private val taskExecutor: TaskExecutor) {




fun handle(devicesByUser: Map<String, List<MXDeviceInfo>>, callback: MatrixCallback<MXUsersDevicesMap<MXOlmSessionResult>>?) { fun handle(devicesByUser: Map<String, List<MXDeviceInfo>>, callback: MatrixCallback<MXUsersDevicesMap<MXOlmSessionResult>>?) {
@ -48,7 +50,7 @@ internal class EnsureOlmSessionsForDevicesAction(private val mOlmDevice: MXOlmDe
val deviceId = deviceInfo.deviceId val deviceId = deviceInfo.deviceId
val key = deviceInfo.identityKey() val key = deviceInfo.identityKey()


val sessionId = mOlmDevice.getSessionId(key!!) val sessionId = olmDevice.getSessionId(key!!)


if (TextUtils.isEmpty(sessionId)) { if (TextUtils.isEmpty(sessionId)) {
devicesWithoutSession.add(deviceInfo) devicesWithoutSession.add(deviceInfo)
@ -81,7 +83,8 @@ internal class EnsureOlmSessionsForDevicesAction(private val mOlmDevice: MXOlmDe


Timber.v("## claimOneTimeKeysForUsersDevices() : $usersDevicesToClaim") Timber.v("## claimOneTimeKeysForUsersDevices() : $usersDevicesToClaim")


mClaimOneTimeKeysForUsersDeviceTask
oneTimeKeysForUsersDeviceTask
.configureWith(ClaimOneTimeKeysForUsersDeviceTask.Params(usersDevicesToClaim)) .configureWith(ClaimOneTimeKeysForUsersDeviceTask.Params(usersDevicesToClaim))
.dispatchTo(object : MatrixCallback<MXUsersDevicesMap<MXKey>> { .dispatchTo(object : MatrixCallback<MXUsersDevicesMap<MXKey>> {
override fun onSuccess(data: MXUsersDevicesMap<MXKey>) { override fun onSuccess(data: MXUsersDevicesMap<MXKey>) {
@ -137,7 +140,7 @@ internal class EnsureOlmSessionsForDevicesAction(private val mOlmDevice: MXOlmDe
callback?.onFailure(failure) callback?.onFailure(failure)
} }
}) })
.executeBy(mTaskExecutor) .executeBy(taskExecutor)
} }




@ -153,7 +156,7 @@ internal class EnsureOlmSessionsForDevicesAction(private val mOlmDevice: MXOlmDe
var errorMessage: String? = null var errorMessage: String? = null


try { try {
mOlmDevice.verifySignature(deviceInfo.fingerprint()!!, oneTimeKey.signalableJSONDictionary(), signature) olmDevice.verifySignature(deviceInfo.fingerprint()!!, oneTimeKey.signalableJSONDictionary(), signature)
isVerified = true isVerified = true
} catch (e: Exception) { } catch (e: Exception) {
errorMessage = e.message errorMessage = e.message
@ -161,7 +164,7 @@ internal class EnsureOlmSessionsForDevicesAction(private val mOlmDevice: MXOlmDe


// Check one-time key signature // Check one-time key signature
if (isVerified) { if (isVerified) {
sessionId = mOlmDevice.createOutboundSession(deviceInfo.identityKey()!!, oneTimeKey.value) sessionId = olmDevice.createOutboundSession(deviceInfo.identityKey()!!, oneTimeKey.value)


if (!TextUtils.isEmpty(sessionId)) { if (!TextUtils.isEmpty(sessionId)) {
Timber.v("## verifyKeyAndStartSession() : Started new sessionid " + sessionId Timber.v("## verifyKeyAndStartSession() : Started new sessionid " + sessionId

View File

@ -26,9 +26,9 @@ import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
import timber.log.Timber import timber.log.Timber
import java.util.* import java.util.*


internal class EnsureOlmSessionsForUsersAction(private val mOlmDevice: MXOlmDevice, internal class EnsureOlmSessionsForUsersAction(private val olmDevice: MXOlmDevice,
private val mCryptoStore: IMXCryptoStore, private val cryptoStore: IMXCryptoStore,
private val mEnsureOlmSessionsForDevicesAction: EnsureOlmSessionsForDevicesAction) { private val ensureOlmSessionsForDevicesAction: EnsureOlmSessionsForDevicesAction) {


/** /**
* Try to make sure we have established olm sessions for the given users. * Try to make sure we have established olm sessions for the given users.
@ -46,12 +46,12 @@ internal class EnsureOlmSessionsForUsersAction(private val mOlmDevice: MXOlmDevi
for (userId in users) { for (userId in users) {
devicesByUser[userId] = ArrayList() devicesByUser[userId] = ArrayList()


val devices = mCryptoStore.getUserDevices(userId)?.values ?: emptyList() val devices = cryptoStore.getUserDevices(userId)?.values ?: emptyList()


for (device in devices) { for (device in devices) {
val key = device.identityKey() val key = device.identityKey()


if (TextUtils.equals(key, mOlmDevice.deviceCurve25519Key)) { if (TextUtils.equals(key, olmDevice.deviceCurve25519Key)) {
// Don't bother setting up session to ourself // Don't bother setting up session to ourself
continue continue
} }
@ -65,6 +65,6 @@ internal class EnsureOlmSessionsForUsersAction(private val mOlmDevice: MXOlmDevi
} }
} }


mEnsureOlmSessionsForDevicesAction.handle(devicesByUser, callback) ensureOlmSessionsForDevicesAction.handle(devicesByUser, callback)
} }
} }

View File

@ -24,7 +24,13 @@ import im.vector.matrix.android.api.session.crypto.MXCryptoError
import im.vector.matrix.android.api.session.events.model.Event 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.EventType
import im.vector.matrix.android.api.session.events.model.toModel import im.vector.matrix.android.api.session.events.model.toModel
import im.vector.matrix.android.internal.crypto.* import im.vector.matrix.android.internal.crypto.CryptoAsyncHelper
import im.vector.matrix.android.internal.crypto.DeviceListManager
import im.vector.matrix.android.internal.crypto.IncomingRoomKeyRequest
import im.vector.matrix.android.internal.crypto.MXDecryptionException
import im.vector.matrix.android.internal.crypto.MXEventDecryptionResult
import im.vector.matrix.android.internal.crypto.MXOlmDevice
import im.vector.matrix.android.internal.crypto.OutgoingRoomKeyRequestManager
import im.vector.matrix.android.internal.crypto.actions.EnsureOlmSessionsForDevicesAction import im.vector.matrix.android.internal.crypto.actions.EnsureOlmSessionsForDevicesAction
import im.vector.matrix.android.internal.crypto.actions.MessageEncrypter import im.vector.matrix.android.internal.crypto.actions.MessageEncrypter
import im.vector.matrix.android.internal.crypto.algorithms.IMXDecrypting import im.vector.matrix.android.internal.crypto.algorithms.IMXDecrypting
@ -39,27 +45,28 @@ import im.vector.matrix.android.internal.crypto.model.rest.ForwardedRoomKeyConte
import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyRequestBody import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyRequestBody
import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
import im.vector.matrix.android.internal.crypto.tasks.SendToDeviceTask import im.vector.matrix.android.internal.crypto.tasks.SendToDeviceTask
import im.vector.matrix.android.internal.task.TaskExecutor import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
import im.vector.matrix.android.internal.task.configureWith import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import timber.log.Timber import timber.log.Timber
import java.util.* import java.util.*


internal class MXMegolmDecryption(private val mCredentials: Credentials, internal class MXMegolmDecryption(private val credentials: Credentials,
private val mOlmDevice: MXOlmDevice, private val olmDevice: MXOlmDevice,
private val mDeviceListManager: DeviceListManager, private val deviceListManager: DeviceListManager,
private val mOutgoingRoomKeyRequestManager: OutgoingRoomKeyRequestManager, private val outgoingRoomKeyRequestManager: OutgoingRoomKeyRequestManager,
private val mMessageEncrypter: MessageEncrypter, private val messageEncrypter: MessageEncrypter,
private val mEnsureOlmSessionsForDevicesAction: EnsureOlmSessionsForDevicesAction, private val ensureOlmSessionsForDevicesAction: EnsureOlmSessionsForDevicesAction,
private val mCryptoStore: IMXCryptoStore, private val cryptoStore: IMXCryptoStore,
private val mSendToDeviceTask: SendToDeviceTask, private val sendToDeviceTask: SendToDeviceTask,
private val mTaskExecutor: TaskExecutor) private val coroutineDispatchers: MatrixCoroutineDispatchers)
: IMXDecrypting { : IMXDecrypting {


/** /**
* Events which we couldn't decrypt due to unknown sessions / indexes: map from * Events which we couldn't decrypt due to unknown sessions / indexes: map from
* senderKey|sessionId to timelines to list of MatrixEvents. * senderKey|sessionId to timelines to list of MatrixEvents.
*/ */
private var mPendingEvents: MutableMap<String /* senderKey|sessionId */, MutableMap<String /* timelineId */, MutableList<Event>>> = HashMap() private var pendingEvents: MutableMap<String /* senderKey|sessionId */, MutableMap<String /* timelineId */, MutableList<Event>>> = HashMap()


@Throws(MXDecryptionException::class) @Throws(MXDecryptionException::class)
override fun decryptEvent(event: Event, timeline: String): MXEventDecryptionResult? { override fun decryptEvent(event: Event, timeline: String): MXEventDecryptionResult? {
@ -72,7 +79,7 @@ internal class MXMegolmDecryption(private val mCredentials: Credentials,
val encryptedEventContent = event.content.toModel<EncryptedEventContent>()!! val encryptedEventContent = event.content.toModel<EncryptedEventContent>()!!
if (TextUtils.isEmpty(encryptedEventContent.senderKey) || TextUtils.isEmpty(encryptedEventContent.sessionId) || TextUtils.isEmpty(encryptedEventContent.ciphertext)) { if (TextUtils.isEmpty(encryptedEventContent.senderKey) || TextUtils.isEmpty(encryptedEventContent.sessionId) || TextUtils.isEmpty(encryptedEventContent.ciphertext)) {
throw MXDecryptionException(MXCryptoError(MXCryptoError.MISSING_FIELDS_ERROR_CODE, throw MXDecryptionException(MXCryptoError(MXCryptoError.MISSING_FIELDS_ERROR_CODE,
MXCryptoError.UNABLE_TO_DECRYPT, MXCryptoError.MISSING_FIELDS_REASON)) MXCryptoError.UNABLE_TO_DECRYPT, MXCryptoError.MISSING_FIELDS_REASON))
} }


var eventDecryptionResult: MXEventDecryptionResult? = null var eventDecryptionResult: MXEventDecryptionResult? = null
@ -80,24 +87,24 @@ internal class MXMegolmDecryption(private val mCredentials: Credentials,
var decryptGroupMessageResult: MXDecryptionResult? = null var decryptGroupMessageResult: MXDecryptionResult? = null


try { try {
decryptGroupMessageResult = mOlmDevice.decryptGroupMessage(encryptedEventContent.ciphertext!!, event.roomId!!, timeline, encryptedEventContent.sessionId!!, encryptedEventContent.senderKey!!) decryptGroupMessageResult = olmDevice.decryptGroupMessage(encryptedEventContent.ciphertext!!, event.roomId!!, timeline, encryptedEventContent.sessionId!!, encryptedEventContent.senderKey!!)
} catch (e: MXDecryptionException) { } catch (e: MXDecryptionException) {
cryptoError = e.cryptoError cryptoError = e.cryptoError
} }


// the decryption succeeds // the decryption succeeds
if (null != decryptGroupMessageResult && null != decryptGroupMessageResult.mPayload && null == cryptoError) { if (decryptGroupMessageResult?.mPayload != null && cryptoError == null) {
eventDecryptionResult = MXEventDecryptionResult() eventDecryptionResult = MXEventDecryptionResult()


eventDecryptionResult.mClearEvent = decryptGroupMessageResult.mPayload eventDecryptionResult.clearEvent = decryptGroupMessageResult.mPayload
eventDecryptionResult.mSenderCurve25519Key = decryptGroupMessageResult.mSenderKey eventDecryptionResult.senderCurve25519Key = decryptGroupMessageResult.mSenderKey


if (null != decryptGroupMessageResult.mKeysClaimed) { if (null != decryptGroupMessageResult.mKeysClaimed) {
eventDecryptionResult.mClaimedEd25519Key = decryptGroupMessageResult.mKeysClaimed!!["ed25519"] eventDecryptionResult.claimedEd25519Key = decryptGroupMessageResult.mKeysClaimed!!["ed25519"]
} }


eventDecryptionResult.mForwardingCurve25519KeyChain = decryptGroupMessageResult.mForwardingCurve25519KeyChain!! eventDecryptionResult.forwardingCurve25519KeyChain = decryptGroupMessageResult.mForwardingCurve25519KeyChain!!
} else if (null != cryptoError) { } else if (cryptoError != null) {
if (cryptoError.isOlmError) { if (cryptoError.isOlmError) {
if (TextUtils.equals("UNKNOWN_MESSAGE_INDEX", cryptoError.message)) { if (TextUtils.equals("UNKNOWN_MESSAGE_INDEX", cryptoError.message)) {
addEventToPendingList(event, timeline) addEventToPendingList(event, timeline)
@ -120,7 +127,6 @@ internal class MXMegolmDecryption(private val mCredentials: Credentials,
requestKeysForEvent(event) requestKeysForEvent(event)
} }
} }

throw MXDecryptionException(cryptoError) throw MXDecryptionException(cryptoError)
} }


@ -141,11 +147,11 @@ internal class MXMegolmDecryption(private val mCredentials: Credentials,
val recipients = ArrayList<Map<String, String>>() val recipients = ArrayList<Map<String, String>>()


val selfMap = HashMap<String, String>() val selfMap = HashMap<String, String>()
selfMap["userId"] = mCredentials.userId // TODO Replace this hard coded keys (see OutgoingRoomKeyRequestManager) selfMap["userId"] = credentials.userId // TODO Replace this hard coded keys (see OutgoingRoomKeyRequestManager)
selfMap["deviceId"] = "*" selfMap["deviceId"] = "*"
recipients.add(selfMap) recipients.add(selfMap)


if (!TextUtils.equals(sender, mCredentials.userId)) { if (!TextUtils.equals(sender, credentials.userId)) {
val senderMap = HashMap<String, String>() val senderMap = HashMap<String, String>()
senderMap["userId"] = sender senderMap["userId"] = sender
senderMap["deviceId"] = encryptedEventContent.deviceId!! senderMap["deviceId"] = encryptedEventContent.deviceId!!
@ -159,7 +165,7 @@ internal class MXMegolmDecryption(private val mCredentials: Credentials,
requestBody.senderKey = encryptedEventContent.senderKey requestBody.senderKey = encryptedEventContent.senderKey
requestBody.sessionId = encryptedEventContent.sessionId requestBody.sessionId = encryptedEventContent.sessionId


mOutgoingRoomKeyRequestManager.sendRoomKeyRequest(requestBody, recipients) outgoingRoomKeyRequestManager.sendRoomKeyRequest(requestBody, recipients)
} }


/** /**
@ -174,17 +180,17 @@ internal class MXMegolmDecryption(private val mCredentials: Credentials,


val k = "${encryptedEventContent.senderKey}|${encryptedEventContent.sessionId}" val k = "${encryptedEventContent.senderKey}|${encryptedEventContent.sessionId}"


if (!mPendingEvents.containsKey(k)) { if (!pendingEvents.containsKey(k)) {
mPendingEvents[k] = HashMap() pendingEvents[k] = HashMap()
} }


if (!mPendingEvents[k]!!.containsKey(timelineId)) { if (!pendingEvents[k]!!.containsKey(timelineId)) {
mPendingEvents[k]!!.put(timelineId, ArrayList<Event>()) pendingEvents[k]!!.put(timelineId, ArrayList<Event>())
} }


if (mPendingEvents[k]!![timelineId]!!.indexOf(event) < 0) { if (pendingEvents[k]!![timelineId]!!.indexOf(event) < 0) {
Timber.v("## addEventToPendingList() : add Event " + event.eventId + " in room id " + event.roomId) Timber.v("## addEventToPendingList() : add Event " + event.eventId + " in room id " + event.roomId)
mPendingEvents[k]!![timelineId]!!.add(event) pendingEvents[k]!![timelineId]!!.add(event)
} }
} }


@ -208,7 +214,7 @@ internal class MXMegolmDecryption(private val mCredentials: Credentials,


if (event.getClearType() == EventType.FORWARDED_ROOM_KEY) { if (event.getClearType() == EventType.FORWARDED_ROOM_KEY) {
Timber.v("## onRoomKeyEvent(), forward adding key : roomId " + roomKeyContent.roomId + " sessionId " + roomKeyContent.sessionId Timber.v("## onRoomKeyEvent(), forward adding key : roomId " + roomKeyContent.roomId + " sessionId " + roomKeyContent.sessionId
+ " sessionKey " + roomKeyContent.sessionKey) // from " + event); + " sessionKey " + roomKeyContent.sessionKey) // from " + event);
val forwardedRoomKeyContent = event.getClearContent().toModel<ForwardedRoomKeyContent>()!! val forwardedRoomKeyContent = event.getClearContent().toModel<ForwardedRoomKeyContent>()!!


if (null == forwardedRoomKeyContent.forwardingCurve25519KeyChain) { if (null == forwardedRoomKeyContent.forwardingCurve25519KeyChain) {
@ -234,7 +240,7 @@ internal class MXMegolmDecryption(private val mCredentials: Credentials,
keysClaimed["ed25519"] = forwardedRoomKeyContent.senderClaimedEd25519Key!! keysClaimed["ed25519"] = forwardedRoomKeyContent.senderClaimedEd25519Key!!
} else { } else {
Timber.v("## onRoomKeyEvent(), Adding key : roomId " + roomKeyContent.roomId + " sessionId " + roomKeyContent.sessionId Timber.v("## onRoomKeyEvent(), Adding key : roomId " + roomKeyContent.roomId + " sessionId " + roomKeyContent.sessionId
+ " sessionKey " + roomKeyContent.sessionKey) // from " + event); + " sessionKey " + roomKeyContent.sessionKey) // from " + event);


if (null == senderKey) { if (null == senderKey) {
Timber.e("## onRoomKeyEvent() : key event has no sender key (not encrypted?)") Timber.e("## onRoomKeyEvent() : key event has no sender key (not encrypted?)")
@ -245,7 +251,7 @@ internal class MXMegolmDecryption(private val mCredentials: Credentials,
keysClaimed = event.getKeysClaimed().toMutableMap() keysClaimed = event.getKeysClaimed().toMutableMap()
} }


val added = mOlmDevice.addInboundGroupSession(roomKeyContent.sessionId!!, roomKeyContent.sessionKey!!, roomKeyContent.roomId!!, senderKey, forwarding_curve25519_key_chain!!, keysClaimed, exportFormat) val added = olmDevice.addInboundGroupSession(roomKeyContent.sessionId!!, roomKeyContent.sessionKey!!, roomKeyContent.roomId!!, senderKey, forwarding_curve25519_key_chain!!, keysClaimed, exportFormat)


if (added) { if (added) {
keysBackup.maybeBackupKeys() keysBackup.maybeBackupKeys()
@ -257,7 +263,7 @@ internal class MXMegolmDecryption(private val mCredentials: Credentials,
content.sessionId = roomKeyContent.sessionId content.sessionId = roomKeyContent.sessionId
content.senderKey = senderKey content.senderKey = senderKey


mOutgoingRoomKeyRequestManager.cancelRoomKeyRequest(content) outgoingRoomKeyRequestManager.cancelRoomKeyRequest(content)


onNewSession(senderKey, roomKeyContent.sessionId!!) onNewSession(senderKey, roomKeyContent.sessionId!!)
} }
@ -272,11 +278,11 @@ internal class MXMegolmDecryption(private val mCredentials: Credentials,
override fun onNewSession(senderKey: String, sessionId: String) { override fun onNewSession(senderKey: String, sessionId: String) {
val k = "$senderKey|$sessionId" val k = "$senderKey|$sessionId"


val pending = mPendingEvents[k] val pending = pendingEvents[k]


if (null != pending) { if (null != pending) {
// Have another go at decrypting events sent with this session. // Have another go at decrypting events sent with this session.
mPendingEvents.remove(k) pendingEvents.remove(k)


val timelineIds = pending.keys val timelineIds = pending.keys


@ -307,81 +313,81 @@ internal class MXMegolmDecryption(private val mCredentials: Credentials,
} }


override fun hasKeysForKeyRequest(request: IncomingRoomKeyRequest): Boolean { override fun hasKeysForKeyRequest(request: IncomingRoomKeyRequest): Boolean {
return (null != request.mRequestBody return (null != request.requestBody
&& mOlmDevice.hasInboundSessionKeys(request.mRequestBody!!.roomId!!, request.mRequestBody!!.senderKey!!, request.mRequestBody!!.sessionId!!)) && olmDevice.hasInboundSessionKeys(request.requestBody!!.roomId!!, request.requestBody!!.senderKey!!, request.requestBody!!.sessionId!!))
} }


override fun shareKeysWithDevice(request: IncomingRoomKeyRequest) { override fun shareKeysWithDevice(request: IncomingRoomKeyRequest) {
// sanity checks // sanity checks
if (request.mRequestBody == null) { if (request.requestBody == null) {
return return
} }
val userId = request.userId!!
CoroutineScope(coroutineDispatchers.crypto).launch {
deviceListManager.downloadKeys(listOf(userId), false, object : MatrixCallback<MXUsersDevicesMap<MXDeviceInfo>> {
override fun onSuccess(data: MXUsersDevicesMap<MXDeviceInfo>) {
val deviceId = request.deviceId
val deviceInfo = cryptoStore.getUserDevice(deviceId!!, userId)


val userId = request.mUserId!! if (null != deviceInfo) {
val body = request.requestBody


mDeviceListManager.downloadKeys(listOf(userId), false, object : MatrixCallback<MXUsersDevicesMap<MXDeviceInfo>> { val devicesByUser = HashMap<String, List<MXDeviceInfo>>()
override fun onSuccess(data: MXUsersDevicesMap<MXDeviceInfo>) { devicesByUser[userId] = ArrayList(Arrays.asList(deviceInfo))
val deviceId = request.mDeviceId
val deviceInfo = mCryptoStore.getUserDevice(deviceId!!, userId)


if (null != deviceInfo) { ensureOlmSessionsForDevicesAction.handle(devicesByUser, object : MatrixCallback<MXUsersDevicesMap<MXOlmSessionResult>> {
val body = request.mRequestBody override fun onSuccess(data: MXUsersDevicesMap<MXOlmSessionResult>) {
val olmSessionResult = data.getObject(deviceId, userId)


val devicesByUser = HashMap<String, List<MXDeviceInfo>>() if (olmSessionResult?.mSessionId == null) {
devicesByUser[userId] = ArrayList(Arrays.asList(deviceInfo)) // no session with this device, probably because there
// were no one-time keys.
//
// ensureOlmSessionsForUsers has already done the logging,
// so just skip it.
return
}
Timber.v("## shareKeysWithDevice() : sharing keys for session " + body!!.senderKey + "|" + body.sessionId
+ " with device " + userId + ":" + deviceId)
val inboundGroupSession = olmDevice.getInboundGroupSession(body.sessionId, body.senderKey, body.roomId)


mEnsureOlmSessionsForDevicesAction.handle(devicesByUser, object : MatrixCallback<MXUsersDevicesMap<MXOlmSessionResult>> { val payloadJson = HashMap<String, Any>()
override fun onSuccess(data: MXUsersDevicesMap<MXOlmSessionResult>) { payloadJson["type"] = EventType.FORWARDED_ROOM_KEY
val olmSessionResult = data.getObject(deviceId, userId) payloadJson["content"] = inboundGroupSession!!.exportKeys()!!


if (null == olmSessionResult || null == olmSessionResult.mSessionId) { val encodedPayload = messageEncrypter.encryptMessage(payloadJson, Arrays.asList(deviceInfo))
// no session with this device, probably because there val sendToDeviceMap = MXUsersDevicesMap<Any>()
// were no one-time keys. sendToDeviceMap.setObject(encodedPayload, userId, deviceId)
// Timber.v("## shareKeysWithDevice() : sending to $userId:$deviceId")
// ensureOlmSessionsForUsers has already done the logging,
// so just skip it. val sendToDeviceParams = SendToDeviceTask.Params(EventType.ENCRYPTED, sendToDeviceMap)
return sendToDeviceTask
.execute(sendToDeviceParams)
.fold(
{
Timber.e(it, "## shareKeysWithDevice() : sendToDevice $userId:$deviceId failed")
}
, {
Timber.v("## shareKeysWithDevice() : sent to $userId:$deviceId")
}
)
} }


Timber.v("## shareKeysWithDevice() : sharing keys for session " + body!!.senderKey + "|" + body.sessionId override fun onFailure(failure: Throwable) {
+ " with device " + userId + ":" + deviceId) Timber.e(failure, "## shareKeysWithDevice() : ensureOlmSessionsForDevices $userId:$deviceId failed")

}
val inboundGroupSession = mOlmDevice.getInboundGroupSession(body.sessionId, body.senderKey, body.roomId) })

} else {
val payloadJson = HashMap<String, Any>() Timber.e("## shareKeysWithDevice() : ensureOlmSessionsForDevices $userId:$deviceId not found")
payloadJson["type"] = EventType.FORWARDED_ROOM_KEY }
payloadJson["content"] = inboundGroupSession!!.exportKeys()!!

val encodedPayload = mMessageEncrypter.encryptMessage(payloadJson, Arrays.asList(deviceInfo))
val sendToDeviceMap = MXUsersDevicesMap<Any>()
sendToDeviceMap.setObject(encodedPayload, userId, deviceId)

Timber.v("## shareKeysWithDevice() : sending to $userId:$deviceId")
mSendToDeviceTask.configureWith(SendToDeviceTask.Params(EventType.ENCRYPTED, sendToDeviceMap))
.dispatchTo(object : MatrixCallback<Unit> {
override fun onSuccess(data: Unit) {
Timber.v("## shareKeysWithDevice() : sent to $userId:$deviceId")
}

override fun onFailure(failure: Throwable) {
Timber.e(failure, "## shareKeysWithDevice() : sendToDevice $userId:$deviceId failed")
}
})
.executeBy(mTaskExecutor)
}

override fun onFailure(failure: Throwable) {
Timber.e(failure, "## shareKeysWithDevice() : ensureOlmSessionsForDevices $userId:$deviceId failed")
}
})
} else {
Timber.e("## shareKeysWithDevice() : ensureOlmSessionsForDevices $userId:$deviceId not found")
} }
}


override fun onFailure(failure: Throwable) { override fun onFailure(failure: Throwable) {
Timber.e(failure, "## shareKeysWithDevice() : downloadKeys $userId failed") Timber.e(failure, "## shareKeysWithDevice() : downloadKeys $userId failed")
} }
}) })
}


} }
} }

View File

@ -25,6 +25,7 @@ import im.vector.matrix.android.internal.crypto.actions.MessageEncrypter
import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
import im.vector.matrix.android.internal.crypto.tasks.SendToDeviceTask import im.vector.matrix.android.internal.crypto.tasks.SendToDeviceTask
import im.vector.matrix.android.internal.task.TaskExecutor import im.vector.matrix.android.internal.task.TaskExecutor
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers


internal class MXMegolmDecryptionFactory(private val mCredentials: Credentials, internal class MXMegolmDecryptionFactory(private val mCredentials: Credentials,
private val mOlmDevice: MXOlmDevice, private val mOlmDevice: MXOlmDevice,
@ -34,9 +35,10 @@ internal class MXMegolmDecryptionFactory(private val mCredentials: Credentials,
private val mEnsureOlmSessionsForDevicesAction: EnsureOlmSessionsForDevicesAction, private val mEnsureOlmSessionsForDevicesAction: EnsureOlmSessionsForDevicesAction,
private val mCryptoStore: IMXCryptoStore, private val mCryptoStore: IMXCryptoStore,
private val mSendToDeviceTask: SendToDeviceTask, private val mSendToDeviceTask: SendToDeviceTask,
private val coroutineDispatchers: MatrixCoroutineDispatchers,
private val mTaskExecutor: TaskExecutor) { private val mTaskExecutor: TaskExecutor) {


fun instantiate(): MXMegolmDecryption { fun create(): MXMegolmDecryption {
return MXMegolmDecryption( return MXMegolmDecryption(
mCredentials, mCredentials,
mOlmDevice, mOlmDevice,
@ -46,6 +48,6 @@ internal class MXMegolmDecryptionFactory(private val mCredentials: Credentials,
mEnsureOlmSessionsForDevicesAction, mEnsureOlmSessionsForDevicesAction,
mCryptoStore, mCryptoStore,
mSendToDeviceTask, mSendToDeviceTask,
mTaskExecutor) coroutineDispatchers)
} }
} }

View File

@ -49,36 +49,36 @@ import java.util.*


internal class MXMegolmEncryption( internal class MXMegolmEncryption(
// The id of the room we will be sending to. // The id of the room we will be sending to.
private var mRoomId: String, private var roomId: String,


private val olmDevice: MXOlmDevice, private val olmDevice: MXOlmDevice,
private val mKeysBackup: KeysBackup, private val keysBackup: KeysBackup,
private val mCryptoStore: IMXCryptoStore, private val cryptoStore: IMXCryptoStore,
private val mDeviceListManager: DeviceListManager, private val deviceListManager: DeviceListManager,
private val mEnsureOlmSessionsForDevicesAction: EnsureOlmSessionsForDevicesAction, private val ensureOlmSessionsForDevicesAction: EnsureOlmSessionsForDevicesAction,
private val mCredentials: Credentials, private val credentials: Credentials,
private val mSendToDeviceTask: SendToDeviceTask, private val sendToDeviceTask: SendToDeviceTask,
private val mTaskExecutor: TaskExecutor, private val taskExecutor: TaskExecutor,
private val mMessageEncrypter: MessageEncrypter, private val messageEncrypter: MessageEncrypter,
private val mWarnOnUnknownDevicesRepository: WarnOnUnknownDeviceRepository private val warnOnUnknownDevicesRepository: WarnOnUnknownDeviceRepository
) : IMXEncrypting { ) : IMXEncrypting {




// OutboundSessionInfo. Null if we haven't yet started setting one up. Note // OutboundSessionInfo. Null if we haven't yet started setting one up. Note
// that even if this is non-null, it may not be ready for use (in which // that even if this is non-null, it may not be ready for use (in which
// case outboundSession.shareOperation will be non-null.) // case outboundSession.shareOperation will be non-null.)
private var mOutboundSession: MXOutboundSessionInfo? = null private var outboundSession: MXOutboundSessionInfo? = null


// true when there is an HTTP operation in progress // true when there is an HTTP operation in progress
private var mShareOperationIsProgress: Boolean = false private var shareOperationIsProgress: Boolean = false


private val mPendingEncryptions = ArrayList<MXQueuedEncryption>() private val _pendingEncryptions = ArrayList<MXQueuedEncryption>()


// Default rotation periods // Default rotation periods
// TODO: Make it configurable via parameters // TODO: Make it configurable via parameters
// Session rotation periods // Session rotation periods
private var mSessionRotationPeriodMsgs: Int = 100 private var sessionRotationPeriodMsgs: Int = 100
private var mSessionRotationPeriodMs: Int = 7 * 24 * 3600 * 1000 private var sessionRotationPeriodMs: Int = 7 * 24 * 3600 * 1000


/** /**
* @return a snapshot of the pending encryptions * @return a snapshot of the pending encryptions
@ -86,11 +86,9 @@ internal class MXMegolmEncryption(
private val pendingEncryptions: List<MXQueuedEncryption> private val pendingEncryptions: List<MXQueuedEncryption>
get() { get() {
val list = ArrayList<MXQueuedEncryption>() val list = ArrayList<MXQueuedEncryption>()

synchronized(_pendingEncryptions) {
synchronized(mPendingEncryptions) { list.addAll(_pendingEncryptions)
list.addAll(mPendingEncryptions)
} }

return list return list
} }


@ -102,12 +100,12 @@ internal class MXMegolmEncryption(
// It will be processed when everything is set up // It will be processed when everything is set up
val queuedEncryption = MXQueuedEncryption() val queuedEncryption = MXQueuedEncryption()


queuedEncryption.mEventContent = eventContent queuedEncryption.eventContent = eventContent
queuedEncryption.mEventType = eventType queuedEncryption.eventType = eventType
queuedEncryption.mApiCallback = callback queuedEncryption.apiCallback = callback


synchronized(mPendingEncryptions) { synchronized(_pendingEncryptions) {
mPendingEncryptions.add(queuedEncryption) _pendingEncryptions.add(queuedEncryption)
} }


val t0 = System.currentTimeMillis() val t0 = System.currentTimeMillis()
@ -124,11 +122,11 @@ internal class MXMegolmEncryption(
val queuedEncryptions = pendingEncryptions val queuedEncryptions = pendingEncryptions


for (queuedEncryption in queuedEncryptions) { for (queuedEncryption in queuedEncryptions) {
queuedEncryption.mApiCallback?.onFailure(failure) queuedEncryption.apiCallback?.onFailure(failure)
} }


synchronized(mPendingEncryptions) { synchronized(_pendingEncryptions) {
mPendingEncryptions.removeAll(queuedEncryptions) _pendingEncryptions.removeAll(queuedEncryptions)
} }
} }


@ -162,10 +160,10 @@ internal class MXMegolmEncryption(
val keysClaimedMap = HashMap<String, String>() val keysClaimedMap = HashMap<String, String>()
keysClaimedMap["ed25519"] = olmDevice.deviceEd25519Key!! keysClaimedMap["ed25519"] = olmDevice.deviceEd25519Key!!


olmDevice.addInboundGroupSession(sessionId!!, olmDevice.getSessionKey(sessionId)!!, mRoomId, olmDevice.deviceCurve25519Key!!, olmDevice.addInboundGroupSession(sessionId!!, olmDevice.getSessionKey(sessionId)!!, roomId, olmDevice.deviceCurve25519Key!!,
ArrayList(), keysClaimedMap, false) ArrayList(), keysClaimedMap, false)


mKeysBackup.maybeBackupKeys() keysBackup.maybeBackupKeys()


return MXOutboundSessionInfo(sessionId) return MXOutboundSessionInfo(sessionId)
} }
@ -177,18 +175,18 @@ internal class MXMegolmEncryption(
* @param callback the asynchronous callback. * @param callback the asynchronous callback.
*/ */
private fun ensureOutboundSession(devicesInRoom: MXUsersDevicesMap<MXDeviceInfo>, callback: MatrixCallback<MXOutboundSessionInfo>?) { private fun ensureOutboundSession(devicesInRoom: MXUsersDevicesMap<MXDeviceInfo>, callback: MatrixCallback<MXOutboundSessionInfo>?) {
var session = mOutboundSession var session = outboundSession


if (null == session if (null == session
// Need to make a brand new session? // Need to make a brand new session?
|| session.needsRotation(mSessionRotationPeriodMsgs, mSessionRotationPeriodMs) || session.needsRotation(sessionRotationPeriodMsgs, sessionRotationPeriodMs)
// Determine if we have shared with anyone we shouldn't have // Determine if we have shared with anyone we shouldn't have
|| session.sharedWithTooManyDevices(devicesInRoom)) { || session.sharedWithTooManyDevices(devicesInRoom)) {
session = prepareNewSessionInRoom() session = prepareNewSessionInRoom()
mOutboundSession = session outboundSession = session
} }


if (mShareOperationIsProgress) { if (shareOperationIsProgress) {
Timber.v("## ensureOutboundSessionInRoom() : already in progress") Timber.v("## ensureOutboundSessionInRoom() : already in progress")
// Key share already in progress // Key share already in progress
return return
@ -218,7 +216,7 @@ internal class MXMegolmEncryption(


shareKey(fSession, shareMap, object : MatrixCallback<Unit> { shareKey(fSession, shareMap, object : MatrixCallback<Unit> {
override fun onSuccess(data: Unit) { override fun onSuccess(data: Unit) {
mShareOperationIsProgress = false shareOperationIsProgress = false
callback?.onSuccess(fSession) callback?.onSuccess(fSession)
} }


@ -226,7 +224,7 @@ internal class MXMegolmEncryption(
Timber.e("## ensureOutboundSessionInRoom() : shareKey onFailure") Timber.e("## ensureOutboundSessionInRoom() : shareKey onFailure")


callback?.onFailure(failure) callback?.onFailure(failure)
mShareOperationIsProgress = false shareOperationIsProgress = false
} }
}) })


@ -303,7 +301,7 @@ internal class MXMegolmEncryption(


val submap = HashMap<String, Any>() val submap = HashMap<String, Any>()
submap["algorithm"] = MXCRYPTO_ALGORITHM_MEGOLM submap["algorithm"] = MXCRYPTO_ALGORITHM_MEGOLM
submap["room_id"] = mRoomId submap["room_id"] = roomId
submap["session_id"] = session.mSessionId submap["session_id"] = session.mSessionId
submap["session_key"] = sessionKey!! submap["session_key"] = sessionKey!!
submap["chain_index"] = chainIndex submap["chain_index"] = chainIndex
@ -315,10 +313,10 @@ internal class MXMegolmEncryption(
val t0 = System.currentTimeMillis() val t0 = System.currentTimeMillis()
Timber.v("## shareUserDevicesKey() : starts") Timber.v("## shareUserDevicesKey() : starts")


mEnsureOlmSessionsForDevicesAction.handle(devicesByUser, object : MatrixCallback<MXUsersDevicesMap<MXOlmSessionResult>> { ensureOlmSessionsForDevicesAction.handle(devicesByUser, object : MatrixCallback<MXUsersDevicesMap<MXOlmSessionResult>> {
override fun onSuccess(data: MXUsersDevicesMap<MXOlmSessionResult>) { override fun onSuccess(data: MXUsersDevicesMap<MXOlmSessionResult>) {
Timber.v("## shareUserDevicesKey() : ensureOlmSessionsForDevices succeeds after " Timber.v("## shareUserDevicesKey() : ensureOlmSessionsForDevices succeeds after "
+ (System.currentTimeMillis() - t0) + " ms") + (System.currentTimeMillis() - t0) + " ms")
val contentMap = MXUsersDevicesMap<Any>() val contentMap = MXUsersDevicesMap<Any>()


var haveTargets = false var haveTargets = false
@ -348,7 +346,7 @@ internal class MXMegolmEncryption(


Timber.v("## shareUserDevicesKey() : Sharing keys with device $userId:$deviceID") Timber.v("## shareUserDevicesKey() : Sharing keys with device $userId:$deviceID")
//noinspection ArraysAsListWithZeroOrOneArgument,ArraysAsListWithZeroOrOneArgument //noinspection ArraysAsListWithZeroOrOneArgument,ArraysAsListWithZeroOrOneArgument
contentMap.setObject(mMessageEncrypter.encryptMessage(payload, Arrays.asList(sessionResult.mDevice)), userId, deviceID) contentMap.setObject(messageEncrypter.encryptMessage(payload, Arrays.asList(sessionResult.mDevice)), userId, deviceID)
haveTargets = true haveTargets = true
} }
} }
@ -357,11 +355,11 @@ internal class MXMegolmEncryption(
val t0 = System.currentTimeMillis() val t0 = System.currentTimeMillis()
Timber.v("## shareUserDevicesKey() : has target") Timber.v("## shareUserDevicesKey() : has target")


mSendToDeviceTask.configureWith(SendToDeviceTask.Params(EventType.ENCRYPTED, contentMap)) sendToDeviceTask.configureWith(SendToDeviceTask.Params(EventType.ENCRYPTED, contentMap))
.dispatchTo(object : MatrixCallback<Unit> { .dispatchTo(object : MatrixCallback<Unit> {
override fun onSuccess(data: Unit) { override fun onSuccess(data: Unit) {
Timber.v("## shareUserDevicesKey() : sendToDevice succeeds after " Timber.v("## shareUserDevicesKey() : sendToDevice succeeds after "
+ (System.currentTimeMillis() - t0) + " ms") + (System.currentTimeMillis() - t0) + " ms")


// Add the devices we have shared with to session.sharedWithDevices. // Add the devices we have shared with to session.sharedWithDevices.
// we deliberately iterate over devicesByUser (ie, the devices we // we deliberately iterate over devicesByUser (ie, the devices we
@ -387,7 +385,7 @@ internal class MXMegolmEncryption(
callback?.onFailure(failure) callback?.onFailure(failure)
} }
}) })
.executeBy(mTaskExecutor) .executeBy(taskExecutor)
} else { } else {
Timber.v("## shareUserDevicesKey() : no need to sharekey") Timber.v("## shareUserDevicesKey() : no need to sharekey")


@ -416,9 +414,9 @@ internal class MXMegolmEncryption(
for (queuedEncryption in queuedEncryptions) { for (queuedEncryption in queuedEncryptions) {
val payloadJson = HashMap<String, Any>() val payloadJson = HashMap<String, Any>()


payloadJson["room_id"] = mRoomId payloadJson["room_id"] = roomId
payloadJson["type"] = queuedEncryption.mEventType!! payloadJson["type"] = queuedEncryption.eventType!!
payloadJson["content"] = queuedEncryption.mEventContent!! payloadJson["content"] = queuedEncryption.eventContent!!


// Get canonical Json from // Get canonical Json from


@ -433,15 +431,15 @@ internal class MXMegolmEncryption(


// Include our device ID so that recipients can send us a // Include our device ID so that recipients can send us a
// m.new_device message if they don't have our session key. // m.new_device message if they don't have our session key.
map["device_id"] = mCredentials.deviceId!! map["device_id"] = credentials.deviceId!!


CryptoAsyncHelper.getUiHandler().post { queuedEncryption.mApiCallback?.onSuccess(map) } CryptoAsyncHelper.getUiHandler().post { queuedEncryption.apiCallback?.onSuccess(map) }


session.mUseCount++ session.mUseCount++
} }


synchronized(mPendingEncryptions) { synchronized(_pendingEncryptions) {
mPendingEncryptions.removeAll(queuedEncryptions) _pendingEncryptions.removeAll(queuedEncryptions)
} }
} }
} }
@ -458,10 +456,10 @@ internal class MXMegolmEncryption(
// have a list of the user's devices, then we already share an e2e room // have a list of the user's devices, then we already share an e2e room
// with them, which means that they will have announced any new devices via // with them, which means that they will have announced any new devices via
// an m.new_device. // an m.new_device.
mDeviceListManager.downloadKeys(userIds, false, object : MatrixCallback<MXUsersDevicesMap<MXDeviceInfo>> { deviceListManager.downloadKeys(userIds, false, object : MatrixCallback<MXUsersDevicesMap<MXDeviceInfo>> {
override fun onSuccess(data: MXUsersDevicesMap<MXDeviceInfo>) { override fun onSuccess(data: MXUsersDevicesMap<MXDeviceInfo>) {
val encryptToVerifiedDevicesOnly = mCryptoStore.getGlobalBlacklistUnverifiedDevices() val encryptToVerifiedDevicesOnly = cryptoStore.getGlobalBlacklistUnverifiedDevices()
|| mCryptoStore.getRoomsListBlacklistUnverifiedDevices().contains(mRoomId) || cryptoStore.getRoomsListBlacklistUnverifiedDevices().contains(roomId)


val devicesInRoom = MXUsersDevicesMap<MXDeviceInfo>() val devicesInRoom = MXUsersDevicesMap<MXDeviceInfo>()
val unknownDevices = MXUsersDevicesMap<MXDeviceInfo>() val unknownDevices = MXUsersDevicesMap<MXDeviceInfo>()
@ -472,7 +470,7 @@ internal class MXMegolmEncryption(
for (deviceId in deviceIds!!) { for (deviceId in deviceIds!!) {
val deviceInfo = data.getObject(deviceId, userId) val deviceInfo = data.getObject(deviceId, userId)


if (mWarnOnUnknownDevicesRepository.warnOnUnknownDevices() && deviceInfo!!.isUnknown) { if (warnOnUnknownDevicesRepository.warnOnUnknownDevices() && deviceInfo!!.isUnknown) {
// The device is not yet known by the user // The device is not yet known by the user
unknownDevices.setObject(deviceInfo, userId, deviceId) unknownDevices.setObject(deviceInfo, userId, deviceId)
continue continue
@ -501,7 +499,7 @@ internal class MXMegolmEncryption(
// if so, warn the user so they can verify or ignore. // if so, warn the user so they can verify or ignore.
if (unknownDevices.map.isNotEmpty()) { if (unknownDevices.map.isNotEmpty()) {
callback.onFailure(Failure.CryptoError(MXCryptoError(MXCryptoError.UNKNOWN_DEVICES_CODE, 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)))
} else { } else {
callback.onSuccess(devicesInRoom) callback.onSuccess(devicesInRoom)
} }

View File

@ -40,10 +40,9 @@ internal class MXMegolmEncryptionFactory(
private val mMessageEncrypter: MessageEncrypter, private val mMessageEncrypter: MessageEncrypter,
private val mWarnOnUnknownDevicesRepository: WarnOnUnknownDeviceRepository) { private val mWarnOnUnknownDevicesRepository: WarnOnUnknownDeviceRepository) {


fun instantiate(roomId: String): MXMegolmEncryption { fun create(roomId: String): MXMegolmEncryption {
return MXMegolmEncryption( return MXMegolmEncryption(
roomId, roomId,

olmDevice, olmDevice,
mKeysBackup, mKeysBackup,
mCryptoStore, mCryptoStore,

View File

@ -18,7 +18,6 @@
package im.vector.matrix.android.internal.crypto.algorithms.olm package im.vector.matrix.android.internal.crypto.algorithms.olm


import android.text.TextUtils import android.text.TextUtils
import com.squareup.moshi.Json
import im.vector.matrix.android.api.auth.data.Credentials import im.vector.matrix.android.api.auth.data.Credentials
import im.vector.matrix.android.api.session.crypto.MXCryptoError import im.vector.matrix.android.api.session.crypto.MXCryptoError
import im.vector.matrix.android.api.session.events.model.Event import im.vector.matrix.android.api.session.events.model.Event
@ -144,9 +143,9 @@ internal class MXOlmDecryption(
} }


val result = MXEventDecryptionResult() val result = MXEventDecryptionResult()
result.mClearEvent = payload result.clearEvent = payload
result.mSenderCurve25519Key = olmEventContent.senderKey result.senderCurve25519Key = olmEventContent.senderKey
result.mClaimedEd25519Key = olmPayloadContent.keys!!.get("ed25519") result.claimedEd25519Key = olmPayloadContent.keys!!.get("ed25519")


return result return result
} }

View File

@ -22,7 +22,7 @@ import im.vector.matrix.android.internal.crypto.MXOlmDevice
internal class MXOlmDecryptionFactory(private val mOlmDevice: MXOlmDevice, internal class MXOlmDecryptionFactory(private val mOlmDevice: MXOlmDevice,
private val mCredentials: Credentials) { private val mCredentials: Credentials) {


fun instantiate(): MXOlmDecryption { fun create(): MXOlmDecryption {
return MXOlmDecryption( return MXOlmDecryption(
mOlmDevice, mOlmDevice,
mCredentials) mCredentials)

View File

@ -31,19 +31,21 @@ import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo
import im.vector.matrix.android.internal.crypto.model.MXOlmSessionResult 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.MXUsersDevicesMap
import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import java.util.* import java.util.*


internal class MXOlmEncryption( internal class MXOlmEncryption(
private var mRoomId: String, private var roomId: String,

private val olmDevice: MXOlmDevice,
private val mOlmDevice: MXOlmDevice, private val cryptoStore: IMXCryptoStore,
private val mCryptoStore: IMXCryptoStore, private val messageEncrypter: MessageEncrypter,
private val mMessageEncrypter: MessageEncrypter, private val deviceListManager: DeviceListManager,
private val mDeviceListManager: DeviceListManager, private val coroutineDispatchers: MatrixCoroutineDispatchers,
private val mEnsureOlmSessionsForUsersAction: EnsureOlmSessionsForUsersAction) private val ensureOlmSessionsForUsersAction: EnsureOlmSessionsForUsersAction)
: IMXEncrypting { : IMXEncrypting {



override fun encryptEventContent(eventContent: Content, override fun encryptEventContent(eventContent: Content,
eventType: String, eventType: String,
userIds: List<String>, userIds: List<String>,
@ -51,40 +53,42 @@ internal class MXOlmEncryption(
// pick the list of recipients based on the membership list. // pick the list of recipients based on the membership list.
// //
// TODO: there is a race condition here! What if a new user turns up // TODO: there is a race condition here! What if a new user turns up
ensureSession(userIds, object : MatrixCallback<Unit> { CoroutineScope(coroutineDispatchers.crypto).launch {
override fun onSuccess(data: Unit) { ensureSession(userIds, object : MatrixCallback<Unit> {
val deviceInfos = ArrayList<MXDeviceInfo>() override fun onSuccess(data: Unit) {
val deviceInfos = ArrayList<MXDeviceInfo>()


for (userId in userIds) { for (userId in userIds) {
val devices = mCryptoStore.getUserDevices(userId)?.values ?: emptyList() val devices = cryptoStore.getUserDevices(userId)?.values ?: emptyList()


for (device in devices) { for (device in devices) {
val key = device.identityKey() val key = device.identityKey()


if (TextUtils.equals(key, mOlmDevice.deviceCurve25519Key)) { if (TextUtils.equals(key, olmDevice.deviceCurve25519Key)) {
// Don't bother setting up session to ourself // Don't bother setting up session to ourself
continue continue
}

if (device.isBlocked) {
// Don't bother setting up sessions with blocked users
continue
}

deviceInfos.add(device)
} }

if (device.isBlocked) {
// Don't bother setting up sessions with blocked users
continue
}

deviceInfos.add(device)
} }

val messageMap = HashMap<String, Any>()
messageMap["room_id"] = roomId
messageMap["type"] = eventType
messageMap["content"] = eventContent

messageEncrypter.encryptMessage(messageMap, deviceInfos)

callback.onSuccess(messageMap.toContent()!!)
} }

})
val messageMap = HashMap<String, Any>() }
messageMap["room_id"] = mRoomId
messageMap["type"] = eventType
messageMap["content"] = eventContent

mMessageEncrypter.encryptMessage(messageMap, deviceInfos)

callback.onSuccess(messageMap.toContent()!!)
}
})
} }


/** /**
@ -94,10 +98,10 @@ internal class MXOlmEncryption(
* @param callback the asynchronous callback * @param callback the asynchronous callback
*/ */
private fun ensureSession(users: List<String>, callback: MatrixCallback<Unit>?) { private fun ensureSession(users: List<String>, callback: MatrixCallback<Unit>?) {
mDeviceListManager.downloadKeys(users, false, object : MatrixCallback<MXUsersDevicesMap<MXDeviceInfo>> { deviceListManager.downloadKeys(users, false, object : MatrixCallback<MXUsersDevicesMap<MXDeviceInfo>> {


override fun onSuccess(data: MXUsersDevicesMap<MXDeviceInfo>) { override fun onSuccess(data: MXUsersDevicesMap<MXDeviceInfo>) {
mEnsureOlmSessionsForUsersAction.handle(users, object : MatrixCallback<MXUsersDevicesMap<MXOlmSessionResult>> { ensureOlmSessionsForUsersAction.handle(users, object : MatrixCallback<MXUsersDevicesMap<MXOlmSessionResult>> {
override fun onSuccess(data: MXUsersDevicesMap<MXOlmSessionResult>) { override fun onSuccess(data: MXUsersDevicesMap<MXOlmSessionResult>) {
callback?.onSuccess(Unit) callback?.onSuccess(Unit)
} }

View File

@ -21,21 +21,23 @@ import im.vector.matrix.android.internal.crypto.MXOlmDevice
import im.vector.matrix.android.internal.crypto.actions.EnsureOlmSessionsForUsersAction import im.vector.matrix.android.internal.crypto.actions.EnsureOlmSessionsForUsersAction
import im.vector.matrix.android.internal.crypto.actions.MessageEncrypter import im.vector.matrix.android.internal.crypto.actions.MessageEncrypter
import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers


internal class MXOlmEncryptionFactory(private val mOlmDevice: MXOlmDevice, internal class MXOlmEncryptionFactory(private val mOlmDevice: MXOlmDevice,
private val mCryptoStore: IMXCryptoStore, private val mCryptoStore: IMXCryptoStore,
private val mMessageEncrypter: MessageEncrypter, private val mMessageEncrypter: MessageEncrypter,
private val mDeviceListManager: DeviceListManager, private val mDeviceListManager: DeviceListManager,
private val coroutineDispatchers: MatrixCoroutineDispatchers,
private val mEnsureOlmSessionsForUsersAction: EnsureOlmSessionsForUsersAction) { private val mEnsureOlmSessionsForUsersAction: EnsureOlmSessionsForUsersAction) {


fun instantiate(roomId: String): MXOlmEncryption { fun create(roomId: String): MXOlmEncryption {
return MXOlmEncryption( return MXOlmEncryption(
roomId, roomId,

mOlmDevice, mOlmDevice,
mCryptoStore, mCryptoStore,
mMessageEncrypter, mMessageEncrypter,
mDeviceListManager, mDeviceListManager,
coroutineDispatchers,
mEnsureOlmSessionsForUsersAction) mEnsureOlmSessionsForUsersAction)
} }
} }

View File

@ -64,34 +64,34 @@ import kotlin.collections.HashMap
* to the user's homeserver. * to the user's homeserver.
*/ */
internal class KeysBackup( internal class KeysBackup(
private val mCredentials: Credentials, private val credentials: Credentials,
private val mCryptoStore: IMXCryptoStore, private val cryptoStore: IMXCryptoStore,
private val mOlmDevice: MXOlmDevice, private val olmDevice: MXOlmDevice,
private val mObjectSigner: ObjectSigner, private val objectSigner: ObjectSigner,
// Actions // Actions
private val mMegolmSessionDataImporter: MegolmSessionDataImporter, private val megolmSessionDataImporter: MegolmSessionDataImporter,
// Tasks // Tasks
private val mCreateKeysBackupVersionTask: CreateKeysBackupVersionTask, private val createKeysBackupVersionTask: CreateKeysBackupVersionTask,
private val mDeleteBackupTask: DeleteBackupTask, private val deleteBackupTask: DeleteBackupTask,
private val mDeleteRoomSessionDataTask: DeleteRoomSessionDataTask, private val deleteRoomSessionDataTask: DeleteRoomSessionDataTask,
private val mDeleteRoomSessionsDataTask: DeleteRoomSessionsDataTask, private val deleteRoomSessionsDataTask: DeleteRoomSessionsDataTask,
private val mDeleteSessionDataTask: DeleteSessionsDataTask, private val deleteSessionDataTask: DeleteSessionsDataTask,
private val mGetKeysBackupLastVersionTask: GetKeysBackupLastVersionTask, private val getKeysBackupLastVersionTask: GetKeysBackupLastVersionTask,
private val mGetKeysBackupVersionTask: GetKeysBackupVersionTask, private val getKeysBackupVersionTask: GetKeysBackupVersionTask,
private val mGetRoomSessionDataTask: GetRoomSessionDataTask, private val getRoomSessionDataTask: GetRoomSessionDataTask,
private val mGetRoomSessionsDataTask: GetRoomSessionsDataTask, private val getRoomSessionsDataTask: GetRoomSessionsDataTask,
private val mGetSessionsDataTask: GetSessionsDataTask, private val getSessionsDataTask: GetSessionsDataTask,
private val mStoreRoomSessionDataTask: StoreRoomSessionDataTask, private val storeRoomSessionDataTask: StoreRoomSessionDataTask,
private val mStoreSessionsDataTask: StoreRoomSessionsDataTask, private val storeSessionsDataTask: StoreRoomSessionsDataTask,
private val mStoreSessionDataTask: StoreSessionsDataTask, private val storeSessionDataTask: StoreSessionsDataTask,
private val mUpdateKeysBackupVersionTask: UpdateKeysBackupVersionTask, private val updateKeysBackupVersionTask: UpdateKeysBackupVersionTask,
// Task executor // Task executor
private val mTaskExecutor: TaskExecutor private val taskExecutor: TaskExecutor
) : KeysBackupService { ) : KeysBackupService {


private val mUIHandler = Handler(Looper.getMainLooper()) private val uiHandler = Handler(Looper.getMainLooper())


private val mKeysBackupStateManager = KeysBackupStateManager(mUIHandler) private val keysBackupStateManager = KeysBackupStateManager(uiHandler)


// The backup version // The backup version
override var mKeysBackupVersion: KeysVersionResult? = null override var mKeysBackupVersion: KeysVersionResult? = null
@ -107,23 +107,23 @@ internal class KeysBackup(
private var mKeysBackupStateListener: KeysBackupService.KeysBackupStateListener? = null private var mKeysBackupStateListener: KeysBackupService.KeysBackupStateListener? = null


override val isEnabled: Boolean override val isEnabled: Boolean
get() = mKeysBackupStateManager.isEnabled get() = keysBackupStateManager.isEnabled


override val isStucked: Boolean override val isStucked: Boolean
get() = mKeysBackupStateManager.isStucked get() = keysBackupStateManager.isStucked


override val state: KeysBackupState override val state: KeysBackupState
get() = mKeysBackupStateManager.state get() = keysBackupStateManager.state


override val currentBackupVersion: String? override val currentBackupVersion: String?
get() = mKeysBackupVersion?.version get() = mKeysBackupVersion?.version


override fun addListener(listener: KeysBackupService.KeysBackupStateListener) { override fun addListener(listener: KeysBackupService.KeysBackupStateListener) {
mKeysBackupStateManager.addListener(listener) keysBackupStateManager.addListener(listener)
} }


override fun removeListener(listener: KeysBackupService.KeysBackupStateListener) { override fun removeListener(listener: KeysBackupService.KeysBackupStateListener) {
mKeysBackupStateManager.removeListener(listener) keysBackupStateManager.removeListener(listener)
} }


/** /**
@ -153,7 +153,7 @@ internal class KeysBackup(
} else { } else {
object : ProgressListener { object : ProgressListener {
override fun onProgress(progress: Int, total: Int) { override fun onProgress(progress: Int, total: Int) {
mUIHandler.post { uiHandler.post {
try { try {
progressListener.onProgress(progress, total) progressListener.onProgress(progress, total)
} catch (e: Exception) { } catch (e: Exception) {
@ -176,7 +176,7 @@ internal class KeysBackup(


val canonicalJson = MoshiProvider.getCanonicalJson(Map::class.java, megolmBackupAuthData.signalableJSONDictionary()) val canonicalJson = MoshiProvider.getCanonicalJson(Map::class.java, megolmBackupAuthData.signalableJSONDictionary())


megolmBackupAuthData.signatures = mObjectSigner.signObject(canonicalJson) megolmBackupAuthData.signatures = objectSigner.signObject(canonicalJson)




val megolmBackupCreationInfo = MegolmBackupCreationInfo() val megolmBackupCreationInfo = MegolmBackupCreationInfo()
@ -184,11 +184,11 @@ internal class KeysBackup(
megolmBackupCreationInfo.authData = megolmBackupAuthData megolmBackupCreationInfo.authData = megolmBackupAuthData
megolmBackupCreationInfo.recoveryKey = computeRecoveryKey(olmPkDecryption.privateKey()) megolmBackupCreationInfo.recoveryKey = computeRecoveryKey(olmPkDecryption.privateKey())


mUIHandler.post { callback.onSuccess(megolmBackupCreationInfo) } uiHandler.post { callback.onSuccess(megolmBackupCreationInfo) }
} catch (e: OlmException) { } catch (e: OlmException) {
Timber.e(e, "OlmException") Timber.e(e, "OlmException")


mUIHandler.post { callback.onFailure(e) } uiHandler.post { callback.onFailure(e) }
} }
} }
} }
@ -206,14 +206,14 @@ internal class KeysBackup(
createKeysBackupVersionBody.authData = MoshiProvider.providesMoshi().adapter(Map::class.java) createKeysBackupVersionBody.authData = MoshiProvider.providesMoshi().adapter(Map::class.java)
.fromJson(keysBackupCreationInfo.authData?.toJsonString()) as Map<String, Any>? .fromJson(keysBackupCreationInfo.authData?.toJsonString()) as Map<String, Any>?


mKeysBackupStateManager.state = KeysBackupState.Enabling keysBackupStateManager.state = KeysBackupState.Enabling


mCreateKeysBackupVersionTask createKeysBackupVersionTask
.configureWith(createKeysBackupVersionBody) .configureWith(createKeysBackupVersionBody)
.dispatchTo(object : MatrixCallback<KeysVersion> { .dispatchTo(object : MatrixCallback<KeysVersion> {
override fun onSuccess(info: KeysVersion) { override fun onSuccess(info: KeysVersion) {
// Reset backup markers. // Reset backup markers.
mCryptoStore.resetBackupMarkers() cryptoStore.resetBackupMarkers()


val keyBackupVersion = KeysVersionResult() val keyBackupVersion = KeysVersionResult()
keyBackupVersion.algorithm = createKeysBackupVersionBody.algorithm keyBackupVersion.algorithm = createKeysBackupVersionBody.algorithm
@ -230,11 +230,11 @@ internal class KeysBackup(
} }


override fun onFailure(failure: Throwable) { override fun onFailure(failure: Throwable) {
mKeysBackupStateManager.state = KeysBackupState.Disabled keysBackupStateManager.state = KeysBackupState.Disabled
callback.onFailure(failure) callback.onFailure(failure)
} }
}) })
.executeBy(mTaskExecutor) .executeBy(taskExecutor)
} }


/** /**
@ -251,10 +251,10 @@ internal class KeysBackup(
if (mKeysBackupVersion != null && version == mKeysBackupVersion!!.version) { if (mKeysBackupVersion != null && version == mKeysBackupVersion!!.version) {
resetKeysBackupData() resetKeysBackupData()
mKeysBackupVersion = null mKeysBackupVersion = null
mKeysBackupStateManager.state = KeysBackupState.Unknown keysBackupStateManager.state = KeysBackupState.Unknown
} }


mDeleteBackupTask.configureWith(DeleteBackupTask.Params(version)) deleteBackupTask.configureWith(DeleteBackupTask.Params(version))
.dispatchTo(object : MatrixCallback<Unit> { .dispatchTo(object : MatrixCallback<Unit> {
private fun eventuallyRestartBackup() { private fun eventuallyRestartBackup() {
// Do not stay in KeysBackupState.Unknown but check what is available on the homeserver // Do not stay in KeysBackupState.Unknown but check what is available on the homeserver
@ -266,16 +266,16 @@ internal class KeysBackup(
override fun onSuccess(data: Unit) { override fun onSuccess(data: Unit) {
eventuallyRestartBackup() eventuallyRestartBackup()


mUIHandler.post { callback?.onSuccess(Unit) } uiHandler.post { callback?.onSuccess(Unit) }
} }


override fun onFailure(failure: Throwable) { override fun onFailure(failure: Throwable) {
eventuallyRestartBackup() eventuallyRestartBackup()


mUIHandler.post { callback?.onFailure(failure) } uiHandler.post { callback?.onFailure(failure) }
} }
}) })
.executeBy(mTaskExecutor) .executeBy(taskExecutor)
} }
} }


@ -287,7 +287,7 @@ internal class KeysBackup(
// Server contains more keys than locally // Server contains more keys than locally
val totalNumberOfKeysLocally = getTotalNumbersOfKeys() val totalNumberOfKeysLocally = getTotalNumbersOfKeys()


val keysBackupData = mCryptoStore.getKeysBackupData() val keysBackupData = cryptoStore.getKeysBackupData()


val totalNumberOfKeysServer = keysBackupData?.backupLastServerNumberOfKeys ?: -1 val totalNumberOfKeysServer = keysBackupData?.backupLastServerNumberOfKeys ?: -1
val hashServer = keysBackupData?.backupLastServerHash val hashServer = keysBackupData?.backupLastServerHash
@ -310,7 +310,7 @@ internal class KeysBackup(
* Facility method to get the total number of locally stored keys * Facility method to get the total number of locally stored keys
*/ */
override fun getTotalNumbersOfKeys(): Int { override fun getTotalNumbersOfKeys(): Int {
return mCryptoStore.inboundGroupSessionsCount(false) return cryptoStore.inboundGroupSessionsCount(false)


} }


@ -318,7 +318,7 @@ internal class KeysBackup(
* Facility method to get the number of backed up keys * Facility method to get the number of backed up keys
*/ */
override fun getTotalNumbersOfBackedUpKeys(): Int { override fun getTotalNumbersOfBackedUpKeys(): Int {
return mCryptoStore.inboundGroupSessionsCount(true) return cryptoStore.inboundGroupSessionsCount(true)
} }


/** /**
@ -370,7 +370,7 @@ internal class KeysBackup(
} }
} }


mKeysBackupStateManager.addListener(mKeysBackupStateListener!!) keysBackupStateManager.addListener(mKeysBackupStateListener!!)


backupKeys() backupKeys()
} }
@ -396,7 +396,7 @@ internal class KeysBackup(
.configureWith(keysBackupVersion) .configureWith(keysBackupVersion)
.dispatchTo(callback) .dispatchTo(callback)
.executeOn(TaskThread.COMPUTATION) .executeOn(TaskThread.COMPUTATION)
.executeBy(mTaskExecutor) .executeBy(taskExecutor)
} }


/** /**
@ -408,7 +408,7 @@ internal class KeysBackup(
*/ */
@WorkerThread @WorkerThread
private fun getKeysBackupTrustBg(keysBackupVersion: KeysVersionResult): KeysBackupVersionTrust { private fun getKeysBackupTrustBg(keysBackupVersion: KeysVersionResult): KeysBackupVersionTrust {
val myUserId = mCredentials.userId val myUserId = credentials.userId


val keysBackupVersionTrust = KeysBackupVersionTrust() val keysBackupVersionTrust = KeysBackupVersionTrust()
val authData = keysBackupVersion.getAuthDataAsMegolmBackupAuthData() val authData = keysBackupVersion.getAuthDataAsMegolmBackupAuthData()
@ -437,7 +437,7 @@ internal class KeysBackup(


var device: MXDeviceInfo? = null var device: MXDeviceInfo? = null
if (deviceId != null) { if (deviceId != null) {
device = mCryptoStore.getUserDevice(deviceId, myUserId) device = cryptoStore.getUserDevice(deviceId, myUserId)


var isSignatureValid = false var isSignatureValid = false


@ -445,7 +445,7 @@ internal class KeysBackup(
Timber.v("getKeysBackupTrust: Signature from unknown device $deviceId") Timber.v("getKeysBackupTrust: Signature from unknown device $deviceId")
} else { } else {
try { try {
mOlmDevice.verifySignature(device.fingerprint()!!, authData.signalableJSONDictionary(), mySigs[keyId] as String) olmDevice.verifySignature(device.fingerprint()!!, authData.signalableJSONDictionary(), mySigs[keyId] as String)
isSignatureValid = true isSignatureValid = true
} catch (e: OlmException) { } catch (e: OlmException) {
Timber.v("getKeysBackupTrust: Bad signature from device " + device.deviceId + " " + e.localizedMessage) Timber.v("getKeysBackupTrust: Bad signature from device " + device.deviceId + " " + e.localizedMessage)
@ -481,7 +481,7 @@ internal class KeysBackup(
Timber.v("trustKeyBackupVersion: $trust, version ${keysBackupVersion.version}") Timber.v("trustKeyBackupVersion: $trust, version ${keysBackupVersion.version}")


CryptoAsyncHelper.getDecryptBackgroundHandler().post { CryptoAsyncHelper.getDecryptBackgroundHandler().post {
val myUserId = mCredentials.userId val myUserId = credentials.userId


// Get auth data to update it // Get auth data to update it
val authData = getMegolmBackupAuthData(keysBackupVersion) val authData = getMegolmBackupAuthData(keysBackupVersion)
@ -489,7 +489,7 @@ internal class KeysBackup(
if (authData == null) { if (authData == null) {
Timber.w("trustKeyBackupVersion:trust: Key backup is missing required data") Timber.w("trustKeyBackupVersion:trust: Key backup is missing required data")


mUIHandler.post { uiHandler.post {
callback.onFailure(IllegalArgumentException("Missing element")) callback.onFailure(IllegalArgumentException("Missing element"))
} }


@ -503,14 +503,14 @@ internal class KeysBackup(
// Add current device signature // Add current device signature
val canonicalJson = MoshiProvider.getCanonicalJson(Map::class.java, authData.signalableJSONDictionary()) val canonicalJson = MoshiProvider.getCanonicalJson(Map::class.java, authData.signalableJSONDictionary())


val deviceSignatures = mObjectSigner.signObject(canonicalJson) val deviceSignatures = objectSigner.signObject(canonicalJson)


deviceSignatures[myUserId]?.forEach { entry -> deviceSignatures[myUserId]?.forEach { entry ->
myUserSignatures[entry.key] = entry.value myUserSignatures[entry.key] = entry.value
} }
} else { } else {
// Remove current device signature // Remove current device signature
myUserSignatures.remove("ed25519:${mCredentials.deviceId}") myUserSignatures.remove("ed25519:${credentials.deviceId}")
} }


// Create an updated version of KeysVersionResult // Create an updated version of KeysVersionResult
@ -532,7 +532,7 @@ internal class KeysBackup(
updateKeysBackupVersionBody.authData = adapter.fromJson(newMegolmBackupAuthData.toJsonString()) as Map<String, Any>? updateKeysBackupVersionBody.authData = adapter.fromJson(newMegolmBackupAuthData.toJsonString()) as Map<String, Any>?


// And send it to the homeserver // And send it to the homeserver
mUpdateKeysBackupVersionTask updateKeysBackupVersionTask
.configureWith(UpdateKeysBackupVersionTask.Params(keysBackupVersion.version!!, updateKeysBackupVersionBody)) .configureWith(UpdateKeysBackupVersionTask.Params(keysBackupVersion.version!!, updateKeysBackupVersionBody))
.dispatchTo(object : MatrixCallback<Unit> { .dispatchTo(object : MatrixCallback<Unit> {
override fun onSuccess(data: Unit) { override fun onSuccess(data: Unit) {
@ -547,18 +547,18 @@ internal class KeysBackup(


checkAndStartWithKeysBackupVersion(newKeysBackupVersion) checkAndStartWithKeysBackupVersion(newKeysBackupVersion)


mUIHandler.post { uiHandler.post {
callback.onSuccess(data) callback.onSuccess(data)
} }
} }


override fun onFailure(failure: Throwable) { override fun onFailure(failure: Throwable) {
mUIHandler.post { uiHandler.post {
callback.onFailure(failure) callback.onFailure(failure)
} }
} }
}) })
.executeBy(mTaskExecutor) .executeBy(taskExecutor)
} }
} }


@ -578,7 +578,7 @@ internal class KeysBackup(
if (!isValidRecoveryKeyForKeysBackupVersion(recoveryKey, keysBackupVersion)) { if (!isValidRecoveryKeyForKeysBackupVersion(recoveryKey, keysBackupVersion)) {
Timber.w("trustKeyBackupVersionWithRecoveryKey: Invalid recovery key.") Timber.w("trustKeyBackupVersionWithRecoveryKey: Invalid recovery key.")


mUIHandler.post { uiHandler.post {
callback.onFailure(IllegalArgumentException("Invalid recovery key or password")) callback.onFailure(IllegalArgumentException("Invalid recovery key or password"))
} }
return@post return@post
@ -606,7 +606,7 @@ internal class KeysBackup(
if (recoveryKey == null) { if (recoveryKey == null) {
Timber.w("trustKeysBackupVersionWithPassphrase: Key backup is missing required data") Timber.w("trustKeysBackupVersionWithPassphrase: Key backup is missing required data")


mUIHandler.post { uiHandler.post {
callback.onFailure(IllegalArgumentException("Missing element")) callback.onFailure(IllegalArgumentException("Missing element"))
} }


@ -652,7 +652,7 @@ internal class KeysBackup(
backupAllGroupSessionsCallback = null backupAllGroupSessionsCallback = null


mKeysBackupStateListener?.let { mKeysBackupStateListener?.let {
mKeysBackupStateManager.removeListener(it) keysBackupStateManager.removeListener(it)
} }


mKeysBackupStateListener = null mKeysBackupStateListener = null
@ -663,10 +663,10 @@ internal class KeysBackup(
*/ */
override fun getBackupProgress(progressListener: ProgressListener) { override fun getBackupProgress(progressListener: ProgressListener) {
CryptoAsyncHelper.getDecryptBackgroundHandler().post { CryptoAsyncHelper.getDecryptBackgroundHandler().post {
val backedUpKeys = mCryptoStore.inboundGroupSessionsCount(true) val backedUpKeys = cryptoStore.inboundGroupSessionsCount(true)
val total = mCryptoStore.inboundGroupSessionsCount(false) val total = cryptoStore.inboundGroupSessionsCount(false)


mUIHandler.post { progressListener.onProgress(backedUpKeys, total) } uiHandler.post { progressListener.onProgress(backedUpKeys, total) }
} }
} }


@ -692,7 +692,7 @@ internal class KeysBackup(
// Check if the recovery is valid before going any further // Check if the recovery is valid before going any further
if (!isValidRecoveryKeyForKeysBackupVersion(recoveryKey, keysVersionResult)) { if (!isValidRecoveryKeyForKeysBackupVersion(recoveryKey, keysVersionResult)) {
Timber.e("restoreKeysWithRecoveryKey: Invalid recovery key for this keys version") Timber.e("restoreKeysWithRecoveryKey: Invalid recovery key for this keys version")
mUIHandler.post { callback.onFailure(InvalidParameterException("Invalid recovery key")) } uiHandler.post { callback.onFailure(InvalidParameterException("Invalid recovery key")) }
return@Runnable return@Runnable
} }


@ -701,12 +701,12 @@ internal class KeysBackup(
if (decryption == null) { if (decryption == null) {
// This should not happen anymore // This should not happen anymore
Timber.e("restoreKeysWithRecoveryKey: Invalid recovery key. Error") Timber.e("restoreKeysWithRecoveryKey: Invalid recovery key. Error")
mUIHandler.post { callback.onFailure(InvalidParameterException("Invalid recovery key")) } uiHandler.post { callback.onFailure(InvalidParameterException("Invalid recovery key")) }
return@Runnable return@Runnable
} }


if (stepProgressListener != null) { if (stepProgressListener != null) {
mUIHandler.post { stepProgressListener.onStepProgress(StepProgressListener.Step.DownloadingKey) } uiHandler.post { stepProgressListener.onStepProgress(StepProgressListener.Step.DownloadingKey) }
} }


// Get backed up keys from the homeserver // Get backed up keys from the homeserver
@ -749,7 +749,7 @@ internal class KeysBackup(
null null
} }


mMegolmSessionDataImporter.handle(sessionsData, !backUp, progressListener, object : MatrixCallback<ImportRoomKeysResult> { megolmSessionDataImporter.handle(sessionsData, !backUp, progressListener, object : MatrixCallback<ImportRoomKeysResult> {
override fun onSuccess(data: ImportRoomKeysResult) { override fun onSuccess(data: ImportRoomKeysResult) {
// Do not back up the key if it comes from a backup recovery // Do not back up the key if it comes from a backup recovery
if (backUp) { if (backUp) {
@ -766,7 +766,7 @@ internal class KeysBackup(
} }


override fun onFailure(failure: Throwable) { override fun onFailure(failure: Throwable) {
mUIHandler.post { callback.onFailure(failure) } uiHandler.post { callback.onFailure(failure) }
} }
}) })
}) })
@ -794,7 +794,7 @@ internal class KeysBackup(
val progressListener = if (stepProgressListener != null) { val progressListener = if (stepProgressListener != null) {
object : ProgressListener { object : ProgressListener {
override fun onProgress(progress: Int, total: Int) { override fun onProgress(progress: Int, total: Int) {
mUIHandler.post { uiHandler.post {
stepProgressListener.onStepProgress(StepProgressListener.Step.ComputingKey(progress, total)) stepProgressListener.onStepProgress(StepProgressListener.Step.ComputingKey(progress, total))
} }
} }
@ -806,7 +806,7 @@ internal class KeysBackup(
val recoveryKey = recoveryKeyFromPassword(password, keysBackupVersion, progressListener) val recoveryKey = recoveryKeyFromPassword(password, keysBackupVersion, progressListener)


if (recoveryKey == null) { if (recoveryKey == null) {
mUIHandler.post { uiHandler.post {
Timber.v("backupKeys: Invalid configuration") Timber.v("backupKeys: Invalid configuration")
callback.onFailure(IllegalStateException("Invalid configuration")) callback.onFailure(IllegalStateException("Invalid configuration"))
} }
@ -828,7 +828,7 @@ internal class KeysBackup(
callback: MatrixCallback<KeysBackupData>) { callback: MatrixCallback<KeysBackupData>) {
if (roomId != null && sessionId != null) { if (roomId != null && sessionId != null) {
// Get key for the room and for the session // Get key for the room and for the session
mGetRoomSessionDataTask getRoomSessionDataTask
.configureWith(GetRoomSessionDataTask.Params(roomId, sessionId, version)) .configureWith(GetRoomSessionDataTask.Params(roomId, sessionId, version))
.dispatchTo(object : MatrixCallback<KeyBackupData> { .dispatchTo(object : MatrixCallback<KeyBackupData> {
override fun onSuccess(data: KeyBackupData) { override fun onSuccess(data: KeyBackupData) {
@ -847,10 +847,10 @@ internal class KeysBackup(
callback.onFailure(failure) callback.onFailure(failure)
} }
}) })
.executeBy(mTaskExecutor) .executeBy(taskExecutor)
} else if (roomId != null) { } else if (roomId != null) {
// Get all keys for the room // Get all keys for the room
mGetRoomSessionsDataTask getRoomSessionsDataTask
.configureWith(GetRoomSessionsDataTask.Params(roomId, version)) .configureWith(GetRoomSessionsDataTask.Params(roomId, version))
.dispatchTo(object : MatrixCallback<RoomKeysBackupData> { .dispatchTo(object : MatrixCallback<RoomKeysBackupData> {
override fun onSuccess(data: RoomKeysBackupData) { override fun onSuccess(data: RoomKeysBackupData) {
@ -866,13 +866,13 @@ internal class KeysBackup(
callback.onFailure(failure) callback.onFailure(failure)
} }
}) })
.executeBy(mTaskExecutor) .executeBy(taskExecutor)
} else { } else {
// Get all keys // Get all keys
mGetSessionsDataTask getSessionsDataTask
.configureWith(GetSessionsDataTask.Params(version)) .configureWith(GetSessionsDataTask.Params(version))
.dispatchTo(callback) .dispatchTo(callback)
.executeBy(mTaskExecutor) .executeBy(taskExecutor)
} }
} }


@ -908,14 +908,14 @@ internal class KeysBackup(
checkAndStartKeysBackup() checkAndStartKeysBackup()
} }
state == KeysBackupState.ReadyToBackUp -> { state == KeysBackupState.ReadyToBackUp -> {
mKeysBackupStateManager.state = KeysBackupState.WillBackUp keysBackupStateManager.state = KeysBackupState.WillBackUp


// Wait between 0 and 10 seconds, to avoid backup requests from // Wait between 0 and 10 seconds, to avoid backup requests from
// different clients hitting the server all at the same time when a // different clients hitting the server all at the same time when a
// new key is sent // new key is sent
val delayInMs = mRandom.nextInt(KEY_BACKUP_WAITING_TIME_TO_SEND_KEY_BACKUP_MILLIS).toLong() val delayInMs = mRandom.nextInt(KEY_BACKUP_WAITING_TIME_TO_SEND_KEY_BACKUP_MILLIS).toLong()


mUIHandler.postDelayed({ backupKeys() }, delayInMs) uiHandler.postDelayed({ backupKeys() }, delayInMs)
} }
else -> { else -> {
Timber.v("maybeBackupKeys: Skip it because state: $state") Timber.v("maybeBackupKeys: Skip it because state: $state")
@ -932,7 +932,7 @@ internal class KeysBackup(
*/ */
override fun getVersion(version: String, override fun getVersion(version: String,
callback: MatrixCallback<KeysVersionResult?>) { callback: MatrixCallback<KeysVersionResult?>) {
mGetKeysBackupVersionTask getKeysBackupVersionTask
.configureWith(version) .configureWith(version)
.dispatchTo(object : MatrixCallback<KeysVersionResult> { .dispatchTo(object : MatrixCallback<KeysVersionResult> {
override fun onSuccess(data: KeysVersionResult) { override fun onSuccess(data: KeysVersionResult) {
@ -950,7 +950,7 @@ internal class KeysBackup(
} }
} }
}) })
.executeBy(mTaskExecutor) .executeBy(taskExecutor)
} }


/** /**
@ -960,7 +960,7 @@ internal class KeysBackup(
* @param callback onSuccess(null) will be called if there is no backup on the server * @param callback onSuccess(null) will be called if there is no backup on the server
*/ */
override fun getCurrentVersion(callback: MatrixCallback<KeysVersionResult?>) { override fun getCurrentVersion(callback: MatrixCallback<KeysVersionResult?>) {
mGetKeysBackupLastVersionTask getKeysBackupLastVersionTask
.configureWith(Unit) .configureWith(Unit)
.dispatchTo(object : MatrixCallback<KeysVersionResult> { .dispatchTo(object : MatrixCallback<KeysVersionResult> {
override fun onSuccess(data: KeysVersionResult) { override fun onSuccess(data: KeysVersionResult) {
@ -978,7 +978,7 @@ internal class KeysBackup(
} }
} }
}) })
.executeBy(mTaskExecutor) .executeBy(taskExecutor)
} }


/** /**
@ -1002,7 +1002,7 @@ internal class KeysBackup(
callback.onSuccess(false) callback.onSuccess(false)
resetKeysBackupData() resetKeysBackupData()
mKeysBackupVersion = null mKeysBackupVersion = null
mKeysBackupStateManager.state = KeysBackupState.Disabled keysBackupStateManager.state = KeysBackupState.Disabled
} }
} else { } else {
if (localBackupVersion == null) { if (localBackupVersion == null) {
@ -1047,7 +1047,7 @@ internal class KeysBackup(
} }


mKeysBackupVersion = null mKeysBackupVersion = null
mKeysBackupStateManager.state = KeysBackupState.CheckingBackUpOnHomeserver keysBackupStateManager.state = KeysBackupState.CheckingBackUpOnHomeserver


getCurrentVersion(object : MatrixCallback<KeysVersionResult?> { getCurrentVersion(object : MatrixCallback<KeysVersionResult?> {
override fun onSuccess(data: KeysVersionResult?) { override fun onSuccess(data: KeysVersionResult?) {
@ -1056,7 +1056,7 @@ internal class KeysBackup(


override fun onFailure(failure: Throwable) { override fun onFailure(failure: Throwable) {
Timber.e(failure, "checkAndStartKeysBackup: Failed to get current version") Timber.e(failure, "checkAndStartKeysBackup: Failed to get current version")
mKeysBackupStateManager.state = KeysBackupState.Unknown keysBackupStateManager.state = KeysBackupState.Unknown
} }
}) })
} }
@ -1069,12 +1069,12 @@ internal class KeysBackup(
if (keyBackupVersion == null) { if (keyBackupVersion == null) {
Timber.v("checkAndStartWithKeysBackupVersion: Found no key backup version on the homeserver") Timber.v("checkAndStartWithKeysBackupVersion: Found no key backup version on the homeserver")
resetKeysBackupData() resetKeysBackupData()
mKeysBackupStateManager.state = KeysBackupState.Disabled keysBackupStateManager.state = KeysBackupState.Disabled
} else { } else {
getKeysBackupTrust(keyBackupVersion, object : MatrixCallback<KeysBackupVersionTrust> { getKeysBackupTrust(keyBackupVersion, object : MatrixCallback<KeysBackupVersionTrust> {


override fun onSuccess(data: KeysBackupVersionTrust) { override fun onSuccess(data: KeysBackupVersionTrust) {
val versionInStore = mCryptoStore.getKeyBackupVersion() val versionInStore = cryptoStore.getKeyBackupVersion()


if (data.usable) { if (data.usable) {
Timber.v("checkAndStartWithKeysBackupVersion: Found usable key backup. version: " + keyBackupVersion.version) Timber.v("checkAndStartWithKeysBackupVersion: Found usable key backup. version: " + keyBackupVersion.version)
@ -1093,7 +1093,7 @@ internal class KeysBackup(
resetKeysBackupData() resetKeysBackupData()
} }


mKeysBackupStateManager.state = KeysBackupState.NotTrusted keysBackupStateManager.state = KeysBackupState.NotTrusted
} }
} }


@ -1213,7 +1213,7 @@ internal class KeysBackup(


if (retrievedMegolmBackupAuthData != null) { if (retrievedMegolmBackupAuthData != null) {
mKeysBackupVersion = keysVersionResult mKeysBackupVersion = keysVersionResult
mCryptoStore.setKeyBackupVersion(keysVersionResult.version) cryptoStore.setKeyBackupVersion(keysVersionResult.version)


onServerDataRetrieved(keysVersionResult.count, keysVersionResult.hash) onServerDataRetrieved(keysVersionResult.count, keysVersionResult.hash)


@ -1223,20 +1223,20 @@ internal class KeysBackup(
} }
} catch (e: OlmException) { } catch (e: OlmException) {
Timber.e(e, "OlmException") Timber.e(e, "OlmException")
mKeysBackupStateManager.state = KeysBackupState.Disabled keysBackupStateManager.state = KeysBackupState.Disabled
return return
} }


mKeysBackupStateManager.state = KeysBackupState.ReadyToBackUp keysBackupStateManager.state = KeysBackupState.ReadyToBackUp


maybeBackupKeys() maybeBackupKeys()
} else { } else {
Timber.e("Invalid authentication data") Timber.e("Invalid authentication data")
mKeysBackupStateManager.state = KeysBackupState.Disabled keysBackupStateManager.state = KeysBackupState.Disabled
} }
} else { } else {
Timber.e("Invalid authentication data") Timber.e("Invalid authentication data")
mKeysBackupStateManager.state = KeysBackupState.Disabled keysBackupStateManager.state = KeysBackupState.Disabled
} }
} }


@ -1244,7 +1244,7 @@ internal class KeysBackup(
* Update the DB with data fetch from the server * Update the DB with data fetch from the server
*/ */
private fun onServerDataRetrieved(count: Int?, hash: String?) { private fun onServerDataRetrieved(count: Int?, hash: String?) {
mCryptoStore.setKeysBackupData(KeysBackupDataEntity() cryptoStore.setKeysBackupData(KeysBackupDataEntity()
.apply { .apply {
backupLastServerNumberOfKeys = count backupLastServerNumberOfKeys = count
backupLastServerHash = hash backupLastServerHash = hash
@ -1260,12 +1260,12 @@ internal class KeysBackup(
private fun resetKeysBackupData() { private fun resetKeysBackupData() {
resetBackupAllGroupSessionsListeners() resetBackupAllGroupSessionsListeners()


mCryptoStore.setKeyBackupVersion(null) cryptoStore.setKeyBackupVersion(null)
mCryptoStore.setKeysBackupData(null) cryptoStore.setKeysBackupData(null)
mBackupKey = null mBackupKey = null


// Reset backup markers // Reset backup markers
mCryptoStore.resetBackupMarkers() cryptoStore.resetBackupMarkers()
} }


/** /**
@ -1291,20 +1291,20 @@ internal class KeysBackup(
} }


// Get a chunk of keys to backup // Get a chunk of keys to backup
val sessions = mCryptoStore.inboundGroupSessionsToBackup(KEY_BACKUP_SEND_KEYS_MAX_COUNT) val sessions = cryptoStore.inboundGroupSessionsToBackup(KEY_BACKUP_SEND_KEYS_MAX_COUNT)


Timber.v("backupKeys: 1 - " + sessions.size + " sessions to back up") Timber.v("backupKeys: 1 - " + sessions.size + " sessions to back up")


if (sessions.isEmpty()) { if (sessions.isEmpty()) {
// Backup is up to date // Backup is up to date
mKeysBackupStateManager.state = KeysBackupState.ReadyToBackUp keysBackupStateManager.state = KeysBackupState.ReadyToBackUp


backupAllGroupSessionsCallback?.onSuccess(Unit) backupAllGroupSessionsCallback?.onSuccess(Unit)
resetBackupAllGroupSessionsListeners() resetBackupAllGroupSessionsListeners()
return return
} }


mKeysBackupStateManager.state = KeysBackupState.BackingUp keysBackupStateManager.state = KeysBackupState.BackingUp


CryptoAsyncHelper.getEncryptBackgroundHandler().post { CryptoAsyncHelper.getEncryptBackgroundHandler().post {
Timber.v("backupKeys: 2 - Encrypting keys") Timber.v("backupKeys: 2 - Encrypting keys")
@ -1332,25 +1332,25 @@ internal class KeysBackup(
Timber.v("backupKeys: 4 - Sending request") Timber.v("backupKeys: 4 - Sending request")


// Make the request // Make the request
mStoreSessionDataTask storeSessionDataTask
.configureWith(StoreSessionsDataTask.Params(mKeysBackupVersion!!.version!!, keysBackupData)) .configureWith(StoreSessionsDataTask.Params(mKeysBackupVersion!!.version!!, keysBackupData))
.dispatchTo(object : MatrixCallback<BackupKeysResult> { .dispatchTo(object : MatrixCallback<BackupKeysResult> {
override fun onSuccess(data: BackupKeysResult) { override fun onSuccess(data: BackupKeysResult) {
mUIHandler.post { uiHandler.post {
Timber.v("backupKeys: 5a - Request complete") Timber.v("backupKeys: 5a - Request complete")


// Mark keys as backed up // Mark keys as backed up
mCryptoStore.markBackupDoneForInboundGroupSessions(sessions) cryptoStore.markBackupDoneForInboundGroupSessions(sessions)


if (sessions.size < KEY_BACKUP_SEND_KEYS_MAX_COUNT) { if (sessions.size < KEY_BACKUP_SEND_KEYS_MAX_COUNT) {
Timber.v("backupKeys: All keys have been backed up") Timber.v("backupKeys: All keys have been backed up")
onServerDataRetrieved(data.count, data.hash) onServerDataRetrieved(data.count, data.hash)


// Note: Changing state will trigger the call to backupAllGroupSessionsCallback.onSuccess() // Note: Changing state will trigger the call to backupAllGroupSessionsCallback.onSuccess()
mKeysBackupStateManager.state = KeysBackupState.ReadyToBackUp keysBackupStateManager.state = KeysBackupState.ReadyToBackUp
} else { } else {
Timber.v("backupKeys: Continue to back up keys") Timber.v("backupKeys: Continue to back up keys")
mKeysBackupStateManager.state = KeysBackupState.WillBackUp keysBackupStateManager.state = KeysBackupState.WillBackUp


backupKeys() backupKeys()
} }
@ -1359,14 +1359,14 @@ internal class KeysBackup(


override fun onFailure(failure: Throwable) { override fun onFailure(failure: Throwable) {
if (failure is Failure.ServerError) { if (failure is Failure.ServerError) {
mUIHandler.post { uiHandler.post {
Timber.e(failure, "backupKeys: backupKeys failed.") Timber.e(failure, "backupKeys: backupKeys failed.")


when (failure.error.code) { when (failure.error.code) {
MatrixError.NOT_FOUND, MatrixError.NOT_FOUND,
MatrixError.WRONG_ROOM_KEYS_VERSION -> { MatrixError.WRONG_ROOM_KEYS_VERSION -> {
// Backup has been deleted on the server, or we are not using the last backup version // Backup has been deleted on the server, or we are not using the last backup version
mKeysBackupStateManager.state = KeysBackupState.WrongBackUpVersion keysBackupStateManager.state = KeysBackupState.WrongBackUpVersion
backupAllGroupSessionsCallback?.onFailure(failure) backupAllGroupSessionsCallback?.onFailure(failure)
resetBackupAllGroupSessionsListeners() resetBackupAllGroupSessionsListeners()
resetKeysBackupData() resetKeysBackupData()
@ -1376,24 +1376,24 @@ internal class KeysBackup(
checkAndStartKeysBackup() checkAndStartKeysBackup()
} }
else -> // Come back to the ready state so that we will retry on the next received key else -> // Come back to the ready state so that we will retry on the next received key
mKeysBackupStateManager.state = KeysBackupState.ReadyToBackUp keysBackupStateManager.state = KeysBackupState.ReadyToBackUp
} }
} }
} else { } else {
mUIHandler.post { uiHandler.post {
backupAllGroupSessionsCallback?.onFailure(failure) backupAllGroupSessionsCallback?.onFailure(failure)
resetBackupAllGroupSessionsListeners() resetBackupAllGroupSessionsListeners()


Timber.e("backupKeys: backupKeys failed.") Timber.e("backupKeys: backupKeys failed.")


// Retry a bit later // Retry a bit later
mKeysBackupStateManager.state = KeysBackupState.ReadyToBackUp keysBackupStateManager.state = KeysBackupState.ReadyToBackUp
maybeBackupKeys() maybeBackupKeys()
} }
} }
} }
}) })
.executeBy(mTaskExecutor) .executeBy(taskExecutor)
} }
} }


@ -1401,7 +1401,7 @@ internal class KeysBackup(
@WorkerThread @WorkerThread
fun encryptGroupSession(session: MXOlmInboundGroupSession2): KeyBackupData { fun encryptGroupSession(session: MXOlmInboundGroupSession2): KeyBackupData {
// Gather information for each key // Gather information for each key
val device = mCryptoStore.deviceWithIdentityKey(session.mSenderKey!!) val device = cryptoStore.deviceWithIdentityKey(session.mSenderKey!!)


// Build the m.megolm_backup.v1.curve25519-aes-sha2 data as defined at // Build the m.megolm_backup.v1.curve25519-aes-sha2 data as defined at
// https://github.com/uhoreg/matrix-doc/blob/e2e_backup/proposals/1219-storing-megolm-keys-serverside.md#mmegolm_backupv1curve25519-aes-sha2-key-format // https://github.com/uhoreg/matrix-doc/blob/e2e_backup/proposals/1219-storing-megolm-keys-serverside.md#mmegolm_backupv1curve25519-aes-sha2-key-format
@ -1496,5 +1496,5 @@ internal class KeysBackup(
* DEBUG INFO * DEBUG INFO
* ========================================================================================== */ * ========================================================================================== */


override fun toString() = "KeysBackup for ${mCredentials.userId}" override fun toString() = "KeysBackup for ${credentials.userId}"
} }

View File

@ -24,11 +24,11 @@ class MXQueuedEncryption {
/** /**
* The data to encrypt. * The data to encrypt.
*/ */
var mEventContent: Content? = null var eventContent: Content? = null
var mEventType: String? = null var eventType: String? = null


/** /**
* the asynchronous callback * the asynchronous callback
*/ */
var mApiCallback: MatrixCallback<Content>? = null var apiCallback: MatrixCallback<Content>? = null
} }

View File

@ -666,18 +666,18 @@ internal class RealmCryptoStore(private val enableFileEncryption: Boolean = fals
doRealmTransaction(realmConfiguration) { doRealmTransaction(realmConfiguration) {
// Delete any previous store request with the same parameters // Delete any previous store request with the same parameters
it.where<IncomingRoomKeyRequestEntity>() it.where<IncomingRoomKeyRequestEntity>()
.equalTo(IncomingRoomKeyRequestEntityFields.USER_ID, incomingRoomKeyRequest.mUserId) .equalTo(IncomingRoomKeyRequestEntityFields.USER_ID, incomingRoomKeyRequest.userId)
.equalTo(IncomingRoomKeyRequestEntityFields.DEVICE_ID, incomingRoomKeyRequest.mDeviceId) .equalTo(IncomingRoomKeyRequestEntityFields.DEVICE_ID, incomingRoomKeyRequest.deviceId)
.equalTo(IncomingRoomKeyRequestEntityFields.REQUEST_ID, incomingRoomKeyRequest.mRequestId) .equalTo(IncomingRoomKeyRequestEntityFields.REQUEST_ID, incomingRoomKeyRequest.requestId)
.findAll() .findAll()
.deleteAllFromRealm() .deleteAllFromRealm()


// Then store it // Then store it
it.createObject(IncomingRoomKeyRequestEntity::class.java).apply { it.createObject(IncomingRoomKeyRequestEntity::class.java).apply {
userId = incomingRoomKeyRequest.mUserId userId = incomingRoomKeyRequest.userId
deviceId = incomingRoomKeyRequest.mDeviceId deviceId = incomingRoomKeyRequest.deviceId
requestId = incomingRoomKeyRequest.mRequestId requestId = incomingRoomKeyRequest.requestId
putRequestBody(incomingRoomKeyRequest.mRequestBody) putRequestBody(incomingRoomKeyRequest.requestBody)
} }
} }
} }
@ -685,9 +685,9 @@ internal class RealmCryptoStore(private val enableFileEncryption: Boolean = fals
override fun deleteIncomingRoomKeyRequest(incomingRoomKeyRequest: IncomingRoomKeyRequest) { override fun deleteIncomingRoomKeyRequest(incomingRoomKeyRequest: IncomingRoomKeyRequest) {
doRealmTransaction(realmConfiguration) { doRealmTransaction(realmConfiguration) {
it.where<IncomingRoomKeyRequestEntity>() it.where<IncomingRoomKeyRequestEntity>()
.equalTo(IncomingRoomKeyRequestEntityFields.USER_ID, incomingRoomKeyRequest.mUserId) .equalTo(IncomingRoomKeyRequestEntityFields.USER_ID, incomingRoomKeyRequest.userId)
.equalTo(IncomingRoomKeyRequestEntityFields.DEVICE_ID, incomingRoomKeyRequest.mDeviceId) .equalTo(IncomingRoomKeyRequestEntityFields.DEVICE_ID, incomingRoomKeyRequest.deviceId)
.equalTo(IncomingRoomKeyRequestEntityFields.REQUEST_ID, incomingRoomKeyRequest.mRequestId) .equalTo(IncomingRoomKeyRequestEntityFields.REQUEST_ID, incomingRoomKeyRequest.requestId)
.findAll() .findAll()
.deleteAllFromRealm() .deleteAllFromRealm()
} }

View File

@ -33,10 +33,10 @@ internal open class IncomingRoomKeyRequestEntity(


fun toIncomingRoomKeyRequest(): IncomingRoomKeyRequest { fun toIncomingRoomKeyRequest(): IncomingRoomKeyRequest {
return IncomingRoomKeyRequest().apply { return IncomingRoomKeyRequest().apply {
mRequestId = requestId requestId = requestId
mUserId = userId userId = userId
mDeviceId = deviceId deviceId = deviceId
mRequestBody = RoomKeyRequestBody().apply { requestBody = RoomKeyRequestBody().apply {
algorithm = requestBodyAlgorithm algorithm = requestBodyAlgorithm
roomId = requestBodyRoomId roomId = requestBodyRoomId
senderKey = requestBodySenderKey senderKey = requestBodySenderKey

View File

@ -38,8 +38,7 @@ class MatrixModule(private val context: Context) {
MatrixCoroutineDispatchers(io = Dispatchers.IO, MatrixCoroutineDispatchers(io = Dispatchers.IO,
computation = Dispatchers.IO, computation = Dispatchers.IO,
main = Dispatchers.Main, main = Dispatchers.Main,
encryption = Executors.newSingleThreadExecutor().asCoroutineDispatcher(), crypto = Executors.newSingleThreadExecutor().asCoroutineDispatcher()
decryption = Executors.newSingleThreadExecutor().asCoroutineDispatcher()
) )
} }



View File

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





View File

@ -22,6 +22,5 @@ internal data class MatrixCoroutineDispatchers(
val io: CoroutineDispatcher, val io: CoroutineDispatcher,
val computation: CoroutineDispatcher, val computation: CoroutineDispatcher,
val main: CoroutineDispatcher, val main: CoroutineDispatcher,
val decryption: CoroutineDispatcher, val crypto: CoroutineDispatcher
val encryption: CoroutineDispatcher
) )

View File

@ -70,11 +70,11 @@ class EncryptedItemFactory(
if (decrypted == null) { if (decrypted == null) {
return null return null
} }
if (decrypted.mClearEvent == null) { if (decrypted.clearEvent == null) {
return null return null
} }
val adapter = MoshiProvider.providesMoshi().adapter(Event::class.java) val adapter = MoshiProvider.providesMoshi().adapter(Event::class.java)
val clearEvent = adapter.fromJsonValue(decrypted.mClearEvent) ?: return null val clearEvent = adapter.fromJsonValue(decrypted.clearEvent) ?: return null
val decryptedTimelineEvent = timelineEvent.copy(root = clearEvent) val decryptedTimelineEvent = timelineEvent.copy(root = clearEvent)


// Success // Success