diff --git a/MatrixSDK.xcodeproj/project.pbxproj b/MatrixSDK.xcodeproj/project.pbxproj index 0ec85e44d4..a02098afc8 100644 --- a/MatrixSDK.xcodeproj/project.pbxproj +++ b/MatrixSDK.xcodeproj/project.pbxproj @@ -1408,8 +1408,6 @@ EC0B941227184E8A00B4D440 /* MXRoomSummaryMO.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC0B940927184E8A00B4D440 /* MXRoomSummaryMO.swift */; }; EC0B941327184E8A00B4D440 /* MXRoomMembersCountMO.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC0B940A27184E8A00B4D440 /* MXRoomMembersCountMO.swift */; }; EC0B941427184E8A00B4D440 /* MXRoomMembersCountMO.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC0B940A27184E8A00B4D440 /* MXRoomMembersCountMO.swift */; }; - EC0B941527184E8A00B4D440 /* MXCoreDataRoomSummaryStore.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = EC0B940B27184E8A00B4D440 /* MXCoreDataRoomSummaryStore.xcdatamodeld */; }; - EC0B941627184E8A00B4D440 /* MXCoreDataRoomSummaryStore.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = EC0B940B27184E8A00B4D440 /* MXCoreDataRoomSummaryStore.xcdatamodeld */; }; EC0B9418271855CA00B4D440 /* MXCoreDataRoomSummaryStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC0B9417271855C900B4D440 /* MXCoreDataRoomSummaryStore.swift */; }; EC0B9419271855CA00B4D440 /* MXCoreDataRoomSummaryStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC0B9417271855C900B4D440 /* MXCoreDataRoomSummaryStore.swift */; }; EC0B941B2718654B00B4D440 /* NSManagedObject+MatrixSDK.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC0B941A2718654B00B4D440 /* NSManagedObject+MatrixSDK.swift */; }; @@ -1737,6 +1735,14 @@ ECF29BDF264195320053E6D6 /* MXAssertedIdentityModel.h in Headers */ = {isa = PBXBuildFile; fileRef = ECF29BDD264195320053E6D6 /* MXAssertedIdentityModel.h */; settings = {ATTRIBUTES = (Public, ); }; }; ECF29BE52641953C0053E6D6 /* MXAssertedIdentityModel.m in Sources */ = {isa = PBXBuildFile; fileRef = ECF29BE42641953C0053E6D6 /* MXAssertedIdentityModel.m */; }; ECF29BE62641953C0053E6D6 /* MXAssertedIdentityModel.m in Sources */ = {isa = PBXBuildFile; fileRef = ECF29BE42641953C0053E6D6 /* MXAssertedIdentityModel.m */; }; + ED21F68528104DA2002FF83D /* MXMegolmEncryptionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED21F68428104DA2002FF83D /* MXMegolmEncryptionTests.swift */; }; + ED21F68628104DA2002FF83D /* MXMegolmEncryptionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED21F68428104DA2002FF83D /* MXMegolmEncryptionTests.swift */; }; + ED35652C281150310002BF6A /* MXOlmInboundGroupSessionUnitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED35652B281150310002BF6A /* MXOlmInboundGroupSessionUnitTests.swift */; }; + ED35652D281150310002BF6A /* MXOlmInboundGroupSessionUnitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED35652B281150310002BF6A /* MXOlmInboundGroupSessionUnitTests.swift */; }; + ED35652F281153480002BF6A /* MXMegolmSessionDataUnitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED35652E281153480002BF6A /* MXMegolmSessionDataUnitTests.swift */; }; + ED356530281153480002BF6A /* MXMegolmSessionDataUnitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED35652E281153480002BF6A /* MXMegolmSessionDataUnitTests.swift */; }; + ED5AE8C52816C8CF00105072 /* MXCoreDataRoomSummaryStore.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = ED5AE8C22816C8CF00105072 /* MXCoreDataRoomSummaryStore.xcdatamodeld */; }; + ED5AE8C62816C8CF00105072 /* MXCoreDataRoomSummaryStore.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = ED5AE8C22816C8CF00105072 /* MXCoreDataRoomSummaryStore.xcdatamodeld */; }; ED88999127F2065D00718486 /* MXRoomAliasResolution.h in Headers */ = {isa = PBXBuildFile; fileRef = ED88998F27F2065C00718486 /* MXRoomAliasResolution.h */; settings = {ATTRIBUTES = (Public, ); }; }; ED88999227F2065D00718486 /* MXRoomAliasResolution.h in Headers */ = {isa = PBXBuildFile; fileRef = ED88998F27F2065C00718486 /* MXRoomAliasResolution.h */; settings = {ATTRIBUTES = (Public, ); }; }; ED88999327F2065D00718486 /* MXRoomAliasResolution.m in Sources */ = {isa = PBXBuildFile; fileRef = ED88999027F2065D00718486 /* MXRoomAliasResolution.m */; }; @@ -2539,7 +2545,6 @@ EC0B940827184E8A00B4D440 /* MXUsersTrustLevelSummaryMO.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MXUsersTrustLevelSummaryMO.swift; sourceTree = ""; }; EC0B940927184E8A00B4D440 /* MXRoomSummaryMO.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MXRoomSummaryMO.swift; sourceTree = ""; }; EC0B940A27184E8A00B4D440 /* MXRoomMembersCountMO.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MXRoomMembersCountMO.swift; sourceTree = ""; }; - EC0B940C27184E8A00B4D440 /* MXRoomSummaryCoreDataStore.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MXRoomSummaryCoreDataStore.xcdatamodel; sourceTree = ""; }; EC0B9417271855C900B4D440 /* MXCoreDataRoomSummaryStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MXCoreDataRoomSummaryStore.swift; sourceTree = ""; }; EC0B941A2718654B00B4D440 /* NSManagedObject+MatrixSDK.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSManagedObject+MatrixSDK.swift"; sourceTree = ""; }; EC0B941D27186C3500B4D440 /* MXRoomListDataSortable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXRoomListDataSortable.swift; sourceTree = ""; }; @@ -2720,7 +2725,12 @@ ECF29BD2264194BB0053E6D6 /* MXCallAssertedIdentityEventContent.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXCallAssertedIdentityEventContent.m; sourceTree = ""; }; ECF29BDD264195320053E6D6 /* MXAssertedIdentityModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXAssertedIdentityModel.h; sourceTree = ""; }; ECF29BE42641953C0053E6D6 /* MXAssertedIdentityModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXAssertedIdentityModel.m; sourceTree = ""; }; + ED21F68428104DA2002FF83D /* MXMegolmEncryptionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXMegolmEncryptionTests.swift; sourceTree = ""; }; ED2F344856EFFCA383E37B22 /* Pods-SDK-MatrixSDK.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SDK-MatrixSDK.release.xcconfig"; path = "Target Support Files/Pods-SDK-MatrixSDK/Pods-SDK-MatrixSDK.release.xcconfig"; sourceTree = ""; }; + ED35652B281150310002BF6A /* MXOlmInboundGroupSessionUnitTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXOlmInboundGroupSessionUnitTests.swift; sourceTree = ""; }; + ED35652E281153480002BF6A /* MXMegolmSessionDataUnitTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXMegolmSessionDataUnitTests.swift; sourceTree = ""; }; + ED5AE8C32816C8CF00105072 /* MXRoomSummaryCoreDataStore2.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MXRoomSummaryCoreDataStore2.xcdatamodel; sourceTree = ""; }; + ED5AE8C42816C8CF00105072 /* MXRoomSummaryCoreDataStore.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MXRoomSummaryCoreDataStore.xcdatamodel; sourceTree = ""; }; ED88998F27F2065C00718486 /* MXRoomAliasResolution.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXRoomAliasResolution.h; sourceTree = ""; }; ED88999027F2065D00718486 /* MXRoomAliasResolution.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXRoomAliasResolution.m; sourceTree = ""; }; ED8943D327E34762000FC39C /* MXMemoryRoomStoreTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXMemoryRoomStoreTests.swift; sourceTree = ""; }; @@ -3839,6 +3849,7 @@ 32C6F93919DD814400EA4E9C /* MatrixSDKTests */ = { isa = PBXGroup; children = ( + ED21F67A28104B9A002FF83D /* Crypto */, EDB4209727DF841A0036AF39 /* JSONModels */, EDB4208E27DF76C60036AF39 /* Data */, 322985C526FA66FD001890BC /* Utils */, @@ -4420,7 +4431,7 @@ isa = PBXGroup; children = ( EC0B9417271855C900B4D440 /* MXCoreDataRoomSummaryStore.swift */, - EC0B940B27184E8A00B4D440 /* MXCoreDataRoomSummaryStore.xcdatamodeld */, + ED5AE8C22816C8CF00105072 /* MXCoreDataRoomSummaryStore.xcdatamodeld */, EC0B941A2718654B00B4D440 /* NSManagedObject+MatrixSDK.swift */, EC0B940627184E8A00B4D440 /* Models */, ); @@ -4799,6 +4810,40 @@ path = Common; sourceTree = ""; }; + ED21F67A28104B9A002FF83D /* Crypto */ = { + isa = PBXGroup; + children = ( + ED35652A281150230002BF6A /* Data */, + ED21F67B28104BA1002FF83D /* Algorithms */, + ); + path = Crypto; + sourceTree = ""; + }; + ED21F67B28104BA1002FF83D /* Algorithms */ = { + isa = PBXGroup; + children = ( + ED21F67C28104BA7002FF83D /* Megolm */, + ); + path = Algorithms; + sourceTree = ""; + }; + ED21F67C28104BA7002FF83D /* Megolm */ = { + isa = PBXGroup; + children = ( + ED21F68428104DA2002FF83D /* MXMegolmEncryptionTests.swift */, + ); + path = Megolm; + sourceTree = ""; + }; + ED35652A281150230002BF6A /* Data */ = { + isa = PBXGroup; + children = ( + ED35652B281150310002BF6A /* MXOlmInboundGroupSessionUnitTests.swift */, + ED35652E281153480002BF6A /* MXMegolmSessionDataUnitTests.swift */, + ); + path = Data; + sourceTree = ""; + }; ED8943D127E3474A000FC39C /* Store */ = { isa = PBXGroup; children = ( @@ -6092,7 +6137,6 @@ EC1848C52685F64D00865E16 /* MXiOSAudioOutputRoute.swift in Sources */, EC60EE08265CFFF400B39A4E /* MXGroupSyncProfile.m in Sources */, B19A30A0240424BD00FB6F35 /* MXQRCodeTransaction.m in Sources */, - EC0B941527184E8A00B4D440 /* MXCoreDataRoomSummaryStore.xcdatamodeld in Sources */, C63E78B01F26588000AC692F /* MXRoomPowerLevels.swift in Sources */, EC0B943F271DB68F00B4D440 /* MXVoidRoomSummaryStore.m in Sources */, EC383BA5253DE6C9002FBBE6 /* MXSyncResponseStore.swift in Sources */, @@ -6236,6 +6280,7 @@ EC1848C72686174E00865E16 /* MXiOSAudioOutputRouteType.swift in Sources */, 3220093919EFA4C9008DE41D /* MXEventListener.m in Sources */, EC5C562A27A36EDB0014CBE9 /* MXInReplyTo.m in Sources */, + ED5AE8C52816C8CF00105072 /* MXCoreDataRoomSummaryStore.xcdatamodeld in Sources */, C6481AF21F1678A9000DB8A0 /* MXSessionEventListener.swift in Sources */, 32A1514F1DAF897600400192 /* MXOlmSessionResult.m in Sources */, ECF29BD3264194BB0053E6D6 /* MXCallAssertedIdentityEventContent.m in Sources */, @@ -6308,6 +6353,7 @@ 32832B5E1BCC048300241108 /* MXStoreNoStoreTests.m in Sources */, A816247C25F60C7700A46F05 /* MXDeviceListOperationsPoolUnitTests.swift in Sources */, B1660F1C260A20B900C3AA12 /* MXSpaceServiceTest.swift in Sources */, + ED35652C281150310002BF6A /* MXOlmInboundGroupSessionUnitTests.swift in Sources */, 32C9B71823E81A1C00C6F30A /* MXCrossSigningVerificationTests.m in Sources */, 323C5A081A70E53500FB0549 /* MXToolsUnitTests.m in Sources */, 3281E89E19E299C000976E1A /* MXErrorUnitTests.m in Sources */, @@ -6365,10 +6411,12 @@ 32D8CAC219DEE6ED002AF8A0 /* MXRestClientNoAuthAPITests.m in Sources */, 32FCAB4D19E578860049C555 /* MXRestClientTests.m in Sources */, 32C78BA7256D227D008130B1 /* MXCryptoMigrationTests.m in Sources */, + ED21F68528104DA2002FF83D /* MXMegolmEncryptionTests.swift in Sources */, 322985CB26FAF898001890BC /* MXSession.swift in Sources */, EC131B192779D8D500712964 /* MXThreadEventTimelineUnitTests.swift in Sources */, B135067427EB201E00BD3276 /* MXLocationServiceTests.swift in Sources */, 18C26C4F273C0EB300805154 /* MXPollAggregatorTests.swift in Sources */, + ED35652F281153480002BF6A /* MXMegolmSessionDataUnitTests.swift in Sources */, 32EEA83F2603CA140041425B /* MXRestClientExtensionsTests.m in Sources */, 18121F7A273E6E4200B68ADF /* PollBuilder.swift in Sources */, 18121F7F273E837300B68ADF /* PollModels.swift in Sources */, @@ -6620,7 +6668,6 @@ B14EF23D2397E90400758AF0 /* MXCallManager.m in Sources */, B14EF23E2397E90400758AF0 /* MXPeekingRoom.m in Sources */, B14EF23F2397E90400758AF0 /* MXEncryptedContentFile.m in Sources */, - EC0B941627184E8A00B4D440 /* MXCoreDataRoomSummaryStore.xcdatamodeld in Sources */, EC8A539625B1BC77004E0802 /* MXUserModel.m in Sources */, EC0B9440271DB68F00B4D440 /* MXVoidRoomSummaryStore.m in Sources */, B14EF2402397E90400758AF0 /* MXSessionEventListener.m in Sources */, @@ -6765,6 +6812,7 @@ ECCA02BF27348FE300B6F34F /* MXThread.swift in Sources */, 320B3937239FA56900BE2C06 /* MXKeyVerificationByDMRequest.m in Sources */, B14EF2832397E90400758AF0 /* MXRoomCreateContent.m in Sources */, + ED5AE8C62816C8CF00105072 /* MXCoreDataRoomSummaryStore.xcdatamodeld in Sources */, EC5C562B27A36EDB0014CBE9 /* MXInReplyTo.m in Sources */, EC8A53B025B1BC77004E0802 /* MXCallAnswerEventContent.m in Sources */, EC60ED7E265CFCD100B39A4E /* MXDeviceListResponse.m in Sources */, @@ -6837,6 +6885,7 @@ 32C9B71923E81A1C00C6F30A /* MXCrossSigningVerificationTests.m in Sources */, B1E09A1D2397FCE90057C069 /* MXCryptoKeyVerificationTests.m in Sources */, B1E09A472397FD990057C069 /* MXEventScanStoreUnitTests.m in Sources */, + ED35652D281150310002BF6A /* MXOlmInboundGroupSessionUnitTests.swift in Sources */, B1E09A3D2397FD820057C069 /* MXStoreFileStoreTests.m in Sources */, 32CEEF3E23AD134A0039BA98 /* MXCrossSigningTests.m in Sources */, 32EEA8402603CA140041425B /* MXRestClientExtensionsTests.m in Sources */, @@ -6894,10 +6943,12 @@ 32B477902638133D00EA5800 /* MXAggregatedReferenceUnitTests.m in Sources */, EC116598270FCA8B0089FA56 /* MXBackgroundTaskUnitTests.swift in Sources */, B1E09A322397FD750057C069 /* MXRoomTests.m in Sources */, + ED21F68628104DA2002FF83D /* MXMegolmEncryptionTests.swift in Sources */, 322985CC26FAF898001890BC /* MXSession.swift in Sources */, EC131B1A2779D8D500712964 /* MXThreadEventTimelineUnitTests.swift in Sources */, B135067527EB201E00BD3276 /* MXLocationServiceTests.swift in Sources */, 18C26C50273C0EB400805154 /* MXPollAggregatorTests.swift in Sources */, + ED356530281153480002BF6A /* MXMegolmSessionDataUnitTests.swift in Sources */, 32C78BA8256D227D008130B1 /* MXCryptoMigrationTests.m in Sources */, 18121F7B273E6E4200B68ADF /* PollBuilder.swift in Sources */, 18121F80273E837400B68ADF /* PollModels.swift in Sources */, @@ -7329,12 +7380,13 @@ /* End XCConfigurationList section */ /* Begin XCVersionGroup section */ - EC0B940B27184E8A00B4D440 /* MXCoreDataRoomSummaryStore.xcdatamodeld */ = { + ED5AE8C22816C8CF00105072 /* MXCoreDataRoomSummaryStore.xcdatamodeld */ = { isa = XCVersionGroup; children = ( - EC0B940C27184E8A00B4D440 /* MXRoomSummaryCoreDataStore.xcdatamodel */, + ED5AE8C32816C8CF00105072 /* MXRoomSummaryCoreDataStore2.xcdatamodel */, + ED5AE8C42816C8CF00105072 /* MXRoomSummaryCoreDataStore.xcdatamodel */, ); - currentVersion = EC0B940C27184E8A00B4D440 /* MXRoomSummaryCoreDataStore.xcdatamodel */; + currentVersion = ED5AE8C32816C8CF00105072 /* MXRoomSummaryCoreDataStore2.xcdatamodel */; path = MXCoreDataRoomSummaryStore.xcdatamodeld; sourceTree = ""; versionGroupType = wrapper.xcdatamodel; diff --git a/MatrixSDK/Background/MXBackgroundSyncService.swift b/MatrixSDK/Background/MXBackgroundSyncService.swift index a0912d1f4b..1b37f240ee 100644 --- a/MatrixSDK/Background/MXBackgroundSyncService.swift +++ b/MatrixSDK/Background/MXBackgroundSyncService.swift @@ -585,7 +585,14 @@ public enum MXBackgroundSyncServiceError: Error { senderKey: senderKey, forwardingCurve25519KeyChain: forwardingKeyChain, keysClaimed: keysClaimed, - exportFormat: exportFormat) + exportFormat: exportFormat, + sharedHistory: isRoomSharingHistory(roomId: roomId)) + } + + private func isRoomSharingHistory(roomId: String) -> Bool { + let summary = roomSummary(forRoomId: roomId) + let visibility = MXRoomHistoryVisibility(identifier: summary?.historyVisibility) + return visibility == .worldReadable || visibility == .shared } private func updateBackgroundServiceStoresIfNeeded() { diff --git a/MatrixSDK/Crypto/Algorithms/Megolm/MXMegolmDecryption.m b/MatrixSDK/Crypto/Algorithms/Megolm/MXMegolmDecryption.m index 3435d43d67..9d58b25e05 100644 --- a/MatrixSDK/Crypto/Algorithms/Megolm/MXMegolmDecryption.m +++ b/MatrixSDK/Crypto/Algorithms/Megolm/MXMegolmDecryption.m @@ -254,7 +254,15 @@ - (void)onRoomKeyEvent:(MXEvent *)event MXLogDebug(@"[MXMegolmDecryption] onRoomKeyEvent: Adding key for megolm session %@|%@ from %@ event", senderKey, sessionId, event.type); - [olmDevice addInboundGroupSession:sessionId sessionKey:sessionKey roomId:roomId senderKey:senderKey forwardingCurve25519KeyChain:forwardingKeyChain keysClaimed:keysClaimed exportFormat:exportFormat]; + BOOL sharedHistory = [crypto isRoomSharingHistory:roomId]; + [olmDevice addInboundGroupSession:sessionId + sessionKey:sessionKey + roomId:roomId + senderKey:senderKey + forwardingCurve25519KeyChain:forwardingKeyChain + keysClaimed:keysClaimed + exportFormat:exportFormat + sharedHistory:sharedHistory]; [crypto.backup maybeSendKeyBackup]; diff --git a/MatrixSDK/Crypto/Algorithms/Megolm/MXMegolmEncryption.m b/MatrixSDK/Crypto/Algorithms/Megolm/MXMegolmEncryption.m index 868fc27355..b79af12b65 100644 --- a/MatrixSDK/Crypto/Algorithms/Megolm/MXMegolmEncryption.m +++ b/MatrixSDK/Crypto/Algorithms/Megolm/MXMegolmEncryption.m @@ -147,6 +147,15 @@ - (MXOutboundSessionInfo *)outboundSession return outboundSession; } +- (BOOL)isSessionSharingHistory:(MXOutboundSessionInfo *)session +{ + // We only store the `sharedHistory` flag on inbound sessions. To see if the current outbound session shares history + // see a corresponding inbound session with the same identifier. + MXOlmInboundGroupSession *matchingInboundSession = [crypto.store inboundGroupSessionWithId:session.sessionId + andSenderKey:crypto.olmDevice.deviceCurve25519Key]; + return matchingInboundSession.sharedHistory; +} + /* Get the list of devices which can encrypt data to. @@ -241,15 +250,7 @@ - (MXHTTPOperation *)ensureOutboundSession:(MXUsersDevicesMap *) { __block MXOutboundSessionInfo *session = self.outboundSession; - // Need to make a brand new session? - if (session && [session needsRotation:sessionRotationPeriodMsgs rotationPeriodMs:sessionRotationPeriodMs]) - { - [crypto.olmDevice discardOutboundGroupSessionForRoomWithRoomId:roomId]; - session = nil; - } - - // Determine if we have shared with anyone we shouldn't have - if (session && [session sharedWithTooManyDevices:devicesInRoom]) + if (session && [self shouldResetSession:session devicesInRoom:devicesInRoom]) { [crypto.olmDevice discardOutboundGroupSessionForRoomWithRoomId:roomId]; session = nil; @@ -301,10 +302,33 @@ - (MXHTTPOperation *)ensureOutboundSession:(MXUsersDevicesMap *) return session.shareOperation; } +- (BOOL)shouldResetSession:(MXOutboundSessionInfo *)session devicesInRoom:(MXUsersDevicesMap *)devicesInRoom +{ + // Need to make a brand new session? + if ([session needsRotation:sessionRotationPeriodMsgs rotationPeriodMs:sessionRotationPeriodMs]) + { + return YES; + } + + // Determine if we have shared with anyone we shouldn't have + else if ([session sharedWithTooManyDevices:devicesInRoom]) + { + return YES; + } + + // Check if room's history visibility has changed + else if ([crypto isRoomSharingHistory:roomId] != [self isSessionSharingHistory:session]) + { + return YES; + } + return NO; +} + - (MXOutboundSessionInfo*)prepareNewSession { MXOlmOutboundGroupSession *session = [crypto.olmDevice createOutboundGroupSessionForRoomWithRoomId:roomId]; + BOOL sharedHistory = [crypto isRoomSharingHistory:roomId]; [crypto.olmDevice addInboundGroupSession:session.sessionId sessionKey:session.sessionKey roomId:roomId @@ -314,6 +338,7 @@ - (MXOutboundSessionInfo*)prepareNewSession @"ed25519": crypto.olmDevice.deviceEd25519Key } exportFormat:NO + sharedHistory:sharedHistory ]; [crypto.backup maybeSendKeyBackup]; diff --git a/MatrixSDK/Crypto/Data/MXMegolmSessionData.h b/MatrixSDK/Crypto/Data/MXMegolmSessionData.h index 9f0dfbdb61..3fa74cf60d 100644 --- a/MatrixSDK/Crypto/Data/MXMegolmSessionData.h +++ b/MatrixSDK/Crypto/Data/MXMegolmSessionData.h @@ -54,6 +54,14 @@ */ @property NSString *sessionKey; +/** + Flag indicating whether the history of this room is considered as shared. + + This is typically the case if room's `historyVisibility` is set to `world_readable` or `shared`. + In this case the keys are allowed to be shared with other users upon invite. + */ +@property BOOL sharedHistory; + /** The algorithm used. */ diff --git a/MatrixSDK/Crypto/Data/MXMegolmSessionData.m b/MatrixSDK/Crypto/Data/MXMegolmSessionData.m index 9b334fcd32..380627546d 100644 --- a/MatrixSDK/Crypto/Data/MXMegolmSessionData.m +++ b/MatrixSDK/Crypto/Data/MXMegolmSessionData.m @@ -28,6 +28,7 @@ + (id)modelFromJSON:(NSDictionary *)JSONDictionary MXJSONModelSetString(sessionData.roomId, JSONDictionary[@"room_id"]); MXJSONModelSetString(sessionData.sessionId, JSONDictionary[@"session_id"]); MXJSONModelSetString(sessionData.sessionKey, JSONDictionary[@"session_key"]); + MXJSONModelSetBoolean(sessionData.sharedHistory, JSONDictionary[@"shared_history"]); MXJSONModelSetString(sessionData.algorithm, JSONDictionary[@"algorithm"]); MXJSONModelSetArray(sessionData.forwardingCurve25519KeyChain, JSONDictionary[@"forwarding_curve25519_key_chain"]) } @@ -43,6 +44,7 @@ - (NSDictionary *)JSONDictionary @"room_id": _roomId, @"session_id": _sessionId, @"session_key":_sessionKey, + @"shared_history": @(_sharedHistory), @"algorithm": _algorithm, @"forwarding_curve25519_key_chain": _forwardingCurve25519KeyChain ? _forwardingCurve25519KeyChain : @[] }; diff --git a/MatrixSDK/Crypto/Data/MXOlmInboundGroupSession.h b/MatrixSDK/Crypto/Data/MXOlmInboundGroupSession.h index 9a1765a538..502d15f676 100644 --- a/MatrixSDK/Crypto/Data/MXOlmInboundGroupSession.h +++ b/MatrixSDK/Crypto/Data/MXOlmInboundGroupSession.h @@ -66,6 +66,14 @@ */ @property (nonatomic) NSDictionary *keysClaimed; +/** + Flag indicating whether the history of this room is considered as shared. + + This is typically the case if room's `historyVisibility` is set to `world_readable` or `shared`. + In this case the keys are allowed to be shared with other users upon invite. + */ +@property (nonatomic) BOOL sharedHistory; + #pragma mark - import/export diff --git a/MatrixSDK/Crypto/Data/MXOlmInboundGroupSession.m b/MatrixSDK/Crypto/Data/MXOlmInboundGroupSession.m index dd2ab66a8e..2732a8df25 100644 --- a/MatrixSDK/Crypto/Data/MXOlmInboundGroupSession.m +++ b/MatrixSDK/Crypto/Data/MXOlmInboundGroupSession.m @@ -57,6 +57,7 @@ - (MXMegolmSessionData *)exportSessionDataAtMessageIndex:(NSUInteger)messageInde sessionData.sessionId = _session.sessionIdentifier; sessionData.sessionKey = sessionKey; sessionData.algorithm = kMXCryptoMegolmAlgorithm; + sessionData.sharedHistory = _sharedHistory; } else { @@ -97,6 +98,7 @@ - (instancetype)initWithImportedSessionData:(MXMegolmSessionData *)data _forwardingCurve25519KeyChain = data.forwardingCurve25519KeyChain; _keysClaimed = data.senderClaimedKeys; _roomId = data.roomId; + _sharedHistory = data.sharedHistory; } return self; } @@ -113,6 +115,7 @@ - (instancetype)initWithCoder:(NSCoder *)aDecoder _senderKey = [aDecoder decodeObjectForKey:@"senderKey"]; _forwardingCurve25519KeyChain = [aDecoder decodeObjectForKey:@"forwardingCurve25519KeyChain"]; _keysClaimed = [aDecoder decodeObjectForKey:@"keysClaimed"]; + _sharedHistory = [[aDecoder decodeObjectForKey:@"sharedHistory"] boolValue]; } return self; } @@ -124,6 +127,7 @@ - (void)encodeWithCoder:(NSCoder *)aCoder [aCoder encodeObject:_senderKey forKey:@"senderKey"]; [aCoder encodeObject:_keysClaimed forKey:@"keysClaimed"]; [aCoder encodeObject:_forwardingCurve25519KeyChain forKey:@"forwardingCurve25519KeyChain"]; + [aCoder encodeObject:@(_sharedHistory) forKey:@"sharedHistory"]; } @end diff --git a/MatrixSDK/Crypto/Data/Store/MXRealmCryptoStore/MXRealmCryptoStore.m b/MatrixSDK/Crypto/Data/Store/MXRealmCryptoStore/MXRealmCryptoStore.m index 645dad9774..2f5bf81fbb 100644 --- a/MatrixSDK/Crypto/Data/Store/MXRealmCryptoStore/MXRealmCryptoStore.m +++ b/MatrixSDK/Crypto/Data/Store/MXRealmCryptoStore/MXRealmCryptoStore.m @@ -32,7 +32,7 @@ #import "MXBackgroundModeHandler.h" #import "RLMRealm+MatrixSDK.h" -NSUInteger const kMXRealmCryptoStoreVersion = 17; +NSUInteger const kMXRealmCryptoStoreVersion = 18; static NSString *const kMXRealmCryptoStoreFolder = @"MXRealmCryptoStore"; @@ -114,6 +114,7 @@ @implementation MXRealmOlmSession @interface MXRealmOlmInboundGroupSession : RLMObject @property NSString *sessionId; @property NSString *senderKey; +@property NSString *roomId; @property NSData *olmInboundGroupSessionData; // A primary key is required to update `backedUp`. @@ -122,6 +123,9 @@ @interface MXRealmOlmInboundGroupSession : RLMObject // Indicate if the key has been backed up to the homeserver @property BOOL backedUp; + +@property BOOL sharedHistory; + @end @implementation MXRealmOlmInboundGroupSession @@ -950,8 +954,10 @@ - (void)storeInboundGroupSessions:(NSArray*)sessions realmSession = [[MXRealmOlmInboundGroupSession alloc] initWithValue:@{ @"sessionId": session.session.sessionIdentifier, @"senderKey": session.senderKey, + @"roomId": session.roomId, @"sessionIdSenderKey": sessionIdSenderKey, - @"olmInboundGroupSessionData": [NSKeyedArchiver archivedDataWithRootObject:session] + @"olmInboundGroupSessionData": [NSKeyedArchiver archivedDataWithRootObject:session], + @"sharedHistory": @(session.sharedHistory) }]; [realm addObject:realmSession]; @@ -2149,6 +2155,8 @@ + (BOOL)finaliseMigrationWith:(RLMMigration *)migration oldSchemaVersion:(uint64 newObject[@"cryptoVersion"] = @(MXCryptoVersion2); } }]; + case 17: + MXLogDebug(@"[MXRealmCryptoStore] Migration from schema #17 -> #18: Automatically adding MXRealmOlmInboundGroupSession.roomId and MXRealmOlmInboundGroupSession.sharedHistory"); } } diff --git a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m index ce20ebaa67..95ac783101 100644 --- a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m +++ b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m @@ -1611,7 +1611,8 @@ - (MXKeyBackupData*)encryptGroupSession:(MXOlmInboundGroupSession*)session @"sender_key": sessionData.senderKey, @"sender_claimed_keys": sessionData.senderClaimedKeys, @"forwarding_curve25519_key_chain": sessionData.forwardingCurve25519KeyChain ? sessionData.forwardingCurve25519KeyChain : @[], - @"session_key": sessionData.sessionKey + @"session_key": sessionData.sessionKey, + @"shared_history": @(sessionData.sharedHistory) }; OLMPkMessage *encryptedSessionBackupData = [_backupKey encryptMessage:[MXTools serialiseJSONObject:sessionBackupData] error:nil]; if (![self checkOLMPkMessage:encryptedSessionBackupData]) diff --git a/MatrixSDK/Crypto/MXCrypto.h b/MatrixSDK/Crypto/MXCrypto.h index 01f980236e..03662b1d38 100644 --- a/MatrixSDK/Crypto/MXCrypto.h +++ b/MatrixSDK/Crypto/MXCrypto.h @@ -610,6 +610,12 @@ extern NSString *const MXDeviceListDidUpdateUsersDevicesNotification; */ - (BOOL)isRoomEncrypted:(NSString *)roomId; +/** + Get the current shared history status of the room, which depends on its `m.room.history_visibility` + (history is considered shared if visibility is set to `shared` or `world_readable`) + */ +- (BOOL)isRoomSharingHistory:(NSString *)roomId; + /**he Set the blacklist of unverified devices in a room. diff --git a/MatrixSDK/Crypto/MXCrypto.m b/MatrixSDK/Crypto/MXCrypto.m index 27e1d27ebf..47ec8f5eb5 100644 --- a/MatrixSDK/Crypto/MXCrypto.m +++ b/MatrixSDK/Crypto/MXCrypto.m @@ -1894,6 +1894,13 @@ - (BOOL)isRoomEncrypted:(NSString *)roomId #endif } +- (BOOL)isRoomSharingHistory:(NSString *)roomId +{ + MXRoom *room = [_mxSession roomWithRoomId:roomId]; + MXRoomHistoryVisibility visibility = room.summary.historyVisibility; + return [visibility isEqualToString:kMXRoomHistoryVisibilityWorldReadable] || [visibility isEqualToString:kMXRoomHistoryVisibilityShared]; +} + - (void)setBlacklistUnverifiedDevicesInRoom:(NSString *)roomId blacklist:(BOOL)blacklist { #ifdef MX_CRYPTO diff --git a/MatrixSDK/Crypto/MXOlmDevice.h b/MatrixSDK/Crypto/MXOlmDevice.h index edcfbe5667..d627a1b684 100644 --- a/MatrixSDK/Crypto/MXOlmDevice.h +++ b/MatrixSDK/Crypto/MXOlmDevice.h @@ -230,15 +230,18 @@ Determine if an incoming messages is a prekey message matching an existing sessi @param forwardingCurve25519KeyChain devices which forwarded this session to us (normally empty) @param keysClaimed Other keys the sender claims. @param exportFormat YES if the megolm keys are in export format (ie, they lack an ed25519 signature). + @param sharedHistory YES if the session was created whilst room's history was set to visible (i.e. `world_readable` or `shared` @return YES if the operation succeeds. */ -- (BOOL)addInboundGroupSession:(NSString*)sessionId sessionKey:(NSString*)sessionKey +- (BOOL)addInboundGroupSession:(NSString*)sessionId + sessionKey:(NSString*)sessionKey roomId:(NSString*)roomId senderKey:(NSString*)senderKey forwardingCurve25519KeyChain:(NSArray *)forwardingCurve25519KeyChain keysClaimed:(NSDictionary*)keysClaimed - exportFormat:(BOOL)exportFormat; + exportFormat:(BOOL)exportFormat + sharedHistory:(BOOL)sharedHistory; /** Add previously-exported inbound group sessions to the session store. diff --git a/MatrixSDK/Crypto/MXOlmDevice.m b/MatrixSDK/Crypto/MXOlmDevice.m index d5af29d656..0139e6256f 100644 --- a/MatrixSDK/Crypto/MXOlmDevice.m +++ b/MatrixSDK/Crypto/MXOlmDevice.m @@ -337,12 +337,14 @@ - (void)discardOutboundGroupSessionForRoomWithRoomId:(NSString *)roomId #pragma mark - Inbound group session -- (BOOL)addInboundGroupSession:(NSString*)sessionId sessionKey:(NSString*)sessionKey +- (BOOL)addInboundGroupSession:(NSString*)sessionId + sessionKey:(NSString*)sessionKey roomId:(NSString*)roomId senderKey:(NSString*)senderKey forwardingCurve25519KeyChain:(NSArray *)forwardingCurve25519KeyChain keysClaimed:(NSDictionary*)keysClaimed exportFormat:(BOOL)exportFormat + sharedHistory:(BOOL)sharedHistory; { MXOlmInboundGroupSession *session; if (exportFormat) @@ -385,6 +387,7 @@ - (BOOL)addInboundGroupSession:(NSString*)sessionId sessionKey:(NSString*)sessio session.roomId = roomId; session.keysClaimed = keysClaimed; session.forwardingCurve25519KeyChain = forwardingCurve25519KeyChain; + session.sharedHistory = sharedHistory; [store storeInboundGroupSessions:@[session]]; @@ -596,7 +599,8 @@ - (NSDictionary*)getInboundGroupSessionKey:(NSString*)roomId senderKey:(NSString @"chain_index": messageIndex, @"key": sessionData.sessionKey, @"forwarding_curve25519_key_chain": forwardingCurve25519KeyChain ? forwardingCurve25519KeyChain : @[], - @"sender_claimed_ed25519_key": senderEd25519Key ? senderEd25519Key : [NSNull null] + @"sender_claimed_ed25519_key": senderEd25519Key ? senderEd25519Key : [NSNull null], + @"shared_history": @(sessionData.sharedHistory) }; } diff --git a/MatrixSDK/Data/MXRoomSummary.h b/MatrixSDK/Data/MXRoomSummary.h index df43a13d0d..70437d7754 100644 --- a/MatrixSDK/Data/MXRoomSummary.h +++ b/MatrixSDK/Data/MXRoomSummary.h @@ -186,6 +186,11 @@ FOUNDATION_EXPORT NSUInteger const MXRoomSummaryPaginationChunkSize; */ @property (nonatomic) NSArray *aliases; +/** + The history visibility of the room. + */ +@property (nonatomic) MXRoomHistoryVisibility historyVisibility; + /** Join rule for the room. */ diff --git a/MatrixSDK/Data/MXRoomSummary.m b/MatrixSDK/Data/MXRoomSummary.m index f491caf296..cc5596e948 100644 --- a/MatrixSDK/Data/MXRoomSummary.m +++ b/MatrixSDK/Data/MXRoomSummary.m @@ -229,6 +229,7 @@ - (void)updateWith:(id)summary _topic = summary.topic; _creatorUserId = summary.creatorUserId; _aliases = summary.aliases; + _historyVisibility = summary.historyVisibility; _joinRule = summary.joinRule; _membership = summary.membership; _membershipTransitionState = summary.membershipTransitionState; @@ -965,6 +966,7 @@ - (instancetype)initWithCoder:(NSCoder *)aDecoder _hiddenFromUser = [aDecoder decodeBoolForKey:@"hiddenFromUser"]; _storedHash = [aDecoder decodeIntegerForKey:@"storedHash"]; _dataTypes = (MXRoomSummaryDataTypes)[aDecoder decodeIntegerForKey:@"dataTypes"]; + _historyVisibility = [aDecoder decodeObjectForKey:@"historyVisibility"]; _joinRule = [aDecoder decodeObjectForKey:@"joinRule"]; _sentStatus = (MXRoomSummarySentStatus)[aDecoder decodeIntegerForKey:@"sentStatus"]; _favoriteTagOrder = [aDecoder decodeObjectForKey:@"favoriteTagOrder"]; @@ -1020,6 +1022,7 @@ - (void)encodeWithCoder:(NSCoder *)aCoder [aCoder encodeBool:_hiddenFromUser forKey:@"hiddenFromUser"]; [aCoder encodeInteger:self.hash forKey:@"storedHash"]; [aCoder encodeInteger:_dataTypes forKey:@"dataTypes"]; + [aCoder encodeObject:_historyVisibility forKey:@"historyVisibility"]; [aCoder encodeObject:_joinRule forKey:@"joinRule"]; [aCoder encodeInteger:_sentStatus forKey:@"sentStatus"]; [aCoder encodeObject:_favoriteTagOrder forKey:@"favoriteTagOrder"]; diff --git a/MatrixSDK/Data/MXRoomSummaryProtocol.h b/MatrixSDK/Data/MXRoomSummaryProtocol.h index 50ac5ef2df..7c1cee6864 100644 --- a/MatrixSDK/Data/MXRoomSummaryProtocol.h +++ b/MatrixSDK/Data/MXRoomSummaryProtocol.h @@ -59,6 +59,9 @@ NS_ASSUME_NONNULL_BEGIN /// The aliases of this room. @property (nonatomic, readonly) NSArray *aliases; +/// The history visibility of the room. +@property (nonatomic, readonly) MXRoomHistoryVisibility _Nullable historyVisibility; + /// Join rule for the room. @property (nonatomic, readonly) MXRoomJoinRule _Nullable joinRule; diff --git a/MatrixSDK/Data/MXRoomSummaryUpdater.m b/MatrixSDK/Data/MXRoomSummaryUpdater.m index 8c5a820a34..3085cccb44 100644 --- a/MatrixSDK/Data/MXRoomSummaryUpdater.m +++ b/MatrixSDK/Data/MXRoomSummaryUpdater.m @@ -248,6 +248,9 @@ - (BOOL)session:(MXSession *)session updateRoomSummary:(MXRoomSummary *)summary [self updateUserIdsSharingLiveBeacon:userIdsSharingLiveBeacon withStateEvent:event]; break; } + case MXEventTypeRoomHistoryVisibility: + summary.historyVisibility = roomState.historyVisibility; + break; default: break; } diff --git a/MatrixSDK/Data/RoomList/CoreData/MXCoreDataRoomListDataFetcher.swift b/MatrixSDK/Data/RoomList/CoreData/MXCoreDataRoomListDataFetcher.swift index 4d12b81334..b253073735 100644 --- a/MatrixSDK/Data/RoomList/CoreData/MXCoreDataRoomListDataFetcher.swift +++ b/MatrixSDK/Data/RoomList/CoreData/MXCoreDataRoomListDataFetcher.swift @@ -410,6 +410,7 @@ private class RoomSummaryForTotalCounts: NSObject, MXRoomSummaryProtocol { var topic: String? var creatorUserId: String = "" var aliases: [String] = [] + var historyVisibility: String? = nil var joinRule: String? = kMXRoomJoinRuleInvite var membership: MXMembership = .unknown var membershipTransitionState: MXMembershipTransitionState = .unknown diff --git a/MatrixSDK/Data/RoomSummaryStore/CoreData/MXCoreDataRoomSummaryStore.xcdatamodeld/.xccurrentversion b/MatrixSDK/Data/RoomSummaryStore/CoreData/MXCoreDataRoomSummaryStore.xcdatamodeld/.xccurrentversion new file mode 100644 index 0000000000..f32d838766 --- /dev/null +++ b/MatrixSDK/Data/RoomSummaryStore/CoreData/MXCoreDataRoomSummaryStore.xcdatamodeld/.xccurrentversion @@ -0,0 +1,8 @@ + + + + + _XCCurrentVersionName + MXRoomSummaryCoreDataStore2.xcdatamodel + + diff --git a/MatrixSDK/Data/RoomSummaryStore/CoreData/MXCoreDataRoomSummaryStore.xcdatamodeld/MXRoomSummaryCoreDataStore2.xcdatamodel/contents b/MatrixSDK/Data/RoomSummaryStore/CoreData/MXCoreDataRoomSummaryStore.xcdatamodeld/MXRoomSummaryCoreDataStore2.xcdatamodel/contents new file mode 100644 index 0000000000..9dfb13aa9c --- /dev/null +++ b/MatrixSDK/Data/RoomSummaryStore/CoreData/MXCoreDataRoomSummaryStore.xcdatamodeld/MXRoomSummaryCoreDataStore2.xcdatamodel/contents @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/MatrixSDK/Data/RoomSummaryStore/CoreData/Models/MXRoomSummaryMO.swift b/MatrixSDK/Data/RoomSummaryStore/CoreData/Models/MXRoomSummaryMO.swift index 6d5e35cd97..15d8b058ff 100644 --- a/MatrixSDK/Data/RoomSummaryStore/CoreData/Models/MXRoomSummaryMO.swift +++ b/MatrixSDK/Data/RoomSummaryStore/CoreData/Models/MXRoomSummaryMO.swift @@ -42,6 +42,7 @@ public class MXRoomSummaryMO: NSManagedObject { @NSManaged public var s_topic: String? @NSManaged public var s_creatorUserId: String @NSManaged public var s_aliases: String? + @NSManaged public var s_historyVisibility: String? @NSManaged public var s_joinRule: String? @NSManaged public var s_membershipInt: Int16 @NSManaged public var s_membershipTransitionStateInt: Int16 @@ -86,6 +87,7 @@ public class MXRoomSummaryMO: NSManagedObject { s_topic = summary.topic s_creatorUserId = summary.creatorUserId s_aliases = summary.aliases.joined(separator: StringArrayDelimiter) + s_historyVisibility = summary.historyVisibility s_joinRule = summary.joinRule s_membershipInt = Int16(summary.membership.rawValue) s_membershipTransitionStateInt = Int16(summary.membershipTransitionState.rawValue) @@ -200,6 +202,10 @@ extension MXRoomSummaryMO: MXRoomSummaryProtocol { return s_aliases?.components(separatedBy: StringArrayDelimiter) ?? [] } + public var historyVisibility: String? { + return s_historyVisibility + } + public var joinRule: String? { return s_joinRule } diff --git a/MatrixSDKTests/Crypto/Algorithms/Megolm/MXMegolmEncryptionTests.swift b/MatrixSDKTests/Crypto/Algorithms/Megolm/MXMegolmEncryptionTests.swift new file mode 100644 index 0000000000..8223217c4e --- /dev/null +++ b/MatrixSDKTests/Crypto/Algorithms/Megolm/MXMegolmEncryptionTests.swift @@ -0,0 +1,125 @@ +// +// Copyright 2022 The Matrix.org Foundation C.I.C +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation +@testable import MatrixSDK + +class MXMegolmEncryptionTests: XCTestCase { + struct SessionIds: Equatable { + let inbound: [String] + let outbound: [String] + } + + private var data: MatrixSDKTestsData! + private var e2eData: MatrixSDKTestsE2EData! + override func setUp() { + super.setUp() + + data = MatrixSDKTestsData() + e2eData = MatrixSDKTestsE2EData(matrixSDKTestsData: data) + } + + private func storedSessionIds(in session: MXSession!) -> SessionIds { + let store = MXRealmCryptoStore(credentials: session.matrixRestClient!.credentials) + return SessionIds( + inbound: store!.inboundGroupSessions().map { + $0.session.sessionIdentifier() + }, + outbound: store!.outboundGroupSessions().map { + $0.sessionId + } + ) + } + + private func isSharedHistoryInLastSession(for session: MXSession!) -> Bool { + let store = MXRealmCryptoStore(credentials: session.matrixRestClient!.credentials) + return store?.inboundGroupSessions().last?.sharedHistory == true + } + + func testResetsSessionIfRoomVisibilityChanges() { + + // The following tests that oubound session Id (and therefore the related inbound session Id) + // is reset whenever the room's history visibility changes from shared to not shared. + e2eData.doE2ETestWithAlice(inARoom: self) { session, roomId, expectation in + + let room = session?.room(withRoomId: roomId) + + // 1st set of messages + room?.sendTextMessages(messages: ["Hi", "Hello"]) { _ in + + // After first few messages we only expect one inbound and one outbound session + let sessionIds1 = self.storedSessionIds(in: session) + XCTAssertEqual(sessionIds1.outbound.count, 1) + XCTAssertEqual(sessionIds1.inbound.count, 1) + XCTAssertEqual(sessionIds1.inbound, sessionIds1.outbound) + XCTAssertTrue(self.isSharedHistoryInLastSession(for: session)) + + // 2nd set of messages + room?.sendTextMessages(messages: ["Hi", "Hello"]) { _ in + + // After second batch of messages nothing has changed that would require resetting + // of sessions, therefore sessionIds are unchanged + let sessionIds2 = self.storedSessionIds(in: session) + XCTAssertEqual(sessionIds2, sessionIds1) + XCTAssertTrue(self.isSharedHistoryInLastSession(for: session)) + + // Changing room visibility from shared by default to more restrictive will reset session keys + room?.setHistoryVisibility(.joined) { _ in + + // 3rd set of messages + room?.sendTextMessages(messages: ["Hi", "Hello"]) { _ in + + // Whilst there is still only one onbound session, its identifier has now changed, + // and inbound sessions have incremented + let sessionIds3 = self.storedSessionIds(in: session) + XCTAssertEqual(sessionIds3.outbound.count, 1) + XCTAssertNotEqual(sessionIds3.outbound, sessionIds2.outbound) + XCTAssertEqual(sessionIds3.inbound.count, 2) + XCTAssertEqual(sessionIds3.inbound, sessionIds1.outbound + sessionIds3.outbound) + XCTAssertFalse(self.isSharedHistoryInLastSession(for: session)) + + // 4th set of messages + room?.sendTextMessages(messages: ["Hi", "Hello"]) { _ in + // After fourth batch of messages nothing has changed that would require resetting + // of sessions, therefore sessionIds are unchanged + let sessionIds4 = self.storedSessionIds(in: session) + XCTAssertEqual(sessionIds4, sessionIds3) + XCTAssertFalse(self.isSharedHistoryInLastSession(for: session)) + + // Final visibility change back to shared will reset sessions once again + room?.setHistoryVisibility(.worldReadable) { _ in + room?.sendTextMessages(messages: ["Hi", "Hello"]) { _ in + + // Still only one outbound session with new ID, and three inbound sessions + let sessionIds5 = self.storedSessionIds(in: session) + XCTAssertEqual(sessionIds5.outbound.count, 1) + XCTAssertNotEqual(sessionIds5.outbound, sessionIds4.outbound) + XCTAssertEqual(sessionIds5.inbound.count, 3) + XCTAssertEqual(sessionIds5.inbound, sessionIds1.outbound + sessionIds3.outbound + sessionIds5.outbound) + XCTAssertTrue(self.isSharedHistoryInLastSession(for: session)) + + session?.close() + expectation?.fulfill() + } + } + } + } + } + } + } + } + } +} diff --git a/MatrixSDKTests/Crypto/Data/MXMegolmSessionDataUnitTests.swift b/MatrixSDKTests/Crypto/Data/MXMegolmSessionDataUnitTests.swift new file mode 100644 index 0000000000..4d66a7d2e9 --- /dev/null +++ b/MatrixSDKTests/Crypto/Data/MXMegolmSessionDataUnitTests.swift @@ -0,0 +1,69 @@ +// +// Copyright 2022 The Matrix.org Foundation C.I.C +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation +@testable import MatrixSDK + +class MXMegolmSessionDataUnitTests: XCTestCase { + func testCanInitWithJSONDictionary() { + let jsonDictionary: [String: Any] = [ + "sender_key": "A", + "sender_claimed_keys": ["B": "C"], + "room_id": "D", + "session_id": "E", + "session_key": "F", + "shared_history": true, + "algorithm": "G", + "forwarding_curve25519_key_chain": ["H", "I"] + ] + + let data = MXMegolmSessionData(fromJSON: jsonDictionary) + + XCTAssertEqual(data?.senderKey, "A") + XCTAssertEqual(data?.senderClaimedKeys, ["B": "C"]) + XCTAssertEqual(data?.roomId, "D") + XCTAssertEqual(data?.sessionId, "E") + XCTAssertEqual(data?.sessionKey, "F") + XCTAssertEqual(data?.sharedHistory, true) + XCTAssertEqual(data?.algorithm, "G") + XCTAssertEqual(data?.forwardingCurve25519KeyChain, ["H", "I"]) + } + + func testJsonDictionary() { + let data = MXMegolmSessionData() + data.senderKey = "A" + data.senderClaimedKeys = ["B": "C"] + data.roomId = "D" + data.sessionId = "E" + data.sessionKey = "F" + data.sharedHistory = true + data.algorithm = "G" + data.forwardingCurve25519KeyChain = ["H", "I"] + + let json = data.jsonDictionary() as NSDictionary + + XCTAssertEqual(json, [ + "sender_key": "A", + "sender_claimed_keys": ["B": "C"], + "room_id": "D", + "session_id": "E", + "session_key": "F", + "shared_history": true, + "algorithm": "G", + "forwarding_curve25519_key_chain": ["H", "I"] + ]) + } +} diff --git a/MatrixSDKTests/Crypto/Data/MXOlmInboundGroupSessionUnitTests.swift b/MatrixSDKTests/Crypto/Data/MXOlmInboundGroupSessionUnitTests.swift new file mode 100644 index 0000000000..9ec0590682 --- /dev/null +++ b/MatrixSDKTests/Crypto/Data/MXOlmInboundGroupSessionUnitTests.swift @@ -0,0 +1,56 @@ +// +// Copyright 2022 The Matrix.org Foundation C.I.C +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation +@testable import MatrixSDK + +class MXOlmInboundGroupSessionUnitTests: XCTestCase { + func testExportsCorrectSessionData() { + let session = MXOlmInboundGroupSession() + session.senderKey = "A" + session.forwardingCurve25519KeyChain = ["B", "C"] + session.keysClaimed = ["D": "E"] + session.roomId = "F" + session.sharedHistory = true + + let data = session.exportData() + + XCTAssertEqual(data?.senderKey, "A") + XCTAssertEqual(data?.forwardingCurve25519KeyChain, ["B", "C"]) + XCTAssertEqual(data?.senderClaimedKeys, ["D": "E"]) + XCTAssertEqual(data?.roomId, "F") + XCTAssert(data?.sharedHistory == true) + } + + @available(iOS 11.0, *) + func testCanEncodeAndDecodeObject() { + let session = MXOlmInboundGroupSession() + session.senderKey = "A" + session.forwardingCurve25519KeyChain = ["B", "C"] + session.keysClaimed = ["D": "E"] + session.roomId = "F" + session.sharedHistory = true + + let data = NSKeyedArchiver.archivedData(withRootObject: session) + let result = NSKeyedUnarchiver.unarchiveObject(with: data) as! MXOlmInboundGroupSession + + XCTAssertEqual(result.senderKey, session.senderKey) + XCTAssertEqual(result.forwardingCurve25519KeyChain, session.forwardingCurve25519KeyChain) + XCTAssertEqual(result.keysClaimed, session.keysClaimed) + XCTAssertEqual(result.roomId, session.roomId) + XCTAssertEqual(result.sharedHistory, session.sharedHistory) + } +} diff --git a/MatrixSDKTests/MXCryptoTests.m b/MatrixSDKTests/MXCryptoTests.m index 2fa3049d1b..097c4412f8 100644 --- a/MatrixSDKTests/MXCryptoTests.m +++ b/MatrixSDKTests/MXCryptoTests.m @@ -3223,6 +3223,60 @@ - (void)testBadSummaryIsEncryptedState }]; } +- (void)testIsRoomSharingHistory +{ + [matrixSDKTestsE2EData doE2ETestWithAliceInARoom:self readyToTest:^(MXSession *session, NSString *roomId, XCTestExpectation *expectation) { + + __block NSInteger caseIndex = 0; + NSArray *caseOutcomes = @[ + @[kMXRoomHistoryVisibilityJoined, @(NO)], + @[kMXRoomHistoryVisibilityShared, @(YES)], + @[kMXRoomHistoryVisibilityInvited, @(NO)], + @[kMXRoomHistoryVisibilityWorldReadable, @(YES)] + ]; + + // Visibility is set to shared by default + XCTAssertTrue([session.crypto isRoomSharingHistory:roomId]); + + MXRoom *room = [session roomWithRoomId:roomId]; + [room liveTimeline:^(id liveTimeline) { + [liveTimeline listenToEventsOfTypes:@[kMXEventTypeStringRoomHistoryVisibility] onEvent:^(MXEvent * _Nonnull event, MXTimelineDirection direction, MXRoomState * _Nullable roomState) { + + BOOL sharedHistory = [session.crypto isRoomSharingHistory:roomId]; + BOOL expectsSharedHistory = [caseOutcomes[caseIndex].lastObject boolValue]; + XCTAssertEqual(expectsSharedHistory, sharedHistory); + + caseIndex++; + if (caseIndex >= caseOutcomes.count) { + [expectation fulfill]; + } + }]; + }]; + + [room setHistoryVisibility:caseOutcomes[0][0] success:^{ + [room setHistoryVisibility:caseOutcomes[1][0] success:^{ + [room setHistoryVisibility:caseOutcomes[2][0] success:^{ + [room setHistoryVisibility:caseOutcomes[3][0] success:^{ + + } failure:^(NSError *error) { + XCTFail(@"Should not fail - error: %@", error); + [expectation fulfill]; + }]; + } failure:^(NSError *error) { + XCTFail(@"Should not fail - error: %@", error); + [expectation fulfill]; + }]; + } failure:^(NSError *error) { + XCTFail(@"Should not fail - error: %@", error); + [expectation fulfill]; + }]; + } failure:^(NSError *error) { + XCTFail(@"Should not fail - error: %@", error); + [expectation fulfill]; + }]; + }]; +} + @end #pragma clang diagnostic pop diff --git a/MatrixSDKTests/Mocks/MockRoomSummary.swift b/MatrixSDKTests/Mocks/MockRoomSummary.swift index 8334ea3412..3a8f3f4c5f 100644 --- a/MatrixSDKTests/Mocks/MockRoomSummary.swift +++ b/MatrixSDKTests/Mocks/MockRoomSummary.swift @@ -33,6 +33,8 @@ internal class MockRoomSummary: NSObject, MXRoomSummaryProtocol { var aliases: [String] = [] + var historyVisibility: String? = nil + var joinRule: String? = kMXRoomJoinRuleInvite var membership: MXMembership = .join diff --git a/MatrixSDKTests/TestPlans/UnitTests.xctestplan b/MatrixSDKTests/TestPlans/UnitTests.xctestplan index fe83624b36..1bec327787 100644 --- a/MatrixSDKTests/TestPlans/UnitTests.xctestplan +++ b/MatrixSDKTests/TestPlans/UnitTests.xctestplan @@ -35,7 +35,7 @@ "MXAsyncTaskQueueUnitTests", "MXAuthenticationSessionUnitTests", "MXBackgroundTaskUnitTests", - "MXBeaconInfoUnitTests", + "MXBeaconInfoUnitTests", "MXCoreDataRoomListDataManagerUnitTests", "MXCredentialsUnitTests", "MXDeviceListOperationsPoolUnitTests", @@ -49,6 +49,8 @@ "MXKeyProviderUnitTests", "MXMediaScanStoreUnitTests", "MXMegolmExportEncryptionUnitTests", + "MXMegolmSessionDataUnitTests", + "MXOlmInboundGroupSessionUnitTests", "MXPushRuleUnitTests", "MXQRCodeDataUnitTests", "MXReplyEventParserUnitTests", diff --git a/changelog.d/4947.change b/changelog.d/4947.change new file mode 100644 index 0000000000..8110c2cada --- /dev/null +++ b/changelog.d/4947.change @@ -0,0 +1 @@ +Crypto: Store `sharedHistory` flag for inbound Megolm sessions