Skip to content

Commit

Permalink
Merge pull request #6475 from vector-im/feature/bca/crypto_unable_sel…
Browse files Browse the repository at this point in the history
…f_verify

Handle case when device cannot be verified
  • Loading branch information
BillCarsonFr authored Jul 26, 2022
2 parents e9bd271 + ac7b47b commit 8e2eb19
Show file tree
Hide file tree
Showing 11 changed files with 178 additions and 26 deletions.
1 change: 1 addition & 0 deletions changelog.d/6466.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
When there is no way to verify a device (no 4S nor other device) propose to reset verification keys
75 changes: 75 additions & 0 deletions vector/src/androidTest/java/im/vector/app/CantVerifyTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* Copyright (c) 2022 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.app

import android.view.View
import androidx.test.espresso.Espresso
import androidx.test.espresso.assertion.ViewAssertions
import androidx.test.espresso.matcher.ViewMatchers
import androidx.test.ext.junit.rules.ActivityScenarioRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.LargeTest
import im.vector.app.features.MainActivity
import im.vector.app.ui.robot.ElementRobot
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import java.util.UUID

@RunWith(AndroidJUnit4::class)
@LargeTest
class CantVerifyTest : VerificationTestBase() {

@get:Rule
val activityRule = ActivityScenarioRule(MainActivity::class.java)

private val elementRobot = ElementRobot()
var userName: String = "loginTest_${UUID.randomUUID()}"

@Test
fun checkCantVerifyPopup() {
// Let' create an account
// This first session will create cross signing keys then logout
elementRobot.signUp(userName)
Espresso.onView(ViewMatchers.isRoot()).perform(SleepViewAction.sleep(2000))

elementRobot.signout(false)
Espresso.onView(ViewMatchers.isRoot()).perform(SleepViewAction.sleep(2000))

// Let's login again now
// There are no methods to verify (no other devices, nor 4S)
// So it should ask to reset all
elementRobot.login(userName)

val activity = EspressoHelper.getCurrentActivity()!!
Espresso.onView(ViewMatchers.isRoot())
.perform(waitForView(ViewMatchers.withText(R.string.crosssigning_cannot_verify_this_session)))

// check that the text is correct
val popup = activity.findViewById<View>(com.tapadoo.alerter.R.id.llAlertBackground)!!
activity.runOnUiThread { popup.performClick() }

// ensure that it's the 4S setup bottomsheet
Espresso.onView(ViewMatchers.withId(R.id.bottomSheetFragmentContainer))
.check(ViewAssertions.matches(ViewMatchers.isDisplayed()))

Espresso.onView(ViewMatchers.isRoot()).perform(SleepViewAction.sleep(2000))

Espresso.onView(ViewMatchers.withText(R.string.bottom_sheet_setup_secure_backup_title))
.check(ViewAssertions.matches(ViewMatchers.isDisplayed()))
}
}
16 changes: 14 additions & 2 deletions vector/src/main/java/im/vector/app/features/home/HomeActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,8 @@ class HomeActivity :
homeActivityViewModel.observeViewEvents {
when (it) {
is HomeActivityViewEvents.AskPasswordToInitCrossSigning -> handleAskPasswordToInitCrossSigning(it)
is HomeActivityViewEvents.OnNewSession -> handleOnNewSession(it)
is HomeActivityViewEvents.CurrentSessionNotVerified -> handleOnNewSession(it)
is HomeActivityViewEvents.CurrentSessionCannotBeVerified -> handleCantVerify(it)
HomeActivityViewEvents.PromptToEnableSessionPush -> handlePromptToEnablePush()
HomeActivityViewEvents.StartRecoverySetupFlow -> handleStartRecoverySetup()
is HomeActivityViewEvents.ForceVerification -> {
Expand Down Expand Up @@ -424,7 +425,7 @@ class HomeActivity :
}
}

private fun handleOnNewSession(event: HomeActivityViewEvents.OnNewSession) {
private fun handleOnNewSession(event: HomeActivityViewEvents.CurrentSessionNotVerified) {
// We need to ask
promptSecurityEvent(
event.userItem,
Expand All @@ -439,6 +440,17 @@ class HomeActivity :
}
}

private fun handleCantVerify(event: HomeActivityViewEvents.CurrentSessionCannotBeVerified) {
// We need to ask
promptSecurityEvent(
event.userItem,
R.string.crosssigning_cannot_verify_this_session,
R.string.crosssigning_cannot_verify_this_session_desc
) {
it.navigator.open4SSetup(it, SetupMode.PASSPHRASE_AND_NEEDED_SECRETS_RESET)
}
}

private fun handlePromptToEnablePush() {
popupAlertManager.postVectorAlert(
DefaultVectorAlert(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,13 @@ import org.matrix.android.sdk.api.util.MatrixItem

sealed interface HomeActivityViewEvents : VectorViewEvents {
data class AskPasswordToInitCrossSigning(val userItem: MatrixItem.UserItem?) : HomeActivityViewEvents
data class OnNewSession(val userItem: MatrixItem.UserItem?, val waitForIncomingRequest: Boolean = true) : HomeActivityViewEvents
data class CurrentSessionNotVerified(
val userItem: MatrixItem.UserItem?,
val waitForIncomingRequest: Boolean = true,
) : HomeActivityViewEvents
data class CurrentSessionCannotBeVerified(
val userItem: MatrixItem.UserItem?,
) : HomeActivityViewEvents
data class OnCrossSignedInvalidated(val userItem: MatrixItem.UserItem) : HomeActivityViewEvents
object PromptToEnableSessionPush : HomeActivityViewEvents
object ShowAnalyticsOptIn : HomeActivityViewEvents
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -362,14 +362,30 @@ class HomeActivityViewModel @AssistedInject constructor(
// If 4S is forced, force verification
_viewEvents.post(HomeActivityViewEvents.ForceVerification(true))
} else {
// New session
_viewEvents.post(
HomeActivityViewEvents.OnNewSession(
session.getUser(session.myUserId)?.toMatrixItem(),
// Always send request instead of waiting for an incoming as per recent EW changes
false
)
)
// 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
// secure backup is setup
val hasTargetDeviceToVerifyAgainst = session
.cryptoService()
.getUserDevices(session.myUserId)
.size >= 2 // this one + another
val is4Ssetup = session.sharedSecretStorageService().isRecoverySetup()
if (hasTargetDeviceToVerifyAgainst || is4Ssetup) {
// New session
_viewEvents.post(
HomeActivityViewEvents.CurrentSessionNotVerified(
session.getUser(session.myUserId)?.toMatrixItem(),
// Always send request instead of waiting for an incoming as per recent EW changes
false
)
)
} else {
_viewEvents.post(
HomeActivityViewEvents.CurrentSessionCannotBeVerified(
session.getUser(session.myUserId)?.toMatrixItem(),
)
)
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,18 @@ class DeviceVerificationInfoBottomSheetController @Inject constructor(
.toEpoxyCharSequence()
)
}
} else {
genericItem {
id("reset${cryptoDeviceInfo.deviceId}")
style(ItemStyle.BIG_TEXT)
titleIconResourceId(shield)
title(host.stringProvider.getString(R.string.crosssigning_cannot_verify_this_session).toEpoxyCharSequence())
description(
host.stringProvider
.getString(R.string.crosssigning_cannot_verify_this_session_desc)
.toEpoxyCharSequence()
)
}
}
} else {
if (!currentSessionIsTrusted) {
Expand Down Expand Up @@ -141,22 +153,42 @@ class DeviceVerificationInfoBottomSheetController @Inject constructor(
description("(${cryptoDeviceInfo.deviceId})".toEpoxyCharSequence())
}

if (isMine && !currentSessionIsTrusted && data.canVerifySession) {
// Add complete security
bottomSheetDividerItem {
id("completeSecurityDiv")
}
bottomSheetVerificationActionItem {
id("completeSecurity")
title(host.stringProvider.getString(R.string.crosssigning_verify_this_session))
titleColor(host.colorProvider.getColorFromAttribute(R.attr.colorPrimary))
iconRes(R.drawable.ic_arrow_right)
iconColor(host.colorProvider.getColorFromAttribute(R.attr.colorPrimary))
listener {
host.callback?.onAction(DevicesAction.CompleteSecurity)
if (isMine) {
if (!currentSessionIsTrusted) {
if (data.canVerifySession) {
// Add complete security
bottomSheetDividerItem {
id("completeSecurityDiv")
}
bottomSheetVerificationActionItem {
id("completeSecurity")
title(host.stringProvider.getString(R.string.crosssigning_verify_this_session))
titleColor(host.colorProvider.getColorFromAttribute(R.attr.colorPrimary))
iconRes(R.drawable.ic_arrow_right)
iconColor(host.colorProvider.getColorFromAttribute(R.attr.colorPrimary))
listener {
host.callback?.onAction(DevicesAction.CompleteSecurity)
}
}
} else {
bottomSheetDividerItem {
id("resetSecurityDiv")
}
bottomSheetVerificationActionItem {
id("resetSecurity")
title(host.stringProvider.getString(R.string.secure_backup_reset_all))
titleColor(host.colorProvider.getColorFromAttribute(R.attr.colorPrimary))
iconRes(R.drawable.ic_arrow_right)
iconColor(host.colorProvider.getColorFromAttribute(R.attr.colorPrimary))
listener {
host.callback?.onAction(DevicesAction.ResetSecurity)
}
}
}
}
} else if (!isMine) {
} else
/** if (!isMine) **/
{
if (currentSessionIsTrusted) {
// we can propose to verify it
val isVerified = cryptoDeviceInfo.trustLevel?.crossSigningVerified.orFalse()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ sealed class DevicesAction : VectorViewModelAction {
data class VerifyMyDevice(val deviceId: String) : DevicesAction()
data class VerifyMyDeviceManually(val deviceId: String) : DevicesAction()
object CompleteSecurity : DevicesAction()
object ResetSecurity : DevicesAction()
data class MarkAsManuallyVerified(val cryptoDeviceInfo: CryptoDeviceInfo) : DevicesAction()

object SsoAuthDone : DevicesAction()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,6 @@ sealed class DevicesViewEvents : VectorViewEvents {
) : DevicesViewEvents()

data class ShowManuallyVerify(val cryptoDeviceInfo: CryptoDeviceInfo) : DevicesViewEvents()

object PromptResetSecrets : DevicesViewEvents()
}
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,7 @@ class DevicesViewModel @AssistedInject constructor(
uiaContinuation = null
pendingAuth = null
}
DevicesAction.ResetSecurity -> _viewEvents.post(DevicesViewEvents.PromptResetSecrets)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.databinding.DialogBaseEditTextBinding
import im.vector.app.databinding.FragmentGenericRecyclerBinding
import im.vector.app.features.auth.ReAuthActivity
import im.vector.app.features.crypto.recover.SetupMode
import im.vector.app.features.crypto.verification.VerificationBottomSheet
import org.matrix.android.sdk.api.auth.data.LoginFlowTypes
import org.matrix.android.sdk.api.session.crypto.model.DeviceInfo
Expand Down Expand Up @@ -89,6 +90,9 @@ class VectorSettingsDevicesFragment @Inject constructor(
viewModel.handle(DevicesAction.MarkAsManuallyVerified(it.cryptoDeviceInfo))
}
}
is DevicesViewEvents.PromptResetSecrets -> {
navigator.open4SSetup(requireContext(), SetupMode.PASSPHRASE_AND_NEEDED_SECRETS_RESET)
}
}
}
}
Expand Down
4 changes: 3 additions & 1 deletion vector/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2352,7 +2352,9 @@
<item quantity="other">%d active sessions</item>
</plurals>

<string name="crosssigning_verify_this_session">Verify this login</string>
<string name="crosssigning_verify_this_session">Verify this device</string>
<string name="crosssigning_cannot_verify_this_session">Unable to verify this device</string>
<string name="crosssigning_cannot_verify_this_session_desc">You won’t be able to access encrypted message history. Reset your Secure Message Backup and verification keys to start fresh.</string>

<string name="verification_open_other_to_verify">Use an existing session to verify this one, granting it access to encrypted messages.</string>

Expand Down

0 comments on commit 8e2eb19

Please sign in to comment.