forked from GitHub-Mirror/riotX-android
Crypto: start reworking threading - WIP (to squash)
This commit is contained in:
parent
3d50393b33
commit
e125862794
@ -25,7 +25,7 @@ import kotlin.random.Random
|
|||||||
|
|
||||||
internal class FakeGetContextOfEventTask(private val tokenChunkEventPersistor: TokenChunkEventPersistor) : GetContextOfEventTask {
|
internal class FakeGetContextOfEventTask(private val tokenChunkEventPersistor: TokenChunkEventPersistor) : GetContextOfEventTask {
|
||||||
|
|
||||||
override fun execute(params: GetContextOfEventTask.Params): Try<TokenChunkEventPersistor.Result> {
|
override suspend fun execute(params: GetContextOfEventTask.Params): Try<TokenChunkEventPersistor.Result> {
|
||||||
val fakeEvents = RoomDataHelper.createFakeListOfEvents(30)
|
val fakeEvents = RoomDataHelper.createFakeListOfEvents(30)
|
||||||
val tokenChunkEvent = FakeTokenChunkEvent(
|
val tokenChunkEvent = FakeTokenChunkEvent(
|
||||||
Random.nextLong(System.currentTimeMillis()).toString(),
|
Random.nextLong(System.currentTimeMillis()).toString(),
|
||||||
|
@ -23,7 +23,7 @@ import kotlin.random.Random
|
|||||||
|
|
||||||
internal class FakePaginationTask(private val tokenChunkEventPersistor: TokenChunkEventPersistor) : PaginationTask {
|
internal class FakePaginationTask(private val tokenChunkEventPersistor: TokenChunkEventPersistor) : PaginationTask {
|
||||||
|
|
||||||
override fun execute(params: PaginationTask.Params): Try<TokenChunkEventPersistor.Result> {
|
override suspend fun execute(params: PaginationTask.Params): Try<TokenChunkEventPersistor.Result> {
|
||||||
val fakeEvents = RoomDataHelper.createFakeListOfEvents(30)
|
val fakeEvents = RoomDataHelper.createFakeListOfEvents(30)
|
||||||
val tokenChunkEvent = FakeTokenChunkEvent(params.from, Random.nextLong(System.currentTimeMillis()).toString(), fakeEvents)
|
val tokenChunkEvent = FakeTokenChunkEvent(params.from, Random.nextLong(System.currentTimeMillis()).toString(), fakeEvents)
|
||||||
return tokenChunkEventPersistor.insertInDb(tokenChunkEvent, params.roomId, params.direction)
|
return tokenChunkEventPersistor.insertInDb(tokenChunkEvent, params.roomId, params.direction)
|
||||||
|
@ -21,6 +21,8 @@ package im.vector.matrix.android.internal.crypto
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.text.TextUtils
|
import android.text.TextUtils
|
||||||
|
import arrow.core.Try
|
||||||
|
import arrow.instances.`try`.applicativeError.handleError
|
||||||
import com.zhuinden.monarchy.Monarchy
|
import com.zhuinden.monarchy.Monarchy
|
||||||
import im.vector.matrix.android.api.MatrixCallback
|
import im.vector.matrix.android.api.MatrixCallback
|
||||||
import im.vector.matrix.android.api.auth.data.Credentials
|
import im.vector.matrix.android.api.auth.data.Credentials
|
||||||
@ -66,14 +68,16 @@ import im.vector.matrix.android.internal.session.room.members.LoadRoomMembersTas
|
|||||||
import im.vector.matrix.android.internal.session.room.members.RoomMembers
|
import im.vector.matrix.android.internal.session.room.members.RoomMembers
|
||||||
import im.vector.matrix.android.internal.session.sync.model.SyncResponse
|
import im.vector.matrix.android.internal.session.sync.model.SyncResponse
|
||||||
import im.vector.matrix.android.internal.task.TaskExecutor
|
import im.vector.matrix.android.internal.task.TaskExecutor
|
||||||
import im.vector.matrix.android.internal.task.TaskThread
|
|
||||||
import im.vector.matrix.android.internal.task.configureWith
|
import im.vector.matrix.android.internal.task.configureWith
|
||||||
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
|
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
import org.matrix.olm.OlmManager
|
import org.matrix.olm.OlmManager
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A `CryptoService` class instance manages the end-to-end crypto for a session.
|
* A `CryptoService` class instance manages the end-to-end crypto for a session.
|
||||||
@ -137,10 +141,10 @@ internal class CryptoManager(
|
|||||||
private val roomEncryptors: MutableMap<String, IMXEncrypting> = HashMap()
|
private val roomEncryptors: MutableMap<String, IMXEncrypting> = HashMap()
|
||||||
|
|
||||||
// the encryption is starting
|
// the encryption is starting
|
||||||
private var isStarting: Boolean = false
|
private var isStarting = AtomicBoolean(false)
|
||||||
|
|
||||||
// tell if the crypto is started
|
// tell if the crypto is started
|
||||||
private var isStarted: Boolean = false
|
private var isStarted = AtomicBoolean(false)
|
||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
//private val mNetworkListener = object : IMXNetworkEventListener {
|
//private val mNetworkListener = object : IMXNetworkEventListener {
|
||||||
@ -220,7 +224,7 @@ internal class CryptoManager(
|
|||||||
* @return true if the crypto is started
|
* @return true if the crypto is started
|
||||||
*/
|
*/
|
||||||
fun isStarted(): Boolean {
|
fun isStarted(): Boolean {
|
||||||
return isStarted
|
return isStarted.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -229,7 +233,7 @@ internal class CryptoManager(
|
|||||||
* @return true if the crypto is starting
|
* @return true if the crypto is starting
|
||||||
*/
|
*/
|
||||||
fun isStarting(): Boolean {
|
fun isStarting(): Boolean {
|
||||||
return isStarting
|
return isStarting.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -241,7 +245,7 @@ internal class CryptoManager(
|
|||||||
* @param isInitialSync true if it starts from an initial sync
|
* @param isInitialSync true if it starts from an initial sync
|
||||||
*/
|
*/
|
||||||
fun start(isInitialSync: Boolean) {
|
fun start(isInitialSync: Boolean) {
|
||||||
if (isStarting) {
|
if (isStarting.get()) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -254,44 +258,33 @@ internal class CryptoManager(
|
|||||||
// return
|
// return
|
||||||
//}
|
//}
|
||||||
|
|
||||||
isStarting = true
|
isStarting.set(true)
|
||||||
|
|
||||||
// Open the store
|
// Open the store
|
||||||
cryptoStore.open()
|
cryptoStore.open()
|
||||||
|
CoroutineScope(coroutineDispatchers.crypto).launch {
|
||||||
uploadDeviceKeys(object : MatrixCallback<KeysUploadResponse> {
|
uploadDeviceKeys()
|
||||||
private fun onError() {
|
.flatMap {
|
||||||
Handler().postDelayed({
|
oneTimeKeysUploader.maybeUploadOneTimeKeys()
|
||||||
|
}
|
||||||
|
.handleError {
|
||||||
|
Handler().postDelayed(
|
||||||
|
{
|
||||||
if (!isStarted()) {
|
if (!isStarted()) {
|
||||||
isStarting = false
|
isStarting.set(false)
|
||||||
start(isInitialSync)
|
start(isInitialSync)
|
||||||
}
|
}
|
||||||
}, 1000)
|
}, 1000
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
.fold(
|
||||||
override fun onSuccess(data: KeysUploadResponse) {
|
{
|
||||||
Timber.v("###########################################################")
|
Timber.e("Start failed: $it")
|
||||||
Timber.v("uploadDeviceKeys done for " + credentials.userId)
|
},
|
||||||
Timber.v(" - device id : " + credentials.deviceId)
|
{
|
||||||
Timber.v(" - ed25519 : " + olmDevice.deviceEd25519Key)
|
isStarting.set(false)
|
||||||
Timber.v(" - curve25519 : " + olmDevice.deviceCurve25519Key)
|
isStarted.set(true)
|
||||||
Timber.v(" - oneTimeKeys: " + oneTimeKeysUploader.mLastPublishedOneTimeKeys)
|
|
||||||
Timber.v("")
|
|
||||||
|
|
||||||
oneTimeKeysUploader.maybeUploadOneTimeKeys(object : MatrixCallback<Unit> {
|
|
||||||
override fun onSuccess(data: Unit) {
|
|
||||||
// TODO
|
|
||||||
//if (null != mNetworkConnectivityReceiver) {
|
|
||||||
// mNetworkConnectivityReceiver!!.removeEventListener(mNetworkListener)
|
|
||||||
//}
|
|
||||||
|
|
||||||
isStarting = false
|
|
||||||
isStarted = true
|
|
||||||
|
|
||||||
outgoingRoomKeyRequestManager.start()
|
outgoingRoomKeyRequestManager.start()
|
||||||
|
|
||||||
keysBackup.checkAndStartKeysBackup()
|
keysBackup.checkAndStartKeysBackup()
|
||||||
|
|
||||||
if (isInitialSync) {
|
if (isInitialSync) {
|
||||||
// refresh the devices list for each known room members
|
// refresh the devices list for each known room members
|
||||||
deviceListManager.invalidateAllDeviceLists()
|
deviceListManager.invalidateAllDeviceLists()
|
||||||
@ -300,19 +293,8 @@ internal class CryptoManager(
|
|||||||
incomingRoomKeyRequestManager.processReceivedRoomKeyRequests()
|
incomingRoomKeyRequestManager.processReceivedRoomKeyRequests()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
)
|
||||||
override fun onFailure(failure: Throwable) {
|
|
||||||
Timber.e(failure, "## start failed")
|
|
||||||
onError()
|
|
||||||
}
|
}
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onFailure(failure: Throwable) {
|
|
||||||
Timber.e(failure, "## start failed")
|
|
||||||
onError()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -320,9 +302,7 @@ internal class CryptoManager(
|
|||||||
*/
|
*/
|
||||||
fun close() {
|
fun close() {
|
||||||
olmDevice.release()
|
olmDevice.release()
|
||||||
|
|
||||||
cryptoStore.close()
|
cryptoStore.close()
|
||||||
|
|
||||||
outgoingRoomKeyRequestManager.stop()
|
outgoingRoomKeyRequestManager.stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -351,6 +331,7 @@ internal class CryptoManager(
|
|||||||
* @param syncResponse the syncResponse
|
* @param syncResponse the syncResponse
|
||||||
*/
|
*/
|
||||||
fun onSyncCompleted(syncResponse: SyncResponse) {
|
fun onSyncCompleted(syncResponse: SyncResponse) {
|
||||||
|
CoroutineScope(coroutineDispatchers.crypto).launch {
|
||||||
if (syncResponse.deviceLists != null) {
|
if (syncResponse.deviceLists != null) {
|
||||||
deviceListManager.handleDeviceListsChanges(syncResponse.deviceLists.changed, syncResponse.deviceLists.left)
|
deviceListManager.handleDeviceListsChanges(syncResponse.deviceLists.changed, syncResponse.deviceLists.left)
|
||||||
}
|
}
|
||||||
@ -358,7 +339,6 @@ internal class CryptoManager(
|
|||||||
val currentCount = syncResponse.deviceOneTimeKeysCount.signedCurve25519 ?: 0
|
val currentCount = syncResponse.deviceOneTimeKeysCount.signedCurve25519 ?: 0
|
||||||
oneTimeKeysUploader.updateOneTimeKeyCount(currentCount)
|
oneTimeKeysUploader.updateOneTimeKeyCount(currentCount)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isStarted()) {
|
if (isStarted()) {
|
||||||
// Make sure we process to-device messages before generating new one-time-keys #2782
|
// Make sure we process to-device messages before generating new one-time-keys #2782
|
||||||
deviceListManager.refreshOutdatedDeviceLists()
|
deviceListManager.refreshOutdatedDeviceLists()
|
||||||
@ -366,6 +346,7 @@ internal class CryptoManager(
|
|||||||
incomingRoomKeyRequestManager.processReceivedRoomKeyRequests()
|
incomingRoomKeyRequestManager.processReceivedRoomKeyRequests()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find a device by curve25519 identity key
|
* Find a device by curve25519 identity key
|
||||||
@ -398,7 +379,7 @@ internal class CryptoManager(
|
|||||||
/**
|
/**
|
||||||
* Set the devices as known
|
* Set the devices as known
|
||||||
*
|
*
|
||||||
* @param devices the devices. Note that the mVerified member of the devices in this list will not be updated by this method.
|
* @param devices the devices. Note that the verified member of the devices in this list will not be updated by this method.
|
||||||
* @param callback the asynchronous callback
|
* @param callback the asynchronous callback
|
||||||
*/
|
*/
|
||||||
override fun setDevicesKnown(devices: List<MXDeviceInfo>, callback: MatrixCallback<Unit>?) {
|
override fun setDevicesKnown(devices: List<MXDeviceInfo>, callback: MatrixCallback<Unit>?) {
|
||||||
@ -431,7 +412,7 @@ internal class CryptoManager(
|
|||||||
// assume if the device is either verified or blocked
|
// assume if the device is either verified or blocked
|
||||||
// it means that the device is known
|
// it means that the device is known
|
||||||
if (null != device && device.isUnknown) {
|
if (null != device && device.isUnknown) {
|
||||||
device.mVerified = MXDeviceInfo.DEVICE_VERIFICATION_UNVERIFIED
|
device.verified = MXDeviceInfo.DEVICE_VERIFICATION_UNVERIFIED
|
||||||
isUpdated = true
|
isUpdated = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -459,7 +440,6 @@ internal class CryptoManager(
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Configure a room to use encryption.
|
* Configure a room to use encryption.
|
||||||
* This method must be called in getEncryptingThreadHandler
|
|
||||||
*
|
*
|
||||||
* @param roomId the room id to enable encryption in.
|
* @param roomId the room id to enable encryption in.
|
||||||
* @param algorithm the encryption config for the room.
|
* @param algorithm the encryption config for the room.
|
||||||
@ -467,7 +447,7 @@ internal class CryptoManager(
|
|||||||
* @param membersId list of members to start tracking their devices
|
* @param membersId list of members to start tracking their devices
|
||||||
* @return true if the operation succeeds.
|
* @return true if the operation succeeds.
|
||||||
*/
|
*/
|
||||||
private 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
|
// If we already have encryption in this room, we should ignore this event
|
||||||
// (for now at least. Maybe we should alert the user somehow?)
|
// (for now at least. Maybe we should alert the user somehow?)
|
||||||
val existingAlgorithm = cryptoStore.getRoomAlgorithm(roomId)
|
val existingAlgorithm = cryptoStore.getRoomAlgorithm(roomId)
|
||||||
@ -578,18 +558,17 @@ internal class CryptoManager(
|
|||||||
// wait that the crypto is really started
|
// wait that the crypto is really started
|
||||||
if (!isStarted()) {
|
if (!isStarted()) {
|
||||||
Timber.v("## encryptEventContent() : wait after e2e init")
|
Timber.v("## encryptEventContent() : wait after e2e init")
|
||||||
|
|
||||||
start(false)
|
start(false)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
CoroutineScope(coroutineDispatchers.crypto).launch {
|
||||||
val userIds = getRoomUserIds(roomId)
|
val userIds = getRoomUserIds(roomId)
|
||||||
var alg = synchronized(roomEncryptors) {
|
var alg = synchronized(roomEncryptors) {
|
||||||
roomEncryptors[roomId]
|
roomEncryptors[roomId]
|
||||||
}
|
}
|
||||||
if (alg == null) {
|
if (alg == null) {
|
||||||
val algorithm = getEncryptionAlgorithm(roomId)
|
val algorithm = getEncryptionAlgorithm(roomId)
|
||||||
if (null != algorithm) {
|
if (algorithm != null) {
|
||||||
if (setEncryptionInRoom(roomId, algorithm, false, userIds)) {
|
if (setEncryptionInRoom(roomId, algorithm, false, userIds)) {
|
||||||
synchronized(roomEncryptors) {
|
synchronized(roomEncryptors) {
|
||||||
alg = roomEncryptors[roomId]
|
alg = roomEncryptors[roomId]
|
||||||
@ -597,15 +576,13 @@ internal class CryptoManager(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
val safeAlgorithm = alg
|
||||||
if (alg != null) {
|
if (safeAlgorithm != null) {
|
||||||
val t0 = System.currentTimeMillis()
|
val t0 = System.currentTimeMillis()
|
||||||
Timber.v("## encryptEventContent() starts")
|
Timber.v("## encryptEventContent() starts")
|
||||||
|
safeAlgorithm.encryptEventContent(eventContent, eventType, userIds, object : MatrixCallback<Content> {
|
||||||
alg!!.encryptEventContent(eventContent, eventType, userIds, object : MatrixCallback<Content> {
|
|
||||||
override fun onSuccess(data: Content) {
|
override fun onSuccess(data: Content) {
|
||||||
Timber.v("## encryptEventContent() : succeeds after " + (System.currentTimeMillis() - t0) + " ms")
|
Timber.v("## encryptEventContent() : succeeds after " + (System.currentTimeMillis() - t0) + " ms")
|
||||||
|
|
||||||
callback.onSuccess(MXEncryptEventContentResult(data, EventType.ENCRYPTED))
|
callback.onSuccess(MXEncryptEventContentResult(data, EventType.ENCRYPTED))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -623,6 +600,7 @@ internal class CryptoManager(
|
|||||||
MXCryptoError.UNABLE_TO_ENCRYPT, reason)))
|
MXCryptoError.UNABLE_TO_ENCRYPT, reason)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decrypt an event
|
* Decrypt an event
|
||||||
@ -633,43 +611,23 @@ internal class CryptoManager(
|
|||||||
*/
|
*/
|
||||||
@Throws(MXDecryptionException::class)
|
@Throws(MXDecryptionException::class)
|
||||||
override fun decryptEvent(event: Event, timeline: String): MXEventDecryptionResult? {
|
override fun decryptEvent(event: Event, timeline: String): MXEventDecryptionResult? {
|
||||||
val eventContent = event.content //wireEventContent?
|
val eventContent = event.content
|
||||||
|
if (eventContent == null) {
|
||||||
if (null == eventContent) {
|
|
||||||
Timber.e("## decryptEvent : empty event content")
|
Timber.e("## decryptEvent : empty event content")
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
val results = ArrayList<MXEventDecryptionResult>()
|
|
||||||
val exceptions = ArrayList<MXDecryptionException>()
|
|
||||||
|
|
||||||
var result: MXEventDecryptionResult? = null
|
|
||||||
val alg = roomDecryptorProvider.getOrCreateRoomDecryptor(event.roomId, eventContent["algorithm"] as String)
|
val alg = roomDecryptorProvider.getOrCreateRoomDecryptor(event.roomId, eventContent["algorithm"] as String)
|
||||||
|
if (alg == null) {
|
||||||
if (null == alg) {
|
|
||||||
val reason = String.format(MXCryptoError.UNABLE_TO_DECRYPT_REASON, event.eventId, eventContent["algorithm"] as String)
|
val reason = String.format(MXCryptoError.UNABLE_TO_DECRYPT_REASON, event.eventId, eventContent["algorithm"] as String)
|
||||||
Timber.e("## decryptEvent() : $reason")
|
Timber.e("## decryptEvent() : $reason")
|
||||||
exceptions.add(MXDecryptionException(MXCryptoError(MXCryptoError.UNABLE_TO_DECRYPT_ERROR_CODE,
|
throw MXDecryptionException(MXCryptoError(MXCryptoError.UNABLE_TO_DECRYPT_ERROR_CODE, MXCryptoError.UNABLE_TO_DECRYPT, reason))
|
||||||
MXCryptoError.UNABLE_TO_DECRYPT, reason)))
|
|
||||||
} else {
|
} else {
|
||||||
try {
|
return runBlocking {
|
||||||
result = alg.decryptEvent(event, timeline)
|
withContext(coroutineDispatchers.crypto) {
|
||||||
} catch (decryptionException: MXDecryptionException) {
|
alg.decryptEvent(event, timeline)
|
||||||
exceptions.add(decryptionException)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (null != result) {
|
|
||||||
results.add(result) // TODO simplify
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!exceptions.isEmpty()) {
|
|
||||||
throw exceptions[0]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return if (!results.isEmpty()) {
|
|
||||||
results[0]
|
|
||||||
} else null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -687,16 +645,17 @@ internal class CryptoManager(
|
|||||||
* @param event the event
|
* @param event the event
|
||||||
*/
|
*/
|
||||||
fun onToDeviceEvent(event: Event) {
|
fun onToDeviceEvent(event: Event) {
|
||||||
|
CoroutineScope(coroutineDispatchers.crypto).launch {
|
||||||
if (event.getClearType() == EventType.ROOM_KEY || event.getClearType() == EventType.FORWARDED_ROOM_KEY) {
|
if (event.getClearType() == EventType.ROOM_KEY || event.getClearType() == EventType.FORWARDED_ROOM_KEY) {
|
||||||
onRoomKeyEvent(event)
|
onRoomKeyEvent(event)
|
||||||
} else if (event.getClearType() == EventType.ROOM_KEY_REQUEST) {
|
} else if (event.getClearType() == EventType.ROOM_KEY_REQUEST) {
|
||||||
incomingRoomKeyRequestManager.onRoomKeyRequestEvent(event)
|
incomingRoomKeyRequestManager.onRoomKeyRequestEvent(event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle a key event.
|
* Handle a key event.
|
||||||
* This method must be called on getDecryptingThreadHandler() thread.
|
|
||||||
*
|
*
|
||||||
* @param event the key event.
|
* @param event the key event.
|
||||||
*/
|
*/
|
||||||
@ -798,25 +757,17 @@ internal class CryptoManager(
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Upload my user's device keys.
|
* Upload my user's device keys.
|
||||||
* This method must called on getEncryptingThreadHandler() thread.
|
|
||||||
* The callback will called on UI thread.
|
|
||||||
*
|
|
||||||
* @param callback the asynchronous callback
|
|
||||||
*/
|
*/
|
||||||
private fun uploadDeviceKeys(callback: MatrixCallback<KeysUploadResponse>) {
|
private suspend fun uploadDeviceKeys(): Try<KeysUploadResponse> {
|
||||||
// Prepare the device keys data to send
|
// Prepare the device keys data to send
|
||||||
// Sign it
|
// Sign it
|
||||||
val canonicalJson = MoshiProvider.getCanonicalJson(Map::class.java, getMyDevice().signalableJSONDictionary())
|
val canonicalJson = MoshiProvider.getCanonicalJson(Map::class.java, getMyDevice().signalableJSONDictionary())
|
||||||
|
|
||||||
getMyDevice().signatures = objectSigner.signObject(canonicalJson)
|
getMyDevice().signatures = objectSigner.signObject(canonicalJson)
|
||||||
|
|
||||||
// For now, we set the device id explicitly, as we may not be using the
|
// For now, we set the device id explicitly, as we may not be using the
|
||||||
// same one as used in login.
|
// same one as used in login.
|
||||||
uploadKeysTask
|
val uploadDeviceKeysParams = UploadKeysTask.Params(getMyDevice().toDeviceKeys(), null, getMyDevice().deviceId)
|
||||||
.configureWith(UploadKeysTask.Params(getMyDevice().toDeviceKeys(), null, getMyDevice().deviceId))
|
return uploadKeysTask.execute(uploadDeviceKeysParams)
|
||||||
.executeOn(TaskThread.ENCRYPTION)
|
|
||||||
.dispatchTo(callback)
|
|
||||||
.executeBy(taskExecutor)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -934,10 +885,13 @@ internal class CryptoManager(
|
|||||||
*/
|
*/
|
||||||
fun checkUnknownDevices(userIds: List<String>, callback: MatrixCallback<Unit>) {
|
fun checkUnknownDevices(userIds: List<String>, callback: MatrixCallback<Unit>) {
|
||||||
// force the refresh to ensure that the devices list is up-to-date
|
// force the refresh to ensure that the devices list is up-to-date
|
||||||
deviceListManager.downloadKeys(userIds, true, object : MatrixCallback<MXUsersDevicesMap<MXDeviceInfo>> {
|
CoroutineScope(coroutineDispatchers.crypto).launch {
|
||||||
override fun onSuccess(data: MXUsersDevicesMap<MXDeviceInfo>) {
|
deviceListManager
|
||||||
val unknownDevices = getUnknownDevices(data)
|
.downloadKeys(userIds, true)
|
||||||
|
.fold(
|
||||||
|
{ callback.onFailure(it) },
|
||||||
|
{
|
||||||
|
val unknownDevices = getUnknownDevices(it)
|
||||||
if (unknownDevices.map.isEmpty()) {
|
if (unknownDevices.map.isEmpty()) {
|
||||||
callback.onSuccess(Unit)
|
callback.onSuccess(Unit)
|
||||||
} else {
|
} else {
|
||||||
@ -947,11 +901,8 @@ internal class CryptoManager(
|
|||||||
MXCryptoError.UNABLE_TO_ENCRYPT, MXCryptoError.UNKNOWN_DEVICES_REASON, unknownDevices)))
|
MXCryptoError.UNABLE_TO_ENCRYPT, MXCryptoError.UNKNOWN_DEVICES_REASON, unknownDevices)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
)
|
||||||
override fun onFailure(failure: Throwable) {
|
|
||||||
callback.onFailure(failure)
|
|
||||||
}
|
}
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1091,7 +1042,6 @@ internal class CryptoManager(
|
|||||||
*/
|
*/
|
||||||
private fun getUnknownDevices(devicesInRoom: MXUsersDevicesMap<MXDeviceInfo>): MXUsersDevicesMap<MXDeviceInfo> {
|
private fun getUnknownDevices(devicesInRoom: MXUsersDevicesMap<MXDeviceInfo>): MXUsersDevicesMap<MXDeviceInfo> {
|
||||||
val unknownDevices = MXUsersDevicesMap<MXDeviceInfo>()
|
val unknownDevices = MXUsersDevicesMap<MXDeviceInfo>()
|
||||||
|
|
||||||
val userIds = devicesInRoom.userIds
|
val userIds = devicesInRoom.userIds
|
||||||
for (userId in userIds) {
|
for (userId in userIds) {
|
||||||
val deviceIds = devicesInRoom.getUserDeviceIds(userId)
|
val deviceIds = devicesInRoom.getUserDeviceIds(userId)
|
||||||
|
@ -19,7 +19,11 @@ package im.vector.matrix.android.internal.crypto
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import im.vector.matrix.android.api.auth.data.Credentials
|
import im.vector.matrix.android.api.auth.data.Credentials
|
||||||
import im.vector.matrix.android.api.session.crypto.CryptoService
|
import im.vector.matrix.android.api.session.crypto.CryptoService
|
||||||
import im.vector.matrix.android.internal.crypto.actions.*
|
import im.vector.matrix.android.internal.crypto.actions.EnsureOlmSessionsForDevicesAction
|
||||||
|
import im.vector.matrix.android.internal.crypto.actions.EnsureOlmSessionsForUsersAction
|
||||||
|
import im.vector.matrix.android.internal.crypto.actions.MegolmSessionDataImporter
|
||||||
|
import im.vector.matrix.android.internal.crypto.actions.MessageEncrypter
|
||||||
|
import im.vector.matrix.android.internal.crypto.actions.SetDeviceVerificationAction
|
||||||
import im.vector.matrix.android.internal.crypto.algorithms.megolm.MXMegolmDecryptionFactory
|
import im.vector.matrix.android.internal.crypto.algorithms.megolm.MXMegolmDecryptionFactory
|
||||||
import im.vector.matrix.android.internal.crypto.algorithms.megolm.MXMegolmEncryptionFactory
|
import im.vector.matrix.android.internal.crypto.algorithms.megolm.MXMegolmEncryptionFactory
|
||||||
import im.vector.matrix.android.internal.crypto.algorithms.olm.MXOlmDecryptionFactory
|
import im.vector.matrix.android.internal.crypto.algorithms.olm.MXOlmDecryptionFactory
|
||||||
@ -27,14 +31,56 @@ import im.vector.matrix.android.internal.crypto.algorithms.olm.MXOlmEncryptionFa
|
|||||||
import im.vector.matrix.android.internal.crypto.api.CryptoApi
|
import im.vector.matrix.android.internal.crypto.api.CryptoApi
|
||||||
import im.vector.matrix.android.internal.crypto.keysbackup.KeysBackup
|
import im.vector.matrix.android.internal.crypto.keysbackup.KeysBackup
|
||||||
import im.vector.matrix.android.internal.crypto.keysbackup.api.RoomKeysApi
|
import im.vector.matrix.android.internal.crypto.keysbackup.api.RoomKeysApi
|
||||||
import im.vector.matrix.android.internal.crypto.keysbackup.tasks.*
|
import im.vector.matrix.android.internal.crypto.keysbackup.tasks.CreateKeysBackupVersionTask
|
||||||
|
import im.vector.matrix.android.internal.crypto.keysbackup.tasks.DefaultCreateKeysBackupVersionTask
|
||||||
|
import im.vector.matrix.android.internal.crypto.keysbackup.tasks.DefaultDeleteBackupTask
|
||||||
|
import im.vector.matrix.android.internal.crypto.keysbackup.tasks.DefaultDeleteRoomSessionDataTask
|
||||||
|
import im.vector.matrix.android.internal.crypto.keysbackup.tasks.DefaultDeleteRoomSessionsDataTask
|
||||||
|
import im.vector.matrix.android.internal.crypto.keysbackup.tasks.DefaultDeleteSessionsDataTask
|
||||||
|
import im.vector.matrix.android.internal.crypto.keysbackup.tasks.DefaultGetKeysBackupLastVersionTask
|
||||||
|
import im.vector.matrix.android.internal.crypto.keysbackup.tasks.DefaultGetKeysBackupVersionTask
|
||||||
|
import im.vector.matrix.android.internal.crypto.keysbackup.tasks.DefaultGetRoomSessionDataTask
|
||||||
|
import im.vector.matrix.android.internal.crypto.keysbackup.tasks.DefaultGetRoomSessionsDataTask
|
||||||
|
import im.vector.matrix.android.internal.crypto.keysbackup.tasks.DefaultGetSessionsDataTask
|
||||||
|
import im.vector.matrix.android.internal.crypto.keysbackup.tasks.DefaultStoreRoomSessionDataTask
|
||||||
|
import im.vector.matrix.android.internal.crypto.keysbackup.tasks.DefaultStoreRoomSessionsDataTask
|
||||||
|
import im.vector.matrix.android.internal.crypto.keysbackup.tasks.DefaultStoreSessionsDataTask
|
||||||
|
import im.vector.matrix.android.internal.crypto.keysbackup.tasks.DefaultUpdateKeysBackupVersionTask
|
||||||
|
import im.vector.matrix.android.internal.crypto.keysbackup.tasks.DeleteBackupTask
|
||||||
|
import im.vector.matrix.android.internal.crypto.keysbackup.tasks.DeleteRoomSessionDataTask
|
||||||
|
import im.vector.matrix.android.internal.crypto.keysbackup.tasks.DeleteRoomSessionsDataTask
|
||||||
|
import im.vector.matrix.android.internal.crypto.keysbackup.tasks.DeleteSessionsDataTask
|
||||||
|
import im.vector.matrix.android.internal.crypto.keysbackup.tasks.GetKeysBackupLastVersionTask
|
||||||
|
import im.vector.matrix.android.internal.crypto.keysbackup.tasks.GetKeysBackupVersionTask
|
||||||
|
import im.vector.matrix.android.internal.crypto.keysbackup.tasks.GetRoomSessionDataTask
|
||||||
|
import im.vector.matrix.android.internal.crypto.keysbackup.tasks.GetRoomSessionsDataTask
|
||||||
|
import im.vector.matrix.android.internal.crypto.keysbackup.tasks.GetSessionsDataTask
|
||||||
|
import im.vector.matrix.android.internal.crypto.keysbackup.tasks.StoreRoomSessionDataTask
|
||||||
|
import im.vector.matrix.android.internal.crypto.keysbackup.tasks.StoreRoomSessionsDataTask
|
||||||
|
import im.vector.matrix.android.internal.crypto.keysbackup.tasks.StoreSessionsDataTask
|
||||||
|
import im.vector.matrix.android.internal.crypto.keysbackup.tasks.UpdateKeysBackupVersionTask
|
||||||
import im.vector.matrix.android.internal.crypto.repository.WarnOnUnknownDeviceRepository
|
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.store.IMXCryptoStore
|
||||||
import im.vector.matrix.android.internal.crypto.store.db.RealmCryptoStore
|
import im.vector.matrix.android.internal.crypto.store.db.RealmCryptoStore
|
||||||
import im.vector.matrix.android.internal.crypto.store.db.RealmCryptoStoreMigration
|
import im.vector.matrix.android.internal.crypto.store.db.RealmCryptoStoreMigration
|
||||||
import im.vector.matrix.android.internal.crypto.store.db.RealmCryptoStoreModule
|
import im.vector.matrix.android.internal.crypto.store.db.RealmCryptoStoreModule
|
||||||
import im.vector.matrix.android.internal.crypto.store.db.hash
|
import im.vector.matrix.android.internal.crypto.store.db.hash
|
||||||
import im.vector.matrix.android.internal.crypto.tasks.*
|
import im.vector.matrix.android.internal.crypto.tasks.ClaimOneTimeKeysForUsersDeviceTask
|
||||||
|
import im.vector.matrix.android.internal.crypto.tasks.DefaultClaimOneTimeKeysForUsersDevice
|
||||||
|
import im.vector.matrix.android.internal.crypto.tasks.DefaultDeleteDeviceTask
|
||||||
|
import im.vector.matrix.android.internal.crypto.tasks.DefaultDownloadKeysForUsers
|
||||||
|
import im.vector.matrix.android.internal.crypto.tasks.DefaultGetDevicesTask
|
||||||
|
import im.vector.matrix.android.internal.crypto.tasks.DefaultGetKeyChangesTask
|
||||||
|
import im.vector.matrix.android.internal.crypto.tasks.DefaultSendToDeviceTask
|
||||||
|
import im.vector.matrix.android.internal.crypto.tasks.DefaultSetDeviceNameTask
|
||||||
|
import im.vector.matrix.android.internal.crypto.tasks.DefaultUploadKeysTask
|
||||||
|
import im.vector.matrix.android.internal.crypto.tasks.DeleteDeviceTask
|
||||||
|
import im.vector.matrix.android.internal.crypto.tasks.DownloadKeysForUsersTask
|
||||||
|
import im.vector.matrix.android.internal.crypto.tasks.GetDevicesTask
|
||||||
|
import im.vector.matrix.android.internal.crypto.tasks.GetKeyChangesTask
|
||||||
|
import im.vector.matrix.android.internal.crypto.tasks.SendToDeviceTask
|
||||||
|
import im.vector.matrix.android.internal.crypto.tasks.SetDeviceNameTask
|
||||||
|
import im.vector.matrix.android.internal.crypto.tasks.UploadKeysTask
|
||||||
import im.vector.matrix.android.internal.crypto.verification.DefaultSasVerificationService
|
import im.vector.matrix.android.internal.crypto.verification.DefaultSasVerificationService
|
||||||
import im.vector.matrix.android.internal.session.DefaultSession
|
import im.vector.matrix.android.internal.session.DefaultSession
|
||||||
import io.realm.RealmConfiguration
|
import io.realm.RealmConfiguration
|
||||||
@ -109,7 +155,7 @@ internal class CryptoModule {
|
|||||||
|
|
||||||
// OneTimeKeysUploader
|
// OneTimeKeysUploader
|
||||||
scope(DefaultSession.SCOPE) {
|
scope(DefaultSession.SCOPE) {
|
||||||
OneTimeKeysUploader(get(), get(), get(), get(), get())
|
OneTimeKeysUploader(get(), get(), get(), get())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actions
|
// Actions
|
||||||
@ -217,7 +263,7 @@ internal class CryptoModule {
|
|||||||
|
|
||||||
// Device list
|
// Device list
|
||||||
scope(DefaultSession.SCOPE) {
|
scope(DefaultSession.SCOPE) {
|
||||||
DeviceListManager(get(), get(), get(), get(), get(), get(), get())
|
DeviceListManager(get(), get(), get(), get(), get())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Crypto tasks
|
// Crypto tasks
|
||||||
@ -319,7 +365,7 @@ internal class CryptoModule {
|
|||||||
* ========================================================================================== */
|
* ========================================================================================== */
|
||||||
|
|
||||||
scope(DefaultSession.SCOPE) {
|
scope(DefaultSession.SCOPE) {
|
||||||
DefaultSasVerificationService(get(), get(), get(), get(), get(), get(), get())
|
DefaultSasVerificationService(get(), get(), get(), get(), get(), get(), get(), get())
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -18,19 +18,15 @@
|
|||||||
package im.vector.matrix.android.internal.crypto
|
package im.vector.matrix.android.internal.crypto
|
||||||
|
|
||||||
import android.text.TextUtils
|
import android.text.TextUtils
|
||||||
import im.vector.matrix.android.api.MatrixCallback
|
import arrow.core.Try
|
||||||
|
import arrow.instances.`try`.applicativeError.handleError
|
||||||
import im.vector.matrix.android.api.MatrixPatterns
|
import im.vector.matrix.android.api.MatrixPatterns
|
||||||
import im.vector.matrix.android.api.auth.data.Credentials
|
import im.vector.matrix.android.api.auth.data.Credentials
|
||||||
import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo
|
import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo
|
||||||
import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap
|
import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap
|
||||||
import im.vector.matrix.android.internal.crypto.model.rest.KeysQueryResponse
|
|
||||||
import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
|
import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
|
||||||
import im.vector.matrix.android.internal.crypto.tasks.DownloadKeysForUsersTask
|
import im.vector.matrix.android.internal.crypto.tasks.DownloadKeysForUsersTask
|
||||||
import im.vector.matrix.android.internal.session.sync.SyncTokenStore
|
import im.vector.matrix.android.internal.session.sync.SyncTokenStore
|
||||||
import im.vector.matrix.android.internal.task.TaskExecutor
|
|
||||||
import im.vector.matrix.android.internal.task.TaskThread
|
|
||||||
import im.vector.matrix.android.internal.task.configureWith
|
|
||||||
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
|
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
@ -39,59 +35,22 @@ internal class DeviceListManager(private val cryptoStore: IMXCryptoStore,
|
|||||||
private val olmDevice: MXOlmDevice,
|
private val olmDevice: MXOlmDevice,
|
||||||
private val syncTokenStore: SyncTokenStore,
|
private val syncTokenStore: SyncTokenStore,
|
||||||
private val credentials: Credentials,
|
private val credentials: Credentials,
|
||||||
private val downloadKeysForUsersTask: DownloadKeysForUsersTask,
|
private val downloadKeysForUsersTask: DownloadKeysForUsersTask) {
|
||||||
private val coroutineDispatchers: MatrixCoroutineDispatchers,
|
|
||||||
private val taskExecutor: TaskExecutor) {
|
|
||||||
|
|
||||||
// keys in progress
|
|
||||||
private val userKeyDownloadsInProgress = HashSet<String>()
|
|
||||||
|
|
||||||
// HS not ready for retry
|
// HS not ready for retry
|
||||||
private val notReadyToRetryHS = HashSet<String>()
|
private val notReadyToRetryHS = HashSet<String>()
|
||||||
|
|
||||||
// indexed by UserId
|
|
||||||
private val pendingDownloadKeysRequestToken = HashMap<String, String>()
|
|
||||||
|
|
||||||
// pending queues list
|
|
||||||
private val downloadKeysQueues = ArrayList<DownloadKeysPromise>()
|
|
||||||
|
|
||||||
// tells if there is a download keys request in progress
|
|
||||||
private var isDownloadingKeys = false
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creator
|
|
||||||
*
|
|
||||||
* @param userIds the user ids list
|
|
||||||
* @param callback the asynchronous callback
|
|
||||||
*/
|
|
||||||
internal inner class DownloadKeysPromise(userIds: List<String>,
|
|
||||||
val callback: MatrixCallback<MXUsersDevicesMap<MXDeviceInfo>>?) {
|
|
||||||
// list of remain pending device keys
|
|
||||||
val mPendingUserIdsList: MutableList<String>
|
|
||||||
|
|
||||||
// the unfiltered user ids list
|
|
||||||
val mUserIdsList: List<String>
|
|
||||||
|
|
||||||
init {
|
|
||||||
mPendingUserIdsList = ArrayList(userIds)
|
|
||||||
mUserIdsList = ArrayList(userIds)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
var isUpdated = false
|
var isUpdated = false
|
||||||
|
|
||||||
val deviceTrackingStatuses = cryptoStore.getDeviceTrackingStatuses().toMutableMap()
|
val deviceTrackingStatuses = cryptoStore.getDeviceTrackingStatuses().toMutableMap()
|
||||||
for (userId in deviceTrackingStatuses.keys) {
|
for (userId in deviceTrackingStatuses.keys) {
|
||||||
val status = deviceTrackingStatuses[userId]!!
|
val status = deviceTrackingStatuses[userId]!!
|
||||||
|
|
||||||
if (TRACKING_STATUS_DOWNLOAD_IN_PROGRESS == status || TRACKING_STATUS_UNREACHABLE_SERVER == status) {
|
if (TRACKING_STATUS_DOWNLOAD_IN_PROGRESS == status || TRACKING_STATUS_UNREACHABLE_SERVER == status) {
|
||||||
// if a download was in progress when we got shut down, it isn't any more.
|
// if a download was in progress when we got shut down, it isn't any more.
|
||||||
deviceTrackingStatuses.put(userId, TRACKING_STATUS_PENDING_DOWNLOAD)
|
deviceTrackingStatuses.put(userId, TRACKING_STATUS_PENDING_DOWNLOAD)
|
||||||
isUpdated = true
|
isUpdated = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isUpdated) {
|
if (isUpdated) {
|
||||||
cryptoStore.saveDeviceTrackingStatuses(deviceTrackingStatuses)
|
cryptoStore.saveDeviceTrackingStatuses(deviceTrackingStatuses)
|
||||||
}
|
}
|
||||||
@ -120,43 +79,6 @@ internal class DeviceListManager(private val cryptoStore: IMXCryptoStore,
|
|||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a download keys promise
|
|
||||||
*
|
|
||||||
* @param userIds the user ids list
|
|
||||||
* @param callback the asynchronous callback
|
|
||||||
* @return the filtered user ids list i.e the one which require a remote request
|
|
||||||
*/
|
|
||||||
private fun addDownloadKeysPromise(userIds: MutableList<String>?, callback: MatrixCallback<MXUsersDevicesMap<MXDeviceInfo>>?): MutableList<String>? {
|
|
||||||
if (null != userIds) {
|
|
||||||
val filteredUserIds = ArrayList<String>()
|
|
||||||
val invalidUserIds = ArrayList<String>()
|
|
||||||
|
|
||||||
for (userId in userIds) {
|
|
||||||
if (MatrixPatterns.isUserId(userId)) {
|
|
||||||
filteredUserIds.add(userId)
|
|
||||||
} else {
|
|
||||||
Timber.e("## userId " + userId + "is not a valid user id")
|
|
||||||
invalidUserIds.add(userId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
synchronized(userKeyDownloadsInProgress) {
|
|
||||||
filteredUserIds.removeAll(userKeyDownloadsInProgress)
|
|
||||||
userKeyDownloadsInProgress.addAll(userIds)
|
|
||||||
// got some email addresses instead of matrix ids
|
|
||||||
userKeyDownloadsInProgress.removeAll(invalidUserIds)
|
|
||||||
userIds.removeAll(invalidUserIds)
|
|
||||||
}
|
|
||||||
|
|
||||||
downloadKeysQueues.add(DownloadKeysPromise(userIds, callback))
|
|
||||||
|
|
||||||
return filteredUserIds
|
|
||||||
} else {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clear the unavailable server lists
|
* Clear the unavailable server lists
|
||||||
*/
|
*/
|
||||||
@ -180,7 +102,7 @@ internal class DeviceListManager(private val cryptoStore: IMXCryptoStore,
|
|||||||
for (userId in userIds) {
|
for (userId in userIds) {
|
||||||
if (!deviceTrackingStatuses.containsKey(userId) || TRACKING_STATUS_NOT_TRACKED == deviceTrackingStatuses[userId]) {
|
if (!deviceTrackingStatuses.containsKey(userId) || TRACKING_STATUS_NOT_TRACKED == deviceTrackingStatuses[userId]) {
|
||||||
Timber.v("## startTrackingDeviceList() : Now tracking device list for $userId")
|
Timber.v("## startTrackingDeviceList() : Now tracking device list for $userId")
|
||||||
deviceTrackingStatuses.put(userId, TRACKING_STATUS_PENDING_DOWNLOAD)
|
deviceTrackingStatuses[userId] = TRACKING_STATUS_PENDING_DOWNLOAD
|
||||||
isUpdated = true
|
isUpdated = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -202,24 +124,20 @@ internal class DeviceListManager(private val cryptoStore: IMXCryptoStore,
|
|||||||
val deviceTrackingStatuses = cryptoStore.getDeviceTrackingStatuses().toMutableMap()
|
val deviceTrackingStatuses = cryptoStore.getDeviceTrackingStatuses().toMutableMap()
|
||||||
|
|
||||||
if (changed?.isNotEmpty() == true) {
|
if (changed?.isNotEmpty() == true) {
|
||||||
clearUnavailableServersList()
|
|
||||||
|
|
||||||
for (userId in changed) {
|
for (userId in changed) {
|
||||||
if (deviceTrackingStatuses.containsKey(userId)) {
|
if (deviceTrackingStatuses.containsKey(userId)) {
|
||||||
Timber.v("## invalidateUserDeviceList() : Marking device list outdated for $userId")
|
Timber.v("## invalidateUserDeviceList() : Marking device list outdated for $userId")
|
||||||
deviceTrackingStatuses.put(userId, TRACKING_STATUS_PENDING_DOWNLOAD)
|
deviceTrackingStatuses[userId] = TRACKING_STATUS_PENDING_DOWNLOAD
|
||||||
isUpdated = true
|
isUpdated = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (left?.isNotEmpty() == true) {
|
if (left?.isNotEmpty() == true) {
|
||||||
clearUnavailableServersList()
|
|
||||||
|
|
||||||
for (userId in left) {
|
for (userId in left) {
|
||||||
if (deviceTrackingStatuses.containsKey(userId)) {
|
if (deviceTrackingStatuses.containsKey(userId)) {
|
||||||
Timber.v("## invalidateUserDeviceList() : No longer tracking device list for $userId")
|
Timber.v("## invalidateUserDeviceList() : No longer tracking device list for $userId")
|
||||||
deviceTrackingStatuses.put(userId, TRACKING_STATUS_NOT_TRACKED)
|
deviceTrackingStatuses[userId] = TRACKING_STATUS_NOT_TRACKED
|
||||||
isUpdated = true
|
isUpdated = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -243,22 +161,13 @@ internal class DeviceListManager(private val cryptoStore: IMXCryptoStore,
|
|||||||
*
|
*
|
||||||
* @param userIds the user ids list
|
* @param userIds the user ids list
|
||||||
*/
|
*/
|
||||||
private fun onKeysDownloadFailed(userIds: List<String>?) {
|
private fun onKeysDownloadFailed(userIds: List<String>) {
|
||||||
if (null != userIds) {
|
|
||||||
synchronized(userKeyDownloadsInProgress) {
|
|
||||||
val deviceTrackingStatuses = cryptoStore.getDeviceTrackingStatuses().toMutableMap()
|
val deviceTrackingStatuses = cryptoStore.getDeviceTrackingStatuses().toMutableMap()
|
||||||
|
|
||||||
for (userId in userIds) {
|
for (userId in userIds) {
|
||||||
userKeyDownloadsInProgress.remove(userId)
|
deviceTrackingStatuses[userId] = TRACKING_STATUS_PENDING_DOWNLOAD
|
||||||
deviceTrackingStatuses.put(userId, TRACKING_STATUS_PENDING_DOWNLOAD)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cryptoStore.saveDeviceTrackingStatuses(deviceTrackingStatuses)
|
cryptoStore.saveDeviceTrackingStatuses(deviceTrackingStatuses)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
isDownloadingKeys = false
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The keys download succeeded.
|
* The keys download succeeded.
|
||||||
@ -266,23 +175,19 @@ internal class DeviceListManager(private val cryptoStore: IMXCryptoStore,
|
|||||||
* @param userIds the userIds list
|
* @param userIds the userIds list
|
||||||
* @param failures the failure map.
|
* @param failures the failure map.
|
||||||
*/
|
*/
|
||||||
private fun onKeysDownloadSucceed(userIds: List<String>?, failures: Map<String, Map<String, Any>>?) {
|
private fun onKeysDownloadSucceed(userIds: List<String>, failures: Map<String, Map<String, Any>>?): MXUsersDevicesMap<MXDeviceInfo> {
|
||||||
if (null != failures) {
|
if (failures != null) {
|
||||||
val keys = failures.keys
|
val keys = failures.keys
|
||||||
|
|
||||||
for (k in keys) {
|
for (k in keys) {
|
||||||
val value = failures[k]
|
val value = failures[k]
|
||||||
|
|
||||||
if (value!!.containsKey("status")) {
|
if (value!!.containsKey("status")) {
|
||||||
val statusCodeAsVoid = value["status"]
|
val statusCodeAsVoid = value["status"]
|
||||||
var statusCode = 0
|
var statusCode = 0
|
||||||
|
|
||||||
if (statusCodeAsVoid is Double) {
|
if (statusCodeAsVoid is Double) {
|
||||||
statusCode = statusCodeAsVoid.toInt()
|
statusCode = statusCodeAsVoid.toInt()
|
||||||
} else if (statusCodeAsVoid is Int) {
|
} else if (statusCodeAsVoid is Int) {
|
||||||
statusCode = statusCodeAsVoid.toInt()
|
statusCode = statusCodeAsVoid.toInt()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (statusCode == 503) {
|
if (statusCode == 503) {
|
||||||
synchronized(notReadyToRetryHS) {
|
synchronized(notReadyToRetryHS) {
|
||||||
notReadyToRetryHS.add(k)
|
notReadyToRetryHS.add(k)
|
||||||
@ -291,29 +196,17 @@ internal class DeviceListManager(private val cryptoStore: IMXCryptoStore,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val deviceTrackingStatuses = cryptoStore.getDeviceTrackingStatuses().toMutableMap()
|
val deviceTrackingStatuses = cryptoStore.getDeviceTrackingStatuses().toMutableMap()
|
||||||
|
|
||||||
if (null != userIds) {
|
|
||||||
if (downloadKeysQueues.size > 0) {
|
|
||||||
val promisesToRemove = ArrayList<DownloadKeysPromise>()
|
|
||||||
|
|
||||||
for (promise in downloadKeysQueues) {
|
|
||||||
promise.mPendingUserIdsList.removeAll(userIds)
|
|
||||||
|
|
||||||
if (promise.mPendingUserIdsList.size == 0) {
|
|
||||||
// private members
|
|
||||||
val usersDevicesInfoMap = MXUsersDevicesMap<MXDeviceInfo>()
|
val usersDevicesInfoMap = MXUsersDevicesMap<MXDeviceInfo>()
|
||||||
|
for (userId in userIds) {
|
||||||
for (userId in promise.mUserIdsList) {
|
|
||||||
val devices = cryptoStore.getUserDevices(userId)
|
val devices = cryptoStore.getUserDevices(userId)
|
||||||
if (null == devices) {
|
if (null == devices) {
|
||||||
if (canRetryKeysDownload(userId)) {
|
if (canRetryKeysDownload(userId)) {
|
||||||
deviceTrackingStatuses.put(userId, TRACKING_STATUS_PENDING_DOWNLOAD)
|
deviceTrackingStatuses[userId] = TRACKING_STATUS_PENDING_DOWNLOAD
|
||||||
Timber.e("failed to retry the devices of $userId : retry later")
|
Timber.e("failed to retry the devices of $userId : retry later")
|
||||||
} else {
|
} else {
|
||||||
if (deviceTrackingStatuses.containsKey(userId) && TRACKING_STATUS_DOWNLOAD_IN_PROGRESS == deviceTrackingStatuses[userId]) {
|
if (deviceTrackingStatuses.containsKey(userId) && TRACKING_STATUS_DOWNLOAD_IN_PROGRESS == deviceTrackingStatuses[userId]) {
|
||||||
deviceTrackingStatuses.put(userId, TRACKING_STATUS_UNREACHABLE_SERVER)
|
deviceTrackingStatuses[userId] = TRACKING_STATUS_UNREACHABLE_SERVER
|
||||||
Timber.e("failed to retry the devices of $userId : the HS is not available")
|
Timber.e("failed to retry the devices of $userId : the HS is not available")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -321,35 +214,15 @@ internal class DeviceListManager(private val cryptoStore: IMXCryptoStore,
|
|||||||
if (deviceTrackingStatuses.containsKey(userId) && TRACKING_STATUS_DOWNLOAD_IN_PROGRESS == deviceTrackingStatuses[userId]) {
|
if (deviceTrackingStatuses.containsKey(userId) && TRACKING_STATUS_DOWNLOAD_IN_PROGRESS == deviceTrackingStatuses[userId]) {
|
||||||
// we didn't get any new invalidations since this download started:
|
// we didn't get any new invalidations since this download started:
|
||||||
// this user's device list is now up to date.
|
// this user's device list is now up to date.
|
||||||
deviceTrackingStatuses.put(userId, TRACKING_STATUS_UP_TO_DATE)
|
deviceTrackingStatuses[userId] = TRACKING_STATUS_UP_TO_DATE
|
||||||
Timber.v("Device list for $userId now up to date")
|
Timber.v("Device list for $userId now up to date")
|
||||||
}
|
}
|
||||||
|
|
||||||
// And the response result
|
// And the response result
|
||||||
usersDevicesInfoMap.setObjects(devices, userId)
|
usersDevicesInfoMap.setObjects(devices, userId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val callback = promise.callback
|
|
||||||
|
|
||||||
if (null != callback) {
|
|
||||||
CryptoAsyncHelper.getUiHandler().post { callback.onSuccess(usersDevicesInfoMap) }
|
|
||||||
}
|
|
||||||
|
|
||||||
promisesToRemove.add(promise)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
downloadKeysQueues.removeAll(promisesToRemove)
|
|
||||||
}
|
|
||||||
|
|
||||||
for (userId in userIds) {
|
|
||||||
userKeyDownloadsInProgress.remove(userId)
|
|
||||||
}
|
|
||||||
|
|
||||||
cryptoStore.saveDeviceTrackingStatuses(deviceTrackingStatuses)
|
cryptoStore.saveDeviceTrackingStatuses(deviceTrackingStatuses)
|
||||||
}
|
return usersDevicesInfoMap
|
||||||
|
|
||||||
isDownloadingKeys = false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -361,31 +234,27 @@ internal class DeviceListManager(private val cryptoStore: IMXCryptoStore,
|
|||||||
* @param forceDownload Always download the keys even if cached.
|
* @param forceDownload Always download the keys even if cached.
|
||||||
* @param callback the asynchronous callback
|
* @param callback the asynchronous callback
|
||||||
*/
|
*/
|
||||||
fun downloadKeys(userIds: List<String>?, forceDownload: Boolean, callback: MatrixCallback<MXUsersDevicesMap<MXDeviceInfo>>?) {
|
suspend fun downloadKeys(userIds: List<String>?, forceDownload: Boolean): Try<MXUsersDevicesMap<MXDeviceInfo>> {
|
||||||
Timber.v("## downloadKeys() : forceDownload $forceDownload : $userIds")
|
Timber.v("## downloadKeys() : forceDownload $forceDownload : $userIds")
|
||||||
|
|
||||||
// Map from userid -> deviceid -> DeviceInfo
|
// Map from userid -> deviceid -> DeviceInfo
|
||||||
val stored = MXUsersDevicesMap<MXDeviceInfo>()
|
val stored = MXUsersDevicesMap<MXDeviceInfo>()
|
||||||
|
|
||||||
// List of user ids we need to download keys for
|
// List of user ids we need to download keys for
|
||||||
val downloadUsers = ArrayList<String>()
|
val downloadUsers = ArrayList<String>()
|
||||||
|
|
||||||
if (null != userIds) {
|
if (null != userIds) {
|
||||||
if (forceDownload) {
|
if (forceDownload) {
|
||||||
downloadUsers.addAll(userIds)
|
downloadUsers.addAll(userIds)
|
||||||
} else {
|
} else {
|
||||||
for (userId in userIds) {
|
for (userId in userIds) {
|
||||||
val status = cryptoStore.getDeviceTrackingStatus(userId, TRACKING_STATUS_NOT_TRACKED)
|
val status = cryptoStore.getDeviceTrackingStatus(userId, TRACKING_STATUS_NOT_TRACKED)
|
||||||
|
|
||||||
// downloading keys ->the keys download won't be triggered twice but the callback requires the dedicated keys
|
// downloading keys ->the keys download won't be triggered twice but the callback requires the dedicated keys
|
||||||
// not yet retrieved
|
// not yet retrieved
|
||||||
if (userKeyDownloadsInProgress.contains(userId) || TRACKING_STATUS_UP_TO_DATE != status && TRACKING_STATUS_UNREACHABLE_SERVER != status) {
|
if (TRACKING_STATUS_UP_TO_DATE != status && TRACKING_STATUS_UNREACHABLE_SERVER != status) {
|
||||||
downloadUsers.add(userId)
|
downloadUsers.add(userId)
|
||||||
} else {
|
} else {
|
||||||
val devices = cryptoStore.getUserDevices(userId)
|
val devices = cryptoStore.getUserDevices(userId)
|
||||||
|
|
||||||
// should always be true
|
// should always be true
|
||||||
if (null != devices) {
|
if (devices != null) {
|
||||||
stored.setObjects(devices, userId)
|
stored.setObjects(devices, userId)
|
||||||
} else {
|
} else {
|
||||||
downloadUsers.add(userId)
|
downloadUsers.add(userId)
|
||||||
@ -394,31 +263,18 @@ internal class DeviceListManager(private val cryptoStore: IMXCryptoStore,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return if (downloadUsers.isEmpty()) {
|
||||||
if (0 == downloadUsers.size) {
|
|
||||||
Timber.v("## downloadKeys() : no new user device")
|
Timber.v("## downloadKeys() : no new user device")
|
||||||
|
Try.just(stored)
|
||||||
if (null != callback) {
|
|
||||||
CryptoAsyncHelper.getUiHandler().post { callback.onSuccess(stored) }
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
Timber.v("## downloadKeys() : starts")
|
Timber.v("## downloadKeys() : starts")
|
||||||
val t0 = System.currentTimeMillis()
|
val t0 = System.currentTimeMillis()
|
||||||
|
doKeyDownloadForUsers(downloadUsers)
|
||||||
doKeyDownloadForUsers(downloadUsers, object : MatrixCallback<MXUsersDevicesMap<MXDeviceInfo>> {
|
.flatMap {
|
||||||
override fun onSuccess(data: MXUsersDevicesMap<MXDeviceInfo>) {
|
|
||||||
Timber.v("## downloadKeys() : doKeyDownloadForUsers succeeds after " + (System.currentTimeMillis() - t0) + " ms")
|
Timber.v("## downloadKeys() : doKeyDownloadForUsers succeeds after " + (System.currentTimeMillis() - t0) + " ms")
|
||||||
|
it.addEntriesFromMap(stored)
|
||||||
data.addEntriesFromMap(stored)
|
Try.just(it)
|
||||||
|
|
||||||
callback?.onSuccess(data)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onFailure(failure: Throwable) {
|
|
||||||
Timber.e(failure, "## downloadKeys() : doKeyDownloadForUsers onFailure")
|
|
||||||
callback?.onFailure(failure)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -430,65 +286,25 @@ internal class DeviceListManager(private val cryptoStore: IMXCryptoStore,
|
|||||||
* @param downloadUsers the user ids list
|
* @param downloadUsers the user ids list
|
||||||
* @param callback the asynchronous callback
|
* @param callback the asynchronous callback
|
||||||
*/
|
*/
|
||||||
private fun doKeyDownloadForUsers(downloadUsers: MutableList<String>, callback: MatrixCallback<MXUsersDevicesMap<MXDeviceInfo>>?) {
|
private suspend fun doKeyDownloadForUsers(downloadUsers: MutableList<String>): Try<MXUsersDevicesMap<MXDeviceInfo>> {
|
||||||
Timber.v("## doKeyDownloadForUsers() : doKeyDownloadForUsers $downloadUsers")
|
Timber.v("## doKeyDownloadForUsers() : doKeyDownloadForUsers $downloadUsers")
|
||||||
|
|
||||||
// get the user ids which did not already trigger a keys download
|
// get the user ids which did not already trigger a keys download
|
||||||
val filteredUsers = addDownloadKeysPromise(downloadUsers, callback)
|
val filteredUsers = downloadUsers.filter { MatrixPatterns.isUserId(it) }
|
||||||
|
if (filteredUsers.isEmpty()) {
|
||||||
// if there is no new keys request
|
|
||||||
if (0 == filteredUsers!!.size) {
|
|
||||||
// trigger nothing
|
// trigger nothing
|
||||||
return
|
return Try.just(MXUsersDevicesMap())
|
||||||
}
|
}
|
||||||
|
val params = DownloadKeysForUsersTask.Params(filteredUsers, syncTokenStore.getLastToken())
|
||||||
// sanity check
|
return downloadKeysForUsersTask.execute(params)
|
||||||
//if (null == mxSession.dataHandler || null == mxSession.dataHandler.store) {
|
.map { response ->
|
||||||
// return
|
|
||||||
//}
|
|
||||||
|
|
||||||
isDownloadingKeys = true
|
|
||||||
|
|
||||||
// track the race condition while sending requests
|
|
||||||
// we defines a tag for each request
|
|
||||||
// and test if the response is the latest request one
|
|
||||||
val downloadToken = filteredUsers.hashCode().toString() + " " + System.currentTimeMillis()
|
|
||||||
|
|
||||||
for (userId in filteredUsers) {
|
|
||||||
pendingDownloadKeysRequestToken[userId] = downloadToken
|
|
||||||
}
|
|
||||||
|
|
||||||
downloadKeysForUsersTask
|
|
||||||
.configureWith(DownloadKeysForUsersTask.Params(filteredUsers, syncTokenStore.getLastToken()))
|
|
||||||
.executeOn(TaskThread.ENCRYPTION)
|
|
||||||
.dispatchTo(object : MatrixCallback<KeysQueryResponse> {
|
|
||||||
override fun onSuccess(data: KeysQueryResponse) {
|
|
||||||
CryptoAsyncHelper.getEncryptBackgroundHandler().post {
|
|
||||||
Timber.v("## doKeyDownloadForUsers() : Got keys for " + filteredUsers.size + " users")
|
Timber.v("## doKeyDownloadForUsers() : Got keys for " + filteredUsers.size + " users")
|
||||||
val userIdsList = ArrayList(filteredUsers)
|
for (userId in filteredUsers) {
|
||||||
|
val devices = response.deviceKeys?.get(userId)
|
||||||
for (userId in userIdsList) {
|
|
||||||
// test if the response is the latest request one
|
|
||||||
if (!TextUtils.equals(pendingDownloadKeysRequestToken[userId], downloadToken)) {
|
|
||||||
Timber.e("## doKeyDownloadForUsers() : Another update in the queue for "
|
|
||||||
+ userId + " not marking up-to-date")
|
|
||||||
filteredUsers.remove(userId)
|
|
||||||
} else {
|
|
||||||
val devices = data.deviceKeys!![userId]
|
|
||||||
|
|
||||||
Timber.v("## doKeyDownloadForUsers() : Got keys for $userId : $devices")
|
Timber.v("## doKeyDownloadForUsers() : Got keys for $userId : $devices")
|
||||||
|
if (devices != null) {
|
||||||
if (null != devices) {
|
|
||||||
val mutableDevices = HashMap(devices)
|
val mutableDevices = HashMap(devices)
|
||||||
val deviceIds = ArrayList(mutableDevices.keys)
|
val deviceIds = ArrayList(mutableDevices.keys)
|
||||||
|
|
||||||
for (deviceId in deviceIds) {
|
for (deviceId in deviceIds) {
|
||||||
// the user has been logged out
|
|
||||||
// TODO
|
|
||||||
//if (null == cryptoStore) {
|
|
||||||
// break
|
|
||||||
//}
|
|
||||||
|
|
||||||
// Get the potential previously store device keys for this device
|
// Get the potential previously store device keys for this device
|
||||||
val previouslyStoredDeviceKeys = cryptoStore.getUserDevice(deviceId, userId)
|
val previouslyStoredDeviceKeys = cryptoStore.getUserDevice(deviceId, userId)
|
||||||
val deviceInfo = mutableDevices[deviceId]
|
val deviceInfo = mutableDevices[deviceId]
|
||||||
@ -496,14 +312,12 @@ internal class DeviceListManager(private val cryptoStore: IMXCryptoStore,
|
|||||||
// in some race conditions (like unit tests)
|
// in some race conditions (like unit tests)
|
||||||
// the self device must be seen as verified
|
// the self device must be seen as verified
|
||||||
if (TextUtils.equals(deviceInfo!!.deviceId, credentials.deviceId) && TextUtils.equals(userId, credentials.userId)) {
|
if (TextUtils.equals(deviceInfo!!.deviceId, credentials.deviceId) && TextUtils.equals(userId, credentials.userId)) {
|
||||||
deviceInfo.mVerified = MXDeviceInfo.DEVICE_VERIFICATION_VERIFIED
|
deviceInfo.verified = MXDeviceInfo.DEVICE_VERIFICATION_VERIFIED
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate received keys
|
// Validate received keys
|
||||||
if (!validateDeviceKeys(deviceInfo, userId, deviceId, previouslyStoredDeviceKeys)) {
|
if (!validateDeviceKeys(deviceInfo, userId, deviceId, previouslyStoredDeviceKeys)) {
|
||||||
// New device keys are not valid. Do not store them
|
// New device keys are not valid. Do not store them
|
||||||
mutableDevices.remove(deviceId)
|
mutableDevices.remove(deviceId)
|
||||||
|
|
||||||
if (null != previouslyStoredDeviceKeys) {
|
if (null != previouslyStoredDeviceKeys) {
|
||||||
// But keep old validated ones if any
|
// But keep old validated ones if any
|
||||||
mutableDevices[deviceId] = previouslyStoredDeviceKeys
|
mutableDevices[deviceId] = previouslyStoredDeviceKeys
|
||||||
@ -512,54 +326,23 @@ internal class DeviceListManager(private val cryptoStore: IMXCryptoStore,
|
|||||||
// The verified status is not sync'ed with hs.
|
// The verified status is not sync'ed with hs.
|
||||||
// This is a client side information, valid only for this client.
|
// This is a client side information, valid only for this client.
|
||||||
// So, transfer its previous value
|
// So, transfer its previous value
|
||||||
mutableDevices[deviceId]!!.mVerified = previouslyStoredDeviceKeys.mVerified
|
mutableDevices[deviceId]!!.verified = previouslyStoredDeviceKeys.verified
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the store
|
// Update the store
|
||||||
// Note that devices which aren't in the response will be removed from the stores
|
// Note that devices which aren't in the response will be removed from the stores
|
||||||
cryptoStore.storeUserDevices(userId, mutableDevices)
|
cryptoStore.storeUserDevices(userId, mutableDevices)
|
||||||
}
|
}
|
||||||
|
|
||||||
// the response is the latest request one
|
|
||||||
pendingDownloadKeysRequestToken.remove(userId)
|
|
||||||
}
|
}
|
||||||
|
onKeysDownloadSucceed(filteredUsers, response.failures)
|
||||||
}
|
}
|
||||||
|
.handleError {
|
||||||
onKeysDownloadSucceed(filteredUsers, data.failures)
|
Timber.e(it, "##doKeyDownloadForUsers(): error")
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onFailed() {
|
|
||||||
CryptoAsyncHelper.getEncryptBackgroundHandler().post {
|
|
||||||
val userIdsList = ArrayList(filteredUsers)
|
|
||||||
|
|
||||||
// test if the response is the latest request one
|
|
||||||
for (userId in userIdsList) {
|
|
||||||
if (!TextUtils.equals(pendingDownloadKeysRequestToken[userId], downloadToken)) {
|
|
||||||
Timber.e("## doKeyDownloadForUsers() : Another update in the queue for $userId not marking up-to-date")
|
|
||||||
filteredUsers.remove(userId)
|
|
||||||
} else {
|
|
||||||
// the response is the latest request one
|
|
||||||
pendingDownloadKeysRequestToken.remove(userId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onKeysDownloadFailed(filteredUsers)
|
onKeysDownloadFailed(filteredUsers)
|
||||||
|
throw it
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onFailure(failure: Throwable) {
|
|
||||||
Timber.e(failure, "##doKeyDownloadForUsers() : onNetworkError")
|
|
||||||
|
|
||||||
onFailed()
|
|
||||||
|
|
||||||
callback?.onFailure(failure)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.executeBy(taskExecutor)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validate device keys.
|
* Validate device keys.
|
||||||
* This method must called on getEncryptingThreadHandler() thread.
|
* This method must called on getEncryptingThreadHandler() thread.
|
||||||
@ -659,7 +442,7 @@ internal class DeviceListManager(private val cryptoStore: IMXCryptoStore,
|
|||||||
* Start device queries for any users who sent us an m.new_device recently
|
* Start device queries for any users who sent us an m.new_device recently
|
||||||
* This method must be called on getEncryptingThreadHandler() thread.
|
* This method must be called on getEncryptingThreadHandler() thread.
|
||||||
*/
|
*/
|
||||||
fun refreshOutdatedDeviceLists() {
|
suspend fun refreshOutdatedDeviceLists() {
|
||||||
val users = ArrayList<String>()
|
val users = ArrayList<String>()
|
||||||
|
|
||||||
val deviceTrackingStatuses = cryptoStore.getDeviceTrackingStatuses().toMutableMap()
|
val deviceTrackingStatuses = cryptoStore.getDeviceTrackingStatuses().toMutableMap()
|
||||||
@ -674,33 +457,24 @@ internal class DeviceListManager(private val cryptoStore: IMXCryptoStore,
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isDownloadingKeys) {
|
|
||||||
// request already in progress - do nothing. (We will automatically
|
|
||||||
// make another request if there are more users with outdated
|
|
||||||
// device lists when the current request completes).
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// update the statuses
|
// update the statuses
|
||||||
for (userId in users) {
|
for (userId in users) {
|
||||||
val status = deviceTrackingStatuses[userId]
|
val status = deviceTrackingStatuses[userId]
|
||||||
|
|
||||||
if (null != status && TRACKING_STATUS_PENDING_DOWNLOAD == status) {
|
if (null != status && TRACKING_STATUS_PENDING_DOWNLOAD == status) {
|
||||||
deviceTrackingStatuses.put(userId, TRACKING_STATUS_DOWNLOAD_IN_PROGRESS)
|
deviceTrackingStatuses.put(userId, TRACKING_STATUS_DOWNLOAD_IN_PROGRESS)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cryptoStore.saveDeviceTrackingStatuses(deviceTrackingStatuses)
|
cryptoStore.saveDeviceTrackingStatuses(deviceTrackingStatuses)
|
||||||
|
doKeyDownloadForUsers(users)
|
||||||
doKeyDownloadForUsers(users, object : MatrixCallback<MXUsersDevicesMap<MXDeviceInfo>> {
|
.fold(
|
||||||
override fun onSuccess(data: MXUsersDevicesMap<MXDeviceInfo>) {
|
{
|
||||||
CryptoAsyncHelper.getEncryptBackgroundHandler().post { Timber.v("## refreshOutdatedDeviceLists() : done") }
|
Timber.e(it, "## refreshOutdatedDeviceLists() : ERROR updating device keys for users $users")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Timber.v("## refreshOutdatedDeviceLists() : done")
|
||||||
}
|
}
|
||||||
|
)
|
||||||
override fun onFailure(failure: Throwable) {
|
|
||||||
Timber.e(failure, "## refreshOutdatedDeviceLists() : ERROR updating device keys for users $users")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -133,7 +133,7 @@ internal class IncomingRoomKeyRequestManager(
|
|||||||
if (device.isVerified) {
|
if (device.isVerified) {
|
||||||
Timber.v("## processReceivedRoomKeyRequests() : device is already verified: sharing keys")
|
Timber.v("## processReceivedRoomKeyRequests() : device is already verified: sharing keys")
|
||||||
mCryptoStore.deleteIncomingRoomKeyRequest(request)
|
mCryptoStore.deleteIncomingRoomKeyRequest(request)
|
||||||
request.share!!.run()
|
request.share?.run()
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ internal class MXOlmDevice(
|
|||||||
/**
|
/**
|
||||||
* The store where crypto data is saved.
|
* The store where crypto data is saved.
|
||||||
*/
|
*/
|
||||||
private val mStore: IMXCryptoStore) {
|
private val store: IMXCryptoStore) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the Curve25519 key for the account.
|
* @return the Curve25519 key for the account.
|
||||||
@ -53,16 +53,16 @@ internal class MXOlmDevice(
|
|||||||
private set
|
private set
|
||||||
|
|
||||||
// The OLM lib account instance.
|
// The OLM lib account instance.
|
||||||
private var mOlmAccount: OlmAccount? = null
|
private var olmAccount: OlmAccount? = null
|
||||||
|
|
||||||
// The OLM lib utility instance.
|
// The OLM lib utility instance.
|
||||||
private var mOlmUtility: OlmUtility? = null
|
private var olmUtility: OlmUtility? = null
|
||||||
|
|
||||||
// The outbound group session.
|
// The outbound group session.
|
||||||
// They are not stored in 'store' to avoid to remember to which devices we sent the session key.
|
// They are not stored in 'store' to avoid to remember to which devices we sent the session key.
|
||||||
// Plus, in cryptography, it is good to refresh sessions from time to time.
|
// Plus, in cryptography, it is good to refresh sessions from time to time.
|
||||||
// The key is the session id, the value the outbound group session.
|
// The key is the session id, the value the outbound group session.
|
||||||
private val mOutboundGroupSessionStore: MutableMap<String, OlmOutboundGroupSession> = HashMap()
|
private val outboundGroupSessionStore: MutableMap<String, OlmOutboundGroupSession> = HashMap()
|
||||||
|
|
||||||
// Store a set of decrypted message indexes for each group session.
|
// Store a set of decrypted message indexes for each group session.
|
||||||
// This partially mitigates a replay attack where a MITM resends a group
|
// This partially mitigates a replay attack where a MITM resends a group
|
||||||
@ -76,25 +76,25 @@ internal class MXOlmDevice(
|
|||||||
// The first level keys are timeline ids.
|
// The first level keys are timeline ids.
|
||||||
// The second level keys are strings of form "<senderKey>|<session_id>|<message_index>"
|
// The second level keys are strings of form "<senderKey>|<session_id>|<message_index>"
|
||||||
// Values are true.
|
// Values are true.
|
||||||
private val mInboundGroupSessionMessageIndexes: MutableMap<String, MutableMap<String, Boolean>> = HashMap()
|
private val inboundGroupSessionMessageIndexes: MutableMap<String, MutableMap<String, Boolean>> = HashMap()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* inboundGroupSessionWithId error
|
* inboundGroupSessionWithId error
|
||||||
*/
|
*/
|
||||||
private var mInboundGroupSessionWithIdError: MXCryptoError? = null
|
private var inboundGroupSessionWithIdError: MXCryptoError? = null
|
||||||
|
|
||||||
init {
|
init {
|
||||||
// Retrieve the account from the store
|
// Retrieve the account from the store
|
||||||
mOlmAccount = mStore.getAccount()
|
olmAccount = store.getAccount()
|
||||||
|
|
||||||
if (null == mOlmAccount) {
|
if (null == olmAccount) {
|
||||||
Timber.v("MXOlmDevice : create a new olm account")
|
Timber.v("MXOlmDevice : create a new olm account")
|
||||||
// Else, create it
|
// Else, create it
|
||||||
try {
|
try {
|
||||||
mOlmAccount = OlmAccount()
|
olmAccount = OlmAccount()
|
||||||
mStore.storeAccount(mOlmAccount!!)
|
store.storeAccount(olmAccount!!)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Timber.e(e, "MXOlmDevice : cannot initialize mOlmAccount")
|
Timber.e(e, "MXOlmDevice : cannot initialize olmAccount")
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
@ -102,20 +102,20 @@ internal class MXOlmDevice(
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
mOlmUtility = OlmUtility()
|
olmUtility = OlmUtility()
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Timber.e(e, "## MXOlmDevice : OlmUtility failed with error")
|
Timber.e(e, "## MXOlmDevice : OlmUtility failed with error")
|
||||||
mOlmUtility = null
|
olmUtility = null
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
deviceCurve25519Key = mOlmAccount!!.identityKeys()[OlmAccount.JSON_KEY_IDENTITY_KEY]
|
deviceCurve25519Key = olmAccount!!.identityKeys()[OlmAccount.JSON_KEY_IDENTITY_KEY]
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Timber.e(e, "## MXOlmDevice : cannot find " + OlmAccount.JSON_KEY_IDENTITY_KEY + " with error")
|
Timber.e(e, "## MXOlmDevice : cannot find " + OlmAccount.JSON_KEY_IDENTITY_KEY + " with error")
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
deviceEd25519Key = mOlmAccount!!.identityKeys()[OlmAccount.JSON_KEY_FINGER_PRINT_KEY]
|
deviceEd25519Key = olmAccount!!.identityKeys()[OlmAccount.JSON_KEY_FINGER_PRINT_KEY]
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Timber.e(e, "## MXOlmDevice : cannot find " + OlmAccount.JSON_KEY_FINGER_PRINT_KEY + " with error")
|
Timber.e(e, "## MXOlmDevice : cannot find " + OlmAccount.JSON_KEY_FINGER_PRINT_KEY + " with error")
|
||||||
}
|
}
|
||||||
@ -126,7 +126,7 @@ internal class MXOlmDevice(
|
|||||||
*/
|
*/
|
||||||
fun getOneTimeKeys(): Map<String, Map<String, String>>? {
|
fun getOneTimeKeys(): Map<String, Map<String, String>>? {
|
||||||
try {
|
try {
|
||||||
return mOlmAccount!!.oneTimeKeys()
|
return olmAccount!!.oneTimeKeys()
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Timber.e(e, "## getOneTimeKeys() : failed")
|
Timber.e(e, "## getOneTimeKeys() : failed")
|
||||||
}
|
}
|
||||||
@ -138,14 +138,14 @@ internal class MXOlmDevice(
|
|||||||
* @return The maximum number of one-time keys the olm account can store.
|
* @return The maximum number of one-time keys the olm account can store.
|
||||||
*/
|
*/
|
||||||
fun getMaxNumberOfOneTimeKeys(): Long {
|
fun getMaxNumberOfOneTimeKeys(): Long {
|
||||||
return mOlmAccount?.maxOneTimeKeys() ?: -1
|
return olmAccount?.maxOneTimeKeys() ?: -1
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Release the instance
|
* Release the instance
|
||||||
*/
|
*/
|
||||||
fun release() {
|
fun release() {
|
||||||
mOlmAccount?.releaseAccount()
|
olmAccount?.releaseAccount()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -156,7 +156,7 @@ internal class MXOlmDevice(
|
|||||||
*/
|
*/
|
||||||
fun signMessage(message: String): String? {
|
fun signMessage(message: String): String? {
|
||||||
try {
|
try {
|
||||||
return mOlmAccount!!.signMessage(message)
|
return olmAccount!!.signMessage(message)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Timber.e(e, "## signMessage() : failed")
|
Timber.e(e, "## signMessage() : failed")
|
||||||
}
|
}
|
||||||
@ -169,8 +169,8 @@ internal class MXOlmDevice(
|
|||||||
*/
|
*/
|
||||||
fun markKeysAsPublished() {
|
fun markKeysAsPublished() {
|
||||||
try {
|
try {
|
||||||
mOlmAccount!!.markOneTimeKeysAsPublished()
|
olmAccount!!.markOneTimeKeysAsPublished()
|
||||||
mStore.storeAccount(mOlmAccount!!)
|
store.storeAccount(olmAccount!!)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Timber.e(e, "## markKeysAsPublished() : failed")
|
Timber.e(e, "## markKeysAsPublished() : failed")
|
||||||
}
|
}
|
||||||
@ -183,8 +183,8 @@ internal class MXOlmDevice(
|
|||||||
*/
|
*/
|
||||||
fun generateOneTimeKeys(numKeys: Int) {
|
fun generateOneTimeKeys(numKeys: Int) {
|
||||||
try {
|
try {
|
||||||
mOlmAccount!!.generateOneTimeKeys(numKeys)
|
olmAccount!!.generateOneTimeKeys(numKeys)
|
||||||
mStore.storeAccount(mOlmAccount!!)
|
store.storeAccount(olmAccount!!)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Timber.e(e, "## generateOneTimeKeys() : failed")
|
Timber.e(e, "## generateOneTimeKeys() : failed")
|
||||||
}
|
}
|
||||||
@ -204,7 +204,7 @@ internal class MXOlmDevice(
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
olmSession = OlmSession()
|
olmSession = OlmSession()
|
||||||
olmSession.initOutboundSession(mOlmAccount!!, theirIdentityKey, theirOneTimeKey)
|
olmSession.initOutboundSession(olmAccount!!, theirIdentityKey, theirOneTimeKey)
|
||||||
|
|
||||||
val mxOlmSession = MXOlmSession(olmSession, 0)
|
val mxOlmSession = MXOlmSession(olmSession, 0)
|
||||||
|
|
||||||
@ -213,7 +213,7 @@ internal class MXOlmDevice(
|
|||||||
// this session
|
// this session
|
||||||
mxOlmSession.onMessageReceived()
|
mxOlmSession.onMessageReceived()
|
||||||
|
|
||||||
mStore.storeSession(mxOlmSession, theirIdentityKey)
|
store.storeSession(mxOlmSession, theirIdentityKey)
|
||||||
|
|
||||||
val sessionIdentifier = olmSession.sessionIdentifier()
|
val sessionIdentifier = olmSession.sessionIdentifier()
|
||||||
|
|
||||||
@ -246,7 +246,7 @@ internal class MXOlmDevice(
|
|||||||
try {
|
try {
|
||||||
try {
|
try {
|
||||||
olmSession = OlmSession()
|
olmSession = OlmSession()
|
||||||
olmSession.initInboundSessionFrom(mOlmAccount!!, theirDeviceIdentityKey, ciphertext)
|
olmSession.initInboundSessionFrom(olmAccount!!, theirDeviceIdentityKey, ciphertext)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Timber.e(e, "## createInboundSession() : the session creation failed")
|
Timber.e(e, "## createInboundSession() : the session creation failed")
|
||||||
return null
|
return null
|
||||||
@ -255,15 +255,15 @@ internal class MXOlmDevice(
|
|||||||
Timber.v("## createInboundSession() : sessionId: " + olmSession.sessionIdentifier())
|
Timber.v("## createInboundSession() : sessionId: " + olmSession.sessionIdentifier())
|
||||||
|
|
||||||
try {
|
try {
|
||||||
mOlmAccount!!.removeOneTimeKeys(olmSession)
|
olmAccount!!.removeOneTimeKeys(olmSession)
|
||||||
mStore.storeAccount(mOlmAccount!!)
|
store.storeAccount(olmAccount!!)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Timber.e(e, "## createInboundSession() : removeOneTimeKeys failed")
|
Timber.e(e, "## createInboundSession() : removeOneTimeKeys failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
Timber.v("## createInboundSession() : ciphertext: $ciphertext")
|
Timber.v("## createInboundSession() : ciphertext: $ciphertext")
|
||||||
try {
|
try {
|
||||||
val sha256 = mOlmUtility!!.sha256(URLEncoder.encode(ciphertext, "utf-8"))
|
val sha256 = olmUtility!!.sha256(URLEncoder.encode(ciphertext, "utf-8"))
|
||||||
Timber.v("## createInboundSession() :ciphertext: SHA256:" + sha256)
|
Timber.v("## createInboundSession() :ciphertext: SHA256:" + sha256)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Timber.e(e, "## createInboundSession() :ciphertext: cannot encode ciphertext")
|
Timber.e(e, "## createInboundSession() :ciphertext: cannot encode ciphertext")
|
||||||
@ -282,7 +282,7 @@ internal class MXOlmDevice(
|
|||||||
// This counts as a received message: set last received message time to now
|
// This counts as a received message: set last received message time to now
|
||||||
mxOlmSession.onMessageReceived()
|
mxOlmSession.onMessageReceived()
|
||||||
|
|
||||||
mStore.storeSession(mxOlmSession, theirDeviceIdentityKey)
|
store.storeSession(mxOlmSession, theirDeviceIdentityKey)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Timber.e(e, "## createInboundSession() : decryptMessage failed")
|
Timber.e(e, "## createInboundSession() : decryptMessage failed")
|
||||||
}
|
}
|
||||||
@ -316,7 +316,7 @@ internal class MXOlmDevice(
|
|||||||
* @return a list of known session ids for the device.
|
* @return a list of known session ids for the device.
|
||||||
*/
|
*/
|
||||||
fun getSessionIds(theirDeviceIdentityKey: String): Set<String>? {
|
fun getSessionIds(theirDeviceIdentityKey: String): Set<String>? {
|
||||||
return mStore.getDeviceSessionIds(theirDeviceIdentityKey)
|
return store.getDeviceSessionIds(theirDeviceIdentityKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -326,7 +326,7 @@ internal class MXOlmDevice(
|
|||||||
* @return the session id, or null if no established session.
|
* @return the session id, or null if no established session.
|
||||||
*/
|
*/
|
||||||
fun getSessionId(theirDeviceIdentityKey: String): String? {
|
fun getSessionId(theirDeviceIdentityKey: String): String? {
|
||||||
return mStore.getLastUsedSessionId(theirDeviceIdentityKey)
|
return store.getLastUsedSessionId(theirDeviceIdentityKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -348,7 +348,7 @@ internal class MXOlmDevice(
|
|||||||
//Timber.v("## encryptMessage() : payloadString: " + payloadString);
|
//Timber.v("## encryptMessage() : payloadString: " + payloadString);
|
||||||
|
|
||||||
olmMessage = mxOlmSession.olmSession.encryptMessage(payloadString)
|
olmMessage = mxOlmSession.olmSession.encryptMessage(payloadString)
|
||||||
mStore.storeSession(mxOlmSession, theirDeviceIdentityKey)
|
store.storeSession(mxOlmSession, theirDeviceIdentityKey)
|
||||||
res = HashMap()
|
res = HashMap()
|
||||||
|
|
||||||
res["body"] = olmMessage.mCipherText
|
res["body"] = olmMessage.mCipherText
|
||||||
@ -384,7 +384,7 @@ internal class MXOlmDevice(
|
|||||||
try {
|
try {
|
||||||
payloadString = mxOlmSession.olmSession.decryptMessage(olmMessage)
|
payloadString = mxOlmSession.olmSession.decryptMessage(olmMessage)
|
||||||
mxOlmSession.onMessageReceived()
|
mxOlmSession.onMessageReceived()
|
||||||
mStore.storeSession(mxOlmSession, theirDeviceIdentityKey)
|
store.storeSession(mxOlmSession, theirDeviceIdentityKey)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Timber.e(e, "## decryptMessage() : decryptMessage failed " + e.message)
|
Timber.e(e, "## decryptMessage() : decryptMessage failed " + e.message)
|
||||||
}
|
}
|
||||||
@ -424,7 +424,7 @@ internal class MXOlmDevice(
|
|||||||
var session: OlmOutboundGroupSession? = null
|
var session: OlmOutboundGroupSession? = null
|
||||||
try {
|
try {
|
||||||
session = OlmOutboundGroupSession()
|
session = OlmOutboundGroupSession()
|
||||||
mOutboundGroupSessionStore[session.sessionIdentifier()] = session
|
outboundGroupSessionStore[session.sessionIdentifier()] = session
|
||||||
return session.sessionIdentifier()
|
return session.sessionIdentifier()
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Timber.e(e, "createOutboundGroupSession " + e.message)
|
Timber.e(e, "createOutboundGroupSession " + e.message)
|
||||||
@ -444,7 +444,7 @@ internal class MXOlmDevice(
|
|||||||
fun getSessionKey(sessionId: String): String? {
|
fun getSessionKey(sessionId: String): String? {
|
||||||
if (!TextUtils.isEmpty(sessionId)) {
|
if (!TextUtils.isEmpty(sessionId)) {
|
||||||
try {
|
try {
|
||||||
return mOutboundGroupSessionStore[sessionId]!!.sessionKey()
|
return outboundGroupSessionStore[sessionId]!!.sessionKey()
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Timber.e(e, "## getSessionKey() : failed " + e.message)
|
Timber.e(e, "## getSessionKey() : failed " + e.message)
|
||||||
}
|
}
|
||||||
@ -461,7 +461,7 @@ internal class MXOlmDevice(
|
|||||||
*/
|
*/
|
||||||
fun getMessageIndex(sessionId: String): Int {
|
fun getMessageIndex(sessionId: String): Int {
|
||||||
return if (!TextUtils.isEmpty(sessionId)) {
|
return if (!TextUtils.isEmpty(sessionId)) {
|
||||||
mOutboundGroupSessionStore[sessionId]!!.messageIndex()
|
outboundGroupSessionStore[sessionId]!!.messageIndex()
|
||||||
} else 0
|
} else 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -475,7 +475,7 @@ internal class MXOlmDevice(
|
|||||||
fun encryptGroupMessage(sessionId: String, payloadString: String): String? {
|
fun encryptGroupMessage(sessionId: String, payloadString: String): String? {
|
||||||
if (!TextUtils.isEmpty(sessionId) && !TextUtils.isEmpty(payloadString)) {
|
if (!TextUtils.isEmpty(sessionId) && !TextUtils.isEmpty(payloadString)) {
|
||||||
try {
|
try {
|
||||||
return mOutboundGroupSessionStore[sessionId]!!.encryptMessage(payloadString)
|
return outboundGroupSessionStore[sessionId]!!.encryptMessage(payloadString)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Timber.e(e, "## encryptGroupMessage() : failed " + e.message)
|
Timber.e(e, "## encryptGroupMessage() : failed " + e.message)
|
||||||
}
|
}
|
||||||
@ -547,7 +547,7 @@ internal class MXOlmDevice(
|
|||||||
session.mKeysClaimed = keysClaimed
|
session.mKeysClaimed = keysClaimed
|
||||||
session.mForwardingCurve25519KeyChain = forwardingCurve25519KeyChain
|
session.mForwardingCurve25519KeyChain = forwardingCurve25519KeyChain
|
||||||
|
|
||||||
mStore.storeInboundGroupSessions(listOf(session))
|
store.storeInboundGroupSessions(listOf(session))
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@ -609,7 +609,7 @@ internal class MXOlmDevice(
|
|||||||
sessions.add(session)
|
sessions.add(session)
|
||||||
}
|
}
|
||||||
|
|
||||||
mStore.storeInboundGroupSessions(sessions)
|
store.storeInboundGroupSessions(sessions)
|
||||||
|
|
||||||
return sessions
|
return sessions
|
||||||
}
|
}
|
||||||
@ -622,7 +622,7 @@ internal class MXOlmDevice(
|
|||||||
*/
|
*/
|
||||||
fun removeInboundGroupSession(sessionId: String?, sessionKey: String?) {
|
fun removeInboundGroupSession(sessionId: String?, sessionKey: String?) {
|
||||||
if (null != sessionId && null != sessionKey) {
|
if (null != sessionId && null != sessionKey) {
|
||||||
mStore.removeInboundGroupSession(sessionId, sessionKey)
|
store.removeInboundGroupSession(sessionId, sessionKey)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -660,41 +660,41 @@ internal class MXOlmDevice(
|
|||||||
|
|
||||||
if (null != decryptResult) {
|
if (null != decryptResult) {
|
||||||
if (null != timeline) {
|
if (null != timeline) {
|
||||||
if (!mInboundGroupSessionMessageIndexes.containsKey(timeline)) {
|
if (!inboundGroupSessionMessageIndexes.containsKey(timeline)) {
|
||||||
mInboundGroupSessionMessageIndexes[timeline] = HashMap()
|
inboundGroupSessionMessageIndexes[timeline] = HashMap()
|
||||||
}
|
}
|
||||||
|
|
||||||
val messageIndexKey = senderKey + "|" + sessionId + "|" + decryptResult.mIndex
|
val messageIndexKey = senderKey + "|" + sessionId + "|" + decryptResult.mIndex
|
||||||
|
|
||||||
if (null != mInboundGroupSessionMessageIndexes[timeline]!![messageIndexKey]) {
|
if (null != inboundGroupSessionMessageIndexes[timeline]!![messageIndexKey]) {
|
||||||
val reason = String.format(MXCryptoError.DUPLICATE_MESSAGE_INDEX_REASON, decryptResult.mIndex)
|
val reason = String.format(MXCryptoError.DUPLICATE_MESSAGE_INDEX_REASON, decryptResult.mIndex)
|
||||||
Timber.e("## decryptGroupMessage() : $reason")
|
Timber.e("## decryptGroupMessage() : $reason")
|
||||||
throw MXDecryptionException(MXCryptoError(MXCryptoError.DUPLICATED_MESSAGE_INDEX_ERROR_CODE,
|
throw MXDecryptionException(MXCryptoError(MXCryptoError.DUPLICATED_MESSAGE_INDEX_ERROR_CODE,
|
||||||
MXCryptoError.UNABLE_TO_DECRYPT, reason))
|
MXCryptoError.UNABLE_TO_DECRYPT, reason))
|
||||||
}
|
}
|
||||||
|
|
||||||
mInboundGroupSessionMessageIndexes[timeline]!!.put(messageIndexKey, true)
|
inboundGroupSessionMessageIndexes[timeline]!!.put(messageIndexKey, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
mStore.storeInboundGroupSessions(listOf(session))
|
store.storeInboundGroupSessions(listOf(session))
|
||||||
try {
|
try {
|
||||||
val adapter = MoshiProvider.providesMoshi().adapter<JsonDict>(JSON_DICT_PARAMETERIZED_TYPE)
|
val adapter = MoshiProvider.providesMoshi().adapter<JsonDict>(JSON_DICT_PARAMETERIZED_TYPE)
|
||||||
val payloadString = convertFromUTF8(decryptResult.mDecryptedMessage)
|
val payloadString = convertFromUTF8(decryptResult.mDecryptedMessage)
|
||||||
val payload = adapter.fromJson(payloadString)
|
val payload = adapter.fromJson(payloadString)
|
||||||
result.mPayload = payload
|
result.payload = payload
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Timber.e(e, "## decryptGroupMessage() : RLEncoder.encode failed " + e.message)
|
Timber.e(e, "## decryptGroupMessage() : RLEncoder.encode failed " + e.message)
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
if (null == result.mPayload) {
|
if (null == result.payload) {
|
||||||
Timber.e("## decryptGroupMessage() : fails to parse the payload")
|
Timber.e("## decryptGroupMessage() : fails to parse the payload")
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
result.mKeysClaimed = session.mKeysClaimed
|
result.keysClaimed = session.mKeysClaimed
|
||||||
result.mSenderKey = senderKey
|
result.senderKey = senderKey
|
||||||
result.mForwardingCurve25519KeyChain = session.mForwardingCurve25519KeyChain
|
result.forwardingCurve25519KeyChain = session.mForwardingCurve25519KeyChain
|
||||||
} else {
|
} else {
|
||||||
Timber.e("## decryptGroupMessage() : failed to decode the message")
|
Timber.e("## decryptGroupMessage() : failed to decode the message")
|
||||||
throw MXDecryptionException(MXCryptoError(MXCryptoError.OLM_ERROR_CODE, errorMessage, null))
|
throw MXDecryptionException(MXCryptoError(MXCryptoError.OLM_ERROR_CODE, errorMessage, null))
|
||||||
@ -707,7 +707,7 @@ internal class MXOlmDevice(
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Timber.e("## decryptGroupMessage() : Cannot retrieve inbound group session $sessionId")
|
Timber.e("## decryptGroupMessage() : Cannot retrieve inbound group session $sessionId")
|
||||||
throw MXDecryptionException(mInboundGroupSessionWithIdError)
|
throw MXDecryptionException(inboundGroupSessionWithIdError)
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
@ -720,7 +720,7 @@ internal class MXOlmDevice(
|
|||||||
*/
|
*/
|
||||||
fun resetReplayAttackCheckInTimeline(timeline: String?) {
|
fun resetReplayAttackCheckInTimeline(timeline: String?) {
|
||||||
if (null != timeline) {
|
if (null != timeline) {
|
||||||
mInboundGroupSessionMessageIndexes.remove(timeline)
|
inboundGroupSessionMessageIndexes.remove(timeline)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -737,7 +737,7 @@ internal class MXOlmDevice(
|
|||||||
@Throws(Exception::class)
|
@Throws(Exception::class)
|
||||||
fun verifySignature(key: String, jsonDictionary: Map<String, Any>, signature: String) {
|
fun verifySignature(key: String, jsonDictionary: Map<String, Any>, signature: String) {
|
||||||
// Check signature on the canonical version of the JSON
|
// Check signature on the canonical version of the JSON
|
||||||
mOlmUtility!!.verifyEd25519Signature(signature, key, MoshiProvider.getCanonicalJson<Map<*, *>>(Map::class.java, jsonDictionary))
|
olmUtility!!.verifyEd25519Signature(signature, key, MoshiProvider.getCanonicalJson<Map<*, *>>(Map::class.java, jsonDictionary))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -747,7 +747,7 @@ internal class MXOlmDevice(
|
|||||||
* @return the base64-encoded hash value.
|
* @return the base64-encoded hash value.
|
||||||
*/
|
*/
|
||||||
fun sha256(message: String): String {
|
fun sha256(message: String): String {
|
||||||
return mOlmUtility!!.sha256(convertToUTF8(message))
|
return olmUtility!!.sha256(convertToUTF8(message))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -760,14 +760,14 @@ internal class MXOlmDevice(
|
|||||||
private fun getSessionForDevice(theirDeviceIdentityKey: String, sessionId: String): MXOlmSession? {
|
private fun getSessionForDevice(theirDeviceIdentityKey: String, sessionId: String): MXOlmSession? {
|
||||||
// sanity check
|
// sanity check
|
||||||
return if (!TextUtils.isEmpty(theirDeviceIdentityKey) && !TextUtils.isEmpty(sessionId)) {
|
return if (!TextUtils.isEmpty(theirDeviceIdentityKey) && !TextUtils.isEmpty(sessionId)) {
|
||||||
mStore.getDeviceSession(sessionId, theirDeviceIdentityKey)
|
store.getDeviceSession(sessionId, theirDeviceIdentityKey)
|
||||||
} else null
|
} else null
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extract an InboundGroupSession from the session store and do some check.
|
* Extract an InboundGroupSession from the session store and do some check.
|
||||||
* mInboundGroupSessionWithIdError describes the failure reason.
|
* inboundGroupSessionWithIdError describes the failure reason.
|
||||||
*
|
*
|
||||||
* @param roomId the room where the session is used.
|
* @param roomId the room where the session is used.
|
||||||
* @param sessionId the session identifier.
|
* @param sessionId the session identifier.
|
||||||
@ -775,9 +775,9 @@ internal class MXOlmDevice(
|
|||||||
* @return the inbound group session.
|
* @return the inbound group session.
|
||||||
*/
|
*/
|
||||||
fun getInboundGroupSession(sessionId: String?, senderKey: String?, roomId: String?): MXOlmInboundGroupSession2? {
|
fun getInboundGroupSession(sessionId: String?, senderKey: String?, roomId: String?): MXOlmInboundGroupSession2? {
|
||||||
mInboundGroupSessionWithIdError = null
|
inboundGroupSessionWithIdError = null
|
||||||
|
|
||||||
val session = mStore.getInboundGroupSession(sessionId!!, senderKey!!)
|
val session = store.getInboundGroupSession(sessionId!!, senderKey!!)
|
||||||
|
|
||||||
if (null != session) {
|
if (null != session) {
|
||||||
// Check that the room id matches the original one for the session. This stops
|
// Check that the room id matches the original one for the session. This stops
|
||||||
@ -785,12 +785,12 @@ internal class MXOlmDevice(
|
|||||||
if (!TextUtils.equals(roomId, session.mRoomId)) {
|
if (!TextUtils.equals(roomId, session.mRoomId)) {
|
||||||
val errorDescription = String.format(MXCryptoError.INBOUND_SESSION_MISMATCH_ROOM_ID_REASON, roomId, session.mRoomId)
|
val errorDescription = String.format(MXCryptoError.INBOUND_SESSION_MISMATCH_ROOM_ID_REASON, roomId, session.mRoomId)
|
||||||
Timber.e("## getInboundGroupSession() : $errorDescription")
|
Timber.e("## getInboundGroupSession() : $errorDescription")
|
||||||
mInboundGroupSessionWithIdError = MXCryptoError(MXCryptoError.INBOUND_SESSION_MISMATCH_ROOM_ID_ERROR_CODE,
|
inboundGroupSessionWithIdError = MXCryptoError(MXCryptoError.INBOUND_SESSION_MISMATCH_ROOM_ID_ERROR_CODE,
|
||||||
MXCryptoError.UNABLE_TO_DECRYPT, errorDescription)
|
MXCryptoError.UNABLE_TO_DECRYPT, errorDescription)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Timber.e("## getInboundGroupSession() : Cannot retrieve inbound group session $sessionId")
|
Timber.e("## getInboundGroupSession() : Cannot retrieve inbound group session $sessionId")
|
||||||
mInboundGroupSessionWithIdError = MXCryptoError(MXCryptoError.UNKNOWN_INBOUND_SESSION_ID_ERROR_CODE,
|
inboundGroupSessionWithIdError = MXCryptoError(MXCryptoError.UNKNOWN_INBOUND_SESSION_ID_ERROR_CODE,
|
||||||
MXCryptoError.UNKNOWN_INBOUND_SESSION_ID_REASON, null)
|
MXCryptoError.UNKNOWN_INBOUND_SESSION_ID_REASON, null)
|
||||||
}
|
}
|
||||||
return session
|
return session
|
||||||
|
@ -50,7 +50,7 @@ internal class MyDeviceInfoHolder(
|
|||||||
myDevice.keys = keys
|
myDevice.keys = keys
|
||||||
|
|
||||||
myDevice.algorithms = MXCryptoAlgorithms.supportedAlgorithms()
|
myDevice.algorithms = MXCryptoAlgorithms.supportedAlgorithms()
|
||||||
myDevice.mVerified = MXDeviceInfo.DEVICE_VERIFICATION_VERIFIED
|
myDevice.verified = MXDeviceInfo.DEVICE_VERIFICATION_VERIFIED
|
||||||
|
|
||||||
// Add our own deviceinfo to the store
|
// Add our own deviceinfo to the store
|
||||||
val endToEndDevicesForUser = cryptoStore.getUserDevices(credentials.userId)
|
val endToEndDevicesForUser = cryptoStore.getUserDevices(credentials.userId)
|
||||||
|
@ -16,15 +16,13 @@
|
|||||||
|
|
||||||
package im.vector.matrix.android.internal.crypto
|
package im.vector.matrix.android.internal.crypto
|
||||||
|
|
||||||
import im.vector.matrix.android.api.MatrixCallback
|
import arrow.core.Try
|
||||||
|
import arrow.instances.`try`.applicativeError.handleError
|
||||||
import im.vector.matrix.android.api.auth.data.Credentials
|
import im.vector.matrix.android.api.auth.data.Credentials
|
||||||
import im.vector.matrix.android.internal.crypto.model.MXKey
|
import im.vector.matrix.android.internal.crypto.model.MXKey
|
||||||
import im.vector.matrix.android.internal.crypto.model.rest.KeysUploadResponse
|
import im.vector.matrix.android.internal.crypto.model.rest.KeysUploadResponse
|
||||||
import im.vector.matrix.android.internal.crypto.tasks.UploadKeysTask
|
import im.vector.matrix.android.internal.crypto.tasks.UploadKeysTask
|
||||||
import im.vector.matrix.android.internal.di.MoshiProvider
|
import im.vector.matrix.android.internal.di.MoshiProvider
|
||||||
import im.vector.matrix.android.internal.task.TaskExecutor
|
|
||||||
import im.vector.matrix.android.internal.task.TaskThread
|
|
||||||
import im.vector.matrix.android.internal.task.configureWith
|
|
||||||
import org.matrix.olm.OlmAccount
|
import org.matrix.olm.OlmAccount
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.util.*
|
import java.util.*
|
||||||
@ -33,15 +31,13 @@ internal class OneTimeKeysUploader(
|
|||||||
private val mCredentials: Credentials,
|
private val mCredentials: Credentials,
|
||||||
private val mOlmDevice: MXOlmDevice,
|
private val mOlmDevice: MXOlmDevice,
|
||||||
private val mObjectSigner: ObjectSigner,
|
private val mObjectSigner: ObjectSigner,
|
||||||
private val mUploadKeysTask: UploadKeysTask,
|
private val mUploadKeysTask: UploadKeysTask
|
||||||
private val mTaskExecutor: TaskExecutor
|
|
||||||
) {
|
) {
|
||||||
// tell if there is a OTK check in progress
|
// tell if there is a OTK check in progress
|
||||||
private var mOneTimeKeyCheckInProgress = false
|
private var mOneTimeKeyCheckInProgress = false
|
||||||
|
|
||||||
// last OTK check timestamp
|
// last OTK check timestamp
|
||||||
private var mLastOneTimeKeyCheck: Long = 0
|
private var mLastOneTimeKeyCheck: Long = 0
|
||||||
|
|
||||||
private var mOneTimeKeyCount: Int? = null
|
private var mOneTimeKeyCount: Int? = null
|
||||||
|
|
||||||
var mLastPublishedOneTimeKeys: Map<String, Map<String, String>>? = null
|
var mLastPublishedOneTimeKeys: Map<String, Map<String, String>>? = null
|
||||||
@ -60,23 +56,17 @@ internal class OneTimeKeysUploader(
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if the OTK must be uploaded.
|
* Check if the OTK must be uploaded.
|
||||||
*
|
|
||||||
* @param callback the asynchronous callback
|
|
||||||
*/
|
*/
|
||||||
fun maybeUploadOneTimeKeys(callback: MatrixCallback<Unit>? = null) {
|
suspend fun maybeUploadOneTimeKeys(): Try<Unit> {
|
||||||
if (mOneTimeKeyCheckInProgress) {
|
if (mOneTimeKeyCheckInProgress) {
|
||||||
callback?.onSuccess(Unit)
|
return Try.just(Unit)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (System.currentTimeMillis() - mLastOneTimeKeyCheck < ONE_TIME_KEY_UPLOAD_PERIOD) {
|
if (System.currentTimeMillis() - mLastOneTimeKeyCheck < ONE_TIME_KEY_UPLOAD_PERIOD) {
|
||||||
// we've done a key upload recently.
|
// we've done a key upload recently.
|
||||||
callback?.onSuccess(Unit)
|
return Try.just(Unit)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mLastOneTimeKeyCheck = System.currentTimeMillis()
|
mLastOneTimeKeyCheck = System.currentTimeMillis()
|
||||||
|
|
||||||
mOneTimeKeyCheckInProgress = true
|
mOneTimeKeyCheckInProgress = true
|
||||||
|
|
||||||
// We then check how many keys we can store in the Account object.
|
// We then check how many keys we can store in the Account object.
|
||||||
@ -89,17 +79,13 @@ internal class OneTimeKeysUploader(
|
|||||||
// discard the oldest private keys first. This will eventually clean
|
// discard the oldest private keys first. This will eventually clean
|
||||||
// out stale private keys that won't receive a message.
|
// out stale private keys that won't receive a message.
|
||||||
val keyLimit = Math.floor(maxOneTimeKeys / 2.0).toInt()
|
val keyLimit = Math.floor(maxOneTimeKeys / 2.0).toInt()
|
||||||
|
if (mOneTimeKeyCount != null) {
|
||||||
if (null != mOneTimeKeyCount) {
|
return uploadOTK(mOneTimeKeyCount!!, keyLimit)
|
||||||
uploadOTK(mOneTimeKeyCount!!, keyLimit, callback)
|
|
||||||
} else {
|
} else {
|
||||||
// ask the server how many keys we have
|
// ask the server how many keys we have
|
||||||
mUploadKeysTask
|
val uploadKeysParams = UploadKeysTask.Params(null, null, mCredentials.deviceId!!)
|
||||||
.configureWith(UploadKeysTask.Params(null, null, mCredentials.deviceId!!))
|
return mUploadKeysTask.execute(uploadKeysParams)
|
||||||
.executeOn(TaskThread.ENCRYPTION)
|
.flatMap {
|
||||||
.dispatchTo(object : MatrixCallback<KeysUploadResponse> {
|
|
||||||
|
|
||||||
override fun onSuccess(data: KeysUploadResponse) {
|
|
||||||
// We need to keep a pool of one time public keys on the server so that
|
// 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
|
// other devices can start conversations with us. But we can only store
|
||||||
// a finite number of private keys in the olm Account object.
|
// a finite number of private keys in the olm Account object.
|
||||||
@ -113,20 +99,14 @@ internal class OneTimeKeysUploader(
|
|||||||
// these factors.
|
// these factors.
|
||||||
// TODO Why we do not set mOneTimeKeyCount here?
|
// TODO Why we do not set mOneTimeKeyCount here?
|
||||||
// TODO This is not needed anymore, see https://github.com/matrix-org/matrix-js-sdk/pull/493 (TODO on iOS also)
|
// TODO This is not needed anymore, see https://github.com/matrix-org/matrix-js-sdk/pull/493 (TODO on iOS also)
|
||||||
val keyCount = data.oneTimeKeyCountsForAlgorithm(MXKey.KEY_SIGNED_CURVE_25519_TYPE)
|
val keyCount = it.oneTimeKeyCountsForAlgorithm(MXKey.KEY_SIGNED_CURVE_25519_TYPE)
|
||||||
uploadOTK(keyCount, keyLimit, callback)
|
uploadOTK(keyCount, keyLimit)
|
||||||
}
|
}
|
||||||
|
.handleError {
|
||||||
override fun onFailure(failure: Throwable) {
|
Timber.e(it, "## uploadKeys() : failed")
|
||||||
Timber.e(failure, "## uploadKeys() : failed")
|
|
||||||
|
|
||||||
mOneTimeKeyCount = null
|
mOneTimeKeyCount = null
|
||||||
mOneTimeKeyCheckInProgress = false
|
mOneTimeKeyCheckInProgress = false
|
||||||
|
|
||||||
callback?.onFailure(failure)
|
|
||||||
}
|
}
|
||||||
})
|
|
||||||
.executeBy(mTaskExecutor)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -135,42 +115,40 @@ internal class OneTimeKeysUploader(
|
|||||||
*
|
*
|
||||||
* @param keyCount the key count
|
* @param keyCount the key count
|
||||||
* @param keyLimit the limit
|
* @param keyLimit the limit
|
||||||
* @param callback the asynchronous callback
|
|
||||||
*/
|
*/
|
||||||
private fun uploadOTK(keyCount: Int, keyLimit: Int, callback: MatrixCallback<Unit>?) {
|
private suspend fun uploadOTK(keyCount: Int, keyLimit: Int): Try<Unit> {
|
||||||
uploadLoop(keyCount, keyLimit, object : MatrixCallback<Unit> {
|
return uploadLoop(keyCount, keyLimit)
|
||||||
private fun uploadKeysDone(errorMessage: String?) {
|
|
||||||
if (null != errorMessage) {
|
|
||||||
Timber.e("## maybeUploadOneTimeKeys() : failed $errorMessage")
|
|
||||||
}
|
|
||||||
mOneTimeKeyCount = null
|
|
||||||
mOneTimeKeyCheckInProgress = false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSuccess(data: Unit) {
|
/**
|
||||||
Timber.v("## maybeUploadOneTimeKeys() : succeeded")
|
* OTK upload loop
|
||||||
uploadKeysDone(null)
|
*
|
||||||
|
* @param keyCount the number of key to generate
|
||||||
callback?.onSuccess(Unit)
|
* @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)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onFailure(failure: Throwable) {
|
val keysThisLoop = Math.min(keyLimit - keyCount, ONE_TIME_KEY_GENERATION_MAX_NUMBER)
|
||||||
uploadKeysDone(failure.message)
|
mOlmDevice.generateOneTimeKeys(keysThisLoop)
|
||||||
|
return uploadOneTimeKeys()
|
||||||
callback?.onFailure(failure)
|
.flatMap {
|
||||||
|
if (it.hasOneTimeKeyCountsForAlgorithm(MXKey.KEY_SIGNED_CURVE_25519_TYPE)) {
|
||||||
|
uploadLoop(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"))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Upload my user's one time keys.
|
* Upload my user's one time keys.
|
||||||
* This method must called on getEncryptingThreadHandler() thread.
|
|
||||||
* The callback will called on UI thread.
|
|
||||||
*
|
|
||||||
* @param callback the asynchronous callback
|
|
||||||
*/
|
*/
|
||||||
private fun uploadOneTimeKeys(callback: MatrixCallback<KeysUploadResponse>?) {
|
private suspend fun uploadOneTimeKeys(): Try<KeysUploadResponse> {
|
||||||
val oneTimeKeys = mOlmDevice.getOneTimeKeys()
|
val oneTimeKeys = mOlmDevice.getOneTimeKeys()
|
||||||
val oneTimeJson = HashMap<String, Any>()
|
val oneTimeJson = HashMap<String, Any>()
|
||||||
|
|
||||||
@ -192,57 +170,14 @@ internal class OneTimeKeysUploader(
|
|||||||
|
|
||||||
// For now, we set the device id explicitly, as we may not be using the
|
// For now, we set the device id explicitly, as we may not be using the
|
||||||
// same one as used in login.
|
// same one as used in login.
|
||||||
mUploadKeysTask
|
val uploadParams = UploadKeysTask.Params(null, oneTimeJson, mCredentials.deviceId!!)
|
||||||
.configureWith(UploadKeysTask.Params(null, oneTimeJson, mCredentials.deviceId!!))
|
return mUploadKeysTask
|
||||||
.executeOn(TaskThread.ENCRYPTION)
|
.execute(uploadParams)
|
||||||
.dispatchTo(object : MatrixCallback<KeysUploadResponse> {
|
.map {
|
||||||
override fun onSuccess(data: KeysUploadResponse) {
|
|
||||||
mLastPublishedOneTimeKeys = oneTimeKeys
|
mLastPublishedOneTimeKeys = oneTimeKeys
|
||||||
mOlmDevice.markKeysAsPublished()
|
mOlmDevice.markKeysAsPublished()
|
||||||
|
it
|
||||||
callback?.onSuccess(data)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onFailure(failure: Throwable) {
|
|
||||||
callback?.onFailure(failure)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.executeBy(mTaskExecutor)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* OTK upload loop
|
|
||||||
*
|
|
||||||
* @param keyCount the number of key to generate
|
|
||||||
* @param keyLimit the limit
|
|
||||||
* @param callback the asynchronous callback
|
|
||||||
*/
|
|
||||||
private fun uploadLoop(keyCount: Int, keyLimit: Int, callback: MatrixCallback<Unit>) {
|
|
||||||
if (keyLimit <= keyCount) {
|
|
||||||
// If we don't need to generate any more keys then we are done.
|
|
||||||
callback.onSuccess(Unit)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
val keysThisLoop = Math.min(keyLimit - keyCount, ONE_TIME_KEY_GENERATION_MAX_NUMBER)
|
|
||||||
|
|
||||||
mOlmDevice.generateOneTimeKeys(keysThisLoop)
|
|
||||||
|
|
||||||
uploadOneTimeKeys(object : MatrixCallback<KeysUploadResponse> {
|
|
||||||
override fun onSuccess(data: KeysUploadResponse) {
|
|
||||||
if (data.hasOneTimeKeyCountsForAlgorithm(MXKey.KEY_SIGNED_CURVE_25519_TYPE)) {
|
|
||||||
uploadLoop(data.oneTimeKeyCountsForAlgorithm(MXKey.KEY_SIGNED_CURVE_25519_TYPE), keyLimit, callback)
|
|
||||||
} else {
|
|
||||||
Timber.e("## uploadLoop() : response for uploading keys does not contain one_time_key_counts.signed_curve25519")
|
|
||||||
callback.onFailure(
|
|
||||||
Exception("response for uploading keys does not contain one_time_key_counts.signed_curve25519"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onFailure(failure: Throwable) {
|
|
||||||
callback.onFailure(failure)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -27,6 +27,7 @@ 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.store.IMXCryptoStore
|
||||||
import im.vector.matrix.android.internal.crypto.tasks.SendToDeviceTask
|
import im.vector.matrix.android.internal.crypto.tasks.SendToDeviceTask
|
||||||
import im.vector.matrix.android.internal.task.TaskExecutor
|
import im.vector.matrix.android.internal.task.TaskExecutor
|
||||||
|
import im.vector.matrix.android.internal.task.TaskThread
|
||||||
import im.vector.matrix.android.internal.task.configureWith
|
import im.vector.matrix.android.internal.task.configureWith
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.util.*
|
import java.util.*
|
||||||
@ -87,7 +88,7 @@ internal class OutgoingRoomKeyRequestManager(
|
|||||||
OutgoingRoomKeyRequest(requestBody, recipients, makeTxnId(), OutgoingRoomKeyRequest.RequestState.UNSENT))
|
OutgoingRoomKeyRequest(requestBody, recipients, makeTxnId(), OutgoingRoomKeyRequest.RequestState.UNSENT))
|
||||||
|
|
||||||
|
|
||||||
if (req!!.mState === OutgoingRoomKeyRequest.RequestState.UNSENT) {
|
if (req?.mState == OutgoingRoomKeyRequest.RequestState.UNSENT) {
|
||||||
startTimer()
|
startTimer()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,12 +24,12 @@ import timber.log.Timber
|
|||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
internal class RoomDecryptorProvider(
|
internal class RoomDecryptorProvider(
|
||||||
private val mMXOlmDecryptionFactory: MXOlmDecryptionFactory,
|
private val olmDecryptionFactory: MXOlmDecryptionFactory,
|
||||||
private val mMXMegolmDecryptionFactory: MXMegolmDecryptionFactory
|
private val megolmDecryptionFactory: MXMegolmDecryptionFactory
|
||||||
) {
|
) {
|
||||||
|
|
||||||
// A map from algorithm to MXDecrypting instance, for each room
|
// A map from algorithm to MXDecrypting instance, for each room
|
||||||
private val mRoomDecryptors: MutableMap<String /* room id */, MutableMap<String /* algorithm */, IMXDecrypting>> = HashMap()
|
private val roomDecryptors: MutableMap<String /* room id */, MutableMap<String /* algorithm */, IMXDecrypting>> = HashMap()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a decryptor for a given room and algorithm.
|
* Get a decryptor for a given room and algorithm.
|
||||||
@ -43,44 +43,36 @@ internal class RoomDecryptorProvider(
|
|||||||
*/
|
*/
|
||||||
fun getOrCreateRoomDecryptor(roomId: String?, algorithm: String?): IMXDecrypting? {
|
fun getOrCreateRoomDecryptor(roomId: String?, algorithm: String?): IMXDecrypting? {
|
||||||
// sanity check
|
// sanity check
|
||||||
if (TextUtils.isEmpty(algorithm)) {
|
if (algorithm.isNullOrEmpty() || roomId.isNullOrEmpty()) {
|
||||||
Timber.e("## getRoomDecryptor() : null algorithm")
|
Timber.e("## getRoomDecryptor() : null algorithm")
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
var alg: IMXDecrypting? = null
|
var alg: IMXDecrypting?
|
||||||
|
synchronized(roomDecryptors) {
|
||||||
if (!TextUtils.isEmpty(roomId)) {
|
if (!roomDecryptors.containsKey(roomId)) {
|
||||||
synchronized(mRoomDecryptors) {
|
roomDecryptors[roomId!!] = HashMap()
|
||||||
if (!mRoomDecryptors.containsKey(roomId)) {
|
|
||||||
mRoomDecryptors[roomId!!] = HashMap()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
alg = mRoomDecryptors[roomId]!![algorithm]
|
alg = roomDecryptors[roomId]!![algorithm]
|
||||||
}
|
}
|
||||||
|
if (alg != null) {
|
||||||
if (null != alg) {
|
|
||||||
return alg
|
return alg
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
val decryptingClass = MXCryptoAlgorithms.hasDecryptorClassForAlgorithm(algorithm)
|
val decryptingClass = MXCryptoAlgorithms.hasDecryptorClassForAlgorithm(algorithm)
|
||||||
|
|
||||||
if (decryptingClass) {
|
if (decryptingClass) {
|
||||||
alg = when (algorithm) {
|
alg = when (algorithm) {
|
||||||
MXCRYPTO_ALGORITHM_MEGOLM -> mMXMegolmDecryptionFactory.create()
|
MXCRYPTO_ALGORITHM_MEGOLM -> megolmDecryptionFactory.create()
|
||||||
else -> mMXOlmDecryptionFactory.create()
|
else -> olmDecryptionFactory.create()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (null != alg) {
|
if (null != alg) {
|
||||||
if (!TextUtils.isEmpty(roomId)) {
|
if (!TextUtils.isEmpty(roomId)) {
|
||||||
synchronized(mRoomDecryptors) {
|
synchronized(roomDecryptors) {
|
||||||
mRoomDecryptors[roomId]!!.put(algorithm!!, alg!!)
|
roomDecryptors[roomId]!!.put(algorithm!!, alg!!)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return alg
|
return alg
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,7 +80,6 @@ internal class RoomDecryptorProvider(
|
|||||||
if (roomId == null || algorithm == null) {
|
if (roomId == null || algorithm == null) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
return roomDecryptors[roomId]?.get(algorithm)
|
||||||
return mRoomDecryptors[roomId]?.get(algorithm)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -18,7 +18,11 @@ package im.vector.matrix.android.internal.crypto.actions
|
|||||||
|
|
||||||
import im.vector.matrix.android.api.MatrixCallback
|
import im.vector.matrix.android.api.MatrixCallback
|
||||||
import im.vector.matrix.android.api.listeners.ProgressListener
|
import im.vector.matrix.android.api.listeners.ProgressListener
|
||||||
import im.vector.matrix.android.internal.crypto.*
|
import im.vector.matrix.android.internal.crypto.CryptoAsyncHelper
|
||||||
|
import im.vector.matrix.android.internal.crypto.MXOlmDevice
|
||||||
|
import im.vector.matrix.android.internal.crypto.MegolmSessionData
|
||||||
|
import im.vector.matrix.android.internal.crypto.OutgoingRoomKeyRequestManager
|
||||||
|
import im.vector.matrix.android.internal.crypto.RoomDecryptorProvider
|
||||||
import im.vector.matrix.android.internal.crypto.model.ImportRoomKeysResult
|
import im.vector.matrix.android.internal.crypto.model.ImportRoomKeysResult
|
||||||
import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyRequestBody
|
import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyRequestBody
|
||||||
import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
|
import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
|
||||||
@ -53,7 +57,6 @@ internal class MegolmSessionDataImporter(private val mOlmDevice: MXOlmDevice,
|
|||||||
progressListener.onProgress(0, 100)
|
progressListener.onProgress(0, 100)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val sessions = mOlmDevice.importInboundGroupSessions(megolmSessionsData)
|
val sessions = mOlmDevice.importInboundGroupSessions(megolmSessionsData)
|
||||||
|
|
||||||
for (megolmSessionData in megolmSessionsData) {
|
for (megolmSessionData in megolmSessionsData) {
|
||||||
@ -65,7 +68,7 @@ internal class MegolmSessionDataImporter(private val mOlmDevice: MXOlmDevice,
|
|||||||
if (null != decrypting) {
|
if (null != decrypting) {
|
||||||
try {
|
try {
|
||||||
val sessionId = megolmSessionData.sessionId
|
val sessionId = megolmSessionData.sessionId
|
||||||
Timber.v("## importRoomKeys retrieve mSenderKey " + megolmSessionData.senderKey + " sessionId " + sessionId)
|
Timber.v("## importRoomKeys retrieve senderKey " + megolmSessionData.senderKey + " sessionId " + sessionId)
|
||||||
|
|
||||||
totalNumbersOfImportedKeys++
|
totalNumbersOfImportedKeys++
|
||||||
|
|
||||||
|
@ -34,8 +34,8 @@ internal class SetDeviceVerificationAction(private val mCryptoStore: IMXCryptoSt
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (device.mVerified != verificationStatus) {
|
if (device.verified != verificationStatus) {
|
||||||
device.mVerified = verificationStatus
|
device.verified = verificationStatus
|
||||||
mCryptoStore.storeUserDevice(userId, device)
|
mCryptoStore.storeUserDevice(userId, device)
|
||||||
|
|
||||||
if (userId == mCredentials.userId) {
|
if (userId == mCredentials.userId) {
|
||||||
|
@ -25,21 +25,21 @@ data class MXDecryptionResult(
|
|||||||
/**
|
/**
|
||||||
* The decrypted payload (with properties 'type', 'content')
|
* The decrypted payload (with properties 'type', 'content')
|
||||||
*/
|
*/
|
||||||
var mPayload: JsonDict? = null,
|
var payload: JsonDict? = null,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* keys that the sender of the event claims ownership of:
|
* keys that the sender of the event claims ownership of:
|
||||||
* map from key type to base64-encoded key.
|
* map from key type to base64-encoded key.
|
||||||
*/
|
*/
|
||||||
var mKeysClaimed: Map<String, String>? = null,
|
var keysClaimed: Map<String, String>? = null,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The curve25519 key that the sender of the event is known to have ownership of.
|
* The curve25519 key that the sender of the event is known to have ownership of.
|
||||||
*/
|
*/
|
||||||
var mSenderKey: String? = null,
|
var senderKey: String? = null,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Devices which forwarded this session to us (normally empty).
|
* Devices which forwarded this session to us (normally empty).
|
||||||
*/
|
*/
|
||||||
var mForwardingCurve25519KeyChain: List<String>? = null
|
var forwardingCurve25519KeyChain: List<String>? = null
|
||||||
)
|
)
|
@ -74,7 +74,6 @@ internal class MXMegolmDecryption(private val credentials: Credentials,
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Throws(MXDecryptionException::class)
|
@Throws(MXDecryptionException::class)
|
||||||
|
|
||||||
private fun decryptEvent(event: Event, timeline: String, requestKeysOnFail: Boolean): MXEventDecryptionResult? {
|
private fun decryptEvent(event: Event, timeline: String, requestKeysOnFail: Boolean): MXEventDecryptionResult? {
|
||||||
val encryptedEventContent = event.content.toModel<EncryptedEventContent>()!!
|
val encryptedEventContent = event.content.toModel<EncryptedEventContent>()!!
|
||||||
if (TextUtils.isEmpty(encryptedEventContent.senderKey) || TextUtils.isEmpty(encryptedEventContent.sessionId) || TextUtils.isEmpty(encryptedEventContent.ciphertext)) {
|
if (TextUtils.isEmpty(encryptedEventContent.senderKey) || TextUtils.isEmpty(encryptedEventContent.sessionId) || TextUtils.isEmpty(encryptedEventContent.ciphertext)) {
|
||||||
@ -93,17 +92,17 @@ internal class MXMegolmDecryption(private val credentials: Credentials,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// the decryption succeeds
|
// the decryption succeeds
|
||||||
if (decryptGroupMessageResult?.mPayload != null && cryptoError == null) {
|
if (decryptGroupMessageResult?.payload != null && cryptoError == null) {
|
||||||
eventDecryptionResult = MXEventDecryptionResult()
|
eventDecryptionResult = MXEventDecryptionResult()
|
||||||
|
|
||||||
eventDecryptionResult.clearEvent = decryptGroupMessageResult.mPayload
|
eventDecryptionResult.clearEvent = decryptGroupMessageResult.payload
|
||||||
eventDecryptionResult.senderCurve25519Key = decryptGroupMessageResult.mSenderKey
|
eventDecryptionResult.senderCurve25519Key = decryptGroupMessageResult.senderKey
|
||||||
|
|
||||||
if (null != decryptGroupMessageResult.mKeysClaimed) {
|
if (null != decryptGroupMessageResult.keysClaimed) {
|
||||||
eventDecryptionResult.claimedEd25519Key = decryptGroupMessageResult.mKeysClaimed!!["ed25519"]
|
eventDecryptionResult.claimedEd25519Key = decryptGroupMessageResult.keysClaimed!!["ed25519"]
|
||||||
}
|
}
|
||||||
|
|
||||||
eventDecryptionResult.forwardingCurve25519KeyChain = decryptGroupMessageResult.mForwardingCurve25519KeyChain!!
|
eventDecryptionResult.forwardingCurve25519KeyChain = decryptGroupMessageResult.forwardingCurve25519KeyChain!!
|
||||||
} else if (cryptoError != null) {
|
} else if (cryptoError != null) {
|
||||||
if (cryptoError.isOlmError) {
|
if (cryptoError.isOlmError) {
|
||||||
if (TextUtils.equals("UNKNOWN_MESSAGE_INDEX", cryptoError.message)) {
|
if (TextUtils.equals("UNKNOWN_MESSAGE_INDEX", cryptoError.message)) {
|
||||||
|
@ -387,7 +387,7 @@ internal class KeysBackup(
|
|||||||
callback: MatrixCallback<KeysBackupVersionTrust>) {
|
callback: MatrixCallback<KeysBackupVersionTrust>) {
|
||||||
// TODO Validate with François that this is correct
|
// TODO Validate with François that this is correct
|
||||||
object : Task<KeysVersionResult, KeysBackupVersionTrust> {
|
object : Task<KeysVersionResult, KeysBackupVersionTrust> {
|
||||||
override fun execute(params: KeysVersionResult): Try<KeysBackupVersionTrust> {
|
override suspend fun execute(params: KeysVersionResult): Try<KeysBackupVersionTrust> {
|
||||||
return Try {
|
return Try {
|
||||||
getKeysBackupTrustBg(params)
|
getKeysBackupTrustBg(params)
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@ internal class DefaultCreateKeysBackupVersionTask(private val roomKeysApi: RoomK
|
|||||||
: CreateKeysBackupVersionTask {
|
: CreateKeysBackupVersionTask {
|
||||||
|
|
||||||
|
|
||||||
override fun execute(params: CreateKeysBackupVersionBody): Try<KeysVersion> {
|
override suspend fun execute(params: CreateKeysBackupVersionBody): Try<KeysVersion> {
|
||||||
return executeRequest {
|
return executeRequest {
|
||||||
apiCall = roomKeysApi.createKeysBackupVersion(params)
|
apiCall = roomKeysApi.createKeysBackupVersion(params)
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,7 @@ internal interface DeleteBackupTask : Task<DeleteBackupTask.Params, Unit> {
|
|||||||
internal class DefaultDeleteBackupTask(private val roomKeysApi: RoomKeysApi)
|
internal class DefaultDeleteBackupTask(private val roomKeysApi: RoomKeysApi)
|
||||||
: DeleteBackupTask {
|
: DeleteBackupTask {
|
||||||
|
|
||||||
override fun execute(params: DeleteBackupTask.Params): Try<Unit> {
|
override suspend fun execute(params: DeleteBackupTask.Params): Try<Unit> {
|
||||||
return executeRequest {
|
return executeRequest {
|
||||||
apiCall = roomKeysApi.deleteBackup(
|
apiCall = roomKeysApi.deleteBackup(
|
||||||
params.version)
|
params.version)
|
||||||
|
@ -33,7 +33,7 @@ internal interface DeleteRoomSessionDataTask : Task<DeleteRoomSessionDataTask.Pa
|
|||||||
internal class DefaultDeleteRoomSessionDataTask(private val roomKeysApi: RoomKeysApi)
|
internal class DefaultDeleteRoomSessionDataTask(private val roomKeysApi: RoomKeysApi)
|
||||||
: DeleteRoomSessionDataTask {
|
: DeleteRoomSessionDataTask {
|
||||||
|
|
||||||
override fun execute(params: DeleteRoomSessionDataTask.Params): Try<Unit> {
|
override suspend fun execute(params: DeleteRoomSessionDataTask.Params): Try<Unit> {
|
||||||
return executeRequest {
|
return executeRequest {
|
||||||
apiCall = roomKeysApi.deleteRoomSessionData(
|
apiCall = roomKeysApi.deleteRoomSessionData(
|
||||||
params.roomId,
|
params.roomId,
|
||||||
|
@ -32,7 +32,7 @@ internal interface DeleteRoomSessionsDataTask : Task<DeleteRoomSessionsDataTask.
|
|||||||
internal class DefaultDeleteRoomSessionsDataTask(private val roomKeysApi: RoomKeysApi)
|
internal class DefaultDeleteRoomSessionsDataTask(private val roomKeysApi: RoomKeysApi)
|
||||||
: DeleteRoomSessionsDataTask {
|
: DeleteRoomSessionsDataTask {
|
||||||
|
|
||||||
override fun execute(params: DeleteRoomSessionsDataTask.Params): Try<Unit> {
|
override suspend fun execute(params: DeleteRoomSessionsDataTask.Params): Try<Unit> {
|
||||||
return executeRequest {
|
return executeRequest {
|
||||||
apiCall = roomKeysApi.deleteRoomSessionsData(
|
apiCall = roomKeysApi.deleteRoomSessionsData(
|
||||||
params.roomId,
|
params.roomId,
|
||||||
|
@ -31,7 +31,7 @@ internal interface DeleteSessionsDataTask : Task<DeleteSessionsDataTask.Params,
|
|||||||
internal class DefaultDeleteSessionsDataTask(private val roomKeysApi: RoomKeysApi)
|
internal class DefaultDeleteSessionsDataTask(private val roomKeysApi: RoomKeysApi)
|
||||||
: DeleteSessionsDataTask {
|
: DeleteSessionsDataTask {
|
||||||
|
|
||||||
override fun execute(params: DeleteSessionsDataTask.Params): Try<Unit> {
|
override suspend fun execute(params: DeleteSessionsDataTask.Params): Try<Unit> {
|
||||||
return executeRequest {
|
return executeRequest {
|
||||||
apiCall = roomKeysApi.deleteSessionsData(
|
apiCall = roomKeysApi.deleteSessionsData(
|
||||||
params.version)
|
params.version)
|
||||||
|
@ -29,7 +29,7 @@ internal class DefaultGetKeysBackupLastVersionTask(private val roomKeysApi: Room
|
|||||||
: GetKeysBackupLastVersionTask {
|
: GetKeysBackupLastVersionTask {
|
||||||
|
|
||||||
|
|
||||||
override fun execute(params: Unit): Try<KeysVersionResult> {
|
override suspend fun execute(params: Unit): Try<KeysVersionResult> {
|
||||||
return executeRequest {
|
return executeRequest {
|
||||||
apiCall = roomKeysApi.getKeysBackupLastVersion()
|
apiCall = roomKeysApi.getKeysBackupLastVersion()
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,7 @@ internal class DefaultGetKeysBackupVersionTask(private val roomKeysApi: RoomKeys
|
|||||||
: GetKeysBackupVersionTask {
|
: GetKeysBackupVersionTask {
|
||||||
|
|
||||||
|
|
||||||
override fun execute(params: String): Try<KeysVersionResult> {
|
override suspend fun execute(params: String): Try<KeysVersionResult> {
|
||||||
return executeRequest {
|
return executeRequest {
|
||||||
apiCall = roomKeysApi.getKeysBackupVersion(params)
|
apiCall = roomKeysApi.getKeysBackupVersion(params)
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,7 @@ internal interface GetRoomSessionDataTask : Task<GetRoomSessionDataTask.Params,
|
|||||||
internal class DefaultGetRoomSessionDataTask(private val roomKeysApi: RoomKeysApi)
|
internal class DefaultGetRoomSessionDataTask(private val roomKeysApi: RoomKeysApi)
|
||||||
: GetRoomSessionDataTask {
|
: GetRoomSessionDataTask {
|
||||||
|
|
||||||
override fun execute(params: GetRoomSessionDataTask.Params): Try<KeyBackupData> {
|
override suspend fun execute(params: GetRoomSessionDataTask.Params): Try<KeyBackupData> {
|
||||||
return executeRequest {
|
return executeRequest {
|
||||||
apiCall = roomKeysApi.getRoomSessionData(
|
apiCall = roomKeysApi.getRoomSessionData(
|
||||||
params.roomId,
|
params.roomId,
|
||||||
|
@ -34,7 +34,7 @@ internal interface GetRoomSessionsDataTask : Task<GetRoomSessionsDataTask.Params
|
|||||||
internal class DefaultGetRoomSessionsDataTask(private val roomKeysApi: RoomKeysApi)
|
internal class DefaultGetRoomSessionsDataTask(private val roomKeysApi: RoomKeysApi)
|
||||||
: GetRoomSessionsDataTask {
|
: GetRoomSessionsDataTask {
|
||||||
|
|
||||||
override fun execute(params: GetRoomSessionsDataTask.Params): Try<RoomKeysBackupData> {
|
override suspend fun execute(params: GetRoomSessionsDataTask.Params): Try<RoomKeysBackupData> {
|
||||||
return executeRequest {
|
return executeRequest {
|
||||||
apiCall = roomKeysApi.getRoomSessionsData(
|
apiCall = roomKeysApi.getRoomSessionsData(
|
||||||
params.roomId,
|
params.roomId,
|
||||||
|
@ -32,7 +32,7 @@ internal interface GetSessionsDataTask : Task<GetSessionsDataTask.Params, KeysBa
|
|||||||
internal class DefaultGetSessionsDataTask(private val roomKeysApi: RoomKeysApi)
|
internal class DefaultGetSessionsDataTask(private val roomKeysApi: RoomKeysApi)
|
||||||
: GetSessionsDataTask {
|
: GetSessionsDataTask {
|
||||||
|
|
||||||
override fun execute(params: GetSessionsDataTask.Params): Try<KeysBackupData> {
|
override suspend fun execute(params: GetSessionsDataTask.Params): Try<KeysBackupData> {
|
||||||
return executeRequest {
|
return executeRequest {
|
||||||
apiCall = roomKeysApi.getSessionsData(
|
apiCall = roomKeysApi.getSessionsData(
|
||||||
params.version)
|
params.version)
|
||||||
|
@ -36,7 +36,7 @@ internal interface StoreRoomSessionDataTask : Task<StoreRoomSessionDataTask.Para
|
|||||||
internal class DefaultStoreRoomSessionDataTask(private val roomKeysApi: RoomKeysApi)
|
internal class DefaultStoreRoomSessionDataTask(private val roomKeysApi: RoomKeysApi)
|
||||||
: StoreRoomSessionDataTask {
|
: StoreRoomSessionDataTask {
|
||||||
|
|
||||||
override fun execute(params: StoreRoomSessionDataTask.Params): Try<BackupKeysResult> {
|
override suspend fun execute(params: StoreRoomSessionDataTask.Params): Try<BackupKeysResult> {
|
||||||
return executeRequest {
|
return executeRequest {
|
||||||
apiCall = roomKeysApi.storeRoomSessionData(
|
apiCall = roomKeysApi.storeRoomSessionData(
|
||||||
params.roomId,
|
params.roomId,
|
||||||
|
@ -35,7 +35,7 @@ internal interface StoreRoomSessionsDataTask : Task<StoreRoomSessionsDataTask.Pa
|
|||||||
internal class DefaultStoreRoomSessionsDataTask(private val roomKeysApi: RoomKeysApi)
|
internal class DefaultStoreRoomSessionsDataTask(private val roomKeysApi: RoomKeysApi)
|
||||||
: StoreRoomSessionsDataTask {
|
: StoreRoomSessionsDataTask {
|
||||||
|
|
||||||
override fun execute(params: StoreRoomSessionsDataTask.Params): Try<BackupKeysResult> {
|
override suspend fun execute(params: StoreRoomSessionsDataTask.Params): Try<BackupKeysResult> {
|
||||||
return executeRequest {
|
return executeRequest {
|
||||||
apiCall = roomKeysApi.storeRoomSessionsData(
|
apiCall = roomKeysApi.storeRoomSessionsData(
|
||||||
params.roomId,
|
params.roomId,
|
||||||
|
@ -34,7 +34,7 @@ internal interface StoreSessionsDataTask : Task<StoreSessionsDataTask.Params, Ba
|
|||||||
internal class DefaultStoreSessionsDataTask(private val roomKeysApi: RoomKeysApi)
|
internal class DefaultStoreSessionsDataTask(private val roomKeysApi: RoomKeysApi)
|
||||||
: StoreSessionsDataTask {
|
: StoreSessionsDataTask {
|
||||||
|
|
||||||
override fun execute(params: StoreSessionsDataTask.Params): Try<BackupKeysResult> {
|
override suspend fun execute(params: StoreSessionsDataTask.Params): Try<BackupKeysResult> {
|
||||||
return executeRequest {
|
return executeRequest {
|
||||||
apiCall = roomKeysApi.storeSessionsData(
|
apiCall = roomKeysApi.storeSessionsData(
|
||||||
params.version,
|
params.version,
|
||||||
|
@ -34,7 +34,7 @@ internal class DefaultUpdateKeysBackupVersionTask(private val roomKeysApi: RoomK
|
|||||||
: UpdateKeysBackupVersionTask {
|
: UpdateKeysBackupVersionTask {
|
||||||
|
|
||||||
|
|
||||||
override fun execute(params: UpdateKeysBackupVersionTask.Params): Try<Unit> {
|
override suspend fun execute(params: UpdateKeysBackupVersionTask.Params): Try<Unit> {
|
||||||
return executeRequest {
|
return executeRequest {
|
||||||
apiCall = roomKeysApi.updateKeysBackupVersion(params.version, params.keysBackupVersionBody)
|
apiCall = roomKeysApi.updateKeysBackupVersion(params.version, params.keysBackupVersionBody)
|
||||||
}
|
}
|
||||||
|
@ -69,7 +69,7 @@ data class MXDeviceInfo(
|
|||||||
/**
|
/**
|
||||||
* Verification state of this device.
|
* Verification state of this device.
|
||||||
*/
|
*/
|
||||||
var mVerified: Int = DEVICE_VERIFICATION_UNKNOWN
|
var verified: Int = DEVICE_VERIFICATION_UNKNOWN
|
||||||
) : Serializable {
|
) : Serializable {
|
||||||
/**
|
/**
|
||||||
* Tells if the device is unknown
|
* Tells if the device is unknown
|
||||||
@ -77,7 +77,7 @@ data class MXDeviceInfo(
|
|||||||
* @return true if the device is unknown
|
* @return true if the device is unknown
|
||||||
*/
|
*/
|
||||||
val isUnknown: Boolean
|
val isUnknown: Boolean
|
||||||
get() = mVerified == DEVICE_VERIFICATION_UNKNOWN
|
get() = verified == DEVICE_VERIFICATION_UNKNOWN
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tells if the device is verified.
|
* Tells if the device is verified.
|
||||||
@ -85,7 +85,7 @@ data class MXDeviceInfo(
|
|||||||
* @return true if the device is verified
|
* @return true if the device is verified
|
||||||
*/
|
*/
|
||||||
val isVerified: Boolean
|
val isVerified: Boolean
|
||||||
get() = mVerified == DEVICE_VERIFICATION_VERIFIED
|
get() = verified == DEVICE_VERIFICATION_VERIFIED
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tells if the device is unverified.
|
* Tells if the device is unverified.
|
||||||
@ -93,7 +93,7 @@ data class MXDeviceInfo(
|
|||||||
* @return true if the device is unverified
|
* @return true if the device is unverified
|
||||||
*/
|
*/
|
||||||
val isUnverified: Boolean
|
val isUnverified: Boolean
|
||||||
get() = mVerified == DEVICE_VERIFICATION_UNVERIFIED
|
get() = verified == DEVICE_VERIFICATION_UNVERIFIED
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tells if the device is blocked.
|
* Tells if the device is blocked.
|
||||||
@ -101,7 +101,7 @@ data class MXDeviceInfo(
|
|||||||
* @return true if the device is blocked
|
* @return true if the device is blocked
|
||||||
*/
|
*/
|
||||||
val isBlocked: Boolean
|
val isBlocked: Boolean
|
||||||
get() = mVerified == DEVICE_VERIFICATION_BLOCKED
|
get() = verified == DEVICE_VERIFICATION_BLOCKED
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the fingerprint
|
* @return the fingerprint
|
||||||
|
@ -37,7 +37,7 @@ internal interface ClaimOneTimeKeysForUsersDeviceTask : Task<ClaimOneTimeKeysFor
|
|||||||
internal class DefaultClaimOneTimeKeysForUsersDevice(private val cryptoApi: CryptoApi)
|
internal class DefaultClaimOneTimeKeysForUsersDevice(private val cryptoApi: CryptoApi)
|
||||||
: ClaimOneTimeKeysForUsersDeviceTask {
|
: ClaimOneTimeKeysForUsersDeviceTask {
|
||||||
|
|
||||||
override fun execute(params: ClaimOneTimeKeysForUsersDeviceTask.Params): Try<MXUsersDevicesMap<MXKey>> {
|
override suspend fun execute(params: ClaimOneTimeKeysForUsersDeviceTask.Params): Try<MXUsersDevicesMap<MXKey>> {
|
||||||
val body = KeysClaimBody(oneTimeKeys = params.usersDevicesKeyTypesMap.map)
|
val body = KeysClaimBody(oneTimeKeys = params.usersDevicesKeyTypesMap.map)
|
||||||
|
|
||||||
return executeRequest<KeysClaimResponse> {
|
return executeRequest<KeysClaimResponse> {
|
||||||
|
@ -32,7 +32,7 @@ internal interface DeleteDeviceTask : Task<DeleteDeviceTask.Params, Unit> {
|
|||||||
internal class DefaultDeleteDeviceTask(private val cryptoApi: CryptoApi)
|
internal class DefaultDeleteDeviceTask(private val cryptoApi: CryptoApi)
|
||||||
: DeleteDeviceTask {
|
: DeleteDeviceTask {
|
||||||
|
|
||||||
override fun execute(params: DeleteDeviceTask.Params): Try<Unit> {
|
override suspend fun execute(params: DeleteDeviceTask.Params): Try<Unit> {
|
||||||
return executeRequest {
|
return executeRequest {
|
||||||
apiCall = cryptoApi.deleteDevice(params.deviceId,
|
apiCall = cryptoApi.deleteDevice(params.deviceId,
|
||||||
DeleteDeviceParams())
|
DeleteDeviceParams())
|
||||||
|
@ -36,7 +36,7 @@ internal interface DownloadKeysForUsersTask : Task<DownloadKeysForUsersTask.Para
|
|||||||
internal class DefaultDownloadKeysForUsers(private val cryptoApi: CryptoApi)
|
internal class DefaultDownloadKeysForUsers(private val cryptoApi: CryptoApi)
|
||||||
: DownloadKeysForUsersTask {
|
: DownloadKeysForUsersTask {
|
||||||
|
|
||||||
override fun execute(params: DownloadKeysForUsersTask.Params): Try<KeysQueryResponse> {
|
override suspend fun execute(params: DownloadKeysForUsersTask.Params): Try<KeysQueryResponse> {
|
||||||
val downloadQuery = HashMap<String, Map<String, Any>>()
|
val downloadQuery = HashMap<String, Map<String, Any>>()
|
||||||
|
|
||||||
if (null != params.userIds) {
|
if (null != params.userIds) {
|
||||||
|
@ -27,7 +27,7 @@ internal interface GetDevicesTask : Task<Unit, DevicesListResponse>
|
|||||||
internal class DefaultGetDevicesTask(private val cryptoApi: CryptoApi)
|
internal class DefaultGetDevicesTask(private val cryptoApi: CryptoApi)
|
||||||
: GetDevicesTask {
|
: GetDevicesTask {
|
||||||
|
|
||||||
override fun execute(params: Unit): Try<DevicesListResponse> {
|
override suspend fun execute(params: Unit): Try<DevicesListResponse> {
|
||||||
return executeRequest {
|
return executeRequest {
|
||||||
apiCall = cryptoApi.getDevices()
|
apiCall = cryptoApi.getDevices()
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,7 @@ internal interface GetKeyChangesTask : Task<GetKeyChangesTask.Params, KeyChanges
|
|||||||
internal class DefaultGetKeyChangesTask(private val cryptoApi: CryptoApi)
|
internal class DefaultGetKeyChangesTask(private val cryptoApi: CryptoApi)
|
||||||
: GetKeyChangesTask {
|
: GetKeyChangesTask {
|
||||||
|
|
||||||
override fun execute(params: GetKeyChangesTask.Params): Try<KeyChangesResponse> {
|
override suspend fun execute(params: GetKeyChangesTask.Params): Try<KeyChangesResponse> {
|
||||||
return executeRequest {
|
return executeRequest {
|
||||||
apiCall = cryptoApi.getKeyChanges(params.from,
|
apiCall = cryptoApi.getKeyChanges(params.from,
|
||||||
params.to)
|
params.to)
|
||||||
|
@ -38,7 +38,7 @@ internal interface SendToDeviceTask : Task<SendToDeviceTask.Params, Unit> {
|
|||||||
internal class DefaultSendToDeviceTask(private val cryptoApi: CryptoApi)
|
internal class DefaultSendToDeviceTask(private val cryptoApi: CryptoApi)
|
||||||
: SendToDeviceTask {
|
: SendToDeviceTask {
|
||||||
|
|
||||||
override fun execute(params: SendToDeviceTask.Params): Try<Unit> {
|
override suspend fun execute(params: SendToDeviceTask.Params): Try<Unit> {
|
||||||
val sendToDeviceBody = SendToDeviceBody()
|
val sendToDeviceBody = SendToDeviceBody()
|
||||||
sendToDeviceBody.messages = params.contentMap.map
|
sendToDeviceBody.messages = params.contentMap.map
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ internal interface SetDeviceNameTask : Task<SetDeviceNameTask.Params, Unit> {
|
|||||||
internal class DefaultSetDeviceNameTask(private val cryptoApi: CryptoApi)
|
internal class DefaultSetDeviceNameTask(private val cryptoApi: CryptoApi)
|
||||||
: SetDeviceNameTask {
|
: SetDeviceNameTask {
|
||||||
|
|
||||||
override fun execute(params: SetDeviceNameTask.Params): Try<Unit> {
|
override suspend fun execute(params: SetDeviceNameTask.Params): Try<Unit> {
|
||||||
val body = UpdateDeviceInfoBody(
|
val body = UpdateDeviceInfoBody(
|
||||||
displayName = if (TextUtils.isEmpty(params.deviceName)) "" else params.deviceName
|
displayName = if (TextUtils.isEmpty(params.deviceName)) "" else params.deviceName
|
||||||
)
|
)
|
||||||
|
@ -39,7 +39,7 @@ internal interface UploadKeysTask : Task<UploadKeysTask.Params, KeysUploadRespon
|
|||||||
internal class DefaultUploadKeysTask(private val cryptoApi: CryptoApi)
|
internal class DefaultUploadKeysTask(private val cryptoApi: CryptoApi)
|
||||||
: UploadKeysTask {
|
: UploadKeysTask {
|
||||||
|
|
||||||
override fun execute(params: UploadKeysTask.Params): Try<KeysUploadResponse> {
|
override suspend fun execute(params: UploadKeysTask.Params): Try<KeysUploadResponse> {
|
||||||
val encodedDeviceId = convertToUTF8(params.deviceId)
|
val encodedDeviceId = convertToUTF8(params.deviceId)
|
||||||
|
|
||||||
val body = KeysUploadBody()
|
val body = KeysUploadBody()
|
||||||
|
@ -33,11 +33,18 @@ import im.vector.matrix.android.internal.crypto.MyDeviceInfoHolder
|
|||||||
import im.vector.matrix.android.internal.crypto.actions.SetDeviceVerificationAction
|
import im.vector.matrix.android.internal.crypto.actions.SetDeviceVerificationAction
|
||||||
import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo
|
import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo
|
||||||
import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap
|
import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap
|
||||||
import im.vector.matrix.android.internal.crypto.model.rest.*
|
import im.vector.matrix.android.internal.crypto.model.rest.KeyVerificationAccept
|
||||||
|
import im.vector.matrix.android.internal.crypto.model.rest.KeyVerificationCancel
|
||||||
|
import im.vector.matrix.android.internal.crypto.model.rest.KeyVerificationKey
|
||||||
|
import im.vector.matrix.android.internal.crypto.model.rest.KeyVerificationMac
|
||||||
|
import im.vector.matrix.android.internal.crypto.model.rest.KeyVerificationStart
|
||||||
import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
|
import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
|
||||||
import im.vector.matrix.android.internal.crypto.tasks.SendToDeviceTask
|
import im.vector.matrix.android.internal.crypto.tasks.SendToDeviceTask
|
||||||
import im.vector.matrix.android.internal.task.TaskExecutor
|
import im.vector.matrix.android.internal.task.TaskExecutor
|
||||||
import im.vector.matrix.android.internal.task.configureWith
|
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 timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.collections.HashMap
|
import kotlin.collections.HashMap
|
||||||
@ -53,6 +60,7 @@ internal class DefaultSasVerificationService(private val mCredentials: Credentia
|
|||||||
private val deviceListManager: DeviceListManager,
|
private val deviceListManager: DeviceListManager,
|
||||||
private val setDeviceVerificationAction: SetDeviceVerificationAction,
|
private val setDeviceVerificationAction: SetDeviceVerificationAction,
|
||||||
private val mSendToDeviceTask: SendToDeviceTask,
|
private val mSendToDeviceTask: SendToDeviceTask,
|
||||||
|
private val coroutineDispatchers: MatrixCoroutineDispatchers,
|
||||||
private val mTaskExecutor: TaskExecutor)
|
private val mTaskExecutor: TaskExecutor)
|
||||||
: VerificationTransaction.Listener, SasVerificationService {
|
: VerificationTransaction.Listener, SasVerificationService {
|
||||||
|
|
||||||
@ -63,8 +71,7 @@ internal class DefaultSasVerificationService(private val mCredentials: Credentia
|
|||||||
|
|
||||||
// Event received from the sync
|
// Event received from the sync
|
||||||
fun onToDeviceEvent(event: Event) {
|
fun onToDeviceEvent(event: Event) {
|
||||||
CryptoAsyncHelper.getDecryptBackgroundHandler().post {
|
CoroutineScope(coroutineDispatchers.crypto).launch {
|
||||||
// TODO We are already in a BG thread
|
|
||||||
when (event.getClearType()) {
|
when (event.getClearType()) {
|
||||||
EventType.KEY_VERIFICATION_START -> {
|
EventType.KEY_VERIFICATION_START -> {
|
||||||
onStartRequestReceived(event)
|
onStartRequestReceived(event)
|
||||||
@ -143,7 +150,7 @@ internal class DefaultSasVerificationService(private val mCredentials: Credentia
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onStartRequestReceived(event: Event) {
|
private suspend fun onStartRequestReceived(event: Event) {
|
||||||
val startReq = event.getClearContent().toModel<KeyVerificationStart>()!!
|
val startReq = event.getClearContent().toModel<KeyVerificationStart>()!!
|
||||||
|
|
||||||
val otherUserId = event.sender
|
val otherUserId = event.sender
|
||||||
@ -208,30 +215,24 @@ internal class DefaultSasVerificationService(private val mCredentials: Credentia
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun checkKeysAreDownloaded(otherUserId: String,
|
private suspend fun checkKeysAreDownloaded(otherUserId: String,
|
||||||
startReq: KeyVerificationStart,
|
startReq: KeyVerificationStart,
|
||||||
success: (MXUsersDevicesMap<MXDeviceInfo>) -> Unit,
|
success: (MXUsersDevicesMap<MXDeviceInfo>) -> Unit,
|
||||||
error: () -> Unit) {
|
error: () -> Unit) {
|
||||||
deviceListManager.downloadKeys(listOf(otherUserId), true, object : MatrixCallback<MXUsersDevicesMap<MXDeviceInfo>> {
|
deviceListManager.downloadKeys(listOf(otherUserId), true)
|
||||||
override fun onFailure(failure: Throwable) {
|
.fold(
|
||||||
CryptoAsyncHelper.getDecryptBackgroundHandler().post {
|
{ error() },
|
||||||
error()
|
{
|
||||||
}
|
if (it != null && it.getUserDeviceIds(otherUserId).contains(startReq.fromDevice)) {
|
||||||
}
|
success(it)
|
||||||
|
|
||||||
override fun onSuccess(data: MXUsersDevicesMap<MXDeviceInfo>) {
|
|
||||||
CryptoAsyncHelper.getDecryptBackgroundHandler().post {
|
|
||||||
if (data.getUserDeviceIds(otherUserId).contains(startReq.fromDevice)) {
|
|
||||||
success(data)
|
|
||||||
} else {
|
} else {
|
||||||
error()
|
error()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
)
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onCancelReceived(event: Event) {
|
private suspend fun onCancelReceived(event: Event) {
|
||||||
Timber.v("## SAS onCancelReceived")
|
Timber.v("## SAS onCancelReceived")
|
||||||
val cancelReq = event.getClearContent().toModel<KeyVerificationCancel>()!!
|
val cancelReq = event.getClearContent().toModel<KeyVerificationCancel>()!!
|
||||||
|
|
||||||
@ -254,7 +255,7 @@ internal class DefaultSasVerificationService(private val mCredentials: Credentia
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onAcceptReceived(event: Event) {
|
private suspend fun onAcceptReceived(event: Event) {
|
||||||
val acceptReq = event.getClearContent().toModel<KeyVerificationAccept>()!!
|
val acceptReq = event.getClearContent().toModel<KeyVerificationAccept>()!!
|
||||||
|
|
||||||
if (!acceptReq.isValid()) {
|
if (!acceptReq.isValid()) {
|
||||||
@ -278,7 +279,7 @@ internal class DefaultSasVerificationService(private val mCredentials: Credentia
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun onKeyReceived(event: Event) {
|
private suspend fun onKeyReceived(event: Event) {
|
||||||
val keyReq = event.getClearContent().toModel<KeyVerificationKey>()!!
|
val keyReq = event.getClearContent().toModel<KeyVerificationKey>()!!
|
||||||
|
|
||||||
if (!keyReq.isValid()) {
|
if (!keyReq.isValid()) {
|
||||||
@ -299,7 +300,7 @@ internal class DefaultSasVerificationService(private val mCredentials: Credentia
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onMacReceived(event: Event) {
|
private suspend fun onMacReceived(event: Event) {
|
||||||
val macReq = event.getClearContent().toModel<KeyVerificationMac>()!!
|
val macReq = event.getClearContent().toModel<KeyVerificationMac>()!!
|
||||||
|
|
||||||
if (!macReq.isValid()) {
|
if (!macReq.isValid()) {
|
||||||
|
@ -25,7 +25,7 @@ internal interface ClearCacheTask : Task<Unit, Unit>
|
|||||||
|
|
||||||
internal class RealmClearCacheTask(val realmConfiguration: RealmConfiguration) : ClearCacheTask {
|
internal class RealmClearCacheTask(val realmConfiguration: RealmConfiguration) : ClearCacheTask {
|
||||||
|
|
||||||
override fun execute(params: Unit): Try<Unit> {
|
override suspend fun execute(params: Unit): Try<Unit> {
|
||||||
return Try {
|
return Try {
|
||||||
val realm = Realm.getInstance(realmConfiguration)
|
val realm = Realm.getInstance(realmConfiguration)
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ internal class DefaultSaveFilterTask(private val sessionParams: SessionParams,
|
|||||||
private val filterRepository: FilterRepository
|
private val filterRepository: FilterRepository
|
||||||
) : SaveFilterTask {
|
) : SaveFilterTask {
|
||||||
|
|
||||||
override fun execute(params: SaveFilterTask.Params): Try<Unit> {
|
override suspend fun execute(params: SaveFilterTask.Params): Try<Unit> {
|
||||||
return executeRequest<FilterResponse> {
|
return executeRequest<FilterResponse> {
|
||||||
// TODO auto retry
|
// TODO auto retry
|
||||||
apiCall = filterAPI.uploadFilter(sessionParams.credentials.userId, params.filter)
|
apiCall = filterAPI.uploadFilter(sessionParams.credentials.userId, params.filter)
|
||||||
|
@ -43,7 +43,7 @@ internal class DefaultGetGroupDataTask(
|
|||||||
private val monarchy: Monarchy
|
private val monarchy: Monarchy
|
||||||
) : GetGroupDataTask {
|
) : GetGroupDataTask {
|
||||||
|
|
||||||
override fun execute(params: GetGroupDataTask.Params): Try<Unit> {
|
override suspend fun execute(params: GetGroupDataTask.Params): Try<Unit> {
|
||||||
val groupId = params.groupId
|
val groupId = params.groupId
|
||||||
return Try.monad().binding {
|
return Try.monad().binding {
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
package im.vector.matrix.android.internal.session.group
|
package im.vector.matrix.android.internal.session.group
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import androidx.work.CoroutineWorker
|
||||||
import androidx.work.Worker
|
import androidx.work.Worker
|
||||||
import androidx.work.WorkerParameters
|
import androidx.work.WorkerParameters
|
||||||
import arrow.core.Try
|
import arrow.core.Try
|
||||||
@ -27,7 +28,7 @@ import org.koin.standalone.inject
|
|||||||
|
|
||||||
internal class GetGroupDataWorker(context: Context,
|
internal class GetGroupDataWorker(context: Context,
|
||||||
workerParameters: WorkerParameters
|
workerParameters: WorkerParameters
|
||||||
) : Worker(context, workerParameters), MatrixKoinComponent {
|
) : CoroutineWorker(context, workerParameters), MatrixKoinComponent {
|
||||||
|
|
||||||
@JsonClass(generateAdapter = true)
|
@JsonClass(generateAdapter = true)
|
||||||
internal data class Params(
|
internal data class Params(
|
||||||
@ -36,7 +37,7 @@ internal class GetGroupDataWorker(context: Context,
|
|||||||
|
|
||||||
private val getGroupDataTask by inject<GetGroupDataTask>()
|
private val getGroupDataTask by inject<GetGroupDataTask>()
|
||||||
|
|
||||||
override fun doWork(): Result {
|
override suspend fun doWork(): Result {
|
||||||
val params = WorkerParamsFactory.fromData<Params>(inputData)
|
val params = WorkerParamsFactory.fromData<Params>(inputData)
|
||||||
?: return Result.failure()
|
?: return Result.failure()
|
||||||
|
|
||||||
@ -47,7 +48,7 @@ internal class GetGroupDataWorker(context: Context,
|
|||||||
return if (isSuccessful) Result.success() else Result.retry()
|
return if (isSuccessful) Result.success() else Result.retry()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun fetchGroupData(groupId: String): Try<Unit> {
|
private suspend fun fetchGroupData(groupId: String): Try<Unit> {
|
||||||
return getGroupDataTask.execute(GetGroupDataTask.Params(groupId))
|
return getGroupDataTask.execute(GetGroupDataTask.Params(groupId))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,7 +41,7 @@ internal class DefaultCreateRoomTask(private val roomAPI: RoomAPI,
|
|||||||
private val realmConfiguration: RealmConfiguration) : CreateRoomTask {
|
private val realmConfiguration: RealmConfiguration) : CreateRoomTask {
|
||||||
|
|
||||||
|
|
||||||
override fun execute(params: CreateRoomParams): Try<String> {
|
override suspend fun execute(params: CreateRoomParams): Try<String> {
|
||||||
return executeRequest<CreateRoomResponse> {
|
return executeRequest<CreateRoomResponse> {
|
||||||
apiCall = roomAPI.createRoom(params)
|
apiCall = roomAPI.createRoom(params)
|
||||||
}.flatMap { createRoomResponse ->
|
}.flatMap { createRoomResponse ->
|
||||||
|
@ -31,7 +31,7 @@ internal interface InviteTask : Task<InviteTask.Params, Unit> {
|
|||||||
|
|
||||||
internal class DefaultInviteTask(private val roomAPI: RoomAPI) : InviteTask {
|
internal class DefaultInviteTask(private val roomAPI: RoomAPI) : InviteTask {
|
||||||
|
|
||||||
override fun execute(params: InviteTask.Params): Try<Unit> {
|
override suspend fun execute(params: InviteTask.Params): Try<Unit> {
|
||||||
return executeRequest {
|
return executeRequest {
|
||||||
val body = InviteBody(params.userId)
|
val body = InviteBody(params.userId)
|
||||||
apiCall = roomAPI.invite(params.roomId, body)
|
apiCall = roomAPI.invite(params.roomId, body)
|
||||||
|
@ -44,7 +44,7 @@ internal class DefaultLoadRoomMembersTask(private val roomAPI: RoomAPI,
|
|||||||
private val roomSummaryUpdater: RoomSummaryUpdater
|
private val roomSummaryUpdater: RoomSummaryUpdater
|
||||||
) : LoadRoomMembersTask {
|
) : LoadRoomMembersTask {
|
||||||
|
|
||||||
override fun execute(params: LoadRoomMembersTask.Params): Try<Boolean> {
|
override suspend fun execute(params: LoadRoomMembersTask.Params): Try<Boolean> {
|
||||||
return if (areAllMembersAlreadyLoaded(params.roomId)) {
|
return if (areAllMembersAlreadyLoaded(params.roomId)) {
|
||||||
Try.just(true)
|
Try.just(true)
|
||||||
} else {
|
} else {
|
||||||
|
@ -50,7 +50,7 @@ internal class DefaultSetReadMarkersTask(private val roomAPI: RoomAPI,
|
|||||||
private val monarchy: Monarchy
|
private val monarchy: Monarchy
|
||||||
) : SetReadMarkersTask {
|
) : SetReadMarkersTask {
|
||||||
|
|
||||||
override fun execute(params: SetReadMarkersTask.Params): Try<Unit> {
|
override suspend fun execute(params: SetReadMarkersTask.Params): Try<Unit> {
|
||||||
val markers = HashMap<String, String>()
|
val markers = HashMap<String, String>()
|
||||||
if (params.fullyReadEventId != null && MatrixPatterns.isEventId(params.fullyReadEventId)) {
|
if (params.fullyReadEventId != null && MatrixPatterns.isEventId(params.fullyReadEventId)) {
|
||||||
markers[READ_MARKER] = params.fullyReadEventId
|
markers[READ_MARKER] = params.fullyReadEventId
|
||||||
|
@ -30,7 +30,7 @@ internal interface SendStateTask : Task<SendStateTask.Params, Unit> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal class DefaultSendStateTask(private val roomAPI: RoomAPI) : SendStateTask {
|
internal class DefaultSendStateTask(private val roomAPI: RoomAPI) : SendStateTask {
|
||||||
override fun execute(params: SendStateTask.Params): Try<Unit> {
|
override suspend fun execute(params: SendStateTask.Params): Try<Unit> {
|
||||||
return executeRequest {
|
return executeRequest {
|
||||||
apiCall = roomAPI.sendStateEvent(params.roomId, params.eventType, params.body)
|
apiCall = roomAPI.sendStateEvent(params.roomId, params.eventType, params.body)
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,7 @@ internal class DefaultGetContextOfEventTask(private val roomAPI: RoomAPI,
|
|||||||
private val tokenChunkEventPersistor: TokenChunkEventPersistor
|
private val tokenChunkEventPersistor: TokenChunkEventPersistor
|
||||||
) : GetContextOfEventTask {
|
) : GetContextOfEventTask {
|
||||||
|
|
||||||
override fun execute(params: GetContextOfEventTask.Params): Try<TokenChunkEventPersistor.Result> {
|
override suspend fun execute(params: GetContextOfEventTask.Params): Try<TokenChunkEventPersistor.Result> {
|
||||||
val filter = filterRepository.getRoomFilter()
|
val filter = filterRepository.getRoomFilter()
|
||||||
return executeRequest<EventContextResponse> {
|
return executeRequest<EventContextResponse> {
|
||||||
apiCall = roomAPI.getContextOfEvent(params.roomId, params.eventId, 0, filter)
|
apiCall = roomAPI.getContextOfEvent(params.roomId, params.eventId, 0, filter)
|
||||||
|
@ -39,7 +39,7 @@ internal class DefaultPaginationTask(private val roomAPI: RoomAPI,
|
|||||||
private val tokenChunkEventPersistor: TokenChunkEventPersistor
|
private val tokenChunkEventPersistor: TokenChunkEventPersistor
|
||||||
) : PaginationTask {
|
) : PaginationTask {
|
||||||
|
|
||||||
override fun execute(params: PaginationTask.Params): Try<TokenChunkEventPersistor.Result> {
|
override suspend fun execute(params: PaginationTask.Params): Try<TokenChunkEventPersistor.Result> {
|
||||||
val filter = filterRepository.getRoomFilter()
|
val filter = filterRepository.getRoomFilter()
|
||||||
return executeRequest<PaginationResponse> {
|
return executeRequest<PaginationResponse> {
|
||||||
apiCall = roomAPI.getRoomMessagesFrom(params.roomId, params.from, params.direction.value, params.limit, filter)
|
apiCall = roomAPI.getRoomMessagesFrom(params.roomId, params.from, params.direction.value, params.limit, filter)
|
||||||
|
@ -30,7 +30,7 @@ internal class GetEventTask(private val roomAPI: RoomAPI
|
|||||||
val eventId: String
|
val eventId: String
|
||||||
)
|
)
|
||||||
|
|
||||||
override fun execute(params: Params): Try<Event> {
|
override suspend fun execute(params: Params): Try<Event> {
|
||||||
return executeRequest {
|
return executeRequest {
|
||||||
apiCall = roomAPI.getEvent(params.roomId, params.eventId)
|
apiCall = roomAPI.getEvent(params.roomId, params.eventId)
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ internal interface SignOutTask : Task<Unit, Unit>
|
|||||||
internal class DefaultSignOutTask(private val signOutAPI: SignOutAPI,
|
internal class DefaultSignOutTask(private val signOutAPI: SignOutAPI,
|
||||||
private val sessionParamsStore: SessionParamsStore) : SignOutTask {
|
private val sessionParamsStore: SessionParamsStore) : SignOutTask {
|
||||||
|
|
||||||
override fun execute(params: Unit): Try<Unit> {
|
override suspend fun execute(params: Unit): Try<Unit> {
|
||||||
return executeRequest<Unit> {
|
return executeRequest<Unit> {
|
||||||
apiCall = signOutAPI.signOut()
|
apiCall = signOutAPI.signOut()
|
||||||
}.flatMap {
|
}.flatMap {
|
||||||
|
@ -37,7 +37,6 @@ internal class CryptoSyncHandler(private val cryptoManager: CryptoManager,
|
|||||||
toDevice.events?.forEach { event ->
|
toDevice.events?.forEach { event ->
|
||||||
// Decrypt event if necessary
|
// Decrypt event if necessary
|
||||||
decryptEvent(event, null)
|
decryptEvent(event, null)
|
||||||
|
|
||||||
if (TextUtils.equals(event.getClearType(), EventType.MESSAGE)
|
if (TextUtils.equals(event.getClearType(), EventType.MESSAGE)
|
||||||
&& event.mClearEvent?.content?.toModel<MessageContent>()?.type == "m.bad.encrypted") {
|
&& event.mClearEvent?.content?.toModel<MessageContent>()?.type == "m.bad.encrypted") {
|
||||||
Timber.e("## handleToDeviceEvent() : Warning: Unable to decrypt to-device event : " + event.content)
|
Timber.e("## handleToDeviceEvent() : Warning: Unable to decrypt to-device event : " + event.content)
|
||||||
|
@ -40,7 +40,7 @@ internal class DefaultSyncTask(private val syncAPI: SyncAPI,
|
|||||||
) : SyncTask {
|
) : SyncTask {
|
||||||
|
|
||||||
|
|
||||||
override fun execute(params: SyncTask.Params): Try<SyncResponse> {
|
override suspend fun execute(params: SyncTask.Params): Try<SyncResponse> {
|
||||||
val requestParams = HashMap<String, String>()
|
val requestParams = HashMap<String, String>()
|
||||||
var timeout = 0
|
var timeout = 0
|
||||||
if (params.token != null) {
|
if (params.token != null) {
|
||||||
|
@ -35,7 +35,7 @@ internal interface UpdateUserTask : Task<UpdateUserTask.Params, Unit> {
|
|||||||
|
|
||||||
internal class DefaultUpdateUserTask(private val monarchy: Monarchy) : UpdateUserTask {
|
internal class DefaultUpdateUserTask(private val monarchy: Monarchy) : UpdateUserTask {
|
||||||
|
|
||||||
override fun execute(params: UpdateUserTask.Params): Try<Unit> {
|
override suspend fun execute(params: UpdateUserTask.Params): Try<Unit> {
|
||||||
return monarchy.tryTransactionSync { realm ->
|
return monarchy.tryTransactionSync { realm ->
|
||||||
params.eventIds.forEach { eventId ->
|
params.eventIds.forEach { eventId ->
|
||||||
val event = EventEntity.where(realm, eventId).findFirst()?.asDomain()
|
val event = EventEntity.where(realm, eventId).findFirst()?.asDomain()
|
||||||
|
@ -34,7 +34,7 @@ internal data class ConfigurableTask<PARAMS, RESULT>(
|
|||||||
) : Task<PARAMS, RESULT> {
|
) : Task<PARAMS, RESULT> {
|
||||||
|
|
||||||
|
|
||||||
override fun execute(params: PARAMS): Try<RESULT> {
|
override suspend fun execute(params: PARAMS): Try<RESULT> {
|
||||||
return task.execute(params)
|
return task.execute(params)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@ import arrow.core.Try
|
|||||||
|
|
||||||
internal interface Task<PARAMS, RESULT> {
|
internal interface Task<PARAMS, RESULT> {
|
||||||
|
|
||||||
fun execute(params: PARAMS): Try<RESULT>
|
suspend fun execute(params: PARAMS): Try<RESULT>
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,8 +68,7 @@ internal class TaskExecutor(private val coroutineDispatchers: MatrixCoroutineDis
|
|||||||
TaskThread.COMPUTATION -> coroutineDispatchers.computation
|
TaskThread.COMPUTATION -> coroutineDispatchers.computation
|
||||||
TaskThread.IO -> coroutineDispatchers.io
|
TaskThread.IO -> coroutineDispatchers.io
|
||||||
TaskThread.CALLER -> EmptyCoroutineContext
|
TaskThread.CALLER -> EmptyCoroutineContext
|
||||||
TaskThread.ENCRYPTION -> coroutineDispatchers.crypto
|
TaskThread.CRYPTO -> coroutineDispatchers.crypto
|
||||||
TaskThread.DECRYPTION -> coroutineDispatchers.crypto
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -21,6 +21,5 @@ internal enum class TaskThread {
|
|||||||
COMPUTATION,
|
COMPUTATION,
|
||||||
IO,
|
IO,
|
||||||
CALLER,
|
CALLER,
|
||||||
ENCRYPTION,
|
CRYPTO
|
||||||
DECRYPTION
|
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user