From edc99aa581d99cd8f9f4172e81b140905621d8a6 Mon Sep 17 00:00:00 2001 From: Andy Uhnak Date: Fri, 14 Apr 2023 15:53:11 +0100 Subject: [PATCH 1/4] Enable Crypto SDK by default --- .github/workflows/ci-crypto-tests.yml | 86 ----- MatrixSDK.xcodeproj/project.pbxproj | 134 ++++--- .../xcschemes/MatrixSDK-macOS.xcscheme | 3 - .../CrossSigning/MXCrossSigningV2.swift | 2 +- MatrixSDK/Crypto/MXCrypto.m | 29 +- MatrixSDK/Crypto/MXCryptoV2Factory.swift | 17 +- MatrixSDK/Crypto/MXCryptoV2Feature.swift | 50 --- MatrixSDK/MXSDKOptions.h | 15 +- MatrixSDK/MXSDKOptions.m | 12 +- .../MXCrossSigningV2UnitTests.swift | 12 + .../CryptoMachine/MXKeyProviderStub.swift | 31 ++ .../Crypto/MXCryptoV2FactoryTests.swift | 144 -------- .../Crypto/MXCryptoV2FactoryUnitTests.swift | 151 ++++++++ .../Crypto/MXSession+LegacyCrypto.swift | 1 - .../LegacyRealmStore/LegacyRealmStore.swift | 162 +++++++++ .../LegacyRealmStore/archived_encrypted_event | Bin 0 -> 1275 bytes .../legacy_deprecated1_account.realm | Bin 0 -> 32768 bytes .../legacy_deprecated3_account.realm | Bin 0 -> 32768 bytes .../legacy_unverified_account.realm | Bin 0 -> 32768 bytes .../legacy_verified_account.realm | Bin 0 -> 65536 bytes .../legacy_version2_account.realm | Bin 0 -> 32768 bytes .../Migration/MXCryptoMigrationV2Tests.swift | 331 ------------------ .../MXCryptoMigrationV2UnitTests.swift | 167 +++++++++ MatrixSDKTests/MXBaseKeyBackupTests.m | 18 +- MatrixSDKTests/MXCrossSigningTests.m | 6 +- .../MXCrossSigningVerificationTests.m | 6 +- MatrixSDKTests/MXCryptoKeyVerificationTests.m | 21 +- MatrixSDKTests/MXCryptoShareTests.m | 15 +- MatrixSDKTests/MXCryptoTests.m | 25 +- MatrixSDKTests/MXCurve25519KeyBackupTests.m | 125 ------- MatrixSDKTests/MatrixSDKTestsE2EData.m | 21 ++ .../TestPlans/AllWorkingTests.xctestplan | 87 ++++- .../TestPlans/CryptoTests.xctestplan | 52 --- MatrixSDKTests/TestPlans/UnitTests.xctestplan | 2 + .../UnitTestsWithSanitizers.xctestplan | 2 + changelog.d/pr-1770.change | 1 + 36 files changed, 791 insertions(+), 937 deletions(-) delete mode 100644 .github/workflows/ci-crypto-tests.yml delete mode 100644 MatrixSDK/Crypto/MXCryptoV2Feature.swift create mode 100644 MatrixSDKTests/Crypto/CryptoMachine/MXKeyProviderStub.swift delete mode 100644 MatrixSDKTests/Crypto/MXCryptoV2FactoryTests.swift create mode 100644 MatrixSDKTests/Crypto/MXCryptoV2FactoryUnitTests.swift create mode 100644 MatrixSDKTests/Crypto/Migration/LegacyRealmStore/LegacyRealmStore.swift create mode 100644 MatrixSDKTests/Crypto/Migration/LegacyRealmStore/archived_encrypted_event create mode 100644 MatrixSDKTests/Crypto/Migration/LegacyRealmStore/legacy_deprecated1_account.realm create mode 100644 MatrixSDKTests/Crypto/Migration/LegacyRealmStore/legacy_deprecated3_account.realm create mode 100644 MatrixSDKTests/Crypto/Migration/LegacyRealmStore/legacy_unverified_account.realm create mode 100644 MatrixSDKTests/Crypto/Migration/LegacyRealmStore/legacy_verified_account.realm create mode 100644 MatrixSDKTests/Crypto/Migration/LegacyRealmStore/legacy_version2_account.realm delete mode 100644 MatrixSDKTests/Crypto/Migration/MXCryptoMigrationV2Tests.swift create mode 100644 MatrixSDKTests/Crypto/Migration/MXCryptoMigrationV2UnitTests.swift delete mode 100644 MatrixSDKTests/MXCurve25519KeyBackupTests.m delete mode 100644 MatrixSDKTests/TestPlans/CryptoTests.xctestplan create mode 100644 changelog.d/pr-1770.change diff --git a/.github/workflows/ci-crypto-tests.yml b/.github/workflows/ci-crypto-tests.yml deleted file mode 100644 index 60bc264fa3..0000000000 --- a/.github/workflows/ci-crypto-tests.yml +++ /dev/null @@ -1,86 +0,0 @@ -name: Crypto Tests CI - -on: - # Triggers the workflow on any pull request and push to develop - push: - branches: [ develop ] - pull_request: - - # Allows you to run this workflow manually from the Actions tab - workflow_dispatch: - -jobs: - crypto-tests: - name: Crypto Tests with Synapse - runs-on: macos-11 - - concurrency: - # When running on develop, use the sha to allow all runs of this workflow to run concurrently. - # Otherwise only allow a single run of this workflow on each branch, automatically cancelling older runs. - group: ${{ github.ref == 'refs/heads/develop' && format('crypto-develop-{0}', github.sha) || format('crypto-{0}', github.ref) }} - cancel-in-progress: true - - steps: - - uses: actions/checkout@v2 - - # Cache for python env for Synapse - - name: Set up Python 3.8 - uses: actions/setup-python@v2 - with: - python-version: 3.8 - - name: Cache pip - uses: actions/cache@v2 - with: - path: ~/.cache/pip - key: ${{ runner.os }}-pip - restore-keys: | - ${{ runner.os }}-pip- - ${{ runner.os }}- - # Cache for Xcode env - - uses: actions/cache@v2 - with: - path: Pods - key: ${{ runner.os }}-pods-${{ hashFiles('**/Podfile.lock') }} - restore-keys: | - ${{ runner.os }}-pods- - - uses: actions/cache@v2 - with: - path: vendor/bundle - key: ${{ runner.os }}-gems-${{ hashFiles('**/Gemfile.lock') }} - restore-keys: | - ${{ runner.os }}-gems- - - name: Start synapse server - uses: michaelkaye/setup-matrix-synapse@v1.0.3 - with: - uploadLogs: true - httpPort: 8080 - disableRateLimiting: true - - - name: Bundle install - run: | - bundle config path vendor/bundle - bundle install --jobs 4 --retry 3 - # Main step - - name: Crypto tests - run: bundle exec fastlane test testplan:CryptoTests - - # Store artifacts - - uses: actions/upload-artifact@v2 - if: always() - with: - name: report.html - path: build/test/report.html - - uses: actions/upload-artifact@v2 - if: always() - with: - name: report.junit - path: build/test/report.junit - - uses: actions/upload-artifact@v2 - if: always() - with: - name: MatrixSDK-macOS.xcresult - path: build/test/MatrixSDK-macOS.xcresult/ - - # Upload coverage - - name: Upload coverage to Codecov - uses: codecov/codecov-action@v3 diff --git a/MatrixSDK.xcodeproj/project.pbxproj b/MatrixSDK.xcodeproj/project.pbxproj index 7e94b2d397..643661e11e 100644 --- a/MatrixSDK.xcodeproj/project.pbxproj +++ b/MatrixSDK.xcodeproj/project.pbxproj @@ -414,7 +414,6 @@ 3291DC8423DF52E20009732F /* MXRoomCreationParameters.h in Headers */ = {isa = PBXBuildFile; fileRef = 3291DC8123DF52E10009732F /* MXRoomCreationParameters.h */; settings = {ATTRIBUTES = (Public, ); }; }; 3291DC8523DF52E20009732F /* MXRoomCreationParameters.m in Sources */ = {isa = PBXBuildFile; fileRef = 3291DC8223DF52E10009732F /* MXRoomCreationParameters.m */; }; 3291DC8623DF52E20009732F /* MXRoomCreationParameters.m in Sources */ = {isa = PBXBuildFile; fileRef = 3291DC8223DF52E10009732F /* MXRoomCreationParameters.m */; }; - 32935F61216FA49D00A1BC24 /* MXCurve25519KeyBackupTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 32935F60216FA49D00A1BC24 /* MXCurve25519KeyBackupTests.m */; }; 3293C700214BBA4F009B3DDB /* MXPeekingRoomSummary.h in Headers */ = {isa = PBXBuildFile; fileRef = 3293C6FE214BBA4F009B3DDB /* MXPeekingRoomSummary.h */; }; 3293C701214BBA4F009B3DDB /* MXPeekingRoomSummary.m in Sources */ = {isa = PBXBuildFile; fileRef = 3293C6FF214BBA4F009B3DDB /* MXPeekingRoomSummary.m */; }; 3294FD9D22F321B0007F1E60 /* MXServiceTermsRestClient.m in Sources */ = {isa = PBXBuildFile; fileRef = 3294FD9922F321B0007F1E60 /* MXServiceTermsRestClient.m */; }; @@ -876,7 +875,7 @@ B14EF1E32397E90400758AF0 /* MXCall.m in Sources */ = {isa = PBXBuildFile; fileRef = 3245A74D1AF7B2930001D8A7 /* MXCall.m */; }; B14EF1E42397E90400758AF0 /* MXWellknownIntegrations.m in Sources */ = {isa = PBXBuildFile; fileRef = 32CF439C2371AF9500907C56 /* MXWellknownIntegrations.m */; }; B14EF1E52397E90400758AF0 /* MXLoginPolicy.m in Sources */ = {isa = PBXBuildFile; fileRef = 3275FD9B21A6B60B00B9C13D /* MXLoginPolicy.m */; }; - B14EF1E62397E90400758AF0 /* BuildFile in Sources */ = {isa = PBXBuildFile; }; + B14EF1E62397E90400758AF0 /* (null) in Sources */ = {isa = PBXBuildFile; }; B14EF1E72397E90400758AF0 /* MXRoomThirdPartyInvite.m in Sources */ = {isa = PBXBuildFile; fileRef = 327F8DB11C6112BA00581CA3 /* MXRoomThirdPartyInvite.m */; }; B14EF1E82397E90400758AF0 /* MXRoomPowerLevels.m in Sources */ = {isa = PBXBuildFile; fileRef = B17982F42119E4A2001FD722 /* MXRoomPowerLevels.m */; }; B14EF1E92397E90400758AF0 /* MXRealmMediaScanMapper.m in Sources */ = {isa = PBXBuildFile; fileRef = B146D4DE21A5AEF100D8C2C6 /* MXRealmMediaScanMapper.m */; }; @@ -933,7 +932,7 @@ B14EF21D2397E90400758AF0 /* MXEncryptedContentKey.m in Sources */ = {isa = PBXBuildFile; fileRef = 021AFBA12179E91800742B2C /* MXEncryptedContentKey.m */; }; B14EF21E2397E90400758AF0 /* MXEventDecryptionResult.m in Sources */ = {isa = PBXBuildFile; fileRef = 32F634AA1FC5E3470054EF49 /* MXEventDecryptionResult.m */; }; B14EF21F2397E90400758AF0 /* MXMyUser.m in Sources */ = {isa = PBXBuildFile; fileRef = 327137261A24D50A00DB6757 /* MXMyUser.m */; }; - B14EF2202397E90400758AF0 /* BuildFile in Sources */ = {isa = PBXBuildFile; }; + B14EF2202397E90400758AF0 /* (null) in Sources */ = {isa = PBXBuildFile; }; B14EF2212397E90400758AF0 /* MX3PID.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6F935831E5B3BE600FC34BF /* MX3PID.swift */; }; B14EF2222397E90400758AF0 /* MXMediaScan.m in Sources */ = {isa = PBXBuildFile; fileRef = B146D47621A5950800D8C2C6 /* MXMediaScan.m */; }; B14EF2232397E90400758AF0 /* MXEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6F935861E5B3BE600FC34BF /* MXEvent.swift */; }; @@ -948,7 +947,7 @@ B14EF22C2397E90400758AF0 /* MXAccountData.m in Sources */ = {isa = PBXBuildFile; fileRef = 3264DB901CEC528D00B99881 /* MXAccountData.m */; }; B14EF22D2397E90400758AF0 /* MXRealmReactionCount.m in Sources */ = {isa = PBXBuildFile; fileRef = 32133018228B010C0070BA9B /* MXRealmReactionCount.m */; }; B14EF22E2397E90400758AF0 /* MXCryptoTools.m in Sources */ = {isa = PBXBuildFile; fileRef = 3250E7C9220C913900736CB5 /* MXCryptoTools.m */; }; - B14EF22F2397E90400758AF0 /* BuildFile in Sources */ = {isa = PBXBuildFile; }; + 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 */; }; @@ -992,7 +991,7 @@ B14EF25B2397E90400758AF0 /* MXSession.m in Sources */ = {isa = PBXBuildFile; fileRef = 320DFDD119DD99B60068622A /* MXSession.m */; }; B14EF25C2397E90400758AF0 /* MXRoomTombStoneContent.m in Sources */ = {isa = PBXBuildFile; fileRef = B17982EE2119E49F001FD722 /* MXRoomTombStoneContent.m */; }; B14EF25D2397E90400758AF0 /* MXImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C602B58D1F22A8D700B67D87 /* MXImage.swift */; }; - B14EF25E2397E90400758AF0 /* BuildFile in Sources */ = {isa = PBXBuildFile; }; + B14EF25E2397E90400758AF0 /* (null) in Sources */ = {isa = PBXBuildFile; }; B14EF25F2397E90400758AF0 /* MXServerNoticeContent.m in Sources */ = {isa = PBXBuildFile; fileRef = 32954018216385F100E300FC /* MXServerNoticeContent.m */; }; B14EF2602397E90400758AF0 /* MXContentScanResult.m in Sources */ = {isa = PBXBuildFile; fileRef = 02CAD434217DD12F0074700B /* MXContentScanResult.m */; }; B14EF2612397E90400758AF0 /* MXRealmAggregationsStore.m in Sources */ = {isa = PBXBuildFile; fileRef = 32133014228AF4EF0070BA9B /* MXRealmAggregationsStore.m */; }; @@ -1030,7 +1029,7 @@ 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 /* BuildFile in Sources */ = {isa = PBXBuildFile; }; + 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 */; }; @@ -1354,7 +1353,6 @@ B1E09A1D2397FCE90057C069 /* MXCryptoKeyVerificationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 329E808E22512DF500A48C3A /* MXCryptoKeyVerificationTests.m */; }; B1E09A1E2397FCE90057C069 /* MXCryptoShareTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 322D01C322492B0700150C68 /* MXCryptoShareTests.m */; }; B1E09A1F2397FCE90057C069 /* MXAutoDiscoveryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 32720DA1222EB5650086FFF5 /* MXAutoDiscoveryTests.m */; }; - B1E09A202397FCE90057C069 /* MXCurve25519KeyBackupTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 32935F60216FA49D00A1BC24 /* MXCurve25519KeyBackupTests.m */; }; B1E09A212397FCE90057C069 /* DirectRoomTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 32C03CB52123076F00D92712 /* DirectRoomTests.m */; }; B1E09A222397FCE90057C069 /* MXRoomSummaryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 320E1BC01E0AD674009635F5 /* MXRoomSummaryTests.m */; }; B1E09A242397FCE90057C069 /* MXPeekingRoomTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 32E226A81D081CE200E6CA54 /* MXPeekingRoomTests.m */; }; @@ -1886,8 +1884,8 @@ 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 /* MXCryptoMigrationV2Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED55806F296F1BEE003443E3 /* MXCryptoMigrationV2Tests.swift */; }; - ED558071296F1BEE003443E3 /* MXCryptoMigrationV2Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED55806F296F1BEE003443E3 /* MXCryptoMigrationV2Tests.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 */; }; @@ -2003,14 +2001,30 @@ ED997857292E2877006B5248 /* MXSessionStartupProgressUnitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED997855292E2877006B5248 /* MXSessionStartupProgressUnitTests.swift */; }; EDA2CDD628F5C4230088ACE7 /* MXQRCodeTransactionV2UnitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDA2CDD528F5C4230088ACE7 /* MXQRCodeTransactionV2UnitTests.swift */; }; EDA2CDD728F5C4230088ACE7 /* MXQRCodeTransactionV2UnitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDA2CDD528F5C4230088ACE7 /* MXQRCodeTransactionV2UnitTests.swift */; }; + EDA40A0529E9D6BE00C0CAB9 /* MXKeyProviderStub.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDA40A0429E9D6BE00C0CAB9 /* MXKeyProviderStub.swift */; }; + EDA40A0629E9D6BE00C0CAB9 /* MXKeyProviderStub.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDA40A0429E9D6BE00C0CAB9 /* MXKeyProviderStub.swift */; }; + EDA40A0F29E9E2BF00C0CAB9 /* legacy_version2_account.realm in Resources */ = {isa = PBXBuildFile; fileRef = EDA40A0829E9E2BF00C0CAB9 /* legacy_version2_account.realm */; }; + EDA40A1029E9E2BF00C0CAB9 /* legacy_version2_account.realm in Resources */ = {isa = PBXBuildFile; fileRef = EDA40A0829E9E2BF00C0CAB9 /* legacy_version2_account.realm */; }; + EDA40A1129E9E2BF00C0CAB9 /* legacy_verified_account.realm in Resources */ = {isa = PBXBuildFile; fileRef = EDA40A0929E9E2BF00C0CAB9 /* legacy_verified_account.realm */; }; + EDA40A1229E9E2BF00C0CAB9 /* legacy_verified_account.realm in Resources */ = {isa = PBXBuildFile; fileRef = EDA40A0929E9E2BF00C0CAB9 /* legacy_verified_account.realm */; }; + EDA40A1329E9E2BF00C0CAB9 /* legacy_unverified_account.realm in Resources */ = {isa = PBXBuildFile; fileRef = EDA40A0A29E9E2BF00C0CAB9 /* legacy_unverified_account.realm */; }; + EDA40A1429E9E2BF00C0CAB9 /* legacy_unverified_account.realm in Resources */ = {isa = PBXBuildFile; fileRef = EDA40A0A29E9E2BF00C0CAB9 /* legacy_unverified_account.realm */; }; + EDA40A1529E9E2BF00C0CAB9 /* legacy_deprecated3_account.realm in Resources */ = {isa = PBXBuildFile; fileRef = EDA40A0B29E9E2BF00C0CAB9 /* legacy_deprecated3_account.realm */; }; + EDA40A1629E9E2BF00C0CAB9 /* legacy_deprecated3_account.realm in Resources */ = {isa = PBXBuildFile; fileRef = EDA40A0B29E9E2BF00C0CAB9 /* legacy_deprecated3_account.realm */; }; + EDA40A1729E9E2BF00C0CAB9 /* legacy_deprecated1_account.realm in Resources */ = {isa = PBXBuildFile; fileRef = EDA40A0C29E9E2BF00C0CAB9 /* legacy_deprecated1_account.realm */; }; + 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 */; }; EDAAC41A28E2FCFE00DD89B5 /* MXCryptoSecretStoreV2.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDAAC41828E2FCFE00DD89B5 /* MXCryptoSecretStoreV2.swift */; }; - EDAAC41C28E30F3C00DD89B5 /* BuildFile in Headers */ = {isa = PBXBuildFile; settings = {ATTRIBUTES = (Public, ); }; }; - EDAAC41D28E30F3C00DD89B5 /* BuildFile in Headers */ = {isa = PBXBuildFile; settings = {ATTRIBUTES = (Public, ); }; }; - EDAAC41F28E30F4C00DD89B5 /* BuildFile in Sources */ = {isa = PBXBuildFile; }; - EDAAC42028E30F4C00DD89B5 /* BuildFile in Sources */ = {isa = PBXBuildFile; }; + EDAAC41C28E30F3C00DD89B5 /* (null) in Headers */ = {isa = PBXBuildFile; settings = {ATTRIBUTES = (Public, ); }; }; + EDAAC41D28E30F3C00DD89B5 /* (null) in Headers */ = {isa = PBXBuildFile; settings = {ATTRIBUTES = (Public, ); }; }; + EDAAC41F28E30F4C00DD89B5 /* (null) in Sources */ = {isa = PBXBuildFile; }; + EDAAC42028E30F4C00DD89B5 /* (null) in Sources */ = {isa = PBXBuildFile; }; EDAAC42128E3174700DD89B5 /* MXCryptoSecretStore.h in Headers */ = {isa = PBXBuildFile; fileRef = EDAAC41228E2F86800DD89B5 /* MXCryptoSecretStore.h */; settings = {ATTRIBUTES = (Public, ); }; }; EDAAC42228E3174700DD89B5 /* MXCryptoSecretStore.h in Headers */ = {isa = PBXBuildFile; fileRef = EDAAC41228E2F86800DD89B5 /* MXCryptoSecretStore.h */; settings = {ATTRIBUTES = (Public, ); }; }; EDAAC42428E3177000DD89B5 /* MXRecoveryServiceDependencies.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDAAC42328E3177000DD89B5 /* MXRecoveryServiceDependencies.swift */; }; @@ -2025,14 +2039,10 @@ EDBCF337281A8ABE00ED5044 /* MXSharedHistoryKeyService.h in Headers */ = {isa = PBXBuildFile; fileRef = EDBCF335281A8AB900ED5044 /* MXSharedHistoryKeyService.h */; settings = {ATTRIBUTES = (Public, ); }; }; EDBCF339281A8D3D00ED5044 /* MXSharedHistoryKeyService.m in Sources */ = {isa = PBXBuildFile; fileRef = EDBCF338281A8D3D00ED5044 /* MXSharedHistoryKeyService.m */; }; EDBCF33A281A8D3D00ED5044 /* MXSharedHistoryKeyService.m in Sources */ = {isa = PBXBuildFile; fileRef = EDBCF338281A8D3D00ED5044 /* MXSharedHistoryKeyService.m */; }; - EDC2A0E628369E740039F3D6 /* CryptoTests.xctestplan in Resources */ = {isa = PBXBuildFile; fileRef = EDC2A0E528369E740039F3D6 /* CryptoTests.xctestplan */; }; - EDC2A0E728369E740039F3D6 /* CryptoTests.xctestplan in Resources */ = {isa = PBXBuildFile; fileRef = EDC2A0E528369E740039F3D6 /* CryptoTests.xctestplan */; }; EDC8C4082968A993003792C5 /* MXKeysQueryScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDC8C4072968A993003792C5 /* MXKeysQueryScheduler.swift */; }; EDC8C4092968A993003792C5 /* MXKeysQueryScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDC8C4072968A993003792C5 /* MXKeysQueryScheduler.swift */; }; EDC8C40D2968C37E003792C5 /* MXKeysQuerySchedulerUnitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDC8C40A2968A9F7003792C5 /* MXKeysQuerySchedulerUnitTests.swift */; }; EDC8C40E2968C37F003792C5 /* MXKeysQuerySchedulerUnitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDC8C40A2968A9F7003792C5 /* MXKeysQuerySchedulerUnitTests.swift */; }; - EDCAD251299BF7F40088B4DA /* MXCryptoV2Feature.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDCAD250299BF7F40088B4DA /* MXCryptoV2Feature.swift */; }; - EDCAD252299BF7F40088B4DA /* MXCryptoV2Feature.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDCAD250299BF7F40088B4DA /* MXCryptoV2Feature.swift */; }; EDCB65E22912AB0C00F55D4D /* MXRoomEventDecryption.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDCB65E12912AB0C00F55D4D /* MXRoomEventDecryption.swift */; }; EDCB65E32912AB0C00F55D4D /* MXRoomEventDecryption.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDCB65E12912AB0C00F55D4D /* MXRoomEventDecryption.swift */; }; EDCF802D2941FF220059E774 /* MXCryptoMigrationV2.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDCF802C2941FF220059E774 /* MXCryptoMigrationV2.swift */; }; @@ -2057,8 +2067,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 /* MXCryptoV2FactoryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDDB07C9297EE0A7005249A6 /* MXCryptoV2FactoryTests.swift */; }; - EDDB07CB297EE0A7005249A6 /* MXCryptoV2FactoryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDDB07C9297EE0A7005249A6 /* MXCryptoV2FactoryTests.swift */; }; + 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 */; }; @@ -2451,7 +2461,6 @@ 3291D4D31A68FFEB00C3BA41 /* MXFileRoomStore.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXFileRoomStore.m; sourceTree = ""; }; 3291DC8123DF52E10009732F /* MXRoomCreationParameters.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXRoomCreationParameters.h; sourceTree = ""; }; 3291DC8223DF52E10009732F /* MXRoomCreationParameters.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXRoomCreationParameters.m; sourceTree = ""; }; - 32935F60216FA49D00A1BC24 /* MXCurve25519KeyBackupTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXCurve25519KeyBackupTests.m; sourceTree = ""; }; 3293C6FE214BBA4F009B3DDB /* MXPeekingRoomSummary.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXPeekingRoomSummary.h; sourceTree = ""; }; 3293C6FF214BBA4F009B3DDB /* MXPeekingRoomSummary.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXPeekingRoomSummary.m; sourceTree = ""; }; 3294FD9922F321B0007F1E60 /* MXServiceTermsRestClient.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXServiceTermsRestClient.m; sourceTree = ""; }; @@ -3117,7 +3126,7 @@ 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 /* MXCryptoMigrationV2Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXCryptoMigrationV2Tests.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 = ""; }; @@ -3176,6 +3185,14 @@ ED8F1D3A2885BB2D00F897E7 /* MXCryptoProtocols.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MXCryptoProtocols.swift; sourceTree = ""; }; ED997855292E2877006B5248 /* MXSessionStartupProgressUnitTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXSessionStartupProgressUnitTests.swift; sourceTree = ""; }; EDA2CDD528F5C4230088ACE7 /* MXQRCodeTransactionV2UnitTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXQRCodeTransactionV2UnitTests.swift; sourceTree = ""; }; + EDA40A0429E9D6BE00C0CAB9 /* MXKeyProviderStub.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXKeyProviderStub.swift; sourceTree = ""; }; + EDA40A0829E9E2BF00C0CAB9 /* legacy_version2_account.realm */ = {isa = PBXFileReference; lastKnownFileType = file; path = legacy_version2_account.realm; sourceTree = ""; }; + EDA40A0929E9E2BF00C0CAB9 /* legacy_verified_account.realm */ = {isa = PBXFileReference; lastKnownFileType = file; path = legacy_verified_account.realm; sourceTree = ""; }; + EDA40A0A29E9E2BF00C0CAB9 /* legacy_unverified_account.realm */ = {isa = PBXFileReference; lastKnownFileType = file; path = legacy_unverified_account.realm; sourceTree = ""; }; + 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 = ""; }; @@ -3185,10 +3202,8 @@ EDB4209827DF842F0036AF39 /* MXEventFixtures.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXEventFixtures.swift; sourceTree = ""; }; EDBCF335281A8AB900ED5044 /* MXSharedHistoryKeyService.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXSharedHistoryKeyService.h; sourceTree = ""; }; EDBCF338281A8D3D00ED5044 /* MXSharedHistoryKeyService.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXSharedHistoryKeyService.m; sourceTree = ""; }; - EDC2A0E528369E740039F3D6 /* CryptoTests.xctestplan */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = CryptoTests.xctestplan; sourceTree = ""; }; EDC8C4072968A993003792C5 /* MXKeysQueryScheduler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXKeysQueryScheduler.swift; sourceTree = ""; }; EDC8C40A2968A9F7003792C5 /* MXKeysQuerySchedulerUnitTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXKeysQuerySchedulerUnitTests.swift; sourceTree = ""; }; - EDCAD250299BF7F40088B4DA /* MXCryptoV2Feature.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXCryptoV2Feature.swift; sourceTree = ""; }; EDCB65E12912AB0C00F55D4D /* MXRoomEventDecryption.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXRoomEventDecryption.swift; sourceTree = ""; }; EDCF802C2941FF220059E774 /* MXCryptoMigrationV2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXCryptoMigrationV2.swift; sourceTree = ""; }; EDD4197D28DCAA5F007F3757 /* MXNativeKeyBackupEngine.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXNativeKeyBackupEngine.h; sourceTree = ""; }; @@ -3201,7 +3216,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 /* MXCryptoV2FactoryTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXCryptoV2FactoryTests.swift; 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 = ""; }; @@ -3546,7 +3561,6 @@ 322A51B41D9AB15900C8536D /* MXCrypto.h */, 322A51B51D9AB15900C8536D /* MXCrypto.m */, ED47CB6C28523995004FD755 /* MXCryptoV2.swift */, - EDCAD250299BF7F40088B4DA /* MXCryptoV2Feature.swift */, ED5EF151297AB33E00A5ADDA /* MXCryptoV2Factory.swift */, 325D1C251DFECE0D0070B8BF /* MXCrypto_Private.h */, 322A51C51D9BBD3C00C8536D /* MXOlmDevice.h */, @@ -4117,7 +4131,6 @@ 326277F0288BC219009A0508 /* AllWorkingTests.xctestplan */, 3298ABD52637FA3100E40B06 /* AllTests.xctestplan */, 3209682F26396385005D64ED /* AllTestsWithSanitizers.xctestplan */, - EDC2A0E528369E740039F3D6 /* CryptoTests.xctestplan */, 3298ABDC2637FB1900E40B06 /* UnitTests.xctestplan */, 3209683026396385005D64ED /* UnitTestsWithSanitizers.xctestplan */, ); @@ -4376,7 +4389,6 @@ 324DD2BA246C3ADE00377005 /* MXCryptoSecretStorageTests.m */, 322D01C322492B0700150C68 /* MXCryptoShareTests.m */, 322A51D71D9E846800C8536D /* MXCryptoTests.m */, - 32935F60216FA49D00A1BC24 /* MXCurve25519KeyBackupTests.m */, 3A7B8CFD267FCD9B00D9DD96 /* MXDehydrationTests.m */, 3281E89D19E299C000976E1A /* MXErrorUnitTests.m */, B146D4FC21A5C0BC00D8C2C6 /* MXEventScanStoreUnitTests.m */, @@ -5443,7 +5455,7 @@ ED5C95CD2833E85600843D82 /* MXOlmDeviceUnitTests.swift */, ED825F8E29014EDA006A614E /* MXSession+LegacyCrypto.swift */, EDDD90C72901611600B760E0 /* MXLegacyCrypto+LegacyCrossSigning.swift */, - EDDB07C9297EE0A7005249A6 /* MXCryptoV2FactoryTests.swift */, + EDDB07C9297EE0A7005249A6 /* MXCryptoV2FactoryUnitTests.swift */, ); path = Crypto; sourceTree = ""; @@ -5499,6 +5511,7 @@ ED8F1D312885AC5700F897E7 /* Device+Stub.swift */, ED1FE90A2912E13A0046F722 /* DecryptedEvent+Stub.swift */, EDC8C40A2968A9F7003792C5 /* MXKeysQuerySchedulerUnitTests.swift */, + EDA40A0429E9D6BE00C0CAB9 /* MXKeyProviderStub.swift */, ); path = CryptoMachine; sourceTree = ""; @@ -5577,8 +5590,9 @@ ED55806A296F0E18003443E3 /* Migration */ = { isa = PBXGroup; children = ( + EDA40A0729E9E2BF00C0CAB9 /* LegacyRealmStore */, ED55806B296F0E1D003443E3 /* Data */, - ED55806F296F1BEE003443E3 /* MXCryptoMigrationV2Tests.swift */, + ED55806F296F1BEE003443E3 /* MXCryptoMigrationV2UnitTests.swift */, ); path = Migration; sourceTree = ""; @@ -5789,6 +5803,20 @@ path = Trust; sourceTree = ""; }; + EDA40A0729E9E2BF00C0CAB9 /* LegacyRealmStore */ = { + isa = PBXGroup; + children = ( + EDA40A0829E9E2BF00C0CAB9 /* legacy_version2_account.realm */, + EDA40A0929E9E2BF00C0CAB9 /* legacy_verified_account.realm */, + EDA40A0A29E9E2BF00C0CAB9 /* legacy_unverified_account.realm */, + EDA40A0B29E9E2BF00C0CAB9 /* legacy_deprecated3_account.realm */, + EDA40A0C29E9E2BF00C0CAB9 /* legacy_deprecated1_account.realm */, + EDA40A0D29E9E2BF00C0CAB9 /* archived_encrypted_event */, + EDA40A0E29E9E2BF00C0CAB9 /* LegacyRealmStore.swift */, + ); + path = LegacyRealmStore; + sourceTree = ""; + }; EDB4208E27DF76C60036AF39 /* Data */ = { isa = PBXGroup; children = ( @@ -5932,7 +5960,7 @@ B146D47421A5945800D8C2C6 /* MXAntivirusScanStatus.h in Headers */, 322691361E5EFF8700966A6E /* MXDeviceListOperationsPool.h in Headers */, 3281E8B719E42DFE00976E1A /* MXJSONModel.h in Headers */, - EDAAC41C28E30F3C00DD89B5 /* BuildFile in Headers */, + EDAAC41C28E30F3C00DD89B5 /* (null) in Headers */, B135066127E9CB6400BD3276 /* MXBeaconInfo.h in Headers */, EC5C562827A36EDB0014CBE9 /* MXInReplyTo.h in Headers */, EC8A539325B1BC77004E0802 /* MXCallSessionDescription.h in Headers */, @@ -6556,7 +6584,7 @@ 324AAC7E2399143400380A66 /* MXKeyVerificationCancel.h in Headers */, ED01915528C64E0400ED3A69 /* MXRoomKeyEventContent.h in Headers */, B14EF3372397E90400758AF0 /* MXRoomTombStoneContent.h in Headers */, - EDAAC41D28E30F3C00DD89B5 /* BuildFile in Headers */, + EDAAC41D28E30F3C00DD89B5 /* (null) in Headers */, 3274538B23FD918800438328 /* MXKeyVerificationByToDeviceRequest.h in Headers */, B14EF3382397E90400758AF0 /* MXFilterObject.h in Headers */, B14EF3392397E90400758AF0 /* MXRealmReactionCount.h in Headers */, @@ -6766,7 +6794,6 @@ 3209683326396385005D64ED /* UnitTestsWithSanitizers.xctestplan in Resources */, 3298ABDD2637FB1900E40B06 /* UnitTests.xctestplan in Resources */, 3209683126396385005D64ED /* AllTestsWithSanitizers.xctestplan in Resources */, - EDC2A0E628369E740039F3D6 /* CryptoTests.xctestplan in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -6774,7 +6801,13 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + EDA40A1529E9E2BF00C0CAB9 /* legacy_deprecated3_account.realm in Resources */, + EDA40A1729E9E2BF00C0CAB9 /* legacy_deprecated1_account.realm in Resources */, + EDA40A1329E9E2BF00C0CAB9 /* legacy_unverified_account.realm in Resources */, EDD7B74A29CB3F1B00548AB4 /* MXCrossSigningInfo_v0 in Resources */, + EDA40A1929E9E2BF00C0CAB9 /* archived_encrypted_event in Resources */, + EDA40A0F29E9E2BF00C0CAB9 /* legacy_version2_account.realm in Resources */, + EDA40A1129E9E2BF00C0CAB9 /* legacy_verified_account.realm in Resources */, EDD7B74829CB3F1B00548AB4 /* MXCrossSigningInfo_v1 in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -6788,7 +6821,6 @@ 3209683426396385005D64ED /* UnitTestsWithSanitizers.xctestplan in Resources */, 3298ABDE2637FB1900E40B06 /* UnitTests.xctestplan in Resources */, 3209683226396385005D64ED /* AllTestsWithSanitizers.xctestplan in Resources */, - EDC2A0E728369E740039F3D6 /* CryptoTests.xctestplan in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -6796,7 +6828,13 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + EDA40A1629E9E2BF00C0CAB9 /* legacy_deprecated3_account.realm in Resources */, + EDA40A1829E9E2BF00C0CAB9 /* legacy_deprecated1_account.realm in Resources */, + EDA40A1429E9E2BF00C0CAB9 /* legacy_unverified_account.realm in Resources */, EDD7B74B29CB3F1B00548AB4 /* MXCrossSigningInfo_v0 in Resources */, + EDA40A1A29E9E2BF00C0CAB9 /* archived_encrypted_event in Resources */, + EDA40A1029E9E2BF00C0CAB9 /* legacy_version2_account.realm in Resources */, + EDA40A1229E9E2BF00C0CAB9 /* legacy_verified_account.realm in Resources */, EDD7B74929CB3F1B00548AB4 /* MXCrossSigningInfo_v1 in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -7184,7 +7222,6 @@ EC2EACFF266625170038B61F /* MXRoomLastMessage.m in Sources */, EDF1B6902876CD2C00BBBCEE /* MXTaskQueue.swift in Sources */, EC8A539525B1BC77004E0802 /* MXUserModel.m in Sources */, - EDCAD251299BF7F40088B4DA /* MXCryptoV2Feature.swift in Sources */, ED5EF14F297AB29F00A5ADDA /* MXEventDecryptionResult+DecryptedEvent.swift in Sources */, 3252DCAF224BE5D40032264F /* MXKeyVerificationManager.m in Sources */, 323E0C5C1A306D7A00A31D73 /* MXEvent.m in Sources */, @@ -7315,7 +7352,7 @@ ED6DABFC28C7542800ECDCB6 /* MXRoomKeyInfoFactory.swift in Sources */, B11556EE230C45C600B2A2CF /* MXIdentityServerRestClient.swift in Sources */, ED5EF145297AB1F200A5ADDA /* MXRoomEventEncryption.swift in Sources */, - EDAAC41F28E30F4C00DD89B5 /* BuildFile in Sources */, + EDAAC41F28E30F4C00DD89B5 /* (null) in Sources */, 321CFDE722525A49004D31DF /* MXSASTransaction.m in Sources */, EDDBA7F0293F353900AD1480 /* MXToDevicePayload.swift in Sources */, 32720D9D222EAA6F0086FFF5 /* MXDiscoveredClientConfig.m in Sources */, @@ -7429,7 +7466,7 @@ ED7019F72886CA6C00FC31B9 /* VerificationRequestStub.swift in Sources */, ED55807629709943003443E3 /* MatrixSDKTestsE2EData.swift in Sources */, B14EECEE2578FE3F00448735 /* MXAuthenticationSessionUnitTests.swift in Sources */, - ED558070296F1BEE003443E3 /* MXCryptoMigrationV2Tests.swift in Sources */, + ED558070296F1BEE003443E3 /* MXCryptoMigrationV2UnitTests.swift in Sources */, ED2DD11D286C4F4400F06731 /* MXCryptoRequestsUnitTests.swift in Sources */, 32832B5D1BCC048300241108 /* MXStoreMemoryStoreTests.m in Sources */, EDB4209927DF842F0036AF39 /* MXEventFixtures.swift in Sources */, @@ -7499,7 +7536,6 @@ 32D5D16323E400A600E3E37C /* MXRoomSummaryTrustTests.m in Sources */, ED28068428F06C6C0070AE9F /* QrCodeStub.swift in Sources */, C61A4AF41E5DD88400442158 /* Dummy.swift in Sources */, - 32935F61216FA49D00A1BC24 /* MXCurve25519KeyBackupTests.m in Sources */, B1F04B162811EFF700103EBE /* MXBeaconAggregationsTests.swift in Sources */, ED8F1D2C2885A80B00F897E7 /* MXDeviceInfoSourceUnitTests.swift in Sources */, 328DDEC11A07E57E008C7DC8 /* MXJSONModelTests.m in Sources */, @@ -7522,6 +7558,7 @@ 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 */, @@ -7530,6 +7567,7 @@ 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 */, @@ -7539,7 +7577,7 @@ 18C26C4F273C0EB300805154 /* MXPollAggregatorTests.swift in Sources */, ED35652F281153480002BF6A /* MXMegolmSessionDataUnitTests.swift in Sources */, 32EEA83F2603CA140041425B /* MXRestClientExtensionsTests.m in Sources */, - EDDB07CA297EE0A7005249A6 /* MXCryptoV2FactoryTests.swift in Sources */, + EDDB07CA297EE0A7005249A6 /* MXCryptoV2FactoryUnitTests.swift in Sources */, 18121F7A273E6E4200B68ADF /* PollBuilder.swift in Sources */, 18121F7F273E837300B68ADF /* PollModels.swift in Sources */, 32C03CB62123076F00D92712 /* DirectRoomTests.m in Sources */, @@ -7626,7 +7664,7 @@ EC1165B527107E330089FA56 /* MXStoreRoomListDataManager.swift in Sources */, 66836ABA27CFA17200515780 /* MXLiveEventListener.swift in Sources */, B14EF1E52397E90400758AF0 /* MXLoginPolicy.m in Sources */, - B14EF1E62397E90400758AF0 /* BuildFile in Sources */, + B14EF1E62397E90400758AF0 /* (null) in Sources */, B18D23F727ECF199004C4277 /* MXLocationService.swift in Sources */, EC60EDB5265CFE6200B39A4E /* MXRoomSyncEphemeral.m in Sources */, B14EF1E72397E90400758AF0 /* MXRoomThirdPartyInvite.m in Sources */, @@ -7768,7 +7806,7 @@ B14EF21F2397E90400758AF0 /* MXMyUser.m in Sources */, EDAAC42528E3177300DD89B5 /* MXRecoveryServiceDependencies.swift in Sources */, EC60EDAB265CFE3B00B39A4E /* MXRoomSyncTimeline.m in Sources */, - B14EF2202397E90400758AF0 /* BuildFile in Sources */, + B14EF2202397E90400758AF0 /* (null) in Sources */, ED647E3F292CE64400A47519 /* MXSessionStartupProgress.swift in Sources */, B14EF2212397E90400758AF0 /* MX3PID.swift in Sources */, 18121F79273E6E4100B68ADF /* PollBuilder.swift in Sources */, @@ -7807,7 +7845,7 @@ B18B0E6825FBDC3000E32151 /* MXSpace.swift in Sources */, B14EF22D2397E90400758AF0 /* MXRealmReactionCount.m in Sources */, B14EF22E2397E90400758AF0 /* MXCryptoTools.m in Sources */, - B14EF22F2397E90400758AF0 /* BuildFile in Sources */, + B14EF22F2397E90400758AF0 /* (null) in Sources */, B14EF2302397E90400758AF0 /* MXDeviceListOperation.m in Sources */, 32C78B6B256CFC4D008130B1 /* MXCryptoMigration.m in Sources */, ECDA763027B292B5000C48CF /* MXThreadModel.swift in Sources */, @@ -7856,7 +7894,6 @@ EC8A53D925B1BCC6004E0802 /* MXThirdPartyProtocolInstance.m in Sources */, EDF1B6912876CD2C00BBBCEE /* MXTaskQueue.swift in Sources */, B14EF2422397E90400758AF0 /* MXDeviceInfo.m in Sources */, - EDCAD252299BF7F40088B4DA /* MXCryptoV2Feature.swift in Sources */, ED5EF150297AB29F00A5ADDA /* MXEventDecryptionResult+DecryptedEvent.swift in Sources */, B14EF2432397E90400758AF0 /* MXIncomingSASTransaction.m in Sources */, B14EF2442397E90400758AF0 /* NSObject+sortedKeys.m in Sources */, @@ -7925,7 +7962,7 @@ B14EF25C2397E90400758AF0 /* MXRoomTombStoneContent.m in Sources */, B1432B52282AB29A00737CA6 /* MXBeaconInfoSummaryAllRoomListener.swift in Sources */, B14EF25D2397E90400758AF0 /* MXImage.swift in Sources */, - B14EF25E2397E90400758AF0 /* BuildFile in Sources */, + B14EF25E2397E90400758AF0 /* (null) in Sources */, 32B090E3261F709B002924AA /* MXAsyncTaskQueue.swift in Sources */, B14EF25F2397E90400758AF0 /* MXServerNoticeContent.m in Sources */, B1F04B112811E7B600103EBE /* MXBeaconInfoSummaryMemoryStore.swift in Sources */, @@ -7987,7 +8024,7 @@ ED6DABFD28C7542800ECDCB6 /* MXRoomKeyInfoFactory.swift in Sources */, B14EF2782397E90400758AF0 /* MXTransactionCancelCode.m in Sources */, ED5EF146297AB1F200A5ADDA /* MXRoomEventEncryption.swift in Sources */, - EDAAC42028E30F4C00DD89B5 /* BuildFile in Sources */, + EDAAC42028E30F4C00DD89B5 /* (null) in Sources */, B14EF2792397E90400758AF0 /* MXEventListener.m in Sources */, EDDBA7F1293F353900AD1480 /* MXToDevicePayload.swift in Sources */, B1710B202613D01400A9B429 /* MXSpaceChildrenRequestParameters.swift in Sources */, @@ -8024,7 +8061,7 @@ EC60ED7E265CFCD100B39A4E /* MXDeviceListResponse.m in Sources */, 323F879025553D84009E9E67 /* MXTaskProfile.m in Sources */, B14EF2842397E90400758AF0 /* MXUIKitBackgroundModeHandler.m in Sources */, - B14EF2852397E90400758AF0 /* BuildFile in Sources */, + B14EF2852397E90400758AF0 /* (null) in Sources */, 32A9F8E1244720B10069C65B /* MXThrottler.m in Sources */, 3274538D23FD918800438328 /* MXKeyVerificationByToDeviceRequest.m in Sources */, 32CEEF5223B0AB030039BA98 /* MXCrossSigning.m in Sources */, @@ -8081,7 +8118,6 @@ B1E09A392397FD7D0057C069 /* MXMyUserTests.m in Sources */, 322985D026FBAE7B001890BC /* TestObserver.swift in Sources */, 3A96CD4A2901512C00F9A5AB /* MXReceiptDataIntegrationTests.swift in Sources */, - B1E09A202397FCE90057C069 /* MXCurve25519KeyBackupTests.m in Sources */, ED7019DD2886C24100FC31B9 /* MXCrossSigningInfoSourceUnitTests.swift in Sources */, ED51943A28462D130006EEC6 /* MXRoomStateUnitTests.swift in Sources */, B1E09A3B2397FD820057C069 /* MXStoreNoStoreTests.m in Sources */, @@ -8101,7 +8137,7 @@ ED7019F82886CA6C00FC31B9 /* VerificationRequestStub.swift in Sources */, ED55807729709943003443E3 /* MatrixSDKTestsE2EData.swift in Sources */, 18121F76273E6D2400B68ADF /* MXPollBuilderTests.swift in Sources */, - ED558071296F1BEE003443E3 /* MXCryptoMigrationV2Tests.swift in Sources */, + ED558071296F1BEE003443E3 /* MXCryptoMigrationV2UnitTests.swift in Sources */, B1E09A1A2397FCE90057C069 /* MXAggregatedEditsTests.m in Sources */, B1E09A1F2397FCE90057C069 /* MXAutoDiscoveryTests.m in Sources */, EDB4209A27DF842F0036AF39 /* MXEventFixtures.swift in Sources */, @@ -8194,6 +8230,7 @@ 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 */, @@ -8202,6 +8239,7 @@ EC116598270FCA8B0089FA56 /* MXBackgroundTaskUnitTests.swift in Sources */, 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 */, @@ -8211,7 +8249,7 @@ ED7019E12886C26D00FC31B9 /* MXCryptoRequestsUnitTests.swift in Sources */, 18C26C50273C0EB400805154 /* MXPollAggregatorTests.swift in Sources */, ED356530281153480002BF6A /* MXMegolmSessionDataUnitTests.swift in Sources */, - EDDB07CB297EE0A7005249A6 /* MXCryptoV2FactoryTests.swift in Sources */, + EDDB07CB297EE0A7005249A6 /* MXCryptoV2FactoryUnitTests.swift in Sources */, 32C78BA8256D227D008130B1 /* MXCryptoMigrationTests.m in Sources */, 18121F7B273E6E4200B68ADF /* PollBuilder.swift in Sources */, 18121F80273E837400B68ADF /* PollModels.swift in Sources */, diff --git a/MatrixSDK.xcodeproj/xcshareddata/xcschemes/MatrixSDK-macOS.xcscheme b/MatrixSDK.xcodeproj/xcshareddata/xcschemes/MatrixSDK-macOS.xcscheme index 6b26075d7f..4cafc18e35 100644 --- a/MatrixSDK.xcodeproj/xcshareddata/xcschemes/MatrixSDK-macOS.xcscheme +++ b/MatrixSDK.xcodeproj/xcshareddata/xcschemes/MatrixSDK-macOS.xcscheme @@ -64,9 +64,6 @@ - - diff --git a/MatrixSDK/Crypto/CrossSigning/MXCrossSigningV2.swift b/MatrixSDK/Crypto/CrossSigning/MXCrossSigningV2.swift index 2c165967ce..92eadb8cfb 100644 --- a/MatrixSDK/Crypto/CrossSigning/MXCrossSigningV2.swift +++ b/MatrixSDK/Crypto/CrossSigning/MXCrossSigningV2.swift @@ -126,7 +126,7 @@ class MXCrossSigningV2: NSObject, MXCrossSigning { // If we are considered verified, there is no need for a verification upgrade // after migrating from legacy crypto if myUserCrossSigningKeys?.trustLevel.isVerified == true { - MXSDKOptions.sharedInstance().cryptoSDKFeature?.needsVerificationUpgrade = false + MXSDKOptions.sharedInstance().needsVerificationUpgrade = false } log.debug("Cross signing state refreshed, new state: \(state)") diff --git a/MatrixSDK/Crypto/MXCrypto.m b/MatrixSDK/Crypto/MXCrypto.m index 674347c44e..9cf0f2511a 100644 --- a/MatrixSDK/Crypto/MXCrypto.m +++ b/MatrixSDK/Crypto/MXCrypto.m @@ -180,19 +180,26 @@ + (void)initializeCryptoWithMatrixSession:(MXSession *)mxSession complete:(void (^)(id crypto, NSError *error))complete { #ifdef MX_CRYPTO - - // Each time we construct the crypto module (app launch, login etc) we have a chance to try to enable - // the newer SDK crypto module, if it is available for this particular user. - [MXSDKOptions.sharedInstance.cryptoSDKFeature enableIfAvailableForUserId:mxSession.myUserId]; + if (MXSDKOptions.sharedInstance.enableCryptoSDK) { - [MXCryptoV2Factory.shared buildCryptoWithSession:mxSession - migrationProgress:migrationProgress - success:^(id crypto) { - complete(crypto, nil); } - failure:^(NSError *error) { - complete(nil, error); - }]; + BOOL enableCrypto = [MXSDKOptions sharedInstance].enableCryptoWhenStartingMXSession || [MXCryptoV2Factory.shared hasCryptoDataFor:mxSession]; + if (enableCrypto) + { + [MXCryptoV2Factory.shared buildCryptoWithSession:mxSession + migrationProgress:migrationProgress + success:^(id crypto) { + complete(crypto, nil); } + failure:^(NSError *error) { + complete(nil, error); + }]; + } + else + { + dispatch_async(dispatch_get_main_queue(), ^{ + complete(nil, nil); + }); + } return; } diff --git a/MatrixSDK/Crypto/MXCryptoV2Factory.swift b/MatrixSDK/Crypto/MXCryptoV2Factory.swift index 4d32bd4495..1843f49fc0 100644 --- a/MatrixSDK/Crypto/MXCryptoV2Factory.swift +++ b/MatrixSDK/Crypto/MXCryptoV2Factory.swift @@ -28,6 +28,21 @@ import Foundation .deprecated3 } + @objc public func hasCryptoData(for session: MXSession!) -> Bool { + guard let userId = session?.myUserId else { + log.error("Missing required dependencies") + return false + } + + do { + let url = try MXCryptoMachineStore.storeURL(for: userId) + return FileManager.default.fileExists(atPath: url.path) + } catch { + log.error("Failed creating url for user", context: error) + return false + } + } + @objc public func buildCrypto( session: MXSession!, migrationProgress: ((Double) -> Void)?, @@ -124,7 +139,7 @@ import Foundation // unless the rust-based crypto already considers the current session to be verified given // the migration data log.debug("Needs verification upgrade") - MXSDKOptions.sharedInstance().cryptoSDKFeature?.needsVerificationUpgrade = true + MXSDKOptions.sharedInstance().needsVerificationUpgrade = true } } } diff --git a/MatrixSDK/Crypto/MXCryptoV2Feature.swift b/MatrixSDK/Crypto/MXCryptoV2Feature.swift deleted file mode 100644 index 5fc1d4d6de..0000000000 --- a/MatrixSDK/Crypto/MXCryptoV2Feature.swift +++ /dev/null @@ -1,50 +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 - -/// Feature representing the availability of the external rust-based Crypto SDK -/// whilst it is not fully available to everyone and / or is an optional feature. -@objc public protocol MXCryptoV2Feature { - /// Is Crypto SDK currently enabled - /// - /// By default this value is `false`. Once enabled, it can only be disabled by logging out, - /// as there is no way to migrate from from Crypto SDK back to legacy crypto. - var isEnabled: Bool { get } - - /// Flag indicating whether this account requires a re-verification after migrating to Crypto SDK - /// - /// This flag is set to true if the legacy account is considered verified but the rust account - /// does not consider the migrated data secure enough, as it applies stricter security conditions. - var needsVerificationUpgrade: Bool { get set } - - /// Manually enable the feature - /// - /// This is typically triggered by some user settings / Labs as an experimental feature. Once called - /// it should restart the session to re-initialize the crypto module. - func enable() - - /// Try to enable the feature for a given user - /// - /// This method should only be called when initializing a crypto module (e.g. during app launch or login), - /// as it is not possible to swap out crypto modules whilst a session is active. - /// - /// The availability conditions are implementation details, typically consisting of - /// various feature flags. - /// - /// If available, this method will set `isEnabled` permanently to `true`. - func enableIfAvailable(forUserId userId: String!) -} diff --git a/MatrixSDK/MXSDKOptions.h b/MatrixSDK/MXSDKOptions.h index 929b183655..422a8c6617 100644 --- a/MatrixSDK/MXSDKOptions.h +++ b/MatrixSDK/MXSDKOptions.h @@ -204,18 +204,21 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic) BOOL enableRoomSharedHistoryOnInvite; /** - An object which controls the availabilty of the rust-based `MatrixCryptoSDK`. + Use the newer rust-based `MatrixCryptoSDK` instead of the legacy `MatrixSDK`'s internal crypto module. - @remark nil by default. + @remark YES by default */ -@property (nonatomic, nullable) id cryptoSDKFeature; +@property (nonatomic) BOOL enableCryptoSDK; /** - Use the rust-based `MatrixCryptoSDK` instead of `MatrixSDK`'s internal crypto module. + Flag indicating whether this account requires a re-verification after migrating to Crypto SDK + + This flag is set to true if the legacy account is considered verified but the rust account + does not consider the migrated data secure enough, as it applies stricter security conditions. - @remark this property is a convenience getter for `cryptoSDKFeature.isEnabled` + @remark NO by default. */ -@property (nonatomic, readonly) BOOL enableCryptoSDK; +@property (nonatomic) BOOL needsVerificationUpgrade; /** The text-based identifier for the crypto module being used (e.g. native vs rust) diff --git a/MatrixSDK/MXSDKOptions.m b/MatrixSDK/MXSDKOptions.m index f9acfda590..e1cc0fa19f 100644 --- a/MatrixSDK/MXSDKOptions.m +++ b/MatrixSDK/MXSDKOptions.m @@ -54,6 +54,8 @@ - (instancetype)init _authEnableRefreshTokens = NO; _enableThreads = NO; _enableRoomSharedHistoryOnInvite = NO; + _enableCryptoSDK = YES; + _needsVerificationUpgrade = NO; _enableSymmetricBackup = NO; _enableNewClientInformationFeature = NO; _enableStartupProgress = YES; @@ -62,16 +64,6 @@ - (instancetype)init return self; } -- (BOOL)enableCryptoSDK -{ - if (!self.cryptoSDKFeature) - { - MXLogError(@"[MXSDKOptions] enableCryptoSDK: Crypto SDK feature is not configured"); - return NO; - } - return self.cryptoSDKFeature.isEnabled; -} - - (NSString *)cryptoModuleId { return self.enableCryptoSDK ? @"rust" : @"native"; diff --git a/MatrixSDKTests/Crypto/CrossSigning/MXCrossSigningV2UnitTests.swift b/MatrixSDKTests/Crypto/CrossSigning/MXCrossSigningV2UnitTests.swift index 617eb44869..104b186305 100644 --- a/MatrixSDKTests/Crypto/CrossSigning/MXCrossSigningV2UnitTests.swift +++ b/MatrixSDKTests/Crypto/CrossSigning/MXCrossSigningV2UnitTests.swift @@ -160,3 +160,15 @@ private extension MXCrossSigningV2 { } } } + +private extension MXCrossSigning { + func refreshState() async throws { + return try await withCheckedThrowingContinuation { continuation in + refreshState { _ in + continuation.resume() + } failure: { error in + continuation.resume(throwing: error) + } + } + } +} diff --git a/MatrixSDKTests/Crypto/CryptoMachine/MXKeyProviderStub.swift b/MatrixSDKTests/Crypto/CryptoMachine/MXKeyProviderStub.swift new file mode 100644 index 0000000000..73b7781fdd --- /dev/null +++ b/MatrixSDKTests/Crypto/CryptoMachine/MXKeyProviderStub.swift @@ -0,0 +1,31 @@ +// +// 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 + +class MXKeyProviderStub: NSObject, MXKeyProviderDelegate { + func isEncryptionAvailableForData(ofType dataType: String) -> Bool { + return true + } + + func hasKeyForData(ofType dataType: String) -> Bool { + return true + } + + func keyDataForData(ofType dataType: String) -> MXKeyData? { + MXRawDataKey(key: "1234".data(using: .ascii)!) + } +} diff --git a/MatrixSDKTests/Crypto/MXCryptoV2FactoryTests.swift b/MatrixSDKTests/Crypto/MXCryptoV2FactoryTests.swift deleted file mode 100644 index 2e1867afc7..0000000000 --- a/MatrixSDKTests/Crypto/MXCryptoV2FactoryTests.swift +++ /dev/null @@ -1,144 +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 - -class MXCryptoV2FactoryTests: XCTestCase { - class KeyProvider: NSObject, MXKeyProviderDelegate { - func isEncryptionAvailableForData(ofType dataType: String) -> Bool { - return true - } - - func hasKeyForData(ofType dataType: String) -> Bool { - return true - } - - func keyDataForData(ofType dataType: String) -> MXKeyData? { - MXRawDataKey(key: "1234".data(using: .ascii)!) - } - } - - var data: MatrixSDKTestsData! - var e2eData: MatrixSDKTestsE2EData! - var factory: MXCryptoV2Factory! - - override func setUp() { - data = .init() - e2eData = .init(matrixSDKTestsData: data) - factory = MXCryptoV2Factory() - MXKeyProvider.sharedInstance().delegate = KeyProvider() - } - - override func tearDown() { - MXKeyProvider.sharedInstance().delegate = nil - } - - private func buildCrypto(session: MXSession) async throws -> (MXCrypto?, Bool) { - 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_doesNotMigrateNewUser() async throws { - let env = try await e2eData.startE2ETest() - let session = env.session - - // Simulating new user as one without a crypto database - MXRealmCryptoStore.delete(with: session.credentials) - - // Build crypto and assert no migration has been performed - let (crypto, hasMigrated) = try await buildCrypto(session: session) - XCTAssertNotNil(crypto) - XCTAssertFalse(hasMigrated) - - // Assert that we have not created any legacy store for this user - let legacyStore = MXRealmCryptoStore.init(credentials: session.credentials) - XCTAssertNil(legacyStore) - - await env.close() - } - - func test_fullyMigratesLegacyUser() async throws { - let env = try await e2eData.startE2ETest() - let session = env.session - var legacyStore = session.legacyCrypto?.store - - // Assert that we have a legacy store that has not yet been deprecated - XCTAssertNotNil(legacyStore) - XCTAssertEqual(legacyStore?.cryptoVersion, .version2) - - // Build crypto and assert migration has been performed - let (crypto, hasMigrated) = try await buildCrypto(session: session) - XCTAssertNotNil(crypto) - XCTAssertTrue(hasMigrated) - - // Assert that we no longer have a legacy store for this user - legacyStore = MXRealmCryptoStore(credentials: session.credentials) - XCTAssertNil(legacyStore) - - await env.close() - } - - func test_migratesPartiallyMigratedUser() async throws { - let env = try await e2eData.startE2ETest() - let session = env.session - - // We set the legacy store as partially deprecated - var legacyStore = session.legacyCrypto?.store - XCTAssertNotNil(legacyStore) - legacyStore?.cryptoVersion = .deprecated1 - - // Build crypto and assert migration has been performed - let (crypto, hasMigrated) = try await buildCrypto(session: session) - XCTAssertNotNil(crypto) - XCTAssertTrue(hasMigrated) - - // Assert that we no longer have a legacy store for this user - legacyStore = MXRealmCryptoStore(credentials: session.credentials) - XCTAssertNil(legacyStore) - - await env.close() - } - - func test_doesNotMigrateDeprecatedStore() async throws { - let env = try await e2eData.startE2ETest() - let session = env.session - - // We set the legacy store as fully deprecated - var legacyStore = session.legacyCrypto?.store - XCTAssertNotNil(legacyStore) - legacyStore?.cryptoVersion = .deprecated3 - - // Build crypto and assert no migration has been performed - let (crypto, hasMigrated) = try await buildCrypto(session: session) - XCTAssertNotNil(crypto) - XCTAssertFalse(hasMigrated) - - // Assert that we no longer have a legacy store for this user - legacyStore = MXRealmCryptoStore(credentials: session.credentials) - XCTAssertNil(legacyStore) - - await env.close() - } -} diff --git a/MatrixSDKTests/Crypto/MXCryptoV2FactoryUnitTests.swift b/MatrixSDKTests/Crypto/MXCryptoV2FactoryUnitTests.swift new file mode 100644 index 0000000000..fae681fca3 --- /dev/null +++ b/MatrixSDKTests/Crypto/MXCryptoV2FactoryUnitTests.swift @@ -0,0 +1,151 @@ +// +// 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/MXSession+LegacyCrypto.swift b/MatrixSDKTests/Crypto/MXSession+LegacyCrypto.swift index 815fdd9887..c98d489229 100644 --- a/MatrixSDKTests/Crypto/MXSession+LegacyCrypto.swift +++ b/MatrixSDKTests/Crypto/MXSession+LegacyCrypto.swift @@ -28,7 +28,6 @@ import Foundation } guard let legacy = crypto as? MXLegacyCrypto else { - assertionFailure("Legacy crypto is not available, adjust test to not depend on legacy APIs") return nil } return legacy diff --git a/MatrixSDKTests/Crypto/Migration/LegacyRealmStore/LegacyRealmStore.swift b/MatrixSDKTests/Crypto/Migration/LegacyRealmStore/LegacyRealmStore.swift new file mode 100644 index 0000000000..4510c3a782 --- /dev/null +++ b/MatrixSDKTests/Crypto/Migration/LegacyRealmStore/LegacyRealmStore.swift @@ -0,0 +1,162 @@ +// +// 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/LegacyRealmStore/archived_encrypted_event b/MatrixSDKTests/Crypto/Migration/LegacyRealmStore/archived_encrypted_event new file mode 100644 index 0000000000000000000000000000000000000000..ba709102e864bb719443f2848b92501b8fad460a GIT binary patch literal 1275 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 literal 0 HcmV?d00001 diff --git a/MatrixSDKTests/Crypto/Migration/LegacyRealmStore/legacy_verified_account.realm b/MatrixSDKTests/Crypto/Migration/LegacyRealmStore/legacy_verified_account.realm new file mode 100644 index 0000000000000000000000000000000000000000..f45e7546e44809dc0e14b63498120e72f3ee2605 GIT binary patch literal 65536 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

3Dlo{5D|z|^|yf#t!~cLRNsrSyFKy> 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 LV1d9teFXj=Kpoz< literal 0 HcmV?d00001 diff --git a/MatrixSDKTests/Crypto/Migration/LegacyRealmStore/legacy_version2_account.realm b/MatrixSDKTests/Crypto/Migration/LegacyRealmStore/legacy_version2_account.realm new file mode 100644 index 0000000000000000000000000000000000000000..b02c1e73f71b3b020094c6346c0a021148600f15 GIT binary patch literal 32768 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

ssrH?FuUhVA);pUaiG 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 Bool { - return true - } - - func hasKeyForData(ofType dataType: String) -> Bool { - return true - } - - func keyDataForData(ofType dataType: String) -> MXKeyData? { - MXRawDataKey(key: "1234".data(using: .ascii)!) - } - } - - var data: MatrixSDKTestsData! - var e2eData: MatrixSDKTestsE2EData! - - override func setUp() { - data = MatrixSDKTestsData() - e2eData = MatrixSDKTestsE2EData(matrixSDKTestsData: data) - } - - // MARK: - Helpers - - private func fullyMigratedOlmMachine(session: MXSession) throws -> MXCryptoMachine { - guard - let store = session.legacyCrypto?.store, - let restClient = session.matrixRestClient - else { - throw Error.missingDependencies - } - - MXKeyProvider.sharedInstance().delegate = KeyProvider() - let migration = MXCryptoMigrationV2(legacyStore: store) - try migration.migrateAllData { _ in } - let machine = try MXCryptoMachine( - userId: store.userId(), - deviceId: store.deviceId(), - restClient: restClient, - getRoomAction: { _ in - return nil - }) - MXKeyProvider.sharedInstance().delegate = nil - return machine - } - - private func partiallyMigratedOlmMachine(session: MXSession) throws -> MXCryptoMachine { - guard - let store = session.legacyCrypto?.store, - let restClient = session.matrixRestClient - else { - throw Error.missingDependencies - } - - MXKeyProvider.sharedInstance().delegate = KeyProvider() - let migration = MXCryptoMigrationV2(legacyStore: store) - try migration.migrateRoomAndGlobalSettingsOnly { _ in } - let machine = try MXCryptoMachine( - userId: store.userId(), - deviceId: store.deviceId(), - restClient: restClient, - getRoomAction: { _ in - return nil - }) - MXKeyProvider.sharedInstance().delegate = nil - return machine - } - - // MARK: - Tests - - func test_migratesAccountDetails() async throws { - let env = try await e2eData.startE2ETest() - let legacySession = env.session - - let machine = try self.fullyMigratedOlmMachine(session: env.session) - - XCTAssertEqual(machine.userId, legacySession.myUserId) - XCTAssertEqual(machine.deviceId, legacySession.myDeviceId) - XCTAssertEqual(machine.deviceCurve25519Key, legacySession.crypto.deviceCurve25519Key) - XCTAssertEqual(machine.deviceEd25519Key, legacySession.crypto.deviceEd25519Key) - - await env.close() - } - - func test_canDecryptMegolmMessageAfterMigration() async throws { - let env = try await e2eData.startE2ETest() - - guard let room = env.session.room(withRoomId: env.roomId) else { - throw Error.missingDependencies - } - - // Send a new message in encrypted room - let event = try await room.sendTextMessage("Hi bob") - - // Erase cleartext and make sure the event was indeed encrypted - event.setClearData(nil) - XCTAssertTrue(event.isEncrypted) - XCTAssertEqual(event.content["algorithm"] as? String, kMXCryptoMegolmAlgorithm) - XCTAssertNotNil(event.content["ciphertext"]) - - // Migrate the session to crypto v2 - let machine = try self.fullyMigratedOlmMachine(session: env.session) - - // 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") - - await env.close() - } - - func test_notCrossSignedAfterMigration() async throws { - let env = try await e2eData.startE2ETest() - - // We start with user who cannot cross-sign (did not setup cross signing keys) - let legacyCrossSigning = env.session.crypto.crossSigning - XCTAssertFalse(legacyCrossSigning.canCrossSign) - XCTAssertFalse(legacyCrossSigning.hasAllPrivateKeys) - - // We then migrate the user into crypto v2 - let machine = try fullyMigratedOlmMachine(session: env.session) - let crossSigningV2 = MXCrossSigningV2(crossSigning: machine, restClient: env.session.matrixRestClient) - try await crossSigningV2.refreshState() - - // As expected we cannot cross sign in v2 either - XCTAssertFalse(crossSigningV2.canCrossSign) - XCTAssertFalse(crossSigningV2.hasAllPrivateKeys) - - await env.close() - } - - func test_migratesCrossSigningStatus() async throws { - let env = try await e2eData.startE2ETest() - - // We start with user who setup cross-signing with password - let legacyCrossSigning = env.session.crypto.crossSigning - try await legacyCrossSigning.setup(withPassword: MXTESTS_ALICE_PWD) - XCTAssertTrue(legacyCrossSigning.canCrossSign) - XCTAssertTrue(legacyCrossSigning.hasAllPrivateKeys) - - // We now migrate the data into crypto v2 - let machine = try fullyMigratedOlmMachine(session: env.session) - let crossSigningV2 = MXCrossSigningV2(crossSigning: machine, restClient: env.session.matrixRestClient) - try await crossSigningV2.refreshState() - - // And confirm that cross signing is ready - XCTAssertTrue(crossSigningV2.canCrossSign) - XCTAssertTrue(crossSigningV2.hasAllPrivateKeys) - - await env.close() - } - - func test_migratesRoomSettings() async throws { - let env = try await e2eData.startE2ETest() - - // We start with user and encrypted room with some settings - let legacyCrypto = env.session.crypto! - try await legacyCrypto.ensureEncryption(roomId: env.roomId) - legacyCrypto.setBlacklistUnverifiedDevicesInRoom(env.roomId, blacklist: true) - XCTAssertTrue(legacyCrypto.isRoomEncrypted(env.roomId)) - XCTAssertTrue(legacyCrypto.isBlacklistUnverifiedDevices(inRoom: env.roomId)) - - // We now migrate the data into crypto v2 - let machine = try fullyMigratedOlmMachine(session: env.session) - - // And confirm that room settings have been migrated - let settings = machine.roomSettings(roomId: env.roomId) - XCTAssertEqual(settings, .init(algorithm: .megolmV1AesSha2, onlyAllowTrustedDevices: true)) - - await env.close() - } - - func test_migratesRoomSettingsInPartialMigration() async throws { - let env = try await e2eData.startE2ETest() - - // We start with user and encrypted room with some settings - let legacyCrypto = env.session.crypto! - try await legacyCrypto.ensureEncryption(roomId: env.roomId) - legacyCrypto.setBlacklistUnverifiedDevicesInRoom(env.roomId, blacklist: true) - XCTAssertTrue(legacyCrypto.isRoomEncrypted(env.roomId)) - XCTAssertTrue(legacyCrypto.isBlacklistUnverifiedDevices(inRoom: env.roomId)) - - // We now migrate the data into crypto v2 - let machine = try partiallyMigratedOlmMachine(session: env.session) - - // And confirm that room settings have been migrated - let settings = machine.roomSettings(roomId: env.roomId) - XCTAssertEqual(settings, .init(algorithm: .megolmV1AesSha2, onlyAllowTrustedDevices: true)) - - await env.close() - } - - func test_migratesGlobalSettings() async throws { - let env1 = try await e2eData.startE2ETest() - env1.session.crypto.globalBlacklistUnverifiedDevices = true - let machine1 = try fullyMigratedOlmMachine(session: env1.session) - XCTAssertTrue(machine1.onlyAllowTrustedDevices) - await env1.close() - - let env2 = try await e2eData.startE2ETest() - env2.session.crypto.globalBlacklistUnverifiedDevices = false - let machine2 = try fullyMigratedOlmMachine(session: env2.session) - XCTAssertFalse(machine2.onlyAllowTrustedDevices) - await env2.close() - } - - func test_migratesGlobalSettingsInPartialMigration() async throws { - let env1 = try await e2eData.startE2ETest() - env1.session.crypto.globalBlacklistUnverifiedDevices = true - let machine1 = try partiallyMigratedOlmMachine(session: env1.session) - XCTAssertTrue(machine1.onlyAllowTrustedDevices) - await env1.close() - - let env2 = try await e2eData.startE2ETest() - env2.session.crypto.globalBlacklistUnverifiedDevices = false - let machine2 = try partiallyMigratedOlmMachine(session: env2.session) - XCTAssertFalse(machine2.onlyAllowTrustedDevices) - await env2.close() - } -} - -private extension MXCrypto { - func downloadKeys(userIds: [String]) async throws { - return try await withCheckedThrowingContinuation { continuation in - downloadKeys(userIds, forceDownload: false) { _, _ in - continuation.resume() - } - } - } - - func ensureEncryption(roomId: String) async throws { - return try await withCheckedThrowingContinuation { continuation in - ensureEncryption(inRoom: roomId) { - continuation.resume() - } - } - } -} - -extension MXCrossSigning { - func refreshState() async throws { - return try await withCheckedThrowingContinuation { continuation in - refreshState { _ in - continuation.resume() - } failure: { error in - continuation.resume(throwing: error) - } - } - } - - func signUser(userId: String) async throws { - return try await withCheckedThrowingContinuation { continuation in - signUser(withUserId: userId) { - continuation.resume() - } failure: { error in - continuation.resume(throwing: error) - } - } - } - - func setup(withPassword password: String) async throws { - return try await withCheckedThrowingContinuation { continuation in - setup(withPassword: password) { - continuation.resume() - } failure: { error in - continuation.resume(throwing: error) - } - } - } -} - -private extension MXRoom { - enum Error: Swift.Error { - case cannotSendMessage - } - - @MainActor - func sendTextMessage(_ text: String) async throws -> MXEvent { - var event: MXEvent? - _ = try await withCheckedThrowingContinuation{ (continuation: CheckedContinuation) in - sendTextMessage(text, localEcho: &event) { response in - switch response { - case .success(let value): - continuation.resume(returning: value) - case .failure(let error): - continuation.resume(throwing: error) - } - } - } - - guard let event = event else { - throw Error.cannotSendMessage - } - return event - } -} - -extension MXCryptoMigrationV2Tests: Logger { - func log(logLine: String) { - MXLog.debug("[MXCryptoMigrationV2Tests]: \(logLine)") - } -} diff --git a/MatrixSDKTests/Crypto/Migration/MXCryptoMigrationV2UnitTests.swift b/MatrixSDKTests/Crypto/Migration/MXCryptoMigrationV2UnitTests.swift new file mode 100644 index 0000000000..88b5cfc676 --- /dev/null +++ b/MatrixSDKTests/Crypto/Migration/MXCryptoMigrationV2UnitTests.swift @@ -0,0 +1,167 @@ +// +// 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/MXBaseKeyBackupTests.m b/MatrixSDKTests/MXBaseKeyBackupTests.m index 5cdca88f5b..94ec9b427a 100644 --- a/MatrixSDKTests/MXBaseKeyBackupTests.m +++ b/MatrixSDKTests/MXBaseKeyBackupTests.m @@ -757,8 +757,7 @@ - (void)testRestoreKeyBackup - Try to restore the e2e backup with a wrong recovery key - It must fail */ -// TODO: test is currently broken -- (void)xtestRestoreKeyBackupWithAWrongRecoveryKey +- (void)testRestoreKeyBackupWithAWrongRecoveryKey { // - Do an e2e backup to the homeserver with a recovery key // - Log Alice on a new device @@ -824,8 +823,7 @@ - (void)testRestoreKeyBackupWithPassword - Try to restore the e2e backup with a wrong password - It must fail */ -// TODO: test is currently broken -- (void)xtestRestoreKeyBackupWithAWrongPassword +- (void)testRestoreKeyBackupWithAWrongPassword { // - Do an e2e backup to the homeserver with a password // - Log Alice on a new device @@ -891,8 +889,7 @@ - (void)testUseRecoveryKeyToRestoreAPasswordKeyKeyBackup - Try to restore the e2e backup with a password - It must fail */ -// TODO: test is currently broken -- (void)xtestUsePasswordToRestoreARecoveryKeyKeyBackup +- (void)testUsePasswordToRestoreARecoveryKeyKeyBackup { // - Do an e2e backup to the homeserver with a recovery key // - And log Alice on a new device @@ -927,8 +924,7 @@ - (void)xtestUsePasswordToRestoreARecoveryKeyKeyBackup - Restart alice session -> The new alice session must back up to the same version */ -// TODO: test is currently broken -- (void)xtestCheckAndStartKeyBackupWhenRestartingAMatrixSession +- (void)testCheckAndStartKeyBackupWhenRestartingAMatrixSession { // - Create a backup version [matrixSDKTestsE2EData doE2ETestWithAliceAndBobInARoomWithCryptedMessages:self cryptedBob:YES readyToTest:^(MXSession *aliceSession, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation) { @@ -1296,8 +1292,7 @@ - (void)testTrustKeyBackupVersionWithRecoveryKey - It must fail - The backup must still be untrusted and disabled */ -// TODO: test is currently broken -- (void)xtestTrustKeyBackupVersionWithWrongRecoveryKey +- (void)testTrustKeyBackupVersionWithWrongRecoveryKey { // - Do an e2e backup to the homeserver with a recovery key // - And log Alice on a new device @@ -1390,8 +1385,7 @@ - (void)testTrustKeyBackupVersionWithPassword - It must fail - The backup must still be untrusted and disabled */ -// TODO: test is currently broken -- (void)xtestTrustKeyBackupVersionWithWrongPassword +- (void)testTrustKeyBackupVersionWithWrongPassword { NSString *password = @"password"; diff --git a/MatrixSDKTests/MXCrossSigningTests.m b/MatrixSDKTests/MXCrossSigningTests.m index e311648759..fc52eb39a1 100644 --- a/MatrixSDKTests/MXCrossSigningTests.m +++ b/MatrixSDKTests/MXCrossSigningTests.m @@ -977,8 +977,7 @@ - (void)testTrustsBetweenBobAndAliceWithTwoDevices // -> Alice2 should see Alice1 as trusted thanks to cross-signing // -> Bob should see Alice3 as trusted thanks to cross-signing // -> Alice3 should see Bob as trusted thanks to cross-signing -// TODO: test is currently broken -- (void)xtestTrustChain +- (void)testTrustChain { // - Have Alice with 2 devices (Alice1 and Alice2) and Bob. All trusted via cross-signing [matrixSDKTestsE2EData doTestWithBobAndAliceWithTwoDevicesAllTrusted:self readyToTest:^(MXSession *aliceSession1, MXSession *aliceSession2, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation) { @@ -1230,8 +1229,7 @@ - (void)testMXCrossSigningResetDetection // - Reset XS on this new device // - Restart Alice first device // -> Alice first device must not trust the cross-signing anymore -// TODO: test is currently broken -- (void)xtestMXCrossSigningResetDetectionAfterRestart +- (void)testMXCrossSigningResetDetectionAfterRestart { // - Have Alice with cross-signing [self doTestWithBobAndBootstrappedAlice:self readyToTest:^(MXSession *bobSession, MXSession *aliceSession, NSString *roomId, XCTestExpectation *expectation) { diff --git a/MatrixSDKTests/MXCrossSigningVerificationTests.m b/MatrixSDKTests/MXCrossSigningVerificationTests.m index 2d38c3d957..c2aecc60ef 100644 --- a/MatrixSDKTests/MXCrossSigningVerificationTests.m +++ b/MatrixSDKTests/MXCrossSigningVerificationTests.m @@ -383,8 +383,7 @@ - (void)testSelfVerificationWithSAS -> Both ends must get a done message - Then, test MXKeyVerification */ -// TODO: test is currently broken -- (void)xtestVerificationByDMFullFlow +- (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) { @@ -616,8 +615,7 @@ - (void)xtestVerificationByDMFullFlow -> Both ends must get a done message - Then, test MXKeyVerification */ -// TODO: test is currently broken -- (void)xtestVerifyingAnotherUserQRCodeVerificationFullFlow +- (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) { diff --git a/MatrixSDKTests/MXCryptoKeyVerificationTests.m b/MatrixSDKTests/MXCryptoKeyVerificationTests.m index 08b3a7c81e..cb8f68918d 100644 --- a/MatrixSDKTests/MXCryptoKeyVerificationTests.m +++ b/MatrixSDKTests/MXCryptoKeyVerificationTests.m @@ -369,8 +369,7 @@ - (void)checkVerificationByToDeviceFullFlowWithBobSession:(MXSession*)bobSession }]; } -// TODO: test is currently broken -- (void)xtestVerificationByToDeviceFullFlow +- (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) { @@ -412,8 +411,7 @@ - (void)testVerificationByToDeviceFullFlowWith2Devices /** Same tests as testVerificationByToDeviceFullFlow but with only alice verifying her 2 devices. */ -// TODO: test is currently broken -- (void)xtestVerificationByToDeviceSelfVerificationFullFlow +- (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) { @@ -563,8 +561,7 @@ - (void)testVerificationByToDeviceRequestWithNoOtherDevice -> Alice gets the requests notification -> They both have it in their pending requests */ -// TODO: test is currently broken -- (void)xtestVerificationByDMRequests +- (void)testVerificationByDMRequests { // - Alice and Bob are in a room [matrixSDKTestsE2EData doE2ETestWithAliceAndBobInARoomWithCryptedMessages:self cryptedBob:YES readyToTest:^(MXSession *aliceSession, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation) { @@ -611,8 +608,7 @@ - (void)xtestVerificationByDMRequests /** Nomical case: The full flow */ -// TODO: test is currently broken -- (void)xtestVerificationByDMFullFlow +- (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) { @@ -1016,8 +1012,7 @@ - (void)testVerificationByDMWithRoomDetection -> Alice gets the requests notification -> They both have it in their pending requests */ -// TODO: test is currently broken -- (void)xtestVerificationByDMWithNoRoom +- (void)testVerificationByDMWithNoRoom { // - Alice and Bob are in a room [matrixSDKTestsE2EData doE2ETestWithBobAndAlice:self readyToTest:^(MXSession *aliceSession, MXSession *bobSession, XCTestExpectation *expectation) { @@ -1075,8 +1070,7 @@ - (void)xtestVerificationByDMWithNoRoom /** Same tests as testVerificationByDMFullFlow but with alice with 2 sessions */ -// TODO: test is currently broken -- (void)xtestVerificationByDMWithAliceWith2Devices +- (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) { @@ -1091,8 +1085,7 @@ - (void)xtestVerificationByDMWithAliceWith2Devices /** Same tests as testVerificationByDMFullFlow but with bob with 2 sessions */ -// TODO: test is currently broken -- (void)xtestVerificationByDMWithAUserWith2Devices +- (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) { diff --git a/MatrixSDKTests/MXCryptoShareTests.m b/MatrixSDKTests/MXCryptoShareTests.m index 6c60b9588a..5f96246e84 100644 --- a/MatrixSDKTests/MXCryptoShareTests.m +++ b/MatrixSDKTests/MXCryptoShareTests.m @@ -142,8 +142,7 @@ - (void)createScenario:(void (^)(MXSession *aliceSession, NSString *roomId, MXMe -> Key share requests must be pending -> Then, they must have been sent */ -// TODO: Test currently broken -- (void)xtestKeyShareRequestFromNewDevice +- (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) { @@ -194,8 +193,7 @@ - (void)xtestKeyShareRequestFromNewDevice -> After a bit, Alice2 should have received all keys -> Key share requests should have complete */ -// TODO: test is currently broken -- (void)xtestNominalCase +- (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) { @@ -271,8 +269,7 @@ - (void)xtestNominalCase - Enable key share requests on Alice2 -> Key share requests should have complete */ -// TODO: test currently broken -- (void)xtestDisableKeyShareRequest +- (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) { @@ -354,8 +351,7 @@ - (void)xtestDisableKeyShareRequest -> key share requests on Alice2 are enabled again -> No m.room_key_request have been made */ -// TODO: Test currently broken -- (void)xtestNoKeyShareRequestIfThereIsABackup +- (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) { @@ -676,8 +672,7 @@ - (void)testShareHistoryKeysWithInvitedUser - Alice invites Bob into the room from her second device -> Bob has recieved only 2 session keys, namely those with `sharedHistory` set to true */ -// TODO: test is currently broken -- (void)xtestSharedHistoryPreservedWhenForwardingKeys +- (void)testSharedHistoryPreservedWhenForwardingKeys { [matrixSDKTestsE2EData doE2ETestWithAliceInARoom:self andStore:[[MXMemoryStore alloc] init] diff --git a/MatrixSDKTests/MXCryptoTests.m b/MatrixSDKTests/MXCryptoTests.m index 1cd1fb68ae..d300fa9e72 100644 --- a/MatrixSDKTests/MXCryptoTests.m +++ b/MatrixSDKTests/MXCryptoTests.m @@ -140,18 +140,18 @@ - (void)testEnableCrypto - (void)testMXSDKOptionsEnableCryptoWhenOpeningMXSession { [MXSDKOptions sharedInstance].enableCryptoWhenStartingMXSession = YES; + MXKeyProvider.sharedInstance.delegate = [[MXKeyProviderStub alloc] init]; [matrixSDKTestsData doMXSessionTestWithBob:self readyToTest:^(MXSession *mxSession, XCTestExpectation *expectation) { // Reset the option to not disturb other tests [MXSDKOptions sharedInstance].enableCryptoWhenStartingMXSession = NO; + MXKeyProvider.sharedInstance.delegate = nil; XCTAssert(mxSession.crypto); - XCTAssert([mxSession.legacyCrypto.store.class hasDataForCredentials:mxSession.matrixRestClient.credentials]); [mxSession enableCrypto:NO success:^{ XCTAssertNil(mxSession.crypto); - XCTAssertFalse([mxSession.legacyCrypto.store.class hasDataForCredentials:mxSession.matrixRestClient.credentials], @"Crypto data must have been trashed"); [expectation fulfill]; @@ -237,8 +237,7 @@ - (void)testCryptoPersistenceInStore }]; } -// TODO: Test currently broken -- (void)xtestMultipleDownloadKeys +- (void)testMultipleDownloadKeys { [matrixSDKTestsE2EData doE2ETestWithBobAndAlice:self readyToTest:^(MXSession *bobSession, MXSession *aliceSession, XCTestExpectation *expectation) { @@ -882,8 +881,7 @@ - (void)testAliceAndBobInACryptedRoomBackPaginationFromMemoryStore }]; } -// TODO: Test currently broken -- (void)xtestAliceAndBobInACryptedRoomBackPaginationFromHomeServer +- (void)testAliceAndBobInACryptedRoomBackPaginationFromHomeServer { [matrixSDKTestsE2EData doE2ETestWithAliceAndBobInARoomWithCryptedMessages:self cryptedBob:YES readyToTest:^(MXSession *aliceSession, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation) { @@ -1323,8 +1321,7 @@ - (void)testAliceAndBlockedBob // Alice sends a message #5 // Check that the message can be decrypted by the Bob's device and the Sam's device -// TODO: Test currently broken -- (void)xtestBlackListUnverifiedDevices +- (void)testBlackListUnverifiedDevices { NSArray *aliceMessages = @[ @"0", @@ -2056,8 +2053,7 @@ - (void)testRoomKeyReshare }]; } -// TODO: Test currently broken -- (void)xtestLateRoomKey +- (void)testLateRoomKey { [matrixSDKTestsE2EData doE2ETestWithAliceAndBobInARoom:self cryptedBob:YES warnOnUnknowDevices:NO readyToTest:^(MXSession *aliceSession, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation) { @@ -2133,8 +2129,7 @@ - (void)xtestLateRoomKey // -> 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 -// TODO: Test currently broken -- (void)xtestOlmSessionUnwedging +- (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) { @@ -2948,8 +2943,7 @@ - (void)testIncomingRoomKeyRequest - 3- Alice sends a second message -> 4- It must be sent (it was never sent before the fix) */ -// TODO: test is currently broken -- (void)xtestDeviceInvalidationWhileSending +- (void)testDeviceInvalidationWhileSending { [matrixSDKTestsE2EData doE2ETestWithAliceInARoom:self readyToTest:^(MXSession *aliceSession, NSString *roomId, XCTestExpectation *expectation) { @@ -3079,8 +3073,7 @@ - (void)testRestoreOlmOutboundKey - 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 */ -// TODO: test is currently broken -- (void)xtestDiscardAndRestoreOlmOutboundKey +- (void)testDiscardAndRestoreOlmOutboundKey { [matrixSDKTestsE2EData doE2ETestWithAliceAndBobInARoomWithCryptedMessages:self cryptedBob:YES readyToTest:^(MXSession *aliceSession, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation) { MXOlmOutboundGroupSession *outboundSession = [aliceSession.legacyCrypto.store outboundGroupSessionWithRoomId:roomId]; diff --git a/MatrixSDKTests/MXCurve25519KeyBackupTests.m b/MatrixSDKTests/MXCurve25519KeyBackupTests.m deleted file mode 100644 index 346ff3a07f..0000000000 --- a/MatrixSDKTests/MXCurve25519KeyBackupTests.m +++ /dev/null @@ -1,125 +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 "MXBaseKeyBackupTests.h" - -#import "MXCrypto_Private.h" -#import "MXCryptoStore.h" -#import "MXRecoveryKey.h" -#import "MXKeybackupPassword.h" -#import "MXOutboundSessionInfo.h" -#import "MXCrossSigning_Private.h" -#import "MXCurve25519BackupAuthData.h" -#import "MXCurve25519KeyBackupAlgorithm.h" - -// Do not bother with retain cycles warnings in tests -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Warc-retain-cycles" - -@interface MXCurve25519KeyBackupTests : MXBaseKeyBackupTests - -@end - -@implementation MXCurve25519KeyBackupTests - -- (NSString *)algorithm -{ - return kMXCryptoCurve25519KeyBackupAlgorithm; -} - -- (BOOL)isUntrusted -{ - return MXCurve25519KeyBackupAlgorithm.isUntrusted; -} - -- (MXKeyBackupVersion*)fakeKeyBackupVersion -{ - return [MXKeyBackupVersion modelFromJSON:@{ - @"algorithm": self.algorithm, - @"auth_data": @{ - @"public_key": @"abcdefg", - @"signatures": @{ - @"something": @{ - @"ed25519:something": @"hijklmnop" - } - } - } - }]; -} - -/** - Check that `[MXKeyBackup prepareKeyBackupVersion]` uses Curve25519 algorithm by default - */ -- (void)testPrepareKeyBackupVersionWithDefaultAlgorithm -{ - [matrixSDKTestsE2EData doE2ETestWithAliceAndBobInARoomWithCryptedMessages:self cryptedBob:YES readyToTest:^(MXSession *aliceSession, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation) { - - XCTAssertNotNil(aliceSession.crypto.backup); - XCTAssertFalse(aliceSession.crypto.backup.enabled); - - // Check that `[MXKeyBackup prepareKeyBackupVersion` returns valid data - [aliceSession.crypto.backup prepareKeyBackupVersionWithPassword:nil algorithm:nil success:^(MXMegolmBackupCreationInfo * _Nonnull keyBackupCreationInfo) { - - XCTAssertNotNil(keyBackupCreationInfo); - XCTAssertEqualObjects(keyBackupCreationInfo.algorithm, kMXCryptoCurve25519KeyBackupAlgorithm); - XCTAssertTrue([keyBackupCreationInfo.authData isKindOfClass:MXCurve25519BackupAuthData.class]); - MXCurve25519BackupAuthData *authData = (MXCurve25519BackupAuthData*) keyBackupCreationInfo.authData; - XCTAssertNotNil(authData.publicKey); - XCTAssertNotNil(authData.signatures); - XCTAssertNotNil(keyBackupCreationInfo.recoveryKey); - - [expectation fulfill]; - - } failure:^(NSError * _Nonnull error) { - XCTFail(@"The request should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - }]; -} - -/** - Check that `[MXKeyBackup prepareKeyBackupVersion` returns valid data - */ -- (void)testPrepareKeyBackupVersion -{ - [matrixSDKTestsE2EData doE2ETestWithAliceAndBobInARoomWithCryptedMessages:self cryptedBob:YES readyToTest:^(MXSession *aliceSession, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation) { - - XCTAssertNotNil(aliceSession.crypto.backup); - XCTAssertFalse(aliceSession.crypto.backup.enabled); - - // Check that `[MXKeyBackup prepareKeyBackupVersion` returns valid data - [aliceSession.crypto.backup prepareKeyBackupVersionWithPassword:nil algorithm:self.algorithm success:^(MXMegolmBackupCreationInfo * _Nonnull keyBackupCreationInfo) { - - XCTAssertNotNil(keyBackupCreationInfo); - XCTAssertEqualObjects(keyBackupCreationInfo.algorithm, kMXCryptoCurve25519KeyBackupAlgorithm); - XCTAssertTrue([keyBackupCreationInfo.authData isKindOfClass:MXCurve25519BackupAuthData.class]); - MXCurve25519BackupAuthData *authData = (MXCurve25519BackupAuthData*) keyBackupCreationInfo.authData; - XCTAssertNotNil(authData.publicKey); - XCTAssertNotNil(authData.signatures); - XCTAssertNotNil(keyBackupCreationInfo.recoveryKey); - - [expectation fulfill]; - - } failure:^(NSError * _Nonnull error) { - XCTFail(@"The request should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - }]; -} - -@end - -#pragma clang diagnostic pop diff --git a/MatrixSDKTests/MatrixSDKTestsE2EData.m b/MatrixSDKTests/MatrixSDKTestsE2EData.m index a93e8479dd..c6e6cf3e33 100644 --- a/MatrixSDKTests/MatrixSDKTestsE2EData.m +++ b/MatrixSDKTests/MatrixSDKTestsE2EData.m @@ -63,12 +63,14 @@ - (void)doE2ETestWithBobAndAlice:(XCTestCase*)testCase readyToTest:(void (^)(MXSession *bobSession, MXSession *aliceSession, XCTestExpectation *expectation))readyToTest { [MXSDKOptions sharedInstance].enableCryptoWhenStartingMXSession = YES; + MXKeyProvider.sharedInstance.delegate = [[MXKeyProviderStub alloc] init]; [matrixSDKTestsData doMXSessionTestWithBob:testCase readyToTest:^(MXSession *bobSession, XCTestExpectation *expectation) { [matrixSDKTestsData doMXSessionTestWithAlice:nil readyToTest:^(MXSession *aliceSession, XCTestExpectation *expectation2) { [MXSDKOptions sharedInstance].enableCryptoWhenStartingMXSession = NO; + MXKeyProvider.sharedInstance.delegate = nil; readyToTest(bobSession, aliceSession, expectation); @@ -85,11 +87,13 @@ - (void)doE2ETestWithAliceInARoom:(XCTestCase*)testCase - (void)doE2ETestWithAliceInARoom:(XCTestCase *)testCase andStore:(id)store readyToTest:(void (^)(MXSession *, NSString *, XCTestExpectation *))readyToTest { [MXSDKOptions sharedInstance].enableCryptoWhenStartingMXSession = YES; + MXKeyProvider.sharedInstance.delegate = [[MXKeyProviderStub alloc] init]; [matrixSDKTestsData doMXSessionTestWithAlice:testCase andStore:store readyToTest:^(MXSession *aliceSession, XCTestExpectation *expectation) { [MXSDKOptions sharedInstance].enableCryptoWhenStartingMXSession = NO; + MXKeyProvider.sharedInstance.delegate = nil; [aliceSession createRoom:nil visibility:kMXRoomDirectoryVisibilityPrivate roomAlias:nil topic:nil success:^(MXRoom *room) { @@ -128,10 +132,15 @@ - (void)doE2ETestWithAliceAndBobInARoom:(XCTestCase*)testCase 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; @@ -180,10 +189,15 @@ - (void)doE2ETestWithAliceByInvitingBobInARoom:(XCTestCase*)testCase 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; @@ -256,12 +270,17 @@ - (void)doE2ETestWithAliceAndBobAndSamInARoom:(XCTestCase*)testCase 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; @@ -309,6 +328,7 @@ - (void)loginUserOnANewDevice:(XCTestCase*)testCase onComplete:(void (^)(MXSession *newSession))onComplete { [MXSDKOptions sharedInstance].enableCryptoWhenStartingMXSession = YES; + MXKeyProvider.sharedInstance.delegate = [[MXKeyProviderStub alloc] init]; MXRestClient *mxRestClient = [[MXRestClient alloc] initWithHomeServer:credentials.homeServer andOnUnrecognizedCertificateBlock:nil]; @@ -327,6 +347,7 @@ - (void)loginUserOnANewDevice:(XCTestCase*)testCase MXStrongifyAndReturnIfNil(newSession); [newSession start:^{ [MXSDKOptions sharedInstance].enableCryptoWhenStartingMXSession = NO; + MXKeyProvider.sharedInstance.delegate = nil; onComplete(newSession); diff --git a/MatrixSDKTests/TestPlans/AllWorkingTests.xctestplan b/MatrixSDKTests/TestPlans/AllWorkingTests.xctestplan index fb12ca99dc..61e671924d 100644 --- a/MatrixSDKTests/TestPlans/AllWorkingTests.xctestplan +++ b/MatrixSDKTests/TestPlans/AllWorkingTests.xctestplan @@ -17,6 +17,7 @@ "DirectRoomTests\/testDirectRoomsAfterInitialSync", "DirectRoomTests\/testDirectRoomsRaceConditions", "DirectRoomTests\/testSummaryAfterInitialSyncAndStorage", + "MXAes256KeyBackupTests", "MXAggregatedEditsTests\/testEditSendAndReceive", "MXAggregatedEditsTests\/testEditSendAndReceiveInE2ERoom", "MXAggregatedEditsTests\/testEditServerSide", @@ -26,7 +27,10 @@ "MXAggregatedEditsTests\/testFormattedEditSendAndReceive", "MXAggregatedEditsTests\/testFormattedEditServerSide", "MXAggregatedEditsUnitTests", + "MXAggregatedReactionTests", + "MXAggregatedReactionTests\/testAggregatedReactionServerSide", "MXAggregatedReactionTests\/testAggregationsFromInitialSync", + "MXAggregatedReactionTests\/testReactionsOnPermalinkFromAGappyInitialSync", "MXAggregatedReactionTests\/testReactionsWhenPaginatingFromAGappyInitialSync", "MXAggregatedReactionTests\/testReactionsWhenPaginatingFromAGappySync", "MXAggregatedReactionTests\/testUnreactAfterInitialSync", @@ -36,36 +40,85 @@ "MXAggregatedReferenceUnitTests", "MXAsyncTaskQueueUnitTests", "MXAuthenticationSessionUnitTests", + "MXAutoDiscoveryTests\/testAutoDiscoveryInvalidIdendityServer", "MXAutoDiscoveryTests\/testAutoDiscoveryNotJSON", "MXAutoDiscoveryTests\/testAutoDiscoverySuccessful", "MXAutoDiscoveryTests\/testAutoDiscoverySuccessfulWithNoContentType", + "MXBackgroundSyncServiceTests", "MXBackgroundSyncServiceTests\/testStoreWithGappyAndOutdatedSync()", "MXBackgroundTaskUnitTests", + "MXBaseKeyBackupTests", "MXBeaconInfoUnitTests", "MXCredentialsUnitTests", "MXCrossSigningTests", "MXCrossSigningVerificationTests", - "MXCrossSigningVerificationTests\/testVerificationByDMFullFlow", - "MXCrossSigningVerificationTests\/testVerifyingAnotherUserQRCodeVerificationFullFlow", "MXCryptoBackupTests", "MXCryptoKeyVerificationTests", - "MXCryptoKeyVerificationTests\/testVerificationByDMFullFlow", - "MXCryptoKeyVerificationTests\/testVerificationByDMWithAUserWith2Devices", - "MXCryptoKeyVerificationTests\/testVerificationByDMWithAliceWith2Devices", + "MXCryptoMigrationTests", "MXCryptoRecoveryServiceTests", "MXCryptoRequestsUnitTests", - "MXCryptoSecretShareTests\/testSecretRequestCancellation", - "MXCryptoSecretStorageTests\/testDeleteSecret", + "MXCryptoSecretShareTests", + "MXCryptoSecretStorageTests", "MXCryptoShareTests", - "MXCryptoTests", + "MXCryptoTests\/testAliceAndBlockedBob", + "MXCryptoTests\/testAliceAndBobInACryptedRoom", + "MXCryptoTests\/testAliceAndBobInACryptedRoom2", + "MXCryptoTests\/testAliceAndBobInACryptedRoomBackPaginationFromHomeServer", + "MXCryptoTests\/testAliceAndBobInACryptedRoomBackPaginationFromMemoryStore", + "MXCryptoTests\/testAliceAndBobInACryptedRoomFromInitialSync", + "MXCryptoTests\/testAliceAndBobWithNewDevice", + "MXCryptoTests\/testAliceAndNotCryptedBobInACryptedRoom", + "MXCryptoTests\/testAliceDecryptOldMessageWithANewDeviceInACryptedRoom", + "MXCryptoTests\/testAliceInACryptedRoom", + "MXCryptoTests\/testAliceInACryptedRoomAfterInitialSync", + "MXCryptoTests\/testAliceInACryptedRoomWithoutEncryption", + "MXCryptoTests\/testAliceWithNewDeviceAndBob", + "MXCryptoTests\/testAliceWithNewDeviceAndBobWithNewDevice", + "MXCryptoTests\/testBadSummaryIsEncryptedState", + "MXCryptoTests\/testBlackListUnverifiedDevices", + "MXCryptoTests\/testCryptoNoDeviceId", + "MXCryptoTests\/testCryptoPersistenceInStore", + "MXCryptoTests\/testDeviceInvalidationWhileSending", + "MXCryptoTests\/testDiscardAndRestoreOlmOutboundKey", + "MXCryptoTests\/testDownloadKeysForUserWithNoDevice", + "MXCryptoTests\/testDownloadKeysWithUnreachableHS", + "MXCryptoTests\/testEnableCrypto", + "MXCryptoTests\/testEnableEncryptionAfterNonCryptedMessages", + "MXCryptoTests\/testEncryptionAlgorithmChange", + "MXCryptoTests\/testEnsureSingleOlmSession", + "MXCryptoTests\/testExportImportRoomKeysWithPassword", + "MXCryptoTests\/testFallbackKeySignatures", + "MXCryptoTests\/testFirstMessageSentWhileSessionWasPaused", + "MXCryptoTests\/testHasKeysToDecryptEvent", + "MXCryptoTests\/testImportRoomKeysWithWrongPassword", + "MXCryptoTests\/testIncomingRoomKeyRequest", + "MXCryptoTests\/testInvitedMemberInACryptedRoom", + "MXCryptoTests\/testInvitedMemberInACryptedRoom2", + "MXCryptoTests\/testIsRoomSharingHistory", + "MXCryptoTests\/testLateRoomKey", + "MXCryptoTests\/testLeftAndJoinedBob", + "MXCryptoTests\/testLeftBobAndAliceWithNewDevice", + "MXCryptoTests\/testMXDeviceListDidUpdateUsersDevicesNotification", + "MXCryptoTests\/testMXSessionEventWithEventId", + "MXCryptoTests\/testMultipleDownloadKeys", + "MXCryptoTests\/testOlmSessionUnwedging", + "MXCryptoTests\/testReplayAttack", + "MXCryptoTests\/testReplayAttackForEventEdits", + "MXCryptoTests\/testRestoreOlmOutboundKey", + "MXCryptoTests\/testRoomIsEncrypted", + "MXCryptoTests\/testRoomKeyReshare", + "MXCryptoTests\/testSendReplyToTextMessage", + "MXDehydrationTests", "MXErrorUnitTests", "MXEventAnnotationUnitTests", "MXEventReferenceUnitTests", "MXEventScanStoreUnitTests", "MXFilterTests\/testFilterAPI", "MXFilterTests\/testFilterCache", + "MXFilterTests\/testUnsupportedSyncFilter", "MXFilterUnitTests", "MXGeoURIComponentsUnitTests", + "MXHTTPClientTests", "MXHTTPClientTests\/testMainThread", "MXJSONModelTests\/testModelFromJSON", "MXJSONModelTests\/testModelsFromJSON", @@ -87,14 +140,30 @@ "MXPollRelationTests\/testBobAndAliceAnswer", "MXPushRuleUnitTests", "MXQRCodeDataUnitTests", + "MXReceiptDataIntegrationTests", + "MXReceiptDataIntegrationTests\/testAcknowledgeMessageInMainTimeline()", + "MXReceiptDataIntegrationTests\/testAcknowledgeMessageInThread()", + "MXReceiptDataIntegrationTests\/testReadReceiptsStorageInThread()", "MXReplyEventParserUnitTests", "MXResponseUnitTests", "MXRestClientExtensionsTests", "MXRestClientNoAuthAPITests", + "MXRestClientTests", + "MXRestClientTests\/testAddAndRemoveTag", + "MXRestClientTests\/testBanUserInRoom", + "MXRestClientTests\/testClaimOneTimeKeysForUsersDevices", + "MXRestClientTests\/testClose", + "MXRestClientTests\/testContextOfEvent", "MXRestClientTests\/testJoinRoomWithRoomAlias", + "MXRoomAliasAvailabilityCheckerResultTests", + "MXRoomAliasAvailabilityCheckerResultTests\/testAliasAvailable()", "MXRoomListDataManagerTests\/testNewRoomInvite()", "MXRoomListDataManagerTests\/testRoomLeave()", + "MXRoomListDataManagerTests\/testRoomUpdateWhenReceivingEncryptedEvent()", "MXRoomListDataManagerTests\/testRoomUpdateWhenReceivingEvent()", + "MXRoomListDataManagerTests\/testRoomUpdateWithLateRoomKeyFix()", + "MXRoomListDataManagerTests\/testRoomUpdateWithLateRoomKeyFixAfterBobRestart()", + "MXRoomListDataManagerTests\/testRoomUpdateWithUTD()", "MXRoomStateTests\/testDeallocation", "MXRoomStateTests\/testInviteByOtherInInitialSync", "MXRoomStateTests\/testInviteByOtherInLive", @@ -109,8 +178,10 @@ "MXSelfSignedHomeserverTests\/testTrustedCertificate", "MXSessionTests", "MXSharedHistoryKeyManagerUnitTests", + "MXSpaceChildContentTests\/testCreateSpaceCheckVisibilityOfPublicRoom()", "MXSpaceChildContentTests\/testUpdateChildSuggestion()", "MXSpaceChildContentTests\/testUpgradeSpaceChild()", + "MXSpaceServiceTest\/testCreateSpace()", "MXStoreFileStoreTests", "MXStoreMemoryStoreTests", "MXStoreNoStoreTests\/testMXNoStoreSeveralPaginateBacks", diff --git a/MatrixSDKTests/TestPlans/CryptoTests.xctestplan b/MatrixSDKTests/TestPlans/CryptoTests.xctestplan deleted file mode 100644 index 4c943befc0..0000000000 --- a/MatrixSDKTests/TestPlans/CryptoTests.xctestplan +++ /dev/null @@ -1,52 +0,0 @@ -{ - "configurations" : [ - { - "id" : "1FA4678B-FC03-4484-997A-D8629931F24B", - "name" : "Configuration 1", - "options" : { - - } - } - ], - "defaultOptions" : { - "codeCoverage" : { - "targets" : [ - { - "containerPath" : "container:MatrixSDK.xcodeproj", - "identifier" : "B14EF1C72397E90400758AF0", - "name" : "MatrixSDK-macOS" - } - ] - }, - "targetForVariableExpansion" : { - "containerPath" : "container:MatrixSDK.xcodeproj", - "identifier" : "B14EF1C72397E90400758AF0", - "name" : "MatrixSDK-macOS" - }, - "uiTestingScreenshotsLifetime" : "keepNever", - "userAttachmentLifetime" : "keepNever" - }, - "testTargets" : [ - { - "selectedTests" : [ - "MXAes256KeyBackupTests", - "MXCrossSigningTests", - "MXCrossSigningVerificationTests", - "MXCryptoKeyVerificationTests", - "MXCryptoMigrationV2Tests", - "MXCryptoSecretStorageTests", - "MXCryptoShareTests", - "MXCryptoTests", - "MXCryptoV2FactoryTests", - "MXCurve25519KeyBackupTests", - "MXMegolmEncryptionTests" - ], - "target" : { - "containerPath" : "container:MatrixSDK.xcodeproj", - "identifier" : "B1E09A0D2397FA950057C069", - "name" : "MatrixSDKTests-macOS" - } - } - ], - "version" : 1 -} diff --git a/MatrixSDKTests/TestPlans/UnitTests.xctestplan b/MatrixSDKTests/TestPlans/UnitTests.xctestplan index 1ebc14fb9b..730bc6d142 100644 --- a/MatrixSDKTests/TestPlans/UnitTests.xctestplan +++ b/MatrixSDKTests/TestPlans/UnitTests.xctestplan @@ -47,7 +47,9 @@ "MXCryptoKeyBackupEngineUnitTests", "MXCryptoMachineUnitTests", "MXCryptoMigrationStoreUnitTests", + "MXCryptoMigrationV2UnitTests", "MXCryptoRequestsUnitTests", + "MXCryptoV2FactoryUnitTests", "MXDeviceInfoSourceUnitTests", "MXDeviceInfoUnitTests", "MXDeviceListOperationsPoolUnitTests", diff --git a/MatrixSDKTests/TestPlans/UnitTestsWithSanitizers.xctestplan b/MatrixSDKTests/TestPlans/UnitTestsWithSanitizers.xctestplan index 392638cb35..4435fd6fbb 100644 --- a/MatrixSDKTests/TestPlans/UnitTestsWithSanitizers.xctestplan +++ b/MatrixSDKTests/TestPlans/UnitTestsWithSanitizers.xctestplan @@ -57,7 +57,9 @@ "MXCryptoKeyBackupEngineUnitTests", "MXCryptoMachineUnitTests", "MXCryptoMigrationStoreUnitTests", + "MXCryptoMigrationV2UnitTests", "MXCryptoRequestsUnitTests", + "MXCryptoV2FactoryUnitTests", "MXDeviceInfoSourceUnitTests", "MXDeviceInfoUnitTests", "MXDeviceListOperationsPoolUnitTests", diff --git a/changelog.d/pr-1770.change b/changelog.d/pr-1770.change new file mode 100644 index 0000000000..3bb992ba9e --- /dev/null +++ b/changelog.d/pr-1770.change @@ -0,0 +1 @@ +Crypto: Enable Crypto SDK by default From 3c614a8d7d92f846d191cc98ae1824113574ef83 Mon Sep 17 00:00:00 2001 From: Andy Uhnak Date: Mon, 17 Apr 2023 16:43:12 +0100 Subject: [PATCH 2/4] Deprecate MXLegacyCrypto --- .../Background/MXBackgroundSyncService.swift | 12 +- MatrixSDK/Contrib/Swift/MXSession.swift | 18 -- MatrixSDK/Crypto/MXCrypto.m | 29 -- .../EventTimeline/Room/MXRoomEventTimeline.m | 6 - .../Thread/MXThreadEventTimeline.swift | 6 - MatrixSDK/MXSDKOptions.h | 20 -- MatrixSDK/MXSDKOptions.m | 7 - MatrixSDK/MXSession.h | 20 +- MatrixSDK/MXSession.m | 303 +++--------------- MatrixSDKTests/MXCryptoKeyVerificationTests.m | 6 - MatrixSDKTests/MXCryptoTests.m | 291 +++-------------- MatrixSDKTests/MatrixSDKTestsData.m | 33 +- changelog.d/pr-1772.change | 1 + 13 files changed, 118 insertions(+), 634 deletions(-) create mode 100644 changelog.d/pr-1772.change diff --git a/MatrixSDK/Background/MXBackgroundSyncService.swift b/MatrixSDK/Background/MXBackgroundSyncService.swift index 5fe4e5abfb..efb2ad4d1f 100644 --- a/MatrixSDK/Background/MXBackgroundSyncService.swift +++ b/MatrixSDK/Background/MXBackgroundSyncService.swift @@ -67,7 +67,6 @@ public enum MXBackgroundSyncServiceError: Error { /// - Parameter credentials: account credentials public init( withCredentials credentials: MXCredentials, - isCryptoSDKEnabled: Bool = false, persistTokenDataHandler: MXRestClientPersistTokenDataHandler? = nil, unauthenticatedHandler: MXRestClientUnauthenticatedHandler? = nil ) { @@ -90,16 +89,9 @@ public enum MXBackgroundSyncServiceError: Error { self.restClient = restClient store = MXBackgroundStore(withCredentials: credentials) - // We can flush any crypto data if our sync response store is empty - let resetBackgroundCryptoStore = syncResponseStoreManager.syncToken() == nil - if isCryptoSDKEnabled { - MXLog.debug("[MXBackgroundSyncService] init: constructing crypto v2") - crypto = MXBackgroundCryptoV2(credentials: credentials, restClient: restClient) - } else { - MXLog.debug("[MXBackgroundSyncService] init: constructing legacy crypto") - crypto = MXLegacyBackgroundCrypto(credentials: credentials, resetBackgroundCryptoStore: resetBackgroundCryptoStore) - } + MXLog.debug("[MXBackgroundSyncService] init: constructing crypto") + crypto = MXBackgroundCryptoV2(credentials: credentials, restClient: restClient) pushRulesManager = MXBackgroundPushRulesManager(withCredentials: credentials) MXLog.debug("[MXBackgroundSyncService] init complete") diff --git a/MatrixSDK/Contrib/Swift/MXSession.swift b/MatrixSDK/Contrib/Swift/MXSession.swift index e738b0e1e4..912332af48 100644 --- a/MatrixSDK/Contrib/Swift/MXSession.swift +++ b/MatrixSDK/Contrib/Swift/MXSession.swift @@ -127,24 +127,6 @@ public extension MXSession { __setStore(store, success: currySuccess(completion), failure: curryFailure(completion)) } - - /** - Enable End-to-End encryption. - - In case of enabling, the operation will complete when the session will be ready - to make encrytion with other users devices - - - parameters: - - isEnabled: `false` stops crypto and erases crypto data. - - completion: A block called when the SDK has completed a catchup, or times out. - - response: Indicates whether the sync was successful. - */ - @nonobjc func enableCrypto(_ isEnabled: Bool, completion: @escaping (_ response: MXResponse) -> Void) { - __enableCrypto(isEnabled, success: currySuccess(completion), failure: curryFailure(completion)) - } - - - /// Create a new room with the given parameters. /// - Parameters: /// - name: Name of the room diff --git a/MatrixSDK/Crypto/MXCrypto.m b/MatrixSDK/Crypto/MXCrypto.m index 9cf0f2511a..fc599496f7 100644 --- a/MatrixSDK/Crypto/MXCrypto.m +++ b/MatrixSDK/Crypto/MXCrypto.m @@ -156,12 +156,6 @@ @implementation MXLegacyCrypto __block id crypto; #ifdef MX_CRYPTO - if (MXSDKOptions.sharedInstance.enableCryptoSDK) - { - MXLogFailure(@"[MXCrypto] createCryptoWithMatrixSession: Crypto V2 should not be created directly, use initializeCryptoWithMatrixSession instead"); - return nil; - } - dispatch_queue_t cryptoQueue = [MXLegacyCrypto dispatchQueueForUser:mxSession.matrixRestClient.credentials.userId]; dispatch_sync(cryptoQueue, ^{ @@ -180,29 +174,6 @@ + (void)initializeCryptoWithMatrixSession:(MXSession *)mxSession complete:(void (^)(id crypto, NSError *error))complete { #ifdef MX_CRYPTO - - if (MXSDKOptions.sharedInstance.enableCryptoSDK) - { - BOOL enableCrypto = [MXSDKOptions sharedInstance].enableCryptoWhenStartingMXSession || [MXCryptoV2Factory.shared hasCryptoDataFor:mxSession]; - if (enableCrypto) - { - [MXCryptoV2Factory.shared buildCryptoWithSession:mxSession - migrationProgress:migrationProgress - success:^(id crypto) { - complete(crypto, nil); } - failure:^(NSError *error) { - complete(nil, error); - }]; - } - else - { - dispatch_async(dispatch_get_main_queue(), ^{ - complete(nil, nil); - }); - } - return; - } - [self initalizeLegacyCryptoWithMatrixSession:mxSession complete:complete]; #else complete(nil); diff --git a/MatrixSDK/Data/EventTimeline/Room/MXRoomEventTimeline.m b/MatrixSDK/Data/EventTimeline/Room/MXRoomEventTimeline.m index cf9dabcaad..300ab9934a 100644 --- a/MatrixSDK/Data/EventTimeline/Room/MXRoomEventTimeline.m +++ b/MatrixSDK/Data/EventTimeline/Room/MXRoomEventTimeline.m @@ -142,8 +142,6 @@ - (void)initialiseState:(NSArray *)stateEvents - (void)destroy { - [room.mxSession resetReplayAttackCheckInTimeline:_timelineId]; - if (httpOperation) { // Cancel the current server request @@ -190,8 +188,6 @@ - (BOOL)canPaginate:(MXTimelineDirection)direction - (void)resetPagination { - [room.mxSession resetReplayAttackCheckInTimeline:_timelineId]; - // Reset the back state to the current room state backState = [[MXRoomState alloc] initBackStateWith:_state]; @@ -203,8 +199,6 @@ - (MXHTTPOperation *)resetPaginationAroundInitialEventWithLimit:(NSUInteger)limi { NSParameterAssert(success); NSAssert(_initialEventId, @"[MXRoomEventTimeline] resetPaginationAroundInitialEventWithLimit cannot be called on live timeline"); - - [room.mxSession resetReplayAttackCheckInTimeline:_timelineId]; // Reset the store if (!store.isPermanent) diff --git a/MatrixSDK/Data/EventTimeline/Thread/MXThreadEventTimeline.swift b/MatrixSDK/Data/EventTimeline/Thread/MXThreadEventTimeline.swift index b5fada9a90..f3b0c1e380 100644 --- a/MatrixSDK/Data/EventTimeline/Thread/MXThreadEventTimeline.swift +++ b/MatrixSDK/Data/EventTimeline/Thread/MXThreadEventTimeline.swift @@ -99,8 +99,6 @@ public class MXThreadEventTimeline: NSObject, MXEventTimeline { } public func destroy() { - thread.session?.resetReplayAttackCheck(inTimeline: timelineId) - removeAllListeners() currentHttpOperation?.cancel() @@ -132,8 +130,6 @@ public class MXThreadEventTimeline: NSObject, MXEventTimeline { } public func resetPagination() { - thread.session?.resetReplayAttackCheck(inTimeline: timelineId) - // Reset store pagination storeMessagesEnumerator = store.messagesEnumerator(forRoom: thread.roomId) @@ -150,8 +146,6 @@ public class MXThreadEventTimeline: NSObject, MXEventTimeline { fatalError("[MXThreadEventTimeline][\(timelineId)] resetPaginationAroundInitialEventWithLimit cannot be called on live timeline") } - thread.session?.resetReplayAttackCheck(inTimeline: timelineId) - // Reset the store if !store.isPermanent { store.deleteAllData() diff --git a/MatrixSDK/MXSDKOptions.h b/MatrixSDK/MXSDKOptions.h index 422a8c6617..778ab3d6d5 100644 --- a/MatrixSDK/MXSDKOptions.h +++ b/MatrixSDK/MXSDKOptions.h @@ -203,13 +203,6 @@ NS_ASSUME_NONNULL_BEGIN */ @property (nonatomic) BOOL enableRoomSharedHistoryOnInvite; -/** - Use the newer rust-based `MatrixCryptoSDK` instead of the legacy `MatrixSDK`'s internal crypto module. - - @remark YES by default - */ -@property (nonatomic) BOOL enableCryptoSDK; - /** Flag indicating whether this account requires a re-verification after migrating to Crypto SDK @@ -220,11 +213,6 @@ NS_ASSUME_NONNULL_BEGIN */ @property (nonatomic) BOOL needsVerificationUpgrade; -/** - The text-based identifier for the crypto module being used (e.g. native vs rust) - */ -@property (nonatomic, readonly) NSString *cryptoModuleId; - /** Enable symmetric room key backups @@ -239,14 +227,6 @@ NS_ASSUME_NONNULL_BEGIN */ @property (nonatomic) BOOL enableNewClientInformationFeature; -/** - Enable the calculating and display of progress during session startup, incl store migration, - syncing and response processing. - - @remark YES by default - */ -@property (nonatomic) BOOL enableStartupProgress; - @end NS_ASSUME_NONNULL_END diff --git a/MatrixSDK/MXSDKOptions.m b/MatrixSDK/MXSDKOptions.m index e1cc0fa19f..0652f18fdc 100644 --- a/MatrixSDK/MXSDKOptions.m +++ b/MatrixSDK/MXSDKOptions.m @@ -54,21 +54,14 @@ - (instancetype)init _authEnableRefreshTokens = NO; _enableThreads = NO; _enableRoomSharedHistoryOnInvite = NO; - _enableCryptoSDK = YES; _needsVerificationUpgrade = NO; _enableSymmetricBackup = NO; _enableNewClientInformationFeature = NO; - _enableStartupProgress = YES; } return self; } -- (NSString *)cryptoModuleId -{ - return self.enableCryptoSDK ? @"rust" : @"native"; -} - - (void)setRoomListDataManagerClass:(Class)roomListDataManagerClass { // Sanity check diff --git a/MatrixSDK/MXSession.h b/MatrixSDK/MXSession.h index a9ce329a02..31e274dc32 100644 --- a/MatrixSDK/MXSession.h +++ b/MatrixSDK/MXSession.h @@ -776,17 +776,9 @@ typedef void (^MXOnBackgroundSyncFail)(NSError *error); - (void)enableVoIPWithCallStack:(id)callStack; /** - Enable End-to-End encryption. - - In case of enabling, the operation will complete when the session will be ready - to make encrytion with other users devices - - @param enableCrypto NO stops crypto and erases crypto data. - - @param success A block object called when the operation succeeds. - @param failure A block object called when the operation fails. + Disable End-to-End encryption. */ -- (void)enableCrypto:(BOOL)enableCrypto success:(void (^)(void))success failure:(void (^)(NSError *error))failure NS_REFINED_FOR_SWIFT; +- (void)disableCrypto; /** Get the versions of the specification supported by the server. @@ -1596,14 +1588,6 @@ typedef void (^MXOnBackgroundSyncFail)(NSError *error); inTimeline:(NSString*)timeline onComplete:(void (^)(NSArray *failedEvents))onComplete; -/** - Reset replay attack data for the given timeline. - - @param timeline the id of the timeline. - */ -- (void)resetReplayAttackCheckInTimeline:(NSString*)timeline; - - #pragma mark - Global events listeners /** Register a global listener to events related to the current session. diff --git a/MatrixSDK/MXSession.m b/MatrixSDK/MXSession.m index 051b41d122..2b0b16e362 100644 --- a/MatrixSDK/MXSession.m +++ b/MatrixSDK/MXSession.m @@ -312,10 +312,7 @@ - (id)initWithMatrixRestClient:(MXRestClient*)mxRestClient _homeserverCapabilitiesService = [[MXHomeserverCapabilitiesService alloc] initWithSession: self]; [_homeserverCapabilitiesService updateWithCompletion:nil]; - if (MXSDKOptions.sharedInstance.enableStartupProgress) - { - _startupProgress = [[MXSessionStartupProgress alloc] init]; - } + _startupProgress = [[MXSessionStartupProgress alloc] init]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onDidDecryptEvent:) name:kMXEventDidDecryptNotification object:nil]; @@ -398,24 +395,12 @@ -(void)setStore:(id)store success:(void (^)(void))onStoreDataReady fail // Check if the user has enabled crypto MXWeakify(self); - [MXLegacyCrypto initializeCryptoWithMatrixSession:self migrationProgress:^(double progress) { - if (MXSDKOptions.sharedInstance.enableStartupProgress) - { - [self.startupProgress updateProgressForStage:MXSessionStartupStageStoreMigration progress:progress]; - } + [self initializeCryptoWithProgress:^(double progress) { + [self.startupProgress updateProgressForStage:MXSessionStartupStageStoreMigration progress:progress]; - } complete:^(id crypto, NSError *error) { + } success:^(id crypto) { MXStrongifyAndReturnIfNil(self); - if (!crypto && error) - { - if (failure) - { - failure(error); - } - return; - } - self->_crypto = crypto; // Sanity check: The session may be closed before the end of this operation. @@ -539,6 +524,11 @@ -(void)setStore:(id)store success:(void (^)(void))onStoreDataReady fail // The SDK client can use this data onStoreDataReady(); } + } failure:^(NSError *error) { + if (failure) + { + failure(error); + } }]; } failure:^(NSError *error) { @@ -551,6 +541,34 @@ -(void)setStore:(id)store success:(void (^)(void))onStoreDataReady fail }]; } +- (void)initializeCryptoWithProgress:(void (^)(double))progress + success:(void (^)(id crypto))success + failure:(void (^)(NSError *error))failure +{ + BOOL enableCrypto = [MXSDKOptions sharedInstance].enableCryptoWhenStartingMXSession || [MXCryptoV2Factory.shared hasCryptoDataFor:self]; + if (!enableCrypto) + { + MXLogWarning(@"[MXSession] initializeCrypto: Not starting crypto automatically due to SDK settings"); + dispatch_async(dispatch_get_main_queue(), ^{ + success(nil); + }); + return; + } + + [MXCryptoV2Factory.shared buildCryptoWithSession:self + migrationProgress:progress + success:^(id crypto) { + + MXLogDebug(@"[MXSession] initializeCrypto: Successfully initialized crypto module"); + success(crypto); + + } failure:^(NSError *error) { + + MXLogErrorDetails(@"[MXSession] initializeCrypto: Error initialized crypto module", error); + failure(error); + }]; +} + - (void)setRoomListDataManager:(id)roomListDataManager { NSParameterAssert(_roomListDataManager == nil); @@ -574,7 +592,7 @@ - (void)handleSyncResponse:(MXSyncResponse *)syncResponse // Check whether this is the initial sync BOOL isInitialSync = !self.isEventStreamInitialised; - [self handleCryptoSyncResponse:syncResponse onComplete:^{ + [self handleCryptoEventsInSyncResponse:syncResponse onComplete:^{ dispatch_group_t dispatchGroup = dispatch_group_create(); @@ -591,7 +609,7 @@ - (void)handleSyncResponse:(MXSyncResponse *)syncResponse void(^dispatch_group_leave_with_progress)(dispatch_group_t) = ^(dispatch_group_t dispatchGroup) { dispatch_group_leave(dispatchGroup); - if (MXSDKOptions.sharedInstance.enableStartupProgress && progress) + if (progress) { progress([self.startupProgress overallProgressForStep:completedRooms totalCount:totalRooms progress:1]); completedRooms += 1; @@ -778,30 +796,6 @@ - (void)handleSyncResponse:(MXSyncResponse *)syncResponse // Sync point: wait that all rooms in the /sync response have been loaded // and their /sync response has been processed dispatch_group_notify(dispatchGroup, dispatch_get_main_queue(), ^{ - - // Legacy crypto requires that we deal with device list changes, OTKs etc at the end of the sync loop. - // This will be removed altogether with `MXLegacyCrypto` - if ([self.crypto isKindOfClass:[MXLegacyCrypto class]]) - { - // Handle device list updates - if (syncResponse.deviceLists) - { - [(MXLegacyCrypto *)self.crypto handleDeviceListsChanges:syncResponse.deviceLists]; - } - - // Handle one_time_keys_count - if (syncResponse.deviceOneTimeKeysCount) - { - [(MXLegacyCrypto *)self.crypto handleDeviceOneTimeKeysCount:syncResponse.deviceOneTimeKeysCount]; - } - - [(MXLegacyCrypto *)self.crypto handleDeviceUnusedFallbackKeys:syncResponse.unusedFallbackKeys]; - - // Tell the crypto module to do its processing - [(MXLegacyCrypto *)self.crypto onSyncCompleted:self.store.eventStreamToken - nextSyncToken:syncResponse.nextBatch - catchingUp:self.catchingUp]; - } // Update live event stream token MXLogDebug(@"[MXSession] Next sync token: %@", syncResponse.nextBatch); @@ -1357,25 +1351,12 @@ - (void)close - (MXHTTPOperation*)logout:(void (^)(void))success failure:(void (^)(NSError *error))failure { - // Create an empty operation that will be mutated later - MXHTTPOperation *operation = [[MXHTTPOperation alloc] init]; - // Clear crypto data // For security and because it will be no more useful as we will get a new device id // on the next log in - MXWeakify(self); - [self enableCrypto:NO success:^{ - MXStrongifyAndReturnIfNil(self); - - if (!operation.isCancelled) - { - MXHTTPOperation *operation2 = [self.matrixRestClient logout:success failure:failure]; - [operation mutateTo:operation2]; - } - - } failure:nil]; - - return operation; + [self disableCrypto]; + + return [self.matrixRestClient logout:success failure:failure]; } - (MXHTTPOperation*)deactivateAccountWithAuthParameters:(NSDictionary*)authParameters @@ -1458,7 +1439,7 @@ - (void)serverSyncWithServerTimeout:(NSUInteger)serverTimeout setPresence:(NSString*)setPresence { // We only want to report sync progress when doing initial sync - BOOL shoulReportStartupProgress = MXSDKOptions.sharedInstance.enableStartupProgress && !self.isEventStreamInitialised; + BOOL shoulReportStartupProgress = !self.isEventStreamInitialised; if (shoulReportStartupProgress) { // There is no way to track percentage progress when syncing with the server, so we always use 0% @@ -1994,7 +1975,6 @@ - (void)handleAccountData:(NSDictionary*)accountDataUpdate } } - [self validateAccountData]; self.store.userAccountData = _accountData.accountData; // Trigger a global notification for the account data update @@ -2007,27 +1987,6 @@ - (void)handleAccountData:(NSDictionary*)accountDataUpdate } } -/** - Private method to validate local account data and report any potential state corruption - */ -- (void)validateAccountData -{ - if (![self.crypto isKindOfClass:[MXLegacyCrypto class]]) - { - return; - } - - // Detecting an issue in legacy crypto where more than one valid SSSS key is present on the client - // https://github.com/vector-im/element-ios/issues/4569 - NSInteger keysCount = ((MXLegacyCrypto *)self.crypto).secretStorage.numberOfValidKeys; - if (keysCount > 1) - { - MXLogErrorDetails(@"[MXSession] validateAccountData: Detected multiple valid SSSS keys, should only have one at most", @{ - @"count": @(keysCount) - }); - } -} - - (void)updateSummaryDirectUserIdForRooms:(NSSet *)roomIds { // If the initial sync response is not processed enough, rooms is not yet mounted. @@ -2052,95 +2011,15 @@ - (void)updateSummaryDirectUserIdForRooms:(NSSet *)roomIds } } -// Temporary junction to deal with sync response depending on the variant of crypto -// that cannot be easily hidden behind a protocol. Legacy implementation will eventually -// be fully removed. -- (void)handleCryptoSyncResponse:(MXSyncResponse *)syncResponse - onComplete:(void (^)(void))onComplete +- (void)handleCryptoEventsInSyncResponse:(MXSyncResponse *)syncResponse onComplete:(void (^)(void))onComplete { - if (!self.crypto || [self.crypto isKindOfClass:[MXLegacyCrypto class]]) - { - // Legacy crypto requires pre-processed to-device events before everything else to make future decryptions work - [self handleToDeviceEvents:syncResponse.toDevice.events onComplete:onComplete]; - } - else - { - // New and all future crypto modules can handle the entire sync response in full - [self.crypto handleSyncResponse:syncResponse onComplete:onComplete]; - } -} - -- (void)handleToDeviceEvents:(NSArray *)events onComplete:(void (^)(void))onComplete -{ - NSMutableArray *supportedEvents = [NSMutableArray arrayWithCapacity:events.count]; - for (MXEvent *event in events) - { - if ([MXTools isSupportedToDeviceEvent:event]) - { - MXLogDebug(@"[MXSession] handleToDeviceEvents: Processing new to-device event msgid: %@", event.content[kMXToDeviceMessageId]) - [supportedEvents addObject:event]; - } - } - - if (supportedEvents.count == 0) + if (!self.crypto) { onComplete(); return; } - [self decryptEvents:supportedEvents inTimeline:nil onComplete:^(NSArray *failedEvents) { - dispatch_group_t dispatchGroup = dispatch_group_create(); - - for (MXEvent *event in supportedEvents) - { - if (!event.decryptionError) - { - MXLogDebug(@"[MXSession] handleToDeviceEvents: Received new to-device event `%@` from `%@` msgid: %@", event.type, event.sender, event.wireContent[kMXToDeviceMessageId]) - dispatch_group_enter(dispatchGroup); - [self handleToDeviceEvent:event onComplete:^{ - dispatch_group_leave(dispatchGroup); - }]; - } - else - { - MXLogDebug(@"[MXSession] handleToDeviceEvents: Warning: Unable to decrypt to-device event: %@\nError: %@", event.wireContent[kMXMessageBodyKey], event.decryptionError); - } - } - - dispatch_group_notify(dispatchGroup, dispatch_get_main_queue(), ^{ - onComplete(); - }); - }]; -} - -- (void)handleToDeviceEvent:(MXEvent *)event onComplete:(void (^)(void))onComplete -{ - void (^onHandleToDeviceEventDone)(void) = ^(void) { - [[NSNotificationCenter defaultCenter] postNotificationName:kMXSessionOnToDeviceEventNotification - object:self - userInfo:@{ - kMXSessionNotificationEventKey: event - }]; - - onComplete(); - }; - - switch (event.eventType) - { - case MXEventTypeRoomKey: - { - if ([_crypto isKindOfClass:[MXLegacyCrypto class]]) - { - [(MXLegacyCrypto *)_crypto handleRoomKeyEvent:event onComplete:onHandleToDeviceEventDone]; - } - break; - } - - default: - onHandleToDeviceEventDone(); - break; - } - [_eventStreamService dispatchOnLiveToDeviceWithEvent:event]; + [self.crypto handleSyncResponse:syncResponse onComplete:onComplete]; } /** @@ -2221,17 +2100,10 @@ - (void)handleBackgroundSyncCacheIfRequiredWithCompletion:(void (^)(void))comple [asyncTaskQueue asyncWithExecute:^(void (^ taskCompleted)(void)) { [syncResponseStoreManager mergedSyncResponseFromSyncResponseIds:outdatedSyncResponseIds completion:^(MXCachedSyncResponse * _Nullable outdatedCachedSyncResponse) { - if (outdatedCachedSyncResponse) - { - [self handleOutdatedSyncResponse:outdatedCachedSyncResponse.syncResponse - completion:^{ - taskCompleted(); - }]; - } - else - { + // There is no need to handle `outdatedCachedSyncResponse` manually anymore, ignoring the result + dispatch_async(dispatch_get_main_queue(), ^{ taskCompleted(); - } + }); }]; }]; @@ -2280,26 +2152,6 @@ - (void)handleBackgroundSyncCacheIfRequiredWithCompletion:(void (^)(void))comple }]; } -- (void)handleOutdatedSyncResponse:(MXSyncResponse *)syncResponse - completion:(void (^)(void))completion -{ - MXLogDebug(@"[MXSession] handleOutdatedSyncResponse: %tu joined rooms, %tu invited rooms, %tu left rooms, %tu toDevice events.", syncResponse.rooms.join.count, syncResponse.rooms.invite.count, syncResponse.rooms.leave.count, syncResponse.toDevice.events.count); - - if (!self.crypto || [self.crypto isKindOfClass:[MXLegacyCrypto class]]) - { - // Legacy crypto requires pre-processed to-device events before everything else to make future decryptions work - [self handleToDeviceEvents:syncResponse.toDevice.events onComplete:completion]; - } - else - { - // Only legacy crypto requires different DBs for foreground and background processes, other crypto modules - // write to a single DB in both processes, so there is no need to replay outdated sync response - dispatch_async(dispatch_get_main_queue(), ^{ - completion(); - }); - } -} - #pragma mark - Options - (void)enableVoIPWithCallStack:(id)callStack { @@ -2309,56 +2161,15 @@ - (void)enableVoIPWithCallStack:(id)callStack _callManager = [[MXCallManager alloc] initWithMatrixSession:self andCallStack:callStack]; } -- (void)enableCrypto:(BOOL)enableCrypto success:(void (^)(void))success failure:(void (^)(NSError *))failure +- (void)disableCrypto { - MXLogDebug(@"[MXSesion] enableCrypto: %@", @(enableCrypto)); + MXLogDebug(@"[MXSesion] disableCrypto"); - if (enableCrypto && !_crypto) - { - NSError *error; - _crypto = [MXLegacyCrypto createCryptoWithMatrixSession:self error:&error]; - if (!_crypto && error) - { - if (failure) - { - failure(error); - } - return; - } - - if (_state == MXSessionStateRunning) - { - [self startCrypto:success failure:failure]; - } - else - { - MXLogDebug(@"[MXSesion] enableCrypto: crypto module will be start later (MXSession.state: %@)", [MXTools readableSessionState:_state]); - - if (success) - { - dispatch_async(dispatch_get_main_queue(), ^{ - success(); - }); - } - } - } - else if (!enableCrypto && _crypto) + if (_crypto) { // Erase all crypto data of this user [_crypto close:YES]; _crypto = nil; - - if (success) - { - success(); - } - } - else - { - if (success) - { - success(); - } } } @@ -3415,7 +3226,7 @@ - (void)fixRoomsSummariesLastMessageWithMaxServerPaginationCount:(NSUInteger)max __block NSInteger completedRooms = 0; void(^dispatch_group_leave_with_progress)(dispatch_group_t) = ^(dispatch_group_t dispatchGroup) { dispatch_group_leave(dispatchGroup); - if (MXSDKOptions.sharedInstance.enableStartupProgress && progress) + if (progress) { progress([self.startupProgress overallProgressForStep:completedRooms totalCount:self.rooms.count progress:1]); completedRooms += 1; @@ -5056,14 +4867,6 @@ - (void)decryptEvents:(NSArray *)events } } -- (void)resetReplayAttackCheckInTimeline:(NSString*)timeline -{ - if ([_crypto isKindOfClass:[MXLegacyCrypto class]]) - { - [(MXLegacyCrypto *)_crypto resetReplayAttackCheckInTimeline:timeline]; - } -} - // Called when an event finally got decrypted after a late room key reception - (void)onDidDecryptEvent:(NSNotification *)notification { diff --git a/MatrixSDKTests/MXCryptoKeyVerificationTests.m b/MatrixSDKTests/MXCryptoKeyVerificationTests.m index cb8f68918d..c803b60442 100644 --- a/MatrixSDKTests/MXCryptoKeyVerificationTests.m +++ b/MatrixSDKTests/MXCryptoKeyVerificationTests.m @@ -455,12 +455,6 @@ - (void)testVerificationByToDeviceRequestCancellation success:^(id requestFromAliceDevice1POV) { // -> The other device list should have been computed well - if (MXSDKOptions.sharedInstance.enableCryptoSDK) - { - XCTFail(@"This test uses `MXKeyVerificationByToDeviceRequest` which is only compatible with Crypto V1. Replace assertions to make it compatible with V2 as well"); - [expectation fulfill]; - return; - } MXKeyVerificationByToDeviceRequest *toDeviceRequestFromAliceDevice1POV = (MXKeyVerificationByToDeviceRequest*)requestFromAliceDevice1POV; XCTAssertNotNil(toDeviceRequestFromAliceDevice1POV.requestedOtherDeviceIds); NSSet *expectedRequestedDevices = [NSSet setWithArray:@[aliceSession2DeviceId, aliceSession3DeviceId]]; diff --git a/MatrixSDKTests/MXCryptoTests.m b/MatrixSDKTests/MXCryptoTests.m index d300fa9e72..4021b939b7 100644 --- a/MatrixSDKTests/MXCryptoTests.m +++ b/MatrixSDKTests/MXCryptoTests.m @@ -105,38 +105,6 @@ - (NSUInteger)checkEncryptedEvent:(MXEvent*)event roomId:(NSString*)roomId clear #pragma mark - MXCrypto -- (void)testEnableCrypto -{ - [matrixSDKTestsData doMXSessionTestWithBob:self readyToTest:^(MXSession *mxSession, XCTestExpectation *expectation) { - - XCTAssertNil(mxSession.crypto, @"Crypto is disabled by default"); - - XCTAssertFalse([mxSession.legacyCrypto.store.class hasDataForCredentials:mxSession.matrixRestClient.credentials]); - - [mxSession enableCrypto:YES success:^{ - - XCTAssert(mxSession.crypto); - XCTAssert([mxSession.legacyCrypto.store.class hasDataForCredentials:mxSession.matrixRestClient.credentials]); - - [mxSession enableCrypto:NO success:^{ - - XCTAssertNil(mxSession.crypto); - XCTAssertFalse([mxSession.legacyCrypto.store.class hasDataForCredentials:mxSession.matrixRestClient.credentials], @"Crypto data must have been trashed"); - - [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]; - }]; - }]; -} - - (void)testMXSDKOptionsEnableCryptoWhenOpeningMXSession { [MXSDKOptions sharedInstance].enableCryptoWhenStartingMXSession = YES; @@ -149,90 +117,11 @@ - (void)testMXSDKOptionsEnableCryptoWhenOpeningMXSession XCTAssert(mxSession.crypto); - [mxSession enableCrypto:NO success:^{ - - XCTAssertNil(mxSession.crypto); - - [expectation fulfill]; - - } failure:^(NSError *error) { - XCTFail(@"The request should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - - }]; -} - -- (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); + [mxSession disableCrypto]; - [expectation fulfill]; - - } failure:^(NSError *error) { - XCTFail(@"Cannot set up intial test conditions - error: %@", error); - [expectation fulfill]; - }]; + XCTAssertNil(mxSession.crypto); - } failure:^(NSError *error) { - XCTFail(@"The request should not fail - NSError: %@", error); - [expectation fulfill]; - }]; + [expectation fulfill]; }]; } @@ -2318,44 +2207,39 @@ - (void)testLeftAndJoinedBob // 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; + [bobSession disableCrypto]; - [bobSession2 joinRoom:roomFromAlicePOV.roomId viaServers:nil success:^(MXRoom *roomFromBobPOV2) { + // Relog bob to simulate a new device + [MXSDKOptions sharedInstance].enableCryptoWhenStartingMXSession = YES; + [matrixSDKTestsData relogUserSession:self session:bobSession withPassword:MXTESTS_BOB_PWD onComplete:^(MXSession *bobSession2) { - // 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) { + [MXSDKOptions sharedInstance].enableCryptoWhenStartingMXSession = NO; - XCTAssert(event.clearEvent, @"Bob must be able to decrypt this new message on his new device"); + [bobSession2 joinRoom:roomFromAlicePOV.roomId viaServers:nil success:^(MXRoom *roomFromBobPOV2) { - XCTAssertEqual(0, [self checkEncryptedEvent:event roomId:roomFromBobPOV2.roomId clearMessage:message2FromAlice senderSession:aliceSession]); + // 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) { - [expectation fulfill]; + 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]); - [roomFromAlicePOV sendTextMessage:message2FromAlice threadId:nil success:nil failure:^(NSError *error) { - XCTFail(@"Cannot set up intial test conditions - error: %@", error); [expectation fulfill]; + }]; + }]; - } failure:^(NSError *error) { + [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) { @@ -2497,51 +2381,47 @@ - (void)testEnableEncryptionAfterNonCryptedMessages // Make Bob come back to the room with a new device // Clear his crypto store - [bobSession enableCrypto:NO success:^{ + [bobSession disableCrypto]; - // Relog bob to simulate a new device - [MXSDKOptions sharedInstance].enableCryptoWhenStartingMXSession = YES; - [matrixSDKTestsData relogUserSession:self session:bobSession withPassword:MXTESTS_BOB_PWD onComplete:^(MXSession *newBobSession) { + // 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; + [MXSDKOptions sharedInstance].enableCryptoWhenStartingMXSession = NO; - MXRoom *roomFromNewBobPOV = [newBobSession roomWithRoomId:roomFromAlicePOV.roomId]; + 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"); + 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:^{ + // 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) { + [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"); + 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]); + 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"); + NSDictionary *bobDevices = [aliceSession.legacyCrypto.store devicesForUser:newBobSession.myUser.userId]; + XCTAssertEqual(bobDevices.count, 1, @"Alice must now know Bob's device keys"); - [expectation fulfill]; + [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); + // 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) { @@ -2685,87 +2565,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) { diff --git a/MatrixSDKTests/MatrixSDKTestsData.m b/MatrixSDKTests/MatrixSDKTestsData.m index 7d0b7b68bb..79e5521068 100644 --- a/MatrixSDKTests/MatrixSDKTestsData.m +++ b/MatrixSDKTests/MatrixSDKTestsData.m @@ -901,35 +901,32 @@ - (void)relogUserSessionWithNewDevice:(XCTestCase*)testCase { NSString *userId = session.matrixRestClient.credentials.userId; - [session enableCrypto:NO success:^{ + [session disableCrypto]; - [session close]; - - MXRestClient *mxRestClient = [[MXRestClient alloc] initWithHomeServer:kMXTestsHomeServerURL - andOnUnrecognizedCertificateBlock:nil]; - [self retain:mxRestClient]; + [session close]; - [mxRestClient loginWithLoginType:kMXLoginFlowTypePassword username:userId password:password success:^(MXCredentials *credentials) { + MXRestClient *mxRestClient = [[MXRestClient alloc] initWithHomeServer:kMXTestsHomeServerURL + andOnUnrecognizedCertificateBlock:nil]; + [self retain:mxRestClient]; - MXRestClient *mxRestClient2 = [[MXRestClient alloc] initWithCredentials:credentials andOnUnrecognizedCertificateBlock:nil]; - [self retain:mxRestClient2]; + [mxRestClient loginWithLoginType:kMXLoginFlowTypePassword username:userId password:password success:^(MXCredentials *credentials) { - MXSession *newSession = [[MXSession alloc] initWithMatrixRestClient:mxRestClient2]; - [self retain:newSession]; + MXRestClient *mxRestClient2 = [[MXRestClient alloc] initWithCredentials:credentials andOnUnrecognizedCertificateBlock:nil]; + [self retain:mxRestClient2]; - [newSession start:^{ + MXSession *newSession = [[MXSession alloc] initWithMatrixRestClient:mxRestClient2]; + [self retain:newSession]; - onComplete(newSession); + [newSession start:^{ - } failure:^(NSError *error) { - [self breakTestCase:testCase reason:@"Cannot set up intial test conditions - error: %@", error]; - }]; + onComplete(newSession); } failure:^(NSError *error) { - [self breakTestCase:testCase reason:@"Cannot relog %@. Error: %@", userId, error]; + [self breakTestCase:testCase reason:@"Cannot set up intial test conditions - error: %@", error]; }]; + } failure:^(NSError *error) { - [self breakTestCase:testCase reason:@"Cannot logout %@. Error: %@", userId, error]; + [self breakTestCase:testCase reason:@"Cannot relog %@. Error: %@", userId, error]; }]; } diff --git a/changelog.d/pr-1772.change b/changelog.d/pr-1772.change new file mode 100644 index 0000000000..7b85c1e3aa --- /dev/null +++ b/changelog.d/pr-1772.change @@ -0,0 +1 @@ +Crypto: Deprecate MXLegacyCrypto From 0b9001f33ce83d030cff02930571c6a64441c855 Mon Sep 17 00:00:00 2001 From: Andy Uhnak Date: Tue, 18 Apr 2023 11:42:35 +0100 Subject: [PATCH 3/4] Add crypto migration delegate --- MatrixSDK/Contrib/Swift/MXSession.swift | 18 ++ .../CrossSigning/MXCrossSigningV2.swift | 2 +- MatrixSDK/Crypto/MXCryptoV2Factory.swift | 12 +- MatrixSDK/MXSDKOptions.h | 11 +- MatrixSDK/MXSDKOptions.m | 2 +- MatrixSDK/MXSession.h | 12 +- MatrixSDK/MXSession.m | 72 ++++- MatrixSDKTests/MXCryptoTests.m | 287 +++++++++++++++--- MatrixSDKTests/MatrixSDKTestsData.m | 33 +- .../TestPlans/AllWorkingTests.xctestplan | 1 - 10 files changed, 371 insertions(+), 79 deletions(-) diff --git a/MatrixSDK/Contrib/Swift/MXSession.swift b/MatrixSDK/Contrib/Swift/MXSession.swift index 912332af48..e738b0e1e4 100644 --- a/MatrixSDK/Contrib/Swift/MXSession.swift +++ b/MatrixSDK/Contrib/Swift/MXSession.swift @@ -127,6 +127,24 @@ public extension MXSession { __setStore(store, success: currySuccess(completion), failure: curryFailure(completion)) } + + /** + Enable End-to-End encryption. + + In case of enabling, the operation will complete when the session will be ready + to make encrytion with other users devices + + - parameters: + - isEnabled: `false` stops crypto and erases crypto data. + - completion: A block called when the SDK has completed a catchup, or times out. + - response: Indicates whether the sync was successful. + */ + @nonobjc func enableCrypto(_ isEnabled: Bool, completion: @escaping (_ response: MXResponse) -> Void) { + __enableCrypto(isEnabled, success: currySuccess(completion), failure: curryFailure(completion)) + } + + + /// Create a new room with the given parameters. /// - Parameters: /// - name: Name of the room diff --git a/MatrixSDK/Crypto/CrossSigning/MXCrossSigningV2.swift b/MatrixSDK/Crypto/CrossSigning/MXCrossSigningV2.swift index 92eadb8cfb..e8ab628c8f 100644 --- a/MatrixSDK/Crypto/CrossSigning/MXCrossSigningV2.swift +++ b/MatrixSDK/Crypto/CrossSigning/MXCrossSigningV2.swift @@ -126,7 +126,7 @@ class MXCrossSigningV2: NSObject, MXCrossSigning { // If we are considered verified, there is no need for a verification upgrade // after migrating from legacy crypto if myUserCrossSigningKeys?.trustLevel.isVerified == true { - MXSDKOptions.sharedInstance().needsVerificationUpgrade = false + MXSDKOptions.sharedInstance().cryptoMigrationDelegate?.needsVerificationUpgrade = false } log.debug("Cross signing state refreshed, new state: \(state)") diff --git a/MatrixSDK/Crypto/MXCryptoV2Factory.swift b/MatrixSDK/Crypto/MXCryptoV2Factory.swift index 1843f49fc0..d910a1dc7a 100644 --- a/MatrixSDK/Crypto/MXCryptoV2Factory.swift +++ b/MatrixSDK/Crypto/MXCryptoV2Factory.swift @@ -16,6 +16,16 @@ import Foundation +/// Delegate for migrating account data from legacy crypto to rust-based Crypto SDK +@objc public protocol MXCryptoV2MigrationDelegate { + + /// Flag indicating whether this account requires a re-verification after migrating to Crypto SDK + /// + /// This flag is set to true if the legacy account is considered verified but the rust account + /// does not consider the migrated data secure enough, as it applies stricter security conditions. + var needsVerificationUpgrade: Bool { get set } +} + @objc public class MXCryptoV2Factory: NSObject { enum Error: Swift.Error { case cryptoNotAvailable @@ -139,7 +149,7 @@ import Foundation // unless the rust-based crypto already considers the current session to be verified given // the migration data log.debug("Needs verification upgrade") - MXSDKOptions.sharedInstance().needsVerificationUpgrade = true + MXSDKOptions.sharedInstance().cryptoMigrationDelegate?.needsVerificationUpgrade = true } } } diff --git a/MatrixSDK/MXSDKOptions.h b/MatrixSDK/MXSDKOptions.h index 778ab3d6d5..e4f82bdba1 100644 --- a/MatrixSDK/MXSDKOptions.h +++ b/MatrixSDK/MXSDKOptions.h @@ -48,7 +48,7 @@ typedef NS_ENUM(NSUInteger, MXCallTransferType) NS_ASSUME_NONNULL_BEGIN -@protocol MXBackgroundModeHandler, MXCryptoV2Feature; +@protocol MXBackgroundModeHandler, MXCryptoV2MigrationDelegate; /** SDK options that can be set at the launch time. @@ -204,14 +204,11 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic) BOOL enableRoomSharedHistoryOnInvite; /** - Flag indicating whether this account requires a re-verification after migrating to Crypto SDK + The delegate for migrating account data from legacy crypto to rust-based Crypto SDK - This flag is set to true if the legacy account is considered verified but the rust account - does not consider the migrated data secure enough, as it applies stricter security conditions. - - @remark NO by default. + By default, nil. */ -@property (nonatomic) BOOL needsVerificationUpgrade; +@property (nonatomic, nullable, weak) id cryptoMigrationDelegate; /** Enable symmetric room key backups diff --git a/MatrixSDK/MXSDKOptions.m b/MatrixSDK/MXSDKOptions.m index 0652f18fdc..d1f9f103ab 100644 --- a/MatrixSDK/MXSDKOptions.m +++ b/MatrixSDK/MXSDKOptions.m @@ -54,9 +54,9 @@ - (instancetype)init _authEnableRefreshTokens = NO; _enableThreads = NO; _enableRoomSharedHistoryOnInvite = NO; - _needsVerificationUpgrade = NO; _enableSymmetricBackup = NO; _enableNewClientInformationFeature = NO; + _cryptoMigrationDelegate = nil; } return self; diff --git a/MatrixSDK/MXSession.h b/MatrixSDK/MXSession.h index 31e274dc32..bfcb723119 100644 --- a/MatrixSDK/MXSession.h +++ b/MatrixSDK/MXSession.h @@ -776,9 +776,17 @@ typedef void (^MXOnBackgroundSyncFail)(NSError *error); - (void)enableVoIPWithCallStack:(id)callStack; /** - Disable End-to-End encryption. + Enable End-to-End encryption. + + In case of enabling, the operation will complete when the session will be ready + to make encrytion with other users devices + + @param enableCrypto NO stops crypto and erases crypto data. + + @param success A block object called when the operation succeeds. + @param failure A block object called when the operation fails. */ -- (void)disableCrypto; +- (void)enableCrypto:(BOOL)enableCrypto success:(void (^)(void))success failure:(void (^)(NSError *error))failure NS_REFINED_FOR_SWIFT; /** Get the versions of the specification supported by the server. diff --git a/MatrixSDK/MXSession.m b/MatrixSDK/MXSession.m index 2b0b16e362..2942cc4b36 100644 --- a/MatrixSDK/MXSession.m +++ b/MatrixSDK/MXSession.m @@ -1351,12 +1351,25 @@ - (void)close - (MXHTTPOperation*)logout:(void (^)(void))success failure:(void (^)(NSError *error))failure { + // Create an empty operation that will be mutated later + MXHTTPOperation *operation = [[MXHTTPOperation alloc] init]; + // Clear crypto data // For security and because it will be no more useful as we will get a new device id // on the next log in - [self disableCrypto]; - - return [self.matrixRestClient logout:success failure:failure]; + MXWeakify(self); + [self enableCrypto:NO success:^{ + MXStrongifyAndReturnIfNil(self); + + if (!operation.isCancelled) + { + MXHTTPOperation *operation2 = [self.matrixRestClient logout:success failure:failure]; + [operation mutateTo:operation2]; + } + + } failure:nil]; + + return operation; } - (MXHTTPOperation*)deactivateAccountWithAuthParameters:(NSDictionary*)authParameters @@ -2161,15 +2174,62 @@ - (void)enableVoIPWithCallStack:(id)callStack _callManager = [[MXCallManager alloc] initWithMatrixSession:self andCallStack:callStack]; } -- (void)disableCrypto +- (void)enableCrypto:(BOOL)enableCrypto success:(void (^)(void))success failure:(void (^)(NSError *))failure { - MXLogDebug(@"[MXSesion] disableCrypto"); + MXLogDebug(@"[MXSesion] enableCrypto: %@", @(enableCrypto)); - if (_crypto) + if (enableCrypto && !_crypto) + { + MXWeakify(self); + [MXCryptoV2Factory.shared buildCryptoWithSession:self + migrationProgress:nil + success:^(id crypto) { + + MXLogDebug(@"[MXSession] enableCrypto: Successfully initialized crypto module"); + MXStrongifyAndReturnIfNil(self); + self->_crypto = crypto; + + if (self->_state == MXSessionStateRunning) + { + [self startCrypto:success failure:failure]; + } + else + { + MXLogDebug(@"[MXSesion] enableCrypto: crypto module will be start later (MXSession.state: %@)", [MXTools readableSessionState:self->_state]); + + if (success) + { + dispatch_async(dispatch_get_main_queue(), ^{ + success(); + }); + } + } + + } failure:^(NSError *error) { + MXLogErrorDetails(@"[MXSession] enableCrypto: Error initialized crypto module", error); + if (failure) + { + failure(error); + } + }]; + } + else if (!enableCrypto && _crypto) { // Erase all crypto data of this user [_crypto close:YES]; _crypto = nil; + + if (success) + { + success(); + } + } + else + { + if (success) + { + success(); + } } } diff --git a/MatrixSDKTests/MXCryptoTests.m b/MatrixSDKTests/MXCryptoTests.m index 4021b939b7..36b50f5138 100644 --- a/MatrixSDKTests/MXCryptoTests.m +++ b/MatrixSDKTests/MXCryptoTests.m @@ -105,6 +105,35 @@ - (NSUInteger)checkEncryptedEvent:(MXEvent*)event roomId:(NSString*)roomId clear #pragma mark - MXCrypto +- (void)testEnableCrypto +{ + [matrixSDKTestsData doMXSessionTestWithBob:self readyToTest:^(MXSession *mxSession, XCTestExpectation *expectation) { + + XCTAssertNil(mxSession.crypto, @"Crypto is disabled by default"); + + MXKeyProvider.sharedInstance.delegate = [[MXKeyProviderStub alloc] init]; + [mxSession enableCrypto:YES success:^{ + MXKeyProvider.sharedInstance.delegate = nil; + + XCTAssert(mxSession.crypto); + + [mxSession enableCrypto:NO success:^{ + + XCTAssertNil(mxSession.crypto); + [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]; + }]; + }]; +} + - (void)testMXSDKOptionsEnableCryptoWhenOpeningMXSession { [MXSDKOptions sharedInstance].enableCryptoWhenStartingMXSession = YES; @@ -117,11 +146,89 @@ - (void)testMXSDKOptionsEnableCryptoWhenOpeningMXSession XCTAssert(mxSession.crypto); - [mxSession disableCrypto]; + [mxSession enableCrypto:NO success:^{ - XCTAssertNil(mxSession.crypto); + XCTAssertNil(mxSession.crypto); + [expectation fulfill]; - [expectation fulfill]; + } failure:^(NSError *error) { + XCTFail(@"The request should not fail - NSError: %@", error); + [expectation fulfill]; + }]; + + }]; +} + +- (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]; + }]; }]; } @@ -2207,39 +2314,44 @@ - (void)testLeftAndJoinedBob // Make Bob come back to the room with a new device // Clear his crypto store - [bobSession disableCrypto]; + [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) { + // 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; + [MXSDKOptions sharedInstance].enableCryptoWhenStartingMXSession = NO; - [bobSession2 joinRoom:roomFromAlicePOV.roomId viaServers:nil success:^(MXRoom *roomFromBobPOV2) { + [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) { + // 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"); + 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]); + XCTAssertEqual(0, [self checkEncryptedEvent:event roomId:roomFromBobPOV2.roomId clearMessage:message2FromAlice senderSession:aliceSession]); - [expectation fulfill]; + [expectation fulfill]; + }]; + }]; + + [roomFromAlicePOV sendTextMessage:message2FromAlice threadId:nil success:nil failure:^(NSError *error) { + XCTFail(@"Cannot set up intial test conditions - error: %@", error); + [expectation fulfill]; }]; - }]; - [roomFromAlicePOV sendTextMessage:message2FromAlice threadId:nil success:nil failure:^(NSError *error) { + } 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) { @@ -2381,47 +2493,51 @@ - (void)testEnableEncryptionAfterNonCryptedMessages // Make Bob come back to the room with a new device // Clear his crypto store - [bobSession disableCrypto]; + [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) { + // 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; + [MXSDKOptions sharedInstance].enableCryptoWhenStartingMXSession = NO; - MXRoom *roomFromNewBobPOV = [newBobSession roomWithRoomId:roomFromAlicePOV.roomId]; + 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"); + 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:^{ + // 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) { + [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"); + 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]); + 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"); + NSDictionary *bobDevices = [aliceSession.legacyCrypto.store devicesForUser:newBobSession.myUser.userId]; + XCTAssertEqual(bobDevices.count, 1, @"Alice must now know Bob's device keys"); - [expectation fulfill]; + [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); + // 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) { @@ -2565,6 +2681,87 @@ - (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) { diff --git a/MatrixSDKTests/MatrixSDKTestsData.m b/MatrixSDKTests/MatrixSDKTestsData.m index 79e5521068..7d0b7b68bb 100644 --- a/MatrixSDKTests/MatrixSDKTestsData.m +++ b/MatrixSDKTests/MatrixSDKTestsData.m @@ -901,32 +901,35 @@ - (void)relogUserSessionWithNewDevice:(XCTestCase*)testCase { NSString *userId = session.matrixRestClient.credentials.userId; - [session disableCrypto]; + [session enableCrypto:NO success:^{ - [session close]; + [session close]; - MXRestClient *mxRestClient = [[MXRestClient alloc] initWithHomeServer:kMXTestsHomeServerURL - andOnUnrecognizedCertificateBlock:nil]; - [self retain:mxRestClient]; + MXRestClient *mxRestClient = [[MXRestClient alloc] initWithHomeServer:kMXTestsHomeServerURL + andOnUnrecognizedCertificateBlock:nil]; + [self retain:mxRestClient]; - [mxRestClient loginWithLoginType:kMXLoginFlowTypePassword username:userId password:password success:^(MXCredentials *credentials) { + [mxRestClient loginWithLoginType:kMXLoginFlowTypePassword username:userId password:password success:^(MXCredentials *credentials) { - MXRestClient *mxRestClient2 = [[MXRestClient alloc] initWithCredentials:credentials andOnUnrecognizedCertificateBlock:nil]; - [self retain:mxRestClient2]; + MXRestClient *mxRestClient2 = [[MXRestClient alloc] initWithCredentials:credentials andOnUnrecognizedCertificateBlock:nil]; + [self retain:mxRestClient2]; - MXSession *newSession = [[MXSession alloc] initWithMatrixRestClient:mxRestClient2]; - [self retain:newSession]; + MXSession *newSession = [[MXSession alloc] initWithMatrixRestClient:mxRestClient2]; + [self retain:newSession]; + + [newSession start:^{ - [newSession start:^{ + onComplete(newSession); - onComplete(newSession); + } failure:^(NSError *error) { + [self breakTestCase:testCase reason:@"Cannot set up intial test conditions - error: %@", error]; + }]; } failure:^(NSError *error) { - [self breakTestCase:testCase reason:@"Cannot set up intial test conditions - error: %@", error]; + [self breakTestCase:testCase reason:@"Cannot relog %@. Error: %@", userId, error]; }]; - } failure:^(NSError *error) { - [self breakTestCase:testCase reason:@"Cannot relog %@. Error: %@", userId, error]; + [self breakTestCase:testCase reason:@"Cannot logout %@. Error: %@", userId, error]; }]; } diff --git a/MatrixSDKTests/TestPlans/AllWorkingTests.xctestplan b/MatrixSDKTests/TestPlans/AllWorkingTests.xctestplan index 61e671924d..711f37fbce 100644 --- a/MatrixSDKTests/TestPlans/AllWorkingTests.xctestplan +++ b/MatrixSDKTests/TestPlans/AllWorkingTests.xctestplan @@ -82,7 +82,6 @@ "MXCryptoTests\/testDiscardAndRestoreOlmOutboundKey", "MXCryptoTests\/testDownloadKeysForUserWithNoDevice", "MXCryptoTests\/testDownloadKeysWithUnreachableHS", - "MXCryptoTests\/testEnableCrypto", "MXCryptoTests\/testEnableEncryptionAfterNonCryptedMessages", "MXCryptoTests\/testEncryptionAlgorithmChange", "MXCryptoTests\/testEnsureSingleOlmSession", From 485490eba7330233a748e51268d39a093a2eb642 Mon Sep 17 00:00:00 2001 From: Andy Uhnak Date: Wed, 19 Apr 2023 14:13:27 +0100 Subject: [PATCH 4/4] One more legacy crypto use --- MatrixSDK/Data/MXRoom.m | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/MatrixSDK/Data/MXRoom.m b/MatrixSDK/Data/MXRoom.m index 020af712d3..b626989f0b 100644 --- a/MatrixSDK/Data/MXRoom.m +++ b/MatrixSDK/Data/MXRoom.m @@ -82,11 +82,6 @@ The list of room operations (sending of text, images...) that must be sent FIFO queue of failure blocks waiting for [self members:]. */ NSMutableArray *pendingMembersFailureBlocks; - - /** - The manager for sharing keys of messages with invited users - */ - MXSharedHistoryKeyManager *sharedHistoryKeyManager; } @end @@ -123,14 +118,6 @@ - (id)initWithRoomId:(NSString *)roomId matrixSession:(MXSession *)mxSession2 an { _roomId = roomId; mxSession = mxSession2; - - if ([mxSession.crypto isKindOfClass:[MXLegacyCrypto class]]) - { - MXMegolmDecryption *decryption = [[MXMegolmDecryption alloc] initWithCrypto:mxSession.crypto]; - sharedHistoryKeyManager = [[MXSharedHistoryKeyManager alloc] initWithRoomId:roomId - crypto:mxSession.crypto - service:decryption]; - } if (store) { @@ -1977,24 +1964,9 @@ - (MXHTTPOperation*)inviteUser:(NSString*)userId success:(void (^)(void))success failure:(void (^)(NSError *error))failure { - if (MXSDKOptions.sharedInstance.enableRoomSharedHistoryOnInvite) - { - [self shareRoomKeysWith:userId]; - } return [mxSession.matrixRestClient inviteUser:userId toRoom:self.roomId success:success failure:failure]; } -- (void)shareRoomKeysWith:(NSString *)userId -{ - // The value of 20 is arbitrary and imprecise, we merely want to ensure that when a user is invited to a room - // they are able to read any immediately preciding messages that may be relevant to the invite. - NSInteger numberOfSharedMessage = 20; - id enumerator = [self enumeratorForStoredMessagesWithTypeIn:@[kMXEventTypeStringRoomMessage]]; - [sharedHistoryKeyManager shareMessageKeysWithUserId:userId - messageEnumerator:enumerator - limit:numberOfSharedMessage]; -} - - (MXHTTPOperation*)inviteUserByEmail:(NSString*)email success:(void (^)(void))success failure:(void (^)(NSError *error))failure