Fix / reset cross signing not well supported

This commit is contained in:
Valere 2020-10-12 17:03:28 +02:00
parent 40aa012588
commit 840c8e0b91
7 changed files with 88 additions and 7 deletions

View file

@ -26,6 +26,7 @@ Bugfix 🐛:
- Invalid popup when pressing back (#1635)
- Simplifies draft management and should fix bunch of draft issues (#952, #683)
- Very long topic cannot be fully visible (#1957)
- Properly detect cross signing keys reset
Translations 🗣:
-

View file

@ -359,7 +359,6 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM
cryptoStore.storeUserDevices(userId, workingCopy)
}
// Handle cross signing keys update
val masterKey = response.masterKeys?.get(userId)?.toCryptoModel().also {
Timber.v("## CRYPTO | CrossSigning : Got keys for $userId : MSK ${it?.unpaddedBase64PublicKey}")
}

View file

@ -302,22 +302,42 @@ internal class RealmCryptoStore @Inject constructor(
userEntity.crossSigningInfoEntity?.deleteFromRealm()
userEntity.crossSigningInfoEntity = null
} else {
var shouldResetMyDevicesLocalTrust = false
CrossSigningInfoEntity.getOrCreate(realm, userId).let { signingInfo ->
// What should we do if we detect a change of the keys?
val existingMaster = signingInfo.getMasterKey()
if (existingMaster != null && existingMaster.publicKeyBase64 == masterKey.unpaddedBase64PublicKey) {
crossSigningKeysMapper.update(existingMaster, masterKey)
} else {
Timber.d("## CrossSigning MSK change for $userId")
val keyEntity = crossSigningKeysMapper.map(masterKey)
signingInfo.setMasterKey(keyEntity)
if (userId == credentials.userId) {
shouldResetMyDevicesLocalTrust = true
// my msk has changed! clear my private key
// Could we have some race here? e.g I am the one that did change the keys
// could i get this update to early and clear the private keys?
// -> initializeCrossSigning is guarding for that by storing all at once
realm.where<CryptoMetadataEntity>().findFirst()?.apply {
xSignMasterPrivateKey = null
}
}
}
val existingSelfSigned = signingInfo.getSelfSignedKey()
if (existingSelfSigned != null && existingSelfSigned.publicKeyBase64 == selfSigningKey.unpaddedBase64PublicKey) {
crossSigningKeysMapper.update(existingSelfSigned, selfSigningKey)
} else {
Timber.d("## CrossSigning SSK change for $userId")
val keyEntity = crossSigningKeysMapper.map(selfSigningKey)
signingInfo.setSelfSignedKey(keyEntity)
if (userId == credentials.userId) {
shouldResetMyDevicesLocalTrust = true
// my ssk has changed! clear my private key
realm.where<CryptoMetadataEntity>().findFirst()?.apply {
xSignSelfSignedPrivateKey = null
}
}
}
// Only for me
@ -326,8 +346,27 @@ internal class RealmCryptoStore @Inject constructor(
if (existingUSK != null && existingUSK.publicKeyBase64 == userSigningKey.unpaddedBase64PublicKey) {
crossSigningKeysMapper.update(existingUSK, userSigningKey)
} else {
Timber.d("## CrossSigning USK change for $userId")
val keyEntity = crossSigningKeysMapper.map(userSigningKey)
signingInfo.setUserSignedKey(keyEntity)
if (userId == credentials.userId) {
shouldResetMyDevicesLocalTrust = true
// my usk has changed! clear my private key
realm.where<CryptoMetadataEntity>().findFirst()?.apply {
xSignUserPrivateKey = null
}
}
}
}
// When my cross signing keys are reset, we consider clearing all existing device trust
if (shouldResetMyDevicesLocalTrust) {
realm.where<UserEntity>()
.equalTo(UserEntityFields.USER_ID, credentials.userId)
.findFirst()
?.devices?.forEach {
it?.trustLevelEntity?.crossSignedVerified = false
it?.trustLevelEntity?.locallyVerified = it.deviceId == credentials.deviceId
}
}
userEntity.crossSigningInfoEntity = signingInfo

View file

@ -138,6 +138,7 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable, UnknownDeviceDet
is HomeActivityViewEvents.AskPasswordToInitCrossSigning -> handleAskPasswordToInitCrossSigning(it)
is HomeActivityViewEvents.OnNewSession -> handleOnNewSession(it)
HomeActivityViewEvents.PromptToEnableSessionPush -> handlePromptToEnablePush()
is HomeActivityViewEvents.OnCrossSignedInvalidated -> handleCrossSigningInvalidated(it)
}.exhaustive
}
homeActivityViewModel.subscribe(this) { renderState(it) }
@ -182,6 +183,17 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable, UnknownDeviceDet
}
}
private fun handleCrossSigningInvalidated(event: HomeActivityViewEvents.OnCrossSignedInvalidated) {
// We need to ask
promptSecurityEvent(
event.userItem,
R.string.crosssigning_verify_this_session,
R.string.confirm_your_identity
) {
it.navigator.waitSessionVerification(it)
}
}
private fun handleOnNewSession(event: HomeActivityViewEvents.OnNewSession) {
// We need to ask
promptSecurityEvent(

View file

@ -22,5 +22,6 @@ import org.matrix.android.sdk.api.util.MatrixItem
sealed class HomeActivityViewEvents : VectorViewEvents {
data class AskPasswordToInitCrossSigning(val userItem: MatrixItem.UserItem?) : HomeActivityViewEvents()
data class OnNewSession(val userItem: MatrixItem.UserItem?, val waitForIncomingRequest: Boolean = true) : HomeActivityViewEvents()
data class OnCrossSignedInvalidated(val userItem: MatrixItem.UserItem?) : HomeActivityViewEvents()
object PromptToEnableSessionPush : HomeActivityViewEvents()
}

View file

@ -27,6 +27,9 @@ import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.platform.VectorViewModel
import im.vector.app.features.login.ReAuthHelper
import im.vector.app.features.settings.VectorPreferences
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.MatrixCallback
import org.matrix.android.sdk.api.NoOpMatrixCallback
import org.matrix.android.sdk.api.pushrules.RuleIds
@ -38,9 +41,7 @@ import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo
import org.matrix.android.sdk.internal.crypto.model.MXUsersDevicesMap
import org.matrix.android.sdk.internal.crypto.model.rest.UserPasswordAuth
import org.matrix.android.sdk.rx.asObservable
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import org.matrix.android.sdk.rx.rx
import timber.log.Timber
class HomeActivityViewModel @AssistedInject constructor(
@ -67,11 +68,39 @@ class HomeActivityViewModel @AssistedInject constructor(
}
private var checkBootstrap = false
private var onceTrusted = false
init {
observeInitialSync()
mayBeInitializeCrossSigning()
checkSessionPushIsOn()
observeCrossSigningReset()
}
private fun observeCrossSigningReset() {
val safeActiveSession = activeSessionHolder.getSafeActiveSession()
val crossSigningService = safeActiveSession
?.cryptoService()
?.crossSigningService()
onceTrusted = crossSigningService
?.allPrivateKeysKnown() ?: false
safeActiveSession
?.rx()
?.liveCrossSigningInfo(safeActiveSession.myUserId)
?.subscribe {
val isVerified = it.getOrNull()?.isTrusted() ?: false
if (!isVerified && onceTrusted) {
// cross signing keys have been reset
// Tigger a popup to re-verify
_viewEvents.post(
HomeActivityViewEvents.OnCrossSignedInvalidated(
safeActiveSession.getUser(safeActiveSession.myUserId)?.toMatrixItem()
)
)
}
onceTrusted = isVerified
}?.disposeOnClear()
}
private fun observeInitialSync() {

View file

@ -30,6 +30,8 @@ import im.vector.app.core.platform.EmptyViewEvents
import im.vector.app.core.platform.VectorViewModel
import im.vector.app.core.platform.VectorViewModelAction
import im.vector.app.features.settings.VectorPreferences
import io.reactivex.Observable
import io.reactivex.functions.Function3
import org.matrix.android.sdk.api.NoOpMatrixCallback
import org.matrix.android.sdk.api.extensions.orFalse
import org.matrix.android.sdk.api.session.Session
@ -39,8 +41,6 @@ import org.matrix.android.sdk.api.util.toMatrixItem
import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo
import org.matrix.android.sdk.internal.crypto.model.rest.DeviceInfo
import org.matrix.android.sdk.internal.crypto.store.PrivateKeysInfo
import io.reactivex.Observable
import io.reactivex.functions.Function3
import org.matrix.android.sdk.rx.rx
import timber.log.Timber
import java.util.concurrent.TimeUnit