mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2024-11-21 17:05:39 +03:00
Wire compute room shields with rust
This commit is contained in:
parent
ae635e2b0a
commit
f209ae26bc
30 changed files with 1867 additions and 1573 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -20,3 +20,5 @@ Cargo.lock
|
|||
/fastlane/report.xml
|
||||
|
||||
/library/build
|
||||
|
||||
matrix-sdk-android/src/main/jniLibs/
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 }
|
||||
))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -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)
|
||||
// }
|
||||
// }
|
||||
|
|
|
@ -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>,
|
||||
|
||||
|
|
|
@ -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> {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 ->
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
Loading…
Reference in a new issue