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) - Invalid popup when pressing back (#1635)
- Simplifies draft management and should fix bunch of draft issues (#952, #683) - Simplifies draft management and should fix bunch of draft issues (#952, #683)
- Very long topic cannot be fully visible (#1957) - Very long topic cannot be fully visible (#1957)
- Properly detect cross signing keys reset
Translations 🗣: Translations 🗣:
- -

View File

@ -359,7 +359,6 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM
cryptoStore.storeUserDevices(userId, workingCopy) cryptoStore.storeUserDevices(userId, workingCopy)
} }
// Handle cross signing keys update
val masterKey = response.masterKeys?.get(userId)?.toCryptoModel().also { val masterKey = response.masterKeys?.get(userId)?.toCryptoModel().also {
Timber.v("## CRYPTO | CrossSigning : Got keys for $userId : MSK ${it?.unpaddedBase64PublicKey}") 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?.deleteFromRealm()
userEntity.crossSigningInfoEntity = null userEntity.crossSigningInfoEntity = null
} else { } else {
var shouldResetMyDevicesLocalTrust = false
CrossSigningInfoEntity.getOrCreate(realm, userId).let { signingInfo -> CrossSigningInfoEntity.getOrCreate(realm, userId).let { signingInfo ->
// What should we do if we detect a change of the keys? // What should we do if we detect a change of the keys?
val existingMaster = signingInfo.getMasterKey() val existingMaster = signingInfo.getMasterKey()
if (existingMaster != null && existingMaster.publicKeyBase64 == masterKey.unpaddedBase64PublicKey) { if (existingMaster != null && existingMaster.publicKeyBase64 == masterKey.unpaddedBase64PublicKey) {
crossSigningKeysMapper.update(existingMaster, masterKey) crossSigningKeysMapper.update(existingMaster, masterKey)
} else { } else {
Timber.d("## CrossSigning MSK change for $userId")
val keyEntity = crossSigningKeysMapper.map(masterKey) val keyEntity = crossSigningKeysMapper.map(masterKey)
signingInfo.setMasterKey(keyEntity) 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() val existingSelfSigned = signingInfo.getSelfSignedKey()
if (existingSelfSigned != null && existingSelfSigned.publicKeyBase64 == selfSigningKey.unpaddedBase64PublicKey) { if (existingSelfSigned != null && existingSelfSigned.publicKeyBase64 == selfSigningKey.unpaddedBase64PublicKey) {
crossSigningKeysMapper.update(existingSelfSigned, selfSigningKey) crossSigningKeysMapper.update(existingSelfSigned, selfSigningKey)
} else { } else {
Timber.d("## CrossSigning SSK change for $userId")
val keyEntity = crossSigningKeysMapper.map(selfSigningKey) val keyEntity = crossSigningKeysMapper.map(selfSigningKey)
signingInfo.setSelfSignedKey(keyEntity) 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 // Only for me
@ -326,10 +346,29 @@ internal class RealmCryptoStore @Inject constructor(
if (existingUSK != null && existingUSK.publicKeyBase64 == userSigningKey.unpaddedBase64PublicKey) { if (existingUSK != null && existingUSK.publicKeyBase64 == userSigningKey.unpaddedBase64PublicKey) {
crossSigningKeysMapper.update(existingUSK, userSigningKey) crossSigningKeysMapper.update(existingUSK, userSigningKey)
} else { } else {
Timber.d("## CrossSigning USK change for $userId")
val keyEntity = crossSigningKeysMapper.map(userSigningKey) val keyEntity = crossSigningKeysMapper.map(userSigningKey)
signingInfo.setUserSignedKey(keyEntity) 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 userEntity.crossSigningInfoEntity = signingInfo
} }
} }
@ -1197,7 +1236,7 @@ internal class RealmCryptoStore @Inject constructor(
.findAll() .findAll()
.mapNotNull { entity -> .mapNotNull { entity ->
when (entity.type) { when (entity.type) {
GossipRequestType.KEY -> { GossipRequestType.KEY -> {
IncomingRoomKeyRequest( IncomingRoomKeyRequest(
userId = entity.otherUserId, userId = entity.otherUserId,
deviceId = entity.otherDeviceId, deviceId = entity.otherDeviceId,

View File

@ -138,6 +138,7 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable, UnknownDeviceDet
is HomeActivityViewEvents.AskPasswordToInitCrossSigning -> handleAskPasswordToInitCrossSigning(it) is HomeActivityViewEvents.AskPasswordToInitCrossSigning -> handleAskPasswordToInitCrossSigning(it)
is HomeActivityViewEvents.OnNewSession -> handleOnNewSession(it) is HomeActivityViewEvents.OnNewSession -> handleOnNewSession(it)
HomeActivityViewEvents.PromptToEnableSessionPush -> handlePromptToEnablePush() HomeActivityViewEvents.PromptToEnableSessionPush -> handlePromptToEnablePush()
is HomeActivityViewEvents.OnCrossSignedInvalidated -> handleCrossSigningInvalidated(it)
}.exhaustive }.exhaustive
} }
homeActivityViewModel.subscribe(this) { renderState(it) } 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) { private fun handleOnNewSession(event: HomeActivityViewEvents.OnNewSession) {
// We need to ask // We need to ask
promptSecurityEvent( promptSecurityEvent(

View File

@ -22,5 +22,6 @@ import org.matrix.android.sdk.api.util.MatrixItem
sealed class HomeActivityViewEvents : VectorViewEvents { sealed class HomeActivityViewEvents : VectorViewEvents {
data class AskPasswordToInitCrossSigning(val userItem: MatrixItem.UserItem?) : HomeActivityViewEvents() data class AskPasswordToInitCrossSigning(val userItem: MatrixItem.UserItem?) : HomeActivityViewEvents()
data class OnNewSession(val userItem: MatrixItem.UserItem?, val waitForIncomingRequest: Boolean = true) : HomeActivityViewEvents() data class OnNewSession(val userItem: MatrixItem.UserItem?, val waitForIncomingRequest: Boolean = true) : HomeActivityViewEvents()
data class OnCrossSignedInvalidated(val userItem: MatrixItem.UserItem?) : HomeActivityViewEvents()
object PromptToEnableSessionPush : 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.core.platform.VectorViewModel
import im.vector.app.features.login.ReAuthHelper import im.vector.app.features.login.ReAuthHelper
import im.vector.app.features.settings.VectorPreferences 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.MatrixCallback
import org.matrix.android.sdk.api.NoOpMatrixCallback import org.matrix.android.sdk.api.NoOpMatrixCallback
import org.matrix.android.sdk.api.pushrules.RuleIds 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.MXUsersDevicesMap
import org.matrix.android.sdk.internal.crypto.model.rest.UserPasswordAuth import org.matrix.android.sdk.internal.crypto.model.rest.UserPasswordAuth
import org.matrix.android.sdk.rx.asObservable import org.matrix.android.sdk.rx.asObservable
import kotlinx.coroutines.Dispatchers import org.matrix.android.sdk.rx.rx
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import timber.log.Timber import timber.log.Timber
class HomeActivityViewModel @AssistedInject constructor( class HomeActivityViewModel @AssistedInject constructor(
@ -67,11 +68,39 @@ class HomeActivityViewModel @AssistedInject constructor(
} }
private var checkBootstrap = false private var checkBootstrap = false
private var onceTrusted = false
init { init {
observeInitialSync() observeInitialSync()
mayBeInitializeCrossSigning() mayBeInitializeCrossSigning()
checkSessionPushIsOn() 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() { 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.VectorViewModel
import im.vector.app.core.platform.VectorViewModelAction import im.vector.app.core.platform.VectorViewModelAction
import im.vector.app.features.settings.VectorPreferences 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.NoOpMatrixCallback
import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.extensions.orFalse
import org.matrix.android.sdk.api.session.Session 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.CryptoDeviceInfo
import org.matrix.android.sdk.internal.crypto.model.rest.DeviceInfo import org.matrix.android.sdk.internal.crypto.model.rest.DeviceInfo
import org.matrix.android.sdk.internal.crypto.store.PrivateKeysInfo 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 org.matrix.android.sdk.rx.rx
import timber.log.Timber import timber.log.Timber
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit