diff --git a/CHANGES.md b/CHANGES.md index ed2c9cb829..2736744324 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,8 @@ +## Changes in 0.27.15 (2024-10-15) + +No significant changes. + + ## Changes in 0.27.14 (2024-09-17) No significant changes. diff --git a/MatrixSDK.podspec b/MatrixSDK.podspec index 77d45aaa0a..a3f19eb26f 100644 --- a/MatrixSDK.podspec +++ b/MatrixSDK.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = "MatrixSDK" - s.version = "0.27.14" + s.version = "0.27.15" s.summary = "The iOS SDK to build apps compatible with Matrix (https://www.matrix.org)" s.description = <<-DESC @@ -42,7 +42,6 @@ Pod::Spec.new do |s| ss.dependency 'SwiftyBeaver', '1.9.5' # Requirements for e2e encryption - ss.dependency 'OLMKit', '~> 3.2.5' ss.dependency 'Realm', '10.27.0' ss.dependency 'libbase58', '~> 0.1.4' ss.dependency 'MatrixSDKCrypto', '0.4.3', :configurations => ["DEBUG", "RELEASE"], :inhibit_warnings => true diff --git a/MatrixSDK.xcodeproj/project.pbxproj b/MatrixSDK.xcodeproj/project.pbxproj index 783a8dae9d..23102b07bd 100644 --- a/MatrixSDK.xcodeproj/project.pbxproj +++ b/MatrixSDK.xcodeproj/project.pbxproj @@ -67,14 +67,8 @@ 320A883D217F4E35002EA952 /* MXMegolmBackupCreationInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = 320A883B217F4E35002EA952 /* MXMegolmBackupCreationInfo.m */; }; 320A8840217F4E3F002EA952 /* MXCurve25519BackupAuthData.h in Headers */ = {isa = PBXBuildFile; fileRef = 320A883E217F4E3E002EA952 /* MXCurve25519BackupAuthData.h */; settings = {ATTRIBUTES = (Public, ); }; }; 320A8841217F4E3F002EA952 /* MXCurve25519BackupAuthData.m in Sources */ = {isa = PBXBuildFile; fileRef = 320A883F217F4E3F002EA952 /* MXCurve25519BackupAuthData.m */; }; - 320B3934239FA56900BE2C06 /* MXKeyVerificationByDMRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = 320B3932239FA56900BE2C06 /* MXKeyVerificationByDMRequest.h */; }; - 320B3935239FA56900BE2C06 /* MXKeyVerificationByDMRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = 320B3932239FA56900BE2C06 /* MXKeyVerificationByDMRequest.h */; }; - 320B3936239FA56900BE2C06 /* MXKeyVerificationByDMRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 320B3933239FA56900BE2C06 /* MXKeyVerificationByDMRequest.m */; }; - 320B3937239FA56900BE2C06 /* MXKeyVerificationByDMRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 320B3933239FA56900BE2C06 /* MXKeyVerificationByDMRequest.m */; }; 320B393A239FD15E00BE2C06 /* MXKeyVerificationRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = 320B3938239FD15E00BE2C06 /* MXKeyVerificationRequest.h */; settings = {ATTRIBUTES = (Public, ); }; }; 320B393B239FD15E00BE2C06 /* MXKeyVerificationRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = 320B3938239FD15E00BE2C06 /* MXKeyVerificationRequest.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 320B393C239FD15E00BE2C06 /* MXKeyVerificationRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 320B3939239FD15E00BE2C06 /* MXKeyVerificationRequest.m */; }; - 320B393D239FD15E00BE2C06 /* MXKeyVerificationRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 320B3939239FD15E00BE2C06 /* MXKeyVerificationRequest.m */; }; 320BBF3C1D6C7D9D0079890E /* MXEventsEnumerator.h in Headers */ = {isa = PBXBuildFile; fileRef = 320BBF3B1D6C7D9D0079890E /* MXEventsEnumerator.h */; settings = {ATTRIBUTES = (Public, ); }; }; 320BBF411D6C81550079890E /* MXEventsByTypesEnumeratorOnArray.m in Sources */ = {isa = PBXBuildFile; fileRef = 320BBF3D1D6C81550079890E /* MXEventsByTypesEnumeratorOnArray.m */; }; 320BBF421D6C81550079890E /* MXEventsByTypesEnumeratorOnArray.h in Headers */ = {isa = PBXBuildFile; fileRef = 320BBF3E1D6C81550079890E /* MXEventsByTypesEnumeratorOnArray.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -112,8 +106,6 @@ 321B41401E09937E009EEEC7 /* MXRoomSummary.m in Sources */ = {isa = PBXBuildFile; fileRef = 321B413E1E09937E009EEEC7 /* MXRoomSummary.m */; }; 321CFDE622525A49004D31DF /* MXSASTransaction.h in Headers */ = {isa = PBXBuildFile; fileRef = 321CFDE422525A49004D31DF /* MXSASTransaction.h */; settings = {ATTRIBUTES = (Public, ); }; }; 321CFDE722525A49004D31DF /* MXSASTransaction.m in Sources */ = {isa = PBXBuildFile; fileRef = 321CFDE522525A49004D31DF /* MXSASTransaction.m */; }; - 321CFDEA22525DEE004D31DF /* MXIncomingSASTransaction.h in Headers */ = {isa = PBXBuildFile; fileRef = 321CFDE822525DED004D31DF /* MXIncomingSASTransaction.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 321CFDEB22525DEE004D31DF /* MXIncomingSASTransaction.m in Sources */ = {isa = PBXBuildFile; fileRef = 321CFDE922525DEE004D31DF /* MXIncomingSASTransaction.m */; }; 321CFDEE225264C4004D31DF /* NSArray+MatrixSDK.h in Headers */ = {isa = PBXBuildFile; fileRef = 321CFDEC225264C4004D31DF /* NSArray+MatrixSDK.h */; }; 321CFDEF225264C4004D31DF /* NSArray+MatrixSDK.m in Sources */ = {isa = PBXBuildFile; fileRef = 321CFDED225264C4004D31DF /* NSArray+MatrixSDK.m */; }; 321CFDF92254E721004D31DF /* MXTransactionCancelCode.m in Sources */ = {isa = PBXBuildFile; fileRef = 321CFDF82254E720004D31DF /* MXTransactionCancelCode.m */; }; @@ -132,15 +124,7 @@ 32261B8B23C74A230018F1E2 /* MXDeviceTrustLevel.h in Headers */ = {isa = PBXBuildFile; fileRef = 32261B8823C74A230018F1E2 /* MXDeviceTrustLevel.h */; settings = {ATTRIBUTES = (Public, ); }; }; 32261B8C23C74A230018F1E2 /* MXDeviceTrustLevel.m in Sources */ = {isa = PBXBuildFile; fileRef = 32261B8923C74A230018F1E2 /* MXDeviceTrustLevel.m */; }; 32261B8D23C74A230018F1E2 /* MXDeviceTrustLevel.m in Sources */ = {isa = PBXBuildFile; fileRef = 32261B8923C74A230018F1E2 /* MXDeviceTrustLevel.m */; }; - 322691321E5EF77D00966A6E /* MXDeviceListOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = 322691301E5EF77D00966A6E /* MXDeviceListOperation.h */; }; - 322691331E5EF77D00966A6E /* MXDeviceListOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 322691311E5EF77D00966A6E /* MXDeviceListOperation.m */; }; - 322691361E5EFF8700966A6E /* MXDeviceListOperationsPool.h in Headers */ = {isa = PBXBuildFile; fileRef = 322691341E5EFF8700966A6E /* MXDeviceListOperationsPool.h */; }; - 322691371E5EFF8700966A6E /* MXDeviceListOperationsPool.m in Sources */ = {isa = PBXBuildFile; fileRef = 322691351E5EFF8700966A6E /* MXDeviceListOperationsPool.m */; }; 3226DC3819DEED3B00866530 /* MatrixSDK.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 32C6F92D19DD814400EA4E9C /* MatrixSDK.framework */; }; - 3229535125A5F7220012FCF0 /* MXBackgroundCryptoStore.h in Headers */ = {isa = PBXBuildFile; fileRef = 3229534F25A5F7220012FCF0 /* MXBackgroundCryptoStore.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 3229535225A5F7220012FCF0 /* MXBackgroundCryptoStore.h in Headers */ = {isa = PBXBuildFile; fileRef = 3229534F25A5F7220012FCF0 /* MXBackgroundCryptoStore.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 3229535325A5F7220012FCF0 /* MXBackgroundCryptoStore.m in Sources */ = {isa = PBXBuildFile; fileRef = 3229535025A5F7220012FCF0 /* MXBackgroundCryptoStore.m */; }; - 3229535425A5F7220012FCF0 /* MXBackgroundCryptoStore.m in Sources */ = {isa = PBXBuildFile; fileRef = 3229535025A5F7220012FCF0 /* MXBackgroundCryptoStore.m */; }; 322985CB26FAF898001890BC /* MXSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 322985CA26FAF898001890BC /* MXSession.swift */; }; 322985CC26FAF898001890BC /* MXSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 322985CA26FAF898001890BC /* MXSession.swift */; }; 322985CF26FBAE7B001890BC /* TestObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 322985CE26FBAE7B001890BC /* TestObserver.swift */; }; @@ -148,11 +132,6 @@ 322985D226FC9E61001890BC /* MXSessionTracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 322985D126FC9E61001890BC /* MXSessionTracker.swift */; }; 322985D326FC9E61001890BC /* MXSessionTracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 322985D126FC9E61001890BC /* MXSessionTracker.swift */; }; 322A51B61D9AB15900C8536D /* MXCrypto.h in Headers */ = {isa = PBXBuildFile; fileRef = 322A51B41D9AB15900C8536D /* MXCrypto.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 322A51B71D9AB15900C8536D /* MXCrypto.m in Sources */ = {isa = PBXBuildFile; fileRef = 322A51B51D9AB15900C8536D /* MXCrypto.m */; }; - 322A51C31D9AC8FE00C8536D /* MXCryptoAlgorithms.h in Headers */ = {isa = PBXBuildFile; fileRef = 322A51C11D9AC8FE00C8536D /* MXCryptoAlgorithms.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 322A51C41D9AC8FE00C8536D /* MXCryptoAlgorithms.m in Sources */ = {isa = PBXBuildFile; fileRef = 322A51C21D9AC8FE00C8536D /* MXCryptoAlgorithms.m */; }; - 322A51C71D9BBD3C00C8536D /* MXOlmDevice.h in Headers */ = {isa = PBXBuildFile; fileRef = 322A51C51D9BBD3C00C8536D /* MXOlmDevice.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 322A51C81D9BBD3C00C8536D /* MXOlmDevice.m in Sources */ = {isa = PBXBuildFile; fileRef = 322A51C61D9BBD3C00C8536D /* MXOlmDevice.m */; }; 322A51D81D9E846800C8536D /* MXCryptoTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 322A51D71D9E846800C8536D /* MXCryptoTests.m */; }; 322AB584260CD5690017E964 /* MXCachedSyncResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = 322AB582260CD5690017E964 /* MXCachedSyncResponse.h */; settings = {ATTRIBUTES = (Public, ); }; }; 322AB585260CD5690017E964 /* MXCachedSyncResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = 322AB582260CD5690017E964 /* MXCachedSyncResponse.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -195,8 +174,6 @@ 324095221AFA432F00D81C97 /* MXCallStackCall.h in Headers */ = {isa = PBXBuildFile; fileRef = 3240951E1AFA432F00D81C97 /* MXCallStackCall.h */; settings = {ATTRIBUTES = (Public, ); }; }; 3240969D1F9F751600DBA607 /* MXPushRuleSenderNotificationPermissionConditionChecker.h in Headers */ = {isa = PBXBuildFile; fileRef = 3240969B1F9F751600DBA607 /* MXPushRuleSenderNotificationPermissionConditionChecker.h */; settings = {ATTRIBUTES = (Public, ); }; }; 3240969E1F9F751600DBA607 /* MXPushRuleSenderNotificationPermissionConditionChecker.m in Sources */ = {isa = PBXBuildFile; fileRef = 3240969C1F9F751600DBA607 /* MXPushRuleSenderNotificationPermissionConditionChecker.m */; }; - 32442FB121EDD21300D2411B /* MXKeyBackupPassword.h in Headers */ = {isa = PBXBuildFile; fileRef = 32442FAF21EDD21300D2411B /* MXKeyBackupPassword.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 32442FB221EDD21300D2411B /* MXKeyBackupPassword.m in Sources */ = {isa = PBXBuildFile; fileRef = 32442FB021EDD21300D2411B /* MXKeyBackupPassword.m */; }; 3245A7501AF7B2930001D8A7 /* MXCall.h in Headers */ = {isa = PBXBuildFile; fileRef = 3245A74C1AF7B2930001D8A7 /* MXCall.h */; settings = {ATTRIBUTES = (Public, ); }; }; 3245A7511AF7B2930001D8A7 /* MXCall.m in Sources */ = {isa = PBXBuildFile; fileRef = 3245A74D1AF7B2930001D8A7 /* MXCall.m */; }; 3245A7521AF7B2930001D8A7 /* MXCallManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 3245A74E1AF7B2930001D8A7 /* MXCallManager.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -267,7 +244,6 @@ 3251D41F25AF01D7001E6E77 /* MXUIKitApplicationStateService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3251D41E25AF01D7001E6E77 /* MXUIKitApplicationStateService.swift */; }; 3251D42025AF01D7001E6E77 /* MXUIKitApplicationStateService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3251D41E25AF01D7001E6E77 /* MXUIKitApplicationStateService.swift */; }; 3252DCAE224BE5D40032264F /* MXKeyVerificationManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 3252DCAC224BE5D40032264F /* MXKeyVerificationManager.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 3252DCAF224BE5D40032264F /* MXKeyVerificationManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 3252DCAD224BE5D40032264F /* MXKeyVerificationManager.m */; }; 3252DCB6224BEA610032264F /* MXKeyVerificationTransaction.h in Headers */ = {isa = PBXBuildFile; fileRef = 3252DCB5224BEA610032264F /* MXKeyVerificationTransaction.h */; settings = {ATTRIBUTES = (Public, ); }; }; 325380E8228DAD4A00ADDEFA /* MXAggregatedReactions.h in Headers */ = {isa = PBXBuildFile; fileRef = 325380E6228DAD4A00ADDEFA /* MXAggregatedReactions.h */; settings = {ATTRIBUTES = (Public, ); }; }; 325380E9228DAD4A00ADDEFA /* MXAggregatedReactions.m in Sources */ = {isa = PBXBuildFile; fileRef = 325380E7228DAD4A00ADDEFA /* MXAggregatedReactions.m */; }; @@ -282,8 +258,6 @@ 32581DE923C8C0C900832EAA /* MXUserTrustLevel.h in Headers */ = {isa = PBXBuildFile; fileRef = 32581DE623C8C0C900832EAA /* MXUserTrustLevel.h */; settings = {ATTRIBUTES = (Public, ); }; }; 32581DEA23C8C0C900832EAA /* MXUserTrustLevel.m in Sources */ = {isa = PBXBuildFile; fileRef = 32581DE723C8C0C900832EAA /* MXUserTrustLevel.m */; }; 32581DEB23C8C0C900832EAA /* MXUserTrustLevel.m in Sources */ = {isa = PBXBuildFile; fileRef = 32581DE723C8C0C900832EAA /* MXUserTrustLevel.m */; }; - 3259CD531DF860C300186944 /* MXRealmCryptoStore.h in Headers */ = {isa = PBXBuildFile; fileRef = 3259CD511DF860C300186944 /* MXRealmCryptoStore.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 3259CD541DF860C300186944 /* MXRealmCryptoStore.m in Sources */ = {isa = PBXBuildFile; fileRef = 3259CD521DF860C300186944 /* MXRealmCryptoStore.m */; }; 3259CFE626026A6F00C365DB /* MXRestClient+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3259CFE526026A6F00C365DB /* MXRestClient+Extensions.swift */; }; 3259CFE726026A6F00C365DB /* MXRestClient+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3259CFE526026A6F00C365DB /* MXRestClient+Extensions.swift */; }; 3259D0082603705300C365DB /* Array.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3259D0072603705300C365DB /* Array.swift */; }; @@ -302,7 +276,6 @@ 325AF3E224897D9400EF937D /* MXSecretRecoveryResult.h in Headers */ = {isa = PBXBuildFile; fileRef = 325AF3DF24897D9400EF937D /* MXSecretRecoveryResult.h */; settings = {ATTRIBUTES = (Public, ); }; }; 325AF3E324897D9400EF937D /* MXSecretRecoveryResult.m in Sources */ = {isa = PBXBuildFile; fileRef = 325AF3E024897D9400EF937D /* MXSecretRecoveryResult.m */; }; 325AF3E424897D9400EF937D /* MXSecretRecoveryResult.m in Sources */ = {isa = PBXBuildFile; fileRef = 325AF3E024897D9400EF937D /* MXSecretRecoveryResult.m */; }; - 325D1C261DFECE0D0070B8BF /* MXCrypto_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 325D1C251DFECE0D0070B8BF /* MXCrypto_Private.h */; }; 326056851C76FDF2009D44AD /* MXRoomEventTimeline.h in Headers */ = {isa = PBXBuildFile; fileRef = 326056831C76FDF1009D44AD /* MXRoomEventTimeline.h */; }; 326056861C76FDF2009D44AD /* MXRoomEventTimeline.m in Sources */ = {isa = PBXBuildFile; fileRef = 326056841C76FDF1009D44AD /* MXRoomEventTimeline.m */; }; 32618E7120ED2DF500E1D2EA /* MXFilterJSONModel.h in Headers */ = {isa = PBXBuildFile; fileRef = 32618E6F20ED2DF500E1D2EA /* MXFilterJSONModel.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -311,8 +284,6 @@ 32618E7C20EFA45B00E1D2EA /* MXRoomMembers.m in Sources */ = {isa = PBXBuildFile; fileRef = 32618E7A20EFA45B00E1D2EA /* MXRoomMembers.m */; }; 326277F1288BC21A009A0508 /* AllWorkingTests.xctestplan in Resources */ = {isa = PBXBuildFile; fileRef = 326277F0288BC219009A0508 /* AllWorkingTests.xctestplan */; }; 326277F2288BC21A009A0508 /* AllWorkingTests.xctestplan in Resources */ = {isa = PBXBuildFile; fileRef = 326277F0288BC219009A0508 /* AllWorkingTests.xctestplan */; }; - 32637ED41E5B00400011E20D /* MXDeviceList.h in Headers */ = {isa = PBXBuildFile; fileRef = 32637ED21E5B00400011E20D /* MXDeviceList.h */; }; - 32637ED51E5B00400011E20D /* MXDeviceList.m in Sources */ = {isa = PBXBuildFile; fileRef = 32637ED31E5B00400011E20D /* MXDeviceList.m */; }; 3264DB911CEC528D00B99881 /* MXAccountData.h in Headers */ = {isa = PBXBuildFile; fileRef = 3264DB8F1CEC528D00B99881 /* MXAccountData.h */; settings = {ATTRIBUTES = (Public, ); }; }; 3264DB921CEC528D00B99881 /* MXAccountData.m in Sources */ = {isa = PBXBuildFile; fileRef = 3264DB901CEC528D00B99881 /* MXAccountData.m */; }; 3264DB941CECA72900B99881 /* MXAccountDataTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3264DB931CECA72900B99881 /* MXAccountDataTests.m */; }; @@ -325,10 +296,6 @@ 327137271A24D50A00DB6757 /* MXMyUser.h in Headers */ = {isa = PBXBuildFile; fileRef = 327137251A24D50A00DB6757 /* MXMyUser.h */; settings = {ATTRIBUTES = (Public, ); }; }; 327137281A24D50A00DB6757 /* MXMyUser.m in Sources */ = {isa = PBXBuildFile; fileRef = 327137261A24D50A00DB6757 /* MXMyUser.m */; }; 3271877B1DA7CAA60071C818 /* MXEncrypting.h in Headers */ = {isa = PBXBuildFile; fileRef = 3271877A1DA7CAA60071C818 /* MXEncrypting.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 327187851DA7D0220071C818 /* MXOlmDecryption.h in Headers */ = {isa = PBXBuildFile; fileRef = 327187831DA7D0220071C818 /* MXOlmDecryption.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 327187861DA7D0220071C818 /* MXOlmDecryption.m in Sources */ = {isa = PBXBuildFile; fileRef = 327187841DA7D0220071C818 /* MXOlmDecryption.m */; }; - 327187891DA7DCE50071C818 /* MXOlmEncryption.h in Headers */ = {isa = PBXBuildFile; fileRef = 327187871DA7DCE50071C818 /* MXOlmEncryption.h */; }; - 3271878A1DA7DCE50071C818 /* MXOlmEncryption.m in Sources */ = {isa = PBXBuildFile; fileRef = 327187881DA7DCE50071C818 /* MXOlmEncryption.m */; }; 32720D9D222EAA6F0086FFF5 /* MXDiscoveredClientConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = 32720D99222EAA6F0086FFF5 /* MXDiscoveredClientConfig.m */; }; 32720D9E222EAA6F0086FFF5 /* MXAutoDiscovery.h in Headers */ = {isa = PBXBuildFile; fileRef = 32720D9A222EAA6F0086FFF5 /* MXAutoDiscovery.h */; settings = {ATTRIBUTES = (Public, ); }; }; 32720D9F222EAA6F0086FFF5 /* MXAutoDiscovery.m in Sources */ = {isa = PBXBuildFile; fileRef = 32720D9B222EAA6F0086FFF5 /* MXAutoDiscovery.m */; }; @@ -338,10 +305,6 @@ 3274538523FD69D600438328 /* MXKeyVerificationRequestByToDeviceJSONModel.h in Headers */ = {isa = PBXBuildFile; fileRef = 3274538223FD69D600438328 /* MXKeyVerificationRequestByToDeviceJSONModel.h */; }; 3274538623FD69D600438328 /* MXKeyVerificationRequestByToDeviceJSONModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 3274538323FD69D600438328 /* MXKeyVerificationRequestByToDeviceJSONModel.m */; }; 3274538723FD69D600438328 /* MXKeyVerificationRequestByToDeviceJSONModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 3274538323FD69D600438328 /* MXKeyVerificationRequestByToDeviceJSONModel.m */; }; - 3274538A23FD918800438328 /* MXKeyVerificationByToDeviceRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = 3274538823FD918800438328 /* MXKeyVerificationByToDeviceRequest.h */; }; - 3274538B23FD918800438328 /* MXKeyVerificationByToDeviceRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = 3274538823FD918800438328 /* MXKeyVerificationByToDeviceRequest.h */; }; - 3274538C23FD918800438328 /* MXKeyVerificationByToDeviceRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 3274538923FD918800438328 /* MXKeyVerificationByToDeviceRequest.m */; }; - 3274538D23FD918800438328 /* MXKeyVerificationByToDeviceRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 3274538923FD918800438328 /* MXKeyVerificationByToDeviceRequest.m */; }; 3275FD9421A6B46600B9C13D /* MXLoginTerms.m in Sources */ = {isa = PBXBuildFile; fileRef = 3275FD9221A6B46600B9C13D /* MXLoginTerms.m */; }; 3275FD9521A6B46600B9C13D /* MXLoginTerms.h in Headers */ = {isa = PBXBuildFile; fileRef = 3275FD9321A6B46600B9C13D /* MXLoginTerms.h */; settings = {ATTRIBUTES = (Public, ); }; }; 3275FD9821A6B53300B9C13D /* MXLoginPolicyData.h in Headers */ = {isa = PBXBuildFile; fileRef = 3275FD9621A6B53300B9C13D /* MXLoginPolicyData.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -402,9 +365,6 @@ 32832B5E1BCC048300241108 /* MXStoreNoStoreTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 32832B5A1BCC048300241108 /* MXStoreNoStoreTests.m */; }; 3283F7781EAF30F700C1688C /* MXBugReportRestClient.h in Headers */ = {isa = PBXBuildFile; fileRef = 3283F7761EAF30F700C1688C /* MXBugReportRestClient.h */; settings = {ATTRIBUTES = (Public, ); }; }; 3283F7791EAF30F700C1688C /* MXBugReportRestClient.m in Sources */ = {isa = PBXBuildFile; fileRef = 3283F7771EAF30F700C1688C /* MXBugReportRestClient.m */; }; - 3284A5A01DB7C00600A09972 /* MXCryptoStore.h in Headers */ = {isa = PBXBuildFile; fileRef = 3284A59D1DB7C00600A09972 /* MXCryptoStore.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 3287164F23C4C11F00D720CA /* MXKeyVerificationManager_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 3252DCCF224D25810032264F /* MXKeyVerificationManager_Private.h */; }; - 3287165023C4C12200D720CA /* MXKeyVerificationManager_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 3252DCCF224D25810032264F /* MXKeyVerificationManager_Private.h */; }; 328BCB3321947BE200A976D3 /* MXKeyBackupVersionTrust.h in Headers */ = {isa = PBXBuildFile; fileRef = 328BCB3121947BE200A976D3 /* MXKeyBackupVersionTrust.h */; settings = {ATTRIBUTES = (Public, ); }; }; 328BCB3421947BE200A976D3 /* MXKeyBackupVersionTrust.m in Sources */ = {isa = PBXBuildFile; fileRef = 328BCB3221947BE200A976D3 /* MXKeyBackupVersionTrust.m */; }; 328DDEC11A07E57E008C7DC8 /* MXJSONModelTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 328DDEC01A07E57E008C7DC8 /* MXJSONModelTests.m */; }; @@ -431,10 +391,6 @@ 3297912823A93D4B00F7BB9B /* MXKeyVerification.h in Headers */ = {isa = PBXBuildFile; fileRef = 3297912523A93D4B00F7BB9B /* MXKeyVerification.h */; settings = {ATTRIBUTES = (Public, ); }; }; 3297912923A93D4B00F7BB9B /* MXKeyVerification.m in Sources */ = {isa = PBXBuildFile; fileRef = 3297912623A93D4B00F7BB9B /* MXKeyVerification.m */; }; 3297912A23A93D4B00F7BB9B /* MXKeyVerification.m in Sources */ = {isa = PBXBuildFile; fileRef = 3297912623A93D4B00F7BB9B /* MXKeyVerification.m */; }; - 3297912E23AA126500F7BB9B /* MXKeyVerificationStatusResolver.h in Headers */ = {isa = PBXBuildFile; fileRef = 3297912C23AA126400F7BB9B /* MXKeyVerificationStatusResolver.h */; }; - 3297912F23AA126500F7BB9B /* MXKeyVerificationStatusResolver.h in Headers */ = {isa = PBXBuildFile; fileRef = 3297912C23AA126400F7BB9B /* MXKeyVerificationStatusResolver.h */; }; - 3297913023AA126500F7BB9B /* MXKeyVerificationStatusResolver.m in Sources */ = {isa = PBXBuildFile; fileRef = 3297912D23AA126400F7BB9B /* MXKeyVerificationStatusResolver.m */; }; - 3297913123AA126500F7BB9B /* MXKeyVerificationStatusResolver.m in Sources */ = {isa = PBXBuildFile; fileRef = 3297912D23AA126400F7BB9B /* MXKeyVerificationStatusResolver.m */; }; 3298ABD62637FA3100E40B06 /* AllTests.xctestplan in Resources */ = {isa = PBXBuildFile; fileRef = 3298ABD52637FA3100E40B06 /* AllTests.xctestplan */; }; 3298ABD72637FA3100E40B06 /* AllTests.xctestplan in Resources */ = {isa = PBXBuildFile; fileRef = 3298ABD52637FA3100E40B06 /* AllTests.xctestplan */; }; 3298ABDD2637FB1900E40B06 /* UnitTests.xctestplan in Resources */ = {isa = PBXBuildFile; fileRef = 3298ABDC2637FB1900E40B06 /* UnitTests.xctestplan */; }; @@ -445,9 +401,6 @@ 32999DE422DCD1AD004FF987 /* MXPusherData.m in Sources */ = {isa = PBXBuildFile; fileRef = 32999DE222DCD1AD004FF987 /* MXPusherData.m */; }; 329D3E621E251027002E2F1E /* MXRoomSummaryUpdater.h in Headers */ = {isa = PBXBuildFile; fileRef = 329D3E601E251027002E2F1E /* MXRoomSummaryUpdater.h */; settings = {ATTRIBUTES = (Public, ); }; }; 329D3E631E251027002E2F1E /* MXRoomSummaryUpdater.m in Sources */ = {isa = PBXBuildFile; fileRef = 329D3E611E251027002E2F1E /* MXRoomSummaryUpdater.m */; }; - 329E8089224E261600A48C3A /* MXKeyVerificationTransaction.m in Sources */ = {isa = PBXBuildFile; fileRef = 329E8088224E261600A48C3A /* MXKeyVerificationTransaction.m */; }; - 329E808C224E2E1B00A48C3A /* MXOutgoingSASTransaction.h in Headers */ = {isa = PBXBuildFile; fileRef = 329E808A224E2E1B00A48C3A /* MXOutgoingSASTransaction.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 329E808D224E2E1B00A48C3A /* MXOutgoingSASTransaction.m in Sources */ = {isa = PBXBuildFile; fileRef = 329E808B224E2E1B00A48C3A /* MXOutgoingSASTransaction.m */; }; 329E808F22512DF500A48C3A /* MXCryptoKeyVerificationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 329E808E22512DF500A48C3A /* MXCryptoKeyVerificationTests.m */; }; 329FB1751A0A3A1600A5E88E /* MXRoomMember.h in Headers */ = {isa = PBXBuildFile; fileRef = 329FB1731A0A3A1600A5E88E /* MXRoomMember.h */; settings = {ATTRIBUTES = (Public, ); }; }; 329FB1761A0A3A1600A5E88E /* MXRoomMember.m in Sources */ = {isa = PBXBuildFile; fileRef = 329FB1741A0A3A1600A5E88E /* MXRoomMember.m */; }; @@ -456,12 +409,6 @@ 329FB17C1A0A963700A5E88E /* MXRoomMemberTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 329FB17B1A0A963700A5E88E /* MXRoomMemberTests.m */; }; 329FB17F1A0B665800A5E88E /* MXUser.h in Headers */ = {isa = PBXBuildFile; fileRef = 329FB17D1A0B665800A5E88E /* MXUser.h */; settings = {ATTRIBUTES = (Public, ); }; }; 329FB1801A0B665800A5E88E /* MXUser.m in Sources */ = {isa = PBXBuildFile; fileRef = 329FB17E1A0B665800A5E88E /* MXUser.m */; }; - 32A151261DABB0CB00400192 /* MXMegolmDecryption.h in Headers */ = {isa = PBXBuildFile; fileRef = 32A151241DABB0CB00400192 /* MXMegolmDecryption.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 32A151271DABB0CB00400192 /* MXMegolmDecryption.m in Sources */ = {isa = PBXBuildFile; fileRef = 32A151251DABB0CB00400192 /* MXMegolmDecryption.m */; }; - 32A151391DAD292400400192 /* MXMegolmEncryption.h in Headers */ = {isa = PBXBuildFile; fileRef = 32A151371DAD292400400192 /* MXMegolmEncryption.h */; }; - 32A1513A1DAD292400400192 /* MXMegolmEncryption.m in Sources */ = {isa = PBXBuildFile; fileRef = 32A151381DAD292400400192 /* MXMegolmEncryption.m */; }; - 32A1513E1DAF768D00400192 /* MXOlmInboundGroupSession.h in Headers */ = {isa = PBXBuildFile; fileRef = 32A1513C1DAF768D00400192 /* MXOlmInboundGroupSession.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 32A1513F1DAF768D00400192 /* MXOlmInboundGroupSession.m in Sources */ = {isa = PBXBuildFile; fileRef = 32A1513D1DAF768D00400192 /* MXOlmInboundGroupSession.m */; }; 32A151461DAF7C0C00400192 /* MXDeviceInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = 32A151401DAF7C0C00400192 /* MXDeviceInfo.h */; settings = {ATTRIBUTES = (Public, ); }; }; 32A151471DAF7C0C00400192 /* MXDeviceInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = 32A151411DAF7C0C00400192 /* MXDeviceInfo.m */; }; 32A151481DAF7C0C00400192 /* MXKey.h in Headers */ = {isa = PBXBuildFile; fileRef = 32A151421DAF7C0C00400192 /* MXKey.h */; }; @@ -475,8 +422,6 @@ 32A1515B1DB525DA00400192 /* NSObject+sortedKeys.h in Headers */ = {isa = PBXBuildFile; fileRef = 32A151591DB525DA00400192 /* NSObject+sortedKeys.h */; }; 32A1515C1DB525DA00400192 /* NSObject+sortedKeys.m in Sources */ = {isa = PBXBuildFile; fileRef = 32A1515A1DB525DA00400192 /* NSObject+sortedKeys.m */; }; 32A27D1F19EC335300BAFADE /* MXRoomTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 32A27D1E19EC335300BAFADE /* MXRoomTests.m */; }; - 32A30B181FB4813400C8309E /* MXIncomingRoomKeyRequestManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 32A30B161FB4813400C8309E /* MXIncomingRoomKeyRequestManager.h */; }; - 32A30B191FB4813400C8309E /* MXIncomingRoomKeyRequestManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 32A30B171FB4813400C8309E /* MXIncomingRoomKeyRequestManager.m */; }; 32A31BBE20D3F2EC005916C7 /* MXFilterObject.h in Headers */ = {isa = PBXBuildFile; fileRef = 32A31BBC20D3F2EC005916C7 /* MXFilterObject.h */; settings = {ATTRIBUTES = (Public, ); }; }; 32A31BBF20D3F2EC005916C7 /* MXFilterObject.m in Sources */ = {isa = PBXBuildFile; fileRef = 32A31BBD20D3F2EC005916C7 /* MXFilterObject.m */; }; 32A31BC120D3F4C4005916C7 /* MXFilterTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 32A31BC020D3F4C4005916C7 /* MXFilterTests.m */; }; @@ -505,10 +450,6 @@ 32AF928B240EA3880008A0FD /* MXSecretShareSend.h in Headers */ = {isa = PBXBuildFile; fileRef = 32AF9288240EA3880008A0FD /* MXSecretShareSend.h */; settings = {ATTRIBUTES = (Public, ); }; }; 32AF928C240EA3880008A0FD /* MXSecretShareSend.m in Sources */ = {isa = PBXBuildFile; fileRef = 32AF9289240EA3880008A0FD /* MXSecretShareSend.m */; }; 32AF928D240EA3880008A0FD /* MXSecretShareSend.m in Sources */ = {isa = PBXBuildFile; fileRef = 32AF9289240EA3880008A0FD /* MXSecretShareSend.m */; }; - 32AF928F24110ADD0008A0FD /* MXSecretShareManager_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 32AF928E24110ADD0008A0FD /* MXSecretShareManager_Private.h */; }; - 32AF929024110ADD0008A0FD /* MXSecretShareManager_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 32AF928E24110ADD0008A0FD /* MXSecretShareManager_Private.h */; }; - 32AF9292241112850008A0FD /* MXCryptoSecretShareTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 32AF9291241112850008A0FD /* MXCryptoSecretShareTests.m */; }; - 32AF9293241112850008A0FD /* MXCryptoSecretShareTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 32AF9291241112850008A0FD /* MXCryptoSecretShareTests.m */; }; 32AF929724115D8B0008A0FD /* MXPendingSecretShareRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = 32AF929524115D8B0008A0FD /* MXPendingSecretShareRequest.h */; }; 32AF929824115D8B0008A0FD /* MXPendingSecretShareRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = 32AF929524115D8B0008A0FD /* MXPendingSecretShareRequest.h */; }; 32AF929924115D8B0008A0FD /* MXPendingSecretShareRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 32AF929624115D8B0008A0FD /* MXPendingSecretShareRequest.m */; }; @@ -559,16 +500,6 @@ 32C474C122AF7A2D00CFBCD2 /* MXReactionOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = 32C474BF22AF7A2D00CFBCD2 /* MXReactionOperation.h */; settings = {ATTRIBUTES = (Public, ); }; }; 32C474C222AF7A2D00CFBCD2 /* MXReactionOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 32C474C022AF7A2D00CFBCD2 /* MXReactionOperation.m */; }; 32C6F93319DD814400EA4E9C /* MatrixSDK.h in Headers */ = {isa = PBXBuildFile; fileRef = 32C6F93219DD814400EA4E9C /* MatrixSDK.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 32C78B68256CFC4D008130B1 /* MXCryptoVersion.h in Headers */ = {isa = PBXBuildFile; fileRef = 32C78B65256CFC4D008130B1 /* MXCryptoVersion.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 32C78B69256CFC4D008130B1 /* MXCryptoVersion.h in Headers */ = {isa = PBXBuildFile; fileRef = 32C78B65256CFC4D008130B1 /* MXCryptoVersion.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 32C78B6A256CFC4D008130B1 /* MXCryptoMigration.m in Sources */ = {isa = PBXBuildFile; fileRef = 32C78B66256CFC4D008130B1 /* MXCryptoMigration.m */; }; - 32C78B6B256CFC4D008130B1 /* MXCryptoMigration.m in Sources */ = {isa = PBXBuildFile; fileRef = 32C78B66256CFC4D008130B1 /* MXCryptoMigration.m */; }; - 32C78B6C256CFC4D008130B1 /* MXCryptoMigration.h in Headers */ = {isa = PBXBuildFile; fileRef = 32C78B67256CFC4D008130B1 /* MXCryptoMigration.h */; }; - 32C78B6D256CFC4D008130B1 /* MXCryptoMigration.h in Headers */ = {isa = PBXBuildFile; fileRef = 32C78B67256CFC4D008130B1 /* MXCryptoMigration.h */; }; - 32C78BA7256D227D008130B1 /* MXCryptoMigrationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 32C78BA6256D227D008130B1 /* MXCryptoMigrationTests.m */; }; - 32C78BA8256D227D008130B1 /* MXCryptoMigrationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 32C78BA6256D227D008130B1 /* MXCryptoMigrationTests.m */; }; - 32C9B71823E81A1C00C6F30A /* MXCrossSigningVerificationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 32C9B71723E81A1C00C6F30A /* MXCrossSigningVerificationTests.m */; }; - 32C9B71923E81A1C00C6F30A /* MXCrossSigningVerificationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 32C9B71723E81A1C00C6F30A /* MXCrossSigningVerificationTests.m */; }; 32CAB1071A91EA34008C5BB9 /* MXPushRuleRoomMemberCountConditionChecker.h in Headers */ = {isa = PBXBuildFile; fileRef = 32CAB1051A91EA34008C5BB9 /* MXPushRuleRoomMemberCountConditionChecker.h */; settings = {ATTRIBUTES = (Public, ); }; }; 32CAB1081A91EA34008C5BB9 /* MXPushRuleRoomMemberCountConditionChecker.m in Sources */ = {isa = PBXBuildFile; fileRef = 32CAB1061A91EA34008C5BB9 /* MXPushRuleRoomMemberCountConditionChecker.m */; }; 32CAB10B1A925B41008C5BB9 /* MXHTTPOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = 32CAB1091A925B41008C5BB9 /* MXHTTPOperation.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -587,8 +518,6 @@ 32CEEF4C23B0A8170039BA98 /* MXCrossSigningTools.m in Sources */ = {isa = PBXBuildFile; fileRef = 32CEEF4823B0A8170039BA98 /* MXCrossSigningTools.m */; }; 32CEEF4F23B0AB030039BA98 /* MXCrossSigning.h in Headers */ = {isa = PBXBuildFile; fileRef = 32CEEF4D23B0AB030039BA98 /* MXCrossSigning.h */; settings = {ATTRIBUTES = (Public, ); }; }; 32CEEF5023B0AB030039BA98 /* MXCrossSigning.h in Headers */ = {isa = PBXBuildFile; fileRef = 32CEEF4D23B0AB030039BA98 /* MXCrossSigning.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 32CEEF5123B0AB030039BA98 /* MXCrossSigning.m in Sources */ = {isa = PBXBuildFile; fileRef = 32CEEF4E23B0AB030039BA98 /* MXCrossSigning.m */; }; - 32CEEF5223B0AB030039BA98 /* MXCrossSigning.m in Sources */ = {isa = PBXBuildFile; fileRef = 32CEEF4E23B0AB030039BA98 /* MXCrossSigning.m */; }; 32CF439D2371AF9500907C56 /* MXWellknownIntegrations.h in Headers */ = {isa = PBXBuildFile; fileRef = 32CF439B2371AF9500907C56 /* MXWellknownIntegrations.h */; settings = {ATTRIBUTES = (Public, ); }; }; 32CF439E2371AF9500907C56 /* MXWellknownIntegrations.m in Sources */ = {isa = PBXBuildFile; fileRef = 32CF439C2371AF9500907C56 /* MXWellknownIntegrations.m */; }; 32D2CC0323422462002BD8CA /* MX3PidAddSession.m in Sources */ = {isa = PBXBuildFile; fileRef = 32D2CBFF23422462002BD8CA /* MX3PidAddSession.m */; }; @@ -633,8 +562,6 @@ 32F945F81FAB83D900622468 /* MXIncomingRoomKeyRequestCancellation.h in Headers */ = {isa = PBXBuildFile; fileRef = 32F945F41FAB83D900622468 /* MXIncomingRoomKeyRequestCancellation.h */; settings = {ATTRIBUTES = (Public, ); }; }; 32F9FA7D1DBA0CF0009D98A6 /* MXDecryptionResult.h in Headers */ = {isa = PBXBuildFile; fileRef = 32F9FA7B1DBA0CF0009D98A6 /* MXDecryptionResult.h */; settings = {ATTRIBUTES = (Public, ); }; }; 32F9FA7E1DBA0CF0009D98A6 /* MXDecryptionResult.m in Sources */ = {isa = PBXBuildFile; fileRef = 32F9FA7C1DBA0CF0009D98A6 /* MXDecryptionResult.m */; }; - 32FA10C11FA1C9EE00E54233 /* MXOutgoingRoomKeyRequestManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 32FA10BF1FA1C9EE00E54233 /* MXOutgoingRoomKeyRequestManager.h */; }; - 32FA10C21FA1C9EE00E54233 /* MXOutgoingRoomKeyRequestManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 32FA10C01FA1C9EE00E54233 /* MXOutgoingRoomKeyRequestManager.m */; }; 32FA10CE1FA1C9F700E54233 /* MXOutgoingRoomKeyRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = 32FA10C81FA1C9F700E54233 /* MXOutgoingRoomKeyRequest.h */; settings = {ATTRIBUTES = (Public, ); }; }; 32FA10CF1FA1C9F700E54233 /* MXOutgoingRoomKeyRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 32FA10C91FA1C9F700E54233 /* MXOutgoingRoomKeyRequest.m */; }; 32FCAB4D19E578860049C555 /* MXRestClientTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 32FCAB4C19E578860049C555 /* MXRestClientTests.m */; }; @@ -672,14 +599,6 @@ 3A4BB663291D93EA006F7585 /* MXRoomEventFilterUnitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A4BB661291D93EA006F7585 /* MXRoomEventFilterUnitTests.swift */; }; 3A5787A528982D4600A0D8A8 /* MXBreadcrumbsRoomListDataFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A5787A428982D4600A0D8A8 /* MXBreadcrumbsRoomListDataFetcher.swift */; }; 3A5787A628982D4600A0D8A8 /* MXBreadcrumbsRoomListDataFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A5787A428982D4600A0D8A8 /* MXBreadcrumbsRoomListDataFetcher.swift */; }; - 3A59A49D25A7A16F00DDA1FC /* MXOlmOutboundGroupSession.h in Headers */ = {isa = PBXBuildFile; fileRef = 3A59A49B25A7A16E00DDA1FC /* MXOlmOutboundGroupSession.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 3A59A49E25A7A16F00DDA1FC /* MXOlmOutboundGroupSession.h in Headers */ = {isa = PBXBuildFile; fileRef = 3A59A49B25A7A16E00DDA1FC /* MXOlmOutboundGroupSession.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 3A59A49F25A7A16F00DDA1FC /* MXOlmOutboundGroupSession.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A59A49C25A7A16F00DDA1FC /* MXOlmOutboundGroupSession.m */; }; - 3A59A4A025A7A16F00DDA1FC /* MXOlmOutboundGroupSession.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A59A49C25A7A16F00DDA1FC /* MXOlmOutboundGroupSession.m */; }; - 3A59A52925A7B1B000DDA1FC /* MXOutboundSessionInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = 3A59A52725A7B1B000DDA1FC /* MXOutboundSessionInfo.h */; }; - 3A59A52A25A7B1B000DDA1FC /* MXOutboundSessionInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = 3A59A52725A7B1B000DDA1FC /* MXOutboundSessionInfo.h */; }; - 3A59A52B25A7B1B000DDA1FC /* MXOutboundSessionInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A59A52825A7B1B000DDA1FC /* MXOutboundSessionInfo.m */; }; - 3A59A52C25A7B1B000DDA1FC /* MXOutboundSessionInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A59A52825A7B1B000DDA1FC /* MXOutboundSessionInfo.m */; }; 3A7509BB26FC61DF00B85773 /* MXSpaceNotificationCounter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A7509BA26FC61DF00B85773 /* MXSpaceNotificationCounter.swift */; }; 3A7509BC26FC61DF00B85773 /* MXSpaceNotificationCounter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A7509BA26FC61DF00B85773 /* MXSpaceNotificationCounter.swift */; }; 3A858DDA2750EE3F006322C1 /* MXHomeserverCapabilitiesService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A858DD92750EE3F006322C1 /* MXHomeserverCapabilitiesService.swift */; }; @@ -744,18 +663,34 @@ A0A2397A295202930001F722 /* MXAggregatedPollsUpdater.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0A23978295202930001F722 /* MXAggregatedPollsUpdater.swift */; }; A0B1217F295306F700E704C2 /* MXAggregatedPollsUpdaterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0B1217E295306F700E704C2 /* MXAggregatedPollsUpdaterTests.swift */; }; A0B12180295306F700E704C2 /* MXAggregatedPollsUpdaterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0B1217E295306F700E704C2 /* MXAggregatedPollsUpdaterTests.swift */; }; + A71E55322CA6FEBD00B06B17 /* MXKeyBackupPassword.h in Headers */ = {isa = PBXBuildFile; fileRef = A71E55302CA6FEBC00B06B17 /* MXKeyBackupPassword.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A71E55332CA6FEBD00B06B17 /* MXKeyBackupPassword.h in Headers */ = {isa = PBXBuildFile; fileRef = A71E55302CA6FEBC00B06B17 /* MXKeyBackupPassword.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A71E55342CA6FEBD00B06B17 /* MXKeyBackupPassword.m in Sources */ = {isa = PBXBuildFile; fileRef = A71E55312CA6FEBC00B06B17 /* MXKeyBackupPassword.m */; }; + A71E55352CA6FEBD00B06B17 /* MXKeyBackupPassword.m in Sources */ = {isa = PBXBuildFile; fileRef = A71E55312CA6FEBC00B06B17 /* MXKeyBackupPassword.m */; }; + A759E23D2C98EE7D002429A8 /* MXCrypto.m in Sources */ = {isa = PBXBuildFile; fileRef = A759E23C2C98EE7D002429A8 /* MXCrypto.m */; }; + A759E23E2C98EE7D002429A8 /* MXCrypto.m in Sources */ = {isa = PBXBuildFile; fileRef = A759E23C2C98EE7D002429A8 /* MXCrypto.m */; }; A75CAD692A9796DE00F06072 /* MXWellKnownAuthentication.m in Sources */ = {isa = PBXBuildFile; fileRef = A75CAD682A9796DE00F06072 /* MXWellKnownAuthentication.m */; }; A75CAD6A2A9796DE00F06072 /* MXWellKnownAuthentication.m in Sources */ = {isa = PBXBuildFile; fileRef = A75CAD682A9796DE00F06072 /* MXWellKnownAuthentication.m */; }; A75CAD6C2A979AC500F06072 /* MXWellKnownAuthentication.h in Headers */ = {isa = PBXBuildFile; fileRef = A75CAD6B2A97970500F06072 /* MXWellKnownAuthentication.h */; settings = {ATTRIBUTES = (Public, ); }; }; A75CAD6D2A979AC600F06072 /* MXWellKnownAuthentication.h in Headers */ = {isa = PBXBuildFile; fileRef = A75CAD6B2A97970500F06072 /* MXWellKnownAuthentication.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A777E5752C98D6F100B39397 /* MXKeyVerificationManager.m in Sources */ = {isa = PBXBuildFile; fileRef = A777E5742C98D6F100B39397 /* MXKeyVerificationManager.m */; }; + A777E5762C98D6F100B39397 /* MXKeyVerificationManager.m in Sources */ = {isa = PBXBuildFile; fileRef = A777E5742C98D6F100B39397 /* MXKeyVerificationManager.m */; }; + A777E5782C98D79D00B39397 /* MXQRCodeTransaction.m in Sources */ = {isa = PBXBuildFile; fileRef = A777E5772C98D79D00B39397 /* MXQRCodeTransaction.m */; }; + A777E5792C98D79D00B39397 /* MXQRCodeTransaction.m in Sources */ = {isa = PBXBuildFile; fileRef = A777E5772C98D79D00B39397 /* MXQRCodeTransaction.m */; }; + A777E57B2C98D82500B39397 /* MXKeyVerificationRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = A777E57A2C98D82500B39397 /* MXKeyVerificationRequest.m */; }; + A777E57C2C98D82500B39397 /* MXKeyVerificationRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = A777E57A2C98D82500B39397 /* MXKeyVerificationRequest.m */; }; + A777E57E2C98D8A500B39397 /* MXKeyVerificationTransaction.m in Sources */ = {isa = PBXBuildFile; fileRef = A777E57D2C98D8A500B39397 /* MXKeyVerificationTransaction.m */; }; + A777E57F2C98D8A500B39397 /* MXKeyVerificationTransaction.m in Sources */ = {isa = PBXBuildFile; fileRef = A777E57D2C98D8A500B39397 /* MXKeyVerificationTransaction.m */; }; + A777E5842C98DF1900B39397 /* MXCrossSigning.m in Sources */ = {isa = PBXBuildFile; fileRef = A777E5832C98DF1900B39397 /* MXCrossSigning.m */; }; + A777E5852C98DF1900B39397 /* MXCrossSigning.m in Sources */ = {isa = PBXBuildFile; fileRef = A777E5832C98DF1900B39397 /* MXCrossSigning.m */; }; + A777E58D2C98E21800B39397 /* MXCryptoVersion.h in Headers */ = {isa = PBXBuildFile; fileRef = A777E58C2C98E21800B39397 /* MXCryptoVersion.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A777E58E2C98E21800B39397 /* MXCryptoVersion.h in Headers */ = {isa = PBXBuildFile; fileRef = A777E58C2C98E21800B39397 /* MXCryptoVersion.h */; settings = {ATTRIBUTES = (Public, ); }; }; A780624E27B2CE74005780C0 /* FileManager+Backup.swift in Sources */ = {isa = PBXBuildFile; fileRef = A780624C27B2CE74005780C0 /* FileManager+Backup.swift */; }; A780624F27B2CE74005780C0 /* FileManager+Backup.swift in Sources */ = {isa = PBXBuildFile; fileRef = A780624C27B2CE74005780C0 /* FileManager+Backup.swift */; }; A780625027B2CE74005780C0 /* FileManager+AppGroupContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = A780624D27B2CE74005780C0 /* FileManager+AppGroupContainer.swift */; }; A780625127B2CE74005780C0 /* FileManager+AppGroupContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = A780624D27B2CE74005780C0 /* FileManager+AppGroupContainer.swift */; }; A7BB11982C933677002F7FA5 /* PKMessageWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7BB11972C933677002F7FA5 /* PKMessageWrapper.swift */; }; A7BB11992C933677002F7FA5 /* PKMessageWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7BB11972C933677002F7FA5 /* PKMessageWrapper.swift */; }; - A816247C25F60C7700A46F05 /* MXDeviceListOperationsPoolUnitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A816247B25F60C7700A46F05 /* MXDeviceListOperationsPoolUnitTests.swift */; }; - A816248525F60D0300A46F05 /* MXDeviceListOperationsPoolUnitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A816247B25F60C7700A46F05 /* MXDeviceListOperationsPoolUnitTests.swift */; }; B105CD9D261E0B70006EB204 /* MXSpaceChildrenSummary.swift in Sources */ = {isa = PBXBuildFile; fileRef = B105CD9C261E0B70006EB204 /* MXSpaceChildrenSummary.swift */; }; B105CD9E261E0B70006EB204 /* MXSpaceChildrenSummary.swift in Sources */ = {isa = PBXBuildFile; fileRef = B105CD9C261E0B70006EB204 /* MXSpaceChildrenSummary.swift */; }; B105CDD6261F54C8006EB204 /* MXSpaceChildContent.h in Headers */ = {isa = PBXBuildFile; fileRef = B105CDD4261F54C8006EB204 /* MXSpaceChildContent.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -849,7 +784,6 @@ B14EECE82577F76100448735 /* MXLoginSSOFlow.m in Sources */ = {isa = PBXBuildFile; fileRef = B14EECE42577F76100448735 /* MXLoginSSOFlow.m */; }; B14EECEE2578FE3F00448735 /* MXAuthenticationSessionUnitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B14EECED2578FE3F00448735 /* MXAuthenticationSessionUnitTests.swift */; }; B14EECEF2578FE3F00448735 /* MXAuthenticationSessionUnitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B14EECED2578FE3F00448735 /* MXAuthenticationSessionUnitTests.swift */; }; - B14EF1CA2397E90400758AF0 /* MXOlmInboundGroupSession.m in Sources */ = {isa = PBXBuildFile; fileRef = 32A1513D1DAF768D00400192 /* MXOlmInboundGroupSession.m */; }; B14EF1CB2397E90400758AF0 /* MXRoomAccountData.m in Sources */ = {isa = PBXBuildFile; fileRef = 32481A831C03572900782AD3 /* MXRoomAccountData.m */; }; B14EF1CC2397E90400758AF0 /* MXEventAnnotationChunk.m in Sources */ = {isa = PBXBuildFile; fileRef = 327E9AD52284803100A98BC1 /* MXEventAnnotationChunk.m */; }; B14EF1CD2397E90400758AF0 /* MXRealmEventScanStore.m in Sources */ = {isa = PBXBuildFile; fileRef = B146D4F921A5BF7100D8C2C6 /* MXRealmEventScanStore.m */; }; @@ -871,7 +805,6 @@ B14EF1DD2397E90400758AF0 /* MXEventsByTypesEnumeratorOnArray.m in Sources */ = {isa = PBXBuildFile; fileRef = 320BBF3D1D6C81550079890E /* MXEventsByTypesEnumeratorOnArray.m */; }; B14EF1DE2397E90400758AF0 /* MXRealmAggregationsMapper.m in Sources */ = {isa = PBXBuildFile; fileRef = 3213301C228B190F0070BA9B /* MXRealmAggregationsMapper.m */; }; B14EF1DF2397E90400758AF0 /* MXAggregatedEditsUpdater.m in Sources */ = {isa = PBXBuildFile; fileRef = 32792BDB2296B90A00F4FC9D /* MXAggregatedEditsUpdater.m */; }; - B14EF1E02397E90400758AF0 /* MXRealmCryptoStore.m in Sources */ = {isa = PBXBuildFile; fileRef = 3259CD521DF860C300186944 /* MXRealmCryptoStore.m */; }; B14EF1E12397E90400758AF0 /* MXRoomSummary.m in Sources */ = {isa = PBXBuildFile; fileRef = 321B413E1E09937E009EEEC7 /* MXRoomSummary.m */; }; B14EF1E22397E90400758AF0 /* MXPushRuleRoomMemberCountConditionChecker.m in Sources */ = {isa = PBXBuildFile; fileRef = 32CAB1061A91EA34008C5BB9 /* MXPushRuleRoomMemberCountConditionChecker.m */; }; B14EF1E32397E90400758AF0 /* MXCall.m in Sources */ = {isa = PBXBuildFile; fileRef = 3245A74D1AF7B2930001D8A7 /* MXCall.m */; }; @@ -883,7 +816,6 @@ B14EF1E92397E90400758AF0 /* MXRealmMediaScanMapper.m in Sources */ = {isa = PBXBuildFile; fileRef = B146D4DE21A5AEF100D8C2C6 /* MXRealmMediaScanMapper.m */; }; B14EF1EA2397E90400758AF0 /* MXRealmMediaScan.m in Sources */ = {isa = PBXBuildFile; fileRef = B146D4DD21A5AEF100D8C2C6 /* MXRealmMediaScan.m */; }; B14EF1EB2397E90400758AF0 /* MXRoomOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 32C235711F827F3800E38FC5 /* MXRoomOperation.m */; }; - B14EF1EC2397E90400758AF0 /* MXCryptoAlgorithms.m in Sources */ = {isa = PBXBuildFile; fileRef = 322A51C21D9AC8FE00C8536D /* MXCryptoAlgorithms.m */; }; B14EF1ED2397E90400758AF0 /* MXIdentityServerHashDetails.m in Sources */ = {isa = PBXBuildFile; fileRef = B182B08623167A640057972E /* MXIdentityServerHashDetails.m */; }; B14EF1EE2397E90400758AF0 /* MXRoomMember.m in Sources */ = {isa = PBXBuildFile; fileRef = 329FB1741A0A3A1600A5E88E /* MXRoomMember.m */; }; B14EF1EF2397E90400758AF0 /* MXIdentityService.m in Sources */ = {isa = PBXBuildFile; fileRef = B1136961230AC9D900E2B2FA /* MXIdentityService.m */; }; @@ -905,7 +837,6 @@ B14EF2002397E90400758AF0 /* MXKeyBackupVersion.m in Sources */ = {isa = PBXBuildFile; fileRef = 32BBAE692178E99100D85F46 /* MXKeyBackupVersion.m */; }; B14EF2012397E90400758AF0 /* MXRoomEventTimeline.m in Sources */ = {isa = PBXBuildFile; fileRef = 326056841C76FDF1009D44AD /* MXRoomEventTimeline.m */; }; B14EF2022397E90400758AF0 /* MXEventUnsignedData.m in Sources */ = {isa = PBXBuildFile; fileRef = 327E9ABB2284521C00A98BC1 /* MXEventUnsignedData.m */; }; - B14EF2032397E90400758AF0 /* MXMegolmEncryption.m in Sources */ = {isa = PBXBuildFile; fileRef = 32A151381DAD292400400192 /* MXMegolmEncryption.m */; }; B14EF2042397E90400758AF0 /* MXIncomingRoomKeyRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 32F945F31FAB83D900622468 /* MXIncomingRoomKeyRequest.m */; }; B14EF2052397E90400758AF0 /* MX3PidAddManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32D2CC08234336D6002BD8CA /* MX3PidAddManager.swift */; }; B14EF2062397E90400758AF0 /* MXGroup.m in Sources */ = {isa = PBXBuildFile; fileRef = F0173EAB1FCF0E8900B5F6A3 /* MXGroup.m */; }; @@ -925,7 +856,6 @@ B14EF2142397E90400758AF0 /* MXPeekingRoomSummary.m in Sources */ = {isa = PBXBuildFile; fileRef = 3293C6FF214BBA4F009B3DDB /* MXPeekingRoomSummary.m */; }; B14EF2152397E90400758AF0 /* MXRoom.swift in Sources */ = {isa = PBXBuildFile; fileRef = C602B58B1F2268F700B67D87 /* MXRoom.swift */; }; B14EF2162397E90400758AF0 /* MXOlmSession.m in Sources */ = {isa = PBXBuildFile; fileRef = 32E402B821C957D2004E87A6 /* MXOlmSession.m */; }; - B14EF2172397E90400758AF0 /* MXMegolmDecryption.m in Sources */ = {isa = PBXBuildFile; fileRef = 32A151251DABB0CB00400192 /* MXMegolmDecryption.m */; }; B14EF2182397E90400758AF0 /* MXEmojiRepresentation.m in Sources */ = {isa = PBXBuildFile; fileRef = 321CFDFC2254E8C4004D31DF /* MXEmojiRepresentation.m */; }; B14EF2192397E90400758AF0 /* MXEventEditsListener.m in Sources */ = {isa = PBXBuildFile; fileRef = B10AFB4622AA8A8D0092E6AF /* MXEventEditsListener.m */; }; B14EF21A2397E90400758AF0 /* MXSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6D5D60D1E4FBD2900706C0F /* MXSession.swift */; }; @@ -939,9 +869,6 @@ B14EF2222397E90400758AF0 /* MXMediaScan.m in Sources */ = {isa = PBXBuildFile; fileRef = B146D47621A5950800D8C2C6 /* MXMediaScan.m */; }; B14EF2232397E90400758AF0 /* MXEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6F935861E5B3BE600FC34BF /* MXEvent.swift */; }; B14EF2242397E90400758AF0 /* MXEventScan.m in Sources */ = {isa = PBXBuildFile; fileRef = B146D47E21A59E2300D8C2C6 /* MXEventScan.m */; }; - B14EF2252397E90400758AF0 /* MXOutgoingSASTransaction.m in Sources */ = {isa = PBXBuildFile; fileRef = 329E808B224E2E1B00A48C3A /* MXOutgoingSASTransaction.m */; }; - B14EF2262397E90400758AF0 /* MXOlmEncryption.m in Sources */ = {isa = PBXBuildFile; fileRef = 327187881DA7DCE50071C818 /* MXOlmEncryption.m */; }; - B14EF2272397E90400758AF0 /* MXOlmDecryption.m in Sources */ = {isa = PBXBuildFile; fileRef = 327187841DA7D0220071C818 /* MXOlmDecryption.m */; }; B14EF2282397E90400758AF0 /* MXNoStore.m in Sources */ = {isa = PBXBuildFile; fileRef = 32114A8E1A262ECB00FF2EC4 /* MXNoStore.m */; }; B14EF2292397E90400758AF0 /* MXRealmEventScanMapper.m in Sources */ = {isa = PBXBuildFile; fileRef = B146D4EC21A5AF7F00D8C2C6 /* MXRealmEventScanMapper.m */; }; B14EF22A2397E90400758AF0 /* MXReplyEventFormattedBodyParts.m in Sources */ = {isa = PBXBuildFile; fileRef = B11BD45822CB58850064D8B0 /* MXReplyEventFormattedBodyParts.m */; }; @@ -950,13 +877,11 @@ B14EF22D2397E90400758AF0 /* MXRealmReactionCount.m in Sources */ = {isa = PBXBuildFile; fileRef = 32133018228B010C0070BA9B /* MXRealmReactionCount.m */; }; B14EF22E2397E90400758AF0 /* MXCryptoTools.m in Sources */ = {isa = PBXBuildFile; fileRef = 3250E7C9220C913900736CB5 /* MXCryptoTools.m */; }; B14EF22F2397E90400758AF0 /* (null) in Sources */ = {isa = PBXBuildFile; }; - B14EF2302397E90400758AF0 /* MXDeviceListOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 322691311E5EF77D00966A6E /* MXDeviceListOperation.m */; }; B14EF2312397E90400758AF0 /* MX3PidAddSession.m in Sources */ = {isa = PBXBuildFile; fileRef = 32D2CBFF23422462002BD8CA /* MX3PidAddSession.m */; }; B14EF2322397E90400758AF0 /* MXBugReportRestClient.m in Sources */ = {isa = PBXBuildFile; fileRef = 3283F7771EAF30F700C1688C /* MXBugReportRestClient.m */; }; B14EF2342397E90400758AF0 /* MXCallKitAdapter.m in Sources */ = {isa = PBXBuildFile; fileRef = 9274AFE71EE580240009BEB6 /* MXCallKitAdapter.m */; }; B14EF2352397E90400758AF0 /* MXRoomPowerLevels.swift in Sources */ = {isa = PBXBuildFile; fileRef = C63E78AF1F26588000AC692F /* MXRoomPowerLevels.swift */; }; B14EF2362397E90400758AF0 /* MXAggregations.m in Sources */ = {isa = PBXBuildFile; fileRef = 327E9AEE2289C61100A98BC1 /* MXAggregations.m */; }; - B14EF2372397E90400758AF0 /* MXKeyVerificationManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 3252DCAD224BE5D40032264F /* MXKeyVerificationManager.m */; }; B14EF2382397E90400758AF0 /* MXEvent.m in Sources */ = {isa = PBXBuildFile; fileRef = 323E0C5A1A306D7A00A31D73 /* MXEvent.m */; }; B14EF2392397E90400758AF0 /* MXMediaManager.m in Sources */ = {isa = PBXBuildFile; fileRef = F03EF4FD1DF014D9009DF592 /* MXMediaManager.m */; }; B14EF23A2397E90400758AF0 /* MXHTTPOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 32CAB10A1A925B41008C5BB9 /* MXHTTPOperation.m */; }; @@ -968,7 +893,6 @@ B14EF2402397E90400758AF0 /* MXSessionEventListener.m in Sources */ = {isa = PBXBuildFile; fileRef = 3220094419EFBF30008DE41D /* MXSessionEventListener.m */; }; B14EF2412397E90400758AF0 /* MXRoomFilter.m in Sources */ = {isa = PBXBuildFile; fileRef = 32A31BC720D401FC005916C7 /* MXRoomFilter.m */; }; B14EF2422397E90400758AF0 /* MXDeviceInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = 32A151411DAF7C0C00400192 /* MXDeviceInfo.m */; }; - B14EF2432397E90400758AF0 /* MXIncomingSASTransaction.m in Sources */ = {isa = PBXBuildFile; fileRef = 321CFDE922525DEE004D31DF /* MXIncomingSASTransaction.m */; }; B14EF2442397E90400758AF0 /* NSObject+sortedKeys.m in Sources */ = {isa = PBXBuildFile; fileRef = 32A1515A1DB525DA00400192 /* NSObject+sortedKeys.m */; }; B14EF2452397E90400758AF0 /* MXAggregatedReactionsUpdater.m in Sources */ = {isa = PBXBuildFile; fileRef = 32792BD32295A86600F4FC9D /* MXAggregatedReactionsUpdater.m */; }; B14EF2462397E90400758AF0 /* MXRoomMembers.m in Sources */ = {isa = PBXBuildFile; fileRef = 32618E7A20EFA45B00E1D2EA /* MXRoomMembers.m */; }; @@ -980,7 +904,6 @@ B14EF24D2397E90400758AF0 /* MXOutgoingRoomKeyRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 32FA10C91FA1C9F700E54233 /* MXOutgoingRoomKeyRequest.m */; }; B14EF24E2397E90400758AF0 /* MXAllowedCertificates.m in Sources */ = {isa = PBXBuildFile; fileRef = 32322A4A1E575F65005DD155 /* MXAllowedCertificates.m */; }; B14EF24F2397E90400758AF0 /* MXLRUCache.m in Sources */ = {isa = PBXBuildFile; fileRef = F03EF5031DF01596009DF592 /* MXLRUCache.m */; }; - B14EF2502397E90400758AF0 /* MXIncomingRoomKeyRequestManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 32A30B171FB4813400C8309E /* MXIncomingRoomKeyRequestManager.m */; }; B14EF2512397E90400758AF0 /* MXRoomEventFilter.m in Sources */ = {isa = PBXBuildFile; fileRef = 323F3F9120D3F0C700D26D6A /* MXRoomEventFilter.m */; }; B14EF2522397E90400758AF0 /* MXLoginPolicyData.m in Sources */ = {isa = PBXBuildFile; fileRef = 3275FD9721A6B53300B9C13D /* MXLoginPolicyData.m */; }; B14EF2532397E90400758AF0 /* MXPushRuleSenderNotificationPermissionConditionChecker.m in Sources */ = {isa = PBXBuildFile; fileRef = 3240969C1F9F751600DBA607 /* MXPushRuleSenderNotificationPermissionConditionChecker.m */; }; @@ -1015,7 +938,6 @@ B14EF2712397E90400758AF0 /* MXSASTransaction.m in Sources */ = {isa = PBXBuildFile; fileRef = 321CFDE522525A49004D31DF /* MXSASTransaction.m */; }; B14EF2722397E90400758AF0 /* MXDiscoveredClientConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = 32720D99222EAA6F0086FFF5 /* MXDiscoveredClientConfig.m */; }; B14EF2732397E90400758AF0 /* MXRealmEventScan.m in Sources */ = {isa = PBXBuildFile; fileRef = B146D4ED21A5AF7F00D8C2C6 /* MXRealmEventScan.m */; }; - B14EF2742397E90400758AF0 /* MXDeviceListOperationsPool.m in Sources */ = {isa = PBXBuildFile; fileRef = 322691351E5EFF8700966A6E /* MXDeviceListOperationsPool.m */; }; B14EF2752397E90400758AF0 /* MXResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6F9357E1E5B3ACA00FC34BF /* MXResponse.swift */; }; B14EF2772397E90400758AF0 /* MXDecryptionResult.m in Sources */ = {isa = PBXBuildFile; fileRef = 32F9FA7C1DBA0CF0009D98A6 /* MXDecryptionResult.m */; }; B14EF2782397E90400758AF0 /* MXTransactionCancelCode.m in Sources */ = {isa = PBXBuildFile; fileRef = 321CFDF82254E720004D31DF /* MXTransactionCancelCode.m */; }; @@ -1024,31 +946,24 @@ B14EF27B2397E90400758AF0 /* MXOlmSessionResult.m in Sources */ = {isa = PBXBuildFile; fileRef = 32A1514D1DAF897600400192 /* MXOlmSessionResult.m */; }; B14EF27C2397E90400758AF0 /* MXRestClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6F9357B1E5B39CA00FC34BF /* MXRestClient.swift */; }; B14EF27D2397E90400758AF0 /* MXKeyBackup.m in Sources */ = {isa = PBXBuildFile; fileRef = 32BBAE732179CF4000D85F46 /* MXKeyBackup.m */; }; - B14EF27E2397E90400758AF0 /* MXCrypto.m in Sources */ = {isa = PBXBuildFile; fileRef = 322A51B51D9AB15900C8536D /* MXCrypto.m */; }; B14EF27F2397E90400758AF0 /* MXRoomPredecessorInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = B17982F32119E4A1001FD722 /* MXRoomPredecessorInfo.m */; }; B14EF2802397E90400758AF0 /* MXServiceTerms.m in Sources */ = {isa = PBXBuildFile; fileRef = 3294FD9A22F321B0007F1E60 /* MXServiceTerms.m */; }; B14EF2812397E90400758AF0 /* MXNotificationCenter.m in Sources */ = {isa = PBXBuildFile; fileRef = 32DC15CE1A8CF7AE006F9AD3 /* MXNotificationCenter.m */; }; - B14EF2822397E90400758AF0 /* MXDeviceList.m in Sources */ = {isa = PBXBuildFile; fileRef = 32637ED31E5B00400011E20D /* MXDeviceList.m */; }; B14EF2832397E90400758AF0 /* MXRoomCreateContent.m in Sources */ = {isa = PBXBuildFile; fileRef = B17982F22119E4A1001FD722 /* MXRoomCreateContent.m */; }; B14EF2842397E90400758AF0 /* MXUIKitBackgroundModeHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 32A9E8231EF4026E0081358A /* MXUIKitBackgroundModeHandler.m */; }; B14EF2852397E90400758AF0 /* (null) in Sources */ = {isa = PBXBuildFile; }; B14EF2862397E90400758AF0 /* MXRealmMediaScanStore.m in Sources */ = {isa = PBXBuildFile; fileRef = B146D4F521A5BB9F00D8C2C6 /* MXRealmMediaScanStore.m */; }; B14EF2872397E90400758AF0 /* MXPusherData.m in Sources */ = {isa = PBXBuildFile; fileRef = 32999DE222DCD1AD004FF987 /* MXPusherData.m */; }; - B14EF2882397E90400758AF0 /* MXOlmDevice.m in Sources */ = {isa = PBXBuildFile; fileRef = 322A51C61D9BBD3C00C8536D /* MXOlmDevice.m */; }; - B14EF2892397E90400758AF0 /* MXKeyBackupPassword.m in Sources */ = {isa = PBXBuildFile; fileRef = 32442FB021EDD21300D2411B /* MXKeyBackupPassword.m */; }; B14EF28A2397E90400758AF0 /* MXTools.m in Sources */ = {isa = PBXBuildFile; fileRef = 329FB1781A0A74B100A5E88E /* MXTools.m */; }; B14EF28B2397E90400758AF0 /* MXRoomSummaryUpdater.m in Sources */ = {isa = PBXBuildFile; fileRef = 329D3E611E251027002E2F1E /* MXRoomSummaryUpdater.m */; }; B14EF28C2397E90400758AF0 /* MXScanRealmInMemoryProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = B146D4C721A5A44E00D8C2C6 /* MXScanRealmInMemoryProvider.m */; }; B14EF28D2397E90400758AF0 /* MXMegolmSessionData.m in Sources */ = {isa = PBXBuildFile; fileRef = 324BE46B1E422766008D99D4 /* MXMegolmSessionData.m */; }; - B14EF28E2397E90400758AF0 /* MXKeyVerificationTransaction.m in Sources */ = {isa = PBXBuildFile; fileRef = 329E8088224E261600A48C3A /* MXKeyVerificationTransaction.m */; }; B14EF28F2397E90400758AF0 /* MXEventTimeline.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6F935841E5B3BE600FC34BF /* MXEventTimeline.swift */; }; B14EF2902397E90400758AF0 /* MXJSONModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6F935871E5B3BE600FC34BF /* MXJSONModels.swift */; }; - B14EF2912397E90400758AF0 /* MXOutgoingRoomKeyRequestManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 32FA10C01FA1C9EE00E54233 /* MXOutgoingRoomKeyRequestManager.m */; }; B14EF2922397E90400758AF0 /* MXWellKnown.m in Sources */ = {isa = PBXBuildFile; fileRef = 323547D22226D3F500F15F94 /* MXWellKnown.m */; }; B14EF2932397E90400758AF0 /* MXRestClient.m in Sources */ = {isa = PBXBuildFile; fileRef = 320DFDD519DD99B60068622A /* MXRestClient.m */; }; B14EF2972397E90400758AF0 /* MXCrypto.h in Headers */ = {isa = PBXBuildFile; fileRef = 322A51B41D9AB15900C8536D /* MXCrypto.h */; settings = {ATTRIBUTES = (Public, ); }; }; B14EF2982397E90400758AF0 /* MXNoStore.h in Headers */ = {isa = PBXBuildFile; fileRef = 32114A8D1A262ECB00FF2EC4 /* MXNoStore.h */; settings = {ATTRIBUTES = (Public, ); }; }; - B14EF29A2397E90400758AF0 /* MXRealmCryptoStore.h in Headers */ = {isa = PBXBuildFile; fileRef = 3259CD511DF860C300186944 /* MXRealmCryptoStore.h */; settings = {ATTRIBUTES = (Public, ); }; }; B14EF29B2397E90400758AF0 /* MXMemoryRoomStore.h in Headers */ = {isa = PBXBuildFile; fileRef = 32D7767F1A27877300FC4AA2 /* MXMemoryRoomStore.h */; settings = {ATTRIBUTES = (Public, ); }; }; B14EF29C2397E90400758AF0 /* MXRealmMediaScan.h in Headers */ = {isa = PBXBuildFile; fileRef = B146D4E021A5AEF100D8C2C6 /* MXRealmMediaScan.h */; }; B14EF29D2397E90400758AF0 /* MXRealmEventScan.h in Headers */ = {isa = PBXBuildFile; fileRef = B146D4E921A5AF7F00D8C2C6 /* MXRealmEventScan.h */; }; @@ -1073,9 +988,7 @@ B14EF2B22397E90400758AF0 /* MXFileStoreMetaData.h in Headers */ = {isa = PBXBuildFile; fileRef = 32CE6FB61A409B1F00317F1E /* MXFileStoreMetaData.h */; }; B14EF2B32397E90400758AF0 /* MXRoomFilter.h in Headers */ = {isa = PBXBuildFile; fileRef = 32A31BC620D401FC005916C7 /* MXRoomFilter.h */; settings = {ATTRIBUTES = (Public, ); }; }; B14EF2B42397E90400758AF0 /* MXAntivirusScanStatus.h in Headers */ = {isa = PBXBuildFile; fileRef = B146D47321A5945800D8C2C6 /* MXAntivirusScanStatus.h */; settings = {ATTRIBUTES = (Public, ); }; }; - B14EF2B52397E90400758AF0 /* MXDeviceListOperationsPool.h in Headers */ = {isa = PBXBuildFile; fileRef = 322691341E5EFF8700966A6E /* MXDeviceListOperationsPool.h */; }; B14EF2B62397E90400758AF0 /* MXJSONModel.h in Headers */ = {isa = PBXBuildFile; fileRef = 3281E8B319E42DFE00976E1A /* MXJSONModel.h */; settings = {ATTRIBUTES = (Public, ); }; }; - B14EF2B72397E90400758AF0 /* MXCryptoStore.h in Headers */ = {isa = PBXBuildFile; fileRef = 3284A59D1DB7C00600A09972 /* MXCryptoStore.h */; settings = {ATTRIBUTES = (Public, ); }; }; B14EF2B82397E90400758AF0 /* MXReactionCount.h in Headers */ = {isa = PBXBuildFile; fileRef = 327E9AF42289D53800A98BC1 /* MXReactionCount.h */; settings = {ATTRIBUTES = (Public, ); }; }; B14EF2B92397E90400758AF0 /* MXMediaScanStore.h in Headers */ = {isa = PBXBuildFile; fileRef = B146D49A21A5A04200D8C2C6 /* MXMediaScanStore.h */; }; B14EF2BA2397E90400758AF0 /* MXOutgoingRoomKeyRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = 32FA10C81FA1C9F700E54233 /* MXOutgoingRoomKeyRequest.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -1108,19 +1021,15 @@ B14EF2D72397E90400758AF0 /* MXAggregationsStore.h in Headers */ = {isa = PBXBuildFile; fileRef = 327E9AFA228AC22800A98BC1 /* MXAggregationsStore.h */; }; B14EF2D82397E90400758AF0 /* MXCredentials.h in Headers */ = {isa = PBXBuildFile; fileRef = 323547DA2226FC5700F15F94 /* MXCredentials.h */; settings = {ATTRIBUTES = (Public, ); }; }; B14EF2D92397E90400758AF0 /* MXEncryptedContentKey.h in Headers */ = {isa = PBXBuildFile; fileRef = 021AFBA22179E91800742B2C /* MXEncryptedContentKey.h */; settings = {ATTRIBUTES = (Public, ); }; }; - B14EF2DB2397E90400758AF0 /* MXOlmEncryption.h in Headers */ = {isa = PBXBuildFile; fileRef = 327187871DA7DCE50071C818 /* MXOlmEncryption.h */; }; B14EF2DC2397E90400758AF0 /* MXOlmSessionResult.h in Headers */ = {isa = PBXBuildFile; fileRef = 32A1514C1DAF897600400192 /* MXOlmSessionResult.h */; }; B14EF2DD2397E90400758AF0 /* MXEventsEnumerator.h in Headers */ = {isa = PBXBuildFile; fileRef = 320BBF3B1D6C7D9D0079890E /* MXEventsEnumerator.h */; settings = {ATTRIBUTES = (Public, ); }; }; - B14EF2DE2397E90400758AF0 /* MXDeviceList.h in Headers */ = {isa = PBXBuildFile; fileRef = 32637ED21E5B00400011E20D /* MXDeviceList.h */; }; B14EF2DF2397E90400758AF0 /* MXDecryptionResult.h in Headers */ = {isa = PBXBuildFile; fileRef = 32F9FA7B1DBA0CF0009D98A6 /* MXDecryptionResult.h */; settings = {ATTRIBUTES = (Public, ); }; }; B14EF2E02397E90400758AF0 /* MXCall.h in Headers */ = {isa = PBXBuildFile; fileRef = 3245A74C1AF7B2930001D8A7 /* MXCall.h */; settings = {ATTRIBUTES = (Public, ); }; }; B14EF2E12397E90400758AF0 /* MXContentScanEncryptedBody.h in Headers */ = {isa = PBXBuildFile; fileRef = 02CAD437217DD12F0074700B /* MXContentScanEncryptedBody.h */; settings = {ATTRIBUTES = (Public, ); }; }; B14EF2E22397E90400758AF0 /* MX3PidAddManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 32D2CC0023422462002BD8CA /* MX3PidAddManager.h */; settings = {ATTRIBUTES = (Public, ); }; }; B14EF2E42397E90400758AF0 /* MXStore.h in Headers */ = {isa = PBXBuildFile; fileRef = 32114A841A262CE000FF2EC4 /* MXStore.h */; settings = {ATTRIBUTES = (Public, ); }; }; - B14EF2E62397E90400758AF0 /* MXMegolmEncryption.h in Headers */ = {isa = PBXBuildFile; fileRef = 32A151371DAD292400400192 /* MXMegolmEncryption.h */; }; B14EF2E72397E90400758AF0 /* MXLRUCache.h in Headers */ = {isa = PBXBuildFile; fileRef = F03EF5021DF01596009DF592 /* MXLRUCache.h */; settings = {ATTRIBUTES = (Public, ); }; }; B14EF2E92397E90400758AF0 /* MXTools.h in Headers */ = {isa = PBXBuildFile; fileRef = 329FB1771A0A74B100A5E88E /* MXTools.h */; settings = {ATTRIBUTES = (Public, ); }; }; - B14EF2EA2397E90400758AF0 /* MXDeviceListOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = 322691301E5EF77D00966A6E /* MXDeviceListOperation.h */; }; B14EF2EB2397E90400758AF0 /* MXRoomAccountData.h in Headers */ = {isa = PBXBuildFile; fileRef = 32481A821C03572900782AD3 /* MXRoomAccountData.h */; settings = {ATTRIBUTES = (Public, ); }; }; B14EF2EC2397E90400758AF0 /* MXEventContentRelatesTo.h in Headers */ = {isa = PBXBuildFile; fileRef = 327E9ADF2285497100A98BC1 /* MXEventContentRelatesTo.h */; settings = {ATTRIBUTES = (Public, ); }; }; B14EF2ED2397E90400758AF0 /* MXReactionCountChangeListener.h in Headers */ = {isa = PBXBuildFile; fileRef = 32133023228BFA800070BA9B /* MXReactionCountChangeListener.h */; }; @@ -1135,14 +1044,12 @@ B14EF2F62397E90400758AF0 /* MXJSONModels.h in Headers */ = {isa = PBXBuildFile; fileRef = 3281E8B519E42DFE00976E1A /* MXJSONModels.h */; settings = {ATTRIBUTES = (Public, ); }; }; B14EF2F72397E90400758AF0 /* MXRoomEventFilter.h in Headers */ = {isa = PBXBuildFile; fileRef = 323F3F9220D3F0C700D26D6A /* MXRoomEventFilter.h */; settings = {ATTRIBUTES = (Public, ); }; }; B14EF2F82397E90400758AF0 /* MXUIKitBackgroundModeHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = 32A9E8221EF4026E0081358A /* MXUIKitBackgroundModeHandler.h */; settings = {ATTRIBUTES = (Public, ); }; }; - B14EF2F92397E90400758AF0 /* MXCrypto_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 325D1C251DFECE0D0070B8BF /* MXCrypto_Private.h */; }; B14EF2FA2397E90400758AF0 /* MXEventReplace.h in Headers */ = {isa = PBXBuildFile; fileRef = B10AFB4122A970060092E6AF /* MXEventReplace.h */; settings = {ATTRIBUTES = (Public, ); }; }; B14EF2FB2397E90400758AF0 /* MXOlmSession.h in Headers */ = {isa = PBXBuildFile; fileRef = 32E402B721C957D2004E87A6 /* MXOlmSession.h */; settings = {ATTRIBUTES = (Public, ); }; }; B14EF2FC2397E90400758AF0 /* MXAggregatedReactionsUpdater.h in Headers */ = {isa = PBXBuildFile; fileRef = 32792BD22295A86600F4FC9D /* MXAggregatedReactionsUpdater.h */; }; B14EF2FD2397E90400758AF0 /* MXScanRealmInMemoryProvider.h in Headers */ = {isa = PBXBuildFile; fileRef = B146D4C521A5A44E00D8C2C6 /* MXScanRealmInMemoryProvider.h */; }; B14EF2FE2397E90400758AF0 /* MXReplyEventBodyParts.h in Headers */ = {isa = PBXBuildFile; fileRef = B11BD45222CB583E0064D8B0 /* MXReplyEventBodyParts.h */; settings = {ATTRIBUTES = (Public, ); }; }; B14EF3002397E90400758AF0 /* MXQueuedEncryption.h in Headers */ = {isa = PBXBuildFile; fileRef = 32A151501DAF8A7200400192 /* MXQueuedEncryption.h */; }; - B14EF3012397E90400758AF0 /* MXIncomingRoomKeyRequestManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 32A30B161FB4813400C8309E /* MXIncomingRoomKeyRequestManager.h */; }; B14EF3022397E90400758AF0 /* MXEncryptedAttachments.h in Headers */ = {isa = PBXBuildFile; fileRef = F03EF5061DF071D5009DF592 /* MXEncryptedAttachments.h */; settings = {ATTRIBUTES = (Public, ); }; }; B14EF3032397E90400758AF0 /* MXEventsByTypesEnumeratorOnArray.h in Headers */ = {isa = PBXBuildFile; fileRef = 320BBF3E1D6C81550079890E /* MXEventsByTypesEnumeratorOnArray.h */; settings = {ATTRIBUTES = (Public, ); }; }; B14EF3042397E90400758AF0 /* MXKeyVerificationTransaction.h in Headers */ = {isa = PBXBuildFile; fileRef = 3252DCB5224BEA610032264F /* MXKeyVerificationTransaction.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -1165,14 +1072,11 @@ B14EF3162397E90400758AF0 /* MXContentScanResult.h in Headers */ = {isa = PBXBuildFile; fileRef = 02CAD433217DD12F0074700B /* MXContentScanResult.h */; settings = {ATTRIBUTES = (Public, ); }; }; B14EF3172397E90400758AF0 /* MXRoomMembers.h in Headers */ = {isa = PBXBuildFile; fileRef = 32618E7920EFA45B00E1D2EA /* MXRoomMembers.h */; settings = {ATTRIBUTES = (Public, ); }; }; B14EF3182397E90400758AF0 /* MXPushRuleSenderNotificationPermissionConditionChecker.h in Headers */ = {isa = PBXBuildFile; fileRef = 3240969B1F9F751600DBA607 /* MXPushRuleSenderNotificationPermissionConditionChecker.h */; settings = {ATTRIBUTES = (Public, ); }; }; - B14EF3192397E90400758AF0 /* MXOutgoingSASTransaction.h in Headers */ = {isa = PBXBuildFile; fileRef = 329E808A224E2E1B00A48C3A /* MXOutgoingSASTransaction.h */; settings = {ATTRIBUTES = (Public, ); }; }; B14EF31A2397E90400758AF0 /* MXScanRealmProvider.h in Headers */ = {isa = PBXBuildFile; fileRef = B146D4C421A5A44E00D8C2C6 /* MXScanRealmProvider.h */; }; B14EF31B2397E90400758AF0 /* MXEventUnsignedData.h in Headers */ = {isa = PBXBuildFile; fileRef = 327E9ABA2284521C00A98BC1 /* MXEventUnsignedData.h */; settings = {ATTRIBUTES = (Public, ); }; }; B14EF31C2397E90400758AF0 /* MXEventEditsListener.h in Headers */ = {isa = PBXBuildFile; fileRef = B10AFB4522AA8A8D0092E6AF /* MXEventEditsListener.h */; }; B14EF31D2397E90400758AF0 /* MXEventDecryptionResult.h in Headers */ = {isa = PBXBuildFile; fileRef = 32F634A91FC5E3470054EF49 /* MXEventDecryptionResult.h */; settings = {ATTRIBUTES = (Public, ); }; }; - B14EF31E2397E90400758AF0 /* MXOlmDevice.h in Headers */ = {isa = PBXBuildFile; fileRef = 322A51C51D9BBD3C00C8536D /* MXOlmDevice.h */; settings = {ATTRIBUTES = (Public, ); }; }; B14EF31F2397E90400758AF0 /* MXPusherData.h in Headers */ = {isa = PBXBuildFile; fileRef = 32999DE122DCD1AD004FF987 /* MXPusherData.h */; settings = {ATTRIBUTES = (Public, ); }; }; - B14EF3202397E90400758AF0 /* MXKeyBackupPassword.h in Headers */ = {isa = PBXBuildFile; fileRef = 32442FAF21EDD21300D2411B /* MXKeyBackupPassword.h */; settings = {ATTRIBUTES = (Public, ); }; }; B14EF3212397E90400758AF0 /* MXRestClient.h in Headers */ = {isa = PBXBuildFile; fileRef = 320DFDD419DD99B60068622A /* MXRestClient.h */; settings = {ATTRIBUTES = (Public, ); }; }; B14EF3222397E90400758AF0 /* MXKeyVerificationManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 3252DCAC224BE5D40032264F /* MXKeyVerificationManager.h */; settings = {ATTRIBUTES = (Public, ); }; }; B14EF3232397E90400758AF0 /* MXRoomState.h in Headers */ = {isa = PBXBuildFile; fileRef = 3265CB361A14C43E00E24B2F /* MXRoomState.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -1180,7 +1084,6 @@ B14EF3252397E90400758AF0 /* MXRoomPowerLevels.h in Headers */ = {isa = PBXBuildFile; fileRef = B17982F02119E4A0001FD722 /* MXRoomPowerLevels.h */; settings = {ATTRIBUTES = (Public, ); }; }; B14EF3262397E90400758AF0 /* MXEventScanStoreDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = B146D4B121A5A21800D8C2C6 /* MXEventScanStoreDelegate.h */; }; B14EF3272397E90400758AF0 /* MXPushRuleEventMatchConditionChecker.h in Headers */ = {isa = PBXBuildFile; fileRef = 32DC15D21A8CF874006F9AD3 /* MXPushRuleEventMatchConditionChecker.h */; settings = {ATTRIBUTES = (Public, ); }; }; - B14EF3282397E90400758AF0 /* MXMegolmDecryption.h in Headers */ = {isa = PBXBuildFile; fileRef = 32A151241DABB0CB00400192 /* MXMegolmDecryption.h */; settings = {ATTRIBUTES = (Public, ); }; }; B14EF3292397E90400758AF0 /* MXAutoDiscovery.h in Headers */ = {isa = PBXBuildFile; fileRef = 32720D9A222EAA6F0086FFF5 /* MXAutoDiscovery.h */; settings = {ATTRIBUTES = (Public, ); }; }; B14EF32A2397E90400758AF0 /* MXSessionEventListener.h in Headers */ = {isa = PBXBuildFile; fileRef = 3220094319EFBF30008DE41D /* MXSessionEventListener.h */; settings = {ATTRIBUTES = (Public, ); }; }; B14EF32B2397E90400758AF0 /* MXBackgroundModeHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = 32A9E8211EF4026E0081358A /* MXBackgroundModeHandler.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -1202,15 +1105,11 @@ B14EF33B2397E90400758AF0 /* MXInvite3PID.h in Headers */ = {isa = PBXBuildFile; fileRef = F082946B1DB66C3D00CEAB63 /* MXInvite3PID.h */; settings = {ATTRIBUTES = (Public, ); }; }; B14EF33C2397E90400758AF0 /* MXFileStore.h in Headers */ = {isa = PBXBuildFile; fileRef = 3233606D1A403A0D0071A488 /* MXFileStore.h */; settings = {ATTRIBUTES = (Public, ); }; }; B14EF33D2397E90400758AF0 /* MXRealmMediaScanStore.h in Headers */ = {isa = PBXBuildFile; fileRef = B146D4F421A5BB9F00D8C2C6 /* MXRealmMediaScanStore.h */; }; - B14EF33E2397E90400758AF0 /* MXOlmInboundGroupSession.h in Headers */ = {isa = PBXBuildFile; fileRef = 32A1513C1DAF768D00400192 /* MXOlmInboundGroupSession.h */; settings = {ATTRIBUTES = (Public, ); }; }; B14EF3402397E90400758AF0 /* MXKeyBackupVersionTrust.h in Headers */ = {isa = PBXBuildFile; fileRef = 328BCB3121947BE200A976D3 /* MXKeyBackupVersionTrust.h */; settings = {ATTRIBUTES = (Public, ); }; }; - B14EF3412397E90400758AF0 /* MXOutgoingRoomKeyRequestManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 32FA10BF1FA1C9EE00E54233 /* MXOutgoingRoomKeyRequestManager.h */; }; B14EF3422397E90400758AF0 /* MXPeekingRoomSummary.h in Headers */ = {isa = PBXBuildFile; fileRef = 3293C6FE214BBA4F009B3DDB /* MXPeekingRoomSummary.h */; }; B14EF3432397E90400758AF0 /* MXRoomEventTimeline.h in Headers */ = {isa = PBXBuildFile; fileRef = 326056831C76FDF1009D44AD /* MXRoomEventTimeline.h */; }; B14EF3442397E90400758AF0 /* NSArray+MatrixSDK.h in Headers */ = {isa = PBXBuildFile; fileRef = 321CFDEC225264C4004D31DF /* NSArray+MatrixSDK.h */; }; B14EF3452397E90400758AF0 /* MXReplyEventParser.h in Headers */ = {isa = PBXBuildFile; fileRef = B11BD44622CB56790064D8B0 /* MXReplyEventParser.h */; settings = {ATTRIBUTES = (Public, ); }; }; - B14EF3462397E90400758AF0 /* MXIncomingSASTransaction.h in Headers */ = {isa = PBXBuildFile; fileRef = 321CFDE822525DED004D31DF /* MXIncomingSASTransaction.h */; settings = {ATTRIBUTES = (Public, ); }; }; - B14EF3472397E90400758AF0 /* MXCryptoAlgorithms.h in Headers */ = {isa = PBXBuildFile; fileRef = 322A51C11D9AC8FE00C8536D /* MXCryptoAlgorithms.h */; settings = {ATTRIBUTES = (Public, ); }; }; B14EF3482397E90400758AF0 /* MXIncomingRoomKeyRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = 32F945F21FAB83D900622468 /* MXIncomingRoomKeyRequest.h */; settings = {ATTRIBUTES = (Public, ); }; }; B14EF3492397E90400758AF0 /* MXMyUser.h in Headers */ = {isa = PBXBuildFile; fileRef = 327137251A24D50A00DB6757 /* MXMyUser.h */; settings = {ATTRIBUTES = (Public, ); }; }; B14EF34A2397E90400758AF0 /* MXAntivirusScanStatusFormatter.h in Headers */ = {isa = PBXBuildFile; fileRef = B146D47A21A5958400D8C2C6 /* MXAntivirusScanStatusFormatter.h */; }; @@ -1287,10 +1186,6 @@ B190AC822833FA8900D6F0C4 /* MXBeaconInfoSummaryRealmStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = B190AC802833FA8900D6F0C4 /* MXBeaconInfoSummaryRealmStore.swift */; }; B19A309C240424BD00FB6F35 /* MXQRCodeTransaction.h in Headers */ = {isa = PBXBuildFile; fileRef = B19A3099240424BD00FB6F35 /* MXQRCodeTransaction.h */; settings = {ATTRIBUTES = (Public, ); }; }; B19A309D240424BD00FB6F35 /* MXQRCodeTransaction.h in Headers */ = {isa = PBXBuildFile; fileRef = B19A3099240424BD00FB6F35 /* MXQRCodeTransaction.h */; settings = {ATTRIBUTES = (Public, ); }; }; - B19A309E240424BD00FB6F35 /* MXQRCodeTransaction_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = B19A309A240424BD00FB6F35 /* MXQRCodeTransaction_Private.h */; }; - B19A309F240424BD00FB6F35 /* MXQRCodeTransaction_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = B19A309A240424BD00FB6F35 /* MXQRCodeTransaction_Private.h */; }; - B19A30A0240424BD00FB6F35 /* MXQRCodeTransaction.m in Sources */ = {isa = PBXBuildFile; fileRef = B19A309B240424BD00FB6F35 /* MXQRCodeTransaction.m */; }; - B19A30A1240424BD00FB6F35 /* MXQRCodeTransaction.m in Sources */ = {isa = PBXBuildFile; fileRef = B19A309B240424BD00FB6F35 /* MXQRCodeTransaction.m */; }; B19A30A62404257700FB6F35 /* MXQRCodeKeyVerificationStart.m in Sources */ = {isa = PBXBuildFile; fileRef = B19A30A22404257600FB6F35 /* MXQRCodeKeyVerificationStart.m */; }; B19A30A72404257700FB6F35 /* MXQRCodeKeyVerificationStart.m in Sources */ = {isa = PBXBuildFile; fileRef = B19A30A22404257600FB6F35 /* MXQRCodeKeyVerificationStart.m */; }; B19A30A82404257700FB6F35 /* MXSASKeyVerificationStart.m in Sources */ = {isa = PBXBuildFile; fileRef = B19A30A32404257600FB6F35 /* MXSASKeyVerificationStart.m */; }; @@ -1337,16 +1232,12 @@ B1A0270126162110001AADFF /* MXSpaceChildrenResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = B1A026FE26162110001AADFF /* MXSpaceChildrenResponse.h */; settings = {ATTRIBUTES = (Public, ); }; }; B1A0270226162110001AADFF /* MXSpaceChildrenResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = B1A026FF26162110001AADFF /* MXSpaceChildrenResponse.m */; }; B1A0270326162110001AADFF /* MXSpaceChildrenResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = B1A026FF26162110001AADFF /* MXSpaceChildrenResponse.m */; }; - B1B44319283D00CA00BB26F4 /* MXMegolmDecryptionUnitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1B44318283D00CA00BB26F4 /* MXMegolmDecryptionUnitTests.swift */; }; - B1B4431A283D00CA00BB26F4 /* MXMegolmDecryptionUnitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1B44318283D00CA00BB26F4 /* MXMegolmDecryptionUnitTests.swift */; }; B1C854EE25E7B497005867D0 /* MXRoomType.h in Headers */ = {isa = PBXBuildFile; fileRef = B1C854ED25E7B492005867D0 /* MXRoomType.h */; settings = {ATTRIBUTES = (Public, ); }; }; B1C854EF25E7B498005867D0 /* MXRoomType.h in Headers */ = {isa = PBXBuildFile; fileRef = B1C854ED25E7B492005867D0 /* MXRoomType.h */; settings = {ATTRIBUTES = (Public, ); }; }; B1D50DD3288EB34900DD1AA3 /* MXBeaconInfoSummaryDeletionAllRoomListener.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1D50DD1288EB34900DD1AA3 /* MXBeaconInfoSummaryDeletionAllRoomListener.swift */; }; B1D50DD4288EB34900DD1AA3 /* MXBeaconInfoSummaryDeletionAllRoomListener.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1D50DD1288EB34900DD1AA3 /* MXBeaconInfoSummaryDeletionAllRoomListener.swift */; }; B1D50DD5288EB34900DD1AA3 /* MXBeaconInfoSummaryDeletionPerRoomListener.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1D50DD2288EB34900DD1AA3 /* MXBeaconInfoSummaryDeletionPerRoomListener.swift */; }; B1D50DD6288EB34900DD1AA3 /* MXBeaconInfoSummaryDeletionPerRoomListener.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1D50DD2288EB34900DD1AA3 /* MXBeaconInfoSummaryDeletionPerRoomListener.swift */; }; - B1DDC9D62418098200D208E3 /* MXIncomingSASTransaction_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = B1DDC9D52418098200D208E3 /* MXIncomingSASTransaction_Private.h */; }; - B1DDC9D72418098200D208E3 /* MXIncomingSASTransaction_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = B1DDC9D52418098200D208E3 /* MXIncomingSASTransaction_Private.h */; }; B1E09A132397FA950057C069 /* MatrixSDK.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B14EF36B2397E90400758AF0 /* MatrixSDK.framework */; }; B1E09A192397FCE90057C069 /* MXReplyEventParserUnitTests.m in Sources */ = {isa = PBXBuildFile; fileRef = B11BD45B22CB8ABC0064D8B0 /* MXReplyEventParserUnitTests.m */; }; B1E09A1A2397FCE90057C069 /* MXAggregatedEditsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 32792BE02296C64200F4FC9D /* MXAggregatedEditsTests.m */; }; @@ -1517,8 +1408,6 @@ EC1165CD27107F3E0089FA56 /* MXStoreRoomListDataManagerUnitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC1165CA27107F3E0089FA56 /* MXStoreRoomListDataManagerUnitTests.swift */; }; EC1165CE27107F3E0089FA56 /* MXRoomListDataManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC1165CB27107F3E0089FA56 /* MXRoomListDataManagerTests.swift */; }; EC1165CF27107F3E0089FA56 /* MXRoomListDataManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC1165CB27107F3E0089FA56 /* MXRoomListDataManagerTests.swift */; }; - EC1165D0271082410089FA56 /* MXDecrypting.h in Headers */ = {isa = PBXBuildFile; fileRef = 3271877C1DA7CB2F0071C818 /* MXDecrypting.h */; settings = {ATTRIBUTES = (Public, ); }; }; - EC1165D1271082920089FA56 /* MXDecrypting.h in Headers */ = {isa = PBXBuildFile; fileRef = 3271877C1DA7CB2F0071C818 /* MXDecrypting.h */; settings = {ATTRIBUTES = (Public, ); }; }; EC131B0A2758D56600712964 /* MXThreadNotificationsCount.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC131B092758D56600712964 /* MXThreadNotificationsCount.swift */; }; EC131B0B2758D56600712964 /* MXThreadNotificationsCount.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC131B092758D56600712964 /* MXThreadNotificationsCount.swift */; }; EC131B0E2768A0C600712964 /* MXEventTimeline.h in Headers */ = {isa = PBXBuildFile; fileRef = EC131B0D2768A0C600712964 /* MXEventTimeline.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -1550,20 +1439,10 @@ EC403835289A672A0067D5B8 /* MXAes256BackupAuthData.h in Headers */ = {isa = PBXBuildFile; fileRef = EC403833289A672A0067D5B8 /* MXAes256BackupAuthData.h */; settings = {ATTRIBUTES = (Public, ); }; }; EC403837289A67440067D5B8 /* MXAes256BackupAuthData.m in Sources */ = {isa = PBXBuildFile; fileRef = EC403836289A67440067D5B8 /* MXAes256BackupAuthData.m */; }; EC403838289A67440067D5B8 /* MXAes256BackupAuthData.m in Sources */ = {isa = PBXBuildFile; fileRef = EC403836289A67440067D5B8 /* MXAes256BackupAuthData.m */; }; - EC403844289A7F0F0067D5B8 /* MXCurve25519KeyBackupAlgorithm.h in Headers */ = {isa = PBXBuildFile; fileRef = EC403842289A7F0F0067D5B8 /* MXCurve25519KeyBackupAlgorithm.h */; settings = {ATTRIBUTES = (Public, ); }; }; - EC403845289A7F0F0067D5B8 /* MXCurve25519KeyBackupAlgorithm.h in Headers */ = {isa = PBXBuildFile; fileRef = EC403842289A7F0F0067D5B8 /* MXCurve25519KeyBackupAlgorithm.h */; settings = {ATTRIBUTES = (Public, ); }; }; - EC403846289A7F0F0067D5B8 /* MXCurve25519KeyBackupAlgorithm.m in Sources */ = {isa = PBXBuildFile; fileRef = EC403843289A7F0F0067D5B8 /* MXCurve25519KeyBackupAlgorithm.m */; }; - EC403847289A7F0F0067D5B8 /* MXCurve25519KeyBackupAlgorithm.m in Sources */ = {isa = PBXBuildFile; fileRef = EC403843289A7F0F0067D5B8 /* MXCurve25519KeyBackupAlgorithm.m */; }; - EC40384A289A7F260067D5B8 /* MXAes256KeyBackupAlgorithm.h in Headers */ = {isa = PBXBuildFile; fileRef = EC403848289A7F260067D5B8 /* MXAes256KeyBackupAlgorithm.h */; settings = {ATTRIBUTES = (Public, ); }; }; - EC40384B289A7F260067D5B8 /* MXAes256KeyBackupAlgorithm.h in Headers */ = {isa = PBXBuildFile; fileRef = EC403848289A7F260067D5B8 /* MXAes256KeyBackupAlgorithm.h */; settings = {ATTRIBUTES = (Public, ); }; }; - EC40384C289A7F260067D5B8 /* MXAes256KeyBackupAlgorithm.m in Sources */ = {isa = PBXBuildFile; fileRef = EC403849289A7F260067D5B8 /* MXAes256KeyBackupAlgorithm.m */; }; - EC40384D289A7F260067D5B8 /* MXAes256KeyBackupAlgorithm.m in Sources */ = {isa = PBXBuildFile; fileRef = EC403849289A7F260067D5B8 /* MXAes256KeyBackupAlgorithm.m */; }; EC403854289ACF5E0067D5B8 /* MXKeyBackupPreparationInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = EC403853289ACF5E0067D5B8 /* MXKeyBackupPreparationInfo.m */; }; EC403855289ACF5E0067D5B8 /* MXKeyBackupPreparationInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = EC403853289ACF5E0067D5B8 /* MXKeyBackupPreparationInfo.m */; }; EC403856289B263F0067D5B8 /* MXBaseKeyBackupAuthData.h in Headers */ = {isa = PBXBuildFile; fileRef = EC40384E289A906A0067D5B8 /* MXBaseKeyBackupAuthData.h */; settings = {ATTRIBUTES = (Public, ); }; }; EC403857289B26400067D5B8 /* MXBaseKeyBackupAuthData.h in Headers */ = {isa = PBXBuildFile; fileRef = EC40384E289A906A0067D5B8 /* MXBaseKeyBackupAuthData.h */; settings = {ATTRIBUTES = (Public, ); }; }; - EC403858289C38BA0067D5B8 /* MXKeyBackupAlgorithm.h in Headers */ = {isa = PBXBuildFile; fileRef = EC403841289A70FC0067D5B8 /* MXKeyBackupAlgorithm.h */; settings = {ATTRIBUTES = (Public, ); }; }; - EC403859289C38BB0067D5B8 /* MXKeyBackupAlgorithm.h in Headers */ = {isa = PBXBuildFile; fileRef = EC403841289A70FC0067D5B8 /* MXKeyBackupAlgorithm.h */; settings = {ATTRIBUTES = (Public, ); }; }; EC40385A289C38FB0067D5B8 /* MXKeyBackupPreparationInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = EC403852289ACDBB0067D5B8 /* MXKeyBackupPreparationInfo.h */; settings = {ATTRIBUTES = (Public, ); }; }; EC40385B289C38FB0067D5B8 /* MXKeyBackupPreparationInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = EC403852289ACDBB0067D5B8 /* MXKeyBackupPreparationInfo.h */; settings = {ATTRIBUTES = (Public, ); }; }; EC40385D28A16EDA0067D5B8 /* MXAes256KeyBackupTests.m in Sources */ = {isa = PBXBuildFile; fileRef = EC40385C28A16EDA0067D5B8 /* MXAes256KeyBackupTests.m */; }; @@ -1778,7 +1657,6 @@ ECD289BF26FDE5F700F268CF /* MXStoreRoomListDataCounts.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECD289BE26FDE5F700F268CF /* MXStoreRoomListDataCounts.swift */; }; ECD289C42701DADB00F268CF /* MXTools.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECD289C32701DADA00F268CF /* MXTools.swift */; }; ECD289C52701DADB00F268CF /* MXTools.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECD289C32701DADA00F268CF /* MXTools.swift */; }; - ECD623FF25D3DCC900DC0A0B /* MXOlmDecryption.h in Headers */ = {isa = PBXBuildFile; fileRef = 327187831DA7D0220071C818 /* MXOlmDecryption.h */; settings = {ATTRIBUTES = (Public, ); }; }; ECDA762A27B28F3B000C48CF /* MXEventRelationThread.h in Headers */ = {isa = PBXBuildFile; fileRef = ECDA762827B28F3B000C48CF /* MXEventRelationThread.h */; settings = {ATTRIBUTES = (Public, ); }; }; ECDA762B27B28F3B000C48CF /* MXEventRelationThread.h in Headers */ = {isa = PBXBuildFile; fileRef = ECDA762827B28F3B000C48CF /* MXEventRelationThread.h */; settings = {ATTRIBUTES = (Public, ); }; }; ECDA762C27B28F3B000C48CF /* MXEventRelationThread.m in Sources */ = {isa = PBXBuildFile; fileRef = ECDA762927B28F3B000C48CF /* MXEventRelationThread.m */; }; @@ -1833,8 +1711,6 @@ ED1FE9072912D2EB0046F722 /* MXRoomEventDecryptionUnitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED1FE9052912D2EB0046F722 /* MXRoomEventDecryptionUnitTests.swift */; }; ED1FE90B2912E13A0046F722 /* DecryptedEvent+Stub.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED1FE90A2912E13A0046F722 /* DecryptedEvent+Stub.swift */; }; ED1FE90C2912E13A0046F722 /* DecryptedEvent+Stub.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED1FE90A2912E13A0046F722 /* DecryptedEvent+Stub.swift */; }; - ED21F68528104DA2002FF83D /* MXMegolmEncryptionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED21F68428104DA2002FF83D /* MXMegolmEncryptionTests.swift */; }; - ED21F68628104DA2002FF83D /* MXMegolmEncryptionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED21F68428104DA2002FF83D /* MXMegolmEncryptionTests.swift */; }; ED28068428F06C6C0070AE9F /* QrCodeStub.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED28068328F06C6C0070AE9F /* QrCodeStub.swift */; }; ED28068528F06C6C0070AE9F /* QrCodeStub.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED28068328F06C6C0070AE9F /* QrCodeStub.swift */; }; ED28068728F06D360070AE9F /* MXQRCodeTransactionV2.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED28068628F06D360070AE9F /* MXQRCodeTransactionV2.swift */; }; @@ -1844,8 +1720,6 @@ ED2DD118286C450600F06731 /* MXCryptoRequests.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED2DD113286C450600F06731 /* MXCryptoRequests.swift */; }; ED2DD119286C450600F06731 /* MXCryptoRequests.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED2DD113286C450600F06731 /* MXCryptoRequests.swift */; }; ED2DD11D286C4F4400F06731 /* MXCryptoRequestsUnitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED2DD11B286C4F3E00F06731 /* MXCryptoRequestsUnitTests.swift */; }; - ED35652C281150310002BF6A /* MXOlmInboundGroupSessionUnitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED35652B281150310002BF6A /* MXOlmInboundGroupSessionUnitTests.swift */; }; - ED35652D281150310002BF6A /* MXOlmInboundGroupSessionUnitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED35652B281150310002BF6A /* MXOlmInboundGroupSessionUnitTests.swift */; }; ED35652F281153480002BF6A /* MXMegolmSessionDataUnitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED35652E281153480002BF6A /* MXMegolmSessionDataUnitTests.swift */; }; ED356530281153480002BF6A /* MXMegolmSessionDataUnitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED35652E281153480002BF6A /* MXMegolmSessionDataUnitTests.swift */; }; ED36ED8628DD9E2200C86416 /* MXCryptoKeyBackupEngine.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED36ED8528DD9E2100C86416 /* MXCryptoKeyBackupEngine.swift */; }; @@ -1856,16 +1730,10 @@ ED4114E9292E496C00728459 /* MXBackgroundCrypto.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED4114E7292E496C00728459 /* MXBackgroundCrypto.swift */; }; ED4114EB292E498100728459 /* MXBackgroundCryptoV2.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED4114EA292E498100728459 /* MXBackgroundCryptoV2.swift */; }; ED4114EC292E498100728459 /* MXBackgroundCryptoV2.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED4114EA292E498100728459 /* MXBackgroundCryptoV2.swift */; }; - ED4114EE292E49C000728459 /* MXLegacyBackgroundCrypto.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED4114ED292E49C000728459 /* MXLegacyBackgroundCrypto.swift */; }; - ED4114EF292E49C000728459 /* MXLegacyBackgroundCrypto.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED4114ED292E49C000728459 /* MXLegacyBackgroundCrypto.swift */; }; - ED4368B129784CCE002B6272 /* MXRealmCryptoStoreTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED4368B029784CCE002B6272 /* MXRealmCryptoStoreTests.swift */; }; - ED4368B229784CCE002B6272 /* MXRealmCryptoStoreTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED4368B029784CCE002B6272 /* MXRealmCryptoStoreTests.swift */; }; ED44F01128180BCC00452A5D /* MXSharedHistoryKeyRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED44F01028180BCC00452A5D /* MXSharedHistoryKeyRequest.swift */; }; ED44F01228180BCC00452A5D /* MXSharedHistoryKeyRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED44F01028180BCC00452A5D /* MXSharedHistoryKeyRequest.swift */; }; ED44F01428180EAB00452A5D /* MXSharedHistoryKeyManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED44F01328180EAB00452A5D /* MXSharedHistoryKeyManager.swift */; }; ED44F01528180EAB00452A5D /* MXSharedHistoryKeyManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED44F01328180EAB00452A5D /* MXSharedHistoryKeyManager.swift */; }; - ED44F01A28180F4000452A5D /* MXSharedHistoryKeyManagerUnitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED44F01728180F1C00452A5D /* MXSharedHistoryKeyManagerUnitTests.swift */; }; - ED44F01B28180F4000452A5D /* MXSharedHistoryKeyManagerUnitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED44F01728180F1C00452A5D /* MXSharedHistoryKeyManagerUnitTests.swift */; }; ED463ECB29B0B75800957941 /* EventEncryptionAlgorithm+String.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED463ECA29B0B75800957941 /* EventEncryptionAlgorithm+String.swift */; }; ED463ECC29B0B75800957941 /* EventEncryptionAlgorithm+String.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED463ECA29B0B75800957941 /* EventEncryptionAlgorithm+String.swift */; }; ED463ECE29B0B8E000957941 /* MXRoomSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED463ECD29B0B8E000957941 /* MXRoomSettings.swift */; }; @@ -1882,12 +1750,6 @@ ED51943D284630090006EEC6 /* MXRestClientStub.m in Sources */ = {isa = PBXBuildFile; fileRef = ED51943B284630090006EEC6 /* MXRestClientStub.m */; }; ED555F59298BB27200C5BD63 /* MXKeysQueryResponseUnitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED555F58298BB27200C5BD63 /* MXKeysQueryResponseUnitTests.swift */; }; ED555F5A298BB27200C5BD63 /* MXKeysQueryResponseUnitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED555F58298BB27200C5BD63 /* MXKeysQueryResponseUnitTests.swift */; }; - ED558068296F0361003443E3 /* MXCryptoMigrationStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED558067296F0361003443E3 /* MXCryptoMigrationStore.swift */; }; - ED558069296F0361003443E3 /* MXCryptoMigrationStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED558067296F0361003443E3 /* MXCryptoMigrationStore.swift */; }; - ED55806D296F0E3A003443E3 /* MXCryptoMigrationStoreUnitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED55806C296F0E3A003443E3 /* MXCryptoMigrationStoreUnitTests.swift */; }; - ED55806E296F0E3A003443E3 /* MXCryptoMigrationStoreUnitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED55806C296F0E3A003443E3 /* MXCryptoMigrationStoreUnitTests.swift */; }; - ED558070296F1BEE003443E3 /* MXCryptoMigrationV2UnitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED55806F296F1BEE003443E3 /* MXCryptoMigrationV2UnitTests.swift */; }; - ED558071296F1BEE003443E3 /* MXCryptoMigrationV2UnitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED55806F296F1BEE003443E3 /* MXCryptoMigrationV2UnitTests.swift */; }; ED5580732970265A003443E3 /* MXCryptoSDKLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED5580722970265A003443E3 /* MXCryptoSDKLogger.swift */; }; ED5580742970265A003443E3 /* MXCryptoSDKLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED5580722970265A003443E3 /* MXCryptoSDKLogger.swift */; }; ED55807629709943003443E3 /* MatrixSDKTestsE2EData.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED55807529709943003443E3 /* MatrixSDKTestsE2EData.swift */; }; @@ -1910,8 +1772,6 @@ ED5C754728B3E80300D24E85 /* MXLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = ED5C753A28B3E80300D24E85 /* MXLogger.m */; }; ED5C754828B3E80300D24E85 /* MXLogObjcWrapper.h in Headers */ = {isa = PBXBuildFile; fileRef = ED5C753B28B3E80300D24E85 /* MXLogObjcWrapper.h */; settings = {ATTRIBUTES = (Public, ); }; }; ED5C754928B3E80300D24E85 /* MXLogObjcWrapper.h in Headers */ = {isa = PBXBuildFile; fileRef = ED5C753B28B3E80300D24E85 /* MXLogObjcWrapper.h */; settings = {ATTRIBUTES = (Public, ); }; }; - ED5C95CE2833E85600843D82 /* MXOlmDeviceUnitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED5C95CD2833E85600843D82 /* MXOlmDeviceUnitTests.swift */; }; - ED5C95CF2833E85600843D82 /* MXOlmDeviceUnitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED5C95CD2833E85600843D82 /* MXOlmDeviceUnitTests.swift */; }; ED5EF145297AB1F200A5ADDA /* MXRoomEventEncryption.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED5EF144297AB1F200A5ADDA /* MXRoomEventEncryption.swift */; }; ED5EF146297AB1F200A5ADDA /* MXRoomEventEncryption.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED5EF144297AB1F200A5ADDA /* MXRoomEventEncryption.swift */; }; ED5EF14B297AB29F00A5ADDA /* MXDeviceVerification+LocalTrust.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED5EF148297AB29F00A5ADDA /* MXDeviceVerification+LocalTrust.swift */; }; @@ -1926,8 +1786,6 @@ ED5EF156297AB93800A5ADDA /* MXRoomEventEncryptionUnitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED5EF154297AB93800A5ADDA /* MXRoomEventEncryptionUnitTests.swift */; }; ED647E3E292CE64400A47519 /* MXSessionStartupProgress.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED647E3D292CE64400A47519 /* MXSessionStartupProgress.swift */; }; ED647E3F292CE64400A47519 /* MXSessionStartupProgress.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED647E3D292CE64400A47519 /* MXSessionStartupProgress.swift */; }; - ED6DABFC28C7542800ECDCB6 /* MXRoomKeyInfoFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED6DABFB28C7542800ECDCB6 /* MXRoomKeyInfoFactory.swift */; }; - ED6DABFD28C7542800ECDCB6 /* MXRoomKeyInfoFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED6DABFB28C7542800ECDCB6 /* MXRoomKeyInfoFactory.swift */; }; ED6DAC0228C76F0A00ECDCB6 /* MXRoomKeyInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED6DAC0128C76F0A00ECDCB6 /* MXRoomKeyInfo.swift */; }; ED6DAC0328C76F0A00ECDCB6 /* MXRoomKeyInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED6DAC0128C76F0A00ECDCB6 /* MXRoomKeyInfo.swift */; }; ED6DAC0728C77E1100ECDCB6 /* MXForwardedRoomKeyEventContentUnitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED6DAC0628C77E1100ECDCB6 /* MXForwardedRoomKeyEventContentUnitTests.swift */; }; @@ -1936,10 +1794,6 @@ ED6DAC0B28C784AE00ECDCB6 /* Dictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED6DAC0928C784AE00ECDCB6 /* Dictionary.swift */; }; ED6DAC0D28C784E200ECDCB6 /* MXRoomKeyEventContentUnitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED6DAC0C28C784E200ECDCB6 /* MXRoomKeyEventContentUnitTests.swift */; }; ED6DAC0E28C784E200ECDCB6 /* MXRoomKeyEventContentUnitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED6DAC0C28C784E200ECDCB6 /* MXRoomKeyEventContentUnitTests.swift */; }; - ED6DAC1128C788A700ECDCB6 /* MXRoomKeyInfoFactoryUnitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED6DAC1028C788A700ECDCB6 /* MXRoomKeyInfoFactoryUnitTests.swift */; }; - ED6DAC1228C788A700ECDCB6 /* MXRoomKeyInfoFactoryUnitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED6DAC1028C788A700ECDCB6 /* MXRoomKeyInfoFactoryUnitTests.swift */; }; - ED6DAC1528C78D4000ECDCB6 /* MXMemoryCryptoStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED6DAC1428C78D4000ECDCB6 /* MXMemoryCryptoStore.swift */; }; - ED6DAC1628C78D4000ECDCB6 /* MXMemoryCryptoStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED6DAC1428C78D4000ECDCB6 /* MXMemoryCryptoStore.swift */; }; ED6DAC1828C799E300ECDCB6 /* MXRoomKeyResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED6DAC1728C799E300ECDCB6 /* MXRoomKeyResult.swift */; }; ED6DAC1928C799E300ECDCB6 /* MXRoomKeyResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED6DAC1728C799E300ECDCB6 /* MXRoomKeyResult.swift */; }; ED6DAC1B28C79AA300ECDCB6 /* MXUnrequestedForwardedRoomKeyManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED6DAC1A28C79AA300ECDCB6 /* MXUnrequestedForwardedRoomKeyManager.swift */; }; @@ -1981,8 +1835,6 @@ ED76A4AE28EDA2CE00036FF0 /* MXKeyVerificationStateResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED76A4AC28EDA2CE00036FF0 /* MXKeyVerificationStateResolver.swift */; }; ED79B9852940BB45008952F6 /* MXToDevicePayloadUnitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED79B9842940BB45008952F6 /* MXToDevicePayloadUnitTests.swift */; }; ED79B9862940BB45008952F6 /* MXToDevicePayloadUnitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED79B9842940BB45008952F6 /* MXToDevicePayloadUnitTests.swift */; }; - ED825F8F29014EDA006A614E /* MXSession+LegacyCrypto.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED825F8E29014EDA006A614E /* MXSession+LegacyCrypto.swift */; }; - ED825F9029014EDA006A614E /* MXSession+LegacyCrypto.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED825F8E29014EDA006A614E /* MXSession+LegacyCrypto.swift */; }; ED88999127F2065D00718486 /* MXRoomAliasResolution.h in Headers */ = {isa = PBXBuildFile; fileRef = ED88998F27F2065C00718486 /* MXRoomAliasResolution.h */; settings = {ATTRIBUTES = (Public, ); }; }; ED88999227F2065D00718486 /* MXRoomAliasResolution.h in Headers */ = {isa = PBXBuildFile; fileRef = ED88998F27F2065C00718486 /* MXRoomAliasResolution.h */; settings = {ATTRIBUTES = (Public, ); }; }; ED88999327F2065D00718486 /* MXRoomAliasResolution.m in Sources */ = {isa = PBXBuildFile; fileRef = ED88999027F2065D00718486 /* MXRoomAliasResolution.m */; }; @@ -2017,8 +1869,6 @@ EDA40A1829E9E2BF00C0CAB9 /* legacy_deprecated1_account.realm in Resources */ = {isa = PBXBuildFile; fileRef = EDA40A0C29E9E2BF00C0CAB9 /* legacy_deprecated1_account.realm */; }; EDA40A1929E9E2BF00C0CAB9 /* archived_encrypted_event in Resources */ = {isa = PBXBuildFile; fileRef = EDA40A0D29E9E2BF00C0CAB9 /* archived_encrypted_event */; }; EDA40A1A29E9E2BF00C0CAB9 /* archived_encrypted_event in Resources */ = {isa = PBXBuildFile; fileRef = EDA40A0D29E9E2BF00C0CAB9 /* archived_encrypted_event */; }; - EDA40A1B29E9E2BF00C0CAB9 /* LegacyRealmStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDA40A0E29E9E2BF00C0CAB9 /* LegacyRealmStore.swift */; }; - EDA40A1C29E9E2BF00C0CAB9 /* LegacyRealmStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDA40A0E29E9E2BF00C0CAB9 /* LegacyRealmStore.swift */; }; EDA69340290BA92E00223252 /* MXCryptoMachineUnitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDA6933F290BA92E00223252 /* MXCryptoMachineUnitTests.swift */; }; EDA69341290BA92E00223252 /* MXCryptoMachineUnitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDA6933F290BA92E00223252 /* MXCryptoMachineUnitTests.swift */; }; EDAAC41928E2FCFE00DD89B5 /* MXCryptoSecretStoreV2.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDAAC41828E2FCFE00DD89B5 /* MXCryptoSecretStoreV2.swift */; }; @@ -2047,12 +1897,6 @@ EDC8C40E2968C37F003792C5 /* MXKeysQuerySchedulerUnitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDC8C40A2968A9F7003792C5 /* MXKeysQuerySchedulerUnitTests.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 */; }; - EDCF802E2941FF220059E774 /* MXCryptoMigrationV2.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDCF802C2941FF220059E774 /* MXCryptoMigrationV2.swift */; }; - EDD4197E28DCAA5F007F3757 /* MXNativeKeyBackupEngine.h in Headers */ = {isa = PBXBuildFile; fileRef = EDD4197D28DCAA5F007F3757 /* MXNativeKeyBackupEngine.h */; }; - EDD4197F28DCAA5F007F3757 /* MXNativeKeyBackupEngine.h in Headers */ = {isa = PBXBuildFile; fileRef = EDD4197D28DCAA5F007F3757 /* MXNativeKeyBackupEngine.h */; }; - EDD4198128DCAA7B007F3757 /* MXNativeKeyBackupEngine.m in Sources */ = {isa = PBXBuildFile; fileRef = EDD4198028DCAA7B007F3757 /* MXNativeKeyBackupEngine.m */; }; - EDD4198228DCAA7B007F3757 /* MXNativeKeyBackupEngine.m in Sources */ = {isa = PBXBuildFile; fileRef = EDD4198028DCAA7B007F3757 /* MXNativeKeyBackupEngine.m */; }; EDD578E12881C37C006739DD /* MXDeviceInfoSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDD578DC2881C37C006739DD /* MXDeviceInfoSource.swift */; }; EDD578E22881C37C006739DD /* MXDeviceInfoSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDD578DC2881C37C006739DD /* MXDeviceInfoSource.swift */; }; EDD578E32881C37C006739DD /* MXTrustLevelSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDD578DD2881C37C006739DD /* MXTrustLevelSource.swift */; }; @@ -2069,12 +1913,8 @@ EDD7B74929CB3F1B00548AB4 /* MXCrossSigningInfo_v1 in Resources */ = {isa = PBXBuildFile; fileRef = EDD7B74629CB3F1B00548AB4 /* MXCrossSigningInfo_v1 */; }; EDD7B74A29CB3F1B00548AB4 /* MXCrossSigningInfo_v0 in Resources */ = {isa = PBXBuildFile; fileRef = EDD7B74729CB3F1B00548AB4 /* MXCrossSigningInfo_v0 */; }; EDD7B74B29CB3F1B00548AB4 /* MXCrossSigningInfo_v0 in Resources */ = {isa = PBXBuildFile; fileRef = EDD7B74729CB3F1B00548AB4 /* MXCrossSigningInfo_v0 */; }; - EDDB07CA297EE0A7005249A6 /* MXCryptoV2FactoryUnitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDDB07C9297EE0A7005249A6 /* MXCryptoV2FactoryUnitTests.swift */; }; - EDDB07CB297EE0A7005249A6 /* MXCryptoV2FactoryUnitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDDB07C9297EE0A7005249A6 /* MXCryptoV2FactoryUnitTests.swift */; }; EDDBA7F0293F353900AD1480 /* MXToDevicePayload.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDDBA7EF293F353900AD1480 /* MXToDevicePayload.swift */; }; EDDBA7F1293F353900AD1480 /* MXToDevicePayload.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDDBA7EF293F353900AD1480 /* MXToDevicePayload.swift */; }; - EDDD90C82901611600B760E0 /* MXLegacyCrypto+LegacyCrossSigning.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDDD90C72901611600B760E0 /* MXLegacyCrypto+LegacyCrossSigning.swift */; }; - EDDD90C92901611600B760E0 /* MXLegacyCrypto+LegacyCrossSigning.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDDD90C72901611600B760E0 /* MXLegacyCrypto+LegacyCrossSigning.swift */; }; EDE1B13B28B7BEAB000DEEE8 /* MXCrossSigningV2UnitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDE1B13A28B7BEAB000DEEE8 /* MXCrossSigningV2UnitTests.swift */; }; EDE1B13C28B7BEAB000DEEE8 /* MXCrossSigningV2UnitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDE1B13A28B7BEAB000DEEE8 /* MXCrossSigningV2UnitTests.swift */; }; EDE70DC528DA1B7F00099736 /* MXCryptoTools.h in Headers */ = {isa = PBXBuildFile; fileRef = 3250E7C8220C913900736CB5 /* MXCryptoTools.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -2183,11 +2023,7 @@ 320A883B217F4E35002EA952 /* MXMegolmBackupCreationInfo.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXMegolmBackupCreationInfo.m; sourceTree = ""; }; 320A883E217F4E3E002EA952 /* MXCurve25519BackupAuthData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXCurve25519BackupAuthData.h; sourceTree = ""; }; 320A883F217F4E3F002EA952 /* MXCurve25519BackupAuthData.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXCurve25519BackupAuthData.m; sourceTree = ""; }; - 320B3932239FA56900BE2C06 /* MXKeyVerificationByDMRequest.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXKeyVerificationByDMRequest.h; sourceTree = ""; }; - 320B3933239FA56900BE2C06 /* MXKeyVerificationByDMRequest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXKeyVerificationByDMRequest.m; sourceTree = ""; }; 320B3938239FD15E00BE2C06 /* MXKeyVerificationRequest.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXKeyVerificationRequest.h; sourceTree = ""; }; - 320B3939239FD15E00BE2C06 /* MXKeyVerificationRequest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXKeyVerificationRequest.m; sourceTree = ""; }; - 320B393E239FD48400BE2C06 /* MXKeyVerificationRequest_Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXKeyVerificationRequest_Private.h; sourceTree = ""; }; 320BBF3B1D6C7D9D0079890E /* MXEventsEnumerator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXEventsEnumerator.h; sourceTree = ""; }; 320BBF3D1D6C81550079890E /* MXEventsByTypesEnumeratorOnArray.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXEventsByTypesEnumeratorOnArray.m; sourceTree = ""; }; 320BBF3E1D6C81550079890E /* MXEventsByTypesEnumeratorOnArray.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXEventsByTypesEnumeratorOnArray.h; sourceTree = ""; }; @@ -2224,12 +2060,8 @@ 321B413E1E09937E009EEEC7 /* MXRoomSummary.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXRoomSummary.m; sourceTree = ""; }; 321CFDE422525A49004D31DF /* MXSASTransaction.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXSASTransaction.h; sourceTree = ""; }; 321CFDE522525A49004D31DF /* MXSASTransaction.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXSASTransaction.m; sourceTree = ""; }; - 321CFDE822525DED004D31DF /* MXIncomingSASTransaction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXIncomingSASTransaction.h; sourceTree = ""; }; - 321CFDE922525DEE004D31DF /* MXIncomingSASTransaction.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXIncomingSASTransaction.m; sourceTree = ""; }; 321CFDEC225264C4004D31DF /* NSArray+MatrixSDK.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSArray+MatrixSDK.h"; sourceTree = ""; }; 321CFDED225264C4004D31DF /* NSArray+MatrixSDK.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSArray+MatrixSDK.m"; sourceTree = ""; }; - 321CFDF022549C39004D31DF /* MXSASTransaction_Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXSASTransaction_Private.h; sourceTree = ""; }; - 321CFDF12254B9FB004D31DF /* MXKeyVerificationTransaction_Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXKeyVerificationTransaction_Private.h; sourceTree = ""; }; 321CFDF82254E720004D31DF /* MXTransactionCancelCode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXTransactionCancelCode.m; sourceTree = ""; }; 321CFDFA2254E728004D31DF /* MXTransactionCancelCode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXTransactionCancelCode.h; sourceTree = ""; }; 321CFDFC2254E8C4004D31DF /* MXEmojiRepresentation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXEmojiRepresentation.m; sourceTree = ""; }; @@ -2243,22 +2075,11 @@ 322360511A8E610500A3CA81 /* MXPushRuleDisplayNameCondtionChecker.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXPushRuleDisplayNameCondtionChecker.m; sourceTree = ""; }; 32261B8823C74A230018F1E2 /* MXDeviceTrustLevel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXDeviceTrustLevel.h; sourceTree = ""; }; 32261B8923C74A230018F1E2 /* MXDeviceTrustLevel.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXDeviceTrustLevel.m; sourceTree = ""; }; - 322691301E5EF77D00966A6E /* MXDeviceListOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXDeviceListOperation.h; sourceTree = ""; }; - 322691311E5EF77D00966A6E /* MXDeviceListOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXDeviceListOperation.m; sourceTree = ""; }; - 322691341E5EFF8700966A6E /* MXDeviceListOperationsPool.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MXDeviceListOperationsPool.h; path = MatrixSDK/Crypto/Data/MXDeviceListOperationsPool.h; sourceTree = SOURCE_ROOT; }; - 322691351E5EFF8700966A6E /* MXDeviceListOperationsPool.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MXDeviceListOperationsPool.m; path = MatrixSDK/Crypto/Data/MXDeviceListOperationsPool.m; sourceTree = SOURCE_ROOT; }; - 3229534F25A5F7220012FCF0 /* MXBackgroundCryptoStore.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXBackgroundCryptoStore.h; sourceTree = ""; }; - 3229535025A5F7220012FCF0 /* MXBackgroundCryptoStore.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXBackgroundCryptoStore.m; sourceTree = ""; }; 322985CA26FAF898001890BC /* MXSession.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXSession.swift; sourceTree = ""; }; 322985CD26FAFC58001890BC /* MatrixSDKTestsSwiftHeader.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MatrixSDKTestsSwiftHeader.h; sourceTree = ""; }; 322985CE26FBAE7B001890BC /* TestObserver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestObserver.swift; sourceTree = ""; }; 322985D126FC9E61001890BC /* MXSessionTracker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXSessionTracker.swift; sourceTree = ""; }; 322A51B41D9AB15900C8536D /* MXCrypto.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXCrypto.h; sourceTree = ""; }; - 322A51B51D9AB15900C8536D /* MXCrypto.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXCrypto.m; sourceTree = ""; }; - 322A51C11D9AC8FE00C8536D /* MXCryptoAlgorithms.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXCryptoAlgorithms.h; sourceTree = ""; }; - 322A51C21D9AC8FE00C8536D /* MXCryptoAlgorithms.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXCryptoAlgorithms.m; sourceTree = ""; }; - 322A51C51D9BBD3C00C8536D /* MXOlmDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXOlmDevice.h; sourceTree = ""; }; - 322A51C61D9BBD3C00C8536D /* MXOlmDevice.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXOlmDevice.m; sourceTree = ""; }; 322A51D71D9E846800C8536D /* MXCryptoTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXCryptoTests.m; sourceTree = ""; }; 322AB582260CD5690017E964 /* MXCachedSyncResponse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXCachedSyncResponse.h; sourceTree = ""; }; 322AB583260CD5690017E964 /* MXCachedSyncResponse.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXCachedSyncResponse.m; sourceTree = ""; }; @@ -2293,8 +2114,6 @@ 3240951E1AFA432F00D81C97 /* MXCallStackCall.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXCallStackCall.h; sourceTree = ""; }; 3240969B1F9F751600DBA607 /* MXPushRuleSenderNotificationPermissionConditionChecker.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXPushRuleSenderNotificationPermissionConditionChecker.h; sourceTree = ""; }; 3240969C1F9F751600DBA607 /* MXPushRuleSenderNotificationPermissionConditionChecker.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXPushRuleSenderNotificationPermissionConditionChecker.m; sourceTree = ""; }; - 32442FAF21EDD21300D2411B /* MXKeyBackupPassword.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXKeyBackupPassword.h; sourceTree = ""; }; - 32442FB021EDD21300D2411B /* MXKeyBackupPassword.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXKeyBackupPassword.m; sourceTree = ""; }; 3245A74C1AF7B2930001D8A7 /* MXCall.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXCall.h; sourceTree = ""; }; 3245A74D1AF7B2930001D8A7 /* MXCall.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXCall.m; sourceTree = ""; }; 3245A74E1AF7B2930001D8A7 /* MXCallManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXCallManager.h; sourceTree = ""; }; @@ -2334,9 +2153,7 @@ 3250E7C9220C913900736CB5 /* MXCryptoTools.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXCryptoTools.m; sourceTree = ""; }; 3251D41E25AF01D7001E6E77 /* MXUIKitApplicationStateService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXUIKitApplicationStateService.swift; sourceTree = ""; }; 3252DCAC224BE5D40032264F /* MXKeyVerificationManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXKeyVerificationManager.h; sourceTree = ""; }; - 3252DCAD224BE5D40032264F /* MXKeyVerificationManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXKeyVerificationManager.m; sourceTree = ""; }; 3252DCB5224BEA610032264F /* MXKeyVerificationTransaction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXKeyVerificationTransaction.h; sourceTree = ""; }; - 3252DCCF224D25810032264F /* MXKeyVerificationManager_Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXKeyVerificationManager_Private.h; sourceTree = ""; }; 325380E6228DAD4A00ADDEFA /* MXAggregatedReactions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXAggregatedReactions.h; sourceTree = ""; }; 325380E7228DAD4A00ADDEFA /* MXAggregatedReactions.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXAggregatedReactions.m; sourceTree = ""; }; 32549AF523F2E2790002576B /* MXKeyVerificationReady.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXKeyVerificationReady.m; sourceTree = ""; }; @@ -2346,8 +2163,6 @@ 3256E3801DCB91EB003C9718 /* MXCryptoConstants.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXCryptoConstants.m; sourceTree = ""; }; 32581DE623C8C0C900832EAA /* MXUserTrustLevel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXUserTrustLevel.h; sourceTree = ""; }; 32581DE723C8C0C900832EAA /* MXUserTrustLevel.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXUserTrustLevel.m; sourceTree = ""; }; - 3259CD511DF860C300186944 /* MXRealmCryptoStore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXRealmCryptoStore.h; sourceTree = ""; }; - 3259CD521DF860C300186944 /* MXRealmCryptoStore.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXRealmCryptoStore.m; sourceTree = ""; }; 3259CFE526026A6F00C365DB /* MXRestClient+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "MXRestClient+Extensions.swift"; sourceTree = ""; }; 3259D0072603705300C365DB /* Array.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Array.swift; sourceTree = ""; }; 3259D00F2603724100C365DB /* MXSummable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXSummable.swift; sourceTree = ""; }; @@ -2358,7 +2173,6 @@ 325AD43E23BE3E7500FF5277 /* MXCrossSigningInfo.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXCrossSigningInfo.m; sourceTree = ""; }; 325AF3DF24897D9400EF937D /* MXSecretRecoveryResult.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXSecretRecoveryResult.h; sourceTree = ""; }; 325AF3E024897D9400EF937D /* MXSecretRecoveryResult.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXSecretRecoveryResult.m; sourceTree = ""; }; - 325D1C251DFECE0D0070B8BF /* MXCrypto_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXCrypto_Private.h; sourceTree = ""; }; 326056831C76FDF1009D44AD /* MXRoomEventTimeline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXRoomEventTimeline.h; sourceTree = ""; }; 326056841C76FDF1009D44AD /* MXRoomEventTimeline.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXRoomEventTimeline.m; sourceTree = ""; }; 32618E6F20ED2DF500E1D2EA /* MXFilterJSONModel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXFilterJSONModel.h; sourceTree = ""; }; @@ -2366,8 +2180,6 @@ 32618E7920EFA45B00E1D2EA /* MXRoomMembers.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXRoomMembers.h; sourceTree = ""; }; 32618E7A20EFA45B00E1D2EA /* MXRoomMembers.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXRoomMembers.m; sourceTree = ""; }; 326277F0288BC219009A0508 /* AllWorkingTests.xctestplan */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = AllWorkingTests.xctestplan; sourceTree = ""; }; - 32637ED21E5B00400011E20D /* MXDeviceList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXDeviceList.h; sourceTree = ""; }; - 32637ED31E5B00400011E20D /* MXDeviceList.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXDeviceList.m; sourceTree = ""; }; 3264DB8F1CEC528D00B99881 /* MXAccountData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXAccountData.h; sourceTree = ""; }; 3264DB901CEC528D00B99881 /* MXAccountData.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXAccountData.m; sourceTree = ""; }; 3264DB931CECA72900B99881 /* MXAccountDataTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXAccountDataTests.m; sourceTree = ""; }; @@ -2380,11 +2192,6 @@ 327137251A24D50A00DB6757 /* MXMyUser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXMyUser.h; sourceTree = ""; }; 327137261A24D50A00DB6757 /* MXMyUser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXMyUser.m; sourceTree = ""; }; 3271877A1DA7CAA60071C818 /* MXEncrypting.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXEncrypting.h; sourceTree = ""; }; - 3271877C1DA7CB2F0071C818 /* MXDecrypting.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXDecrypting.h; sourceTree = ""; }; - 327187831DA7D0220071C818 /* MXOlmDecryption.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXOlmDecryption.h; sourceTree = ""; }; - 327187841DA7D0220071C818 /* MXOlmDecryption.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXOlmDecryption.m; sourceTree = ""; }; - 327187871DA7DCE50071C818 /* MXOlmEncryption.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXOlmEncryption.h; sourceTree = ""; }; - 327187881DA7DCE50071C818 /* MXOlmEncryption.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXOlmEncryption.m; sourceTree = ""; }; 32720D99222EAA6F0086FFF5 /* MXDiscoveredClientConfig.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXDiscoveredClientConfig.m; sourceTree = ""; }; 32720D9A222EAA6F0086FFF5 /* MXAutoDiscovery.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXAutoDiscovery.h; sourceTree = ""; }; 32720D9B222EAA6F0086FFF5 /* MXAutoDiscovery.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXAutoDiscovery.m; sourceTree = ""; }; @@ -2392,8 +2199,6 @@ 32720DA1222EB5650086FFF5 /* MXAutoDiscoveryTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXAutoDiscoveryTests.m; sourceTree = ""; }; 3274538223FD69D600438328 /* MXKeyVerificationRequestByToDeviceJSONModel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXKeyVerificationRequestByToDeviceJSONModel.h; sourceTree = ""; }; 3274538323FD69D600438328 /* MXKeyVerificationRequestByToDeviceJSONModel.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXKeyVerificationRequestByToDeviceJSONModel.m; sourceTree = ""; }; - 3274538823FD918800438328 /* MXKeyVerificationByToDeviceRequest.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXKeyVerificationByToDeviceRequest.h; sourceTree = ""; }; - 3274538923FD918800438328 /* MXKeyVerificationByToDeviceRequest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXKeyVerificationByToDeviceRequest.m; sourceTree = ""; }; 3275FD9221A6B46600B9C13D /* MXLoginTerms.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXLoginTerms.m; sourceTree = ""; }; 3275FD9321A6B46600B9C13D /* MXLoginTerms.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXLoginTerms.h; sourceTree = ""; }; 3275FD9621A6B53300B9C13D /* MXLoginPolicyData.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXLoginPolicyData.h; sourceTree = ""; }; @@ -2455,7 +2260,6 @@ 32832B5B1BCC048300241108 /* MXStoreTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXStoreTests.h; sourceTree = ""; }; 3283F7761EAF30F700C1688C /* MXBugReportRestClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXBugReportRestClient.h; sourceTree = ""; }; 3283F7771EAF30F700C1688C /* MXBugReportRestClient.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXBugReportRestClient.m; sourceTree = ""; }; - 3284A59D1DB7C00600A09972 /* MXCryptoStore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXCryptoStore.h; sourceTree = ""; }; 328BCB3121947BE200A976D3 /* MXKeyBackupVersionTrust.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXKeyBackupVersionTrust.h; sourceTree = ""; }; 328BCB3221947BE200A976D3 /* MXKeyBackupVersionTrust.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXKeyBackupVersionTrust.m; sourceTree = ""; }; 328DDEC01A07E57E008C7DC8 /* MXJSONModelTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXJSONModelTests.m; sourceTree = ""; }; @@ -2479,8 +2283,6 @@ 329571981B024D2B00ABB3BA /* MXMockCallStackCall.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXMockCallStackCall.m; sourceTree = ""; }; 3297912523A93D4B00F7BB9B /* MXKeyVerification.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXKeyVerification.h; sourceTree = ""; }; 3297912623A93D4B00F7BB9B /* MXKeyVerification.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXKeyVerification.m; sourceTree = ""; }; - 3297912C23AA126400F7BB9B /* MXKeyVerificationStatusResolver.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXKeyVerificationStatusResolver.h; sourceTree = ""; }; - 3297912D23AA126400F7BB9B /* MXKeyVerificationStatusResolver.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXKeyVerificationStatusResolver.m; sourceTree = ""; }; 3298ABD52637FA3100E40B06 /* AllTests.xctestplan */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = AllTests.xctestplan; sourceTree = ""; }; 3298ABDC2637FB1900E40B06 /* UnitTests.xctestplan */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = UnitTests.xctestplan; sourceTree = ""; }; 32999DDD22DCD183004FF987 /* MXPusher.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXPusher.h; sourceTree = ""; }; @@ -2489,9 +2291,6 @@ 32999DE222DCD1AD004FF987 /* MXPusherData.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXPusherData.m; sourceTree = ""; }; 329D3E601E251027002E2F1E /* MXRoomSummaryUpdater.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXRoomSummaryUpdater.h; sourceTree = ""; }; 329D3E611E251027002E2F1E /* MXRoomSummaryUpdater.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXRoomSummaryUpdater.m; sourceTree = ""; }; - 329E8088224E261600A48C3A /* MXKeyVerificationTransaction.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXKeyVerificationTransaction.m; sourceTree = ""; }; - 329E808A224E2E1B00A48C3A /* MXOutgoingSASTransaction.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXOutgoingSASTransaction.h; sourceTree = ""; }; - 329E808B224E2E1B00A48C3A /* MXOutgoingSASTransaction.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXOutgoingSASTransaction.m; sourceTree = ""; }; 329E808E22512DF500A48C3A /* MXCryptoKeyVerificationTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXCryptoKeyVerificationTests.m; sourceTree = ""; }; 329EEE4F23BE4D8400FBE484 /* MXCrossSigningInfo_Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXCrossSigningInfo_Private.h; sourceTree = ""; }; 329FB1731A0A3A1600A5E88E /* MXRoomMember.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXRoomMember.h; sourceTree = ""; }; @@ -2501,12 +2300,6 @@ 329FB17B1A0A963700A5E88E /* MXRoomMemberTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXRoomMemberTests.m; sourceTree = ""; }; 329FB17D1A0B665800A5E88E /* MXUser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXUser.h; sourceTree = ""; }; 329FB17E1A0B665800A5E88E /* MXUser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXUser.m; sourceTree = ""; }; - 32A151241DABB0CB00400192 /* MXMegolmDecryption.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MXMegolmDecryption.h; path = Megolm/MXMegolmDecryption.h; sourceTree = ""; }; - 32A151251DABB0CB00400192 /* MXMegolmDecryption.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MXMegolmDecryption.m; path = Megolm/MXMegolmDecryption.m; sourceTree = ""; }; - 32A151371DAD292400400192 /* MXMegolmEncryption.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MXMegolmEncryption.h; path = Megolm/MXMegolmEncryption.h; sourceTree = ""; }; - 32A151381DAD292400400192 /* MXMegolmEncryption.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MXMegolmEncryption.m; path = Megolm/MXMegolmEncryption.m; sourceTree = ""; }; - 32A1513C1DAF768D00400192 /* MXOlmInboundGroupSession.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXOlmInboundGroupSession.h; sourceTree = ""; }; - 32A1513D1DAF768D00400192 /* MXOlmInboundGroupSession.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXOlmInboundGroupSession.m; sourceTree = ""; }; 32A151401DAF7C0C00400192 /* MXDeviceInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXDeviceInfo.h; sourceTree = ""; }; 32A151411DAF7C0C00400192 /* MXDeviceInfo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXDeviceInfo.m; sourceTree = ""; }; 32A151421DAF7C0C00400192 /* MXKey.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXKey.h; sourceTree = ""; }; @@ -2520,8 +2313,6 @@ 32A151591DB525DA00400192 /* NSObject+sortedKeys.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSObject+sortedKeys.h"; path = "Lib/NSObject+sortedKeys.h"; sourceTree = ""; }; 32A1515A1DB525DA00400192 /* NSObject+sortedKeys.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSObject+sortedKeys.m"; path = "Lib/NSObject+sortedKeys.m"; sourceTree = ""; }; 32A27D1E19EC335300BAFADE /* MXRoomTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = MXRoomTests.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; - 32A30B161FB4813400C8309E /* MXIncomingRoomKeyRequestManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXIncomingRoomKeyRequestManager.h; sourceTree = ""; }; - 32A30B171FB4813400C8309E /* MXIncomingRoomKeyRequestManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXIncomingRoomKeyRequestManager.m; sourceTree = ""; }; 32A31BBC20D3F2EC005916C7 /* MXFilterObject.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXFilterObject.h; sourceTree = ""; }; 32A31BBD20D3F2EC005916C7 /* MXFilterObject.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXFilterObject.m; sourceTree = ""; }; 32A31BC020D3F4C4005916C7 /* MXFilterTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXFilterTests.m; sourceTree = ""; }; @@ -2542,8 +2333,6 @@ 32AF9283240EA2430008A0FD /* MXSecretShareRequest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXSecretShareRequest.m; sourceTree = ""; }; 32AF9288240EA3880008A0FD /* MXSecretShareSend.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXSecretShareSend.h; sourceTree = ""; }; 32AF9289240EA3880008A0FD /* MXSecretShareSend.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXSecretShareSend.m; sourceTree = ""; }; - 32AF928E24110ADD0008A0FD /* MXSecretShareManager_Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXSecretShareManager_Private.h; sourceTree = ""; }; - 32AF9291241112850008A0FD /* MXCryptoSecretShareTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXCryptoSecretShareTests.m; sourceTree = ""; }; 32AF929524115D8B0008A0FD /* MXPendingSecretShareRequest.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXPendingSecretShareRequest.h; sourceTree = ""; }; 32AF929624115D8B0008A0FD /* MXPendingSecretShareRequest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXPendingSecretShareRequest.m; sourceTree = ""; }; 32B090E1261F709B002924AA /* MXAsyncTaskQueue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXAsyncTaskQueue.swift; sourceTree = ""; }; @@ -2585,11 +2374,6 @@ 32C6F93219DD814400EA4E9C /* MatrixSDK.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MatrixSDK.h; sourceTree = ""; }; 32C6F93819DD814400EA4E9C /* MatrixSDKTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MatrixSDKTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 32C6F93B19DD814400EA4E9C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 32C78B65256CFC4D008130B1 /* MXCryptoVersion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXCryptoVersion.h; sourceTree = ""; }; - 32C78B66256CFC4D008130B1 /* MXCryptoMigration.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXCryptoMigration.m; sourceTree = ""; }; - 32C78B67256CFC4D008130B1 /* MXCryptoMigration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXCryptoMigration.h; sourceTree = ""; }; - 32C78BA6256D227D008130B1 /* MXCryptoMigrationTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXCryptoMigrationTests.m; sourceTree = ""; }; - 32C9B71723E81A1C00C6F30A /* MXCrossSigningVerificationTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXCrossSigningVerificationTests.m; sourceTree = ""; }; 32CAB1051A91EA34008C5BB9 /* MXPushRuleRoomMemberCountConditionChecker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXPushRuleRoomMemberCountConditionChecker.h; sourceTree = ""; }; 32CAB1061A91EA34008C5BB9 /* MXPushRuleRoomMemberCountConditionChecker.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXPushRuleRoomMemberCountConditionChecker.m; sourceTree = ""; }; 32CAB1091A925B41008C5BB9 /* MXHTTPOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXHTTPOperation.h; sourceTree = ""; }; @@ -2602,8 +2386,6 @@ 32CEEF4723B0A8170039BA98 /* MXCrossSigningTools.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXCrossSigningTools.h; sourceTree = ""; }; 32CEEF4823B0A8170039BA98 /* MXCrossSigningTools.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXCrossSigningTools.m; sourceTree = ""; }; 32CEEF4D23B0AB030039BA98 /* MXCrossSigning.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXCrossSigning.h; sourceTree = ""; }; - 32CEEF4E23B0AB030039BA98 /* MXCrossSigning.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXCrossSigning.m; sourceTree = ""; }; - 32CEEF5323B0AB1C0039BA98 /* MXCrossSigning_Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXCrossSigning_Private.h; sourceTree = ""; }; 32CF439B2371AF9500907C56 /* MXWellknownIntegrations.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXWellknownIntegrations.h; sourceTree = ""; }; 32CF439C2371AF9500907C56 /* MXWellknownIntegrations.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXWellknownIntegrations.m; sourceTree = ""; }; 32D2CBFF23422462002BD8CA /* MX3PidAddSession.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MX3PidAddSession.m; sourceTree = ""; }; @@ -2642,8 +2424,6 @@ 32F945F41FAB83D900622468 /* MXIncomingRoomKeyRequestCancellation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXIncomingRoomKeyRequestCancellation.h; sourceTree = ""; }; 32F9FA7B1DBA0CF0009D98A6 /* MXDecryptionResult.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXDecryptionResult.h; sourceTree = ""; }; 32F9FA7C1DBA0CF0009D98A6 /* MXDecryptionResult.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXDecryptionResult.m; sourceTree = ""; }; - 32FA10BF1FA1C9EE00E54233 /* MXOutgoingRoomKeyRequestManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXOutgoingRoomKeyRequestManager.h; sourceTree = ""; }; - 32FA10C01FA1C9EE00E54233 /* MXOutgoingRoomKeyRequestManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXOutgoingRoomKeyRequestManager.m; sourceTree = ""; }; 32FA10C81FA1C9F700E54233 /* MXOutgoingRoomKeyRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXOutgoingRoomKeyRequest.h; sourceTree = ""; }; 32FA10C91FA1C9F700E54233 /* MXOutgoingRoomKeyRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXOutgoingRoomKeyRequest.m; sourceTree = ""; }; 32FCAB4C19E578860049C555 /* MXRestClientTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = MXRestClientTests.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; @@ -2668,10 +2448,6 @@ 3A2A3237291031A7005EF477 /* MXThreadsNotificationCountTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXThreadsNotificationCountTests.swift; sourceTree = ""; }; 3A4BB661291D93EA006F7585 /* MXRoomEventFilterUnitTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXRoomEventFilterUnitTests.swift; sourceTree = ""; }; 3A5787A428982D4600A0D8A8 /* MXBreadcrumbsRoomListDataFetcher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MXBreadcrumbsRoomListDataFetcher.swift; sourceTree = ""; }; - 3A59A49B25A7A16E00DDA1FC /* MXOlmOutboundGroupSession.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXOlmOutboundGroupSession.h; sourceTree = ""; }; - 3A59A49C25A7A16F00DDA1FC /* MXOlmOutboundGroupSession.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXOlmOutboundGroupSession.m; sourceTree = ""; }; - 3A59A52725A7B1B000DDA1FC /* MXOutboundSessionInfo.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXOutboundSessionInfo.h; sourceTree = ""; }; - 3A59A52825A7B1B000DDA1FC /* MXOutboundSessionInfo.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXOutboundSessionInfo.m; sourceTree = ""; }; 3A7509BA26FC61DF00B85773 /* MXSpaceNotificationCounter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MXSpaceNotificationCounter.swift; sourceTree = ""; }; 3A858DD92750EE3F006322C1 /* MXHomeserverCapabilitiesService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXHomeserverCapabilitiesService.swift; sourceTree = ""; }; 3A858DDB275120D1006322C1 /* MXHomeserverCapabilitiesTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXHomeserverCapabilitiesTests.swift; sourceTree = ""; }; @@ -2711,12 +2487,20 @@ 9274AFE71EE580240009BEB6 /* MXCallKitAdapter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXCallKitAdapter.m; sourceTree = ""; }; A0A23978295202930001F722 /* MXAggregatedPollsUpdater.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXAggregatedPollsUpdater.swift; sourceTree = ""; }; A0B1217E295306F700E704C2 /* MXAggregatedPollsUpdaterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXAggregatedPollsUpdaterTests.swift; sourceTree = ""; }; + A71E55302CA6FEBC00B06B17 /* MXKeyBackupPassword.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXKeyBackupPassword.h; sourceTree = ""; }; + A71E55312CA6FEBC00B06B17 /* MXKeyBackupPassword.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXKeyBackupPassword.m; sourceTree = ""; }; + A759E23C2C98EE7D002429A8 /* MXCrypto.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXCrypto.m; sourceTree = ""; }; A75CAD682A9796DE00F06072 /* MXWellKnownAuthentication.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXWellKnownAuthentication.m; sourceTree = ""; }; A75CAD6B2A97970500F06072 /* MXWellKnownAuthentication.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXWellKnownAuthentication.h; sourceTree = ""; }; + A777E5742C98D6F100B39397 /* MXKeyVerificationManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXKeyVerificationManager.m; sourceTree = ""; }; + A777E5772C98D79D00B39397 /* MXQRCodeTransaction.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXQRCodeTransaction.m; sourceTree = ""; }; + A777E57A2C98D82500B39397 /* MXKeyVerificationRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXKeyVerificationRequest.m; sourceTree = ""; }; + A777E57D2C98D8A500B39397 /* MXKeyVerificationTransaction.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXKeyVerificationTransaction.m; sourceTree = ""; }; + A777E5832C98DF1900B39397 /* MXCrossSigning.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXCrossSigning.m; sourceTree = ""; }; + A777E58C2C98E21800B39397 /* MXCryptoVersion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXCryptoVersion.h; sourceTree = ""; }; A780624C27B2CE74005780C0 /* FileManager+Backup.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "FileManager+Backup.swift"; sourceTree = ""; }; A780624D27B2CE74005780C0 /* FileManager+AppGroupContainer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "FileManager+AppGroupContainer.swift"; sourceTree = ""; }; A7BB11972C933677002F7FA5 /* PKMessageWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PKMessageWrapper.swift; sourceTree = ""; }; - A816247B25F60C7700A46F05 /* MXDeviceListOperationsPoolUnitTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXDeviceListOperationsPoolUnitTests.swift; sourceTree = ""; }; B105CD9C261E0B70006EB204 /* MXSpaceChildrenSummary.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXSpaceChildrenSummary.swift; sourceTree = ""; }; B105CDD4261F54C8006EB204 /* MXSpaceChildContent.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXSpaceChildContent.h; sourceTree = ""; }; B105CDD5261F54C8006EB204 /* MXSpaceChildContent.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXSpaceChildContent.m; sourceTree = ""; }; @@ -2823,8 +2607,6 @@ B18B0E6625FBDC2F00E32151 /* MXSpace.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXSpace.swift; sourceTree = ""; }; B190AC802833FA8900D6F0C4 /* MXBeaconInfoSummaryRealmStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXBeaconInfoSummaryRealmStore.swift; sourceTree = ""; }; B19A3099240424BD00FB6F35 /* MXQRCodeTransaction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXQRCodeTransaction.h; sourceTree = ""; }; - B19A309A240424BD00FB6F35 /* MXQRCodeTransaction_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXQRCodeTransaction_Private.h; sourceTree = ""; }; - B19A309B240424BD00FB6F35 /* MXQRCodeTransaction.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXQRCodeTransaction.m; sourceTree = ""; }; B19A30A22404257600FB6F35 /* MXQRCodeKeyVerificationStart.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXQRCodeKeyVerificationStart.m; sourceTree = ""; }; B19A30A32404257600FB6F35 /* MXSASKeyVerificationStart.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXSASKeyVerificationStart.m; sourceTree = ""; }; B19A30A42404257700FB6F35 /* MXQRCodeKeyVerificationStart.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXQRCodeKeyVerificationStart.h; sourceTree = ""; }; @@ -2848,11 +2630,9 @@ B1A026F526161EF5001AADFF /* MXSpaceChildSummaryResponse.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXSpaceChildSummaryResponse.m; sourceTree = ""; }; B1A026FE26162110001AADFF /* MXSpaceChildrenResponse.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXSpaceChildrenResponse.h; sourceTree = ""; }; B1A026FF26162110001AADFF /* MXSpaceChildrenResponse.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXSpaceChildrenResponse.m; sourceTree = ""; }; - B1B44318283D00CA00BB26F4 /* MXMegolmDecryptionUnitTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MXMegolmDecryptionUnitTests.swift; sourceTree = ""; }; B1C854ED25E7B492005867D0 /* MXRoomType.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXRoomType.h; sourceTree = ""; }; B1D50DD1288EB34900DD1AA3 /* MXBeaconInfoSummaryDeletionAllRoomListener.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MXBeaconInfoSummaryDeletionAllRoomListener.swift; sourceTree = ""; }; B1D50DD2288EB34900DD1AA3 /* MXBeaconInfoSummaryDeletionPerRoomListener.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MXBeaconInfoSummaryDeletionPerRoomListener.swift; sourceTree = ""; }; - B1DDC9D52418098200D208E3 /* MXIncomingSASTransaction_Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXIncomingSASTransaction_Private.h; sourceTree = ""; }; B1E09A0E2397FA950057C069 /* MatrixSDKTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MatrixSDKTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; B1EE98C42804697400AB63F0 /* MXBeacon.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXBeacon.h; sourceTree = ""; }; B1EE98C52804697400AB63F0 /* MXBeacon.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXBeacon.m; sourceTree = ""; }; @@ -2949,11 +2729,6 @@ EC383BC12542F251002FBBE6 /* MatrixSDKTests-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "MatrixSDKTests-Bridging-Header.h"; sourceTree = ""; }; EC403833289A672A0067D5B8 /* MXAes256BackupAuthData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXAes256BackupAuthData.h; sourceTree = ""; }; EC403836289A67440067D5B8 /* MXAes256BackupAuthData.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXAes256BackupAuthData.m; sourceTree = ""; }; - EC403841289A70FC0067D5B8 /* MXKeyBackupAlgorithm.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXKeyBackupAlgorithm.h; sourceTree = ""; }; - EC403842289A7F0F0067D5B8 /* MXCurve25519KeyBackupAlgorithm.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXCurve25519KeyBackupAlgorithm.h; sourceTree = ""; }; - EC403843289A7F0F0067D5B8 /* MXCurve25519KeyBackupAlgorithm.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXCurve25519KeyBackupAlgorithm.m; sourceTree = ""; }; - EC403848289A7F260067D5B8 /* MXAes256KeyBackupAlgorithm.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXAes256KeyBackupAlgorithm.h; sourceTree = ""; }; - EC403849289A7F260067D5B8 /* MXAes256KeyBackupAlgorithm.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXAes256KeyBackupAlgorithm.m; sourceTree = ""; }; EC40384E289A906A0067D5B8 /* MXBaseKeyBackupAuthData.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXBaseKeyBackupAuthData.h; sourceTree = ""; }; EC403852289ACDBB0067D5B8 /* MXKeyBackupPreparationInfo.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXKeyBackupPreparationInfo.h; sourceTree = ""; }; EC403853289ACF5E0067D5B8 /* MXKeyBackupPreparationInfo.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXKeyBackupPreparationInfo.m; sourceTree = ""; }; @@ -3101,23 +2876,18 @@ ED1AE9292881AC7100D3432A /* MXWarnings.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXWarnings.h; sourceTree = ""; }; ED1FE9052912D2EB0046F722 /* MXRoomEventDecryptionUnitTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXRoomEventDecryptionUnitTests.swift; sourceTree = ""; }; ED1FE90A2912E13A0046F722 /* DecryptedEvent+Stub.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DecryptedEvent+Stub.swift"; sourceTree = ""; }; - ED21F68428104DA2002FF83D /* MXMegolmEncryptionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXMegolmEncryptionTests.swift; sourceTree = ""; }; ED28068328F06C6C0070AE9F /* QrCodeStub.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QrCodeStub.swift; sourceTree = ""; }; ED28068628F06D360070AE9F /* MXQRCodeTransactionV2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXQRCodeTransactionV2.swift; sourceTree = ""; }; ED2DD111286C450600F06731 /* MXCryptoMachine.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MXCryptoMachine.swift; sourceTree = ""; }; ED2DD113286C450600F06731 /* MXCryptoRequests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MXCryptoRequests.swift; sourceTree = ""; }; ED2DD11B286C4F3E00F06731 /* MXCryptoRequestsUnitTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXCryptoRequestsUnitTests.swift; sourceTree = ""; }; - ED35652B281150310002BF6A /* MXOlmInboundGroupSessionUnitTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXOlmInboundGroupSessionUnitTests.swift; sourceTree = ""; }; ED35652E281153480002BF6A /* MXMegolmSessionDataUnitTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXMegolmSessionDataUnitTests.swift; sourceTree = ""; }; ED36ED8528DD9E2100C86416 /* MXCryptoKeyBackupEngine.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MXCryptoKeyBackupEngine.swift; sourceTree = ""; }; ED37834829C9B6E700A449DA /* MXEventDecryptionDecoration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MXEventDecryptionDecoration.swift; sourceTree = ""; }; ED4114E7292E496C00728459 /* MXBackgroundCrypto.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXBackgroundCrypto.swift; sourceTree = ""; }; ED4114EA292E498100728459 /* MXBackgroundCryptoV2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXBackgroundCryptoV2.swift; sourceTree = ""; }; - ED4114ED292E49C000728459 /* MXLegacyBackgroundCrypto.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXLegacyBackgroundCrypto.swift; sourceTree = ""; }; - ED4368B029784CCE002B6272 /* MXRealmCryptoStoreTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXRealmCryptoStoreTests.swift; sourceTree = ""; }; ED44F01028180BCC00452A5D /* MXSharedHistoryKeyRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MXSharedHistoryKeyRequest.swift; sourceTree = ""; }; ED44F01328180EAB00452A5D /* MXSharedHistoryKeyManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MXSharedHistoryKeyManager.swift; sourceTree = ""; }; - ED44F01728180F1C00452A5D /* MXSharedHistoryKeyManagerUnitTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXSharedHistoryKeyManagerUnitTests.swift; sourceTree = ""; }; ED463ECA29B0B75800957941 /* EventEncryptionAlgorithm+String.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "EventEncryptionAlgorithm+String.swift"; sourceTree = ""; }; ED463ECD29B0B8E000957941 /* MXRoomSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXRoomSettings.swift; sourceTree = ""; }; ED47CB6C28523995004FD755 /* MXCryptoV2.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MXCryptoV2.swift; sourceTree = ""; }; @@ -3127,9 +2897,6 @@ ED51943B284630090006EEC6 /* MXRestClientStub.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXRestClientStub.m; sourceTree = ""; }; ED51943E284630100006EEC6 /* MXRestClientStub.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXRestClientStub.h; sourceTree = ""; }; ED555F58298BB27200C5BD63 /* MXKeysQueryResponseUnitTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXKeysQueryResponseUnitTests.swift; sourceTree = ""; }; - ED558067296F0361003443E3 /* MXCryptoMigrationStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXCryptoMigrationStore.swift; sourceTree = ""; }; - ED55806C296F0E3A003443E3 /* MXCryptoMigrationStoreUnitTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXCryptoMigrationStoreUnitTests.swift; sourceTree = ""; }; - ED55806F296F1BEE003443E3 /* MXCryptoMigrationV2UnitTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXCryptoMigrationV2UnitTests.swift; sourceTree = ""; }; ED5580722970265A003443E3 /* MXCryptoSDKLogger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXCryptoSDKLogger.swift; sourceTree = ""; }; ED55807529709943003443E3 /* MatrixSDKTestsE2EData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MatrixSDKTestsE2EData.swift; sourceTree = ""; }; ED5580782970A879003443E3 /* MatrixSDKTestsData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MatrixSDKTestsData.swift; sourceTree = ""; }; @@ -3142,7 +2909,6 @@ ED5C753928B3E80300D24E85 /* MXLogObjcWrapper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXLogObjcWrapper.m; sourceTree = ""; }; ED5C753A28B3E80300D24E85 /* MXLogger.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXLogger.m; sourceTree = ""; }; ED5C753B28B3E80300D24E85 /* MXLogObjcWrapper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXLogObjcWrapper.h; sourceTree = ""; }; - ED5C95CD2833E85600843D82 /* MXOlmDeviceUnitTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXOlmDeviceUnitTests.swift; sourceTree = ""; }; ED5EF144297AB1F200A5ADDA /* MXRoomEventEncryption.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXRoomEventEncryption.swift; sourceTree = ""; }; ED5EF148297AB29F00A5ADDA /* MXDeviceVerification+LocalTrust.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "MXDeviceVerification+LocalTrust.swift"; sourceTree = ""; }; ED5EF149297AB29F00A5ADDA /* MXRoomHistoryVisibility+HistoryVisibility.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "MXRoomHistoryVisibility+HistoryVisibility.swift"; sourceTree = ""; }; @@ -3150,13 +2916,10 @@ ED5EF151297AB33E00A5ADDA /* MXCryptoV2Factory.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MXCryptoV2Factory.swift; sourceTree = ""; }; ED5EF154297AB93800A5ADDA /* MXRoomEventEncryptionUnitTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXRoomEventEncryptionUnitTests.swift; sourceTree = ""; }; ED647E3D292CE64400A47519 /* MXSessionStartupProgress.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXSessionStartupProgress.swift; sourceTree = ""; }; - ED6DABFB28C7542800ECDCB6 /* MXRoomKeyInfoFactory.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MXRoomKeyInfoFactory.swift; sourceTree = ""; }; ED6DAC0128C76F0A00ECDCB6 /* MXRoomKeyInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXRoomKeyInfo.swift; sourceTree = ""; }; ED6DAC0628C77E1100ECDCB6 /* MXForwardedRoomKeyEventContentUnitTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXForwardedRoomKeyEventContentUnitTests.swift; sourceTree = ""; }; ED6DAC0928C784AE00ECDCB6 /* Dictionary.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Dictionary.swift; sourceTree = ""; }; ED6DAC0C28C784E200ECDCB6 /* MXRoomKeyEventContentUnitTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXRoomKeyEventContentUnitTests.swift; sourceTree = ""; }; - ED6DAC1028C788A700ECDCB6 /* MXRoomKeyInfoFactoryUnitTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXRoomKeyInfoFactoryUnitTests.swift; sourceTree = ""; }; - ED6DAC1428C78D4000ECDCB6 /* MXMemoryCryptoStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXMemoryCryptoStore.swift; sourceTree = ""; }; ED6DAC1728C799E300ECDCB6 /* MXRoomKeyResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXRoomKeyResult.swift; sourceTree = ""; }; ED6DAC1A28C79AA300ECDCB6 /* MXUnrequestedForwardedRoomKeyManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MXUnrequestedForwardedRoomKeyManager.swift; sourceTree = ""; }; ED6DAC1D28C79D2000ECDCB6 /* MXUnrequestedForwardedRoomKeyManagerUnitTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXUnrequestedForwardedRoomKeyManagerUnitTests.swift; sourceTree = ""; }; @@ -3174,7 +2937,6 @@ ED751DAD28EDEC7E003748C3 /* MXKeyVerificationStateResolverUnitTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXKeyVerificationStateResolverUnitTests.swift; sourceTree = ""; }; ED76A4AC28EDA2CE00036FF0 /* MXKeyVerificationStateResolver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXKeyVerificationStateResolver.swift; sourceTree = ""; }; ED79B9842940BB45008952F6 /* MXToDevicePayloadUnitTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXToDevicePayloadUnitTests.swift; sourceTree = ""; }; - ED825F8E29014EDA006A614E /* MXSession+LegacyCrypto.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MXSession+LegacyCrypto.swift"; sourceTree = ""; }; ED88998F27F2065C00718486 /* MXRoomAliasResolution.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXRoomAliasResolution.h; sourceTree = ""; }; ED88999027F2065D00718486 /* MXRoomAliasResolution.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXRoomAliasResolution.m; sourceTree = ""; }; ED8943D327E34762000FC39C /* MXMemoryRoomStoreUnitTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXMemoryRoomStoreUnitTests.swift; sourceTree = ""; }; @@ -3195,7 +2957,6 @@ EDA40A0B29E9E2BF00C0CAB9 /* legacy_deprecated3_account.realm */ = {isa = PBXFileReference; lastKnownFileType = file; path = legacy_deprecated3_account.realm; sourceTree = ""; }; EDA40A0C29E9E2BF00C0CAB9 /* legacy_deprecated1_account.realm */ = {isa = PBXFileReference; lastKnownFileType = file; path = legacy_deprecated1_account.realm; sourceTree = ""; }; EDA40A0D29E9E2BF00C0CAB9 /* archived_encrypted_event */ = {isa = PBXFileReference; lastKnownFileType = file.bplist; path = archived_encrypted_event; sourceTree = ""; }; - EDA40A0E29E9E2BF00C0CAB9 /* LegacyRealmStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LegacyRealmStore.swift; sourceTree = ""; }; EDA6933F290BA92E00223252 /* MXCryptoMachineUnitTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXCryptoMachineUnitTests.swift; sourceTree = ""; }; EDAAC41228E2F86800DD89B5 /* MXCryptoSecretStore.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXCryptoSecretStore.h; sourceTree = ""; }; EDAAC41828E2FCFE00DD89B5 /* MXCryptoSecretStoreV2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXCryptoSecretStoreV2.swift; sourceTree = ""; }; @@ -3208,9 +2969,6 @@ EDC8C4072968A993003792C5 /* MXKeysQueryScheduler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXKeysQueryScheduler.swift; sourceTree = ""; }; EDC8C40A2968A9F7003792C5 /* MXKeysQuerySchedulerUnitTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXKeysQuerySchedulerUnitTests.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 = ""; }; - EDD4198028DCAA7B007F3757 /* MXNativeKeyBackupEngine.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXNativeKeyBackupEngine.m; sourceTree = ""; }; EDD578DC2881C37C006739DD /* MXDeviceInfoSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MXDeviceInfoSource.swift; sourceTree = ""; }; EDD578DD2881C37C006739DD /* MXTrustLevelSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MXTrustLevelSource.swift; sourceTree = ""; }; EDD578DE2881C37C006739DD /* MXCrossSigningInfoSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MXCrossSigningInfoSource.swift; sourceTree = ""; }; @@ -3219,9 +2977,7 @@ EDD578EB2881C38C006739DD /* MXCrossSigningV2.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MXCrossSigningV2.swift; sourceTree = ""; }; EDD7B74629CB3F1B00548AB4 /* MXCrossSigningInfo_v1 */ = {isa = PBXFileReference; lastKnownFileType = file.bplist; path = MXCrossSigningInfo_v1; sourceTree = ""; }; EDD7B74729CB3F1B00548AB4 /* MXCrossSigningInfo_v0 */ = {isa = PBXFileReference; lastKnownFileType = file.bplist; path = MXCrossSigningInfo_v0; sourceTree = ""; }; - EDDB07C9297EE0A7005249A6 /* MXCryptoV2FactoryUnitTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXCryptoV2FactoryUnitTests.swift; sourceTree = ""; }; EDDBA7EF293F353900AD1480 /* MXToDevicePayload.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXToDevicePayload.swift; sourceTree = ""; }; - EDDD90C72901611600B760E0 /* MXLegacyCrypto+LegacyCrossSigning.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MXLegacyCrypto+LegacyCrossSigning.swift"; sourceTree = ""; }; EDE1B13A28B7BEAB000DEEE8 /* MXCrossSigningV2UnitTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXCrossSigningV2UnitTests.swift; sourceTree = ""; }; EDE70DC728DA22F800099736 /* MXKeyBackupEngine.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXKeyBackupEngine.h; sourceTree = ""; }; EDF154E0296C203E004D7FFE /* MXCryptoMachineStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXCryptoMachineStore.swift; sourceTree = ""; }; @@ -3349,13 +3105,8 @@ isa = PBXGroup; children = ( 320B3938239FD15E00BE2C06 /* MXKeyVerificationRequest.h */, - 320B393E239FD48400BE2C06 /* MXKeyVerificationRequest_Private.h */, - 320B3939239FD15E00BE2C06 /* MXKeyVerificationRequest.m */, + A777E57A2C98D82500B39397 /* MXKeyVerificationRequest.m */, ED7019E72886C33100FC31B9 /* MXKeyVerificationRequestV2.swift */, - 320B3932239FA56900BE2C06 /* MXKeyVerificationByDMRequest.h */, - 320B3933239FA56900BE2C06 /* MXKeyVerificationByDMRequest.m */, - 3274538823FD918800438328 /* MXKeyVerificationByToDeviceRequest.h */, - 3274538923FD918800438328 /* MXKeyVerificationByToDeviceRequest.m */, ); path = Requests; sourceTree = ""; @@ -3556,19 +3307,15 @@ 32BBAE642178E99100D85F46 /* KeyBackup */, 32FA10B21FA1C28100E54233 /* KeySharing */, 3A108A382580E979005EEBE9 /* KeyProvider */, - 32C78B64256CFC4D008130B1 /* Migration */, 32F00AB82488E14300131741 /* Recovery */, 324DD296246AD25E00377005 /* SecretStorage */, 324BE4651E3FADB1008D99D4 /* Utils */, 3252DCAB224BE59E0032264F /* Verification */, ED2DD110286C450600F06731 /* CryptoMachine */, 322A51B41D9AB15900C8536D /* MXCrypto.h */, - 322A51B51D9AB15900C8536D /* MXCrypto.m */, + A759E23C2C98EE7D002429A8 /* MXCrypto.m */, ED47CB6C28523995004FD755 /* MXCryptoV2.swift */, ED5EF151297AB33E00A5ADDA /* MXCryptoV2Factory.swift */, - 325D1C251DFECE0D0070B8BF /* MXCrypto_Private.h */, - 322A51C51D9BBD3C00C8536D /* MXOlmDevice.h */, - 322A51C61D9BBD3C00C8536D /* MXOlmDevice.m */, ); path = Crypto; sourceTree = ""; @@ -3576,12 +3323,7 @@ 322A51C01D9AC8FE00C8536D /* Algorithms */ = { isa = PBXGroup; children = ( - 32A151231DABB0A100400192 /* Megolm */, - 327187821DA7CFF20071C818 /* Olm */, ED1FE9082912D4340046F722 /* RoomEvent */, - 322A51C11D9AC8FE00C8536D /* MXCryptoAlgorithms.h */, - 322A51C21D9AC8FE00C8536D /* MXCryptoAlgorithms.m */, - 3271877C1DA7CB2F0071C818 /* MXDecrypting.h */, 3271877A1DA7CAA60071C818 /* MXEncrypting.h */, 32F9FA7B1DBA0CF0009D98A6 /* MXDecryptionResult.h */, 32F9FA7C1DBA0CF0009D98A6 /* MXDecryptionResult.m */, @@ -3743,8 +3485,7 @@ 3252DCB1224BE6A90032264F /* JSONModels */, 3252DCB0224BE6670032264F /* Data */, 3252DCAC224BE5D40032264F /* MXKeyVerificationManager.h */, - 3252DCAD224BE5D40032264F /* MXKeyVerificationManager.m */, - 3252DCCF224D25810032264F /* MXKeyVerificationManager_Private.h */, + A777E5742C98D6F100B39397 /* MXKeyVerificationManager.m */, ED7019EA2886C33A00FC31B9 /* MXKeyVerificationManagerV2.swift */, ); path = Verification; @@ -3809,8 +3550,7 @@ isa = PBXGroup; children = ( 3252DCB5224BEA610032264F /* MXKeyVerificationTransaction.h */, - 321CFDF12254B9FB004D31DF /* MXKeyVerificationTransaction_Private.h */, - 329E8088224E261600A48C3A /* MXKeyVerificationTransaction.m */, + A777E57D2C98D8A500B39397 /* MXKeyVerificationTransaction.m */, 3252DCB4224BE7390032264F /* SAS */, B19A3098240424BD00FB6F35 /* QRCode */, ); @@ -3821,14 +3561,8 @@ isa = PBXGroup; children = ( 321CFDE422525A49004D31DF /* MXSASTransaction.h */, - 321CFDF022549C39004D31DF /* MXSASTransaction_Private.h */, 321CFDE522525A49004D31DF /* MXSASTransaction.m */, ED7019E42886C32900FC31B9 /* MXSASTransactionV2.swift */, - 321CFDE822525DED004D31DF /* MXIncomingSASTransaction.h */, - B1DDC9D52418098200D208E3 /* MXIncomingSASTransaction_Private.h */, - 321CFDE922525DEE004D31DF /* MXIncomingSASTransaction.m */, - 329E808A224E2E1B00A48C3A /* MXOutgoingSASTransaction.h */, - 329E808B224E2E1B00A48C3A /* MXOutgoingSASTransaction.m */, ); path = SAS; sourceTree = ""; @@ -3836,8 +3570,6 @@ 3259CD4C1DF85F6D00186944 /* MXRealmCryptoStore */ = { isa = PBXGroup; children = ( - 3259CD511DF860C300186944 /* MXRealmCryptoStore.h */, - 3259CD521DF860C300186944 /* MXRealmCryptoStore.m */, ); path = MXRealmCryptoStore; sourceTree = ""; @@ -3882,17 +3614,6 @@ path = Data; sourceTree = ""; }; - 327187821DA7CFF20071C818 /* Olm */ = { - isa = PBXGroup; - children = ( - 327187831DA7D0220071C818 /* MXOlmDecryption.h */, - 327187841DA7D0220071C818 /* MXOlmDecryption.m */, - 327187871DA7DCE50071C818 /* MXOlmEncryption.h */, - 327187881DA7DCE50071C818 /* MXOlmEncryption.m */, - ); - path = Olm; - sourceTree = ""; - }; 32720D98222EAA6F0086FFF5 /* AutoDiscovery */ = { isa = PBXGroup; children = ( @@ -4086,7 +3807,7 @@ 3284A59C1DB7C00600A09972 /* Store */ = { isa = PBXGroup; children = ( - 3284A59D1DB7C00600A09972 /* MXCryptoStore.h */, + A777E58C2C98E21800B39397 /* MXCryptoVersion.h */, EDAAC41228E2F86800DD89B5 /* MXCryptoSecretStore.h */, 3259CD4C1DF85F6D00186944 /* MXRealmCryptoStore */, ); @@ -4124,8 +3845,6 @@ children = ( 3297912523A93D4B00F7BB9B /* MXKeyVerification.h */, 3297912623A93D4B00F7BB9B /* MXKeyVerification.m */, - 3297912C23AA126400F7BB9B /* MXKeyVerificationStatusResolver.h */, - 3297912D23AA126400F7BB9B /* MXKeyVerificationStatusResolver.m */, ED76A4AC28EDA2CE00036FF0 /* MXKeyVerificationStateResolver.swift */, ); path = Status; @@ -4154,17 +3873,6 @@ path = Push; sourceTree = ""; }; - 32A151231DABB0A100400192 /* Megolm */ = { - isa = PBXGroup; - children = ( - 32A151241DABB0CB00400192 /* MXMegolmDecryption.h */, - 32A151251DABB0CB00400192 /* MXMegolmDecryption.m */, - 32A151371DAD292400400192 /* MXMegolmEncryption.h */, - 32A151381DAD292400400192 /* MXMegolmEncryption.m */, - ); - name = Megolm; - sourceTree = ""; - }; 32A1513B1DAF768D00400192 /* Data */ = { isa = PBXGroup; children = ( @@ -4175,20 +3883,10 @@ 021AFBA12179E91800742B2C /* MXEncryptedContentKey.m */, 3256E37F1DCB91EB003C9718 /* MXCryptoConstants.h */, 3256E3801DCB91EB003C9718 /* MXCryptoConstants.m */, - 322691301E5EF77D00966A6E /* MXDeviceListOperation.h */, - 322691311E5EF77D00966A6E /* MXDeviceListOperation.m */, - 322691341E5EFF8700966A6E /* MXDeviceListOperationsPool.h */, - 322691351E5EFF8700966A6E /* MXDeviceListOperationsPool.m */, F03EF5061DF071D5009DF592 /* MXEncryptedAttachments.h */, F03EF5071DF071D5009DF592 /* MXEncryptedAttachments.m */, 32A151421DAF7C0C00400192 /* MXKey.h */, 32A151431DAF7C0C00400192 /* MXKey.m */, - 32A1513C1DAF768D00400192 /* MXOlmInboundGroupSession.h */, - 32A1513D1DAF768D00400192 /* MXOlmInboundGroupSession.m */, - 3A59A49B25A7A16E00DDA1FC /* MXOlmOutboundGroupSession.h */, - 3A59A49C25A7A16F00DDA1FC /* MXOlmOutboundGroupSession.m */, - 3A59A52725A7B1B000DDA1FC /* MXOutboundSessionInfo.h */, - 3A59A52825A7B1B000DDA1FC /* MXOutboundSessionInfo.m */, 32A1514C1DAF897600400192 /* MXOlmSessionResult.h */, 32A1514D1DAF897600400192 /* MXOlmSessionResult.m */, 32A151501DAF8A7200400192 /* MXQueuedEncryption.h */, @@ -4222,7 +3920,6 @@ 32AF9281240EA03F0008A0FD /* JSONModels */, 32AF927B240EA0190008A0FD /* MXSecretShareManager.m */, 32AF927C240EA0190008A0FD /* MXSecretShareManager.h */, - 32AF928E24110ADD0008A0FD /* MXSecretShareManager_Private.h */, ); path = Secret; sourceTree = ""; @@ -4250,16 +3947,15 @@ 32BBAE642178E99100D85F46 /* KeyBackup */ = { isa = PBXGroup; children = ( + A71E55302CA6FEBC00B06B17 /* MXKeyBackupPassword.h */, + A71E55312CA6FEBC00B06B17 /* MXKeyBackupPassword.m */, EDE70DC628DA22E200099736 /* Engine */, 32BBAE652178E99100D85F46 /* Data */, 32BBAE722179CF4000D85F46 /* MXKeyBackup.h */, 32BBAE732179CF4000D85F46 /* MXKeyBackup.m */, - EC403841289A70FC0067D5B8 /* MXKeyBackupAlgorithm.h */, 32FFB4ED217DC0E900C96002 /* MXKeyBackup_Private.h */, 32FFB4EE217E146A00C96002 /* MXRecoveryKey.h */, 32FFB4EF217E146A00C96002 /* MXRecoveryKey.m */, - 32442FAF21EDD21300D2411B /* MXKeyBackupPassword.h */, - 32442FB021EDD21300D2411B /* MXKeyBackupPassword.m */, ); path = KeyBackup; sourceTree = ""; @@ -4387,11 +4083,8 @@ 32720DA1222EB5650086FFF5 /* MXAutoDiscoveryTests.m */, EC40386028A1A3830067D5B8 /* MXBaseKeyBackupTests.m */, 32CEEF3C23AD134A0039BA98 /* MXCrossSigningTests.m */, - 32C9B71723E81A1C00C6F30A /* MXCrossSigningVerificationTests.m */, 329E808E22512DF500A48C3A /* MXCryptoKeyVerificationTests.m */, - 32C78BA6256D227D008130B1 /* MXCryptoMigrationTests.m */, 321EA11C24893A0400E35B02 /* MXCryptoRecoveryServiceTests.m */, - 32AF9291241112850008A0FD /* MXCryptoSecretShareTests.m */, 324DD2BA246C3ADE00377005 /* MXCryptoSecretStorageTests.m */, 322D01C322492B0700150C68 /* MXCryptoShareTests.m */, 322A51D71D9E846800C8536D /* MXCryptoTests.m */, @@ -4445,7 +4138,6 @@ ECDBE69228E5E16F000C83AF /* MXClientInformationServiceUnitTests.swift */, EC0B944527206D0B00B4D440 /* MXCoreDataRoomListDataManagerUnitTests.swift */, ECB6FA8D267CFF4300A941E4 /* MXCredentialsUnitTests.swift */, - A816247B25F60C7700A46F05 /* MXDeviceListOperationsPoolUnitTests.swift */, 327E9ACE2284783E00A98BC1 /* MXEventAnnotationUnitTests.swift */, 32B0E3E623A3864C0054FF1A /* MXEventReferenceUnitTests.swift */, B1EE98D328048ACF00AB63F0 /* MXGeoURIComponentsUnitTests.swift */, @@ -4490,26 +4182,13 @@ name = "Supporting Files"; sourceTree = ""; }; - 32C78B64256CFC4D008130B1 /* Migration */ = { - isa = PBXGroup; - children = ( - ED558066296F034F003443E3 /* Data */, - 32C78B65256CFC4D008130B1 /* MXCryptoVersion.h */, - 32C78B66256CFC4D008130B1 /* MXCryptoMigration.m */, - 32C78B67256CFC4D008130B1 /* MXCryptoMigration.h */, - EDCF802C2941FF220059E774 /* MXCryptoMigrationV2.swift */, - ); - path = Migration; - sourceTree = ""; - }; 32CEEF3F23AD2A340039BA98 /* CrossSigning */ = { isa = PBXGroup; children = ( 325AD43C23BE3CEA00FF5277 /* Data */, 32CEEF4023AD2A520039BA98 /* JSONModels */, 32CEEF4D23B0AB030039BA98 /* MXCrossSigning.h */, - 32CEEF4E23B0AB030039BA98 /* MXCrossSigning.m */, - 32CEEF5323B0AB1C0039BA98 /* MXCrossSigning_Private.h */, + A777E5832C98DF1900B39397 /* MXCrossSigning.m */, EDD578EB2881C38C006739DD /* MXCrossSigningV2.swift */, 32CEEF4723B0A8170039BA98 /* MXCrossSigningTools.h */, 32CEEF4823B0A8170039BA98 /* MXCrossSigningTools.m */, @@ -4604,11 +4283,7 @@ children = ( 32AF927A240EA0190008A0FD /* Secret */, 32FA10C31FA1C9F700E54233 /* Data */, - 32FA10BF1FA1C9EE00E54233 /* MXOutgoingRoomKeyRequestManager.h */, - 32FA10C01FA1C9EE00E54233 /* MXOutgoingRoomKeyRequestManager.m */, - 32A30B161FB4813400C8309E /* MXIncomingRoomKeyRequestManager.h */, ED6DAC1A28C79AA300ECDCB6 /* MXUnrequestedForwardedRoomKeyManager.swift */, - 32A30B171FB4813400C8309E /* MXIncomingRoomKeyRequestManager.m */, ED44F01328180EAB00452A5D /* MXSharedHistoryKeyManager.swift */, EDBCF335281A8AB900ED5044 /* MXSharedHistoryKeyService.h */, EDBCF338281A8D3D00ED5044 /* MXSharedHistoryKeyService.m */, @@ -4942,8 +4617,7 @@ isa = PBXGroup; children = ( B19A3099240424BD00FB6F35 /* MXQRCodeTransaction.h */, - B19A309A240424BD00FB6F35 /* MXQRCodeTransaction_Private.h */, - B19A309B240424BD00FB6F35 /* MXQRCodeTransaction.m */, + A777E5772C98D79D00B39397 /* MXQRCodeTransaction.m */, ED28068628F06D360070AE9F /* MXQRCodeTransactionV2.swift */, ); path = QRCode; @@ -5153,8 +4827,6 @@ EC383BA1253DE4B1002FBBE6 /* MXBackgroundSyncService.swift */, EC383BB52540E15E002FBBE6 /* MXBackgroundPushRulesManager.swift */, EC383BAB254030DF002FBBE6 /* MXBackgroundStore.swift */, - 3229534F25A5F7220012FCF0 /* MXBackgroundCryptoStore.h */, - 3229535025A5F7220012FCF0 /* MXBackgroundCryptoStore.m */, EC383BA3253DE6C0002FBBE6 /* Store */, ECB5D98B2552C9B4000AD89C /* MXStopwatch.swift */, ); @@ -5185,8 +4857,6 @@ children = ( 320A883E217F4E3E002EA952 /* MXCurve25519BackupAuthData.h */, 320A883F217F4E3F002EA952 /* MXCurve25519BackupAuthData.m */, - EC403842289A7F0F0067D5B8 /* MXCurve25519KeyBackupAlgorithm.h */, - EC403843289A7F0F0067D5B8 /* MXCurve25519KeyBackupAlgorithm.m */, ); path = Curve25519; sourceTree = ""; @@ -5196,8 +4866,6 @@ children = ( EC403833289A672A0067D5B8 /* MXAes256BackupAuthData.h */, EC403836289A67440067D5B8 /* MXAes256BackupAuthData.m */, - EC403848289A7F260067D5B8 /* MXAes256KeyBackupAlgorithm.h */, - EC403849289A7F260067D5B8 /* MXAes256KeyBackupAlgorithm.m */, ); path = Aes256; sourceTree = ""; @@ -5456,10 +5124,6 @@ ED21F67B28104BA1002FF83D /* Algorithms */, ED2DD11A286C4F3100F06731 /* CryptoMachine */, ED7019ED2886CA6C00FC31B9 /* Verification */, - ED5C95CD2833E85600843D82 /* MXOlmDeviceUnitTests.swift */, - ED825F8E29014EDA006A614E /* MXSession+LegacyCrypto.swift */, - EDDD90C72901611600B760E0 /* MXLegacyCrypto+LegacyCrossSigning.swift */, - EDDB07C9297EE0A7005249A6 /* MXCryptoV2FactoryUnitTests.swift */, ); path = Crypto; sourceTree = ""; @@ -5476,8 +5140,6 @@ ED21F67C28104BA7002FF83D /* Megolm */ = { isa = PBXGroup; children = ( - B1B44318283D00CA00BB26F4 /* MXMegolmDecryptionUnitTests.swift */, - ED21F68428104DA2002FF83D /* MXMegolmEncryptionTests.swift */, ); path = Megolm; sourceTree = ""; @@ -5524,7 +5186,6 @@ isa = PBXGroup; children = ( ED6DAC1328C78D3700ECDCB6 /* Store */, - ED35652B281150310002BF6A /* MXOlmInboundGroupSessionUnitTests.swift */, ED35652E281153480002BF6A /* MXMegolmSessionDataUnitTests.swift */, ); path = Data; @@ -5535,7 +5196,6 @@ children = ( ED4114E7292E496C00728459 /* MXBackgroundCrypto.swift */, ED4114EA292E498100728459 /* MXBackgroundCryptoV2.swift */, - ED4114ED292E49C000728459 /* MXLegacyBackgroundCrypto.swift */, ); path = Crypto; sourceTree = ""; @@ -5543,7 +5203,6 @@ ED4368AF29784CA3002B6272 /* MXRealmCryptoStore */ = { isa = PBXGroup; children = ( - ED4368B029784CCE002B6272 /* MXRealmCryptoStoreTests.swift */, ); path = MXRealmCryptoStore; sourceTree = ""; @@ -5552,7 +5211,6 @@ isa = PBXGroup; children = ( ED6DAC0528C77E0600ECDCB6 /* Data */, - ED44F01728180F1C00452A5D /* MXSharedHistoryKeyManagerUnitTests.swift */, ED6DAC1D28C79D2000ECDCB6 /* MXUnrequestedForwardedRoomKeyManagerUnitTests.swift */, ); path = KeySharing; @@ -5583,20 +5241,11 @@ path = Data; sourceTree = ""; }; - ED558066296F034F003443E3 /* Data */ = { - isa = PBXGroup; - children = ( - ED558067296F0361003443E3 /* MXCryptoMigrationStore.swift */, - ); - path = Data; - sourceTree = ""; - }; ED55806A296F0E18003443E3 /* Migration */ = { isa = PBXGroup; children = ( EDA40A0729E9E2BF00C0CAB9 /* LegacyRealmStore */, ED55806B296F0E1D003443E3 /* Data */, - ED55806F296F1BEE003443E3 /* MXCryptoMigrationV2UnitTests.swift */, ); path = Migration; sourceTree = ""; @@ -5604,7 +5253,6 @@ ED55806B296F0E1D003443E3 /* Data */ = { isa = PBXGroup; children = ( - ED55806C296F0E3A003443E3 /* MXCryptoMigrationStoreUnitTests.swift */, ); path = Data; sourceTree = ""; @@ -5639,7 +5287,6 @@ children = ( ED6DAC0128C76F0A00ECDCB6 /* MXRoomKeyInfo.swift */, ED6DAC1728C799E300ECDCB6 /* MXRoomKeyResult.swift */, - ED6DABFB28C7542800ECDCB6 /* MXRoomKeyInfoFactory.swift */, ); path = RoomKeys; sourceTree = ""; @@ -5656,7 +5303,6 @@ ED6DAC0F28C7889A00ECDCB6 /* RoomKeys */ = { isa = PBXGroup; children = ( - ED6DAC1028C788A700ECDCB6 /* MXRoomKeyInfoFactoryUnitTests.swift */, ); path = RoomKeys; sourceTree = ""; @@ -5665,7 +5311,6 @@ isa = PBXGroup; children = ( ED4368AF29784CA3002B6272 /* MXRealmCryptoStore */, - ED6DAC1428C78D4000ECDCB6 /* MXMemoryCryptoStore.swift */, ); path = Store; sourceTree = ""; @@ -5764,8 +5409,6 @@ isa = PBXGroup; children = ( ED8F1D282885A7BC00F897E7 /* Data */, - 32637ED21E5B00400011E20D /* MXDeviceList.h */, - 32637ED31E5B00400011E20D /* MXDeviceList.m */, EDD578DC2881C37C006739DD /* MXDeviceInfoSource.swift */, ); path = Devices; @@ -5816,7 +5459,6 @@ EDA40A0B29E9E2BF00C0CAB9 /* legacy_deprecated3_account.realm */, EDA40A0C29E9E2BF00C0CAB9 /* legacy_deprecated1_account.realm */, EDA40A0D29E9E2BF00C0CAB9 /* archived_encrypted_event */, - EDA40A0E29E9E2BF00C0CAB9 /* LegacyRealmStore.swift */, ); path = LegacyRealmStore; sourceTree = ""; @@ -5854,8 +5496,6 @@ isa = PBXGroup; children = ( EDE70DC728DA22F800099736 /* MXKeyBackupEngine.h */, - EDD4197D28DCAA5F007F3757 /* MXNativeKeyBackupEngine.h */, - EDD4198028DCAA7B007F3757 /* MXNativeKeyBackupEngine.m */, ED36ED8528DD9E2100C86416 /* MXCryptoKeyBackupEngine.swift */, ED6F4EFB2987F0FC007D1191 /* MXEncryptedKeyBackup.swift */, ); @@ -5911,8 +5551,6 @@ EC8A539925B1BC77004E0802 /* MXCallNegotiateEventContent.h in Headers */, 324BE46C1E422766008D99D4 /* MXMegolmSessionData.h in Headers */, 32FA10CE1FA1C9F700E54233 /* MXOutgoingRoomKeyRequest.h in Headers */, - 32A1513E1DAF768D00400192 /* MXOlmInboundGroupSession.h in Headers */, - 3A59A52925A7B1B000DDA1FC /* MXOutboundSessionInfo.h in Headers */, 3271877B1DA7CAA60071C818 /* MXEncrypting.h in Headers */, 32A9F8DE244720B10069C65B /* MXThrottler.h in Headers */, 324DD299246AD2B500377005 /* MXSecretStorage.h in Headers */, @@ -5927,7 +5565,6 @@ B146D49C21A5A04300D8C2C6 /* MXMediaScanStoreDelegate.h in Headers */, 32322A4B1E575F65005DD155 /* MXAllowedCertificates.h in Headers */, B14EECD72577DE7A00448735 /* MXLoginSSOIdentityProvider.h in Headers */, - EC403844289A7F0F0067D5B8 /* MXCurve25519KeyBackupAlgorithm.h in Headers */, 3213301D228B190F0070BA9B /* MXRealmAggregationsMapper.h in Headers */, 32A1515B1DB525DA00400192 /* NSObject+sortedKeys.h in Headers */, 329D3E621E251027002E2F1E /* MXRoomSummaryUpdater.h in Headers */, @@ -5962,7 +5599,6 @@ 32A31BC820D401FC005916C7 /* MXRoomFilter.h in Headers */, 324AAC74239913AD00380A66 /* MXKeyVerificationDone.h in Headers */, B146D47421A5945800D8C2C6 /* MXAntivirusScanStatus.h in Headers */, - 322691361E5EFF8700966A6E /* MXDeviceListOperationsPool.h in Headers */, 3281E8B719E42DFE00976E1A /* MXJSONModel.h in Headers */, EDAAC41C28E30F3C00DD89B5 /* (null) in Headers */, B135066127E9CB6400BD3276 /* MXBeaconInfo.h in Headers */, @@ -5977,23 +5613,18 @@ 325380E8228DAD4A00ADDEFA /* MXAggregatedReactions.h in Headers */, 327E9AE72285A8C400A98BC1 /* MXAggregationPaginatedResponse.h in Headers */, 32CEEF4923B0A8170039BA98 /* MXCrossSigningTools.h in Headers */, - EC403858289C38BA0067D5B8 /* MXKeyBackupAlgorithm.h in Headers */, EC8A53DA25B1BCC6004E0802 /* MXThirdPartyUsersResponse.h in Headers */, EC40385A289C38FB0067D5B8 /* MXKeyBackupPreparationInfo.h in Headers */, 323E0C5B1A306D7A00A31D73 /* MXEvent.h in Headers */, 323F8773255460B5009E9E67 /* MXProfiler.h in Headers */, 327A5F4B239805F600ED6329 /* MXKeyVerificationCancel.h in Headers */, 327F8DB21C6112BA00581CA3 /* MXRoomThirdPartyInvite.h in Headers */, - B19A309E240424BD00FB6F35 /* MXQRCodeTransaction_Private.h in Headers */, - EC40384A289A7F260067D5B8 /* MXAes256KeyBackupAlgorithm.h in Headers */, - EDD4197E28DCAA5F007F3757 /* MXNativeKeyBackupEngine.h in Headers */, 32BBAE742179CF4000D85F46 /* MXKeyBackup.h in Headers */, 92634B821EF2E3C400DB9F60 /* MXCallKitConfiguration.h in Headers */, 32618E7120ED2DF500E1D2EA /* MXFilterJSONModel.h in Headers */, B146D47221A5939100D8C2C6 /* MXRealmHelper.h in Headers */, 32B94E01228EDEBC00716A26 /* MXReactionRelation.h in Headers */, F03EF5001DF014D9009DF592 /* MXMediaManager.h in Headers */, - 3297912E23AA126500F7BB9B /* MXKeyVerificationStatusResolver.h in Headers */, 3245A7521AF7B2930001D8A7 /* MXCallManager.h in Headers */, 32B94E05228EE90300716A26 /* MXRealmReactionRelation.h in Headers */, ECDA763927B6B74C000C48CF /* MXCapabilities.h in Headers */, @@ -6015,6 +5646,7 @@ 32B76EA320FDE2BE00B095F6 /* MXRoomMembersCount.h in Headers */, 32C6F93319DD814400EA4E9C /* MatrixSDK.h in Headers */, 32C474C122AF7A2D00CFBCD2 /* MXReactionOperation.h in Headers */, + A777E58D2C98E21800B39397 /* MXCryptoVersion.h in Headers */, 324AAC6F239913AD00380A66 /* MXKeyVerificationJSONModel.h in Headers */, 327E9AFC228AC22800A98BC1 /* MXAggregationsStore.h in Headers */, 324DD2AC246AEB7B00377005 /* MXSecretStoragePassphrase.h in Headers */, @@ -6023,14 +5655,11 @@ EC8A53DC25B1BCC6004E0802 /* MXThirdPartyProtocol.h in Headers */, ECDA764627BA939E000C48CF /* MXRoomVersionsCapability.h in Headers */, 325AD43F23BE3E7500FF5277 /* MXCrossSigningInfo.h in Headers */, - 32AF928F24110ADD0008A0FD /* MXSecretShareManager_Private.h in Headers */, ED5C753E28B3E80300D24E85 /* MXLog.h in Headers */, ECD289B426F9F00E00F268CF /* MXRoomSummarySentStatus.h in Headers */, 021AFBA62179E91900742B2C /* MXEncryptedContentKey.h in Headers */, - 327187891DA7DCE50071C818 /* MXOlmEncryption.h in Headers */, 32A1514E1DAF897600400192 /* MXOlmSessionResult.h in Headers */, 320BBF3C1D6C7D9D0079890E /* MXEventsEnumerator.h in Headers */, - 32637ED41E5B00400011E20D /* MXDeviceList.h in Headers */, 3245A7501AF7B2930001D8A7 /* MXCall.h in Headers */, EC60ED7B265CFCD100B39A4E /* MXDeviceListResponse.h in Headers */, 180F858427A2AF3000F4E5A5 /* MXWellKnownTileServerConfig.h in Headers */, @@ -6043,7 +5672,6 @@ 32114A851A262CE000FF2EC4 /* MXStore.h in Headers */, EC60ED67265CFC7200B39A4E /* MXPresenceSyncResponse.h in Headers */, 3AC13802264482A100EE1E74 /* MXExportedOlmDevice.h in Headers */, - 32A151391DAD292400400192 /* MXMegolmEncryption.h in Headers */, EC8A538B25B1BC77004E0802 /* MXCallCandidate.h in Headers */, F03EF5041DF01596009DF592 /* MXLRUCache.h in Headers */, EC8A539D25B1BC77004E0802 /* MXCallCandidatesEventContent.h in Headers */, @@ -6051,7 +5679,6 @@ 18B22A7027707CDD00482170 /* MXEventContentLocation.h in Headers */, EC60EDC6265CFEA800B39A4E /* MXRoomSyncUnreadNotifications.h in Headers */, 329FB1791A0A74B100A5E88E /* MXTools.h in Headers */, - 322691321E5EF77D00966A6E /* MXDeviceListOperation.h in Headers */, EC60EE06265CFFF400B39A4E /* MXGroupSyncProfile.h in Headers */, 32481A841C03572900782AD3 /* MXRoomAccountData.h in Headers */, 327E9AE12285497100A98BC1 /* MXEventContentRelatesTo.h in Headers */, @@ -6066,14 +5693,11 @@ B146D4E121A5AEF200D8C2C6 /* MXRealmMediaScanMapper.h in Headers */, EC116595270FBF090089FA56 /* MXApplicationProtocol.h in Headers */, 1838927E2702F553003F0C4F /* MXSendReplyEventDefaultStringLocalizer.h in Headers */, - 32C78B6C256CFC4D008130B1 /* MXCryptoMigration.h in Headers */, F08B8D5C1E014711006171A8 /* NSData+MatrixSDK.h in Headers */, C60165381E3AA57900B92CFA /* MXSDKOptions.h in Headers */, 32CEEF4F23B0AB030039BA98 /* MXCrossSigning.h in Headers */, EC6D007928E1F15400152144 /* MXDevice.h in Headers */, EC619D9324DD834B00663A80 /* MXPushGatewayRestClient.h in Headers */, - 32C78B68256CFC4D008130B1 /* MXCryptoVersion.h in Headers */, - 320B3934239FA56900BE2C06 /* MXKeyVerificationByDMRequest.h in Headers */, EDE70DC528DA1B7F00099736 /* MXCryptoTools.h in Headers */, EC60EDE8265CFF3100B39A4E /* MXRoomInviteState.h in Headers */, 324DD2A6246AE81300377005 /* MXSecretStorageKeyContent.h in Headers */, @@ -6094,7 +5718,6 @@ 32A9E8251EF4026E0081358A /* MXUIKitBackgroundModeHandler.h in Headers */, EC403834289A672A0067D5B8 /* MXAes256BackupAuthData.h in Headers */, B19A30BC2404268600FB6F35 /* MXQRCodeDataCoder.h in Headers */, - 325D1C261DFECE0D0070B8BF /* MXCrypto_Private.h in Headers */, B10AFB4322A970060092E6AF /* MXEventReplace.h in Headers */, 3A108A7E25810C96005EEBE9 /* MXKeyData.h in Headers */, 327A5F4D239805F600ED6329 /* MXKeyVerificationStart.h in Headers */, @@ -6109,7 +5732,6 @@ EDAAC42128E3174700DD89B5 /* MXCryptoSecretStore.h in Headers */, 32549AF923F2E2790002576B /* MXKeyVerificationReady.h in Headers */, 32A151521DAF8A7200400192 /* MXQueuedEncryption.h in Headers */, - 32A30B181FB4813400C8309E /* MXIncomingRoomKeyRequestManager.h in Headers */, F03EF5081DF071D5009DF592 /* MXEncryptedAttachments.h in Headers */, 3291DC8323DF52E10009732F /* MXRoomCreationParameters.h in Headers */, 320BBF421D6C81550079890E /* MXEventsByTypesEnumeratorOnArray.h in Headers */, @@ -6127,6 +5749,7 @@ 3A23A741256D322C00B9D00F /* MXAes.h in Headers */, EC0B943D271DB68F00B4D440 /* MXVoidRoomSummaryStore.h in Headers */, 32BBAE6D2178E99100D85F46 /* MXKeyBackupVersion.h in Headers */, + A71E55322CA6FEBD00B06B17 /* MXKeyBackupPassword.h in Headers */, 323547D82226D5D600F15F94 /* MXWellKnownBaseConfig.h in Headers */, 32A151461DAF7C0C00400192 /* MXDeviceInfo.h in Headers */, EC8A53A925B1BC77004E0802 /* MXCallRejectEventContent.h in Headers */, @@ -6140,7 +5763,6 @@ EC8A539B25B1BC77004E0802 /* MXCallAnswerEventContent.h in Headers */, EDE70DC828DA22F800099736 /* MXKeyBackupEngine.h in Headers */, ECDA764C27BA963D000C48CF /* MXBooleanCapability.h in Headers */, - 3274538A23FD918800438328 /* MXKeyVerificationByToDeviceRequest.h in Headers */, B1136963230AC9D900E2B2FA /* MXIdentityServerRestClient.h in Headers */, 32CAB10B1A925B41008C5BB9 /* MXHTTPOperation.h in Headers */, 3275FD9521A6B46600B9C13D /* MXLoginTerms.h in Headers */, @@ -6150,7 +5772,6 @@ EC60EDFC265CFFD200B39A4E /* MXInvitedGroupSync.h in Headers */, 323F879625554FF2009E9E67 /* MXTaskProfile_Private.h in Headers */, 32618E7B20EFA45B00E1D2EA /* MXRoomMembers.h in Headers */, - 329E808C224E2E1B00A48C3A /* MXOutgoingSASTransaction.h in Headers */, B146D4D521A5A44E00D8C2C6 /* MXScanRealmProvider.h in Headers */, EC60EDD0265CFECC00B39A4E /* MXRoomSyncSummary.h in Headers */, EC60EDBC265CFE8600B39A4E /* MXRoomSyncAccountData.h in Headers */, @@ -6161,30 +5782,20 @@ 3A108A9825810F62005EEBE9 /* MXAesKeyData.h in Headers */, B1EE98CC2804820800AB63F0 /* MXLocation.h in Headers */, B10AFB4722AA8A8E0092E6AF /* MXEventEditsListener.h in Headers */, - 3287164F23C4C11F00D720CA /* MXKeyVerificationManager_Private.h in Headers */, 32F634AB1FC5E3480054EF49 /* MXEventDecryptionResult.h in Headers */, 32999DE322DCD1AD004FF987 /* MXPusherData.h in Headers */, A75CAD6C2A979AC500F06072 /* MXWellKnownAuthentication.h in Headers */, - 3284A5A01DB7C00600A09972 /* MXCryptoStore.h in Headers */, - 32442FB121EDD21300D2411B /* MXKeyBackupPassword.h in Headers */, - 3259CD531DF860C300186944 /* MXRealmCryptoStore.h in Headers */, 320DFDE419DD99B60068622A /* MXRestClient.h in Headers */, - 322A51C31D9AC8FE00C8536D /* MXCryptoAlgorithms.h in Headers */, - 327187851DA7D0220071C818 /* MXOlmDecryption.h in Headers */, - 322A51C71D9BBD3C00C8536D /* MXOlmDevice.h in Headers */, 3252DCAE224BE5D40032264F /* MXKeyVerificationManager.h in Headers */, - 3229535125A5F7220012FCF0 /* MXBackgroundCryptoStore.h in Headers */, 3265CB381A14C43E00E24B2F /* MXRoomState.h in Headers */, 322360521A8E610500A3CA81 /* MXPushRuleDisplayNameCondtionChecker.h in Headers */, ECDA762A27B28F3B000C48CF /* MXEventRelationThread.h in Headers */, - B1DDC9D62418098200D208E3 /* MXIncomingSASTransaction_Private.h in Headers */, 32CAB1071A91EA34008C5BB9 /* MXPushRuleRoomMemberCountConditionChecker.h in Headers */, EC8A53B925B1BC77004E0802 /* MXCallEventContent.h in Headers */, 3240969D1F9F751600DBA607 /* MXPushRuleSenderNotificationPermissionConditionChecker.h in Headers */, 3283F7781EAF30F700C1688C /* MXBugReportRestClient.h in Headers */, 32B0E33923A2989A0054FF1A /* MXEventReferenceChunk.h in Headers */, 32DC15D41A8CF874006F9AD3 /* MXPushRuleEventMatchConditionChecker.h in Headers */, - 32A151261DABB0CB00400192 /* MXMegolmDecryption.h in Headers */, 32E402B921C957D2004E87A6 /* MXOlmSession.h in Headers */, B17982F82119E4A2001FD722 /* MXRoomPowerLevels.h in Headers */, B14766B723D9D9410091F721 /* MXUsersTrustLevelSummary.h in Headers */, @@ -6203,7 +5814,6 @@ EC8A53C125B1BC77004E0802 /* MXCallCapabilitiesModel.h in Headers */, 32FE41361D0AB7070060835E /* MXEnumConstants.h in Headers */, 32E226A61D06AC9F00E6CA54 /* MXPeekingRoom.h in Headers */, - EC1165D1271082920089FA56 /* MXDecrypting.h in Headers */, 329FB1751A0A3A1600A5E88E /* MXRoomMember.h in Headers */, 320DFDE019DD99B60068622A /* MXSession.h in Headers */, ECF29BCC264194AE0053E6D6 /* MXCallAssertedIdentityEventContent.h in Headers */, @@ -6224,17 +5834,14 @@ B146D4F621A5BB9F00D8C2C6 /* MXRealmMediaScanStore.h in Headers */, 328BCB3321947BE200A976D3 /* MXKeyBackupVersionTrust.h in Headers */, 325AF3E124897D9400EF937D /* MXSecretRecoveryResult.h in Headers */, - 32FA10C11FA1C9EE00E54233 /* MXOutgoingRoomKeyRequestManager.h in Headers */, 18C26C53273C16C800805154 /* MXEventContentPollStart.h in Headers */, 3293C700214BBA4F009B3DDB /* MXPeekingRoomSummary.h in Headers */, 326056851C76FDF2009D44AD /* MXRoomEventTimeline.h in Headers */, - 3A59A49D25A7A16F00DDA1FC /* MXOlmOutboundGroupSession.h in Headers */, 32AF927F240EA0190008A0FD /* MXSecretShareManager.h in Headers */, ED01915828C64E0400ED3A69 /* MXForwardedRoomKeyEventContent.h in Headers */, 321CFDEE225264C4004D31DF /* NSArray+MatrixSDK.h in Headers */, 1838928827031D1D003F0C4F /* MXSendReplyEventStringLocalizerProtocol.h in Headers */, B11BD44822CB56790064D8B0 /* MXReplyEventParser.h in Headers */, - 321CFDEA22525DEE004D31DF /* MXIncomingSASTransaction.h in Headers */, 32F945F61FAB83D900622468 /* MXIncomingRoomKeyRequest.h in Headers */, 327137271A24D50A00DB6757 /* MXMyUser.h in Headers */, B146D47C21A5958400D8C2C6 /* MXAntivirusScanStatusFormatter.h in Headers */, @@ -6287,11 +5894,7 @@ B14EF2D62397E90400758AF0 /* MXMegolmSessionData.h in Headers */, EC8A53BA25B1BC77004E0802 /* MXCallEventContent.h in Headers */, B14EF2BA2397E90400758AF0 /* MXOutgoingRoomKeyRequest.h in Headers */, - B14EF33E2397E90400758AF0 /* MXOlmInboundGroupSession.h in Headers */, B14EF2FB2397E90400758AF0 /* MXOlmSession.h in Headers */, - B14EF3282397E90400758AF0 /* MXMegolmDecryption.h in Headers */, - EC403845289A7F0F0067D5B8 /* MXCurve25519KeyBackupAlgorithm.h in Headers */, - B14EF31E2397E90400758AF0 /* MXOlmDevice.h in Headers */, B14EF2DF2397E90400758AF0 /* MXDecryptionResult.h in Headers */, EC60ED68265CFC7200B39A4E /* MXPresenceSyncResponse.h in Headers */, B14EF2C52397E90400758AF0 /* MXPushRuleDisplayNameCondtionChecker.h in Headers */, @@ -6305,8 +5908,6 @@ B14EF3062397E90400758AF0 /* MXEncrypting.h in Headers */, B14EF2972397E90400758AF0 /* MXCrypto.h in Headers */, B14EF2982397E90400758AF0 /* MXNoStore.h in Headers */, - B14EF29A2397E90400758AF0 /* MXRealmCryptoStore.h in Headers */, - 3297912F23AA126500F7BB9B /* MXKeyVerificationStatusResolver.h in Headers */, B14EF29B2397E90400758AF0 /* MXMemoryRoomStore.h in Headers */, EC5C560B2798CEA00014CBE9 /* NSDictionary+MutableDeepCopy.h in Headers */, B14EF29C2397E90400758AF0 /* MXRealmMediaScan.h in Headers */, @@ -6355,9 +5956,7 @@ B14EF2B42397E90400758AF0 /* MXAntivirusScanStatus.h in Headers */, 324DD2A7246AE81300377005 /* MXSecretStorageKeyContent.h in Headers */, 3A108AA325810FE5005EEBE9 /* MXRawDataKey.h in Headers */, - B14EF2B52397E90400758AF0 /* MXDeviceListOperationsPool.h in Headers */, B14EF2B62397E90400758AF0 /* MXJSONModel.h in Headers */, - B14EF2B72397E90400758AF0 /* MXCryptoStore.h in Headers */, B14766B823D9D9410091F721 /* MXUsersTrustLevelSummary.h in Headers */, B14EF2B82397E90400758AF0 /* MXReactionCount.h in Headers */, B14EF2B92397E90400758AF0 /* MXMediaScanStore.h in Headers */, @@ -6368,11 +5967,9 @@ B14EF2BC2397E90400758AF0 /* MXAggregatedReactions.h in Headers */, 32AF9280240EA0190008A0FD /* MXSecretShareManager.h in Headers */, B14EF2BD2397E90400758AF0 /* MXAggregationPaginatedResponse.h in Headers */, - 3229535225A5F7220012FCF0 /* MXBackgroundCryptoStore.h in Headers */, B14EF2BE2397E90400758AF0 /* MXEvent.h in Headers */, EC0B9430271D95CC00B4D440 /* MXFileRoomSummaryStore.h in Headers */, B14EF2BF2397E90400758AF0 /* MXRoomThirdPartyInvite.h in Headers */, - EDD4197F28DCAA5F007F3757 /* MXNativeKeyBackupEngine.h in Headers */, ECBF657F26DE2A4900AA3A99 /* MXMemoryRoomOutgoingMessagesStore.h in Headers */, EC8A538C25B1BC77004E0802 /* MXCallCandidate.h in Headers */, B14EF2C12397E90400758AF0 /* MXKeyBackup.h in Headers */, @@ -6388,13 +5985,11 @@ B14EF2C42397E90400758AF0 /* MXRealmHelper.h in Headers */, B14EF2C62397E90400758AF0 /* MXReactionRelation.h in Headers */, B14EF2C72397E90400758AF0 /* MXMediaManager.h in Headers */, - EC1165D0271082410089FA56 /* MXDecrypting.h in Headers */, EC131B0F2768A0C600712964 /* MXEventTimeline.h in Headers */, 1838928927031D1D003F0C4F /* MXSendReplyEventStringLocalizerProtocol.h in Headers */, EC60EDDB265CFF0600B39A4E /* MXInvitedRoomSync.h in Headers */, B14EF2C82397E90400758AF0 /* MXCallManager.h in Headers */, EC6D007A28E1F15400152144 /* MXDevice.h in Headers */, - EC40384B289A7F260067D5B8 /* MXAes256KeyBackupAlgorithm.h in Headers */, B14EF2C92397E90400758AF0 /* MXRealmReactionRelation.h in Headers */, 91F0685E2767CA430079F8FA /* MXTaskProfileName.h in Headers */, B14EF2CA2397E90400758AF0 /* MXAnalyticsDelegate.h in Headers */, @@ -6409,11 +6004,8 @@ 324DD2C0246D658500377005 /* MXHkdfSha256.h in Headers */, 8EC5110B256822B400EC4E5B /* MXTaggedEvents.h in Headers */, B14EF2D12397E90400758AF0 /* MXEventsEnumeratorOnArray.h in Headers */, - B1DDC9D72418098200D208E3 /* MXIncomingSASTransaction_Private.h in Headers */, B14EF2D22397E90400758AF0 /* MXReplyEventParts.h in Headers */, B14EF2D32397E90400758AF0 /* MXRoomMembersCount.h in Headers */, - EC403859289C38BB0067D5B8 /* MXKeyBackupAlgorithm.h in Headers */, - 32AF929024110ADD0008A0FD /* MXSecretShareManager_Private.h in Headers */, B19A30C52404268600FB6F35 /* MXVerifyingAnotherUserQRCodeData.h in Headers */, B14EF2D42397E90400758AF0 /* MatrixSDK.h in Headers */, B14EF2D52397E90400758AF0 /* MXReactionOperation.h in Headers */, @@ -6424,13 +6016,11 @@ B14EF2D72397E90400758AF0 /* MXAggregationsStore.h in Headers */, B14EF2D82397E90400758AF0 /* MXCredentials.h in Headers */, B14EF2D92397E90400758AF0 /* MXEncryptedContentKey.h in Headers */, - B14EF2DB2397E90400758AF0 /* MXOlmEncryption.h in Headers */, EC05473525FF8A3C0047ECD7 /* MXVirtualRoomInfo.h in Headers */, B14EF2DC2397E90400758AF0 /* MXOlmSessionResult.h in Headers */, 3274538523FD69D600438328 /* MXKeyVerificationRequestByToDeviceJSONModel.h in Headers */, B14EF2DD2397E90400758AF0 /* MXEventsEnumerator.h in Headers */, EC8A53E925B1BCC6004E0802 /* MXThirdpartyProtocolsResponse.h in Headers */, - B14EF2DE2397E90400758AF0 /* MXDeviceList.h in Headers */, B14EF2E02397E90400758AF0 /* MXCall.h in Headers */, B14EF2E12397E90400758AF0 /* MXContentScanEncryptedBody.h in Headers */, B14EF2E22397E90400758AF0 /* MX3PidAddManager.h in Headers */, @@ -6439,16 +6029,14 @@ ECD2899F26EB570C00F268CF /* MXRoomSummaryStore.h in Headers */, B14EF2E42397E90400758AF0 /* MXStore.h in Headers */, 32AF928B240EA3880008A0FD /* MXSecretShareSend.h in Headers */, - B14EF2E62397E90400758AF0 /* MXMegolmEncryption.h in Headers */, EC8A53E125B1BCC6004E0802 /* MXThirdPartyProtocolInstance.h in Headers */, ED5C753F28B3E80300D24E85 /* MXLog.h in Headers */, B14EF2E72397E90400758AF0 /* MXLRUCache.h in Headers */, + A777E58E2C98E21800B39397 /* MXCryptoVersion.h in Headers */, B105CDD7261F54C8006EB204 /* MXSpaceChildContent.h in Headers */, 32AF929824115D8B0008A0FD /* MXPendingSecretShareRequest.h in Headers */, B14EF2E92397E90400758AF0 /* MXTools.h in Headers */, B1EE98CD2804820800AB63F0 /* MXLocation.h in Headers */, - B14EF2EA2397E90400758AF0 /* MXDeviceListOperation.h in Headers */, - ECD623FF25D3DCC900DC0A0B /* MXOlmDecryption.h in Headers */, EDAAC42228E3174700DD89B5 /* MXCryptoSecretStore.h in Headers */, B14EF2EB2397E90400758AF0 /* MXRoomAccountData.h in Headers */, B14EF2EC2397E90400758AF0 /* MXEventContentRelatesTo.h in Headers */, @@ -6470,22 +6058,18 @@ B14EECD82577DE7A00448735 /* MXLoginSSOIdentityProvider.h in Headers */, EC054974260123BE0047ECD7 /* MXRoomAccountDataUpdating.h in Headers */, B14EF2F62397E90400758AF0 /* MXJSONModels.h in Headers */, - 3A59A49E25A7A16F00DDA1FC /* MXOlmOutboundGroupSession.h in Headers */, B14EF2F72397E90400758AF0 /* MXRoomEventFilter.h in Headers */, B14EF2F82397E90400758AF0 /* MXUIKitBackgroundModeHandler.h in Headers */, - B14EF2F92397E90400758AF0 /* MXCrypto_Private.h in Headers */, B14EF2FA2397E90400758AF0 /* MXEventReplace.h in Headers */, B14EF2FC2397E90400758AF0 /* MXAggregatedReactionsUpdater.h in Headers */, EC05478325FF99450047ECD7 /* MXRoomAccountDataUpdater.h in Headers */, B14EF2FD2397E90400758AF0 /* MXScanRealmInMemoryProvider.h in Headers */, B14EF2FE2397E90400758AF0 /* MXReplyEventBodyParts.h in Headers */, - 32C78B69256CFC4D008130B1 /* MXCryptoVersion.h in Headers */, 1838928727031D1D003F0C4F /* MXRoomNameStringLocalizerProtocol.h in Headers */, EC8A53BC25B1BC77004E0802 /* MXCallSelectAnswerEventContent.h in Headers */, B1A026F726161EF5001AADFF /* MXSpaceChildSummaryResponse.h in Headers */, B14EF3002397E90400758AF0 /* MXQueuedEncryption.h in Headers */, 324AAC822399143400380A66 /* MXKeyVerificationMac.h in Headers */, - B14EF3012397E90400758AF0 /* MXIncomingRoomKeyRequestManager.h in Headers */, 325AF3E224897D9400EF937D /* MXSecretRecoveryResult.h in Headers */, B14EF3022397E90400758AF0 /* MXEncryptedAttachments.h in Headers */, B14EF3032397E90400758AF0 /* MXEventsByTypesEnumeratorOnArray.h in Headers */, @@ -6523,11 +6107,9 @@ B14EF3152397E90400758AF0 /* MXIncomingRoomKeyRequestCancellation.h in Headers */, EC8A539E25B1BC77004E0802 /* MXCallCandidatesEventContent.h in Headers */, 325AD44023BE3E7500FF5277 /* MXCrossSigningInfo.h in Headers */, - 3A59A52A25A7B1B000DDA1FC /* MXOutboundSessionInfo.h in Headers */, B14EF3162397E90400758AF0 /* MXContentScanResult.h in Headers */, ECD289B526F9F00E00F268CF /* MXRoomSummarySentStatus.h in Headers */, B14EF3172397E90400758AF0 /* MXRoomMembers.h in Headers */, - B14EF3192397E90400758AF0 /* MXOutgoingSASTransaction.h in Headers */, EC8A53DD25B1BCC6004E0802 /* MXThirdPartyProtocol.h in Headers */, ECF29BDF264195320053E6D6 /* MXAssertedIdentityModel.h in Headers */, B14EF31A2397E90400758AF0 /* MXScanRealmProvider.h in Headers */, @@ -6537,7 +6119,6 @@ B14EF31C2397E90400758AF0 /* MXEventEditsListener.h in Headers */, B14EF31D2397E90400758AF0 /* MXEventDecryptionResult.h in Headers */, B14EF31F2397E90400758AF0 /* MXPusherData.h in Headers */, - B14EF3202397E90400758AF0 /* MXKeyBackupPassword.h in Headers */, B14EF3212397E90400758AF0 /* MXRestClient.h in Headers */, 324DD2B2246BDC6800377005 /* MXSecretStorage_Private.h in Headers */, ED01915928C64E0400ED3A69 /* MXForwardedRoomKeyEventContent.h in Headers */, @@ -6552,7 +6133,6 @@ B14EF3242397E90400758AF0 /* MXBugReportRestClient.h in Headers */, 323F877C25546170009E9E67 /* MXBaseProfiler.h in Headers */, B14EF3252397E90400758AF0 /* MXRoomPowerLevels.h in Headers */, - B19A309F240424BD00FB6F35 /* MXQRCodeTransaction_Private.h in Headers */, 3AC13803264482A100EE1E74 /* MXExportedOlmDevice.h in Headers */, B14EF3262397E90400758AF0 /* MXEventScanStoreDelegate.h in Headers */, B14EF3292397E90400758AF0 /* MXAutoDiscovery.h in Headers */, @@ -6562,6 +6142,7 @@ EC60EDBD265CFE8600B39A4E /* MXRoomSyncAccountData.h in Headers */, B14EF32B2397E90400758AF0 /* MXBackgroundModeHandler.h in Headers */, B14EF32C2397E90400758AF0 /* MXRoomPredecessorInfo.h in Headers */, + A71E55332CA6FEBD00B06B17 /* MXKeyBackupPassword.h in Headers */, B14EF32D2397E90400758AF0 /* MXCallStack.h in Headers */, B14EF32E2397E90400758AF0 /* MXMediaLoader.h in Headers */, ECDA763A27B6B74C000C48CF /* MXCapabilities.h in Headers */, @@ -6582,14 +6163,12 @@ B14EF3352397E90400758AF0 /* MXDiscoveredClientConfig.h in Headers */, ECDA764727BA939E000C48CF /* MXRoomVersionsCapability.h in Headers */, B14EF3362397E90400758AF0 /* MXEventScanStore.h in Headers */, - 3287165023C4C12200D720CA /* MXKeyVerificationManager_Private.h in Headers */, EC60EE07265CFFF400B39A4E /* MXGroupSyncProfile.h in Headers */, EC11658E270F3ABF0089FA56 /* RLMRealm+MatrixSDK.h in Headers */, 324AAC7E2399143400380A66 /* MXKeyVerificationCancel.h in Headers */, ED01915528C64E0400ED3A69 /* MXRoomKeyEventContent.h in Headers */, B14EF3372397E90400758AF0 /* MXRoomTombStoneContent.h in Headers */, EDAAC41D28E30F3C00DD89B5 /* (null) in Headers */, - 3274538B23FD918800438328 /* MXKeyVerificationByToDeviceRequest.h in Headers */, B14EF3382397E90400758AF0 /* MXFilterObject.h in Headers */, B14EF3392397E90400758AF0 /* MXRealmReactionCount.h in Headers */, B14EF33A2397E90400758AF0 /* MXLoginPolicyData.h in Headers */, @@ -6599,9 +6178,7 @@ B14EF33D2397E90400758AF0 /* MXRealmMediaScanStore.h in Headers */, B14EF3402397E90400758AF0 /* MXKeyBackupVersionTrust.h in Headers */, EC2EACFE266625170038B61F /* MXRoomLastMessage.h in Headers */, - 320B3935239FA56900BE2C06 /* MXKeyVerificationByDMRequest.h in Headers */, 3A108A9925810F62005EEBE9 /* MXAesKeyData.h in Headers */, - B14EF3412397E90400758AF0 /* MXOutgoingRoomKeyRequestManager.h in Headers */, B14EF3422397E90400758AF0 /* MXPeekingRoomSummary.h in Headers */, B14EF3432397E90400758AF0 /* MXRoomEventTimeline.h in Headers */, B14EF3442397E90400758AF0 /* NSArray+MatrixSDK.h in Headers */, @@ -6611,8 +6188,6 @@ B14EF3452397E90400758AF0 /* MXReplyEventParser.h in Headers */, 323F878E25553D84009E9E67 /* MXTaskProfile.h in Headers */, 322AB585260CD5690017E964 /* MXCachedSyncResponse.h in Headers */, - B14EF3462397E90400758AF0 /* MXIncomingSASTransaction.h in Headers */, - B14EF3472397E90400758AF0 /* MXCryptoAlgorithms.h in Headers */, B14EF3482397E90400758AF0 /* MXIncomingRoomKeyRequest.h in Headers */, B14EF3492397E90400758AF0 /* MXMyUser.h in Headers */, B19A30AB2404257700FB6F35 /* MXQRCodeKeyVerificationStart.h in Headers */, @@ -6648,7 +6223,6 @@ B14EF3602397E90400758AF0 /* MXHTTPClient.h in Headers */, B19A30C92404268600FB6F35 /* MXQRCodeDataBuilder.h in Headers */, 323F879725554FF2009E9E67 /* MXTaskProfile_Private.h in Headers */, - 32C78B6D256CFC4D008130B1 /* MXCryptoMigration.h in Headers */, 32F00AC12488FB3600131741 /* MXRecoveryService_Private.h in Headers */, B14EF3612397E90400758AF0 /* MXCurve25519BackupAuthData.h in Headers */, EC8A53B225B1BC77004E0802 /* MXCallReplacesEventContent.h in Headers */, @@ -6943,7 +6517,6 @@ files = ( ECD289BC26FDE4E300F268CF /* MXRoomListDataCounts.swift in Sources */, B1EE98CE2804820800AB63F0 /* MXLocation.m in Sources */, - 32A1513F1DAF768D00400192 /* MXOlmInboundGroupSession.m in Sources */, 3259CFE626026A6F00C365DB /* MXRestClient+Extensions.swift in Sources */, EC60ED91265CFD3B00B39A4E /* MXRoomSync.m in Sources */, 189B8D102A864C250088D7CE /* DehydrationService.swift in Sources */, @@ -6960,6 +6533,7 @@ B146D4FB21A5BF7200D8C2C6 /* MXRealmEventScanStore.m in Sources */, B146D4D921A5A44E00D8C2C6 /* MXScanRealmFileProvider.m in Sources */, EC1165C627107E330089FA56 /* MXRoomListDataFetcher.swift in Sources */, + A777E5842C98DF1900B39397 /* MXCrossSigning.m in Sources */, B16C2447283AA85E00F5D1FE /* RLMSupport.swift in Sources */, EC60ED69265CFC7200B39A4E /* MXPresenceSyncResponse.m in Sources */, 3259D02326037A7200C365DB /* NSArray.swift in Sources */, @@ -6976,7 +6550,6 @@ EC8A53A525B1BC77004E0802 /* MXCallInviteEventContent.m in Sources */, 323F877D25546170009E9E67 /* MXBaseProfiler.m in Sources */, B1EE98DC280865A200AB63F0 /* MXBeaconAggregations.swift in Sources */, - 320B3936239FA56900BE2C06 /* MXKeyVerificationByDMRequest.m in Sources */, B18B0E4425FB73C300E32151 /* MXSpaceService.swift in Sources */, B146D47121A5939100D8C2C6 /* MXRealmHelper.m in Sources */, 32261B8C23C74A230018F1E2 /* MXDeviceTrustLevel.m in Sources */, @@ -6988,7 +6561,7 @@ 32999DE022DCD183004FF987 /* MXPusher.m in Sources */, F03EF4FF1DF014D9009DF592 /* MXMediaLoader.m in Sources */, ECDA763227B293D9000C48CF /* MXThreadProtocol.swift in Sources */, - EDCF802D2941FF220059E774 /* MXCryptoMigrationV2.swift in Sources */, + A777E57E2C98D8A500B39397 /* MXKeyVerificationTransaction.m in Sources */, 320A8841217F4E3F002EA952 /* MXCurve25519BackupAuthData.m in Sources */, B1A0270226162110001AADFF /* MXSpaceChildrenResponse.m in Sources */, 66398BA527A4085B00466E89 /* MXRefreshResponse.m in Sources */, @@ -7005,7 +6578,6 @@ 32792BDD2296B90A00F4FC9D /* MXAggregatedEditsUpdater.m in Sources */, ECDBE69028E5D961000C83AF /* MXClientInformationService.swift in Sources */, ED44F01428180EAB00452A5D /* MXSharedHistoryKeyManager.swift in Sources */, - 3259CD541DF860C300186944 /* MXRealmCryptoStore.m in Sources */, ED44F01128180BCC00452A5D /* MXSharedHistoryKeyRequest.swift in Sources */, B1D50DD3288EB34900DD1AA3 /* MXBeaconInfoSummaryDeletionAllRoomListener.swift in Sources */, EC60EDAA265CFE3B00B39A4E /* MXRoomSyncTimeline.m in Sources */, @@ -7027,7 +6599,6 @@ ED5C754628B3E80300D24E85 /* MXLogger.m in Sources */, 3291DC8523DF52E20009732F /* MXRoomCreationParameters.m in Sources */, 32C235731F827F3800E38FC5 /* MXRoomOperation.m in Sources */, - 322A51C41D9AC8FE00C8536D /* MXCryptoAlgorithms.m in Sources */, B1798D0824091A0100308A8F /* MXBase64Tools.m in Sources */, B182B08823167A640057972E /* MXIdentityServerHashDetails.m in Sources */, EC60EDBE265CFE8600B39A4E /* MXRoomSyncAccountData.m in Sources */, @@ -7035,7 +6606,6 @@ 324DD2A2246AE1EF00377005 /* MXEncryptedSecretContent.m in Sources */, 329FB1761A0A3A1600A5E88E /* MXRoomMember.m in Sources */, B16C2455283AB0DE00F5D1FE /* MXRealmBeaconMapper.swift in Sources */, - EDD4198128DCAA7B007F3757 /* MXNativeKeyBackupEngine.m in Sources */, 3251D41F25AF01D7001E6E77 /* MXUIKitApplicationStateService.swift in Sources */, B1136965230AC9D900E2B2FA /* MXIdentityService.m in Sources */, 66836AB727CFA17200515780 /* MXEventStreamService.swift in Sources */, @@ -7055,7 +6625,6 @@ 324DD2B8246C21C700377005 /* MXSecretStorageKeyCreationInfo.m in Sources */, EC1165C027107E330089FA56 /* MXSuggestedRoomListDataCache.swift in Sources */, ECD2897726E8ED0900F268CF /* MXRoomListDataFetchOptions.swift in Sources */, - EC403846289A7F0F0067D5B8 /* MXCurve25519KeyBackupAlgorithm.m in Sources */, EC0B941B2718654B00B4D440 /* NSManagedObject+MatrixSDK.swift in Sources */, EDD578E12881C37C006739DD /* MXDeviceInfoSource.swift in Sources */, 327E9AE82285A8C400A98BC1 /* MXAggregationPaginatedResponse.m in Sources */, @@ -7088,11 +6657,11 @@ 327E9ABD2284521C00A98BC1 /* MXEventUnsignedData.m in Sources */, 32AF9286240EA2430008A0FD /* MXSecretShareRequest.m in Sources */, ED5C754428B3E80300D24E85 /* MXLogObjcWrapper.m in Sources */, + A71E55342CA6FEBD00B06B17 /* MXKeyBackupPassword.m in Sources */, ED47CB6D28523995004FD755 /* MXCryptoV2.swift in Sources */, ED463ECB29B0B75800957941 /* EventEncryptionAlgorithm+String.swift in Sources */, ED37834929C9B6E700A449DA /* MXEventDecryptionDecoration.swift in Sources */, EDF154E1296C203E004D7FFE /* MXCryptoMachineStore.swift in Sources */, - 32A1513A1DAD292400400192 /* MXMegolmEncryption.m in Sources */, EC60EDFE265CFFD200B39A4E /* MXInvitedGroupSync.m in Sources */, 8EC5110C256822B400EC4E5B /* MXTaggedEvents.m in Sources */, B16C2452283AB08E00F5D1FE /* MXRealmBeaconInfo.swift in Sources */, @@ -7102,10 +6671,8 @@ F0173EAD1FCF0E8900B5F6A3 /* MXGroup.m in Sources */, EC60EDDC265CFF0600B39A4E /* MXInvitedRoomSync.m in Sources */, 3AF85F9226FC7AE800A9E67B /* MXSpaceNotificationState.swift in Sources */, - 32CEEF5123B0AB030039BA98 /* MXCrossSigning.m in Sources */, 32720D9F222EAA6F0086FFF5 /* MXAutoDiscovery.m in Sources */, 3297912923A93D4B00F7BB9B /* MXKeyVerification.m in Sources */, - 32C78B6A256CFC4D008130B1 /* MXCryptoMigration.m in Sources */, EC8A53B725B1BC77004E0802 /* MXCallCandidatesEventContent.m in Sources */, 32D2CC0623422462002BD8CA /* MX3PidAddManager.m in Sources */, 92634B831EF2E3C400DB9F60 /* MXCallKitConfiguration.m in Sources */, @@ -7139,14 +6706,12 @@ EDAAC42428E3177000DD89B5 /* MXRecoveryServiceDependencies.swift in Sources */, 3A7509BB26FC61DF00B85773 /* MXSpaceNotificationCounter.swift in Sources */, 32E402BA21C957D2004E87A6 /* MXOlmSession.m in Sources */, + A777E5752C98D6F100B39397 /* MXKeyVerificationManager.m in Sources */, ED647E3E292CE64400A47519 /* MXSessionStartupProgress.swift in Sources */, EC8A53C525B1BC77004E0802 /* MXTurnServerResponse.m in Sources */, - 32A151271DABB0CB00400192 /* MXMegolmDecryption.m in Sources */, - ED558068296F0361003443E3 /* MXCryptoMigrationStore.swift in Sources */, 18121F78273E6E1E00B68ADF /* PollBuilder.swift in Sources */, 327A5F50239805F600ED6329 /* MXKeyVerificationKey.m in Sources */, B16C56E2261D0A9D00604765 /* MXSpaceChildInfo.swift in Sources */, - EC40384C289A7F260067D5B8 /* MXAes256KeyBackupAlgorithm.m in Sources */, ECBF658726DE3DF800AA3A99 /* MXFileRoomOutgoingMessagesStore.m in Sources */, 3AC13804264482A100EE1E74 /* MXExportedOlmDevice.m in Sources */, 321CFDFE2254E8C4004D31DF /* MXEmojiRepresentation.m in Sources */, @@ -7159,7 +6724,6 @@ B1F04B1A2812A4E000103EBE /* MXBeaconInfoSummaryProtocol.swift in Sources */, 021AFBA52179E91900742B2C /* MXEncryptedContentKey.m in Sources */, 32F634AC1FC5E3480054EF49 /* MXEventDecryptionResult.m in Sources */, - ED4114EE292E49C000728459 /* MXLegacyBackgroundCrypto.swift in Sources */, ED01915628C64E0400ED3A69 /* MXRoomKeyEventContent.m in Sources */, 327137281A24D50A00DB6757 /* MXMyUser.m in Sources */, ECDA763B27B6B74C000C48CF /* MXCapabilities.m in Sources */, @@ -7170,32 +6734,25 @@ 3A108AA425810FE5005EEBE9 /* MXRawDataKey.m in Sources */, C6F9358A1E5B3BE600FC34BF /* MXEvent.swift in Sources */, B146D48021A59E2400D8C2C6 /* MXEventScan.m in Sources */, - 329E808D224E2E1B00A48C3A /* MXOutgoingSASTransaction.m in Sources */, 1838927A2702F553003F0C4F /* MXSendReplyEventDefaultStringLocalizer.m in Sources */, EC0B941E27186C3500B4D440 /* MXRoomListDataSortable.swift in Sources */, 32581DEA23C8C0C900832EAA /* MXUserTrustLevel.m in Sources */, 66AC9D25278CE627002E9B8F /* MXRefreshTokenData.m in Sources */, - 3271878A1DA7DCE50071C818 /* MXOlmEncryption.m in Sources */, - 327187861DA7D0220071C818 /* MXOlmDecryption.m in Sources */, EC383BA8253DE6EE002FBBE6 /* MXSyncResponseFileStore.swift in Sources */, 32114A901A262ECB00FF2EC4 /* MXNoStore.m in Sources */, B19A30C02404268600FB6F35 /* MXQRCodeDataCoder.m in Sources */, - 3297913023AA126500F7BB9B /* MXKeyVerificationStatusResolver.m in Sources */, ECDA762F27B292B5000C48CF /* MXThreadModel.swift in Sources */, - 3A59A49F25A7A16F00DDA1FC /* MXOlmOutboundGroupSession.m in Sources */, B146D4F221A5AF7F00D8C2C6 /* MXRealmEventScanMapper.m in Sources */, B11BD45A22CB58850064D8B0 /* MXReplyEventFormattedBodyParts.m in Sources */, ED505DC428E206FC0079A3D3 /* MXKeyBackupVersion+Stub.swift in Sources */, B2EC7E2327F483DB00F40C26 /* MXEventAssetTypeMapper.swift in Sources */, 32CE6FB91A409B1F00317F1E /* MXFileStoreMetaData.m in Sources */, 3264DB921CEC528D00B99881 /* MXAccountData.m in Sources */, - 3229535325A5F7220012FCF0 /* MXBackgroundCryptoStore.m in Sources */, 3213301A228B010C0070BA9B /* MXRealmReactionCount.m in Sources */, 3A858DE127517C0E006322C1 /* MXRoomCapabilityType.swift in Sources */, 91CC0FCC26A033AE00C2A387 /* MXURLPreview.m in Sources */, 322AB591260CDBC10017E964 /* MXSyncResponseStoreManager.swift in Sources */, 3250E7CB220C913900736CB5 /* MXCryptoTools.m in Sources */, - 322691331E5EF77D00966A6E /* MXDeviceListOperation.m in Sources */, EC0B940D27184E8A00B4D440 /* MXRoomLastMessageMO.swift in Sources */, ECD2897D26E8F06F00F268CF /* MXStoreRoomListDataFetcher.swift in Sources */, 32D2CC0323422462002BD8CA /* MX3PidAddSession.m in Sources */, @@ -7204,7 +6761,6 @@ 32B0E33B23A2989A0054FF1A /* MXEventReferenceChunk.m in Sources */, A780625027B2CE74005780C0 /* FileManager+AppGroupContainer.swift in Sources */, 9274AFE91EE580240009BEB6 /* MXCallKitAdapter.m in Sources */, - 3274538C23FD918800438328 /* MXKeyVerificationByToDeviceRequest.m in Sources */, A75CAD692A9796DE00F06072 /* MXWellKnownAuthentication.m in Sources */, ED6DAC1828C799E300ECDCB6 /* MXRoomKeyResult.swift in Sources */, ECB6FA952683811800A941E4 /* MXiOSAudioOutputRouter.swift in Sources */, @@ -7214,7 +6770,6 @@ B1432B53282AB29A00737CA6 /* MXBeaconInfoSummaryPerRoomListener.swift in Sources */, B1D50DD5288EB34900DD1AA3 /* MXBeaconInfoSummaryDeletionPerRoomListener.swift in Sources */, EDD578E92881C37C006739DD /* MXCryptoUserIdentityWrapper.swift in Sources */, - B19A30A0240424BD00FB6F35 /* MXQRCodeTransaction.m in Sources */, C63E78B01F26588000AC692F /* MXRoomPowerLevels.swift in Sources */, EC0B943F271DB68F00B4D440 /* MXVoidRoomSummaryStore.m in Sources */, EC383BA5253DE6C9002FBBE6 /* MXSyncResponseStore.swift in Sources */, @@ -7228,11 +6783,11 @@ EDF1B6902876CD2C00BBBCEE /* MXTaskQueue.swift in Sources */, EC8A539525B1BC77004E0802 /* MXUserModel.m in Sources */, ED5EF14F297AB29F00A5ADDA /* MXEventDecryptionResult+DecryptedEvent.swift in Sources */, - 3252DCAF224BE5D40032264F /* MXKeyVerificationManager.m in Sources */, 323E0C5C1A306D7A00A31D73 /* MXEvent.m in Sources */, F03EF5011DF014D9009DF592 /* MXMediaManager.m in Sources */, 32CAB10C1A925B41008C5BB9 /* MXHTTPOperation.m in Sources */, ED28068728F06D360070AE9F /* MXQRCodeTransactionV2.swift in Sources */, + A777E57B2C98D82500B39397 /* MXKeyVerificationRequest.m in Sources */, 32BBAE6C2178E99100D85F46 /* MXKeyBackupData.m in Sources */, 32AF928C240EA3880008A0FD /* MXSecretShareSend.m in Sources */, 324DD29B246AD2B500377005 /* MXSecretStorage.m in Sources */, @@ -7244,7 +6799,6 @@ 021AFBA72179E91900742B2C /* MXEncryptedContentFile.m in Sources */, 3220094619EFBF30008DE41D /* MXSessionEventListener.m in Sources */, EC0B940F27184E8A00B4D440 /* MXUsersTrustLevelSummaryMO.swift in Sources */, - 320B393C239FD15E00BE2C06 /* MXKeyVerificationRequest.m in Sources */, EC383BAC254030DF002FBBE6 /* MXBackgroundStore.swift in Sources */, EC0B942A2718F55C00B4D440 /* MXCoreDataRoomListDataManager.swift in Sources */, 32A31BC920D401FC005916C7 /* MXRoomFilter.m in Sources */, @@ -7253,13 +6807,12 @@ ED6DAC0A28C784AE00ECDCB6 /* Dictionary.swift in Sources */, 32A151471DAF7C0C00400192 /* MXDeviceInfo.m in Sources */, ECDA764E27BA963D000C48CF /* MXBooleanCapability.m in Sources */, - 321CFDEB22525DEE004D31DF /* MXIncomingSASTransaction.m in Sources */, EC1165C427107E330089FA56 /* MXRoomListDataFilterOptions.swift in Sources */, ED6F4EFC2987F0FC007D1191 /* MXEncryptedKeyBackup.swift in Sources */, 3A108A8025810C96005EEBE9 /* MXKeyData.m in Sources */, - 3A59A52B25A7B1B000DDA1FC /* MXOutboundSessionInfo.m in Sources */, 32A1515C1DB525DA00400192 /* NSObject+sortedKeys.m in Sources */, 32792BD52295A86600F4FC9D /* MXAggregatedReactionsUpdater.m in Sources */, + A759E23D2C98EE7D002429A8 /* MXCrypto.m in Sources */, 32618E7C20EFA45B00E1D2EA /* MXRoomMembers.m in Sources */, ED2DD118286C450600F06731 /* MXCryptoRequests.swift in Sources */, EDAAC41928E2FCFE00DD89B5 /* MXCryptoSecretStoreV2.swift in Sources */, @@ -7295,7 +6848,6 @@ F03EF5051DF01596009DF592 /* MXLRUCache.m in Sources */, EC8A53AF25B1BC77004E0802 /* MXCallAnswerEventContent.m in Sources */, B1432B51282AB29A00737CA6 /* MXBeaconInfoSummaryAllRoomListener.swift in Sources */, - 32A30B191FB4813400C8309E /* MXIncomingRoomKeyRequestManager.m in Sources */, A7BB11982C933677002F7FA5 /* PKMessageWrapper.swift in Sources */, 323F3F9320D3F0C700D26D6A /* MXRoomEventFilter.m in Sources */, 3275FD9921A6B53300B9C13D /* MXLoginPolicyData.m in Sources */, @@ -7355,7 +6907,6 @@ 3291D4D51A68FFEB00C3BA41 /* MXFileRoomStore.m in Sources */, 329FB1801A0B665800A5E88E /* MXUser.m in Sources */, 324AAC73239913AD00380A66 /* MXKeyVerificationDone.m in Sources */, - ED6DABFC28C7542800ECDCB6 /* MXRoomKeyInfoFactory.swift in Sources */, B11556EE230C45C600B2A2CF /* MXIdentityServerRestClient.swift in Sources */, ED5EF145297AB1F200A5ADDA /* MXRoomEventEncryption.swift in Sources */, EDAAC41F28E30F4C00DD89B5 /* (null) in Sources */, @@ -7370,7 +6921,6 @@ B14EECD92577DE7A00448735 /* MXLoginSSOIdentityProvider.m in Sources */, B141DF0E283CDD180023867A /* Realm+MatrixSDK.swift in Sources */, 32B090E2261F709B002924AA /* MXAsyncTaskQueue.swift in Sources */, - 322691371E5EFF8700966A6E /* MXDeviceListOperationsPool.m in Sources */, C6F935801E5B3ACA00FC34BF /* MXResponse.swift in Sources */, ECDA764827BA939E000C48CF /* MXRoomVersionsCapability.m in Sources */, 32EEA8632604014A0041425B /* MXSummable.swift in Sources */, @@ -7396,13 +6946,11 @@ C6F9357C1E5B39CA00FC34BF /* MXRestClient.swift in Sources */, 32BBAE752179CF4000D85F46 /* MXKeyBackup.m in Sources */, EC8A53AD25B1BC77004E0802 /* MXCallNegotiateEventContent.m in Sources */, - 322A51B71D9AB15900C8536D /* MXCrypto.m in Sources */, B17982FB2119E4A2001FD722 /* MXRoomPredecessorInfo.m in Sources */, 32AF927D240EA0190008A0FD /* MXSecretShareManager.m in Sources */, 3294FD9E22F321B0007F1E60 /* MXServiceTerms.m in Sources */, 3290293424CB10D000DA7BB6 /* MatrixSDKVersion.m in Sources */, 32DC15D11A8CF7AE006F9AD3 /* MXNotificationCenter.m in Sources */, - 32637ED51E5B00400011E20D /* MXDeviceList.m in Sources */, 327A5F55239805F600ED6329 /* MXKeyVerificationMac.m in Sources */, B17982FA2119E4A2001FD722 /* MXRoomCreateContent.m in Sources */, ED5EF14B297AB29F00A5ADDA /* MXDeviceVerification+LocalTrust.swift in Sources */, @@ -7411,14 +6959,13 @@ 180F858627A2AF3000F4E5A5 /* MXWellKnownTileServerConfig.m in Sources */, B18B0E6725FBDC3000E32151 /* MXSpace.swift in Sources */, B146D4F721A5BB9F00D8C2C6 /* MXRealmMediaScanStore.m in Sources */, + A777E5782C98D79D00B39397 /* MXQRCodeTransaction.m in Sources */, 32999DE422DCD1AD004FF987 /* MXPusherData.m in Sources */, ED5EF14D297AB29F00A5ADDA /* MXRoomHistoryVisibility+HistoryVisibility.swift in Sources */, - 322A51C81D9BBD3C00C8536D /* MXOlmDevice.m in Sources */, EDD578E72881C37C006739DD /* MXCryptoDeviceWrapper.swift in Sources */, EC60ED5F265CFC2C00B39A4E /* MXSyncResponse.m in Sources */, EC383BA2253DE4B1002FBBE6 /* MXBackgroundSyncService.swift in Sources */, 18121F7D273E835D00B68ADF /* PollModels.swift in Sources */, - 32442FB221EDD21300D2411B /* MXKeyBackupPassword.m in Sources */, 3A108A9A25810F62005EEBE9 /* MXAesKeyData.m in Sources */, 329FB17A1A0A74B100A5E88E /* MXTools.m in Sources */, B1EE98D9280723BE00AB63F0 /* MXBeaconInfoSummary.swift in Sources */, @@ -7426,7 +6973,6 @@ B146D4D821A5A44E00D8C2C6 /* MXScanRealmInMemoryProvider.m in Sources */, 324BE46D1E422766008D99D4 /* MXMegolmSessionData.m in Sources */, 325AD44123BE3E7500FF5277 /* MXCrossSigningInfo.m in Sources */, - 329E8089224E261600A48C3A /* MXKeyVerificationTransaction.m in Sources */, C6F935891E5B3BE600FC34BF /* MXEventTimeline.swift in Sources */, B1F04B132811E9D300103EBE /* MXBeaconInfoSummaryStoreProtocol.swift in Sources */, 183892802702F553003F0C4F /* MXRoomNameDefaultStringLocalizer.m in Sources */, @@ -7434,7 +6980,6 @@ ED5580732970265A003443E3 /* MXCryptoSDKLogger.swift in Sources */, EC8A539725B1BC77004E0802 /* MXCallReplacesEventContent.m in Sources */, B135066E27EA44C800BD3276 /* MXLocationServiceError.swift in Sources */, - 32FA10C21FA1C9EE00E54233 /* MXOutgoingRoomKeyRequestManager.m in Sources */, A780624E27B2CE74005780C0 /* FileManager+Backup.swift in Sources */, 323547D42226D3F500F15F94 /* MXWellKnown.m in Sources */, 320DFDE519DD99B60068622A /* MXRestClient.m in Sources */, @@ -7447,7 +6992,6 @@ buildActionMask = 2147483647; files = ( 329E808F22512DF500A48C3A /* MXCryptoKeyVerificationTests.m in Sources */, - EDDD90C82901611600B760E0 /* MXLegacyCrypto+LegacyCrossSigning.swift in Sources */, 322985D226FC9E61001890BC /* MXSessionTracker.swift in Sources */, 32B477822638133C00EA5800 /* MXFilterUnitTests.m in Sources */, 322985CF26FBAE7B001890BC /* TestObserver.swift in Sources */, @@ -7472,7 +7016,6 @@ ED7019F72886CA6C00FC31B9 /* VerificationRequestStub.swift in Sources */, ED55807629709943003443E3 /* MatrixSDKTestsE2EData.swift in Sources */, B14EECEE2578FE3F00448735 /* MXAuthenticationSessionUnitTests.swift in Sources */, - ED558070296F1BEE003443E3 /* MXCryptoMigrationV2UnitTests.swift in Sources */, ED2DD11D286C4F4400F06731 /* MXCryptoRequestsUnitTests.swift in Sources */, 32832B5D1BCC048300241108 /* MXStoreMemoryStoreTests.m in Sources */, EDB4209927DF842F0036AF39 /* MXEventFixtures.swift in Sources */, @@ -7480,11 +7023,8 @@ 32114A7F1A24E15500FF2EC4 /* MXMyUserTests.m in Sources */, ED79B9852940BB45008952F6 /* MXToDevicePayloadUnitTests.swift in Sources */, 32832B5E1BCC048300241108 /* MXStoreNoStoreTests.m in Sources */, - A816247C25F60C7700A46F05 /* MXDeviceListOperationsPoolUnitTests.swift in Sources */, B1660F1C260A20B900C3AA12 /* MXSpaceServiceTest.swift in Sources */, EDA69340290BA92E00223252 /* MXCryptoMachineUnitTests.swift in Sources */, - ED35652C281150310002BF6A /* MXOlmInboundGroupSessionUnitTests.swift in Sources */, - 32C9B71823E81A1C00C6F30A /* MXCrossSigningVerificationTests.m in Sources */, 323C5A081A70E53500FB0549 /* MXToolsUnitTests.m in Sources */, 3281E89E19E299C000976E1A /* MXErrorUnitTests.m in Sources */, ED1FE9062912D2EB0046F722 /* MXRoomEventDecryptionUnitTests.swift in Sources */, @@ -7495,7 +7035,6 @@ EC40385D28A16EDA0067D5B8 /* MXAes256KeyBackupTests.m in Sources */, ED5EF155297AB93800A5ADDA /* MXRoomEventEncryptionUnitTests.swift in Sources */, ED6DAC0728C77E1100ECDCB6 /* MXForwardedRoomKeyEventContentUnitTests.swift in Sources */, - ED4368B129784CCE002B6272 /* MXRealmCryptoStoreTests.swift in Sources */, 3A9E2B4328EB3960000DB2A7 /* MXMatrixVersionsUnitTests.swift in Sources */, 3265CB3B1A151C3800E24B2F /* MXRoomStateTests.m in Sources */, ED8F1D302885AB0300F897E7 /* MXTrustLevelSourceUnitTests.swift in Sources */, @@ -7505,7 +7044,6 @@ 326D1EF51BFC79300030947B /* MXPushRuleUnitTests.m in Sources */, EC1165CE27107F3E0089FA56 /* MXRoomListDataManagerTests.swift in Sources */, 324BE45B1E3FA7A8008D99D4 /* MXMegolmExportEncryptionUnitTests.m in Sources */, - ED825F8F29014EDA006A614E /* MXSession+LegacyCrypto.swift in Sources */, 327E9ACF2284783E00A98BC1 /* MXEventAnnotationUnitTests.swift in Sources */, EC40386128A1A3830067D5B8 /* MXBaseKeyBackupTests.m in Sources */, ED6DAC0D28C784E200ECDCB6 /* MXRoomKeyEventContentUnitTests.swift in Sources */, @@ -7521,21 +7059,17 @@ 32B477852638133C00EA5800 /* MXAggregatedEditsUnitTests.m in Sources */, ECB6FA8E267CFF4300A941E4 /* MXCredentialsUnitTests.swift in Sources */, ED7019F52886CA6C00FC31B9 /* MXKeyVerificationRequestV2UnitTests.swift in Sources */, - ED6DAC1128C788A700ECDCB6 /* MXRoomKeyInfoFactoryUnitTests.swift in Sources */, 3A108E6725826F52005EEBE9 /* MXKeyProviderUnitTests.m in Sources */, 3A858DE8275511A4006322C1 /* MXRoomAliasAvailabilityCheckerResultTests.swift in Sources */, EC116593270FB6970089FA56 /* MXBackgroundTaskUnitTests.swift in Sources */, B11BD45C22CB8ABC0064D8B0 /* MXReplyEventParserUnitTests.m in Sources */, ECAE7AEC24ED75F1002FA813 /* MXHTTPAdditionalHeadersUnitTests.m in Sources */, - 32AF9292241112850008A0FD /* MXCryptoSecretShareTests.m in Sources */, ED8F1D342885ADE200F897E7 /* MXCryptoProtocolStubs.swift in Sources */, 3281E8A019E2CC1200976E1A /* MXHTTPClientTests.m in Sources */, 321809B919EEBF3000377451 /* MXEventTests.m in Sources */, - B1B44319283D00CA00BB26F4 /* MXMegolmDecryptionUnitTests.swift in Sources */, EDF4678727E3331D00435913 /* EventsEnumeratorDataSourceStub.swift in Sources */, EC746C59274E61EF002AD24C /* MXThreadingServiceTests.swift in Sources */, 32A31BC120D3F4C4005916C7 /* MXFilterTests.m in Sources */, - ED6DAC1528C78D4000ECDCB6 /* MXMemoryCryptoStore.swift in Sources */, 32B477842638133C00EA5800 /* MXAggregatedReferenceUnitTests.m in Sources */, 32B0E3E423A384D40054FF1A /* MXAggregatedReferenceTests.m in Sources */, 32D5D16323E400A600E3E37C /* MXRoomSummaryTrustTests.m in Sources */, @@ -7553,9 +7087,7 @@ ED7019FB2886CA6C00FC31B9 /* MXSASTransactionV2UnitTests.swift in Sources */, 32B0E3E723A3864C0054FF1A /* MXEventReferenceUnitTests.swift in Sources */, 32720DA2222EB5650086FFF5 /* MXAutoDiscoveryTests.m in Sources */, - ED55806D296F0E3A003443E3 /* MXCryptoMigrationStoreUnitTests.swift in Sources */, ED8943D427E34762000FC39C /* MXMemoryRoomStoreUnitTests.swift in Sources */, - ED5C95CE2833E85600843D82 /* MXOlmDeviceUnitTests.swift in Sources */, 327E37B91A977810007F026F /* MXLoggerUnitTests.m in Sources */, 18937E7C273A5AE500902626 /* MXPollRelationTests.m in Sources */, EDA2CDD628F5C4230088ACE7 /* MXQRCodeTransactionV2UnitTests.swift in Sources */, @@ -7563,18 +7095,14 @@ 329571931B0240CE00ABB3BA /* MXVoIPTests.m in Sources */, ED8F1D322885AC5700F897E7 /* Device+Stub.swift in Sources */, EDE1B13B28B7BEAB000DEEE8 /* MXCrossSigningV2UnitTests.swift in Sources */, - EDA40A1B29E9E2BF00C0CAB9 /* LegacyRealmStore.swift in Sources */, EC746C56274E5197002AD24C /* MXThreadingServiceUnitTests.swift in Sources */, ED8F1D252885A39800F897E7 /* MXCrossSigningInfoSourceUnitTests.swift in Sources */, ED6DAC1E28C79D2000ECDCB6 /* MXUnrequestedForwardedRoomKeyManagerUnitTests.swift in Sources */, 32A27D1F19EC335300BAFADE /* MXRoomTests.m in Sources */, 32D8CAC219DEE6ED002AF8A0 /* MXRestClientNoAuthAPITests.m in Sources */, 32FCAB4D19E578860049C555 /* MXRestClientTests.m in Sources */, - 32C78BA7256D227D008130B1 /* MXCryptoMigrationTests.m in Sources */, ED7019F92886CA6C00FC31B9 /* SasStub.swift in Sources */, EDA40A0529E9D6BE00C0CAB9 /* MXKeyProviderStub.swift in Sources */, - ED21F68528104DA2002FF83D /* MXMegolmEncryptionTests.swift in Sources */, - ED44F01A28180F4000452A5D /* MXSharedHistoryKeyManagerUnitTests.swift in Sources */, 322985CB26FAF898001890BC /* MXSession.swift in Sources */, EC131B192779D8D500712964 /* MXThreadEventTimelineUnitTests.swift in Sources */, B135067427EB201E00BD3276 /* MXLocationServiceTests.swift in Sources */, @@ -7582,7 +7110,6 @@ 18C26C4F273C0EB300805154 /* MXPollAggregatorTests.swift in Sources */, ED35652F281153480002BF6A /* MXMegolmSessionDataUnitTests.swift in Sources */, 32EEA83F2603CA140041425B /* MXRestClientExtensionsTests.m in Sources */, - EDDB07CA297EE0A7005249A6 /* MXCryptoV2FactoryUnitTests.swift in Sources */, 18121F7A273E6E4200B68ADF /* PollBuilder.swift in Sources */, 18121F7F273E837300B68ADF /* PollModels.swift in Sources */, 32C03CB62123076F00D92712 /* DirectRoomTests.m in Sources */, @@ -7617,7 +7144,6 @@ ECD289BD26FDE4E300F268CF /* MXRoomListDataCounts.swift in Sources */, B1EE98CF2804820800AB63F0 /* MXLocation.m in Sources */, B19EC8A3260E134A00543BEC /* MXRoomInitialStateEventBuilder.swift in Sources */, - B14EF1CA2397E90400758AF0 /* MXOlmInboundGroupSession.m in Sources */, B14EF1CB2397E90400758AF0 /* MXRoomAccountData.m in Sources */, 189B8D112A864C250088D7CE /* DehydrationService.swift in Sources */, 3AB5EBB5270B332B0058703A /* MXSpaceStore.swift in Sources */, @@ -7633,6 +7159,7 @@ B14EF1CF2397E90400758AF0 /* MXScanManager.m in Sources */, 3291DC8623DF52E20009732F /* MXRoomCreationParameters.m in Sources */, B16C2448283AA85E00F5D1FE /* RLMSupport.swift in Sources */, + A777E5852C98DF1900B39397 /* MXCrossSigning.m in Sources */, B14EF1D02397E90400758AF0 /* MXInvite3PID.m in Sources */, B14EF1D12397E90400758AF0 /* MXPushRuleDisplayNameCondtionChecker.m in Sources */, B14EF1D22397E90400758AF0 /* MXFilter.m in Sources */, @@ -7659,9 +7186,8 @@ 3274538723FD69D600438328 /* MXKeyVerificationRequestByToDeviceJSONModel.m in Sources */, B14EF1DF2397E90400758AF0 /* MXAggregatedEditsUpdater.m in Sources */, ECDA763327B293D9000C48CF /* MXThreadProtocol.swift in Sources */, - B14EF1E02397E90400758AF0 /* MXRealmCryptoStore.m in Sources */, + A777E57F2C98D8A500B39397 /* MXKeyVerificationTransaction.m in Sources */, B14EF1E12397E90400758AF0 /* MXRoomSummary.m in Sources */, - EDCF802E2941FF220059E774 /* MXCryptoMigrationV2.swift in Sources */, 66398BA627A4085B00466E89 /* MXRefreshResponse.m in Sources */, B14EF1E22397E90400758AF0 /* MXPushRuleRoomMemberCountConditionChecker.m in Sources */, B14EF1E32397E90400758AF0 /* MXCall.m in Sources */, @@ -7687,7 +7213,6 @@ B14EF1EA2397E90400758AF0 /* MXRealmMediaScan.m in Sources */, 3AB5EBB8270ED1C00058703A /* MXSpaceFileStore.swift in Sources */, B14EF1EB2397E90400758AF0 /* MXRoomOperation.m in Sources */, - B14EF1EC2397E90400758AF0 /* MXCryptoAlgorithms.m in Sources */, B14EF1ED2397E90400758AF0 /* MXIdentityServerHashDetails.m in Sources */, B14EF1EE2397E90400758AF0 /* MXRoomMember.m in Sources */, ECBF658226DE2A8500AA3A99 /* MXMemoryRoomOutgoingMessagesStore.m in Sources */, @@ -7709,11 +7234,9 @@ B14EF1F72397E90400758AF0 /* MXLoginTerms.m in Sources */, B14EF1F82397E90400758AF0 /* MXReplyEventParts.m in Sources */, B16C2456283AB0DE00F5D1FE /* MXRealmBeaconMapper.swift in Sources */, - EDD4198228DCAA7B007F3757 /* MXNativeKeyBackupEngine.m in Sources */, 3259D02426037A7200C365DB /* NSArray.swift in Sources */, 3A108A8125810C96005EEBE9 /* MXKeyData.m in Sources */, 66836AB827CFA17200515780 /* MXEventStreamService.swift in Sources */, - 3A59A4A025A7A16F00DDA1FC /* MXOlmOutboundGroupSession.m in Sources */, ED01915328C64E0400ED3A69 /* MXForwardedRoomKeyEventContent.m in Sources */, ED8F1D3C2885BB2D00F897E7 /* MXCryptoProtocols.swift in Sources */, EC0B941227184E8A00B4D440 /* MXRoomSummaryMO.swift in Sources */, @@ -7729,7 +7252,6 @@ 32F00ABE2488E1CD00131741 /* MXRecoveryService.m in Sources */, B14EF1FB2397E90400758AF0 /* MXEnumConstants.swift in Sources */, B14EF1FC2397E90400758AF0 /* MXEventRelations.m in Sources */, - EC403847289A7F0F0067D5B8 /* MXCurve25519KeyBackupAlgorithm.m in Sources */, EC0B941C2718654B00B4D440 /* NSManagedObject+MatrixSDK.swift in Sources */, EDD578E22881C37C006739DD /* MXDeviceInfoSource.swift in Sources */, EC8A53E325B1BCC6004E0802 /* MXThirdPartyUserInstance.m in Sources */, @@ -7750,7 +7272,6 @@ ECE3DF852707370000FB4C96 /* MXRoomListDataPaginationOptions.swift in Sources */, B14EF2022397E90400758AF0 /* MXEventUnsignedData.m in Sources */, 324AAC792399140D00380A66 /* MXKeyVerificationKey.m in Sources */, - B14EF2032397E90400758AF0 /* MXMegolmEncryption.m in Sources */, B14EECDA2577DE7B00448735 /* MXLoginSSOIdentityProvider.m in Sources */, B14EF2042397E90400758AF0 /* MXIncomingRoomKeyRequest.m in Sources */, 32581DEB23C8C0C900832EAA /* MXUserTrustLevel.m in Sources */, @@ -7760,9 +7281,9 @@ B14EF2052397E90400758AF0 /* MX3PidAddManager.swift in Sources */, B14EF2062397E90400758AF0 /* MXGroup.m in Sources */, B14EF2072397E90400758AF0 /* MXAutoDiscovery.m in Sources */, - 3297913123AA126500F7BB9B /* MXKeyVerificationStatusResolver.m in Sources */, ED5C754528B3E80300D24E85 /* MXLogObjcWrapper.m in Sources */, ED47CB6E28523995004FD755 /* MXCryptoV2.swift in Sources */, + A71E55352CA6FEBD00B06B17 /* MXKeyBackupPassword.m in Sources */, ED463ECC29B0B75800957941 /* EventEncryptionAlgorithm+String.swift in Sources */, EDF154E2296C203E004D7FFE /* MXCryptoMachineStore.swift in Sources */, ED37834A29C9B6E700A449DA /* MXEventDecryptionDecoration.swift in Sources */, @@ -7790,7 +7311,6 @@ B14EF2162397E90400758AF0 /* MXOlmSession.m in Sources */, EC8A53A425B1BC77004E0802 /* MXCallEventContent.m in Sources */, ED7019EC2886C33A00FC31B9 /* MXKeyVerificationManagerV2.swift in Sources */, - B14EF2172397E90400758AF0 /* MXMegolmDecryption.m in Sources */, 3A23A740256D322C00B9D00F /* MXAes.m in Sources */, B14EF2182397E90400758AF0 /* MXEmojiRepresentation.m in Sources */, EC8A53E525B1BCC6004E0802 /* MXThirdPartyUsersResponse.m in Sources */, @@ -7804,7 +7324,6 @@ B14EF21E2397E90400758AF0 /* MXEventDecryptionResult.m in Sources */, EC60EDBF265CFE8600B39A4E /* MXRoomSyncAccountData.m in Sources */, ED2DD115286C450600F06731 /* MXCryptoMachine.swift in Sources */, - 320B393D239FD15E00BE2C06 /* MXKeyVerificationRequest.m in Sources */, 18B22A7327707CDD00482170 /* MXEventContentLocation.m in Sources */, EC05473725FF8A3C0047ECD7 /* MXVirtualRoomInfo.m in Sources */, 3290293524CB10D000DA7BB6 /* MatrixSDKVersion.m in Sources */, @@ -7812,28 +7331,23 @@ B14EF21F2397E90400758AF0 /* MXMyUser.m in Sources */, EDAAC42528E3177300DD89B5 /* MXRecoveryServiceDependencies.swift in Sources */, EC60EDAB265CFE3B00B39A4E /* MXRoomSyncTimeline.m in Sources */, + A777E5762C98D6F100B39397 /* MXKeyVerificationManager.m in Sources */, B14EF2202397E90400758AF0 /* (null) in Sources */, ED647E3F292CE64400A47519 /* MXSessionStartupProgress.swift in Sources */, B14EF2212397E90400758AF0 /* MX3PID.swift in Sources */, 18121F79273E6E4100B68ADF /* PollBuilder.swift in Sources */, - ED558069296F0361003443E3 /* MXCryptoMigrationStore.swift in Sources */, EC383BB325406892002FBBE6 /* MXSyncResponseStore.swift in Sources */, ECBF658826DE3DF800AA3A99 /* MXFileRoomOutgoingMessagesStore.m in Sources */, B14EF2222397E90400758AF0 /* MXMediaScan.m in Sources */, - EC40384D289A7F260067D5B8 /* MXAes256KeyBackupAlgorithm.m in Sources */, B14EF2232397E90400758AF0 /* MXEvent.swift in Sources */, B14EF2242397E90400758AF0 /* MXEventScan.m in Sources */, - B14EF2252397E90400758AF0 /* MXOutgoingSASTransaction.m in Sources */, - B14EF2262397E90400758AF0 /* MXOlmEncryption.m in Sources */, 324AAC782399140D00380A66 /* MXKeyVerificationJSONModel.m in Sources */, EC60ED74265CFCA500B39A4E /* MXToDeviceSyncResponse.m in Sources */, - B14EF2272397E90400758AF0 /* MXOlmDecryption.m in Sources */, B14EF2282397E90400758AF0 /* MXNoStore.m in Sources */, ECF29BD4264194BB0053E6D6 /* MXCallAssertedIdentityEventContent.m in Sources */, B1F04B1B2812A4E000103EBE /* MXBeaconInfoSummaryProtocol.swift in Sources */, EC60EDDD265CFF0600B39A4E /* MXInvitedRoomSync.m in Sources */, B14EF2292397E90400758AF0 /* MXRealmEventScanMapper.m in Sources */, - ED4114EF292E49C000728459 /* MXLegacyBackgroundCrypto.swift in Sources */, ED01915728C64E0400ED3A69 /* MXRoomKeyEventContent.m in Sources */, ECDA763C27B6B74C000C48CF /* MXCapabilities.m in Sources */, EC0B94252718E3EF00B4D440 /* MXCoreDataRoomListDataFetcher.swift in Sources */, @@ -7852,8 +7366,6 @@ B14EF22D2397E90400758AF0 /* MXRealmReactionCount.m in Sources */, B14EF22E2397E90400758AF0 /* MXCryptoTools.m in Sources */, B14EF22F2397E90400758AF0 /* (null) in Sources */, - B14EF2302397E90400758AF0 /* MXDeviceListOperation.m in Sources */, - 32C78B6B256CFC4D008130B1 /* MXCryptoMigration.m in Sources */, ECDA763027B292B5000C48CF /* MXThreadModel.swift in Sources */, B14EF2312397E90400758AF0 /* MX3PidAddSession.m in Sources */, B14EF2322397E90400758AF0 /* MXBugReportRestClient.m in Sources */, @@ -7866,7 +7378,6 @@ 91CC0FCD26A033AE00C2A387 /* MXURLPreview.m in Sources */, B14EF2362397E90400758AF0 /* MXAggregations.m in Sources */, 3A858DE327529183006322C1 /* MXRoomCapabilityType.swift in Sources */, - B14EF2372397E90400758AF0 /* MXKeyVerificationManager.m in Sources */, 8EC5110D256822B400EC4E5B /* MXTaggedEvents.m in Sources */, B14EF2382397E90400758AF0 /* MXEvent.m in Sources */, EC0B940E27184E8A00B4D440 /* MXRoomLastMessageMO.swift in Sources */, @@ -7902,10 +7413,9 @@ EDF1B6912876CD2C00BBBCEE /* MXTaskQueue.swift in Sources */, B14EF2422397E90400758AF0 /* MXDeviceInfo.m in Sources */, ED5EF150297AB29F00A5ADDA /* MXEventDecryptionResult+DecryptedEvent.swift in Sources */, - B14EF2432397E90400758AF0 /* MXIncomingSASTransaction.m in Sources */, B14EF2442397E90400758AF0 /* NSObject+sortedKeys.m in Sources */, + A777E57C2C98D82500B39397 /* MXKeyVerificationRequest.m in Sources */, B14EF2452397E90400758AF0 /* MXAggregatedReactionsUpdater.m in Sources */, - 3A59A52C25A7B1B000DDA1FC /* MXOutboundSessionInfo.m in Sources */, ED28068828F06D360070AE9F /* MXQRCodeTransactionV2.swift in Sources */, B14EECE82577F76100448735 /* MXLoginSSOFlow.m in Sources */, B14EF2462397E90400758AF0 /* MXRoomMembers.m in Sources */, @@ -7928,13 +7438,13 @@ EC1165C527107E330089FA56 /* MXRoomListDataFilterOptions.swift in Sources */, 3A858DE227528EEB006322C1 /* MXHomeserverCapabilitiesService.swift in Sources */, ECDA764F27BA963D000C48CF /* MXBooleanCapability.m in Sources */, + A759E23E2C98EE7D002429A8 /* MXCrypto.m in Sources */, B14EF24C2397E90400758AF0 /* MXCredentials.m in Sources */, ED6F4EFD2987F0FC007D1191 /* MXEncryptedKeyBackup.swift in Sources */, EC116590270F3ABF0089FA56 /* RLMRealm+MatrixSDK.m in Sources */, B14EF24D2397E90400758AF0 /* MXOutgoingRoomKeyRequest.m in Sources */, B14EF24E2397E90400758AF0 /* MXAllowedCertificates.m in Sources */, B14EF24F2397E90400758AF0 /* MXLRUCache.m in Sources */, - B14EF2502397E90400758AF0 /* MXIncomingRoomKeyRequestManager.m in Sources */, ED2DD119286C450600F06731 /* MXCryptoRequests.swift in Sources */, EDAAC41A28E2FCFE00DD89B5 /* MXCryptoSecretStoreV2.swift in Sources */, B14EF2512397E90400758AF0 /* MXRoomEventFilter.m in Sources */, @@ -8021,15 +7531,12 @@ B14EF2712397E90400758AF0 /* MXSASTransaction.m in Sources */, EC0B9419271855CA00B4D440 /* MXCoreDataRoomSummaryStore.swift in Sources */, B14EF2722397E90400758AF0 /* MXDiscoveredClientConfig.m in Sources */, - B19A30A1240424BD00FB6F35 /* MXQRCodeTransaction.m in Sources */, B14EF2732397E90400758AF0 /* MXRealmEventScan.m in Sources */, EC2EAD00266625170038B61F /* MXRoomLastMessage.m in Sources */, EC60ED88265CFD0700B39A4E /* MXRoomsSyncResponse.m in Sources */, - B14EF2742397E90400758AF0 /* MXDeviceListOperationsPool.m in Sources */, B18B0E4B25FB783B00E32151 /* MXSpaceCreationParameters.swift in Sources */, B14EF2752397E90400758AF0 /* MXResponse.swift in Sources */, B14EF2772397E90400758AF0 /* MXDecryptionResult.m in Sources */, - ED6DABFD28C7542800ECDCB6 /* MXRoomKeyInfoFactory.swift in Sources */, B14EF2782397E90400758AF0 /* MXTransactionCancelCode.m in Sources */, ED5EF146297AB1F200A5ADDA /* MXRoomEventEncryption.swift in Sources */, EDAAC42028E30F4C00DD89B5 /* (null) in Sources */, @@ -8048,19 +7555,15 @@ B14EF27D2397E90400758AF0 /* MXKeyBackup.m in Sources */, EC1165BD27107E330089FA56 /* MXRoomListDataFetchOptions.swift in Sources */, ECDA764927BA939E000C48CF /* MXRoomVersionsCapability.m in Sources */, - B14EF27E2397E90400758AF0 /* MXCrypto.m in Sources */, B14EF27F2397E90400758AF0 /* MXRoomPredecessorInfo.m in Sources */, B14EF2802397E90400758AF0 /* MXServiceTerms.m in Sources */, - 3229535425A5F7220012FCF0 /* MXBackgroundCryptoStore.m in Sources */, B14EF2812397E90400758AF0 /* MXNotificationCenter.m in Sources */, EC60ED60265CFC2C00B39A4E /* MXSyncResponse.m in Sources */, EC6D007C28E1F15400152144 /* MXDevice.m in Sources */, EDCB65E32912AB0C00F55D4D /* MXRoomEventDecryption.swift in Sources */, 3A108AB825812995005EEBE9 /* MXKeyProvider.m in Sources */, - B14EF2822397E90400758AF0 /* MXDeviceList.m in Sources */, ED7019E62886C32900FC31B9 /* MXSASTransactionV2.swift in Sources */, ECCA02BF27348FE300B6F34F /* MXThread.swift in Sources */, - 320B3937239FA56900BE2C06 /* MXKeyVerificationByDMRequest.m in Sources */, B14EF2832397E90400758AF0 /* MXRoomCreateContent.m in Sources */, ED5AE8C62816C8CF00105072 /* MXCoreDataRoomSummaryStore.xcdatamodeld in Sources */, EC5C562B27A36EDB0014CBE9 /* MXInReplyTo.m in Sources */, @@ -8070,8 +7573,6 @@ B14EF2842397E90400758AF0 /* MXUIKitBackgroundModeHandler.m in Sources */, B14EF2852397E90400758AF0 /* (null) in Sources */, 32A9F8E1244720B10069C65B /* MXThrottler.m in Sources */, - 3274538D23FD918800438328 /* MXKeyVerificationByToDeviceRequest.m in Sources */, - 32CEEF5223B0AB030039BA98 /* MXCrossSigning.m in Sources */, 324DD2C8246E638B00377005 /* MXAesHmacSha2.m in Sources */, B14EF2862397E90400758AF0 /* MXRealmMediaScanStore.m in Sources */, 320F7D2D260A8E3B00EF8608 /* MXSyncResponseStoreMetaDataModel.swift in Sources */, @@ -8079,11 +7580,10 @@ 324DD2A3246AE1EF00377005 /* MXEncryptedSecretContent.m in Sources */, EC8A53B625B1BC77004E0802 /* MXCallHangupEventContent.m in Sources */, ED5EF14C297AB29F00A5ADDA /* MXDeviceVerification+LocalTrust.swift in Sources */, - B14EF2882397E90400758AF0 /* MXOlmDevice.m in Sources */, B14766BA23D9D9420091F721 /* MXUsersTrustLevelSummary.m in Sources */, - B14EF2892397E90400758AF0 /* MXKeyBackupPassword.m in Sources */, 180F858727A2AF3000F4E5A5 /* MXWellKnownTileServerConfig.m in Sources */, 3A108A9B25810F62005EEBE9 /* MXAesKeyData.m in Sources */, + A777E5792C98D79D00B39397 /* MXQRCodeTransaction.m in Sources */, B14EF28A2397E90400758AF0 /* MXTools.m in Sources */, ED5EF14E297AB29F00A5ADDA /* MXRoomHistoryVisibility+HistoryVisibility.swift in Sources */, B14EF28B2397E90400758AF0 /* MXRoomSummaryUpdater.m in Sources */, @@ -8096,7 +7596,6 @@ EC8A53BE25B1BC77004E0802 /* MXCallRejectEventContent.m in Sources */, B1EE98DA280723BE00AB63F0 /* MXBeaconInfoSummary.swift in Sources */, B1A0270326162110001AADFF /* MXSpaceChildrenResponse.m in Sources */, - B14EF28E2397E90400758AF0 /* MXKeyVerificationTransaction.m in Sources */, EC60EE09265CFFF400B39A4E /* MXGroupSyncProfile.m in Sources */, B14EF28F2397E90400758AF0 /* MXEventTimeline.swift in Sources */, B14EF2902397E90400758AF0 /* MXJSONModels.swift in Sources */, @@ -8105,7 +7604,6 @@ EC8A53A225B1BC77004E0802 /* MXCallSelectAnswerEventContent.m in Sources */, 183892812702F553003F0C4F /* MXRoomNameDefaultStringLocalizer.m in Sources */, ED5580742970265A003443E3 /* MXCryptoSDKLogger.swift in Sources */, - B14EF2912397E90400758AF0 /* MXOutgoingRoomKeyRequestManager.m in Sources */, B14EF2922397E90400758AF0 /* MXWellKnown.m in Sources */, B1A026F926161EF5001AADFF /* MXSpaceChildSummaryResponse.m in Sources */, A780624F27B2CE74005780C0 /* FileManager+Backup.swift in Sources */, @@ -8120,7 +7618,6 @@ buildActionMask = 2147483647; files = ( B1E09A282397FD010057C069 /* MXMockCallStackCall.m in Sources */, - EDDD90C92901611600B760E0 /* MXLegacyCrypto+LegacyCrossSigning.swift in Sources */, 322985D326FC9E61001890BC /* MXSessionTracker.swift in Sources */, B1E09A392397FD7D0057C069 /* MXMyUserTests.m in Sources */, 322985D026FBAE7B001890BC /* TestObserver.swift in Sources */, @@ -8144,18 +7641,15 @@ ED7019F82886CA6C00FC31B9 /* VerificationRequestStub.swift in Sources */, ED55807729709943003443E3 /* MatrixSDKTestsE2EData.swift in Sources */, 18121F76273E6D2400B68ADF /* MXPollBuilderTests.swift in Sources */, - ED558071296F1BEE003443E3 /* MXCryptoMigrationV2UnitTests.swift in Sources */, B1E09A1A2397FCE90057C069 /* MXAggregatedEditsTests.m in Sources */, B1E09A1F2397FCE90057C069 /* MXAutoDiscoveryTests.m in Sources */, EDB4209A27DF842F0036AF39 /* MXEventFixtures.swift in Sources */, ECDBE69428E5E16F000C83AF /* MXClientInformationServiceUnitTests.swift in Sources */, B1E09A2E2397FD750057C069 /* MXRestClientTests.m in Sources */, ED79B9862940BB45008952F6 /* MXToDevicePayloadUnitTests.swift in Sources */, - 32C9B71923E81A1C00C6F30A /* MXCrossSigningVerificationTests.m in Sources */, B1E09A1D2397FCE90057C069 /* MXCryptoKeyVerificationTests.m in Sources */, B1E09A472397FD990057C069 /* MXEventScanStoreUnitTests.m in Sources */, EDA69341290BA92E00223252 /* MXCryptoMachineUnitTests.swift in Sources */, - ED35652D281150310002BF6A /* MXOlmInboundGroupSessionUnitTests.swift in Sources */, B1E09A3D2397FD820057C069 /* MXStoreFileStoreTests.m in Sources */, 32CEEF3E23AD134A0039BA98 /* MXCrossSigningTests.m in Sources */, 32EEA8402603CA140041425B /* MXRestClientExtensionsTests.m in Sources */, @@ -8167,7 +7661,6 @@ EC40385E28A16EDA0067D5B8 /* MXAes256KeyBackupTests.m in Sources */, ED5EF156297AB93800A5ADDA /* MXRoomEventEncryptionUnitTests.swift in Sources */, ED6DAC0828C77E1100ECDCB6 /* MXForwardedRoomKeyEventContentUnitTests.swift in Sources */, - ED4368B229784CCE002B6272 /* MXRealmCryptoStoreTests.swift in Sources */, 3A9E2B4428EB3960000DB2A7 /* MXMatrixVersionsUnitTests.swift in Sources */, 32B477AA2638186000EA5800 /* MXHTTPAdditionalHeadersUnitTests.m in Sources */, B135066A27EA100100BD3276 /* MXBeaconInfoUnitTests.swift in Sources */, @@ -8177,7 +7670,6 @@ EC1165CF27107F3E0089FA56 /* MXRoomListDataManagerTests.swift in Sources */, ED7019E22886C28900FC31B9 /* MXCryptoProtocolStubs.swift in Sources */, B1E09A3E2397FD820057C069 /* MXToolsUnitTests.m in Sources */, - ED825F9029014EDA006A614E /* MXSession+LegacyCrypto.swift in Sources */, 32B477912638133D00EA5800 /* MXAggregatedEditsUnitTests.m in Sources */, EC40386228A1A3830067D5B8 /* MXBaseKeyBackupTests.m in Sources */, ED6DAC0E28C784E200ECDCB6 /* MXRoomKeyEventContentUnitTests.swift in Sources */, @@ -8190,10 +7682,8 @@ 324DD2BC246C3ADE00377005 /* MXCryptoSecretStorageTests.m in Sources */, B19A30D92404335D00FB6F35 /* MXQRCodeDataUnitTests.m in Sources */, B1E09A212397FCE90057C069 /* DirectRoomTests.m in Sources */, - 32AF9293241112850008A0FD /* MXCryptoSecretShareTests.m in Sources */, B1E09A462397FD990057C069 /* MXMediaScanStoreUnitTests.m in Sources */, ED7019F62886CA6C00FC31B9 /* MXKeyVerificationRequestV2UnitTests.swift in Sources */, - ED6DAC1228C788A700ECDCB6 /* MXRoomKeyInfoFactoryUnitTests.swift in Sources */, ECB6FA8F267CFF4300A941E4 /* MXCredentialsUnitTests.swift in Sources */, 3A858DE9275511A4006322C1 /* MXRoomAliasAvailabilityCheckerResultTests.swift in Sources */, ED7019DF2886C25600FC31B9 /* MXDeviceInfoUnitTests.swift in Sources */, @@ -8204,10 +7694,8 @@ 32D5D16423E400A600E3E37C /* MXRoomSummaryTrustTests.m in Sources */, B1E09A1B2397FCE90057C069 /* MXAggregatedReactionTests.m in Sources */, B1E09A3F2397FD820057C069 /* MXNotificationCenterTests.m in Sources */, - B1B4431A283D00CA00BB26F4 /* MXMegolmDecryptionUnitTests.swift in Sources */, EDF4678827E3331D00435913 /* EventsEnumeratorDataSourceStub.swift in Sources */, EC746C5A274E61EF002AD24C /* MXThreadingServiceTests.swift in Sources */, - ED6DAC1628C78D4000ECDCB6 /* MXMemoryCryptoStore.swift in Sources */, B1E09A222397FCE90057C069 /* MXRoomSummaryTests.m in Sources */, B1E09A3A2397FD820057C069 /* MXStoreTests.m in Sources */, B1E09A342397FD750057C069 /* MXRoomStateDynamicTests.m in Sources */, @@ -8226,18 +7714,14 @@ 32B4778F2638133D00EA5800 /* MXJSONModelUnitTests.m in Sources */, B1F939F626289F2600D0E525 /* MXSpaceChildContentTests.swift in Sources */, EC40386828A279220067D5B8 /* MXKeyBackupUnitTests.swift in Sources */, - ED55806E296F0E3A003443E3 /* MXCryptoMigrationStoreUnitTests.swift in Sources */, B1E09A412397FD820057C069 /* MXAccountDataTests.m in Sources */, B1E09A2D2397FD750057C069 /* MXRestClientNoAuthAPITests.m in Sources */, ED8943D527E34762000FC39C /* MXMemoryRoomStoreUnitTests.swift in Sources */, - ED5C95CF2833E85600843D82 /* MXOlmDeviceUnitTests.swift in Sources */, EDA2CDD728F5C4230088ACE7 /* MXQRCodeTransactionV2UnitTests.swift in Sources */, B1E09A332397FD750057C069 /* MXRoomStateTests.m in Sources */, 18937E7D273A5AE500902626 /* MXPollRelationTests.m in Sources */, B1E09A352397FD7D0057C069 /* MXEventTests.m in Sources */, EDE1B13C28B7BEAB000DEEE8 /* MXCrossSigningV2UnitTests.swift in Sources */, - EDA40A1C29E9E2BF00C0CAB9 /* LegacyRealmStore.swift in Sources */, - A816248525F60D0300A46F05 /* MXDeviceListOperationsPoolUnitTests.swift in Sources */, EC746C57274E5197002AD24C /* MXThreadingServiceUnitTests.swift in Sources */, ED6DAC1F28C79D2000ECDCB6 /* MXUnrequestedForwardedRoomKeyManagerUnitTests.swift in Sources */, B1E09A312397FD750057C069 /* MXSessionTests.m in Sources */, @@ -8246,8 +7730,6 @@ B1E09A322397FD750057C069 /* MXRoomTests.m in Sources */, ED7019FA2886CA6C00FC31B9 /* SasStub.swift in Sources */, EDA40A0629E9D6BE00C0CAB9 /* MXKeyProviderStub.swift in Sources */, - ED21F68628104DA2002FF83D /* MXMegolmEncryptionTests.swift in Sources */, - ED44F01B28180F4000452A5D /* MXSharedHistoryKeyManagerUnitTests.swift in Sources */, 322985CC26FAF898001890BC /* MXSession.swift in Sources */, EC131B1A2779D8D500712964 /* MXThreadEventTimelineUnitTests.swift in Sources */, B135067527EB201E00BD3276 /* MXLocationServiceTests.swift in Sources */, @@ -8255,8 +7737,6 @@ ED7019E12886C26D00FC31B9 /* MXCryptoRequestsUnitTests.swift in Sources */, 18C26C50273C0EB400805154 /* MXPollAggregatorTests.swift in Sources */, ED356530281153480002BF6A /* MXMegolmSessionDataUnitTests.swift in Sources */, - EDDB07CB297EE0A7005249A6 /* MXCryptoV2FactoryUnitTests.swift in Sources */, - 32C78BA8256D227D008130B1 /* MXCryptoMigrationTests.m in Sources */, 18121F7B273E6E4200B68ADF /* PollBuilder.swift in Sources */, 18121F80273E837400B68ADF /* PollModels.swift in Sources */, ED505DC028E1FD160079A3D3 /* MXCryptoKeyBackupEngineUnitTests.swift in Sources */, diff --git a/MatrixSDK/Background/Crypto/MXLegacyBackgroundCrypto.swift b/MatrixSDK/Background/Crypto/MXLegacyBackgroundCrypto.swift deleted file mode 100644 index 5ebd44c784..0000000000 --- a/MatrixSDK/Background/Crypto/MXLegacyBackgroundCrypto.swift +++ /dev/null @@ -1,205 +0,0 @@ -// -// Copyright 2022 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 - -class MXLegacyBackgroundCrypto: MXBackgroundCrypto { - private let credentials: MXCredentials - private let cryptoStore: MXBackgroundCryptoStore - private let olmDevice: MXOlmDevice - - init(credentials: MXCredentials, resetBackgroundCryptoStore: Bool) { - self.credentials = credentials - cryptoStore = MXBackgroundCryptoStore(credentials: credentials, resetBackgroundCryptoStore: resetBackgroundCryptoStore) - olmDevice = MXOlmDevice(store: cryptoStore) - } - - func handleSyncResponse(_ syncResponse: MXSyncResponse) { - for event in syncResponse.toDevice?.events ?? [] { - handleToDeviceEvent(event) - } - } - - func canDecryptEvent(_ event: MXEvent) -> Bool { - if !event.isEncrypted { - return true - } - - guard let senderKey = event.content["sender_key"] as? String, - let sessionId = event.content["session_id"] as? String else { - return false - } - - return cryptoStore.inboundGroupSession(withId: sessionId, andSenderKey: senderKey) != nil - } - - func decryptEvent(_ event: MXEvent) throws { - if !event.isEncrypted { - return - } - - guard let senderKey = event.content["sender_key"] as? String, - let algorithm = event.content["algorithm"] as? String else { - throw MXBackgroundSyncServiceError.unknown - } - - guard let decryptorClass = MXCryptoAlgorithms.shared()?.decryptorClass(forAlgorithm: algorithm) else { - throw MXBackgroundSyncServiceError.unknownAlgorithm - } - - if decryptorClass == MXMegolmDecryption.self { - guard let ciphertext = event.content["ciphertext"] as? String, - let sessionId = event.content["session_id"] as? String else { - throw MXBackgroundSyncServiceError.unknown - } - - let olmResult = try olmDevice.decryptGroupMessage(ciphertext, isEditEvent: event.isEdit(), roomId: event.roomId, inTimeline: nil, sessionId: sessionId, senderKey: senderKey) - - let decryptionResult = MXEventDecryptionResult() - decryptionResult.clearEvent = olmResult.payload - decryptionResult.senderCurve25519Key = olmResult.senderKey - decryptionResult.claimedEd25519Key = olmResult.keysClaimed?["ed25519"] as? String - decryptionResult.forwardingCurve25519KeyChain = olmResult.forwardingCurve25519KeyChain - decryptionResult.decoration = MXEventDecryptionDecoration( - color: olmResult.isUntrusted ? .grey : .none, - message: nil - ) - event.setClearData(decryptionResult) - } else if decryptorClass == MXOlmDecryption.self { - guard let ciphertextDict = event.content["ciphertext"] as? [AnyHashable: Any], - let deviceCurve25519Key = olmDevice.deviceCurve25519Key, - let message = ciphertextDict[deviceCurve25519Key] as? [AnyHashable: Any], - let payloadString = decryptMessageWithOlm(message: message, theirDeviceIdentityKey: senderKey) else { - throw MXBackgroundSyncServiceError.decryptionFailure - } - guard let payloadData = payloadString.data(using: .utf8), - let payload = try? JSONSerialization.jsonObject(with: payloadData, - options: .init(rawValue: 0)) as? [AnyHashable: Any], - let recipient = payload["recipient"] as? String, - recipient == credentials.userId, - let recipientKeys = payload["recipient_keys"] as? [AnyHashable: Any], - let ed25519 = recipientKeys["ed25519"] as? String, - ed25519 == olmDevice.deviceEd25519Key, - let sender = payload["sender"] as? String, - sender == event.sender else { - throw MXBackgroundSyncServiceError.decryptionFailure - } - if let roomId = event.roomId { - guard payload["room_id"] as? String == roomId else { - throw MXBackgroundSyncServiceError.decryptionFailure - } - } - - let claimedKeys = payload["keys"] as? [AnyHashable: Any] - let decryptionResult = MXEventDecryptionResult() - decryptionResult.clearEvent = payload - decryptionResult.senderCurve25519Key = senderKey - decryptionResult.claimedEd25519Key = claimedKeys?["ed25519"] as? String - event.setClearData(decryptionResult) - } else { - throw MXBackgroundSyncServiceError.unknownAlgorithm - } - } - - func reset() { - cryptoStore.reset() - } - - // MARK: - Private - - private func handleToDeviceEvent(_ event: MXEvent) { - // only handle supported events - guard MXTools.isSupportedToDeviceEvent(event) else { - MXLog.debug("[MXLegacyBackgroundCrypto] handleToDeviceEvent: ignore unsupported event") - return - } - - if event.isEncrypted { - do { - try decryptEvent(event) - } catch let error { - MXLog.debug("[MXLegacyBackgroundCrypto] handleToDeviceEvent: Could not decrypt to-device event: \(error)") - return - } - } - - guard let userId = credentials.userId else { - MXLog.error("[MXLegacyBackgroundCrypto] handleToDeviceEvent: Cannot get userId") - return - } - - let factory = MXRoomKeyInfoFactory(myUserId: userId, store: cryptoStore) - guard let key = factory.roomKey(for: event) else { - MXLog.error("[MXLegacyBackgroundCrypto] handleToDeviceEvent: Cannot create megolm key from event") - return - } - - switch key.type { - case .safe: - olmDevice.addInboundGroupSession( - key.info.sessionId, - sessionKey: key.info.sessionKey, - roomId: key.info.roomId, - senderKey: key.info.senderKey, - forwardingCurve25519KeyChain: key.info.forwardingKeyChain, - keysClaimed: key.info.keysClaimed, - exportFormat: key.info.exportFormat, - sharedHistory: key.info.sharedHistory, - untrusted: key.type != .safe - ) - case .unsafe: - MXLog.warning("[MXLegacyBackgroundCrypto] handleToDeviceEvent: Ignoring unsafe keys") - case .unrequested: - MXLog.warning("[MXLegacyBackgroundCrypto] handleToDeviceEvent: Ignoring unrequested keys") - } - } - - private func decryptMessageWithOlm(message: [AnyHashable: Any], theirDeviceIdentityKey: String) -> String? { - let sessionIds = olmDevice.sessionIds(forDevice: theirDeviceIdentityKey) - let messageBody = message[kMXMessageBodyKey] as? String - let messageType = message["type"] as? UInt ?? 0 - - for sessionId in sessionIds ?? [] { - if let payload = olmDevice.decryptMessage(messageBody, - withType: messageType, - sessionId: sessionId, - theirDeviceIdentityKey: theirDeviceIdentityKey) { - return payload - } else { - let foundSession = olmDevice.matchesSession(theirDeviceIdentityKey, - sessionId: sessionId, - messageType: messageType, - ciphertext: messageBody) - if foundSession { - return nil - } - } - } - - if messageType != 0 { - return nil - } - - var payload: NSString? - guard let _ = olmDevice.createInboundSession(theirDeviceIdentityKey, - messageType: messageType, - cipherText: messageBody, - payload: &payload) else { - return nil - } - return payload as String? - } -} diff --git a/MatrixSDK/Background/MXBackgroundCryptoStore.h b/MatrixSDK/Background/MXBackgroundCryptoStore.h deleted file mode 100644 index 1c754f4d2a..0000000000 --- a/MatrixSDK/Background/MXBackgroundCryptoStore.h +++ /dev/null @@ -1,47 +0,0 @@ -// -// Copyright 2021 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 - -#import "MXCryptoStore.h" - -NS_ASSUME_NONNULL_BEGIN - -/** - The `MXBackgroundCryptoStore` implements a minimal set of the `MXCryptoStore` protocol for `MXBackgroundSyncService` - needs. - It ensures that DB write operations required by `MXBackgroundSyncService` are made on a separate DB. - This avoids to have deadlocks between the app process and an iOS notification extension service. - */ -@interface MXBackgroundCryptoStore : NSObject - -/** - Create the store for the passed credentials. - - @param theCredentials the credentials of the account. - @param resetBackgroundCryptoStore if YES, clear the separate DB. - @return the store. - */ -- (instancetype)initWithCredentials:(MXCredentials *)theCredentials resetBackgroundCryptoStore:(BOOL)resetBackgroundCryptoStore; - -/** - Reset the intermediate store. - */ -- (void)reset; - -@end - -NS_ASSUME_NONNULL_END diff --git a/MatrixSDK/Background/MXBackgroundCryptoStore.m b/MatrixSDK/Background/MXBackgroundCryptoStore.m deleted file mode 100644 index f2de55cdd3..0000000000 --- a/MatrixSDK/Background/MXBackgroundCryptoStore.m +++ /dev/null @@ -1,578 +0,0 @@ -// -// Copyright 2021 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 "MXBackgroundCryptoStore.h" - -#import - -#import "MXRealmCryptoStore.h" -#import "MXTools.h" - -NSString *const MXBackgroundCryptoStoreUserIdSuffix = @":bgCryptoStore"; - - -@interface MXBackgroundCryptoStore() -{ - MXCredentials *credentials; - - // The MXRealmCryptoStore used by the app process - // It is used in a read-only way. - MXRealmCryptoStore *cryptoStore; - - // A MXRealmCryptoStore instance we use as an intermediate read-write cache for data (olm and megolm keys) that comes during background syncs. - // Write operations happen only in this instance. - MXRealmCryptoStore *bgCryptoStore; -} -@end - -@implementation MXBackgroundCryptoStore - -- (instancetype)initWithCredentials:(MXCredentials *)theCredentials resetBackgroundCryptoStore:(BOOL)resetBackgroundCryptoStore -{ - self = [super init]; - if (self) - { - credentials = theCredentials; - - // Do not compact Realm DBs from the backgrounc sync process to avoid race conditions on self.cryptoStore with the app process. - // self.bgCryptoStore should not become so big that it needs compaction. It will be reset before. - MXRealmCryptoStore.shouldCompactOnLaunch = NO; - - if ([MXRealmCryptoStore hasDataForCredentials:credentials]) - { - cryptoStore = [[MXRealmCryptoStore alloc] initWithCredentials:credentials]; - cryptoStore.readOnly = YES; - } - else - { - // this is not very likely, Read-only Realm may be out-of-date. Remove it and try again. It'll be recopied from the original Realm on the next call of `hasDataForCredentials` method. - MXLogDebug(@"[MXBackgroundCryptoStore] initWithCredentials: Remove read-only store with credentials: %@:%@", credentials.userId, credentials.deviceId); - [MXRealmCryptoStore deleteReadonlyStoreWithCredentials:credentials]; - - if ([MXRealmCryptoStore hasDataForCredentials:credentials]) - { - cryptoStore = [[MXRealmCryptoStore alloc] initWithCredentials:credentials]; - cryptoStore.readOnly = YES; - } - else - { - // Should never happen - MXLogDebug(@"[MXBackgroundCryptoStore] initWithCredentials: Warning: createStoreWithCredentials: %@:%@", credentials.userId, credentials.deviceId); - cryptoStore = [MXRealmCryptoStore createStoreWithCredentials:credentials]; - cryptoStore.readOnly = NO; - } - } - - MXCredentials *bgCredentials = [MXBackgroundCryptoStore credentialForBgCryptoStoreWithCredentials:credentials]; - - if (resetBackgroundCryptoStore) - { - MXLogDebug(@"[MXBackgroundCryptoStore] initWithCredentials: Delete existing bgCryptoStore if any"); - [MXRealmCryptoStore deleteStoreWithCredentials:bgCredentials]; - } - - if ([MXRealmCryptoStore hasDataForCredentials:bgCredentials]) - { - MXLogDebug(@"[MXBackgroundCryptoStore] initWithCredentials: Reuse existing bgCryptoStore"); - bgCryptoStore = [[MXRealmCryptoStore alloc] initWithCredentials:bgCredentials]; - } - else - { - MXLogDebug(@"[MXBackgroundCryptoStore] initWithCredentials: Create bgCryptoStore"); - bgCryptoStore = [MXRealmCryptoStore createStoreWithCredentials:bgCredentials]; - } - } - return self; -} - -- (void)reset -{ - if (bgCryptoStore) - { - MXCredentials *bgCredentials = [MXBackgroundCryptoStore credentialForBgCryptoStoreWithCredentials:credentials]; - [MXRealmCryptoStore deleteStoreWithCredentials:bgCredentials]; - [MXRealmCryptoStore deleteReadonlyStoreWithCredentials:credentials]; - bgCryptoStore = [MXRealmCryptoStore createStoreWithCredentials:bgCredentials]; - } -} - -- (instancetype)initWithCredentials:(MXCredentials *)theCredentials -{ - NSAssert(NO, @"This method should be useless in the context of MXBackgroundCryptoStore"); - return nil; -} - -+ (BOOL)hasDataForCredentials:(MXCredentials*)credentials -{ - NSAssert(NO, @"This method should be useless in the context of MXBackgroundCryptoStore"); - return NO; -} - -+ (instancetype)createStoreWithCredentials:(MXCredentials*)credentials -{ - NSAssert(NO, @"This method should be useless in the context of MXBackgroundCryptoStore"); - return nil; -} - - -#pragma mark - libolm - -- (OLMAccount*)account -{ - return cryptoStore.account; -} - -- (void)setAccount:(OLMAccount*)account -{ - // Should never happen - MXLogDebug(@"[MXBackgroundCryptoStore] setAccount: identityKeys: %@", account.identityKeys); - - [cryptoStore setAccount:account]; - [bgCryptoStore setAccount:account]; -} - - -- (void)performAccountOperationWithBlock:(void (^)(OLMAccount *olmAccount))block -{ - // If needed, transfer data from cryptoStore to bgCryptoStore first - if (!bgCryptoStore.account) - { - MXLogDebug(@"[MXBackgroundCryptoStore] performAccountOperationWithBlock: Transfer data from cryptoStore to bgCryptoStore"); - [bgCryptoStore setAccount:cryptoStore.account]; - } - - [bgCryptoStore performAccountOperationWithBlock:block]; -} - - -#pragma mark - Olm - -- (void)performSessionOperationWithDevice:(NSString*)deviceKey andSessionId:(NSString*)sessionId block:(void (^)(MXOlmSession *mxOlmSession))block -{ - // If needed, transfer data from cryptoStore to bgCryptoStore first - MXOlmSession *olmSession = [bgCryptoStore sessionWithDevice:deviceKey andSessionId:sessionId]; - if (!olmSession) - { - olmSession = [cryptoStore sessionWithDevice:deviceKey andSessionId:sessionId]; - if (olmSession) - { - MXLogDebug(@"[MXBackgroundCryptoStore] performSessionOperationWithDevice: Transfer data for %@ from cryptoStore to bgCryptoStore", sessionId); - [bgCryptoStore storeSession:olmSession]; - } - } - - [bgCryptoStore performSessionOperationWithDevice:deviceKey andSessionId:sessionId block:block]; -} - -- (MXOlmSession*)sessionWithDevice:(NSString*)deviceKey andSessionId:(NSString*)sessionId -{ - MXOlmSession *olmSession = [bgCryptoStore sessionWithDevice:deviceKey andSessionId:sessionId]; - if (!olmSession) - { - olmSession = [cryptoStore sessionWithDevice:deviceKey andSessionId:sessionId]; - } - return olmSession; -} - -- (NSArray*)sessionsWithDevice:(NSString*)deviceKey -{ - NSArray *bgSessions = [bgCryptoStore sessionsWithDevice:deviceKey] ?: @[]; - NSArray *appSessions = [cryptoStore sessionsWithDevice:deviceKey] ?: @[]; - - NSMutableArray *sessions = [NSMutableArray array]; - [sessions addObjectsFromArray:bgSessions]; - [sessions addObjectsFromArray:appSessions]; - - return sessions; -} - -- (void)storeSession:(MXOlmSession*)session -{ - [bgCryptoStore storeSession:session]; -} - - -#pragma mark - Megolm - -- (MXOlmInboundGroupSession*)inboundGroupSessionWithId:(NSString*)sessionId andSenderKey:(NSString*)senderKey -{ - MXOlmInboundGroupSession *inboundGroupSession = [bgCryptoStore inboundGroupSessionWithId:sessionId andSenderKey:senderKey]; - if (!inboundGroupSession) - { - inboundGroupSession = [cryptoStore inboundGroupSessionWithId:sessionId andSenderKey:senderKey]; - } - return inboundGroupSession; -} - -- (void)storeInboundGroupSessions:(NSArray*)sessions -{ - [bgCryptoStore storeInboundGroupSessions:sessions]; -} - -- (void)performSessionOperationWithGroupSessionWithId:(NSString*)sessionId senderKey:(NSString*)senderKey block:(void (^)(MXOlmInboundGroupSession *inboundGroupSession))block -{ - // If needed, transfer data from cryptoStore to bgCryptoStore first - MXOlmInboundGroupSession *inboundGroupSession = [bgCryptoStore inboundGroupSessionWithId:sessionId andSenderKey:senderKey]; - if (!inboundGroupSession) - { - inboundGroupSession = [cryptoStore inboundGroupSessionWithId:sessionId andSenderKey:senderKey]; - if (inboundGroupSession) - { - MXLogDebug(@"[MXBackgroundCryptoStore] performSessionOperationWithGroupSessionWithId: Transfer data for %@ from cryptoStore to bgCryptoStore", sessionId); - [bgCryptoStore storeInboundGroupSessions:@[inboundGroupSession]]; - } - } - - [bgCryptoStore performSessionOperationWithGroupSessionWithId:sessionId senderKey:senderKey block:block]; -} - -#pragma mark - MXRealmOlmOutboundGroupSession - -- (MXOlmOutboundGroupSession *)outboundGroupSessionWithRoomId:(NSString *)roomId -{ - NSAssert(NO, @"This method should be useless in the context of MXBackgroundCryptoStore"); - return nil; -} - -- (MXOlmOutboundGroupSession *)storeOutboundGroupSession:(OLMOutboundGroupSession *)session withRoomId:(NSString *)roomId -{ - NSAssert(NO, @"This method should be useless in the context of MXBackgroundCryptoStore"); - return nil; -} - -- (NSArray *)outboundGroupSessions -{ - NSAssert(NO, @"This method should be useless in the context of MXBackgroundCryptoStore"); - return @[]; -} - -- (void)removeOutboundGroupSessionWithRoomId:(NSString *)roomId -{ - NSAssert(NO, @"This method should be useless in the context of MXBackgroundCryptoStore"); -} - -- (NSNumber *)messageIndexForSharedDeviceInRoomWithId:(NSString *)roomId sessionId:(NSString *)sessionId userId:(NSString *)userId deviceId:(NSString *)deviceId -{ - NSAssert(NO, @"This method should be useless in the context of MXBackgroundCryptoStore"); - return nil; -} - - -- (MXUsersDevicesMap *)sharedDevicesForOutboundGroupSessionInRoomWithId:(NSString *)roomId sessionId:(NSString *)sessionId -{ - NSAssert(NO, @"This method should be useless in the context of MXBackgroundCryptoStore"); - return [MXUsersDevicesMap new]; -} - - -- (void)storeSharedDevices:(MXUsersDevicesMap *)devices messageIndex:(NSUInteger)messageIndex forOutboundGroupSessionInRoomWithId:(NSString *)roomId sessionId:(NSString *)sessionId -{ - NSAssert(NO, @"This method should be useless in the context of MXBackgroundCryptoStore"); -} - - -#pragma mark - Private Methods - -+ (MXCredentials*)credentialForBgCryptoStoreWithCredentials:(MXCredentials*)credentials -{ - MXCredentials *bgCredentials = [credentials copy]; - bgCredentials.userId = [credentials.userId stringByAppendingString:MXBackgroundCryptoStoreUserIdSuffix]; - - return bgCredentials; -} - - -#pragma mark - No-op - -+ (void)deleteStoreWithCredentials:(MXCredentials*)credentials -{ - NSAssert(NO, @"This method should be useless in the context of MXBackgroundCryptoStore"); -} - -+ (void)deleteAllStores -{ - NSAssert(NO, @"This method should be useless in the context of MXBackgroundCryptoStore"); -} - -+ (void)deleteReadonlyStoreWithCredentials:(MXCredentials*)credentials -{ - NSAssert(NO, @"This method should be useless in the context of MXBackgroundCryptoStore"); -} - -- (NSString *)userId -{ - NSAssert(NO, @"This method should be useless in the context of MXBackgroundCryptoStore"); - return nil; -} - -- (void)storeDeviceId:(NSString*)deviceId -{ - NSAssert(NO, @"This method should be useless in the context of MXBackgroundCryptoStore"); -} - -- (NSString*)deviceId -{ - NSAssert(NO, @"This method should be useless in the context of MXBackgroundCryptoStore"); - return nil; -} - -- (void)storeDeviceSyncToken:(NSString*)deviceSyncToken -{ - NSAssert(NO, @"This method should be useless in the context of MXBackgroundCryptoStore"); -} - -- (void)enumerateSessionsBy:(NSInteger)batchSize block:(void (^)(NSArray *, double))block -{ - NSAssert(NO, @"This method should be useless in the context of MXBackgroundCryptoStore"); -} - -- (void)enumerateInboundGroupSessionsBy:(NSInteger)batchSize block:(void (^)(NSArray *, NSSet *, double))block -{ - NSAssert(NO, @"This method should be useless in the context of MXBackgroundCryptoStore"); -} - -- (NSUInteger)sessionsCount -{ - NSAssert(NO, @"This method should be useless in the context of MXBackgroundCryptoStore"); - return 0; -} - -- (NSArray *)inboundGroupSessions -{ - NSAssert(NO, @"This method should be useless in the context of MXBackgroundCryptoStore"); - return nil; -} - -- (NSString*)deviceSyncToken -{ - NSAssert(NO, @"This method should be useless in the context of MXBackgroundCryptoStore"); - return nil; -} - -- (void)storeDeviceForUser:(NSString*)userId device:(MXDeviceInfo*)device -{ - NSAssert(NO, @"This method should be useless in the context of MXBackgroundCryptoStore"); -} - -- (MXDeviceInfo*)deviceWithDeviceId:(NSString*)deviceId forUser:(NSString*)userId; -{ - NSAssert(NO, @"This method should be useless in the context of MXBackgroundCryptoStore"); - return nil; -} - -- (MXDeviceInfo*)deviceWithIdentityKey:(NSString*)identityKey -{ - NSAssert(NO, @"This method should be useless in the context of MXBackgroundCryptoStore"); - return nil; -} - -- (void)storeDevicesForUser:(NSString*)userId devices:(NSDictionary*)devices -{ - NSAssert(NO, @"This method should be useless in the context of MXBackgroundCryptoStore"); -} - -- (NSDictionary*)devicesForUser:(NSString*)userId -{ - NSAssert(NO, @"This method should be useless in the context of MXBackgroundCryptoStore"); - return nil; -} - -- (NSDictionary*)deviceTrackingStatus -{ - NSAssert(NO, @"This method should be useless in the context of MXBackgroundCryptoStore"); - return nil; -} - -- (void)storeDeviceTrackingStatus:(NSDictionary*)statusMap -{ - NSAssert(NO, @"This method should be useless in the context of MXBackgroundCryptoStore"); -} - -- (void)storeCrossSigningKeys:(MXCrossSigningInfo*)crossSigningInfo -{ - NSAssert(NO, @"This method should be useless in the context of MXBackgroundCryptoStore"); -} - -- (MXCrossSigningInfo*)crossSigningKeysForUser:(NSString*)userId -{ - NSAssert(NO, @"This method should be useless in the context of MXBackgroundCryptoStore"); - return nil; -} - -- (NSArray *)crossSigningKeys -{ - NSAssert(NO, @"This method should be useless in the context of MXBackgroundCryptoStore"); - return nil; -} - -- (void)storeAlgorithmForRoom:(NSString*)roomId algorithm:(NSString*)algorithm -{ - NSAssert(NO, @"This method should be useless in the context of MXBackgroundCryptoStore"); -} - -- (NSString*)algorithmForRoom:(NSString*)roomId -{ - NSAssert(NO, @"This method should be useless in the context of MXBackgroundCryptoStore"); - return nil; -} - --(NSString *)backupVersion -{ - NSAssert(NO, @"This method should be useless in the context of MXBackgroundCryptoStore"); - return nil; -} - -- (void)setBackupVersion:(NSString *)backupVersion{ - NSAssert(NO, @"This method should be useless in the context of MXBackgroundCryptoStore"); -} - -- (void)resetBackupMarkers -{ - NSAssert(NO, @"This method should be useless in the context of MXBackgroundCryptoStore"); -} - -- (void)markBackupDoneForInboundGroupSessions:(NSArray*)sessions -{ - NSAssert(NO, @"This method should be useless in the context of MXBackgroundCryptoStore"); -} - -- (NSArray*)inboundGroupSessionsToBackup:(NSUInteger)limit -{ - NSAssert(NO, @"This method should be useless in the context of MXBackgroundCryptoStore"); - return nil; -} - -- (NSUInteger)inboundGroupSessionsCount:(BOOL)onlyBackedUp -{ - NSAssert(NO, @"This method should be useless in the context of MXBackgroundCryptoStore"); - return 0; -} - -- (MXOutgoingRoomKeyRequest*)outgoingRoomKeyRequestWithRequestBody:(NSDictionary *)requestBody -{ - NSAssert(NO, @"This method should be useless in the context of MXBackgroundCryptoStore"); - return nil; -} - -- (MXOutgoingRoomKeyRequest*)outgoingRoomKeyRequestWithState:(MXRoomKeyRequestState)state -{ - NSAssert(NO, @"This method should be useless in the context of MXBackgroundCryptoStore"); - return nil; -} - -- (NSArray *)allOutgoingRoomKeyRequestsWithState:(MXRoomKeyRequestState)state -{ - NSAssert(NO, @"This method should be useless in the context of MXBackgroundCryptoStore"); - return nil; -} - -- (void)storeOutgoingRoomKeyRequest:(MXOutgoingRoomKeyRequest*)request -{ - NSAssert(NO, @"This method should be useless in the context of MXBackgroundCryptoStore"); -} - -- (void)updateOutgoingRoomKeyRequest:(MXOutgoingRoomKeyRequest*)request -{ - NSAssert(NO, @"This method should be useless in the context of MXBackgroundCryptoStore"); -} - -- (void)deleteOutgoingRoomKeyRequestWithRequestId:(NSString*)requestId -{ - NSAssert(NO, @"This method should be useless in the context of MXBackgroundCryptoStore"); -} - -- (void)storeIncomingRoomKeyRequest:(MXIncomingRoomKeyRequest*)request -{ - NSAssert(NO, @"This method should be useless in the context of MXBackgroundCryptoStore"); -} - -- (void)deleteIncomingRoomKeyRequest:(NSString*)requestId fromUser:(NSString*)userId andDevice:(NSString*)deviceId -{ - NSAssert(NO, @"This method should be useless in the context of MXBackgroundCryptoStore"); -} - -- (MXIncomingRoomKeyRequest*)incomingRoomKeyRequestWithRequestId:(NSString*)requestId fromUser:(NSString*)userId andDevice:(NSString*)deviceId -{ - NSAssert(NO, @"This method should be useless in the context of MXBackgroundCryptoStore"); - return nil; -} - -- (MXUsersDevicesMap *> *)incomingRoomKeyRequests -{ - NSAssert(NO, @"This method should be useless in the context of MXBackgroundCryptoStore"); - return nil; -} - -- (void)storeSecret:(NSString*)secret withSecretId:(NSString*)secretId -{ - NSAssert(NO, @"This method should be useless in the context of MXBackgroundCryptoStore"); -} - -- (BOOL)hasSecretWithSecretId:(NSString *)secretId -{ - NSAssert(NO, @"This method should be useless in the context of MXBackgroundCryptoStore"); - return NO; -} - -- (NSString*)secretWithSecretId:(NSString*)secretId -{ - NSAssert(NO, @"This method should be useless in the context of MXBackgroundCryptoStore"); - return nil; -} - -- (void)deleteSecretWithSecretId:(NSString*)secretId -{ - NSAssert(NO, @"This method should be useless in the context of MXBackgroundCryptoStore"); -} - -- (BOOL)globalBlacklistUnverifiedDevices -{ - NSAssert(NO, @"This method should be useless in the context of MXBackgroundCryptoStore"); - return NO; -} - -- (void)setGlobalBlacklistUnverifiedDevices:(BOOL)globalBlacklistUnverifiedDevices{ - NSAssert(NO, @"This method should be useless in the context of MXBackgroundCryptoStore"); -} - -- (BOOL)blacklistUnverifiedDevicesInRoom:(NSString *)roomId -{ - NSAssert(NO, @"This method should be useless in the context of MXBackgroundCryptoStore"); - return NO; -} - -- (void)storeBlacklistUnverifiedDevicesInRoom:(NSString *)roomId blacklist:(BOOL)blacklist -{ - NSAssert(NO, @"This method should be useless in the context of MXBackgroundCryptoStore"); -} - -- (void)removeInboundGroupSessionWithId:(NSString*)sessionId andSenderKey:(NSString*)senderKey -{ - NSAssert(NO, @"This method should be useless in the context of MXBackgroundCryptoStore"); -} - -- (MXCryptoVersion)cryptoVersion -{ - NSAssert(NO, @"This method should be useless in the context of MXBackgroundCryptoStore"); - return MXCryptoVersionUndefined; -} - -- (void)setCryptoVersion:(MXCryptoVersion)cryptoVersion -{ - NSAssert(NO, @"This method should be useless in the context of MXBackgroundCryptoStore"); -} - -@end diff --git a/MatrixSDK/Background/MXBackgroundSyncService.swift b/MatrixSDK/Background/MXBackgroundSyncService.swift index efb2ad4d1f..65e0cc7c65 100644 --- a/MatrixSDK/Background/MXBackgroundSyncService.swift +++ b/MatrixSDK/Background/MXBackgroundSyncService.swift @@ -485,14 +485,6 @@ public enum MXBackgroundSyncServiceError: Error { } } - if syncResponseStoreManager.syncResponseStore.syncResponseIds.count == 0, let crypto = crypto as? MXLegacyBackgroundCrypto { - // To avoid dead lock between processes, we write to the cryptoStore only from only one process. - // If there is no cached sync responses, it means they have been consumed by MXSession. Now is the - // right time to clean the cryptoStore. - MXLog.debug("[MXBackgroundSyncService] updateBackgroundServiceStoresIfNeeded: Reset MXBackgroundCryptoStore") - crypto.reset() - } - syncPushRuleManagerWithAccountData() } diff --git a/MatrixSDK/Crypto/Algorithms/MXCryptoAlgorithms.h b/MatrixSDK/Crypto/Algorithms/MXCryptoAlgorithms.h deleted file mode 100644 index f1d6796159..0000000000 --- a/MatrixSDK/Crypto/Algorithms/MXCryptoAlgorithms.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - Copyright 2016 OpenMarket 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. - */ - -#import - -#import "MXCryptoConstants.h" -#import "MXEncrypting.h" -#import "MXDecrypting.h" - - -@interface MXCryptoAlgorithms : NSObject - -/** - The shared 'MXCryptoAlgorithms' instance. - */ -+ (instancetype)sharedAlgorithms; - -/** - Register encryption class for a particular algorithm. - - @param encryptorClass a class implementing 'MXEncrypting'. - @param algorithm the algorithm tag to register for. - */ -- (void)registerEncryptorClass:(Class)encryptorClass forAlgorithm:(NSString*)algorithm; - -/** - Register decryption class for a particular algorithm. - - @param decryptorClass a class implementing 'MXDecrypting'. - @param algorithm the algorithm tag to register for. - */ -- (void)registerDecryptorClass:(Class)decryptorClass forAlgorithm:(NSString *)algorithm; - -/** - Get the class implementing encryption for the provided algorithm. - - @param algorithm the algorithm tag. - @return A class implementing 'MXEncrypting'. - */ -- (Class)encryptorClassForAlgorithm:(NSString*)algorithm; - -/** - Get the class implementing decryption for the provided algorithm. - - @param algorithm the algorithm tag. - @return A class implementing 'MXDecrypting'. - */ -- (Class)decryptorClassForAlgorithm:(NSString*)algorithm; - -/** - The list of registered algorithms. - */ -- (NSArray*)supportedAlgorithms; - -@end diff --git a/MatrixSDK/Crypto/Algorithms/MXCryptoAlgorithms.m b/MatrixSDK/Crypto/Algorithms/MXCryptoAlgorithms.m deleted file mode 100644 index b2cef3299b..0000000000 --- a/MatrixSDK/Crypto/Algorithms/MXCryptoAlgorithms.m +++ /dev/null @@ -1,77 +0,0 @@ -/* - Copyright 2016 OpenMarket 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. - */ - -#import "MXCryptoAlgorithms.h" - -@interface MXCryptoAlgorithms () -{ - NSMutableDictionary> *encryptors; - NSMutableDictionary> *decryptors; -} - -@end - -static MXCryptoAlgorithms *sharedOnceInstance = nil; - -@implementation MXCryptoAlgorithms - -+ (instancetype)sharedAlgorithms -{ - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - sharedOnceInstance = [[self alloc] init]; - }); - return sharedOnceInstance; -} - -- (instancetype)init -{ - self = [super init]; - if (self) - { - encryptors = [NSMutableDictionary dictionary]; - decryptors = [NSMutableDictionary dictionary]; - } - return self; -} - --(void)registerEncryptorClass:(Class)encryptorClass forAlgorithm:(NSString *)algorithm -{ - encryptors[algorithm] = encryptorClass; -} - -- (void)registerDecryptorClass:(Class)decryptorClass forAlgorithm:(NSString *)algorithm -{ - decryptors[algorithm] = decryptorClass; -} - -- (Class)encryptorClassForAlgorithm:(NSString *)algorithm -{ - return encryptors[algorithm]; -} - -- (Class)decryptorClassForAlgorithm:(NSString *)algorithm -{ - return decryptors[algorithm]; -} - -- (NSArray *)supportedAlgorithms -{ - return encryptors.allKeys; -} - -@end - diff --git a/MatrixSDK/Crypto/Algorithms/MXDecrypting.h b/MatrixSDK/Crypto/Algorithms/MXDecrypting.h deleted file mode 100644 index 84b7f6cbbd..0000000000 --- a/MatrixSDK/Crypto/Algorithms/MXDecrypting.h +++ /dev/null @@ -1,100 +0,0 @@ -/* - Copyright 2016 OpenMarket Ltd - Copyright 2020 The Matrix.org Foundation C.I.C - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -#import - -#import "MXHTTPOperation.h" -#import "MXEvent.h" -#import "MXDecryptionResult.h" -#import "MXEventDecryptionResult.h" -#import "MXIncomingRoomKeyRequest.h" - -@class MXLegacyCrypto, MXOlmInboundGroupSession, MXRoomKeyResult; - - -@protocol MXDecrypting - -/** - Constructor. - - @param crypto the related 'MXCrypto'. -*/ -- (instancetype)initWithCrypto:(MXLegacyCrypto*)crypto; - -/** - Check if we have keys to decrypt an event. - - @param event the event to decrypt. - - @return YES if keys are present. - */ -- (BOOL)hasKeysToDecryptEvent:(MXEvent*)event; - -/** - Decrypt a message. - - @param event the raw event. - @param timeline the id of the timeline where the event is decrypted. It is used - to prevent replay attack. Can be nil. - - @return The decryption result. - */ -- (MXEventDecryptionResult *)decryptEvent:(MXEvent*)event inTimeline:(NSString*)timeline; - -/** - Handle a key event. - - @param event the key event. - */ -- (void)onRoomKeyEvent:(MXEvent*)event; - -/** - Handle new room key - - @param key the domain object with key details and safety - */ -- (void)onRoomKey:(MXRoomKeyResult*)key; - -/** - Notification that a room key has been imported. - - @param session the session data to import. - */ -- (void)didImportRoomKey:(MXOlmInboundGroupSession*)session; - -/** - Determine if we have the keys necessary to respond to a room key request. - - @param keyRequest the key request. - @return YES if we have the keys and could (theoretically) share them; else NO. - */ -- (BOOL)hasKeysForKeyRequest:(MXIncomingRoomKeyRequest*)keyRequest; - -/** - Send the response to a room key request. - - @param keyRequest the key request. - - @param success A block object called when the operation succeeds. - @param failure A block object called when the operation fails. - - @return a MXHTTPOperation instance. - */ -- (MXHTTPOperation*)shareKeysWithDevice:(MXIncomingRoomKeyRequest*)keyRequest - success:(void (^)(void))success - failure:(void (^)(NSError *error))failure; -@end diff --git a/MatrixSDK/Crypto/Algorithms/Megolm/MXMegolmDecryption.h b/MatrixSDK/Crypto/Algorithms/Megolm/MXMegolmDecryption.h deleted file mode 100644 index 77da986481..0000000000 --- a/MatrixSDK/Crypto/Algorithms/Megolm/MXMegolmDecryption.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - Copyright 2016 OpenMarket 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. - */ - -#import - -#import "MXSDKOptions.h" - -#ifdef MX_CRYPTO - -#import "MXDecrypting.h" -#import "MXSharedHistoryKeyService.h" - -@interface MXMegolmDecryption : NSObject - -@end - -#endif diff --git a/MatrixSDK/Crypto/Algorithms/Megolm/MXMegolmDecryption.m b/MatrixSDK/Crypto/Algorithms/Megolm/MXMegolmDecryption.m deleted file mode 100644 index be54d00a0e..0000000000 --- a/MatrixSDK/Crypto/Algorithms/Megolm/MXMegolmDecryption.m +++ /dev/null @@ -1,548 +0,0 @@ -/* - Copyright 2016 OpenMarket Ltd - Copyright 2017 Vector Creations Ltd - Copyright 2020 The Matrix.org Foundation C.I.C - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -#import "MXMegolmDecryption.h" - -#ifdef MX_CRYPTO - -#import -#import "MXCryptoAlgorithms.h" -#import "MXCrypto_Private.h" -#import "MXTools.h" -#import "MatrixSDKSwiftHeader.h" -#import "MXSharedHistoryKeyService.h" -#import "MXForwardedRoomKeyEventContent.h" - -@interface MXMegolmDecryption () -{ - // The crypto module - MXLegacyCrypto *crypto; - - // The olm device interface - MXOlmDevice *olmDevice; - - // Events which we couldn't decrypt due to unknown sessions / indexes: map from - // senderKey|sessionId to timelines to list of MatrixEvents - NSMutableDictionary*>*> *pendingEvents; -} - -// Factory to create room key info -@property (nonatomic, strong) MXRoomKeyInfoFactory *roomKeyInfoFactory; - -@end - -@implementation MXMegolmDecryption - -+ (void)load -{ - // Register this class as the decryptor for olm - [[MXCryptoAlgorithms sharedAlgorithms] registerDecryptorClass:MXMegolmDecryption.class forAlgorithm:kMXCryptoMegolmAlgorithm]; -} - -#pragma mark - MXDecrypting -- (instancetype)initWithCrypto:(MXLegacyCrypto *)theCrypto -{ - self = [super init]; - if (self) - { - crypto = theCrypto; - olmDevice = theCrypto.olmDevice; - _roomKeyInfoFactory = [[MXRoomKeyInfoFactory alloc] initWithMyUserId:crypto.mxSession.credentials.userId store:crypto.store]; - pendingEvents = [NSMutableDictionary dictionary]; - } - return self; -} - -- (BOOL)hasKeysToDecryptEvent:(MXEvent *)event -{ - BOOL hasKeys = NO; - - NSString *senderKey, *sessionId; - - MXJSONModelSetString(senderKey, event.wireContent[@"sender_key"]); - MXJSONModelSetString(sessionId, event.wireContent[@"session_id"]); - if (senderKey && sessionId) - { - hasKeys = ([crypto.store inboundGroupSessionWithId:sessionId andSenderKey:senderKey] != nil); - } - - return hasKeys; -} - -- (MXEventDecryptionResult *)decryptEvent:(MXEvent*)event inTimeline:(NSString*)timeline -{ - MXEventDecryptionResult *result; - NSString *senderKey, *ciphertext, *sessionId; - - MXJSONModelSetString(senderKey, event.wireContent[@"sender_key"]); - MXJSONModelSetString(ciphertext, event.wireContent[@"ciphertext"]); - MXJSONModelSetString(sessionId, event.wireContent[@"session_id"]); - - if (!senderKey || !sessionId || !ciphertext) - { - result = [MXEventDecryptionResult new]; - result.error = [NSError errorWithDomain:MXDecryptingErrorDomain - code:MXDecryptingErrorMissingFieldsCode - userInfo:@{ - NSLocalizedDescriptionKey: MXDecryptingErrorMissingFieldsReason - }]; - return result; - } - - NSError *olmError; - MXDecryptionResult *olmResult = [olmDevice decryptGroupMessage:ciphertext - isEditEvent:event.isEditEvent - roomId:event.roomId - inTimeline:timeline - sessionId:sessionId - senderKey:senderKey - error:&olmError]; - - result = [MXEventDecryptionResult new]; - if (olmResult) - { - result.clearEvent = olmResult.payload; - result.senderCurve25519Key = olmResult.senderKey; - result.claimedEd25519Key = olmResult.keysClaimed[@"ed25519"]; - result.forwardingCurve25519KeyChain = olmResult.forwardingCurve25519KeyChain; - - MXEventDecryptionDecorationColor decryptionColor = [self decryptionColorForEvent:event decryptionResult:olmResult]; - result.decoration = [[MXEventDecryptionDecoration alloc] initWithColor:decryptionColor message:nil]; - } - else - { - if ([olmError.domain isEqualToString:OLMErrorDomain]) - { - // Manage OLMKit error - if ([olmError.localizedDescription isEqualToString:@"UNKNOWN_MESSAGE_INDEX"]) - { - // Do nothing more on the calling thread - dispatch_async(crypto.cryptoQueue, ^{ - [self addEventToPendingList:event inTimeline:timeline]; - }); - } - - // Package olm error into MXDecryptingErrorDomain - olmError = [NSError errorWithDomain:MXDecryptingErrorDomain - code:MXDecryptingErrorOlmCode - userInfo:@{ - NSLocalizedDescriptionKey: [NSString stringWithFormat:MXDecryptingErrorOlm, olmError.localizedDescription], - NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:MXDecryptingErrorOlmReason, ciphertext, olmError] - }]; - } - else if ([olmError.domain isEqualToString:MXDecryptingErrorDomain] && olmError.code == MXDecryptingErrorUnknownInboundSessionIdCode) - { - // Do nothing more on the calling thread - dispatch_async(crypto.cryptoQueue, ^{ - [self addEventToPendingList:event inTimeline:timeline]; - }); - } - - result.error = olmError; - } - - [crypto.mxSession.eventStreamService dispatchLiveEventDecryptionAttemptedWithEvent:event result:result]; - return result; -} - -- (MXEventDecryptionDecorationColor)decryptionColorForEvent:(MXEvent *)event - decryptionResult:(MXDecryptionResult *)decryptionResult -{ - if (event.sender && [crypto trustLevelForUser:event.sender].isVerified) - { - NSString *algorithm = event.wireContent[@"algorithm"]; - MXDeviceInfo *deviceInfo = [crypto.deviceList deviceWithIdentityKey:decryptionResult.senderKey andAlgorithm:algorithm]; - if (!deviceInfo.trustLevel.isVerified) - { - return MXEventDecryptionDecorationColorRed; - } - } - - return decryptionResult.isUntrusted ? MXEventDecryptionDecorationColorGrey : MXEventDecryptionDecorationColorNone; -} - -/** - Add an event to the list of those we couldn't decrypt the first time we - saw them. - - @param event the event to try to decrypt later. - */ -- (void)addEventToPendingList:(MXEvent*)event inTimeline:(NSString*)timelineId -{ - NSDictionary *content = event.wireContent; - NSString *k = [NSString stringWithFormat:@"%@|%@", content[@"sender_key"], content[@"session_id"]]; - - if (!timelineId) - { - timelineId = @""; - } - - if (!pendingEvents[k]) - { - pendingEvents[k] = [NSMutableDictionary dictionary]; - } - - if (!pendingEvents[k][timelineId]) - { - pendingEvents[k][timelineId] = [NSMutableDictionary dictionary]; - } - - if (!pendingEvents[k][timelineId][event.eventId]) - { - MXLogDebug(@"[MXMegolmDecryption] addEventToPendingList: %@ in %@ for %@", event.eventId, event.roomId, k); - pendingEvents[k][timelineId][event.eventId] = event; - - [self requestKeysForEvent:event]; - } -} - -- (void)onRoomKeyEvent:(MXEvent *)event -{ - MXRoomKeyResult *key = [self.roomKeyInfoFactory roomKeyFor:event]; - if (!key) - { - MXLogError(@"[MXMegolmDecryption] onRoomKeyEvent: Cannot create megolm key from event"); - return; - } - - switch (key.type) { - case MXRoomKeyTypeSafe: - MXLogDebug(@"[MXMegolmDecryption] onRoomKeyEvent: Adding key for megolm session %@|%@ from %@ event", key.info.senderKey, key.info.sessionId, event.type); - [self onRoomKey:key]; - break; - case MXRoomKeyTypeUnsafe: - MXLogWarning(@"[MXMegolmDecryption] onRoomKeyEvent: Ignoring unsafe key"); - break; - case MXRoomKeyTypeUnrequested: - [crypto handleUnrequestedRoomKeyInfo:key.info senderId:event.sender senderKey:event.senderKey]; - break; - default: - MXLogFailureDetails(@"[MXMegolmDecryption] onRoomKeyEvent: Unknown key type", @{ - @"key_type": @(key.type) - }); - break; - } -} - -- (void)onRoomKey:(MXRoomKeyResult *)key -{ - MXRoomKeyInfo *keyInfo = key.info; - [olmDevice addInboundGroupSession:keyInfo.sessionId - sessionKey:keyInfo.sessionKey - roomId:keyInfo.roomId - senderKey:keyInfo.senderKey - forwardingCurve25519KeyChain:keyInfo.forwardingKeyChain - keysClaimed:keyInfo.keysClaimed - exportFormat:keyInfo.exportFormat - sharedHistory:keyInfo.sharedHistory - untrusted:key.type != MXRoomKeyTypeSafe]; - - [crypto.backup maybeSendKeyBackup]; - - MXWeakify(self); - [self retryDecryption:keyInfo.senderKey sessionId:keyInfo.sessionId complete:^(BOOL allDecrypted) { - MXStrongifyAndReturnIfNil(self); - - if (allDecrypted) - { - // cancel any outstanding room key requests for this session - [self->crypto cancelRoomKeyRequest:@{ - @"algorithm": keyInfo.algorithm, - @"room_id": keyInfo.roomId, - @"session_id": keyInfo.sessionId, - @"sender_key": keyInfo.senderKey - }]; - } - }]; -} - -- (void)didImportRoomKey:(MXOlmInboundGroupSession *)session -{ - // Have another go at decrypting events sent with this session - MXWeakify(self); - [self retryDecryption:session.senderKey sessionId:session.session.sessionIdentifier complete:^(BOOL allDecrypted) { - MXStrongifyAndReturnIfNil(self); - - if (allDecrypted) - { - // cancel any outstanding room key requests for this session - [self->crypto cancelRoomKeyRequest:@{ - @"algorithm": kMXCryptoMegolmAlgorithm, - @"room_id": session.roomId, - @"session_id": session.session.sessionIdentifier, - @"sender_key": session.senderKey - }]; - } - }]; -} - -- (BOOL)hasKeysForKeyRequest:(MXIncomingRoomKeyRequest*)keyRequest -{ - NSDictionary *body = keyRequest.requestBody; - - NSString *roomId, *senderKey, *sessionId; - MXJSONModelSetString(roomId, body[@"room_id"]); - MXJSONModelSetString(senderKey, body[@"sender_key"]); - MXJSONModelSetString(sessionId, body[@"session_id"]); - - if (roomId && senderKey && sessionId) - { - return [olmDevice hasInboundSessionKeys:roomId senderKey:senderKey sessionId:sessionId]; - } - - return NO; -} - -- (MXHTTPOperation*)shareKeysWithDevice:(MXIncomingRoomKeyRequest*)keyRequest - success:(void (^)(void))success - failure:(void (^)(NSError *error))failure -{ - NSString *userId = keyRequest.userId; - NSString *deviceId = keyRequest.deviceId; - MXDeviceInfo *deviceInfo = [crypto.deviceList storedDevice:userId deviceId:deviceId]; - NSDictionary *body = keyRequest.requestBody; - NSString *roomId, *senderKey, *sessionId; - MXJSONModelSetString(roomId, body[@"room_id"]); - MXJSONModelSetString(senderKey, body[@"sender_key"]); - MXJSONModelSetString(sessionId, body[@"session_id"]); - - return [self shareKeysWitUserId:userId - devices:@[deviceInfo] - forceEnsureOlmSessions:NO - roomId:roomId - sessionId:sessionId - senderKey:senderKey - success:success - failure:failure]; -} - -#pragma mark - Private methods - -- (MXHTTPOperation *)shareKeysWitUserId:(NSString *)userId - devices:(NSArray *)devices - forceEnsureOlmSessions:(BOOL)forceEnsureOlmSessions - roomId:(NSString *)roomId - sessionId:(NSString *)sessionId - senderKey:(NSString *)senderKey - success:(void (^)(void))success - failure:(void (^)(NSError *error))failure -{ - MXHTTPOperation *operation; - MXWeakify(self); - operation = [crypto ensureOlmSessionsForDevices:@{ - userId: devices - } - force:forceEnsureOlmSessions - success:^(MXUsersDevicesMap *results) - { - MXStrongifyAndReturnIfNil(self); - NSDictionary *payload = [self->crypto buildMegolmKeyForwardingMessage:roomId - senderKey:senderKey - sessionId:sessionId - chainIndex:nil]; - - MXUsersDevicesMap *contentMap = [[MXUsersDevicesMap alloc] init]; - for (MXDeviceInfo *deviceInfo in devices) - { - MXOlmSessionResult *olmSessionResult = [results objectForDevice:deviceInfo.deviceId forUser:userId]; - if (olmSessionResult.sessionId) - { - NSDictionary *message = [self->crypto encryptMessage:payload forDevices:@[deviceInfo]]; - [contentMap setObject:message forUser:userId andDevice:deviceInfo.deviceId]; - } - else - { - MXLogDebug(@"[MXMegolmDecryption] No session with device %@, cannot share keys", deviceInfo.deviceId); - } - } - - if (contentMap.count == 0) - { - MXLogDebug(@"[MXMegolmDecryption] No devices available for user %@, cannot share keys", userId); - if (success) - { - success(); - } - return; - } - - MXLogDebug(@"[MXMegolmDecryption] shareKeysWithDevices: sharing keys for session %@|%@ with devices of user %@", senderKey, sessionId, userId); - - MXToDevicePayload *toDevicePayload = [[MXToDevicePayload alloc] initWithEventType:kMXEventTypeStringRoomEncrypted - contentMap:contentMap]; - MXHTTPOperation *operation2 = [self->crypto.matrixRestClient sendToDevice:toDevicePayload - success:success - failure:failure]; - [operation mutateTo:operation2]; - - } failure:failure]; - - return operation; -} - -#pragma mark - Private methods - -/** - Have another go at decrypting events after we receive a key. - - @param senderKey the sender key. - @param sessionId the session id. - @param complete allDecrypted. - */ -- (void)retryDecryption:(NSString*)senderKey sessionId:(NSString*)sessionId complete:(void (^)(BOOL allDecrypted))complete; -{ - __block BOOL allDecrypted = YES; - dispatch_group_t group = dispatch_group_create(); - - NSString *k = [NSString stringWithFormat:@"%@|%@", senderKey, sessionId]; - NSDictionary*> *pending = pendingEvents[k]; - if (pending) - { - // Have another go at decrypting events sent with this session. - [pendingEvents removeObjectForKey:k]; - - for (NSString *timelineId in pending) - { - for (MXEvent *event in pending[timelineId].allValues) - { - if (event.clearEvent) - { - // This can happen when the event is in several timelines - MXLogDebug(@"[MXMegolmDecryption] retryDecryption: %@ already decrypted", event.eventId); - } - else - { - // Decrypt on the current thread (Must be MXCrypto.cryptoQueue) - MXEventDecryptionResult *result = [self decryptEvent:event inTimeline:(timelineId.length ? timelineId : nil)]; - - // And set the result on the main thread to be compatible with other modules - dispatch_group_enter(group); - dispatch_async(dispatch_get_main_queue(), ^{ - if (result.error) - { - MXLogDebug(@"[MXMegolmDecryption] retryDecryption: Still can't decrypt %@. Error: %@", event.eventId, result.error); - allDecrypted = NO; - } - - if (event.clearEvent) - { - // This can happen when the event is in several timelines - MXLogDebug(@"[MXMegolmDecryption] retryDecryption: %@ already decrypted on main thread", event.eventId); - } - else - { - [event setClearData:result]; - } - - dispatch_group_leave(group); - }); - - } - } - } - } - - dispatch_group_notify(group, crypto.cryptoQueue, ^{ - complete(allDecrypted); - }); -} - -- (void)requestKeysForEvent:(MXEvent*)event -{ - NSString *sender = event.sender; - NSDictionary *wireContent = event.wireContent; - - NSString *myUserId = crypto.matrixRestClient.credentials.userId; - - // send the request to all of our own devices, and the - // original sending device if it wasn't us. - NSMutableArray *> *recipients = [NSMutableArray array]; - [recipients addObject:@{ - @"userId": myUserId, - @"deviceId": @"*" - }]; - - if (![sender isEqualToString:myUserId]) - { - NSString *deviceId; - MXJSONModelSetString(deviceId, wireContent[@"device_id"]); - - if (sender && deviceId) - { - [recipients addObject:@{ - @"userId": sender, - @"deviceId": deviceId - }]; - } - else - { - MXLogDebug(@"[MXMegolmDecryption] requestKeysForEvent: ERROR: missing fields for recipients in event %@", event); - } - } - - NSString *algorithm, *senderKey, *sessionId; - MXJSONModelSetString(algorithm, wireContent[@"algorithm"]); - MXJSONModelSetString(senderKey, wireContent[@"sender_key"]); - MXJSONModelSetString(sessionId, wireContent[@"session_id"]); - - if (algorithm && senderKey && sessionId) - { - [crypto requestRoomKey:@{ - @"room_id": event.roomId, - @"algorithm": algorithm, - @"sender_key": senderKey, - @"session_id": sessionId - } - recipients:recipients]; - } - else - { - MXLogDebug(@"[MXMegolmDecryption] requestKeysForEvent: ERROR: missing fields in event %@", event); - } -} - -#pragma mark - MXSharedHistoryKeyStore - -- (BOOL)hasSharedHistoryForRoomId:(NSString *)roomId - sessionId:(NSString *)sessionId - senderKey:(NSString *)senderKey -{ - MXOlmInboundGroupSession *session = [crypto.store inboundGroupSessionWithId:sessionId - andSenderKey:senderKey]; - return session.sharedHistory && [session.roomId isEqualToString:roomId]; -} - -- (void)shareKeysForRequest:(MXSharedHistoryKeyRequest *)request - success:(void (^)(void))success - failure:(void (^)(NSError *))failure -{ - [self shareKeysWitUserId:request.userId - devices:request.devices - forceEnsureOlmSessions:YES - roomId:request.roomId - sessionId:request.sessionId - senderKey:request.senderKey - success:success - failure:failure]; -} - -@end - -#endif diff --git a/MatrixSDK/Crypto/Algorithms/Megolm/MXMegolmEncryption.h b/MatrixSDK/Crypto/Algorithms/Megolm/MXMegolmEncryption.h deleted file mode 100644 index 08bd0f4f18..0000000000 --- a/MatrixSDK/Crypto/Algorithms/Megolm/MXMegolmEncryption.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - Copyright 2016 OpenMarket 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. - */ - -#import - -#import "MXEncrypting.h" - -@interface MXMegolmEncryption : NSObject - -@end diff --git a/MatrixSDK/Crypto/Algorithms/Megolm/MXMegolmEncryption.m b/MatrixSDK/Crypto/Algorithms/Megolm/MXMegolmEncryption.m deleted file mode 100644 index 103994d5c9..0000000000 --- a/MatrixSDK/Crypto/Algorithms/Megolm/MXMegolmEncryption.m +++ /dev/null @@ -1,611 +0,0 @@ -/* - Copyright 2016 OpenMarket Ltd - Copyright 2017 Vector Creations Ltd - Copyright 2018 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. - */ - -#import "MXSDKOptions.h" - -#ifdef MX_CRYPTO - -#import "MXMegolmEncryption.h" - -#import "MXCryptoAlgorithms.h" -#import "MXCrypto_Private.h" -#import "MXQueuedEncryption.h" -#import "MXTools.h" -#import "MXOutboundSessionInfo.h" -#import -#import "MXSharedHistoryKeyService.h" -#import "MatrixSDKSwiftHeader.h" - - -@interface MXMegolmEncryption () -{ - MXLegacyCrypto *crypto; - - // The id of the room we will be sending to. - NSString *roomId; - - NSString *deviceId; - - NSMutableArray *pendingEncryptions; - - // Session rotation periods - NSUInteger sessionRotationPeriodMsgs; - NSUInteger sessionRotationPeriodMs; -} - -@end - - -@implementation MXMegolmEncryption - -+ (void)load -{ - // Register this class as the encryptor for olm - [[MXCryptoAlgorithms sharedAlgorithms] registerEncryptorClass:MXMegolmEncryption.class forAlgorithm:kMXCryptoMegolmAlgorithm]; -} - - -#pragma mark - MXEncrypting -- (instancetype)initWithCrypto:(MXLegacyCrypto *)theCrypto andRoom:(NSString *)theRoomId -{ - self = [super init]; - if (self) - { - crypto = theCrypto; - roomId = theRoomId; - deviceId = crypto.store.deviceId; - - pendingEncryptions = [NSMutableArray array]; - - // Default rotation periods - // TODO: Make it configurable via parameters - sessionRotationPeriodMsgs = 100; - sessionRotationPeriodMs = 7 * 24 * 3600 * 1000; - } - return self; -} - -- (MXHTTPOperation*)encryptEventContent:(NSDictionary*)eventContent eventType:(MXEventTypeString)eventType - forUsers:(NSArray*)users - success:(void (^)(NSDictionary *encryptedContent))success - failure:(void (^)(NSError *error))failure -{ - // Queue the encryption request - // It will be processed when everything is set up - MXQueuedEncryption *queuedEncryption = [[MXQueuedEncryption alloc] init]; - queuedEncryption.eventContent = eventContent; - queuedEncryption.eventType = eventType; - queuedEncryption.success = success; - queuedEncryption.failure = failure; - [pendingEncryptions addObject:queuedEncryption]; - - return [self ensureSessionForUsers:users success:^(NSObject *sessionInfo) { - - MXOutboundSessionInfo *session = (MXOutboundSessionInfo*)sessionInfo; - [self processPendingEncryptionsInSession:session withError:nil]; - - } failure:^(NSError *error) { - [self processPendingEncryptionsInSession:nil withError:error]; - }]; -} - -- (MXHTTPOperation*)ensureSessionForUsers:(NSArray*)users - success:(void (^)(NSObject *sessionInfo))success - failure:(void (^)(NSError *error))failure -{ - NSDate *startDate = [NSDate date]; - - MXHTTPOperation *operation; - operation = [self getDevicesInRoom:users success:^(MXUsersDevicesMap *devicesInRoom) { - - MXHTTPOperation *operation2 = [self ensureOutboundSession:devicesInRoom success:^(MXOutboundSessionInfo *session) { - - MXLogDebug(@"[MXMegolmEncryption] ensureSessionForUsers took %.0fms", [[NSDate date] timeIntervalSinceDate:startDate] * 1000); - - if (success) - { - success(session); - } - - } failure:failure]; - - [operation mutateTo:operation2]; - - } failure:failure]; - - return operation; -} - - -#pragma mark - Private methods - -- (MXOutboundSessionInfo *)outboundSession -{ - // restore last saved outbound session for this room - MXOlmOutboundGroupSession *restoredOutboundGroupSession = [crypto.olmDevice outboundGroupSessionForRoomWithRoomId:roomId]; - - MXOutboundSessionInfo *outboundSession; - if (restoredOutboundGroupSession) - { - outboundSession = [[MXOutboundSessionInfo alloc] initWithSession:restoredOutboundGroupSession]; - outboundSession.sharedWithDevices = [crypto.store sharedDevicesForOutboundGroupSessionInRoomWithId:roomId sessionId:outboundSession.sessionId]; - } - - return outboundSession; -} - -- (BOOL)isSessionSharingHistory:(MXOutboundSessionInfo *)session -{ - // We only store the `sharedHistory` flag on inbound sessions. To see if the current outbound session shares history - // see a corresponding inbound session with the same identifier. - MXOlmInboundGroupSession *matchingInboundSession = [crypto.store inboundGroupSessionWithId:session.sessionId - andSenderKey:crypto.olmDevice.deviceCurve25519Key]; - return matchingInboundSession.sharedHistory; -} - -/* - Get the list of devices which can encrypt data to. - - @param users the users whose devices must be checked. - - @param success A block object called when the operation succeeds. - @param failure A block object called when the operation fails. -*/ -- (MXHTTPOperation *)getDevicesInRoom:(NSArray*)users - success:(void (^)(MXUsersDevicesMap *devicesInRoom))success - failure:(void (^)(NSError *))failure -{ - // We are happy to use a cached version here: we assume that if we already - // have a list of the user's devices, then we already share an e2e room - // with them, which means that they will have announced any new devices via - // an m.new_device. - MXWeakify(self); - return [crypto.deviceList downloadKeys:users forceDownload:NO success:^(MXUsersDevicesMap *devices, NSDictionary *crossSigningKeysMap) { - MXStrongifyAndReturnIfNil(self); - - BOOL encryptToVerifiedDevicesOnly = self->crypto.globalBlacklistUnverifiedDevices - || [self->crypto isBlacklistUnverifiedDevicesInRoom:self->roomId]; - - MXUsersDevicesMap *devicesInRoom = [[MXUsersDevicesMap alloc] init]; - MXUsersDevicesMap *unknownDevices = [[MXUsersDevicesMap alloc] init]; - - for (NSString *userId in devices.userIds) - { - for (NSString *deviceID in [devices deviceIdsForUser:userId]) - { - MXDeviceInfo *deviceInfo = [devices objectForDevice:deviceID forUser:userId]; - - if (!deviceInfo.trustLevel.isVerified - && self->crypto.warnOnUnknowDevices && deviceInfo.trustLevel.localVerificationStatus == MXDeviceUnknown) - { - // The device is not yet known by the user - [unknownDevices setObject:deviceInfo forUser:userId andDevice:deviceID]; - continue; - } - - if (deviceInfo.trustLevel.localVerificationStatus == MXDeviceBlocked - || (!deviceInfo.trustLevel.isVerified && encryptToVerifiedDevicesOnly)) - { - // Remove any blocked devices - MXLogDebug(@"[MXMegolmEncryption] getDevicesInRoom: blocked device: %@", deviceInfo); - continue; - } - - if ([deviceInfo.identityKey isEqualToString:self->crypto.olmDevice.deviceCurve25519Key]) - { - // Don't bother sending to ourself - continue; - } - - [devicesInRoom setObject:deviceInfo forUser:userId andDevice:deviceID]; - } - } - - // Check if any of these devices are not yet known to the user. - // if so, warn the user so they can verify or ignore. - if (!unknownDevices.count) - { - success(devicesInRoom); - } - else - { - NSError *error = [NSError errorWithDomain:MXEncryptingErrorDomain - code:MXEncryptingErrorUnknownDeviceCode - userInfo:@{ - NSLocalizedDescriptionKey: MXEncryptingErrorUnknownDeviceReason, - @"MXEncryptingErrorUnknownDeviceDevicesKey": unknownDevices - }]; - - failure(error); - } - - } failure: failure]; - -} - -/** - Ensure that we have an outbound session ready for the devices in the room. - - @param devicesInRoom the devices in the room. - - @param success A block object called when the operation succeeds. - @param failure A block object called when the operation fails. - */ -- (MXHTTPOperation *)ensureOutboundSession:(MXUsersDevicesMap *)devicesInRoom - success:(void (^)(MXOutboundSessionInfo *session))success - failure:(void (^)(NSError *))failure -{ - __block MXOutboundSessionInfo *session = self.outboundSession; - - if (session && [self shouldResetSession:session devicesInRoom:devicesInRoom]) - { - [crypto.olmDevice discardOutboundGroupSessionForRoomWithRoomId:roomId]; - session = nil; - } - - if (!session) - { - session = [self prepareNewSession]; - } - - if (session.shareOperation) - { - // Prep already in progress - return session.shareOperation; - } - - // No share in progress: Share the current setup - - NSMutableDictionary*> *shareMap = [NSMutableDictionary dictionary]; - - for (NSString *userId in devicesInRoom.userIds) - { - for (NSString *deviceID in [devicesInRoom deviceIdsForUser:userId]) - { - MXDeviceInfo *deviceInfo = [devicesInRoom objectForDevice:deviceID forUser:userId]; - - if (![session.sharedWithDevices objectForDevice:deviceID forUser:userId]) - { - if (!shareMap[userId]) - { - shareMap[userId] = [NSMutableArray array]; - } - [shareMap[userId] addObject:deviceInfo]; - } - } - } - - session.shareOperation = [self shareKey:session withDevices:shareMap success:^{ - - session.shareOperation = nil; - success(session); - - } failure:^(NSError *error) { - - session.shareOperation = nil; - failure(error); - }]; - - return session.shareOperation; -} - -- (BOOL)shouldResetSession:(MXOutboundSessionInfo *)session devicesInRoom:(MXUsersDevicesMap *)devicesInRoom -{ - // Need to make a brand new session? - if ([session needsRotation:sessionRotationPeriodMsgs rotationPeriodMs:sessionRotationPeriodMs]) - { - return YES; - } - - // Determine if we have shared with anyone we shouldn't have - else if ([session sharedWithTooManyDevices:devicesInRoom]) - { - return YES; - } - - // Check if room's history visibility has changed - else if ([crypto isRoomSharingHistory:roomId] != [self isSessionSharingHistory:session]) - { - return YES; - } - return NO; -} - -- (MXOutboundSessionInfo*)prepareNewSession -{ - MXOlmOutboundGroupSession *session = [crypto.olmDevice createOutboundGroupSessionForRoomWithRoomId:roomId]; - - BOOL sharedHistory = [crypto isRoomSharingHistory:roomId]; - [crypto.olmDevice addInboundGroupSession:session.sessionId - sessionKey:session.sessionKey - roomId:roomId - senderKey:crypto.olmDevice.deviceCurve25519Key - forwardingCurve25519KeyChain:@[] - keysClaimed:@{ - @"ed25519": crypto.olmDevice.deviceEd25519Key - } - exportFormat:NO - sharedHistory:sharedHistory - untrusted:NO - ]; - - [crypto.backup maybeSendKeyBackup]; - - return [[MXOutboundSessionInfo alloc] initWithSession:session]; -} - -- (MXHTTPOperation*)shareKey:(MXOutboundSessionInfo*)session - withDevices:(NSDictionary*>*)devicesByUser - success:(void (^)(void))success - failure:(void (^)(NSError *))failure - -{ - NSString *sessionKey = session.session.sessionKey; - NSUInteger chainIndex = session.session.messageIndex; - BOOL sharedHistory = MXSDKOptions.sharedInstance.enableRoomSharedHistoryOnInvite && [self isSessionSharingHistory:session]; - - NSDictionary *payload = @{ - @"type": kMXEventTypeStringRoomKey, - @"content": @{ - @"algorithm": kMXCryptoMegolmAlgorithm, - @"room_id": roomId, - @"session_id": session.sessionId, - @"session_key": sessionKey, - @"chain_index": @(chainIndex), - kMXSharedHistoryKeyName: @(sharedHistory) - } - }; - - MXLogDebug(@"[MXMegolmEncryption] shareKey: with %tu users: %@", devicesByUser.count, devicesByUser); - - MXHTTPOperation *operation; - MXWeakify(self); - operation = [crypto ensureOlmSessionsForDevices:devicesByUser force:NO success:^(MXUsersDevicesMap *results) { - MXStrongifyAndReturnIfNil(self); - - MXLogDebug(@"[MXMegolmEncryption] shareKey: ensureOlmSessionsForDevices result (users: %tu - devices: %tu): %@", results.map.count, results.count, results); - - MXUsersDevicesMap *contentMap = [[MXUsersDevicesMap alloc] init]; - BOOL haveTargets = NO; - - for (NSString *userId in devicesByUser.allKeys) - { - NSArray *devicesToShareWith = devicesByUser[userId]; - - for (MXDeviceInfo *deviceInfo in devicesToShareWith) - { - NSString *deviceID = deviceInfo.deviceId; - - MXOlmSessionResult *sessionResult = [results objectForDevice:deviceID forUser:userId]; - if (!sessionResult.sessionId) - { - // no session with this device, probably because there - // were no one-time keys. - // - // we could send them a to_device message anyway, as a - // signal that they have missed out on the key sharing - // message because of the lack of keys, but there's not - // much point in that really; it will mostly serve to clog - // up to_device inboxes. - - MXLogDebug(@"[MXMegolmEncryption] shareKey: Cannot share key with device %@:%@. No one time key", userId, deviceID); - continue; - } - - MXLogDebug(@"[MXMegolmEncryption] shareKey: Sharing keys with device %@:%@", userId, deviceID); - - MXDeviceInfo *deviceInfo = sessionResult.device; - - [contentMap setObject:[self->crypto encryptMessage:payload forDevices:@[deviceInfo]] - forUser:userId andDevice:deviceID]; - - haveTargets = YES; - } - } - - if (haveTargets) - { - //MXLogDebug(@"[MXMegolmEncryption] shareKey. Actually share with %tu users and %tu devices: %@", contentMap.userIds.count, contentMap.count, contentMap); - MXLogDebug(@"[MXMegolmEncryption] shareKey: Actually share with %tu users and %tu devices", contentMap.userIds.count, contentMap.count); - - MXToDevicePayload *payload = [[MXToDevicePayload alloc] initWithEventType:kMXEventTypeStringRoomEncrypted - contentMap:contentMap]; - MXHTTPOperation *operation2 = [self->crypto.matrixRestClient sendToDevice:payload success:^{ - - MXLogDebug(@"[MXMegolmEncryption] shareKey: request succeeded"); - - // Add the devices we have shared with to session.sharedWithDevices. - // - // we deliberately iterate over devicesByUser (ie, the devices we - // attempted to share with) rather than the contentMap (those we did - // share with), because we don't want to try to claim a one-time-key - // for dead devices on every message. - - // store chain index for devices the session has been shared with - MXUsersDevicesMap *sharedWithDevices = [MXUsersDevicesMap new]; - - for (NSString *userId in devicesByUser) - { - NSArray *devicesToShareWith = devicesByUser[userId]; - for (MXDeviceInfo *deviceInfo in devicesToShareWith) - { - [session.sharedWithDevices setObject:@(chainIndex) forUser:userId andDevice:deviceInfo.deviceId]; - [sharedWithDevices setObject:@(chainIndex) forUser:userId andDevice:deviceInfo.deviceId]; - } - } - - if (sharedWithDevices.count) - { - [self->crypto.store storeSharedDevices:sharedWithDevices messageIndex:chainIndex forOutboundGroupSessionInRoomWithId:self->roomId sessionId:session.session.sessionId]; - } - - success(); - - } failure:failure]; - [operation mutateTo:operation2]; - } - else - { - success(); - } - - } failure:^(NSError *error) { - - MXLogDebug(@"[MXMegolmEncryption] shareKey: request failed. Error: %@", error); - if (failure) - { - failure(error); - } - }]; - - return operation; -} - -- (MXHTTPOperation*)reshareKey:(NSString*)sessionId - withUser:(NSString*)userId - andDevice:(NSString*)deviceId - senderKey:(NSString*)senderKey - success:(void (^)(void))success - failure:(void (^)(NSError *error))failure -{ - MXLogDebug(@"[MXMegolmEncryption] reshareKey: %@ to %@:%@", sessionId, userId, deviceId); - - MXDeviceInfo *deviceInfo = [crypto.store deviceWithDeviceId:deviceId forUser:userId]; - if (!deviceInfo) - { - MXLogDebug(@"[MXMegolmEncryption] reshareKey: ERROR: Unknown device"); - NSError *error = [NSError errorWithDomain:MXEncryptingErrorDomain - code:MXEncryptingErrorUnknownDeviceCode - userInfo:nil]; - failure(error); - return nil; - } - - // Get the chain index of the key we previously sent this device - NSNumber *chainIndex = [crypto.store messageIndexForSharedDeviceInRoomWithId:roomId sessionId:sessionId userId:userId deviceId:deviceId]; - if (!chainIndex) - { - MXLogDebug(@"[MXMegolmEncryption] reshareKey: ERROR: Never shared megolm key with this device"); - NSError *error = [NSError errorWithDomain:MXEncryptingErrorDomain - code:MXEncryptingErrorReshareNotAllowedCode - userInfo:nil]; - failure(error); - return nil; - } - - MXHTTPOperation *operation; - MXWeakify(self); - operation = [crypto ensureOlmSessionsForDevices:@{ - userId: @[deviceInfo] - } - force:NO - success:^(MXUsersDevicesMap *results) - { - MXStrongifyAndReturnIfNil(self); - - MXOlmSessionResult *olmSessionResult = [results objectForDevice:deviceId forUser:userId]; - if (!olmSessionResult.sessionId) - { - // no session with this device, probably because there - // were no one-time keys. - // - // ensureOlmSessionsForUsers has already done the logging, - // so just skip it. - if (success) - { - success(); - } - return; - } - - MXDeviceInfo *deviceInfo = olmSessionResult.device; - - MXLogDebug(@"[MXMegolmEncryption] reshareKey: sharing keys for session %@|%@:%@ with device %@:%@", senderKey, sessionId, chainIndex, userId, deviceId); - - NSDictionary *payload = [self->crypto buildMegolmKeyForwardingMessage:self->roomId senderKey:senderKey sessionId:sessionId chainIndex:chainIndex]; - - - MXUsersDevicesMap *contentMap = [[MXUsersDevicesMap alloc] init]; - [contentMap setObject:[self->crypto encryptMessage:payload forDevices:@[deviceInfo]] - forUser:userId andDevice:deviceId]; - - MXToDevicePayload *toDevicePayload = [[MXToDevicePayload alloc] initWithEventType:kMXEventTypeStringRoomEncrypted - contentMap:contentMap]; - MXHTTPOperation *operation2 = [self->crypto.matrixRestClient sendToDevice:toDevicePayload success:success failure:failure]; - [operation mutateTo:operation2]; - - } failure:failure]; - - return operation; -} - -- (void)processPendingEncryptionsInSession:(MXOutboundSessionInfo*)session withError:(NSError*)error -{ - if (session) - { - // Everything is in place, encrypt all pending events - for (MXQueuedEncryption *queuedEncryption in pendingEncryptions) - { - NSDictionary *payloadJson = @{ - @"room_id": roomId, - @"type": queuedEncryption.eventType, - @"content": queuedEncryption.eventContent - }; - - NSData *payloadData = [NSJSONSerialization dataWithJSONObject:payloadJson options:0 error:nil]; - NSString *payloadString = [[NSString alloc] initWithData:payloadData encoding:NSUTF8StringEncoding]; - - NSError *error = nil; - NSString *ciphertext = [session.session encryptMessage:payloadString error:&error]; - - if (error) - { - MXLogDebug(@"[MXMegolmEncryption] processPendingEncryptionsInSession: failed to encrypt text: %@", error); - } - - queuedEncryption.success(@{ - @"algorithm": kMXCryptoMegolmAlgorithm, - @"sender_key": crypto.olmDevice.deviceCurve25519Key, - @"ciphertext": ciphertext, - @"session_id": session.sessionId, - - // Include our device ID so that recipients can send us a - // m.new_device message if they don't have our session key. - @"device_id": deviceId - }); - - session.useCount++; - - // We have to store the session in the DB every time a message is encrypted to save the session useCount - [crypto.olmDevice storeOutboundGroupSession:session.session]; - } - } - else - { - for (MXQueuedEncryption *queuedEncryption in pendingEncryptions) - { - queuedEncryption.failure(error); - } - } - - [pendingEncryptions removeAllObjects]; -} - -@end - -#endif diff --git a/MatrixSDK/Crypto/Algorithms/Olm/MXOlmDecryption.h b/MatrixSDK/Crypto/Algorithms/Olm/MXOlmDecryption.h deleted file mode 100644 index fb362c6742..0000000000 --- a/MatrixSDK/Crypto/Algorithms/Olm/MXOlmDecryption.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - Copyright 2016 OpenMarket 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. - */ - -#import - -#import "MXSDKOptions.h" - -#ifdef MX_CRYPTO - -#import "MXDecrypting.h" - -@interface MXOlmDecryption : NSObject - -@end - -#endif diff --git a/MatrixSDK/Crypto/Algorithms/Olm/MXOlmDecryption.m b/MatrixSDK/Crypto/Algorithms/Olm/MXOlmDecryption.m deleted file mode 100644 index bb93af7d8f..0000000000 --- a/MatrixSDK/Crypto/Algorithms/Olm/MXOlmDecryption.m +++ /dev/null @@ -1,339 +0,0 @@ -/* - Copyright 2016 OpenMarket Ltd - Copyright 2020 The Matrix.org Foundation C.I.C - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -#import "MXOlmDecryption.h" - -#ifdef MX_CRYPTO - -#import "MXCryptoAlgorithms.h" -#import "MXCrypto_Private.h" - -@interface MXOlmDecryption () -{ - // The olm device interface - MXOlmDevice *olmDevice; - - // Our user id - NSString *userId; -} -@end - - -@implementation MXOlmDecryption - -+ (void)load -{ - // Register this class as the decryptor for olm - [[MXCryptoAlgorithms sharedAlgorithms] registerDecryptorClass:MXOlmDecryption.class forAlgorithm:kMXCryptoOlmAlgorithm]; -} - - -#pragma mark - MXDecrypting -- (instancetype)initWithCrypto:(MXLegacyCrypto *)crypto -{ - self = [super init]; - if (self) - { - olmDevice = crypto.olmDevice; - userId = crypto.matrixRestClient.credentials.userId; - } - return self; -} - -- (BOOL)hasKeysToDecryptEvent:(MXEvent *)event -{ - MXLogDebug(@"[MXOlmDecryption] hasKeysToDecryptEvent: ERROR: Not implemented yet"); - return NO; -} - -- (MXEventDecryptionResult *)decryptEvent:(MXEvent *)event inTimeline:(NSString *)timeline -{ - NSString *deviceKey; - NSDictionary *ciphertext; - - MXJSONModelSetString(deviceKey, event.wireContent[@"sender_key"]); - MXJSONModelSetDictionary(ciphertext, event.wireContent[@"ciphertext"]); - - if (!ciphertext) - { - MXLogDebug(@"[MXOlmDecryption] decryptEvent: Error: Missing ciphertext"); - - MXEventDecryptionResult *result = [MXEventDecryptionResult new]; - result.error = [NSError errorWithDomain:MXDecryptingErrorDomain - code:MXDecryptingErrorMissingCiphertextCode - userInfo:@{ - NSLocalizedDescriptionKey: MXDecryptingErrorMissingCiphertextReason - }]; - return result; - } - - if (!ciphertext[olmDevice.deviceCurve25519Key]) - { - MXLogDebug(@"[MXOlmDecryption] decryptEvent: Error: our device %@ is not included in recipients. Event: %@", olmDevice.deviceCurve25519Key, event.JSONDictionary); - - MXEventDecryptionResult *result = [MXEventDecryptionResult new]; - result.error = [NSError errorWithDomain:MXDecryptingErrorDomain - code:MXDecryptingErrorNotIncludedInRecipientsCode - userInfo:@{ - NSLocalizedDescriptionKey: MXDecryptingErrorNotIncludedInRecipientsReason - }]; - return result; - } - - // The message for myUser - NSDictionary *message = ciphertext[olmDevice.deviceCurve25519Key]; - - NSString *payloadString = [self decryptMessage:message andTheirDeviceIdentityKey:deviceKey]; - if (!payloadString) - { - MXLogDebug(@"[MXOlmDecryption] decryptEvent: Failed to decrypt Olm event (id= %@) from %@", event.eventId, deviceKey); - - MXEventDecryptionResult *result = [MXEventDecryptionResult new]; - result.error = [NSError errorWithDomain:MXDecryptingErrorDomain - code:MXDecryptingErrorBadEncryptedMessageCode - userInfo:@{ - NSLocalizedDescriptionKey: MXDecryptingErrorBadEncryptedMessageReason - }]; - return result; - } - - NSDictionary *payload = [NSJSONSerialization JSONObjectWithData:[payloadString dataUsingEncoding:NSUTF8StringEncoding] options:0 error:nil]; - - // Check that we were the intended recipient, to avoid unknown-key attack - // https://github.com/vector-im/vector-web/issues/2483 - if (!payload[@"recipient"]) - { - MXLogDebug(@"[MXOlmDecryption] decryptEvent: Olm event (id=%@) contains no 'recipient' property; cannot prevent unknown-key attack", event.eventId); - - MXEventDecryptionResult *result = [MXEventDecryptionResult new]; - result.error = [NSError errorWithDomain:MXDecryptingErrorDomain - code:MXDecryptingErrorMissingPropertyCode - userInfo:@{ - NSLocalizedDescriptionKey: [NSString stringWithFormat:MXDecryptingErrorMissingPropertyReason, @"recipient"] - }]; - return result; - } - else if (![payload[@"recipient"] isEqualToString:userId]) - { - MXLogDebug(@"[MXOlmDecryption] decryptEvent: Event %@: Intended recipient %@ does not match our id %@", event.eventId, payload[@"recipient"], userId); - - MXEventDecryptionResult *result = [MXEventDecryptionResult new]; - result.error = [NSError errorWithDomain:MXDecryptingErrorDomain - code:MXDecryptingErrorBadRecipientCode - userInfo:@{ - NSLocalizedDescriptionKey: [NSString stringWithFormat:MXDecryptingErrorBadRecipientReason, payload[@"recipient"]] - }]; - return result; - } - - if (!payload[@"recipient_keys"]) - { - MXLogDebug(@"[MXOlmDecryption] decryptEvent: Olm event (id=%@) contains no 'recipient_keys' property; cannot prevent unknown-key attack", event.eventId); - - MXEventDecryptionResult *result = [MXEventDecryptionResult new]; - result.error = [NSError errorWithDomain:MXDecryptingErrorDomain - code:MXDecryptingErrorMissingPropertyCode - userInfo:@{ - NSLocalizedDescriptionKey: [NSString stringWithFormat:MXDecryptingErrorMissingPropertyReason, @"recipient_keys"] - }]; - return result; - } - else if (![payload[@"recipient_keys"][@"ed25519"] isEqualToString:olmDevice.deviceEd25519Key]) - { - MXLogDebug(@"[MXOlmDecryption] decryptEvent: Event %@: Intended recipient ed25519 key %@ does not match ours", event.eventId, payload[@"recipient_keys"][@"ed25519"]); - - MXEventDecryptionResult *result = [MXEventDecryptionResult new]; - result.error = [NSError errorWithDomain:MXDecryptingErrorDomain - code:MXDecryptingErrorBadRecipientKeyCode - userInfo:@{ - NSLocalizedDescriptionKey: MXDecryptingErrorBadRecipientKeyReason - }]; - return result; - } - - // Check that the original sender matches what the homeserver told us, to - // avoid people masquerading as others. - // (this check is also provided via the sender's embedded ed25519 key, - // which is checked elsewhere). - if (!payload[@"sender"]) - { - MXLogDebug(@"[MXOlmDecryption] decryptEvent: Olm event (id=%@) contains no 'sender' property; cannot prevent unknown-key attack", event.eventId); - - MXEventDecryptionResult *result = [MXEventDecryptionResult new]; - result.error = [NSError errorWithDomain:MXDecryptingErrorDomain - code:MXDecryptingErrorMissingPropertyCode - userInfo:@{ - NSLocalizedDescriptionKey: [NSString stringWithFormat:MXDecryptingErrorMissingPropertyReason, @"sender"] - }]; - return result; - } - else if (![payload[@"sender"] isEqualToString:event.sender]) - { - MXLogDebug(@"[MXOlmDecryption] decryptEvent: Event %@: original sender %@ does not match reported sender %@", event.eventId, payload[@"sender"], event.sender); - - MXEventDecryptionResult *result = [MXEventDecryptionResult new]; - result.error = [NSError errorWithDomain:MXDecryptingErrorDomain - code:MXDecryptingErrorForwardedMessageCode - userInfo:@{ - NSLocalizedDescriptionKey: [NSString stringWithFormat:MXDecryptingErrorForwardedMessageReason, payload[@"sender"]] - }]; - return result; - } - - // Olm events intended for a room have a room_id. - if (event.roomId && ![payload[@"room_id"] isEqualToString:event.roomId]) - { - MXLogDebug(@"[MXOlmDecryption] decryptEvent: Event %@: original room %@ does not match reported room %@", event.eventId, payload[@"room_id"], event.roomId); - - MXEventDecryptionResult *result = [MXEventDecryptionResult new]; - result.error = [NSError errorWithDomain:MXDecryptingErrorDomain - code:MXDecryptingErrorBadRoomCode - userInfo:@{ - NSLocalizedDescriptionKey: [NSString stringWithFormat:MXDecryptingErrorBadRoomReason, payload[@"room_id"]] - }]; - return result; - } - - NSDictionary *claimedKeys = payload[@"keys"]; - - MXEventDecryptionResult *result = [[MXEventDecryptionResult alloc] init]; - result.clearEvent = payload; - result.senderCurve25519Key = deviceKey; - result.claimedEd25519Key = claimedKeys[@"ed25519"]; - - return result; -} - -- (void)onRoomKeyEvent:(MXEvent *)event -{ - // No impact for olm -} - -- (void)onRoomKeyInfo:(MXRoomKeyInfo *)keyInfo -{ - // No impact for olm -} - -- (void)onRoomKey:(MXRoomKeyResult *)key -{ - // No impact for olm -} - -- (void)didImportRoomKey:(MXOlmInboundGroupSession *)session -{ - // No impact for olm -} - -- (BOOL)hasKeysForKeyRequest:(MXIncomingRoomKeyRequest*)keyRequest -{ - // No need for olm - return NO; -} - -- (MXHTTPOperation*)shareKeysWithDevice:(MXIncomingRoomKeyRequest*)keyRequest - success:(void (^)(void))success - failure:(void (^)(NSError *error))failure -{ - // No need for olm - return nil; -} - -#pragma mark - Private methods -/** - Attempt to decrypt an Olm message. - - @param theirDeviceIdentityKey the Curve25519 identity key of the sender. - @param message message object, with 'type' and 'body' fields. - - @return payload, if decrypted successfully. - */ -- (NSString*)decryptMessage:(NSDictionary*)message andTheirDeviceIdentityKey:(NSString*)theirDeviceIdentityKey -{ - NSArray *sessionIds = [olmDevice sessionIdsForDevice:theirDeviceIdentityKey]; - - NSString *messageBody = message[kMXMessageBodyKey]; - if (![message[@"type"] isKindOfClass:NSNumber.class]) - { - MXLogFailureDetails(@"[MXOlmDecryption] decryptMessage: Invalid type of message", @{ - @"type": message[@"type"] ?: @"unknown" - }) - return nil; - } - - NSUInteger messageType = [((NSNumber*)message[@"type"]) unsignedIntegerValue]; - - // Try each session in turn - for (NSString *sessionId in sessionIds) - { - NSString *payload = [olmDevice decryptMessage:messageBody - withType:messageType - sessionId:sessionId - theirDeviceIdentityKey:theirDeviceIdentityKey]; - - if (payload) - { - MXLogDebug(@"[MXOlmDecryption] decryptMessage: Decrypted Olm message from sender key %@ with session %@", theirDeviceIdentityKey, sessionId); - return payload; - } - else - { - BOOL foundSession = [olmDevice matchesSession:theirDeviceIdentityKey sessionId:sessionId messageType:messageType ciphertext:messageBody]; - - if (foundSession) - { - // Decryption failed, but it was a prekey message matching this - // session, so it should have worked. - MXLogDebug(@"[MXOlmDecryption] Error decrypting prekey message with existing session id %@", sessionId); - return nil; - } - } - } - - if (messageType != 0) - { - // not a prekey message, so it should have matched an existing session, but it - // didn't work. - if (sessionIds.count == 0) - { - MXLogDebug(@"[MXOlmDecryption] decryptMessage: No existing sessions"); - } - else - { - MXLogDebug(@"[MXOlmDecryption] decryptMessage: Error decrypting non-prekey message with existing sessions"); - } - - return nil; - } - - // prekey message which doesn't match any existing sessions: make a new - // session. - NSString *payload; - NSString *sessionId = [olmDevice createInboundSession:theirDeviceIdentityKey messageType:messageType cipherText:messageBody payload:&payload]; - if (!sessionId) - { - MXLogDebug(@"[MXOlmDecryption] decryptMessage: Cannot create new inbound Olm session. Error decrypting non-prekey message with existing sessions"); - return nil; - } - - MXLogDebug(@"[MXOlmDecryption] Created new inbound Olm session id %@ with %@", sessionId, theirDeviceIdentityKey); - - return payload; -}; - -@end - -#endif diff --git a/MatrixSDK/Crypto/Algorithms/Olm/MXOlmEncryption.h b/MatrixSDK/Crypto/Algorithms/Olm/MXOlmEncryption.h deleted file mode 100644 index 80a428cee3..0000000000 --- a/MatrixSDK/Crypto/Algorithms/Olm/MXOlmEncryption.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - Copyright 2016 OpenMarket 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. - */ - -#import "MXSDKOptions.h" - -#ifdef MX_CRYPTO - -#import "MXEncrypting.h" - -@interface MXOlmEncryption : NSObject - -@end - -#endif diff --git a/MatrixSDK/Crypto/Algorithms/Olm/MXOlmEncryption.m b/MatrixSDK/Crypto/Algorithms/Olm/MXOlmEncryption.m deleted file mode 100644 index 57f6eb2fe7..0000000000 --- a/MatrixSDK/Crypto/Algorithms/Olm/MXOlmEncryption.m +++ /dev/null @@ -1,138 +0,0 @@ -/* - Copyright 2016 OpenMarket Ltd - Copyright 2018 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. - */ - -#import "MXOlmEncryption.h" - -#import "MXCryptoAlgorithms.h" -#import "MXCrypto_Private.h" - -#ifdef MX_CRYPTO - -#import "MXTools.h" - -@interface MXOlmEncryption () -{ - MXLegacyCrypto *crypto; - - // The id of the room we will be sending to. - NSString *roomId; -} - -@end - - -@implementation MXOlmEncryption - -+ (void)load -{ - // Register this class as the encryptor for olm - [[MXCryptoAlgorithms sharedAlgorithms] registerEncryptorClass:MXOlmEncryption.class forAlgorithm:kMXCryptoOlmAlgorithm]; -} - - -#pragma mark - MXEncrypting -- (instancetype)initWithCrypto:(MXLegacyCrypto *)theCrypto andRoom:(NSString *)theRoomId -{ - self = [super init]; - if (self) - { - crypto = theCrypto; - roomId = theRoomId; - } - return self; -} - -- (MXHTTPOperation*)encryptEventContent:(NSDictionary*)eventContent eventType:(MXEventTypeString)eventType - forUsers:(NSArray*)users - success:(void (^)(NSDictionary *encryptedContent))success - failure:(void (^)(NSError *error))failure -{ - MXWeakify(self); - return [self ensureSessionForUsers:users success:^(NSObject *sessionInfo) { - MXStrongifyAndReturnIfNil(self); - - NSMutableArray *participantDevices = [NSMutableArray array]; - - for (NSString *userId in users) - { - NSArray *devices = [self->crypto.deviceList storedDevicesForUser:userId]; - for (MXDeviceInfo *device in devices) - { - if ([device.identityKey isEqualToString:self->crypto.olmDevice.deviceCurve25519Key]) - { - // Don't bother setting up session to ourself - continue; - } - - if (device.trustLevel.localVerificationStatus == MXDeviceBlocked) - { - // Don't bother setting up sessions with blocked users - continue; - } - - [participantDevices addObject:device]; - } - } - - NSDictionary *encryptedMessage = [self->crypto encryptMessage:@{ - @"room_id": self->roomId, - @"type": eventType, - @"content": eventContent - } - forDevices:participantDevices]; - success(encryptedMessage); - - } failure:failure]; -} - -- (MXHTTPOperation*)ensureSessionForUsers:(NSArray*)users - success:(void (^)(NSObject *sessionInfo))success - failure:(void (^)(NSError *error))failure -{ - // TODO: Avoid to do this request for every message. Instead, manage a queue of messages waiting for encryption - // XXX: This class is not used so fix it later - MXWeakify(self); - MXHTTPOperation *operation; - operation = [crypto.deviceList downloadKeys:users forceDownload:NO success:^(MXUsersDevicesMap *usersDevicesInfoMap, NSDictionary *crossSigningKeysMap) { - - MXStrongifyAndReturnIfNil(self); - - MXHTTPOperation *operation2 = [self->crypto ensureOlmSessionsForUsers:users success:^(MXUsersDevicesMap *results) { - success(nil); - } failure:failure]; - - [operation mutateTo:operation2]; - - } failure:failure]; - - return operation; -} - -- (MXHTTPOperation*)reshareKey:(NSString*)sessionId - withUser:(NSString*)userId - andDevice:(NSString*)deviceId - senderKey:(NSString*)senderKey - success:(void (^)(void))success - failure:(void (^)(NSError *error))failure -{ - // No need for olm - return nil; -} - -@end - -#endif diff --git a/MatrixSDK/Crypto/CrossSigning/MXCrossSigning.m b/MatrixSDK/Crypto/CrossSigning/MXCrossSigning.m index 3d657445c1..5005edd720 100644 --- a/MatrixSDK/Crypto/CrossSigning/MXCrossSigning.m +++ b/MatrixSDK/Crypto/CrossSigning/MXCrossSigning.m @@ -14,14 +14,7 @@ limitations under the License. */ -#import "MXCrossSigning_Private.h" - -#import "MXCrypto_Private.h" -#import "MXDeviceInfo_Private.h" -#import "MXCrossSigningInfo_Private.h" -#import "MXKey.h" -#import "MXBase64Tools.h" - +#import "MXCrossSigning.h" #pragma mark - Constants @@ -30,1069 +23,3 @@ NSString *const MXCrossSigningNotificationDeviceIdsKey = @"deviceIds"; NSString *const MXCrossSigningErrorDomain = @"org.matrix.sdk.crosssigning"; - - -@interface MXLegacyCrossSigning () -@property (nonatomic, nullable, strong) MXCrossSigningInfo *myUserCrossSigningKeys; -@end - - -@implementation MXLegacyCrossSigning - -@synthesize state = _state; - -- (BOOL)canCrossSign -{ - return (_state >= MXCrossSigningStateCanCrossSign); -} - -- (BOOL)canTrustCrossSigning -{ - return (_state >= MXCrossSigningStateTrustCrossSigning); -} - -- (BOOL)hasAllPrivateKeys -{ - id cryptoStore = self.crypto.store; - - return ([cryptoStore secretWithSecretId:MXSecretId.crossSigningMaster] - && [cryptoStore secretWithSecretId:MXSecretId.crossSigningSelfSigning] - && [cryptoStore secretWithSecretId:MXSecretId.crossSigningUserSigning]); -} - -- (void)setupWithPassword:(NSString*)password - success:(void (^)(void))success - failure:(void (^)(NSError *error))failure -{ - MXCredentials *myCreds = _crypto.mxSession.matrixRestClient.credentials; - - // Do the auth dance to upload them to the HS - [self.crypto.matrixRestClient authSessionToUploadDeviceSigningKeys:^(MXAuthenticationSession *authSession) { - - NSDictionary *authParams; - - if (authSession) - { - authParams= @{ - @"session": authSession.session, - @"user": myCreds.userId, - @"password": password, - @"type": kMXLoginFlowTypePassword - }; - } - else - { - // Try to setup cross-signing without authentication parameters in case if a grace period is enabled - authParams = @{}; - } - - [self setupWithAuthParams:authParams success:success failure:failure]; - - } failure:^(NSError *error) { - if (failure) - { - dispatch_async(dispatch_get_main_queue(), ^{ - failure(error); - }); - } - }]; -} - -- (void)setupWithAuthParams:(NSDictionary*)authParams - success:(void (^)(void))success - failure:(void (^)(NSError *error))failure -{ - MXCredentials *myCreds = _crypto.mxSession.matrixRestClient.credentials; - - // Create keys - NSDictionary *privateKeys; - MXCrossSigningInfo *keys = [self createKeys:&privateKeys]; - - MXLogDebug(@"[MXCrossSigning] setup on device %@. MSK: %@", myCreds.deviceId, keys.masterKeys.keys); - - void (^failureBlock)(NSError *error) = ^void(NSError *error) { - - // Clean in-flight keys - [self.crypto.store deleteSecretWithSecretId:MXSecretId.crossSigningMaster]; - [self.crypto.store deleteSecretWithSecretId:MXSecretId.crossSigningUserSigning]; - [self.crypto.store deleteSecretWithSecretId:MXSecretId.crossSigningSelfSigning]; - - dispatch_async(dispatch_get_main_queue(), ^{ - failure(error); - }); - }; - - // Delegate the storage of them - [self storeCrossSigningKeys:privateKeys success:^{ - - NSDictionary *signingKeys = @{ - @"master_key": keys.masterKeys.JSONDictionary, - @"self_signing_key": keys.selfSignedKeys.JSONDictionary, - @"user_signing_key": keys.userSignedKeys.JSONDictionary, - }; - - - [self.crypto.matrixRestClient uploadDeviceSigningKeys:signingKeys authParams:authParams success:^{ - - // Store our user's keys - [keys updateTrustLevel:[MXUserTrustLevel trustLevelWithCrossSigningVerified:YES locallyVerified:YES]]; - [self.crypto.store storeCrossSigningKeys:keys]; - - // Cross-signing is bootstrapped - // Refresh our state so that we can cross-sign - [self refreshStateWithSuccess:^(BOOL stateUpdated) { - // Expose this device to other users as signed by me - [self crossSignDeviceWithDeviceId:myCreds.deviceId userId:myCreds.userId success:^{ - success(); - } failure:failureBlock]; - } failure:failureBlock]; - - } failure:failureBlock]; - - } failure:failureBlock]; -} - - -- (MXCrossSigningInfo *)createKeys:(NSDictionary *__autoreleasing _Nonnull *)outPrivateKeys -{ - NSString *myUserId = _crypto.mxSession.myUserId; - NSString *myDeviceId = _crypto.mxSession.myDeviceId; - - MXCrossSigningInfo *crossSigningKeys = [[MXCrossSigningInfo alloc] initWithUserId:myUserId]; - - NSMutableDictionary *privateKeys = [NSMutableDictionary dictionary]; - - // Master key - NSData *masterKeyPrivate; - OLMPkSigning *masterSigning; - NSString *masterKeyPublic = [self makeSigningKey:&masterSigning privateKey:&masterKeyPrivate]; - - if (masterKeyPublic) - { - NSString *type = MXCrossSigningKeyType.master; - - MXCrossSigningKey *masterKey = [[MXCrossSigningKey alloc] initWithUserId:myUserId usage:@[type] keys:masterKeyPublic]; - [crossSigningKeys addCrossSigningKey:masterKey type:type]; - privateKeys[type] = masterKeyPrivate; - - // Sign the MSK with device - [masterKey addSignatureFromUserId:myUserId publicKey:myDeviceId signature:[_crypto.olmDevice signJSON:masterKey.signalableJSONDictionary]]; - } - - // self_signing key - NSData *sskPrivate; - NSString *sskPublic = [self makeSigningKey:nil privateKey:&sskPrivate]; - - if (sskPublic) - { - NSString *type = MXCrossSigningKeyType.selfSigning; - - MXCrossSigningKey *ssk = [[MXCrossSigningKey alloc] initWithUserId:myUserId usage:@[type] keys:sskPublic]; - [_crossSigningTools pkSignKey:ssk withPkSigning:masterSigning userId:myUserId publicKey:masterKeyPublic]; - - [crossSigningKeys addCrossSigningKey:ssk type:type]; - privateKeys[type] = sskPrivate; - } - - // user_signing key - NSData *uskPrivate; - NSString *uskPublic = [self makeSigningKey:nil privateKey:&uskPrivate]; - - if (uskPublic) - { - NSString *type = MXCrossSigningKeyType.userSigning; - - MXCrossSigningKey *usk = [[MXCrossSigningKey alloc] initWithUserId:myUserId usage:@[type] keys:uskPublic]; - [_crossSigningTools pkSignKey:usk withPkSigning:masterSigning userId:myUserId publicKey:masterKeyPublic]; - - [crossSigningKeys addCrossSigningKey:usk type:type]; - privateKeys[type] = uskPrivate; - } - - if (outPrivateKeys) - { - *outPrivateKeys = privateKeys; - } - - return crossSigningKeys; -} - -- (void)crossSignDeviceWithDeviceId:(NSString*)deviceId - userId:(NSString *)userId - success:(void (^)(void))success - failure:(void (^)(NSError *error))failure -{ - MXLogDebug(@"[MXCrossSigning] crossSignDeviceWithDeviceId: %@", deviceId); - - NSString *myUserId = self.crypto.mxSession.myUserId; - - dispatch_async(self.crypto.cryptoQueue, ^{ - - // Make sure we have latest data from the user - [self.crypto.deviceList downloadKeys:@[myUserId] forceDownload:NO success:^(MXUsersDevicesMap *userDevices, NSDictionary *crossSigningKeysMap) { - - MXDeviceInfo *device = [self.crypto.store deviceWithDeviceId:deviceId forUser:myUserId]; - - // Sanity check - if (!device) - { - MXLogDebug(@"[MXCrossSigning] crossSignDeviceWithDeviceId: Unknown device %@", deviceId); - dispatch_async(dispatch_get_main_queue(), ^{ - NSError *error = [NSError errorWithDomain:MXCrossSigningErrorDomain - code:MXCrossSigningUnknownDeviceIdErrorCode - userInfo:@{ - NSLocalizedDescriptionKey: @"Unknown device", - }]; - failure(error); - }); - return; - } - - [self signDevice:device success:^{ - dispatch_async(dispatch_get_main_queue(), ^{ - success(); - }); - } failure:^(NSError *error) { - dispatch_async(dispatch_get_main_queue(), ^{ - failure(error); - }); - }]; - } failure:^(NSError *error) { - dispatch_async(dispatch_get_main_queue(), ^{ - failure(error); - }); - }]; - }); -} - -- (void)signUserWithUserId:(NSString*)userId - success:(void (^)(void))success - failure:(void (^)(NSError *error))failure -{ - MXLogDebug(@"[MXCrossSigning] signUserWithUserId: %@", userId); - - dispatch_async(self.crypto.cryptoQueue, ^{ - // Make sure we have latest data from the user - [self.crypto.deviceList downloadKeys:@[userId] forceDownload:NO success:^(MXUsersDevicesMap *userDevices, NSDictionary *crossSigningKeysMap) { - - MXCrossSigningInfo *otherUserKeys = [self.crypto.store crossSigningKeysForUser:userId]; - MXCrossSigningKey *otherUserMasterKeys = otherUserKeys.masterKeys; - - // Sanity check - if (!otherUserMasterKeys) - { - MXLogDebug(@"[MXCrossSigning] signUserWithUserId: User %@ unknown locally", userId); - dispatch_async(dispatch_get_main_queue(), ^{ - NSError *error = [NSError errorWithDomain:MXCrossSigningErrorDomain - code:MXCrossSigningUnknownUserIdErrorCode - userInfo:@{ - NSLocalizedDescriptionKey: @"Unknown user", - }]; - failure(error); - }); - return; - } - - [self signKey:otherUserMasterKeys success:^{ - - // Update other user's devices trust - [self checkTrustLevelForDevicesOfUser:userId]; - - dispatch_async(dispatch_get_main_queue(), ^{ - success(); - }); - } failure:^(NSError *error) { - dispatch_async(dispatch_get_main_queue(), ^{ - failure(error); - }); - }]; - - } failure:^(NSError *error) { - dispatch_async(dispatch_get_main_queue(), ^{ - failure(error); - }); - }]; - }); -} - -- (MXCrossSigningInfo *)crossSigningKeysForUser:(NSString *)userId -{ - return [self.crypto.store crossSigningKeysForUser:userId]; -} - -- (void)requestPrivateKeysToDeviceIds:(nullable NSArray*)deviceIds - success:(void (^)(void))success - onPrivateKeysReceived:(void (^)(void))onPrivateKeysReceived - failure:(void (^)(NSError *error))failure -{ - MXLogDebug(@"[MXCrossSigning] requestPrivateKeysToDeviceIds: %@", deviceIds); - - // Make a secret share request for MSK, USK and SSK - dispatch_group_t successGroup = dispatch_group_create(); - - // Note: this may never resolve because this depends on other user's devices. - // We could improve it a bit but we will never fix all cases - dispatch_group_t onPrivateKeysReceivedGroup = dispatch_group_create(); - __block BOOL secretReceivedForMSK = NO; - __block BOOL secretReceivedForUSK = NO; - __block BOOL secretReceivedForSSK = NO; - - __block NSString *mskRequestId, *uskRequestId, *sskRequestId; - - - // MSK - dispatch_group_enter(successGroup); - dispatch_group_enter(onPrivateKeysReceivedGroup); - [self.crypto.secretShareManager requestSecret:MXSecretId.crossSigningMaster toDeviceIds:deviceIds success:^(NSString * _Nonnull requestId) { - mskRequestId = requestId; - dispatch_group_leave(successGroup); - } onSecretReceived:^BOOL(NSString * _Nonnull secret) { - - BOOL isSecretValid = NO; - if (self.myUserCrossSigningKeys.masterKeys.keys) - { - isSecretValid = [self.crossSigningTools isSecretValid:secret forPublicKeys:self.myUserCrossSigningKeys.masterKeys.keys]; - } - else - { - // Accept the secret anyway (It should not happen) - isSecretValid = YES; - } - - MXLogDebug(@"[MXCrossSigning] requestPrivateKeysToDeviceIds: Got MSK. isSecretValid: %@", @(isSecretValid)); - if (isSecretValid) - { - [self.crypto.store storeSecret:secret withSecretId:MXSecretId.crossSigningMaster]; - if (!secretReceivedForMSK) - { - secretReceivedForMSK = YES; - dispatch_group_leave(onPrivateKeysReceivedGroup); - } - } - return isSecretValid; - } failure:^(NSError * _Nonnull error) { - // Cancel the other request - if (mskRequestId) - { - [self.crypto.secretShareManager cancelRequestWithRequestId:mskRequestId success:^{} failure:^(NSError * _Nonnull error) { - }]; - } - failure(error); - }]; - - - // USK - dispatch_group_enter(successGroup); - dispatch_group_enter(onPrivateKeysReceivedGroup); - [self.crypto.secretShareManager requestSecret:MXSecretId.crossSigningUserSigning toDeviceIds:deviceIds success:^(NSString * _Nonnull requestId) { - uskRequestId = requestId; - dispatch_group_leave(successGroup); - } onSecretReceived:^BOOL(NSString * _Nonnull secret) { - - BOOL isSecretValid = NO; - if (self.myUserCrossSigningKeys.userSignedKeys.keys) - { - isSecretValid = [self.crossSigningTools isSecretValid:secret forPublicKeys:self.myUserCrossSigningKeys.userSignedKeys.keys]; - } - else - { - // Accept the secret anyway (It should not happen) - isSecretValid = YES; - } - - MXLogDebug(@"[MXCrossSigning] requestPrivateKeysToDeviceIds: Got USK. isSecretValid: %@", @(isSecretValid)); - if (isSecretValid) - { - [self.crypto.store storeSecret:secret withSecretId:MXSecretId.crossSigningUserSigning]; - if (!secretReceivedForUSK) - { - secretReceivedForUSK = YES; - dispatch_group_leave(onPrivateKeysReceivedGroup); - } - } - return isSecretValid; - } failure:^(NSError * _Nonnull error) { - // Cancel the other request - if (sskRequestId) - { - [self.crypto.secretShareManager cancelRequestWithRequestId:sskRequestId success:^{} failure:^(NSError * _Nonnull error) { - }]; - } - failure(error); - }]; - - - // SSK - dispatch_group_enter(successGroup); - dispatch_group_enter(onPrivateKeysReceivedGroup); - [self.crypto.secretShareManager requestSecret:MXSecretId.crossSigningSelfSigning toDeviceIds:deviceIds success:^(NSString * _Nonnull requestId) { - sskRequestId = requestId; - dispatch_group_leave(successGroup); - } onSecretReceived:^BOOL(NSString * _Nonnull secret) { - - BOOL isSecretValid = NO; - if (self.myUserCrossSigningKeys.selfSignedKeys.keys) - { - isSecretValid = [self.crossSigningTools isSecretValid:secret forPublicKeys:self.myUserCrossSigningKeys.selfSignedKeys.keys]; - } - else - { - // Accept the secret anyway (It should not happen) - isSecretValid = YES; - } - - MXLogDebug(@"[MXCrossSigning] requestPrivateKeysToDeviceIds: Got SSK. isSecretValid: %@", @(isSecretValid)); - if (isSecretValid) - { - [self.crypto.store storeSecret:secret withSecretId:MXSecretId.crossSigningSelfSigning]; - if (!secretReceivedForSSK) - { - secretReceivedForSSK = YES; - dispatch_group_leave(onPrivateKeysReceivedGroup); - } - } - return isSecretValid; - } failure:^(NSError * _Nonnull error) { - // Cancel the other request - if (uskRequestId) - { - [self.crypto.secretShareManager cancelRequestWithRequestId:uskRequestId success:^{} failure:^(NSError * _Nonnull error) { - }]; - } - failure(error); - }]; - - dispatch_group_notify(successGroup, dispatch_get_main_queue(), ^{ - MXLogDebug(@"[MXCrossSigning] requestPrivateKeysToDeviceIds: request succeeded"); - success(); - }); - - dispatch_group_notify(onPrivateKeysReceivedGroup, dispatch_get_main_queue(), ^{ - MXLogDebug(@"[MXCrossSigning] requestPrivateKeysToDeviceIds: Got keys"); - [self refreshStateWithSuccess:^(BOOL stateUpdated) { - onPrivateKeysReceived(); - } failure:^(NSError * _Nonnull error) { - onPrivateKeysReceived(); - }]; - }); -} - - -#pragma mark - SDK-Private methods - - -- (instancetype)initWithCrypto:(MXLegacyCrypto *)crypto; -{ - self = [super init]; - if (self) - { - _state = MXCrossSigningStateNotBootstrapped; - _crypto = crypto; - _crossSigningTools = [MXCrossSigningTools new]; - - NSString *myUserId = _crypto.mxSession.myUserId; - _myUserCrossSigningKeys = [_crypto.store crossSigningKeysForUser:myUserId]; - - [self computeState]; - [self registerUsersDevicesUpdateNotification]; - } - return self; -} - -- (void)refreshStateWithSuccess:(nullable void (^)(BOOL stateUpdated))success - failure:(nullable void (^)(NSError *error))failure -{ - MXCrossSigningState stateBefore = _state; - BOOL canTrustCrossSigningBefore = self.canTrustCrossSigning; - MXCrossSigningInfo *myUserCrossSigningKeysBefore = self.myUserCrossSigningKeys; - - NSString *myUserId = _crypto.mxSession.myUserId; - _myUserCrossSigningKeys = [_crypto.store crossSigningKeysForUser:myUserId]; - - MXLogDebug(@"[MXCrossSigning] refreshState for device %@: Current state: %@", self.crypto.store.deviceId, @(self.state)); - - // Refresh user's keys - [self.crypto downloadKeys:@[myUserId] forceDownload:YES success:^(MXUsersDevicesMap *usersDevicesInfoMap, NSDictionary *crossSigningKeysMap) { - - BOOL sameCrossSigningKeys = [myUserCrossSigningKeysBefore hasSameKeysAsCrossSigningInfo:crossSigningKeysMap[myUserId]]; - self.myUserCrossSigningKeys = crossSigningKeysMap[myUserId]; - if (self.myUserCrossSigningKeys) - { - // Store it. computeState checks what is in the store - [self.crypto.store storeCrossSigningKeys:self.myUserCrossSigningKeys]; - } - - [self computeState]; - - // If keys have changed, we need to recompute cross-signing trusts. - // Compute cross-signing trusts also if we detect we can now. - if (!sameCrossSigningKeys - || (!canTrustCrossSigningBefore && self.canTrustCrossSigning)) - { - [self resetTrust]; - } - - MXLogDebug(@"[MXCrossSigning] refreshState for device %@: Updated state: %@", self.crypto.store.deviceId, @(self.state)); - - if (success) - { - dispatch_async(dispatch_get_main_queue(), ^{ - success(self.state != stateBefore - || sameCrossSigningKeys); - }); - } - } failure:^(NSError *error) { - MXLogDebug(@"[MXCrossSigning] refreshStateWithSuccess: Failed to load my user's keys"); - if (failure) - { - dispatch_async(dispatch_get_main_queue(), ^{ - failure(error); - }); - } - }]; -} - -- (BOOL)isUserWithCrossSigningKeysVerified:(MXCrossSigningInfo*)crossSigningKeys -{ - BOOL isUserVerified = NO; - - NSString *myUserId = _crypto.mxSession.myUserId; - if ([myUserId isEqualToString:crossSigningKeys.userId]) - { - // Can we trust the current cross-signing setup? - return [self isSelfTrusted]; - } - - if (!self.myUserCrossSigningKeys.userSignedKeys) - { - // If there's no user signing key, they can't possibly be verified - return NO; - } - - NSError *error; - isUserVerified = [self.crossSigningTools pkVerifyKey:crossSigningKeys.masterKeys - userId:myUserId - publicKey:self.myUserCrossSigningKeys.userSignedKeys.keys - error:&error]; - if (error) - { - MXLogDebug(@"[MXCrossSigning] computeUserTrustLevelForCrossSigningKeys failed. Error: %@", error); - } - - return isUserVerified; -} - -- (BOOL)isDeviceVerified:(MXDeviceInfo*)device -{ - BOOL isDeviceVerified = NO; - - MXCrossSigningInfo *userCrossSigning = [self.crypto.store crossSigningKeysForUser:device.userId]; - MXUserTrustLevel *userTrustLevel = [self.crypto trustLevelForUser:device.userId]; - - MXCrossSigningKey *userSSK = userCrossSigning.selfSignedKeys; - if (!userSSK) - { - // If the user has no self-signing key then we cannot make any - // trust assertions about this device from cross-signing - return NO; - } - - // If we can verify the user's SSK from their master key... - BOOL userSSKVerify = [self.crossSigningTools pkVerifyKey:userSSK - userId:userCrossSigning.userId - publicKey:userCrossSigning.masterKeys.keys - error:nil]; - - // ...and this device's key from their SSK... - BOOL deviceVerify = [self.crossSigningTools pkVerifyObject:device.JSONDictionary - userId:userCrossSigning.userId - publicKey:userSSK.keys - error:nil]; - - // ...then we trust this device as much as far as we trust the user - if (userSSKVerify && deviceVerify) - { - isDeviceVerified = userTrustLevel.isCrossSigningVerified; - } - - return isDeviceVerified; -} - -- (void)checkTrustLevelForDevicesOfUser:(NSString*)userId -{ - NSArray *devices = [self.crypto.store devicesForUser:userId].allValues; - - for (MXDeviceInfo *device in devices) - { - BOOL crossSigningVerified = [self isDeviceVerified:device]; - MXDeviceTrustLevel *trustLevel = [MXDeviceTrustLevel trustLevelWithLocalVerificationStatus:device.trustLevel.localVerificationStatus crossSigningVerified:crossSigningVerified]; - - if ([device updateTrustLevel:trustLevel]) - { - [self.crypto.store storeDeviceForUser:device.userId device:device]; - } - } -} - -- (void)requestPrivateKeys -{ - [self requestPrivateKeysToDeviceIds:nil success:^{ - } onPrivateKeysReceived:^{ - } failure:^(NSError * _Nonnull error) { - }]; -} - -#pragma mark - Private methods - - -- (void)computeState -{ - MXCrossSigningState state = MXCrossSigningStateNotBootstrapped; - - BOOL didTrustCrossSigning = self.canTrustCrossSigning; - - if (_myUserCrossSigningKeys) - { - state = MXCrossSigningStateCrossSigningExists; - - if ([self isSelfTrusted]) - { - state = MXCrossSigningStateTrustCrossSigning; - - if (self.haveCrossSigningPrivateKeysInCryptoStore) - { - state = MXCrossSigningStateCanCrossSign; - } - } - } - - MXLogDebug(@"[MXCrossSigning] computeState: myUserCrossSigningKeys: %@", _myUserCrossSigningKeys); - MXLogDebug(@"[MXCrossSigning] computeState: state: %@ (was %@)", @(state), @(_state)); - - _state = state; - - if (didTrustCrossSigning && !self.canTrustCrossSigning) - { - MXLogDebug(@"[MXCrossSigning] computeState: Detected new cross-signing keys"); - dispatch_async(dispatch_get_main_queue(), ^{ - [[NSNotificationCenter defaultCenter] postNotificationName:MXCrossSigningDidChangeCrossSigningKeysNotification object:self userInfo:nil]; - }); - } -} - -// Recompute cross-signing trust on all users we know -- (void)resetTrust -{ - MXLogDebug(@"[MXCrossSigning] resetTrust for device %@", self.crypto.mxSession.matrixRestClient.credentials.deviceId); - - for (MXCrossSigningInfo *crossSigningInfo in self.crypto.store.crossSigningKeys) - { - BOOL isCrossSigningVerified = [self isUserWithCrossSigningKeysVerified:crossSigningInfo]; - if (crossSigningInfo.trustLevel.isCrossSigningVerified != isCrossSigningVerified) - { - MXLogDebug(@"[MXCrossSigning] resetTrust: Change trust for %@: %@ -> %@", crossSigningInfo.userId, - @(crossSigningInfo.trustLevel.isCrossSigningVerified), - @(isCrossSigningVerified)); - - MXUserTrustLevel *newTrustLevel = [MXUserTrustLevel trustLevelWithCrossSigningVerified:isCrossSigningVerified locallyVerified:crossSigningInfo.trustLevel.isLocallyVerified]; - if ([crossSigningInfo updateTrustLevel:newTrustLevel]) - { - [self.crypto.store storeCrossSigningKeys:crossSigningInfo]; - } - - // Update trust on associated devices - [self checkTrustLevelForDevicesOfUser:crossSigningInfo.userId]; - } - } -} - -- (NSString *)makeSigningKey:(OLMPkSigning * _Nullable *)signing privateKey:(NSData* _Nullable *)privateKey -{ - OLMPkSigning *pkSigning = [[OLMPkSigning alloc] init]; - - NSError *error; - NSData *privKey = [OLMPkSigning generateSeed]; - NSString *pubKey = [pkSigning doInitWithSeed:privKey error:&error]; - if (error) - { - MXLogDebug(@"[MXCrossSigning] makeSigningKey failed. Error: %@", error); - return nil; - } - - if (signing) - { - *signing = pkSigning; - } - if (privateKey) - { - *privateKey = privKey; - } - return pubKey; -} - -/** - Check that MSK is trusted by this device. - Then, check that USK and SSK are trusted by the MSK. - */ -- (BOOL)isSelfTrusted -{ - // Is the master key trusted? - BOOL isMasterKeyTrusted = NO; - MXCrossSigningKey *myMasterKey = _myUserCrossSigningKeys.masterKeys; - if (!myMasterKey) - { - // Cross-signing is not set up - MXLogDebug(@"[MXCrossSigning] isSelfTrusted: NO (No MSK)"); - return NO; - } - - NSString *myUserId = _crypto.mxSession.myUserId; - - // Is the master key trusted? - MXCrossSigningInfo *myCrossSigningInfo = [_crypto.store crossSigningKeysForUser:myUserId]; - if (myCrossSigningInfo && myCrossSigningInfo.trustLevel.isLocallyVerified) - { - isMasterKeyTrusted = YES; - } - else if ([self hasMatchingMasterPrivateKeyInCryptoStore:myCrossSigningInfo.masterKeys]) - { - isMasterKeyTrusted = YES; - } - else - { - // Is it signed by a locally trusted device? - NSDictionary *myUserSignatures = myMasterKey.signatures.map[myUserId]; - for (NSString *publicKeyId in myUserSignatures) - { - MXKey *key = [[MXKey alloc] initWithKeyFullId:publicKeyId value:myUserSignatures[publicKeyId]]; - if ([key.type isEqualToString:kMXKeyEd25519Type]) - { - MXDeviceInfo *device = [self.crypto.store deviceWithDeviceId:key.keyId forUser:myUserId]; - if (device && device.trustLevel.isLocallyVerified) - { - // Check signature validity - NSError *error; - isMasterKeyTrusted = [_crypto.olmDevice verifySignature:device.fingerprint JSON:myMasterKey.signalableJSONDictionary signature:key.value error:&error]; - - if (isMasterKeyTrusted) - { - break; - } - } - } - } - } - - - if (!isMasterKeyTrusted) - { - MXLogDebug(@"[MXCrossSigning] isSelfTrusted: NO (MSK not trusted). MSK: %@", myMasterKey); - MXLogDebug(@"[MXCrossSigning] isSelfTrusted: My cross-signing info: %@", myCrossSigningInfo); - MXLogDebug(@"[MXCrossSigning] isSelfTrusted: My user devices: %@", [self.crypto.store devicesForUser:myUserId]); - - return NO; - } - - // Is USK signed? - MXCrossSigningKey *myUserKey = _myUserCrossSigningKeys.userSignedKeys; - BOOL isUSKSignatureValid = [self checkSignatureOnKey:myUserKey byKey:myMasterKey userId:myUserId]; - if (!isUSKSignatureValid) - { - MXLogDebug(@"[MXCrossSigning] isSelfTrusted: NO (Invalid MSK signature for USK). USK: %@", myUserKey); - MXLogDebug(@"[MXCrossSigning] isSelfTrusted: MSK: %@", myMasterKey); - return NO; - } - - // Is SSK signed? - MXCrossSigningKey *mySelfKey = _myUserCrossSigningKeys.selfSignedKeys; - BOOL isSSKSignatureValid = [self checkSignatureOnKey:mySelfKey byKey:myMasterKey userId:myUserId]; - if (!isSSKSignatureValid) - { - MXLogDebug(@"[MXCrossSigning] isSelfTrusted: NO (Invalid MSK signature for SSK). SSK: %@", mySelfKey); - MXLogDebug(@"[MXCrossSigning] isSelfTrusted: MSK: %@", myMasterKey); - return NO; - } - - return YES; -} - -- (BOOL)checkSignatureOnKey:(nullable MXCrossSigningKey*)key byKey:(MXCrossSigningKey*)signingKey userId:(NSString*)userId -{ - if (!key) - { - MXLogDebug(@"[MXCrossSigning] checkSignatureOnKey: NO (No key)"); - return NO; - } - - NSString *signingPublicKeyId = [NSString stringWithFormat:@"%@:%@", kMXKeyEd25519Type, signingKey.keys]; - NSString *signatureMadeBySigningKey = [key.signatures objectForDevice:signingPublicKeyId forUser:userId]; - if (!signatureMadeBySigningKey) - { - MXLogDebug(@"[MXCrossSigning] checkSignatureOnKey: NO (Key not signed)"); - return NO; - } - - NSError *error; - BOOL isSignatureValid = [_crypto.olmDevice verifySignature:signingKey.keys JSON:key.signalableJSONDictionary signature:signatureMadeBySigningKey error:&error]; - if (!isSignatureValid) - { - MXLogDebug(@"[MXCrossSigning] checkSignatureOnKey: NO (Invalid signature)"); - return NO; - } - - return YES; -} - -- (void)registerUsersDevicesUpdateNotification -{ - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(usersDevicesDidUpdate:) name:MXDeviceListDidUpdateUsersDevicesNotification object:self.crypto]; -} - -- (void)usersDevicesDidUpdate:(NSNotification*)notification -{ - // If we cannot cross-sign, we cannot self verify new devices of our user - if (!self.canCrossSign) - { - return; - } - - NSDictionary *userInfo = notification.userInfo; - - MXCredentials *myUser = _crypto.mxSession.matrixRestClient.credentials; - - NSArray *myUserDevices = userInfo[myUser.userId]; - - if (myUserDevices) - { - NSMutableArray *newDeviceIds = [NSMutableArray new]; - - for (MXDeviceInfo *deviceInfo in myUserDevices) - { - if (!deviceInfo.trustLevel.isVerified - && deviceInfo.trustLevel.localVerificationStatus == MXDeviceUnknown) - { - [newDeviceIds addObject:deviceInfo.deviceId]; - } - } - - if (newDeviceIds.count) - { - NSDictionary *userInfo = @{ MXCrossSigningNotificationDeviceIdsKey: newDeviceIds }; - - dispatch_async(dispatch_get_main_queue(), ^{ - [[NSNotificationCenter defaultCenter] postNotificationName:MXCrossSigningMyUserDidSignInOnNewDeviceNotification object:self userInfo:userInfo]; - }); - } - } -} - - -#pragma mark - Signing - -- (void)signDevice:(MXDeviceInfo*)device - success:(void (^)(void))success - failure:(void (^)(NSError *error))failure -{ - NSString *myUserId = _crypto.mxSession.myUserId; - - NSDictionary *object = @{ - @"algorithms": device.algorithms, - @"keys": device.keys, - @"device_id": device.deviceId, - @"user_id": myUserId, - }; - - // Sign the device - [self signObject:object - withKeyType:MXCrossSigningKeyType.selfSigning - success:^(NSDictionary *signedObject) - { - // And upload the signature - [self.crypto.mxSession.matrixRestClient uploadKeySignatures:@{ - myUserId: @{ - device.deviceId: signedObject - } - } - success:^ - { - [self refreshStateWithSuccess:^(BOOL stateUpdated) { - success(); - } failure:failure]; - - } failure:failure]; - - } failure:failure]; -} - -- (void)signKey:(MXCrossSigningKey*)key - success:(void (^)(void))success - failure:(void (^)(NSError *error))failure -{ - // Sign the other user key - [self signObject:key.signalableJSONDictionary - withKeyType:MXCrossSigningKeyType.userSigning - success:^(NSDictionary *signedObject) - { - // And upload the signature - [self.crypto.mxSession.matrixRestClient uploadKeySignatures:@{ - key.userId: @{ - key.keys: signedObject - } - } - success:^ - { - // Refresh data locally before returning - // TODO: This network request is suboptimal. We could update data in the store directly - [self.crypto.deviceList downloadKeys:@[key.userId] forceDownload:YES success:^(MXUsersDevicesMap *usersDevicesInfoMap, NSDictionary *crossSigningKeysMap) { - success(); - } failure:failure]; - - } failure:failure]; - } - failure:failure]; -} - -- (void)signObject:(NSDictionary*)object withKeyType:(NSString*)keyType - success:(void (^)(NSDictionary *signedObject))success - failure:(void (^)(NSError *error))failure -{ - [self crossSigningKeyWithKeyType:keyType success:^(NSString *publicKey, OLMPkSigning *signing) { - - NSString *myUserId = self.crypto.mxSession.myUserId; - - NSError *error; - NSDictionary *signedObject = [self.crossSigningTools pkSignObject:object withPkSigning:signing userId:myUserId publicKey:publicKey error:&error]; - if (!error) - { - success(signedObject); - } - else - { - failure(error); - } - } failure:failure]; -} - - -#pragma mark - Private keys storage - -- (BOOL)hasMatchingMasterPrivateKeyInCryptoStore:(MXCrossSigningKey *)masterKey -{ - NSString *mskPrivateKeyBase64 = [self.crypto.store secretWithSecretId:MXSecretId.crossSigningMaster]; - // Check it is valid and corresponds to our current master keys - if (mskPrivateKeyBase64 && masterKey) - { - OLMPkSigning *mskPkSigning = [self.crossSigningTools pkSigningFromBase64PrivateKey:mskPrivateKeyBase64 - withExpectedPublicKey:masterKey.keys]; - if (mskPkSigning) - { - return YES; - } - } - - return NO; -} - -- (BOOL)haveCrossSigningPrivateKeysInCryptoStore -{ - NSString *uskPrivateKeyBase64 = [self.crypto.store secretWithSecretId:MXSecretId.crossSigningUserSigning]; - NSString *sskPrivateKeyBase64 = [self.crypto.store secretWithSecretId:MXSecretId.crossSigningSelfSigning]; - if (uskPrivateKeyBase64 && sskPrivateKeyBase64) - { - // Check they are valid and they correspond to our current cross-signing keys - if (_myUserCrossSigningKeys.userSignedKeys - && _myUserCrossSigningKeys.selfSignedKeys) - { - OLMPkSigning *uskPkSigning = [self.crossSigningTools pkSigningFromBase64PrivateKey:uskPrivateKeyBase64 - withExpectedPublicKey:_myUserCrossSigningKeys.userSignedKeys.keys]; - OLMPkSigning *sskPkSigning = [self.crossSigningTools pkSigningFromBase64PrivateKey:sskPrivateKeyBase64 - withExpectedPublicKey:_myUserCrossSigningKeys.selfSignedKeys.keys]; - - if (uskPkSigning && sskPkSigning) - { - return YES; - } - } - - // Else, delete them - MXLogDebug(@"[MXCrossSigning] haveCrossSigningPrivateKeysInCryptoStore: Delete local keys. They are obsolete"); - [self.crypto.store deleteSecretWithSecretId:MXSecretId.crossSigningUserSigning]; - [self.crypto.store deleteSecretWithSecretId:MXSecretId.crossSigningSelfSigning]; - } - - return NO; -} - -- (void)crossSigningKeyWithKeyType:(NSString*)keyType - success:(void (^)(NSString *publicKey, OLMPkSigning *signing))success - failure:(void (^)(NSError *error))failure -{ - NSString *expectedPublicKey = _myUserCrossSigningKeys.keys[keyType].keys; - if (!expectedPublicKey) - { - MXLogDebug(@"[MXCrossSigning] getCrossSigningKeyWithKeyType: %@ failed. No such key present", keyType); - failure(nil); - return; - } - - // Check local store - NSString *secretId = [self secretIdFromKeyType:keyType]; - if (secretId) - { - NSString *privateKeyBase64 = [self.crypto.store secretWithSecretId:secretId]; - if (privateKeyBase64) - { - OLMPkSigning *pkSigning = [self.crossSigningTools pkSigningFromBase64PrivateKey:privateKeyBase64 withExpectedPublicKey:expectedPublicKey]; - if (!pkSigning) - { - MXLogDebug(@"[MXCrossSigning] getCrossSigningKeyWithKeyType failed to get PK signing"); - failure(nil); - return; - } - - success(expectedPublicKey, pkSigning); - return; - } - } - - MXLogDebug(@"[MXCrossSigning] getCrossSigningKeyWithKeyType: %@ failed. No such key present", keyType); - failure(nil); -} - -- (void)storeCrossSigningKeys:(NSDictionary*)privateKeys - success:(void (^)(void))success - failure:(void (^)(NSError *error))failure -{ - // Store MSK, USK & SSK keys to crypto store - for (NSString *keyType in privateKeys) - { - NSString *secretId = [self secretIdFromKeyType:keyType]; - if (secretId) - { - NSString *secret = [MXBase64Tools unpaddedBase64FromData:privateKeys[keyType]]; - [self.crypto.store storeSecret:secret withSecretId:secretId]; - } - } - - success(); -} - -// Convert a cross-signing key type to a SSSS secret id -- (nullable NSString*)secretIdFromKeyType:(NSString*)keyType -{ - NSString *secretId; - if ([keyType isEqualToString:MXCrossSigningKeyType.master]) - { - secretId = MXSecretId.crossSigningMaster; - } - else if ([keyType isEqualToString:MXCrossSigningKeyType.selfSigning]) - { - secretId = MXSecretId.crossSigningSelfSigning; - } - else if ([keyType isEqualToString:MXCrossSigningKeyType.userSigning]) - { - secretId = MXSecretId.crossSigningUserSigning; - } - - return secretId; -} - -@end diff --git a/MatrixSDK/Crypto/CrossSigning/MXCrossSigningTools.h b/MatrixSDK/Crypto/CrossSigning/MXCrossSigningTools.h index 5a9990d07f..71e03fd861 100644 --- a/MatrixSDK/Crypto/CrossSigning/MXCrossSigningTools.h +++ b/MatrixSDK/Crypto/CrossSigning/MXCrossSigningTools.h @@ -16,10 +16,6 @@ #import -#import - -#import "MXCrossSigningKey.h" - NS_ASSUME_NONNULL_BEGIN @@ -32,22 +28,4 @@ typedef NS_ENUM(NSInteger, MXCrossSigningToolsErrorCode) MXCrossSigningToolsMissingSignatureErrorCode, }; - -@interface MXCrossSigningTools : NSObject - -- (NSDictionary*)pkSignObject:(NSDictionary*)object withPkSigning:(OLMPkSigning*)pkSigning userId:(NSString*)userId publicKey:(NSString*)publicKey error:(NSError* _Nullable *)error; - -- (BOOL)pkVerifyObject:(NSDictionary*)object userId:(NSString*)userId publicKey:(NSString*)publicKey error:(NSError**)error; - - -- (void)pkSignKey:(MXCrossSigningKey*)crossSigningKey withPkSigning:(OLMPkSigning*)pkSigning userId:(NSString*)userId publicKey:(NSString*)publicKey; - -- (BOOL)pkVerifyKey:(MXCrossSigningKey*)crossSigningKey userId:(NSString*)userId publicKey:(NSString*)publicKey error:(NSError**)error; - -- (BOOL)isSecretValid:(NSString*)secret forPublicKeys:(NSString*)keys; - -- (nullable OLMPkSigning*)pkSigningFromBase64PrivateKey:(NSString*)base64PrivateKey withExpectedPublicKey:(NSString*)expectedPublicKey; - -@end - NS_ASSUME_NONNULL_END diff --git a/MatrixSDK/Crypto/CrossSigning/MXCrossSigningTools.m b/MatrixSDK/Crypto/CrossSigning/MXCrossSigningTools.m index 3c64043ebd..9bf6bb367c 100644 --- a/MatrixSDK/Crypto/CrossSigning/MXCrossSigningTools.m +++ b/MatrixSDK/Crypto/CrossSigning/MXCrossSigningTools.m @@ -16,151 +16,6 @@ #import "MXCrossSigningTools.h" -#import "MXCryptoTools.h" -#import "MXKey.h" -#import "MXCryptoConstants.h" -#import "MXBase64Tools.h" - #pragma mark - Constants NSString *const MXCrossSigningToolsErrorDomain = @"org.matrix.sdk.crosssigning.tools"; - - -@interface MXCrossSigningTools () -{ - OLMUtility *olmUtility; -} -@end - -@implementation MXCrossSigningTools - -- (instancetype)init -{ - self = [super init]; - if (self) - { - olmUtility = [OLMUtility new]; - } - return self; -} - -- (NSDictionary*)pkSignObject:(NSDictionary*)object withPkSigning:(OLMPkSigning*)pkSigning userId:(NSString*)userId publicKey:(NSString*)publicKey error:(NSError**)error -{ - // Sign the passed object without its `signatures` and `unsigned` fields - NSMutableDictionary *signatures = [(object[@"signatures"] ?: @{}) mutableCopy]; - NSDictionary *unsignedData = object[@"unsigned"]; - - NSMutableDictionary *signedObject = [object mutableCopy]; - [signedObject removeObjectsForKeys:@[@"signatures", @"unsigned"]]; - - NSString *signature = [pkSigning sign:[MXCryptoTools canonicalJSONStringForJSON:signedObject] error:error]; - - if (!*error) - { - // Reinject data - if (unsignedData) - { - signedObject[@"unsigned"] = unsignedData; - } - - NSMutableDictionary *userSignatures = [(signatures[userId]?: @{}) mutableCopy]; - NSString *keyId = [NSString stringWithFormat:@"%@:%@", kMXKeyEd25519Type, publicKey]; - userSignatures[keyId] = signature; - signatures[userId] = userSignatures; - - signedObject[@"signatures"] = signatures; - } - - return signedObject; -} - -- (BOOL)pkVerifyObject:(NSDictionary*)object userId:(NSString*)userId publicKey:(NSString*)publicKey error:(NSError**)error -{ - NSString *keyId = [NSString stringWithFormat:@"%@:%@", kMXKeyEd25519Type, publicKey]; - NSString *signature; - MXJSONModelSetString(signature, object[@"signatures"][userId][keyId]); - - if (!signature) - { - MXLogDebug(@"[MXCrossSigningTools] pkVerifyObject. Error: Missing signature for %@:%@ in %@", userId, keyId, object[@"signatures"]); - if (error) - { - *error = [NSError errorWithDomain:MXCrossSigningToolsErrorDomain - code:MXCrossSigningToolsMissingSignatureErrorCode - userInfo:@{ - NSLocalizedDescriptionKey: @"Missing signature", - }]; - } - return NO; - } - - NSMutableDictionary *signedObject = [object mutableCopy]; - [signedObject removeObjectsForKeys:@[@"signatures", @"unsigned"]]; - - NSData *message = [[MXCryptoTools canonicalJSONStringForJSON:signedObject] dataUsingEncoding:NSUTF8StringEncoding]; - return [olmUtility verifyEd25519Signature:signature key:publicKey message:message error:error]; -} - -- (void)pkSignKey:(MXCrossSigningKey*)crossSigningKey withPkSigning:(OLMPkSigning*)pkSigning userId:(NSString*)userId publicKey:(NSString*)publicKey -{ - NSError *error; - NSString *signature = [pkSigning sign:[MXCryptoTools canonicalJSONStringForJSON:crossSigningKey.signalableJSONDictionary] error:&error]; - if (!error) - { - [crossSigningKey addSignatureFromUserId:userId publicKey:publicKey signature:signature]; - } -} - -- (BOOL)pkVerifyKey:(MXCrossSigningKey*)crossSigningKey userId:(NSString*)userId publicKey:(NSString*)publicKey error:(NSError**)error; -{ - NSString *signature = [crossSigningKey signatureFromUserId:userId withPublicKey:publicKey]; - - if (!signature) - { - return NO; - } - - NSData *message = [[MXCryptoTools canonicalJSONStringForJSON:crossSigningKey.signalableJSONDictionary] dataUsingEncoding:NSUTF8StringEncoding]; - return [olmUtility verifyEd25519Signature:signature key:publicKey message:message error:error]; -} - -- (BOOL)isSecretValid:(NSString*)secret forPublicKeys:(NSString*)keys -{ - return (nil != [self pkSigningFromBase64PrivateKey:secret - withExpectedPublicKey:keys]); -} - -- (nullable OLMPkSigning*)pkSigningFromBase64PrivateKey:(NSString*)base64PrivateKey withExpectedPublicKey:(NSString*)expectedPublicKey -{ - OLMPkSigning *pkSigning; - - NSData *privateKey = [MXBase64Tools dataFromBase64:base64PrivateKey]; - if (privateKey) - { - pkSigning = [self pkSigningFromPrivateKey:privateKey withExpectedPublicKey:expectedPublicKey]; - } - - return pkSigning; -} - -- (nullable OLMPkSigning*)pkSigningFromPrivateKey:(NSData*)privateKey withExpectedPublicKey:(NSString*)expectedPublicKey -{ - NSError *error; - OLMPkSigning *pkSigning = [[OLMPkSigning alloc] init]; - NSString *gotPublicKey = [pkSigning doInitWithSeed:privateKey error:&error]; - if (error) - { - MXLogDebug(@"[MXCrossSigningTools] pkSigningFromPrivateKey failed to build PK signing. Error: %@", error); - return nil; - } - - if (![gotPublicKey isEqualToString:expectedPublicKey]) - { - MXLogDebug(@"[MXCrossSigningTools] pkSigningFromPrivateKey failed. Keys do not match: %@ vs %@", gotPublicKey, expectedPublicKey); - return nil; - } - - return pkSigning; -} - -@end diff --git a/MatrixSDK/Crypto/CrossSigning/MXCrossSigning_Private.h b/MatrixSDK/Crypto/CrossSigning/MXCrossSigning_Private.h deleted file mode 100644 index 6cab112463..0000000000 --- a/MatrixSDK/Crypto/CrossSigning/MXCrossSigning_Private.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - Copyright 2019 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 "MXCrossSigning.h" - -#import "MXCrossSigningTools.h" -#import "MXDeviceInfo.h" - - -NS_ASSUME_NONNULL_BEGIN - -@interface MXLegacyCrossSigning () - -@property (nonatomic) MXCrossSigningTools *crossSigningTools; - -/** - Constructor. - - @param crypto the related 'MXCrypto' instance. - */ -- (instancetype)initWithCrypto:(MXLegacyCrypto *)crypto; - -- (BOOL)isUserWithCrossSigningKeysVerified:(MXCrossSigningInfo*)crossSigningKeys; -- (BOOL)isDeviceVerified:(MXDeviceInfo*)device; - -- (void)requestPrivateKeys; - -- (void)signObject:(NSDictionary*)object withKeyType:(NSString*)keyType - success:(void (^)(NSDictionary *signedObject))success - failure:(void (^)(NSError *error))failure; - -@end - - -NS_ASSUME_NONNULL_END diff --git a/MatrixSDK/Crypto/CryptoMachine/MXCryptoMachine.swift b/MatrixSDK/Crypto/CryptoMachine/MXCryptoMachine.swift index 3b384051d0..5760e13258 100644 --- a/MatrixSDK/Crypto/CryptoMachine/MXCryptoMachine.swift +++ b/MatrixSDK/Crypto/CryptoMachine/MXCryptoMachine.swift @@ -611,11 +611,12 @@ extension MXCryptoMachine: MXCryptoCrossSigning { } } - func importCrossSigningKeys(export: CrossSigningKeyExport) { + func importCrossSigningKeys(export: CrossSigningKeyExport) throws { do { try machine.importCrossSigningKeys(export: export) } catch { log.error("Failed importing cross signing keys", context: error) + throw error } } diff --git a/MatrixSDK/Crypto/CryptoMachine/MXCryptoProtocols.swift b/MatrixSDK/Crypto/CryptoMachine/MXCryptoProtocols.swift index b9b4dabc75..384b2aca2b 100644 --- a/MatrixSDK/Crypto/CryptoMachine/MXCryptoProtocols.swift +++ b/MatrixSDK/Crypto/CryptoMachine/MXCryptoProtocols.swift @@ -89,7 +89,7 @@ protocol MXCryptoCrossSigning: MXCryptoUserIdentitySource, MXCryptoDevicesSource func crossSigningStatus() -> CrossSigningStatus func bootstrapCrossSigning(authParams: [AnyHashable: Any]) async throws func exportCrossSigningKeys() -> CrossSigningKeyExport? - func importCrossSigningKeys(export: CrossSigningKeyExport) + func importCrossSigningKeys(export: CrossSigningKeyExport) throws func queryMissingSecretsFromOtherSessions() async throws } diff --git a/MatrixSDK/Crypto/Data/MXDeviceListOperation.h b/MatrixSDK/Crypto/Data/MXDeviceListOperation.h deleted file mode 100644 index b3f333c16e..0000000000 --- a/MatrixSDK/Crypto/Data/MXDeviceListOperation.h +++ /dev/null @@ -1,77 +0,0 @@ -/* - Copyright 2017 Vector Creations 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. - */ - -#import "MXSDKOptions.h" - -#ifdef MX_CRYPTO - -#import "MXHTTPOperation.h" - -#import "MXDeviceInfo.h" -#import "MXCryptoConstants.h" -#import "MXUsersDevicesMap.h" - -@class MXDeviceListOperationsPool; - -/** - `MXDeviceListOperation` is a hack over `MXHTTPOperation` that allows to have several - `MXDeviceListOperation` instances that point to the same `MXHTTPOperation` instance - managed by a `MXDeviceListOperationsPool` instance. - - This `MXHTTPOperation` will be cancelled only if all its children of `MXDeviceListOperationsPool` - are cancelled. - - `MXDeviceListOperation` exposes the same interface as `MXHTTPOperation` to not break - the operations chaining through the sdk. Especially, it has a cancel method. - */ -@interface MXDeviceListOperation : MXHTTPOperation - -/** - The users targeted for this operation. - */ -@property (nonatomic, readonly) NSArray *userIds; - -/** - The block called in case of success. - */ -@property (nonatomic, readonly) void (^success)(NSArray *succeededUserIds, NSArray *failedUserIds); - -/** -The block called in case of failure. - */ -@property (nonatomic, readonly) void (^failure)(NSError *error); - -/** - Create a `MXDeviceListOperation` instance - - @param userIds users targeted for this operation. - @param success the block called in case of success. - @param failure the block called in case of failure. - */ -- (id)initWithUserIds:(NSArray*)userIds - success:(void (^)(NSArray *succeededUserIds, NSArray *failedUserIds))success - failure:(void (^)(NSError *error))failure; - -/** - Move the operation into a pool - - @param pool the `MXDeviceListOperationsPool` instance it will belong to. - */ -- (void)addToPool:(MXDeviceListOperationsPool*)pool; - -@end - -#endif diff --git a/MatrixSDK/Crypto/Data/MXDeviceListOperation.m b/MatrixSDK/Crypto/Data/MXDeviceListOperation.m deleted file mode 100644 index 1626e481af..0000000000 --- a/MatrixSDK/Crypto/Data/MXDeviceListOperation.m +++ /dev/null @@ -1,110 +0,0 @@ -/* - Copyright 2017 Vector Creations 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. - */ - -#import "MXDeviceListOperation.h" - -#ifdef MX_CRYPTO - -#import "MXDeviceListOperationsPool.h" - -@interface MXDeviceListOperation () -{ - __weak MXDeviceListOperationsPool *pool; -} - -@end - -@implementation MXDeviceListOperation - -- (id)initWithUserIds:(NSArray*)userIds - success:(void (^)(NSArray *succeededUserIds, NSArray *failedUserIds))success - failure:(void (^)(NSError *error))failure -{ - self = [super init]; - if (self) - { - _userIds = userIds; - _success = success; - _failure = failure; - } - return self; -} - -- (void)addToPool:(MXDeviceListOperationsPool *)thePool -{ - NSParameterAssert(!pool); - - MXLogDebug(@"[MXDeviceListOperation] addToPool: add operation: %p to pool %p", self, thePool); - - pool = thePool; - [pool addOperation:self]; -} - -- (void)cancel -{ - [pool removeOperation:self]; -} - -#pragma mark - MXHTTPOperation methods forwarding - -- (NSURLSessionDataTask *)operation -{ - return pool.httpOperation.operation; -} - -- (void)setOperation:(NSURLSessionDataTask *)operation -{ - pool.httpOperation.operation = operation; -} - -- (NSUInteger)age -{ - return pool.httpOperation.age; -} - -- (NSUInteger)numberOfTries -{ - return pool.httpOperation.numberOfTries; -} - -- (void)setNumberOfTries:(NSUInteger)numberOfTries -{ - pool.httpOperation.numberOfTries = numberOfTries; -} - -- (NSUInteger)maxNumberOfTries -{ - return pool.httpOperation.maxNumberOfTries; -} - -- (void)setMaxNumberOfTries:(NSUInteger)maxNumberOfTries -{ - pool.httpOperation.maxNumberOfTries = maxNumberOfTries; -} - -- (NSUInteger)maxRetriesTime -{ - return pool.httpOperation.maxRetriesTime; -} - -- (void)setMaxRetriesTime:(NSUInteger)maxRetriesTime -{ - pool.httpOperation.maxRetriesTime = maxRetriesTime; -} - -@end - -#endif diff --git a/MatrixSDK/Crypto/Data/MXDeviceListOperationsPool.h b/MatrixSDK/Crypto/Data/MXDeviceListOperationsPool.h deleted file mode 100644 index 7cae5b86ab..0000000000 --- a/MatrixSDK/Crypto/Data/MXDeviceListOperationsPool.h +++ /dev/null @@ -1,79 +0,0 @@ -/* - Copyright 2017 Vector Creations 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. - */ - - -#import "MXSDKOptions.h" - -#ifdef MX_CRYPTO - -#import "MXDeviceListOperation.h" - -@class MXLegacyCrypto; - -/** - `MXDeviceListOperationsPool` manages a pool of `MXDeviceListOperation` operations - in order to gather keys downloads into one single `MXHTTPOperation` query. - */ -@interface MXDeviceListOperationsPool : NSObject - -/** - The pool of operations. - */ -@property (nonatomic, readonly) NSMutableArray *operations; - -/** - The current http request. - */ -@property (nonatomic, readonly) MXHTTPOperation *httpOperation; - -/** - The list of users targetted by sub operations. - */ -@property (nonatomic, readonly) NSSet *userIds; - - -/** - Create a `MXDeviceListOperation` instance - - @param crypto the crypto module. - */ - -- (id)initWithCrypto:(MXLegacyCrypto *)crypto; - -/** - Add/Remove an operation to/from the pool. - - @param operation the operation. - */ -- (void)addOperation:(MXDeviceListOperation *)operation; -- (void)removeOperation:(MXDeviceListOperation *)operation; - -/** - Check if the pool will download keys these users. - - @param userIds an array of users. - @return YES if the pool contains all users. - */ -- (BOOL)hasUsers:(NSArray*)userIds; - -/** - Launch the download request for all users identified by all MXDeviceListOperation children. - */ -- (void)downloadKeys:(NSString *)token complete:(void (^)(NSDictionary *failedUserIds))complete; - -@end - -#endif diff --git a/MatrixSDK/Crypto/Data/MXDeviceListOperationsPool.m b/MatrixSDK/Crypto/Data/MXDeviceListOperationsPool.m deleted file mode 100644 index 6b54fb3ada..0000000000 --- a/MatrixSDK/Crypto/Data/MXDeviceListOperationsPool.m +++ /dev/null @@ -1,405 +0,0 @@ -/* - Copyright 2017 Vector Creations 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. - */ - -#import "MXDeviceListOperationsPool.h" - -#ifdef MX_CRYPTO - -#import "MXCrypto_Private.h" -#import "MXCrossSigning_Private.h" -#import "MXDeviceInfo_Private.h" -#import "MXCrossSigningInfo_Private.h" -#import "MXTools.h" -#import "MatrixSDKSwiftHeader.h" - - -#pragma mark - Constants definitions - -// Max number of user to request in /keys/query requests -static NSUInteger const kMXDeviceListOperationsPoolKeyQueryLimit = 250; - - -@interface MXDeviceListOperationsPool () -{ - __weak MXLegacyCrypto *crypto; -} -@end - -@implementation MXDeviceListOperationsPool - -- (id)initWithCrypto:(MXLegacyCrypto *)theCrypto -{ - self = [super init]; - if (self) - { - crypto = theCrypto; - _operations = [NSMutableArray array]; - } - return self; -} - -- (NSSet *)userIds -{ - NSMutableSet *userIds = [NSMutableSet set]; - for (MXDeviceListOperation *operation in _operations) - { - [userIds addObjectsFromArray:operation.userIds]; - } - return userIds; -} - -- (void)cancel -{ - [_httpOperation cancel]; - _httpOperation = nil; -} - -- (void)addOperation:(MXDeviceListOperation *)operation -{ - // If the request is already made, we can only accept operations - // for users we made the request - NSParameterAssert(_httpOperation == nil - || [self hasUsers:operation.userIds]); - - if (![_operations containsObject:operation]) - { - [_operations addObject:operation]; - } -} - -- (void)removeOperation:(MXDeviceListOperation *)operation -{ - [_operations removeObject:operation]; - - if (_operations.count == 0) - { - [self cancel]; - } -} - -- (BOOL)hasUsers:(NSArray*)userIds -{ - return [[NSSet setWithArray:userIds] isSubsetOfSet:self.userIds]; -} - -- (void)downloadKeys:(NSString *)token complete:(void (^)(NSDictionary *failedUserIds))complete -{ - [self doKeyDownloadForUsers:self.userIds.allObjects token:token complete:complete]; -} - -- (void)doKeyDownloadForUsers:(NSArray *)users token:(NSString *)token complete:(void (^)(NSDictionary *failedUserIds))complete -{ - MXLogDebug(@"[MXDeviceListOperationsPool] doKeyDownloadForUsers(pool: %p) %@ users: %@", self, @(users.count), users); - - // Download - MXWeakify(self); - _httpOperation = [crypto.matrixRestClient downloadKeysByChunkForUsers:users token:token chunkSize:kMXDeviceListOperationsPoolKeyQueryLimit success:^(MXKeysQueryResponse *keysQueryResponse) { - MXStrongifyAndReturnIfNil(self); - - MXLogDebug(@"[MXDeviceListOperationsPool] doKeyDownloadForUsers(pool: %p) -> DONE. Got keys for %@ users and %@ devices. Got cross-signing keys for %@ users", self, @(keysQueryResponse.deviceKeys.map.count), @(keysQueryResponse.deviceKeys.count), @(keysQueryResponse.crossSigningKeys.count)); - - self->_httpOperation = nil; - - NSMutableDictionary*> *usersDevices = [NSMutableDictionary new]; - NSMutableDictionary*> *updatedUsersDevices = [NSMutableDictionary new]; - - BOOL myUserCrossSigningKeysChanged = NO; - - for (NSString *userId in users) - { - // Handle user cross-signing keys - MXCrossSigningInfo *crossSigningKeys = keysQueryResponse.crossSigningKeys[userId]; - if (crossSigningKeys) - { - MXLogDebug(@"[MXDeviceListOperationsPool] doKeyDownloadForUsers: Got cross-signing keys for %@: %@", userId, crossSigningKeys); - - MXCrossSigningInfo *storedCrossSigningKeys = [self->crypto.store crossSigningKeysForUser:userId]; - - // Detect rotation in my user cross-signing keys - if (storedCrossSigningKeys - && [self->crypto.mxSession.myUserId isEqualToString:userId] - && ![storedCrossSigningKeys hasSameKeysAsCrossSigningInfo:crossSigningKeys]) - { - // Cross-signing keys have been reset from another device - MXLogDebug(@"[MXDeviceListOperationsPool] doKeyDownloadForUsers: Detected cross-signing keys rotation"); - myUserCrossSigningKeysChanged = YES; - } - - // Use current trust level - MXUserTrustLevel *oldTrustLevel = storedCrossSigningKeys.trustLevel; - if (myUserCrossSigningKeysChanged) - { - // Except if we cannot trust it anymore - oldTrustLevel = [MXUserTrustLevel new]; - } - - [crossSigningKeys setTrustLevel:oldTrustLevel]; - - // Compute trust on this user - // Note this overwrites the previous value - BOOL isCrossSigningVerified = [self.crossSigning isUserWithCrossSigningKeysVerified:crossSigningKeys]; - MXUserTrustLevel *newTrustLevel = [MXUserTrustLevel trustLevelWithCrossSigningVerified:isCrossSigningVerified - locallyVerified:oldTrustLevel.isLocallyVerified]; - - [crossSigningKeys updateTrustLevel:newTrustLevel]; - - // Note that keys which aren't in the response will be removed from the store - [self->crypto.store storeCrossSigningKeys:crossSigningKeys]; - } - - - // Handle user devices keys - NSDictionary *devices = keysQueryResponse.deviceKeys.map[userId]; - - MXLogDebug(@"[MXDeviceListOperationsPool] doKeyDownloadForUsers: Got keys for %@: %@ devices: %@", userId, @(devices.count), devices); - - if (devices) - { - NSMutableDictionary *mutabledevices = [NSMutableDictionary dictionaryWithDictionary:devices]; - - NSDictionary *storedDevices = [self->crypto.store devicesForUser:userId]; - - for (NSString *deviceId in mutabledevices.allKeys) - { - // Get the potential previously store device keys for this device - MXDeviceInfo *previouslyStoredDeviceKeys = storedDevices[deviceId]; - - MXDeviceVerification previousLocalState = MXDeviceUnknown; - - // Validate received keys - if (![self validateDeviceKeys:mutabledevices[deviceId] forUser:userId andDevice:deviceId previouslyStoredDeviceKeys:previouslyStoredDeviceKeys]) - { - // New device keys are not valid. Do not store them - [mutabledevices removeObjectForKey:deviceId]; - - if (previouslyStoredDeviceKeys) - { - // But keep old validated ones if any - mutabledevices[deviceId] = previouslyStoredDeviceKeys; - } - } - else if (previouslyStoredDeviceKeys) - { - // The verified status is not sync'ed with hs. - // This is a client side information, valid only for this client. - // So, transfer its previous value - previousLocalState = previouslyStoredDeviceKeys.trustLevel.localVerificationStatus; - } - - // Be sure to force previous local state to verified for current device. Our own device is always locally verified. - if ([self->crypto.mxSession.myUserId isEqualToString:userId] - && [self->crypto.mxSession.myDeviceId isEqualToString:deviceId]) - { - previousLocalState = MXDeviceVerified; - } - - // Use current trust level - MXDeviceTrustLevel *oldTrustLevel = [MXDeviceTrustLevel trustLevelWithLocalVerificationStatus:previousLocalState - crossSigningVerified:previouslyStoredDeviceKeys.trustLevel.isCrossSigningVerified]; - [mutabledevices[deviceId] setTrustLevel:oldTrustLevel]; - - - BOOL crossSigningVerified = [self.crossSigning isDeviceVerified:mutabledevices[deviceId]]; - MXDeviceTrustLevel *trustLevel = [MXDeviceTrustLevel trustLevelWithLocalVerificationStatus:previousLocalState - crossSigningVerified:crossSigningVerified]; - - [mutabledevices[deviceId] updateTrustLevel:trustLevel]; - } - - NSArray *mutableDevicesValues = mutabledevices.allValues; - usersDevices[userId] = mutableDevicesValues; - - if (![mutabledevices isEqualToDictionary:storedDevices]) - { - NSArray *storedDevicesValues = storedDevices.allValues; - - // Keep only devices that are not identical to those present in the database - NSArray *updatedUserDevices = [mutableDevicesValues filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) { - return ![storedDevicesValues containsObject:evaluatedObject]; - }]]; - - if (updatedUserDevices.count) - { - updatedUsersDevices[userId] = updatedUserDevices; - } - - // Update the store - // Note that devices which aren't in the response will be removed from the store - [self->crypto.store storeDevicesForUser:userId devices:mutabledevices]; - } - } - } - - if (updatedUsersDevices.count) - { - dispatch_async(dispatch_get_main_queue(), ^{ - // Post notification using MXCrypto instance as MXDeviceListOperationsPool is an internal class. - [[NSNotificationCenter defaultCenter] postNotificationName:MXDeviceListDidUpdateUsersDevicesNotification object:self->crypto userInfo:updatedUsersDevices]; - }); - } - - if (myUserCrossSigningKeysChanged) - { - MXLogDebug(@"[MXDeviceListOperationsPool] doKeyDownloadForUsers: Reset cross-signing state."); - [self->crypto.crossSigning refreshStateWithSuccess:nil failure:nil]; - } - - // Delay - dispatch_async(self->crypto.matrixRestClient.completionQueue, ^{ - - for (MXDeviceListOperation *operation in self.operations) - { - // Report the success to children - if (operation.success) - { - NSMutableArray *succeededUserIds = [NSMutableArray array]; - NSMutableArray *failedUserIds = [NSMutableArray array]; - - for (NSString *userId in operation.userIds) - { - // Check we got a response for this user - if (keysQueryResponse.deviceKeys.map[userId]) - { - [succeededUserIds addObject:userId]; - } - else - { - // TODO: do something with keysQueryResponse.failures - [failedUserIds addObject:userId]; - } - } - operation.success(succeededUserIds, failedUserIds); - } - } - - }); - - if (complete) - { - if (keysQueryResponse.failures.count) - { - MXLogDebug(@"[MXDeviceListOperationsPool] doKeyDownloadForUsers. Failures: %@", keysQueryResponse.failures); - } - complete(keysQueryResponse.failures); - } - - } failure:^(NSError *error) { - MXStrongifyAndReturnIfNil(self); - - self->_httpOperation = nil; - - MXLogDebug(@"[MXDeviceListOperationsPool] doKeyDownloadForUsers(pool: %p) -> FAILED. Error: %@", self, error); - - dispatch_async(self->crypto.matrixRestClient.completionQueue, ^{ - for (MXDeviceListOperation *operation in self.operations) - { - if (operation.failure) - { - operation.failure(error); - } - } - }); - - if (complete) - { - complete(nil); - } - - }]; -} - -/** - Validate device keys. - - @param deviceKeys the device keys to validate. - @param userId the id of the user of the device. - @param deviceId the id of the device. - @param previouslyStoredDeviceKeys the device keys we received before for this device - @return YES if valid. - */ -- (BOOL)validateDeviceKeys:(MXDeviceInfo*)deviceKeys forUser:(NSString*)userId andDevice:(NSString*)deviceId previouslyStoredDeviceKeys:(MXDeviceInfo*)previouslyStoredDeviceKeys -{ - if (!deviceKeys.keys) - { - // no keys? - return NO; - } - - // Check that the user_id and device_id in the received deviceKeys are correct - if (![deviceKeys.userId isEqualToString:userId]) - { - MXLogDebug(@"[MXDeviceListOperationsPool] validateDeviceKeys: Mismatched user_id %@ in keys from %@:%@", deviceKeys.userId, userId, deviceId); - return NO; - } - if (![deviceKeys.deviceId isEqualToString:deviceId]) - { - MXLogDebug(@"[MXDeviceListOperationsPool] validateDeviceKeys: Mismatched device_id %@ in keys from %@:%@", deviceKeys.deviceId, userId, deviceId); - return NO; - } - - NSString *signKeyId = [NSString stringWithFormat:@"ed25519:%@", deviceKeys.deviceId]; - NSString* signKey = deviceKeys.keys[signKeyId]; - if (!signKey) - { - MXLogDebug(@"[MXDeviceListOperationsPool] validateDeviceKeys: Device %@:%@ has no ed25519 key", userId, deviceKeys.deviceId); - return NO; - } - - NSString *signature = deviceKeys.signatures[userId][signKeyId]; - if (!signature) - { - MXLogDebug(@"[MXDeviceListOperationsPool] validateDeviceKeys: Device %@:%@ is not signed", userId, deviceKeys.deviceId); - return NO; - } - - NSError *error; - if (![crypto.olmDevice verifySignature:signKey JSON:deviceKeys.signalableJSONDictionary signature:signature error:&error]) - { - MXLogDebug(@"[MXDeviceListOperationsPool] validateDeviceKeys: Unable to verify signature on device %@:%@", userId, deviceKeys.deviceId); - return NO; - } - - if (previouslyStoredDeviceKeys) - { - if (![previouslyStoredDeviceKeys.fingerprint isEqualToString:signKey]) - { - // This should only happen if the list has been MITMed; we are - // best off sticking with the original keys. - // - // Should we warn the user about it somehow? - MXLogDebug(@"[MXDeviceListOperationsPool] validateDeviceKeys: WARNING:Ed25519 key for device %@:%@ has changed: %@ -> %@", userId, deviceKeys.deviceId, previouslyStoredDeviceKeys.fingerprint, signKey); - MXLogDebug(@"[MXDeviceListOperationsPool] validateDeviceKeys: %@ -> %@", previouslyStoredDeviceKeys, deviceKeys); - MXLogDebug(@"[MXDeviceListOperationsPool] validateDeviceKeys: %@ -> %@", previouslyStoredDeviceKeys.keys, deviceKeys.keys); - return NO; - } - } - - return YES; -} - -- (MXLegacyCrossSigning *)crossSigning -{ - if (![self->crypto.crossSigning isKindOfClass:[MXLegacyCrossSigning class]]) - { - MXLogFailure(@"[MXDeviceListOperationsPool] Using incompatible cross signing implementation, can only use legacy"); - return nil; - } - return (MXLegacyCrossSigning *)self->crypto.crossSigning; -} - -@end - -#endif diff --git a/MatrixSDK/Crypto/Data/MXOlmInboundGroupSession.h b/MatrixSDK/Crypto/Data/MXOlmInboundGroupSession.h deleted file mode 100644 index c7143a79fb..0000000000 --- a/MatrixSDK/Crypto/Data/MXOlmInboundGroupSession.h +++ /dev/null @@ -1,105 +0,0 @@ -/* - Copyright 2016 OpenMarket 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. - */ - -#import - -#import "MXSDKOptions.h" - -#ifdef MX_CRYPTO - -@class OLMInboundGroupSession; - -#import "MXMegolmSessionData.h" - -/** - The 'MXOlmInboundGroupSession' class adds more context to a OLMInboundGroupSession - object from OLMKit. - - This allows additional checks. - The class implements NSCoding so that OLMInboundGroupSession can be stored with its - context. - */ -@interface MXOlmInboundGroupSession : NSObject - -/** - Initialise the underneath olm inbound group session. - - @param sessionKey the session key. - */ -- (instancetype)initWithSessionKey:(NSString*)sessionKey; - -/** - The associated olm inbound group session. - */ -@property (nonatomic, readonly) OLMInboundGroupSession *session; - -/** - The room in which this session is used. - */ -@property (nonatomic) NSString *roomId; - -/** - The base64-encoded curve25519 key of the sender. - */ -@property (nonatomic) NSString *senderKey; - -/** - Devices which forwarded this session to us. - */ -@property NSArray *forwardingCurve25519KeyChain; - -/** - Other keys the sender claims. - */ -@property (nonatomic) NSDictionary *keysClaimed; - -/** - Flag indicating whether the history of this room is considered as shared. - - This is typically the case if room's `historyVisibility` is set to `world_readable` or `shared`. - In this case the keys are allowed to be shared with other users upon invite. - */ -@property (nonatomic) BOOL sharedHistory; - -/** - Flag indicating this session is untrusted or not. - */ -@property (nonatomic, getter=isUntrusted) BOOL untrusted; - - -#pragma mark - import/export - -/** - Export the session data from a given message. - - @param messageIndex the index of message from which to export the session. - @return the exported data. - */ -- (MXMegolmSessionData *)exportSessionDataAtMessageIndex:(NSUInteger)messageIndex; - -/** - Export the session data from the first known message. - - @return the exported data. - */ -- (MXMegolmSessionData *)exportSessionData; - -- (instancetype)initWithImportedSessionData:(MXMegolmSessionData*)data; -- (instancetype)initWithImportedSessionKey:(NSString*)sessionKey; - -@end - -#endif // MX_CRYPTO diff --git a/MatrixSDK/Crypto/Data/MXOlmInboundGroupSession.m b/MatrixSDK/Crypto/Data/MXOlmInboundGroupSession.m deleted file mode 100644 index 1f2ec6d96d..0000000000 --- a/MatrixSDK/Crypto/Data/MXOlmInboundGroupSession.m +++ /dev/null @@ -1,141 +0,0 @@ -/* - Copyright 2016 OpenMarket 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. - */ - -#import "MXOlmInboundGroupSession.h" - -#ifdef MX_CRYPTO - -#import -#import "MXCryptoConstants.h" - -@implementation MXOlmInboundGroupSession - -- (instancetype)initWithSessionKey:(NSString *)sessionKey -{ - self = [self init]; - if (self) - { - _session = [[OLMInboundGroupSession alloc] initInboundGroupSessionWithSessionKey:sessionKey error:nil]; - if (!_session) - { - return nil; - } - } - return self; -} - - -#pragma mark - import/export -- (MXMegolmSessionData *)exportSessionDataAtMessageIndex:(NSUInteger)messageIndex -{ - MXMegolmSessionData *sessionData; - - NSError *error; - NSString *sessionKey = [_session exportSessionAtMessageIndex:messageIndex error:&error]; - - if (!error) - { - sessionData = [[MXMegolmSessionData alloc] init]; - - sessionData.senderKey = _senderKey; - sessionData.forwardingCurve25519KeyChain = _forwardingCurve25519KeyChain; - sessionData.senderClaimedKeys = _keysClaimed; - sessionData.roomId = _roomId; - sessionData.sessionId = _session.sessionIdentifier; - sessionData.sessionKey = sessionKey; - sessionData.algorithm = kMXCryptoMegolmAlgorithm; - sessionData.sharedHistory = _sharedHistory; - sessionData.untrusted = _untrusted; - } - else - { - MXLogDebug(@"[MXOlmInboundGroupSession] exportSessionData: Cannot export session with id %@-%@. Error: %@", _session.sessionIdentifier, _senderKey, error); - } - - return sessionData; -} - -- (MXMegolmSessionData *)exportSessionData -{ - return [self exportSessionDataAtMessageIndex:_session.firstKnownIndex]; -} - -- (instancetype)initWithImportedSessionKey:(NSString *)sessionKey -{ - self = [self init]; - if (self) - { - NSError *error; - _session = [[OLMInboundGroupSession alloc] initInboundGroupSessionWithImportedSession:sessionKey error:&error]; - if (!_session) - { - MXLogDebug(@"[MXOlmInboundGroupSession] initWithImportedSessionKey failed. Error: %@", error); - return nil; - } - } - - return self; -} - -- (instancetype)initWithImportedSessionData:(MXMegolmSessionData *)data -{ - self = [self initWithImportedSessionKey:data.sessionKey]; - if (self) - { - _senderKey = data.senderKey; - _forwardingCurve25519KeyChain = data.forwardingCurve25519KeyChain; - _keysClaimed = data.senderClaimedKeys; - _roomId = data.roomId; - _sharedHistory = data.sharedHistory; - _untrusted = data.isUntrusted; - } - return self; -} - - -#pragma mark - NSCoding -- (instancetype)initWithCoder:(NSCoder *)aDecoder -{ - self = [super init]; - if (self) - { - _session = [aDecoder decodeObjectForKey:@"session"]; - _roomId = [aDecoder decodeObjectForKey:@"roomId"]; - _senderKey = [aDecoder decodeObjectForKey:@"senderKey"]; - _forwardingCurve25519KeyChain = [aDecoder decodeObjectForKey:@"forwardingCurve25519KeyChain"]; - _keysClaimed = [aDecoder decodeObjectForKey:@"keysClaimed"]; - _sharedHistory = [[aDecoder decodeObjectForKey:@"sharedHistory_v2"] boolValue]; - // if "untrusted" is not encoded, mark it as untrusted - _untrusted = [aDecoder containsValueForKey:@"untrusted"] ? [aDecoder decodeBoolForKey:@"untrusted"] : YES; - } - return self; -} - -- (void)encodeWithCoder:(NSCoder *)aCoder -{ - [aCoder encodeObject:_session forKey:@"session"]; - [aCoder encodeObject:_roomId forKey:@"roomId"]; - [aCoder encodeObject:_senderKey forKey:@"senderKey"]; - [aCoder encodeObject:_keysClaimed forKey:@"keysClaimed"]; - [aCoder encodeObject:_forwardingCurve25519KeyChain forKey:@"forwardingCurve25519KeyChain"]; - [aCoder encodeObject:@(_sharedHistory) forKey:@"sharedHistory_v2"]; - [aCoder encodeBool:_untrusted forKey:@"untrusted"]; -} - -@end - -#endif - diff --git a/MatrixSDK/Crypto/Data/MXOlmOutboundGroupSession.h b/MatrixSDK/Crypto/Data/MXOlmOutboundGroupSession.h deleted file mode 100644 index 936d21219c..0000000000 --- a/MatrixSDK/Crypto/Data/MXOlmOutboundGroupSession.h +++ /dev/null @@ -1,77 +0,0 @@ -// -// Copyright 2021 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 - -@class OLMOutboundGroupSession; - -@interface MXOlmOutboundGroupSession : NSObject - -/** - Initialise the underneath olm inbound group session. - - @param session the associated session instance. - @param roomId The ID room in which this session is used. - @param creationTime Timestamp of the creation of the session - */ -- (instancetype)initWithSession:(OLMOutboundGroupSession *)session roomId:(NSString *)roomId creationTime:(NSTimeInterval) creationTime; - -/** - The associated olm outbound group session. - */ -@property (nonatomic, readonly) OLMOutboundGroupSession *session; - -/** - the ID of the current session. - */ -@property (nonatomic, readonly) NSString *sessionId; - -/** - the key of the current session. - */ -@property (nonatomic, readonly) NSString *sessionKey; - -/** - the message index of the current session. - */ -@property (nonatomic, readonly) NSUInteger messageIndex; - -/** - The room in which this session is used. - */ -@property (nonatomic, readonly) NSString *roomId; - -/** - Timestamp of the creation of the session - */ -@property (nonatomic, readonly) NSTimeInterval creationTime; - -/** - NSDate related to creationTime. - */ -@property (nonatomic, readonly) NSDate *creationDate; - -/** - Encrypt a given text message using the current session. - - @param message text message to be encrypted - @param error instance of an NSError if an error occured. - - @return the encrypted message. Nil if error occured. - */ -- (NSString *)encryptMessage:(NSString *)message error:(NSError**)error; - -@end diff --git a/MatrixSDK/Crypto/Data/MXOlmOutboundGroupSession.m b/MatrixSDK/Crypto/Data/MXOlmOutboundGroupSession.m deleted file mode 100644 index c61cce7d89..0000000000 --- a/MatrixSDK/Crypto/Data/MXOlmOutboundGroupSession.m +++ /dev/null @@ -1,60 +0,0 @@ -// -// Copyright 2021 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 "MXOlmOutboundGroupSession.h" - -#import - -@implementation MXOlmOutboundGroupSession - -- (instancetype)initWithSession:(OLMOutboundGroupSession *)session roomId:(NSString *)roomId creationTime:(NSTimeInterval) creationTime -{ - self = [self init]; - if (self) - { - _session = session; - _roomId = roomId; - _creationTime = creationTime; - } - return self; -} - -- (NSString *)sessionId -{ - return _session.sessionIdentifier; -} - -- (NSString *)sessionKey -{ - return _session.sessionKey; -} - -- (NSUInteger)messageIndex -{ - return _session.messageIndex; -} - -- (NSDate *)creationDate -{ - return [NSDate dateWithTimeIntervalSince1970:_creationTime]; -} - -- (NSString *)encryptMessage:(NSString *)message error:(NSError**)error -{ - return [_session encryptMessage:message error:error]; -} - -@end diff --git a/MatrixSDK/Crypto/Data/MXOlmSession.m b/MatrixSDK/Crypto/Data/MXOlmSession.m index 4b4c0a867d..d6f1548f73 100644 --- a/MatrixSDK/Crypto/Data/MXOlmSession.m +++ b/MatrixSDK/Crypto/Data/MXOlmSession.m @@ -15,7 +15,6 @@ */ #import "MXOlmSession.h" -#import @implementation MXOlmSession diff --git a/MatrixSDK/Crypto/Data/MXOutboundSessionInfo.h b/MatrixSDK/Crypto/Data/MXOutboundSessionInfo.h deleted file mode 100644 index 3b6ee1207a..0000000000 --- a/MatrixSDK/Crypto/Data/MXOutboundSessionInfo.h +++ /dev/null @@ -1,75 +0,0 @@ -// -// Copyright 2021 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 - -#import "MXTools.h" -#import "MXOlmOutboundGroupSession.h" -#import "MXHTTPOperation.h" - -@interface MXOutboundSessionInfo : NSObject -{ - // When the session was created - NSDate *creationTime; -} - -- (instancetype)initWithSession:(MXOlmOutboundGroupSession *)session; - -/** - Check if it's time to rotate the session. - - @param rotationPeriodMsgs the max number of encryptions before rotating. - @param rotationPeriodMs the max duration of an encryption session before rotating. - @return YES if rotation is needed. - */ -- (BOOL)needsRotation:(NSUInteger)rotationPeriodMsgs rotationPeriodMs:(NSUInteger)rotationPeriodMs; - -/** - Determine if this session has been shared with devices which it shouldn't - have been. - - @param devicesInRoom userId -> {deviceId -> object} devices we should shared the session with. - @return YES if we have shared the session with devices which aren't in devicesInRoom. - */ -- (BOOL)sharedWithTooManyDevices:(MXUsersDevicesMap *)devicesInRoom; - -/** - The related session - */ -@property (nonatomic, readonly) MXOlmOutboundGroupSession *session; - -/** - The id of the session - */ -@property (nonatomic, readonly) NSString *sessionId; - -/** - Number of times this session has been used - */ -@property (nonatomic) NSUInteger useCount; - -/** - If a share operation is in progress, the corresponding http request - */ -@property (nonatomic) MXHTTPOperation* shareOperation; - -/** - Devices with which we have shared the session key - userId -> {deviceId -> msgindex} - */ -@property (nonatomic) MXUsersDevicesMap *sharedWithDevices; - -@end diff --git a/MatrixSDK/Crypto/Data/MXOutboundSessionInfo.m b/MatrixSDK/Crypto/Data/MXOutboundSessionInfo.m deleted file mode 100644 index 92c4946fcd..0000000000 --- a/MatrixSDK/Crypto/Data/MXOutboundSessionInfo.m +++ /dev/null @@ -1,71 +0,0 @@ -// -// Copyright 2020 The Matrix.org Foundation C.I.C -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#import "MXOutboundSessionInfo.h" - -@implementation MXOutboundSessionInfo - -- (instancetype)initWithSession:(MXOlmOutboundGroupSession *)session -{ - self = [super init]; - if (self) - { - _sessionId = session.sessionId; - _session = session; - creationTime = session.creationDate; - _sharedWithDevices = [[MXUsersDevicesMap alloc] init]; - } - return self; -} - -- (BOOL)needsRotation:(NSUInteger)rotationPeriodMsgs rotationPeriodMs:(NSUInteger)rotationPeriodMs -{ - BOOL needsRotation = NO; - NSUInteger sessionLifetime = [[NSDate date] timeIntervalSinceDate:creationTime] * 1000; - - if (_useCount >= rotationPeriodMsgs || sessionLifetime >= rotationPeriodMs) - { - MXLogDebug(@"[MXOutboundSessionInfo] Rotating megolm session after %tu messages, %tu ms", _useCount, sessionLifetime); - needsRotation = YES; - } - - return needsRotation; -} - -- (BOOL)sharedWithTooManyDevices:(MXUsersDevicesMap *)devicesInRoom -{ - for (NSString *userId in _sharedWithDevices.userIds) - { - if (![devicesInRoom deviceIdsForUser:userId]) - { - MXLogDebug(@"[MXOutboundSessionInfo] Starting new session because we shared with %@", userId); - return YES; - } - - for (NSString *deviceId in [_sharedWithDevices deviceIdsForUser:userId]) - { - if (! [devicesInRoom objectForDevice:deviceId forUser:userId]) - { - MXLogDebug(@"[MXOutboundSessionInfo] Starting new session because we shared with %@:%@", userId, deviceId); - return YES; - } - } - } - - return NO; -} - -@end diff --git a/MatrixSDK/Crypto/Data/Store/MXCryptoSecretStore.h b/MatrixSDK/Crypto/Data/Store/MXCryptoSecretStore.h index ba6fbc354e..27fa974157 100644 --- a/MatrixSDK/Crypto/Data/Store/MXCryptoSecretStore.h +++ b/MatrixSDK/Crypto/Data/Store/MXCryptoSecretStore.h @@ -31,7 +31,7 @@ NS_ASSUME_NONNULL_BEGIN @param secret the secret. @param secretId the id of the secret. */ -- (void)storeSecret:(NSString *)secret withSecretId:(NSString *)secretId; +- (void)storeSecret:(NSString *)secret withSecretId:(NSString *)secretId errorHandler:(void (^)(NSError *error))errorHandler; /** Check if a given secret is stored diff --git a/MatrixSDK/Crypto/Data/Store/MXCryptoStore.h b/MatrixSDK/Crypto/Data/Store/MXCryptoStore.h deleted file mode 100644 index aa2206d5d4..0000000000 --- a/MatrixSDK/Crypto/Data/Store/MXCryptoStore.h +++ /dev/null @@ -1,603 +0,0 @@ -/* - Copyright 2016 OpenMarket Ltd - Copyright 2017 Vector Creations 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. - */ - -#import - -#import "MXSDKOptions.h" - -#ifdef MX_CRYPTO - -#import "MXJSONModels.h" -#import "MXCredentials.h" -#import "MXCryptoVersion.h" -#import "MXOlmSession.h" -#import "MXOlmInboundGroupSession.h" -#import "MXOlmOutboundGroupSession.h" -#import "MXDeviceInfo.h" -#import "MXCrossSigningInfo.h" -#import "MXOutgoingRoomKeyRequest.h" -#import "MXIncomingRoomKeyRequest.h" -#import "MXCryptoSecretStore.h" - -@class OLMAccount; -@class OLMOutboundGroupSession; -@class MXRoomSettings; - -/** - The `MXCryptoStore` protocol defines an interface that must be implemented in order to store - crypto data for a matrix account. - */ -@protocol MXCryptoStore - -/** - Indicate if the store contains data for the passed account. - YES means that the user enabled the crypto in a previous sesison. - */ -+ (BOOL)hasDataForCredentials:(MXCredentials*)credentials; - -/** - Create a crypto store for the passed credentials. - - @param credentials the credentials of the account. - @return the ready to use store. - */ -+ (instancetype)createStoreWithCredentials:(MXCredentials*)credentials; - -/** - Delete the crypto store for the passed credentials. Implementation should also attempt to delete read-only store. - - @param credentials the credentials of the account. - */ -+ (void)deleteStoreWithCredentials:(MXCredentials*)credentials; - -/** - Delete crypto stores for all users. Implementations should also attempt to delete read-only stores. - */ -+ (void)deleteAllStores; - -/** - Delete the read-only crypto store for the passed credentials. - - @param credentials the credentials of the account. - */ -+ (void)deleteReadonlyStoreWithCredentials:(MXCredentials*)credentials; - -/** - Create a crypto store for the passed credentials. - - @param credentials the credentials of the account. - @return the store. Call the open method before using it. - */ -- (instancetype)initWithCredentials:(MXCredentials *)credentials; - -/** - The user id. - */ -- (NSString*)userId; - -/** - Store the device id. - */ -- (void)storeDeviceId:(NSString*)deviceId; - -/** - The device id. - */ -- (NSString*)deviceId; - -/** - Store the user olm account for this device. - - This method MUST be used only on setup to store a new olm account. - */ -- (void)setAccount:(OLMAccount*)account; - -/** - The user olm account for this device. - - This is safe to use the returned for read-only olm operation. - */ -- (OLMAccount*)account; - -/** - Perform an action that will advance the olm account state. - - Some cryptographic operations update the internal state of the olm account. They must be executed - into this method to make those operations atomic. This method stores the new olm account state - when the block retuns, - The implementation must call the block before returning. It must be multi-thread and multi-process safe. - - @param block the block where olm operations can be safely made. - */ -- (void)performAccountOperationWithBlock:(void (^)(OLMAccount *olmAccount))block; - -/** - Store the sync token corresponding to the device list. - - This is used when starting the client, to get a list of the users who - have changed their device list since the list time we were running. - - @param deviceSyncToken the token. - */ -- (void)storeDeviceSyncToken:(NSString*)deviceSyncToken; - -/** - Get the sync token corresponding to the device list. - - @return the token. - */ -- (NSString*)deviceSyncToken; - -/** - Store a device for a user. - - @param userId the user's id. - @param device the device to store. - */ -- (void)storeDeviceForUser:(NSString*)userId device:(MXDeviceInfo*)device; - -/** - Retrieve a device for a user. - - @param deviceId the device id. - @param userId the user's id. - @return The device. - */ -- (MXDeviceInfo*)deviceWithDeviceId:(NSString*)deviceId forUser:(NSString*)userId; - -/** - Retrieve a device by its identity key. - - @param identityKey the device identity key (`MXDeviceInfo.identityKey`)/ - @return The device. - */ -- (MXDeviceInfo*)deviceWithIdentityKey:(NSString*)identityKey; - -/** - Store the known devices for a user. - - @param userId The user's id. - @param devices A map from device id to 'MXDevice' object for the device. - */ -- (void)storeDevicesForUser:(NSString*)userId devices:(NSDictionary*)devices; - -/** - Retrieve the known devices for a user. - - @param userId The user's id. - @return A map from device id to 'MXDevice' object for the device or nil if we haven't - managed to get a list of devices for this user yet. - */ -- (NSDictionary*)devicesForUser:(NSString*)userId; - -/** - The device tracking status. - - @return A map from user id to MXDeviceTrackingStatus. - */ -- (NSDictionary*)deviceTrackingStatus; - -/** - Store the device tracking status. - - @param statusMap A map from user id to MXDeviceTrackingStatus. - */ -- (void)storeDeviceTrackingStatus:(NSDictionary*)statusMap; - - -#pragma mark - Cross-signing keys - -/** - Store cross signing keys for a user. - - @param crossSigningInfo The user's cross signing keys. - */ -- (void)storeCrossSigningKeys:(MXCrossSigningInfo*)crossSigningInfo; - -/** - Retrieve the cross signing keys for a user. - - @param userId The user's id. - @return the cross signing keys. - */ -- (MXCrossSigningInfo*)crossSigningKeysForUser:(NSString*)userId; - -/** - Return all cross-signing keys we know about. - - @return all cross signing keys. - */ -- (NSArray *)crossSigningKeys; - -#pragma mark - Secrets - -/** - Delete a secret. - - @param secretId the id of the secret. - */ -- (void)deleteSecretWithSecretId:(NSString *)secretId; - -#pragma mark - Message keys - -/** - Store the crypto algorithm for a room. - - @param roomId the id of the room. - @algorithm the algorithm. - */ -- (void)storeAlgorithmForRoom:(NSString*)roomId algorithm:(NSString*)algorithm; - -/** - The crypto algorithm used in a room. - nil if the room is not encrypted. - */ -- (NSString*)algorithmForRoom:(NSString*)roomId; - -/** - Fetch all stored room settings, containing room algorithm and other crypto options - */ -- (NSArray *)roomSettings; - -/** - Store a session between this device and another device. - - @param session the end-to-end session. - */ -- (void)storeSession:(MXOlmSession*)session; - -/** - Retrieve an end-to-end session between this device and another device. - - @param deviceKey the public key of the other device. - @param sessionId the session id. - - @return the end-to-end session. - */ -- (MXOlmSession*)sessionWithDevice:(NSString*)deviceKey andSessionId:(NSString*)sessionId; - -/** - Perform an action that will advance the passed end-to-end session. - - See performAccountOperationWithBlock for more details. - - @param deviceKey the public key of the other device. - @param sessionId the session id. - @param block the block where olm session operations can be safely made. - */ -- (void)performSessionOperationWithDevice:(NSString*)deviceKey andSessionId:(NSString*)sessionId block:(void (^)(MXOlmSession *mxOlmSession))block; - -/** - Retrieve all end-to-end sessions between this device and another - device sorted by `lastReceivedMessageTs`, the most recent(higest value) first. - - @param deviceKey the public key of the other device. - @return a array of end-to-end sessions. - */ -- (NSArray*)sessionsWithDevice:(NSString*)deviceKey; - -/** - Enumerate all end-to-end sessions in batches of `batchSize` - - Each block is internally wrapped in `@autoreleasepool` so that memory footprint remains constant - regardless of the number of stored sessions. - - @param batchSize the max number of sessions in a single batch - @param block function that will be executed with each batch, incl. list of sessions and current progress of batching - */ -- (void)enumerateSessionsBy:(NSInteger)batchSize - block:(void (^)(NSArray *sessions, - double progress))block; - -/** - The number of stored end-to-end sessions - */ -- (NSUInteger)sessionsCount; - -/** - Store inbound group sessions. - - @param sessions inbound group sessions. - */ -- (void)storeInboundGroupSessions:(NSArray*)sessions; - -/** - Retrieve an inbound group session. - - @param sessionId the session identifier. - @param senderKey the base64-encoded curve25519 key of the sender. - @return an inbound group session. - */ -- (MXOlmInboundGroupSession*)inboundGroupSessionWithId:(NSString*)sessionId andSenderKey:(NSString*)senderKey; - -/** - Perform an action that will advance the passed end-to-end group session. - - See performAccountOperationWithBlock for more details. - - @param sessionId the session identifier. - @param senderKey the base64-encoded curve25519 key of the sender. - @param block the block where olm session operations can be safely made. - */ -- (void)performSessionOperationWithGroupSessionWithId:(NSString*)sessionId senderKey:(NSString*)senderKey block:(void (^)(MXOlmInboundGroupSession *inboundGroupSession))block; - -/** - Retrieve all inbound group sessions. - - @return the list of all inbound group sessions. - */ -- (NSArray *)inboundGroupSessions; - -/** - Enumerate all inbound group sessions in batches of `batchSize` - - Each block is internally wrapped in `@autoreleasepool` so that memory footprint remains constant - regardless of the number of stored sessions. - - @param batchSize the max number of sessions in a single batch - @param block function that will be executed with each batch, incl. list of sessions and current progress of batching - */ -- (void)enumerateInboundGroupSessionsBy:(NSInteger)batchSize - block:(void (^)(NSArray *sessions, - NSSet *backedUp, - double progress))block; - -/** - Store outbound group session. - - @param session outbound group session. - @param roomId related room ID. - - @return the related stored outbound group session. - */ -- (MXOlmOutboundGroupSession *)storeOutboundGroupSession:(OLMOutboundGroupSession *)session withRoomId:(NSString *)roomId; - -/** - Retrieve an outbound group session for a specific room. - - @param roomId the room identifier. - @return an outbound group session if found. Nil otherwise - */ -- (MXOlmOutboundGroupSession *)outboundGroupSessionWithRoomId:(NSString*)roomId; - -/** - Retrieve all outbound group sessions. - - @return the list of all stored outbound group sessions. - */ -- (NSArray *)outboundGroupSessions; - -/** - Remove stored outbound group session for a specific room. - - @param roomId the room identifier. - */ -- (void)removeOutboundGroupSessionWithRoomId:(NSString*)roomId; - -/** - Store the message index shared with a list of devices in dedicated room - - @param devices list of devices the message index has been shared with - @param messageIndex the current message index of the outbound group session - @param roomId ID of the room of the outbound group session - @param sessionId ID of the session of the outbound group session - */ -- (void)storeSharedDevices:(MXUsersDevicesMap *)devices messageIndex:(NSUInteger) messageIndex forOutboundGroupSessionInRoomWithId:(NSString *)roomId sessionId:(NSString *)sessionId; - -/** - Retrieves all the devices the outbound group session has been shared with - - @param roomId ID of the room of the outbound group session - @param sessionId ID of the session of the outbound group session - - @return MXUsersDevicesMap of the message indexes - */ -- (MXUsersDevicesMap *)sharedDevicesForOutboundGroupSessionInRoomWithId:(NSString *)roomId sessionId:(NSString *)sessionId; - -/** - Retrieves the message index of the outbound session when it has been shared with a given device. - - @param roomId ID of the room of the outbound group session - @param sessionId ID of the session of the outbound group session - @param userId user ID of the device - @param deviceId ID of the device - - @return the NSNumber of the message index of the outbound session when it has been shared with a given device. Nil if the session has not been shared with the given device. - */ -- (NSNumber *)messageIndexForSharedDeviceInRoomWithId:(NSString *)roomId sessionId:(NSString *)sessionId userId:(NSString *)userId deviceId:(NSString *)deviceId; - -#pragma mark - Key backup - -/** - The backup version currently used. - Nil means no backup. - */ -@property (nonatomic) NSString *backupVersion; - -/** - Mark all inbound group sessions as not backed up. - */ -- (void)resetBackupMarkers; - -/** - Mark inbound group sessions as backed up on the user homeserver. - - @param sessions inbound group sessions. - */ -- (void)markBackupDoneForInboundGroupSessions:(NSArray*)sessions; - -/** - Retrieve inbound group sessions that are not yet backed up. - - @param limit the maximum number of sessions to return. - @return an array of non backed up inbound group sessions. - */ -- (NSArray*)inboundGroupSessionsToBackup:(NSUInteger)limit; - -/** - Number of stored inbound group sessions. - - @param onlyBackedUp if YES, count only session marked as backed up. - @return a count. - */ -- (NSUInteger)inboundGroupSessionsCount:(BOOL)onlyBackedUp; - - -#pragma mark - Key sharing - Outgoing key requests - -/** - Look for existing outgoing room key request, and returns the result synchronously. - - @param requestBody the existing request to look for. - @return a MXOutgoingRoomKeyRequest matching the request, or nil if not found. - */ -- (MXOutgoingRoomKeyRequest*)outgoingRoomKeyRequestWithRequestBody:(NSDictionary *)requestBody; - -/** - Look for the first outgoing key request that matches the state. - - @param state to look for. - @return a MXOutgoingRoomKeyRequest matching the request, or nil if not found. - */ -- (MXOutgoingRoomKeyRequest*)outgoingRoomKeyRequestWithState:(MXRoomKeyRequestState)state; - -/** - Get all outgoing key requests that match the state. - - @param state to look for. - @return a MXOutgoingRoomKeyRequest matching the request, or nil if not found. - */ -- (NSArray *)allOutgoingRoomKeyRequestsWithState:(MXRoomKeyRequestState)state; - -/** - Store an outgoing room key request. - - @param request the room key request to store. - */ -- (void)storeOutgoingRoomKeyRequest:(MXOutgoingRoomKeyRequest*)request; - -/** - Update an outgoing room key request. - - @request the room key request to update in the store. - */ -- (void)updateOutgoingRoomKeyRequest:(MXOutgoingRoomKeyRequest*)request; - -/** - Delete an outgoing room key request. - - @param requestId the id of the request to delete. - */ -- (void)deleteOutgoingRoomKeyRequestWithRequestId:(NSString*)requestId; - - -#pragma mark - Key sharing - Incoming key requests - -/** - Store an incoming room key request. - - @param request the room key request to store. - */ -- (void)storeIncomingRoomKeyRequest:(MXIncomingRoomKeyRequest*)request; - -/** - Delete an incoming room key request. - - @param requestId the id of the request to delete. - @param userId the user id. - @param deviceId the user's device id. - */ -- (void)deleteIncomingRoomKeyRequest:(NSString*)requestId fromUser:(NSString*)userId andDevice:(NSString*)deviceId; - -/** - Get an incoming room key request. - - @param requestId the id of the request to retrieve. - @param userId the user id. - @param deviceId the user's device id. - @return a MXIncomingRoomKeyRequest matching the request, or nil if not found. - */ -- (MXIncomingRoomKeyRequest*)incomingRoomKeyRequestWithRequestId:(NSString*)requestId fromUser:(NSString*)userId andDevice:(NSString*)deviceId; - -/** - Get all incoming room key requests. - - @return a map userId -> deviceId -> [MXIncomingRoomKeyRequest*]. - */ -- (MXUsersDevicesMap *> *)incomingRoomKeyRequests; - -#pragma mark - Crypto settings - -/** - The global override for whether the client should ever send encrypted - messages to unverified devices. - - This settings is stored in the crypto store. - - If NO, it can still be overridden per-room. - If YES, it overrides the per-room settings. - - Default is NO. - */ -@property (nonatomic) BOOL globalBlacklistUnverifiedDevices; - -/** - Tells whether the client should encrypt messages only for the verified devices - in this room. - - Will be ignored if globalBlacklistUnverifiedDevices is YES. - This settings is stored in the crypto store. - - The default value is NO. - - @param roomId the room id. - @return YES if the client should encrypt messages only for the verified devices. - */ -- (BOOL)blacklistUnverifiedDevicesInRoom:(NSString *)roomId; - -/** - Set the blacklist of unverified devices in a room. - - @param roomId the room id. - @param blacklist YES to encrypt messsages for only verified devices. - */ -- (void)storeBlacklistUnverifiedDevicesInRoom:(NSString *)roomId blacklist:(BOOL)blacklist; - - -#pragma mark - Methods for unitary tests purpose -/** - Remove an inbound group session. - - @param sessionId the session identifier. - @param senderKey the base64-encoded curve25519 key of the sender. - */ -- (void)removeInboundGroupSessionWithId:(NSString*)sessionId andSenderKey:(NSString*)senderKey; - - -#pragma mark - Versioning - -/** - The version of the crypto module implementation. - - It is used to handle logical migration between crypto module updates. - Must return MXCryptoVersionUndefined if not defined yet. - */ -@property (nonatomic) MXCryptoVersion cryptoVersion; - -@end - -#endif diff --git a/MatrixSDK/Crypto/Migration/MXCryptoVersion.h b/MatrixSDK/Crypto/Data/Store/MXCryptoVersion.h similarity index 100% rename from MatrixSDK/Crypto/Migration/MXCryptoVersion.h rename to MatrixSDK/Crypto/Data/Store/MXCryptoVersion.h diff --git a/MatrixSDK/Crypto/Data/Store/MXRealmCryptoStore/MXRealmCryptoStore.h b/MatrixSDK/Crypto/Data/Store/MXRealmCryptoStore/MXRealmCryptoStore.h deleted file mode 100644 index 4b1b0a61ac..0000000000 --- a/MatrixSDK/Crypto/Data/Store/MXRealmCryptoStore/MXRealmCryptoStore.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - Copyright 2016 OpenMarket 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. - */ - -#import - -#import "MXSDKOptions.h" - -#ifdef MX_CRYPTO - -#import "MXCryptoStore.h" - -@interface MXRealmCryptoStore : NSObject - -/** - Flag to check if Realm DB compaction must be done. - Default is YES. - - @discussion - It may be useful to disable compaction when running on a different process than the main one in order - to avoid race conditions. - */ -@property (class) BOOL shouldCompactOnLaunch; - -/** - Flag to control if Realm DB will be opened in read-only mode. - Default is NO. - */ -@property (nonatomic, assign) BOOL readOnly; - -@end - -#endif diff --git a/MatrixSDK/Crypto/Data/Store/MXRealmCryptoStore/MXRealmCryptoStore.m b/MatrixSDK/Crypto/Data/Store/MXRealmCryptoStore/MXRealmCryptoStore.m deleted file mode 100644 index 277553c20a..0000000000 --- a/MatrixSDK/Crypto/Data/Store/MXRealmCryptoStore/MXRealmCryptoStore.m +++ /dev/null @@ -1,2298 +0,0 @@ -/* - Copyright 2016 OpenMarket Ltd - Copyright 2017 Vector Creations 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. - */ - -#import "MXRealmCryptoStore.h" - -#ifdef MX_CRYPTO - -#import -#import -#import "MXSession.h" -#import "MXTools.h" -#import "MXCryptoTools.h" -#import "MXKeyProvider.h" -#import "MXRawDataKey.h" -#import "MXAes.h" -#import "MatrixSDKSwiftHeader.h" -#import "MXRealmHelper.h" -#import "MXBackgroundModeHandler.h" -#import "RLMRealm+MatrixSDK.h" - -NSUInteger const kMXRealmCryptoStoreVersion = 17; - -static NSString *const kMXRealmCryptoStoreFolder = @"MXRealmCryptoStore"; - - -#pragma mark - Realm objects that encapsulate existing ones - -@interface MXRealmDeviceInfo : RLMObject -@property NSData *deviceInfoData; -@property (nonatomic) NSString *deviceId; -@property (nonatomic) NSString *identityKey; -@end - -@implementation MXRealmDeviceInfo -@end -RLM_ARRAY_TYPE(MXRealmDeviceInfo) - -@interface MXRealmCrossSigningInfo : RLMObject -@property NSData *data; -@end - -@implementation MXRealmCrossSigningInfo -@end -RLM_ARRAY_TYPE(MXRealmCrossSigningInfo) - - -@interface MXRealmUser : RLMObject -@property (nonatomic) NSString *userId; -@property RLMArray *devices; -@property MXRealmCrossSigningInfo *crossSigningKeys; -@end - -@implementation MXRealmUser -+ (NSString *)primaryKey -{ - return @"userId"; -} -@end -RLM_ARRAY_TYPE(MXRealmUser) - - -@interface MXRealmRoomAlgorithm : RLMObject -@property NSString *roomId; -@property NSString *algorithm; -@property BOOL blacklistUnverifiedDevices; -@end - -@implementation MXRealmRoomAlgorithm - -- (instancetype)init -{ - self = [super init]; - if (self) - { - _blacklistUnverifiedDevices = NO; - } - return self; -} - -+ (NSString *)primaryKey -{ - return @"roomId"; -} -@end -RLM_ARRAY_TYPE(MXRealmRoomAlgorithm) - - -@interface MXRealmOlmSession : RLMObject -@property NSString *sessionId; -@property NSString *deviceKey; -@property NSTimeInterval lastReceivedMessageTs; -@property NSData *olmSessionData; -@end - -@implementation MXRealmOlmSession -@end -RLM_ARRAY_TYPE(MXRealmOlmSession) - - -@interface MXRealmOlmInboundGroupSession : RLMObject -@property NSString *sessionId; -@property NSString *senderKey; -@property NSData *olmInboundGroupSessionData; - -// A primary key is required to update `backedUp`. -// Do our combined primary key ourselves as it is not supported by Realm. -@property NSString *sessionIdSenderKey; - -// Indicate if the key has been backed up to the homeserver -@property BOOL backedUp; - -@end - -@implementation MXRealmOlmInboundGroupSession -+ (NSString *)primaryKey -{ - return @"sessionIdSenderKey"; -} - -+ (NSString *)primaryKeyWithSessionId:(NSString*)sessionId senderKey:(NSString*)senderKey -{ - return [NSString stringWithFormat:@"%@|%@", sessionId, senderKey]; -} -@end -RLM_ARRAY_TYPE(MXRealmOlmInboundGroupSession) - - -@interface MXRealmOlmOutboundGroupSession : RLMObject -@property NSString *roomId; -@property NSString *sessionId; -@property NSTimeInterval creationTime; -@property NSData *sessionData; -@end - -@implementation MXRealmOlmOutboundGroupSession -+ (NSString *)primaryKey -{ - return @"roomId"; -} -@end -RLM_ARRAY_TYPE(MXRealmOlmOutboundGroupSession) - -@interface MXRealmSharedOutboundSession : RLMObject - -@property NSString *roomId; -@property NSString *sessionId; -@property MXRealmDeviceInfo *device; -@property NSNumber *messageIndex; - -@end - -@implementation MXRealmSharedOutboundSession -@end -RLM_ARRAY_TYPE(MXRealmSharedOutboundSession) - -@interface MXRealmOlmAccount : RLMObject - -/** - The user id. - */ -@property (nonatomic) NSString *userId; - -/** - The device id. - */ -@property (nonatomic) NSString *deviceId; - -/** - The version of the crypto module implementation. - */ -@property (nonatomic) MXCryptoVersion cryptoVersion; - -/** - The pickled OLMAccount object. - */ -@property NSData *olmAccountData; - -/** - The sync token corresponding to the device list. - */ -@property (nonatomic) NSString *deviceSyncToken; - -/** - NSData serialisation of users we are tracking device status for. - userId -> MXDeviceTrackingStatus* - */ -@property (nonatomic) NSData *deviceTrackingStatusData; - -/** - Settings for blacklisting unverified devices. - */ -@property (nonatomic) BOOL globalBlacklistUnverifiedDevices; - -/** - The backup version currently used. - */ -@property (nonatomic) NSString *backupVersion; - -@end - -@implementation MXRealmOlmAccount - -- (instancetype)init -{ - self = [super init]; - if (self) - { - _globalBlacklistUnverifiedDevices = NO; - } - return self; -} - -+ (NSString *)primaryKey -{ - return @"userId"; -} -@end - -@interface MXRealmOutgoingRoomKeyRequest : RLMObject -@property (nonatomic) NSString *requestId; -@property (nonatomic) NSString *cancellationTxnId; -@property (nonatomic) NSData *recipientsData; -@property (nonatomic) NSString *requestBodyString; -@property (nonatomic) NSString *requestBodyHash; -@property (nonatomic) NSNumber *state; - -- (MXOutgoingRoomKeyRequest *)outgoingRoomKeyRequest; - -@end - -@implementation MXRealmOutgoingRoomKeyRequest -+ (NSString *)primaryKey -{ - return @"requestId"; -} - -- (MXOutgoingRoomKeyRequest *)outgoingRoomKeyRequest -{ - MXOutgoingRoomKeyRequest *outgoingRoomKeyRequest = [[MXOutgoingRoomKeyRequest alloc] init]; - - outgoingRoomKeyRequest.requestId = self.requestId; - outgoingRoomKeyRequest.cancellationTxnId = self.cancellationTxnId; - outgoingRoomKeyRequest.state = (MXRoomKeyRequestState)[self.state unsignedIntegerValue]; - outgoingRoomKeyRequest.recipients = [NSKeyedUnarchiver unarchiveObjectWithData:self.recipientsData]; - outgoingRoomKeyRequest.requestBody = [MXTools deserialiseJSONString:self.requestBodyString]; - - return outgoingRoomKeyRequest; -} - -@end - -@interface MXRealmIncomingRoomKeyRequest : RLMObject -@property (nonatomic) NSString *requestId; -@property (nonatomic) NSString *userId; -@property (nonatomic) NSString *deviceId; -@property (nonatomic) NSData *requestBodyData; - -- (MXIncomingRoomKeyRequest *)incomingRoomKeyRequest; - -@end - -@implementation MXRealmIncomingRoomKeyRequest - -- (MXIncomingRoomKeyRequest *)incomingRoomKeyRequest -{ - MXIncomingRoomKeyRequest *incomingRoomKeyRequest = [[MXIncomingRoomKeyRequest alloc] init]; - - incomingRoomKeyRequest.requestId = self.requestId; - incomingRoomKeyRequest.userId = self.userId; - incomingRoomKeyRequest.deviceId = self.deviceId; - incomingRoomKeyRequest.requestBody = [NSKeyedUnarchiver unarchiveObjectWithData:self.requestBodyData]; - - return incomingRoomKeyRequest; -} - -@end - - -@interface MXRealmSecret : RLMObject -@property NSString *secretId; -@property NSString *secret; - -@property NSData *encryptedSecret; -@property NSData *iv; -@end - -@implementation MXRealmSecret - -+ (NSString *)primaryKey -{ - return @"secretId"; -} -@end -RLM_ARRAY_TYPE(MXRealmSecret) - - -#pragma mark - MXRealmCryptoStore - -NSString *const MXRealmCryptoStoreReadonlySuffix = @"readonly"; - -@interface MXRealmCryptoStore () -{ - NSString *userId; - NSString *deviceId; -} - -/** - The realm on the current thread. - - As MXCryptoStore methods can be called from different threads, we need to load realm objects - from the root. This is how Realm works in multi-threading environment. - */ -@property (readonly) RLMRealm *realm; - -/** - The MXRealmOlmAccount on the current thread. - */ -@property (readonly) MXRealmOlmAccount *accountInCurrentThread; - -@end - -@implementation MXRealmCryptoStore - -+ (BOOL)hasDataForCredentials:(MXCredentials*)credentials -{ - RLMRealm *realm = [MXRealmCryptoStore realmForUser:credentials.userId andDevice:credentials.deviceId readOnly:YES]; - if (realm == nil) - { - // there is no Realm with this config - return NO; - } - return nil != [MXRealmOlmAccount objectInRealm:realm forPrimaryKey:credentials.userId]; -} - -+ (instancetype)createStoreWithCredentials:(MXCredentials*)credentials -{ - MXLogDebug(@"[MXRealmCryptoStore] createStore for %@:%@", credentials.userId, credentials.deviceId); - - RLMRealm *realm = [MXRealmCryptoStore realmForUser:credentials.userId andDevice:credentials.deviceId readOnly:NO]; - - MXRealmOlmAccount *account = [[MXRealmOlmAccount alloc] initWithValue:@{ - @"userId" : credentials.userId, - }]; - account.deviceId = credentials.deviceId; - - [realm beginWriteTransaction]; - [realm addObject:account]; - [realm commitWriteTransaction]; - - return [[MXRealmCryptoStore alloc] initWithCredentials:credentials]; -} - -+ (void)deleteStoreWithCredentials:(MXCredentials*)credentials -{ - // Delete both stores - [self _deleteStoreWithCredentials:credentials readOnly:NO]; - [self _deleteStoreWithCredentials:credentials readOnly:YES]; -} - -+ (void)deleteAllStores -{ - [[NSFileManager defaultManager] removeItemAtURL:[self storeFolderURL] error:nil]; -} - -+ (void)deleteReadonlyStoreWithCredentials:(MXCredentials *)credentials -{ - [self _deleteStoreWithCredentials:credentials readOnly:YES]; -} - -+ (void)_deleteStoreWithCredentials:(MXCredentials*)credentials readOnly:(BOOL)readOnly -{ - MXLogDebug(@"[MXRealmCryptoStore] deleteStore for %@:%@, readOnly: %@", credentials.userId, credentials.deviceId, readOnly ? @"YES" : @"NO"); - - // Delete db file directly - // So that we can even delete corrupted realm db - RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration]; - NSURL *realmFileURL = [self realmFileURLForUserWithUserId:credentials.userId andDevice:credentials.deviceId]; - if (readOnly) - { - config.fileURL = [self readonlyURLFrom:realmFileURL]; - } - else - { - config.fileURL = realmFileURL; - } - - if (![RLMRealm fileExistsForConfiguration:config]) - { - MXLogDebug(@"[MXRealmCryptoStore] deleteStore: Realm db does not exist"); - return; - } - - NSError *error; - [RLMRealm deleteFilesForConfiguration:config error:&error]; - if (error) - { - MXLogErrorDetails(@"[MXRealmCryptoStore] deleteStore error", error); - - if (!readOnly) - { - // The db is probably still opened elsewhere (RLMErrorAlreadyOpen), which means it is valid. - // Use the old method to clear the db - error = nil; - RLMRealm *realm = [MXRealmCryptoStore realmForUser:credentials.userId andDevice:credentials.deviceId readOnly:readOnly]; - if (!error) - { - MXLogDebug(@"[MXRealmCryptoStore] deleteStore: Delete at least its content"); - [realm transactionWithName:@"[MXRealmCryptoStore] deleteStore" block:^{ - [realm deleteAllObjects]; - }]; - } - else - { - MXLogErrorDetails(@"[MXRealmCryptoStore] deleteStore: Cannot open realm.", error); - } - } - } -} - -- (instancetype)initWithCredentials:(MXCredentials *)credentials -{ - MXLogDebug(@"[MXRealmCryptoStore] initWithCredentials for %@:%@", credentials.userId, credentials.deviceId); - - self = [super init]; - if (self) - { - userId = credentials.userId; - deviceId = credentials.deviceId; - - MXRealmOlmAccount *account = self.accountInCurrentThread; - if (!account) - { - return nil; - } - else - { - // Make sure the device id corresponds - if (account.deviceId && ![account.deviceId isEqualToString:credentials.deviceId]) - { - MXLogDebug(@"[MXRealmCryptoStore] Credentials do not match"); - [MXRealmCryptoStore deleteStoreWithCredentials:credentials]; - self = [MXRealmCryptoStore createStoreWithCredentials:credentials]; - self.cryptoVersion = MXCryptoVersionLast; - } - } - - MXLogDebug(@"[MXRealmCryptoStore] Schema version: %llu", account.realm.configuration.schemaVersion); - } - return self; -} - -- (RLMRealm *)realm -{ - return [MXRealmCryptoStore realmForUser:userId andDevice:deviceId readOnly:_readOnly]; -} - -- (MXRealmOlmAccount*)accountInCurrentThread -{ - return [MXRealmOlmAccount objectInRealm:self.realm forPrimaryKey:userId]; -} - -- (NSString *)userId -{ - return self.accountInCurrentThread.userId; -} - -- (void)storeDeviceId:(NSString*)deviceId -{ - MXRealmOlmAccount *account = self.accountInCurrentThread; - - [account.realm transactionWithName:@"[MXRealmCryptoStore] storeDeviceId" block:^{ - account.deviceId = deviceId; - }]; -} - -- (NSString*)deviceId -{ - MXRealmOlmAccount *account = self.accountInCurrentThread; - - return account.deviceId; -} - -- (void)setAccount:(OLMAccount*)olmAccount -{ - NSDate *startDate = [NSDate date]; - - MXRealmOlmAccount *account = self.accountInCurrentThread; - - [account.realm transactionWithName:@"[MXRealmCryptoStore] setAccount" block:^{ - account.olmAccountData = [NSKeyedArchiver archivedDataWithRootObject:olmAccount]; - }]; - - MXLogDebug(@"[MXRealmCryptoStore] storeAccount in %.3fms", [[NSDate date] timeIntervalSinceDate:startDate] * 1000); -} - -- (OLMAccount*)account -{ - MXRealmOlmAccount *account = self.accountInCurrentThread; - if (account.olmAccountData) - { - return [NSKeyedUnarchiver unarchiveObjectWithData:account.olmAccountData]; - } - return nil; -} - -- (void)performAccountOperationWithBlock:(void (^)(OLMAccount *))block -{ - [self.realm transactionWithName:@"[MXRealmCryptoStore] performAccountOperationWithBlock" block:^{ - MXRealmOlmAccount *account = self.accountInCurrentThread; - if (account.olmAccountData) - { - OLMAccount *olmAccount = [NSKeyedUnarchiver unarchiveObjectWithData:account.olmAccountData]; - if (olmAccount) - { - block(olmAccount); - account.olmAccountData = [NSKeyedArchiver archivedDataWithRootObject:olmAccount]; - } - else - { - MXLogError(@"[MXRealmCryptoStore] performAccountOperationWithBlock. Error: Cannot build OLMAccount"); - block(nil); - } - } - else - { - MXLogError(@"[MXRealmCryptoStore] performAccountOperationWithBlock. Error: No OLMAccount yet"); - block(nil); - } - }]; -} - -- (void)storeDeviceSyncToken:(NSString*)deviceSyncToken -{ - MXRealmOlmAccount *account = self.accountInCurrentThread; - [account.realm transactionWithName:@"[MXRealmCryptoStore] storeDeviceSyncToken" block:^{ - account.deviceSyncToken = deviceSyncToken; - }]; -} - -- (NSString*)deviceSyncToken -{ - MXRealmOlmAccount *account = self.accountInCurrentThread; - return account.deviceSyncToken; -} - -- (void)storeDeviceForUser:(NSString*)userID device:(MXDeviceInfo*)device -{ - NSDate *startDate = [NSDate date]; - - RLMRealm *realm = self.realm; - - [realm transactionWithName:@"[MXRealmCryptoStore] storeDeviceForUser" block:^{ - - MXRealmUser *realmUser = [MXRealmUser objectInRealm:realm forPrimaryKey:userID]; - if (!realmUser) - { - realmUser = [[MXRealmUser alloc] initWithValue:@{ - @"userId": userID, - }]; - - [realm addObject:realmUser]; - } - - MXRealmDeviceInfo *realmDevice = [[realmUser.devices objectsWhere:@"deviceId = %@", device.deviceId] firstObject]; - if (!realmDevice) - { - realmDevice = [[MXRealmDeviceInfo alloc] initWithValue:@{ - @"deviceId": device.deviceId, - @"deviceInfoData": [NSKeyedArchiver archivedDataWithRootObject:device] - }]; - realmDevice.identityKey = device.identityKey; - [realmUser.devices addObject:realmDevice]; - } - else - { - realmDevice.deviceInfoData = [NSKeyedArchiver archivedDataWithRootObject:device]; - } - - }]; - - MXLogDebug(@"[MXRealmCryptoStore] storeDeviceForUser in %.3fms", [[NSDate date] timeIntervalSinceDate:startDate] * 1000); -} - -- (MXDeviceInfo*)deviceWithDeviceId:(NSString*)deviceId forUser:(NSString*)userID -{ - MXRealmUser *realmUser = [MXRealmUser objectInRealm:self.realm forPrimaryKey:userID]; - - MXRealmDeviceInfo *realmDevice = [[realmUser.devices objectsWhere:@"deviceId = %@", deviceId] firstObject]; - if (realmDevice) - { - return [NSKeyedUnarchiver unarchiveObjectWithData:realmDevice.deviceInfoData]; - } - - return nil; -} - -- (MXDeviceInfo*)deviceWithIdentityKey:(NSString*)identityKey -{ - MXRealmDeviceInfo *realmDevice = [MXRealmDeviceInfo objectsInRealm:self.realm where:@"identityKey = %@", identityKey].firstObject; - if (realmDevice) - { - return [NSKeyedUnarchiver unarchiveObjectWithData:realmDevice.deviceInfoData]; - } - - return nil; -} - -- (void)storeDevicesForUser:(NSString*)userID devices:(NSDictionary*)devices -{ - NSDate *startDate = [NSDate date]; - - RLMRealm *realm = self.realm; - - [realm transactionWithName:@"[MXRealmCryptoStore] storeDevicesForUser" block:^{ - - MXRealmUser *realmUser = [MXRealmUser objectInRealm:realm forPrimaryKey:userID];; - if (!realmUser) - { - realmUser = [[MXRealmUser alloc] initWithValue:@{ - @"userId": userID, - }]; - [realm addObject:realmUser]; - } - else - { - // Reset all previously stored devices for this user - [realm deleteObjects:realmUser.devices]; - } - - for (NSString *deviceId in devices) - { - MXDeviceInfo *device = devices[deviceId]; - MXRealmDeviceInfo *realmDevice = [[MXRealmDeviceInfo alloc] initWithValue:@{ - @"deviceId": device.deviceId, - @"deviceInfoData": [NSKeyedArchiver archivedDataWithRootObject:device] - }]; - realmDevice.identityKey = device.identityKey; - [realmUser.devices addObject:realmDevice]; - } - }]; - - MXLogDebug(@"[MXRealmCryptoStore] storeDevicesForUser (count: %tu) in %.3fms", devices.count, [[NSDate date] timeIntervalSinceDate:startDate] * 1000); -} - -- (NSDictionary*)devicesForUser:(NSString*)userID -{ - NSMutableDictionary *devicesForUser; - - MXRealmUser *realmUser = [MXRealmUser objectInRealm:self.realm forPrimaryKey:userID]; - if (realmUser) - { - devicesForUser = [NSMutableDictionary dictionary]; - - for (MXRealmDeviceInfo *realmDevice in realmUser.devices) - { - devicesForUser[realmDevice.deviceId] = [NSKeyedUnarchiver unarchiveObjectWithData:realmDevice.deviceInfoData]; - } - } - - return devicesForUser; -} - -- (NSDictionary*)deviceTrackingStatus -{ - MXRealmOlmAccount *account = self.accountInCurrentThread; - return [NSKeyedUnarchiver unarchiveObjectWithData:account.deviceTrackingStatusData]; -} - -- (void)storeDeviceTrackingStatus:(NSDictionary*)statusMap -{ - MXRealmOlmAccount *account = self.accountInCurrentThread; - [account.realm transactionWithName:@"[MXRealmCryptoStore] storeDeviceTrackingStatus" block:^{ - - account.deviceTrackingStatusData = [NSKeyedArchiver archivedDataWithRootObject:statusMap]; - }]; -} - - -#pragma mark - Cross-signing keys - -- (void)storeCrossSigningKeys:(MXCrossSigningInfo*)crossSigningInfo -{ - RLMRealm *realm = self.realm; - - [realm transactionWithName:@"[MXRealmCryptoStore] storeCrossSigningKeys" block:^{ - - MXRealmUser *realmUser = [MXRealmUser objectInRealm:realm forPrimaryKey:crossSigningInfo.userId]; - if (!realmUser) - { - realmUser = [[MXRealmUser alloc] initWithValue:@{ - @"userId": crossSigningInfo.userId, - }]; - - [realm addObject:realmUser]; - } - - MXRealmCrossSigningInfo *realmCrossSigningKeys = [[MXRealmCrossSigningInfo alloc] initWithValue:@{ - @"data": [NSKeyedArchiver archivedDataWithRootObject:crossSigningInfo] - }]; - if (realmUser.crossSigningKeys) - { - // Remove orphan MXRealmCrossSigningInfo objects from the DB - [realm deleteObject:realmUser.crossSigningKeys]; - } - - realmUser.crossSigningKeys = realmCrossSigningKeys; - }]; -} - -- (MXCrossSigningInfo*)crossSigningKeysForUser:(NSString*)userId -{ - MXCrossSigningInfo *crossSigningKeys; - - MXRealmUser *realmUser = [MXRealmUser objectInRealm:self.realm forPrimaryKey:userId]; - if (realmUser) - { - crossSigningKeys = [NSKeyedUnarchiver unarchiveObjectWithData:realmUser.crossSigningKeys.data]; - } - - return crossSigningKeys; -} - -- (NSArray *)crossSigningKeys -{ - NSMutableArray *crossSigningKeys = [NSMutableArray array]; - - for (MXRealmCrossSigningInfo *realmCrossSigningKey in [MXRealmCrossSigningInfo allObjectsInRealm:self.realm]) - { - [crossSigningKeys addObject:[NSKeyedUnarchiver unarchiveObjectWithData:realmCrossSigningKey.data]]; - } - - return crossSigningKeys; -} - - -#pragma mark - Message keys - -- (void)storeAlgorithmForRoom:(NSString*)roomId algorithm:(NSString*)algorithm -{ - __block BOOL isNew = NO; - NSDate *startDate = [NSDate date]; - - RLMRealm *realm = self.realm; - [realm transactionWithName:@"[MXRealmCryptoStore] storeAlgorithmForRoom" block:^{ - - MXRealmRoomAlgorithm *roomAlgorithm = [self realmRoomAlgorithmForRoom:roomId inRealm:realm]; - if (roomAlgorithm) - { - // Update the existing one - roomAlgorithm.algorithm = algorithm; - } - else - { - // Create it - roomAlgorithm = [[MXRealmRoomAlgorithm alloc] initWithValue:@{ - @"roomId": roomId, - @"algorithm": algorithm - }]; - [realm addObject:roomAlgorithm]; - } - }]; - - MXLogDebug(@"[MXRealmCryptoStore] storeAlgorithmForRoom (%@) in %.3fms", (isNew?@"NEW":@"UPDATE"), [[NSDate date] timeIntervalSinceDate:startDate] * 1000); -} - -- (NSString*)algorithmForRoom:(NSString*)roomId -{ - return [self realmRoomAlgorithmForRoom:roomId inRealm:self.realm].algorithm; -} - -- (void)storeBlacklistUnverifiedDevicesInRoom:(NSString *)roomId blacklist:(BOOL)blacklist -{ - BOOL isNew = NO; - NSDate *startDate = [NSDate date]; - - RLMRealm *realm = self.realm; - [realm transactionWithName:@"[MXRealmCryptoStore] storeBlacklistUnverifiedDevicesInRoom" block:^{ - - MXRealmRoomAlgorithm *roomAlgorithm = [self realmRoomAlgorithmForRoom:roomId inRealm:realm]; - if (roomAlgorithm) - { - // Update the existing one - roomAlgorithm.blacklistUnverifiedDevices = blacklist; - } - else - { - // Create it - roomAlgorithm = [[MXRealmRoomAlgorithm alloc] initWithValue:@{ - @"roomId": roomId, - @"blacklist": @(blacklist) - }]; - [realm addObject:roomAlgorithm]; - } - }]; - - MXLogDebug(@"[MXRealmCryptoStore] storeBlacklistUnverifiedDevicesInRoom (%@) in %.3fms", (isNew?@"NEW":@"UPDATE"), [[NSDate date] timeIntervalSinceDate:startDate] * 1000); -} - -- (BOOL)blacklistUnverifiedDevicesInRoom:(NSString *)roomId -{ - return [self realmRoomAlgorithmForRoom:roomId inRealm:self.realm].blacklistUnverifiedDevices; -} - -- (MXRealmRoomAlgorithm *)realmRoomAlgorithmForRoom:(NSString*)roomId inRealm:(RLMRealm*)realm -{ - return [MXRealmRoomAlgorithm objectInRealm:realm forPrimaryKey:roomId]; -} - -- (NSArray *)roomSettings -{ - NSMutableArray *objects = [NSMutableArray array]; - for (MXRealmRoomAlgorithm *item in [MXRealmRoomAlgorithm allObjectsInRealm:self.realm]) { - NSError *error = nil; - MXRoomSettings *settings = [[MXRoomSettings alloc] initWithRoomId:item.roomId - algorithm:item.algorithm - blacklistUnverifiedDevices:item.blacklistUnverifiedDevices - error:&error]; - if (settings) { - [objects addObject:settings]; - } else { - MXLogErrorDetails(@"[MXRealmCryptoStore] roomSettings: Cannot create settings", error); - } - } - return objects.copy; -} - -- (void)storeSession:(MXOlmSession*)session -{ - __block BOOL isNew = NO; - NSDate *startDate = [NSDate date]; - - RLMRealm *realm = self.realm; - [realm transactionWithName:@"[MXRealmCryptoStore] storeSession" block:^{ - - MXRealmOlmSession *realmOlmSession = [MXRealmOlmSession objectsInRealm:realm where:@"sessionId = %@ AND deviceKey = %@", session.session.sessionIdentifier, session.deviceKey].firstObject; - if (realmOlmSession) - { - // Update the existing one - realmOlmSession.olmSessionData = [NSKeyedArchiver archivedDataWithRootObject:session.session]; - } - else - { - // Create it - isNew = YES; - realmOlmSession = [[MXRealmOlmSession alloc] initWithValue:@{ - @"sessionId": session.session.sessionIdentifier, - @"deviceKey": session.deviceKey, - @"olmSessionData": [NSKeyedArchiver archivedDataWithRootObject:session.session] - }]; - realmOlmSession.lastReceivedMessageTs = session.lastReceivedMessageTs; - - [realm addObject:realmOlmSession]; - } - }]; - - MXLogDebug(@"[MXRealmCryptoStore] storeSession (%@) in %.3fms", (isNew?@"NEW":@"UPDATE"), [[NSDate date] timeIntervalSinceDate:startDate] * 1000); -} - -- (MXOlmSession*)sessionWithDevice:(NSString*)deviceKey andSessionId:(NSString*)sessionId -{ - MXRealmOlmSession *realmOlmSession = [MXRealmOlmSession objectsInRealm:self.realm - where:@"sessionId = %@ AND deviceKey = %@", sessionId, deviceKey].firstObject; - return [self olmSessionForRealmSession:realmOlmSession]; -} - -- (void)performSessionOperationWithDevice:(NSString*)deviceKey andSessionId:(NSString*)sessionId block:(void (^)(MXOlmSession *olmSession))block -{ - [self.realm transactionWithName:@"[MXRealmCryptoStore] performSessionOperationWithDevice" block:^{ - MXRealmOlmSession *realmOlmSession = [MXRealmOlmSession objectsInRealm:self.realm - where:@"sessionId = %@ AND deviceKey = %@", sessionId, deviceKey].firstObject; - MXOlmSession *session = [self olmSessionForRealmSession:realmOlmSession]; - if (session) - { - block(session); - realmOlmSession.olmSessionData = [NSKeyedArchiver archivedDataWithRootObject:session.session]; - } - else - { - MXLogErrorDetails(@"[MXRealmCryptoStore] performSessionOperationWithDevice. Error: olm session not found", @{ - @"sessionId": sessionId ?: @"unknown" - }); - block(nil); - } - }]; -} - -- (NSArray*)sessionsWithDevice:(NSString*)deviceKey; -{ - NSMutableArray *sessionsWithDevice; - - RLMResults *realmOlmSessions = [[MXRealmOlmSession objectsInRealm:self.realm - where:@"deviceKey = %@", deviceKey] - sortedResultsUsingKeyPath:@"lastReceivedMessageTs" ascending:NO]; - for (MXRealmOlmSession *realmOlmSession in realmOlmSessions) - { - if (!sessionsWithDevice) - { - sessionsWithDevice = [NSMutableArray array]; - } - - MXOlmSession *session = [self olmSessionForRealmSession:realmOlmSession]; - if (session) - { - [sessionsWithDevice addObject:session]; - } - } - - return sessionsWithDevice; -} - -- (NSArray*)sessions -{ - NSMutableArray *sessions = [NSMutableArray array]; - - RLMResults *realmOlmSessions = [MXRealmOlmSession allObjectsInRealm:self.realm]; - for (MXRealmOlmSession *realmOlmSession in realmOlmSessions) - { - MXOlmSession *session = [self olmSessionForRealmSession:realmOlmSession]; - if (session) - { - [sessions addObject:session]; - } - } - - return sessions; -} - -- (void)enumerateSessionsBy:(NSInteger)batchSize - block:(void (^)(NSArray *sessions, - double progress))block -{ - RLMResults *query = [MXRealmOlmSession allObjectsInRealm:self.realm]; - for (NSInteger i = 0; i < query.count; i += batchSize) - { - @autoreleasepool { - NSInteger count = MIN(batchSize, query.count - i); - NSIndexSet *batchSet = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(i, count)]; - MXLogDebug(@"[MXRealmCryptoStore] enumerateSessionsBy: Batch %@", batchSet); - - NSMutableArray *sessions = [NSMutableArray array]; - for (MXRealmOlmSession *realmOlmSession in [query objectsAtIndexes:batchSet]) - { - MXOlmSession *session = [self olmSessionForRealmSession:realmOlmSession]; - if (session) - { - [sessions addObject:session]; - } - } - - double progress = (double)(batchSet.lastIndex + 1)/(double)query.count; - block(sessions.copy, progress); - } - } -} - -- (NSUInteger)sessionsCount -{ - RLMResults *sessions = [MXRealmOlmSession allObjectsInRealm:self.realm]; - return sessions.count; -} - -- (MXOlmSession *)olmSessionForRealmSession:(MXRealmOlmSession *)realmSession -{ - if (!realmSession.olmSessionData) - { - MXLogFailure(@"[MXRealmCryptoStore] olmSessionForRealmSession: Missing olm session data"); - return nil; - } - - OLMSession *olmSession = [NSKeyedUnarchiver unarchiveObjectWithData:realmSession.olmSessionData]; - - MXOlmSession *session = [[MXOlmSession alloc] initWithOlmSession:olmSession deviceKey:realmSession.deviceKey]; - session.lastReceivedMessageTs = realmSession.lastReceivedMessageTs; - - return session; -} - -#pragma mark - MXRealmOlmInboundGroupSession - -- (void)storeInboundGroupSessions:(NSArray*)sessions -{ - __block NSUInteger newCount = 0; - NSDate *startDate = [NSDate date]; - - RLMRealm *realm = self.realm; - [realm transactionWithName:@"[MXRealmCryptoStore] storeInboundGroupSessions" block:^{ - - for (MXOlmInboundGroupSession *session in sessions) - { - NSString *sessionIdSenderKey = [MXRealmOlmInboundGroupSession primaryKeyWithSessionId:session.session.sessionIdentifier - senderKey:session.senderKey]; - MXRealmOlmInboundGroupSession *realmSession = [MXRealmOlmInboundGroupSession objectInRealm:realm forPrimaryKey:sessionIdSenderKey]; - if (realmSession) - { - // Update the existing one - realmSession.olmInboundGroupSessionData = [NSKeyedArchiver archivedDataWithRootObject:session]; - } - else - { - // Create it - newCount++; - NSString *sessionIdSenderKey = [MXRealmOlmInboundGroupSession primaryKeyWithSessionId:session.session.sessionIdentifier - senderKey:session.senderKey]; - realmSession = [[MXRealmOlmInboundGroupSession alloc] initWithValue:@{ - @"sessionId": session.session.sessionIdentifier, - @"senderKey": session.senderKey, - @"sessionIdSenderKey": sessionIdSenderKey, - @"olmInboundGroupSessionData": [NSKeyedArchiver archivedDataWithRootObject:session], - }]; - - [realm addObject:realmSession]; - } - } - }]; - - - MXLogDebug(@"[MXRealmCryptoStore] storeInboundGroupSessions: store %@ keys (%@ new) in %.3fms", @(sessions.count), @(newCount), [[NSDate date] timeIntervalSinceDate:startDate] * 1000); -} - -- (MXOlmInboundGroupSession*)inboundGroupSessionWithId:(NSString*)sessionId andSenderKey:(NSString*)senderKey -{ - MXOlmInboundGroupSession *session; - NSString *sessionIdSenderKey = [MXRealmOlmInboundGroupSession primaryKeyWithSessionId:sessionId - senderKey:senderKey]; - MXRealmOlmInboundGroupSession *realmSession = [MXRealmOlmInboundGroupSession objectInRealm:self.realm forPrimaryKey:sessionIdSenderKey]; - - MXLogDebug(@"[MXRealmCryptoStore] inboundGroupSessionWithId: %@ -> %@", sessionId, realmSession ? @"found" : @"not found"); - - if (realmSession) - { - session = [NSKeyedUnarchiver unarchiveObjectWithData:realmSession.olmInboundGroupSessionData]; - - if (!session) - { - MXLogDebug(@"[MXRealmCryptoStore] inboundGroupSessionWithId: ERROR: Failed to create MXOlmInboundGroupSession object"); - } - } - - return session; -} - -- (void)performSessionOperationWithGroupSessionWithId:(NSString*)sessionId senderKey:(NSString*)senderKey block:(void (^)(MXOlmInboundGroupSession *inboundGroupSession))block -{ - [self.realm transactionWithName:@"[MXRealmCryptoStore] performSessionOperationWithGroupSessionWithId" block:^{ - NSString *sessionIdSenderKey = [MXRealmOlmInboundGroupSession primaryKeyWithSessionId:sessionId - senderKey:senderKey]; - MXRealmOlmInboundGroupSession *realmSession = [MXRealmOlmInboundGroupSession objectInRealm:self.realm forPrimaryKey:sessionIdSenderKey]; - - if (realmSession.olmInboundGroupSessionData) - { - MXOlmInboundGroupSession *session = [NSKeyedUnarchiver unarchiveObjectWithData:realmSession.olmInboundGroupSessionData]; - - if (session) - { - block(session); - - realmSession.olmInboundGroupSessionData = [NSKeyedArchiver archivedDataWithRootObject:session]; - } - else - { - MXLogErrorDetails(@"[MXRealmCryptoStore] performSessionOperationWithGroupSessionWithId. Error: Cannot build MXOlmInboundGroupSession for megolm session", @{ - @"sessionId": sessionId ?: @"unknown" - }); - block(nil); - } - } - else - { - MXLogErrorDetails(@"[MXRealmCryptoStore] performSessionOperationWithGroupSessionWithId. Error: megolm session not found", @{ - @"sessionId": sessionId ?: @"unknown" - }); - block(nil); - } - }]; -} - -- (NSArray *)inboundGroupSessions -{ - NSMutableArray *sessions = [NSMutableArray array]; - - for (MXRealmOlmInboundGroupSession *realmSession in [MXRealmOlmInboundGroupSession allObjectsInRealm:self.realm]) - { - [sessions addObject:[NSKeyedUnarchiver unarchiveObjectWithData:realmSession.olmInboundGroupSessionData]]; - } - - return sessions; -} - -- (void)enumerateInboundGroupSessionsBy:(NSInteger)batchSize - block:(void (^)(NSArray *sessions, - NSSet *backedUp, - double progress))block -{ - RLMResults *query = [MXRealmOlmInboundGroupSession allObjectsInRealm:self.realm]; - for (NSInteger i = 0; i < query.count; i += batchSize) - { - @autoreleasepool { - NSInteger count = MIN(batchSize, query.count - i); - NSIndexSet *batchSet = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(i, count)]; - MXLogDebug(@"[MXRealmCryptoStore] enumerateInboundGroupSessions: Batch %@", batchSet); - - NSMutableArray *sessions = [NSMutableArray array]; - NSMutableSet *backedUp = [NSMutableSet set]; - for (MXRealmOlmInboundGroupSession *realmSession in [query objectsAtIndexes:batchSet]) - { - [sessions addObject:[NSKeyedUnarchiver unarchiveObjectWithData:realmSession.olmInboundGroupSessionData]]; - if (realmSession.backedUp) - { - [backedUp addObject:realmSession.sessionId]; - } - } - - double progress = (double)(batchSet.lastIndex + 1)/(double)query.count; - block(sessions.copy, backedUp.copy, progress); - } - } -} - -- (void)removeInboundGroupSessionWithId:(NSString*)sessionId andSenderKey:(NSString*)senderKey -{ - RLMRealm *realm = self.realm; - [realm transactionWithName:@"[MXRealmCryptoStore] removeInboundGroupSessionWithId" block:^{ - - RLMResults *realmSessions = [MXRealmOlmInboundGroupSession objectsInRealm:realm where:@"sessionId = %@ AND senderKey = %@", sessionId, senderKey]; - - [realm deleteObjects:realmSessions]; - }]; -} - - -#pragma mark - MXRealmOlmOutboundGroupSession - -- (MXOlmOutboundGroupSession *)storeOutboundGroupSession:(OLMOutboundGroupSession *)session withRoomId:(NSString *)roomId -{ - __block NSUInteger newCount = 0; - NSDate *startDate = [NSDate date]; - - __block MXOlmOutboundGroupSession *storedSession = nil; - - RLMRealm *realm = self.realm; - [realm transactionWithName:@"[MXRealmCryptoStore] storeOutboundGroupSession" block:^{ - - MXRealmOlmOutboundGroupSession *realmSession = [MXRealmOlmOutboundGroupSession objectInRealm:realm forPrimaryKey:roomId]; - if (realmSession && [realmSession.sessionId isEqual:session.sessionIdentifier]) - { - // Update the existing one - realmSession.sessionData = [NSKeyedArchiver archivedDataWithRootObject:session]; - } - else - { - if (realmSession) - { - // outbound group session exists but session Identifier has changed -> delete previously stored session - [realm deleteObject:realmSession]; - } - - // Create it - newCount++; - realmSession = [[MXRealmOlmOutboundGroupSession alloc] initWithValue: @{ - @"roomId": roomId, - @"sessionId": session.sessionIdentifier, - @"sessionData": [NSKeyedArchiver archivedDataWithRootObject:session] - }]; - realmSession.creationTime = [[NSDate date] timeIntervalSince1970]; - - [realm addObject:realmSession]; - } - - storedSession = [[MXOlmOutboundGroupSession alloc] initWithSession:session roomId:roomId creationTime:realmSession.creationTime]; - }]; - - MXLogDebug(@"[MXRealmCryptoStore] storeOutboundGroupSession: store 1 key (%lu new) in %.3fms", newCount, [[NSDate date] timeIntervalSinceDate:startDate] * 1000); - - return storedSession; -} - -- (MXOlmOutboundGroupSession *)outboundGroupSessionWithRoomId:(NSString*)roomId -{ - OLMOutboundGroupSession *session; - MXRealmOlmOutboundGroupSession *realmSession = [MXRealmOlmOutboundGroupSession objectInRealm:self.realm forPrimaryKey:roomId]; - - MXLogDebug(@"[MXRealmCryptoStore] outboundGroupSessionWithRoomId: %@ -> %@", roomId, realmSession ? @"found" : @"not found"); - - if (realmSession) - { - session = [NSKeyedUnarchiver unarchiveObjectWithData:realmSession.sessionData]; - - if (!session) - { - MXLogDebug(@"[MXRealmCryptoStore] outboundGroupSessionWithRoomId: ERROR: Failed to create OLMOutboundGroupSession object"); - } - } - - if (session) - { - return [[MXOlmOutboundGroupSession alloc] initWithSession:session roomId:roomId creationTime:realmSession.creationTime]; - } - - return nil; -} - -- (NSArray *)outboundGroupSessions -{ - NSMutableArray *sessions = [NSMutableArray array]; - - for (MXRealmOlmOutboundGroupSession *realmSession in [MXRealmOlmOutboundGroupSession allObjectsInRealm:self.realm]) - { - MXOlmOutboundGroupSession * session = [[MXOlmOutboundGroupSession alloc] - initWithSession:[NSKeyedUnarchiver unarchiveObjectWithData:realmSession.sessionData] - roomId:realmSession.roomId - creationTime:realmSession.creationTime]; - [sessions addObject:session]; - } - - MXLogDebug(@"[MXRealmCryptoStore] outboundGroupSessions: found %lu entries", sessions.count); - return sessions; -} - -- (void)removeOutboundGroupSessionWithRoomId:(NSString*)roomId -{ - RLMRealm *realm = self.realm; - [realm transactionWithName:@"[MXRealmCryptoStore] removeOutboundGroupSessionWithRoomId" block:^{ - RLMResults *realmSessions = [MXRealmOlmOutboundGroupSession objectsInRealm:realm where:@"roomId = %@", roomId]; - - [realm deleteObjects:realmSessions]; - MXLogDebug(@"[MXRealmCryptoStore] removeOutboundGroupSessionWithRoomId%@: removed %lu entries", roomId, realmSessions.count); - }]; -} - -- (void)storeSharedDevices:(MXUsersDevicesMap *)devices messageIndex:(NSUInteger) messageIndex forOutboundGroupSessionInRoomWithId:(NSString *)roomId sessionId:(NSString *)sessionId -{ - NSDate *startDate = [NSDate date]; - - RLMRealm *realm = self.realm; - - [realm transactionWithName:@"[MXRealmCryptoStore] storeSharedDevices" block:^{ - - for (NSString *userId in [devices userIds]) - { - for (NSString *deviceId in [devices deviceIdsForUser:userId]) - { - MXRealmUser *realmUser = [MXRealmUser objectInRealm:realm forPrimaryKey:userId]; - if (!realmUser) - { - MXLogDebug(@"[MXRealmCryptoStore] storeSharedDevices cannot find user with the ID %@", userId); - continue; - } - - MXRealmDeviceInfo *realmDevice = [[realmUser.devices objectsWhere:@"deviceId = %@", deviceId] firstObject]; - if (!realmDevice) - { - MXLogDebug(@"[MXRealmCryptoStore] storeSharedDevices cannot find device with the ID %@", deviceId); - continue; - } - - MXRealmSharedOutboundSession *sharedInfo = [[MXRealmSharedOutboundSession alloc] initWithValue: @{ - @"roomId": roomId, - @"sessionId": sessionId, - @"device": realmDevice, - @"messageIndex": @(messageIndex) - }]; - [realm addObject:sharedInfo]; - } - } - }]; - - MXLogDebug(@"[MXRealmCryptoStore] storeSharedDevices (count: %tu) in %.3fms", devices.count, [[NSDate date] timeIntervalSinceDate:startDate] * 1000); -} - -- (MXUsersDevicesMap *)sharedDevicesForOutboundGroupSessionInRoomWithId:(NSString *)roomId sessionId:(NSString *)sessionId -{ - NSDate *startDate = [NSDate date]; - - MXUsersDevicesMap *devices = [MXUsersDevicesMap new]; - - RLMRealm *realm = self.realm; - - RLMResults *results = [MXRealmSharedOutboundSession objectsInRealm:realm where:@"roomId = %@ AND sessionId = %@", roomId, sessionId]; - - for (MXRealmSharedOutboundSession *sharedInfo in results) - { - MXDeviceInfo *deviceInfo = [NSKeyedUnarchiver unarchiveObjectWithData:sharedInfo.device.deviceInfoData]; - if (!deviceInfo) - { - MXLogDebug(@"[MXRealmCryptoStore] sharedDevicesForOutboundGroupSessionInRoomWithId cannot unarchive deviceInfo"); - continue; - } - [devices setObject:sharedInfo.messageIndex forUser:deviceInfo.userId andDevice:deviceInfo.deviceId]; - } - - MXLogDebug(@"[MXRealmCryptoStore] sharedDevicesForOutboundGroupSessionInRoomWithId (count: %tu) in %.3fms", results.count, [[NSDate date] timeIntervalSinceDate:startDate] * 1000); - - return devices; -} - -- (NSNumber *)messageIndexForSharedDeviceInRoomWithId:(NSString *)roomId sessionId:(NSString *)sessionId userId:(NSString *)userId deviceId:(NSString *)deviceId -{ - NSNumber *messageIndex = nil; - RLMRealm *realm = self.realm; - - RLMResults *sessions = [MXRealmSharedOutboundSession objectsInRealm:realm where:@"roomId = %@ AND sessionId = %@ AND device.deviceId = %@", roomId, sessionId, deviceId]; - for (MXRealmSharedOutboundSession *session in sessions) - { - MXDeviceInfo *deviceInfo = [NSKeyedUnarchiver unarchiveObjectWithData:session.device.deviceInfoData]; - if ([deviceInfo.userId isEqualToString:userId]) - { - messageIndex = session.messageIndex; - break; - } - } - - return messageIndex; -} - -#pragma mark - Key backup - -- (void)setBackupVersion:(NSString *)backupVersion -{ - MXRealmOlmAccount *account = self.accountInCurrentThread; - [account.realm transactionWithName:@"[MXRealmCryptoStore] setBackupVersion" block:^{ - account.backupVersion = backupVersion; - }]; -} - -- (NSString *)backupVersion -{ - MXRealmOlmAccount *account = self.accountInCurrentThread; - return account.backupVersion; -} - -- (void)resetBackupMarkers -{ - RLMRealm *realm = self.realm; - [realm transactionWithName:@"[MXRealmCryptoStore] resetBackupMarkers" block:^{ - - RLMResults *realmSessions = [MXRealmOlmInboundGroupSession allObjectsInRealm:realm]; - - for (MXRealmOlmInboundGroupSession *realmSession in realmSessions) - { - realmSession.backedUp = NO; - } - - [realm addOrUpdateObjects:realmSessions]; - }]; -} - -- (void)markBackupDoneForInboundGroupSessions:(NSArray*)sessions -{ - RLMRealm *realm = self.realm; - [realm transactionWithName:@"[MXRealmCryptoStore] markBackupDoneForInboundGroupSessions" block:^{ - - for (MXOlmInboundGroupSession *session in sessions) - { - NSString *sessionIdSenderKey = [MXRealmOlmInboundGroupSession primaryKeyWithSessionId:session.session.sessionIdentifier - senderKey:session.senderKey]; - MXRealmOlmInboundGroupSession *realmSession = [MXRealmOlmInboundGroupSession objectInRealm:realm forPrimaryKey:sessionIdSenderKey]; - - if (realmSession) - { - realmSession.backedUp = YES; - - [realm addOrUpdateObject:realmSession]; - } - } - }]; -} - -- (NSArray*)inboundGroupSessionsToBackup:(NSUInteger)limit -{ - NSMutableArray *sessions = [NSMutableArray new]; - - RLMRealm *realm = self.realm; - - RLMResults *realmSessions = [MXRealmOlmInboundGroupSession objectsInRealm:realm where:@"backedUp = NO"]; - - for (MXRealmOlmInboundGroupSession *realmSession in realmSessions) - { - MXOlmInboundGroupSession *session = [NSKeyedUnarchiver unarchiveObjectWithData:realmSession.olmInboundGroupSessionData]; - [sessions addObject:session]; - - if (sessions.count >= limit) - { - break; - } - } - - return sessions; -} - -- (NSUInteger)inboundGroupSessionsCount:(BOOL)onlyBackedUp -{ - RLMRealm *realm = self.realm; - RLMResults *realmSessions; - - if (onlyBackedUp) - { - realmSessions = [MXRealmOlmInboundGroupSession objectsInRealm:realm where:@"backedUp = YES"]; - } - else - { - realmSessions = [MXRealmOlmInboundGroupSession allObjectsInRealm:realm]; - } - - return realmSessions.count; -} - -#pragma mark - Key sharing - Outgoing key requests - -- (MXOutgoingRoomKeyRequest*)outgoingRoomKeyRequestWithRequestBody:(NSDictionary *)requestBody -{ - MXOutgoingRoomKeyRequest *request; - - NSString *requestBodyHash = [MXCryptoTools canonicalJSONStringForJSON:requestBody]; - - RLMResults *realmOutgoingRoomKeyRequests = [MXRealmOutgoingRoomKeyRequest objectsInRealm:self.realm where:@"requestBodyHash = %@", requestBodyHash]; - if (realmOutgoingRoomKeyRequests.count) - { - request = realmOutgoingRoomKeyRequests[0].outgoingRoomKeyRequest; - } - - return request; -} - -- (MXOutgoingRoomKeyRequest*)outgoingRoomKeyRequestWithState:(MXRoomKeyRequestState)state -{ - MXOutgoingRoomKeyRequest *request; - - RLMResults *realmOutgoingRoomKeyRequests = [MXRealmOutgoingRoomKeyRequest objectsInRealm:self.realm where:@"state = %@", @(state)]; - if (realmOutgoingRoomKeyRequests.count) - { - request = realmOutgoingRoomKeyRequests[0].outgoingRoomKeyRequest; - } - - return request; -} - -- (NSArray *)allOutgoingRoomKeyRequestsWithState:(MXRoomKeyRequestState)state -{ - NSMutableArray *allOutgoingRoomKeyRequests = [NSMutableArray array]; - - for (MXRealmOutgoingRoomKeyRequest *realmOutgoingRoomKeyRequest in [MXRealmOutgoingRoomKeyRequest allObjectsInRealm:self.realm]) - { - [allOutgoingRoomKeyRequests addObject:realmOutgoingRoomKeyRequest.outgoingRoomKeyRequest]; - } - - return allOutgoingRoomKeyRequests; -} - -- (void)storeOutgoingRoomKeyRequest:(MXOutgoingRoomKeyRequest*)request -{ - RLMRealm *realm = self.realm; - [realm transactionWithName:@"[MXRealmCryptoStore] storeOutgoingRoomKeyRequest" block:^{ - - NSString *requestBodyString = [MXTools serialiseJSONObject:request.requestBody]; - NSString *requestBodyHash = [MXCryptoTools canonicalJSONStringForJSON:request.requestBody]; - - MXRealmOutgoingRoomKeyRequest *realmOutgoingRoomKeyRequest = - [[MXRealmOutgoingRoomKeyRequest alloc] initWithValue:@{ - @"requestId": request.requestId, - @"recipientsData": [NSKeyedArchiver archivedDataWithRootObject:request.recipients], - @"requestBodyString": requestBodyString, - @"requestBodyHash": requestBodyHash, - @"state": @(request.state) - }]; - - realmOutgoingRoomKeyRequest.cancellationTxnId = request.cancellationTxnId; - - [realm addObject:realmOutgoingRoomKeyRequest]; - }]; -} - -- (void)updateOutgoingRoomKeyRequest:(MXOutgoingRoomKeyRequest*)request -{ - RLMRealm *realm = self.realm; - [realm transactionWithName:@"[MXRealmCryptoStore] updateOutgoingRoomKeyRequest" block:^{ - - MXRealmOutgoingRoomKeyRequest *realmOutgoingRoomKeyRequest = [MXRealmOutgoingRoomKeyRequest objectsInRealm:realm where:@"requestId = %@", request.requestId].firstObject; - - if (realmOutgoingRoomKeyRequest) - { - // Well, only the state changes - realmOutgoingRoomKeyRequest.state = @(request.state); - - [realm addOrUpdateObject:realmOutgoingRoomKeyRequest]; - } - }]; -} - -- (void)deleteOutgoingRoomKeyRequestWithRequestId:(NSString*)requestId -{ - RLMRealm *realm = self.realm; - [realm transactionWithName:@"[MXRealmCryptoStore] deleteOutgoingRoomKeyRequestWithRequestId" block:^{ - - RLMResults *realmOutgoingRoomKeyRequests = [MXRealmOutgoingRoomKeyRequest objectsInRealm:realm where:@"requestId = %@", requestId]; - - [realm deleteObjects:realmOutgoingRoomKeyRequests]; - }]; -} - - -#pragma mark - Key sharing - Incoming key requests - -- (void)storeIncomingRoomKeyRequest:(MXIncomingRoomKeyRequest*)request -{ - RLMRealm *realm = self.realm; - [realm transactionWithName:@"[MXRealmCryptoStore] storeIncomingRoomKeyRequest" block:^{ - - MXRealmIncomingRoomKeyRequest *realmIncomingRoomKeyRequest = - [[MXRealmIncomingRoomKeyRequest alloc] initWithValue:@{ - @"requestId": request.requestId, - @"userId": request.userId, - @"deviceId": request.deviceId, - @"requestBodyData": [NSKeyedArchiver archivedDataWithRootObject:request.requestBody] - }]; - [realm addObject:realmIncomingRoomKeyRequest]; - }]; -} - -- (void)deleteIncomingRoomKeyRequest:(NSString*)requestId fromUser:(NSString*)userId andDevice:(NSString*)deviceId -{ - RLMRealm *realm = self.realm; - [realm transactionWithName:@"[MXRealmCryptoStore] deleteIncomingRoomKeyRequest" block:^{ - - RLMResults *realmIncomingRoomKeyRequests = [MXRealmIncomingRoomKeyRequest objectsInRealm:realm where:@"requestId = %@ AND userId = %@ AND deviceId = %@", requestId, userId, deviceId]; - - [realm deleteObjects:realmIncomingRoomKeyRequests]; - }]; -} - -- (MXIncomingRoomKeyRequest*)incomingRoomKeyRequestWithRequestId:(NSString*)requestId fromUser:(NSString*)userId andDevice:(NSString*)deviceId -{ - RLMRealm *realm = self.realm; - - RLMResults *realmIncomingRoomKeyRequests = [MXRealmIncomingRoomKeyRequest objectsInRealm:realm where:@"requestId = %@ AND userId = %@ AND deviceId = %@", requestId, userId, deviceId]; - - return realmIncomingRoomKeyRequests.firstObject.incomingRoomKeyRequest; -} - -- (MXUsersDevicesMap *> *)incomingRoomKeyRequests -{ - MXUsersDevicesMap *> *incomingRoomKeyRequests = [[MXUsersDevicesMap alloc] init]; - - RLMRealm *realm = self.realm; - - RLMResults *realmIncomingRoomKeyRequests = [MXRealmIncomingRoomKeyRequest allObjectsInRealm:realm]; - for (MXRealmIncomingRoomKeyRequest *realmRequest in realmIncomingRoomKeyRequests) - { - MXIncomingRoomKeyRequest *request = realmRequest.incomingRoomKeyRequest; - - NSMutableArray *requests = [incomingRoomKeyRequests objectForDevice:request.deviceId forUser:request.userId]; - if (!requests) - { - requests = [[NSMutableArray alloc] init]; - [incomingRoomKeyRequests setObject:requests forUser:request.userId andDevice:request.deviceId]; - } - - [requests addObject:request]; - } - - return incomingRoomKeyRequests; -} - - -#pragma mark - Secret storage - -- (void)storeSecret:(NSString*)secret withSecretId:(NSString*)secretId -{ - RLMRealm *realm = self.realm; - [realm transactionWithName:@"[MXRealmCryptoStore] storeSecret" block:^{ - - MXRealmSecret *realmSecret; - - // Encrypt if enabled - NSData *key = self.encryptionKey; - if (key) - { - NSData *secretData = [secret dataUsingEncoding:NSUTF8StringEncoding]; - NSData *iv = [MXAes iv]; - - NSError *error; - NSData *encryptedSecret = [MXAes encrypt:secretData aesKey:key iv:iv error:&error]; - if (error) - { - MXLogDebug(@"[MXRealmCryptoStore] storeSecret: Encryption failed for secret %@. Error: %@", secretId, error); - return; - } - - realmSecret = [[MXRealmSecret alloc] initWithValue:@{ - @"secretId": secretId, - @"encryptedSecret": encryptedSecret, - @"iv": iv - }]; - } - else - { - realmSecret = [[MXRealmSecret alloc] initWithValue:@{ - @"secretId": secretId, - @"secret": secret, - }]; - } - - [realm addOrUpdateObject:realmSecret]; - }]; -} - -- (BOOL)hasSecretWithSecretId:(NSString *)secretId -{ - return [self secretWithSecretId:secretId] != nil; -} - -- (NSString*)secretWithSecretId:(NSString*)secretId -{ - MXRealmSecret *realmSecret = [MXRealmSecret objectsInRealm:self.realm where:@"secretId = %@", secretId].firstObject; - NSString *secret; - - if (realmSecret.encryptedSecret) - { - NSData *key = self.encryptionKey; - if (!key) - { - MXLogDebug(@"[MXRealmCryptoStore] secretWithSecretId: ERROR: Key to decrypt secret %@ is unavailable", secretId); - return nil; - } - - NSData *iv = realmSecret.iv; - if (!iv) - { - MXLogDebug(@"[MXRealmCryptoStore] secretWithSecretId: ERROR: IV for %@ is unavailable", secretId); - return nil; - } - - NSError *error; - NSData *secretData = [MXAes decrypt:realmSecret.encryptedSecret aesKey:key iv:iv error:&error]; - if (error || !secretData) - { - MXLogDebug(@"[MXRealmCryptoStore] secretWithSecretId: Decryption failed for secret %@. Error: %@", secretId, error); - return nil; - } - - secret = [[NSString alloc] initWithData:secretData encoding:NSUTF8StringEncoding]; - } - else - { - secret = realmSecret.secret; - } - return secret; -} - -- (void)deleteSecretWithSecretId:(NSString*)secretId -{ - RLMRealm *realm = self.realm; - [realm transactionWithName:@"[MXRealmCryptoStore] deleteSecretWithSecretId" block:^{ - [realm deleteObjects:[MXRealmSecret objectsInRealm:realm where:@"secretId = %@", secretId]]; - }]; -} - - -#pragma mark - Crypto settings - -- (BOOL)globalBlacklistUnverifiedDevices -{ - MXRealmOlmAccount *account = self.accountInCurrentThread; - return account.globalBlacklistUnverifiedDevices; -} - -- (void)setGlobalBlacklistUnverifiedDevices:(BOOL)globalBlacklistUnverifiedDevices -{ - MXRealmOlmAccount *account = self.accountInCurrentThread; - [account.realm transactionWithName:@"[MXRealmCryptoStore] setGlobalBlacklistUnverifiedDevices" block:^{ - account.globalBlacklistUnverifiedDevices = globalBlacklistUnverifiedDevices; - }]; -} - - -#pragma mark - Versioning - -- (MXCryptoVersion)cryptoVersion -{ - MXRealmOlmAccount *account = self.accountInCurrentThread; - return account.cryptoVersion; -} - --(void)setCryptoVersion:(MXCryptoVersion)cryptoVersion -{ - MXRealmOlmAccount *account = self.accountInCurrentThread; - [account.realm transactionWithName:@"[MXRealmCryptoStore] setCryptoVersion" block:^{ - account.cryptoVersion = cryptoVersion; - }]; -} - - -#pragma mark - Private methods -/** - Get Realm instance for the given user and device. - - @param userId User id for the Realm - @param deviceId Device id for the Realm - @param readOnly Flag to indicate whether Realm should be a read-only one. - @returns Desired Realm instance for the given parameters, or nil if cannot create such a Realm instance. For instance: if desired a read-only Realm but no real store exists. - */ -+ (nullable RLMRealm*)realmForUser:(NSString*)userId andDevice:(NSString*)deviceId readOnly:(BOOL)readOnly -{ - // Each user has its own db file. - // Else, it can lead to issue with primary keys. - // Ex: if 2 users are is the same encrypted room, [self storeAlgorithmForRoom] - // will be called twice for the same room id which breaks the uniqueness of the - // primary key (roomId) for this table. - NSURL *realmFileURL = [self realmFileURLForUserWithUserId:userId andDevice:deviceId]; - - if (readOnly && [[NSFileManager defaultManager] fileExistsAtPath:realmFileURL.path]) - { - // just open Realm once in writable mode to trigger migrations - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - @autoreleasepool { - [self realmForUser:userId andDevice:deviceId readOnly:NO]; - } - }); - } - - RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration]; - [self ensurePathExistenceForFileAtFileURL:realmFileURL]; - config.fileURL = realmFileURL; - - // Manage only our objects in this realm - config.objectClasses = @[ - MXRealmDeviceInfo.class, - MXRealmCrossSigningInfo.class, - MXRealmUser.class, - MXRealmRoomAlgorithm.class, - MXRealmOlmSession.class, - MXRealmOlmInboundGroupSession.class, - MXRealmOlmAccount.class, - MXRealmOutgoingRoomKeyRequest.class, - MXRealmIncomingRoomKeyRequest.class, - MXRealmSecret.class, - MXRealmOlmOutboundGroupSession.class, - MXRealmSharedOutboundSession.class - ]; - - config.schemaVersion = kMXRealmCryptoStoreVersion; - - __block BOOL cleanDuplicatedDevices = NO; - - // Set the block which will be called automatically when opening a Realm with a - // schema version lower than the one set above - config.migrationBlock = ^(RLMMigration *migration, uint64_t oldSchemaVersion) { - cleanDuplicatedDevices = [self finaliseMigrationWith:migration oldSchemaVersion:oldSchemaVersion]; - }; - - if (readOnly) - { - NSURL *readOnlyURL = [self readonlyURLFrom:config.fileURL]; - // copy to read-only file if needed - if ([[NSFileManager defaultManager] fileExistsAtPath:config.fileURL.path] && - ![[NSFileManager defaultManager] fileExistsAtPath:readOnlyURL.path]) - { - NSError *error; - NSDictionary *fileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:config.fileURL.path error:nil]; - unsigned long long fileSize = [[fileAttributes objectForKey:NSFileSize] unsignedLongLongValue]; - MXStopwatch *stopwatch = [MXStopwatch new]; - [[NSFileManager defaultManager] removeItemAtURL:readOnlyURL error:nil]; - [[NSFileManager defaultManager] copyItemAtURL:config.fileURL toURL:readOnlyURL error:&error]; - if (error) - { - MXLogDebug(@"[MXRealmCryptoStore] realmForUser: readonly copy file error: %@", error); - } - else - { - MXLogDebug(@"[MXRealmCryptoStore] realmForUser: readonly copy file lasted %@, fileSize: %@", [stopwatch readableIn:MXStopwatchMeasurementUnitMilliseconds], [MXTools fileSizeToString:fileSize round:NO]); - } - } - - if (![[NSFileManager defaultManager] fileExistsAtPath:readOnlyURL.path]) - { - MXLogDebug(@"[MXRealmCryptoStore] realmForUser: cannot create a read-only Realm for non-existent file."); - return nil; - } - config.fileURL = readOnlyURL; - config.readOnly = YES; - } - else - { - [self setupShouldCompactOnLaunch:config userId:userId deviceId:deviceId]; - } - - NSError *error; - RLMRealm *realm; - - @autoreleasepool - { - realm = [RLMRealm realmWithConfiguration:config error:&error]; - if (error) - { - MXLogDebug(@"[MXRealmCryptoStore] realmForUser gets error: %@", error); - - // Remove the db file - NSError *error; - [[NSFileManager defaultManager] removeItemAtPath:config.fileURL.path error:&error]; - MXLogDebug(@"[MXRealmCryptoStore] removeItemAtPath error result: %@", error); - - if (config.readOnly) - { - MXLogDebug(@"[MXRealmCryptoStore] realmForUser: returning nil for read-only Realm"); - return nil; - } - - // And try again - realm = [RLMRealm realmWithConfiguration:config error:&error]; - if (!realm) - { - MXLogDebug(@"[MXRealmCryptoStore] realmForUser still gets after reset. Error: %@", error); - } - - // Report this db reset to higher modules - // A user logout and in is anyway required to make crypto work reliably again - dispatch_async(dispatch_get_main_queue(),^{ - [[NSNotificationCenter defaultCenter] postNotificationName:kMXSessionCryptoDidCorruptDataNotification - object:userId - userInfo:nil]; - }); - } - } - - if (!readOnly) - { - if (cleanDuplicatedDevices) - { - MXLogDebug(@"[MXRealmCryptoStore] Do cleaning for duplicating devices"); - - NSUInteger before = [MXRealmDeviceInfo allObjectsInRealm:realm].count; - [self cleanDuplicatedDevicesInRealm:realm]; - NSUInteger after = [MXRealmDeviceInfo allObjectsInRealm:realm].count; - - MXLogDebug(@"[MXRealmCryptoStore] Cleaning for duplicating devices completed. There are now %@ devices. There were %@ before. %@ devices have been removed.", @(after), @(before), @(before - after)); - } - - // Wait for completion of other operations on this realm launched from other threads - [realm refresh]; - } - - return realm; -} - -+ (NSURL*)storeFolderURL -{ - // Check for a potential application group container - NSURL *sharedContainerURL = [[NSFileManager defaultManager] applicationGroupContainerURL]; - if (sharedContainerURL) - { - return [sharedContainerURL URLByAppendingPathComponent:kMXRealmCryptoStoreFolder]; - } - else - { - // Use the default URL - NSURL *defaultRealmPathURL = [RLMRealmConfiguration defaultConfiguration].fileURL.URLByDeletingLastPathComponent; - return [defaultRealmPathURL URLByAppendingPathComponent:kMXRealmCryptoStoreFolder]; - } -} - -// Return the realm db file to use for a given user and device -+ (NSURL*)realmFileURLForUserWithUserId:(NSString*)userId andDevice:(NSString*)deviceId -{ - // Default db file URL: use the default directory, but replace the filename with the userId. - NSString *fileName = [self realmFileNameWithUserId:userId - deviceId:deviceId]; - - return [[[self storeFolderURL] URLByAppendingPathComponent:fileName] URLByAppendingPathExtension:@"realm"]; -} - -/** - Gives the file name of the Realm file - - @param userId ID of the current user - @param deviceId ID of the current device (used for unit tests) - - @return the file name of the Realm file according to the given user and device IDs. - */ -+ (NSString *)realmFileNameWithUserId:(NSString *)userId deviceId:deviceId -{ - if (MXTools.isRunningUnitTests) - { - // Append the device id for unit tests so that we can run e2e tests - // with users with several devices - return [NSString stringWithFormat:@"%@-%@", userId, deviceId]; - } - - return userId; -} - -// Make sure the full path exists before giving it to Realm -+ (void)ensurePathExistenceForFileAtFileURL:(NSURL*)fileURL -{ - NSURL *fileFolderURL = fileURL.URLByDeletingLastPathComponent; - if (![NSFileManager.defaultManager fileExistsAtPath:fileFolderURL.path]) - { - MXLogDebug(@"[MXRealmCryptoStore] ensurePathExistenceForFileAtFileURL: Create full path hierarchy for %@", fileURL); - [[NSFileManager defaultManager] createDirectoryExcludedFromBackupAtPath:fileFolderURL.path error:nil]; - } -} - -/** - Gives the encryption key if encryption is needed - - @return the encryption key if encryption is needed. Nil otherwise. - */ -- (NSData *)encryptionKey -{ - // It is up to the app to provide a key for additional encryption - MXKeyData * keyData = [[MXKeyProvider sharedInstance] keyDataForDataOfType:MXCryptoOlmPickleKeyDataType isMandatory:NO expectedKeyType:kRawData]; - if (keyData && [keyData isKindOfClass:[MXRawDataKey class]]) - { - return ((MXRawDataKey *)keyData).key; - } - - return nil; -} - - -#pragma mark - shouldCompactOnLaunch - -/** - Set the shouldCompactOnLaunch block to the given RLMRealmConfiguration instance. - - @param config RLMRealmConfiguration instance to be set up. - @param userId ID of the current user. - @param deviceId ID of the current device. - */ -+ (void)setupShouldCompactOnLaunch:(RLMRealmConfiguration *)config userId:(NSString *)userId deviceId:deviceId -{ - config.shouldCompactOnLaunch = nil; - if ([self shouldCompactReamDBForUserWithUserId:userId andDevice:deviceId]) - { - config.shouldCompactOnLaunch = ^BOOL(NSUInteger totalBytes, NSUInteger bytesUsed) { - // totalBytes refers to the size of the file on disk in bytes (data + free space) - // usedBytes refers to the number of bytes used by data in the file - - static BOOL logDBFileSizeAtLaunch = YES; - if (logDBFileSizeAtLaunch) - { - MXLogDebug(@"[MXRealmCryptoStore] Realm DB file size (in bytes): %lu, used (in bytes): %lu", (unsigned long)totalBytes, (unsigned long)bytesUsed); - logDBFileSizeAtLaunch = NO; - } - - // Compact if the file is less than 50% 'used' - BOOL result = (float)((float)bytesUsed / totalBytes) < 0.5; - if (result) - { - MXLogDebug(@"[MXRealmCryptoStore] Will compact database: File size (in bytes): %lu, used (in bytes): %lu", (unsigned long)totalBytes, (unsigned long)bytesUsed); - } - - return result; - }; - } -} - -static BOOL shouldCompactOnLaunch = YES; -+ (BOOL)shouldCompactOnLaunch -{ - return shouldCompactOnLaunch; -} - -+ (void)setShouldCompactOnLaunch:(BOOL)theShouldCompactOnLaunch -{ - MXLogDebug(@"[MXRealmCryptoStore] setShouldCompactOnLaunch: %@", theShouldCompactOnLaunch ? @"YES" : @"NO"); - shouldCompactOnLaunch = theShouldCompactOnLaunch; -} - -// Ensure we compact the DB only once -+ (BOOL)shouldCompactReamDBForUserWithUserId:userId andDevice:(NSString*)deviceId -{ - if (!self.shouldCompactOnLaunch) - { - return NO; - } - - static NSMutableDictionary *compactedDB; - if (!compactedDB) - { - compactedDB = [NSMutableDictionary dictionary]; - } - - NSString *userDeviceId = [NSString stringWithFormat:@"%@-%@", userId, deviceId]; - if (compactedDB[userDeviceId]) - { - return NO; - } - - compactedDB[userDeviceId] = @(YES); - - return YES; -} - -#pragma mark - readOnly - -- (void)setReadOnly:(BOOL)readOnly -{ - MXLogDebug(@"[MXRealmCryptoStore] setReadOnly: %@", readOnly ? @"YES" : @"NO"); - _readOnly = readOnly; -} - -+ (NSURL *)readonlyURLFrom:(NSURL *)realmFileURL -{ - return [[[realmFileURL URLByDeletingPathExtension] URLByAppendingPathExtension:MXRealmCryptoStoreReadonlySuffix] URLByAppendingPathExtension:[MXRealmHelper realmFileExtension]]; -} - -#pragma mark - Schema migration -/** - Finalise migration performed by Realm. - - Basically fixes migration glitches between some versions of schema. - - @param migration A `RLMMigration` object used to perform the migration. The - migration object allows you to enumerate and alter any - existing objects which require migration. - - @param oldSchemaVersion The schema version of the Realm being migrated. - - @return YES if a clean up of duplicated devices should be performed. NO otherwise. - */ -+ (BOOL)finaliseMigrationWith:(RLMMigration *)migration oldSchemaVersion:(uint64_t)oldSchemaVersion -{ - BOOL cleanDuplicatedDevices = NO; - - // Note: There is nothing to do most of the time - // Realm will automatically detect new properties and removed properties - // And will update the schema on disk automatically - - if (oldSchemaVersion < kMXRealmCryptoStoreVersion) - { - MXLogDebug(@"[MXRealmCryptoStore] Required migration detected. oldSchemaVersion: %llu - current: %tu", oldSchemaVersion, kMXRealmCryptoStoreVersion); - - switch (oldSchemaVersion) - { - case 1: - { - // There was a bug in schema version #1 where inbound group sessions - // and olm sessions were duplicated: - // https://github.com/matrix-org/matrix-ios-sdk/issues/227 - - MXLogDebug(@"[MXRealmCryptoStore] Migration from schema #1 -> #2"); - - // We need to update the db because a sessionId property has been added MXRealmOlmSession - // to ensure uniqueness - MXLogDebug(@"[MXRealmCryptoStore] Add sessionId field to all MXRealmOlmSession objects"); - [migration enumerateObjects:MXRealmOlmSession.className block:^(RLMObject *oldObject, RLMObject *newObject) { - - OLMSession *olmSession = [NSKeyedUnarchiver unarchiveObjectWithData:oldObject[@"olmSessionData"]]; - - newObject[@"sessionId"] = olmSession.sessionIdentifier; - }]; - - // We need to clean the db from duplicated MXRealmOlmSessions - MXLogDebug(@"[MXRealmCryptoStore] Make MXRealmOlmSession objects unique for the (sessionId, deviceKey) pair"); - __block NSUInteger deleteCount = 0; - NSMutableArray *olmSessionUniquePairs = [NSMutableArray array]; - [migration enumerateObjects:MXRealmOlmSession.className block:^(RLMObject *oldObject, RLMObject *newObject) { - - NSString *olmSessionUniquePair = [NSString stringWithFormat:@"%@ - %@", newObject[@"sessionId"], newObject[@"deviceKey"]]; - - if (NSNotFound == [olmSessionUniquePairs indexOfObject:olmSessionUniquePair]) - { - [olmSessionUniquePairs addObject:olmSessionUniquePair]; - } - else - { - MXLogDebug(@"[MXRealmCryptoStore] - delete MXRealmOlmSession: %@", olmSessionUniquePair); - [migration deleteObject:newObject]; - deleteCount++; - } - }]; - - MXLogDebug(@"[MXRealmCryptoStore] -> deleted %tu duplicated MXRealmOlmSession objects", deleteCount); - - // And from duplicated MXRealmOlmInboundGroupSessions - MXLogDebug(@"[MXRealmCryptoStore] Make MXRealmOlmInboundGroupSession objects unique for the (sessionId, senderKey) pair"); - deleteCount = 0; - NSMutableArray *olmInboundGroupSessionUniquePairs = [NSMutableArray array]; - [migration enumerateObjects:MXRealmOlmInboundGroupSession.className block:^(RLMObject *oldObject, RLMObject *newObject) { - - NSString *olmInboundGroupSessionUniquePair = [NSString stringWithFormat:@"%@ - %@", newObject[@"sessionId"], newObject[@"senderKey"]]; - - if (NSNotFound == [olmInboundGroupSessionUniquePairs indexOfObject:olmInboundGroupSessionUniquePair]) - { - [olmInboundGroupSessionUniquePairs addObject:olmInboundGroupSessionUniquePair]; - } - else - { - MXLogDebug(@"[MXRealmCryptoStore] - delete MXRealmOlmInboundGroupSession: %@", olmInboundGroupSessionUniquePair); - [migration deleteObject:newObject]; - deleteCount++; - } - }]; - - MXLogDebug(@"[MXRealmCryptoStore] -> deleted %tu duplicated MXRealmOlmInboundGroupSession objects", deleteCount); - - MXLogDebug(@"[MXRealmCryptoStore] Migration from schema #1 -> #2 completed"); - } - - case 2: - MXLogDebug(@"[MXRealmCryptoStore] Migration from schema #2 -> #3: Nothing to do (add MXRealmOlmAccount.deviceSyncToken)"); - - case 3: - MXLogDebug(@"[MXRealmCryptoStore] Migration from schema #3 -> #4: Nothing to do (add MXRealmOlmAccount.globalBlacklistUnverifiedDevices & MXRealmRoomAlgortithm.blacklistUnverifiedDevices)"); - - case 4: - MXLogDebug(@"[MXRealmCryptoStore] Migration from schema #4 -> #5: Nothing to do (add deviceTrackingStatusData)"); - - case 5: - MXLogDebug(@"[MXRealmCryptoStore] Migration from schema #5 -> #6: Nothing to do (remove MXRealmOlmAccount.deviceAnnounced)"); - - case 6: - { - MXLogDebug(@"[MXRealmCryptoStore] Migration from schema #6 -> #7"); - - // We need to update the db because a sessionId property has been added to MXRealmOlmInboundGroupSession - // to ensure uniqueness - MXLogDebug(@"[MXRealmCryptoStore] Add sessionIdSenderKey, a combined primary key, to all MXRealmOlmInboundGroupSession objects"); - [migration enumerateObjects:MXRealmOlmInboundGroupSession.className block:^(RLMObject *oldObject, RLMObject *newObject) { - - newObject[@"sessionIdSenderKey"] = [MXRealmOlmInboundGroupSession primaryKeyWithSessionId:oldObject[@"sessionId"] - senderKey:oldObject[@"senderKey"]]; - }]; - - // We need to update the db because a identityKey property has been added to MXRealmDeviceInfo - MXLogDebug(@"[MXRealmCryptoStore] Add identityKey to all MXRealmDeviceInfo objects"); - [migration enumerateObjects:MXRealmDeviceInfo.className block:^(RLMObject *oldObject, RLMObject *newObject) { - - MXDeviceInfo *device = [NSKeyedUnarchiver unarchiveObjectWithData:oldObject[@"deviceInfoData"]]; - NSString *identityKey = device.identityKey; - if (identityKey) - { - newObject[@"identityKey"] = identityKey; - } - }]; - - MXLogDebug(@"[MXRealmCryptoStore] Migration from schema #6 -> #7 completed"); - } - - case 7: - { - MXLogDebug(@"[MXRealmCryptoStore] Migration from schema #7 -> #8"); - - // This schema update is only for cleaning duplicated devices. - // With the Realm Obj-C SDK, the realm instance is not public. We cannot - // make queries. So, the cleaning will be done afterwards. - cleanDuplicatedDevices = YES; - } - - case 8: - { - // MXRealmOlmSession.lastReceivedMessageTs has been added to implement: - // Use the last olm session that got a message - // https://github.com/vector-im/riot-ios/issues/2128 - - MXLogDebug(@"[MXRealmCryptoStore] Migration from schema #8 -> #9"); - - MXLogDebug(@"[MXRealmCryptoStore] Add lastReceivedMessageTs = 0 to all MXRealmOlmSession objects"); - [migration enumerateObjects:MXRealmOlmSession.className block:^(RLMObject *oldObject, RLMObject *newObject) { - - newObject[@"lastReceivedMessageTs"] = @(0); - }]; - - MXLogDebug(@"[MXRealmCryptoStore] Migration from schema #8 -> #9 completed"); - } - - case 9: - { - MXLogDebug(@"[MXRealmCryptoStore] Migration from schema #9 -> #10"); - - MXLogDebug(@"[MXRealmCryptoStore] Add requestBodyHash to all MXRealmOutgoingRoomKeyRequest objects"); - [migration enumerateObjects:MXRealmOutgoingRoomKeyRequest.className block:^(RLMObject *oldObject, RLMObject *newObject) { - - NSDictionary *requestBody = [MXTools deserialiseJSONString:oldObject[@"requestBodyString"]]; - if (requestBody) - { - newObject[@"requestBodyHash"] = [MXCryptoTools canonicalJSONStringForJSON:requestBody]; - } - }]; - - // This schema update needs a fix of cleanDuplicatedDevicesInRealm introduced in schema #8. - cleanDuplicatedDevices = YES; - } - - case 10: - MXLogDebug(@"[MXRealmCryptoStore] Migration from schema #10 -> #11: Nothing to do (added optional MXRealmUser.crossSigningKeys)"); - - case 11: - { - MXLogDebug(@"[MXRealmCryptoStore] Migration from schema #10 -> #11"); - - // Because of https://github.com/vector-im/riot-ios/issues/2896, algorithms were not stored - // Fix it by defaulting to usual values - MXLogDebug(@"[MXRealmCryptoStore] Fix missing algorithms to all MXRealmDeviceInfo objects"); - - [migration enumerateObjects:MXRealmDeviceInfo.className block:^(RLMObject *oldObject, RLMObject *newObject) { - - MXDeviceInfo *device = [NSKeyedUnarchiver unarchiveObjectWithData:oldObject[@"deviceInfoData"]]; - if (!device.algorithms) - { - device.algorithms = @[ - kMXCryptoOlmAlgorithm, - kMXCryptoMegolmAlgorithm - ]; - } - newObject[@"deviceInfoData"] = [NSKeyedArchiver archivedDataWithRootObject:device]; - }]; - } - - case 12: - { - MXLogDebug(@"[MXRealmCryptoStore] Migration from schema #12 -> #13"); - - // Ìntroduction of MXCryptoStore.cryptoVersion - // Set the default value - MXLogDebug(@"[MXRealmCryptoStore] Add new MXRealmOlmAccount.cryptoVersion. Set it to MXCryptoVersion1"); - - [migration enumerateObjects:MXRealmOlmAccount.className block:^(RLMObject *oldObject, RLMObject *newObject) { - newObject[@"cryptoVersion"] = @(MXCryptoVersion1); - }]; - } - - case 13: - MXLogDebug(@"[MXRealmCryptoStore] Migration from schema #13 -> #14: Nothing to do (added MXRealmOlmOutboundGroupSession)"); - - case 14: - MXLogDebug(@"[MXRealmCryptoStore] Migration from schema #14 -> #15: Nothing to do (added MXRealmSharedOutboundSession)"); - - case 15: - MXLogDebug(@"[MXRealmCryptoStore] Migration from schema #15 -> #16: Nothing to do (added optional MXRealmSecret.encryptedSecret)"); - - case 16: - MXLogDebug(@"[MXRealmCryptoStore] Migration from schema #16 -> #17"); - - MXLogDebug(@"[MXRealmCryptoStore] Make sure MXRealmOlmAccount.cryptoVersion is MXCryptoVersion2"); - [migration enumerateObjects:MXRealmOlmAccount.className block:^(RLMObject *oldObject, RLMObject *newObject) { - NSNumber *version; - MXJSONModelSetNumber(version, oldObject[@"cryptoVersion"]); - if (version && version.intValue == 0) - { - MXLogDebug(@"[MXRealmCryptoStore] -> Fix MXRealmOlmAccount.cryptoVersion"); - newObject[@"cryptoVersion"] = @(MXCryptoVersion2); - } - }]; - } - } - - return cleanDuplicatedDevices; -} - -/** - Clean duplicated & orphan devices. - - @param realm the DB instance to clean. - */ -+ (void)cleanDuplicatedDevicesInRealm:(RLMRealm*)realm -{ - [realm transactionWithName:@"[MXRealmCryptoStore] cleanDuplicatedDevicesInRealm" block:^{ - - // Due to a bug (https://github.com/vector-im/riot-ios/issues/2132), there were - // duplicated devices living in the database without no more relationship with - // their user. - // Keep only devices with a relationship with a user and delete all others. - for (MXRealmUser *realmUser in [MXRealmUser allObjectsInRealm:realm]) - { - for (MXRealmDeviceInfo *device in realmUser.devices) - { - if (!device.isInvalidated) - { - // The related device needs to be cloned in order to add it afterwards - MXRealmDeviceInfo *deviceCopy = [[MXRealmDeviceInfo alloc] initWithValue:device]; - - [realm deleteObjects:[MXRealmDeviceInfo objectsInRealm:realm where:@"identityKey = %@", device.identityKey]]; - - [realmUser.devices addObject:deviceCopy]; - } - } - } - }]; -} - -@end - -#endif diff --git a/MatrixSDK/Crypto/Devices/MXDeviceList.h b/MatrixSDK/Crypto/Devices/MXDeviceList.h deleted file mode 100644 index 00a3c6a35a..0000000000 --- a/MatrixSDK/Crypto/Devices/MXDeviceList.h +++ /dev/null @@ -1,184 +0,0 @@ -/* - Copyright 2017 Vector Creations 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. - */ - -#import - -#import "MXSDKOptions.h" - -#ifdef MX_CRYPTO - -#import "MXDeviceInfo.h" -#import "MXCryptoConstants.h" - -#import "MXRestClient.h" - -@class MXLegacyCrypto; - - -/** - State transition diagram for DeviceList.deviceTrackingStatus - - | - stopTrackingDeviceList V - +---------------------> NOT_TRACKED - | | - +<--------------------+ | startTrackingDeviceList - | | V - | +-------------> PENDING_DOWNLOAD <--------------------+-+ - | | ^ | | | - | | restart download | | start download | | invalidateUserDeviceList - | | client failed | | | | - | | | V | | - | +------------ DOWNLOAD_IN_PROGRESS -------------------+ | - | | | | - +<-------------------+ | download successful | - ^ V | - +----------------------- UP_TO_DATE ------------------------+ - - */ - -// Constants for DeviceList.deviceTrackingStatus -typedef enum : NSUInteger -{ - MXDeviceTrackingStatusNotTracked = 0, - MXDeviceTrackingStatusPendingDownload, - MXDeviceTrackingStatusDownloadInProgress, - MXDeviceTrackingStatusUnreachableServer, - MXDeviceTrackingStatusUpToDate - -} MXDeviceTrackingStatus; - -// Helper to transform a NSNumber stored in a NSDictionary to MXDeviceTrackingStatus -#define MXDeviceTrackingStatusFromNSNumber(aNSNumberObject) ((MXDeviceTrackingStatus)[aNSNumberObject integerValue]) - - -/** - `MXDeviceList` manages the list of other users' devices. - */ -@interface MXDeviceList : NSObject - -/** - The last sync token. - */ -@property (nonatomic) NSString *lastKnownSyncToken; - -/** - Constructor. - - @param crypto the MXCrypto instance. - @return a new MXDeviceList instance. - */ -- (id)initWithCrypto:(MXLegacyCrypto*)crypto; - -/** - Called when the client is stopped. - */ -- (void)close; - -/** - Download device and cross-sigining keys for a list of users and stores the keys in the MXStore. - - @param userIds The users to fetch. - @param forceDownload Always download the keys even if cached. - - @param success A block object called when the operation succeeds. - @param failure A block object called when the operation fails. - - @return a MXHTTPOperation instance. May be nil if the data is already in the store. - */ -- (MXHTTPOperation*)downloadKeys:(NSArray*)userIds forceDownload:(BOOL)forceDownload - success:(void (^)(MXUsersDevicesMap *usersDevicesInfoMap, - NSDictionary *crossSigningKeysMap))success - failure:(void (^)(NSError *error))failure; - -/** - Get the stored device keys for a user. - - @param userId the user to list keys for. - @return the list of devices. - */ -- (NSArray*)storedDevicesForUser:(NSString*)userId; - -/** - Get the stored keys for a single device. - - @param userId the user. - @param deviceId the device. - - @return the device, or nil if we don't know about this device - */ -- (MXDeviceInfo*)storedDevice:(NSString*)userId deviceId:(NSString*)deviceId; - -/** - Find a device by curve25519 identity key - - @param algorithm the encryption algorithm. - @param senderKey the curve25519 key to match. - @return the device info. - */ -- (MXDeviceInfo*)deviceWithIdentityKey:(NSString*)senderKey andAlgorithm:(NSString*)algorithm; - -/** - Flag the given user for device-list tracking, if they are not already. - - This will mean that a subsequent call to refreshOutdatedDeviceLists - will download the device list for the user, and that subsequent calls to - invalidateUserDeviceList will trigger more updates. - - @param userId the user to start to track devices. - */ -- (void)startTrackingDeviceList:(NSString*)userId; - -/** - Mark the given user as no longer being tracked for device-list updates. - - This won't affect any in-progress downloads, which will still go on to - complete; it will just mean that we don't think that we have an up-to-date - list for future calls to downloadKeys. - - @param userId the user to stop to track devices. - */ -- (void)stopTrackingDeviceList:(NSString*)userId; - -/** - Mark the cached device list for the given user outdated. - - If we are not tracking this user's devices, we'll do nothing. Otherwise - we flag the user as needing an update. - - This doesn't set off an update, so that several users can be batched - together. Call refreshOutdatedDeviceLists for that. - - @param userId the user whose devices must be invalidated. - */ -- (void)invalidateUserDeviceList:(NSString*)userId; - -/** - Mark all tracked device lists as outdated. - - This will flag each user whose devices we are tracking as in need of an - update. - */ -- (void)invalidateAllDeviceLists; - -/** - If we have users who have outdated device lists, start key downloads for them. - */ -- (void)refreshOutdatedDeviceLists; - -@end - -#endif diff --git a/MatrixSDK/Crypto/Devices/MXDeviceList.m b/MatrixSDK/Crypto/Devices/MXDeviceList.m deleted file mode 100644 index 244ee50e94..0000000000 --- a/MatrixSDK/Crypto/Devices/MXDeviceList.m +++ /dev/null @@ -1,462 +0,0 @@ -/* - Copyright 2017 Vector Creations Ltd - Copyright 2018 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. - */ - -#import "MXDeviceList.h" - -#ifdef MX_CRYPTO - -#import "MXCrypto_Private.h" - -#import "MXDeviceListOperationsPool.h" -#import "MXTools.h" - -@interface MXDeviceList () -{ - __weak MXLegacyCrypto *crypto; - - // Users we are tracking device status for. - // userId -> MXDeviceTrackingStatus* - NSMutableDictionary *deviceTrackingStatus; - - // The current request for each user. - // userId -> MXDeviceListOperation - NSMutableDictionary *keyDownloadsInProgressByUser; - - /** - The pool which the http request is currenlty being processed. - (nil if there is no current request). - - Note that currentPoolQuery.usersIds corresponds to the inProgressUsersWithNewDevices - ivar we used before. - */ - MXDeviceListOperationsPool *currentQueryPool; - - /** - When currentPoolQuery is already being processed, all download - requests go in this pool which will be launched once currentPoolQuery is - complete. - */ - MXDeviceListOperationsPool *nextQueryPool; -} -@end - - -@implementation MXDeviceList - -- (id)initWithCrypto:(MXLegacyCrypto *)theCrypto -{ - self = [super init]; - if (self) - { - crypto = theCrypto; - - // Retrieve tracking status from the store - deviceTrackingStatus = [NSMutableDictionary dictionaryWithDictionary:[crypto.store deviceTrackingStatus]]; - - keyDownloadsInProgressByUser = [NSMutableDictionary dictionary]; - - for (NSString *userId in deviceTrackingStatus.allKeys) - { - // if a download was in progress or failed when we got shut down, it isn't any more. - MXDeviceTrackingStatus trackingStatus = MXDeviceTrackingStatusFromNSNumber(deviceTrackingStatus[userId]); - if (trackingStatus == MXDeviceTrackingStatusDownloadInProgress - || trackingStatus == MXDeviceTrackingStatusUnreachableServer) - { - deviceTrackingStatus[userId] = @(MXDeviceTrackingStatusPendingDownload); - } - } - } - return self; -} - -- (void)close -{ - crypto = nil; -} - -- (MXHTTPOperation*)downloadKeys:(NSArray*)userIds forceDownload:(BOOL)forceDownload - success:(void (^)(MXUsersDevicesMap *usersDevicesInfoMap, - NSDictionary *crossSigningKeysMap))success - failure:(void (^)(NSError *error))failure -{ - MXLogDebug(@"[MXDeviceList] downloadKeys(forceDownload: %d) for %tu users", forceDownload, userIds.count); - - NSMutableArray *usersToDownload = [NSMutableArray array]; - BOOL doANewQuery = NO; - - for (NSString *userId in userIds) - { - if (![MXTools isMatrixUserIdentifier:userId]) - { - MXLogDebug(@"[MXDeviceList] downloadKeys: ERROR: Ignore malformed user id: %@", userId); - continue; - } - - MXDeviceTrackingStatus trackingStatus = MXDeviceTrackingStatusFromNSNumber(deviceTrackingStatus[userId]); - - if (trackingStatus == MXDeviceTrackingStatusDownloadInProgress) - { - // already a key download in progress/queued for this user; its results - // will be good enough for us. - [usersToDownload addObject:userId]; - } - else if (forceDownload - || (trackingStatus != MXDeviceTrackingStatusUpToDate && trackingStatus != MXDeviceTrackingStatusUnreachableServer)) - { - [usersToDownload addObject:userId]; - doANewQuery = YES; - } - else - { - MXLogDebug(@"[MXDeviceList] downloadKeys: Skip user %@. trackingStatus: %@", userId, @(trackingStatus)); - } - } - - MXDeviceListOperation *operation; - - if (usersToDownload.count) - { - MXLogDebug(@"[MXDeviceList] downloadKeys for actually %tu users: %@", usersToDownload.count, usersToDownload); - - for (NSString *userId in usersToDownload) - { - deviceTrackingStatus[userId] = @(MXDeviceTrackingStatusDownloadInProgress); - } - - // Persist the tracking status before launching download - [self persistDeviceTrackingStatus]; - - MXWeakify(self); - __block MXWeakify(operation); - weakoperation = operation = [[MXDeviceListOperation alloc] initWithUserIds:usersToDownload success:^(NSArray *succeededUserIds, NSArray *failedUserIds) { - MXStrongifyAndReturnIfNil(self); - MXStrongifyAndReturnIfNil(operation); - - MXLogDebug(@"[MXDeviceList] downloadKeys (operation: %p) -> DONE", operation); - - for (NSString *userId in succeededUserIds) - { - // we may have queued up another download request for this user - // since we started this request. If that happens, we should - // ignore the completion of the first one. - if (self->keyDownloadsInProgressByUser[userId] != operation) - { - MXLogDebug(@"[MXDeviceList] downloadKeys: Another update (operation: %p) in the queue for %@ - not marking up-to-date", self->keyDownloadsInProgressByUser[userId], userId); - continue; - } - [self->keyDownloadsInProgressByUser removeObjectForKey:userId]; - - MXDeviceTrackingStatus trackingStatus = MXDeviceTrackingStatusFromNSNumber(self->deviceTrackingStatus[userId]); - if (trackingStatus == MXDeviceTrackingStatusDownloadInProgress) - { - // we didn't get any new invalidations since this download started: - // this user's device list is now up to date. - self->deviceTrackingStatus[userId] = @(MXDeviceTrackingStatusUpToDate); - } - } - - if (failedUserIds.count) - { - MXLogDebug(@"[MXDeviceList] downloadKeys. Error updating device keys for users %@", failedUserIds); - - for (NSString *userId in failedUserIds) - { - MXDeviceTrackingStatus trackingStatus = MXDeviceTrackingStatusFromNSNumber(self->deviceTrackingStatus[userId]); - if (trackingStatus == MXDeviceTrackingStatusDownloadInProgress) - { - self->deviceTrackingStatus[userId] = @(MXDeviceTrackingStatusUnreachableServer); - } - } - } - - [self persistDeviceTrackingStatus]; - - if (success) - { - MXUsersDevicesMap *usersDevicesInfoMap = [self devicesForUsers:userIds]; - NSDictionary *crossSigningKeysMap = [self crossSigningKeysForUsers:userIds]; - success(usersDevicesInfoMap, crossSigningKeysMap); - } - - } failure:failure]; - - for (NSString *userId in usersToDownload) - { - keyDownloadsInProgressByUser[userId] = operation; - } - - if (currentQueryPool && doANewQuery == NO) - { - // Before reusing currentQueryPool, make sure it has all requested user ids - BOOL containsAllUserIds = [currentQueryPool hasUsers:operation.userIds]; - if (containsAllUserIds == NO) - { - // It does not have all - // Some user ids are in nextQueryPool - doANewQuery = YES; - } - } - - if (doANewQuery || !currentQueryPool) - { - MXLogDebug(@"[MXDeviceList] downloadKeys: waiting for next key query. Operation: %p", operation); - - [self startOrQueueDeviceQuery:operation]; - } - else - { - MXLogDebug(@"[MXDeviceList] downloadKeys: waiting for in-flight query to complete. Operation: %p", operation); - [operation addToPool:currentQueryPool]; - } - } - else - { - MXLogDebug(@"[MXDeviceList] downloadKeys: already have all necessary keys"); - if (success) - { - success([self devicesForUsers:userIds], - [self crossSigningKeysForUsers:userIds]); - } - } - - return operation; -} - -- (NSArray *)storedDevicesForUser:(NSString *)userId -{ - return [crypto.store devicesForUser:userId].allValues; -} - -- (MXDeviceInfo*)storedDevice:(NSString*)userId deviceId:(NSString*)deviceId -{ - return [crypto.store devicesForUser:userId][deviceId]; -} - -- (MXDeviceInfo *)deviceWithIdentityKey:(NSString *)senderKey andAlgorithm:(NSString *)algorithm -{ - if (![algorithm isEqualToString:kMXCryptoOlmAlgorithm] - && ![algorithm isEqualToString:kMXCryptoMegolmAlgorithm]) - { - // We only deal in olm keys - return nil; - } - - return [crypto.store deviceWithIdentityKey:senderKey]; -} - -- (void)startTrackingDeviceList:(NSString*)userId -{ - if (![MXTools isMatrixUserIdentifier:userId]) - { - MXLogDebug(@"[MXDeviceList] startTrackingDeviceList: ERROR: Ignore malformed user id: %@", userId); - return; - } - - MXDeviceTrackingStatus trackingStatus = MXDeviceTrackingStatusFromNSNumber(deviceTrackingStatus[userId]); - - if (!trackingStatus) - { - MXLogDebug(@"[MXDeviceList] Now tracking device list for %@", userId); - deviceTrackingStatus[userId] = @(MXDeviceTrackingStatusPendingDownload); - } - // we don't yet persist the tracking status, since there may be a lot - // of calls; instead we wait for the forthcoming - // refreshOutdatedDeviceLists. -} - -- (void)stopTrackingDeviceList:(NSString *)userId -{ - MXDeviceTrackingStatus trackingStatus = MXDeviceTrackingStatusFromNSNumber(deviceTrackingStatus[userId]); - - if (trackingStatus) - { - MXLogDebug(@"[MXDeviceList] No longer tracking device list for %@", userId); - deviceTrackingStatus[userId] = @(MXDeviceTrackingStatusNotTracked); - } - // we don't yet persist the tracking status, since there may be a lot - // of calls; instead we wait for the forthcoming - // refreshOutdatedDeviceLists. -} - -- (void)invalidateUserDeviceList:(NSString *)userId -{ - MXDeviceTrackingStatus trackingStatus = MXDeviceTrackingStatusFromNSNumber(deviceTrackingStatus[userId]); - - if (trackingStatus) - { - MXLogDebug(@"[MXDeviceList] Marking device list outdated for %@", userId); - deviceTrackingStatus[userId] = @(MXDeviceTrackingStatusPendingDownload); - } - // we don't yet persist the tracking status, since there may be a lot - // of calls; instead we wait for the forthcoming - // refreshOutdatedDeviceLists. -} - -- (void)invalidateAllDeviceLists; -{ - for (NSString *userId in deviceTrackingStatus.allKeys) - { - [self invalidateUserDeviceList:userId]; - } -} - -- (void)refreshOutdatedDeviceLists -{ - NSMutableArray *users = [NSMutableArray array]; - // copy dictionary in order to avoid mutations when enumerating. - // no need to deep copy - NSDictionary *deviceTrackingStatusCopy = [deviceTrackingStatus copy]; - for (NSString *userId in deviceTrackingStatusCopy) - { - MXDeviceTrackingStatus trackingStatus = MXDeviceTrackingStatusFromNSNumber(deviceTrackingStatusCopy[userId]); - if (trackingStatus == MXDeviceTrackingStatusPendingDownload) - // || trackingStatus == MXDeviceTrackingStatusUnreachableServer) // TODO: It would be nice to retry them sometimes. - // At the moment, they are retried after app restart - { - [users addObject:userId]; - } - } - - if (users.count) - { - [self downloadKeys:users forceDownload:NO success:^(MXUsersDevicesMap *usersDevicesInfoMap, NSDictionary *crossSigningKeysMap) { - MXLogDebug(@"[MXDeviceList] refreshOutdatedDeviceLists (users: %tu): %@", usersDevicesInfoMap.userIds.count, usersDevicesInfoMap.userIds); - } failure:^(NSError *error) { - MXLogDebug(@"[MXDeviceList] refreshOutdatedDeviceLists: ERROR updating device keys for users %@", users); - }]; - } -} - -- (void)persistDeviceTrackingStatus -{ - [crypto.store storeDeviceTrackingStatus:deviceTrackingStatus]; -} - -/** - Get the stored device keys for a list of user ids. - - @param userIds the list of users to list keys for. - @return users devices. -*/ -- (MXUsersDevicesMap *)devicesForUsers:(NSArray*)userIds -{ - MXUsersDevicesMap *usersDevicesInfoMap = [[MXUsersDevicesMap alloc] init]; - - for (NSString *userId in userIds) - { - // Retrive the data from the store - NSDictionary *devices = [crypto.store devicesForUser:userId]; - if (devices) - { - [usersDevicesInfoMap setObjects:devices forUser:userId]; - } - } - - MXLogDebug(@"[MXDeviceList] devicesForUsers: %@", usersDevicesInfoMap); - - return usersDevicesInfoMap; -} - -/** - Get the stored cross-signing keys for a list of user ids. - - @param userIds the list of users to list keys for. - @return users cross-signing keys. - */ -- (NSDictionary *)crossSigningKeysForUsers:(NSArray*)userIds -{ - NSMutableDictionary *crossSigningKeysMap = [NSMutableDictionary dictionary]; - - for (NSString *userId in userIds) - { - // Retrive the data from the store - MXCrossSigningInfo *crossSigningKeys = [crypto.store crossSigningKeysForUser:userId]; - if (crossSigningKeys) - { - crossSigningKeysMap[userId] = crossSigningKeys; - } - } - - return crossSigningKeysMap; -} - - -- (void)startOrQueueDeviceQuery:(MXDeviceListOperation *)operation -{ - if (!currentQueryPool) - { - // No pool is currently being queried - if (nextQueryPool) - { - // Launch the query for the existing next pool - currentQueryPool = nextQueryPool; - nextQueryPool = nil; - } - else - { - // Create a new pool to query right now - currentQueryPool = [[MXDeviceListOperationsPool alloc] initWithCrypto:crypto]; - } - - [operation addToPool:currentQueryPool]; - [self startCurrentPoolQuery]; - } - else - { - // Append the device list operation to the next pool - if (!nextQueryPool) - { - nextQueryPool = [[MXDeviceListOperationsPool alloc] initWithCrypto:crypto]; - } - [operation addToPool:nextQueryPool]; - } -} - -- (void)startCurrentPoolQuery -{ - MXLogDebug(@"[MXDeviceList] startCurrentPoolQuery(pool: %p) (users: %tu): %@", currentQueryPool, currentQueryPool.userIds.count, currentQueryPool.userIds); - - if (currentQueryPool.userIds) - { - NSString *token = _lastKnownSyncToken; - - // Add token - MXWeakify(self); - [currentQueryPool downloadKeys:token complete:^(NSDictionary *failedUserIds) { - MXStrongifyAndReturnIfNil(self); - - MXLogDebug(@"[MXDeviceList] startCurrentPoolQuery(pool: %p) -> DONE. failedUserIds (users: %tu): %@", self->currentQueryPool, failedUserIds.count, failedUserIds); - - if (token) - { - [self->crypto.store storeDeviceSyncToken:token]; - } - - self->currentQueryPool = nil; - if (self->nextQueryPool) - { - self->currentQueryPool = self->nextQueryPool; - self->nextQueryPool = nil; - [self startCurrentPoolQuery]; - } - }]; - } -} - -@end - -#endif diff --git a/MatrixSDK/Crypto/KeyBackup/Data/Aes256/MXAes256KeyBackupAlgorithm.h b/MatrixSDK/Crypto/KeyBackup/Data/Aes256/MXAes256KeyBackupAlgorithm.h deleted file mode 100644 index ab29b8879e..0000000000 --- a/MatrixSDK/Crypto/KeyBackup/Data/Aes256/MXAes256KeyBackupAlgorithm.h +++ /dev/null @@ -1,26 +0,0 @@ -// -// Copyright 2022 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 -#import "MXKeyBackupAlgorithm.h" - -NS_ASSUME_NONNULL_BEGIN - -@interface MXAes256KeyBackupAlgorithm : NSObject - -@end - -NS_ASSUME_NONNULL_END diff --git a/MatrixSDK/Crypto/KeyBackup/Data/Aes256/MXAes256KeyBackupAlgorithm.m b/MatrixSDK/Crypto/KeyBackup/Data/Aes256/MXAes256KeyBackupAlgorithm.m deleted file mode 100644 index 0dfa110bc5..0000000000 --- a/MatrixSDK/Crypto/KeyBackup/Data/Aes256/MXAes256KeyBackupAlgorithm.m +++ /dev/null @@ -1,315 +0,0 @@ -// -// Copyright 2022 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 "MXAes256KeyBackupAlgorithm.h" - -#import "MXCrypto_Private.h" - -#import -#import "MXKeyBackupPassword.h" -#import "MXTools.h" -#import "MXBase64Tools.h" -#import "MXSharedHistoryKeyService.h" -#import "MXAes256BackupAuthData.h" -#import "MXSecretStorage_Private.h" -#import "MXEncryptedSecretContent.h" - -@interface MXAes256KeyBackupAlgorithm () - -@property (nonatomic, strong) MXLegacyCrypto *crypto; - -@property (nonatomic, strong) MXAes256BackupAuthData *authData; - -@property (nonatomic, copy) MXKeyBackupPrivateKeyGetterBlock keyGetterBlock; - -@end - -@implementation MXAes256KeyBackupAlgorithm - -+ (NSString *)algorithmName -{ - return kMXCryptoAes256KeyBackupAlgorithm; -} - -- (instancetype)initWithCrypto:(MXLegacyCrypto *)crypto authData:(id)authData keyGetterBlock:(nonnull MXKeyBackupPrivateKeyGetterBlock)keyGetterBlock -{ - if (self = [super init]) - { - if (authData == nil || ![authData isKindOfClass:MXAes256BackupAuthData.class]) - { - MXLogError(@"[MXAes256KeyBackupAlgorithm] init: auth data missing required information"); - return nil; - } - self.crypto = crypto; - self.authData = (MXAes256BackupAuthData *)authData; - self.keyGetterBlock = keyGetterBlock; - } - return self; -} - -+ (MXKeyBackupPreparationInfo *)prepareWith:(NSString*)password error:(NSError *__autoreleasing _Nullable *)error -{ - NSData *privateKey; - MXAes256BackupAuthData *authData = [MXAes256BackupAuthData new]; - - if (!password) - { - privateKey = [OLMUtility randomBytesOfLength:32]; - } - else - { - NSString *salt; - NSUInteger iterations; - privateKey = [MXKeyBackupPassword generatePrivateKeyWithPassword:password salt:&salt iterations:&iterations error:error]; - authData.privateKeySalt = salt; - authData.privateKeyIterations = iterations; - } - - if (*error) - { - MXLogErrorDetails(@"[MXAes256KeyBackupAlgorithm] prepare", @{ - @"error": *error ?: @"unknown" - }); - return nil; - } - - MXEncryptedSecretContent *secret = [self.class calculateKeyCheck:privateKey iv:nil]; - authData.iv = secret.iv; - authData.mac = secret.mac; - - return [[MXKeyBackupPreparationInfo alloc] initWithPrivateKey:privateKey authData:authData]; -} - -- (BOOL)keyMatches:(NSData *)privateKey error:(NSError *__autoreleasing _Nullable *)error -{ - return [self.class keyMatches:privateKey withAuthData:_authData.JSONDictionary error:error]; -} - -+ (BOOL)keyMatches:(NSData *)privateKey withAuthData:(NSDictionary *)authData error:(NSError *__autoreleasing _Nullable *)error -{ - if (authData[@"mac"]) - { - MXEncryptedSecretContent *encrypted = [self.class calculateKeyCheck:privateKey iv:authData[@"iv"]]; - - // MACs should match - // Compare bytes instead of base64 strings to avoid base64 padding issue - NSData *authDataMac = [MXBase64Tools dataFromBase64:authData[@"mac"]]; - NSData *encryptedMac = encrypted.mac ? [MXBase64Tools dataFromBase64:encrypted.mac] : nil; - - return [authDataMac isEqualToData:encryptedMac]; - } - else - { - // if we have no information, we have to assume the key is right - return YES; - } -} - -+ (BOOL)isUntrusted -{ - return NO; -} - -- (MXKeyBackupData *)encryptGroupSession:(MXOlmInboundGroupSession *)session -{ - NSData *privateKey = self.privateKey; - if (!privateKey) - { - MXLogDebug(@"[MXAes256KeyBackupAlgorithm] encryptGroupSession: Error: No private key"); - return nil; - } - // Build the m.megolm_backup.v1.aes-hmac-sha2 data as defined at - // https://github.com/uhoreg/matrix-doc/blob/symmetric-backups/proposals/3270-symmetric-megolm-backup.md#encryption - MXMegolmSessionData *sessionData = session.exportSessionData; - if (![sessionData checkFieldsBeforeEncryption]) - { - MXLogDebug(@"[MXAes256KeyBackupAlgorithm] encryptGroupSession: Error: Invalid MXMegolmSessionData for %@", session.senderKey); - return nil; - } - - BOOL sharedHistory = MXSDKOptions.sharedInstance.enableRoomSharedHistoryOnInvite && sessionData.sharedHistory; - NSDictionary *sessionBackupData = @{ - @"algorithm": sessionData.algorithm, - @"sender_key": sessionData.senderKey, - @"sender_claimed_keys": sessionData.senderClaimedKeys, - @"forwarding_curve25519_key_chain": sessionData.forwardingCurve25519KeyChain ?: @[], - @"session_key": sessionData.sessionKey, - kMXSharedHistoryKeyName: @(sharedHistory), - @"untrusted": @(sessionData.isUntrusted) - }; - - MXSecretStorage *storage = [[MXSecretStorage alloc] init]; - NSString *secret = [MXTools serialiseJSONObject:sessionBackupData]; - - NSError *error; - MXEncryptedSecretContent *encryptedSessionBackupData = [storage encryptSecret:secret - withSecretId:session.session.sessionIdentifier - privateKey:privateKey - iv:nil - error:&error]; - - if (![self checkEncryptedSecretContent:encryptedSessionBackupData]) - { - MXLogDebug(@"[MXAes256KeyBackupAlgorithm] encryptGroupSession: Error: Invalid MXEncryptedSecretContent for %@", session.senderKey); - return nil; - } - - // Gather information for each key - MXDeviceInfo *device = [_crypto.deviceList deviceWithIdentityKey:session.senderKey andAlgorithm:kMXCryptoMegolmAlgorithm]; - - // Build backup data for that key - MXKeyBackupData *keyBackupData = [MXKeyBackupData new]; - keyBackupData.firstMessageIndex = session.session.firstKnownIndex; - keyBackupData.forwardedCount = session.forwardingCurve25519KeyChain.count; - keyBackupData.verified = device ? device.trustLevel.isVerified : NO; - keyBackupData.sessionData = @{ - @"ciphertext": encryptedSessionBackupData.ciphertext, - @"mac": encryptedSessionBackupData.mac, - @"iv": encryptedSessionBackupData.iv, - }; - - return keyBackupData; -} - -- (MXMegolmSessionData *)decryptKeyBackupData:(MXKeyBackupData *)keyBackupData forSession:(NSString *)sessionId inRoom:(NSString *)roomId -{ - NSData *privateKey = self.privateKey; - if (!privateKey) - { - MXLogDebug(@"[MXAes256KeyBackupAlgorithm] decryptKeyBackupData: Error: No private key"); - return nil; - } - MXMegolmSessionData *sessionData; - - MXEncryptedSecretContent *encryptedSecret = [MXEncryptedSecretContent new]; - - MXJSONModelSetString(encryptedSecret.ciphertext, keyBackupData.sessionData[@"ciphertext"]); - MXJSONModelSetString(encryptedSecret.mac, keyBackupData.sessionData[@"mac"]); - MXJSONModelSetString(encryptedSecret.iv, keyBackupData.sessionData[@"iv"]); - - if ([self checkEncryptedSecretContent:encryptedSecret]) - { - MXSecretStorage *secretStorage = [MXSecretStorage new]; - - NSError *error; - NSString *text = [secretStorage decryptSecretWithSecretId:sessionId secretContent:encryptedSecret withPrivateKey:privateKey error:&error]; - - if (!error) - { - NSDictionary *sessionBackupData = [MXTools deserialiseJSONString:text]; - - if (sessionBackupData) - { - MXJSONModelSetMXJSONModel(sessionData, MXMegolmSessionData, sessionBackupData); - - sessionData.sessionId = sessionId; - sessionData.roomId = roomId; - sessionData.untrusted |= self.class.isUntrusted; - } - } - else - { - MXLogDebug(@"[MXAes256KeyBackupAlgorithm] decryptKeyBackupData: Failed to decrypt session from backup. Error: %@", error); - } - } - - return sessionData; -} - -+ (BOOL)checkBackupVersion:(MXKeyBackupVersion *)backupVersion -{ - NSString *iv = backupVersion.authData[@"iv"]; - NSString *mac = backupVersion.authData[@"mac"]; - return iv != nil && mac != nil; -} - -+ (id)authDataFromJSON:(NSDictionary *)JSON error:(NSError *__autoreleasing _Nullable *)error -{ - MXAes256BackupAuthData *authData = [MXAes256BackupAuthData modelFromJSON:JSON]; - if (authData.iv && authData.mac && authData.signatures) - { - return authData; - } - else - { - MXLogError(@"[MXAes256KeyBackupAlgorithm] authDataFromJSON: Auth data is missing required data"); - - *error = [NSError errorWithDomain:MXKeyBackupErrorDomain - code:MXKeyBackupErrorMissingAuthDataCode - userInfo:@{ - NSLocalizedDescriptionKey: @"Auth data is missing required data" - }]; - - return nil; - } -} - -#pragma mark - Private - -- (NSData*)privateKey -{ - NSData *key = self.keyGetterBlock(); - if (!key) - { - MXLogError(@"[MXAes256KeyBackupAlgorithm] privateKey: missing private key"); - return nil; - } - if (![self keyMatches:key error:nil]) - { - MXLogError(@"[MXAes256KeyBackupAlgorithm] privateKey: Private key does not match"); - return nil; - } - return key; -} - -// Sanity checks on MXEncryptedSecretContent -- (BOOL)checkEncryptedSecretContent:(MXEncryptedSecretContent*)encryptedSecret -{ - if (!encryptedSecret.ciphertext) - { - MXLogDebug(@"[MXAes256KeyBackupAlgorithm] checkEncryptedSecretContent: missing ciphertext"); - return NO; - } - if (!encryptedSecret.mac) - { - MXLogDebug(@"[MXAes256KeyBackupAlgorithm] checkEncryptedSecretContent: missing mac"); - return NO; - } - if (!encryptedSecret.iv) - { - MXLogDebug(@"[MXAes256KeyBackupAlgorithm] checkEncryptedSecretContent: missing iv"); - return NO; - } - - return YES; -} - -/// Calculate the MAC for checking the key. -/// @param key the key to use -/// @param iv The initialization vector as a base64-encoded string. If not provided, a random initialization vector will be created. -+ (MXEncryptedSecretContent *)calculateKeyCheck:(NSData *)key iv:(NSString *)iv -{ - MXSecretStorage *storage = [[MXSecretStorage alloc] init]; - NSData *ivData = iv ? [MXBase64Tools dataFromBase64:iv] : nil; - if (ivData.length == 0) - { - ivData = nil; - } - NSError *error; - return [storage encryptedZeroStringWithPrivateKey:key iv:ivData error:&error]; -} - -@end diff --git a/MatrixSDK/Crypto/KeyBackup/Data/Curve25519/MXCurve25519KeyBackupAlgorithm.h b/MatrixSDK/Crypto/KeyBackup/Data/Curve25519/MXCurve25519KeyBackupAlgorithm.h deleted file mode 100644 index d9eb3de215..0000000000 --- a/MatrixSDK/Crypto/KeyBackup/Data/Curve25519/MXCurve25519KeyBackupAlgorithm.h +++ /dev/null @@ -1,26 +0,0 @@ -// -// Copyright 2022 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 -#import "MXKeyBackupAlgorithm.h" - -NS_ASSUME_NONNULL_BEGIN - -@interface MXCurve25519KeyBackupAlgorithm : NSObject - -@end - -NS_ASSUME_NONNULL_END diff --git a/MatrixSDK/Crypto/KeyBackup/Data/Curve25519/MXCurve25519KeyBackupAlgorithm.m b/MatrixSDK/Crypto/KeyBackup/Data/Curve25519/MXCurve25519KeyBackupAlgorithm.m deleted file mode 100644 index 83bd421b0d..0000000000 --- a/MatrixSDK/Crypto/KeyBackup/Data/Curve25519/MXCurve25519KeyBackupAlgorithm.m +++ /dev/null @@ -1,266 +0,0 @@ -// -// Copyright 2022 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 "MXCurve25519KeyBackupAlgorithm.h" - -#import "MXCrypto_Private.h" -#import -#import "MXKeyBackupPassword.h" -#import "MXTools.h" -#import "MXBase64Tools.h" -#import "MXError.h" -#import "MXSharedHistoryKeyService.h" -#import "MXCurve25519BackupAuthData.h" - -@interface MXCurve25519KeyBackupAlgorithm () - -@property (nonatomic, strong) MXLegacyCrypto *crypto; - -/** - The backup key being used. - */ -@property (nonatomic, nullable) OLMPkEncryption *backupKey; - -@property (nonatomic, strong) MXCurve25519BackupAuthData *authData; - -@property (nonatomic, copy) MXKeyBackupPrivateKeyGetterBlock keyGetterBlock; - -@end - -@implementation MXCurve25519KeyBackupAlgorithm - -+ (NSString *)algorithmName -{ - return kMXCryptoCurve25519KeyBackupAlgorithm; -} - -- (instancetype)initWithCrypto:(nonnull MXLegacyCrypto *)crypto - authData:(nonnull id)authData - keyGetterBlock:(nonnull MXKeyBackupPrivateKeyGetterBlock)keyGetterBlock -{ - if (self = [super init]) - { - if (authData == nil || ![authData isKindOfClass:MXCurve25519BackupAuthData.class] || ((MXCurve25519BackupAuthData *)authData).publicKey == nil) - { - MXLogError(@"[MXCurve25519KeyBackupAlgorithm] init: auth data missing required information"); - return nil; - } - self.crypto = crypto; - self.authData = (MXCurve25519BackupAuthData *)authData; - self.backupKey = [OLMPkEncryption new]; - [self.backupKey setRecipientKey:self.authData.publicKey]; - self.keyGetterBlock = keyGetterBlock; - } - return self; -} - -+ (MXKeyBackupPreparationInfo *)prepareWith:(NSString *)password error:(NSError *__autoreleasing _Nullable *)error -{ - OLMPkDecryption *decryption = [OLMPkDecryption new]; - MXCurve25519BackupAuthData *authData = [MXCurve25519BackupAuthData new]; - - if (!password) - { - authData.publicKey = [decryption generateKey:error]; - } - else - { - NSString *salt; - NSUInteger iterations; - NSData *privateKey = [MXKeyBackupPassword generatePrivateKeyWithPassword:password salt:&salt iterations:&iterations error:error]; - authData.privateKeySalt = salt; - authData.privateKeyIterations = iterations; - authData.publicKey = [decryption setPrivateKey:privateKey error:error]; - } - - if (*error) - { - MXLogErrorDetails(@"[MXCurve25519KeyBackupAlgorithm] prepare", @{ - @"error": *error ?: @"unknown" - }); - return nil; - } - return [[MXKeyBackupPreparationInfo alloc] initWithPrivateKey:decryption.privateKey authData:authData]; -} - -- (BOOL)keyMatches:(NSData *)privateKey error:(NSError *__autoreleasing _Nullable *)error -{ - return [self.class keyMatches:privateKey withAuthData:_authData.JSONDictionary error:error]; -} - -+ (BOOL)keyMatches:(NSData *)privateKey withAuthData:(NSDictionary *)authData error:(NSError *__autoreleasing _Nullable *)error -{ - return [[self.class publicKeyFrom:privateKey] isEqualToString:authData[@"public_key"]]; -} - -+ (BOOL)isUntrusted -{ - return YES; -} - -- (MXKeyBackupData *)encryptGroupSession:(MXOlmInboundGroupSession *)session -{ - // 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 - MXMegolmSessionData *sessionData = session.exportSessionData; - if (![sessionData checkFieldsBeforeEncryption]) - { - MXLogDebug(@"[MXCurve25519KeyBackupAlgorithm] encryptGroupSession: Error: Invalid MXMegolmSessionData for %@", session.senderKey); - return nil; - } - - BOOL sharedHistory = MXSDKOptions.sharedInstance.enableRoomSharedHistoryOnInvite && sessionData.sharedHistory; - NSDictionary *sessionBackupData = @{ - @"algorithm": sessionData.algorithm, - @"sender_key": sessionData.senderKey, - @"sender_claimed_keys": sessionData.senderClaimedKeys, - @"forwarding_curve25519_key_chain": sessionData.forwardingCurve25519KeyChain ? sessionData.forwardingCurve25519KeyChain : @[], - @"session_key": sessionData.sessionKey, - kMXSharedHistoryKeyName: @(sharedHistory) - }; - OLMPkMessage *encryptedSessionBackupData = [_backupKey encryptMessage:[MXTools serialiseJSONObject:sessionBackupData] error:nil]; - if (![self checkOLMPkMessage:encryptedSessionBackupData]) - { - MXLogDebug(@"[MXCurve25519KeyBackupAlgorithm] encryptGroupSession: Error: Invalid OLMPkMessage for %@", session.senderKey); - return nil; - } - - // Gather information for each key - MXDeviceInfo *device = [_crypto.deviceList deviceWithIdentityKey:session.senderKey andAlgorithm:kMXCryptoMegolmAlgorithm]; - - // Build backup data for that key - MXKeyBackupData *keyBackupData = [MXKeyBackupData new]; - keyBackupData.firstMessageIndex = session.session.firstKnownIndex; - keyBackupData.forwardedCount = session.forwardingCurve25519KeyChain.count; - keyBackupData.verified = device ? device.trustLevel.isVerified : NO; - keyBackupData.sessionData = @{ - @"ciphertext": encryptedSessionBackupData.ciphertext, - @"mac": encryptedSessionBackupData.mac, - @"ephemeral": encryptedSessionBackupData.ephemeralKey, - }; - - return keyBackupData; -} - -- (MXMegolmSessionData *)decryptKeyBackupData:(MXKeyBackupData *)keyBackupData forSession:(NSString *)sessionId inRoom:(NSString *)roomId -{ - MXMegolmSessionData *sessionData; - - NSString *ciphertext, *mac, *ephemeralKey; - - MXJSONModelSetString(ciphertext, keyBackupData.sessionData[@"ciphertext"]); - MXJSONModelSetString(mac, keyBackupData.sessionData[@"mac"]); - MXJSONModelSetString(ephemeralKey, keyBackupData.sessionData[@"ephemeral"]); - - if (ciphertext && mac && ephemeralKey) - { - OLMPkMessage *encrypted = [[OLMPkMessage alloc] initWithCiphertext:ciphertext mac:mac ephemeralKey:ephemeralKey]; - - OLMPkDecryption *decryption = [OLMPkDecryption new]; - NSError *error; - NSData *privateKey = self.keyGetterBlock(); - if (!privateKey) - { - MXLogDebug(@"[MXCurve25519KeyBackupAlgorithm] decryptKeyBackupData: No private key."); - return nil; - } - [decryption setPrivateKey:privateKey error:&error]; - NSString *text = [decryption decryptMessage:encrypted error:&error]; - - if (!error) - { - NSDictionary *sessionBackupData = [MXTools deserialiseJSONString:text]; - - if (sessionBackupData) - { - MXJSONModelSetMXJSONModel(sessionData, MXMegolmSessionData, sessionBackupData); - - sessionData.sessionId = sessionId; - sessionData.roomId = roomId; - sessionData.untrusted |= self.class.isUntrusted; - } - } - else - { - MXLogDebug(@"[MXCurve25519KeyBackupAlgorithm] decryptKeyBackupData: Failed to decrypt session from backup. Error: %@", error); - } - } - - return sessionData; -} - -+ (BOOL)checkBackupVersion:(MXKeyBackupVersion *)backupVersion -{ - return backupVersion.authData[@"public_key"] != nil; -} - -+ (id)authDataFromJSON:(NSDictionary *)JSON error:(NSError *__autoreleasing _Nullable *)error -{ - MXCurve25519BackupAuthData *authData = [MXCurve25519BackupAuthData modelFromJSON:JSON]; - if (authData.publicKey && authData.signatures) - { - return authData; - } - else - { - MXLogError(@"[MXCurve25519KeyBackupAlgorithm] authDataFromJSON: Auth data is missing required data"); - - *error = [NSError errorWithDomain:MXKeyBackupErrorDomain - code:MXKeyBackupErrorMissingAuthDataCode - userInfo:@{ - NSLocalizedDescriptionKey: @"Auth data is missing required data" - }]; - - return nil; - } -} - -#pragma mark - Private - -+ (NSString*)publicKeyFrom:(NSData*)privateKey -{ - if (privateKey) - { - // Built the PK decryption with it - OLMPkDecryption *decryption = [OLMPkDecryption new]; - return [decryption setPrivateKey:privateKey error:nil]; - } - return nil; -} - -// Sanity checks on OLMPkMessage -- (BOOL)checkOLMPkMessage:(OLMPkMessage*)message -{ - if (!message.ciphertext) - { - MXLogDebug(@"[MXCurve25519KeyBackupAlgorithm] checkOLMPkMessage: missing ciphertext"); - return NO; - } - if (!message.mac) - { - MXLogDebug(@"[MXCurve25519KeyBackupAlgorithm] checkOLMPkMessage: missing mac"); - return NO; - } - if (!message.ephemeralKey) - { - MXLogDebug(@"[MXCurve25519KeyBackupAlgorithm] checkOLMPkMessage: missing ephemeralKey"); - return NO; - } - - return YES; -} - -@end diff --git a/MatrixSDK/Crypto/KeyBackup/Engine/MXKeyBackupEngine.h b/MatrixSDK/Crypto/KeyBackup/Engine/MXKeyBackupEngine.h index a327dcbc76..b7b040b130 100644 --- a/MatrixSDK/Crypto/KeyBackup/Engine/MXKeyBackupEngine.h +++ b/MatrixSDK/Crypto/KeyBackup/Engine/MXKeyBackupEngine.h @@ -20,7 +20,6 @@ #import "MXMegolmSessionData.h" #import "MXMegolmBackupCreationInfo.h" #import "MXKeyBackupVersionTrust.h" -#import "MXKeyBackupAlgorithm.h" #import "MXKeyBackupData.h" NS_ASSUME_NONNULL_BEGIN diff --git a/MatrixSDK/Crypto/KeyBackup/Engine/MXNativeKeyBackupEngine.h b/MatrixSDK/Crypto/KeyBackup/Engine/MXNativeKeyBackupEngine.h deleted file mode 100644 index 8bc4342a6f..0000000000 --- a/MatrixSDK/Crypto/KeyBackup/Engine/MXNativeKeyBackupEngine.h +++ /dev/null @@ -1,37 +0,0 @@ -// -// Copyright 2022 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. -// - -#ifndef MXNativeKeyBackupEngine_h -#define MXNativeKeyBackupEngine_h - -#import "MXKeyBackupEngine.h" - -NS_ASSUME_NONNULL_BEGIN - -@interface MXNativeKeyBackupEngine : NSObject - -- (instancetype)initWithCrypto:(MXLegacyCrypto *)crypto; - -/** - The backup algorithm being used. Nil if key backup not enabled yet. - */ -@property (nonatomic, nullable, readonly) id keyBackupAlgorithm; - -@end - -NS_ASSUME_NONNULL_END - -#endif /* MXNativeKeyBackupEngine_h */ diff --git a/MatrixSDK/Crypto/KeyBackup/Engine/MXNativeKeyBackupEngine.m b/MatrixSDK/Crypto/KeyBackup/Engine/MXNativeKeyBackupEngine.m deleted file mode 100644 index bf51d56dcf..0000000000 --- a/MatrixSDK/Crypto/KeyBackup/Engine/MXNativeKeyBackupEngine.m +++ /dev/null @@ -1,724 +0,0 @@ -// -// Copyright 2022 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 -#import "MXNativeKeyBackupEngine.h" -#import "MXCrypto.h" -#import "MXCrypto_Private.h" -#import "MXCrossSigning_Private.h" -#import "MXKeyBackupAlgorithm.h" -#import "OLMInboundGroupSession.h" -#import "MatrixSDKSwiftHeader.h" -#import "MXRecoveryKey.h" -#import "MXKeyBackupData.h" - -/** - Maximum number of keys to send at a time to the homeserver. - */ -NSUInteger const kMXKeyBackupSendKeysMaxCount = 100; -NSUInteger const kMXKeyBackupImportBatchSize = 1000; - -static NSDictionary> *AlgorithmClassesByName; -static Class DefaultAlgorithmClass; - -@interface MXNativeKeyBackupEngine () - -@property (nonatomic, weak) MXLegacyCrypto *crypto; -@property (nonatomic, nullable) MXKeyBackupVersion *keyBackupVersion; -@property (nonatomic, nullable) id keyBackupAlgorithm; -@property (nonatomic, nullable) NSProgress *activeImportProgress; -@property (nonatomic, nullable) dispatch_queue_t importQueue; - -@end - -@implementation MXNativeKeyBackupEngine - -+ (void)initialize -{ - if (MXSDKOptions.sharedInstance.enableSymmetricBackup) - { - AlgorithmClassesByName = @{ - kMXCryptoCurve25519KeyBackupAlgorithm: MXCurve25519KeyBackupAlgorithm.class, - kMXCryptoAes256KeyBackupAlgorithm: MXAes256KeyBackupAlgorithm.class - }; - } - else - { - AlgorithmClassesByName = @{ - kMXCryptoCurve25519KeyBackupAlgorithm: MXCurve25519KeyBackupAlgorithm.class, - }; - } - DefaultAlgorithmClass = MXCurve25519KeyBackupAlgorithm.class; -} - -- (instancetype)initWithCrypto:(MXLegacyCrypto *)crypto -{ - self = [self init]; - if (self) - { - _crypto = crypto; - _importQueue = dispatch_queue_create(@"MXNativeKeyBackupEngine".UTF8String, DISPATCH_QUEUE_SERIAL); - } - return self; -} - -#pragma mark - Enable / Disable engine - -- (BOOL)enabled -{ - return self.version != nil; -} - -- (NSString *)version -{ - return self.crypto.store.backupVersion; -} - -- (BOOL)enableBackupWithKeyBackupVersion:(MXKeyBackupVersion *)keyBackupVersion error:(NSError **)error -{ - [self validateKeyBackupVersion:keyBackupVersion]; - - id authData = [self authDataFromKeyBackupVersion:keyBackupVersion error:error]; - if (!authData) - { - return NO; - } - - self.keyBackupVersion = keyBackupVersion; - self.crypto.store.backupVersion = keyBackupVersion.version; - Class algorithmClass = AlgorithmClassesByName[keyBackupVersion.algorithm]; - // store the desired backup algorithm - MXWeakify(self); - self.keyBackupAlgorithm = [[algorithmClass alloc] initWithCrypto:self.crypto authData:authData keyGetterBlock:^NSData * _Nullable{ - MXStrongifyAndReturnValueIfNil(self, nil); - return self.privateKey; - }]; - MXLogDebug(@"[MXNativeKeyBackupEngine] enableBackupWithVersion: Algorithm set to: %@", self.keyBackupAlgorithm); - return YES; -} - -- (void)disableBackup -{ - self.keyBackupVersion = nil; - self.crypto.store.backupVersion = nil; - [self.crypto.store deleteSecretWithSecretId:MXSecretId.keyBackup]; - self.keyBackupAlgorithm = nil; - - // Reset backup markers - [self.crypto.store resetBackupMarkers]; -} - -#pragma mark - Private / Recovery key management - -- (nullable NSData *)privateKey -{ - NSString *privateKeyBase64 = [self.crypto.store secretWithSecretId:MXSecretId.keyBackup]; - if (!privateKeyBase64) - { - MXLogDebug(@"[MXNativeKeyBackupEngine] privateKey. Error: No secret in crypto store"); - return nil; - } - - return [MXBase64Tools dataFromBase64:privateKeyBase64]; -} - -- (void)savePrivateKey:(NSData *)privateKey version:(NSString *)version -{ - NSString *privateKeyBase64 = [MXBase64Tools unpaddedBase64FromData:privateKey]; - [self.crypto.store storeSecret:privateKeyBase64 withSecretId:MXSecretId.keyBackup]; -} - -- (BOOL)hasValidPrivateKey -{ - NSData *privateKey = self.privateKey; - if (!privateKey) - { - MXLogDebug(@"[MXNativeKeyBackupEngine] hasValidPrivateKey: No private key"); - return NO; - } - - NSError *error; - BOOL keyMatches = [self.keyBackupAlgorithm keyMatches:privateKey error:&error]; - if (!keyMatches) - { - MXLogDebug(@"[MXNativeKeyBackupEngine] hasValidPrivateKey: Error: Private key does not match: %@", error); - [self.crypto.store deleteSecretWithSecretId:MXSecretId.keyBackup]; - return NO; - } - return YES; -} - -- (BOOL)hasValidPrivateKeyForKeyBackupVersion:(MXKeyBackupVersion *)keyBackupVersion -{ - NSData *privateKey = self.privateKey; - if (!privateKey) - { - MXLogDebug(@"[MXNativeKeyBackupEngine] hasValidPrivateKeyForKeyBackupVersion: No private key"); - return NO; - } - - NSError *error; - id algorithm = [self getOrCreateKeyBackupAlgorithmFor:keyBackupVersion privateKey:privateKey]; - BOOL keyMatches = [algorithm keyMatches:privateKey error:&error]; - if (!keyMatches) - { - MXLogDebug(@"[MXNativeKeyBackupEngine] hasValidPrivateKeyForKeyBackupVersion: Error: Private key does not match: %@", error); - return NO; - } - return YES; -} - -- (NSData *)validPrivateKeyForRecoveryKey:(NSString *)recoveryKey - forKeyBackupVersion:(MXKeyBackupVersion *)keyBackupVersion - error:(NSError **)error -{ - NSData *privateKey = [MXRecoveryKey decode:recoveryKey error:error]; - - if (*error) - { - MXLogDebug(@"[MXNativeKeyBackupEngine] isValidRecoveryKey: Invalid recovery key. Error: %@", *error); - - // Return a generic error - *error = [NSError errorWithDomain:MXKeyBackupErrorDomain - code:MXKeyBackupErrorInvalidRecoveryKeyCode - userInfo:@{ - NSLocalizedDescriptionKey: @"Invalid recovery key or password" - }]; - return nil; - } - - Class algorithm = AlgorithmClassesByName[keyBackupVersion.algorithm]; - if (algorithm == NULL) - { - MXLogDebug(@"[MXNativeKeyBackupEngine] isValidRecoveryKey: unknown algorithm: %@", keyBackupVersion.algorithm); - - *error = [NSError errorWithDomain:MXKeyBackupErrorDomain - code:MXKeyBackupErrorUnknownAlgorithm - userInfo:@{ - NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Unknown algorithm (%@)", keyBackupVersion.algorithm] - }]; - return nil; - } - BOOL result = [algorithm keyMatches:privateKey withAuthData:keyBackupVersion.authData error:error]; - - if (!result) - { - MXLogDebug(@"[MXNativeKeyBackupEngine] isValidRecoveryKey: Public keys mismatch"); - - *error = [NSError errorWithDomain:MXKeyBackupErrorDomain - code:MXKeyBackupErrorInvalidRecoveryKeyCode - userInfo:@{ - NSLocalizedDescriptionKey: @"Invalid recovery key or password: public keys mismatch" - }]; - } - - return privateKey; -} - -- (nullable NSString*)recoveryKeyFromPassword:(NSString *)password - inKeyBackupVersion:(MXKeyBackupVersion *)keyBackupVersion - error:(NSError **)error -{ - // Extract MXBaseKeyBackupAuthData - id authData = [self authDataFromKeyBackupVersion:keyBackupVersion error:error]; - if (*error) - { - return nil; - } - - if (!authData.privateKeySalt || !authData.privateKeyIterations) - { - MXLogDebug(@"[MXNativeKeyBackupEngine] recoveryFromPassword: Salt and/or iterations not found in key backup auth data"); - *error = [NSError errorWithDomain:MXKeyBackupErrorDomain - code:MXKeyBackupErrorMissingPrivateKeySaltCode - userInfo:@{ - NSLocalizedDescriptionKey: @"Salt and/or iterations not found in key backup auth data" - }]; - return nil; - } - - - // Extract the recovery key from the passphrase - NSData *recoveryKeyData = [MXKeyBackupPassword retrievePrivateKeyWithPassword:password salt:authData.privateKeySalt iterations:authData.privateKeyIterations error:error]; - if (*error) - { - MXLogDebug(@"[MXNativeKeyBackupEngine] recoveryFromPassword: retrievePrivateKeyWithPassword failed: %@", *error); - return nil; - } - - return [MXRecoveryKey encode:recoveryKeyData]; -} - -#pragma mark - Backup versions - -- (void)prepareKeyBackupVersionWithPassword:(NSString *)password - algorithm:(NSString *)algorithm - success:(void (^)(MXMegolmBackupCreationInfo *))success - failure:(void (^)(NSError *))failure -{ - Class algorithmClass = algorithm ? AlgorithmClassesByName[algorithm] : DefaultAlgorithmClass; - if (algorithmClass == NULL) - { - if (failure) - { - dispatch_async(dispatch_get_main_queue(), ^{ - NSError *error = [NSError errorWithDomain:MXKeyBackupErrorDomain - code:MXKeyBackupErrorUnknownAlgorithm - userInfo:@{ - NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Unknown algorithm (%@) to prepare the backup", algorithm] - }]; - failure(error); - }); - } - return; - } - NSError *error; - MXKeyBackupPreparationInfo *preparationInfo = [algorithmClass prepareWith:password error:&error]; - if (error) - { - if (failure) - { - dispatch_async(dispatch_get_main_queue(), ^{ - failure(error); - }); - } - return; - } - id authData = preparationInfo.authData; - - MXMegolmBackupCreationInfo *keyBackupCreationInfo = [MXMegolmBackupCreationInfo new]; - keyBackupCreationInfo.algorithm = [algorithmClass algorithmName]; - keyBackupCreationInfo.authData = authData; - keyBackupCreationInfo.recoveryKey = [MXRecoveryKey encode:preparationInfo.privateKey]; - - NSString *myUserId = self.crypto.matrixRestClient.credentials.userId; - NSMutableDictionary *signatures = [NSMutableDictionary dictionary]; - - NSDictionary *deviceSignature = [self.crypto signObject:authData.signalableJSONDictionary]; - [signatures addEntriesFromDictionary:deviceSignature[myUserId]]; - - if ([self.crypto.crossSigning canCrossSign] == NO) - { - authData.signatures = @{myUserId: signatures}; - keyBackupCreationInfo.authData = authData; - - if (success) - { - dispatch_async(dispatch_get_main_queue(), ^{ - success(keyBackupCreationInfo); - }); - } - - return; - } - - [self.crossSigning signObject:authData.signalableJSONDictionary withKeyType:MXCrossSigningKeyType.master success:^(NSDictionary *signedObject) { - - [signatures addEntriesFromDictionary:signedObject[@"signatures"][myUserId]]; - - authData.signatures = @{myUserId: signatures}; - keyBackupCreationInfo.authData = authData; - - if (success) - { - dispatch_async(dispatch_get_main_queue(), ^{ - success(keyBackupCreationInfo); - }); - } - } failure:^(NSError *error) { - if (failure) - { - dispatch_async(dispatch_get_main_queue(), ^{ - failure(error); - }); - } - }]; -} - -- (MXKeyBackupVersionTrust *)trustForKeyBackupVersion:(MXKeyBackupVersion *)keyBackupVersion -{ - NSString *myUserId = self.crypto.matrixRestClient.credentials.userId; - - MXKeyBackupVersionTrust *keyBackupVersionTrust = [MXKeyBackupVersionTrust new]; - - NSError *error; - id authData = [self authDataFromKeyBackupVersion:keyBackupVersion error:&error]; - if (error) - { - MXLogDebug(@"[MXNativeKeyBackupEngine] trustForKeyBackupVersion: Key backup is absent or missing required data"); - return keyBackupVersionTrust; - } - - NSDictionary *mySigs = authData.signatures[myUserId]; - NSMutableArray *signatures = [NSMutableArray array]; - for (NSString *keyId in mySigs) - { - // XXX: is this how we're supposed to get the device id? - NSString *deviceId; - NSArray *components = [keyId componentsSeparatedByString:@":"]; - if (components.count == 2) - { - deviceId = components[1]; - } - - if (deviceId) - { - BOOL valid = NO; - - MXDeviceInfo *device = [self.crypto.deviceList storedDevice:myUserId deviceId:deviceId]; - if (device) - { - NSError *error; - valid = [self.crypto.olmDevice verifySignature:device.fingerprint JSON:authData.signalableJSONDictionary signature:mySigs[keyId] error:&error]; - - if (!valid) - { - MXLogDebug(@"[MXNativeKeyBackupEngine] trustForKeyBackupVersion: Bad signature from device %@: %@", device.deviceId, error); - } - - MXKeyBackupVersionTrustSignature *signature = [MXKeyBackupVersionTrustSignature new]; - signature.deviceId = deviceId; - signature.device = device; - signature.valid = valid; - [signatures addObject:signature]; - } - else // Try interpreting it as the MSK public key - { - NSError *error; - BOOL valid = [self.crossSigning.crossSigningTools pkVerifyObject:authData.JSONDictionary userId:myUserId publicKey:deviceId error:&error]; - - if (!valid) - { - MXLogDebug(@"[MXNativeKeyBackupEngine] trustForKeyBackupVersion: Signature with unknown key %@", deviceId); - } - else - { - MXKeyBackupVersionTrustSignature *signature = [MXKeyBackupVersionTrustSignature new]; - signature.keys = deviceId; - signature.valid = valid; - [signatures addObject:signature]; - } - } - } - } - - keyBackupVersionTrust.signatures = signatures; - - for (MXKeyBackupVersionTrustSignature *signature in keyBackupVersionTrust.signatures) - { - if (signature.valid && signature.device && signature.device.trustLevel.isVerified) - { - keyBackupVersionTrust.usable = YES; - } - } - - return keyBackupVersionTrust; -} - -- (nullable id)authDataFromKeyBackupVersion:(MXKeyBackupVersion *)keyBackupVersion - error:(NSError **)error -{ - Class algorithmClass = AlgorithmClassesByName[keyBackupVersion.algorithm]; - if (algorithmClass == NULL) - { - NSString *message = [NSString stringWithFormat:@"[MXNativeKeyBackupEngine] megolmBackupAuthDataFromKeyBackupVersion: Key backup for unknown algorithm: %@", keyBackupVersion.algorithm]; - MXLogError(message); - - *error = [NSError errorWithDomain:MXKeyBackupErrorDomain - code:MXKeyBackupErrorUnknownAlgorithm - userInfo:@{ - NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Unknown algorithm (%@) for the backup", keyBackupVersion.algorithm] - }]; - - return nil; - } - - return [algorithmClass authDataFromJSON:keyBackupVersion.authData error:error]; -} - -- (NSDictionary *)signObject:(NSDictionary *)object -{ - return [self.crypto signObject:object]; -} - -#pragma mark - Backup keys - -- (BOOL)hasKeysToBackup -{ - return [self.crypto.store inboundGroupSessionsToBackup:1].count > 0; -} - -- (NSProgress *)backupProgress -{ - NSUInteger keys = [self.crypto.store inboundGroupSessionsCount:NO]; - NSUInteger backedUpkeys = [self.crypto.store inboundGroupSessionsCount:YES]; - - NSProgress *progress = [NSProgress progressWithTotalUnitCount:keys]; - progress.completedUnitCount = backedUpkeys; - return progress; -} - -- (void)backupKeysWithSuccess:(void (^)(void))success - failure:(void (^)(NSError *))failure -{ - if (!self.keyBackupAlgorithm) - { - MXLogDebug(@"[MXNativeKeyBackupEngine] roomKeysBackupPayload: No known backup algorithm"); - NSError *error = [NSError errorWithDomain:MXKeyBackupErrorDomain - code:MXKeyBackupErrorUnknownAlgorithm - userInfo:nil]; - failure(error); - return; - } - - // Get a chunk of keys to backup - NSArray *sessions = [self.crypto.store inboundGroupSessionsToBackup:kMXKeyBackupSendKeysMaxCount]; - - MXLogDebug(@"[MXNativeKeyBackupEngine] roomKeysBackupPayload: 1 - %@ sessions to back up", @(sessions.count)); - - // Gather data to send to the homeserver - // roomId -> sessionId -> MXKeyBackupData - NSMutableDictionary *> *roomsKeyBackup = [NSMutableDictionary dictionary]; - - for (MXOlmInboundGroupSession *session in sessions) - { - MXKeyBackupData *keyBackupData = [self.keyBackupAlgorithm encryptGroupSession:session]; - - if (keyBackupData) - { - if (!roomsKeyBackup[session.roomId]) - { - roomsKeyBackup[session.roomId] = [NSMutableDictionary dictionary]; - } - roomsKeyBackup[session.roomId][session.session.sessionIdentifier] = keyBackupData; - } - } - - MXLogDebug(@"[MXNativeKeyBackupEngine] roomKeysBackupPayload: 2 - Finalising data to send"); - - // Finalise data to send - NSMutableDictionary *rooms = [NSMutableDictionary dictionary]; - for (NSString *roomId in roomsKeyBackup) - { - NSMutableDictionary *roomSessions = [NSMutableDictionary dictionary]; - for (NSString *sessionId in roomsKeyBackup[roomId]) - { - roomSessions[sessionId] = roomsKeyBackup[roomId][sessionId]; - } - MXRoomKeysBackupData *roomKeysBackupData = [MXRoomKeysBackupData new]; - roomKeysBackupData.sessions = roomSessions; - - rooms[roomId] = roomKeysBackupData; - } - - MXKeysBackupData *keysBackupData = [MXKeysBackupData new]; - keysBackupData.rooms = rooms; - - // Make the request - MXWeakify(self); - [self.crypto.matrixRestClient sendKeysBackup:keysBackupData version:self.keyBackupVersion.version success:^(NSDictionary *JSONResponse){ - MXStrongifyAndReturnIfNil(self); - [self.crypto.store markBackupDoneForInboundGroupSessions:sessions]; - success(); - } failure:failure]; -} - -- (NSProgress *)importProgress -{ - return self.activeImportProgress; -} - -- (void)importKeysWithKeysBackupData:(MXKeysBackupData *)keysBackupData - privateKey:(NSData *)privateKey - keyBackupVersion:(MXKeyBackupVersion *)keyBackupVersion - success:(void (^)(NSUInteger, NSUInteger))success - failure:(void (^)(NSError *))failure -{ - // There is no way to cancel import so we may have one ongoing already - if (self.activeImportProgress) - { - MXLogError(@"[MXNativeKeyBackupEngine] importKeysWithKeysBackupData: Another import is already ongoing"); - if (failure) - { - NSError *error = [NSError errorWithDomain:MXKeyBackupErrorDomain code:MXKeyBackupErrorAlreadyInProgress userInfo:nil]; - dispatch_async(dispatch_get_main_queue(), ^{ - failure(error); - }); - } - return; - } - - id algorithm = [self getOrCreateKeyBackupAlgorithmFor:keyBackupVersion privateKey:privateKey]; - - // Collect all sessions that we need to decrypt and import - NSMutableArray *encryptedSessions = [[NSMutableArray alloc] init]; - for (NSString *roomId in keysBackupData.rooms) - { - for (NSString *sessionId in keysBackupData.rooms[roomId].sessions) - { - MXKeyBackupData *keyBackupData = keysBackupData.rooms[roomId].sessions[sessionId]; - MXEncryptedKeyBackup *backup = [[MXEncryptedKeyBackup alloc] initWithRoomId:roomId sessionId:sessionId keyBackup:keyBackupData]; - [encryptedSessions addObject:backup]; - } - } - - NSUInteger totalKeysCount = encryptedSessions.count; - __block NSUInteger importedKeysCount = 0; - - self.activeImportProgress = [NSProgress progressWithTotalUnitCount:totalKeysCount]; - MXLogDebug(@"[MXNativeKeyBackupEngine] importKeysWithKeysBackupData: Importing %lu encrypted sessions", totalKeysCount); - - NSDate *startDate = [NSDate date]; - - // Ensure we are on a separate queue so that decrypting and importing can happen in parallel - dispatch_async(self.importQueue, ^{ - dispatch_group_t dispatchGroup = dispatch_group_create(); - - // Itterate through the array in memory-isolated batches - for (NSInteger batchIndex = 0; batchIndex < totalKeysCount; batchIndex += kMXKeyBackupImportBatchSize) - { - MXLogDebug(@"[MXNativeKeyBackupEngine] importKeysWithKeysBackupData: Decrypting and importing batch %ld", batchIndex); - dispatch_group_enter(dispatchGroup); - - @autoreleasepool { - - // Decrypt batch of sessions - NSMutableArray *sessions = [NSMutableArray array]; - - NSInteger endIndex = MIN(batchIndex + kMXKeyBackupImportBatchSize, totalKeysCount); - for (NSInteger idx = batchIndex; idx < endIndex; idx++) - { - MXEncryptedKeyBackup *session = encryptedSessions[idx]; - MXMegolmSessionData *sessionData = [algorithm decryptKeyBackupData:session.keyBackup forSession:session.sessionId inRoom:session.roomId]; - if (sessionData) - { - [sessions addObject:sessionData]; - } - } - - // Do not trigger a backup for them if they come from the backup version we are using - BOOL backUp = ![keyBackupVersion.version isEqualToString:self.keyBackupVersion.version]; - if (backUp) - { - MXLogDebug(@"[MXNativeKeyBackupEngine] importKeysWithKeysBackupData: Those keys will be backed up to backup version: %@", self.keyBackupVersion.version); - } - - // Import them into the crypto store - MXWeakify(self); - [self.crypto importMegolmSessionDatas:sessions backUp:backUp success:^(NSUInteger total, NSUInteger imported) { - MXStrongifyAndReturnIfNil(self); - MXLogDebug(@"[MXNativeKeyBackupEngine] importKeysWithKeysBackupData: Imported batch %ld", batchIndex); - importedKeysCount += imported; - - self.activeImportProgress.completedUnitCount += kMXKeyBackupImportBatchSize; - dispatch_group_leave(dispatchGroup); - } failure:^(NSError *error) { - MXLogErrorDetails(@"[MXNativeKeyBackupEngine] importKeysWithKeysBackupData: Failed importing batch of sessions", error); - - self.activeImportProgress.completedUnitCount += kMXKeyBackupImportBatchSize; - dispatch_group_leave(dispatchGroup); - }]; - } - } - - dispatch_group_notify(dispatchGroup, dispatch_get_main_queue(), ^{ - NSTimeInterval duration = [[NSDate date] timeIntervalSinceDate:startDate] * 1000; - - MXLogDebug(@"[MXNativeKeyBackupEngine] importKeysWithKeysBackupData: Successfully imported %ld out of %ld sessions in %f ms", importedKeysCount, totalKeysCount, duration); - self.activeImportProgress = nil; - - if (success) { - success(totalKeysCount, importedKeysCount); - } - }); - }); -} - -#pragma mark - Private methods - - -- (id)getOrCreateKeyBackupAlgorithmFor:(MXKeyBackupVersion *)keyBackupVersion privateKey:(NSData *)privateKey -{ - if (self.enabled - && [self.keyBackupVersion.JSONDictionary isEqualToDictionary:keyBackupVersion.JSONDictionary] - && [self.privateKey isEqualToData:privateKey]) - { - return self.keyBackupAlgorithm; - } - Class algorithmClass = AlgorithmClassesByName[keyBackupVersion.algorithm]; - if (algorithmClass == NULL) - { - NSString *message = [NSString stringWithFormat:@"[MXNativeKeyBackupEngine] getOrCreateKeyBackupAlgorithmFor: unknown algorithm: %@", keyBackupVersion.algorithm]; - MXLogError(message); - return nil; - } - if (![algorithmClass checkBackupVersion:keyBackupVersion]) - { - MXLogError(@"[MXNativeKeyBackupEngine] getOrCreateKeyBackupAlgorithmFor: invalid backup data returned"); - return nil; - } - NSError *error; - id authData = [self authDataFromKeyBackupVersion:keyBackupVersion error:&error]; - if (error) - { - MXLogError(@"[MXNativeKeyBackupEngine] getOrCreateKeyBackupAlgorithmFor: invalid auth data"); - return nil; - } - return [[algorithmClass.class alloc] initWithCrypto:self.crypto authData:authData keyGetterBlock:^NSData * _Nullable{ - return privateKey; - }]; -} - -- (void)validateKeyBackupVersion:(MXKeyBackupVersion *)keyBackupVersion -{ - // Check private keys - if (self.privateKey) - { - Class algorithmClass = AlgorithmClassesByName[keyBackupVersion.algorithm]; - if (algorithmClass == NULL) - { - NSString *message = [NSString stringWithFormat:@"[MXNativeKeyBackupEngine] validateKeyBackupVersion: unknown algorithm: %@", keyBackupVersion.algorithm]; - MXLogError(message); - return; - } - if (![algorithmClass checkBackupVersion:keyBackupVersion]) - { - MXLogError(@"[MXNativeKeyBackupEngine] validateKeyBackupVersion: invalid backup data returned"); - return; - } - - NSData *privateKey = self.privateKey; - NSError *error; - BOOL keyMatches = [algorithmClass keyMatches:privateKey withAuthData:keyBackupVersion.authData error:&error]; - if (error || !keyMatches) - { - MXLogDebug(@"[MXNativeKeyBackupEngine] validateKeyBackupVersion: -> private key does not match: %@, will be removed", error); - [self.crypto.store deleteSecretWithSecretId:MXSecretId.keyBackup]; - } - } -} - -- (MXLegacyCrossSigning *)crossSigning -{ - if (![self.crypto.crossSigning isKindOfClass:[MXLegacyCrossSigning class]]) - { - MXLogFailure(@"[MXNativeKeyBackupEngine] Using incompatible cross signing implementation, can only use legacy"); - return nil; - } - return (MXLegacyCrossSigning *)self.crypto.crossSigning; -} - -@end diff --git a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.h b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.h index 9d95cc0e1a..e7c8853031 100644 --- a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.h +++ b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.h @@ -124,7 +124,6 @@ FOUNDATION_EXPORT NSString *const kMXKeyBackupDidStateChangeNotification; */ - (instancetype)initWithEngine:(id)engine restClient:(MXRestClient *)restClient - secretShareManager:(MXSecretShareManager *)secretShareManager queue:(dispatch_queue_t)queue; #pragma mark - Backup management @@ -372,22 +371,6 @@ FOUNDATION_EXPORT NSString *const kMXKeyBackupDidStateChangeNotification; failure:(nullable void (^)(NSError *error))failure; -#pragma mark - Private keys sharing - -/** - Request backup private keys from other devices. - - @param deviceIds ids of device to make requests to. Nil to request all. - - @param success A block object called when the operation succeeds. - @param onPrivateKeysReceived A block called when the secret has been received from another device. - @param failure A block object called when the operation fails. - */ -- (void)requestPrivateKeysToDeviceIds:(nullable NSArray*)deviceIds - success:(void (^)(void))success - onPrivateKeysReceived:(void (^)(void))onPrivateKeysReceived - failure:(void (^)(NSError *error))failure; - #pragma mark - Backup state /** diff --git a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m index 3f5c836fff..0008207675 100644 --- a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m +++ b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m @@ -17,18 +17,13 @@ #import "MXKeyBackup.h" #import "MXKeyBackup_Private.h" -#import "MXCrypto_Private.h" - -#import #import "MXRecoveryKey.h" -#import "MXKeyBackupPassword.h" #import "MXSession.h" #import "MXTools.h" #import "MXBase64Tools.h" #import "MXError.h" #import "MXKeyProvider.h" #import "MXRawDataKey.h" -#import "MXCrossSigning_Private.h" #import "MXSharedHistoryKeyService.h" #import "MXKeyBackupEngine.h" #import "MatrixSDKSwiftHeader.h" @@ -56,7 +51,6 @@ @interface MXKeyBackup () @property (nonatomic, strong) id engine; @property (nonatomic, strong) MXRestClient *restClient; -@property (nonatomic, strong) MXSecretShareManager *secretShareManager; @end @@ -66,7 +60,6 @@ @implementation MXKeyBackup - (instancetype)initWithEngine:(id)engine restClient:(MXRestClient *)restClient - secretShareManager:(MXSecretShareManager *)secretShareManager queue:(dispatch_queue_t)queue { self = [self init]; @@ -75,7 +68,6 @@ - (instancetype)initWithEngine:(id)engine _state = MXKeyBackupStateUnknown; _engine = engine; _restClient = restClient; - _secretShareManager = secretShareManager; cryptoQueue = queue; } return self; @@ -294,21 +286,6 @@ - (void)sendKeyBackup }]; } -- (void)requestPrivateKeys:(void (^)(void))onComplete -{ - MXLogDebug(@"[MXKeyBackup] requestPrivateKeys"); - - [self requestPrivateKeysToDeviceIds:nil success:^{ - } onPrivateKeysReceived:^{ - - [self restoreKeyBackupAutomaticallyWithPrivateKey:onComplete]; - - } failure:^(NSError * _Nonnull error) { - MXLogErrorDetails(@"[MXKeyBackup] requestPrivateKeys. Error for requestPrivateKeys", error); - onComplete(); - }]; -} - - (void)restoreKeyBackupAutomaticallyWithPrivateKey:(void (^)(void))onComplete { // Check we have alreaded loaded the backup before going further @@ -1044,32 +1021,6 @@ - (MXHTTPOperation *)trustKeyBackupVersion:(MXKeyBackupVersion *)keyBackupVersio #pragma mark - Private keys sharing -- (void)requestPrivateKeysToDeviceIds:(nullable NSArray*)deviceIds - success:(void (^)(void))success - onPrivateKeysReceived:(void (^)(void))onPrivateKeysReceived - failure:(void (^)(NSError *error))failure -{ - MXLogDebug(@"[MXKeyBackup] requestPrivateKeysToDeviceIds: %@", deviceIds); - - MXWeakify(self); - [self.secretShareManager requestSecret:MXSecretId.keyBackup toDeviceIds:deviceIds success:^(NSString * _Nonnull requestId) { - } onSecretReceived:^BOOL(NSString * _Nonnull secret) { - MXStrongifyAndReturnValueIfNil(self, NO); - - BOOL isSecretValid = !self.keyBackupVersion // Accept the secret if the backup is not known yet - || [self isSecretValid:secret forKeyBackupVersion:self.keyBackupVersion]; - - MXLogDebug(@"[MXKeyBackup] requestPrivateKeysToDeviceIds: Got key. isSecretValid: %@", @(isSecretValid)); - if (isSecretValid) - { - NSData *privateKey = [MXBase64Tools dataFromBase64:secret]; - [self.engine savePrivateKey:privateKey version:self.keyBackupVersion.version]; - onPrivateKeysReceived(); - } - return isSecretValid; - } failure:failure]; -} - - (BOOL)isSecretValid:(NSString*)secret forKeyBackupVersion:(MXKeyBackupVersion*)keyBackupVersion { NSData *privateKey = [MXBase64Tools dataFromBase64:secret]; diff --git a/MatrixSDK/Crypto/KeyBackup/MXKeyBackupAlgorithm.h b/MatrixSDK/Crypto/KeyBackup/MXKeyBackupAlgorithm.h deleted file mode 100644 index a76abaf9ee..0000000000 --- a/MatrixSDK/Crypto/KeyBackup/MXKeyBackupAlgorithm.h +++ /dev/null @@ -1,98 +0,0 @@ -// -// Copyright 2022 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 "MXBaseKeyBackupAuthData.h" -#import "MXKeyBackupPreparationInfo.h" - -@class MXKeyBackupData; -@class MXOlmInboundGroupSession; -@class MXMegolmSessionData; -@class MXLegacyCrypto; -@class MXKeyBackupVersion; - -#ifndef MXKeyBackupAlgorithm_h -#define MXKeyBackupAlgorithm_h - -NS_ASSUME_NONNULL_BEGIN - -/// Block to get a private key to be called when required -typedef NSData* _Nullable (^MXKeyBackupPrivateKeyGetterBlock)(void); - -/// Protocol defining an algorithm for key backup operations. -@protocol MXKeyBackupAlgorithm - -/// Name of the algorithm. Constants defined in `MXCryptoConstants`. -@property (class, nonatomic, readonly) NSString *algorithmName; - -/// Flag indicating the algorithm is untrusted or not. -@property (class, nonatomic, readonly, getter=isUntrusted) BOOL untrusted; - - -/// Initializer. Returns nil if the given auth data is invalid. -/// @param crypto crypto instance -/// @param authData auth data instance -/// @param keyGetterBlock block to be called when private key is required. -- (nullable instancetype)initWithCrypto:(MXLegacyCrypto*)crypto - authData:(id)authData - keyGetterBlock:(MXKeyBackupPrivateKeyGetterBlock)keyGetterBlock; - -/// Prepare a private key and auth data for a given password for the algorithm. Returns a preparation info if successful, otherwise returns nil. -/// @param password password to use. If not provided, a new one will be generated. -/// @param error error instance to be set on errors -+ (nullable MXKeyBackupPreparationInfo*)prepareWith:(nullable NSString*)password - error:(NSError *__autoreleasing _Nullable *)error; - -/// Method to check a private key against receiver's internal auth data (the one given at initialization) -/// @param privateKey private key to check -/// @param error error instance to be set on errors -- (BOOL)keyMatches:(NSData*)privateKey - error:(NSError *__autoreleasing _Nullable *)error __attribute__((swift_error(nonnull_error))); - -/// Method to check a private key against a given auth data -/// @param privateKey private key to check -/// @param authData auth data to check against -/// @param error error instance to be set on errors -+ (BOOL)keyMatches:(NSData*)privateKey - withAuthData:(NSDictionary*)authData - error:(NSError *__autoreleasing _Nullable *)error __attribute__((swift_error(nonnull_error))); - -/// Encrypt group session with the receiver algorithm. -/// @param session session instance to encrypt. -- (nullable MXKeyBackupData*)encryptGroupSession:(MXOlmInboundGroupSession*)session; - -/// Decrypt key backup data -/// @param keyBackupData key backup data -/// @param sessionId session id to use -/// @param roomId room id to use -- (nullable MXMegolmSessionData*)decryptKeyBackupData:(MXKeyBackupData*)keyBackupData - forSession:(NSString*)sessionId - inRoom:(NSString*)roomId; - -/// Method to check the algorithm against a given key backup version -/// @param backupVersion key backup version to check against -+ (BOOL)checkBackupVersion:(MXKeyBackupVersion *)backupVersion; - -/// Generate auth data from a given dictionary. Returns nil if there is missing data in the dictionary. -/// @param JSON Auth data dictionary object -/// @param error error instance to be set on errors -+ (nullable id)authDataFromJSON:(NSDictionary*)JSON - error:(NSError**)error; - -@end - -NS_ASSUME_NONNULL_END - -#endif /* MXKeyBackupAlgorithm_h */ diff --git a/MatrixSDK/Crypto/KeyBackup/MXKeyBackupPassword.m b/MatrixSDK/Crypto/KeyBackup/MXKeyBackupPassword.m index 0b4d94d6d5..a004435180 100644 --- a/MatrixSDK/Crypto/KeyBackup/MXKeyBackupPassword.m +++ b/MatrixSDK/Crypto/KeyBackup/MXKeyBackupPassword.m @@ -19,8 +19,6 @@ #import "MXTools.h" #import "MXCryptoConstants.h" -#import - #import #import #import @@ -68,7 +66,7 @@ + (nullable NSData *)deriveKey:(NSString*)password salt:(NSString*)salt iteratio NSData *passwordData = [password dataUsingEncoding:NSUTF8StringEncoding]; NSData *saltData = [salt dataUsingEncoding:NSUTF8StringEncoding]; - NSMutableData *derivedKey = [NSMutableData dataWithLength:[OLMPkDecryption privateKeyLength]]; + NSMutableData *derivedKey = [NSMutableData dataWithLength: 32]; int result = CCKeyDerivationPBKDF(kCCPBKDF2, passwordData.bytes, diff --git a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup_Private.h b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup_Private.h index 4b980498c7..0ae1790955 100644 --- a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup_Private.h +++ b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup_Private.h @@ -32,8 +32,6 @@ NS_ASSUME_NONNULL_BEGIN */ - (void)resetKeyBackupData; -- (void)requestPrivateKeys:(void (^)(void))onComplete; - - (BOOL)isSecretValid:(NSString*)secret forKeyBackupVersion:(MXKeyBackupVersion*)keyBackupVersion; @end diff --git a/MatrixSDK/Crypto/KeyBackup/MXRecoveryKey.m b/MatrixSDK/Crypto/KeyBackup/MXRecoveryKey.m index 59a084960d..1fda763a50 100644 --- a/MatrixSDK/Crypto/KeyBackup/MXRecoveryKey.m +++ b/MatrixSDK/Crypto/KeyBackup/MXRecoveryKey.m @@ -18,8 +18,6 @@ #import "MXTools.h" -#import - #import @@ -71,18 +69,17 @@ + (NSData *)decode:(NSString *)recoveryKey error:(NSError **)error }]; return nil; } - - // Check length + if (result.length != - sizeof(kOlmRecoveryKeyPrefix) + [OLMPkDecryption privateKeyLength] + 1) + sizeof(kOlmRecoveryKeyPrefix) + 33) { if (error) { *error = [NSError errorWithDomain:MXRecoveryKeyErrorDomain code:MXRecoveryKeyErrorLengthCode userInfo:@{ - NSLocalizedDescriptionKey: @"Incorrect length", - }]; + NSLocalizedDescriptionKey: @"Incorrect length", + }]; } return nil; } diff --git a/MatrixSDK/Crypto/KeySharing/MXIncomingRoomKeyRequestManager.h b/MatrixSDK/Crypto/KeySharing/MXIncomingRoomKeyRequestManager.h deleted file mode 100644 index d78f1088ff..0000000000 --- a/MatrixSDK/Crypto/KeySharing/MXIncomingRoomKeyRequestManager.h +++ /dev/null @@ -1,78 +0,0 @@ -/* - Copyright 2017 OpenMarket 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. - */ - -#import - -#import "MXUsersDevicesMap.h" -#import "MXIncomingRoomKeyRequest.h" -#import "MXIncomingRoomKeyRequestCancellation.h" - -#import "MXSDKOptions.h" - -#ifdef MX_CRYPTO - -@class MXLegacyCrypto; - -/** - A `MXKKeyRequestManager` object gathers incoming key requests from the attached - Matrix session. - It then sorts them by user/device pairs so that when the user accept to share keys - with a user's device, all pending incoming key requests from this device will be - accepted. - */ -@interface MXIncomingRoomKeyRequestManager : NSObject - -/** - Constructor. - - @param crypto the related `MXCrypto`. - @return the newly created `MXIncomingRoomKeyRequestManager` instance. - */ -- (instancetype)initWithCrypto:(MXLegacyCrypto*)crypto; - -/** - Stop the incoming key request manager. - */ -- (void)close; - -/** - Called when we get an m.room_key_request event. - - @param event the key request event. - */ -- (void)onRoomKeyRequestEvent:(MXEvent*)event; - -/** - Process any m.room_key_request events which were queued up during the - current sync. - */ -- (void)processReceivedRoomKeyRequests; - -/** - Remove the pending key request matching given ids. - */ -- (void)removePendingKeyRequest:(NSString*)requestId fromUser:(NSString*)userId andDevice:(NSString*)deviceId; - -/** - Pending key requests at the last sync computing completion. - userId -> deviceId -> [keyRequest] - */ -@property (nonatomic, readonly) MXUsersDevicesMap *> *pendingKeyRequests; - -@end - -#endif - diff --git a/MatrixSDK/Crypto/KeySharing/MXIncomingRoomKeyRequestManager.m b/MatrixSDK/Crypto/KeySharing/MXIncomingRoomKeyRequestManager.m deleted file mode 100644 index b069a6ae78..0000000000 --- a/MatrixSDK/Crypto/KeySharing/MXIncomingRoomKeyRequestManager.m +++ /dev/null @@ -1,312 +0,0 @@ -/* - Copyright 2017 OpenMarket Ltd - Copyright 2018 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. - */ - -#import "MXIncomingRoomKeyRequestManager.h" - -#import "MXCrypto_Private.h" -#import "MXTools.h" - -#ifdef MX_CRYPTO - - -NSTimeInterval kFixMissingUserInRoomRateLimit = 3600; - - -@interface MXIncomingRoomKeyRequestManager () -{ - __weak MXLegacyCrypto *crypto; - - // The list of MXIncomingRoomKeyRequests/MXIncomingRoomKeyRequestCancellations - // we received in the current sync. - NSMutableArray *receivedRoomKeyRequests; - NSMutableArray *receivedRoomKeyRequestCancellations; - - // The list of rooms we fixed in the fixMissingUser:inRoom: method - // roomId -> Date of the last fix - NSMutableDictionary *roomsFixedForMissingUser; -} - -@end - -@implementation MXIncomingRoomKeyRequestManager - -- (instancetype)initWithCrypto:(MXLegacyCrypto*)theCrypto -{ - self = [super init]; - if (self) - { - crypto = theCrypto; - - // The list of MXIncomingRoomKeyRequests/MXIncomingRoomKeyRequestCancellations - // we received in the current sync. - receivedRoomKeyRequests = [NSMutableArray array]; - receivedRoomKeyRequestCancellations = [NSMutableArray array]; - - roomsFixedForMissingUser = [NSMutableDictionary dictionary]; - } - return self; -} - -- (void)close -{ - [receivedRoomKeyRequests removeAllObjects]; - receivedRoomKeyRequests = nil; - - [receivedRoomKeyRequestCancellations removeAllObjects]; - receivedRoomKeyRequestCancellations = nil; - - crypto = nil; -} - -- (void)onRoomKeyRequestEvent:(MXEvent*)event -{ - NSString *action; - MXJSONModelSetString(action, event.content[@"action"]); - - MXLogDebug(@"[MXIncomingRoomKeyRequestManager] onRoomKeyRequestEvent: action: %@ (id %@)", action, event.content[@"request_id"]); - - if ([action isEqualToString:@"request"]) - { - // Queue it up for now, because they tend to arrive before the room state - // events at initial sync, and we want to see if we know anything about the - // room before passing them on to the app. - MXIncomingRoomKeyRequest *req = [[MXIncomingRoomKeyRequest alloc] initWithMXEvent:event]; - [receivedRoomKeyRequests addObject:req]; - } - else if ([action isEqualToString:@"request_cancellation"]) - { - MXIncomingRoomKeyRequestCancellation *req = [[MXIncomingRoomKeyRequestCancellation alloc] initWithMXEvent:event]; - [receivedRoomKeyRequestCancellations addObject:req]; - } -} - -- (void)processReceivedRoomKeyRequests -{ - // we need to grab and clear the queues in the synchronous bit of this method, - // so that we don't end up racing with the next /sync. - NSArray *requests = [receivedRoomKeyRequests copy]; - [receivedRoomKeyRequests removeAllObjects]; - NSArray *cancellations = [receivedRoomKeyRequestCancellations copy]; - [receivedRoomKeyRequestCancellations removeAllObjects]; - - // Process all of the requests, *then* all of the cancellations. - // - // This makes sure that if we get a request and its cancellation in the - // same /sync result, then we process the request before the - // cancellation (and end up with a cancelled request), rather than the - // cancellation before the request (and end up with an outstanding - // request which should have been cancelled.) - for (MXIncomingRoomKeyRequest *req in requests) - { - [self processReceivedRoomKeyRequest:req]; - } - for (MXIncomingRoomKeyRequestCancellation *cancellation in cancellations) - { - [self processReceivedRoomKeyRequestCancellation:cancellation]; - } -} - -/** - Helper for processReceivedRoomKeyRequests. - - @param req the request. - */ -- (void)processReceivedRoomKeyRequest:(MXIncomingRoomKeyRequest*)req -{ - NSString *userId = req.userId; - NSString *deviceId = req.deviceId; - NSString *requestId = req.requestId; - - NSDictionary *body = req.requestBody; - NSString *roomId, *alg; - - MXJSONModelSetString(roomId, body[@"room_id"]); - MXJSONModelSetString(alg, body[@"algorithm"]); - - MXLogDebug(@"[MXIncomingRoomKeyRequestManager] processReceivedRoomKeyRequest: m.room_key_request from %@:%@ for %@ / %@ (id %@)", userId, deviceId, roomId, body[@"session_id"], req.requestId); - - if (![userId isEqualToString:crypto.matrixRestClient.credentials.userId]) - { - NSString *senderKey, *sessionId; - MXJSONModelSetString(senderKey, body[@"sender_key"]); - MXJSONModelSetString(sessionId, body[@"session_id"]); - - if (!senderKey && !sessionId) - { - return; - } - - id encryptor = [crypto getRoomEncryptor:roomId algorithm:alg]; - if (!encryptor) - { - MXLogDebug(@"[MXIncomingRoomKeyRequestManager] room key request for unknown alg %@ in room %@", alg, roomId); - return; - } - - [encryptor reshareKey:sessionId withUser:userId andDevice:deviceId senderKey:senderKey success:^{ - - } failure:^(NSError *error) { - MXLogDebug(@"[MXIncomingRoomKeyRequestManager] reshareKey failed. Error: %@", error); - - if ([error.domain isEqualToString:MXEncryptingErrorDomain] - && (error.code == MXEncryptingErrorUnknownDeviceCode - || error.code == MXEncryptingErrorReshareNotAllowedCode)) - { - [self fixMissingUser:userId inRoom:roomId]; - } - }]; - return; - } - - // todo: should we queue up requests we don't yet have keys for, - // in case they turn up later? - - // if we don't have a decryptor for this room/alg, we don't have - // the keys for the requested events, and can drop the requests. - id decryptor = [crypto getRoomDecryptor:roomId algorithm:alg]; - if (!decryptor) - { - MXLogDebug(@"[MXIncomingRoomKeyRequestManager] room key request for unknown alg %@ in room %@", alg, roomId); - return; - } - - if (![decryptor hasKeysForKeyRequest:req]) - { - MXLogDebug(@"[MXIncomingRoomKeyRequestManager] room key request for unknown session %@ / %@", roomId, body[@"session_id"]); - return; - } - - // if the device is verified already, share the keys - MXDeviceInfo *device = [crypto.store deviceWithDeviceId:deviceId forUser:userId]; - if (device && device.trustLevel.isVerified) - { - MXLogDebug(@"[MXIncomingRoomKeyRequestManager] device is already verified: sharing keys"); - [decryptor shareKeysWithDevice:req success:nil failure:nil]; - return; - } - - // check if we already have this request - if ([crypto.store incomingRoomKeyRequestWithRequestId:requestId fromUser:userId andDevice:deviceId]) - { - MXLogDebug(@"[MXIncomingRoomKeyRequestManager] Already have this key request, ignoring"); - return; - } - - // Add it to pending key requests - [crypto.store storeIncomingRoomKeyRequest:req]; - - // Broadcast the room key request - dispatch_async(dispatch_get_main_queue(), ^{ - if (self->crypto) - { - [[NSNotificationCenter defaultCenter] postNotificationName:kMXCryptoRoomKeyRequestNotification - object:self->crypto - userInfo:@{ - kMXCryptoRoomKeyRequestNotificationRequestKey: req - }]; - } - }); -} - -/** - Helper for processReceivedRoomKeyRequests. - - @param cancellation the request cancellation. - */ -- (void)processReceivedRoomKeyRequestCancellation:(MXIncomingRoomKeyRequestCancellation*)cancellation -{ - NSString *userId = cancellation.userId; - NSString *deviceId = cancellation.deviceId; - NSString *requestId = cancellation.requestId; - - MXLogDebug(@"[MXIncomingRoomKeyRequestManager] processReceivedRoomKeyRequestCancellation: m.room_key_request cancellation for %@:%@ (id %@)", userId, deviceId, requestId); - - if (![crypto.store incomingRoomKeyRequestWithRequestId:requestId fromUser:userId andDevice:deviceId]) - { - // Do not notify cancellations already notified - MXLogDebug(@"[MXIncomingRoomKeyRequestManager] handleKeyRequest: Already cancelled this key request, ignoring"); - return; - } - - MXLogDebug(@"[MXIncomingRoomKeyRequestManager] Forgetting room key request"); - [self removePendingKeyRequest:requestId fromUser:userId andDevice:deviceId]; - - // Broadcast the room key request cancelation - MXWeakify(self); - dispatch_async(dispatch_get_main_queue(), ^{ - MXStrongifyAndReturnIfNil(self); - - [[NSNotificationCenter defaultCenter] postNotificationName:kMXCryptoRoomKeyRequestCancellationNotification - object:self->crypto - userInfo:@{ - kMXCryptoRoomKeyRequestCancellationNotificationRequestKey: cancellation - }]; - }); -} - -- (void)removePendingKeyRequest:(NSString*)requestId fromUser:(NSString*)userId andDevice:(NSString*)deviceId -{ - [crypto.store deleteIncomingRoomKeyRequest:requestId fromUser:userId andDevice:deviceId]; -} - -- (MXUsersDevicesMap *> *)pendingKeyRequests -{ - return [crypto.store incomingRoomKeyRequests]; -} - -/** - Reset the flag that indicates that all room members in a room have been loaded. - - @param userId the if of the user that failed to get the key. - @param roomId the room id. - */ -- (void)fixMissingUser:(NSString *)userId inRoom:(NSString *)roomId -{ - // TODO: Remove this method once the root issue is fixed - - // This is a workaround for https://github.com/vector-im/element-ios/issues/3807 - // where the SDK seems to have a bad view of current members in a room. This make it "forget" to send - // the megolm key to all other users. - - // If a user has this issue, their app will send a re-share request. - // The request will be rejected but this is the good time to attempt to reset the flag that indicates - // that all room members in the room have been loaded. - - // On the next message encryption, the SDK will fetch all members again from the server and will share better the key. - // Next message should be decryptable for others. - - // Rate limit the reset to 1h or one life cycle - NSDate *lastFixDate = roomsFixedForMissingUser[roomId]; - if (lastFixDate - && [[NSDate date] timeIntervalSinceDate:lastFixDate] < kFixMissingUserInRoomRateLimit) - { - // To early to retry - MXLogDebug(@"[MXIncomingRoomKeyRequestManager] fixMissingUser: %@ inRoom: %@ already requested at %@", userId, roomId, lastFixDate); - return; - } - - MXLogDebug(@"[MXIncomingRoomKeyRequestManager] fixMissingUser: %@ inRoom: %@", userId, roomId); - roomsFixedForMissingUser[roomId] = [NSDate date]; - - // Reset the flag - // This is ugly. We need to remove this workaround as soon as possible - [crypto.mxSession.store storeHasLoadedAllRoomMembersForRoom:roomId andValue:NO]; -} - -@end - -#endif diff --git a/MatrixSDK/Crypto/KeySharing/MXOutgoingRoomKeyRequestManager.h b/MatrixSDK/Crypto/KeySharing/MXOutgoingRoomKeyRequestManager.h deleted file mode 100644 index 8b66b132c9..0000000000 --- a/MatrixSDK/Crypto/KeySharing/MXOutgoingRoomKeyRequestManager.h +++ /dev/null @@ -1,96 +0,0 @@ -/* - Copyright 2017 OpenMarket 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. - */ - -#import - -#import "MXRestClient.h" -#import "MXCryptoStore.h" - -#ifdef MX_CRYPTO - -/** - Management of outgoing room key requests. - - See https://docs.google.com/document/d/1m4gQkcnJkxNuBmb5NoFCIadIY-DyqqNAS3lloE73BlQ - for draft documentation on what we're supposed to be implementing here. - */ -@interface MXOutgoingRoomKeyRequestManager : NSObject - -/** - Create a MXSession instance. - This instance will use the passed MXRestClient to make requests to the home server. - - @param mxRestClient The MXRestClient to the home server. - @param deviceId The user device id. - @param cryptoQueue The crypto thread. - @param cryptoStore The crypto store. - - @return The newly-initialized MXSession. - */ -- (id)initWithMatrixRestClient:(MXRestClient*)mxRestClient - deviceId:(NSString*)deviceId - cryptoQueue:(dispatch_queue_t)cryptoQueue - cryptoStore:(id)cryptoStore; - -/** - Called when the client is started. Sets background processes running. - */ -- (void)start; - -/** - Enable or disable key share requests. - Enabled by default - - @param enabled the new enable state. - */ -- (void)setEnabled:(BOOL)enabled; -- (BOOL)isEnabled; - -/** - Called when the client is stopped. Stops any running background processes. - */ -- (void)close; - -/** - Send off a room key request, if we haven't already done so. - - The `requestBody` is compared (with a deep-equality check) against - previous queued or sent requests and if it matches, no change is made. - Otherwise, a request is added to the pending list, and a job is started - in the background to send it. - - @param requestBody the requestBody. - @param recipients a {Array<{userId: string, deviceId: string}>}. - */ -- (void)sendRoomKeyRequest:(NSDictionary*)requestBody recipients:(NSArray*>*)recipients; - -/** - Cancel room key requests, if any match the given details. - - @param requestBody parameters to match for cancellation. - */ -- (void)cancelRoomKeyRequest:(NSDictionary*)requestBody; - -/** - Resend a room key request, if any match the given details. - - @param requestBody parameters to match for resend. - */ -- (void)resendRoomKeyRequest:(NSDictionary*)requestBody; - -@end - -#endif diff --git a/MatrixSDK/Crypto/KeySharing/MXOutgoingRoomKeyRequestManager.m b/MatrixSDK/Crypto/KeySharing/MXOutgoingRoomKeyRequestManager.m deleted file mode 100644 index 61832eddfe..0000000000 --- a/MatrixSDK/Crypto/KeySharing/MXOutgoingRoomKeyRequestManager.m +++ /dev/null @@ -1,407 +0,0 @@ -/* - Copyright 2017 OpenMarket 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. - */ - -#import "MXOutgoingRoomKeyRequestManager.h" - -#import "MXTools.h" -#import "MXOutgoingRoomKeyRequest.h" -#import "MatrixSDKSwiftHeader.h" - -#ifdef MX_CRYPTO - -// delay between deciding we want some keys, and sending out the request, to -// allow for (a) it turning up anyway, (b) grouping requests together -NSUInteger const SEND_KEY_REQUESTS_DELAY_MS = 500; - - -@interface MXOutgoingRoomKeyRequestManager () -{ - MXRestClient *matrixRestClient; - NSString *deviceId; - dispatch_queue_t cryptoQueue; - id cryptoStore; - - // handle for the delayed call to sendOutgoingRoomKeyRequests. Non-null - // if the callback has been set, or if it is still running. - NSTimer *sendOutgoingRoomKeyRequestsTimer; -} - -@property (nonatomic, assign, getter = isEnabled) BOOL enabled; - -@end - -@implementation MXOutgoingRoomKeyRequestManager - -- (id)initWithMatrixRestClient:(MXRestClient*)mxRestClient - deviceId:(NSString*)theDeviceId - cryptoQueue:(dispatch_queue_t)theCryptoQueue - cryptoStore:(id)theCryptoStore -{ - self = [super init]; - if (self) - { - matrixRestClient = mxRestClient; - deviceId = theDeviceId; - cryptoQueue = theCryptoQueue; - cryptoStore = theCryptoStore; - _enabled = YES; - } - return self; -} - -- (void)start -{ - // set the timer going, to handle any requests which didn't get sent - // on the previous run of the client. - [self startTimer]; -} - -- (void)close -{ - // Close is planned to be called from the main thread - NSParameterAssert([NSThread isMainThread]); - - [sendOutgoingRoomKeyRequestsTimer invalidate]; - sendOutgoingRoomKeyRequestsTimer = nil; -} - -- (void)setEnabled:(BOOL)enabled -{ - MXLogDebug(@"[MXOutgoingRoomKeyRequestManager] setEnabled: %@ (old: %@)", @(enabled), @(_enabled)); - if (enabled == _enabled) - { - return; - } - - if (enabled) - { - // Check keys we got while this manager was disabled - [self checkAllPendingOutgoingRoomKeyRequests]; - } - - _enabled = enabled; - [self startTimer]; -} - - -- (void)sendRoomKeyRequest:(NSDictionary *)requestBody recipients:(NSArray *> *)recipients -{ - MXOutgoingRoomKeyRequest *request = [self getOrAddOutgoingRoomKeyRequest:requestBody recipients:recipients]; - - if (request.state == MXRoomKeyRequestStateUnsent) - { - [self startTimer]; - } -} - -- (void)cancelRoomKeyRequest:(NSDictionary *)requestBody -{ - [self cancelRoomKeyRequest:requestBody andResend:NO]; -} - -- (void)resendRoomKeyRequest:(NSDictionary *)requestBody -{ - [self cancelRoomKeyRequest:requestBody andResend:YES]; -} - -- (void)cancelRoomKeyRequest:(NSDictionary *)requestBody andResend:(BOOL)resend -{ - MXOutgoingRoomKeyRequest *request = [cryptoStore outgoingRoomKeyRequestWithRequestBody:requestBody]; - - if (!request) - { - // no request was made for this key - return; - } - - MXLogDebug(@"[MXOutgoingRoomKeyRequestManager] cancelRoomKeyRequest:andResend:%@: request.requestId: %@. state: %@", @(resend), request.requestId, @(request.state)); - - switch (request.state) - { - case MXRoomKeyRequestStateCancellationPending: - case MXRoomKeyRequestStateCancellationPendingAndWillResend: - // nothing to do here - break; - - case MXRoomKeyRequestStateUnsent: - // just delete it - - // FIXME: ghahah we may have attempted to send it, and - // not yet got a successful response. So the server - // may have seen it, so we still need to send a cancellation - // in that case :/ - - MXLogDebug(@"[MXOutgoingRoomKeyRequestManager] cancelRoomKeyRequest: deleting unnecessary room key request %@", request.requestId); - - [cryptoStore deleteOutgoingRoomKeyRequestWithRequestId:request.requestId]; - break; - - case MXRoomKeyRequestStateSent: - // send a cancellation. - request.state = resend ? MXRoomKeyRequestStateCancellationPendingAndWillResend : MXRoomKeyRequestStateCancellationPending; - - request.cancellationTxnId = [MXTools generateTransactionId]; - - [cryptoStore updateOutgoingRoomKeyRequest:request]; - - // We don't want to wait for the timer, so we send it - // immediately. (We might actually end up racing with the timer, - // but that's ok: even if we make the request twice, we'll do it - // with the same transaction_id, so only one message will get - // sent). - // - // (We also don't want to wait for the response from the server - // here, as it will slow down processing of received keys if we - // do.) - MXWeakify(self); - [self sendOutgoingRoomKeyRequestCancellation:request andResend:resend success:nil failure:^(NSError *error) { - MXStrongifyAndReturnIfNil(self); - - MXLogDebug(@"[MXOutgoingRoomKeyRequestManager] cancelRoomKeyRequest: Error sending room key request cancellation; will retry later."); - - [self startTimer]; - }]; - } -} - -#pragma mark - Private methods - -- (void)startTimer -{ - // Must be called on the crypto thread - // So, move on the main thread to create NSTimer - MXWeakify(self); - dispatch_async(dispatch_get_main_queue(), ^{ - MXStrongifyAndReturnIfNil(self); - - if (self->sendOutgoingRoomKeyRequestsTimer) - { - return; - } - - // Start timer - self->sendOutgoingRoomKeyRequestsTimer = [[NSTimer alloc] initWithFireDate:[NSDate dateWithTimeIntervalSinceNow:SEND_KEY_REQUESTS_DELAY_MS / 1000.0] - interval:0 - target:self - selector:@selector(sendOutgoingRoomKeyRequests) - userInfo:nil - repeats:NO]; - [[NSRunLoop mainRunLoop] addTimer:self->sendOutgoingRoomKeyRequestsTimer forMode:NSDefaultRunLoopMode]; - }); -} - -- (void)checkAllPendingOutgoingRoomKeyRequests -{ - NSArray *requests = [self->cryptoStore allOutgoingRoomKeyRequestsWithState:MXRoomKeyRequestStateUnsent]; - - NSUInteger deleted = 0; - for (MXOutgoingRoomKeyRequest *request in requests) - { - // Check if we have now a valid key - MXOlmInboundGroupSession *inboundGroupSession = [cryptoStore inboundGroupSessionWithId:request.sessionId andSenderKey:request.senderKey]; - if ([inboundGroupSession.roomId isEqualToString:request.roomId]) - { - [cryptoStore deleteOutgoingRoomKeyRequestWithRequestId:request.requestId]; - deleted++; - } - } - - MXLogDebug(@"[MXOutgoingRoomKeyRequestManager] checkAllPendingOutgoingRoomKeyRequests: Cleared %@ requests out of %@", @(deleted), @(requests.count)); -} - -- (void)sendOutgoingRoomKeyRequests -{ - [sendOutgoingRoomKeyRequestsTimer invalidate]; - sendOutgoingRoomKeyRequestsTimer = nil; - - // Do not start - if (!self.isEnabled) - { - MXLogDebug(@"[MXOutgoingRoomKeyRequestManager] startSendingOutgoingRoomKeyRequests: Disabled."); - return; - } - - MXLogDebug(@"[MXOutgoingRoomKeyRequestManager] startSendingOutgoingRoomKeyRequests: Looking for queued outgoing room key requests."); - - // This method is called on the [NSRunLoop mainRunLoop]. Go to the crypto thread - MXWeakify(self); - dispatch_async(cryptoQueue, ^{ - MXStrongifyAndReturnIfNil(self); - - MXOutgoingRoomKeyRequest* request = [self->cryptoStore outgoingRoomKeyRequestWithState:MXRoomKeyRequestStateCancellationPending]; - if (!request) - { - request = [self->cryptoStore outgoingRoomKeyRequestWithState:MXRoomKeyRequestStateUnsent]; - } - - if (!request) - { - MXLogDebug(@"[MXOutgoingRoomKeyRequestManager] startSendingOutgoingRoomKeyRequests: No more outgoing room key requests"); - return; - } - - MXWeakify(self); - void(^onSuccess)(void) = ^(void) { - MXStrongifyAndReturnIfNil(self); - - // go around the loop again - [self sendOutgoingRoomKeyRequests]; - }; - - void(^onFailure)(NSError *) = ^(NSError *error) { - MXStrongifyAndReturnIfNil(self); - - [self startTimer]; - }; - - switch (request.state) - { - case MXRoomKeyRequestStateUnsent: - [self sendOutgoingRoomKeyRequest:request success:onSuccess failure:onFailure]; - break; - - case MXRoomKeyRequestStateCancellationPending: - [self sendOutgoingRoomKeyRequestCancellation:request andResend:NO success:onSuccess failure:onFailure]; - break; - - case MXRoomKeyRequestStateCancellationPendingAndWillResend: - [self sendOutgoingRoomKeyRequestCancellation:request andResend:YES success:onSuccess failure:onFailure]; - break; - - default: - break; - } - }); -} - -// given a RoomKeyRequest, send it and update the request record -- (void)sendOutgoingRoomKeyRequest:(MXOutgoingRoomKeyRequest*)request - success:(void (^)(void))success - failure:(void (^)(NSError *error))failure -{ - MXLogDebug(@"[MXOutgoingRoomKeyRequestManager] sendOutgoingRoomKeyRequest: Requesting key %@ using request id %@ to %@: %@", request.sessionId, request.requestId, request.recipients, request.requestBody); - - NSDictionary *requestMessage = @{ - @"action": @"request", - @"requesting_device_id": deviceId, - @"request_id": request.requestId, - kMXMessageBodyKey: request.requestBody - }; - - MXWeakify(self); - [self sendMessageToDevices:requestMessage recipients:request.recipients txnId:request.requestId success:^{ - MXStrongifyAndReturnIfNil(self); - - request.state = MXRoomKeyRequestStateSent; - [self->cryptoStore updateOutgoingRoomKeyRequest:request]; - - success(); - - } failure:failure]; -} - -// Given a RoomKeyRequest, cancel it and delete the request record. -// If resend is set, send a new request. -- (void)sendOutgoingRoomKeyRequestCancellation:(MXOutgoingRoomKeyRequest*)request - andResend:(BOOL)resend - success:(void (^)(void))success - failure:(void (^)(NSError *error))failure -{ - MXLogDebug(@"[MXOutgoingRoomKeyRequestManager] sendOutgoingRoomKeyRequestCancellation: Sending cancellation for key request (request id %@) for key %@ (cancellation id %@)", request.requestId, request.sessionId, request.cancellationTxnId); - - NSDictionary *requestMessage = @{ - @"action": @"request_cancellation", - @"requesting_device_id": deviceId, - @"request_id": request.requestId - }; - - MXWeakify(self); - [self sendMessageToDevices:requestMessage recipients:request.recipients txnId:request.cancellationTxnId success:^{ - MXStrongifyAndReturnIfNil(self); - - [self->cryptoStore deleteOutgoingRoomKeyRequestWithRequestId:request.requestId]; - - if (resend) - { - // Resend by creating a request (with new requestId) - [self sendRoomKeyRequest:request.requestBody recipients:request.recipients]; - } - - if (success) - { - success(); - } - - } failure:failure]; -} - -- (void)sendMessageToDevices:(NSDictionary*)message - recipients:(NSArray *> *)recipients - txnId:(NSString*)txnId - success:(void (^)(void))success - failure:(void (^)(NSError *error))failure -{ - MXUsersDevicesMap *contentMap = [[MXUsersDevicesMap alloc] init]; - for (NSDictionary *recipient in recipients) - { - [contentMap setObject:message forUser:recipient[@"userId"] andDevice:recipient[@"deviceId"]]; - } - - MXToDevicePayload *payload = [[MXToDevicePayload alloc] initWithEventType:kMXEventTypeStringRoomKeyRequest - contentMap:contentMap - transactionId:txnId - addMessageId:YES]; - [matrixRestClient sendToDevice:payload success:success failure:failure]; -} - -/** - Look for an existing outgoing room key request, and if none is found, - add a new one - - @param requestBody the body of the request. - @param recipients the recipients. - @returns the existing outgoing room key request or a new one. - */ -- (MXOutgoingRoomKeyRequest*)getOrAddOutgoingRoomKeyRequest:(NSDictionary *)requestBody - recipients:(NSArray *> *)recipients -{ - // first see if we already have an entry for this request. - MXOutgoingRoomKeyRequest *outgoingRoomKeyRequest = [cryptoStore outgoingRoomKeyRequestWithRequestBody:requestBody]; - if (outgoingRoomKeyRequest) - { - // this entry matches the request - return it. - MXLogDebug(@"[MXOutgoingRoomKeyRequestManager] getOrAddOutgoingRoomKeyRequest: already have key request outstanding for %@ / %@: not sending another", requestBody[@"room_id"], requestBody[@"session_id"]); - return outgoingRoomKeyRequest; - } - - // we got to the end of the list without finding a match - // - add the new request. - MXLogDebug(@"[MXOutgoingRoomKeyRequestManager] getOrAddOutgoingRoomKeyRequest: enqueueing key request for %@ / %@", requestBody[@"room_id"], requestBody[@"session_id"]); - - outgoingRoomKeyRequest = [[MXOutgoingRoomKeyRequest alloc] init]; - outgoingRoomKeyRequest.requestBody = requestBody; - outgoingRoomKeyRequest.recipients = recipients; - outgoingRoomKeyRequest.requestId = [MXTools generateTransactionId]; - outgoingRoomKeyRequest.state = MXRoomKeyRequestStateUnsent; - - [cryptoStore storeOutgoingRoomKeyRequest:outgoingRoomKeyRequest]; - - return outgoingRoomKeyRequest; -} - -@end - -#endif diff --git a/MatrixSDK/Crypto/KeySharing/Secret/MXSecretShareManager.h b/MatrixSDK/Crypto/KeySharing/Secret/MXSecretShareManager.h index 700c31c5fb..8fa0ab3721 100644 --- a/MatrixSDK/Crypto/KeySharing/Secret/MXSecretShareManager.h +++ b/MatrixSDK/Crypto/KeySharing/Secret/MXSecretShareManager.h @@ -18,8 +18,6 @@ #import "MXHTTPOperation.h" -@class MXCrypto; - NS_ASSUME_NONNULL_BEGIN @@ -35,46 +33,4 @@ extern const struct MXSecretId { } MXSecretId; -/** - Secret sharing manager. - - See https://github.com/uhoreg/matrix-doc/blob/ssss/proposals/1946-secure_server-side_storage.md#sharing. - */ -@interface MXSecretShareManager : NSObject - -/** - Request a secret from other user's devices. - - @param secretId the id of the secret - @param deviceIds ids of device to make request. Nil to request all. - - @param success A block object called when the operation succeeds. It provides the id of the request. - @param onSecretReceived A block called when the secret has been received from another device. - Must return YES if the secret is valid. - @param failure A block object called when the operation fails. - - @return a MXHTTPOperation instance. - */ -- (MXHTTPOperation *)requestSecret:(NSString*)secretId - toDeviceIds:(nullable NSArray*)deviceIds - success:(void (^)(NSString *requestId))success - onSecretReceived:(BOOL (^)(NSString *secret))onSecretReceived - failure:(void (^)(NSError *error))failure; - -/** - Cancel a secret request. - - @param requestId the id of the request. - - @param success A block object called when the operation succeeds. - @param failure A block object called when the operation fails. - - @return a MXHTTPOperation instance. - */ -- (MXHTTPOperation *)cancelRequestWithRequestId:(NSString*)requestId - success:(void (^)(void))success - failure:(void (^)(NSError *error))failure; - -@end - NS_ASSUME_NONNULL_END diff --git a/MatrixSDK/Crypto/KeySharing/Secret/MXSecretShareManager.m b/MatrixSDK/Crypto/KeySharing/Secret/MXSecretShareManager.m index 724a1363c0..7c3a304d67 100644 --- a/MatrixSDK/Crypto/KeySharing/Secret/MXSecretShareManager.m +++ b/MatrixSDK/Crypto/KeySharing/Secret/MXSecretShareManager.m @@ -14,15 +14,13 @@ limitations under the License. */ -#import "MXSecretShareManager_Private.h" - -#import "MXCrypto_Private.h" #import "MXSecretShareRequest.h" #import "MXPendingSecretShareRequest.h" #import "MXSecretShareSend.h" #import "MXTools.h" #import "MatrixSDKSwiftHeader.h" +#import "MXSecretShareManager.h" #pragma mark - Constants @@ -33,436 +31,3 @@ .keyBackup = @"m.megolm_backup.v1", .dehydratedDevice = @"org.matrix.msc3814" // @"m.dehydrated_device" }; - - -static NSArray *kMXSecretShareEventTypes; - - -@interface MXSecretShareManager () -{ - NSMutableDictionary *pendingSecretShareRequests; - NSMutableArray *cancelledRequestIds; -} - -@property (nonatomic, readonly, weak) MXLegacyCrypto *crypto; - -@end - - -@implementation MXSecretShareManager - -- (MXHTTPOperation *)requestSecret:(NSString*)secretId - toDeviceIds:(nullable NSArray*)deviceIds - success:(void (^)(NSString *requestId))success - onSecretReceived:(BOOL (^)(NSString *secret))onSecretReceived - failure:(void (^)(NSError *error))failure -{ - MXLogDebug(@"[MXSecretShareManager] requestSecret: %@ to %@", secretId, deviceIds); - - // Create an empty operation that will be mutated later - MXHTTPOperation *operation = [[MXHTTPOperation alloc] init]; - - MXWeakify(self); - dispatch_async(_crypto.cryptoQueue, ^{ - MXStrongifyAndReturnIfNil(self); - - MXCredentials *myUser = self.crypto.mxSession.matrixRestClient.credentials; - - MXSecretShareRequest *request = [MXSecretShareRequest new]; - request.name = secretId; - request.action = MXSecretShareRequestAction.request; - request.requestingDeviceId = myUser.deviceId; - request.requestId = [MXTools generateTransactionId]; - - MXPendingSecretShareRequest *pendingRequest = [MXPendingSecretShareRequest new]; - pendingRequest.request = request; - pendingRequest.onSecretReceivedBlock = onSecretReceived; - pendingRequest.requestedDeviceIds = deviceIds; - - self->pendingSecretShareRequests[request.requestId] = pendingRequest; - - MXWeakify(self); - MXHTTPOperation *operation2 = [self sendMessage:request.JSONDictionary toDeviceIds:deviceIds success:^{ - - dispatch_async(dispatch_get_main_queue(), ^{ - success(request.requestId); - }); - - } failure:^(NSError *error) { - MXStrongifyAndReturnIfNil(self); - - [self->pendingSecretShareRequests removeObjectForKey:request.requestId]; - - dispatch_async(dispatch_get_main_queue(), ^{ - failure(error); - }); - }]; - - [operation mutateTo:operation2]; - }); - - return operation; -} - -- (MXHTTPOperation *)cancelRequestWithRequestId:(NSString*)requestId - success:(void (^)(void))success - failure:(void (^)(NSError *error))failure -{ - MXLogDebug(@"[MXSecretShareManager] cancelRequestWithRequestId: %@", requestId); - - // Sanity check - if (!requestId) - { - MXLogDebug(@"[MXSecretShareManager] cancelRequestWithRequestId: Nil request id"); - failure(nil); - } - - // Create an empty operation that will be mutated later - MXHTTPOperation *operation = [[MXHTTPOperation alloc] init]; - - MXWeakify(self); - dispatch_async(_crypto.cryptoQueue, ^{ - MXStrongifyAndReturnIfNil(self); - - MXPendingSecretShareRequest *pendingRequest = self->pendingSecretShareRequests[requestId]; - if (!pendingRequest) - { - dispatch_async(dispatch_get_main_queue(), ^{ - MXLogDebug(@"[MXSecretShareManager] cancelRequestWithRequestId: Unknown request: %@", requestId); - failure(nil); - }); - } - - [self->pendingSecretShareRequests removeObjectForKey:requestId]; - - MXCredentials *myUser = self.crypto.mxSession.matrixRestClient.credentials; - - MXSecretShareRequest *request = [MXSecretShareRequest new]; - request.action = MXSecretShareRequestAction.requestCancellation; - request.requestingDeviceId = myUser.deviceId; - request.requestId = requestId; - - MXHTTPOperation *operation2 = [self sendMessage:request.JSONDictionary toDeviceIds:pendingRequest.requestedDeviceIds success:^{ - dispatch_async(dispatch_get_main_queue(), ^{ - success(); - }); - - } failure:^(NSError *error) { - - dispatch_async(dispatch_get_main_queue(), ^{ - failure(error); - }); - }]; - - [operation mutateTo:operation2]; - }); - - return operation; -} - - -#pragma mark - SDK-Private methods - - -+ (void)initialize -{ - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - kMXSecretShareEventTypes = @[ - kMXEventTypeStringSecretRequest, - kMXEventTypeStringSecretSend - ]; - }); -} - -- (instancetype)initWithCrypto:(MXLegacyCrypto *)crypto; -{ - self = [super init]; - if (self) - { - _crypto = crypto; - pendingSecretShareRequests = [NSMutableDictionary dictionary]; - cancelledRequestIds = [NSMutableArray array]; - - // Observe incoming secret share requests - [self setupIncomingRequests]; - } - return self; -} - - -#pragma mark - Private methods - - -- (MXHTTPOperation*)sendMessage:(NSDictionary*)message - toDeviceIds:(nullable NSArray*)deviceIds - success:(void (^)(void))success - failure:(void (^)(NSError *error))failure -{ - MXCredentials *myUser = _crypto.mxSession.matrixRestClient.credentials; - - MXUsersDevicesMap *contentMap = [[MXUsersDevicesMap alloc] init]; - if (deviceIds) - { - for (NSString *deviceId in deviceIds) - { - [contentMap setObject:message forUser:myUser.userId andDevice:deviceId]; - } - } - else - { - [contentMap setObject:message forUser:myUser.userId andDevice:@"*"]; - } - - MXToDevicePayload *payload = [[MXToDevicePayload alloc] initWithEventType:kMXEventTypeStringSecretRequest - contentMap:contentMap]; - return [_crypto.matrixRestClient sendToDevice:payload success:success failure:failure]; -} - -- (BOOL)isSecretShareEvent:(MXEventTypeString)type -{ - return [kMXSecretShareEventTypes containsObject:type]; -} - -- (void)setupIncomingRequests -{ - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onToDeviceEvent:) name:kMXSessionOnToDeviceEventNotification object:_crypto.mxSession]; -} - -- (void)onToDeviceEvent:(NSNotification *)notification -{ - MXEvent *event = notification.userInfo[kMXSessionNotificationEventKey]; - - if ([self isSecretShareEvent:event.type]) - { - [self handleSecretShareEvent:event]; - } -} - -- (void)handleSecretShareEvent:(MXEvent*)event -{ - MXLogDebug(@"[MXSecretShareManager] handleSecretShareEvent: eventType: %@", event.type); - - dispatch_async(_crypto.cryptoQueue, ^{ - switch (event.eventType) - { - case MXEventTypeSecretRequest: - [self handleSecretRequestEvent:event]; - break; - - case MXEventTypeSecretSend: - [self handleSecretSendEvent:event]; - break; - - default: - break; - } - }); -} - -- (void)handleSecretRequestEvent:(MXEvent*)event -{ - MXCredentials *myUser = _crypto.mxSession.matrixRestClient.credentials; - - if (![event.sender isEqualToString:myUser.userId]) - { - return; - } - - MXSecretShareRequest *request; - MXJSONModelSetMXJSONModel(request, MXSecretShareRequest, event.content); - if (!request) - { - MXLogDebug(@"[MXSecretShareManager] handleSecretRequestEvent: Bad content format: %@", event.JSONDictionary); - return; - } - - if ([request.requestingDeviceId isEqualToString:myUser.deviceId]) - { - // Ignore own requests - return; - } - - if ([request.action isEqualToString:MXSecretShareRequestAction.request]) - { - [self handleSecretRequest:request]; - } - else if ([request.action isEqualToString:MXSecretShareRequestAction.requestCancellation]) - { - [self handleSecretRequestCancellation:request]; - } - else - { - MXLogDebug(@"[MXSecretShareManager] handleSecretRequestEvent. Unsupported action: %@. Event: %@", request.action, event.JSONDictionary); - } -} - -- (void)handleSecretRequest:(MXSecretShareRequest*)request -{ - // Handle secret requests only when the sync has been done. - // This allows to manage cancellations events for that requests - if (self.crypto.mxSession.state == MXSessionStateSyncInProgress - || self.crypto.mxSession.state == MXSessionStateBackgroundSyncInProgress) - { - // TODO: Be more accurate to detect the first sync is done - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.1 * NSEC_PER_SEC), self.crypto.cryptoQueue, ^{ - [self handleSecretRequest:request]; - }); - return; - } - - [self handleSecretRequest2:request]; -} - -- (void)handleSecretRequest2:(MXSecretShareRequest*)request -{ - if ([cancelledRequestIds containsObject:request.requestId]) - { - MXLogDebug(@"[MXSecretShareManager] handleSecretRequestEvent: Ignored cancelled request: %@", request.requestId); - [cancelledRequestIds removeObject:request.requestId]; - return; - } - - MXCredentials *myUser = _crypto.mxSession.matrixRestClient.credentials; - - MXDeviceInfo *otherDevice = [_crypto.store deviceWithDeviceId:request.requestingDeviceId forUser:myUser.userId]; - if (!otherDevice.trustLevel.isVerified) - { - MXLogDebug(@"[MXSecretShareManager] handleSecretRequestEvent: Ignore secret share request from untrusted device: %@", otherDevice); - return; - } - - // TODO: Add a timeout constraint - // Share the secret only if the verification occurred less than 5min ago - // https://github.com/vector-im/riot-ios/issues/3023 - - NSString *secret = [_crypto.store secretWithSecretId:request.name]; - if (!secret) - { - MXLogDebug(@"[MXSecretShareManager] handleSecretRequestEvent: Unknown secret id: %@", request.name); - return; - } - - [self shareSecret:secret toRequest:request]; -} - -- (void)handleSecretRequestCancellation:(MXSecretShareRequest*)request -{ - MXLogDebug(@"[MXSecretShareManager] handleSecretRequestCancellation: %@ from device %@", request.name, request.requestingDeviceId); - - // Store cancelled requests - // Those requests will be ignored at the end of the sync processing - if (self.crypto.mxSession.state == MXSessionStateSyncInProgress - || self.crypto.mxSession.state == MXSessionStateBackgroundSyncInProgress) - { - [cancelledRequestIds addObject:request.requestId]; - } -} - -- (void)shareSecret:(NSString*)secret toRequest:(MXSecretShareRequest*)request -{ - MXLogDebug(@"[MXSecretShareManager] shareSecret: %@ to device %@", request.name, request.requestingDeviceId); - - MXCredentials *myUser = _crypto.mxSession.matrixRestClient.credentials; - - MXDeviceInfo *device = [_crypto.store deviceWithDeviceId:request.requestingDeviceId forUser:myUser.userId]; - if (!device) - { - MXLogDebug(@"[MXSecretShareManager] shareSecret: ERROR: Unknown device: %@", request.requestingDeviceId); - return; - } - - NSDictionary *userDevice = @{ - myUser.userId: @[device] - }; - - [_crypto ensureOlmSessionsForDevices:userDevice force:NO success:^(MXUsersDevicesMap *results) { - - // Build the response - MXSecretShareSend *shareSend = [MXSecretShareSend new]; - shareSend.requestId = request.requestId; - shareSend.secret = secret; - - NSDictionary *message = @{ - @"type": kMXEventTypeStringSecretSend, - @"content": shareSend.JSONDictionary - }; - - // Encrypt it - NSDictionary *encryptedContent = [self.crypto encryptMessage:message - forDevices:@[device]]; - - // Send it encrypted as an m.room.encrypted to-device event. - MXUsersDevicesMap *contentMap = [MXUsersDevicesMap new]; - [contentMap setObject:encryptedContent forUser:myUser.userId andDevice:device.deviceId]; - - MXToDevicePayload *payload = [[MXToDevicePayload alloc] initWithEventType:kMXEventTypeStringRoomEncrypted - contentMap:contentMap]; - [self.crypto.matrixRestClient sendToDevice:payload success:nil failure:^(NSError *error) { - MXLogDebug(@"[MXSecretShareManager] shareSecret: ERROR for sendToDevice: %@", error); - }]; - - } failure:^(NSError *error) { - MXLogDebug(@"[MXSecretShareManager] shareSecret: ERROR for ensureOlmSessionsForDevices: %@", error); - }]; -} - - -- (void)handleSecretSendEvent:(MXEvent*)event -{ - if (![self canAcceptSecretSendEvent:event]) - { - MXLogDebug(@"[MXSecretShareManager] handleSecretSendEvent: Rejecting unacceptable secret"); - return; - } - - MXSecretShareSend *shareSend; - MXJSONModelSetMXJSONModel(shareSend, MXSecretShareSend, event.content); - if (!shareSend) - { - MXLogDebug(@"[MXSecretShareManager] handleSecretSendEvent: Bad content format: %@", event.JSONDictionary); - return; - } - - MXPendingSecretShareRequest *pendingRequest = pendingSecretShareRequests[shareSend.requestId]; - if (!pendingRequest) - { - MXLogDebug(@"[MXSecretShareManager] handleSecretSendEvent: Unexpected response to request: %@", shareSend.requestId); - return; - } - - if (pendingRequest.onSecretReceivedBlock(shareSend.secret)) - { - MXLogDebug(@"[MXSecretShareManager] handleSecretSendEvent: Secret has been validated. Cancel the request %@", shareSend.requestId); - [self cancelRequestWithRequestId:shareSend.requestId success:^{} failure:^(NSError * _Nonnull error) {}]; - } - else - { - MXLogDebug(@"[MXSecretShareManager] handleSecretSendEvent: Not valid secret. Keep request %@ on", shareSend.requestId); - } -} - -- (BOOL)canAcceptSecretSendEvent:(MXEvent*)event -{ - // No need to download keys, after a verification we already forced download - MXDeviceInfo *sendingDevice = [self.crypto.store deviceWithIdentityKey:event.senderKey]; - if (!sendingDevice) - { - MXLogError(@"[MXSecretShareManager] canAcceptSecretSendEvent: Unknown sending device"); - return NO; - } - - if (![sendingDevice.userId isEqualToString:self.crypto.mxSession.myUserId]) - { - MXLogDebug(@"[MXSecretShareManager] canAcceptSecretSendEvent: Ignoring secret from another user"); - return NO; - } - - if (!sendingDevice.trustLevel.isVerified) - { - MXLogDebug(@"[MXSecretShareManager] canAcceptSecretSendEvent: Ignoring secret from untrusted device"); - return NO; - } - return YES; -} - - -@end diff --git a/MatrixSDK/Crypto/KeySharing/Secret/MXSecretShareManager_Private.h b/MatrixSDK/Crypto/KeySharing/Secret/MXSecretShareManager_Private.h deleted file mode 100644 index 0f47794b81..0000000000 --- a/MatrixSDK/Crypto/KeySharing/Secret/MXSecretShareManager_Private.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - Copyright 2020 The Matrix.org Foundation C.I.C - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -#import "MXSecretShareManager.h" - -@class MXLegacyCrypto; - -NS_ASSUME_NONNULL_BEGIN - -@interface MXSecretShareManager () - -/** - Constructor. - - @param crypto the related 'MXCrypto' instance. - */ -- (instancetype)initWithCrypto:(MXLegacyCrypto *)crypto; - -@end - -NS_ASSUME_NONNULL_END diff --git a/MatrixSDK/Crypto/MXCrypto.h b/MatrixSDK/Crypto/MXCrypto.h index 4b7f1f3114..b28319964e 100644 --- a/MatrixSDK/Crypto/MXCrypto.h +++ b/MatrixSDK/Crypto/MXCrypto.h @@ -383,230 +383,3 @@ extern NSString *const MXDeviceListDidUpdateUsersDevicesNotification; @end NS_ASSUME_NONNULL_END - -MX_ASSUME_MISSING_NULLABILITY_BEGIN - -@interface MXLegacyCrypto : NSObject - -/** - The olm library version. - */ -@property (nonatomic, readonly) NSString *olmVersion; - -/** - The secret storage on homeserver manager. - */ -@property (nonatomic, readonly) MXSecretStorage *secretStorage; - -/** - The secret share manager. - */ -@property (nonatomic, readonly) MXSecretShareManager *secretShareManager; - -/** - Create a new crypto instance and data for the given user. - - @param mxSession the session on which to enable crypto. - @param error pointer to error that is non-nil if crypto failed to be created - @return the fresh crypto instance. - */ -+ (id)createCryptoWithMatrixSession:(MXSession*)mxSession - error:(NSError **)error; - -/** - Initialize the crypto module - - If the user has previously enabled crypto it will be opened, otherwise a new crypto - store will be created. - - @param migrationProgress a block called repeatedly with percentage of migration done, if any necessasry - @param complete a block called in any case when the operation completes. - */ -+ (void)initializeCryptoWithMatrixSession:(MXSession*)mxSession - migrationProgress:(void (^)(double progress))migrationProgress - complete:(void (^)(id crypto, NSError *error))complete; - -/** - Stores the exportedOlmDevice related to the credentials into the store. - - @param exportedOlmDevice OlmDevice data to be stored - @param credentials credentials related to the exportedOlmDevice - @param complete a block called in any case when the operation completes. - */ -+ (void)rehydrateExportedOlmDevice:(MXExportedOlmDevice*)exportedOlmDevice - withCredentials:(MXCredentials *)credentials - complete:(void (^)(BOOL success))complete; - -/** - Check if we have keys to decrypt an event. - - @param event the event to decrypt. - - @param onComplete the block called when the operations completes. It returns the result - */ -- (void)hasKeysToDecryptEvent:(MXEvent*)event - onComplete:(void (^)(BOOL))onComplete; - - -/** - Handle list of changed users provided in the /sync response. - - @param deviceLists the list of users who have a change in their devices. - */ -- (void)handleDeviceListsChanges:(MXDeviceListResponse*)deviceLists; - -/** - Handle one-time keys count returned in the /sync response. - - @param deviceOneTimeKeysCount the number of one-time keys the server has for our device. - */ -- (void)handleDeviceOneTimeKeysCount:(NSDictionary*)deviceOneTimeKeysCount; - -/** - Handle the unused fallback keys returned in the /sync response. - - @param deviceUnusedFallbackKeys the algorithms for which there are unused fallback keys - */ -- (void)handleDeviceUnusedFallbackKeys:(NSArray *)deviceUnusedFallbackKeys; - -/** - Handle a room key event. - - @param event the room key event. - @param onComplete the block called when the operation completes. - */ -- (void)handleRoomKeyEvent:(MXEvent*)event onComplete:(void (^)(void))onComplete; - -/** - Handle the completion of a /sync. - - This is called after the processing of each successful /sync response. - It is an opportunity to do a batch process on the information received. - - @param oldSyncToken The 'since' token passed to /sync. nil for the first successful - sync since this client was started. - @param nextSyncToken The 'next_batch' result from /sync, which will become the 'since' - token for the next call to /sync. - @param catchingUp YES if we are working our way through a backlog of events after connecting. - */ -- (void)onSyncCompleted:(NSString*)oldSyncToken nextSyncToken:(NSString*)nextSyncToken catchingUp:(BOOL)catchingUp; - -/** - Move all the passed devices from the MXDeviceUnknown state to MXDeviceUnverified. - - @param devices the list of devices. - - @param complete A block object called when the operation completes. - */ -- (void)setDevicesKnown:(MXUsersDevicesMap*)devices - complete:(void (^)(void))complete; - -/** - Reset replay attack data for the given timeline. - - @param timeline the id of the timeline. - */ -- (void)resetReplayAttackCheckInTimeline:(NSString*)timeline; - -/** - Reset stored devices keys. - - This method, to take effect, must be called before [MXSession start] when MXSession - is going to do an initial /sync, ie when the app cleared its cache. - - It helps the end user to fix UISIs that other people get from his messages. - */ -- (void)resetDeviceKeys; - -/** - Delete the crypto store. - - @param onComplete the callback called once operation is done. - */ -- (void)deleteStore:(void (^)(void))onComplete; - -/** - Make requests to get key private keys from other user's devices. - */ -- (void)requestAllPrivateKeys; - -/** - Get all pending key requests sorted by userId/deviceId pairs. - - @param onComplete A block object called with the list of pending key requests. - */ -- (void)pendingKeyRequests:(void (^)(MXUsersDevicesMap *> *pendingKeyRequests))onComplete; - -/** - Send response to a key request. - - @param keyRequest the accepted key request. - @param success A block object called when the operation succeeds. - @param failure A block object called when the operation fails. - */ -- (void)acceptKeyRequest:(MXIncomingRoomKeyRequest *)keyRequest - success:(void (^)(void))success - failure:(void (^)(NSError *error))failure; - - -/** - Send responses to the key requests made by a user's device. - - @param userId the id of the user. - @param deviceId the id of the user's device. - @param onComplete A block object called when the operation completes. - */ -- (void)acceptAllPendingKeyRequestsFromUser:(NSString*)userId andDevice:(NSString*)deviceId onComplete:(void (^)(void))onComplete; - -/** - Ignore a key request. - - @param keyRequest the key request to ignore - @param onComplete A block object called when the operation completes. - */ -- (void)ignoreKeyRequest:(MXIncomingRoomKeyRequest *)keyRequest onComplete:(void (^)(void))onComplete; - -/** - Ignore all pending key requests made by a user's device. - - @param userId the id of the user. - @param deviceId the id of the user's device. - @param onComplete A block object called when the operation completes. - */ -- (void)ignoreAllPendingKeyRequestsFromUser:(NSString*)userId andDevice:(NSString*)deviceId onComplete:(void (^)(void))onComplete; - -/** - Enable or disable outgoing key share requests. - Enabled by default - - @param enabled the new enable state. - @param onComplete the block called when the operation completes - */ -- (void)setOutgoingKeyRequestsEnabled:(BOOL)enabled onComplete:(void (^)(void))onComplete; - -- (BOOL)isOutgoingKeyRequestsEnabled; - -/** - Automatically re-enable outgoing key share requests once another device has been verified. - - Default is YES. - */ -@property (nonatomic) BOOL enableOutgoingKeyRequestsOnceSelfVerificationDone; - -/** - Warn (generates a NSError) when the user wants to send a message in a room where - there is at least one device they have never seen. - - Default is YES. - */ -@property (nonatomic) BOOL warnOnUnknowDevices; - -/** - Get the current shared history status of the room, which depends on its `m.room.history_visibility` - (history is considered shared if visibility is set to `shared` or `world_readable`) - */ -- (BOOL)isRoomSharingHistory:(NSString *)roomId; - -@end - -MX_ASSUME_MISSING_NULLABILITY_END diff --git a/MatrixSDK/Crypto/MXCrypto.m b/MatrixSDK/Crypto/MXCrypto.m index c75c39b8d0..c63e27732a 100644 --- a/MatrixSDK/Crypto/MXCrypto.m +++ b/MatrixSDK/Crypto/MXCrypto.m @@ -19,42 +19,6 @@ #import "MXCrypto.h" -#import "MXCrypto_Private.h" - -#import "MXSession.h" -#import "MXTools.h" - -#import "MXOlmDevice.h" -#import "MXUsersDevicesMap.h" -#import "MXDeviceInfo.h" -#import "MXKey.h" - -#import "MXRealmCryptoStore.h" -#import "MXCryptoMigration.h" - -#import "MXMegolmSessionData.h" -#import "MXMegolmExportEncryption.h" - -#import "MXOutgoingRoomKeyRequestManager.h" -#import "MXIncomingRoomKeyRequestManager.h" - -#import "MXSecretStorage_Private.h" -#import "MXSecretShareManager_Private.h" -#import "MXRecoveryService_Private.h" - -#import "MXKeyVerificationManager_Private.h" -#import "MXDeviceInfo_Private.h" -#import "MXCrossSigningInfo_Private.h" -#import "MXCrossSigning_Private.h" - -#import "NSArray+MatrixSDK.h" - -#import "MXDeviceListResponse.h" - -#import "MatrixSDKSwiftHeader.h" -#import "MXSharedHistoryKeyService.h" -#import "MXNativeKeyBackupEngine.h" - #warning File has not been annotated with nullability, see MX_ASSUME_MISSING_NULLABILITY_BEGIN /** @@ -72,3186 +36,3 @@ static NSString *const kMXCryptoOneTimeKeyClaimCompleteNotification = @"kMXCryptoOneTimeKeyClaimCompleteNotification"; static NSString *const kMXCryptoOneTimeKeyClaimCompleteNotificationDevicesKey = @"kMXCryptoOneTimeKeyClaimCompleteNotificationDevicesKey"; static NSString *const kMXCryptoOneTimeKeyClaimCompleteNotificationErrorKey = @"kMXCryptoOneTimeKeyClaimCompleteNotificationErrorKey"; - - -#ifdef MX_CRYPTO - -// Frequency with which to check & upload one-time keys -NSTimeInterval kMXCryptoUploadOneTimeKeysPeriod = 60.0; // one minute -NSTimeInterval kMXCryptoMinForceSessionPeriod = 3600.0; // one hour - -@interface MXLegacyCrypto () -{ - // MXEncrypting instance for each room. - NSMutableDictionary> *roomEncryptors; - - // A map from algorithm to MXDecrypting instance, for each room - NSMutableDictionary>*> *roomDecryptors; - - // Listener on memberships changes - id roomMembershipEventsListener; - - // The one-time keys count sent by /sync - // -1 means the information was not sent by the server - NSUInteger oneTimeKeyCount; - - // Last time we check available one-time keys on the homeserver - NSDate *lastOneTimeKeyCheck; - - // The current one-time key operation, if any - MXHTTPOperation *uploadOneTimeKeysOperation; - - // The operation used for crypto starting requests - MXHTTPOperation *startOperation; - - // The manager for sending room key requests - MXOutgoingRoomKeyRequestManager *outgoingRoomKeyRequestManager; - - // The manager for incoming room key requests - MXIncomingRoomKeyRequestManager *incomingRoomKeyRequestManager; - - // The manager for unrequested m.forwarded_room_keys - MXUnrequestedForwardedRoomKeyManager *unrequestedForwardedRoomKeyManager; - - // The date of the last time we forced establishment - // of a new session for each user:device. - MXUsersDevicesMap *lastNewSessionForcedDates; - - // The dedicated queue used for decryption. - // This queue is used to get the key from the crypto store and decrypt the event. No more. - // Thus, it can respond quicker than cryptoQueue for this operation that must return - // synchronously for MXSession. - dispatch_queue_t decryptionQueue; - - // The queue to manage bulk import and export of keys. - // It only reads and writes keys from and to the crypto store. - dispatch_queue_t cargoQueue; - - // The list of devices (by their identity key) we are establishing - // an olm session with. - NSMutableArray *ensureOlmSessionsInProgress; - - // Migration tool - MXCryptoMigration *cryptoMigration; -} - -// The current fallback key operation, if any -@property(nonatomic, strong) MXHTTPOperation *uploadFallbackKeyOperation; - -@end - -#endif - -@implementation MXLegacyCrypto - -@synthesize backup = _backup; -@synthesize crossSigning = _crossSigning; -@synthesize keyVerificationManager = _keyVerificationManager; -@synthesize recoveryService = _recoveryService; - -+ (id)createCryptoWithMatrixSession:(MXSession *)mxSession - error:(NSError **)error -{ - __block id crypto; - -#ifdef MX_CRYPTO - dispatch_queue_t cryptoQueue = [MXLegacyCrypto dispatchQueueForUser:mxSession.matrixRestClient.credentials.userId]; - dispatch_sync(cryptoQueue, ^{ - - MXCryptoStoreClass *cryptoStore = [MXCryptoStoreClass createStoreWithCredentials:mxSession.matrixRestClient.credentials]; - cryptoStore.cryptoVersion = MXCryptoVersionLast; - crypto = [[MXLegacyCrypto alloc] initWithMatrixSession:mxSession cryptoQueue:cryptoQueue andStore:cryptoStore]; - - }); -#endif - - return crypto; -} - -+ (void)initializeCryptoWithMatrixSession:(MXSession *)mxSession - migrationProgress:(void (^)(double))migrationProgress - complete:(void (^)(id crypto, NSError *error))complete -{ -#ifdef MX_CRYPTO - [self initalizeLegacyCryptoWithMatrixSession:mxSession complete:complete]; -#else - complete(nil); -#endif -} - -+ (void)initalizeLegacyCryptoWithMatrixSession:(MXSession*)mxSession complete:(void (^)(id crypto, NSError *error))complete -{ -#ifdef MX_CRYPTO - - MXLogDebug(@"[MXCrypto] checkCryptoWithMatrixSession for %@", mxSession.matrixRestClient.credentials.userId); - - dispatch_queue_t cryptoQueue = [MXLegacyCrypto dispatchQueueForUser:mxSession.matrixRestClient.credentials.userId]; - dispatch_async(cryptoQueue, ^{ - - // clear the read-only store - [MXCryptoStoreClass deleteReadonlyStoreWithCredentials:mxSession.credentials]; - - if ([MXCryptoStoreClass hasDataForCredentials:mxSession.matrixRestClient.credentials]) - { - MXLogDebug(@"[MXCrypto] checkCryptoWithMatrixSession: Crypto store exists"); - - // If it already exists, init store and crypto - MXCryptoStoreClass *cryptoStore = [[MXCryptoStoreClass alloc] initWithCredentials:mxSession.matrixRestClient.credentials]; - - MXLogDebug(@"[MXCrypto] checkCryptoWithMatrixSession: Crypto store initialized"); - - id crypto = [[MXLegacyCrypto alloc] initWithMatrixSession:mxSession cryptoQueue:cryptoQueue andStore:cryptoStore]; - - dispatch_async(dispatch_get_main_queue(), ^{ - complete(crypto, nil); - }); - } - else if ([MXSDKOptions sharedInstance].enableCryptoWhenStartingMXSession - // Without the device id provided by the hs, the crypto does not work - && mxSession.matrixRestClient.credentials.deviceId) - { - MXLogDebug(@"[MXCrypto] checkCryptoWithMatrixSession: Need to create the store"); - - // Create it - MXCryptoStoreClass *cryptoStore = [MXCryptoStoreClass createStoreWithCredentials:mxSession.matrixRestClient.credentials]; - cryptoStore.cryptoVersion = MXCryptoVersionLast; - id crypto = [[MXLegacyCrypto alloc] initWithMatrixSession:mxSession cryptoQueue:cryptoQueue andStore:cryptoStore]; - - dispatch_async(dispatch_get_main_queue(), ^{ - complete(crypto, nil); - }); - } - else - { - // Else do not enable crypto - dispatch_async(dispatch_get_main_queue(), ^{ - complete(nil, nil); - }); - } - - }); - -#else - complete(nil, nil); -#endif -} - -+ (void)rehydrateExportedOlmDevice:(MXExportedOlmDevice*)exportedOlmDevice - withCredentials:(MXCredentials *)credentials - complete:(void (^)(BOOL success))complete; -{ -#ifdef MX_CRYPTO - dispatch_queue_t cryptoQueue = [MXLegacyCrypto dispatchQueueForUser:credentials.userId]; - dispatch_async(cryptoQueue, ^{ - if ([MXCryptoStoreClass hasDataForCredentials:credentials]) - { - MXLogErrorDetails(@"the exported Olm device shouldn't exist locally", @{ - @"device_id": credentials.deviceId ?: @"unknown" - }); - complete(false); - return; - } - - // Create a new store for the given credentials - MXCryptoStoreClass *cryptoStore = [MXCryptoStoreClass createStoreWithCredentials:credentials]; - cryptoStore.cryptoVersion = MXCryptoVersionLast; - - // store the exported olm account - NSError *error = nil; - OLMAccount *olmAccount = [[OLMAccount alloc] initWithSerializedData:exportedOlmDevice.pickledAccount key:exportedOlmDevice.pickleKey error:&error]; - [cryptoStore setAccount:olmAccount]; - - complete(error == nil); - }); -#else - complete(false); -#endif -} - -- (void)deleteStore:(void (^)(void))onComplete; -{ -#ifdef MX_CRYPTO - MXWeakify(self); - dispatch_async(_cryptoQueue, ^{ - MXStrongifyAndReturnIfNil(self); - - [MXCryptoStoreClass deleteStoreWithCredentials:self.mxSession.matrixRestClient.credentials]; - - if (onComplete) - { - dispatch_async(dispatch_get_main_queue(), ^{ - onComplete(); - }); - } - }); -#endif -} - -- (void)start:(void (^)(void))success - failure:(void (^)(NSError *error))failure -{ - -#ifdef MX_CRYPTO - MXLogDebug(@"[MXCrypto] start"); - - // The session must be initialised enough before starting this module - if (!_mxSession.myUser.userId) - { - MXLogError(@"[MXCrypto] start. ERROR: mxSession.myUser.userId cannot be nil"); - failure(nil); - return; - } - - // Check migration - if ([cryptoMigration shouldMigrate]) - { - MXWeakify(self); - [cryptoMigration migrateWithSuccess:^{ - MXStrongifyAndReturnIfNil(self); - - // Migration is done - self->cryptoMigration = nil; - [self start:success failure:failure]; - - } failure:^(NSError * _Nonnull error) { - MXStrongifyAndReturnIfNil(self); - - // We have no mandatory migration for now - // We can try again at the next MXCrypto startup - MXLogDebug(@"[MXCrypto] start. Migration failed. Ignore it for now"); - self->cryptoMigration = nil; - [self start:success failure:failure]; - }]; - return; - } - else - { - cryptoMigration = nil; - } - - // Start uploading user device keys - MXWeakify(self); - startOperation = [self uploadDeviceKeys:^(MXKeysUploadResponse *keysUploadResponse) { - MXStrongifyAndReturnIfNil(self); - - if (!self->startOperation) - { - return; - } - - // Upload our one-time keys - // TODO: matrix-js-sdk does not do it anymore and waits for the completion - // of /sync (see comments of the other usage of maybeUploadOneTimeKeys in - // this file) - // On iOS, for test purpose, we still need to know when the OTKs are sent - // so that we can start sending message to a device. - // Keep maybeUploadOneTimeKeys for the moment. - MXWeakify(self); - [self maybeUploadOneTimeKeys:^{ - MXStrongifyAndReturnIfNil(self); - - MXLogDebug(@"[MXCrypto] start ###########################################################"); - MXLogDebug(@"[MXCrypto] uploadDeviceKeys done for %@: ", self.mxSession.myUserId); - - MXLogDebug(@"[MXCrypto] - device id : %@", self.store.deviceId); - MXLogDebug(@"[MXCrypto] - ed25519 : %@", self.olmDevice.deviceEd25519Key); - MXLogDebug(@"[MXCrypto] - curve25519 : %@", self.olmDevice.deviceCurve25519Key); - MXLogDebug(@"[MXCrypto] "); - MXLogDebug(@"[MXCrypto] Store: %@", self.store); - MXLogDebug(@"[MXCrypto] "); - - [self->_crossSigning refreshStateWithSuccess:nil failure:nil]; - - [self->outgoingRoomKeyRequestManager start]; - - [self->_backup checkAndStartKeyBackup]; - - dispatch_async(dispatch_get_main_queue(), ^{ - self->startOperation = nil; - success(); - }); - - - } failure:^(NSError *error) { - MXStrongifyAndReturnIfNil(self); - - MXLogErrorDetails(@"[MXCrypto] start. Error in maybeUploadOneTimeKeys", @{ - @"error": error ?: @"unknown" - }); - dispatch_async(dispatch_get_main_queue(), ^{ - self->startOperation = nil; - failure(error); - }); - }]; - - } failure:^(NSError *error) { - MXStrongifyAndReturnIfNil(self); - - MXLogErrorDetails(@"[MXCrypto] start. Error in uploadDeviceKeys", @{ - @"error": error ?: @"unknown" - }); - dispatch_async(dispatch_get_main_queue(), ^{ - self->startOperation = nil; - failure(error); - }); - }]; - -#endif -} - -- (void)close:(BOOL)deleteStore -{ -#ifdef MX_CRYPTO - - MXLogDebug(@"[MXCrypto] close. store: %@", _store); - - [_mxSession removeListener:roomMembershipEventsListener]; - - [startOperation cancel]; - startOperation = nil; - - if (_myDevice == nil) - { - MXLogDebug(@"[MXCrypto] close: already closed"); - return; - } - - MXWeakify(self); - dispatch_sync(_cryptoQueue, ^{ - MXStrongifyAndReturnIfNil(self); - - // Cancel pending one-time keys upload - [self->uploadOneTimeKeysOperation cancel]; - self->uploadOneTimeKeysOperation = nil; - - [self.uploadFallbackKeyOperation cancel]; - self.uploadFallbackKeyOperation = nil; - - [self->outgoingRoomKeyRequestManager close]; - self->outgoingRoomKeyRequestManager = nil; - - [self->unrequestedForwardedRoomKeyManager close]; - self->outgoingRoomKeyRequestManager = nil; - - if (deleteStore) - { - [MXCryptoStoreClass deleteStoreWithCredentials:self.mxSession.matrixRestClient.credentials]; - } - - self->_olmDevice = nil; - self->_cryptoQueue = nil; - self->_store = nil; - - [self.deviceList close]; - self->_deviceList = nil; - - [self.matrixRestClient close]; - self->_matrixRestClient = nil; - - [self->roomEncryptors removeAllObjects]; - self->roomEncryptors = nil; - - [self->roomDecryptors removeAllObjects]; - self->roomDecryptors = nil; - - self->_backup = nil; - self->_keyVerificationManager = nil; - self->_recoveryService = nil; - self->_secretStorage = nil; - self->_secretShareManager = nil; - self->_crossSigning = nil; - - self->_myDevice = nil; - - MXLogDebug(@"[MXCrypto] close: done"); - }); - -#endif -} - -- (MXHTTPOperation *)encryptEventContent:(NSDictionary *)eventContent withType:(MXEventTypeString)eventType inRoom:(MXRoom *)room - success:(void (^)(NSDictionary *, NSString *))success - failure:(void (^)(NSError *))failure -{ -#ifdef MX_CRYPTO - - MXLogDebug(@"[MXCrypto] encryptEventContent"); - - NSDate *startDate = [NSDate date]; - - // Create an empty operation that will be mutated later - MXHTTPOperation *operation = [[MXHTTPOperation alloc] init]; - - // Pick the list of recipients based on the membership list. - - // TODO: there is a race condition here! What if a new user turns up - // just as you are sending a secret message? - - MXWeakify(self); - [room state:^(MXRoomState *roomState) { - MXStrongifyAndReturnIfNil(self); - - MXWeakify(self); - [room members:^(MXRoomMembers *roomMembers) { - MXStrongifyAndReturnIfNil(self); - - NSMutableArray *userIds = [NSMutableArray array]; - NSArray *encryptionTargetMembers = [roomMembers encryptionTargetMembers:roomState.historyVisibility]; - for (MXRoomMember *roomMember in encryptionTargetMembers) - { - [userIds addObject:roomMember.userId]; - } - - MXWeakify(self); - dispatch_async(self.cryptoQueue, ^{ - MXStrongifyAndReturnIfNil(self); - - NSString *algorithm; - id alg = self->roomEncryptors[room.roomId]; - - MXLogDebug(@"[MXCrypto] encryptEventContent: with %@ for %@ users", roomState.encryptionAlgorithm, @(userIds.count)); - - if (!alg) - { - // If the crypto has been enabled after the initialSync (the global one or the one for this room), - // the algorithm has not been initialised yet. So, do it now from room state information - algorithm = roomState.encryptionAlgorithm; - if (!algorithm) - { - algorithm = [self->_store algorithmForRoom:room.roomId]; - MXLogWarning(@"[MXCrypto] encryptEventContent: roomState.encryptionAlgorithm is nil for room %@. Try to use algorithm in the crypto store: %@", room.roomId, algorithm); - } - - if (algorithm) - { - [self setEncryptionInRoom:room.roomId withMembers:userIds algorithm:algorithm inhibitDeviceQuery:NO]; - alg = self->roomEncryptors[room.roomId]; - } - } - else - { - // For log purpose - algorithm = NSStringFromClass(alg.class); - } - - // Sanity check (we don't expect an encrypted content here). - if (alg && [eventType isEqualToString:kMXEventTypeStringRoomEncrypted] == NO) - { -#ifdef DEBUG - MXLogDebug(@"[MXCrypto] encryptEventContent: content: %@", eventContent); -#endif - - MXHTTPOperation *operation2 = [alg encryptEventContent:eventContent eventType:eventType forUsers:userIds success:^(NSDictionary *encryptedContent) { - - MXLogDebug(@"[MXCrypto] encryptEventContent: Success in %.0fms using sessionId: %@", - [[NSDate date] timeIntervalSinceDate:startDate] * 1000, - encryptedContent[@"session_id"]); - - dispatch_async(dispatch_get_main_queue(), ^{ - success(encryptedContent, kMXEventTypeStringRoomEncrypted); - }); - - } failure:^(NSError *error) { - MXLogErrorDetails(@"[MXCrypto] encryptEventContent: Error", @{ - @"error": error ?: @"unknown" - }); - - dispatch_async(dispatch_get_main_queue(), ^{ - failure(error); - }); - }]; - - // Mutate the HTTP operation if an HTTP is required for the encryption - [operation mutateTo:operation2]; - } - else - { - MXLogDebug(@"[MXCrypto] encryptEventContent: Invalid algorithm"); - - NSError *error = [NSError errorWithDomain:MXDecryptingErrorDomain - code:MXDecryptingErrorUnableToEncryptCode - userInfo:@{ - NSLocalizedDescriptionKey: MXDecryptingErrorUnableToEncrypt, - NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:MXDecryptingErrorUnableToEncryptReason, algorithm] - }]; - - dispatch_async(dispatch_get_main_queue(), ^{ - failure(error); - }); - } - }); - - } failure:failure]; - }]; - - return operation; - -#else - return nil; -#endif -} - -- (void)hasKeysToDecryptEvent:(MXEvent *)event onComplete:(void (^)(BOOL))onComplete -{ -#ifdef MX_CRYPTO - - // We need to go to decryptionQueue only to use getRoomDecryptor - // Other subsequent calls are thread safe because of the implementation of MXCryptoStore - dispatch_async(decryptionQueue, ^{ - NSString *algorithm = event.wireContent[@"algorithm"]; - id alg = [self getRoomDecryptor:event.roomId algorithm:algorithm]; - - BOOL hasKeys = [alg hasKeysToDecryptEvent:event]; - onComplete(hasKeys); - }); - -#endif -} - -- (MXEventDecryptionResult *)decryptEvent:(MXEvent *)event inTimeline:(NSString*)timeline -{ - MXEventDecryptionResult *result; - - if (!event.content.count) - { - MXLogDebug(@"[MXCrypto] decryptEvent: No content to decrypt in event %@ (isRedacted: %@). Event: %@", event.eventId, @(event.isRedactedEvent), event.JSONDictionary); - result = [[MXEventDecryptionResult alloc] init]; - result.clearEvent = event.content; - return result; - } - - NSString *algorithm = event.wireContent[@"algorithm"]; - id alg = [self getRoomDecryptor:event.roomId algorithm:algorithm]; - if (!alg) - { - MXLogDebug(@"[MXCrypto] decryptEvent: Unable to decrypt %@ with algorithm %@. Event: %@", event.eventId, algorithm, event.JSONDictionary); - - result = [MXEventDecryptionResult new]; - result.error = [NSError errorWithDomain:MXDecryptingErrorDomain - code:MXDecryptingErrorUnableToDecryptCode - userInfo:@{ - NSLocalizedDescriptionKey: MXDecryptingErrorUnableToDecrypt, - NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:MXDecryptingErrorUnableToDecryptReason, event, algorithm] - }]; - } - else - { - result = [alg decryptEvent:event inTimeline:timeline]; - if (result.error) - { - NSDictionary *details = @{ - @"event_id": event.eventId ?: @"unknown", - @"error": result.error ?: @"unknown" - }; - MXLogErrorDetails(@"[MXCrypto] decryptEvent", details); - MXLogDebug(@"[MXCrypto] decryptEvent: Unable to decrypt event %@", event.JSONDictionary); - - if ([result.error.domain isEqualToString:MXDecryptingErrorDomain] - && result.error.code == MXDecryptingErrorBadEncryptedMessageCode) - { - dispatch_async(self.cryptoQueue, ^{ - [self markOlmSessionForUnwedgingInEvent:event]; - }); - } - } - } - - return result; -} - -- (void)decryptEvents:(NSArray *)events - inTimeline:(NSString*)timeline - onComplete:(void (^)(NSArray*))onComplete -{ - dispatch_async(decryptionQueue, ^{ - NSMutableArray *results = [NSMutableArray arrayWithCapacity:events.count]; - - // TODO: Implement bulk decryption to speed up the process. - // We need a [MXDecrypting decryptEvents:] method to limit the number of back and forth with olm/megolm module. - for (MXEvent *event in events) - { - [results addObject:[self decryptEvent:event inTimeline:timeline]]; - } - - dispatch_async(dispatch_get_main_queue(), ^{ - onComplete(results); - }); - }); -} - -- (MXHTTPOperation*)ensureEncryptionInRoom:(NSString*)roomId - success:(void (^)(void))success - failure:(void (^)(NSError *error))failure -{ - // Create an empty operation that will be mutated later - MXHTTPOperation *operation = [[MXHTTPOperation alloc] init]; - -#ifdef MX_CRYPTO - MXRoom *room = [_mxSession roomWithRoomId:roomId]; - if (room.summary.isEncrypted) - { - MXWeakify(self); - [room state:^(MXRoomState *roomState) { - MXStrongifyAndReturnIfNil(self); - - MXWeakify(self); - [room members:^(MXRoomMembers *roomMembers) { - MXStrongifyAndReturnIfNil(self); - - // Get user ids in this room - NSMutableArray *userIds = [NSMutableArray array]; - NSArray *encryptionTargetMembers = [roomMembers encryptionTargetMembers:roomState.historyVisibility]; - for (MXRoomMember *member in encryptionTargetMembers) - { - [userIds addObject:member.userId]; - } - - MXWeakify(self); - dispatch_async(self.cryptoQueue, ^{ - MXStrongifyAndReturnIfNil(self); - - NSString *algorithm; - id alg = self->roomEncryptors[room.roomId]; - - if (!alg) - { - // The algorithm has not been initialised yet. So, do it now from room state information - algorithm = roomState.encryptionAlgorithm; - if (algorithm) - { - [self setEncryptionInRoom:room.roomId withMembers:userIds algorithm:algorithm inhibitDeviceQuery:NO]; - alg = self->roomEncryptors[room.roomId]; - } - } - - if (alg) - { - // Check we have everything to encrypt events - MXHTTPOperation *operation2 = [alg ensureSessionForUsers:userIds success:^(NSObject *sessionInfo) { - - if (success) - { - dispatch_async(dispatch_get_main_queue(), ^{ - success(); - }); - } - - } failure:^(NSError *error) { - if (failure) - { - dispatch_async(dispatch_get_main_queue(), ^{ - failure(error); - }); - } - }]; - - [operation mutateTo:operation2]; - } - else if (failure) - { - NSError *error = [NSError errorWithDomain:MXDecryptingErrorDomain - code:MXDecryptingErrorUnableToEncryptCode - userInfo:@{ - NSLocalizedDescriptionKey: MXDecryptingErrorUnableToEncrypt, - NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:MXDecryptingErrorUnableToEncryptReason, algorithm] - }]; - - dispatch_async(dispatch_get_main_queue(), ^{ - failure(error); - }); - } - }); - - - } failure:failure]; - }]; - } - else -#endif - { - if (success) - { - success(); - } - } - - return operation; -} - -- (void)discardOutboundGroupSessionForRoomWithRoomId:(NSString*)roomId onComplete:(void (^)(void))onComplete -{ -#ifdef MX_CRYPTO - MXWeakify(self); - dispatch_async(self.cryptoQueue, ^{ - MXStrongifyAndReturnIfNil(self); - - [self.olmDevice discardOutboundGroupSessionForRoomWithRoomId:roomId]; - - dispatch_async(dispatch_get_main_queue(), ^{ - onComplete(); - }); - }); -#else - onComplete(); -#endif -} - -- (void)handleDeviceListsChanges:(MXDeviceListResponse*)deviceLists -{ -#ifdef MX_CRYPTO - - if (deviceLists.changed.count == 0 && deviceLists.left.count == 0) - { - // Don't go further if there is nothing to process - return; - } - - MXLogDebug(@"[MXCrypto] handleDeviceListsChanges (changes: %@, left: %@):\nchanges: %@\nleft: %@", @(deviceLists.changed.count), @(deviceLists.left.count), - deviceLists.changed, deviceLists.left); - - MXWeakify(self); - dispatch_async(_cryptoQueue, ^{ - MXStrongifyAndReturnIfNil(self); - - // Flag users to refresh - for (NSString *userId in deviceLists.changed) - { - [self.deviceList invalidateUserDeviceList:userId]; - } - - for (NSString *userId in deviceLists.left) - { - [self.deviceList stopTrackingDeviceList:userId]; - } - - // don't flush the outdated device list yet - we do it once we finish - // processing the sync. - }); - -#endif -} - -- (void)handleRoomKeyEvent:(MXEvent*)event onComplete:(void (^)(void))onComplete -{ - // Use decryptionQueue as synchronisation because decryptions require room keys - dispatch_async(decryptionQueue, ^{ - [self onRoomKeyEvent:event]; - - dispatch_async(dispatch_get_main_queue(), ^{ - onComplete(); - }); - }); -} - -- (void)handleDeviceOneTimeKeysCount:(NSDictionary*)deviceOneTimeKeysCount -{ -#ifdef MX_CRYPTO - - if (deviceOneTimeKeysCount.count == 0) - { - // Don't go further if there is nothing to process - return; - } - - MXLogDebug(@"[MXCrypto] handleDeviceOneTimeKeysCount: %@ keys on the homeserver", deviceOneTimeKeysCount[kMXKeySignedCurve25519Type]); - - MXWeakify(self); - dispatch_async(_cryptoQueue, ^{ - MXStrongifyAndReturnIfNil(self); - - NSNumber *currentCount; - MXJSONModelSetNumber(currentCount, deviceOneTimeKeysCount[kMXKeySignedCurve25519Type]); - - if (currentCount) - { - self->oneTimeKeyCount = [currentCount unsignedIntegerValue]; - } - }); - -#endif -} - -- (void)handleDeviceUnusedFallbackKeys:(NSArray *)deviceFallbackKeys -{ -#ifdef MX_CRYPTO - if (deviceFallbackKeys == nil) { - return; - } - - if ([deviceFallbackKeys containsObject:kMXKeySignedCurve25519Type]) { - return; - } - - if (self.uploadFallbackKeyOperation) - { - MXLogDebug(@"[MXCrypto] handleDeviceUnusedFallbackKeys: Fallback key upload already in progress."); - return; - } - - // We will be checking this often enough for it not to warrant automatic retries. - self.uploadFallbackKeyOperation = [self generateAndUploadFallbackKey]; -#endif -} - -- (void)handleSyncResponse:(MXSyncResponse *)syncResponse onComplete:(void (^)(void))onComplete -{ - // Not implemented, the default `MXCrypto` instead uses more specific functions - // such as `handleRoomKeyEvent` and `handleDeviceUnusedFallbackKeys`. The method - // is possibly used by `MXCrypto` subclasses. - onComplete(); -} - -- (void)onSyncCompleted:(NSString *)oldSyncToken nextSyncToken:(NSString *)nextSyncToken catchingUp:(BOOL)catchingUp -{ -#ifdef MX_CRYPTO - - MXWeakify(self); - dispatch_async(_cryptoQueue, ^{ - MXStrongifyAndReturnIfNil(self); - - if (!oldSyncToken) - { - MXLogDebug(@"[MXCrypto] onSyncCompleted: Completed initial sync"); - - // If we have a deviceSyncToken, we can tell the deviceList to - // invalidate devices which have changed since then. - NSString *oldDeviceSyncToken = self.store.deviceSyncToken; - if (oldDeviceSyncToken) - { - MXLogDebug(@"[MXCrypto] onSyncCompleted: invalidating device list from deviceSyncToken: %@", oldDeviceSyncToken); - - [self invalidateDeviceListsSince:oldDeviceSyncToken to:nextSyncToken success:^() { - - self.deviceList.lastKnownSyncToken = nextSyncToken; - [self.deviceList refreshOutdatedDeviceLists]; - - } failure:^(NSError *error) { - - // If that failed, we fall back to invalidating everyone. - MXLogErrorDetails(@"[MXCrypto] onSyncCompleted: Error fetching changed device list", @{ - @"error": error ?: @"unknown" - }); - [self.deviceList invalidateAllDeviceLists]; - }]; - } - else - { - // Otherwise, we have to invalidate all devices for all users we - // are tracking. - MXLogDebug(@"[MXCrypto] onSyncCompleted: Completed first initialsync; invalidating all device list caches"); - [self.deviceList invalidateAllDeviceLists]; - } - } - - // we can now store our sync token so that we can get an update on - // restart rather than having to invalidate everyone. - // - // (we don't really need to do this on every sync - we could just - // do it periodically) - [self.store storeDeviceSyncToken:nextSyncToken]; - - // catch up on any new devices we got told about during the sync. - self.deviceList.lastKnownSyncToken = nextSyncToken; - [self.deviceList refreshOutdatedDeviceLists]; - - // We don't start uploading one-time keys until we've caught up with - // to-device messages, to help us avoid throwing away one-time-keys that we - // are about to receive messages for - // (https://github.com/vector-im/riot-web/issues/2782). - if (!catchingUp) - { - [self maybeUploadOneTimeKeys:nil failure:nil]; - [self->incomingRoomKeyRequestManager processReceivedRoomKeyRequests]; - [self->unrequestedForwardedRoomKeyManager processUnrequestedKeys]; - } - }); - -#endif -} - -- (MXDeviceInfo *)eventDeviceInfo:(MXEvent *)event -{ - __block MXDeviceInfo *device; - -#ifdef MX_CRYPTO - - if (event.isEncrypted) - { - // This is a simple read in the db which is thread safe. - // Return synchronously - NSString *algorithm = event.wireContent[@"algorithm"]; - device = [self.deviceList deviceWithIdentityKey:event.senderKey andAlgorithm:algorithm]; - } - -#endif - - return device; -} - - -#pragma mark - Local trust - -- (void)setDeviceVerification:(MXDeviceVerification)verificationStatus forDevice:(NSString*)deviceId ofUser:(NSString*)userId - success:(void (^)(void))success - failure:(void (^)(NSError *error))failure -{ -#ifdef MX_CRYPTO - dispatch_async(_cryptoQueue, ^{ - [self setDeviceVerification2:verificationStatus forDevice:deviceId ofUser:userId downloadIfNeeded:YES success:success failure:failure]; - }); -#else - if (success) - { - success(); - } -#endif -} - -- (void)setDeviceVerification2:(MXDeviceVerification)verificationStatus forDevice:(NSString*)deviceId ofUser:(NSString*)userId - downloadIfNeeded:(BOOL)downloadIfNeeded - success:(void (^)(void))success - failure:(void (^)(NSError *error))failure -{ -#ifdef MX_CRYPTO - MXDeviceInfo *device = [self.store deviceWithDeviceId:deviceId forUser:userId]; - - // Sanity check - if (!device) - { - if (downloadIfNeeded) - { - MXLogDebug(@"[MXCrypto] setDeviceVerificationForDevice: Unknown device. Try to download user's keys for %@:%@", userId, deviceId); - [self.deviceList downloadKeys:@[userId] forceDownload:YES success:^(MXUsersDevicesMap *usersDevicesInfoMap, NSDictionary *crossSigningKeysMap) { - [self setDeviceVerification2:verificationStatus forDevice:deviceId ofUser:userId downloadIfNeeded:NO success:success failure:failure]; - } failure:^(NSError *error) { - if (failure) - { - dispatch_async(dispatch_get_main_queue(), ^{ - failure(error); - }); - } - }]; - } - else - { - MXLogDebug(@"[MXCrypto] setDeviceVerificationForDevice: Unknown device %@:%@", userId, deviceId); - if (failure) - { - dispatch_async(dispatch_get_main_queue(), ^{ - failure(nil); - }); - } - } - return; - } - - MXDeviceTrustLevel *trustLevel = [MXDeviceTrustLevel trustLevelWithLocalVerificationStatus:verificationStatus - crossSigningVerified:device.trustLevel.isCrossSigningVerified]; - [device updateTrustLevel:trustLevel]; - [self.store storeDeviceForUser:userId device:device]; - - if ([userId isEqualToString:self.mxSession.myUserId]) - { - // If one of the user's own devices is being marked as verified / unverified, - // check the key backup status, since whether or not we use this depends on - // whether it has a signature from a verified device - [self.backup checkAndStartKeyBackup]; - - // Manage self-verification - if (verificationStatus == MXDeviceVerified) - { - // This is a good time to request all private keys - MXLogDebug(@"[MXCrypto] setDeviceVerificationForDevice: Request all private keys"); - [self scheduleRequestsForAllPrivateKeys]; - - // Check cross-signing - if (self.crossSigning.canCrossSign) - { - // Cross-sign our own device - MXLogDebug(@"[MXCrypto] setDeviceVerificationForDevice: Mark device %@ as self verified", deviceId); - [self.crossSigning crossSignDeviceWithDeviceId:deviceId userId:userId success:success failure:failure]; - - // Wait the end of cross-sign before returning - return; - } - } - } - - if (success) - { - dispatch_async(dispatch_get_main_queue(), ^{ - success(); - }); - } -#endif -} - -- (void)setDevicesKnown:(MXUsersDevicesMap *)devices complete:(void (^)(void))complete -{ -#ifdef MX_CRYPTO - MXWeakify(self); - dispatch_async(_cryptoQueue, ^{ - MXStrongifyAndReturnIfNil(self); - - for (NSString *userId in devices.userIds) - { - for (NSString *deviceID in [devices deviceIdsForUser:userId]) - { - MXDeviceInfo *device = [devices objectForDevice:deviceID forUser:userId]; - - if (device.trustLevel.localVerificationStatus == MXDeviceUnknown) - { - MXDeviceTrustLevel *trustLevel = - [MXDeviceTrustLevel trustLevelWithLocalVerificationStatus:MXDeviceUnverified - crossSigningVerified:device.trustLevel.isCrossSigningVerified]; - [device updateTrustLevel:trustLevel]; - [self.store storeDeviceForUser:device.userId device:device]; - } - } - } - - if (complete) - { - dispatch_async(dispatch_get_main_queue(), ^{ - complete(); - }); - } - }); -#else - if (complete) - { - complete(); - } -#endif -} - -- (void)setUserVerification:(BOOL)verificationStatus forUser:(NSString*)userId - success:(void (^)(void))success - failure:(void (^)(NSError *error))failure -{ - // We cannot remove cross-signing trust for a user in the matrix spec - NSParameterAssert(verificationStatus); - -#ifdef MX_CRYPTO - dispatch_async(_cryptoQueue, ^{ - [self setUserVerification2:verificationStatus forUser:userId downloadIfNeeded:YES success:success failure:failure]; - }); -#else - if (success) - { - success(); - } -#endif -} - -- (void)setUserVerification2:(BOOL)verificationStatus forUser:(NSString*)userId - downloadIfNeeded:(BOOL)downloadIfNeeded - success:(void (^)(void))success - failure:(void (^)(NSError *error))failure -{ -#ifdef MX_CRYPTO - MXCrossSigningInfo *crossSigningInfo = [self.store crossSigningKeysForUser:userId]; - - // Sanity check - if (!crossSigningInfo) - { - if (downloadIfNeeded) - { - MXLogDebug(@"[MXCrypto] setUserVerification: Unknown user. Try to download user's keys for %@", userId); - [self.deviceList downloadKeys:@[userId] forceDownload:YES success:^(MXUsersDevicesMap *usersDevicesInfoMap, NSDictionary *crossSigningKeysMap) { - [self setUserVerification2:verificationStatus forUser:userId downloadIfNeeded:NO success:success failure:failure]; - } failure:^(NSError *error) { - if (failure) - { - dispatch_async(dispatch_get_main_queue(), ^{ - failure(error); - }); - } - }]; - } - else - { - MXLogDebug(@"[MXCrypto] setUserVerification: Unknown user %@", userId); - if (failure) - { - dispatch_async(dispatch_get_main_queue(), ^{ - failure(nil); - }); - } - } - return; - } - - // Store information locally - if (verificationStatus != crossSigningInfo.trustLevel.isLocallyVerified) - { - MXUserTrustLevel *newTrustLevel = [MXUserTrustLevel trustLevelWithCrossSigningVerified:crossSigningInfo.trustLevel.isCrossSigningVerified - locallyVerified:verificationStatus];; - [crossSigningInfo updateTrustLevel:newTrustLevel]; - [_store storeCrossSigningKeys:crossSigningInfo]; - } - - // Cross-sign if possible - if (verificationStatus != crossSigningInfo.trustLevel.isCrossSigningVerified) - { - if (self.crossSigning.canCrossSign) - { - MXLogDebug(@"[MXCrypto] setUserVerification: Sign user %@ as verified", userId); - [self.crossSigning signUserWithUserId:userId success:success failure:failure]; - - // Wait the end of cross-sign before returning - return; - } - else - { - // Cross-signing ability should have been checked before going into this hole - MXLogDebug(@"[MXCrypto] setUserVerification: Cross-signing not enabled. Current state: %@", @(self.crossSigning.state)); - - } - } - - if (success) - { - dispatch_async(dispatch_get_main_queue(), ^{ - success(); - }); - } -#endif -} - - -#pragma mark - Cross-signing trust - -- (MXUserTrustLevel*)trustLevelForUser:(NSString*)userId -{ - return [self.store crossSigningKeysForUser:userId].trustLevel ?: [MXUserTrustLevel new]; -} - -- (MXDeviceTrustLevel*)deviceTrustLevelForDevice:(NSString*)deviceId ofUser:(NSString*)userId; -{ - return [self.store deviceWithDeviceId:deviceId forUser:userId].trustLevel; -} - -- (void)trustLevelSummaryForUserIds:(NSArray*)userIds - forceDownload:(BOOL)forceDownload - success:(void (^)(MXUsersTrustLevelSummary *usersTrustLevelSummary))success - failure:(void (^)(NSError *error))failure -{ - [self downloadKeys:userIds forceDownload:forceDownload success:^(MXUsersDevicesMap *usersDevicesInfoMap, NSDictionary *crossSigningKeysMap) { - - // Read data from the store - // It has been updated in the process of the downloadKeys response - [self trustLevelSummaryForUserIds:userIds onComplete:^(MXUsersTrustLevelSummary *trustLevelSummary) { - success(trustLevelSummary); - }]; - - } failure:failure]; -} - -- (void)trustLevelSummaryForUserIds:(NSArray*)userIds onComplete:(void (^)(MXUsersTrustLevelSummary *trustLevelSummary))onComplete; -{ - // Use cargoQueue for potential huge read requests from the store - MXWeakify(self); - dispatch_async(cargoQueue, ^{ - MXStrongifyAndReturnIfNil(self); - - NSUInteger usersCount = 0; - NSUInteger trustedUsersCount = 0; - NSUInteger devicesCount = 0; - NSUInteger trustedDevicesCount = 0; - - for (NSString *userId in userIds) - { - usersCount++; - - MXUserTrustLevel *userTrustLevel = [self trustLevelForUser:userId]; - if (userTrustLevel.isVerified) - { - trustedUsersCount++; - - for (MXDeviceInfo *device in [self.store devicesForUser:userId].allValues) - { - devicesCount++; - if (device.trustLevel.isVerified) - { - trustedDevicesCount++; - } - } - } - } - - NSProgress *trustedUsersProgress = [NSProgress progressWithTotalUnitCount:usersCount]; - trustedUsersProgress.completedUnitCount = trustedUsersCount; - - NSProgress *trustedDevicesProgress = [NSProgress progressWithTotalUnitCount:devicesCount]; - trustedDevicesProgress.completedUnitCount = trustedDevicesCount; - - MXUsersTrustLevelSummary *trustLevelSummary = [[MXUsersTrustLevelSummary alloc] initWithTrustedUsersProgress:trustedUsersProgress - andTrustedDevicesProgress:trustedDevicesProgress]; - - dispatch_async(dispatch_get_main_queue(), ^{ - onComplete(trustLevelSummary); - }); - }); -} - -#pragma mark - Users keys - -- (MXHTTPOperation*)downloadKeys:(NSArray*)userIds - forceDownload:(BOOL)forceDownload - success:(void (^)(MXUsersDevicesMap *usersDevicesInfoMap, - NSDictionary *crossSigningKeysMap))success - failure:(void (^)(NSError *error))failure -{ -#ifdef MX_CRYPTO - - // Create an empty operation that will be mutated later - MXHTTPOperation *operation = [[MXHTTPOperation alloc] init]; - - dispatch_async(_cryptoQueue, ^{ - - MXHTTPOperation *operation2 = [self.deviceList downloadKeys:userIds forceDownload:forceDownload success:^(MXUsersDevicesMap *usersDevicesInfoMap, NSDictionary *crossSigningKeysMap) { - if (success) - { - dispatch_async(dispatch_get_main_queue(), ^{ - success(usersDevicesInfoMap, crossSigningKeysMap); - }); - } - } failure:^(NSError *error) { - if (failure) - { - dispatch_async(dispatch_get_main_queue(), ^{ - failure(error); - }); - } - }]; - - [operation mutateTo:operation2]; - }); - - return operation; -#else - if (success) - { - success(nil); - } - return nil; -#endif -} - -- (NSDictionary*)devicesForUser:(NSString*)userId -{ - NSDictionary *devices; - -#ifdef MX_CRYPTO - devices = [self.store devicesForUser:userId]; -#endif - - return devices; -} - -- (MXDeviceInfo *)deviceWithDeviceId:(NSString*)deviceId ofUser:(NSString*)userId -{ - MXDeviceInfo *device; - -#ifdef MX_CRYPTO - device = [self.store devicesForUser:userId][deviceId]; -#endif - - return device; -} - -- (void)resetReplayAttackCheckInTimeline:(NSString*)timeline -{ -#ifdef MX_CRYPTO - MXWeakify(self); - dispatch_async(decryptionQueue, ^{ - MXStrongifyAndReturnIfNil(self); - [self.olmDevice resetReplayAttackCheckInTimeline:timeline]; - }); -#endif -} - -- (void)resetDeviceKeys -{ -#ifdef MX_CRYPTO - MXWeakify(self); - dispatch_sync(decryptionQueue, ^{ - MXStrongifyAndReturnIfNil(self); - - // Reset tracking status - [self.store storeDeviceTrackingStatus:nil]; - - // Reset the sync token - // [self handleDeviceListsChanges] will download all keys at the coming initial /sync - [self.store storeDeviceSyncToken:nil]; - }); -#endif -} - -- (NSString *)version -{ - return [NSString stringWithFormat:@"OLM %@", self.olmVersion]; -} - -- (NSString *)deviceCurve25519Key -{ -#ifdef MX_CRYPTO - return _olmDevice.deviceCurve25519Key; -#else - return nil; -#endif -} - -- (NSString *)deviceEd25519Key -{ -#ifdef MX_CRYPTO - return _olmDevice.deviceEd25519Key; -#else - return nil; -#endif -} - -- (NSString *)olmVersion -{ -#ifdef MX_CRYPTO - return _olmDevice.olmVersion; -#else - return nil; -#endif -} - -- (uint64_t)deviceCreationTs -{ - // Device creation timestamp is not support in legacy crypto - return 0; -} - - -#pragma mark - Gossipping - -- (void)requestAllPrivateKeys -{ - MXLogDebug(@"[MXCrypto] requestAllPrivateKeys"); - - // Request backup private keys - if (!self.backup.hasPrivateKeyInCryptoStore || !self.backup.enabled) - { - MXLogDebug(@"[MXCrypto] requestAllPrivateKeys: Request key backup private keys"); - - MXWeakify(self); - [self.backup requestPrivateKeys:^{ - MXStrongifyAndReturnIfNil(self); - - if (self.enableOutgoingKeyRequestsOnceSelfVerificationDone) - { - [self->outgoingRoomKeyRequestManager setEnabled:YES]; - } - }]; - } - - // Check cross-signing private keys - if (!self.crossSigning.canCrossSign) - { - MXLogDebug(@"[MXCrypto] requestAllPrivateKeys: Request cross-signing private keys"); - [(MXLegacyCrossSigning *)self.crossSigning requestPrivateKeys]; - } -} - -- (void)scheduleRequestsForAllPrivateKeys -{ - // For the moment, we have no better solution than waiting a bit before making such request. - // This 1.5s delay lets time to the other peer to set our device as trusted - // so that it will accept to gossip the keys to our device. - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.5 * NSEC_PER_SEC)), _cryptoQueue, ^{ - [self requestAllPrivateKeys]; - }); -} - - -#pragma mark - import/export - -- (void)exportRoomKeysWithPassword:(NSString *)password success:(void (^)(NSData *))success failure:(void (^)(NSError *))failure -{ -#ifdef MX_CRYPTO - MXWeakify(self); - dispatch_async(cargoQueue, ^{ - MXStrongifyAndReturnIfNil(self); - - NSData *keyFile; - NSError *error; - - NSDate *startDate = [NSDate date]; - - // Export the keys - NSMutableArray *keys = [NSMutableArray array]; - for (MXOlmInboundGroupSession *session in [self.store inboundGroupSessions]) - { - MXMegolmSessionData *sessionData = [session exportSessionData]; - if (sessionData) - { - [keys addObject:sessionData.JSONDictionary]; - } - } - - MXLogDebug(@"[MXCrypto] exportRoomKeysWithPassword: Exportion of %tu keys took %.0fms", keys.count, [[NSDate date] timeIntervalSinceDate:startDate] * 1000); - - // Convert them to JSON - NSData *jsonData = [NSJSONSerialization dataWithJSONObject:keys - options:NSJSONWritingPrettyPrinted - error:&error]; - if (jsonData) - { - // Encrypt them - keyFile = [MXMegolmExportEncryption encryptMegolmKeyFile:jsonData withPassword:password kdfRounds:0 error:&error]; - } - - MXLogDebug(@"[MXCrypto] exportRoomKeysWithPassword: Exported and encrypted %tu keys in %.0fms", keys.count, [[NSDate date] timeIntervalSinceDate:startDate] * 1000); - - dispatch_async(dispatch_get_main_queue(), ^{ - - if (keyFile) - { - if (success) - { - success(keyFile); - } - } - else - { - MXLogDebug(@"[MXCrypto] exportRoomKeysWithPassword: Error: %@", error); - if (failure) - { - failure(error); - } - } - }); - }); -#endif -} - -- (void)importRoomKeys:(NSArray *)keys success:(void (^)(NSUInteger total, NSUInteger imported))success failure:(void (^)(NSError *))failure -{ -#ifdef MX_CRYPTO - dispatch_async(cargoQueue, ^{ - - MXLogDebug(@"[MXCrypto] importRoomKeys:"); - - // Convert JSON to MXMegolmSessionData - NSArray *sessionDatas = [MXMegolmSessionData modelsFromJSON:keys]; - - [self importMegolmSessionDatas:sessionDatas backUp:YES success:success failure:failure]; - }); -#endif -} - -- (void)importMegolmSessionDatas:(NSArray*)sessionDatas backUp:(BOOL)backUp success:(void (^)(NSUInteger total, NSUInteger imported))success failure:(void (^)(NSError *error))failure -{ -#ifdef MX_CRYPTO - MXWeakify(self); - dispatch_async(cargoQueue, ^{ - MXStrongifyAndReturnIfNil(self); - - MXLogDebug(@"[MXCrypto] importMegolmSessionDatas: backUp: %@", @(backUp)); - - NSDate *startDate = [NSDate date]; - - // Import keys - NSArray* sessions = [self.olmDevice importInboundGroupSessions:sessionDatas]; - - MXLogDebug(@"[MXCrypto] importMegolmSessionDatas: Imported %@ keys in store", @(sessions.count)); - - dispatch_async(self.cryptoQueue, ^{ - // Do not back up the key if it comes from a backup recovery - if (backUp) - { - [self.backup maybeSendKeyBackup]; - } - else - { - [self.store markBackupDoneForInboundGroupSessions:sessions]; - } - - // Notify there are new keys - MXLogDebug(@"[MXCrypto] importMegolmSessionDatas: Notifying about new keys..."); - for (MXOlmInboundGroupSession *session in sessions) - { - id alg = [self getRoomDecryptor:session.roomId algorithm:kMXCryptoMegolmAlgorithm]; - [alg didImportRoomKey:session]; - } - - NSUInteger imported = sessions.count; - NSUInteger totalKeyCount = sessionDatas.count; - - MXLogDebug(@"[MXCrypto] importMegolmSessionDatas: Complete. Imported %tu keys from %tu provided keys in %.0fms", imported, totalKeyCount, [[NSDate date] timeIntervalSinceDate:startDate] * 1000); - - dispatch_async(dispatch_get_main_queue(), ^{ - - if (success) - { - success(totalKeyCount, imported); - } - }); - }); - - }); -#endif -} - -- (void)importRoomKeys:(NSData *)keyFile withPassword:(NSString *)password success:(void (^)(NSUInteger total, NSUInteger imported))success failure:(void (^)(NSError *))failure -{ -#ifdef MX_CRYPTO - dispatch_async(cargoQueue, ^{ - - MXLogDebug(@"[MXCrypto] importRoomKeys:withPassword:"); - - NSError *error; - NSDate *startDate = [NSDate date]; - - NSData *jsonData = [MXMegolmExportEncryption decryptMegolmKeyFile:keyFile withPassword:password error:&error]; - if(jsonData) - { - NSArray *keys = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:&error]; - if (keys) - { - [self importRoomKeys:keys success:^(NSUInteger total, NSUInteger imported) { - - MXLogDebug(@"[MXCrypto] importRoomKeys:withPassword: Imported %tu keys from %tu provided keys in %.0fms", imported, total, [[NSDate date] timeIntervalSinceDate:startDate] * 1000); - - if (success) - { - success(total, imported); - } - - } failure:failure]; - return; - } - } - - dispatch_async(dispatch_get_main_queue(), ^{ - - MXLogErrorDetails(@"[MXCrypto] importRoomKeys:withPassword: Error", @{ - @"error": error ?: @"unknown" - }); - - if (failure) - { - failure(error); - } - }); - }); -#endif -} - -#pragma mark - Key sharing - -- (void)pendingKeyRequests:(void (^)(MXUsersDevicesMap *> *pendingKeyRequests))onComplete -{ - NSParameterAssert(onComplete); - -#ifdef MX_CRYPTO - MXWeakify(self); - dispatch_async(_cryptoQueue, ^{ - MXStrongifyAndReturnIfNil(self); - - MXUsersDevicesMap *> *pendingKeyRequests = self->incomingRoomKeyRequestManager.pendingKeyRequests; - - dispatch_async(dispatch_get_main_queue(), ^{ - onComplete(pendingKeyRequests); - }); - }); -#endif -} - -- (void)acceptKeyRequest:(MXIncomingRoomKeyRequest *)keyRequest - success:(void (^)(void))success - failure:(void (^)(NSError *error))failure -{ -#ifdef MX_CRYPTO - dispatch_async(_cryptoQueue, ^{ - - MXLogDebug(@"[MXCrypto] acceptKeyRequest: %@", keyRequest); - [self acceptKeyRequestFromCryptoThread:keyRequest success:success failure:failure]; - }); -#endif -} - -- (void)acceptAllPendingKeyRequestsFromUser:(NSString *)userId andDevice:(NSString *)deviceId onComplete:(void (^)(void))onComplete -{ -#ifdef MX_CRYPTO - MXWeakify(self); - dispatch_async(_cryptoQueue, ^{ - MXStrongifyAndReturnIfNil(self); - - NSArray *requests = [self->incomingRoomKeyRequestManager.pendingKeyRequests objectForDevice:deviceId forUser:userId]; - - MXLogDebug(@"[MXCrypto] acceptAllPendingKeyRequestsFromUser from %@:%@. %@ pending requests", userId, deviceId, @(requests.count)); - - for (MXIncomingRoomKeyRequest *request in requests) - { - // TODO: Add success and failure blocks to acceptAllPendingKeyRequestsFromUser - [self acceptKeyRequestFromCryptoThread:request success:nil failure:nil]; - } - - dispatch_async(dispatch_get_main_queue(), ^{ - if (onComplete) - { - onComplete(); - } - }); - }); -#endif -} - -#ifdef MX_CRYPTO -- (void)acceptKeyRequestFromCryptoThread:(MXIncomingRoomKeyRequest *)keyRequest - success:(void (^)(void))success - failure:(void (^)(NSError *error))failure -{ - NSString *userId = keyRequest.userId; - NSString *deviceId = keyRequest.deviceId; - NSString *requestId = keyRequest.requestId; - - NSDictionary *body = keyRequest.requestBody; - NSString *roomId, *alg; - - MXJSONModelSetString(roomId, body[@"room_id"]); - MXJSONModelSetString(alg, body[@"algorithm"]); - - // The request is no more pending - [incomingRoomKeyRequestManager removePendingKeyRequest:requestId fromUser:userId andDevice:deviceId]; - - id decryptor = [self getRoomDecryptor:roomId algorithm:alg]; - if (decryptor) - { - [decryptor shareKeysWithDevice:keyRequest success:success failure:failure]; - } - else - { - dispatch_async(dispatch_get_main_queue(), ^{ - - NSDictionary *details = @{ - @"algorithm": alg ?: @"unknown", - @"room_id": roomId ?: @"unknown", - }; - MXLogErrorDetails(@"[MXCrypto] acceptPendingKeyRequests: ERROR: unknown alg in room", details); - if (failure) - { - failure(nil); - } - }); - } -} -#endif - -- (void)ignoreKeyRequest:(MXIncomingRoomKeyRequest *)keyRequest onComplete:(void (^)(void))onComplete -{ -#ifdef MX_CRYPTO - dispatch_async(_cryptoQueue, ^{ - - MXLogDebug(@"[MXCrypto] ignoreKeyRequest: %@", keyRequest); - [self ignoreKeyRequestFromCryptoThread:keyRequest]; - - dispatch_async(dispatch_get_main_queue(), ^{ - if (onComplete) - { - onComplete(); - } - }); - }); -#endif -} - -- (void)ignoreAllPendingKeyRequestsFromUser:(NSString *)userId andDevice:(NSString *)deviceId onComplete:(void (^)(void))onComplete -{ -#ifdef MX_CRYPTO - MXWeakify(self); - dispatch_async(_cryptoQueue, ^{ - MXStrongifyAndReturnIfNil(self); - - NSArray *requests = [self->incomingRoomKeyRequestManager.pendingKeyRequests objectForDevice:deviceId forUser:userId]; - - MXLogDebug(@"[MXCrypto] ignoreAllPendingKeyRequestsFromUser from %@:%@. %@ pending requests", userId, deviceId, @(requests.count)); - - for (MXIncomingRoomKeyRequest *request in requests) - { - [self ignoreKeyRequestFromCryptoThread:request]; - } - - dispatch_async(dispatch_get_main_queue(), ^{ - if (onComplete) - { - onComplete(); - } - }); - }); -#endif -} - -#ifdef MX_CRYPTO -- (void)ignoreKeyRequestFromCryptoThread:(MXIncomingRoomKeyRequest *)keyRequest -{ - NSString *userId = keyRequest.userId; - NSString *deviceId = keyRequest.deviceId; - NSString *requestId = keyRequest.requestId; - - // Make request no more pending - [incomingRoomKeyRequestManager removePendingKeyRequest:requestId fromUser:userId andDevice:deviceId]; -} -#endif - - -- (void)setOutgoingKeyRequestsEnabled:(BOOL)enabled onComplete:(void (^)(void))onComplete -{ -#ifdef MX_CRYPTO - MXWeakify(self); - dispatch_async(_cryptoQueue, ^{ - MXStrongifyAndReturnIfNil(self); - - [self->outgoingRoomKeyRequestManager setEnabled:enabled]; - - if (onComplete) - { - dispatch_async(dispatch_get_main_queue(), ^{ - onComplete(); - }); - } - }); -#endif -} - -- (BOOL)isOutgoingKeyRequestsEnabled -{ - return outgoingRoomKeyRequestManager.isEnabled; -} - -- (void)reRequestRoomKeyForEvent:(MXEvent *)event -{ -#ifdef MX_CRYPTO - MXWeakify(self); - dispatch_async(_cryptoQueue, ^{ - MXStrongifyAndReturnIfNil(self); - - MXLogDebug(@"[MXCrypto] reRequestRoomKeyForEvent: %@", event.eventId); - - NSDictionary *wireContent = event.wireContent; - NSString *algorithm, *senderKey, *sessionId; - MXJSONModelSetString(algorithm, wireContent[@"algorithm"]); - MXJSONModelSetString(senderKey, wireContent[@"sender_key"]); - MXJSONModelSetString(sessionId, wireContent[@"session_id"]); - - if (algorithm && senderKey && sessionId) - { - [self->outgoingRoomKeyRequestManager resendRoomKeyRequest:@{ - @"room_id": event.roomId, - @"algorithm": algorithm, - @"sender_key": senderKey, - @"session_id": sessionId - }]; - } - }); -#endif -} - - -#pragma mark - Crypto settings -- (BOOL)globalBlacklistUnverifiedDevices -{ -#ifdef MX_CRYPTO - return _store.globalBlacklistUnverifiedDevices; -#else - return NO; -#endif -} - -- (void)setGlobalBlacklistUnverifiedDevices:(BOOL)globalBlacklistUnverifiedDevices -{ -#ifdef MX_CRYPTO - _store.globalBlacklistUnverifiedDevices = globalBlacklistUnverifiedDevices; -#endif -} - -- (BOOL)isBlacklistUnverifiedDevicesInRoom:(NSString *)roomId -{ -#ifdef MX_CRYPTO - return [_store blacklistUnverifiedDevicesInRoom:roomId]; -#else - return NO; -#endif -} - -- (BOOL)isRoomEncrypted:(NSString *)roomId -{ -#ifdef MX_CRYPTO - return [_store algorithmForRoom:roomId] != nil; -#else - return NO; -#endif -} - -- (BOOL)isRoomSharingHistory:(NSString *)roomId -{ - if (!MXSDKOptions.sharedInstance.enableRoomSharedHistoryOnInvite) - { - return NO; - } - - MXRoom *room = [self.mxSession roomWithRoomId:roomId]; - MXRoomHistoryVisibility visibility = room.summary.historyVisibility; - return [visibility isEqualToString:kMXRoomHistoryVisibilityWorldReadable] || [visibility isEqualToString:kMXRoomHistoryVisibilityShared]; -} - -- (void)setBlacklistUnverifiedDevicesInRoom:(NSString *)roomId blacklist:(BOOL)blacklist -{ -#ifdef MX_CRYPTO - [_store storeBlacklistUnverifiedDevicesInRoom:roomId blacklist:blacklist]; -#endif -} - - -#pragma mark - Private API - -#ifdef MX_CRYPTO - -- (instancetype)initWithMatrixSession:(MXSession*)matrixSession cryptoQueue:(dispatch_queue_t)theCryptoQueue andStore:(id)store -{ - // This method must be called on the crypto thread - self = [super init]; - if (self) - { - _mxSession = matrixSession; - _cryptoQueue = theCryptoQueue; - _store = store; - - // Default configuration - _warnOnUnknowDevices = YES; - _enableOutgoingKeyRequestsOnceSelfVerificationDone = YES; - - decryptionQueue = [MXLegacyCrypto dispatchQueueForUser:_mxSession.matrixRestClient.credentials.userId]; - - cargoQueue = dispatch_queue_create([NSString stringWithFormat:@"MXCrypto-Cargo-%@", _mxSession.myDeviceId].UTF8String, DISPATCH_QUEUE_SERIAL); - - ensureOlmSessionsInProgress = [NSMutableArray array]; - - _olmDevice = [[MXOlmDevice alloc] initWithStore:_store]; - - _deviceList = [[MXDeviceList alloc] initWithCrypto:self]; - - // Use our own REST client that answers on the crypto thread - _matrixRestClient = [[MXRestClient alloc] initWithCredentials:_mxSession.matrixRestClient.credentials andOnUnrecognizedCertificateBlock:nil andPersistentTokenDataHandler:_mxSession.matrixRestClient.persistTokenDataHandler andUnauthenticatedHandler:_mxSession.matrixRestClient.unauthenticatedHandler]; - _matrixRestClient.completionQueue = _cryptoQueue; - - roomEncryptors = [NSMutableDictionary dictionary]; - roomDecryptors = [NSMutableDictionary dictionary]; - - // Build our device keys: they will later be uploaded - NSString *deviceId = _store.deviceId; - if (!deviceId) - { - // Generate a device id if the homeserver did not provide it or it was lost - deviceId = [self generateDeviceId]; - - MXLogDebug(@"[MXCrypto] Warning: No device id in MXCredentials. The id %@ was created", deviceId); - - [_store storeDeviceId:deviceId]; - } - - NSString *userId = _matrixRestClient.credentials.userId; - - _myDevice = [_store deviceWithDeviceId:deviceId forUser:userId]; - if (!_myDevice) - { - _myDevice = [[MXDeviceInfo alloc] initWithDeviceId:deviceId]; - _myDevice.userId = userId; - _myDevice.keys = @{ - [NSString stringWithFormat:@"%@:%@", kMXKeyEd25519Type, deviceId]: _olmDevice.deviceEd25519Key, - [NSString stringWithFormat:@"%@:%@", kMXKeyCurve25519Type, deviceId]: _olmDevice.deviceCurve25519Key, - }; - _myDevice.algorithms = [[MXCryptoAlgorithms sharedAlgorithms] supportedAlgorithms]; - [_myDevice updateTrustLevel:[MXDeviceTrustLevel trustLevelWithLocalVerificationStatus:MXDeviceVerified - crossSigningVerified:NO]]; - - // Add our own deviceinfo to the store - [_store storeDeviceForUser:userId device:_myDevice]; - } - - oneTimeKeyCount = -1; - - outgoingRoomKeyRequestManager = [[MXOutgoingRoomKeyRequestManager alloc] - initWithMatrixRestClient:_matrixRestClient - deviceId:_myDevice.deviceId - cryptoQueue:[MXLegacyCrypto dispatchQueueForUser:_myDevice.userId] - cryptoStore:_store]; - - incomingRoomKeyRequestManager = [[MXIncomingRoomKeyRequestManager alloc] initWithCrypto:self]; - - unrequestedForwardedRoomKeyManager = [[MXUnrequestedForwardedRoomKeyManager alloc] init]; - unrequestedForwardedRoomKeyManager.delegate = self; - - _keyVerificationManager = [[MXLegacyKeyVerificationManager alloc] initWithCrypto:self]; - - _secretStorage = [[MXSecretStorage alloc] initWithMatrixSession:_mxSession processingQueue:_cryptoQueue]; - _secretShareManager = [[MXSecretShareManager alloc] initWithCrypto:self]; - - _crossSigning = [[MXLegacyCrossSigning alloc] initWithCrypto:self]; - - if ([MXSDKOptions sharedInstance].enableKeyBackupWhenStartingMXCrypto) - { - id engine = [[MXNativeKeyBackupEngine alloc] initWithCrypto:self]; - _backup = [[MXKeyBackup alloc] initWithEngine:engine - restClient:_matrixRestClient - secretShareManager:_secretShareManager - queue:_cryptoQueue]; - } - - MXRecoveryServiceDependencies *dependencies = [[MXRecoveryServiceDependencies alloc] initWithCredentials:_mxSession.matrixRestClient.credentials - backup:_backup - secretStorage:_secretStorage - secretStore:_store - crossSigning:_crossSigning - cryptoQueue:_cryptoQueue]; - _recoveryService = [[MXRecoveryService alloc] initWithDependencies:dependencies delegate:self]; - - cryptoMigration = [[MXCryptoMigration alloc] initWithCrypto:self]; - - lastNewSessionForcedDates = [MXUsersDevicesMap new]; - - [self registerEventHandlers]; - - } - return self; -} - -- (MXDeviceInfo *)eventSenderDeviceOfEvent:(MXEvent *)event -{ - NSString *senderKey = event.senderKey; - NSString *algorithm = event.wireContent[@"algorithm"]; - - if (!senderKey || !algorithm) - { - return nil; - } - - NSArray *forwardingChain = event.forwardingCurve25519KeyChain; - if (forwardingChain.count > 0) - { - // we got this event from somewhere else - // TODO: check if we can trust the forwarders. - return nil; - } - - // senderKey is the Curve25519 identity key of the device which the event - // was sent from. In the case of Megolm, it's actually the Curve25519 - // identity key of the device which set up the Megolm session. - MXDeviceInfo *device = [_deviceList deviceWithIdentityKey:senderKey andAlgorithm:algorithm]; - if (!device) - { - // we haven't downloaded the details of this device yet. - return nil; - } - - // So far so good, but now we need to check that the sender of this event - // hadn't advertised someone else's Curve25519 key as their own. We do that - // by checking the Ed25519 claimed by the event (or, in the case of megolm, - // the event which set up the megolm session), to check that it matches the - // fingerprint of the purported sending device. - // - // (see https://github.com/vector-im/vector-web/issues/2215) - NSString *claimedKey = event.keysClaimed[kMXKeyEd25519Type]; - if (!claimedKey) - { - MXLogDebug(@"[MXCrypto] eventSenderDeviceOfEvent: Event %@ claims no ed25519 key. Cannot verify sending device", event.eventId); - return nil; - } - - if (![claimedKey isEqualToString:device.fingerprint]) - { - MXLogDebug(@"[MXCrypto] eventSenderDeviceOfEvent: Event %@ claims ed25519 key %@. Cannot verify sending device but sender device has key %@", event.eventId, claimedKey, device.fingerprint); - return nil; - } - - return device; -} - -- (BOOL)setEncryptionInRoom:(NSString*)roomId withMembers:(NSArray*)members algorithm:(NSString*)algorithm inhibitDeviceQuery:(BOOL)inhibitDeviceQuery -{ - NSString *existingAlgorithm = [_store algorithmForRoom:roomId]; - if (existingAlgorithm && ![existingAlgorithm isEqualToString:algorithm]) - { - MXLogWarning(@"[MXCrypto] setEncryptionInRoom: New m.room.encryption event in %@ with an algorithm change from %@ to %@", roomId, existingAlgorithm, algorithm); - - // Reset the current encryption in this room. - // If the new algo is supported, it will be used - // Else, encryption and sending will be no more possible in this room - [roomEncryptors removeObjectForKey:roomId]; - } - - Class encryptionClass = [[MXCryptoAlgorithms sharedAlgorithms] encryptorClassForAlgorithm:algorithm]; - if (!encryptionClass) - { - NSString *message = [NSString stringWithFormat:@"[MXCrypto] setEncryptionInRoom: Unable to encrypt with %@", algorithm]; - MXLogError(message); - return NO; - } - - if (!existingAlgorithm) - { - [_store storeAlgorithmForRoom:roomId algorithm:algorithm]; - } - - id alg = [[encryptionClass alloc] initWithCrypto:self andRoom:roomId]; - - roomEncryptors[roomId] = alg; - - // make sure we are tracking the device lists for all users in this room. - MXLogDebug(@"[MXCrypto] setEncryptionInRoom: Enabling encryption in %@; starting to track device lists for all users therein", roomId); - - for (NSString *userId in members) - { - [_deviceList startTrackingDeviceList:userId]; - } - - if (!inhibitDeviceQuery) - { - [_deviceList refreshOutdatedDeviceLists]; - } - - return YES; -} - -- (MXHTTPOperation*)ensureOlmSessionsForUsers:(NSArray*)users - success:(void (^)(MXUsersDevicesMap *results))success - failure:(void (^)(NSError *error))failure -{ - MXLogDebug(@"[MXCrypto] ensureOlmSessionsForUsers: %@", users); - - NSMutableDictionary*> *devicesByUser = [NSMutableDictionary dictionary]; - - for (NSString *userId in users) - { - devicesByUser[userId] = [NSMutableArray array]; - - NSArray *devices = [self.deviceList storedDevicesForUser:userId]; - for (MXDeviceInfo *device in devices) - { - NSString *key = device.identityKey; - - if ([key isEqualToString:_olmDevice.deviceCurve25519Key]) - { - // Don't bother setting up session to ourself - continue; - } - - if (device.trustLevel.localVerificationStatus == MXDeviceBlocked) { - // Don't bother setting up sessions with blocked users - continue; - } - - [devicesByUser[userId] addObject:device]; - } - } - - return [self ensureOlmSessionsForDevices:devicesByUser force:NO success:success failure:failure]; -} - -- (MXHTTPOperation*)ensureOlmSessionsForDevices:(NSDictionary*>*)devicesByUser - force:(BOOL)force - success:(void (^)(MXUsersDevicesMap *results))success - failure:(void (^)(NSError *error))failure - -{ - NSMutableArray *devicesWithoutSession = [NSMutableArray array]; - - MXUsersDevicesMap *results = [[MXUsersDevicesMap alloc] init]; - - NSUInteger count = 0; - for (NSString *userId in devicesByUser) - { - count += devicesByUser[userId].count; - - for (MXDeviceInfo *deviceInfo in devicesByUser[userId]) - { - NSString *deviceId = deviceInfo.deviceId; - NSString *key = deviceInfo.identityKey; - - NSString *sessionId = [_olmDevice sessionIdForDevice:key]; - if (!sessionId || force) - { - [devicesWithoutSession addObject:deviceInfo]; - } - - MXOlmSessionResult *olmSessionResult = [[MXOlmSessionResult alloc] initWithDevice:deviceInfo andOlmSession:sessionId]; - [results setObject:olmSessionResult forUser:userId andDevice:deviceId]; - } - } - - MXLogDebug(@"[MXCrypto] ensureOlmSessionsForDevices (users: %tu - devices: %tu - force: %@): %@", devicesByUser.count, count, @(force), devicesByUser); - - if (devicesWithoutSession.count == 0) - { - MXLogDebug(@"[MXCrypto] ensureOlmSessionsForDevices: Have already sessions for all"); - if (success) - { - success(results); - } - return nil; - } - - - NSString *oneTimeKeyAlgorithm = kMXKeySignedCurve25519Type; - - // Devices for which we will make a /claim request - MXUsersDevicesMap *usersDevicesToClaim = [[MXUsersDevicesMap alloc] init]; - // The same but devices are listed by their identity key - NSMutableArray *devicesToClaim = [NSMutableArray array]; - - // Devices (by their identity key) that are waiting for a response to /claim request - // That can be devices for which we are going to make a /claim request OR devices that - // already have a pending requests. - // Once we have emptied this array, we can call the success or the failure block. The - // operation is complete. - NSMutableArray *devicesInProgress = [NSMutableArray array]; - - // Prepare the request for claiming one-time keys - for (MXDeviceInfo *device in devicesWithoutSession) - { - NSString *deviceIdentityKey = device.identityKey; - - // Claim only if a request is not yet pending - if (![ensureOlmSessionsInProgress containsObject:deviceIdentityKey]) - { - [usersDevicesToClaim setObject:oneTimeKeyAlgorithm forUser:device.userId andDevice:device.deviceId]; - [devicesToClaim addObject:deviceIdentityKey]; - - [ensureOlmSessionsInProgress addObject:deviceIdentityKey]; - } - - // In both case, we need to wait for the creation of the olm session for this device - [devicesInProgress addObject:deviceIdentityKey]; - } - - MXLogDebug(@"[MXCrypto] ensureOlmSessionsForDevices: %@ out of %@ sessions to claim one time keys", @(usersDevicesToClaim.count), @(devicesWithoutSession.count)); - - - // Wait for the result of claim request(s) - // Listen to the dedicated notification - MXWeakify(self); - __block id observer; - observer = [[NSNotificationCenter defaultCenter] addObserverForName:kMXCryptoOneTimeKeyClaimCompleteNotification object:self queue:nil usingBlock:^(NSNotification * _Nonnull note) { - MXStrongifyAndReturnIfNil(self); - - NSArray *devices = note.userInfo[kMXCryptoOneTimeKeyClaimCompleteNotificationDevicesKey]; - NSError *error = note.userInfo[kMXCryptoOneTimeKeyClaimCompleteNotificationErrorKey]; - - // Was it a /claim request for us? - if ([devicesInProgress mx_intersectArray:devices]) - { - if (error) - { - MXLogDebug(@"[MXCrypto] ensureOlmSessionsForDevices: Got a notification failure for %@ devices. Fail our current pool of %@ devices", @(devices.count), @(devicesInProgress.count)); - - // Consider the failure for all requests of the current pool - [self->ensureOlmSessionsInProgress removeObjectsInArray:devices]; - [devicesInProgress removeAllObjects]; - - // The game is over for this pool - [[NSNotificationCenter defaultCenter] removeObserver:observer]; - if (failure) - { - failure(error); - } - } - else - { - for (NSString *deviceIdentityKey in devices) - { - if ([devicesInProgress containsObject:deviceIdentityKey]) - { - MXDeviceInfo *device = [self.store deviceWithIdentityKey:deviceIdentityKey]; - NSString *olmSessionId = [self.olmDevice sessionIdForDevice:deviceIdentityKey]; - - // Update the result - MXOlmSessionResult *olmSessionResult = [results objectForDevice:device.deviceId forUser:device.userId]; - olmSessionResult.sessionId = olmSessionId; - - // This device is no more in progress - [devicesInProgress removeObject:deviceIdentityKey]; - [self->ensureOlmSessionsInProgress removeObject:deviceIdentityKey]; - } - } - - MXLogDebug(@"[MXCrypto] ensureOlmSessionsForDevices: Got olm sessions for %@ devices. Still missing %@ sessions", @(devices.count), @(devicesInProgress.count)); - - // If the pool is empty, we are done - if (!devicesInProgress.count) - { - [[NSNotificationCenter defaultCenter] removeObserver:observer]; - if (success) - { - success(results); - } - } - } - } - }]; - - - if (usersDevicesToClaim.count == 0) - { - MXLogDebug(@"[MXCrypto] ensureOlmSessionsForDevices: All missing sessions are already pending"); - return nil; - } - - - MXLogDebug(@"[MXCrypto] ensureOlmSessionsForDevices: claimOneTimeKeysForUsersDevices (users: %tu - devices: %tu)", - usersDevicesToClaim.map.count, usersDevicesToClaim.count); - - return [_matrixRestClient claimOneTimeKeysForUsersDevices:usersDevicesToClaim success:^(MXKeysClaimResponse *keysClaimResponse) { - - MXLogDebug(@"[MXCrypto] ensureOlmSessionsForDevices: claimOneTimeKeysForUsersDevices response (users: %tu - devices: %tu): %@", - keysClaimResponse.oneTimeKeys.map.count, keysClaimResponse.oneTimeKeys.count, keysClaimResponse.oneTimeKeys); - - for (NSString *userId in devicesByUser) - { - for (MXDeviceInfo *deviceInfo in devicesByUser[userId]) - { - MXKey *oneTimeKey; - for (NSString *deviceId in [keysClaimResponse.oneTimeKeys deviceIdsForUser:userId]) - { - MXOlmSessionResult *olmSessionResult = [results objectForDevice:deviceId forUser:userId]; - if (olmSessionResult.sessionId && !force) - { - // We already have a result for this device - continue; - } - - MXKey *key = [keysClaimResponse.oneTimeKeys objectForDevice:deviceId forUser:userId]; - if ([key.type isEqualToString:oneTimeKeyAlgorithm]) - { - oneTimeKey = key; - } - - if (!oneTimeKey) - { - MXLogDebug(@"[MXCrypto] ensureOlmSessionsForDevices: No one-time keys (alg=%@) for device %@:%@", oneTimeKeyAlgorithm, userId, deviceId); - continue; - } - - [self verifyKeyAndStartSession:oneTimeKey userId:userId deviceInfo:deviceInfo]; - } - } - } - - // Broadcast the /claim request is done - [[NSNotificationCenter defaultCenter] postNotificationName:kMXCryptoOneTimeKeyClaimCompleteNotification - object:self - userInfo: @{ - kMXCryptoOneTimeKeyClaimCompleteNotificationDevicesKey: devicesToClaim - }]; - - } failure:^(NSError *error) { - - MXLogError(@"[MXCrypto] ensureOlmSessionsForDevices: claimOneTimeKeysForUsersDevices request failed."); - - // Broadcast the /claim request is done - [[NSNotificationCenter defaultCenter] postNotificationName:kMXCryptoOneTimeKeyClaimCompleteNotification - object:self - userInfo: @{ - kMXCryptoOneTimeKeyClaimCompleteNotificationDevicesKey: devicesToClaim, - kMXCryptoOneTimeKeyClaimCompleteNotificationErrorKey: error - }]; - }]; -} - -- (NSString*)verifyKeyAndStartSession:(MXKey*)oneTimeKey userId:(NSString*)userId deviceInfo:(MXDeviceInfo*)deviceInfo -{ - NSString *sessionId; - - NSString *deviceId = deviceInfo.deviceId; - NSString *signKeyId = [NSString stringWithFormat:@"%@:%@", kMXKeyEd25519Type, deviceId]; - NSString *signature = [oneTimeKey.signatures objectForDevice:signKeyId forUser:userId]; - - // Check one-time key signature - NSError *error; - if ([_olmDevice verifySignature:deviceInfo.fingerprint JSON:oneTimeKey.signalableJSONDictionary signature:signature error:&error]) - { - // Update the result for this device in results - sessionId = [_olmDevice createOutboundSession:deviceInfo.identityKey theirOneTimeKey:oneTimeKey.value]; - - if (sessionId) - { - MXLogDebug(@"[MXCrypto] verifyKeyAndStartSession: Started new olm session id %@ for device %@ (theirOneTimeKey: %@)", sessionId, deviceInfo, oneTimeKey.value); - } - else - { - // Possibly a bad key - MXLogErrorDetails(@"[MXCrypto] verifyKeyAndStartSession: Error starting olm session with device", @{ - @"device_id": deviceId ?: @"unknown" - }); - } - } - else - { - NSDictionary *details = @{ - @"device_id": deviceId ?: @"unknown", - @"error": error ?: @"unknown" - }; - MXLogErrorDetails(@"[MXCrypto] verifyKeyAndStartSession: Unable to verify signature on one-time key for device", details); - } - - return sessionId; -} - -- (NSDictionary*)encryptMessage:(NSDictionary*)payloadFields forDevices:(NSArray*)devices -{ - NSMutableDictionary *ciphertext = [NSMutableDictionary dictionary]; - for (MXDeviceInfo *recipientDevice in devices) - { - NSString *sessionId = [_olmDevice sessionIdForDevice:recipientDevice.identityKey]; - if (sessionId) - { - NSMutableDictionary *payloadJson = [NSMutableDictionary dictionaryWithDictionary:payloadFields]; - payloadJson[@"sender"] = _matrixRestClient.credentials.userId; - payloadJson[@"sender_device"] = _store.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. - payloadJson[@"keys"] = @{ - kMXKeyEd25519Type: _olmDevice.deviceEd25519Key - }; - - // Include the recipient device details in the payload, - // to avoid unknown key attacks, per - // https://github.com/vector-im/vector-web/issues/2483 - payloadJson[@"recipient"] = recipientDevice.userId; - payloadJson[@"recipient_keys"] = @{ - kMXKeyEd25519Type: recipientDevice.fingerprint - }; - - NSData *payloadData = [NSJSONSerialization dataWithJSONObject:payloadJson options:0 error:nil]; - NSString *payloadString = [[NSString alloc] initWithData:payloadData encoding:NSUTF8StringEncoding]; - - //MXLogDebug(@"[MXCrypto] encryptMessage: %@\nUsing sessionid %@ for device %@", payloadJson, sessionId, recipientDevice.identityKey); - ciphertext[recipientDevice.identityKey] = [_olmDevice encryptMessage:recipientDevice.identityKey sessionId:sessionId payloadString:payloadString]; - } - } - - return @{ - @"algorithm": kMXCryptoOlmAlgorithm, - @"sender_key": _olmDevice.deviceCurve25519Key, - @"ciphertext": ciphertext - }; -} - -- (id)getRoomDecryptor:(NSString*)roomId algorithm:(NSString*)algorithm -{ - id alg; - - if (roomId) - { - if (!roomDecryptors[roomId]) - { - roomDecryptors[roomId] = [NSMutableDictionary dictionary]; - } - - alg = roomDecryptors[roomId][algorithm]; - if (alg) - { - return alg; - } - } - - Class algClass = [[MXCryptoAlgorithms sharedAlgorithms] decryptorClassForAlgorithm:algorithm]; - if (algClass) - { - alg = [[algClass alloc] initWithCrypto:self]; - - if (roomId) - { - roomDecryptors[roomId][algorithm] = alg; - } - } - - return alg; -} - -- (id)getRoomEncryptor:(NSString*)roomId algorithm:(NSString*)algorithm -{ - if (![algorithm isEqualToString:kMXCryptoMegolmAlgorithm]) - { - MXLogErrorDetails(@"[MXCrypto] getRoomEncryptor: algorithm is not supported", @{ - @"algorithm": algorithm ?: @"unknown" - }); - return nil; - } - - id alg = roomEncryptors[roomId]; - if (alg) - { - return alg; - } - - NSString *existingAlgorithm = [self.store algorithmForRoom:roomId]; - if ([algorithm isEqualToString:existingAlgorithm]) - { - MXLogErrorDetails(@"[MXCrypto] getRoomEncryptor: algorithm does not match the room", @{ - @"algorithm": algorithm ?: @"unknown" - }); - return nil; - } - - Class algClass = [[MXCryptoAlgorithms sharedAlgorithms] encryptorClassForAlgorithm:algorithm]; - if (!algClass) - { - MXLogErrorDetails(@"[MXCrypto] getRoomEncryptor: cannot get encryptor for algorithm", @{ - @"algorithm": algorithm ?: @"unknown" - }); - return nil; - } - - alg = [[algClass alloc] initWithCrypto:self andRoom:roomId]; - roomEncryptors[roomId] = alg; - return alg; -} - -- (NSDictionary*)signObject:(NSDictionary*)object -{ - return @{ - _myDevice.userId: @{ - [NSString stringWithFormat:@"%@:%@", kMXKeyEd25519Type, _myDevice.deviceId]: [_olmDevice signJSON:object] - } - }; -} - - -#pragma mark - Key sharing -- (void)requestRoomKey:(NSDictionary*)requestBody recipients:(NSArray*>*)recipients -{ - [outgoingRoomKeyRequestManager sendRoomKeyRequest:requestBody recipients:recipients]; -} - -- (void)cancelRoomKeyRequest:(NSDictionary*)requestBody -{ - [outgoingRoomKeyRequestManager cancelRoomKeyRequest:requestBody]; -} - -- (void)handleUnrequestedRoomKeyInfo:(MXRoomKeyInfo *)keyInfo senderId:(NSString *)senderId senderKey:(NSString *)senderKey -{ - [unrequestedForwardedRoomKeyManager addPendingKeyWithKeyInfo:keyInfo senderId:senderId senderKey:senderKey]; -} - -- (NSDictionary*)buildMegolmKeyForwardingMessage:(NSString*)roomId senderKey:(NSString*)senderKey sessionId:(NSString*)sessionId chainIndex:(NSNumber*)chainIndex -{ - NSDictionary *key = [self.olmDevice getInboundGroupSessionKey:roomId senderKey:senderKey sessionId:sessionId chainIndex:chainIndex]; - if (key) - { - return @{ - @"type": kMXEventTypeStringRoomForwardedKey, - @"content": @{ - @"algorithm": kMXCryptoMegolmAlgorithm, - @"room_id": roomId, - @"sender_key": senderKey, - @"sender_claimed_ed25519_key": key[@"sender_claimed_ed25519_key"], - @"session_id": sessionId, - @"session_key": key[@"key"], - @"chain_index": key[@"chain_index"], - @"forwarding_curve25519_key_chain": key[@"forwarding_curve25519_key_chain"], - kMXSharedHistoryKeyName: key[@"shared_history"] - } - }; - } - - return nil; -} - -#pragma mark - Private methods -/** - Get or create the GCD queue for a given user. - - @param userId the user id. - @return the dispatch queue to use to handle the crypto for this user. - */ -+ (dispatch_queue_t)dispatchQueueForUser:(NSString*)userId -{ - static NSMutableDictionary *dispatchQueues; - - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - dispatchQueues = [NSMutableDictionary dictionary]; - }); - - dispatch_queue_t queue = dispatchQueues[userId]; - if (!queue) - { - @synchronized (dispatchQueues) - { - MXLogDebug(@"[MXCrypto] Create dispatch queue for %@'s crypto", userId); - queue = dispatch_queue_create([NSString stringWithFormat:@"MXCrypto-%@", userId].UTF8String, DISPATCH_QUEUE_SERIAL); - dispatchQueues[userId] = queue; - } - } - - return queue; -} - -- (NSString*)generateDeviceId -{ - return [[[MXTools generateSecret] stringByReplacingOccurrencesOfString:@"-" withString:@""] substringToIndex:10]; -} - -/** - Ask the server which users have new devices since a given token, - and invalidate them. - - @param oldSyncToken the old token. - @param lastKnownSyncToken the new token. - @param success A block object called when the operation succeeds. - @param failure A block object called when the operation fails. - */ -- (void)invalidateDeviceListsSince:(NSString*)oldSyncToken to:(NSString*)lastKnownSyncToken - success:(void (^)(void))success - failure:(void (^)(NSError *error))failure -{ - [_matrixRestClient keyChangesFrom:oldSyncToken to:lastKnownSyncToken success:^(MXDeviceListResponse *deviceLists) { - - MXLogDebug(@"[MXCrypto] invalidateDeviceListsSince: got key changes since %@: changed: %@\nleft: %@", oldSyncToken, deviceLists.changed, deviceLists.left); - - [self handleDeviceListsChanges:deviceLists]; - - success(); - - } failure:failure]; -} - -/** - Listen to events that change the signatures chain. - */ -- (void)registerEventHandlers -{ - dispatch_async(dispatch_get_main_queue(), ^{ - - // Observe incoming to-device events - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onToDeviceEvent:) name:kMXSessionOnToDeviceEventNotification object:self.mxSession]; - - // Observe membership changes - MXWeakify(self); - self->roomMembershipEventsListener = [self.mxSession listenToEventsOfTypes:@[kMXEventTypeStringRoomEncryption, kMXEventTypeStringRoomMember] onEvent:^(MXEvent *event, MXTimelineDirection direction, id customObject) { - - MXStrongifyAndReturnIfNil(self); - - if (direction == MXTimelineDirectionForwards) - { - if (event.eventType == MXEventTypeRoomEncryption) - { - [self onCryptoEvent:event]; - } - else if (event.eventType == MXEventTypeRoomMember) - { - [self onRoomMembership:event roomState:customObject]; - } - } - }]; - - }); -} - -/** - Handle a to-device event. - - @param notification the notification containing the to-device event. - */ -- (void)onToDeviceEvent:(NSNotification *)notification -{ - MXEvent *event = notification.userInfo[kMXSessionNotificationEventKey]; - - MXLogDebug(@"[MXCrypto] onToDeviceEvent: event.type: %@", event.type); - - if (_cryptoQueue) - { - MXWeakify(self); - switch (event.eventType) - { - case MXEventTypeRoomForwardedKey: - { - dispatch_async(_cryptoQueue, ^{ - MXStrongifyAndReturnIfNil(self); - - [self onRoomKeyEvent:event]; - }); - break; - } - - case MXEventTypeRoomKeyRequest: - { - dispatch_async(_cryptoQueue, ^{ - MXStrongifyAndReturnIfNil(self); - - [self->incomingRoomKeyRequestManager onRoomKeyRequestEvent:event]; - }); - break; - } - - default: - break; - } - } -} - -/** - Handle a key event. - - @param event the key event. - */ -- (void)onRoomKeyEvent:(MXEvent*)event -{ - if (!event.content[@"room_id"] || !event.content[@"algorithm"]) - { - MXLogError(@"[MXCrypto] onRoomKeyEvent: ERROR: Key event is missing fields"); - return; - } - - id alg = [self getRoomDecryptor:event.content[@"room_id"] algorithm:event.content[@"algorithm"]]; - if (!alg) - { - NSString *message = [NSString stringWithFormat:@"[MXCrypto] onRoomKeyEvent: ERROR: Unable to handle keys for %@", event.content[@"algorithm"]]; - MXLogError(message); - return; - } - - [alg onRoomKeyEvent:event]; -} - -/** - Handle an m.room.encryption event. - - @param event the encryption event. - */ -- (void)onCryptoEvent:(MXEvent*)event -{ - MXRoom *room = [_mxSession roomWithRoomId:event.roomId]; - - MXWeakify(self); - [room state:^(MXRoomState *roomState) { - MXStrongifyAndReturnIfNil(self); - - // We can start tracking only lazy loaded room members - // All room members will be loaded when necessary, ie when encrypting in the room - MXRoomMembers *roomMembers = roomState.members; - - NSMutableArray *members = [NSMutableArray array]; - NSArray *encryptionTargetMembers = [roomMembers encryptionTargetMembers:roomState.historyVisibility]; - for (MXRoomMember *roomMember in encryptionTargetMembers) - { - [members addObject:roomMember.userId]; - } - - if (self.cryptoQueue) - { - dispatch_async(self.cryptoQueue, ^{ - [self setEncryptionInRoom:event.roomId withMembers:members algorithm:event.content[@"algorithm"] inhibitDeviceQuery:YES]; - }); - } - }]; -} - -/** - Handle a change in the membership state of a member of a room. - - @param event the membership event causing the change. - @param roomState the know state of the room when the event occurs. - */ -- (void)onRoomMembership:(MXEvent*)event roomState:(MXRoomState*)roomState -{ - // Check whether we have to track the devices for this user. - BOOL shouldTrack = NO; - NSString *userId = event.stateKey; - - MXRoomMemberEventContent *content = [MXRoomMemberEventContent modelFromJSON:event.content]; - if ([userId isEqualToString:self.mxSession.credentials.userId] && [content.membership isEqualToString:kMXMembershipStringInvite]) - { - [unrequestedForwardedRoomKeyManager onRoomInviteWithRoomId:event.roomId senderId:event.sender]; - } - - MXRoomMember *member = [roomState.members memberWithUserId:userId]; - if (member) - { - if (member.membership == MXMembershipJoin) - { - MXLogDebug(@"[MXCrypto] onRoomMembership: Join event for %@ in %@", member.userId, event.roomId); - shouldTrack = YES; - } - // Check whether we should encrypt for the invited members too - else if (member.membership == MXMembershipInvite && ![roomState.historyVisibility isEqualToString:kMXRoomHistoryVisibilityJoined]) - { - // 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. - // They therefore will not send device updates if a user logs in whilst - // their state is invite. - MXLogDebug(@"[MXCrypto] onRoomMembership: Invite event for %@ in %@", member.userId, event.roomId); - shouldTrack = YES; - } - } - - if (shouldTrack && self.cryptoQueue) - { - MXWeakify(self); - dispatch_async(self.cryptoQueue, ^{ - MXStrongifyAndReturnIfNil(self); - - // make sure we are tracking the deviceList for this user - [self.deviceList startTrackingDeviceList:member.userId]; - }); - } -} - -/** - Upload my user's device keys. - */ -- (MXHTTPOperation *)uploadDeviceKeys:(void (^)(MXKeysUploadResponse *keysUploadResponse))success failure:(void (^)(NSError *))failure -{ - // Sanity check - if (!_matrixRestClient.credentials.userId) - { - MXLogError(@"[MXCrypto] uploadDeviceKeys. ERROR: _matrixRestClient.credentials.userId cannot be nil"); - failure(nil); - return nil; - } - - // Prepare the device keys data to send - // Sign it - NSString *signature = [_olmDevice signJSON:_myDevice.signalableJSONDictionary]; - _myDevice.signatures = @{ - _matrixRestClient.credentials.userId: @{ - [NSString stringWithFormat:@"%@:%@", kMXKeyEd25519Type, _myDevice.deviceId]: signature - } - }; - - // For now, we set the device id explicitly, as we may not be using the - // same one as used in login. - return [_matrixRestClient uploadKeys:_myDevice.JSONDictionary oneTimeKeys:nil fallbackKeys:nil success:success failure:failure]; -} - -/** - Check if it's time to upload one-time keys, and do so if so. - */ -- (void)maybeUploadOneTimeKeys:(void (^)(void))success failure:(void (^)(NSError *))failure -{ - if (uploadOneTimeKeysOperation) - { - MXLogDebug(@"[MXCrypto] maybeUploadOneTimeKeys: already in progress"); - if (success) - { - success(); - } - return; - } - - NSDate *now = [NSDate date]; - if (lastOneTimeKeyCheck && [now timeIntervalSinceDate:lastOneTimeKeyCheck] < kMXCryptoUploadOneTimeKeysPeriod) - { - // We've done a key upload recently. - if (success) - { - success(); - } - return; - } - - lastOneTimeKeyCheck = now; - - if (oneTimeKeyCount != -1) - { - // We already have the current one_time_key count from a /sync response. - // Use this value instead of asking the server for the current key count. - MXLogDebug(@"[MXCrypto] maybeUploadOneTimeKeys: there are %tu one-time keys on the homeserver", oneTimeKeyCount); - - MXWeakify(self); - uploadOneTimeKeysOperation = [self generateAndUploadOneTimeKeys:oneTimeKeyCount retry:YES success:^{ - MXStrongifyAndReturnIfNil(self); - - self->uploadOneTimeKeysOperation = nil; - if (success) - { - success(); - } - - } failure:^(NSError *error) { - MXStrongifyAndReturnIfNil(self); - - MXLogErrorDetails(@"[MXCrypto] maybeUploadOneTimeKeys: Failed to publish one-time keys", @{ - @"error": error ?: @"unknown" - }); - self->uploadOneTimeKeysOperation = nil; - - if (failure) - { - failure(error); - } - }]; - - if (!uploadOneTimeKeysOperation && success) - { - success(); - } - - // Reset oneTimeKeyCount to prevent start uploading based on old data. - // It will be set again on the next /sync-response - oneTimeKeyCount = -1; - } - else - { - // Ask the server how many keys we have - MXWeakify(self); - uploadOneTimeKeysOperation = [self publishedOneTimeKeysCount:^(NSUInteger keyCount) { - - if (!self->uploadOneTimeKeysOperation) - { - if (success) - { - success(); - } - return; - }; - - MXWeakify(self); - MXHTTPOperation *operation2 = [self generateAndUploadOneTimeKeys:keyCount retry:YES success:^{ - MXStrongifyAndReturnIfNil(self); - - self->uploadOneTimeKeysOperation = nil; - if (success) - { - success(); - } - - } failure:^(NSError *error) { - MXStrongifyAndReturnIfNil(self); - - MXLogErrorDetails(@"[MXCrypto] maybeUploadOneTimeKeys: Failed to publish one-time keys", @{ - @"error": error ?: @"unknown" - }); - self->uploadOneTimeKeysOperation = nil; - - if (failure) - { - failure(error); - } - }]; - - if (operation2) - { - [self->uploadOneTimeKeysOperation mutateTo:operation2]; - } - else - { - self->uploadOneTimeKeysOperation = nil; - if (success) - { - success(); - } - } - - } failure:^(NSError *error) { - MXStrongifyAndReturnIfNil(self); - - MXLogErrorDetails(@"[MXCrypto] maybeUploadOneTimeKeys: Get published one-time keys count failed", @{ - @"error": error ?: @"unknown" - }); - self->uploadOneTimeKeysOperation = nil; - - if (failure) - { - failure(error); - } - }]; - } -} - -- (MXHTTPOperation *)generateAndUploadOneTimeKeys:(NSUInteger)keyCount retry:(BOOL)retry success:(void (^)(void))success failure:(void (^)(NSError *))failure -{ - MXLogDebug(@"[MXCrypto] generateAndUploadOneTimeKeys: %@ one time keys are available on the homeserver", @(keyCount)); - - MXHTTPOperation *operation; - - if ([self generateOneTimeKeys:keyCount]) - { - operation = [self uploadOneTimeKeys:^(MXKeysUploadResponse *keysUploadResponse) { - success(); - } failure:^(NSError *error) { - MXLogErrorDetails(@"[MXCrypto] generateAndUploadOneTimeKeys: Failed to publish one-time keys", @{ - @"error": error ?: @"unknown" - }); - - if ([MXError isMXError:error] && retry) - { - // The homeserver explicitly rejected the request. - // Reset local OTKs we tried to push and retry - // There is no matrix specific error but we really want to detect the error described at - // https://github.com/vector-im/element-ios/issues/3721 - MXLogError(@"[MXCrypto] uploadOneTimeKeys: Reset local OTKs because the server does not like them"); - [self.olmDevice markOneTimeKeysAsPublished]; - - [self generateAndUploadOneTimeKeys:keyCount retry:NO success:success failure:failure]; - } - else - { - failure(error); - } - }]; - } - - return operation; -} - -- (MXHTTPOperation *)generateAndUploadFallbackKey -{ - [_olmDevice generateFallbackKey]; - - NSDictionary *fallbackKey = _olmDevice.fallbackKey; - NSMutableDictionary *fallbackKeyJson = [NSMutableDictionary dictionary]; - - for (NSString *keyId in fallbackKey[kMXKeyCurve25519Type]) - { - // Sign the fallback key - NSMutableDictionary *signedKey = [NSMutableDictionary dictionary]; - signedKey[@"key"] = fallbackKey[kMXKeyCurve25519Type][keyId]; - signedKey[@"fallback"] = @(YES); - signedKey[@"signatures"] = [self signObject:signedKey]; - - fallbackKeyJson[[NSString stringWithFormat:@"%@:%@", kMXKeySignedCurve25519Type, keyId]] = signedKey; - } - - MXLogDebug(@"[MXCrypto] generateAndUploadFallbackKey: Started uploading fallback key."); - - MXWeakify(self); - return [_matrixRestClient uploadKeys:nil oneTimeKeys:nil fallbackKeys:fallbackKeyJson success:^(MXKeysUploadResponse *keysUploadResponse) { - MXStrongifyAndReturnIfNil(self); - - self.uploadFallbackKeyOperation = nil; - MXLogDebug(@"[MXCrypto] generateAndUploadFallbackKey: Finished uploading fallback key."); - } failure:^(NSError *error) { - MXStrongifyAndReturnIfNil(self); - - self.uploadFallbackKeyOperation = nil; - MXLogError(@"[MXCrypto] generateAndUploadFallbackKey: Failed uploading fallback key."); - }]; -} - -/** - Generate required one-time keys. - - @param keyCount the number of key currently available on the homeserver. - @return NO if no keys need to be generated. - */ -- (BOOL)generateOneTimeKeys:(NSUInteger)keyCount -{ - // We need to keep a pool of one time public keys on the server so that - // other devices can start conversations with us. But we can only store - // a finite number of private keys in the olm Account object. - // To complicate things further then can be a delay between a device - // claiming a public one time key from the server and it sending us a - // message. We need to keep the corresponding private key locally until - // we receive the message. - // But that message might never arrive leaving us stuck with duff - // private keys clogging up our local storage. - // So we need some kind of enginering compromise to balance all of - // these factors. - - MXLogDebug(@"[MXCrypto] generateOneTimeKeys: %tu one-time keys on the homeserver", keyCount); - - // First check how many keys we can store in the Account object. - NSUInteger maxOneTimeKeys = _olmDevice.maxNumberOfOneTimeKeys; - - // Try to keep at most half that number on the server. This leaves the - // rest of the slots free to hold keys that have been claimed from the - // server but we haven't recevied a message for. - // If we run out of slots when generating new keys then olm will - // discard the oldest private keys first. This will eventually clean - // out stale private keys that won't receive a message. - NSUInteger keyLimit = maxOneTimeKeys / 2; - - // We work out how many new keys we need to create to top up the server - // If there are too many keys on the server then we don't need to - // create any more keys. - NSUInteger numberToGenerate = 0; - if (keyLimit > keyCount) - { - numberToGenerate = keyLimit - keyCount; - } - - - MXLogDebug(@"[MXCrypto] generateOneTimeKeys: Generate %tu keys", numberToGenerate); - - if (numberToGenerate) - { - // Ask olm to generate new one time keys, then upload them to synapse. - NSDate *startDate = [NSDate date]; - [_olmDevice generateOneTimeKeys:numberToGenerate]; - MXLogDebug(@"[MXCrypto] generateOneTimeKeys: Keys generated in %.0fms", [[NSDate date] timeIntervalSinceDate:startDate] * 1000); - } - - return (numberToGenerate > 0); -} - -/** - Upload my user's one time keys. - */ -- (MXHTTPOperation *)uploadOneTimeKeys:(void (^)(MXKeysUploadResponse *keysUploadResponse))success failure:(void (^)(NSError *))failure -{ - NSDictionary *oneTimeKeys = _olmDevice.oneTimeKeys; - NSMutableDictionary *oneTimeJson = [NSMutableDictionary dictionary]; - - for (NSString *keyId in oneTimeKeys[kMXKeyCurve25519Type]) - { - // Sign each one-time key - NSMutableDictionary *k = [NSMutableDictionary dictionary]; - k[@"key"] = oneTimeKeys[kMXKeyCurve25519Type][keyId]; - k[@"signatures"] = [self signObject:k]; - - oneTimeJson[[NSString stringWithFormat:@"%@:%@", kMXKeySignedCurve25519Type, keyId]] = k; - } - - MXLogDebug(@"[MXCrypto] uploadOneTimeKeys: Upload %tu keys", ((NSDictionary*)oneTimeKeys[kMXKeyCurve25519Type]).count); - - // For now, we set the device id explicitly, as we may not be using the - // same one as used in login. - MXWeakify(self); - return [_matrixRestClient uploadKeys:nil oneTimeKeys:oneTimeJson fallbackKeys:nil success:^(MXKeysUploadResponse *keysUploadResponse) { - MXStrongifyAndReturnIfNil(self); - - [self.olmDevice markOneTimeKeysAsPublished]; - success(keysUploadResponse); - - } failure:^(NSError *error) { - MXLogError(@"[MXCrypto] uploadOneTimeKeys fails."); - failure(error); - }]; -} - -// Ask the server how many keys we have -- (MXHTTPOperation *)publishedOneTimeKeysCount:(void (^)(NSUInteger publishedKeyCount))success failure:(void (^)(NSError *))failure -{ - return [_matrixRestClient uploadKeys:_myDevice.JSONDictionary oneTimeKeys:nil fallbackKeys:nil success:^(MXKeysUploadResponse *keysUploadResponse) { - - NSUInteger publishedkeyCount = [keysUploadResponse oneTimeKeyCountsForAlgorithm:kMXKeySignedCurve25519Type]; - - MXLogDebug(@"[MXCrypto] publishedOneTimeKeysCount: %@ OTKs on the homeserver", @(publishedkeyCount)); - - success(publishedkeyCount); - - } failure:^(NSError *error) { - MXLogErrorDetails(@"[MXCrypto] publishedOneTimeKeysCount failed", @{ - @"error": error ?: @"unknown" - }); - failure(error); - }]; -} - - -#pragma mark Wedged olm sessions - -- (void)markOlmSessionForUnwedgingInEvent:(MXEvent*)event -{ - NSString *sender = event.sender; - NSString *deviceKey, *algorithm; - MXJSONModelSetString(deviceKey, event.content[@"sender_key"]); - MXJSONModelSetString(algorithm, event.content[@"algorithm"]); - - MXLogDebug(@"[MXCrypto] markOlmSessionForUnwedging from %@:%@", sender, deviceKey); - - if (!sender || !deviceKey || !algorithm) - { - return; - } - - if ([sender isEqualToString:_mxSession.myUserId] - && [deviceKey isEqualToString:self.olmDevice.deviceCurve25519Key]) - { - MXLogDebug(@"[MXCrypto] markOlmSessionForUnwedging: Do not unwedge ourselves"); - return; - } - - // Check when we last forced a new session with this device: if we've already done so - // recently, don't do it again. - NSDate *lastNewSessionForcedDate = [lastNewSessionForcedDates objectForDevice:deviceKey forUser:sender]; - if (lastNewSessionForcedDate - && -[lastNewSessionForcedDate timeIntervalSinceNow] < kMXCryptoMinForceSessionPeriod) - { - MXLogDebug(@"[MXCrypto] markOlmSessionForUnwedging: New session already forced with device at %@. Not forcing another", lastNewSessionForcedDate); - return; - } - - // Establish a new olm session with this device since we're failing to decrypt messages - // on a current session. - MXDeviceInfo *device = [_store deviceWithIdentityKey:deviceKey]; - if (!device) - { - MXLogDebug(@"[MXCrypto] markOlmSessionForUnwedgingInEvent: Couldn't find device for identity key %@: not re-establishing session", deviceKey); - return; - } - - MXLogDebug(@"[MXCrypto] markOlmSessionForUnwedging from %@:%@", sender, device.deviceId); - - [lastNewSessionForcedDates setObject:[NSDate date] forUser:sender andDevice:deviceKey]; - - NSDictionary *userDevice = @{ - sender: @[device] - }; - [self ensureOlmSessionsForDevices:userDevice force:YES success:^(MXUsersDevicesMap *results) { - - // Now send a blank message on that session so the other side knows about it. - // (The keyshare request is sent in the clear so that won't do) - // We send this first such that, as long as the toDevice messages arrive in the - // same order we sent them, the other end will get this first, set up the new session, - // then get the keyshare request and send the key over this new session (because it - // is the session it has most recently received a message on). - NSDictionary *encryptedContent = [self encryptMessage:@{ - @"type": @"m.dummy" - } - forDevices:@[device]]; - - MXUsersDevicesMap *contentMap = [MXUsersDevicesMap new]; - [contentMap setObject:encryptedContent forUser:sender andDevice:device.deviceId]; - - MXToDevicePayload *payload = [[MXToDevicePayload alloc] initWithEventType:kMXEventTypeStringRoomEncrypted - contentMap:contentMap]; - [self.matrixRestClient sendToDevice:payload success:nil failure:^(NSError *error) { - MXLogDebug(@"[MXCrypto] markOlmSessionForUnwedgingInEvent: ERROR for sendToDevice: %@", error); - }]; - - } failure:^(NSError *error) { - MXLogErrorDetails(@"[MXCrypto] markOlmSessionForUnwedgingInEvent: ERROR for ensureOlmSessionsForDevices", @{ - @"error": error ?: @"unknown" - }); - }]; -} - -#pragma mark - MXUnrequestedForwardedRoomKeyManagerDelegate - -- (void)downloadDeviceKeysWithUserId:(NSString *)userId completion:(void (^)(MXUsersDevicesMap *))completion -{ - [self downloadKeys:@[userId] forceDownload:YES success:^(MXUsersDevicesMap *usersDevicesInfoMap, NSDictionary *crossSigningKeysMap) { - completion(usersDevicesInfoMap); - } failure:^(NSError *error) { - MXLogError(@"[MXCrypto]: Failed downloading keys for key forward manager"); - completion([[MXUsersDevicesMap alloc] init]); - }]; -} - -- (void)acceptRoomKeyWithKeyInfo:(MXRoomKeyInfo *)keyInfo -{ - id decryptor = [self getRoomDecryptor:keyInfo.roomId algorithm:keyInfo.algorithm]; - MXRoomKeyResult *key = [[MXRoomKeyResult alloc] initWithType:MXRoomKeyTypeUnsafe info:keyInfo]; - [decryptor onRoomKey:key]; -} - -#endif - -@end diff --git a/MatrixSDK/Crypto/MXCryptoV2.swift b/MatrixSDK/Crypto/MXCryptoV2.swift index 0619d0516d..007260146b 100644 --- a/MatrixSDK/Crypto/MXCryptoV2.swift +++ b/MatrixSDK/Crypto/MXCryptoV2.swift @@ -115,7 +115,6 @@ class MXCryptoV2: NSObject, MXCrypto { backup = MXKeyBackup( engine: engine, restClient: restClient, - secretShareManager: MXSecretShareManager(), queue: legacyQueue ) } else { diff --git a/MatrixSDK/Crypto/MXCryptoV2Factory.swift b/MatrixSDK/Crypto/MXCryptoV2Factory.swift index d910a1dc7a..8c7543d3c1 100644 --- a/MatrixSDK/Crypto/MXCryptoV2Factory.swift +++ b/MatrixSDK/Crypto/MXCryptoV2Factory.swift @@ -75,14 +75,6 @@ import Foundation Task.detached { [weak self] in guard let self = self else { return } - do { - try self.deprecateLegacyStoreIfNecessary(credentials: credentials) { - migrationProgress?($0) - } - } catch { - self.log.error("Failed to migrate / deprecate legacy store", context: error) - } - do { let crypto = try await MXCryptoV2( userId: userId, @@ -101,55 +93,4 @@ import Foundation } } } - - private func deprecateLegacyStoreIfNecessary( - credentials: MXCredentials, - updateProgress: @escaping ((Double) -> Void) - ) throws { - guard - MXRealmCryptoStore.hasData(for: credentials), - let legacyStore = MXRealmCryptoStore(credentials: credentials) - else { - log.debug("Legacy crypto store does not exist") - return - } - - log.debug("Legacy crypto store exists") - try migrateIfNecessary(legacyStore: legacyStore, updateProgress: updateProgress) - - log.debug("Removing legacy crypto store entirely") - MXRealmCryptoStore.delete(with: credentials) - } - - private func migrateIfNecessary( - legacyStore: MXCryptoStore, - updateProgress: @escaping (Double) -> Void - ) throws { - let legacyVersion = legacyStore.cryptoVersion.rawValue - guard legacyVersion < lastDeprecatedVersion.rawValue else { - log.debug("Legacy crypto has already been deprecated, no need to migrate") - return - } - - log.debug("Requires migration from legacy crypto version \(legacyVersion) to version \(lastDeprecatedVersion.rawValue)") - let migration = MXCryptoMigrationV2(legacyStore: legacyStore) - - // Full vs partial/room migration are mutually exclusive, only one should be run - if legacyVersion < MXCryptoVersion.deprecated1.rawValue { - log.debug("Full migration of crypto data") - try migration.migrateAllData(updateProgress: updateProgress) - - } else if legacyVersion < MXCryptoVersion.deprecated2.rawValue { - log.debug("Partial migration of room and global settings") - try migration.migrateRoomAndGlobalSettingsOnly(updateProgress: updateProgress) - } - - if legacyVersion < MXCryptoVersion.deprecated3.rawValue { - // The following flag will result in displaying a different UX when verifying current session, - // unless the rust-based crypto already considers the current session to be verified given - // the migration data - log.debug("Needs verification upgrade") - MXSDKOptions.sharedInstance().cryptoMigrationDelegate?.needsVerificationUpgrade = true - } - } } diff --git a/MatrixSDK/Crypto/MXCrypto_Private.h b/MatrixSDK/Crypto/MXCrypto_Private.h deleted file mode 100644 index b1f705f3d4..0000000000 --- a/MatrixSDK/Crypto/MXCrypto_Private.h +++ /dev/null @@ -1,240 +0,0 @@ -/* - Copyright 2016 OpenMarket Ltd - Copyright 2020 The Matrix.org Foundation C.I.C - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -#import - -#import "MXSDKOptions.h" - -#ifdef MX_CRYPTO - -#import "MXCryptoStore.h" -#import "MXSession.h" -#import "MXRestClient.h" -#import "MXOlmDevice.h" -#import "MXDeviceList.h" -#import "MXCryptoAlgorithms.h" -#import "MXUsersDevicesMap.h" -#import "MXOlmSessionResult.h" -#import "MXKeyBackup_Private.h" - -#import "MXCrypto.h" - -@class MXRoomKeyInfo; - -/** - The `MXCrypto_Private` extension exposes internal operations. - - These methods run on a dedicated thread and must be called with the corresponding care. - */ -@interface MXLegacyCrypto () - -/** - The store for crypto data. - */ -@property (nonatomic, readonly) id store; - -/** - The libolm wrapper. - */ -@property (nonatomic, readonly) MXOlmDevice *olmDevice; - -/** - The Matrix session. - */ -@property (nonatomic, readonly) MXSession *mxSession; - -/** - The instance used to make requests to the homeserver. - */ -@property (nonatomic, readonly) MXRestClient *matrixRestClient; - -/** - Our device keys - */ -@property (nonatomic, readonly) MXDeviceInfo *myDevice; - -/** - The queue used for almost all crypto processing. - */ -@property (nonatomic, readonly) dispatch_queue_t cryptoQueue; - -/** - The list of devices. - */ -@property (nonatomic, readonly) MXDeviceList *deviceList; - -/** - Get the device which sent an event. - - @param event the event to be checked. - @return device info. - */ -- (MXDeviceInfo*)eventSenderDeviceOfEvent:(MXEvent*)event; - -/** - Configure a room to use encryption. - - @param roomId the room id to enable encryption in. - @param members a list of user ids. - @param algorithm the encryption config for the room. - @param inhibitDeviceQuery YES to suppress device list query for users in the room (for now) - @return YES if the operation succeeds. - */ -- (BOOL)setEncryptionInRoom:(NSString*)roomId withMembers:(NSArray*)members algorithm:(NSString*)algorithm inhibitDeviceQuery:(BOOL)inhibitDeviceQuery; - -/** - Try to make sure we have established olm sessions for the given users. - - @param users a list of user ids. - - @param success A block object called when the operation succeeds. - @param failure A block object called when the operation fails. - - @return a MXHTTPOperation instance. May be nil if the data is already in the store. - */ -- (MXHTTPOperation*)ensureOlmSessionsForUsers:(NSArray*)users - success:(void (^)(MXUsersDevicesMap *results))success - failure:(void (^)(NSError *error))failure; - -/** - Try to make sure we have established olm sessions for the given devices. - - @param devicesByUser a map from userid to list of devices. - - @param success A block object called when the operation succeeds. - @param failure A block object called when the operation fails. - */ -- (MXHTTPOperation*)ensureOlmSessionsForDevices:(NSDictionary*>*)devicesByUser - force:(BOOL)force - success:(void (^)(MXUsersDevicesMap *results))success - failure:(void (^)(NSError *error))failure; - -/** - Encrypt an event payload for a list of devices. - - @param payloadFields fields to include in the encrypted payload. - @param devices the list of the recipient devices. - - @return the content for an m.room.encrypted event. - */ -- (NSDictionary*)encryptMessage:(NSDictionary*)payloadFields forDevices:(NSArray*)devices; - -/** - Get a decryptor for a given room and algorithm. - - If we already have a decryptor for the given room and algorithm, return - it. Otherwise try to instantiate it. - - @param roomId room id for decryptor. If undefined, a temporary decryptor is instantiated. - @param algorithm the crypto algorithm. - @return the decryptor. - */ -- (id)getRoomDecryptor:(NSString*)roomId algorithm:(NSString*)algorithm; - -/** - Get the encryptor for a given room and algorithm. - - @param roomId room id for encryptor. - @param algorithm the crypto algorithm. - @return the decryptor. - */ -- (id)getRoomEncryptor:(NSString*)roomId algorithm:(NSString*)algorithm; - -/** - Sign the given object with our ed25519 key. - - @param object the dictionary to sign. - @return signatures. - */ -- (NSDictionary*)signObject:(NSDictionary*)object; - - -#pragma mark - One Time keys - -/** - Retrieve the number of one time keys published on the homeserver. - - @param success A block object called when the operation succeeds. - It provides the number of OTKs. - @param failure A block object called when the operation fails. - */ -- (MXHTTPOperation *)publishedOneTimeKeysCount:(void (^)(NSUInteger publishedKeyCount))success - failure:(void (^)(NSError *))failure; - -/** - Generate and publish enough one time keys on the homeserver. - - @param keyCount the number of keys currently available on the homeserver. - @param retry YES to retry in case of server error. - @param success A block object called when the operation succeeds. - @param failure A block object called when the operation fails. - */ -- (MXHTTPOperation *)generateAndUploadOneTimeKeys:(NSUInteger)keyCount - retry:(BOOL)retry - success:(void (^)(void))success - failure:(void (^)(NSError *))failure; - - -#pragma mark - import/export - -/** - Import a list of megolm session keys. - - @param sessionDatas megolm sessions. - @param backUp YES to back up them to the homeserver. - @param success A block object called when the operation succeeds. - It provides the number of found keys and the number of successfully imported keys. - @param failure A block object called when the operation fails. - */ -- (void)importMegolmSessionDatas:(NSArray*)sessionDatas - backUp:(BOOL)backUp - success:(void (^)(NSUInteger total, NSUInteger imported))success - failure:(void (^)(NSError *error))failure; - - -#pragma mark - Key sharing - -/** - Send a request for some room keys, if we have not already done so. - - @param requestBody the requestBody. - @param recipients a {Array<{userId: string, deviceId: string}>}. - */ -- (void)requestRoomKey:(NSDictionary*)requestBody recipients:(NSArray*>*)recipients; - -/** - Cancel any earlier room key request. - - @param requestBody parameters to match for cancellation - */ -- (void)cancelRoomKeyRequest:(NSDictionary*)requestBody; - -// Create a message to forward a megolm session -- (NSDictionary*)buildMegolmKeyForwardingMessage:(NSString*)roomId senderKey:(NSString*)senderKey sessionId:(NSString*)sessionId chainIndex:(NSNumber*)chainIndex; - -/** - Handle forwarded room key that was not requested by this device - - @param keyInfo details about the key - @param senderId userId of the person who sent us the key - @param senderKey identity of the person who sent us the room key - */ -- (void)handleUnrequestedRoomKeyInfo:(MXRoomKeyInfo *)keyInfo senderId:(NSString *)senderId senderKey:(NSString *)senderKey; - -@end - -#endif diff --git a/MatrixSDK/Crypto/MXOlmDevice.h b/MatrixSDK/Crypto/MXOlmDevice.h deleted file mode 100644 index 9fe170f572..0000000000 --- a/MatrixSDK/Crypto/MXOlmDevice.h +++ /dev/null @@ -1,353 +0,0 @@ -/* - Copyright 2016 OpenMarket 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. - */ - -#import - -#import "MXSDKOptions.h" - -#ifdef MX_CRYPTO - -#import "MXCryptoStore.h" -#import "MXDecrypting.h" - -/** - An instance of MXOlmDevice manages the olm cryptography functions. - - Each OlmDevice has a single OlmAccount and a number of OlmSessions. - Accounts and sessions are kept pickled in a MXStore. - */ -@interface MXOlmDevice : NSObject - -/** - Create the `MXOlmDevice` instance. - - @param store the crypto data storage. - @return the newly created MXOlmDevice instance. - */ -- (instancetype)initWithStore:(id)store; - -/** - Curve25519 key for the account. - */ -@property (nonatomic, readonly) NSString *deviceCurve25519Key; - -/** - Ed25519 key for the account. - */ -@property (nonatomic, readonly) NSString *deviceEd25519Key; - -/** - The olm library version. - */ -@property (nonatomic, readonly) NSString *olmVersion; - -/** - Signs a message with the ed25519 key for this account. - - @param message the message to be signed. - @return the base64-encoded signature. - */ -- (NSString*)signMessage:(NSData*)message; - -/** - Signs a JSON dictionary with the ed25519 key for this account. - - The signature is done on canonical version of the JSON. - - @param JSONDictinary the JSON to be signed. - @return the base64-encoded signature - */ -- (NSString*)signJSON:(NSDictionary*)JSONDictinary; - -/** - The current (unused, unpublished) one-time keys for this account. - - @return a dictionary with one key which is "curve25519". - Its value is a dictionary where keys are keys ids - and values, the Curve25519 keys. - */ -@property (nonatomic, readonly) NSDictionary *oneTimeKeys; - -/** - The maximum number of one-time keys the olm account can store. - */ -@property (nonatomic, readonly)NSUInteger maxNumberOfOneTimeKeys; - -/** - * Marks all of the one-time keys as published. - */ -- (void)markOneTimeKeysAsPublished; - -/** - Generate some new one-time keys - - @param numKeys the number of keys to generate - */ -- (void)generateOneTimeKeys:(NSUInteger)numKeys; - -/** - The current fallback key for this account. - - @return a dictionary with one key which is "curve25519". - Its value is a dictionary where keys are keys ids - and values, the Curve25519 keys. - */ -@property (nonatomic, readonly) NSDictionary *fallbackKey; - -/** - Generate a new fallback key - */ -- (void)generateFallbackKey; - -/** - Generate a new outbound session. - - The new session will be stored in the MXStore. - - @param theirIdentityKey the remote user's Curve25519 identity key - @param theirOneTimeKey the remote user's one-time Curve25519 key - @return the session id for the outbound session. - */ -- (NSString*)createOutboundSession:(NSString*)theirIdentityKey theirOneTimeKey:(NSString*)theirOneTimeKey; - -/** - Generate a new inbound session, given an incoming message. - - @param theirDeviceIdentityKey the remote user's Curve25519 identity key. - @param messageType the message_type field from the received message (must be 0). - @param ciphertext base64-encoded body from the received message. - @param payload the decoded payload. - - @return the session id. Nil if the received message was not valid (for instance, it - didn't use a valid one-time key). - */ -- (NSString*)createInboundSession:(NSString*)theirDeviceIdentityKey messageType:(NSUInteger)messageType cipherText:(NSString*)ciphertext payload:(NSString**)payload; - -/** - Get a list of known session IDs for the given device. - - @param theirDeviceIdentityKey the Curve25519 identity key for the remote device. - @return a list of known session ids for the device. - */ -- (NSArray*)sessionIdsForDevice:(NSString*)theirDeviceIdentityKey; - -/** - Get the right olm session id for encrypting messages to the given identity key. - - @param theirDeviceIdentityKey the Curve25519 identity key for the remote device. - @return the session id, or nil if no established session. - */ -- (NSString*)sessionIdForDevice:(NSString*)theirDeviceIdentityKey; - -/** - Encrypt an outgoing message using an existing session. - - @param theirDeviceIdentityKey the Curve25519 identity key for the remote device. - @param sessionId the id of the active session - @param payloadString the payload to be encrypted and sent - - @return a dictionary containing a "body", the ciphertext, and a "type", the message type. - */ -- (NSDictionary*)encryptMessage:(NSString*)theirDeviceIdentityKey sessionId:(NSString*)sessionId payloadString:(NSString*)payloadString; - -/** - Decrypt an incoming message using an existing session. - - @param ciphertext the base64-encoded body from the received message. - @param messageType message_type field from the received message. - @param theirDeviceIdentityKey the Curve25519 identity key for the remote device. - @param sessionId the id of the active session. - - @return the decrypted payload. - */ -- (NSString*)decryptMessage:(NSString*)ciphertext withType:(NSUInteger)messageType sessionId:(NSString*)sessionId theirDeviceIdentityKey:(NSString*)theirDeviceIdentityKey; - -/** -Determine if an incoming messages is a prekey message matching an existing session. - - @param theirDeviceIdentityKey the Curve25519 identity key for the remote device. - @param sessionId the id of the active session. - @param messageType message_type field from the received message. - @param ciphertext the base64-encoded body from the received message. - - @return YES if the received message is a prekey message which matchesthe given session. - */ -- (BOOL)matchesSession:(NSString*)theirDeviceIdentityKey sessionId:(NSString*)sessionId messageType:(NSUInteger)messageType ciphertext:(NSString*)ciphertext; - - -#pragma mark - Outbound group session -/** - Generate a new outbound group session. - - @param roomId Identifer of the room. - @return the newly created and stored outbound group session. - */ -- (MXOlmOutboundGroupSession *)createOutboundGroupSessionForRoomWithRoomId:(NSString *)roomId; - -/** - Store a outbound group session with session ID and room with ID. - - @param session outbound group session to be stored. - */ -- (void)storeOutboundGroupSession:(MXOlmOutboundGroupSession *)session; - -/** - Retrieve a outbound group session Info for a specific room. - - @param roomId Identifer of the room. - @return the session info instance for the outbound session. Nil if not found in store. - */ -- (MXOlmOutboundGroupSession *)outboundGroupSessionForRoomWithRoomId:(NSString *)roomId; - -/** - Remove the outbound group session for a specific room. - - @param roomId Identifer of the room. - */ -- (void)discardOutboundGroupSessionForRoomWithRoomId:(NSString *)roomId; - -#pragma mark - Inbound group session -/** - Add an inbound group session to the session store. - - @param sessionId the session identifier. - @param sessionKey base64-encoded secret key. - @param roomId the id of the room in which this session will be used. - @param senderKey the base64-encoded curve25519 key of the sender. - @param forwardingCurve25519KeyChain devices which forwarded this session to us (normally empty) - @param keysClaimed Other keys the sender claims. - @param exportFormat YES if the megolm keys are in export format (ie, they lack an ed25519 signature). - @param sharedHistory YES if the session was created whilst room's history was set to visible (i.e. `world_readable` or `shared` - - @return YES if the operation succeeds. - */ -- (BOOL)addInboundGroupSession:(NSString*)sessionId - sessionKey:(NSString*)sessionKey - roomId:(NSString*)roomId - senderKey:(NSString*)senderKey - forwardingCurve25519KeyChain:(NSArray *)forwardingCurve25519KeyChain - keysClaimed:(NSDictionary*)keysClaimed - exportFormat:(BOOL)exportFormat - sharedHistory:(BOOL)sharedHistory - untrusted:(BOOL)untrusted; - -/** - Add previously-exported inbound group sessions to the session store. - - @param inboundGroupSessionsData the group sessions data. - @return the imported keys. - */ -- (NSArray*)importInboundGroupSessions:(NSArray*)inboundGroupSessionsData; - -/** - Decrypt a received message with an inbound group session. - - @param body the base64-encoded body of the encrypted message. - @param isEditEvent whether the event has an edit relationship to another event. - This is used when detecting a replay attack as a way to - distinguish an edit of a message from the original edited message. - @param roomId the room in which the message was received. - @param timeline the id of the timeline where the event is decrypted. It is used - to prevent replay attack. - @param sessionId the session identifier. - @param senderKey the base64-encoded curve25519 key of the sender. - @param error the result error if there is a problem decrypting the event. - - @return the decrypting result. Nil if the sessionId is unknown. - */ -- (MXDecryptionResult*)decryptGroupMessage:(NSString*)body - isEditEvent:(BOOL)isEditEvent - roomId:(NSString*)roomId - inTimeline:(NSString*)timeline - sessionId:(NSString*)sessionId senderKey:(NSString*)senderKey - error:(NSError** )error; - -/** - Reset replay attack data for the given timeline. - - @param timeline the id of the timeline. - */ -- (void)resetReplayAttackCheckInTimeline:(NSString*)timeline; - -/** - Determine if we have the keys for a given megolm session. - - @param roomId the room in which the message was received. - @param senderKey the base64-encoded curve25519 key of the sender. - @param sessionId the session identifier. - @return YES if we have the keys to this session. - */ -- (BOOL)hasInboundSessionKeys:(NSString*)roomId senderKey:(NSString*)senderKey sessionId:(NSString*)sessionId; - -/** - Extract the keys to a given megolm session, for sharing. - - @param roomId the room in which the message was received. - @param senderKey the base64-encoded curve25519 key of the sender. - @param sessionId the session identifier. - @param chainIndex The chain index at which to export the session. - If nil, export at the first index we know about. - - @return a dictinary { - chain_index: number, - key: string, - forwarding_curve25519_key_chain: Array, - sender_claimed_ed25519_key: string - } details of the session key. The key is a base64-encoded megolm key in export format. - */ -- (NSDictionary*)getInboundGroupSessionKey:(NSString*)roomId senderKey:(NSString*)senderKey sessionId:(NSString*)sessionId chainIndex:(NSNumber*)chainIndex; - - -#pragma mark - Utilities -/** - Verify an ed25519 signature. - - @param key the ed25519 key. - @param message the message which was signed. - @param signature the base64-encoded signature to be checked. - @param error the result error if there is a problem with the verification. - If the key was too small then the message will be "OLM.INVALID_BASE64". - If the signature was invalid then the message will be "OLM.BAD_MESSAGE_MAC". - - @return YES if valid. - */ -- (BOOL)verifySignature:(NSString*)key message:(NSString*)message signature:(NSString*)signature error:(NSError**)error; - -/** - Verify an ed25519 signature on a JSON object. - - @param key the ed25519 key. - @param JSONDictinary the JSON object which was signed. - @param signature the base64-encoded signature to be checked. - @param error the result error if there is a problem with the verification. - If the key was too small then the message will be "OLM.INVALID_BASE64". - If the signature was invalid then the message will be "OLM.BAD_MESSAGE_MAC". - - @return YES if valid. - */ -- (BOOL)verifySignature:(NSString*)key JSON:(NSDictionary*)JSONDictinary signature:(NSString*)signature error:(NSError**)error; - -/** - Calculate the SHA-256 hash of the input and encodes it as base64. - - @param message the message to hash. - @return the base64-encoded hash value. - */ -- (NSString*)sha256:(NSString*)message; - -@end - -#endif diff --git a/MatrixSDK/Crypto/MXOlmDevice.m b/MatrixSDK/Crypto/MXOlmDevice.m deleted file mode 100644 index 3d830da9d8..0000000000 --- a/MatrixSDK/Crypto/MXOlmDevice.m +++ /dev/null @@ -1,736 +0,0 @@ -/* - Copyright 2016 OpenMarket 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. - */ - -#import "MXSDKOptions.h" - -#ifdef MX_CRYPTO - -#import "MXOlmDevice.h" - -#import - -#import "MXTools.h" -#import "MXCryptoTools.h" -#import "MXRealmCryptoStore.h" - -#import "MXKeyProvider.h" -#import "MXRawDataKey.h" -#import "MXCryptoConstants.h" - -NSInteger const kMXInboundGroupSessionCacheSize = 100; - -@interface MXOlmDevice () -{ - // The OLMKit utility instance. - OLMUtility *olmUtility; - - // Store a set of decrypted message indexes for each group session. - // This partially mitigates a replay attack where a MITM resends a group - // message into the room. - // - // The Matrix SDK exposes events through MXEventTimelines. A developer can open several - // timelines from a same room so that a message can be decrypted several times but from - // a different timeline. - // So, store these message indexes per timeline id. - // - // The first level keys are timeline ids. - // The second level keys are strings of form "||" - // Values are @(YES). - NSMutableDictionary *> *inboundGroupSessionMessageIndexes; -} - -// The store where crypto data is saved. -@property (nonatomic, readonly) id store; - -// Cache to avoid refetching unchanged sessions from the crypto store -@property (nonatomic, strong) MXLRUCache *inboundGroupSessionCache; - -@end - -@implementation MXOlmDevice -@synthesize store; - -- (instancetype)initWithStore:(id)theStore -{ - self = [super init]; - if (self) - { - store = theStore; - - // It is up to the app to provide an encryption key that it safely manages. - // If provided, this key will be used as a global pickle key for all olm pickes. - // Else, libolm will create pickle keys internally. - if ([MXKeyProvider.sharedInstance hasKeyForDataOfType:MXCryptoOlmPickleKeyDataType isMandatory:NO]) - { - MXLogDebug(@"[MXOlmDevice] initWithStore: Use a global pickle key for libolm"); - OLMKit.sharedInstance.pickleKeyDelegate = self; - } - - // Retrieve the account from the store - OLMAccount *olmAccount = store.account; - if (!olmAccount) - { - MXLogDebug(@"[MXOlmDevice] initWithStore: Create new OLMAccount"); - - // Else, create it - // create a OLM account - olmAccount = [[OLMAccount alloc] initNewAccount]; - - [store setAccount:olmAccount]; - } - else - { - MXLogDebug(@"[MXOlmDevice] initWithStore: Reuse OLMAccount from store"); - } - - olmUtility = [[OLMUtility alloc] init]; - - inboundGroupSessionMessageIndexes = [NSMutableDictionary dictionary]; - - _deviceCurve25519Key = olmAccount.identityKeys[kMXKeyCurve25519Type]; - _deviceEd25519Key = olmAccount.identityKeys[kMXKeyEd25519Type]; - - _inboundGroupSessionCache = [[MXLRUCache alloc] initWithCapacity:kMXInboundGroupSessionCacheSize]; - } - return self; -} - -- (NSString *)olmVersion -{ - return [OLMKit versionString]; -} - -- (NSString *)signMessage:(NSData*)message -{ - return [store.account signMessage:message]; -} - -- (NSString *)signJSON:(NSDictionary *)JSONDictinary -{ - return [self signMessage:[MXCryptoTools canonicalJSONDataForJSON:JSONDictinary]]; -} - -- (NSDictionary *)oneTimeKeys -{ - return store.account.oneTimeKeys; -} - -- (NSUInteger)maxNumberOfOneTimeKeys -{ - return store.account.maxOneTimeKeys; -} - -- (void)markOneTimeKeysAsPublished -{ - [store performAccountOperationWithBlock:^(OLMAccount *olmAccount) { - [olmAccount markOneTimeKeysAsPublished]; - }]; -} - -- (void)generateOneTimeKeys:(NSUInteger)numKeys -{ - [store performAccountOperationWithBlock:^(OLMAccount *olmAccount) { - [olmAccount generateOneTimeKeys:numKeys]; - }]; -} - -- (NSDictionary *)fallbackKey -{ - return store.account.fallbackKey; -} - -- (void)generateFallbackKey -{ - [store performAccountOperationWithBlock:^(OLMAccount *olmAccount) { - [olmAccount generateFallbackKey]; - }]; -} - -- (NSString *)createOutboundSession:(NSString *)theirIdentityKey theirOneTimeKey:(NSString *)theirOneTimeKey -{ - NSError *error; - - MXLogDebug(@"[MXOlmDevice] createOutboundSession: theirIdentityKey: %@. theirOneTimeKey: %@", theirIdentityKey, theirOneTimeKey); - - OLMSession *olmSession = [[OLMSession alloc] initOutboundSessionWithAccount:store.account theirIdentityKey:theirIdentityKey theirOneTimeKey:theirOneTimeKey error:&error]; - - MXLogDebug(@"[MXOlmDevice] createOutboundSession: Olm Session id: %@", olmSession.sessionIdentifier); - - if (olmSession) - { - MXOlmSession *mxOlmSession = [[MXOlmSession alloc] initWithOlmSession:olmSession deviceKey:theirIdentityKey]; - - // Pretend we've received a message at this point, otherwise - // if we try to send a message to the device, it won't use - // this session - [mxOlmSession didReceiveMessage]; - - [store storeSession:mxOlmSession]; - return olmSession.sessionIdentifier; - } - else if (error) - { - MXLogDebug(@"[MXOlmDevice] createOutboundSession. Error: %@", error); - } - - return nil; -} - -- (NSString*)createInboundSession:(NSString*)theirDeviceIdentityKey messageType:(NSUInteger)messageType cipherText:(NSString*)ciphertext payload:(NSString**)payload -{ - MXLogDebug(@"[MXOlmDevice] createInboundSession: theirIdentityKey: %@", theirDeviceIdentityKey); - - __block OLMSession *olmSession; - - [store performAccountOperationWithBlock:^(OLMAccount *olmAccount) { - NSError *error; - olmSession = [[OLMSession alloc] initInboundSessionWithAccount:olmAccount theirIdentityKey:theirDeviceIdentityKey oneTimeKeyMessage:ciphertext error:&error]; - - MXLogDebug(@"[MXOlmDevice] createInboundSession: Olm Session id: %@", olmSession.sessionIdentifier); - - if (olmSession) - { - [olmAccount removeOneTimeKeysForSession:olmSession]; - } - else if (error) - { - MXLogDebug(@"[MXOlmDevice] createInboundSession. Error: %@", error); - } - }]; - - if (olmSession) - { - NSError *error; - *payload = [olmSession decryptMessage:[[OLMMessage alloc] initWithCiphertext:ciphertext type:messageType] error:&error]; - if (error) - { - MXLogDebug(@"[MXOlmDevice] createInboundSession. decryptMessage error: %@", error); - } - - MXOlmSession *mxOlmSession = [[MXOlmSession alloc] initWithOlmSession:olmSession deviceKey:theirDeviceIdentityKey]; - - // This counts as a received message: set last received message time - // to now - [mxOlmSession didReceiveMessage]; - - [store storeSession:mxOlmSession]; - } - - return olmSession.sessionIdentifier; -} - -- (NSArray *)sessionIdsForDevice:(NSString *)theirDeviceIdentityKey -{ - NSArray *sessions = [store sessionsWithDevice:theirDeviceIdentityKey]; - - NSMutableArray *sessionIds = [NSMutableArray arrayWithCapacity:sessions.count]; - for (MXOlmSession *session in sessions) - { - if (session.session.sessionIdentifier) - { - [sessionIds addObject:session.session.sessionIdentifier]; - } - } - - return sessionIds; -} - -- (NSString *)sessionIdForDevice:(NSString *)theirDeviceIdentityKey -{ - // Use the session that has most recently received a message - // This is the first item in the sorted array returned by the store - return [store sessionsWithDevice:theirDeviceIdentityKey].firstObject.session.sessionIdentifier; -} - -- (NSDictionary *)encryptMessage:(NSString *)theirDeviceIdentityKey sessionId:(NSString *)sessionId payloadString:(NSString *)payloadString -{ - __block OLMMessage *olmMessage; - - MXLogDebug(@"[MXOlmDevice] encryptMessage: Olm Session id %@ to %@", sessionId, theirDeviceIdentityKey); - - [store performSessionOperationWithDevice:theirDeviceIdentityKey andSessionId:sessionId block:^(MXOlmSession *mxOlmSession) { - - if (mxOlmSession.session) - { - NSError *error; - olmMessage = [mxOlmSession.session encryptMessage:payloadString error:&error]; - - if (error) - { - MXLogDebug(@"[MXOlmDevice] encryptMessage failed for session id %@ and sender %@: %@", sessionId, theirDeviceIdentityKey, error); - } - } - }]; - - return @{ - kMXMessageBodyKey: olmMessage.ciphertext, - @"type": @(olmMessage.type) - }; -} - -- (NSString*)decryptMessage:(NSString*)ciphertext withType:(NSUInteger)messageType sessionId:(NSString*)sessionId theirDeviceIdentityKey:(NSString*)theirDeviceIdentityKey -{ - __block NSString *payloadString; - - MXLogDebug(@"[MXOlmDevice] decryptMessage: Olm Session id %@(%@) from %@" ,sessionId, @(messageType), theirDeviceIdentityKey); - - [store performSessionOperationWithDevice:theirDeviceIdentityKey andSessionId:sessionId block:^(MXOlmSession *mxOlmSession) { - if (mxOlmSession) - { - NSError *error; - payloadString = [mxOlmSession.session decryptMessage:[[OLMMessage alloc] initWithCiphertext:ciphertext type:messageType] error:&error]; - - if (error) - { - MXLogDebug(@"[MXOlmDevice] decryptMessage. Error: %@", error); - } - - [mxOlmSession didReceiveMessage]; - } - }]; - - return payloadString; -} - -- (BOOL)matchesSession:(NSString *)theirDeviceIdentityKey sessionId:(NSString *)sessionId messageType:(NSUInteger)messageType ciphertext:(NSString *)ciphertext -{ - if (messageType != 0) - { - return NO; - } - - MXOlmSession *mxOlmSession = [store sessionWithDevice:theirDeviceIdentityKey andSessionId:sessionId]; - return [mxOlmSession.session matchesInboundSession:ciphertext]; -} - - -#pragma mark - Outbound group session - -- (MXOlmOutboundGroupSession *)createOutboundGroupSessionForRoomWithRoomId:(NSString *)roomId -{ - OLMOutboundGroupSession *session = [[OLMOutboundGroupSession alloc] initOutboundGroupSession]; - return [store storeOutboundGroupSession:session withRoomId:roomId]; -} - -- (void)storeOutboundGroupSession:(MXOlmOutboundGroupSession *)session -{ - MXLogDebug(@"[MXOlmDevice] storing Outbound Group Session For Room With ID %@", session.roomId); - [store storeOutboundGroupSession:session.session withRoomId:session.roomId]; -} - -- (MXOlmOutboundGroupSession *)outboundGroupSessionForRoomWithRoomId:(NSString *)roomId -{ - return [store outboundGroupSessionWithRoomId:roomId]; -} - -- (void)discardOutboundGroupSessionForRoomWithRoomId:(NSString *)roomId -{ - [store removeOutboundGroupSessionWithRoomId:roomId]; -} - - -#pragma mark - Inbound group session -- (BOOL)addInboundGroupSession:(NSString*)sessionId - sessionKey:(NSString*)sessionKey - roomId:(NSString*)roomId - senderKey:(NSString*)senderKey - forwardingCurve25519KeyChain:(NSArray *)forwardingCurve25519KeyChain - keysClaimed:(NSDictionary*)keysClaimed - exportFormat:(BOOL)exportFormat - sharedHistory:(BOOL)sharedHistory - untrusted:(BOOL)untrusted -{ - MXOlmInboundGroupSession *session; - if (exportFormat) - { - session = [[MXOlmInboundGroupSession alloc] initWithImportedSessionKey:sessionKey]; - } - else - { - session = [[MXOlmInboundGroupSession alloc] initWithSessionKey:sessionKey]; - } - - MXOlmInboundGroupSession *existingSession = [store inboundGroupSessionWithId:sessionId andSenderKey:senderKey]; - if ([self checkInboundGroupSession:existingSession roomId:roomId]) - { - existingSession = nil; - } - - if (existingSession) - { - // If we already have this session, consider updating it - MXLogDebug(@"[MXOlmDevice] addInboundGroupSession: Considering updates for megolm session %@|%@", senderKey, sessionId); - - BOOL isExistingSessionBetter = existingSession.session.firstKnownIndex <= session.session.firstKnownIndex; - if (isExistingSessionBetter) - { - BOOL isNewSessionSafer = existingSession.isUntrusted && !session.isUntrusted; - if (!isNewSessionSafer) - { - MXLogDebug(@"[MXOlmDevice] addInboundGroupSession: Skip it. The index of the incoming session is higher (%@ vs %@)", @(session.session.firstKnownIndex), @(existingSession.session.firstKnownIndex)); - return NO; - } - - if ([self connectsSession1:existingSession session2:session]) - { - MXLogDebug(@"[MXOlmDevice] addInboundGroupSession: Skipping new session, and upgrading the safety of existing session"); - [self upgradeSafetyForSession:existingSession]; - return NO; - } - else - { - MXLogWarning(@"[MXOlmDevice] addInboundGroupSession: Received a safer but disconnected key, which will override the existing unsafe key"); - existingSession = nil; - } - } - } - - MXLogDebug(@"[MXOlmDevice] addInboundGroupSession: Add megolm session %@|%@ (import: %@)", senderKey, sessionId, exportFormat ? @"YES" : @"NO"); - - if (![session.session.sessionIdentifier isEqualToString:sessionId]) - { - MXLogDebug(@"[MXOlmDevice] addInboundGroupSession: ERROR: Mismatched group session ID from senderKey: %@", senderKey); - return NO; - } - - session.senderKey = senderKey; - session.roomId = roomId; - session.keysClaimed = keysClaimed; - session.forwardingCurve25519KeyChain = forwardingCurve25519KeyChain; - session.untrusted = untrusted; - - // If we already have a session stored, the sharedHistory flag will not be overwritten - if (!existingSession && MXSDKOptions.sharedInstance.enableRoomSharedHistoryOnInvite) - { - session.sharedHistory = sharedHistory; - } - - [self storeInboundGroupSessions:@[session]]; - - return YES; -} - -- (void)upgradeSafetyForSession:(MXOlmInboundGroupSession *)session -{ - [self.store performSessionOperationWithGroupSessionWithId:session.session.sessionIdentifier senderKey:session.senderKey block:^(MXOlmInboundGroupSession *inboundGroupSession) { - inboundGroupSession.untrusted = NO; - }]; - @synchronized (self.inboundGroupSessionCache) - { - session.untrusted = NO; - [self.inboundGroupSessionCache put:session.session.sessionIdentifier object:session]; - } -} - -- (BOOL)connectsSession1:(MXOlmInboundGroupSession *)session1 session2:(MXOlmInboundGroupSession *)session2 -{ - // `connects` function will be moved to libolm in the future to avoid having to export the session - NSUInteger lowestCommonIndex = MAX(session1.session.firstKnownIndex, session2.session.firstKnownIndex); - MXMegolmSessionData *export1 = [session1 exportSessionDataAtMessageIndex:lowestCommonIndex]; - MXMegolmSessionData *export2 = [session2 exportSessionDataAtMessageIndex:lowestCommonIndex]; - return [export1.sessionKey isEqualToString:export2.sessionKey]; -} - -- (NSArray*)importInboundGroupSessions:(NSArray*)inboundGroupSessionsData; -{ - NSMutableArray *sessions = [NSMutableArray arrayWithCapacity:inboundGroupSessionsData.count]; - - for (MXMegolmSessionData *sessionData in inboundGroupSessionsData) - { - if (!sessionData.roomId || !sessionData.algorithm) - { - MXLogDebug(@"[MXOlmDevice] importInboundGroupSessions: ignoring session entry with missing fields: %@", sessionData); - continue; - } - - MXOlmInboundGroupSession *session = [[MXOlmInboundGroupSession alloc] initWithImportedSessionData:sessionData]; - - MXOlmInboundGroupSession *existingSession = [store inboundGroupSessionWithId:sessionData.sessionId andSenderKey:sessionData.senderKey]; - if ([self checkInboundGroupSession:existingSession roomId:sessionData.roomId]) - { - existingSession = nil; - } - - if (existingSession) - { - // If we already have this session, consider updating it - MXLogDebug(@"[MXOlmDevice] importInboundGroupSessions: Update for megolm session %@|%@", sessionData.senderKey, sessionData.sessionId); - - // If our existing session is better, we keep it - if (existingSession.session.firstKnownIndex <= session.session.firstKnownIndex) - { - MXLogDebug(@"[MXOlmDevice] importInboundGroupSessions: Skip it. The index of the incoming session is higher (%@ vs %@)", @(session.session.firstKnownIndex), @(existingSession.session.firstKnownIndex)); - continue; - } - - if (existingSession.sharedHistory != session.sharedHistory) - { - MXLogDebug(@"[MXOlmDevice] importInboundGroupSessions: Existing value of sharedHistory = %d is not allowed to be overriden by the updated session", existingSession.sharedHistory); - session.sharedHistory = existingSession.sharedHistory; - } - } - - [sessions addObject:session]; - } - - [self storeInboundGroupSessions:sessions]; - - return sessions; -} - -- (MXDecryptionResult *)decryptGroupMessage:(NSString *)body - isEditEvent:(BOOL)isEditEvent - roomId:(NSString *)roomId - inTimeline:(NSString *)timeline - sessionId:(NSString *)sessionId - senderKey:(NSString *)senderKey - error:(NSError *__autoreleasing *)error -{ - __block NSUInteger messageIndex; - __block NSString *payloadString; - __block NSDictionary *keysClaimed; - __block NSArray *forwardingCurve25519KeyChain; - __block BOOL untrusted; - - MXDecryptionResult *result; - - [self performGroupSessionOperationWithSessionId:sessionId senderKey:senderKey block:^(MXOlmInboundGroupSession *session) { - - *error = [self checkInboundGroupSession:session roomId:roomId]; - if (*error) - { - MXLogDebug(@"[MXOlmDevice] decryptGroupMessage: Cannot decrypt in room %@ with session %@|%@. Error: %@", roomId, senderKey, sessionId, *error); - session = nil; - } - else - { - payloadString = [session.session decryptMessage:body messageIndex:&messageIndex error:error]; - keysClaimed = session.keysClaimed; - forwardingCurve25519KeyChain = session.forwardingCurve25519KeyChain; - untrusted = session.isUntrusted; - } - - }]; - - if (payloadString) - { - // Check if we have seen this message index before to detect replay attacks. - if (timeline) - { - if (!inboundGroupSessionMessageIndexes[timeline]) - { - inboundGroupSessionMessageIndexes[timeline] = [NSMutableDictionary dictionary]; - } - - NSString *messageIndexKey = [NSString stringWithFormat:@"%@|%@|%tu|%d", senderKey, sessionId, messageIndex, isEditEvent]; - if (inboundGroupSessionMessageIndexes[timeline][messageIndexKey]) - { - MXLogDebug(@"[MXOlmDevice] decryptGroupMessage: Warning: Possible replay attack %@", messageIndexKey); - - if (error) - { - *error = [NSError errorWithDomain:MXDecryptingErrorDomain - code:MXDecryptingErrorDuplicateMessageIndexCode - userInfo:@{ - NSLocalizedDescriptionKey: [NSString stringWithFormat:MXDecryptingErrorDuplicateMessageIndexReason, messageIndexKey] - }]; - } - - return nil; - } - - inboundGroupSessionMessageIndexes[timeline][messageIndexKey] = @(YES); - } - - result = [[MXDecryptionResult alloc] init]; - result.payload = [NSJSONSerialization JSONObjectWithData:[payloadString dataUsingEncoding:NSUTF8StringEncoding] options:0 error:nil]; - result.keysClaimed = keysClaimed; - result.senderKey = senderKey; - result.forwardingCurve25519KeyChain = forwardingCurve25519KeyChain; - result.untrusted = untrusted; - } - - return result; -} - -- (void)performGroupSessionOperationWithSessionId:(NSString*)sessionId senderKey:(NSString*)senderKey block:(void (^)(MXOlmInboundGroupSession *inboundGroupSession))block -{ - StopDurationTracking stopTracking = [MXSDKOptions.sharedInstance.analyticsDelegate startDurationTrackingForName:@"MXOlmDevice" operation:@"megolm.decrypt.cache"]; - @synchronized (self.inboundGroupSessionCache) - { - MXOlmInboundGroupSession *session = (MXOlmInboundGroupSession *)[self.inboundGroupSessionCache get:sessionId]; - if (!session) - { - session = [store inboundGroupSessionWithId:sessionId andSenderKey:senderKey]; - [self.inboundGroupSessionCache put:sessionId object:session]; - } - block(session); - } - if (stopTracking) - { - stopTracking(); - } -} - -- (void)resetReplayAttackCheckInTimeline:(NSString*)timeline -{ - [inboundGroupSessionMessageIndexes removeObjectForKey:timeline]; -} - -/** - Check an InboundGroupSession - - @paral session the session to check. - @param roomId the room where the sesion is used. - @return an error if there is an issue. - */ -- (NSError *)checkInboundGroupSession:(MXOlmInboundGroupSession *)session roomId:(NSString *)roomId -{ - NSError *error; - if (session) - { - // Check that the room id matches the original one for the session. This stops - // the HS pretending a message was targeting a different room. - if (![roomId isEqualToString:session.roomId]) - { - MXLogDebug(@"[MXOlmDevice] inboundGroupSessionWithId: ERROR: Mismatched room_id for inbound group session (expected %@, was %@)", roomId, session.roomId); - - NSString *errorDescription = [NSString stringWithFormat:MXDecryptingErrorInboundSessionMismatchRoomIdReason, roomId, session.roomId]; - - error = [NSError errorWithDomain:MXDecryptingErrorDomain - code:MXDecryptingErrorUnknownInboundSessionIdCode - userInfo:@{ - NSLocalizedDescriptionKey: errorDescription - }]; - } - } - else - { - error = [NSError errorWithDomain:MXDecryptingErrorDomain - code:MXDecryptingErrorUnknownInboundSessionIdCode - userInfo:@{ - NSLocalizedDescriptionKey: MXDecryptingErrorUnknownInboundSessionIdReason - }]; - } - return error; -} - -- (BOOL)hasInboundSessionKeys:(NSString*)roomId senderKey:(NSString*)senderKey sessionId:(NSString*)sessionId -{ - MXOlmInboundGroupSession *session = [store inboundGroupSessionWithId:sessionId andSenderKey:senderKey]; - - if (!session) - { - return NO; - } - - if (![session.roomId isEqualToString:roomId]) - { - MXLogDebug(@"[MXOlmDevice] hasInboundSessionKeys: requested keys for inbound group session %@|%@`, with incorrect room_id (expected %@, was %@)", senderKey, sessionId, session.roomId, roomId); - - return NO; - } - - return YES; -} - -- (NSDictionary*)getInboundGroupSessionKey:(NSString*)roomId senderKey:(NSString*)senderKey sessionId:(NSString*)sessionId chainIndex:(NSNumber*)chainIndex -{ - NSDictionary *inboundGroupSessionKey; - - MXOlmInboundGroupSession *session = [store inboundGroupSessionWithId:sessionId andSenderKey:senderKey]; - NSError *error = [self checkInboundGroupSession:session roomId:roomId]; - if (error) - { - MXLogDebug(@"[MXOlmDevice] getInboundGroupSessionKey in room %@ with session %@|%@. Error: %@", roomId, senderKey, sessionId, error); - session = nil; - } - - if (session) - { - NSNumber *messageIndex = chainIndex; - if (!messageIndex) - { - messageIndex = @(session.session.firstKnownIndex); - } - - NSDictionary *claimedKeys = session.keysClaimed; - NSString *senderEd25519Key = claimedKeys[@"ed25519"]; - - MXMegolmSessionData *sessionData = [session exportSessionDataAtMessageIndex:[messageIndex unsignedIntegerValue]]; - NSArray *forwardingCurve25519KeyChain = sessionData.forwardingCurve25519KeyChain; - BOOL sharedHistory = MXSDKOptions.sharedInstance.enableRoomSharedHistoryOnInvite && sessionData.sharedHistory; - - inboundGroupSessionKey = @{ - @"chain_index": messageIndex, - @"key": sessionData.sessionKey, - @"forwarding_curve25519_key_chain": forwardingCurve25519KeyChain ? forwardingCurve25519KeyChain : @[], - @"sender_claimed_ed25519_key": senderEd25519Key ? senderEd25519Key : [NSNull null], - @"shared_history": @(sharedHistory) - }; - } - - return inboundGroupSessionKey; -} - -- (void)storeInboundGroupSessions:(NSArray *)sessions -{ - [store storeInboundGroupSessions:sessions]; - @synchronized (self.inboundGroupSessionCache) - { - for (MXOlmInboundGroupSession *session in sessions) - { - [self.inboundGroupSessionCache put:session.session.sessionIdentifier object:session]; - } - } -} - - -#pragma mark - OLMKitPickleKeyDelegate - -- (NSData *)pickleKey -{ - // If this delegate is called, we must have a key to provide - MXKeyData *keyData = [[MXKeyProvider sharedInstance] keyDataForDataOfType:MXCryptoOlmPickleKeyDataType isMandatory:YES expectedKeyType:kRawData]; - if (keyData && [keyData isKindOfClass:[MXRawDataKey class]]) - { - return ((MXRawDataKey *)keyData).key; - } - - return nil; -} - - -#pragma mark - Utilities -- (BOOL)verifySignature:(NSString *)key message:(NSString *)message signature:(NSString *)signature error:(NSError *__autoreleasing *)error -{ - return [olmUtility verifyEd25519Signature:signature key:key message:[message dataUsingEncoding:NSUTF8StringEncoding] error:error]; -} - -- (BOOL)verifySignature:(NSString *)key JSON:(NSDictionary *)JSONDictinary signature:(NSString *)signature error:(NSError *__autoreleasing *)error -{ - return [olmUtility verifyEd25519Signature:signature key:key message:[MXCryptoTools canonicalJSONDataForJSON:JSONDictinary] error:error]; -} - -- (NSString *)sha256:(NSString *)message -{ - return [olmUtility sha256:[message dataUsingEncoding:NSUTF8StringEncoding]]; -} - -@end - -#endif diff --git a/MatrixSDK/Crypto/Migration/Data/MXCryptoMigrationStore.swift b/MatrixSDK/Crypto/Migration/Data/MXCryptoMigrationStore.swift deleted file mode 100644 index f06a64ef81..0000000000 --- a/MatrixSDK/Crypto/Migration/Data/MXCryptoMigrationStore.swift +++ /dev/null @@ -1,234 +0,0 @@ -// -// 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 -import OLMKit -import MatrixSDKCrypto - -struct MXCryptoMigrationStore { - struct GlobalSettings { - let onlyAllowTrustedDevices: Bool - } - - enum Error: Swift.Error { - case missingAccount - } - - let legacyStore: MXCryptoStore - - var userId: String? { - legacyStore.userId() - } - - var deviceId: String? { - legacyStore.deviceId() - } - - var olmSessionCount: UInt { - legacyStore.sessionsCount() - } - - var megolmSessionCount: UInt { - legacyStore.inboundGroupSessionsCount(false) - } - - var globalSettings: GlobalSettings { - .init(onlyAllowTrustedDevices: legacyStore.globalBlacklistUnverifiedDevices) - } - - func extractData(with pickleKey: Data) throws -> MigrationData { - return .init( - account: try pickledAccount(pickleKey: pickleKey), - sessions: [], // Sessions are extracted in batches separately - inboundGroupSessions: [], // Group sessions are extracted in batches separately - pickleKey: pickleKey, - backupVersion: legacyStore.backupVersion, - backupRecoveryKey: backupRecoveryKey(), - crossSigning: crossSigning(), - trackedUsers: trackedUsers(), - roomSettings: extractRoomSettings() - ) - } - - func extractSessions( - with pickleKey: Data, - batchSize: Int, - callback: @escaping ([PickledSession], Double) -> Void - ) { - legacyStore.enumerateSessions(by: batchSize) { sessions, progress in - let pickled: [PickledSession] = sessions?.compactMap { - do { - return try PickledSession(session: $0, pickleKey: pickleKey) - } catch { - MXLog.error("[MXCryptoMigrationStore] cannot extract olm session", context: error) - return nil - } - } ?? [] - callback(pickled, progress) - } - } - - func extractGroupSessions( - with pickleKey: Data, - batchSize: Int, - callback: @escaping ([PickledInboundGroupSession], Double) -> Void - ) { - legacyStore.enumerateInboundGroupSessions(by: batchSize) { sessions, backedUp, progress in - let pickled: [PickledInboundGroupSession] = sessions?.compactMap { - do { - return try PickledInboundGroupSession( - session: $0, - pickleKey: pickleKey, - backedUp: backedUp?.contains($0.session.sessionIdentifier()) == true - ) - } catch { - MXLog.error("[MXCryptoMigrationStore] cannot extract megolm session", context: error) - return nil - } - } ?? [] - callback(pickled, progress) - } - } - - func extractRoomSettings() -> [String: RoomSettings] { - return legacyStore - .roomSettings() - .reduce(into: [String: RoomSettings]()) { dict, item in - do { - let algorithm = try EventEncryptionAlgorithm(string: item.algorithm) - dict[item.roomId] = RoomSettings( - algorithm: algorithm, - onlyAllowTrustedDevices: item.blacklistUnverifiedDevices - ) - } catch { - MXLog.error("[MXCryptoMigrationStore] cannot extract room settings", context: error) - } - } - } - - // MARK: - Private - - private func pickledAccount(pickleKey: Data) throws -> PickledAccount { - guard - let userId = legacyStore.userId(), - let deviceId = legacyStore.deviceId(), - let account = legacyStore.account() - else { - throw Error.missingAccount - } - return try PickledAccount( - userId: userId, - deviceId: deviceId, - account: account, - pickleKey: pickleKey - ) - } - - private func backupRecoveryKey() -> String? { - guard let privateKey = secret(for: MXSecretId.keyBackup) else { - return nil - } - - let data = MXBase64Tools.data(fromBase64: privateKey) - return MXRecoveryKey.encode(data) - } - - private func crossSigning() -> CrossSigningKeyExport { - let master = secret(for: MXSecretId.crossSigningMaster) - let selfSigning = secret(for: MXSecretId.crossSigningSelfSigning) - let userSigning = secret(for: MXSecretId.crossSigningUserSigning) - - return .init( - masterKey: master, - selfSigningKey: selfSigning, - userSigningKey: userSigning - ) - } - - private func trackedUsers() -> [String] { - var users = [String]() - for (user, status) in legacyStore.deviceTrackingStatus() ?? [:] { - if status != 0 { - users.append(user) - } - } - return users - } - - private func secret(for secretId: Unmanaged) -> String? { - return legacyStore.secret(withSecretId: secretId.takeUnretainedValue() as String) - } -} - -private extension PickledAccount { - init( - userId: String, - deviceId: String, - account: OLMAccount, - pickleKey: Data - ) throws { - let pickle = try account.serializeData(withKey: pickleKey) - self.init( - userId: userId, - deviceId: deviceId, - pickle: pickle, - shared: true, // Not yet implemented - uploadedSignedKeyCount: 50 // Not yet implemented - ) - } -} - -private extension PickledSession { - init(session: MXOlmSession, pickleKey: Data) throws { - let pickle = try session.session.serializeData(withKey: pickleKey) - let time = UInt64(session.lastReceivedMessageTs) - - self.init( - pickle: pickle, - senderKey: session.deviceKey, - createdUsingFallbackKey: false, // Not yet implemented - creationTime: time, // Not yet implemented - lastUseTime: time - ) - } -} - -private extension PickledInboundGroupSession { - enum Error: Swift.Error { - case invalidSession - } - - init(session: MXOlmInboundGroupSession, pickleKey: Data, backedUp: Bool) throws { - guard - let senderKey = session.senderKey, - let roomId = session.roomId - else { - throw Error.invalidSession - } - - let pickle = try session.session.serializeData(withKey: pickleKey) - - self.init( - pickle: pickle, - senderKey: senderKey, - signingKey: session.keysClaimed ?? [:], - roomId: roomId, - forwardingChains: session.forwardingCurve25519KeyChain ?? [], - imported: session.isUntrusted, - backedUp: backedUp - ) - } -} diff --git a/MatrixSDK/Crypto/Migration/MXCryptoMigration.h b/MatrixSDK/Crypto/Migration/MXCryptoMigration.h deleted file mode 100644 index 90325b021f..0000000000 --- a/MatrixSDK/Crypto/Migration/MXCryptoMigration.h +++ /dev/null @@ -1,59 +0,0 @@ -// -// Copyright 2020 The Matrix.org Foundation C.I.C -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#import - -@class MXLegacyCrypto; - -NS_ASSUME_NONNULL_BEGIN - - -FOUNDATION_EXPORT NSString *const MXCryptoMigrationErrorDomain; -typedef NS_ENUM(NSInteger, MXCryptoMigrationErrorCode) -{ - MXCryptoMigrationCannotPurgeAllOneTimeKeysErrorCode, -}; - - -/** - The `MXCryptoMigration` class handles the migration logic between breaking changes in the implementation - of the MXCrypto module. - It helps to update data between version (MXCryptoVersion) changes. - */ -@interface MXCryptoMigration : NSObject - -- (instancetype)initWithCrypto:(MXLegacyCrypto *)crypto; - -/** - Indicate if the data must be updated. - - @return YES if a migration should be done. - */ -- (BOOL)shouldMigrate; - -/** - Migrate the data to the latest version of the implementation (MXCryptoVersionLast). - - @param success A block object called when the operation succeeds. - @param failure A block object called when the operation fails. - */ -- (void)migrateWithSuccess:(void (^)(void))success - failure:(void (^)(NSError *error))failure; - - -@end - -NS_ASSUME_NONNULL_END diff --git a/MatrixSDK/Crypto/Migration/MXCryptoMigration.m b/MatrixSDK/Crypto/Migration/MXCryptoMigration.m deleted file mode 100644 index 0e3bf90fc6..0000000000 --- a/MatrixSDK/Crypto/Migration/MXCryptoMigration.m +++ /dev/null @@ -1,213 +0,0 @@ -// -// Copyright 2020 The Matrix.org Foundation C.I.C -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#import "MXCryptoMigration.h" - -#import "MXCrypto_Private.h" -#import "MXKey.h" -#import "MXTools.h" - - -#pragma mark - Constants definitions - -NSString *const MXCryptoMigrationErrorDomain = @"org.matrix.sdk.crypto.migration"; - -// The number of keys to purge (claim) in a batch -NSUInteger const kMXCryptoMigrationKeyPurgeStepCount = 5; - -// In case of error, time to wait before processing the next purge batch -NSTimeInterval const kMXCryptoMigrationKeyPurgeBatchPeriod = 0.5; - -// Number of times we can retry if API requests fail -NSUInteger const kMXCryptoMigrationKeyPurgeRetryCountLimit = 10; - - -@interface MXCryptoMigration () -{ - __weak MXLegacyCrypto *crypto; - NSUInteger keyPurgeRetryCount; -} - -@end - - -@implementation MXCryptoMigration - -- (instancetype)initWithCrypto:(MXLegacyCrypto *)theCrypto -{ - self = [self init]; - if (self) - { - crypto = theCrypto; - keyPurgeRetryCount = 0; - } - return self; -} - -- (BOOL)shouldMigrate -{ - MXCryptoVersion lastUsedCryptoVersion = crypto.store.cryptoVersion; - BOOL shouldMigrate = lastUsedCryptoVersion < MXCryptoVersionLast; - - if (shouldMigrate) - { - MXLogDebug(@"[MXCryptoMigration] shouldMigrate: YES from version %@ to %@", @(lastUsedCryptoVersion), @(MXCryptoVersionLast)); - } - else - { - MXLogDebug(@"[MXCryptoMigration] shouldMigrate: NO"); - } - - return shouldMigrate; -} - -- (void)migrateWithSuccess:(void (^)(void))success failure:(void (^)(NSError * _Nonnull))failure -{ - MXCryptoVersion lastUsedCryptoVersion = crypto.store.cryptoVersion; - MXLogDebug(@"[MXCryptoMigration] migrate from version %@", @(lastUsedCryptoVersion)); - - switch (lastUsedCryptoVersion) - { - case MXCryptoVersion1: - [self migrateToCryptoVersion2:success failure:failure]; - break; - - default: - MXLogDebug(@"[MXCryptoMigration] migrate. Error: Unsupported migration"); - break; - } -} - - -#pragma mark - Private methods - -- (void)migrateToCryptoVersion2:(void (^)(void))success failure:(void (^)(NSError *))failure -{ - MXLogDebug(@"[MXCryptoMigration] migrateToCryptoVersion2: start"); - - // 1- Remove all one time keys already published on the server because some can be bad - // https://github.com/vector-im/element-ios/issues/3818 - MXWeakify(self); - [self purgePublishedOneTimeKeys:^{ - MXStrongifyAndReturnIfNil(self); - - // 2- Reset any pending local one time keys to start from a fresh set of OTKs - [self->crypto.olmDevice markOneTimeKeysAsPublished]; - - // 3- Upload fresh and valid OTKs - [self->crypto generateAndUploadOneTimeKeys:0 retry:YES success:^{ - - // Migration is done - MXLogDebug(@"[MXCryptoMigration] migrateToCryptoVersion2: completed"); - self->crypto.store.cryptoVersion = MXCryptoVersion2; - - success(); - - } failure:^(NSError *error) { - MXLogDebug(@"[MXCryptoMigration] migrateToCryptoVersion2: generateAndUploadOneTimeKeys failed. Error: %@", error); - failure(error); - }]; - - } failure:^(NSError *error) { - MXLogDebug(@"[MXCryptoMigration] migrateToCryptoVersion2: purgePublishedOneTimeKeys failed. Error: %@", error); - failure(error); - }]; -} - -// Purge one time keys uploaded by this device. We purge them by claiming them. -// -// A one time key can be used and claimed only once. Claiming one time key removes it from -// the published list of OTKs. -- (void)purgePublishedOneTimeKeys:(void (^)(void))success failure:(void (^)(NSError *))failure -{ - MXLogDebug(@"[MXCryptoMigration] purgePublishedOneTimeKeys"); - [crypto publishedOneTimeKeysCount:^(NSUInteger publishedKeyCount) { - - // Purge/Claim keys by batch - NSUInteger keysToClaim = MIN(kMXCryptoMigrationKeyPurgeStepCount, publishedKeyCount); - - [self claimOwnOneTimeKeys:keysToClaim success:^(NSUInteger keyClaimed) { - - MXLogDebug(@"[MXCryptoMigration] purgePublishedOneTimeKeys: %@ out of %@ purged", @(keyClaimed), @(publishedKeyCount)); - - if (keyClaimed == publishedKeyCount) - { - MXLogDebug(@"[MXCryptoMigration] purgePublishedOneTimeKeys: completed"); - success(); - } - else if (keyClaimed < keysToClaim) - { - if (self->keyPurgeRetryCount++ < kMXCryptoMigrationKeyPurgeRetryCountLimit) - { - MXLogDebug(@"[MXCryptoMigration] purgePublishedOneTimeKeys: Delay the next batch because this batch was not 100%% successful"); - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, kMXCryptoMigrationKeyPurgeBatchPeriod * NSEC_PER_SEC), dispatch_get_main_queue(), ^{ - [self purgePublishedOneTimeKeys:success failure:failure]; - }); - } - else - { - MXLogDebug(@"[MXCryptoMigration] purgePublishedOneTimeKeys: Failed to purge all one time keys after %@ tries. Give up.", @(self->keyPurgeRetryCount)); - NSError *error = [NSError errorWithDomain:MXCryptoMigrationErrorDomain - code:MXCryptoMigrationCannotPurgeAllOneTimeKeysErrorCode - userInfo:@{ - NSLocalizedDescriptionKey: @"Failed to purge all one time keys", - }]; - failure(error); - - } - } - else - { - // Purge the next batch - [self purgePublishedOneTimeKeys:success failure:failure]; - } - - } failure:failure]; - - } failure:failure]; -} - -- (void)claimOwnOneTimeKeys:(NSUInteger)keyCount success:(void (^)(NSUInteger keyClaimed))success failure:(void (^)(NSError *))failure -{ - MXLogDebug(@"[MXCryptoMigration] claimOwnOneTimeKeys: %@", @(keyCount)); - - MXUsersDevicesMap *usersDevicesToClaim = [MXUsersDevicesMap new]; - [usersDevicesToClaim setObject:kMXKeySignedCurve25519Type forUser:crypto.mxSession.myUserId andDevice:crypto.mxSession.myDeviceId]; - - dispatch_group_t group = dispatch_group_create(); - - __block NSUInteger keyClaimed = 0; - for (NSUInteger i = 0; i < keyCount; i++) - { - dispatch_group_enter(group); - [crypto.matrixRestClient claimOneTimeKeysForUsersDevices:usersDevicesToClaim success:^(MXKeysClaimResponse *keysClaimResponse) { - - keyClaimed++; - dispatch_group_leave(group); - - } failure:^(NSError *error) { - MXLogDebug(@"[MXCryptoMigration] claimOwnOneTimeKeys: claimOneTimeKeysForUsersDevices failed. Error: %@", error); - dispatch_group_leave(group); - }]; - } - - dispatch_group_notify(group, crypto.cryptoQueue, ^{ - MXLogDebug(@"[MXCryptoMigration] claimOwnOneTimeKeys: Successful claimed %@ (requested: %@) one time keys", @(keyClaimed), @(keyCount)); - success(keyClaimed); - }); -} - -@end diff --git a/MatrixSDK/Crypto/Migration/MXCryptoMigrationV2.swift b/MatrixSDK/Crypto/Migration/MXCryptoMigrationV2.swift deleted file mode 100644 index 4368ad68ea..0000000000 --- a/MatrixSDK/Crypto/Migration/MXCryptoMigrationV2.swift +++ /dev/null @@ -1,222 +0,0 @@ -// -// Copyright 2022 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 -import OLMKit -import MatrixSDKCrypto - -class MXCryptoMigrationV2: NSObject { - enum Error: Swift.Error { - case missingCredentials - case unknownPickleKey - } - - private static let SessionBatchSize = 1000 - - private let legacyDevice: MXOlmDevice - private let store: MXCryptoMigrationStore - private let log = MXNamedLog(name: "MXCryptoMachineMigration") - - init(legacyStore: MXCryptoStore) { - MXCryptoSDKLogger.shared.log(logLine: "Starting logs") - - // We need to create legacy OlmDevice which sets itself internally as pickle key delegate - // Once established we can get the pickleKey from OLMKit which is used to decrypt and migrate - // the legacy store data - legacyDevice = MXOlmDevice(store: legacyStore) - store = .init(legacyStore: legacyStore) - } - - func migrateAllData(updateProgress: @escaping (Double) -> Void) throws { - log.debug("Starting migration") - - let startDate = Date() - updateProgress(0) - - let pickleKey = try legacyPickleKey() - let data = try store.extractData(with: pickleKey) - - let url = try MXCryptoMachineStore.storeURL(for: data.account.userId) - let passphrase = try MXCryptoMachineStore.storePassphrase() - - if FileManager.default.fileExists(atPath: url.path) { - try FileManager.default.removeItem(at: url) - } - - let details = """ - Migration summary - - user id : \(data.account.userId) - - device id : \(data.account.deviceId) - - olm_sessions : \(store.olmSessionCount) - - megolm_sessions : \(store.megolmSessionCount) - - backup_key : \(data.backupRecoveryKey != nil ? "true" : "false") - - master_key : \(data.crossSigning.masterKey != nil ? "true" : "false") - - user_signing_key : \(data.crossSigning.userSigningKey != nil ? "true" : "false") - - self_signing_key : \(data.crossSigning.selfSigningKey != nil ? "true" : "false") - - tracked_users : \(data.trackedUsers.count) - - room_settings : \(data.roomSettings.count) - - global_settings : \(store.globalSettings) - """ - log.debug(details) - - try migrate( - data: data, - path: url.path, - passphrase: passphrase, - progressListener: self - ) - - log.debug("Migrating olm sessions in batches") - - // How much does migration of olm vs megolm sessions contribute to the overall progress - let totalSessions = store.olmSessionCount + store.megolmSessionCount - let olmToMegolmRatio = totalSessions > 0 ? Double(store.olmSessionCount)/Double(totalSessions) : 0 - - store.extractSessions(with: pickleKey, batchSize: Self.SessionBatchSize) { [weak self] batch, progress in - updateProgress(progress * olmToMegolmRatio) - - do { - try self?.migrateSessionsBatch( - data: data, - sessions: batch, - url: url, - passphrase: passphrase - ) - } catch { - self?.log.error("Error migrating some sessions", context: error) - } - } - - log.debug("Migrating megolm sessions in batches") - - store.extractGroupSessions(with: pickleKey, batchSize: Self.SessionBatchSize) { [weak self] batch, progress in - updateProgress(olmToMegolmRatio + progress * (1 - olmToMegolmRatio)) - - do { - try self?.migrateSessionsBatch( - data: data, - inboundGroupSessions: batch, - url: url, - passphrase: passphrase - ) - } catch { - self?.log.error("Error migrating some sessions", context: error) - } - } - - log.debug("Migrating global settings") - try migrateGlobalSettings( - userId: data.account.userId, - deviceId: data.account.deviceId, - url: url, - passphrase: passphrase - ) - - let duration = Date().timeIntervalSince(startDate) * 1000 - log.debug("Migration completed in \(duration) ms") - updateProgress(1) - } - - func migrateRoomAndGlobalSettingsOnly(updateProgress: @escaping (Double) -> Void) throws { - guard let userId = store.userId, let deviceId = store.deviceId else { - throw Error.missingCredentials - } - - let url = try MXCryptoMachineStore.storeURL(for: userId) - let passphrase = try MXCryptoMachineStore.storePassphrase() - let settings = store.extractRoomSettings() - - let details = """ - Settings migration summary - - user id : \(userId) - - device id : \(deviceId) - - room_settings : \(settings.count) - - global_settings : \(store.globalSettings) - """ - log.debug(details) - updateProgress(0) - - try migrateRoomSettings( - roomSettings: settings, - path: url.path, - passphrase: passphrase - ) - - log.debug("Migrating global settings") - try migrateGlobalSettings( - userId: userId, - deviceId: deviceId, - url: url, - passphrase: passphrase - ) - - log.debug("Migration completed") - updateProgress(1) - } - - private func legacyPickleKey() throws -> Data { - guard let key = OLMKit.sharedInstance().pickleKeyDelegate?.pickleKey() else { - throw Error.unknownPickleKey - } - return key - } - - private func migrateSessionsBatch( - data: MigrationData, - sessions: [PickledSession] = [], - inboundGroupSessions: [PickledInboundGroupSession] = [], - url: URL, - passphrase: String - ) throws { - try migrateSessions( - data: .init( - userId: data.account.userId, - deviceId: data.account.deviceId, - curve25519Key: legacyDevice.deviceCurve25519Key, - ed25519Key: legacyDevice.deviceEd25519Key, - sessions: sessions, - inboundGroupSessions: inboundGroupSessions, - pickleKey: data.pickleKey - ), - path: url.path, - passphrase: passphrase, - progressListener: self - ) - } - - private func migrateGlobalSettings( - userId: String, - deviceId: String, - url: URL, - passphrase: String - ) throws { - let machine = try OlmMachine( - userId: userId, - deviceId: deviceId, - path: url.path, - passphrase: passphrase - ) - - let onlyTrusted = store.globalSettings.onlyAllowTrustedDevices - try machine.setOnlyAllowTrustedDevices(onlyAllowTrustedDevices: onlyTrusted) - } -} - -extension MXCryptoMigrationV2: ProgressListener { - func onProgress(progress: Int32, total: Int32) { - // Progress loggged manually - } -} diff --git a/MatrixSDK/Crypto/Recovery/MXRecoveryService.m b/MatrixSDK/Crypto/Recovery/MXRecoveryService.m index ff444a76cc..36caf25e25 100644 --- a/MatrixSDK/Crypto/Recovery/MXRecoveryService.m +++ b/MatrixSDK/Crypto/Recovery/MXRecoveryService.m @@ -15,17 +15,15 @@ */ #import "MXRecoveryService_Private.h" +#import "MXKeyBackup_Private.h" - -#import "MXCrypto_Private.h" -#import "MXCrossSigning_Private.h" #import "MXKeyBackupPassword.h" #import "MXRecoveryKey.h" #import "MXAesHmacSha2.h" #import "MXTools.h" #import "NSArray+MatrixSDK.h" #import "MatrixSDKSwiftHeader.h" - +#import "MXCrossSigningTools.h" #pragma mark - Constants @@ -556,26 +554,16 @@ - (void)recoverSecrets:(nullable NSArray*)secrets [self.dependencies.secretStorage secretWithSecretId:secretId withSecretStorageKeyId:secretStorageKeyId privateKey:privateKey success:^(NSString * _Nonnull unpaddedBase64Secret) { NSString *secret = unpaddedBase64Secret; - // Validate the secret before storing it - if ([self checkSecret:secret withSecretId:secretId]) + if (![secret isEqualToString:[self.dependencies.secretStore secretWithSecretId:secretId]]) { - if (![secret isEqualToString:[self.dependencies.secretStore secretWithSecretId:secretId]]) - { - MXLogDebug(@"[MXRecoveryService] recoverSecrets: Recovered secret %@", secretId); - - [updatedSecrets addObject:secretId]; - [self.dependencies.secretStore storeSecret:secret withSecretId:secretId]; - } - else - { - MXLogDebug(@"[MXRecoveryService] recoverSecrets: Secret %@ was already known", secretId); - } - } - else - { - MXLogDebug(@"[MXRecoveryService] recoverSecrets: Secret %@ is invalid", secretId); - [invalidSecrets addObject:secretId]; + MXLogDebug(@"[MXRecoveryService] recoverSecrets: Recovered secret %@", secretId); + + [updatedSecrets addObject:secretId]; + [self.dependencies.secretStore storeSecret:secret withSecretId:secretId errorHandler:^(NSError * _Nonnull anError) { + MXLogDebug(@"[MXRecoveryService] recoverSecrets: Secret %@ is invalid", secretId); + [invalidSecrets addObject:secretId]; + }]; } dispatch_group_leave(dispatchGroup); @@ -846,66 +834,6 @@ - (void)privateKeyFromPassphrase:(NSString*)passphrase #pragma mark - Private methods - -- (BOOL)checkSecret:(NSString*)secret withSecretId:(NSString*)secretId -{ - // Accept secrets by default - BOOL valid = YES; - MXCrossSigningTools *tools = [[MXCrossSigningTools alloc] init]; - - if ([secretId isEqualToString:MXSecretId.keyBackup]) - { - MXKeyBackupVersion *keyBackupVersion = self.dependencies.backup.keyBackupVersion; - if (keyBackupVersion) - { - valid = [self.dependencies.backup isSecretValid:secret forKeyBackupVersion:keyBackupVersion]; - } - else - { - MXLogDebug(@"[MXRecoveryService] checkSecret: Backup is not enabled yet. Accept the secret by default"); - } - } - else if ([secretId isEqualToString:MXSecretId.crossSigningMaster]) - { - MXCrossSigningInfo *crossSigningInfo = self.dependencies.crossSigning.myUserCrossSigningKeys; - if (crossSigningInfo) - { - valid = [tools isSecretValid:secret forPublicKeys:crossSigningInfo.masterKeys.keys]; - } - else - { - MXLogDebug(@"[MXRecoveryService] checkSecret: Cross-signing is not enabled yet. Accept the secret by default"); - } - } - else if ([secretId isEqualToString:MXSecretId.crossSigningSelfSigning]) - { - MXCrossSigningInfo *crossSigningInfo = self.dependencies.crossSigning.myUserCrossSigningKeys; - if (crossSigningInfo) - { - valid = [tools isSecretValid:secret forPublicKeys:crossSigningInfo.selfSignedKeys.keys]; - } - else - { - MXLogDebug(@"[MXRecoveryService] checkSecret: Cross-signing is not enabled yet. Accept the secret by default"); - } - } - else if ([secretId isEqualToString:MXSecretId.crossSigningUserSigning]) - { - MXCrossSigningInfo *crossSigningInfo = self.dependencies.crossSigning.myUserCrossSigningKeys; - if (crossSigningInfo) - { - valid = [tools isSecretValid:secret forPublicKeys:crossSigningInfo.userSignedKeys.keys]; - } - else - { - MXLogDebug(@"[MXRecoveryService] checkSecret: Cross-signing is not enabled yet. Accept the secret by default"); - } - } - - MXLogDebug(@"[MXRecoveryService] checkSecret: Secret for %@ is %@", secretId, valid ? @"valid" : @"invalid"); - - return valid; -} - // Try to convert an error from another module to meaningful error for this module - (NSError*)domainErrorFromError:(NSError*)error { diff --git a/MatrixSDK/Crypto/RoomKeys/MXRoomKeyInfoFactory.swift b/MatrixSDK/Crypto/RoomKeys/MXRoomKeyInfoFactory.swift deleted file mode 100644 index 315b81ee06..0000000000 --- a/MatrixSDK/Crypto/RoomKeys/MXRoomKeyInfoFactory.swift +++ /dev/null @@ -1,100 +0,0 @@ -// -// Copyright 2022 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 - -@objcMembers -public class MXRoomKeyInfoFactory: NSObject { - private let myUserId: String - private let store: MXCryptoStore - private let log = MXNamedLog(name: "MXRoomKeyFactory") - - public init(myUserId: String, store: MXCryptoStore) { - self.myUserId = myUserId - self.store = store - } - - public func roomKey(for event: MXEvent) -> MXRoomKeyResult? { - if event.eventType == .roomKey { - return roomKeyEventInfo(for: event) - } else if event.eventType == .roomForwardedKey { - return forwardedRoomKeyEventInfo(for: event) - } else { - log.error("Unknown event type", context: event.eventType) - return nil - } - } - - private func roomKeyEventInfo(for event: MXEvent) -> MXRoomKeyResult? { - guard - let content = MXRoomKeyEventContent(fromJSON: event.content), - let info = MXRoomKeyInfo(roomKey: content, event: event) - else { - log.error("Invalid room key") - return nil - } - - return .init(type: .safe, info: info) - } - - private func forwardedRoomKeyEventInfo(for event: MXEvent) -> MXRoomKeyResult? { - guard let eventSenderKey = event.senderKey else { - log.error("Unknown event sender") - return nil - } - - guard let content = MXForwardedRoomKeyEventContent(fromJSON: event.content) else { - log.error("Invalid forwarded key") - return nil - } - - content.forwardingCurve25519KeyChain += [eventSenderKey] - - return .init( - type: keyType(for: content, senderKey: eventSenderKey), - info: .init(forwardedRoomKey: content) - ) - } - - private func keyType(for content: MXForwardedRoomKeyEventContent, senderKey: String) -> MXRoomKeyType { - if !hasPendingRequest(for: content) { - log.debug("Key was not requested") - return .unrequested - } else if isMyVerifiedDevice(identityKey: senderKey) { - return .safe - } else { - log.debug("Key forward is not from my verified device") - return .unsafe - } - } - - private func isMyVerifiedDevice(identityKey: String) -> Bool { - guard let device = store.device(withIdentityKey: identityKey) else { - return false - } - return device.userId == myUserId && device.trustLevel.isVerified - } - - private func hasPendingRequest(for content: MXForwardedRoomKeyEventContent) -> Bool { - let request = store.outgoingRoomKeyRequest(withRequestBody: [ - "room_id": content.roomId, - "algorithm": content.algorithm, - "sender_key": content.senderKey, - "session_id": content.sessionId - ]) - return request != nil - } -} diff --git a/MatrixSDK/Crypto/SecretStorage/MXCryptoSecretStoreV2.swift b/MatrixSDK/Crypto/SecretStorage/MXCryptoSecretStoreV2.swift index 75fbff0e64..7e2e9774b4 100644 --- a/MatrixSDK/Crypto/SecretStorage/MXCryptoSecretStoreV2.swift +++ b/MatrixSDK/Crypto/SecretStorage/MXCryptoSecretStoreV2.swift @@ -15,11 +15,17 @@ // import Foundation +import MatrixSDKCrypto + +enum MXCryptoError: Error { + case secretDoesNotMatch +} /// Secret store compatible with Rust-based Crypto V2, where /// backup secrets are stored internally in the Crypto machine /// and others have to be managed manually. class MXCryptoSecretStoreV2: NSObject, MXCryptoSecretStore { + private let backup: MXKeyBackup? private let backupEngine: MXKeyBackupEngine? private let crossSigning: MXCryptoCrossSigning @@ -31,44 +37,55 @@ class MXCryptoSecretStoreV2: NSObject, MXCryptoSecretStore { self.crossSigning = crossSigning } - func storeSecret(_ secret: String, withSecretId secretId: String) { + func storeSecret(_ secret: String, withSecretId secretId: String, errorHandler: @escaping (Error) -> Void) { log.debug("Storing new secret \(secretId)") - - switch secretId as NSString { - case MXSecretId.crossSigningMaster.takeUnretainedValue(): - crossSigning.importCrossSigningKeys( - export: .init( - masterKey: secret, - selfSigningKey: nil, - userSigningKey: nil + do { + switch secretId as NSString { + case MXSecretId.crossSigningMaster.takeUnretainedValue(): + try crossSigning.importCrossSigningKeys( + export: .init( + masterKey: secret, + selfSigningKey: nil, + userSigningKey: nil + ) ) - ) - case MXSecretId.crossSigningSelfSigning.takeUnretainedValue(): - crossSigning.importCrossSigningKeys( - export: .init( - masterKey: nil, - selfSigningKey: secret, - userSigningKey: nil + case MXSecretId.crossSigningSelfSigning.takeUnretainedValue(): + try crossSigning.importCrossSigningKeys( + export: .init( + masterKey: nil, + selfSigningKey: secret, + userSigningKey: nil + ) ) - ) - case MXSecretId.crossSigningUserSigning.takeUnretainedValue(): - crossSigning.importCrossSigningKeys( - export: .init( - masterKey: nil, - selfSigningKey: nil, - userSigningKey: secret + case MXSecretId.crossSigningUserSigning.takeUnretainedValue(): + try crossSigning.importCrossSigningKeys( + export: .init( + masterKey: nil, + selfSigningKey: nil, + userSigningKey: secret + ) ) - ) - case MXSecretId.keyBackup.takeUnretainedValue(): - guard let version = backup?.keyBackupVersion?.version else { - log.error("No key backup version available") - return + case MXSecretId.keyBackup.takeUnretainedValue(): + guard let version = backup?.keyBackupVersion?.version else { + log.error("No key backup version available") + return + } + + let expectedPublicKey = try BackupRecoveryKey.fromBase64(key: secret).megolmV1PublicKey().publicKey + + guard let authData = backup?.keyBackupVersion?.authData, + MXCurve25519BackupAuthData(fromJSON: authData).publicKey == expectedPublicKey else { + errorHandler(MXCryptoError.secretDoesNotMatch) + return + } + + let privateKey = MXBase64Tools.data(fromBase64: secret) + backupEngine?.savePrivateKey(privateKey, version: version) + default: + log.error("Unsupported type of secret", context: secretId) } - - let privateKey = MXBase64Tools.data(fromBase64: secret) - backupEngine?.savePrivateKey(privateKey, version: version) - default: - log.error("Unsupported type of secret", context: secretId) + } catch { + errorHandler(error) } } diff --git a/MatrixSDK/Crypto/SecretStorage/MXSecretStorage.m b/MatrixSDK/Crypto/SecretStorage/MXSecretStorage.m index f2d0cb38ce..1f4bfcca6d 100644 --- a/MatrixSDK/Crypto/SecretStorage/MXSecretStorage.m +++ b/MatrixSDK/Crypto/SecretStorage/MXSecretStorage.m @@ -18,12 +18,10 @@ #import "MXSession.h" #import "MXTools.h" -#import "MXKeyBackupPassword.h" #import "MXRecoveryKey.h" #import "MXHkdfSha256.h" #import "MXAesHmacSha2.h" #import "MXBase64Tools.h" -#import #import "MXEncryptedSecretContent.h" @@ -128,104 +126,6 @@ - (MXHTTPOperation*)createKeyWithKeyId:(nullable NSString*)keyId return operation; } -- (MXHTTPOperation*)createKeyWithKeyId:(nullable NSString*)keyId - keyName:(nullable NSString*)keyName - passphrase:(nullable NSString*)passphrase - success:(void (^)(MXSecretStorageKeyCreationInfo *keyCreationInfo))success - failure:(void (^)(NSError *error))failure -{ - MXLogDebug(@"[MXSecretStorage] createKeyWithKeyId: Creating new key with passphrase"); - keyId = keyId ?: [[NSUUID UUID] UUIDString]; - - MXHTTPOperation *operation = [MXHTTPOperation new]; - - MXWeakify(self); - dispatch_async(processingQueue, ^{ - MXStrongifyAndReturnIfNil(self); - - NSError *error; - - NSData *privateKey; - MXSecretStoragePassphrase *passphraseInfo; - - if (passphrase) - { - // Generate a private key from the passphrase - NSString *salt; - NSUInteger iterations; - privateKey = [MXKeyBackupPassword generatePrivateKeyWithPassword:passphrase - salt:&salt - iterations:&iterations - error:&error]; - if (!error) - { - passphraseInfo = [MXSecretStoragePassphrase new]; - passphraseInfo.algorithm = @"m.pbkdf2"; - passphraseInfo.salt = salt; - passphraseInfo.iterations = iterations; - } - } - else - { - OLMPkDecryption *decryption = [OLMPkDecryption new]; - [decryption generateKey:&error]; - privateKey = decryption.privateKey; - } - - if (error) - { - dispatch_async(dispatch_get_main_queue(), ^{ - MXLogDebug(@"[MXSecretStorage] createKeyWithKeyId: Failed to create a new key - %@", error); - failure(error); - }); - return; - } - - // Build iv and mac - MXEncryptedSecretContent *encryptedZeroString = [self encryptedZeroStringWithPrivateKey:privateKey iv:nil error:&error]; - if (error) - { - dispatch_async(dispatch_get_main_queue(), ^{ - MXLogDebug(@"[MXSecretStorage] createKeyWithKeyId: Failed to create a new key - %@", error); - failure(error); - }); - return; - } - - MXSecretStorageKeyContent *ssssKeyContent = [MXSecretStorageKeyContent new]; - ssssKeyContent.name = keyName; - ssssKeyContent.algorithm = MXSecretStorageKeyAlgorithm.aesHmacSha2; - ssssKeyContent.passphrase = passphraseInfo; - ssssKeyContent.iv = encryptedZeroString.iv; - ssssKeyContent.mac = encryptedZeroString.mac; - - NSString *accountDataId = [self storageKeyIdForKey:keyId]; - MXHTTPOperation *operation2 = [self setAccountData:ssssKeyContent.JSONDictionary forType:accountDataId success:^{ - - MXSecretStorageKeyCreationInfo *keyCreationInfo = [MXSecretStorageKeyCreationInfo new]; - keyCreationInfo.keyId = keyId; - keyCreationInfo.content = ssssKeyContent; - keyCreationInfo.privateKey = privateKey; - keyCreationInfo.recoveryKey = [MXRecoveryKey encode:privateKey]; - - dispatch_async(dispatch_get_main_queue(), ^{ - MXLogDebug(@"[MXSecretStorage] createKeyWithKeyId: Successfully created a new key"); - success(keyCreationInfo); - }); - - } failure:^(NSError *error) { - dispatch_async(dispatch_get_main_queue(), ^{ - MXLogDebug(@"[MXSecretStorage] createKeyWithKeyId: Failed to create a new key - %@", error); - failure(error); - }); - }]; - - [operation mutateTo:operation2]; - }); - - return operation; -} - - (MXHTTPOperation*)deleteKeyWithKeyId:(nullable NSString*)keyId success:(void (^)(void))success failure:(void (^)(NSError *error))failure diff --git a/MatrixSDK/Crypto/Verification/MXKeyVerificationManager.h b/MatrixSDK/Crypto/Verification/MXKeyVerificationManager.h index e0921658d9..1a501bd9bf 100644 --- a/MatrixSDK/Crypto/Verification/MXKeyVerificationManager.h +++ b/MatrixSDK/Crypto/Verification/MXKeyVerificationManager.h @@ -22,8 +22,6 @@ #import "MXKeyVerification.h" #import "MXSASTransaction.h" -#import "MXIncomingSASTransaction.h" -#import "MXOutgoingSASTransaction.h" #import "MXQRCodeTransaction.h" @@ -176,14 +174,4 @@ FOUNDATION_EXPORT NSString *const MXKeyVerificationManagerNotificationTransactio @end -@interface MXLegacyKeyVerificationManager : NSObject - -- (void)notifyOthersOfAcceptanceWithTransactionId:(NSString*)transactionId - acceptedUserId:(NSString*)acceptedUserId - acceptedDeviceId:(NSString*)acceptedDeviceId - success:(void(^)(void))success - failure:(void(^)(NSError *error))failure; - -@end - NS_ASSUME_NONNULL_END diff --git a/MatrixSDK/Crypto/Verification/MXKeyVerificationManager.m b/MatrixSDK/Crypto/Verification/MXKeyVerificationManager.m index 1596bb9a31..4952ebc93d 100644 --- a/MatrixSDK/Crypto/Verification/MXKeyVerificationManager.m +++ b/MatrixSDK/Crypto/Verification/MXKeyVerificationManager.m @@ -16,28 +16,16 @@ */ #import "MXKeyVerificationManager.h" -#import "MXKeyVerificationManager_Private.h" -#import "MXIncomingSASTransaction_Private.h" #import "MXSession.h" -#import "MXCrypto_Private.h" -#import "MXCrossSigning_Private.h" #import "MXTools.h" #import "MXTransactionCancelCode.h" -#import "MXKeyVerificationRequest_Private.h" -#import "MXKeyVerificationByToDeviceRequest.h" -#import "MXKeyVerificationByDMRequest.h" #import "MXKeyVerificationRequestByToDeviceJSONModel.h" #import "MXKeyVerificationRequestByDMJSONModel.h" -#import "MXKeyVerificationStatusResolver.h" - -#import "MXSASTransaction_Private.h" -#import "MXQRCodeTransaction_Private.h" - #import "MXQRCodeDataBuilder.h" #import "MatrixSDKSwiftHeader.h" @@ -57,2001 +45,3 @@ NSTimeInterval const MXRequestDefaultTimeout = 5 * 60.0; static NSArray *kMXKeyVerificationManagerVerificationEventTypes; - - -@interface MXLegacyKeyVerificationManager () -{ - // The queue to run background tasks - dispatch_queue_t cryptoQueue; - - // All running transactions - MXUsersDevicesMap *transactions; - // Timer to cancel transactions - NSTimer *transactionTimeoutTimer; - - // All pending requests - // Request id -> request - NSMutableDictionary *pendingRequestsMap; - - // Timer to cancel requests - NSTimer *requestTimeoutTimer; - - MXKeyVerificationStatusResolver *statusResolver; -} - -@property (nonatomic, strong) MXQRCodeDataBuilder *qrCodeDataBuilder; - -/** - The timeout for requests. - Default is 5 min. - */ -@property (nonatomic) NSTimeInterval requestTimeout; - -@end - -@implementation MXLegacyKeyVerificationManager - -#pragma mark - Public methods - - -#pragma mark Requests - -- (void)requestVerificationByToDeviceWithUserId:(NSString*)userId - deviceIds:(nullable NSArray*)deviceIds - methods:(NSArray*)methods - success:(void(^)(id request))success - failure:(void(^)(NSError *error))failure -{ - MXLogDebug(@"[MXKeyVerification] requestVerificationByToDeviceWithUserId: %@. deviceIds: %@", userId, deviceIds); - if (deviceIds.count) - { - [self requestVerificationByToDeviceWithUserId2:userId deviceIds:deviceIds methods:methods success:success failure:failure]; - } - else - { - [self otherDeviceIdsOfUser:userId success:^(NSArray *otherDeviceIds) { - if (otherDeviceIds.count) - { - [self requestVerificationByToDeviceWithUserId2:userId deviceIds:otherDeviceIds methods:methods success:success failure:failure]; - } - else - { - NSError *error = [NSError errorWithDomain:MXKeyVerificationErrorDomain - code:MXKeyVerificatioNoOtherDeviceCode - userInfo:@{ - NSLocalizedDescriptionKey: [NSString stringWithFormat:@"The user has no other device"] - }]; - failure(error); - } - } failure:failure]; - } -} - -- (void)otherDeviceIdsOfUser:(NSString*)userId - success:(void(^)(NSArray *deviceIds))success - failure:(void(^)(NSError *error))failure -{ - [self.crypto downloadKeys:@[userId] forceDownload:YES success:^(MXUsersDevicesMap *usersDevicesInfoMap, NSDictionary *crossSigningKeysMap) { - - - NSMutableArray *deviceIds = [[usersDevicesInfoMap deviceIdsForUser:userId] mutableCopy]; - - MXCredentials *myUser = self.crypto.mxSession.matrixRestClient.credentials; - if ([userId isEqualToString:myUser.userId]) - { - [deviceIds removeObject:myUser.deviceId]; - } - - success(deviceIds); - } failure:failure]; -} - -- (void)requestVerificationByToDeviceWithUserId2:(NSString*)userId - deviceIds:(NSArray*)deviceIds - methods:(NSArray*)methods - success:(void(^)(id request))success - failure:(void(^)(NSError *error))failure -{ - NSParameterAssert(deviceIds.count > 0); - - MXKeyVerificationRequestByToDeviceJSONModel *requestJSONModel = [MXKeyVerificationRequestByToDeviceJSONModel new]; - requestJSONModel.fromDevice = _crypto.myDevice.deviceId; - requestJSONModel.transactionId = [MXTools generateSecret]; - requestJSONModel.methods = methods; - requestJSONModel.timestamp = [NSDate date].timeIntervalSince1970 * 1000; - - MXUsersDevicesMap *contentMap = [[MXUsersDevicesMap alloc] init]; - - for (NSString *deviceId in deviceIds) - { - [contentMap setObject:requestJSONModel.JSONDictionary forUser:userId andDevice:deviceId]; - } - - MXToDevicePayload *payload = [[MXToDevicePayload alloc] - initWithEventType:kMXMessageTypeKeyVerificationRequest - contentMap:contentMap - transactionId:nil - addMessageId:NO]; // Should not add anything for verification events as it would break their signatures - [self.crypto.matrixRestClient sendToDevice:payload success:^{ - - MXEvent *event = [MXEvent modelFromJSON:@{ - @"sender": self.crypto.mxSession.myUserId, - @"type": kMXMessageTypeKeyVerificationRequest, - @"content": requestJSONModel.JSONDictionary - }]; - MXKeyVerificationByToDeviceRequest *request = [[MXKeyVerificationByToDeviceRequest alloc] initWithEvent:event andManager:self to:userId requestedOtherDeviceIds:deviceIds]; - [request updateState:MXKeyVerificationRequestStatePending notifiy:YES]; - [self addPendingRequest:request notify:NO]; - - success(request); - - } failure:failure]; -} - -- (void)requestVerificationByDMWithUserId:(NSString*)userId - roomId:(nullable NSString*)roomId - fallbackText:(NSString*)fallbackText - methods:(NSArray*)methods - success:(void(^)(id request))success - failure:(void(^)(NSError *error))failure -{ - if (roomId) - { - [self requestVerificationByDMWithUserId2:userId roomId:roomId fallbackText:fallbackText methods:methods success:success failure:failure]; - } - else - { - [self.crypto.mxSession getOrCreateDirectJoinedRoomWithUserId:userId success:^(MXRoom *room) { - [self requestVerificationByDMWithUserId2:userId - roomId:room.roomId - fallbackText:fallbackText - methods:methods - success:success - failure:failure]; - } failure:failure]; - } -} - -- (void)requestVerificationByDMWithUserId2:(NSString*)userId - roomId:(NSString*)roomId - fallbackText:(NSString*)fallbackText - methods:(NSArray*)methods - success:(void(^)(id request))success - failure:(void(^)(NSError *error))failure -{ - MXLogDebug(@"[MXKeyVerification] requestVerificationByDMWithUserId: %@. RoomId: %@", userId, roomId); - - MXKeyVerificationRequestByDMJSONModel *request = [MXKeyVerificationRequestByDMJSONModel new]; - request.body = fallbackText; - request.methods = methods; - request.to = userId; - request.fromDevice = _crypto.myDevice.deviceId; - - [self sendEventOfType:kMXEventTypeStringRoomMessage toRoom:roomId content:request.JSONDictionary success:^(NSString *eventId) { - - // Build the corresponding the event - MXRoom *room = [self.crypto.mxSession roomWithRoomId:roomId]; - MXEvent *event = [room fakeRoomMessageEventWithEventId:eventId andContent:request.JSONDictionary threadId:nil]; - - MXLegacyKeyVerificationRequest *request = [self verificationRequestInDMEvent:event]; - [request updateState:MXKeyVerificationRequestStatePending notifiy:YES]; - [self addPendingRequest:request notify:NO]; - - success(request); - } failure:failure]; -} - -#pragma mark Current requests - -- (NSArray> *)pendingRequests -{ - return pendingRequestsMap.allValues; -} - - -#pragma mark Transactions - -- (void)beginKeyVerificationFromRequest:(id)request - method:(NSString*)method - success:(void(^)(id transaction))success - failure:(void(^)(NSError *error))failure -{ - MXLogDebug(@"[MXKeyVerification] beginKeyVerificationFromRequest: event: %@", request.requestId); - - // Sanity checks - if (!request.otherDevice) - { - NSError *error = [NSError errorWithDomain:MXKeyVerificationErrorDomain - code:MXKeyVerificationUnknownDeviceCode - userInfo:@{ - NSLocalizedDescriptionKey: [NSString stringWithFormat:@"from_device not found"] - }]; - failure(error); - return; - } - - if (request.state != MXKeyVerificationRequestStateAccepted && request.state != MXKeyVerificationRequestStateReady) - { - NSError *error = [NSError errorWithDomain:MXKeyVerificationErrorDomain - code:MXKeyVerificationInvalidStateCode - userInfo:@{ - NSLocalizedDescriptionKey: [NSString stringWithFormat:@"The verification request has not been accepted. Current state: %@", @(request.state)] - }]; - failure(error); - return; - } - - switch (request.transport) - { - case MXKeyVerificationTransportDirectMessage: - if ([request isKindOfClass:MXKeyVerificationByDMRequest.class]) - { - MXKeyVerificationByDMRequest *requestByDM = (MXKeyVerificationByDMRequest*)request; - [self beginKeyVerificationWithUserId:request.otherUser andDeviceId:request.otherDevice transactionId:request.requestId dmRoomId:requestByDM.roomId dmEventId:requestByDM.eventId method:method success:^(id transaction) { - [self removePendingRequestWithRequestId:request.requestId]; - success(transaction); - } failure:failure]; - } - break; - - case MXKeyVerificationTransportToDevice: - [self beginKeyVerificationWithUserId:request.otherUser andDeviceId:request.otherDevice transactionId:request.requestId dmRoomId:nil dmEventId:nil method:method success:^(id _Nonnull transaction) { - [self removePendingRequestWithRequestId:request.requestId]; - success(transaction); - } failure:failure]; - break; - } -} - -- (void)beginKeyVerificationWithUserId:(NSString*)userId - andDeviceId:(NSString*)deviceId - transactionId:(nullable NSString*)transactionId - dmRoomId:(nullable NSString*)dmRoomId - dmEventId:(nullable NSString*)dmEventId - method:(NSString*)method - success:(void(^)(id transaction))success - failure:(void(^)(NSError *error))failure -{ - MXLogDebug(@"[MXKeyVerification] beginKeyVerification: device: %@:%@ roomId: %@ method:%@", userId, deviceId, dmRoomId, method); - - // Make sure we have other device keys - [self loadDeviceWithDeviceId:deviceId andUserId:userId success:^(MXDeviceInfo *otherDevice) { - - id transaction; - NSError *error; - - // We support only SAS at the moment - if ([method isEqualToString:MXKeyVerificationMethodSAS]) - { - MXOutgoingSASTransaction *sasTransaction = [[MXOutgoingSASTransaction alloc] initWithOtherDevice:otherDevice andManager:self]; - if (transactionId) - { - sasTransaction.transactionId = transactionId; - } - - // Detect verification by DM - if (dmRoomId) - { - [sasTransaction setDirectMessageTransportInRoom:dmRoomId originalEvent:dmEventId]; - } - - [sasTransaction start]; - - transaction = sasTransaction; - [self addTransaction:transaction]; - } - else - { - error = [NSError errorWithDomain:MXKeyVerificationErrorDomain - code:MXKeyVerificationUnsupportedMethodCode - userInfo:@{ - NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Unsupported verification method: %@", method] - }]; - } - - dispatch_async(dispatch_get_main_queue(), ^{ - if (transaction) - { - success(transaction); - } - else - { - failure(error); - } - }); - - } failure:^(NSError *error) { - MXLogDebug(@"[MXKeyVerification] beginKeyVerification: Error: %@", error); - failure(error); - }]; -} - -- (void)createQRCodeTransactionFromRequest:(id)request - qrCodeData:(nullable MXQRCodeData*)qrCodeData - success:(void(^)(MXLegacyQRCodeTransaction *transaction))success - failure:(void(^)(NSError *error))failure -{ - MXLogDebug(@"[MXKeyVerification] createQRCodeTransactionFromRequest: event: %@", request.requestId); - - // Sanity checks - if (!request.otherDevice) - { - NSError *error = [NSError errorWithDomain:MXKeyVerificationErrorDomain - code:MXKeyVerificationUnknownDeviceCode - userInfo:@{ - NSLocalizedDescriptionKey: [NSString stringWithFormat:@"from_device not found"] - }]; - failure(error); - return; - } - - if (request.state != MXKeyVerificationRequestStatePending) - { - NSError *error = [NSError errorWithDomain:MXKeyVerificationErrorDomain - code:MXKeyVerificationInvalidStateCode - userInfo:@{ - NSLocalizedDescriptionKey: [NSString stringWithFormat:@"The verification request has not been accepted. Current state: %@", @(request.state)] - }]; - failure(error); - return; - } - - NSString *dmRoomId; - NSString *dmEventId; - - switch (request.transport) - { - case MXKeyVerificationTransportDirectMessage: - if ([request isKindOfClass:MXKeyVerificationByDMRequest.class]) - { - MXKeyVerificationByDMRequest *requestByDM = (MXKeyVerificationByDMRequest*)request; - - dmRoomId = requestByDM.roomId; - dmEventId = requestByDM.eventId; - } - break; - case MXKeyVerificationTransportToDevice: - break; - } - - [self createQRCodeTransactionWithQRCodeData:qrCodeData - userId:request.otherUser - deviceId:request.otherDevice - transactionId:request.requestId - dmRoomId:dmRoomId - dmEventId:dmEventId - success:success - failure:failure]; -} - -- (void)createQRCodeTransactionWithQRCodeData:(nullable MXQRCodeData*)qrCodeData - userId:(NSString*)userId - deviceId:(NSString*)deviceId - transactionId:(nullable NSString*)transactionId - dmRoomId:(nullable NSString*)dmRoomId - dmEventId:(nullable NSString*)dmEventId - success:(void(^)(MXLegacyQRCodeTransaction *transaction))success - failure:(void(^)(NSError *error))failure -{ - MXLogDebug(@"[MXKeyVerification] createQRCodeTransaction: device: %@:%@ roomId: %@", userId, deviceId, dmRoomId); - - // Make sure we have other device keys - [self loadDeviceWithDeviceId:deviceId andUserId:userId success:^(MXDeviceInfo *otherDevice) { - - MXLegacyQRCodeTransaction *transaction = [[MXLegacyQRCodeTransaction alloc] initWithOtherDevice:otherDevice qrCodeData:qrCodeData andManager:self]; - - - if (transactionId) - { - transaction.transactionId = transactionId; - } - - // Detect verification by DM - if (dmRoomId) - { - [transaction setDirectMessageTransportInRoom:dmRoomId originalEvent:dmEventId]; - } - - [self addTransaction:transaction]; - - - - dispatch_async(dispatch_get_main_queue(), ^{ - if (transaction) - { - success(transaction); - } - else - { - NSError *error = [NSError errorWithDomain:MXKeyVerificationErrorDomain - code:MXKeyVerificationUnknownIdentifier - userInfo:@{ - NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Fail to create transaction with id: %@", transactionId] - }]; - - failure(error); - } - }); - - } failure:^(NSError *error) { - MXLogDebug(@"[MXKeyVerification] createQRCodeTransaction: Error: %@", error); - dispatch_async(dispatch_get_main_queue(), ^{ - failure(error); - }); - }]; -} - -- (void)removeQRCodeTransactionWithTransactionId:(NSString*)transactionId -{ - MXLegacyQRCodeTransaction *qrCodeTransaction = [self qrCodeTransactionWithTransactionId:transactionId]; - - if (qrCodeTransaction) - { - [self removeTransactionWithTransactionId:qrCodeTransaction.transactionId]; - } -} - -- (void)transactions:(void(^)(NSArray> *transactions))complete -{ - MXWeakify(self); - dispatch_async(self->cryptoQueue, ^{ - MXStrongifyAndReturnIfNil(self); - - NSArray> *transactions = self->transactions.allObjects; - dispatch_async(dispatch_get_main_queue(), ^{ - complete(transactions); - }); - }); -} - - -#pragma mark Verification status - -- (nullable MXHTTPOperation *)keyVerificationFromKeyVerificationEvent:(MXEvent*)event - roomId:(NSString *)roomId - success:(void(^)(MXKeyVerification *keyVerification))success - failure:(void(^)(NSError *error))failure -{ - MXKeyVerificationTransport transport = MXKeyVerificationTransportToDevice; - MXKeyVerification *keyVerification; - - // Check if it is a Verification by DM Event - NSString *keyVerificationId = [self keyVerificationIdFromDMEvent:event]; - if (keyVerificationId) - { - transport = MXKeyVerificationTransportDirectMessage; - } - else - { - NSError *error = [NSError errorWithDomain:MXKeyVerificationErrorDomain - code:MXKeyVerificationUnknownIdentifier - userInfo:@{ - NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Unknown id or not supported transport"] - }]; - failure(error); - return nil; - } - - keyVerification = [self pendingKeyVerificationWithKeyVerificationId:keyVerificationId]; - if (keyVerification) - { - success(keyVerification); - return nil; - } - - - return [statusResolver keyVerificationWithKeyVerificationId:keyVerificationId event:event transport:transport success:success failure:failure]; -} - -- (nullable NSString *)keyVerificationIdFromDMEvent:(MXEvent*)event -{ - NSString *keyVerificationId; - - // Original event or one of the thread? - if (event.eventType == MXEventTypeRoomMessage - && [event.content[kMXMessageTypeKey] isEqualToString:kMXMessageTypeKeyVerificationRequest]) - { - keyVerificationId = event.eventId; - } - else if ([self isVerificationEventType:event.type]) - { - MXKeyVerificationJSONModel *keyVerificationJSONModel; - MXJSONModelSetMXJSONModel(keyVerificationJSONModel, MXKeyVerificationJSONModel, event.content); - keyVerificationId = keyVerificationJSONModel.relatedEventId; - } - - return keyVerificationId; -} - -- (nullable MXKeyVerification *)pendingKeyVerificationWithKeyVerificationId:(NSString*)keyVerificationId -{ - id transaction = [self transactionWithTransactionId:keyVerificationId]; - MXLegacyKeyVerificationRequest *request = [self pendingRequestWithRequestId:keyVerificationId]; - - return [self->statusResolver keyVerificationFromRequest:request andTransaction:transaction]; -} - -#pragma mark - SDK-Private methods - - -+ (void)initialize -{ - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - kMXKeyVerificationManagerVerificationEventTypes = @[ - kMXMessageTypeKeyVerificationRequest, - kMXEventTypeStringKeyVerificationReady, - kMXEventTypeStringKeyVerificationStart, - kMXEventTypeStringKeyVerificationAccept, - kMXEventTypeStringKeyVerificationKey, - kMXEventTypeStringKeyVerificationMac, - kMXEventTypeStringKeyVerificationCancel, - kMXEventTypeStringKeyVerificationDone - ]; - }); -} - -- (instancetype)initWithCrypto:(MXLegacyCrypto *)crypto -{ - self = [super init]; - if (self) - { - _crypto = crypto; - cryptoQueue = self.crypto.cryptoQueue; - - transactions = [MXUsersDevicesMap new]; - - // Observe incoming to-device events - [self setupIncomingToDeviceEvents]; - - // Observe incoming DM events - [self setupIncomingDMEvents]; - - _requestTimeout = MXRequestDefaultTimeout; - pendingRequestsMap = [NSMutableDictionary dictionary]; - [self setupVericationByDMRequests]; - - statusResolver = [[MXKeyVerificationStatusResolver alloc] initWithManager:self matrixSession:crypto.mxSession]; - - _qrCodeDataBuilder = [MXQRCodeDataBuilder new]; - } - return self; -} - -- (void)dealloc -{ - if (transactionTimeoutTimer) - { - [transactionTimeoutTimer invalidate]; - transactionTimeoutTimer = nil; - } - - if (requestTimeoutTimer) - { - [requestTimeoutTimer invalidate]; - requestTimeoutTimer = nil; - } -} - - -#pragma mark - Requests - -- (MXHTTPOperation*)sendToOtherInRequest:(id)request - eventType:(NSString*)eventType - content:(NSDictionary*)content - success:(dispatch_block_t)success - failure:(void (^)(NSError *error))failure -{ - MXLogDebug(@"[MXKeyVerification] sendToOtherInRequest: eventType: %@\n%@", - eventType, content); - - MXHTTPOperation *operation; - switch (request.transport) - { - case MXKeyVerificationTransportDirectMessage: - if ([request isKindOfClass:MXKeyVerificationByDMRequest.class]) - { - MXKeyVerificationByDMRequest *requestByDM = (MXKeyVerificationByDMRequest*)request; - operation = [self sendMessage:request.otherUser roomId:requestByDM.roomId eventType:eventType relatedTo:requestByDM.eventId content:content success:success failure:failure]; - } - break; - - case MXKeyVerificationTransportToDevice: - if (request.otherDevice) - { - operation = [self sendToDevice:request.otherUser deviceId:request.otherDevice eventType:eventType content:content success:success failure:failure]; - } - else - { - // This happens when cancelling our own request. - // There is no otherDevice in this case. We broadcast to all devices we made the request to. - if ([request isKindOfClass:MXKeyVerificationByToDeviceRequest.class]) - { - MXKeyVerificationByToDeviceRequest *requestByToDevice = (MXKeyVerificationByToDeviceRequest*)request; - if (requestByToDevice.requestedOtherDeviceIds) - { - operation = [self sendToDevices:request.otherUser deviceIds:requestByToDevice.requestedOtherDeviceIds eventType:eventType content:content success:success failure:failure]; - } - } - } - - break; - } - - // We should be always able to talk to the other peer - NSParameterAssert(operation); - - return operation; -} - -- (void)notifyOthersOfAcceptanceWithTransactionId:(NSString*)transactionId - acceptedUserId:(NSString*)acceptedUserId - acceptedDeviceId:(NSString*)acceptedDeviceId - success:(void(^)(void))success - failure:(void(^)(NSError *error))failure -{ - [self otherDeviceIdsOfUser:acceptedUserId success:^(NSArray *deviceIds) { - NSMutableArray *nonChosenDevices = [deviceIds mutableCopy]; - [nonChosenDevices removeObject:acceptedDeviceId]; - - MXKeyVerificationCancel *cancel = [MXKeyVerificationCancel new]; - MXTransactionCancelCode *cancelCode = MXTransactionCancelCode.accepted; - cancel.transactionId = transactionId; - cancel.code = cancelCode.value; - cancel.reason = cancelCode.humanReadable; - [self sendToDevices:acceptedUserId deviceIds:nonChosenDevices eventType:kMXEventTypeStringKeyVerificationCancel content:cancel.JSONDictionary success:success failure:failure]; - } failure:failure]; -} - -- (void)cancelVerificationRequest:(id)request - success:(void(^)(void))success - failure:(void(^)(NSError *error))failure -{ - MXTransactionCancelCode *cancelCode = MXTransactionCancelCode.user; - - // If there is transaction in progress, cancel it - id transaction = [self transactionWithTransactionId:request.requestId]; - if (transaction) - { - [self cancelTransaction:transaction code:cancelCode success:success failure:failure]; - } - else - { - // Else only cancel the request - MXKeyVerificationCancel *cancel = [MXKeyVerificationCancel new]; - cancel.transactionId = request.requestId; - cancel.code = cancelCode.value; - cancel.reason = cancelCode.humanReadable; - - [self sendToOtherInRequest:request eventType:kMXEventTypeStringKeyVerificationCancel content:cancel.JSONDictionary success:success failure:failure]; - } -} - - -#pragma mark - Transactions - -- (MXHTTPOperation*)sendToOtherInTransaction:(id)transaction - eventType:(NSString*)eventType - content:(NSDictionary*)content - success:(void (^)(void))success - failure:(void (^)(NSError *error))failure -{ - MXLogDebug(@"[MXKeyVerification] sendToOtherInTransaction%@: eventType: %@\n%@", - transaction.dmEventId ? @"(DM)" : @"", - eventType, content); - - MXHTTPOperation *operation; - switch (transaction.transport) - { - case MXKeyVerificationTransportToDevice: - operation = [self sendToDevice:transaction.otherUserId deviceId:transaction.otherDeviceId eventType:eventType content:content success:success failure:failure]; - break; - case MXKeyVerificationTransportDirectMessage: - operation = [self sendMessage:transaction.otherUserId roomId:transaction.dmRoomId eventType:eventType relatedTo:transaction.dmEventId content:content success:success failure:failure]; - break; - } - - return operation; -} - -- (void)cancelTransaction:(id)transaction - code:(MXTransactionCancelCode*)code - success:(void (^)(void))success - failure:(void (^)(NSError *error))failure -{ - MXLogDebug(@"[MXKeyVerification] cancelTransaction. code: %@", code.value); - - MXKeyVerificationCancel *cancel = [MXKeyVerificationCancel new]; - cancel.transactionId = transaction.transactionId; - cancel.code = code.value; - cancel.reason = code.humanReadable; - - [self sendToOtherInTransaction:transaction eventType:kMXEventTypeStringKeyVerificationCancel content:cancel.JSONDictionary success:^{ - - if ([transaction isKindOfClass:[MXLegacyKeyVerificationTransaction class]]) - { - ((MXLegacyKeyVerificationTransaction *)transaction).reasonCancelCode = code; - } - else - { - NSString *message = [NSString stringWithFormat:@"[MXKeyVerification] cancelTransaction: Cannot set cancellation reason on unknown transaction type: %@", NSStringFromClass([transaction class])]; - MXLogFailure(message) - } - - if (success) - { - success(); - } - - } failure:^(NSError *error) { - - MXLogDebug(@"[MXKeyVerification] cancelTransaction. Error: %@", error); - if (failure) - { - failure(error); - } - }]; - - [self removeTransactionWithTransactionId:transaction.transactionId]; -} - -// Special handling for incoming requests that are not yet valid transactions -- (void)cancelTransactionFromStartEvent:(MXEvent*)event code:(MXTransactionCancelCode*)code -{ - MXLogDebug(@"[MXKeyVerification] cancelTransactionFromStartEvent. code: %@", code.value); - - MXKeyVerificationStart *keyVerificationStart; - MXJSONModelSetMXJSONModel(keyVerificationStart, MXKeyVerificationStart, event.content); - - if (keyVerificationStart) - { - MXKeyVerificationCancel *cancel = [MXKeyVerificationCancel new]; - cancel.transactionId = keyVerificationStart.transactionId; - cancel.code = code.value; - cancel.reason = code.humanReadable; - - // Which transport? DM or to_device events? - if (keyVerificationStart.relatedEventId) - { - [self sendMessage:event.sender roomId:event.roomId eventType:kMXEventTypeStringKeyVerificationCancel relatedTo:keyVerificationStart.relatedEventId content:cancel.JSONDictionary success:nil failure:^(NSError *error) { - - MXLogDebug(@"[MXKeyVerification] cancelTransactionFromStartEvent. Error: %@", error); - }]; - } - else - { - [self sendToDevice:event.sender deviceId:keyVerificationStart.fromDevice eventType:kMXEventTypeStringKeyVerificationCancel content:cancel.JSONDictionary success:nil failure:^(NSError *error) { - - MXLogDebug(@"[MXKeyVerification] cancelTransactionFromStartEvent. Error: %@", error); - }]; - } - - [self removeTransactionWithTransactionId:keyVerificationStart.transactionId]; - } -} - - -#pragma mark - Incoming events - -- (void)handleKeyVerificationEvent:(MXEvent*)event isToDeviceEvent:(BOOL)isToDeviceEvent -{ - dispatch_async(cryptoQueue, ^{ - - BOOL eventFromMyUser = [event.sender isEqualToString:self.crypto.mxSession.myUserId]; - BOOL isEventIntendedForMyDevice = isToDeviceEvent || !eventFromMyUser; - - MXLogDebug(@"[MXKeyVerification] handleKeyVerificationEvent(from my user: %@, isToDeviceEvent: %@, intendedForMyDevice: %@): eventType: %@ \n%@", - eventFromMyUser ? @"YES": @"NO", - isToDeviceEvent ? @"YES": @"NO", - isEventIntendedForMyDevice ? @"MAYBE": @"NO", // MAYBE because it depends on the type of event - event.type, - event.clearEvent ? event.clearEvent.JSONDictionary : event.JSONDictionary); - - switch (event.eventType) - { - case MXEventTypeKeyVerificationRequest: - // Only requests by to_device come here - [self handleToDeviceRequestEvent:event]; - break; - - case MXEventTypeKeyVerificationReady: - [self handleReadyEvent:event isToDeviceEvent:isToDeviceEvent]; - break; - - case MXEventTypeKeyVerificationStart: - if (isEventIntendedForMyDevice) - { - [self handleStartEvent:event]; - } - break; - - case MXEventTypeKeyVerificationCancel: - if (isEventIntendedForMyDevice) - { - [self handleCancelEvent:event]; - } - break; - - case MXEventTypeKeyVerificationAccept: - if (isEventIntendedForMyDevice) - { - [self handleAcceptEvent:event]; - } - break; - - case MXEventTypeKeyVerificationKey: - if (isEventIntendedForMyDevice) - { - [self handleKeyEvent:event]; - } - break; - - case MXEventTypeKeyVerificationMac: - if (isEventIntendedForMyDevice) - { - [self handleMacEvent:event]; - } - break; - case MXEventTypeKeyVerificationDone: - if (isEventIntendedForMyDevice) - { - [self handleDoneEvent:event]; - } - break; - default: - break; - } - }); -} - - -- (void)handleToDeviceRequestEvent:(MXEvent*)event -{ - MXLogDebug(@"[MXKeyVerification] handleToDeviceRequestEvent"); - - MXKeyVerificationByToDeviceRequest *keyVerificationRequest = [[MXKeyVerificationByToDeviceRequest alloc] initWithEvent:event andManager:self to:self.crypto.mxSession.myUserId requestedOtherDeviceIds:@[]]; - - if (!keyVerificationRequest) - { - return; - } - - if (![self isRequestStillValid:keyVerificationRequest]) - { - return; - } - - [self addPendingRequest:keyVerificationRequest notify:YES]; -} - -- (void)handleReadyEvent:(MXEvent*)event isToDeviceEvent:(BOOL)isToDeviceEvent -{ - MXLogDebug(@"[MXKeyVerification] handleReadyEvent"); - - MXKeyVerificationReady *keyVerificationReady; - MXJSONModelSetMXJSONModel(keyVerificationReady, MXKeyVerificationReady, event.content); - - if (!keyVerificationReady) - { - return; - } - - NSString *requestId = keyVerificationReady.transactionId; - MXLegacyKeyVerificationRequest *request = [self pendingRequestWithRequestId:requestId]; - - if (request) - { - MXCredentials *myCreds = _crypto.mxSession.matrixRestClient.credentials; - - BOOL eventFromMyUser = [event.sender isEqualToString:myCreds.userId]; - BOOL isEventIntendedForMyDevice = isToDeviceEvent || !eventFromMyUser; - - if (isEventIntendedForMyDevice) - { - [request handleReady:keyVerificationReady]; - } - else - { - BOOL eventFromMyDevice = [keyVerificationReady.fromDevice isEqualToString:myCreds.deviceId]; - if (!eventFromMyDevice) - { - // This is a ready response to a request the user made from another device - // Remove it from pending requests will ignore any other events related to this request id - MXLogDebug(@"[MXKeyVerification] handleReadyEvent: The request (%@) has been accepted on another device. Ignore it.", requestId); - [self removePendingRequestWithRequestId:request.requestId]; - } - } - } -} - -- (void)handleCancelEvent:(MXEvent*)event -{ - MXLogDebug(@"[MXKeyVerification] handleCancelEvent"); - - MXKeyVerificationCancel *cancelContent; - MXJSONModelSetMXJSONModel(cancelContent, MXKeyVerificationCancel, event.content); - - if (cancelContent) - { - MXLegacyKeyVerificationTransaction *transaction = [self transactionWithTransactionId:cancelContent.transactionId]; - if (transaction) - { - [transaction handleCancel:cancelContent]; - [self removeTransactionWithTransactionId:transaction.transactionId]; - } - - NSString *requestId = cancelContent.transactionId; - MXLegacyKeyVerificationRequest *request = [self pendingRequestWithRequestId:requestId]; - if (request) - { - [request handleCancel:cancelContent]; - } - } - else - { - MXLogDebug(@"[MXKeyVerification] handleCancelEvent. Invalid event: %@", event.JSONDictionary); - } -} - -- (void)handleStartEvent:(MXEvent*)event -{ - MXLogDebug(@"[MXKeyVerification] handleStartEvent"); - - MXSASKeyVerificationStart *keyVerificationSASStart; - MXJSONModelSetMXJSONModel(keyVerificationSASStart, MXSASKeyVerificationStart, event.content); - - if (keyVerificationSASStart) - { - [self handleSASKeyVerificationStart:keyVerificationSASStart withEvent:event]; - return; - } - - MXQRCodeKeyVerificationStart *keyVerificationQRCodeStart; - MXJSONModelSetMXJSONModel(keyVerificationQRCodeStart, MXQRCodeKeyVerificationStart, event.content); - - if (keyVerificationQRCodeStart) - { - [self handleQRCodeKeyVerificationStart:keyVerificationQRCodeStart withEvent:event]; - return; - } - - MXLogDebug(@"[MXKeyVerification] handleStartEvent: Unknown start event %@", event); - [self cancelTransactionFromStartEvent:event code:MXTransactionCancelCode.unknownMethod]; -} - -#pragma mark SAS - -- (void)handleSASKeyVerificationStart:(MXSASKeyVerificationStart*)keyVerificationStart withEvent:(MXEvent*)event -{ - MXLogDebug(@"[MXKeyVerification] handleSASKeyVerificationStart"); - - if (!keyVerificationStart) - { - return; - } - - NSString *requestId = keyVerificationStart.transactionId; - MXLegacyKeyVerificationRequest *request = [self pendingRequestWithRequestId:requestId]; - if (request) - { - // We have a start response. The request is complete - [self removePendingRequestWithRequestId:request.requestId]; - } - - if ([event.relatesTo.relationType isEqualToString:MXEventRelationTypeReference]) - { - if (!request - || (request.isFromMyUser && !request.isFromMyDevice)) - { - // This is a start response to a request we did not make. Ignore it - MXLogDebug(@"[MXKeyVerification] handleStartEvent: Start event for verification by DM(%@) not triggered by this device. Ignore it", requestId); - return; - } - } - - if (!keyVerificationStart.isValid) - { - if (keyVerificationStart.transactionId && keyVerificationStart.fromDevice) - { - [self cancelTransactionFromStartEvent:event code:MXTransactionCancelCode.invalidMessage]; - } - - return; - } - - - // Make sure we have other device keys - [self loadDeviceWithDeviceId:keyVerificationStart.fromDevice andUserId:event.sender success:^(MXDeviceInfo *otherDevice) { - - id existingTransaction = [self transactionWithUser:event.sender andDevice:keyVerificationStart.fromDevice]; - - if ([existingTransaction isKindOfClass:MXLegacyQRCodeTransaction.class]) - { - MXLegacyQRCodeTransaction *existingQRCodeTransaction = (MXLegacyQRCodeTransaction*)existingTransaction; - - if (existingQRCodeTransaction.state == MXQRCodeTransactionStateUnknown) - { - // Remove fake QR code transaction - [self removeQRCodeTransactionWithTransactionId:existingQRCodeTransaction.transactionId]; - existingTransaction = nil; - } - } - - if (existingTransaction) - { - MXLogDebug(@"[MXKeyVerification] handleStartEvent: already existing transaction. Cancel both"); - - [existingTransaction cancelWithCancelCode:MXTransactionCancelCode.invalidMessage]; - [self cancelTransactionFromStartEvent:event code:MXTransactionCancelCode.invalidMessage]; - return; - } - - // Multiple keyshares between two devices: any two devices may only have at most one key verification in flight at a time. - NSArray> *transactionsWithUser = [self transactionsWithUser:event.sender]; - if (transactionsWithUser.count) - { - MXLogDebug(@"[MXKeyVerification] handleStartEvent: already existing transaction with the user. Cancel both"); - - [transactionsWithUser[0] cancelWithCancelCode:MXTransactionCancelCode.invalidMessage]; - [self cancelTransactionFromStartEvent:event code:MXTransactionCancelCode.invalidMessage]; - return; - } - - MXIncomingSASTransaction *transaction = [[MXIncomingSASTransaction alloc] initWithOtherDevice:otherDevice startEvent:event andManager:self]; - if (transaction) - { - if ([self isCreationDateValid:transaction]) - { - [self addTransaction:transaction]; - - if (request) - { - MXLogDebug(@"[MXKeyVerification] handleStartEvent: auto accept incoming transaction in response of a request"); - [transaction accept]; - } - } - else - { - MXLogDebug(@"[MXKeyVerification] handleStartEvent: Expired transaction: %@", transaction); - [self cancelTransactionFromStartEvent:event code:MXTransactionCancelCode.timeout]; - } - } - else - { - MXLogDebug(@"[MXKeyVerification] handleStartEvent: Unsupported transaction method: %@", event); - [self cancelTransactionFromStartEvent:event code:MXTransactionCancelCode.unknownMethod]; - } - - } failure:^(NSError *error) { - MXLogDebug(@"[MXKeyVerification] handleStartEvent: Failed to get other device keys: %@", event); - [self cancelTransactionFromStartEvent:event code:MXTransactionCancelCode.invalidMessage]; - }]; -} - -- (void)handleAcceptEvent:(MXEvent*)event -{ - MXLogDebug(@"[MXKeyVerification] handleAcceptEvent"); - - MXKeyVerificationAccept *acceptContent; - MXJSONModelSetMXJSONModel(acceptContent, MXKeyVerificationAccept, event.content); - - if (acceptContent) - { - MXLegacySASTransaction *transaction = [self sasTransactionWithTransactionId:acceptContent.transactionId]; - if (transaction) - { - [transaction handleAccept:acceptContent]; - } - else - { - MXLogDebug(@"[MXKeyVerification] handleAcceptEvent. Unknown SAS transaction: %@", event); - } - } - else - { - MXLogDebug(@"[MXKeyVerification] handleAcceptEvent. Invalid event: %@", event); - } -} - -- (void)handleKeyEvent:(MXEvent*)event -{ - MXLogDebug(@"[MXKeyVerification] handleKeyEvent"); - - MXKeyVerificationKey *keyContent; - MXJSONModelSetMXJSONModel(keyContent, MXKeyVerificationKey, event.content); - - if (keyContent) - { - MXLegacySASTransaction *transaction = [self sasTransactionWithTransactionId:keyContent.transactionId]; - if (transaction) - { - [transaction handleKey:keyContent]; - } - else - { - MXLogDebug(@"[MXKeyVerification] handleKeyEvent. Unknown SAS transaction: %@", event); - } - } - else - { - MXLogDebug(@"[MXKeyVerification] handleKeyEvent. Invalid event: %@", event); - } -} - -- (void)handleMacEvent:(MXEvent*)event -{ - MXLogDebug(@"[MXKeyVerification] handleMacEvent"); - - MXKeyVerificationMac *macContent; - MXJSONModelSetMXJSONModel(macContent, MXKeyVerificationMac, event.content); - - if (macContent) - { - MXLegacySASTransaction *transaction = [self sasTransactionWithTransactionId:macContent.transactionId]; - if (transaction) - { - [transaction handleMac:macContent]; - } - else - { - MXLogDebug(@"[MXKeyVerification] handleMacEvent. Unknown SAS transaction: %@", event); - } - } - else - { - MXLogDebug(@"[MXKeyVerification] handleMacEvent. Invalid event: %@", event); - } -} - -- (void)handleDoneEvent:(MXEvent*)event -{ - MXKeyVerificationDone *doneEvent; - MXJSONModelSetMXJSONModel(doneEvent, MXKeyVerificationDone, event.content); - - if (doneEvent) - { - MXLegacyQRCodeTransaction *qrCodeTransaction = [self qrCodeTransactionWithTransactionId:doneEvent.transactionId]; - if (qrCodeTransaction) - { - [qrCodeTransaction handleDone:doneEvent]; - } - else - { - MXLogDebug(@"[MXKeyVerification] handleDoneEvent. Not handled for SAS transaction: %@", event); - } - - id transaction = [self transactionWithTransactionId:doneEvent.transactionId]; - if (transaction && transaction.otherDeviceId) - { - BOOL eventFromMyDevice = [transaction.otherDeviceId isEqualToString:self.crypto.mxSession.myDeviceId]; - if (!eventFromMyDevice) - { - MXLogDebug(@"[MXKeyVerification] handleDoneEvent: requestAllPrivateKeys"); - [self.crypto requestAllPrivateKeys]; - } - } - else - { - // The done event from the other can happen long time after. - // That means the transaction can be no more in memory. In this case, request private keys with no condition - MXLogDebug(@"[MXKeyVerification] handleDoneEvent: requestAllPrivateKeys anyway"); - [self.crypto requestAllPrivateKeys]; - } - } - else - { - MXLogDebug(@"[MXKeyVerification] handleMacEvent. Invalid event: %@", event); - } -} - -#pragma mark QR Code - -- (void)handleQRCodeKeyVerificationStart:(MXQRCodeKeyVerificationStart*)keyVerificationStart withEvent:(MXEvent*)event -{ - MXLogDebug(@"[MXKeyVerification] handleQRCodeKeyVerificationStart"); - - if (!keyVerificationStart) - { - return; - } - - NSString *requestId = keyVerificationStart.transactionId; - MXLegacyKeyVerificationRequest *request = [self pendingRequestWithRequestId:requestId]; - if (request) - { - // We have a start response. The request is complete - [self removePendingRequestWithRequestId:request.requestId]; - } - - if ([event.relatesTo.relationType isEqualToString:MXEventRelationTypeReference]) - { - if (!request - || (request.isFromMyUser && !request.isFromMyDevice)) - { - // This is a start response to a request we did not make. Ignore it - MXLogDebug(@"[MXKeyVerification] handleStartEvent: Start event for verification by DM(%@) not triggered by this device. Ignore it", requestId); - return; - } - } - - if (!keyVerificationStart.isValid) - { - [self cancelTransactionFromStartEvent:event code:MXTransactionCancelCode.invalidMessage]; - return; - } - - MXLegacyQRCodeTransaction *qrCodeTransaction = [self qrCodeTransactionWithTransactionId:requestId]; - - // Verify existing transaction - if (!qrCodeTransaction) - { - MXLogDebug(@"[MXKeyVerification] handleStartEvent: Start event for verification not triggered by this device no existing transaction. Start event: %@", event); - [self cancelTransactionFromStartEvent:event code:MXTransactionCancelCode.userMismatchError]; - return; - } - - // Verify sender user match - if (![qrCodeTransaction.otherUserId isEqualToString:event.sender]) - { - MXLogDebug(@"[MXKeyVerification] handleStartEvent: Invalid start event sender user mismatch. Start event: %@.", event); - [self cancelTransactionFromStartEvent:event code:MXTransactionCancelCode.userMismatchError]; - return; - } - - // Verify sender device match - if (![qrCodeTransaction.otherDevice.deviceId isEqualToString:keyVerificationStart.fromDevice]) - { - MXLogDebug(@"[MXKeyVerification] handleStartEvent: Invalid start event sender device mismatch. Start event: %@.", event); - [self cancelTransactionFromStartEvent:event code:MXTransactionCancelCode.userMismatchError]; - return; - } - - // Verify shared secret match - [qrCodeTransaction handleStart:keyVerificationStart]; -} - - -#pragma mark - Transport - -#pragma mark to_device - -- (void)setupIncomingToDeviceEvents -{ - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onToDeviceEvent:) name:kMXSessionOnToDeviceEventNotification object:_crypto.mxSession]; -} - -- (void)onToDeviceEvent:(NSNotification *)notification -{ - MXEvent *event = notification.userInfo[kMXSessionNotificationEventKey]; - - if ([self isVerificationEventType:event.type]) - { - [self handleKeyVerificationEvent:event isToDeviceEvent:YES]; - } -} - -- (MXHTTPOperation*)sendToDevice:(NSString*)userId - deviceId:(NSString*)deviceId - eventType:(NSString*)eventType - content:(NSDictionary*)content - success:(void (^)(void))success - failure:(void (^)(NSError *error))failure -{ - return [self sendToDevices:userId deviceIds:@[deviceId] eventType:eventType content:content success:success failure:failure]; -} - -- (MXHTTPOperation*)sendToDevices:(NSString*)userId - deviceIds:(NSArray*)deviceIds - eventType:(NSString*)eventType - content:(NSDictionary*)content - success:(void (^)(void))success - failure:(void (^)(NSError *error))failure -{ - MXUsersDevicesMap *contentMap = [[MXUsersDevicesMap alloc] init]; - - for (NSString *deviceId in deviceIds) - { - [contentMap setObject:content forUser:userId andDevice:deviceId]; - } - - MXToDevicePayload *payload = [[MXToDevicePayload alloc] initWithEventType:eventType - contentMap:contentMap - transactionId:nil - addMessageId:NO]; // // Should not add anything for verification events as it would break their signatures - return [self.crypto.matrixRestClient sendToDevice:payload success:success failure:failure]; -} - - -#pragma mark DM - -- (void)setupIncomingDMEvents -{ - [_crypto.mxSession listenToEventsOfTypes:kMXKeyVerificationManagerVerificationEventTypes onEvent:^(MXEvent *event, MXTimelineDirection direction, id customObject) { - if (direction == MXTimelineDirectionForwards) - { - [self handleKeyVerificationEvent:event isToDeviceEvent:NO]; - } - }]; -} - -- (BOOL)isVerificationEventType:(MXEventTypeString)type -{ - return [kMXKeyVerificationManagerVerificationEventTypes containsObject:type]; -} - -- (MXHTTPOperation*)sendMessage:(NSString*)userId - roomId:(NSString*)roomId - eventType:(NSString*)eventType - relatedTo:(NSString*)relatedTo - content:(NSDictionary*)content - success:(void (^)(void))success - failure:(void (^)(NSError *error))failure -{ - NSMutableDictionary *eventContent = [content mutableCopy]; - - eventContent[kMXEventRelationRelatesToKey] = @{ - kMXEventContentRelatesToKeyRelationType: MXEventRelationTypeReference, - kMXEventContentRelatesToKeyEventId: relatedTo, - }; - - [eventContent removeObjectForKey:@"transaction_id"]; - - return [self sendEventOfType:eventType toRoom:roomId content:eventContent success:^(NSString *eventId) { - if (success) - { - success(); - } - } failure:failure]; -} - -- (void)setupVericationByDMRequests -{ - NSArray *types = @[ - kMXEventTypeStringRoomMessage - ]; - - [_crypto.mxSession listenToEventsOfTypes:types onEvent:^(MXEvent *event, MXTimelineDirection direction, id customObject) { - if (direction == MXTimelineDirectionForwards - && [event.content[kMXMessageTypeKey] isEqualToString:kMXMessageTypeKeyVerificationRequest]) - { - MXKeyVerificationByDMRequest *requestByDM = [[MXKeyVerificationByDMRequest alloc] initWithEvent:event andManager:self]; - if (requestByDM) - { - [self handleKeyVerificationRequestByDM:requestByDM event:event]; - } - } - }]; -} - - -- (void)handleKeyVerificationRequestByDM:(MXKeyVerificationByDMRequest*)request event:(MXEvent*)event -{ - MXLogDebug(@"[MXKeyVerification] handleKeyVerificationRequestByDM: %@", request); - - if (![request.request.to isEqualToString:self.crypto.mxSession.myUserId]) - { - MXLogDebug(@"[MXKeyVerification] handleKeyVerificationRequestByDM: Request for another user: %@", request.request.to); - return; - } - - MXWeakify(self); - dispatch_async(dispatch_get_main_queue(), ^{ - MXStrongifyAndReturnIfNil(self); - - // This is a live event, we should have all data - [self->statusResolver keyVerificationWithKeyVerificationId:request.requestId event:event transport:MXKeyVerificationTransportDirectMessage success:^(MXKeyVerification * _Nonnull keyVerification) { - - if (keyVerification.request.state == MXKeyVerificationRequestStatePending) - { - [self addPendingRequest:request notify:YES]; - } - - } failure:^(NSError *error) { - MXLogDebug(@"[MXKeyVerificationRequest] handleKeyVerificationRequestByDM: Failed to resolve state: %@", request.requestId); - }]; - }); -} - - - -#pragma mark - Private methods - - -- (void)loadDeviceWithDeviceId:(NSString*)deviceId - andUserId:(NSString*)userId - success:(void (^)(MXDeviceInfo *otherDevice))success - failure:(void (^)(NSError *error))failure -{ - MXWeakify(self); - [_crypto downloadKeys:@[userId] forceDownload:NO success:^(MXUsersDevicesMap *usersDevicesInfoMap, NSDictionary *crossSigningKeysMap) { - MXStrongifyAndReturnIfNil(self); - - dispatch_async(self->cryptoQueue, ^{ - MXDeviceInfo *otherDevice = [usersDevicesInfoMap objectForDevice:deviceId forUser:userId]; - if (otherDevice) - { - success(otherDevice); - } - else - { - NSError *error = [NSError errorWithDomain:MXKeyVerificationErrorDomain - code:MXKeyVerificationUnknownDeviceCode - userInfo:@{ - NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Unknown device: %@:%@", userId, deviceId] - }]; - failure(error); - } - }); - - } failure:failure]; -} - -/** - Send a message to a room even if it is e2e encrypted. - This may require to mark unknown devices as known, which is legitimate because - we are going to verify them or their user. - */ -- (MXHTTPOperation*)sendEventOfType:(MXEventTypeString)eventType - toRoom:(NSString*)roomId - content:(NSDictionary*)content - success:(void (^)(NSString *eventId))success - failure:(void (^)(NSError *error))failure -{ - // Check we have a room - MXRoom *room = [_crypto.mxSession roomWithRoomId:roomId]; - if (!room) - { - NSError *error = [NSError errorWithDomain:MXKeyVerificationErrorDomain - code:MXKeyVerificationUnknownRoomCode - userInfo:@{ - NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Unknown room: %@", roomId] - }]; - if (failure) - { - failure(error); - } - return nil; - } - - MXHTTPOperation *operation = [MXHTTPOperation new]; - operation = [room sendEventOfType:eventType content:content threadId:nil localEcho:nil success:success failure:^(NSError *error) { - - if ([error.domain isEqualToString:MXEncryptingErrorDomain] && - error.code == MXEncryptingErrorUnknownDeviceCode) - { - // Acknownledge unknown devices - MXUsersDevicesMap *unknownDevices = error.userInfo[MXEncryptingErrorUnknownDeviceDevicesKey]; - [self.crypto setDevicesKnown:unknownDevices complete:^{ - // And retry - MXHTTPOperation *operation2 = [room sendEventOfType:eventType content:content threadId:nil localEcho:nil success:success failure:failure]; - [operation mutateTo:operation2]; - }]; - } - else if (failure) - { - failure(error); - } - }]; - - return operation; -} - -- (void)computeReadyMethodsFromVerificationRequestWithId:(NSString*)transactionId - andSupportedMethods:(NSArray*)supportedMethods - completion:(void (^)(NSArray* readyMethods, MXQRCodeData *qrCodeData))completion; -{ - MXLegacyKeyVerificationRequest *keyVerificationRequest = [self pendingRequestWithRequestId:transactionId]; - - if (!keyVerificationRequest) - { - MXLogDebug(@"[MXKeyVerification] computeReadyMethodsFromVerificationRequestWithId: Failed to find request with ID: %@", transactionId); - } - - NSMutableSet *readyMethods = [NSMutableSet new]; - MXQRCodeData *outputQRCodeData; - - NSArray *incomingMethods = keyVerificationRequest.methods; - - if ([incomingMethods containsObject:MXKeyVerificationMethodSAS] && [supportedMethods containsObject:MXKeyVerificationMethodSAS]) - { - // Other can do SAS and so do I - [readyMethods addObject:MXKeyVerificationMethodSAS]; - } - - if ([incomingMethods containsObject:MXKeyVerificationMethodQRCodeScan] || [incomingMethods containsObject:MXKeyVerificationMethodQRCodeShow]) - { - // Other user wants to verify using QR code. Cross-signing has to be setup - MXQRCodeData *qrCodeData = [self createQRCodeDataWithTransactionId:keyVerificationRequest.requestId - otherUserId:keyVerificationRequest.otherUser - otherDeviceId:keyVerificationRequest.fromDevice]; - - if (qrCodeData) - { - if ([incomingMethods containsObject:MXKeyVerificationMethodQRCodeScan] && [supportedMethods containsObject:MXKeyVerificationMethodQRCodeShow]) - { - // Other can Scan and I can show QR code - [readyMethods addObject:MXKeyVerificationMethodQRCodeShow]; - [readyMethods addObject:MXKeyVerificationMethodReciprocate]; - } - - if ([incomingMethods containsObject:MXKeyVerificationMethodQRCodeShow] && [supportedMethods containsObject:MXKeyVerificationMethodQRCodeScan]) - { - // Other can show and I can scan QR code - [readyMethods addObject:MXKeyVerificationMethodQRCodeScan]; - [readyMethods addObject:MXKeyVerificationMethodReciprocate]; - } - - if ([readyMethods containsObject:MXKeyVerificationMethodReciprocate]) - { - outputQRCodeData = qrCodeData; - } - } - } - - completion([readyMethods allObjects], outputQRCodeData); -} - -- (BOOL)isOtherQRCodeDataKeysValid:(MXQRCodeData*)otherQRCodeData otherUserId:(NSString*)otherUserId otherDevice:(MXDeviceInfo*)otherDevice -{ - BOOL isOtherQRCodeDataValid = YES; - - id crossSigning = self.crypto.crossSigning; - - NSString *masterKeyPublic = crossSigning.myUserCrossSigningKeys.masterKeys.keys; - - if ([otherQRCodeData isMemberOfClass:MXVerifyingAnotherUserQRCodeData.class]) - { - MXVerifyingAnotherUserQRCodeData *verifyingAnotherUserQRCodeData = (MXVerifyingAnotherUserQRCodeData*)otherQRCodeData; - - MXCrossSigningInfo *otherUserCrossSigningKeys = [self.crypto.crossSigning crossSigningKeysForUser:otherUserId]; - NSString *otherUserMasterKeyPublic = otherUserCrossSigningKeys.masterKeys.keys; - - // verifyingAnotherUserQRCodeData.otherUserCrossSigningMasterKeyPublic -> Current user master key public - if (![verifyingAnotherUserQRCodeData.otherUserCrossSigningMasterKeyPublic isEqualToString:masterKeyPublic]) - { - MXLogDebug(@"[MXKeyVerification] checkOtherQRCodeData: Invalid other master key %@", verifyingAnotherUserQRCodeData.otherUserCrossSigningMasterKeyPublic); - isOtherQRCodeDataValid = NO; - } - // verifyingAnotherUserQRCodeData.userCrossSigningMasterKeyPublic -> Other user master key public - else if (![verifyingAnotherUserQRCodeData.userCrossSigningMasterKeyPublic isEqualToString:otherUserMasterKeyPublic]) - { - MXLogDebug(@"[MXKeyVerification] checkOtherQRCodeData: Invalid user master key %@", verifyingAnotherUserQRCodeData.userCrossSigningMasterKeyPublic); - isOtherQRCodeDataValid = NO; - } - } - else if ([otherQRCodeData isMemberOfClass:MXSelfVerifyingMasterKeyTrustedQRCodeData.class]) - { - MXSelfVerifyingMasterKeyTrustedQRCodeData *selfVerifyingMasterKeyTrustedQRCodeData = (MXSelfVerifyingMasterKeyTrustedQRCodeData*)otherQRCodeData; - - NSString *currentDeviceKey = self.currentDevice.fingerprint; - - // selfVerifyingMasterKeyTrustedQRCodeData.userCrossSigningMasterKeyPublic -> Current user master key public - if (![selfVerifyingMasterKeyTrustedQRCodeData.userCrossSigningMasterKeyPublic isEqualToString:masterKeyPublic]) - { - MXLogDebug(@"[MXKeyVerification] checkOtherQRCodeData: Invalid user master key %@", selfVerifyingMasterKeyTrustedQRCodeData.userCrossSigningMasterKeyPublic); - isOtherQRCodeDataValid = NO; - } - // selfVerifyingMasterKeyTrustedQRCodeData.otherDeviceKey -> Current device key - else if (![selfVerifyingMasterKeyTrustedQRCodeData.otherDeviceKey isEqualToString:currentDeviceKey]) - { - MXLogDebug(@"[MXKeyVerification] checkOtherQRCodeData: Invalid other device key %@", selfVerifyingMasterKeyTrustedQRCodeData.otherDeviceKey); - isOtherQRCodeDataValid = NO; - } - } - else if ([otherQRCodeData isMemberOfClass:MXSelfVerifyingMasterKeyNotTrustedQRCodeData.class]) - { - MXSelfVerifyingMasterKeyNotTrustedQRCodeData *selfVerifyingMasterKeyNotTrustedQRCodeData = (MXSelfVerifyingMasterKeyNotTrustedQRCodeData*)otherQRCodeData; - NSString *otherDeviceKey = otherDevice.fingerprint; - - // selfVerifyingMasterKeyNotTrustedQRCodeData.currentDeviceKey -> other device key - if (![selfVerifyingMasterKeyNotTrustedQRCodeData.currentDeviceKey isEqualToString:otherDeviceKey]) - { - MXLogDebug(@"[MXKeyVerification] checkOtherQRCodeData: Current device key %@", selfVerifyingMasterKeyNotTrustedQRCodeData.currentDeviceKey); - isOtherQRCodeDataValid = NO; - } - // selfVerifyingMasterKeyNotTrustedQRCodeData.userCrossSigningMasterKeyPublic -> Current user master key public - else if (![selfVerifyingMasterKeyNotTrustedQRCodeData.userCrossSigningMasterKeyPublic isEqualToString:masterKeyPublic]) - { - MXLogDebug(@"[MXKeyVerification] checkOtherQRCodeData: Invalid user master key %@", selfVerifyingMasterKeyNotTrustedQRCodeData.userCrossSigningMasterKeyPublic); - isOtherQRCodeDataValid = NO; - } - } - - return isOtherQRCodeDataValid; -} - -#pragma mark - Requests queue - -- (nullable MXKeyVerificationByDMRequest*)verificationRequestInDMEvent:(MXEvent*)event -{ - MXKeyVerificationByDMRequest *request; - if ([event.content[kMXMessageTypeKey] isEqualToString:kMXMessageTypeKeyVerificationRequest]) - { - request = [[MXKeyVerificationByDMRequest alloc] initWithEvent:event andManager:self]; - } - return request; -} - -- (nullable MXLegacyKeyVerificationRequest*)pendingRequestWithRequestId:(NSString*)requestId -{ - return pendingRequestsMap[requestId]; -} - -- (void)addPendingRequest:(id)request notify:(BOOL)notify -{ - if (!pendingRequestsMap[request.requestId]) - { - pendingRequestsMap[request.requestId] = request; - - if (notify) - { - dispatch_async(dispatch_get_main_queue(),^{ - [[NSNotificationCenter defaultCenter] postNotificationName:MXKeyVerificationManagerNewRequestNotification object:self userInfo: - @{ - MXKeyVerificationManagerNotificationRequestKey: request - }]; - }); - } - } - [self scheduleRequestTimeoutTimer]; -} - -- (void)removePendingRequestWithRequestId:(NSString*)requestId -{ - if (pendingRequestsMap[requestId]) - { - [pendingRequestsMap removeObjectForKey:requestId]; - [self scheduleRequestTimeoutTimer]; - } -} - - -#pragma mark - Timeout management - -- (nullable NSDate*)oldestRequestDate -{ - NSDate *oldestRequestDate; - for (MXLegacyKeyVerificationRequest *request in pendingRequestsMap.allValues) - { - if (!oldestRequestDate - || request.timestamp < oldestRequestDate.timeIntervalSince1970) - { - oldestRequestDate = [NSDate dateWithTimeIntervalSince1970:(request.timestamp / 1000)]; - } - } - return oldestRequestDate; -} - -- (BOOL)isRequestStillValid:(MXLegacyKeyVerificationRequest *)request -{ - NSDate *requestDate = [NSDate dateWithTimeIntervalSince1970:(request.timestamp / 1000)]; - return (requestDate.timeIntervalSinceNow > -_requestTimeout); -} - -- (void)scheduleRequestTimeoutTimer -{ - if (requestTimeoutTimer) - { - if (!pendingRequestsMap.count) - { - MXLogDebug(@"[MXKeyVerificationRequest] scheduleTimeoutTimer: Disable timer as there is no more requests"); - [requestTimeoutTimer invalidate]; - requestTimeoutTimer = nil; - } - - return; - } - - NSDate *oldestRequestDate = [self oldestRequestDate]; - if (oldestRequestDate) - { - NSDate *timeoutDate = [oldestRequestDate dateByAddingTimeInterval:self.requestTimeout]; - MXLogDebug(@"[MXKeyVerificationRequest] scheduleTimeoutTimer: Create timer"); - MXWeakify(self); - requestTimeoutTimer = [[NSTimer alloc] initWithFireDate:timeoutDate - interval:0 - repeats:NO - block:^(NSTimer * _Nonnull timer) { - MXStrongifyAndReturnIfNil(self); - [self onRequestTimeoutTimer]; - }]; - [[NSRunLoop mainRunLoop] addTimer:requestTimeoutTimer forMode:NSDefaultRunLoopMode]; - } -} - -- (void)onRequestTimeoutTimer -{ - MXLogDebug(@"[MXKeyVerificationRequest] onTimeoutTimer"); - requestTimeoutTimer = nil; - - [self checkRequestTimeoutsWithCompletion:^{ - [self scheduleRequestTimeoutTimer]; - }]; -} - -- (void)checkRequestTimeoutsWithCompletion:(dispatch_block_t)completionBlock -{ - dispatch_group_t group = dispatch_group_create(); - for (MXLegacyKeyVerificationRequest *request in pendingRequestsMap.allValues) - { - if (![self isRequestStillValid:request]) - { - MXLogDebug(@"[MXKeyVerificationRequest] checkTimeouts: timeout %@", request); - - dispatch_group_enter(group); - [request cancelWithCancelCode:MXTransactionCancelCode.timeout success:^{ - dispatch_group_leave(group); - } failure:^(NSError * _Nonnull error) { - dispatch_group_leave(group); - }]; - } - } - - dispatch_group_notify(group, dispatch_get_main_queue(), completionBlock); -} - - -#pragma mark - Transactions queue - -- (id)transactionWithUser:(NSString*)userId andDevice:(NSString*)deviceId -{ - return [transactions objectForDevice:deviceId forUser:userId]; -} - -- (NSArray>*)transactionsWithUser:(NSString*)userId -{ - return [transactions objectsForUser:userId]; -} - -- (MXLegacyKeyVerificationTransaction *)transactionWithTransactionId:(NSString*)transactionId -{ - MXLegacyKeyVerificationTransaction *transaction; - for (MXLegacyKeyVerificationTransaction *t in transactions.allObjects) - { - if ([t.transactionId isEqualToString:transactionId]) - { - transaction = t; - break; - } - } - - return transaction; -} - -- (MXLegacySASTransaction *)sasTransactionWithTransactionId:(NSString*)transactionId -{ - MXLegacySASTransaction *sasTransaction; - - MXLegacyKeyVerificationTransaction *transaction = [self transactionWithTransactionId:transactionId]; - - if ([transaction isKindOfClass:[MXLegacySASTransaction class]]) - { - sasTransaction = (MXLegacySASTransaction *)transaction; - } - - return sasTransaction; -} - -- (id)qrCodeTransactionWithTransactionId:(NSString*)transactionId -{ - MXLegacyQRCodeTransaction *qrCodeTransaction; - - id transaction = [self transactionWithTransactionId:transactionId]; - - if ([transaction isKindOfClass:MXLegacyQRCodeTransaction.class]) - { - qrCodeTransaction = (MXLegacyQRCodeTransaction *)transaction; - } - - return qrCodeTransaction; -} - -- (void)addTransaction:(id)transaction -{ - [transactions setObject:transaction forUser:transaction.otherUserId andDevice:transaction.otherDeviceId]; - [self scheduleTransactionTimeoutTimer]; - - dispatch_async(dispatch_get_main_queue(),^{ - [[NSNotificationCenter defaultCenter] postNotificationName:MXKeyVerificationManagerNewTransactionNotification object:self userInfo: - @{ - MXKeyVerificationManagerNotificationTransactionKey: transaction - }]; - }); -} - -- (void)removeTransactionWithTransactionId:(NSString*)transactionId -{ - id transaction = [self transactionWithTransactionId:transactionId]; - if (transaction) - { - [transactions removeObjectForUser:transaction.otherUserId andDevice:transaction.otherDeviceId]; - [self scheduleTransactionTimeoutTimer]; - } -} - -- (nullable NSDate*)oldestTransactionCreationDate -{ - NSDate *oldestCreationDate; - for (MXLegacyKeyVerificationTransaction *transaction in transactions.allObjects) - { - if (!oldestCreationDate - || transaction.creationDate.timeIntervalSince1970 < oldestCreationDate.timeIntervalSince1970) - { - oldestCreationDate = transaction.creationDate; - } - } - return oldestCreationDate; -} - -- (BOOL)isCreationDateValid:(MXLegacyKeyVerificationTransaction *)transaction -{ - return (transaction.creationDate.timeIntervalSinceNow > -MXTransactionTimeout); -} - - -#pragma mark Timeout management - -- (void)scheduleTransactionTimeoutTimer -{ - if (transactionTimeoutTimer) - { - if (!transactions.count) - { - MXLogDebug(@"[MXKeyVerification] scheduleTimeoutTimer: Disable timer as there is no more transactions"); - [transactionTimeoutTimer invalidate]; - transactionTimeoutTimer = nil; - } - - return; - } - - NSDate *oldestCreationDate = [self oldestTransactionCreationDate]; - if (oldestCreationDate) - { - MXWeakify(self); - dispatch_async(dispatch_get_main_queue(), ^{ - MXStrongifyAndReturnIfNil(self); - - if (self->transactionTimeoutTimer) - { - return; - } - - MXLogDebug(@"[MXKeyVerification] scheduleTimeoutTimer: Create timer"); - - NSDate *timeoutDate = [oldestCreationDate dateByAddingTimeInterval:MXTransactionTimeout]; - - MXWeakify(self); - self->transactionTimeoutTimer = [[NSTimer alloc] initWithFireDate:timeoutDate - interval:0 - repeats:NO - block:^(NSTimer * _Nonnull timer) { - MXStrongifyAndReturnIfNil(self); - [self onTransactionTimeoutTimer]; - }]; - [[NSRunLoop mainRunLoop] addTimer:self->transactionTimeoutTimer forMode:NSDefaultRunLoopMode]; - }); - } -} - -- (void)onTransactionTimeoutTimer -{ - MXLogDebug(@"[MXKeyVerification] onTimeoutTimer"); - self->transactionTimeoutTimer = nil; - - if (cryptoQueue) - { - dispatch_async(cryptoQueue, ^{ - [self checkTransactionTimeouts]; - [self scheduleTransactionTimeoutTimer]; - }); - } -} - -- (void)checkTransactionTimeouts -{ - for (MXLegacyKeyVerificationTransaction *transaction in transactions.allObjects) - { - if (![self isCreationDateValid:transaction]) - { - MXLogDebug(@"[MXKeyVerification] checkTimeouts: timeout %@", transaction); - [transaction cancelWithCancelCode:MXTransactionCancelCode.timeout]; - } - } -} - -#pragma mark - QR Code - -- (MXQRCodeData*)createQRCodeDataWithTransactionId:(NSString*)transactionId otherUserId:(NSString*)otherUserId otherDeviceId:(NSString*)otherDeviceId -{ - MXQRCodeData *qrCodeData; - - NSString *currentUserId = self.crypto.mxSession.myUserId; - MXUserTrustLevel *currentUserTrustLevel = [self.crypto trustLevelForUser:currentUserId]; - - if ([otherUserId isEqualToString:currentUserId]) - { - if (currentUserTrustLevel.isCrossSigningVerified) - { - // This is a self verification and I am the old device (Osborne2) - qrCodeData = [self createSelfVerifyingMasterKeyTrustedQRCodeDataWithTransactionId:transactionId otherDeviceId:otherDeviceId]; - } - else - { - // This is a self verification and I am the new device (Dynabook) - qrCodeData = [self createSelfVerifyingMasterKeyNotTrustedQRCodeDataWithTransactionId:transactionId]; - } - } - else - { - qrCodeData = [self createVerifyingAnotherUserQRCodeDataWithTransactionId:transactionId otherUserId:otherUserId]; - } - - return qrCodeData; -} - -- (MXVerifyingAnotherUserQRCodeData*)createVerifyingAnotherUserQRCodeDataWithTransactionId:(NSString*)transactionId - otherUserId:(NSString*)otherUserId -{ - MXCrossSigningInfo *myUserCrossSigningKeys = self.crypto.crossSigning.myUserCrossSigningKeys; - MXCrossSigningInfo *otherUserCrossSigningKeys = [self.crypto.crossSigning crossSigningKeysForUser:otherUserId]; - - NSString *userCrossSigningMasterKeyPublic = myUserCrossSigningKeys.masterKeys.keys; - NSString *otherUserCrossSigningMasterKeyPublic = otherUserCrossSigningKeys.masterKeys.keys; - - if (!userCrossSigningMasterKeyPublic || !otherUserCrossSigningMasterKeyPublic) - { - MXLogDebug(@"[MXKeyVerification] createVerifyingAnotherUserQRCodeData fails to get userCrossSigningMasterKeyPublic or otherUserCrossSigningMasterKeyPublic"); - return nil; - } - - return [self.qrCodeDataBuilder buildVerifyingAnotherUserQRCodeDataWithTransactionId:transactionId - userCrossSigningMasterKeyPublic:userCrossSigningMasterKeyPublic - otherUserCrossSigningMasterKeyPublic:otherUserCrossSigningMasterKeyPublic]; -} - -// Create a QR code to display on the old device (Osborne2) of current user -- (MXSelfVerifyingMasterKeyTrustedQRCodeData*)createSelfVerifyingMasterKeyTrustedQRCodeDataWithTransactionId:(NSString*)transactionId - otherDeviceId:(NSString*)otherDeviceId -{ - MXCrossSigningInfo *myUserCrossSigningKeys = self.crypto.crossSigning.myUserCrossSigningKeys; - NSString *currentUserId = self.crypto.mxSession.myUserId; - MXDeviceInfo *otherDevice = [self.crypto deviceWithDeviceId:otherDeviceId ofUser:currentUserId]; - - NSString *userCrossSigningMasterKeyPublic = myUserCrossSigningKeys.masterKeys.keys; - NSString *otherDeviceKey = otherDevice.fingerprint; - - if (!userCrossSigningMasterKeyPublic || !otherDeviceKey) - { - MXLogDebug(@"[MXKeyVerification] createSelfVerifyingMasterKeyTrustedQRCodeData fails to get userCrossSigningMasterKeyPublic or otherDeviceKey"); - return nil; - } - - return [self.qrCodeDataBuilder buildSelfVerifyingMasterKeyTrustedQRCodeDataWithTransactionId:transactionId - userCrossSigningMasterKeyPublic:userCrossSigningMasterKeyPublic - otherDeviceKey:otherDeviceKey]; -} - -// Create a QR code to display on the new device (Dynabook) of current user -- (MXSelfVerifyingMasterKeyNotTrustedQRCodeData*)createSelfVerifyingMasterKeyNotTrustedQRCodeDataWithTransactionId:(NSString*)transactionId -{ - MXCrossSigningInfo *myUserCrossSigningKeys = self.crypto.crossSigning.myUserCrossSigningKeys; - - NSString *userCrossSigningMasterKeyPublic = myUserCrossSigningKeys.masterKeys.keys; - NSString *currentDeviceKey = self.currentDevice.fingerprint; - - if (!userCrossSigningMasterKeyPublic || !currentDeviceKey) - { - MXLogDebug(@"[MXKeyVerification] createSelfVerifyingMasterKeyNotTrustedQRCodeData fails to get userCrossSigningMasterKeyPublic or currentDeviceKey"); - return nil; - } - - return [self.qrCodeDataBuilder buildSelfVerifyingMasterKeyNotTrustedQRCodeDataWithTransactionId:transactionId - currentDeviceKey:currentDeviceKey - userCrossSigningMasterKeyPublic:userCrossSigningMasterKeyPublic]; -} - -- (MXDeviceInfo*)currentDevice -{ - NSString *currentUserId = self.crypto.mxSession.myUserId; - NSString *currentDeviceId = self.crypto.mxSession.matrixRestClient.credentials.deviceId; - return [self.crypto deviceWithDeviceId:currentDeviceId ofUser:currentUserId]; -} - -@end diff --git a/MatrixSDK/Crypto/Verification/MXKeyVerificationManager_Private.h b/MatrixSDK/Crypto/Verification/MXKeyVerificationManager_Private.h deleted file mode 100644 index 3f7f6c5a6a..0000000000 --- a/MatrixSDK/Crypto/Verification/MXKeyVerificationManager_Private.h +++ /dev/null @@ -1,143 +0,0 @@ -/* - 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. - */ - -#import "MXKeyVerificationManager.h" - -#import "MXKeyVerificationTransaction_Private.h" - -@class MXLegacyCrypto; -@class MXQRCodeData; - -NS_ASSUME_NONNULL_BEGIN - -/** - The `MXKeyBackup_Private` extension exposes internal operations. - */ -@interface MXLegacyKeyVerificationManager () - -/** - The Matrix crypto. - */ -@property (nonatomic, readonly, weak) MXLegacyCrypto *crypto; - -/** - Constructor. - - @param crypto the related 'MXCrypto'. - */ -- (instancetype)initWithCrypto:(MXLegacyCrypto *)crypto; - - -#pragma mark - Requests - -/** - Send a message to the other peer in a device verification request. - - @param request the request to talk trough. - @param eventType the message type. - @param content the message content. - - @param success A block object called when the operation succeeds. - @param failure A block object called when the operation fails. - - @return a MXHTTPOperation instance. - */ -- (MXHTTPOperation*)sendToOtherInRequest:(id)request - eventType:(NSString*)eventType - content:(NSDictionary*)content - success:(dispatch_block_t)success - failure:(void (^)(NSError *error))failure; - -/** - Cancel a key verification request or reject an incoming key verification request. - - @param request the request. - @param success a block called when the operation succeeds. - @param failure a block called when the operation fails. - */ -- (void)cancelVerificationRequest:(id)request - success:(void(^)(void))success - failure:(void(^)(NSError *error))failure; - -- (BOOL)isRequestStillValid:(id)request; - -- (void)removePendingRequestWithRequestId:(NSString*)requestId; - -- (void)computeReadyMethodsFromVerificationRequestWithId:(NSString*)transactionId - andSupportedMethods:(NSArray*)supportedMethods - completion:(void (^)(NSArray* readyMethods, MXQRCodeData * _Nullable qrCodeData))completion; - -- (MXQRCodeData*)createQRCodeDataWithTransactionId:(NSString*)transactionId otherUserId:(NSString*)otherUserId otherDeviceId:(NSString*)otherDeviceId; - -- (void)createQRCodeTransactionWithQRCodeData:(nullable MXQRCodeData*)qrCodeData - userId:(NSString*)userId - deviceId:(NSString*)deviceId - transactionId:(nullable NSString*)transactionId - dmRoomId:(nullable NSString*)dmRoomId - dmEventId:(nullable NSString*)dmEventId - success:(void(^)(MXLegacyQRCodeTransaction *transaction))success - failure:(void(^)(NSError *error))failure; - -- (void)createQRCodeTransactionFromRequest:(id)request - qrCodeData:(nullable MXQRCodeData*)qrCodeData - success:(void(^)(MXLegacyQRCodeTransaction *transaction))success - failure:(void(^)(NSError *error))failure; - -- (BOOL)isOtherQRCodeDataKeysValid:(MXQRCodeData*)otherQRCodeData otherUserId:(NSString*)otherUserId otherDevice:(MXDeviceInfo*)otherDevice; - -#pragma mark - Transactions - -/** - Send a message to the other peer in a device verification transaction. - - @param transaction the transation to talk trough. - @param eventType the message type. - @param content the message content. - - @param success A block object called when the operation succeeds. - @param failure A block object called when the operation fails. - - @return a MXHTTPOperation instance. - */ -- (MXHTTPOperation*)sendToOtherInTransaction:(id)transaction - eventType:(NSString*)eventType - content:(NSDictionary*)content - success:(void (^)(void))success - failure:(void (^)(NSError *error))failure; - -/** - Cancel a transaction. Send a cancellation event to the other peer. - - @param transaction the transaction to cancel. - @param code the cancellation reason. - @param success A block object called when the operation succeeds. - @param failure A block object called when the operation fails. - */ -- (void)cancelTransaction:(id)transaction - code:(MXTransactionCancelCode*)code - success:(void (^)(void))success - failure:(void (^)(NSError *error))failure; - -/** - Remove a transaction from the queue. - - @param transactionId the transaction to remove. - */ -- (void)removeTransactionWithTransactionId:(NSString*)transactionId; - -@end - -NS_ASSUME_NONNULL_END diff --git a/MatrixSDK/Crypto/Verification/Requests/MXKeyVerificationByDMRequest.h b/MatrixSDK/Crypto/Verification/Requests/MXKeyVerificationByDMRequest.h deleted file mode 100644 index 53960212b4..0000000000 --- a/MatrixSDK/Crypto/Verification/Requests/MXKeyVerificationByDMRequest.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - Copyright 2019 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 - -#import "MXKeyVerificationRequest.h" -#import "MXKeyVerificationRequestByDMJSONModel.h" - -@class MXEvent; - - -NS_ASSUME_NONNULL_BEGIN - -/** - An handler on an interactive request for verification by Direct Message. - */ -@interface MXKeyVerificationByDMRequest : MXLegacyKeyVerificationRequest - -@property (nonatomic, readonly) NSString *eventId; - -@property (nonatomic, readonly) MXKeyVerificationRequestByDMJSONModel *request; - -@end - -NS_ASSUME_NONNULL_END diff --git a/MatrixSDK/Crypto/Verification/Requests/MXKeyVerificationByDMRequest.m b/MatrixSDK/Crypto/Verification/Requests/MXKeyVerificationByDMRequest.m deleted file mode 100644 index 7e1735159e..0000000000 --- a/MatrixSDK/Crypto/Verification/Requests/MXKeyVerificationByDMRequest.m +++ /dev/null @@ -1,91 +0,0 @@ -/* - Copyright 2019 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 "MXKeyVerificationByDMRequest.h" - -#import "MXKeyVerificationRequest_Private.h" -#import "MXKeyVerificationManager_Private.h" -#import "MXCrypto_Private.h" - -#import "MXEvent.h" - - -@implementation MXKeyVerificationByDMRequest - -- (instancetype)initWithEvent:(MXEvent*)event andManager:(MXLegacyKeyVerificationManager*)manager -{ - // Check verification by DM request format - MXKeyVerificationRequestByDMJSONModel *request; - MXJSONModelSetMXJSONModel(request, MXKeyVerificationRequestByDMJSONModel.class, event.content); - - if (!request) - { - return nil; - } - - self = [super initWithEvent:event andManager:manager]; - if (self) - { - _request = request; - _eventId = event.eventId; - - MXCredentials *myCreds = manager.crypto.mxSession.matrixRestClient.credentials; - self.isFromMyUser = [event.sender isEqualToString:myCreds.userId]; - self.isFromMyDevice = [request.fromDevice isEqualToString:myCreds.deviceId]; - } - return self; -} - - -// Shortcuts -- (NSString *)requestId -{ - return self.event.eventId; -} - -- (MXKeyVerificationTransport)transport -{ - return MXKeyVerificationTransportDirectMessage; -} - -- (NSString *)fromDevice -{ - return _request.fromDevice; -} - -- (uint64_t)timestamp -{ - return self.event.ageLocalTs; -} - -- (NSArray *)methods -{ - return _request.methods; -} - -// Shortcuts to the original request -- (NSString *)otherUser -{ - return self.isFromMyUser ? _request.to : self.event.sender; -} - -- (NSString *)otherDevice -{ - return self.isFromMyDevice ? self.acceptedData.fromDevice : _request.fromDevice; -} - -@end diff --git a/MatrixSDK/Crypto/Verification/Requests/MXKeyVerificationByToDeviceRequest.h b/MatrixSDK/Crypto/Verification/Requests/MXKeyVerificationByToDeviceRequest.h deleted file mode 100644 index c491703149..0000000000 --- a/MatrixSDK/Crypto/Verification/Requests/MXKeyVerificationByToDeviceRequest.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - Copyright 2020 The Matrix.org Foundation C.I.C - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -#import - -#import "MXKeyVerificationRequest.h" -#import "MXKeyVerificationRequestByToDeviceJSONModel.h" - -@class MXLegacyKeyVerificationManager; - -NS_ASSUME_NONNULL_BEGIN - -/** - An handler on an interactive request for verification by to_device events. - */ -@interface MXKeyVerificationByToDeviceRequest : MXLegacyKeyVerificationRequest - -@property (nonatomic, readonly) MXKeyVerificationRequestByToDeviceJSONModel *request; - -// The recipient user id for this request -@property (nonatomic, readonly) NSString *to; - -// The requested recipient device ids. Note that `otherDevice` is the device id that accepts the request first. Only filled when request is sent bu current user otherwise this array should be empty. -@property (nonatomic, readonly) NSArray *requestedOtherDeviceIds; - -- (instancetype)initWithEvent:(MXEvent*)event andManager:(MXLegacyKeyVerificationManager*)manager to:(NSString*)toUserId requestedOtherDeviceIds:(NSArray*)otherDeviceIds; - -@end - -NS_ASSUME_NONNULL_END diff --git a/MatrixSDK/Crypto/Verification/Requests/MXKeyVerificationByToDeviceRequest.m b/MatrixSDK/Crypto/Verification/Requests/MXKeyVerificationByToDeviceRequest.m deleted file mode 100644 index 6d9023b927..0000000000 --- a/MatrixSDK/Crypto/Verification/Requests/MXKeyVerificationByToDeviceRequest.m +++ /dev/null @@ -1,98 +0,0 @@ -/* - Copyright 2020 The Matrix.org Foundation C.I.C - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -#import "MXKeyVerificationByToDeviceRequest.h" - -#import "MXKeyVerificationRequest_Private.h" -#import "MXKeyVerificationManager_Private.h" -#import "MXCrypto_Private.h" - -#import "MXEvent.h" - -@interface MXKeyVerificationByToDeviceRequest() - -@property (nonatomic, readwrite) NSArray *requestedOtherDeviceIds; - -@end - -@implementation MXKeyVerificationByToDeviceRequest - -- (instancetype)initWithEvent:(MXEvent*)event andManager:(MXLegacyKeyVerificationManager*)manager to:(nonnull NSString *)toUserId requestedOtherDeviceIds:(nonnull NSArray *)requestedOtherDeviceIds -{ - // Check verification by DM request format - MXKeyVerificationRequestByToDeviceJSONModel *request; - MXJSONModelSetMXJSONModel(request, MXKeyVerificationRequestByToDeviceJSONModel.class, event.content); - - if (!request) - { - return nil; - } - - self = [super initWithEvent:event andManager:manager]; - if (self) - { - _request = request; - _to = toUserId; - - MXCredentials *myCreds = manager.crypto.mxSession.matrixRestClient.credentials; - self.isFromMyUser = [event.sender isEqualToString:myCreds.userId]; - self.isFromMyDevice = [request.fromDevice isEqualToString:myCreds.deviceId]; - - _requestedOtherDeviceIds = self.isFromMyDevice ? requestedOtherDeviceIds : @[]; - } - return self; -} - - -// Shortcuts -- (NSString *)requestId -{ - return _request.transactionId; -} - -- (MXKeyVerificationTransport)transport -{ - return MXKeyVerificationTransportToDevice; -} - -- (NSString *)fromDevice -{ - return _request.fromDevice; -} - -- (NSArray *)methods -{ - return _request.methods; -} - -- (uint64_t)timestamp -{ - return _request.timestamp; -} - - -// Shortcuts to the original request -- (NSString *)otherUser -{ - return self.isFromMyUser ? _to : self.event.sender; -} - -- (NSString *)otherDevice -{ - return self.isFromMyDevice ? self.acceptedData.fromDevice : _request.fromDevice; -} - -@end diff --git a/MatrixSDK/Crypto/Verification/Requests/MXKeyVerificationRequest.h b/MatrixSDK/Crypto/Verification/Requests/MXKeyVerificationRequest.h index a0b864ba3e..2e9927d545 100644 --- a/MatrixSDK/Crypto/Verification/Requests/MXKeyVerificationRequest.h +++ b/MatrixSDK/Crypto/Verification/Requests/MXKeyVerificationRequest.h @@ -98,22 +98,4 @@ Accept an incoming key verification request. @end -/** - Default implementation of verification request used by the SDK - */ -@interface MXLegacyKeyVerificationRequest : NSObject - -// Original data for this request -@property (nonatomic, readonly) MXEvent *event; - -// Shortcuts to the accepted event -@property (nonatomic, readonly) NSString *fromDevice; -@property (nonatomic, readonly) uint64_t timestamp; - -// Original data from the accepted (aka m.verification.ready) event -@property (nonatomic, readonly, nullable) MXKeyVerificationReady *acceptedData; -@property (nonatomic, readonly, nullable) NSArray *acceptedMethods; - -@end - NS_ASSUME_NONNULL_END diff --git a/MatrixSDK/Crypto/Verification/Requests/MXKeyVerificationRequest.m b/MatrixSDK/Crypto/Verification/Requests/MXKeyVerificationRequest.m index 4dd196cb16..d12e22e1e1 100644 --- a/MatrixSDK/Crypto/Verification/Requests/MXKeyVerificationRequest.m +++ b/MatrixSDK/Crypto/Verification/Requests/MXKeyVerificationRequest.m @@ -14,239 +14,11 @@ limitations under the License. */ -#import "MXKeyVerificationRequest_Private.h" -#import "MXKeyVerificationManager_Private.h" +#import "MXKeyVerificationRequest.h" -#import "MXCrypto_Private.h" #warning File has not been annotated with nullability, see MX_ASSUME_MISSING_NULLABILITY_BEGIN #pragma mark - Constants NSString * const MXKeyVerificationRequestDidChangeNotification = @"MXKeyVerificationRequestDidChangeNotification"; - -@interface MXLegacyKeyVerificationRequest() - -@property (nonatomic, readwrite) MXKeyVerificationRequestState state; - -@end - -@implementation MXLegacyKeyVerificationRequest - -@synthesize roomId = _roomId; -@synthesize event = _event; -@synthesize fromDevice = _fromDevice; -@synthesize methods = _methods; -@synthesize otherDevice = _otherDevice; -@synthesize otherUser = _otherUser; -@synthesize reasonCancelCode = _reasonCancelCode; -@synthesize requestId = _requestId; -@synthesize timestamp = _timestamp; -@synthesize transport = _transport; - -#pragma mark - SDK-Private methods - - -- (instancetype)initWithEvent:(MXEvent*)event andManager:(MXLegacyKeyVerificationManager*)manager -{ - self = [super init]; - if (self) - { - _event = event; - _roomId = event.roomId; - _state = MXKeyVerificationRequestStatePending; - _manager = manager; - } - return self; -} - -- (void)acceptWithMethods:(NSArray *)methods success:(dispatch_block_t)success failure:(void (^)(NSError * _Nonnull))failure -{ - [self.manager computeReadyMethodsFromVerificationRequestWithId:self.requestId - andSupportedMethods:methods - completion:^(NSArray * _Nonnull readyMethods, MXQRCodeData * _Nullable qrCodeData) - { - if (!readyMethods.count) - { - void (^noReadyMethodsFailure)(void) = ^{ - - NSError *error = [NSError errorWithDomain:MXKeyVerificationErrorDomain - code:MXKeyVerificationUnsupportedMethodCode - userInfo:@{ - NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Unsupported verification methods: %@", self.methods] - }]; - - if (failure) - { - dispatch_async(dispatch_get_main_queue(), ^{ - failure(error); - }); - } - }; - - [self cancelWithCancelCode:MXTransactionCancelCode.unexpectedMessage success:^{ - noReadyMethodsFailure(); - } failure:^(NSError * _Nonnull error) { - noReadyMethodsFailure(); - }]; - - return; - } - - NSString *myDeviceId = self.manager.crypto.mxSession.matrixRestClient.credentials.deviceId; - - MXKeyVerificationReady *ready = [MXKeyVerificationReady new]; - ready.transactionId = self.requestId; - ready.relatedEventId = self.event.eventId; - ready.methods = readyMethods; - ready.fromDevice = myDeviceId; - - [self.manager sendToOtherInRequest:self eventType:kMXEventTypeStringKeyVerificationReady content:ready.JSONDictionary success:^{ - - self.acceptedData = ready; - - if (qrCodeData) - { - [self.manager createQRCodeTransactionFromRequest:self qrCodeData:qrCodeData success:^(MXLegacyQRCodeTransaction * _Nonnull transaction) { - [self updateState:MXKeyVerificationRequestStateReady notifiy:YES]; - dispatch_async(dispatch_get_main_queue(), ^{ - success(); - }); - } failure:^(NSError * _Nonnull error) { - - MXLogDebug(@"[MXKeyVerificationRequest] acceptWithMethods fail to create qrCodeData."); - - [self cancelWithCancelCode:MXTransactionCancelCode.unexpectedMessage success:^{ - - } failure:^(NSError * _Nonnull error) { - MXLogDebug(@"[MXKeyVerificationRequest] acceptWithMethods fail to cancel request"); - }]; - - dispatch_async(dispatch_get_main_queue(), ^{ - failure(error); - }); - }]; - } - else - { - [self updateState:MXKeyVerificationRequestStateReady notifiy:YES]; - dispatch_async(dispatch_get_main_queue(), ^{ - success(); - }); - } - - } failure:^(NSError * _Nonnull error) { - dispatch_async(dispatch_get_main_queue(), ^{ - failure(error); - }); - }]; - }]; -} - -- (void)cancelWithCancelCode:(MXTransactionCancelCode*)code success:(void(^)(void))success failure:(void(^)(NSError *error))failure -{ - [self.manager cancelVerificationRequest:self success:^{ - self.reasonCancelCode = code; - - [self updateState:MXKeyVerificationRequestStateCancelledByMe notifiy:YES]; - [self.manager removePendingRequestWithRequestId:self.requestId]; - - if (success) - { - success(); - } - - } failure:failure]; -} - -- (void)updateState:(MXKeyVerificationRequestState)state notifiy:(BOOL)notify -{ - if (state == self.state) - { - return; - } - - self.state = state; - - if (notify) - { - [self didUpdateState]; - } -} - -- (void)didUpdateState -{ - dispatch_async(dispatch_get_main_queue(),^{ - [[NSNotificationCenter defaultCenter] postNotificationName:MXKeyVerificationRequestDidChangeNotification object:self userInfo:nil]; - }); -} - -- (void)handleReady:(MXKeyVerificationReady*)readyContent -{ - MXQRCodeData *qrCodeData; - - // Check if other user is able to scan QR code - if ([readyContent.methods containsObject:MXKeyVerificationMethodQRCodeScan] && [self.methods containsObject:MXKeyVerificationMethodQRCodeShow]) - { - qrCodeData = [self.manager createQRCodeDataWithTransactionId:self.requestId otherUserId:self.otherUser otherDeviceId:self.otherDevice]; - } - - self.acceptedData = readyContent; - - if ([readyContent.methods containsObject:MXKeyVerificationMethodReciprocate]) - { - [self.manager createQRCodeTransactionFromRequest:self qrCodeData:qrCodeData success:^(MXLegacyQRCodeTransaction * _Nonnull transaction) { - - [self updateState:MXKeyVerificationRequestStateReady notifiy:YES]; - - } failure:^(NSError * _Nonnull error) { - [self cancelWithCancelCode:MXTransactionCancelCode.unexpectedMessage success:^{ - - } failure:^(NSError * _Nonnull error) { - MXLogDebug(@"[MXKeyVerificationRequest] handleReady fail to cancel request"); - }]; - }]; - } - else - { - [self updateState:MXKeyVerificationRequestStateReady notifiy:YES]; - } - [self.manager notifyOthersOfAcceptanceWithTransactionId:self.requestId acceptedUserId:self.otherUser acceptedDeviceId: self.otherDevice success:^{ - MXLogDebug(@"[MXKeyVerificationRequest] handleReady notified others of acceptance"); - } failure:^(NSError * _Nonnull error) { - MXLogError(@"[MXKeyVerificationRequest] handleReady failed notify others of acceptance"); - }]; -} - -- (void)handleCancel:(MXKeyVerificationCancel *)cancelContent -{ - self.reasonCancelCode = [[MXTransactionCancelCode alloc] initWithValue:cancelContent.code - humanReadable:cancelContent.reason]; - - [self updateState:MXKeyVerificationRequestStateCancelled notifiy:YES]; - [self.manager removePendingRequestWithRequestId:self.requestId]; -} - - -// Shortcuts to the accepted event --(NSArray *)acceptedMethods -{ - return _acceptedData.methods; -} - -- (NSString *)myUserId -{ - return self.manager.crypto.mxSession.credentials.userId; -} - -// Shortcuts of methods according to the point of view -- (NSArray *)myMethods -{ - return _isFromMyDevice ? self.methods : self.acceptedMethods; -} - -- (NSArray *)otherMethods -{ - return _isFromMyDevice ? self.acceptedMethods : self.methods; -} - -@end diff --git a/MatrixSDK/Crypto/Verification/Requests/MXKeyVerificationRequest_Private.h b/MatrixSDK/Crypto/Verification/Requests/MXKeyVerificationRequest_Private.h deleted file mode 100644 index 4c589b65d3..0000000000 --- a/MatrixSDK/Crypto/Verification/Requests/MXKeyVerificationRequest_Private.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - Copyright 2019 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 "MXKeyVerificationRequest.h" - -#import "MXKeyVerificationReady.h" -#import "MXKeyVerificationCancel.h" - -MX_ASSUME_MISSING_NULLABILITY_BEGIN - -@class MXLegacyKeyVerificationManager, MXHTTPOperation; - - -/** - The `MXKeyVerificationRequest` extension exposes internal operations. - */ -@interface MXLegacyKeyVerificationRequest () - -@property (nonatomic, readonly, weak) MXLegacyKeyVerificationManager *manager; - -- (instancetype)initWithEvent:(MXEvent*)event andManager:(MXLegacyKeyVerificationManager*)manager; - -@property (nonatomic, nullable) MXTransactionCancelCode *reasonCancelCode; -@property (nonatomic) BOOL isFromMyUser; -@property (nonatomic) BOOL isFromMyDevice; - -@property (nonatomic) MXKeyVerificationReady *acceptedData; - -- (void)updateState:(MXKeyVerificationRequestState)state notifiy:(BOOL)notify; - -- (void)handleReady:(MXKeyVerificationReady*)readyContent; -- (void)handleCancel:(MXKeyVerificationCancel*)cancelContent; - -@end - -MX_ASSUME_MISSING_NULLABILITY_END diff --git a/MatrixSDK/Crypto/Verification/Status/MXKeyVerificationStatusResolver.h b/MatrixSDK/Crypto/Verification/Status/MXKeyVerificationStatusResolver.h deleted file mode 100644 index c89fd02ddf..0000000000 --- a/MatrixSDK/Crypto/Verification/Status/MXKeyVerificationStatusResolver.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - Copyright 2019 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 - -#import "MXKeyVerification.h" - -@class MXLegacyKeyVerificationManager, MXSession, MXHTTPOperation, MXEvent; - -NS_ASSUME_NONNULL_BEGIN - -/** - `MXKeyVerificationStatusResolver` computes MXKeyVerification status from an event - of the verification process. - */ -@interface MXKeyVerificationStatusResolver : NSObject - -- (instancetype)initWithManager:(MXLegacyKeyVerificationManager*)manager matrixSession:(MXSession*)matrixSession; - -- (nullable MXHTTPOperation *)keyVerificationWithKeyVerificationId:(NSString*)keyVerificationId - event:(MXEvent*)event - transport:(MXKeyVerificationTransport)transport - success:(void(^)(MXKeyVerification *keyVerification))success - failure:(void(^)(NSError *error))failure; - -- (nullable MXKeyVerification*)keyVerificationFromRequest:(nullable id)request andTransaction:(nullable id)transaction; - -@end - -NS_ASSUME_NONNULL_END diff --git a/MatrixSDK/Crypto/Verification/Status/MXKeyVerificationStatusResolver.m b/MatrixSDK/Crypto/Verification/Status/MXKeyVerificationStatusResolver.m deleted file mode 100644 index 0ea0770760..0000000000 --- a/MatrixSDK/Crypto/Verification/Status/MXKeyVerificationStatusResolver.m +++ /dev/null @@ -1,411 +0,0 @@ -/* - Copyright 2019 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 "MXKeyVerificationStatusResolver.h" - -#import "MXSession.h" -#import "MXKeyVerificationByDMRequest.h" -#import "MXKeyVerificationRequest_Private.h" -#import "MXKeyVerification.h" -#import "MXKeyVerificationManager_Private.h" - -#import "MXKeyVerificationCancel.h" - -#import "MXQRCodeTransaction.h" -#import "MXSASTransaction.h" - -@interface MXKeyVerificationStatusResolver () -@property (nonatomic, weak) MXLegacyKeyVerificationManager *manager; -@property (nonatomic) MXSession *mxSession; -@end - - -@implementation MXKeyVerificationStatusResolver - -#pragma mark - Setup - -- (instancetype)initWithManager:(MXLegacyKeyVerificationManager*)manager matrixSession:(MXSession*)matrixSession; - -{ - self = [super init]; - if (self) - { - self.manager = manager; - self.mxSession = matrixSession; - } - return self; -} - -#pragma mark - Public - -- (nullable MXHTTPOperation *)keyVerificationWithKeyVerificationId:(NSString*)keyVerificationId - event:(MXEvent*)event - transport:(MXKeyVerificationTransport)transport - success:(void(^)(MXKeyVerification *keyVerification))success - failure:(void(^)(NSError *error))failure -{ - MXHTTPOperation *operation; - switch (transport) - { - case MXKeyVerificationTransportDirectMessage: - { - operation = [self eventsInVerificationByDMThreadFromOriginalEventId:keyVerificationId inRoom:event.roomId success:^(MXEvent *originalEvent, NSArray *events) { - - if (!originalEvent) - { - originalEvent = event; - } - - MXKeyVerification *keyVerification = [self makeKeyVerificationFromOriginalDMEvent:originalEvent events:events]; - if (keyVerification) - { - success(keyVerification); - } - else - { - NSError *error = [NSError errorWithDomain:MXKeyVerificationErrorDomain - code:MXKeyVerificationUnknownIdentifier - userInfo:@{ - NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Unknown id"] - }]; - failure(error); - } - - } failure:failure]; - break; - } - - default: - // Requests by to_device are not supported - // TODO: Can we really do something? This is all by DM here - NSParameterAssert(NO); - break; - } - - return operation; -} - -- (nullable MXKeyVerification*)keyVerificationFromRequest:(nullable id)request andTransaction:(nullable id)transaction -{ - if (!request && !transaction) - { - return nil; - } - - MXKeyVerification *keyVerification = [MXKeyVerification new]; - - MXKeyVerificationState state = [self stateFromRequest:request andTransaction:transaction]; - - if (transaction && state >= MXKeyVerificationStateTransactionStarted) - { - keyVerification.transaction = transaction; - } - else - { - keyVerification.request = request; - } - - keyVerification.state = state; - - return keyVerification; -} - -#pragma mark - Private - -- (nullable MXHTTPOperation *)eventsInVerificationByDMThreadFromOriginalEventId:(NSString*)originalEventId - inRoom:(NSString*)roomId - success:(void(^)(MXEvent *originalEvent, NSArray *events))success - failure:(void(^)(NSError *error))failure -{ - // Get all related events - return [self.mxSession.aggregations referenceEventsForEvent:originalEventId inRoom:roomId from:nil limit:-1 success:^(MXAggregationPaginatedResponse * _Nonnull paginatedResponse) { - success(paginatedResponse.originalEvent, paginatedResponse.chunk); - } failure:failure]; -} - -- (nullable MXKeyVerification *)makeKeyVerificationFromOriginalDMEvent:(nullable MXEvent*)originalEvent events:(NSArray *)events -{ - MXKeyVerification *keyVerification; - - id request = [self verificationRequestInDMEvent:originalEvent events:events]; - - if (request) - { - keyVerification = [MXKeyVerification new]; - keyVerification.request = request; - - keyVerification.state = [self stateFromRequestState:request.state andEvents:events]; - } - - return keyVerification; -} - -- (nullable MXKeyVerificationByDMRequest*)verificationRequestInDMEvent:(MXEvent*)event events:(NSArray *)events -{ - MXKeyVerificationByDMRequest *request; - if (![event.content[kMXMessageTypeKey] isEqualToString:kMXMessageTypeKeyVerificationRequest]) - { - return nil; - } - - request = [[MXKeyVerificationByDMRequest alloc] initWithEvent:event andManager:self.manager]; - - if (!request) - { - return nil; - } - - MXKeyVerificationRequestState requestState = MXKeyVerificationRequestStatePending; - NSString *myUserId = self.mxSession.myUserId; - - MXEvent *firstEvent = events.firstObject; - if (firstEvent.eventType == MXEventTypeKeyVerificationCancel) - { - // If the first event is a cancel, the request has been cancelled - // by me or declined by the other - if ([firstEvent.sender isEqualToString:myUserId]) - { - requestState = MXKeyVerificationRequestStateCancelledByMe; - } - else - { - requestState = MXKeyVerificationRequestStateCancelled; - } - } - else if (events.count) - { - // If there are events but no cancel event at first, the transaction - // has started = the request has been accepted - for (MXEvent *event in events) - { - // In case the other sent a ready event, store its content - if (event.eventType == MXEventTypeKeyVerificationReady) - { - // Avoid to overwrite requestState if value was set to MXKeyVerificationRequestStateAccepted - if (requestState != MXKeyVerificationRequestStateAccepted) - { - requestState = MXKeyVerificationRequestStateReady; - } - - MXKeyVerificationReady *keyVerificationReady; - MXJSONModelSetMXJSONModel(keyVerificationReady, MXKeyVerificationReady, event.content); - request.acceptedData = keyVerificationReady; - } - // For SAS or QR code if I sent MXEventTypeKeyVerificationStart I have accepted the request. - else if (event.eventType == MXEventTypeKeyVerificationStart) - { - requestState = MXKeyVerificationRequestStateAccepted; - } - } - } - // There is only the request event. What is the status of it? - else if (![self.manager isRequestStillValid:request]) - { - requestState = MXKeyVerificationRequestStateExpired; - } - else - { - requestState = MXKeyVerificationRequestStatePending; - } - - [request updateState:requestState notifiy:NO]; - - return request; -} - - -- (MXKeyVerificationState)stateFromRequestState:(MXKeyVerificationRequestState)requestState andEvents:(NSArray *)events -{ - MXKeyVerificationState state; - - if (requestState == MXKeyVerificationRequestStateAccepted) - { - state = [self computeTranscationStateWithEvents:events]; - } - else - { - state = [self stateFromRequestState:requestState]; - } - - return state; -} - -- (MXKeyVerificationState)computeTranscationStateWithEvents:(NSArray *)events -{ - MXKeyVerificationState state = MXKeyVerificationStateTransactionStarted; - - BOOL exitLoop = NO; - - for (MXEvent *event in events) - { - NSString *myUserId = self.mxSession.myUserId; - - switch (event.eventType) - { - case MXEventTypeKeyVerificationCancel: - { - MXKeyVerificationCancel *cancel; - MXJSONModelSetMXJSONModel(cancel, MXKeyVerificationCancel.class, event.content); - - NSString *cancelCode = cancel.code; - if ([cancelCode isEqualToString:MXTransactionCancelCode.user.value] - || [cancelCode isEqualToString:MXTransactionCancelCode.timeout.value]) - { - if ([event.sender isEqualToString:myUserId]) - { - state = MXKeyVerificationStateTransactionCancelledByMe; - exitLoop = YES; - } - else - { - state = MXKeyVerificationStateTransactionCancelled; - exitLoop = YES; - } - } - else - { - state = MXKeyVerificationStateTransactionFailed; - exitLoop = YES; - } - break; - } - case MXEventTypeKeyVerificationReady: - state = MXKeyVerificationStateRequestReady; - break; - case MXEventTypeKeyVerificationDone: - if ([event.sender isEqualToString:myUserId]) - { - state = MXKeyVerificationStateVerified; - exitLoop = YES; - } - break; - - default: - break; - } - - if (exitLoop) - { - break; - } - } - - return state; -} - -- (MXKeyVerificationState)stateFromRequestState:(MXKeyVerificationRequestState)requestState -{ - MXKeyVerificationState state; - switch (requestState) - { - case MXKeyVerificationRequestStatePending: - state = MXKeyVerificationStateRequestPending; - break; - case MXKeyVerificationRequestStateExpired: - state = MXKeyVerificationStateRequestExpired; - break; - case MXKeyVerificationRequestStateCancelled: - state = MXKeyVerificationStateRequestCancelled; - break; - case MXKeyVerificationRequestStateCancelledByMe: - state = MXKeyVerificationStateRequestCancelledByMe; - break; - case MXKeyVerificationRequestStateReady: - state = MXKeyVerificationStateRequestReady; - break; - case MXKeyVerificationRequestStateAccepted: - state = MXKeyVerificationStateTransactionStarted; - break; - } - - return state; -} - -- (MXKeyVerificationState)stateFromRequest:(nullable id)request andTransaction:(nullable id)transaction -{ - MXKeyVerificationState keyVerificationState = MXKeyVerificationStateRequestPending; - - if (transaction) - { - if ([transaction isKindOfClass:MXLegacyQRCodeTransaction.class]) - { - MXLegacyQRCodeTransaction *qrCodeTransaction = (MXLegacyQRCodeTransaction*)transaction; - - switch (qrCodeTransaction.state) { - case MXQRCodeTransactionStateUnknown: - - if (request) - { - keyVerificationState = [self stateFromRequestState:request.state]; - } - else - { - keyVerificationState = MXKeyVerificationStateRequestPending; - } - - break; - case MXQRCodeTransactionStateQRScannedByOther: - case MXQRCodeTransactionStateScannedOtherQR: - keyVerificationState = MXKeyVerificationStateTransactionStarted; - break; - case MXQRCodeTransactionStateVerified: - keyVerificationState = MXKeyVerificationStateVerified; - break; - case MXQRCodeTransactionStateCancelled: - keyVerificationState = MXKeyVerificationStateTransactionCancelled; - break; - case MXQRCodeTransactionStateCancelledByMe: - keyVerificationState = MXKeyVerificationStateTransactionCancelledByMe; - break; - case MXQRCodeTransactionStateError: - keyVerificationState = MXKeyVerificationStateTransactionFailed; - break; - default: - break; - } - } - else if ([transaction conformsToProtocol:@protocol(MXSASTransaction)]) - { - id sasTransaction = (id)transaction; - - switch (sasTransaction.state) { - case MXSASTransactionStateVerified: - keyVerificationState = MXKeyVerificationStateVerified; - break; - case MXSASTransactionStateCancelled: - keyVerificationState = MXKeyVerificationStateTransactionCancelled; - break; - case MXSASTransactionStateCancelledByMe: - keyVerificationState = MXKeyVerificationStateTransactionCancelledByMe; - break; - case MXSASTransactionStateError: - keyVerificationState = MXKeyVerificationStateTransactionFailed; - break; - default: - keyVerificationState = MXKeyVerificationStateTransactionStarted; - break; - } - } - } - else if (request) - { - keyVerificationState = [self stateFromRequestState:request.state]; - } - - return keyVerificationState; -} - -@end diff --git a/MatrixSDK/Crypto/Verification/Transactions/MXKeyVerificationTransaction.h b/MatrixSDK/Crypto/Verification/Transactions/MXKeyVerificationTransaction.h index 2b4600e6e2..b38e99e510 100644 --- a/MatrixSDK/Crypto/Verification/Transactions/MXKeyVerificationTransaction.h +++ b/MatrixSDK/Crypto/Verification/Transactions/MXKeyVerificationTransaction.h @@ -103,30 +103,4 @@ typedef NS_ENUM(NSInteger, MXKeyVerificationTransport) { @end -/** - Default implementation of verification transaction used by the SDK - */ -@interface MXLegacyKeyVerificationTransaction: NSObject - -/** - The creation date. - */ -@property (nonatomic, strong) NSDate *creationDate; - -@property (nonatomic) BOOL isIncoming; - -/** - The other user device. - */ -@property (nonatomic, readonly) MXDeviceInfo *otherDevice; - -@property (nonatomic, nullable) MXTransactionCancelCode *reasonCancelCode; - -/** - The occured error (like network error), if any. - */ -@property (nonatomic, nullable) NSError *error; - -@end - NS_ASSUME_NONNULL_END diff --git a/MatrixSDK/Crypto/Verification/Transactions/MXKeyVerificationTransaction.m b/MatrixSDK/Crypto/Verification/Transactions/MXKeyVerificationTransaction.m index 59e7f856dd..c2a8454cb7 100644 --- a/MatrixSDK/Crypto/Verification/Transactions/MXKeyVerificationTransaction.m +++ b/MatrixSDK/Crypto/Verification/Transactions/MXKeyVerificationTransaction.m @@ -15,103 +15,6 @@ */ #import "MXKeyVerificationTransaction.h" -#import "MXKeyVerificationTransaction_Private.h" - -#import "MXKeyVerificationManager_Private.h" -#import "MXCrypto_Private.h" - -#import "MXTools.h" - #pragma mark - Constants NSString * const MXKeyVerificationTransactionDidChangeNotification = @"MXKeyVerificationTransactionDidChangeNotification"; - - -@implementation MXLegacyKeyVerificationTransaction - -@synthesize creationDate = _creationDate; -@synthesize dmEventId = _dmEventId; -@synthesize dmRoomId = _dmRoomId; -@synthesize error = _error; -@synthesize isIncoming = _isIncoming; -@synthesize otherDevice = _otherDevice; -@synthesize reasonCancelCode = _reasonCancelCode; -@synthesize transport = _transport; - -- (instancetype)initWithOtherDevice:(MXDeviceInfo*)otherDevice andManager:(MXLegacyKeyVerificationManager*)manager; -{ - self = [self init]; - if (self) - { - _manager = manager; - _otherDevice = otherDevice; - _transactionId = [MXLegacyKeyVerificationTransaction createUniqueIdWithOtherUser:self.otherUserId otherDevice:self.otherDeviceId myUser:manager.crypto.mxSession.matrixRestClient.credentials]; - _creationDate = [NSDate date]; - } - return self; -} - -- (void)setDirectMessageTransportInRoom:(NSString *)roomId originalEvent:(NSString *)eventId -{ - _transport = MXKeyVerificationTransportDirectMessage; - _dmRoomId = roomId; - _dmEventId = eventId; - - // The original event id is used as the transaction id - _transactionId = eventId; -} - -- (NSString *)otherUserId -{ - return _otherDevice.userId; -} - -- (NSString *)otherDeviceId -{ - return _otherDevice.deviceId; -} - -- (void)cancelWithCancelCode:(MXTransactionCancelCode*)code -{ - // Must be handled by the specific implementation - NSAssert(NO, @"%@ does not implement cancelWithCancelCode", self.class); -} - -- (void)cancelWithCancelCode:(MXTransactionCancelCode *)code - success:(void (^)(void))success - failure:(void (^)(NSError *error))failure -{ - dispatch_async(self.manager.crypto.cryptoQueue,^{ - [self.manager cancelTransaction:self code:code success:success failure:failure]; - }); -} - -- (MXHTTPOperation*)sendToOther:(NSString*)eventType content:(NSDictionary*)content - success:(void (^)(void))success - failure:(void (^)(NSError *error))failure -{ - return [_manager sendToOtherInTransaction:self eventType:eventType content:content success:success failure:failure]; -} - -- (void)didUpdateState -{ - dispatch_async(dispatch_get_main_queue(),^{ - [[NSNotificationCenter defaultCenter] postNotificationName:MXKeyVerificationTransactionDidChangeNotification object:self userInfo:nil]; - }); -} - -- (void)handleCancel:(MXKeyVerificationCancel*)cancelContent -{ - // Must be handled by the specific implementation - NSAssert(NO, @"%@ does not implement handleCancel", self.class); -} - -+ (NSString*)createUniqueIdWithOtherUser:(NSString*)otherUser otherDevice:(NSString*)otherDevice myUser:(MXCredentials*)myUser -{ - return [NSString stringWithFormat:@"%@:%@|%@:%@|%@", - myUser.userId, myUser.deviceId, - otherUser, otherDevice, - [MXTools generateTransactionId]]; -} - -@end diff --git a/MatrixSDK/Crypto/Verification/Transactions/MXKeyVerificationTransaction_Private.h b/MatrixSDK/Crypto/Verification/Transactions/MXKeyVerificationTransaction_Private.h deleted file mode 100644 index 207de6ccc3..0000000000 --- a/MatrixSDK/Crypto/Verification/Transactions/MXKeyVerificationTransaction_Private.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - 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. - */ - -#import "MXKeyVerificationTransaction.h" - -#import "MXKeyVerificationAccept.h" -#import "MXKeyVerificationCancel.h" -#import "MXKeyVerificationKey.h" -#import "MXKeyVerificationMac.h" -#import "MXKeyVerificationStart.h" -#import "MXKeyVerificationDone.h" - - -@class MXLegacyKeyVerificationManager, MXHTTPOperation, MXEvent; - - -NS_ASSUME_NONNULL_BEGIN - -/** - The `MXKeyVerificationTransaction` extension exposes internal operations. - */ -@interface MXLegacyKeyVerificationTransaction () - -@property (nonatomic, readonly, weak) MXLegacyKeyVerificationManager *manager; -@property (nonatomic, readwrite) NSString *transactionId; - -- (instancetype)initWithOtherDevice:(MXDeviceInfo*)otherDevice andManager:(MXLegacyKeyVerificationManager*)manager; - -- (void)setDirectMessageTransportInRoom:(NSString*)roomId originalEvent:(NSString*)eventId; - -- (void)didUpdateState; - -#pragma mark - Outgoing to_device events -- (MXHTTPOperation*)sendToOther:(NSString*)eventType content:(NSDictionary*)content - success:(void (^)(void))success - failure:(void (^)(NSError *error))failure; - - -#pragma mark - Incoming to_device events - -- (void)handleCancel:(MXKeyVerificationCancel*)cancelContent; - -@end - -NS_ASSUME_NONNULL_END diff --git a/MatrixSDK/Crypto/Verification/Transactions/QRCode/MXQRCodeTransaction.h b/MatrixSDK/Crypto/Verification/Transactions/QRCode/MXQRCodeTransaction.h index d71d75b306..2bc80a673c 100644 --- a/MatrixSDK/Crypto/Verification/Transactions/QRCode/MXQRCodeTransaction.h +++ b/MatrixSDK/Crypto/Verification/Transactions/QRCode/MXQRCodeTransaction.h @@ -59,8 +59,4 @@ typedef NS_ENUM(NSInteger, MXQRCodeTransactionState) { @end -@interface MXLegacyQRCodeTransaction : MXLegacyKeyVerificationTransaction - -@end - NS_ASSUME_NONNULL_END diff --git a/MatrixSDK/Crypto/Verification/Transactions/QRCode/MXQRCodeTransaction.m b/MatrixSDK/Crypto/Verification/Transactions/QRCode/MXQRCodeTransaction.m index cf05f1c8f6..972e491fdd 100644 --- a/MatrixSDK/Crypto/Verification/Transactions/QRCode/MXQRCodeTransaction.m +++ b/MatrixSDK/Crypto/Verification/Transactions/QRCode/MXQRCodeTransaction.m @@ -16,10 +16,6 @@ #import "MXQRCodeTransaction.h" -#import "MXQRCodeTransaction_Private.h" - -#import "MXKeyVerificationManager_Private.h" -#import "MXCrypto_Private.h" #import "MXCryptoTools.h" #import "NSArray+MatrixSDK.h" @@ -35,338 +31,3 @@ NSString * const MXKeyVerificationMethodQRCodeScan = @"m.qr_code.scan.v1"; NSString * const MXKeyVerificationMethodReciprocate = @"m.reciprocate.v1"; - -@interface MXLegacyQRCodeTransaction() - -@property (nonatomic, strong) MXQRCodeDataCoder *qrCodeDataCoder; -@property (nonatomic, strong) MXQRCodeData *scannedOtherQRCodeData; - -@end - - -@implementation MXLegacyQRCodeTransaction - -#pragma mark - Setup - -- (nullable instancetype)initWithOtherDevice:(MXDeviceInfo*)otherDevice - qrCodeData:(nullable MXQRCodeData*)qrCodeData - andManager:(MXLegacyKeyVerificationManager *)manager -{ - self = [super initWithOtherDevice:otherDevice andManager:manager]; - if (self) - { - _qrCodeData = qrCodeData; - _qrCodeDataCoder = [MXQRCodeDataCoder new]; - } - return self; -} - -#pragma mark - Properties overrides - -- (void)setState:(MXQRCodeTransactionState)state -{ - MXLogDebug(@"[MXKeyVerification][MXQRCodeTransaction] setState: %@ -> %@", @(_state), @(state)); - - _state = state; - [self didUpdateState]; -} - -#pragma mark - Public - -- (void)otherUserScannedMyQrCode:(BOOL)otherUserScanned -{ - if (self.state != MXQRCodeTransactionStateQRScannedByOther) - { - MXLogDebug(@"[MXQRCodeTransaction]: otherUserScannedMyQrCode: QR code has not been scanned"); - [self cancelWithCancelCode:MXTransactionCancelCode.unexpectedMessage]; - return; - } - - if (!otherUserScanned) - { - [self cancelWithCancelCode:MXTransactionCancelCode.mismatchedKeys]; - } - else - { - [self trustOtherWithMyQRCodeData]; - } -} - -- (void)userHasScannedOtherQrCodeData:(MXQRCodeData*)otherQRCodeData -{ - BOOL isOtherQRCodeDataKeysValid = [self.manager isOtherQRCodeDataKeysValid:otherQRCodeData otherUserId:self.otherUserId otherDevice:self.otherDevice]; - - if (!isOtherQRCodeDataKeysValid) - { - [self cancelWithCancelCode:MXTransactionCancelCode.mismatchedKeys]; - return; - } - - self.scannedOtherQRCodeData = otherQRCodeData; - - // All checks are correct. Send the shared secret so that sender can trust me. - // otherQRCodeData.sharedSecret will be used to send the start request - [self startWithOtherQRCodeSharedSecret:otherQRCodeData.sharedSecret success:^{ - self.state = MXQRCodeTransactionStateWaitingOtherConfirm; - } failure:^(NSError *error) { - [self cancelWithCancelCode:MXTransactionCancelCode.invalidMessage]; - }]; -} - -- (void)handleCancel:(MXKeyVerificationCancel *)cancelContent -{ - self.reasonCancelCode = [[MXTransactionCancelCode alloc] initWithValue:cancelContent.code - humanReadable:cancelContent.reason]; - - self.state = MXQRCodeTransactionStateCancelled; -} - -- (void)cancelWithCancelCode:(MXTransactionCancelCode*)code -{ - [self cancelWithCancelCode:code success:^{ - self.state = MXQRCodeTransactionStateCancelledByMe; - } failure:^(NSError * _Nonnull error) { - MXLogDebug(@"[MXKeyVerification][MXSASTransaction] Fail to cancel with error: %@", error); - }]; -} - -#pragma mark - SDK-Private methods - - -- (void)handleStart:(MXQRCodeKeyVerificationStart *)start -{ - if (self.state != MXQRCodeTransactionStateUnknown) - { - MXLogDebug(@"[MXQRCodeTransaction]: ERROR: A start event was already retrieved"); - [self cancelWithCancelCode:MXTransactionCancelCode.unexpectedMessage]; - return; - } - - if (!start || !start.isValid) - { - MXLogDebug(@"[MXQRCodeTransaction]: ERROR: Invalid start: %@", start); - [self cancelWithCancelCode:MXTransactionCancelCode.unexpectedMessage]; - return; - } - - if (!self.qrCodeData) - { - MXLogDebug(@"[MXQRCodeTransaction]: ERROR: Invalid start, no QR code to scan: %@", start); - [self cancelWithCancelCode:MXTransactionCancelCode.unexpectedMessage]; - return; - } - - // Verify shared secret - NSData *startSharedSecretData = [MXBase64Tools dataFromBase64:start.sharedSecret]; - - if (![startSharedSecretData isEqualToData:self.qrCodeData.sharedSecret]) - { - MXLogDebug(@"[MXQRCodeTransaction]: ERROR: Invalid start, mismatch shared secret: %@", start); - [self cancelWithCancelCode:MXTransactionCancelCode.mismatchedKeys]; - return; - } - - self.startContent = start; - self.state = MXQRCodeTransactionStateQRScannedByOther; -} - -- (void)handleDone:(MXKeyVerificationDone*)doneEvent -{ - switch (self.state) { - case MXQRCodeTransactionStateWaitingOtherConfirm: - { - if (self.scannedOtherQRCodeData) - { - [self trustOtherWithOtherQRCodeData:self.scannedOtherQRCodeData]; - } - else - { - [self cancelWithCancelCode:MXTransactionCancelCode.unexpectedMessage]; - MXLogDebug(@"[MXQRCodeTransaction]: ERROR: Done event was already retrieved"); - } - } - break; - case MXQRCodeTransactionStateScannedOtherQR: - [self cancelWithCancelCode:MXTransactionCancelCode.unexpectedMessage]; - MXLogDebug(@"[MXQRCodeTransaction]: ERROR: Unexpected state for done event"); - default: - break; - } -} - -#pragma mark - Private - -- (void)startWithOtherQRCodeSharedSecret:(NSData*)otherQRCodeSharedSecret success:(dispatch_block_t)success failure:(void (^)(NSError* error))failure; -{ - MXLogDebug(@"[MXKeyVerification][MXQRCodeTransaction] start"); - - if (self.state != MXQRCodeTransactionStateUnknown) - { - MXLogDebug(@"[MXKeyVerification][MXQRCodeTransaction] start: wrong state: %@", self); - self.state = MXQRCodeTransactionStateCancelled; - return; - } - - MXQRCodeKeyVerificationStart *startContent = [MXQRCodeKeyVerificationStart new]; - startContent.fromDevice = self.manager.crypto.myDevice.deviceId; - startContent.method = MXKeyVerificationMethodReciprocate; - startContent.transactionId = self.transactionId; - startContent.sharedSecret = [MXBase64Tools unpaddedBase64FromData:otherQRCodeSharedSecret]; - - if (self.transport == MXKeyVerificationTransportDirectMessage) - { - startContent.relatedEventId = self.dmEventId; - } - - if (startContent.isValid) - { - self.startContent = startContent; - self.state = MXQRCodeTransactionStateScannedOtherQR; - - [self sendToOther:kMXEventTypeStringKeyVerificationStart content:startContent.JSONDictionary success:^{ - MXLogDebug(@"[MXKeyVerification][MXQRCodeTransaction] start: sendToOther:kMXEventTypeStringKeyVerificationStart succeeds"); - success(); - } failure:^(NSError * _Nonnull error) { - MXLogDebug(@"[MXKeyVerification][MXQRCodeTransaction] start: sendToOther:kMXEventTypeStringKeyVerificationStart failed. Error: %@", error); - self.error = error; - self.state = MXQRCodeTransactionStateError; - failure(error); - }]; - } - else - { - MXLogDebug(@"[MXKeyVerification][MXQRCodeTransaction] start: Invalid startContent: %@", startContent); - self.state = MXQRCodeTransactionStateCancelled; - } -} - -- (void)trustOtherWithOtherQRCodeData:(MXQRCodeData*)otherQRCodeData -{ - if (self.state != MXQRCodeTransactionStateWaitingOtherConfirm) - { - MXLogDebug(@"[MXKeyVerification][MXQRCodeTransaction] trustOtherWithOtherQRCodeData: wrong state: %@", self); - [self cancelWithCancelCode:MXTransactionCancelCode.unexpectedMessage]; - return; - } - - NSString *currentUserId = self.manager.crypto.mxSession.myUserId; - BOOL isSelfVerification = [currentUserId isEqualToString:self.otherUserId]; - - // If not me sign his MSK and upload the signature - if (otherQRCodeData.verificationMode == MXQRCodeVerificationModeVerifyingAnotherUser && !isSelfVerification) - { - // we should trust this master key - [self trustOtherUserWithId:self.otherUserId andDeviceId:self.otherDeviceId]; - } - else if (otherQRCodeData.verificationMode == MXQRCodeVerificationModeSelfVerifyingMasterKeyNotTrusted && isSelfVerification) - { - // My device is already trusted and I want to verify a new device - [self trustOtherDeviceWithId:self.otherDeviceId]; - } - else if (otherQRCodeData.verificationMode == MXQRCodeVerificationModeSelfVerifyingMasterKeyTrusted && isSelfVerification) - { - // I'm the new device. The other device will sign me. Trust the other device locally. - [self trustOtherUserWithId:self.otherUserId andDeviceId:self.otherDeviceId]; - } - else - { - MXLogDebug(@"[MXKeyVerification][MXQRCodeTransaction] trustOtherWithOtherQRCodeData: QR code is invalid"); - [self cancelWithCancelCode:MXTransactionCancelCode.qrCodeInvalid]; - } -} - -- (void)trustOtherWithMyQRCodeData -{ - if (self.state != MXQRCodeTransactionStateQRScannedByOther) - { - MXLogDebug(@"[MXKeyVerification][MXQRCodeTransaction] trustOtherWithMyQRCodeData: wrong state: %@", self); - [self cancelWithCancelCode:MXTransactionCancelCode.unexpectedMessage]; - return; - } - - if (!self.qrCodeData) - { - MXLogDebug(@"[MXKeyVerification][MXQRCodeTransaction] start: wrong state: %@", self); - [self cancelWithCancelCode:MXTransactionCancelCode.unexpectedMessage]; - return; - } - - MXQRCodeData *qrCodeData = self.qrCodeData; - - NSString *currentUserId = self.manager.crypto.mxSession.myUserId; - BOOL isSelfVerification = [currentUserId isEqualToString:self.otherUserId]; - - // If not me, sign their MSK and upload the signature - if (qrCodeData.verificationMode == MXQRCodeVerificationModeVerifyingAnotherUser && !isSelfVerification) - { - // we should trust their master key - [self trustOtherUserWithId:self.otherUserId andDeviceId:self.otherDeviceId]; - } - else if (qrCodeData.verificationMode == MXQRCodeVerificationModeSelfVerifyingMasterKeyNotTrusted && isSelfVerification) - { - // I'm the new device. The other device will sign me. Trust the other device locally. - [self trustOtherUserWithId:self.otherUserId andDeviceId:self.otherDeviceId]; - } - else if (qrCodeData.verificationMode == MXQRCodeVerificationModeSelfVerifyingMasterKeyTrusted && isSelfVerification) - { - // New device from my user scanned my QR code. Trust it - [self trustOtherDeviceWithId:self.otherDeviceId]; - } - else - { - MXLogDebug(@"[MXKeyVerification][MXQRCodeTransaction] trustOtherWithMyQRCodeData: My QR code is invalid"); - [self cancelWithCancelCode:MXTransactionCancelCode.invalidMessage]; - } -} - -- (void)trustOtherUserWithId:(NSString*)otherUserId andDeviceId:(NSString*)otherDeviceId -{ - MXLogDebug(@"[MXKeyVerification][MXQRCodeTransaction] trustOtherUserWithId: Mark user %@:%@ as verified", otherUserId, otherDeviceId); - - // Mark the device as locally verified - [self.manager.crypto setDeviceVerification:MXDeviceVerified forDevice:otherDeviceId ofUser:otherUserId success:^{ - - // Mark user as verified - [self.manager.crypto setUserVerification:YES forUser:otherUserId success:^{ - [self sendVerified]; - - } failure:^(NSError *error) { - MXLogDebug(@"[MXKeyVerification][MXQRCodeTransaction] trustOtherWithQRCodeData: Fail to cross sign user %@", otherUserId); - [self cancelWithCancelCode:MXTransactionCancelCode.mismatchedKeys]; - }]; - - } failure:^(NSError *error) { - MXLogDebug(@"[MXKeyVerification][MXQRCodeTransaction] trustOtherWithQRCodeData: Fail to mark device %@:%@ as locally verified", otherUserId, otherDeviceId); - [self cancelWithCancelCode:MXTransactionCancelCode.mismatchedKeys]; - }]; -} - -- (void)trustOtherDeviceWithId:(NSString*)otherDeviceId -{ - NSString *currentUserId = self.manager.crypto.mxSession.myUserId; - - // setDeviceVerification will automatically cross sign the device - [self.manager.crypto setDeviceVerification:MXDeviceVerified forDevice:otherDeviceId ofUser:currentUserId success:^{ - [self sendVerified]; - } failure:^(NSError *error) { - [self cancelWithCancelCode:MXTransactionCancelCode.invalidMessage]; - }]; -} - -- (void)sendVerified -{ - // Inform the other peer we are done - MXKeyVerificationDone *doneContent = [MXKeyVerificationDone new]; - doneContent.transactionId = self.transactionId; - if (self.transport == MXKeyVerificationTransportDirectMessage) - { - doneContent.relatedEventId = self.dmEventId; - } - [self sendToOther:kMXEventTypeStringKeyVerificationDone content:doneContent.JSONDictionary success:^{} failure:^(NSError * _Nonnull error) {}]; - - self.state = MXQRCodeTransactionStateVerified; - - // Remove transaction - [self.manager removeTransactionWithTransactionId:self.transactionId]; -} - -@end diff --git a/MatrixSDK/Crypto/Verification/Transactions/QRCode/MXQRCodeTransaction_Private.h b/MatrixSDK/Crypto/Verification/Transactions/QRCode/MXQRCodeTransaction_Private.h deleted file mode 100644 index aa00d4ce53..0000000000 --- a/MatrixSDK/Crypto/Verification/Transactions/QRCode/MXQRCodeTransaction_Private.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - Copyright 2020 The Matrix.org Foundation C.I.C - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -#import "MXQRCodeTransaction.h" -#import "MXKeyVerificationTransaction_Private.h" -#import "MXQRCodeKeyVerificationStart.h" -#import "MXQRCodeData.h" - -NS_ASSUME_NONNULL_BEGIN - -#pragma mark - Constants - -/** - The `MXQRCodeTransaction` extension exposes internal operations. - */ -@interface MXLegacyQRCodeTransaction () - -@property (nonatomic) MXQRCodeTransactionState state; -@property (nonatomic, nullable) MXQRCodeKeyVerificationStart *startContent; -@property (nonatomic, strong, nullable) MXQRCodeData *qrCodeData; // Current user QR code, used to show, if support method MXKeyVerificationMethodQRCodeShow - -- (nullable instancetype)initWithOtherDevice:(MXDeviceInfo*)otherDevice - qrCodeData:(nullable MXQRCodeData*)qrCodeData - andManager:(MXLegacyKeyVerificationManager *)manager; - -- (void)handleStart:(MXQRCodeKeyVerificationStart*)startContent; - -- (void)handleDone:(MXKeyVerificationDone*)doneEvent; - -@end - -NS_ASSUME_NONNULL_END diff --git a/MatrixSDK/Crypto/Verification/Transactions/SAS/MXIncomingSASTransaction.h b/MatrixSDK/Crypto/Verification/Transactions/SAS/MXIncomingSASTransaction.h deleted file mode 100644 index bc4f721768..0000000000 --- a/MatrixSDK/Crypto/Verification/Transactions/SAS/MXIncomingSASTransaction.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - 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. - */ - -#import "MXSASTransaction.h" - - -NS_ASSUME_NONNULL_BEGIN - -/** - An handler on an incoming SAS device verification. - */ -@interface MXIncomingSASTransaction : MXLegacySASTransaction - -@end - -NS_ASSUME_NONNULL_END diff --git a/MatrixSDK/Crypto/Verification/Transactions/SAS/MXIncomingSASTransaction.m b/MatrixSDK/Crypto/Verification/Transactions/SAS/MXIncomingSASTransaction.m deleted file mode 100644 index d85f363457..0000000000 --- a/MatrixSDK/Crypto/Verification/Transactions/SAS/MXIncomingSASTransaction.m +++ /dev/null @@ -1,197 +0,0 @@ -/* - 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. - */ - -#import "MXIncomingSASTransaction.h" -#import "MXSASTransaction_Private.h" -#import "MXIncomingSASTransaction_Private.h" - -#import "MXKeyVerificationManager_Private.h" -#import "MXCrypto_Private.h" - -#import "MXCryptoTools.h" -#import "NSArray+MatrixSDK.h" -#import "MXTools.h" - -@implementation MXIncomingSASTransaction - -- (void)accept; -{ - MXLogDebug(@"[MXKeyVerification][MXIncomingSASTransaction] accept"); - - if (self.state != MXSASTransactionStateIncomingShowAccept) - { - MXLogDebug(@"[MXKeyVerification][MXIncomingSASTransaction] accept: wrong state: %@", self); - [self cancelWithCancelCode:MXTransactionCancelCode.unexpectedMessage]; - return; - } - - MXKeyVerificationAccept *acceptContent = [MXKeyVerificationAccept new]; - acceptContent.transactionId = self.transactionId; - - - // Select a key agreement protocol, a hash algorithm, a message authentication code, - // and short authentication string methods out of the lists given in requester's message - acceptContent.keyAgreementProtocol = [self.startContent.keyAgreementProtocols mx_intersectArray:kKnownAgreementProtocols].firstObject; - acceptContent.hashAlgorithm = [self.startContent.hashAlgorithms mx_intersectArray:kKnownHashes].firstObject; - acceptContent.messageAuthenticationCode = [self.startContent.messageAuthenticationCodes mx_intersectArray:kKnownMacs].firstObject; - acceptContent.shortAuthenticationString = [self.startContent.shortAuthenticationString mx_intersectArray:kKnownShortCodes]; - - self.accepted = acceptContent; - - // The hash commitment is the hash (using the selected hash algorithm) of the unpadded base64 representation of QB, - // concatenated with the canonical JSON representation of the content of the m.key.verification.start message - acceptContent.commitment = [NSString stringWithFormat:@"%@%@", self.olmSAS.publicKey, [MXCryptoTools canonicalJSONStringForJSON:self.startContent.JSONDictionary]]; - acceptContent.commitment = [self hashUsingAgreedHashMethod:acceptContent.commitment]; - - // No common key sharing/hashing/hmac/SAS methods. - // If a device is unable to complete the verification because the devices are unable to find a common key sharing, - // hashing, hmac, or SAS method, then it should send a m.key.verification.cancel message - if (acceptContent.isValid) - { - [self sendToOther:kMXEventTypeStringKeyVerificationAccept content:acceptContent.JSONDictionary success:^{ - - self.state = MXSASTransactionStateWaitForPartnerKey; - } failure:^(NSError * _Nonnull error) { - - MXLogDebug(@"[MXKeyVerification][MXIncomingSASTransaction] accept: sendToOther:kMXEventTypeStringKeyVerificationAccept failed. Error: %@", error); - self.error = error; - self.state = MXSASTransactionStateError; - }]; - } - else - { - MXLogDebug(@"[MXKeyVerification][MXIncomingSASTransaction] accept: Failed to find agreement"); - [self cancelWithCancelCode:MXTransactionCancelCode.unknownMethod]; - return; - } -} - - -#pragma mark - SDK-Private methods - - -- (nullable instancetype)initWithOtherDevice:(MXDeviceInfo *)otherDevice startEvent:(MXEvent *)event andManager:(MXLegacyKeyVerificationManager *)manager -{ - MXSASKeyVerificationStart *startContent; - MXJSONModelSetMXJSONModel(startContent, MXSASKeyVerificationStart, event.content); - if (!startContent || !startContent.isValid) - { - MXLogDebug(@"[MXKeyVerificationTransaction]: ERROR: Invalid start event: %@", event); - return nil; - } - - self = [super initWithOtherDevice:otherDevice andManager:manager]; - if (self) - { - self.startContent = startContent; - self.transactionId = startContent.transactionId; - - // Detect verification by DM - if (startContent.relatedEventId) - { - [self setDirectMessageTransportInRoom:event.roomId originalEvent:startContent.relatedEventId]; - } - - // It would have been nice to timeout from the event creation date - // but we do not receive the information. originServerTs = 0 - // So, use the time when we receive it instead - //_creationDate = [NSDate dateWithTimeIntervalSince1970: (event.originServerTs / 1000)]; - - // Check validity - if (![self.startContent.method isEqualToString:MXKeyVerificationMethodSAS] - || ![self.startContent.shortAuthenticationString containsObject:MXKeyVerificationSASModeDecimal]) - { - MXLogDebug(@"[MXKeyVerification][MXIncomingSASTransaction]: ERROR: Invalid start event: %@", event); - return nil; - } - - // Bob's case - self.state = MXSASTransactionStateIncomingShowAccept; - self.isIncoming = YES; - } - return self; -} - - -#pragma mark - Incoming to_device events - -- (void)handleAccept:(MXKeyVerificationAccept*)acceptContent -{ - MXLogDebug(@"[MXKeyVerification][MXIncomingSASTransaction] handleAccept"); - - [self cancelWithCancelCode:MXTransactionCancelCode.unexpectedMessage]; -} - -- (void)handleKey:(MXKeyVerificationKey *)keyContent -{ - MXLogDebug(@"[MXKeyVerification][MXIncomingSASTransaction] handleKey"); - - if (self.state != MXSASTransactionStateWaitForPartnerKey) - { - MXLogDebug(@"[MXKeyVerification][MXIncomingSASTransaction] handleKey: wrong state: %@. keyContent: %@", self, keyContent); - [self cancelWithCancelCode:MXTransactionCancelCode.unexpectedMessage]; - return; - } - - if (!keyContent.isValid) - { - MXLogDebug(@"[MXKeyVerification][MXIncomingSASTransaction] handleKey: key content is invalid. keyContent: %@", keyContent); - [self cancelWithCancelCode:MXTransactionCancelCode.unexpectedMessage]; - return; - } - - // Upon receipt of the m.key.verification.key message from Alice’s device, - // Bob’s device replies with a to_device message with type set to m.key.verification.key, - // sending Bob’s public key QB - NSString *pubKey = self.olmSAS.publicKey; - - MXKeyVerificationKey *bobKeyContent = [MXKeyVerificationKey new]; - bobKeyContent.transactionId = self.transactionId; - bobKeyContent.key = pubKey; - - MXWeakify(self); - [self sendToOther:kMXEventTypeStringKeyVerificationKey content:bobKeyContent.JSONDictionary success:^{ - MXStrongifyAndReturnIfNil(self); - - self.sasBytes = [self generateSasBytesWithTheirPublicKey:keyContent.key - requestingDevice:self.otherDevice sasPublicKey:keyContent.key - otherDevice:self.manager.crypto.myDevice otherSasPublicKey:self.olmSAS.publicKey]; - -// MXLogDebug(@"[MXKeyVerification][MXIncomingSASTransaction] handleKey: BOB CODE: %@", self.sasDecimal); -// MXLogDebug(@"[MXKeyVerification][MXIncomingSASTransaction] handleKey: BOB EMOJI CODE: %@", self.sasEmoji); - - self.state = MXSASTransactionStateShowSAS; - - } failure:^(NSError * _Nonnull error) { - - MXLogDebug(@"[MXKeyVerification][MXIncomingSASTransaction] handleKey: sendToOther:kMXEventTypeStringKeyVerificationKey failed. Error: %@", error); - self.error = error; - self.state = MXSASTransactionStateError; - }]; -} - - -#pragma mark - Private methods - -- (NSString *)description -{ - return [NSString stringWithFormat:@" id:%@ from %@:%@. State %@", - self, - self.transactionId, - self.otherUserId, self.otherDeviceId, - @(self.state)]; -} - -@end diff --git a/MatrixSDK/Crypto/Verification/Transactions/SAS/MXIncomingSASTransaction_Private.h b/MatrixSDK/Crypto/Verification/Transactions/SAS/MXIncomingSASTransaction_Private.h deleted file mode 100644 index e1832de663..0000000000 --- a/MatrixSDK/Crypto/Verification/Transactions/SAS/MXIncomingSASTransaction_Private.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - Copyright 2020 The Matrix.org Foundation C.I.C - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -#import - -NS_ASSUME_NONNULL_BEGIN - -/** - The `MXKeyVerificationTransaction` extension exposes internal operations. - */ -@interface MXIncomingSASTransaction () - -- (nullable instancetype)initWithOtherDevice:(MXDeviceInfo*)otherDevice startEvent:(MXEvent *)event andManager:(MXLegacyKeyVerificationManager *)manager; - -@end - -NS_ASSUME_NONNULL_END diff --git a/MatrixSDK/Crypto/Verification/Transactions/SAS/MXOutgoingSASTransaction.h b/MatrixSDK/Crypto/Verification/Transactions/SAS/MXOutgoingSASTransaction.h deleted file mode 100644 index c58394c0b0..0000000000 --- a/MatrixSDK/Crypto/Verification/Transactions/SAS/MXOutgoingSASTransaction.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - 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. - */ - -#import "MXSASTransaction.h" - - -NS_ASSUME_NONNULL_BEGIN - -/** - An handler on an outgoing SAS device verification. - */ -@interface MXOutgoingSASTransaction : MXLegacySASTransaction - -/** - Start the device verification process. - */ -- (void)start; - -@end - -NS_ASSUME_NONNULL_END diff --git a/MatrixSDK/Crypto/Verification/Transactions/SAS/MXOutgoingSASTransaction.m b/MatrixSDK/Crypto/Verification/Transactions/SAS/MXOutgoingSASTransaction.m deleted file mode 100644 index 51b3c225f7..0000000000 --- a/MatrixSDK/Crypto/Verification/Transactions/SAS/MXOutgoingSASTransaction.m +++ /dev/null @@ -1,201 +0,0 @@ -/* - 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. - */ - -#import "MXOutgoingSASTransaction.h" -#import "MXSASTransaction_Private.h" - -#import "MXKeyVerificationManager_Private.h" -#import "MXCrypto_Private.h" - -#import "MXCryptoTools.h" -#import "NSArray+MatrixSDK.h" - -@interface MXOutgoingSASTransaction () - -@end - -@implementation MXOutgoingSASTransaction - -- (void)start; -{ - MXLogDebug(@"[MXKeyVerification][MXOutgoingSASTransaction] start"); - - if (self.state != MXSASTransactionStateUnknown) - { - MXLogDebug(@"[MXKeyVerification][MXOutgoingSASTransaction] start: wrong state: %@", self); - self.state = MXSASTransactionStateCancelled; - return; - } - - MXSASKeyVerificationStart *startContent = [MXSASKeyVerificationStart new]; - startContent.fromDevice = self.manager.crypto.myDevice.deviceId; - startContent.method = MXKeyVerificationMethodSAS; - startContent.transactionId = self.transactionId; - startContent.keyAgreementProtocols = kKnownAgreementProtocols; - startContent.hashAlgorithms = kKnownHashes; - startContent.messageAuthenticationCodes = kKnownMacs; - startContent.shortAuthenticationString = kKnownShortCodes; - - if (self.transport == MXKeyVerificationTransportDirectMessage) - { - startContent.relatedEventId = self.dmEventId; - } - - if (startContent.isValid) - { - self.startContent = startContent; - self.state = MXSASTransactionStateOutgoingWaitForPartnerToAccept; - - [self sendToOther:kMXEventTypeStringKeyVerificationStart content:startContent.JSONDictionary success:^{ - MXLogDebug(@"[MXKeyVerification][MXOutgoingSASTransaction] start: sendToOther:kMXEventTypeStringKeyVerificationStart succeeds"); - } failure:^(NSError * _Nonnull error) { - MXLogDebug(@"[MXKeyVerification][MXOutgoingSASTransaction] start: sendToOther:kMXEventTypeStringKeyVerificationStart failed. Error: %@", error); - self.error = error; - self.state = MXSASTransactionStateError; - }]; - } - else - { - MXLogDebug(@"[MXKeyVerification][MXOutgoingSASTransaction] start: Invalid startContent: %@", startContent); - self.state = MXSASTransactionStateCancelled; - } -} - - -#pragma mark - SDK-Private methods - - -- (instancetype)initWithOtherDevice:(MXDeviceInfo *)otherDevice andManager:(MXLegacyKeyVerificationManager *)manager -{ - self = [super initWithOtherDevice:otherDevice andManager:manager]; - if (self) - { - // Alice's case - self.state = MXSASTransactionStateUnknown; - self.isIncoming = NO; - } - return self; -} - - -#pragma mark - Incoming to_device events - -- (void)handleAccept:(MXKeyVerificationAccept*)acceptContent -{ - // Alice's POV - MXLogDebug(@"[MXKeyVerification][MXOutgoingSASTransaction] handleAccept"); - - if (self.state != MXSASTransactionStateOutgoingWaitForPartnerToAccept) - { - MXLogDebug(@"[MXKeyVerification][MXOutgoingSASTransaction] handleAccept: wrong state: %@. acceptContent: %@", self, acceptContent); - [self cancelWithCancelCode:MXTransactionCancelCode.unexpectedMessage]; - return; - } - - // Check that the agreement is correct - if (![kKnownAgreementProtocols containsObject:acceptContent.keyAgreementProtocol] - || ![kKnownHashes containsObject:acceptContent.hashAlgorithm] - || ![kKnownMacs containsObject:acceptContent.messageAuthenticationCode] - || ![acceptContent.shortAuthenticationString mx_intersectArray:kKnownShortCodes].count) - { - MXLogDebug(@"[MXKeyVerification][MXOutgoingSASTransaction] handleAccept: wrong method: %@. acceptContent: %@", self, acceptContent); - [self cancelWithCancelCode:MXTransactionCancelCode.unknownMethod]; - return; - } - - // Upon receipt of the m.key.verification.accept message from Bob’s device, - // Alice’s device stores the commitment value for later use. - self.accepted = acceptContent; - - // Alice’s device creates an ephemeral Curve25519 key pair (dA,QA), - // and replies with a to_device message with type set to “m.key.verification.key”, sending Alice’s public key QA - NSString *pubKey = self.olmSAS.publicKey; - - MXKeyVerificationKey *keyContent = [MXKeyVerificationKey new]; - keyContent.transactionId = self.transactionId; - keyContent.key = pubKey; - - [self sendToOther:kMXEventTypeStringKeyVerificationKey content:keyContent.JSONDictionary success:^{ - - self.state = MXSASTransactionStateWaitForPartnerKey; - - } failure:^(NSError * _Nonnull error) { - MXLogDebug(@"[MXKeyVerification][MXOutgoingSASTransaction] handleAccept: sendToOther:kMXEventTypeStringKeyVerificationKey failed. Error: %@", error); - self.error = error; - self.state = MXSASTransactionStateError; - }]; -} - -- (void)handleKey:(MXKeyVerificationKey *)keyContent -{ - MXLogDebug(@"[MXKeyVerification][MXOutgoingSASTransaction] handleKey"); - - if (self.state != MXSASTransactionStateWaitForPartnerKey) - { - MXLogDebug(@"[MXKeyVerification][MXOutgoingSASTransaction] handleKey: wrong state: %@. keyContent: %@", self, keyContent); - [self cancelWithCancelCode:MXTransactionCancelCode.unexpectedMessage]; - return; - } - - if (!keyContent.isValid) - { - MXLogDebug(@"[MXKeyVerification][MXOutgoingSASTransaction] handleKey: key content is invalid. keyContent: %@", keyContent); - [self cancelWithCancelCode:MXTransactionCancelCode.unexpectedMessage]; - return; - } - - // Upon receipt of the m.key.verification.key message from Bob’s device, - // Alice’s device checks that the commitment property from the Bob’s m.key.verification.accept - // message is the same as the expected value based on the value of the key property received - // in Bob’s m.key.verification.key and the content of Alice’s m.key.verification.start message. - - // Check commitment - NSString *otherCommitment = [NSString stringWithFormat:@"%@%@", - keyContent.key, - [MXCryptoTools canonicalJSONStringForJSON:self.startContent.JSONDictionary]]; - otherCommitment = [self hashUsingAgreedHashMethod:otherCommitment]; - - if ([self.accepted.commitment isEqualToString:otherCommitment]) - { - self.sasBytes = [self generateSasBytesWithTheirPublicKey:keyContent.key - requestingDevice:self.manager.crypto.myDevice sasPublicKey:self.olmSAS.publicKey - otherDevice:self.otherDevice otherSasPublicKey:keyContent.key]; - -// MXLogDebug(@"[MXKeyVerification][MXOutgoingSASTransaction] handleKey: ALICE CODE: %@", self.sasDecimal); -// MXLogDebug(@"[MXKeyVerification][MXOutgoingSASTransaction] handleKey: ALICE EMOJI CODE: %@", self.sasEmoji); - - self.state = MXSASTransactionStateShowSAS; - } - else - { - MXLogDebug(@"[MXKeyVerification][MXOutgoingSASTransaction] handleKey: Bad commitment:\n%@\n%@", self.accepted.commitment, otherCommitment); - - [self cancelWithCancelCode:MXTransactionCancelCode.mismatchedCommitment]; - } -} - - -#pragma mark - Private methods - -- (NSString *)description -{ - return [NSString stringWithFormat:@" id:%@ from %@:%@. State %@", - self, - self.transactionId, - self.otherUserId, self.otherDeviceId, - @(self.state)]; -} - -@end diff --git a/MatrixSDK/Crypto/Verification/Transactions/SAS/MXSASTransaction.h b/MatrixSDK/Crypto/Verification/Transactions/SAS/MXSASTransaction.h index dc9d25b19c..486f3b2ae8 100644 --- a/MatrixSDK/Crypto/Verification/Transactions/SAS/MXSASTransaction.h +++ b/MatrixSDK/Crypto/Verification/Transactions/SAS/MXSASTransaction.h @@ -78,15 +78,10 @@ typedef enum : NSUInteger /** Default implementation of SAS transaction used by the SDK */ -@interface MXLegacySASTransaction : MXLegacyKeyVerificationTransaction +@interface MXLegacySASTransaction: NSObject + (NSArray *)allEmojiRepresentations; -/** - The Short Authentication Code bytes data. - */ -@property (nonatomic, nullable) NSData *sasBytes; - @end NS_ASSUME_NONNULL_END diff --git a/MatrixSDK/Crypto/Verification/Transactions/SAS/MXSASTransaction.m b/MatrixSDK/Crypto/Verification/Transactions/SAS/MXSASTransaction.m index 4388bc3037..8d8724733a 100644 --- a/MatrixSDK/Crypto/Verification/Transactions/SAS/MXSASTransaction.m +++ b/MatrixSDK/Crypto/Verification/Transactions/SAS/MXSASTransaction.m @@ -15,11 +15,6 @@ */ #import "MXSASTransaction.h" -#import "MXSASTransaction_Private.h" - -#import "MXCrypto_Private.h" -#import "MXCrossSigning_Private.h" -#import "MXKeyVerificationManager_Private.h" #import "MXKey.h" @@ -33,11 +28,6 @@ NSString * const MXKeyVerificationSASMacSha256 = @"hkdf-hmac-sha256"; NSString * const MXKeyVerificationSASMacSha256LongKdf = @"hmac-sha256"; -const struct MXSASAgreementProtocols MXSASAgreementProtocols = { - .v1 = @"curve25519", - .v2 = @"curve25519-hkdf-sha256", -}; - NSArray *kKnownAgreementProtocols; NSArray *kKnownHashes; NSArray *kKnownMacs; @@ -48,556 +38,84 @@ @implementation MXLegacySASTransaction -@synthesize state = _state; -@synthesize sasBytes = _sasBytes; - -- (void)accept -{ - MXLogFailure(@"[MXKeyVerification] Cannot accept outgoing transaction"); -} - -- (NSString *)sasDecimal -{ - NSString *sasDecimal; - if (_sasBytes && [self.accepted.shortAuthenticationString containsObject:MXKeyVerificationSASModeDecimal]) - { - sasDecimal = [[MXLegacySASTransaction decimalRepresentationForSas:_sasBytes] componentsJoinedByString:@" "]; - } - - return sasDecimal; -} - -- (NSArray *)sasEmoji -{ - NSArray *sasEmoji; - if (_sasBytes && [self.accepted.shortAuthenticationString containsObject:MXKeyVerificationSASModeEmoji]) - { - sasEmoji = [MXLegacySASTransaction emojiRepresentationForSas:_sasBytes]; - } - - return sasEmoji; -} - -- (void)confirmSASMatch -{ - if (self.state != MXSASTransactionStateShowSAS) - { - // Ignore and cancel - MXLogDebug(@"[MXKeyVerification][MXSASTransaction] accept: Accepted short code from invalid state (%@)", @(self.state)); - [self cancelWithCancelCode:MXTransactionCancelCode.unexpectedMessage]; - - return; - } - - MXKeyVerificationMac *macContent = [self macContentWithDevice:self.manager.crypto.myDevice - andOtherDevice:self.otherDevice]; - - if (macContent) - { - self.state = MXSASTransactionStateWaitForPartnerToConfirm; - self.myMac = macContent; - - [self sendToOther:kMXEventTypeStringKeyVerificationMac content:macContent.JSONDictionary success:^{ - - } failure:^(NSError * _Nonnull error) { - MXLogDebug(@"[MXKeyVerification][MXSASTransaction] accept: sendToOther:kMXEventTypeStringKeyVerificationAccept failed. Error: %@", error); - self.error = error; - self.state = MXSASTransactionStateError; - }]; - - // If we already have the other mac, compare them - if (self.theirMac) - { - [self verifyMacs]; - } - } - else - { - MXLogDebug(@"[MXKeyVerification][MXSASTransaction] confirmSASMatch: Failed to send KeyMac, empty key hashes"); - [self cancelWithCancelCode:MXTransactionCancelCode.unexpectedMessage]; - } -} - -#pragma mark - SDK-Private methods - - -+ (void)initialize -{ - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - - kKnownAgreementProtocols = @[MXSASAgreementProtocols.v2, MXSASAgreementProtocols.v1]; - kKnownHashes = @[@"sha256"]; - kKnownMacs = @[MXKeyVerificationSASMacSha256, MXKeyVerificationSASMacSha256LongKdf]; - kKnownShortCodes = @[MXKeyVerificationSASModeEmoji, MXKeyVerificationSASModeDecimal]; - - [self initializeSasEmojis]; - }); -} - -- (instancetype)initWithOtherDevice:(MXDeviceInfo*)otherDevice andManager:(MXLegacyKeyVerificationManager*)manager -{ - self = [super initWithOtherDevice:otherDevice andManager:manager]; - if (self) - { - _olmSAS = [OLMSAS new]; - } - return self; -} - -- (void)handleAccept:(MXKeyVerificationAccept*)acceptContent -{ - // Must be handled by the specific implementation - NSAssert(NO, @"%@ does not implement handleAccept", self.class); -} - -- (void)handleKey:(MXKeyVerificationKey*)keyContent -{ - // Must be handled by the specific implementation - NSAssert(NO, @"%@ does not implement handleKey", self.class); -} - -- (NSString*)hashUsingAgreedHashMethod:(NSString*)string -{ - NSString *hashUsingAgreedHashMethod; - if ([_accepted.hashAlgorithm isEqualToString:@"sha256"]) - { - hashUsingAgreedHashMethod = [[OLMUtility new] sha256:[string dataUsingEncoding:NSUTF8StringEncoding]]; - } - else - { - MXLogDebug(@"[MXKeyVerification][MXSASTransaction] hashUsingAgreedHashMethod: Unsupported hash: %@", _accepted.hashAlgorithm); - } - - return hashUsingAgreedHashMethod; -} - -- (NSData*)generateSasBytesWithTheirPublicKey:(NSString*)theirPublicKey - requestingDevice:(MXDeviceInfo*)requestingDevice sasPublicKey:(NSString*)sasPublicKey - otherDevice:(MXDeviceInfo*)otherDevice otherSasPublicKey:(NSString*)otherSasPublicKey -{ - // Alice’s and Bob’s devices perform an Elliptic-curve Diffie-Hellman - // (calculate the point (x,y)=dAQB=dBQA and use x as the result of the ECDH), - // using the result as the shared secret. - [self.olmSAS setTheirPublicKey:theirPublicKey]; - - NSString *sasInfo; - if ([_accepted.keyAgreementProtocol isEqualToString:MXSASAgreementProtocols.v1]) - { - // (Note: In all of the following HKDF is as defined in RFC 5869, and uses the previously agreed-on hash function as the hash function, - // the shared secret as the input keying material, no salt, and with the input parameter set to the concatenation of: - // - the string “MATRIX_KEY_VERIFICATION_SAS”, - // - the Matrix ID of the user who sent the m.key.verification.start message, - // - the device ID of the device that sent the m.key.verification.start message, - // - the Matrix ID of the user who sent the m.key.verification.accept message, - // - he device ID of the device that sent the m.key.verification.accept message - // - the transaction ID. - sasInfo = [NSString stringWithFormat:@"MATRIX_KEY_VERIFICATION_SAS%@%@%@%@%@", - requestingDevice.userId, requestingDevice.deviceId, - otherDevice.userId, otherDevice.deviceId, - self.transactionId]; - } - else if ([_accepted.keyAgreementProtocol isEqualToString:MXSASAgreementProtocols.v2]) - { - // v2 adds the SAS public key, and separate by `|` to v1 - sasInfo = [NSString stringWithFormat:@"MATRIX_KEY_VERIFICATION_SAS|%@|%@|%@|%@|%@|%@|%@", - requestingDevice.userId, requestingDevice.deviceId, sasPublicKey, - otherDevice.userId, otherDevice.deviceId, otherSasPublicKey, - self.transactionId]; - } - - // decimal: generate five bytes by using HKDF - // emoji: generate six bytes by using HKDF - return [self.olmSAS generateBytes:sasInfo length:6]; -} - -- (NSString*)macUsingAgreedMethod:(NSString*)message info:(NSString*)info -{ - NSString *macUsingAgreedMethod; - NSError *error; - - if ([_accepted.messageAuthenticationCode isEqualToString:MXKeyVerificationSASMacSha256LongKdf]) - { - macUsingAgreedMethod = [_olmSAS calculateMacLongKdf:message info:info error:&error]; - } - else if ([_accepted.messageAuthenticationCode isEqualToString:MXKeyVerificationSASMacSha256]) - { - macUsingAgreedMethod = [_olmSAS calculateMac:message info:info error:&error]; - } - else - { - MXLogDebug(@"[MXKeyVerification][MXSASTransaction] macUsingAgreedMethod: Unsupported MAC format: %@", _accepted.messageAuthenticationCode); - } - - if (error) - { - MXLogDebug(@"[MXKeyVerification][MXSASTransaction] macUsingAgreedMethod: Error with MAC format: %@. Error: %@", _accepted.messageAuthenticationCode, error); - } - - return macUsingAgreedMethod; -} - -- (void)cancelWithCancelCode:(MXTransactionCancelCode*)code -{ - [self cancelWithCancelCode:code success:^{ - self.state = MXSASTransactionStateCancelledByMe; - } failure:^(NSError * _Nonnull error) { - MXLogDebug(@"[MXKeyVerification][MXSASTransaction] Fail to cancel with error: %@", error); - }]; -} - -#pragma mark - Incoming to_device events - -- (void)handleMac:(MXKeyVerificationMac*)macContent -{ - if (self.state != MXSASTransactionStateWaitForPartnerToConfirm - && self.state != MXSASTransactionStateShowSAS) - { - MXLogDebug(@"[MXKeyVerification] handleMac: wrong state: %@", self); - [self cancelWithCancelCode:MXTransactionCancelCode.unexpectedMessage]; - return; - } - - self.theirMac = macContent; - [self verifyMacs]; -} - -- (void)handleCancel:(MXKeyVerificationCancel *)cancelContent -{ - self.reasonCancelCode = [[MXTransactionCancelCode alloc] initWithValue:cancelContent.code - humanReadable:cancelContent.reason]; - - self.state = MXSASTransactionStateCancelled; -} - - -#pragma mark - Private methods - - -- (void)setState:(MXSASTransactionState)state -{ - MXLogDebug(@"[MXKeyVerification][MXSASTransaction] setState: %@ -> %@", @(_state), @(state)); - - _state = state; - [self didUpdateState]; -} - -- (MXKeyVerificationMac*)macContentWithDevice:(MXDeviceInfo*)device andOtherDevice:(MXDeviceInfo*)otherDevice -{ - MXKeyVerificationMac *macContent; - - // Alice and Bob’ devices calculate the HMAC of their own device keys and a comma-separated, - // sorted list of the key IDs that they wish the other user to verify, - // the shared secret as the input keying material, no salt, and with the input - // parameter set to the concatenation of: - // - the string “MATRIX_KEY_VERIFICATION_MAC”, - // - the Matrix ID of the user whose key is being MAC-ed, - // - the device ID of the device sending the MAC, - // - the Matrix ID of the other user, - // - the device ID of the device receiving the MAC, - // - the transaction ID, and - // - the key ID of the key being MAC-ed, or the string “KEY_IDS” if the item being MAC-ed is the list of key IDs. - NSString *baseInfo = [self baseInfoWithDevice:device andOtherDevice:otherDevice]; - - NSMutableDictionary *mac; - NSMutableArray* keyList = [NSMutableArray array]; - - // MAC with own device keys - MXKey *deviceKey = [[MXKey alloc] initWithType:kMXKeyEd25519Type - keyId:device.deviceId - value:@""]; - deviceKey.value = [self macUsingAgreedMethod:device.fingerprint - info:[NSString stringWithFormat:@"%@%@", baseInfo, deviceKey.keyFullId]]; - [keyList addObject:deviceKey.keyFullId]; - - mac = [deviceKey.JSONDictionary mutableCopy]; - - // MAC with own cross-signing key - MXCrossSigningInfo *myUserCrossSigningKeys = self.manager.crypto.crossSigning.myUserCrossSigningKeys; - if (myUserCrossSigningKeys) - { - NSString *crossSigningId = myUserCrossSigningKeys.masterKeys.keys; - MXKey *crossSigninKey = [[MXKey alloc] initWithType:kMXKeyEd25519Type - keyId:crossSigningId - value:@""]; - crossSigninKey.value = [self macUsingAgreedMethod:crossSigningId - info:[NSString stringWithFormat:@"%@%@", baseInfo, crossSigninKey.keyFullId]]; - [keyList addObject:crossSigninKey.keyFullId]; - - [mac addEntriesFromDictionary:crossSigninKey.JSONDictionary]; - } - - // MAC of the list of key IDs - NSString *keyListIds = [[keyList sortedArrayUsingSelector:@selector(compare:)] - componentsJoinedByString:@","]; - NSString *keyStrings = [self macUsingAgreedMethod:keyListIds - info:[NSString stringWithFormat:@"%@KEY_IDS", baseInfo]]; - - if (mac.count >= 1 && keyStrings.length) - { - macContent = [MXKeyVerificationMac new]; - macContent.transactionId = self.transactionId; - macContent.mac = mac; - macContent.keys = keyStrings; - - // TODO: To remove - MXLogDebug(@"keyListIds: %@", keyListIds); - MXLogDebug(@"otherUserMasterKeys: %@", myUserCrossSigningKeys.masterKeys.keys); - MXLogDebug(@"info: %@", [NSString stringWithFormat:@"%@%@", baseInfo, deviceKey.keyFullId]); - } - - return macContent; -} - -- (void)verifyMacs -{ - if (self.myMac && self.theirMac) - { - NSString *baseInfo = [self baseInfoWithDevice:self.otherDevice andOtherDevice:self.manager.crypto.myDevice]; - - // Check MAC of the list of key IDs - NSString *keyListIds = [[self.theirMac.mac.allKeys sortedArrayUsingSelector:@selector(compare:)] - componentsJoinedByString:@","]; - NSString *keyStrings = [self macUsingAgreedMethod:keyListIds - info:[NSString stringWithFormat:@"%@KEY_IDS", baseInfo]]; - - if (![self.theirMac.keys isEqualToString:keyStrings]) - { - [self cancelWithCancelCode:MXTransactionCancelCode.mismatchedKeys]; - return; - } - - - __block MXTransactionCancelCode *cancelCode; - dispatch_group_t group = dispatch_group_create(); - - MXCrossSigningKey *otherUserMasterKeys= [self.manager.crypto.crossSigning crossSigningKeysForUser:self.otherDevice.userId].masterKeys; - - for (NSString *keyFullId in self.theirMac.mac) - { - MXKey *key = [[MXKey alloc] initWithKeyFullId:keyFullId value:self.theirMac.mac[keyFullId]]; - - // Check MAC with device keys - MXDeviceInfo *device = [self.manager.crypto deviceWithDeviceId:key.keyId ofUser:self.otherDevice.userId]; - if (device) - { - if ([device.deviceId isEqualToString:otherUserMasterKeys.keys]) - { - MXLogWarning(@"[MXKeyVerification][MXSASTransaction] verifyMacs: Device id should not be the same as master key"); - cancelCode = MXTransactionCancelCode.invalidMessage; - break; - } - - if ([key.value isEqualToString:[self macUsingAgreedMethod:device.keys[keyFullId] - info:[NSString stringWithFormat:@"%@%@", baseInfo, keyFullId]]]) - { - // Mark device as verified - MXLogDebug(@"[MXKeyVerification][MXSASTransaction] verifyMacs: Mark device %@ as verified", device); - dispatch_group_enter(group); - [self.manager.crypto setDeviceVerification:MXDeviceVerified forDevice:self.otherDeviceId ofUser:self.otherUserId success:^{ - dispatch_group_leave(group); - - } failure:^(NSError *error) { - // Should never happen - cancelCode = MXTransactionCancelCode.invalidMessage; - - dispatch_group_leave(group); - }]; - } - else - { - MXLogDebug(@"[MXKeyVerification][MXSASTransaction] verifyMacs: ERROR: mac for device keys do not match: %@\vs %@", self.theirMac.JSONDictionary, self.myMac.JSONDictionary); - cancelCode = MXTransactionCancelCode.mismatchedKeys; - break; - } - } - else - { - // This key is maybe a cross-signing master key - if (otherUserMasterKeys) - { - // Check MAC with user's MSK keys - if ([key.value isEqualToString:[self macUsingAgreedMethod:otherUserMasterKeys.keys - info:[NSString stringWithFormat:@"%@%@", baseInfo, keyFullId]]]) - { - // Mark user as verified - MXLogDebug(@"[MXKeyVerification][MXSASTransaction] verifyMacs: Mark user %@ as verified", self.otherDevice.userId); - dispatch_group_enter(group); - [self.manager.crypto setUserVerification:YES forUser:self.otherDevice.userId success:^{ - dispatch_group_leave(group); - - } failure:^(NSError *error) { - // Should never happen - cancelCode = MXTransactionCancelCode.invalidMessage; - - dispatch_group_leave(group); - }]; - } - else - { - MXLogDebug(@"[MXKeyVerification][MXSASTransaction] verifyMacs: ERROR: mac for master keys do not match: %@\vs %@", self.theirMac.JSONDictionary, self.myMac.JSONDictionary); - MXLogDebug(@"keyListIds: %@", keyListIds); - MXLogDebug(@"otherUserMasterKeys: %@", otherUserMasterKeys.keys); - MXLogDebug(@"info: %@", [NSString stringWithFormat:@"%@%@", baseInfo, keyFullId]); - - cancelCode = MXTransactionCancelCode.mismatchedKeys; - break; - } - } - else - { - // Unknown key - MXLogDebug(@"[MXKeyVerification][MXSASTransaction] verifyMacs: Could not find keys %@ to verify", keyFullId); - } - } - } - - dispatch_group_notify(group, self.manager.crypto.cryptoQueue, ^{ - if (cancelCode) - { - [self cancelWithCancelCode:cancelCode]; - } - else - { - [self sendVerified]; - } - [self.manager removeTransactionWithTransactionId:self.transactionId]; - }); - } -} - -- (NSString*)baseInfoWithDevice:(MXDeviceInfo*)device andOtherDevice:(MXDeviceInfo*)otherDevice -{ - return [NSString stringWithFormat:@"MATRIX_KEY_VERIFICATION_MAC%@%@%@%@%@", - device.userId, device.deviceId, - otherDevice.userId, otherDevice.deviceId, - self.transactionId]; -} - -- (void)sendVerified -{ - // Inform the other peer we are done - MXKeyVerificationDone *doneContent = [MXKeyVerificationDone new]; - doneContent.transactionId = self.transactionId; - if (self.transport == MXKeyVerificationTransportDirectMessage) - { - doneContent.relatedEventId = self.dmEventId; - } - [self sendToOther:kMXEventTypeStringKeyVerificationDone content:doneContent.JSONDictionary success:^{} failure:^(NSError * _Nonnull error) {}]; - - self.state = MXSASTransactionStateVerified; -} - - -#pragma mark - Decimal representation - -+ (NSArray *)decimalRepresentationForSas:(NSData*)sas -{ - UInt8 *sasBytes = (UInt8 *)sas.bytes; - - /** - * +--------+--------+--------+--------+--------+ - * | Byte 0 | Byte 1 | Byte 2 | Byte 3 | Byte 4 | - * +--------+--------+--------+--------+--------+ - * bits: 87654321 87654321 87654321 87654321 87654321 - * \____________/\_____________/\____________/ - * 1st number 2nd number 3rd number - */ - return @[ - @((sasBytes[0] << 5 | sasBytes[1] >> 3) + 1000), - @(((sasBytes[1] & 0x7) << 10 | sasBytes[2] << 2 | sasBytes[3] >> 6) + 1000), - @(((sasBytes[3] & 0x3f) << 7 | sasBytes[4] >> 1) + 1000), - ]; -} - - #pragma mark - Emoji representation + (void)initializeSasEmojis { - kSasEmojis = @[ - [[MXEmojiRepresentation alloc] initWithEmoji:@"🐶" andName:@"dog"], // 0 - [[MXEmojiRepresentation alloc] initWithEmoji:@"🐱" andName:@"cat"], // 1 - [[MXEmojiRepresentation alloc] initWithEmoji:@"🦁" andName:@"lion"], // 2 - [[MXEmojiRepresentation alloc] initWithEmoji:@"🐎" andName:@"horse"], // 3 - [[MXEmojiRepresentation alloc] initWithEmoji:@"🦄" andName:@"unicorn"], // 4 - [[MXEmojiRepresentation alloc] initWithEmoji:@"🐷" andName:@"pig"], // 5 - [[MXEmojiRepresentation alloc] initWithEmoji:@"🐘" andName:@"elephant"], // 6 - [[MXEmojiRepresentation alloc] initWithEmoji:@"🐰" andName:@"rabbit"], // 7 - [[MXEmojiRepresentation alloc] initWithEmoji:@"🐼" andName:@"panda"], // 8 - [[MXEmojiRepresentation alloc] initWithEmoji:@"🐓" andName:@"rooster"], // 9 - [[MXEmojiRepresentation alloc] initWithEmoji:@"🐧" andName:@"penguin"], // 10 - [[MXEmojiRepresentation alloc] initWithEmoji:@"🐢" andName:@"turtle"], // 11 - [[MXEmojiRepresentation alloc] initWithEmoji:@"🐟" andName:@"fish"], // 12 - [[MXEmojiRepresentation alloc] initWithEmoji:@"🐙" andName:@"octopus"], // 13 - [[MXEmojiRepresentation alloc] initWithEmoji:@"🦋" andName:@"butterfly"], // 14 - [[MXEmojiRepresentation alloc] initWithEmoji:@"🌷" andName:@"flower"], // 15 - [[MXEmojiRepresentation alloc] initWithEmoji:@"🌳" andName:@"tree"], // 16 - [[MXEmojiRepresentation alloc] initWithEmoji:@"🌵" andName:@"cactus"], // 17 - [[MXEmojiRepresentation alloc] initWithEmoji:@"🍄" andName:@"mushroom"], // 18 - [[MXEmojiRepresentation alloc] initWithEmoji:@"🌏" andName:@"globe"], // 19 - [[MXEmojiRepresentation alloc] initWithEmoji:@"🌙" andName:@"moon"], // 20 - [[MXEmojiRepresentation alloc] initWithEmoji:@"☁️" andName:@"cloud"], // 21 - [[MXEmojiRepresentation alloc] initWithEmoji:@"🔥" andName:@"fire"], // 22 - [[MXEmojiRepresentation alloc] initWithEmoji:@"🍌" andName:@"banana"], // 23 - [[MXEmojiRepresentation alloc] initWithEmoji:@"🍎" andName:@"apple"], // 24 - [[MXEmojiRepresentation alloc] initWithEmoji:@"🍓" andName:@"strawberry"], // 25 - [[MXEmojiRepresentation alloc] initWithEmoji:@"🌽" andName:@"corn"], // 26 - [[MXEmojiRepresentation alloc] initWithEmoji:@"🍕" andName:@"pizza"], // 27 - [[MXEmojiRepresentation alloc] initWithEmoji:@"🎂" andName:@"cake"], // 28 - [[MXEmojiRepresentation alloc] initWithEmoji:@"❤️" andName:@"heart"], // 29 - [[MXEmojiRepresentation alloc] initWithEmoji:@"🙂" andName:@"smiley"], // 30 - [[MXEmojiRepresentation alloc] initWithEmoji:@"🤖" andName:@"robot"], // 31 - [[MXEmojiRepresentation alloc] initWithEmoji:@"🎩" andName:@"hat"], // 32 - [[MXEmojiRepresentation alloc] initWithEmoji:@"👓" andName:@"glasses"], // 33 - [[MXEmojiRepresentation alloc] initWithEmoji:@"🔧" andName:@"spanner"], // 34 - [[MXEmojiRepresentation alloc] initWithEmoji:@"🎅" andName:@"santa"], // 35 - [[MXEmojiRepresentation alloc] initWithEmoji:@"👍" andName:@"thumbs up"], // 36 - [[MXEmojiRepresentation alloc] initWithEmoji:@"☂️" andName:@"umbrella"], // 37 - [[MXEmojiRepresentation alloc] initWithEmoji:@"⌛" andName:@"hourglass"], // 38 - [[MXEmojiRepresentation alloc] initWithEmoji:@"⏰" andName:@"clock"], // 39 - [[MXEmojiRepresentation alloc] initWithEmoji:@"🎁" andName:@"gift"], // 40 - [[MXEmojiRepresentation alloc] initWithEmoji:@"💡" andName:@"light bulb"], // 41 - [[MXEmojiRepresentation alloc] initWithEmoji:@"📕" andName:@"book"], // 42 - [[MXEmojiRepresentation alloc] initWithEmoji:@"✏️" andName:@"pencil"], // 43 - [[MXEmojiRepresentation alloc] initWithEmoji:@"📎" andName:@"paperclip"], // 44 - [[MXEmojiRepresentation alloc] initWithEmoji:@"✂️" andName:@"scissors"], // 45 - [[MXEmojiRepresentation alloc] initWithEmoji:@"🔒" andName:@"lock"], // 46 - [[MXEmojiRepresentation alloc] initWithEmoji:@"🔑" andName:@"key"], // 47 - [[MXEmojiRepresentation alloc] initWithEmoji:@"🔨" andName:@"hammer"], // 48 - [[MXEmojiRepresentation alloc] initWithEmoji:@"☎️" andName:@"telephone"], // 49 - [[MXEmojiRepresentation alloc] initWithEmoji:@"🏁" andName:@"flag"], // 50 - [[MXEmojiRepresentation alloc] initWithEmoji:@"🚂" andName:@"train"], // 51 - [[MXEmojiRepresentation alloc] initWithEmoji:@"🚲" andName:@"bicycle"], // 52 - [[MXEmojiRepresentation alloc] initWithEmoji:@"✈️" andName:@"aeroplane"], // 53 - [[MXEmojiRepresentation alloc] initWithEmoji:@"🚀" andName:@"rocket"], // 54 - [[MXEmojiRepresentation alloc] initWithEmoji:@"🏆" andName:@"trophy"], // 55 - [[MXEmojiRepresentation alloc] initWithEmoji:@"⚽" andName:@"ball"], // 56 - [[MXEmojiRepresentation alloc] initWithEmoji:@"🎸" andName:@"guitar"], // 57 - [[MXEmojiRepresentation alloc] initWithEmoji:@"🎺" andName:@"trumpet"], // 58 - [[MXEmojiRepresentation alloc] initWithEmoji:@"🔔" andName:@"bell"], // 59 - [[MXEmojiRepresentation alloc] initWithEmoji:@"⚓️" andName:@"anchor"], // 60 - [[MXEmojiRepresentation alloc] initWithEmoji:@"🎧" andName:@"headphones"], // 61 - [[MXEmojiRepresentation alloc] initWithEmoji:@"📁" andName:@"folder"], // 62 - [[MXEmojiRepresentation alloc] initWithEmoji:@"📌" andName:@"pin"], // 63 - ]; + if (!kSasEmojis) + { + kSasEmojis = @[ + [[MXEmojiRepresentation alloc] initWithEmoji:@"🐶" andName:@"dog"], // 0 + [[MXEmojiRepresentation alloc] initWithEmoji:@"🐱" andName:@"cat"], // 1 + [[MXEmojiRepresentation alloc] initWithEmoji:@"🦁" andName:@"lion"], // 2 + [[MXEmojiRepresentation alloc] initWithEmoji:@"🐎" andName:@"horse"], // 3 + [[MXEmojiRepresentation alloc] initWithEmoji:@"🦄" andName:@"unicorn"], // 4 + [[MXEmojiRepresentation alloc] initWithEmoji:@"🐷" andName:@"pig"], // 5 + [[MXEmojiRepresentation alloc] initWithEmoji:@"🐘" andName:@"elephant"], // 6 + [[MXEmojiRepresentation alloc] initWithEmoji:@"🐰" andName:@"rabbit"], // 7 + [[MXEmojiRepresentation alloc] initWithEmoji:@"🐼" andName:@"panda"], // 8 + [[MXEmojiRepresentation alloc] initWithEmoji:@"🐓" andName:@"rooster"], // 9 + [[MXEmojiRepresentation alloc] initWithEmoji:@"🐧" andName:@"penguin"], // 10 + [[MXEmojiRepresentation alloc] initWithEmoji:@"🐢" andName:@"turtle"], // 11 + [[MXEmojiRepresentation alloc] initWithEmoji:@"🐟" andName:@"fish"], // 12 + [[MXEmojiRepresentation alloc] initWithEmoji:@"🐙" andName:@"octopus"], // 13 + [[MXEmojiRepresentation alloc] initWithEmoji:@"🦋" andName:@"butterfly"], // 14 + [[MXEmojiRepresentation alloc] initWithEmoji:@"🌷" andName:@"flower"], // 15 + [[MXEmojiRepresentation alloc] initWithEmoji:@"🌳" andName:@"tree"], // 16 + [[MXEmojiRepresentation alloc] initWithEmoji:@"🌵" andName:@"cactus"], // 17 + [[MXEmojiRepresentation alloc] initWithEmoji:@"🍄" andName:@"mushroom"], // 18 + [[MXEmojiRepresentation alloc] initWithEmoji:@"🌏" andName:@"globe"], // 19 + [[MXEmojiRepresentation alloc] initWithEmoji:@"🌙" andName:@"moon"], // 20 + [[MXEmojiRepresentation alloc] initWithEmoji:@"☁️" andName:@"cloud"], // 21 + [[MXEmojiRepresentation alloc] initWithEmoji:@"🔥" andName:@"fire"], // 22 + [[MXEmojiRepresentation alloc] initWithEmoji:@"🍌" andName:@"banana"], // 23 + [[MXEmojiRepresentation alloc] initWithEmoji:@"🍎" andName:@"apple"], // 24 + [[MXEmojiRepresentation alloc] initWithEmoji:@"🍓" andName:@"strawberry"], // 25 + [[MXEmojiRepresentation alloc] initWithEmoji:@"🌽" andName:@"corn"], // 26 + [[MXEmojiRepresentation alloc] initWithEmoji:@"🍕" andName:@"pizza"], // 27 + [[MXEmojiRepresentation alloc] initWithEmoji:@"🎂" andName:@"cake"], // 28 + [[MXEmojiRepresentation alloc] initWithEmoji:@"❤️" andName:@"heart"], // 29 + [[MXEmojiRepresentation alloc] initWithEmoji:@"🙂" andName:@"smiley"], // 30 + [[MXEmojiRepresentation alloc] initWithEmoji:@"🤖" andName:@"robot"], // 31 + [[MXEmojiRepresentation alloc] initWithEmoji:@"🎩" andName:@"hat"], // 32 + [[MXEmojiRepresentation alloc] initWithEmoji:@"👓" andName:@"glasses"], // 33 + [[MXEmojiRepresentation alloc] initWithEmoji:@"🔧" andName:@"spanner"], // 34 + [[MXEmojiRepresentation alloc] initWithEmoji:@"🎅" andName:@"santa"], // 35 + [[MXEmojiRepresentation alloc] initWithEmoji:@"👍" andName:@"thumbs up"], // 36 + [[MXEmojiRepresentation alloc] initWithEmoji:@"☂️" andName:@"umbrella"], // 37 + [[MXEmojiRepresentation alloc] initWithEmoji:@"⌛" andName:@"hourglass"], // 38 + [[MXEmojiRepresentation alloc] initWithEmoji:@"⏰" andName:@"clock"], // 39 + [[MXEmojiRepresentation alloc] initWithEmoji:@"🎁" andName:@"gift"], // 40 + [[MXEmojiRepresentation alloc] initWithEmoji:@"💡" andName:@"light bulb"], // 41 + [[MXEmojiRepresentation alloc] initWithEmoji:@"📕" andName:@"book"], // 42 + [[MXEmojiRepresentation alloc] initWithEmoji:@"✏️" andName:@"pencil"], // 43 + [[MXEmojiRepresentation alloc] initWithEmoji:@"📎" andName:@"paperclip"], // 44 + [[MXEmojiRepresentation alloc] initWithEmoji:@"✂️" andName:@"scissors"], // 45 + [[MXEmojiRepresentation alloc] initWithEmoji:@"🔒" andName:@"lock"], // 46 + [[MXEmojiRepresentation alloc] initWithEmoji:@"🔑" andName:@"key"], // 47 + [[MXEmojiRepresentation alloc] initWithEmoji:@"🔨" andName:@"hammer"], // 48 + [[MXEmojiRepresentation alloc] initWithEmoji:@"☎️" andName:@"telephone"], // 49 + [[MXEmojiRepresentation alloc] initWithEmoji:@"🏁" andName:@"flag"], // 50 + [[MXEmojiRepresentation alloc] initWithEmoji:@"🚂" andName:@"train"], // 51 + [[MXEmojiRepresentation alloc] initWithEmoji:@"🚲" andName:@"bicycle"], // 52 + [[MXEmojiRepresentation alloc] initWithEmoji:@"✈️" andName:@"aeroplane"], // 53 + [[MXEmojiRepresentation alloc] initWithEmoji:@"🚀" andName:@"rocket"], // 54 + [[MXEmojiRepresentation alloc] initWithEmoji:@"🏆" andName:@"trophy"], // 55 + [[MXEmojiRepresentation alloc] initWithEmoji:@"⚽" andName:@"ball"], // 56 + [[MXEmojiRepresentation alloc] initWithEmoji:@"🎸" andName:@"guitar"], // 57 + [[MXEmojiRepresentation alloc] initWithEmoji:@"🎺" andName:@"trumpet"], // 58 + [[MXEmojiRepresentation alloc] initWithEmoji:@"🔔" andName:@"bell"], // 59 + [[MXEmojiRepresentation alloc] initWithEmoji:@"⚓️" andName:@"anchor"], // 60 + [[MXEmojiRepresentation alloc] initWithEmoji:@"🎧" andName:@"headphones"], // 61 + [[MXEmojiRepresentation alloc] initWithEmoji:@"📁" andName:@"folder"], // 62 + [[MXEmojiRepresentation alloc] initWithEmoji:@"📌" andName:@"pin"], // 63 + ]; + } } + (NSArray *)allEmojiRepresentations { + [MXLegacySASTransaction initializeSasEmojis]; return kSasEmojis; } -+ (NSArray *)emojiRepresentationForSas:(NSData*)sas -{ - UInt8 *sasBytes = (UInt8 *)sas.bytes; - - return @[ - kSasEmojis[sasBytes[0] >> 2], - kSasEmojis[(sasBytes[0] & 0x3) << 4 | sasBytes[1] >> 4], - kSasEmojis[(sasBytes[1] & 0xf) << 2 | sasBytes[2] >> 6], - kSasEmojis[sasBytes[2] & 0x3f], - kSasEmojis[sasBytes[3] >> 2], - kSasEmojis[(sasBytes[3] & 0x3) << 4 | sasBytes[4] >> 4], - kSasEmojis[(sasBytes[4] & 0xf) << 2 | sasBytes[5] >> 6] - ]; -} - @end diff --git a/MatrixSDK/Crypto/Verification/Transactions/SAS/MXSASTransaction_Private.h b/MatrixSDK/Crypto/Verification/Transactions/SAS/MXSASTransaction_Private.h deleted file mode 100644 index e5714cdc77..0000000000 --- a/MatrixSDK/Crypto/Verification/Transactions/SAS/MXSASTransaction_Private.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - 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. - */ - -#import "MXSASTransaction.h" -#import "MXKeyVerificationTransaction_Private.h" -#import "MXSASKeyVerificationStart.h" - -#import - -NS_ASSUME_NONNULL_BEGIN - -#pragma mark - Constants - -extern const struct MXSASAgreementProtocols { - __unsafe_unretained NSString *v1; - __unsafe_unretained NSString *v2; -} MXSASAgreementProtocols; - -FOUNDATION_EXPORT NSArray *kKnownAgreementProtocols; -FOUNDATION_EXPORT NSArray *kKnownHashes; -FOUNDATION_EXPORT NSArray *kKnownMacs; -FOUNDATION_EXPORT NSArray *kKnownShortCodes; - -/** - The `MXKeyVerificationTransaction` extension exposes internal operations. - */ -@interface MXLegacySASTransaction () - -@property (nonatomic) MXSASTransactionState state; -@property (nonatomic) OLMSAS *olmSAS; -@property (nonatomic, nullable) MXSASKeyVerificationStart *startContent; -@property (nonatomic) MXKeyVerificationAccept *accepted; - -@property (nonatomic, nullable) MXKeyVerificationMac *myMac; -@property (nonatomic, nullable) MXKeyVerificationMac *theirMac; - -- (void)handleAccept:(MXKeyVerificationAccept*)acceptContent; -- (void)handleKey:(MXKeyVerificationKey*)keyContent; -- (void)handleMac:(MXKeyVerificationMac*)macContent; - -- (NSString*)hashUsingAgreedHashMethod:(NSString*)string; -- (NSData*)generateSasBytesWithTheirPublicKey:(NSString*)theirPublicKey - requestingDevice:(MXDeviceInfo*)requestingDevice sasPublicKey:(NSString*)sasPublicKey - otherDevice:(MXDeviceInfo*)otherDevice otherSasPublicKey:(NSString*)otherSasPublicKey; - -@end - -NS_ASSUME_NONNULL_END diff --git a/MatrixSDK/IdentityServer/MXIdentityServerRestClient.m b/MatrixSDK/IdentityServer/MXIdentityServerRestClient.m index 68279159c6..fb3d8227e6 100644 --- a/MatrixSDK/IdentityServer/MXIdentityServerRestClient.m +++ b/MatrixSDK/IdentityServer/MXIdentityServerRestClient.m @@ -17,7 +17,7 @@ #import "MXIdentityServerRestClient.h" #import -#import +#import #import "MXHTTPClient.h" #import "MXError.h" @@ -388,9 +388,11 @@ - (MXHTTPOperation*)lookup3pids:(NSArray*> *)threepids case MXIdentityServerHashAlgorithmSHA256: { NSString *threePidConcatenation = [NSString stringWithFormat:@"%@ %@ %@", threepid, medium, pepper]; - - OLMUtility *olmUtility = [OLMUtility new]; - NSString *hashedSha256ThreePid = [olmUtility sha256:[threePidConcatenation dataUsingEncoding:NSUTF8StringEncoding]]; + + // Hash the concatenated string using the helper method + NSString *hashedSha256ThreePid = sha256(threePidConcatenation); + + // Convert hashed SHA-256 string to base64 URL hashedTreePid = [MXBase64Tools base64ToBase64Url:hashedSha256ThreePid]; threePidArrayByThreePidConcatHash[hashedTreePid] = threepidArray; @@ -483,6 +485,20 @@ - (MXHTTPOperation*)lookup3pids:(NSArray*> *)threepids } +// Helper method to perform SHA256 hashing +NSString *sha256(NSString *input) { + const char *str = [input UTF8String]; + unsigned char result[CC_SHA256_DIGEST_LENGTH]; + CC_SHA256(str, (CC_LONG)strlen(str), result); + + NSMutableString *hash = [NSMutableString stringWithCapacity:CC_SHA256_DIGEST_LENGTH * 2]; + for (int i = 0; i < CC_SHA256_DIGEST_LENGTH; i++) { + [hash appendFormat:@"%02x", result[i]]; + } + + return hash; +} + #pragma mark Establishing associations - (MXHTTPOperation*)requestEmailValidation:(NSString*)email diff --git a/MatrixSDK/MatrixSDK.h b/MatrixSDK/MatrixSDK.h index 9f56bca97a..0695176848 100644 --- a/MatrixSDK/MatrixSDK.h +++ b/MatrixSDK/MatrixSDK.h @@ -73,11 +73,8 @@ FOUNDATION_EXPORT NSString *MatrixSDKVersion; #import "MXKeyVerificationRequestByDMJSONModel.h" #import "MXSASKeyVerificationStart.h" #import "MXQRCodeKeyVerificationStart.h" -#import "MXKeyBackupAlgorithm.h" #import "MXCurve25519BackupAuthData.h" #import "MXAes256BackupAuthData.h" -#import "MXCurve25519KeyBackupAlgorithm.h" -#import "MXAes256KeyBackupAlgorithm.h" #import "MXKeyBackupPassword.h" #import "MXAes.h" @@ -154,22 +151,16 @@ FOUNDATION_EXPORT NSString *MatrixSDKVersion; #import "MXPushGatewayRestClient.h" #import "MXEncryptedAttachments.h" #import "MXLoginSSOIdentityProviderBrand.h" +#import "MXDecryptionResult.h" // Bridging to Swift #import "MXCryptoConstants.h" -#import "MXCryptoStore.h" -#import "MXRealmCryptoStore.h" -#import "MXCryptoAlgorithms.h" -#import "MXOlmDevice.h" #import "MXEventDecryptionResult.h" #import "MXPushRuleEventMatchConditionChecker.h" #import "MXPushRuleDisplayNameCondtionChecker.h" #import "MXPushRuleRoomMemberCountConditionChecker.h" #import "MXPushRuleSenderNotificationPermissionConditionChecker.h" -#import "MXMegolmDecryption.h" -#import "MXOlmDecryption.h" #import "MXCachedSyncResponse.h" -#import "MXBackgroundCryptoStore.h" #import "MXSharedHistoryKeyService.h" #import "MXRoomKeyEventContent.h" #import "MXForwardedRoomKeyEventContent.h" @@ -177,6 +168,8 @@ FOUNDATION_EXPORT NSString *MatrixSDKVersion; #import "MXCryptoTools.h" #import "MXRecoveryKey.h" #import "MXSecretShareSend.h" +#import "MXCryptoSecretStore.h" +#import "MXCryptoVersion.h" // Sync response models #import "MXSyncResponse.h" diff --git a/MatrixSDK/MatrixSDKVersion.m b/MatrixSDK/MatrixSDKVersion.m index 5ec5ca3e3b..2c484b282b 100644 --- a/MatrixSDK/MatrixSDKVersion.m +++ b/MatrixSDK/MatrixSDKVersion.m @@ -16,4 +16,4 @@ #import -NSString *const MatrixSDKVersion = @"0.27.14"; +NSString *const MatrixSDKVersion = @"0.27.15"; diff --git a/MatrixSDK/PushGateway/MXPushGatewayRestClient.m b/MatrixSDK/PushGateway/MXPushGatewayRestClient.m index e7d396e0ce..a213ffafe0 100644 --- a/MatrixSDK/PushGateway/MXPushGatewayRestClient.m +++ b/MatrixSDK/PushGateway/MXPushGatewayRestClient.m @@ -17,7 +17,6 @@ #import "MXPushGatewayRestClient.h" #import -#import #import "MXError.h" #import "MXTools.h" diff --git a/MatrixSDK/Utils/MXBugReportRestClient.m b/MatrixSDK/Utils/MXBugReportRestClient.m index 578a3b2b0f..e722c1be4d 100644 --- a/MatrixSDK/Utils/MXBugReportRestClient.m +++ b/MatrixSDK/Utils/MXBugReportRestClient.m @@ -31,7 +31,6 @@ #endif #ifdef MX_CRYPTO -#import #endif #if __has_include() diff --git a/MatrixSDKTests/Crypto/Algorithms/Megolm/MXMegolmDecryptionUnitTests.swift b/MatrixSDKTests/Crypto/Algorithms/Megolm/MXMegolmDecryptionUnitTests.swift deleted file mode 100644 index 344f49e0d2..0000000000 --- a/MatrixSDKTests/Crypto/Algorithms/Megolm/MXMegolmDecryptionUnitTests.swift +++ /dev/null @@ -1,328 +0,0 @@ -// -// Copyright 2022 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 -import MatrixSDKCrypto -@testable import MatrixSDK - -class MXMegolmDecryptionUnitTests: XCTestCase { - /// Spy session used to assert on the results of the test - struct SpySession: Equatable { - let sharedHistory: Bool - } - - /// Spy olm device used to collect spy sessions for the purpose of test assertions - class DeviceStub: MXOlmDevice { - var sessions: [SpySession] = [] - - override func addInboundGroupSession( - _ sessionId: String!, - sessionKey: String!, - roomId: String!, - senderKey: String!, - forwardingCurve25519KeyChain: [String]!, - keysClaimed: [String : String]!, - exportFormat: Bool, - sharedHistory: Bool, - untrusted: Bool - ) -> Bool { - sessions.append( - .init(sharedHistory: sharedHistory) - ) - return true - } - - var stubbedResult: MXDecryptionResult? - - override func decryptGroupMessage( - _ body: String!, - isEditEvent: Bool, - roomId: String!, - inTimeline timeline: String!, - sessionId: String!, - senderKey: String! - ) throws -> MXDecryptionResult { - stubbedResult ?? MXDecryptionResult() - } - } - - /// Stub of the session which returns a preconfigured summary of rooms - class SessionStub: MXSession { - var historyVisibility: String = kMXRoomHistoryVisibilityWorldReadable - - override func room(withRoomId roomId: String!) -> MXRoom! { - return MXRoom(roomId: roomId, andMatrixSession: self) - } - - override func roomSummary(withRoomId roomId: String!) -> MXRoomSummary! { - let summary = MXRoomSummary() - summary.historyVisibility = historyVisibility - return summary - } - } - - class CryptoStoreStub: MXRealmCryptoStore { - var sessions: [String: MXOlmInboundGroupSession] = [:] - - override func inboundGroupSession( - withId sessionId: String!, - andSenderKey senderKey: String! - ) -> MXOlmInboundGroupSession! { - return sessions["\(sessionId!)-\(senderKey!)"] - } - } - - /// Stub of crypto which connects various other stubbed objects (device, session) - class CryptoStub: MXLegacyCrypto { - private let device: MXOlmDevice - private let cryptoStore: MXCryptoStore - private let session: MXSession - private let devices: MXDeviceList - - init(device: MXOlmDevice, store: MXCryptoStore, session: MXSession, devices: MXDeviceList) { - self.device = device - self.cryptoStore = store - self.session = session - self.devices = devices - } - - override var olmDevice: MXOlmDevice! { - return device - } - - override var cryptoQueue: DispatchQueue! { - return DispatchQueue.main - } - - override var store: MXCryptoStore! { - return cryptoStore - } - - override var mxSession: MXSession! { - return session - } - - override var deviceList: MXDeviceList! { - return devices - } - - override func trustLevel(forUser userId: String) -> MXUserTrustLevel { - return MXUserTrustLevel(crossSigningVerified: true, locallyVerified: true) - } - } - - class DeviceListStub: MXDeviceList { - var stubbedDevices = [String: Device]() - override func device(withIdentityKey senderKey: String!, andAlgorithm algorithm: String!) -> MXDeviceInfo! { - guard algorithm != nil, let senderKey = senderKey, let device = stubbedDevices[senderKey] else { - return nil - } - - return .init(device: .init(device: device)) - } - } - - let roomId1 = "ABC" - let roomId2 = "XYZ" - let sessionId1 = "123" - let sessionId2 = "999" - let senderKey = "456" - - var device: DeviceStub! - var store: CryptoStoreStub! - var session: SessionStub! - var crypto: CryptoStub! - var devicesList: DeviceListStub! - var decryption: MXMegolmDecryption! - - override func setUp() { - super.setUp() - - device = DeviceStub() - store = CryptoStoreStub() - session = SessionStub() - devicesList = DeviceListStub() - crypto = CryptoStub(device: device, store: store, session: session, devices: devicesList) - decryption = MXMegolmDecryption(crypto: crypto) - } - - // Ensure that `sharedHistory` is added to an inbound session only - // if explicitly set by the room key event - func test_onRoomKeyEvent_addsSessionSharedHistoryFromEvent() { - MXSDKOptions.sharedInstance().enableRoomSharedHistoryOnInvite = true - - // Configure a set of event values and outcome values where `sharedHistory` - // on the final session is set to whatever value the event has, defaulting - // to false if not specified. This is even if the room's history is currently - // set to shared or world readable - let eventToExpectation: [(Bool?, Bool)] = [ - (nil, false), - (false, false), - (true, true) - ] - session.historyVisibility = kMXRoomHistoryVisibilityWorldReadable - - for (eventValue, expectedValue) in eventToExpectation { - let event = MXEvent.roomKeyFixture( - sharedHistory: eventValue - ) - device.sessions = [] - - decryption.onRoomKeyEvent(event) - - XCTAssertEqual(device.sessions.count, 1) - XCTAssertEqual(device.sessions.first, SpySession(sharedHistory: expectedValue)) - } - } - - // Ensure that checking for the presence of shared history, only session - // with matching roomId is queried - func test_hasSharedHistory_queriesSessionWithMatchingRoomId() { - // Parametrize the test via a configuration and a set of cases - // with expected outcome - struct Configuration { - let sharedHistory: Bool - let sessionId: String - let roomId: String - } - let allCases = [ - - // Mismatched session id -> does not have keys - ( - Configuration( - sharedHistory: true, - sessionId: sessionId2, - roomId: roomId1 - ), - false - ), - - // Mismatched room id -> does not have keys - ( - Configuration( - sharedHistory: true, - sessionId: sessionId1, - roomId: roomId2 - ), - false - ), - - // Matching session and room id but not sharing history -> does not have keys - ( - Configuration( - sharedHistory: false, - sessionId: sessionId1, - roomId: roomId1 - ), - false - ), - - // Matching session and room id and sharing history -> has keys - ( - Configuration( - sharedHistory: true, - sessionId: sessionId1, - roomId: roomId1 - ), - true - ) - ] - - for (config, expectedValue) in allCases { - let session = MXOlmInboundGroupSession() - session.sharedHistory = config.sharedHistory - session.roomId = config.roomId - - store.sessions = [ - "\(config.sessionId)-\(senderKey)": session - ] - - let hasSharedHistory = decryption.hasSharedHistory(forRoomId: roomId1, sessionId: sessionId1, senderKey: senderKey) - XCTAssertEqual(hasSharedHistory, expectedValue) - } - } - - func test_decryptEvent_untrustedResult() { - let event = MXEvent.encryptedFixture() - device.stubbedResult = MXDecryptionResult() - device.stubbedResult?.senderKey = "ABCD" - - devicesList.stubbedDevices = [ - "ABCD": Device.stub( - locallyTrusted: false, - crossSigningTrusted: true - ) - ] - - device.stubbedResult?.isUntrusted = true - let result1 = decryption.decryptEvent(event, inTimeline: nil) - XCTAssertEqual(result1?.decoration.color, .grey) - - device.stubbedResult?.isUntrusted = false - let result2 = decryption.decryptEvent(event, inTimeline: nil) - XCTAssertEqual(result2?.decoration.color, MXEventDecryptionDecorationColor.none) - } - - func test_decryptEvent_untrustedDevice() { - let event = MXEvent.encryptedFixture() - device.stubbedResult = MXDecryptionResult() - device.stubbedResult?.senderKey = "XYZ" - - devicesList.stubbedDevices = [ - "XYZ": Device.stub( - locallyTrusted: false, - crossSigningTrusted: false - ) - ] - let result1 = decryption.decryptEvent(event, inTimeline: nil) - XCTAssertEqual(result1?.decoration.color, .red) - - devicesList.stubbedDevices = [ - "XYZ": Device.stub( - locallyTrusted: false, - crossSigningTrusted: true - ) - ] - let result2 = decryption.decryptEvent(event, inTimeline: nil) - XCTAssertEqual(result2?.decoration.color, MXEventDecryptionDecorationColor.none) - } - - func test_decryptEvent_unknownDevice() { - let invalidKey = "123" - let validKey = "456" - - let event = MXEvent.encryptedFixture() - device.stubbedResult = MXDecryptionResult() - device.stubbedResult?.senderKey = validKey - - let stub = Device.stub( - locallyTrusted: true, - crossSigningTrusted: true - ) - - devicesList.stubbedDevices = [ - invalidKey: stub - ] - let result1 = decryption.decryptEvent(event, inTimeline: nil) - XCTAssertEqual(result1?.decoration.color, .red) - - devicesList.stubbedDevices = [ - validKey: stub - ] - let result2 = decryption.decryptEvent(event, inTimeline: nil) - XCTAssertEqual(result2?.decoration.color, MXEventDecryptionDecorationColor.none) - } -} diff --git a/MatrixSDKTests/Crypto/Algorithms/Megolm/MXMegolmEncryptionTests.swift b/MatrixSDKTests/Crypto/Algorithms/Megolm/MXMegolmEncryptionTests.swift deleted file mode 100644 index 8915048019..0000000000 --- a/MatrixSDKTests/Crypto/Algorithms/Megolm/MXMegolmEncryptionTests.swift +++ /dev/null @@ -1,200 +0,0 @@ -// -// Copyright 2022 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 -@testable import MatrixSDK - -class MXMegolmEncryptionTests: XCTestCase { - struct SessionIds: Equatable { - let inbound: [String] - let outbound: [String] - } - - private var data: MatrixSDKTestsData! - private var e2eData: MatrixSDKTestsE2EData! - override func setUp() { - super.setUp() - - data = MatrixSDKTestsData() - e2eData = MatrixSDKTestsE2EData(matrixSDKTestsData: data) - } - - private func storedSessionIds(in session: MXSession!) -> SessionIds { - let store = MXRealmCryptoStore(credentials: session.matrixRestClient!.credentials) - return SessionIds( - inbound: store!.inboundGroupSessions().map { - $0.session.sessionIdentifier() - }, - outbound: store!.outboundGroupSessions().map { - $0.sessionId - } - ) - } - - private func isSharedHistoryInLastSession(for session: MXSession!) -> Bool { - let store = MXRealmCryptoStore(credentials: session.matrixRestClient!.credentials) - return store?.inboundGroupSessions().last?.sharedHistory == true - } - - func test_resetsSession_ifRoomVisibilityChanges() { - MXSDKOptions.sharedInstance().enableRoomSharedHistoryOnInvite = true - - // The following tests that oubound session Id (and therefore the related inbound session Id) - // is reset whenever the room's history visibility changes from shared to not shared. - e2eData.doE2ETestWithAlice(inARoom: self) { session, roomId, expectation in - - let room = session?.room(withRoomId: roomId) - - // 1st set of messages - room?.sendTextMessages(messages: ["Hi", "Hello"]) { _ in - - // After first few messages we only expect one inbound and one outbound session - let sessionIds1 = self.storedSessionIds(in: session) - XCTAssertEqual(sessionIds1.outbound.count, 1) - XCTAssertEqual(sessionIds1.inbound.count, 1) - XCTAssertEqual(sessionIds1.inbound, sessionIds1.outbound) - XCTAssertTrue(self.isSharedHistoryInLastSession(for: session)) - - // 2nd set of messages - room?.sendTextMessages(messages: ["Hi", "Hello"]) { _ in - - // After second batch of messages nothing has changed that would require resetting - // of sessions, therefore sessionIds are unchanged - let sessionIds2 = self.storedSessionIds(in: session) - XCTAssertEqual(sessionIds2, sessionIds1) - XCTAssertTrue(self.isSharedHistoryInLastSession(for: session)) - - // Changing room visibility from shared by default to more restrictive will reset session keys - room?.setHistoryVisibility(.joined) { _ in - - // 3rd set of messages - room?.sendTextMessages(messages: ["Hi", "Hello"]) { _ in - - // Whilst there is still only one onbound session, its identifier has now changed, - // and inbound sessions have incremented - let sessionIds3 = self.storedSessionIds(in: session) - XCTAssertEqual(sessionIds3.outbound.count, 1) - XCTAssertNotEqual(sessionIds3.outbound, sessionIds2.outbound) - XCTAssertEqual(sessionIds3.inbound.count, 2) - XCTAssertEqual(sessionIds3.inbound, sessionIds1.outbound + sessionIds3.outbound) - XCTAssertFalse(self.isSharedHistoryInLastSession(for: session)) - - // 4th set of messages - room?.sendTextMessages(messages: ["Hi", "Hello"]) { _ in - // After fourth batch of messages nothing has changed that would require resetting - // of sessions, therefore sessionIds are unchanged - let sessionIds4 = self.storedSessionIds(in: session) - XCTAssertEqual(sessionIds4, sessionIds3) - XCTAssertFalse(self.isSharedHistoryInLastSession(for: session)) - - // Final visibility change back to shared will reset sessions once again - room?.setHistoryVisibility(.worldReadable) { _ in - room?.sendTextMessages(messages: ["Hi", "Hello"]) { _ in - - // Still only one outbound session with new ID, and three inbound sessions - let sessionIds5 = self.storedSessionIds(in: session) - XCTAssertEqual(sessionIds5.outbound.count, 1) - XCTAssertNotEqual(sessionIds5.outbound, sessionIds4.outbound) - XCTAssertEqual(sessionIds5.inbound.count, 3) - XCTAssertEqual(sessionIds5.inbound, sessionIds1.outbound + sessionIds3.outbound + sessionIds5.outbound) - XCTAssertTrue(self.isSharedHistoryInLastSession(for: session)) - - session?.close() - expectation?.fulfill() - } - } - } - } - } - } - } - } - } - - func test_doesNotResetsSession_ifRoomVisibilityChanges_andFeatureDisabled() { - MXSDKOptions.sharedInstance().enableRoomSharedHistoryOnInvite = false - - // The following tests that if the feature is disabled, then outbound session Id - // is not reset when room visibility changes - e2eData.doE2ETestWithAlice(inARoom: self) { session, roomId, expectation in - - let room = session?.room(withRoomId: roomId) - - // 1st set of messages - room?.sendTextMessages(messages: ["Hi", "Hello"]) { _ in - - // After first few messages we only expect one inbound and one outbound session - let sessionIds1 = self.storedSessionIds(in: session) - XCTAssertEqual(sessionIds1.outbound.count, 1) - XCTAssertEqual(sessionIds1.inbound.count, 1) - XCTAssertEqual(sessionIds1.inbound, sessionIds1.outbound) - XCTAssertFalse(self.isSharedHistoryInLastSession(for: session)) - - // 2nd set of messages - room?.sendTextMessages(messages: ["Hi", "Hello"]) { _ in - - // After second batch of messages nothing has changed that would require resetting - // of sessions, therefore sessionIds are unchanged - let sessionIds2 = self.storedSessionIds(in: session) - XCTAssertEqual(sessionIds2, sessionIds1) - XCTAssertFalse(self.isSharedHistoryInLastSession(for: session)) - - // Changing room visibility from shared by default to more restrictive will not reset session keys - room?.setHistoryVisibility(.joined) { _ in - - // 3rd set of messages - room?.sendTextMessages(messages: ["Hi", "Hello"]) { _ in - - // Sessions are identical as before - let sessionIds3 = self.storedSessionIds(in: session) - XCTAssertEqual(sessionIds3.outbound.count, 1) - XCTAssertEqual(sessionIds3.outbound, sessionIds2.outbound) - XCTAssertEqual(sessionIds3.inbound.count, 1) - XCTAssertEqual(sessionIds3.inbound, sessionIds1.outbound) - XCTAssertFalse(self.isSharedHistoryInLastSession(for: session)) - - // 4th set of messages - room?.sendTextMessages(messages: ["Hi", "Hello"]) { _ in - // After fourth batch of messages nothing has changed that would require resetting - // of sessions, therefore sessionIds are unchanged - let sessionIds4 = self.storedSessionIds(in: session) - XCTAssertEqual(sessionIds4, sessionIds3) - XCTAssertFalse(self.isSharedHistoryInLastSession(for: session)) - - // Final visibility change back to shared will still not reset sessions - room?.setHistoryVisibility(.worldReadable) { _ in - room?.sendTextMessages(messages: ["Hi", "Hello"]) { _ in - - // Sessions are still identical as before - let sessionIds5 = self.storedSessionIds(in: session) - XCTAssertEqual(sessionIds5.outbound.count, 1) - XCTAssertEqual(sessionIds5.outbound, sessionIds4.outbound) - XCTAssertEqual(sessionIds5.inbound.count, 1) - XCTAssertEqual(sessionIds5.inbound, sessionIds1.outbound) - XCTAssertFalse(self.isSharedHistoryInLastSession(for: session)) - - session?.close() - expectation?.fulfill() - } - } - } - } - } - } - } - } - } -} diff --git a/MatrixSDKTests/Crypto/Data/MXOlmInboundGroupSessionUnitTests.swift b/MatrixSDKTests/Crypto/Data/MXOlmInboundGroupSessionUnitTests.swift deleted file mode 100644 index 26a06a9585..0000000000 --- a/MatrixSDKTests/Crypto/Data/MXOlmInboundGroupSessionUnitTests.swift +++ /dev/null @@ -1,55 +0,0 @@ -// -// Copyright 2022 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 -@testable import MatrixSDK - -class MXOlmInboundGroupSessionUnitTests: XCTestCase { - func testExportsCorrectSessionData() { - let session = MXOlmInboundGroupSession() - session.senderKey = "A" - session.forwardingCurve25519KeyChain = ["B", "C"] - session.keysClaimed = ["D": "E"] - session.roomId = "F" - session.sharedHistory = true - - let data = session.exportData() - - XCTAssertEqual(data?.senderKey, "A") - XCTAssertEqual(data?.forwardingCurve25519KeyChain, ["B", "C"]) - XCTAssertEqual(data?.senderClaimedKeys, ["D": "E"]) - XCTAssertEqual(data?.roomId, "F") - XCTAssert(data?.sharedHistory == true) - } - - func testCanEncodeAndDecodeObject() { - let session = MXOlmInboundGroupSession() - session.senderKey = "A" - session.forwardingCurve25519KeyChain = ["B", "C"] - session.keysClaimed = ["D": "E"] - session.roomId = "F" - session.sharedHistory = true - - let data = NSKeyedArchiver.archivedData(withRootObject: session) - let result = NSKeyedUnarchiver.unarchiveObject(with: data) as! MXOlmInboundGroupSession - - XCTAssertEqual(result.senderKey, session.senderKey) - XCTAssertEqual(result.forwardingCurve25519KeyChain, session.forwardingCurve25519KeyChain) - XCTAssertEqual(result.keysClaimed, session.keysClaimed) - XCTAssertEqual(result.roomId, session.roomId) - XCTAssertEqual(result.sharedHistory, session.sharedHistory) - } -} diff --git a/MatrixSDKTests/Crypto/Data/Store/MXMemoryCryptoStore.swift b/MatrixSDKTests/Crypto/Data/Store/MXMemoryCryptoStore.swift deleted file mode 100644 index 2b1c240c56..0000000000 --- a/MatrixSDKTests/Crypto/Data/Store/MXMemoryCryptoStore.swift +++ /dev/null @@ -1,547 +0,0 @@ -// -// Copyright 2022 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 -import OLMKit - -public class MXMemoryCryptoStore: NSObject, MXCryptoStore { - - private static var stores: [MXCredentials: MXMemoryCryptoStore] = [:] - - private let credentials: MXCredentials - private var storeAccount: Account? - private var devices: [String: [MXDeviceInfo]] = [:] - private var algorithms: [String: RoomAlgorithm] = [:] - private var inboundSessions: [InboundSession] = [] - private var outboundSessions: [String: MXOlmOutboundGroupSession] = [:] - private var secrets: [String: String] = [:] - private var incomingRoomKeyRequestsMap: [String: MXIncomingRoomKeyRequest] = [:] - private var outgoingRoomKeyRequests: [String: MXOutgoingRoomKeyRequest] = [:] - private var olmSessions: [OlmSessionMapKey: MXOlmSession] = [:] - private var crossSigningKeysMap: [String: MXCrossSigningInfo] = [:] - private var sharedOutboundSessions: [SharedOutboundSession] = [] - - // MARK: - MXCryptoStore - - public required init!(credentials: MXCredentials!) { - self.credentials = credentials - storeAccount = Account() - storeAccount?.userId = credentials.userId - storeAccount?.deviceId = credentials.deviceId - storeAccount?.cryptoVersion = MXCryptoVersion(rawValue: MXCryptoVersion.versionCount.rawValue - 1) ?? .versionUndefined - super.init() - } - - public static func hasData(for credentials: MXCredentials!) -> Bool { - stores[credentials] != nil - } - - public static func createStore(with credentials: MXCredentials!) -> Self! { - if let existingStore = stores[credentials] as? Self { - return existingStore - } - if let newStore = Self(credentials: credentials) { - stores[credentials] = newStore - return newStore - } - return nil - } - - public static func delete(with credentials: MXCredentials!) { - stores.removeValue(forKey: credentials) - } - - public static func deleteAllStores() { - stores.removeAll() - } - - public static func deleteReadonlyStore(with credentials: MXCredentials!) { - // no-op - } - - // MARK: - User ID - - public func userId() -> String! { - storeAccount?.userId - } - - // MARK: - Device ID - - public func storeDeviceId(_ deviceId: String!) { - storeAccount?.deviceId = deviceId - } - - public func deviceId() -> String! { - storeAccount?.deviceId - } - - // MARK: - Account - - public func setAccount(_ account: OLMAccount!) { - storeAccount?.olmAccount = account - } - - public func account() -> OLMAccount! { - storeAccount?.olmAccount - } - - public func performAccountOperation(_ block: ((OLMAccount?) -> Void)!) { - block?(storeAccount?.olmAccount) - } - - // MARK: - Device Sync Token - - public func storeDeviceSyncToken(_ deviceSyncToken: String!) { - storeAccount?.deviceSyncToken = deviceSyncToken - } - - public func deviceSyncToken() -> String! { - storeAccount?.deviceSyncToken - } - - // MARK: - Devices - - public func storeDevice(forUser userId: String!, device: MXDeviceInfo!) { - if devices[userId] == nil { - devices[userId] = [] - } - devices[userId]?.append(device) - } - - public func device(withDeviceId deviceId: String!, forUser userId: String!) -> MXDeviceInfo! { - devices[userId]?.first { $0.deviceId == deviceId } - } - - public func device(withIdentityKey identityKey: String!) -> MXDeviceInfo! { - Array(devices.values).flatMap { $0 }.first { $0.identityKey == identityKey } - } - - public func storeDevices(forUser userId: String!, devices: [String : MXDeviceInfo]!) { - if self.devices[userId] != nil { - // Reset all previously stored devices for this user - self.devices.removeValue(forKey: userId) - } - - self.devices[userId] = Array(devices.values) - } - - public func devices(forUser userId: String!) -> [String : MXDeviceInfo]! { - let devices = devices[userId] ?? [] - - var result: [String: MXDeviceInfo] = [:] - - for device in devices { - result[device.deviceId] = device - } - - return result - } - - // MARK: - Device Tracking Status - - public func deviceTrackingStatus() -> [String : NSNumber]! { - storeAccount?.deviceTrackingStatus - } - - public func storeDeviceTrackingStatus(_ statusMap: [String : NSNumber]!) { - storeAccount?.deviceTrackingStatus = statusMap - } - - // MARK: - Cross Signing Keys - - public func storeCrossSigningKeys(_ crossSigningInfo: MXCrossSigningInfo!) { - crossSigningKeysMap[crossSigningInfo.userId] = crossSigningInfo - } - - public func crossSigningKeys(forUser userId: String!) -> MXCrossSigningInfo! { - crossSigningKeysMap[userId] - } - - public func crossSigningKeys() -> [MXCrossSigningInfo]! { - Array(crossSigningKeysMap.values) - } - - // MARK: - Room Algorithm - - public func storeAlgorithm(forRoom roomId: String!, algorithm: String!) { - algorithms[roomId] = RoomAlgorithm(algorithm: algorithm) - } - - public func algorithm(forRoom roomId: String!) -> String! { - algorithms[roomId]?.algorithm - } - - // MARK: - Room Settings - - public func roomSettings() -> [MXRoomSettings]! { - return algorithms.compactMap { roomId, item in - do { - return try MXRoomSettings( - roomId: roomId, - algorithm: item.algorithm, - blacklistUnverifiedDevices: item.blacklistUnverifiedDevices - ) - } catch { - MXLog.debug("[MXMemoryCryptoStore] roomSettings: Failed creating algorithm", context: error) - return nil - } - } - } - - // MARK: - OLM Session - - public func store(_ session: MXOlmSession!) { - let key = OlmSessionMapKey(sessionId: session.session.sessionIdentifier(), deviceKey: session.deviceKey) - olmSessions[key] = session - } - - public func session(withDevice deviceKey: String!, andSessionId sessionId: String!) -> MXOlmSession! { - let key = OlmSessionMapKey(sessionId: sessionId, deviceKey: deviceKey) - return olmSessions[key] - } - - public func performSessionOperation(withDevice deviceKey: String!, andSessionId sessionId: String!, block: ((MXOlmSession?) -> Void)!) { - let session = session(withDevice: deviceKey, andSessionId: sessionId) - block?(session) - } - - public func sessions(withDevice deviceKey: String!) -> [MXOlmSession]! { - Array(olmSessions.filter { $0.key.deviceKey == deviceKey }.values) - } - - public func enumerateSessions(by batchSize: Int, block: (([MXOlmSession]?, Double) -> Void)!) { - block(Array(olmSessions.values), 1) - } - - public func sessionsCount() -> UInt { - UInt(olmSessions.count) - } - - // MARK: - Inbound Group Sessions - - public func store(_ sessions: [MXOlmInboundGroupSession]!) { - inboundSessions.append(contentsOf: sessions.map { InboundSession(session: $0) } ) - } - - public func inboundGroupSession(withId sessionId: String!, andSenderKey senderKey: String!) -> MXOlmInboundGroupSession! { - inboundSessions.first { $0.sessionId == sessionId && $0.session.senderKey == senderKey }?.session - } - - public func performSessionOperationWithGroupSession(withId sessionId: String!, senderKey: String!, block: ((MXOlmInboundGroupSession?) -> Void)!) { - let session = inboundGroupSession(withId: sessionId, andSenderKey: senderKey) - block?(session) - } - - public func inboundGroupSessions() -> [MXOlmInboundGroupSession]! { - inboundSessions.map { $0.session } - } - - public func enumerateInboundGroupSessions(by batchSize: Int, block: (([MXOlmInboundGroupSession]?, Set?, Double) -> Void)!) { - let backedUp = inboundSessions.filter { $0.backedUp }.map(\.sessionId) - block(inboundGroupSessions(), Set(backedUp), 1) - } - - public func inboundGroupSessions(withSessionId sessionId: String!) -> [MXOlmInboundGroupSession]! { - inboundSessions.filter { $0.sessionId == sessionId }.map { $0.session } - } - - public func removeInboundGroupSession(withId sessionId: String!, andSenderKey senderKey: String!) { - inboundSessions.removeAll { $0.sessionId == sessionId && $0.session.senderKey == senderKey } - } - - // MARK: - Outbound Group Sessions - - public func store(_ session: OLMOutboundGroupSession!, withRoomId roomId: String!) -> MXOlmOutboundGroupSession! { - let creationTime: TimeInterval - - if let existingSession = outboundSessions[roomId], - existingSession.sessionId == session.sessionIdentifier() { - // Update the existing one - creationTime = existingSession.creationTime - } else { - creationTime = Date().timeIntervalSince1970 - } - - if let newSession = MXOlmOutboundGroupSession(session: session, roomId: roomId, creationTime: creationTime) { - outboundSessions[roomId] = newSession - return newSession - } - - return nil - } - - public func outboundGroupSession(withRoomId roomId: String!) -> MXOlmOutboundGroupSession! { - outboundSessions[roomId] - } - - public func outboundGroupSessions() -> [MXOlmOutboundGroupSession]! { - Array(outboundSessions.values) - } - - public func removeOutboundGroupSession(withRoomId roomId: String!) { - outboundSessions.removeValue(forKey: roomId) - } - - // MARK: - Shared Devices - - public func storeSharedDevices(_ devices: MXUsersDevicesMap!, messageIndex: UInt, forOutboundGroupSessionInRoomWithId roomId: String!, sessionId: String!) { - for userId in devices.userIds() { - for deviceId in devices.deviceIds(forUser: userId) { - guard let device = device(withDeviceId: deviceId, forUser: userId) else { - continue - } - - let session = SharedOutboundSession(roomId: roomId, sessionId: sessionId, device: device, messageIndex: messageIndex) - sharedOutboundSessions.append(session) - } - } - } - - public func sharedDevicesForOutboundGroupSessionInRoom(withId roomId: String!, sessionId: String!) -> MXUsersDevicesMap! { - let result = MXUsersDevicesMap() - - let sessions = sharedOutboundSessions.filter { $0.roomId == roomId && $0.sessionId == sessionId } - - for session in sessions { - result.setObject(NSNumber(value: session.messageIndex), - forUser: session.device.userId, - andDevice: session.device.deviceId) - } - - return result - } - - public func messageIndexForSharedDeviceInRoom(withId roomId: String!, sessionId: String!, userId: String!, deviceId: String!) -> NSNumber! { - guard let index = sharedOutboundSessions.first(where: { $0.roomId == roomId - && $0.sessionId == sessionId - && $0.device.deviceId == deviceId })?.messageIndex else { - return nil - } - return NSNumber(value: index) - } - - // MARK: - Backup Markers - - public var backupVersion: String! { - get { - storeAccount?.backupVersion - } set { - storeAccount?.backupVersion = newValue - } - } - - public func resetBackupMarkers() { - inboundSessions.forEach { $0.backedUp = false } - } - - public func markBackupDone(for sessions: [MXOlmInboundGroupSession]!) { - for session in sessions { - inboundSessions.filter({ $0.sessionId == session.session.sessionIdentifier() }).forEach { $0.backedUp = true } - } - } - - public func inboundGroupSessions(toBackup limit: UInt) -> [MXOlmInboundGroupSession]! { - let toBackup = inboundSessions.filter { !$0.backedUp } - if toBackup.isEmpty { - return [] - } - let toDrop = toBackup.count > limit ? toBackup.count - Int(limit) : 0 - return toBackup.dropLast(toDrop).map { $0.session } - } - - public func inboundGroupSessionsCount(_ onlyBackedUp: Bool) -> UInt { - UInt(onlyBackedUp ? inboundSessions.filter { $0.backedUp }.count : inboundSessions.count) - } - - // MARK: - Outgoing Room Key Requests - - public func outgoingRoomKeyRequest(withRequestBody requestBody: [AnyHashable : Any]!) -> MXOutgoingRoomKeyRequest! { - outgoingRoomKeyRequests.first(where: { NSDictionary(dictionary: $1.requestBody).isEqual(to: requestBody) })?.value - } - - public func outgoingRoomKeyRequest(with state: MXRoomKeyRequestState) -> MXOutgoingRoomKeyRequest! { - outgoingRoomKeyRequests.first(where: { $0.value.state == state })?.value - } - - public func allOutgoingRoomKeyRequests(with state: MXRoomKeyRequestState) -> [MXOutgoingRoomKeyRequest]! { - Array(outgoingRoomKeyRequests.filter { $1.state == state }.values) - } - - public func allOutgoingRoomKeyRequests(withRoomId roomId: String!, sessionId: String!, algorithm: String!, senderKey: String!) -> [MXOutgoingRoomKeyRequest]! { - Array(outgoingRoomKeyRequests.filter { - $1.roomId == roomId - && $1.sessionId == sessionId - && $1.algorithm == algorithm - && $1.senderKey == senderKey - }.values) - } - - public func store(_ request: MXOutgoingRoomKeyRequest!) { - outgoingRoomKeyRequests[request.requestId] = request - } - - public func update(_ request: MXOutgoingRoomKeyRequest!) { - outgoingRoomKeyRequests[request.requestId] = request - } - - public func deleteOutgoingRoomKeyRequest(withRequestId requestId: String!) { - outgoingRoomKeyRequests.removeValue(forKey: requestId) - } - - // MARK: - Incoming Room Key Requests - - public func store(_ request: MXIncomingRoomKeyRequest!) { - incomingRoomKeyRequestsMap[request.requestId] = request - } - - public func deleteIncomingRoomKeyRequest(_ requestId: String!, fromUser userId: String!, andDevice deviceId: String!) { - let toBeRemoved = incomingRoomKeyRequestsMap.filter { $1.requestId == requestId && $1.userId == userId && $1.deviceId == deviceId } - for identifier in toBeRemoved { - incomingRoomKeyRequestsMap.removeValue(forKey: identifier.key) - } - } - - public func incomingRoomKeyRequest(withRequestId requestId: String!, fromUser userId: String!, andDevice deviceId: String!) -> MXIncomingRoomKeyRequest! { - incomingRoomKeyRequestsMap.first(where: { $1.requestId == requestId && $1.userId == userId && $1.deviceId == deviceId })?.value - } - - public func incomingRoomKeyRequests() -> MXUsersDevicesMap! { - let result = MXUsersDevicesMap() - - for request in incomingRoomKeyRequestsMap { - if let requests = result.object(forDevice: request.value.deviceId, forUser: request.value.userId) { - requests.add(request.value) - } else { - let requests = NSMutableArray(object: request.value) - result.setObject(requests, forUser: request.value.userId, andDevice: request.value.deviceId) - } - } - - return result as? MXUsersDevicesMap - } - - // MARK: - Secrets - - public func storeSecret(_ secret: String, withSecretId secretId: String) { - secrets[secretId] = secret - } - - public func hasSecret(withSecretId secretId: String) -> Bool { - return secrets[secretId] != nil - } - - public func secret(withSecretId secretId: String) -> String? { - secrets[secretId] - } - - public func deleteSecret(withSecretId secretId: String) { - secrets.removeValue(forKey: secretId) - } - - // MARK: - Blacklist Unverified Devices - - public var globalBlacklistUnverifiedDevices: Bool { - get { - storeAccount?.globalBlacklistUnverifiedDevices ?? false - } set { - storeAccount?.globalBlacklistUnverifiedDevices = newValue - } - } - - public func blacklistUnverifiedDevices(inRoom roomId: String!) -> Bool { - algorithms[roomId]?.blacklistUnverifiedDevices ?? false - } - - public func storeBlacklistUnverifiedDevices(inRoom roomId: String!, blacklist: Bool) { - if let algorithm = algorithms[roomId] { - algorithm.blacklistUnverifiedDevices = blacklist - } else { - algorithms[roomId] = RoomAlgorithm(algorithm: nil, blacklistUnverifiedDevices: blacklist) - } - } - - // MARK: - Crypto Version - - public var cryptoVersion: MXCryptoVersion { - get { - storeAccount?.cryptoVersion ?? .versionUndefined - } set { - storeAccount?.cryptoVersion = newValue - } - } - -} - -// MARK: - Models - -// MARK: InboundSession - -private class InboundSession { - let session: MXOlmInboundGroupSession - var backedUp: Bool - - var sessionId: String { - session.session.sessionIdentifier() - } - - init(session: MXOlmInboundGroupSession, - backedUp: Bool = false) { - self.session = session - self.backedUp = backedUp - } -} - -// MARK: OlmSessionMapKey - -private struct OlmSessionMapKey: Hashable { - let sessionId: String - let deviceKey: String -} - -// MARK: Account - -private struct Account { - var userId: String? - var deviceId: String? - var cryptoVersion: MXCryptoVersion = .versionUndefined - var deviceSyncToken: String? - var olmAccount: OLMAccount? - var backupVersion: String? - var globalBlacklistUnverifiedDevices: Bool = false - var deviceTrackingStatus: [String : NSNumber]? -} - -// MARK: SharedOutboundSession - -private struct SharedOutboundSession { - let roomId: String - let sessionId: String - let device: MXDeviceInfo - let messageIndex: UInt -} - -// MARK: RoomAlgorithm - -private class RoomAlgorithm { - let algorithm: String? - var blacklistUnverifiedDevices: Bool - - init(algorithm: String?, - blacklistUnverifiedDevices: Bool = false) { - self.algorithm = algorithm - self.blacklistUnverifiedDevices = blacklistUnverifiedDevices - } -} diff --git a/MatrixSDKTests/Crypto/Data/Store/MXRealmCryptoStore/MXRealmCryptoStoreTests.swift b/MatrixSDKTests/Crypto/Data/Store/MXRealmCryptoStore/MXRealmCryptoStoreTests.swift deleted file mode 100644 index 303670f7fe..0000000000 --- a/MatrixSDKTests/Crypto/Data/Store/MXRealmCryptoStore/MXRealmCryptoStoreTests.swift +++ /dev/null @@ -1,114 +0,0 @@ -// -// 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 -@testable import MatrixSDK - -class MXRealmCryptoStoreTests: XCTestCase { - var store: MXRealmCryptoStore! - override func setUp() { - store = MXRealmCryptoStore() - } - - override func tearDown() { - MXRealmCryptoStore.deleteAllStores() - } - - func makeSession( - deviceKey: String = "XYZ" - ) -> MXOlmSession { - return MXOlmSession(olmSession: OLMSession(), deviceKey: deviceKey) - } - - func makeGroupSession( - roomId: String = "ABC", - senderKey: String? = "Bob", - isUntrusted: Bool = false, - backedUp: Bool = false - ) -> MXOlmInboundGroupSession { - let device = MXOlmDevice(store: store)! - let outbound = device.createOutboundGroupSessionForRoom(withRoomId: roomId) - - let session = MXOlmInboundGroupSession(sessionKey: outbound!.sessionKey)! - session.senderKey = senderKey - session.roomId = roomId - session.keysClaimed = ["A": "1"] - session.isUntrusted = isUntrusted - return session - } - - // MARK: - Olm sessions - - func test_saveAndLoadSession() { - let session = makeSession() - - store.store(session) - XCTAssertEqual(store.sessionsCount(), 1) - - let fetched = store.sessions(withDevice: "XYZ") - XCTAssertEqual(fetched?.count, 1) - } - - func test_enumerateSessions() { - for i in 0 ..< 15 { - let session = makeSession(deviceKey: "\(i)") - store.store(session) - } - - XCTAssertEqual(store.sessionsCount(), 15) - - var count = 0 - var batches = 0 - store.enumerateSessions(by: 4) { sessions, _ in - count += sessions?.count ?? 0 - batches += 1 - } - - XCTAssertEqual(count, 15) - XCTAssertEqual(batches, 4) - } - - // MARK: - Megolm sessions - - func test_saveAndLoadGroupSession() { - let session = makeGroupSession() - - store.store([session]) - XCTAssertEqual(store.inboundGroupSessionsCount(false), 1) - - let fetched = store.inboundGroupSessions() - XCTAssertEqual(fetched?.count, 1) - } - - func test_enumerateGroupSessions() { - for _ in 0 ..< 111 { - let session = makeGroupSession() - store.store([session]) - } - - XCTAssertEqual(store.inboundGroupSessionsCount(false), 111) - - var count = 0 - var batches = 0 - store.enumerateInboundGroupSessions(by: 20) { sessions, backedUp, progress in - count += sessions?.count ?? 0 - batches += 1 - } - - XCTAssertEqual(count, 111) - XCTAssertEqual(batches, 6) - } -} diff --git a/MatrixSDKTests/Crypto/KeyBackup/Engine/MXCryptoKeyBackupEngineUnitTests.swift b/MatrixSDKTests/Crypto/KeyBackup/Engine/MXCryptoKeyBackupEngineUnitTests.swift index ba9271fe0b..828bb989ce 100644 --- a/MatrixSDKTests/Crypto/KeyBackup/Engine/MXCryptoKeyBackupEngineUnitTests.swift +++ b/MatrixSDKTests/Crypto/KeyBackup/Engine/MXCryptoKeyBackupEngineUnitTests.swift @@ -106,127 +106,4 @@ class MXCryptoKeyBackupEngineUnitTests: XCTestCase { XCTFail("Unknown error \(error)") } } - - func test_prepareKeyBackupVersion() { - backup.stubbedSignature = [ - "Bob": [ - "ed25519": "123" - ] - ] - - let exp = expectation(description: "exp") - engine.prepareKeyBackupVersion( - withPassword: "pass", - algorithm: nil, - success: { info in - - let privateKey = try! MXRecoveryKey.decode(info.recoveryKey) - let publicKey = OLMPkDecryption().setPrivateKey(privateKey, error: nil) - - let authData = info.authData as? MXCurve25519BackupAuthData - XCTAssertEqual(info.algorithm, kMXCryptoCurve25519KeyBackupAlgorithm) - XCTAssertEqual(authData?.publicKey, publicKey) - XCTAssertEqual(authData?.signatures as? NSDictionary, [ - "Bob": [ - "ed25519": "123" - ] - ]) - - exp.fulfill() - }, - failure: { - exp.fulfill() - XCTFail("Unknown error \($0)") - } - ) - - waitForExpectations(timeout: 1) - } - - func test_importKeys() { - // Prepare private and public key for encryption - let key = BackupRecoveryKey() - let privateKey = try! MXRecoveryKey.decode(key.toBase58()) - let publicKey = OLMPkDecryption().setPrivateKey(privateKey, error: nil) - - // Encrypt some data - let encryption = OLMPkEncryption() - encryption.setRecipientKey(publicKey) - let message = MXTools.serialiseJSONObject([ - "session_key": "SESSION_KEY_ABC" - ])! - let encrypted = encryption.encryptMessage(message, error: nil) - - // Prepare encrypted backup keys - let keys = MXKeysBackupData(fromJSON: [ - "rooms": [ - "A": [ - "sessions": [ - "1": [ - "session_data": [ - "ciphertext": encrypted.ciphertext, - "mac": encrypted.mac, - "ephemeral": encrypted.ephemeralKey - ] - ], - "2": [ - "session_data": [ - "ciphertext": encrypted.ciphertext, - "mac": encrypted.mac, - "ephemeral": encrypted.ephemeralKey - ] - ] - ] - ], - "B": [ - "sessions": [ - "3": [ - "session_data": [ - "ciphertext": encrypted.ciphertext, - "mac": encrypted.mac, - "ephemeral": encrypted.ephemeralKey - ] - ] - ] - ], - ] - ])! - - // Import keys - let exp = expectation(description: "exp") - engine.importKeys( - with: keys, - privateKey: privateKey, - keyBackupVersion: .stub(), - success: { total, imported in - - XCTAssertEqual(total, 3) - XCTAssertEqual(imported, 3) - - let decryptedKeys = self.backup.roomKeysSpy! - let sessionKeys = decryptedKeys.compactMap { $0.sessionKey } - let untrusted = decryptedKeys.compactMap { $0.isUntrusted } - let sessionIds = decryptedKeys.compactMap { $0.sessionId } - let roomIds = decryptedKeys.compactMap { $0.roomId } - - XCTAssertEqual(decryptedKeys.count, 3) - XCTAssertEqual(Set(sessionKeys), ["SESSION_KEY_ABC", "SESSION_KEY_ABC", "SESSION_KEY_ABC"]) - XCTAssertEqual(Set(untrusted), [true, true, true]) - XCTAssertEqual(Set(sessionIds), ["1", "2", "3"]) - XCTAssertEqual(Set(roomIds), ["A", "B"]) - - Task { - let sessionIds = await self.decryptor.spySessionIds - XCTAssertEqual(Set(sessionIds), ["1", "2", "3"]) - exp.fulfill() - } - }, - failure: { - XCTFail("Importing failed with error \($0)") - exp.fulfill() - } - ) - - waitForExpectations(timeout: 1) - } } diff --git a/MatrixSDKTests/Crypto/KeySharing/MXSharedHistoryKeyManagerUnitTests.swift b/MatrixSDKTests/Crypto/KeySharing/MXSharedHistoryKeyManagerUnitTests.swift deleted file mode 100644 index 101e7c9d36..0000000000 --- a/MatrixSDKTests/Crypto/KeySharing/MXSharedHistoryKeyManagerUnitTests.swift +++ /dev/null @@ -1,260 +0,0 @@ -// -// Copyright 2022 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 -import XCTest -@testable import MatrixSDK - -class MXSharedHistoryKeyManagerUnitTests: XCTestCase { - class CryptoStub: MXLegacyCrypto { - var devices = MXUsersDevicesMap() - - override func downloadKeys(_ userIds: [String], forceDownload: Bool, success: ((MXUsersDevicesMap, [String : MXCrossSigningInfo]) -> Void)?, failure: ((Error) -> Void)? = nil) -> MXHTTPOperation? { - success?(devices, [:]) - return MXHTTPOperation() - } - } - - class SpyService: NSObject, MXSharedHistoryKeyService { - struct SessionStub: Hashable { - let roomId: String - let sessionId: String - let senderKey: String - } - - var sharedHistory: Set? - func hasSharedHistory(forRoomId roomId: String!, sessionId: String!, senderKey: String!) -> Bool { - guard let sharedHistory = sharedHistory else { - return true - } - - let session = SessionStub(roomId: roomId, sessionId: sessionId, senderKey: senderKey) - return sharedHistory.contains(session) - } - - var requests = [MXSharedHistoryKeyRequest]() - func shareKeys(for request: MXSharedHistoryKeyRequest!, success: (() -> Void)!, failure: ((Error?) -> Void)!) { - requests.append(request) - success?() - } - } - - class EnumeratorStub: NSObject, MXEventsEnumerator { - var messages: [MXEvent] = [] - - func nextEventsBatch(_ eventsCount: UInt, threadId: String!) -> [MXEvent]! { - return nil - } - - var nextEvent: MXEvent? { - if messages.isEmpty { - return nil - } - return messages.removeFirst() - } - - var remaining: UInt { - return UInt(messages.count) - } - } - - var enumerator: EnumeratorStub! - var crypto: CryptoStub! - var service: SpyService! - var manager: MXSharedHistoryKeyManager! - - override func setUp() { - super.setUp() - - enumerator = EnumeratorStub() - crypto = CryptoStub() - crypto.devices.setObject(MXDeviceInfo(deviceId: "1"), forUser: "user1", andDevice: "1") - - service = SpyService() - } - - private func makeEvent( - sessionId: String = "123", - senderKey: String = "456" - ) -> MXEvent { - MXEvent(fromJSON: [ - "room_id": "ABC", - "type": kMXEventTypeStringRoomEncrypted, - "content": [ - "session_id": sessionId, - "sender_key": senderKey, - ] - ]) - } - - private func makeInboundSession( - roomId: String = "ABC", - sessionId: String = "123", - senderKey: String = "456" - ) -> SpyService.SessionStub { - return .init(roomId: roomId, sessionId: sessionId, senderKey: senderKey) - } - - private func shareKeys( - userId: String = "user1", - roomId: String = "ABC", - enumerator: MXEventsEnumerator? = nil, - limit: Int = .max - ) { - manager = MXSharedHistoryKeyManager(roomId: roomId, crypto: crypto, service: service) - manager.shareMessageKeys( - withUserId: userId, - messageEnumerator: enumerator ?? self.enumerator, - limit: limit - ) - } - - func testDoesNotCreateRequestIfNoKnownDevices() { - enumerator.messages = [ - makeEvent(sessionId: "A", senderKey: "B") - ] - crypto.devices = MXUsersDevicesMap() - - shareKeys() - - XCTAssertEqual(service.requests.count, 0) - } - - func testCreateRequestForSingleMessage() { - enumerator.messages = [ - makeEvent(sessionId: "A", senderKey: "B") - ] - crypto.devices.setObject(MXDeviceInfo(deviceId: "1"), forUser: "user1", andDevice: "1") - crypto.devices.setObject(MXDeviceInfo(deviceId: "2"), forUser: "user1", andDevice: "2") - crypto.devices.setObject(MXDeviceInfo(deviceId: "3"), forUser: "user2", andDevice: "3") - - shareKeys() - - XCTAssertEqual(service.requests.count, 1) - XCTAssertEqual( - service.requests.first, - MXSharedHistoryKeyRequest( - userId: "user1", - devices: [ - MXDeviceInfo(deviceId: "1"), - MXDeviceInfo(deviceId: "2") - ], - roomId: "ABC", - sessionId: "A", - senderKey: "B" - ) - ) - } - - func testCreateOneRequestPerSessionIdAndSenderKey() { - enumerator.messages = [ - makeEvent(sessionId: "1", senderKey: "A"), - makeEvent(sessionId: "1", senderKey: "B"), - makeEvent(sessionId: "1", senderKey: "A"), - makeEvent(sessionId: "2", senderKey: "A"), - makeEvent(sessionId: "3", senderKey: "A"), - makeEvent(sessionId: "2", senderKey: "A"), - makeEvent(sessionId: "3", senderKey: "B"), - ] - - shareKeys() - - let identifiers = service.requests.map { [$0.sessionId, $0.senderKey] } - XCTAssertEqual(service.requests.count, 5) - XCTAssertTrue(identifiers.contains(["1", "A"])) - XCTAssertTrue(identifiers.contains(["1", "B"])) - XCTAssertTrue(identifiers.contains(["2", "A"])) - XCTAssertTrue(identifiers.contains(["3", "A"])) - XCTAssertTrue(identifiers.contains(["3", "B"])) - } - - func testCreateRequestsWithinLimit() { - enumerator.messages = [ - makeEvent(sessionId: "5"), - makeEvent(sessionId: "4"), - makeEvent(sessionId: "3"), - makeEvent(sessionId: "2"), - makeEvent(sessionId: "1"), - ] - - shareKeys(limit: 3) - - let identifiers = service.requests.map { $0.sessionId } - XCTAssertEqual(service.requests.count, 3) - XCTAssertEqual(Set(identifiers), ["5", "4", "3"]) - } - - func testCreateRequestsOnlyForSessionsWithSharedHistory() { - enumerator.messages = [ - makeEvent(sessionId: "1"), - makeEvent(sessionId: "2"), - makeEvent(sessionId: "3"), - makeEvent(sessionId: "4"), - makeEvent(sessionId: "5"), - ] - service.sharedHistory = [ - makeInboundSession(sessionId: "1"), - makeInboundSession(sessionId: "2"), - makeInboundSession(sessionId: "4"), - ] - - shareKeys() - - let identifiers = service.requests.map { $0.sessionId } - XCTAssertEqual(service.requests.count, 3) - XCTAssertEqual(Set(identifiers), ["1", "2", "4"]) - } - - func testIgnoresEventsWithMismatchedRoomId() { - enumerator.messages = [ - makeEvent(sessionId: "1"), - makeEvent(sessionId: "2"), - makeEvent(sessionId: "3"), - ] - service.sharedHistory = [ - makeInboundSession( - roomId: "XYZ", - sessionId: "1" - ), - makeInboundSession( - roomId: "ABC", - sessionId: "2" - ), - makeInboundSession( - roomId: "XYZ", - sessionId: "3" - ), - ] - - shareKeys(roomId: "ABC") - - XCTAssertEqual(service.requests.count, 1) - XCTAssertEqual(service.requests.first?.sessionId, "2") - } -} - -extension MXSharedHistoryKeyRequest { - public override func isEqual(_ object: Any?) -> Bool { - guard let object = object as? MXSharedHistoryKeyRequest else { - return false - } - return object.userId == userId - && object.devices.map { $0.deviceId } == devices.map { $0.deviceId } - && object.roomId == roomId - && object.sessionId == sessionId - && object.senderKey == senderKey - } -} diff --git a/MatrixSDKTests/Crypto/MXCryptoV2FactoryUnitTests.swift b/MatrixSDKTests/Crypto/MXCryptoV2FactoryUnitTests.swift deleted file mode 100644 index fae681fca3..0000000000 --- a/MatrixSDKTests/Crypto/MXCryptoV2FactoryUnitTests.swift +++ /dev/null @@ -1,151 +0,0 @@ -// -// 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 -@testable import MatrixSDK - -class MXCryptoV2FactoryUnitTests: XCTestCase { - class MXSessionStub: MXSession { - var stubbedCredentials: MXCredentials! - override var credentials: MXCredentials! { - return stubbedCredentials - } - - override var myUserId: String! { - return stubbedCredentials.userId - } - - override var aggregations: MXAggregations! { - return MXAggregations() - } - - override var matrixRestClient: MXRestClient! { - return MXRestClientStub(credentials: credentials) - } - } - - var factory: MXCryptoV2Factory! - - override func setUp() async throws { - factory = MXCryptoV2Factory() - MXKeyProvider.sharedInstance().delegate = MXKeyProviderStub() - } - - override func tearDown() async throws { - try LegacyRealmStore.deleteAllStores() - MXKeyProvider.sharedInstance().delegate = nil - } - - private func makeSession(userId: String) -> MXSession { - let credentials = MXCredentials() - credentials.userId = userId - - let session = MXSessionStub() - session.stubbedCredentials = credentials - return session - } - - private func buildCrypto(account: LegacyRealmStore.Account) async throws -> (MXCrypto?, Bool) { - let session = MXSessionStub() - session.stubbedCredentials = account.credentials - - return try await withCheckedThrowingContinuation { cont in - var hasMigrated = false - factory.buildCrypto( - session: session) { _ in - hasMigrated = true - } success: { - cont.resume(returning: ($0, hasMigrated)) - } failure: { - cont.resume(throwing: $0) - } - } - } - - func test_hasCryptoData() throws { - let alice = "Alice" - let bob = "Bob" - - // Only create crypto data for alice - let aliceUrl = try MXCryptoMachineStore.storeURL(for: alice) - let data = "something".data(using: .ascii)! - try data.write(to: aliceUrl) - - let aliceSession = makeSession(userId: alice) - XCTAssertTrue(MXCryptoV2Factory.shared.hasCryptoData(for: aliceSession)) - - let bobSession = makeSession(userId: bob) - XCTAssertFalse(MXCryptoV2Factory.shared.hasCryptoData(for: bobSession)) - } - - func test_doesNotMigrateNewUser() async throws { - // Build crypto and assert no migration has been performed - let (crypto, hasMigrated) = try await buildCrypto(account: .version2) - XCTAssertNotNil(crypto) - XCTAssertFalse(hasMigrated) - } - - func test_fullyMigratesLegacyUser() async throws { - // Load the unmigrated legacy store - let account = LegacyRealmStore.Account.version2 - XCTAssertFalse(LegacyRealmStore.hasData(for: account)) - let legacyStore = try LegacyRealmStore.load(account: account) - XCTAssertTrue(LegacyRealmStore.hasData(for: account)) - XCTAssertEqual(legacyStore.cryptoVersion, .version2) - - // Build crypto and assert migration has been performed - let (crypto, hasMigrated) = try await buildCrypto(account: account) - XCTAssertNotNil(crypto) - XCTAssertTrue(hasMigrated) - - // Assert that data for the legacy store has been removed - XCTAssertFalse(LegacyRealmStore.hasData(for: account)) - } - - func test_migratesPartiallyMigratedUser() async throws { - // Load partially deprecated legacy store - let account = LegacyRealmStore.Account.deprecated1 - XCTAssertFalse(LegacyRealmStore.hasData(for: account)) - let legacyStore = try LegacyRealmStore.load(account: account) - XCTAssertTrue(LegacyRealmStore.hasData(for: account)) - XCTAssertEqual(legacyStore.cryptoVersion, .deprecated1) - - // Build crypto and assert migration has been performed - let (crypto, hasMigrated) = try await buildCrypto(account: account) - XCTAssertNotNil(crypto) - XCTAssertTrue(hasMigrated) - - // Assert that data for the legacy store has been removed - XCTAssertFalse(LegacyRealmStore.hasData(for: account)) - } - - func test_doesNotMigrateDeprecatedStore() async throws { - // Load fully deprecated legacy store - let account = LegacyRealmStore.Account.deprecated3 - XCTAssertFalse(LegacyRealmStore.hasData(for: account)) - let legacyStore = try LegacyRealmStore.load(account: account) - XCTAssertTrue(LegacyRealmStore.hasData(for: account)) - XCTAssertEqual(legacyStore.cryptoVersion, .deprecated3) - - // Build crypto and assert no migration has been performed - let (crypto, hasMigrated) = try await buildCrypto(account: .deprecated3) - XCTAssertNotNil(crypto) - XCTAssertFalse(hasMigrated) - - // Assert that data for the legacy store has been removed - XCTAssertFalse(LegacyRealmStore.hasData(for: account)) - } -} diff --git a/MatrixSDKTests/Crypto/MXLegacyCrypto+LegacyCrossSigning.swift b/MatrixSDKTests/Crypto/MXLegacyCrypto+LegacyCrossSigning.swift deleted file mode 100644 index 5bc7f1dc67..0000000000 --- a/MatrixSDKTests/Crypto/MXLegacyCrypto+LegacyCrossSigning.swift +++ /dev/null @@ -1,32 +0,0 @@ -// -// Copyright 2022 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 - -@objc extension MXLegacyCrypto { - - // MXLegacyCrossSigning exposes a number of internal methods and properties in `MXCrossSigning_Private` - // which are used in integration tests to perform actions and assert outcomes. - // These methods and properties will not be available in MXCrossSigningV2 and therefore given - // integration tests cannot be run (or have to be re-written). - var legacyCrossSigning: MXLegacyCrossSigning? { - guard let legacy = crossSigning as? MXLegacyCrossSigning else { - assertionFailure("Legacy cross signing is not available, adjust test to not depend on legacy APIs") - return nil - } - return legacy - } -} diff --git a/MatrixSDKTests/Crypto/MXOlmDeviceUnitTests.swift b/MatrixSDKTests/Crypto/MXOlmDeviceUnitTests.swift deleted file mode 100644 index 6ccabdc3aa..0000000000 --- a/MatrixSDKTests/Crypto/MXOlmDeviceUnitTests.swift +++ /dev/null @@ -1,151 +0,0 @@ -// -// Copyright 2022 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 -@testable import MatrixSDK - -class MXOlmDeviceUnitTests: XCTestCase { - - /// Stubbed olm session that overrides first known index - class MXOlmSessionStub: MXOlmInboundGroupSession { - class OlmSessionStub: OLMInboundGroupSession { - override func firstKnownIndex() -> UInt { - return UInt.max - } - } - - override var session: OLMInboundGroupSession! { - return OlmSessionStub() - } - } - - /// Crypto store spy used to assert on for the test outcome - class CryptoStoreSpy: MXRealmCryptoStore { - var session: MXOlmInboundGroupSession? - - override func inboundGroupSession(withId sessionId: String!, andSenderKey senderKey: String!) -> MXOlmInboundGroupSession! { - return session - } - - override func store(_ sessions: [MXOlmInboundGroupSession]!) { - session = sessions.first - } - } - - let senderKey = "ABC" - let roomId = "123" - var store: CryptoStoreSpy! - var device: MXOlmDevice! - override func setUp() { - super.setUp() - - MXSDKOptions.sharedInstance().enableRoomSharedHistoryOnInvite = true - store = CryptoStoreSpy() - device = MXOlmDevice(store: store) - } - - private func addInboundGroupSession( - sessionId: String, - sessionKey: String, - roomId: String, - sharedHistory: Bool - ) { - device.addInboundGroupSession( - sessionId, - sessionKey: sessionKey, - roomId: roomId, - senderKey: senderKey, - forwardingCurve25519KeyChain: [], - keysClaimed: [:], - exportFormat: false, - sharedHistory: sharedHistory, - untrusted: false - ) - } - - func test_addInboundGroupSession_storesSharedHistory() { - let session = device.createOutboundGroupSessionForRoom(withRoomId: roomId)! - - addInboundGroupSession( - sessionId: session.sessionId, - sessionKey: session.sessionKey, - roomId: roomId, - sharedHistory: true - ) - - XCTAssertNotNil(store.session) - XCTAssertTrue(store.session!.sharedHistory) - } - - func test_addInboundGroupSession_doesNotOverrideSharedHistory() { - let session = device.createOutboundGroupSessionForRoom(withRoomId: roomId)! - - // Add first inbound session that is not sharing history - addInboundGroupSession( - sessionId: session.sessionId, - sessionKey: session.sessionKey, - roomId: roomId, - sharedHistory: false - ) - - // Modify the now stored session so that it will be considered outdated - store.session = stubbedSession(for: store.session!) - - // Add second inbound session with the same ID which is sharing history - addInboundGroupSession( - sessionId: session.sessionId, - sessionKey: session.sessionKey, - roomId: roomId, - sharedHistory: true - ) - - // After the update the shared history should not be changed - XCTAssertNotNil(store.session) - XCTAssertFalse(store.session!.sharedHistory) - } - - func test_addMultipleInboundGroupSessions_doesNotOverrideSharedHistory() { - let session = device.createOutboundGroupSessionForRoom(withRoomId: roomId)! - - // Add first inbound session that is not sharing history - addInboundGroupSession( - sessionId: session.sessionId, - sessionKey: session.sessionKey, - roomId: roomId, - sharedHistory: false - ) - - // Modify the now stored session so that it will be considered outdated - store.session = stubbedSession(for: store.session!) - - // Add multiple sessions via exported data which are sharing history - let data = store.session!.exportData()! - data.sharedHistory = true - device.importInboundGroupSessions([data]) - - // After the update the shared history should not be changed - XCTAssertNotNil(store.session) - XCTAssertFalse(store.session!.sharedHistory) - } - - // MARK: - Helpers - - /// Create a stubbed version of olm session with custom index - private func stubbedSession(for session: MXOlmInboundGroupSession) -> MXOlmSessionStub { - let data = session.exportData()! - return MXOlmSessionStub(importedSessionData: data)! - } -} diff --git a/MatrixSDKTests/Crypto/MXSession+LegacyCrypto.swift b/MatrixSDKTests/Crypto/MXSession+LegacyCrypto.swift deleted file mode 100644 index c98d489229..0000000000 --- a/MatrixSDKTests/Crypto/MXSession+LegacyCrypto.swift +++ /dev/null @@ -1,35 +0,0 @@ -// -// Copyright 2022 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 - -@objc extension MXSession { - - // MXLegacyCrypto exposes a number of internal methods and properties in `MXCrypto_Private` - // which are used in integration tests to perform actions and assert outcomes. - // These methods and properties will not be available in MXCryptoV2 and therefore given - // integration tests cannot be run (or have to be re-written). - var legacyCrypto: MXLegacyCrypto? { - guard let crypto = crypto else { - return nil - } - - guard let legacy = crypto as? MXLegacyCrypto else { - return nil - } - return legacy - } -} diff --git a/MatrixSDKTests/Crypto/Migration/Data/MXCryptoMigrationStoreUnitTests.swift b/MatrixSDKTests/Crypto/Migration/Data/MXCryptoMigrationStoreUnitTests.swift deleted file mode 100644 index 462895baa5..0000000000 --- a/MatrixSDKTests/Crypto/Migration/Data/MXCryptoMigrationStoreUnitTests.swift +++ /dev/null @@ -1,284 +0,0 @@ -// -// 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 -import XCTest -import MatrixSDKCrypto -@testable import MatrixSDK - -class MXCryptoMigrationStoreUnitTests: XCTestCase { - - var pickleKey: Data! - var legacyStore: MXMemoryCryptoStore! - var store: MXCryptoMigrationStore! - - override func setUp() { - pickleKey = "1234".data(using: .ascii)! - - let credentials = MXCredentials() - credentials.userId = "Alice" - credentials.deviceId = "ABC" - - legacyStore = MXMemoryCryptoStore(credentials: credentials) - legacyStore.setAccount(OLMAccount(newAccount: ())) - - store = .init(legacyStore: legacyStore) - } - - // MARK: - Helpers - - func extractData(pickleKey: Data? = nil) throws -> MigrationData { - try store.extractData(with: pickleKey ?? self.pickleKey) - } - - func extractSessions(pickleKey: Data? = nil) throws -> [PickledSession] { - var sessions = [PickledSession]() - store.extractSessions(with: pickleKey ?? self.pickleKey, batchSize: .max) { batch, progress in - sessions += batch - } - return sessions - } - - func extractGroupSessions(pickleKey: Data? = nil) throws -> [PickledInboundGroupSession] { - var sessions = [PickledInboundGroupSession]() - store.extractGroupSessions(with: pickleKey ?? self.pickleKey, batchSize: .max) { batch, progress in - sessions += batch - } - return sessions - } - - @discardableResult - func storeGroupSession( - roomId: String = "ABC", - senderKey: String? = "Bob", - isUntrusted: Bool = false, - backedUp: Bool = false - ) -> MXOlmInboundGroupSession { - let device = MXOlmDevice(store: legacyStore)! - let outbound = device.createOutboundGroupSessionForRoom(withRoomId: roomId) - - let session = MXOlmInboundGroupSession(sessionKey: outbound!.sessionKey)! - session.senderKey = senderKey - session.roomId = roomId - session.keysClaimed = ["A": "1"] - session.isUntrusted = isUntrusted - legacyStore.store([session]) - - if backedUp { - legacyStore.markBackupDone(for: [session]) - } - return session - } - - func storeSecret(_ secret: String, secretId: Unmanaged) { - legacyStore.storeSecret(secret, withSecretId: secretId.takeUnretainedValue() as String) - } - - // MARK: - Tests - - func test_credentials() { - XCTAssertEqual(store.userId, "Alice") - XCTAssertEqual(store.deviceId, "ABC") - } - - func test_trustedSettings() { - legacyStore.globalBlacklistUnverifiedDevices = false - XCTAssertFalse(store.globalSettings.onlyAllowTrustedDevices) - - legacyStore.globalBlacklistUnverifiedDevices = true - XCTAssertTrue(store.globalSettings.onlyAllowTrustedDevices) - } - - func test_missingAccountFailsExtraction() { - legacyStore.setAccount(nil) - do { - _ = try extractData() - XCTFail("Should not succeed") - } catch MXCryptoMigrationStore.Error.missingAccount { - XCTAssert(true) - } catch { - XCTFail("Unknown error") - } - } - - func test_extractsAccount() throws { - let legacyPickle = try legacyStore.account().serializeData(withKey: pickleKey) - - let account = try extractData().account - - XCTAssertEqual(account.userId, "Alice") - XCTAssertEqual(account.deviceId, "ABC") - XCTAssertEqual(account.pickle, legacyPickle) - XCTAssertTrue(account.shared) - XCTAssertEqual(account.uploadedSignedKeyCount, 50) - } - - func test_extractsSession() throws { - let session = MXOlmSession(olmSession: OLMSession(), deviceKey: "XYZ") - session.lastReceivedMessageTs = 123 - legacyStore.store(session) - let pickle = try session.session.serializeData(withKey: pickleKey) - - // There are no sessions in the general migration data - XCTAssertTrue(try extractData().sessions.isEmpty) - - // But they can be accumulated by batching - let sessions = try extractSessions() - XCTAssertEqual(sessions.count, 1) - XCTAssertEqual(sessions[0].pickle, pickle) - XCTAssertEqual(sessions[0].senderKey, "XYZ") - XCTAssertFalse(sessions[0].createdUsingFallbackKey) - XCTAssertEqual(sessions[0].creationTime, 123) - XCTAssertEqual(sessions[0].lastUseTime, 123) - } - - func test_extractsMultipleSessionsInBatches() throws { - for i in 0 ..< 12 { - legacyStore.store(MXOlmSession(olmSession: OLMSession(), deviceKey: "\(i)")) - } - - // There are no sessions in the general migration data - XCTAssertTrue(try extractData().sessions.isEmpty) - // But they can be accumulated by batching - let sessions = try extractSessions() - XCTAssertEqual(sessions.count, 12) - } - - func test_extractsGroupSession() throws { - let session = storeGroupSession(roomId: "abcd") - let pickle = try session.session.serializeData(withKey: pickleKey) - - // There are no sessions in the general migration data - XCTAssertTrue(try extractData().inboundGroupSessions.isEmpty) - - // But they can be accumulated by batching - let sessions = try extractGroupSessions() - XCTAssertEqual(sessions.count, 1) - XCTAssertEqual(sessions[0].pickle, pickle) - XCTAssertEqual(sessions[0].senderKey, "Bob") - XCTAssertEqual(sessions[0].signingKey, ["A": "1"]) - XCTAssertEqual(sessions[0].roomId, "abcd") - XCTAssertEqual(sessions[0].forwardingChains, []) - } - - func test_extractsOnlyValidGroupSessions() throws { - for i in 0 ..< 4 { - let isValid = i % 2 == 0 - storeGroupSession(senderKey: isValid ? "Bob" : nil) - } - - // There are no sessions in the general migration data - XCTAssertTrue(try extractData().inboundGroupSessions.isEmpty) - - // But they can be accumulated by batching - let sessions = try extractGroupSessions() - XCTAssertEqual(sessions.count, 2) - } - - func test_extractsImportedGroupSessionStatus() throws { - storeGroupSession(isUntrusted: true) - storeGroupSession(isUntrusted: false) - storeGroupSession(isUntrusted: false) - - let sessions = try extractGroupSessions() - - XCTAssertEqual(sessions.count, 3) - XCTAssertTrue(sessions[0].imported) - XCTAssertFalse(sessions[1].imported) - XCTAssertFalse(sessions[1].imported) - } - - func test_extractsBackedUpGroupSessionStatus() throws { - storeGroupSession(backedUp: false) - storeGroupSession(backedUp: true) - storeGroupSession(backedUp: false) - - let sessions = try extractGroupSessions() - - XCTAssertEqual(sessions.count, 3) - XCTAssertFalse(sessions[0].backedUp) - XCTAssertTrue(sessions[1].backedUp) - XCTAssertFalse(sessions[2].backedUp) - } - - func test_extractsBackupVersion() throws { - legacyStore.backupVersion = "5" - let version = try extractData().backupVersion - XCTAssertEqual(version, "5") - } - - func test_extractsBackupRecoveryKey() throws { - let privateKey = "ABCD" - storeSecret(privateKey, secretId: MXSecretId.keyBackup) - - let key = try extractData().backupRecoveryKey - - let recovery = MXRecoveryKey.encode(MXBase64Tools.data(fromBase64: privateKey)) - XCTAssertNotNil(key) - XCTAssertNotNil(recovery) - XCTAssertEqual(key, recovery) - } - - func test_extractsPickeKey() throws { - let pickleKey = "some key".data(using: .ascii)! - let key = try extractData(pickleKey: pickleKey).pickleKey - XCTAssertEqual(key, pickleKey) - } - - func test_extractsCrossSigning() throws { - storeSecret("MASTER", secretId: MXSecretId.crossSigningMaster) - storeSecret("USER", secretId: MXSecretId.crossSigningUserSigning) - storeSecret("SELF", secretId: MXSecretId.crossSigningSelfSigning) - - let crossSigning = try extractData().crossSigning - - XCTAssertEqual(crossSigning.masterKey, "MASTER") - XCTAssertEqual(crossSigning.userSigningKey, "USER") - XCTAssertEqual(crossSigning.selfSigningKey, "SELF") - } - - func test_extractsOnlyTrackedUsers() throws { - let users = [ - "Alice": MXDeviceTrackingStatusNotTracked, - "Bob": MXDeviceTrackingStatusPendingDownload, - "Carol": MXDeviceTrackingStatusDownloadInProgress, - "Dave": MXDeviceTrackingStatusUpToDate, - ].mapValues { NSNumber(value: $0.rawValue) } - legacyStore.storeDeviceTrackingStatus(users) - - let trackedUsers = try extractData().trackedUsers - - XCTAssertEqual(Set(trackedUsers), ["Bob", "Carol", "Dave"]) - } - - func test_extractsRoomSettings() throws { - legacyStore.storeAlgorithm(forRoom: "room1", algorithm: kMXCryptoOlmAlgorithm) - legacyStore.storeBlacklistUnverifiedDevices(inRoom: "room1", blacklist: true) - - legacyStore.storeAlgorithm(forRoom: "room2", algorithm: kMXCryptoMegolmAlgorithm) - legacyStore.storeBlacklistUnverifiedDevices(inRoom: "room2", blacklist: false) - - legacyStore.storeAlgorithm(forRoom: "room3", algorithm: "something invalid") - legacyStore.storeBlacklistUnverifiedDevices(inRoom: "room3", blacklist: true) - - let settings = try extractData().roomSettings - - XCTAssertEqual(settings, [ - "room1": RoomSettings(algorithm: .olmV1Curve25519AesSha2, onlyAllowTrustedDevices: true), - "room2": RoomSettings(algorithm: .megolmV1AesSha2, onlyAllowTrustedDevices: false), - ]) - } -} diff --git a/MatrixSDKTests/Crypto/Migration/LegacyRealmStore/LegacyRealmStore.swift b/MatrixSDKTests/Crypto/Migration/LegacyRealmStore/LegacyRealmStore.swift deleted file mode 100644 index 4510c3a782..0000000000 --- a/MatrixSDKTests/Crypto/Migration/LegacyRealmStore/LegacyRealmStore.swift +++ /dev/null @@ -1,162 +0,0 @@ -// -// 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 -import Realm - -/// Class simulating legacy crypto store associated with the now-deprecated native crypto module -/// -/// It has access to several pre-made realm files with legacy accounts that can be loaded -/// and migrated to current crypto module -class LegacyRealmStore { - enum Error: Swift.Error { - case missingDependencies - } - - /// A few pre-created accounts with hardcoded details - enum Account { - - /// Realm store with crypto version `version2`, used for migration testing - case version2 - - /// Realm store with crypto version `deprecated1`, used for migration testing - case deprecated1 - - /// Realm store with crypto version `deprecated3`, used for migration testing - case deprecated3 - - /// Realm store with a verified accounts used to test cross-signing migration - case verified - - /// Realm store with an unverified accounts used to test cross-signing migration - case unverified - - /// File name for the associated account file - var fileName: String { - switch self { - case .version2: - return "legacy_version2_account" - case .deprecated1: - return "legacy_deprecated1_account" - case .deprecated3: - return "legacy_deprecated3_account" - case .verified: - return "legacy_verified_account" - case .unverified: - return "legacy_unverified_account" - } - } - - /// Hardcoded room id matching a given account - var roomId: String? { - switch self { - case .version2: - return nil - case .deprecated1: - return nil - case .deprecated3: - return nil - case .verified: - return "!QUWVMCIhJqIqTMLxof:x.y.z" - case .unverified: - return nil - } - } - - /// Hardcoded account credentials matching a given account - var credentials: MXCredentials { - let cred = MXCredentials() - switch self { - case .version2: - cred.userId = "@mxalice-54aeab93-b4b2-4edf-85e1-bc0dbbb710ee:x.y.z" - cred.deviceId = "NAHEYWCBBM" - case .deprecated1: - cred.userId = "@mxalice-d9eed33b-e269-4171-9352-8ee8b84b37a1:x.y.z" - cred.deviceId = "KLWNEPIHMX" - case .deprecated3: - cred.userId = "@mxalice-4c5a01ea-9fac-4568-bda6-09e2d14f0e5d:x.y.z" - cred.deviceId = "DCGBYVZFQI" - case .verified: - cred.userId = "@mxalice-107ca1c5-4d03-4ff4-affc-369f4ce6de6f:x.y.z" - cred.deviceId = "AXDAYKSETI" - case .unverified: - cred.userId = "@mxalice-f5314669-7d43-4662-8262-771728e1921f:x.y.z" - cred.deviceId = "ELSGFERWHH" - } - return cred - } - } - - static func load(account: Account) throws -> MXRealmCryptoStore { - guard - let sourceUrl = Bundle(for: Self.self).url(forResource: account.fileName, withExtension: "realm"), - let folder = realmFolder() - else { - throw Error.missingDependencies - } - - if !FileManager.default.fileExists(atPath: folder.path) { - try FileManager.default.createDirectory(at: folder, withIntermediateDirectories: true) - } - - let credentials = account.credentials - let file = "\(credentials.userId!)-\(credentials.deviceId!).realm" - - let targetUrl = folder.appendingPathComponent(file) - if FileManager.default.fileExists(atPath: targetUrl.path) { - try FileManager.default.removeItem(at: targetUrl) - } - - try FileManager.default.copyItem(at: sourceUrl, to: targetUrl) - return MXRealmCryptoStore(credentials: credentials) - } - - static func hasData(for account: Account) -> Bool { - let credentials = account.credentials - let file = "\(credentials.userId!)-\(credentials.deviceId!).realm" - - return MXRealmCryptoStore.hasData(for: credentials) - } - - static func deleteAllStores() throws { - guard let folder = realmFolder() else { - throw Error.missingDependencies - } - - if FileManager.default.fileExists(atPath: folder.path) { - try FileManager.default.removeItem(at: folder) - } - } - - private static func realmFolder() -> URL? { - platformDirectoryURL()? - .appendingPathComponent("MXRealmCryptoStore") - } - - private static func platformDirectoryURL() -> URL? { - #if os(OSX) - guard - let applicationSupport = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask).first, - let identifier = Bundle.main.bundleIdentifier - else { - return nil - } - return applicationSupport.appendingPathComponent(identifier) - #else - return FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first - #endif - } -} diff --git a/MatrixSDKTests/Crypto/Migration/MXCryptoMigrationV2UnitTests.swift b/MatrixSDKTests/Crypto/Migration/MXCryptoMigrationV2UnitTests.swift deleted file mode 100644 index 88b5cfc676..0000000000 --- a/MatrixSDKTests/Crypto/Migration/MXCryptoMigrationV2UnitTests.swift +++ /dev/null @@ -1,167 +0,0 @@ -// -// 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 -import MatrixSDKCrypto -@testable import MatrixSDK - -class MXCryptoMigrationV2UnitTests: XCTestCase { - enum Error: Swift.Error { - case missingEvent - } - - override func tearDown() async throws { - try LegacyRealmStore.deleteAllStores() - } - - // MARK: - Helpers - - private func fullyMigratedOlmMachine(legacyStore: MXCryptoStore) throws -> MXCryptoMachine { - MXKeyProvider.sharedInstance().delegate = MXKeyProviderStub() - let migration = MXCryptoMigrationV2(legacyStore: legacyStore) - try migration.migrateAllData { _ in } - let machine = try MXCryptoMachine( - userId: legacyStore.userId(), - deviceId: legacyStore.deviceId(), - restClient: MXRestClientStub(), - getRoomAction: { _ in - return nil - }) - MXKeyProvider.sharedInstance().delegate = nil - return machine - } - - private func partiallyMigratedOlmMachine(legacyStore: MXCryptoStore) throws -> MXCryptoMachine { - MXKeyProvider.sharedInstance().delegate = MXKeyProviderStub() - let migration = MXCryptoMigrationV2(legacyStore: legacyStore) - try migration.migrateRoomAndGlobalSettingsOnly { _ in } - let machine = try MXCryptoMachine( - userId: legacyStore.userId(), - deviceId: legacyStore.deviceId(), - restClient: MXRestClientStub(), - getRoomAction: { _ in - return nil - }) - MXKeyProvider.sharedInstance().delegate = nil - return machine - } - - private func loadEncryptedEvent() throws -> MXEvent { - guard let url = Bundle(for: Self.self).url(forResource: "archived_encrypted_event", withExtension: nil) else { - throw Error.missingEvent - } - - let data = try Data(contentsOf: url) - guard let event = NSKeyedUnarchiver.unarchiveObject(with: data) as? MXEvent else { - throw Error.missingEvent - } - return event - } - - // MARK: - Tests - - func test_migratesAccountDetails() throws { - let store = try LegacyRealmStore.load(account: .verified) - let machine = try fullyMigratedOlmMachine(legacyStore: store) - - XCTAssertEqual(machine.userId, store.userId()) - XCTAssertEqual(machine.deviceId, store.deviceId()) - XCTAssertNotNil(machine.deviceCurve25519Key) - XCTAssertEqual(machine.deviceCurve25519Key, store.account().identityKeys()[kMXKeyCurve25519Type] as? String) - XCTAssertNotNil(machine.deviceEd25519Key) - XCTAssertEqual(machine.deviceEd25519Key, store.account().identityKeys()[kMXKeyEd25519Type] as? String) - } - - func test_canDecryptMegolmMessageAfterMigration() throws { - // Load a previously archived and encrypted event - let event = try loadEncryptedEvent() - XCTAssertTrue(event.isEncrypted) - XCTAssertEqual(event.content["algorithm"] as? String, kMXCryptoMegolmAlgorithm) - XCTAssertNotNil(event.content["ciphertext"]) - - // Migrate data to crypto v2 - let store = try LegacyRealmStore.load(account: .verified) - let machine = try fullyMigratedOlmMachine(legacyStore: store) - - // Decrypt the event using crypto v2 - let decrypted = try machine.decryptRoomEvent(event) - let result = try MXEventDecryptionResult(event: decrypted) - let content = result.clearEvent["content"] as? [String: Any] - - // At this point we should be able to read back the original message after - // having decrypted the event with room keys migrated earlier - XCTAssertEqual(content?["body"] as? String, "Hi bob") - } - - func test_notCrossSignedAfterMigration() throws { - let store = try LegacyRealmStore.load(account: .unverified) - let machine = try fullyMigratedOlmMachine(legacyStore: store) - - let crossSigningV2 = MXCrossSigningV2(crossSigning: machine, restClient: MXRestClientStub()) - XCTAssertFalse(crossSigningV2.canCrossSign) - XCTAssertFalse(crossSigningV2.hasAllPrivateKeys) - } - - func test_migratesCrossSigningStatus() throws { - let store = try LegacyRealmStore.load(account: .verified) - let machine = try fullyMigratedOlmMachine(legacyStore: store) - - let crossSigningV2 = MXCrossSigningV2(crossSigning: machine, restClient: MXRestClientStub()) - XCTAssertTrue(crossSigningV2.hasAllPrivateKeys) - } - - func test_migratesRoomSettings() throws { - let store = try LegacyRealmStore.load(account: .verified) - let machine = try fullyMigratedOlmMachine(legacyStore: store) - - let settings = machine.roomSettings(roomId: LegacyRealmStore.Account.verified.roomId!) - XCTAssertEqual(settings, .init(algorithm: .megolmV1AesSha2, onlyAllowTrustedDevices: true)) - } - - func test_migratesRoomSettingsInPartialMigration() throws { - let store = try LegacyRealmStore.load(account: .verified) - let machine = try partiallyMigratedOlmMachine(legacyStore: store) - - let settings = machine.roomSettings(roomId: LegacyRealmStore.Account.verified.roomId!) - XCTAssertEqual(settings, .init(algorithm: .megolmV1AesSha2, onlyAllowTrustedDevices: true)) - } - - func test_migratesGlobalSettings() throws { - let store1 = try LegacyRealmStore.load(account: .unverified) - let machine1 = try fullyMigratedOlmMachine(legacyStore: store1) - XCTAssertTrue(machine1.onlyAllowTrustedDevices) - - let store2 = try LegacyRealmStore.load(account: .verified) - let machine2 = try fullyMigratedOlmMachine(legacyStore: store2) - XCTAssertFalse(machine2.onlyAllowTrustedDevices) - } - - func test_migratesGlobalSettingsInPartialMigration() throws { - let store1 = try LegacyRealmStore.load(account: .unverified) - let machine1 = try partiallyMigratedOlmMachine(legacyStore: store1) - XCTAssertTrue(machine1.onlyAllowTrustedDevices) - - let store2 = try LegacyRealmStore.load(account: .verified) - let machine2 = try partiallyMigratedOlmMachine(legacyStore: store2) - XCTAssertFalse(machine2.onlyAllowTrustedDevices) - } -} - -extension MXCryptoMigrationV2UnitTests: Logger { - func log(logLine: String) { - MXLog.debug("[MXCryptoMigrationV2Tests]: \(logLine)") - } -} diff --git a/MatrixSDKTests/Crypto/RoomKeys/MXRoomKeyInfoFactoryUnitTests.swift b/MatrixSDKTests/Crypto/RoomKeys/MXRoomKeyInfoFactoryUnitTests.swift deleted file mode 100644 index 609374da01..0000000000 --- a/MatrixSDKTests/Crypto/RoomKeys/MXRoomKeyInfoFactoryUnitTests.swift +++ /dev/null @@ -1,184 +0,0 @@ -// -// Copyright 2022 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 -import XCTest -@testable import MatrixSDK - -class MXRoomKeyInfoFactoryUnitTests: XCTestCase { - - var store: MXMemoryCryptoStore! - var factory: MXRoomKeyInfoFactory! - override func setUp() { - store = MXMemoryCryptoStore(credentials: MXCredentials(homeServer: "", userId: "Alice", accessToken: nil)) - factory = MXRoomKeyInfoFactory(myUserId: "Alice", store: store) - storeOutgoingKeyRequest(requestId: "1") - } - - // MARK: - Any event - - func test_roomKeyInfo_isNilForInvalidEvent() { - let info = factory.roomKey(for: MXEvent(fromJSON: [:])) - XCTAssertNil(info) - } - - // MARK: - Room key event - - func test_roomKeyInfo_createFromRoomKeyEvent() { - let event = MXEvent.roomKeyFixture() - - let key = factory.roomKey(for: event) - - let info = key?.info - XCTAssertNotNil(key) - XCTAssertEqual(key?.type, .safe) - XCTAssertEqual(info?.algorithm, "megolm") - XCTAssertEqual(info?.sessionId, "session1") - XCTAssertEqual(info?.sessionKey, "") - XCTAssertEqual(info?.roomId, "!123:matrix.org") - XCTAssertEqual(info?.senderKey, "") - XCTAssertNil(info?.forwardingKeyChain) - XCTAssertEqual(info?.keysClaimed as? NSDictionary, ["ed25519": ""]) - XCTAssertEqual(info?.exportFormat, false) - XCTAssertEqual(info?.sharedHistory, false) - } - - // MARK: - Forwarded room key event - - func test_roomKeyInfo_isUnrequestedIfKeyNotRequested() { - store.deleteOutgoingRoomKeyRequest(withRequestId: "1") - storeDevice(userId: "Alice", trusted: true, identityKey: "AliceSender") - let event = MXEvent.forwardedRoomKeyFixture( - senderKey: "AliceSender" - ) - - let key = factory.roomKey(for: event) - - let info = key?.info - XCTAssertNotNil(key) - XCTAssertEqual(key?.type, .unrequested) - XCTAssertEqual(info?.algorithm, "megolm") - XCTAssertEqual(info?.sessionId, "session1") - XCTAssertEqual(info?.sessionKey, "") - XCTAssertEqual(info?.roomId, "!123:matrix.org") - XCTAssertEqual(info?.senderKey, "") - XCTAssertEqual(info?.forwardingKeyChain, ["AliceSender"]) - XCTAssertEqual(info?.keysClaimed as? NSDictionary, ["ed25519": ""]) - XCTAssertEqual(info?.exportFormat, true) - XCTAssertEqual(info?.sharedHistory, false) - } - - func test_roomKeyInfo_isUnsafeIfNotFromMyself() { - storeDevice(userId: "Bob", trusted: true, identityKey: "AliceSender") - let event = MXEvent.forwardedRoomKeyFixture( - senderKey: "AliceSender" - ) - - let key = factory.roomKey(for: event) - - let info = key?.info - XCTAssertNotNil(key) - XCTAssertEqual(key?.type, .unsafe) - XCTAssertEqual(info?.algorithm, "megolm") - XCTAssertEqual(info?.sessionId, "session1") - XCTAssertEqual(info?.sessionKey, "") - XCTAssertEqual(info?.roomId, "!123:matrix.org") - XCTAssertEqual(info?.senderKey, "") - XCTAssertEqual(info?.forwardingKeyChain, ["AliceSender"]) - XCTAssertEqual(info?.keysClaimed as? NSDictionary, ["ed25519": ""]) - XCTAssertEqual(info?.exportFormat, true) - XCTAssertEqual(info?.sharedHistory, false) - } - - func test_roomKeyInfo_isUnsafeIfFromUnverifiedDevice() { - storeDevice(userId: "Alice", trusted: false, identityKey: "AliceSender") - let event = MXEvent.forwardedRoomKeyFixture( - senderKey: "AliceSender" - ) - - let key = factory.roomKey(for: event) - - let info = key?.info - XCTAssertNotNil(key) - XCTAssertEqual(key?.type, .unsafe) - XCTAssertEqual(info?.algorithm, "megolm") - XCTAssertEqual(info?.sessionId, "session1") - XCTAssertEqual(info?.sessionKey, "") - XCTAssertEqual(info?.roomId, "!123:matrix.org") - XCTAssertEqual(info?.senderKey, "") - XCTAssertEqual(info?.forwardingKeyChain, ["AliceSender"]) - XCTAssertEqual(info?.keysClaimed as? NSDictionary, ["ed25519": ""]) - XCTAssertEqual(info?.exportFormat, true) - XCTAssertEqual(info?.sharedHistory, false) - } - - func test_roomKeyInfo_createFromForwardedRoomKeyEvent() { - storeDevice(userId: "Alice", trusted: true, identityKey: "AliceSender") - let event = MXEvent.forwardedRoomKeyFixture( - senderKey: "AliceSender" - ) - - let key = factory.roomKey(for: event) - - let info = key?.info - XCTAssertNotNil(key) - XCTAssertEqual(key?.type, .safe) - XCTAssertEqual(info?.algorithm, "megolm") - XCTAssertEqual(info?.sessionId, "session1") - XCTAssertEqual(info?.sessionKey, "") - XCTAssertEqual(info?.roomId, "!123:matrix.org") - XCTAssertEqual(info?.senderKey, "") - XCTAssertEqual(info?.forwardingKeyChain, ["AliceSender"]) - XCTAssertEqual(info?.keysClaimed as? NSDictionary, ["ed25519": ""]) - XCTAssertEqual(info?.exportFormat, true) - XCTAssertEqual(info?.sharedHistory, false) - } - - // MARK: - Helpers - - func storeOutgoingKeyRequest( - requestId: String = "1", - algorithm: String = "megolm", - roomId: String = "!123:matrix.org", - sessionId: String = "session1", - senderKey: String = "" - ) { - let request = MXOutgoingRoomKeyRequest() - request.requestId = requestId - - request.requestBody = [ - "room_id": roomId, - "algorithm": algorithm, - "sender_key": senderKey, - "session_id": sessionId - ] - store.store(request) - } - - func storeDevice(userId: String, trusted: Bool, identityKey: String) { - let trust = MXDeviceTrustLevel( - localVerificationStatus: trusted ? .verified : .unverified, - crossSigningVerified: false - ) - let device = MXDeviceInfo(fromJSON: [ - "user_id": userId, - "device_id": "ABC", - "keys": ["curve25519:ABC": identityKey] - ])! - device.setValue(trust, forKey: "trustLevel") - store.storeDevice(forUser: userId, device: device) - } -} diff --git a/MatrixSDKTests/MXAes256KeyBackupTests.m b/MatrixSDKTests/MXAes256KeyBackupTests.m index 9ca670639a..64e9e8700a 100644 --- a/MatrixSDKTests/MXAes256KeyBackupTests.m +++ b/MatrixSDKTests/MXAes256KeyBackupTests.m @@ -17,7 +17,6 @@ #import "MXBaseKeyBackupTests.h" #import "MXAes256BackupAuthData.h" -#import "MXAes256KeyBackupAlgorithm.h" // Do not bother with retain cycles warnings in tests #pragma clang diagnostic push @@ -44,11 +43,6 @@ - (NSString *)algorithm return kMXCryptoAes256KeyBackupAlgorithm; } -- (BOOL)isUntrusted -{ - return MXAes256KeyBackupAlgorithm.isUntrusted; -} - - (MXKeyBackupVersion*)fakeKeyBackupVersion { return [MXKeyBackupVersion modelFromJSON:@{ diff --git a/MatrixSDKTests/MXAggregatedEditsTests.m b/MatrixSDKTests/MXAggregatedEditsTests.m index 277d0d3a95..c59a74e35b 100644 --- a/MatrixSDKTests/MXAggregatedEditsTests.m +++ b/MatrixSDKTests/MXAggregatedEditsTests.m @@ -696,88 +696,6 @@ - (void)testEditServerSideInE2ERoom }]; } -// - Send a message in an e2e room -// - Edit its local echo -// -> We must get notified about the replace event -// -> The local echo block must have been called twice -- (void)testEditOfEventBeingSentInE2ERoom -{ - // - Send a message in an e2e room - [matrixSDKTestsE2EData doE2ETestWithAliceAndBobInARoom:self cryptedBob:YES warnOnUnknowDevices:NO aliceStore:[[MXMemoryStore alloc] init] bobStore:[[MXMemoryStore alloc] init] readyToTest:^(MXSession *mxSession, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation) { - - MXRoom *room = [mxSession roomWithRoomId:roomId]; - - __block NSString *eventId; - __block NSUInteger localEchoBlockCount = 0; - - // - Send a message - MXEvent *localEcho; - [room sendTextMessage:kOriginalMessageText formattedText:nil threadId:nil localEcho:&localEcho success:^(NSString *theEventId) { - eventId = theEventId; - } failure:^(NSError *error) { - XCTFail(@"Cannot set up intial test conditions - error: %@", error); - [expectation fulfill]; - }]; - - // - Edit its local echo - [mxSession.aggregations replaceTextMessageEvent:localEcho withTextMessage:kEditedMessageText formattedText:nil localEchoBlock:^(MXEvent * _Nonnull localEcho) { - - localEchoBlockCount++; - - XCTAssertEqual(localEcho.eventType, MXEventTypeRoomMessage); - - XCTAssertNotNil(localEcho.relatesTo); - XCTAssertEqualObjects(localEcho.relatesTo.relationType, MXEventRelationTypeReplace); - - XCTAssertEqualObjects(localEcho.content[kMXMessageContentKeyNewContent][kMXMessageTypeKey], kMXMessageTypeText); - XCTAssertEqualObjects(localEcho.content[kMXMessageContentKeyNewContent][kMXMessageBodyKey], kEditedMessageText); - - switch (localEchoBlockCount) { - case 1: - // The first local echo must point to a local echo - XCTAssertEqual(localEcho.sentState, MXEventSentStateEncrypting); - XCTAssertTrue([localEcho.relatesTo.eventId hasPrefix:kMXEventLocalEventIdPrefix]); - break; - case 2: - // The second local echo must point to the final event id - XCTAssertEqual(localEcho.sentState, MXEventSentStateEncrypting); - XCTAssertFalse([localEcho.relatesTo.eventId hasPrefix:kMXEventLocalEventIdPrefix]); - break; - - default: - break; - } - - } success:^(NSString * _Nonnull eventId) { - XCTAssertNotNil(eventId); - } failure:^(NSError *error) { - XCTFail(@"The operation should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - - // -> We must get notified about the replace event - [mxSession.aggregations listenToEditsUpdateInRoom:room.roomId block:^(MXEvent * _Nonnull replaceEvent) { - - XCTAssertNotNil(eventId, @"The original event must have been sent before receiving the final edit"); - - MXEvent *editedEvent = [mxSession.store eventWithEventId:eventId inRoom:room.roomId]; - - XCTAssertNotNil(editedEvent); - XCTAssertTrue(editedEvent.contentHasBeenEdited); - XCTAssertEqualObjects(editedEvent.unsignedData.relations.replace.eventId, replaceEvent.eventId); - XCTAssertEqualObjects(editedEvent.content[kMXMessageBodyKey], kEditedMessageText); - - XCTAssertEqualObjects(replaceEvent.relatesTo.eventId, eventId); - - // -> The local echo block must have been called twice - XCTAssertEqual(localEchoBlockCount, 2); - - [expectation fulfill]; - }]; - }]; -} - - #pragma mark - Edits history // Edit a message a number of times diff --git a/MatrixSDKTests/MXBackgroundSyncServiceTests.swift b/MatrixSDKTests/MXBackgroundSyncServiceTests.swift index 736de954ab..d2345a3cd0 100644 --- a/MatrixSDKTests/MXBackgroundSyncServiceTests.swift +++ b/MatrixSDKTests/MXBackgroundSyncServiceTests.swift @@ -227,123 +227,6 @@ class MXBackgroundSyncServiceTests: XCTestCase { } - // Nonimal test with encryption: Get and decrypt an event from the background service - // - Alice and Bob are in an encrypted room - // - Bob stops their app - // - Alice relogins with a new device and creates new megolm keys - // - Alice sends a message - // - Bob uses the MXBackgroundSyncService to fetch it - // -> The message can be read and decypted from MXBackgroundSyncService - // -> Keys are stored in the intermediate MXBackgroundSyncService crypto store, not in the main crypto store - // - Bob restarts their MXSession - // -> The message is available from MXSession and no more from MXBackgroundSyncService - // -> Keys are stored in the main crypto store but no more in the intermediate MXBackgroundSyncService crypto store - func testWithEncryptedEventRollingKeys() { - - let aliceStore = MXFileStore() - let bobStore = MXFileStore() - let warnOnUnknownDevices = false - e2eTestData.doE2ETestWithAliceAndBob(inARoom: self, cryptedBob: true, warnOnUnknowDevices: warnOnUnknownDevices, aliceStore: aliceStore, bobStore: bobStore) { (aliceSession, bobSession, roomId, expectation) in - - guard let roomId = roomId else { - XCTFail("Cannot set up initial test conditions - error: room cannot be retrieved") - expectation?.fulfill() - return - } - - guard let aliceRestClient = aliceSession?.matrixRestClient, let bobCredentials = bobSession?.credentials else { - XCTFail("Cannot set up initial test conditions - error: Bob's credentials cannot be retrieved") - expectation?.fulfill() - return - } - bobSession?.close() - - aliceSession?.close() - self.e2eTestData.loginUser(onANewDevice: self, credentials: aliceRestClient.credentials, withPassword: MXTESTS_ALICE_PWD) { newAliceSession in - - guard let room = newAliceSession?.room(withRoomId: roomId) else { - XCTFail("Cannot set up initial test conditions - error: room cannot be retrieved") - expectation?.fulfill() - return - } - - newAliceSession?.legacyCrypto?.warnOnUnknowDevices = warnOnUnknownDevices - - // - Alice sends a message - var localEcho: MXEvent? - room.sendTextMessage(Constants.messageText, localEcho: &localEcho) { (response) in - switch response { - case .success(let eventId): - - guard let eventId = eventId else { - XCTFail("Cannot set up initial test conditions - error: room cannot be retrieved") - expectation?.fulfill() - return - } - - // - Bob uses the MXBackgroundSyncService to fetch it - self.bgSyncService = MXBackgroundSyncService(withCredentials: bobCredentials) - - self.bgSyncService?.event(withEventId: eventId, inRoom: roomId) { (response) in - switch response { - case .success(let event): - // -> The message can be read and decypted from MXBackgroundSyncService - XCTAssertTrue(event.isEncrypted, "Event should be encrypted") - XCTAssertNotNil(event.clear, "Event should be decrypted successfully") - - let text = event.content["body"] as? String - XCTAssertEqual(text, Constants.messageText, "Event content should match") - - XCTAssertNil(bobStore.event(withEventId: eventId, inRoom: roomId), "Event should not be in session store yet") - - let syncResponseStore = MXSyncResponseFileStore(withCredentials: bobCredentials) - let syncResponseStoreManager = MXSyncResponseStoreManager(syncResponseStore: syncResponseStore) - XCTAssertNotNil(syncResponseStoreManager.event(withEventId: eventId, inRoom: roomId), "Event should be stored in sync response store") - - // -> Keys are stored in the intermediate MXBackgroundSyncService crypto store, not in the main crypto store - let cryptoStore = MXRealmCryptoStore(credentials: bobCredentials) - let bgSyncServiceCryptoStore = MXRealmCryptoStore(credentials: self.credentialForBgCryptoStore(withCredentials: bobCredentials)) - let sessionId = event.wireContent["session_id"] as! String - let senderKey = event.wireContent["sender_key"] as! String - XCTAssertNil(cryptoStore?.inboundGroupSession(withId: sessionId, andSenderKey: senderKey), "Key must not be yet stored in the main MXRealmCryptoStore") - XCTAssertNotNil(bgSyncServiceCryptoStore?.inboundGroupSession(withId: sessionId, andSenderKey: senderKey), "Key must be stored in the intermediate MXBackgroundService MXRealmCryptoStore") - - // - Bob restarts their MXSession - let newBobSession = MXSession(matrixRestClient: MXRestClient(credentials: bobCredentials, unrecognizedCertificateHandler: nil)) - self.testData.retain(newBobSession) - newBobSession?.setStore(bobStore, completion: { (_) in - newBobSession?.start(withSyncFilterId: bobStore.syncFilterId, completion: { (_) in - // -> The message is available from MXSession and no more from MXBackgroundSyncService - XCTAssertNil(syncResponseStoreManager.event(withEventId: eventId, inRoom: roomId), "Event should not be stored in sync response store anymore") - XCTAssertNotNil(bobStore.event(withEventId: eventId, inRoom: roomId), "Event should be in session store anymore") - - // -> Keys are stored in the main crypto store but no more in the intermediate MXBackgroundSyncService crypto store - let cryptoStore = MXRealmCryptoStore(credentials: bobCredentials) - XCTAssertNotNil(cryptoStore?.inboundGroupSession(withId: sessionId, andSenderKey: senderKey), "Key must be now stored in the main MXRealmCryptoStore") - - self.bgSyncService?.event(withEventId: eventId, inRoom: roomId) { _ in - let bgSyncServiceCryptoStore = MXRealmCryptoStore(credentials: self.credentialForBgCryptoStore(withCredentials: bobCredentials)) - XCTAssertNil(bgSyncServiceCryptoStore?.inboundGroupSession(withId: sessionId, andSenderKey: senderKey), "Key must be no more stored in the intermediate MXBackgroundService MXRealmCryptoStore") - - expectation?.fulfill() - } - }) - }) - case .failure(let error): - XCTFail("Cannot fetch the event from background sync service - error: \(error)") - expectation?.fulfill() - } - } - case .failure(let error): - XCTFail("Cannot set up initial test conditions - error: \(error)") - expectation?.fulfill() - } - } - - } - } - } - // Nonimal test with room summary: Get the room summary from the background service // - Alice and Bob are in an encrypted room // - Bob stops their app diff --git a/MatrixSDKTests/MXBaseKeyBackupTests.m b/MatrixSDKTests/MXBaseKeyBackupTests.m index 94ec9b427a..44186684aa 100644 --- a/MatrixSDKTests/MXBaseKeyBackupTests.m +++ b/MatrixSDKTests/MXBaseKeyBackupTests.m @@ -16,15 +16,8 @@ #import "MXBaseKeyBackupTests.h" -#import "MXCrypto_Private.h" -#import "MXCryptoStore.h" #import "MXRecoveryKey.h" -#import "MXKeybackupPassword.h" -#import "MXOutboundSessionInfo.h" -#import "MXCrossSigning_Private.h" -#import "MXKeyBackupAlgorithm.h" #import "MXAes256BackupAuthData.h" -#import "MXNativeKeyBackupEngine.h" #import "MatrixSDKTestsSwiftHeader.h" @implementation MXBaseKeyBackupTests @@ -235,42 +228,6 @@ - (void)testRESTDeleteBackupKeys }]; } - -/** - - From doE2ETestWithAliceAndBobInARoomWithCryptedMessages, we should have no backed up keys - - Check backup keys after having marked one as backed up - - Reset keys backup markers - */ -- (void)testBackupStore -{ - [matrixSDKTestsE2EData doE2ETestWithAliceAndBobInARoomWithCryptedMessages:self cryptedBob:YES readyToTest:^(MXSession *aliceSession, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation) { - - // - From doE2ETestWithAliceAndBobInARoomWithCryptedMessages, we should have no backed up keys - NSArray *sessions = [aliceSession.legacyCrypto.store inboundGroupSessionsToBackup:100]; - NSUInteger sessionsCount = sessions.count; - XCTAssertGreaterThan(sessionsCount, 0); - XCTAssertEqual([aliceSession.legacyCrypto.store inboundGroupSessionsCount:NO], sessionsCount); - XCTAssertEqual([aliceSession.legacyCrypto.store inboundGroupSessionsCount:YES], 0); - - // - Check backup keys after having marked one as backed up - MXOlmInboundGroupSession *session = sessions.firstObject; - [aliceSession.legacyCrypto.store markBackupDoneForInboundGroupSessions:@[session]]; - sessions = [aliceSession.legacyCrypto.store inboundGroupSessionsToBackup:100]; - XCTAssertEqual(sessions.count, sessionsCount - 1); - XCTAssertEqual([aliceSession.legacyCrypto.store inboundGroupSessionsCount:NO], sessionsCount); - XCTAssertEqual([aliceSession.legacyCrypto.store inboundGroupSessionsCount:YES], 1); - - // - Reset keys backup markers - [aliceSession.legacyCrypto.store resetBackupMarkers]; - sessions = [aliceSession.legacyCrypto.store inboundGroupSessionsToBackup:100]; - XCTAssertEqual(sessions.count, sessionsCount); - XCTAssertEqual([aliceSession.legacyCrypto.store inboundGroupSessionsCount:NO], sessionsCount); - XCTAssertEqual([aliceSession.legacyCrypto.store inboundGroupSessionsCount:YES], 0); - - [expectation fulfill]; - }]; -} - /** - Check [MXRecoveryKey encode:] - Check [MXRecoveryKey decode:error:] with a valid recovery key @@ -323,60 +280,6 @@ - (void)testIsValidRecoveryKey XCTAssertFalse([MXRecoveryKey isValidRecoveryKey:invalidRecoveryKey3]); } -/** - Check `MXKeyBackupPassword` utilities bijection. - */ -- (void)testPassword -{ - NSString *password = @"password"; - NSString *salt; - NSUInteger iterations; - NSError *error; - - NSData *generatedPrivateKey = [MXKeyBackupPassword generatePrivateKeyWithPassword:password salt:&salt iterations:&iterations error:&error]; - - XCTAssertNil(error); - XCTAssertNotNil(salt); - XCTAssertEqual(salt.length, 32); // kSaltLength - XCTAssertEqual(iterations, 500000); // kDefaultIterations - XCTAssertNotNil(generatedPrivateKey); - XCTAssertEqual(generatedPrivateKey.length, [OLMPkDecryption privateKeyLength]); - - NSData *retrievedPrivateKey = [MXKeyBackupPassword retrievePrivateKeyWithPassword:password salt:salt iterations:iterations error:&error]; - XCTAssertNil(error); - XCTAssertNotNil(retrievedPrivateKey); - XCTAssertEqual(retrievedPrivateKey.length, [OLMPkDecryption privateKeyLength]); - XCTAssertEqualObjects(retrievedPrivateKey, generatedPrivateKey); -} - -/** - Check `[MXKeyBackupPassword retrievePrivateKeyWithPassword:]` with data coming from - another platform. - */ -- (void)testPasswordInteroperability -{ - // This data has been generated from riot-web - NSString *password = @"This is a passphrase!"; - NSString *salt = @"TO0lxhQ9aYgGfMsclVWPIAublg8h9Nlu"; - NSUInteger iterations = 500000; - UInt8 privateKeyBytes[] = { - 116, 224, 229, 224, 9, 3, 178, 162, - 120, 23, 108, 218, 22, 61, 241, 200, - 235, 173, 236, 100, 115, 247, 33, 132, - 195, 154, 64, 158, 184, 148, 20, 85 - }; - NSData *privateKey = [NSData dataWithBytes:privateKeyBytes length:sizeof(privateKeyBytes)]; - - NSError *error; - NSData *retrievedPrivateKey = [MXKeyBackupPassword retrievePrivateKeyWithPassword:password salt:salt iterations:iterations error:&error]; - XCTAssertNil(error); - - XCTAssertNotNil(retrievedPrivateKey); - XCTAssertEqual(retrievedPrivateKey.length, [OLMPkDecryption privateKeyLength]); - - XCTAssertEqualObjects(retrievedPrivateKey, privateKey); -} - /** Check that `[MXKeyBackup createKeyBackupVersion` returns valid data */ @@ -410,50 +313,6 @@ - (void)testCreateKeyBackupVersion }]; } -/** - - Check that `[MXKeyBackup createKeyBackupVersion` launches the backup - - Check the backup completes - */ -- (void)testBackupAfterCreateKeyBackupVersion -{ - [matrixSDKTestsE2EData doE2ETestWithAliceAndBobInARoomWithCryptedMessages:self cryptedBob:YES readyToTest:^(MXSession *aliceSession, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation) { - - [aliceSession.crypto.backup prepareKeyBackupVersionWithPassword:nil algorithm:self.algorithm success:^(MXMegolmBackupCreationInfo * _Nonnull keyBackupCreationInfo) { - [aliceSession.crypto.backup createKeyBackupVersion:keyBackupCreationInfo success:^(MXKeyBackupVersion * _Nonnull keyBackupVersion) { - - // Check that `[MXKeyBackup createKeyBackupVersion` launches the backup - XCTAssert(aliceSession.crypto.backup.state == MXKeyBackupStateEnabling - || aliceSession.crypto.backup.state == MXKeyBackupStateWillBackUp); - - NSUInteger keys = [aliceSession.legacyCrypto.store inboundGroupSessionsCount:NO]; - - __block id observer; - observer = [[NSNotificationCenter defaultCenter] addObserverForName:kMXKeyBackupDidStateChangeNotification object:aliceSession.crypto.backup queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { - - // Check the backup completes - if (observer && aliceSession.crypto.backup.state == MXKeyBackupStateReadyToBackUp) - { - [[NSNotificationCenter defaultCenter] removeObserver:observer]; - observer = nil; - - NSUInteger backedUpkeys = [aliceSession.legacyCrypto.store inboundGroupSessionsCount:YES]; - XCTAssertEqual(backedUpkeys, keys, @"All keys must have been marked as backed up"); - - [expectation fulfill]; - } - }]; - - } failure:^(NSError * _Nonnull error) { - XCTFail(@"The request should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - } failure:^(NSError * _Nonnull error) { - XCTFail(@"The request should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - }]; -} - /** - Create a backup version - Check the returned MXKeyBackupVersion is trusted @@ -552,371 +411,6 @@ - (void)testCrossSigningMSKTrustForKeyBackupVersion }]; } -/** - Check that `[MXKeyBackup backupAllGroupSessions]` returns valid data - */ -- (void)testBackupAllGroupSessions -{ - [matrixSDKTestsE2EData doE2ETestWithAliceAndBobInARoomWithCryptedMessages:self cryptedBob:YES readyToTest:^(MXSession *aliceSession, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation) { - - // Check that `[MXKeyBackup backupAllGroupSessions]` returns valid data - [aliceSession.crypto.backup prepareKeyBackupVersionWithPassword:nil algorithm:self.algorithm success:^(MXMegolmBackupCreationInfo * _Nonnull keyBackupCreationInfo) { - [aliceSession.crypto.backup createKeyBackupVersion:keyBackupCreationInfo success:^(MXKeyBackupVersion * _Nonnull keyBackupVersion) { - - NSUInteger keys = [aliceSession.legacyCrypto.store inboundGroupSessionsCount:NO]; - __block NSUInteger lastbackedUpkeysProgress = 0; - - [aliceSession.crypto.backup backupAllGroupSessions:^{ - - NSUInteger backedUpkeys = [aliceSession.legacyCrypto.store inboundGroupSessionsCount:YES]; - XCTAssertEqual(backedUpkeys, keys, @"All keys must have been marked as backed up"); - - XCTAssertEqual(lastbackedUpkeysProgress, keys); - - [expectation fulfill]; - - } progress:^(NSProgress * _Nonnull backupProgress) { - - XCTAssertEqual(backupProgress.totalUnitCount, keys); - lastbackedUpkeysProgress = backupProgress.completedUnitCount; - - } failure:^(NSError * _Nonnull error) { - XCTFail(@"The request should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - } failure:^(NSError * _Nonnull error) { - XCTFail(@"The request should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - } failure:^(NSError * _Nonnull error) { - XCTFail(@"The request should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - }]; -} - -/** - Check encryption and decryption of megolm keys in the backup. - - Pick a megolm key - - Check [MXKeyBackup encryptGroupSession] returns stg - - Check [MXKeyBackup pkDecryptionFromRecoveryKey] is able to create a OLMPkDecryption - - Check [MXKeyBackup decryptKeyBackupData] returns stg - - Compare the decrypted megolm key with the original one - */ -- (void)testEncryptAndDecryptKeyBackupData -{ - [matrixSDKTestsE2EData doE2ETestWithAliceAndBobInARoomWithCryptedMessages:self cryptedBob:YES readyToTest:^(MXSession *aliceSession, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation) { - - // - Pick a megolm key - MXOlmInboundGroupSession *session = [aliceSession.legacyCrypto.store inboundGroupSessionsToBackup:1].firstObject; - XCTAssertFalse(session.isUntrusted); - session.untrusted = self.isUntrusted; - - [aliceSession.crypto.backup prepareKeyBackupVersionWithPassword:nil algorithm:self.algorithm success:^(MXMegolmBackupCreationInfo * _Nonnull keyBackupCreationInfo) { - [aliceSession.crypto.backup createKeyBackupVersion:keyBackupCreationInfo success:^(MXKeyBackupVersion * _Nonnull keyBackupVersion) { - - // This test relies on internal implementation detail (keyBackupAlgorithm class) only available with crypto v1. - // When run as V2 this test should fail until a better test is written - id engine = [aliceSession.crypto.backup valueForKey:@"engine"]; - if (!engine || ![engine isKindOfClass:[MXNativeKeyBackupEngine class]]) { - XCTFail(@"Cannot verify test"); - [expectation fulfill]; - } - id keyBackupAlgorithm = ((MXNativeKeyBackupEngine *)engine).keyBackupAlgorithm; - - // - Check [MXKeyBackupAlgorithm encryptGroupSession] returns stg - MXKeyBackupData *keyBackupData = [keyBackupAlgorithm encryptGroupSession:session]; - XCTAssertNotNil(keyBackupData); - XCTAssertNotNil(keyBackupData.sessionData); - - // - Check [MXKeyBackupAlgorithm decryptKeyBackupData] returns stg - MXMegolmSessionData *sessionData = [keyBackupAlgorithm decryptKeyBackupData:keyBackupData forSession:session.session.sessionIdentifier inRoom:roomId]; - XCTAssertNotNil(sessionData); - XCTAssertEqual(sessionData.isUntrusted, self.isUntrusted); - - // - Compare the decrypted megolm key with the original one - XCTAssertEqualObjects(session.exportSessionData.JSONDictionary, sessionData.JSONDictionary); - - [expectation fulfill]; - - } failure:^(NSError * _Nonnull error) { - XCTFail(@"The request should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - } failure:^(NSError * _Nonnull error) { - XCTFail(@"The request should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - }]; -} - -/** - Common initial conditions: - - Do an e2e backup to the homeserver - - Log Alice on a new device - */ -- (void)createKeyBackupScenarioWithPassword:(NSString*)password readyToTest:(void (^)(NSString *version, MXMegolmBackupCreationInfo *keyBackupCreationInfo, NSArray *aliceKeys, MXSession *aliceSession, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation))readyToTest -{ - [matrixSDKTestsE2EData doE2ETestWithAliceAndBobInARoomWithCryptedMessages:self cryptedBob:YES readyToTest:^(MXSession *aliceSession, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation) { - - NSArray *aliceKeys = [aliceSession.legacyCrypto.store inboundGroupSessionsToBackup:100]; - for (MXOlmInboundGroupSession *key in aliceKeys) - { - key.untrusted = self.isUntrusted; - } - - // - Do an e2e backup to the homeserver - [aliceSession.crypto.backup prepareKeyBackupVersionWithPassword:password algorithm:self.algorithm success:^(MXMegolmBackupCreationInfo * _Nonnull keyBackupCreationInfo) { - [aliceSession.crypto.backup createKeyBackupVersion:keyBackupCreationInfo success:^(MXKeyBackupVersion * _Nonnull keyBackupVersion) { - [aliceSession.crypto.backup backupAllGroupSessions:^{ - - // - Log Alice on a new device - [MXSDKOptions sharedInstance].enableCryptoWhenStartingMXSession = YES; - [self->matrixSDKTestsData relogUserSessionWithNewDevice:self session:aliceSession withPassword:MXTESTS_ALICE_PWD onComplete:^(MXSession *aliceSession2) { - [MXSDKOptions sharedInstance].enableCryptoWhenStartingMXSession = NO; - - // Test check: aliceSession2 has no keys at login - XCTAssertEqual([aliceSession2.legacyCrypto.store inboundGroupSessionsCount:NO], 0); - - readyToTest(keyBackupVersion.version, keyBackupCreationInfo, aliceKeys, aliceSession2, bobSession, roomId, expectation); - - }]; - } progress:nil failure:^(NSError * _Nonnull error) { - XCTFail(@"The request should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - } failure:^(NSError * _Nonnull error) { - XCTFail(@"The request should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - } failure:^(NSError * _Nonnull error) { - XCTFail(@"The request should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - }]; -} - -/** - Common restore success check after `createKeyBackupScenarioWithPassword`: - - Imported keys number must be correct - - The new device must have the same count of megolm keys - - Alice must have the same keys on both devices - */ -- (void)checkRestoreSuccess:(NSArray *)aliceKeys aliceSession:(MXSession *)aliceSession total:(NSUInteger)total imported:(NSUInteger)imported -{ - // - Imported keys number must be correct - XCTAssertEqual(total, aliceKeys.count); - XCTAssertEqual(total, imported); - - // - The new device must have the same count of megolm keys - XCTAssertEqual([aliceSession.legacyCrypto.store inboundGroupSessionsCount:NO], aliceKeys.count); - - // - Alice must have the same keys on both devices - for (MXOlmInboundGroupSession *aliceKey1 in aliceKeys) - { - MXOlmInboundGroupSession *aliceKey2 = [aliceSession.legacyCrypto.store inboundGroupSessionWithId:aliceKey1.session.sessionIdentifier andSenderKey:aliceKey1.senderKey]; - XCTAssertNotNil(aliceKey2); - XCTAssertEqualObjects(aliceKey2.exportSessionData.JSONDictionary, aliceKey1.exportSessionData.JSONDictionary); - } -} - -/** - - Do an e2e backup to the homeserver with a recovery key - - And log Alice on a new device - - Restore the e2e backup with recovery key - - Restore must be successful - */ -- (void)testRestoreKeyBackup -{ - // - Do an e2e backup to the homeserver with a recovery key - // - And log Alice on a new device - [self createKeyBackupScenarioWithPassword:nil readyToTest:^(NSString *version, MXMegolmBackupCreationInfo *keyBackupCreationInfo, NSArray *aliceKeys, MXSession *aliceSession, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation) { - - // - Restore the e2e backup with recovery key - [aliceSession.crypto.backup restoreKeyBackup:aliceSession.crypto.backup.keyBackupVersion - withRecoveryKey:keyBackupCreationInfo.recoveryKey - room:nil - session:nil - success:^(NSUInteger total, NSUInteger imported) - { - // - Restore must be successful - [self checkRestoreSuccess:aliceKeys aliceSession:aliceSession total:total imported:imported]; - - [expectation fulfill]; - - } failure:^(NSError * _Nonnull error) { - XCTFail(@"The request should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - }]; -} - -/** - - Do an e2e backup to the homeserver with a recovery key - - Log Alice on a new device - - Try to restore the e2e backup with a wrong recovery key - - It must fail - */ -- (void)testRestoreKeyBackupWithAWrongRecoveryKey -{ - // - Do an e2e backup to the homeserver with a recovery key - // - Log Alice on a new device - [self createKeyBackupScenarioWithPassword:nil readyToTest:^(NSString *version, MXMegolmBackupCreationInfo *keyBackupCreationInfo, NSArray *aliceKeys, MXSession *aliceSession, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation) { - - // - Try to restore the e2e backup with a wrong recovery key - [aliceSession.crypto.backup restoreKeyBackup:aliceSession.crypto.backup.keyBackupVersion - withRecoveryKey:@"EsTc LW2K PGiF wKEA 3As5 g5c4 BXwk qeeJ ZJV8 Q9fu gUMN UE4d" - room:nil session:nil - success:^(NSUInteger total, NSUInteger imported) - { - // - It must fail - XCTFail(@"It must fail"); - - [expectation fulfill]; - - } failure:^(NSError * _Nonnull error) { - - // - It must fail - XCTAssertEqualObjects(error.domain, MXKeyBackupErrorDomain); - XCTAssertEqual(error.code, MXKeyBackupErrorInvalidRecoveryKeyCode); - - [expectation fulfill]; - }]; - }]; -} - -/** - - Do an e2e backup to the homeserver with a password - - Log Alice on a new device - - Restore the e2e backup with the password - - Restore must be successful - */ -- (void)testRestoreKeyBackupWithPassword -{ - NSString *password = @"password"; - - // - Do an e2e backup to the homeserver with a password - // - And log Alice on a new device - [self createKeyBackupScenarioWithPassword:password readyToTest:^(NSString *version, MXMegolmBackupCreationInfo *keyBackupCreationInfo, NSArray *aliceKeys, MXSession *aliceSession, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation) { - - // - Restore the e2e backup with the password - [aliceSession.crypto.backup restoreKeyBackup:aliceSession.crypto.backup.keyBackupVersion - withPassword:password - room:nil session:nil - success:^(NSUInteger total, NSUInteger imported) - { - // - Restore must be successful - [self checkRestoreSuccess:aliceKeys aliceSession:aliceSession total:total imported:imported]; - - [expectation fulfill]; - - } failure:^(NSError * _Nonnull error) { - XCTFail(@"The request should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - }]; -} - -/** - - Do an e2e backup to the homeserver with a password - - Log Alice on a new device - - Try to restore the e2e backup with a wrong password - - It must fail - */ -- (void)testRestoreKeyBackupWithAWrongPassword -{ - // - Do an e2e backup to the homeserver with a password - // - Log Alice on a new device - [self createKeyBackupScenarioWithPassword:@"password" readyToTest:^(NSString *version, MXMegolmBackupCreationInfo *keyBackupCreationInfo, NSArray *aliceKeys, MXSession *aliceSession, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation) { - - // - Try to restore the e2e backup with a wrong password - [aliceSession.crypto.backup restoreKeyBackup:aliceSession.crypto.backup.keyBackupVersion - withPassword:@"WrongPassword" - room:nil session:nil - success:^(NSUInteger total, NSUInteger imported) - { - // - It must fail - XCTFail(@"It must fail"); - - [expectation fulfill]; - - } failure:^(NSError * _Nonnull error) { - - // - It must fail - XCTAssertEqualObjects(error.domain, MXKeyBackupErrorDomain); - XCTAssertEqual(error.code, MXKeyBackupErrorInvalidRecoveryKeyCode); - - [expectation fulfill]; - }]; - }]; -} - -/** - - Do an e2e backup to the homeserver with a password - - Log Alice on a new device - - Restore the e2e backup with the recovery key. - - Restore must be successful - */ -- (void)testUseRecoveryKeyToRestoreAPasswordKeyKeyBackup -{ - NSString *password = @"password"; - - // - Do an e2e backup to the homeserver with a password - // - And log Alice on a new device - [self createKeyBackupScenarioWithPassword:password readyToTest:^(NSString *version, MXMegolmBackupCreationInfo *keyBackupCreationInfo, NSArray *aliceKeys, MXSession *aliceSession, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation) { - - // - Restore the e2e backup with the recovery key. - [aliceSession.crypto.backup restoreKeyBackup:aliceSession.crypto.backup.keyBackupVersion - withRecoveryKey:keyBackupCreationInfo.recoveryKey - room:nil session:nil - success:^(NSUInteger total, NSUInteger imported) - { - // - Restore must be successful - [self checkRestoreSuccess:aliceKeys aliceSession:aliceSession total:total imported:imported]; - - [expectation fulfill]; - - } failure:^(NSError * _Nonnull error) { - XCTFail(@"The request should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - }]; -} - -/** - - Do an e2e backup to the homeserver with a recovery key - - And log Alice on a new device - - Try to restore the e2e backup with a password - - It must fail - */ -- (void)testUsePasswordToRestoreARecoveryKeyKeyBackup -{ - // - Do an e2e backup to the homeserver with a recovery key - // - And log Alice on a new device - [self createKeyBackupScenarioWithPassword:nil readyToTest:^(NSString *version, MXMegolmBackupCreationInfo *keyBackupCreationInfo, NSArray *aliceKeys, MXSession *aliceSession, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation) { - - // - Try to restore the e2e backup with a password - [aliceSession.crypto.backup restoreKeyBackup:aliceSession.crypto.backup.keyBackupVersion - withPassword:@"password" - room:nil session:nil - success:^(NSUInteger total, NSUInteger imported) - { - // - It must fail - XCTFail(@"Restoring with a password a backup created with only a recovery key must fail"); - - [expectation fulfill]; - - } failure:^(NSError * _Nonnull error) { - - // - It must fail - XCTAssertEqualObjects(error.domain, MXKeyBackupErrorDomain); - XCTAssertEqual(error.code, MXKeyBackupErrorMissingPrivateKeySaltCode); - - [expectation fulfill]; - }]; - }]; -} - /** Check backup starts automatically if there is an existing and compatible backup version on the homeserver. @@ -981,31 +475,31 @@ - (void)testCheckAndStartKeyBackupWhenRestartingAMatrixSession - (void)testBackupWhenAnotherBackupWasCreated { [matrixSDKTestsE2EData doE2ETestWithAliceAndBobInARoomWithCryptedMessages:self cryptedBob:YES readyToTest:^(MXSession *aliceSession, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation) { - + // - Make alice back up her keys to her homeserver [aliceSession.crypto.backup prepareKeyBackupVersionWithPassword:nil algorithm:self.algorithm success:^(MXMegolmBackupCreationInfo * _Nonnull keyBackupCreationInfo) { [aliceSession.crypto.backup createKeyBackupVersion:keyBackupCreationInfo success:^(MXKeyBackupVersion * _Nonnull keyBackupVersion) { - + XCTAssertTrue(aliceSession.crypto.backup.enabled); - + // - Create a new backup with fake data on the homeserver [aliceSession.matrixRestClient createKeyBackupVersion:self.fakeKeyBackupVersion success:^(NSString *version) { - + // - Make alice back up all her keys again [aliceSession.crypto.backup backupAllGroupSessions:^{ - + XCTFail(@"The backup must fail"); [expectation fulfill]; - + } progress:nil failure:^(NSError * _Nonnull error) { - + // -> That must fail and her backup state must be MXKeyBackupStateWrongBackUpVersion XCTAssertEqual(aliceSession.crypto.backup.state, MXKeyBackupStateWrongBackUpVersion); XCTAssertFalse(aliceSession.crypto.backup.enabled); - + [expectation fulfill]; }]; - + } failure:^(NSError * _Nonnull error) { XCTFail(@"The request should not fail - NSError: %@", error); [expectation fulfill]; @@ -1021,401 +515,6 @@ - (void)testBackupWhenAnotherBackupWasCreated }]; } -/** - - Do an e2e backup to the homeserver - - Log Alice on a new device - - Post a message to have a new megolm session - - Try to backup all - -> It must fail. Backup state must be MXKeyBackupStateNotTrusted - - Validate the old device from the new one - -> Backup should automatically enable on the new device - -> It must use the same backup version - - Try to backup all again - -> It must success - */ -- (void)testBackupAfterVerifyingADevice -{ - [matrixSDKTestsE2EData doE2ETestWithAliceAndBobInARoomWithCryptedMessages:self cryptedBob:YES readyToTest:^(MXSession *aliceSession, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation) { - - // - Do an e2e backup to the homeserver - [aliceSession.crypto.backup prepareKeyBackupVersionWithPassword:nil algorithm:self.algorithm success:^(MXMegolmBackupCreationInfo * _Nonnull keyBackupCreationInfo) { - [aliceSession.crypto.backup createKeyBackupVersion:keyBackupCreationInfo success:^(MXKeyBackupVersion * _Nonnull keyBackupVersion) { - [aliceSession.crypto.backup backupAllGroupSessions:^{ - - NSString *oldDeviceId = aliceSession.matrixRestClient.credentials.deviceId; - MXKeyBackupVersion *oldKeyBackupVersion = keyBackupVersion; - - // - Log Alice on a new device - [MXSDKOptions sharedInstance].enableCryptoWhenStartingMXSession = YES; - [self->matrixSDKTestsData relogUserSessionWithNewDevice:self session:aliceSession withPassword:MXTESTS_ALICE_PWD onComplete:^(MXSession *aliceSession2) { - [MXSDKOptions sharedInstance].enableCryptoWhenStartingMXSession = NO; - - // - Post a message to have a new megolm session - aliceSession2.legacyCrypto.warnOnUnknowDevices = NO; - MXRoom *room2 = [aliceSession2 roomWithRoomId:roomId]; - [room2 sendTextMessage:@"New keys" threadId:nil success:^(NSString *eventId) { - - // - Try to backup all - [aliceSession2.crypto.backup backupAllGroupSessions:^{ - - XCTFail(@"The backup must fail"); - [expectation fulfill]; - - } progress:nil failure:^(NSError * _Nonnull error) { - - // -> It must fail. Backup state must be MXKeyBackupStateNotTrusted - XCTAssertEqualObjects(error.domain, MXKeyBackupErrorDomain); - XCTAssertEqual(error.code, MXKeyBackupErrorInvalidStateCode); - XCTAssertEqual(aliceSession2.crypto.backup.state, MXKeyBackupStateNotTrusted); - XCTAssertFalse(aliceSession2.crypto.backup.enabled); - - // - Validate the old device from the new one - [aliceSession2.crypto setDeviceVerification:MXDeviceVerified forDevice:oldDeviceId ofUser:aliceSession2.myUser.userId success:nil failure:^(NSError *error) { - XCTFail(@"The request should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - - // -> Backup should automatically enable on the new device - __block id observer; - observer = [[NSNotificationCenter defaultCenter] addObserverForName:kMXKeyBackupDidStateChangeNotification object:aliceSession2.crypto.backup queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { - - if (observer && aliceSession2.crypto.backup.state == MXKeyBackupStateReadyToBackUp) - { - [[NSNotificationCenter defaultCenter] removeObserver:observer]; - observer = nil; - - // -> It must use the same backup version - XCTAssertEqualObjects(oldKeyBackupVersion.version, aliceSession2.crypto.backup.keyBackupVersion.version); - - // - Try to backup all again - [aliceSession2.crypto.backup backupAllGroupSessions:^{ - - // -> It must success - XCTAssertTrue(aliceSession2.crypto.backup.enabled); - - [expectation fulfill]; - - } progress:nil failure:^(NSError * _Nonnull error) { - XCTFail(@"The request should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - } - }]; - }]; - } failure:^(NSError *error) { - XCTFail(@"The request should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - }]; - } progress:nil failure:^(NSError * _Nonnull error) { - XCTFail(@"The request should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - } failure:^(NSError * _Nonnull error) { - XCTFail(@"The request should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - } failure:^(NSError * _Nonnull error) { - XCTFail(@"The request should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - }]; -} - -/** - This is the same as `testRestoreKeyBackup` but this test checks that pending key - share requests are cancelled. - - - Do an e2e backup to the homeserver with a recovery key - - And log Alice on a new device - - Check the SDK sent key share requests - - Restore the e2e backup with recovery key - - Restore must be successful - - There must be no more pending key share requests - */ -- (void)testRestoreKeyBackupAndKeyShareRequests -{ - // - Do an e2e backup to the homeserver with a recovery key - // - And log Alice on a new device - [self createKeyBackupScenarioWithPassword:nil readyToTest:^(NSString *version, MXMegolmBackupCreationInfo *keyBackupCreationInfo, NSArray *aliceKeys, MXSession *aliceSession, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation) { - - // - Check the SDK sent key share requests - [self->matrixSDKTestsE2EData outgoingRoomKeyRequestInSession:aliceSession complete:^(MXOutgoingRoomKeyRequest *outgoingRoomKeyRequest) { - - XCTAssertNotNil(outgoingRoomKeyRequest); - - // - Restore the e2e backup with recovery key - [aliceSession.crypto.backup restoreKeyBackup:aliceSession.crypto.backup.keyBackupVersion - withRecoveryKey:keyBackupCreationInfo.recoveryKey - room:nil session:nil - success:^(NSUInteger total, NSUInteger imported) - { - // - Restore must be successful - [self checkRestoreSuccess:aliceKeys aliceSession:aliceSession total:total imported:imported]; - - // Wait to check that no notification happens - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{ - - // - There must be no more pending key share requests - [self->matrixSDKTestsE2EData outgoingRoomKeyRequestInSession:aliceSession complete:^(MXOutgoingRoomKeyRequest *outgoingRoomKeyRequest) { - - XCTAssertNil(outgoingRoomKeyRequest); - - [expectation fulfill]; - }]; - - }); - - } failure:^(NSError * _Nonnull error) { - XCTFail(@"The request should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - - }]; - }]; -} - -/** - - Do an e2e backup to the homeserver with a recovery key - - And log Alice on a new device - - The new device must see the previous backup as not trusted - - Trust the backup from the new device - - Backup must be enabled on the new device - - Retrieve the last version from the server - - It must be the same - - It must be trusted and must have with 2 signatures now - */ -- (void)testTrustKeyBackupVersion -{ - // - Do an e2e backup to the homeserver with a recovery key - // - And log Alice on a new device - [self createKeyBackupScenarioWithPassword:nil readyToTest:^(NSString *version, MXMegolmBackupCreationInfo *keyBackupCreationInfo, NSArray *aliceKeys, MXSession *aliceSession, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation) { - - // - The new device must see the previous backup as not trusted - XCTAssertNotNil(aliceSession.crypto.backup.keyBackupVersion); - XCTAssertFalse(aliceSession.crypto.backup.enabled); - XCTAssertEqual(aliceSession.crypto.backup.state, MXKeyBackupStateNotTrusted); - - // - Trust the backup from the new device - [aliceSession.crypto.backup trustKeyBackupVersion:aliceSession.crypto.backup.keyBackupVersion trust:YES success:^{ - - // - Backup must be enabled on the new device - XCTAssertEqualObjects(aliceSession.crypto.backup.keyBackupVersion.version, version); - XCTAssertTrue(aliceSession.crypto.backup.enabled); - XCTAssertGreaterThan(aliceSession.crypto.backup.state, MXKeyBackupStateNotTrusted); - - // - Retrieve the last version from the server - [aliceSession.crypto.backup version:nil success:^(MXKeyBackupVersion * _Nullable serverKeyBackupVersion) { - - // - It must be the same - XCTAssertEqualObjects(serverKeyBackupVersion.version, version); - - [aliceSession.crypto.backup trustForKeyBackupVersion:serverKeyBackupVersion onComplete:^(MXKeyBackupVersionTrust * _Nonnull keyBackupVersionTrust) { - - // - It must be trusted and must have 2 signatures now - XCTAssertTrue(keyBackupVersionTrust.usable); - XCTAssertEqual(keyBackupVersionTrust.signatures.count, 2); - - [expectation fulfill]; - }]; - - } failure:^(NSError * _Nonnull error) { - XCTFail(@"The request should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - } failure:^(NSError * _Nonnull error) { - XCTFail(@"The request should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - }]; -} - -/** - - Do an e2e backup to the homeserver with a recovery key - - And log Alice on a new device - - The new device must see the previous backup as not trusted - - Trust the backup from the new device with the recovery key - - Backup must be enabled on the new device - - Retrieve the last version from the server - - It must be the same - - It must be trusted and must have with 2 signatures now - */ -- (void)testTrustKeyBackupVersionWithRecoveryKey -{ - // - Do an e2e backup to the homeserver with a recovery key - // - And log Alice on a new device - [self createKeyBackupScenarioWithPassword:nil readyToTest:^(NSString *version, MXMegolmBackupCreationInfo *keyBackupCreationInfo, NSArray *aliceKeys, MXSession *aliceSession, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation) { - - // - The new device must see the previous backup as not trusted - XCTAssertNotNil(aliceSession.crypto.backup.keyBackupVersion); - XCTAssertFalse(aliceSession.crypto.backup.enabled); - XCTAssertEqual(aliceSession.crypto.backup.state, MXKeyBackupStateNotTrusted); - - // - Trust the backup from the new device with the recovery key - [aliceSession.crypto.backup trustKeyBackupVersion:aliceSession.crypto.backup.keyBackupVersion withRecoveryKey:keyBackupCreationInfo.recoveryKey success:^{ - - // - Backup must be enabled on the new device - XCTAssertEqualObjects(aliceSession.crypto.backup.keyBackupVersion.version, version); - XCTAssertTrue(aliceSession.crypto.backup.enabled); - XCTAssertGreaterThan(aliceSession.crypto.backup.state, MXKeyBackupStateNotTrusted); - - // - Retrieve the last version from the server - [aliceSession.crypto.backup version:nil success:^(MXKeyBackupVersion * _Nullable serverKeyBackupVersion) { - - // - It must be the same - XCTAssertEqualObjects(serverKeyBackupVersion.version, version); - - [aliceSession.crypto.backup trustForKeyBackupVersion:serverKeyBackupVersion onComplete:^(MXKeyBackupVersionTrust * _Nonnull keyBackupVersionTrust) { - - // - It must be trusted and must have 2 signatures now - XCTAssertTrue(keyBackupVersionTrust.usable); - XCTAssertEqual(keyBackupVersionTrust.signatures.count, 2); - - [expectation fulfill]; - }]; - - } failure:^(NSError * _Nonnull error) { - XCTFail(@"The request should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - } failure:^(NSError * _Nonnull error) { - XCTFail(@"The request should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - }]; -} - -/** - - Do an e2e backup to the homeserver with a recovery key - - And log Alice on a new device - - Try to trust the backup from the new device with a wrong recovery key - - It must fail - - The backup must still be untrusted and disabled - */ -- (void)testTrustKeyBackupVersionWithWrongRecoveryKey -{ - // - Do an e2e backup to the homeserver with a recovery key - // - And log Alice on a new device - [self createKeyBackupScenarioWithPassword:nil readyToTest:^(NSString *version, MXMegolmBackupCreationInfo *keyBackupCreationInfo, NSArray *aliceKeys, MXSession *aliceSession, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation) { - - // - Try to trust the backup from the new device with a wrong recovery key - [aliceSession.crypto.backup trustKeyBackupVersion:aliceSession.crypto.backup.keyBackupVersion withRecoveryKey:@"Not a recovery key" success:^{ - - // - It must fail - XCTFail(@"The trust must fail"); - [expectation fulfill]; - - } failure:^(NSError * _Nonnull error) { - - // - It must fail - XCTAssertEqualObjects(error.domain, MXKeyBackupErrorDomain); - XCTAssertEqual(error.code, MXKeyBackupErrorInvalidRecoveryKeyCode); - - // - The backup must still be untrusted and disabled - XCTAssertEqualObjects(aliceSession.crypto.backup.keyBackupVersion.version, version); - XCTAssertFalse(aliceSession.crypto.backup.enabled); - XCTAssertEqual(aliceSession.crypto.backup.state, MXKeyBackupStateNotTrusted); - - [expectation fulfill]; - }]; - }]; -} - -/** - - Do an e2e backup to the homeserver with a password - - And log Alice on a new device - - The new device must see the previous backup as not trusted - - Trust the backup from the new device with the password - - Backup must be enabled on the new device - - Retrieve the last version from the server - - It must be the same - - It must be trusted and must have with 2 signatures now - */ -- (void)testTrustKeyBackupVersionWithPassword -{ - NSString *password = @"password"; - - // - Do an e2e backup to the homeserver with a password - // - And log Alice on a new device - [self createKeyBackupScenarioWithPassword:password readyToTest:^(NSString *version, MXMegolmBackupCreationInfo *keyBackupCreationInfo, NSArray *aliceKeys, MXSession *aliceSession, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation) { - - // - The new device must see the previous backup as not trusted - XCTAssertNotNil(aliceSession.crypto.backup.keyBackupVersion); - XCTAssertFalse(aliceSession.crypto.backup.enabled); - XCTAssertEqual(aliceSession.crypto.backup.state, MXKeyBackupStateNotTrusted); - - // - Trust the backup from the new device with the password - [aliceSession.crypto.backup trustKeyBackupVersion:aliceSession.crypto.backup.keyBackupVersion withPassword:password success:^{ - - // - Backup must be enabled on the new device - XCTAssertEqualObjects(aliceSession.crypto.backup.keyBackupVersion.version, version); - XCTAssertTrue(aliceSession.crypto.backup.enabled); - XCTAssertGreaterThan(aliceSession.crypto.backup.state, MXKeyBackupStateNotTrusted); - - // - Retrieve the last version from the server - [aliceSession.crypto.backup version:nil success:^(MXKeyBackupVersion * _Nullable serverKeyBackupVersion) { - - // - It must be the same - XCTAssertEqualObjects(serverKeyBackupVersion.version, version); - - [aliceSession.crypto.backup trustForKeyBackupVersion:serverKeyBackupVersion onComplete:^(MXKeyBackupVersionTrust * _Nonnull keyBackupVersionTrust) { - - // - It must be trusted and must have 2 signatures now - XCTAssertTrue(keyBackupVersionTrust.usable); - XCTAssertEqual(keyBackupVersionTrust.signatures.count, 2); - - [expectation fulfill]; - }]; - - } failure:^(NSError * _Nonnull error) { - XCTFail(@"The request should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - } failure:^(NSError * _Nonnull error) { - XCTFail(@"The request should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - }]; -} - -/** - - Do an e2e backup to the homeserver with a password - - And log Alice on a new device - - Try to trust the backup from the new device with a wrong password - - It must fail - - The backup must still be untrusted and disabled - */ -- (void)testTrustKeyBackupVersionWithWrongPassword -{ - NSString *password = @"password"; - - // - Do an e2e backup to the homeserver with a password - // - And log Alice on a new device - [self createKeyBackupScenarioWithPassword:password readyToTest:^(NSString *version, MXMegolmBackupCreationInfo *keyBackupCreationInfo, NSArray *aliceKeys, MXSession *aliceSession, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation) { - - // - Try to trust the backup from the new device with a wrong password - [aliceSession.crypto.backup trustKeyBackupVersion:aliceSession.crypto.backup.keyBackupVersion withPassword:@"Wrong" success:^{ - - // - It must fail - XCTFail(@"The trust must fail"); - [expectation fulfill]; - - } failure:^(NSError * _Nonnull error) { - - // - It must fail - XCTAssertEqualObjects(error.domain, MXKeyBackupErrorDomain); - XCTAssertEqual(error.code, MXKeyBackupErrorInvalidRecoveryKeyCode); - - // - The backup must still be untrusted and disabled - XCTAssertEqualObjects(aliceSession.crypto.backup.keyBackupVersion.version, version); - XCTAssertFalse(aliceSession.crypto.backup.enabled); - XCTAssertEqual(aliceSession.crypto.backup.state, MXKeyBackupStateNotTrusted); - - [expectation fulfill]; - }]; - }]; -} - #pragma mark - Private keys @@ -1489,125 +588,4 @@ - (void)testLocalPrivateKey }]; } - -/** - - Do an e2e backup to the homeserver - - Erase local private key locally (that simulates usage of the backup from another device) - - Restore the backup with a password - -> We should have now the private key locally - */ -- (void)testCatchPrivateKeyOnRecoverWithPassword -{ - [matrixSDKTestsE2EData doE2ETestWithAliceAndBobInARoomWithCryptedMessages:self cryptedBob:YES readyToTest:^(MXSession *aliceSession, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation) { - - // - Do an e2e backup to the homeserver - NSString *password = @"qwerty"; - [aliceSession.crypto.backup prepareKeyBackupVersionWithPassword:password algorithm:self.algorithm success:^(MXMegolmBackupCreationInfo * _Nonnull keyBackupCreationInfo) { - [aliceSession.crypto.backup createKeyBackupVersion:keyBackupCreationInfo success:^(MXKeyBackupVersion * _Nonnull keyBackupVersion) { - - NSString *backupSecret = [aliceSession.legacyCrypto.store secretWithSecretId:MXSecretId.keyBackup]; - XCTAssertTrue(aliceSession.crypto.backup.hasPrivateKeyInCryptoStore); - - // - Erase local private key locally (that simulates usage of the backup from another device) - [aliceSession.legacyCrypto.store deleteSecretWithSecretId:MXSecretId.keyBackup]; - XCTAssertFalse(aliceSession.crypto.backup.hasPrivateKeyInCryptoStore); - - // - Restore the backup with a password - [aliceSession.crypto.backup restoreKeyBackup:keyBackupVersion withPassword:password room:nil session:nil success:^(NSUInteger total, NSUInteger imported) { - - // -> We should have now the private key locally - XCTAssertTrue(aliceSession.crypto.backup.hasPrivateKeyInCryptoStore); - - NSString *backupSecret2 = [aliceSession.legacyCrypto.store secretWithSecretId:MXSecretId.keyBackup]; - XCTAssertEqualObjects(backupSecret, backupSecret2); - - [expectation fulfill]; - - } failure:^(NSError * _Nonnull error) { - XCTFail(@"The request should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - - } failure:^(NSError * _Nonnull error) { - XCTFail(@"The request should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - } failure:^(NSError * _Nonnull error) { - XCTFail(@"The request should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - }]; -} - - -/** - - Do an e2e backup to the homeserver - - Log Alice on a new device - - Make each Alice device trust each other - -> Alice2 should have the private backup key thanks to gossiping - -> Alice2 should have all her history decrypted. - */ -- (void)testGossipKey -{ - [matrixSDKTestsE2EData doE2ETestWithAliceAndBobInARoomWithCryptedMessages:self cryptedBob:YES readyToTest:^(MXSession *aliceSession1, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation) { - - // - Do an e2e backup to the homeserver - [aliceSession1.crypto.backup prepareKeyBackupVersionWithPassword:nil algorithm:self.algorithm success:^(MXMegolmBackupCreationInfo * _Nonnull keyBackupCreationInfo) { - [aliceSession1.crypto.backup createKeyBackupVersion:keyBackupCreationInfo success:^(MXKeyBackupVersion * _Nonnull keyBackupVersion) { - [aliceSession1.crypto.backup backupAllGroupSessions:^{ - - [self->matrixSDKTestsE2EData loginUserOnANewDevice:self credentials:aliceSession1.matrixRestClient.credentials withPassword:MXTESTS_ALICE_PWD onComplete:^(MXSession *aliceSession2) { - - // -> We must have the backup private key locally - XCTAssertFalse(aliceSession2.crypto.backup.hasPrivateKeyInCryptoStore); - - NSString *aliceUserId = aliceSession1.matrixRestClient.credentials.userId; - NSString *aliceSession1DeviceId = aliceSession1.matrixRestClient.credentials.deviceId; - NSString *aliceSession2DeviceId = aliceSession2.matrixRestClient.credentials.deviceId; - - // - Make each Alice device trust each other - // This simulates a self verification and trigger backup restore in background - [aliceSession1.crypto setDeviceVerification:MXDeviceVerified forDevice:aliceSession2DeviceId ofUser:aliceUserId success:^{ - [aliceSession2.crypto setDeviceVerification:MXDeviceVerified forDevice:aliceSession1DeviceId ofUser:aliceUserId success:^{ - - // Wait a bit to make background requests happen - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 3 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{ - - // -> Alice2 should have the private backup key thanks to gossiping - XCTAssertTrue(aliceSession2.crypto.backup.hasPrivateKeyInCryptoStore); - - // -> Alice2 should have all her history decrypted - NSUInteger inboundGroupSessionsCount = [aliceSession2.legacyCrypto.store inboundGroupSessionsCount:NO]; - XCTAssertGreaterThan(inboundGroupSessionsCount, 0); - XCTAssertEqual(inboundGroupSessionsCount, [aliceSession1.legacyCrypto.store inboundGroupSessionsCount:NO]); - - [expectation fulfill]; - }); - - } failure:^(NSError *error) { - XCTFail(@"Cannot set up intial test conditions - error: %@", error); - [expectation fulfill]; - }]; - } failure:^(NSError *error) { - XCTFail(@"Cannot set up intial test conditions - error: %@", error); - [expectation fulfill]; - }]; - - }]; - - } progress:nil failure:^(NSError * _Nonnull error) { - XCTFail(@"The request should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - } failure:^(NSError * _Nonnull error) { - XCTFail(@"The request should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - } failure:^(NSError * _Nonnull error) { - XCTFail(@"The request should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - }]; -} - @end diff --git a/MatrixSDKTests/MXCoreDataRoomListDataManagerUnitTests.swift b/MatrixSDKTests/MXCoreDataRoomListDataManagerUnitTests.swift index ab883f92fc..15bdc4ac8b 100644 --- a/MatrixSDKTests/MXCoreDataRoomListDataManagerUnitTests.swift +++ b/MatrixSDKTests/MXCoreDataRoomListDataManagerUnitTests.swift @@ -36,7 +36,6 @@ class MXCoreDataRoomListDataManagerUnitTests: XCTestCase { override class func setUp() { super.setUp() MXSDKOptions.sharedInstance().roomListDataManagerClass = MXCoreDataRoomListDataManager.self - MXRealmCryptoStore.deleteAllStores() } override class func tearDown() { diff --git a/MatrixSDKTests/MXCrossSigningTests.m b/MatrixSDKTests/MXCrossSigningTests.m index fc52eb39a1..eefafd8591 100644 --- a/MatrixSDKTests/MXCrossSigningTests.m +++ b/MatrixSDKTests/MXCrossSigningTests.m @@ -19,8 +19,6 @@ #import "MatrixSDKTestsData.h" #import "MatrixSDKTestsE2EData.h" -#import "MXCrypto_Private.h" -#import "MXCrossSigning_Private.h" #import "MXCrossSigningInfo_Private.h" #import "MXCrossSigningTools.h" @@ -230,108 +228,6 @@ - (void)testMXCrossSigningKeyMXJSONModel XCTAssertTrue([key.JSONDictionary isEqualToDictionary:JSONDict], "\n%@\nvs\n%@", key.JSONDictionary, JSONDict); } -// Test [MXCrossSigningTools testPkVerifyObject:] -- (void)testPkVerifyObject -{ - MXCrossSigningTools *crossSigningTools = [MXCrossSigningTools new]; - - // Data taken from js-sdk tests to check cross-platform compatibility - NSDictionary *JSONDict = @{ - @"user_id": @"@alice:example.com", - @"usage": @[@"self-signing"], - @"keys": @{ - @"ed25519:EmkqvokUn8p+vQAGZitOk4PWjp7Ukp3txV2TbMPEiBQ": - @"EmkqvokUn8p+vQAGZitOk4PWjp7Ukp3txV2TbMPEiBQ", - }, - @"signatures": @{ - @"@alice:example.com": @{ - @"ed25519:nqOvzeuGWT/sRx3h7+MHoInYj3Uk2LD/unI9kDYcHwk": - @"Wqx/HXR851KIi8/u/UX+fbAMtq9Uj8sr8FsOcqrLfVYa6lAmbXsVhfy4AlZ3dnEtjgZx0U0QDrghEn2eYBeOCA", - }, - } - }; - - NSError *error; - BOOL result = [crossSigningTools pkVerifyObject:JSONDict userId:@"@alice:example.com" publicKey:@"nqOvzeuGWT/sRx3h7+MHoInYj3Uk2LD/unI9kDYcHwk" error:&error]; - - XCTAssertTrue(result); - XCTAssertNil(error); - - - NSDictionary *JSONDictWithCorruptedSignature = @{ - @"user_id": @"@alice:example.com", - @"usage": @[@"self-signing"], - @"keys": @{ - @"ed25519:EmkqvokUn8p+vQAGZitOk4PWjp7Ukp3txV2TbMPEiBQ": - @"EmkqvokUn8p+vQAGZitOk4PWjp7Ukp3txV2TbMPEiBQ", - }, - @"signatures": @{ - @"@alice:example.com": @{ - @"ed25519:nqOvzeuGWT/sRx3h7+MHoInYj3Uk2LD/unI9kDYcHwk": - @"Bug/HXR851KIi8/u/UX+fbAMtq9Uj8sr8FsOcqrLfVYa6lAmbXsVhfy4AlZ3dnEtjgZx0U0QDrghEn2eYBeOCA", - }, - } - }; - - result = [crossSigningTools pkVerifyObject:JSONDictWithCorruptedSignature userId:@"@alice:example.com" publicKey:@"nqOvzeuGWT/sRx3h7+MHoInYj3Uk2LD/unI9kDYcHwk" error:&error]; - - XCTAssertFalse(result); - XCTAssertNotNil(error); -} - -// Test [MXCrossSigningTools pkVerifyKey:] -- (void)testPkVerifyKey -{ - MXCrossSigningTools *crossSigningTools = [MXCrossSigningTools new]; - - // Data taken from js-sdk tests to check cross-platform compatibility - NSDictionary *JSONDict = @{ - @"user_id": @"@alice:example.com", - @"usage": @[@"self-signing"], - @"keys": @{ - @"ed25519:EmkqvokUn8p+vQAGZitOk4PWjp7Ukp3txV2TbMPEiBQ": - @"EmkqvokUn8p+vQAGZitOk4PWjp7Ukp3txV2TbMPEiBQ", - }, - @"signatures": @{ - @"@alice:example.com": @{ - @"ed25519:nqOvzeuGWT/sRx3h7+MHoInYj3Uk2LD/unI9kDYcHwk": - @"Wqx/HXR851KIi8/u/UX+fbAMtq9Uj8sr8FsOcqrLfVYa6lAmbXsVhfy4AlZ3dnEtjgZx0U0QDrghEn2eYBeOCA", - }, - } - }; - - MXCrossSigningKey *key; - MXJSONModelSetMXJSONModel(key, MXCrossSigningKey, JSONDict); - - NSError *error; - BOOL result = [crossSigningTools pkVerifyKey:key userId:@"@alice:example.com" publicKey:@"nqOvzeuGWT/sRx3h7+MHoInYj3Uk2LD/unI9kDYcHwk" error:&error]; - - XCTAssertTrue(result); - XCTAssertNil(error); - - - NSDictionary *JSONDictWithCorruptedSignature = @{ - @"user_id": @"@alice:example.com", - @"usage": @[@"self-signing"], - @"keys": @{ - @"ed25519:EmkqvokUn8p+vQAGZitOk4PWjp7Ukp3txV2TbMPEiBQ": - @"EmkqvokUn8p+vQAGZitOk4PWjp7Ukp3txV2TbMPEiBQ", - }, - @"signatures": @{ - @"@alice:example.com": @{ - @"ed25519:nqOvzeuGWT/sRx3h7+MHoInYj3Uk2LD/unI9kDYcHwk": - @"Bug/HXR851KIi8/u/UX+fbAMtq9Uj8sr8FsOcqrLfVYa6lAmbXsVhfy4AlZ3dnEtjgZx0U0QDrghEn2eYBeOCA", - }, - } - }; - MXJSONModelSetMXJSONModel(key, MXCrossSigningKey, JSONDictWithCorruptedSignature); - - result = [crossSigningTools pkVerifyKey:key userId:@"@alice:example.com" publicKey:@"nqOvzeuGWT/sRx3h7+MHoInYj3Uk2LD/unI9kDYcHwk" error:&error]; - - XCTAssertFalse(result); - XCTAssertNotNil(error); -} - // - Create Alice // - Bootstrap cross-signing on Alice using password // -> Cross-signing must be bootstrapped @@ -464,78 +360,6 @@ - (void)testRefreshState }]; } -// -// Verify that a verified device gets cross-signing private keys so that it can cross-sign. -// -// - Bootstrap cross-signing on a 1st device -// - Create a 2nd devices -// - Make each device trust each other -// This simulates a self verification and trigger cross-signing behind the shell -// - The 2nd device requests cross-signing keys from the 1st one -// -> The 2nd device should be able to cross-sign now -// -> The 2nd device must have all cross-signing private keys -- (void)testPrivateKeysGossiping -{ - // - Create Alice - [matrixSDKTestsE2EData doE2ETestWithBobAndAlice:self readyToTest:^(MXSession *bobSession, MXSession *aliceSession, XCTestExpectation *expectation) { - - // - Bootstrap cross-signing on a 1st device - [aliceSession.crypto.crossSigning setupWithPassword:MXTESTS_ALICE_PWD success:^{ - XCTAssertEqual(aliceSession.crypto.crossSigning.state, MXCrossSigningStateCanCrossSign); - - // - Create a 2nd device - [matrixSDKTestsE2EData loginUserOnANewDevice:self credentials:aliceSession.matrixRestClient.credentials withPassword:MXTESTS_ALICE_PWD onComplete:^(MXSession *newAliceSession) { - - // - Make each device trust each other - // This simulates a self verification and trigger cross-signing behind the shell - [newAliceSession.crypto setDeviceVerification:MXDeviceVerified forDevice:aliceSession.matrixRestClient.credentials.deviceId ofUser:aliceSession.matrixRestClient.credentials.userId success:^{ - - [aliceSession.crypto setDeviceVerification:MXDeviceVerified forDevice:newAliceSession.matrixRestClient.credentials.deviceId ofUser:aliceSession.matrixRestClient.credentials.userId success:^{ - - [newAliceSession.crypto.crossSigning refreshStateWithSuccess:^(BOOL stateUpdated) { - - XCTAssertEqual(newAliceSession.crypto.crossSigning.state, MXCrossSigningStateTrustCrossSigning); - - // - The 2nd device requests cross-signing keys from the 1st one - [newAliceSession.legacyCrypto.legacyCrossSigning requestPrivateKeysToDeviceIds:nil success:^{ - } onPrivateKeysReceived:^{ - - // -> The 2nd device should be able to cross-sign now - XCTAssertEqual(newAliceSession.crypto.crossSigning.state, MXCrossSigningStateCanCrossSign); - - // -> The 2nd device must have all cross-signing private keys - XCTAssertTrue(newAliceSession.crypto.crossSigning.hasAllPrivateKeys); - [expectation fulfill]; - - } failure:^(NSError * _Nonnull error) { - XCTFail(@"The operation should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - - } failure:^(NSError * _Nonnull error) { - XCTFail(@"Cannot set up intial test conditions - error: %@", error); - [expectation fulfill]; - }]; - - } failure:^(NSError * _Nonnull error) { - XCTFail(@"Cannot set up intial test conditions - error: %@", error); - [expectation fulfill]; - }]; - - } failure:^(NSError * _Nonnull error) { - XCTFail(@"Cannot set up intial test conditions - error: %@", error); - [expectation fulfill]; - }]; - - }]; - - } failure:^(NSError *error) { - XCTFail(@"Cannot set up intial test conditions - error: %@", error); - [expectation fulfill]; - }]; - }]; -} - // Test /keys/query response parsing for cross signing data // - Set up the scenario with alice with cross-signing keys @@ -622,43 +446,6 @@ - (void)testMXCryptoDownloadKeys }]; } -// Check MXCrossSigningInfo storage in the crypto store -// - Create Alice's cross-signing keys -// - Store their keys and retrieve them -// - Update keys test -- (void)testMXCrossSigningInfoStorage -{ - // - Set up the scenario with alice with cross-signing keys - [matrixSDKTestsE2EData doE2ETestWithBobAndAlice:self readyToTest:^(MXSession *bobSession, MXSession *aliceSession, XCTestExpectation *expectation) { - - NSString *aliceUserId = aliceSession.matrixRestClient.credentials.userId; - - // - Create Alice's cross-signing keys - NSDictionary *privateKeys; - MXCrossSigningInfo *keys = [aliceSession.legacyCrypto.legacyCrossSigning createKeys:&privateKeys]; - - // - Store their keys and retrieve them - [aliceSession.legacyCrypto.store storeCrossSigningKeys:keys]; - MXCrossSigningInfo *storedKeys = [aliceSession.legacyCrypto.store crossSigningKeysForUser:aliceUserId]; - XCTAssertNotNil(storedKeys); - - XCTAssertEqualObjects(storedKeys.userId, keys.userId); - XCTAssertFalse(storedKeys.trustLevel.isVerified); - XCTAssertEqual(storedKeys.keys.count, keys.keys.count); - XCTAssertEqualObjects(storedKeys.masterKeys.JSONDictionary, keys.masterKeys.JSONDictionary); - XCTAssertEqualObjects(storedKeys.selfSignedKeys.JSONDictionary, keys.selfSignedKeys.JSONDictionary); - XCTAssertEqualObjects(storedKeys.userSignedKeys.JSONDictionary, keys.userSignedKeys.JSONDictionary); - - // - Update keys test - [keys updateTrustLevel:[MXUserTrustLevel trustLevelWithCrossSigningVerified:YES locallyVerified:NO]]; - [aliceSession.legacyCrypto.store storeCrossSigningKeys:keys]; - storedKeys = [aliceSession.legacyCrypto.store crossSigningKeysForUser:aliceUserId]; - XCTAssertTrue(storedKeys.trustLevel.isVerified); - - [expectation fulfill]; - }]; -} - // Test cross-sign of a device (as seen HS side) // - Set up the scenario with alice with 2 devices (cross-signing is enabled on the 2nd device) // - Download alice devices diff --git a/MatrixSDKTests/MXCrossSigningVerificationTests.m b/MatrixSDKTests/MXCrossSigningVerificationTests.m deleted file mode 100644 index c2aecc60ef..0000000000 --- a/MatrixSDKTests/MXCrossSigningVerificationTests.m +++ /dev/null @@ -1,855 +0,0 @@ -/* - Copyright 2020 The Matrix.org Foundation C.I.C - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - - -#import - -#import "MatrixSDKTestsData.h" -#import "MatrixSDKTestsE2EData.h" - -#import "MXCrypto_Private.h" -#import "MXKeyVerificationManager_Private.h" -#import "MXFileStore.h" - -#import "MXKeyVerificationRequestByDMJSONModel.h" - -#import "MXQRCodeTransaction_Private.h" -#import "MatrixSDKTestsSwiftHeader.h" - -// Do not bother with retain cycles warnings in tests -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Warc-retain-cycles" - -@interface MXLegacyKeyVerificationManager (Testing) - -- (id)transactionWithTransactionId:(NSString*)transactionId; -- (MXLegacyQRCodeTransaction*)qrCodeTransactionWithTransactionId:(NSString*)transactionId; - -@end - -@interface MXCrossSigningVerificationTests : XCTestCase -{ - MatrixSDKTestsData *matrixSDKTestsData; - MatrixSDKTestsE2EData *matrixSDKTestsE2EData; - - NSMutableArray *observers; -} -@end - -@implementation MXCrossSigningVerificationTests - -- (void)setUp -{ - [super setUp]; - - matrixSDKTestsData = [[MatrixSDKTestsData alloc] init]; - matrixSDKTestsE2EData = [[MatrixSDKTestsE2EData alloc] initWithMatrixSDKTestsData:matrixSDKTestsData]; - - observers = [NSMutableArray array]; -} - -- (void)tearDown -{ - matrixSDKTestsData = nil; - matrixSDKTestsE2EData = nil; - - for (id observer in observers) - { - [[NSNotificationCenter defaultCenter] removeObserver:observer]; - } - - [super tearDown]; -} - -- (void)observeSASIncomingTransactionInSession:(MXSession*)session block:(void (^)(MXIncomingSASTransaction * _Nullable transaction))block -{ - id observer = [[NSNotificationCenter defaultCenter] addObserverForName:MXKeyVerificationManagerNewTransactionNotification object:session.crypto.keyVerificationManager queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { - - idtransaction = notif.userInfo[MXKeyVerificationManagerNotificationTransactionKey]; - if (transaction.isIncoming && [transaction isKindOfClass:MXIncomingSASTransaction.class]) - { - block((MXIncomingSASTransaction*)transaction); - } - else - { - XCTFail(@"We support only SAS. transaction: %@", transaction); - } - }]; - - [observers addObject:observer]; -} - -- (void)observeNewQRCodeTransactionInSession:(MXSession*)session block:(void (^)(MXLegacyQRCodeTransaction * _Nullable transaction))block -{ - id observer = [[NSNotificationCenter defaultCenter] addObserverForName:MXKeyVerificationManagerNewTransactionNotification object:session.crypto.keyVerificationManager queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { - - idtransaction = notif.userInfo[MXKeyVerificationManagerNotificationTransactionKey]; - if ([transaction isKindOfClass:MXLegacyQRCodeTransaction.class]) - { - block((MXLegacyQRCodeTransaction*)transaction); - } - else - { - XCTFail(@"We support only QR code. transaction: %@", transaction); - } - }]; - - [observers addObject:observer]; -} - -- (void)observeTransactionUpdate:(id)transaction block:(void (^)(void))block -{ - id observer = [[NSNotificationCenter defaultCenter] addObserverForName:MXKeyVerificationTransactionDidChangeNotification object:transaction queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { - block(); - }]; - - [observers addObject:observer]; -} - -- (void)observeKeyVerificationRequestChangeWithBlock:(void (^)(id _Nullable request))block -{ - id observer = [[NSNotificationCenter defaultCenter] addObserverForName:MXKeyVerificationRequestDidChangeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { - - idrequest = notif.object; - - if ([request conformsToProtocol:@protocol(MXKeyVerificationRequest)]) - { - block((id)request); - } - else - { - XCTFail(@"We support only MXKeyVerificationRequest. request: %@", request); - } - }]; - - [observers addObject:observer]; -} - -- (void)bootstrapCrossSigningOnSession:(MXSession*)session - password:(NSString*)password - completion:(void (^)(void))completionBlock -{ - [session.crypto.crossSigning setupWithPassword:password success:^{ - completionBlock(); - } failure:^(NSError *error) { - XCTAssert(NO, @"Cannot set up intial test conditions - error: %@", error); - }]; -} - - -- (void)observeKeyVerificationRequestInSession:(MXSession*)session block:(void (^)(id _Nullable request))block -{ - id observer = [[NSNotificationCenter defaultCenter] addObserverForName:MXKeyVerificationManagerNewRequestNotification object:session.crypto.keyVerificationManager queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { - - id request = notif.userInfo[MXKeyVerificationManagerNotificationRequestKey]; - if ([request conformsToProtocol:@protocol(MXKeyVerificationRequest)]) - { - block((id)request); - } - else - { - XCTFail(@"We support only SAS. transaction: %@", request); - } - }]; - - [observers addObject:observer]; -} - - -#pragma mark - Self Verification (by to_device) - - -// After verifying a signin with cross-signing enabled, check that cross-signing is up on both side. -// This is the exact same code as testVerificationByToDeviceSelfVerificationFullFlow but we cross-signing on. -// Check tests in checkBothVerified(): -// -> Devices must be really verified -// -> My user must be really verified -- (void)testSelfVerificationWithSAS -{ - // - Have Alice - [matrixSDKTestsE2EData doE2ETestWithAliceAndBobInARoom:self cryptedBob:YES warnOnUnknowDevices:YES aliceStore:[[MXMemoryStore alloc] init] bobStore:[[MXMemoryStore alloc] init] readyToTest:^(MXSession *aliceSession1, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation) { - - // - Alice bootstrap cross-signing - [self bootstrapCrossSigningOnSession:aliceSession1 password:MXTESTS_ALICE_PWD completion:^{ - - // - Alice has a second sign-in - [matrixSDKTestsE2EData loginUserOnANewDevice:self credentials:aliceSession1.matrixRestClient.credentials withPassword:MXTESTS_ALICE_PWD onComplete:^(MXSession *aliceSession2) { - __block NSString *requestId; - - MXCredentials *alice = aliceSession1.matrixRestClient.credentials; - MXCredentials *alice2 = aliceSession2.matrixRestClient.credentials; - - NSArray *methods = @[MXKeyVerificationMethodSAS, @"toto"]; - - // - Bob requests a verification of Alice in this Room - [aliceSession2.crypto.keyVerificationManager requestVerificationByToDeviceWithUserId:alice.userId - deviceIds:@[alice.deviceId] - methods:@[MXKeyVerificationMethodSAS, @"toto"] - success:^(id requestFromBobPOV) - { - requestId = requestFromBobPOV.requestId; - - XCTAssertEqualObjects(requestFromBobPOV.otherUser, alice.userId); - XCTAssertNil(requestFromBobPOV.otherDevice); - } - failure:^(NSError * _Nonnull error) - { - XCTFail(@"The request should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - - - __block MXOutgoingSASTransaction *sasTransactionFromAlicePOV; - - // - Alice gets the requests notification - [self observeKeyVerificationRequestInSession:aliceSession1 block:^(id _Nullable requestFromAlicePOV) { - XCTAssertEqualObjects(requestFromAlicePOV.requestId, requestId); - - // Wait a bit - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{ - - XCTAssertEqualObjects(requestFromAlicePOV.methods, methods); - XCTAssertEqualObjects(requestFromAlicePOV.otherMethods, methods); - XCTAssertNil(requestFromAlicePOV.myMethods); - - XCTAssertEqualObjects(requestFromAlicePOV.otherUser, alice2.userId); - XCTAssertEqualObjects(requestFromAlicePOV.otherDevice, alice2.deviceId); - - // - Alice accepts it - [requestFromAlicePOV acceptWithMethods:@[MXKeyVerificationMethodSAS] success:^{ - - id requestFromAlicePOV2 = aliceSession1.crypto.keyVerificationManager.pendingRequests.firstObject; - XCTAssertNotNil(requestFromAlicePOV2); - XCTAssertEqualObjects(requestFromAlicePOV2.myMethods, @[MXKeyVerificationMethodSAS]); - - // - Alice begins a SAS verification - [aliceSession1.crypto.keyVerificationManager beginKeyVerificationFromRequest:requestFromAlicePOV2 method:MXKeyVerificationMethodSAS success:^(id _Nonnull transactionFromAlicePOV) { - - XCTAssertEqualObjects(transactionFromAlicePOV.transactionId, requestFromAlicePOV.requestId); - - XCTAssert(transactionFromAlicePOV); - XCTAssertTrue([transactionFromAlicePOV isKindOfClass:MXOutgoingSASTransaction.class]); - sasTransactionFromAlicePOV = (MXOutgoingSASTransaction*)transactionFromAlicePOV; - - } failure:^(NSError * _Nonnull error) { - XCTFail(@"The request should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - - } failure:^(NSError * _Nonnull error) { - XCTFail(@"The request should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - }); - }]; - - - [self observeSASIncomingTransactionInSession:aliceSession2 block:^(MXIncomingSASTransaction * _Nullable transactionFromAlice2POV) { - - // Final checks - void (^checkBothVerified)(void) = ^ void () - { - if (sasTransactionFromAlicePOV.state == MXSASTransactionStateVerified - && transactionFromAlice2POV.state == MXSASTransactionStateVerified) - { - // -> Devices must be really verified - MXDeviceInfo *aliceDevice2FromAlice1POV = [aliceSession1.legacyCrypto.store deviceWithDeviceId:alice2.deviceId forUser:alice2.userId]; - MXDeviceInfo *aliceDevice1FromAlice2POV = [aliceSession2.legacyCrypto.store deviceWithDeviceId:alice.deviceId forUser:alice.userId]; - - XCTAssertEqual(aliceDevice2FromAlice1POV.trustLevel.localVerificationStatus, MXDeviceVerified); - XCTAssertTrue(aliceDevice2FromAlice1POV.trustLevel.isCrossSigningVerified); - XCTAssertEqual(aliceDevice1FromAlice2POV.trustLevel.localVerificationStatus, MXDeviceVerified); - XCTAssertTrue(aliceDevice1FromAlice2POV.trustLevel.isCrossSigningVerified); - - // -> My user must be really verified - MXCrossSigningInfo *aliceFromAlice1POV = [aliceSession1.legacyCrypto.store crossSigningKeysForUser:alice.userId]; - MXCrossSigningInfo *aliceFromAlice2POV = [aliceSession2.legacyCrypto.store crossSigningKeysForUser:alice.userId]; - - XCTAssertTrue(aliceFromAlice1POV.trustLevel.isCrossSigningVerified); - XCTAssertTrue(aliceFromAlice1POV.trustLevel.isLocallyVerified); - XCTAssertTrue(aliceFromAlice2POV.trustLevel.isCrossSigningVerified); - XCTAssertTrue(aliceFromAlice2POV.trustLevel.isLocallyVerified); - - // -> Transaction must not be listed anymore - XCTAssertNil([(MXLegacyKeyVerificationManager *)aliceSession1.crypto.keyVerificationManager transactionWithTransactionId:sasTransactionFromAlicePOV.transactionId]); - XCTAssertNil([(MXLegacyKeyVerificationManager *)aliceSession2.crypto.keyVerificationManager transactionWithTransactionId:transactionFromAlice2POV.transactionId]); - - [expectation fulfill]; - } - }; - - // -> Transaction on Alice side must be WaitForPartnerKey, then ShowSAS - [self observeTransactionUpdate:sasTransactionFromAlicePOV block:^{ - - switch (sasTransactionFromAlicePOV.state) - { - // -> 2. Transaction on Alice side must then move to WaitForPartnerKey - case MXSASTransactionStateWaitForPartnerKey: - XCTAssertEqual(transactionFromAlice2POV.state, MXSASTransactionStateWaitForPartnerKey); - break; - // -> 4. Transaction on Alice side must then move to ShowSAS - case MXSASTransactionStateShowSAS: - XCTAssertEqual(transactionFromAlice2POV.state, MXSASTransactionStateShowSAS); - - // -> 5. SASs must be the same - XCTAssertEqualObjects(sasTransactionFromAlicePOV.sasBytes, transactionFromAlice2POV.sasBytes); - XCTAssertEqualObjects(sasTransactionFromAlicePOV.sasDecimal, transactionFromAlice2POV.sasDecimal); - XCTAssertEqualObjects(sasTransactionFromAlicePOV.sasEmoji, transactionFromAlice2POV.sasEmoji); - - // - Alice confirms SAS - [sasTransactionFromAlicePOV confirmSASMatch]; - break; - // -> 6. Transaction on Alice side must then move to WaitForPartnerToConfirm - case MXSASTransactionStateWaitForPartnerToConfirm: - // - Bob confirms SAS - [transactionFromAlice2POV confirmSASMatch]; - break; - // -> 7. Transaction on Alice side must then move to Verified - case MXSASTransactionStateVerified: - checkBothVerified(); - break; - default: - XCTAssert(NO, @"Unexpected Alice transation state: %@", @(sasTransactionFromAlicePOV.state)); - break; - } - }]; - - // -> Transaction on Bob side must be WaitForPartnerKey, then ShowSAS - [self observeTransactionUpdate:transactionFromAlice2POV block:^{ - - switch (transactionFromAlice2POV.state) - { - // -> 1. Transaction on Bob side must be WaitForPartnerKey (Alice is WaitForPartnerToAccept) - case MXSASTransactionStateWaitForPartnerKey: - XCTAssertEqual(sasTransactionFromAlicePOV.state, MXSASTransactionStateOutgoingWaitForPartnerToAccept); - break; - // -> 3. Transaction on Bob side must then move to ShowSAS - case MXSASTransactionStateShowSAS: - break; - case MXSASTransactionStateWaitForPartnerToConfirm: - break; - // 7. Transaction on Bob side must then move to Verified - case MXSASTransactionStateVerified: - checkBothVerified(); - break; - default: - XCTAssert(NO, @"Unexpected Bob transation state: %@", @(sasTransactionFromAlicePOV.state)); - break; - } - }]; - }]; - }]; - }]; - - }]; -} - -#pragma mark - Verification of others (by DM) - - -/** - Nomical case: The full flow - It reuses code from testVerificationByDMFullFlow from MXCryptoKeyVerificationTests. - - - Alice and Bob are in a room - - Alice and Bob bootstrap cross-signing (This is the single difference with original testVerificationByDMFullFlow). - - Bob requests a verification of Alice in this Room - - Alice gets the request in the timeline - - Alice accepts it and begins a SAS verification - -> 1. Transaction on Bob side must be WaitForPartnerKey (Alice is WaitForPartnerToAccept) - -> 2. Transaction on Alice side must then move to WaitForPartnerKey - -> 3. Transaction on Bob side must then move to ShowSAS - -> 4. Transaction on Alice side must then move to ShowSAS - -> 5. SASs must be the same - - Alice confirms SAS - -> 6. Transaction on Alice side must then move to WaitForPartnerToConfirm - - Bob confirms SAS - -> 7. Transaction on Bob side must then move to Verified - -> 7. Transaction on Alice side must then move to Verified - -> Devices must be really verified - -> Users must be really verified - -> Transaction must not be listed anymore - -> Both ends must get a done message - - Then, test MXKeyVerification - */ -- (void)testVerificationByDMFullFlow -{ - // - Alice and Bob are in a room - [matrixSDKTestsE2EData doE2ETestWithAliceAndBobInARoom:self cryptedBob:YES warnOnUnknowDevices:YES aliceStore:[[MXMemoryStore alloc] init] bobStore:[[MXMemoryStore alloc] init] readyToTest:^(MXSession *aliceSession, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation) { - - // - Alice and Bob bootstrap cross-signing - [self bootstrapCrossSigningOnSession:aliceSession password:MXTESTS_ALICE_PWD completion:^{ - [self bootstrapCrossSigningOnSession:bobSession password:MXTESTS_BOB_PWD completion:^{ - - NSString *fallbackText = @"fallbackText"; - __block NSString *requestId; - - MXCredentials *alice = aliceSession.matrixRestClient.credentials; - MXCredentials *bob = bobSession.matrixRestClient.credentials; - - // - Bob requests a verification of Alice in this Room - [bobSession.crypto.keyVerificationManager requestVerificationByDMWithUserId:alice.userId - roomId:roomId - fallbackText:fallbackText - methods:@[MXKeyVerificationMethodSAS, @"toto"] - success:^(id request) - { - requestId = request.requestId; - } - failure:^(NSError * _Nonnull error) - { - XCTFail(@"The request should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - - - __block MXOutgoingSASTransaction *sasTransactionFromAlicePOV; - - - // Alice gets the request in the timeline - [aliceSession listenToEventsOfTypes:@[kMXEventTypeStringRoomMessage] - onEvent:^(MXEvent *event, MXTimelineDirection direction, id customObject) - { - if ([event.content[kMXMessageTypeKey] isEqualToString:kMXMessageTypeKeyVerificationRequest]) - { - XCTAssertEqualObjects(event.eventId, requestId); - - // Check verification by DM request format - MXKeyVerificationRequestByDMJSONModel *requestJSON; - MXJSONModelSetMXJSONModel(requestJSON, MXKeyVerificationRequestByDMJSONModel.class, event.content); - XCTAssertNotNil(requestJSON); - - // - Alice accepts it and begins a SAS verification - - // Wait a bit - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{ - // - Alice rejects the incoming request - id requestFromAlicePOV = aliceSession.crypto.keyVerificationManager.pendingRequests.firstObject; - XCTAssertNotNil(requestFromAlicePOV); - - [requestFromAlicePOV acceptWithMethods:@[MXKeyVerificationMethodSAS] success:^{ - - [aliceSession.crypto.keyVerificationManager beginKeyVerificationFromRequest:requestFromAlicePOV method:MXKeyVerificationMethodSAS success:^(id _Nonnull transactionFromAlicePOV) { - - XCTAssertEqualObjects(transactionFromAlicePOV.transactionId, event.eventId); - - XCTAssert(transactionFromAlicePOV); - XCTAssertTrue([transactionFromAlicePOV isKindOfClass:MXOutgoingSASTransaction.class]); - sasTransactionFromAlicePOV = (MXOutgoingSASTransaction*)transactionFromAlicePOV; - - } failure:^(NSError * _Nonnull error) { - XCTFail(@"The request should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - - } failure:^(NSError * _Nonnull error) { - XCTFail(@"The request should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - }); - } - }]; - - - [self observeSASIncomingTransactionInSession:bobSession block:^(MXIncomingSASTransaction * _Nullable transactionFromBobPOV) { - - // Final checks - void (^checkBothDeviceVerified)(void) = ^ void () - { - if (sasTransactionFromAlicePOV.state == MXSASTransactionStateVerified - && transactionFromBobPOV.state == MXSASTransactionStateVerified) - { - // -> Devices must be really verified - MXDeviceInfo *bobDeviceFromAlicePOV = [aliceSession.legacyCrypto.store deviceWithDeviceId:bob.deviceId forUser:bob.userId]; - MXDeviceInfo *aliceDeviceFromBobPOV = [bobSession.legacyCrypto.store deviceWithDeviceId:alice.deviceId forUser:alice.userId]; - - XCTAssertEqual(bobDeviceFromAlicePOV.trustLevel.localVerificationStatus, MXDeviceVerified); - XCTAssertTrue(bobDeviceFromAlicePOV.trustLevel.isCrossSigningVerified); - XCTAssertEqual(aliceDeviceFromBobPOV.trustLevel.localVerificationStatus, MXDeviceVerified); - XCTAssertTrue(aliceDeviceFromBobPOV.trustLevel.isCrossSigningVerified); - - // -> Users must be really verified - MXCrossSigningInfo *bobFromAlicePOV = [aliceSession.legacyCrypto.store crossSigningKeysForUser:bob.userId]; - MXCrossSigningInfo *aliceFromBobPOV = [bobSession.legacyCrypto.store crossSigningKeysForUser:alice.userId]; - - XCTAssertTrue(bobFromAlicePOV.trustLevel.isCrossSigningVerified); - XCTAssertTrue(bobFromAlicePOV.trustLevel.isLocallyVerified); - XCTAssertTrue(aliceFromBobPOV.trustLevel.isCrossSigningVerified); - XCTAssertTrue(aliceFromBobPOV.trustLevel.isLocallyVerified); - - // -> Transaction must not be listed anymore - XCTAssertNil([(MXLegacyKeyVerificationManager *)aliceSession.crypto.keyVerificationManager transactionWithTransactionId:sasTransactionFromAlicePOV.transactionId]); - XCTAssertNil([(MXLegacyKeyVerificationManager *)bobSession.crypto.keyVerificationManager transactionWithTransactionId:transactionFromBobPOV.transactionId]); - } - }; - - // -> Transaction on Alice side must be WaitForPartnerKey, then ShowSAS - [self observeTransactionUpdate:sasTransactionFromAlicePOV block:^{ - - switch (sasTransactionFromAlicePOV.state) - { - // -> 2. Transaction on Alice side must then move to WaitForPartnerKey - case MXSASTransactionStateWaitForPartnerKey: - XCTAssertEqual(transactionFromBobPOV.state, MXSASTransactionStateWaitForPartnerKey); - break; - // -> 4. Transaction on Alice side must then move to ShowSAS - case MXSASTransactionStateShowSAS: - XCTAssertEqual(transactionFromBobPOV.state, MXSASTransactionStateShowSAS); - - // -> 5. SASs must be the same - XCTAssertEqualObjects(sasTransactionFromAlicePOV.sasBytes, transactionFromBobPOV.sasBytes); - XCTAssertEqualObjects(sasTransactionFromAlicePOV.sasDecimal, transactionFromBobPOV.sasDecimal); - XCTAssertEqualObjects(sasTransactionFromAlicePOV.sasEmoji, transactionFromBobPOV.sasEmoji); - - // - Alice confirms SAS - [sasTransactionFromAlicePOV confirmSASMatch]; - break; - // -> 6. Transaction on Alice side must then move to WaitForPartnerToConfirm - case MXSASTransactionStateWaitForPartnerToConfirm: - // - Bob confirms SAS - [transactionFromBobPOV confirmSASMatch]; - break; - // -> 7. Transaction on Alice side must then move to Verified - case MXSASTransactionStateVerified: - checkBothDeviceVerified(); - break; - default: - XCTAssert(NO, @"Unexpected Alice transation state: %@", @(sasTransactionFromAlicePOV.state)); - break; - } - }]; - - // -> Transaction on Bob side must be WaitForPartnerKey, then ShowSAS - [self observeTransactionUpdate:transactionFromBobPOV block:^{ - - switch (transactionFromBobPOV.state) - { - // -> 1. Transaction on Bob side must be WaitForPartnerKey (Alice is WaitForPartnerToAccept) - case MXSASTransactionStateWaitForPartnerKey: - XCTAssertEqual(sasTransactionFromAlicePOV.state, MXSASTransactionStateOutgoingWaitForPartnerToAccept); - break; - // -> 3. Transaction on Bob side must then move to ShowSAS - case MXSASTransactionStateShowSAS: - break; - case MXSASTransactionStateWaitForPartnerToConfirm: - break; - // 7. Transaction on Bob side must then move to Verified - case MXSASTransactionStateVerified: - checkBothDeviceVerified(); - break; - default: - XCTAssert(NO, @"Unexpected Bob transation state: %@", @(sasTransactionFromAlicePOV.state)); - break; - } - }]; - }]; - - // -> Both ends must get a done message - NSMutableArray *doneDone = [NSMutableArray new]; - void (^checkDoneDone)(MXEvent *event, MXTimelineDirection direction, id customObject) = ^ void (MXEvent *event, MXTimelineDirection direction, id customObject) - { - XCTAssertEqual(event.eventType, MXEventTypeKeyVerificationDone); - - // Check done format - MXKeyVerificationDone *done; - MXJSONModelSetMXJSONModel(done, MXKeyVerificationDone.class, event.content); - XCTAssertNotNil(done); - - [doneDone addObject:done]; - if (doneDone.count == 4) - { - // Then, test MXKeyVerification - MXEvent *event = [aliceSession.store eventWithEventId:requestId inRoom:roomId]; - [aliceSession.crypto.keyVerificationManager keyVerificationFromKeyVerificationEvent:event roomId:roomId success:^(MXKeyVerification * _Nonnull verificationFromAlicePOV) { - - XCTAssertEqual(verificationFromAlicePOV.state, MXKeyVerificationStateVerified); - - [expectation fulfill]; - } failure:^(NSError * _Nonnull error) { - XCTFail(@"The request should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - } - }; - - [aliceSession listenToEventsOfTypes:@[kMXEventTypeStringKeyVerificationDone] - onEvent:checkDoneDone]; - [bobSession listenToEventsOfTypes:@[kMXEventTypeStringKeyVerificationDone] - onEvent:checkDoneDone]; - }]; - }]; - }]; -} - -/** - Verify another user by QR code - It reuses code from testVerificationByDMFullFlow from MXCryptoKeyVerificationTests. - - - Alice and Bob are in a room - - Alice and Bob bootstrap cross-signing (This is the single difference with original testVerificationByDMFullFlow). - - Bob requests a verification of Alice in this Room - - Alice gets the request in the timeline - - Alice accepts it and wait for Bob to scan her QR code - -> 1. Transaction on Bob side must be Unknown - -> 2. Transaction on Alice side must be Unknown - - Bob scans Alice QR code - -> 3. Transaction on Bob side must then move to ScannedOtherQR - -> 4. Transaction on Bob side must then move to WaitingOtherConfirm, if the start request succeed - -> 5. Transaction on Alice side must then move to QRScannedByOther - - Alice confirms that Bob has scanned her QR code - -> 6. Transaction on Alice side must then move to Verified - -> 7. Transaction on Bob side must then move to Verified - -> Users must be verified - -> Transaction must not be listed anymore - -> Both ends must get a done message - - Then, test MXKeyVerification - */ -- (void)testVerifyingAnotherUserQRCodeVerificationFullFlow -{ - // - Alice and Bob are in a room - [matrixSDKTestsE2EData doE2ETestWithAliceAndBobInARoom:self cryptedBob:YES warnOnUnknowDevices:YES aliceStore:[[MXMemoryStore alloc] init] bobStore:[[MXMemoryStore alloc] init] readyToTest:^(MXSession *aliceSession, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation) { - - // - Alice and Bob bootstrap cross-signing - [self bootstrapCrossSigningOnSession:aliceSession password:MXTESTS_ALICE_PWD completion:^{ - [self bootstrapCrossSigningOnSession:bobSession password:MXTESTS_BOB_PWD completion:^{ - - NSString *fallbackText = @"fallbackText"; - __block NSString *requestId; - - __block MXQRCodeData *aliceQRCodeData; - - MXCredentials *alice = aliceSession.matrixRestClient.credentials; - MXCredentials *bob = bobSession.matrixRestClient.credentials; - - // - Bob requests a verification of Alice in this Room - [bobSession.crypto.keyVerificationManager requestVerificationByDMWithUserId:alice.userId - roomId:roomId - fallbackText:fallbackText - methods:@[MXKeyVerificationMethodQRCodeShow, MXKeyVerificationMethodQRCodeScan, MXKeyVerificationMethodReciprocate] - success:^(id request) - { - requestId = request.requestId; - } - failure:^(NSError * _Nonnull error) - { - XCTFail(@"The request should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - - __block MXLegacyQRCodeTransaction *qrCodeTransactionFromAlicePOV; - - // Alice gets the request in the timeline - [aliceSession listenToEventsOfTypes:@[kMXEventTypeStringRoomMessage] - onEvent:^(MXEvent *event, MXTimelineDirection direction, id customObject) - { - if ([event.content[kMXMessageTypeKey] isEqualToString:kMXMessageTypeKeyVerificationRequest]) - { - XCTAssertEqualObjects(event.eventId, requestId); - - // Check verification by DM request format - MXKeyVerificationRequestByDMJSONModel *requestJSON; - MXJSONModelSetMXJSONModel(requestJSON, MXKeyVerificationRequestByDMJSONModel.class, event.content); - XCTAssertNotNil(requestJSON); - - // - Alice accepts it and creates a QR code transaction - [self observeKeyVerificationRequestInSession:aliceSession block:^(id _Nullable request) { - - // - Alice accepts the incoming request - id requestFromAlicePOV = aliceSession.crypto.keyVerificationManager.pendingRequests.firstObject; - XCTAssertNotNil(requestFromAlicePOV); - - [requestFromAlicePOV acceptWithMethods:@[MXKeyVerificationMethodQRCodeShow, MXKeyVerificationMethodReciprocate] success:^{ - - qrCodeTransactionFromAlicePOV = [aliceSession.crypto.keyVerificationManager qrCodeTransactionWithTransactionId:requestFromAlicePOV.requestId]; - - aliceQRCodeData = qrCodeTransactionFromAlicePOV.qrCodeData; - - XCTAssertEqual(requestFromAlicePOV.state, MXKeyVerificationRequestStateReady); - XCTAssertNotNil(aliceQRCodeData); - XCTAssertEqual(aliceQRCodeData.verificationMode, MXQRCodeVerificationModeVerifyingAnotherUser); - - } failure:^(NSError * _Nonnull error) { - XCTFail(@"The request should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - }]; - } - }]; - - - [self observeNewQRCodeTransactionInSession:bobSession block:^(MXLegacyQRCodeTransaction * _Nullable qrCodeTransactionFromBobPOV) { - - // Final checks - void (^checkBothDeviceVerified)(void) = ^ void () - { - if (qrCodeTransactionFromAlicePOV.state == MXQRCodeTransactionStateVerified - && qrCodeTransactionFromBobPOV.state == MXQRCodeTransactionStateVerified) - { - // -> Devices must be really verified - MXDeviceInfo *bobDeviceFromAlicePOV = [aliceSession.legacyCrypto.store deviceWithDeviceId:bob.deviceId forUser:bob.userId]; - MXDeviceInfo *aliceDeviceFromBobPOV = [bobSession.legacyCrypto.store deviceWithDeviceId:alice.deviceId forUser:alice.userId]; - - XCTAssertEqual(bobDeviceFromAlicePOV.trustLevel.localVerificationStatus, MXDeviceVerified); - XCTAssertTrue(bobDeviceFromAlicePOV.trustLevel.isCrossSigningVerified); - XCTAssertEqual(aliceDeviceFromBobPOV.trustLevel.localVerificationStatus, MXDeviceVerified); - XCTAssertTrue(aliceDeviceFromBobPOV.trustLevel.isCrossSigningVerified); - - // -> Users must be really verified - MXCrossSigningInfo *bobFromAlicePOV = [aliceSession.legacyCrypto.store crossSigningKeysForUser:bob.userId]; - MXCrossSigningInfo *aliceFromBobPOV = [bobSession.legacyCrypto.store crossSigningKeysForUser:alice.userId]; - - XCTAssertTrue(bobFromAlicePOV.trustLevel.isCrossSigningVerified); - XCTAssertTrue(bobFromAlicePOV.trustLevel.isLocallyVerified); - XCTAssertTrue(aliceFromBobPOV.trustLevel.isCrossSigningVerified); - XCTAssertTrue(aliceFromBobPOV.trustLevel.isLocallyVerified); - - // -> Transaction must not be listed anymore - XCTAssertNil([(MXLegacyKeyVerificationManager *)aliceSession.crypto.keyVerificationManager transactionWithTransactionId:qrCodeTransactionFromAlicePOV.transactionId]); - XCTAssertNil([(MXLegacyKeyVerificationManager *)bobSession.crypto.keyVerificationManager transactionWithTransactionId:qrCodeTransactionFromBobPOV.transactionId]); - } - }; - - // -> Transaction on Alice side must be Unknown, then QRScannedByOther - [self observeTransactionUpdate:qrCodeTransactionFromAlicePOV block:^{ - - switch (qrCodeTransactionFromAlicePOV.state) - { - // -> 2. Transaction on Alice side must be Unknown - case MXQRCodeTransactionStateUnknown: - XCTAssertEqual(qrCodeTransactionFromBobPOV.state, MXQRCodeTransactionStateUnknown); - break; - // -> 5. Transaction on Alice side must then move to QRScannedByOther - case MXQRCodeTransactionStateQRScannedByOther: - XCTAssertEqual(qrCodeTransactionFromBobPOV.state, MXQRCodeTransactionStateWaitingOtherConfirm); - - // Alice confirms that Bob has scanned her QR code - [qrCodeTransactionFromAlicePOV otherUserScannedMyQrCode:YES]; - break; - // -> 6. Transaction on Alice side must then move to Verified - case MXQRCodeTransactionStateVerified: - checkBothDeviceVerified(); - break; - default: - XCTAssert(NO, @"Unexpected Alice transation state: %@", @(qrCodeTransactionFromAlicePOV.state)); - break; - } - }]; - - // -> Transaction on Bob side must be Unknown, then ScannedOtherQR - [self observeTransactionUpdate:qrCodeTransactionFromBobPOV block:^{ - - switch (qrCodeTransactionFromBobPOV.state) - { - // -> 1. Transaction on Bob side must be Unknown (Alice is Unknown) - case MXQRCodeTransactionStateUnknown: - XCTAssertEqual(qrCodeTransactionFromAlicePOV.state, MXQRCodeTransactionStateUnknown); - break; - // -> 3. Transaction on Bob side must then move to ScannedOtherQR - case MXQRCodeTransactionStateScannedOtherQR: - XCTAssertEqual(qrCodeTransactionFromAlicePOV.state, MXQRCodeTransactionStateUnknown); - break; - // -> 4. Transaction on Bob side must then move to WaitingOtherConfirm - case MXQRCodeTransactionStateWaitingOtherConfirm: - XCTAssertEqual(qrCodeTransactionFromAlicePOV.state, MXQRCodeTransactionStateUnknown); - break; - // -> 7. Transaction on Bob side must then move to Verified - case MXQRCodeTransactionStateVerified: - checkBothDeviceVerified(); - break; - default: - XCTAssert(NO, @"Unexpected Bob transation state: %@", @(qrCodeTransactionFromAlicePOV.state)); - break; - } - }]; - }]; - - // -> Both ends must get a done message - NSMutableArray *doneDone = [NSMutableArray new]; - void (^checkDoneDone)(MXEvent *event, MXTimelineDirection direction, id customObject) = ^ void (MXEvent *event, MXTimelineDirection direction, id customObject) - { - XCTAssertEqual(event.eventType, MXEventTypeKeyVerificationDone); - - // Check done format - MXKeyVerificationDone *done; - MXJSONModelSetMXJSONModel(done, MXKeyVerificationDone.class, event.content); - XCTAssertNotNil(done); - - [doneDone addObject:done]; - if (doneDone.count == 4) - { - // Then, to test MXKeyVerification - MXEvent *event = [aliceSession.store eventWithEventId:requestId inRoom:roomId]; - [aliceSession.crypto.keyVerificationManager keyVerificationFromKeyVerificationEvent:event roomId:roomId success:^(MXKeyVerification * _Nonnull verificationFromAlicePOV) { - - XCTAssertEqual(verificationFromAlicePOV.state, MXKeyVerificationStateVerified); - - [expectation fulfill]; - } failure:^(NSError * _Nonnull error) { - XCTFail(@"The request should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - } - }; - - [aliceSession listenToEventsOfTypes:@[kMXEventTypeStringKeyVerificationDone] - onEvent:checkDoneDone]; - [bobSession listenToEventsOfTypes:@[kMXEventTypeStringKeyVerificationDone] - onEvent:checkDoneDone]; - - // -> Bob gets the requests notification - [self observeKeyVerificationRequestChangeWithBlock:^(id _Nullable request) { - - if (!request.isFromMyUser) - { - return; - } - - XCTAssertEqualObjects(request.requestId, requestId); - XCTAssertTrue(request.isFromMyUser); - - id requestFromAlicePOV = aliceSession.crypto.keyVerificationManager.pendingRequests.firstObject; - id requestFromBobPOV = bobSession.crypto.keyVerificationManager.pendingRequests.firstObject; - - XCTAssertNotNil(requestFromAlicePOV); - XCTAssertEqual(requestFromAlicePOV.transport, MXKeyVerificationTransportDirectMessage); - XCTAssertNotNil(requestFromBobPOV); - XCTAssertEqual(requestFromBobPOV.transport, MXKeyVerificationTransportDirectMessage); - - switch (request.state) - { - case MXKeyVerificationRequestStateReady: - { - MXLegacyQRCodeTransaction *qrCodeTransactionFromBobPOV = [bobSession.crypto.keyVerificationManager qrCodeTransactionWithTransactionId:request.requestId]; - XCTAssertNotNil(qrCodeTransactionFromBobPOV); - XCTAssertNil(qrCodeTransactionFromBobPOV.qrCodeData); // Bob cannot show QR code - - // Bob scan Alice QR code - [qrCodeTransactionFromBobPOV userHasScannedOtherQrCodeData:aliceQRCodeData]; - } - break; - case MXKeyVerificationRequestStateCancelled: - XCTFail(@"The request should not be cancel - Cancel code: %@", request.reasonCancelCode); - break; - default: - break; - } - }]; - }]; - }]; - }]; -} - -@end - -#pragma clang diagnostic pop diff --git a/MatrixSDKTests/MXCryptoKeyVerificationTests.m b/MatrixSDKTests/MXCryptoKeyVerificationTests.m index c803b60442..7fe2988b48 100644 --- a/MatrixSDKTests/MXCryptoKeyVerificationTests.m +++ b/MatrixSDKTests/MXCryptoKeyVerificationTests.m @@ -19,12 +19,9 @@ #import "MatrixSDKTestsData.h" #import "MatrixSDKTestsE2EData.h" -#import "MXCrypto_Private.h" -#import "MXKeyVerificationManager_Private.h" #import "MXFileStore.h" #import "MXKeyVerificationRequestByDMJSONModel.h" -#import "MXKeyVerificationByToDeviceRequest.h" #import "MatrixSDKTestsSwiftHeader.h" // Do not bother with retain cycles warnings in tests @@ -32,12 +29,6 @@ #pragma clang diagnostic ignored "-Warc-retain-cycles" #pragma clang diagnostic ignored "-Wdeprecated" -@interface MXLegacyKeyVerificationManager (Testing) - -- (id)transactionWithTransactionId:(NSString*)transactionId; - -@end - @interface MXCryptoKeyVerificationTests : XCTestCase { MatrixSDKTestsData *matrixSDKTestsData; @@ -72,24 +63,6 @@ - (void)tearDown [super tearDown]; } -- (void)observeSASIncomingTransactionInSession:(MXSession*)session block:(void (^)(MXIncomingSASTransaction * _Nullable transaction))block -{ - id observer = [[NSNotificationCenter defaultCenter] addObserverForName:MXKeyVerificationManagerNewTransactionNotification object:session.crypto.keyVerificationManager queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { - - id transaction = notif.userInfo[MXKeyVerificationManagerNotificationTransactionKey]; - if (transaction.isIncoming && [transaction isKindOfClass:MXIncomingSASTransaction.class]) - { - block((MXIncomingSASTransaction*)transaction); - } - else - { - XCTFail(@"We support only SAS. transaction: %@", transaction); - } - }]; - - [observers addObject:observer]; -} - - (void)observeTransactionUpdate:(id)transaction block:(void (^)(void))block { id observer = [[NSNotificationCenter defaultCenter] addObserverForName:MXKeyVerificationTransactionDidChangeNotification object:transaction queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { @@ -179,338 +152,6 @@ - (void)testVerificationByToDeviceRequests }]; } -/** - Nomical case: The full flow - - - Alice and Bob are in a room - - Bob requests a verification of Alice in this Room - - Alice gets the requests notification - - Alice accepts it - - Alice begins a SAS verification - -> 1. Transaction on Bob side must be WaitForPartnerKey (Alice is WaitForPartnerToAccept) - -> 2. Transaction on Alice side must then move to WaitForPartnerKey - -> 3. Transaction on Bob side must then move to ShowSAS - -> 4. Transaction on Alice side must then move to ShowSAS - -> 5. SASs must be the same - - Alice confirms SAS - -> 6. Transaction on Alice side must then move to WaitForPartnerToConfirm - - Bob confirms SAS - -> 7. Transaction on Bob side must then move to Verified - -> 7. Transaction on Alice side must then move to Verified - -> Devices must be really verified - -> Transaction must not be listed anymore - */ -- (void)checkVerificationByToDeviceFullFlowWithBobSession:(MXSession*)bobSession aliceSession:(MXSession*)aliceSession roomId:(NSString*)roomId expectation:(XCTestExpectation*)expectation -{ - __block id requestFromBobPOV; - __block id requestFromAlicePOV; - __block MXOutgoingSASTransaction *sasTransactionFromAlicePOV; - - MXCredentials *alice = aliceSession.matrixRestClient.credentials; - MXCredentials *bob = bobSession.matrixRestClient.credentials; - - NSArray *methods = @[MXKeyVerificationMethodSAS, @"toto"]; - - void (^requestCompletionBlock)(void) = ^{ - // Wait until both parties have recieved the request - if (!requestFromBobPOV || !requestFromAlicePOV) { - return; - } - - XCTAssertNotNil(requestFromBobPOV.requestId); - XCTAssertNotNil(requestFromAlicePOV.requestId); - XCTAssertEqualObjects(requestFromBobPOV.requestId, requestFromAlicePOV.requestId); - - // - Alice accepts it - [requestFromAlicePOV acceptWithMethods:@[MXKeyVerificationMethodSAS] success:^{ - - id requestFromAlicePOV2 = aliceSession.crypto.keyVerificationManager.pendingRequests.firstObject; - XCTAssertNotNil(requestFromAlicePOV2); - XCTAssertEqualObjects(requestFromAlicePOV2.myMethods, @[MXKeyVerificationMethodSAS]); - - // - Alice begins a SAS verification - [aliceSession.crypto.keyVerificationManager beginKeyVerificationFromRequest:requestFromAlicePOV2 method:MXKeyVerificationMethodSAS success:^(id _Nonnull transactionFromAlicePOV) { - - XCTAssertEqualObjects(transactionFromAlicePOV.transactionId, requestFromAlicePOV.requestId); - - XCTAssert(transactionFromAlicePOV); - XCTAssertTrue([transactionFromAlicePOV isKindOfClass:MXOutgoingSASTransaction.class]); - sasTransactionFromAlicePOV = (MXOutgoingSASTransaction*)transactionFromAlicePOV; - - } failure:^(NSError * _Nonnull error) { - XCTFail(@"The request should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - - } failure:^(NSError * _Nonnull error) { - XCTFail(@"The request should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - }; - - // - Bob requests a verification of Alice in this Room - [bobSession.crypto.keyVerificationManager requestVerificationByToDeviceWithUserId:alice.userId - deviceIds:@[alice.deviceId] - methods:@[MXKeyVerificationMethodSAS, @"toto"] - success:^(id request) - { - XCTAssertNotNil(request); - XCTAssertNotNil(request.requestId); - XCTAssertEqualObjects(request.otherUser, alice.userId); - XCTAssertNil(request.otherDevice); - - requestFromBobPOV = request; - requestCompletionBlock(); - } - failure:^(NSError * _Nonnull error) - { - XCTFail(@"The request should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - - // - Alice gets the requests notification - [self observeKeyVerificationRequestInSession:aliceSession block:^(id _Nullable request) { - XCTAssertNotNil(request); - XCTAssertNotNil(request.requestId); - XCTAssertEqualObjects(request.methods, methods); - XCTAssertEqualObjects(request.otherMethods, methods); - XCTAssertNil(request.myMethods); - - XCTAssertEqualObjects(request.otherUser, bob.userId); - XCTAssertEqualObjects(request.otherDevice, bob.deviceId); - - requestFromAlicePOV = request; - requestCompletionBlock(); - }]; - - - [self observeSASIncomingTransactionInSession:bobSession block:^(MXIncomingSASTransaction * _Nullable transactionFromBobPOV) { - - // Final checks - void (^checkBothDeviceVerified)(void) = ^ void () - { - if (sasTransactionFromAlicePOV.state == MXSASTransactionStateVerified - && transactionFromBobPOV.state == MXSASTransactionStateVerified) - { - // -> Devices must be really verified - MXDeviceInfo *bobDeviceFromAlicePOV = [aliceSession.legacyCrypto.store deviceWithDeviceId:bob.deviceId forUser:bob.userId]; - MXDeviceInfo *aliceDeviceFromBobPOV = [bobSession.legacyCrypto.store deviceWithDeviceId:alice.deviceId forUser:alice.userId]; - - XCTAssertEqual(bobDeviceFromAlicePOV.trustLevel.localVerificationStatus, MXDeviceVerified); - XCTAssertEqual(aliceDeviceFromBobPOV.trustLevel.localVerificationStatus, MXDeviceVerified); - - // -> Transaction must not be listed anymore - XCTAssertNil([(MXLegacyKeyVerificationManager *)aliceSession.crypto.keyVerificationManager transactionWithTransactionId:sasTransactionFromAlicePOV.transactionId]); - XCTAssertNil([(MXLegacyKeyVerificationManager *)bobSession.crypto.keyVerificationManager transactionWithTransactionId:transactionFromBobPOV.transactionId]); - - [expectation fulfill]; - } - }; - - // -> Transaction on Alice side must be WaitForPartnerKey, then ShowSAS - [self observeTransactionUpdate:sasTransactionFromAlicePOV block:^{ - - switch (sasTransactionFromAlicePOV.state) - { - // -> 2. Transaction on Alice side must then move to WaitForPartnerKey - case MXSASTransactionStateWaitForPartnerKey: - XCTAssertEqual(transactionFromBobPOV.state, MXSASTransactionStateWaitForPartnerKey); - break; - // -> 4. Transaction on Alice side must then move to ShowSAS - case MXSASTransactionStateShowSAS: - XCTAssertEqual(transactionFromBobPOV.state, MXSASTransactionStateShowSAS); - - // -> 5. SASs must be the same - XCTAssertEqualObjects(sasTransactionFromAlicePOV.sasBytes, transactionFromBobPOV.sasBytes); - XCTAssertEqualObjects(sasTransactionFromAlicePOV.sasDecimal, transactionFromBobPOV.sasDecimal); - XCTAssertEqualObjects(sasTransactionFromAlicePOV.sasEmoji, transactionFromBobPOV.sasEmoji); - - // - Alice confirms SAS - [sasTransactionFromAlicePOV confirmSASMatch]; - break; - // -> 6. Transaction on Alice side must then move to WaitForPartnerToConfirm - case MXSASTransactionStateWaitForPartnerToConfirm: - // - Bob confirms SAS - [transactionFromBobPOV confirmSASMatch]; - break; - // -> 7. Transaction on Alice side must then move to Verified - case MXSASTransactionStateVerified: - checkBothDeviceVerified(); - break; - default: - XCTAssert(NO, @"Unexpected Alice transation state: %@", @(sasTransactionFromAlicePOV.state)); - break; - } - }]; - - // -> Transaction on Bob side must be WaitForPartnerKey, then ShowSAS - [self observeTransactionUpdate:transactionFromBobPOV block:^{ - - switch (transactionFromBobPOV.state) - { - // -> 1. Transaction on Bob side must be WaitForPartnerKey (Alice is WaitForPartnerToAccept) - case MXSASTransactionStateWaitForPartnerKey: - XCTAssertEqual(sasTransactionFromAlicePOV.state, MXSASTransactionStateOutgoingWaitForPartnerToAccept); - break; - // -> 3. Transaction on Bob side must then move to ShowSAS - case MXSASTransactionStateShowSAS: - break; - case MXSASTransactionStateWaitForPartnerToConfirm: - break; - // 7. Transaction on Bob side must then move to Verified - case MXSASTransactionStateVerified: - checkBothDeviceVerified(); - break; - default: - XCTAssert(NO, @"Unexpected Bob transation state: %@", @(sasTransactionFromAlicePOV.state)); - break; - } - }]; - }]; -} - -- (void)testVerificationByToDeviceFullFlow -{ - // - Alice and Bob are in a room - [matrixSDKTestsE2EData doE2ETestWithAliceAndBobInARoom:self cryptedBob:YES warnOnUnknowDevices:YES aliceStore:[[MXMemoryStore alloc] init] bobStore:[[MXMemoryStore alloc] init] readyToTest:^(MXSession *aliceSession, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation) { - - [self checkVerificationByToDeviceFullFlowWithBobSession:bobSession aliceSession:aliceSession roomId:roomId expectation:expectation]; - }]; -} - -/** - Same tests as testVerificationByToDeviceFullFlow but with alice with 2 sessions - */ -- (void)testVerificationByToDeviceFullFlowWithAliceWith2Devices -{ - // - Alice and Bob are in a room - [matrixSDKTestsE2EData doE2ETestWithAliceAndBobInARoom:self cryptedBob:YES warnOnUnknowDevices:YES aliceStore:[[MXMemoryStore alloc] init] bobStore:[[MXMemoryStore alloc] init] readyToTest:^(MXSession *aliceSession, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation) { - - [matrixSDKTestsE2EData loginUserOnANewDevice:self credentials:aliceSession.matrixRestClient.credentials withPassword:MXTESTS_ALICE_PWD onComplete:^(MXSession *newAliceSession) { - - [self checkVerificationByToDeviceFullFlowWithBobSession:bobSession aliceSession:aliceSession roomId:roomId expectation:expectation]; - }]; - }]; -} - -/** - Same tests as testVerificationByToDeviceFullFlow but with bob with 2 sessions - */ -- (void)testVerificationByToDeviceFullFlowWith2Devices -{ - // - Alice and Bob are in a room - [matrixSDKTestsE2EData doE2ETestWithAliceAndBobInARoom:self cryptedBob:YES warnOnUnknowDevices:YES aliceStore:[[MXMemoryStore alloc] init] bobStore:[[MXMemoryStore alloc] init] readyToTest:^(MXSession *aliceSession, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation) { - - [matrixSDKTestsE2EData loginUserOnANewDevice:self credentials:bobSession.matrixRestClient.credentials withPassword:MXTESTS_BOB_PWD onComplete:^(MXSession *newBobSession) { - - [self checkVerificationByToDeviceFullFlowWithBobSession:bobSession aliceSession:aliceSession roomId:roomId expectation:expectation]; - }]; - }]; -} - -/** - Same tests as testVerificationByToDeviceFullFlow but with only alice verifying her 2 devices. - */ -- (void)testVerificationByToDeviceSelfVerificationFullFlow -{ - // - Alice and Bob are in a room - [matrixSDKTestsE2EData doE2ETestWithAliceAndBobInARoom:self cryptedBob:YES warnOnUnknowDevices:YES aliceStore:[[MXMemoryStore alloc] init] bobStore:[[MXMemoryStore alloc] init] readyToTest:^(MXSession *aliceSession, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation) { - - [matrixSDKTestsE2EData loginUserOnANewDevice:self credentials:aliceSession.matrixRestClient.credentials withPassword:MXTESTS_ALICE_PWD onComplete:^(MXSession *newAliceSession) { - - [self checkVerificationByToDeviceFullFlowWithBobSession:aliceSession aliceSession:newAliceSession roomId:roomId expectation:expectation]; - }]; - }]; -} - - -/** - Test self verification request cancellation with 3 devices. - - Have Alice with 3 sessions - - Alice sends a self verification request to her all other devices - -> The other device list should have been computed well - - Alice cancels it from device #1 - -> All other devices should get the cancellation - */ -- (void)testVerificationByToDeviceRequestCancellation -{ - // - Have Alice with 3 sessions - [matrixSDKTestsE2EData doE2ETestWithAliceAndBobInARoom:self cryptedBob:YES warnOnUnknowDevices:YES aliceStore:[[MXMemoryStore alloc] init] bobStore:[[MXMemoryStore alloc] init] readyToTest:^(MXSession *aliceSession1, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation) { - - [matrixSDKTestsE2EData loginUserOnANewDevice:self credentials:aliceSession1.matrixRestClient.credentials withPassword:MXTESTS_ALICE_PWD onComplete:^(MXSession *aliceSession2) { - - [matrixSDKTestsE2EData loginUserOnANewDevice:self credentials:aliceSession1.matrixRestClient.credentials withPassword:MXTESTS_ALICE_PWD onComplete:^(MXSession *aliceSession3) { - - NSString *aliceUserId = aliceSession1.matrixRestClient.credentials.userId; - - NSString *aliceSession2DeviceId = aliceSession2.matrixRestClient.credentials.deviceId; - NSString *aliceSession3DeviceId = aliceSession3.matrixRestClient.credentials.deviceId; - - NSArray *methods = @[MXKeyVerificationMethodSAS]; - - // - Alice sends a self verification request to her all other devices - [aliceSession1.crypto.keyVerificationManager requestVerificationByToDeviceWithUserId:aliceUserId - deviceIds:nil - methods:methods - success:^(id requestFromAliceDevice1POV) - { - // -> The other device list should have been computed well - MXKeyVerificationByToDeviceRequest *toDeviceRequestFromAliceDevice1POV = (MXKeyVerificationByToDeviceRequest*)requestFromAliceDevice1POV; - XCTAssertNotNil(toDeviceRequestFromAliceDevice1POV.requestedOtherDeviceIds); - NSSet *expectedRequestedDevices = [NSSet setWithArray:@[aliceSession2DeviceId, aliceSession3DeviceId]]; - NSSet *requestedDevices = [NSSet setWithArray:toDeviceRequestFromAliceDevice1POV.requestedOtherDeviceIds]; - XCTAssertEqualObjects(requestedDevices, expectedRequestedDevices); - - - // - Alice cancels it from device #1 - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{ - - [requestFromAliceDevice1POV cancelWithCancelCode:MXTransactionCancelCode.user success:^{ - - } failure:^(NSError * _Nonnull error) { - XCTFail(@"The request should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - }); - } - failure:^(NSError * _Nonnull error) - { - XCTFail(@"The request should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - - - // -> All other devices should get the cancellation - dispatch_group_t cancelledGroup = dispatch_group_create(); - - dispatch_group_enter(cancelledGroup); - [self observeKeyVerificationRequestInSession:aliceSession2 block:^(id _Nullable requestFromAliceDevice2POV) { - [self observeKeyVerificationRequestUpdate:requestFromAliceDevice2POV block:^{ - if (requestFromAliceDevice2POV.state == MXKeyVerificationRequestStateCancelled) - { - dispatch_group_leave(cancelledGroup); - } - }]; - }]; - - dispatch_group_enter(cancelledGroup); - [self observeKeyVerificationRequestInSession:aliceSession3 block:^(id _Nullable requestFromAliceDevice3POV) { - [self observeKeyVerificationRequestUpdate:requestFromAliceDevice3POV block:^{ - if (requestFromAliceDevice3POV.state == MXKeyVerificationRequestStateCancelled) - { - dispatch_group_leave(cancelledGroup); - } - }]; - }]; - - dispatch_group_notify(cancelledGroup, dispatch_get_main_queue(), ^{ - [expectation fulfill]; - }); - }]; - }]; - }]; -} - /** Test self verification request when no other device. - Have Alice with 1 device @@ -599,253 +240,6 @@ - (void)testVerificationByDMRequests }]; } -/** - Nomical case: The full flow - */ -- (void)testVerificationByDMFullFlow -{ - // - Alice and Bob are in a room - [matrixSDKTestsE2EData doE2ETestWithAliceAndBobInARoom:self cryptedBob:YES warnOnUnknowDevices:YES aliceStore:[[MXMemoryStore alloc] init] bobStore:[[MXMemoryStore alloc] init] readyToTest:^(MXSession *aliceSession, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation) { - - [self checkVerificationByDMFullFlowWithAliceSession:aliceSession bobSession:bobSession roomId:roomId expectation:expectation]; - }]; -} - -/** - It reuses code from testFullFlowWithAliceAndBob. - - - Alice and Bob are in a room - - Bob requests a verification of Alice in this Room - - Alice gets the request in the timeline - - Alice accepts it - - Alice begins a SAS verification - -> 1. Transaction on Bob side must be WaitForPartnerKey (Alice is WaitForPartnerToAccept) - -> 2. Transaction on Alice side must then move to WaitForPartnerKey - -> 3. Transaction on Bob side must then move to ShowSAS - -> 4. Transaction on Alice side must then move to ShowSAS - -> 5. SASs must be the same - - Alice confirms SAS - -> 6. Transaction on Alice side must then move to WaitForPartnerToConfirm - - Bob confirms SAS - -> 7. Transaction on Bob side must then move to Verified - -> 7. Transaction on Alice side must then move to Verified - -> Devices must be really verified - -> Transaction must not be listed anymore - -> Both ends must get a done message - - Then, test MXKeyVerification - */ -- (void)checkVerificationByDMFullFlowWithAliceSession:(MXSession*)aliceSession bobSession:(MXSession*)bobSession roomId:(NSString*)roomId expectation:(XCTestExpectation*)expectation -{ - NSString *fallbackText = @"fallbackText"; - __block id requestFromBobPOV; - __block id requestFromAlicePOV; - __block MXOutgoingSASTransaction *sasTransactionFromAlicePOV; - - MXCredentials *alice = aliceSession.matrixRestClient.credentials; - MXCredentials *bob = bobSession.matrixRestClient.credentials; - - NSArray *methods = @[MXKeyVerificationMethodSAS, @"toto"]; - - void (^requestCompletionBlock)(void) = ^{ - // Wait until both parties have recieved the request - if (!requestFromBobPOV || !requestFromAlicePOV) { - return; - } - - XCTAssertNotNil(requestFromAlicePOV); - - XCTAssertEqualObjects(requestFromAlicePOV.methods, methods); - XCTAssertEqualObjects(requestFromAlicePOV.otherMethods, methods); - XCTAssertNil(requestFromAlicePOV.myMethods); - - XCTAssertEqualObjects(requestFromAlicePOV.otherUser, bob.userId); - XCTAssertEqualObjects(requestFromAlicePOV.otherDevice, bob.deviceId); - - // - Alice accepts it - [requestFromAlicePOV acceptWithMethods:@[MXKeyVerificationMethodSAS] success:^{ - - id requestFromAlicePOV2 = aliceSession.crypto.keyVerificationManager.pendingRequests.firstObject; - XCTAssertNotNil(requestFromAlicePOV2); - XCTAssertEqualObjects(requestFromAlicePOV2.myMethods, @[MXKeyVerificationMethodSAS]); - - // - Alice begins a SAS verification - [aliceSession.crypto.keyVerificationManager beginKeyVerificationFromRequest:requestFromAlicePOV2 method:MXKeyVerificationMethodSAS success:^(id _Nonnull transactionFromAlicePOV) { - - XCTAssertEqualObjects(transactionFromAlicePOV.transactionId, requestFromAlicePOV.requestId); - - XCTAssert(transactionFromAlicePOV); - XCTAssertTrue([transactionFromAlicePOV isKindOfClass:MXOutgoingSASTransaction.class]); - sasTransactionFromAlicePOV = (MXOutgoingSASTransaction*)transactionFromAlicePOV; - - } failure:^(NSError * _Nonnull error) { - XCTFail(@"The request should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - - } failure:^(NSError * _Nonnull error) { - XCTFail(@"The request should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - }; - - // - Bob requests a verification of Alice in this Room - [bobSession.crypto.keyVerificationManager requestVerificationByDMWithUserId:alice.userId - roomId:roomId - fallbackText:fallbackText - methods:methods - success:^(id request) - { - XCTAssertNotNil(request); - XCTAssertNotNil(request.requestId); - XCTAssertEqualObjects(request.otherUser, alice.userId); - XCTAssertNil(request.otherDevice); - - requestFromBobPOV = request; - requestCompletionBlock(); - } - failure:^(NSError * _Nonnull error) - { - XCTFail(@"The request should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - - - // Alice gets the request in the timeline - [aliceSession listenToEventsOfTypes:@[kMXEventTypeStringRoomMessage] - onEvent:^(MXEvent *event, MXTimelineDirection direction, id customObject) - { - if ([event.content[kMXMessageTypeKey] isEqualToString:kMXMessageTypeKeyVerificationRequest]) - { - // Check verification by DM request format - MXKeyVerificationRequestByDMJSONModel *requestJSON; - MXJSONModelSetMXJSONModel(requestJSON, MXKeyVerificationRequestByDMJSONModel.class, event.content); - XCTAssertNotNil(requestJSON); - - [self observeKeyVerificationRequestInSession:aliceSession block:^(id _Nullable request) { - requestFromAlicePOV = request; - requestCompletionBlock(); - }]; - } - }]; - - [self observeSASIncomingTransactionInSession:bobSession block:^(MXIncomingSASTransaction * _Nullable transactionFromBobPOV) { - - // Final checks - void (^checkBothDeviceVerified)(void) = ^ void () - { - if (sasTransactionFromAlicePOV.state == MXSASTransactionStateVerified - && transactionFromBobPOV.state == MXSASTransactionStateVerified) - { - // -> Devices must be really verified - MXDeviceInfo *bobDeviceFromAlicePOV = [aliceSession.legacyCrypto.store deviceWithDeviceId:bob.deviceId forUser:bob.userId]; - MXDeviceInfo *aliceDeviceFromBobPOV = [bobSession.legacyCrypto.store deviceWithDeviceId:alice.deviceId forUser:alice.userId]; - - XCTAssertEqual(bobDeviceFromAlicePOV.trustLevel.localVerificationStatus, MXDeviceVerified); - XCTAssertEqual(aliceDeviceFromBobPOV.trustLevel.localVerificationStatus, MXDeviceVerified); - - // -> Transaction must not be listed anymore - XCTAssertNil([(MXLegacyKeyVerificationManager *)aliceSession.crypto.keyVerificationManager transactionWithTransactionId:sasTransactionFromAlicePOV.transactionId]); - XCTAssertNil([(MXLegacyKeyVerificationManager *)bobSession.crypto.keyVerificationManager transactionWithTransactionId:transactionFromBobPOV.transactionId]); - } - }; - - // -> Transaction on Alice side must be WaitForPartnerKey, then ShowSAS - [self observeTransactionUpdate:sasTransactionFromAlicePOV block:^{ - - switch (sasTransactionFromAlicePOV.state) - { - // -> 2. Transaction on Alice side must then move to WaitForPartnerKey - case MXSASTransactionStateWaitForPartnerKey: - XCTAssertEqual(transactionFromBobPOV.state, MXSASTransactionStateWaitForPartnerKey); - break; - // -> 4. Transaction on Alice side must then move to ShowSAS - case MXSASTransactionStateShowSAS: - XCTAssertEqual(transactionFromBobPOV.state, MXSASTransactionStateShowSAS); - - // -> 5. SASs must be the same - XCTAssertEqualObjects(sasTransactionFromAlicePOV.sasBytes, transactionFromBobPOV.sasBytes); - XCTAssertEqualObjects(sasTransactionFromAlicePOV.sasDecimal, transactionFromBobPOV.sasDecimal); - XCTAssertEqualObjects(sasTransactionFromAlicePOV.sasEmoji, transactionFromBobPOV.sasEmoji); - - // - Alice confirms SAS - [sasTransactionFromAlicePOV confirmSASMatch]; - break; - // -> 6. Transaction on Alice side must then move to WaitForPartnerToConfirm - case MXSASTransactionStateWaitForPartnerToConfirm: - // - Bob confirms SAS - [transactionFromBobPOV confirmSASMatch]; - break; - // -> 7. Transaction on Alice side must then move to Verified - case MXSASTransactionStateVerified: - checkBothDeviceVerified(); - break; - default: - XCTAssert(NO, @"Unexpected Alice transation state: %@", @(sasTransactionFromAlicePOV.state)); - break; - } - }]; - - // -> Transaction on Bob side must be WaitForPartnerKey, then ShowSAS - [self observeTransactionUpdate:transactionFromBobPOV block:^{ - - switch (transactionFromBobPOV.state) - { - // -> 1. Transaction on Bob side must be WaitForPartnerKey (Alice is WaitForPartnerToAccept) - case MXSASTransactionStateWaitForPartnerKey: - XCTAssertEqual(sasTransactionFromAlicePOV.state, MXSASTransactionStateOutgoingWaitForPartnerToAccept); - break; - // -> 3. Transaction on Bob side must then move to ShowSAS - case MXSASTransactionStateShowSAS: - break; - case MXSASTransactionStateWaitForPartnerToConfirm: - break; - // 7. Transaction on Bob side must then move to Verified - case MXSASTransactionStateVerified: - checkBothDeviceVerified(); - break; - default: - XCTAssert(NO, @"Unexpected Bob transation state: %@", @(sasTransactionFromAlicePOV.state)); - break; - } - }]; - }]; - - // -> Both ends must get a done message - NSMutableArray *doneDone = [NSMutableArray new]; - void (^checkDoneDone)(MXEvent *event, MXTimelineDirection direction, id customObject) = ^ void (MXEvent *event, MXTimelineDirection direction, id customObject) - { - XCTAssertEqual(event.eventType, MXEventTypeKeyVerificationDone); - - // Check done format - MXKeyVerificationDone *done; - MXJSONModelSetMXJSONModel(done, MXKeyVerificationDone.class, event.content); - XCTAssertNotNil(done); - - [doneDone addObject:done]; - if (doneDone.count == 4) - { - // Then, test MXKeyVerification - MXEvent *event = [aliceSession.store eventWithEventId:requestFromAlicePOV.requestId inRoom:roomId]; - [aliceSession.crypto.keyVerificationManager keyVerificationFromKeyVerificationEvent:event roomId:roomId success:^(MXKeyVerification * _Nonnull verificationFromAlicePOV) { - - XCTAssertEqual(verificationFromAlicePOV.state, MXKeyVerificationStateVerified); - - [expectation fulfill]; - } failure:^(NSError * _Nonnull error) { - XCTFail(@"The request should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - } - }; - - [aliceSession listenToEventsOfTypes:@[kMXEventTypeStringKeyVerificationDone] - onEvent:checkDoneDone]; - [bobSession listenToEventsOfTypes:@[kMXEventTypeStringKeyVerificationDone] - onEvent:checkDoneDone]; - -} - - /** Nomical case: The full flow It reuses code from testFullFlowWithAliceAndBob. @@ -1061,36 +455,6 @@ - (void)testVerificationByDMWithNoRoom }]; } -/** - Same tests as testVerificationByDMFullFlow but with alice with 2 sessions - */ -- (void)testVerificationByDMWithAliceWith2Devices -{ - // - Alice and Bob are in a room - [matrixSDKTestsE2EData doE2ETestWithAliceAndBobInARoom:self cryptedBob:YES warnOnUnknowDevices:YES aliceStore:[[MXMemoryStore alloc] init] bobStore:[[MXMemoryStore alloc] init] readyToTest:^(MXSession *aliceSession, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation) { - - [matrixSDKTestsE2EData loginUserOnANewDevice:self credentials:aliceSession.matrixRestClient.credentials withPassword:MXTESTS_ALICE_PWD onComplete:^(MXSession *newAliceSession) { - - [self checkVerificationByDMFullFlowWithAliceSession:aliceSession bobSession:bobSession roomId:roomId expectation:expectation]; - }]; - }]; -} - -/** - Same tests as testVerificationByDMFullFlow but with bob with 2 sessions - */ -- (void)testVerificationByDMWithAUserWith2Devices -{ - // - Alice and Bob are in a room - [matrixSDKTestsE2EData doE2ETestWithAliceAndBobInARoom:self cryptedBob:YES warnOnUnknowDevices:YES aliceStore:[[MXMemoryStore alloc] init] bobStore:[[MXMemoryStore alloc] init] readyToTest:^(MXSession *aliceSession, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation) { - - [matrixSDKTestsE2EData loginUserOnANewDevice:self credentials:bobSession.matrixRestClient.credentials withPassword:MXTESTS_BOB_PWD onComplete:^(MXSession *newBobSession) { - [self checkVerificationByDMFullFlowWithAliceSession:aliceSession bobSession:bobSession roomId:roomId expectation:expectation]; - }]; - }]; -} - - @end #pragma clang diagnostic pop diff --git a/MatrixSDKTests/MXCryptoMigrationTests.m b/MatrixSDKTests/MXCryptoMigrationTests.m deleted file mode 100644 index 5838de5b2c..0000000000 --- a/MatrixSDKTests/MXCryptoMigrationTests.m +++ /dev/null @@ -1,154 +0,0 @@ -// -// Copyright 2020 The Matrix.org Foundation C.I.C -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#import - -#import "MXCryptoMigration.h" - -#import "MatrixSDKTestsData.h" -#import "MatrixSDKTestsE2EData.h" -#import "MXCrypto_Private.h" -#import "MatrixSDKTestsSwiftHeader.h" - - -@interface MXCryptoMigration () - -- (void)migrateToCryptoVersion2:(void (^)(void))success failure:(void (^)(NSError *))failure; -- (void)claimOwnOneTimeKeys:(NSUInteger)keyCount success:(void (^)(NSUInteger keyClaimed))success failure:(void (^)(NSError *))failure; - -@end - - -@interface MXCryptoMigrationTests : XCTestCase -{ - MatrixSDKTestsData *matrixSDKTestsData; - MatrixSDKTestsE2EData *matrixSDKTestsE2EData; -} -@end - - -@implementation MXCryptoMigrationTests - -- (void)setUp -{ - [super setUp]; - - matrixSDKTestsData = [[MatrixSDKTestsData alloc] init]; - matrixSDKTestsE2EData = [[MatrixSDKTestsE2EData alloc] initWithMatrixSDKTestsData:matrixSDKTestsData]; -} - -- (void)tearDown -{ - matrixSDKTestsData = nil; - matrixSDKTestsE2EData = nil; - - [super tearDown]; -} - -/** - Test shouldMigrate. - - - Have Alice - -> There is no need to migrate on a fresh login - */ -- (void)testShouldMigrate -{ - // - Have Alice - [matrixSDKTestsE2EData doE2ETestWithBobAndAlice:self readyToTest:^(MXSession *bobSession, MXSession *aliceSession, XCTestExpectation *expectation) { - - MXCryptoMigration *cryptoMigration = [[MXCryptoMigration alloc] initWithCrypto:aliceSession.crypto]; - - // -> There is no need to migrate on a fresh login - XCTAssertFalse([cryptoMigration shouldMigrate]); - - [expectation fulfill]; - }]; -} - -/** - Test migration to MXCryptoVersion2, which purges all published one time keys. - - - Alice and Bob are in a room - - Consume some one time keys to check later that the migration actually completes after having uploading the fresh 50 OTKs - - Bob does a migration - -> Bob must have 50 OTKs available again - - Alice sends a message (it will use an olm session based one of those new OTKs) - -> Bob must be able to decrypt the message - */ -- (void)testMigrationToMXCryptoVersion2 -{ - // - Alice and Bob are in a room - [matrixSDKTestsE2EData doE2ETestWithAliceAndBobInARoom:self cryptedBob:YES warnOnUnknowDevices:NO readyToTest:^(MXSession *aliceSession, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation) { - - MXCryptoMigration *bobCryptoMigration = [[MXCryptoMigration alloc] initWithCrypto:bobSession.crypto]; - - // - Consume some one time keys to check later that the migration actually completes after having uploading the fresh 50 OTKs - [bobCryptoMigration claimOwnOneTimeKeys:3 success:^(NSUInteger keyClaimed) { - XCTAssertEqual(keyClaimed, 3); - - // - Bob does a migration - [bobCryptoMigration migrateToCryptoVersion2:^{ - - // -> Bob must have 50 OTKs available again - [bobSession.legacyCrypto publishedOneTimeKeysCount:^(NSUInteger publishedKeyCount) { - - XCTAssertEqual(publishedKeyCount, 50); - - // - Alice sends a message (it will use an olm session based one of those new OTKs) - NSString *messageFromAlice = @"Hello I'm Alice!"; - - MXRoom *roomFromBobPOV = [bobSession roomWithRoomId:roomId]; - MXRoom *roomFromAlicePOV = [aliceSession roomWithRoomId:roomId]; - - XCTAssert(roomFromBobPOV.summary.isEncrypted); - XCTAssert(roomFromAlicePOV.summary.isEncrypted); - - [roomFromBobPOV liveTimeline:^(id liveTimeline) { - - [liveTimeline listenToEventsOfTypes:@[kMXEventTypeStringRoomMessage] onEvent:^(MXEvent *event, MXTimelineDirection direction, MXRoomState *roomState) { - - // -> Bob must be able to decrypt the message - XCTAssertEqual(event.eventType, MXEventTypeRoomMessage); - XCTAssertNil(event.decryptionError); - - [expectation fulfill]; - }]; - }]; - - [roomFromAlicePOV sendTextMessage:messageFromAlice threadId:nil success:nil failure:^(NSError *error) { - XCTFail(@"Cannot set up intial test conditions - error: %@", error); - [expectation fulfill]; - }]; - - } failure:^(NSError *error) { - XCTFail(@"The operation should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - - } failure:^(NSError *error) { - XCTFail(@"The operation should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - - } failure:^(NSError *error) { - XCTFail(@"Cannot set up intial test conditions - error: %@", error); - [expectation fulfill]; - }]; - - }]; -} - -@end diff --git a/MatrixSDKTests/MXCryptoRecoveryServiceTests.m b/MatrixSDKTests/MXCryptoRecoveryServiceTests.m index e59c9422d1..e3fc5ea45b 100644 --- a/MatrixSDKTests/MXCryptoRecoveryServiceTests.m +++ b/MatrixSDKTests/MXCryptoRecoveryServiceTests.m @@ -16,7 +16,6 @@ #import -#import "MXCrypto_Private.h" #import "MXMemoryStore.h" #import "MatrixSDKTestsData.h" @@ -112,134 +111,6 @@ - (void)doTestWithAliceWithCrossSigningAndKeyBackup:(XCTestCase*)testCase }]; } -// Test the recovery creation and its restoration. -// -// - Test creation of a recovery -// - Have Alice with cross-signing bootstrapped -// -> There should be no recovery on the HS -// -> The service should see 4 keys to back up (MSK, SSK, USK, Key Backup) -// Create a recovery with a passphrase -// -> The 3 keys should be in the recovery -// -> The recovery must indicate it has a passphrase -// -> Key backup must be up and running -// Forget all secrets for the test -// Recover all secrets -// -> We should have restored the 3 ones -// -> Make sure the secret is still correct -- (void)testRecoveryWithPassphrase -{ - // - Have Alice with cross-signing bootstrapped - [self doTestWithAliceWithCrossSigning:self readyToTest:^(MXSession *aliceSession, NSString *roomId, XCTestExpectation *expectation) { - - NSString *msk = [aliceSession.legacyCrypto.store secretWithSecretId:MXSecretId.crossSigningMaster]; - - MXRecoveryService *recoveryService = aliceSession.crypto.recoveryService; - XCTAssertNotNil(recoveryService); - - // -> There should be no recovery on the HS - XCTAssertFalse(recoveryService.hasRecovery); - XCTAssertEqual(recoveryService.secretsStoredInRecovery.count, 0); - - // -> The service should see 3 keys to back up (MSK, SSK, USK) - XCTAssertEqual(recoveryService.secretsStoredLocally.count, 3); - - // Create a recovery with a passphrase - NSString *passphrase = @"A passphrase"; - [recoveryService createRecoveryForSecrets:nil withPassphrase:passphrase createServicesBackups:YES success:^(MXSecretStorageKeyCreationInfo * _Nonnull keyCreationInfo) { - - XCTAssertNotNil(keyCreationInfo); - - // -> The 3 keys should be in the recovery - XCTAssertTrue(recoveryService.hasRecovery); - XCTAssertEqual(recoveryService.secretsStoredInRecovery.count, 4); - - // -> The recovery must indicate it has a passphrase - XCTAssertTrue(recoveryService.usePassphrase); - - // -> Key backup must be up and running - XCTAssertTrue(aliceSession.crypto.backup.enabled); - - - // Forget all secrets for the test - [aliceSession.legacyCrypto.store deleteSecretWithSecretId:MXSecretId.crossSigningMaster]; - [aliceSession.legacyCrypto.store deleteSecretWithSecretId:MXSecretId.crossSigningSelfSigning]; - [aliceSession.legacyCrypto.store deleteSecretWithSecretId:MXSecretId.crossSigningUserSigning]; - [aliceSession.legacyCrypto.store deleteSecretWithSecretId:MXSecretId.keyBackup]; - - - // Recover all secrets - [recoveryService privateKeyFromPassphrase:passphrase success:^(NSData * _Nonnull privateKey) { - [recoveryService recoverSecrets:nil withPrivateKey:privateKey recoverServices:NO success:^(MXSecretRecoveryResult * _Nonnull recoveryResult) { - - // -> We should have restored the 3 ones - XCTAssertEqual(recoveryResult.secrets.count, 4); - XCTAssertEqual(recoveryResult.updatedSecrets.count, 4); - XCTAssertEqual(recoveryResult.invalidSecrets.count, 0); - - // -> Make sure the secret is still correct - NSString *msk2 = [aliceSession.legacyCrypto.store secretWithSecretId:MXSecretId.crossSigningMaster]; - XCTAssertEqualObjects(msk, msk2); - - [expectation fulfill]; - } failure:^(NSError * _Nonnull error) { - XCTFail(@"The operation should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - - } failure:^(NSError * _Nonnull error) { - XCTFail(@"The operation should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - - } failure:^(NSError * _Nonnull error) { - XCTFail(@"The operation should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - - }]; -} - -// Test privateKeyFromRecoveryKey & privateKeyFromPassphrase -// -// - Have Alice with cross-signing bootstrapped -// - Create a recovery with a passphrase -// -> privateKeyFromRecoveryKey must return the same private key -// -> privateKeyFromPassphrase must return the same private key -- (void)testPrivateKeyTools -{ - // - Have Alice with cross-signing bootstrapped - [self doTestWithAliceWithCrossSigning:self readyToTest:^(MXSession *aliceSession, NSString *roomId, XCTestExpectation *expectation) { - - MXRecoveryService *recoveryService = aliceSession.crypto.recoveryService; - - // - Create a recovery with a passphrase - NSString *passphrase = @"A passphrase"; - [recoveryService createRecoveryForSecrets:nil withPassphrase:passphrase createServicesBackups:NO success:^(MXSecretStorageKeyCreationInfo * _Nonnull keyCreationInfo) { - - // -> privateKeyFromRecoveryKey must return the same private key - NSError *error; - NSData *privateKeyFromRecoveryKey = [recoveryService privateKeyFromRecoveryKey:keyCreationInfo.recoveryKey error:&error]; - XCTAssertNil(error); - XCTAssertEqualObjects(privateKeyFromRecoveryKey, keyCreationInfo.privateKey); - - // -> privateKeyFromPassphrase must return the same private key - [recoveryService privateKeyFromPassphrase:passphrase success:^(NSData * _Nonnull privateKey) { - - XCTAssertEqualObjects(privateKey, keyCreationInfo.privateKey); - - [expectation fulfill]; - } failure:^(NSError * _Nonnull error) { - XCTFail(@"The operation should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - - } failure:^(NSError * _Nonnull error) { - XCTFail(@"The operation should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - }]; -} - // Test bad recovery key string format // @@ -267,244 +138,4 @@ - (void)testBadRecoveryKeyFormat }]; } -// Test wrong private key -// -// - Have Alice with cross-signing bootstrapped -// - Create a recovery with a passphrase -// - Build a bad recovery key from a bad passphrase -// - Try to recover with this bad key -// -> It must error with expected NSError domain and code -- (void)testWrongRecoveryKey -{ - // - Have Alice with cross-signing bootstrapped - [self doTestWithAliceWithCrossSigning:self readyToTest:^(MXSession *aliceSession, NSString *roomId, XCTestExpectation *expectation) { - - MXRecoveryService *recoveryService = aliceSession.crypto.recoveryService; - - // - Create a recovery with a passphrase - [recoveryService createRecoveryForSecrets:nil withPassphrase:@"A passphrase" createServicesBackups:NO success:^(MXSecretStorageKeyCreationInfo * _Nonnull keyCreationInfo) { - - // - Build a bad recovery key from a bad passphrase - [recoveryService privateKeyFromPassphrase:@"A bad passphrase" success:^(NSData * _Nonnull badPrivateKey) { - - // - Try to recover with this bad key - [recoveryService recoverSecrets:nil withPrivateKey:badPrivateKey recoverServices:NO success:^(MXSecretRecoveryResult * _Nonnull recoveryResult) { - - XCTFail(@"The operation should not succeed"); - [expectation fulfill]; - - } failure:^(NSError * _Nonnull error) { - - // -> It must error with expected NSError domain and code - XCTAssertNotNil(error); - XCTAssertEqualObjects(error.domain, MXRecoveryServiceErrorDomain); - XCTAssertEqual(error.code, MXRecoveryServiceBadRecoveryKeyErrorCode); - - [expectation fulfill]; - }]; - - } failure:^(NSError * _Nonnull error) { - XCTFail(@"The operation should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - - } failure:^(NSError * _Nonnull error) { - XCTFail(@"The operation should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - }]; -} - - -// Test createRecoveryForSecrets when there is already a key backup with the private key stored locally -// -// - Have Alice with cross-signing and key backup bootstrapped -// - Create a recovery with createServicesBackup:YES -// -> The operation must succeed -// -> The key backup should be the same -- (void)testCreateRecoveryWithKeyBackupExists -{ - // - Have Alice with cross-signing and key backup bootstrapped - [self doTestWithAliceWithCrossSigningAndKeyBackup:self readyToTest:^(MXSession *aliceSession, NSString *roomId, XCTestExpectation *expectation) { - - NSString *keyBackupVersion = aliceSession.crypto.backup.keyBackupVersion.version; - - // - Create a recovery with createServicesBackup:YES - [aliceSession.crypto.recoveryService createRecoveryForSecrets:nil withPassphrase:nil createServicesBackups:YES success:^(MXSecretStorageKeyCreationInfo * _Nonnull keyCreationInfo) { - - XCTAssertEqualObjects(keyBackupVersion, aliceSession.crypto.backup.keyBackupVersion.version); - [expectation fulfill]; - - } failure:^(NSError *error) { - XCTFail(@"The operation should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - }]; -} - - -// Test MXRecoveryServiceKeyBackupExistsButNoPrivateKeyErrorCode -// -// - Have Alice with cross-signing and key backup bootstrapped -// - Forget the key backup private key (this micmics key backup created from another device) -// - Create a recovery with createServicesBackup:YES -// -> The operation must fail -- (void)testKeyBackupExistsButNoPrivateKey -{ - // - Have Alice with cross-signing and key backup bootstrapped - [self doTestWithAliceWithCrossSigningAndKeyBackup:self readyToTest:^(MXSession *aliceSession, NSString *roomId, XCTestExpectation *expectation) { - - // - Forget the key backup private key (this micmics key backup created from another device) - [aliceSession.legacyCrypto.store deleteSecretWithSecretId:MXSecretId.keyBackup]; - - // - Create a recovery with createServicesBackup:YES - [aliceSession.crypto.recoveryService createRecoveryForSecrets:nil withPassphrase:nil createServicesBackups:YES success:^(MXSecretStorageKeyCreationInfo * _Nonnull keyCreationInfo) { - - XCTFail(@"The operation must not succeed"); - [expectation fulfill]; - - } failure:^(NSError *error) { - - // -> The operation must fail - XCTAssertEqualObjects(error.domain, MXRecoveryServiceErrorDomain); - XCTAssertEqual(error.code, MXRecoveryServiceKeyBackupExistsButNoPrivateKeyErrorCode); - [expectation fulfill]; - }]; - }]; -} - - -// Test recovery of services -// -// - Have Alice with cross-signing bootstrapped -// - Create a recovery -// - Log Alice on a new device -// - Recover secrets and services -// -> The new device must have cross-signing fully on -// -> The new device must be cross-signed -// -> The new device must trust and send keys to the existing key backup -- (void)testRecoverServicesAssociatedWithSecrets -{ - // - Have Alice with cross-signing bootstrapped - [self doTestWithAliceWithCrossSigning:self readyToTest:^(MXSession *aliceSession, NSString *roomId, XCTestExpectation *expectation) { - - // - Create a recovery - [aliceSession.crypto.recoveryService createRecoveryForSecrets:nil withPassphrase:nil createServicesBackups:YES success:^(MXSecretStorageKeyCreationInfo * _Nonnull keyCreationInfo) { - - NSData *recoveryPrivateKey = keyCreationInfo.privateKey; - - // - Log Alice on a new device - [MXSDKOptions sharedInstance].enableCryptoWhenStartingMXSession = YES; - [self->matrixSDKTestsData relogUserSessionWithNewDevice:self session:aliceSession withPassword:MXTESTS_ALICE_PWD onComplete:^(MXSession *aliceSession2) { - [MXSDKOptions sharedInstance].enableCryptoWhenStartingMXSession = NO; - - [aliceSession2.crypto.crossSigning refreshStateWithSuccess:^(BOOL stateUpdated) { - - // Before recover, the device can do nothing with existing cross-signing and key backup - XCTAssertEqual(aliceSession2.crypto.crossSigning.state, MXCrossSigningStateCrossSigningExists); - - XCTAssertNotNil(aliceSession2.crypto.backup.keyBackupVersion); - XCTAssertFalse(aliceSession2.crypto.backup.enabled); - XCTAssertEqual(aliceSession2.legacyCrypto.store.inboundGroupSessions.count, 0); - - - // - Recover secrets and services - [aliceSession2.crypto.recoveryService recoverSecrets:nil withPrivateKey:recoveryPrivateKey recoverServices:YES success:^(MXSecretRecoveryResult * _Nonnull recoveryResult) { - - // -> The new device must have cross-signing fully on - XCTAssertEqual(aliceSession2.crypto.crossSigning.state, MXCrossSigningStateCanCrossSign); - - // -> The new device must be cross-signed - MXDeviceTrustLevel *newDeviceTrust = [aliceSession2.crypto deviceTrustLevelForDevice:aliceSession2.myDeviceId ofUser:aliceSession2.myUserId]; - XCTAssertTrue(newDeviceTrust.isCrossSigningVerified); - - - // -> The new device must trust and send keys to the existing key backup - XCTAssertTrue(aliceSession2.crypto.backup.hasPrivateKeyInCryptoStore); - XCTAssertTrue(aliceSession2.crypto.backup.enabled); - - // -> The new device should have restore keys from the backup - XCTAssertEqual(aliceSession2.legacyCrypto.store.inboundGroupSessions.count, 1); - - - [expectation fulfill]; - - } failure:^(NSError *error) { - XCTFail(@"Cannot set up intial test conditions - error: %@", error); - [expectation fulfill]; - }]; - } failure:^(NSError *error) { - XCTFail(@"Cannot set up intial test conditions - error: %@", error); - [expectation fulfill]; - }]; - }]; - - } failure:^(NSError *error) { - XCTFail(@"Cannot set up intial test conditions - error: %@", error); - [expectation fulfill]; - }]; - }]; -} - - -// Test privateKeyFromRecoveryKey & privateKeyFromPassphrase -// -// - Have Alice with cross-signing bootstrapped -// - Create a recovery -// - Delete it -// -> No more recovery -// -> No more underlying SSSS -// -> No more underlying key backup -- (void)testDeleteRecovery -{ - // - Have Alice with cross-signing bootstrapped - [self doTestWithAliceWithCrossSigning:self readyToTest:^(MXSession *aliceSession, NSString *roomId, XCTestExpectation *expectation) { - - MXRecoveryService *recoveryService = aliceSession.crypto.recoveryService; - - // - Create a recovery - [recoveryService createRecoveryForSecrets:nil withPassphrase:nil createServicesBackups:YES success:^(MXSecretStorageKeyCreationInfo * _Nonnull keyCreationInfo) { - - // Check the test is right - XCTAssertTrue(recoveryService.hasRecovery); - XCTAssertEqual(recoveryService.secretsStoredInRecovery.count, 4); - XCTAssertTrue(aliceSession.crypto.backup.enabled); - - NSString *ssssKeyId = recoveryService.recoveryId; - - // - Delete it - [recoveryService deleteRecoveryWithDeleteServicesBackups:YES success:^{ - - // -> No more recovery - XCTAssertFalse(recoveryService.hasRecovery); - XCTAssertEqual(recoveryService.secretsStoredInRecovery.count, 0); - XCTAssertEqual(recoveryService.secretsStoredLocally.count, 3); - - // -> No more underlying SSSS - MXSecretStorage *secretStorage = aliceSession.legacyCrypto.secretStorage; - XCTAssertNil(secretStorage.defaultKey); - XCTAssertFalse([secretStorage hasSecretWithSecretId:MXSecretId.crossSigningMaster withSecretStorageKeyId:ssssKeyId]); - XCTAssertFalse([secretStorage hasSecretWithSecretId:MXSecretId.crossSigningSelfSigning withSecretStorageKeyId:ssssKeyId]); - XCTAssertFalse([secretStorage hasSecretWithSecretId:MXSecretId.crossSigningUserSigning withSecretStorageKeyId:ssssKeyId]); - XCTAssertFalse([secretStorage hasSecretWithSecretId:MXSecretId.keyBackup withSecretStorageKeyId:ssssKeyId]); - - // -> No more underlying key backup - XCTAssertFalse(aliceSession.crypto.backup.enabled); - XCTAssertNil(aliceSession.crypto.backup.keyBackupVersion); - - [expectation fulfill]; - - } failure:^(NSError * _Nonnull error) { - XCTFail(@"The operation should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - - } failure:^(NSError * _Nonnull error) { - XCTFail(@"The operation should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - }]; -} - - @end diff --git a/MatrixSDKTests/MXCryptoSecretShareTests.m b/MatrixSDKTests/MXCryptoSecretShareTests.m deleted file mode 100644 index 63f07c624b..0000000000 --- a/MatrixSDKTests/MXCryptoSecretShareTests.m +++ /dev/null @@ -1,195 +0,0 @@ -/* - Copyright 2020 The Matrix.org Foundation C.I.C - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - - -#import - -#import "MXCrypto_Private.h" -#import "MXCryptoStore.h" -#import "MXSession.h" - -#import "MatrixSDKTestsData.h" -#import "MatrixSDKTestsE2EData.h" -#import "MatrixSDKTestsSwiftHeader.h" - - -@interface MXCryptoSecretShareTests : XCTestCase -{ - MatrixSDKTestsData *matrixSDKTestsData; - MatrixSDKTestsE2EData *matrixSDKTestsE2EData; -} - -@end - - -@implementation MXCryptoSecretShareTests - -- (void)setUp -{ - [super setUp]; - - matrixSDKTestsData = [[MatrixSDKTestsData alloc] init]; - matrixSDKTestsE2EData = [[MatrixSDKTestsE2EData alloc] initWithMatrixSDKTestsData:matrixSDKTestsData]; -} - -- (void)tearDown -{ - matrixSDKTestsData = nil; - matrixSDKTestsE2EData = nil; - - [super tearDown]; -} - -/** - Tests secrets storage in MXCryptoStore. - */ -- (void)testLocalSecretStorage -{ - [matrixSDKTestsE2EData doE2ETestWithAliceInARoom:self readyToTest:^(MXSession *aliceSession, NSString *roomId, XCTestExpectation *expectation) { - NSString *secretId = @"secretId"; - NSString *secret = @"A secret"; - NSString *secret2 = @"A secret2"; - - XCTAssertNil([aliceSession.legacyCrypto.store secretWithSecretId:secretId]); - - [aliceSession.legacyCrypto.store storeSecret:secret withSecretId:secretId]; - XCTAssertEqualObjects([aliceSession.legacyCrypto.store secretWithSecretId:secretId], secret); - - [aliceSession.legacyCrypto.store storeSecret:secret2 withSecretId:secretId]; - XCTAssertEqualObjects([aliceSession.legacyCrypto.store secretWithSecretId:secretId], secret2); - - [aliceSession.legacyCrypto.store deleteSecretWithSecretId:secretId]; - XCTAssertNil([aliceSession.legacyCrypto.store secretWithSecretId:secretId]); - - [expectation fulfill]; - }]; -} - -/** - Nomical case: Gossip a secret between 2 devices. - - - Alice has a secret on her 1st device - - Alice logs in on a new device - - Alice trusts the new device and vice versa - - Alice requests the secret from the new device - -> She gets the secret - */ -- (void)testSecretShare -{ - [matrixSDKTestsE2EData doE2ETestWithAliceInARoom:self readyToTest:^(MXSession *aliceSession, NSString *roomId, XCTestExpectation *expectation) { - - NSString *secretId = @"secretId"; - NSString *secret = @"A secret"; - - // - Alice has a secret on her 1st device - [aliceSession.legacyCrypto.store storeSecret:secret withSecretId:secretId]; - - // - Alice logs in on a new device - [matrixSDKTestsE2EData loginUserOnANewDevice:self credentials:aliceSession.matrixRestClient.credentials withPassword:MXTESTS_ALICE_PWD onComplete:^(MXSession *newAliceSession) { - - MXCredentials *newAlice = newAliceSession.matrixRestClient.credentials; - - // - Alice trusts the new device and vice versa - [aliceSession.crypto setDeviceVerification:MXDeviceVerified forDevice:newAlice.deviceId ofUser:newAlice.userId success:nil failure:nil]; - [newAliceSession.crypto setDeviceVerification:MXDeviceVerified forDevice:aliceSession.myDeviceId ofUser:aliceSession.myUserId success:nil failure:nil]; - - // - Alice requests the secret from the new device - [newAliceSession.legacyCrypto.secretShareManager requestSecret:secretId toDeviceIds:nil success:^(NSString * _Nonnull requestId) { - XCTAssertNotNil(requestId); - } onSecretReceived:^BOOL(NSString * _Nonnull sharedSecret) { - - // -> She gets the secret - XCTAssertEqualObjects(sharedSecret, secret); - [expectation fulfill]; - return YES; - } failure:^(NSError * _Nonnull error) { - XCTFail(@"The operation should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - }]; - }]; -} - -/** - Test cancellation: Make sure devices do share secrets when the request has been cancelled. - - - Alice has a secret on her 1st device - - Alice logs in on a new device - - Alice trusts the new device - - Alice pauses the first device - - Alice requests the secret from the new device - - Alice cancels the request - - Alice resumes the first device - -> The first device should not have sent the secret through MXEventTypeSecretSend event - */ -- (void)testSecretRequestCancellation -{ - [matrixSDKTestsE2EData doE2ETestWithAliceInARoom:self readyToTest:^(MXSession *aliceSession, NSString *roomId, XCTestExpectation *expectation) { - - NSString *secretId = @"secretId"; - NSString *secret = @"A secret"; - - // - Alice has a secret on her 1st device - [aliceSession.legacyCrypto.store storeSecret:secret withSecretId:secretId]; - - // - Alice logs in on a new device - [matrixSDKTestsE2EData loginUserOnANewDevice:self credentials:aliceSession.matrixRestClient.credentials withPassword:MXTESTS_ALICE_PWD onComplete:^(MXSession *newAliceSession) { - - MXCredentials *newAlice = newAliceSession.matrixRestClient.credentials; - - // - Alice trusts the new device - [aliceSession.crypto setDeviceVerification:MXDeviceVerified forDevice:newAlice.deviceId ofUser:newAlice.userId success:nil failure:nil]; - - // - Alice pauses the first device - [aliceSession pause]; - - // - Alice requests the secret from the new device - [newAliceSession.legacyCrypto.secretShareManager requestSecret:secretId toDeviceIds:nil success:^(NSString * _Nonnull requestId) { - - // - Alice cancels the request - [newAliceSession.legacyCrypto.secretShareManager cancelRequestWithRequestId:requestId success:^{ - } failure:^(NSError * _Nonnull error) { - XCTFail(@"The operation should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - - [aliceSession resume:^{ - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{ - [expectation fulfill]; - }); - }]; - - } onSecretReceived:^BOOL(NSString * _Nonnull secret) { - XCTFail(@"The operation should never complete"); - [expectation fulfill]; - return YES; - } failure:^(NSError * _Nonnull error) { - XCTFail(@"The operation should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - - [[NSNotificationCenter defaultCenter] addObserverForName:kMXSessionOnToDeviceEventNotification object:newAliceSession queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull notification) { - - // -> The first device should not have sent the secret through MXEventTypeSecretSend event - MXEvent *event = notification.userInfo[kMXSessionNotificationEventKey]; - XCTAssertNotEqual(event.eventType, MXEventTypeSecretSend); - }]; - }]; - }]; -} - - -@end diff --git a/MatrixSDKTests/MXCryptoSecretStorageTests.m b/MatrixSDKTests/MXCryptoSecretStorageTests.m index 75bc9044ed..c74496c22b 100644 --- a/MatrixSDKTests/MXCryptoSecretStorageTests.m +++ b/MatrixSDKTests/MXCryptoSecretStorageTests.m @@ -162,454 +162,6 @@ - (void)createScenarioWithMatrixJsSDKData:(void (^)(MXSession *aliceSession, NSS }]; } - -#pragma mark - Secret Storage Key - -// Test MXSecretStorage.createKeyWithKeyId -// - Have Alice with encryption -// - Create a new secret storage key -// -> MXSecretStorageKeyCreationInfo must be filled as expected -// - Get back the key -// -> We must get it with the same value as MXSecretStorageKeyCreationInfo -- (void)testSecretStorageKeyCreation -{ - // - Have Alice with encryption - [matrixSDKTestsE2EData doE2ETestWithAliceInARoom:self readyToTest:^(MXSession *aliceSession, NSString *roomId, XCTestExpectation *expectation) { - - // - Create a new secret storage key - MXSecretStorage *secretStorage = aliceSession.legacyCrypto.secretStorage; - [secretStorage createKeyWithKeyId:nil keyName:nil passphrase:nil success:^(MXSecretStorageKeyCreationInfo * _Nonnull keyCreationInfo) { - - // -> MXSecretStorageKeyCreationInfo must be filled as expected - XCTAssertNotNil(keyCreationInfo); - XCTAssertNotNil(keyCreationInfo.keyId); - XCTAssertNotNil(keyCreationInfo.privateKey); - XCTAssertNotNil(keyCreationInfo.recoveryKey); - - MXSecretStorageKeyContent *keyContent = keyCreationInfo.content; - XCTAssertNotNil(keyContent); - XCTAssertEqualObjects(keyContent.algorithm, MXSecretStorageKeyAlgorithm.aesHmacSha2); - XCTAssertNil(keyContent.name); - XCTAssertNotNil(keyContent.iv); - XCTAssertNotNil(keyContent.mac); - XCTAssertNil(keyContent.passphrase); - - - // - Get back the key - MXSecretStorageKeyContent *key = [secretStorage keyWithKeyId:keyCreationInfo.keyId]; - - // -> We must get it with the same value as MXSecretStorageKeyCreationInfo - XCTAssertNotNil(key); - XCTAssertEqualObjects(key.JSONDictionary, keyContent.JSONDictionary); - - [expectation fulfill]; - } failure:^(NSError * _Nonnull error) { - XCTFail(@"The operation should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - }]; -} - -// Test MXSecretStorage.createKeyWithKeyId with passphrase -// - Have Alice with encryption -// - Create a new secret storage key with a passphrase -// -> MXSecretStorageKeyCreationInfo must be filled as expected -// - Get back the key -// -> We must get it with the same value as MXSecretStorageKeyCreationInfo -- (void)testSecretStorageKeyCreationWithPassphrase -{ - // - Have Alice with encryption - [matrixSDKTestsE2EData doE2ETestWithAliceInARoom:self readyToTest:^(MXSession *aliceSession, NSString *roomId, XCTestExpectation *expectation) { - - NSString *KEY_ID = @"KEYID"; - NSString *KEY_NAME = @"A Key Name"; - NSString *PASSPHRASE = @"a passphrase"; - - - // - Create a new secret storage key - MXSecretStorage *secretStorage = aliceSession.legacyCrypto.secretStorage; - [secretStorage createKeyWithKeyId:KEY_ID keyName:KEY_NAME passphrase:PASSPHRASE success:^(MXSecretStorageKeyCreationInfo * _Nonnull keyCreationInfo) { - - // -> MXSecretStorageKeyCreationInfo must be filled as expected - XCTAssertNotNil(keyCreationInfo); - XCTAssertEqualObjects(keyCreationInfo.keyId, KEY_ID); - XCTAssertNotNil(keyCreationInfo.privateKey); - XCTAssertNotNil(keyCreationInfo.recoveryKey); - - MXSecretStorageKeyContent *keyContent = keyCreationInfo.content; - XCTAssertNotNil(keyContent); - XCTAssertEqualObjects(keyContent.algorithm, MXSecretStorageKeyAlgorithm.aesHmacSha2); - XCTAssertEqualObjects(keyContent.name, KEY_NAME); - XCTAssertNotNil(keyContent.iv); - XCTAssertNotNil(keyContent.mac); - - MXSecretStoragePassphrase *passphraseInfo = keyContent.passphrase; - XCTAssertNotNil(passphraseInfo); - XCTAssertEqualObjects(passphraseInfo.algorithm, @"m.pbkdf2"); - XCTAssertGreaterThan(passphraseInfo.iterations, 0); - XCTAssertNotNil(passphraseInfo.salt); - XCTAssertEqual(passphraseInfo.bits, 256); - - - // - Get back the key - MXSecretStorageKeyContent *key = [secretStorage keyWithKeyId:keyCreationInfo.keyId]; - - // -> We must get it with the same value as MXSecretStorageKeyCreationInfo - XCTAssertNotNil(key); - XCTAssertEqualObjects(key.JSONDictionary, keyCreationInfo.content.JSONDictionary); - - [expectation fulfill]; - } failure:^(NSError * _Nonnull error) { - XCTFail(@"The operation should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - }]; -} - -// Test MXSecretStorage.deleteKeyWithKeyId -// - Have Alice with encryption -// - Create a new secret storage key -// - Set it as default -// - Delete it -// -> The SSSS must not exist anymore -// -> There must be a default key anymore -- (void)testSecretStorageKeyDeletion -{ - // - Have Alice with encryption - [matrixSDKTestsE2EData doE2ETestWithAliceInARoom:self readyToTest:^(MXSession *aliceSession, NSString *roomId, XCTestExpectation *expectation) { - - // - Create a new secret storage key - __weak MXSecretStorage *secretStorage = aliceSession.legacyCrypto.secretStorage; - [secretStorage createKeyWithKeyId:nil keyName:nil passphrase:nil success:^(MXSecretStorageKeyCreationInfo * _Nonnull keyCreationInfo) { - - // - Set it as default - [secretStorage setAsDefaultKeyWithKeyId:keyCreationInfo.keyId success:^{ - - // Check the test is right - XCTAssertNotNil(secretStorage.defaultKey); - - // - Delete it - [secretStorage deleteKeyWithKeyId:nil success:^{ - - // -> The SSSS must not exist anymore - MXSecretStorageKeyContent *key2 = [secretStorage keyWithKeyId:keyCreationInfo.keyId]; - XCTAssertNil(key2); - - // -> There must be a default key anymore - XCTAssertNil(secretStorage.defaultKey); - - [expectation fulfill]; - - } failure:^(NSError * _Nonnull error) { - XCTFail(@"The operation should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - - } failure:^(NSError * _Nonnull error) { - XCTFail(@"The operation should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - - } failure:^(NSError * _Nonnull error) { - XCTFail(@"The operation should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - }]; -} - -// Test MXSecretStorage.defaultKey -// - Have Alice with encryption -// - Create a secret storage key -// - Set it as default -// - Get back the default key -// -> We must get it with the same value as MXSecretStorageKeyCreationInfo -- (void)testDefaultSecretStorageKey -{ - // - Have Alice with encryption - [matrixSDKTestsE2EData doE2ETestWithAliceInARoom:self readyToTest:^(MXSession *aliceSession, NSString *roomId, XCTestExpectation *expectation) { - - // - Create a secret storage key - MXSecretStorage *secretStorage = aliceSession.legacyCrypto.secretStorage; - [secretStorage createKeyWithKeyId:nil keyName:nil passphrase:nil success:^(MXSecretStorageKeyCreationInfo * _Nonnull keyCreationInfo) { - - // - Set it as default - [secretStorage setAsDefaultKeyWithKeyId:keyCreationInfo.keyId success:^{ - - // - Get back the default key - MXSecretStorageKeyContent *defaultKey = secretStorage.defaultKey; - - // -> We must get it with the same value as MXSecretStorageKeyCreationInfo - XCTAssertNotNil(defaultKey); - XCTAssertEqualObjects(defaultKey.JSONDictionary, keyCreationInfo.content.JSONDictionary); - - [expectation fulfill]; - - } failure:^(NSError * _Nonnull error) { - XCTFail(@"The operation should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - - } failure:^(NSError * _Nonnull error) { - XCTFail(@"The operation should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - }]; -} - -// Test MXSecretStorage.checkPrivateKey -// - Have Alice with SSSS bootstrapped -// - Check the private key we have match the SSSS key -// -> It must match -- (void)testCheckPrivateKey -{ - // - Have Alice with SSSS bootstrapped - [self createScenarioWithMatrixJsSDKData:^(MXSession *aliceSession, NSString *roomId, XCTestExpectation *expectation) { - - MXSecretStorage *secretStorage = aliceSession.legacyCrypto.secretStorage; - - NSError *error; - NSData *privateKey = [MXRecoveryKey decode:jsSDKDataRecoveryKey error:&error]; - XCTAssertNotNil(privateKey); - - MXSecretStorageKeyContent *defaultKey = secretStorage.defaultKey; - XCTAssert(defaultKey); - - // - Check the private key we have match the SSSS key - [secretStorage checkPrivateKey:privateKey withKey:defaultKey complete:^(BOOL match) { - - // -> It must match - XCTAssertTrue(match); - - [expectation fulfill]; - }]; - }]; -} - -// Test the number of valid (i.e. non-empty) keys -// - Have Alice with SSSS bootstrapped -// -> Should only have one SSSS key -// - Add two more SSSS without deleting previous ones -// -> Should now have 3 SSSS keys -- (void)testNumberOfValidKeys -{ - NSDictionary *ssssKeyContent = @{ - @"algorithm": @"m.secret_storage.v1.aes-hmac-sha2", - @"passphrase": @{ - @"algorithm": @"m.pbkdf2", - @"iterations": @(500000), - @"salt": @"Djb0XcHWHu5Mx3GTDar6OfvbkxScBR6N" - }, - @"iv": @"5SwqbVexZodcLg+PQcPhHw==", - @"mac": @"NBJLmrWo6uXoiNHpKUcBA9d4xKcoj0GnB+4F234zNwI=", - }; - - // - Have Alice with SSSS bootstrapped - [self createScenarioWithMatrixJsSDKData:^(MXSession *aliceSession, NSString *roomId, XCTestExpectation *expectation) { - - // -> Should only have one SSSS key - MXSecretStorage *secretStorage = aliceSession.legacyCrypto.secretStorage; - XCTAssertEqual(secretStorage.numberOfValidKeys, 1); - - // - Add two more SSSS without deleting previous ones - [aliceSession setAccountData:ssssKeyContent forType:@"m.secret_storage.key.AAAA" success:^{ - [aliceSession setAccountData:ssssKeyContent forType:@"m.secret_storage.key.BBBB" success:^{ - - // -> Should now have 3 SSSS keys - MXSecretStorage *secretStorage = aliceSession.legacyCrypto.secretStorage; - XCTAssertEqual(secretStorage.numberOfValidKeys, 3); - [expectation fulfill]; - - } failure:^(NSError *error) { - XCTFail(@"Failed to set account data - %@", error); - [expectation fulfill]; - }]; - - } failure:^(NSError *error) { - XCTFail(@"Failed to set account data - %@", error); - [expectation fulfill]; - }]; - }]; -} - -#pragma mark - Secret storage - -// Test MXSecretStorage.secretStorageKeysUsedForSecretWithSecretId -// - Have Alice with SSSS bootstrapped -// - Get keys used for encrypting the MSK -// -> There should be one, the default key -- (void)testSecretStorageKeysUsedForSecretWithSecretId -{ - // - Have Alice with SSSS bootstrapped - [self createScenarioWithMatrixJsSDKData:^(MXSession *aliceSession, NSString *roomId, XCTestExpectation *expectation) { - - MXSecretStorage *secretStorage = aliceSession.legacyCrypto.secretStorage; - - // Test scenario creation - MXSecretStorageKeyContent *defaultKey = secretStorage.defaultKey; - XCTAssert(defaultKey); - - // - Get keys used for encrypting the MSK - NSDictionary *secretStorageKeys = [secretStorage secretStorageKeysUsedForSecretWithSecretId:MXSecretId.crossSigningMaster]; - - // -> There should be one, the default key - XCTAssertEqual(secretStorageKeys.count, 1); - XCTAssertEqualObjects(secretStorageKeys.allKeys.firstObject, secretStorage.defaultKeyId); - - [expectation fulfill]; - }]; -} - -// Test MXSecretStorage.secretWithSecretId -// - Have Alice with SSSS bootstrapped -// - Get the backup key from SSSS using the default key -// -> We should get it -// -> It should be the one created by matrix-js-sdk -- (void)testGetSecret -{ - // - Have Alice with SSSS bootstrapped - [self createScenarioWithMatrixJsSDKData:^(MXSession *aliceSession, NSString *roomId, XCTestExpectation *expectation) { - - MXSecretStorage *secretStorage = aliceSession.legacyCrypto.secretStorage; - - NSError *error; - NSData *privateKey = [MXRecoveryKey decode:jsSDKDataRecoveryKey error:&error]; - XCTAssertNotNil(privateKey); - - // - Get the backup key from SSSS using the default key - [secretStorage secretWithSecretId:MXSecretId.keyBackup withSecretStorageKeyId:nil privateKey:privateKey success:^(NSString * _Nonnull unpaddedBase64Secret) { - - // -> We should get it - XCTAssertNotNil(unpaddedBase64Secret); - - // -> It should be the one created by matrix-js-sdk - NSData *key = [MXBase64Tools dataFromBase64:unpaddedBase64Secret]; - NSData *jsKey = [NSData dataWithBytes:jsSDKDataBackupKeyBytes length:sizeof(jsSDKDataBackupKeyBytes)]; - - XCTAssertEqualObjects(key, jsKey); - - [expectation fulfill]; - - } failure:^(NSError * _Nonnull error) { - XCTFail(@"The operation should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - - }]; -} - -// Test MXSecretStorage.secretWithSecretId -// - Have Alice with SSSS bootstrapped -// -> Store a new secret -// - Get it back -// -> We should get it and it should be right one -- (void)testStoreSecret -{ - // - Have Alice with SSSS bootstrapped - [self createScenarioWithMatrixJsSDKData:^(MXSession *aliceSession, NSString *roomId, XCTestExpectation *expectation) { - - NSString *theSecretText = @"A secret"; - NSData *theSecret = [theSecretText dataUsingEncoding:kCFStringEncodingUTF8]; - NSString *theSecretUnpaddedBase64 = [MXBase64Tools unpaddedBase64FromData:theSecret]; - - NSString *theSecretId = @"theSecretId"; - - MXSecretStorage *secretStorage = aliceSession.legacyCrypto.secretStorage; - - NSError *error; - NSData *privateKey = [MXRecoveryKey decode:jsSDKDataRecoveryKey error:&error]; - XCTAssertNotNil(privateKey); - - // Build the key - NSDictionary *keys = @{ - secretStorage.defaultKeyId: privateKey - }; - - - // -> Store a new secret - [secretStorage storeSecret:theSecretUnpaddedBase64 withSecretId:theSecretId withSecretStorageKeys:keys success:^(NSString * _Nonnull secretId) { - - XCTAssertEqualObjects(theSecretId, secretId); - - XCTAssertTrue([secretStorage hasSecretWithSecretId:theSecretId withSecretStorageKeyId:nil]); - - // - Get it back - [secretStorage secretWithSecretId:theSecretId withSecretStorageKeyId:nil privateKey:privateKey success:^(NSString * _Nonnull unpaddedBase64Secret) { - - // -> We should get it and it should be right one - XCTAssertNotNil(unpaddedBase64Secret); - XCTAssertEqualObjects(unpaddedBase64Secret, theSecretUnpaddedBase64); - - [expectation fulfill]; - - } failure:^(NSError * _Nonnull error) { - XCTFail(@"The operation should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - - } failure:^(NSError * _Nonnull error) { - XCTFail(@"The operation should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - - }]; -} - - -// Test MXSecretStorage.deleteSecretWithSecretId -// - Have Alice with SSSS bootstrapped -// -> Store a new secret -// -> Delete it -// -> The secret must have been deleted -- (void)testDeleteSecret -{ - // - Have Alice with SSSS bootstrapped - [self createScenarioWithMatrixJsSDKData:^(MXSession *aliceSession, NSString *roomId, XCTestExpectation *expectation) { - - NSString *theSecretText = @"A secret"; - NSData *theSecret = [theSecretText dataUsingEncoding:kCFStringEncodingUTF8]; - NSString *theSecretUnpaddedBase64 = [MXBase64Tools unpaddedBase64FromData:theSecret]; - - NSString *theSecretId = @"theSecretId"; - - MXSecretStorage *secretStorage = aliceSession.legacyCrypto.secretStorage; - - NSError *error; - NSData *privateKey = [MXRecoveryKey decode:jsSDKDataRecoveryKey error:&error]; - - - // Build the key - NSDictionary *keys = @{ - secretStorage.defaultKeyId: privateKey - }; - - - // -> Store a new secret - [secretStorage storeSecret:theSecretUnpaddedBase64 withSecretId:theSecretId withSecretStorageKeys:keys success:^(NSString * _Nonnull secretId) { - - // Check the test is right - XCTAssertTrue([secretStorage hasSecretWithSecretId:theSecretId withSecretStorageKeyId:nil]); - - // - Delete it - [secretStorage deleteSecretWithSecretId:theSecretId success:^{ - - // -> The secret must have been deleted - XCTAssertFalse([secretStorage hasSecretWithSecretId:theSecretId withSecretStorageKeyId:nil]); - - [expectation fulfill]; - - } failure:^(NSError * _Nonnull error) { - XCTFail(@"The operation should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - - } failure:^(NSError * _Nonnull error) { - XCTFail(@"The operation should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - - }]; -} - @end #pragma clang diagnostic pop diff --git a/MatrixSDKTests/MXCryptoShareTests.m b/MatrixSDKTests/MXCryptoShareTests.m index 5f96246e84..7ce6c113bb 100644 --- a/MatrixSDKTests/MXCryptoShareTests.m +++ b/MatrixSDKTests/MXCryptoShareTests.m @@ -19,8 +19,6 @@ #import "MatrixSDKTestsData.h" #import "MatrixSDKTestsE2EData.h" -#import "MXCrypto_Private.h" -#import "MXCryptoStore.h" #import "MXMemoryStore.h" #import "MatrixSDKTestsSwiftHeader.h" @@ -57,731 +55,8 @@ - (void)tearDown [super tearDown]; } -// Import megolm session data as if they come from a response to a key share request -- (void)mimicKeyShareResponseForSession:(MXSession*)session withSessionData:(MXMegolmSessionData*)sessionData complete:(void (^)(void))complete -{ - [session.legacyCrypto importMegolmSessionDatas:@[sessionData] backUp:NO success:^(NSUInteger total, NSUInteger imported) { - complete(); - } failure:^(NSError *error) { - [matrixSDKTestsData breakTestCase:self reason:@"Cannot set up intial test conditions - error: %@", error]; - }]; -} - - -/** - Common initial conditions: - - Alice and Bob are in a room - - Bob sends messages - - Alice gets them decrypted - - Export partial and full megolm session data - - Log Alice on a new device - */ -- (void)createScenario:(void (^)(MXSession *aliceSession, NSString *roomId, MXMegolmSessionData *sessionData, MXMegolmSessionData *partialSessionData, XCTestExpectation *expectation))readyToTest -{ - // - Alice and Bob are in a room - [matrixSDKTestsE2EData doE2ETestWithAliceAndBobInARoom:self cryptedBob:YES warnOnUnknowDevices:NO readyToTest:^(MXSession *aliceSession, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation) { - - NSArray *messages = @[@"A", @"Z", @"E", @"R", @"T"]; - - // - Bob sends messages - MXRoom *roomFromBobPOV = [bobSession roomWithRoomId:roomId]; - [roomFromBobPOV sendTextMessage:messages[0] threadId:nil success:^(NSString *eventId) { - [roomFromBobPOV sendTextMessage:messages[1] threadId:nil success:^(NSString *eventId) { - [roomFromBobPOV sendTextMessage:messages[2] threadId:nil success:^(NSString *eventId) { - [roomFromBobPOV sendTextMessage:messages[3] threadId:nil success:^(NSString *eventId) { - [roomFromBobPOV sendTextMessage:messages[4] threadId:nil success:nil failure:nil]; - } failure:nil]; - } failure:nil]; - } failure:nil]; - } failure:^(NSError *error) { - [matrixSDKTestsData breakTestCase:self reason:@"Cannot set up intial test conditions - error: %@", error]; - }]; - - - // - Alice gets them decrypted - MXRoom *roomFromAlicePOV = [aliceSession roomWithRoomId:roomId]; - __block NSUInteger messagesCount = 0; - [roomFromAlicePOV liveTimeline:^(id liveTimeline) { - [liveTimeline listenToEventsOfTypes:@[kMXEventTypeStringRoomMessage] onEvent:^(MXEvent *event, MXTimelineDirection direction, MXRoomState *roomState) { - - if (++messagesCount == messages.count) - { - NSString *sessionId, *senderKey; - MXJSONModelSetString(sessionId, event.wireContent[@"session_id"]); - MXJSONModelSetString(senderKey, event.wireContent[@"sender_key"]); - - MXOlmInboundGroupSession *session = [aliceSession.legacyCrypto.store inboundGroupSessionWithId:sessionId andSenderKey:senderKey]; - XCTAssert(session); - - // - Export partial and full megolm session data - MXMegolmSessionData *sessionData = [session exportSessionDataAtMessageIndex:0]; - MXMegolmSessionData *partialSessionData = [session exportSessionDataAtMessageIndex:1]; - XCTAssert(sessionData); - XCTAssert(partialSessionData); - - // - Log Alice on a new device - [MXSDKOptions sharedInstance].enableCryptoWhenStartingMXSession = YES; - [matrixSDKTestsData relogUserSessionWithNewDevice:self session:aliceSession withPassword:MXTESTS_ALICE_PWD onComplete:^(MXSession *aliceSession2) { - [MXSDKOptions sharedInstance].enableCryptoWhenStartingMXSession = NO; - - readyToTest(aliceSession2, roomId, sessionData, partialSessionData, expectation); - }]; - } - }]; - }]; - }]; -} - - -/** - Check that a new device makes requests for keys of messages it cannot decrypt. - - - Have Alice and Bob in e2ee room with messages - - Alice signs in on a new device - - Alice2 paginates - -> Key share requests must be pending - -> Then, they must have been sent - */ -- (void)testKeyShareRequestFromNewDevice -{ - // - Have Alice and Bob in e2ee room with messages - [matrixSDKTestsE2EData doE2ETestWithAliceAndBobInARoomWithCryptedMessages:self cryptedBob:YES readyToTest:^(MXSession *aliceSession1, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation) { - - //- Alice signs in on a new device - [matrixSDKTestsE2EData loginUserOnANewDevice:self credentials:aliceSession1.matrixRestClient.credentials withPassword:MXTESTS_ALICE_PWD onComplete:^(MXSession *aliceSession2) { - - // - Alice2 paginates in the room - MXRoom *roomFromAlice2POV = [aliceSession2 roomWithRoomId:roomId]; - [roomFromAlice2POV liveTimeline:^(id liveTimeline) { - [liveTimeline resetPagination]; - [liveTimeline paginate:10 direction:MXTimelineDirectionBackwards onlyFromStore:NO complete:^{ - - // - Key share requests must be pending - XCTAssertNotNil([aliceSession2.legacyCrypto.store outgoingRoomKeyRequestWithState:MXRoomKeyRequestStateUnsent]); - - // Wait a bit - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{ - - // -> Then, they must have been sent - XCTAssertNil([aliceSession2.legacyCrypto.store outgoingRoomKeyRequestWithState:MXRoomKeyRequestStateUnsent]); - XCTAssertNotNil([aliceSession2.legacyCrypto.store outgoingRoomKeyRequestWithState:MXRoomKeyRequestStateSent]); - - // -> Alice2 should have received no keys - XCTAssertEqual(aliceSession2.legacyCrypto.store.inboundGroupSessions.count, 0); - [expectation fulfill]; - }); - - } failure:^(NSError *error) { - XCTFail(@"Cannot set up intial test conditions - error: %@", error); - [expectation fulfill]; - }]; - }]; - }]; - }]; -} - - -/** - Full flow for the nominal case: - Check that a new device gets messages keys from a device that trusts it. - - - Have Alice and Bob in e2ee room with messages - - Alice signs in on a new device - - Make each Alice device trust each other - - Alice2 paginates in the room - -> Key share requests must be pending --> After a bit, Alice2 should have received all keys - -> Key share requests should have complete - */ -- (void)testNominalCase -{ - // - Have Alice and Bob in e2ee room with messages - [matrixSDKTestsE2EData doE2ETestWithAliceAndBobInARoomWithCryptedMessages:self cryptedBob:YES readyToTest:^(MXSession *aliceSession1, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation) { - - //- Alice signs in on a new device - [matrixSDKTestsE2EData loginUserOnANewDevice:self credentials:aliceSession1.matrixRestClient.credentials withPassword:MXTESTS_ALICE_PWD onComplete:^(MXSession *aliceSession2) { - - NSString *aliceUserId = aliceSession1.matrixRestClient.credentials.userId; - - NSString *aliceSession1DeviceId = aliceSession1.matrixRestClient.credentials.deviceId; - NSString *aliceSession2DeviceId = aliceSession2.matrixRestClient.credentials.deviceId; - - // - Make each Alice device trust each other - // This simulates a self verification and trigger cross-signing behind the shell - [aliceSession1.crypto setDeviceVerification:MXDeviceVerified forDevice:aliceSession2DeviceId ofUser:aliceUserId success:^{ - [aliceSession2.crypto setDeviceVerification:MXDeviceVerified forDevice:aliceSession1DeviceId ofUser:aliceUserId success:^{ - - // - Alice2 pagingates in the room - MXRoom *roomFromAlice2POV = [aliceSession2 roomWithRoomId:roomId]; - if (!roomFromAlice2POV) { - XCTFail(@"Failed to fetch room"); - [expectation fulfill]; - } - - [roomFromAlice2POV liveTimeline:^(id liveTimeline) { - [liveTimeline resetPagination]; - [liveTimeline paginate:10 direction:MXTimelineDirectionBackwards onlyFromStore:NO complete:^{ - - // -> Key share requests must be pending - XCTAssertNotNil([aliceSession2.legacyCrypto.store outgoingRoomKeyRequestWithState:MXRoomKeyRequestStateUnsent]); - - // Wait a bit - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{ - - // -> After a bit, Alice2 should have received all keys - XCTAssertEqual(aliceSession2.legacyCrypto.store.inboundGroupSessions.count, aliceSession1.legacyCrypto.store.inboundGroupSessions.count); - - // -> Key share requests should have complete - XCTAssertNil([aliceSession2.legacyCrypto.store outgoingRoomKeyRequestWithState:MXRoomKeyRequestStateUnsent]); - XCTAssertNil([aliceSession2.legacyCrypto.store outgoingRoomKeyRequestWithState:MXRoomKeyRequestStateSent]); - - [expectation fulfill]; - }); - - } failure:^(NSError *error) { - XCTFail(@"Cannot set up intial test conditions - error: %@", error); - [expectation fulfill]; - }]; - }]; - - } failure:^(NSError *error) { - XCTFail(@"Cannot set up intial test conditions - error: %@", error); - [expectation fulfill]; - }]; - } failure:^(NSError *error) { - XCTFail(@"Cannot set up intial test conditions - error: %@", error); - [expectation fulfill]; - }]; - }]; - }]; -} - - -/** - Same test as testNominalCase but key share requests are disabled, then re-enabled. - - - Have Alice and Bob in e2ee room with messages - - Alice signs in on a new device - - Disable key share requests on Alice2 - - Make each Alice device trust each other - - Alice2 paginates in the room - -> Key share requests must be still pending - - Enable key share requests on Alice2 - -> Key share requests should have complete - */ -- (void)testDisableKeyShareRequest -{ - // - Have Alice and Bob in e2ee room with messages - [matrixSDKTestsE2EData doE2ETestWithAliceAndBobInARoomWithCryptedMessages:self cryptedBob:YES readyToTest:^(MXSession *aliceSession1, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation) { - - //- Alice signs in on a new device - [matrixSDKTestsE2EData loginUserOnANewDevice:self credentials:aliceSession1.matrixRestClient.credentials withPassword:MXTESTS_ALICE_PWD onComplete:^(MXSession *aliceSession2) { - - // - Disable key share requests on Alice2 - [aliceSession2.legacyCrypto setOutgoingKeyRequestsEnabled:NO onComplete:nil]; - aliceSession2.legacyCrypto.enableOutgoingKeyRequestsOnceSelfVerificationDone = NO; - - NSString *aliceUserId = aliceSession1.matrixRestClient.credentials.userId; - - NSString *aliceSession1DeviceId = aliceSession1.matrixRestClient.credentials.deviceId; - NSString *aliceSession2DeviceId = aliceSession2.matrixRestClient.credentials.deviceId; - - // - Make each Alice device trust each other - // This simulates a self verification and trigger cross-signing behind the shell - [aliceSession1.crypto setDeviceVerification:MXDeviceVerified forDevice:aliceSession2DeviceId ofUser:aliceUserId success:^{ - [aliceSession2.crypto setDeviceVerification:MXDeviceVerified forDevice:aliceSession1DeviceId ofUser:aliceUserId success:^{ - - // - Alice2 pagingates in the room - MXRoom *roomFromAlice2POV = [aliceSession2 roomWithRoomId:roomId]; - [roomFromAlice2POV liveTimeline:^(id liveTimeline) { - [liveTimeline resetPagination]; - [liveTimeline paginate:10 direction:MXTimelineDirectionBackwards onlyFromStore:NO complete:^{ - - // Wait a bit - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{ - - // -> Key share requests must be pending - XCTAssertNotNil([aliceSession2.legacyCrypto.store outgoingRoomKeyRequestWithState:MXRoomKeyRequestStateUnsent]); - - // - Enable key share requests on Alice2 - [aliceSession2.legacyCrypto setOutgoingKeyRequestsEnabled:YES onComplete:^{ - - // Wait a bit - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{ - - // -> Key share requests should have complete - XCTAssertNil([aliceSession2.legacyCrypto.store outgoingRoomKeyRequestWithState:MXRoomKeyRequestStateUnsent]); - XCTAssertNil([aliceSession2.legacyCrypto.store outgoingRoomKeyRequestWithState:MXRoomKeyRequestStateSent]); - [expectation fulfill]; - - }); - }]; - }); - - } failure:^(NSError *error) { - XCTFail(@"Cannot set up intial test conditions - error: %@", error); - [expectation fulfill]; - }]; - }]; - - } failure:^(NSError *error) { - XCTFail(@"Cannot set up intial test conditions - error: %@", error); - [expectation fulfill]; - }]; - } failure:^(NSError *error) { - XCTFail(@"Cannot set up intial test conditions - error: %@", error); - [expectation fulfill]; - }]; - }]; - }]; -} - - -/** - Tests that we get keys from backup rather than key share requests on a new verified sign-in. - This demonstrates what happens on Riot when completing the security of a new sign-in. - - - Have Alice and Bob in e2ee room with messages - - Alice sets up a backup - - Alice signs in on a new device - - Disable key share requests on Alice2 - - Alice2 paginates in the room - - Make each Alice device trust each other - -> After a bit, Alice2 should have all keys - -> key share requests on Alice2 are enabled again - -> No m.room_key_request have been made - */ -- (void)testNoKeyShareRequestIfThereIsABackup -{ - // - Have Alice and Bob in e2ee room with messages - [matrixSDKTestsE2EData doE2ETestWithAliceAndBobInARoomWithCryptedMessages:self cryptedBob:YES readyToTest:^(MXSession *aliceSession1, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation) { - - // - Alice set up a backup - [aliceSession1.crypto.backup prepareKeyBackupVersionWithPassword:nil algorithm:nil success:^(MXMegolmBackupCreationInfo * _Nonnull keyBackupCreationInfo) { - [aliceSession1.crypto.backup createKeyBackupVersion:keyBackupCreationInfo success:^(MXKeyBackupVersion * _Nonnull keyBackupVersion) { - [aliceSession1.crypto.backup backupAllGroupSessions:^{ - - - //- Alice signs in on a new device - [matrixSDKTestsE2EData loginUserOnANewDevice:self credentials:aliceSession1.matrixRestClient.credentials withPassword:MXTESTS_ALICE_PWD onComplete:^(MXSession *aliceSession2) { - - // - Disable key share requests on Alice2 - [aliceSession2.legacyCrypto setOutgoingKeyRequestsEnabled:NO onComplete:nil]; - - - // - Alice2 pagingates in the room - MXRoom *roomFromAlice2POV = [aliceSession2 roomWithRoomId:roomId]; - [roomFromAlice2POV liveTimeline:^(id liveTimeline) { - [liveTimeline resetPagination]; - [liveTimeline paginate:10 direction:MXTimelineDirectionBackwards onlyFromStore:NO complete:^{ - - - NSString *aliceUserId = aliceSession1.matrixRestClient.credentials.userId; - NSString *aliceSession1DeviceId = aliceSession1.matrixRestClient.credentials.deviceId; - NSString *aliceSession2DeviceId = aliceSession2.matrixRestClient.credentials.deviceId; - - // - Make each Alice device trust each other - // This simulates a self verification and trigger cross-signing behind the shell - [aliceSession1.crypto setDeviceVerification:MXDeviceVerified forDevice:aliceSession2DeviceId ofUser:aliceUserId success:^{ - [aliceSession2.crypto setDeviceVerification:MXDeviceVerified forDevice:aliceSession1DeviceId ofUser:aliceUserId success:^{ - - - // Wait a bit that gossip happens - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{ - - // -> After a bit, Alice2 should have all keys - XCTAssertEqual(aliceSession2.legacyCrypto.store.inboundGroupSessions.count, aliceSession1.legacyCrypto.store.inboundGroupSessions.count); - - // -> key share requests on Alice2 are enabled again - XCTAssertTrue(aliceSession2.legacyCrypto.isOutgoingKeyRequestsEnabled); - - [expectation fulfill]; - }); - - } failure:^(NSError *error) { - XCTFail(@"Cannot set up intial test conditions - error: %@", error); - [expectation fulfill]; - }]; - } failure:^(NSError *error) { - XCTFail(@"Cannot set up intial test conditions - error: %@", error); - [expectation fulfill]; - }]; - - - - } failure:^(NSError *error) { - XCTFail(@"Cannot set up intial test conditions - error: %@", error); - [expectation fulfill]; - }]; - }]; - }]; - - - - } progress:^(NSProgress * _Nonnull backupProgress) { - } failure:^(NSError * _Nonnull error) { - XCTFail(@"Cannot set up intial test conditions - error: %@", error); - [expectation fulfill]; - }]; - } failure:^(NSError * _Nonnull error) { - XCTFail(@"Cannot set up intial test conditions - error: %@", error); - [expectation fulfill]; }]; - } failure:^(NSError * _Nonnull error) { - XCTFail(@"Cannot set up intial test conditions - error: %@", error); - [expectation fulfill]; - }]; - - - // -> No m.room_key_request have been made - [HTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) { - XCTAssertFalse([request.URL.absoluteString containsString:@"m.room_key_request"]); - return NO; - } withStubResponse:^HTTPStubsResponse*(NSURLRequest *request) { - return nil; - }]; - - }]; -} - - -/** - Test that a partial shared session does not cancel key share requests. - - From the scenario: - - Make Alice paginate back in the room - -> There must be pending outgoing key share request - - Import the partial megolm session data as if they come from key sharing - -> The outgoing key share request should still exist - - Import the full megolm session data as if they come from key sharing - -> There should be no more outgoing key share request - */ -- (void)testPartialSharedSession -{ - [self createScenario:^(MXSession *aliceSession, NSString *roomId, MXMegolmSessionData *sessionData, MXMegolmSessionData *partialSessionData, XCTestExpectation *expectation) { - - // - Make Alice paginate back in the room - MXRoom *roomFromAlicePOV = [aliceSession roomWithRoomId:roomId]; - [roomFromAlicePOV liveTimeline:^(id liveTimeline) { - - [liveTimeline resetPagination]; - [liveTimeline paginate:30 direction:MXTimelineDirectionBackwards onlyFromStore:NO complete:^{ - - [matrixSDKTestsE2EData outgoingRoomKeyRequestInSession:aliceSession complete:^(MXOutgoingRoomKeyRequest *outgoingRoomKeyRequest) { - - // -> There must be pending outgoing key share request - XCTAssert(outgoingRoomKeyRequest); - - // - Import the partial megolm session data as if they come from key sharing - [self mimicKeyShareResponseForSession:aliceSession withSessionData:partialSessionData complete:^{ - - // -> The outgoing key share request should still exist - [matrixSDKTestsE2EData outgoingRoomKeyRequestInSession:aliceSession complete:^(MXOutgoingRoomKeyRequest *pendingOutgoingRoomKeyRequest) { - - XCTAssertEqualObjects(pendingOutgoingRoomKeyRequest.requestId, outgoingRoomKeyRequest.requestId); - - // - Import the full megolm session data as if they come from key sharing - [self mimicKeyShareResponseForSession:aliceSession withSessionData:sessionData complete:^{ - - [matrixSDKTestsE2EData outgoingRoomKeyRequestInSession:aliceSession complete:^(MXOutgoingRoomKeyRequest *stillPendingOutgoingRoomKeyRequest) { - - // -> There should be no more outgoing key share request - XCTAssertNil(stillPendingOutgoingRoomKeyRequest); - - [expectation fulfill]; - }]; - }]; - }]; - }]; - - }]; - } failure:^(NSError *error) { - XCTFail(@"The request should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - }]; - }]; -} - -/** - Test that a shared session that is better (ie, lower index) than what we have - in the store is correctly imported. - - From the scenario: - - Import the partial megolm session data - -> It must be successfully imported - - Import the full megolm session data - -> It must be successfully imported - */ -- (void)testBetterSharedSession -{ - [self createScenario:^(MXSession *aliceSession, NSString *roomId, MXMegolmSessionData *sessionData, MXMegolmSessionData *partialSessionData, XCTestExpectation *expectation) { - - // - Import the partial megolm session data - [aliceSession.legacyCrypto importMegolmSessionDatas:@[partialSessionData] backUp:NO success:^(NSUInteger total, NSUInteger imported) { - - // -> It must be successfully imported - XCTAssertEqual(imported, 1); - - // - Import the full megolm session dats - [aliceSession.legacyCrypto importMegolmSessionDatas:@[sessionData] backUp:NO success:^(NSUInteger total, NSUInteger imported) { - - // -> It must be successfully imported - XCTAssertEqual(imported, 1); - [expectation fulfill]; - - } failure:^(NSError *error) { - XCTFail(@"The request should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - } failure:^(NSError *error) { - XCTFail(@"The request should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - }]; -} - -/** - Test that a shared session that is not better (ie, higher index) than what we have - in the store does not overwrite what we have. - - From the scenario: - - Import the full megolm session data - -> It must be successfully imported - - Import the partial megolm session data - -> It must not be imported - */ -- (void)testNotBetterSharedSession -{ - [self createScenario:^(MXSession *aliceSession, NSString *roomId, MXMegolmSessionData *sessionData, MXMegolmSessionData *partialSessionData, XCTestExpectation *expectation) { - - // - Import the full megolm session data - [aliceSession.legacyCrypto importMegolmSessionDatas:@[sessionData] backUp:NO success:^(NSUInteger total, NSUInteger imported) { - - // -> It must be successfully imported - XCTAssertEqual(imported, 1); - - // - Import the partial megolm session data - [aliceSession.legacyCrypto importMegolmSessionDatas:@[partialSessionData] backUp:NO success:^(NSUInteger total, NSUInteger imported) { - - // -> It must not be imported - XCTAssertEqual(imported, 0); - [expectation fulfill]; - - } failure:^(NSError *error) { - XCTFail(@"The request should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - } failure:^(NSError *error) { - XCTFail(@"The request should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - }]; -} - -/** - Test that we share the correct session keys for encrypted rooms when inviting - another user to the room, so that they can read any immediate context relevant - to their invite. - - - Alice creates a new room - -> She has no inbound session keys so far - - Alice sends one message to the room - -> Alice has one inbound session keys - - She changes the room's history visibility to not shared and sends another message - -> Alice now has two inbdound session keys, one with `sharedHistory` true and the other false - - She changes the visibility back to shared and sends last message - -> Alice now has 3 keys, 2 with `sharedHistory`, one without - - Alice invites Bob into the room - -> Bob has recieved only 2 session keys, namely those with `sharedHistory` set to true - */ -- (void)testShareHistoryKeysWithInvitedUser -{ - [matrixSDKTestsE2EData doE2ETestWithAliceInARoom:self - andStore:[[MXMemoryStore alloc] init] - readyToTest:^(MXSession *aliceSession, NSString *roomId, XCTestExpectation *expectation) - { - void (^failureBlock)(NSError *) = ^(NSError *error) - { - XCTFail("Test failure - %@", error); - [expectation fulfill]; - }; - - [MXSDKOptions sharedInstance].enableRoomSharedHistoryOnInvite = YES; - [MXSDKOptions sharedInstance].enableCryptoWhenStartingMXSession = YES; - [matrixSDKTestsData doMXSessionTestWithBob:nil - readyToTest:^(MXSession *bobSession, XCTestExpectation *expectation2) - { - // No keys present at the beginning - MXRoom *room = [aliceSession roomWithRoomId:roomId]; - XCTAssertEqual([self numberOfKeysInSession:aliceSession], 0); - - [self sendMessage:@"Hello" room:room success:^{ - // Sending one message will create the first session - XCTAssertEqual([self numberOfKeysInSession:aliceSession], 1); - - [self setHistoryVisibility:kMXRoomHistoryVisibilityJoined room:room success:^{ - [self sendMessage:@"Hi" room:room success:^{ - // The room visibility has changed, so sending another message will rotate - // megolm sessions, increasing to total of 2 - XCTAssertEqual([self numberOfKeysInSession:aliceSession], 2); - - [self setHistoryVisibility:kMXRoomHistoryVisibilityShared room:room success:^{ - [self sendMessage:@"How are you?" room:room success:^{ - // The room visibility has changed again, so another rotation leads to 3 sessions - XCTAssertEqual([self numberOfKeysInSession:aliceSession], 3); - - // Finally inviting a user (the outcome of this captured in the notification listener) - [room inviteUser:bobSession.myUser.userId success:^{ - } failure:failureBlock]; - } failure:failureBlock]; - } failure:failureBlock]; - } failure:failureBlock]; - } failure:failureBlock]; - } failure:failureBlock]; - - // Listen to a notification of to_device events, which will store keys on Bob's device - __block id observer = [[NSNotificationCenter defaultCenter] addObserverForName:kMXSessionOnToDeviceEventNotification - object:bobSession - queue:[NSOperationQueue mainQueue] - usingBlock:^(NSNotification *notif) - { - - // Give some extra time, as we are storing keys in Realm - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{ - if (observer) - { - [[NSNotificationCenter defaultCenter] removeObserver:observer]; - observer = nil; - - // Bob should only have recieved 2 keys, as the third Alice's key has `sharedHistory` set to false - XCTAssertEqual([self numberOfKeysInSession:bobSession], 2); - [expectation fulfill]; - } - }); - }]; - }]; - }]; -} - -/** - Test that we preserve the `sharedHistory` flag as we pass keys between different devices - and different users - - - Alice creates a new room, sends a few messages and logs in with another device - -> Her second device has the same amount of keys as the first, even if some of the keys do not have shared history - - Alice invites Bob into the room from her second device - -> Bob has recieved only 2 session keys, namely those with `sharedHistory` set to true - */ -- (void)testSharedHistoryPreservedWhenForwardingKeys -{ - [matrixSDKTestsE2EData doE2ETestWithAliceInARoom:self - andStore:[[MXMemoryStore alloc] init] - readyToTest:^(MXSession *aliceSession1, NSString *roomId, XCTestExpectation *expectation) - { - void (^failureBlock)(NSError *) = ^(NSError *error) - { - XCTFail("Test failure - %@", error); - [expectation fulfill]; - }; - - [MXSDKOptions sharedInstance].enableRoomSharedHistoryOnInvite = YES; - [MXSDKOptions sharedInstance].enableCryptoWhenStartingMXSession = YES; - [matrixSDKTestsData doMXSessionTestWithBob:nil - readyToTest:^(MXSession *bobSession, XCTestExpectation *expectation2) - { - - // Send a bunch of messages whilst changing room visibility - MXRoom *room = [aliceSession1 roomWithRoomId:roomId]; - [self sendMessage:@"Hello" room:room success:^{ - [self setHistoryVisibility:kMXRoomHistoryVisibilityJoined room:room success:^{ - [self sendMessage:@"Hi" room:room success:^{ - [self setHistoryVisibility:kMXRoomHistoryVisibilityShared room:room success:^{ - [self sendMessage:@"How are you?" room:room success:^{ - - // Alice signs in on a new device - [matrixSDKTestsE2EData loginUserOnANewDevice:self - credentials:aliceSession1.matrixRestClient.credentials - withPassword:MXTESTS_ALICE_PWD store:[[MXMemoryStore alloc] init] - onComplete:^(MXSession *aliceSession2) - { - - // Initially Alice2 has no keys - XCTAssertEqual([self numberOfKeysInSession:aliceSession2], 0); - - __block id observer = [[NSNotificationCenter defaultCenter] addObserverForName:kMXSessionNewRoomNotification - object:aliceSession2 - queue:[NSOperationQueue mainQueue] - usingBlock:^(NSNotification *notif) - { - if (!observer) { return; } - [[NSNotificationCenter defaultCenter] removeObserver:observer]; - observer = nil; - - // Make each Alice device trust each other - [aliceSession1.crypto setDeviceVerification:MXDeviceVerified forDevice:aliceSession2.myDeviceId ofUser:aliceSession1.myUserId success:^{ - [aliceSession2.crypto setDeviceVerification:MXDeviceVerified forDevice:aliceSession1.myDeviceId ofUser:aliceSession1.myUserId success:^{ - - // Alice2 paginates in the room to get the keys forwarded to her - MXRoom *roomFromAlice2POV = [aliceSession2 roomWithRoomId:roomId]; - [roomFromAlice2POV liveTimeline:^(id liveTimeline) { - [liveTimeline resetPagination]; - [liveTimeline paginate:10 direction:MXTimelineDirectionBackwards onlyFromStore:NO complete:^{ - - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{ - - // Alice2 now has all 3 keys, despite only two of them having shared history - XCTAssertEqual([self numberOfKeysInSession:aliceSession2], 3); - - // Now Alice2 invites Bob into the conversation - [roomFromAlice2POV inviteUser:bobSession.myUser.userId success:^{ - } failure:failureBlock]; - }); - } failure:failureBlock]; - }]; - } failure:failureBlock]; - } failure:failureBlock]; - }]; - }]; - } failure:failureBlock]; - } failure:failureBlock]; - } failure:failureBlock]; - } failure:failureBlock]; - } failure:failureBlock]; - - // Listen to a notification of to_device events, which will store keys on Bob's device - __block id observer = [[NSNotificationCenter defaultCenter] addObserverForName:kMXSessionOnToDeviceEventNotification - object:bobSession - queue:[NSOperationQueue mainQueue] - usingBlock:^(NSNotification *notif) - { - - // Give some extra time, sa we are storing keys in Realm - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{ - if (observer) - { - [[NSNotificationCenter defaultCenter] removeObserver:observer]; - observer = nil; - - // Bob should only have recieved 2 keys, as the third Alice's key has `sharedHistory` set to false - XCTAssertEqual([self numberOfKeysInSession:bobSession], 2); - [expectation fulfill]; - } - }); - }]; - }]; - }]; -} - #pragma mark - Helpers -/** - Get number of inbound keys stored in a session - */ -- (NSUInteger)numberOfKeysInSession:(MXSession *)session -{ - return [session.legacyCrypto.store inboundGroupSessions].count; -} - /** Send message and await its delivery */ diff --git a/MatrixSDKTests/MXCryptoTests.m b/MatrixSDKTests/MXCryptoTests.m index 36b50f5138..03664f26b7 100644 --- a/MatrixSDKTests/MXCryptoTests.m +++ b/MatrixSDKTests/MXCryptoTests.m @@ -21,16 +21,12 @@ #import "MatrixSDKTestsE2EData.h" #import "MXSession.h" -#import "MXCrypto_Private.h" #import "MXMegolmExportEncryption.h" -#import "MXDeviceListOperation.h" #import "MXFileStore.h" #import "MXSDKOptions.h" #import "MXTools.h" #import "MXSendReplyEventDefaultStringLocalizer.h" -#import "MXOutboundSessionInfo.h" -#import #import "MXLRUCache.h" #import "MatrixSDKTestsSwiftHeader.h" @@ -87,7 +83,6 @@ - (NSUInteger)checkEncryptedEvent:(MXEvent*)event roomId:(NSString*)roomId clear XCTAssertNotNil(event.wireContent[@"ciphertext"]); XCTAssertNotNil(event.wireContent[@"session_id"]); XCTAssertNotNil(event.wireContent[@"sender_key"]); - XCTAssertEqualObjects(event.wireContent[@"device_id"], senderSession.legacyCrypto.store.deviceId); // Check decrypted data XCTAssert(event.eventId); @@ -159,194 +154,6 @@ - (void)testMXSDKOptionsEnableCryptoWhenOpeningMXSession }]; } -- (void)testCryptoNoDeviceId -{ - [matrixSDKTestsData doMXSessionTestWithBob:self readyToTest:^(MXSession *mxSession, XCTestExpectation *expectation) { - // Simulate no device id provided by the home server - mxSession.matrixRestClient.credentials.deviceId = nil; - - [mxSession enableCrypto:YES success:^{ - - XCTAssertGreaterThan(mxSession.legacyCrypto.store.deviceId.length, 0, "If the hs did not provide a device id, the crypto module must create one"); - [expectation fulfill]; - - } failure:^(NSError *error) { - XCTFail(@"The request should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - - }]; -} - -- (void)testCryptoPersistenceInStore -{ - [matrixSDKTestsData doMXSessionTestWithBob:self readyToTest:^(MXSession *mxSession, XCTestExpectation *expectation) { - - XCTAssertNil(mxSession.crypto, @"Crypto is disabled by default"); - - __block MXSession *mxSession2 = mxSession; - - [mxSession enableCrypto:YES success:^{ - - XCTAssert(mxSession2.crypto); - - NSString *deviceCurve25519Key = mxSession2.legacyCrypto.olmDevice.deviceCurve25519Key; - NSString *deviceEd25519Key = mxSession2.legacyCrypto.olmDevice.deviceEd25519Key; - - NSArray *myUserDevices = [mxSession2.legacyCrypto.deviceList storedDevicesForUser:mxSession.myUserId]; - XCTAssertEqual(myUserDevices.count, 1); - - MXRestClient *bobRestClient = mxSession2.matrixRestClient; - [mxSession2 close]; - mxSession2 = nil; - - // Reopen the session - MXFileStore *store = [[MXFileStore alloc] init]; - - mxSession2 = [[MXSession alloc] initWithMatrixRestClient:bobRestClient]; - [matrixSDKTestsData retain:mxSession2]; - - [mxSession2 setStore:store success:^{ - - XCTAssert(mxSession2.crypto, @"MXSession must recall that it has crypto engaged"); - - XCTAssertEqualObjects(deviceCurve25519Key, mxSession2.legacyCrypto.olmDevice.deviceCurve25519Key); - XCTAssertEqualObjects(deviceEd25519Key, mxSession2.legacyCrypto.olmDevice.deviceEd25519Key); - - NSArray *myUserDevices2 = [mxSession2.legacyCrypto.deviceList storedDevicesForUser:mxSession2.myUser.userId]; - XCTAssertEqual(myUserDevices2.count, 1); - - XCTAssertEqualObjects(myUserDevices[0].deviceId, myUserDevices2[0].deviceId); - - [expectation fulfill]; - - } failure:^(NSError *error) { - XCTFail(@"Cannot set up intial test conditions - error: %@", error); - [expectation fulfill]; - }]; - - } failure:^(NSError *error) { - XCTFail(@"The request should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - - }]; -} - -- (void)testMultipleDownloadKeys -{ - [matrixSDKTestsE2EData doE2ETestWithBobAndAlice:self readyToTest:^(MXSession *bobSession, MXSession *aliceSession, XCTestExpectation *expectation) { - - __block NSUInteger count = 0; - void(^onSuccess)(void) = ^(void) { - - if (++count == 2) - { - MXHTTPOperation *operation = [aliceSession.legacyCrypto.deviceList downloadKeys:@[bobSession.myUser.userId] forceDownload:NO success:nil failure:nil]; - - XCTAssertNil(operation, "@Alice shouldn't do another /query when the user devices are in the store"); - - // Check deviceTrackingStatus in store - NSDictionary *deviceTrackingStatus = [aliceSession.legacyCrypto.store deviceTrackingStatus]; - MXDeviceTrackingStatus bobTrackingStatus = MXDeviceTrackingStatusFromNSNumber(deviceTrackingStatus[bobSession.myUser.userId]); - XCTAssertEqual(bobTrackingStatus, MXDeviceTrackingStatusUpToDate); - - [expectation fulfill]; - } - }; - - MXHTTPOperation *operation1 = [aliceSession.legacyCrypto.deviceList downloadKeys:@[bobSession.myUser.userId] forceDownload:NO success:^(MXUsersDevicesMap *usersDevicesInfoMap, NSDictionary *crossSigningKeysMap) { - - onSuccess(); - - } failure:^(NSError *error) { - XCTFail(@"The request should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - - XCTAssertNotNil(operation1); - XCTAssert([operation1 isKindOfClass:MXDeviceListOperation.class], @"Returned object must be indeed a MXDeviceListOperation object"); - - // Check deviceTrackingStatus in store - NSDictionary *deviceTrackingStatus = [aliceSession.legacyCrypto.store deviceTrackingStatus]; - MXDeviceTrackingStatus bobTrackingStatus = MXDeviceTrackingStatusFromNSNumber(deviceTrackingStatus[bobSession.myUser.userId]); - XCTAssertEqual(bobTrackingStatus, MXDeviceTrackingStatusDownloadInProgress); - - - // A parallel operation - MXHTTPOperation *operation2 = [aliceSession.legacyCrypto.deviceList downloadKeys:@[bobSession.myUser.userId] forceDownload:NO success:^(MXUsersDevicesMap *usersDevicesInfoMap, NSDictionary *crossSigningKeysMap) { - - onSuccess(); - - } failure:^(NSError *error) { - XCTFail(@"The request should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - - XCTAssert(operation2); - XCTAssert([operation2 isKindOfClass:MXDeviceListOperation.class], @"Returned object must be indeed a MXDeviceListOperation object"); - - XCTAssertEqual(operation1.operation, operation2.operation, @"The 2 MXDeviceListOperations must share the same http request query from the same MXDeviceListOperationsPool"); - }]; -} - -// TODO: test others scenarii like -// - We are downloading keys for [a,b], ask the download for [b,c] in //. -// - We are downloading keys for [a,b], ask the download for [a] in //. The 1st download fails for network reason. The 2nd should then succeed. -// - We are downloading keys for [a,b,c], ask the download for [a,b] in //. The 1st download returns only keys for [a,b] because c'hs is down. The 2nd should succeed. -// - We are downloading keys for [a,b,c], ask the download for [c] in //. The 1st download returns only keys for [a,b] because c'hs is down. The 2nd should fail (or complete but with an indication TBD) - -- (void)testDownloadKeysForUserWithNoDevice -{ - // No device = non-e2e-capable device - - [matrixSDKTestsE2EData doE2ETestWithAliceAndBobInARoom:self cryptedBob:NO warnOnUnknowDevices:NO readyToTest:^(MXSession *aliceSession, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation) { - [aliceSession.legacyCrypto.deviceList downloadKeys:@[bobSession.myUser.userId] forceDownload:NO success:^(MXUsersDevicesMap *usersDevicesInfoMap, NSDictionary *crossSigningKeysMap) { - - NSArray *bobDevices = [usersDevicesInfoMap deviceIdsForUser:bobSession.myUser.userId]; - XCTAssertNotNil(bobDevices, @"[MXCrypto downloadKeys] should return @[] for Bob to distinguish him from an unknown user"); - XCTAssertEqual(0, bobDevices.count); - - MXHTTPOperation *operation = [aliceSession.legacyCrypto.deviceList downloadKeys:@[bobSession.myUser.userId] forceDownload:NO success:nil failure:^(NSError *error) { - XCTFail(@"The request should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - - XCTAssertNil(operation, "@Alice shouldn't do a second /query for non-e2e-capable devices"); - [expectation fulfill]; - - } failure:^(NSError *error) { - XCTFail(@"The request should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - - }]; -} - -- (void)testDownloadKeysWithUnreachableHS -{ - [matrixSDKTestsE2EData doE2ETestWithBobAndAlice:self readyToTest:^(MXSession *bobSession, MXSession *aliceSession, XCTestExpectation *expectation) { - // Try to get info from a user on matrix.org. - // The local hs we use for tests is not federated and is not able to talk with matrix.org - [aliceSession.legacyCrypto.deviceList downloadKeys:@[bobSession.myUser.userId, @"@auser:matrix.org"] forceDownload:NO success:^(MXUsersDevicesMap *usersDevicesInfoMap, NSDictionary *crossSigningKeysMap) { - - // We can get info only for Bob - XCTAssertEqual(1, usersDevicesInfoMap.map.count); - - NSArray *bobDevices = [usersDevicesInfoMap deviceIdsForUser:bobSession.myUser.userId]; - XCTAssertNotNil(bobDevices); - - [expectation fulfill]; - - } failure:^(NSError *error) { - XCTFail(@"The request should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - - }]; -} - - #pragma mark - MXSession // Test MXSession.event(withEventId:) @@ -380,43 +187,6 @@ - (void)testMXSessionEventWithEventId }]; } - -#pragma mark - MXRoom -- (void)testRoomIsEncrypted -{ - [MXSDKOptions sharedInstance].enableCryptoWhenStartingMXSession = YES; - - [matrixSDKTestsData doMXSessionTestWithBob:self readyToTest:^(MXSession *mxSession, XCTestExpectation *expectation) { - - [MXSDKOptions sharedInstance].enableCryptoWhenStartingMXSession = NO; - - [mxSession createRoom:@{} success:^(MXRoom *room) { - - XCTAssertFalse(room.summary.isEncrypted); - - [room enableEncryptionWithAlgorithm:kMXCryptoMegolmAlgorithm success:^{ - - XCTAssert(room.summary.isEncrypted); - - // mxSession.crypto.store is a private member - // and should be used only from the cryptoQueue. Particularly for this test - dispatch_async(mxSession.legacyCrypto.cryptoQueue, ^{ - XCTAssertEqualObjects(kMXCryptoMegolmAlgorithm, [mxSession.legacyCrypto.store algorithmForRoom:room.roomId]); - - [expectation fulfill]; - }); - - } failure:^(NSError *error) { - XCTFail(@"The request should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - } failure:^(NSError *error) { - XCTFail(@"Cannot set up intial test conditions - error: %@", error); - [expectation fulfill]; - }]; - }]; -} - - (void)testAliceInACryptedRoom { [matrixSDKTestsE2EData doE2ETestWithAliceInARoom:self readyToTest:^(MXSession *aliceSession, NSString *roomId, XCTestExpectation *expectation) { @@ -446,119 +216,6 @@ - (void)testAliceInACryptedRoom }]; } -// Test various scenarios in which encryption of a room is disabled, incl: -// - event is not a message, but a reaction -// - crypto module is not present -// - room encryption is not set but is fixed -// - room encryption is not set in neither crypto nor summary store -- (void)testAliceInACryptedRoomWithoutEncryption -{ - [matrixSDKTestsE2EData doE2ETestWithAliceInARoom:self - readyToTest:^(MXSession *session, NSString *roomId, XCTestExpectation *expectation) - { - // Prepare room and event to be sent - MXLegacyCrypto *crypto = session.legacyCrypto; - MXRoom *room = [session roomWithRoomId:roomId]; - NSString *message = @"Hello myself!"; - NSDictionary *content = @{ - kMXMessageTypeKey: kMXMessageTypeText, - kMXMessageBodyKey: message - }; - - void (^failureBlock)(NSError *) = ^(NSError *error) - { - XCTFail("Test failure - %@", error); - [expectation fulfill]; - }; - - // A few helper methods that enable or disable aspects of state which is usually - // not mutable in production code, but could happen as a result of data race, - // or memory / state corruption - void (^enableCryptoModule)(BOOL) = ^(BOOL isCryptoEnabled){ - [session setValue:isCryptoEnabled ? crypto : nil forKey:@"crypto"]; - }; - - void (^enableRoomAlgorithm)(BOOL) = ^(BOOL isAlgorithmEnabled){ - [crypto.store storeAlgorithmForRoom:roomId algorithm:isAlgorithmEnabled ? @"abc" : nil]; - }; - - void (^enableSummaryEncryption)(BOOL) = ^(BOOL isSummaryEncrypted){ - [room.summary setValue:@(isSummaryEncrypted) forKey:@"_isEncrypted"]; - }; - - // Room is encrypted by default - XCTAssertTrue(room.summary.isEncrypted); - - // 1. Send the first event as message - [self sendEventOfType:kMXEventTypeStringRoomMessage - content:content - room:room - success:^(MXEvent *event) { - - // At this point we expect the message to be properly encrypted - XCTAssertEqual(event.wireEventType, MXEventTypeRoomEncrypted); - XCTAssertEqual(0, [self checkEncryptedEvent:event roomId:roomId clearMessage:message senderSession:session]); - - // 2. Send an event of reaction type, which does not require encryption - [self sendEventOfType:kMXEventTypeStringReaction - content:content - room:room - success:^(MXEvent *event) { - - // Event is indeed not encrypted - XCTAssertTrue(room.summary.isEncrypted); - XCTAssertNotEqual(event.wireEventType, MXEventTypeRoomEncrypted); - - // 3. Send the third message whilst simulating the loss of crypto module - // (e.g. some corruption or memory deallocation) - enableCryptoModule(NO); - [self sendEventOfType:kMXEventTypeStringRoomMessage - content:content - room:room - success:^(MXEvent *event) { - - // Event is not encrypted, even though it should be (error logs will be printed) - XCTAssertTrue(room.summary.isEncrypted); - XCTAssertNotEqual(event.wireEventType, MXEventTypeRoomEncrypted); - - // 4. Re-enable crypto module but erase the encryption for the room (both in crypto store and summary). - // This is not possible in production code, but simulates data corruption or memory less - enableCryptoModule(YES); - enableRoomAlgorithm(NO); - enableSummaryEncryption(NO); - [self sendEventOfType:kMXEventTypeStringRoomMessage - content:content - room:room - success:^(MXEvent *event) { - - // Event indeed not encrypted - XCTAssertFalse(room.summary.isEncrypted); - XCTAssertNotEqual(event.wireEventType, MXEventTypeRoomEncrypted); - - // 5. This time we store an algoritm in crypto store but keep summary as not encrypted. We expect - // the state of the summary to be restored and for the event to be encrypted again - enableRoomAlgorithm(YES); - enableSummaryEncryption(NO); - [self sendEventOfType:kMXEventTypeStringRoomMessage - content:content - room:room - success:^(MXEvent *event) { - - // The system detects that there is an inconsistency between crypto and summary store, - // and restores the encryption - XCTAssertTrue(room.summary.isEncrypted); - XCTAssertEqual(event.wireEventType, MXEventTypeRoomEncrypted); - XCTAssertEqual(0, [self checkEncryptedEvent:event roomId:roomId clearMessage:message senderSession:session]); - [expectation fulfill]; - - } failure:failureBlock]; - } failure:failureBlock]; - } failure:failureBlock]; - } failure:failureBlock]; - } failure:failureBlock]; - }]; -} - - (void)sendEventOfType:(MXEventTypeString)eventTypeString content:(NSDictionary *)content room:(MXRoom *)room @@ -1048,42 +705,6 @@ - (void)testAliceDecryptOldMessageWithANewDeviceInACryptedRoom }]; } -- (void)testAliceWithNewDeviceAndBob -{ - [matrixSDKTestsE2EData doE2ETestWithAliceAndBobInARoomWithCryptedMessages:self cryptedBob:YES readyToTest:^(MXSession *aliceSession, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation) { - - // Relog alice to simulate a new device - [MXSDKOptions sharedInstance].enableCryptoWhenStartingMXSession = YES; - [matrixSDKTestsData relogUserSession:self session:aliceSession withPassword:MXTESTS_ALICE_PWD onComplete:^(MXSession *aliceSession2) { - [MXSDKOptions sharedInstance].enableCryptoWhenStartingMXSession = NO; - - aliceSession2.legacyCrypto.warnOnUnknowDevices = NO; - - MXRoom *roomFromBobPOV = [bobSession roomWithRoomId:roomId]; - MXRoom *roomFromAlice2POV = [aliceSession2 roomWithRoomId:roomId]; - - NSString *messageFromAlice = @"Hello I'm still Alice!"; - - [roomFromBobPOV liveTimeline:^(id liveTimeline) { - [liveTimeline listenToEventsOfTypes:@[kMXEventTypeStringRoomMessage, kMXEventTypeStringRoomEncrypted] onEvent:^(MXEvent *event, MXTimelineDirection direction, MXRoomState *roomState) { - - XCTAssertEqual(0, [self checkEncryptedEvent:event roomId:roomId clearMessage:messageFromAlice senderSession:aliceSession2]); - - [expectation fulfill]; - - }]; - }]; - - [roomFromAlice2POV sendTextMessage:messageFromAlice threadId:nil success:nil failure:^(NSError *error) { - XCTFail(@"Cannot set up intial test conditions - error: %@", error); - [expectation fulfill]; - }]; - - }]; - - }]; -} - - (void)testAliceAndBobWithNewDevice { [matrixSDKTestsE2EData doE2ETestWithAliceAndBobInARoom:self cryptedBob:YES warnOnUnknowDevices:NO readyToTest:^(MXSession *aliceSession, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation) { @@ -1136,68 +757,6 @@ - (void)testAliceAndBobWithNewDevice }]; } -- (void)testAliceWithNewDeviceAndBobWithNewDevice -{ - [matrixSDKTestsE2EData doE2ETestWithAliceAndBobInARoomWithCryptedMessages:self cryptedBob:YES readyToTest:^(MXSession *aliceSession, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation) { - - // Relog alice to simulate a new device - [MXSDKOptions sharedInstance].enableCryptoWhenStartingMXSession = YES; - [matrixSDKTestsData relogUserSession:self session:aliceSession withPassword:MXTESTS_ALICE_PWD onComplete:^(MXSession *aliceSession2) { - [MXSDKOptions sharedInstance].enableCryptoWhenStartingMXSession = NO; - - // Relog bob to simulate a new device - [MXSDKOptions sharedInstance].enableCryptoWhenStartingMXSession = YES; - [matrixSDKTestsData relogUserSession:self session:bobSession withPassword:MXTESTS_BOB_PWD onComplete:^(MXSession *bobSession2) { - [MXSDKOptions sharedInstance].enableCryptoWhenStartingMXSession = NO; - - aliceSession2.legacyCrypto.warnOnUnknowDevices = NO; - bobSession2.legacyCrypto.warnOnUnknowDevices = NO; - - MXRoom *roomFromBob2POV = [bobSession2 roomWithRoomId:roomId]; - MXRoom *roomFromAlice2POV = [aliceSession2 roomWithRoomId:roomId]; - - XCTAssert(roomFromBob2POV.summary.isEncrypted, @"The room must still appear as encrypted"); - - [bobSession2 eventWithEventId:roomFromBob2POV.summary.lastMessage.eventId - inRoom:roomFromBob2POV.roomId - success:^(MXEvent *event) { - - XCTAssert(event.isEncrypted); - - XCTAssertNil(event.clearEvent); - XCTAssert(event.decryptionError); - XCTAssertEqual(event.decryptionError.code, MXDecryptingErrorUnknownInboundSessionIdCode); - - - NSString *messageFromAlice = @"Hello I'm still Alice!"; - - [roomFromBob2POV liveTimeline:^(id liveTimeline) { - [liveTimeline listenToEventsOfTypes:@[kMXEventTypeStringRoomMessage, kMXEventTypeStringRoomEncrypted] onEvent:^(MXEvent *event, MXTimelineDirection direction, MXRoomState *roomState) { - - XCTAssertEqual(0, [self checkEncryptedEvent:event roomId:roomId clearMessage:messageFromAlice senderSession:aliceSession2]); - - [expectation fulfill]; - - }]; - }]; - - [roomFromAlice2POV sendTextMessage:messageFromAlice threadId:nil success:nil failure:^(NSError *error) { - XCTFail(@"Cannot set up intial test conditions - error: %@", error); - [expectation fulfill]; - }]; - - } failure:^(NSError *error) { - XCTFail(@"Cannot set up initial test conditions - error: %@", error); - [expectation fulfill]; - }]; - - }]; - - }]; - - }]; -} - - (void)testAliceAndBlockedBob { [matrixSDKTestsE2EData doE2ETestWithAliceAndBobInARoom:self cryptedBob:YES warnOnUnknowDevices:NO readyToTest:^(MXSession *aliceSession, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation) { @@ -1291,243 +850,8 @@ - (void)testAliceAndBlockedBob }]; } -// Test un - -// Bob, Alice and Sam are in an encrypted room -// Alice sends a message #0 -// The message sending fails because of unknown devices (Bob and Sam ones) - -// Alice marks the Bob and Sam devices as known (UNVERIFIED) -// Alice sends another message #1 -// Checks that the Bob and Sam devices receive the message and can decrypt it. - -// Alice blacklists the unverified devices -// Alice sends a message #2 -// checks that the Sam and the Bob devices receive the message but it cannot be decrypted - -// Alice unblacklists the unverified devices -// Alice sends a message #3 -// checks that the Sam and the Bob devices receive the message and it can be decrypted on the both devices - -// Alice verifies the Bob device and blacklists the unverified devices in the current room. -// Alice sends a message #4 -// Check that the message can be decrypted by Bob's device but not by Sam's device - -// Alice unblacklists the unverified devices in the current room -// Alice sends a message #5 -// Check that the message can be decrypted by the Bob's device and the Sam's device - -- (void)testBlackListUnverifiedDevices -{ - NSArray *aliceMessages = @[ - @"0", - @"1", - @"2", - @"3", - @"4", - @"5" - ]; - - [matrixSDKTestsE2EData doE2ETestWithAliceAndBobAndSamInARoom:self cryptedBob:YES cryptedSam:YES warnOnUnknowDevices:YES readyToTest:^(MXSession *aliceSession, MXSession *bobSession, MXSession *samSession, NSString *roomId, XCTestExpectation *expectation) { - - MXRoom *roomFromAlicePOV = [aliceSession roomWithRoomId:roomId]; - MXRoom *roomFromBobPOV = [bobSession roomWithRoomId:roomId]; - MXRoom *roomFromSamPOV = [samSession roomWithRoomId:roomId]; - - __block NSUInteger bobMessageCount = 1; - __block NSUInteger samMessageCount = 1; - - [roomFromBobPOV liveTimeline:^(id liveTimeline) { - [liveTimeline listenToEventsOfTypes:@[kMXEventTypeStringRoomMessage, kMXEventTypeStringRoomEncrypted] onEvent:^(MXEvent *event, MXTimelineDirection direction, MXRoomState *roomState) { - - switch (bobMessageCount++) - { - case 1: - XCTAssertEqual(0, [self checkEncryptedEvent:event roomId:roomId clearMessage:aliceMessages[1] senderSession:aliceSession]); - break; - - case 2: - XCTAssertEqual(event.eventType, MXEventTypeRoomEncrypted); - XCTAssertNil(event.clearEvent); - XCTAssertEqual(event.decryptionError.code, MXDecryptingErrorUnknownInboundSessionIdCode); - break; - - case 3: - XCTAssertEqual(0, [self checkEncryptedEvent:event roomId:roomId clearMessage:aliceMessages[3] senderSession:aliceSession]); - break; - - case 4: - XCTAssertEqual(0, [self checkEncryptedEvent:event roomId:roomId clearMessage:aliceMessages[4] senderSession:aliceSession]); - break; - - case 5: - XCTAssertEqual(0, [self checkEncryptedEvent:event roomId:roomId clearMessage:aliceMessages[5] senderSession:aliceSession]); - - if (samMessageCount > 5) - { - [expectation fulfill]; - } - break; - - default: - break; - } - }]; - }]; - - [roomFromSamPOV liveTimeline:^(id liveTimeline) { - [liveTimeline listenToEventsOfTypes:@[kMXEventTypeStringRoomMessage, kMXEventTypeStringRoomEncrypted] onEvent:^(MXEvent *event, MXTimelineDirection direction, MXRoomState *roomState) { - - - switch (samMessageCount++) - { - case 1: - XCTAssertEqual(0, [self checkEncryptedEvent:event roomId:roomId clearMessage:aliceMessages[1] senderSession:aliceSession]); - break; - - case 2: - XCTAssertEqual(event.eventType, MXEventTypeRoomEncrypted); - XCTAssertNil(event.clearEvent); - XCTAssertEqual(event.decryptionError.code, MXDecryptingErrorUnknownInboundSessionIdCode); - break; - - case 3: - XCTAssertEqual(0, [self checkEncryptedEvent:event roomId:roomId clearMessage:aliceMessages[3] senderSession:aliceSession]); - break; - - case 4: - XCTAssertEqual(event.eventType, MXEventTypeRoomEncrypted); - XCTAssertNil(event.clearEvent); - XCTAssertEqual(event.decryptionError.code, MXDecryptingErrorUnknownInboundSessionIdCode); - break; - - case 5: - XCTAssertEqual(0, [self checkEncryptedEvent:event roomId:roomId clearMessage:aliceMessages[5] senderSession:aliceSession]); - - if (bobMessageCount > 5) - { - [expectation fulfill]; - } - break; - - default: - break; - } - }]; - - }]; - - // Let alice sends messages and control this test flow - [roomFromAlicePOV sendTextMessage:aliceMessages[0] threadId:nil success:^(NSString *eventId) { - - XCTFail(@"Sending of message #0 should fail due to unkwnown devices"); - [expectation fulfill]; - - } failure:^(NSError *error) { - - XCTAssert(error); - XCTAssertEqualObjects(error.domain, MXEncryptingErrorDomain); - XCTAssertEqual(error.code, MXEncryptingErrorUnknownDeviceCode); - - MXUsersDevicesMap *unknownDevices = error.userInfo[MXEncryptingErrorUnknownDeviceDevicesKey]; - XCTAssertEqual(unknownDevices.count, 2); - - - __block NSUInteger aliceMessageCount = 1; - [roomFromAlicePOV liveTimeline:^(id liveTimeline) { - [liveTimeline listenToEventsOfTypes:@[kMXEventTypeStringRoomMessage] onEvent:^(MXEvent *event, MXTimelineDirection direction, MXRoomState *roomState) { - - switch (aliceMessageCount++) - { - case 1: - { - // Alice blacklists the unverified devices - aliceSession.crypto.globalBlacklistUnverifiedDevices = YES; - - [roomFromAlicePOV sendTextMessage:aliceMessages[2] threadId:nil success:nil failure:^(NSError *error) { - XCTFail(@"Alice should be able to send message #2 - error: %@", error); - [expectation fulfill]; - }]; - - break; - } - - case 2: - { - // Alice unblacklists the unverified devices - aliceSession.crypto.globalBlacklistUnverifiedDevices = NO; - - [roomFromAlicePOV sendTextMessage:aliceMessages[3] threadId:nil success:nil failure:^(NSError *error) { - XCTFail(@"Alice should be able to send message #3 - error: %@", error); - [expectation fulfill]; - }]; - - break; - } - - case 3: - { - // Alice verifies the Bob device and blacklists the unverified devices in the current room - XCTAssertFalse([aliceSession.crypto isBlacklistUnverifiedDevicesInRoom:roomId]); - [aliceSession.crypto setBlacklistUnverifiedDevicesInRoom:roomId blacklist:YES]; - XCTAssert([aliceSession.crypto isBlacklistUnverifiedDevicesInRoom:roomId]); - - NSString *bobDeviceId = [unknownDevices deviceIdsForUser:bobSession.myUser.userId][0]; - [aliceSession.crypto setDeviceVerification:MXDeviceVerified forDevice:bobDeviceId ofUser:bobSession.myUser.userId success:^{ - - [roomFromAlicePOV sendTextMessage:aliceMessages[4] threadId:nil success:nil failure:^(NSError *error) { - XCTFail(@"Alice should be able to send message #4 - error: %@", error); - [expectation fulfill]; - }]; - - } failure:^(NSError *error) { - XCTFail(@"Cannot set up intial test conditions - error: %@", error); - [expectation fulfill]; - }]; - - break; - } - - case 4: - { - // Alice unblacklists the unverified devices - XCTAssert([aliceSession.crypto isBlacklistUnverifiedDevicesInRoom:roomId]); - [aliceSession.crypto setBlacklistUnverifiedDevicesInRoom:roomId blacklist:NO]; - XCTAssertFalse([aliceSession.crypto isBlacklistUnverifiedDevicesInRoom:roomId]); - - [roomFromAlicePOV sendTextMessage:aliceMessages[5] threadId:nil success:nil failure:^(NSError *error) { - XCTFail(@"Alice should be able to send message #5 - error: %@", error); - [expectation fulfill]; - }]; - - break; - } - - default: - break; - } - - }]; - - }]; - - // Alice marks the Bob and Sam devices as known (UNVERIFIED) - [aliceSession.legacyCrypto setDevicesKnown:unknownDevices complete:^{ - - [roomFromAlicePOV sendTextMessage:aliceMessages[1] threadId:nil success:nil failure:^(NSError *error) { - XCTFail(@"Alice should be able to send message #1 - error: %@", error); - [expectation fulfill]; - }]; - - }]; - - }]; - }]; - -} - -// Test method copy from MXRoomTests -testSendReplyToTextMessage -- (void)testSendReplyToTextMessage +// Test method copy from MXRoomTests -testSendReplyToTextMessage +- (void)testSendReplyToTextMessage { NSString *firstMessage = @"**First message!**"; NSString *firstFormattedMessage = @"

First message!

"; @@ -1772,131 +1096,8 @@ - (void)testInvitedMemberInACryptedRoom2 }]; } -// - Have Alice and Bob in an e2e room -// - Bob pauses his session -// - Alice sends a message -// - Bob can get the message using /event API -// -> But he does not have keys decrypt it -// - Bob resumes his session -// -> He has keys now -- (void)testHasKeysToDecryptEvent -{ - // - Have Alice and Bob in an e2e room - [matrixSDKTestsE2EData doE2ETestWithAliceAndBobInARoom:self cryptedBob:YES warnOnUnknowDevices:NO readyToTest:^(MXSession *aliceSession, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation) { - - NSString *messageFromAlice = @"Hello I'm Alice!"; - - MXRoom *roomFromAlicePOV = [aliceSession roomWithRoomId:roomId]; - - // - Bob pauses his session - dispatch_async(dispatch_get_main_queue(), ^{ - [bobSession pause]; - - // - Alice sends a message - [roomFromAlicePOV sendTextMessage:messageFromAlice threadId:nil success:^(NSString *eventId) { - - // - Bob can get the message using /event API - [bobSession eventWithEventId:eventId inRoom:roomId success:^(MXEvent *event) { - - // -> But he does not have keys decrypt it - [bobSession.legacyCrypto hasKeysToDecryptEvent:event onComplete:^(BOOL hasKeys) { - XCTAssertFalse(hasKeys); - - // - Bob resumes his session - [bobSession resume:^{ - - // -> He has keys now - [bobSession.legacyCrypto hasKeysToDecryptEvent:event onComplete:^(BOOL hasKeys) { - XCTAssertTrue(hasKeys); - - [expectation fulfill]; - }]; - }]; - }]; - - } failure:^(NSError *error) { - XCTFail(@"The operation should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - - } failure:^(NSError *error) { - XCTFail(@"Cannot set up intial test conditions - error: %@", error); - [expectation fulfill]; - }]; - }); - }]; -} - #pragma mark - Edge cases - -// Trying to set up several olm sessions in parallel should result in the creation of a single olm session -// -// - Have Alice and Bob -// - Make Alice know Bob's device -// - Move to the crypto thread (this is an internal technical test) -// - Create a first olm session -// -> It must succeed -// - Create a second olm session in parallel -// -> It must not create another HTTP request -// -> It must succeed using the same olm session - -- (void)testEnsureSingleOlmSession -{ - // - Have Alice and Bob - [matrixSDKTestsE2EData doE2ETestWithAliceAndBobInARoom:self cryptedBob:YES warnOnUnknowDevices:NO readyToTest:^(MXSession *aliceSession, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation) { - - // - Make Alice know Bob's device - [aliceSession.crypto downloadKeys:@[bobSession.myUserId] forceDownload:NO success:^(MXUsersDevicesMap *usersDevicesInfoMap, NSDictionary *crossSigningKeysMap) { - - // - Move to the crypto thread (this is an internal technical test) - dispatch_async(aliceSession.legacyCrypto.cryptoQueue, ^{ - - MXHTTPOperation *operation; - __block NSString *olmSessionId; - - - // - Create a first olm session - operation = [aliceSession.legacyCrypto ensureOlmSessionsForUsers:@[bobSession.myUserId] success:^(MXUsersDevicesMap *results) { - - // -> It must succeed - olmSessionId = [results objectForDevice:bobSession.myDeviceId forUser:bobSession.myUserId].sessionId; - XCTAssertNotNil(olmSessionId); - - } failure:^(NSError *error) { - XCTFail(@"The operation should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - - XCTAssertNotNil(operation); - - - // - Create a second olm session in parallel - operation = [aliceSession.legacyCrypto ensureOlmSessionsForUsers:@[bobSession.myUserId] success:^(MXUsersDevicesMap *results) { - - // -> It must succeed using the same olm session - NSString *olmSessionId2 = [results objectForDevice:bobSession.myDeviceId forUser:bobSession.myUserId].sessionId; - XCTAssertNotNil(olmSessionId2); - XCTAssertEqualObjects(olmSessionId, olmSessionId2); - - [expectation fulfill]; - - } failure:^(NSError *error) { - XCTFail(@"The operation should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - - // -> It must not create another HTTP request - XCTAssertNil(operation); - - }); - - } failure:^(NSError *error) { - XCTFail(@"Cannot set up intial test conditions - error: %@", error); - [expectation fulfill]; - }]; - }]; -} - (void)testReplayAttack { @@ -1991,7 +1192,10 @@ - (void)testReplayAttackForEventEdits }]; } -- (void)testRoomKeyReshare +#pragma mark - Tests for reproducing bugs + +// Test for https://github.com/vector-im/riot-ios/issues/913 +- (void)testFirstMessageSentWhileSessionWasPaused { [matrixSDKTestsE2EData doE2ETestWithAliceAndBobInARoom:self cryptedBob:YES warnOnUnknowDevices:NO readyToTest:^(MXSession *aliceSession, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation) { @@ -2000,576 +1204,48 @@ - (void)testRoomKeyReshare MXRoom *roomFromBobPOV = [bobSession roomWithRoomId:roomId]; MXRoom *roomFromAlicePOV = [aliceSession roomWithRoomId:roomId]; - __block MXEvent *toDeviceEvent; - - observer = [[NSNotificationCenter defaultCenter] addObserverForName:kMXSessionOnToDeviceEventNotification object:bobSession queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { - - toDeviceEvent = notif.userInfo[kMXSessionNotificationEventKey]; - }]; - - - [roomFromBobPOV liveTimeline:^(id liveTimeline) { - [liveTimeline listenToEventsOfTypes:@[kMXEventTypeStringRoomMessage] onEvent:^(MXEvent *event, MXTimelineDirection direction, MXRoomState *roomState) { + // Pause the session outside this callback + dispatch_async(dispatch_get_main_queue(), ^{ + [bobSession pause]; - XCTAssertEqual(0, [self checkEncryptedEvent:event roomId:roomId clearMessage:messageFromAlice senderSession:aliceSession]); + [roomFromAlicePOV sendTextMessage:messageFromAlice threadId:nil success:^(NSString *eventId) { - // Reinject a modified version of the received room_key event from Alice. - // From Bob pov, that mimics Alice resharing her keys but with an advanced outbound group session. - XCTAssert(toDeviceEvent); - - MXOlmOutboundGroupSession *session = [aliceSession.legacyCrypto.olmDevice outboundGroupSessionForRoomWithRoomId:roomId]; - XCTAssertNotNil(session); - - MXOutboundSessionInfo *sessionInfo = [[MXOutboundSessionInfo alloc] initWithSession: session]; + __block BOOL testDone = NO; - NSMutableDictionary *newContent = [NSMutableDictionary dictionaryWithDictionary:toDeviceEvent.content]; - newContent[@"session_key"] = sessionInfo.session.sessionKey; - toDeviceEvent.clearEvent.wireContent = newContent; + [roomFromBobPOV liveTimeline:^(id liveTimeline) { + [liveTimeline listenToEventsOfTypes:@[kMXEventTypeStringRoomMessage] onEvent:^(MXEvent *event, MXTimelineDirection direction, MXRoomState *roomState) { - [bobSession.legacyCrypto handleRoomKeyEvent:toDeviceEvent onComplete:^{}]; + XCTAssertEqual(0, [self checkEncryptedEvent:event roomId:roomId clearMessage:messageFromAlice senderSession:aliceSession]); + testDone = YES; - // We still must be able to decrypt the event - // ie, the implementation must have ignored the new room key with the advanced outbound group - // session key - [event setClearData:nil]; - [bobSession decryptEvents:@[event] inTimeline:nil onComplete:^(NSArray *failedEvents) { - - XCTAssertEqual(failedEvents.count, 0); - XCTAssertEqual(0, [self checkEncryptedEvent:event roomId:roomId clearMessage:messageFromAlice senderSession:aliceSession]); + }]; + }]; + [bobSession resume:^{ + XCTAssert(testDone); [expectation fulfill]; }]; - }]; - }]; - [roomFromAlicePOV sendTextMessage:messageFromAlice threadId:nil success:nil failure:^(NSError *error) { - XCTFail(@"Cannot set up intial test conditions - error: %@", error); - [expectation fulfill]; - }]; + } failure:^(NSError *error) { + XCTFail(@"Cannot set up intial test conditions - error: %@", error); + [expectation fulfill]; + }]; + }); }]; } -- (void)testLateRoomKey -{ - [matrixSDKTestsE2EData doE2ETestWithAliceAndBobInARoom:self cryptedBob:YES warnOnUnknowDevices:NO readyToTest:^(MXSession *aliceSession, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation) { - - NSString *messageFromAlice = @"Hello I'm Alice!"; - - MXRoom *roomFromBobPOV = [bobSession roomWithRoomId:roomId]; - MXRoom *roomFromAlicePOV = [aliceSession roomWithRoomId:roomId]; - - __block MXEvent *toDeviceEvent; - - observer = [[NSNotificationCenter defaultCenter] addObserverForName:kMXSessionOnToDeviceEventNotification object:bobSession queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { - - toDeviceEvent = notif.userInfo[kMXSessionNotificationEventKey]; - }]; - - [roomFromBobPOV liveTimeline:^(id liveTimeline) { - [liveTimeline listenToEventsOfTypes:@[kMXEventTypeStringRoomMessage] onEvent:^(MXEvent *event, MXTimelineDirection direction, MXRoomState *roomState) { +// Test that clearing devices keys makes crypto work again: +// - Alice and Bob are in an encrypted room - XCTAssertEqual(0, [self checkEncryptedEvent:event roomId:roomId clearMessage:messageFromAlice senderSession:aliceSession]); +// - Do a hack to make Alice forget Bob's device (this mimics a buggy situation that may still happen in real life). +// - Alice sends a message to Bob -> Bob receives an UISI for this message. - // Make crypto forget the inbound group session - XCTAssert(toDeviceEvent); - NSString *sessionId = toDeviceEvent.content[@"session_id"]; +// - Alice does a new MXSession (which is what apps do when clearing cache). This leads to an initial /sync. +// - Alice sends a message to Bob -> Bob still receives an UISI for this message. - id bobCryptoStore = (id)[bobSession.legacyCrypto.olmDevice valueForKey:@"store"]; - [bobCryptoStore removeInboundGroupSessionWithId:sessionId andSenderKey:toDeviceEvent.senderKey]; - MXLRUCache *cache = [bobSession.legacyCrypto.olmDevice valueForKey:@"inboundGroupSessionCache"]; - [cache clear]; - - // So that we cannot decrypt it anymore right now - [event setClearData:nil]; - [bobSession decryptEvents:@[event] inTimeline:nil onComplete:^(NSArray *failedEvents) { - - XCTAssertEqual(failedEvents.count, 1); - XCTAssertEqual(event.decryptionError.code, MXDecryptingErrorUnknownInboundSessionIdCode); - - // The event must be decrypted once we reinject the m.room_key event - __block __weak id observer2 = [[NSNotificationCenter defaultCenter] addObserverForName:kMXEventDidDecryptNotification object:event queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) { - - XCTAssert([NSThread currentThread].isMainThread); - - [[NSNotificationCenter defaultCenter] removeObserver:observer2]; - - XCTAssertEqual(0, [self checkEncryptedEvent:event roomId:roomId clearMessage:messageFromAlice senderSession:aliceSession]); - [expectation fulfill]; - }]; - - // Reinject the m.room_key event. This mimics a room_key event that arrives after message events. - [bobSession.legacyCrypto handleRoomKeyEvent:toDeviceEvent onComplete:^{}]; - }]; - }]; - }]; - - [roomFromAlicePOV sendTextMessage:messageFromAlice threadId:nil success:nil failure:^(NSError *error) { - XCTFail(@"Cannot set up intial test conditions - error: %@", error); - [expectation fulfill]; - }]; - }]; -} - -// Test the restart of broken Olm sessions (https://github.com/vector-im/riot-ios/issues/2129) -// Inspired from https://github.com/poljar/matrix-nio/blob/0.7.1/tests/encryption_test.py#L872 -// -// - Alice & Bob in a e2e room -// - Alice sends a 1st message with a 1st megolm session -// - Store the olm session between A&B devices -// - Alice sends a 2nd message with a 2nd megolm session -// - Simulate Alice using a backup of her OS and make her crypto state like after the first message -// - Alice sends a 3rd message with a 3rd megolm session but a wedged olm session -// -// What Bob must see: -// -> No issue with the 2 first messages -// -> The third event must fail to decrypt at first because Bob the olm session is wedged -// -> This is automatically fixed after SDKs restarted the olm session - -- (void)testOlmSessionUnwedging -{ - // - Alice & Bob have messages in a room - [matrixSDKTestsE2EData doE2ETestWithAliceAndBobInARoom:self cryptedBob:YES warnOnUnknowDevices:NO aliceStore:[[MXFileStore alloc] init] bobStore:[[MXFileStore alloc] init] readyToTest:^(MXSession *aliceSession, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation) { - - // - Alice sends a 1st message with a 1st megolm session - MXRoom *roomFromAlicePOV = [aliceSession roomWithRoomId:roomId]; - [roomFromAlicePOV sendTextMessage:@"0" threadId:nil success:^(NSString *eventId) { - - // - Store the olm session between A&B devices - // Let us pickle our session with bob here so we can later unpickle it - // and wedge our session. - MXOlmSession *olmSession = [aliceSession.legacyCrypto.store sessionsWithDevice:bobSession.crypto.deviceCurve25519Key].firstObject; - - // Relaunch Alice - // This forces her to use a new megolm session for sending message "11" - // This will move the olm session ratchet to share this new megolm session - MXSession *aliceSession1 = [[MXSession alloc] initWithMatrixRestClient:aliceSession.matrixRestClient]; - - [aliceSession close]; - [aliceSession1 setStore:[[MXFileStore alloc] init] success:^{ - [aliceSession1 start:^{ - aliceSession1.legacyCrypto.warnOnUnknowDevices = NO; - - // - Alice sends a 2nd message with a 2nd megolm session - MXRoom *roomFromAlicePOV1 = [aliceSession1 roomWithRoomId:roomId]; - [roomFromAlicePOV1 sendTextMessage:@"11" threadId:nil success:^(NSString *eventId) { - - - // - Simulate Alice using a backup of her OS and make her crypto state like after the first message - // Relaunch again alice - MXSession *aliceSession2 = [[MXSession alloc] initWithMatrixRestClient:aliceSession1.matrixRestClient]; - [matrixSDKTestsData retain:aliceSession2]; - - [aliceSession1 close]; - [aliceSession2 setStore:[[MXFileStore alloc] init] success:^{ - [aliceSession2 start:^{ - aliceSession2.legacyCrypto.warnOnUnknowDevices = NO; - - // Let us wedge the session now. Set crypto state like after the first message - [aliceSession2.legacyCrypto.store storeSession:olmSession]; - - // - Alice sends a 3rd message with a 3rd megolm session but a wedged olm session - MXRoom *roomFromAlicePOV2 = [aliceSession2 roomWithRoomId:roomId]; - [roomFromAlicePOV2 sendTextMessage:@"222" threadId:nil success:nil failure:^(NSError *error) { - XCTFail(@"Cannot set up intial test conditions - error: %@", error); - [expectation fulfill]; - }]; - } failure:nil]; - } failure:nil]; - - - - } failure:^(NSError *error) { - XCTFail(@"Cannot set up intial test conditions - error: %@", error); - [expectation fulfill]; - }]; - } failure:nil]; - } failure:nil]; - - } failure:^(NSError *error) { - XCTFail(@"Cannot set up intial test conditions - error: %@", error); - [expectation fulfill]; - }]; - - - // What Bob must see: - __block NSUInteger messageCount = 0; - [bobSession listenToEventsOfTypes:@[kMXEventTypeStringRoomMessage, kMXEventTypeStringRoomEncrypted] onEvent:^(MXEvent *event, MXTimelineDirection direction, id customObject) { - - switch (messageCount++) - { - case 0: - case 1: - { - // -> No issue with the 2 first messages - // The 2 first events can be decrypted. They just use different megolm session - XCTAssertTrue(event.isEncrypted); - XCTAssertNotNil(event.clearEvent); - XCTAssertEqual(event.eventType, MXEventTypeRoomMessage); - - break; - } - - case 2: - { - // -> The third event must fail to decrypt at first because Bob the olm session is wedged - XCTAssertTrue(event.isEncrypted); - XCTAssertNil(event.clearEvent); - - observer = [[NSNotificationCenter defaultCenter] addObserverForName:kMXEventDidDecryptNotification object:event queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) { - - // -> This is automatically fixed after SDKs restarted the olm session - MXEvent *event2 = note.object; - - XCTAssertTrue(event2.isEncrypted); - XCTAssertNotNil(event2.clearEvent); - XCTAssertEqual(event2.eventType, MXEventTypeRoomMessage); - - [expectation fulfill]; - }]; - - if (event.clearEvent) - { - XCTAssert(NO, @"The scenario went wrong. Escape now to avoid to wait forever"); - [expectation fulfill]; - } - - break; - } - - default: - break; - } - }]; - }]; -} - - -#pragma mark - Tests for reproducing bugs - -// Test for https://github.com/vector-im/riot-ios/issues/913 -- (void)testFirstMessageSentWhileSessionWasPaused -{ - [matrixSDKTestsE2EData doE2ETestWithAliceAndBobInARoom:self cryptedBob:YES warnOnUnknowDevices:NO readyToTest:^(MXSession *aliceSession, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation) { - - NSString *messageFromAlice = @"Hello I'm Alice!"; - - MXRoom *roomFromBobPOV = [bobSession roomWithRoomId:roomId]; - MXRoom *roomFromAlicePOV = [aliceSession roomWithRoomId:roomId]; - - // Pause the session outside this callback - dispatch_async(dispatch_get_main_queue(), ^{ - [bobSession pause]; - - [roomFromAlicePOV sendTextMessage:messageFromAlice threadId:nil success:^(NSString *eventId) { - - __block BOOL testDone = NO; - - [roomFromBobPOV liveTimeline:^(id liveTimeline) { - [liveTimeline listenToEventsOfTypes:@[kMXEventTypeStringRoomMessage] onEvent:^(MXEvent *event, MXTimelineDirection direction, MXRoomState *roomState) { - - XCTAssertEqual(0, [self checkEncryptedEvent:event roomId:roomId clearMessage:messageFromAlice senderSession:aliceSession]); - testDone = YES; - - }]; - }]; - - [bobSession resume:^{ - XCTAssert(testDone); - [expectation fulfill]; - }]; - - } failure:^(NSError *error) { - XCTFail(@"Cannot set up intial test conditions - error: %@", error); - [expectation fulfill]; - }]; - }); - }]; -} - -// Test for https://github.com/vector-im/riot-ios/issues/955 -- (void)testLeftAndJoinedBob -{ - NSString *messageFromAlice = @"Hello I'm Alice!"; - NSString *message2FromAlice = @"I'm still Alice!"; - - [matrixSDKTestsE2EData doE2ETestWithBobAndAlice:self readyToTest:^(MXSession *bobSession, MXSession *aliceSession, XCTestExpectation *expectation) { - - aliceSession.legacyCrypto.warnOnUnknowDevices = NO; - bobSession.legacyCrypto.warnOnUnknowDevices = NO; - - [aliceSession createRoom:nil visibility:kMXRoomDirectoryVisibilityPublic roomAlias:nil topic:nil success:^(MXRoom *roomFromAlicePOV) { - - [roomFromAlicePOV enableEncryptionWithAlgorithm:kMXCryptoMegolmAlgorithm success:^{ - - [bobSession joinRoom:roomFromAlicePOV.roomId viaServers:nil success:^(MXRoom *roomFromBobPOV) { - - [roomFromBobPOV liveTimeline:^(id liveTimeline) { - [liveTimeline listenToEventsOfTypes:@[kMXEventTypeStringRoomMessage] onEvent:^(MXEvent *event, MXTimelineDirection direction, MXRoomState *roomState) { - - [liveTimeline removeAllListeners]; - - XCTAssertEqual(0, [self checkEncryptedEvent:event roomId:roomFromBobPOV.roomId clearMessage:messageFromAlice senderSession:aliceSession]); - - [roomFromBobPOV leave:^{ - - // Make Bob come back to the room with a new device - // Clear his crypto store - [bobSession enableCrypto:NO success:^{ - - // Relog bob to simulate a new device - [MXSDKOptions sharedInstance].enableCryptoWhenStartingMXSession = YES; - [matrixSDKTestsData relogUserSession:self session:bobSession withPassword:MXTESTS_BOB_PWD onComplete:^(MXSession *bobSession2) { - - [MXSDKOptions sharedInstance].enableCryptoWhenStartingMXSession = NO; - - [bobSession2 joinRoom:roomFromAlicePOV.roomId viaServers:nil success:^(MXRoom *roomFromBobPOV2) { - - // Bob should be able to receive the message from Alice - [roomFromBobPOV2 liveTimeline:^(id liveTimeline) { - [liveTimeline listenToEventsOfTypes:@[kMXEventTypeStringRoomMessage, kMXEventTypeStringRoomEncrypted] onEvent:^(MXEvent *event, MXTimelineDirection direction, MXRoomState *roomState) { - - XCTAssert(event.clearEvent, @"Bob must be able to decrypt this new message on his new device"); - - XCTAssertEqual(0, [self checkEncryptedEvent:event roomId:roomFromBobPOV2.roomId clearMessage:message2FromAlice senderSession:aliceSession]); - - [expectation fulfill]; - - }]; - }]; - - [roomFromAlicePOV sendTextMessage:message2FromAlice threadId:nil success:nil failure:^(NSError *error) { - XCTFail(@"Cannot set up intial test conditions - error: %@", error); - [expectation fulfill]; - }]; - - } failure:^(NSError *error) { - XCTFail(@"Cannot set up intial test conditions - error: %@", error); - [expectation fulfill]; - }]; - - }]; - - } failure:^(NSError *error) { - XCTFail(@"Cannot set up intial test conditions - error: %@", error); - [expectation fulfill]; - }]; - - } failure:^(NSError *error) { - XCTFail(@"Cannot set up intial test conditions - error: %@", error); - [expectation fulfill]; - }]; - - }]; - }]; - - [roomFromAlicePOV sendTextMessage:messageFromAlice threadId:nil success:nil failure:^(NSError *error) { - XCTFail(@"Cannot set up intial test conditions - error: %@", error); - [expectation fulfill]; - }]; - - } failure:^(NSError *error) { - XCTFail(@"Cannot set up intial test conditions - error: %@", error); - [expectation fulfill]; - }]; - - } failure:^(NSError *error) { - XCTFail(@"Cannot set up intial test conditions - error: %@", error); - [expectation fulfill]; - }]; - - } failure:^(NSError *error) { - XCTFail(@"Cannot set up intial test conditions - error: %@", error); - [expectation fulfill]; - }]; - - }]; -} - -// Test for https://github.com/vector-im/riot-web/issues/4983 -// - Alice and Bob share an e2e room; Bob tracks Alice's devices -// - Bob leaves the room, so stops getting updates -// - Alice adds a new device -// - Alice and Bob start sharing a room again -// - Bob has an out of date list of Alice's devices -- (void)testLeftBobAndAliceWithNewDevice -{ - // - Alice and Bob share an e2e room; Bob tracks Alice's devices - [matrixSDKTestsE2EData doE2ETestWithAliceAndBobInARoomWithCryptedMessages:self cryptedBob:YES readyToTest:^(MXSession *aliceSession, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation) { - - // - Bob leaves the room, so stops getting updates - [bobSession leaveRoom:roomId success:^{ - - // - Alice adds a new device - [MXSDKOptions sharedInstance].enableCryptoWhenStartingMXSession = YES; - [matrixSDKTestsData relogUserSession:self session:aliceSession withPassword:MXTESTS_ALICE_PWD onComplete:^(MXSession *aliceSession2) { - [MXSDKOptions sharedInstance].enableCryptoWhenStartingMXSession = NO; - - aliceSession2.legacyCrypto.warnOnUnknowDevices = NO; - - // - Alice and Bob start sharing a room again - [aliceSession2 createRoom:nil visibility:kMXRoomDirectoryVisibilityPublic roomAlias:nil topic:nil success:^(MXRoom *roomFromAlice2POV) { - - NSString *newRoomId = roomFromAlice2POV.roomId; - - [roomFromAlice2POV enableEncryptionWithAlgorithm:kMXCryptoMegolmAlgorithm success:^{ - - observer = [[NSNotificationCenter defaultCenter] addObserverForName:kMXSessionNewRoomNotification object:bobSession queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) { - - [bobSession joinRoom:note.userInfo[kMXSessionNotificationRoomIdKey] viaServers:nil success:^(MXRoom *room) { - - // - Bob has an out of date list of Alice's devices - MXRoom *roomFromBobPOV = [bobSession roomWithRoomId:newRoomId]; - - NSString *messageFromBob = @"Hello Alice with new device!"; - - [roomFromAlice2POV liveTimeline:^(id liveTimeline) { - [liveTimeline listenToEventsOfTypes:@[kMXEventTypeStringRoomMessage, kMXEventTypeStringRoomEncrypted] onEvent:^(MXEvent *event, MXTimelineDirection direction, MXRoomState *roomState) { - - XCTAssertEqual(0, [self checkEncryptedEvent:event roomId:newRoomId clearMessage:messageFromBob senderSession:bobSession]); - - [expectation fulfill]; - - }]; - }]; - - [roomFromBobPOV sendTextMessage:messageFromBob threadId:nil success:nil failure:^(NSError *error) { - XCTFail(@"Cannot set up intial test conditions - error: %@", error); - [expectation fulfill]; - }]; - - } failure:^(NSError *error) { - XCTFail(@"Cannot set up intial test conditions - error: %@", error); - [expectation fulfill]; - }]; - - }]; - - [roomFromAlice2POV inviteUser:bobSession.myUser.userId success:nil failure:^(NSError *error) { - XCTFail(@"Cannot invite Bob (%@) - error: %@", bobSession.myUser.userId, error); - [expectation fulfill]; - }]; - - } failure:^(NSError *error) { - XCTFail(@"Cannot set up intial test conditions - error: %@", error); - [expectation fulfill]; - }]; - - } failure:^(NSError *error) { - XCTFail(@"Cannot set up intial test conditions - error: %@", error); - [expectation fulfill]; - }]; - - }]; - - } failure:^(NSError *error) { - XCTFail(@"Cannot set up intial test conditions - error: %@", error); - [expectation fulfill]; - }]; - - }]; -} - -// Test for https://github.com/matrix-org/matrix-js-sdk/pull/359 -// - Alice sends a message to Bob to a non encrypted room -// - Bob logs in with a new device -// - Alice turns the crypto ON in the room -// - Alice sends a message -// -> Bob must be able to decrypt this message -- (void)testEnableEncryptionAfterNonCryptedMessages -{ - NSString *messageFromAlice = @"Hello I'm Alice!"; - NSString *encryptedMessageFromAlice = @"I'm still Alice!"; - - [matrixSDKTestsE2EData doE2ETestWithBobAndAlice:self readyToTest:^(MXSession *bobSession, MXSession *aliceSession, XCTestExpectation *expectation) { - - aliceSession.legacyCrypto.warnOnUnknowDevices = NO; - bobSession.legacyCrypto.warnOnUnknowDevices = NO; - - [aliceSession createRoom:nil visibility:kMXRoomDirectoryVisibilityPublic roomAlias:nil topic:nil success:^(MXRoom *roomFromAlicePOV) { - - [bobSession joinRoom:roomFromAlicePOV.roomId viaServers:nil success:^(MXRoom *room) { - - [roomFromAlicePOV sendTextMessage:messageFromAlice threadId:nil success:^(NSString *eventId) { - - // Make Bob come back to the room with a new device - // Clear his crypto store - [bobSession enableCrypto:NO success:^{ - - // Relog bob to simulate a new device - [MXSDKOptions sharedInstance].enableCryptoWhenStartingMXSession = YES; - [matrixSDKTestsData relogUserSession:self session:bobSession withPassword:MXTESTS_BOB_PWD onComplete:^(MXSession *newBobSession) { - - [MXSDKOptions sharedInstance].enableCryptoWhenStartingMXSession = NO; - - MXRoom *roomFromNewBobPOV = [newBobSession roomWithRoomId:roomFromAlicePOV.roomId]; - - NSDictionary *bobDevices = [aliceSession.legacyCrypto.store devicesForUser:newBobSession.myUser.userId]; - XCTAssertEqual(bobDevices.count, 0, @"Alice should not have needed Bob's keys at this time"); - - // Turn the crypto ON in the room - [roomFromAlicePOV enableEncryptionWithAlgorithm:kMXCryptoMegolmAlgorithm success:^{ - - [roomFromNewBobPOV liveTimeline:^(id liveTimeline) { - [liveTimeline listenToEventsOfTypes:@[kMXEventTypeStringRoomMessage, kMXEventTypeStringRoomEncrypted] onEvent:^(MXEvent *event, MXTimelineDirection direction, MXRoomState *roomState) { - - XCTAssert(event.clearEvent, @"Bob must be able to decrypt message from his new device after the crypto is ON"); - - XCTAssertEqual(0, [self checkEncryptedEvent:event roomId:roomFromNewBobPOV.roomId clearMessage:encryptedMessageFromAlice senderSession:aliceSession]); - - NSDictionary *bobDevices = [aliceSession.legacyCrypto.store devicesForUser:newBobSession.myUser.userId]; - XCTAssertEqual(bobDevices.count, 1, @"Alice must now know Bob's device keys"); - - [expectation fulfill]; - - }]; - }]; - - // Post an encrypted message - [roomFromAlicePOV sendTextMessage:encryptedMessageFromAlice threadId:nil success:nil failure:^(NSError *error) { - XCTFail(@"Cannot set up intial test conditions - error: %@", error); - [expectation fulfill]; - }]; - - } failure:^(NSError *error) { - XCTFail(@"The operation should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - }]; - } failure:^(NSError *error) { - XCTFail(@"The operation should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - - } failure:^(NSError *error) { - XCTFail(@"The operation should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - - } failure:^(NSError *error) { - XCTFail(@"The operation should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - - } failure:^(NSError *error) { - XCTFail(@"The operation should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - - }]; -} - -// Test that clearing devices keys makes crypto work again: -// - Alice and Bob are in an encrypted room - -// - Do a hack to make Alice forget Bob's device (this mimics a buggy situation that may still happen in real life). -// - Alice sends a message to Bob -> Bob receives an UISI for this message. - -// - Alice does a new MXSession (which is what apps do when clearing cache). This leads to an initial /sync. -// - Alice sends a message to Bob -> Bob still receives an UISI for this message. - -// - Alice resets her devices keys -// - Alice does a new MXSession (which is what apps do when clearing cache). This leads to an initial /sync. -// - Alice sends a message to Bob -> Bob can decrypt this message. +// - Alice resets her devices keys +// - Alice does a new MXSession (which is what apps do when clearing cache). This leads to an initial /sync. +// - Alice sends a message to Bob -> Bob can decrypt this message. // TODO: Disabled because with the last rework on device list tracking logic (the one with deviceStatusTracking), // it became impossible to simulate a UISI. @@ -2681,87 +1357,6 @@ - (void)testEnableEncryptionAfterNonCryptedMessages #pragma mark - import/export -// Almost same code as testImportRoomKeys -- (void)testExportImportRoomKeysWithPassword -{ - [matrixSDKTestsE2EData doE2ETestWithAliceAndBobInARoomWithCryptedMessages:self cryptedBob:YES readyToTest:^(MXSession *aliceSession, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation) { - - NSString *password = @"motdepasse"; - - [bobSession.crypto exportRoomKeysWithPassword:password success:^(NSData *keyFile) { - - // Clear bob crypto data - [bobSession enableCrypto:NO success:^{ - - XCTAssertFalse([bobSession.legacyCrypto.store.class hasDataForCredentials:bobSession.matrixRestClient.credentials], @"Bob's keys should have been deleted"); - - [bobSession enableCrypto:YES success:^{ - - MXRoom *roomFromBobPOV = [bobSession roomWithRoomId:roomId]; - - - NSMutableArray *encryptedEvents = [NSMutableArray array]; - - [roomFromBobPOV liveTimeline:^(id liveTimeline) { - [liveTimeline listenToEventsOfTypes:@[kMXEventTypeStringRoomEncrypted] onEvent:^(MXEvent *event, MXTimelineDirection direction, MXRoomState *roomState) { - - [encryptedEvents addObject:event]; - }]; - - - [liveTimeline resetPagination]; - [liveTimeline paginate:100 direction:MXTimelineDirectionBackwards onlyFromStore:NO complete:^{ - - XCTAssertEqual(encryptedEvents.count, 5, @"There are 5 encrypted messages in the room. They cannot be decrypted at this step in the test"); - - - // All these events must be decrypted once we import the keys - observer = [[NSNotificationCenter defaultCenter] addObserverForName:kMXEventDidDecryptNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) { - - [encryptedEvents removeObject:note.object]; - }]; - - // Import the exported keys - [bobSession.crypto importRoomKeys:keyFile withPassword:password success:^(NSUInteger total, NSUInteger imported) { - - XCTAssertGreaterThan(total, 0); - XCTAssertEqual(total, imported); - - XCTAssertEqual(encryptedEvents.count, 0, @"All events should have been decrypted after the keys import"); - - [expectation fulfill]; - - } failure:^(NSError *error) { - - XCTFail(@"The operation should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - - } failure:^(NSError *error) { - XCTFail(@"The operation should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - }]; - - } failure:^(NSError *error) { - XCTFail(@"The operation should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - - - } failure:^(NSError *error) { - XCTFail(@"The operation should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - - } failure:^(NSError *error) { - XCTFail(@"The operation should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - - }]; -} - - (void)testImportRoomKeysWithWrongPassword { [matrixSDKTestsE2EData doE2ETestWithAliceAndBobInARoomWithCryptedMessages:self cryptedBob:YES readyToTest:^(MXSession *aliceSession, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation) { @@ -2806,346 +1401,9 @@ - (void)testImportRoomKeysWithWrongPassword 10 - Check [MXSession.crypto acceptAllPendingKeyRequestsFromUser:] with a wrong userId:deviceId pair 11 - Check [MXSession.crypto acceptAllPendingKeyRequestsFromUser:] with a valid userId:deviceId pair */ -- (void)testIncomingRoomKeyRequest -{ - // 1 - Create a first MXSession for Alice with a device - [matrixSDKTestsE2EData doE2ETestWithAliceInARoom:self readyToTest:^(MXSession *aliceSession, NSString *roomId, XCTestExpectation *expectation) { - - // 2 - Close it by keeping her credentials - MXCredentials *alice1Credentials = aliceSession.matrixRestClient.credentials; - - // 3 - Recreate a second MXSession, aliceSession2, for Alice with a new device - // Relog alice to simulate a new device - [MXSDKOptions sharedInstance].enableCryptoWhenStartingMXSession = YES; - [matrixSDKTestsData relogUserSessionWithNewDevice:self session:aliceSession withPassword:MXTESTS_ALICE_PWD onComplete:^(MXSession *aliceSession2) { - [MXSDKOptions sharedInstance].enableCryptoWhenStartingMXSession = NO; - - aliceSession2.legacyCrypto.warnOnUnknowDevices = NO; - - MXRoom *roomFromAlice2POV = [aliceSession2 roomWithRoomId:roomId]; - - // 4 - Send a message to a room with aliceSession2 - NSString *messageFromAlice = @"Hello I'm still Alice!"; - [roomFromAlice2POV sendTextMessage:messageFromAlice threadId:nil success:nil failure:^(NSError *error) { - XCTFail(@"Cannot set up intial test conditions - error: %@", error); - [expectation fulfill]; - }]; - - [roomFromAlice2POV liveTimeline:^(id liveTimeline) { - [liveTimeline listenToEventsOfTypes:@[kMXEventTypeStringRoomMessage, kMXEventTypeStringRoomEncrypted] onEvent:^(MXEvent *event, MXTimelineDirection direction, MXRoomState *roomState) { - - XCTAssertEqual(0, [self checkEncryptedEvent:event roomId:roomId clearMessage:messageFromAlice senderSession:aliceSession2]); - - // 5 - Instantiante a MXRestclient, alice1MatrixRestClient - MXRestClient *alice1MatrixRestClient = [[MXRestClient alloc] initWithCredentials:alice1Credentials andOnUnrecognizedCertificateBlock:nil andPersistentTokenDataHandler:nil andUnauthenticatedHandler:nil]; - [matrixSDKTestsData retain:alice1MatrixRestClient]; - - // 6 - Make alice1MatrixRestClient make a fake room key request for the message sent at step #4 - NSDictionary *requestMessage = @{ - @"action": @"request", - kMXMessageBodyKey: @{ - @"algorithm": event.wireContent[@"algorithm"], - @"room_id": roomId, - @"sender_key": event.wireContent[@"sender_key"], - @"session_id": event.wireContent[@"session_id"] - }, - @"request_id": @"my_request_id", - @"requesting_device_id": alice1Credentials.deviceId - }; - - MXUsersDevicesMap *contentMap = [[MXUsersDevicesMap alloc] init]; - [contentMap setObject:requestMessage forUser:alice1Credentials.userId andDevice:@"*"]; - - MXToDevicePayload *payload = [[MXToDevicePayload alloc] initWithEventType:kMXEventTypeStringRoomKeyRequest - contentMap:contentMap - transactionId:requestMessage[@"request_id"] - addMessageId:YES]; - [alice1MatrixRestClient sendToDevice:payload success:nil failure:^(NSError *error) { - XCTFail(@"The operation should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - - // 7 - aliceSession2 must receive kMXCryptoRoomKeyRequestNotification - observer = [[NSNotificationCenter defaultCenter] addObserverForName:kMXCryptoRoomKeyRequestNotification - object:aliceSession2.crypto - queue:[NSOperationQueue mainQueue] - usingBlock:^(NSNotification *notif) - { - // 8 - Do checks - MXIncomingRoomKeyRequest *incomingKeyRequest = notif.userInfo[kMXCryptoRoomKeyRequestNotificationRequestKey]; - XCTAssert(incomingKeyRequest); - XCTAssert([incomingKeyRequest isKindOfClass:MXIncomingRoomKeyRequest.class], @"Notified object must be indeed a MXIncomingRoomKeyRequest object. Not %@", incomingKeyRequest); - - XCTAssertEqualObjects(incomingKeyRequest.requestId, requestMessage[@"request_id"]); - XCTAssertEqualObjects(incomingKeyRequest.userId, alice1Credentials.userId); - XCTAssertEqualObjects(incomingKeyRequest.deviceId, alice1Credentials.deviceId); - XCTAssert(incomingKeyRequest.requestBody); - - //9 - Check [MXSession.crypto pendingKeyRequests:] result - [aliceSession2.legacyCrypto pendingKeyRequests:^(MXUsersDevicesMap *> *pendingKeyRequests) { - - XCTAssertEqual(pendingKeyRequests.count, 1); - - MXIncomingRoomKeyRequest *keyRequest = [pendingKeyRequests objectForDevice:alice1Credentials.deviceId forUser:alice1Credentials.userId][0]; - - // Should be the same request - XCTAssertEqualObjects(keyRequest.requestId, incomingKeyRequest.requestId); - XCTAssertEqualObjects(keyRequest.userId, incomingKeyRequest.userId); - XCTAssertEqualObjects(keyRequest.deviceId, incomingKeyRequest.deviceId); - XCTAssertEqualObjects(keyRequest.requestBody, incomingKeyRequest.requestBody); - - // 10 - Check [MXSession.crypto acceptAllPendingKeyRequestsFromUser:] with a wrong userId:deviceId pair - [aliceSession2.legacyCrypto acceptAllPendingKeyRequestsFromUser:alice1Credentials.userId andDevice:@"DEADBEEF" onComplete:^{ - - [aliceSession2.legacyCrypto pendingKeyRequests:^(MXUsersDevicesMap *> *pendingKeyRequests2) { - - XCTAssertEqual(pendingKeyRequests2.count, 1, @"The pending request should be still here"); - - // 11 - Check [MXSession.crypto acceptAllPendingKeyRequestsFromUser:] with a valid userId:deviceId pair - [aliceSession2.legacyCrypto acceptAllPendingKeyRequestsFromUser:alice1Credentials.userId andDevice:alice1Credentials.deviceId onComplete:^{ - - [aliceSession2.legacyCrypto pendingKeyRequests:^(MXUsersDevicesMap *> *pendingKeyRequests3) { - - XCTAssertEqual(pendingKeyRequests3.count, 0, @"There should be no more pending request"); - - [expectation fulfill]; - }]; - }]; - }]; - }]; - }]; - }]; - }]; - }]; - - }]; - }]; -} - - -#pragma mark - Bug fix - -/** - Test for https://github.com/vector-im/riot-ios/issues/2541. - - You need to hack the code and apply the following patch in MXDeviceListOperationsPool.m - to reproduce the race condition every time. - - dispatch_async(self->crypto.matrixRestClient.completionQueue, ^{ - + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC), self->crypto.matrixRestClient.completionQueue, ^{ - - The test does: - - 1- Alice sends a message in a room - - 2- one device got updated in the room - - 3- Alice sends a second message - -> 4- It must be sent (it was never sent before the fix) - */ -- (void)testDeviceInvalidationWhileSending -{ - [matrixSDKTestsE2EData doE2ETestWithAliceInARoom:self readyToTest:^(MXSession *aliceSession, NSString *roomId, XCTestExpectation *expectation) { - - NSString *message = @"message"; - NSString *message2 = @"message2"; - - MXRoom *roomFromAlicePOV = [aliceSession roomWithRoomId:roomId]; - - XCTAssert(roomFromAlicePOV.summary.isEncrypted); - - __block NSUInteger messageCount = 0; - [roomFromAlicePOV liveTimeline:^(id liveTimeline) { - - [liveTimeline listenToEventsOfTypes:@[kMXEventTypeStringRoomMessage] onEvent:^(MXEvent *event, MXTimelineDirection direction, MXRoomState *roomState) { - - switch (++messageCount) { - case 1: - { - - // - 2- one device got updated in the room - [aliceSession.legacyCrypto.deviceList invalidateUserDeviceList:aliceSession.myUser.userId]; - - // Delay the new message request so that the downloadKeys request from invalidateUserDeviceList can complete - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.5 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{ - - // - 3- Alice sends a second message - [roomFromAlicePOV sendTextMessage:message2 threadId:nil success:nil failure:^(NSError *error) { - XCTFail(@"Cannot set up intial test conditions - error: %@", error); - [expectation fulfill]; - }]; - - }); - - break; - } - - case 2: - { - // -> 4- It must be sent (it was never sent before the fix) - XCTAssertEqual(0, [self checkEncryptedEvent:event roomId:roomId clearMessage:message2 senderSession:aliceSession]); - - [expectation fulfill]; - break; - } - - default: - break; - } - - }]; - }]; - - // - 1- Alice sends a message in a room - [roomFromAlicePOV sendTextMessage:message threadId:nil success:nil failure:^(NSError *error) { - XCTFail(@"Cannot set up intial test conditions - error: %@", error); - [expectation fulfill]; - }]; - }]; -} - -// - Have Alice -// - Alice logs in on a new device -// -> The first device must get notified by the new sign-in -- (void)testMXDeviceListDidUpdateUsersDevicesNotification -{ - // - Have Alice - [matrixSDKTestsE2EData doE2ETestWithAliceInARoom:self readyToTest:^(MXSession *aliceSession, NSString *roomId, XCTestExpectation *expectation) { - - // - Alice logs in on a new device - [matrixSDKTestsE2EData loginUserOnANewDevice:self credentials:aliceSession.matrixRestClient.credentials withPassword:MXTESTS_ALICE_PWD onComplete:^(MXSession *newAliceSession) { - }]; - - // -> The first device must get notified by the new sign-in - observer = [[NSNotificationCenter defaultCenter] addObserverForName:MXDeviceListDidUpdateUsersDevicesNotification object:aliceSession.crypto queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notification) { - - NSDictionary *userInfo = notification.userInfo; - NSArray *updatedDevices = userInfo[aliceSession.myUser.userId]; - - XCTAssertEqual(updatedDevices.count, 1); - XCTAssertNotNil(updatedDevices.firstObject.deviceId); - XCTAssertNotEqualObjects(updatedDevices.firstObject.deviceId, aliceSession.myDeviceId); - - [expectation fulfill]; - }]; - }]; -} #pragma mark - Outbound Group Session -/** - - From doE2ETestWithAliceAndBobInARoomWithCryptedMessages, we should have an outbound group session for the current room - - Restore the outbound group session for the current room and check it exists - - close current session and open a new session - - Restore the outbound group session for the current room and check it exists and contains the same key as before -*/ -- (void)testRestoreOlmOutboundKey -{ - [matrixSDKTestsE2EData doE2ETestWithAliceAndBobInARoomWithCryptedMessages:self cryptedBob:YES readyToTest:^(MXSession *aliceSession, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation) { - - MXOlmOutboundGroupSession *outboundSession = [aliceSession.legacyCrypto.store outboundGroupSessionWithRoomId:roomId]; - XCTAssertNotNil(outboundSession); - - NSString *sessionKey = outboundSession.session.sessionKey; - - // - Restart the session - MXSession *aliceSession2 = [[MXSession alloc] initWithMatrixRestClient:aliceSession.matrixRestClient]; - [matrixSDKTestsData retain:aliceSession2]; - - [aliceSession close]; - [aliceSession2 start:^{ - MXOlmOutboundGroupSession *outboundSession = [aliceSession2.legacyCrypto.store outboundGroupSessionWithRoomId:roomId]; - XCTAssertNotNil(outboundSession); - NSString *sessionKey2 = outboundSession.session.sessionKey; - XCTAssertEqualObjects(sessionKey, sessionKey2); - [expectation fulfill]; - } failure:^(NSError * _Nonnull error) { - XCTFail(@"The request should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - }]; -} - -/** - - From doE2ETestWithAliceAndBobInARoomWithCryptedMessages, we should have an outbound group session for the current room - - Restore the outbound group session for the current room and check it exists - - discard current outbound group session - - close current session and open a new session - - Restore the outbound group session for the current room and check it exists and contains the new key -*/ -- (void)testDiscardAndRestoreOlmOutboundKey -{ - [matrixSDKTestsE2EData doE2ETestWithAliceAndBobInARoomWithCryptedMessages:self cryptedBob:YES readyToTest:^(MXSession *aliceSession, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation) { - MXOlmOutboundGroupSession *outboundSession = [aliceSession.legacyCrypto.store outboundGroupSessionWithRoomId:roomId]; - XCTAssertNotNil(outboundSession); - - NSString *sessionKey = outboundSession.session.sessionKey; - - [aliceSession.legacyCrypto.olmDevice discardOutboundGroupSessionForRoomWithRoomId:roomId]; - - // - Restart the session - MXSession *aliceSession2 = [[MXSession alloc] initWithMatrixRestClient:aliceSession.matrixRestClient]; - [matrixSDKTestsData retain:aliceSession2]; - - [aliceSession close]; - [aliceSession2 start:^{ - MXOlmOutboundGroupSession *outboundSession = [aliceSession2.legacyCrypto.store outboundGroupSessionWithRoomId:roomId]; - XCTAssertNil(outboundSession); - XCTAssertNotNil([aliceSession2.legacyCrypto.olmDevice createOutboundGroupSessionForRoomWithRoomId:roomId]); - outboundSession = [aliceSession2.legacyCrypto.store outboundGroupSessionWithRoomId:roomId]; - XCTAssertNotNil(outboundSession); - NSString *sessionKey2 = outboundSession.session.sessionKey; - XCTAssertNotEqualObjects(sessionKey, sessionKey2, @"%@ == %@", sessionKey, sessionKey2); - [expectation fulfill]; - } failure:^(NSError * _Nonnull error) { - XCTFail(@"The request should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - }]; -} - -#pragma mark - One time / fallback keys - -- (void)testFallbackKeySignatures -{ - [MXSDKOptions sharedInstance].enableCryptoWhenStartingMXSession = YES; - [matrixSDKTestsData doMXSessionTestWithBob:self readyToTest:^(MXSession *mxSession, XCTestExpectation *expectation) { - [MXSDKOptions sharedInstance].enableCryptoWhenStartingMXSession = NO; - - [mxSession.legacyCrypto.olmDevice generateFallbackKey]; - - NSDictionary *fallbackKeyDictionary = mxSession.legacyCrypto.olmDevice.fallbackKey; - NSMutableDictionary *fallbackKeyJson = [NSMutableDictionary dictionary]; - - for (NSString *keyId in fallbackKeyDictionary[kMXKeyCurve25519Type]) - { - // Sign the fallback key - NSMutableDictionary *signedKey = [NSMutableDictionary dictionary]; - signedKey[@"key"] = fallbackKeyDictionary[kMXKeyCurve25519Type][keyId]; - signedKey[@"fallback"] = @(YES); - signedKey[@"signatures"] = [mxSession.legacyCrypto signObject:signedKey]; - - fallbackKeyJson[[NSString stringWithFormat:@"%@:%@", kMXKeySignedCurve25519Type, keyId]] = signedKey; - } - - MXKey *fallbackKey = [MXKey modelFromJSON:fallbackKeyJson]; - - NSString *signKeyId = [NSString stringWithFormat:@"%@:%@", kMXKeyEd25519Type, mxSession.myDeviceId]; - NSString *signature = [fallbackKey.signatures objectForDevice:signKeyId forUser:mxSession.myUserId]; - - MXUsersDevicesMap *usersDevicesKeyTypesMap = [[MXUsersDevicesMap alloc] init]; - [usersDevicesKeyTypesMap setObject:@"curve25519" - forUser:mxSession.matrixRestClient.credentials.userId - andDevice:mxSession.matrixRestClient.credentials.deviceId]; - - MXDeviceInfo *deviceInfo = [mxSession.crypto deviceWithDeviceId:mxSession.myDeviceId - ofUser:mxSession.myUserId]; - - NSError *error; - BOOL result = [mxSession.legacyCrypto.olmDevice verifySignature:deviceInfo.fingerprint JSON:fallbackKey.signalableJSONDictionary signature:signature error:&error]; - - XCTAssertNil(error); - XCTAssertTrue(result); - - [expectation fulfill]; - }]; -} // Test encryption algorithm change with a blank m.room.encryption event // - Alice and bob in a megolm encrypted room @@ -3271,65 +1529,6 @@ - (void)testBadSummaryIsEncryptedState }]; } -- (void)testIsRoomSharingHistory -{ - [matrixSDKTestsE2EData doE2ETestWithAliceInARoom:self readyToTest:^(MXSession *session, NSString *roomId, XCTestExpectation *expectation) { - - __block NSInteger caseIndex = 0; - NSArray *caseOutcomes = @[ - @[kMXRoomHistoryVisibilityJoined, @(NO)], - @[kMXRoomHistoryVisibilityShared, @(YES)], - @[kMXRoomHistoryVisibilityInvited, @(NO)], - @[kMXRoomHistoryVisibilityWorldReadable, @(YES)] - ]; - - // Visibility is set to not shared by default - MXSDKOptions.sharedInstance.enableRoomSharedHistoryOnInvite = NO; - XCTAssertFalse([session.legacyCrypto isRoomSharingHistory:roomId]); - - // But can be enabled with a build flag - MXSDKOptions.sharedInstance.enableRoomSharedHistoryOnInvite = YES; - XCTAssertTrue([session.legacyCrypto isRoomSharingHistory:roomId]); - - MXRoom *room = [session roomWithRoomId:roomId]; - [room liveTimeline:^(id liveTimeline) { - [liveTimeline listenToEventsOfTypes:@[kMXEventTypeStringRoomHistoryVisibility] onEvent:^(MXEvent * _Nonnull event, MXTimelineDirection direction, MXRoomState * _Nullable roomState) { - - BOOL sharedHistory = [session.legacyCrypto isRoomSharingHistory:roomId]; - BOOL expectsSharedHistory = [caseOutcomes[caseIndex].lastObject boolValue]; - XCTAssertEqual(expectsSharedHistory, sharedHistory); - - caseIndex++; - if (caseIndex >= caseOutcomes.count) { - [expectation fulfill]; - } - }]; - }]; - - [room setHistoryVisibility:caseOutcomes[0][0] success:^{ - [room setHistoryVisibility:caseOutcomes[1][0] success:^{ - [room setHistoryVisibility:caseOutcomes[2][0] success:^{ - [room setHistoryVisibility:caseOutcomes[3][0] success:^{ - - } failure:^(NSError *error) { - XCTFail(@"Should not fail - error: %@", error); - [expectation fulfill]; - }]; - } failure:^(NSError *error) { - XCTFail(@"Should not fail - error: %@", error); - [expectation fulfill]; - }]; - } failure:^(NSError *error) { - XCTFail(@"Should not fail - error: %@", error); - [expectation fulfill]; - }]; - } failure:^(NSError *error) { - XCTFail(@"Should not fail - error: %@", error); - [expectation fulfill]; - }]; - }]; -} - #pragma mark Helpers /** diff --git a/MatrixSDKTests/MXDeviceListOperationsPoolUnitTests.swift b/MatrixSDKTests/MXDeviceListOperationsPoolUnitTests.swift deleted file mode 100644 index 76055c8fed..0000000000 --- a/MatrixSDKTests/MXDeviceListOperationsPoolUnitTests.swift +++ /dev/null @@ -1,31 +0,0 @@ -// -// Copyright 2021 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 XCTest - -import MatrixSDK - -class MXDeviceListOperationsPoolUnitTests: XCTestCase { - - func testDeallocate() { - var pool: MXDeviceListOperationsPool? = MXDeviceListOperationsPool(crypto: nil) - weak var weakPool = pool - MXDeviceListOperation(userIds: ["foo"], success: nil, failure: nil).add(to: pool) - pool = nil - XCTAssertNil(weakPool) - } - -} diff --git a/MatrixSDKTests/MXKeyBackupUnitTests.swift b/MatrixSDKTests/MXKeyBackupUnitTests.swift index d352122750..faca56b772 100644 --- a/MatrixSDKTests/MXKeyBackupUnitTests.swift +++ b/MatrixSDKTests/MXKeyBackupUnitTests.swift @@ -22,11 +22,6 @@ class MXKeyBackupUnitTests: XCTestCase { // MARK: - Curve25519 - func testCurve25519AlgorithmClass() throws { - XCTAssertEqual(MXCurve25519KeyBackupAlgorithm.algorithmName, kMXCryptoCurve25519KeyBackupAlgorithm) - XCTAssertTrue(MXCurve25519KeyBackupAlgorithm.isUntrusted) - } - func testCurve25519AuthData() throws { let publicKey = "abcdefg" let privateKeySalt = "hijklmno" @@ -58,111 +53,6 @@ class MXKeyBackupUnitTests: XCTestCase { XCTAssertNil(authData.signalableJSONDictionary["signatures"]) } - func testCurve25519KeyMatch() throws { - var salt: NSString? = "" - var iterations: UInt = 0 - var error: NSError? = nil - let privateKey = try MXKeyBackupPassword.generatePrivateKey(withPassword: "password", - salt: &salt, - iterations: &iterations) - let olmPkDecryption = OLMPkDecryption() - let publicKey = olmPkDecryption.setPrivateKey(privateKey, error: &error) - - XCTAssertTrue(try MXCurve25519KeyBackupAlgorithm.keyMatches(privateKey, withAuthData: ["public_key": publicKey])) - } - - func testCurve25519PreparationWithNoPassword() throws { - let preparationInfoWithNoPass = try MXCurve25519KeyBackupAlgorithm.prepare(with: nil) - guard let authDataWithNoPass = preparationInfoWithNoPass.authData as? MXCurve25519BackupAuthData else { - XCTFail("Failed to setup test conditions") - return - } - XCTAssertFalse(authDataWithNoPass.publicKey.isEmpty) - XCTAssertNil(authDataWithNoPass.privateKeySalt) - XCTAssertEqual(authDataWithNoPass.privateKeyIterations, 0) - } - - func testCurve25519PreparationWithPassword() throws { - let preparationInfoWithPass = try MXCurve25519KeyBackupAlgorithm.prepare(with: "password") - guard let authDataWithPass = preparationInfoWithPass.authData as? MXCurve25519BackupAuthData else { - XCTFail("Failed to setup test conditions") - return - } - XCTAssertFalse(authDataWithPass.publicKey.isEmpty) - XCTAssertNotNil(authDataWithPass.privateKeySalt) - XCTAssertNotEqual(authDataWithPass.privateKeyIterations, 0) - } - - func testCurve25519AuthDataGeneration() throws { - let json: [String: Any] = [ - "public_key": "abcdefg", - "signatures": [ - "something": [ - "ed25519:something": "hijklmnop" - ] - ] - ] - - guard let authData = try MXCurve25519KeyBackupAlgorithm.authData(fromJSON: json) as? MXCurve25519BackupAuthData, - let signatures = authData.signatures else { - XCTFail("Failed to setup test conditions") - return - } - XCTAssertFalse(authData.publicKey.isEmpty) - XCTAssertFalse(signatures.isEmpty) - } - - func testCurve25519KeyBackupVersionCheck() throws { - let json: [String: Any] = [ - "algorithm": kMXCryptoCurve25519KeyBackupAlgorithm, - "auth_data": [ - "public_key": "abcdefg" - ], - "version": "1" - ] - - guard let keyBackupVersion = MXKeyBackupVersion(fromJSON: json) else { - XCTFail("Failed to setup test conditions") - return - } - XCTAssertTrue(MXCurve25519KeyBackupAlgorithm.check(keyBackupVersion)) - } - - func testCurve25519AlgorithmInstance() throws { - var salt: NSString? = "" - var iterations: UInt = 0 - let privateKey = try MXKeyBackupPassword.generatePrivateKey(withPassword: "password", salt: &salt, iterations: &iterations) - - let olmPkDecryption = OLMPkDecryption() - var error: NSError? = nil - let publicKey = olmPkDecryption.setPrivateKey(privateKey, error: &error) - - let crypto = MXLegacyCrypto() - let json: [String: Any] = [ - "public_key": publicKey, - "signatures": [ - "something": [ - "ed25519:something": "hijklmnop" - ] - ] - ] - - let authData = try MXCurve25519KeyBackupAlgorithm.authData(fromJSON: json) - guard let algorithm = MXCurve25519KeyBackupAlgorithm(crypto: crypto, - authData: authData, - keyGetterBlock: { return privateKey }) else { - return - } - XCTAssertTrue(try algorithm.keyMatches(privateKey)) - } - - // MARK: - Aes256 - - func testAes256AlgorithmClass() throws { - XCTAssertEqual(MXAes256KeyBackupAlgorithm.algorithmName, kMXCryptoAes256KeyBackupAlgorithm) - XCTAssertFalse(MXAes256KeyBackupAlgorithm.isUntrusted) - } - func testAes256AuthData() throws { let iv = "abcdefg" let mac = "abcdefgtyu" @@ -197,116 +87,4 @@ class MXKeyBackupUnitTests: XCTestCase { XCTAssertNil(authData.signalableJSONDictionary["signatures"]) } - func testAes256KeyMatch() throws { - var salt: NSString? = "" - var iterations: UInt = 0 - let privateKey = try MXKeyBackupPassword.generatePrivateKey(withPassword: "password", - salt: &salt, - iterations: &iterations) - let secretContent = try MXSecretStorage().encryptedZeroString(withPrivateKey: privateKey, iv: nil) - - guard let mac = secretContent.mac, let iv = secretContent.iv else { - XCTFail("Failed to setup test conditions") - return - } - - XCTAssertTrue(try MXAes256KeyBackupAlgorithm.keyMatches(privateKey, withAuthData: [:])) - XCTAssertTrue(try MXAes256KeyBackupAlgorithm.keyMatches(privateKey, withAuthData: ["mac": mac, "iv": iv])) - } - - func testAes256PreparationWithNoPassword() throws { - let preparationInfoWithNoPass = try MXAes256KeyBackupAlgorithm.prepare(with: nil) - guard let authDataWithNoPass = preparationInfoWithNoPass.authData as? MXAes256BackupAuthData else { - XCTFail("Failed to setup test conditions") - return - } - XCTAssertNotNil(authDataWithNoPass.iv) - XCTAssertNotNil(authDataWithNoPass.mac) - XCTAssertNil(authDataWithNoPass.privateKeySalt) - XCTAssertEqual(authDataWithNoPass.privateKeyIterations, 0) - } - - func testAes256PreparationWithPassword() throws { - let preparationInfoWithPass = try MXAes256KeyBackupAlgorithm.prepare(with: "password") - guard let authDataWithPass = preparationInfoWithPass.authData as? MXAes256BackupAuthData else { - XCTFail("Failed to setup test conditions") - return - } - XCTAssertNotNil(authDataWithPass.iv) - XCTAssertNotNil(authDataWithPass.mac) - XCTAssertNotNil(authDataWithPass.privateKeySalt) - XCTAssertNotEqual(authDataWithPass.privateKeyIterations, 0) - } - - func testAes256AuthDataGeneration() throws { - let json: [String: Any] = [ - "iv": "abcdefg", - "mac": "asdbasdsd", - "signatures": [ - "something": [ - "ed25519:something": "hijklmnop" - ] - ] - ] - - guard let authData = try MXAes256KeyBackupAlgorithm.authData(fromJSON: json) as? MXAes256BackupAuthData, - let signatures = authData.signatures else { - XCTFail("Failed to setup test conditions") - return - } - XCTAssertNotNil(authData.iv) - XCTAssertNotNil(authData.mac) - XCTAssertFalse(signatures.isEmpty) - } - - func testAes256KeyBackupVersionCheck() throws { - let json: [String: Any] = [ - "algorithm": kMXCryptoAes256KeyBackupAlgorithm, - "auth_data": [ - "iv": "abcdefgh", - "mac": "zdkcsdfsdf" - ], - "version": "1" - ] - - guard let keyBackupVersion = MXKeyBackupVersion(fromJSON: json) else { - XCTFail("Failed to setup test conditions") - return - } - XCTAssertTrue(MXAes256KeyBackupAlgorithm.check(keyBackupVersion)) - } - - func testAes256AlgorithmInstance() throws { - var salt: NSString? = "" - var iterations: UInt = 0 - let privateKey = try MXKeyBackupPassword.generatePrivateKey(withPassword: "password", - salt: &salt, - iterations: &iterations) - let secretContent = try MXSecretStorage().encryptedZeroString(withPrivateKey: privateKey, iv: nil) - - guard let mac = secretContent.mac, let iv = secretContent.iv else { - XCTFail("Failed to setup test conditions") - return - } - - let crypto = MXLegacyCrypto() - let json: [String: Any] = [ - "iv": iv, - "mac": mac, - "signatures": [ - "something": [ - "ed25519:something": "hijklmnop" - ] - ] - ] - - let authData = try MXAes256KeyBackupAlgorithm.authData(fromJSON: json) - guard let algorithm = MXAes256KeyBackupAlgorithm(crypto: crypto, - authData: authData, - keyGetterBlock: { return privateKey }) else { - return - } - XCTAssertTrue(try algorithm.keyMatches(privateKey)) - } - } diff --git a/MatrixSDKTests/MXLazyLoadingTests.m b/MatrixSDKTests/MXLazyLoadingTests.m index 2bda1299b3..4f791f78ec 100644 --- a/MatrixSDKTests/MXLazyLoadingTests.m +++ b/MatrixSDKTests/MXLazyLoadingTests.m @@ -1180,59 +1180,6 @@ - (void)testRoomSummaryDisplayNameInDirectChatWithLazyLoadingOFF } -// Check encryption from a lazy loaded room state -// - Alice sends a message from its lazy loaded room state where there is no Charlie -// - Charlie must be able to decrypt it -- (void)checkEncryptedMessageWithLazyLoading:(BOOL)lazyLoading -{ - [MXSDKOptions sharedInstance].enableCryptoWhenStartingMXSession = YES; - [self createScenarioWithLazyLoading:lazyLoading readyToTest:^(MXSession *aliceSession, MXSession *bobSession, MXSession *charlieSession, NSString *roomId, XCTestExpectation *expectation) { - [MXSDKOptions sharedInstance].enableCryptoWhenStartingMXSession = NO; - - MXRoom *room = [aliceSession roomWithRoomId:roomId]; - [room listenToEventsOfTypes:@[kMXEventTypeStringRoomEncryption] onEvent:^(MXEvent *event, MXTimelineDirection direction, MXRoomState *roomState) { - - aliceSession.legacyCrypto.warnOnUnknowDevices = NO; - - NSString *messageFromAlice = @"An encrypted message"; - - [charlieSession listenToEventsOfTypes:@[kMXEventTypeStringRoomMessage] onEvent:^(MXEvent *event, MXTimelineDirection direction, id customObject) { - - XCTAssertTrue(event.isEncrypted); - XCTAssert(event.clearEvent); - XCTAssertEqualObjects(event.content[kMXMessageBodyKey], messageFromAlice); - - [expectation fulfill]; - }]; - - MXRoomSummary *summary = [aliceSession roomSummaryWithRoomId:roomId]; - XCTAssertTrue(summary.isEncrypted); - - [room sendTextMessage:messageFromAlice threadId:nil success:nil failure:^(NSError *error) { - XCTFail(@"The operation should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - }]; - - MXRoom *roomFromBobPOV = [bobSession roomWithRoomId:roomId]; - [roomFromBobPOV enableEncryptionWithAlgorithm:kMXCryptoMegolmAlgorithm success:nil failure:^(NSError *error) { - XCTFail(@"The operation should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - }]; -} - -- (void)testEncryptedMessage -{ - [self checkEncryptedMessageWithLazyLoading:YES]; -} - -- (void)testEncryptedMessageWithLazyLoadingOFF -{ - [self checkEncryptedMessageWithLazyLoading:NO]; -} - - // After the test scenario, create a temporary timeline on the last event. // The timeline state should be lazy loaded and partial. // There should be only Alice and state.members.count = 1 diff --git a/MatrixSDKTests/MXRoomSummaryTests.m b/MatrixSDKTests/MXRoomSummaryTests.m index 0644aa9e14..c3043acf11 100644 --- a/MatrixSDKTests/MXRoomSummaryTests.m +++ b/MatrixSDKTests/MXRoomSummaryTests.m @@ -26,8 +26,6 @@ #import "MXRoomSummaryUpdater.h" -#import "MXCrypto_Private.h" - #import "MXTools.h" #import "MXKeyProvider.h" #import "MXAesKeyData.h" @@ -1333,156 +1331,6 @@ - (void)testEncryptedLastMessageEvent }]; } -// The same test as MXCryptoTests but dedicated to MXRoomSummary -- (void)testLateRoomKey -{ - [matrixSDKTestsE2EData doE2ETestWithAliceAndBobInARoom:self - cryptedBob:YES - warnOnUnknowDevices:NO - aliceStore:[[MXMemoryStore alloc] init] - bobStore:[[MXMemoryStore alloc] init] - readyToTest:^(MXSession *aliceSession, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation) - { - bobSession.roomSummaryUpdateDelegate = self; - - NSString *messageFromAlice = @"Hello I'm Alice!"; - - MXRoom *roomFromBobPOV = [bobSession roomWithRoomId:roomId]; - MXRoom *roomFromAlicePOV = [aliceSession roomWithRoomId:roomId]; - - // Some hack to set up test conditions - __block MXEvent *toDeviceEvent; - - observer = [[NSNotificationCenter defaultCenter] addObserverForName:kMXSessionOnToDeviceEventNotification object:bobSession queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { - - toDeviceEvent = notif.userInfo[kMXSessionNotificationEventKey]; - }]; - - [roomFromBobPOV liveTimeline:^(id liveTimeline) { - - [liveTimeline listenToEventsOfTypes:@[kMXEventTypeStringRoomMessage] onEvent:^(MXEvent *event, MXTimelineDirection direction, MXRoomState *roomState) { - - if (direction == MXTimelineDirectionForwards) - { - // Make crypto forget the inbound group session now - // MXRoomSummary will not be able to decrypt it - XCTAssert(toDeviceEvent); - NSString *sessionId = toDeviceEvent.content[@"session_id"]; - - id bobCryptoStore = (id)[bobSession.legacyCrypto.olmDevice valueForKey:@"store"]; - [bobCryptoStore removeInboundGroupSessionWithId:sessionId andSenderKey:toDeviceEvent.senderKey]; - - // So that we cannot decrypt it anymore right now - [event setClearData:nil]; - } - }]; - }]; - - MXRoomSummary *roomSummaryFromBobPOV = roomFromBobPOV.summary; - - __block NSString *lastMessageEventId; - __block NSUInteger notifCount = 0; - - id summaryObserver; - summaryObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kMXRoomSummaryDidChangeNotification object:roomSummaryFromBobPOV queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) { - - [bobSession eventWithEventId:roomSummaryFromBobPOV.lastMessage.eventId - inRoom:roomSummaryFromBobPOV.roomId - success:^(MXEvent *event) { - - switch (notifCount++) - { - case 0: - { - XCTAssertEqualObjects(roomSummaryFromBobPOV.lastMessage.eventId, lastMessageEventId); - XCTAssertEqualObjects(roomSummaryFromBobPOV.lastMessage.text, uisiString, @"Without the key, we have a UISI"); - - XCTAssertNil(event.clearEvent); - - // Attempt a new decryption - [bobSession decryptEvents:@[event] inTimeline:nil onComplete:^(NSArray *failedEvents) { - // Reinject the m.room_key event. This mimics a room_key event that arrives after message events. - [bobSession.legacyCrypto handleRoomKeyEvent:toDeviceEvent onComplete:^{}]; - }]; - - break; - } - - case 1: - { - // The last message must be decrypted now - XCTAssertEqualObjects(roomSummaryFromBobPOV.lastMessage.eventId, lastMessageEventId); - XCTAssertEqualObjects(roomSummaryFromBobPOV.lastMessage.text, messageFromAlice, @"The message must be now decrypted"); - - XCTAssert(event.clearEvent); - - [expectation fulfill]; - break; - } - - default: - break; - } - - } failure:^(NSError *error) { - XCTFail(@"Cannot set up initial test conditions - error: %@", error); - [expectation fulfill]; - }]; - - }]; - - [roomFromAlicePOV sendTextMessage:messageFromAlice threadId:nil success:^(NSString *eventId) { - lastMessageEventId = eventId; - } failure:^(NSError *error) { - XCTFail(@"Cannot set up intial test conditions - error: %@", error); - [expectation fulfill]; - }]; - }]; -} - -// https://github.com/matrix-org/matrix-ios-sdk/issues/409 -// 1 - Bob creates a room and invite Alice -// 2 - Alice sends a message -// 3 -> From Bob's POV, the room notification count must increase -- (void)testNotificationCountUpdate -{ - // 1 - Bob creates a room and invite Alice - [matrixSDKTestsData doMXSessionTestWithBobAndAliceInARoom:self andStore:[[MXFileStore alloc] init] readyToTest:^(MXSession *bobSession, MXRestClient *aliceRestClient, NSString *roomId, XCTestExpectation *expectation) { - - bobSession.roomSummaryUpdateDelegate = self; - - MXRoom *room = [bobSession roomWithRoomId:roomId]; - - // Set a RR position to get notifications for new incoming messsages - [room markAllAsRead]; - - MXRoomSummary *summary = room.summary; - - NSUInteger notificationCount = room.summary.notificationCount; - __block NSString *lastMessageEventId = nil; - - self->observer = [[NSNotificationCenter defaultCenter] addObserverForName:kMXRoomSummaryDidChangeNotification object:summary queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) { - - if ([room.summary.lastMessage.eventId isEqualToString:lastMessageEventId]) - { - // 3 -> From Bob's POV, the room notification count must increase - XCTAssertEqual(room.summary.notificationCount, notificationCount + 1); - - [expectation fulfill]; - } - }]; - - // 2 - Alice sends a message - NSString *message = [NSString stringWithFormat:@"%@: Hello", bobSession.myUser.userId]; - [aliceRestClient sendTextMessageToRoom:roomId threadId:nil text:message success:^(NSString *eventId) { - lastMessageEventId = eventId; - } failure:^(NSError *error) { - XCTFail(@"Cannot set up intial test conditions - error: %@", error); - [expectation fulfill]; - }]; - }]; -} - @end #pragma clang diagnostic pop diff --git a/MatrixSDKTests/MXStoreRoomListDataManagerUnitTests.swift b/MatrixSDKTests/MXStoreRoomListDataManagerUnitTests.swift index e68d6778dd..0d20109029 100644 --- a/MatrixSDKTests/MXStoreRoomListDataManagerUnitTests.swift +++ b/MatrixSDKTests/MXStoreRoomListDataManagerUnitTests.swift @@ -33,7 +33,6 @@ class MXStoreRoomListDataManagerUnitTests: XCTestCase { override class func setUp() { MXSDKOptions.sharedInstance().roomListDataManagerClass = MXStoreRoomListDataManager.self - MXRealmCryptoStore.deleteAllStores() } private var basicFetchOptions: MXRoomListDataFetchOptions { diff --git a/MatrixSDKTests/MXThreadEventTimelineUnitTests.swift b/MatrixSDKTests/MXThreadEventTimelineUnitTests.swift index 396a031f31..893dc201f9 100644 --- a/MatrixSDKTests/MXThreadEventTimelineUnitTests.swift +++ b/MatrixSDKTests/MXThreadEventTimelineUnitTests.swift @@ -29,7 +29,6 @@ class MXThreadEventTimelineUnitTests: XCTestCase { } override class func setUp() { - MXRealmCryptoStore.deleteAllStores() MXSDKOptions.sharedInstance().enableThreads = true } diff --git a/MatrixSDKTests/MXThreadingServiceUnitTests.swift b/MatrixSDKTests/MXThreadingServiceUnitTests.swift index 85688e3128..c5eba80820 100644 --- a/MatrixSDKTests/MXThreadingServiceUnitTests.swift +++ b/MatrixSDKTests/MXThreadingServiceUnitTests.swift @@ -31,7 +31,6 @@ class MXThreadingServiceUnitTests: XCTestCase { } override class func setUp() { - MXRealmCryptoStore.deleteAllStores() MXSDKOptions.sharedInstance().enableThreads = true } diff --git a/MatrixSDKTests/MatrixSDKTests-Bridging-Header.h b/MatrixSDKTests/MatrixSDKTests-Bridging-Header.h index cc92091b65..c7c21f2d6e 100644 --- a/MatrixSDKTests/MatrixSDKTests-Bridging-Header.h +++ b/MatrixSDKTests/MatrixSDKTests-Bridging-Header.h @@ -19,17 +19,12 @@ #import "MatrixSDKTestsData.h" #import "MatrixSDKTestsE2EData.h" -#import "MXDeviceListOperationsPool.h" #import "MXBackgroundTask.h" #import "MXUIKitBackgroundModeHandler.h" #import "MXApplicationProtocol.h" -#import "MXCrypto_Private.h" #import "MXRestClientStub.h" -#import "MXCurve25519KeyBackupAlgorithm.h" -#import "MXAes256KeyBackupAlgorithm.h" #import "MXCurve25519BackupAuthData.h" #import "MXAes256BackupAuthData.h" -#import "MXKeyBackupPassword.h" #import "MXSecretStorage_Private.h" #import "MXEncryptedSecretContent.h" diff --git a/MatrixSDKTests/MatrixSDKTestsData.m b/MatrixSDKTests/MatrixSDKTestsData.m index 7d0b7b68bb..38dd35aa4c 100644 --- a/MatrixSDKTests/MatrixSDKTestsData.m +++ b/MatrixSDKTests/MatrixSDKTestsData.m @@ -1045,7 +1045,6 @@ - (void)releaseRetainedObjects } } _retainedObjects = nil; - [MXRealmCryptoStore deleteAllStores]; } @end diff --git a/MatrixSDKTests/MatrixSDKTestsE2EData.h b/MatrixSDKTests/MatrixSDKTestsE2EData.h index 982b2d302b..8cd89e7a60 100644 --- a/MatrixSDKTests/MatrixSDKTestsE2EData.h +++ b/MatrixSDKTests/MatrixSDKTestsE2EData.h @@ -15,7 +15,6 @@ */ #import -#import #import "MXSDKOptions.h" diff --git a/MatrixSDKTests/MatrixSDKTestsE2EData.m b/MatrixSDKTests/MatrixSDKTestsE2EData.m index c6e6cf3e33..2ec2db2a79 100644 --- a/MatrixSDKTests/MatrixSDKTestsE2EData.m +++ b/MatrixSDKTests/MatrixSDKTestsE2EData.m @@ -19,9 +19,7 @@ #ifdef MX_CRYPTO #import "MXSession.h" -#import "MXCrypto_Private.h" #import "MXMegolmExportEncryption.h" -#import "MXDeviceListOperation.h" #import "MXFileStore.h" #import "MXNoStore.h" #import "MXTools.h" @@ -112,63 +110,6 @@ - (void)doE2ETestWithAliceInARoom:(XCTestCase *)testCase andStore:(id)s }]; } -- (void)doE2ETestWithAliceAndBobInARoom:(XCTestCase*)testCase - cryptedBob:(BOOL)cryptedBob - warnOnUnknowDevices:(BOOL)warnOnUnknowDevices - readyToTest:(void (^)(MXSession *aliceSession, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation))readyToTest -{ - [self doE2ETestWithAliceAndBobInARoom:testCase cryptedBob:cryptedBob warnOnUnknowDevices:warnOnUnknowDevices aliceStore:[[MXNoStore alloc] init] bobStore:[[MXNoStore alloc] init] readyToTest:readyToTest]; -} - -- (void)doE2ETestWithAliceAndBobInARoom:(XCTestCase*)testCase - cryptedBob:(BOOL)cryptedBob - warnOnUnknowDevices:(BOOL)warnOnUnknowDevices - aliceStore:(id)aliceStore - bobStore:(id)bobStore - readyToTest:(void (^)(MXSession *aliceSession, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation))readyToTest -{ - [self doE2ETestWithAliceInARoom:testCase andStore:aliceStore readyToTest:^(MXSession *aliceSession, NSString *roomId, XCTestExpectation *expectation) { - - MXRoom *room = [aliceSession roomWithRoomId:roomId]; - - [MXSDKOptions sharedInstance].enableCryptoWhenStartingMXSession = cryptedBob; - if (cryptedBob) - { - MXKeyProvider.sharedInstance.delegate = [[MXKeyProviderStub alloc] init]; - } - - [matrixSDKTestsData doMXSessionTestWithBob:nil andStore:bobStore readyToTest:^(MXSession *bobSession, XCTestExpectation *expectation2) { - - [MXSDKOptions sharedInstance].enableCryptoWhenStartingMXSession = NO; - MXKeyProvider.sharedInstance.delegate = nil; - - aliceSession.legacyCrypto.warnOnUnknowDevices = warnOnUnknowDevices; - bobSession.legacyCrypto.warnOnUnknowDevices = warnOnUnknowDevices; - - // Listen to Bob MXSessionNewRoomNotification event - __block __weak id observer = [[NSNotificationCenter defaultCenter] addObserverForName:kMXSessionNewRoomNotification object:bobSession queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) { - - [[NSNotificationCenter defaultCenter] removeObserver:observer]; - - [bobSession joinRoom:note.userInfo[kMXSessionNotificationRoomIdKey] viaServers:nil success:^(MXRoom *room) { - - readyToTest(aliceSession, bobSession, room.roomId, expectation); - - } failure:^(NSError *error) { - [matrixSDKTestsData breakTestCase:testCase reason:@"Cannot join a room - error: %@", error]; - }]; - }]; - - [room inviteUser:bobSession.myUser.userId success:nil failure:^(NSError *error) { - [[NSNotificationCenter defaultCenter] removeObserver:observer]; - [matrixSDKTestsData breakTestCase:testCase reason:@"Cannot invite Bob (%@) - error: %@", bobSession.myUser.userId, error]; - }]; - - }]; - - }]; -} - - (void)doE2ETestWithAliceByInvitingBobInARoom:(XCTestCase*)testCase cryptedBob:(BOOL)cryptedBob warnOnUnknowDevices:(BOOL)warnOnUnknowDevices @@ -177,42 +118,6 @@ - (void)doE2ETestWithAliceByInvitingBobInARoom:(XCTestCase*)testCase [self doE2ETestWithAliceByInvitingBobInARoom:testCase cryptedBob:cryptedBob warnOnUnknowDevices:warnOnUnknowDevices aliceStore:[[MXNoStore alloc] init] bobStore:[[MXNoStore alloc] init] readyToTest:readyToTest]; } -- (void)doE2ETestWithAliceByInvitingBobInARoom:(XCTestCase*)testCase - cryptedBob:(BOOL)cryptedBob - warnOnUnknowDevices:(BOOL)warnOnUnknowDevices - aliceStore:(id)aliceStore - bobStore:(id)bobStore - readyToTest:(void (^)(MXSession *aliceSession, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation))readyToTest -{ - [self doE2ETestWithAliceInARoom:testCase andStore:aliceStore readyToTest:^(MXSession *aliceSession, NSString *roomId, XCTestExpectation *expectation) { - - MXRoom *room = [aliceSession roomWithRoomId:roomId]; - - [MXSDKOptions sharedInstance].enableCryptoWhenStartingMXSession = cryptedBob; - if (cryptedBob) - { - MXKeyProvider.sharedInstance.delegate = [[MXKeyProviderStub alloc] init]; - } - - [matrixSDKTestsData doMXSessionTestWithBob:nil andStore:bobStore readyToTest:^(MXSession *bobSession, XCTestExpectation *expectation2) { - - [MXSDKOptions sharedInstance].enableCryptoWhenStartingMXSession = NO; - MXKeyProvider.sharedInstance.delegate = nil; - - aliceSession.legacyCrypto.warnOnUnknowDevices = warnOnUnknowDevices; - bobSession.legacyCrypto.warnOnUnknowDevices = warnOnUnknowDevices; - - [room inviteUser:bobSession.myUser.userId success:^{ - readyToTest(aliceSession, bobSession, room.roomId, expectation); - } failure:^(NSError *error) { - [matrixSDKTestsData breakTestCase:testCase reason:@"Cannot invite Bob (%@) - error: %@", bobSession.myUser.userId, error]; - }]; - - }]; - - }]; -} - - (void)doE2ETestWithAliceAndBobInARoomWithCryptedMessages:(XCTestCase*)testCase cryptedBob:(BOOL)cryptedBob readyToTest:(void (^)(MXSession *aliceSession, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation))readyToTest @@ -258,57 +163,6 @@ - (void)doE2ETestWithAliceAndBobInARoomWithCryptedMessages:(XCTestCase*)testCase }]; } -- (void)doE2ETestWithAliceAndBobAndSamInARoom:(XCTestCase*)testCase - cryptedBob:(BOOL)cryptedBob - cryptedSam:(BOOL)cryptedSam - warnOnUnknowDevices:(BOOL)warnOnUnknowDevices - readyToTest:(void (^)(MXSession *aliceSession, MXSession *bobSession, MXSession *samSession, NSString *roomId, XCTestExpectation *expectation))readyToTest -{ - [self doE2ETestWithAliceAndBobInARoom:testCase cryptedBob:cryptedBob warnOnUnknowDevices:warnOnUnknowDevices readyToTest:^(MXSession *aliceSession, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation) { - - - MXRoom *room = [aliceSession roomWithRoomId:roomId]; - - [MXSDKOptions sharedInstance].enableCryptoWhenStartingMXSession = cryptedSam; - if (cryptedSam) - { - MXKeyProvider.sharedInstance.delegate = [[MXKeyProviderStub alloc] init]; - } - - // Ugly hack: Create a bob from another MatrixSDKTestsData instance and call him Sam... - MatrixSDKTestsData *matrixSDKTestsData2 = [[MatrixSDKTestsData alloc] init]; - [matrixSDKTestsData2 doMXSessionTestWithBob:nil readyToTest:^(MXSession *samSession, XCTestExpectation *expectation2) { - - [MXSDKOptions sharedInstance].enableCryptoWhenStartingMXSession = NO; - MXKeyProvider.sharedInstance.delegate = nil; - - aliceSession.legacyCrypto.warnOnUnknowDevices = warnOnUnknowDevices; - bobSession.legacyCrypto.warnOnUnknowDevices = warnOnUnknowDevices; - samSession.legacyCrypto.warnOnUnknowDevices = warnOnUnknowDevices; - - // Listen to Sam MXSessionNewRoomNotification event - __block __weak id observer = [[NSNotificationCenter defaultCenter] addObserverForName:kMXSessionNewRoomNotification object:samSession queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) { - - [[NSNotificationCenter defaultCenter] removeObserver:observer]; - - [samSession joinRoom:note.userInfo[kMXSessionNotificationRoomIdKey] viaServers:nil success:^(MXRoom *room) { - - readyToTest(aliceSession, bobSession, samSession, room.roomId, expectation); - - } failure:^(NSError *error) { - [matrixSDKTestsData breakTestCase:testCase reason:@"Cannot join a room - error: %@", error]; - }]; - }]; - - [room inviteUser:samSession.myUser.userId success:nil failure:^(NSError *error) { - [[NSNotificationCenter defaultCenter] removeObserver:observer]; - [matrixSDKTestsData breakTestCase:testCase reason:@"Cannot invite Alice - error: %@", error]; - }]; - - }]; - }]; -} - - (void)loginUserOnANewDevice:(XCTestCase*)testCase credentials:(MXCredentials*)credentials withPassword:(NSString*)password @@ -450,24 +304,6 @@ - (void)doTestWithBobAndAliceWithTwoDevicesAllTrusted:(XCTestCase*)testCase } -#pragma mark - Tools - -- (void)outgoingRoomKeyRequestInSession:(MXSession*)session complete:(void (^)(MXOutgoingRoomKeyRequest*))complete -{ - dispatch_async(session.legacyCrypto.cryptoQueue, ^{ - MXOutgoingRoomKeyRequest *outgoingRoomKeyRequest = [session.legacyCrypto.store outgoingRoomKeyRequestWithState:MXRoomKeyRequestStateUnsent]; - if (!outgoingRoomKeyRequest) - { - outgoingRoomKeyRequest = [session.legacyCrypto.store outgoingRoomKeyRequestWithState:MXRoomKeyRequestStateSent]; - } - - dispatch_async(dispatch_get_main_queue(), ^{ - complete(outgoingRoomKeyRequest); - }); - }); -} - - @end #endif // MX_CRYPTO diff --git a/Podfile b/Podfile index a4bf99cc4e..899fe8e075 100644 --- a/Podfile +++ b/Podfile @@ -13,9 +13,6 @@ abstract_target 'MatrixSDK' do pod 'SwiftyBeaver', '1.9.5' - pod 'OLMKit', '~> 3.2.5', :inhibit_warnings => true - #pod 'OLMKit', :path => '../olm/OLMKit.podspec' - pod 'Realm', '10.27.0' pod 'libbase58', '~> 0.1.4' pod 'MatrixSDKCrypto', '0.4.3', :inhibit_warnings => true diff --git a/Podfile.lock b/Podfile.lock index 3041ea5cb6..86011bb538 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -30,11 +30,6 @@ PODS: - OHHTTPStubs/NSURLSession (9.1.0): - OHHTTPStubs/Core - OHHTTPStubs/OHPathHelpers (9.1.0) - - OLMKit (3.2.12): - - OLMKit/olmc (= 3.2.12) - - OLMKit/olmcpp (= 3.2.12) - - OLMKit/olmc (3.2.12) - - OLMKit/olmcpp (3.2.12) - Realm (10.27.0): - Realm/Headers (= 10.27.0) - Realm/Headers (10.27.0) @@ -46,7 +41,6 @@ DEPENDENCIES: - libbase58 (~> 0.1.4) - MatrixSDKCrypto (= 0.4.3) - OHHTTPStubs (~> 9.1.0) - - OLMKit (~> 3.2.5) - Realm (= 10.27.0) - SwiftyBeaver (= 1.9.5) @@ -57,7 +51,6 @@ SPEC REPOS: - libbase58 - MatrixSDKCrypto - OHHTTPStubs - - OLMKit - Realm - SwiftyBeaver @@ -67,10 +60,9 @@ SPEC CHECKSUMS: libbase58: 7c040313537b8c44b6e2d15586af8e21f7354efd MatrixSDKCrypto: 27bee960e0e8b3a3039f3f3e93dd2ec88299c77e OHHTTPStubs: 90eac6d8f2c18317baeca36698523dc67c513831 - OLMKit: da115f16582e47626616874e20f7bb92222c7a51 Realm: 9ca328bd7e700cc19703799785e37f77d1a130f2 SwiftyBeaver: 84069991dd5dca07d7069100985badaca7f0ce82 -PODFILE CHECKSUM: cb51affdced38356ae06d1eb0f8f4526e203e1cd +PODFILE CHECKSUM: a2fe7b4dcd95b04f52989dc47cded48c782c02a4 COCOAPODS: 1.15.2