From b31d2cd828d24db7f20ab5d200c2fca00eeb278b Mon Sep 17 00:00:00 2001 From: yostyle Date: Tue, 14 Nov 2023 09:58:01 +0100 Subject: [PATCH 1/5] Force to configure secure backup --- .../crypto/recover/BootstrapSetupRecoveryKeyFragment.kt | 4 ++-- .../features/crypto/recover/BootstrapSharedViewModel.kt | 7 ++++--- .../crypto/verification/self/SelfVerificationViewModel.kt | 4 ++-- .../im/vector/app/features/home/HomeActivityViewModel.kt | 5 +++-- .../vector/app/features/raw/wellknown/ElementWellKnown.kt | 4 ++-- 5 files changed, 13 insertions(+), 11 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapSetupRecoveryKeyFragment.kt b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapSetupRecoveryKeyFragment.kt index 5ec1c9858c..2e94294dbb 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapSetupRecoveryKeyFragment.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapSetupRecoveryKeyFragment.kt @@ -95,7 +95,7 @@ class BootstrapSetupRecoveryKeyFragment : private fun renderBackupMethodActions(method: SecureBackupMethod) = with(views) { bootstrapSetupSecureUseSecurityKey.isVisible = method.isKeyAvailable // TCHAP Hide Security Passphrase -// views.bootstrapSetupSecureUseSecurityPassphrase.isVisible = method.isPassphraseAvailable -// views.bootstrapSetupSecureUseSecurityPassphraseSeparator.isVisible = method.isPassphraseAvailable +// bootstrapSetupSecureUseSecurityPassphrase.isVisible = method.isPassphraseAvailable +// bootstrapSetupSecureUseSecurityPassphraseSeparator.isVisible = method.isPassphraseAvailable } } diff --git a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapSharedViewModel.kt b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapSharedViewModel.kt index c9c2c5ce9a..53000b0475 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapSharedViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapSharedViewModel.kt @@ -45,7 +45,7 @@ import org.matrix.android.sdk.api.auth.UserPasswordAuth import org.matrix.android.sdk.api.auth.data.LoginFlowTypes import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse import org.matrix.android.sdk.api.auth.registration.nextUncompletedStage -import org.matrix.android.sdk.api.extensions.orFalse +import org.matrix.android.sdk.api.extensions.orTrue import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.failure.Failure import org.matrix.android.sdk.api.raw.RawService @@ -92,8 +92,9 @@ class BootstrapSharedViewModel @AssistedInject constructor( val wellKnown = rawService.getElementWellknown(session.sessionParams) setState { copy( - isSecureBackupRequired = wellKnown?.isSecureBackupRequired().orFalse(), - secureBackupMethod = wellKnown?.secureBackupMethod() ?: SecureBackupMethod.KEY_OR_PASSPHRASE, + // Tchap: force to configure secure backup key even if well-known is null + isSecureBackupRequired = wellKnown?.isSecureBackupRequired().orTrue(), + secureBackupMethod = wellKnown?.secureBackupMethod() ?: SecureBackupMethod.KEY, ) } } diff --git a/vector/src/main/java/im/vector/app/features/crypto/verification/self/SelfVerificationViewModel.kt b/vector/src/main/java/im/vector/app/features/crypto/verification/self/SelfVerificationViewModel.kt index 029fa2ca15..d39b92136b 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/verification/self/SelfVerificationViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/verification/self/SelfVerificationViewModel.kt @@ -46,7 +46,7 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import org.matrix.android.sdk.api.Matrix -import org.matrix.android.sdk.api.extensions.orFalse +import org.matrix.android.sdk.api.extensions.orTrue import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.raw.RawService import org.matrix.android.sdk.api.session.Session @@ -142,7 +142,7 @@ class SelfVerificationViewModel @AssistedInject constructor( viewModelScope.launch(Dispatchers.IO) { val wellKnown = rawService.getElementWellknown(session.sessionParams) setState { - copy(isVerificationRequired = wellKnown?.isSecureBackupRequired().orFalse()) + copy(isVerificationRequired = wellKnown?.isSecureBackupRequired().orTrue()) // Tchap: force to configure secure backup even if well-known is null } } diff --git a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt index bc2bbad6bc..30915a505a 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt @@ -63,6 +63,7 @@ import org.matrix.android.sdk.api.auth.UserPasswordAuth import org.matrix.android.sdk.api.auth.data.LoginFlowTypes import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse import org.matrix.android.sdk.api.auth.registration.nextUncompletedStage +import org.matrix.android.sdk.api.extensions.orTrue import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.raw.RawService import org.matrix.android.sdk.api.session.crypto.crosssigning.CrossSigningService @@ -391,7 +392,7 @@ class HomeActivityViewModel @AssistedInject constructor( private fun sessionHasBeenUnverified(elementWellKnown: ElementWellKnown?) { val session = activeSessionHolder.getSafeActiveSession() ?: return - val isSecureBackupRequired = elementWellKnown?.isSecureBackupRequired() ?: false + val isSecureBackupRequired = elementWellKnown?.isSecureBackupRequired().orTrue() // Tchap: force to configure secure backup even if well-known is null if (isSecureBackupRequired) { // If 4S is forced, force verification // for stability cancel all pending verifications? @@ -425,7 +426,7 @@ class HomeActivityViewModel @AssistedInject constructor( } val elementWellKnown = rawService.getElementWellknown(session.sessionParams) - val isSecureBackupRequired = elementWellKnown?.isSecureBackupRequired() ?: false + val isSecureBackupRequired = elementWellKnown?.isSecureBackupRequired().orTrue() // Tchap: force to configure secure backup even if well-known is null // In case of account creation, it is already done before if (initialState.authenticationDescription is AuthenticationDescription.Register) { diff --git a/vector/src/main/java/im/vector/app/features/raw/wellknown/ElementWellKnown.kt b/vector/src/main/java/im/vector/app/features/raw/wellknown/ElementWellKnown.kt index 78329350d3..fafc9ce1bb 100644 --- a/vector/src/main/java/im/vector/app/features/raw/wellknown/ElementWellKnown.kt +++ b/vector/src/main/java/im/vector/app/features/raw/wellknown/ElementWellKnown.kt @@ -56,7 +56,7 @@ data class E2EWellKnownConfig( val e2eDefault: Boolean? = null, @Json(name = "secure_backup_required") - val secureBackupRequired: Boolean? = null, + val secureBackupRequired: Boolean? = true, // Tchap: force to configure secure backup even if well-known is empty /** * The new field secure_backup_setup_methods is an array listing the methods the client should display. @@ -65,7 +65,7 @@ data class E2EWellKnownConfig( * clients should fallback to the default value of: ["key", "passphrase"]. */ @Json(name = "secure_backup_setup_methods") - val secureBackupSetupMethods: List? = null, + val secureBackupSetupMethods: List? = listOf("key"), // Tchap: force to configure secure backup even if well-known is empty /** * Configuration for sharing keys strategy which should be used instead of [im.vector.app.config.Config.KEY_SHARING_STRATEGY]. From 11422fe6fff31947e100d2cf2ef5257a8cfd3bf4 Mon Sep 17 00:00:00 2001 From: yostyle Date: Tue, 28 Nov 2023 11:34:53 +0100 Subject: [PATCH 2/5] Do not force cross signing reset --- .../debug/features/DebugVectorFeatures.kt | 2 ++ .../src/btchap/res/values/config-features.xml | 1 + .../src/devTchap/res/values/config-features.xml | 1 + .../src/tchap/res/values/config-features.xml | 1 + .../java/im/vector/app/features/VectorFeatures.kt | 2 ++ .../crypto/recover/BootstrapSharedViewModel.kt | 10 ++++++---- .../self/SelfVerificationViewModel.kt | 9 ++++----- .../app/features/home/HomeActivityViewModel.kt | 15 +++++++-------- .../features/raw/wellknown/ElementWellKnown.kt | 4 ++-- 9 files changed, 26 insertions(+), 19 deletions(-) diff --git a/vector-app/src/debug/java/im/vector/app/features/debug/features/DebugVectorFeatures.kt b/vector-app/src/debug/java/im/vector/app/features/debug/features/DebugVectorFeatures.kt index 54c7de9b7a..f982223665 100644 --- a/vector-app/src/debug/java/im/vector/app/features/debug/features/DebugVectorFeatures.kt +++ b/vector-app/src/debug/java/im/vector/app/features/debug/features/DebugVectorFeatures.kt @@ -50,6 +50,8 @@ class DebugVectorFeatures( override fun tchapIsLabsVisible(domain: String) = vectorFeatures.tchapIsLabsVisible(domain) + override fun tchapIsSecureBackupRequired() = vectorFeatures.tchapIsSecureBackupRequired() + override fun onboardingVariant(): OnboardingVariant { return readPreferences().getEnum() ?: vectorFeatures.onboardingVariant() } diff --git a/vector-config/src/btchap/res/values/config-features.xml b/vector-config/src/btchap/res/values/config-features.xml index e30415e59d..3fb2fc083d 100755 --- a/vector-config/src/btchap/res/values/config-features.xml +++ b/vector-config/src/btchap/res/values/config-features.xml @@ -3,6 +3,7 @@ true true false + false diff --git a/vector-config/src/devTchap/res/values/config-features.xml b/vector-config/src/devTchap/res/values/config-features.xml index e30415e59d..683c904075 100755 --- a/vector-config/src/devTchap/res/values/config-features.xml +++ b/vector-config/src/devTchap/res/values/config-features.xml @@ -3,6 +3,7 @@ true true false + true diff --git a/vector-config/src/tchap/res/values/config-features.xml b/vector-config/src/tchap/res/values/config-features.xml index e30415e59d..3fb2fc083d 100755 --- a/vector-config/src/tchap/res/values/config-features.xml +++ b/vector-config/src/tchap/res/values/config-features.xml @@ -3,6 +3,7 @@ true true false + false diff --git a/vector/src/main/java/im/vector/app/features/VectorFeatures.kt b/vector/src/main/java/im/vector/app/features/VectorFeatures.kt index 70e9593172..2315005aec 100644 --- a/vector/src/main/java/im/vector/app/features/VectorFeatures.kt +++ b/vector/src/main/java/im/vector/app/features/VectorFeatures.kt @@ -32,6 +32,7 @@ interface VectorFeatures { fun tchapIsKeyBackupEnabled(): Boolean fun tchapIsThreadEnabled(): Boolean fun tchapIsLabsVisible(domain: String): Boolean + fun tchapIsSecureBackupRequired(): Boolean fun onboardingVariant(): OnboardingVariant fun isOnboardingAlreadyHaveAccountSplashEnabled(): Boolean fun isOnboardingSplashCarouselEnabled(): Boolean @@ -71,6 +72,7 @@ class DefaultVectorFeatures @Inject constructor( override fun tchapIsThreadEnabled() = booleanProvider.getBoolean(R.bool.tchap_is_thread_enabled) override fun tchapIsLabsVisible(domain: String) = booleanProvider.getBoolean(R.bool.settings_root_labs_visible) || domain == appNameProvider.getAppName() + override fun tchapIsSecureBackupRequired() = booleanProvider.getBoolean(R.bool.tchap_is_secure_backup_required) override fun onboardingVariant() = Config.ONBOARDING_VARIANT override fun isOnboardingAlreadyHaveAccountSplashEnabled() = true override fun isOnboardingSplashCarouselEnabled() = false // TCHAP no carousel diff --git a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapSharedViewModel.kt b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapSharedViewModel.kt index 53000b0475..64c7553ec2 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapSharedViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapSharedViewModel.kt @@ -32,6 +32,7 @@ import im.vector.app.core.error.ErrorFormatter import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.platform.WaitingViewData import im.vector.app.core.resources.StringProvider +import im.vector.app.features.VectorFeatures import im.vector.app.features.auth.PendingAuthHandler import im.vector.app.features.raw.wellknown.SecureBackupMethod import im.vector.app.features.raw.wellknown.getElementWellknown @@ -45,7 +46,6 @@ import org.matrix.android.sdk.api.auth.UserPasswordAuth import org.matrix.android.sdk.api.auth.data.LoginFlowTypes import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse import org.matrix.android.sdk.api.auth.registration.nextUncompletedStage -import org.matrix.android.sdk.api.extensions.orTrue import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.failure.Failure import org.matrix.android.sdk.api.raw.RawService @@ -63,6 +63,7 @@ class BootstrapSharedViewModel @AssistedInject constructor( @Assisted initialState: BootstrapViewState, private val stringProvider: StringProvider, private val errorFormatter: ErrorFormatter, + private val vectorFeatures: VectorFeatures, private val session: Session, private val rawService: RawService, private val bootstrapTask: BootstrapCrossSigningTask, @@ -93,7 +94,7 @@ class BootstrapSharedViewModel @AssistedInject constructor( setState { copy( // Tchap: force to configure secure backup key even if well-known is null - isSecureBackupRequired = wellKnown?.isSecureBackupRequired().orTrue(), + isSecureBackupRequired = wellKnown?.isSecureBackupRequired() ?: vectorFeatures.tchapIsSecureBackupRequired(), secureBackupMethod = wellKnown?.secureBackupMethod() ?: SecureBackupMethod.KEY, ) } @@ -434,7 +435,8 @@ class BootstrapSharedViewModel @AssistedInject constructor( progressListener = progressListener, passphrase = state.passphrase, keySpec = state.migrationRecoveryKey?.let { extractCurveKeyFromRecoveryKey(it)?.let { RawBytesKeySpec(it) } }, - forceResetIfSomeSecretsAreMissing = state.isSecureBackupRequired, + // Tchap: do not reset cross signing +// forceResetIfSomeSecretsAreMissing = state.isSecureBackupRequired, setupMode = state.setupMode ) ) { bootstrapResult -> @@ -604,4 +606,4 @@ class BootstrapSharedViewModel @AssistedInject constructor( } } -private val BootstrapViewState.canLeave: Boolean get() = !isSecureBackupRequired || isRecoverySetup +private val BootstrapViewState.canLeave: Boolean get() = isRecoverySetup // Tchap: can leave even if secure backup is required diff --git a/vector/src/main/java/im/vector/app/features/crypto/verification/self/SelfVerificationViewModel.kt b/vector/src/main/java/im/vector/app/features/crypto/verification/self/SelfVerificationViewModel.kt index d39b92136b..c87a237f18 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/verification/self/SelfVerificationViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/verification/self/SelfVerificationViewModel.kt @@ -32,13 +32,12 @@ import im.vector.app.core.di.MavericksAssistedViewModelFactory import im.vector.app.core.di.hiltMavericksViewModelFactory import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider +import im.vector.app.features.VectorFeatures import im.vector.app.features.crypto.verification.SupportedVerificationMethodsProvider import im.vector.app.features.crypto.verification.VerificationAction import im.vector.app.features.crypto.verification.VerificationBottomSheetViewEvents import im.vector.app.features.crypto.verification.user.VerificationTransactionData import im.vector.app.features.crypto.verification.user.toDataClass -import im.vector.app.features.raw.wellknown.getElementWellknown -import im.vector.app.features.raw.wellknown.isSecureBackupRequired import im.vector.app.features.session.coroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.filter @@ -46,7 +45,6 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import org.matrix.android.sdk.api.Matrix -import org.matrix.android.sdk.api.extensions.orTrue import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.raw.RawService import org.matrix.android.sdk.api.session.Session @@ -98,6 +96,7 @@ data class SelfVerificationViewState( class SelfVerificationViewModel @AssistedInject constructor( @Assisted private val initialState: SelfVerificationViewState, private val session: Session, + private val vectorFeatures: VectorFeatures, private val supportedVerificationMethodsProvider: SupportedVerificationMethodsProvider, private val rawService: RawService, private val stringProvider: StringProvider, @@ -140,9 +139,9 @@ class SelfVerificationViewModel @AssistedInject constructor( // This is async, but at this point should be in cache // so it's ok to not wait until result viewModelScope.launch(Dispatchers.IO) { - val wellKnown = rawService.getElementWellknown(session.sessionParams) + // Tchap: force verification when recovery is setup setState { - copy(isVerificationRequired = wellKnown?.isSecureBackupRequired().orTrue()) // Tchap: force to configure secure backup even if well-known is null + copy(isVerificationRequired = session.sharedSecretStorageService().isRecoverySetup()) } } diff --git a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt index 30915a505a..91ff947fee 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt @@ -63,7 +63,6 @@ import org.matrix.android.sdk.api.auth.UserPasswordAuth import org.matrix.android.sdk.api.auth.data.LoginFlowTypes import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse import org.matrix.android.sdk.api.auth.registration.nextUncompletedStage -import org.matrix.android.sdk.api.extensions.orTrue import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.raw.RawService import org.matrix.android.sdk.api.session.crypto.crosssigning.CrossSigningService @@ -392,7 +391,7 @@ class HomeActivityViewModel @AssistedInject constructor( private fun sessionHasBeenUnverified(elementWellKnown: ElementWellKnown?) { val session = activeSessionHolder.getSafeActiveSession() ?: return - val isSecureBackupRequired = elementWellKnown?.isSecureBackupRequired().orTrue() // Tchap: force to configure secure backup even if well-known is null + val isSecureBackupRequired = elementWellKnown?.isSecureBackupRequired() ?: vectorFeatures.tchapIsSecureBackupRequired() // Tchap: force to configure secure backup even if well-known is null if (isSecureBackupRequired) { // If 4S is forced, force verification // for stability cancel all pending verifications? @@ -426,7 +425,7 @@ class HomeActivityViewModel @AssistedInject constructor( } val elementWellKnown = rawService.getElementWellknown(session.sessionParams) - val isSecureBackupRequired = elementWellKnown?.isSecureBackupRequired().orTrue() // Tchap: force to configure secure backup even if well-known is null + val isSecureBackupRequired = elementWellKnown?.isSecureBackupRequired() ?: vectorFeatures.tchapIsSecureBackupRequired() // Tchap: force to configure secure backup even if well-known is null // In case of account creation, it is already done before if (initialState.authenticationDescription is AuthenticationDescription.Register) { @@ -465,10 +464,10 @@ class HomeActivityViewModel @AssistedInject constructor( // Is there already cross signing keys here? val mxCrossSigningInfo = session.cryptoService().crossSigningService().getMyCrossSigningKeys() if (mxCrossSigningInfo != null) { - if (isSecureBackupRequired && !session.sharedSecretStorageService().isRecoverySetup()) { - // If 4S is forced, start the full interactive setup flow - _viewEvents.post(HomeActivityViewEvents.StartRecoverySetupFlow) - } else { +// if (isSecureBackupRequired && !session.sharedSecretStorageService().isRecoverySetup()) { +// // If 4S is forced, start the full interactive setup flow +// _viewEvents.post(HomeActivityViewEvents.StartRecoverySetupFlow) +// } else { // Cross-signing is already set up for this user, is it trusted? if (!mxCrossSigningInfo.isTrusted()) { if (isSecureBackupRequired) { @@ -500,7 +499,7 @@ class HomeActivityViewModel @AssistedInject constructor( } } } - } +// } } else { // Cross signing is not initialized if (isSecureBackupRequired) { diff --git a/vector/src/main/java/im/vector/app/features/raw/wellknown/ElementWellKnown.kt b/vector/src/main/java/im/vector/app/features/raw/wellknown/ElementWellKnown.kt index fafc9ce1bb..78329350d3 100644 --- a/vector/src/main/java/im/vector/app/features/raw/wellknown/ElementWellKnown.kt +++ b/vector/src/main/java/im/vector/app/features/raw/wellknown/ElementWellKnown.kt @@ -56,7 +56,7 @@ data class E2EWellKnownConfig( val e2eDefault: Boolean? = null, @Json(name = "secure_backup_required") - val secureBackupRequired: Boolean? = true, // Tchap: force to configure secure backup even if well-known is empty + val secureBackupRequired: Boolean? = null, /** * The new field secure_backup_setup_methods is an array listing the methods the client should display. @@ -65,7 +65,7 @@ data class E2EWellKnownConfig( * clients should fallback to the default value of: ["key", "passphrase"]. */ @Json(name = "secure_backup_setup_methods") - val secureBackupSetupMethods: List? = listOf("key"), // Tchap: force to configure secure backup even if well-known is empty + val secureBackupSetupMethods: List? = null, /** * Configuration for sharing keys strategy which should be used instead of [im.vector.app.config.Config.KEY_SHARING_STRATEGY]. From 216762a06bd7c31fa2c9ea7417006863426bed1f Mon Sep 17 00:00:00 2001 From: yostyle Date: Tue, 9 Jul 2024 00:59:47 +0200 Subject: [PATCH 3/5] Fix reset SSSS --- .../quads/SharedSecureStorageViewModel.kt | 35 ++++++++++----- .../BootstrapSetupRecoveryKeyFragment.kt | 23 +++++----- .../recover/BootstrapSharedViewModel.kt | 17 +++++--- .../features/home/HomeActivityViewModel.kt | 43 ++++++++++++------- 4 files changed, 75 insertions(+), 43 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecureStorageViewModel.kt b/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecureStorageViewModel.kt index 4ab11a218c..206a93aa67 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecureStorageViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecureStorageViewModel.kt @@ -32,12 +32,16 @@ import im.vector.app.core.di.hiltMavericksViewModelFactory import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.platform.WaitingViewData import im.vector.app.core.resources.StringProvider +import im.vector.app.features.VectorFeatures +import im.vector.app.features.raw.wellknown.getElementWellknown +import im.vector.app.features.raw.wellknown.isSecureBackupRequired import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import org.matrix.android.sdk.api.Matrix import org.matrix.android.sdk.api.listeners.ProgressListener +import org.matrix.android.sdk.api.raw.RawService import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.securestorage.IntegrityResult import org.matrix.android.sdk.api.session.securestorage.KeyInfo @@ -88,7 +92,9 @@ data class SharedSecureStorageViewState( class SharedSecureStorageViewModel @AssistedInject constructor( @Assisted private val initialState: SharedSecureStorageViewState, private val stringProvider: StringProvider, + private val vectorFeatures: VectorFeatures, private val session: Session, + private val rawService: RawService, private val matrix: Matrix, ) : VectorViewModel(initialState) { @@ -102,16 +108,25 @@ class SharedSecureStorageViewModel @AssistedInject constructor( setState { copy(userId = session.myUserId) } - if (initialState.requestType is RequestType.ReadSecrets) { - val integrityResult = - session.sharedSecretStorageService().checkShouldBeAbleToAccessSecrets(initialState.requestType.secretsName, initialState.keyId) - if (integrityResult !is IntegrityResult.Success) { - _viewEvents.post( - SharedSecureStorageViewEvent.Error( - stringProvider.getString(R.string.enter_secret_storage_invalid), - true - ) - ) + + // TCHAP force to configure secure backup even if well-known is null + // Do not check integrity if the session is not verified + viewModelScope.launch(Dispatchers.IO) { + val elementWellKnown = rawService.getElementWellknown(session.sessionParams) + val isSecureBackupRequired = elementWellKnown?.isSecureBackupRequired() ?: vectorFeatures.tchapIsSecureBackupRequired() + val isThisSessionVerified = session.cryptoService().crossSigningService().isCrossSigningVerified() + + if ((isThisSessionVerified || !isSecureBackupRequired) && initialState.requestType is RequestType.ReadSecrets) { + val integrityResult = + session.sharedSecretStorageService().checkShouldBeAbleToAccessSecrets(initialState.requestType.secretsName, initialState.keyId) + if (integrityResult !is IntegrityResult.Success) { + _viewEvents.post( + SharedSecureStorageViewEvent.Error( + stringProvider.getString(R.string.enter_secret_storage_invalid), + true + ) + ) + } } } diff --git a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapSetupRecoveryKeyFragment.kt b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapSetupRecoveryKeyFragment.kt index 2e94294dbb..08e929b192 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapSetupRecoveryKeyFragment.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapSetupRecoveryKeyFragment.kt @@ -44,18 +44,17 @@ class BootstrapSetupRecoveryKeyFragment : // TCHAP we directly send user to Security Key // Actions when a key backup exist - // views.bootstrapSetupSecureSubmit.views.bottomSheetActionClickableZone.debouncedClicks { - // sharedViewModel.handle(BootstrapActions.StartKeyBackupMigration) - // } - - // Actions when there is no key backup - // views.bootstrapSetupSecureUseSecurityKey.views.bottomSheetActionClickableZone.debouncedClicks { - // sharedViewModel.handle(BootstrapActions.Start(userWantsToEnterPassphrase = false)) - // } - // views.bootstrapSetupSecureUseSecurityPassphrase.views.bottomSheetActionClickableZone.debouncedClicks { - // sharedViewModel.handle(BootstrapActions.Start(userWantsToEnterPassphrase = true)) - // } - +// views.bootstrapSetupSecureSubmit.views.bottomSheetActionClickableZone.debouncedClicks { +// sharedViewModel.handle(BootstrapActions.StartKeyBackupMigration) +// } +// +// // Actions when there is no key backup +// views.bootstrapSetupSecureUseSecurityKey.views.bottomSheetActionClickableZone.debouncedClicks { +// sharedViewModel.handle(BootstrapActions.Start(userWantsToEnterPassphrase = false)) +// } +// views.bootstrapSetupSecureUseSecurityPassphrase.views.bottomSheetActionClickableZone.debouncedClicks { +// sharedViewModel.handle(BootstrapActions.Start(userWantsToEnterPassphrase = true)) +// } sharedViewModel.handle(BootstrapActions.Start(userWantsToEnterPassphrase = false)) } diff --git a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapSharedViewModel.kt b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapSharedViewModel.kt index 64c7553ec2..e56ad09284 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapSharedViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapSharedViewModel.kt @@ -93,7 +93,7 @@ class BootstrapSharedViewModel @AssistedInject constructor( val wellKnown = rawService.getElementWellknown(session.sessionParams) setState { copy( - // Tchap: force to configure secure backup key even if well-known is null + // TCHAP force to configure secure backup key even if well-known is null isSecureBackupRequired = wellKnown?.isSecureBackupRequired() ?: vectorFeatures.tchapIsSecureBackupRequired(), secureBackupMethod = wellKnown?.secureBackupMethod() ?: SecureBackupMethod.KEY, ) @@ -214,7 +214,10 @@ class BootstrapSharedViewModel @AssistedInject constructor( } is BootstrapActions.DoInitialize -> { if (state.passphrase == state.passphraseRepeat) { - startInitializeFlow(state) + // TCHAP do not ask user password multiple times + if (state.step !is BootstrapStep.AccountReAuth) { + startInitializeFlow(state) + } } else { setState { copy( @@ -224,7 +227,10 @@ class BootstrapSharedViewModel @AssistedInject constructor( } } is BootstrapActions.DoInitializeGeneratedKey -> { - startInitializeFlow(state) + // TCHAP do not ask user password multiple times + if (state.step !is BootstrapStep.AccountReAuth) { + startInitializeFlow(state) + } } BootstrapActions.RecoveryKeySaved -> { _viewEvents.post(BootstrapViewEvents.RecoveryKeySaved) @@ -435,8 +441,7 @@ class BootstrapSharedViewModel @AssistedInject constructor( progressListener = progressListener, passphrase = state.passphrase, keySpec = state.migrationRecoveryKey?.let { extractCurveKeyFromRecoveryKey(it)?.let { RawBytesKeySpec(it) } }, - // Tchap: do not reset cross signing -// forceResetIfSomeSecretsAreMissing = state.isSecureBackupRequired, + forceResetIfSomeSecretsAreMissing = state.isSecureBackupRequired, setupMode = state.setupMode ) ) { bootstrapResult -> @@ -606,4 +611,4 @@ class BootstrapSharedViewModel @AssistedInject constructor( } } -private val BootstrapViewState.canLeave: Boolean get() = isRecoverySetup // Tchap: can leave even if secure backup is required +private val BootstrapViewState.canLeave: Boolean get() = isRecoverySetup // TCHAP can leave even if secure backup is required diff --git a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt index 91ff947fee..dc88747c25 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt @@ -47,6 +47,7 @@ import im.vector.app.features.raw.wellknown.isSecureBackupRequired import im.vector.app.features.raw.wellknown.withElementWellKnown import im.vector.app.features.session.coroutineScope import im.vector.app.features.settings.VectorPreferences +import im.vector.app.features.settings.devices.v2.verification.CheckIfCurrentSessionCanBeVerifiedUseCase import im.vector.app.features.voicebroadcast.recording.usecase.StopOngoingVoiceBroadcastUseCase import im.vector.lib.core.utils.compat.getParcelableExtraCompat import kotlinx.coroutines.Dispatchers @@ -97,6 +98,7 @@ class HomeActivityViewModel @AssistedInject constructor( private val unregisterUnifiedPushUseCase: UnregisterUnifiedPushUseCase, private val ensureFcmTokenIsRetrievedUseCase: EnsureFcmTokenIsRetrievedUseCase, private val ensureSessionSyncingUseCase: EnsureSessionSyncingUseCase, + private val checkIfCurrentSessionCanBeVerifiedUseCase: CheckIfCurrentSessionCanBeVerifiedUseCase, private val coroutineDispatchers: CoroutineDispatchers, ) : VectorViewModel(initialState) { @@ -391,7 +393,8 @@ class HomeActivityViewModel @AssistedInject constructor( private fun sessionHasBeenUnverified(elementWellKnown: ElementWellKnown?) { val session = activeSessionHolder.getSafeActiveSession() ?: return - val isSecureBackupRequired = elementWellKnown?.isSecureBackupRequired() ?: vectorFeatures.tchapIsSecureBackupRequired() // Tchap: force to configure secure backup even if well-known is null + val isSecureBackupRequired = elementWellKnown?.isSecureBackupRequired() + ?: vectorFeatures.tchapIsSecureBackupRequired() // TCHAP force to configure secure backup even if well-known is null if (isSecureBackupRequired) { // If 4S is forced, force verification // for stability cancel all pending verifications? @@ -425,7 +428,8 @@ class HomeActivityViewModel @AssistedInject constructor( } val elementWellKnown = rawService.getElementWellknown(session.sessionParams) - val isSecureBackupRequired = elementWellKnown?.isSecureBackupRequired() ?: vectorFeatures.tchapIsSecureBackupRequired() // Tchap: force to configure secure backup even if well-known is null + val isSecureBackupRequired = elementWellKnown?.isSecureBackupRequired() + ?: vectorFeatures.tchapIsSecureBackupRequired() // TCHAP force to configure secure backup even if well-known is null // In case of account creation, it is already done before if (initialState.authenticationDescription is AuthenticationDescription.Register) { @@ -464,15 +468,24 @@ class HomeActivityViewModel @AssistedInject constructor( // Is there already cross signing keys here? val mxCrossSigningInfo = session.cryptoService().crossSigningService().getMyCrossSigningKeys() if (mxCrossSigningInfo != null) { -// if (isSecureBackupRequired && !session.sharedSecretStorageService().isRecoverySetup()) { -// // If 4S is forced, start the full interactive setup flow -// _viewEvents.post(HomeActivityViewEvents.StartRecoverySetupFlow) -// } else { + // TCHAP Setup 4S and cross-signing if needed + if (isSecureBackupRequired && !session.sharedSecretStorageService().isRecoverySetup() && mxCrossSigningInfo.isTrusted()) { + // If 4S is forced, start the full interactive setup flow + _viewEvents.post(HomeActivityViewEvents.StartRecoverySetupFlow) + } else { // Cross-signing is already set up for this user, is it trusted? if (!mxCrossSigningInfo.isTrusted()) { if (isSecureBackupRequired) { - // If 4S is forced, force verification - _viewEvents.post(HomeActivityViewEvents.ForceVerification(true)) + // TCHAP Setup 4S and cross-signing if needed + viewModelScope.launch { + val currentSessionCanBeVerified = checkIfCurrentSessionCanBeVerifiedUseCase.execute() + if (currentSessionCanBeVerified) { + // If 4S is forced, force verification + _viewEvents.post(HomeActivityViewEvents.ForceVerification(true)) + } else { + _viewEvents.post(HomeActivityViewEvents.StartRecoverySetupFlow) + } + } } else { // we wan't to check if there is a way to actually verify this session, // that means that there is another session to verify against, or @@ -499,7 +512,7 @@ class HomeActivityViewModel @AssistedInject constructor( } } } -// } + } } else { // Cross signing is not initialized if (isSecureBackupRequired) { @@ -592,11 +605,11 @@ class HomeActivityViewModel @AssistedInject constructor( private suspend fun CrossSigningService.awaitCrossSigninInitialization( block: Continuation.(response: RegistrationFlowResponse, errCode: String?) -> Unit ) { - initializeCrossSigning( - object : UserInteractiveAuthInterceptor { - override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation) { - promise.block(flowResponse, errCode) - } + initializeCrossSigning( + object : UserInteractiveAuthInterceptor { + override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation) { + promise.block(flowResponse, errCode) } - ) + } + ) } From bc5b151f1fd57b8fecd3ba4747ed112dfd082833 Mon Sep 17 00:00:00 2001 From: yostyle Date: Tue, 9 Jul 2024 17:55:45 +0200 Subject: [PATCH 4/5] Update wordings --- .../src/main/res/values-fr/strings.xml | 16 ++++++++-------- .../src/btchap/res/values/config-features.xml | 2 +- .../src/tchap/res/values/config-features.xml | 2 +- .../crypto/recover/BootstrapSharedViewModel.kt | 2 +- .../self/SelfVerificationController.kt | 3 ++- .../self/SelfVerificationViewModel.kt | 4 +--- 6 files changed, 14 insertions(+), 15 deletions(-) diff --git a/library/ui-strings/src/main/res/values-fr/strings.xml b/library/ui-strings/src/main/res/values-fr/strings.xml index 340611eda6..7c88b18119 100644 --- a/library/ui-strings/src/main/res/values-fr/strings.xml +++ b/library/ui-strings/src/main/res/values-fr/strings.xml @@ -1292,7 +1292,7 @@ Nom d’utilisateur Outils de développement Données du compte - Utiliser le Code de Récupération + Vérifier avec le Code de Récupération Si vous n’avez pas accès à un appareil existant Impossible de trouver les secrets dans le stockage Supprimer… @@ -1521,7 +1521,7 @@ EN SAVOIR PLUS COMPRIS L’apparence et l’organisation de votre application évoluent.\nDe nouveaux changements seront introduits progressivement pour faciliter et enrichir votre expérience. - Bienvenue dans la nouvelle version de Tchap ! + Bienvenue dans la nouvelle version de ${app_name} ! Attente de l’historique du chiffrement Impossible d’accéder à ce message car l’envoyeur n’a intentionnellement pas envoyé les clés Vous ne pouvez pas accéder à ce message car l’envoyeur n’a pas confiance en votre appareil @@ -1542,9 +1542,9 @@ Définir le rôle Vous redémarrerez sans aucun historique, message, appareil ou utilisateurs connus Si vous réinitialisez tout - Faites uniquement ceci si vous n\'avez aucun autre appareil pouvant vérifier celui-ci. + Uniquement si vous avez perdu votre Code et n\'avez aucun autre appareil connecté à ${app_name}. Réinitialiser tout - Vous avez perdu votre Code de Récupération ? Générez-en un nouveau. + Générer un nouveau Code de Récupération Impossible d’enregistrer le fichier multimédia Ce compte a été désactivé. Si vous annulez maintenant, vous pourrez perdre les messages et données chiffrés si vous perdez accès à vos identifiants. @@ -2940,12 +2940,12 @@ Message Message de %s Chiffré par un appareil supprimé - Veuillez ne continuer que si vous êtes certain d’avoir perdu tous vos autres appareils et votre clé de sécurité. - La réinitialisation de vos clés de vérification ne peut pas être annulé. Après la réinitialisation, vous n’aurez plus accès à vos anciens messages chiffrés, et tous les amis que vous aviez précédemment vérifiés verront des avertissement de sécurité jusqu\'à ce vous les vérifiiez à nouveau. + Veuillez ne continuer que si vous êtes certain d’avoir perdu tous vos autres appareils et votre Code de Récupération. + La réinitialisation ne peut pas être annulée. Vous n’aurez plus accès à vos anciens messages chiffrés. La demande de vérification n’a pas été trouvée. Elle a peut-être été annulée, ou prise en charge dans une autre session. - Une demande de vérification a été envoyée. Ouvrez l’une de vos autres sessions pour accepter et commencer la vérification. + Une demande de vérification a été envoyée. Ouvrez ${app_name} sur l’un de vos autres appareils pour accepter et commencer la vérification. Reprendre - Vérifiez votre identité pour accéder aux messages chiffrés et prouver votre identité aux autres. + Vérifiez cet appareil pour accéder à vos messages. Vérifier avec un autre appareil Vérification depuis la clé ou phrase de sécurité… Politique d’utilisation acceptable diff --git a/vector-config/src/btchap/res/values/config-features.xml b/vector-config/src/btchap/res/values/config-features.xml index 3fb2fc083d..683c904075 100755 --- a/vector-config/src/btchap/res/values/config-features.xml +++ b/vector-config/src/btchap/res/values/config-features.xml @@ -3,7 +3,7 @@ true true false - false + true diff --git a/vector-config/src/tchap/res/values/config-features.xml b/vector-config/src/tchap/res/values/config-features.xml index 3fb2fc083d..683c904075 100755 --- a/vector-config/src/tchap/res/values/config-features.xml +++ b/vector-config/src/tchap/res/values/config-features.xml @@ -3,7 +3,7 @@ true true false - false + true diff --git a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapSharedViewModel.kt b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapSharedViewModel.kt index e56ad09284..d13da10ee7 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapSharedViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapSharedViewModel.kt @@ -611,4 +611,4 @@ class BootstrapSharedViewModel @AssistedInject constructor( } } -private val BootstrapViewState.canLeave: Boolean get() = isRecoverySetup // TCHAP can leave even if secure backup is required +private val BootstrapViewState.canLeave: Boolean get() = !isSecureBackupRequired || isRecoverySetup // TCHAP add a reminder instead ? diff --git a/vector/src/main/java/im/vector/app/features/crypto/verification/self/SelfVerificationController.kt b/vector/src/main/java/im/vector/app/features/crypto/verification/self/SelfVerificationController.kt index 9871d21601..8548aa3d04 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/verification/self/SelfVerificationController.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/verification/self/SelfVerificationController.kt @@ -296,7 +296,8 @@ class SelfVerificationController @Inject constructor( id("passphrase") title(host.stringProvider.getString(R.string.verification_cannot_access_other_session)) titleColor(host.colorProvider.getColorFromAttribute(R.attr.vctr_content_primary)) - subTitle(host.stringProvider.getString(R.string.verification_use_passphrase)) + // TCHAP use recovery key with no restriction +// subTitle(host.stringProvider.getString(R.string.verification_use_passphrase)) iconRes(R.drawable.ic_arrow_right) iconColor(host.colorProvider.getColorFromAttribute(R.attr.vctr_content_primary)) listener { host.selfVerificationListener?.onClickRecoverFromPassphrase() } diff --git a/vector/src/main/java/im/vector/app/features/crypto/verification/self/SelfVerificationViewModel.kt b/vector/src/main/java/im/vector/app/features/crypto/verification/self/SelfVerificationViewModel.kt index c87a237f18..99d3165fcb 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/verification/self/SelfVerificationViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/verification/self/SelfVerificationViewModel.kt @@ -32,7 +32,6 @@ import im.vector.app.core.di.MavericksAssistedViewModelFactory import im.vector.app.core.di.hiltMavericksViewModelFactory import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider -import im.vector.app.features.VectorFeatures import im.vector.app.features.crypto.verification.SupportedVerificationMethodsProvider import im.vector.app.features.crypto.verification.VerificationAction import im.vector.app.features.crypto.verification.VerificationBottomSheetViewEvents @@ -96,7 +95,6 @@ data class SelfVerificationViewState( class SelfVerificationViewModel @AssistedInject constructor( @Assisted private val initialState: SelfVerificationViewState, private val session: Session, - private val vectorFeatures: VectorFeatures, private val supportedVerificationMethodsProvider: SupportedVerificationMethodsProvider, private val rawService: RawService, private val stringProvider: StringProvider, @@ -139,7 +137,7 @@ class SelfVerificationViewModel @AssistedInject constructor( // This is async, but at this point should be in cache // so it's ok to not wait until result viewModelScope.launch(Dispatchers.IO) { - // Tchap: force verification when recovery is setup + // TCHAP force verification when recovery is setup setState { copy(isVerificationRequired = session.sharedSecretStorageService().isRecoverySetup()) } From e6784fbe92b624b590a6c3cbf383f98046ac595a Mon Sep 17 00:00:00 2001 From: yostyle Date: Tue, 9 Jul 2024 18:05:54 +0200 Subject: [PATCH 5/5] Add changelog --- changelog.d/988.feature | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/988.feature diff --git a/changelog.d/988.feature b/changelog.d/988.feature new file mode 100644 index 0000000000..438387c2b3 --- /dev/null +++ b/changelog.d/988.feature @@ -0,0 +1 @@ +Forcer la génération du code de récupération et la sauvegarde automatique. \ No newline at end of file