From 5996fb79a01db4234ac7dc12d0f95c22c8ebcc51 Mon Sep 17 00:00:00 2001 From: Andy Uhnak Date: Tue, 31 Jan 2023 15:46:22 +0000 Subject: [PATCH] New CryptoMachine on each background operation --- .../Crypto/MXBackgroundCryptoV2.swift | 53 +++++++++++-------- .../Background/MXBackgroundSyncService.swift | 16 ++---- 2 files changed, 37 insertions(+), 32 deletions(-) diff --git a/MatrixSDK/Background/Crypto/MXBackgroundCryptoV2.swift b/MatrixSDK/Background/Crypto/MXBackgroundCryptoV2.swift index 570a670531..d9bb9ed756 100644 --- a/MatrixSDK/Background/Crypto/MXBackgroundCryptoV2.swift +++ b/MatrixSDK/Background/Crypto/MXBackgroundCryptoV2.swift @@ -27,29 +27,13 @@ class MXBackgroundCryptoV2: MXBackgroundCrypto { case missingCredentials } - private let machine: MXCryptoMachine + private let credentials: MXCredentials + private let restClient: MXRestClient private let log = MXNamedLog(name: "MXBackgroundCryptoV2") - init(credentials: MXCredentials, restClient: MXRestClient) throws { - guard - let userId = credentials.userId, - let deviceId = credentials.deviceId - else { - throw Error.missingCredentials - } - - // `MXCryptoMachine` will load the same store as the main application meaning that background and foreground - // sync services have access to the same data / keys. Possible race conditions are handled internally. - machine = try MXCryptoMachine( - userId: userId, - deviceId: deviceId, - restClient: restClient, - getRoomAction: { [log] _ in - log.error("The background crypto should not be accessing rooms") - return nil - } - ) - + init(credentials: MXCredentials, restClient: MXRestClient) { + self.credentials = credentials + self.restClient = restClient log.debug("Initialized background crypto module") } @@ -66,6 +50,7 @@ class MXBackgroundCryptoV2: MXBackgroundCrypto { log.debug(details) do { + let machine = try createMachine() _ = try await machine.handleSyncResponse( toDevice: syncResponse.toDevice, deviceLists: syncResponse.deviceLists, @@ -100,6 +85,7 @@ class MXBackgroundCryptoV2: MXBackgroundCrypto { do { // Rust-sdk does not expose api to see if we have a given session key yet (will be added in the future) // so for the time being to find out if we can decrypt we simply perform the (more expensive) decryption + let machine = try createMachine() _ = try machine.decryptRoomEvent(event) log.debug("Event `\(eventId)` can be decrypted with session `\(sessionId)`") return true @@ -117,6 +103,7 @@ class MXBackgroundCryptoV2: MXBackgroundCrypto { log.debug("Decrypting event `\(eventId)`") do { + let machine = try createMachine() let decrypted = try machine.decryptRoomEvent(event) let result = try MXEventDecryptionResult(event: decrypted) event.setClearData(result) @@ -127,6 +114,30 @@ class MXBackgroundCryptoV2: MXBackgroundCrypto { throw error } } + + // `MXCryptoMachine` will load the same store as the main application meaning that background and foreground + // sync services have access to the same data / keys. The machine is not fully multi-thread and multi-process + // safe, and until this is resolved we open a new instance of `MXCryptoMachine` on each background operation + // to ensure we are always up-to-date with whatever has been written by the foreground process in the meanwhile. + // See https://github.com/matrix-org/matrix-rust-sdk/issues/1415 for more details. + private func createMachine() throws -> MXCryptoMachine { + guard + let userId = credentials.userId, + let deviceId = credentials.deviceId + else { + throw Error.missingCredentials + } + + return try MXCryptoMachine( + userId: userId, + deviceId: deviceId, + restClient: restClient, + getRoomAction: { [log] _ in + log.error("The background crypto should not be accessing rooms") + return nil + } + ) + } } #endif diff --git a/MatrixSDK/Background/MXBackgroundSyncService.swift b/MatrixSDK/Background/MXBackgroundSyncService.swift index 7cf9463d8e..dc5f6b58dc 100644 --- a/MatrixSDK/Background/MXBackgroundSyncService.swift +++ b/MatrixSDK/Background/MXBackgroundSyncService.swift @@ -40,7 +40,7 @@ public enum MXBackgroundSyncServiceError: Error { private let processingQueue: DispatchQueue public let credentials: MXCredentials private let syncResponseStoreManager: MXSyncResponseStoreManager - private let crypto: MXBackgroundCrypto? + private let crypto: MXBackgroundCrypto private var store: MXStore private let restClient: MXRestClient private var pushRulesManager: MXBackgroundPushRulesManager @@ -91,12 +91,7 @@ public enum MXBackgroundSyncServiceError: Error { crypto = { #if DEBUG if MXSDKOptions.sharedInstance().isCryptoSDKAvailable && MXSDKOptions.sharedInstance().enableCryptoSDK { - do { - return try MXBackgroundCryptoV2(credentials: credentials, restClient: restClient) - } catch { - MXLog.failure("Cannot create background crypto", context: error) - return nil - } + return MXBackgroundCryptoV2(credentials: credentials, restClient: restClient) } #endif return MXLegacyBackgroundCrypto(credentials: credentials, resetBackgroundCryptoStore: resetBackgroundCryptoStore) @@ -299,7 +294,7 @@ public enum MXBackgroundSyncServiceError: Error { } // should decrypt it first - if let crypto = crypto, crypto.canDecryptEvent(event) { + if crypto.canDecryptEvent(event) { // we have keys to decrypt the event MXLog.debug("[MXBackgroundSyncService] fetchEvent: Event needs to be decrpyted, and we have the keys to decrypt it.") @@ -406,8 +401,7 @@ public enum MXBackgroundSyncServiceError: Error { await self.handleSyncResponse(syncResponse, syncToken: eventStreamToken) if let event = self.syncResponseStoreManager.event(withEventId: eventId, inRoom: roomId), - let crypto = self.crypto, - !crypto.canDecryptEvent(event), + !self.crypto.canDecryptEvent(event), (syncResponse.toDevice?.events ?? []).count > 0 { // we got the event but not the keys to decrypt it. continue to sync self.launchBackgroundSync(forEventId: eventId, roomId: roomId, completion: completion) @@ -446,7 +440,7 @@ public enum MXBackgroundSyncServiceError: Error { } syncResponseStoreManager.updateStore(with: syncResponse, syncToken: syncToken) - await crypto?.handleSyncResponse(syncResponse) + await crypto.handleSyncResponse(syncResponse) if MXSDKOptions.sharedInstance().autoAcceptRoomInvites, let invitedRooms = syncResponse.rooms?.invite {