mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2025-02-16 20:10:04 +03:00
Crypto: continue threading rework. WIP to shash
This commit is contained in:
parent
e125862794
commit
6b0ab10231
27 changed files with 472 additions and 800 deletions
|
@ -41,7 +41,7 @@ class MXCryptoError(var code: String,
|
|||
* @return true if the current error is an olm one.
|
||||
*/
|
||||
val isOlmError: Boolean
|
||||
get() = TextUtils.equals(OLM_ERROR_CODE, code)
|
||||
get() = OLM_ERROR_CODE == code
|
||||
|
||||
|
||||
/**
|
||||
|
@ -98,6 +98,7 @@ class MXCryptoError(var code: String,
|
|||
const val MISSING_PROPERTY_ERROR_CODE = "MISSING_PROPERTY"
|
||||
const val OLM_ERROR_CODE = "OLM_ERROR_CODE"
|
||||
const val UNKNOWN_DEVICES_CODE = "UNKNOWN_DEVICES_CODE"
|
||||
const val UNKNOWN_MESSAGE_INDEX = "UNKNOWN_MESSAGE_INDEX"
|
||||
|
||||
/**
|
||||
* short error reasons
|
||||
|
|
|
@ -19,10 +19,8 @@
|
|||
package im.vector.matrix.android.internal.crypto
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Handler
|
||||
import android.text.TextUtils
|
||||
import arrow.core.Try
|
||||
import arrow.instances.`try`.applicativeError.handleError
|
||||
import com.zhuinden.monarchy.Monarchy
|
||||
import im.vector.matrix.android.api.MatrixCallback
|
||||
import im.vector.matrix.android.api.auth.data.Credentials
|
||||
|
@ -70,10 +68,7 @@ import im.vector.matrix.android.internal.session.sync.model.SyncResponse
|
|||
import im.vector.matrix.android.internal.task.TaskExecutor
|
||||
import im.vector.matrix.android.internal.task.configureWith
|
||||
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.coroutines.*
|
||||
import org.matrix.olm.OlmManager
|
||||
import timber.log.Timber
|
||||
import java.util.*
|
||||
|
@ -245,56 +240,41 @@ internal class CryptoManager(
|
|||
* @param isInitialSync true if it starts from an initial sync
|
||||
*/
|
||||
fun start(isInitialSync: Boolean) {
|
||||
if (isStarting.get()) {
|
||||
CoroutineScope(coroutineDispatchers.crypto).launch {
|
||||
internalStart(isInitialSync)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun internalStart(isInitialSync: Boolean) {
|
||||
if (isStarted.get() || isStarting.get()) {
|
||||
return
|
||||
}
|
||||
|
||||
// do not start if there is not network connection
|
||||
// TODO
|
||||
//if (null != mNetworkConnectivityReceiver && !mNetworkConnectivityReceiver!!.isConnected()) {
|
||||
// // wait that a valid network connection is retrieved
|
||||
// mNetworkConnectivityReceiver!!.removeEventListener(mNetworkListener)
|
||||
// mNetworkConnectivityReceiver!!.addEventListener(mNetworkListener)
|
||||
// return
|
||||
//}
|
||||
|
||||
isStarting.set(true)
|
||||
// Open the store
|
||||
cryptoStore.open()
|
||||
CoroutineScope(coroutineDispatchers.crypto).launch {
|
||||
uploadDeviceKeys()
|
||||
.flatMap {
|
||||
oneTimeKeysUploader.maybeUploadOneTimeKeys()
|
||||
}
|
||||
.handleError {
|
||||
Handler().postDelayed(
|
||||
{
|
||||
if (!isStarted()) {
|
||||
isStarting.set(false)
|
||||
start(isInitialSync)
|
||||
}
|
||||
}, 1000
|
||||
)
|
||||
}
|
||||
.fold(
|
||||
{
|
||||
Timber.e("Start failed: $it")
|
||||
},
|
||||
{
|
||||
isStarting.set(false)
|
||||
isStarted.set(true)
|
||||
outgoingRoomKeyRequestManager.start()
|
||||
keysBackup.checkAndStartKeysBackup()
|
||||
if (isInitialSync) {
|
||||
// refresh the devices list for each known room members
|
||||
deviceListManager.invalidateAllDeviceLists()
|
||||
deviceListManager.refreshOutdatedDeviceLists()
|
||||
} else {
|
||||
incomingRoomKeyRequestManager.processReceivedRoomKeyRequests()
|
||||
}
|
||||
uploadDeviceKeys()
|
||||
.flatMap { oneTimeKeysUploader.maybeUploadOneTimeKeys() }
|
||||
.fold(
|
||||
{
|
||||
Timber.e("Start failed: $it")
|
||||
delay(1000)
|
||||
isStarting.set(false)
|
||||
internalStart(isInitialSync)
|
||||
},
|
||||
{
|
||||
isStarting.set(false)
|
||||
isStarted.set(true)
|
||||
outgoingRoomKeyRequestManager.start()
|
||||
keysBackup.checkAndStartKeysBackup()
|
||||
if (isInitialSync) {
|
||||
// refresh the devices list for each known room members
|
||||
deviceListManager.invalidateAllDeviceLists()
|
||||
deviceListManager.refreshOutdatedDeviceLists()
|
||||
} else {
|
||||
incomingRoomKeyRequestManager.processReceivedRoomKeyRequests()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -447,7 +427,10 @@ internal class CryptoManager(
|
|||
* @param membersId list of members to start tracking their devices
|
||||
* @return true if the operation succeeds.
|
||||
*/
|
||||
private suspend fun setEncryptionInRoom(roomId: String, algorithm: String?, inhibitDeviceQuery: Boolean, membersId: List<String>): Boolean {
|
||||
private suspend fun setEncryptionInRoom(roomId: String,
|
||||
algorithm: String?,
|
||||
inhibitDeviceQuery: Boolean,
|
||||
membersId: List<String>): Boolean {
|
||||
// If we already have encryption in this room, we should ignore this event
|
||||
// (for now at least. Maybe we should alert the user somehow?)
|
||||
val existingAlgorithm = cryptoStore.getRoomAlgorithm(roomId)
|
||||
|
@ -555,13 +538,11 @@ internal class CryptoManager(
|
|||
eventType: String,
|
||||
roomId: String,
|
||||
callback: MatrixCallback<MXEncryptEventContentResult>) {
|
||||
// wait that the crypto is really started
|
||||
if (!isStarted()) {
|
||||
Timber.v("## encryptEventContent() : wait after e2e init")
|
||||
start(false)
|
||||
return
|
||||
}
|
||||
CoroutineScope(coroutineDispatchers.crypto).launch {
|
||||
if (!isStarted()) {
|
||||
Timber.v("## encryptEventContent() : wait after e2e init")
|
||||
internalStart(false)
|
||||
}
|
||||
val userIds = getRoomUserIds(roomId)
|
||||
var alg = synchronized(roomEncryptors) {
|
||||
roomEncryptors[roomId]
|
||||
|
@ -580,24 +561,21 @@ internal class CryptoManager(
|
|||
if (safeAlgorithm != null) {
|
||||
val t0 = System.currentTimeMillis()
|
||||
Timber.v("## encryptEventContent() starts")
|
||||
safeAlgorithm.encryptEventContent(eventContent, eventType, userIds, object : MatrixCallback<Content> {
|
||||
override fun onSuccess(data: Content) {
|
||||
Timber.v("## encryptEventContent() : succeeds after " + (System.currentTimeMillis() - t0) + " ms")
|
||||
callback.onSuccess(MXEncryptEventContentResult(data, EventType.ENCRYPTED))
|
||||
}
|
||||
|
||||
override fun onFailure(failure: Throwable) {
|
||||
callback.onFailure(failure)
|
||||
}
|
||||
})
|
||||
safeAlgorithm.encryptEventContent(eventContent, eventType, userIds)
|
||||
.fold(
|
||||
{ callback.onFailure(it) },
|
||||
{
|
||||
Timber.v("## encryptEventContent() : succeeds after " + (System.currentTimeMillis() - t0) + " ms")
|
||||
callback.onSuccess(MXEncryptEventContentResult(it, EventType.ENCRYPTED))
|
||||
}
|
||||
)
|
||||
} else {
|
||||
val algorithm = getEncryptionAlgorithm(roomId)
|
||||
val reason = String.format(MXCryptoError.UNABLE_TO_ENCRYPT_REASON,
|
||||
algorithm ?: MXCryptoError.NO_MORE_ALGORITHM_REASON)
|
||||
algorithm ?: MXCryptoError.NO_MORE_ALGORITHM_REASON)
|
||||
Timber.e("## encryptEventContent() : $reason")
|
||||
|
||||
callback.onFailure(Failure.CryptoError(MXCryptoError(MXCryptoError.UNABLE_TO_ENCRYPT_ERROR_CODE,
|
||||
MXCryptoError.UNABLE_TO_ENCRYPT, reason)))
|
||||
MXCryptoError.UNABLE_TO_ENCRYPT, reason)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -616,14 +594,14 @@ internal class CryptoManager(
|
|||
Timber.e("## decryptEvent : empty event content")
|
||||
return null
|
||||
}
|
||||
val alg = roomDecryptorProvider.getOrCreateRoomDecryptor(event.roomId, eventContent["algorithm"] as String)
|
||||
if (alg == null) {
|
||||
val reason = String.format(MXCryptoError.UNABLE_TO_DECRYPT_REASON, event.eventId, eventContent["algorithm"] as String)
|
||||
Timber.e("## decryptEvent() : $reason")
|
||||
throw MXDecryptionException(MXCryptoError(MXCryptoError.UNABLE_TO_DECRYPT_ERROR_CODE, MXCryptoError.UNABLE_TO_DECRYPT, reason))
|
||||
} else {
|
||||
return runBlocking {
|
||||
withContext(coroutineDispatchers.crypto) {
|
||||
return runBlocking {
|
||||
withContext(coroutineDispatchers.crypto) {
|
||||
val alg = roomDecryptorProvider.getOrCreateRoomDecryptor(event.roomId, eventContent["algorithm"] as String)
|
||||
if (alg == null) {
|
||||
val reason = String.format(MXCryptoError.UNABLE_TO_DECRYPT_REASON, event.eventId, eventContent["algorithm"] as String)
|
||||
Timber.e("## decryptEvent() : $reason")
|
||||
throw MXDecryptionException(MXCryptoError(MXCryptoError.UNABLE_TO_DECRYPT_ERROR_CODE, MXCryptoError.UNABLE_TO_DECRYPT, reason))
|
||||
} else {
|
||||
alg.decryptEvent(event, timeline)
|
||||
}
|
||||
}
|
||||
|
@ -661,19 +639,15 @@ internal class CryptoManager(
|
|||
*/
|
||||
private fun onRoomKeyEvent(event: Event) {
|
||||
val roomKeyContent = event.getClearContent().toModel<RoomKeyContent>()!!
|
||||
|
||||
if (TextUtils.isEmpty(roomKeyContent.roomId) || TextUtils.isEmpty(roomKeyContent.algorithm)) {
|
||||
Timber.e("## onRoomKeyEvent() : missing fields")
|
||||
return
|
||||
}
|
||||
|
||||
val alg = roomDecryptorProvider.getOrCreateRoomDecryptor(roomKeyContent.roomId, roomKeyContent.algorithm)
|
||||
|
||||
if (null == alg) {
|
||||
if (alg == null) {
|
||||
Timber.e("## onRoomKeyEvent() : Unable to handle keys for " + roomKeyContent.algorithm)
|
||||
return
|
||||
}
|
||||
|
||||
alg.onRoomKeyEvent(event, keysBackup)
|
||||
}
|
||||
|
||||
|
@ -699,7 +673,7 @@ internal class CryptoManager(
|
|||
monarchy.doWithRealm { realm ->
|
||||
// Check whether the event content must be encrypted for the invited members.
|
||||
val encryptForInvitedMembers = isEncryptionEnabledForInvitedUser()
|
||||
&& shouldEncryptForInvitedMembers(roomId)
|
||||
&& shouldEncryptForInvitedMembers(roomId)
|
||||
|
||||
userIds = if (encryptForInvitedMembers) {
|
||||
RoomMembers(realm, roomId).getActiveRoomMemberIds()
|
||||
|
@ -734,8 +708,8 @@ internal class CryptoManager(
|
|||
// make sure we are tracking the deviceList for this user.
|
||||
deviceListManager.startTrackingDeviceList(Arrays.asList(userId))
|
||||
} else if (membership == Membership.INVITE
|
||||
&& shouldEncryptForInvitedMembers(roomId)
|
||||
&& cryptoConfig.mEnableEncryptionForInvitedMembers) {
|
||||
&& shouldEncryptForInvitedMembers(roomId)
|
||||
&& cryptoConfig.mEnableEncryptionForInvitedMembers) {
|
||||
// track the deviceList for this invited user.
|
||||
// Caution: there's a big edge case here in that federated servers do not
|
||||
// know what other servers are in the room at the time they've been invited.
|
||||
|
@ -898,7 +872,7 @@ internal class CryptoManager(
|
|||
// trigger an an unknown devices exception
|
||||
callback.onFailure(
|
||||
Failure.CryptoError(MXCryptoError(MXCryptoError.UNKNOWN_DEVICES_CODE,
|
||||
MXCryptoError.UNABLE_TO_ENCRYPT, MXCryptoError.UNKNOWN_DEVICES_REASON, unknownDevices)))
|
||||
MXCryptoError.UNABLE_TO_ENCRYPT, MXCryptoError.UNKNOWN_DEVICES_REASON, unknownDevices)))
|
||||
}
|
||||
}
|
||||
)
|
||||
|
@ -936,7 +910,7 @@ internal class CryptoManager(
|
|||
* @param roomId the room id
|
||||
* @return true if the client should encrypt messages only for the verified devices.
|
||||
*/
|
||||
// TODO add this info in CryptoRoomEntity?
|
||||
// TODO add this info in CryptoRoomEntity?
|
||||
override fun isRoomBlacklistUnverifiedDevices(roomId: String?): Boolean {
|
||||
return if (null != roomId) {
|
||||
cryptoStore.getRoomsListBlacklistUnverifiedDevices().contains(roomId)
|
||||
|
@ -984,7 +958,7 @@ internal class CryptoManager(
|
|||
setRoomBlacklistUnverifiedDevices(roomId, false)
|
||||
}
|
||||
|
||||
// TODO Check if this method is still necessary
|
||||
// TODO Check if this method is still necessary
|
||||
/**
|
||||
* Cancel any earlier room key request
|
||||
*
|
||||
|
@ -1057,9 +1031,9 @@ internal class CryptoManager(
|
|||
return unknownDevices
|
||||
}
|
||||
|
||||
/* ==========================================================================================
|
||||
* DEBUG INFO
|
||||
* ========================================================================================== */
|
||||
/* ==========================================================================================
|
||||
* DEBUG INFO
|
||||
* ========================================================================================== */
|
||||
|
||||
override fun toString(): String {
|
||||
return "CryptoManager of " + credentials.userId + " (" + credentials.deviceId + ")"
|
||||
|
|
|
@ -169,7 +169,7 @@ internal class CryptoModule {
|
|||
}
|
||||
|
||||
scope(DefaultSession.SCOPE) {
|
||||
EnsureOlmSessionsForDevicesAction(get(), get(), get(), get())
|
||||
EnsureOlmSessionsForDevicesAction(get(), get())
|
||||
}
|
||||
|
||||
scope(DefaultSession.SCOPE) {
|
||||
|
@ -192,7 +192,7 @@ internal class CryptoModule {
|
|||
// Factories
|
||||
scope(DefaultSession.SCOPE) {
|
||||
MXMegolmDecryptionFactory(
|
||||
get(), get(), get(), get(), get(), get(), get(), get(), get(), get()
|
||||
get(), get(), get(), get(), get(), get(), get(), get(), get()
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -27,21 +27,21 @@ import timber.log.Timber
|
|||
import java.util.*
|
||||
|
||||
internal class IncomingRoomKeyRequestManager(
|
||||
private val mCredentials: Credentials,
|
||||
private val mCryptoStore: IMXCryptoStore,
|
||||
private val mRoomDecryptorProvider: RoomDecryptorProvider) {
|
||||
private val credentials: Credentials,
|
||||
private val cryptoStore: IMXCryptoStore,
|
||||
private val roomDecryptorProvider: RoomDecryptorProvider) {
|
||||
|
||||
|
||||
// list of IncomingRoomKeyRequests/IncomingRoomKeyRequestCancellations
|
||||
// we received in the current sync.
|
||||
private val mReceivedRoomKeyRequests = ArrayList<IncomingRoomKeyRequest>()
|
||||
private val mReceivedRoomKeyRequestCancellations = ArrayList<IncomingRoomKeyRequestCancellation>()
|
||||
private val receivedRoomKeyRequests = ArrayList<IncomingRoomKeyRequest>()
|
||||
private val receivedRoomKeyRequestCancellations = ArrayList<IncomingRoomKeyRequestCancellation>()
|
||||
|
||||
// the listeners
|
||||
private val mRoomKeysRequestListeners: MutableSet<RoomKeysRequestListener> = HashSet()
|
||||
private val roomKeysRequestListeners: MutableSet<RoomKeysRequestListener> = HashSet()
|
||||
|
||||
init {
|
||||
mReceivedRoomKeyRequests.addAll(mCryptoStore.getPendingIncomingRoomKeyRequests())
|
||||
receivedRoomKeyRequests.addAll(cryptoStore.getPendingIncomingRoomKeyRequests())
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -52,13 +52,12 @@ internal class IncomingRoomKeyRequestManager(
|
|||
*/
|
||||
fun onRoomKeyRequestEvent(event: Event) {
|
||||
val roomKeyShare = event.getClearContent().toModel<RoomKeyShare>()
|
||||
|
||||
when (roomKeyShare?.action) {
|
||||
RoomKeyShare.ACTION_SHARE_REQUEST -> synchronized(mReceivedRoomKeyRequests) {
|
||||
mReceivedRoomKeyRequests.add(IncomingRoomKeyRequest(event))
|
||||
RoomKeyShare.ACTION_SHARE_REQUEST -> synchronized(receivedRoomKeyRequests) {
|
||||
receivedRoomKeyRequests.add(IncomingRoomKeyRequest(event))
|
||||
}
|
||||
RoomKeyShare.ACTION_SHARE_CANCELLATION -> synchronized(mReceivedRoomKeyRequestCancellations) {
|
||||
mReceivedRoomKeyRequestCancellations.add(IncomingRoomKeyRequestCancellation(event))
|
||||
RoomKeyShare.ACTION_SHARE_CANCELLATION -> synchronized(receivedRoomKeyRequestCancellations) {
|
||||
receivedRoomKeyRequestCancellations.add(IncomingRoomKeyRequestCancellation(event))
|
||||
}
|
||||
else -> Timber.e("## onRoomKeyRequestEvent() : unsupported action " + roomKeyShare?.action)
|
||||
}
|
||||
|
@ -71,10 +70,10 @@ internal class IncomingRoomKeyRequestManager(
|
|||
fun processReceivedRoomKeyRequests() {
|
||||
var receivedRoomKeyRequests: List<IncomingRoomKeyRequest>? = null
|
||||
|
||||
synchronized(mReceivedRoomKeyRequests) {
|
||||
if (!mReceivedRoomKeyRequests.isEmpty()) {
|
||||
receivedRoomKeyRequests = ArrayList(mReceivedRoomKeyRequests)
|
||||
mReceivedRoomKeyRequests.clear()
|
||||
synchronized(this.receivedRoomKeyRequests) {
|
||||
if (this.receivedRoomKeyRequests.isNotEmpty()) {
|
||||
receivedRoomKeyRequests = ArrayList(this.receivedRoomKeyRequests)
|
||||
this.receivedRoomKeyRequests.clear()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -88,7 +87,7 @@ internal class IncomingRoomKeyRequestManager(
|
|||
|
||||
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(credentials.userId, userId)) {
|
||||
// TODO: determine if we sent this device the keys already: in
|
||||
Timber.e("## processReceivedRoomKeyRequests() : Ignoring room key request from other user for now")
|
||||
return
|
||||
|
@ -100,7 +99,7 @@ internal class IncomingRoomKeyRequestManager(
|
|||
// if we don't have a decryptor for this room/alg, we don't have
|
||||
// the keys for the requested events, and can drop the requests.
|
||||
|
||||
val decryptor = mRoomDecryptorProvider.getRoomDecryptor(roomId, alg)
|
||||
val decryptor = roomDecryptorProvider.getRoomDecryptor(roomId, alg)
|
||||
|
||||
if (null == decryptor) {
|
||||
Timber.e("## processReceivedRoomKeyRequests() : room key request for unknown $alg in room $roomId")
|
||||
|
@ -109,52 +108,52 @@ internal class IncomingRoomKeyRequestManager(
|
|||
|
||||
if (!decryptor.hasKeysForKeyRequest(request)) {
|
||||
Timber.e("## processReceivedRoomKeyRequests() : room key request for unknown session " + body.sessionId!!)
|
||||
mCryptoStore.deleteIncomingRoomKeyRequest(request)
|
||||
cryptoStore.deleteIncomingRoomKeyRequest(request)
|
||||
continue
|
||||
}
|
||||
|
||||
if (TextUtils.equals(deviceId, mCredentials.deviceId) && TextUtils.equals(mCredentials.userId, userId)) {
|
||||
if (TextUtils.equals(deviceId, credentials.deviceId) && TextUtils.equals(credentials.userId, userId)) {
|
||||
Timber.v("## processReceivedRoomKeyRequests() : oneself device - ignored")
|
||||
mCryptoStore.deleteIncomingRoomKeyRequest(request)
|
||||
cryptoStore.deleteIncomingRoomKeyRequest(request)
|
||||
continue
|
||||
}
|
||||
|
||||
request.share = Runnable {
|
||||
decryptor.shareKeysWithDevice(request)
|
||||
mCryptoStore.deleteIncomingRoomKeyRequest(request)
|
||||
cryptoStore.deleteIncomingRoomKeyRequest(request)
|
||||
}
|
||||
|
||||
request.ignore = Runnable { mCryptoStore.deleteIncomingRoomKeyRequest(request) }
|
||||
request.ignore = Runnable { cryptoStore.deleteIncomingRoomKeyRequest(request) }
|
||||
|
||||
// if the device is verified already, share the keys
|
||||
val device = mCryptoStore.getUserDevice(deviceId!!, userId)
|
||||
val device = cryptoStore.getUserDevice(deviceId!!, userId)
|
||||
|
||||
if (null != device) {
|
||||
if (device.isVerified) {
|
||||
Timber.v("## processReceivedRoomKeyRequests() : device is already verified: sharing keys")
|
||||
mCryptoStore.deleteIncomingRoomKeyRequest(request)
|
||||
cryptoStore.deleteIncomingRoomKeyRequest(request)
|
||||
request.share?.run()
|
||||
continue
|
||||
}
|
||||
|
||||
if (device.isBlocked) {
|
||||
Timber.v("## processReceivedRoomKeyRequests() : device is blocked -> ignored")
|
||||
mCryptoStore.deleteIncomingRoomKeyRequest(request)
|
||||
cryptoStore.deleteIncomingRoomKeyRequest(request)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
mCryptoStore.storeIncomingRoomKeyRequest(request)
|
||||
cryptoStore.storeIncomingRoomKeyRequest(request)
|
||||
onRoomKeyRequest(request)
|
||||
}
|
||||
}
|
||||
|
||||
var receivedRoomKeyRequestCancellations: List<IncomingRoomKeyRequestCancellation>? = null
|
||||
|
||||
synchronized(mReceivedRoomKeyRequestCancellations) {
|
||||
if (!mReceivedRoomKeyRequestCancellations.isEmpty()) {
|
||||
receivedRoomKeyRequestCancellations = mReceivedRoomKeyRequestCancellations.toList()
|
||||
mReceivedRoomKeyRequestCancellations.clear()
|
||||
synchronized(this.receivedRoomKeyRequestCancellations) {
|
||||
if (!this.receivedRoomKeyRequestCancellations.isEmpty()) {
|
||||
receivedRoomKeyRequestCancellations = this.receivedRoomKeyRequestCancellations.toList()
|
||||
this.receivedRoomKeyRequestCancellations.clear()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -167,7 +166,7 @@ internal class IncomingRoomKeyRequestManager(
|
|||
// about, but we don't currently have a record of that, so we just pass
|
||||
// everything through.
|
||||
onRoomKeyRequestCancellation(request)
|
||||
mCryptoStore.deleteIncomingRoomKeyRequest(request)
|
||||
cryptoStore.deleteIncomingRoomKeyRequest(request)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -178,8 +177,8 @@ internal class IncomingRoomKeyRequestManager(
|
|||
* @param request the request
|
||||
*/
|
||||
private fun onRoomKeyRequest(request: IncomingRoomKeyRequest) {
|
||||
synchronized(mRoomKeysRequestListeners) {
|
||||
for (listener in mRoomKeysRequestListeners) {
|
||||
synchronized(roomKeysRequestListeners) {
|
||||
for (listener in roomKeysRequestListeners) {
|
||||
try {
|
||||
listener.onRoomKeyRequest(request)
|
||||
} catch (e: Exception) {
|
||||
|
@ -197,8 +196,8 @@ internal class IncomingRoomKeyRequestManager(
|
|||
* @param request the cancellation request
|
||||
*/
|
||||
private fun onRoomKeyRequestCancellation(request: IncomingRoomKeyRequestCancellation) {
|
||||
synchronized(mRoomKeysRequestListeners) {
|
||||
for (listener in mRoomKeysRequestListeners) {
|
||||
synchronized(roomKeysRequestListeners) {
|
||||
for (listener in roomKeysRequestListeners) {
|
||||
try {
|
||||
listener.onRoomKeyRequestCancellation(request)
|
||||
} catch (e: Exception) {
|
||||
|
@ -210,14 +209,14 @@ internal class IncomingRoomKeyRequestManager(
|
|||
}
|
||||
|
||||
fun addRoomKeysRequestListener(listener: RoomKeysRequestListener) {
|
||||
synchronized(mRoomKeysRequestListeners) {
|
||||
mRoomKeysRequestListeners.add(listener)
|
||||
synchronized(roomKeysRequestListeners) {
|
||||
roomKeysRequestListeners.add(listener)
|
||||
}
|
||||
}
|
||||
|
||||
fun removeRoomKeysRequestListener(listener: RoomKeysRequestListener) {
|
||||
synchronized(mRoomKeysRequestListeners) {
|
||||
mRoomKeysRequestListeners.remove(listener)
|
||||
synchronized(roomKeysRequestListeners) {
|
||||
roomKeysRequestListeners.remove(listener)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -28,20 +28,19 @@ import timber.log.Timber
|
|||
import java.util.*
|
||||
|
||||
internal class OneTimeKeysUploader(
|
||||
private val mCredentials: Credentials,
|
||||
private val mOlmDevice: MXOlmDevice,
|
||||
private val mObjectSigner: ObjectSigner,
|
||||
private val mUploadKeysTask: UploadKeysTask
|
||||
private val credentials: Credentials,
|
||||
private val olmDevice: MXOlmDevice,
|
||||
private val objectSigner: ObjectSigner,
|
||||
private val uploadKeysTask: UploadKeysTask
|
||||
) {
|
||||
// tell if there is a OTK check in progress
|
||||
private var mOneTimeKeyCheckInProgress = false
|
||||
private var oneTimeKeyCheckInProgress = false
|
||||
|
||||
// last OTK check timestamp
|
||||
private var mLastOneTimeKeyCheck: Long = 0
|
||||
private var mOneTimeKeyCount: Int? = null
|
||||
private var lastOneTimeKeyCheck: Long = 0
|
||||
private var oneTimeKeyCount: Int? = null
|
||||
|
||||
var mLastPublishedOneTimeKeys: Map<String, Map<String, String>>? = null
|
||||
private set
|
||||
private var lastPublishedOneTimeKeys: Map<String, Map<String, String>>? = null
|
||||
|
||||
/**
|
||||
* Stores the current one_time_key count which will be handled later (in a call of
|
||||
|
@ -50,7 +49,7 @@ internal class OneTimeKeysUploader(
|
|||
* @param currentCount the new count
|
||||
*/
|
||||
fun updateOneTimeKeyCount(currentCount: Int) {
|
||||
mOneTimeKeyCount = currentCount
|
||||
oneTimeKeyCount = currentCount
|
||||
}
|
||||
|
||||
|
||||
|
@ -58,19 +57,19 @@ internal class OneTimeKeysUploader(
|
|||
* Check if the OTK must be uploaded.
|
||||
*/
|
||||
suspend fun maybeUploadOneTimeKeys(): Try<Unit> {
|
||||
if (mOneTimeKeyCheckInProgress) {
|
||||
if (oneTimeKeyCheckInProgress) {
|
||||
return Try.just(Unit)
|
||||
}
|
||||
if (System.currentTimeMillis() - mLastOneTimeKeyCheck < ONE_TIME_KEY_UPLOAD_PERIOD) {
|
||||
if (System.currentTimeMillis() - lastOneTimeKeyCheck < ONE_TIME_KEY_UPLOAD_PERIOD) {
|
||||
// we've done a key upload recently.
|
||||
return Try.just(Unit)
|
||||
}
|
||||
|
||||
mLastOneTimeKeyCheck = System.currentTimeMillis()
|
||||
mOneTimeKeyCheckInProgress = true
|
||||
lastOneTimeKeyCheck = System.currentTimeMillis()
|
||||
oneTimeKeyCheckInProgress = true
|
||||
|
||||
// We then check how many keys we can store in the Account object.
|
||||
val maxOneTimeKeys = mOlmDevice.getMaxNumberOfOneTimeKeys()
|
||||
val maxOneTimeKeys = olmDevice.getMaxNumberOfOneTimeKeys()
|
||||
|
||||
// Try to keep at most half that number on the server. This leaves the
|
||||
// rest of the slots free to hold keys that have been claimed from the
|
||||
|
@ -79,12 +78,12 @@ internal class OneTimeKeysUploader(
|
|||
// discard the oldest private keys first. This will eventually clean
|
||||
// out stale private keys that won't receive a message.
|
||||
val keyLimit = Math.floor(maxOneTimeKeys / 2.0).toInt()
|
||||
if (mOneTimeKeyCount != null) {
|
||||
return uploadOTK(mOneTimeKeyCount!!, keyLimit)
|
||||
val result = if (oneTimeKeyCount != null) {
|
||||
uploadOTK(oneTimeKeyCount!!, keyLimit)
|
||||
} else {
|
||||
// ask the server how many keys we have
|
||||
val uploadKeysParams = UploadKeysTask.Params(null, null, mCredentials.deviceId!!)
|
||||
return mUploadKeysTask.execute(uploadKeysParams)
|
||||
val uploadKeysParams = UploadKeysTask.Params(null, null, credentials.deviceId!!)
|
||||
uploadKeysTask.execute(uploadKeysParams)
|
||||
.flatMap {
|
||||
// We need to keep a pool of one time public keys on the server so that
|
||||
// other devices can start conversations with us. But we can only store
|
||||
|
@ -97,17 +96,23 @@ internal class OneTimeKeysUploader(
|
|||
// private keys clogging up our local storage.
|
||||
// So we need some kind of engineering compromise to balance all of
|
||||
// these factors.
|
||||
// TODO Why we do not set mOneTimeKeyCount here?
|
||||
// TODO Why we do not set oneTimeKeyCount here?
|
||||
// TODO This is not needed anymore, see https://github.com/matrix-org/matrix-js-sdk/pull/493 (TODO on iOS also)
|
||||
val keyCount = it.oneTimeKeyCountsForAlgorithm(MXKey.KEY_SIGNED_CURVE_25519_TYPE)
|
||||
uploadOTK(keyCount, keyLimit)
|
||||
}
|
||||
.handleError {
|
||||
Timber.e(it, "## uploadKeys() : failed")
|
||||
mOneTimeKeyCount = null
|
||||
mOneTimeKeyCheckInProgress = false
|
||||
}
|
||||
}
|
||||
return result
|
||||
.map {
|
||||
Timber.v("## uploadKeys() : success")
|
||||
oneTimeKeyCount = null
|
||||
oneTimeKeyCheckInProgress = false
|
||||
}
|
||||
.handleError {
|
||||
Timber.e(it, "## uploadKeys() : failed")
|
||||
oneTimeKeyCount = null
|
||||
oneTimeKeyCheckInProgress = false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -117,27 +122,17 @@ internal class OneTimeKeysUploader(
|
|||
* @param keyLimit the limit
|
||||
*/
|
||||
private suspend fun uploadOTK(keyCount: Int, keyLimit: Int): Try<Unit> {
|
||||
return uploadLoop(keyCount, keyLimit)
|
||||
}
|
||||
|
||||
/**
|
||||
* OTK upload loop
|
||||
*
|
||||
* @param keyCount the number of key to generate
|
||||
* @param keyLimit the limit
|
||||
*/
|
||||
private suspend fun uploadLoop(keyCount: Int, keyLimit: Int): Try<Unit> {
|
||||
if (keyLimit <= keyCount) {
|
||||
// If we don't need to generate any more keys then we are done.
|
||||
return Try.just(Unit)
|
||||
}
|
||||
|
||||
val keysThisLoop = Math.min(keyLimit - keyCount, ONE_TIME_KEY_GENERATION_MAX_NUMBER)
|
||||
mOlmDevice.generateOneTimeKeys(keysThisLoop)
|
||||
olmDevice.generateOneTimeKeys(keysThisLoop)
|
||||
return uploadOneTimeKeys()
|
||||
.flatMap {
|
||||
if (it.hasOneTimeKeyCountsForAlgorithm(MXKey.KEY_SIGNED_CURVE_25519_TYPE)) {
|
||||
uploadLoop(it.oneTimeKeyCountsForAlgorithm(MXKey.KEY_SIGNED_CURVE_25519_TYPE), keyLimit)
|
||||
uploadOTK(it.oneTimeKeyCountsForAlgorithm(MXKey.KEY_SIGNED_CURVE_25519_TYPE), keyLimit)
|
||||
} else {
|
||||
Timber.e("## uploadLoop() : response for uploading keys does not contain one_time_key_counts.signed_curve25519")
|
||||
Try.raise(Exception("response for uploading keys does not contain one_time_key_counts.signed_curve25519"))
|
||||
|
@ -149,7 +144,7 @@ internal class OneTimeKeysUploader(
|
|||
* Upload my user's one time keys.
|
||||
*/
|
||||
private suspend fun uploadOneTimeKeys(): Try<KeysUploadResponse> {
|
||||
val oneTimeKeys = mOlmDevice.getOneTimeKeys()
|
||||
val oneTimeKeys = olmDevice.getOneTimeKeys()
|
||||
val oneTimeJson = HashMap<String, Any>()
|
||||
|
||||
val curve25519Map = oneTimeKeys!![OlmAccount.JSON_KEY_ONE_TIME_KEY]
|
||||
|
@ -162,7 +157,7 @@ internal class OneTimeKeysUploader(
|
|||
// the key is also signed
|
||||
val canonicalJson = MoshiProvider.getCanonicalJson(Map::class.java, k)
|
||||
|
||||
k["signatures"] = mObjectSigner.signObject(canonicalJson)
|
||||
k["signatures"] = objectSigner.signObject(canonicalJson)
|
||||
|
||||
oneTimeJson["signed_curve25519:$key_id"] = k
|
||||
}
|
||||
|
@ -170,12 +165,12 @@ internal class OneTimeKeysUploader(
|
|||
|
||||
// For now, we set the device id explicitly, as we may not be using the
|
||||
// same one as used in login.
|
||||
val uploadParams = UploadKeysTask.Params(null, oneTimeJson, mCredentials.deviceId!!)
|
||||
return mUploadKeysTask
|
||||
val uploadParams = UploadKeysTask.Params(null, oneTimeJson, credentials.deviceId!!)
|
||||
return uploadKeysTask
|
||||
.execute(uploadParams)
|
||||
.map {
|
||||
mLastPublishedOneTimeKeys = oneTimeKeys
|
||||
mOlmDevice.markKeysAsPublished()
|
||||
lastPublishedOneTimeKeys = oneTimeKeys
|
||||
olmDevice.markKeysAsPublished()
|
||||
it
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,15 +24,15 @@ import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyRequestBody
|
|||
*/
|
||||
class OutgoingRoomKeyRequest(
|
||||
// RequestBody
|
||||
var mRequestBody: RoomKeyRequestBody?, // list of recipients for the request
|
||||
var mRecipients: List<Map<String, String>>, // Unique id for this request. Used for both
|
||||
var requestBody: RoomKeyRequestBody?, // list of recipients for the request
|
||||
var recipients: List<Map<String, String>>, // Unique id for this request. Used for both
|
||||
// an id within the request for later pairing with a cancellation, and for
|
||||
// the transaction id when sending the to_device messages to our local
|
||||
var mRequestId: String, // current state of this request
|
||||
var mState: RequestState) {
|
||||
var requestId: String, // current state of this request
|
||||
var state: RequestState) {
|
||||
|
||||
// transaction id for the cancellation, if any
|
||||
var mCancellationTxnId: String? = null
|
||||
var cancellationTxnId: String? = null
|
||||
|
||||
/**
|
||||
* Used only for log.
|
||||
|
@ -40,8 +40,8 @@ class OutgoingRoomKeyRequest(
|
|||
* @return the room id.
|
||||
*/
|
||||
val roomId: String?
|
||||
get() = if (null != mRequestBody) {
|
||||
mRequestBody!!.roomId
|
||||
get() = if (null != requestBody) {
|
||||
requestBody!!.roomId
|
||||
} else null
|
||||
|
||||
/**
|
||||
|
@ -50,8 +50,8 @@ class OutgoingRoomKeyRequest(
|
|||
* @return the session id
|
||||
*/
|
||||
val sessionId: String?
|
||||
get() = if (null != mRequestBody) {
|
||||
mRequestBody!!.sessionId
|
||||
get() = if (null != requestBody) {
|
||||
requestBody!!.sessionId
|
||||
} else null
|
||||
|
||||
/**
|
||||
|
|
|
@ -27,7 +27,6 @@ import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyShareRequest
|
|||
import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
|
||||
import im.vector.matrix.android.internal.crypto.tasks.SendToDeviceTask
|
||||
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 timber.log.Timber
|
||||
import java.util.*
|
||||
|
@ -88,7 +87,7 @@ internal class OutgoingRoomKeyRequestManager(
|
|||
OutgoingRoomKeyRequest(requestBody, recipients, makeTxnId(), OutgoingRoomKeyRequest.RequestState.UNSENT))
|
||||
|
||||
|
||||
if (req?.mState == OutgoingRoomKeyRequest.RequestState.UNSENT) {
|
||||
if (req?.state == OutgoingRoomKeyRequest.RequestState.UNSENT) {
|
||||
startTimer()
|
||||
}
|
||||
}
|
||||
|
@ -122,20 +121,20 @@ internal class OutgoingRoomKeyRequestManager(
|
|||
?: // no request was made for this key
|
||||
return
|
||||
|
||||
Timber.v("cancelRoomKeyRequest: requestId: " + req.mRequestId + " state: " + req.mState + " andResend: " + andResend)
|
||||
Timber.v("cancelRoomKeyRequest: requestId: " + req.requestId + " state: " + req.state + " andResend: " + andResend)
|
||||
|
||||
if (req.mState === OutgoingRoomKeyRequest.RequestState.CANCELLATION_PENDING || req.mState === OutgoingRoomKeyRequest.RequestState.CANCELLATION_PENDING_AND_WILL_RESEND) {
|
||||
if (req.state === OutgoingRoomKeyRequest.RequestState.CANCELLATION_PENDING || req.state === OutgoingRoomKeyRequest.RequestState.CANCELLATION_PENDING_AND_WILL_RESEND) {
|
||||
// nothing to do here
|
||||
} else if (req.mState === OutgoingRoomKeyRequest.RequestState.UNSENT || req.mState === OutgoingRoomKeyRequest.RequestState.FAILED) {
|
||||
} else if (req.state === OutgoingRoomKeyRequest.RequestState.UNSENT || req.state === OutgoingRoomKeyRequest.RequestState.FAILED) {
|
||||
Timber.v("## cancelRoomKeyRequest() : deleting unnecessary room key request for $requestBody")
|
||||
cryptoStore.deleteOutgoingRoomKeyRequest(req.mRequestId)
|
||||
} else if (req.mState === OutgoingRoomKeyRequest.RequestState.SENT) {
|
||||
cryptoStore.deleteOutgoingRoomKeyRequest(req.requestId)
|
||||
} else if (req.state === OutgoingRoomKeyRequest.RequestState.SENT) {
|
||||
if (andResend) {
|
||||
req.mState = OutgoingRoomKeyRequest.RequestState.CANCELLATION_PENDING_AND_WILL_RESEND
|
||||
req.state = OutgoingRoomKeyRequest.RequestState.CANCELLATION_PENDING_AND_WILL_RESEND
|
||||
} else {
|
||||
req.mState = OutgoingRoomKeyRequest.RequestState.CANCELLATION_PENDING
|
||||
req.state = OutgoingRoomKeyRequest.RequestState.CANCELLATION_PENDING
|
||||
}
|
||||
req.mCancellationTxnId = makeTxnId()
|
||||
req.cancellationTxnId = makeTxnId()
|
||||
cryptoStore.updateOutgoingRoomKeyRequest(req)
|
||||
sendOutgoingRoomKeyRequestCancellation(req)
|
||||
}
|
||||
|
@ -149,7 +148,6 @@ internal class OutgoingRoomKeyRequestManager(
|
|||
if (sendOutgoingRoomKeyRequestsRunning) {
|
||||
return
|
||||
}
|
||||
|
||||
Handler().postDelayed(Runnable {
|
||||
if (sendOutgoingRoomKeyRequestsRunning) {
|
||||
Timber.v("## startTimer() : RoomKeyRequestSend already in progress!")
|
||||
|
@ -182,7 +180,7 @@ internal class OutgoingRoomKeyRequestManager(
|
|||
return
|
||||
}
|
||||
|
||||
if (OutgoingRoomKeyRequest.RequestState.UNSENT === outgoingRoomKeyRequest.mState) {
|
||||
if (OutgoingRoomKeyRequest.RequestState.UNSENT === outgoingRoomKeyRequest.state) {
|
||||
sendOutgoingRoomKeyRequest(outgoingRoomKeyRequest)
|
||||
} else {
|
||||
sendOutgoingRoomKeyRequestCancellation(outgoingRoomKeyRequest)
|
||||
|
@ -195,20 +193,20 @@ internal class OutgoingRoomKeyRequestManager(
|
|||
* @param request the request
|
||||
*/
|
||||
private fun sendOutgoingRoomKeyRequest(request: OutgoingRoomKeyRequest) {
|
||||
Timber.v("## sendOutgoingRoomKeyRequest() : Requesting keys " + request.mRequestBody
|
||||
+ " from " + request.mRecipients + " id " + request.mRequestId)
|
||||
Timber.v("## sendOutgoingRoomKeyRequest() : Requesting keys " + request.requestBody
|
||||
+ " from " + request.recipients + " id " + request.requestId)
|
||||
|
||||
val requestMessage = RoomKeyShareRequest()
|
||||
requestMessage.requestingDeviceId = cryptoStore.getDeviceId()
|
||||
requestMessage.requestId = request.mRequestId
|
||||
requestMessage.body = request.mRequestBody
|
||||
requestMessage.requestId = request.requestId
|
||||
requestMessage.body = request.requestBody
|
||||
|
||||
sendMessageToDevices(requestMessage, request.mRecipients, request.mRequestId, object : MatrixCallback<Unit> {
|
||||
sendMessageToDevices(requestMessage, request.recipients, request.requestId, object : MatrixCallback<Unit> {
|
||||
private fun onDone(state: OutgoingRoomKeyRequest.RequestState) {
|
||||
if (request.mState !== OutgoingRoomKeyRequest.RequestState.UNSENT) {
|
||||
Timber.v("## sendOutgoingRoomKeyRequest() : Cannot update room key request from UNSENT as it was already updated to " + request.mState)
|
||||
if (request.state !== OutgoingRoomKeyRequest.RequestState.UNSENT) {
|
||||
Timber.v("## sendOutgoingRoomKeyRequest() : Cannot update room key request from UNSENT as it was already updated to " + request.state)
|
||||
} else {
|
||||
request.mState = state
|
||||
request.state = state
|
||||
cryptoStore.updateOutgoingRoomKeyRequest(request)
|
||||
}
|
||||
|
||||
|
@ -234,17 +232,17 @@ internal class OutgoingRoomKeyRequestManager(
|
|||
* @param request the request
|
||||
*/
|
||||
private fun sendOutgoingRoomKeyRequestCancellation(request: OutgoingRoomKeyRequest) {
|
||||
Timber.v("## sendOutgoingRoomKeyRequestCancellation() : Sending cancellation for key request for " + request.mRequestBody
|
||||
+ " to " + request.mRecipients
|
||||
+ " cancellation id " + request.mCancellationTxnId)
|
||||
Timber.v("## sendOutgoingRoomKeyRequestCancellation() : Sending cancellation for key request for " + request.requestBody
|
||||
+ " to " + request.recipients
|
||||
+ " cancellation id " + request.cancellationTxnId)
|
||||
|
||||
val roomKeyShareCancellation = RoomKeyShareCancellation()
|
||||
roomKeyShareCancellation.requestingDeviceId = cryptoStore.getDeviceId()
|
||||
roomKeyShareCancellation.requestId = request.mCancellationTxnId
|
||||
roomKeyShareCancellation.requestId = request.cancellationTxnId
|
||||
|
||||
sendMessageToDevices(roomKeyShareCancellation, request.mRecipients, request.mCancellationTxnId, object : MatrixCallback<Unit> {
|
||||
sendMessageToDevices(roomKeyShareCancellation, request.recipients, request.cancellationTxnId, object : MatrixCallback<Unit> {
|
||||
private fun onDone() {
|
||||
cryptoStore.deleteOutgoingRoomKeyRequest(request.mRequestId)
|
||||
cryptoStore.deleteOutgoingRoomKeyRequest(request.requestId)
|
||||
sendOutgoingRoomKeyRequestsRunning = false
|
||||
startTimer()
|
||||
}
|
||||
|
@ -252,13 +250,13 @@ internal class OutgoingRoomKeyRequestManager(
|
|||
|
||||
override fun onSuccess(data: Unit) {
|
||||
Timber.v("## sendOutgoingRoomKeyRequestCancellation() : done")
|
||||
val resend = request.mState === OutgoingRoomKeyRequest.RequestState.CANCELLATION_PENDING_AND_WILL_RESEND
|
||||
val resend = request.state === OutgoingRoomKeyRequest.RequestState.CANCELLATION_PENDING_AND_WILL_RESEND
|
||||
|
||||
onDone()
|
||||
|
||||
// Resend the request with a new ID
|
||||
if (resend) {
|
||||
sendRoomKeyRequest(request.mRequestBody, request.mRecipients)
|
||||
sendRoomKeyRequest(request.requestBody, request.recipients)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -43,37 +43,35 @@ internal class RoomDecryptorProvider(
|
|||
*/
|
||||
fun getOrCreateRoomDecryptor(roomId: String?, algorithm: String?): IMXDecrypting? {
|
||||
// sanity check
|
||||
if (algorithm.isNullOrEmpty() || roomId.isNullOrEmpty()) {
|
||||
if (algorithm.isNullOrEmpty()) {
|
||||
Timber.e("## getRoomDecryptor() : null algorithm")
|
||||
return null
|
||||
}
|
||||
|
||||
var alg: IMXDecrypting?
|
||||
synchronized(roomDecryptors) {
|
||||
if (!roomDecryptors.containsKey(roomId)) {
|
||||
roomDecryptors[roomId!!] = HashMap()
|
||||
}
|
||||
|
||||
alg = roomDecryptors[roomId]!![algorithm]
|
||||
}
|
||||
if (alg != null) {
|
||||
return alg
|
||||
}
|
||||
val decryptingClass = MXCryptoAlgorithms.hasDecryptorClassForAlgorithm(algorithm)
|
||||
if (decryptingClass) {
|
||||
alg = when (algorithm) {
|
||||
MXCRYPTO_ALGORITHM_MEGOLM -> megolmDecryptionFactory.create()
|
||||
else -> olmDecryptionFactory.create()
|
||||
}
|
||||
if (null != alg) {
|
||||
if (!TextUtils.isEmpty(roomId)) {
|
||||
synchronized(roomDecryptors) {
|
||||
roomDecryptors[roomId]!!.put(algorithm!!, alg!!)
|
||||
}
|
||||
if(roomId != null && roomId.isNotEmpty()) {
|
||||
synchronized(roomDecryptors) {
|
||||
if (!roomDecryptors.containsKey(roomId)) {
|
||||
roomDecryptors[roomId] = HashMap()
|
||||
}
|
||||
val alg = roomDecryptors[roomId]?.get(algorithm)
|
||||
if (alg != null) {
|
||||
return alg
|
||||
}
|
||||
}
|
||||
}
|
||||
return alg
|
||||
val decryptingClass = MXCryptoAlgorithms.hasDecryptorClassForAlgorithm(algorithm)
|
||||
if (decryptingClass) {
|
||||
val alg = when (algorithm) {
|
||||
MXCRYPTO_ALGORITHM_MEGOLM -> megolmDecryptionFactory.create()
|
||||
else -> olmDecryptionFactory.create()
|
||||
}
|
||||
if (roomId != null && !TextUtils.isEmpty(roomId)) {
|
||||
synchronized(roomDecryptors) {
|
||||
roomDecryptors[roomId]?.put(algorithm, alg)
|
||||
}
|
||||
}
|
||||
return alg
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
fun getRoomDecryptor(roomId: String?, algorithm: String?): IMXDecrypting? {
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
package im.vector.matrix.android.internal.crypto.actions
|
||||
|
||||
import android.text.TextUtils
|
||||
import arrow.core.Try
|
||||
import im.vector.matrix.android.api.MatrixCallback
|
||||
import im.vector.matrix.android.internal.crypto.MXOlmDevice
|
||||
import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo
|
||||
|
@ -31,12 +32,10 @@ import timber.log.Timber
|
|||
import java.util.*
|
||||
|
||||
internal class EnsureOlmSessionsForDevicesAction(private val olmDevice: MXOlmDevice,
|
||||
private val oneTimeKeysForUsersDeviceTask: ClaimOneTimeKeysForUsersDeviceTask,
|
||||
private val coroutineDispatchers: MatrixCoroutineDispatchers,
|
||||
private val taskExecutor: TaskExecutor) {
|
||||
private val oneTimeKeysForUsersDeviceTask: ClaimOneTimeKeysForUsersDeviceTask) {
|
||||
|
||||
|
||||
fun handle(devicesByUser: Map<String, List<MXDeviceInfo>>, callback: MatrixCallback<MXUsersDevicesMap<MXOlmSessionResult>>?) {
|
||||
suspend fun handle(devicesByUser: Map<String, List<MXDeviceInfo>>): Try<MXUsersDevicesMap<MXOlmSessionResult>> {
|
||||
val devicesWithoutSession = ArrayList<MXDeviceInfo>()
|
||||
|
||||
val results = MXUsersDevicesMap<MXOlmSessionResult>()
|
||||
|
@ -62,8 +61,7 @@ internal class EnsureOlmSessionsForDevicesAction(private val olmDevice: MXOlmDev
|
|||
}
|
||||
|
||||
if (devicesWithoutSession.size == 0) {
|
||||
callback?.onSuccess(results)
|
||||
return
|
||||
return Try.just(results)
|
||||
}
|
||||
|
||||
// Prepare the request for claiming one-time keys
|
||||
|
@ -83,67 +81,42 @@ internal class EnsureOlmSessionsForDevicesAction(private val olmDevice: MXOlmDev
|
|||
|
||||
Timber.v("## claimOneTimeKeysForUsersDevices() : $usersDevicesToClaim")
|
||||
|
||||
|
||||
oneTimeKeysForUsersDeviceTask
|
||||
.configureWith(ClaimOneTimeKeysForUsersDeviceTask.Params(usersDevicesToClaim))
|
||||
.dispatchTo(object : MatrixCallback<MXUsersDevicesMap<MXKey>> {
|
||||
override fun onSuccess(data: MXUsersDevicesMap<MXKey>) {
|
||||
try {
|
||||
Timber.v("## claimOneTimeKeysForUsersDevices() : keysClaimResponse.oneTimeKeys: $data")
|
||||
|
||||
for (userId in userIds) {
|
||||
val deviceInfos = devicesByUser[userId]
|
||||
|
||||
for (deviceInfo in deviceInfos!!) {
|
||||
|
||||
var oneTimeKey: MXKey? = null
|
||||
|
||||
val deviceIds = data.getUserDeviceIds(userId)
|
||||
|
||||
if (null != deviceIds) {
|
||||
for (deviceId in deviceIds) {
|
||||
val olmSessionResult = results.getObject(deviceId, userId)
|
||||
|
||||
if (null != olmSessionResult!!.mSessionId) {
|
||||
// We already have a result for this device
|
||||
continue
|
||||
}
|
||||
|
||||
val key = data.getObject(deviceId, userId)
|
||||
|
||||
if (TextUtils.equals(key!!.type, oneTimeKeyAlgorithm)) {
|
||||
oneTimeKey = key
|
||||
}
|
||||
|
||||
if (null == oneTimeKey) {
|
||||
Timber.v("## ensureOlmSessionsForDevices() : No one-time keys " + oneTimeKeyAlgorithm
|
||||
+ " for device " + userId + " : " + deviceId)
|
||||
continue
|
||||
}
|
||||
|
||||
// Update the result for this device in results
|
||||
olmSessionResult.mSessionId = verifyKeyAndStartSession(oneTimeKey, userId, deviceInfo)
|
||||
}
|
||||
val claimParams = ClaimOneTimeKeysForUsersDeviceTask.Params(usersDevicesToClaim)
|
||||
return oneTimeKeysForUsersDeviceTask
|
||||
.execute(claimParams)
|
||||
.map {
|
||||
Timber.v("## claimOneTimeKeysForUsersDevices() : keysClaimResponse.oneTimeKeys: $it")
|
||||
for (userId in userIds) {
|
||||
val deviceInfos = devicesByUser[userId]
|
||||
for (deviceInfo in deviceInfos!!) {
|
||||
var oneTimeKey: MXKey? = null
|
||||
val deviceIds = it.getUserDeviceIds(userId)
|
||||
if (null != deviceIds) {
|
||||
for (deviceId in deviceIds) {
|
||||
val olmSessionResult = results.getObject(deviceId, userId)
|
||||
if (olmSessionResult!!.mSessionId != null) {
|
||||
// We already have a result for this device
|
||||
continue
|
||||
}
|
||||
val key = it.getObject(deviceId, userId)
|
||||
if (key?.type == oneTimeKeyAlgorithm) {
|
||||
oneTimeKey = key
|
||||
}
|
||||
if (oneTimeKey == null) {
|
||||
Timber.v("## ensureOlmSessionsForDevices() : No one-time keys " + oneTimeKeyAlgorithm
|
||||
+ " for device " + userId + " : " + deviceId)
|
||||
continue
|
||||
}
|
||||
// Update the result for this device in results
|
||||
olmSessionResult.mSessionId = verifyKeyAndStartSession(oneTimeKey, userId, deviceInfo)
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "## ensureOlmSessionsForDevices() " + e.message)
|
||||
}
|
||||
|
||||
callback?.onSuccess(results)
|
||||
}
|
||||
|
||||
override fun onFailure(failure: Throwable) {
|
||||
Timber.e(failure, "## ensureOlmSessionsForUsers(): claimOneTimeKeysForUsersDevices request failed")
|
||||
|
||||
callback?.onFailure(failure)
|
||||
}
|
||||
})
|
||||
.executeBy(taskExecutor)
|
||||
results
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun verifyKeyAndStartSession(oneTimeKey: MXKey, userId: String, deviceInfo: MXDeviceInfo): String? {
|
||||
var sessionId: String? = null
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
package im.vector.matrix.android.internal.crypto.actions
|
||||
|
||||
import android.text.TextUtils
|
||||
import arrow.core.Try
|
||||
import im.vector.matrix.android.api.MatrixCallback
|
||||
import im.vector.matrix.android.internal.crypto.MXOlmDevice
|
||||
import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo
|
||||
|
@ -32,15 +33,10 @@ internal class EnsureOlmSessionsForUsersAction(private val olmDevice: MXOlmDevic
|
|||
|
||||
/**
|
||||
* Try to make sure we have established olm sessions for the given users.
|
||||
* It must be called in getEncryptingThreadHandler() thread.
|
||||
* The callback is called in the UI thread.
|
||||
*
|
||||
* @param users a list of user ids.
|
||||
* @param callback the asynchronous callback
|
||||
*/
|
||||
fun handle(users: List<String>, callback: MatrixCallback<MXUsersDevicesMap<MXOlmSessionResult>>) {
|
||||
suspend fun handle(users: List<String>) : Try<MXUsersDevicesMap<MXOlmSessionResult>> {
|
||||
Timber.v("## ensureOlmSessionsForUsers() : ensureOlmSessionsForUsers $users")
|
||||
|
||||
val devicesByUser = HashMap<String /* userId */, MutableList<MXDeviceInfo>>()
|
||||
|
||||
for (userId in users) {
|
||||
|
@ -64,7 +60,6 @@ internal class EnsureOlmSessionsForUsersAction(private val olmDevice: MXOlmDevic
|
|||
devicesByUser[userId]!!.add(device)
|
||||
}
|
||||
}
|
||||
|
||||
ensureOlmSessionsForDevicesAction.handle(devicesByUser, callback)
|
||||
return ensureOlmSessionsForDevicesAction.handle(devicesByUser)
|
||||
}
|
||||
}
|
|
@ -37,7 +37,7 @@ internal interface IMXDecrypting {
|
|||
* @throws MXDecryptionException the decryption failure reason
|
||||
*/
|
||||
@Throws(MXDecryptionException::class)
|
||||
fun decryptEvent(event: Event, timeline: String): MXEventDecryptionResult?
|
||||
suspend fun decryptEvent(event: Event, timeline: String): MXEventDecryptionResult?
|
||||
|
||||
/**
|
||||
* Handle a key event.
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
package im.vector.matrix.android.internal.crypto.algorithms
|
||||
|
||||
import arrow.core.Try
|
||||
import im.vector.matrix.android.api.MatrixCallback
|
||||
import im.vector.matrix.android.api.session.events.model.Content
|
||||
|
||||
|
@ -31,7 +32,7 @@ internal interface IMXEncrypting {
|
|||
* @param eventContent the content of the event.
|
||||
* @param eventType the type of the event.
|
||||
* @param userIds the room members the event will be sent to.
|
||||
* @param callback the asynchronous callback
|
||||
* @return the encrypted content wrapped by [Try]
|
||||
*/
|
||||
fun encryptEventContent(eventContent: Content, eventType: String, userIds: List<String>, callback: MatrixCallback<Content>)
|
||||
suspend fun encryptEventContent(eventContent: Content, eventType: String, userIds: List<String>): Try<Content>
|
||||
}
|
||||
|
|
|
@ -18,26 +18,19 @@
|
|||
package im.vector.matrix.android.internal.crypto.algorithms.megolm
|
||||
|
||||
import android.text.TextUtils
|
||||
import im.vector.matrix.android.api.MatrixCallback
|
||||
import arrow.core.Try
|
||||
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.events.model.Event
|
||||
import im.vector.matrix.android.api.session.events.model.EventType
|
||||
import im.vector.matrix.android.api.session.events.model.toModel
|
||||
import im.vector.matrix.android.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.*
|
||||
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.algorithms.IMXDecrypting
|
||||
import im.vector.matrix.android.internal.crypto.algorithms.MXDecryptionResult
|
||||
import im.vector.matrix.android.internal.crypto.keysbackup.KeysBackup
|
||||
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.MXUsersDevicesMap
|
||||
import im.vector.matrix.android.internal.crypto.model.event.EncryptedEventContent
|
||||
import im.vector.matrix.android.internal.crypto.model.event.RoomKeyContent
|
||||
|
@ -69,7 +62,7 @@ internal class MXMegolmDecryption(private val credentials: Credentials,
|
|||
private var pendingEvents: MutableMap<String /* senderKey|sessionId */, MutableMap<String /* timelineId */, MutableList<Event>>> = HashMap()
|
||||
|
||||
@Throws(MXDecryptionException::class)
|
||||
override fun decryptEvent(event: Event, timeline: String): MXEventDecryptionResult? {
|
||||
override suspend fun decryptEvent(event: Event, timeline: String): MXEventDecryptionResult? {
|
||||
return decryptEvent(event, timeline, true)
|
||||
}
|
||||
|
||||
|
@ -78,7 +71,7 @@ internal class MXMegolmDecryption(private val credentials: Credentials,
|
|||
val encryptedEventContent = event.content.toModel<EncryptedEventContent>()!!
|
||||
if (TextUtils.isEmpty(encryptedEventContent.senderKey) || TextUtils.isEmpty(encryptedEventContent.sessionId) || TextUtils.isEmpty(encryptedEventContent.ciphertext)) {
|
||||
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
|
||||
|
@ -90,7 +83,6 @@ internal class MXMegolmDecryption(private val credentials: Credentials,
|
|||
} catch (e: MXDecryptionException) {
|
||||
cryptoError = e.cryptoError
|
||||
}
|
||||
|
||||
// the decryption succeeds
|
||||
if (decryptGroupMessageResult?.payload != null && cryptoError == null) {
|
||||
eventDecryptionResult = MXEventDecryptionResult()
|
||||
|
@ -105,9 +97,8 @@ internal class MXMegolmDecryption(private val credentials: Credentials,
|
|||
eventDecryptionResult.forwardingCurve25519KeyChain = decryptGroupMessageResult.forwardingCurve25519KeyChain!!
|
||||
} else if (cryptoError != null) {
|
||||
if (cryptoError.isOlmError) {
|
||||
if (TextUtils.equals("UNKNOWN_MESSAGE_INDEX", cryptoError.message)) {
|
||||
if (MXCryptoError.UNKNOWN_MESSAGE_INDEX == cryptoError.message) {
|
||||
addEventToPendingList(event, timeline)
|
||||
|
||||
if (requestKeysOnFail) {
|
||||
requestKeysForEvent(event)
|
||||
}
|
||||
|
@ -176,20 +167,19 @@ internal class MXMegolmDecryption(private val credentials: Credentials,
|
|||
*/
|
||||
private fun addEventToPendingList(event: Event, timelineId: String) {
|
||||
val encryptedEventContent = event.content.toModel<EncryptedEventContent>()!!
|
||||
val pendingEventsKey = "${encryptedEventContent.senderKey}|${encryptedEventContent.sessionId}"
|
||||
|
||||
val k = "${encryptedEventContent.senderKey}|${encryptedEventContent.sessionId}"
|
||||
|
||||
if (!pendingEvents.containsKey(k)) {
|
||||
pendingEvents[k] = HashMap()
|
||||
if (!pendingEvents.containsKey(pendingEventsKey)) {
|
||||
pendingEvents[pendingEventsKey] = HashMap()
|
||||
}
|
||||
|
||||
if (!pendingEvents[k]!!.containsKey(timelineId)) {
|
||||
pendingEvents[k]!!.put(timelineId, ArrayList<Event>())
|
||||
if (!pendingEvents[pendingEventsKey]!!.containsKey(timelineId)) {
|
||||
pendingEvents[pendingEventsKey]!![timelineId] = ArrayList()
|
||||
}
|
||||
|
||||
if (pendingEvents[k]!![timelineId]!!.indexOf(event) < 0) {
|
||||
if (pendingEvents[pendingEventsKey]!![timelineId]!!.indexOf(event) < 0) {
|
||||
Timber.v("## addEventToPendingList() : add Event " + event.eventId + " in room id " + event.roomId)
|
||||
pendingEvents[k]!![timelineId]!!.add(event)
|
||||
pendingEvents[pendingEventsKey]!![timelineId]!!.add(event)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -213,7 +203,7 @@ internal class MXMegolmDecryption(private val credentials: Credentials,
|
|||
|
||||
if (event.getClearType() == EventType.FORWARDED_ROOM_KEY) {
|
||||
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>()!!
|
||||
|
||||
if (null == forwardedRoomKeyContent.forwardingCurve25519KeyChain) {
|
||||
|
@ -239,7 +229,7 @@ internal class MXMegolmDecryption(private val credentials: Credentials,
|
|||
keysClaimed["ed25519"] = forwardedRoomKeyContent.senderClaimedEd25519Key!!
|
||||
} else {
|
||||
Timber.v("## onRoomKeyEvent(), Adding key : roomId " + roomKeyContent.roomId + " sessionId " + roomKeyContent.sessionId
|
||||
+ " sessionKey " + roomKeyContent.sessionKey) // from " + event);
|
||||
+ " sessionKey " + roomKeyContent.sessionKey) // from " + event);
|
||||
|
||||
if (null == senderKey) {
|
||||
Timber.e("## onRoomKeyEvent() : key event has no sender key (not encrypted?)")
|
||||
|
@ -275,7 +265,8 @@ internal class MXMegolmDecryption(private val credentials: Credentials,
|
|||
* @param sessionId the session id
|
||||
*/
|
||||
override fun onNewSession(senderKey: String, sessionId: String) {
|
||||
val k = "$senderKey|$sessionId"
|
||||
//TODO see how to handle this
|
||||
/*val k = "$senderKey|$sessionId"
|
||||
|
||||
val pending = pendingEvents[k]
|
||||
|
||||
|
@ -309,6 +300,7 @@ internal class MXMegolmDecryption(private val credentials: Credentials,
|
|||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
override fun hasKeysForKeyRequest(request: IncomingRoomKeyRequest): Boolean {
|
||||
|
@ -323,70 +315,46 @@ internal class MXMegolmDecryption(private val credentials: Credentials,
|
|||
}
|
||||
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)
|
||||
deviceListManager
|
||||
.downloadKeys(listOf(userId), false)
|
||||
.flatMap {
|
||||
val deviceId = request.deviceId
|
||||
val deviceInfo = cryptoStore.getUserDevice(deviceId!!, userId)
|
||||
if (deviceInfo == null) {
|
||||
throw RuntimeException()
|
||||
} else {
|
||||
val devicesByUser = HashMap<String, List<MXDeviceInfo>>()
|
||||
devicesByUser[userId] = ArrayList(Arrays.asList(deviceInfo))
|
||||
ensureOlmSessionsForDevicesAction
|
||||
.handle(devicesByUser)
|
||||
.flatMap {
|
||||
val body = request.requestBody
|
||||
val olmSessionResult = it.getObject(deviceId, userId)
|
||||
if (olmSessionResult?.mSessionId == null) {
|
||||
// no session with this device, probably because there
|
||||
// were no one-time keys.
|
||||
Try.just(Unit)
|
||||
}
|
||||
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)
|
||||
|
||||
if (null != deviceInfo) {
|
||||
val body = request.requestBody
|
||||
val payloadJson = HashMap<String, Any>()
|
||||
payloadJson["type"] = EventType.FORWARDED_ROOM_KEY
|
||||
payloadJson["content"] = inboundGroupSession!!.exportKeys()!!
|
||||
|
||||
val devicesByUser = HashMap<String, List<MXDeviceInfo>>()
|
||||
devicesByUser[userId] = ArrayList(Arrays.asList(deviceInfo))
|
||||
val encodedPayload = messageEncrypter.encryptMessage(payloadJson, Arrays.asList(deviceInfo))
|
||||
val sendToDeviceMap = MXUsersDevicesMap<Any>()
|
||||
sendToDeviceMap.setObject(encodedPayload, userId, deviceId)
|
||||
Timber.v("## shareKeysWithDevice() : sending to $userId:$deviceId")
|
||||
val sendToDeviceParams = SendToDeviceTask.Params(EventType.ENCRYPTED, sendToDeviceMap)
|
||||
sendToDeviceTask.execute(sendToDeviceParams)
|
||||
}
|
||||
|
||||
ensureOlmSessionsForDevicesAction.handle(devicesByUser, object : MatrixCallback<MXUsersDevicesMap<MXOlmSessionResult>> {
|
||||
override fun onSuccess(data: MXUsersDevicesMap<MXOlmSessionResult>) {
|
||||
val olmSessionResult = data.getObject(deviceId, userId)
|
||||
|
||||
if (olmSessionResult?.mSessionId == null) {
|
||||
// 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)
|
||||
|
||||
val payloadJson = HashMap<String, Any>()
|
||||
payloadJson["type"] = EventType.FORWARDED_ROOM_KEY
|
||||
payloadJson["content"] = inboundGroupSession!!.exportKeys()!!
|
||||
|
||||
val encodedPayload = messageEncrypter.encryptMessage(payloadJson, Arrays.asList(deviceInfo))
|
||||
val sendToDeviceMap = MXUsersDevicesMap<Any>()
|
||||
sendToDeviceMap.setObject(encodedPayload, userId, deviceId)
|
||||
Timber.v("## shareKeysWithDevice() : sending to $userId:$deviceId")
|
||||
|
||||
val sendToDeviceParams = SendToDeviceTask.Params(EventType.ENCRYPTED, sendToDeviceMap)
|
||||
sendToDeviceTask
|
||||
.execute(sendToDeviceParams)
|
||||
.fold(
|
||||
{
|
||||
Timber.e(it, "## shareKeysWithDevice() : sendToDevice $userId:$deviceId failed")
|
||||
}
|
||||
, {
|
||||
Timber.v("## shareKeysWithDevice() : sent to $userId:$deviceId")
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
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) {
|
||||
Timber.e(failure, "## shareKeysWithDevice() : downloadKeys $userId failed")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -24,7 +24,6 @@ import im.vector.matrix.android.internal.crypto.actions.EnsureOlmSessionsForDevi
|
|||
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.tasks.SendToDeviceTask
|
||||
import im.vector.matrix.android.internal.task.TaskExecutor
|
||||
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
|
||||
|
||||
internal class MXMegolmDecryptionFactory(private val mCredentials: Credentials,
|
||||
|
@ -35,8 +34,7 @@ internal class MXMegolmDecryptionFactory(private val mCredentials: Credentials,
|
|||
private val mEnsureOlmSessionsForDevicesAction: EnsureOlmSessionsForDevicesAction,
|
||||
private val mCryptoStore: IMXCryptoStore,
|
||||
private val mSendToDeviceTask: SendToDeviceTask,
|
||||
private val coroutineDispatchers: MatrixCoroutineDispatchers,
|
||||
private val mTaskExecutor: TaskExecutor) {
|
||||
private val coroutineDispatchers: MatrixCoroutineDispatchers) {
|
||||
|
||||
fun create(): MXMegolmDecryption {
|
||||
return MXMegolmDecryption(
|
||||
|
|
|
@ -19,13 +19,10 @@
|
|||
package im.vector.matrix.android.internal.crypto.algorithms.megolm
|
||||
|
||||
import android.text.TextUtils
|
||||
import im.vector.matrix.android.api.MatrixCallback
|
||||
import arrow.core.Try
|
||||
import im.vector.matrix.android.api.auth.data.Credentials
|
||||
import im.vector.matrix.android.api.failure.Failure
|
||||
import im.vector.matrix.android.api.session.crypto.MXCryptoError
|
||||
import im.vector.matrix.android.api.session.events.model.Content
|
||||
import im.vector.matrix.android.api.session.events.model.EventType
|
||||
import im.vector.matrix.android.internal.crypto.CryptoAsyncHelper
|
||||
import im.vector.matrix.android.internal.crypto.DeviceListManager
|
||||
import im.vector.matrix.android.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM
|
||||
import im.vector.matrix.android.internal.crypto.MXOlmDevice
|
||||
|
@ -34,15 +31,11 @@ import im.vector.matrix.android.internal.crypto.actions.MessageEncrypter
|
|||
import im.vector.matrix.android.internal.crypto.algorithms.IMXEncrypting
|
||||
import im.vector.matrix.android.internal.crypto.keysbackup.KeysBackup
|
||||
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.MXQueuedEncryption
|
||||
import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap
|
||||
import im.vector.matrix.android.internal.crypto.repository.WarnOnUnknownDeviceRepository
|
||||
import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
|
||||
import im.vector.matrix.android.internal.crypto.tasks.SendToDeviceTask
|
||||
import im.vector.matrix.android.internal.di.MoshiProvider
|
||||
import im.vector.matrix.android.internal.task.TaskExecutor
|
||||
import im.vector.matrix.android.internal.task.configureWith
|
||||
import im.vector.matrix.android.internal.util.convertToUTF8
|
||||
import timber.log.Timber
|
||||
import java.util.*
|
||||
|
@ -50,7 +43,6 @@ import java.util.*
|
|||
internal class MXMegolmEncryption(
|
||||
// The id of the room we will be sending to.
|
||||
private var roomId: String,
|
||||
|
||||
private val olmDevice: MXOlmDevice,
|
||||
private val keysBackup: KeysBackup,
|
||||
private val cryptoStore: IMXCryptoStore,
|
||||
|
@ -58,7 +50,6 @@ internal class MXMegolmEncryption(
|
|||
private val ensureOlmSessionsForDevicesAction: EnsureOlmSessionsForDevicesAction,
|
||||
private val credentials: Credentials,
|
||||
private val sendToDeviceTask: SendToDeviceTask,
|
||||
private val taskExecutor: TaskExecutor,
|
||||
private val messageEncrypter: MessageEncrypter,
|
||||
private val warnOnUnknownDevicesRepository: WarnOnUnknownDeviceRepository
|
||||
) : IMXEncrypting {
|
||||
|
@ -69,84 +60,20 @@ internal class MXMegolmEncryption(
|
|||
// case outboundSession.shareOperation will be non-null.)
|
||||
private var outboundSession: MXOutboundSessionInfo? = null
|
||||
|
||||
// true when there is an HTTP operation in progress
|
||||
private var shareOperationIsProgress: Boolean = false
|
||||
|
||||
private val _pendingEncryptions = ArrayList<MXQueuedEncryption>()
|
||||
|
||||
// Default rotation periods
|
||||
// TODO: Make it configurable via parameters
|
||||
// Session rotation periods
|
||||
private var sessionRotationPeriodMsgs: Int = 100
|
||||
private var sessionRotationPeriodMs: Int = 7 * 24 * 3600 * 1000
|
||||
|
||||
/**
|
||||
* @return a snapshot of the pending encryptions
|
||||
*/
|
||||
private val pendingEncryptions: List<MXQueuedEncryption>
|
||||
get() {
|
||||
val list = ArrayList<MXQueuedEncryption>()
|
||||
synchronized(_pendingEncryptions) {
|
||||
list.addAll(_pendingEncryptions)
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
override fun encryptEventContent(eventContent: Content,
|
||||
eventType: String,
|
||||
userIds: List<String>,
|
||||
callback: MatrixCallback<Content>) {
|
||||
// Queue the encryption request
|
||||
// It will be processed when everything is set up
|
||||
val queuedEncryption = MXQueuedEncryption()
|
||||
|
||||
queuedEncryption.eventContent = eventContent
|
||||
queuedEncryption.eventType = eventType
|
||||
queuedEncryption.apiCallback = callback
|
||||
|
||||
synchronized(_pendingEncryptions) {
|
||||
_pendingEncryptions.add(queuedEncryption)
|
||||
}
|
||||
|
||||
val t0 = System.currentTimeMillis()
|
||||
Timber.v("## encryptEventContent () starts")
|
||||
|
||||
getDevicesInRoom(userIds, object : MatrixCallback<MXUsersDevicesMap<MXDeviceInfo>> {
|
||||
|
||||
/**
|
||||
* A network error has been received while encrypting
|
||||
* @param failure the exception
|
||||
*/
|
||||
private fun dispatchFailure(failure: Throwable) {
|
||||
Timber.e(failure, "## encryptEventContent() : failure")
|
||||
val queuedEncryptions = pendingEncryptions
|
||||
|
||||
for (queuedEncryption in queuedEncryptions) {
|
||||
queuedEncryption.apiCallback?.onFailure(failure)
|
||||
override suspend fun encryptEventContent(eventContent: Content,
|
||||
eventType: String,
|
||||
userIds: List<String>): Try<Content> {
|
||||
return getDevicesInRoom(userIds)
|
||||
.flatMap { ensureOutboundSession(it) }
|
||||
.flatMap {
|
||||
encryptContent(it, eventType, eventContent)
|
||||
}
|
||||
|
||||
synchronized(_pendingEncryptions) {
|
||||
_pendingEncryptions.removeAll(queuedEncryptions)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSuccess(devicesInRoom: MXUsersDevicesMap<MXDeviceInfo>) {
|
||||
ensureOutboundSession(devicesInRoom, object : MatrixCallback<MXOutboundSessionInfo> {
|
||||
override fun onSuccess(data: MXOutboundSessionInfo) {
|
||||
Timber.v("## encryptEventContent () processPendingEncryptions after " + (System.currentTimeMillis() - t0) + "ms")
|
||||
processPendingEncryptions(data)
|
||||
}
|
||||
|
||||
override fun onFailure(failure: Throwable) {
|
||||
dispatchFailure(failure)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun onFailure(failure: Throwable) {
|
||||
dispatchFailure(failure)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -161,7 +88,7 @@ internal class MXMegolmEncryption(
|
|||
keysClaimedMap["ed25519"] = olmDevice.deviceEd25519Key!!
|
||||
|
||||
olmDevice.addInboundGroupSession(sessionId!!, olmDevice.getSessionKey(sessionId)!!, roomId, olmDevice.deviceCurve25519Key!!,
|
||||
ArrayList(), keysClaimedMap, false)
|
||||
ArrayList(), keysClaimedMap, false)
|
||||
|
||||
keysBackup.maybeBackupKeys()
|
||||
|
||||
|
@ -172,62 +99,33 @@ internal class MXMegolmEncryption(
|
|||
* Ensure the outbound session
|
||||
*
|
||||
* @param devicesInRoom the devices list
|
||||
* @param callback the asynchronous callback.
|
||||
*/
|
||||
private fun ensureOutboundSession(devicesInRoom: MXUsersDevicesMap<MXDeviceInfo>, callback: MatrixCallback<MXOutboundSessionInfo>?) {
|
||||
private suspend fun ensureOutboundSession(devicesInRoom: MXUsersDevicesMap<MXDeviceInfo>): Try<MXOutboundSessionInfo> {
|
||||
var session = outboundSession
|
||||
|
||||
if (null == session
|
||||
// Need to make a brand new session?
|
||||
|| session.needsRotation(sessionRotationPeriodMsgs, sessionRotationPeriodMs)
|
||||
// Determine if we have shared with anyone we shouldn't have
|
||||
|| session.sharedWithTooManyDevices(devicesInRoom)) {
|
||||
if (session == null
|
||||
// Need to make a brand new session?
|
||||
|| session.needsRotation(sessionRotationPeriodMsgs, sessionRotationPeriodMs)
|
||||
// Determine if we have shared with anyone we shouldn't have
|
||||
|| session.sharedWithTooManyDevices(devicesInRoom)) {
|
||||
session = prepareNewSessionInRoom()
|
||||
outboundSession = session
|
||||
}
|
||||
|
||||
if (shareOperationIsProgress) {
|
||||
Timber.v("## ensureOutboundSessionInRoom() : already in progress")
|
||||
// Key share already in progress
|
||||
return
|
||||
}
|
||||
|
||||
val fSession = session
|
||||
|
||||
val safeSession = session
|
||||
val shareMap = HashMap<String, MutableList<MXDeviceInfo>>()/* userId */
|
||||
|
||||
val userIds = devicesInRoom.userIds
|
||||
|
||||
for (userId in userIds) {
|
||||
val deviceIds = devicesInRoom.getUserDeviceIds(userId)
|
||||
|
||||
for (deviceId in deviceIds!!) {
|
||||
val deviceInfo = devicesInRoom.getObject(deviceId, userId)
|
||||
|
||||
if (null == fSession.mSharedWithDevices.getObject(deviceId, userId)) {
|
||||
if (null == safeSession.sharedWithDevices.getObject(deviceId, userId)) {
|
||||
if (!shareMap.containsKey(userId)) {
|
||||
shareMap[userId] = ArrayList()
|
||||
}
|
||||
|
||||
shareMap[userId]!!.add(deviceInfo)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
shareKey(fSession, shareMap, object : MatrixCallback<Unit> {
|
||||
override fun onSuccess(data: Unit) {
|
||||
shareOperationIsProgress = false
|
||||
callback?.onSuccess(fSession)
|
||||
}
|
||||
|
||||
override fun onFailure(failure: Throwable) {
|
||||
Timber.e("## ensureOutboundSessionInRoom() : shareKey onFailure")
|
||||
|
||||
callback?.onFailure(failure)
|
||||
shareOperationIsProgress = false
|
||||
}
|
||||
})
|
||||
|
||||
return shareKey(safeSession, shareMap).map { safeSession!! }
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -235,55 +133,33 @@ internal class MXMegolmEncryption(
|
|||
*
|
||||
* @param session the session info
|
||||
* @param devicesByUsers the devices map
|
||||
* @param callback the asynchronous callback
|
||||
*/
|
||||
private fun shareKey(session: MXOutboundSessionInfo,
|
||||
devicesByUsers: MutableMap<String, MutableList<MXDeviceInfo>>,
|
||||
callback: MatrixCallback<Unit>?) {
|
||||
private suspend fun shareKey(session: MXOutboundSessionInfo,
|
||||
devicesByUsers: Map<String, List<MXDeviceInfo>>): Try<Unit> {
|
||||
// nothing to send, the task is done
|
||||
if (0 == devicesByUsers.size) {
|
||||
if (devicesByUsers.isEmpty()) {
|
||||
Timber.v("## shareKey() : nothing more to do")
|
||||
|
||||
if (null != callback) {
|
||||
CryptoAsyncHelper.getUiHandler().post { callback.onSuccess(Unit) }
|
||||
}
|
||||
|
||||
return
|
||||
return Try.just(Unit)
|
||||
}
|
||||
|
||||
// reduce the map size to avoid request timeout when there are too many devices (Users size * devices per user)
|
||||
val subMap = HashMap<String, List<MXDeviceInfo>>()
|
||||
|
||||
val userIds = ArrayList<String>()
|
||||
var devicesCount = 0
|
||||
|
||||
for (userId in devicesByUsers.keys) {
|
||||
val devicesList = devicesByUsers[userId]
|
||||
|
||||
userIds.add(userId)
|
||||
subMap[userId] = devicesList!!
|
||||
|
||||
devicesCount += devicesList.size
|
||||
|
||||
if (devicesCount > 100) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
Timber.v("## shareKey() ; userId $userIds")
|
||||
shareUserDevicesKey(session, subMap, object : MatrixCallback<Unit> {
|
||||
override fun onSuccess(data: Unit) {
|
||||
for (userId in userIds) {
|
||||
devicesByUsers.remove(userId)
|
||||
return shareUserDevicesKey(session, subMap)
|
||||
.flatMap {
|
||||
val remainingDevices = devicesByUsers.filterKeys { userIds.contains(it) }
|
||||
shareKey(session, remainingDevices)
|
||||
}
|
||||
shareKey(session, devicesByUsers, callback)
|
||||
}
|
||||
|
||||
override fun onFailure(failure: Throwable) {
|
||||
Timber.e(failure, "## shareKey() ; userIds " + userIds + " failed")
|
||||
callback?.onFailure(failure)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -293,16 +169,15 @@ internal class MXMegolmEncryption(
|
|||
* @param devicesByUser the devices map
|
||||
* @param callback the asynchronous callback
|
||||
*/
|
||||
private fun shareUserDevicesKey(session: MXOutboundSessionInfo,
|
||||
devicesByUser: Map<String, List<MXDeviceInfo>>,
|
||||
callback: MatrixCallback<Unit>?) {
|
||||
val sessionKey = olmDevice.getSessionKey(session.mSessionId)
|
||||
val chainIndex = olmDevice.getMessageIndex(session.mSessionId)
|
||||
private suspend fun shareUserDevicesKey(session: MXOutboundSessionInfo,
|
||||
devicesByUser: Map<String, List<MXDeviceInfo>>): Try<Unit> {
|
||||
val sessionKey = olmDevice.getSessionKey(session.sessionId)
|
||||
val chainIndex = olmDevice.getMessageIndex(session.sessionId)
|
||||
|
||||
val submap = HashMap<String, Any>()
|
||||
submap["algorithm"] = MXCRYPTO_ALGORITHM_MEGOLM
|
||||
submap["room_id"] = roomId
|
||||
submap["session_id"] = session.mSessionId
|
||||
submap["session_id"] = session.sessionId
|
||||
submap["session_key"] = sessionKey!!
|
||||
submap["chain_index"] = chainIndex
|
||||
|
||||
|
@ -310,56 +185,48 @@ internal class MXMegolmEncryption(
|
|||
payload["type"] = EventType.ROOM_KEY
|
||||
payload["content"] = submap
|
||||
|
||||
val t0 = System.currentTimeMillis()
|
||||
var t0 = System.currentTimeMillis()
|
||||
Timber.v("## shareUserDevicesKey() : starts")
|
||||
|
||||
ensureOlmSessionsForDevicesAction.handle(devicesByUser, object : MatrixCallback<MXUsersDevicesMap<MXOlmSessionResult>> {
|
||||
override fun onSuccess(data: MXUsersDevicesMap<MXOlmSessionResult>) {
|
||||
Timber.v("## shareUserDevicesKey() : ensureOlmSessionsForDevices succeeds after "
|
||||
+ (System.currentTimeMillis() - t0) + " ms")
|
||||
val contentMap = MXUsersDevicesMap<Any>()
|
||||
|
||||
var haveTargets = false
|
||||
val userIds = data.userIds
|
||||
|
||||
for (userId in userIds) {
|
||||
val devicesToShareWith = devicesByUser[userId]
|
||||
|
||||
for ((deviceID) in devicesToShareWith!!) {
|
||||
|
||||
val sessionResult = data.getObject(deviceID, userId)
|
||||
|
||||
if (null == sessionResult || null == sessionResult.mSessionId) {
|
||||
// no session with this device, probably because there
|
||||
// were no one-time keys.
|
||||
//
|
||||
// we could send them a to_device message anyway, as a
|
||||
// signal that they have missed out on the key sharing
|
||||
// message because of the lack of keys, but there's not
|
||||
// much point in that really; it will mostly serve to clog
|
||||
// up to_device inboxes.
|
||||
//
|
||||
// ensureOlmSessionsForUsers has already done the logging,
|
||||
// so just skip it.
|
||||
continue
|
||||
return ensureOlmSessionsForDevicesAction.handle(devicesByUser)
|
||||
.flatMap {
|
||||
Timber.v("## shareUserDevicesKey() : ensureOlmSessionsForDevices succeeds after "
|
||||
+ (System.currentTimeMillis() - t0) + " ms")
|
||||
val contentMap = MXUsersDevicesMap<Any>()
|
||||
var haveTargets = false
|
||||
val userIds = it.userIds
|
||||
for (userId in userIds) {
|
||||
val devicesToShareWith = devicesByUser[userId]
|
||||
for ((deviceID) in devicesToShareWith!!) {
|
||||
val sessionResult = it.getObject(deviceID, userId)
|
||||
if (sessionResult?.mSessionId == null) {
|
||||
// no session with this device, probably because there
|
||||
// were no one-time keys.
|
||||
//
|
||||
// we could send them a to_device message anyway, as a
|
||||
// signal that they have missed out on the key sharing
|
||||
// message because of the lack of keys, but there's not
|
||||
// much point in that really; it will mostly serve to clog
|
||||
// up to_device inboxes.
|
||||
//
|
||||
// ensureOlmSessionsForUsers has already done the logging,
|
||||
// so just skip it.
|
||||
continue
|
||||
}
|
||||
Timber.v("## shareUserDevicesKey() : Sharing keys with device $userId:$deviceID")
|
||||
//noinspection ArraysAsListWithZeroOrOneArgument,ArraysAsListWithZeroOrOneArgument
|
||||
contentMap.setObject(messageEncrypter.encryptMessage(payload, Arrays.asList(sessionResult.mDevice)), userId, deviceID)
|
||||
haveTargets = true
|
||||
}
|
||||
|
||||
Timber.v("## shareUserDevicesKey() : Sharing keys with device $userId:$deviceID")
|
||||
//noinspection ArraysAsListWithZeroOrOneArgument,ArraysAsListWithZeroOrOneArgument
|
||||
contentMap.setObject(messageEncrypter.encryptMessage(payload, Arrays.asList(sessionResult.mDevice)), userId, deviceID)
|
||||
haveTargets = true
|
||||
}
|
||||
}
|
||||
|
||||
if (haveTargets) {
|
||||
val t0 = System.currentTimeMillis()
|
||||
Timber.v("## shareUserDevicesKey() : has target")
|
||||
|
||||
sendToDeviceTask.configureWith(SendToDeviceTask.Params(EventType.ENCRYPTED, contentMap))
|
||||
.dispatchTo(object : MatrixCallback<Unit> {
|
||||
override fun onSuccess(data: Unit) {
|
||||
if (haveTargets) {
|
||||
t0 = System.currentTimeMillis()
|
||||
Timber.v("## shareUserDevicesKey() : has target")
|
||||
val sendToDeviceParams = SendToDeviceTask.Params(EventType.ENCRYPTED, contentMap)
|
||||
sendToDeviceTask.execute(sendToDeviceParams)
|
||||
.map {
|
||||
Timber.v("## shareUserDevicesKey() : sendToDevice succeeds after "
|
||||
+ (System.currentTimeMillis() - t0) + " ms")
|
||||
+ (System.currentTimeMillis() - t0) + " ms")
|
||||
|
||||
// Add the devices we have shared with to session.sharedWithDevices.
|
||||
// we deliberately iterate over devicesByUser (ie, the devices we
|
||||
|
@ -368,80 +235,45 @@ internal class MXMegolmEncryption(
|
|||
// for dead devices on every message.
|
||||
for (userId in devicesByUser.keys) {
|
||||
val devicesToShareWith = devicesByUser[userId]
|
||||
|
||||
for ((deviceId) in devicesToShareWith!!) {
|
||||
session.mSharedWithDevices.setObject(chainIndex, userId, deviceId)
|
||||
session.sharedWithDevices.setObject(chainIndex, userId, deviceId)
|
||||
}
|
||||
}
|
||||
|
||||
CryptoAsyncHelper.getUiHandler().post {
|
||||
callback?.onSuccess(Unit)
|
||||
}
|
||||
Unit
|
||||
}
|
||||
|
||||
override fun onFailure(failure: Throwable) {
|
||||
Timber.e(failure, "## shareUserDevicesKey() : sendToDevice")
|
||||
|
||||
callback?.onFailure(failure)
|
||||
}
|
||||
})
|
||||
.executeBy(taskExecutor)
|
||||
} else {
|
||||
Timber.v("## shareUserDevicesKey() : no need to sharekey")
|
||||
|
||||
if (null != callback) {
|
||||
CryptoAsyncHelper.getUiHandler().post { callback.onSuccess(Unit) }
|
||||
} else {
|
||||
Timber.v("## shareUserDevicesKey() : no need to sharekey")
|
||||
Try.just(Unit)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFailure(failure: Throwable) {
|
||||
Timber.e(failure, "## shareUserDevicesKey() : ensureOlmSessionsForDevices failed")
|
||||
|
||||
callback?.onFailure(failure)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* process the pending encryptions
|
||||
*/
|
||||
private fun processPendingEncryptions(session: MXOutboundSessionInfo?) {
|
||||
if (null != session) {
|
||||
val queuedEncryptions = pendingEncryptions
|
||||
private suspend fun encryptContent(session: MXOutboundSessionInfo, eventType: String, eventContent: Content) = Try<Content> {
|
||||
// Everything is in place, encrypt all pending events
|
||||
val payloadJson = HashMap<String, Any>()
|
||||
payloadJson["room_id"] = roomId
|
||||
payloadJson["type"] = eventType
|
||||
payloadJson["content"] = eventContent
|
||||
|
||||
// Everything is in place, encrypt all pending events
|
||||
for (queuedEncryption in queuedEncryptions) {
|
||||
val payloadJson = HashMap<String, Any>()
|
||||
// Get canonical Json from
|
||||
|
||||
payloadJson["room_id"] = roomId
|
||||
payloadJson["type"] = queuedEncryption.eventType!!
|
||||
payloadJson["content"] = queuedEncryption.eventContent!!
|
||||
val payloadString = convertToUTF8(MoshiProvider.getCanonicalJson(Map::class.java, payloadJson))
|
||||
val ciphertext = olmDevice.encryptGroupMessage(session.sessionId, payloadString!!)
|
||||
|
||||
// Get canonical Json from
|
||||
val map = HashMap<String, Any>()
|
||||
map["algorithm"] = MXCRYPTO_ALGORITHM_MEGOLM
|
||||
map["sender_key"] = olmDevice.deviceCurve25519Key!!
|
||||
map["ciphertext"] = ciphertext!!
|
||||
map["session_id"] = session.sessionId
|
||||
|
||||
val payloadString = convertToUTF8(MoshiProvider.getCanonicalJson(Map::class.java, payloadJson))
|
||||
val ciphertext = olmDevice.encryptGroupMessage(session.mSessionId, payloadString!!)
|
||||
|
||||
val map = HashMap<String, Any>()
|
||||
map["algorithm"] = MXCRYPTO_ALGORITHM_MEGOLM
|
||||
map["sender_key"] = olmDevice.deviceCurve25519Key!!
|
||||
map["ciphertext"] = ciphertext!!
|
||||
map["session_id"] = session.mSessionId
|
||||
|
||||
// Include our device ID so that recipients can send us a
|
||||
// m.new_device message if they don't have our session key.
|
||||
map["device_id"] = credentials.deviceId!!
|
||||
|
||||
CryptoAsyncHelper.getUiHandler().post { queuedEncryption.apiCallback?.onSuccess(map) }
|
||||
|
||||
session.mUseCount++
|
||||
}
|
||||
|
||||
synchronized(_pendingEncryptions) {
|
||||
_pendingEncryptions.removeAll(queuedEncryptions)
|
||||
}
|
||||
}
|
||||
// Include our device ID so that recipients can send us a
|
||||
// m.new_device message if they don't have our session key.
|
||||
map["device_id"] = credentials.deviceId!!
|
||||
session.useCount++
|
||||
map
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -451,65 +283,47 @@ internal class MXMegolmEncryption(
|
|||
* @param userIds the user ids whose devices must be checked.
|
||||
* @param callback the asynchronous callback
|
||||
*/
|
||||
private fun getDevicesInRoom(userIds: List<String>, callback: MatrixCallback<MXUsersDevicesMap<MXDeviceInfo>>) {
|
||||
private suspend fun getDevicesInRoom(userIds: List<String>): Try<MXUsersDevicesMap<MXDeviceInfo>> {
|
||||
// We are happy to use a cached version here: we assume that if we already
|
||||
// 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
|
||||
// an m.new_device.
|
||||
deviceListManager.downloadKeys(userIds, false, object : MatrixCallback<MXUsersDevicesMap<MXDeviceInfo>> {
|
||||
override fun onSuccess(data: MXUsersDevicesMap<MXDeviceInfo>) {
|
||||
val encryptToVerifiedDevicesOnly = cryptoStore.getGlobalBlacklistUnverifiedDevices()
|
||||
|| cryptoStore.getRoomsListBlacklistUnverifiedDevices().contains(roomId)
|
||||
return deviceListManager
|
||||
.downloadKeys(userIds, false)
|
||||
.map {
|
||||
val encryptToVerifiedDevicesOnly = cryptoStore.getGlobalBlacklistUnverifiedDevices()
|
||||
|| cryptoStore.getRoomsListBlacklistUnverifiedDevices().contains(roomId)
|
||||
|
||||
val devicesInRoom = MXUsersDevicesMap<MXDeviceInfo>()
|
||||
val unknownDevices = MXUsersDevicesMap<MXDeviceInfo>()
|
||||
val devicesInRoom = MXUsersDevicesMap<MXDeviceInfo>()
|
||||
val unknownDevices = MXUsersDevicesMap<MXDeviceInfo>()
|
||||
|
||||
for (userId in data.userIds) {
|
||||
val deviceIds = data.getUserDeviceIds(userId)
|
||||
for (userId in it.userIds) {
|
||||
val deviceIds = it.getUserDeviceIds(userId)
|
||||
|
||||
for (deviceId in deviceIds!!) {
|
||||
val deviceInfo = data.getObject(deviceId, userId)
|
||||
for (deviceId in deviceIds!!) {
|
||||
val deviceInfo = it.getObject(deviceId, userId)
|
||||
if (warnOnUnknownDevicesRepository.warnOnUnknownDevices() && deviceInfo!!.isUnknown) {
|
||||
// The device is not yet known by the user
|
||||
unknownDevices.setObject(deviceInfo, userId, deviceId)
|
||||
continue
|
||||
}
|
||||
if (deviceInfo!!.isBlocked) {
|
||||
// Remove any blocked devices
|
||||
continue
|
||||
}
|
||||
|
||||
if (warnOnUnknownDevicesRepository.warnOnUnknownDevices() && deviceInfo!!.isUnknown) {
|
||||
// The device is not yet known by the user
|
||||
unknownDevices.setObject(deviceInfo, userId, deviceId)
|
||||
continue
|
||||
if (!deviceInfo.isVerified && encryptToVerifiedDevicesOnly) {
|
||||
continue
|
||||
}
|
||||
|
||||
if (TextUtils.equals(deviceInfo.identityKey(), olmDevice.deviceCurve25519Key)) {
|
||||
// Don't bother sending to ourself
|
||||
continue
|
||||
}
|
||||
devicesInRoom.setObject(deviceInfo, userId, deviceId)
|
||||
}
|
||||
|
||||
if (deviceInfo!!.isBlocked) {
|
||||
// Remove any blocked devices
|
||||
continue
|
||||
}
|
||||
|
||||
if (!deviceInfo.isVerified && encryptToVerifiedDevicesOnly) {
|
||||
continue
|
||||
}
|
||||
|
||||
if (TextUtils.equals(deviceInfo.identityKey(), olmDevice.deviceCurve25519Key)) {
|
||||
// Don't bother sending to ourself
|
||||
continue
|
||||
}
|
||||
|
||||
devicesInRoom.setObject(deviceInfo, userId, deviceId)
|
||||
}
|
||||
devicesInRoom
|
||||
}
|
||||
|
||||
CryptoAsyncHelper.getUiHandler().post {
|
||||
// Check if any of these devices are not yet known to the user.
|
||||
// if so, warn the user so they can verify or ignore.
|
||||
if (unknownDevices.map.isNotEmpty()) {
|
||||
callback.onFailure(Failure.CryptoError(MXCryptoError(MXCryptoError.UNKNOWN_DEVICES_CODE,
|
||||
MXCryptoError.UNABLE_TO_ENCRYPT, MXCryptoError.UNKNOWN_DEVICES_REASON, unknownDevices)))
|
||||
} else {
|
||||
callback.onSuccess(devicesInRoom)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFailure(failure: Throwable) {
|
||||
callback.onFailure(failure)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,7 +50,6 @@ internal class MXMegolmEncryptionFactory(
|
|||
mEnsureOlmSessionsForDevicesAction,
|
||||
mCredentials,
|
||||
mSendToDeviceTask,
|
||||
mTaskExecutor,
|
||||
mMessageEncrypter,
|
||||
mWarnOnUnknownDevicesRepository)
|
||||
}
|
||||
|
|
|
@ -23,23 +23,23 @@ import timber.log.Timber
|
|||
|
||||
internal class MXOutboundSessionInfo(
|
||||
// The id of the session
|
||||
val mSessionId: String) {
|
||||
val sessionId: String) {
|
||||
// When the session was created
|
||||
private val mCreationTime = System.currentTimeMillis()
|
||||
private val creationTime = System.currentTimeMillis()
|
||||
|
||||
// Number of times this session has been used
|
||||
var mUseCount: Int = 0
|
||||
var useCount: Int = 0
|
||||
|
||||
// Devices with which we have shared the session key
|
||||
// userId -> {deviceId -> msgindex}
|
||||
val mSharedWithDevices: MXUsersDevicesMap<Int> = MXUsersDevicesMap()
|
||||
val sharedWithDevices: MXUsersDevicesMap<Int> = MXUsersDevicesMap()
|
||||
|
||||
fun needsRotation(rotationPeriodMsgs: Int, rotationPeriodMs: Int): Boolean {
|
||||
var needsRotation = false
|
||||
val sessionLifetime = System.currentTimeMillis() - mCreationTime
|
||||
val sessionLifetime = System.currentTimeMillis() - creationTime
|
||||
|
||||
if (mUseCount >= rotationPeriodMsgs || sessionLifetime >= rotationPeriodMs) {
|
||||
Timber.v("## needsRotation() : Rotating megolm session after " + mUseCount + ", " + sessionLifetime + "ms")
|
||||
if (useCount >= rotationPeriodMsgs || sessionLifetime >= rotationPeriodMs) {
|
||||
Timber.v("## needsRotation() : Rotating megolm session after " + useCount + ", " + sessionLifetime + "ms")
|
||||
needsRotation = true
|
||||
}
|
||||
|
||||
|
@ -53,7 +53,7 @@ internal class MXOutboundSessionInfo(
|
|||
* @return true if we have shared the session with devices which aren't in devicesInRoom.
|
||||
*/
|
||||
fun sharedWithTooManyDevices(devicesInRoom: MXUsersDevicesMap<MXDeviceInfo>): Boolean {
|
||||
val userIds = mSharedWithDevices.userIds
|
||||
val userIds = sharedWithDevices.userIds
|
||||
|
||||
for (userId in userIds) {
|
||||
if (null == devicesInRoom.getUserDeviceIds(userId)) {
|
||||
|
@ -61,7 +61,7 @@ internal class MXOutboundSessionInfo(
|
|||
return true
|
||||
}
|
||||
|
||||
val deviceIds = mSharedWithDevices.getUserDeviceIds(userId)
|
||||
val deviceIds = sharedWithDevices.getUserDeviceIds(userId)
|
||||
|
||||
for (deviceId in deviceIds!!) {
|
||||
if (null == devicesInRoom.getObject(deviceId, userId)) {
|
||||
|
|
|
@ -44,7 +44,7 @@ internal class MXOlmDecryption(
|
|||
: IMXDecrypting {
|
||||
|
||||
@Throws(MXDecryptionException::class)
|
||||
override fun decryptEvent(event: Event, timeline: String): MXEventDecryptionResult? {
|
||||
override suspend fun decryptEvent(event: Event, timeline: String): MXEventDecryptionResult? {
|
||||
val olmEventContent = event.content.toModel<OlmEventContent>()!!
|
||||
|
||||
if (null == olmEventContent.ciphertext) {
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
package im.vector.matrix.android.internal.crypto.algorithms.olm
|
||||
|
||||
import android.text.TextUtils
|
||||
import im.vector.matrix.android.api.MatrixCallback
|
||||
import arrow.core.Try
|
||||
import im.vector.matrix.android.api.session.events.model.Content
|
||||
import im.vector.matrix.android.api.session.events.model.toContent
|
||||
import im.vector.matrix.android.internal.crypto.DeviceListManager
|
||||
|
@ -28,12 +28,7 @@ import im.vector.matrix.android.internal.crypto.actions.EnsureOlmSessionsForUser
|
|||
import im.vector.matrix.android.internal.crypto.actions.MessageEncrypter
|
||||
import im.vector.matrix.android.internal.crypto.algorithms.IMXEncrypting
|
||||
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.MXUsersDevicesMap
|
||||
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.*
|
||||
|
||||
internal class MXOlmEncryption(
|
||||
|
@ -42,38 +37,28 @@ internal class MXOlmEncryption(
|
|||
private val cryptoStore: IMXCryptoStore,
|
||||
private val messageEncrypter: MessageEncrypter,
|
||||
private val deviceListManager: DeviceListManager,
|
||||
private val coroutineDispatchers: MatrixCoroutineDispatchers,
|
||||
private val ensureOlmSessionsForUsersAction: EnsureOlmSessionsForUsersAction)
|
||||
: IMXEncrypting {
|
||||
|
||||
override fun encryptEventContent(eventContent: Content,
|
||||
eventType: String,
|
||||
userIds: List<String>,
|
||||
callback: MatrixCallback<Content>) {
|
||||
override suspend fun encryptEventContent(eventContent: Content, eventType: String, userIds: List<String>): Try<Content> {
|
||||
// pick the list of recipients based on the membership list.
|
||||
//
|
||||
// TODO: there is a race condition here! What if a new user turns up
|
||||
CoroutineScope(coroutineDispatchers.crypto).launch {
|
||||
ensureSession(userIds, object : MatrixCallback<Unit> {
|
||||
override fun onSuccess(data: Unit) {
|
||||
return ensureSession(userIds)
|
||||
.map {
|
||||
val deviceInfos = ArrayList<MXDeviceInfo>()
|
||||
|
||||
for (userId in userIds) {
|
||||
val devices = cryptoStore.getUserDevices(userId)?.values ?: emptyList()
|
||||
|
||||
for (device in devices) {
|
||||
val key = device.identityKey()
|
||||
|
||||
if (TextUtils.equals(key, olmDevice.deviceCurve25519Key)) {
|
||||
// Don't bother setting up session to ourself
|
||||
continue
|
||||
}
|
||||
|
||||
if (device.isBlocked) {
|
||||
// Don't bother setting up sessions with blocked users
|
||||
continue
|
||||
}
|
||||
|
||||
deviceInfos.add(device)
|
||||
}
|
||||
}
|
||||
|
@ -84,37 +69,22 @@ internal class MXOlmEncryption(
|
|||
messageMap["content"] = eventContent
|
||||
|
||||
messageEncrypter.encryptMessage(messageMap, deviceInfos)
|
||||
|
||||
callback.onSuccess(messageMap.toContent()!!)
|
||||
messageMap.toContent()!!
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Ensure that the session
|
||||
*
|
||||
* @param users the user ids list
|
||||
* @param callback the asynchronous callback
|
||||
*/
|
||||
private fun ensureSession(users: List<String>, callback: MatrixCallback<Unit>?) {
|
||||
deviceListManager.downloadKeys(users, false, object : MatrixCallback<MXUsersDevicesMap<MXDeviceInfo>> {
|
||||
private suspend fun ensureSession(users: List<String>): Try<Unit> {
|
||||
return deviceListManager
|
||||
.downloadKeys(users, false)
|
||||
.flatMap { ensureOlmSessionsForUsersAction.handle(users) }
|
||||
.map { Unit }
|
||||
|
||||
override fun onSuccess(data: MXUsersDevicesMap<MXDeviceInfo>) {
|
||||
ensureOlmSessionsForUsersAction.handle(users, object : MatrixCallback<MXUsersDevicesMap<MXOlmSessionResult>> {
|
||||
override fun onSuccess(data: MXUsersDevicesMap<MXOlmSessionResult>) {
|
||||
callback?.onSuccess(Unit)
|
||||
}
|
||||
|
||||
override fun onFailure(failure: Throwable) {
|
||||
callback?.onFailure(failure)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun onFailure(failure: Throwable) {
|
||||
callback?.onFailure(failure)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,7 +37,6 @@ internal class MXOlmEncryptionFactory(private val mOlmDevice: MXOlmDevice,
|
|||
mCryptoStore,
|
||||
mMessageEncrypter,
|
||||
mDeviceListManager,
|
||||
coroutineDispatchers,
|
||||
mEnsureOlmSessionsForUsersAction)
|
||||
}
|
||||
}
|
|
@ -603,11 +603,11 @@ internal class RealmCryptoStore(private val enableFileEncryption: Boolean = fals
|
|||
}
|
||||
|
||||
override fun getOrAddOutgoingRoomKeyRequest(request: OutgoingRoomKeyRequest): OutgoingRoomKeyRequest? {
|
||||
if (request.mRequestBody == null) {
|
||||
if (request.requestBody == null) {
|
||||
return null
|
||||
}
|
||||
|
||||
val existingOne = getOutgoingRoomKeyRequest(request.mRequestBody!!)
|
||||
val existingOne = getOutgoingRoomKeyRequest(request.requestBody!!)
|
||||
|
||||
if (existingOne != null) {
|
||||
return existingOne
|
||||
|
@ -615,11 +615,11 @@ internal class RealmCryptoStore(private val enableFileEncryption: Boolean = fals
|
|||
|
||||
// Insert the request and return the one passed in parameter
|
||||
doRealmTransaction(realmConfiguration) {
|
||||
it.createObject(OutgoingRoomKeyRequestEntity::class.java, request.mRequestId).apply {
|
||||
putRequestBody(request.mRequestBody)
|
||||
putRecipients(request.mRecipients)
|
||||
cancellationTxnId = request.mCancellationTxnId
|
||||
state = request.mState.ordinal
|
||||
it.createObject(OutgoingRoomKeyRequestEntity::class.java, request.requestId).apply {
|
||||
putRequestBody(request.requestBody)
|
||||
putRecipients(request.recipients)
|
||||
cancellationTxnId = request.cancellationTxnId
|
||||
state = request.state.ordinal
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -638,11 +638,11 @@ internal class RealmCryptoStore(private val enableFileEncryption: Boolean = fals
|
|||
override fun updateOutgoingRoomKeyRequest(request: OutgoingRoomKeyRequest) {
|
||||
doRealmTransaction(realmConfiguration) {
|
||||
val obj = OutgoingRoomKeyRequestEntity().apply {
|
||||
requestId = request.mRequestId
|
||||
cancellationTxnId = request.mCancellationTxnId
|
||||
state = request.mState.ordinal
|
||||
putRecipients(request.mRecipients)
|
||||
putRequestBody(request.mRequestBody)
|
||||
requestId = request.requestId
|
||||
cancellationTxnId = request.cancellationTxnId
|
||||
state = request.state.ordinal
|
||||
putRecipients(request.recipients)
|
||||
putRequestBody(request.requestBody)
|
||||
}
|
||||
|
||||
it.insertOrUpdate(obj)
|
||||
|
|
|
@ -52,7 +52,7 @@ internal open class OutgoingRoomKeyRequestEntity(
|
|||
requestId!!,
|
||||
OutgoingRoomKeyRequest.RequestState.from(state)
|
||||
).apply {
|
||||
this.mCancellationTxnId = cancellationTxnId
|
||||
this.cancellationTxnId = cancellationTxnId
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -17,13 +17,13 @@
|
|||
package im.vector.matrix.android.internal.di
|
||||
|
||||
import android.content.Context
|
||||
import im.vector.matrix.android.internal.crypto.CryptoAsyncHelper
|
||||
import im.vector.matrix.android.internal.task.TaskExecutor
|
||||
import im.vector.matrix.android.internal.util.BackgroundDetectionObserver
|
||||
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.asCoroutineDispatcher
|
||||
import kotlinx.coroutines.android.asCoroutineDispatcher
|
||||
import org.koin.dsl.module.module
|
||||
import java.util.concurrent.Executors
|
||||
|
||||
|
||||
class MatrixModule(private val context: Context) {
|
||||
|
@ -35,10 +35,11 @@ class MatrixModule(private val context: Context) {
|
|||
}
|
||||
|
||||
single {
|
||||
val cryptoHandler = CryptoAsyncHelper.getDecryptBackgroundHandler()
|
||||
MatrixCoroutineDispatchers(io = Dispatchers.IO,
|
||||
computation = Dispatchers.IO,
|
||||
main = Dispatchers.Main,
|
||||
crypto = Executors.newSingleThreadExecutor().asCoroutineDispatcher()
|
||||
computation = Dispatchers.IO,
|
||||
main = Dispatchers.Main,
|
||||
crypto = cryptoHandler.asCoroutineDispatcher("crypto")
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -16,9 +16,11 @@
|
|||
|
||||
package im.vector.matrix.android.internal.session.room.timeline
|
||||
|
||||
import im.vector.matrix.android.api.failure.Failure
|
||||
import im.vector.matrix.android.api.session.crypto.CryptoService
|
||||
import im.vector.matrix.android.api.session.events.model.EventType
|
||||
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
||||
import im.vector.matrix.android.internal.crypto.MXDecryptionException
|
||||
import im.vector.matrix.android.internal.database.mapper.asDomain
|
||||
import im.vector.matrix.android.internal.database.model.EventEntity
|
||||
import im.vector.matrix.android.internal.session.room.members.SenderRoomMemberExtractor
|
||||
|
@ -44,6 +46,9 @@ internal class TimelineEventFactory(private val roomMemberExtractor: SenderRoomM
|
|||
event.setClearData(result)
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e)
|
||||
if (e is MXDecryptionException) {
|
||||
event.setCryptoError(e.cryptoError)
|
||||
}
|
||||
}
|
||||
}
|
||||
return TimelineEvent(
|
||||
|
|
|
@ -97,7 +97,6 @@ internal class RoomSyncHandler(private val monarchy: Monarchy,
|
|||
roomEntity.addStateEvents(roomSync.state.events, filterDuplicates = true, stateIndex = untimelinedStateIndex)
|
||||
|
||||
// Give info to crypto module
|
||||
// TODO Remove
|
||||
roomSync.state.events.forEach {
|
||||
mCrypto.onStateEvent(roomId, it)
|
||||
}
|
||||
|
|
|
@ -49,7 +49,9 @@ internal class SyncResponseHandler(private val roomSyncHandler: RoomSyncHandler,
|
|||
cryptoSyncHandler.onSyncCompleted(syncResponse, fromToken, isCatchingUp)
|
||||
}
|
||||
val isInitialSync = fromToken == null
|
||||
cryptoManager.start(isInitialSync)
|
||||
if (!cryptoManager.isStarted()) {
|
||||
cryptoManager.start(isInitialSync)
|
||||
}
|
||||
Timber.v("Finish handling sync in $measure ms")
|
||||
syncResponse
|
||||
}
|
||||
|
|
|
@ -45,15 +45,12 @@ class EncryptedItemFactory(
|
|||
|
||||
return when {
|
||||
EventType.ENCRYPTED == timelineEvent.root.getClearType() -> {
|
||||
val decrypted: MXEventDecryptionResult?
|
||||
try {
|
||||
decrypted = session.decryptEvent(timelineEvent.root, "TODO")
|
||||
} catch (e: MXDecryptionException) {
|
||||
val cryptoError = timelineEvent.root.mCryptoError
|
||||
val errorDescription =
|
||||
if (e.cryptoError?.code == MXCryptoError.UNKNOWN_INBOUND_SESSION_ID_ERROR_CODE) {
|
||||
if (cryptoError?.code == MXCryptoError.UNKNOWN_INBOUND_SESSION_ID_ERROR_CODE) {
|
||||
stringProvider.getString(R.string.notice_crypto_error_unkwown_inbound_session_id)
|
||||
} else {
|
||||
e.localizedMessage
|
||||
cryptoError?.message
|
||||
}
|
||||
|
||||
val message = stringProvider.getString(R.string.notice_crypto_unable_to_decrypt, errorDescription)
|
||||
|
@ -65,20 +62,6 @@ class EncryptedItemFactory(
|
|||
.noticeText(spannableStr)
|
||||
.avatarUrl(timelineEvent.senderAvatar)
|
||||
.memberName(timelineEvent.senderName)
|
||||
}
|
||||
|
||||
if (decrypted == null) {
|
||||
return null
|
||||
}
|
||||
if (decrypted.clearEvent == null) {
|
||||
return null
|
||||
}
|
||||
val adapter = MoshiProvider.providesMoshi().adapter(Event::class.java)
|
||||
val clearEvent = adapter.fromJsonValue(decrypted.clearEvent) ?: return null
|
||||
val decryptedTimelineEvent = timelineEvent.copy(root = clearEvent)
|
||||
|
||||
// Success
|
||||
return messageItemFactory.create(decryptedTimelineEvent, nextEvent, callback)
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue