From 56f12fe63bae2602fbec66dda27eec0f51cc6b29 Mon Sep 17 00:00:00 2001 From: Andy Uhnak Date: Tue, 14 Feb 2023 17:03:19 +0000 Subject: [PATCH] Control CryptoSDK via feature flag --- MatrixSDK.xcodeproj/project.pbxproj | 6 +++ .../Background/MXBackgroundSyncService.swift | 22 +++++---- MatrixSDK/Crypto/MXCrypto.m | 36 ++++++--------- MatrixSDK/Crypto/MXCryptoV2Factory.swift | 1 + MatrixSDK/Crypto/MXCryptoV2Feature.swift | 45 +++++++++++++++++++ MatrixSDK/MXSDKOptions.h | 21 ++++----- MatrixSDK/MXSDKOptions.m | 23 +++++++--- changelog.d/pr-1719.change | 1 + 8 files changed, 106 insertions(+), 49 deletions(-) create mode 100644 MatrixSDK/Crypto/MXCryptoV2Feature.swift create mode 100644 changelog.d/pr-1719.change diff --git a/MatrixSDK.xcodeproj/project.pbxproj b/MatrixSDK.xcodeproj/project.pbxproj index b8880a7f4d..605bd26367 100644 --- a/MatrixSDK.xcodeproj/project.pbxproj +++ b/MatrixSDK.xcodeproj/project.pbxproj @@ -2025,6 +2025,8 @@ EDC8C4092968A993003792C5 /* MXKeysQueryScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDC8C4072968A993003792C5 /* MXKeysQueryScheduler.swift */; }; EDC8C40D2968C37E003792C5 /* MXKeysQuerySchedulerUnitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDC8C40A2968A9F7003792C5 /* MXKeysQuerySchedulerUnitTests.swift */; }; EDC8C40E2968C37F003792C5 /* MXKeysQuerySchedulerUnitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDC8C40A2968A9F7003792C5 /* MXKeysQuerySchedulerUnitTests.swift */; }; + EDCAD251299BF7F40088B4DA /* MXCryptoV2Feature.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDCAD250299BF7F40088B4DA /* MXCryptoV2Feature.swift */; }; + EDCAD252299BF7F40088B4DA /* MXCryptoV2Feature.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDCAD250299BF7F40088B4DA /* MXCryptoV2Feature.swift */; }; EDCB65E22912AB0C00F55D4D /* MXRoomEventDecryption.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDCB65E12912AB0C00F55D4D /* MXRoomEventDecryption.swift */; }; EDCB65E32912AB0C00F55D4D /* MXRoomEventDecryption.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDCB65E12912AB0C00F55D4D /* MXRoomEventDecryption.swift */; }; EDCF802D2941FF220059E774 /* MXCryptoMigrationV2.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDCF802C2941FF220059E774 /* MXCryptoMigrationV2.swift */; }; @@ -3171,6 +3173,7 @@ EDC2A0E528369E740039F3D6 /* CryptoTests.xctestplan */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = CryptoTests.xctestplan; sourceTree = ""; }; EDC8C4072968A993003792C5 /* MXKeysQueryScheduler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXKeysQueryScheduler.swift; sourceTree = ""; }; EDC8C40A2968A9F7003792C5 /* MXKeysQuerySchedulerUnitTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXKeysQuerySchedulerUnitTests.swift; sourceTree = ""; }; + EDCAD250299BF7F40088B4DA /* MXCryptoV2Feature.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXCryptoV2Feature.swift; sourceTree = ""; }; EDCB65E12912AB0C00F55D4D /* MXRoomEventDecryption.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXRoomEventDecryption.swift; sourceTree = ""; }; EDCF802C2941FF220059E774 /* MXCryptoMigrationV2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXCryptoMigrationV2.swift; sourceTree = ""; }; EDD4197D28DCAA5F007F3757 /* MXNativeKeyBackupEngine.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXNativeKeyBackupEngine.h; sourceTree = ""; }; @@ -3525,6 +3528,7 @@ 322A51B41D9AB15900C8536D /* MXCrypto.h */, 322A51B51D9AB15900C8536D /* MXCrypto.m */, ED47CB6C28523995004FD755 /* MXCryptoV2.swift */, + EDCAD250299BF7F40088B4DA /* MXCryptoV2Feature.swift */, ED5EF151297AB33E00A5ADDA /* MXCryptoV2Factory.swift */, 325D1C251DFECE0D0070B8BF /* MXCrypto_Private.h */, 322A51C51D9BBD3C00C8536D /* MXOlmDevice.h */, @@ -7142,6 +7146,7 @@ EC2EACFF266625170038B61F /* MXRoomLastMessage.m in Sources */, EDF1B6902876CD2C00BBBCEE /* MXTaskQueue.swift in Sources */, EC8A539525B1BC77004E0802 /* MXUserModel.m in Sources */, + EDCAD251299BF7F40088B4DA /* MXCryptoV2Feature.swift in Sources */, ED5EF14F297AB29F00A5ADDA /* MXEventDecryptionResult+DecryptedEvent.swift in Sources */, 3252DCAF224BE5D40032264F /* MXKeyVerificationManager.m in Sources */, 323E0C5C1A306D7A00A31D73 /* MXEvent.m in Sources */, @@ -7809,6 +7814,7 @@ EC8A53D925B1BCC6004E0802 /* MXThirdPartyProtocolInstance.m in Sources */, EDF1B6912876CD2C00BBBCEE /* MXTaskQueue.swift in Sources */, B14EF2422397E90400758AF0 /* MXDeviceInfo.m in Sources */, + EDCAD252299BF7F40088B4DA /* MXCryptoV2Feature.swift in Sources */, ED5EF150297AB29F00A5ADDA /* MXEventDecryptionResult+DecryptedEvent.swift in Sources */, B14EF2432397E90400758AF0 /* MXIncomingSASTransaction.m in Sources */, B14EF2442397E90400758AF0 /* NSObject+sortedKeys.m in Sources */, diff --git a/MatrixSDK/Background/MXBackgroundSyncService.swift b/MatrixSDK/Background/MXBackgroundSyncService.swift index ac7e9303e9..5fe4e5abfb 100644 --- a/MatrixSDK/Background/MXBackgroundSyncService.swift +++ b/MatrixSDK/Background/MXBackgroundSyncService.swift @@ -65,7 +65,12 @@ public enum MXBackgroundSyncServiceError: Error { /// Initializer /// - Parameter credentials: account credentials - public init(withCredentials credentials: MXCredentials, persistTokenDataHandler: MXRestClientPersistTokenDataHandler? = nil, unauthenticatedHandler: MXRestClientUnauthenticatedHandler? = nil) { + public init( + withCredentials credentials: MXCredentials, + isCryptoSDKEnabled: Bool = false, + persistTokenDataHandler: MXRestClientPersistTokenDataHandler? = nil, + unauthenticatedHandler: MXRestClientUnauthenticatedHandler? = nil + ) { processingQueue = DispatchQueue(label: "MXBackgroundSyncServiceQueue-" + MXTools.generateSecret()) self.credentials = credentials @@ -88,14 +93,13 @@ public enum MXBackgroundSyncServiceError: Error { // We can flush any crypto data if our sync response store is empty let resetBackgroundCryptoStore = syncResponseStoreManager.syncToken() == nil - crypto = { - if MXSDKOptions.sharedInstance().isCryptoSDKAvailable && MXSDKOptions.sharedInstance().enableCryptoSDK { - return MXBackgroundCryptoV2(credentials: credentials, restClient: restClient) - } - - MXLog.debug("[MXBackgroundSyncService] init: constructing legacy crypto \(MXSDKOptions.sharedInstance().isCryptoSDKAvailable) \(MXSDKOptions.sharedInstance().enableCryptoSDK)") - return MXLegacyBackgroundCrypto(credentials: credentials, resetBackgroundCryptoStore: resetBackgroundCryptoStore) - }() + if isCryptoSDKEnabled { + MXLog.debug("[MXBackgroundSyncService] init: constructing crypto v2") + crypto = MXBackgroundCryptoV2(credentials: credentials, restClient: restClient) + } else { + MXLog.debug("[MXBackgroundSyncService] init: constructing legacy crypto") + crypto = MXLegacyBackgroundCrypto(credentials: credentials, resetBackgroundCryptoStore: resetBackgroundCryptoStore) + } pushRulesManager = MXBackgroundPushRulesManager(withCredentials: credentials) MXLog.debug("[MXBackgroundSyncService] init complete") diff --git a/MatrixSDK/Crypto/MXCrypto.m b/MatrixSDK/Crypto/MXCrypto.m index d6feec3aeb..ef657ca5eb 100644 --- a/MatrixSDK/Crypto/MXCrypto.m +++ b/MatrixSDK/Crypto/MXCrypto.m @@ -150,25 +150,13 @@ @implementation MXLegacyCrypto @synthesize keyVerificationManager = _keyVerificationManager; @synthesize recoveryService = _recoveryService; -+ (MXCryptoV2Factory *)sharedCryptoV2Factory -{ - static MXCryptoV2Factory *factory = nil; - static dispatch_once_t onceToken; - - dispatch_once(&onceToken, ^{ - factory = [[MXCryptoV2Factory alloc] init]; - }); - - return factory; -} - + (id)createCryptoWithMatrixSession:(MXSession *)mxSession error:(NSError **)error { __block id crypto; #ifdef MX_CRYPTO - if (MXSDKOptions.sharedInstance.isCryptoSDKAvailable && MXSDKOptions.sharedInstance.enableCryptoSDK) + if (MXSDKOptions.sharedInstance.enableCryptoSDK) { MXLogFailure(@"[MXCrypto] createCryptoWithMatrixSession: Crypto V2 should not be created directly, use initializeCryptoWithMatrixSession instead"); return nil; @@ -192,15 +180,19 @@ + (void)initializeCryptoWithMatrixSession:(MXSession *)mxSession complete:(void (^)(id crypto, NSError *error))complete { #ifdef MX_CRYPTO - if (MXSDKOptions.sharedInstance.isCryptoSDKAvailable && MXSDKOptions.sharedInstance.enableCryptoSDK) - { - MXCryptoV2Factory *factory = [MXLegacyCrypto sharedCryptoV2Factory]; - [factory buildCryptoWithSession:mxSession migrationProgress:migrationProgress - success:^(id crypto) { - complete(crypto, nil); - } failure:^(NSError *error) { - complete(nil, error); - }]; + + // Each time we construct the crypto module (app launch, login etc) we have a chance to try to enable + // the newer SDK crypto module, if it is available for this particular user. + [MXSDKOptions.sharedInstance.cryptoSDKFeature enableIfAvailableForUserId:mxSession.myUserId]; + if (MXSDKOptions.sharedInstance.enableCryptoSDK) + { + [MXCryptoV2Factory.shared buildCryptoWithSession:mxSession + migrationProgress:migrationProgress + success:^(id crypto) { + complete(crypto, nil); } + failure:^(NSError *error) { + complete(nil, error); + }]; return; } diff --git a/MatrixSDK/Crypto/MXCryptoV2Factory.swift b/MatrixSDK/Crypto/MXCryptoV2Factory.swift index 14d8c7b0fa..2226aabef5 100644 --- a/MatrixSDK/Crypto/MXCryptoV2Factory.swift +++ b/MatrixSDK/Crypto/MXCryptoV2Factory.swift @@ -22,6 +22,7 @@ import Foundation case storeNotAvailable } + @objc public static let shared = MXCryptoV2Factory() private let log = MXNamedLog(name: "MXCryptoV2Factory") @objc public func buildCrypto( diff --git a/MatrixSDK/Crypto/MXCryptoV2Feature.swift b/MatrixSDK/Crypto/MXCryptoV2Feature.swift new file mode 100644 index 0000000000..0af26c28a1 --- /dev/null +++ b/MatrixSDK/Crypto/MXCryptoV2Feature.swift @@ -0,0 +1,45 @@ +// +// Copyright 2023 The Matrix.org Foundation C.I.C +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +/// Feature representing the availability of the external rust-based Crypto SDK +/// whilst it is not fully available to everyone and / or is an optional feature. +@objc public protocol MXCryptoV2Feature { + + /// Is Crypto SDK currently enabled + /// + /// By default this value is `false`. Once enabled, it can only be disabled by logging out, + /// as there is no way to migrate from from Crypto SDK back to legacy crypto. + var isEnabled: Bool { get } + + /// Manually enable the feature + /// + /// This is typically triggered by some user settings / Labs as an experimental feature. Once called + /// it should restart the session to re-initialize the crypto module. + func enable() + + /// Try to enable the feature for a given user + /// + /// This method should only be called when initializing a crypto module (e.g. during app launch or login), + /// as it is not possible to swap out crypto modules whilst a session is active. + /// + /// The availability conditions are implementation details, typically consisting of + /// various feature flags. + /// + /// If available, this method will set `isEnabled` permanently to `true`. + func enableIfAvailable(forUserId userId: String!) +} diff --git a/MatrixSDK/MXSDKOptions.h b/MatrixSDK/MXSDKOptions.h index 23c4f8ce76..1d59459e0b 100644 --- a/MatrixSDK/MXSDKOptions.h +++ b/MatrixSDK/MXSDKOptions.h @@ -48,7 +48,7 @@ typedef NS_ENUM(NSUInteger, MXCallTransferType) NS_ASSUME_NONNULL_BEGIN -@protocol MXBackgroundModeHandler; +@protocol MXBackgroundModeHandler, MXCryptoV2Feature; /** SDK options that can be set at the launch time. @@ -204,23 +204,18 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic) BOOL enableRoomSharedHistoryOnInvite; /** - The state of the rust-based `MatrixCryptoSDK` which replaces `MatrixSDK`'s internal crypto module, - and whether it is available to a user as an option. + An object which controls the availabilty of the rust-based `MatrixCryptoSDK`. - To control which crypto module is actually used, set `enableCryptoSDK`. - - @remark NO by default. + @remark nil by default. */ -@property (nonatomic) BOOL isCryptoSDKAvailable; +@property (nonatomic, nullable) id cryptoSDKFeature; /** Use the rust-based `MatrixCryptoSDK` instead of `MatrixSDK`'s internal crypto module. - This option should only be enabled if `isCryptoSDKAvailable` is set to YES. - - @remark NO by default. + @remark this property is a convenience getter for `cryptoSDKFeature.isEnabled` */ -@property (nonatomic) BOOL enableCryptoSDK; +@property (nonatomic, readonly) BOOL enableCryptoSDK; /** Enable symmetric room key backups @@ -240,9 +235,9 @@ NS_ASSUME_NONNULL_BEGIN Enable the calculating of progress during session startup, incl counting the number of attempts to sync with the server and percentage of response data processed. - @remark NO by default + @remark the value currently depends on `enableCryptoSDK` being `YES` */ -@property (nonatomic) BOOL enableStartupProgress; +@property (nonatomic, readonly) BOOL enableStartupProgress; @end diff --git a/MatrixSDK/MXSDKOptions.m b/MatrixSDK/MXSDKOptions.m index 820cd5f632..69d7df7c3b 100644 --- a/MatrixSDK/MXSDKOptions.m +++ b/MatrixSDK/MXSDKOptions.m @@ -54,18 +54,31 @@ - (instancetype)init _authEnableRefreshTokens = NO; _enableThreads = NO; _enableRoomSharedHistoryOnInvite = NO; - - _isCryptoSDKAvailable = YES; - _enableCryptoSDK = NO; - _enableSymmetricBackup = NO; _enableNewClientInformationFeature = NO; - _enableStartupProgress = NO; } return self; } +- (BOOL)enableCryptoSDK +{ + if (!self.cryptoSDKFeature) + { + MXLogError(@"[MXSDKOptions] enableCryptoSDK: Crypto SDK feature is not configured"); + return NO; + } + return self.cryptoSDKFeature.isEnabled; +} + +- (BOOL)enableStartupProgress +{ + // The value of `enableStartupProgress` depends on `enableCryptoSDK` as the latter provides some new UX elements + // such as initial data migration. It is a good opportunity to enable startup progress as well, before it becomes + // default to all. + return self.enableCryptoSDK; +} + - (void)setRoomListDataManagerClass:(Class)roomListDataManagerClass { // Sanity check diff --git a/changelog.d/pr-1719.change b/changelog.d/pr-1719.change new file mode 100644 index 0000000000..a84bfa1981 --- /dev/null +++ b/changelog.d/pr-1719.change @@ -0,0 +1 @@ +CryptoV2: Control CryptoSDK via feature flag