Wire compute room shields with rust

This commit is contained in:
Valere 2021-10-29 15:24:18 +02:00
parent ae635e2b0a
commit f209ae26bc
30 changed files with 1867 additions and 1573 deletions

2
.gitignore vendored
View file

@ -20,3 +20,5 @@ Cargo.lock
/fastlane/report.xml
/library/build
matrix-sdk-android/src/main/jniLibs/

View file

@ -35,13 +35,11 @@ import org.matrix.android.sdk.internal.crypto.OutgoingRoomKeyRequest
import org.matrix.android.sdk.internal.crypto.crosssigning.DeviceTrustLevel
import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo
import org.matrix.android.sdk.internal.crypto.model.ImportRoomKeysResult
import org.matrix.android.sdk.internal.crypto.model.MXDeviceInfo
import org.matrix.android.sdk.internal.crypto.model.MXEncryptEventContentResult
import org.matrix.android.sdk.internal.crypto.model.MXUsersDevicesMap
import org.matrix.android.sdk.internal.crypto.model.event.RoomKeyWithHeldContent
import org.matrix.android.sdk.internal.crypto.model.rest.DeviceInfo
import org.matrix.android.sdk.internal.crypto.model.rest.DevicesListResponse
import org.matrix.android.sdk.internal.crypto.model.rest.RoomKeyRequestBody
interface CryptoService {
@ -153,4 +151,11 @@ interface CryptoService {
* send, in order to speed up sending of the message.
*/
fun prepareToEncrypt(roomId: String, callback: MatrixCallback<Unit>)
/**
* When LL all room members might not be loaded when setting up encryption.
* This is called after room members have been loaded
* ... not sure if shoud be API
*/
fun onE2ERoomMemberLoadedFromServer(roomId: String)
}

View file

@ -19,6 +19,7 @@ package org.matrix.android.sdk.api.session.crypto.crosssigning
import androidx.lifecycle.LiveData
import org.matrix.android.sdk.api.MatrixCallback
import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
import org.matrix.android.sdk.api.crypto.RoomEncryptionTrustLevel
import org.matrix.android.sdk.api.util.Optional
import org.matrix.android.sdk.internal.crypto.crosssigning.DeviceTrustResult
import org.matrix.android.sdk.internal.crypto.crosssigning.UserTrustResult
@ -104,6 +105,8 @@ interface CrossSigningService {
fun trustDevice(deviceId: String,
callback: MatrixCallback<Unit>)
suspend fun shieldForGroup(userIds: List<String>) : RoomEncryptionTrustLevel
/**
* Check if a device is trusted
*

View file

@ -27,7 +27,6 @@ import org.matrix.android.sdk.api.session.crypto.crosssigning.CrossSigningServic
import org.matrix.android.sdk.internal.crypto.api.CryptoApi
import org.matrix.android.sdk.internal.crypto.crosssigning.ComputeTrustTask
import org.matrix.android.sdk.internal.crypto.crosssigning.DefaultComputeTrustTask
import org.matrix.android.sdk.internal.crypto.crosssigning.DefaultCrossSigningService
import org.matrix.android.sdk.internal.crypto.keysbackup.api.RoomKeysApi
import org.matrix.android.sdk.internal.crypto.keysbackup.tasks.CreateKeysBackupVersionTask
import org.matrix.android.sdk.internal.crypto.keysbackup.tasks.DefaultCreateKeysBackupVersionTask
@ -96,6 +95,11 @@ import org.matrix.android.sdk.internal.di.UserMd5
import org.matrix.android.sdk.internal.session.SessionScope
import org.matrix.android.sdk.internal.session.cache.ClearCacheTask
import org.matrix.android.sdk.internal.session.cache.RealmClearCacheTask
import io.realm.RealmConfiguration
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import org.matrix.android.sdk.api.session.crypto.verification.VerificationService
import org.matrix.android.sdk.internal.crypto.verification.RustVerificationService
import retrofit2.Retrofit
import java.io.File
@ -238,7 +242,10 @@ internal abstract class CryptoModule {
abstract fun bindClaimOneTimeKeysForUsersDeviceTask(task: DefaultClaimOneTimeKeysForUsersDevice): ClaimOneTimeKeysForUsersDeviceTask
@Binds
abstract fun bindCrossSigningService(service: DefaultCrossSigningService): CrossSigningService
abstract fun bindCrossSigningService(service: RustCrossSigningService): CrossSigningService
@Binds
abstract fun bindVerificationService(service: RustVerificationService): VerificationService
@Binds
abstract fun bindCryptoStore(store: RealmCryptoStore): IMXCryptoStore

View file

@ -17,13 +17,21 @@
package org.matrix.android.sdk.internal.crypto
import com.zhuinden.monarchy.Monarchy
import org.matrix.android.sdk.api.crypto.RoomEncryptionTrustLevel
import org.matrix.android.sdk.api.session.events.model.EventType
import org.matrix.android.sdk.internal.database.model.EventEntity
import org.matrix.android.sdk.internal.database.model.EventEntityFields
import org.matrix.android.sdk.internal.database.model.RoomMemberSummaryEntity
import org.matrix.android.sdk.internal.database.model.RoomMemberSummaryEntityFields
import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity
import org.matrix.android.sdk.internal.database.query.where
import org.matrix.android.sdk.internal.database.query.whereType
import org.matrix.android.sdk.internal.di.SessionDatabase
import org.matrix.android.sdk.internal.di.UserId
import org.matrix.android.sdk.internal.session.room.membership.RoomMemberHelper
import org.matrix.android.sdk.internal.util.fetchCopied
import org.matrix.android.sdk.internal.util.logLimit
import timber.log.Timber
import javax.inject.Inject
/**
@ -31,7 +39,8 @@ import javax.inject.Inject
* in the session DB, this class encapsulate this functionality
*/
internal class CryptoSessionInfoProvider @Inject constructor(
@SessionDatabase private val monarchy: Monarchy
@SessionDatabase private val monarchy: Monarchy,
@UserId private val myUserId: String
) {
fun isRoomEncrypted(roomId: String): Boolean {
@ -47,7 +56,7 @@ internal class CryptoSessionInfoProvider @Inject constructor(
/**
* @param allActive if true return joined as well as invited, if false, only joined
*/
fun getRoomUserIds(roomId: String, allActive: Boolean): List<String> {
fun getRoomUserIds(roomId: String, allActive: Boolean): List<String> {
var userIds: List<String> = emptyList()
monarchy.doWithRealm { realm ->
userIds = if (allActive) {
@ -58,4 +67,41 @@ internal class CryptoSessionInfoProvider @Inject constructor(
}
return userIds
}
fun getUserListForShieldComputation(roomId: String): List<String> {
var userIds: List<String> = emptyList()
monarchy.doWithRealm { realm ->
userIds = RoomMemberHelper(realm, roomId).getActiveRoomMemberIds()
}
var isDirect = false
monarchy.doWithRealm { realm ->
isDirect = RoomSummaryEntity.where(realm, roomId = roomId).findFirst()?.isDirect == true
}
return if (isDirect || userIds.size <= 2) {
userIds.filter { it != myUserId }
} else {
userIds
}
}
fun getRoomsWhereUsersAreParticipating(userList: List<String>): List<String> {
var roomIds: List<String>? = null
monarchy.doWithRealm { sessionRealm ->
roomIds = sessionRealm.where(RoomMemberSummaryEntity::class.java)
.`in`(RoomMemberSummaryEntityFields.USER_ID, userList.toTypedArray())
.distinct(RoomMemberSummaryEntityFields.ROOM_ID)
.findAll()
.map { it.roomId }
.also { Timber.d("## CrossSigning - ... impacted rooms ${it.logLimit()}") }
}
return roomIds.orEmpty()
}
fun updateShieldForRoom(roomId: String, shield: RoomEncryptionTrustLevel) {
monarchy.writeAsync { realm ->
val summary = RoomSummaryEntity.where(realm, roomId = roomId).findFirst()
summary?.roomEncryptionTrustLevel = shield
}
}
}

View file

@ -88,31 +88,17 @@ import org.matrix.android.sdk.internal.crypto.model.event.SecretSendEventContent
import org.matrix.android.sdk.internal.crypto.model.rest.DeviceInfo
import org.matrix.android.sdk.internal.crypto.model.rest.DevicesListResponse
import org.matrix.android.sdk.internal.crypto.model.rest.ForwardedRoomKeyContent
import org.matrix.android.sdk.internal.crypto.model.rest.KeysClaimResponse
import org.matrix.android.sdk.internal.crypto.model.rest.KeysQueryResponse
import org.matrix.android.sdk.internal.crypto.model.rest.KeysUploadResponse
import org.matrix.android.sdk.internal.crypto.model.rest.RestKeyInfo
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.ClaimOneTimeKeysForUsersDeviceTask
import org.matrix.android.sdk.internal.crypto.tasks.DefaultSendVerificationMessageTask
import org.matrix.android.sdk.internal.crypto.tasks.DeleteDeviceTask
import org.matrix.android.sdk.internal.crypto.tasks.DownloadKeysForUsersTask
import org.matrix.android.sdk.internal.crypto.tasks.GetDeviceInfoTask
import org.matrix.android.sdk.internal.crypto.tasks.GetDevicesTask
import org.matrix.android.sdk.internal.crypto.tasks.SendToDeviceTask
import org.matrix.android.sdk.internal.crypto.tasks.SendVerificationMessageTask
import org.matrix.android.sdk.internal.crypto.tasks.SetDeviceNameTask
import org.matrix.android.sdk.internal.crypto.tasks.UploadKeysTask
import org.matrix.android.sdk.internal.crypto.tasks.UploadSignaturesTask
import org.matrix.android.sdk.internal.crypto.tasks.UploadSigningKeysTask
import org.matrix.android.sdk.internal.crypto.verification.RustVerificationService
import org.matrix.android.sdk.internal.di.DeviceId
import org.matrix.android.sdk.internal.di.MoshiProvider
import org.matrix.android.sdk.internal.di.SessionFilesDirectory
import org.matrix.android.sdk.internal.di.UserId
import org.matrix.android.sdk.internal.extensions.foldToCallback
import org.matrix.android.sdk.internal.network.parsing.CheckNumberType
import org.matrix.android.sdk.internal.session.SessionScope
import org.matrix.android.sdk.internal.session.room.membership.LoadRoomMembersTask
import org.matrix.android.sdk.internal.task.TaskExecutor
@ -120,235 +106,13 @@ import org.matrix.android.sdk.internal.task.TaskThread
import org.matrix.android.sdk.internal.task.configureWith
import org.matrix.android.sdk.internal.task.launchToCallback
import timber.log.Timber
import uniffi.olm.OutgoingVerificationRequest
import uniffi.olm.Request
import uniffi.olm.RequestType
import uniffi.olm.SignatureUploadRequest
import uniffi.olm.UploadSigningKeysRequest
import java.io.File
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.atomic.AtomicBoolean
import javax.inject.Inject
import kotlin.math.max
internal class RequestSender @Inject constructor(
private val sendToDeviceTask: SendToDeviceTask,
private val oneTimeKeysForUsersDeviceTask: ClaimOneTimeKeysForUsersDeviceTask,
private val uploadKeysTask: UploadKeysTask,
private val downloadKeysForUsersTask: DownloadKeysForUsersTask,
private val signaturesUploadTask: UploadSignaturesTask,
private val sendVerificationMessageTask: Lazy<DefaultSendVerificationMessageTask>,
private val uploadSigningKeysTask: UploadSigningKeysTask,
private val getKeysBackupLastVersionTask: GetKeysBackupLastVersionTask,
private val getKeysBackupVersionTask: GetKeysBackupVersionTask,
private val deleteBackupTask: DeleteBackupTask,
private val createKeysBackupVersionTask: CreateKeysBackupVersionTask,
private val backupRoomKeysTask: StoreSessionsDataTask,
private val updateKeysBackupVersionTask: UpdateKeysBackupVersionTask,
private val getSessionsDataTask: GetSessionsDataTask,
private val getRoomSessionsDataTask: GetRoomSessionsDataTask,
private val getRoomSessionDataTask: GetRoomSessionDataTask,
) {
companion object {
const val REQUEST_RETRY_COUNT = 3
}
suspend fun claimKeys(request: Request.KeysClaim): String {
val claimParams = ClaimOneTimeKeysForUsersDeviceTask.Params(request.oneTimeKeys)
val response = oneTimeKeysForUsersDeviceTask.executeRetry(claimParams, REQUEST_RETRY_COUNT)
val adapter = MoshiProvider
.providesMoshi()
.adapter(KeysClaimResponse::class.java)
return adapter.toJson(response)!!
}
suspend fun queryKeys(request: Request.KeysQuery): String {
val params = DownloadKeysForUsersTask.Params(request.users, null)
val response = downloadKeysForUsersTask.executeRetry(params, REQUEST_RETRY_COUNT)
val adapter = MoshiProvider.providesMoshi().adapter(KeysQueryResponse::class.java)
return adapter.toJson(response)!!
}
suspend fun uploadKeys(request: Request.KeysUpload): String {
val body = MoshiProvider.providesMoshi().adapter<JsonDict>(Map::class.java).fromJson(request.body)!!
val params = UploadKeysTask.Params(body)
val response = uploadKeysTask.executeRetry(params, REQUEST_RETRY_COUNT)
val adapter = MoshiProvider.providesMoshi().adapter(KeysUploadResponse::class.java)
return adapter.toJson(response)!!
}
suspend fun sendVerificationRequest(request: OutgoingVerificationRequest) {
when (request) {
is OutgoingVerificationRequest.InRoom -> sendRoomMessage(request)
is OutgoingVerificationRequest.ToDevice -> sendToDevice(request)
}
}
suspend fun sendRoomMessage(request: OutgoingVerificationRequest.InRoom): String {
return sendRoomMessage(request.eventType, request.roomId, request.content, request.requestId)
}
suspend fun sendRoomMessage(request: Request.RoomMessage): String {
return sendRoomMessage(request.eventType, request.roomId, request.content, request.requestId)
}
suspend fun sendRoomMessage(eventType: String, roomId: String, content: String, transactionId: String): String {
val adapter = MoshiProvider.providesMoshi().adapter<Content>(Map::class.java)
val jsonContent = adapter.fromJson(content)
val event = Event(eventType, transactionId, jsonContent, roomId = roomId)
val params = SendVerificationMessageTask.Params(event)
return this.sendVerificationMessageTask.get().executeRetry(params, REQUEST_RETRY_COUNT)
}
suspend fun sendSignatureUpload(request: Request.SignatureUpload) {
sendSignatureUpload(request.body)
}
suspend fun sendSignatureUpload(request: SignatureUploadRequest) {
sendSignatureUpload(request.body)
}
private suspend fun sendSignatureUpload(body: String) {
val adapter = MoshiProvider.providesMoshi().adapter<Map<String, Map<String, Any>>>(Map::class.java)
val signatures = adapter.fromJson(body)!!
val params = UploadSignaturesTask.Params(signatures)
this.signaturesUploadTask.executeRetry(params, REQUEST_RETRY_COUNT)
}
suspend fun uploadCrossSigningKeys(
request: UploadSigningKeysRequest,
interactiveAuthInterceptor: UserInteractiveAuthInterceptor?
) {
val adapter = MoshiProvider.providesMoshi().adapter(RestKeyInfo::class.java)
val masterKey = adapter.fromJson(request.masterKey)!!.toCryptoModel()
val selfSigningKey = adapter.fromJson(request.selfSigningKey)!!.toCryptoModel()
val userSigningKey = adapter.fromJson(request.userSigningKey)!!.toCryptoModel()
val uploadSigningKeysParams = UploadSigningKeysTask.Params(
masterKey,
userSigningKey,
selfSigningKey,
null
)
try {
uploadSigningKeysTask.execute(uploadSigningKeysParams)
} catch (failure: Throwable) {
if (interactiveAuthInterceptor == null
|| !handleUIA(
failure = failure,
interceptor = interactiveAuthInterceptor,
retryBlock = { authUpdate ->
uploadSigningKeysTask.executeRetry(
uploadSigningKeysParams.copy(userAuthParam = authUpdate),
REQUEST_RETRY_COUNT
)
}
)
) {
Timber.d("## UIA: propagate failure")
throw failure
}
}
}
suspend fun sendToDevice(request: Request.ToDevice) {
sendToDevice(request.eventType, request.body, request.requestId)
}
suspend fun sendToDevice(request: OutgoingVerificationRequest.ToDevice) {
sendToDevice(request.eventType, request.body, request.requestId)
}
suspend fun sendToDevice(eventType: String, body: String, transactionId: String) {
val adapter = MoshiProvider
.providesMoshi()
.newBuilder()
.add(CheckNumberType.JSON_ADAPTER_FACTORY)
.build()
.adapter<Map<String, HashMap<String, Any>>>(Map::class.java)
val jsonBody = adapter.fromJson(body)!!
val userMap = MXUsersDevicesMap<Any>()
userMap.join(jsonBody)
val sendToDeviceParams = SendToDeviceTask.Params(eventType, userMap, transactionId)
sendToDeviceTask.executeRetry(sendToDeviceParams, REQUEST_RETRY_COUNT)
}
suspend fun getKeyBackupVersion(version: String? = null): KeysVersionResult? {
return try {
if (version != null) {
getKeysBackupVersionTask.execute(version)
} else {
getKeysBackupLastVersionTask.execute(Unit)
}
} catch (failure: Throwable) {
if (failure is Failure.ServerError
&& failure.error.code == MatrixError.M_NOT_FOUND) {
null
} else {
throw failure
}
}
}
suspend fun createKeyBackup(body: CreateKeysBackupVersionBody): KeysVersion {
return createKeysBackupVersionTask.execute(body)
}
suspend fun deleteKeyBackup(version: String) {
val params = DeleteBackupTask.Params(version)
deleteBackupTask.execute(params)
}
suspend fun backupRoomKeys(request: Request.KeysBackup): String {
val adapter = MoshiProvider
.providesMoshi()
.newBuilder()
.add(CheckNumberType.JSON_ADAPTER_FACTORY)
.build()
.adapter<MutableMap<String, RoomKeysBackupData>>(
Types.newParameterizedType(
Map::class.java,
String::class.java,
RoomKeysBackupData::class.java
))
val keys = adapter.fromJson(request.rooms)!!
val params = StoreSessionsDataTask.Params(request.version, KeysBackupData(keys))
val response = backupRoomKeysTask.executeRetry(params, REQUEST_RETRY_COUNT)
val responseAdapter = MoshiProvider.providesMoshi().adapter(BackupKeysResult::class.java)
return responseAdapter.toJson(response)!!
}
suspend fun updateBackup(keysBackupVersion: KeysVersionResult, body: UpdateKeysBackupVersionBody) {
val params = UpdateKeysBackupVersionTask.Params(keysBackupVersion.version, body)
updateKeysBackupVersionTask.executeRetry(params, REQUEST_RETRY_COUNT)
}
suspend fun downloadBackedUpKeys(version: String, roomId: String, sessionId: String): KeysBackupData {
val data = getRoomSessionDataTask.execute(GetRoomSessionDataTask.Params(roomId, sessionId, version))
return KeysBackupData(mutableMapOf(
roomId to RoomKeysBackupData(mutableMapOf(
sessionId to data
))
))
}
suspend fun downloadBackedUpKeys(version: String, roomId: String): KeysBackupData {
val data = getRoomSessionsDataTask.execute(GetRoomSessionsDataTask.Params(roomId, version))
// Convert to KeysBackupData
return KeysBackupData(mutableMapOf(roomId to data))
}
suspend fun downloadBackedUpKeys(version: String): KeysBackupData {
return getSessionsDataTask.execute(GetSessionsDataTask.Params(version))
}
}
/**
* A `CryptoService` class instance manages the end-to-end crypto for a session.
*
@ -365,8 +129,10 @@ internal class DefaultCryptoService @Inject constructor(
private val userId: String,
@DeviceId
private val deviceId: String?,
@SessionFilesDirectory
private val dataDir: File,
// @SessionId
// private val sessionId: String,
// @SessionFilesDirectory
// private val dataDir: File,
// the crypto store
private val cryptoStore: IMXCryptoStore,
// Set of parameters used to configure/customize the end-to-end crypto.
@ -384,18 +150,20 @@ internal class DefaultCryptoService @Inject constructor(
private val taskExecutor: TaskExecutor,
private val cryptoCoroutineScope: CoroutineScope,
private val sender: RequestSender,
private val crossSigningService: CrossSigningService,
private val verificationService: RustVerificationService,
private val olmMachineProvider: OlmMachineProvider
) : CryptoService {
private val isStarting = AtomicBoolean(false)
private val isStarted = AtomicBoolean(false)
private var olmMachine: OlmMachine? = null
private val olmMachine by lazy { olmMachineProvider.olmMachine }
// The verification service.
private var verificationService: RustVerificationService? = null
// private var verificationService: RustVerificationService? = null
// The cross signing service.
private var crossSigningService: RustCrossSigningService? = null
// private val deviceObserver: DeviceUpdateObserver = DeviceUpdateObserver()
// The key backup service.
private var keysBackupService: RustKeyBackupService? = null
@ -410,7 +178,7 @@ internal class DefaultCryptoService @Inject constructor(
// TODO does this need to be concurrent?
private val newSessionListeners = ArrayList<NewSessionListener>()
suspend fun onStateEvent(roomId: String, event: Event) {
fun onStateEvent(roomId: String, event: Event) {
when (event.getClearType()) {
EventType.STATE_ROOM_ENCRYPTION -> onRoomEncryptionEvent(roomId, event)
EventType.STATE_ROOM_MEMBER -> onRoomMembershipEvent(roomId, event)
@ -418,12 +186,14 @@ internal class DefaultCryptoService @Inject constructor(
}
}
suspend fun onLiveEvent(roomId: String, event: Event) {
fun onLiveEvent(roomId: String, event: Event) {
when (event.getClearType()) {
EventType.STATE_ROOM_ENCRYPTION -> onRoomEncryptionEvent(roomId, event)
EventType.STATE_ROOM_MEMBER -> onRoomMembershipEvent(roomId, event)
EventType.STATE_ROOM_HISTORY_VISIBILITY -> onRoomHistoryVisibilityEvent(roomId, event)
else -> this.verificationService?.onEvent(event)
else -> cryptoCoroutineScope.launch {
this@DefaultCryptoService.verificationService?.onEvent(event)
}
}
}
@ -463,7 +233,7 @@ internal class DefaultCryptoService @Inject constructor(
}
override fun getMyDevice(): CryptoDeviceInfo {
return runBlocking { olmMachine!!.ownDevice() }
return runBlocking { olmMachine.ownDevice() }
}
override fun fetchDevicesList(callback: MatrixCallback<DevicesListResponse>) {
@ -565,14 +335,10 @@ internal class DefaultCryptoService @Inject constructor(
try {
setRustLogger()
val machine = OlmMachine(userId, deviceId!!, dataDir, deviceObserver, sender)
olmMachine = machine
verificationService = RustVerificationService(machine)
crossSigningService = RustCrossSigningService(machine)
keysBackupService = RustKeyBackupService(machine, sender, coroutineDispatchers, cryptoCoroutineScope)
Timber.v(
"## CRYPTO | Successfully started up an Olm machine for " +
"${userId}, ${deviceId}, identity keys: ${this.olmMachine?.identityKeys()}")
"${userId}, ${deviceId}, identity keys: ${this.olmMachine.identityKeys()}")
} catch (throwable: Throwable) {
Timber.v("Failed create an Olm machine: $throwable")
}
@ -614,38 +380,9 @@ internal class DefaultCryptoService @Inject constructor(
/**
* @return the VerificationService
*/
override fun verificationService(): VerificationService {
// TODO yet another problem because the CryptoService is started in the
// sync loop
//
// The `KeyRequestHandler` and `IncomingVerificationHandler` want to add
// listeners to the verification service, they are initialized in the
// `ActiveSessionHolder` class in the `setActiveSession()` method. In
// the `setActiveSession()` method we call the `start()` method of the
// handlers without first calling the `start()` method of the
// `DefaultCryptoService`.
//
// The start method of the crypto service isn't part of the
// `CryptoService` interface so it currently can't be called there. I'm
// inclined to believe that it should be, and that it should be
// initialized before anything else tries to do something with it.
//
// Let's initialize here as a workaround until we figure out if the
// above conclusion is correct.
if (verificationService == null) {
internalStart()
}
override fun verificationService() = verificationService
return verificationService!!
}
override fun crossSigningService(): CrossSigningService {
if (crossSigningService == null) {
internalStart()
}
return crossSigningService!!
}
override fun crossSigningService() = crossSigningService
/**
* A sync response has been received
@ -685,7 +422,7 @@ internal class DefaultCryptoService @Inject constructor(
override fun getDeviceInfo(userId: String, deviceId: String?): CryptoDeviceInfo? {
return if (userId.isNotEmpty() && !deviceId.isNullOrEmpty()) {
runBlocking {
this@DefaultCryptoService.olmMachine?.getCryptoDeviceInfo(userId, deviceId)
this@DefaultCryptoService.olmMachine.getCryptoDeviceInfo(userId, deviceId)
}
} else {
null
@ -694,7 +431,7 @@ internal class DefaultCryptoService @Inject constructor(
override fun getCryptoDeviceInfo(userId: String): List<CryptoDeviceInfo> {
return runBlocking {
this@DefaultCryptoService.olmMachine?.getCryptoDeviceInfo(userId) ?: listOf()
this@DefaultCryptoService.olmMachine.getCryptoDeviceInfo(userId) ?: listOf()
}
}
@ -704,7 +441,7 @@ internal class DefaultCryptoService @Inject constructor(
override fun getLiveCryptoDeviceInfo(userIds: List<String>): LiveData<List<CryptoDeviceInfo>> {
return runBlocking {
this@DefaultCryptoService.olmMachine?.getLiveDevices(userIds) ?: LiveDevice(userIds, deviceObserver)
this@DefaultCryptoService.olmMachine.getLiveDevices(userIds) //?: LiveDevice(userIds, deviceObserver)
}
}
@ -755,7 +492,7 @@ internal class DefaultCryptoService @Inject constructor(
Timber.v("Enabling encryption in $roomId for the first time; invalidating device lists for all users therein")
val userIds = ArrayList(membersId)
olmMachine!!.updateTrackedUsers(userIds)
olmMachine.updateTrackedUsers(userIds)
}
return true
@ -833,7 +570,7 @@ internal class DefaultCryptoService @Inject constructor(
}
override fun discardOutboundSession(roomId: String) {
olmMachine?.discardRoomKey(roomId)
olmMachine.discardRoomKey(roomId)
}
/**
@ -846,7 +583,7 @@ internal class DefaultCryptoService @Inject constructor(
@Throws(MXCryptoError::class)
override fun decryptEvent(event: Event, timeline: String): MXEventDecryptionResult {
return runBlocking {
olmMachine!!.decryptRoomEvent(event)
olmMachine.decryptRoomEvent(event)
}
}
@ -873,20 +610,29 @@ internal class DefaultCryptoService @Inject constructor(
Timber.w("Invalid encryption event")
return
}
// Do not load members here, would defeat lazy loading
cryptoCoroutineScope.launch(coroutineDispatchers.crypto) {
val params = LoadRoomMembersTask.Params(roomId)
try {
loadRoomMembersTask.execute(params)
} catch (throwable: Throwable) {
Timber.e(throwable, "## CRYPTO | onRoomEncryptionEvent ERROR FAILED TO SETUP CRYPTO ")
} finally {
val userIds = getRoomUserIds(roomId)
olmMachine!!.updateTrackedUsers(userIds)
setEncryptionInRoom(roomId, event.content?.get("algorithm")?.toString(), userIds)
}
// val params = LoadRoomMembersTask.Params(roomId)
// try {
// loadRoomMembersTask.execute(params)
// } catch (throwable: Throwable) {
// Timber.e(throwable, "## CRYPTO | onRoomEncryptionEvent ERROR FAILED TO SETUP CRYPTO ")
// } finally {
val userIds = getRoomUserIds(roomId)
olmMachine.updateTrackedUsers(userIds)
setEncryptionInRoom(roomId, event.content?.get("algorithm")?.toString(), userIds)
// }
}
}
override fun onE2ERoomMemberLoadedFromServer(roomId: String) {
cryptoCoroutineScope.launch(coroutineDispatchers.crypto) {
val userIds = getRoomUserIds(roomId)
// Because of LL we might want to update tracked users
olmMachine.updateTrackedUsers(userIds)
}
}
private fun getRoomUserIds(roomId: String): List<String> {
val encryptForInvitedMembers = isEncryptionEnabledForInvitedUser() &&
shouldEncryptForInvitedMembers(roomId)
@ -898,7 +644,7 @@ internal class DefaultCryptoService @Inject constructor(
*
* @param event the membership event causing the change
*/
private suspend fun onRoomMembershipEvent(roomId: String, event: Event) {
private fun onRoomMembershipEvent(roomId: String, event: Event) {
// We only care about the memberships if this room is encrypted
if (isRoomEncrypted(roomId)) {
return
@ -909,7 +655,9 @@ internal class DefaultCryptoService @Inject constructor(
val membership = roomMember?.membership
if (membership == Membership.JOIN) {
// make sure we are tracking the deviceList for this user.
olmMachine!!.updateTrackedUsers(listOf(userId))
cryptoCoroutineScope.launch {
olmMachine.updateTrackedUsers(listOf(userId))
}
} else if (membership == Membership.INVITE
&& shouldEncryptForInvitedMembers(roomId)
&& isEncryptionEnabledForInvitedUser()) {
@ -918,7 +666,11 @@ internal class DefaultCryptoService @Inject constructor(
// know what other servers are in the room at the time they've been invited.
// They therefore will not send device updates if a user logs in whilst
// their state is invite.
olmMachine!!.updateTrackedUsers(listOf(userId))
cryptoCoroutineScope.launch {
olmMachine.updateTrackedUsers(listOf(userId))
}
} else {
// nop
}
}
}
@ -952,7 +704,7 @@ internal class DefaultCryptoService @Inject constructor(
deviceChanges: DeviceListResponse?,
keyCounts: DeviceOneTimeKeysCountSyncResponse?) {
// Decrypt and handle our to-device events
val toDeviceEvents = this.olmMachine!!.receiveSyncChanges(toDevice, deviceChanges, keyCounts)
val toDeviceEvents = this.olmMachine.receiveSyncChanges(toDevice, deviceChanges, keyCounts)
// Notify the our listeners about room keys so decryption is retried.
if (toDeviceEvents.events != null) {
@ -981,7 +733,7 @@ internal class DefaultCryptoService @Inject constructor(
this.keysBackupService?.onSecretKeyGossip(secretContent.secretValue)
}
else -> {
this.verificationService?.onEvent(event)
this.verificationService.onEvent(event)
}
}
}
@ -990,7 +742,7 @@ internal class DefaultCryptoService @Inject constructor(
private suspend fun preshareRoomKey(roomId: String, roomMembers: List<String>) {
keyClaimLock.withLock {
val request = this.olmMachine!!.getMissingSessions(roomMembers)
val request = this.olmMachine.getMissingSessions(roomMembers)
// This request can only be a keys claim request.
if (request != null) {
when (request) {
@ -1008,7 +760,7 @@ internal class DefaultCryptoService @Inject constructor(
keyShareLock.withLock {
coroutineScope {
this@DefaultCryptoService.olmMachine!!.shareRoomKey(roomId, roomMembers).map {
this@DefaultCryptoService.olmMachine.shareRoomKey(roomId, roomMembers).map {
when (it) {
is Request.ToDevice -> {
sharedKey = true
@ -1035,18 +787,28 @@ internal class DefaultCryptoService @Inject constructor(
}
private suspend fun encrypt(roomId: String, eventType: String, content: Content): Content {
return olmMachine!!.encrypt(roomId, eventType, content)
return olmMachine.encrypt(roomId, eventType, content)
}
private suspend fun uploadKeys(request: Request.KeysUpload) {
val response = this.sender.uploadKeys(request)
this.olmMachine!!.markRequestAsSent(request.requestId, RequestType.KEYS_UPLOAD, response)
this.olmMachine.markRequestAsSent(request.requestId, RequestType.KEYS_UPLOAD, response)
}
private suspend fun queryKeys(request: Request.KeysQuery) {
try {
val response = this.sender.queryKeys(request)
this.olmMachine!!.markRequestAsSent(request.requestId, RequestType.KEYS_QUERY, response)
this.olmMachine.markRequestAsSent(request.requestId, RequestType.KEYS_QUERY, response)
// Update the shields!
cryptoCoroutineScope.launch {
cryptoSessionInfoProvider.getRoomsWhereUsersAreParticipating(request.users).forEach { roomId ->
val userGroup = cryptoSessionInfoProvider.getUserListForShieldComputation(roomId)
val shield = crossSigningService.shieldForGroup(userGroup)
cryptoSessionInfoProvider.updateShieldForRoom(roomId, shield)
}
}
} catch (throwable: Throwable) {
Timber.e(throwable, "## CRYPTO | doKeyDownloadForUsers(): error")
}
@ -1054,23 +816,23 @@ internal class DefaultCryptoService @Inject constructor(
private suspend fun sendToDevice(request: Request.ToDevice) {
this.sender.sendToDevice(request)
olmMachine!!.markRequestAsSent(request.requestId, RequestType.TO_DEVICE, "{}")
olmMachine.markRequestAsSent(request.requestId, RequestType.TO_DEVICE, "{}")
}
private suspend fun claimKeys(request: Request.KeysClaim) {
val response = this.sender.claimKeys(request)
olmMachine!!.markRequestAsSent(request.requestId, RequestType.KEYS_CLAIM, response)
olmMachine.markRequestAsSent(request.requestId, RequestType.KEYS_CLAIM, response)
}
private suspend fun signatureUpload(request: Request.SignatureUpload) {
this.sender.sendSignatureUpload(request)
olmMachine!!.markRequestAsSent(request.requestId, RequestType.SIGNATURE_UPLOAD, "{}")
olmMachine.markRequestAsSent(request.requestId, RequestType.SIGNATURE_UPLOAD, "{}")
}
private suspend fun sendOutgoingRequests() {
outgoingRequestsLock.withLock {
coroutineScope {
olmMachine!!.outgoingRequests().map {
olmMachine.outgoingRequests().map {
when (it) {
is Request.KeysUpload -> {
async {
@ -1122,7 +884,7 @@ internal class DefaultCryptoService @Inject constructor(
*/
override suspend fun exportRoomKeys(password: String): ByteArray {
val iterationCount = max(10000, MXMegolmExportEncryption.DEFAULT_ITERATION_COUNT)
return olmMachine!!.exportKeys(password, iterationCount)
return olmMachine.exportKeys(password, iterationCount)
}
/**
@ -1234,7 +996,7 @@ internal class DefaultCryptoService @Inject constructor(
*/
override fun reRequestRoomKeyForEvent(event: Event) {
cryptoCoroutineScope.launch(coroutineDispatchers.crypto) {
val requestPair = olmMachine!!.requestRoomKey(event)
val requestPair = olmMachine.requestRoomKey(event)
val cancellation = requestPair.cancellation
val request = requestPair.keyRequest
@ -1279,9 +1041,9 @@ internal class DefaultCryptoService @Inject constructor(
if (forceDownload) {
// TODO replicate the logic from the device list manager
// where we would download the fresh info from the server.
this@DefaultCryptoService.olmMachine?.getUserDevicesMap(userIds) ?: MXUsersDevicesMap()
this@DefaultCryptoService.olmMachine.getUserDevicesMap(userIds) // ?: MXUsersDevicesMap()
} else {
this@DefaultCryptoService.olmMachine?.getUserDevicesMap(userIds) ?: MXUsersDevicesMap()
this@DefaultCryptoService.olmMachine.getUserDevicesMap(userIds) //?: MXUsersDevicesMap()
}
}.foldToCallback(callback)
}

View file

@ -39,7 +39,7 @@ internal class Device(
private val machine: OlmMachine,
private var inner: InnerDevice,
private val sender: RequestSender,
private val listeners: ArrayList<VerificationService.Listener>,
private val listeners: ArrayList<VerificationService.Listener>
) {
@Throws(CryptoStoreException::class)
private suspend fun refreshData() {

View file

@ -35,6 +35,7 @@ import timber.log.Timber
import javax.inject.Inject
// Legacy name: MXDeviceList
@Deprecated("In favor of rust olmMachine")
@SessionScope
internal class DeviceListManager @Inject constructor(private val cryptoStore: IMXCryptoStore,
private val olmDevice: MXOlmDevice,

View file

@ -22,6 +22,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
import org.matrix.android.sdk.api.extensions.orFalse
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.MXCrossSigningInfo
@ -99,7 +100,7 @@ internal class LiveDevice(
internal class LiveUserIdentity(
internal var userId: String,
private var observer: UserIdentityUpdateObserver,
private var observer: UserIdentityUpdateObserver
) : MutableLiveData<Optional<MXCrossSigningInfo>>() {
override fun onActive() {
observer.addUserIdentityUpdateListener(this)
@ -529,8 +530,13 @@ internal class OlmMachine(
return when (identity) {
is RustUserIdentity.Other -> {
val masterKey = adapter.fromJson(identity.masterKey)!!.toCryptoModel()
val selfSigningKey = adapter.fromJson(identity.selfSigningKey)!!.toCryptoModel()
val verified = this.inner().isIdentityVerified(userId)
val masterKey = adapter.fromJson(identity.masterKey)!!.toCryptoModel().apply {
trustLevel = DeviceTrustLevel(verified, verified)
}
val selfSigningKey = adapter.fromJson(identity.selfSigningKey)!!.toCryptoModel().apply {
trustLevel = DeviceTrustLevel(verified, verified)
}
UserIdentity(identity.userId, masterKey, selfSigningKey, this, this.requestSender)
}

View file

@ -0,0 +1,37 @@
/*
* Copyright (c) 2021 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.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 java.io.File
import javax.inject.Inject
@SessionScope
internal class OlmMachineProvider @Inject constructor(
@UserId private val userId: String,
@DeviceId private val deviceId: String?,
@SessionFilesDirectory private val dataDir: File,
requestSender: RequestSender
) {
private val deviceObserver: DeviceUpdateObserver = DeviceUpdateObserver()
var olmMachine: OlmMachine = OlmMachine(userId, deviceId!!, dataDir, deviceObserver, requestSender)
}

View file

@ -37,7 +37,7 @@ internal class QrCodeVerification(
private var request: VerificationRequest,
private var inner: QrCode?,
private val sender: RequestSender,
listeners: ArrayList<VerificationService.Listener>,
listeners: ArrayList<VerificationService.Listener>
) : QrCodeVerificationTransaction {
private val dispatcher = UpdateDispatcher(listeners)

View file

@ -0,0 +1,282 @@
/*
* Copyright (c) 2021 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 com.squareup.moshi.Types
import dagger.Lazy
import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
import org.matrix.android.sdk.api.failure.Failure
import org.matrix.android.sdk.api.failure.MatrixError
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.util.JsonDict
import org.matrix.android.sdk.internal.auth.registration.handleUIA
import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.BackupKeysResult
import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.CreateKeysBackupVersionBody
import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeysBackupData
import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeysVersion
import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeysVersionResult
import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.RoomKeysBackupData
import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.UpdateKeysBackupVersionBody
import org.matrix.android.sdk.internal.crypto.keysbackup.tasks.CreateKeysBackupVersionTask
import org.matrix.android.sdk.internal.crypto.keysbackup.tasks.DeleteBackupTask
import org.matrix.android.sdk.internal.crypto.keysbackup.tasks.GetKeysBackupLastVersionTask
import org.matrix.android.sdk.internal.crypto.keysbackup.tasks.GetKeysBackupVersionTask
import org.matrix.android.sdk.internal.crypto.keysbackup.tasks.GetRoomSessionDataTask
import org.matrix.android.sdk.internal.crypto.keysbackup.tasks.GetRoomSessionsDataTask
import org.matrix.android.sdk.internal.crypto.keysbackup.tasks.GetSessionsDataTask
import org.matrix.android.sdk.internal.crypto.keysbackup.tasks.StoreSessionsDataTask
import org.matrix.android.sdk.internal.crypto.keysbackup.tasks.UpdateKeysBackupVersionTask
import org.matrix.android.sdk.internal.crypto.model.MXUsersDevicesMap
import org.matrix.android.sdk.internal.crypto.model.rest.KeysClaimResponse
import org.matrix.android.sdk.internal.crypto.model.rest.KeysQueryResponse
import org.matrix.android.sdk.internal.crypto.model.rest.KeysUploadResponse
import org.matrix.android.sdk.internal.crypto.model.rest.RestKeyInfo
import org.matrix.android.sdk.internal.crypto.tasks.ClaimOneTimeKeysForUsersDeviceTask
import org.matrix.android.sdk.internal.crypto.tasks.DefaultSendVerificationMessageTask
import org.matrix.android.sdk.internal.crypto.tasks.DownloadKeysForUsersTask
import org.matrix.android.sdk.internal.crypto.tasks.SendToDeviceTask
import org.matrix.android.sdk.internal.crypto.tasks.SendVerificationMessageTask
import org.matrix.android.sdk.internal.crypto.tasks.UploadKeysTask
import org.matrix.android.sdk.internal.crypto.tasks.UploadSignaturesTask
import org.matrix.android.sdk.internal.crypto.tasks.UploadSigningKeysTask
import org.matrix.android.sdk.internal.di.MoshiProvider
import org.matrix.android.sdk.internal.network.parsing.CheckNumberType
import timber.log.Timber
import uniffi.olm.OutgoingVerificationRequest
import uniffi.olm.Request
import uniffi.olm.SignatureUploadRequest
import uniffi.olm.UploadSigningKeysRequest
import javax.inject.Inject
internal class RequestSender @Inject constructor(
private val sendToDeviceTask: SendToDeviceTask,
private val oneTimeKeysForUsersDeviceTask: ClaimOneTimeKeysForUsersDeviceTask,
private val uploadKeysTask: UploadKeysTask,
private val downloadKeysForUsersTask: DownloadKeysForUsersTask,
private val signaturesUploadTask: UploadSignaturesTask,
private val sendVerificationMessageTask: Lazy<DefaultSendVerificationMessageTask>,
private val uploadSigningKeysTask: UploadSigningKeysTask,
private val getKeysBackupLastVersionTask: GetKeysBackupLastVersionTask,
private val getKeysBackupVersionTask: GetKeysBackupVersionTask,
private val deleteBackupTask: DeleteBackupTask,
private val createKeysBackupVersionTask: CreateKeysBackupVersionTask,
private val backupRoomKeysTask: StoreSessionsDataTask,
private val updateKeysBackupVersionTask: UpdateKeysBackupVersionTask,
private val getSessionsDataTask: GetSessionsDataTask,
private val getRoomSessionsDataTask: GetRoomSessionsDataTask,
private val getRoomSessionDataTask: GetRoomSessionDataTask,
) {
companion object {
const val REQUEST_RETRY_COUNT = 3
}
suspend fun claimKeys(request: Request.KeysClaim): String {
val claimParams = ClaimOneTimeKeysForUsersDeviceTask.Params(request.oneTimeKeys)
val response = oneTimeKeysForUsersDeviceTask.executeRetry(claimParams, REQUEST_RETRY_COUNT)
val adapter = MoshiProvider
.providesMoshi()
.adapter(KeysClaimResponse::class.java)
return adapter.toJson(response)!!
}
suspend fun queryKeys(request: Request.KeysQuery): String {
val params = DownloadKeysForUsersTask.Params(request.users, null)
val response = downloadKeysForUsersTask.executeRetry(params, REQUEST_RETRY_COUNT)
val adapter = MoshiProvider.providesMoshi().adapter(KeysQueryResponse::class.java)
return adapter.toJson(response)!!
}
suspend fun uploadKeys(request: Request.KeysUpload): String {
val body = MoshiProvider.providesMoshi().adapter<JsonDict>(Map::class.java).fromJson(request.body)!!
val params = UploadKeysTask.Params(body)
val response = uploadKeysTask.executeRetry(params, REQUEST_RETRY_COUNT)
val adapter = MoshiProvider.providesMoshi().adapter(KeysUploadResponse::class.java)
return adapter.toJson(response)!!
}
suspend fun sendVerificationRequest(request: OutgoingVerificationRequest) {
when (request) {
is OutgoingVerificationRequest.InRoom -> sendRoomMessage(request)
is OutgoingVerificationRequest.ToDevice -> sendToDevice(request)
}
}
suspend fun sendRoomMessage(request: OutgoingVerificationRequest.InRoom): String {
return sendRoomMessage(request.eventType, request.roomId, request.content, request.requestId)
}
suspend fun sendRoomMessage(request: Request.RoomMessage): String {
return sendRoomMessage(request.eventType, request.roomId, request.content, request.requestId)
}
suspend fun sendRoomMessage(eventType: String, roomId: String, content: String, transactionId: String): String {
val adapter = MoshiProvider.providesMoshi().adapter<Content>(Map::class.java)
val jsonContent = adapter.fromJson(content)
val event = Event(eventType, transactionId, jsonContent, roomId = roomId)
val params = SendVerificationMessageTask.Params(event)
return this.sendVerificationMessageTask.get().executeRetry(params, REQUEST_RETRY_COUNT)
}
suspend fun sendSignatureUpload(request: Request.SignatureUpload) {
sendSignatureUpload(request.body)
}
suspend fun sendSignatureUpload(request: SignatureUploadRequest) {
sendSignatureUpload(request.body)
}
private suspend fun sendSignatureUpload(body: String) {
val adapter = MoshiProvider.providesMoshi().adapter<Map<String, Map<String, Any>>>(Map::class.java)
val signatures = adapter.fromJson(body)!!
val params = UploadSignaturesTask.Params(signatures)
this.signaturesUploadTask.executeRetry(params, REQUEST_RETRY_COUNT)
}
suspend fun uploadCrossSigningKeys(
request: UploadSigningKeysRequest,
interactiveAuthInterceptor: UserInteractiveAuthInterceptor?
) {
val adapter = MoshiProvider.providesMoshi().adapter(RestKeyInfo::class.java)
val masterKey = adapter.fromJson(request.masterKey)!!.toCryptoModel()
val selfSigningKey = adapter.fromJson(request.selfSigningKey)!!.toCryptoModel()
val userSigningKey = adapter.fromJson(request.userSigningKey)!!.toCryptoModel()
val uploadSigningKeysParams = UploadSigningKeysTask.Params(
masterKey,
userSigningKey,
selfSigningKey,
null
)
try {
uploadSigningKeysTask.execute(uploadSigningKeysParams)
} catch (failure: Throwable) {
if (interactiveAuthInterceptor == null
|| !handleUIA(
failure = failure,
interceptor = interactiveAuthInterceptor,
retryBlock = { authUpdate ->
uploadSigningKeysTask.executeRetry(
uploadSigningKeysParams.copy(userAuthParam = authUpdate),
REQUEST_RETRY_COUNT
)
}
)
) {
Timber.d("## UIA: propagate failure")
throw failure
}
}
}
suspend fun sendToDevice(request: Request.ToDevice) {
sendToDevice(request.eventType, request.body, request.requestId)
}
suspend fun sendToDevice(request: OutgoingVerificationRequest.ToDevice) {
sendToDevice(request.eventType, request.body, request.requestId)
}
suspend fun sendToDevice(eventType: String, body: String, transactionId: String) {
val adapter = MoshiProvider
.providesMoshi()
.newBuilder()
.add(CheckNumberType.JSON_ADAPTER_FACTORY)
.build()
.adapter<Map<String, HashMap<String, Any>>>(Map::class.java)
val jsonBody = adapter.fromJson(body)!!
val userMap = MXUsersDevicesMap<Any>()
userMap.join(jsonBody)
val sendToDeviceParams = SendToDeviceTask.Params(eventType, userMap, transactionId)
sendToDeviceTask.executeRetry(sendToDeviceParams, REQUEST_RETRY_COUNT)
}
suspend fun getKeyBackupVersion(version: String? = null): KeysVersionResult? {
return try {
if (version != null) {
getKeysBackupVersionTask.execute(version)
} else {
getKeysBackupLastVersionTask.execute(Unit)
}
} catch (failure: Throwable) {
if (failure is Failure.ServerError
&& failure.error.code == MatrixError.M_NOT_FOUND) {
null
} else {
throw failure
}
}
}
suspend fun createKeyBackup(body: CreateKeysBackupVersionBody): KeysVersion {
return createKeysBackupVersionTask.execute(body)
}
suspend fun deleteKeyBackup(version: String) {
val params = DeleteBackupTask.Params(version)
deleteBackupTask.execute(params)
}
suspend fun backupRoomKeys(request: Request.KeysBackup): String {
val adapter = MoshiProvider
.providesMoshi()
.newBuilder()
.add(CheckNumberType.JSON_ADAPTER_FACTORY)
.build()
.adapter<MutableMap<String, RoomKeysBackupData>>(
Types.newParameterizedType(
Map::class.java,
String::class.java,
RoomKeysBackupData::class.java
))
val keys = adapter.fromJson(request.rooms)!!
val params = StoreSessionsDataTask.Params(request.version, KeysBackupData(keys))
val response = backupRoomKeysTask.executeRetry(params, REQUEST_RETRY_COUNT)
val responseAdapter = MoshiProvider.providesMoshi().adapter(BackupKeysResult::class.java)
return responseAdapter.toJson(response)!!
}
suspend fun updateBackup(keysBackupVersion: KeysVersionResult, body: UpdateKeysBackupVersionBody) {
val params = UpdateKeysBackupVersionTask.Params(keysBackupVersion.version, body)
updateKeysBackupVersionTask.executeRetry(params, REQUEST_RETRY_COUNT)
}
suspend fun downloadBackedUpKeys(version: String, roomId: String, sessionId: String): KeysBackupData {
val data = getRoomSessionDataTask.execute(GetRoomSessionDataTask.Params(roomId, sessionId, version))
return KeysBackupData(mutableMapOf(
roomId to RoomKeysBackupData(mutableMapOf(
sessionId to data
))
))
}
suspend fun downloadBackedUpKeys(version: String, roomId: String): KeysBackupData {
val data = getRoomSessionsDataTask.execute(GetRoomSessionsDataTask.Params(roomId, version))
// Convert to KeysBackupData
return KeysBackupData(mutableMapOf(roomId to data))
}
suspend fun downloadBackedUpKeys(version: String): KeysBackupData {
return getSessionsDataTask.execute(GetSessionsDataTask.Params(version))
}
}

View file

@ -21,6 +21,8 @@ import kotlinx.coroutines.runBlocking
import org.matrix.android.sdk.api.MatrixCallback
import org.matrix.android.sdk.api.NoOpMatrixCallback
import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
import org.matrix.android.sdk.api.crypto.RoomEncryptionTrustLevel
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.MXCrossSigningInfo
import org.matrix.android.sdk.api.util.Optional
@ -28,9 +30,18 @@ import org.matrix.android.sdk.internal.crypto.crosssigning.DeviceTrustResult
import org.matrix.android.sdk.internal.crypto.crosssigning.UserTrustResult
import org.matrix.android.sdk.internal.crypto.crosssigning.isVerified
import org.matrix.android.sdk.internal.crypto.store.PrivateKeysInfo
import org.matrix.android.sdk.internal.di.SessionId
import org.matrix.android.sdk.internal.di.UserId
import org.matrix.android.sdk.internal.extensions.foldToCallback
import javax.inject.Inject
internal class RustCrossSigningService(private val olmMachine: OlmMachine) : CrossSigningService {
internal class RustCrossSigningService @Inject constructor(
@SessionId private val sessionId: String,
@UserId private val myUserId: String,
private val olmMachineProvider: OlmMachineProvider
) : CrossSigningService {
val olmMachine = olmMachineProvider.olmMachine
/**
* Is our own device signed by our own cross signing identity
*/
@ -89,7 +100,9 @@ internal class RustCrossSigningService(private val olmMachine: OlmMachine) : Cro
sskPrivateKey: String?
): UserTrustResult {
val export = PrivateKeysInfo(masterKeyPrivateKey, sskPrivateKey, uskKeyPrivateKey)
return runBlocking { olmMachine.importCrossSigningKeys(export) }
return runBlocking {
olmMachineProvider.olmMachine.importCrossSigningKeys(export)
}
}
/**
@ -206,4 +219,46 @@ internal class RustCrossSigningService(private val olmMachine: OlmMachine) : Cro
override fun onSecretUSKGossip(uskPrivateKey: String) {
// And this.
}
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
}
}
}
}
}
}

View file

@ -37,7 +37,7 @@ internal class SasVerification(
private val machine: OlmMachine,
private var inner: Sas,
private val sender: RequestSender,
listeners: ArrayList<VerificationService.Listener>,
listeners: ArrayList<VerificationService.Listener>
) :
SasVerificationTransaction {
private val dispatcher = UpdateDispatcher(listeners)

View file

@ -250,7 +250,14 @@ internal class UserIdentity(
* Convert the identity into a MxCrossSigningInfo class.
*/
override fun toMxCrossSigningInfo(): MXCrossSigningInfo {
val crossSigningKeys = listOf(this.masterKey, this.selfSigningKey)
return MXCrossSigningInfo(this.userId, crossSigningKeys)
// val crossSigningKeys = listOf(this.masterKey, this.selfSigningKey)
val trustLevel = DeviceTrustLevel(runBlocking { verified() }, false)
// TODO remove this, this is silly, we have way too many methods to check if a user is verified
masterKey.trustLevel = trustLevel
selfSigningKey.trustLevel = trustLevel
return MXCrossSigningInfo(this.userId, listOf(
this.masterKey.also { it.trustLevel = trustLevel },
this.selfSigningKey.also { it.trustLevel = trustLevel }
))
}
}

View file

@ -45,7 +45,7 @@ internal class VerificationRequest(
private val machine: OlmMachine,
private var inner: VerificationRequest,
private val sender: RequestSender,
private val listeners: ArrayList<VerificationService.Listener>,
private val listeners: ArrayList<VerificationService.Listener>
) {
private val uiHandler = Handler(Looper.getMainLooper())

View file

@ -24,7 +24,6 @@ import org.matrix.android.sdk.internal.crypto.model.rest.KeysClaimBody
import org.matrix.android.sdk.internal.crypto.model.rest.KeysClaimResponse
import org.matrix.android.sdk.internal.crypto.model.rest.KeysQueryBody
import org.matrix.android.sdk.internal.crypto.model.rest.KeysQueryResponse
import org.matrix.android.sdk.internal.crypto.model.rest.KeysUploadBody
import org.matrix.android.sdk.internal.crypto.model.rest.KeysUploadResponse
import org.matrix.android.sdk.internal.crypto.model.rest.SendToDeviceBody
import org.matrix.android.sdk.internal.crypto.model.rest.SignatureUploadResponse

View file

@ -1,347 +1,389 @@
/*
* Copyright 2020 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.crosssigning
import android.content.Context
import androidx.work.WorkerParameters
import com.squareup.moshi.JsonClass
import io.realm.Realm
import io.realm.RealmConfiguration
import io.realm.kotlin.where
import org.matrix.android.sdk.api.crypto.RoomEncryptionTrustLevel
import org.matrix.android.sdk.api.extensions.orFalse
import org.matrix.android.sdk.api.session.crypto.crosssigning.MXCrossSigningInfo
import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
import org.matrix.android.sdk.internal.crypto.store.db.mapper.CrossSigningKeysMapper
import org.matrix.android.sdk.internal.crypto.store.db.model.CrossSigningInfoEntity
import org.matrix.android.sdk.internal.crypto.store.db.model.CrossSigningInfoEntityFields
import org.matrix.android.sdk.internal.crypto.store.db.model.CryptoMapper
import org.matrix.android.sdk.internal.crypto.store.db.model.TrustLevelEntity
import org.matrix.android.sdk.internal.crypto.store.db.model.UserEntity
import org.matrix.android.sdk.internal.crypto.store.db.model.UserEntityFields
import org.matrix.android.sdk.internal.database.awaitTransaction
import org.matrix.android.sdk.internal.database.model.RoomMemberSummaryEntity
import org.matrix.android.sdk.internal.database.model.RoomMemberSummaryEntityFields
import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity
import org.matrix.android.sdk.internal.database.model.RoomSummaryEntityFields
import org.matrix.android.sdk.internal.database.query.where
import org.matrix.android.sdk.internal.di.CryptoDatabase
import org.matrix.android.sdk.internal.di.SessionDatabase
import org.matrix.android.sdk.internal.di.UserId
import org.matrix.android.sdk.internal.session.SessionComponent
import org.matrix.android.sdk.internal.session.room.membership.RoomMemberHelper
import org.matrix.android.sdk.internal.util.logLimit
import org.matrix.android.sdk.internal.worker.SessionSafeCoroutineWorker
import org.matrix.android.sdk.internal.worker.SessionWorkerParams
import timber.log.Timber
import javax.inject.Inject
internal class UpdateTrustWorker(context: Context,
params: WorkerParameters) :
SessionSafeCoroutineWorker<UpdateTrustWorker.Params>(context, params, Params::class.java) {
@JsonClass(generateAdapter = true)
internal data class Params(
override val sessionId: String,
override val lastFailureMessage: String? = null,
// Kept for compatibility, but not used anymore (can be used for pending Worker)
val updatedUserIds: List<String>? = null,
// Passing a long list of userId can break the Work Manager due to data size limitation.
// so now we use a temporary file to store the data
val filename: String? = null
) : SessionWorkerParams
@Inject lateinit var crossSigningService: DefaultCrossSigningService
// It breaks the crypto store contract, but we need to batch things :/
@CryptoDatabase
@Inject lateinit var cryptoRealmConfiguration: RealmConfiguration
@SessionDatabase
@Inject lateinit var sessionRealmConfiguration: RealmConfiguration
@UserId
@Inject lateinit var myUserId: String
@Inject lateinit var crossSigningKeysMapper: CrossSigningKeysMapper
@Inject lateinit var updateTrustWorkerDataRepository: UpdateTrustWorkerDataRepository
// @Inject lateinit var roomSummaryUpdater: RoomSummaryUpdater
@Inject lateinit var cryptoStore: IMXCryptoStore
override fun injectWith(injector: SessionComponent) {
injector.inject(this)
}
override suspend fun doSafeWork(params: Params): Result {
val userList = params.filename
?.let { updateTrustWorkerDataRepository.getParam(it) }
?.userIds
?: params.updatedUserIds.orEmpty()
// List should not be empty, but let's avoid go further in case of empty list
if (userList.isNotEmpty()) {
// Unfortunately we don't have much info on what did exactly changed (is it the cross signing keys of that user,
// or a new device?) So we check all again :/
Timber.d("## CrossSigning - Updating trust for users: ${userList.logLimit()}")
updateTrust(userList)
}
cleanup(params)
return Result.success()
}
private suspend fun updateTrust(userListParam: List<String>) {
var userList = userListParam
var myCrossSigningInfo: MXCrossSigningInfo? = null
// First we check that the users MSK are trusted by mine
// After that we check the trust chain for each devices of each users
awaitTransaction(cryptoRealmConfiguration) { cryptoRealm ->
// By mapping here to model, this object is not live
// I should update it if needed
myCrossSigningInfo = getCrossSigningInfo(cryptoRealm, myUserId)
var myTrustResult: UserTrustResult? = null
if (userList.contains(myUserId)) {
Timber.d("## CrossSigning - Clear all trust as a change on my user was detected")
// i am in the list.. but i don't know exactly the delta of change :/
// If it's my cross signing keys we should refresh all trust
// do it anyway ?
userList = cryptoRealm.where(CrossSigningInfoEntity::class.java)
.findAll()
.mapNotNull { it.userId }
// check right now my keys and mark it as trusted as other trust depends on it
val myDevices = cryptoRealm.where<UserEntity>()
.equalTo(UserEntityFields.USER_ID, myUserId)
.findFirst()
?.devices
?.map { CryptoMapper.mapToModel(it) }
myTrustResult = crossSigningService.checkSelfTrust(myCrossSigningInfo, myDevices)
updateCrossSigningKeysTrust(cryptoRealm, myUserId, myTrustResult.isVerified())
// update model reference
myCrossSigningInfo = getCrossSigningInfo(cryptoRealm, myUserId)
}
val otherInfos = userList.associateWith { userId ->
getCrossSigningInfo(cryptoRealm, userId)
}
val trusts = otherInfos.mapValues { entry ->
when (entry.key) {
myUserId -> myTrustResult
else -> {
crossSigningService.checkOtherMSKTrusted(myCrossSigningInfo, entry.value).also {
Timber.d("## CrossSigning - user:${entry.key} result:$it")
}
}
}
}
// TODO! if it's me and my keys has changed... I have to reset trust for everyone!
// i have all the new trusts, update DB
trusts.forEach {
val verified = it.value?.isVerified() == true
updateCrossSigningKeysTrust(cryptoRealm, it.key, verified)
}
// Ok so now we have to check device trust for all these users..
Timber.v("## CrossSigning - Updating devices cross trust users: ${trusts.keys.logLimit()}")
trusts.keys.forEach { userId ->
val devicesEntities = cryptoRealm.where<UserEntity>()
.equalTo(UserEntityFields.USER_ID, userId)
.findFirst()
?.devices
val trustMap = devicesEntities?.associateWith { device ->
// get up to date from DB has could have been updated
val otherInfo = getCrossSigningInfo(cryptoRealm, userId)
crossSigningService.checkDeviceTrust(myCrossSigningInfo, otherInfo, CryptoMapper.mapToModel(device))
}
// Update trust if needed
devicesEntities?.forEach { device ->
val crossSignedVerified = trustMap?.get(device)?.isCrossSignedVerified()
Timber.d("## CrossSigning - Trust for ${device.userId}|${device.deviceId} : cross verified: ${trustMap?.get(device)}")
if (device.trustLevelEntity?.crossSignedVerified != crossSignedVerified) {
Timber.d("## CrossSigning - Trust change detected for ${device.userId}|${device.deviceId} : cross verified: $crossSignedVerified")
// need to save
val trustEntity = device.trustLevelEntity
if (trustEntity == null) {
device.trustLevelEntity = cryptoRealm.createObject(TrustLevelEntity::class.java).also {
it.locallyVerified = false
it.crossSignedVerified = crossSignedVerified
}
} else {
trustEntity.crossSignedVerified = crossSignedVerified
}
}
}
}
}
// So Cross Signing keys trust is updated, device trust is updated
// We can now update room shields? in the session DB?
updateTrustStep2(userList, myCrossSigningInfo)
}
private suspend fun updateTrustStep2(userList: List<String>, myCrossSigningInfo: MXCrossSigningInfo?) {
Timber.d("## CrossSigning - Updating shields for impacted rooms...")
awaitTransaction(sessionRealmConfiguration) { sessionRealm ->
Realm.getInstance(cryptoRealmConfiguration).use { cryptoRealm ->
sessionRealm.where(RoomMemberSummaryEntity::class.java)
.`in`(RoomMemberSummaryEntityFields.USER_ID, userList.toTypedArray())
.distinct(RoomMemberSummaryEntityFields.ROOM_ID)
.findAll()
.map { it.roomId }
.also { Timber.d("## CrossSigning - ... impacted rooms ${it.logLimit()}") }
.forEach { roomId ->
RoomSummaryEntity.where(sessionRealm, roomId)
.equalTo(RoomSummaryEntityFields.IS_ENCRYPTED, true)
.findFirst()
?.let { roomSummary ->
Timber.d("## CrossSigning - Check shield state for room $roomId")
val allActiveRoomMembers = RoomMemberHelper(sessionRealm, roomId).getActiveRoomMemberIds()
try {
val updatedTrust = computeRoomShield(
myCrossSigningInfo,
cryptoRealm,
allActiveRoomMembers,
roomSummary
)
if (roomSummary.roomEncryptionTrustLevel != updatedTrust) {
Timber.d("## CrossSigning - Shield change detected for $roomId -> $updatedTrust")
roomSummary.roomEncryptionTrustLevel = updatedTrust
}
} catch (failure: Throwable) {
Timber.e(failure)
}
}
}
}
}
}
private fun getCrossSigningInfo(cryptoRealm: Realm, userId: String): MXCrossSigningInfo? {
return cryptoRealm.where(CrossSigningInfoEntity::class.java)
.equalTo(CrossSigningInfoEntityFields.USER_ID, userId)
.findFirst()
?.let { mapCrossSigningInfoEntity(it) }
}
private fun cleanup(params: Params) {
params.filename
?.let { updateTrustWorkerDataRepository.delete(it) }
}
private fun updateCrossSigningKeysTrust(cryptoRealm: Realm, userId: String, verified: Boolean) {
cryptoRealm.where(CrossSigningInfoEntity::class.java)
.equalTo(CrossSigningInfoEntityFields.USER_ID, userId)
.findFirst()
?.crossSigningKeys
?.forEach { info ->
// optimization to avoid trigger updates when there is no change..
if (info.trustLevelEntity?.isVerified() != verified) {
Timber.d("## CrossSigning - Trust change for $userId : $verified")
val level = info.trustLevelEntity
if (level == null) {
info.trustLevelEntity = cryptoRealm.createObject(TrustLevelEntity::class.java).also {
it.locallyVerified = verified
it.crossSignedVerified = verified
}
} else {
level.locallyVerified = verified
level.crossSignedVerified = verified
}
}
}
}
private fun computeRoomShield(myCrossSigningInfo: MXCrossSigningInfo?,
cryptoRealm: Realm,
activeMemberUserIds: List<String>,
roomSummaryEntity: RoomSummaryEntity): RoomEncryptionTrustLevel {
Timber.d("## CrossSigning - computeRoomShield ${roomSummaryEntity.roomId} -> ${activeMemberUserIds.logLimit()}")
// The set of “all users” depends on the type of room:
// For regular / topic rooms which have more than 2 members (including yourself) are considered when decorating a room
// For 1:1 and group DM rooms, all other users (i.e. excluding yourself) are considered when decorating a room
val listToCheck = if (roomSummaryEntity.isDirect || activeMemberUserIds.size <= 2) {
activeMemberUserIds.filter { it != myUserId }
} else {
activeMemberUserIds
}
val allTrustedUserIds = listToCheck
.filter { userId ->
getCrossSigningInfo(cryptoRealm, userId)?.isTrusted() == 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
.mapNotNull { userId ->
cryptoRealm.where<UserEntity>()
.equalTo(UserEntityFields.USER_ID, userId)
.findFirst()
?.devices
?.map { CryptoMapper.mapToModel(it) }
}
.flatten()
.let { allDevices ->
Timber.v("## CrossSigning - computeRoomShield ${roomSummaryEntity.roomId} devices ${allDevices.map { it.deviceId }.logLimit()}")
if (myCrossSigningInfo != null) {
allDevices.any { !it.trustLevel?.crossSigningVerified.orFalse() }
} else {
// Legacy method
allDevices.any { !it.isVerified }
}
}
.let { hasWarning ->
if (hasWarning) {
RoomEncryptionTrustLevel.Warning
} else {
if (listToCheck.size == allTrustedUserIds.size) {
// all users are trusted and all devices are verified
RoomEncryptionTrustLevel.Trusted
} else {
RoomEncryptionTrustLevel.Default
}
}
}
}
}
private fun mapCrossSigningInfoEntity(xsignInfo: CrossSigningInfoEntity): MXCrossSigningInfo {
val userId = xsignInfo.userId ?: ""
return MXCrossSigningInfo(
userId = userId,
crossSigningKeys = xsignInfo.crossSigningKeys.mapNotNull {
crossSigningKeysMapper.map(userId, it)
}
)
}
override fun buildErrorParams(params: Params, message: String): Params {
return params.copy(lastFailureMessage = params.lastFailureMessage ?: message)
}
}
// /*
// * Copyright 2020 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.crosssigning
//
// import android.content.Context
// import androidx.work.WorkerParameters
// import com.squareup.moshi.JsonClass
// import io.realm.Realm
// import io.realm.RealmConfiguration
// import io.realm.kotlin.where
// import kotlinx.coroutines.runBlocking
// import org.matrix.android.sdk.api.crypto.RoomEncryptionTrustLevel
// import org.matrix.android.sdk.api.extensions.orFalse
// import org.matrix.android.sdk.api.session.crypto.crosssigning.MXCrossSigningInfo
// import org.matrix.android.sdk.internal.crypto.OlmMachineProvider
// import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
// import org.matrix.android.sdk.internal.crypto.store.db.mapper.CrossSigningKeysMapper
// import org.matrix.android.sdk.internal.crypto.store.db.model.CrossSigningInfoEntity
// import org.matrix.android.sdk.internal.crypto.store.db.model.CrossSigningInfoEntityFields
// import org.matrix.android.sdk.internal.crypto.store.db.model.CryptoMapper
// import org.matrix.android.sdk.internal.crypto.store.db.model.TrustLevelEntity
// import org.matrix.android.sdk.internal.crypto.store.db.model.UserEntity
// import org.matrix.android.sdk.internal.crypto.store.db.model.UserEntityFields
// import org.matrix.android.sdk.internal.database.awaitTransaction
// import org.matrix.android.sdk.internal.database.model.RoomMemberSummaryEntity
// import org.matrix.android.sdk.internal.database.model.RoomMemberSummaryEntityFields
// import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity
// import org.matrix.android.sdk.internal.database.model.RoomSummaryEntityFields
// import org.matrix.android.sdk.internal.database.query.where
// import org.matrix.android.sdk.internal.di.CryptoDatabase
// import org.matrix.android.sdk.internal.di.SessionDatabase
// import org.matrix.android.sdk.internal.di.UserId
// import org.matrix.android.sdk.internal.session.SessionComponent
// import org.matrix.android.sdk.internal.session.room.membership.RoomMemberHelper
// import org.matrix.android.sdk.internal.util.logLimit
// import org.matrix.android.sdk.internal.worker.SessionSafeCoroutineWorker
// import org.matrix.android.sdk.internal.worker.SessionWorkerParams
// import timber.log.Timber
// import javax.inject.Inject
//
// @Deprecated("Now using olm machine")
// internal class UpdateTrustWorker(context: Context,
// params: WorkerParameters)
// : SessionSafeCoroutineWorker<UpdateTrustWorker.Params>(context, params, Params::class.java) {
//
// @JsonClass(generateAdapter = true)
// internal data class Params(
// override val sessionId: String,
// override val lastFailureMessage: String? = null,
// // Kept for compatibility, but not used anymore (can be used for pending Worker)
// val updatedUserIds: List<String>? = null,
// // Passing a long list of userId can break the Work Manager due to data size limitation.
// // so now we use a temporary file to store the data
// val filename: String? = null
// ) : SessionWorkerParams
//
// // @Inject lateinit var crossSigningService: RustCrossSigningService
// @Inject lateinit var olmMachineProvider: OlmMachineProvider//: RustCrossSigningService
//
// // It breaks the crypto store contract, but we need to batch things :/
// @CryptoDatabase
// @Inject lateinit var cryptoRealmConfiguration: RealmConfiguration
//
// @SessionDatabase
// @Inject lateinit var sessionRealmConfiguration: RealmConfiguration
//
// @UserId
// @Inject lateinit var myUserId: String
// @Inject lateinit var crossSigningKeysMapper: CrossSigningKeysMapper
// @Inject lateinit var updateTrustWorkerDataRepository: UpdateTrustWorkerDataRepository
//
// // @Inject lateinit var roomSummaryUpdater: RoomSummaryUpdater
// @Inject lateinit var cryptoStore: IMXCryptoStore
//
// override fun injectWith(injector: SessionComponent) {
// injector.inject(this)
// }
//
// override suspend fun doSafeWork(params: Params): Result {
// val userList = params.filename
// ?.let { updateTrustWorkerDataRepository.getParam(it) }
// ?.userIds
// ?: params.updatedUserIds.orEmpty()
//
// // List should not be empty, but let's avoid go further in case of empty list
// if (userList.isNotEmpty()) {
// // Unfortunately we don't have much info on what did exactly changed (is it the cross signing keys of that user,
// // or a new device?) So we check all again :/
// Timber.d("## CrossSigning - Updating trust for users: ${userList.logLimit()}")
// updateTrust(userList)
// }
//
// cleanup(params)
// return Result.success()
// }
//
// private suspend fun updateTrust(userListParam: List<String>) {
// var userList = userListParam
// var myCrossSigningInfo: MXCrossSigningInfo? = null
//
// // First we check that the users MSK are trusted by mine
// // After that we check the trust chain for each devices of each users
// awaitTransaction(cryptoRealmConfiguration) { cryptoRealm ->
// // By mapping here to model, this object is not live
// // I should update it if needed
// myCrossSigningInfo = getCrossSigningInfo(cryptoRealm, myUserId)
//
// var myTrustResult: UserTrustResult? = null
//
// if (userList.contains(myUserId)) {
// Timber.d("## CrossSigning - Clear all trust as a change on my user was detected")
// // i am in the list.. but i don't know exactly the delta of change :/
// // If it's my cross signing keys we should refresh all trust
// // do it anyway ?
// userList = cryptoRealm.where(CrossSigningInfoEntity::class.java)
// .findAll()
// .mapNotNull { it.userId }
//
// // check right now my keys and mark it as trusted as other trust depends on it
// // val myDevices = olmMachineProvider.olmMachine.getUserDevices(myUserId)
// // cryptoRealm.where<UserEntity>()
// // .equalTo(UserEntityFields.USER_ID, myUserId)
// // .findFirst()
// // ?.devices
// // ?.map { CryptoMapper.mapToModel(it) }
//
//
// val identity = olmMachineProvider.olmMachine.getIdentity(olmMachineProvider.olmMachine.userId())
// myTrustResult = if (identity?.verified() == true) {
// UserTrustResult.Success
// } else {
// if (identity?.toMxCrossSigningInfo() == null) {
// UserTrustResult.CrossSigningNotConfigured(olmMachineProvider.olmMachine.userId())
// } else {
// UserTrustResult.KeysNotTrusted(identity.toMxCrossSigningInfo())
// }
// }
//
// // crossSigningService.checkSelfTrust(myCrossSigningInfo, myDevices)
// updateCrossSigningKeysTrust(cryptoRealm, myUserId, myTrustResult.isVerified())
// // update model reference
// myCrossSigningInfo = getCrossSigningInfo(cryptoRealm, myUserId)
// }
//
// val otherInfos = userList.associateWith { userId ->
// getCrossSigningInfo(cryptoRealm, userId)
// }
//
// val trusts = otherInfos.mapValues { entry ->
// when (entry.key) {
// myUserId -> myTrustResult
// else -> {
// val userId = entry.value?.userId ?: ""
// val identity = olmMachineProvider.olmMachine.getIdentity(userId)
// if (identity?.verified() == true) {
// UserTrustResult.Success
// } else {
// if (identity?.toMxCrossSigningInfo() == null) {
// UserTrustResult.CrossSigningNotConfigured(userId)
// } else {
// UserTrustResult.KeysNotTrusted(identity.toMxCrossSigningInfo())
// }
// }
// }
// }
// }
//
// // TODO! if it's me and my keys has changed... I have to reset trust for everyone!
// // i have all the new trusts, update DB
// // trusts.forEach {
// // val verified = it.value?.isVerified() == true
// // updateCrossSigningKeysTrust(cryptoRealm, it.key, verified)
// // }
//
// // Ok so now we have to check device trust for all these users..
// Timber.v("## CrossSigning - Updating devices cross trust users: ${trusts.keys.logLimit()}")
// trusts.keys.forEach { userId ->
// val devicesEntities = olmMachineProvider.olmMachine.getUserDevices(userId)
// // cryptoRealm.where<UserEntity>()
// // .equalTo(UserEntityFields.USER_ID, userId)
// // .findFirst()
// // ?.devices
//
// val trustMap = devicesEntities.associateWith { device ->
// // val cryptoDevice = device.toCryptoDeviceInfo()
// // get up to date from DB has could have been updated
// // val otherInfo = getCrossSigningInfo(cryptoRealm, userId)
// // val deviceId = device.i deviceId ?: ""
// // val device = olmMachineProvider.olmMachine.getDevice(userId, deviceId)
// // if (device != null) {
// // TODO i don't quite understand the semantics here and there are no docs for
// // DeviceTrustResult, what do the different result types mean exactly,
// // do you return success only if the Device is cross signing verified?
// // what about the local trust if it isn't? why is the local trust passed as an argument here?
// DeviceTrustResult.Success(runBlocking { device.trustLevel() })
// // } else {
// // DeviceTrustResult.UnknownDevice(deviceId)
// // }
// // crossSigningService.checkDeviceTrust(myCrossSigningInfo, otherInfo, CryptoMapper.mapToModel(device))
// }
//
// // Update trust if needed
// // devicesEntities?.forEach { device ->
// // val crossSignedVerified = trustMap?.get(device)?.isCrossSignedVerified()
// // Timber.d("## CrossSigning - Trust for ${device.userId}|${device.deviceId} : cross verified: ${trustMap?.get(device)}")
// // if (device.trustLevelEntity?.crossSignedVerified != crossSignedVerified) {
// // Timber.d("## CrossSigning - Trust change detected for ${device.userId}|${device.deviceId} : cross verified: $crossSignedVerified")
// // // need to save
// // val trustEntity = device.trustLevelEntity
// // if (trustEntity == null) {
// // device.trustLevelEntity = cryptoRealm.createObject(TrustLevelEntity::class.java).also {
// // it.locallyVerified = false
// // it.crossSignedVerified = crossSignedVerified
// // }
// // } else {
// // trustEntity.crossSignedVerified = crossSignedVerified
// // }
// // }
// // }
// }
// }
//
// // So Cross Signing keys trust is updated, device trust is updated
// // We can now update room shields? in the session DB?
// updateTrustStep2(userList, myCrossSigningInfo)
// }
//
// private suspend fun updateTrustStep2(userList: List<String>, myCrossSigningInfo: MXCrossSigningInfo?) {
// Timber.d("## CrossSigning - Updating shields for impacted rooms...")
// awaitTransaction(sessionRealmConfiguration) { sessionRealm ->
// Realm.getInstance(cryptoRealmConfiguration).use { cryptoRealm ->
// sessionRealm.where(RoomMemberSummaryEntity::class.java)
// .`in`(RoomMemberSummaryEntityFields.USER_ID, userList.toTypedArray())
// .distinct(RoomMemberSummaryEntityFields.ROOM_ID)
// .findAll()
// .map { it.roomId }
// .also { Timber.d("## CrossSigning - ... impacted rooms ${it.logLimit()}") }
// .forEach { roomId ->
// RoomSummaryEntity.where(sessionRealm, roomId)
// .equalTo(RoomSummaryEntityFields.IS_ENCRYPTED, true)
// .findFirst()
// ?.let { roomSummary ->
// Timber.d("## CrossSigning - Check shield state for room $roomId")
// val allActiveRoomMembers = RoomMemberHelper(sessionRealm, roomId).getActiveRoomMemberIds()
// try {
// val updatedTrust = computeRoomShield(
// myCrossSigningInfo,
// cryptoRealm,
// allActiveRoomMembers,
// roomSummary
// )
// if (roomSummary.roomEncryptionTrustLevel != updatedTrust) {
// Timber.d("## CrossSigning - Shield change detected for $roomId -> $updatedTrust")
// roomSummary.roomEncryptionTrustLevel = updatedTrust
// }
// } catch (failure: Throwable) {
// Timber.e(failure)
// }
// }
// }
// }
// }
// }
//
// private suspend fun getCrossSigningInfo(cryptoRealm: Realm, userId: String): MXCrossSigningInfo? {
// return olmMachineProvider.olmMachine.getIdentity(userId)?.toMxCrossSigningInfo()
// // cryptoRealm.where(CrossSigningInfoEntity::class.java)
// // .equalTo(CrossSigningInfoEntityFields.USER_ID, userId)
// // .findFirst()
// // ?.let { mapCrossSigningInfoEntity(it) }
// }
//
// private fun cleanup(params: Params) {
// params.filename
// ?.let { updateTrustWorkerDataRepository.delete(it) }
// }
//
// private fun updateCrossSigningKeysTrust(cryptoRealm: Realm, userId: String, verified: Boolean) {
// cryptoRealm.where(CrossSigningInfoEntity::class.java)
// .equalTo(CrossSigningInfoEntityFields.USER_ID, userId)
// .findFirst()
// ?.crossSigningKeys
// ?.forEach { info ->
// // optimization to avoid trigger updates when there is no change..
// if (info.trustLevelEntity?.isVerified() != verified) {
// Timber.d("## CrossSigning - Trust change for $userId : $verified")
// val level = info.trustLevelEntity
// if (level == null) {
// info.trustLevelEntity = cryptoRealm.createObject(TrustLevelEntity::class.java).also {
// it.locallyVerified = verified
// it.crossSignedVerified = verified
// }
// } else {
// level.locallyVerified = verified
// level.crossSignedVerified = verified
// }
// }
// }
// }
//
// private suspend fun computeRoomShield(myCrossSigningInfo: MXCrossSigningInfo?,
// cryptoRealm: Realm,
// activeMemberUserIds: List<String>,
// roomSummaryEntity: RoomSummaryEntity): RoomEncryptionTrustLevel {
// Timber.d("## CrossSigning - computeRoomShield ${roomSummaryEntity.roomId} -> ${activeMemberUserIds.logLimit()}")
// // The set of “all users” depends on the type of room:
// // For regular / topic rooms which have more than 2 members (including yourself) are considered when decorating a room
// // For 1:1 and group DM rooms, all other users (i.e. excluding yourself) are considered when decorating a room
// val listToCheck = if (roomSummaryEntity.isDirect || activeMemberUserIds.size <= 2) {
// activeMemberUserIds.filter { it != myUserId }
// } else {
// activeMemberUserIds
// }
//
// val allTrustedUserIds = listToCheck
// .filter { userId ->
// getCrossSigningInfo(cryptoRealm, userId)?.isTrusted() == true
// }
// Timber.d("## CrossSigning - allTrustedIds ${allTrustedUserIds}")
//
// 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
// .mapNotNull { userId ->
// // cryptoRealm.where<UserEntity>()
// // .equalTo(UserEntityFields.USER_ID, userId)
// // .findFirst()
// // ?.devices
// // ?.map { CryptoMapper.mapToModel(it) }
// olmMachineProvider.olmMachine.getUserDevices(userId)
// }
// .flatten()
// .let { allDevices ->
// Timber.v("## CrossSigning - computeRoomShield ${roomSummaryEntity.roomId} devices ${allDevices.map { "${it.toCryptoDeviceInfo().deviceId} trust ${it.toCryptoDeviceInfo().trustLevel}" }.logLimit()}")
//
// if (myCrossSigningInfo != null) {
// allDevices.any { !it.toCryptoDeviceInfo().trustLevel?.crossSigningVerified.orFalse() }
// } else {
// // Legacy method
// allDevices.any { !it.toCryptoDeviceInfo().isVerified }
// }
// }
// .let { hasWarning ->
// if (hasWarning) {
// RoomEncryptionTrustLevel.Warning
// } else {
// if (listToCheck.size == allTrustedUserIds.size) {
// // all users are trusted and all devices are verified
// RoomEncryptionTrustLevel.Trusted
// } else {
// RoomEncryptionTrustLevel.Default
// }
// }
// }
// }
// }
//
// private fun mapCrossSigningInfoEntity(xsignInfo: CrossSigningInfoEntity): MXCrossSigningInfo {
// val userId = xsignInfo.userId ?: ""
// return MXCrossSigningInfo(
// userId = userId,
// crossSigningKeys = xsignInfo.crossSigningKeys.mapNotNull {
// crossSigningKeysMapper.map(userId, it)
// }
// )
// }
//
// override fun buildErrorParams(params: Params, message: String): Params {
// return params.copy(lastFailureMessage = params.lastFailureMessage ?: message)
// }
// }

View file

@ -24,8 +24,8 @@ import com.squareup.moshi.JsonClass
*/
@JsonClass(generateAdapter = true)
internal data class KeysClaimResponse(
/// If any remote homeservers could not be reached, they are recorded here.
/// The names of the properties are the names of the unreachable servers.
// / If any remote homeservers could not be reached, they are recorded here.
// / The names of the properties are the names of the unreachable servers.
@Json(name = "failures")
val failures: Map<String, Any>,

View file

@ -17,14 +17,11 @@
package org.matrix.android.sdk.internal.crypto.tasks
import org.matrix.android.sdk.internal.crypto.api.CryptoApi
import org.matrix.android.sdk.internal.crypto.model.MXKey
import org.matrix.android.sdk.internal.crypto.model.MXUsersDevicesMap
import org.matrix.android.sdk.internal.crypto.model.rest.KeysClaimBody
import org.matrix.android.sdk.internal.crypto.model.rest.KeysClaimResponse
import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
import org.matrix.android.sdk.internal.network.executeRequest
import org.matrix.android.sdk.internal.task.Task
import timber.log.Timber
import javax.inject.Inject
internal interface ClaimOneTimeKeysForUsersDeviceTask : Task<ClaimOneTimeKeysForUsersDeviceTask.Params, KeysClaimResponse> {

View file

@ -18,8 +18,6 @@ package org.matrix.android.sdk.internal.crypto.tasks
import org.matrix.android.sdk.api.util.JsonDict
import org.matrix.android.sdk.internal.crypto.api.CryptoApi
import org.matrix.android.sdk.internal.crypto.model.rest.DeviceKeys
import org.matrix.android.sdk.internal.crypto.model.rest.KeysUploadBody
import org.matrix.android.sdk.internal.crypto.model.rest.KeysUploadResponse
import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
import org.matrix.android.sdk.internal.network.executeRequest

View file

@ -16,8 +16,6 @@
package org.matrix.android.sdk.internal.crypto.verification
import android.os.Handler
import android.os.Looper
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
import kotlinx.coroutines.runBlocking
@ -30,7 +28,7 @@ import org.matrix.android.sdk.api.session.events.model.EventType
import org.matrix.android.sdk.api.session.events.model.toModel
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.OlmMachine
import org.matrix.android.sdk.internal.crypto.OlmMachineProvider
import org.matrix.android.sdk.internal.crypto.OwnUserIdentity
import org.matrix.android.sdk.internal.crypto.SasVerification
import org.matrix.android.sdk.internal.crypto.UserIdentity
@ -38,13 +36,15 @@ import org.matrix.android.sdk.internal.crypto.model.rest.VERIFICATION_METHOD_QR_
import org.matrix.android.sdk.internal.crypto.model.rest.VERIFICATION_METHOD_QR_CODE_SHOW
import org.matrix.android.sdk.internal.crypto.model.rest.VERIFICATION_METHOD_RECIPROCATE
import org.matrix.android.sdk.internal.crypto.model.rest.toValue
import org.matrix.android.sdk.internal.session.SessionScope
import timber.log.Timber
import javax.inject.Inject
/** A helper class to deserialize to-device `m.key.verification.*` events to fetch the transaction id out */
@JsonClass(generateAdapter = true)
internal data class ToDeviceVerificationEvent(
@Json(name = "sender") val sender: String?,
@Json(name = "transaction_id") val transactionId: String,
@Json(name = "transaction_id") val transactionId: String
)
/** Helper method to fetch the unique ID of the verification event */
@ -70,61 +70,13 @@ internal fun prepareMethods(methods: List<VerificationMethod>): List<String> {
return stringMethods
}
/** 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())
@SessionScope
internal class RustVerificationService @Inject constructor(private val olmMachineProvider: OlmMachineProvider) : VerificationService {
internal fun addListener(listener: VerificationService.Listener) {
uiHandler.post {
if (!this.listeners.contains(listener)) {
this.listeners.add(listener)
}
}
val olmMachine by lazy {
olmMachineProvider.olmMachine
}
internal fun removeListener(listener: VerificationService.Listener) {
uiHandler.post { this.listeners.remove(listener) }
}
internal fun dispatchTxAdded(tx: VerificationTransaction) {
uiHandler.post {
this.listeners.forEach {
try {
it.transactionCreated(tx)
} catch (e: Throwable) {
Timber.e(e, "## Error while notifying listeners")
}
}
}
}
internal fun dispatchTxUpdated(tx: VerificationTransaction) {
uiHandler.post {
this.listeners.forEach {
try {
it.transactionUpdated(tx)
} catch (e: Throwable) {
Timber.e(e, "## Error while notifying listeners")
}
}
}
}
internal fun dispatchRequestAdded(tx: PendingVerificationRequest) {
Timber.v("## SAS dispatchRequestAdded txId:${tx.transactionId} $tx")
uiHandler.post {
this.listeners.forEach {
try {
it.verificationRequestCreated(tx)
} catch (e: Throwable) {
Timber.e(e, "## Error while notifying listeners")
}
}
}
}
}
internal class RustVerificationService(private val olmMachine: OlmMachine) : VerificationService {
private val dispatcher = UpdateDispatcher(this.olmMachine.verificationListeners)
/** The main entry point for the verification service
@ -279,8 +231,8 @@ internal class RustVerificationService(private val olmMachine: OlmMachine) : Ver
): PendingVerificationRequest {
val verification = when (val identity = runBlocking { olmMachine.getIdentity(otherUserId) }) {
is OwnUserIdentity -> runBlocking { identity.requestVerification(methods) }
is UserIdentity -> throw IllegalArgumentException("This method doesn't support verification of other users devices")
null -> throw IllegalArgumentException("Cross signing has not been bootstrapped for our own user")
is UserIdentity -> throw IllegalArgumentException("This method doesn't support verification of other users devices")
null -> throw IllegalArgumentException("Cross signing has not been bootstrapped for our own user")
}
return verification.toPendingVerificationRequest()
@ -294,9 +246,9 @@ internal class RustVerificationService(private val olmMachine: OlmMachine) : Ver
): PendingVerificationRequest {
Timber.i("## SAS Requesting verification to user: $otherUserId in room $roomId")
val verification = when (val identity = runBlocking { olmMachine.getIdentity(otherUserId) }) {
is UserIdentity -> runBlocking { identity.requestVerification(methods, roomId, localId!!) }
is UserIdentity -> runBlocking { identity.requestVerification(methods, roomId, localId!!) }
is OwnUserIdentity -> throw IllegalArgumentException("This method doesn't support verification of our own user")
null -> throw IllegalArgumentException("The user that we wish to verify doesn't support cross signing")
null -> throw IllegalArgumentException("The user that we wish to verify doesn't support cross signing")
}
return verification.toPendingVerificationRequest()

View file

@ -0,0 +1,78 @@
/*
* Copyright (c) 2021 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 android.os.Handler
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 timber.log.Timber
/** 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) {
uiHandler.post {
if (!this.listeners.contains(listener)) {
this.listeners.add(listener)
}
}
}
internal fun removeListener(listener: VerificationService.Listener) {
uiHandler.post { this.listeners.remove(listener) }
}
internal fun dispatchTxAdded(tx: VerificationTransaction) {
uiHandler.post {
this.listeners.forEach {
try {
it.transactionCreated(tx)
} catch (e: Throwable) {
Timber.e(e, "## Error while notifying listeners")
}
}
}
}
internal fun dispatchTxUpdated(tx: VerificationTransaction) {
uiHandler.post {
this.listeners.forEach {
try {
it.transactionUpdated(tx)
} catch (e: Throwable) {
Timber.e(e, "## Error while notifying listeners")
}
}
}
}
internal fun dispatchRequestAdded(tx: PendingVerificationRequest) {
Timber.v("## SAS dispatchRequestAdded txId:${tx.transactionId} $tx")
uiHandler.post {
this.listeners.forEach {
try {
it.verificationRequestCreated(tx)
} catch (e: Throwable) {
Timber.e(e, "## Error while notifying listeners")
}
}
}
}
}

View file

@ -68,7 +68,7 @@ internal class RoomSummaryMapper @Inject constructor(private val timelineEventMa
isEncrypted = roomSummaryEntity.isEncrypted,
encryptionEventTs = roomSummaryEntity.encryptionEventTs,
breadcrumbsIndex = roomSummaryEntity.breadcrumbsIndex,
roomEncryptionTrustLevel = roomSummaryEntity.roomEncryptionTrustLevel,
roomEncryptionTrustLevel = roomSummaryEntity.roomEncryptionTrustLevel.takeIf { roomSummaryEntity.isEncrypted },
inviterId = roomSummaryEntity.inviterId,
hasFailedSending = roomSummaryEntity.hasFailedSending,
roomType = roomSummaryEntity.roomType,

View file

@ -23,9 +23,9 @@ import org.matrix.android.sdk.api.auth.data.SessionParams
import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.internal.crypto.CancelGossipRequestWorker
import org.matrix.android.sdk.internal.crypto.CryptoModule
import org.matrix.android.sdk.internal.crypto.OlmMachineProvider
import org.matrix.android.sdk.internal.crypto.SendGossipRequestWorker
import org.matrix.android.sdk.internal.crypto.SendGossipWorker
import org.matrix.android.sdk.internal.crypto.crosssigning.UpdateTrustWorker
import org.matrix.android.sdk.internal.di.MatrixComponent
import org.matrix.android.sdk.internal.federation.FederationModule
import org.matrix.android.sdk.internal.network.NetworkConnectivityChecker
@ -117,6 +117,8 @@ internal interface SessionComponent {
fun taskExecutor(): TaskExecutor
fun olmMachineProvider() : OlmMachineProvider
fun inject(worker: SendEventWorker)
fun inject(worker: MultipleEventSendingDispatcherWorker)
@ -137,7 +139,7 @@ internal interface SessionComponent {
fun inject(worker: SendGossipWorker)
fun inject(worker: UpdateTrustWorker)
// fun inject(worker: UpdateTrustWorker)
@Component.Factory
interface Factory {

View file

@ -23,8 +23,8 @@ import org.matrix.android.sdk.api.session.identity.IdentityServiceError
import org.matrix.android.sdk.api.session.identity.toMedium
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams
import org.matrix.android.sdk.api.util.MimeTypes
import org.matrix.android.sdk.internal.crypto.DeviceListManager
import org.matrix.android.sdk.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM
import org.matrix.android.sdk.internal.crypto.OlmMachineProvider
import org.matrix.android.sdk.internal.di.AuthenticatedIdentity
import org.matrix.android.sdk.internal.di.UserId
import org.matrix.android.sdk.internal.network.token.AccessTokenProvider
@ -39,7 +39,8 @@ import javax.inject.Inject
internal class CreateRoomBodyBuilder @Inject constructor(
private val ensureIdentityTokenTask: EnsureIdentityTokenTask,
private val deviceListManager: DeviceListManager,
// private val deviceListManager: DeviceListManager,
private val olmMachineProvider: OlmMachineProvider,
private val identityStore: IdentityStore,
private val fileUploader: FileUploader,
@UserId
@ -173,14 +174,15 @@ internal class CreateRoomBodyBuilder @Inject constructor(
}
private suspend fun canEnableEncryption(params: CreateRoomParams): Boolean {
return params.enableEncryptionIfInvitedUsersSupportIt &&
return params.enableEncryptionIfInvitedUsersSupportIt
// Parity with web, enable if users have encryption ready devices
// for now remove checks on cross signing and 3pid invites
// && crossSigningService.isCrossSigningVerified()
params.invite3pids.isEmpty() &&
params.invitedUserIds.isNotEmpty() &&
params.invitedUserIds.let { userIds ->
val keys = deviceListManager.downloadKeys(userIds, forceDownload = false)
&& params.invite3pids.isEmpty()
&& params.invitedUserIds.isNotEmpty()
&& params.invitedUserIds.let { userIds ->
val keys = olmMachineProvider.olmMachine.getUserDevicesMap(userIds)
// deviceListManager.downloadKeys(userIds, forceDownload = false)
userIds.all { userId ->
keys.map[userId].let { deviceMap ->

View file

@ -17,13 +17,14 @@
package org.matrix.android.sdk.internal.session.room.membership
import com.zhuinden.monarchy.Monarchy
import dagger.Lazy
import io.realm.Realm
import io.realm.kotlin.createObject
import kotlinx.coroutines.TimeoutCancellationException
import org.matrix.android.sdk.api.session.crypto.CryptoService
import org.matrix.android.sdk.api.session.room.model.Membership
import org.matrix.android.sdk.api.session.room.send.SendState
import org.matrix.android.sdk.internal.crypto.CryptoSessionInfoProvider
import org.matrix.android.sdk.internal.crypto.DeviceListManager
import org.matrix.android.sdk.internal.database.awaitNotEmptyResult
import org.matrix.android.sdk.internal.database.mapper.toEntity
import org.matrix.android.sdk.internal.database.model.CurrentStateEventEntity
@ -60,7 +61,7 @@ internal class DefaultLoadRoomMembersTask @Inject constructor(
private val roomSummaryUpdater: RoomSummaryUpdater,
private val roomMemberEventHandler: RoomMemberEventHandler,
private val cryptoSessionInfoProvider: CryptoSessionInfoProvider,
private val deviceListManager: DeviceListManager,
private val cryptoService: Lazy<CryptoService>,
private val globalErrorReceiver: GlobalErrorReceiver
) : LoadRoomMembersTask {
@ -130,7 +131,10 @@ internal class DefaultLoadRoomMembersTask @Inject constructor(
}
if (cryptoSessionInfoProvider.isRoomEncrypted(roomId)) {
deviceListManager.onRoomMembersLoadedFor(roomId)
cryptoService.get().onE2ERoomMemberLoadedFromServer(roomId)
// val userIds = cryptoSessionInfoProvider.getRoomUserIds(roomId, true)
// olmMachineProvider.olmMachine.updateTrackedUsers(userIds)
// deviceListManager.onRoomMembersLoadedFor(roomId)
}
}

View file

@ -18,6 +18,7 @@ package org.matrix.android.sdk.internal.session.room.summary
import io.realm.Realm
import io.realm.kotlin.createObject
import org.matrix.android.sdk.api.session.crypto.crosssigning.CrossSigningService
import org.matrix.android.sdk.api.session.events.model.EventType
import org.matrix.android.sdk.api.session.events.model.toModel
import org.matrix.android.sdk.api.session.room.accountdata.RoomAccountDataTypes
@ -36,7 +37,6 @@ 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.MXCRYPTO_ALGORITHM_MEGOLM
import org.matrix.android.sdk.internal.crypto.crosssigning.DefaultCrossSigningService
import org.matrix.android.sdk.internal.database.mapper.ContentMapper
import org.matrix.android.sdk.internal.database.model.CurrentStateEventEntity
import org.matrix.android.sdk.internal.database.model.EventEntity
@ -71,9 +71,8 @@ internal class RoomSummaryUpdater @Inject constructor(
@UserId private val userId: String,
private val roomDisplayNameResolver: RoomDisplayNameResolver,
private val roomAvatarResolver: RoomAvatarResolver,
private val crossSigningService: DefaultCrossSigningService,
private val roomAccountDataDataSource: RoomAccountDataDataSource,
private val normalizer: Normalizer) {
private val crossSigningService: CrossSigningService,
private val roomAccountDataDataSource: RoomAccountDataDataSource) {
fun update(realm: Realm,
roomId: String,
@ -168,8 +167,8 @@ internal class RoomSummaryUpdater @Inject constructor(
roomSummaryEntity.otherMemberIds.clear()
roomSummaryEntity.otherMemberIds.addAll(otherRoomMembers)
if (roomSummaryEntity.isEncrypted && otherRoomMembers.isNotEmpty()) {
// mmm maybe we could only refresh shield instead of checking trust also?
crossSigningService.onUsersDeviceUpdate(otherRoomMembers)
// TODO do we really need to force a shield refresh here?
// crossSigningService.ensureRoomShieldForRoom(roomId)
}
}
}

View file

@ -1,11 +1,19 @@
x86_64:
cargo build --release --target x86_64-linux-android
install -D target/x86_64-linux-android/release/libmatrix_crypto.so ../matrix-sdk-android/src/main/jniLibs/x86_64/libuniffi_olm.so
# x86_64:
# cargo build --release --target x86_64-linux-android
# mkdir -p ../matrix-sdk-android/src/main/jniLibs/x86_64/
# cp target/x86_64-linux-android/release/libmatrix_crypto.so ../matrix-sdk-android/src/main/jniLibs/x86_64/libuniffi_olm.so
x86:
cargo build --release --target i686-linux-android
install -D target/i686-linux-android/release/libmatrix_crypto.so ../matrix-sdk-android/src/main/jniLibs/x86/libuniffi_olm.so
mkdir -p ../matrix-sdk-android/src/main/jniLibs/x86/
cp target/i686-linux-android/release/libmatrix_crypto.so ../matrix-sdk-android/src/main/jniLibs/x86/libuniffi_olm.so
aarch64:
cargo build --release --target aarch64-linux-android
install -D target/aarch64-linux-android/release/libmatrix_crypto.so ../matrix-sdk-android/src/main/jniLibs/arm64-v8a/libuniffi_olm.so
mkdir -p ../matrix-sdk-android/src/main/jniLibs/arm64-v8a/
cp target/aarch64-linux-android/release/libmatrix_crypto.so ../matrix-sdk-android/src/main/jniLibs/arm64-v8a/libuniffi_olm.so
armv7-linux-androideabi:
cargo build --release --target armv7-linux-androideabi
mkdir -p ../matrix-sdk-android/src/main/jniLibs/armeabi-v7a/
cp target/armv7-linux-androideabi/release/libmatrix_crypto.so ../matrix-sdk-android/src/main/jniLibs/armeabi-v7a/libuniffi_olm.so