Merge pull request #2248 from vector-im/feature/bca/detect_reset
Fix / reset cross signing not well supported
This commit is contained in:
commit
d0cb5f4212
@ -28,6 +28,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 🗣:
|
||||||
- Move store data to `/fastlane/metadata/android` (#812)
|
- Move store data to `/fastlane/metadata/android` (#812)
|
||||||
|
@ -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}")
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
|
@ -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(
|
||||||
|
@ -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()
|
||||||
}
|
}
|
||||||
|
@ -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() {
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user