1
0
mirror of https://github.com/vector-im/riotX-android synced 2025-10-06 00:02:48 +02:00

Compare commits

...

2 Commits

Author SHA1 Message Date
Benoit Marty
55bd65d79f Create Manager to inject directly instead of service
Cleanup the Service from app point of view
2021-06-24 13:46:06 +02:00
Benoit Marty
b2a4b0c2d6 Rename files and class - code does not compile, it's just to ensure a better git history 2021-06-24 10:40:18 +02:00
34 changed files with 5322 additions and 5035 deletions

View File

@@ -77,9 +77,4 @@ interface CrossSigningService {
fun checkDeviceTrust(otherUserId: String,
otherDeviceId: String,
locallyTrusted: Boolean?): DeviceTrustResult
// FIXME Those method do not have to be in the service
fun onSecretMSKGossip(mskPrivateKey: String)
fun onSecretSSKGossip(sskPrivateKey: String)
fun onSecretUSKGossip(uskPrivateKey: String)
}

View File

@@ -173,8 +173,6 @@ interface KeysBackupService {
password: String,
callback: MatrixCallback<Unit>)
fun onSecretKeyGossip(secret: String)
/**
* Restore a backup with a recovery key from a given backup version stored on the homeserver.
*

View File

@@ -16,7 +16,6 @@
package org.matrix.android.sdk.api.session.crypto.verification
import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.session.events.model.LocalEcho
/**
@@ -137,6 +136,4 @@ interface VerificationService {
return age in tooInThePast..tooInTheFuture
}
}
fun onPotentiallyInterestingEventRoomFailToDecrypt(event: Event)
}

View File

@@ -0,0 +1,23 @@
/*
* 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.api.session.events.model.Event
internal interface CryptoDecryptor {
fun decryptEvent(event: Event, timeline: String): MXEventDecryptionResult
}

View File

@@ -0,0 +1,27 @@
/*
* 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.api.session.events.model.Event
internal interface CryptoManagerInput {
fun onSyncWillProcess(isInitialSync: Boolean)
fun onStateEvent(roomId: String, event: Event)
fun onLiveEvent(roomId: String, event: Event)
}

View File

@@ -96,6 +96,14 @@ 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.keysbackup.KeysBackupService
import org.matrix.android.sdk.api.session.crypto.verification.VerificationService
import org.matrix.android.sdk.internal.crypto.crosssigning.CrossSigningManager
import org.matrix.android.sdk.internal.crypto.crosssigning.CrossSigningManagerInput
import org.matrix.android.sdk.internal.crypto.keysbackup.DefaultKeysBackupService
import org.matrix.android.sdk.internal.crypto.keysbackup.KeysBackupManager
import org.matrix.android.sdk.internal.crypto.keysbackup.KeysBackupManagerInput
import org.matrix.android.sdk.internal.crypto.verification.DefaultVerificationService
import retrofit2.Retrofit
import java.io.File
@@ -160,6 +168,21 @@ internal abstract class CryptoModule {
@Binds
abstract fun bindCryptoService(service: DefaultCryptoService): CryptoService
@Binds
abstract fun bindKeysBackupService(service: DefaultKeysBackupService): KeysBackupService
@Binds
abstract fun bindVerificationService(service: DefaultVerificationService): VerificationService
@Binds
abstract fun bindCryptoManagerInput(manager: CryptoManager): CryptoManagerInput
@Binds
abstract fun bindKeysBackupManagerInput(manager: KeysBackupManager): KeysBackupManagerInput
@Binds
abstract fun bindCrossSigningManagerInput(manager: CrossSigningManager): CrossSigningManagerInput
@Binds
abstract fun bindDeleteDeviceTask(task: DefaultDeleteDeviceTask): DeleteDeviceTask

View File

@@ -0,0 +1,26 @@
/*
* 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.api.session.events.model.Event
import org.matrix.android.sdk.internal.session.sync.model.SyncResponse
internal interface CryptoSyncInput {
fun onToDeviceEvent(event: Event)
fun onSyncCompleted(syncResponse: SyncResponse)
}

View File

@@ -44,10 +44,6 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM
coroutineDispatchers: MatrixCoroutineDispatchers,
private val taskExecutor: TaskExecutor) {
interface UserDevicesUpdateListener {
fun onUsersDeviceUpdate(userIds: List<String>)
}
private val deviceChangeListeners = mutableListOf<UserDevicesUpdateListener>()
fun addListener(listener: UserDevicesUpdateListener) {

View File

@@ -136,7 +136,7 @@ internal class EventDecryptor @Inject constructor(
val lastForcedDate = lastNewSessionForcedDates.getObject(senderId, deviceKey) ?: 0
val now = System.currentTimeMillis()
if (now - lastForcedDate < DefaultCryptoService.CRYPTO_MIN_FORCE_SESSION_PERIOD_MILLIS) {
if (now - lastForcedDate < CryptoManager.CRYPTO_MIN_FORCE_SESSION_PERIOD_MILLIS) {
Timber.w("## CRYPTO | markOlmSessionForUnwedging: New session already forced with device at $lastForcedDate. Not forcing another")
return
}

View File

@@ -0,0 +1,21 @@
/*
* 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
internal interface UserDevicesUpdateListener {
fun onUsersDeviceUpdate(userIds: List<String>)
}

View File

@@ -17,7 +17,7 @@
package org.matrix.android.sdk.internal.crypto.actions
import org.matrix.android.sdk.internal.crypto.crosssigning.DeviceTrustLevel
import org.matrix.android.sdk.internal.crypto.keysbackup.DefaultKeysBackupService
import org.matrix.android.sdk.internal.crypto.keysbackup.KeysBackupManager
import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
import org.matrix.android.sdk.internal.di.UserId
import timber.log.Timber
@@ -26,7 +26,8 @@ import javax.inject.Inject
internal class SetDeviceVerificationAction @Inject constructor(
private val cryptoStore: IMXCryptoStore,
@UserId private val userId: String,
private val defaultKeysBackupService: DefaultKeysBackupService) {
private val keysBackupManager: KeysBackupManager
) {
fun handle(trustLevel: DeviceTrustLevel, userId: String, deviceId: String) {
val device = cryptoStore.getUserDevice(userId, deviceId)
@@ -42,7 +43,7 @@ internal class SetDeviceVerificationAction @Inject constructor(
// If one of the user's own devices is being marked as verified / unverified,
// check the key backup status, since whether or not we use this depends on
// whether it has a signature from a verified device
defaultKeysBackupService.checkAndStartKeysBackup()
keysBackupManager.checkAndStartKeysBackup()
}
}

View File

@@ -21,7 +21,6 @@ import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.internal.crypto.IncomingRoomKeyRequest
import org.matrix.android.sdk.internal.crypto.IncomingSecretShareRequest
import org.matrix.android.sdk.internal.crypto.MXEventDecryptionResult
import org.matrix.android.sdk.internal.crypto.keysbackup.DefaultKeysBackupService
/**
* An interface for decrypting data
@@ -42,8 +41,9 @@ internal interface IMXDecrypting {
* Handle a key event.
*
* @param event the key event.
* @return true if the key should be backed up
*/
fun onRoomKeyEvent(event: Event, defaultKeysBackupService: DefaultKeysBackupService) {}
fun onRoomKeyEvent(event: Event): Boolean = false
/**
* Check if the some messages can be decrypted with a new session

View File

@@ -30,7 +30,6 @@ import org.matrix.android.sdk.internal.crypto.actions.EnsureOlmSessionsForDevice
import org.matrix.android.sdk.internal.crypto.actions.MessageEncrypter
import org.matrix.android.sdk.internal.crypto.algorithms.IMXDecrypting
import org.matrix.android.sdk.internal.crypto.algorithms.IMXWithHeldExtension
import org.matrix.android.sdk.internal.crypto.keysbackup.DefaultKeysBackupService
import org.matrix.android.sdk.internal.crypto.model.MXUsersDevicesMap
import org.matrix.android.sdk.internal.crypto.model.event.EncryptedEventContent
import org.matrix.android.sdk.internal.crypto.model.event.RoomKeyContent
@@ -229,10 +228,10 @@ internal class MXMegolmDecryption(private val userId: String,
*
* @param event the key event.
*/
override fun onRoomKeyEvent(event: Event, defaultKeysBackupService: DefaultKeysBackupService) {
override fun onRoomKeyEvent(event: Event): Boolean {
Timber.v("## CRYPTO | onRoomKeyEvent()")
var exportFormat = false
val roomKeyContent = event.getClearContent().toModel<RoomKeyContent>() ?: return
val roomKeyContent = event.getClearContent().toModel<RoomKeyContent>() ?: return false
var senderKey: String? = event.getSenderKey()
var keysClaimed: MutableMap<String, String> = HashMap()
@@ -240,12 +239,12 @@ internal class MXMegolmDecryption(private val userId: String,
if (roomKeyContent.roomId.isNullOrEmpty() || roomKeyContent.sessionId.isNullOrEmpty() || roomKeyContent.sessionKey.isNullOrEmpty()) {
Timber.e("## CRYPTO | onRoomKeyEvent() : Key event is missing fields")
return
return false
}
if (event.getClearType() == EventType.FORWARDED_ROOM_KEY) {
Timber.i("## CRYPTO | onRoomKeyEvent(), forward adding key : ${roomKeyContent.roomId}|${roomKeyContent.sessionId}")
val forwardedRoomKeyContent = event.getClearContent().toModel<ForwardedRoomKeyContent>()
?: return
?: return false
forwardedRoomKeyContent.forwardingCurve25519KeyChain?.let {
forwardingCurve25519KeyChain.addAll(it)
@@ -253,7 +252,7 @@ internal class MXMegolmDecryption(private val userId: String,
if (senderKey == null) {
Timber.e("## CRYPTO | onRoomKeyEvent() : event is missing sender_key field")
return
return false
}
forwardingCurve25519KeyChain.add(senderKey)
@@ -262,12 +261,12 @@ internal class MXMegolmDecryption(private val userId: String,
senderKey = forwardedRoomKeyContent.senderKey
if (null == senderKey) {
Timber.e("## CRYPTO | onRoomKeyEvent() : forwarded_room_key event is missing sender_key field")
return
return false
}
if (null == forwardedRoomKeyContent.senderClaimedEd25519Key) {
Timber.e("## CRYPTO | forwarded_room_key_event is missing sender_claimed_ed25519_key field")
return
return false
}
keysClaimed["ed25519"] = forwardedRoomKeyContent.senderClaimedEd25519Key
@@ -275,7 +274,7 @@ internal class MXMegolmDecryption(private val userId: String,
Timber.i("## CRYPTO | onRoomKeyEvent(), Adding key : ${roomKeyContent.roomId}|${roomKeyContent.sessionId}")
if (null == senderKey) {
Timber.e("## onRoomKeyEvent() : key event has no sender key (not encrypted?)")
return
return false
}
// inherit the claimed ed25519 key from the setup message
@@ -292,8 +291,6 @@ internal class MXMegolmDecryption(private val userId: String,
exportFormat)
if (added) {
defaultKeysBackupService.maybeBackupKeys()
val content = RoomKeyRequestBody(
algorithm = roomKeyContent.algorithm,
roomId = roomKeyContent.roomId,
@@ -305,6 +302,8 @@ internal class MXMegolmDecryption(private val userId: String,
onNewSession(senderKey, roomKeyContent.sessionId)
}
return added
}
/**

View File

@@ -29,7 +29,7 @@ import org.matrix.android.sdk.internal.crypto.actions.EnsureOlmSessionsForDevice
import org.matrix.android.sdk.internal.crypto.actions.MessageEncrypter
import org.matrix.android.sdk.internal.crypto.algorithms.IMXEncrypting
import org.matrix.android.sdk.internal.crypto.algorithms.IMXGroupEncryption
import org.matrix.android.sdk.internal.crypto.keysbackup.DefaultKeysBackupService
import org.matrix.android.sdk.internal.crypto.keysbackup.KeysBackupManager
import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo
import org.matrix.android.sdk.internal.crypto.model.MXUsersDevicesMap
import org.matrix.android.sdk.internal.crypto.model.event.RoomKeyWithHeldContent
@@ -47,7 +47,7 @@ internal class MXMegolmEncryption(
// The id of the room we will be sending to.
private val roomId: String,
private val olmDevice: MXOlmDevice,
private val defaultKeysBackupService: DefaultKeysBackupService,
private val keysBackupManager: KeysBackupManager,
private val cryptoStore: IMXCryptoStore,
private val deviceListManager: DeviceListManager,
private val ensureOlmSessionsForDevicesAction: EnsureOlmSessionsForDevicesAction,
@@ -142,7 +142,7 @@ internal class MXMegolmEncryption(
olmDevice.addInboundGroupSession(sessionId!!, olmDevice.getSessionKey(sessionId)!!, roomId, olmDevice.deviceCurve25519Key!!,
emptyList(), keysClaimedMap, false)
defaultKeysBackupService.maybeBackupKeys()
keysBackupManager.maybeBackupKeys()
return MXOutboundSessionInfo(sessionId, SharedWithHelper(roomId, sessionId, cryptoStore))
}

View File

@@ -21,7 +21,7 @@ import org.matrix.android.sdk.internal.crypto.DeviceListManager
import org.matrix.android.sdk.internal.crypto.MXOlmDevice
import org.matrix.android.sdk.internal.crypto.actions.EnsureOlmSessionsForDevicesAction
import org.matrix.android.sdk.internal.crypto.actions.MessageEncrypter
import org.matrix.android.sdk.internal.crypto.keysbackup.DefaultKeysBackupService
import org.matrix.android.sdk.internal.crypto.keysbackup.KeysBackupManager
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.SendToDeviceTask
@@ -32,7 +32,7 @@ import javax.inject.Inject
internal class MXMegolmEncryptionFactory @Inject constructor(
private val olmDevice: MXOlmDevice,
private val defaultKeysBackupService: DefaultKeysBackupService,
private val keysBackupManager: KeysBackupManager,
private val cryptoStore: IMXCryptoStore,
private val deviceListManager: DeviceListManager,
private val ensureOlmSessionsForDevicesAction: EnsureOlmSessionsForDevicesAction,
@@ -48,7 +48,7 @@ internal class MXMegolmEncryptionFactory @Inject constructor(
return MXMegolmEncryption(
roomId = roomId,
olmDevice = olmDevice,
defaultKeysBackupService = defaultKeysBackupService,
keysBackupManager = keysBackupManager,
cryptoStore = cryptoStore,
deviceListManager = deviceListManager,
ensureOlmSessionsForDevicesAction = ensureOlmSessionsForDevicesAction,

View File

@@ -0,0 +1,809 @@
/*
* 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 androidx.lifecycle.LiveData
import androidx.work.BackoffPolicy
import androidx.work.ExistingWorkPolicy
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.MatrixCallback
import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
import org.matrix.android.sdk.api.extensions.orFalse
import org.matrix.android.sdk.api.session.crypto.crosssigning.CrossSigningService
import org.matrix.android.sdk.api.session.crypto.crosssigning.MXCrossSigningInfo
import org.matrix.android.sdk.api.util.Optional
import org.matrix.android.sdk.internal.crypto.DeviceListManager
import org.matrix.android.sdk.internal.crypto.UserDevicesUpdateListener
import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo
import org.matrix.android.sdk.internal.crypto.model.rest.UploadSignatureQueryBuilder
import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
import org.matrix.android.sdk.internal.crypto.store.PrivateKeysInfo
import org.matrix.android.sdk.internal.crypto.tasks.InitializeCrossSigningTask
import org.matrix.android.sdk.internal.crypto.tasks.UploadSignaturesTask
import org.matrix.android.sdk.internal.di.SessionId
import org.matrix.android.sdk.internal.di.UserId
import org.matrix.android.sdk.internal.di.WorkManagerProvider
import org.matrix.android.sdk.internal.session.SessionScope
import org.matrix.android.sdk.internal.task.TaskExecutor
import org.matrix.android.sdk.internal.task.TaskThread
import org.matrix.android.sdk.internal.task.configureWith
import org.matrix.android.sdk.internal.util.JsonCanonicalizer
import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers
import org.matrix.android.sdk.internal.util.logLimit
import org.matrix.android.sdk.internal.worker.WorkerParamsFactory
import org.matrix.olm.OlmPkSigning
import org.matrix.olm.OlmUtility
import timber.log.Timber
import java.util.concurrent.TimeUnit
import javax.inject.Inject
@SessionScope
internal class CrossSigningManager @Inject constructor(
@UserId private val userId: String,
@SessionId private val sessionId: String,
private val cryptoStore: IMXCryptoStore,
private val deviceListManager: DeviceListManager,
private val initializeCrossSigningTask: InitializeCrossSigningTask,
private val uploadSignaturesTask: UploadSignaturesTask,
private val taskExecutor: TaskExecutor,
private val coroutineDispatchers: MatrixCoroutineDispatchers,
private val cryptoCoroutineScope: CoroutineScope,
private val workManagerProvider: WorkManagerProvider,
private val updateTrustWorkerDataRepository: UpdateTrustWorkerDataRepository
) : CrossSigningService,
CrossSigningManagerInput,
UserDevicesUpdateListener {
private var olmUtility: OlmUtility? = null
private var masterPkSigning: OlmPkSigning? = null
private var userPkSigning: OlmPkSigning? = null
private var selfSigningPkSigning: OlmPkSigning? = null
init {
try {
olmUtility = OlmUtility()
// Try to get stored keys if they exist
cryptoStore.getMyCrossSigningInfo()?.let { mxCrossSigningInfo ->
Timber.i("## CrossSigning - Found Existing self signed keys")
Timber.i("## CrossSigning - Checking if private keys are known")
cryptoStore.getCrossSigningPrivateKeys()?.let { privateKeysInfo ->
privateKeysInfo.master
?.fromBase64()
?.let { privateKeySeed ->
val pkSigning = OlmPkSigning()
if (pkSigning.initWithSeed(privateKeySeed) == mxCrossSigningInfo.masterKey()?.unpaddedBase64PublicKey) {
masterPkSigning = pkSigning
Timber.i("## CrossSigning - Loading master key success")
} else {
Timber.w("## CrossSigning - Public master key does not match the private key")
pkSigning.releaseSigning()
// TODO untrust?
}
}
privateKeysInfo.user
?.fromBase64()
?.let { privateKeySeed ->
val pkSigning = OlmPkSigning()
if (pkSigning.initWithSeed(privateKeySeed) == mxCrossSigningInfo.userKey()?.unpaddedBase64PublicKey) {
userPkSigning = pkSigning
Timber.i("## CrossSigning - Loading User Signing key success")
} else {
Timber.w("## CrossSigning - Public User key does not match the private key")
pkSigning.releaseSigning()
// TODO untrust?
}
}
privateKeysInfo.selfSigned
?.fromBase64()
?.let { privateKeySeed ->
val pkSigning = OlmPkSigning()
if (pkSigning.initWithSeed(privateKeySeed) == mxCrossSigningInfo.selfSigningKey()?.unpaddedBase64PublicKey) {
selfSigningPkSigning = pkSigning
Timber.i("## CrossSigning - Loading Self Signing key success")
} else {
Timber.w("## CrossSigning - Public Self Signing key does not match the private key")
pkSigning.releaseSigning()
// TODO untrust?
}
}
}
// Recover local trust in case private key are there?
setUserKeysAsTrusted(userId, checkUserTrust(userId).isVerified())
}
} catch (e: Throwable) {
// Mmm this kind of a big issue
Timber.e(e, "Failed to initialize Cross Signing")
}
deviceListManager.addListener(this)
}
fun release() {
olmUtility?.releaseUtility()
listOf(masterPkSigning, userPkSigning, selfSigningPkSigning).forEach { it?.releaseSigning() }
deviceListManager.removeListener(this)
}
protected fun finalize() {
release()
}
/**
* - Make 3 key pairs (MSK, USK, SSK)
* - Save the private keys with proper security
* - Sign the keys and upload them
* - Sign the current device with SSK and sign MSK with device key (migration) and upload signatures
*/
override fun initializeCrossSigning(uiaInterceptor: UserInteractiveAuthInterceptor?, callback: MatrixCallback<Unit>) {
Timber.d("## CrossSigning initializeCrossSigning")
val params = InitializeCrossSigningTask.Params(
interactiveAuthInterceptor = uiaInterceptor
)
initializeCrossSigningTask.configureWith(params) {
this.callbackThread = TaskThread.CRYPTO
this.callback = object : MatrixCallback<InitializeCrossSigningTask.Result> {
override fun onFailure(failure: Throwable) {
Timber.e(failure, "Error in initializeCrossSigning()")
callback.onFailure(failure)
}
override fun onSuccess(data: InitializeCrossSigningTask.Result) {
val crossSigningInfo = MXCrossSigningInfo(userId, listOf(data.masterKeyInfo, data.userKeyInfo, data.selfSignedKeyInfo))
cryptoStore.setMyCrossSigningInfo(crossSigningInfo)
setUserKeysAsTrusted(userId, true)
cryptoStore.storePrivateKeysInfo(data.masterKeyPK, data.userKeyPK, data.selfSigningKeyPK)
masterPkSigning = OlmPkSigning().apply { initWithSeed(data.masterKeyPK.fromBase64()) }
userPkSigning = OlmPkSigning().apply { initWithSeed(data.userKeyPK.fromBase64()) }
selfSigningPkSigning = OlmPkSigning().apply { initWithSeed(data.selfSigningKeyPK.fromBase64()) }
callback.onSuccess(Unit)
}
}
}.executeBy(taskExecutor)
}
override fun onSecretMSKGossip(mskPrivateKey: String) {
Timber.i("## CrossSigning - onSecretSSKGossip")
val mxCrossSigningInfo = getMyCrossSigningKeys() ?: return Unit.also {
Timber.e("## CrossSigning - onSecretMSKGossip() received secret but public key is not known")
}
mskPrivateKey.fromBase64()
.let { privateKeySeed ->
val pkSigning = OlmPkSigning()
try {
if (pkSigning.initWithSeed(privateKeySeed) == mxCrossSigningInfo.masterKey()?.unpaddedBase64PublicKey) {
masterPkSigning?.releaseSigning()
masterPkSigning = pkSigning
Timber.i("## CrossSigning - Loading MSK success")
cryptoStore.storeMSKPrivateKey(mskPrivateKey)
return
} else {
Timber.e("## CrossSigning - onSecretMSKGossip() private key do not match public key")
pkSigning.releaseSigning()
}
} catch (failure: Throwable) {
Timber.e("## CrossSigning - onSecretMSKGossip() ${failure.localizedMessage}")
pkSigning.releaseSigning()
}
}
}
override fun onSecretSSKGossip(sskPrivateKey: String) {
Timber.i("## CrossSigning - onSecretSSKGossip")
val mxCrossSigningInfo = getMyCrossSigningKeys() ?: return Unit.also {
Timber.e("## CrossSigning - onSecretSSKGossip() received secret but public key is not known")
}
sskPrivateKey.fromBase64()
.let { privateKeySeed ->
val pkSigning = OlmPkSigning()
try {
if (pkSigning.initWithSeed(privateKeySeed) == mxCrossSigningInfo.selfSigningKey()?.unpaddedBase64PublicKey) {
selfSigningPkSigning?.releaseSigning()
selfSigningPkSigning = pkSigning
Timber.i("## CrossSigning - Loading SSK success")
cryptoStore.storeSSKPrivateKey(sskPrivateKey)
return
} else {
Timber.e("## CrossSigning - onSecretSSKGossip() private key do not match public key")
pkSigning.releaseSigning()
}
} catch (failure: Throwable) {
Timber.e("## CrossSigning - onSecretSSKGossip() ${failure.localizedMessage}")
pkSigning.releaseSigning()
}
}
}
override fun onSecretUSKGossip(uskPrivateKey: String) {
Timber.i("## CrossSigning - onSecretUSKGossip")
val mxCrossSigningInfo = getMyCrossSigningKeys() ?: return Unit.also {
Timber.e("## CrossSigning - onSecretUSKGossip() received secret but public key is not knwow ")
}
uskPrivateKey.fromBase64()
.let { privateKeySeed ->
val pkSigning = OlmPkSigning()
try {
if (pkSigning.initWithSeed(privateKeySeed) == mxCrossSigningInfo.userKey()?.unpaddedBase64PublicKey) {
userPkSigning?.releaseSigning()
userPkSigning = pkSigning
Timber.i("## CrossSigning - Loading USK success")
cryptoStore.storeUSKPrivateKey(uskPrivateKey)
return
} else {
Timber.e("## CrossSigning - onSecretUSKGossip() private key do not match public key")
pkSigning.releaseSigning()
}
} catch (failure: Throwable) {
pkSigning.releaseSigning()
}
}
}
override fun checkTrustFromPrivateKeys(masterKeyPrivateKey: String?,
uskKeyPrivateKey: String?,
sskPrivateKey: String?
): UserTrustResult {
val mxCrossSigningInfo = getMyCrossSigningKeys() ?: return UserTrustResult.CrossSigningNotConfigured(userId)
var masterKeyIsTrusted = false
var userKeyIsTrusted = false
var selfSignedKeyIsTrusted = false
masterKeyPrivateKey?.fromBase64()
?.let { privateKeySeed ->
val pkSigning = OlmPkSigning()
try {
if (pkSigning.initWithSeed(privateKeySeed) == mxCrossSigningInfo.masterKey()?.unpaddedBase64PublicKey) {
masterPkSigning?.releaseSigning()
masterPkSigning = pkSigning
masterKeyIsTrusted = true
Timber.i("## CrossSigning - Loading master key success")
} else {
pkSigning.releaseSigning()
}
} catch (failure: Throwable) {
pkSigning.releaseSigning()
}
}
uskKeyPrivateKey?.fromBase64()
?.let { privateKeySeed ->
val pkSigning = OlmPkSigning()
try {
if (pkSigning.initWithSeed(privateKeySeed) == mxCrossSigningInfo.userKey()?.unpaddedBase64PublicKey) {
userPkSigning?.releaseSigning()
userPkSigning = pkSigning
userKeyIsTrusted = true
Timber.i("## CrossSigning - Loading master key success")
} else {
pkSigning.releaseSigning()
}
} catch (failure: Throwable) {
pkSigning.releaseSigning()
}
}
sskPrivateKey?.fromBase64()
?.let { privateKeySeed ->
val pkSigning = OlmPkSigning()
try {
if (pkSigning.initWithSeed(privateKeySeed) == mxCrossSigningInfo.selfSigningKey()?.unpaddedBase64PublicKey) {
selfSigningPkSigning?.releaseSigning()
selfSigningPkSigning = pkSigning
selfSignedKeyIsTrusted = true
Timber.i("## CrossSigning - Loading master key success")
} else {
pkSigning.releaseSigning()
}
} catch (failure: Throwable) {
pkSigning.releaseSigning()
}
}
if (!masterKeyIsTrusted || !userKeyIsTrusted || !selfSignedKeyIsTrusted) {
return UserTrustResult.KeysNotTrusted(mxCrossSigningInfo)
} else {
cryptoStore.markMyMasterKeyAsLocallyTrusted(true)
val checkSelfTrust = checkSelfTrust()
if (checkSelfTrust.isVerified()) {
cryptoStore.storePrivateKeysInfo(masterKeyPrivateKey, uskKeyPrivateKey, sskPrivateKey)
setUserKeysAsTrusted(userId, true)
}
return checkSelfTrust
}
}
/**
*
* ┏━━━━━━━━┓ ┏━━━━━━━━┓
* ┃ ALICE ┃ ┃ BOB ┃
* ┗━━━━━━━━┛ ┗━━━━━━━━┛
* MSK ┌────────────▶ MSK
* │
* │ │
* │ SSK │
* │ │
* │ │
* └──▶ USK ────────────┘
*/
override fun isUserTrusted(otherUserId: String): Boolean {
return cryptoStore.getCrossSigningInfo(userId)?.isTrusted() == true
}
override fun isCrossSigningVerified(): Boolean {
return checkSelfTrust().isVerified()
}
/**
* Will not force a download of the key, but will verify signatures trust chain
*/
override fun checkUserTrust(otherUserId: String): UserTrustResult {
Timber.v("## CrossSigning checkUserTrust for $otherUserId")
if (otherUserId == userId) {
return checkSelfTrust()
}
// I trust a user if I trust his master key
// I can trust the master key if it is signed by my user key
// TODO what if the master key is signed by a device key that i have verified
// First let's get my user key
val myCrossSigningInfo = cryptoStore.getCrossSigningInfo(userId)
checkOtherMSKTrusted(myCrossSigningInfo, cryptoStore.getCrossSigningInfo(otherUserId))
return UserTrustResult.Success
}
fun checkOtherMSKTrusted(myCrossSigningInfo: MXCrossSigningInfo?, otherInfo: MXCrossSigningInfo?): UserTrustResult {
val myUserKey = myCrossSigningInfo?.userKey()
?: return UserTrustResult.CrossSigningNotConfigured(userId)
if (!myCrossSigningInfo.isTrusted()) {
return UserTrustResult.KeysNotTrusted(myCrossSigningInfo)
}
// Let's get the other user master key
val otherMasterKey = otherInfo?.masterKey()
?: return UserTrustResult.UnknownCrossSignatureInfo(otherInfo?.userId ?: "")
val masterKeySignaturesMadeByMyUserKey = otherMasterKey.signatures
?.get(userId) // Signatures made by me
?.get("ed25519:${myUserKey.unpaddedBase64PublicKey}")
if (masterKeySignaturesMadeByMyUserKey.isNullOrBlank()) {
Timber.d("## CrossSigning checkUserTrust false for ${otherInfo.userId}, not signed by my UserSigningKey")
return UserTrustResult.KeyNotSigned(otherMasterKey)
}
// Check that Alice USK signature of Bob MSK is valid
try {
olmUtility!!.verifyEd25519Signature(masterKeySignaturesMadeByMyUserKey, myUserKey.unpaddedBase64PublicKey, otherMasterKey.canonicalSignable())
} catch (failure: Throwable) {
return UserTrustResult.InvalidSignature(myUserKey, masterKeySignaturesMadeByMyUserKey)
}
return UserTrustResult.Success
}
private fun checkSelfTrust(): UserTrustResult {
// Special case when it's me,
// I have to check that MSK -> USK -> SSK
// and that MSK is trusted (i know the private key, or is signed by a trusted device)
val myCrossSigningInfo = cryptoStore.getCrossSigningInfo(userId)
return checkSelfTrust(myCrossSigningInfo, cryptoStore.getUserDeviceList(userId))
}
fun checkSelfTrust(myCrossSigningInfo: MXCrossSigningInfo?, myDevices: List<CryptoDeviceInfo>?): UserTrustResult {
// Special case when it's me,
// I have to check that MSK -> USK -> SSK
// and that MSK is trusted (i know the private key, or is signed by a trusted device)
// val myCrossSigningInfo = cryptoStore.getCrossSigningInfo(userId)
val myMasterKey = myCrossSigningInfo?.masterKey()
?: return UserTrustResult.CrossSigningNotConfigured(userId)
// Is the master key trusted
// 1) check if I know the private key
val masterPrivateKey = cryptoStore.getCrossSigningPrivateKeys()
?.master
?.fromBase64()
var isMaterKeyTrusted = false
if (myMasterKey.trustLevel?.locallyVerified == true) {
isMaterKeyTrusted = true
} else if (masterPrivateKey != null) {
// Check if private match public
var olmPkSigning: OlmPkSigning? = null
try {
olmPkSigning = OlmPkSigning()
val expectedPK = olmPkSigning.initWithSeed(masterPrivateKey)
isMaterKeyTrusted = myMasterKey.unpaddedBase64PublicKey == expectedPK
} catch (failure: Throwable) {
Timber.e(failure)
}
olmPkSigning?.releaseSigning()
} else {
// Maybe it's signed by a locally trusted device?
myMasterKey.signatures?.get(userId)?.forEach { (key, value) ->
val potentialDeviceId = key.removePrefix("ed25519:")
val potentialDevice = myDevices?.firstOrNull { it.deviceId == potentialDeviceId } // cryptoStore.getUserDevice(userId, potentialDeviceId)
if (potentialDevice != null && potentialDevice.isVerified) {
// Check signature validity?
try {
olmUtility?.verifyEd25519Signature(value, potentialDevice.fingerprint(), myMasterKey.canonicalSignable())
isMaterKeyTrusted = true
return@forEach
} catch (failure: Throwable) {
// log
Timber.w(failure, "Signature not valid?")
}
}
}
}
if (!isMaterKeyTrusted) {
return UserTrustResult.KeysNotTrusted(myCrossSigningInfo)
}
val myUserKey = myCrossSigningInfo.userKey()
?: return UserTrustResult.CrossSigningNotConfigured(userId)
val userKeySignaturesMadeByMyMasterKey = myUserKey.signatures
?.get(userId) // Signatures made by me
?.get("ed25519:${myMasterKey.unpaddedBase64PublicKey}")
if (userKeySignaturesMadeByMyMasterKey.isNullOrBlank()) {
Timber.d("## CrossSigning checkUserTrust false for $userId, USK not signed by MSK")
return UserTrustResult.KeyNotSigned(myUserKey)
}
// Check that Alice USK signature of Alice MSK is valid
try {
olmUtility!!.verifyEd25519Signature(userKeySignaturesMadeByMyMasterKey, myMasterKey.unpaddedBase64PublicKey, myUserKey.canonicalSignable())
} catch (failure: Throwable) {
return UserTrustResult.InvalidSignature(myUserKey, userKeySignaturesMadeByMyMasterKey)
}
val mySSKey = myCrossSigningInfo.selfSigningKey()
?: return UserTrustResult.CrossSigningNotConfigured(userId)
val ssKeySignaturesMadeByMyMasterKey = mySSKey.signatures
?.get(userId) // Signatures made by me
?.get("ed25519:${myMasterKey.unpaddedBase64PublicKey}")
if (ssKeySignaturesMadeByMyMasterKey.isNullOrBlank()) {
Timber.d("## CrossSigning checkUserTrust false for $userId, SSK not signed by MSK")
return UserTrustResult.KeyNotSigned(mySSKey)
}
// Check that Alice USK signature of Alice MSK is valid
try {
olmUtility!!.verifyEd25519Signature(ssKeySignaturesMadeByMyMasterKey, myMasterKey.unpaddedBase64PublicKey, mySSKey.canonicalSignable())
} catch (failure: Throwable) {
return UserTrustResult.InvalidSignature(mySSKey, ssKeySignaturesMadeByMyMasterKey)
}
return UserTrustResult.Success
}
override fun getUserCrossSigningKeys(otherUserId: String): MXCrossSigningInfo? {
return cryptoStore.getCrossSigningInfo(otherUserId)
}
override fun getLiveCrossSigningKeys(userId: String): LiveData<Optional<MXCrossSigningInfo>> {
return cryptoStore.getLiveCrossSigningInfo(userId)
}
override fun getMyCrossSigningKeys(): MXCrossSigningInfo? {
return cryptoStore.getMyCrossSigningInfo()
}
override fun getCrossSigningPrivateKeys(): PrivateKeysInfo? {
return cryptoStore.getCrossSigningPrivateKeys()
}
override fun getLiveCrossSigningPrivateKeys(): LiveData<Optional<PrivateKeysInfo>> {
return cryptoStore.getLiveCrossSigningPrivateKeys()
}
override fun canCrossSign(): Boolean {
return checkSelfTrust().isVerified() && cryptoStore.getCrossSigningPrivateKeys()?.selfSigned != null
&& cryptoStore.getCrossSigningPrivateKeys()?.user != null
}
override fun allPrivateKeysKnown(): Boolean {
return checkSelfTrust().isVerified()
&& cryptoStore.getCrossSigningPrivateKeys()?.allKnown().orFalse()
}
override fun trustUser(otherUserId: String, callback: MatrixCallback<Unit>) {
cryptoCoroutineScope.launch(coroutineDispatchers.crypto) {
Timber.d("## CrossSigning - Mark user $userId as trusted ")
// We should have this user keys
val otherMasterKeys = getUserCrossSigningKeys(otherUserId)?.masterKey()
if (otherMasterKeys == null) {
callback.onFailure(Throwable("## CrossSigning - Other master signing key is not known"))
return@launch
}
val myKeys = getUserCrossSigningKeys(userId)
if (myKeys == null) {
callback.onFailure(Throwable("## CrossSigning - CrossSigning is not setup for this account"))
return@launch
}
val userPubKey = myKeys.userKey()?.unpaddedBase64PublicKey
if (userPubKey == null || userPkSigning == null) {
callback.onFailure(Throwable("## CrossSigning - Cannot sign from this account, privateKeyUnknown $userPubKey"))
return@launch
}
// Sign the other MasterKey with our UserSigning key
val newSignature = JsonCanonicalizer.getCanonicalJson(Map::class.java,
otherMasterKeys.signalableJSONDictionary()).let { userPkSigning?.sign(it) }
if (newSignature == null) {
// race??
callback.onFailure(Throwable("## CrossSigning - Failed to sign"))
return@launch
}
cryptoStore.setUserKeysAsTrusted(otherUserId, true)
// TODO update local copy with new signature directly here? kind of local echo of trust?
Timber.d("## CrossSigning - Upload signature of $userId MSK signed by USK")
val uploadQuery = UploadSignatureQueryBuilder()
.withSigningKeyInfo(otherMasterKeys.copyForSignature(userId, userPubKey, newSignature))
.build()
uploadSignaturesTask.configureWith(UploadSignaturesTask.Params(uploadQuery)) {
this.executionThread = TaskThread.CRYPTO
this.callback = callback
}.executeBy(taskExecutor)
}
}
override fun markMyMasterKeyAsTrusted() {
cryptoCoroutineScope.launch(coroutineDispatchers.crypto) {
cryptoStore.markMyMasterKeyAsLocallyTrusted(true)
checkSelfTrust()
// re-verify all trusts
onUsersDeviceUpdate(listOf(userId))
}
}
override fun trustDevice(deviceId: String, callback: MatrixCallback<Unit>) {
cryptoCoroutineScope.launch(coroutineDispatchers.crypto) {
// This device should be yours
val device = cryptoStore.getUserDevice(userId, deviceId)
if (device == null) {
callback.onFailure(IllegalArgumentException("This device [$deviceId] is not known, or not yours"))
return@launch
}
val myKeys = getUserCrossSigningKeys(userId)
if (myKeys == null) {
callback.onFailure(Throwable("CrossSigning is not setup for this account"))
return@launch
}
val ssPubKey = myKeys.selfSigningKey()?.unpaddedBase64PublicKey
if (ssPubKey == null || selfSigningPkSigning == null) {
callback.onFailure(Throwable("Cannot sign from this account, public and/or privateKey Unknown $ssPubKey"))
return@launch
}
// Sign with self signing
val newSignature = selfSigningPkSigning?.sign(device.canonicalSignable())
if (newSignature == null) {
// race??
callback.onFailure(Throwable("Failed to sign"))
return@launch
}
val toUpload = device.copy(
signatures = mapOf(
userId
to
mapOf(
"ed25519:$ssPubKey" to newSignature
)
)
)
val uploadQuery = UploadSignatureQueryBuilder()
.withDeviceInfo(toUpload)
.build()
uploadSignaturesTask.configureWith(UploadSignaturesTask.Params(uploadQuery)) {
this.executionThread = TaskThread.CRYPTO
this.callback = callback
}.executeBy(taskExecutor)
}
}
override fun checkDeviceTrust(otherUserId: String, otherDeviceId: String, locallyTrusted: Boolean?): DeviceTrustResult {
val otherDevice = cryptoStore.getUserDevice(otherUserId, otherDeviceId)
?: return DeviceTrustResult.UnknownDevice(otherDeviceId)
val myKeys = getUserCrossSigningKeys(userId)
?: return legacyFallbackTrust(locallyTrusted, DeviceTrustResult.CrossSigningNotConfigured(userId))
if (!myKeys.isTrusted()) return legacyFallbackTrust(locallyTrusted, DeviceTrustResult.KeysNotTrusted(myKeys))
val otherKeys = getUserCrossSigningKeys(otherUserId)
?: return legacyFallbackTrust(locallyTrusted, DeviceTrustResult.CrossSigningNotConfigured(otherUserId))
// TODO should we force verification ?
if (!otherKeys.isTrusted()) return legacyFallbackTrust(locallyTrusted, DeviceTrustResult.KeysNotTrusted(otherKeys))
// Check if the trust chain is valid
/*
* ┏━━━━━━━━┓ ┏━━━━━━━━┓
* ┃ ALICE ┃ ┃ BOB ┃
* ┗━━━━━━━━┛ ┗━━━━━━━━┛
* MSK ┌────────────▶MSK
* │
* │ │ │
* │ SSK │ └──▶ SSK ──────────────────┐
* │ │ │
* │ │ USK │
* └──▶ USK ────────────┘ (not visible by │
* Alice) │
* ▼
* ┌──────────────┐
* │ BOB's Device │
* └──────────────┘
*/
val otherSSKSignature = otherDevice.signatures?.get(otherUserId)?.get("ed25519:${otherKeys.selfSigningKey()?.unpaddedBase64PublicKey}")
?: return legacyFallbackTrust(
locallyTrusted,
DeviceTrustResult.MissingDeviceSignature(otherDeviceId, otherKeys.selfSigningKey()
?.unpaddedBase64PublicKey
?: ""
)
)
// Check bob's device is signed by bob's SSK
try {
olmUtility!!.verifyEd25519Signature(otherSSKSignature, otherKeys.selfSigningKey()?.unpaddedBase64PublicKey, otherDevice.canonicalSignable())
} catch (e: Throwable) {
return legacyFallbackTrust(locallyTrusted, DeviceTrustResult.InvalidDeviceSignature(otherDeviceId, otherSSKSignature, e))
}
return DeviceTrustResult.Success(DeviceTrustLevel(crossSigningVerified = true, locallyVerified = locallyTrusted))
}
fun checkDeviceTrust(myKeys: MXCrossSigningInfo?, otherKeys: MXCrossSigningInfo?, otherDevice: CryptoDeviceInfo): DeviceTrustResult {
val locallyTrusted = otherDevice.trustLevel?.isLocallyVerified()
myKeys ?: return legacyFallbackTrust(locallyTrusted, DeviceTrustResult.CrossSigningNotConfigured(userId))
if (!myKeys.isTrusted()) return legacyFallbackTrust(locallyTrusted, DeviceTrustResult.KeysNotTrusted(myKeys))
otherKeys ?: return legacyFallbackTrust(locallyTrusted, DeviceTrustResult.CrossSigningNotConfigured(otherDevice.userId))
// TODO should we force verification ?
if (!otherKeys.isTrusted()) return legacyFallbackTrust(locallyTrusted, DeviceTrustResult.KeysNotTrusted(otherKeys))
// Check if the trust chain is valid
/*
* ┏━━━━━━━━┓ ┏━━━━━━━━┓
* ┃ ALICE ┃ ┃ BOB ┃
* ┗━━━━━━━━┛ ┗━━━━━━━━┛
* MSK ┌────────────▶MSK
* │
* │ │ │
* │ SSK │ └──▶ SSK ──────────────────┐
* │ │ │
* │ │ USK │
* └──▶ USK ────────────┘ (not visible by │
* Alice) │
* ▼
* ┌──────────────┐
* │ BOB's Device │
* └──────────────┘
*/
val otherSSKSignature = otherDevice.signatures?.get(otherKeys.userId)?.get("ed25519:${otherKeys.selfSigningKey()?.unpaddedBase64PublicKey}")
?: return legacyFallbackTrust(
locallyTrusted,
DeviceTrustResult.MissingDeviceSignature(otherDevice.deviceId, otherKeys.selfSigningKey()
?.unpaddedBase64PublicKey
?: ""
)
)
// Check bob's device is signed by bob's SSK
try {
olmUtility!!.verifyEd25519Signature(otherSSKSignature, otherKeys.selfSigningKey()?.unpaddedBase64PublicKey, otherDevice.canonicalSignable())
} catch (e: Throwable) {
return legacyFallbackTrust(locallyTrusted, DeviceTrustResult.InvalidDeviceSignature(otherDevice.deviceId, otherSSKSignature, e))
}
return DeviceTrustResult.Success(DeviceTrustLevel(crossSigningVerified = true, locallyVerified = locallyTrusted))
}
private fun legacyFallbackTrust(locallyTrusted: Boolean?, crossSignTrustFail: DeviceTrustResult): DeviceTrustResult {
return if (locallyTrusted == true) {
DeviceTrustResult.Success(DeviceTrustLevel(crossSigningVerified = false, locallyVerified = true))
} else {
crossSignTrustFail
}
}
override fun onUsersDeviceUpdate(userIds: List<String>) {
Timber.d("## CrossSigning - onUsersDeviceUpdate for users: ${userIds.logLimit()}")
val workerParams = UpdateTrustWorker.Params(
sessionId = sessionId,
filename = updateTrustWorkerDataRepository.createParam(userIds)
)
val workerData = WorkerParamsFactory.toData(workerParams)
val workRequest = workManagerProvider.matrixOneTimeWorkRequestBuilder<UpdateTrustWorker>()
.setInputData(workerData)
.setBackoffCriteria(BackoffPolicy.LINEAR, WorkManagerProvider.BACKOFF_DELAY_MILLIS, TimeUnit.MILLISECONDS)
.build()
workManagerProvider.workManager
.beginUniqueWork("TRUST_UPDATE_QUEUE", ExistingWorkPolicy.APPEND_OR_REPLACE, workRequest)
.enqueue()
}
private fun setUserKeysAsTrusted(otherUserId: String, trusted: Boolean) {
val currentTrust = cryptoStore.getCrossSigningInfo(otherUserId)?.isTrusted()
cryptoStore.setUserKeysAsTrusted(otherUserId, trusted)
// If it's me, recheck trust of all users and devices?
val users = ArrayList<String>()
if (otherUserId == userId && currentTrust != trusted) {
// reRequestAllPendingRoomKeyRequest()
cryptoStore.updateUsersTrust {
users.add(it)
checkUserTrust(it).isVerified()
}
users.forEach {
cryptoStore.getUserDeviceList(it)?.forEach { device ->
val updatedTrust = checkDeviceTrust(it, device.deviceId, device.trustLevel?.isLocallyVerified() ?: false)
Timber.v("## CrossSigning - update trust for device ${device.deviceId} of user $otherUserId , verified=$updatedTrust")
cryptoStore.setDeviceTrust(it, device.deviceId, updatedTrust.isCrossSignedVerified(), updatedTrust.isLocallyVerified())
}
}
}
}
// private fun reRequestAllPendingRoomKeyRequest() {
// cryptoCoroutineScope.launch(coroutineDispatchers.crypto) {
// Timber.d("## CrossSigning - reRequest pending outgoing room key requests")
// cryptoStore.getOutgoingRoomKeyRequests().forEach {
// it.requestBody?.let { requestBody ->
// if (cryptoStore.getInboundGroupSession(requestBody.sessionId ?: "", requestBody.senderKey ?: "") == null) {
// outgoingRoomKeyRequestManager.resendRoomKeyRequest(requestBody)
// } else {
// outgoingRoomKeyRequestManager.cancelRoomKeyRequest(requestBody)
// }
// }
// }
// }
// }
}

View File

@@ -0,0 +1,23 @@
/*
* 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.crosssigning
internal interface CrossSigningManagerInput {
fun onSecretMSKGossip(mskPrivateKey: String)
fun onSecretSSKGossip(sskPrivateKey: String)
fun onSecretUSKGossip(uskPrivateKey: String)
}

View File

@@ -16,792 +16,9 @@
package org.matrix.android.sdk.internal.crypto.crosssigning
import androidx.lifecycle.LiveData
import androidx.work.BackoffPolicy
import androidx.work.ExistingWorkPolicy
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.MatrixCallback
import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
import org.matrix.android.sdk.api.extensions.orFalse
import org.matrix.android.sdk.api.session.crypto.crosssigning.CrossSigningService
import org.matrix.android.sdk.api.session.crypto.crosssigning.MXCrossSigningInfo
import org.matrix.android.sdk.api.util.Optional
import org.matrix.android.sdk.internal.crypto.DeviceListManager
import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo
import org.matrix.android.sdk.internal.crypto.model.rest.UploadSignatureQueryBuilder
import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
import org.matrix.android.sdk.internal.crypto.store.PrivateKeysInfo
import org.matrix.android.sdk.internal.crypto.tasks.InitializeCrossSigningTask
import org.matrix.android.sdk.internal.crypto.tasks.UploadSignaturesTask
import org.matrix.android.sdk.internal.di.SessionId
import org.matrix.android.sdk.internal.di.UserId
import org.matrix.android.sdk.internal.di.WorkManagerProvider
import org.matrix.android.sdk.internal.session.SessionScope
import org.matrix.android.sdk.internal.task.TaskExecutor
import org.matrix.android.sdk.internal.task.TaskThread
import org.matrix.android.sdk.internal.task.configureWith
import org.matrix.android.sdk.internal.util.JsonCanonicalizer
import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers
import org.matrix.android.sdk.internal.util.logLimit
import org.matrix.android.sdk.internal.worker.WorkerParamsFactory
import org.matrix.olm.OlmPkSigning
import org.matrix.olm.OlmUtility
import timber.log.Timber
import java.util.concurrent.TimeUnit
import javax.inject.Inject
@SessionScope
internal class DefaultCrossSigningService @Inject constructor(
@UserId private val userId: String,
@SessionId private val sessionId: String,
private val cryptoStore: IMXCryptoStore,
private val deviceListManager: DeviceListManager,
private val initializeCrossSigningTask: InitializeCrossSigningTask,
private val uploadSignaturesTask: UploadSignaturesTask,
private val taskExecutor: TaskExecutor,
private val coroutineDispatchers: MatrixCoroutineDispatchers,
private val cryptoCoroutineScope: CoroutineScope,
private val workManagerProvider: WorkManagerProvider,
private val updateTrustWorkerDataRepository: UpdateTrustWorkerDataRepository
) : CrossSigningService,
DeviceListManager.UserDevicesUpdateListener {
private var olmUtility: OlmUtility? = null
private var masterPkSigning: OlmPkSigning? = null
private var userPkSigning: OlmPkSigning? = null
private var selfSigningPkSigning: OlmPkSigning? = null
init {
try {
olmUtility = OlmUtility()
// Try to get stored keys if they exist
cryptoStore.getMyCrossSigningInfo()?.let { mxCrossSigningInfo ->
Timber.i("## CrossSigning - Found Existing self signed keys")
Timber.i("## CrossSigning - Checking if private keys are known")
cryptoStore.getCrossSigningPrivateKeys()?.let { privateKeysInfo ->
privateKeysInfo.master
?.fromBase64()
?.let { privateKeySeed ->
val pkSigning = OlmPkSigning()
if (pkSigning.initWithSeed(privateKeySeed) == mxCrossSigningInfo.masterKey()?.unpaddedBase64PublicKey) {
masterPkSigning = pkSigning
Timber.i("## CrossSigning - Loading master key success")
} else {
Timber.w("## CrossSigning - Public master key does not match the private key")
pkSigning.releaseSigning()
// TODO untrust?
}
}
privateKeysInfo.user
?.fromBase64()
?.let { privateKeySeed ->
val pkSigning = OlmPkSigning()
if (pkSigning.initWithSeed(privateKeySeed) == mxCrossSigningInfo.userKey()?.unpaddedBase64PublicKey) {
userPkSigning = pkSigning
Timber.i("## CrossSigning - Loading User Signing key success")
} else {
Timber.w("## CrossSigning - Public User key does not match the private key")
pkSigning.releaseSigning()
// TODO untrust?
}
}
privateKeysInfo.selfSigned
?.fromBase64()
?.let { privateKeySeed ->
val pkSigning = OlmPkSigning()
if (pkSigning.initWithSeed(privateKeySeed) == mxCrossSigningInfo.selfSigningKey()?.unpaddedBase64PublicKey) {
selfSigningPkSigning = pkSigning
Timber.i("## CrossSigning - Loading Self Signing key success")
} else {
Timber.w("## CrossSigning - Public Self Signing key does not match the private key")
pkSigning.releaseSigning()
// TODO untrust?
}
}
}
// Recover local trust in case private key are there?
setUserKeysAsTrusted(userId, checkUserTrust(userId).isVerified())
}
} catch (e: Throwable) {
// Mmm this kind of a big issue
Timber.e(e, "Failed to initialize Cross Signing")
}
deviceListManager.addListener(this)
}
fun release() {
olmUtility?.releaseUtility()
listOf(masterPkSigning, userPkSigning, selfSigningPkSigning).forEach { it?.releaseSigning() }
deviceListManager.removeListener(this)
}
protected fun finalize() {
release()
}
/**
* - Make 3 key pairs (MSK, USK, SSK)
* - Save the private keys with proper security
* - Sign the keys and upload them
* - Sign the current device with SSK and sign MSK with device key (migration) and upload signatures
*/
override fun initializeCrossSigning(uiaInterceptor: UserInteractiveAuthInterceptor?, callback: MatrixCallback<Unit>) {
Timber.d("## CrossSigning initializeCrossSigning")
val params = InitializeCrossSigningTask.Params(
interactiveAuthInterceptor = uiaInterceptor
)
initializeCrossSigningTask.configureWith(params) {
this.callbackThread = TaskThread.CRYPTO
this.callback = object : MatrixCallback<InitializeCrossSigningTask.Result> {
override fun onFailure(failure: Throwable) {
Timber.e(failure, "Error in initializeCrossSigning()")
callback.onFailure(failure)
}
override fun onSuccess(data: InitializeCrossSigningTask.Result) {
val crossSigningInfo = MXCrossSigningInfo(userId, listOf(data.masterKeyInfo, data.userKeyInfo, data.selfSignedKeyInfo))
cryptoStore.setMyCrossSigningInfo(crossSigningInfo)
setUserKeysAsTrusted(userId, true)
cryptoStore.storePrivateKeysInfo(data.masterKeyPK, data.userKeyPK, data.selfSigningKeyPK)
masterPkSigning = OlmPkSigning().apply { initWithSeed(data.masterKeyPK.fromBase64()) }
userPkSigning = OlmPkSigning().apply { initWithSeed(data.userKeyPK.fromBase64()) }
selfSigningPkSigning = OlmPkSigning().apply { initWithSeed(data.selfSigningKeyPK.fromBase64()) }
callback.onSuccess(Unit)
}
}
}.executeBy(taskExecutor)
}
override fun onSecretMSKGossip(mskPrivateKey: String) {
Timber.i("## CrossSigning - onSecretSSKGossip")
val mxCrossSigningInfo = getMyCrossSigningKeys() ?: return Unit.also {
Timber.e("## CrossSigning - onSecretMSKGossip() received secret but public key is not known")
}
mskPrivateKey.fromBase64()
.let { privateKeySeed ->
val pkSigning = OlmPkSigning()
try {
if (pkSigning.initWithSeed(privateKeySeed) == mxCrossSigningInfo.masterKey()?.unpaddedBase64PublicKey) {
masterPkSigning?.releaseSigning()
masterPkSigning = pkSigning
Timber.i("## CrossSigning - Loading MSK success")
cryptoStore.storeMSKPrivateKey(mskPrivateKey)
return
} else {
Timber.e("## CrossSigning - onSecretMSKGossip() private key do not match public key")
pkSigning.releaseSigning()
}
} catch (failure: Throwable) {
Timber.e("## CrossSigning - onSecretMSKGossip() ${failure.localizedMessage}")
pkSigning.releaseSigning()
}
}
}
override fun onSecretSSKGossip(sskPrivateKey: String) {
Timber.i("## CrossSigning - onSecretSSKGossip")
val mxCrossSigningInfo = getMyCrossSigningKeys() ?: return Unit.also {
Timber.e("## CrossSigning - onSecretSSKGossip() received secret but public key is not known")
}
sskPrivateKey.fromBase64()
.let { privateKeySeed ->
val pkSigning = OlmPkSigning()
try {
if (pkSigning.initWithSeed(privateKeySeed) == mxCrossSigningInfo.selfSigningKey()?.unpaddedBase64PublicKey) {
selfSigningPkSigning?.releaseSigning()
selfSigningPkSigning = pkSigning
Timber.i("## CrossSigning - Loading SSK success")
cryptoStore.storeSSKPrivateKey(sskPrivateKey)
return
} else {
Timber.e("## CrossSigning - onSecretSSKGossip() private key do not match public key")
pkSigning.releaseSigning()
}
} catch (failure: Throwable) {
Timber.e("## CrossSigning - onSecretSSKGossip() ${failure.localizedMessage}")
pkSigning.releaseSigning()
}
}
}
override fun onSecretUSKGossip(uskPrivateKey: String) {
Timber.i("## CrossSigning - onSecretUSKGossip")
val mxCrossSigningInfo = getMyCrossSigningKeys() ?: return Unit.also {
Timber.e("## CrossSigning - onSecretUSKGossip() received secret but public key is not knwow ")
}
uskPrivateKey.fromBase64()
.let { privateKeySeed ->
val pkSigning = OlmPkSigning()
try {
if (pkSigning.initWithSeed(privateKeySeed) == mxCrossSigningInfo.userKey()?.unpaddedBase64PublicKey) {
userPkSigning?.releaseSigning()
userPkSigning = pkSigning
Timber.i("## CrossSigning - Loading USK success")
cryptoStore.storeUSKPrivateKey(uskPrivateKey)
return
} else {
Timber.e("## CrossSigning - onSecretUSKGossip() private key do not match public key")
pkSigning.releaseSigning()
}
} catch (failure: Throwable) {
pkSigning.releaseSigning()
}
}
}
override fun checkTrustFromPrivateKeys(masterKeyPrivateKey: String?,
uskKeyPrivateKey: String?,
sskPrivateKey: String?
): UserTrustResult {
val mxCrossSigningInfo = getMyCrossSigningKeys() ?: return UserTrustResult.CrossSigningNotConfigured(userId)
var masterKeyIsTrusted = false
var userKeyIsTrusted = false
var selfSignedKeyIsTrusted = false
masterKeyPrivateKey?.fromBase64()
?.let { privateKeySeed ->
val pkSigning = OlmPkSigning()
try {
if (pkSigning.initWithSeed(privateKeySeed) == mxCrossSigningInfo.masterKey()?.unpaddedBase64PublicKey) {
masterPkSigning?.releaseSigning()
masterPkSigning = pkSigning
masterKeyIsTrusted = true
Timber.i("## CrossSigning - Loading master key success")
} else {
pkSigning.releaseSigning()
}
} catch (failure: Throwable) {
pkSigning.releaseSigning()
}
}
uskKeyPrivateKey?.fromBase64()
?.let { privateKeySeed ->
val pkSigning = OlmPkSigning()
try {
if (pkSigning.initWithSeed(privateKeySeed) == mxCrossSigningInfo.userKey()?.unpaddedBase64PublicKey) {
userPkSigning?.releaseSigning()
userPkSigning = pkSigning
userKeyIsTrusted = true
Timber.i("## CrossSigning - Loading master key success")
} else {
pkSigning.releaseSigning()
}
} catch (failure: Throwable) {
pkSigning.releaseSigning()
}
}
sskPrivateKey?.fromBase64()
?.let { privateKeySeed ->
val pkSigning = OlmPkSigning()
try {
if (pkSigning.initWithSeed(privateKeySeed) == mxCrossSigningInfo.selfSigningKey()?.unpaddedBase64PublicKey) {
selfSigningPkSigning?.releaseSigning()
selfSigningPkSigning = pkSigning
selfSignedKeyIsTrusted = true
Timber.i("## CrossSigning - Loading master key success")
} else {
pkSigning.releaseSigning()
}
} catch (failure: Throwable) {
pkSigning.releaseSigning()
}
}
if (!masterKeyIsTrusted || !userKeyIsTrusted || !selfSignedKeyIsTrusted) {
return UserTrustResult.KeysNotTrusted(mxCrossSigningInfo)
} else {
cryptoStore.markMyMasterKeyAsLocallyTrusted(true)
val checkSelfTrust = checkSelfTrust()
if (checkSelfTrust.isVerified()) {
cryptoStore.storePrivateKeysInfo(masterKeyPrivateKey, uskKeyPrivateKey, sskPrivateKey)
setUserKeysAsTrusted(userId, true)
}
return checkSelfTrust
}
}
/**
*
* ┏━━━━━━━━┓ ┏━━━━━━━━┓
* ┃ ALICE ┃ ┃ BOB ┃
* ┗━━━━━━━━┛ ┗━━━━━━━━┛
* MSK ┌────────────▶ MSK
* │
* │ │
* │ SSK │
* │ │
* │ │
* └──▶ USK ────────────┘
*/
override fun isUserTrusted(otherUserId: String): Boolean {
return cryptoStore.getCrossSigningInfo(userId)?.isTrusted() == true
}
override fun isCrossSigningVerified(): Boolean {
return checkSelfTrust().isVerified()
}
/**
* Will not force a download of the key, but will verify signatures trust chain
*/
override fun checkUserTrust(otherUserId: String): UserTrustResult {
Timber.v("## CrossSigning checkUserTrust for $otherUserId")
if (otherUserId == userId) {
return checkSelfTrust()
}
// I trust a user if I trust his master key
// I can trust the master key if it is signed by my user key
// TODO what if the master key is signed by a device key that i have verified
// First let's get my user key
val myCrossSigningInfo = cryptoStore.getCrossSigningInfo(userId)
checkOtherMSKTrusted(myCrossSigningInfo, cryptoStore.getCrossSigningInfo(otherUserId))
return UserTrustResult.Success
}
fun checkOtherMSKTrusted(myCrossSigningInfo: MXCrossSigningInfo?, otherInfo: MXCrossSigningInfo?): UserTrustResult {
val myUserKey = myCrossSigningInfo?.userKey()
?: return UserTrustResult.CrossSigningNotConfigured(userId)
if (!myCrossSigningInfo.isTrusted()) {
return UserTrustResult.KeysNotTrusted(myCrossSigningInfo)
}
// Let's get the other user master key
val otherMasterKey = otherInfo?.masterKey()
?: return UserTrustResult.UnknownCrossSignatureInfo(otherInfo?.userId ?: "")
val masterKeySignaturesMadeByMyUserKey = otherMasterKey.signatures
?.get(userId) // Signatures made by me
?.get("ed25519:${myUserKey.unpaddedBase64PublicKey}")
if (masterKeySignaturesMadeByMyUserKey.isNullOrBlank()) {
Timber.d("## CrossSigning checkUserTrust false for ${otherInfo.userId}, not signed by my UserSigningKey")
return UserTrustResult.KeyNotSigned(otherMasterKey)
}
// Check that Alice USK signature of Bob MSK is valid
try {
olmUtility!!.verifyEd25519Signature(masterKeySignaturesMadeByMyUserKey, myUserKey.unpaddedBase64PublicKey, otherMasterKey.canonicalSignable())
} catch (failure: Throwable) {
return UserTrustResult.InvalidSignature(myUserKey, masterKeySignaturesMadeByMyUserKey)
}
return UserTrustResult.Success
}
private fun checkSelfTrust(): UserTrustResult {
// Special case when it's me,
// I have to check that MSK -> USK -> SSK
// and that MSK is trusted (i know the private key, or is signed by a trusted device)
val myCrossSigningInfo = cryptoStore.getCrossSigningInfo(userId)
return checkSelfTrust(myCrossSigningInfo, cryptoStore.getUserDeviceList(userId))
}
fun checkSelfTrust(myCrossSigningInfo: MXCrossSigningInfo?, myDevices: List<CryptoDeviceInfo>?): UserTrustResult {
// Special case when it's me,
// I have to check that MSK -> USK -> SSK
// and that MSK is trusted (i know the private key, or is signed by a trusted device)
// val myCrossSigningInfo = cryptoStore.getCrossSigningInfo(userId)
val myMasterKey = myCrossSigningInfo?.masterKey()
?: return UserTrustResult.CrossSigningNotConfigured(userId)
// Is the master key trusted
// 1) check if I know the private key
val masterPrivateKey = cryptoStore.getCrossSigningPrivateKeys()
?.master
?.fromBase64()
var isMaterKeyTrusted = false
if (myMasterKey.trustLevel?.locallyVerified == true) {
isMaterKeyTrusted = true
} else if (masterPrivateKey != null) {
// Check if private match public
var olmPkSigning: OlmPkSigning? = null
try {
olmPkSigning = OlmPkSigning()
val expectedPK = olmPkSigning.initWithSeed(masterPrivateKey)
isMaterKeyTrusted = myMasterKey.unpaddedBase64PublicKey == expectedPK
} catch (failure: Throwable) {
Timber.e(failure)
}
olmPkSigning?.releaseSigning()
} else {
// Maybe it's signed by a locally trusted device?
myMasterKey.signatures?.get(userId)?.forEach { (key, value) ->
val potentialDeviceId = key.removePrefix("ed25519:")
val potentialDevice = myDevices?.firstOrNull { it.deviceId == potentialDeviceId } // cryptoStore.getUserDevice(userId, potentialDeviceId)
if (potentialDevice != null && potentialDevice.isVerified) {
// Check signature validity?
try {
olmUtility?.verifyEd25519Signature(value, potentialDevice.fingerprint(), myMasterKey.canonicalSignable())
isMaterKeyTrusted = true
return@forEach
} catch (failure: Throwable) {
// log
Timber.w(failure, "Signature not valid?")
}
}
}
}
if (!isMaterKeyTrusted) {
return UserTrustResult.KeysNotTrusted(myCrossSigningInfo)
}
val myUserKey = myCrossSigningInfo.userKey()
?: return UserTrustResult.CrossSigningNotConfigured(userId)
val userKeySignaturesMadeByMyMasterKey = myUserKey.signatures
?.get(userId) // Signatures made by me
?.get("ed25519:${myMasterKey.unpaddedBase64PublicKey}")
if (userKeySignaturesMadeByMyMasterKey.isNullOrBlank()) {
Timber.d("## CrossSigning checkUserTrust false for $userId, USK not signed by MSK")
return UserTrustResult.KeyNotSigned(myUserKey)
}
// Check that Alice USK signature of Alice MSK is valid
try {
olmUtility!!.verifyEd25519Signature(userKeySignaturesMadeByMyMasterKey, myMasterKey.unpaddedBase64PublicKey, myUserKey.canonicalSignable())
} catch (failure: Throwable) {
return UserTrustResult.InvalidSignature(myUserKey, userKeySignaturesMadeByMyMasterKey)
}
val mySSKey = myCrossSigningInfo.selfSigningKey()
?: return UserTrustResult.CrossSigningNotConfigured(userId)
val ssKeySignaturesMadeByMyMasterKey = mySSKey.signatures
?.get(userId) // Signatures made by me
?.get("ed25519:${myMasterKey.unpaddedBase64PublicKey}")
if (ssKeySignaturesMadeByMyMasterKey.isNullOrBlank()) {
Timber.d("## CrossSigning checkUserTrust false for $userId, SSK not signed by MSK")
return UserTrustResult.KeyNotSigned(mySSKey)
}
// Check that Alice USK signature of Alice MSK is valid
try {
olmUtility!!.verifyEd25519Signature(ssKeySignaturesMadeByMyMasterKey, myMasterKey.unpaddedBase64PublicKey, mySSKey.canonicalSignable())
} catch (failure: Throwable) {
return UserTrustResult.InvalidSignature(mySSKey, ssKeySignaturesMadeByMyMasterKey)
}
return UserTrustResult.Success
}
override fun getUserCrossSigningKeys(otherUserId: String): MXCrossSigningInfo? {
return cryptoStore.getCrossSigningInfo(otherUserId)
}
override fun getLiveCrossSigningKeys(userId: String): LiveData<Optional<MXCrossSigningInfo>> {
return cryptoStore.getLiveCrossSigningInfo(userId)
}
override fun getMyCrossSigningKeys(): MXCrossSigningInfo? {
return cryptoStore.getMyCrossSigningInfo()
}
override fun getCrossSigningPrivateKeys(): PrivateKeysInfo? {
return cryptoStore.getCrossSigningPrivateKeys()
}
override fun getLiveCrossSigningPrivateKeys(): LiveData<Optional<PrivateKeysInfo>> {
return cryptoStore.getLiveCrossSigningPrivateKeys()
}
override fun canCrossSign(): Boolean {
return checkSelfTrust().isVerified() && cryptoStore.getCrossSigningPrivateKeys()?.selfSigned != null
&& cryptoStore.getCrossSigningPrivateKeys()?.user != null
}
override fun allPrivateKeysKnown(): Boolean {
return checkSelfTrust().isVerified()
&& cryptoStore.getCrossSigningPrivateKeys()?.allKnown().orFalse()
}
override fun trustUser(otherUserId: String, callback: MatrixCallback<Unit>) {
cryptoCoroutineScope.launch(coroutineDispatchers.crypto) {
Timber.d("## CrossSigning - Mark user $userId as trusted ")
// We should have this user keys
val otherMasterKeys = getUserCrossSigningKeys(otherUserId)?.masterKey()
if (otherMasterKeys == null) {
callback.onFailure(Throwable("## CrossSigning - Other master signing key is not known"))
return@launch
}
val myKeys = getUserCrossSigningKeys(userId)
if (myKeys == null) {
callback.onFailure(Throwable("## CrossSigning - CrossSigning is not setup for this account"))
return@launch
}
val userPubKey = myKeys.userKey()?.unpaddedBase64PublicKey
if (userPubKey == null || userPkSigning == null) {
callback.onFailure(Throwable("## CrossSigning - Cannot sign from this account, privateKeyUnknown $userPubKey"))
return@launch
}
// Sign the other MasterKey with our UserSigning key
val newSignature = JsonCanonicalizer.getCanonicalJson(Map::class.java,
otherMasterKeys.signalableJSONDictionary()).let { userPkSigning?.sign(it) }
if (newSignature == null) {
// race??
callback.onFailure(Throwable("## CrossSigning - Failed to sign"))
return@launch
}
cryptoStore.setUserKeysAsTrusted(otherUserId, true)
// TODO update local copy with new signature directly here? kind of local echo of trust?
Timber.d("## CrossSigning - Upload signature of $userId MSK signed by USK")
val uploadQuery = UploadSignatureQueryBuilder()
.withSigningKeyInfo(otherMasterKeys.copyForSignature(userId, userPubKey, newSignature))
.build()
uploadSignaturesTask.configureWith(UploadSignaturesTask.Params(uploadQuery)) {
this.executionThread = TaskThread.CRYPTO
this.callback = callback
}.executeBy(taskExecutor)
}
}
override fun markMyMasterKeyAsTrusted() {
cryptoCoroutineScope.launch(coroutineDispatchers.crypto) {
cryptoStore.markMyMasterKeyAsLocallyTrusted(true)
checkSelfTrust()
// re-verify all trusts
onUsersDeviceUpdate(listOf(userId))
}
}
override fun trustDevice(deviceId: String, callback: MatrixCallback<Unit>) {
cryptoCoroutineScope.launch(coroutineDispatchers.crypto) {
// This device should be yours
val device = cryptoStore.getUserDevice(userId, deviceId)
if (device == null) {
callback.onFailure(IllegalArgumentException("This device [$deviceId] is not known, or not yours"))
return@launch
}
val myKeys = getUserCrossSigningKeys(userId)
if (myKeys == null) {
callback.onFailure(Throwable("CrossSigning is not setup for this account"))
return@launch
}
val ssPubKey = myKeys.selfSigningKey()?.unpaddedBase64PublicKey
if (ssPubKey == null || selfSigningPkSigning == null) {
callback.onFailure(Throwable("Cannot sign from this account, public and/or privateKey Unknown $ssPubKey"))
return@launch
}
// Sign with self signing
val newSignature = selfSigningPkSigning?.sign(device.canonicalSignable())
if (newSignature == null) {
// race??
callback.onFailure(Throwable("Failed to sign"))
return@launch
}
val toUpload = device.copy(
signatures = mapOf(
userId
to
mapOf(
"ed25519:$ssPubKey" to newSignature
)
)
)
val uploadQuery = UploadSignatureQueryBuilder()
.withDeviceInfo(toUpload)
.build()
uploadSignaturesTask.configureWith(UploadSignaturesTask.Params(uploadQuery)) {
this.executionThread = TaskThread.CRYPTO
this.callback = callback
}.executeBy(taskExecutor)
}
}
override fun checkDeviceTrust(otherUserId: String, otherDeviceId: String, locallyTrusted: Boolean?): DeviceTrustResult {
val otherDevice = cryptoStore.getUserDevice(otherUserId, otherDeviceId)
?: return DeviceTrustResult.UnknownDevice(otherDeviceId)
val myKeys = getUserCrossSigningKeys(userId)
?: return legacyFallbackTrust(locallyTrusted, DeviceTrustResult.CrossSigningNotConfigured(userId))
if (!myKeys.isTrusted()) return legacyFallbackTrust(locallyTrusted, DeviceTrustResult.KeysNotTrusted(myKeys))
val otherKeys = getUserCrossSigningKeys(otherUserId)
?: return legacyFallbackTrust(locallyTrusted, DeviceTrustResult.CrossSigningNotConfigured(otherUserId))
// TODO should we force verification ?
if (!otherKeys.isTrusted()) return legacyFallbackTrust(locallyTrusted, DeviceTrustResult.KeysNotTrusted(otherKeys))
// Check if the trust chain is valid
/*
* ┏━━━━━━━━┓ ┏━━━━━━━━┓
* ┃ ALICE ┃ ┃ BOB ┃
* ┗━━━━━━━━┛ ┗━━━━━━━━┛
* MSK ┌────────────▶MSK
* │
* │ │ │
* │ SSK │ └──▶ SSK ──────────────────┐
* │ │ │
* │ │ USK │
* └──▶ USK ────────────┘ (not visible by │
* Alice) │
* ▼
* ┌──────────────┐
* │ BOB's Device │
* └──────────────┘
*/
val otherSSKSignature = otherDevice.signatures?.get(otherUserId)?.get("ed25519:${otherKeys.selfSigningKey()?.unpaddedBase64PublicKey}")
?: return legacyFallbackTrust(
locallyTrusted,
DeviceTrustResult.MissingDeviceSignature(otherDeviceId, otherKeys.selfSigningKey()
?.unpaddedBase64PublicKey
?: ""
)
)
// Check bob's device is signed by bob's SSK
try {
olmUtility!!.verifyEd25519Signature(otherSSKSignature, otherKeys.selfSigningKey()?.unpaddedBase64PublicKey, otherDevice.canonicalSignable())
} catch (e: Throwable) {
return legacyFallbackTrust(locallyTrusted, DeviceTrustResult.InvalidDeviceSignature(otherDeviceId, otherSSKSignature, e))
}
return DeviceTrustResult.Success(DeviceTrustLevel(crossSigningVerified = true, locallyVerified = locallyTrusted))
}
fun checkDeviceTrust(myKeys: MXCrossSigningInfo?, otherKeys: MXCrossSigningInfo?, otherDevice: CryptoDeviceInfo): DeviceTrustResult {
val locallyTrusted = otherDevice.trustLevel?.isLocallyVerified()
myKeys ?: return legacyFallbackTrust(locallyTrusted, DeviceTrustResult.CrossSigningNotConfigured(userId))
if (!myKeys.isTrusted()) return legacyFallbackTrust(locallyTrusted, DeviceTrustResult.KeysNotTrusted(myKeys))
otherKeys ?: return legacyFallbackTrust(locallyTrusted, DeviceTrustResult.CrossSigningNotConfigured(otherDevice.userId))
// TODO should we force verification ?
if (!otherKeys.isTrusted()) return legacyFallbackTrust(locallyTrusted, DeviceTrustResult.KeysNotTrusted(otherKeys))
// Check if the trust chain is valid
/*
* ┏━━━━━━━━┓ ┏━━━━━━━━┓
* ┃ ALICE ┃ ┃ BOB ┃
* ┗━━━━━━━━┛ ┗━━━━━━━━┛
* MSK ┌────────────▶MSK
* │
* │ │ │
* │ SSK │ └──▶ SSK ──────────────────┐
* │ │ │
* │ │ USK │
* └──▶ USK ────────────┘ (not visible by │
* Alice) │
* ▼
* ┌──────────────┐
* │ BOB's Device │
* └──────────────┘
*/
val otherSSKSignature = otherDevice.signatures?.get(otherKeys.userId)?.get("ed25519:${otherKeys.selfSigningKey()?.unpaddedBase64PublicKey}")
?: return legacyFallbackTrust(
locallyTrusted,
DeviceTrustResult.MissingDeviceSignature(otherDevice.deviceId, otherKeys.selfSigningKey()
?.unpaddedBase64PublicKey
?: ""
)
)
// Check bob's device is signed by bob's SSK
try {
olmUtility!!.verifyEd25519Signature(otherSSKSignature, otherKeys.selfSigningKey()?.unpaddedBase64PublicKey, otherDevice.canonicalSignable())
} catch (e: Throwable) {
return legacyFallbackTrust(locallyTrusted, DeviceTrustResult.InvalidDeviceSignature(otherDevice.deviceId, otherSSKSignature, e))
}
return DeviceTrustResult.Success(DeviceTrustLevel(crossSigningVerified = true, locallyVerified = locallyTrusted))
}
private fun legacyFallbackTrust(locallyTrusted: Boolean?, crossSignTrustFail: DeviceTrustResult): DeviceTrustResult {
return if (locallyTrusted == true) {
DeviceTrustResult.Success(DeviceTrustLevel(crossSigningVerified = false, locallyVerified = true))
} else {
crossSignTrustFail
}
}
override fun onUsersDeviceUpdate(userIds: List<String>) {
Timber.d("## CrossSigning - onUsersDeviceUpdate for users: ${userIds.logLimit()}")
val workerParams = UpdateTrustWorker.Params(
sessionId = sessionId,
filename = updateTrustWorkerDataRepository.createParam(userIds)
)
val workerData = WorkerParamsFactory.toData(workerParams)
val workRequest = workManagerProvider.matrixOneTimeWorkRequestBuilder<UpdateTrustWorker>()
.setInputData(workerData)
.setBackoffCriteria(BackoffPolicy.LINEAR, WorkManagerProvider.BACKOFF_DELAY_MILLIS, TimeUnit.MILLISECONDS)
.build()
workManagerProvider.workManager
.beginUniqueWork("TRUST_UPDATE_QUEUE", ExistingWorkPolicy.APPEND_OR_REPLACE, workRequest)
.enqueue()
}
private fun setUserKeysAsTrusted(otherUserId: String, trusted: Boolean) {
val currentTrust = cryptoStore.getCrossSigningInfo(otherUserId)?.isTrusted()
cryptoStore.setUserKeysAsTrusted(otherUserId, trusted)
// If it's me, recheck trust of all users and devices?
val users = ArrayList<String>()
if (otherUserId == userId && currentTrust != trusted) {
// reRequestAllPendingRoomKeyRequest()
cryptoStore.updateUsersTrust {
users.add(it)
checkUserTrust(it).isVerified()
}
users.forEach {
cryptoStore.getUserDeviceList(it)?.forEach { device ->
val updatedTrust = checkDeviceTrust(it, device.deviceId, device.trustLevel?.isLocallyVerified() ?: false)
Timber.v("## CrossSigning - update trust for device ${device.deviceId} of user $otherUserId , verified=$updatedTrust")
cryptoStore.setDeviceTrust(it, device.deviceId, updatedTrust.isCrossSignedVerified(), updatedTrust.isLocallyVerified())
}
}
}
}
// private fun reRequestAllPendingRoomKeyRequest() {
// cryptoCoroutineScope.launch(coroutineDispatchers.crypto) {
// Timber.d("## CrossSigning - reRequest pending outgoing room key requests")
// cryptoStore.getOutgoingRoomKeyRequests().forEach {
// it.requestBody?.let { requestBody ->
// if (cryptoStore.getInboundGroupSession(requestBody.sessionId ?: "", requestBody.senderKey ?: "") == null) {
// outgoingRoomKeyRequestManager.resendRoomKeyRequest(requestBody)
// } else {
// outgoingRoomKeyRequestManager.cancelRoomKeyRequest(requestBody)
// }
// }
// }
// }
// }
}
crossSigningManager: CrossSigningManager
) : CrossSigningService by crossSigningManager

View File

@@ -65,7 +65,7 @@ internal class UpdateTrustWorker(context: Context,
val filename: String? = null
) : SessionWorkerParams
@Inject lateinit var crossSigningService: DefaultCrossSigningService
@Inject lateinit var crossSigningManager: CrossSigningManager
// It breaks the crypto store contract, but we need to batch things :/
@CryptoDatabase
@@ -133,7 +133,7 @@ internal class UpdateTrustWorker(context: Context,
?.devices
?.map { CryptoMapper.mapToModel(it) }
myTrustResult = crossSigningService.checkSelfTrust(myCrossSigningInfo, myDevices)
myTrustResult = crossSigningManager.checkSelfTrust(myCrossSigningInfo, myDevices)
updateCrossSigningKeysTrust(cryptoRealm, myUserId, myTrustResult.isVerified())
// update model reference
myCrossSigningInfo = getCrossSigningInfo(cryptoRealm, myUserId)
@@ -147,7 +147,7 @@ internal class UpdateTrustWorker(context: Context,
when (entry.key) {
myUserId -> myTrustResult
else -> {
crossSigningService.checkOtherMSKTrusted(myCrossSigningInfo, entry.value).also {
crossSigningManager.checkOtherMSKTrusted(myCrossSigningInfo, entry.value).also {
Timber.d("## CrossSigning - user:${entry.key} result:$it")
}
}
@@ -172,7 +172,7 @@ internal class UpdateTrustWorker(context: Context,
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))
crossSigningManager.checkDeviceTrust(myCrossSigningInfo, otherInfo, CryptoMapper.mapToModel(device))
}
// Update trust if needed

View File

@@ -0,0 +1,21 @@
/*
* 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.keysbackup
internal interface KeysBackupManagerInput {
fun onSecretKeyGossip(secret: String)
}

View File

@@ -0,0 +1,29 @@
/*
* 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 org.matrix.android.sdk.api.session.events.model.Event
internal interface VerificationManagerInput {
fun onPotentiallyInterestingEventRoomFailToDecrypt(event: Event)
fun onRoomRequestHandledByOtherDevice(event: Event)
fun onRoomEvent(event: Event)
suspend fun onRoomRequestReceived(event: Event)
}

View File

@@ -40,7 +40,7 @@ import javax.inject.Inject
internal class VerificationMessageProcessor @Inject constructor(
private val eventDecryptor: EventDecryptor,
private val verificationService: DefaultVerificationService,
private val verificationManager: VerificationManager,
@UserId private val userId: String,
@DeviceId private val deviceId: String?
) : EventInsertLiveProcessor {
@@ -91,7 +91,7 @@ internal class VerificationMessageProcessor @Inject constructor(
)
} catch (e: MXCryptoError) {
Timber.e("## SAS Failed to decrypt event: ${event.eventId}")
verificationService.onPotentiallyInterestingEventRoomFailToDecrypt(event)
verificationManager.onPotentiallyInterestingEventRoomFailToDecrypt(event)
}
}
Timber.v("## SAS Verification live observer: received msgId: ${event.eventId} type: ${event.getClearType()}")
@@ -120,7 +120,7 @@ internal class VerificationMessageProcessor @Inject constructor(
// The verification is started from another device
Timber.v("## SAS Verification live observer: Transaction started by other device tid:$relatesToEventId ")
relatesToEventId?.let { txId -> transactionsHandledByOtherDevice.add(txId) }
verificationService.onRoomRequestHandledByOtherDevice(event)
verificationManager.onRoomRequestHandledByOtherDevice(event)
}
}
} else if (EventType.KEY_VERIFICATION_READY == event.getClearType()) {
@@ -129,13 +129,13 @@ internal class VerificationMessageProcessor @Inject constructor(
// The verification is started from another device
Timber.v("## SAS Verification live observer: Transaction started by other device tid:$relatesToEventId ")
relatesToEventId?.let { txId -> transactionsHandledByOtherDevice.add(txId) }
verificationService.onRoomRequestHandledByOtherDevice(event)
verificationManager.onRoomRequestHandledByOtherDevice(event)
}
}
} else if (EventType.KEY_VERIFICATION_CANCEL == event.getClearType() || EventType.KEY_VERIFICATION_DONE == event.getClearType()) {
relatesToEventId?.let {
transactionsHandledByOtherDevice.remove(it)
verificationService.onRoomRequestHandledByOtherDevice(event)
verificationManager.onRoomRequestHandledByOtherDevice(event)
}
}
@@ -156,11 +156,11 @@ internal class VerificationMessageProcessor @Inject constructor(
EventType.KEY_VERIFICATION_CANCEL,
EventType.KEY_VERIFICATION_READY,
EventType.KEY_VERIFICATION_DONE -> {
verificationService.onRoomEvent(event)
verificationManager.onRoomEvent(event)
}
EventType.MESSAGE -> {
if (MessageType.MSGTYPE_VERIFICATION_REQUEST == event.getClearContent().toModel<MessageContent>()?.msgType) {
verificationService.onRoomRequestReceived(event)
verificationManager.onRoomRequestReceived(event)
}
}
}

View File

@@ -17,6 +17,9 @@
package org.matrix.android.sdk.internal.database
import com.zhuinden.monarchy.Monarchy
import io.realm.RealmConfiguration
import io.realm.RealmResults
import kotlinx.coroutines.launch
import org.matrix.android.sdk.internal.database.mapper.asDomain
import org.matrix.android.sdk.internal.database.model.EventEntity
import org.matrix.android.sdk.internal.database.model.EventInsertEntity
@@ -24,17 +27,13 @@ import org.matrix.android.sdk.internal.database.model.EventInsertEntityFields
import org.matrix.android.sdk.internal.database.query.where
import org.matrix.android.sdk.internal.di.SessionDatabase
import org.matrix.android.sdk.internal.session.EventInsertLiveProcessor
import io.realm.RealmConfiguration
import io.realm.RealmResults
import kotlinx.coroutines.launch
import org.matrix.android.sdk.internal.crypto.EventDecryptor
import timber.log.Timber
import javax.inject.Inject
internal class EventInsertLiveObserver @Inject constructor(@SessionDatabase realmConfiguration: RealmConfiguration,
private val processors: Set<@JvmSuppressWildcards EventInsertLiveProcessor>,
private val eventDecryptor: EventDecryptor)
: RealmLiveEntityObserver<EventInsertEntity>(realmConfiguration) {
internal class EventInsertLiveObserver @Inject constructor(
@SessionDatabase realmConfiguration: RealmConfiguration,
private val processors: Set<@JvmSuppressWildcards EventInsertLiveProcessor>,
) : RealmLiveEntityObserver<EventInsertEntity>(realmConfiguration) {
override val query = Monarchy.Query<EventInsertEntity> {
it.where(EventInsertEntity::class.java)
@@ -86,23 +85,6 @@ internal class EventInsertLiveObserver @Inject constructor(@SessionDatabase real
}
}
// private fun decryptIfNeeded(event: Event) {
// if (event.isEncrypted() && event.mxDecryptionResult == null) {
// try {
// val result = eventDecryptor.decryptEvent(event, event.roomId ?: "")
// event.mxDecryptionResult = OlmDecryptionResult(
// payload = result.clearEvent,
// senderKey = result.senderCurve25519Key,
// keysClaimed = result.claimedEd25519Key?.let { k -> mapOf("ed25519" to k) },
// forwardingCurve25519KeyChain = result.forwardingCurve25519KeyChain
// )
// } catch (e: MXCryptoError) {
// Timber.v("Failed to decrypt event")
// // TODO -> we should keep track of this and retry, or some processing will never be handled
// }
// }
// }
private fun shouldProcess(eventInsertEntity: EventInsertEntity): Boolean {
return processors.any {
it.shouldProcess(eventInsertEntity.eventId, eventInsertEntity.eventType, eventInsertEntity.insertType)

View File

@@ -63,7 +63,7 @@ import org.matrix.android.sdk.api.session.widgets.WidgetService
import org.matrix.android.sdk.api.util.appendParamToUrl
import org.matrix.android.sdk.internal.auth.SSO_UIA_FALLBACK_PATH
import org.matrix.android.sdk.internal.auth.SessionParamsStore
import org.matrix.android.sdk.internal.crypto.DefaultCryptoService
import org.matrix.android.sdk.internal.crypto.CryptoManager
import org.matrix.android.sdk.internal.database.tools.RealmDebugTools
import org.matrix.android.sdk.internal.di.SessionDatabase
import org.matrix.android.sdk.internal.di.SessionId
@@ -101,7 +101,8 @@ internal class DefaultSession @Inject constructor(
private val pushersService: Lazy<PushersService>,
private val termsService: Lazy<TermsService>,
private val searchService: Lazy<SearchService>,
private val cryptoService: Lazy<DefaultCryptoService>,
private val cryptoService: Lazy<CryptoService>,
private val cryptoManager: Lazy<CryptoManager>,
private val defaultFileService: Lazy<FileService>,
private val permalinkService: Lazy<PermalinkService>,
private val secureStorageService: Lazy<SecureStorageService>,
@@ -164,7 +165,7 @@ internal class DefaultSession @Inject constructor(
assert(!isOpen)
isOpen = true
globalErrorHandler.listener = this
cryptoService.get().ensureDevice()
cryptoManager.get().ensureDevice()
uiHandler.post {
lifecycleObservers.forEach {
it.onSessionStarted(this)
@@ -216,7 +217,7 @@ internal class DefaultSession @Inject constructor(
listener.onSessionStopped(this)
}
}
cryptoService.get().close()
cryptoManager.get().close()
globalErrorHandler.listener = null
isOpen = false
}

View File

@@ -34,7 +34,7 @@ import org.matrix.android.sdk.api.session.room.model.create.RoomCreateContent
import org.matrix.android.sdk.api.session.room.send.SendState
import org.matrix.android.sdk.internal.crypto.EventDecryptor
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.crypto.crosssigning.CrossSigningManager
import org.matrix.android.sdk.internal.database.mapper.ContentMapper
import org.matrix.android.sdk.internal.database.mapper.asDomain
import org.matrix.android.sdk.internal.database.model.CurrentStateEventEntity
@@ -72,7 +72,7 @@ internal class RoomSummaryUpdater @Inject constructor(
private val roomDisplayNameResolver: RoomDisplayNameResolver,
private val roomAvatarResolver: RoomAvatarResolver,
private val eventDecryptor: EventDecryptor,
private val crossSigningService: DefaultCrossSigningService,
private val crossSigningManager: CrossSigningManager,
private val roomAccountDataDataSource: RoomAccountDataDataSource) {
fun update(realm: Realm,
@@ -177,7 +177,7 @@ internal class RoomSummaryUpdater @Inject constructor(
roomSummaryEntity.otherMemberIds.addAll(otherRoomMembers)
if (roomSummaryEntity.isEncrypted && otherRoomMembers.isNotEmpty()) {
// mmm maybe we could only refresh shield instead of checking trust also?
crossSigningService.onUsersDeviceUpdate(otherRoomMembers)
crossSigningManager.onUsersDeviceUpdate(otherRoomMembers)
}
}
}

View File

@@ -21,19 +21,21 @@ import org.matrix.android.sdk.api.session.events.model.Event
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.MessageContent
import org.matrix.android.sdk.internal.crypto.DefaultCryptoService
import org.matrix.android.sdk.internal.crypto.CryptoManager
import org.matrix.android.sdk.internal.crypto.MXEventDecryptionResult
import org.matrix.android.sdk.internal.crypto.algorithms.olm.OlmDecryptionResult
import org.matrix.android.sdk.internal.crypto.model.event.OlmEventContent
import org.matrix.android.sdk.internal.crypto.verification.DefaultVerificationService
import org.matrix.android.sdk.internal.crypto.verification.VerificationManager
import org.matrix.android.sdk.internal.session.initsync.ProgressReporter
import org.matrix.android.sdk.internal.session.sync.model.SyncResponse
import org.matrix.android.sdk.internal.session.sync.model.ToDeviceSyncResponse
import timber.log.Timber
import javax.inject.Inject
internal class CryptoSyncHandler @Inject constructor(private val cryptoService: DefaultCryptoService,
private val verificationService: DefaultVerificationService) {
internal class CryptoSyncHandler @Inject constructor(
private val cryptoManager: CryptoManager,
private val verificationManager: VerificationManager
) {
fun handleToDevice(toDevice: ToDeviceSyncResponse, progressReporter: ProgressReporter? = null) {
val total = toDevice.events?.size ?: 0
@@ -46,14 +48,14 @@ internal class CryptoSyncHandler @Inject constructor(private val cryptoService:
&& event.getClearContent()?.toModel<MessageContent>()?.msgType == "m.bad.encrypted") {
Timber.e("## CRYPTO | handleToDeviceEvent() : Warning: Unable to decrypt to-device event : ${event.content}")
} else {
verificationService.onToDeviceEvent(event)
cryptoService.onToDeviceEvent(event)
verificationManager.onToDeviceEvent(event)
cryptoManager.onToDeviceEvent(event)
}
}
}
fun onSyncCompleted(syncResponse: SyncResponse) {
cryptoService.onSyncCompleted(syncResponse)
cryptoManager.onSyncCompleted(syncResponse)
}
/**
@@ -68,12 +70,12 @@ internal class CryptoSyncHandler @Inject constructor(private val cryptoService:
if (event.getClearType() == EventType.ENCRYPTED) {
var result: MXEventDecryptionResult? = null
try {
result = cryptoService.decryptEvent(event, timelineId ?: "")
result = cryptoManager.decryptEvent(event, timelineId ?: "")
} catch (exception: MXCryptoError) {
event.mCryptoError = (exception as? MXCryptoError.Base)?.errorType // setCryptoError(exception.cryptoError)
val senderKey = event.content.toModel<OlmEventContent>()?.senderKey ?: "<unknown sender key>"
// try to find device id to ease log reading
val deviceId = cryptoService.getCryptoDeviceInfo(event.senderId!!).firstOrNull {
val deviceId = cryptoManager.getCryptoDeviceInfo(event.senderId!!).firstOrNull {
it.identityKey() == senderKey
}?.deviceId ?: senderKey
Timber.e("## CRYPTO | Failed to decrypt to device event from ${event.senderId}|$deviceId reason:<${event.mCryptoError ?: exception}>")

View File

@@ -26,7 +26,8 @@ import org.matrix.android.sdk.api.session.initsync.InitSyncStep
import org.matrix.android.sdk.api.session.room.model.Membership
import org.matrix.android.sdk.api.session.room.model.RoomMemberContent
import org.matrix.android.sdk.api.session.room.send.SendState
import org.matrix.android.sdk.internal.crypto.DefaultCryptoService
import org.matrix.android.sdk.internal.crypto.CryptoManagerInput
import org.matrix.android.sdk.internal.crypto.EventDecryptor
import org.matrix.android.sdk.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM
import org.matrix.android.sdk.internal.crypto.algorithms.olm.OlmDecryptionResult
import org.matrix.android.sdk.internal.database.helper.addIfNecessary
@@ -70,7 +71,8 @@ import javax.inject.Inject
internal class RoomSyncHandler @Inject constructor(private val readReceiptHandler: ReadReceiptHandler,
private val roomSummaryUpdater: RoomSummaryUpdater,
private val roomAccountDataHandler: RoomSyncAccountDataHandler,
private val cryptoService: DefaultCryptoService,
private val cryptoManagerInput: CryptoManagerInput,
private val eventDecryptor: EventDecryptor,
private val roomMemberEventHandler: RoomMemberEventHandler,
private val roomTypingUsersHandler: RoomTypingUsersHandler,
private val roomChangeMembershipStateDataSource: RoomChangeMembershipStateDataSource,
@@ -220,7 +222,7 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle
root = eventEntity
}
// Give info to crypto module
cryptoService.onStateEvent(roomId, event)
cryptoManagerInput.onStateEvent(roomId, event)
roomMemberEventHandler.handle(realm, roomId, event)
}
}
@@ -382,7 +384,7 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle
chunkEntity.addTimelineEvent(roomId, eventEntity, PaginationDirection.FORWARDS, roomMemberContentsByUser)
// Give info to crypto module
cryptoService.onLiveEvent(roomEntity.roomId, event)
cryptoManagerInput.onLiveEvent(roomEntity.roomId, event)
// Try to remove local echo
event.unsignedData?.transactionId?.also {
@@ -413,7 +415,7 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle
private fun decryptIfNeeded(event: Event, roomId: String) {
try {
// Event from sync does not have roomId, so add it to the event first
val result = cryptoService.decryptEvent(event.copy(roomId = roomId), "")
val result = eventDecryptor.decryptEvent(event.copy(roomId = roomId), "")
event.mxDecryptionResult = OlmDecryptionResult(
payload = result.clearEvent,
senderKey = result.senderCurve25519Key,

View File

@@ -21,7 +21,7 @@ import com.zhuinden.monarchy.Monarchy
import org.matrix.android.sdk.api.pushrules.PushRuleService
import org.matrix.android.sdk.api.pushrules.RuleScope
import org.matrix.android.sdk.api.session.initsync.InitSyncStep
import org.matrix.android.sdk.internal.crypto.DefaultCryptoService
import org.matrix.android.sdk.internal.crypto.CryptoManager
import org.matrix.android.sdk.internal.di.SessionDatabase
import org.matrix.android.sdk.internal.di.SessionId
import org.matrix.android.sdk.internal.di.WorkManagerProvider
@@ -52,7 +52,7 @@ internal class SyncResponseHandler @Inject constructor(
private val groupSyncHandler: GroupSyncHandler,
private val cryptoSyncHandler: CryptoSyncHandler,
private val aggregatorHandler: SyncResponsePostTreatmentAggregatorHandler,
private val cryptoService: DefaultCryptoService,
private val cryptoManager: CryptoManager,
private val tokenStore: SyncTokenStore,
private val processEventForPushTask: ProcessEventForPushTask,
private val pushRuleService: PushRuleService) {
@@ -64,11 +64,11 @@ internal class SyncResponseHandler @Inject constructor(
Timber.v("Start handling sync, is InitialSync: $isInitialSync")
measureTimeMillis {
if (!cryptoService.isStarted()) {
if (!cryptoManager.isStarted()) {
Timber.v("Should start cryptoService")
cryptoService.start()
cryptoManager.start()
}
cryptoService.onSyncWillProcess(isInitialSync)
cryptoManager.onSyncWillProcess(isInitialSync)
}.also {
Timber.v("Finish handling start cryptoService in $it ms")
}