From f0a9be2ec7277c1a81854e02991cd0a99c529b7c Mon Sep 17 00:00:00 2001 From: Valere Date: Wed, 29 Apr 2020 18:46:11 +0200 Subject: [PATCH] Better session detection --- .../java/im/vector/matrix/rx/RxSession.kt | 8 +++++ .../crosssigning/CrossSigningService.kt | 2 ++ .../DefaultCrossSigningService.kt | 4 +++ .../internal/crypto/store/IMXCryptoStore.kt | 1 + .../crypto/store/db/RealmCryptoStore.kt | 19 ++++++++++++ .../riotx/features/home/HomeDetailFragment.kt | 6 +++- .../UnknwonDeviceDetectorSharedViewModel.kt | 29 +++++++++---------- 7 files changed, 53 insertions(+), 16 deletions(-) diff --git a/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxSession.kt b/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxSession.kt index c2e65823ba..c40306044a 100644 --- a/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxSession.kt +++ b/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxSession.kt @@ -32,6 +32,7 @@ import im.vector.matrix.android.api.util.Optional import im.vector.matrix.android.api.util.toOptional import im.vector.matrix.android.internal.crypto.model.CryptoDeviceInfo import im.vector.matrix.android.internal.crypto.model.rest.DeviceInfo +import im.vector.matrix.android.internal.crypto.store.PrivateKeysInfo import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountDataEvent import io.reactivex.Observable import io.reactivex.Single @@ -131,6 +132,13 @@ class RxSession(private val session: Session) { } } + fun liveCrossSigningPrivateKeys(): Observable> { + return session.cryptoService().crossSigningService().getLiveCrossSigningPrivateKeys().asObservable() + .startWithCallable { + session.cryptoService().crossSigningService().getCrossSigningPrivateKeys().toOptional() + } + } + fun liveAccountData(types: Set): Observable> { return session.getLiveAccountDataEvents(types).asObservable() .startWithCallable { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/crosssigning/CrossSigningService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/crosssigning/CrossSigningService.kt index 4085e1233d..f1c998cee5 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/crosssigning/CrossSigningService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/crosssigning/CrossSigningService.kt @@ -55,6 +55,8 @@ interface CrossSigningService { fun getCrossSigningPrivateKeys(): PrivateKeysInfo? + fun getLiveCrossSigningPrivateKeys(): LiveData> + fun canCrossSign(): Boolean fun trustUser(otherUserId: String, diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/crosssigning/DefaultCrossSigningService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/crosssigning/DefaultCrossSigningService.kt index 2166e4be3a..2fee8130fb 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/crosssigning/DefaultCrossSigningService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/crosssigning/DefaultCrossSigningService.kt @@ -470,6 +470,10 @@ internal class DefaultCrossSigningService @Inject constructor( return cryptoStore.getCrossSigningPrivateKeys() } + override fun getLiveCrossSigningPrivateKeys(): LiveData> { + return cryptoStore.getLiveCrossSigningPrivateKeys() + } + override fun canCrossSign(): Boolean { return checkSelfTrust().isVerified() && cryptoStore.getCrossSigningPrivateKeys()?.selfSigned != null && cryptoStore.getCrossSigningPrivateKeys()?.user != null diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/IMXCryptoStore.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/IMXCryptoStore.kt index 53598a1bb5..b6037075b0 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/IMXCryptoStore.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/IMXCryptoStore.kt @@ -409,6 +409,7 @@ internal interface IMXCryptoStore { fun storeUSKPrivateKey(usk: String?) fun getCrossSigningPrivateKeys(): PrivateKeysInfo? + fun getLiveCrossSigningPrivateKeys(): LiveData> fun saveBackupRecoveryKey(recoveryKey: String?, version: String?) fun getKeyBackupRecoveryKeyInfo() : SavedKeyBackupKeyInfo? diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/RealmCryptoStore.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/RealmCryptoStore.kt index 942c51feb6..809bb080fa 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/RealmCryptoStore.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/RealmCryptoStore.kt @@ -366,6 +366,25 @@ internal class RealmCryptoStore @Inject constructor( } } + override fun getLiveCrossSigningPrivateKeys(): LiveData> { + val liveData = monarchy.findAllMappedWithChanges( + { realm: Realm -> + realm + .where() + }, + { + PrivateKeysInfo( + master = it.xSignMasterPrivateKey, + selfSigned = it.xSignSelfSignedPrivateKey, + user = it.xSignUserPrivateKey + ) + } + ) + return Transformations.map(liveData) { + it.firstOrNull().toOptional() + } + } + override fun storePrivateKeysInfo(msk: String?, usk: String?, ssk: String?) { doRealmTransaction(realmConfiguration) { realm -> realm.where().findFirst()?.apply { diff --git a/vector/src/main/java/im/vector/riotx/features/home/HomeDetailFragment.kt b/vector/src/main/java/im/vector/riotx/features/home/HomeDetailFragment.kt index fdf6481f23..5dfd8ae121 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/HomeDetailFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/HomeDetailFragment.kt @@ -90,7 +90,8 @@ class HomeDetailFragment @Inject constructor( unknownDeviceDetectorSharedViewModel.subscribe { state -> state.unknownSessions.invoke()?.let { unknownDevices -> - if (state.canCrossSign && unknownDevices.isNotEmpty()) { +// Timber.v("## Detector Triggerred in fragment - ${unknownDevices.firstOrNull()}") + if (unknownDevices.firstOrNull()?.currentSessionTrust == true) { Timber.v("## Detector - ${unknownDevices.size} Unknown sessions") unknownDevices.forEachIndexed { index, detectionInfo -> Timber.v("## Detector - #$index deviceId:${detectionInfo.deviceInfo.deviceId} lastSeenTs:${detectionInfo.deviceInfo.lastSeenTs} is New: ${detectionInfo.isNew}") @@ -125,6 +126,9 @@ class HomeDetailFragment @Inject constructor( (weakCurrentActivity?.get() as? VectorBaseActivity) ?.navigator ?.requestSessionVerification(requireContext(), newest.deviceId ?: "") + unknownDeviceDetectorSharedViewModel.handle( + UnknownDeviceDetectorSharedViewModel.Action.IgnoreDevice(newest.deviceId?.let { listOf(it) } ?: emptyList()) + ) } dismissedAction = Runnable { unknownDeviceDetectorSharedViewModel.handle( diff --git a/vector/src/main/java/im/vector/riotx/features/home/UnknwonDeviceDetectorSharedViewModel.kt b/vector/src/main/java/im/vector/riotx/features/home/UnknwonDeviceDetectorSharedViewModel.kt index c366a75c33..50d0ef05c6 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/UnknwonDeviceDetectorSharedViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/UnknwonDeviceDetectorSharedViewModel.kt @@ -26,9 +26,11 @@ import im.vector.matrix.android.api.NoOpMatrixCallback import im.vector.matrix.android.api.extensions.orFalse import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.api.util.MatrixItem +import im.vector.matrix.android.api.util.Optional import im.vector.matrix.android.api.util.toMatrixItem import im.vector.matrix.android.internal.crypto.model.CryptoDeviceInfo import im.vector.matrix.android.internal.crypto.model.rest.DeviceInfo +import im.vector.matrix.android.internal.crypto.store.PrivateKeysInfo import im.vector.matrix.rx.rx import im.vector.riotx.core.di.HasScreenInjector import im.vector.riotx.core.platform.EmptyViewEvents @@ -36,19 +38,19 @@ import im.vector.riotx.core.platform.VectorViewModel import im.vector.riotx.core.platform.VectorViewModelAction import im.vector.riotx.features.settings.VectorPreferences import io.reactivex.Observable -import io.reactivex.functions.BiFunction +import io.reactivex.functions.Function3 import timber.log.Timber import java.util.concurrent.TimeUnit data class UnknownDevicesState( val myMatrixItem: MatrixItem.UserItem? = null, - val unknownSessions: Async> = Uninitialized, - val canCrossSign: Boolean = false + val unknownSessions: Async> = Uninitialized ) : MvRxState data class DeviceDetectionInfo( val deviceInfo: DeviceInfo, - val isNew: Boolean + val isNew: Boolean, + val currentSessionTrust: Boolean ) class UnknownDeviceDetectorSharedViewModel( @@ -76,10 +78,13 @@ class UnknownDeviceDetectorSharedViewModel( } ) - Observable.combineLatest, List, List>( + Observable.combineLatest, List, Optional, List>( session.rx().liveUserCryptoDevices(session.myUserId), session.rx().liveMyDeviceInfo(), - BiFunction { cryptoList, infoList -> + session.rx().liveCrossSigningPrivateKeys(), + Function3 { cryptoList, infoList, pInfo -> +// Timber.v("## Detector trigger ${cryptoList.map { "${it.deviceId} ${it.trustLevel}" }}") +// Timber.v("## Detector trigger canCrossSign ${pInfo.get().selfSigned != null}") infoList .filter { info -> // filter verified session, by checking the crypto device info @@ -92,16 +97,15 @@ class UnknownDeviceDetectorSharedViewModel( val deviceKnownSince = cryptoList.firstOrNull { it.deviceId == deviceInfo.deviceId }?.firsTimeSeenLocalTs ?: 0 DeviceDetectionInfo( deviceInfo, - deviceKnownSince > currentSessionTs + 60_000 // short window to avoid false positive + deviceKnownSince > currentSessionTs + 60_000, // short window to avoid false positive, + pInfo.getOrNull()?.selfSigned != null // adding this to pass distinct when cross sign change ) -// .also { -// Timber.v("## Detector - first seen Difference with ${deviceInfo.deviceId} is ${deviceKnownSince - currentSessionTs}") -// } } } ) .distinct() .execute { async -> +// Timber.v("## Detector trigger passed distinct") copy( myMatrixItem = session.getUser(session.myUserId)?.toMatrixItem(), unknownSessions = async @@ -116,11 +120,6 @@ class UnknownDeviceDetectorSharedViewModel( session.cryptoService().fetchDevicesList(NoOpMatrixCallback()) }.disposeOnClear() - session.rx().liveCrossSigningInfo(session.myUserId) - .execute { - copy(canCrossSign = session.cryptoService().crossSigningService().canCrossSign()) - } - // trigger a refresh of lastSeen / last Ip session.cryptoService().fetchDevicesList(NoOpMatrixCallback()) }