Update Self Verification BottomSheet for quads

This commit is contained in:
Valere 2020-02-13 12:45:42 +01:00 committed by Benoit Marty
parent cb73955946
commit 3dc89c8d87
21 changed files with 112 additions and 70 deletions

View File

@ -53,6 +53,8 @@ interface CrossSigningService {
fun trustUser(otherUserId: String,
callback: MatrixCallback<Unit>)
fun markMyMasterKeyAsTrusted()
/**
* Sign one of your devices and upload the signature
*/

View File

@ -374,7 +374,9 @@ internal class DefaultCrossSigningService @Inject constructor(
?.fromBase64NoPadding()
var isMaterKeyTrusted = false
if (masterPrivateKey != null) {
if (myMasterKey.trustLevel?.locallyVerified == true) {
isMaterKeyTrusted = true
} else if (masterPrivateKey != null) {
// Check if private match public
var olmPkSigning: OlmPkSigning? = null
try {
@ -507,6 +509,11 @@ internal class DefaultCrossSigningService @Inject constructor(
}.executeBy(taskExecutor)
}
override fun markMyMasterKeyAsTrusted() {
cryptoStore.markMyMasterKeyAsLocallyTrusted(true)
checkSelfTrust()
}
override fun signDevice(deviceId: String, callback: MatrixCallback<Unit>) {
// This device should be yours
val device = cryptoStore.getUserDevice(userId, deviceId)

View File

@ -413,6 +413,8 @@ internal interface IMXCryptoStore {
fun getLiveCrossSigningInfo(userId: String) : LiveData<Optional<MXCrossSigningInfo>>
fun setCrossSigningInfo(userId: String, info: MXCrossSigningInfo?)
fun markMyMasterKeyAsLocallyTrusted(trusted: Boolean)
fun storePrivateKeysInfo(msk: String?, usk: String?, ssk: String?)
fun getCrossSigningPrivateKeys() : PrivateKeysInfo?

View File

@ -1094,6 +1094,24 @@ internal class RealmCryptoStore @Inject constructor(
}
}
override fun markMyMasterKeyAsLocallyTrusted(trusted: Boolean) {
doRealmTransaction(realmConfiguration) { realm ->
realm.where<CryptoMetadataEntity>().findFirst()?.userId?.let { myUserId ->
CrossSigningInfoEntity.get(realm, myUserId)?.getMasterKey()?.let { xInfoEntity ->
val level = xInfoEntity.trustLevelEntity
if (level == null) {
val newLevel = realm.createObject(TrustLevelEntity::class.java)
newLevel.locallyVerified = trusted
xInfoEntity.trustLevelEntity = newLevel
} else {
level.locallyVerified = trusted
}
}
}
}
}
private fun addOrUpdateCrossSigningInfo(realm: Realm, userId: String, info: MXCrossSigningInfo?): CrossSigningInfoEntity? {
var existing = CrossSigningInfoEntity.get(realm, userId)
if (info == null) {

View File

@ -222,14 +222,19 @@ internal class DefaultQrCodeVerificationTransaction(
private fun trust(canTrustOtherUserMasterKey: Boolean, toVerifyDeviceIds: List<String>) {
// If not me sign his MSK and upload the signature
if (otherUserId != userId && canTrustOtherUserMasterKey) {
// we should trust this master key
// And check verification MSK -> SSK?
crossSigningService.trustUser(otherUserId, object : MatrixCallback<Unit> {
override fun onFailure(failure: Throwable) {
Timber.e(failure, "## QR Verification: Failed to trust User $otherUserId")
}
})
if (canTrustOtherUserMasterKey) {
if (otherUserId != userId ) {
// we should trust this master key
// And check verification MSK -> SSK?
crossSigningService.trustUser(otherUserId, object : MatrixCallback<Unit> {
override fun onFailure(failure: Throwable) {
Timber.e(failure, "## QR Verification: Failed to trust User $otherUserId")
}
})
} else {
//Mark my keys as trusted locally
crossSigningService.markMyMasterKeyAsTrusted()
}
}
if (otherUserId == userId) {

View File

@ -28,4 +28,5 @@ sealed class VerificationAction : VectorViewModelAction {
data class SASMatchAction(val otherUserId: String, val sasTransactionId: String) : VerificationAction()
data class SASDoNotMatchAction(val otherUserId: String, val sasTransactionId: String) : VerificationAction()
object GotItConclusion : VerificationAction()
object SkipVerification : VerificationAction()
}

View File

@ -29,6 +29,7 @@ import butterknife.BindView
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.sas.VerificationTxState
import im.vector.riotx.R
import im.vector.riotx.core.di.ScreenComponent
@ -54,8 +55,8 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment() {
val otherUserId: String,
val verificationId: String? = null,
val roomId: String? = null,
// Special mode where UX should show loading wheel until other user sends a request/tx
val waitForIncomingRequest: Boolean = false
// Special mode where UX should show loading wheel until other session sends a request/tx
val selfVerificationMode: Boolean = false
) : Parcelable
@Inject
@ -183,7 +184,7 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment() {
}
// If it's an outgoing
if (state.pendingRequest.invoke() == null || state.pendingRequest.invoke()?.isIncoming == false || state.waitForOtherUserMode) {
if (state.pendingRequest.invoke() == null || state.pendingRequest.invoke()?.isIncoming == false || state.selfVerificationMode) {
Timber.v("## SAS show bottom sheet for outgoing request")
if (state.pendingRequest.invoke()?.isReady == true) {
Timber.v("## SAS show bottom sheet for outgoing and ready request")
@ -230,14 +231,25 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment() {
}
companion object {
fun withArgs(roomId: String?, otherUserId: String, transactionId: String? = null, waitForIncomingRequest: Boolean = false): VerificationBottomSheet {
fun withArgs(roomId: String?, otherUserId: String, transactionId: String? = null): VerificationBottomSheet {
return VerificationBottomSheet().apply {
arguments = Bundle().apply {
putParcelable(MvRx.KEY_ARG, VerificationArgs(
otherUserId = otherUserId,
roomId = roomId,
verificationId = transactionId,
waitForIncomingRequest = waitForIncomingRequest
selfVerificationMode = false
))
}
}
}
fun forSelfVerification(session: Session) : VerificationBottomSheet{
return VerificationBottomSheet().apply {
arguments = Bundle().apply {
putParcelable(MvRx.KEY_ARG, VerificationArgs(
otherUserId = session.myUserId,
selfVerificationMode = true
))
}
}

View File

@ -52,7 +52,7 @@ data class VerificationBottomSheetViewState(
val qrTransactionState: VerificationTxState? = null,
val transactionId: String? = null,
// true when we display the loading and we wait for the other (incoming request)
val waitForOtherUserMode: Boolean = false,
val selfVerificationMode: Boolean = false,
val isMe: Boolean = false
) : MvRxState
@ -67,10 +67,10 @@ class VerificationBottomSheetViewModel @AssistedInject constructor(@Assisted ini
val userItem = session.getUser(args.otherUserId)
val isWaitingForOtherMode = args.waitForIncomingRequest
val selfVerificationMode = args.selfVerificationMode
var autoReady = false
val pr = if (isWaitingForOtherMode) {
val pr = if (selfVerificationMode) {
// See if active tx for this user and take it
session.cryptoService().verificationService().getExistingVerificationRequest(args.otherUserId)
@ -100,7 +100,7 @@ class VerificationBottomSheetViewModel @AssistedInject constructor(@Assisted ini
qrTransactionState = qrTx?.state,
transactionId = pr?.transactionId ?: args.verificationId,
pendingRequest = if (pr != null) Success(pr) else Uninitialized,
waitForOtherUserMode = isWaitingForOtherMode,
selfVerificationMode = selfVerificationMode,
roomId = args.roomId,
isMe = args.otherUserId == session.myUserId
)
@ -250,6 +250,9 @@ class VerificationBottomSheetViewModel @AssistedInject constructor(@Assisted ini
is VerificationAction.GotItConclusion -> {
_viewEvents.post(VerificationBottomSheetViewEvents.Dismiss)
}
is VerificationAction.SkipVerification -> {
_viewEvents.post(VerificationBottomSheetViewEvents.Dismiss)
}
}.exhaustive
}
@ -258,7 +261,7 @@ class VerificationBottomSheetViewModel @AssistedInject constructor(@Assisted ini
}
override fun transactionUpdated(tx: VerificationTransaction) = withState { state ->
if (state.waitForOtherUserMode && state.transactionId == null) {
if (state.selfVerificationMode && state.transactionId == null) {
// is this an incoming with that user
if (tx.isIncoming && tx.otherUserId == state.otherUserMxItem?.id) {
// Also auto accept incoming if needed!
@ -308,7 +311,7 @@ class VerificationBottomSheetViewModel @AssistedInject constructor(@Assisted ini
override fun verificationRequestUpdated(pr: PendingVerificationRequest) = withState { state ->
if (state.waitForOtherUserMode && state.pendingRequest.invoke() == null && state.transactionId == null) {
if (state.selfVerificationMode && state.pendingRequest.invoke() == null && state.transactionId == null) {
// is this an incoming with that user
if (pr.isIncoming && pr.otherUserId == state.otherUserMxItem?.id) {
if (!pr.isReady) {

View File

@ -50,7 +50,7 @@ class VerificationRequestController @Inject constructor(
val state = viewState ?: return
val matrixItem = viewState?.otherUserMxItem ?: return
if (state.waitForOtherUserMode) {
if (state.selfVerificationMode) {
bottomSheetVerificationNoticeItem {
id("notice")
notice(stringProvider.getString(R.string.verification_open_other_to_verify))
@ -62,8 +62,28 @@ class VerificationRequestController @Inject constructor(
bottomSheetVerificationWaitingItem {
id("waiting")
title(stringProvider.getString(R.string.verification_request_waiting_for, matrixItem.getBestName()))
title(stringProvider.getString(R.string.verification_request_waiting, matrixItem.getBestName()))
}
bottomSheetVerificationActionItem {
id("passphrase")
title(stringProvider.getString(R.string.verification_cannot_access_other_session))
titleColor(colorProvider.getColorFromAttribute(R.attr.riotx_text_primary))
subTitle(stringProvider.getString(R.string.verification_use_passphrase))
iconRes(R.drawable.ic_arrow_right)
iconColor(colorProvider.getColorFromAttribute(R.attr.riotx_text_primary))
listener { listener?.onClickRecoverFromPassphrase() }
}
bottomSheetVerificationActionItem {
id("skip")
title(stringProvider.getString(R.string.skip))
titleColor(colorProvider.getColor(R.color.riotx_destructive_accent))
// subTitle(stringProvider.getString(R.string.verification_use_passphrase))
iconRes(R.drawable.ic_arrow_right)
iconColor(colorProvider.getColor(R.color.riotx_destructive_accent))
listener { listener?.onClickDismiss() }
}
} else {
val styledText = matrixItem.let {
stringProvider.getString(R.string.verification_request_notice, it.id)
@ -112,5 +132,7 @@ class VerificationRequestController @Inject constructor(
interface Listener {
fun onClickOnVerificationStart()
fun onClickRecoverFromPassphrase()
fun onClickDismiss()
}
}

View File

@ -61,4 +61,11 @@ class VerificationRequestFragment @Inject constructor(
viewModel.handle(VerificationAction.RequestVerificationByDM(otherUserId, state.roomId))
}
}
override fun onClickRecoverFromPassphrase() {
}
override fun onClickDismiss() {
viewModel.handle(VerificationAction.SkipVerification)
}
}

View File

@ -145,41 +145,12 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable {
val crossSigningEnabledOnAccount = myCrossSigningKeys != null
if (crossSigningEnabledOnAccount && myCrossSigningKeys?.isTrusted() == false) {
// We need to ask
sharedActionViewModel.hasDisplayedCompleteSecurityPrompt = true
PopupAlertManager.postVectorAlert(
PopupAlertManager.VectorAlert(
uid = "completeSecurity",
title = getString(R.string.crosssigning_verify_this_session),
description = getString(R.string.crosssigning_other_user_not_trust),
iconId = R.drawable.ic_shield_warning
).apply {
colorInt = ContextCompat.getColor(this@HomeActivity, R.color.riotx_positive_accent)
contentAction = Runnable {
Runnable {
(weakCurrentActivity?.get() as? VectorBaseActivity)?.let {
it.navigator.waitSessionVerification(it)
}
}
}
dismissedAction = Runnable {
// tx.cancel()
}
addButton(
getString(R.string.later),
Runnable {
}
)
addButton(
getString(R.string.verification_profile_verify),
Runnable {
(weakCurrentActivity?.get() as? VectorBaseActivity)?.let {
it.navigator.waitSessionVerification(it)
}
}
)
}
)
navigator.waitSessionVerification(this)
} else {
// TODO upgrade security -> bootstrap cross signing
}
}

View File

@ -95,12 +95,8 @@ class DefaultNavigator @Inject constructor(
override fun waitSessionVerification(context: Context) {
val session = sessionHolder.getSafeActiveSession() ?: return
if (context is VectorBaseActivity) {
VerificationBottomSheet.withArgs(
roomId = null,
otherUserId = session.myUserId,
waitForIncomingRequest = true
).show(context.supportFragmentManager, VerificationBottomSheet.WAITING_SELF_VERIF_TAG)
VerificationBottomSheet.forSelfVerification(session)
.show(context.supportFragmentManager, VerificationBottomSheet.WAITING_SELF_VERIF_TAG)
}
}

View File

@ -60,7 +60,7 @@
android:id="@+id/bottomSheetFragmentContainer"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_marginTop="8dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/verificationRequestAvatar" />

View File

@ -2140,7 +2140,6 @@ Abisua: Fitxategi hau ezabatu daiteke aplikazioa desinstalatzen bada.</string>
<item quantity="other">%d saio aktibo</item>
</plurals>
<string name="crosssigning_verify_this_session">Egiaztatu saio hau</string>
<string name="crosssigning_other_user_not_trust">Beste erabiltzaile batzuk ez fidagarritzat jo lezakete</string>
<string name="complete_security">Bete segurtasuna</string>

View File

@ -2148,7 +2148,6 @@ Si vous navez pas configuré de nouvelle méthode de récupération, un attaq
<item quantity="other">%d sessions actives</item>
</plurals>
<string name="crosssigning_verify_this_session">Vérifier cette session</string>
<string name="crosssigning_other_user_not_trust">Les autres utilisateurs ne lui font peut-être pas confiance</string>
<string name="complete_security">Compléter la sécurité</string>

View File

@ -2143,7 +2143,6 @@ Ha nem te állítottad be a visszaállítási metódust, akkor egy támadó pró
<item quantity="other">%d munkamenet használatban</item>
</plurals>
<string name="crosssigning_verify_this_session">Munkamenet ellenőrzése</string>
<string name="crosssigning_other_user_not_trust">Más felhasználók lehet, hogy nem bíznak benne</string>
<string name="complete_security">Biztonság beállítása</string>

View File

@ -2193,7 +2193,6 @@
<item quantity="other">%d sessioni attive</item>
</plurals>
<string name="crosssigning_verify_this_session">Verifica questa sessione</string>
<string name="crosssigning_other_user_not_trust">Gli altri utenti potrebbero non fidarsi</string>
<string name="complete_security">Completa la sicurezza</string>

View File

@ -2062,7 +2062,6 @@ Që të garantoni se sju shpëton gjë, thjesht mbajeni të aktivizuar mekani
<item quantity="other">%d sesione aktive</item>
</plurals>
<string name="crosssigning_verify_this_session">Verifikoni këtë sesion</string>
<string name="crosssigning_other_user_not_trust">Përdorues të tjerë mund të mos e besojnë</string>
<string name="complete_security">Siguri e Plotë</string>

View File

@ -2093,7 +2093,6 @@ Matrix 中的消息可見度類似于電子郵件。我們忘記您的郵件意
<item quantity="other">%d 活躍的工作階段</item>
</plurals>
<string name="crosssigning_verify_this_session">驗證此工作階段</string>
<string name="crosssigning_other_user_not_trust">其他使用者可能不會信任它</string>
<string name="complete_security">全面的安全性</string>

View File

@ -2121,11 +2121,10 @@ Not all features in Riot are implemented in RiotX yet. Main missing (and coming
<item quantity="other">%d active sessions</item>
</plurals>
<string name="crosssigning_verify_this_session">Verify this session</string>
<string name="crosssigning_other_user_not_trust">Other users may not trust it</string>
<string name="complete_security">Complete Security</string>
<string name="verification_open_other_to_verify">Open an existing session &amp; use it to verify this one, granting it access to encrypted messages. If you cant access one, use your recovery key or passphrase.</string>
<string name="verification_open_other_to_verify">Open an existing session &amp; use it to verify this one, granting it access to encrypted messages.</string>
<string name="verification_profile_verify">Verify</string>

View File

@ -18,6 +18,9 @@
</plurals>
<string name="poll_item_selected_aria">Selected Option</string>
<string name="command_description_poll">Creates a simple poll</string>
<string name="verification_cannot_access_other_session">Cant access an existing session?</string>
<string name="verification_use_passphrase">Use your recovery key or passphrase</string>
<!-- END Strings added by Valere -->