Create OneTimeKeysManager

This commit is contained in:
Benoit Marty 2019-05-17 12:39:18 +02:00
parent a2210a6b0d
commit c66e82c4ae
6 changed files with 341 additions and 289 deletions

View File

@ -55,7 +55,6 @@ import im.vector.matrix.android.internal.session.sync.model.SyncResponse
import im.vector.matrix.android.internal.task.TaskExecutor
import im.vector.matrix.android.internal.task.configureWith
import im.vector.matrix.android.internal.util.convertToUTF8
import org.matrix.olm.OlmAccount
import org.matrix.olm.OlmManager
import timber.log.Timber
import java.util.*
@ -84,6 +83,10 @@ internal class CryptoManager(
// The key backup service.
private val mKeysBackup: KeysBackup,
//
private val mObjectSigner: ObjectSigner,
//
private val mOneTimeKeysManager: OneTimeKeysManager,
//
private val roomDecryptorProvider: RoomDecryptorProvider,
// The SAS verification service.
private val mSasVerificationService: DefaultSasVerificationService,
@ -119,8 +122,6 @@ internal class CryptoManager(
*/
private val myDevice: MXDeviceInfo

private var mLastPublishedOneTimeKeys: Map<String, Map<String, String>>? = null

// the encryption is starting
private var mIsStarting: Boolean = false

@ -137,8 +138,6 @@ internal class CryptoManager(
// the UI thread
private val mUIHandler: Handler

private var mOneTimeKeyCount: Int? = null

// TODO
//private val mNetworkListener = object : IMXNetworkEventListener {
// override fun onNetworkConnectionUpdate(isConnected: Boolean) {
@ -163,12 +162,6 @@ internal class CryptoManager(
// Warn the user if some new devices are detected while encrypting a message.
private var mWarnOnUnknownDevices = true

// tell if there is a OTK check in progress
private var mOneTimeKeyCheckInProgress = false

// last OTK check timestamp
private var mLastOneTimeKeyCheck: Long = 0

// Set of parameters used to configure/customize the end-to-end crypto.
private var mCryptoConfig: MXCryptoConfig? = null

@ -421,11 +414,11 @@ internal class CryptoManager(
Timber.d(" - device id : " + mCredentials.deviceId)
Timber.d(" - ed25519 : " + mOlmDevice.deviceEd25519Key)
Timber.d(" - curve25519 : " + mOlmDevice.deviceCurve25519Key)
Timber.d(" - oneTimeKeys: " + mLastPublishedOneTimeKeys)
Timber.d(" - oneTimeKeys: " + mOneTimeKeysManager.mLastPublishedOneTimeKeys)
Timber.d("")

encryptingThreadHandler.post {
maybeUploadOneTimeKeys(object : MatrixCallback<Unit> {
mOneTimeKeysManager.maybeUploadOneTimeKeys(object : MatrixCallback<Unit> {
override fun onSuccess(data: Unit) {
encryptingThreadHandler.post {
// TODO
@ -546,7 +539,7 @@ internal class CryptoManager(

if (null != syncResponse.deviceOneTimeKeysCount) {
val currentCount = syncResponse.deviceOneTimeKeysCount.signedCurve25519 ?: 0
updateOneTimeKeyCount(currentCount)
mOneTimeKeysManager.updateOneTimeKeyCount(currentCount)
}

if (isStarted()) {
@ -555,7 +548,7 @@ internal class CryptoManager(
}

if (!isCatchingUp && isStarted()) {
maybeUploadOneTimeKeys()
mOneTimeKeysManager.maybeUploadOneTimeKeys()

mIncomingRoomKeyRequestManager.processReceivedRoomKeyRequests()
}
@ -578,16 +571,6 @@ internal class CryptoManager(
}
}

/**
* Stores the current one_time_key count which will be handled later (in a call of
* _onSyncCompleted). The count is e.g. coming from a /sync response.
*
* @param currentCount the new count
*/
private fun updateOneTimeKeyCount(currentCount: Int) {
mOneTimeKeyCount = currentCount
}

/**
* Find a device by curve25519 identity key
*
@ -1264,33 +1247,6 @@ internal class CryptoManager(
return res
}

/**
* Sign Object
*
* Example:
* <pre>
* {
* "[MY_USER_ID]": {
* "ed25519:[MY_DEVICE_ID]": "sign(str)"
* }
* }
* </pre>
*
* @param strToSign the String to sign and to include in the Map
* @return a Map (see example)
*/
override fun signObject(strToSign: String): Map<String, Map<String, String>> {
val result = HashMap<String, Map<String, String>>()

val content = HashMap<String, String>()

content["ed25519:" + myDevice.deviceId] = mOlmDevice.signMessage(strToSign)!!

result[myDevice.userId] = content

return result
}

/**
* Handle the 'toDevice' event
*
@ -1416,7 +1372,7 @@ internal class CryptoManager(
// Sign it
val canonicalJson = MoshiProvider.getCanonicalJson(Map::class.java, myDevice.signalableJSONDictionary())

myDevice.signatures = signObject(canonicalJson)
myDevice.signatures = mObjectSigner.signObject(canonicalJson)

// For now, we set the device id explicitly, as we may not be using the
// same one as used in login.
@ -1426,217 +1382,6 @@ internal class CryptoManager(
.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.
mUIHandler.post { 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) {
encryptingThreadHandler.post {
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")
mUIHandler.post {
callback.onFailure(
Exception("response for uploading keys does not contain one_time_key_counts.signed_curve25519"))
}
}
}
}

override fun onFailure(failure: Throwable) {
callback.onFailure(failure)
}
})
}

/**
* Check if the OTK must be uploaded.
*
* @param callback the asynchronous callback
*/
private fun maybeUploadOneTimeKeys(callback: MatrixCallback<Unit>? = null) {
if (mOneTimeKeyCheckInProgress) {
mUIHandler.post {
callback?.onSuccess(Unit)
}
return
}

if (System.currentTimeMillis() - mLastOneTimeKeyCheck < ONE_TIME_KEY_UPLOAD_PERIOD) {
// we've done a key upload recently.
mUIHandler.post {
callback?.onSuccess(Unit)
}
return
}

mLastOneTimeKeyCheck = System.currentTimeMillis()

mOneTimeKeyCheckInProgress = true

// We then check how many keys we can store in the Account object.
val maxOneTimeKeys = mOlmDevice.getMaxNumberOfOneTimeKeys()

// Try to keep at most half that number on the server. This leaves the
// rest of the slots free to hold keys that have been claimed from the
// server but we haven't received a message for.
// If we run out of slots when generating new keys then olm will
// discard the oldest private keys first. This will eventually clean
// out stale private keys that won't receive a message.
val keyLimit = Math.floor(maxOneTimeKeys / 2.0).toInt()

if (null != mOneTimeKeyCount) {
uploadOTK(mOneTimeKeyCount!!, keyLimit, callback)
} else {
// ask the server how many keys we have
mUploadKeysTask
.configureWith(UploadKeysTask.Params(null, null, myDevice.deviceId))
.dispatchTo(object : MatrixCallback<KeysUploadResponse> {

override fun onSuccess(data: KeysUploadResponse) {
encryptingThreadHandler.post {
if (!hasBeenReleased()) {
// 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
// a finite number of private keys in the olm Account object.
// To complicate things further then can be a delay between a device
// claiming a public one time key from the server and it sending us a
// message. We need to keep the corresponding private key locally until
// we receive the message.
// But that message might never arrive leaving us stuck with duff
// private keys clogging up our local storage.
// So we need some kind of enginering compromise to balance all of
// these factors. // TODO Why we do not set mOneTimeKeyCount here?
val keyCount = data.oneTimeKeyCountsForAlgorithm(MXKey.KEY_SIGNED_CURVE_25519_TYPE)
uploadOTK(keyCount, keyLimit, callback)
}
}
}

override fun onFailure(failure: Throwable) {
Timber.e(failure, "## uploadKeys() : failed")

mOneTimeKeyCount = null
mOneTimeKeyCheckInProgress = false

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

/**
* Upload some the OTKs.
*
* @param keyCount the key count
* @param keyLimit the limit
* @param callback the asynchronous callback
*/
private fun uploadOTK(keyCount: Int, keyLimit: Int, callback: MatrixCallback<Unit>?) {
uploadLoop(keyCount, keyLimit, object : MatrixCallback<Unit> {
private fun uploadKeysDone(errorMessage: String?) {
if (null != errorMessage) {
Timber.e("## maybeUploadOneTimeKeys() : failed $errorMessage")
}
mOneTimeKeyCount = null
mOneTimeKeyCheckInProgress = false
}

override fun onSuccess(data: Unit) {
Timber.d("## maybeUploadOneTimeKeys() : succeeded")
uploadKeysDone(null)

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

override fun onFailure(failure: Throwable) {
uploadKeysDone(failure.message)

mUIHandler.post {
callback?.onFailure(failure)
}
}
})

}

/**
* 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>?) {
val oneTimeKeys = mOlmDevice.getOneTimeKeys()
val oneTimeJson = HashMap<String, Any>()

val curve25519Map = oneTimeKeys!![OlmAccount.JSON_KEY_ONE_TIME_KEY]

if (null != curve25519Map) {
for (key_id in curve25519Map.keys) {
val k = HashMap<String, Any>()
k["key"] = curve25519Map[key_id]!!

// the key is also signed
val canonicalJson = MoshiProvider.getCanonicalJson(Map::class.java, k)

k["signatures"] = signObject(canonicalJson)

oneTimeJson["signed_curve25519:$key_id"] = k
}
}

// For now, we set the device id explicitly, as we may not be using the
// same one as used in login.
mUploadKeysTask
.configureWith(UploadKeysTask.Params(null, oneTimeJson, myDevice.deviceId))
.dispatchTo(object : MatrixCallback<KeysUploadResponse> {
override fun onSuccess(data: KeysUploadResponse) {
encryptingThreadHandler.post {
if (!hasBeenReleased()) {
mLastPublishedOneTimeKeys = oneTimeKeys
mOlmDevice.markKeysAsPublished()

if (null != callback) {
mUIHandler.post { callback.onSuccess(data) }
}
}
}
}

override fun onFailure(failure: Throwable) {
if (null != callback) {
mUIHandler.post { callback.onFailure(failure) }
}

}
})
.executeBy(mTaskExecutor)
}


/**
* Export the crypto keys
*
@ -2068,15 +1813,6 @@ internal class CryptoManager(
}

companion object {
// max number of keys to upload at once
// Creating keys can be an expensive operation so we limit the
// number we generate in one go to avoid blocking the application
// for too long.
private const val ONE_TIME_KEY_GENERATION_MAX_NUMBER = 5

// frequency with which to check & upload one-time keys
private const val ONE_TIME_KEY_UPLOAD_PERIOD = (60 * 1000).toLong() // one minute

/**
* Provides the list of unknown devices
*
@ -2102,6 +1838,3 @@ internal class CryptoManager(
}
}
}
/**
* Check if the OTK must be uploaded.
*/

View File

@ -20,14 +20,14 @@ import android.content.Context
import im.vector.matrix.android.api.auth.data.Credentials
import im.vector.matrix.android.api.session.crypto.CryptoService
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.api.RoomKeysApi
import im.vector.matrix.android.internal.crypto.keysbackup.tasks.*
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.RealmCryptoStoreMigration
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.keysbackup.KeysBackup
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.tasks.*
import im.vector.matrix.android.internal.crypto.verification.DefaultSasVerificationService
import im.vector.matrix.android.internal.session.DefaultSession
@ -97,6 +97,16 @@ internal class CryptoModule {
MXOlmDevice(get())
}

// ObjectSigner
scope(DefaultSession.SCOPE) {
ObjectSigner(get(), get())
}

// OneTimeKeysManager
scope(DefaultSession.SCOPE) {
OneTimeKeysManager(get(), get(), get(), get(), get())
}

// CryptoManager
scope(DefaultSession.SCOPE) {
CryptoManager(
@ -112,6 +122,8 @@ internal class CryptoModule {
get(),
get(),
get(),
get(),
get(),
// Tasks
get(), get(), get(), get(), get(), get(), get(),
// Task executor
@ -178,6 +190,7 @@ internal class CryptoModule {
// CryptoStore
get(),
get(),
get(),
// Task
get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(),
// Task executor

View File

@ -263,7 +263,8 @@ internal class MXOlmDevice(

Timber.d("## createInboundSession() : ciphertext: $ciphertext")
try {
Timber.d("## createInboundSession() :ciphertext: SHA256:" + mOlmUtility!!.sha256(URLEncoder.encode(ciphertext, "utf-8"))) // TODO Extract code from the Log method...
val sha256 = mOlmUtility!!.sha256(URLEncoder.encode(ciphertext, "utf-8"))
Timber.d("## createInboundSession() :ciphertext: SHA256:" + sha256)
} catch (e: Exception) {
Timber.e(e, "## createInboundSession() :ciphertext: cannot encode ciphertext")
}

View File

@ -0,0 +1,53 @@
/*
* Copyright 2019 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package im.vector.matrix.android.internal.crypto

import im.vector.matrix.android.api.auth.data.Credentials
import java.util.*

internal class ObjectSigner(
private val mCredentials: Credentials,
private val mOlmDevice: MXOlmDevice) {

/**
* Sign Object
*
* Example:
* <pre>
* {
* "[MY_USER_ID]": {
* "ed25519:[MY_DEVICE_ID]": "sign(str)"
* }
* }
* </pre>
*
* @param strToSign the String to sign and to include in the Map
* @return a Map (see example)
*/
fun signObject(strToSign: String): Map<String, Map<String, String>> {
val result = HashMap<String, Map<String, String>>()

val content = HashMap<String, String>()

content["ed25519:" + mCredentials.deviceId] = mOlmDevice.signMessage(strToSign)!!

result[mCredentials.userId] = content

return result
}

}

View File

@ -0,0 +1,253 @@
/*
* Copyright 2019 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package im.vector.matrix.android.internal.crypto

import im.vector.matrix.android.api.MatrixCallback
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.rest.KeysUploadResponse
import im.vector.matrix.android.internal.crypto.tasks.UploadKeysTask
import im.vector.matrix.android.internal.di.MoshiProvider
import im.vector.matrix.android.internal.task.TaskExecutor
import im.vector.matrix.android.internal.task.configureWith
import org.matrix.olm.OlmAccount
import timber.log.Timber
import java.util.*

internal class OneTimeKeysManager(
private val mCredentials: Credentials,
private val mOlmDevice: MXOlmDevice,
private val mObjectSigner: ObjectSigner,
private val mUploadKeysTask: UploadKeysTask,
private val mTaskExecutor: TaskExecutor
) {
// tell if there is a OTK check in progress
private var mOneTimeKeyCheckInProgress = false

// last OTK check timestamp
private var mLastOneTimeKeyCheck: Long = 0

private var mOneTimeKeyCount: Int? = null

var mLastPublishedOneTimeKeys: Map<String, Map<String, String>>? = null
private set

/**
* Stores the current one_time_key count which will be handled later (in a call of
* _onSyncCompleted). The count is e.g. coming from a /sync response.
*
* @param currentCount the new count
*/
fun updateOneTimeKeyCount(currentCount: Int) {
mOneTimeKeyCount = currentCount
}


/**
* Check if the OTK must be uploaded.
*
* @param callback the asynchronous callback
*/
fun maybeUploadOneTimeKeys(callback: MatrixCallback<Unit>? = null) {
if (mOneTimeKeyCheckInProgress) {
callback?.onSuccess(Unit)
return
}

if (System.currentTimeMillis() - mLastOneTimeKeyCheck < ONE_TIME_KEY_UPLOAD_PERIOD) {
// we've done a key upload recently.
callback?.onSuccess(Unit)
return
}

mLastOneTimeKeyCheck = System.currentTimeMillis()

mOneTimeKeyCheckInProgress = true

// We then check how many keys we can store in the Account object.
val maxOneTimeKeys = mOlmDevice.getMaxNumberOfOneTimeKeys()

// Try to keep at most half that number on the server. This leaves the
// rest of the slots free to hold keys that have been claimed from the
// server but we haven't received a message for.
// If we run out of slots when generating new keys then olm will
// discard the oldest private keys first. This will eventually clean
// out stale private keys that won't receive a message.
val keyLimit = Math.floor(maxOneTimeKeys / 2.0).toInt()

if (null != mOneTimeKeyCount) {
uploadOTK(mOneTimeKeyCount!!, keyLimit, callback)
} else {
// ask the server how many keys we have
mUploadKeysTask
.configureWith(UploadKeysTask.Params(null, null, mCredentials.deviceId!!))
.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
// other devices can start conversations with us. But we can only store
// a finite number of private keys in the olm Account object.
// To complicate things further then can be a delay between a device
// claiming a public one time key from the server and it sending us a
// message. We need to keep the corresponding private key locally until
// we receive the message.
// But that message might never arrive leaving us stuck with duff
// private keys clogging up our local storage.
// So we need some kind of engineering compromise to balance all of
// these factors. // TODO Why we do not set mOneTimeKeyCount here?
val keyCount = data.oneTimeKeyCountsForAlgorithm(MXKey.KEY_SIGNED_CURVE_25519_TYPE)
uploadOTK(keyCount, keyLimit, callback)
}

override fun onFailure(failure: Throwable) {
Timber.e(failure, "## uploadKeys() : failed")

mOneTimeKeyCount = null
mOneTimeKeyCheckInProgress = false

callback?.onFailure(failure)
}
})
.executeBy(mTaskExecutor)
}
}

/**
* Upload some the OTKs.
*
* @param keyCount the key count
* @param keyLimit the limit
* @param callback the asynchronous callback
*/
private fun uploadOTK(keyCount: Int, keyLimit: Int, callback: MatrixCallback<Unit>?) {
uploadLoop(keyCount, keyLimit, object : MatrixCallback<Unit> {
private fun uploadKeysDone(errorMessage: String?) {
if (null != errorMessage) {
Timber.e("## maybeUploadOneTimeKeys() : failed $errorMessage")
}
mOneTimeKeyCount = null
mOneTimeKeyCheckInProgress = false
}

override fun onSuccess(data: Unit) {
Timber.d("## maybeUploadOneTimeKeys() : succeeded")
uploadKeysDone(null)

callback?.onSuccess(Unit)
}

override fun onFailure(failure: Throwable) {
uploadKeysDone(failure.message)

callback?.onFailure(failure)
}
})

}

/**
* 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>?) {
val oneTimeKeys = mOlmDevice.getOneTimeKeys()
val oneTimeJson = HashMap<String, Any>()

val curve25519Map = oneTimeKeys!![OlmAccount.JSON_KEY_ONE_TIME_KEY]

if (null != curve25519Map) {
for (key_id in curve25519Map.keys) {
val k = HashMap<String, Any>()
k["key"] = curve25519Map.getValue(key_id)

// the key is also signed
val canonicalJson = MoshiProvider.getCanonicalJson(Map::class.java, k)

k["signatures"] = mObjectSigner.signObject(canonicalJson)

oneTimeJson["signed_curve25519:$key_id"] = k
}
}

// For now, we set the device id explicitly, as we may not be using the
// same one as used in login.
mUploadKeysTask
.configureWith(UploadKeysTask.Params(null, oneTimeJson, mCredentials.deviceId!!))
.dispatchTo(object : MatrixCallback<KeysUploadResponse> {
override fun onSuccess(data: KeysUploadResponse) {
mLastPublishedOneTimeKeys = oneTimeKeys
mOlmDevice.markKeysAsPublished()

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 {
// max number of keys to upload at once
// Creating keys can be an expensive operation so we limit the
// number we generate in one go to avoid blocking the application
// for too long.
private const val ONE_TIME_KEY_GENERATION_MAX_NUMBER = 5

// frequency with which to check & upload one-time keys
private const val ONE_TIME_KEY_UPLOAD_PERIOD = (60 * 1000).toLong() // one minute
}
}

View File

@ -36,14 +36,14 @@ import im.vector.matrix.android.internal.crypto.keysbackup.model.KeysBackupVersi
import im.vector.matrix.android.internal.crypto.keysbackup.model.MegolmBackupAuthData
import im.vector.matrix.android.internal.crypto.keysbackup.model.MegolmBackupCreationInfo
import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.*
import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
import im.vector.matrix.android.internal.crypto.store.db.model.KeysBackupDataEntity
import im.vector.matrix.android.internal.crypto.model.ImportRoomKeysResult
import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo
import im.vector.matrix.android.internal.crypto.model.MXOlmInboundGroupSession2
import im.vector.matrix.android.internal.crypto.keysbackup.tasks.*
import im.vector.matrix.android.internal.crypto.keysbackup.util.computeRecoveryKey
import im.vector.matrix.android.internal.crypto.keysbackup.util.extractCurveKeyFromRecoveryKey
import im.vector.matrix.android.internal.crypto.model.ImportRoomKeysResult
import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo
import im.vector.matrix.android.internal.crypto.model.MXOlmInboundGroupSession2
import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
import im.vector.matrix.android.internal.crypto.store.db.model.KeysBackupDataEntity
import im.vector.matrix.android.internal.di.MoshiProvider
import im.vector.matrix.android.internal.task.Task
import im.vector.matrix.android.internal.task.TaskExecutor
@ -66,6 +66,7 @@ internal class KeysBackup(
private val mCredentials: Credentials,
private val mCryptoStore: IMXCryptoStore,
private val mOlmDevice: MXOlmDevice,
private val mObjectSigner: ObjectSigner,
// Tasks
private val mCreateKeysBackupVersionTask: CreateKeysBackupVersionTask,
private val mDeleteBackupTask: DeleteBackupTask,
@ -175,7 +176,7 @@ internal class KeysBackup(

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

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


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

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

deviceSignatures[myUserId]?.forEach { entry ->
myUserSignatures[entry.key] = entry.value
@ -1484,8 +1485,6 @@ internal class KeysBackup(
}

interface KeysBackupCryptoListener {
fun signObject(strToSign: String): Map<String, Map<String, String>>

fun importMegolmSessionsData(megolmSessionsData: List<MegolmSessionData>,
backUpKeys: Boolean,
progressListener: ProgressListener?,