From 34dec64d9cd2d6fb8c34e596385d00a5b0484270 Mon Sep 17 00:00:00 2001 From: Valere Date: Wed, 8 Apr 2020 17:55:47 +0200 Subject: [PATCH] Fixes #1214 --- CHANGES.md | 1 + .../quads/SharedSecureStorageViewModel.kt | 15 ++- .../verification/VerificationBottomSheet.kt | 3 +- .../VerificationBottomSheetViewModel.kt | 109 +++++++++++++----- 4 files changed, 92 insertions(+), 36 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 983c9841cd..174f4e3a6f 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -14,6 +14,7 @@ Improvements 🙌: - Cross-Signing | Setup key backup as part of SSSS bootstrapping (#1201) - Cross-Signing | Gossip key backup recovery key (#1200) - Show room encryption status as a bubble tile (#1078) + - Cross-Signing | Restore history after recover from passphrase (#1214) Bugfix 🐛: - Missing avatar/displayname after verification request message (#841) diff --git a/vector/src/main/java/im/vector/riotx/features/crypto/quads/SharedSecureStorageViewModel.kt b/vector/src/main/java/im/vector/riotx/features/crypto/quads/SharedSecureStorageViewModel.kt index a9f5d33888..3eef4b20cf 100644 --- a/vector/src/main/java/im/vector/riotx/features/crypto/quads/SharedSecureStorageViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/crypto/quads/SharedSecureStorageViewModel.kt @@ -37,6 +37,7 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import kotlinx.coroutines.withContext +import timber.log.Timber import java.io.ByteArrayOutputStream data class SharedSecureStorageViewState( @@ -117,11 +118,15 @@ class SharedSecureStorageViewModel @AssistedInject constructor( withContext(Dispatchers.IO) { args.requestedSecrets.forEach { val res = awaitCallback { callback -> - session.sharedSecretStorageService.getSecret( - name = it, - keyId = keyInfo.id, - secretKey = keySpec, - callback = callback) + if (session.getAccountDataEvent(it) != null) { + session.sharedSecretStorageService.getSecret( + name = it, + keyId = keyInfo.id, + secretKey = keySpec, + callback = callback) + } else { + Timber.w("## Cannot find secret $it in SSSS, skip") + } } decryptedSecretMap[it] = res } diff --git a/vector/src/main/java/im/vector/riotx/features/crypto/verification/VerificationBottomSheet.kt b/vector/src/main/java/im/vector/riotx/features/crypto/verification/VerificationBottomSheet.kt index cd91cc3712..6b75c0147a 100644 --- a/vector/src/main/java/im/vector/riotx/features/crypto/verification/VerificationBottomSheet.kt +++ b/vector/src/main/java/im/vector/riotx/features/crypto/verification/VerificationBottomSheet.kt @@ -32,6 +32,7 @@ import com.airbnb.mvrx.MvRx import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.withState import im.vector.matrix.android.api.session.Session +import im.vector.matrix.android.api.session.crypto.crosssigning.KEYBACKUP_SECRET_SSSS_NAME import im.vector.matrix.android.api.session.crypto.crosssigning.MASTER_KEY_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 @@ -108,7 +109,7 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment() { startActivityForResult(SharedSecureStorageActivity.newIntent( requireContext(), null, // use default key - listOf(MASTER_KEY_SSSS_NAME, USER_SIGNING_KEY_SSSS_NAME, SELF_SIGNING_KEY_SSSS_NAME), + listOf(MASTER_KEY_SSSS_NAME, USER_SIGNING_KEY_SSSS_NAME, SELF_SIGNING_KEY_SSSS_NAME, KEYBACKUP_SECRET_SSSS_NAME), SharedSecureStorageActivity.DEFAULT_RESULT_KEYSTORE_ALIAS ), SECRET_REQUEST_CODE) } diff --git a/vector/src/main/java/im/vector/riotx/features/crypto/verification/VerificationBottomSheetViewModel.kt b/vector/src/main/java/im/vector/riotx/features/crypto/verification/VerificationBottomSheetViewModel.kt index c37bba1469..71a9ec73e7 100644 --- a/vector/src/main/java/im/vector/riotx/features/crypto/verification/VerificationBottomSheetViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/crypto/verification/VerificationBottomSheetViewModel.kt @@ -15,6 +15,7 @@ */ package im.vector.riotx.features.crypto.verification +import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.Async import com.airbnb.mvrx.Fail import com.airbnb.mvrx.FragmentViewModelContext @@ -28,6 +29,7 @@ import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.AssistedInject import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.session.Session +import im.vector.matrix.android.api.session.crypto.crosssigning.KEYBACKUP_SECRET_SSSS_NAME import im.vector.matrix.android.api.session.crypto.crosssigning.MASTER_KEY_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 @@ -46,8 +48,13 @@ import im.vector.matrix.android.api.util.MatrixItem import im.vector.matrix.android.api.util.toMatrixItem import im.vector.matrix.android.internal.crypto.crosssigning.fromBase64 import im.vector.matrix.android.internal.crypto.crosssigning.isVerified +import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeysVersionResult +import im.vector.matrix.android.internal.crypto.keysbackup.util.computeRecoveryKey +import im.vector.matrix.android.internal.crypto.model.ImportRoomKeysResult +import im.vector.matrix.android.internal.util.awaitCallback import im.vector.riotx.core.extensions.exhaustive import im.vector.riotx.core.platform.VectorViewModel +import kotlinx.coroutines.launch import timber.log.Timber data class VerificationBottomSheetViewState( @@ -334,40 +341,82 @@ class VerificationBottomSheetViewModel @AssistedInject constructor( _viewEvents.post(VerificationBottomSheetViewEvents.AccessSecretStore) } is VerificationAction.GotResultFromSsss -> { - try { - action.cypherData.fromBase64().inputStream().use { ins -> - val res = session.loadSecureSecret>(ins, action.alias) - val trustResult = session.cryptoService().crossSigningService().checkTrustFromPrivateKeys( - res?.get(MASTER_KEY_SSSS_NAME), - res?.get(USER_SIGNING_KEY_SSSS_NAME), - res?.get(SELF_SIGNING_KEY_SSSS_NAME) - ) - if (trustResult.isVerified()) { - // Sign this device and upload the signature - session.sessionParams.credentials.deviceId?.let { deviceId -> - session.cryptoService() - .crossSigningService().trustDevice(deviceId, object : MatrixCallback { - override fun onFailure(failure: Throwable) { - Timber.w(failure, "Failed to sign my device after recovery") - } - }) - } - - setState { - copy(verifiedFromPrivateKeys = true) - } - } else { - // POP UP something - _viewEvents.post(VerificationBottomSheetViewEvents.ModalError("Failed to import keys")) - } - } - } catch (failure: Throwable) { - _viewEvents.post(VerificationBottomSheetViewEvents.ModalError(failure.localizedMessage)) - } + handleSecretBackFromSSSS(action) } }.exhaustive } + private fun handleSecretBackFromSSSS(action: VerificationAction.GotResultFromSsss) { + try { + action.cypherData.fromBase64().inputStream().use { ins -> + val res = session.loadSecureSecret>(ins, action.alias) + val trustResult = session.cryptoService().crossSigningService().checkTrustFromPrivateKeys( + res?.get(MASTER_KEY_SSSS_NAME), + res?.get(USER_SIGNING_KEY_SSSS_NAME), + res?.get(SELF_SIGNING_KEY_SSSS_NAME) + ) + if (trustResult.isVerified()) { + // Sign this device and upload the signature + session.sessionParams.credentials.deviceId?.let { deviceId -> + session.cryptoService() + .crossSigningService().trustDevice(deviceId, object : MatrixCallback { + override fun onFailure(failure: Throwable) { + Timber.w(failure, "Failed to sign my device after recovery") + } + }) + } + + setState { + copy(verifiedFromPrivateKeys = true) + } + + // try to get keybackup key + } else { + // POP UP something + _viewEvents.post(VerificationBottomSheetViewEvents.ModalError("Failed to import keys")) + } + + // try the keybackup + tentativeRestoreBackup(res) + Unit + } + } catch (failure: Throwable) { + _viewEvents.post(VerificationBottomSheetViewEvents.ModalError(failure.localizedMessage)) + } + } + + private fun tentativeRestoreBackup(res: Map?) { + viewModelScope.launch { + try { + val secret = res?.get(KEYBACKUP_SECRET_SSSS_NAME) ?: return@launch Unit.also { + Timber.v("## Keybackup secret not restored from SSSS") + } + + val version = awaitCallback { + session.cryptoService().keysBackupService().getCurrentVersion(it) + } ?: return@launch + + awaitCallback { + session.cryptoService().keysBackupService().restoreKeysWithRecoveryKey( + version, + computeRecoveryKey(secret.fromBase64()), + null, + null, + null, + it + ) + } + + awaitCallback { + session.cryptoService().keysBackupService().trustKeysBackupVersion(version, true, it) + } + } catch (failure: Throwable) { + // Just ignore for now + Timber.v("## Failed to restore backup after SSSS recovery") + } + } + } + override fun transactionCreated(tx: VerificationTransaction) { transactionUpdated(tx) }