DeviceId should be non-null so we can inject it properly and OlmMachine too.

This commit is contained in:
ganfra 2022-06-06 14:53:56 +02:00
parent e519561edf
commit c1961d1fda
32 changed files with 462 additions and 449 deletions

View File

@ -49,7 +49,7 @@ data class Credentials(
/** /**
* ID of the logged-in device. Will be the same as the corresponding parameter in the request, if one was specified. * ID of the logged-in device. Will be the same as the corresponding parameter in the request, if one was specified.
*/ */
@Json(name = "device_id") val deviceId: String?, @Json(name = "device_id") val deviceId: String,
/** /**
* Optional client configuration provided by the server. If present, clients SHOULD use the provided object to * Optional client configuration provided by the server. If present, clients SHOULD use the provided object to
* reconfigure themselves, optionally validating the URLs within. * reconfigure themselves, optionally validating the URLs within.
@ -59,5 +59,5 @@ data class Credentials(
) )
internal fun Credentials.sessionId(): String { internal fun Credentials.sessionId(): String {
return (if (deviceId.isNullOrBlank()) userId else "$userId|$deviceId").md5() return (if (deviceId.isBlank()) userId else "$userId|$deviceId").md5()
} }

View File

@ -119,7 +119,7 @@ interface CryptoService {
fun shouldEncryptForInvitedMembers(roomId: String): Boolean fun shouldEncryptForInvitedMembers(roomId: String): Boolean
suspend fun downloadKeys(userIds: List<String>, forceDownload: Boolean = false): MXUsersDevicesMap<CryptoDeviceInfo> suspend fun downloadKeysIfNeeded(userIds: List<String>, forceDownload: Boolean = false): MXUsersDevicesMap<CryptoDeviceInfo>
suspend fun getCryptoDeviceInfoList(userId: String): List<CryptoDeviceInfo> suspend fun getCryptoDeviceInfoList(userId: String): List<CryptoDeviceInfo>

View File

@ -0,0 +1,69 @@
/*
* Copyright (c) 2022 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.matrix.android.sdk.internal.crypto
import org.matrix.android.sdk.api.extensions.orFalse
import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel
import org.matrix.android.sdk.internal.di.UserId
import javax.inject.Inject
internal class ComputeShieldForGroupUseCase @Inject constructor(
@UserId private val myUserId: String
) {
suspend operator fun invoke(olmMachine: OlmMachine, userIds: List<String>): RoomEncryptionTrustLevel {
val myIdentity = olmMachine.getIdentity(myUserId)
val allTrustedUserIds = userIds
.filter { userId ->
olmMachine.getIdentity(userId)?.verified() == true
}
return if (allTrustedUserIds.isEmpty()) {
RoomEncryptionTrustLevel.Default
} else {
// If one of the verified user as an untrusted device -> warning
// If all devices of all verified users are trusted -> green
// else -> black
allTrustedUserIds
.map { userId ->
olmMachine.getUserDevices(userId)
}
.flatten()
.let { allDevices ->
if (myIdentity != null) {
allDevices.any { !it.toCryptoDeviceInfo().trustLevel?.crossSigningVerified.orFalse() }
} else {
// TODO check that if myIdentity is null ean
// Legacy method
allDevices.any { !it.toCryptoDeviceInfo().isVerified }
}
}
.let { hasWarning ->
if (hasWarning) {
RoomEncryptionTrustLevel.Warning
} else {
if (userIds.size == allTrustedUserIds.size) {
// all users are trusted and all devices are verified
RoomEncryptionTrustLevel.Trusted
} else {
RoomEncryptionTrustLevel.Default
}
}
}
}
}
}

View File

@ -20,9 +20,7 @@ import org.matrix.android.sdk.api.session.crypto.model.MXEventDecryptionResult
import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.Event
import javax.inject.Inject import javax.inject.Inject
internal class DecryptRoomEventUseCase @Inject constructor(olmMachineProvider: OlmMachineProvider) { internal class DecryptRoomEventUseCase @Inject constructor(private val olmMachine: OlmMachine) {
private val olmMachine = olmMachineProvider.olmMachine
suspend operator fun invoke(event: Event): MXEventDecryptionResult { suspend operator fun invoke(event: Event): MXEventDecryptionResult {
return olmMachine.decryptRoomEvent(event) return olmMachine.decryptRoomEvent(event)

View File

@ -66,7 +66,6 @@ import org.matrix.android.sdk.api.session.sync.model.DeviceOneTimeKeysCountSyncR
import org.matrix.android.sdk.api.session.sync.model.ToDeviceSyncResponse import org.matrix.android.sdk.api.session.sync.model.ToDeviceSyncResponse
import org.matrix.android.sdk.internal.crypto.keysbackup.RustKeyBackupService import org.matrix.android.sdk.internal.crypto.keysbackup.RustKeyBackupService
import org.matrix.android.sdk.internal.crypto.network.OutgoingRequestsProcessor import org.matrix.android.sdk.internal.crypto.network.OutgoingRequestsProcessor
import org.matrix.android.sdk.internal.crypto.network.RequestSender
import org.matrix.android.sdk.internal.crypto.repository.WarnOnUnknownDeviceRepository import org.matrix.android.sdk.internal.crypto.repository.WarnOnUnknownDeviceRepository
import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
import org.matrix.android.sdk.internal.crypto.tasks.DeleteDeviceTask import org.matrix.android.sdk.internal.crypto.tasks.DeleteDeviceTask
@ -99,7 +98,7 @@ private val loggerTag = LoggerTag("DefaultCryptoService", LoggerTag.CRYPTO)
@SessionScope @SessionScope
internal class DefaultCryptoService @Inject constructor( internal class DefaultCryptoService @Inject constructor(
@UserId private val userId: String, @UserId private val userId: String,
@DeviceId private val deviceId: String?, @DeviceId private val deviceId: String,
// the crypto store // the crypto store
private val cryptoStore: IMXCryptoStore, private val cryptoStore: IMXCryptoStore,
// Set of parameters used to configure/customize the end-to-end crypto. // Set of parameters used to configure/customize the end-to-end crypto.
@ -114,30 +113,21 @@ internal class DefaultCryptoService @Inject constructor(
private val cryptoSessionInfoProvider: CryptoSessionInfoProvider, private val cryptoSessionInfoProvider: CryptoSessionInfoProvider,
private val coroutineDispatchers: MatrixCoroutineDispatchers, private val coroutineDispatchers: MatrixCoroutineDispatchers,
private val cryptoCoroutineScope: CoroutineScope, private val cryptoCoroutineScope: CoroutineScope,
private val requestSender: RequestSender, private val olmMachine: OlmMachine,
private val crossSigningService: CrossSigningService, private val crossSigningService: CrossSigningService,
private val verificationService: RustVerificationService, private val verificationService: RustVerificationService,
private val keysBackupService: RustKeyBackupService, private val keysBackupService: RustKeyBackupService,
private val megolmSessionImportManager: MegolmSessionImportManager, private val megolmSessionImportManager: MegolmSessionImportManager,
private val olmMachineProvider: OlmMachineProvider,
private val liveEventManager: dagger.Lazy<StreamEventsManager>, private val liveEventManager: dagger.Lazy<StreamEventsManager>,
private val prepareToEncrypt: PrepareToEncryptUseCase, private val prepareToEncrypt: PrepareToEncryptUseCase,
private val encryptEventContent: EncryptEventContentUseCase, private val encryptEventContent: EncryptEventContentUseCase,
private val getRoomUserIds: GetRoomUserIdsUseCase, private val getRoomUserIds: GetRoomUserIdsUseCase,
private val outgoingRequestsProcessor: OutgoingRequestsProcessor,
) : CryptoService { ) : CryptoService {
private val isStarting = AtomicBoolean(false) private val isStarting = AtomicBoolean(false)
private val isStarted = AtomicBoolean(false) private val isStarted = AtomicBoolean(false)
private val olmMachine by lazy { olmMachineProvider.olmMachine }
private val outgoingRequestsProcessor = OutgoingRequestsProcessor(
requestSender = requestSender,
coroutineScope = cryptoCoroutineScope,
cryptoSessionInfoProvider = cryptoSessionInfoProvider,
shieldComputer = crossSigningService::shieldForGroup
)
fun onStateEvent(roomId: String, event: Event) { fun onStateEvent(roomId: String, event: Event) {
when (event.type) { when (event.type) {
EventType.STATE_ROOM_ENCRYPTION -> onRoomEncryptionEvent(roomId, event) EventType.STATE_ROOM_ENCRYPTION -> onRoomEncryptionEvent(roomId, event)
@ -164,7 +154,7 @@ internal class DefaultCryptoService @Inject constructor(
val params = SetDeviceNameTask.Params(deviceId, deviceName) val params = SetDeviceNameTask.Params(deviceId, deviceName)
setDeviceNameTask.execute(params) setDeviceNameTask.execute(params)
try { try {
downloadKeys(listOf(userId), true) downloadKeysIfNeeded(listOf(userId), true)
} catch (failure: Throwable) { } catch (failure: Throwable) {
Timber.tag(loggerTag.value).w(failure, "setDeviceName: Failed to refresh of crypto device") Timber.tag(loggerTag.value).w(failure, "setDeviceName: Failed to refresh of crypto device")
} }
@ -489,7 +479,6 @@ internal class DefaultCryptoService @Inject constructor(
if (!isRoomEncrypted(roomId)) { if (!isRoomEncrypted(roomId)) {
return return
} }
event.stateKey?.let { userId -> event.stateKey?.let { userId ->
val roomMember: RoomMemberContent? = event.content.toModel() val roomMember: RoomMemberContent? = event.content.toModel()
val membership = roomMember?.membership val membership = roomMember?.membership
@ -722,7 +711,7 @@ internal class DefaultCryptoService @Inject constructor(
// TODO // TODO
} }
override suspend fun downloadKeys(userIds: List<String>, forceDownload: Boolean): MXUsersDevicesMap<CryptoDeviceInfo> { override suspend fun downloadKeysIfNeeded(userIds: List<String>, forceDownload: Boolean): MXUsersDevicesMap<CryptoDeviceInfo> {
return withContext(coroutineDispatchers.crypto) { return withContext(coroutineDispatchers.crypto) {
olmMachine.ensureUserDevicesMap(userIds, forceDownload) olmMachine.ensureUserDevicesMap(userIds, forceDownload)
} }

View File

@ -16,20 +16,20 @@
package org.matrix.android.sdk.internal.crypto package org.matrix.android.sdk.internal.crypto
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.matrix.android.sdk.api.MatrixCoroutineDispatchers import org.matrix.android.sdk.api.MatrixCoroutineDispatchers
import org.matrix.android.sdk.api.session.crypto.crosssigning.DeviceTrustLevel import org.matrix.android.sdk.api.session.crypto.crosssigning.DeviceTrustLevel
import org.matrix.android.sdk.api.session.crypto.model.CryptoDeviceInfo import org.matrix.android.sdk.api.session.crypto.model.CryptoDeviceInfo
import org.matrix.android.sdk.api.session.crypto.model.UnsignedDeviceInfo import org.matrix.android.sdk.api.session.crypto.model.UnsignedDeviceInfo
import org.matrix.android.sdk.api.session.crypto.verification.VerificationMethod import org.matrix.android.sdk.api.session.crypto.verification.VerificationMethod
import org.matrix.android.sdk.api.session.crypto.verification.VerificationService
import org.matrix.android.sdk.internal.crypto.network.RequestSender import org.matrix.android.sdk.internal.crypto.network.RequestSender
import org.matrix.android.sdk.internal.crypto.verification.SasVerification import org.matrix.android.sdk.internal.crypto.verification.SasVerification
import org.matrix.android.sdk.internal.crypto.verification.VerificationRequest import org.matrix.android.sdk.internal.crypto.verification.VerificationRequest
import org.matrix.android.sdk.internal.crypto.verification.VerificationRequestFactory
import org.matrix.android.sdk.internal.crypto.verification.prepareMethods import org.matrix.android.sdk.internal.crypto.verification.prepareMethods
import uniffi.olm.CryptoStoreException import uniffi.olm.CryptoStoreException
import uniffi.olm.OlmMachine
import uniffi.olm.SignatureException import uniffi.olm.SignatureException
import uniffi.olm.Device as InnerDevice import uniffi.olm.Device as InnerDevice
@ -38,14 +38,22 @@ import uniffi.olm.Device as InnerDevice
* This class can be used to directly start a verification flow with the device * This class can be used to directly start a verification flow with the device
* or to manually verify the device. * or to manually verify the device.
*/ */
internal class Device( internal class Device @AssistedInject constructor(
private val innerMachine: OlmMachine, @Assisted private var innerDevice: InnerDevice,
private var innerDevice: InnerDevice, olmMachine: OlmMachine,
private val requestSender: RequestSender, private val requestSender: RequestSender,
private val coroutineDispatchers: MatrixCoroutineDispatchers, private val coroutineDispatchers: MatrixCoroutineDispatchers,
private val listeners: ArrayList<VerificationService.Listener>, private val verificationRequestFactory: VerificationRequest.Factory,
private val verificationRequestFactory: VerificationRequestFactory, private val sasVerificationFactory: SasVerification.Factory
) { ) {
@AssistedFactory
interface Factory {
fun create(innerDevice: InnerDevice): Device
}
private val innerMachine = olmMachine.inner()
@Throws(CryptoStoreException::class) @Throws(CryptoStoreException::class)
private suspend fun refreshData() { private suspend fun refreshData() {
val device = withContext(coroutineDispatchers.io) { val device = withContext(coroutineDispatchers.io) {
@ -99,13 +107,7 @@ internal class Device(
return if (result != null) { return if (result != null) {
requestSender.sendVerificationRequest(result.request) requestSender.sendVerificationRequest(result.request)
SasVerification( sasVerificationFactory.create(result.sas)
machine = innerMachine,
inner = result.sas,
sender = requestSender,
coroutineDispatchers = coroutineDispatchers,
listeners = listeners
)
} else { } else {
null null
} }
@ -173,6 +175,7 @@ internal class Device(
trustLevel = DeviceTrustLevel(crossSigningVerified = innerDevice.crossSigningTrusted, locallyVerified = innerDevice.locallyTrusted), trustLevel = DeviceTrustLevel(crossSigningVerified = innerDevice.crossSigningTrusted, locallyVerified = innerDevice.locallyTrusted),
isBlocked = innerDevice.isBlocked, isBlocked = innerDevice.isBlocked,
// TODO // TODO
firstTimeSeenLocalTs = null) firstTimeSeenLocalTs = null
)
} }
} }

View File

@ -26,15 +26,15 @@ import javax.inject.Inject
private val loggerTag = LoggerTag("EncryptEventContentUseCase", LoggerTag.CRYPTO) private val loggerTag = LoggerTag("EncryptEventContentUseCase", LoggerTag.CRYPTO)
internal class EncryptEventContentUseCase @Inject constructor(olmMachineProvider: OlmMachineProvider, internal class EncryptEventContentUseCase @Inject constructor(
private val prepareToEncrypt: PrepareToEncryptUseCase, private val olmMachine: OlmMachine,
private val clock: Clock) { private val prepareToEncrypt: PrepareToEncryptUseCase,
private val clock: Clock) {
private val olmMachine = olmMachineProvider.olmMachine suspend operator fun invoke(
eventContent: Content,
suspend operator fun invoke(eventContent: Content, eventType: String,
eventType: String, roomId: String): MXEncryptEventContentResult {
roomId: String): MXEncryptEventContentResult {
val t0 = clock.epochMillis() val t0 = clock.epochMillis()
prepareToEncrypt(roomId, ensureAllMembersAreLoaded = false) prepareToEncrypt(roomId, ensureAllMembersAreLoaded = false)
val content = olmMachine.encrypt(roomId, eventType, eventContent) val content = olmMachine.encrypt(roomId, eventType, eventContent)

View File

@ -0,0 +1,62 @@
/*
* Copyright (c) 2022 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.matrix.android.sdk.internal.crypto
import kotlinx.coroutines.withContext
import org.matrix.android.sdk.api.MatrixCoroutineDispatchers
import org.matrix.android.sdk.api.extensions.tryOrNull
import org.matrix.android.sdk.internal.crypto.network.OutgoingRequestsProcessor
import org.matrix.android.sdk.internal.crypto.network.RequestSender
import uniffi.olm.Request
import uniffi.olm.RequestType
import java.util.UUID
import javax.inject.Inject
import javax.inject.Provider
internal class EnsureUsersKeysUseCase @Inject constructor(
private val olmMachine: Provider<OlmMachine>,
private val outgoingRequestsProcessor: OutgoingRequestsProcessor,
private val requestSender: RequestSender,
private val coroutineDispatchers: MatrixCoroutineDispatchers) {
suspend operator fun invoke(userIds: List<String>, forceDownload: Boolean) {
val olmMachine = olmMachine.get()
if (forceDownload) {
tryOrNull("Failed to download keys for $userIds") {
forceKeyDownload(olmMachine, userIds)
}
} else {
userIds.filter { userId ->
!olmMachine.isUserTracked(userId)
}.also { untrackedUserIds ->
olmMachine.updateTrackedUsers(untrackedUserIds)
}
outgoingRequestsProcessor.processOutgoingRequests(olmMachine) {
it is Request.KeysQuery && it.users.intersect(userIds.toSet()).isNotEmpty()
}
}
}
@Throws
private suspend fun forceKeyDownload(olmMachine: OlmMachine, userIds: List<String>) {
withContext(coroutineDispatchers.io) {
val requestId = UUID.randomUUID().toString()
val response = requestSender.queryKeys(Request.KeysQuery(requestId, userIds))
olmMachine.markRequestAsSent(requestId, RequestType.KEYS_QUERY, response)
}
}
}

View File

@ -22,20 +22,22 @@ import org.matrix.android.sdk.api.MatrixCoroutineDispatchers
import org.matrix.android.sdk.api.session.crypto.crosssigning.DeviceTrustLevel import org.matrix.android.sdk.api.session.crypto.crosssigning.DeviceTrustLevel
import org.matrix.android.sdk.internal.crypto.model.rest.RestKeyInfo import org.matrix.android.sdk.internal.crypto.model.rest.RestKeyInfo
import org.matrix.android.sdk.internal.crypto.network.RequestSender import org.matrix.android.sdk.internal.crypto.network.RequestSender
import org.matrix.android.sdk.internal.crypto.verification.VerificationRequestFactory import org.matrix.android.sdk.internal.crypto.verification.VerificationRequest
import uniffi.olm.CryptoStoreException import uniffi.olm.CryptoStoreException
import uniffi.olm.OlmMachine import javax.inject.Inject
import javax.inject.Provider
internal class GetUserIdentityUseCase( internal class GetUserIdentityUseCase @Inject constructor(
private val innerMachine: OlmMachine, private val olmMachine: Provider<OlmMachine>,
private val requestSender: RequestSender, private val requestSender: RequestSender,
private val coroutineDispatchers: MatrixCoroutineDispatchers, private val coroutineDispatchers: MatrixCoroutineDispatchers,
private val moshi: Moshi, private val moshi: Moshi,
private val verificationRequestFactory: VerificationRequestFactory private val verificationRequestFactory: VerificationRequest.Factory
) { ) {
@Throws(CryptoStoreException::class) @Throws(CryptoStoreException::class)
suspend operator fun invoke(userId: String): UserIdentities? { suspend operator fun invoke(userId: String): UserIdentities? {
val innerMachine = olmMachine.get().inner()
val identity = withContext(coroutineDispatchers.io) { val identity = withContext(coroutineDispatchers.io) {
innerMachine.getIdentity(userId) innerMachine.getIdentity(userId)
} }

View File

@ -25,7 +25,6 @@ import kotlinx.coroutines.withContext
import org.matrix.android.sdk.api.MatrixCoroutineDispatchers import org.matrix.android.sdk.api.MatrixCoroutineDispatchers
import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
import org.matrix.android.sdk.api.crypto.MXCRYPTO_ALGORITHM_MEGOLM_BACKUP import org.matrix.android.sdk.api.crypto.MXCRYPTO_ALGORITHM_MEGOLM_BACKUP
import org.matrix.android.sdk.api.extensions.tryOrNull
import org.matrix.android.sdk.api.listeners.ProgressListener import org.matrix.android.sdk.api.listeners.ProgressListener
import org.matrix.android.sdk.api.session.crypto.MXCryptoError import org.matrix.android.sdk.api.session.crypto.MXCryptoError
import org.matrix.android.sdk.api.session.crypto.crosssigning.DeviceTrustLevel import org.matrix.android.sdk.api.session.crypto.crosssigning.DeviceTrustLevel
@ -38,7 +37,6 @@ import org.matrix.android.sdk.api.session.crypto.model.ImportRoomKeysResult
import org.matrix.android.sdk.api.session.crypto.model.MXEventDecryptionResult import org.matrix.android.sdk.api.session.crypto.model.MXEventDecryptionResult
import org.matrix.android.sdk.api.session.crypto.model.MXUsersDevicesMap import org.matrix.android.sdk.api.session.crypto.model.MXUsersDevicesMap
import org.matrix.android.sdk.api.session.crypto.model.UnsignedDeviceInfo import org.matrix.android.sdk.api.session.crypto.model.UnsignedDeviceInfo
import org.matrix.android.sdk.api.session.crypto.verification.VerificationService
import org.matrix.android.sdk.api.session.crypto.verification.VerificationTransaction import org.matrix.android.sdk.api.session.crypto.verification.VerificationTransaction
import org.matrix.android.sdk.api.session.events.model.Content import org.matrix.android.sdk.api.session.events.model.Content
import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.Event
@ -52,10 +50,13 @@ import org.matrix.android.sdk.internal.coroutines.builder.safeInvokeOnClose
import org.matrix.android.sdk.internal.crypto.network.RequestSender import org.matrix.android.sdk.internal.crypto.network.RequestSender
import org.matrix.android.sdk.internal.crypto.verification.SasVerification import org.matrix.android.sdk.internal.crypto.verification.SasVerification
import org.matrix.android.sdk.internal.crypto.verification.VerificationRequest import org.matrix.android.sdk.internal.crypto.verification.VerificationRequest
import org.matrix.android.sdk.internal.crypto.verification.VerificationRequestFactory import org.matrix.android.sdk.internal.crypto.verification.VerificationsProvider
import org.matrix.android.sdk.internal.crypto.verification.qrcode.QrCodeVerification import org.matrix.android.sdk.internal.crypto.verification.qrcode.QrCodeVerification
import org.matrix.android.sdk.internal.di.DeviceId
import org.matrix.android.sdk.internal.di.SessionFilesDirectory
import org.matrix.android.sdk.internal.di.UserId
import org.matrix.android.sdk.internal.network.parsing.CheckNumberType import org.matrix.android.sdk.internal.network.parsing.CheckNumberType
import org.matrix.android.sdk.internal.util.time.Clock import org.matrix.android.sdk.internal.session.SessionScope
import timber.log.Timber import timber.log.Timber
import uniffi.olm.BackupKeys import uniffi.olm.BackupKeys
import uniffi.olm.CrossSigningKeyExport import uniffi.olm.CrossSigningKeyExport
@ -72,7 +73,7 @@ import uniffi.olm.RoomKeyCounts
import uniffi.olm.setLogger import uniffi.olm.setLogger
import java.io.File import java.io.File
import java.nio.charset.Charset import java.nio.charset.Charset
import java.util.UUID import javax.inject.Inject
import uniffi.olm.OlmMachine as InnerMachine import uniffi.olm.OlmMachine as InnerMachine
import uniffi.olm.ProgressListener as RustProgressListener import uniffi.olm.ProgressListener as RustProgressListener
@ -105,20 +106,22 @@ fun setRustLogger() {
setLogger(CryptoLogger() as Logger) setLogger(CryptoLogger() as Logger)
} }
internal class OlmMachine( @SessionScope
user_id: String, internal class OlmMachine @Inject constructor(
device_id: String, @UserId userId: String,
path: File, @DeviceId deviceId: String,
clock: Clock, @SessionFilesDirectory path: File,
private val requestSender: RequestSender, private val requestSender: RequestSender,
private val coroutineDispatchers: MatrixCoroutineDispatchers, private val coroutineDispatchers: MatrixCoroutineDispatchers,
private val moshi: Moshi, private val moshi: Moshi,
private val verificationsProvider: VerificationsProvider,
private val deviceFactory: Device.Factory,
private val getUserIdentity: GetUserIdentityUseCase,
private val ensureUsersKeys: EnsureUsersKeysUseCase,
) { ) {
private val inner: InnerMachine = InnerMachine(user_id, device_id, path.toString(), null) private val inner: InnerMachine = InnerMachine(userId, deviceId, path.toString(), null)
val verificationListeners = ArrayList<VerificationService.Listener>()
private val verificationRequestFactory = VerificationRequestFactory(inner, requestSender, coroutineDispatchers, verificationListeners, clock)
private val getUserIdentity = GetUserIdentityUseCase(inner, requestSender, coroutineDispatchers, moshi, verificationRequestFactory)
private val flowCollectors = FlowCollectors() private val flowCollectors = FlowCollectors()
/** Get our own user ID. */ /** Get our own user ID. */
@ -525,13 +528,12 @@ internal class OlmMachine(
val innerDevice = withContext(coroutineDispatchers.io) { val innerDevice = withContext(coroutineDispatchers.io) {
inner.getDevice(userId, deviceId) inner.getDevice(userId, deviceId)
} ?: return null } ?: return null
return deviceFactory.create(innerDevice)
return innerDevice.wrap()
} }
suspend fun getUserDevices(userId: String): List<Device> { suspend fun getUserDevices(userId: String): List<Device> {
return withContext(coroutineDispatchers.io) { return withContext(coroutineDispatchers.io) {
inner.getUserDevices(userId).map { innerDevice -> innerDevice.wrap() } inner.getUserDevices(userId).map(deviceFactory::create)
} }
} }
@ -545,16 +547,6 @@ internal class OlmMachine(
@Throws(CryptoStoreException::class) @Throws(CryptoStoreException::class)
suspend fun getCryptoDeviceInfo(userId: String): List<CryptoDeviceInfo> { suspend fun getCryptoDeviceInfo(userId: String): List<CryptoDeviceInfo> {
return getUserDevices(userId).map { it.toCryptoDeviceInfo() } return getUserDevices(userId).map { it.toCryptoDeviceInfo() }
/*
// EA doesn't differentiate much between our own and other devices of
// while the rust-sdk does, append our own device here.
if (userId == userId()) {
devices.add(ownDevice())
}
return devices
*/
} }
/** /**
@ -575,16 +567,7 @@ internal class OlmMachine(
return plainDevices return plainDevices
} }
@Throws private suspend fun getUserDevicesMap(userIds: List<String>): MXUsersDevicesMap<CryptoDeviceInfo> {
suspend fun forceKeyDownload(userIds: List<String>) {
withContext(coroutineDispatchers.io) {
val requestId = UUID.randomUUID().toString()
val response = requestSender.queryKeys(Request.KeysQuery(requestId, userIds))
markRequestAsSent(requestId, RequestType.KEYS_QUERY, response)
}
}
suspend fun getUserDevicesMap(userIds: List<String>): MXUsersDevicesMap<CryptoDeviceInfo> {
val userMap = MXUsersDevicesMap<CryptoDeviceInfo>() val userMap = MXUsersDevicesMap<CryptoDeviceInfo>()
for (user in userIds) { for (user in userIds) {
@ -616,18 +599,7 @@ internal class OlmMachine(
* The key query request will be retried a few time in case of shaky connection, but could fail. * The key query request will be retried a few time in case of shaky connection, but could fail.
*/ */
suspend fun ensureUsersKeys(userIds: List<String>, forceDownload: Boolean = false) { suspend fun ensureUsersKeys(userIds: List<String>, forceDownload: Boolean = false) {
val userIdsToFetchKeys = if (forceDownload) { ensureUsersKeys.invoke(userIds, forceDownload)
userIds
} else {
userIds.mapNotNull { userId ->
userId.takeIf { !isUserTracked(it) }
}.also {
updateTrackedUsers(it)
}
}
tryOrNull("Failed to download keys for $userIdsToFetchKeys") {
forceKeyDownload(userIdsToFetchKeys)
}
} }
fun getLiveUserIdentity(userId: String): Flow<Optional<MXCrossSigningInfo>> { fun getLiveUserIdentity(userId: String): Flow<Optional<MXCrossSigningInfo>> {
@ -692,14 +664,12 @@ internal class OlmMachine(
* @return The list of [VerificationRequest] that we share with the given user * @return The list of [VerificationRequest] that we share with the given user
*/ */
fun getVerificationRequests(userId: String): List<VerificationRequest> { fun getVerificationRequests(userId: String): List<VerificationRequest> {
return inner.getVerificationRequests(userId).map(verificationRequestFactory::create) return verificationsProvider.getVerificationRequests(userId)
} }
/** Get a verification request for the given user with the given flow ID */ /** Get a verification request for the given user with the given flow ID */
fun getVerificationRequest(userId: String, flowId: String): VerificationRequest? { fun getVerificationRequest(userId: String, flowId: String): VerificationRequest? {
return inner.getVerificationRequest(userId, flowId)?.let { innerVerificationRequest -> return verificationsProvider.getVerificationRequest(userId, flowId)
verificationRequestFactory.create(innerVerificationRequest)
}
} }
/** Get an active verification for the given user and given flow ID. /** Get an active verification for the given user and given flow ID.
@ -708,48 +678,7 @@ internal class OlmMachine(
* verification. * verification.
*/ */
fun getVerification(userId: String, flowId: String): VerificationTransaction? { fun getVerification(userId: String, flowId: String): VerificationTransaction? {
return when (val verification = inner.getVerification(userId, flowId)) { return verificationsProvider.getVerification(userId, flowId)
is uniffi.olm.Verification.QrCodeV1 -> {
val request = getVerificationRequest(userId, flowId) ?: return null
QrCodeVerification(
machine = inner,
request = request,
inner = verification.qrcode,
sender = requestSender,
coroutineDispatchers = coroutineDispatchers,
listeners = verificationListeners
)
}
is uniffi.olm.Verification.SasV1 -> {
SasVerification(
machine = inner,
inner = verification.sas,
sender = requestSender,
coroutineDispatchers = coroutineDispatchers,
listeners = verificationListeners
)
}
null -> {
// This branch exists because scanning a QR code is tied to the QrCodeVerification,
// i.e. instead of branching into a scanned QR code verification from the verification request,
// like it's done for SAS verifications, the public API expects us to create an empty dummy
// QrCodeVerification object that gets populated once a QR code is scanned.
val request = getVerificationRequest(userId, flowId) ?: return null
if (request.canScanQrCodes()) {
QrCodeVerification(
machine = inner,
request = request,
inner = null,
sender = requestSender,
coroutineDispatchers = coroutineDispatchers,
listeners = verificationListeners
)
} else {
null
}
}
}
} }
suspend fun bootstrapCrossSigning(uiaInterceptor: UserInteractiveAuthInterceptor?) { suspend fun bootstrapCrossSigning(uiaInterceptor: UserInteractiveAuthInterceptor?) {
@ -868,13 +797,4 @@ internal class OlmMachine(
inner.verifyBackup(serializedAuthData) inner.verifyBackup(serializedAuthData)
} }
} }
private fun uniffi.olm.Device.wrap() = Device(
innerMachine = inner,
innerDevice = this,
requestSender = requestSender,
coroutineDispatchers = coroutineDispatchers,
listeners = verificationListeners,
verificationRequestFactory = verificationRequestFactory
)
} }

View File

@ -1,52 +0,0 @@
/*
* Copyright 2021 The Matrix.org Foundation C.I.C.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.matrix.android.sdk.internal.crypto
import com.squareup.moshi.Moshi
import org.matrix.android.sdk.api.MatrixCoroutineDispatchers
import org.matrix.android.sdk.internal.crypto.network.RequestSender
import org.matrix.android.sdk.internal.di.DeviceId
import org.matrix.android.sdk.internal.di.SessionFilesDirectory
import org.matrix.android.sdk.internal.di.UserId
import org.matrix.android.sdk.internal.session.SessionScope
import org.matrix.android.sdk.internal.util.time.Clock
import java.io.File
import javax.inject.Inject
@SessionScope
internal class OlmMachineProvider @Inject constructor(
@UserId userId: String,
@DeviceId deviceId: String?,
@SessionFilesDirectory dataDir: File,
requestSender: RequestSender,
coroutineDispatchers: MatrixCoroutineDispatchers,
moshi: Moshi,
clock: Clock
) {
val olmMachine: OlmMachine by lazy {
OlmMachine(
user_id = userId,
device_id = deviceId!!,
path = dataDir,
clock = clock,
requestSender = requestSender,
coroutineDispatchers = coroutineDispatchers,
moshi = moshi
)
}
}

View File

@ -39,17 +39,16 @@ import javax.inject.Inject
private val loggerTag = LoggerTag("PrepareToEncryptUseCase", LoggerTag.CRYPTO) private val loggerTag = LoggerTag("PrepareToEncryptUseCase", LoggerTag.CRYPTO)
@SessionScope @SessionScope
internal class PrepareToEncryptUseCase @Inject constructor(olmMachineProvider: OlmMachineProvider, internal class PrepareToEncryptUseCase @Inject constructor(
private val coroutineDispatchers: MatrixCoroutineDispatchers, private val olmMachine: OlmMachine,
private val cryptoStore: IMXCryptoStore, private val coroutineDispatchers: MatrixCoroutineDispatchers,
private val getRoomUserIds: GetRoomUserIdsUseCase, private val cryptoStore: IMXCryptoStore,
private val requestSender: RequestSender, private val getRoomUserIds: GetRoomUserIdsUseCase,
private val loadRoomMembersTask: LoadRoomMembersTask, private val requestSender: RequestSender,
private val keysBackupService: RustKeyBackupService private val loadRoomMembersTask: LoadRoomMembersTask,
private val keysBackupService: RustKeyBackupService
) { ) {
private val olmMachine = olmMachineProvider.olmMachine
private val keyClaimLock: Mutex = Mutex() private val keyClaimLock: Mutex = Mutex()
private val roomKeyShareLocks: ConcurrentHashMap<String, Mutex> = ConcurrentHashMap() private val roomKeyShareLocks: ConcurrentHashMap<String, Mutex> = ConcurrentHashMap()
@ -91,7 +90,7 @@ internal class PrepareToEncryptUseCase @Inject constructor(olmMachineProvider: O
is Request.ToDevice -> { is Request.ToDevice -> {
sharedKey = true sharedKey = true
async { async {
sendToDevice(it) sendToDevice(olmMachine, it)
} }
} }
else -> { else -> {
@ -113,7 +112,7 @@ internal class PrepareToEncryptUseCase @Inject constructor(olmMachineProvider: O
} }
private suspend fun claimMissingKeys(roomMembers: List<String>) = keyClaimLock.withLock { private suspend fun claimMissingKeys(roomMembers: List<String>) = keyClaimLock.withLock {
val request = this.olmMachine.getMissingSessions(roomMembers) val request = olmMachine.getMissingSessions(roomMembers)
// This request can only be a keys claim request. // This request can only be a keys claim request.
when (request) { when (request) {
is Request.KeysClaim -> { is Request.KeysClaim -> {
@ -124,7 +123,7 @@ internal class PrepareToEncryptUseCase @Inject constructor(olmMachineProvider: O
} }
} }
private suspend fun sendToDevice(request: Request.ToDevice) { private suspend fun sendToDevice(olmMachine: OlmMachine, request: Request.ToDevice) {
try { try {
requestSender.sendToDevice(request) requestSender.sendToDevice(request)
olmMachine.markRequestAsSent(request.requestId, RequestType.TO_DEVICE, "{}") olmMachine.markRequestAsSent(request.requestId, RequestType.TO_DEVICE, "{}")

View File

@ -18,7 +18,6 @@ package org.matrix.android.sdk.internal.crypto
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
import org.matrix.android.sdk.api.extensions.orFalse
import org.matrix.android.sdk.api.session.crypto.crosssigning.CrossSigningService import org.matrix.android.sdk.api.session.crypto.crosssigning.CrossSigningService
import org.matrix.android.sdk.api.session.crypto.crosssigning.DeviceTrustResult import org.matrix.android.sdk.api.session.crypto.crosssigning.DeviceTrustResult
import org.matrix.android.sdk.api.session.crypto.crosssigning.MXCrossSigningInfo import org.matrix.android.sdk.api.session.crypto.crosssigning.MXCrossSigningInfo
@ -27,17 +26,13 @@ import org.matrix.android.sdk.api.session.crypto.crosssigning.UserTrustResult
import org.matrix.android.sdk.api.session.crypto.crosssigning.isVerified import org.matrix.android.sdk.api.session.crypto.crosssigning.isVerified
import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel
import org.matrix.android.sdk.api.util.Optional import org.matrix.android.sdk.api.util.Optional
import org.matrix.android.sdk.internal.di.UserId
import javax.inject.Inject import javax.inject.Inject
internal class RustCrossSigningService @Inject constructor( internal class RustCrossSigningService @Inject constructor(
// @SessionId private val sessionId: String, private val olmMachine: OlmMachine,
@UserId private val myUserId: String, private val computeShieldForGroup: ComputeShieldForGroupUseCase
private val olmMachineProvider: OlmMachineProvider
) : CrossSigningService { ) : CrossSigningService {
val olmMachine = olmMachineProvider.olmMachine
/** /**
* Is our own device signed by our own cross signing identity * Is our own device signed by our own cross signing identity
*/ */
@ -211,44 +206,6 @@ internal class RustCrossSigningService @Inject constructor(
} }
override suspend fun shieldForGroup(userIds: List<String>): RoomEncryptionTrustLevel { override suspend fun shieldForGroup(userIds: List<String>): RoomEncryptionTrustLevel {
val myIdentity = olmMachine.getIdentity(myUserId) return computeShieldForGroup(olmMachine, userIds)
val allTrustedUserIds = userIds
.filter { userId ->
olmMachine.getIdentity(userId)?.verified() == true
}
return if (allTrustedUserIds.isEmpty()) {
RoomEncryptionTrustLevel.Default
} else {
// If one of the verified user as an untrusted device -> warning
// If all devices of all verified users are trusted -> green
// else -> black
allTrustedUserIds
.map { userId ->
olmMachineProvider.olmMachine.getUserDevices(userId)
}
.flatten()
.let { allDevices ->
if (myIdentity != null) {
allDevices.any { !it.toCryptoDeviceInfo().trustLevel?.crossSigningVerified.orFalse() }
} else {
// TODO check that if myIdentity is null ean
// Legacy method
allDevices.any { !it.toCryptoDeviceInfo().isVerified }
}
}
.let { hasWarning ->
if (hasWarning) {
RoomEncryptionTrustLevel.Warning
} else {
if (userIds.size == allTrustedUserIds.size) {
// all users are trusted and all devices are verified
RoomEncryptionTrustLevel.Trusted
} else {
RoomEncryptionTrustLevel.Default
}
}
}
}
} }
} }

View File

@ -25,7 +25,6 @@ import org.matrix.android.sdk.api.session.crypto.verification.VerificationMethod
import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.EventType
import org.matrix.android.sdk.internal.crypto.network.RequestSender import org.matrix.android.sdk.internal.crypto.network.RequestSender
import org.matrix.android.sdk.internal.crypto.verification.VerificationRequest import org.matrix.android.sdk.internal.crypto.verification.VerificationRequest
import org.matrix.android.sdk.internal.crypto.verification.VerificationRequestFactory
import org.matrix.android.sdk.internal.crypto.verification.prepareMethods import org.matrix.android.sdk.internal.crypto.verification.prepareMethods
import uniffi.olm.CryptoStoreException import uniffi.olm.CryptoStoreException
import uniffi.olm.OlmMachine import uniffi.olm.OlmMachine
@ -85,7 +84,7 @@ internal class OwnUserIdentity(
private val innerMachine: OlmMachine, private val innerMachine: OlmMachine,
private val requestSender: RequestSender, private val requestSender: RequestSender,
private val coroutineDispatchers: MatrixCoroutineDispatchers, private val coroutineDispatchers: MatrixCoroutineDispatchers,
private val verificationRequestFactory: VerificationRequestFactory, private val verificationRequestFactory: VerificationRequest.Factory,
) : UserIdentities() { ) : UserIdentities() {
/** /**
* Our own user id. * Our own user id.
@ -174,7 +173,7 @@ internal class UserIdentity(
private val innerMachine: OlmMachine, private val innerMachine: OlmMachine,
private val requestSender: RequestSender, private val requestSender: RequestSender,
private val coroutineDispatchers: MatrixCoroutineDispatchers, private val coroutineDispatchers: MatrixCoroutineDispatchers,
private val verificationRequestFactory: VerificationRequestFactory, private val verificationRequestFactory: VerificationRequest.Factory,
) : UserIdentities() { ) : UserIdentities() {
/** /**
* The unique ID of the user that this identity belongs to. * The unique ID of the user that this identity belongs to.

View File

@ -49,7 +49,7 @@ import org.matrix.android.sdk.api.session.crypto.keysbackup.toKeysVersionResult
import org.matrix.android.sdk.api.session.crypto.model.ImportRoomKeysResult import org.matrix.android.sdk.api.session.crypto.model.ImportRoomKeysResult
import org.matrix.android.sdk.internal.crypto.MegolmSessionData import org.matrix.android.sdk.internal.crypto.MegolmSessionData
import org.matrix.android.sdk.internal.crypto.MegolmSessionImportManager import org.matrix.android.sdk.internal.crypto.MegolmSessionImportManager
import org.matrix.android.sdk.internal.crypto.OlmMachineProvider import org.matrix.android.sdk.internal.crypto.OlmMachine
import org.matrix.android.sdk.internal.crypto.keysbackup.model.SignalableMegolmBackupAuthData import org.matrix.android.sdk.internal.crypto.keysbackup.model.SignalableMegolmBackupAuthData
import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.CreateKeysBackupVersionBody import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.CreateKeysBackupVersionBody
import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeyBackupData import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeyBackupData
@ -73,7 +73,7 @@ import kotlin.random.Random
*/ */
@SessionScope @SessionScope
internal class RustKeyBackupService @Inject constructor( internal class RustKeyBackupService @Inject constructor(
olmMachineProvider: OlmMachineProvider, private val olmMachine: OlmMachine,
private val sender: RequestSender, private val sender: RequestSender,
private val coroutineDispatchers: MatrixCoroutineDispatchers, private val coroutineDispatchers: MatrixCoroutineDispatchers,
private val megolmSessionImportManager: MegolmSessionImportManager, private val megolmSessionImportManager: MegolmSessionImportManager,
@ -88,8 +88,6 @@ internal class RustKeyBackupService @Inject constructor(
private val keysBackupStateManager = KeysBackupStateManager(uiHandler) private val keysBackupStateManager = KeysBackupStateManager(uiHandler)
private val olmMachine = olmMachineProvider.olmMachine
// The backup version // The backup version
override var keysBackupVersion: KeysVersionResult? = null override var keysBackupVersion: KeysVersionResult? = null
private set private set

View File

@ -18,38 +18,43 @@ package org.matrix.android.sdk.internal.crypto.network
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.async import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.joinAll
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.sync.withLock
import org.matrix.android.sdk.api.logger.LoggerTag import org.matrix.android.sdk.api.logger.LoggerTag
import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel
import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.internal.crypto.ComputeShieldForGroupUseCase
import org.matrix.android.sdk.internal.crypto.CryptoSessionInfoProvider import org.matrix.android.sdk.internal.crypto.CryptoSessionInfoProvider
import org.matrix.android.sdk.internal.crypto.OlmMachine import org.matrix.android.sdk.internal.crypto.OlmMachine
import org.matrix.android.sdk.internal.session.SessionScope
import timber.log.Timber import timber.log.Timber
import uniffi.olm.Request import uniffi.olm.Request
import uniffi.olm.RequestType import uniffi.olm.RequestType
import javax.inject.Inject
private val loggerTag = LoggerTag("OutgoingRequestsProcessor", LoggerTag.CRYPTO) private val loggerTag = LoggerTag("OutgoingRequestsProcessor", LoggerTag.CRYPTO)
internal class OutgoingRequestsProcessor(private val requestSender: RequestSender, @SessionScope
private val coroutineScope: CoroutineScope, internal class OutgoingRequestsProcessor @Inject constructor(
private val cryptoSessionInfoProvider: CryptoSessionInfoProvider, private val requestSender: RequestSender,
private val shieldComputer: ShieldComputer,) { private val coroutineScope: CoroutineScope,
private val cryptoSessionInfoProvider: CryptoSessionInfoProvider,
fun interface ShieldComputer { private val computeShieldForGroup: ComputeShieldForGroupUseCase
suspend fun compute(userIds: List<String>): RoomEncryptionTrustLevel ) {
}
private val lock: Mutex = Mutex() private val lock: Mutex = Mutex()
suspend fun processOutgoingRequests(olmMachine: OlmMachine) { suspend fun processOutgoingRequests(olmMachine: OlmMachine,
lock.withLock { filter: (Request) -> Boolean = { true }
): Boolean {
return lock.withLock {
coroutineScope { coroutineScope {
Timber.v("OutgoingRequests: ${olmMachine.outgoingRequests()}") val outgoingRequests = olmMachine.outgoingRequests()
olmMachine.outgoingRequests().map { val filteredOutgoingRequests = outgoingRequests.filter(filter)
Timber.v("OutgoingRequests to process: $filteredOutgoingRequests}")
filteredOutgoingRequests.map {
when (it) { when (it) {
is Request.KeysUpload -> { is Request.KeysUpload -> {
async { async {
@ -85,10 +90,11 @@ internal class OutgoingRequestsProcessor(private val requestSender: RequestSende
async { async {
// The rust-sdk won't ever produce KeysBackup requests here, // The rust-sdk won't ever produce KeysBackup requests here,
// those only get explicitly created. // those only get explicitly created.
true
} }
} }
} }
}.joinAll() }.awaitAll().all { it }
} }
} }
} }
@ -112,66 +118,78 @@ internal class OutgoingRequestsProcessor(private val requestSender: RequestSende
} }
} }
private suspend fun uploadKeys(olmMachine: OlmMachine, request: Request.KeysUpload) { private suspend fun uploadKeys(olmMachine: OlmMachine, request: Request.KeysUpload): Boolean {
try { return try {
val response = requestSender.uploadKeys(request) val response = requestSender.uploadKeys(request)
olmMachine.markRequestAsSent(request.requestId, RequestType.KEYS_UPLOAD, response) olmMachine.markRequestAsSent(request.requestId, RequestType.KEYS_UPLOAD, response)
true
} catch (throwable: Throwable) { } catch (throwable: Throwable) {
Timber.tag(loggerTag.value).e(throwable, "## uploadKeys(): error") Timber.tag(loggerTag.value).e(throwable, "## uploadKeys(): error")
false
} }
} }
private suspend fun queryKeys(olmMachine: OlmMachine, request: Request.KeysQuery) { private suspend fun queryKeys(olmMachine: OlmMachine, request: Request.KeysQuery): Boolean {
try { return try {
val response = requestSender.queryKeys(request) val response = requestSender.queryKeys(request)
olmMachine.markRequestAsSent(request.requestId, RequestType.KEYS_QUERY, response) olmMachine.markRequestAsSent(request.requestId, RequestType.KEYS_QUERY, response)
coroutineScope.updateShields(request.users) coroutineScope.updateShields(olmMachine, request.users)
true
} catch (throwable: Throwable) { } catch (throwable: Throwable) {
Timber.tag(loggerTag.value).e(throwable, "## queryKeys(): error") Timber.tag(loggerTag.value).e(throwable, "## queryKeys(): error")
false
} }
} }
private fun CoroutineScope.updateShields(userIds: List<String>) = launch { private fun CoroutineScope.updateShields(olmMachine: OlmMachine, userIds: List<String>) = launch {
cryptoSessionInfoProvider.getRoomsWhereUsersAreParticipating(userIds).forEach { roomId -> cryptoSessionInfoProvider.getRoomsWhereUsersAreParticipating(userIds).forEach { roomId ->
val userGroup = cryptoSessionInfoProvider.getUserListForShieldComputation(roomId) val userGroup = cryptoSessionInfoProvider.getUserListForShieldComputation(roomId)
val shield = shieldComputer.compute(userGroup) val shield = computeShieldForGroup(olmMachine, userGroup)
cryptoSessionInfoProvider.updateShieldForRoom(roomId, shield) cryptoSessionInfoProvider.updateShieldForRoom(roomId, shield)
} }
} }
private suspend fun sendToDevice(olmMachine: OlmMachine, request: Request.ToDevice) { private suspend fun sendToDevice(olmMachine: OlmMachine, request: Request.ToDevice): Boolean {
try { return try {
requestSender.sendToDevice(request) requestSender.sendToDevice(request)
olmMachine.markRequestAsSent(request.requestId, RequestType.TO_DEVICE, "{}") olmMachine.markRequestAsSent(request.requestId, RequestType.TO_DEVICE, "{}")
true
} catch (throwable: Throwable) { } catch (throwable: Throwable) {
Timber.tag(loggerTag.value).e(throwable, "## sendToDevice(): error") Timber.tag(loggerTag.value).e(throwable, "## sendToDevice(): error")
false
} }
} }
private suspend fun claimKeys(olmMachine: OlmMachine, request: Request.KeysClaim) { private suspend fun claimKeys(olmMachine: OlmMachine, request: Request.KeysClaim): Boolean {
try { return try {
val response = requestSender.claimKeys(request) val response = requestSender.claimKeys(request)
olmMachine.markRequestAsSent(request.requestId, RequestType.KEYS_CLAIM, response) olmMachine.markRequestAsSent(request.requestId, RequestType.KEYS_CLAIM, response)
true
} catch (throwable: Throwable) { } catch (throwable: Throwable) {
Timber.tag(loggerTag.value).e(throwable, "## claimKeys(): error") Timber.tag(loggerTag.value).e(throwable, "## claimKeys(): error")
false
} }
} }
private suspend fun signatureUpload(olmMachine: OlmMachine, request: Request.SignatureUpload) { private suspend fun signatureUpload(olmMachine: OlmMachine, request: Request.SignatureUpload): Boolean {
try { return try {
val response = requestSender.sendSignatureUpload(request) val response = requestSender.sendSignatureUpload(request)
olmMachine.markRequestAsSent(request.requestId, RequestType.SIGNATURE_UPLOAD, response) olmMachine.markRequestAsSent(request.requestId, RequestType.SIGNATURE_UPLOAD, response)
true
} catch (throwable: Throwable) { } catch (throwable: Throwable) {
Timber.tag(loggerTag.value).e(throwable, "## signatureUpload(): error") Timber.tag(loggerTag.value).e(throwable, "## signatureUpload(): error")
false
} }
} }
private suspend fun sendRoomMessage(olmMachine: OlmMachine, request: Request.RoomMessage) { private suspend fun sendRoomMessage(olmMachine: OlmMachine, request: Request.RoomMessage): Boolean {
try { return try {
val response = requestSender.sendRoomMessage(request) val response = requestSender.sendRoomMessage(request)
olmMachine.markRequestAsSent(request.requestId, RequestType.ROOM_MESSAGE, response) olmMachine.markRequestAsSent(request.requestId, RequestType.ROOM_MESSAGE, response)
true
} catch (throwable: Throwable) { } catch (throwable: Throwable) {
Timber.tag(loggerTag.value).e(throwable, "## sendRoomMessage(): error") Timber.tag(loggerTag.value).e(throwable, "## sendRoomMessage(): error")
false
} }
} }
} }

View File

@ -112,7 +112,7 @@ internal class RealmCryptoStore @Inject constructor(
@CryptoDatabase private val realmConfiguration: RealmConfiguration, @CryptoDatabase private val realmConfiguration: RealmConfiguration,
private val crossSigningKeysMapper: CrossSigningKeysMapper, private val crossSigningKeysMapper: CrossSigningKeysMapper,
@UserId private val userId: String, @UserId private val userId: String,
@DeviceId private val deviceId: String?, @DeviceId private val deviceId: String,
private val clock: Clock, private val clock: Clock,
) : IMXCryptoStore { ) : IMXCryptoStore {
@ -155,7 +155,7 @@ internal class RealmCryptoStore @Inject constructor(
// The device id may not have been provided in credentials. // The device id may not have been provided in credentials.
// Check it only if provided, else trust the stored one. // Check it only if provided, else trust the stored one.
if (currentMetadata.userId != userId || if (currentMetadata.userId != userId ||
(deviceId != null && deviceId != currentMetadata.deviceId)) { (deviceId != currentMetadata.deviceId)) {
Timber.w("## open() : Credentials do not match, close this store and delete data") Timber.w("## open() : Credentials do not match, close this store and delete data")
deleteAll = true deleteAll = true
currentMetadata = null currentMetadata = null

View File

@ -28,7 +28,7 @@ import org.matrix.android.sdk.api.session.events.model.toModel
import org.matrix.android.sdk.api.session.room.model.message.MessageContent import org.matrix.android.sdk.api.session.room.model.message.MessageContent
import org.matrix.android.sdk.api.session.room.model.message.MessageRelationContent import org.matrix.android.sdk.api.session.room.model.message.MessageRelationContent
import org.matrix.android.sdk.api.session.room.model.message.MessageType import org.matrix.android.sdk.api.session.room.model.message.MessageType
import org.matrix.android.sdk.internal.crypto.OlmMachineProvider import org.matrix.android.sdk.internal.crypto.OlmMachine
import org.matrix.android.sdk.internal.crypto.OwnUserIdentity import org.matrix.android.sdk.internal.crypto.OwnUserIdentity
import org.matrix.android.sdk.internal.crypto.UserIdentity import org.matrix.android.sdk.internal.crypto.UserIdentity
import org.matrix.android.sdk.internal.crypto.model.rest.VERIFICATION_METHOD_QR_CODE_SCAN import org.matrix.android.sdk.internal.crypto.model.rest.VERIFICATION_METHOD_QR_CODE_SCAN
@ -70,13 +70,9 @@ internal fun prepareMethods(methods: List<VerificationMethod>): List<String> {
} }
@SessionScope @SessionScope
internal class RustVerificationService @Inject constructor(private val olmMachineProvider: OlmMachineProvider) : VerificationService { internal class RustVerificationService @Inject constructor(
private val olmMachine: OlmMachine,
val olmMachine by lazy { private val verificationListenersHolder: VerificationListenersHolder) : VerificationService {
olmMachineProvider.olmMachine
}
private val dispatcher = UpdateDispatcher(olmMachine.verificationListeners)
/** /**
* *
@ -121,7 +117,7 @@ internal class RustVerificationService @Inject constructor(private val olmMachin
olmMachine.getVerificationRequest(sender, flowId)?.dispatchRequestUpdated() olmMachine.getVerificationRequest(sender, flowId)?.dispatchRequestUpdated()
val verification = getExistingTransaction(sender, flowId) ?: return val verification = getExistingTransaction(sender, flowId) ?: return
dispatcher.dispatchTxUpdated(verification) verificationListenersHolder.dispatchTxUpdated(verification)
} }
/** Check if the start event created new verification objects and dispatch updates */ /** Check if the start event created new verification objects and dispatch updates */
@ -141,15 +137,15 @@ internal class RustVerificationService @Inject constructor(private val olmMachin
Timber.d("## Verification: Auto accepting SAS verification with $sender") Timber.d("## Verification: Auto accepting SAS verification with $sender")
verification.accept() verification.accept()
} else { } else {
dispatcher.dispatchTxUpdated(verification) verificationListenersHolder.dispatchTxUpdated(verification)
} }
} else { } else {
// This didn't originate from a request, so tell our listeners that // This didn't originate from a request, so tell our listeners that
// this is a new verification. // this is a new verification.
dispatcher.dispatchTxAdded(verification) verificationListenersHolder.dispatchTxAdded(verification)
// The IncomingVerificationRequestHandler seems to only listen to updates // The IncomingVerificationRequestHandler seems to only listen to updates
// so let's trigger an update after the addition as well. // so let's trigger an update after the addition as well.
dispatcher.dispatchTxUpdated(verification) verificationListenersHolder.dispatchTxUpdated(verification)
} }
} }
@ -163,15 +159,15 @@ internal class RustVerificationService @Inject constructor(private val olmMachin
val sender = event.senderId ?: return val sender = event.senderId ?: return
val request = getExistingVerificationRequest(sender, flowId) ?: return val request = getExistingVerificationRequest(sender, flowId) ?: return
dispatcher.dispatchRequestAdded(request) verificationListenersHolder.dispatchRequestAdded(request)
} }
override fun addListener(listener: VerificationService.Listener) { override fun addListener(listener: VerificationService.Listener) {
dispatcher.addListener(listener) verificationListenersHolder.addListener(listener)
} }
override fun removeListener(listener: VerificationService.Listener) { override fun removeListener(listener: VerificationService.Listener) {
dispatcher.removeListener(listener) verificationListenersHolder.removeListener(listener)
} }
override suspend fun markedLocallyAsManuallyVerified(userId: String, deviceID: String) { override suspend fun markedLocallyAsManuallyVerified(userId: String, deviceID: String) {
@ -275,7 +271,7 @@ internal class RustVerificationService @Inject constructor(private val olmMachin
val qrcode = request.startQrVerification() val qrcode = request.startQrVerification()
if (qrcode != null) { if (qrcode != null) {
dispatcher.dispatchTxAdded(qrcode) verificationListenersHolder.dispatchTxAdded(qrcode)
} }
true true
@ -298,7 +294,7 @@ internal class RustVerificationService @Inject constructor(private val olmMachin
val sas = request?.startSasVerification() val sas = request?.startSasVerification()
if (sas != null) { if (sas != null) {
dispatcher.dispatchTxAdded(sas) verificationListenersHolder.dispatchTxAdded(sas)
sas.transactionId sas.transactionId
} else { } else {
null null
@ -317,7 +313,7 @@ internal class RustVerificationService @Inject constructor(private val olmMachin
val otherDevice = olmMachine.getDevice(otherUserId, otherDeviceId) val otherDevice = olmMachine.getDevice(otherUserId, otherDeviceId)
val verification = otherDevice?.startVerification() val verification = otherDevice?.startVerification()
return if (verification != null) { return if (verification != null) {
dispatcher.dispatchTxAdded(verification) verificationListenersHolder.dispatchTxAdded(verification)
verification.transactionId verification.transactionId
} else { } else {
null null

View File

@ -16,34 +16,42 @@
package org.matrix.android.sdk.internal.crypto.verification package org.matrix.android.sdk.internal.crypto.verification
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.matrix.android.sdk.api.MatrixCoroutineDispatchers import org.matrix.android.sdk.api.MatrixCoroutineDispatchers
import org.matrix.android.sdk.api.session.crypto.verification.CancelCode import org.matrix.android.sdk.api.session.crypto.verification.CancelCode
import org.matrix.android.sdk.api.session.crypto.verification.EmojiRepresentation import org.matrix.android.sdk.api.session.crypto.verification.EmojiRepresentation
import org.matrix.android.sdk.api.session.crypto.verification.SasVerificationTransaction import org.matrix.android.sdk.api.session.crypto.verification.SasVerificationTransaction
import org.matrix.android.sdk.api.session.crypto.verification.VerificationService
import org.matrix.android.sdk.api.session.crypto.verification.VerificationTxState import org.matrix.android.sdk.api.session.crypto.verification.VerificationTxState
import org.matrix.android.sdk.api.session.crypto.verification.safeValueOf import org.matrix.android.sdk.api.session.crypto.verification.safeValueOf
import org.matrix.android.sdk.internal.crypto.OlmMachine
import org.matrix.android.sdk.internal.crypto.network.RequestSender import org.matrix.android.sdk.internal.crypto.network.RequestSender
import uniffi.olm.CryptoStoreException import uniffi.olm.CryptoStoreException
import uniffi.olm.OlmMachine
import uniffi.olm.Sas import uniffi.olm.Sas
import uniffi.olm.Verification import uniffi.olm.Verification
/** Class representing a short auth string verification flow */ /** Class representing a short auth string verification flow */
internal class SasVerification( internal class SasVerification @AssistedInject constructor(
private val machine: OlmMachine, @Assisted private var inner: Sas,
private var inner: Sas, private val olmMachine: OlmMachine,
private val sender: RequestSender, private val sender: RequestSender,
private val coroutineDispatchers: MatrixCoroutineDispatchers, private val coroutineDispatchers: MatrixCoroutineDispatchers,
listeners: ArrayList<VerificationService.Listener> private val verificationListenersHolder: VerificationListenersHolder
) : ) :
SasVerificationTransaction { SasVerificationTransaction {
private val dispatcher = UpdateDispatcher(listeners)
@AssistedFactory
interface Factory {
fun create(inner: Sas): SasVerification
}
private val innerMachine = olmMachine.inner()
private fun dispatchTxUpdated() { private fun dispatchTxUpdated() {
refreshData() refreshData()
dispatcher.dispatchTxUpdated(this) verificationListenersHolder.dispatchTxUpdated(this)
} }
/** The user ID of the other user that is participating in this verification flow */ /** The user ID of the other user that is participating in this verification flow */
@ -167,7 +175,7 @@ internal class SasVerification(
* in a presentable state. * in a presentable state.
*/ */
override fun getDecimalCodeRepresentation(): String { override fun getDecimalCodeRepresentation(): String {
val decimals = machine.getDecimals(inner.otherUserId, inner.flowId) val decimals = innerMachine.getDecimals(inner.otherUserId, inner.flowId)
return decimals?.joinToString(" ") ?: "" return decimals?.joinToString(" ") ?: ""
} }
@ -179,13 +187,13 @@ internal class SasVerification(
* state. * state.
*/ */
override fun getEmojiCodeRepresentation(): List<EmojiRepresentation> { override fun getEmojiCodeRepresentation(): List<EmojiRepresentation> {
val emojiIndex = machine.getEmojiIndex(inner.otherUserId, inner.flowId) val emojiIndex = innerMachine.getEmojiIndex(inner.otherUserId, inner.flowId)
return emojiIndex?.map { getEmojiForCode(it) } ?: listOf() return emojiIndex?.map { getEmojiForCode(it) } ?: listOf()
} }
internal suspend fun accept() { internal suspend fun accept() {
val request = machine.acceptSasVerification(inner.otherUserId, inner.flowId) val request = innerMachine.acceptSasVerification(inner.otherUserId, inner.flowId)
if (request != null) { if (request != null) {
sender.sendVerificationRequest(request) sender.sendVerificationRequest(request)
@ -196,7 +204,7 @@ internal class SasVerification(
@Throws(CryptoStoreException::class) @Throws(CryptoStoreException::class)
private suspend fun confirm() { private suspend fun confirm() {
val result = withContext(coroutineDispatchers.io) { val result = withContext(coroutineDispatchers.io) {
machine.confirmVerification(inner.otherUserId, inner.flowId) innerMachine.confirmVerification(inner.otherUserId, inner.flowId)
} }
if (result != null) { if (result != null) {
for (verificationRequest in result.requests) { for (verificationRequest in result.requests) {
@ -211,7 +219,7 @@ internal class SasVerification(
} }
private suspend fun cancelHelper(code: CancelCode) { private suspend fun cancelHelper(code: CancelCode) {
val request = machine.cancelVerification(inner.otherUserId, inner.flowId, code.value) val request = innerMachine.cancelVerification(inner.otherUserId, inner.flowId, code.value)
if (request != null) { if (request != null) {
sender.sendVerificationRequest(request) sender.sendVerificationRequest(request)
@ -221,7 +229,7 @@ internal class SasVerification(
/** Fetch fresh data from the Rust side for our verification flow */ /** Fetch fresh data from the Rust side for our verification flow */
private fun refreshData() { private fun refreshData() {
when (val verification = machine.getVerification(inner.otherUserId, inner.flowId)) { when (val verification = innerMachine.getVerification(inner.otherUserId, inner.flowId)) {
is Verification.SasV1 -> { is Verification.SasV1 -> {
inner = verification.sas inner = verification.sas
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2021 The Matrix.org Foundation C.I.C. * Copyright (c) 2022 New Vector Ltd
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -21,13 +21,20 @@ import android.os.Looper
import org.matrix.android.sdk.api.session.crypto.verification.PendingVerificationRequest import org.matrix.android.sdk.api.session.crypto.verification.PendingVerificationRequest
import org.matrix.android.sdk.api.session.crypto.verification.VerificationService import org.matrix.android.sdk.api.session.crypto.verification.VerificationService
import org.matrix.android.sdk.api.session.crypto.verification.VerificationTransaction import org.matrix.android.sdk.api.session.crypto.verification.VerificationTransaction
import org.matrix.android.sdk.internal.session.SessionScope
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject
@SessionScope
internal class VerificationListenersHolder @Inject constructor() {
private val listeners = ArrayList<VerificationService.Listener>()
/** Class that implements some common methods to dispatch updates for the verification related classes */
internal class UpdateDispatcher(private val listeners: ArrayList<VerificationService.Listener>) {
private val uiHandler = Handler(Looper.getMainLooper()) private val uiHandler = Handler(Looper.getMainLooper())
internal fun addListener(listener: VerificationService.Listener) { fun listeners(): List<VerificationService.Listener> = listeners
fun addListener(listener: VerificationService.Listener) {
uiHandler.post { uiHandler.post {
if (!this.listeners.contains(listener)) { if (!this.listeners.contains(listener)) {
this.listeners.add(listener) this.listeners.add(listener)
@ -35,11 +42,11 @@ internal class UpdateDispatcher(private val listeners: ArrayList<VerificationSer
} }
} }
internal fun removeListener(listener: VerificationService.Listener) { fun removeListener(listener: VerificationService.Listener) {
uiHandler.post { this.listeners.remove(listener) } uiHandler.post { this.listeners.remove(listener) }
} }
internal fun dispatchTxAdded(tx: VerificationTransaction) { fun dispatchTxAdded(tx: VerificationTransaction) {
uiHandler.post { uiHandler.post {
this.listeners.forEach { this.listeners.forEach {
try { try {
@ -51,7 +58,7 @@ internal class UpdateDispatcher(private val listeners: ArrayList<VerificationSer
} }
} }
internal fun dispatchTxUpdated(tx: VerificationTransaction) { fun dispatchTxUpdated(tx: VerificationTransaction) {
uiHandler.post { uiHandler.post {
this.listeners.forEach { this.listeners.forEach {
try { try {
@ -63,12 +70,24 @@ internal class UpdateDispatcher(private val listeners: ArrayList<VerificationSer
} }
} }
internal fun dispatchRequestAdded(tx: PendingVerificationRequest) { fun dispatchRequestAdded(verificationRequest: PendingVerificationRequest) {
Timber.v("## SAS dispatchRequestAdded txId:${tx.transactionId} $tx") Timber.v("## SAS dispatchRequestAdded txId:${verificationRequest.transactionId} $verificationRequest")
uiHandler.post { uiHandler.post {
this.listeners.forEach { this.listeners.forEach {
try { try {
it.verificationRequestCreated(tx) it.verificationRequestCreated(verificationRequest)
} catch (e: Throwable) {
Timber.e(e, "## Error while notifying listeners")
}
}
}
}
fun dispatchRequestUpdated(verificationRequest: PendingVerificationRequest) {
uiHandler.post {
listeners.forEach {
try {
it.verificationRequestUpdated(verificationRequest)
} catch (e: Throwable) { } catch (e: Throwable) {
Timber.e(e, "## Error while notifying listeners") Timber.e(e, "## Error while notifying listeners")
} }

View File

@ -16,8 +16,9 @@
package org.matrix.android.sdk.internal.crypto.verification package org.matrix.android.sdk.internal.crypto.verification
import android.os.Handler import dagger.assisted.Assisted
import android.os.Looper import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.matrix.android.sdk.api.MatrixCoroutineDispatchers import org.matrix.android.sdk.api.MatrixCoroutineDispatchers
import org.matrix.android.sdk.api.session.crypto.verification.CancelCode import org.matrix.android.sdk.api.session.crypto.verification.CancelCode
@ -25,16 +26,14 @@ import org.matrix.android.sdk.api.session.crypto.verification.PendingVerificatio
import org.matrix.android.sdk.api.session.crypto.verification.ValidVerificationInfoReady import org.matrix.android.sdk.api.session.crypto.verification.ValidVerificationInfoReady
import org.matrix.android.sdk.api.session.crypto.verification.ValidVerificationInfoRequest import org.matrix.android.sdk.api.session.crypto.verification.ValidVerificationInfoRequest
import org.matrix.android.sdk.api.session.crypto.verification.VerificationMethod import org.matrix.android.sdk.api.session.crypto.verification.VerificationMethod
import org.matrix.android.sdk.api.session.crypto.verification.VerificationService
import org.matrix.android.sdk.api.session.crypto.verification.safeValueOf import org.matrix.android.sdk.api.session.crypto.verification.safeValueOf
import org.matrix.android.sdk.api.util.toBase64NoPadding import org.matrix.android.sdk.api.util.toBase64NoPadding
import org.matrix.android.sdk.internal.crypto.OlmMachine
import org.matrix.android.sdk.internal.crypto.model.rest.VERIFICATION_METHOD_QR_CODE_SCAN import org.matrix.android.sdk.internal.crypto.model.rest.VERIFICATION_METHOD_QR_CODE_SCAN
import org.matrix.android.sdk.internal.crypto.network.RequestSender import org.matrix.android.sdk.internal.crypto.network.RequestSender
import org.matrix.android.sdk.internal.crypto.verification.qrcode.QrCodeVerification import org.matrix.android.sdk.internal.crypto.verification.qrcode.QrCodeVerification
import org.matrix.android.sdk.internal.util.time.Clock import org.matrix.android.sdk.internal.util.time.Clock
import timber.log.Timber import uniffi.olm.VerificationRequest as InnerVerificationRequest
import uniffi.olm.OlmMachine
import uniffi.olm.VerificationRequest
/** A verification request object /** A verification request object
* *
@ -43,26 +42,27 @@ import uniffi.olm.VerificationRequest
* Once the VerificationRequest gets to a ready state users can transition into the different * Once the VerificationRequest gets to a ready state users can transition into the different
* concrete verification flows. * concrete verification flows.
*/ */
internal class VerificationRequest( internal class VerificationRequest @AssistedInject constructor(
private val innerOlmMachine: OlmMachine, @Assisted private var innerVerificationRequest: InnerVerificationRequest,
private var innerVerificationRequest: VerificationRequest, olmMachine: OlmMachine,
private val requestSender: RequestSender, private val requestSender: RequestSender,
private val coroutineDispatchers: MatrixCoroutineDispatchers, private val coroutineDispatchers: MatrixCoroutineDispatchers,
private val listeners: ArrayList<VerificationService.Listener>, private val verificationListenersHolder: VerificationListenersHolder,
private val sasVerificationFactory: SasVerification.Factory,
private val qrCodeVerificationFactory: QrCodeVerification.Factory,
private val clock: Clock, private val clock: Clock,
) { ) {
private val uiHandler = Handler(Looper.getMainLooper())
private val innerOlmMachine = olmMachine.inner()
@AssistedFactory
interface Factory {
fun create(innerVerificationRequest: InnerVerificationRequest): VerificationRequest
}
internal fun dispatchRequestUpdated() { internal fun dispatchRequestUpdated() {
uiHandler.post { val tx = toPendingVerificationRequest()
listeners.forEach { verificationListenersHolder.dispatchRequestUpdated(tx)
try {
it.verificationRequestUpdated(toPendingVerificationRequest())
} catch (e: Throwable) {
Timber.e(e, "## Error while notifying listeners")
}
}
}
} }
/** Get the flow ID of this verification request /** Get the flow ID of this verification request
@ -167,7 +167,7 @@ internal class VerificationRequest(
if (result != null) { if (result != null) {
requestSender.sendVerificationRequest(result.request) requestSender.sendVerificationRequest(result.request)
SasVerification(innerOlmMachine, result.sas, requestSender, coroutineDispatchers, listeners) sasVerificationFactory.create(result.sas)
} else { } else {
null null
} }
@ -194,7 +194,7 @@ internal class VerificationRequest(
val result = innerOlmMachine.scanQrCode(otherUser(), flowId(), encodedData) ?: return null val result = innerOlmMachine.scanQrCode(otherUser(), flowId(), encodedData) ?: return null
requestSender.sendVerificationRequest(result.request) requestSender.sendVerificationRequest(result.request)
return QrCodeVerification(innerOlmMachine, this, result.qr, requestSender, coroutineDispatchers, listeners) return qrCodeVerificationFactory.create(this, result.qr)
} }
/** Transition into a QR code verification to display a QR code /** Transition into a QR code verification to display a QR code
@ -216,16 +216,8 @@ internal class VerificationRequest(
*/ */
internal fun startQrVerification(): QrCodeVerification? { internal fun startQrVerification(): QrCodeVerification? {
val qrcode = innerOlmMachine.startQrVerification(innerVerificationRequest.otherUserId, innerVerificationRequest.flowId) val qrcode = innerOlmMachine.startQrVerification(innerVerificationRequest.otherUserId, innerVerificationRequest.flowId)
return if (qrcode != null) { return if (qrcode != null) {
QrCodeVerification( qrCodeVerificationFactory.create(this, qrcode)
machine = innerOlmMachine,
request = this,
inner = qrcode,
sender = requestSender,
coroutineDispatchers = coroutineDispatchers,
listeners = listeners,
)
} else { } else {
null null
} }

View File

@ -1,43 +0,0 @@
/*
* Copyright (c) 2022 The Matrix.org Foundation C.I.C.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.matrix.android.sdk.internal.crypto.verification
import org.matrix.android.sdk.api.MatrixCoroutineDispatchers
import org.matrix.android.sdk.api.session.crypto.verification.VerificationService
import org.matrix.android.sdk.internal.crypto.network.RequestSender
import org.matrix.android.sdk.internal.util.time.Clock
import uniffi.olm.OlmMachine
internal class VerificationRequestFactory(
private val innerOlmMachine: OlmMachine,
private val requestSender: RequestSender,
private val coroutineDispatchers: MatrixCoroutineDispatchers,
private val listeners: ArrayList<VerificationService.Listener>,
private val clock: Clock,
) {
fun create(inner: uniffi.olm.VerificationRequest): VerificationRequest {
return VerificationRequest(
innerOlmMachine = innerOlmMachine,
innerVerificationRequest = inner,
requestSender = requestSender,
coroutineDispatchers = coroutineDispatchers,
listeners = listeners,
clock = clock
)
}
}

View File

@ -0,0 +1,74 @@
/*
* Copyright (c) 2022 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.matrix.android.sdk.internal.crypto.verification
import org.matrix.android.sdk.api.session.crypto.verification.VerificationTransaction
import org.matrix.android.sdk.internal.crypto.OlmMachine
import org.matrix.android.sdk.internal.crypto.verification.qrcode.QrCodeVerification
import javax.inject.Inject
import javax.inject.Provider
import uniffi.olm.OlmMachine as InnerOlmMachine
internal class VerificationsProvider @Inject constructor(
private val olmMachine: Provider<OlmMachine>,
private val verificationRequestFactory: VerificationRequest.Factory,
private val sasVerificationFactory: SasVerification.Factory,
private val qrVerificationFactory: QrCodeVerification.Factory) {
private val innerMachine: InnerOlmMachine
get() = olmMachine.get().inner()
fun getVerificationRequests(userId: String): List<VerificationRequest> {
return innerMachine.getVerificationRequests(userId).map(verificationRequestFactory::create)
}
/** Get a verification request for the given user with the given flow ID */
fun getVerificationRequest(userId: String, flowId: String): VerificationRequest? {
return innerMachine.getVerificationRequest(userId, flowId)?.let { innerVerificationRequest ->
verificationRequestFactory.create(innerVerificationRequest)
}
}
/** Get an active verification for the given user and given flow ID.
*
* @return Either a [SasVerification] verification or a [QrCodeVerification]
* verification.
*/
fun getVerification(userId: String, flowId: String): VerificationTransaction? {
return when (val verification = innerMachine.getVerification(userId, flowId)) {
is uniffi.olm.Verification.QrCodeV1 -> {
val request = getVerificationRequest(userId, flowId) ?: return null
qrVerificationFactory.create(request, verification.qrcode)
}
is uniffi.olm.Verification.SasV1 -> {
sasVerificationFactory.create(verification.sas)
}
null -> {
// This branch exists because scanning a QR code is tied to the QrCodeVerification,
// i.e. instead of branching into a scanned QR code verification from the verification request,
// like it's done for SAS verifications, the public API expects us to create an empty dummy
// QrCodeVerification object that gets populated once a QR code is scanned.
val request = getVerificationRequest(userId, flowId) ?: return null
if (request.canScanQrCodes()) {
qrVerificationFactory.create(request, null)
} else {
null
}
}
}
}
}

View File

@ -16,37 +16,44 @@
package org.matrix.android.sdk.internal.crypto.verification.qrcode package org.matrix.android.sdk.internal.crypto.verification.qrcode
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.matrix.android.sdk.api.MatrixCoroutineDispatchers import org.matrix.android.sdk.api.MatrixCoroutineDispatchers
import org.matrix.android.sdk.api.session.crypto.verification.CancelCode import org.matrix.android.sdk.api.session.crypto.verification.CancelCode
import org.matrix.android.sdk.api.session.crypto.verification.QrCodeVerificationTransaction import org.matrix.android.sdk.api.session.crypto.verification.QrCodeVerificationTransaction
import org.matrix.android.sdk.api.session.crypto.verification.VerificationService
import org.matrix.android.sdk.api.session.crypto.verification.VerificationTxState import org.matrix.android.sdk.api.session.crypto.verification.VerificationTxState
import org.matrix.android.sdk.api.session.crypto.verification.safeValueOf import org.matrix.android.sdk.api.session.crypto.verification.safeValueOf
import org.matrix.android.sdk.api.util.fromBase64 import org.matrix.android.sdk.api.util.fromBase64
import org.matrix.android.sdk.internal.crypto.OlmMachine
import org.matrix.android.sdk.internal.crypto.network.RequestSender import org.matrix.android.sdk.internal.crypto.network.RequestSender
import org.matrix.android.sdk.internal.crypto.verification.UpdateDispatcher import org.matrix.android.sdk.internal.crypto.verification.VerificationListenersHolder
import org.matrix.android.sdk.internal.crypto.verification.VerificationRequest import org.matrix.android.sdk.internal.crypto.verification.VerificationRequest
import uniffi.olm.CryptoStoreException import uniffi.olm.CryptoStoreException
import uniffi.olm.OlmMachine
import uniffi.olm.QrCode import uniffi.olm.QrCode
import uniffi.olm.Verification import uniffi.olm.Verification
/** Class representing a QR code based verification flow */ /** Class representing a QR code based verification flow */
internal class QrCodeVerification( internal class QrCodeVerification @AssistedInject constructor(
private val machine: OlmMachine, @Assisted private var request: VerificationRequest,
private var request: VerificationRequest, @Assisted private var inner: QrCode?,
private var inner: QrCode?, private val olmMachine: OlmMachine,
private val sender: RequestSender, private val sender: RequestSender,
private val coroutineDispatchers: MatrixCoroutineDispatchers, private val coroutineDispatchers: MatrixCoroutineDispatchers,
listeners: ArrayList<VerificationService.Listener> private val verificationListenersHolder: VerificationListenersHolder,
) : QrCodeVerificationTransaction { ) : QrCodeVerificationTransaction {
private val dispatcher = UpdateDispatcher(listeners) @AssistedFactory
interface Factory {
fun create(request: VerificationRequest, inner: QrCode?): QrCodeVerification
}
private val innerMachine = olmMachine.inner()
private fun dispatchTxUpdated() { private fun dispatchTxUpdated() {
refreshData() refreshData()
dispatcher.dispatchTxUpdated(this) verificationListenersHolder.dispatchTxUpdated(this)
} }
/** Generate, if possible, data that should be encoded as a QR code for QR code verification. /** Generate, if possible, data that should be encoded as a QR code for QR code verification.
@ -63,7 +70,7 @@ internal class QrCodeVerification(
*/ */
override val qrCodeText: String? override val qrCodeText: String?
get() { get() {
val data = inner?.let { machine.generateQrCode(it.otherUserId, it.flowId) } val data = inner?.let { innerMachine.generateQrCode(it.otherUserId, it.flowId) }
// TODO Why are we encoding this to ISO_8859_1? If we're going to encode, why not base64? // TODO Why are we encoding this to ISO_8859_1? If we're going to encode, why not base64?
return data?.fromBase64()?.toString(Charsets.ISO_8859_1) return data?.fromBase64()?.toString(Charsets.ISO_8859_1)
@ -176,7 +183,7 @@ internal class QrCodeVerification(
@Throws(CryptoStoreException::class) @Throws(CryptoStoreException::class)
private suspend fun confirm() { private suspend fun confirm() {
val result = withContext(coroutineDispatchers.io) { val result = withContext(coroutineDispatchers.io) {
machine.confirmVerification(request.otherUser(), request.flowId()) innerMachine.confirmVerification(request.otherUser(), request.flowId())
} }
if (result != null) { if (result != null) {
@ -192,7 +199,7 @@ internal class QrCodeVerification(
} }
private suspend fun cancelHelper(code: CancelCode) { private suspend fun cancelHelper(code: CancelCode) {
val request = machine.cancelVerification(request.otherUser(), request.flowId(), code.value) val request = innerMachine.cancelVerification(request.otherUser(), request.flowId(), code.value)
if (request != null) { if (request != null) {
sender.sendVerificationRequest(request) sender.sendVerificationRequest(request)
@ -202,7 +209,7 @@ internal class QrCodeVerification(
/** Fetch fresh data from the Rust side for our verification flow */ /** Fetch fresh data from the Rust side for our verification flow */
private fun refreshData() { private fun refreshData() {
when (val verification = machine.getVerification(request.otherUser(), request.flowId())) { when (val verification = innerMachine.getVerification(request.otherUser(), request.flowId())) {
is Verification.QrCodeV1 -> { is Verification.QrCodeV1 -> {
inner = verification.qrcode inner = verification.qrcode
} }

View File

@ -22,7 +22,7 @@ import org.matrix.android.sdk.api.MatrixCoroutineDispatchers
import org.matrix.android.sdk.api.auth.data.SessionParams import org.matrix.android.sdk.api.auth.data.SessionParams
import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.internal.crypto.CryptoModule import org.matrix.android.sdk.internal.crypto.CryptoModule
import org.matrix.android.sdk.internal.crypto.OlmMachineProvider import org.matrix.android.sdk.internal.crypto.OlmMachine
import org.matrix.android.sdk.internal.di.MatrixComponent import org.matrix.android.sdk.internal.di.MatrixComponent
import org.matrix.android.sdk.internal.federation.FederationModule import org.matrix.android.sdk.internal.federation.FederationModule
import org.matrix.android.sdk.internal.network.NetworkConnectivityChecker import org.matrix.android.sdk.internal.network.NetworkConnectivityChecker
@ -113,9 +113,9 @@ internal interface SessionComponent {
fun networkConnectivityChecker(): NetworkConnectivityChecker fun networkConnectivityChecker(): NetworkConnectivityChecker
fun taskExecutor(): TaskExecutor //fun olmMachine(): OlmMachine
fun olmMachineProvider(): OlmMachineProvider fun taskExecutor(): TaskExecutor
fun inject(worker: SendEventWorker) fun inject(worker: SendEventWorker)

View File

@ -142,7 +142,7 @@ internal abstract class SessionModule {
@JvmStatic @JvmStatic
@DeviceId @DeviceId
@Provides @Provides
fun providesDeviceId(credentials: Credentials): String? { fun providesDeviceId(credentials: Credentials): String {
return credentials.deviceId return credentials.deviceId
} }

View File

@ -32,7 +32,7 @@ import org.matrix.android.sdk.internal.util.time.Clock
import javax.inject.Inject import javax.inject.Inject
internal class MxCallFactory @Inject constructor( internal class MxCallFactory @Inject constructor(
@DeviceId private val deviceId: String?, @DeviceId private val deviceId: String,
private val localEchoEventFactory: LocalEchoEventFactory, private val localEchoEventFactory: LocalEchoEventFactory,
private val eventSenderProcessor: EventSenderProcessor, private val eventSenderProcessor: EventSenderProcessor,
private val matrixConfiguration: MatrixConfiguration, private val matrixConfiguration: MatrixConfiguration,
@ -48,7 +48,7 @@ internal class MxCallFactory @Inject constructor(
isOutgoing = false, isOutgoing = false,
roomId = roomId, roomId = roomId,
userId = userId, userId = userId,
ourPartyId = deviceId ?: "", ourPartyId = deviceId,
isVideoCall = content.isVideo(), isVideoCall = content.isVideo(),
localEchoEventFactory = localEchoEventFactory, localEchoEventFactory = localEchoEventFactory,
eventSenderProcessor = eventSenderProcessor, eventSenderProcessor = eventSenderProcessor,
@ -66,7 +66,7 @@ internal class MxCallFactory @Inject constructor(
isOutgoing = true, isOutgoing = true,
roomId = roomId, roomId = roomId,
userId = userId, userId = userId,
ourPartyId = deviceId ?: "", ourPartyId = deviceId,
isVideoCall = isVideoCall, isVideoCall = isVideoCall,
localEchoEventFactory = localEchoEventFactory, localEchoEventFactory = localEchoEventFactory,
eventSenderProcessor = eventSenderProcessor, eventSenderProcessor = eventSenderProcessor,

View File

@ -180,7 +180,7 @@ internal class CreateRoomBodyBuilder @Inject constructor(
params.invite3pids.isEmpty() && params.invite3pids.isEmpty() &&
params.invitedUserIds.isNotEmpty() && params.invitedUserIds.isNotEmpty() &&
params.invitedUserIds.let { userIds -> params.invitedUserIds.let { userIds ->
val keys = cryptoService.downloadKeys(userIds, forceDownload = false) val keys = cryptoService.downloadKeysIfNeeded(userIds, forceDownload = false)
userIds.all { userId -> userIds.all { userId ->
keys.map[userId].let { deviceMap -> keys.map[userId].let { deviceMap ->
if (deviceMap.isNullOrEmpty()) { if (deviceMap.isNullOrEmpty()) {

View File

@ -39,7 +39,7 @@ import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper
import org.matrix.android.sdk.api.session.room.send.SendState import org.matrix.android.sdk.api.session.room.send.SendState
import org.matrix.android.sdk.api.session.sync.model.RoomSyncSummary import org.matrix.android.sdk.api.session.sync.model.RoomSyncSummary
import org.matrix.android.sdk.api.session.sync.model.RoomSyncUnreadNotifications import org.matrix.android.sdk.api.session.sync.model.RoomSyncUnreadNotifications
import org.matrix.android.sdk.internal.crypto.OlmMachineProvider import org.matrix.android.sdk.internal.crypto.OlmMachine
import org.matrix.android.sdk.internal.database.mapper.ContentMapper import org.matrix.android.sdk.internal.database.mapper.ContentMapper
import org.matrix.android.sdk.internal.database.mapper.asDomain import org.matrix.android.sdk.internal.database.mapper.asDomain
import org.matrix.android.sdk.internal.database.model.CurrentStateEventEntity import org.matrix.android.sdk.internal.database.model.CurrentStateEventEntity
@ -71,12 +71,10 @@ internal class RoomSummaryUpdater @Inject constructor(
@UserId private val userId: String, @UserId private val userId: String,
private val roomDisplayNameResolver: RoomDisplayNameResolver, private val roomDisplayNameResolver: RoomDisplayNameResolver,
private val roomAvatarResolver: RoomAvatarResolver, private val roomAvatarResolver: RoomAvatarResolver,
private val olmMachineProvider: OlmMachineProvider, private val olmMachine: OlmMachine,
private val roomAccountDataDataSource: RoomAccountDataDataSource private val roomAccountDataDataSource: RoomAccountDataDataSource
) { ) {
private val olmMachine = olmMachineProvider.olmMachine
fun refreshLatestPreviewContent(realm: Realm, roomId: String) { fun refreshLatestPreviewContent(realm: Realm, roomId: String) {
val roomSummaryEntity = RoomSummaryEntity.getOrNull(realm, roomId) val roomSummaryEntity = RoomSummaryEntity.getOrNull(realm, roomId)
if (roomSummaryEntity != null) { if (roomSummaryEntity != null) {

View File

@ -120,7 +120,7 @@ class KeyRequestHandler @Inject constructor(
scope?.launch { scope?.launch {
try { try {
val data = session?.cryptoService()?.downloadKeys(listOf(userId), false) val data = session?.cryptoService()?.downloadKeysIfNeeded(listOf(userId), false)
?: return@launch ?: return@launch
val deviceInfo = data.getObject(userId, deviceId) val deviceInfo = data.getObject(userId, deviceId)

View File

@ -243,7 +243,7 @@ class HomeActivityViewModel @AssistedInject constructor(
val session = activeSessionHolder.getSafeActiveSession() ?: return@launch val session = activeSessionHolder.getSafeActiveSession() ?: return@launch
tryOrNull("## MaybeBootstrapCrossSigning: Failed to download keys") { tryOrNull("## MaybeBootstrapCrossSigning: Failed to download keys") {
session.cryptoService().downloadKeys(listOf(session.myUserId), true) session.cryptoService().downloadKeysIfNeeded(listOf(session.myUserId), true)
} }
// From there we are up to date with server // From there we are up to date with server

View File

@ -155,7 +155,7 @@ class DevicesViewModel @AssistedInject constructor(
refreshSource.stream().throttleFirst(4_000) refreshSource.stream().throttleFirst(4_000)
.onEach { .onEach {
session.cryptoService().fetchDevicesList() session.cryptoService().fetchDevicesList()
session.cryptoService().downloadKeys(listOf(session.myUserId), true) session.cryptoService().downloadKeysIfNeeded(listOf(session.myUserId), true)
} }
.launchIn(viewModelScope) .launchIn(viewModelScope)
// then force download // then force download