mirror of
https://github.com/element-hq/element-android
synced 2024-11-27 20:06:51 +03:00
Fix / reset cross signing not well supported
This commit is contained in:
parent
40aa012588
commit
840c8e0b91
7 changed files with 88 additions and 7 deletions
|
@ -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 🗣:
|
||||
-
|
||||
|
|
|
@ -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}")
|
||||
}
|
||||
|
|
|
@ -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,10 +346,29 @@ 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
|
||||
}
|
||||
}
|
||||
|
@ -1197,7 +1236,7 @@ internal class RealmCryptoStore @Inject constructor(
|
|||
.findAll()
|
||||
.mapNotNull { entity ->
|
||||
when (entity.type) {
|
||||
GossipRequestType.KEY -> {
|
||||
GossipRequestType.KEY -> {
|
||||
IncomingRoomKeyRequest(
|
||||
userId = entity.otherUserId,
|
||||
deviceId = entity.otherDeviceId,
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue