From 5b4b5e7a578d780657aca4d6efb18a505302b99d Mon Sep 17 00:00:00 2001 From: Valere Date: Fri, 3 Apr 2020 15:52:47 +0200 Subject: [PATCH 1/6] Gossip keybackup key after verification! --- .../CrossSigningSsssSecretConstants.kt | 2 ++ .../crypto/keysbackup/KeysBackupService.kt | 9 ++++++ .../internal/crypto/DefaultCryptoService.kt | 5 +++ .../crypto/IncomingGossipingRequestManager.kt | 2 ++ .../keysbackup/DefaultKeysBackupService.kt | 31 +++++++++++++++++++ .../internal/crypto/store/IMXCryptoStore.kt | 3 ++ .../crypto/store/SavedKeyBackupKeyInfo.kt | 22 +++++++++++++ .../crypto/store/db/RealmCryptoStore.kt | 28 +++++++++++++++-- .../store/db/RealmCryptoStoreMigration.kt | 11 ++++++- .../store/db/model/CryptoMetadataEntity.kt | 4 ++- .../DefaultVerificationTransaction.kt | 2 ++ .../recover/BootstrapCrossSigningTask.kt | 20 +++++++++++- vector/src/main/res/values/strings_riotX.xml | 1 + 13 files changed, 135 insertions(+), 5 deletions(-) create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/SavedKeyBackupKeyInfo.kt diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/crosssigning/CrossSigningSsssSecretConstants.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/crosssigning/CrossSigningSsssSecretConstants.kt index d46a724463..2c3425bcb4 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/crosssigning/CrossSigningSsssSecretConstants.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/crosssigning/CrossSigningSsssSecretConstants.kt @@ -21,3 +21,5 @@ const val MASTER_KEY_SSSS_NAME = "m.cross_signing.master" const val USER_SIGNING_KEY_SSSS_NAME = "m.cross_signing.user_signing" const val SELF_SIGNING_KEY_SSSS_NAME = "m.cross_signing.self_signing" + +const val KEYBACKUP_SECRET_SSSS_NAME = "m.megolm_backup.v1" diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/keysbackup/KeysBackupService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/keysbackup/KeysBackupService.kt index cb06dbf665..a938ba7f25 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/keysbackup/KeysBackupService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/keysbackup/KeysBackupService.kt @@ -24,6 +24,7 @@ import im.vector.matrix.android.internal.crypto.keysbackup.model.MegolmBackupCre import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeysVersion import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeysVersionResult import im.vector.matrix.android.internal.crypto.model.ImportRoomKeysResult +import im.vector.matrix.android.internal.crypto.store.SavedKeyBackupKeyInfo interface KeysBackupService { /** @@ -172,6 +173,9 @@ interface KeysBackupService { password: String, callback: MatrixCallback) + + fun onSecretKeyGossip(recoveryKey: String) + /** * Restore a backup with a recovery key from a given backup version stored on the homeserver. * @@ -210,4 +214,9 @@ interface KeysBackupService { val isEnabled: Boolean val isStucked: Boolean val state: KeysBackupState + + + // For gossiping + fun saveBackupRecoveryKey(recoveryKey: String?, version: String?) + fun getKeyBackupRecoveryKeyInfo() : SavedKeyBackupKeyInfo? } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/DefaultCryptoService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/DefaultCryptoService.kt index d3e5ada8cb..3ddbf32ee7 100755 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/DefaultCryptoService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/DefaultCryptoService.kt @@ -33,6 +33,7 @@ import im.vector.matrix.android.api.failure.Failure import im.vector.matrix.android.api.listeners.ProgressListener import im.vector.matrix.android.api.session.crypto.CryptoService import im.vector.matrix.android.api.session.crypto.MXCryptoError +import im.vector.matrix.android.api.session.crypto.crosssigning.KEYBACKUP_SECRET_SSSS_NAME import im.vector.matrix.android.api.session.crypto.crosssigning.SELF_SIGNING_KEY_SSSS_NAME import im.vector.matrix.android.api.session.crypto.crosssigning.USER_SIGNING_KEY_SSSS_NAME import im.vector.matrix.android.api.session.crypto.keyshare.GossipingRequestListener @@ -775,6 +776,10 @@ internal class DefaultCryptoService @Inject constructor( crossSigningService.onSecretUSKGossip(secretContent.secretValue) return } + KEYBACKUP_SECRET_SSSS_NAME -> { + keysBackupService.onSecretKeyGossip(secretContent.secretValue) + return + } else -> { // Ask to application layer? Timber.v("## onSecretSend() : secret not handled by SDK") diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingGossipingRequestManager.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingGossipingRequestManager.kt index 8bec87b341..d29ebb1798 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingGossipingRequestManager.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingGossipingRequestManager.kt @@ -19,6 +19,7 @@ package im.vector.matrix.android.internal.crypto import im.vector.matrix.android.api.auth.data.Credentials import im.vector.matrix.android.api.auth.data.sessionId import im.vector.matrix.android.api.crypto.MXCryptoConfig +import im.vector.matrix.android.api.session.crypto.crosssigning.KEYBACKUP_SECRET_SSSS_NAME import im.vector.matrix.android.api.session.crypto.crosssigning.SELF_SIGNING_KEY_SSSS_NAME import im.vector.matrix.android.api.session.crypto.crosssigning.USER_SIGNING_KEY_SSSS_NAME import im.vector.matrix.android.api.session.crypto.keyshare.GossipingRequestListener @@ -281,6 +282,7 @@ internal class IncomingGossipingRequestManager @Inject constructor( when (secretName) { SELF_SIGNING_KEY_SSSS_NAME -> cryptoStore.getCrossSigningPrivateKeys()?.selfSigned USER_SIGNING_KEY_SSSS_NAME -> cryptoStore.getCrossSigningPrivateKeys()?.user + KEYBACKUP_SECRET_SSSS_NAME -> cryptoStore.getKeyBackupRecoveryKeyInfo()?.recoveryKey else -> null }?.let { secretValue -> Timber.i("## GOSSIP processIncomingSecretShareRequest() : Sharing secret $secretName with $device locally trusted") diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/DefaultKeysBackupService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/DefaultKeysBackupService.kt index 3ec5a2f979..1c0f80ac86 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/DefaultKeysBackupService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/DefaultKeysBackupService.kt @@ -67,6 +67,7 @@ import im.vector.matrix.android.internal.crypto.keysbackup.util.extractCurveKeyF import im.vector.matrix.android.internal.crypto.model.ImportRoomKeysResult import im.vector.matrix.android.internal.crypto.model.OlmInboundGroupSessionWrapper import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore +import im.vector.matrix.android.internal.crypto.store.SavedKeyBackupKeyInfo 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.di.UserId @@ -580,6 +581,28 @@ internal class DefaultKeysBackupService @Inject constructor( } } + override fun onSecretKeyGossip(recoveryKey: String) { + + Timber.v("onSecretKeyGossip: version ${keysBackupVersion?.version}") + + cryptoCoroutineScope.launch(coroutineDispatchers.main) { + try { + val keysBackupVersion = getKeysBackupLastVersionTask.execute(Unit) + if (isValidRecoveryKeyForKeysBackupVersion(recoveryKey, keysBackupVersion)) { + awaitCallback { + trustKeysBackupVersion(keysBackupVersion, true, it) + } + val importResult = awaitCallback { + restoreKeysWithRecoveryKey(keysBackupVersion, recoveryKey, null, null, null, it) + } + Timber.i("onSecretKeyGossip: Recovered keys ${importResult.successfullyNumberOfImportedKeys} out of ${importResult.totalNumberOfKeys}") + } + } catch (failure: Throwable) { + Timber.e("onSecretKeyGossip: failed to trust key backup version ${keysBackupVersion?.version}") + } + } + } + /** * Get public key from a Recovery key * @@ -1391,6 +1414,14 @@ internal class DefaultKeysBackupService @Inject constructor( .executeBy(taskExecutor) } + override fun getKeyBackupRecoveryKeyInfo(): SavedKeyBackupKeyInfo? { + return cryptoStore.getKeyBackupRecoveryKeyInfo() + } + + override fun saveBackupRecoveryKey(recoveryKey: String?, version: String?) { + cryptoStore.saveBackupRecoveryKey(recoveryKey, version) + } + companion object { // Maximum delay in ms in {@link maybeBackupKeys} private const val KEY_BACKUP_WAITING_TIME_TO_SEND_KEY_BACKUP_MILLIS = 10_000L diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/IMXCryptoStore.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/IMXCryptoStore.kt index 726d56a2f7..a8f65e9219 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/IMXCryptoStore.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/IMXCryptoStore.kt @@ -405,6 +405,9 @@ internal interface IMXCryptoStore { fun getCrossSigningPrivateKeys(): PrivateKeysInfo? + fun saveBackupRecoveryKey(recoveryKey: String?, version: String?) + fun getKeyBackupRecoveryKeyInfo() : SavedKeyBackupKeyInfo? + fun setUserKeysAsTrusted(userId: String, trusted: Boolean = true) fun setDeviceTrust(userId: String, deviceId: String, crossSignedVerified: Boolean, locallyVerified: Boolean) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/SavedKeyBackupKeyInfo.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/SavedKeyBackupKeyInfo.kt new file mode 100644 index 0000000000..d73e8ed00c --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/SavedKeyBackupKeyInfo.kt @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2020 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.store + +data class SavedKeyBackupKeyInfo ( + val recoveryKey : String, + val version: String +) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/RealmCryptoStore.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/RealmCryptoStore.kt index 7f136df54d..bd51cf8539 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/RealmCryptoStore.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/RealmCryptoStore.kt @@ -45,6 +45,7 @@ import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyRequestBody import im.vector.matrix.android.internal.crypto.model.toEntity import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore import im.vector.matrix.android.internal.crypto.store.PrivateKeysInfo +import im.vector.matrix.android.internal.crypto.store.SavedKeyBackupKeyInfo import im.vector.matrix.android.internal.crypto.store.db.model.CrossSigningInfoEntity import im.vector.matrix.android.internal.crypto.store.db.model.CrossSigningInfoEntityFields import im.vector.matrix.android.internal.crypto.store.db.model.CryptoMapper @@ -216,8 +217,8 @@ internal class RealmCryptoStore @Inject constructor( override fun getOrCreateOlmAccount(): OlmAccount { doRealmTransaction(realmConfiguration) { - val metaData = it.where().findFirst() - val existing = metaData!!.getOlmAccount() + val metaData = it.where().findFirst() + val existing = metaData!!.getOlmAccount() if (existing == null) { Timber.d("## Crypto Creating olm account") val created = OlmAccount() @@ -389,6 +390,29 @@ internal class RealmCryptoStore @Inject constructor( } } + override fun saveBackupRecoveryKey(recoveryKey: String?, version: String?) { + doRealmTransaction(realmConfiguration) { realm -> + realm.where().findFirst()?.apply { + keyBackupRecoveryKey = recoveryKey + keyBackupRecoveryKeyVersion = version + } + } + } + + override fun getKeyBackupRecoveryKeyInfo(): SavedKeyBackupKeyInfo? { + return doRealmQueryAndCopy(realmConfiguration) { realm -> + realm.where().findFirst() + }?.let { + val key = it.keyBackupRecoveryKey + val version = it.keyBackupRecoveryKeyVersion + if (!key.isNullOrBlank() && !version.isNullOrBlank()) { + SavedKeyBackupKeyInfo(recoveryKey = key, version = version) + } else { + null + } + } + } + override fun storeSSKPrivateKey(ssk: String?) { doRealmTransaction(realmConfiguration) { realm -> realm.where().findFirst()?.apply { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/RealmCryptoStoreMigration.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/RealmCryptoStoreMigration.kt index 588ae3f9c5..81e671a4e4 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/RealmCryptoStoreMigration.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/RealmCryptoStoreMigration.kt @@ -37,13 +37,14 @@ import timber.log.Timber internal object RealmCryptoStoreMigration : RealmMigration { // Version 1L added Cross Signing info persistence - const val CRYPTO_STORE_SCHEMA_VERSION = 2L + const val CRYPTO_STORE_SCHEMA_VERSION = 3L override fun migrate(realm: DynamicRealm, oldVersion: Long, newVersion: Long) { Timber.v("Migrating Realm Crypto from $oldVersion to $newVersion") if (oldVersion <= 0) migrateTo1(realm) if (oldVersion <= 1) migrateTo2(realm) + if (oldVersion <= 2) migrateTo3(realm) } private fun migrateTo1(realm: DynamicRealm) { @@ -185,4 +186,12 @@ internal object RealmCryptoStoreMigration : RealmMigration { .addIndex(OutgoingGossipingRequestEntityFields.TYPE_STR) .addField(OutgoingGossipingRequestEntityFields.REQUEST_STATE_STR, String::class.java) } + + + private fun migrateTo3(realm: DynamicRealm) { + Timber.d("Updating CryptoMetadataEntity table") + realm.schema.get("CryptoMetadataEntity") + ?.addField(CryptoMetadataEntityFields.KEY_BACKUP_RECOVERY_KEY, String::class.java) + ?.addField(CryptoMetadataEntityFields.KEY_BACKUP_RECOVERY_KEY_VERSION, String::class.java) + } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/CryptoMetadataEntity.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/CryptoMetadataEntity.kt index 8a2c2914da..2d4706ba76 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/CryptoMetadataEntity.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/CryptoMetadataEntity.kt @@ -38,7 +38,9 @@ internal open class CryptoMetadataEntity( var xSignMasterPrivateKey: String? = null, var xSignUserPrivateKey: String? = null, - var xSignSelfSignedPrivateKey: String? = null + var xSignSelfSignedPrivateKey: String? = null, + var keyBackupRecoveryKey: String? = null, + var keyBackupRecoveryKeyVersion: String? = null // var crossSigningInfoEntity: CrossSigningInfoEntity? = null ) : RealmObject() { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/DefaultVerificationTransaction.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/DefaultVerificationTransaction.kt index 973b9944cb..5b8191fc99 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/DefaultVerificationTransaction.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/DefaultVerificationTransaction.kt @@ -17,6 +17,7 @@ package im.vector.matrix.android.internal.crypto.verification import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.session.crypto.crosssigning.CrossSigningService +import im.vector.matrix.android.api.session.crypto.crosssigning.KEYBACKUP_SECRET_SSSS_NAME import im.vector.matrix.android.api.session.crypto.crosssigning.SELF_SIGNING_KEY_SSSS_NAME import im.vector.matrix.android.api.session.crypto.crosssigning.USER_SIGNING_KEY_SSSS_NAME import im.vector.matrix.android.api.session.crypto.verification.VerificationTransaction @@ -103,6 +104,7 @@ internal abstract class DefaultVerificationTransaction( if (otherUserId == userId && !crossSigningService.canCrossSign()) { outgoingGossipingRequestManager.sendSecretShareRequest(SELF_SIGNING_KEY_SSSS_NAME, mapOf(userId to listOf(otherDeviceId ?: "*"))) outgoingGossipingRequestManager.sendSecretShareRequest(USER_SIGNING_KEY_SSSS_NAME, mapOf(userId to listOf(otherDeviceId ?: "*"))) + outgoingGossipingRequestManager.sendSecretShareRequest(KEYBACKUP_SECRET_SSSS_NAME, mapOf(userId to listOf(otherDeviceId ?: "*"))) } } diff --git a/vector/src/main/java/im/vector/riotx/features/crypto/recover/BootstrapCrossSigningTask.kt b/vector/src/main/java/im/vector/riotx/features/crypto/recover/BootstrapCrossSigningTask.kt index 2571ef674b..115033a688 100644 --- a/vector/src/main/java/im/vector/riotx/features/crypto/recover/BootstrapCrossSigningTask.kt +++ b/vector/src/main/java/im/vector/riotx/features/crypto/recover/BootstrapCrossSigningTask.kt @@ -27,6 +27,8 @@ import im.vector.matrix.android.api.session.securestorage.SharedSecretStorageSer import im.vector.matrix.android.api.session.securestorage.SsssKeyCreationInfo import im.vector.matrix.android.internal.auth.data.LoginFlowTypes import im.vector.matrix.android.internal.auth.registration.RegistrationFlowResponse +import im.vector.matrix.android.internal.crypto.keysbackup.model.MegolmBackupCreationInfo +import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeysVersion import im.vector.matrix.android.internal.crypto.model.rest.UserPasswordAuth import im.vector.matrix.android.internal.di.MoshiProvider import im.vector.matrix.android.internal.util.awaitCallback @@ -36,6 +38,8 @@ import im.vector.riotx.core.resources.StringProvider import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.async import kotlinx.coroutines.launch +import timber.log.Timber +import java.util.Timer import java.util.UUID import javax.inject.Inject @@ -195,7 +199,21 @@ class BootstrapCrossSigningTask @Inject constructor( return BootstrapResult.FailedToStorePrivateKeyInSSSS(failure) } - // TODO configure key backup? + params.progressListener?.onProgress(WaitingViewData(stringProvider.getString(R.string.bootstrap_crosssigning_progress_key_backup), isIndeterminate = true)) + try { + val creationInfo = awaitCallback { + session.cryptoService().keysBackupService().prepareKeysBackupVersion(null, null, it) + } + val version = awaitCallback { + session.cryptoService().keysBackupService().createKeysBackupVersion(creationInfo, it) + } + // Save it for gossiping + session.cryptoService().keysBackupService().saveBackupRecoveryKey(creationInfo.recoveryKey, version = version.version) + + } catch (failure: Throwable) { + Timber.e("## BootstrapCrossSigningTask: Failed to init keybackup") + } + return BootstrapResult.Success(keyInfo) } diff --git a/vector/src/main/res/values/strings_riotX.xml b/vector/src/main/res/values/strings_riotX.xml index c850316912..29e57d1133 100644 --- a/vector/src/main/res/values/strings_riotX.xml +++ b/vector/src/main/res/values/strings_riotX.xml @@ -71,6 +71,7 @@ Synchronizing Master key Synchronizing User key Synchronizing Self Signing key + Setting Up Key Backup From dc61ee61f525548130c38af0387aa79cc0b090c4 Mon Sep 17 00:00:00 2001 From: Valere Date: Fri, 3 Apr 2020 18:30:38 +0200 Subject: [PATCH 2/6] Fix / wrong export format --- .../api/session/crypto/keysbackup/KeysBackupService.kt | 2 +- .../internal/crypto/IncomingGossipingRequestManager.kt | 4 +++- .../crypto/keysbackup/DefaultKeysBackupService.kt | 10 +++++++--- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/keysbackup/KeysBackupService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/keysbackup/KeysBackupService.kt index a938ba7f25..4656cd71cc 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/keysbackup/KeysBackupService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/keysbackup/KeysBackupService.kt @@ -174,7 +174,7 @@ interface KeysBackupService { callback: MatrixCallback) - fun onSecretKeyGossip(recoveryKey: String) + fun onSecretKeyGossip(secret: String) /** * Restore a backup with a recovery key from a given backup version stored on the homeserver. diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingGossipingRequestManager.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingGossipingRequestManager.kt index d29ebb1798..8915f35ff3 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingGossipingRequestManager.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingGossipingRequestManager.kt @@ -26,6 +26,8 @@ import im.vector.matrix.android.api.session.crypto.keyshare.GossipingRequestList import im.vector.matrix.android.api.session.events.model.Event import im.vector.matrix.android.api.session.events.model.EventType import im.vector.matrix.android.api.session.events.model.toModel +import im.vector.matrix.android.internal.crypto.crosssigning.toBase64NoPadding +import im.vector.matrix.android.internal.crypto.keysbackup.util.extractCurveKeyFromRecoveryKey import im.vector.matrix.android.internal.crypto.model.rest.GossipingDefaultContent import im.vector.matrix.android.internal.crypto.model.rest.GossipingToDeviceObject import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore @@ -282,7 +284,7 @@ internal class IncomingGossipingRequestManager @Inject constructor( when (secretName) { SELF_SIGNING_KEY_SSSS_NAME -> cryptoStore.getCrossSigningPrivateKeys()?.selfSigned USER_SIGNING_KEY_SSSS_NAME -> cryptoStore.getCrossSigningPrivateKeys()?.user - KEYBACKUP_SECRET_SSSS_NAME -> cryptoStore.getKeyBackupRecoveryKeyInfo()?.recoveryKey + KEYBACKUP_SECRET_SSSS_NAME -> cryptoStore.getKeyBackupRecoveryKeyInfo()?.recoveryKey?.let { extractCurveKeyFromRecoveryKey(it)?.toBase64NoPadding() } else -> null }?.let { secretValue -> Timber.i("## GOSSIP processIncomingSecretShareRequest() : Sharing secret $secretName with $device locally trusted") diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/DefaultKeysBackupService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/DefaultKeysBackupService.kt index 1c0f80ac86..75e37d27f6 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/DefaultKeysBackupService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/DefaultKeysBackupService.kt @@ -36,6 +36,7 @@ import im.vector.matrix.android.internal.crypto.MXOlmDevice import im.vector.matrix.android.internal.crypto.MegolmSessionData import im.vector.matrix.android.internal.crypto.ObjectSigner import im.vector.matrix.android.internal.crypto.actions.MegolmSessionDataImporter +import im.vector.matrix.android.internal.crypto.crosssigning.fromBase64 import im.vector.matrix.android.internal.crypto.keysbackup.model.KeysBackupVersionTrust import im.vector.matrix.android.internal.crypto.keysbackup.model.KeysBackupVersionTrustSignature import im.vector.matrix.android.internal.crypto.keysbackup.model.MegolmBackupAuthData @@ -581,13 +582,13 @@ internal class DefaultKeysBackupService @Inject constructor( } } - override fun onSecretKeyGossip(recoveryKey: String) { - - Timber.v("onSecretKeyGossip: version ${keysBackupVersion?.version}") + override fun onSecretKeyGossip(secret: String) { + Timber.i("## CrossSigning - onSecretKeyGossip") cryptoCoroutineScope.launch(coroutineDispatchers.main) { try { val keysBackupVersion = getKeysBackupLastVersionTask.execute(Unit) + val recoveryKey = computeRecoveryKey(secret.fromBase64()) if (isValidRecoveryKeyForKeysBackupVersion(recoveryKey, keysBackupVersion)) { awaitCallback { trustKeysBackupVersion(keysBackupVersion, true, it) @@ -595,7 +596,10 @@ internal class DefaultKeysBackupService @Inject constructor( val importResult = awaitCallback { restoreKeysWithRecoveryKey(keysBackupVersion, recoveryKey, null, null, null, it) } + cryptoStore.saveBackupRecoveryKey(recoveryKey, keysBackupVersion.version) Timber.i("onSecretKeyGossip: Recovered keys ${importResult.successfullyNumberOfImportedKeys} out of ${importResult.totalNumberOfKeys}") + } else { + Timber.e("onSecretKeyGossip: Recovery key is not valid ${keysBackupVersion.version}") } } catch (failure: Throwable) { Timber.e("onSecretKeyGossip: failed to trust key backup version ${keysBackupVersion?.version}") From 1a436f962f8af99c4d420b8746b028922e0e7daf Mon Sep 17 00:00:00 2001 From: Valere Date: Fri, 3 Apr 2020 18:47:14 +0200 Subject: [PATCH 3/6] update change log cleaning --- CHANGES.md | 2 ++ .../android/api/session/crypto/keysbackup/KeysBackupService.kt | 2 -- .../android/internal/crypto/store/SavedKeyBackupKeyInfo.kt | 2 +- .../internal/crypto/store/db/RealmCryptoStoreMigration.kt | 1 - .../riotx/features/crypto/recover/BootstrapCrossSigningTask.kt | 3 --- 5 files changed, 3 insertions(+), 7 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 651a732f27..74269178dd 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -10,6 +10,8 @@ Improvements 🙌: - Verification DM / Handle concurrent .start after .ready (#794) - Cross-Signing | Update Shield Logic for DM (#963) - Cross-Signing | Complete security new session design update (#1135) + - Cross-Signing | Setup key backup as part of SSSS bootstrapping (#1201) + - Cross-Signing | Gossip key backup recovery key (#1200) Bugfix 🐛: - Missing avatar/displayname after verification request message (#841) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/keysbackup/KeysBackupService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/keysbackup/KeysBackupService.kt index 4656cd71cc..e9ed36ba23 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/keysbackup/KeysBackupService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/keysbackup/KeysBackupService.kt @@ -173,7 +173,6 @@ interface KeysBackupService { password: String, callback: MatrixCallback) - fun onSecretKeyGossip(secret: String) /** @@ -215,7 +214,6 @@ interface KeysBackupService { val isStucked: Boolean val state: KeysBackupState - // For gossiping fun saveBackupRecoveryKey(recoveryKey: String?, version: String?) fun getKeyBackupRecoveryKeyInfo() : SavedKeyBackupKeyInfo? diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/SavedKeyBackupKeyInfo.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/SavedKeyBackupKeyInfo.kt index d73e8ed00c..fda9bb1d72 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/SavedKeyBackupKeyInfo.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/SavedKeyBackupKeyInfo.kt @@ -16,7 +16,7 @@ package im.vector.matrix.android.internal.crypto.store -data class SavedKeyBackupKeyInfo ( +data class SavedKeyBackupKeyInfo( val recoveryKey : String, val version: String ) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/RealmCryptoStoreMigration.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/RealmCryptoStoreMigration.kt index 81e671a4e4..d5972b5686 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/RealmCryptoStoreMigration.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/RealmCryptoStoreMigration.kt @@ -187,7 +187,6 @@ internal object RealmCryptoStoreMigration : RealmMigration { .addField(OutgoingGossipingRequestEntityFields.REQUEST_STATE_STR, String::class.java) } - private fun migrateTo3(realm: DynamicRealm) { Timber.d("Updating CryptoMetadataEntity table") realm.schema.get("CryptoMetadataEntity") diff --git a/vector/src/main/java/im/vector/riotx/features/crypto/recover/BootstrapCrossSigningTask.kt b/vector/src/main/java/im/vector/riotx/features/crypto/recover/BootstrapCrossSigningTask.kt index 115033a688..f30f3ec83a 100644 --- a/vector/src/main/java/im/vector/riotx/features/crypto/recover/BootstrapCrossSigningTask.kt +++ b/vector/src/main/java/im/vector/riotx/features/crypto/recover/BootstrapCrossSigningTask.kt @@ -39,7 +39,6 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.async import kotlinx.coroutines.launch import timber.log.Timber -import java.util.Timer import java.util.UUID import javax.inject.Inject @@ -209,12 +208,10 @@ class BootstrapCrossSigningTask @Inject constructor( } // Save it for gossiping session.cryptoService().keysBackupService().saveBackupRecoveryKey(creationInfo.recoveryKey, version = version.version) - } catch (failure: Throwable) { Timber.e("## BootstrapCrossSigningTask: Failed to init keybackup") } - return BootstrapResult.Success(keyInfo) } From d4d73db5ae95b0cd3263666dfb077bc8fc133d01 Mon Sep 17 00:00:00 2001 From: Valere Date: Mon, 6 Apr 2020 11:38:44 +0200 Subject: [PATCH 4/6] keybackup gossip test --- .../crypto/gossiping/KeyShareTests.kt | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/gossiping/KeyShareTests.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/gossiping/KeyShareTests.kt index aec5e6c423..7ac92ed74c 100644 --- a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/gossiping/KeyShareTests.kt +++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/gossiping/KeyShareTests.kt @@ -34,6 +34,8 @@ import im.vector.matrix.android.common.TestConstants import im.vector.matrix.android.internal.crypto.GossipingRequestState import im.vector.matrix.android.internal.crypto.OutgoingGossipingRequestState import im.vector.matrix.android.internal.crypto.crosssigning.DeviceTrustLevel +import im.vector.matrix.android.internal.crypto.keysbackup.model.MegolmBackupCreationInfo +import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeysVersion import im.vector.matrix.android.internal.crypto.model.CryptoDeviceInfo import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap import im.vector.matrix.android.internal.crypto.model.event.EncryptedEventContent @@ -197,6 +199,16 @@ class KeyShareTests : InstrumentedTest { ), it) } + // Also bootstrap keybackup on first session + val creationInfo = mTestHelper.doSync { + aliceSession1.cryptoService().keysBackupService().prepareKeysBackupVersion(null, null, it) + } + val version = mTestHelper.doSync { + aliceSession1.cryptoService().keysBackupService().createKeysBackupVersion(creationInfo, it) + } + // Save it for gossiping + aliceSession1.cryptoService().keysBackupService().saveBackupRecoveryKey(creationInfo.recoveryKey, version = version.version) + val aliceSession2 = mTestHelper.logIntoAccount(aliceSession1.myUserId, SessionTestParams(true)) val aliceVerificationService1 = aliceSession1.cryptoService().verificationService() @@ -260,9 +272,18 @@ class KeyShareTests : InstrumentedTest { mTestHelper.waitWithLatch(60_000) { latch -> mTestHelper.retryPeriodicallyWithLatch(latch) { - Log.d("#TEST", "CAN XS :${ aliceSession2.cryptoService().crossSigningService().getMyCrossSigningKeys()}") + Log.d("#TEST", "CAN XS :${aliceSession2.cryptoService().crossSigningService().getMyCrossSigningKeys()}") aliceSession2.cryptoService().crossSigningService().canCrossSign() } } + + // Test that key backup key has been shared to + mTestHelper.waitWithLatch(60_000) { latch -> + val keysBackupService = aliceSession2.cryptoService().keysBackupService() + mTestHelper.retryPeriodicallyWithLatch(latch) { + Log.d("#TEST", "Recovery :${ keysBackupService.getKeyBackupRecoveryKeyInfo()?.recoveryKey}") + keysBackupService.getKeyBackupRecoveryKeyInfo()?.recoveryKey != creationInfo.recoveryKey + } + } } } From 6c5da97c162202072f2abf77e03e72bf0759aad1 Mon Sep 17 00:00:00 2001 From: Valere Date: Tue, 7 Apr 2020 15:25:31 +0200 Subject: [PATCH 5/6] code review --- .../internal/crypto/DefaultCryptoService.kt | 33 +++++++++++-------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/DefaultCryptoService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/DefaultCryptoService.kt index 3ddbf32ee7..ee96e45e85 100755 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/DefaultCryptoService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/DefaultCryptoService.kt @@ -240,7 +240,7 @@ internal class DefaultCryptoService @Inject constructor( override fun getDevicesList(callback: MatrixCallback) { getDevicesTask .configureWith { -// this.executionThread = TaskThread.CRYPTO + // this.executionThread = TaskThread.CRYPTO this.callback = callback } .executeBy(taskExecutor) @@ -636,7 +636,7 @@ internal class DefaultCryptoService @Inject constructor( */ @Throws(MXCryptoError::class) override fun decryptEvent(event: Event, timeline: String): MXEventDecryptionResult { - return internalDecryptEvent(event, timeline) + return internalDecryptEvent(event, timeline) } /** @@ -767,23 +767,30 @@ internal class DefaultCryptoService @Inject constructor( return } - when (existingRequest.secretName) { + if (!handleSDKLevelGossip(existingRequest.secretName, secretContent.secretValue)) { + // TODO Ask to application layer? + Timber.v("## onSecretSend() : secret not handled by SDK") + } + } + + /** + * Returns true if handled by SDK, otherwise should be sent to application layer + */ + private fun handleSDKLevelGossip(secretName: String?, secretValue: String): Boolean { + return when (secretName) { SELF_SIGNING_KEY_SSSS_NAME -> { - crossSigningService.onSecretSSKGossip(secretContent.secretValue) - return + crossSigningService.onSecretSSKGossip(secretValue) + true } USER_SIGNING_KEY_SSSS_NAME -> { - crossSigningService.onSecretUSKGossip(secretContent.secretValue) - return + crossSigningService.onSecretUSKGossip(secretValue) + true } KEYBACKUP_SECRET_SSSS_NAME -> { - keysBackupService.onSecretKeyGossip(secretContent.secretValue) - return - } - else -> { - // Ask to application layer? - Timber.v("## onSecretSend() : secret not handled by SDK") + keysBackupService.onSecretKeyGossip(secretValue) + true } + else -> false } } From 8904ca27f21a44064781648728a4da4dcf2fd35b Mon Sep 17 00:00:00 2001 From: Valere Date: Tue, 7 Apr 2020 15:30:19 +0200 Subject: [PATCH 6/6] clean --- .../internal/crypto/IncomingGossipingRequestManager.kt | 8 ++++++-- .../features/crypto/recover/BootstrapCrossSigningTask.kt | 7 ++++++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingGossipingRequestManager.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingGossipingRequestManager.kt index 8915f35ff3..da596960dd 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingGossipingRequestManager.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingGossipingRequestManager.kt @@ -17,7 +17,6 @@ package im.vector.matrix.android.internal.crypto import im.vector.matrix.android.api.auth.data.Credentials -import im.vector.matrix.android.api.auth.data.sessionId import im.vector.matrix.android.api.crypto.MXCryptoConfig import im.vector.matrix.android.api.session.crypto.crosssigning.KEYBACKUP_SECRET_SSSS_NAME import im.vector.matrix.android.api.session.crypto.crosssigning.SELF_SIGNING_KEY_SSSS_NAME @@ -284,7 +283,10 @@ internal class IncomingGossipingRequestManager @Inject constructor( when (secretName) { SELF_SIGNING_KEY_SSSS_NAME -> cryptoStore.getCrossSigningPrivateKeys()?.selfSigned USER_SIGNING_KEY_SSSS_NAME -> cryptoStore.getCrossSigningPrivateKeys()?.user - KEYBACKUP_SECRET_SSSS_NAME -> cryptoStore.getKeyBackupRecoveryKeyInfo()?.recoveryKey?.let { extractCurveKeyFromRecoveryKey(it)?.toBase64NoPadding() } + KEYBACKUP_SECRET_SSSS_NAME -> cryptoStore.getKeyBackupRecoveryKeyInfo()?.recoveryKey + ?.let { + extractCurveKeyFromRecoveryKey(it)?.toBase64NoPadding() + } else -> null }?.let { secretValue -> Timber.i("## GOSSIP processIncomingSecretShareRequest() : Sharing secret $secretName with $device locally trusted") @@ -305,6 +307,8 @@ internal class IncomingGossipingRequestManager @Inject constructor( return } + Timber.v("## GOSSIP processIncomingSecretShareRequest() : $secretName unknown at SDK level, asking to app layer") + request.ignore = Runnable { cryptoStore.updateGossipingRequestState(request, GossipingRequestState.REJECTED) } diff --git a/vector/src/main/java/im/vector/riotx/features/crypto/recover/BootstrapCrossSigningTask.kt b/vector/src/main/java/im/vector/riotx/features/crypto/recover/BootstrapCrossSigningTask.kt index f30f3ec83a..a19604d78e 100644 --- a/vector/src/main/java/im/vector/riotx/features/crypto/recover/BootstrapCrossSigningTask.kt +++ b/vector/src/main/java/im/vector/riotx/features/crypto/recover/BootstrapCrossSigningTask.kt @@ -198,7 +198,12 @@ class BootstrapCrossSigningTask @Inject constructor( return BootstrapResult.FailedToStorePrivateKeyInSSSS(failure) } - params.progressListener?.onProgress(WaitingViewData(stringProvider.getString(R.string.bootstrap_crosssigning_progress_key_backup), isIndeterminate = true)) + params.progressListener?.onProgress( + WaitingViewData( + stringProvider.getString(R.string.bootstrap_crosssigning_progress_key_backup), + isIndeterminate = true + ) + ) try { val creationInfo = awaitCallback { session.cryptoService().keysBackupService().prepareKeysBackupVersion(null, null, it)