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.
*/
@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
* reconfigure themselves, optionally validating the URLs within.
@ -59,5 +59,5 @@ data class Credentials(
)
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
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>

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 javax.inject.Inject
internal class DecryptRoomEventUseCase @Inject constructor(olmMachineProvider: OlmMachineProvider) {
private val olmMachine = olmMachineProvider.olmMachine
internal class DecryptRoomEventUseCase @Inject constructor(private val olmMachine: OlmMachine) {
suspend operator fun invoke(event: Event): MXEventDecryptionResult {
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.internal.crypto.keysbackup.RustKeyBackupService
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.store.IMXCryptoStore
import org.matrix.android.sdk.internal.crypto.tasks.DeleteDeviceTask
@ -99,7 +98,7 @@ private val loggerTag = LoggerTag("DefaultCryptoService", LoggerTag.CRYPTO)
@SessionScope
internal class DefaultCryptoService @Inject constructor(
@UserId private val userId: String,
@DeviceId private val deviceId: String?,
@DeviceId private val deviceId: String,
// the crypto store
private val cryptoStore: IMXCryptoStore,
// 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 coroutineDispatchers: MatrixCoroutineDispatchers,
private val cryptoCoroutineScope: CoroutineScope,
private val requestSender: RequestSender,
private val olmMachine: OlmMachine,
private val crossSigningService: CrossSigningService,
private val verificationService: RustVerificationService,
private val keysBackupService: RustKeyBackupService,
private val megolmSessionImportManager: MegolmSessionImportManager,
private val olmMachineProvider: OlmMachineProvider,
private val liveEventManager: dagger.Lazy<StreamEventsManager>,
private val prepareToEncrypt: PrepareToEncryptUseCase,
private val encryptEventContent: EncryptEventContentUseCase,
private val getRoomUserIds: GetRoomUserIdsUseCase,
private val outgoingRequestsProcessor: OutgoingRequestsProcessor,
) : CryptoService {
private val isStarting = 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) {
when (event.type) {
EventType.STATE_ROOM_ENCRYPTION -> onRoomEncryptionEvent(roomId, event)
@ -164,7 +154,7 @@ internal class DefaultCryptoService @Inject constructor(
val params = SetDeviceNameTask.Params(deviceId, deviceName)
setDeviceNameTask.execute(params)
try {
downloadKeys(listOf(userId), true)
downloadKeysIfNeeded(listOf(userId), true)
} catch (failure: Throwable) {
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)) {
return
}
event.stateKey?.let { userId ->
val roomMember: RoomMemberContent? = event.content.toModel()
val membership = roomMember?.membership
@ -722,7 +711,7 @@ internal class DefaultCryptoService @Inject constructor(
// 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) {
olmMachine.ensureUserDevicesMap(userIds, forceDownload)
}

View File

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

View File

@ -26,13 +26,13 @@ import javax.inject.Inject
private val loggerTag = LoggerTag("EncryptEventContentUseCase", LoggerTag.CRYPTO)
internal class EncryptEventContentUseCase @Inject constructor(olmMachineProvider: OlmMachineProvider,
internal class EncryptEventContentUseCase @Inject constructor(
private val olmMachine: OlmMachine,
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,
roomId: String): MXEncryptEventContentResult {
val t0 = clock.epochMillis()

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.internal.crypto.model.rest.RestKeyInfo
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.OlmMachine
import javax.inject.Inject
import javax.inject.Provider
internal class GetUserIdentityUseCase(
private val innerMachine: OlmMachine,
internal class GetUserIdentityUseCase @Inject constructor(
private val olmMachine: Provider<OlmMachine>,
private val requestSender: RequestSender,
private val coroutineDispatchers: MatrixCoroutineDispatchers,
private val moshi: Moshi,
private val verificationRequestFactory: VerificationRequestFactory
private val verificationRequestFactory: VerificationRequest.Factory
) {
@Throws(CryptoStoreException::class)
suspend operator fun invoke(userId: String): UserIdentities? {
val innerMachine = olmMachine.get().inner()
val identity = withContext(coroutineDispatchers.io) {
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.auth.UserInteractiveAuthInterceptor
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.session.crypto.MXCryptoError
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.MXUsersDevicesMap
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.events.model.Content
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.verification.SasVerification
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.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.util.time.Clock
import org.matrix.android.sdk.internal.session.SessionScope
import timber.log.Timber
import uniffi.olm.BackupKeys
import uniffi.olm.CrossSigningKeyExport
@ -72,7 +73,7 @@ import uniffi.olm.RoomKeyCounts
import uniffi.olm.setLogger
import java.io.File
import java.nio.charset.Charset
import java.util.UUID
import javax.inject.Inject
import uniffi.olm.OlmMachine as InnerMachine
import uniffi.olm.ProgressListener as RustProgressListener
@ -105,20 +106,22 @@ fun setRustLogger() {
setLogger(CryptoLogger() as Logger)
}
internal class OlmMachine(
user_id: String,
device_id: String,
path: File,
clock: Clock,
@SessionScope
internal class OlmMachine @Inject constructor(
@UserId userId: String,
@DeviceId deviceId: String,
@SessionFilesDirectory path: File,
private val requestSender: RequestSender,
private val coroutineDispatchers: MatrixCoroutineDispatchers,
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)
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 inner: InnerMachine = InnerMachine(userId, deviceId, path.toString(), null)
private val flowCollectors = FlowCollectors()
/** Get our own user ID. */
@ -525,13 +528,12 @@ internal class OlmMachine(
val innerDevice = withContext(coroutineDispatchers.io) {
inner.getDevice(userId, deviceId)
} ?: return null
return innerDevice.wrap()
return deviceFactory.create(innerDevice)
}
suspend fun getUserDevices(userId: String): List<Device> {
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)
suspend fun getCryptoDeviceInfo(userId: String): List<CryptoDeviceInfo> {
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
}
@Throws
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> {
private suspend fun getUserDevicesMap(userIds: List<String>): MXUsersDevicesMap<CryptoDeviceInfo> {
val userMap = MXUsersDevicesMap<CryptoDeviceInfo>()
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.
*/
suspend fun ensureUsersKeys(userIds: List<String>, forceDownload: Boolean = false) {
val userIdsToFetchKeys = if (forceDownload) {
userIds
} else {
userIds.mapNotNull { userId ->
userId.takeIf { !isUserTracked(it) }
}.also {
updateTrackedUsers(it)
}
}
tryOrNull("Failed to download keys for $userIdsToFetchKeys") {
forceKeyDownload(userIdsToFetchKeys)
}
ensureUsersKeys.invoke(userIds, forceDownload)
}
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
*/
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 */
fun getVerificationRequest(userId: String, flowId: String): VerificationRequest? {
return inner.getVerificationRequest(userId, flowId)?.let { innerVerificationRequest ->
verificationRequestFactory.create(innerVerificationRequest)
}
return verificationsProvider.getVerificationRequest(userId, flowId)
}
/** Get an active verification for the given user and given flow ID.
@ -708,48 +678,7 @@ internal class OlmMachine(
* verification.
*/
fun getVerification(userId: String, flowId: String): VerificationTransaction? {
return when (val verification = inner.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
}
}
}
return verificationsProvider.getVerification(userId, flowId)
}
suspend fun bootstrapCrossSigning(uiaInterceptor: UserInteractiveAuthInterceptor?) {
@ -868,13 +797,4 @@ internal class OlmMachine(
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,7 +39,8 @@ import javax.inject.Inject
private val loggerTag = LoggerTag("PrepareToEncryptUseCase", LoggerTag.CRYPTO)
@SessionScope
internal class PrepareToEncryptUseCase @Inject constructor(olmMachineProvider: OlmMachineProvider,
internal class PrepareToEncryptUseCase @Inject constructor(
private val olmMachine: OlmMachine,
private val coroutineDispatchers: MatrixCoroutineDispatchers,
private val cryptoStore: IMXCryptoStore,
private val getRoomUserIds: GetRoomUserIdsUseCase,
@ -48,8 +49,6 @@ internal class PrepareToEncryptUseCase @Inject constructor(olmMachineProvider: O
private val keysBackupService: RustKeyBackupService
) {
private val olmMachine = olmMachineProvider.olmMachine
private val keyClaimLock: Mutex = Mutex()
private val roomKeyShareLocks: ConcurrentHashMap<String, Mutex> = ConcurrentHashMap()
@ -91,7 +90,7 @@ internal class PrepareToEncryptUseCase @Inject constructor(olmMachineProvider: O
is Request.ToDevice -> {
sharedKey = true
async {
sendToDevice(it)
sendToDevice(olmMachine, it)
}
}
else -> {
@ -113,7 +112,7 @@ internal class PrepareToEncryptUseCase @Inject constructor(olmMachineProvider: O
}
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.
when (request) {
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 {
requestSender.sendToDevice(request)
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 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.DeviceTrustResult
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.model.RoomEncryptionTrustLevel
import org.matrix.android.sdk.api.util.Optional
import org.matrix.android.sdk.internal.di.UserId
import javax.inject.Inject
internal class RustCrossSigningService @Inject constructor(
// @SessionId private val sessionId: String,
@UserId private val myUserId: String,
private val olmMachineProvider: OlmMachineProvider
private val olmMachine: OlmMachine,
private val computeShieldForGroup: ComputeShieldForGroupUseCase
) : CrossSigningService {
val olmMachine = olmMachineProvider.olmMachine
/**
* 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 {
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 ->
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
}
}
}
}
return computeShieldForGroup(olmMachine, userIds)
}
}

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.internal.crypto.network.RequestSender
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 uniffi.olm.CryptoStoreException
import uniffi.olm.OlmMachine
@ -85,7 +84,7 @@ internal class OwnUserIdentity(
private val innerMachine: OlmMachine,
private val requestSender: RequestSender,
private val coroutineDispatchers: MatrixCoroutineDispatchers,
private val verificationRequestFactory: VerificationRequestFactory,
private val verificationRequestFactory: VerificationRequest.Factory,
) : UserIdentities() {
/**
* Our own user id.
@ -174,7 +173,7 @@ internal class UserIdentity(
private val innerMachine: OlmMachine,
private val requestSender: RequestSender,
private val coroutineDispatchers: MatrixCoroutineDispatchers,
private val verificationRequestFactory: VerificationRequestFactory,
private val verificationRequestFactory: VerificationRequest.Factory,
) : UserIdentities() {
/**
* 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.internal.crypto.MegolmSessionData
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.rest.CreateKeysBackupVersionBody
import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeyBackupData
@ -73,7 +73,7 @@ import kotlin.random.Random
*/
@SessionScope
internal class RustKeyBackupService @Inject constructor(
olmMachineProvider: OlmMachineProvider,
private val olmMachine: OlmMachine,
private val sender: RequestSender,
private val coroutineDispatchers: MatrixCoroutineDispatchers,
private val megolmSessionImportManager: MegolmSessionImportManager,
@ -88,8 +88,6 @@ internal class RustKeyBackupService @Inject constructor(
private val keysBackupStateManager = KeysBackupStateManager(uiHandler)
private val olmMachine = olmMachineProvider.olmMachine
// The backup version
override var keysBackupVersion: KeysVersionResult? = null
private set

View File

@ -18,38 +18,43 @@ package org.matrix.android.sdk.internal.crypto.network
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.joinAll
import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
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.internal.crypto.ComputeShieldForGroupUseCase
import org.matrix.android.sdk.internal.crypto.CryptoSessionInfoProvider
import org.matrix.android.sdk.internal.crypto.OlmMachine
import org.matrix.android.sdk.internal.session.SessionScope
import timber.log.Timber
import uniffi.olm.Request
import uniffi.olm.RequestType
import javax.inject.Inject
private val loggerTag = LoggerTag("OutgoingRequestsProcessor", LoggerTag.CRYPTO)
internal class OutgoingRequestsProcessor(private val requestSender: RequestSender,
@SessionScope
internal class OutgoingRequestsProcessor @Inject constructor(
private val requestSender: RequestSender,
private val coroutineScope: CoroutineScope,
private val cryptoSessionInfoProvider: CryptoSessionInfoProvider,
private val shieldComputer: ShieldComputer,) {
fun interface ShieldComputer {
suspend fun compute(userIds: List<String>): RoomEncryptionTrustLevel
}
private val computeShieldForGroup: ComputeShieldForGroupUseCase
) {
private val lock: Mutex = Mutex()
suspend fun processOutgoingRequests(olmMachine: OlmMachine) {
lock.withLock {
suspend fun processOutgoingRequests(olmMachine: OlmMachine,
filter: (Request) -> Boolean = { true }
): Boolean {
return lock.withLock {
coroutineScope {
Timber.v("OutgoingRequests: ${olmMachine.outgoingRequests()}")
olmMachine.outgoingRequests().map {
val outgoingRequests = olmMachine.outgoingRequests()
val filteredOutgoingRequests = outgoingRequests.filter(filter)
Timber.v("OutgoingRequests to process: $filteredOutgoingRequests}")
filteredOutgoingRequests.map {
when (it) {
is Request.KeysUpload -> {
async {
@ -85,10 +90,11 @@ internal class OutgoingRequestsProcessor(private val requestSender: RequestSende
async {
// The rust-sdk won't ever produce KeysBackup requests here,
// 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) {
try {
private suspend fun uploadKeys(olmMachine: OlmMachine, request: Request.KeysUpload): Boolean {
return try {
val response = requestSender.uploadKeys(request)
olmMachine.markRequestAsSent(request.requestId, RequestType.KEYS_UPLOAD, response)
true
} catch (throwable: Throwable) {
Timber.tag(loggerTag.value).e(throwable, "## uploadKeys(): error")
false
}
}
private suspend fun queryKeys(olmMachine: OlmMachine, request: Request.KeysQuery) {
try {
private suspend fun queryKeys(olmMachine: OlmMachine, request: Request.KeysQuery): Boolean {
return try {
val response = requestSender.queryKeys(request)
olmMachine.markRequestAsSent(request.requestId, RequestType.KEYS_QUERY, response)
coroutineScope.updateShields(request.users)
coroutineScope.updateShields(olmMachine, request.users)
true
} catch (throwable: Throwable) {
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 ->
val userGroup = cryptoSessionInfoProvider.getUserListForShieldComputation(roomId)
val shield = shieldComputer.compute(userGroup)
val shield = computeShieldForGroup(olmMachine, userGroup)
cryptoSessionInfoProvider.updateShieldForRoom(roomId, shield)
}
}
private suspend fun sendToDevice(olmMachine: OlmMachine, request: Request.ToDevice) {
try {
private suspend fun sendToDevice(olmMachine: OlmMachine, request: Request.ToDevice): Boolean {
return try {
requestSender.sendToDevice(request)
olmMachine.markRequestAsSent(request.requestId, RequestType.TO_DEVICE, "{}")
true
} catch (throwable: Throwable) {
Timber.tag(loggerTag.value).e(throwable, "## sendToDevice(): error")
false
}
}
private suspend fun claimKeys(olmMachine: OlmMachine, request: Request.KeysClaim) {
try {
private suspend fun claimKeys(olmMachine: OlmMachine, request: Request.KeysClaim): Boolean {
return try {
val response = requestSender.claimKeys(request)
olmMachine.markRequestAsSent(request.requestId, RequestType.KEYS_CLAIM, response)
true
} catch (throwable: Throwable) {
Timber.tag(loggerTag.value).e(throwable, "## claimKeys(): error")
false
}
}
private suspend fun signatureUpload(olmMachine: OlmMachine, request: Request.SignatureUpload) {
try {
private suspend fun signatureUpload(olmMachine: OlmMachine, request: Request.SignatureUpload): Boolean {
return try {
val response = requestSender.sendSignatureUpload(request)
olmMachine.markRequestAsSent(request.requestId, RequestType.SIGNATURE_UPLOAD, response)
true
} catch (throwable: Throwable) {
Timber.tag(loggerTag.value).e(throwable, "## signatureUpload(): error")
false
}
}
private suspend fun sendRoomMessage(olmMachine: OlmMachine, request: Request.RoomMessage) {
try {
private suspend fun sendRoomMessage(olmMachine: OlmMachine, request: Request.RoomMessage): Boolean {
return try {
val response = requestSender.sendRoomMessage(request)
olmMachine.markRequestAsSent(request.requestId, RequestType.ROOM_MESSAGE, response)
true
} catch (throwable: Throwable) {
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,
private val crossSigningKeysMapper: CrossSigningKeysMapper,
@UserId private val userId: String,
@DeviceId private val deviceId: String?,
@DeviceId private val deviceId: String,
private val clock: Clock,
) : IMXCryptoStore {
@ -155,7 +155,7 @@ internal class RealmCryptoStore @Inject constructor(
// The device id may not have been provided in credentials.
// Check it only if provided, else trust the stored one.
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")
deleteAll = true
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.MessageRelationContent
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.UserIdentity
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
internal class RustVerificationService @Inject constructor(private val olmMachineProvider: OlmMachineProvider) : VerificationService {
val olmMachine by lazy {
olmMachineProvider.olmMachine
}
private val dispatcher = UpdateDispatcher(olmMachine.verificationListeners)
internal class RustVerificationService @Inject constructor(
private val olmMachine: OlmMachine,
private val verificationListenersHolder: VerificationListenersHolder) : VerificationService {
/**
*
@ -121,7 +117,7 @@ internal class RustVerificationService @Inject constructor(private val olmMachin
olmMachine.getVerificationRequest(sender, flowId)?.dispatchRequestUpdated()
val verification = getExistingTransaction(sender, flowId) ?: return
dispatcher.dispatchTxUpdated(verification)
verificationListenersHolder.dispatchTxUpdated(verification)
}
/** 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")
verification.accept()
} else {
dispatcher.dispatchTxUpdated(verification)
verificationListenersHolder.dispatchTxUpdated(verification)
}
} else {
// This didn't originate from a request, so tell our listeners that
// this is a new verification.
dispatcher.dispatchTxAdded(verification)
verificationListenersHolder.dispatchTxAdded(verification)
// The IncomingVerificationRequestHandler seems to only listen to updates
// 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 request = getExistingVerificationRequest(sender, flowId) ?: return
dispatcher.dispatchRequestAdded(request)
verificationListenersHolder.dispatchRequestAdded(request)
}
override fun addListener(listener: VerificationService.Listener) {
dispatcher.addListener(listener)
verificationListenersHolder.addListener(listener)
}
override fun removeListener(listener: VerificationService.Listener) {
dispatcher.removeListener(listener)
verificationListenersHolder.removeListener(listener)
}
override suspend fun markedLocallyAsManuallyVerified(userId: String, deviceID: String) {
@ -275,7 +271,7 @@ internal class RustVerificationService @Inject constructor(private val olmMachin
val qrcode = request.startQrVerification()
if (qrcode != null) {
dispatcher.dispatchTxAdded(qrcode)
verificationListenersHolder.dispatchTxAdded(qrcode)
}
true
@ -298,7 +294,7 @@ internal class RustVerificationService @Inject constructor(private val olmMachin
val sas = request?.startSasVerification()
if (sas != null) {
dispatcher.dispatchTxAdded(sas)
verificationListenersHolder.dispatchTxAdded(sas)
sas.transactionId
} else {
null
@ -317,7 +313,7 @@ internal class RustVerificationService @Inject constructor(private val olmMachin
val otherDevice = olmMachine.getDevice(otherUserId, otherDeviceId)
val verification = otherDevice?.startVerification()
return if (verification != null) {
dispatcher.dispatchTxAdded(verification)
verificationListenersHolder.dispatchTxAdded(verification)
verification.transactionId
} else {
null

View File

@ -16,34 +16,42 @@
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 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.EmojiRepresentation
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.safeValueOf
import org.matrix.android.sdk.internal.crypto.OlmMachine
import org.matrix.android.sdk.internal.crypto.network.RequestSender
import uniffi.olm.CryptoStoreException
import uniffi.olm.OlmMachine
import uniffi.olm.Sas
import uniffi.olm.Verification
/** Class representing a short auth string verification flow */
internal class SasVerification(
private val machine: OlmMachine,
private var inner: Sas,
internal class SasVerification @AssistedInject constructor(
@Assisted private var inner: Sas,
private val olmMachine: OlmMachine,
private val sender: RequestSender,
private val coroutineDispatchers: MatrixCoroutineDispatchers,
listeners: ArrayList<VerificationService.Listener>
private val verificationListenersHolder: VerificationListenersHolder
) :
SasVerificationTransaction {
private val dispatcher = UpdateDispatcher(listeners)
@AssistedFactory
interface Factory {
fun create(inner: Sas): SasVerification
}
private val innerMachine = olmMachine.inner()
private fun dispatchTxUpdated() {
refreshData()
dispatcher.dispatchTxUpdated(this)
verificationListenersHolder.dispatchTxUpdated(this)
}
/** 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.
*/
override fun getDecimalCodeRepresentation(): String {
val decimals = machine.getDecimals(inner.otherUserId, inner.flowId)
val decimals = innerMachine.getDecimals(inner.otherUserId, inner.flowId)
return decimals?.joinToString(" ") ?: ""
}
@ -179,13 +187,13 @@ internal class SasVerification(
* state.
*/
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()
}
internal suspend fun accept() {
val request = machine.acceptSasVerification(inner.otherUserId, inner.flowId)
val request = innerMachine.acceptSasVerification(inner.otherUserId, inner.flowId)
if (request != null) {
sender.sendVerificationRequest(request)
@ -196,7 +204,7 @@ internal class SasVerification(
@Throws(CryptoStoreException::class)
private suspend fun confirm() {
val result = withContext(coroutineDispatchers.io) {
machine.confirmVerification(inner.otherUserId, inner.flowId)
innerMachine.confirmVerification(inner.otherUserId, inner.flowId)
}
if (result != null) {
for (verificationRequest in result.requests) {
@ -211,7 +219,7 @@ internal class SasVerification(
}
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) {
sender.sendVerificationRequest(request)
@ -221,7 +229,7 @@ internal class SasVerification(
/** Fetch fresh data from the Rust side for our verification flow */
private fun refreshData() {
when (val verification = machine.getVerification(inner.otherUserId, inner.flowId)) {
when (val verification = innerMachine.getVerification(inner.otherUserId, inner.flowId)) {
is Verification.SasV1 -> {
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");
* 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.VerificationService
import org.matrix.android.sdk.api.session.crypto.verification.VerificationTransaction
import org.matrix.android.sdk.internal.session.SessionScope
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())
internal fun addListener(listener: VerificationService.Listener) {
fun listeners(): List<VerificationService.Listener> = listeners
fun addListener(listener: VerificationService.Listener) {
uiHandler.post {
if (!this.listeners.contains(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) }
}
internal fun dispatchTxAdded(tx: VerificationTransaction) {
fun dispatchTxAdded(tx: VerificationTransaction) {
uiHandler.post {
this.listeners.forEach {
try {
@ -51,7 +58,7 @@ internal class UpdateDispatcher(private val listeners: ArrayList<VerificationSer
}
}
internal fun dispatchTxUpdated(tx: VerificationTransaction) {
fun dispatchTxUpdated(tx: VerificationTransaction) {
uiHandler.post {
this.listeners.forEach {
try {
@ -63,12 +70,24 @@ internal class UpdateDispatcher(private val listeners: ArrayList<VerificationSer
}
}
internal fun dispatchRequestAdded(tx: PendingVerificationRequest) {
Timber.v("## SAS dispatchRequestAdded txId:${tx.transactionId} $tx")
fun dispatchRequestAdded(verificationRequest: PendingVerificationRequest) {
Timber.v("## SAS dispatchRequestAdded txId:${verificationRequest.transactionId} $verificationRequest")
uiHandler.post {
this.listeners.forEach {
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) {
Timber.e(e, "## Error while notifying listeners")
}

View File

@ -16,8 +16,9 @@
package org.matrix.android.sdk.internal.crypto.verification
import android.os.Handler
import android.os.Looper
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.withContext
import org.matrix.android.sdk.api.MatrixCoroutineDispatchers
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.ValidVerificationInfoRequest
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.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.network.RequestSender
import org.matrix.android.sdk.internal.crypto.verification.qrcode.QrCodeVerification
import org.matrix.android.sdk.internal.util.time.Clock
import timber.log.Timber
import uniffi.olm.OlmMachine
import uniffi.olm.VerificationRequest
import uniffi.olm.VerificationRequest as InnerVerificationRequest
/** 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
* concrete verification flows.
*/
internal class VerificationRequest(
private val innerOlmMachine: OlmMachine,
private var innerVerificationRequest: VerificationRequest,
internal class VerificationRequest @AssistedInject constructor(
@Assisted private var innerVerificationRequest: InnerVerificationRequest,
olmMachine: OlmMachine,
private val requestSender: RequestSender,
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 uiHandler = Handler(Looper.getMainLooper())
private val innerOlmMachine = olmMachine.inner()
@AssistedFactory
interface Factory {
fun create(innerVerificationRequest: InnerVerificationRequest): VerificationRequest
}
internal fun dispatchRequestUpdated() {
uiHandler.post {
listeners.forEach {
try {
it.verificationRequestUpdated(toPendingVerificationRequest())
} catch (e: Throwable) {
Timber.e(e, "## Error while notifying listeners")
}
}
}
val tx = toPendingVerificationRequest()
verificationListenersHolder.dispatchRequestUpdated(tx)
}
/** Get the flow ID of this verification request
@ -167,7 +167,7 @@ internal class VerificationRequest(
if (result != null) {
requestSender.sendVerificationRequest(result.request)
SasVerification(innerOlmMachine, result.sas, requestSender, coroutineDispatchers, listeners)
sasVerificationFactory.create(result.sas)
} else {
null
}
@ -194,7 +194,7 @@ internal class VerificationRequest(
val result = innerOlmMachine.scanQrCode(otherUser(), flowId(), encodedData) ?: return null
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
@ -216,16 +216,8 @@ internal class VerificationRequest(
*/
internal fun startQrVerification(): QrCodeVerification? {
val qrcode = innerOlmMachine.startQrVerification(innerVerificationRequest.otherUserId, innerVerificationRequest.flowId)
return if (qrcode != null) {
QrCodeVerification(
machine = innerOlmMachine,
request = this,
inner = qrcode,
sender = requestSender,
coroutineDispatchers = coroutineDispatchers,
listeners = listeners,
)
qrCodeVerificationFactory.create(this, qrcode)
} else {
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
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.withContext
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.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.safeValueOf
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.verification.UpdateDispatcher
import org.matrix.android.sdk.internal.crypto.verification.VerificationListenersHolder
import org.matrix.android.sdk.internal.crypto.verification.VerificationRequest
import uniffi.olm.CryptoStoreException
import uniffi.olm.OlmMachine
import uniffi.olm.QrCode
import uniffi.olm.Verification
/** Class representing a QR code based verification flow */
internal class QrCodeVerification(
private val machine: OlmMachine,
private var request: VerificationRequest,
private var inner: QrCode?,
internal class QrCodeVerification @AssistedInject constructor(
@Assisted private var request: VerificationRequest,
@Assisted private var inner: QrCode?,
private val olmMachine: OlmMachine,
private val sender: RequestSender,
private val coroutineDispatchers: MatrixCoroutineDispatchers,
listeners: ArrayList<VerificationService.Listener>
private val verificationListenersHolder: VerificationListenersHolder,
) : QrCodeVerificationTransaction {
private val dispatcher = UpdateDispatcher(listeners)
@AssistedFactory
interface Factory {
fun create(request: VerificationRequest, inner: QrCode?): QrCodeVerification
}
private val innerMachine = olmMachine.inner()
private fun dispatchTxUpdated() {
refreshData()
dispatcher.dispatchTxUpdated(this)
verificationListenersHolder.dispatchTxUpdated(this)
}
/** 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?
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?
return data?.fromBase64()?.toString(Charsets.ISO_8859_1)
@ -176,7 +183,7 @@ internal class QrCodeVerification(
@Throws(CryptoStoreException::class)
private suspend fun confirm() {
val result = withContext(coroutineDispatchers.io) {
machine.confirmVerification(request.otherUser(), request.flowId())
innerMachine.confirmVerification(request.otherUser(), request.flowId())
}
if (result != null) {
@ -192,7 +199,7 @@ internal class QrCodeVerification(
}
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) {
sender.sendVerificationRequest(request)
@ -202,7 +209,7 @@ internal class QrCodeVerification(
/** Fetch fresh data from the Rust side for our verification flow */
private fun refreshData() {
when (val verification = machine.getVerification(request.otherUser(), request.flowId())) {
when (val verification = innerMachine.getVerification(request.otherUser(), request.flowId())) {
is Verification.QrCodeV1 -> {
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.session.Session
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.federation.FederationModule
import org.matrix.android.sdk.internal.network.NetworkConnectivityChecker
@ -113,9 +113,9 @@ internal interface SessionComponent {
fun networkConnectivityChecker(): NetworkConnectivityChecker
fun taskExecutor(): TaskExecutor
//fun olmMachine(): OlmMachine
fun olmMachineProvider(): OlmMachineProvider
fun taskExecutor(): TaskExecutor
fun inject(worker: SendEventWorker)

View File

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

View File

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

View File

@ -180,7 +180,7 @@ internal class CreateRoomBodyBuilder @Inject constructor(
params.invite3pids.isEmpty() &&
params.invitedUserIds.isNotEmpty() &&
params.invitedUserIds.let { userIds ->
val keys = cryptoService.downloadKeys(userIds, forceDownload = false)
val keys = cryptoService.downloadKeysIfNeeded(userIds, forceDownload = false)
userIds.all { userId ->
keys.map[userId].let { deviceMap ->
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.sync.model.RoomSyncSummary
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.asDomain
import org.matrix.android.sdk.internal.database.model.CurrentStateEventEntity
@ -71,12 +71,10 @@ internal class RoomSummaryUpdater @Inject constructor(
@UserId private val userId: String,
private val roomDisplayNameResolver: RoomDisplayNameResolver,
private val roomAvatarResolver: RoomAvatarResolver,
private val olmMachineProvider: OlmMachineProvider,
private val olmMachine: OlmMachine,
private val roomAccountDataDataSource: RoomAccountDataDataSource
) {
private val olmMachine = olmMachineProvider.olmMachine
fun refreshLatestPreviewContent(realm: Realm, roomId: String) {
val roomSummaryEntity = RoomSummaryEntity.getOrNull(realm, roomId)
if (roomSummaryEntity != null) {

View File

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

View File

@ -243,7 +243,7 @@ class HomeActivityViewModel @AssistedInject constructor(
val session = activeSessionHolder.getSafeActiveSession() ?: return@launch
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

View File

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