From bdb9d2fbb8322014b8ca59bb83bb8dbbcab3f4df Mon Sep 17 00:00:00 2001 From: Benoit Marty <benoitm@matrix.org> Date: Wed, 4 Dec 2019 16:26:32 +0100 Subject: [PATCH 1/2] Improve and cleanup OneTimeKey uploader Fix boolean reset if request fails Implement https://github.com/matrix-org/matrix-js-sdk/pull/493 --- .../internal/crypto/OneTimeKeysUploader.kt | 63 +++++++++---------- 1 file changed, 29 insertions(+), 34 deletions(-) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/OneTimeKeysUploader.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/OneTimeKeysUploader.kt index e6b57d149f..ccf7173f6d 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/OneTimeKeysUploader.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/OneTimeKeysUploader.kt @@ -42,8 +42,6 @@ internal class OneTimeKeysUploader @Inject constructor( private var lastOneTimeKeyCheck: Long = 0 private var oneTimeKeyCount: Int? = null - private var lastPublishedOneTimeKeys: Map<String, Map<String, String>>? = null - /** * Stores the current one_time_key count which will be handled later (in a call of * _onSyncCompleted). The count is e.g. coming from a /sync response. @@ -59,10 +57,12 @@ internal class OneTimeKeysUploader @Inject constructor( */ suspend fun maybeUploadOneTimeKeys() { if (oneTimeKeyCheckInProgress) { + Timber.v("maybeUploadOneTimeKeys: already in progress") return } if (System.currentTimeMillis() - lastOneTimeKeyCheck < ONE_TIME_KEY_UPLOAD_PERIOD) { // we've done a key upload recently. + Timber.v("maybeUploadOneTimeKeys: executed too recently") return } @@ -79,12 +79,8 @@ internal class OneTimeKeysUploader @Inject constructor( // discard the oldest private keys first. This will eventually clean // out stale private keys that won't receive a message. val keyLimit = floor(maxOneTimeKeys / 2.0).toInt() - if (oneTimeKeyCount != null) { - uploadOTK(oneTimeKeyCount!!, keyLimit) - } else { - // ask the server how many keys we have - val uploadKeysParams = UploadKeysTask.Params(null, null, credentials.deviceId!!) - val response = uploadKeysTask.execute(uploadKeysParams) + val oneTimeKeyCountFromSync = oneTimeKeyCount + if (oneTimeKeyCountFromSync != null) { // 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. @@ -96,14 +92,16 @@ internal class OneTimeKeysUploader @Inject constructor( // 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 oneTimeKeyCount here? - // TODO This is not needed anymore, see https://github.com/matrix-org/matrix-js-sdk/pull/493 (TODO on iOS also) - val keyCount = response.oneTimeKeyCountsForAlgorithm(MXKey.KEY_SIGNED_CURVE_25519_TYPE) - uploadOTK(keyCount, keyLimit) + try { + uploadOTK(oneTimeKeyCountFromSync, keyLimit) + } finally { + oneTimeKeyCheckInProgress = false + } + Timber.v("## uploadKeys() : success") + } else { + Timber.w("maybeUploadOneTimeKeys: waiting to know the number of OTK from the sync") + oneTimeKeyCheckInProgress = false } - Timber.v("## uploadKeys() : success") - oneTimeKeyCount = null - oneTimeKeyCheckInProgress = false } /** @@ -119,45 +117,42 @@ internal class OneTimeKeysUploader @Inject constructor( } val keysThisLoop = min(keyLimit - keyCount, ONE_TIME_KEY_GENERATION_MAX_NUMBER) olmDevice.generateOneTimeKeys(keysThisLoop) - val response = uploadOneTimeKeys() + val response = uploadOneTimeKeys(olmDevice.getOneTimeKeys()) + olmDevice.markKeysAsPublished() + if (response.hasOneTimeKeyCountsForAlgorithm(MXKey.KEY_SIGNED_CURVE_25519_TYPE)) { + // Maybe upload other keys uploadOTK(response.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") + Timber.e("## uploadOTK() : response for uploading keys does not contain one_time_key_counts.signed_curve25519") throw Exception("response for uploading keys does not contain one_time_key_counts.signed_curve25519") } } /** - * Upload my user's one time keys. + * Upload curve25519 one time keys. */ - private suspend fun uploadOneTimeKeys(): KeysUploadResponse { - val oneTimeKeys = olmDevice.getOneTimeKeys() + private suspend fun uploadOneTimeKeys(oneTimeKeys: Map<String, Map<String, String>>?): KeysUploadResponse { val oneTimeJson = mutableMapOf<String, Any>() - val curve25519Map = oneTimeKeys?.get(OlmAccount.JSON_KEY_ONE_TIME_KEY) + val curve25519Map = oneTimeKeys?.get(OlmAccount.JSON_KEY_ONE_TIME_KEY) ?: emptyMap() - if (null != curve25519Map) { - for ((key_id, value) in curve25519Map) { - val k = mutableMapOf<String, Any>() - k["key"] = value + curve25519Map.forEach { (key_id, value) -> + val k = mutableMapOf<String, Any>() + k["key"] = value - // the key is also signed - val canonicalJson = JsonCanonicalizer.getCanonicalJson(Map::class.java, k) + // the key is also signed + val canonicalJson = JsonCanonicalizer.getCanonicalJson(Map::class.java, k) - k["signatures"] = objectSigner.signObject(canonicalJson) + k["signatures"] = objectSigner.signObject(canonicalJson) - oneTimeJson["signed_curve25519:$key_id"] = k - } + 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. val uploadParams = UploadKeysTask.Params(null, oneTimeJson, credentials.deviceId!!) - val response = uploadKeysTask.execute(uploadParams) - lastPublishedOneTimeKeys = oneTimeKeys - olmDevice.markKeysAsPublished() - return response + return uploadKeysTask.execute(uploadParams) } companion object { From f31c1b69cb9425d7c1e9bbbbc663b83cb8b45403 Mon Sep 17 00:00:00 2001 From: Benoit Marty <benoitm@matrix.org> Date: Wed, 4 Dec 2019 16:52:55 +0100 Subject: [PATCH 2/2] Remove delay when waiting for first sync to finish and add number of sent keys in the log --- .../android/internal/crypto/OneTimeKeysUploader.kt | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/OneTimeKeysUploader.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/OneTimeKeysUploader.kt index ccf7173f6d..a0483335e5 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/OneTimeKeysUploader.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/OneTimeKeysUploader.kt @@ -93,14 +93,15 @@ internal class OneTimeKeysUploader @Inject constructor( // So we need some kind of engineering compromise to balance all of // these factors. try { - uploadOTK(oneTimeKeyCountFromSync, keyLimit) + val uploadedKeys = uploadOTK(oneTimeKeyCountFromSync, keyLimit) + Timber.v("## uploadKeys() : success, $uploadedKeys key(s) sent") } finally { oneTimeKeyCheckInProgress = false } - Timber.v("## uploadKeys() : success") } else { Timber.w("maybeUploadOneTimeKeys: waiting to know the number of OTK from the sync") oneTimeKeyCheckInProgress = false + lastOneTimeKeyCheck = 0 } } @@ -109,11 +110,12 @@ internal class OneTimeKeysUploader @Inject constructor( * * @param keyCount the key count * @param keyLimit the limit + * @return the number of uploaded keys */ - private suspend fun uploadOTK(keyCount: Int, keyLimit: Int) { + private suspend fun uploadOTK(keyCount: Int, keyLimit: Int): Int { if (keyLimit <= keyCount) { // If we don't need to generate any more keys then we are done. - return + return 0 } val keysThisLoop = min(keyLimit - keyCount, ONE_TIME_KEY_GENERATION_MAX_NUMBER) olmDevice.generateOneTimeKeys(keysThisLoop) @@ -122,7 +124,7 @@ internal class OneTimeKeysUploader @Inject constructor( if (response.hasOneTimeKeyCountsForAlgorithm(MXKey.KEY_SIGNED_CURVE_25519_TYPE)) { // Maybe upload other keys - uploadOTK(response.oneTimeKeyCountsForAlgorithm(MXKey.KEY_SIGNED_CURVE_25519_TYPE), keyLimit) + return keysThisLoop + uploadOTK(response.oneTimeKeyCountsForAlgorithm(MXKey.KEY_SIGNED_CURVE_25519_TYPE), keyLimit) } else { Timber.e("## uploadOTK() : response for uploading keys does not contain one_time_key_counts.signed_curve25519") throw Exception("response for uploading keys does not contain one_time_key_counts.signed_curve25519")