diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/keysbackup/KeysBackupService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/keysbackup/KeysBackupService.kt index 85a96b5f..e508feb8 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/keysbackup/KeysBackupService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/keysbackup/KeysBackupService.kt @@ -34,7 +34,6 @@ interface KeysBackupService { fun backupAllGroupSessions(progressListener: ProgressListener?, callback: MatrixCallback?) fun getKeysBackupTrust(keysBackupVersion: KeysVersionResult, callback: MatrixCallback) fun getBackupProgress(progressListener: ProgressListener) - fun maybeBackupKeys() fun getVersion(version: String, callback: MatrixCallback) fun forceUsingLastVersion(callback: MatrixCallback) fun checkAndStartKeysBackup() diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/CryptoManager.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/CryptoManager.kt index 15e06bba..33b3dc06 100755 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/CryptoManager.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/CryptoManager.kt @@ -37,15 +37,19 @@ import im.vector.matrix.android.api.session.events.model.toModel import im.vector.matrix.android.api.session.room.Room import im.vector.matrix.android.api.session.room.RoomService import im.vector.matrix.android.api.session.room.model.Membership +import im.vector.matrix.android.internal.crypto.actions.EnsureOlmSessionsForDevicesAction +import im.vector.matrix.android.internal.crypto.actions.MegolmSessionDataImporter import im.vector.matrix.android.internal.crypto.actions.SetDeviceVerificationAction import im.vector.matrix.android.internal.crypto.algorithms.IMXEncrypting +import im.vector.matrix.android.internal.crypto.algorithms.megolm.MXMegolmEncryptionFactory +import im.vector.matrix.android.internal.crypto.algorithms.olm.MXOlmEncryptionFactory import im.vector.matrix.android.internal.crypto.keysbackup.KeysBackup import im.vector.matrix.android.internal.crypto.model.* import im.vector.matrix.android.internal.crypto.model.event.RoomKeyContent import im.vector.matrix.android.internal.crypto.model.rest.DevicesListResponse -import im.vector.matrix.android.internal.crypto.model.rest.EncryptedMessage import im.vector.matrix.android.internal.crypto.model.rest.KeysUploadResponse import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyRequestBody +import im.vector.matrix.android.internal.crypto.repository.WarnOnUnknownDeviceRepository import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore import im.vector.matrix.android.internal.crypto.tasks.* import im.vector.matrix.android.internal.crypto.verification.DefaultSasVerificationService @@ -53,7 +57,6 @@ import im.vector.matrix.android.internal.di.MoshiProvider import im.vector.matrix.android.internal.session.sync.model.SyncResponse import im.vector.matrix.android.internal.task.TaskExecutor import im.vector.matrix.android.internal.task.configureWith -import im.vector.matrix.android.internal.util.convertToUTF8 import org.matrix.olm.OlmManager import timber.log.Timber import java.util.* @@ -71,11 +74,13 @@ import java.util.* internal class CryptoManager( // The credentials, private val mCredentials: Credentials, + private val mMyDeviceInfoHolder: MyDeviceInfoHolder, // the crypto store private val mCryptoStore: IMXCryptoStore, // Olm device private val mOlmDevice: MXOlmDevice, - cryptoConfig: MXCryptoConfig?, + // Set of parameters used to configure/customize the end-to-end crypto. + private val mCryptoConfig: MXCryptoConfig = MXCryptoConfig(), // Device list manager private val deviceListManager: DeviceListManager, // The key backup service. @@ -98,6 +103,12 @@ internal class CryptoManager( private val mOlmManager: OlmManager, // Actions private val mSetDeviceVerificationAction: SetDeviceVerificationAction, + private val mMegolmSessionDataImporter: MegolmSessionDataImporter, + private val mEnsureOlmSessionsForDevicesAction: EnsureOlmSessionsForDevicesAction, + // Repository + private val mWarnOnUnknownDevicesRepository: WarnOnUnknownDeviceRepository, + private val mMXMegolmEncryptionFactory: MXMegolmEncryptionFactory, + private val mMXOlmEncryptionFactory: MXOlmEncryptionFactory, // Tasks private val mClaimOneTimeKeysForUsersDeviceTask: ClaimOneTimeKeysForUsersDeviceTask, private val mDeleteDeviceTask: DeleteDeviceTask, @@ -108,19 +119,11 @@ internal class CryptoManager( private val mUploadKeysTask: UploadKeysTask, // TaskExecutor private val mTaskExecutor: TaskExecutor -) : KeysBackup.KeysBackupCryptoListener, - DefaultSasVerificationService.SasCryptoListener, - CryptoService { +) : CryptoService { // MXEncrypting instance for each room. private val mRoomEncryptors: MutableMap = HashMap() - // Our device keys - /** - * @return my device info - */ - private val myDevice: MXDeviceInfo - // the encryption is starting private var mIsStarting: Boolean = false @@ -148,84 +151,6 @@ internal class CryptoManager( // initialization callbacks private val mInitializationCallbacks = ArrayList>() - // Warn the user if some new devices are detected while encrypting a message. - private var mWarnOnUnknownDevices = true - - // Set of parameters used to configure/customize the end-to-end crypto. - private var mCryptoConfig: MXCryptoConfig? = null - - init { - if (null != cryptoConfig) { - mCryptoConfig = cryptoConfig - } else { - // Consider the default configuration value - mCryptoConfig = MXCryptoConfig() - } - - var deviceId = mCredentials.deviceId - // deviceId should always be defined - val refreshDevicesList = !TextUtils.isEmpty(deviceId) - - if (TextUtils.isEmpty(deviceId)) { - // use the stored one - deviceId = this.mCryptoStore.getDeviceId() - - // Should not happen anymore - TODO() - //mSession.setDeviceId(deviceId) - } - - if (TextUtils.isEmpty(deviceId)) { - deviceId = UUID.randomUUID().toString() - // Should not happen anymore - TODO() - //mSession.setDeviceId(deviceId) - Timber.d("Warning: No device id in MXCredentials. An id was created. Think of storing it") - this.mCryptoStore.storeDeviceId(deviceId) - } - - myDevice = MXDeviceInfo(deviceId!!, mCredentials.userId) - - val keys = HashMap() - - if (!TextUtils.isEmpty(mOlmDevice.deviceEd25519Key)) { - keys["ed25519:" + mCredentials.deviceId] = mOlmDevice.deviceEd25519Key!! - } - - if (!TextUtils.isEmpty(mOlmDevice.deviceCurve25519Key)) { - keys["curve25519:" + mCredentials.deviceId] = mOlmDevice.deviceCurve25519Key!! - } - - myDevice.keys = keys - - myDevice.algorithms = MXCryptoAlgorithms.supportedAlgorithms() - myDevice.mVerified = MXDeviceInfo.DEVICE_VERIFICATION_VERIFIED - - // Add our own deviceinfo to the store - val endToEndDevicesForUser = this.mCryptoStore.getUserDevices(mCredentials.userId) - - val myDevices: MutableMap - - if (null != endToEndDevicesForUser) { - myDevices = HashMap(endToEndDevicesForUser) - } else { - myDevices = HashMap() - } - - myDevices[myDevice.deviceId] = myDevice - - this.mCryptoStore.storeUserDevices(mCredentials.userId, myDevices) - - if (refreshDevicesList) { - // ensure to have the up-to-date devices list - // got some issues when upgrading from Riot < 0.6.4 - deviceListManager.handleDeviceListsChanges(listOf(mCredentials.userId), null) - } - - mKeysBackup.setCryptoInternalListener(this) - mSasVerificationService.setCryptoInternalListener(this) - } - override fun setDeviceName(deviceId: String, deviceName: String, callback: MatrixCallback) { mSetDeviceNameTask .configureWith(SetDeviceNameTask.Params(deviceId, deviceName)) @@ -245,7 +170,7 @@ internal class CryptoManager( } override fun getMyDevice(): MXDeviceInfo { - return myDevice + return mMyDeviceInfoHolder.myDevice } override fun getDevicesList(callback: MatrixCallback) { @@ -462,7 +387,6 @@ internal class CryptoManager( * * @param userId the user id * @param deviceId the device id - * @param callback the asynchronous callback */ override fun getDeviceInfo(userId: String, deviceId: String?): MXDeviceInfo? { return if (!TextUtils.isEmpty(userId) && !TextUtils.isEmpty(deviceId)) { @@ -529,7 +453,6 @@ internal class CryptoManager( * @param verificationStatus the new verification status * @param deviceId the unique identifier for the device. * @param userId the owner of the device - * @param callback the asynchronous callback */ override fun setDeviceVerification(verificationStatus: Int, deviceId: String, userId: String) { mSetDeviceVerificationAction.handle(verificationStatus, deviceId, userId) @@ -555,34 +478,20 @@ internal class CryptoManager( return false } - val encryptingClass = MXCryptoAlgorithms.encryptorClassForAlgorithm(algorithm) + val encryptingClass = MXCryptoAlgorithms.hasEncryptorClassForAlgorithm(algorithm) - if (null == encryptingClass) { + if (!encryptingClass) { Timber.e("## setEncryptionInRoom() : Unable to encrypt with " + algorithm!!) return false } mCryptoStore.storeRoomAlgorithm(roomId, algorithm!!) - val alg: IMXEncrypting - - try { - val ctor = encryptingClass.constructors[0] - alg = ctor.newInstance() as IMXEncrypting - } catch (e: Exception) { - Timber.e(e, "## setEncryptionInRoom() : fail to load the class") - return false + val alg: IMXEncrypting = when (algorithm) { + MXCRYPTO_ALGORITHM_MEGOLM -> mMXMegolmEncryptionFactory.instantiate(roomId) + else -> mMXOlmEncryptionFactory.instantiate(roomId) } - alg.initWithMatrixSession(this, - mOlmDevice, - mKeysBackup, - deviceListManager, - mCredentials, - mSendToDeviceTask, - mTaskExecutor, - roomId) - synchronized(mRoomEncryptors) { mRoomEncryptors.put(roomId, alg) } @@ -641,44 +550,7 @@ internal class CryptoManager( return if (null != map) ArrayList(map.values) else ArrayList() } - /** - * Try to make sure we have established olm sessions for the given users. - * It must be called in getEncryptingThreadHandler() thread. - * The callback is called in the UI thread. - * - * @param users a list of user ids. - * @param callback the asynchronous callback - */ - fun ensureOlmSessionsForUsers(users: List, callback: MatrixCallback>) { - Timber.d("## ensureOlmSessionsForUsers() : ensureOlmSessionsForUsers $users") - - val devicesByUser = HashMap>() - - for (userId in users) { - devicesByUser[userId] = ArrayList() - - val devices = getUserDevices(userId) - - for (device in devices) { - val key = device.identityKey() - - if (TextUtils.equals(key, mOlmDevice.deviceCurve25519Key)) { - // Don't bother setting up session to ourself - continue - } - - if (device.isVerified) { - // Don't bother setting up sessions with blocked users - continue - } - - devicesByUser[userId]!!.add(device) - } - } - - ensureOlmSessionsForDevices(devicesByUser, callback) - } - + // TODO Remove ? /** * Try to make sure we have established olm sessions for the given devices. * It must be called in getCryptoHandler() thread. @@ -689,150 +561,9 @@ internal class CryptoManager( */ fun ensureOlmSessionsForDevices(devicesByUser: Map>, callback: MatrixCallback>?) { - val devicesWithoutSession = ArrayList() - - val results = MXUsersDevicesMap() - - val userIds = devicesByUser.keys - - for (userId in userIds) { - val deviceInfos = devicesByUser[userId] - - for (deviceInfo in deviceInfos!!) { - val deviceId = deviceInfo.deviceId - val key = deviceInfo.identityKey() - - val sessionId = mOlmDevice.getSessionId(key!!) - - if (TextUtils.isEmpty(sessionId)) { - devicesWithoutSession.add(deviceInfo) - } - - val olmSessionResult = MXOlmSessionResult(deviceInfo, sessionId) - results.setObject(olmSessionResult, userId, deviceId) - } - } - - if (devicesWithoutSession.size == 0) { - callback?.onSuccess(results) - return - } - - // Prepare the request for claiming one-time keys - val usersDevicesToClaim = MXUsersDevicesMap() - - val oneTimeKeyAlgorithm = MXKey.KEY_SIGNED_CURVE_25519_TYPE - - for (device in devicesWithoutSession) { - usersDevicesToClaim.setObject(oneTimeKeyAlgorithm, device.userId, device.deviceId) - } - - // TODO: this has a race condition - if we try to send another message - // while we are claiming a key, we will end up claiming two and setting up - // two sessions. - // - // That should eventually resolve itself, but it's poor form. - - Timber.d("## claimOneTimeKeysForUsersDevices() : $usersDevicesToClaim") - - mClaimOneTimeKeysForUsersDeviceTask - .configureWith(ClaimOneTimeKeysForUsersDeviceTask.Params(usersDevicesToClaim)) - .dispatchTo(object : MatrixCallback> { - override fun onSuccess(data: MXUsersDevicesMap) { - try { - Timber.d("## claimOneTimeKeysForUsersDevices() : keysClaimResponse.oneTimeKeys: $data") - - for (userId in userIds) { - val deviceInfos = devicesByUser[userId] - - for (deviceInfo in deviceInfos!!) { - - var oneTimeKey: MXKey? = null - - val deviceIds = data.getUserDeviceIds(userId) - - if (null != deviceIds) { - for (deviceId in deviceIds) { - val olmSessionResult = results.getObject(deviceId, userId) - - if (null != olmSessionResult!!.mSessionId) { - // We already have a result for this device - continue - } - - val key = data.getObject(deviceId, userId) - - if (TextUtils.equals(key!!.type, oneTimeKeyAlgorithm)) { - oneTimeKey = key - } - - if (null == oneTimeKey) { - Timber.d("## ensureOlmSessionsForDevices() : No one-time keys " + oneTimeKeyAlgorithm - + " for device " + userId + " : " + deviceId) - continue - } - - // Update the result for this device in results - olmSessionResult.mSessionId = verifyKeyAndStartSession(oneTimeKey, userId, deviceInfo) - } - } - } - } - } catch (e: Exception) { - Timber.e(e, "## ensureOlmSessionsForDevices() " + e.message) - } - - callback?.onSuccess(results) - } - - override fun onFailure(failure: Throwable) { - Timber.e(failure, "## ensureOlmSessionsForUsers(): claimOneTimeKeysForUsersDevices request failed") - - callback?.onFailure(failure) - } - }) - .executeBy(mTaskExecutor) + mEnsureOlmSessionsForDevicesAction.handle(devicesByUser, callback) } - private fun verifyKeyAndStartSession(oneTimeKey: MXKey, userId: String, deviceInfo: MXDeviceInfo): String? { - var sessionId: String? = null - - val deviceId = deviceInfo.deviceId - val signKeyId = "ed25519:$deviceId" - val signature = oneTimeKey.signatureForUserId(userId, signKeyId) - - if (!TextUtils.isEmpty(signature) && !TextUtils.isEmpty(deviceInfo.fingerprint())) { - var isVerified = false - var errorMessage: String? = null - - try { - mOlmDevice.verifySignature(deviceInfo.fingerprint()!!, oneTimeKey.signalableJSONDictionary(), signature) - isVerified = true - } catch (e: Exception) { - errorMessage = e.message - } - - // Check one-time key signature - if (isVerified) { - sessionId = mOlmDevice.createOutboundSession(deviceInfo.identityKey()!!, oneTimeKey.value) - - if (!TextUtils.isEmpty(sessionId)) { - Timber.d("## verifyKeyAndStartSession() : Started new sessionid " + sessionId - + " for device " + deviceInfo + "(theirOneTimeKey: " + oneTimeKey.value + ")") - } else { - // Possibly a bad key - Timber.e("## verifyKeyAndStartSession() : Error starting session with device $userId:$deviceId") - } - } else { - Timber.e("## verifyKeyAndStartSession() : Unable to verify signature on one-time key for device " + userId - + ":" + deviceId + " Error " + errorMessage) - } - } - - return sessionId - } - - /** * Encrypt an event content according to the configuration of the room. * @@ -865,7 +596,7 @@ internal class CryptoManager( } // Check whether the event content must be encrypted for the invited members. - val encryptForInvitedMembers = mCryptoConfig!!.mEnableEncryptionForInvitedMembers && room.shouldEncryptForInvitedMembers() + val encryptForInvitedMembers = mCryptoConfig.mEnableEncryptionForInvitedMembers && room.shouldEncryptForInvitedMembers() val userIds = if (encryptForInvitedMembers) { room.getActiveRoomMemberIds() @@ -939,7 +670,7 @@ internal class CryptoManager( val exceptions = ArrayList() var result: MXEventDecryptionResult? = null - val alg = roomDecryptorProvider.getOrCreateRoomDecryptor(this, event.roomId, eventContent["algorithm"] as String) + val alg = roomDecryptorProvider.getOrCreateRoomDecryptor(event.roomId, eventContent["algorithm"] as String) if (null == alg) { val reason = String.format(MXCryptoError.UNABLE_TO_DECRYPT_REASON, event.eventId, eventContent["algorithm"] as String) @@ -954,7 +685,7 @@ internal class CryptoManager( } if (null != result) { - results.add(result) + results.add(result) // TODO simplify } } @@ -976,72 +707,6 @@ internal class CryptoManager( mOlmDevice.resetReplayAttackCheckInTimeline(timelineId) } - /** - * Encrypt an event payload for a list of devices. - * This method must be called from the getCryptoHandler() thread. - * - * @param payloadFields fields to include in the encrypted payload. - * @param deviceInfos list of device infos to encrypt for. - * @return the content for an m.room.encrypted event. - */ - fun encryptMessage(payloadFields: Map, deviceInfos: List): EncryptedMessage { - val deviceInfoParticipantKey = HashMap() - val participantKeys = ArrayList() - - for (di in deviceInfos) { - participantKeys.add(di.identityKey()!!) - deviceInfoParticipantKey[di.identityKey()!!] = di - } - - val payloadJson = HashMap(payloadFields) - - payloadJson["sender"] = mCredentials.userId - payloadJson["sender_device"] = mCredentials.deviceId - - // Include the Ed25519 key so that the recipient knows what - // device this message came from. - // We don't need to include the curve25519 key since the - // recipient will already know this from the olm headers. - // When combined with the device keys retrieved from the - // homeserver signed by the ed25519 key this proves that - // the curve25519 key and the ed25519 key are owned by - // the same device. - val keysMap = HashMap() - keysMap["ed25519"] = mOlmDevice.deviceEd25519Key!! - payloadJson["keys"] = keysMap - - val ciphertext = HashMap() - - for (deviceKey in participantKeys) { - val sessionId = mOlmDevice.getSessionId(deviceKey) - - if (!TextUtils.isEmpty(sessionId)) { - Timber.d("Using sessionid $sessionId for device $deviceKey") - val deviceInfo = deviceInfoParticipantKey[deviceKey] - - payloadJson["recipient"] = deviceInfo!!.userId - - val recipientsKeysMap = HashMap() - recipientsKeysMap["ed25519"] = deviceInfo.fingerprint()!! - payloadJson["recipient_keys"] = recipientsKeysMap - - // FIXME We have to canonicalize the JSON - //JsonUtility.canonicalize(JsonUtility.getGson(false).toJsonTree(payloadJson)).toString() - - val payloadString = convertToUTF8(MoshiProvider.getCanonicalJson(Map::class.java, payloadJson)) - ciphertext[deviceKey] = mOlmDevice.encryptMessage(deviceKey, sessionId!!, payloadString!!)!! - } - } - - val res = EncryptedMessage() - - res.algorithm = MXCRYPTO_ALGORITHM_OLM - res.senderKey = mOlmDevice.deviceCurve25519Key - res.cipherText = ciphertext - - return res - } - /** * Handle the 'toDevice' event * @@ -1061,13 +726,7 @@ internal class CryptoManager( * * @param event the key event. */ - private fun onRoomKeyEvent(event: Event?) { - // sanity check - if (null == event) { - Timber.e("## onRoomKeyEvent() : null event") - return - } - + private fun onRoomKeyEvent(event: Event) { val roomKeyContent = event.content.toModel()!! if (TextUtils.isEmpty(roomKeyContent.roomId) || TextUtils.isEmpty(roomKeyContent.algorithm)) { @@ -1075,14 +734,14 @@ internal class CryptoManager( return } - val alg = roomDecryptorProvider.getOrCreateRoomDecryptor(this, roomKeyContent.roomId, roomKeyContent.algorithm) + val alg = roomDecryptorProvider.getOrCreateRoomDecryptor(roomKeyContent.roomId, roomKeyContent.algorithm) if (null == alg) { - Timber.e("## onRoomKeyEvent() : Unable to handle keys for " + roomKeyContent.algorithm!!) + Timber.e("## onRoomKeyEvent() : Unable to handle keys for " + roomKeyContent.algorithm) return } - alg.onRoomKeyEvent(event) + alg.onRoomKeyEvent(event, mKeysBackup) } /** @@ -1097,7 +756,7 @@ internal class CryptoManager( val room = mRoomService.getRoom(roomId)!! // Check whether the event content must be encrypted for the invited members. - val encryptForInvitedMembers = mCryptoConfig!!.mEnableEncryptionForInvitedMembers && room.shouldEncryptForInvitedMembers() + val encryptForInvitedMembers = mCryptoConfig.mEnableEncryptionForInvitedMembers && room.shouldEncryptForInvitedMembers() val userIds = if (encryptForInvitedMembers) { room.getActiveRoomMemberIds() @@ -1138,7 +797,7 @@ internal class CryptoManager( deviceListManager.startTrackingDeviceList(Arrays.asList(userId)) } else if (membership == Membership.INVITE && room.shouldEncryptForInvitedMembers() - && mCryptoConfig!!.mEnableEncryptionForInvitedMembers) { + && mCryptoConfig.mEnableEncryptionForInvitedMembers) { // track the deviceList for this invited user. // Caution: there's a big edge case here in that federated servers do not // know what other servers are in the room at the time they've been invited. @@ -1159,14 +818,14 @@ internal class CryptoManager( private fun uploadDeviceKeys(callback: MatrixCallback) { // Prepare the device keys data to send // Sign it - val canonicalJson = MoshiProvider.getCanonicalJson(Map::class.java, myDevice.signalableJSONDictionary()) + val canonicalJson = MoshiProvider.getCanonicalJson(Map::class.java, getMyDevice().signalableJSONDictionary()) - myDevice.signatures = mObjectSigner.signObject(canonicalJson) + getMyDevice().signatures = mObjectSigner.signObject(canonicalJson) // For now, we set the device id explicitly, as we may not be using the // same one as used in login. mUploadKeysTask - .configureWith(UploadKeysTask.Params(myDevice.toDeviceKeys(), null, myDevice.deviceId)) + .configureWith(UploadKeysTask.Params(getMyDevice().toDeviceKeys(), null, getMyDevice().deviceId)) .dispatchTo(callback) .executeBy(mTaskExecutor) } @@ -1264,98 +923,7 @@ internal class CryptoManager( Timber.d("## importRoomKeys : JSON parsing " + (t2 - t1) + " ms") - importMegolmSessionsData(importedSessions, true, progressListener, callback) - } - - /** - * Import a list of megolm session keys. - * - * @param megolmSessionsData megolm sessions. - * @param backUpKeys true to back up them to the homeserver. - * @param progressListener the progress listener - * @param callback - */ - override fun importMegolmSessionsData(megolmSessionsData: List, - backUpKeys: Boolean, - progressListener: ProgressListener?, - callback: MatrixCallback) { - val t0 = System.currentTimeMillis() - - val totalNumbersOfKeys = megolmSessionsData.size - var cpt = 0 - var lastProgress = 0 - var totalNumbersOfImportedKeys = 0 - - if (progressListener != null) { - progressListener.onProgress(0, 100) - } - - val sessions = mOlmDevice.importInboundGroupSessions(megolmSessionsData) - - for (megolmSessionData in megolmSessionsData) { - cpt++ - - - val decrypting = roomDecryptorProvider.getOrCreateRoomDecryptor(this, megolmSessionData.roomId, megolmSessionData.algorithm) - - if (null != decrypting) { - try { - val sessionId = megolmSessionData.sessionId - Timber.d("## importRoomKeys retrieve mSenderKey " + megolmSessionData.senderKey + " sessionId " + sessionId) - - totalNumbersOfImportedKeys++ - - // cancel any outstanding room key requests for this session - val roomKeyRequestBody = RoomKeyRequestBody() - - roomKeyRequestBody.algorithm = megolmSessionData.algorithm - roomKeyRequestBody.roomId = megolmSessionData.roomId - roomKeyRequestBody.senderKey = megolmSessionData.senderKey - roomKeyRequestBody.sessionId = megolmSessionData.sessionId - - cancelRoomKeyRequest(roomKeyRequestBody) - - // Have another go at decrypting events sent with this session - decrypting.onNewSession(megolmSessionData.senderKey!!, sessionId!!) - } catch (e: Exception) { - Timber.e(e, "## importRoomKeys() : onNewSession failed") - } - } - - if (progressListener != null) { - val progress = 100 * cpt / totalNumbersOfKeys - - if (lastProgress != progress) { - lastProgress = progress - - progressListener.onProgress(progress, 100) - } - } - } - - // Do not back up the key if it comes from a backup recovery - if (backUpKeys) { - mKeysBackup.maybeBackupKeys() - } else { - mCryptoStore.markBackupDoneForInboundGroupSessions(sessions) - } - - val t1 = System.currentTimeMillis() - - Timber.d("## importMegolmSessionsData : sessions import " + (t1 - t0) + " ms (" + megolmSessionsData.size + " sessions)") - - val finalTotalNumbersOfImportedKeys = totalNumbersOfImportedKeys - - callback.onSuccess(ImportRoomKeysResult(totalNumbersOfKeys, finalTotalNumbersOfImportedKeys)) - } - - /** - * Tells if the encryption must fail if some unknown devices are detected. - * - * @return true to warn when some unknown devices are detected. - */ - fun warnOnUnknownDevices(): Boolean { - return mWarnOnUnknownDevices + mMegolmSessionDataImporter.handle(importedSessions, true, progressListener, callback) } /** @@ -1364,7 +932,7 @@ internal class CryptoManager( * @param warn true to warn when some unknown devices are detected. */ override fun setWarnOnUnknownDevices(warn: Boolean) { - mWarnOnUnknownDevices = warn + mWarnOnUnknownDevicesRepository.setWarnOnUnknownDevices(warn) } /** @@ -1468,7 +1036,6 @@ internal class CryptoManager( * Add this room to the ones which don't encrypt messages to unverified devices. * * @param roomId the room id - * @param callback the asynchronous callback */ override fun setRoomBlacklistUnverifiedDevices(roomId: String) { setRoomBlacklistUnverifiedDevices(roomId, true) @@ -1478,22 +1045,12 @@ internal class CryptoManager( * Remove this room to the ones which don't encrypt messages to unverified devices. * * @param roomId the room id - * @param callback the asynchronous callback */ override fun setRoomUnBlacklistUnverifiedDevices(roomId: String) { setRoomBlacklistUnverifiedDevices(roomId, false) } - /** - * Send a request for some room keys, if we have not already done so. - * - * @param requestBody requestBody - * @param recipients recipients - */ - fun requestRoomKey(requestBody: RoomKeyRequestBody, recipients: List>) { - mOutgoingRoomKeyRequestManager.sendRoomKeyRequest(requestBody, recipients) - } - + // TODO Check if this method is still necessary /** * Cancel any earlier room key request * @@ -1548,7 +1105,7 @@ internal class CryptoManager( * ========================================================================================== */ override fun toString(): String { - return myDevice.userId + " (" + myDevice.deviceId + ")" + return "CryptoManager of " + mCredentials.userId + " (" + mCredentials.deviceId + ")" } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/CryptoModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/CryptoModule.kt index 9093a5fe..6792b124 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/CryptoModule.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/CryptoModule.kt @@ -19,11 +19,16 @@ package im.vector.matrix.android.internal.crypto import android.content.Context import im.vector.matrix.android.api.auth.data.Credentials import im.vector.matrix.android.api.session.crypto.CryptoService -import im.vector.matrix.android.internal.crypto.actions.SetDeviceVerificationAction +import im.vector.matrix.android.internal.crypto.actions.* +import im.vector.matrix.android.internal.crypto.algorithms.megolm.MXMegolmDecryptionFactory +import im.vector.matrix.android.internal.crypto.algorithms.megolm.MXMegolmEncryptionFactory +import im.vector.matrix.android.internal.crypto.algorithms.olm.MXOlmDecryptionFactory +import im.vector.matrix.android.internal.crypto.algorithms.olm.MXOlmEncryptionFactory import im.vector.matrix.android.internal.crypto.api.CryptoApi import im.vector.matrix.android.internal.crypto.keysbackup.KeysBackup import im.vector.matrix.android.internal.crypto.keysbackup.api.RoomKeysApi import im.vector.matrix.android.internal.crypto.keysbackup.tasks.* +import im.vector.matrix.android.internal.crypto.repository.WarnOnUnknownDeviceRepository import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore import im.vector.matrix.android.internal.crypto.store.db.RealmCryptoStore import im.vector.matrix.android.internal.crypto.store.db.RealmCryptoStoreMigration @@ -88,7 +93,7 @@ internal class CryptoModule { } scope(DefaultSession.SCOPE) { - RoomDecryptorProvider(get(), get(), get(), get(), get()) + RoomDecryptorProvider(get(), get()) } scope(DefaultSession.SCOPE) { @@ -113,6 +118,56 @@ internal class CryptoModule { SetDeviceVerificationAction(get(), get(), get()) } + // Device info + scope(DefaultSession.SCOPE) { + MyDeviceInfoHolder(get(), get(), get()) + } + + scope(DefaultSession.SCOPE) { + EnsureOlmSessionsForDevicesAction(get(), get(), get()) + } + + scope(DefaultSession.SCOPE) { + EnsureOlmSessionsForUsersAction(get(), get(), get()) + } + + scope(DefaultSession.SCOPE) { + MegolmSessionDataImporter(get(), get(), get(), get()) + } + + scope(DefaultSession.SCOPE) { + MessageEncrypter(get(), get()) + } + + + scope(DefaultSession.SCOPE) { + WarnOnUnknownDeviceRepository() + } + + // Factories + scope(DefaultSession.SCOPE) { + MXMegolmDecryptionFactory( + get(), get(), get(), get(), get(), get(), get(), get(), get() + ) + } + + scope(DefaultSession.SCOPE) { + MXMegolmEncryptionFactory( + get(), get(), get(), get(), get(), get(), get(), get(), get(), get() + ) + } + + scope(DefaultSession.SCOPE) { + MXOlmDecryptionFactory( + get(), get() + ) + } + + scope(DefaultSession.SCOPE) { + MXOlmEncryptionFactory( + get(), get(), get(), get(), get() + ) + } // CryptoManager scope(DefaultSession.SCOPE) { @@ -131,8 +186,14 @@ internal class CryptoModule { get(), get(), get(), + get(), + get(), // Actions get(), + get(), + get(), + // Factory + get(), get(), // Tasks get(), get(), get(), get(), get(), get(), get(), // Task executor @@ -200,6 +261,7 @@ internal class CryptoModule { get(), get(), get(), + get(), // Task get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), // Task executor @@ -255,7 +317,7 @@ internal class CryptoModule { * ========================================================================================== */ scope(DefaultSession.SCOPE) { - DefaultSasVerificationService(get(), get(), get(), get(), get()) + DefaultSasVerificationService(get(), get(), get(), get(), get(), get(), get()) } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingRoomKeyRequestManager.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingRoomKeyRequestManager.kt index d3b81731..57572769 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingRoomKeyRequestManager.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingRoomKeyRequestManager.kt @@ -38,7 +38,7 @@ internal class IncomingRoomKeyRequestManager( private val mReceivedRoomKeyRequestCancellations = ArrayList() // the listeners - val mRoomKeysRequestListeners: MutableSet = HashSet() + private val mRoomKeysRequestListeners: MutableSet = HashSet() init { mReceivedRoomKeyRequests.addAll(mCryptoStore.getPendingIncomingRoomKeyRequests()) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MXCryptoAlgorithms.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MXCryptoAlgorithms.kt index 292839bd..61622c37 100755 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MXCryptoAlgorithms.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MXCryptoAlgorithms.kt @@ -17,60 +17,19 @@ package im.vector.matrix.android.internal.crypto -import android.text.TextUtils -import im.vector.matrix.android.internal.crypto.algorithms.IMXDecrypting -import im.vector.matrix.android.internal.crypto.algorithms.IMXEncrypting -import timber.log.Timber -import java.util.* - internal object MXCryptoAlgorithms { - // encryptors map - private val mEncryptors: MutableMap> - - // decryptors map - private val mDecryptors: MutableMap> - - init { - mEncryptors = HashMap() - try { - mEncryptors[MXCRYPTO_ALGORITHM_MEGOLM] = Class.forName("im.vector.matrix.android.internal.crypto.algorithms.megolm.MXMegolmEncryption") as Class - } catch (e: Exception) { - Timber.e("## MXCryptoAlgorithms() : fails to add MXCRYPTO_ALGORITHM_MEGOLM") - } - - try { - mEncryptors[MXCRYPTO_ALGORITHM_OLM] = Class.forName("im.vector.matrix.android.internal.crypto.algorithms.olm.MXOlmEncryption") as Class - } catch (e: Exception) { - Timber.e("## MXCryptoAlgorithms() : fails to add MXCRYPTO_ALGORITHM_OLM") - } - - mDecryptors = HashMap() - try { - mDecryptors[MXCRYPTO_ALGORITHM_MEGOLM] = Class.forName("im.vector.matrix.android.internal.crypto.algorithms.megolm.MXMegolmDecryption") as Class - } catch (e: Exception) { - Timber.e("## MXCryptoAlgorithms() : fails to add MXCRYPTO_ALGORITHM_MEGOLM") - } - - try { - mDecryptors[MXCRYPTO_ALGORITHM_OLM] = Class.forName("im.vector.matrix.android.internal.crypto.algorithms.olm.MXOlmDecryption") as Class - } catch (e: Exception) { - Timber.e("## MXCryptoAlgorithms() : fails to add MXCRYPTO_ALGORITHM_OLM") - } - - } - /** * Get the class implementing encryption for the provided algorithm. * * @param algorithm the algorithm tag. * @return A class implementing 'IMXEncrypting'. */ - fun encryptorClassForAlgorithm(algorithm: String?): Class? { - return if (!TextUtils.isEmpty(algorithm)) { - mEncryptors[algorithm] - } else { - null + fun hasEncryptorClassForAlgorithm(algorithm: String?): Boolean { + return when (algorithm) { + MXCRYPTO_ALGORITHM_MEGOLM, + MXCRYPTO_ALGORITHM_OLM -> true + else -> false } } @@ -81,11 +40,11 @@ internal object MXCryptoAlgorithms { * @return A class implementing 'IMXDecrypting'. */ - fun decryptorClassForAlgorithm(algorithm: String?): Class? { - return if (!TextUtils.isEmpty(algorithm)) { - mDecryptors[algorithm] - } else { - null + fun hasDecryptorClassForAlgorithm(algorithm: String?): Boolean { + return when (algorithm) { + MXCRYPTO_ALGORITHM_MEGOLM, + MXCRYPTO_ALGORITHM_OLM -> true + else -> false } } @@ -93,6 +52,6 @@ internal object MXCryptoAlgorithms { * @return The list of registered algorithms. */ fun supportedAlgorithms(): List { - return ArrayList(mEncryptors.keys) + return listOf(MXCRYPTO_ALGORITHM_MEGOLM, MXCRYPTO_ALGORITHM_OLM) } } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MXOutgoingRoomKeyRequestManager.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MXOutgoingRoomKeyRequestManager.kt index ce186c44..80f908dd 100755 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MXOutgoingRoomKeyRequestManager.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MXOutgoingRoomKeyRequestManager.kt @@ -37,7 +37,7 @@ internal class MXOutgoingRoomKeyRequestManager( private val mTaskExecutor: TaskExecutor) { // running - var mClientRunning: Boolean = false + private var mClientRunning: Boolean = false // transaction counter private var mTxnCtr: Int = 0 diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MyDeviceInfoHolder.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MyDeviceInfoHolder.kt new file mode 100644 index 00000000..0eb376f6 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MyDeviceInfoHolder.kt @@ -0,0 +1,70 @@ +/* + * Copyright 2019 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 im.vector.matrix.android.internal.crypto + +import android.text.TextUtils +import im.vector.matrix.android.api.auth.data.Credentials +import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo +import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore +import java.util.* + +internal class MyDeviceInfoHolder( + // The credentials, + credentials: Credentials, + // the crypto store + cryptoStore: IMXCryptoStore, + // Olm device + olmDevice: MXOlmDevice +) { + // Our device keys + /** + * my device info + */ + val myDevice: MXDeviceInfo = MXDeviceInfo(credentials.deviceId!!, credentials.userId) + + init { + val keys = HashMap() + + if (!TextUtils.isEmpty(olmDevice.deviceEd25519Key)) { + keys["ed25519:" + credentials.deviceId] = olmDevice.deviceEd25519Key!! + } + + if (!TextUtils.isEmpty(olmDevice.deviceCurve25519Key)) { + keys["curve25519:" + credentials.deviceId] = olmDevice.deviceCurve25519Key!! + } + + myDevice.keys = keys + + myDevice.algorithms = MXCryptoAlgorithms.supportedAlgorithms() + myDevice.mVerified = MXDeviceInfo.DEVICE_VERIFICATION_VERIFIED + + // Add our own deviceinfo to the store + val endToEndDevicesForUser = cryptoStore.getUserDevices(credentials.userId) + + val myDevices: MutableMap + + if (null != endToEndDevicesForUser) { + myDevices = HashMap(endToEndDevicesForUser) + } else { + myDevices = HashMap() + } + + myDevices[myDevice.deviceId] = myDevice + + cryptoStore.storeUserDevices(credentials.userId, myDevices) + } +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/OneTimeKeysManager.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/OneTimeKeysManager.kt index fa085edf..8c0df101 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/OneTimeKeysManager.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/OneTimeKeysManager.kt @@ -108,7 +108,9 @@ internal class OneTimeKeysManager( // But that message might never arrive leaving us stuck with duff // private keys clogging up our local storage. // So we need some kind of engineering compromise to balance all of - // these factors. // TODO Why we do not set mOneTimeKeyCount here? + // these factors. + // TODO Why we do not set mOneTimeKeyCount here? + // TODO This is not needed anymore, see https://github.com/matrix-org/matrix-js-sdk/pull/493 (TODO on iOS also) val keyCount = data.oneTimeKeyCountsForAlgorithm(MXKey.KEY_SIGNED_CURVE_25519_TYPE) uploadOTK(keyCount, keyLimit, callback) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/RoomDecryptorProvider.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/RoomDecryptorProvider.kt index 7a687221..9aa1f179 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/RoomDecryptorProvider.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/RoomDecryptorProvider.kt @@ -17,19 +17,15 @@ package im.vector.matrix.android.internal.crypto import android.text.TextUtils -import im.vector.matrix.android.api.auth.data.Credentials import im.vector.matrix.android.internal.crypto.algorithms.IMXDecrypting -import im.vector.matrix.android.internal.crypto.tasks.SendToDeviceTask -import im.vector.matrix.android.internal.task.TaskExecutor +import im.vector.matrix.android.internal.crypto.algorithms.megolm.MXMegolmDecryptionFactory +import im.vector.matrix.android.internal.crypto.algorithms.olm.MXOlmDecryptionFactory import timber.log.Timber import java.util.* internal class RoomDecryptorProvider( - val mCredentials: Credentials, - val olmDevice: MXOlmDevice, - val deviceListManager: DeviceListManager, - val mSendToDeviceTask: SendToDeviceTask, - val mTaskExecutor: TaskExecutor + private val mMXOlmDecryptionFactory: MXOlmDecryptionFactory, + private val mMXMegolmDecryptionFactory: MXMegolmDecryptionFactory ) { // A map from algorithm to MXDecrypting instance, for each room @@ -43,20 +39,15 @@ internal class RoomDecryptorProvider( * @param roomId the room id * @param algorithm the crypto algorithm * @return the decryptor - * TODO do not provide cryptoManager? // TODO Create another method for the case of roomId is null + * // TODO Create another method for the case of roomId is null */ - fun getOrCreateRoomDecryptor(cryptoManager: CryptoManager, roomId: String?, algorithm: String?): IMXDecrypting? { + fun getOrCreateRoomDecryptor(roomId: String?, algorithm: String?): IMXDecrypting? { // sanity check if (TextUtils.isEmpty(algorithm)) { Timber.e("## getRoomDecryptor() : null algorithm") return null } - if (null == mRoomDecryptors) { - Timber.e("## getRoomDecryptor() : null mRoomDecryptors") - return null - } - var alg: IMXDecrypting? = null if (!TextUtils.isEmpty(roomId)) { @@ -73,27 +64,21 @@ internal class RoomDecryptorProvider( } } - val decryptingClass = MXCryptoAlgorithms.decryptorClassForAlgorithm(algorithm) + val decryptingClass = MXCryptoAlgorithms.hasDecryptorClassForAlgorithm(algorithm) - if (null != decryptingClass) { - try { - val ctor = decryptingClass.constructors[0] - alg = ctor.newInstance() as IMXDecrypting - - if (null != alg) { - alg!!.initWithMatrixSession(mCredentials, cryptoManager, olmDevice, deviceListManager, mSendToDeviceTask, mTaskExecutor) - - if (!TextUtils.isEmpty(roomId)) { - synchronized(mRoomDecryptors) { - mRoomDecryptors[roomId]!!.put(algorithm!!, alg!!) - } - } - } - } catch (e: Exception) { - Timber.e(e, "## getRoomDecryptor() : fail to load the class") - return null + if (decryptingClass) { + alg = when (algorithm) { + MXCRYPTO_ALGORITHM_MEGOLM -> mMXMegolmDecryptionFactory.instantiate() + else -> mMXOlmDecryptionFactory.instantiate() } + if (null != alg) { + if (!TextUtils.isEmpty(roomId)) { + synchronized(mRoomDecryptors) { + mRoomDecryptors[roomId]!!.put(algorithm!!, alg!!) + } + } + } } return alg diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/EnsureOlmSessionsForDevicesAction.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/EnsureOlmSessionsForDevicesAction.kt new file mode 100644 index 00000000..d2e6a314 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/EnsureOlmSessionsForDevicesAction.kt @@ -0,0 +1,182 @@ +/* + * Copyright 2019 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 im.vector.matrix.android.internal.crypto.actions + +import android.text.TextUtils +import im.vector.matrix.android.api.MatrixCallback +import im.vector.matrix.android.internal.crypto.MXOlmDevice +import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo +import im.vector.matrix.android.internal.crypto.model.MXKey +import im.vector.matrix.android.internal.crypto.model.MXOlmSessionResult +import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap +import im.vector.matrix.android.internal.crypto.tasks.ClaimOneTimeKeysForUsersDeviceTask +import im.vector.matrix.android.internal.task.TaskExecutor +import im.vector.matrix.android.internal.task.configureWith +import timber.log.Timber +import java.util.* + +internal class EnsureOlmSessionsForDevicesAction(private val mOlmDevice: MXOlmDevice, + private val mClaimOneTimeKeysForUsersDeviceTask: ClaimOneTimeKeysForUsersDeviceTask, + private val mTaskExecutor: TaskExecutor) { + + + fun handle(devicesByUser: Map>, callback: MatrixCallback>?) { + val devicesWithoutSession = ArrayList() + + val results = MXUsersDevicesMap() + + val userIds = devicesByUser.keys + + for (userId in userIds) { + val deviceInfos = devicesByUser[userId] + + for (deviceInfo in deviceInfos!!) { + val deviceId = deviceInfo.deviceId + val key = deviceInfo.identityKey() + + val sessionId = mOlmDevice.getSessionId(key!!) + + if (TextUtils.isEmpty(sessionId)) { + devicesWithoutSession.add(deviceInfo) + } + + val olmSessionResult = MXOlmSessionResult(deviceInfo, sessionId) + results.setObject(olmSessionResult, userId, deviceId) + } + } + + if (devicesWithoutSession.size == 0) { + callback?.onSuccess(results) + return + } + + // Prepare the request for claiming one-time keys + val usersDevicesToClaim = MXUsersDevicesMap() + + val oneTimeKeyAlgorithm = MXKey.KEY_SIGNED_CURVE_25519_TYPE + + for (device in devicesWithoutSession) { + usersDevicesToClaim.setObject(oneTimeKeyAlgorithm, device.userId, device.deviceId) + } + + // TODO: this has a race condition - if we try to send another message + // while we are claiming a key, we will end up claiming two and setting up + // two sessions. + // + // That should eventually resolve itself, but it's poor form. + + Timber.d("## claimOneTimeKeysForUsersDevices() : $usersDevicesToClaim") + + mClaimOneTimeKeysForUsersDeviceTask + .configureWith(ClaimOneTimeKeysForUsersDeviceTask.Params(usersDevicesToClaim)) + .dispatchTo(object : MatrixCallback> { + override fun onSuccess(data: MXUsersDevicesMap) { + try { + Timber.d("## claimOneTimeKeysForUsersDevices() : keysClaimResponse.oneTimeKeys: $data") + + for (userId in userIds) { + val deviceInfos = devicesByUser[userId] + + for (deviceInfo in deviceInfos!!) { + + var oneTimeKey: MXKey? = null + + val deviceIds = data.getUserDeviceIds(userId) + + if (null != deviceIds) { + for (deviceId in deviceIds) { + val olmSessionResult = results.getObject(deviceId, userId) + + if (null != olmSessionResult!!.mSessionId) { + // We already have a result for this device + continue + } + + val key = data.getObject(deviceId, userId) + + if (TextUtils.equals(key!!.type, oneTimeKeyAlgorithm)) { + oneTimeKey = key + } + + if (null == oneTimeKey) { + Timber.d("## ensureOlmSessionsForDevices() : No one-time keys " + oneTimeKeyAlgorithm + + " for device " + userId + " : " + deviceId) + continue + } + + // Update the result for this device in results + olmSessionResult.mSessionId = verifyKeyAndStartSession(oneTimeKey, userId, deviceInfo) + } + } + } + } + } catch (e: Exception) { + Timber.e(e, "## ensureOlmSessionsForDevices() " + e.message) + } + + callback?.onSuccess(results) + } + + override fun onFailure(failure: Throwable) { + Timber.e(failure, "## ensureOlmSessionsForUsers(): claimOneTimeKeysForUsersDevices request failed") + + callback?.onFailure(failure) + } + }) + .executeBy(mTaskExecutor) + } + + + private fun verifyKeyAndStartSession(oneTimeKey: MXKey, userId: String, deviceInfo: MXDeviceInfo): String? { + var sessionId: String? = null + + val deviceId = deviceInfo.deviceId + val signKeyId = "ed25519:$deviceId" + val signature = oneTimeKey.signatureForUserId(userId, signKeyId) + + if (!TextUtils.isEmpty(signature) && !TextUtils.isEmpty(deviceInfo.fingerprint())) { + var isVerified = false + var errorMessage: String? = null + + try { + mOlmDevice.verifySignature(deviceInfo.fingerprint()!!, oneTimeKey.signalableJSONDictionary(), signature) + isVerified = true + } catch (e: Exception) { + errorMessage = e.message + } + + // Check one-time key signature + if (isVerified) { + sessionId = mOlmDevice.createOutboundSession(deviceInfo.identityKey()!!, oneTimeKey.value) + + if (!TextUtils.isEmpty(sessionId)) { + Timber.d("## verifyKeyAndStartSession() : Started new sessionid " + sessionId + + " for device " + deviceInfo + "(theirOneTimeKey: " + oneTimeKey.value + ")") + } else { + // Possibly a bad key + Timber.e("## verifyKeyAndStartSession() : Error starting session with device $userId:$deviceId") + } + } else { + Timber.e("## verifyKeyAndStartSession() : Unable to verify signature on one-time key for device " + userId + + ":" + deviceId + " Error " + errorMessage) + } + } + + return sessionId + } + +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/EnsureOlmSessionsForUsersAction.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/EnsureOlmSessionsForUsersAction.kt new file mode 100644 index 00000000..de9101d7 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/EnsureOlmSessionsForUsersAction.kt @@ -0,0 +1,70 @@ +/* + * Copyright 2019 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 im.vector.matrix.android.internal.crypto.actions + +import android.text.TextUtils +import im.vector.matrix.android.api.MatrixCallback +import im.vector.matrix.android.internal.crypto.MXOlmDevice +import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo +import im.vector.matrix.android.internal.crypto.model.MXOlmSessionResult +import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap +import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore +import timber.log.Timber +import java.util.* + +internal class EnsureOlmSessionsForUsersAction(private val mOlmDevice: MXOlmDevice, + private val mCryptoStore: IMXCryptoStore, + private val mEnsureOlmSessionsForDevicesAction: EnsureOlmSessionsForDevicesAction) { + + /** + * Try to make sure we have established olm sessions for the given users. + * It must be called in getEncryptingThreadHandler() thread. + * The callback is called in the UI thread. + * + * @param users a list of user ids. + * @param callback the asynchronous callback + */ + fun handle(users: List, callback: MatrixCallback>) { + Timber.d("## ensureOlmSessionsForUsers() : ensureOlmSessionsForUsers $users") + + val devicesByUser = HashMap>() + + for (userId in users) { + devicesByUser[userId] = ArrayList() + + val devices = mCryptoStore.getUserDevices(userId)?.values ?: emptyList() + + for (device in devices) { + val key = device.identityKey() + + if (TextUtils.equals(key, mOlmDevice.deviceCurve25519Key)) { + // Don't bother setting up session to ourself + continue + } + + if (device.isVerified) { + // Don't bother setting up sessions with blocked users + continue + } + + devicesByUser[userId]!!.add(device) + } + } + + mEnsureOlmSessionsForDevicesAction.handle(devicesByUser, callback) + } +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/MegolmSessionDataImporter.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/MegolmSessionDataImporter.kt new file mode 100644 index 00000000..1c04b769 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/MegolmSessionDataImporter.kt @@ -0,0 +1,117 @@ +/* + * Copyright 2019 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 im.vector.matrix.android.internal.crypto.actions + +import im.vector.matrix.android.api.MatrixCallback +import im.vector.matrix.android.api.listeners.ProgressListener +import im.vector.matrix.android.internal.crypto.* +import im.vector.matrix.android.internal.crypto.model.ImportRoomKeysResult +import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyRequestBody +import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore +import timber.log.Timber + +internal class MegolmSessionDataImporter(private val mOlmDevice: MXOlmDevice, + private val roomDecryptorProvider: RoomDecryptorProvider, + private val mOutgoingRoomKeyRequestManager: MXOutgoingRoomKeyRequestManager, + private val mCryptoStore: IMXCryptoStore) { + + /** + * Import a list of megolm session keys. + * + * @param megolmSessionsData megolm sessions. + * @param backUpKeys true to back up them to the homeserver. + * @param progressListener the progress listener + * @param callback + */ + fun handle(megolmSessionsData: List, + fromBackup: Boolean, + progressListener: ProgressListener?, + callback: MatrixCallback) { + val t0 = System.currentTimeMillis() + + val totalNumbersOfKeys = megolmSessionsData.size + var cpt = 0 + var lastProgress = 0 + var totalNumbersOfImportedKeys = 0 + + if (progressListener != null) { + CryptoAsyncHelper.getUiHandler().post { + progressListener.onProgress(0, 100) + } + } + + val sessions = mOlmDevice.importInboundGroupSessions(megolmSessionsData) + + for (megolmSessionData in megolmSessionsData) { + cpt++ + + + val decrypting = roomDecryptorProvider.getOrCreateRoomDecryptor(megolmSessionData.roomId, megolmSessionData.algorithm) + + if (null != decrypting) { + try { + val sessionId = megolmSessionData.sessionId + Timber.d("## importRoomKeys retrieve mSenderKey " + megolmSessionData.senderKey + " sessionId " + sessionId) + + totalNumbersOfImportedKeys++ + + // cancel any outstanding room key requests for this session + val roomKeyRequestBody = RoomKeyRequestBody() + + roomKeyRequestBody.algorithm = megolmSessionData.algorithm + roomKeyRequestBody.roomId = megolmSessionData.roomId + roomKeyRequestBody.senderKey = megolmSessionData.senderKey + roomKeyRequestBody.sessionId = megolmSessionData.sessionId + + mOutgoingRoomKeyRequestManager.cancelRoomKeyRequest(roomKeyRequestBody) + + // Have another go at decrypting events sent with this session + decrypting.onNewSession(megolmSessionData.senderKey!!, sessionId!!) + } catch (e: Exception) { + Timber.e(e, "## importRoomKeys() : onNewSession failed") + } + } + + if (progressListener != null) { + CryptoAsyncHelper.getUiHandler().post { + val progress = 100 * cpt / totalNumbersOfKeys + + if (lastProgress != progress) { + lastProgress = progress + + progressListener.onProgress(progress, 100) + } + } + } + } + + // Do not back up the key if it comes from a backup recovery + if (fromBackup) { + mCryptoStore.markBackupDoneForInboundGroupSessions(sessions) + } + + val t1 = System.currentTimeMillis() + + Timber.d("## importMegolmSessionsData : sessions import " + (t1 - t0) + " ms (" + megolmSessionsData.size + " sessions)") + + val finalTotalNumbersOfImportedKeys = totalNumbersOfImportedKeys + + CryptoAsyncHelper.getUiHandler().post { + callback.onSuccess(ImportRoomKeysResult(totalNumbersOfKeys, finalTotalNumbersOfImportedKeys)) + } + } +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/MessageEncrypter.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/MessageEncrypter.kt new file mode 100644 index 00000000..40c96302 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/MessageEncrypter.kt @@ -0,0 +1,99 @@ +/* + * Copyright 2019 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 im.vector.matrix.android.internal.crypto.actions + +import android.text.TextUtils +import im.vector.matrix.android.api.auth.data.Credentials +import im.vector.matrix.android.internal.crypto.MXCRYPTO_ALGORITHM_OLM +import im.vector.matrix.android.internal.crypto.MXOlmDevice +import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo +import im.vector.matrix.android.internal.crypto.model.rest.EncryptedMessage +import im.vector.matrix.android.internal.di.MoshiProvider +import im.vector.matrix.android.internal.util.convertToUTF8 +import timber.log.Timber +import java.util.* + +internal class MessageEncrypter(private val mCredentials: Credentials, + private val mOlmDevice: MXOlmDevice) { + + /** + * Encrypt an event payload for a list of devices. + * This method must be called from the getCryptoHandler() thread. + * + * @param payloadFields fields to include in the encrypted payload. + * @param deviceInfos list of device infos to encrypt for. + * @return the content for an m.room.encrypted event. + */ + fun encryptMessage(payloadFields: Map, deviceInfos: List): EncryptedMessage { + val deviceInfoParticipantKey = HashMap() + val participantKeys = ArrayList() + + for (di in deviceInfos) { + participantKeys.add(di.identityKey()!!) + deviceInfoParticipantKey[di.identityKey()!!] = di + } + + val payloadJson = HashMap(payloadFields) + + payloadJson["sender"] = mCredentials.userId + payloadJson["sender_device"] = mCredentials.deviceId + + // Include the Ed25519 key so that the recipient knows what + // device this message came from. + // We don't need to include the curve25519 key since the + // recipient will already know this from the olm headers. + // When combined with the device keys retrieved from the + // homeserver signed by the ed25519 key this proves that + // the curve25519 key and the ed25519 key are owned by + // the same device. + val keysMap = HashMap() + keysMap["ed25519"] = mOlmDevice.deviceEd25519Key!! + payloadJson["keys"] = keysMap + + val ciphertext = HashMap() + + for (deviceKey in participantKeys) { + val sessionId = mOlmDevice.getSessionId(deviceKey) + + if (!TextUtils.isEmpty(sessionId)) { + Timber.d("Using sessionid $sessionId for device $deviceKey") + val deviceInfo = deviceInfoParticipantKey[deviceKey] + + payloadJson["recipient"] = deviceInfo!!.userId + + val recipientsKeysMap = HashMap() + recipientsKeysMap["ed25519"] = deviceInfo.fingerprint()!! + payloadJson["recipient_keys"] = recipientsKeysMap + + // FIXME We have to canonicalize the JSON + //JsonUtility.canonicalize(JsonUtility.getGson(false).toJsonTree(payloadJson)).toString() + + val payloadString = convertToUTF8(MoshiProvider.getCanonicalJson(Map::class.java, payloadJson)) + ciphertext[deviceKey] = mOlmDevice.encryptMessage(deviceKey, sessionId!!, payloadString!!)!! + } + } + + val res = EncryptedMessage() + + res.algorithm = MXCRYPTO_ALGORITHM_OLM + res.senderKey = mOlmDevice.deviceCurve25519Key + res.cipherText = ciphertext + + return res + } + +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/IMXDecrypting.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/IMXDecrypting.kt index a8711a1b..b6a911c8 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/IMXDecrypting.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/IMXDecrypting.kt @@ -17,30 +17,17 @@ package im.vector.matrix.android.internal.crypto.algorithms -import im.vector.matrix.android.api.auth.data.Credentials import im.vector.matrix.android.api.session.events.model.Event -import im.vector.matrix.android.internal.crypto.* -import im.vector.matrix.android.internal.crypto.CryptoManager -import im.vector.matrix.android.internal.crypto.tasks.SendToDeviceTask -import im.vector.matrix.android.internal.task.TaskExecutor +import im.vector.matrix.android.internal.crypto.IncomingRoomKeyRequest +import im.vector.matrix.android.internal.crypto.MXDecryptionException +import im.vector.matrix.android.internal.crypto.MXEventDecryptionResult +import im.vector.matrix.android.internal.crypto.keysbackup.KeysBackup /** * An interface for decrypting data */ internal interface IMXDecrypting { - /** - * Init the object fields - * - * @param matrixSession the session - */ - fun initWithMatrixSession(credentials: Credentials, - crypto: CryptoManager, - olmDevice: MXOlmDevice, - deviceListManager: DeviceListManager, - sendToDeviceTask: SendToDeviceTask, - taskExecutor: TaskExecutor) - /** * Decrypt an event * @@ -57,7 +44,7 @@ internal interface IMXDecrypting { * * @param event the key event. */ - fun onRoomKeyEvent(event: Event) + fun onRoomKeyEvent(event: Event, keysBackup: KeysBackup) {} /** * Check if the some messages can be decrypted with a new session @@ -65,7 +52,7 @@ internal interface IMXDecrypting { * @param senderKey the session sender key * @param sessionId the session id */ - fun onNewSession(senderKey: String, sessionId: String) + fun onNewSession(senderKey: String, sessionId: String) {} /** * Determine if we have the keys necessary to respond to a room key request @@ -73,12 +60,12 @@ internal interface IMXDecrypting { * @param request keyRequest * @return true if we have the keys and could (theoretically) share */ - fun hasKeysForKeyRequest(request: IncomingRoomKeyRequest): Boolean + fun hasKeysForKeyRequest(request: IncomingRoomKeyRequest): Boolean = false /** * Send the response to a room key request. * * @param request keyRequest */ - fun shareKeysWithDevice(request: IncomingRoomKeyRequest) + fun shareKeysWithDevice(request: IncomingRoomKeyRequest) {} } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/IMXEncrypting.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/IMXEncrypting.kt index bbfe7201..baa5fb64 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/IMXEncrypting.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/IMXEncrypting.kt @@ -18,35 +18,13 @@ package im.vector.matrix.android.internal.crypto.algorithms import im.vector.matrix.android.api.MatrixCallback -import im.vector.matrix.android.api.auth.data.Credentials import im.vector.matrix.android.api.session.events.model.Content -import im.vector.matrix.android.internal.crypto.DeviceListManager -import im.vector.matrix.android.internal.crypto.MXOlmDevice -import im.vector.matrix.android.internal.crypto.CryptoManager -import im.vector.matrix.android.internal.crypto.keysbackup.KeysBackup -import im.vector.matrix.android.internal.crypto.tasks.SendToDeviceTask -import im.vector.matrix.android.internal.task.TaskExecutor /** * An interface for encrypting data */ internal interface IMXEncrypting { - /** - * Init - * - * @param matrixSession the related 'MXSession'. - * @param roomId the id of the room we will be sending to. - */ - fun initWithMatrixSession(crypto: CryptoManager, - olmDevice: MXOlmDevice, - keysBackup: KeysBackup, - deviceListManager: DeviceListManager, - credentials: Credentials, - sendToDeviceTask: SendToDeviceTask, - taskExecutor: TaskExecutor, - roomId: String) - /** * Encrypt an event content according to the configuration of the room. * diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt index b87867d5..73eab5c5 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt @@ -18,7 +18,6 @@ package im.vector.matrix.android.internal.crypto.algorithms.megolm import android.text.TextUtils -import androidx.annotation.Keep import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.auth.data.Credentials import im.vector.matrix.android.api.session.crypto.MXCryptoError @@ -26,8 +25,11 @@ import im.vector.matrix.android.api.session.events.model.Event import im.vector.matrix.android.api.session.events.model.EventType import im.vector.matrix.android.api.session.events.model.toModel import im.vector.matrix.android.internal.crypto.* +import im.vector.matrix.android.internal.crypto.actions.EnsureOlmSessionsForDevicesAction +import im.vector.matrix.android.internal.crypto.actions.MessageEncrypter import im.vector.matrix.android.internal.crypto.algorithms.IMXDecrypting import im.vector.matrix.android.internal.crypto.algorithms.MXDecryptionResult +import im.vector.matrix.android.internal.crypto.keysbackup.KeysBackup import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo import im.vector.matrix.android.internal.crypto.model.MXOlmSessionResult import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap @@ -42,44 +44,22 @@ import im.vector.matrix.android.internal.task.configureWith import timber.log.Timber import java.util.* -@Keep -internal class MXMegolmDecryption : IMXDecrypting { - /** - * The olm device interface - */ - - // the matrix credentials - private lateinit var mCredentials: Credentials - - private lateinit var mCrypto: CryptoManager - private lateinit var mOlmDevice: MXOlmDevice - private lateinit var mDeviceListManager: DeviceListManager - private lateinit var mCryptoStore: IMXCryptoStore - private lateinit var mSendToDeviceTask: SendToDeviceTask - private lateinit var mTaskExecutor: TaskExecutor +internal class MXMegolmDecryption(private val mCredentials: Credentials, + private val mOlmDevice: MXOlmDevice, + private val mDeviceListManager: DeviceListManager, + private val mOutgoingRoomKeyRequestManager: MXOutgoingRoomKeyRequestManager, + private val mMessageEncrypter: MessageEncrypter, + private val mEnsureOlmSessionsForDevicesAction: EnsureOlmSessionsForDevicesAction, + private val mCryptoStore: IMXCryptoStore, + private val mSendToDeviceTask: SendToDeviceTask, + private val mTaskExecutor: TaskExecutor) + : IMXDecrypting { /** * Events which we couldn't decrypt due to unknown sessions / indexes: map from * senderKey|sessionId to timelines to list of MatrixEvents. */ - private var mPendingEvents: MutableMap>> = HashMap() - - /** - * Init the object fields - */ - override fun initWithMatrixSession(credentials: Credentials, - crypto: CryptoManager, - olmDevice: MXOlmDevice, - deviceListManager: DeviceListManager, - sendToDeviceTask: SendToDeviceTask, - taskExecutor: TaskExecutor) { - mCredentials = credentials - mCrypto = crypto - mOlmDevice = olmDevice - mDeviceListManager = deviceListManager - mSendToDeviceTask = sendToDeviceTask - mTaskExecutor = taskExecutor - } + private var mPendingEvents: MutableMap>> = HashMap() @Throws(MXDecryptionException::class) override fun decryptEvent(event: Event, timeline: String): MXEventDecryptionResult? { @@ -87,8 +67,8 @@ internal class MXMegolmDecryption : IMXDecrypting { } @Throws(MXDecryptionException::class) - private fun decryptEvent(event: Event?, timeline: String, requestKeysOnFail: Boolean): MXEventDecryptionResult? { - // sanity check + private fun decryptEvent(event: Event, timeline: String, requestKeysOnFail: Boolean): MXEventDecryptionResult? { + // sanity check // TODO Remove check if (null == event) { Timber.e("## decryptEvent() : null event") return null @@ -185,7 +165,7 @@ internal class MXMegolmDecryption : IMXDecrypting { requestBody.senderKey = encryptedEventContent.senderKey requestBody.sessionId = encryptedEventContent.sessionId - mCrypto.requestRoomKey(requestBody, recipients) + mOutgoingRoomKeyRequestManager.sendRoomKeyRequest(requestBody, recipients) } /** @@ -217,9 +197,9 @@ internal class MXMegolmDecryption : IMXDecrypting { /** * Handle a key event. * - * @param roomKeyEvent the key event. + * @param event the key event. */ - override fun onRoomKeyEvent(event: Event) { + override fun onRoomKeyEvent(event: Event, keysBackup: KeysBackup) { var exportFormat = false val roomKeyContent = event.content.toModel()!! @@ -274,7 +254,7 @@ internal class MXMegolmDecryption : IMXDecrypting { val added = mOlmDevice.addInboundGroupSession(roomKeyContent.sessionId!!, roomKeyContent.sessionKey!!, roomKeyContent.roomId!!, senderKey, forwarding_curve25519_key_chain!!, keysClaimed, exportFormat) if (added) { - mCrypto.getKeysBackupService().maybeBackupKeys() + keysBackup.maybeBackupKeys() val content = RoomKeyRequestBody() @@ -283,7 +263,7 @@ internal class MXMegolmDecryption : IMXDecrypting { content.sessionId = roomKeyContent.sessionId content.senderKey = senderKey - mCrypto.cancelRoomKeyRequest(content) + mOutgoingRoomKeyRequestManager.cancelRoomKeyRequest(content) onNewSession(senderKey, roomKeyContent.sessionId!!) } @@ -334,14 +314,13 @@ internal class MXMegolmDecryption : IMXDecrypting { } override fun hasKeysForKeyRequest(request: IncomingRoomKeyRequest): Boolean { - return (null != request - && null != request.mRequestBody + return (null != request.mRequestBody && mOlmDevice.hasInboundSessionKeys(request.mRequestBody!!.roomId!!, request.mRequestBody!!.senderKey!!, request.mRequestBody!!.sessionId!!)) } override fun shareKeysWithDevice(request: IncomingRoomKeyRequest) { // sanity checks - if (null == request || null == request.mRequestBody) { + if (request.mRequestBody == null) { return } @@ -358,9 +337,9 @@ internal class MXMegolmDecryption : IMXDecrypting { val devicesByUser = HashMap>() devicesByUser[userId] = ArrayList(Arrays.asList(deviceInfo)) - mCrypto.ensureOlmSessionsForDevices(devicesByUser, object : MatrixCallback> { - override fun onSuccess(map: MXUsersDevicesMap) { - val olmSessionResult = map.getObject(deviceId, userId) + mEnsureOlmSessionsForDevicesAction.handle(devicesByUser, object : MatrixCallback> { + override fun onSuccess(data: MXUsersDevicesMap) { + val olmSessionResult = data.getObject(deviceId, userId) if (null == olmSessionResult || null == olmSessionResult.mSessionId) { // no session with this device, probably because there @@ -380,7 +359,7 @@ internal class MXMegolmDecryption : IMXDecrypting { payloadJson["type"] = EventType.FORWARDED_ROOM_KEY payloadJson["content"] = inboundGroupSession!!.exportKeys()!! - val encodedPayload = mCrypto.encryptMessage(payloadJson, Arrays.asList(deviceInfo)) + val encodedPayload = mMessageEncrypter.encryptMessage(payloadJson, Arrays.asList(deviceInfo)) val sendToDeviceMap = MXUsersDevicesMap() sendToDeviceMap.setObject(encodedPayload, userId, deviceId) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmDecryptionFactory.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmDecryptionFactory.kt new file mode 100644 index 00000000..0b5ffdf9 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmDecryptionFactory.kt @@ -0,0 +1,51 @@ +/* + * Copyright 2019 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 im.vector.matrix.android.internal.crypto.algorithms.megolm + +import im.vector.matrix.android.api.auth.data.Credentials +import im.vector.matrix.android.internal.crypto.DeviceListManager +import im.vector.matrix.android.internal.crypto.MXOlmDevice +import im.vector.matrix.android.internal.crypto.MXOutgoingRoomKeyRequestManager +import im.vector.matrix.android.internal.crypto.actions.EnsureOlmSessionsForDevicesAction +import im.vector.matrix.android.internal.crypto.actions.MessageEncrypter +import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore +import im.vector.matrix.android.internal.crypto.tasks.SendToDeviceTask +import im.vector.matrix.android.internal.task.TaskExecutor + +internal class MXMegolmDecryptionFactory(private val mCredentials: Credentials, + private val mOlmDevice: MXOlmDevice, + private val mDeviceListManager: DeviceListManager, + private val mOutgoingRoomKeyRequestManager: MXOutgoingRoomKeyRequestManager, + private val mMessageEncrypter: MessageEncrypter, + private val mEnsureOlmSessionsForDevicesAction: EnsureOlmSessionsForDevicesAction, + private val mCryptoStore: IMXCryptoStore, + private val mSendToDeviceTask: SendToDeviceTask, + private val mTaskExecutor: TaskExecutor) { + + fun instantiate(): MXMegolmDecryption { + return MXMegolmDecryption( + mCredentials, + mOlmDevice, + mDeviceListManager, + mOutgoingRoomKeyRequestManager, + mMessageEncrypter, + mEnsureOlmSessionsForDevicesAction, + mCryptoStore, + mSendToDeviceTask, + mTaskExecutor) + } +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmEncryption.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmEncryption.kt index e598f6a3..230cc0c1 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmEncryption.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmEncryption.kt @@ -19,7 +19,6 @@ package im.vector.matrix.android.internal.crypto.algorithms.megolm import android.text.TextUtils -import androidx.annotation.Keep import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.auth.data.Credentials import im.vector.matrix.android.api.failure.Failure @@ -27,13 +26,20 @@ import im.vector.matrix.android.api.session.crypto.MXCryptoError import im.vector.matrix.android.api.session.events.model.Content import im.vector.matrix.android.api.session.events.model.EventType import im.vector.matrix.android.api.session.events.model.toContent -import im.vector.matrix.android.internal.crypto.* +import im.vector.matrix.android.internal.crypto.CryptoAsyncHelper +import im.vector.matrix.android.internal.crypto.DeviceListManager +import im.vector.matrix.android.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM +import im.vector.matrix.android.internal.crypto.MXOlmDevice +import im.vector.matrix.android.internal.crypto.actions.EnsureOlmSessionsForDevicesAction +import im.vector.matrix.android.internal.crypto.actions.MessageEncrypter import im.vector.matrix.android.internal.crypto.algorithms.IMXEncrypting import im.vector.matrix.android.internal.crypto.keysbackup.KeysBackup import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo import im.vector.matrix.android.internal.crypto.model.MXOlmSessionResult import im.vector.matrix.android.internal.crypto.model.MXQueuedEncryption import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap +import im.vector.matrix.android.internal.crypto.repository.WarnOnUnknownDeviceRepository +import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore import im.vector.matrix.android.internal.crypto.tasks.SendToDeviceTask import im.vector.matrix.android.internal.di.MoshiProvider import im.vector.matrix.android.internal.task.TaskExecutor @@ -42,22 +48,22 @@ import im.vector.matrix.android.internal.util.convertToUTF8 import timber.log.Timber import java.util.* -@Keep -internal class MXMegolmEncryption : IMXEncrypting { +internal class MXMegolmEncryption( + // The id of the room we will be sending to. + private var mRoomId: String, - private lateinit var mCrypto: CryptoManager - private lateinit var olmDevice: MXOlmDevice - private lateinit var mKeysBackup: KeysBackup - private lateinit var mDeviceListManager: DeviceListManager + private val olmDevice: MXOlmDevice, + private val mKeysBackup: KeysBackup, + private val mCryptoStore: IMXCryptoStore, + private val mDeviceListManager: DeviceListManager, + private val mEnsureOlmSessionsForDevicesAction: EnsureOlmSessionsForDevicesAction, + private val mCredentials: Credentials, + private val mSendToDeviceTask: SendToDeviceTask, + private val mTaskExecutor: TaskExecutor, + private val mMessageEncrypter: MessageEncrypter, + private val mWarnOnUnknownDevicesRepository: WarnOnUnknownDeviceRepository + ) : IMXEncrypting { - private lateinit var mCredentials: Credentials - private lateinit var mSendToDeviceTask: SendToDeviceTask - private lateinit var mTaskExecutor: TaskExecutor - - // The id of the room we will be sending to. - private lateinit var mRoomId: String - - private var mDeviceId: String? = null // OutboundSessionInfo. Null if we haven't yet started setting one up. Note // that even if this is non-null, it may not be ready for use (in which @@ -69,9 +75,11 @@ internal class MXMegolmEncryption : IMXEncrypting { private val mPendingEncryptions = ArrayList() + // Default rotation periods + // TODO: Make it configurable via parameters // Session rotation periods - private var mSessionRotationPeriodMsgs: Int = 0 - private var mSessionRotationPeriodMs: Int = 0 + private var mSessionRotationPeriodMsgs: Int = 100 + private var mSessionRotationPeriodMs: Int = 7 * 24 * 3600 * 1000 /** * @return a snapshot of the pending encryptions @@ -87,32 +95,6 @@ internal class MXMegolmEncryption : IMXEncrypting { return list } - override fun initWithMatrixSession(crypto: CryptoManager, - olmDevice: MXOlmDevice, - keysBackup: KeysBackup, - deviceListManager: DeviceListManager, - credentials: Credentials, - sendToDeviceTask: SendToDeviceTask, - taskExecutor: TaskExecutor, - roomId: String) { - mCrypto = crypto - this.olmDevice = olmDevice - mDeviceListManager = deviceListManager - mKeysBackup = keysBackup - mCredentials = credentials - mSendToDeviceTask = sendToDeviceTask - mTaskExecutor = taskExecutor - - mRoomId = roomId - mDeviceId = mCredentials.deviceId - - - // Default rotation periods - // TODO: Make it configurable via parameters - mSessionRotationPeriodMsgs = 100 - mSessionRotationPeriodMs = 7 * 24 * 3600 * 1000 - } - override fun encryptEventContent(eventContent: Content, eventType: String, userIds: List, @@ -334,7 +316,7 @@ internal class MXMegolmEncryption : IMXEncrypting { val t0 = System.currentTimeMillis() Timber.d("## shareUserDevicesKey() : starts") - mCrypto.ensureOlmSessionsForDevices(devicesByUser, object : MatrixCallback> { + mEnsureOlmSessionsForDevicesAction.handle(devicesByUser, object : MatrixCallback> { override fun onSuccess(data: MXUsersDevicesMap) { Timber.d("## shareUserDevicesKey() : ensureOlmSessionsForDevices succeeds after " + (System.currentTimeMillis() - t0) + " ms") @@ -367,7 +349,7 @@ internal class MXMegolmEncryption : IMXEncrypting { Timber.d("## shareUserDevicesKey() : Sharing keys with device $userId:$deviceID") //noinspection ArraysAsListWithZeroOrOneArgument,ArraysAsListWithZeroOrOneArgument - contentMap.setObject(mCrypto.encryptMessage(payload, Arrays.asList(sessionResult.mDevice)), userId, deviceID) + contentMap.setObject(mMessageEncrypter.encryptMessage(payload, Arrays.asList(sessionResult.mDevice)), userId, deviceID) haveTargets = true } } @@ -453,7 +435,7 @@ internal class MXMegolmEncryption : IMXEncrypting { // Include our device ID so that recipients can send us a // m.new_device message if they don't have our session key. - map["device_id"] = mDeviceId!! + map["device_id"] = mCredentials.deviceId!! CryptoAsyncHelper.getUiHandler().post { queuedEncryption.mApiCallback?.onSuccess(map.toContent()!!) } @@ -480,7 +462,8 @@ internal class MXMegolmEncryption : IMXEncrypting { // an m.new_device. mDeviceListManager.downloadKeys(userIds, false, object : MatrixCallback> { override fun onSuccess(data: MXUsersDevicesMap) { - val encryptToVerifiedDevicesOnly = mCrypto.getGlobalBlacklistUnverifiedDevices() || mCrypto.isRoomBlacklistUnverifiedDevices(mRoomId) + val encryptToVerifiedDevicesOnly = mCryptoStore.getGlobalBlacklistUnverifiedDevices() + || mCryptoStore.getRoomsListBlacklistUnverifiedDevices().contains(mRoomId) val devicesInRoom = MXUsersDevicesMap() val unknownDevices = MXUsersDevicesMap() @@ -491,7 +474,7 @@ internal class MXMegolmEncryption : IMXEncrypting { for (deviceId in deviceIds!!) { val deviceInfo = data.getObject(deviceId, userId) - if (mCrypto.warnOnUnknownDevices() && deviceInfo!!.isUnknown) { + if (mWarnOnUnknownDevicesRepository.warnOnUnknownDevices() && deviceInfo!!.isUnknown) { // The device is not yet known by the user unknownDevices.setObject(deviceInfo, userId, deviceId) continue diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmEncryptionFactory.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmEncryptionFactory.kt new file mode 100644 index 00000000..0d6af980 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmEncryptionFactory.kt @@ -0,0 +1,58 @@ +/* + * Copyright 2019 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 im.vector.matrix.android.internal.crypto.algorithms.megolm + +import im.vector.matrix.android.api.auth.data.Credentials +import im.vector.matrix.android.internal.crypto.DeviceListManager +import im.vector.matrix.android.internal.crypto.MXOlmDevice +import im.vector.matrix.android.internal.crypto.actions.EnsureOlmSessionsForDevicesAction +import im.vector.matrix.android.internal.crypto.actions.MessageEncrypter +import im.vector.matrix.android.internal.crypto.keysbackup.KeysBackup +import im.vector.matrix.android.internal.crypto.repository.WarnOnUnknownDeviceRepository +import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore +import im.vector.matrix.android.internal.crypto.tasks.SendToDeviceTask +import im.vector.matrix.android.internal.task.TaskExecutor + +internal class MXMegolmEncryptionFactory( + private val olmDevice: MXOlmDevice, + private val mKeysBackup: KeysBackup, + private val mCryptoStore: IMXCryptoStore, + private val mDeviceListManager: DeviceListManager, + private val mEnsureOlmSessionsForDevicesAction: EnsureOlmSessionsForDevicesAction, + + private val mCredentials: Credentials, + private val mSendToDeviceTask: SendToDeviceTask, + private val mTaskExecutor: TaskExecutor, + private val mMessageEncrypter: MessageEncrypter, + private val mWarnOnUnknownDevicesRepository: WarnOnUnknownDeviceRepository) { + + fun instantiate(roomId: String): MXMegolmEncryption { + return MXMegolmEncryption( + roomId, + + olmDevice, + mKeysBackup, + mCryptoStore, + mDeviceListManager, + mEnsureOlmSessionsForDevicesAction, + mCredentials, + mSendToDeviceTask, + mTaskExecutor, + mMessageEncrypter, + mWarnOnUnknownDevicesRepository) + } +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmDecryption.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmDecryption.kt index 8aa9f17e..c0faeef1 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmDecryption.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmDecryption.kt @@ -18,60 +18,30 @@ package im.vector.matrix.android.internal.crypto.algorithms.olm import android.text.TextUtils -import androidx.annotation.Keep import im.vector.matrix.android.api.auth.data.Credentials import im.vector.matrix.android.api.session.crypto.MXCryptoError import im.vector.matrix.android.api.session.events.model.Event import im.vector.matrix.android.api.session.events.model.toModel -import im.vector.matrix.android.internal.crypto.* +import im.vector.matrix.android.internal.crypto.MXDecryptionException +import im.vector.matrix.android.internal.crypto.MXEventDecryptionResult +import im.vector.matrix.android.internal.crypto.MXOlmDevice import im.vector.matrix.android.internal.crypto.algorithms.IMXDecrypting import im.vector.matrix.android.internal.crypto.model.event.OlmEventContent import im.vector.matrix.android.internal.crypto.model.event.OlmPayloadContent -import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore -import im.vector.matrix.android.internal.crypto.tasks.SendToDeviceTask -import im.vector.matrix.android.internal.task.TaskExecutor import im.vector.matrix.android.internal.util.convertFromUTF8 import timber.log.Timber import java.util.* -/** - * An interface for encrypting data - */ -@Keep -internal class MXOlmDecryption : IMXDecrypting { +internal class MXOlmDecryption( + // The olm device interface + private val mOlmDevice: MXOlmDevice, - // The olm device interface - private lateinit var mOlmDevice: MXOlmDevice - - // the matrix credentials - private lateinit var mCredentials: Credentials - - private lateinit var mCrypto: CryptoManager - private lateinit var mCryptoStore: IMXCryptoStore - private lateinit var mSendToDeviceTask: SendToDeviceTask - private lateinit var mTaskExecutor: TaskExecutor - - override fun initWithMatrixSession(credentials: Credentials, - crypto: CryptoManager, - olmDevice: MXOlmDevice, - deviceListManager: DeviceListManager, - sendToDeviceTask: SendToDeviceTask, - taskExecutor: TaskExecutor) { - mCredentials = credentials - mCrypto = crypto - mOlmDevice = olmDevice - mSendToDeviceTask = sendToDeviceTask - mTaskExecutor = taskExecutor - } + // the matrix credentials + private val mCredentials: Credentials) + : IMXDecrypting { @Throws(MXDecryptionException::class) override fun decryptEvent(event: Event, timeline: String): MXEventDecryptionResult? { - // sanity check - if (null == event) { - Timber.e("## decryptEvent() : null event") - return null - } - val olmEventContent = event.content.toModel()!! if (null == olmEventContent.ciphertext) { @@ -89,7 +59,7 @@ internal class MXOlmDecryption : IMXDecrypting { // The message for myUser val message = olmEventContent.ciphertext!![mOlmDevice.deviceCurve25519Key] as Map - val payloadString = decryptMessage(message, olmEventContent.senderKey) + val payloadString = decryptMessage(message, olmEventContent.senderKey!!) if (null == payloadString) { Timber.e("## decryptEvent() Failed to decrypt Olm event (id= " + event.eventId + " ) from " + olmEventContent.senderKey) @@ -164,27 +134,13 @@ internal class MXOlmDecryption : IMXDecrypting { } val result = MXEventDecryptionResult() - // TODO result.mClearEvent = payload + // FIXME result.mClearEvent = payload result.mSenderCurve25519Key = olmEventContent.senderKey result.mClaimedEd25519Key = olmPayloadContent.keys!!.get("ed25519") return result } - override fun onRoomKeyEvent(event: Event) { - // No impact for olm - } - - override fun onNewSession(senderKey: String, sessionId: String) { - // No impact for olm - } - - override fun hasKeysForKeyRequest(request: IncomingRoomKeyRequest): Boolean { - return false - } - - override fun shareKeysWithDevice(request: IncomingRoomKeyRequest) {} - /** * Attempt to decrypt an Olm message. * @@ -192,8 +148,8 @@ internal class MXOlmDecryption : IMXDecrypting { * @param message message object, with 'type' and 'body' fields. * @return payload, if decrypted successfully. */ - private fun decryptMessage(message: Map, theirDeviceIdentityKey: String?): String? { - val sessionIdsSet = mOlmDevice.getSessionIds(theirDeviceIdentityKey!!) + private fun decryptMessage(message: Map, theirDeviceIdentityKey: String): String? { + val sessionIdsSet = mOlmDevice.getSessionIds(theirDeviceIdentityKey) val sessionIds: List diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmDecryptionFactory.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmDecryptionFactory.kt new file mode 100644 index 00000000..250a2b93 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmDecryptionFactory.kt @@ -0,0 +1,30 @@ +/* + * Copyright 2019 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 im.vector.matrix.android.internal.crypto.algorithms.olm + +import im.vector.matrix.android.api.auth.data.Credentials +import im.vector.matrix.android.internal.crypto.MXOlmDevice + +internal class MXOlmDecryptionFactory(private val mOlmDevice: MXOlmDevice, + private val mCredentials: Credentials) { + + fun instantiate(): MXOlmDecryption { + return MXOlmDecryption( + mOlmDevice, + mCredentials) + } +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmEncryption.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmEncryption.kt index aa4e42a3..280ff486 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmEncryption.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmEncryption.kt @@ -19,49 +19,30 @@ package im.vector.matrix.android.internal.crypto.algorithms.olm import android.text.TextUtils -import androidx.annotation.Keep import im.vector.matrix.android.api.MatrixCallback -import im.vector.matrix.android.api.auth.data.Credentials import im.vector.matrix.android.api.session.events.model.Content import im.vector.matrix.android.api.session.events.model.toContent -import im.vector.matrix.android.internal.crypto.CryptoManager import im.vector.matrix.android.internal.crypto.DeviceListManager import im.vector.matrix.android.internal.crypto.MXOlmDevice +import im.vector.matrix.android.internal.crypto.actions.EnsureOlmSessionsForUsersAction +import im.vector.matrix.android.internal.crypto.actions.MessageEncrypter import im.vector.matrix.android.internal.crypto.algorithms.IMXEncrypting -import im.vector.matrix.android.internal.crypto.keysbackup.KeysBackup import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo import im.vector.matrix.android.internal.crypto.model.MXOlmSessionResult import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap -import im.vector.matrix.android.internal.crypto.tasks.SendToDeviceTask -import im.vector.matrix.android.internal.task.TaskExecutor +import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore import java.util.* -@Keep -internal class MXOlmEncryption : IMXEncrypting { - private lateinit var mCrypto: CryptoManager - private lateinit var mOlmDevice: MXOlmDevice - private lateinit var mDeviceListManager: DeviceListManager +internal class MXOlmEncryption( + private var mRoomId: String, - private lateinit var mCredentials: Credentials - private lateinit var mSendToDeviceTask: SendToDeviceTask - private lateinit var mTaskExecutor: TaskExecutor + private val mOlmDevice: MXOlmDevice, + private val mCryptoStore: IMXCryptoStore, + private val mMessageEncrypter: MessageEncrypter, + private val mDeviceListManager: DeviceListManager, + private val mEnsureOlmSessionsForUsersAction: EnsureOlmSessionsForUsersAction) + : IMXEncrypting { - private lateinit var mRoomId: String - - override fun initWithMatrixSession(crypto: CryptoManager, - olmDevice: MXOlmDevice, - keysBackup: KeysBackup, - deviceListManager: DeviceListManager, - credentials: Credentials, - sendToDeviceTask: SendToDeviceTask, - taskExecutor: TaskExecutor, - roomId: String) { - mCrypto = crypto - mOlmDevice = olmDevice - mDeviceListManager = deviceListManager - - mRoomId = roomId - } override fun encryptEventContent(eventContent: Content, eventType: String, @@ -75,24 +56,22 @@ internal class MXOlmEncryption : IMXEncrypting { val deviceInfos = ArrayList() for (userId in userIds) { - val devices = mCrypto.getUserDevices(userId) + val devices = mCryptoStore.getUserDevices(userId)?.values ?: emptyList() - if (null != devices) { - for (device in devices) { - val key = device.identityKey() + for (device in devices) { + val key = device.identityKey() - if (TextUtils.equals(key, mOlmDevice.deviceCurve25519Key)) { - // Don't bother setting up session to ourself - continue - } - - if (device.isBlocked) { - // Don't bother setting up sessions with blocked users - continue - } - - deviceInfos.add(device) + if (TextUtils.equals(key, mOlmDevice.deviceCurve25519Key)) { + // Don't bother setting up session to ourself + continue } + + if (device.isBlocked) { + // Don't bother setting up sessions with blocked users + continue + } + + deviceInfos.add(device) } } @@ -101,7 +80,7 @@ internal class MXOlmEncryption : IMXEncrypting { messageMap["type"] = eventType messageMap["content"] = eventContent - mCrypto.encryptMessage(messageMap, deviceInfos) + mMessageEncrypter.encryptMessage(messageMap, deviceInfos) callback.onSuccess(messageMap.toContent()!!) } @@ -118,7 +97,7 @@ internal class MXOlmEncryption : IMXEncrypting { mDeviceListManager.downloadKeys(users, false, object : MatrixCallback> { override fun onSuccess(data: MXUsersDevicesMap) { - mCrypto.ensureOlmSessionsForUsers(users, object : MatrixCallback> { + mEnsureOlmSessionsForUsersAction.handle(users, object : MatrixCallback> { override fun onSuccess(data: MXUsersDevicesMap) { callback?.onSuccess(Unit) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmEncryptionFactory.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmEncryptionFactory.kt new file mode 100644 index 00000000..cc79a9f1 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmEncryptionFactory.kt @@ -0,0 +1,41 @@ +/* + * Copyright 2019 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 im.vector.matrix.android.internal.crypto.algorithms.olm + +import im.vector.matrix.android.internal.crypto.DeviceListManager +import im.vector.matrix.android.internal.crypto.MXOlmDevice +import im.vector.matrix.android.internal.crypto.actions.EnsureOlmSessionsForUsersAction +import im.vector.matrix.android.internal.crypto.actions.MessageEncrypter +import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore + +internal class MXOlmEncryptionFactory(private val mOlmDevice: MXOlmDevice, + private val mCryptoStore: IMXCryptoStore, + private val mMessageEncrypter: MessageEncrypter, + private val mDeviceListManager: DeviceListManager, + private val mEnsureOlmSessionsForUsersAction: EnsureOlmSessionsForUsersAction) { + + fun instantiate(roomId: String): MXOlmEncryption { + return MXOlmEncryption( + roomId, + + mOlmDevice, + mCryptoStore, + mMessageEncrypter, + mDeviceListManager, + mEnsureOlmSessionsForUsersAction) + } +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackup.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackup.kt index b4629df1..5d1a550b 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackup.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackup.kt @@ -31,6 +31,7 @@ import im.vector.matrix.android.api.listeners.StepProgressListener import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupService import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupState import im.vector.matrix.android.internal.crypto.* +import im.vector.matrix.android.internal.crypto.actions.MegolmSessionDataImporter import im.vector.matrix.android.internal.crypto.keysbackup.model.KeysBackupVersionTrust import im.vector.matrix.android.internal.crypto.keysbackup.model.KeysBackupVersionTrustSignature import im.vector.matrix.android.internal.crypto.keysbackup.model.MegolmBackupAuthData @@ -67,6 +68,8 @@ internal class KeysBackup( private val mCryptoStore: IMXCryptoStore, private val mOlmDevice: MXOlmDevice, private val mObjectSigner: ObjectSigner, + // Actions + private val mMegolmSessionDataImporter: MegolmSessionDataImporter, // Tasks private val mCreateKeysBackupVersionTask: CreateKeysBackupVersionTask, private val mDeleteBackupTask: DeleteBackupTask, @@ -115,9 +118,6 @@ internal class KeysBackup( override val currentBackupVersion: String? get() = mKeysBackupVersion?.version - // Internal listener - private lateinit var mKeysBackupCryptoListener: KeysBackupCryptoListener - override fun addListener(listener: KeysBackupService.KeysBackupStateListener) { mKeysBackupStateManager.addListener(listener) } @@ -749,7 +749,20 @@ internal class KeysBackup( null } - mKeysBackupCryptoListener.importMegolmSessionsData(sessionsData, backUp, progressListener, callback) + mMegolmSessionDataImporter.handle(sessionsData, !backUp, progressListener, object : MatrixCallback { + override fun onSuccess(data: ImportRoomKeysResult) { + // Do not back up the key if it comes from a backup recovery + if (backUp) { + maybeBackupKeys() + } + + callback.onSuccess(data) + } + + override fun onFailure(failure: Throwable) { + callback.onFailure(failure) + } + }) } override fun onFailure(failure: Throwable) { @@ -887,7 +900,7 @@ internal class KeysBackup( /** * Do a backup if there are new keys, with a delay */ - override fun maybeBackupKeys() { + fun maybeBackupKeys() { when { isStucked -> { // If not already done, or in error case, check for a valid backup version on the homeserver. @@ -1388,7 +1401,7 @@ internal class KeysBackup( @WorkerThread fun encryptGroupSession(session: MXOlmInboundGroupSession2): KeyBackupData { // Gather information for each key - val device = mKeysBackupCryptoListener.deviceWithIdentityKey(session.mSenderKey!!, MXCRYPTO_ALGORITHM_MEGOLM) + val device = mCryptoStore.deviceWithIdentityKey(session.mSenderKey!!) // Build the m.megolm_backup.v1.curve25519-aes-sha2 data as defined at // https://github.com/uhoreg/matrix-doc/blob/e2e_backup/proposals/1219-storing-megolm-keys-serverside.md#mmegolm_backupv1curve25519-aes-sha2-key-format @@ -1479,21 +1492,6 @@ internal class KeysBackup( private const val KEY_BACKUP_SEND_KEYS_MAX_COUNT = 100 } - - fun setCryptoInternalListener(listener: KeysBackupCryptoListener) { - mKeysBackupCryptoListener = listener - } - - interface KeysBackupCryptoListener { - fun importMegolmSessionsData(megolmSessionsData: List, - backUpKeys: Boolean, - progressListener: ProgressListener?, - callback: MatrixCallback) - - fun deviceWithIdentityKey(senderKey: String, algorithm: String): MXDeviceInfo? - } - - /* ========================================================================================== * DEBUG INFO * ========================================================================================== */ diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackupStateManager.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackupStateManager.kt index fe765712..70e6c34b 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackupStateManager.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackupStateManager.kt @@ -22,7 +22,7 @@ import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupState import timber.log.Timber import java.util.* -internal class KeysBackupStateManager(val uiHandler: Handler) { +internal class KeysBackupStateManager(private val uiHandler: Handler) { private val mListeners = ArrayList() diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/repository/WarnOnUnknownDeviceRepository.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/repository/WarnOnUnknownDeviceRepository.kt new file mode 100644 index 00000000..128c7bed --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/repository/WarnOnUnknownDeviceRepository.kt @@ -0,0 +1,40 @@ +/* + * Copyright 2019 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 im.vector.matrix.android.internal.crypto.repository + +internal class WarnOnUnknownDeviceRepository { + + // Warn the user if some new devices are detected while encrypting a message. + private var mWarnOnUnknownDevices = true + + /** + * Tells if the encryption must fail if some unknown devices are detected. + * + * @return true to warn when some unknown devices are detected. + */ + fun warnOnUnknownDevices() = mWarnOnUnknownDevices + + /** + * Update the warn status when some unknown devices are detected. + * + * @param warn true to warn when some unknown devices are detected. + */ + fun setWarnOnUnknownDevices(warn: Boolean) { + mWarnOnUnknownDevices = warn + } + +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/RealmCryptoStore.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/RealmCryptoStore.kt index 826fcaf4..d21648a8 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/RealmCryptoStore.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/RealmCryptoStore.kt @@ -201,7 +201,7 @@ internal class RealmCryptoStore(private val enableFileEncryption: Boolean = fals .let { u -> // Add the devices // Ensure all other devices are deleted - u.devices.deleteAllFromRealm() // Device is null!! + u.devices.deleteAllFromRealm() u.devices.addAll( devices.map { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/DefaultSasVerificationService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/DefaultSasVerificationService.kt index 0355c112..c9aa63b4 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/DefaultSasVerificationService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/DefaultSasVerificationService.kt @@ -29,6 +29,7 @@ import im.vector.matrix.android.api.session.events.model.EventType import im.vector.matrix.android.api.session.events.model.toModel import im.vector.matrix.android.internal.crypto.CryptoAsyncHelper import im.vector.matrix.android.internal.crypto.DeviceListManager +import im.vector.matrix.android.internal.crypto.MyDeviceInfoHolder import im.vector.matrix.android.internal.crypto.actions.SetDeviceVerificationAction import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap @@ -48,6 +49,7 @@ import kotlin.collections.HashMap */ internal class DefaultSasVerificationService(private val mCredentials: Credentials, private val mCryptoStore: IMXCryptoStore, + private val mMyDeviceInfoHolder: MyDeviceInfoHolder, private val deviceListManager: DeviceListManager, private val setDeviceVerificationAction: SetDeviceVerificationAction, private val mSendToDeviceTask: SendToDeviceTask, @@ -86,10 +88,6 @@ internal class DefaultSasVerificationService(private val mCredentials: Credentia } } - // Internal listener - private lateinit var mCryptoListener: SasCryptoListener - - private var listeners = ArrayList() override fun addListener(listener: SasVerificationService.SasVerificationListener) { @@ -188,11 +186,12 @@ internal class DefaultSasVerificationService(private val mCredentials: Credentia Timber.d("## SAS onStartRequestReceived - request accepted ${startReq.transactionID!!}") val tx = IncomingSASVerificationTransaction( this, + setDeviceVerificationAction, mCredentials, mCryptoStore, mSendToDeviceTask, mTaskExecutor, - mCryptoListener.getMyDevice().fingerprint()!!, + mMyDeviceInfoHolder.myDevice.fingerprint()!!, startReq.transactionID!!, otherUserId) addTransaction(tx) @@ -362,11 +361,12 @@ internal class DefaultSasVerificationService(private val mCredentials: Credentia if (KeyVerificationStart.VERIF_METHOD_SAS == method) { val tx = OutgoingSASVerificationRequest( this, + setDeviceVerificationAction, mCredentials, mCryptoStore, mSendToDeviceTask, mTaskExecutor, - mCryptoListener.getMyDevice().fingerprint()!!, + mMyDeviceInfoHolder.myDevice.fingerprint()!!, txID, userId, deviceID) @@ -424,12 +424,4 @@ internal class DefaultSasVerificationService(private val mCredentials: Credentia }) .executeBy(mTaskExecutor) } - - fun setCryptoInternalListener(listener: SasCryptoListener) { - mCryptoListener = listener - } - - interface SasCryptoListener { - fun getMyDevice(): MXDeviceInfo - } } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/IncomingSASVerificationTransaction.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/IncomingSASVerificationTransaction.kt index 33b792e3..813e17db 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/IncomingSASVerificationTransaction.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/IncomingSASVerificationTransaction.kt @@ -23,6 +23,7 @@ import im.vector.matrix.android.api.session.crypto.sas.SasMode import im.vector.matrix.android.api.session.crypto.sas.SasVerificationTxState import im.vector.matrix.android.api.session.events.model.EventType import im.vector.matrix.android.internal.crypto.CryptoAsyncHelper +import im.vector.matrix.android.internal.crypto.actions.SetDeviceVerificationAction import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore import im.vector.matrix.android.internal.crypto.model.rest.KeyVerificationAccept import im.vector.matrix.android.internal.crypto.model.rest.KeyVerificationKey @@ -35,6 +36,7 @@ import timber.log.Timber internal class IncomingSASVerificationTransaction( private val mSasVerificationService: DefaultSasVerificationService, + private val mSetDeviceVerificationAction: SetDeviceVerificationAction, private val mCredentials: Credentials, private val mCryptoStore: IMXCryptoStore, private val mSendToDeviceTask: SendToDeviceTask, @@ -44,6 +46,7 @@ internal class IncomingSASVerificationTransaction( otherUserID: String) : SASVerificationTransaction( mSasVerificationService, + mSetDeviceVerificationAction, mCredentials, mCryptoStore, mSendToDeviceTask, diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/OutgoingSASVerificationRequest.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/OutgoingSASVerificationRequest.kt index e2cc5d3a..d2c5de15 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/OutgoingSASVerificationRequest.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/OutgoingSASVerificationRequest.kt @@ -20,6 +20,7 @@ import im.vector.matrix.android.api.session.crypto.sas.CancelCode import im.vector.matrix.android.api.session.crypto.sas.OutgoingSasVerificationRequest import im.vector.matrix.android.api.session.crypto.sas.SasVerificationTxState import im.vector.matrix.android.api.session.events.model.EventType +import im.vector.matrix.android.internal.crypto.actions.SetDeviceVerificationAction import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap import im.vector.matrix.android.internal.crypto.model.rest.KeyVerificationAccept @@ -33,6 +34,7 @@ import timber.log.Timber internal class OutgoingSASVerificationRequest( private val mSasVerificationService: DefaultSasVerificationService, + private val mSetDeviceVerificationAction: SetDeviceVerificationAction, private val mCredentials: Credentials, private val mCryptoStore: IMXCryptoStore, private val mSendToDeviceTask: SendToDeviceTask, @@ -43,6 +45,7 @@ internal class OutgoingSASVerificationRequest( otherDeviceId: String) : SASVerificationTransaction( mSasVerificationService, + mSetDeviceVerificationAction, mCredentials, mCryptoStore, mSendToDeviceTask, diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/SASVerificationTransaction.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/SASVerificationTransaction.kt index 9f92d4e0..a1629ff3 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/SASVerificationTransaction.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/SASVerificationTransaction.kt @@ -24,6 +24,7 @@ import im.vector.matrix.android.api.session.crypto.sas.SasMode import im.vector.matrix.android.api.session.crypto.sas.SasVerificationTxState import im.vector.matrix.android.api.session.events.model.EventType import im.vector.matrix.android.internal.crypto.CryptoAsyncHelper +import im.vector.matrix.android.internal.crypto.actions.SetDeviceVerificationAction import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo import im.vector.matrix.android.internal.crypto.model.MXKey import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap @@ -42,6 +43,7 @@ import kotlin.properties.Delegates */ internal abstract class SASVerificationTransaction( private val mSasVerificationService: DefaultSasVerificationService, + private val mSetDeviceVerificationAction: SetDeviceVerificationAction, private val mCredentials: Credentials, private val mCryptoStore: IMXCryptoStore, private val mSendToDeviceTask: SendToDeviceTask, @@ -245,7 +247,7 @@ internal abstract class SASVerificationTransaction( } private fun setDeviceVerified(deviceId: String, userId: String) { - mSasVerificationService.setDeviceVerification(MXDeviceInfo.DEVICE_VERIFICATION_VERIFIED, + mSetDeviceVerificationAction.handle(MXDeviceInfo.DEVICE_VERIFICATION_VERIFIED, deviceId, userId) } diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/factory/TimelineItemFactory.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/factory/TimelineItemFactory.kt index 21759e20..937dee53 100644 --- a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/factory/TimelineItemFactory.kt +++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/factory/TimelineItemFactory.kt @@ -21,6 +21,7 @@ import im.vector.matrix.android.api.session.room.timeline.TimelineEvent import im.vector.riotredesign.core.epoxy.EmptyItem_ import im.vector.riotredesign.core.epoxy.VectorEpoxyModel import im.vector.riotredesign.features.home.room.detail.timeline.TimelineEventController +import timber.log.Timber class TimelineItemFactory(private val messageItemFactory: MessageItemFactory, private val roomNameItemFactory: RoomNameItemFactory, @@ -59,6 +60,8 @@ class TimelineItemFactory(private val messageItemFactory: MessageItemFactory, else -> null } } catch (e: Exception) { + Timber.e(e, "Error") + defaultItemFactory.create(event, e) } return (computedModel ?: EmptyItem_())