From 6579e76a07b00acd6d1319695bc0c7bb66f171ec Mon Sep 17 00:00:00 2001 From: David-Henner Date: Thu, 13 Nov 2025 09:51:51 +0100 Subject: [PATCH 01/28] chore(cryptobox): retire `ProteusProvider` - WPB-21821 --- ...ManagedObjectContext+ProteusProvider.swift | 27 -- .../Model/User/ZMUser+LegalHoldRequest.swift | 48 ++-- .../Source/Model/UserClient/UserClient.swift | 118 +-------- .../UserClientChangeInfo.swift | 2 +- .../Source/Proteus/ProteusProvider.swift | 118 --------- .../Source/Utilis/KeychainManager.swift | 2 +- .../Support/Sources/MockProteusProvider.swift | 79 ------ .../Model/UserClient/UserClientTests.swift | 63 +---- .../WireDataModel.xcodeproj/project.pbxproj | 8 - .../Message Sending/MessageSenderTests.swift | 1 + .../Decoding/EventDecoder.swift | 92 +------ .../Decoding/EventDecoderTest.swift | 35 +-- .../Helpers/MessagingTest+Encryption.swift | 144 ----------- .../Tests/Helpers/MessagingTestBase.swift | 234 ++++++++++-------- .../Helpers/ProteusClientSimulator.swift | 208 ++++++++++++++++ ...LocalNotificationTests_SystemMessage.swift | 25 +- ...geSendingStatusPayloadProcessorTests.swift | 8 +- .../project.pbxproj | 8 +- .../Strategies/PrekeyGenerator.swift | 39 +-- .../Strategies/UserClientRequestFactory.swift | 1 - .../UserClientRequestStrategy.swift | 5 +- .../Synchronization/StrategyDirectory.swift | 8 +- .../GetUserClientFingerprintUseCase.swift | 79 ++---- .../NSManagedObject+CryptoStack.swift | 5 - .../ZMUserSession/ZMUserSession.swift | 11 +- .../E2EE/UserClientRequestStrategyTests.swift | 36 +-- ...GetUserClientFingerprintUseCaseTests.swift | 64 ++--- 27 files changed, 485 insertions(+), 983 deletions(-) delete mode 100644 wire-ios-data-model/Source/ManagedObjectContext/NSManagedObjectContext+ProteusProvider.swift delete mode 100644 wire-ios-data-model/Source/Proteus/ProteusProvider.swift delete mode 100644 wire-ios-data-model/Support/Sources/MockProteusProvider.swift delete mode 100644 wire-ios-request-strategy/Tests/Helpers/MessagingTest+Encryption.swift create mode 100644 wire-ios-request-strategy/Tests/Helpers/ProteusClientSimulator.swift diff --git a/wire-ios-data-model/Source/ManagedObjectContext/NSManagedObjectContext+ProteusProvider.swift b/wire-ios-data-model/Source/ManagedObjectContext/NSManagedObjectContext+ProteusProvider.swift deleted file mode 100644 index 49acc714183..00000000000 --- a/wire-ios-data-model/Source/ManagedObjectContext/NSManagedObjectContext+ProteusProvider.swift +++ /dev/null @@ -1,27 +0,0 @@ -// -// Wire -// Copyright (C) 2025 Wire Swiss GmbH -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see http://www.gnu.org/licenses/. -// - -import Foundation - -public extension NSManagedObjectContext { - - var proteusProvider: ProteusProviding { - precondition(zm_isSyncContext, "ProteusProvider should only be accessed on the sync context") - return ProteusProvider(context: self) - } -} diff --git a/wire-ios-data-model/Source/Model/User/ZMUser+LegalHoldRequest.swift b/wire-ios-data-model/Source/Model/User/ZMUser+LegalHoldRequest.swift index d902cd24b49..5d3383b3398 100644 --- a/wire-ios-data-model/Source/Model/User/ZMUser+LegalHoldRequest.swift +++ b/wire-ios-data-model/Source/Model/User/ZMUser+LegalHoldRequest.swift @@ -17,9 +17,9 @@ // import Foundation -import WireCryptobox +import WireLogging -private let log = ZMSLog(tag: "UserClient") +private let log = WireLogger.userClient public typealias SelfUserLegalHoldable = EditableUserType & SelfLegalHoldSubject & UserType @@ -285,37 +285,25 @@ extension ZMUser: SelfLegalHoldSubject { public var fingerprint: String? { get async { - guard let (syncContext, prekey) = await managedObjectContext?.perform({ - (self.managedObjectContext?.zm_sync, self.legalHoldRequest?.lastPrekey) - }), let syncContext, let prekey else { return nil } - - let proteusProvider = await syncContext.perform { syncContext.proteusProvider } - return await proteusProvider.performAsync { proteusService in - await fetchFingerprint(for: prekey, through: proteusService) - } withKeyStore: { keyStore in - fetchFingerprint(for: prekey, through: keyStore) + let syncContext = await managedObjectContext?.perform { [managedObjectContext] in + managedObjectContext?.zm_sync } - } - } - private func fetchFingerprint( - for prekey: LegalHoldRequest.Prekey, - through proteusService: ProteusServiceInterface - ) async -> String? { - do { - return try await proteusService.fingerprint(fromPrekey: prekey.key.base64EncodedString()) - } catch { - log.error("Could not fetch fingerprint for \(self)") - return nil - } - } + guard + let syncContext, + let prekey = legalHoldRequest?.lastPrekey, + let proteusService = await syncContext.perform({ syncContext.proteusService }) + else { + return nil + } - private func fetchFingerprint( - for prekey: LegalHoldRequest.Prekey, - through keystore: UserClientKeysStore - ) -> String? { - guard let fingerprintData = EncryptionSessionsDirectory.fingerprint(fromPrekey: prekey.key) else { return nil } - return String(decoding: fingerprintData, as: UTF8.self) + do { + return try await proteusService.fingerprint(fromPrekey: prekey.key.base64EncodedString()) + } catch { + log.error("Could not fetch fingerprint for \(self)") + return nil + } + } } } diff --git a/wire-ios-data-model/Source/Model/UserClient/UserClient.swift b/wire-ios-data-model/Source/Model/UserClient/UserClient.swift index 2c4d8b98f65..85abd03bd4d 100644 --- a/wire-ios-data-model/Source/Model/UserClient/UserClient.swift +++ b/wire-ios-data-model/Source/Model/UserClient/UserClient.swift @@ -280,28 +280,13 @@ public class UserClient: ZMManagedObject, UserClientType { get async { guard let sessionID = await managedObjectContext?.perform({ self.proteusSessionID }), - let proteusProvider = await managedObjectContext? - .perform({ self.managedObjectContext?.proteusProvider }) + let proteusService = await managedObjectContext? + .perform({ [managedObjectContext] in managedObjectContext?.proteusService }) else { return false } - var hasSession = false - - await proteusProvider.performAsync( - withProteusService: { proteusService in - hasSession = await proteusService.sessionExists(id: sessionID) - }, - withKeyStore: { keyStore in - managedObjectContext?.performAndWait { - keyStore.encryptionContext.perform { sessionsDirectory in - hasSession = sessionsDirectory.hasSession(for: sessionID.mapToEncryptionSessionID()) - } - } - } - ) - - return hasSession + return await proteusService.sessionExists(id: sessionID) } } @@ -572,21 +557,12 @@ public extension UserClient { guard let context = managedObjectContext, await context.perform({ !self.isSelfClient() }), - let sessionID = await context.perform({ self.proteusSessionID }) + let sessionID = await context.perform({ self.proteusSessionID }), + let proteusService = await context.perform({ context.proteusService }) else { return } - let proteusProvider = await context.perform { context.proteusProvider } - try await proteusProvider.performAsync( - withProteusService: { proteusService in - try await proteusService.deleteSession(id: sessionID) - }, - withKeyStore: { keyStore in - keyStore.encryptionContext.perform { sessionsDirectory in - sessionsDirectory.delete(sessionID.mapToEncryptionSessionID()) - } - } - ) + try await proteusService.deleteSession(id: sessionID) } func establishSessionWithClient( @@ -594,56 +570,18 @@ public extension UserClient { usingPreKey preKey: String ) async -> Bool { guard - let proteusProvider = await managedObjectContext?.perform({ self.managedObjectContext?.proteusProvider }), - let sessionId = await managedObjectContext?.perform({ client.sessionIdentifier }) + let context = managedObjectContext, + let proteusService = await context.perform({ context.proteusService }), + let sessionId = await context.perform({ client.proteusSessionID }) else { return false } - return await establishSessionWithClient( - sessionId: sessionId, - usingPreKey: preKey, - proteusProviding: proteusProvider - ) - } - - /// Creates a session between the selfClient and the given userClient - /// Returns false if the session could not be established - /// Use this method only for the selfClient - func establishSessionWithClient( - sessionId: EncryptionSessionIdentifier, - usingPreKey preKey: String, - proteusProviding: ProteusProviding - ) async -> Bool { - await proteusProviding.performAsync { proteusService in - await establishSession( - through: proteusService, - sessionId: sessionId, - preKey: preKey - ) - } withKeyStore: { keystore in - establishSession( - through: keystore, - sessionId: sessionId, - preKey: preKey - ) - } - } - - private func establishSession( - through proteusService: ProteusServiceInterface, - sessionId: EncryptionSessionIdentifier, - preKey: String - ) async -> Bool { do { // swiftlint:disable:next todo_requires_jira_link // TODO: check if we should delete session if it exists before creating new one - let proteusSessionId = ProteusSessionID( - domain: sessionId.domain, - userID: sessionId.userId, - clientID: sessionId.clientId - ) - try await proteusService.establishSession(id: proteusSessionId, fromPrekey: preKey) + + try await proteusService.establishSession(id: sessionId, fromPrekey: preKey) return true } catch { zmLog.error("Cannot create session for prekey \(preKey): \(String(describing: error))") @@ -651,40 +589,6 @@ public extension UserClient { } } - func establishSession( - through keystore: UserClientKeysStore, - sessionId: EncryptionSessionIdentifier, - preKey: String - ) -> Bool { - var didEstablishSession = false - managedObjectContext?.performAndWait { - - keystore.encryptionContext.perform { sessionsDirectory in - - // Session is already established? - if sessionsDirectory.hasSession(for: sessionId) { - zmLog.debug("Session with \(sessionId) was already established, re-creating") - sessionsDirectory.delete(sessionId) - } - } - - // Because of caching within the `perform` block, it commits to disk only at the end of a block. - // I don't think the cache is smart enough to perform the sum of operations (delete + recreate) - // if at the end of the block the session is still there. Just to be safe, I split the operations - // in two separate `perform` blocks. - - keystore.encryptionContext.perform { sessionsDirectory in - do { - try sessionsDirectory.createClientSession(sessionId, base64PreKeyString: preKey) - didEstablishSession = true - } catch { - zmLog.error("Cannot create session for prekey \(preKey)") - } - } - } - return didEstablishSession - } - /// Use this method only for the selfClient @objc func decrementNumberOfRemainingProteusKeys() { diff --git a/wire-ios-data-model/Source/Notifications/ObjectObserverTokens/UserClientChangeInfo.swift b/wire-ios-data-model/Source/Notifications/ObjectObserverTokens/UserClientChangeInfo.swift index fa53e964430..7ebb5fe1fe3 100644 --- a/wire-ios-data-model/Source/Notifications/ObjectObserverTokens/UserClientChangeInfo.swift +++ b/wire-ios-data-model/Source/Notifications/ObjectObserverTokens/UserClientChangeInfo.swift @@ -20,7 +20,7 @@ import Foundation public extension UserClient { override var description: String { - "Client: \(String(describing: sessionIdentifier?.rawValue)), user name: \(String(describing: user?.name)) email: \(String(describing: user?.emailAddress)) platform: \(String(describing: deviceClass)), label: \(String(describing: label)), model: \(String(describing: model))" + "Client: \(String(describing: proteusSessionID?.rawValue)), user name: \(String(describing: user?.name)) email: \(String(describing: user?.emailAddress)) platform: \(String(describing: deviceClass)), label: \(String(describing: label)), model: \(String(describing: model))" } } diff --git a/wire-ios-data-model/Source/Proteus/ProteusProvider.swift b/wire-ios-data-model/Source/Proteus/ProteusProvider.swift deleted file mode 100644 index 5bb2d78f805..00000000000 --- a/wire-ios-data-model/Source/Proteus/ProteusProvider.swift +++ /dev/null @@ -1,118 +0,0 @@ -// -// Wire -// Copyright (C) 2025 Wire Swiss GmbH -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see http://www.gnu.org/licenses/. -// - -import Foundation -import WireLogging -import WireUtilities - -public typealias KeyStorePerformBlock = (UserClientKeysStore) throws -> T -public typealias ProteusServicePerformBlock = (ProteusServiceInterface) throws -> T - -public typealias KeyStorePerformAsyncBlock = (UserClientKeysStore) async throws -> T -public typealias ProteusServicePerformAsyncBlock = (ProteusServiceInterface) async throws -> T - -public protocol ProteusProviding { - - func perform( - withProteusService proteusServiceBlock: ProteusServicePerformBlock, - withKeyStore keyStoreBlock: KeyStorePerformBlock - ) rethrows -> T - - func performAsync( - withProteusService proteusServiceBlock: ProteusServicePerformAsyncBlock, - withKeyStore keyStoreBlock: KeyStorePerformAsyncBlock - ) async rethrows -> T - - var canPerform: Bool { get } -} - -public class ProteusProvider: ProteusProviding { - - private let proteusService: ProteusServiceInterface? - private let keyStore: UserClientKeysStore? - private let proteusViaCoreCrypto: Bool - - convenience init( - context: NSManagedObjectContext, - proteusViaCoreCrypto: Bool = DeveloperFlag.proteusViaCoreCrypto.isOn - ) { - self.init( - proteusService: context.proteusService, - keyStore: context.zm_cryptKeyStore, - proteusViaCoreCrypto: proteusViaCoreCrypto - ) - } - - public init( - proteusService: ProteusServiceInterface?, - keyStore: UserClientKeysStore?, - proteusViaCoreCrypto: Bool = DeveloperFlag.proteusViaCoreCrypto.isOn - ) { - self.proteusService = proteusService - self.keyStore = keyStore - self.proteusViaCoreCrypto = proteusViaCoreCrypto - } - - public func perform( - withProteusService proteusServiceBlock: ProteusServicePerformBlock, - withKeyStore keyStoreBlock: KeyStorePerformBlock - ) rethrows -> T { - - if let proteusService, proteusViaCoreCrypto { - - return try proteusServiceBlock(proteusService) - - } else if let keyStore, !proteusViaCoreCrypto { - - // remove comment once implementation of proteus via core crypto is done - return try keyStoreBlock(keyStore) - } else { - - WireLogger.coreCrypto.error("can't access any proteus cryptography service") - fatal("can't access any proteus cryptography service") - } - } - - public func performAsync( - withProteusService proteusServiceBlock: ProteusServicePerformAsyncBlock, - withKeyStore keyStoreBlock: KeyStorePerformAsyncBlock - ) async rethrows -> T { - - if let proteusService, proteusViaCoreCrypto { - - return try await proteusServiceBlock(proteusService) - - } else if let keyStore, !proteusViaCoreCrypto { - - // remove comment once implementation of proteus via core crypto is done - return try await keyStoreBlock(keyStore) - } else { - - WireLogger.coreCrypto.error("can't access any proteus cryptography service") - fatal("can't access any proteus cryptography service") - } - } - - public var canPerform: Bool { - let canUseProteusService = proteusViaCoreCrypto && proteusService != nil - let canUseKeyStore = !proteusViaCoreCrypto - - return canUseProteusService || canUseKeyStore - } - -} diff --git a/wire-ios-data-model/Source/Utilis/KeychainManager.swift b/wire-ios-data-model/Source/Utilis/KeychainManager.swift index 08149daf53b..0d9a379eb87 100644 --- a/wire-ios-data-model/Source/Utilis/KeychainManager.swift +++ b/wire-ios-data-model/Source/Utilis/KeychainManager.swift @@ -95,7 +95,7 @@ public enum KeychainManager { // MARK: - Key generation - static func generateKey(numberOfBytes: UInt = 32) throws -> Data { + public static func generateKey(numberOfBytes: UInt = 32) throws -> Data { var key = [UInt8](repeating: 0, count: Int(numberOfBytes)) let status = SecRandomCopyBytes(kSecRandomDefault, key.count, &key) diff --git a/wire-ios-data-model/Support/Sources/MockProteusProvider.swift b/wire-ios-data-model/Support/Sources/MockProteusProvider.swift deleted file mode 100644 index ce3bea7caa4..00000000000 --- a/wire-ios-data-model/Support/Sources/MockProteusProvider.swift +++ /dev/null @@ -1,79 +0,0 @@ -// -// Wire -// Copyright (C) 2025 Wire Swiss GmbH -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see http://www.gnu.org/licenses/. -// - -import Foundation -import WireDataModel - -public class MockProteusProvider: ProteusProviding { - - public let mockProteusService: MockProteusServiceInterface - public let mockKeyStore: SpyUserClientKeyStore - public var useProteusService: Bool - - public init( - mockProteusService: MockProteusServiceInterface = MockProteusServiceInterface(), - mockKeyStore: SpyUserClientKeyStore = MockProteusProvider.spyForTests(), - useProteusService: Bool = false - ) { - self.mockProteusService = mockProteusService - self.mockKeyStore = mockKeyStore - self.useProteusService = useProteusService - } - - public func perform( - withProteusService proteusServiceBlock: (ProteusServiceInterface) throws -> T, - withKeyStore keyStoreBlock: (UserClientKeysStore) throws -> T - ) - rethrows -> T { - if useProteusService { - try proteusServiceBlock(mockProteusService) - } else { - try keyStoreBlock(mockKeyStore) - } - } - - public func performAsync( - withProteusService proteusServiceBlock: (ProteusServiceInterface) async throws -> T, - withKeyStore keyStoreBlock: (UserClientKeysStore) async throws -> T - ) - async rethrows -> T { - if useProteusService { - try await proteusServiceBlock(mockProteusService) - } else { - try await keyStoreBlock(mockKeyStore) - } - } - - public var mockCanPerform = true - public var canPerform: Bool { - mockCanPerform - } - - public static func spyForTests() -> SpyUserClientKeyStore { - let url = Self.createTempFolder() - return SpyUserClientKeyStore(accountDirectory: url, applicationContainer: url) - } - - // FIXME: [WPB-5867] this is defined in WireTesting, somehow it is not possible to import here for now - [Francois] - public static func createTempFolder() -> URL { - let url = URL(fileURLWithPath: [NSTemporaryDirectory(), UUID().uuidString].joined(separator: "/")) - try! FileManager.default.createDirectory(at: url, withIntermediateDirectories: true, attributes: [:]) - return url - } - -} diff --git a/wire-ios-data-model/Tests/Source/Model/UserClient/UserClientTests.swift b/wire-ios-data-model/Tests/Source/Model/UserClient/UserClientTests.swift index f14a5283ea9..cefc4bbfab7 100644 --- a/wire-ios-data-model/Tests/Source/Model/UserClient/UserClientTests.swift +++ b/wire-ios-data-model/Tests/Source/Model/UserClient/UserClientTests.swift @@ -855,13 +855,13 @@ extension UserClientTests { client.needsSessionMigration = true let userID = client.user!.remoteIdentifier.uuidString - let expectedSessionIdentifier = EncryptionSessionIdentifier( - userId: userID, - clientId: clientID + let expectedSessionIdentifier = ProteusSessionID( + userID: userID, + clientID: clientID ) // when - let sessionIdentifier = client.sessionIdentifier + let sessionIdentifier = client.proteusSessionID // then XCTAssertEqual(sessionIdentifier, expectedSessionIdentifier) @@ -880,14 +880,14 @@ extension UserClientTests { client.needsSessionMigration = false let userID = client.user!.remoteIdentifier.uuidString - let expectedSessionIdentifier = EncryptionSessionIdentifier( + let expectedSessionIdentifier = ProteusSessionID( domain: domain, - userId: userID, - clientId: clientID + userID: userID, + clientID: clientID ) // when - let sessionIdentifier = client.sessionIdentifier + let sessionIdentifier = client.proteusSessionID // then XCTAssertEqual(sessionIdentifier, expectedSessionIdentifier) @@ -930,50 +930,3 @@ extension UserClientTests { } } - -// MARK: - Proteus - -extension UserClientTests { - - func test_GivenDeveloperFlagProteusViaCoreCryptoEnabled_ItUsesCoreKrypto() async { - // GIVEN - let context = syncMOC - var mockMethodCalled = false - let prekey = "test".utf8Data!.base64String() - var resultOfMethod = false - - let mockProteusService = MockProteusServiceInterface() - mockProteusService.establishSessionIdFromPrekey_MockMethod = { _, _ in - mockMethodCalled = true - } - mockProteusService.remoteFingerprintForSession_MockMethod = { _ in - "test" - } - - let mock = MockProteusProvider(mockProteusService: mockProteusService) - mock.useProteusService = true - - var sut: UserClient! - var clientB: UserClient! - var sessionId: EncryptionSessionIdentifier! - - await context.performGrouped { - sut = self.createSelfClient(onMOC: context) - let userB = self.createUser(in: context) - clientB = self.createClient(for: userB, createSessionWithSelfUser: false, onMOC: context) - sessionId = clientB.sessionIdentifier - } - - // WHEN - resultOfMethod = await sut.establishSessionWithClient( - sessionId: sessionId, - usingPreKey: prekey, - proteusProviding: mock - ) - - // THEN - XCTAssertTrue(mockMethodCalled) - XCTAssertTrue(resultOfMethod) - } - -} diff --git a/wire-ios-data-model/WireDataModel.xcodeproj/project.pbxproj b/wire-ios-data-model/WireDataModel.xcodeproj/project.pbxproj index cef5f16987e..38c68696298 100644 --- a/wire-ios-data-model/WireDataModel.xcodeproj/project.pbxproj +++ b/wire-ios-data-model/WireDataModel.xcodeproj/project.pbxproj @@ -344,7 +344,6 @@ 63B1336A29A503D100009D84 /* ClaimMLSKeyPackageAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63B1334F29A503D000009D84 /* ClaimMLSKeyPackageAction.swift */; }; 63B1336B29A503D100009D84 /* MLSGroupStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63B1335029A503D000009D84 /* MLSGroupStatus.swift */; }; 63B1336E29A503D100009D84 /* StaleMLSKeyMaterialDetector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63B1335329A503D000009D84 /* StaleMLSKeyMaterialDetector.swift */; }; - 63B1337329A798C800009D84 /* ProteusProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63B1337229A798C800009D84 /* ProteusProvider.swift */; }; 63B658DE243754E100EF463F /* GenericMessage+UpdateEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63B658DD243754E100EF463F /* GenericMessage+UpdateEvent.swift */; }; 63B658E0243789DE00EF463F /* GenericMessage+Assets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63B658DF243789DE00EF463F /* GenericMessage+Assets.swift */; }; 63B74CBD2AE1715000A73006 /* ProteusToMLSMigrationCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63B74CBC2AE1715000A73006 /* ProteusToMLSMigrationCoordinator.swift */; }; @@ -563,7 +562,6 @@ EEC794F62A384464008E1A3B /* MLSEncryptionService.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEC794F52A384464008E1A3B /* MLSEncryptionService.swift */; }; EEC794F82A385359008E1A3B /* MLSDecryptionServiceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEC794F72A385359008E1A3B /* MLSDecryptionServiceTests.swift */; }; EEC794FA2A38963F008E1A3B /* MLSEncryptionServiceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEC794F92A38963F008E1A3B /* MLSEncryptionServiceTests.swift */; }; - EEC80B3629B0AD8100099727 /* NSManagedObjectContext+ProteusProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEC80B3529B0AD8100099727 /* NSManagedObjectContext+ProteusProvider.swift */; }; EED6C7142A30B94100CB8B60 /* MLSUserID.swift in Sources */ = {isa = PBXBuildFile; fileRef = EED6C7132A30B94100CB8B60 /* MLSUserID.swift */; }; EED6C7162A31AD1200CB8B60 /* MLSUserIDTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EED6C7152A31AD1200CB8B60 /* MLSUserIDTests.swift */; }; EEDA9C152513A1DA003A5B27 /* ZMClientMessage+EncryptionAtRest.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEDA9C132513A0A5003A5B27 /* ZMClientMessage+EncryptionAtRest.swift */; }; @@ -1085,7 +1083,6 @@ 63B1334F29A503D000009D84 /* ClaimMLSKeyPackageAction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ClaimMLSKeyPackageAction.swift; sourceTree = ""; }; 63B1335029A503D000009D84 /* MLSGroupStatus.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MLSGroupStatus.swift; sourceTree = ""; }; 63B1335329A503D000009D84 /* StaleMLSKeyMaterialDetector.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StaleMLSKeyMaterialDetector.swift; sourceTree = ""; }; - 63B1337229A798C800009D84 /* ProteusProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProteusProvider.swift; sourceTree = ""; }; 63B658DD243754E100EF463F /* GenericMessage+UpdateEvent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "GenericMessage+UpdateEvent.swift"; sourceTree = ""; }; 63B658DF243789DE00EF463F /* GenericMessage+Assets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "GenericMessage+Assets.swift"; sourceTree = ""; }; 63B74CBC2AE1715000A73006 /* ProteusToMLSMigrationCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProteusToMLSMigrationCoordinator.swift; sourceTree = ""; }; @@ -1305,7 +1302,6 @@ EEC794F52A384464008E1A3B /* MLSEncryptionService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MLSEncryptionService.swift; sourceTree = ""; }; EEC794F72A385359008E1A3B /* MLSDecryptionServiceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MLSDecryptionServiceTests.swift; sourceTree = ""; }; EEC794F92A38963F008E1A3B /* MLSEncryptionServiceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MLSEncryptionServiceTests.swift; sourceTree = ""; }; - EEC80B3529B0AD8100099727 /* NSManagedObjectContext+ProteusProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSManagedObjectContext+ProteusProvider.swift"; sourceTree = ""; }; EED6C7132A30B94100CB8B60 /* MLSUserID.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MLSUserID.swift; sourceTree = ""; }; EED6C7152A31AD1200CB8B60 /* MLSUserIDTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MLSUserIDTests.swift; sourceTree = ""; }; EEDA9C132513A0A5003A5B27 /* ZMClientMessage+EncryptionAtRest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ZMClientMessage+EncryptionAtRest.swift"; sourceTree = ""; }; @@ -1910,7 +1906,6 @@ children = ( 63B1333729A503D000009D84 /* ProteusServiceInterface.swift */, 63B1333829A503D000009D84 /* ProteusService.swift */, - 63B1337229A798C800009D84 /* ProteusProvider.swift */, EE032B2F29A62CA600E1DDF3 /* ProteusSessionID.swift */, EE032B3029A62CA600E1DDF3 /* ProteusSessionID+Mapping.swift */, EE79699529D4684C00075E38 /* CryptoboxMigrationManager.swift */, @@ -2314,7 +2309,6 @@ 163C92A92630A80400F8DC14 /* NSManagedObjectContext+SelfUser.swift */, 63DA335D286C9CF000818C3C /* NSManagedObjectContext+MLSService.swift */, EE9B9F562993E57900A257BC /* NSManagedObjectContext+ProteusService.swift */, - EEC80B3529B0AD8100099727 /* NSManagedObjectContext+ProteusProvider.swift */, EE9B9F5829964F6A00A257BC /* NSManagedObjectContext+CoreCrypto.swift */, F9A705D01CAEE01D00C2F5FE /* NSNotification+ManagedObjectContextSave.h */, F9A705D11CAEE01D00C2F5FE /* NSNotification+ManagedObjectContextSave.m */, @@ -3512,7 +3506,6 @@ A90676EB238EB05F006417AC /* Role.swift in Sources */, 6313CF2A2B69142500B41A33 /* CRLExpirationDatesRepository.swift in Sources */, 63B658DE243754E100EF463F /* GenericMessage+UpdateEvent.swift in Sources */, - EEC80B3629B0AD8100099727 /* NSManagedObjectContext+ProteusProvider.swift in Sources */, 63370C6C242A510A0072C37F /* ZMOTRMessage+UpdateEvent.swift in Sources */, 06D0648E2DDFF2560089EB6D /* CoreCryptoKeyMigrationManagerProtocol.swift in Sources */, 0179629F2B83FCB200D6C7B6 /* NSManagedObjectContext+Migration.swift in Sources */, @@ -3759,7 +3752,6 @@ EEC794F62A384464008E1A3B /* MLSEncryptionService.swift in Sources */, F9A706A31CAEE01D00C2F5FE /* ConversationListChangeInfo.swift in Sources */, EE9AD9162696F01700DD5F51 /* LegacyFeatureRepository.swift in Sources */, - 63B1337329A798C800009D84 /* ProteusProvider.swift in Sources */, 0630E4B6257F888600C75BFB /* NSManagedObjectContext+AppLock.swift in Sources */, 599EA3D32C2468A0009319D4 /* FilterConversationsUseCase Protocol.swift in Sources */, E6E5043A2BC542C5004948E7 /* EARPrivateKeys.swift in Sources */, diff --git a/wire-ios-request-strategy/Sources/Message Sending/MessageSenderTests.swift b/wire-ios-request-strategy/Sources/Message Sending/MessageSenderTests.swift index 59790f1406e..ab1bfc5da81 100644 --- a/wire-ios-request-strategy/Sources/Message Sending/MessageSenderTests.swift +++ b/wire-ios-request-strategy/Sources/Message Sending/MessageSenderTests.swift @@ -1024,6 +1024,7 @@ final class MessageSenderTests: MessagingTestBase { // success dumb data ["test": Data()] } + proteusService.sessionExistsId_MockValue = true } return self } diff --git a/wire-ios-request-strategy/Sources/Synchronization/Decoding/EventDecoder.swift b/wire-ios-request-strategy/Sources/Synchronization/Decoding/EventDecoder.swift index e83873e2f60..5f22bc8471f 100644 --- a/wire-ios-request-strategy/Sources/Synchronization/Decoding/EventDecoder.swift +++ b/wire-ios-request-strategy/Sources/Synchronization/Decoding/EventDecoder.swift @@ -18,7 +18,6 @@ import Foundation import GenericMessageProtocol -import WireCryptobox import WireDataModel import WireLogging import WireUtilities @@ -76,11 +75,10 @@ public final class EventDecoder: NSObject, EventDecoderProtocol { super.init() } - /// Guarantee to get proteusProvider from correct context - /// - Note: to be replaced when proteusProvider is not attached to context 🤞 - private var proteusProvider: ProteusProviding { + /// Guarantee to get proteusService from correct context + private var proteusService: ProteusServiceInterface? { syncMOC.performAndWait { - syncMOC.proteusProvider + syncMOC.proteusService } } @@ -102,33 +100,21 @@ extension EventDecoder { _ events: [ZMUpdateEvent], publicKeys: EARPublicKeys? = nil ) async throws -> [ZMUpdateEvent] { - let lastIndex = await eventMOC.perform { + let lastIndex = await eventMOC.perform { [eventMOC] in // Get the highest index of events in the DB - StoredUpdateEvent.highestIndex(self.eventMOC) + StoredUpdateEvent.highestIndex(eventMOC) } - guard proteusProvider.canPerform else { - WireLogger.proteus.warn("ignore decrypting events because it is not safe") + guard let proteusService else { + WireLogger.proteus.warn("ignore decrypting events because proteus service is not available") return [] } - let decryptedEvents: [ZMUpdateEvent] = try await proteusProvider.performAsync( - withProteusService: { proteusService in - try await self.decryptAndStoreEvents( - events, - startingAtIndex: lastIndex, - publicKeys: publicKeys, - proteusService: proteusService - ) - }, - withKeyStore: { keyStore in - await self.legacyDecryptAndStoreEvents( - events, - startingAtIndex: lastIndex, - publicKeys: publicKeys, - keyStore: keyStore - ) - } + let decryptedEvents: [ZMUpdateEvent] = try await decryptAndStoreEvents( + events, + startingAtIndex: lastIndex, + publicKeys: publicKeys, + proteusService: proteusService ) if !events.isEmpty { @@ -280,60 +266,6 @@ extension EventDecoder { } } - private func legacyDecryptAndStoreEvents( - _ events: [ZMUpdateEvent], - startingAtIndex startIndex: Int64, - publicKeys: EARPublicKeys?, - keyStore: UserClientKeysStore - ) async -> [ZMUpdateEvent] { - var decryptedEvents = [ZMUpdateEvent]() - - await keyStore.encryptionContext.performAsync { [weak self] sessionsDirectory in - guard let self else { return } - - for event in events { - switch event.type { - case .conversationOtrMessageAdd, .conversationOtrAssetAdd: - let proteusEvent = await decryptProteusEventAndAddClient( - event, - in: syncMOC - ) { sessionID, encryptedData in - try sessionsDirectory.decryptData( - encryptedData, - for: sessionID.mapToEncryptionSessionID() - ) - } - if let proteusEvent { - decryptedEvents.append(proteusEvent) - } - - case .conversationMLSWelcome: - await processWelcomeMessage(from: event, context: syncMOC) - decryptedEvents.append(event) - - case .conversationMLSMessageAdd: - let events = try? await decryptMlsMessage(from: event, context: syncMOC) - decryptedEvents.append(contentsOf: events ?? []) - - default: - decryptedEvents.append(event) - } - } - - // This call has to be synchronous to ensure that we close the - // encryption context only if we stored all events in the database. - await eventMOC.perform { - self.storeUpdateEvents(decryptedEvents, startingAtIndex: startIndex, publicKeys: publicKeys) - } - } - - if let lastEventID = decryptedEvents.last(where: { !$0.isTransient })?.uuid { - lastEventIDRepository.storeLastEventID(lastEventID) - } - - return decryptedEvents - } - // Insert the decrypted events in the event database using a `storeIndex` // incrementing from the highest index currently stored in the database. // The encryptedPayload property is encrypted using the public key. diff --git a/wire-ios-request-strategy/Sources/Synchronization/Decoding/EventDecoderTest.swift b/wire-ios-request-strategy/Sources/Synchronization/Decoding/EventDecoderTest.swift index 41e9c4ed107..35e6d17fe78 100644 --- a/wire-ios-request-strategy/Sources/Synchronization/Decoding/EventDecoderTest.swift +++ b/wire-ios-request-strategy/Sources/Synchronization/Decoding/EventDecoderTest.swift @@ -464,10 +464,8 @@ extension EventDecoderTest { (didCreateNewSession: false, decryptedData: data) } - let event = await syncMOC.perform { - let message = GenericMessage(content: Text(content: "foo")) - return self.encryptedUpdateEventToSelfFromOtherClient(message: message) - } + let message = GenericMessage(content: Text(content: "foo")) + let event = try await encryptedUpdateEventToSelfFromOtherClient(message: message) proteusViaCoreCrypto.isOn = true @@ -502,10 +500,8 @@ extension EventDecoderTest { throw FakeError.decryptionError } - let event = await syncMOC.perform { - let message = GenericMessage(content: Text(content: "foo")) - return self.encryptedUpdateEventToSelfFromOtherClient(message: message) - } + let message = GenericMessage(content: Text(content: "foo")) + let event = try await encryptedUpdateEventToSelfFromOtherClient(message: message) await syncMOC.perform { self.syncMOC.proteusService = mockProteusService @@ -658,29 +654,6 @@ extension EventDecoderTest { ) } - func test_ProteusEventDecryption_Legacy() async throws { - var proteusViaCoreCrypto = DeveloperFlag.proteusViaCoreCrypto - - // Given - let event = await syncMOC.perform { - let message = GenericMessage(content: Text(content: "foo")) - return self.encryptedUpdateEventToSelfFromOtherClient(message: message) - } - - proteusViaCoreCrypto.isOn = false - - // When - let decryptedEvents = try await sut.decryptAndStoreEvents([event]) - XCTAssertEqual(decryptedEvents.count, 1) - - // Then - // We could decrypt, and the proteus service doesn't exist, so it used the keystore. - let proteusService = await syncMOC.perform { self.syncMOC.proteusService } - XCTAssertNil(proteusService) - - XCTAssert(waitForAllGroupsToBeEmpty(withTimeout: 0.5)) - } - } // MARK: - MLS Event Decryption diff --git a/wire-ios-request-strategy/Tests/Helpers/MessagingTest+Encryption.swift b/wire-ios-request-strategy/Tests/Helpers/MessagingTest+Encryption.swift deleted file mode 100644 index cf2e4669991..00000000000 --- a/wire-ios-request-strategy/Tests/Helpers/MessagingTest+Encryption.swift +++ /dev/null @@ -1,144 +0,0 @@ -// -// Wire -// Copyright (C) 2025 Wire Swiss GmbH -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see http://www.gnu.org/licenses/. -// - -import GenericMessageProtocol -import WireCryptobox -import WireDataModel -import WireTesting -import XCTest - -extension MessagingTestBase { - - /// Encrypts a message from the given client to the self user. - /// It will create a session between the two if needed - func encryptedMessageToSelf(message: GenericMessage, from sender: UserClient) -> Data { - - let selfClient = ZMUser.selfUser(in: syncMOC).selfClient()! - if selfClient.user!.remoteIdentifier == nil { - selfClient.user!.remoteIdentifier = UUID() - } - if selfClient.remoteIdentifier == nil { - selfClient.remoteIdentifier = UUID.create().transportString() - } - - var cypherText: Data? - encryptionContext(for: sender).perform { session in - if !session.hasSession(for: selfClient.sessionIdentifier!) { - // swiftlint:disable:next todo_requires_jira_link - // TODO: [John] use flag here - guard let lastPrekey = try? syncMOC.zm_cryptKeyStore.lastPreKey() else { - fatalError("Can't get prekey for self user") - } - try! session.createClientSession(selfClient.sessionIdentifier!, base64PreKeyString: lastPrekey) - } - - do { - cypherText = try session.encrypt(message.serializedData(), for: selfClient.sessionIdentifier!) - } catch { - fatalError("Error in encrypting: \(error)") - } - } - return cypherText! - } - - /// Creates a session between the self client to the given user, if it does not - /// exists already - @objc(establishSessionFromSelfToClient:) - func establishSessionFromSelf(to client: UserClient) { - - // this makes sure the client has remote identifier - _ = encryptionContext(for: client) - - var hasSessionWithSelfClient = false - syncMOC.zm_cryptKeyStore.encryptionContext.perform { sessionsDirectory in - if let id = client.sessionIdentifier { - hasSessionWithSelfClient = sessionsDirectory.hasSession(for: id) - } else { - hasSessionWithSelfClient = false - } - } - - if hasSessionWithSelfClient { - // done! - return - } - - var prekey: String? - encryptionContext(for: client).perform { session in - prekey = try! session.generateLastPrekey() - } - - // swiftlint:disable:next todo_requires_jira_link - // TODO: [John] use flag here - syncMOC.zm_cryptKeyStore.encryptionContext.perform { session in - try! session.createClientSession(client.sessionIdentifier!, base64PreKeyString: prekey!) - } - } - - /// Decrypts a message that was sent from self to a given user - func decryptMessageFromSelf(cypherText: Data, to client: UserClient) -> Data? { - - let selfClient = ZMUser.selfUser(in: syncMOC).selfClient()! - var plainText: Data? - encryptionContext(for: client).perform { session in - if session.hasSession(for: selfClient.sessionIdentifier!) { - do { - plainText = try session.decrypt(cypherText, from: selfClient.sessionIdentifier!) - } catch { - XCTFail("Decryption error: \(error)") - } - } else { - do { - plainText = try session.createClientSessionAndReturnPlaintext( - for: selfClient.sessionIdentifier!, - prekeyMessage: cypherText - ) - } catch { - XCTFail("Decryption error: \(error)") - } - } - } - return plainText - } -} - -extension MessagingTestBase { - - /// Delete all other clients encryption contexts - func deleteAllOtherEncryptionContexts() { - try? FileManager.default.removeItem(at: otherClientsEncryptionContextsURL) - } - - /// Returns the folder where the encryption contexts for other test clients are stored - var otherClientsEncryptionContextsURL: URL { - FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first! - .appendingPathComponent("OtherClients") - } - - /// Returns the encryption context to use for a given client. There are extra cryptobox sessions - /// that simulate a remote client able to decrypt/encrypt data with its own cryptobox instance. - /// If the client has no remote identifier, it will create one - private func encryptionContext(for client: UserClient) -> EncryptionContext { - if client.remoteIdentifier == nil { - client.remoteIdentifier = UUID.create().transportString() - } - let url = otherClientsEncryptionContextsURL.appendingPathComponent("client-\(client.remoteIdentifier!)") - try! FileManager.default.createDirectory(at: url, withIntermediateDirectories: true, attributes: [:]) - return EncryptionContext(path: url) - } -} diff --git a/wire-ios-request-strategy/Tests/Helpers/MessagingTestBase.swift b/wire-ios-request-strategy/Tests/Helpers/MessagingTestBase.swift index 97a3298862d..d9178c01748 100644 --- a/wire-ios-request-strategy/Tests/Helpers/MessagingTestBase.swift +++ b/wire-ios-request-strategy/Tests/Helpers/MessagingTestBase.swift @@ -17,8 +17,9 @@ // import GenericMessageProtocol -import WireCryptobox +import WireCoreCrypto import WireDataModel +import WireDataModelSupport import WireLogging import WireTesting @@ -33,9 +34,11 @@ class MessagingTestBase: ZMTBaseTest { fileprivate(set) var otherUser: ZMUser! fileprivate(set) var thirdUser: ZMUser! fileprivate(set) var otherClient: UserClient! - fileprivate(set) var otherEncryptionContext: EncryptionContext! fileprivate(set) var coreDataStack: CoreDataStack! fileprivate(set) var accountIdentifier: UUID! + fileprivate(set) var coreCrypto: SafeCoreCrypto! + fileprivate(set) var proteusService: ProteusServiceInterface! + fileprivate(set) var proteusClientSimulator: ProteusClientSimulator! let owningDomain = "example.com" @@ -55,61 +58,73 @@ class MessagingTestBase: ZMTBaseTest { coreDataStack.eventContext } - override class func setUp() { - super.setUp() - var flag = DeveloperFlag.proteusViaCoreCrypto - flag.isOn = false - } - override func setUp() async throws { try await super.setUp() BackgroundActivityFactory.shared.activityManager = UIApplication.shared BackgroundActivityFactory.shared.resume() - deleteAllOtherEncryptionContexts() - deleteAllFilesInCache() + // Set up core data stack + accountIdentifier = UUID() try await coreDataStack = createCoreDataStack( userIdentifier: accountIdentifier, inMemoryStore: useInMemoryStore ) + + // Set up proteus client simulator + + let cacheFolder = try XCTUnwrap(FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first) + let storageURL = cacheFolder.appendingPathComponent("OtherClients") + try? FileManager.default.removeItem(at: storageURL) + + proteusClientSimulator = ProteusClientSimulator( + syncMOC: syncMOC, + owningDomain: owningDomain, + storageURL: storageURL + ) + + // Caches, timers and proteus service + await setupCaches(in: coreDataStack) await setupTimers() + try await setupProteusService() - await syncMOC.perform { - self.syncMOC.zm_cryptKeyStore.deleteAndCreateNewBox() + // Set up managed objects - self.setupUsersAndClients() - self.groupConversation = self.createGroupConversation(with: self.otherUser) - self.oneToOneConversation = self.setupOneToOneConversation(with: self.otherUser) - self.oneToOneConnection = self.otherUser.connection - self.syncMOC.saveOrRollback() + await syncMOC.perform { [self] in + setupUsersAndClients() + groupConversation = createGroupConversation(with: otherUser) + oneToOneConversation = setupOneToOneConversation(with: otherUser) + oneToOneConnection = otherUser.connection + syncMOC.saveOrRollback() } + + // Establish session after users/clients are created + try await proteusClientSimulator.establishSessionFromSelf(to: otherClient) } - override func tearDown() { + override func tearDown() async throws { BackgroundActivityFactory.shared.activityManager = nil - _ = waitForAllGroupsToBeEmpty(withTimeout: 10) - - syncMOC.performGroupedAndWait { + await syncMOC.perform { [syncMOC] in + syncMOC?.proteusService = nil self.otherUser = nil self.otherClient = nil self.selfClient = nil self.groupConversation = nil } - stopEphemeralMessageTimers() - - _ = waitForAllGroupsToBeEmpty(withTimeout: 10) - - deleteAllFilesInCache() - deleteAllOtherEncryptionContexts() + await stopEphemeralMessageTimers() + proteusClientSimulator.cleanup() + try coreCrypto.tearDown() + proteusService = nil + coreCrypto = nil accountIdentifier = nil coreDataStack = nil + proteusClientSimulator = nil - super.tearDown() + try await super.tearDown() } } @@ -140,7 +155,7 @@ extension MessagingTestBase { eventDecoder: EventDecoder ) async throws -> ZMUpdateEvent { - let cyphertext = await syncMOC.perform { self.encryptedMessageToSelf(message: message, from: self.otherClient) } + let cyphertext = try await proteusClientSimulator.encryptedMessageToSelf(message: message, from: otherClient) let innerPayload = await syncMOC.perform { [self] in [ "recipient": selfClient.remoteIdentifier!, @@ -166,7 +181,7 @@ extension MessagingTestBase { eventDecoder: EventDecoder ) async throws -> ZMUpdateEvent { - let cyphertext = await syncMOC.perform { self.encryptedMessageToSelf(message: message, from: self.otherClient) } + let cyphertext = try await proteusClientSimulator.encryptedMessageToSelf(message: message, from: otherClient) // Note: [F] added info to make it ZMSLog SafeTypes happy - this event conversation.otr-asset-add is deprecated let innerPayload = await syncMOC.perform { [self] in [ @@ -190,24 +205,26 @@ extension MessagingTestBase { message: GenericMessage, conversation: ZMConversation? = nil, source: ZMUpdateEventSource = .pushNotification - ) -> ZMUpdateEvent { - let cyphertext = encryptedMessageToSelf( + ) async throws -> ZMUpdateEvent { + let cyphertext = try await proteusClientSimulator.encryptedMessageToSelf( message: message, from: otherClient ) - let innerPayload = [ - "recipient": selfClient.remoteIdentifier!, - "sender": otherClient.remoteIdentifier!, - "text": cyphertext.base64String() - ] + return await syncMOC.perform { [self] in + let innerPayload = [ + "recipient": selfClient.remoteIdentifier!, + "sender": otherClient.remoteIdentifier!, + "text": cyphertext.base64String() + ] - return encryptedUpdateEventFromOtherClient( - innerPayload: innerPayload, - conversation: conversation, - source: source, - type: "conversation.otr-message-add" - ) + return encryptedUpdateEventFromOtherClient( + innerPayload: innerPayload, + conversation: conversation, + source: source, + type: "conversation.otr-message-add" + ) + } } /// Creates an update event with encrypted message from the other client, decrypts it and returns it @@ -218,7 +235,9 @@ extension MessagingTestBase { type: String, eventDecoder: EventDecoder ) async throws -> ZMUpdateEvent { - let event = await syncMOC.perform { + let context = try XCTUnwrap(syncMOC) + + let event = await context.perform { self.encryptedUpdateEventFromOtherClient( innerPayload: innerPayload, conversation: conversation, @@ -227,27 +246,22 @@ extension MessagingTestBase { ) } - var decryptedEvent: ZMUpdateEvent? - let proteusProvider = await syncMOC.perform { self.syncMOC.proteusProvider } - await proteusProvider.performAsync(withProteusService: { proteusService in + let proteusService = await context.perform { context.proteusService } - decryptedEvent = await eventDecoder.decryptProteusEventAndAddClient( - event, - in: self.syncMOC - ) { sessionID, encryptedData in - let result = try await proteusService.decrypt(data: encryptedData, forSession: sessionID, context: nil) - return (didCreateNewSession: result.didCreateNewSession, decryptedData: result.decryptedData) - } - }, withKeyStore: { keyStore in - await keyStore.encryptionContext.performAsync { session in - decryptedEvent = await eventDecoder.decryptProteusEventAndAddClient( - event, - in: self.syncMOC - ) { sessionID, encryptedData in - try session.decryptData(encryptedData, for: sessionID.mapToEncryptionSessionID()) - } + let decryptedEvent = await eventDecoder.decryptProteusEventAndAddClient( + event, + in: syncMOC + ) { sessionID, encryptedData in + guard let result = try await proteusService?.decrypt( + data: encryptedData, + forSession: sessionID, + context: nil + ) else { + return nil } - }) + return (didCreateNewSession: result.didCreateNewSession, decryptedData: result.decryptedData) + } + return try XCTUnwrap(decryptedEvent) } @@ -292,7 +306,7 @@ extension MessagingTestBase { for client: UserClient, line: UInt = #line, file: StaticString = #filePath - ) -> GenericMessage? { + ) async throws -> GenericMessage? { guard let data = request.binaryData, let protobuf = try? Proteus_NewOtrMessage(serializedData: data) else { XCTFail("No binary data", file: file, line: line) @@ -311,7 +325,10 @@ extension MessagingTestBase { } // text content - guard let plaintext = decryptMessageFromSelf(cypherText: clientEntry.text, to: otherClient) else { + guard let plaintext = try await proteusClientSimulator.decryptMessageFromSelf( + cypherText: clientEntry.text, + to: otherClient + ) else { XCTFail("failed to decrypt", file: file, line: line) return nil } @@ -445,17 +462,13 @@ extension MessagingTestBase { return team } - /// Creates an encryption context in a temp folder and creates keys + /// Creates users and clients private func setupUsersAndClients() { otherUser = createUser(alsoCreateClient: true) otherClient = otherUser.clients.first! thirdUser = createUser(alsoCreateClient: true) selfClient = createSelfClient() - - syncMOC.saveOrRollback() - - establishSessionFromSelf(to: otherClient) } /// Creates self client and user @@ -480,24 +493,22 @@ extension MessagingTestBase { extension MessagingTestBase { func setupTimers() async { - await syncMOC.perform { - self.syncMOC.zm_createMessageObfuscationTimer() + await syncMOC.perform { [syncMOC] in + syncMOC.zm_createMessageObfuscationTimer() } - await uiMOC.perform { - self.uiMOC.zm_createMessageDeletionTimer() + await uiMOC.perform { [uiMOC] in + uiMOC.zm_createMessageDeletionTimer() } } - func stopEphemeralMessageTimers() { - syncMOC.performGroupedAndWait { - self.syncMOC.zm_teardownMessageObfuscationTimer() + func stopEphemeralMessageTimers() async { + await syncMOC.perform { [syncMOC] in + syncMOC.zm_teardownMessageObfuscationTimer() } - XCTAssert(waitForAllGroupsToBeEmpty(withTimeout: 0.5)) - uiMOC.performGroupedAndWait { - self.uiMOC.zm_teardownMessageDeletionTimer() + await uiMOC.perform { [uiMOC] in + uiMOC.zm_teardownMessageDeletionTimer() } - XCTAssert(waitForAllGroupsToBeEmpty(withTimeout: 0.5)) } } @@ -518,29 +529,6 @@ extension MessagingTestBase { } } -// MARK: - Cache cleaning - -extension MessagingTestBase { - - private var cacheFolder: URL { - FileManager.default.randomCacheURL! - } - - private func deleteAllFilesInCache() { - let files = try? FileManager.default.contentsOfDirectory( - at: cacheFolder, - includingPropertiesForKeys: [URLResourceKey.nameKey] - ) - files?.forEach { - do { - try FileManager.default.removeItem(at: $0) - } catch { - WireLogger.system.error("error deleting file \($0.absoluteString) in cache: \(error)") - } - } - } -} - // MARK: - Payload for message extension MessagingTestBase { @@ -615,3 +603,41 @@ extension MessagingTestBase { } } + +// MARK: - ProteusService Setup + +extension MessagingTestBase { + + func setupProteusService() async throws { + let accountDir = coreDataStack.accountContainer + + let mockKeyMigrationManager = MockCoreCryptoKeyMigrationManagerProtocol() + mockKeyMigrationManager.isKeyRotationNeeded = false + mockKeyMigrationManager.isMigrationToBytesNeeded = false + mockKeyMigrationManager.isMigrationToScopedKeyNeeded = false + + // Create CoreCryptoProvider which handles proper initialization + let coreCryptoProvider = CoreCryptoProvider( + selfUserID: accountIdentifier, + sharedContainerURL: coreDataStack.applicationContainer, + accountDirectory: accountDir, + sharedUserDefaults: UserDefaults.standard, + syncContext: syncMOC, + cryptoboxMigrationManager: CryptoboxMigrationManager(), + coreCryptoKeyMigrationManager: mockKeyMigrationManager, + allowCreation: true, + localDomain: owningDomain + ) + + // Initialize CoreCrypto (this calls proteusInit internally) + coreCrypto = try await coreCryptoProvider.coreCrypto() as? SafeCoreCrypto + + // Create ProteusService with the provider + proteusService = ProteusService(coreCryptoProvider: coreCryptoProvider) + + await syncMOC.perform { [syncMOC, service = self.proteusService] in + syncMOC.proteusService = service + } + } + +} diff --git a/wire-ios-request-strategy/Tests/Helpers/ProteusClientSimulator.swift b/wire-ios-request-strategy/Tests/Helpers/ProteusClientSimulator.swift new file mode 100644 index 00000000000..759f094beee --- /dev/null +++ b/wire-ios-request-strategy/Tests/Helpers/ProteusClientSimulator.swift @@ -0,0 +1,208 @@ +// +// Wire +// Copyright (C) 2025 Wire Swiss GmbH +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see http://www.gnu.org/licenses/. +// + +import GenericMessageProtocol +import WireCoreCrypto +@preconcurrency import WireDataModel +import WireDataModelSupport +import WireTesting +import XCTest + +/// Simulates remote Proteus clients for testing encryption/decryption scenarios. +/// Each simulated client has its own ProteusService and CoreCrypto instance. +final class ProteusClientSimulator { + + private let syncMOC: NSManagedObjectContext + private let owningDomain: String + private let storageURL: URL + + /// Dictionary to store ProteusService instances for simulated remote clients + private var clientServices: [String: (service: ProteusServiceInterface, coreCrypto: SafeCoreCryptoProtocol)] = [:] + + init(syncMOC: NSManagedObjectContext, owningDomain: String, storageURL: URL) { + self.syncMOC = syncMOC + self.owningDomain = owningDomain + self.storageURL = storageURL + } + + /// Gets or creates a ProteusService for a test client (simulating a remote client) + func proteusService(for client: UserClient) async throws -> ProteusServiceInterface { + + // Ensure client has remote identifier and return it + let clientKey = try await syncMOC.perform { [syncMOC, objectID = client.objectID] in + let client = try syncMOC.existingObject(with: objectID) as! UserClient + + guard let remoteIdentifier = client.remoteIdentifier else { + let id = UUID.create().transportString() + client.remoteIdentifier = id + return id + } + + return remoteIdentifier + } + + if let existing = clientServices[clientKey] { + return existing.service + } + + // Create CoreCrypto instance for this client via CoreCryptoProvider + let clientDirectory = storageURL + .appendingPathComponent("client-\(clientKey)") + try FileManager.default.createDirectory( + at: clientDirectory, + withIntermediateDirectories: true, + attributes: [:] + ) + + let mockKeyMigrationManager = MockCoreCryptoKeyMigrationManagerProtocol() + mockKeyMigrationManager.isKeyRotationNeeded = false + mockKeyMigrationManager.isMigrationToBytesNeeded = false + mockKeyMigrationManager.isMigrationToScopedKeyNeeded = false + + let clientUserID = UUID.create() + let coreCryptoProvider = CoreCryptoProvider( + selfUserID: clientUserID, + sharedContainerURL: storageURL, + accountDirectory: clientDirectory, + sharedUserDefaults: UserDefaults.standard, + syncContext: syncMOC, + cryptoboxMigrationManager: CryptoboxMigrationManager(), + coreCryptoKeyMigrationManager: mockKeyMigrationManager, + allowCreation: true, + localDomain: owningDomain + ) + + // Initialize CoreCrypto + let coreCrypto = try await coreCryptoProvider.coreCrypto() + + let proteusService = ProteusService(coreCryptoProvider: coreCryptoProvider) + clientServices[clientKey] = (service: proteusService, coreCrypto: coreCrypto) + + return proteusService + } + + /// Encrypts a message from the given client to the self user using ProteusService + /// It will create a session between the two if needed + func encryptedMessageToSelf(message: GenericMessage, from sender: UserClient) async throws -> Data { + // Ensure sender has remote identifier and get selfSessionID + let selfSessionID = try await syncMOC.perform { [syncMOC] in + let selfClient = try XCTUnwrap(ZMUser.selfUser(in: syncMOC).selfClient()) + let selfUser = try XCTUnwrap(selfClient.user) + + // Ensure self user and client have identifiers + if selfUser.remoteIdentifier == nil { + selfUser.remoteIdentifier = UUID() + } + if selfClient.remoteIdentifier == nil { + selfClient.remoteIdentifier = UUID.create().transportString() + } + + // Ensure sender has remote identifier + if sender.remoteIdentifier == nil { + sender.remoteIdentifier = UUID.create().transportString() + } + + return try XCTUnwrap(selfClient.proteusSessionID) + } + + let senderProteusService = try await proteusService(for: sender) + + // Check if session exists, if not: establish it + if !(await senderProteusService.sessionExists(id: selfSessionID)) { + // Get self's last prekey + let selfProteusService = try await getSelfProteusService() + let lastPrekey = try await selfProteusService.lastPrekey() + + // Establish session from sender to self + try await senderProteusService.establishSession(id: selfSessionID, fromPrekey: lastPrekey) + } + + // Encrypt the message + return try await senderProteusService.encrypt( + data: message.serializedData(), + forSession: selfSessionID + ) + } + + /// Creates a session between the self client to the given user, if it does not + /// exists already + func establishSessionFromSelf(to client: UserClient) async throws { + let selfProteusService = try await getSelfProteusService() + + guard let clientSessionID = await syncMOC.perform({ client.proteusSessionID }) else { + return XCTFail("Client session ID not available") + } + + // Check if session already exists + if await selfProteusService.sessionExists(id: clientSessionID) { + return + } + + // Get prekey from the other client + let clientProteusService = try await proteusService(for: client) + let prekey = try await clientProteusService.lastPrekey() + + // Establish session from self to client + try await selfProteusService.establishSession(id: clientSessionID, fromPrekey: prekey) + } + + /// Decrypts a message from self to a given client using ProteusService + func decryptMessageFromSelf(cypherText: Data, to client: UserClient) async throws -> Data? { + // Get self session ID in context + guard let selfSessionID = await syncMOC.perform({ [syncMOC] in + ZMUser.selfUser(in: syncMOC).selfClient()?.proteusSessionID + }) else { + XCTFail("Self session ID not available") + return nil + } + + let clientProteusService = try await proteusService(for: client) + + do { + let result = try await clientProteusService.decrypt( + data: cypherText, + forSession: selfSessionID, + context: nil + ) + return result.decryptedData + } catch { + XCTFail("Decryption error: \(error)") + return nil + } + } + + /// Delete all simulated client ProteusService instances and their storage + func cleanup() { + // Tear down all CoreCrypto instances + for (_, entry) in clientServices { + try? entry.coreCrypto.tearDown() + } + + clientServices.removeAll() + + // Delete storage + try? FileManager.default.removeItem(at: storageURL) + } + + private func getSelfProteusService() async throws -> ProteusServiceInterface { + let proteusService = await syncMOC.perform { [syncMOC] in + syncMOC.proteusService + } + return try XCTUnwrap(proteusService) + } +} diff --git a/wire-ios-request-strategy/Tests/Sources/Notifications/PushNotifications/ZMLocalNotificationTests_SystemMessage.swift b/wire-ios-request-strategy/Tests/Sources/Notifications/PushNotifications/ZMLocalNotificationTests_SystemMessage.swift index e4fb439af69..ff4cd55871a 100644 --- a/wire-ios-request-strategy/Tests/Sources/Notifications/PushNotifications/ZMLocalNotificationTests_SystemMessage.swift +++ b/wire-ios-request-strategy/Tests/Sources/Notifications/PushNotifications/ZMLocalNotificationTests_SystemMessage.swift @@ -82,36 +82,33 @@ class ZMLocalNotificationTests_SystemMessage: ZMLocalNotificationTests { } } - func testThatItCreatesANotificationForParticipantAdd_Self() { + func testThatItCreatesANotificationForParticipantAdd_Self() throws { // "push.notification.member.join.self" = "%1$@ added you"; // "push.notification.member.join.self.noconversationname" = "%1$@ added you to a conversation"; // given, when - syncMOC.performGroupedAndWait { - let note1 = self.noteForParticipantAdded( + try syncMOC.performGroupedAndWait { + let note1 = try XCTUnwrap(self.noteForParticipantAdded( self.groupConversation, aSender: self.sender, otherUsers: [self.selfUser] - ) - let note2 = self.noteForParticipantAdded( + )) + let note2 = try XCTUnwrap(self.noteForParticipantAdded( self.groupConversationWithoutName, aSender: self.sender, otherUsers: [self.selfUser] - ) - let note3 = self.noteForParticipantAdded( + )) + let note3 = try XCTUnwrap(self.noteForParticipantAdded( self.groupConversation, aSender: self.sender, otherUsers: [self.selfUser, self.otherUser1] - ) + )) // then - XCTAssertNotNil(note1) - XCTAssertNotNil(note2) - XCTAssertNotNil(note3) - XCTAssertEqual(note1!.body, "Super User added you") - XCTAssertEqual(note2!.body, "Super User added you to a conversation") - XCTAssertEqual(note3!.body, "Super User added you") + XCTAssertEqual(note1.body, "Super User added you") + XCTAssertEqual(note2.body, "Super User added you to a conversation") + XCTAssertEqual(note3.body, "Super User added you") } } diff --git a/wire-ios-request-strategy/Tests/Sources/Payloads/Processing/MessageSendingStatusPayloadProcessorTests.swift b/wire-ios-request-strategy/Tests/Sources/Payloads/Processing/MessageSendingStatusPayloadProcessorTests.swift index c67b41a51f4..068c83e8f5c 100644 --- a/wire-ios-request-strategy/Tests/Sources/Payloads/Processing/MessageSendingStatusPayloadProcessorTests.swift +++ b/wire-ios-request-strategy/Tests/Sources/Payloads/Processing/MessageSendingStatusPayloadProcessorTests.swift @@ -112,20 +112,20 @@ final class MessageSendingStatusPayloadProcessorTests: MessagingTestBase { } } - func testThatClientsAreNotMarkedAsMissing_WhenMissingClientsAlreadyHaveASession() async { + func testThatClientsAreNotMarkedAsMissing_WhenMissingClientsAlreadyHaveASession() async throws { // given var message: MockOTREntity! var payload: Payload.MessageSendingStatus! + var userClient: UserClient! await syncMOC.performGrouped { message = MockOTREntity(conversation: self.groupConversation, context: self.syncMOC) let clientID = UUID().transportString() - let userClient = UserClient.fetchUserClient( + userClient = UserClient.fetchUserClient( withRemoteId: clientID, forUser: self.otherUser, createIfNeeded: true )! - self.establishSessionFromSelf(to: userClient) let missing: Payload.ClientListByQualifiedUserID = [ self.domain: @@ -141,6 +141,8 @@ final class MessageSendingStatusPayloadProcessorTests: MessagingTestBase { ) } + try await proteusClientSimulator.establishSessionFromSelf(to: userClient) + // when await sut.updateClientsChanges( from: payload, diff --git a/wire-ios-request-strategy/WireRequestStrategy.xcodeproj/project.pbxproj b/wire-ios-request-strategy/WireRequestStrategy.xcodeproj/project.pbxproj index a1180a36652..44cec563e67 100644 --- a/wire-ios-request-strategy/WireRequestStrategy.xcodeproj/project.pbxproj +++ b/wire-ios-request-strategy/WireRequestStrategy.xcodeproj/project.pbxproj @@ -408,7 +408,7 @@ F18401F12073C26C00E9F4CC /* AssetV3DownloadRequestStrategyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F18401812073BE0800E9F4CC /* AssetV3DownloadRequestStrategyTests.swift */; }; F18401F32073C26C00E9F4CC /* AssetClientMessageRequestStrategyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F18401832073BE0800E9F4CC /* AssetClientMessageRequestStrategyTests.swift */; }; F18401F42073C27200E9F4CC /* LinkPreviewPreprocessorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F18401892073BE0800E9F4CC /* LinkPreviewPreprocessorTests.swift */; }; - F18401FE2073C2EA00E9F4CC /* MessagingTest+Encryption.swift in Sources */ = {isa = PBXBuildFile; fileRef = F18401F82073C2E600E9F4CC /* MessagingTest+Encryption.swift */; }; + F18401FE2073C2EA00E9F4CC /* ProteusClientSimulator.swift in Sources */ = {isa = PBXBuildFile; fileRef = F18401F82073C2E600E9F4CC /* ProteusClientSimulator.swift */; }; F18401FF2073C2EA00E9F4CC /* MessagingTestBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = F18401F62073C2E500E9F4CC /* MessagingTestBase.swift */; }; F18402002073C2EA00E9F4CC /* MockObjects.swift in Sources */ = {isa = PBXBuildFile; fileRef = F18401F92073C2E600E9F4CC /* MockObjects.swift */; }; F18402012073C2EA00E9F4CC /* RequestStrategyTestBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = F18401F72073C2E600E9F4CC /* RequestStrategyTestBase.swift */; }; @@ -924,7 +924,7 @@ F18401992073BE0800E9F4CC /* EncryptionSessionDirectory+UpdateEvents.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "EncryptionSessionDirectory+UpdateEvents.swift"; sourceTree = ""; }; F18401F62073C2E500E9F4CC /* MessagingTestBase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessagingTestBase.swift; sourceTree = ""; }; F18401F72073C2E600E9F4CC /* RequestStrategyTestBase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RequestStrategyTestBase.swift; sourceTree = ""; }; - F18401F82073C2E600E9F4CC /* MessagingTest+Encryption.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "MessagingTest+Encryption.swift"; sourceTree = ""; }; + F18401F82073C2E600E9F4CC /* ProteusClientSimulator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProteusClientSimulator.swift; sourceTree = ""; }; F18401F92073C2E600E9F4CC /* MockObjects.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockObjects.swift; sourceTree = ""; }; F18402022073C2FF00E9F4CC /* animated.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; path = animated.gif; sourceTree = ""; }; F18402032073C2FF00E9F4CC /* video.mp4 */ = {isa = PBXFileReference; lastKnownFileType = file; path = video.mp4; sourceTree = ""; }; @@ -1223,7 +1223,7 @@ isa = PBXGroup; children = ( A90C56EB2685C23400F1007B /* ZMImagePreprocessingTrackerTests.h */, - F18401F82073C2E600E9F4CC /* MessagingTest+Encryption.swift */, + F18401F82073C2E600E9F4CC /* ProteusClientSimulator.swift */, 168D7BBB26F330DE00789960 /* MessagingTest+Payloads.swift */, F18401F62073C2E500E9F4CC /* MessagingTestBase.swift */, F18401F92073C2E600E9F4CC /* MockObjects.swift */, @@ -2708,7 +2708,7 @@ 0693115624F7B17300D14DF5 /* ZMLocalNotificationTests_SystemMessage.swift in Sources */, 63DA33A8286F27DD00818C3C /* MLSEventProcessorTests.swift in Sources */, EE3246042823196100F2A84A /* VoIPPushHelperTests.swift in Sources */, - F18401FE2073C2EA00E9F4CC /* MessagingTest+Encryption.swift in Sources */, + F18401FE2073C2EA00E9F4CC /* ProteusClientSimulator.swift in Sources */, 6308F8AE2A273FBB0072A177 /* BaseFetchMLSGroupInfoActionHandlerTests.swift in Sources */, EE7F022B2A8391C500FE5695 /* ConversationServiceTests.swift in Sources */, 7AEBB679285A16400090B524 /* CountSelfMLSKeyPackagesActionHandlerTests.swift in Sources */, diff --git a/wire-ios-sync-engine/Source/Synchronization/Strategies/PrekeyGenerator.swift b/wire-ios-sync-engine/Source/Synchronization/Strategies/PrekeyGenerator.swift index d70bc57235d..2b8ce9bf48b 100644 --- a/wire-ios-sync-engine/Source/Synchronization/Strategies/PrekeyGenerator.swift +++ b/wire-ios-sync-engine/Source/Synchronization/Strategies/PrekeyGenerator.swift @@ -17,47 +17,24 @@ // import Foundation -import WireCryptobox class PrekeyGenerator { - // This is needed to save ~3 seconds for every unit test run - // as generating 100 keys is an expensive operation - static var _test_overrideNumberOfKeys: UInt16? + let proteusService: ProteusServiceInterface + let keyCount: UInt16 = 100 - let proteusProvider: ProteusProviding - let keyCount: UInt16 = _test_overrideNumberOfKeys ?? 100 - - init(proteusProvider: ProteusProviding) { - self.proteusProvider = proteusProvider + init(proteusService: ProteusServiceInterface) { + self.proteusService = proteusService } func generatePrekeys(startIndex: UInt16 = 0) async throws -> [IdPrekeyTuple] { - try await proteusProvider.performAsync( - withProteusService: { proteusService in - try await proteusService.generatePrekeys(start: startIndex, count: keyCount) - }, - withKeyStore: { keyStore in - try keyStore.generateMoreKeys(keyCount, start: startIndex) - } - ) + try await proteusService.generatePrekeys(start: startIndex, count: keyCount) } func generateLastResortPrekey() async throws -> IdPrekeyTuple { - try await proteusProvider.performAsync( - withProteusService: { proteusService in - ( - id: await proteusService.lastPrekeyID, - prekey: try await proteusService.lastPrekey() - - ) - }, - withKeyStore: { keyStore in - ( - id: CBOX_LAST_PREKEY_ID, - prekey: try keyStore.lastPreKey() - ) - } + ( + id: await proteusService.lastPrekeyID, + prekey: try await proteusService.lastPrekey() ) } } diff --git a/wire-ios-sync-engine/Source/Synchronization/Strategies/UserClientRequestFactory.swift b/wire-ios-sync-engine/Source/Synchronization/Strategies/UserClientRequestFactory.swift index 2bcce595710..2e7b5100755 100644 --- a/wire-ios-sync-engine/Source/Synchronization/Strategies/UserClientRequestFactory.swift +++ b/wire-ios-sync-engine/Source/Synchronization/Strategies/UserClientRequestFactory.swift @@ -17,7 +17,6 @@ // import Foundation -import WireCryptobox import WireDataModel import WireDomain diff --git a/wire-ios-sync-engine/Source/Synchronization/Strategies/UserClientRequestStrategy.swift b/wire-ios-sync-engine/Source/Synchronization/Strategies/UserClientRequestStrategy.swift index c925c8f0af8..85c24fd58ba 100644 --- a/wire-ios-sync-engine/Source/Synchronization/Strategies/UserClientRequestStrategy.swift +++ b/wire-ios-sync-engine/Source/Synchronization/Strategies/UserClientRequestStrategy.swift @@ -17,7 +17,6 @@ // import Foundation -import WireCryptobox import WireDataModel import WireLogging import WireSystem @@ -62,12 +61,12 @@ public final class UserClientRequestStrategy: ZMObjectSyncStrategy, ZMObjectStra clientRegistrationStatus: ZMClientRegistrationStatus, clientUpdateStatus: ClientUpdateStatus, context: NSManagedObjectContext, - proteusProvider: ProteusProviding + proteusService: ProteusServiceInterface ) { self.clientRegistrationStatus = clientRegistrationStatus self.clientUpdateStatus = clientUpdateStatus self.requestsFactory = UserClientRequestFactory() - self.prekeyGenerator = PrekeyGenerator(proteusProvider: proteusProvider) + self.prekeyGenerator = PrekeyGenerator(proteusService: proteusService) super.init(managedObjectContext: context) diff --git a/wire-ios-sync-engine/Source/Synchronization/StrategyDirectory.swift b/wire-ios-sync-engine/Source/Synchronization/StrategyDirectory.swift index 58fe407bf7d..77638606121 100644 --- a/wire-ios-sync-engine/Source/Synchronization/StrategyDirectory.swift +++ b/wire-ios-sync-engine/Source/Synchronization/StrategyDirectory.swift @@ -55,7 +55,7 @@ public class StrategyDirectory: NSObject, StrategyDirectoryProtocol { localNotificationDispatcher: LocalNotificationDispatcher, lastEventIDRepository: LastEventIDRepositoryInterface, transportSession: TransportSessionType, - proteusProvider: ProteusProviding, + proteusService: ProteusServiceInterface, mlsService: MLSServiceInterface, coreCryptoProvider: CoreCryptoProviderProtocol, pullSelfUserClientsFactory: @escaping PullSelfUserClientsFactory, @@ -74,7 +74,7 @@ public class StrategyDirectory: NSObject, StrategyDirectoryProtocol { localNotificationDispatcher: localNotificationDispatcher, lastEventIDRepository: lastEventIDRepository, transportSession: transportSession, - proteusProvider: proteusProvider, + proteusService: proteusService, mlsService: mlsService, coreCryptoProvider: coreCryptoProvider, pullSelfUserClientsFactory: pullSelfUserClientsFactory, @@ -115,7 +115,7 @@ public class StrategyDirectory: NSObject, StrategyDirectoryProtocol { localNotificationDispatcher: LocalNotificationDispatcher, lastEventIDRepository: LastEventIDRepositoryInterface, transportSession: TransportSessionType, - proteusProvider: ProteusProviding, + proteusService: ProteusServiceInterface, mlsService: MLSServiceInterface, coreCryptoProvider: CoreCryptoProviderProtocol, pullSelfUserClientsFactory: @escaping PullSelfUserClientsFactory, @@ -145,7 +145,7 @@ public class StrategyDirectory: NSObject, StrategyDirectoryProtocol { clientRegistrationStatus: applicationStatusDirectory.clientRegistrationStatus, clientUpdateStatus: applicationStatusDirectory.clientUpdateStatus, context: syncMOC, - proteusProvider: proteusProvider + proteusService: proteusService ), ZMMissingUpdateEventsTranscoder( managedObjectContext: syncMOC, diff --git a/wire-ios-sync-engine/Source/Use cases/GetUserClientFingerprintUseCase.swift b/wire-ios-sync-engine/Source/Use cases/GetUserClientFingerprintUseCase.swift index 4dd68c5d17f..1ebe653ce0c 100644 --- a/wire-ios-sync-engine/Source/Use cases/GetUserClientFingerprintUseCase.swift +++ b/wire-ios-sync-engine/Source/Use cases/GetUserClientFingerprintUseCase.swift @@ -27,7 +27,7 @@ public protocol GetUserClientFingerprintUseCaseProtocol { public struct GetUserClientFingerprintUseCase: GetUserClientFingerprintUseCaseProtocol { - let proteusProvider: ProteusProviding + let proteusService: ProteusServiceInterface let context: NSManagedObjectContext let sessionEstablisher: SessionEstablisherInterface let metadata: BackendMetadataProvider @@ -37,7 +37,7 @@ public struct GetUserClientFingerprintUseCase: GetUserClientFingerprintUseCasePr init( syncContext: NSManagedObjectContext, transportSession: TransportSessionType, - proteusProvider: ProteusProviding, + proteusService: ProteusServiceInterface, metadata: BackendMetadataProvider ) { let httpClient = HttpClientImpl( @@ -51,7 +51,7 @@ public struct GetUserClientFingerprintUseCase: GetUserClientFingerprintUseCasePr ) self.init( - proteusProvider: proteusProvider, + proteusService: proteusService, sessionEstablisher: sessionEstablisher, managedObjectContext: syncContext, metadata: metadata @@ -59,12 +59,12 @@ public struct GetUserClientFingerprintUseCase: GetUserClientFingerprintUseCasePr } init( - proteusProvider: ProteusProviding, + proteusService: ProteusServiceInterface, sessionEstablisher: SessionEstablisherInterface, managedObjectContext: NSManagedObjectContext, metadata: BackendMetadataProvider ) { - self.proteusProvider = proteusProvider + self.proteusService = proteusService self.context = managedObjectContext self.sessionEstablisher = sessionEstablisher self.metadata = metadata @@ -100,15 +100,6 @@ public struct GetUserClientFingerprintUseCase: GetUserClientFingerprintUseCasePr existingClient.isSelfClient() } - let canPerform = await context.perform { - proteusProvider.canPerform - } - - guard canPerform else { - WireLogger.proteus.error("cannot get localFingerprint, proteusProvider not ready") - return nil - } - if isSelfClient { return await localFingerprint() } else { @@ -117,24 +108,13 @@ public struct GetUserClientFingerprintUseCase: GetUserClientFingerprintUseCasePr } func localFingerprint() async -> Data? { - var fingerprintData: Data? - - await proteusPerform( - withProteusService: { proteusService in - do { - let fingerprint = try await proteusService.localFingerprint() - fingerprintData = fingerprint.utf8Data - } catch { - WireLogger.proteus.error("Cannot fetch local fingerprint") - } - }, - withKeyStore: { keyStore in - keyStore.encryptionContext.perform { sessionsDirectory in - fingerprintData = sessionsDirectory.localFingerprint - } - } - ) - return fingerprintData + do { + let fingerprint = try await proteusService.localFingerprint() + return fingerprint.utf8Data + } catch { + WireLogger.proteus.error("Cannot fetch local fingerprint") + return nil + } } func fetchRemoteFingerprint(for userClient: UserClient) async -> Data? { @@ -142,33 +122,12 @@ public struct GetUserClientFingerprintUseCase: GetUserClientFingerprintUseCasePr return nil } - var fingerprintData: Data? - - await proteusPerform( - withProteusService: { proteusService in - do { - let fingerprint = try await proteusService.remoteFingerprint(forSession: sessionId) - fingerprintData = fingerprint.utf8Data - } catch { - WireLogger.proteus.error("Cannot fetch remote fingerprint for \(userClient)") - } - }, - withKeyStore: { keyStore in - keyStore.encryptionContext.perform { sessionsDirectory in - fingerprintData = sessionsDirectory.fingerprint(for: sessionId.mapToEncryptionSessionID()) - } - } - ) - - return fingerprintData - } - - // MARK: - Helpers - - private func proteusPerform( - withProteusService proteusServiceBlock: @escaping ProteusServicePerformAsyncBlock, - withKeyStore keyStoreBlock: @escaping KeyStorePerformAsyncBlock - ) async rethrows -> T { - try await proteusProvider.performAsync(withProteusService: proteusServiceBlock, withKeyStore: keyStoreBlock) + do { + let fingerprint = try await proteusService.remoteFingerprint(forSession: sessionId) + return fingerprint.utf8Data + } catch { + WireLogger.proteus.error("Cannot fetch remote fingerprint for \(userClient)") + return nil + } } } diff --git a/wire-ios-sync-engine/Source/UserSession/NSManagedObject+CryptoStack.swift b/wire-ios-sync-engine/Source/UserSession/NSManagedObject+CryptoStack.swift index 59794a00319..3fdd14f41a8 100644 --- a/wire-ios-sync-engine/Source/UserSession/NSManagedObject+CryptoStack.swift +++ b/wire-ios-sync-engine/Source/UserSession/NSManagedObject+CryptoStack.swift @@ -24,11 +24,6 @@ public extension NSManagedObjectContext { @objc func tearDownCryptoStack() { - proteusProvider.perform( - withProteusService: { _ in }, - withKeyStore: { keyStore in keyStore.deleteAndCreateNewBox() } - ) - proteusService = nil mlsService = nil do { diff --git a/wire-ios-sync-engine/Source/UserSession/ZMUserSession/ZMUserSession.swift b/wire-ios-sync-engine/Source/UserSession/ZMUserSession/ZMUserSession.swift index 596e47c0614..a4f67cefef5 100644 --- a/wire-ios-sync-engine/Source/UserSession/ZMUserSession/ZMUserSession.swift +++ b/wire-ios-sync-engine/Source/UserSession/ZMUserSession/ZMUserSession.swift @@ -83,7 +83,6 @@ public final class ZMUserSession: NSObject { private(set) var userId: UUID let proteusService: ProteusServiceInterface private(set) var mlsService: MLSServiceInterface - private(set) var proteusProvider: ProteusProviding! let proteusToMLSMigrationCoordinator: ProteusToMLSMigrationCoordinating public lazy var featureRepository = LegacyFeatureRepository(context: syncContext) @@ -308,7 +307,7 @@ public final class ZMUserSession: NSObject { GetUserClientFingerprintUseCase( syncContext: coreDataStack.syncContext, transportSession: transportSession, - proteusProvider: proteusProvider, + proteusService: proteusService, metadata: resolvedBackendMetadata ) } @@ -558,12 +557,6 @@ public final class ZMUserSession: NSObject { localNotificationDispatcher = LocalNotificationDispatcher(in: coreDataStack.syncContext) configureTransportSession() - // need to be before we create strategies since it is passed - proteusProvider = ProteusProvider( - proteusService: proteusService, - keyStore: syncManagedObjectContext.zm_cryptKeyStore - ) - self.strategyDirectory = strategyDirectory ?? createStrategyDirectory() legacyUpdateEventProcessor = eventProcessor ?? createUpdateEventProcessor() self.syncStrategy = syncStrategy ?? createSyncStrategy() @@ -767,7 +760,7 @@ public final class ZMUserSession: NSObject { localNotificationDispatcher: localNotificationDispatcher!, lastEventIDRepository: lastEventIDRepository, transportSession: transportSession, - proteusProvider: proteusProvider, + proteusService: proteusService, mlsService: mlsService, coreCryptoProvider: coreCryptoProvider, pullSelfUserClientsFactory: { [weak self] context in diff --git a/wire-ios-sync-engine/Tests/Source/E2EE/UserClientRequestStrategyTests.swift b/wire-ios-sync-engine/Tests/Source/E2EE/UserClientRequestStrategyTests.swift index ac53c2e5a62..b26765890ce 100644 --- a/wire-ios-sync-engine/Tests/Source/E2EE/UserClientRequestStrategyTests.swift +++ b/wire-ios-sync-engine/Tests/Source/E2EE/UserClientRequestStrategyTests.swift @@ -65,9 +65,7 @@ final class UserClientRequestStrategyTests: RequestStrategyTestBase { var cookieStorage: ZMPersistentCookieStorage! - var spyKeyStore: SpyUserClientKeyStore! var proteusService: MockProteusServiceInterface! - var proteusProvider: MockProteusProvider! var coreCryptoProvider: MockCoreCryptoProviderProtocol! var postLoginAuthenticationObserverToken: Any? @@ -76,16 +74,9 @@ final class UserClientRequestStrategyTests: RequestStrategyTestBase { super.setUp() syncMOC.performGroupedAndWait { - let spyKeyStore = SpyUserClientKeyStore( - accountDirectory: self.accountDirectory, - applicationContainer: self.sharedContainerURL - ) - self.spyKeyStore = spyKeyStore - self.proteusService = MockProteusServiceInterface() - self.proteusProvider = MockProteusProvider( - mockProteusService: self.proteusService, - mockKeyStore: spyKeyStore - ) + + self.setupProteusService() + self.coreCryptoProvider = MockCoreCryptoProviderProtocol() self.cookieStorage = ZMPersistentCookieStorage( forServerName: "myServer", @@ -106,7 +97,7 @@ final class UserClientRequestStrategyTests: RequestStrategyTestBase { clientRegistrationStatus: self.clientRegistrationStatus, clientUpdateStatus: self.clientUpdateStatus, context: self.syncMOC, - proteusProvider: self.proteusProvider + proteusService: self.proteusService ) let selfUser = ZMUser.selfUser(in: self.syncMOC) selfUser.remoteIdentifier = self.userIdentifier @@ -116,17 +107,30 @@ final class UserClientRequestStrategyTests: RequestStrategyTestBase { } override func tearDown() { - try? FileManager.default.removeItem(at: spyKeyStore.cryptoboxDirectory) - clientRegistrationStatus = nil mockClientRegistrationStatusDelegate = nil clientUpdateStatus = nil - spyKeyStore = nil sut.tearDown() sut = nil postLoginAuthenticationObserverToken = nil super.tearDown() } + + private func setupProteusService() { + proteusService = MockProteusServiceInterface() + proteusService.generatePrekeysStartCount_MockMethod = { start, count async throws in + var prekeys = [IdPrekeyTuple]() + + for index in start ... (start + count) { + prekeys.append((index, "prekey-\(index)")) + } + + return prekeys + } + proteusService.lastPrekey_MockValue = "last-resort-prekey" + proteusService.underlyingLastPrekeyID = UInt16.max + } + } // MARK: Inserting diff --git a/wire-ios-sync-engine/Tests/Source/Use cases/GetUserClientFingerprintUseCaseTests.swift b/wire-ios-sync-engine/Tests/Source/Use cases/GetUserClientFingerprintUseCaseTests.swift index 7bdb60f70b9..08659977084 100644 --- a/wire-ios-sync-engine/Tests/Source/Use cases/GetUserClientFingerprintUseCaseTests.swift +++ b/wire-ios-sync-engine/Tests/Source/Use cases/GetUserClientFingerprintUseCaseTests.swift @@ -24,11 +24,11 @@ import XCTest @testable import WireSyncEngine +@preconcurrency final class GetUserClientFingerprintUseCaseTests: MessagingTest { var sut: GetUserClientFingerprintUseCase! var mockProteusService: MockProteusServiceInterface! - var mockProteusProvider: MockProteusProvider! var mockSessionEstablisher: MockSessionEstablisherInterface! let fingerprint = "1234" @@ -43,7 +43,6 @@ final class GetUserClientFingerprintUseCaseTests: MessagingTest { override func tearDown() { sut = nil mockProteusService = nil - mockProteusProvider = nil mockSessionEstablisher = nil super.tearDown() DeveloperFlag.storage = .standard @@ -68,13 +67,14 @@ final class GetUserClientFingerprintUseCaseTests: MessagingTest { syncMOC.performAndWait { syncMOC.proteusService = mockProteusService } - sut = createSut(proteusEnabled: true) + sut = createSut() mockProteusService.sessionExistsId_MockValue = sessionEstablished - var userClient: UserClient! - await syncMOC.perform { - userClient = self.createSelfClient() + + let userClient = await syncMOC.perform { + let userClient = self.createSelfClient() userClient.user?.domain = "example.com" + return userClient } let expectation = XCTestExpectation(description: "should call establishSession") @@ -92,36 +92,18 @@ final class GetUserClientFingerprintUseCaseTests: MessagingTest { // MARK: - fetchRemoteFingerprint - func test_fetchRemoteFingerprint_with_Cryptobox() async { - // GIVEN - sut = createSut(proteusEnabled: false) - - var userClient: UserClient! - await syncMOC.perform { - userClient = self.createSelfClient() - } - - // WHEN - _ = await sut.fetchRemoteFingerprint(for: userClient) - - // THEN - XCTAssertEqual(mockProteusProvider.mockKeyStore.accessEncryptionContextCount, 1) - } - - func test_fetchRemoteFingerprint_ProteusViaCoreCryptoFlagEnabled() async { + func test_fetchRemoteFingerprint() async { // GIVEN - sut = createSut(proteusEnabled: true) + sut = createSut() - var userClient: UserClient! - await syncMOC.perform { - userClient = self.createSelfClient() + let userClient = await syncMOC.perform { + self.createSelfClient() } // WHEN let result = await sut.fetchRemoteFingerprint(for: userClient) // THEN - XCTAssertEqual(mockProteusProvider.mockKeyStore.accessEncryptionContextCount, 0) XCTAssertEqual(result, fingerprint.data(using: .utf8)) } @@ -130,7 +112,7 @@ final class GetUserClientFingerprintUseCaseTests: MessagingTest { func test_itLoadsLocalFingerprint_ProteusViaCoreCryptoFlagEnabled() async { // GIVEN - sut = createSut(proteusEnabled: true) + sut = createSut() await syncMOC.perform { _ = self.createSelfClient() @@ -146,33 +128,19 @@ final class GetUserClientFingerprintUseCaseTests: MessagingTest { XCTAssertEqual(String(decoding: result, as: UTF8.self), fingerprint) } - func test_itLoadsLocalFingerprint_ProteusViaCoreCryptoFlagDisabled() async { - // GIVEN - sut = createSut(proteusEnabled: false) - - // WHEN - _ = await sut.localFingerprint() - - // THEN - XCTAssertEqual(mockProteusProvider.mockKeyStore.accessEncryptionContextCount, 1) - } - // MARK: - Helpers - private func createSut(proteusEnabled: Bool) -> GetUserClientFingerprintUseCase { - mockProteusProvider = MockProteusProvider( - mockProteusService: mockProteusService, - useProteusService: proteusEnabled - ) - mockProteusProvider.mockProteusService.localFingerprint_MockMethod = { + private func createSut() -> GetUserClientFingerprintUseCase { + + mockProteusService.localFingerprint_MockMethod = { self.fingerprint } - mockProteusProvider.mockProteusService.remoteFingerprintForSession_MockMethod = { _ in + mockProteusService.remoteFingerprintForSession_MockMethod = { _ in self.fingerprint } return GetUserClientFingerprintUseCase( - proteusProvider: mockProteusProvider, + proteusService: mockProteusService, sessionEstablisher: mockSessionEstablisher, managedObjectContext: syncMOC, metadata: .mock() From b9749002c35ba05436bde975e57ac479e6e7aff1 Mon Sep 17 00:00:00 2001 From: David-Henner Date: Mon, 24 Nov 2025 17:37:14 +0100 Subject: [PATCH 02/28] remove dead code --- .../Messages/BaseClientMessageTests.swift | 141 +----------------- .../Messages/ZMAssetClientMessageTests.swift | 17 --- .../ZMClientMessageTests+Deletion.swift | 3 +- .../Model/Messages/ZMClientMessageTests.swift | 2 +- .../Tests/Source/Model/User/ZMUserTests.m | 8 +- .../Model/UserClient/UserClientTests.swift | 81 +--------- .../Source/Model/ZMBaseManagedObjectTest.m | 5 - .../Model/ZMBaseManagedObjectTest.swift | 20 +-- ...GetUserClientFingerprintUseCaseTests.swift | 7 +- 9 files changed, 14 insertions(+), 270 deletions(-) diff --git a/wire-ios-data-model/Tests/Source/Model/Messages/BaseClientMessageTests.swift b/wire-ios-data-model/Tests/Source/Model/Messages/BaseClientMessageTests.swift index 87605f6bfd7..171b584a6aa 100644 --- a/wire-ios-data-model/Tests/Source/Model/Messages/BaseClientMessageTests.swift +++ b/wire-ios-data-model/Tests/Source/Model/Messages/BaseClientMessageTests.swift @@ -26,39 +26,15 @@ class BaseZMClientMessageTests: BaseZMMessageTests { var syncSelfUser: ZMUser! var syncUser1: ZMUser! var syncUser2: ZMUser! - var syncUser3: ZMUser! - var syncSelfClient1: UserClient! - var syncSelfClient2: UserClient! - var syncUser1Client1: UserClient! - var syncUser1Client2: UserClient! - var syncUser2Client1: UserClient! - var syncUser2Client2: UserClient! - var syncUser3Client1: UserClient! - var syncConversation: ZMConversation! - var syncExpectedRecipients: [String: [String]]! var user1: ZMUser! var user2: ZMUser! - var user3: ZMUser! - var selfClient1: UserClient! - var selfClient2: UserClient! - var user1Client1: UserClient! - var user1Client2: UserClient! - var user2Client1: UserClient! - var user2Client2: UserClient! - var user3Client1: UserClient! - var conversation: ZMConversation! - var expectedRecipients: [String: [String]]! - override func setUp() { - DeveloperFlag.storage = UserDefaults(suiteName: UUID().uuidString)! - var flag = DeveloperFlag.proteusViaCoreCrypto - flag.isOn = false super.setUp() @@ -68,63 +44,19 @@ class BaseZMClientMessageTests: BaseZMMessageTests { self.syncSelfClient1 = self.createSelfClient(onMOC: self.syncMOC) self.syncMOC.setPersistentStoreMetadata(self.syncSelfClient1.remoteIdentifier!, key: ZMPersistedClientIdKey) - self.syncSelfClient2 = self.createClient( - for: self.syncSelfUser, - createSessionWithSelfUser: true, - onMOC: self.syncMOC - ) - self.syncUser1 = ZMUser.insertNewObject(in: self.syncMOC) - self.syncUser1Client1 = self.createClient( - for: self.syncUser1, - createSessionWithSelfUser: true, - onMOC: self.syncMOC - ) - self.syncUser1Client2 = self.createClient( - for: self.syncUser1, - createSessionWithSelfUser: true, - onMOC: self.syncMOC - ) - self.syncUser2 = ZMUser.insertNewObject(in: self.syncMOC) - self.syncUser2Client1 = self.createClient( - for: self.syncUser2, - createSessionWithSelfUser: true, - onMOC: self.syncMOC - ) - self.syncUser2Client2 = self.createClient( - for: self.syncUser2, - createSessionWithSelfUser: false, - onMOC: self.syncMOC - ) - - self.syncUser3 = ZMUser.insertNewObject(in: self.syncMOC) - self.syncUser3Client1 = self.createClient( - for: self.syncUser3, - createSessionWithSelfUser: false, - onMOC: self.syncMOC - ) + _ = self.createClient(for: syncUser1, onMOC: syncMOC) + _ = self.createClient(for: syncUser2, onMOC: syncMOC) + self.syncConversation = ZMConversation.insertGroupConversation( moc: self.syncMOC, - participants: [self.syncUser1!, self.syncUser2!, self.syncUser3!] + participants: [self.syncUser1!, self.syncUser2!] ) self.syncConversation.remoteIdentifier = UUID.create() - self.expectedRecipients = [ - self.syncSelfUser.remoteIdentifier!.transportString(): [ - self.syncSelfClient2.remoteIdentifier! - ], - self.syncUser1.remoteIdentifier!.transportString(): [ - self.syncUser1Client1.remoteIdentifier!, - self.syncUser1Client2.remoteIdentifier! - ], - self.syncUser2.remoteIdentifier!.transportString(): [ - self.syncUser2Client1.remoteIdentifier! - ] - ] - self.syncMOC.saveOrRollback() } @@ -134,32 +66,10 @@ class BaseZMClientMessageTests: BaseZMMessageTests { selfClient1 = try! uiMOC.existingObject(with: syncSelfClient1.objectID) as! UserClient uiMOC.setPersistentStoreMetadata(selfClient1.remoteIdentifier!, key: ZMPersistedClientIdKey) - selfClient2 = try! uiMOC.existingObject(with: syncSelfClient2.objectID) as! UserClient - user1 = try! uiMOC.existingObject(with: syncUser1.objectID) as! ZMUser - user1Client1 = try! uiMOC.existingObject(with: syncUser1Client1.objectID) as! UserClient - user1Client2 = try! uiMOC.existingObject(with: syncUser1Client2.objectID) as! UserClient - user2 = try! uiMOC.existingObject(with: syncUser2.objectID) as! ZMUser - user2Client1 = try! uiMOC.existingObject(with: syncUser2Client1.objectID) as! UserClient - user2Client2 = try! uiMOC.existingObject(with: syncUser2Client2.objectID) as! UserClient - - user3 = try! uiMOC.existingObject(with: syncUser3.objectID) as! ZMUser - user3Client1 = try! uiMOC.existingObject(with: syncUser3Client1.objectID) as! UserClient conversation = try! uiMOC.existingObject(with: syncConversation.objectID) as! ZMConversation - expectedRecipients = [ - selfUser.remoteIdentifier!.transportString(): [ - selfClient2.remoteIdentifier! - ], - user1.remoteIdentifier!.transportString(): [ - user1Client1.remoteIdentifier!, - user1Client2.remoteIdentifier! - ], - user2.remoteIdentifier!.transportString(): [ - user2Client1.remoteIdentifier! - ] - ] } override func tearDown() { @@ -170,56 +80,13 @@ class BaseZMClientMessageTests: BaseZMMessageTests { syncSelfUser = nil syncUser1 = nil syncUser2 = nil - syncUser3 = nil - syncSelfClient1 = nil - syncSelfClient2 = nil - syncUser1Client1 = nil - syncUser1Client2 = nil - syncUser2Client1 = nil - syncUser2Client2 = nil - syncUser3Client1 = nil - syncConversation = nil - syncExpectedRecipients = nil - user1 = nil user2 = nil - user3 = nil - selfClient1 = nil - selfClient2 = nil - user1Client1 = nil - user1Client2 = nil - user2Client1 = nil - user2Client2 = nil - user3Client1 = nil - conversation = nil - - expectedRecipients = nil super.tearDown() - DeveloperFlag.storage = UserDefaults.standard - } - - func assertRecipients(_ recipients: [Proteus_UserEntry], file: StaticString = #filePath, line: UInt = #line) { - XCTAssertEqual(recipients.count, expectedRecipients.count, file: file, line: line) - - for recipientEntry in recipients { - guard let uuid = UUID(data: recipientEntry.user.uuid) else { - XCTFail("Missing user UUID", file: file, line: line) - return - } - guard let expectedClientsIds: [String] = expectedRecipients[uuid.transportString()]?.sorted() else { - XCTFail("Unexpected otr client in recipients", file: file, line: line) - return - } - let clientIds = (recipientEntry.clients).map { String(format: "%llx", $0.client.client) }.sorted() - XCTAssertEqual(clientIds, expectedClientsIds, file: file, line: line) - let hasTexts = (recipientEntry.clients).map(\.hasText) - XCTAssertFalse(hasTexts.contains(false), file: file, line: line) - - } } func createUpdateEvent( diff --git a/wire-ios-data-model/Tests/Source/Model/Messages/ZMAssetClientMessageTests.swift b/wire-ios-data-model/Tests/Source/Model/Messages/ZMAssetClientMessageTests.swift index 6e7d794dee7..a596db04bed 100644 --- a/wire-ios-data-model/Tests/Source/Model/Messages/ZMAssetClientMessageTests.swift +++ b/wire-ios-data-model/Tests/Source/Model/Messages/ZMAssetClientMessageTests.swift @@ -784,23 +784,6 @@ extension ZMAssetClientMessageTests { } } -// MARK: Helpers - -extension ZMAssetClientMessageTests { - - func createOtherClientAndConversation() -> (UserClient, ZMConversation) { - let otherUser = ZMUser.insertNewObject(in: syncMOC) - otherUser.remoteIdentifier = .create() - let otherClient = createClient(for: otherUser, createSessionWithSelfUser: true) - let conversation = ZMConversation.insertNewObject(in: syncMOC) - conversation.conversationType = .group - conversation.addParticipantAndUpdateConversationState(user: otherUser, role: nil) - XCTAssertTrue(syncMOC.saveOrRollback()) - - return (otherClient, conversation) - } -} - // MARK: - Associated Task Identifier extension ZMAssetClientMessageTests { diff --git a/wire-ios-data-model/Tests/Source/Model/Messages/ZMClientMessageTests+Deletion.swift b/wire-ios-data-model/Tests/Source/Model/Messages/ZMClientMessageTests+Deletion.swift index 6e6ec13d252..2c749207de5 100644 --- a/wire-ios-data-model/Tests/Source/Model/Messages/ZMClientMessageTests+Deletion.swift +++ b/wire-ios-data-model/Tests/Source/Model/Messages/ZMClientMessageTests+Deletion.swift @@ -565,11 +565,10 @@ extension ZMClientMessageTests_Deletion { ).users.keys // then all users receive delete message - XCTAssertEqual(4, recipients.count) + XCTAssertEqual(3, recipients.count) XCTAssertTrue(recipients.contains(self.syncSelfUser)) XCTAssertTrue(recipients.contains(self.syncUser1)) XCTAssertTrue(recipients.contains(self.syncUser2)) - XCTAssertTrue(recipients.contains(self.syncUser3)) } XCTAssertTrue(waitForAllGroupsToBeEmpty(withTimeout: 0.5)) diff --git a/wire-ios-data-model/Tests/Source/Model/Messages/ZMClientMessageTests.swift b/wire-ios-data-model/Tests/Source/Model/Messages/ZMClientMessageTests.swift index 542b6c100d4..ff8e15e17f9 100644 --- a/wire-ios-data-model/Tests/Source/Model/Messages/ZMClientMessageTests.swift +++ b/wire-ios-data-model/Tests/Source/Model/Messages/ZMClientMessageTests.swift @@ -933,7 +933,7 @@ extension ClientMessageTests { self.createSelfClient(onMOC: self.syncMOC) let otherUser = ZMUser.insertNewObject(in: self.syncMOC) otherUser.remoteIdentifier = UUID.create() - let firstClient = self.createClient(for: otherUser, createSessionWithSelfUser: true, onMOC: self.syncMOC) + let firstClient = self.createClient(for: otherUser, onMOC: self.syncMOC) let messageEvent = self.encryptedExternalMessageFixtureWithBlob(from: firstClient) let base64SHA = "kKSSlbMxXEdd+7fekxB8Qr67/mpjjboBsr2wLcW7wzE=" diff --git a/wire-ios-data-model/Tests/Source/Model/User/ZMUserTests.m b/wire-ios-data-model/Tests/Source/Model/User/ZMUserTests.m index eedef2dbfa0..d2b3a4bf2e9 100644 --- a/wire-ios-data-model/Tests/Source/Model/User/ZMUserTests.m +++ b/wire-ios-data-model/Tests/Source/Model/User/ZMUserTests.m @@ -1209,19 +1209,19 @@ - (void)testThatClientsRequiringUserAttentionContainsUntrustedClientsWithNeedsTo ZMUser *user = [ZMUser selfUserInContext:self.syncMOC]; UserClient *selfClient = [self createSelfClientOnMOC:self.syncMOC]; - UserClient *trustedClient1 = [self createClientForUser:user createSessionWithSelfUser:NO onMOC:self.syncMOC]; + UserClient *trustedClient1 = [self createClientForUser:user onMOC:self.syncMOC]; [selfClient trustClient:trustedClient1]; trustedClient1.needsToNotifyUser = YES; - UserClient *trustedClient2 = [self createClientForUser:user createSessionWithSelfUser:NO onMOC:self.syncMOC]; + UserClient *trustedClient2 = [self createClientForUser:user onMOC:self.syncMOC]; [selfClient trustClient:trustedClient2]; trustedClient2.needsToNotifyUser = NO; - UserClient *ignoredClient1 = [self createClientForUser:user createSessionWithSelfUser:NO onMOC:self.syncMOC]; + UserClient *ignoredClient1 = [self createClientForUser:user onMOC:self.syncMOC]; [selfClient ignoreClient:ignoredClient1]; ignoredClient1.needsToNotifyUser = YES; - UserClient *ignoredClient2 = [self createClientForUser:user createSessionWithSelfUser:NO onMOC:self.syncMOC]; + UserClient *ignoredClient2 = [self createClientForUser:user onMOC:self.syncMOC]; [selfClient ignoreClient:ignoredClient2]; ignoredClient2.needsToNotifyUser = NO; diff --git a/wire-ios-data-model/Tests/Source/Model/UserClient/UserClientTests.swift b/wire-ios-data-model/Tests/Source/Model/UserClient/UserClientTests.swift index cefc4bbfab7..0773c7af682 100644 --- a/wire-ios-data-model/Tests/Source/Model/UserClient/UserClientTests.swift +++ b/wire-ios-data-model/Tests/Source/Model/UserClient/UserClientTests.swift @@ -198,44 +198,6 @@ final class UserClientTests: ZMBaseManagedObjectTest { flag.isOn = false } - func testThatItDeletesASession_Legacy() async throws { - // Given - var selfClient: UserClient! - var otherClient: UserClient! - var preKeys: [(id: UInt16, prekey: String)] = [] - - await syncMOC.performGrouped { - selfClient = self.createSelfClient(onMOC: self.syncMOC) - self.syncMOC.zm_cryptKeyStore.encryptionContext.perform { sessionsDirectory in - preKeys = try! sessionsDirectory.generatePrekeys(0 ..< 2) - } - - otherClient = UserClient.insertNewObject(in: self.syncMOC) - otherClient.remoteIdentifier = UUID.create().transportString() - otherClient.user = ZMUser.insertNewObject(in: self.syncMOC) - otherClient.user?.remoteIdentifier = UUID.create() - } - - guard let preKey = preKeys.first else { - XCTFail("could not generate prekeys") - return - } - - let didEstablishedSession = await selfClient.establishSessionWithClient(otherClient, usingPreKey: preKey.prekey) - let hasSession = await otherClient.hasSessionWithSelfClient - - XCTAssertTrue(didEstablishedSession) - XCTAssertTrue(hasSession) - - // When - try await otherClient.deleteSession() - - // Then - let hasSessionAfterDeletion = await otherClient.hasSessionWithSelfClient - XCTAssertFalse(hasSessionAfterDeletion) - XCTAssert(waitForAllGroupsToBeEmpty(withTimeout: 0.5)) - } - func testThatItDeletesASessionWhenDeletingAClient() async { // Given var flag = DeveloperFlag.proteusViaCoreCrypto @@ -271,47 +233,6 @@ final class UserClientTests: ZMBaseManagedObjectTest { flag.isOn = false } - func testThatItDeletesASessionWhenDeletingAClient_Legacy() async { - // given - var preKeys: [(id: UInt16, prekey: String)] = [] - var selfClient: UserClient! - var otherClient: UserClient! - - await syncMOC.performGrouped { - selfClient = self.createSelfClient(onMOC: self.syncMOC) - self.syncMOC.zm_cryptKeyStore.encryptionContext.perform { sessionsDirectory in - preKeys = try! sessionsDirectory.generatePrekeys(0 ..< 2) - } - - otherClient = UserClient.insertNewObject(in: self.syncMOC) - otherClient.remoteIdentifier = UUID.create().transportString() - let otherUser = ZMUser.insertNewObject(in: self.syncMOC) - otherUser.remoteIdentifier = UUID.create() - otherClient.user = otherUser - } - - guard let preKey = preKeys.first else { - XCTFail("could not generate prekeys") - return - } - - let didEstablishSession = await selfClient.establishSessionWithClient(otherClient, usingPreKey: preKey.prekey) - let hasSession = await otherClient.hasSessionWithSelfClient - XCTAssertTrue(didEstablishSession) - XCTAssertTrue(hasSession) - - // when - await otherClient.deleteClientAndEndSession() - - // then - let hasSessionAfterDeletion = await otherClient.hasSessionWithSelfClient - XCTAssertFalse(hasSessionAfterDeletion) - await syncMOC.perform { - XCTAssertTrue(otherClient.isZombieObject) - } - XCTAssert(waitForAllGroupsToBeEmpty(withTimeout: 0.5)) - } - func testThatItUpdatesConversationSecurityLevelWhenDeletingClient() async { // given var otherUser: ZMUser! @@ -352,7 +273,7 @@ final class UserClientTests: ZMBaseManagedObjectTest { // when await otherClient2.deleteClientAndEndSession() - _ = await syncMOC.perform { self.syncMOC.saveOrRollback() } + _ = await syncMOC.perform { [syncMOC] in syncMOC.saveOrRollback() } // then await syncMOC.performGrouped { diff --git a/wire-ios-data-model/Tests/Source/Model/ZMBaseManagedObjectTest.m b/wire-ios-data-model/Tests/Source/Model/ZMBaseManagedObjectTest.m index 93c9d8b1448..dd67a6515df 100644 --- a/wire-ios-data-model/Tests/Source/Model/ZMBaseManagedObjectTest.m +++ b/wire-ios-data-model/Tests/Source/Model/ZMBaseManagedObjectTest.m @@ -257,11 +257,6 @@ - (UserClient *)createSelfClientOnMOC:(NSManagedObjectContext *)moc return selfClient; } -- (UserClient *)createClientForUser:(ZMUser *)user createSessionWithSelfUser:(BOOL)createSessionWithSelfUser -{ - return [self createClientForUser:user createSessionWithSelfUser:createSessionWithSelfUser onMOC:self.uiMOC]; -} - @end diff --git a/wire-ios-data-model/Tests/Source/Model/ZMBaseManagedObjectTest.swift b/wire-ios-data-model/Tests/Source/Model/ZMBaseManagedObjectTest.swift index de95645d860..ce8a4f95336 100644 --- a/wire-ios-data-model/Tests/Source/Model/ZMBaseManagedObjectTest.swift +++ b/wire-ios-data-model/Tests/Source/Model/ZMBaseManagedObjectTest.swift @@ -20,6 +20,7 @@ import GenericMessageProtocol import XCTest @testable import WireDataModel +@testable import WireDataModelSupport extension ZMBaseManagedObjectTest { @@ -49,10 +50,9 @@ extension ZMBaseManagedObjectTest { return message } - @objc(createClientForUser:createSessionWithSelfUser:onMOC:) + @objc(createClientForUser:onMOC:) func createClient( for user: ZMUser, - createSessionWithSelfUser: Bool, onMOC moc: NSManagedObjectContext ) -> UserClient { if user.remoteIdentifier == nil { @@ -63,22 +63,6 @@ extension ZMBaseManagedObjectTest { userClient.remoteIdentifier = String.randomClientIdentifier() userClient.user = user - if createSessionWithSelfUser { - let selfClient = ZMUser.selfUser(in: moc).selfClient() - performPretendingUiMocIsSyncMoc { - do { - let prekey = try moc.zm_cryptKeyStore.lastPreKey() - let selfClient = try XCTUnwrap(selfClient) - _ = selfClient.establishSession( - through: moc.zm_cryptKeyStore, - sessionId: userClient.sessionIdentifier!, - preKey: prekey - ) - } catch { - XCTFail("unexpected error: \(String(reflecting: error))") - } - } - } return userClient } diff --git a/wire-ios-sync-engine/Tests/Source/Use cases/GetUserClientFingerprintUseCaseTests.swift b/wire-ios-sync-engine/Tests/Source/Use cases/GetUserClientFingerprintUseCaseTests.swift index 08659977084..bd1172563ac 100644 --- a/wire-ios-sync-engine/Tests/Source/Use cases/GetUserClientFingerprintUseCaseTests.swift +++ b/wire-ios-sync-engine/Tests/Source/Use cases/GetUserClientFingerprintUseCaseTests.swift @@ -34,7 +34,6 @@ final class GetUserClientFingerprintUseCaseTests: MessagingTest { let fingerprint = "1234" override func setUp() { - DeveloperFlag.storage = .temporary() mockProteusService = MockProteusServiceInterface() mockSessionEstablisher = MockSessionEstablisherInterface() super.setUp() @@ -45,7 +44,6 @@ final class GetUserClientFingerprintUseCaseTests: MessagingTest { mockProteusService = nil mockSessionEstablisher = nil super.tearDown() - DeveloperFlag.storage = .standard } // MARK: - invoke() establishSession @@ -60,10 +58,7 @@ final class GetUserClientFingerprintUseCaseTests: MessagingTest { func internalTestEstablishSession(sessionEstablished: Bool) async { // GIVEN - // we force the flag on here, - // since ProteusProvider is created on the fly when accessed by managedObjectContext - // when checking the hasSessionWithSelfClient - DeveloperFlag.proteusViaCoreCrypto.enable(true) + syncMOC.performAndWait { syncMOC.proteusService = mockProteusService } From f8b7b6237e1022d736ee9922a15be403f75d6548 Mon Sep 17 00:00:00 2001 From: David-Henner Date: Mon, 24 Nov 2025 17:37:29 +0100 Subject: [PATCH 03/28] fix legal hold tests --- .../Model/User/ZMUserLegalHoldTests.swift | 39 ++++++++++++------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/wire-ios-data-model/Tests/Source/Model/User/ZMUserLegalHoldTests.swift b/wire-ios-data-model/Tests/Source/Model/User/ZMUserLegalHoldTests.swift index 09bff4a09ba..561392fcef4 100644 --- a/wire-ios-data-model/Tests/Source/Model/User/ZMUserLegalHoldTests.swift +++ b/wire-ios-data-model/Tests/Source/Model/User/ZMUserLegalHoldTests.swift @@ -17,8 +17,11 @@ // import XCTest -@testable import WireDataModel +import WireDataModelSupport +@preconcurrency @testable import WireDataModel + +@preconcurrency class ZMUserLegalHoldTests: ModelObjectsTests { override func setUp() { @@ -82,29 +85,37 @@ class ZMUserLegalHoldTests: ModelObjectsTests { XCTAssertTrue(selfUser.needsToAcknowledgeLegalHoldStatus) } - func testThatLegalHoldStatusIsEnabled_AfterAcceptingRequest() async { + func testThatLegalHoldStatusIsEnabled_AfterAcceptingRequest() async throws { // GIVEN - var legalHoldRequest: LegalHoldRequest! - var selfUser: ZMUser! - var conversation: ZMConversation! - - await syncMOC.perform { [self] in - selfUser = ZMUser.selfUser(in: syncMOC) + + let (legalHoldRequest, selfUser, conversationOID) = await syncMOC.perform { [self, syncMOC] in + + let mockProteusService = MockProteusServiceInterface() + mockProteusService.establishSessionIdFromPrekey_MockMethod = { _, _ in } + syncMOC.proteusService = mockProteusService + + let selfUser = ZMUser.selfUser(in: syncMOC) createSelfClient(onMOC: syncMOC) - conversation = createConversation(in: syncMOC) + let conversation = createConversation(in: syncMOC) conversation.addParticipantAndUpdateConversationState(user: selfUser, role: nil) - legalHoldRequest = LegalHoldRequest.mockRequest(for: selfUser) + let legalHoldRequest = LegalHoldRequest.mockRequest(for: selfUser) selfUser.userDidReceiveLegalHoldRequest(legalHoldRequest) + + return (legalHoldRequest, selfUser, conversation.objectID) } // WHEN _ = await selfUser.addLegalHoldClient(from: legalHoldRequest) - await syncMOC.perform { selfUser.userDidAcceptLegalHoldRequest(legalHoldRequest) } - - // THEN - await syncMOC.perform { + + try await syncMOC.perform { [selfUserOID = selfUser.objectID, syncMOC] in + let selfUser = try syncMOC.existingObject(with: selfUserOID) as! ZMUser + let conversation = try syncMOC.existingObject(with: conversationOID) as! ZMConversation + + selfUser.userDidAcceptLegalHoldRequest(legalHoldRequest) + + // THEN XCTAssertEqual(selfUser.legalHoldStatus, .enabled) XCTAssertTrue(selfUser.needsToAcknowledgeLegalHoldStatus) XCTAssertTrue( From ecd6b2830b127adbfce86ede3436ae31f7af352c Mon Sep 17 00:00:00 2001 From: David-Henner Date: Mon, 24 Nov 2025 17:41:53 +0100 Subject: [PATCH 04/28] format --- .../Model/Messages/BaseClientMessageTests.swift | 2 +- .../Source/Model/User/ZMUserLegalHoldTests.swift | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/wire-ios-data-model/Tests/Source/Model/Messages/BaseClientMessageTests.swift b/wire-ios-data-model/Tests/Source/Model/Messages/BaseClientMessageTests.swift index 171b584a6aa..a70fde98e61 100644 --- a/wire-ios-data-model/Tests/Source/Model/Messages/BaseClientMessageTests.swift +++ b/wire-ios-data-model/Tests/Source/Model/Messages/BaseClientMessageTests.swift @@ -49,7 +49,7 @@ class BaseZMClientMessageTests: BaseZMMessageTests { _ = self.createClient(for: syncUser1, onMOC: syncMOC) _ = self.createClient(for: syncUser2, onMOC: syncMOC) - + self.syncConversation = ZMConversation.insertGroupConversation( moc: self.syncMOC, participants: [self.syncUser1!, self.syncUser2!] diff --git a/wire-ios-data-model/Tests/Source/Model/User/ZMUserLegalHoldTests.swift b/wire-ios-data-model/Tests/Source/Model/User/ZMUserLegalHoldTests.swift index 561392fcef4..328d6873ab4 100644 --- a/wire-ios-data-model/Tests/Source/Model/User/ZMUserLegalHoldTests.swift +++ b/wire-ios-data-model/Tests/Source/Model/User/ZMUserLegalHoldTests.swift @@ -16,8 +16,8 @@ // along with this program. If not, see http://www.gnu.org/licenses/. // -import XCTest import WireDataModelSupport +import XCTest @preconcurrency @testable import WireDataModel @@ -87,9 +87,9 @@ class ZMUserLegalHoldTests: ModelObjectsTests { func testThatLegalHoldStatusIsEnabled_AfterAcceptingRequest() async throws { // GIVEN - + let (legalHoldRequest, selfUser, conversationOID) = await syncMOC.perform { [self, syncMOC] in - + let mockProteusService = MockProteusServiceInterface() mockProteusService.establishSessionIdFromPrekey_MockMethod = { _, _ in } syncMOC.proteusService = mockProteusService @@ -102,19 +102,19 @@ class ZMUserLegalHoldTests: ModelObjectsTests { let legalHoldRequest = LegalHoldRequest.mockRequest(for: selfUser) selfUser.userDidReceiveLegalHoldRequest(legalHoldRequest) - + return (legalHoldRequest, selfUser, conversation.objectID) } // WHEN _ = await selfUser.addLegalHoldClient(from: legalHoldRequest) - + try await syncMOC.perform { [selfUserOID = selfUser.objectID, syncMOC] in let selfUser = try syncMOC.existingObject(with: selfUserOID) as! ZMUser let conversation = try syncMOC.existingObject(with: conversationOID) as! ZMConversation - + selfUser.userDidAcceptLegalHoldRequest(legalHoldRequest) - + // THEN XCTAssertEqual(selfUser.legalHoldStatus, .enabled) XCTAssertTrue(selfUser.needsToAcknowledgeLegalHoldStatus) From c93bc5719d5c3002ee179d9795768104ab4ce1e5 Mon Sep 17 00:00:00 2001 From: David-Henner Date: Tue, 25 Nov 2025 11:50:36 +0100 Subject: [PATCH 05/28] fix tests --- .../generated/AutoMockable.generated.swift | 24 +++++++++++ .../E2EE/UserClientEventConsumerTests.swift | 28 ++++--------- .../Synchronization/EventProcessorTests.swift | 42 ++++++++++--------- 3 files changed, 53 insertions(+), 41 deletions(-) diff --git a/WireDomain/Sources/WireDomainSupport/Sourcery/generated/AutoMockable.generated.swift b/WireDomain/Sources/WireDomainSupport/Sourcery/generated/AutoMockable.generated.swift index 40393c06ba9..3570d5415ab 100644 --- a/WireDomain/Sources/WireDomainSupport/Sourcery/generated/AutoMockable.generated.swift +++ b/WireDomain/Sources/WireDomainSupport/Sourcery/generated/AutoMockable.generated.swift @@ -82,6 +82,30 @@ class MockAppExtensionPushChannelCoordinatorProtocol: AppExtensionPushChannelCoo } +public class MockAssetTransferStateResolverProtocol: AssetTransferStateResolverProtocol { + + // MARK: - Life cycle + + public init() {} + + + // MARK: - resolveTransferState + + public var resolveTransferStateAssetMessageGenericMessageContext_Invocations: [(assetMessage: ZMAssetClientMessage, genericMessage: GenericMessage, context: NSManagedObjectContext)] = [] + public var resolveTransferStateAssetMessageGenericMessageContext_MockMethod: ((ZMAssetClientMessage, GenericMessage, NSManagedObjectContext) -> Void)? + + public func resolveTransferState(assetMessage: ZMAssetClientMessage, genericMessage: GenericMessage, context: NSManagedObjectContext) { + resolveTransferStateAssetMessageGenericMessageContext_Invocations.append((assetMessage: assetMessage, genericMessage: genericMessage, context: context)) + + guard let mock = resolveTransferStateAssetMessageGenericMessageContext_MockMethod else { + fatalError("no mock for `resolveTransferStateAssetMessageGenericMessageContext`") + } + + mock(assetMessage, genericMessage, context) + } + +} + public class MockBackendConfigLocalStoreProtocol: BackendConfigLocalStoreProtocol { // MARK: - Life cycle diff --git a/wire-ios-sync-engine/Tests/Source/E2EE/UserClientEventConsumerTests.swift b/wire-ios-sync-engine/Tests/Source/E2EE/UserClientEventConsumerTests.swift index e3e6c4fe79f..411e1ffbe03 100644 --- a/wire-ios-sync-engine/Tests/Source/E2EE/UserClientEventConsumerTests.swift +++ b/wire-ios-sync-engine/Tests/Source/E2EE/UserClientEventConsumerTests.swift @@ -273,23 +273,18 @@ final class UserClientEventConsumerTests: RequestStrategyTestBase { XCTAssertEqual(resolveOneOnOneConversations.invoke_Invocations.count, 1) } - func testThatItInvalidatesTheCurrentSelfClientAndWipeCryptoBoxWhenReceivingAPush() async { + func testThatItInvalidatesTheCurrentSelfClient_AndWipesCryptoStack_WhenReceivingAPush() async { var event: ZMUpdateEvent? - var fingerprint: Data? var selfUser: ZMUser! - var previousLastPrekey: String? await syncMOC.performGrouped { // given selfUser = ZMUser.selfUser(in: self.syncMOC) let existingClient = self.createSelfClient() - // swiftlint:disable:next todo_requires_jira_link - // TODO: [John] use flag here - self.syncMOC.zm_cryptKeyStore.encryptionContext.perform { sessionsDirectory in - fingerprint = sessionsDirectory.localFingerprint - } - previousLastPrekey = try? self.syncMOC.zm_cryptKeyStore.lastPreKey() + self.syncMOC.proteusService = MockProteusServiceInterface() + self.syncMOC.mlsService = MockMLSServiceInterface() + self.syncMOC.coreCrypto = MockSafeCoreCrypto(coreCrypto: MockCoreCryptoProtocol()) XCTAssertEqual(selfUser.clients.count, 1) let payload: [String: Any] = [ @@ -310,20 +305,11 @@ final class UserClientEventConsumerTests: RequestStrategyTestBase { await syncMOC.performGrouped { // then - var newFingerprint: Data? - self.syncMOC.zm_cryptKeyStore.encryptionContext.perform { sessionsDirectory in - newFingerprint = sessionsDirectory.localFingerprint - } - let newLastPrekey = try? self.syncMOC.zm_cryptKeyStore.lastPreKey() - - XCTAssertNotNil(fingerprint) - XCTAssertNotNil(newFingerprint) - XCTAssertNotEqual(fingerprint, newFingerprint) XCTAssertNil(selfUser.clients.first?.remoteIdentifier) XCTAssertNil(self.syncMOC.persistentStoreMetadata(forKey: ZMPersistedClientIdKey)) - XCTAssertNotNil(fingerprint) - XCTAssertNotNil(newFingerprint) - XCTAssertNotEqual(previousLastPrekey, newLastPrekey) + XCTAssertNil(self.syncMOC.proteusService) + XCTAssertNil(self.syncMOC.mlsService) + XCTAssertNil(self.syncMOC.coreCrypto) } } diff --git a/wire-ios-sync-engine/Tests/Source/Synchronization/EventProcessorTests.swift b/wire-ios-sync-engine/Tests/Source/Synchronization/EventProcessorTests.swift index 158a73e3f7e..239b3d31d03 100644 --- a/wire-ios-sync-engine/Tests/Source/Synchronization/EventProcessorTests.swift +++ b/wire-ios-sync-engine/Tests/Source/Synchronization/EventProcessorTests.swift @@ -31,7 +31,8 @@ final class EventProcessorTests: MessagingTest { var mockStrategyDirectory: MockStrategyDirectory! var mockEventsConsumers: [MockEventConsumer]! var mockEventAsyncConsumers: [MockEventAsyncConsumer]! - var earService: MockEARServiceInterface! + var mockEarService: MockEARServiceInterface! + var mockEventDecoder: MockEventDecoderProtocol! override func setUp() { super.setUp() @@ -48,18 +49,19 @@ final class EventProcessorTests: MessagingTest { eventProcessingTracker = EventProcessingTracker() - earService = MockEARServiceInterface() - earService.fetchPublicKeys_MockError = MockError() - earService.fetchPrivateKeysIncludingPrimary_MockError = MockError() + mockEarService = MockEARServiceInterface() + mockEarService.fetchPublicKeys_MockError = MockError() + mockEarService.fetchPrivateKeysIncludingPrimary_MockError = MockError() + + mockEventDecoder = MockEventDecoderProtocol() sut = EventProcessor( storeProvider: coreDataStack, + eventDecoder: mockEventDecoder, eventProcessingTracker: eventProcessingTracker, - earService: earService, - lastEventIDRepository: lastEventIDRepository, + earService: mockEarService, strategyDirectory: mockStrategyDirectory, additionalEventConsumers: [], - isFederationEnabled: false ) } @@ -68,7 +70,7 @@ final class EventProcessorTests: MessagingTest { mockEventsConsumers = nil mockEventAsyncConsumers = nil eventProcessingTracker = nil - earService = nil + mockEarService = nil sut = nil super.tearDown() @@ -113,6 +115,10 @@ final class EventProcessorTests: MessagingTest { func testThatEventsAreForwardedToAllEventConsumers_WhenProcessed() async throws { // given let events = createSampleEvents() + mockEventDecoder.decryptAndStoreEventsPublicKeys_MockValue = events + mockEventDecoder.processStoredEventsWithCallEventsOnly_MockMethod = { _, _, processBlock in + await processBlock(events) + } // when try await sut.processEvents(events) @@ -159,12 +165,14 @@ final class EventProcessorTests: MessagingTest { authenticationContext: MockAuthenticationContextProtocol() ) earService.setInitialEARFlagValue(true) - try syncMOC.performAndWait { + + try await syncMOC.perform { [syncMOC] in try earService.enableEncryptionAtRest( context: syncMOC, skipMigration: true ) } + earService.lockDatabase() let events = createSampleEvents() @@ -185,6 +193,10 @@ final class EventProcessorTests: MessagingTest { let events = createSampleEvents() await sut.bufferEvents(events) XCTAssertTrue(waitForAllGroupsToBeEmpty(withTimeout: 0.5)) + mockEventDecoder.decryptAndStoreEventsPublicKeys_MockValue = events + mockEventDecoder.processStoredEventsWithCallEventsOnly_MockMethod = { _, _, processBlock in + await processBlock(events) + } // when try await sut.processBufferedEvents() @@ -223,24 +235,14 @@ final class EventProcessorTests: MessagingTest { func testItProcessesEventsOnlyOnce() async throws { // Given let events = createSampleEvents() - let eventDecoder = MockEventDecoderProtocol() // Simulate trying to process same events multiple times. - eventDecoder.processStoredEventsWithCallEventsOnly_MockMethod = { _, _, processBlock in + mockEventDecoder.processStoredEventsWithCallEventsOnly_MockMethod = { _, _, processBlock in await processBlock(events) await processBlock(events) await processBlock(events) } - sut = EventProcessor( - storeProvider: coreDataStack, - eventDecoder: eventDecoder, - eventProcessingTracker: eventProcessingTracker, - earService: earService, - strategyDirectory: mockStrategyDirectory, - additionalEventConsumers: [] - ) - // When await sut.processStoredUpdateEvents() From 37d02a7105db4371f7659077a9a74af884f6f713 Mon Sep 17 00:00:00 2001 From: David-Henner Date: Tue, 25 Nov 2025 12:12:57 +0100 Subject: [PATCH 06/28] chore(cryptobox): remove proteusViaCoreCrypto flag - WPB-14604 --- .../ManagedObjectContext/CoreDataStack.swift | 7 ----- .../Proteus/CryptoboxMigrationManager.swift | 1 - .../CryptoboxMigrationManagerTests.swift | 27 +++---------------- .../ZMConversationLastMessagesTest.swift | 13 --------- .../Model/Messages/CacheAssetTests.swift | 12 --------- ...geTests+MLSEncryptedPayloadGenerator.swift | 13 --------- .../Model/Messages/ZMClientMessageTests.swift | 12 --------- .../Tests/Source/Model/PermissionsTests.swift | 12 --------- .../Source/Model/TeamDeletionRuleTests.swift | 12 --------- .../Source/Model/TextSearchQueryTests.swift | 12 --------- .../Model/User/ZMUserLegalHoldTests.swift | 12 --------- .../Model/UserClient/UserClientTests.swift | 19 ------------- .../Model/Utils/ProtobufUtilitiesTests.swift | 12 --------- .../Sources/NotificationSession.swift | 2 +- .../MessageInfoExtractorTests.swift | 1 - .../ProteusMessagePayloadBuilderTests.swift | 1 - .../Decoding/EventDecoderTest.swift | 11 -------- .../Sources/SharingSession.swift | 2 +- .../Source/Integration/IntegrationTest.swift | 5 ---- .../Tests/Source/MessagingTest+Swift.swift | 3 --- .../CallingRequestStrategyTests.swift | 7 ----- ...GetUserClientFingerprintUseCaseTests.swift | 2 +- wire-ios-utilities/Source/DeveloperFlag.swift | 6 ----- wire-ios-utilities/WireUtilities-Info.plist | 2 -- .../Configuration/Developer-Flags.xcconfig | 1 - .../AVAsset/AVURLAsset+conversionTests.swift | 12 --------- 26 files changed, 6 insertions(+), 213 deletions(-) diff --git a/wire-ios-data-model/Source/ManagedObjectContext/CoreDataStack.swift b/wire-ios-data-model/Source/ManagedObjectContext/CoreDataStack.swift index a0218f0e2cd..d506c80a340 100644 --- a/wire-ios-data-model/Source/ManagedObjectContext/CoreDataStack.swift +++ b/wire-ios-data-model/Source/ManagedObjectContext/CoreDataStack.swift @@ -401,13 +401,6 @@ public class CoreDataStack: NSObject, CoreDataStackProtocol { context.accountDirectoryURL = self.accountContainer context.applicationContainerURL = self.applicationContainer - if !DeveloperFlag.proteusViaCoreCrypto.isOn { - context.setupUserKeyStore( - accountDirectory: self.accountContainer, - applicationContainer: self.applicationContainer - ) - } - context.undoManager = nil context.mergePolicy = NSMergePolicy(merge: .mergeByPropertyObjectTrumpMergePolicyType) diff --git a/wire-ios-data-model/Source/Proteus/CryptoboxMigrationManager.swift b/wire-ios-data-model/Source/Proteus/CryptoboxMigrationManager.swift index 14210c11202..8a7d0737fda 100644 --- a/wire-ios-data-model/Source/Proteus/CryptoboxMigrationManager.swift +++ b/wire-ios-data-model/Source/Proteus/CryptoboxMigrationManager.swift @@ -60,7 +60,6 @@ public class CryptoboxMigrationManager: CryptoboxMigrationManagerInterface { // MARK: - Methods public func isMigrationNeeded(accountDirectory: URL) -> Bool { - guard DeveloperFlag.proteusViaCoreCrypto.isOn else { return false } let cryptoboxDirectory = fileManager.cryptoboxDirectory(in: accountDirectory) return fileManager.fileExists(atPath: cryptoboxDirectory.path) } diff --git a/wire-ios-data-model/Tests/Proteus/CryptoboxMigrationManagerTests.swift b/wire-ios-data-model/Tests/Proteus/CryptoboxMigrationManagerTests.swift index ec8757a9192..853c033da8f 100644 --- a/wire-ios-data-model/Tests/Proteus/CryptoboxMigrationManagerTests.swift +++ b/wire-ios-data-model/Tests/Proteus/CryptoboxMigrationManagerTests.swift @@ -27,13 +27,10 @@ class CryptoboxMigrationManagerTests: ZMBaseManagedObjectTest { var sut: CryptoboxMigrationManager! var mockFileManager: MockFileManagerInterface! - var proteusViaCoreCryptoFlag: DeveloperFlag! var mockSafeCoreCrypto: MockSafeCoreCrypto! override func setUp() { super.setUp() - DeveloperFlag.storage = UserDefaults(suiteName: UUID().uuidString)! - proteusViaCoreCryptoFlag = .proteusViaCoreCrypto mockFileManager = MockFileManagerInterface() mockFileManager.cryptoboxDirectoryIn_MockValue = cryptoboxDirectory @@ -51,9 +48,6 @@ class CryptoboxMigrationManagerTests: ZMBaseManagedObjectTest { syncMOC.proteusService = nil } - proteusViaCoreCryptoFlag.isOn = false - DeveloperFlag.storage = UserDefaults.standard - super.tearDown() } @@ -67,10 +61,9 @@ class CryptoboxMigrationManagerTests: ZMBaseManagedObjectTest { // MARK: - Verifying if migration is needed - func test_IsMigrationNeeded_FilesExistAndFlagIsOn() { + func test_IsMigrationNeeded_FilesExist() { // Given mockFileManager.fileExistsAtPath_MockValue = true - proteusViaCoreCryptoFlag.isOn = true // When let result = sut.isMigrationNeeded(accountDirectory: accountDirectory) @@ -79,22 +72,9 @@ class CryptoboxMigrationManagerTests: ZMBaseManagedObjectTest { XCTAssertTrue(result) } - func test_IsMigrationNeeded_FilesExistAndFlagIsOff() { - // Given - mockFileManager.fileExistsAtPath_MockValue = true - proteusViaCoreCryptoFlag.isOn = false - - // When - let result = sut.isMigrationNeeded(accountDirectory: accountDirectory) - - // Then - XCTAssertFalse(result) - } - - func test_IsMigrationNeeded_FilesDoNotExistAndFlagIsOn() { + func test_IsMigrationNeeded_FilesDoNotExist() { // Given mockFileManager.fileExistsAtPath_MockValue = false - proteusViaCoreCryptoFlag.isOn = true // When let result = sut.isMigrationNeeded(accountDirectory: accountDirectory) @@ -109,7 +89,7 @@ class CryptoboxMigrationManagerTests: ZMBaseManagedObjectTest { // Given let migrated = customExpectation(description: "Cryptobox was migrated") mockFileManager.fileExistsAtPath_MockValue = true - proteusViaCoreCryptoFlag.isOn = true + mockSafeCoreCrypto.coreCryptoContext.proteusCryptoboxMigratePath_MockMethod = { _ in migrated.fulfill() } @@ -125,7 +105,6 @@ class CryptoboxMigrationManagerTests: ZMBaseManagedObjectTest { func test_itDoesNotPerformMigration_CoreCryptoError() async { // Given mockFileManager.fileExistsAtPath_MockValue = true - proteusViaCoreCryptoFlag.isOn = true mockSafeCoreCrypto.coreCryptoContext.proteusCryptoboxMigratePath_MockMethod = { _ in throw CryptoboxMigrationManager.Failure.failedToMigrateData diff --git a/wire-ios-data-model/Tests/Source/Model/Conversation/ZMConversationLastMessagesTest.swift b/wire-ios-data-model/Tests/Source/Model/Conversation/ZMConversationLastMessagesTest.swift index 76f107328e7..328079baa1d 100644 --- a/wire-ios-data-model/Tests/Source/Model/Conversation/ZMConversationLastMessagesTest.swift +++ b/wire-ios-data-model/Tests/Source/Model/Conversation/ZMConversationLastMessagesTest.swift @@ -23,19 +23,6 @@ import XCTest class ZMConversationLastMessagesTest: ZMBaseManagedObjectTest { - override class func setUp() { - DeveloperFlag.storage = UserDefaults(suiteName: UUID().uuidString)! - var flag = DeveloperFlag.proteusViaCoreCrypto - flag.isOn = false - - super.setUp() - } - - override class func tearDown() { - super.tearDown() - DeveloperFlag.storage = UserDefaults.standard - } - func createConversation(on moc: NSManagedObjectContext? = nil) -> ZMConversation { let conversation = ZMConversation.insertNewObject(in: moc ?? uiMOC) conversation.remoteIdentifier = UUID() diff --git a/wire-ios-data-model/Tests/Source/Model/Messages/CacheAssetTests.swift b/wire-ios-data-model/Tests/Source/Model/Messages/CacheAssetTests.swift index f30019e4efc..665d1dd61ba 100644 --- a/wire-ios-data-model/Tests/Source/Model/Messages/CacheAssetTests.swift +++ b/wire-ios-data-model/Tests/Source/Model/Messages/CacheAssetTests.swift @@ -21,18 +21,6 @@ import XCTest class CacheAssetTests: BaseZMAssetClientMessageTests { - override class func setUp() { - super.setUp() - DeveloperFlag.storage = UserDefaults(suiteName: UUID().uuidString)! - var flag = DeveloperFlag.proteusViaCoreCrypto - flag.isOn = false - } - - override class func tearDown() { - super.tearDown() - DeveloperFlag.storage = UserDefaults.standard - } - // MARK: - Fixtures func fileAsset() -> WireDataModel.CacheAsset { diff --git a/wire-ios-data-model/Tests/Source/Model/Messages/ZMClientMessageTests+MLSEncryptedPayloadGenerator.swift b/wire-ios-data-model/Tests/Source/Model/Messages/ZMClientMessageTests+MLSEncryptedPayloadGenerator.swift index d81908866c5..1ca13c3bdc9 100644 --- a/wire-ios-data-model/Tests/Source/Model/Messages/ZMClientMessageTests+MLSEncryptedPayloadGenerator.swift +++ b/wire-ios-data-model/Tests/Source/Model/Messages/ZMClientMessageTests+MLSEncryptedPayloadGenerator.swift @@ -23,19 +23,6 @@ import GenericMessageProtocol final class ZMClientMessageTests_MLSEncryptedPayloadGenerator: BaseZMClientMessageTests { - override func setUp() { - DeveloperFlag.storage = UserDefaults(suiteName: UUID().uuidString)! - var flag = DeveloperFlag.proteusViaCoreCrypto - flag.isOn = true - - super.setUp() - } - - override func tearDown() { - super.tearDown() - DeveloperFlag.storage = UserDefaults.standard - } - let encryptionFunction: (Data) throws -> Data = { $0.zmSHA256Digest() } diff --git a/wire-ios-data-model/Tests/Source/Model/Messages/ZMClientMessageTests.swift b/wire-ios-data-model/Tests/Source/Model/Messages/ZMClientMessageTests.swift index ff8e15e17f9..627d77b67c2 100644 --- a/wire-ios-data-model/Tests/Source/Model/Messages/ZMClientMessageTests.swift +++ b/wire-ios-data-model/Tests/Source/Model/Messages/ZMClientMessageTests.swift @@ -23,18 +23,6 @@ import XCTest final class ClientMessageTests: BaseZMClientMessageTests { - override static func setUp() { - super.setUp() - DeveloperFlag.storage = UserDefaults(suiteName: UUID().uuidString)! - var flag = DeveloperFlag.proteusViaCoreCrypto - flag.isOn = false - } - - override static func tearDown() { - super.tearDown() - DeveloperFlag.storage = UserDefaults.standard - } - func testThatItDoesNotCreateTextMessagesFromUpdateEventIfThereIsAlreadyAClientMessageWithTheSameNonce() { // given let nonce = UUID.create() diff --git a/wire-ios-data-model/Tests/Source/Model/PermissionsTests.swift b/wire-ios-data-model/Tests/Source/Model/PermissionsTests.swift index f43e2080db4..131a7ae93e3 100644 --- a/wire-ios-data-model/Tests/Source/Model/PermissionsTests.swift +++ b/wire-ios-data-model/Tests/Source/Model/PermissionsTests.swift @@ -21,18 +21,6 @@ import WireTesting class PermissionsTests: BaseZMClientMessageTests { - override class func setUp() { - super.setUp() - DeveloperFlag.storage = UserDefaults(suiteName: UUID().uuidString)! - var flag = DeveloperFlag.proteusViaCoreCrypto - flag.isOn = false - } - - override class func tearDown() { - super.tearDown() - DeveloperFlag.storage = UserDefaults.standard - } - private let allPermissions: Permissions = [ .createConversation, .deleteConversation, diff --git a/wire-ios-data-model/Tests/Source/Model/TeamDeletionRuleTests.swift b/wire-ios-data-model/Tests/Source/Model/TeamDeletionRuleTests.swift index 276065ede18..826452fcd85 100644 --- a/wire-ios-data-model/Tests/Source/Model/TeamDeletionRuleTests.swift +++ b/wire-ios-data-model/Tests/Source/Model/TeamDeletionRuleTests.swift @@ -21,18 +21,6 @@ import WireTesting class TeamDeletionRuleTests: BaseZMClientMessageTests { - override class func setUp() { - super.setUp() - DeveloperFlag.storage = UserDefaults(suiteName: UUID().uuidString)! - var flag = DeveloperFlag.proteusViaCoreCrypto - flag.isOn = false - } - - override class func tearDown() { - super.tearDown() - DeveloperFlag.storage = UserDefaults.standard - } - func testThatItDoesntDeleteConversationsWhichArePartOfATeamWhenTeamGetsDeleted() { // given let team = Team.insertNewObject(in: uiMOC) diff --git a/wire-ios-data-model/Tests/Source/Model/TextSearchQueryTests.swift b/wire-ios-data-model/Tests/Source/Model/TextSearchQueryTests.swift index 207d2689014..d01b9498a9a 100644 --- a/wire-ios-data-model/Tests/Source/Model/TextSearchQueryTests.swift +++ b/wire-ios-data-model/Tests/Source/Model/TextSearchQueryTests.swift @@ -32,18 +32,6 @@ private class MockTextSearchQueryDelegate: TextSearchQueryDelegate { class TextSearchQueryTests: BaseZMClientMessageTests { - override class func setUp() { - super.setUp() - DeveloperFlag.storage = UserDefaults(suiteName: UUID().uuidString)! - var flag = DeveloperFlag.proteusViaCoreCrypto - flag.isOn = false - } - - override class func tearDown() { - super.tearDown() - DeveloperFlag.storage = UserDefaults.standard - } - func testThatItOnlyReturnsResultFromTheCorrectConversationNotYetIndexed() { // Given let conversation = ZMConversation.insertNewObject(in: uiMOC) diff --git a/wire-ios-data-model/Tests/Source/Model/User/ZMUserLegalHoldTests.swift b/wire-ios-data-model/Tests/Source/Model/User/ZMUserLegalHoldTests.swift index 328d6873ab4..f06dd6534ff 100644 --- a/wire-ios-data-model/Tests/Source/Model/User/ZMUserLegalHoldTests.swift +++ b/wire-ios-data-model/Tests/Source/Model/User/ZMUserLegalHoldTests.swift @@ -24,18 +24,6 @@ import XCTest @preconcurrency class ZMUserLegalHoldTests: ModelObjectsTests { - override func setUp() { - DeveloperFlag.storage = .temporary() - var flag = DeveloperFlag.proteusViaCoreCrypto - flag.isOn = false - super.setUp() - } - - override func tearDown() { - DeveloperFlag.storage = .standard - super.tearDown() - } - func testThatLegalHoldStatusIsDisabled_ByDefault() { // GIVEN let selfUser = ZMUser.selfUser(in: uiMOC) diff --git a/wire-ios-data-model/Tests/Source/Model/UserClient/UserClientTests.swift b/wire-ios-data-model/Tests/Source/Model/UserClient/UserClientTests.swift index 0773c7af682..3dbaf86405c 100644 --- a/wire-ios-data-model/Tests/Source/Model/UserClient/UserClientTests.swift +++ b/wire-ios-data-model/Tests/Source/Model/UserClient/UserClientTests.swift @@ -16,7 +16,6 @@ // along with this program. If not, see http://www.gnu.org/licenses/. // -import WireCryptobox import WireUtilities import XCTest @@ -25,18 +24,6 @@ import XCTest final class UserClientTests: ZMBaseManagedObjectTest { - override static func setUp() { - super.setUp() - DeveloperFlag.storage = UserDefaults(suiteName: UUID().uuidString)! - var flag = DeveloperFlag.proteusViaCoreCrypto - flag.isOn = false - } - - override static func tearDown() { - super.tearDown() - DeveloperFlag.storage = UserDefaults.standard - } - func clientWithTrustedClientCount( _ trustedCount: UInt, ignoredClientCount: UInt, @@ -169,8 +156,6 @@ final class UserClientTests: ZMBaseManagedObjectTest { func testThatItDeletesASession() async throws { // Given - var flag = DeveloperFlag.proteusViaCoreCrypto - flag.isOn = true var otherClient: UserClient! let mockProteusService = MockProteusServiceInterface() mockProteusService.deleteSessionId_MockMethod = { _ in @@ -195,13 +180,10 @@ final class UserClientTests: ZMBaseManagedObjectTest { } XCTAssert(waitForAllGroupsToBeEmpty(withTimeout: 0.5)) - flag.isOn = false } func testThatItDeletesASessionWhenDeletingAClient() async { // Given - var flag = DeveloperFlag.proteusViaCoreCrypto - flag.isOn = true var otherClient: UserClient! var otherClientSessionID: ProteusSessionID! let mockProteusService = MockProteusServiceInterface() @@ -230,7 +212,6 @@ final class UserClientTests: ZMBaseManagedObjectTest { } XCTAssert(waitForAllGroupsToBeEmpty(withTimeout: 0.5)) - flag.isOn = false } func testThatItUpdatesConversationSecurityLevelWhenDeletingClient() async { diff --git a/wire-ios-data-model/Tests/Source/Model/Utils/ProtobufUtilitiesTests.swift b/wire-ios-data-model/Tests/Source/Model/Utils/ProtobufUtilitiesTests.swift index 8da9bdc8fd2..5789343bd03 100644 --- a/wire-ios-data-model/Tests/Source/Model/Utils/ProtobufUtilitiesTests.swift +++ b/wire-ios-data-model/Tests/Source/Model/Utils/ProtobufUtilitiesTests.swift @@ -24,18 +24,6 @@ import XCTest class ProtobufUtilitiesTests: BaseZMClientMessageTests { - override class func setUp() { - super.setUp() - DeveloperFlag.storage = UserDefaults(suiteName: UUID().uuidString)! - var flag = DeveloperFlag.proteusViaCoreCrypto - flag.isOn = false - } - - override class func tearDown() { - super.tearDown() - DeveloperFlag.storage = UserDefaults.standard - } - func testThatItSetsAndReadsTheLoudness() { // given diff --git a/wire-ios-notification-engine/Sources/NotificationSession.swift b/wire-ios-notification-engine/Sources/NotificationSession.swift index f12cceea837..ac1909b3509 100644 --- a/wire-ios-notification-engine/Sources/NotificationSession.swift +++ b/wire-ios-notification-engine/Sources/NotificationSession.swift @@ -152,7 +152,7 @@ public final class NotificationSession { throw InitializationError.pendingCryptoboxMigration } coreDataStack.syncContext.performAndWait { - if DeveloperFlag.proteusViaCoreCrypto.isOn, coreDataStack.syncContext.proteusService == nil { + if coreDataStack.syncContext.proteusService == nil { coreDataStack.syncContext.proteusService = proteusService } diff --git a/wire-ios-request-strategy/Sources/Message Sending/MessageInfoExtractorTests.swift b/wire-ios-request-strategy/Sources/Message Sending/MessageInfoExtractorTests.swift index e9d3ecd3897..b74bc2359cb 100644 --- a/wire-ios-request-strategy/Sources/Message Sending/MessageInfoExtractorTests.swift +++ b/wire-ios-request-strategy/Sources/Message Sending/MessageInfoExtractorTests.swift @@ -32,7 +32,6 @@ final class MessageInfoExtractorTests: XCTestCase { override func setUp() async throws { try await super.setUp() - DeveloperFlag.proteusViaCoreCrypto.enable(true, storage: .temporary()) coreDataStack = CoreDataStack( account: .init(userName: "F", userIdentifier: .create()), diff --git a/wire-ios-request-strategy/Sources/Message Sending/ProteusMessagePayloadBuilderTests.swift b/wire-ios-request-strategy/Sources/Message Sending/ProteusMessagePayloadBuilderTests.swift index 03aba5277c7..6f42b427ed4 100644 --- a/wire-ios-request-strategy/Sources/Message Sending/ProteusMessagePayloadBuilderTests.swift +++ b/wire-ios-request-strategy/Sources/Message Sending/ProteusMessagePayloadBuilderTests.swift @@ -29,7 +29,6 @@ final class ProteusMessagePayloadBuilderTests: XCTestCase { override func setUpWithError() throws { try super.setUpWithError() - DeveloperFlag.proteusViaCoreCrypto.enable(true, storage: .temporary()) proteusService = MockProteusServiceInterface() } diff --git a/wire-ios-request-strategy/Sources/Synchronization/Decoding/EventDecoderTest.swift b/wire-ios-request-strategy/Sources/Synchronization/Decoding/EventDecoderTest.swift index 35e6d17fe78..8d16fb3248e 100644 --- a/wire-ios-request-strategy/Sources/Synchronization/Decoding/EventDecoderTest.swift +++ b/wire-ios-request-strategy/Sources/Synchronization/Decoding/EventDecoderTest.swift @@ -456,7 +456,6 @@ extension EventDecoderTest { extension EventDecoderTest { func test_ProteusEventDecryption() async throws { - var proteusViaCoreCrypto = DeveloperFlag.proteusViaCoreCrypto let mockProteusService = MockProteusServiceInterface() // Given @@ -467,7 +466,6 @@ extension EventDecoderTest { let message = GenericMessage(content: Text(content: "foo")) let event = try await encryptedUpdateEventToSelfFromOtherClient(message: message) - proteusViaCoreCrypto.isOn = true await syncMOC.perform { self.syncMOC.proteusService = mockProteusService @@ -482,13 +480,10 @@ extension EventDecoderTest { XCTAssertEqual(mockProteusService.decryptDataForSessionContext_Invocations.count, 1) // Cleanup - proteusViaCoreCrypto.isOn = false } func test_ProteusEventDecryptionDoesStoreLastEventIdIfFails() async throws { - DeveloperFlag.proteusViaCoreCrypto.enable(true, storage: .temporary()) defer { - DeveloperFlag.proteusViaCoreCrypto.enable(false, storage: .standard) } let mockProteusService = MockProteusServiceInterface() @@ -520,9 +515,7 @@ extension EventDecoderTest { func test_MLSEventDecryptionDoesNotStoreLastEventIdIfFails() async throws { // Given - DeveloperFlag.proteusViaCoreCrypto.enable(true, storage: .temporary()) defer { - DeveloperFlag.proteusViaCoreCrypto.enable(false, storage: .standard) } let mockProteusService = MockProteusServiceInterface() let decryptionErrorReason = DummyError() @@ -560,9 +553,7 @@ extension EventDecoderTest { func test_MLSEventDecryptionStoresLastEventIdIfDecryptionSuccessWithEmptyResults() async throws { // Given - DeveloperFlag.proteusViaCoreCrypto.enable(true, storage: .temporary()) defer { - DeveloperFlag.proteusViaCoreCrypto.enable(false, storage: .standard) } let mockProteusService = MockProteusServiceInterface() @@ -609,9 +600,7 @@ extension EventDecoderTest { func test_MLSEventDecryptionStoresLastEventIdIfDecryptionSuccessWithProposalResult() async throws { // Given - DeveloperFlag.proteusViaCoreCrypto.enable(true, storage: .temporary()) defer { - DeveloperFlag.proteusViaCoreCrypto.enable(false, storage: .standard) } let mockProteusService = MockProteusServiceInterface() diff --git a/wire-ios-share-engine/Sources/SharingSession.swift b/wire-ios-share-engine/Sources/SharingSession.swift index b81c58ad7f0..6cab6f429a3 100644 --- a/wire-ios-share-engine/Sources/SharingSession.swift +++ b/wire-ios-share-engine/Sources/SharingSession.swift @@ -296,7 +296,7 @@ public final class SharingSession { } coreDataStack.syncContext.performAndWait { - if DeveloperFlag.proteusViaCoreCrypto.isOn, coreDataStack.syncContext.proteusService == nil { + if coreDataStack.syncContext.proteusService == nil { coreDataStack.syncContext.proteusService = proteusService } diff --git a/wire-ios-sync-engine/Tests/Source/Integration/IntegrationTest.swift b/wire-ios-sync-engine/Tests/Source/Integration/IntegrationTest.swift index b5092458ea6..e585acdd967 100644 --- a/wire-ios-sync-engine/Tests/Source/Integration/IntegrationTest.swift +++ b/wire-ios-sync-engine/Tests/Source/Integration/IntegrationTest.swift @@ -150,17 +150,12 @@ extension IntegrationTest { MockJailbreakDetector() } - var proteusViaCoreCryptoEnabled: Bool { - false - } @objc func _setUp() { PrekeyGenerator._test_overrideNumberOfKeys = 1 - var flag = DeveloperFlag.proteusViaCoreCrypto - flag.isOn = proteusViaCoreCryptoEnabled sharedContainerDirectory = Bundle.main.appGroupIdentifier.map(FileManager.sharedContainerDirectory) deleteSharedContainerContent() diff --git a/wire-ios-sync-engine/Tests/Source/MessagingTest+Swift.swift b/wire-ios-sync-engine/Tests/Source/MessagingTest+Swift.swift index f6a1fca477e..bca761dc0e3 100644 --- a/wire-ios-sync-engine/Tests/Source/MessagingTest+Swift.swift +++ b/wire-ios-sync-engine/Tests/Source/MessagingTest+Swift.swift @@ -91,9 +91,6 @@ public extension MessagingTest { func setBackendInfoDefaults() { BackendInfo.apiVersion = .v0 BackendInfo.domain = "example.com" - - var proteusViaCoreCrypto = DeveloperFlag.proteusViaCoreCrypto - proteusViaCoreCrypto.isOn = false } @objc diff --git a/wire-ios-sync-engine/Tests/Source/Synchronization/Strategies/CallingRequestStrategyTests.swift b/wire-ios-sync-engine/Tests/Source/Synchronization/Strategies/CallingRequestStrategyTests.swift index ecb7a1a36c1..20e710cc4f6 100644 --- a/wire-ios-sync-engine/Tests/Source/Synchronization/Strategies/CallingRequestStrategyTests.swift +++ b/wire-ios-sync-engine/Tests/Source/Synchronization/Strategies/CallingRequestStrategyTests.swift @@ -32,13 +32,6 @@ class CallingRequestStrategyTests: MessagingTest { var mockFetchUserClientsUseCase: MockFetchUserClientsUseCase! var mockMessageSender: MockMessageSenderInterface! - override class func setUp() { - super.setUp() - - var flag = DeveloperFlag.proteusViaCoreCrypto - flag.isOn = false - } - override func setUp() { super.setUp() diff --git a/wire-ios-sync-engine/Tests/Source/Use cases/GetUserClientFingerprintUseCaseTests.swift b/wire-ios-sync-engine/Tests/Source/Use cases/GetUserClientFingerprintUseCaseTests.swift index bd1172563ac..a520be38b8a 100644 --- a/wire-ios-sync-engine/Tests/Source/Use cases/GetUserClientFingerprintUseCaseTests.swift +++ b/wire-ios-sync-engine/Tests/Source/Use cases/GetUserClientFingerprintUseCaseTests.swift @@ -104,7 +104,7 @@ final class GetUserClientFingerprintUseCaseTests: MessagingTest { // MARK: - localFingerprint - func test_itLoadsLocalFingerprint_ProteusViaCoreCryptoFlagEnabled() async { + func test_itLoadsLocalFingerprint() async { // GIVEN sut = createSut() diff --git a/wire-ios-utilities/Source/DeveloperFlag.swift b/wire-ios-utilities/Source/DeveloperFlag.swift index 4c2c8373c36..0138c94f629 100644 --- a/wire-ios-utilities/Source/DeveloperFlag.swift +++ b/wire-ios-utilities/Source/DeveloperFlag.swift @@ -24,7 +24,6 @@ public enum DeveloperFlag: String, CaseIterable { case createLegacyBackups case showCreateMLSGroupToggle - case proteusViaCoreCrypto case forceDatabaseLoadingFailure case ignoreIncomingEvents case skipMLSMessagesDecryption @@ -51,9 +50,6 @@ public enum DeveloperFlag: String, CaseIterable { case .showCreateMLSGroupToggle: "Turn on to show the MLS toggle when creating a new group." - case .proteusViaCoreCrypto: - "Turn on to use CoreCrypto for proteus messaging." - case .forceDatabaseLoadingFailure: "Turn on to force database loading failure in the process of database migration" @@ -134,8 +130,6 @@ public enum DeveloperFlag: String, CaseIterable { switch self { case .createLegacyBackups: "CreateLegacyBackupsEnabled" - case .proteusViaCoreCrypto: - "ProteusByCoreCryptoEnabled" case .forceDatabaseLoadingFailure: "ForceDatabaseLoadingFailure" case .ignoreIncomingEvents: diff --git a/wire-ios-utilities/WireUtilities-Info.plist b/wire-ios-utilities/WireUtilities-Info.plist index 5651f3bcb66..4ed7d8e640a 100644 --- a/wire-ios-utilities/WireUtilities-Info.plist +++ b/wire-ios-utilities/WireUtilities-Info.plist @@ -4,8 +4,6 @@ MultibackendEnabled $(MULTIBACKEND_ENABLED) - ProteusByCoreCryptoEnabled - $(PROTEUS_BY_CORECRYPTO_ENABLED) WireAuthenticationEnabled $(WIRE_AUTHENTICATION_ENABLED) CreateLegacyBackupsEnabled diff --git a/wire-ios/Configuration/Developer-Flags.xcconfig b/wire-ios/Configuration/Developer-Flags.xcconfig index 39e50628f83..7da38d42395 100644 --- a/wire-ios/Configuration/Developer-Flags.xcconfig +++ b/wire-ios/Configuration/Developer-Flags.xcconfig @@ -16,6 +16,5 @@ // along with this program. If not, see http://www.gnu.org/licenses/. // -PROTEUS_BY_CORECRYPTO_ENABLED=1 WIRE_AUTHENTICATION_ENABLED=1 MULTIBACKEND_ENABLED=1 diff --git a/wire-ios/Wire-iOS Tests/AVAsset/AVURLAsset+conversionTests.swift b/wire-ios/Wire-iOS Tests/AVAsset/AVURLAsset+conversionTests.swift index 766c6959d71..f05399f85d0 100644 --- a/wire-ios/Wire-iOS Tests/AVAsset/AVURLAsset+conversionTests.swift +++ b/wire-ios/Wire-iOS Tests/AVAsset/AVURLAsset+conversionTests.swift @@ -22,18 +22,6 @@ import XCTest final class AVURLAsset_conversionTests: XCTestCase { - override static func setUp() { - super.setUp() - DeveloperFlag.storage = UserDefaults(suiteName: UUID().uuidString)! - var flag = DeveloperFlag.proteusViaCoreCrypto - flag.isOn = false - } - - override static func tearDown() { - super.tearDown() - DeveloperFlag.storage = UserDefaults.standard - } - func testThatVideoIsConvertedToUploadFormat() { // GIVEN let videoURL = urlForResource(inTestBundleNamed: "video.mp4") From 4fe5399bc435799351e0c53129a5ba41d65c12e3 Mon Sep 17 00:00:00 2001 From: David-Henner Date: Tue, 25 Nov 2025 16:38:18 +0100 Subject: [PATCH 07/28] avoid force unwrapping note # Conflicts: # wire-ios-request-strategy/Tests/Sources/Notifications/PushNotifications/ZMLocalNotificationTests_Message.swift --- .../ZMLocalNotificationTests_Message.swift | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/wire-ios-request-strategy/Tests/Sources/Notifications/PushNotifications/ZMLocalNotificationTests_Message.swift b/wire-ios-request-strategy/Tests/Sources/Notifications/PushNotifications/ZMLocalNotificationTests_Message.swift index 253f0fb50ec..fbd5c47791d 100644 --- a/wire-ios-request-strategy/Tests/Sources/Notifications/PushNotifications/ZMLocalNotificationTests_Message.swift +++ b/wire-ios-request-strategy/Tests/Sources/Notifications/PushNotifications/ZMLocalNotificationTests_Message.swift @@ -159,24 +159,24 @@ final class ZMLocalNotificationTests_Message: ZMLocalNotificationTests { } } - func testItCreatesMessageNotificationsCorrectly() { + func testItCreatesMessageNotificationsCorrectly() throws { // "push.notification.add.message.oneonone" = "%1$@"; // "push.notification.add.message.group" = "%1$@: %2$@"; // "push.notification.add.message.group.noconversationname" = "%1$@ in a conversation: %2$@"; - syncMOC.performGroupedAndWait { - XCTAssertEqual(self.bodyForNote(self.oneOnOneConversation, sender: self.sender), "Hello Hello!") - XCTAssertEqual(self.bodyForNote(self.groupConversation, sender: self.sender), "Super User: Hello Hello!") + try syncMOC.performGroupedAndWait { + XCTAssertEqual(try bodyForNote(oneOnOneConversation, sender: sender), "Hello Hello!") + XCTAssertEqual(try bodyForNote(groupConversation, sender: sender), "Super User: Hello Hello!") XCTAssertEqual( - self.bodyForNote(self.groupConversationWithoutUserDefinedName, sender: self.sender), + try bodyForNote(groupConversationWithoutUserDefinedName, sender: sender), "Super User: Hello Hello!" ) XCTAssertEqual( - self.bodyForNote(self.groupConversationWithoutName, sender: self.sender), + try bodyForNote(groupConversationWithoutName, sender: sender), "Super User in a conversation: Hello Hello!" ) XCTAssertEqual( - self.bodyForNote(self.invalidConversation, sender: self.sender), + try bodyForNote(invalidConversation, sender: sender), "Super User in a conversation: Hello Hello!" ) } @@ -198,10 +198,10 @@ final class ZMLocalNotificationTests_Message: ZMLocalNotificationTests { } } - func testThatItDoesNotDuplicatePercentageSignsInTextAndConversationName() { - syncMOC.performGroupedAndWait { + func testThatItDoesNotDuplicatePercentageSignsInTextAndConversationName() throws { + try syncMOC.performGroupedAndWait { XCTAssertEqual( - self.bodyForNote(self.groupConversation, sender: self.sender, text: "Today we grew by 100%"), + try bodyForNote(groupConversation, sender: sender, text: "Today we grew by 100%"), "Super User: Today we grew by 100%" ) } From c81fd15a6451e571c2fa5f8e0e1244b9058cc357 Mon Sep 17 00:00:00 2001 From: David-Henner Date: Tue, 25 Nov 2025 17:39:39 +0100 Subject: [PATCH 08/28] fix `CallingRequestStrategyTests` --- .../CallingRequestStrategyTests.swift | 46 ++++++++----------- 1 file changed, 18 insertions(+), 28 deletions(-) diff --git a/wire-ios-sync-engine/Tests/Source/Synchronization/Strategies/CallingRequestStrategyTests.swift b/wire-ios-sync-engine/Tests/Source/Synchronization/Strategies/CallingRequestStrategyTests.swift index 20e710cc4f6..c47b8ac7bfc 100644 --- a/wire-ios-sync-engine/Tests/Source/Synchronization/Strategies/CallingRequestStrategyTests.swift +++ b/wire-ios-sync-engine/Tests/Source/Synchronization/Strategies/CallingRequestStrategyTests.swift @@ -19,12 +19,13 @@ import Foundation import GenericMessageProtocol import WireDataModelSupport -import WireRequestStrategy +@preconcurrency import WireRequestStrategy import WireSyncEngineSupport import WireTransport @testable import WireSyncEngine +@preconcurrency class CallingRequestStrategyTests: MessagingTest { var sut: CallingRequestStrategy! @@ -496,7 +497,6 @@ class CallingRequestStrategyTests: MessagingTest { // MARK: - Targeted Calling Messages func testThatItTargetsCallMessagesIfTargetClientsAreSpecified() throws { - var sentMessage: GenericMessageEntity? let (conversationAVSID, user1, client1, user2, client2, targets) = try syncMOC.performAndWait { [self] in sut = CallingRequestStrategy( managedObjectContext: syncMOC, @@ -540,13 +540,14 @@ class CallingRequestStrategyTests: MessagingTest { let avsClient2 = AVSClient(userId: user2.avsIdentifier, clientId: client2.remoteIdentifier!) let targets = [avsClient1, avsClient2] - mockMessageSender.sendMessageMessage_MockMethod = { message in - sentMessage = message as? GenericMessageEntity - } - let conversationAVSID = try XCTUnwrap(conversation.avsIdentifier) return (conversationAVSID, user1, client1, user2, client2, targets) } + + var sentMessage: GenericMessageEntity? + mockMessageSender.sendMessageMessage_MockMethod = { message in + sentMessage = message as? GenericMessageEntity + } // When we schedule the targeted message syncMOC.performGroupedBlock { @@ -589,7 +590,7 @@ class CallingRequestStrategyTests: MessagingTest { } func testThatItDoesNotTargetCallMessagesIfNoTargetClientsAreSpecified() async throws { - let (user1, user2, client1, client2, client3, client4, conversationAVSID) = try await syncMOC + let conversationAVSID = try await syncMOC .perform { [self] in // Given let selfClient = createSelfClient() @@ -598,15 +599,15 @@ class CallingRequestStrategyTests: MessagingTest { let user1 = ZMUser.insertNewObject(in: syncMOC) user1.remoteIdentifier = .create() - let client1 = createClient(for: user1, connectedTo: selfClient) - let client2 = createClient(for: user1, connectedTo: selfClient) + createClient(for: user1, connectedTo: selfClient) + createClient(for: user1, connectedTo: selfClient) // Another user with two clients connected to self let user2 = ZMUser.insertNewObject(in: syncMOC) user2.remoteIdentifier = .create() - let client3 = createClient(for: user2, connectedTo: selfClient) - let client4 = createClient(for: user2, connectedTo: selfClient) + createClient(for: user2, connectedTo: selfClient) + createClient(for: user2, connectedTo: selfClient) // A conversation with both users and self let conversation = ZMConversation.insertNewObject(in: syncMOC) @@ -619,8 +620,7 @@ class CallingRequestStrategyTests: MessagingTest { syncMOC.saveOrRollback() - let conversationAVSID = try XCTUnwrap(conversation.avsIdentifier) - return (user1, user2, client1, client2, client3, client4, conversationAVSID) + return try XCTUnwrap(conversation.avsIdentifier) } var sentMessage: GenericMessageEntity? @@ -658,16 +658,6 @@ class CallingRequestStrategyTests: MessagingTest { let client = UserClient.insertNewObject(in: syncMOC) client.remoteIdentifier = .randomRemoteIdentifier() client.user = user - - // swiftlint:disable:next todo_requires_jira_link - // TODO: [John] use flag here - syncMOC.zm_cryptKeyStore.encryptionContext.perform { session in - try! session.createClientSession( - client.sessionIdentifier!, - base64PreKeyString: syncMOC.zm_cryptKeyStore.lastPreKey() - ) - } - return client } @@ -721,7 +711,6 @@ class CallingRequestStrategyTests: MessagingTest { } func test_ThatItHandlesMLSRejectMessage() throws { - var sentMessage: GenericMessageEntity? let (user1AVSIdentifier, client1RemoteIdentifier, conversationAVSID) = try syncMOC.performAndWait { [self] in // Given createMLSSelfConversation() @@ -748,10 +737,6 @@ class CallingRequestStrategyTests: MessagingTest { syncMOC.saveOrRollback() - mockMessageSender.sendMessageMessage_MockMethod = { message in - sentMessage = message as? GenericMessageEntity - } - let mockMLSService = MockMLSServiceInterface() syncMOC.mlsService = mockMLSService @@ -760,6 +745,11 @@ class CallingRequestStrategyTests: MessagingTest { return (user1.avsIdentifier, client1.remoteIdentifier!, conversationAVSID) } + var sentMessage: GenericMessageEntity? + mockMessageSender.sendMessageMessage_MockMethod = { message in + sentMessage = message as? GenericMessageEntity + } + // Targeting one client let avsClient1 = AVSClient(userId: user1AVSIdentifier, clientId: client1RemoteIdentifier) let targets = [avsClient1] From f4042146b8e4b1e63e6555e47d9d86fd7289ca2d Mon Sep 17 00:00:00 2001 From: David-Henner Date: Fri, 28 Nov 2025 14:37:56 +0100 Subject: [PATCH 09/28] remove `try`s for non-throwing methods --- .../ZMLocalNotificationTests_Message.swift | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/wire-ios-request-strategy/Tests/Sources/Notifications/PushNotifications/ZMLocalNotificationTests_Message.swift b/wire-ios-request-strategy/Tests/Sources/Notifications/PushNotifications/ZMLocalNotificationTests_Message.swift index fbd5c47791d..be7af94bcf1 100644 --- a/wire-ios-request-strategy/Tests/Sources/Notifications/PushNotifications/ZMLocalNotificationTests_Message.swift +++ b/wire-ios-request-strategy/Tests/Sources/Notifications/PushNotifications/ZMLocalNotificationTests_Message.swift @@ -159,24 +159,24 @@ final class ZMLocalNotificationTests_Message: ZMLocalNotificationTests { } } - func testItCreatesMessageNotificationsCorrectly() throws { + func testItCreatesMessageNotificationsCorrectly() { // "push.notification.add.message.oneonone" = "%1$@"; // "push.notification.add.message.group" = "%1$@: %2$@"; // "push.notification.add.message.group.noconversationname" = "%1$@ in a conversation: %2$@"; - try syncMOC.performGroupedAndWait { - XCTAssertEqual(try bodyForNote(oneOnOneConversation, sender: sender), "Hello Hello!") - XCTAssertEqual(try bodyForNote(groupConversation, sender: sender), "Super User: Hello Hello!") + syncMOC.performGroupedAndWait { + XCTAssertEqual(bodyForNote(oneOnOneConversation, sender: sender), "Hello Hello!") + XCTAssertEqual(bodyForNote(groupConversation, sender: sender), "Super User: Hello Hello!") XCTAssertEqual( - try bodyForNote(groupConversationWithoutUserDefinedName, sender: sender), + bodyForNote(groupConversationWithoutUserDefinedName, sender: sender), "Super User: Hello Hello!" ) XCTAssertEqual( - try bodyForNote(groupConversationWithoutName, sender: sender), + bodyForNote(groupConversationWithoutName, sender: sender), "Super User in a conversation: Hello Hello!" ) XCTAssertEqual( - try bodyForNote(invalidConversation, sender: sender), + bodyForNote(invalidConversation, sender: sender), "Super User in a conversation: Hello Hello!" ) } From ae55be2e9c2a2c2673acccfb018ed10789663670 Mon Sep 17 00:00:00 2001 From: David-Henner Date: Fri, 28 Nov 2025 14:38:05 +0100 Subject: [PATCH 10/28] fix typo --- ...t+Unpack.swift => NSManagedObjectContext+Unpack.swift} | 0 .../WireDataModel.xcodeproj/project.pbxproj | 8 ++++---- 2 files changed, 4 insertions(+), 4 deletions(-) rename wire-ios-data-model/Source/Utilis/{NSManagedObjectContact+Unpack.swift => NSManagedObjectContext+Unpack.swift} (100%) diff --git a/wire-ios-data-model/Source/Utilis/NSManagedObjectContact+Unpack.swift b/wire-ios-data-model/Source/Utilis/NSManagedObjectContext+Unpack.swift similarity index 100% rename from wire-ios-data-model/Source/Utilis/NSManagedObjectContact+Unpack.swift rename to wire-ios-data-model/Source/Utilis/NSManagedObjectContext+Unpack.swift diff --git a/wire-ios-data-model/WireDataModel.xcodeproj/project.pbxproj b/wire-ios-data-model/WireDataModel.xcodeproj/project.pbxproj index cc36bd50c88..f1326f2fbd9 100644 --- a/wire-ios-data-model/WireDataModel.xcodeproj/project.pbxproj +++ b/wire-ios-data-model/WireDataModel.xcodeproj/project.pbxproj @@ -8,7 +8,7 @@ /* Begin PBXBuildFile section */ 010093B32D4BA2F400429015 /* ZMConversationTests_SystemMessages.swift in Sources */ = {isa = PBXBuildFile; fileRef = 010093B22D4BA2DB00429015 /* ZMConversationTests_SystemMessages.swift */; }; - 010347222ED4566D000676DE /* NSManagedObjectContact+Unpack.swift in Sources */ = {isa = PBXBuildFile; fileRef = 010347212ED4566D000676DE /* NSManagedObjectContact+Unpack.swift */; }; + 010347222ED4566D000676DE /* NSManagedObjectContext+Unpack.swift in Sources */ = {isa = PBXBuildFile; fileRef = 010347212ED4566D000676DE /* NSManagedObjectContext+Unpack.swift */; }; 0129E7F929A520870065E6DB /* SafeCoreCrypto.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0129E7F829A520870065E6DB /* SafeCoreCrypto.swift */; }; 0129E7FB29A520EB0065E6DB /* SafeFileContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0129E7FA29A520EB0065E6DB /* SafeFileContext.swift */; }; 013887A22B9A5C6000323DD0 /* CleanupModels107PreAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 013887A12B9A5C6000323DD0 /* CleanupModels107PreAction.swift */; }; @@ -769,7 +769,7 @@ /* Begin PBXFileReference section */ 010093B22D4BA2DB00429015 /* ZMConversationTests_SystemMessages.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZMConversationTests_SystemMessages.swift; sourceTree = ""; }; - 010347212ED4566D000676DE /* NSManagedObjectContact+Unpack.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSManagedObjectContact+Unpack.swift"; sourceTree = ""; }; + 010347212ED4566D000676DE /* NSManagedObjectContext+Unpack.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSManagedObjectContext+Unpack.swift"; sourceTree = ""; }; 0129E7F829A520870065E6DB /* SafeCoreCrypto.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SafeCoreCrypto.swift; sourceTree = ""; }; 0129E7FA29A520EB0065E6DB /* SafeFileContext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SafeFileContext.swift; sourceTree = ""; }; 013887A12B9A5C6000323DD0 /* CleanupModels107PreAction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CleanupModels107PreAction.swift; sourceTree = ""; }; @@ -2559,7 +2559,7 @@ 0614E96C2A863EED007BB1F6 /* NSPredicate+BaseCompounds.swift */, 1657FA9B2D9C432200A7B337 /* CoreCryptoContextProtocolExt.swift */, E66029D12BDA43E10033C524 /* SearchUsersCache.swift */, - 010347212ED4566D000676DE /* NSManagedObjectContact+Unpack.swift */, + 010347212ED4566D000676DE /* NSManagedObjectContext+Unpack.swift */, ); path = Utilis; sourceTree = ""; @@ -3811,7 +3811,7 @@ 063D292A24212AFD00FA6FEE /* ZMClientMessage.swift in Sources */, CB1BF6FC2C8AF6A5001EC670 /* ExpirationReason+Description.swift in Sources */, EE9B9F5929964F6A00A257BC /* NSManagedObjectContext+CoreCrypto.swift in Sources */, - 010347222ED4566D000676DE /* NSManagedObjectContact+Unpack.swift in Sources */, + 010347222ED4566D000676DE /* NSManagedObjectContext+Unpack.swift in Sources */, 599EA3D52C246955009319D4 /* MutableConversationContainer.swift in Sources */, 017B55792CFFB560005FFBD4 /* NSManagedObjectContext+SyncResources.swift in Sources */, 5986A08F2C6B744300299181 /* TextMessage.swift in Sources */, From 8ce62417338a13e428983c80ab10c266751ab25c Mon Sep 17 00:00:00 2001 From: David-Henner Date: Fri, 28 Nov 2025 14:51:36 +0100 Subject: [PATCH 11/28] formatting --- .../Synchronization/Decoding/EventDecoderTest.swift | 13 ++++--------- .../Tests/Source/Integration/IntegrationTest.swift | 2 -- .../Strategies/CallingRequestStrategyTests.swift | 2 +- 3 files changed, 5 insertions(+), 12 deletions(-) diff --git a/wire-ios-request-strategy/Sources/Synchronization/Decoding/EventDecoderTest.swift b/wire-ios-request-strategy/Sources/Synchronization/Decoding/EventDecoderTest.swift index 8d16fb3248e..2dd4171f0b3 100644 --- a/wire-ios-request-strategy/Sources/Synchronization/Decoding/EventDecoderTest.swift +++ b/wire-ios-request-strategy/Sources/Synchronization/Decoding/EventDecoderTest.swift @@ -466,7 +466,6 @@ extension EventDecoderTest { let message = GenericMessage(content: Text(content: "foo")) let event = try await encryptedUpdateEventToSelfFromOtherClient(message: message) - await syncMOC.perform { self.syncMOC.proteusService = mockProteusService } @@ -483,8 +482,7 @@ extension EventDecoderTest { } func test_ProteusEventDecryptionDoesStoreLastEventIdIfFails() async throws { - defer { - } + defer {} let mockProteusService = MockProteusServiceInterface() enum FakeError: Error { @@ -515,8 +513,7 @@ extension EventDecoderTest { func test_MLSEventDecryptionDoesNotStoreLastEventIdIfFails() async throws { // Given - defer { - } + defer {} let mockProteusService = MockProteusServiceInterface() let decryptionErrorReason = DummyError() @@ -553,8 +550,7 @@ extension EventDecoderTest { func test_MLSEventDecryptionStoresLastEventIdIfDecryptionSuccessWithEmptyResults() async throws { // Given - defer { - } + defer {} let mockProteusService = MockProteusServiceInterface() mockProteusService.decryptDataForSessionContext_MockMethod = { data, _, _ in @@ -600,8 +596,7 @@ extension EventDecoderTest { func test_MLSEventDecryptionStoresLastEventIdIfDecryptionSuccessWithProposalResult() async throws { // Given - defer { - } + defer {} let mockProteusService = MockProteusServiceInterface() mockProteusService.decryptDataForSessionContext_MockMethod = { data, _, _ in diff --git a/wire-ios-sync-engine/Tests/Source/Integration/IntegrationTest.swift b/wire-ios-sync-engine/Tests/Source/Integration/IntegrationTest.swift index e585acdd967..f364df0cf4a 100644 --- a/wire-ios-sync-engine/Tests/Source/Integration/IntegrationTest.swift +++ b/wire-ios-sync-engine/Tests/Source/Integration/IntegrationTest.swift @@ -150,13 +150,11 @@ extension IntegrationTest { MockJailbreakDetector() } - @objc func _setUp() { PrekeyGenerator._test_overrideNumberOfKeys = 1 - sharedContainerDirectory = Bundle.main.appGroupIdentifier.map(FileManager.sharedContainerDirectory) deleteSharedContainerContent() ZMPersistentCookieStorage.setDoNotPersistToKeychain(!useRealKeychain) diff --git a/wire-ios-sync-engine/Tests/Source/Synchronization/Strategies/CallingRequestStrategyTests.swift b/wire-ios-sync-engine/Tests/Source/Synchronization/Strategies/CallingRequestStrategyTests.swift index c47b8ac7bfc..d8c5dc072d7 100644 --- a/wire-ios-sync-engine/Tests/Source/Synchronization/Strategies/CallingRequestStrategyTests.swift +++ b/wire-ios-sync-engine/Tests/Source/Synchronization/Strategies/CallingRequestStrategyTests.swift @@ -543,7 +543,7 @@ class CallingRequestStrategyTests: MessagingTest { let conversationAVSID = try XCTUnwrap(conversation.avsIdentifier) return (conversationAVSID, user1, client1, user2, client2, targets) } - + var sentMessage: GenericMessageEntity? mockMessageSender.sendMessageMessage_MockMethod = { message in sentMessage = message as? GenericMessageEntity From 32f779e492b8b4fc5bb44d0ed71f8da8e43d082a Mon Sep 17 00:00:00 2001 From: David-Henner Date: Fri, 28 Nov 2025 14:59:33 +0100 Subject: [PATCH 12/28] chore(cryptobox): remove keyStore - WPB-7393 --- ...anagedObjectContext+AccountDirectory.swift | 49 +++++ .../Source/Model/UserClient/UserClient.swift | 53 ----- .../Proteus/CryptoboxMigrationManager.swift | 12 +- .../Proteus/ProteusSessionID+Mapping.swift | 32 --- .../Source/Utilis/CryptoBox.swift | 190 ------------------ .../Sources/SpyUserClientKeyStore.swift | 74 ------- .../WireDataModel.xcodeproj/project.pbxproj | 12 +- ...ryptionSessionDirectory+UpdateEvents.swift | 40 ---- .../FetchingClientRequestStrategyTests.swift | 22 +- .../project.pbxproj | 4 - .../ZMUserSession+Debugging.swift | 111 +--------- 11 files changed, 76 insertions(+), 523 deletions(-) create mode 100644 wire-ios-data-model/Source/ManagedObjectContext/NSManagedObjectContext+AccountDirectory.swift delete mode 100644 wire-ios-data-model/Source/Proteus/ProteusSessionID+Mapping.swift delete mode 100644 wire-ios-data-model/Source/Utilis/CryptoBox.swift delete mode 100644 wire-ios-data-model/Support/Sources/SpyUserClientKeyStore.swift delete mode 100644 wire-ios-request-strategy/Sources/Helpers/EncryptionSessionDirectory+UpdateEvents.swift diff --git a/wire-ios-data-model/Source/ManagedObjectContext/NSManagedObjectContext+AccountDirectory.swift b/wire-ios-data-model/Source/ManagedObjectContext/NSManagedObjectContext+AccountDirectory.swift new file mode 100644 index 00000000000..1b356afd2d5 --- /dev/null +++ b/wire-ios-data-model/Source/ManagedObjectContext/NSManagedObjectContext+AccountDirectory.swift @@ -0,0 +1,49 @@ +// +// Wire +// Copyright (C) 2025 Wire Swiss GmbH +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see http://www.gnu.org/licenses/. +// + +public extension NSManagedObjectContext { + + private static let AccountDirectoryURLKey = "AccountDirectoryURLKey" + + var accountDirectoryURL: URL? { + get { + precondition(zm_isSyncContext, "accountDirectoryURL should only be accessed on the sync context") + return userInfo[Self.AccountDirectoryURLKey] as? URL + } + + set { + precondition(zm_isSyncContext, "accountDirectoryURL should only be accessed on the sync context") + userInfo[Self.AccountDirectoryURLKey] = newValue + } + } + + private static let ApplicationContainerURLKey = "ApplicationContainerURLKey" + + var applicationContainerURL: URL? { + get { + precondition(zm_isSyncContext, "applicationContainerURL should only be accessed on the sync context") + return userInfo[Self.ApplicationContainerURLKey] as? URL + } + + set { + precondition(zm_isSyncContext, "applicationContainerURL should only be accessed on the sync context") + userInfo[Self.ApplicationContainerURLKey] = newValue + } + } + +} diff --git a/wire-ios-data-model/Source/Model/UserClient/UserClient.swift b/wire-ios-data-model/Source/Model/UserClient/UserClient.swift index 85abd03bd4d..cef1abba94f 100644 --- a/wire-ios-data-model/Source/Model/UserClient/UserClient.swift +++ b/wire-ios-data-model/Source/Model/UserClient/UserClient.swift @@ -756,59 +756,6 @@ public extension UserClient { } -// MARK: - Session identifier - -extension UserClient { - - /// Session identifier of the local cryptobox session with this client. - - public var sessionIdentifier: EncryptionSessionIdentifier? { - if needsSessionMigration { - sessionIdentifier_V2 - } else { - sessionIdentifier_V3 - } - } - - /// Previous session identifiers. - - private var sessionIdentifier_V1: String? { - remoteIdentifier - } - - private var sessionIdentifier_V2: EncryptionSessionIdentifier? { - guard - let userIdentifier = user?.remoteIdentifier, - let clientIdentifier = remoteIdentifier - else { - return nil - } - - return EncryptionSessionIdentifier( - userId: userIdentifier.uuidString, - clientId: clientIdentifier - ) - } - - private var sessionIdentifier_V3: EncryptionSessionIdentifier? { - guard - let user, - let domain = user.domain ?? managedObjectContext?.localDomain, - let userIdentifier = user.remoteIdentifier, - let clientIdentifier = remoteIdentifier - else { - return nil - } - - return EncryptionSessionIdentifier( - domain: domain, - userId: userIdentifier.uuidString, - clientId: clientIdentifier - ) - } - -} - // MARK: - Proteus Session ID extension UserClient { diff --git a/wire-ios-data-model/Source/Proteus/CryptoboxMigrationManager.swift b/wire-ios-data-model/Source/Proteus/CryptoboxMigrationManager.swift index 8a7d0737fda..87c8b04d16a 100644 --- a/wire-ios-data-model/Source/Proteus/CryptoboxMigrationManager.swift +++ b/wire-ios-data-model/Source/Proteus/CryptoboxMigrationManager.swift @@ -107,11 +107,21 @@ protocol FileManagerInterface { extension FileManager: FileManagerInterface { + private static let keyStoreFolderPrefix = "otr" + func cryptoboxDirectory(in accountDirectory: URL) -> URL { - FileManager.keyStoreURL( + keyStoreURL( accountDirectory: accountDirectory, createParentIfNeeded: false ) } + /// Returns the URL for the keyStore + private func keyStoreURL(accountDirectory: URL, createParentIfNeeded: Bool) -> URL { + if createParentIfNeeded { + try! FileManager.default.createAndProtectDirectory(at: accountDirectory) + } + return accountDirectory.appendingPathComponent(FileManager.keyStoreFolderPrefix) + } + } diff --git a/wire-ios-data-model/Source/Proteus/ProteusSessionID+Mapping.swift b/wire-ios-data-model/Source/Proteus/ProteusSessionID+Mapping.swift deleted file mode 100644 index f076981cbbb..00000000000 --- a/wire-ios-data-model/Source/Proteus/ProteusSessionID+Mapping.swift +++ /dev/null @@ -1,32 +0,0 @@ -// -// Wire -// Copyright (C) 2025 Wire Swiss GmbH -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see http://www.gnu.org/licenses/. -// - -import Foundation -import WireCryptobox - -public extension ProteusSessionID { - - func mapToEncryptionSessionID() -> EncryptionSessionIdentifier { - EncryptionSessionIdentifier( - domain: domain, - userId: userID, - clientId: clientID - ) - } - -} diff --git a/wire-ios-data-model/Source/Utilis/CryptoBox.swift b/wire-ios-data-model/Source/Utilis/CryptoBox.swift deleted file mode 100644 index 164957e8d5f..00000000000 --- a/wire-ios-data-model/Source/Utilis/CryptoBox.swift +++ /dev/null @@ -1,190 +0,0 @@ -// -// Wire -// Copyright (C) 2025 Wire Swiss GmbH -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see http://www.gnu.org/licenses/. -// - -import Foundation -import WireCryptobox - -public extension NSManagedObjectContext { - - private static let AccountDirectoryURLKey = "AccountDirectoryURLKey" - - var accountDirectoryURL: URL? { - get { - precondition(zm_isSyncContext, "accountDirectoryURL should only be accessed on the sync context") - return userInfo[Self.AccountDirectoryURLKey] as? URL - } - - set { - precondition(zm_isSyncContext, "accountDirectoryURL should only be accessed on the sync context") - userInfo[Self.AccountDirectoryURLKey] = newValue - } - } - - private static let ApplicationContainerURLKey = "ApplicationContainerURLKey" - - var applicationContainerURL: URL? { - get { - precondition(zm_isSyncContext, "applicationContainerURL should only be accessed on the sync context") - return userInfo[Self.ApplicationContainerURLKey] as? URL - } - - set { - precondition(zm_isSyncContext, "applicationContainerURL should only be accessed on the sync context") - userInfo[Self.ApplicationContainerURLKey] = newValue - } - } - - private static let ZMUserClientKeysStoreKey = "ZMUserClientKeysStore" - - @objc(setupUserKeyStoreInAccountDirectory:applicationContainer:) - func setupUserKeyStore(accountDirectory: URL, applicationContainer: URL) { - if !zm_isSyncContext { - fatal("Can't initiliazie crypto box on non-sync context") - } - - let newKeyStore = UserClientKeysStore( - accountDirectory: accountDirectory, - applicationContainer: applicationContainer - ) - userInfo[NSManagedObjectContext.ZMUserClientKeysStoreKey] = newKeyStore - } - - /// Returns the cryptobox instance associated with this managed object context - @objc var zm_cryptKeyStore: UserClientKeysStore! { - guard zm_isSyncContext else { - fatal("Can't access key store: Currently not on sync context") - } - - let keyStore = userInfo.object(forKey: NSManagedObjectContext.ZMUserClientKeysStoreKey) - return keyStore as? UserClientKeysStore - } - - @objc - func zm_tearDownCryptKeyStore() { - userInfo.removeObject(forKey: NSManagedObjectContext.ZMUserClientKeysStoreKey) - } -} - -public extension FileManager { - - @objc static let keyStoreFolderPrefix = "otr" - - /// Returns the URL for the keyStore - @objc(keyStoreURLForAccountInDirectory:createParentIfNeeded:) - static func keyStoreURL(accountDirectory: URL, createParentIfNeeded: Bool) -> URL { - if createParentIfNeeded { - try! FileManager.default.createAndProtectDirectory(at: accountDirectory) - } - return accountDirectory.appendingPathComponent(FileManager.keyStoreFolderPrefix) - } - -} - -public enum UserClientKeyStoreError: Error { - case canNotGeneratePreKeys - case preKeysCountNeedsToBePositive -} - -/// A storage for cryptographic keys material -@objc(UserClientKeysStore) @objcMembers -open class UserClientKeysStore: NSObject { - - /// Maximum possible ID for prekey - public static let MaxPreKeyID: UInt16 = .max - 1 - - open var encryptionContext: EncryptionContext - - /// Fallback prekeys (when no other prekey is available, this will always work) - fileprivate var internalLastPreKey: String? - - /// Folder where the material is stored (managed by Cryptobox) - public private(set) var cryptoboxDirectory: URL - - public private(set) var applicationContainer: URL - - /// Loads new key store (if not present) or load an existing one - public init(accountDirectory: URL, applicationContainer: URL) { - self.cryptoboxDirectory = FileManager.keyStoreURL( - accountDirectory: accountDirectory, - createParentIfNeeded: true - ) - self.applicationContainer = applicationContainer - self.encryptionContext = UserClientKeysStore.setupContext(in: cryptoboxDirectory)! - } - - private static func setupContext(in directory: URL) -> EncryptionContext? { - try! FileManager.default.createAndProtectDirectory(at: directory) - return EncryptionContext(path: directory) - } - - open func deleteAndCreateNewBox() { - _ = try? FileManager.default.removeItem(at: cryptoboxDirectory) - encryptionContext = UserClientKeysStore.setupContext(in: cryptoboxDirectory)! - internalLastPreKey = nil - } - - open func lastPreKey() throws -> String { - var error: NSError? - if internalLastPreKey == nil { - encryptionContext.perform { [weak self] sessionsDirectory in - guard let self else { return } - do { - internalLastPreKey = try sessionsDirectory.generateLastPrekey() - } catch let anError as NSError { - error = anError - } - } - } - if let error { - throw error - } - return internalLastPreKey! - } - - open func generateMoreKeys(_ count: UInt16 = 1, start: UInt16 = 0) throws -> [(id: UInt16, prekey: String)] { - if count > 0 { - var error: Error? - var newPreKeys: [(id: UInt16, prekey: String)] = [] - - let range = preKeysRange(count, start: start) - encryptionContext.perform { sessionsDirectory in - do { - newPreKeys = try sessionsDirectory.generatePrekeys(range) - if newPreKeys.isEmpty { - error = UserClientKeyStoreError.canNotGeneratePreKeys - } - } catch let anError as NSError { - error = anError - } - } - if let error { - throw error - } - return newPreKeys - } - throw UserClientKeyStoreError.preKeysCountNeedsToBePositive - } - - fileprivate func preKeysRange(_ count: UInt16, start: UInt16) -> CountableRange { - if start >= UserClientKeysStore.MaxPreKeyID - count { - return 0 ..< count - } - return start ..< (start + count) - } - -} diff --git a/wire-ios-data-model/Support/Sources/SpyUserClientKeyStore.swift b/wire-ios-data-model/Support/Sources/SpyUserClientKeyStore.swift deleted file mode 100644 index 7a383f1ef58..00000000000 --- a/wire-ios-data-model/Support/Sources/SpyUserClientKeyStore.swift +++ /dev/null @@ -1,74 +0,0 @@ -// -// Wire -// Copyright (C) 2025 Wire Swiss GmbH -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see http://www.gnu.org/licenses/. -// - -import Foundation -import WireCryptobox -import WireDataModel - -// used by tests to fake errors on genrating pre keys -public class SpyUserClientKeyStore: UserClientKeysStore { - - public var failToGeneratePreKeys: Bool = false - public var failToGenerateLastPreKey: Bool = false - - public var lastGeneratedKeys: [(id: UInt16, prekey: String)] = [] - public var lastGeneratedLastPrekey: String? - - public override func generateMoreKeys(_ count: UInt16, start: UInt16) throws -> [(id: UInt16, prekey: String)] { - - if failToGeneratePreKeys { - let error = NSError( - domain: "cryptobox.error", - code: 0, - userInfo: ["reason": "using fake store with simulated fail"] - ) - throw error - } else { - let keys = try! super.generateMoreKeys(count, start: start) - lastGeneratedKeys = keys - return keys - } - } - - public override func lastPreKey() throws -> String { - if failToGenerateLastPreKey { - let error = NSError( - domain: "cryptobox.error", - code: 0, - userInfo: ["reason": "using fake store with simulated fail"] - ) - throw error - } else { - lastGeneratedLastPrekey = try! super.lastPreKey() - return lastGeneratedLastPrekey! - } - } - - public var accessEncryptionContextCount = 0 - - public override var encryptionContext: EncryptionContext { - get { - accessEncryptionContextCount += 1 - return super.encryptionContext - } - set { - super.encryptionContext = newValue - } - } - -} diff --git a/wire-ios-data-model/WireDataModel.xcodeproj/project.pbxproj b/wire-ios-data-model/WireDataModel.xcodeproj/project.pbxproj index f1326f2fbd9..2a1e5529022 100644 --- a/wire-ios-data-model/WireDataModel.xcodeproj/project.pbxproj +++ b/wire-ios-data-model/WireDataModel.xcodeproj/project.pbxproj @@ -386,6 +386,7 @@ 882B85752E83ED7E008A50CA /* StaleCoreCryptoKeysTrackerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 882B85742E83ED7A008A50CA /* StaleCoreCryptoKeysTrackerTests.swift */; }; 882B86BC2E843D0C008A50CA /* RemoveCoreCryptoKeysUseCaseTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 882B86BB2E843D05008A50CA /* RemoveCoreCryptoKeysUseCaseTests.swift */; }; 882B86BE2E8441AA008A50CA /* RemoveCoreCryptoKeysUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 882B86BD2E84419A008A50CA /* RemoveCoreCryptoKeysUseCase.swift */; }; + 8838CFA42EC4CB1B00495012 /* NSManagedObjectContext+AccountDirectory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8838CFA32EC4CB0D00495012 /* NSManagedObjectContext+AccountDirectory.swift */; }; 884371662EB8E9F3003DA083 /* store2-132-0.wiredatabase in Resources */ = {isa = PBXBuildFile; fileRef = 884371652EB8E9F3003DA083 /* store2-132-0.wiredatabase */; }; 88B81BD62EC232C600248D7A /* store2-133-0.wiredatabase in Resources */ = {isa = PBXBuildFile; fileRef = 88B81BD52EC232C600248D7A /* store2-133-0.wiredatabase */; }; A90676E7238EAE8B006417AC /* ParticipantRole.swift in Sources */ = {isa = PBXBuildFile; fileRef = A90676E6238EAE8B006417AC /* ParticipantRole.swift */; }; @@ -503,7 +504,6 @@ E9C7DD9B27B533D000FB9AE8 /* AccessRoleMappingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9C7DD9A27B533D000FB9AE8 /* AccessRoleMappingTests.swift */; }; EE002F222878345C0027D63A /* store2-104-0.wiredatabase in Resources */ = {isa = PBXBuildFile; fileRef = EE002F212878345C0027D63A /* store2-104-0.wiredatabase */; }; EE032B3129A62CA600E1DDF3 /* ProteusSessionID.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE032B2F29A62CA600E1DDF3 /* ProteusSessionID.swift */; }; - EE032B3229A62CA600E1DDF3 /* ProteusSessionID+Mapping.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE032B3029A62CA600E1DDF3 /* ProteusSessionID+Mapping.swift */; }; EE032B3629A62CD600E1DDF3 /* ProteusServiceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE032B3529A62CD600E1DDF3 /* ProteusServiceTests.swift */; }; EE04084E28CA85B2009E4B8D /* Date+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE04084D28CA85B2009E4B8D /* Date+Helpers.swift */; }; EE0DE5042A24D2A10029746C /* DeleteSubgroupAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE0DE5032A24D2A10029746C /* DeleteSubgroupAction.swift */; }; @@ -661,7 +661,6 @@ F9A706B41CAEE01D00C2F5FE /* ObjectChangeInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9A706391CAEE01D00C2F5FE /* ObjectChangeInfo.swift */; }; F9A706B61CAEE01D00C2F5FE /* UserClientChangeInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9A7063B1CAEE01D00C2F5FE /* UserClientChangeInfo.swift */; }; F9A706B71CAEE01D00C2F5FE /* UserChangeInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9A7063C1CAEE01D00C2F5FE /* UserChangeInfo.swift */; }; - F9A706BD1CAEE01D00C2F5FE /* CryptoBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9A706431CAEE01D00C2F5FE /* CryptoBox.swift */; }; F9A706C31CAEE01D00C2F5FE /* UserImageLocalCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9A706491CAEE01D00C2F5FE /* UserImageLocalCache.swift */; }; F9A706C51CAEE01D00C2F5FE /* ZMFetchRequestBatch.h in Headers */ = {isa = PBXBuildFile; fileRef = F9A7064B1CAEE01D00C2F5FE /* ZMFetchRequestBatch.h */; settings = {ATTRIBUTES = (Public, ); }; }; F9A706C61CAEE01D00C2F5FE /* ZMFetchRequestBatch.m in Sources */ = {isa = PBXBuildFile; fileRef = F9A7064C1CAEE01D00C2F5FE /* ZMFetchRequestBatch.m */; }; @@ -1129,6 +1128,7 @@ 882B85742E83ED7A008A50CA /* StaleCoreCryptoKeysTrackerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StaleCoreCryptoKeysTrackerTests.swift; sourceTree = ""; }; 882B86BB2E843D05008A50CA /* RemoveCoreCryptoKeysUseCaseTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoveCoreCryptoKeysUseCaseTests.swift; sourceTree = ""; }; 882B86BD2E84419A008A50CA /* RemoveCoreCryptoKeysUseCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoveCoreCryptoKeysUseCase.swift; sourceTree = ""; }; + 8838CFA32EC4CB0D00495012 /* NSManagedObjectContext+AccountDirectory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSManagedObjectContext+AccountDirectory.swift"; sourceTree = ""; }; 884371652EB8E9F3003DA083 /* store2-132-0.wiredatabase */ = {isa = PBXFileReference; lastKnownFileType = file; path = "store2-132-0.wiredatabase"; sourceTree = ""; }; 88B81BD52EC232C600248D7A /* store2-133-0.wiredatabase */ = {isa = PBXFileReference; lastKnownFileType = file; path = "store2-133-0.wiredatabase"; sourceTree = ""; }; A90676E6238EAE8B006417AC /* ParticipantRole.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParticipantRole.swift; sourceTree = ""; }; @@ -1243,7 +1243,6 @@ E9C7DD9A27B533D000FB9AE8 /* AccessRoleMappingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccessRoleMappingTests.swift; sourceTree = ""; }; EE002F212878345C0027D63A /* store2-104-0.wiredatabase */ = {isa = PBXFileReference; lastKnownFileType = file; path = "store2-104-0.wiredatabase"; sourceTree = ""; }; EE032B2F29A62CA600E1DDF3 /* ProteusSessionID.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProteusSessionID.swift; sourceTree = ""; }; - EE032B3029A62CA600E1DDF3 /* ProteusSessionID+Mapping.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ProteusSessionID+Mapping.swift"; sourceTree = ""; }; EE032B3529A62CD600E1DDF3 /* ProteusServiceTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProteusServiceTests.swift; sourceTree = ""; }; EE04084D28CA85B2009E4B8D /* Date+Helpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+Helpers.swift"; sourceTree = ""; }; EE0DE5032A24D2A10029746C /* DeleteSubgroupAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeleteSubgroupAction.swift; sourceTree = ""; }; @@ -1407,7 +1406,6 @@ F9A706391CAEE01D00C2F5FE /* ObjectChangeInfo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObjectChangeInfo.swift; sourceTree = ""; }; F9A7063B1CAEE01D00C2F5FE /* UserClientChangeInfo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserClientChangeInfo.swift; sourceTree = ""; }; F9A7063C1CAEE01D00C2F5FE /* UserChangeInfo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserChangeInfo.swift; sourceTree = ""; }; - F9A706431CAEE01D00C2F5FE /* CryptoBox.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CryptoBox.swift; sourceTree = ""; }; F9A706491CAEE01D00C2F5FE /* UserImageLocalCache.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserImageLocalCache.swift; sourceTree = ""; }; F9A7064B1CAEE01D00C2F5FE /* ZMFetchRequestBatch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ZMFetchRequestBatch.h; sourceTree = ""; }; F9A7064C1CAEE01D00C2F5FE /* ZMFetchRequestBatch.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ZMFetchRequestBatch.m; sourceTree = ""; }; @@ -1910,7 +1908,6 @@ 63B1333729A503D000009D84 /* ProteusServiceInterface.swift */, 63B1333829A503D000009D84 /* ProteusService.swift */, EE032B2F29A62CA600E1DDF3 /* ProteusSessionID.swift */, - EE032B3029A62CA600E1DDF3 /* ProteusSessionID+Mapping.swift */, EE79699529D4684C00075E38 /* CryptoboxMigrationManager.swift */, ); path = Proteus; @@ -2297,6 +2294,7 @@ F9A705CC1CAEE01D00C2F5FE /* NSManagedObjectContext+zmessaging.h */, F9A705CD1CAEE01D00C2F5FE /* NSManagedObjectContext+zmessaging.m */, 0179629B2B83FCA800D6C7B6 /* NSManagedObjectContext+Migration.swift */, + 8838CFA32EC4CB0D00495012 /* NSManagedObjectContext+AccountDirectory.swift */, 017B55782CFFB560005FFBD4 /* NSManagedObjectContext+SyncResources.swift */, 54FB03AE1E41FC86000E13DC /* NSManagedObjectContext+Patches.swift */, 347FA5FF2E70131700F34C6A /* NSManagedObjectContext+BackendMetadata.swift */, @@ -2534,7 +2532,6 @@ F9331C811CB4191B00139ECC /* NSPredicate+ZMSearch.h */, D5FA30CE2063F8EC00716618 /* Version.swift */, F9331C821CB4191B00139ECC /* NSPredicate+ZMSearch.m */, - F9A706431CAEE01D00C2F5FE /* CryptoBox.swift */, F9A706491CAEE01D00C2F5FE /* UserImageLocalCache.swift */, 014DD8D22B6D1FE9007ECFD1 /* UUID+SafeLogging.swift */, F9A7064B1CAEE01D00C2F5FE /* ZMFetchRequestBatch.h */, @@ -3593,7 +3590,6 @@ F9331C841CB4191B00139ECC /* NSPredicate+ZMSearch.m in Sources */, BFCD502D21511D58008CD845 /* DraftMessage.swift in Sources */, 63B1336B29A503D100009D84 /* MLSGroupStatus.swift in Sources */, - F9A706BD1CAEE01D00C2F5FE /* CryptoBox.swift in Sources */, EE3EFE9725305A84009499E5 /* ModifiedObjects+Mergeable.swift in Sources */, EE42938A252C437900E70670 /* Notification.Name+ManagedObjectObservation.swift in Sources */, 16AD86BA1F75426C00E4C797 /* NSManagedObjectContext+NotificationContext.swift in Sources */, @@ -3767,6 +3763,7 @@ F99C5B8A1ED460E20049CCD7 /* TeamChangeInfo.swift in Sources */, 165124D221886EDB006A3C75 /* ZMOTRMessage+Quotes.swift in Sources */, 6308F8A42A273C680072A177 /* FetchMLSSubconversationGroupInfoAction.swift in Sources */, + 8838CFA42EC4CB1B00495012 /* NSManagedObjectContext+AccountDirectory.swift in Sources */, 87D9CCE91F27606200AA4388 /* NSManagedObjectContext+TearDown.swift in Sources */, F963E9711D9ADD5A00098AD3 /* ZMImageAssetEncryptionKeys.m in Sources */, 0604F7C8265184B70016A71E /* ZMSystemMessage+ParticipantsRemovedReason.swift in Sources */, @@ -3782,7 +3779,6 @@ F9A706B71CAEE01D00C2F5FE /* UserChangeInfo.swift in Sources */, EE68EEC9252DC4450013B242 /* ChangeDetector.swift in Sources */, 63B1335629A503D100009D84 /* MLSGroup.swift in Sources */, - EE032B3229A62CA600E1DDF3 /* ProteusSessionID+Mapping.swift in Sources */, F9AB00271F0CE5520037B437 /* FileManager+FileLocations.swift in Sources */, F9A7067F1CAEE01D00C2F5FE /* ZMImageMessage.m in Sources */, 63D41E7124597E420076826F /* GenericMessage+Flags.swift in Sources */, diff --git a/wire-ios-request-strategy/Sources/Helpers/EncryptionSessionDirectory+UpdateEvents.swift b/wire-ios-request-strategy/Sources/Helpers/EncryptionSessionDirectory+UpdateEvents.swift deleted file mode 100644 index cad5b7bcae9..00000000000 --- a/wire-ios-request-strategy/Sources/Helpers/EncryptionSessionDirectory+UpdateEvents.swift +++ /dev/null @@ -1,40 +0,0 @@ -// -// Wire -// Copyright (C) 2025 Wire Swiss GmbH -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see http://www.gnu.org/licenses/. -// - -import Foundation -import WireCryptobox -import WireSystem - -private let zmLog = ZMSLog(tag: "cryptobox") - -extension EncryptionSessionsDirectory { - - func decryptData( - _ encryptedData: Data, - for sessionID: EncryptionSessionIdentifier - ) throws -> (didCreateNewSession: Bool, decryptedData: Data) { - if hasSession(for: sessionID) { - let decryptedData = try decrypt(encryptedData, from: sessionID) - return (didCreateNewSession: false, decryptedData: decryptedData) - } else { - let decryptedData = try createClientSessionAndReturnPlaintext(for: sessionID, prekeyMessage: encryptedData) - return (didCreateNewSession: true, decryptedData: decryptedData) - } - } - -} diff --git a/wire-ios-request-strategy/Sources/Request Strategies/User Clients/FetchingClientRequestStrategyTests.swift b/wire-ios-request-strategy/Sources/Request Strategies/User Clients/FetchingClientRequestStrategyTests.swift index 0548b8b9f74..11de77eb968 100644 --- a/wire-ios-request-strategy/Sources/Request Strategies/User Clients/FetchingClientRequestStrategyTests.swift +++ b/wire-ios-request-strategy/Sources/Request Strategies/User Clients/FetchingClientRequestStrategyTests.swift @@ -684,22 +684,22 @@ final class FetchClientRequestStrategyTests: MessagingTestBase { sut = createSUT(apiVersion: apiVersion) var payload: ZMTransportData! let remoteIdentifier = "aabbccdd0011" - var sessionIdentifier: EncryptionSessionIdentifier! + var sessionIdentifier: ProteusSessionID! syncMOC.performGroupedAndWait { - sessionIdentifier = EncryptionSessionIdentifier( - userId: self.otherUser!.remoteIdentifier.uuidString, - clientId: remoteIdentifier + sessionIdentifier = ProteusSessionID( + userID: self.otherUser!.remoteIdentifier.uuidString, + clientID: remoteIdentifier ) self.otherUser.fetchUserClients() payload = [["id": remoteIdentifier, "class": "phone"]] as NSArray // swiftlint:disable:next todo_requires_jira_link - // TODO: [John] use flag here - self.syncMOC.zm_cryptKeyStore.encryptionContext.perform { - try! $0.createClientSession( - sessionIdentifier, - base64PreKeyString: self.syncMOC.zm_cryptKeyStore.lastPreKey() - ) // just a bogus key is OK - } +// // TODO: [John] use flag here +// self.syncMOC.zm_cryptKeyStore.encryptionContext.perform { +// try! $0.createClientSession( +// sessionIdentifier, +// base64PreKeyString: self.syncMOC.zm_cryptKeyStore.lastPreKey() +// ) // just a bogus key is OK +// } } XCTAssertTrue(waitForAllGroupsToBeEmpty(withTimeout: 0.2)) let response = ZMTransportResponse( diff --git a/wire-ios-request-strategy/WireRequestStrategy.xcodeproj/project.pbxproj b/wire-ios-request-strategy/WireRequestStrategy.xcodeproj/project.pbxproj index 44cec563e67..ca610f66aec 100644 --- a/wire-ios-request-strategy/WireRequestStrategy.xcodeproj/project.pbxproj +++ b/wire-ios-request-strategy/WireRequestStrategy.xcodeproj/project.pbxproj @@ -394,7 +394,6 @@ F18401D42073BE0800E9F4CC /* MessageExpirationTimer.swift in Sources */ = {isa = PBXBuildFile; fileRef = F18401942073BE0800E9F4CC /* MessageExpirationTimer.swift */; }; F18401D62073BE0800E9F4CC /* ZMStrategyConfigurationOption.h in Headers */ = {isa = PBXBuildFile; fileRef = F18401962073BE0800E9F4CC /* ZMStrategyConfigurationOption.h */; settings = {ATTRIBUTES = (Public, ); }; }; F18401D82073BE0800E9F4CC /* ZMConversation+Notifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = F18401982073BE0800E9F4CC /* ZMConversation+Notifications.swift */; }; - F18401D92073BE0800E9F4CC /* EncryptionSessionDirectory+UpdateEvents.swift in Sources */ = {isa = PBXBuildFile; fileRef = F18401992073BE0800E9F4CC /* EncryptionSessionDirectory+UpdateEvents.swift */; }; F18401DB2073C25300E9F4CC /* EventDecoderDecryptionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F18401952073BE0800E9F4CC /* EventDecoderDecryptionTests.swift */; }; F18401DC2073C25300E9F4CC /* MessageExpirationTimerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F18401972073BE0800E9F4CC /* MessageExpirationTimerTests.swift */; }; F18401E02073C25900E9F4CC /* ClientMessageRequestFactoryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F18401542073BE0800E9F4CC /* ClientMessageRequestFactoryTests.swift */; }; @@ -921,7 +920,6 @@ F18401962073BE0800E9F4CC /* ZMStrategyConfigurationOption.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ZMStrategyConfigurationOption.h; sourceTree = ""; }; F18401972073BE0800E9F4CC /* MessageExpirationTimerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageExpirationTimerTests.swift; sourceTree = ""; }; F18401982073BE0800E9F4CC /* ZMConversation+Notifications.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ZMConversation+Notifications.swift"; sourceTree = ""; }; - F18401992073BE0800E9F4CC /* EncryptionSessionDirectory+UpdateEvents.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "EncryptionSessionDirectory+UpdateEvents.swift"; sourceTree = ""; }; F18401F62073C2E500E9F4CC /* MessagingTestBase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessagingTestBase.swift; sourceTree = ""; }; F18401F72073C2E600E9F4CC /* RequestStrategyTestBase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RequestStrategyTestBase.swift; sourceTree = ""; }; F18401F82073C2E600E9F4CC /* ProteusClientSimulator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProteusClientSimulator.swift; sourceTree = ""; }; @@ -2109,7 +2107,6 @@ F18401942073BE0800E9F4CC /* MessageExpirationTimer.swift */, F18401972073BE0800E9F4CC /* MessageExpirationTimerTests.swift */, F18401932073BE0800E9F4CC /* ZMMessage+Dependency.swift */, - F18401992073BE0800E9F4CC /* EncryptionSessionDirectory+UpdateEvents.swift */, 0649D14E24F63DCC001DDC78 /* ZMSound.swift */, 0649D1A524F673E7001DDC78 /* Logging.swift */, F18401952073BE0800E9F4CC /* EventDecoderDecryptionTests.swift */, @@ -2504,7 +2501,6 @@ E629E3B52B4703E100D526AD /* Payload+ConversationAddMember.swift in Sources */, 1623F8DE2AE945E2004F0319 /* MessageAPI.swift in Sources */, E629E3C82B470AB600D526AD /* Payload+UpdateConversationMessageTimer.swift in Sources */, - F18401D92073BE0800E9F4CC /* EncryptionSessionDirectory+UpdateEvents.swift in Sources */, EEDBD0A92A7B7F0C00F3956F /* SelfUserRequestStrategy.swift in Sources */, F18401C72073BE0800E9F4CC /* AssetV3PreviewDownloadStrategy.swift in Sources */, 16367F1E26FDB0740028AF8B /* Payload+Connection.swift in Sources */, diff --git a/wire-ios-sync-engine/Source/UserSession/ZMUserSession/ZMUserSession+Debugging.swift b/wire-ios-sync-engine/Source/UserSession/ZMUserSession/ZMUserSession+Debugging.swift index 6ebc255726d..e5dcbda3261 100644 --- a/wire-ios-sync-engine/Source/UserSession/ZMUserSession/ZMUserSession+Debugging.swift +++ b/wire-ios-sync-engine/Source/UserSession/ZMUserSession/ZMUserSession+Debugging.swift @@ -17,7 +17,6 @@ // import Foundation -import WireCryptobox extension ZMUserSession { @@ -50,7 +49,6 @@ extension ZMUserSession { static func initDebugCommands() -> [String: DebugCommand] { let commands = [ - DebugCommandLogEncryption(), DebugCommandShowIdentifiers(), DebugCommandHelp(), DebugCommandVariables() @@ -153,113 +151,6 @@ public enum DebugCommandResult { // MARK: - Command execution -private extension EncryptionSessionIdentifier { - - init?(string: String) { - let split = string.split(separator: "_") - guard split.count == 2 else { return nil } - let user = String(split[0]) - let client = String(split[1]) - self.init(userId: user, clientId: client) - } -} - -private class DebugCommandLogEncryption: DebugCommandMixin { - - var currentlyEnabledLogs: Set = Set() - - private var usage: String { - "\(keyword) " - } - - init() { - super.init(keyword: "logEncryption") - } - - override func execute( - arguments: [String], - userSession: ZMUserSession, - state: [String: Any], - onComplete: @escaping ((DebugCommandResult) -> Void) - ) { - defer { - saveEnabledLogs(userSession: userSession) - } - - if arguments.first == "list" { - return onComplete(.success( - info: - "Enabled:\n" + - currentlyEnabledLogs - .map(\.rawValue) - .joined(separator: "\n") - )) - } - - guard arguments.count == 2, - arguments[0] == "add" || arguments[0] == "remove" - else { - return onComplete(.failure(error: "usage: \(usage)")) - } - - let isAdding = arguments[0] == "add" - let subject = arguments[1] - - userSession.syncManagedObjectContext.perform { - // swiftlint:disable:next todo_requires_jira_link - // TODO: [John] use flag here - guard let keyStore = userSession.syncManagedObjectContext.zm_cryptKeyStore else { - return onComplete(.failure(error: "No encryption context")) - } - - if !isAdding, subject == "all" { - keyStore.encryptionContext.disableExtendedLoggingOnAllSessions() - self.currentlyEnabledLogs = Set() - return onComplete(.success(info: "all removed")) - } - - guard let identifier = EncryptionSessionIdentifier(string: subject) else { - return onComplete(.failure(error: "Invalid id \(subject)")) - } - - if isAdding { - self.currentlyEnabledLogs.insert(identifier) - } else { - self.currentlyEnabledLogs.remove(identifier) - } - - keyStore.encryptionContext.setExtendedLogging(identifier: identifier, enabled: isAdding) - return onComplete(.success(info: "Added logging for identifier \(identifier)")) - } - } - - private let logsKey = "enabledLogs" - - private func saveEnabledLogs(userSession: ZMUserSession) { - let idsToSave = currentlyEnabledLogs.map(\.rawValue) - saveState(userSession: userSession, state: [logsKey: idsToSave]) - } - - override func restoreFromState( - userSession: ZMUserSession, - state: [String: Any] - ) { - guard let logs = state[logsKey] as? [String] else { return } - currentlyEnabledLogs = Set(logs.compactMap { - EncryptionSessionIdentifier(string: $0) - }) - userSession.syncManagedObjectContext.performGroupedBlock { - guard let keyStore = userSession.syncManagedObjectContext.zm_cryptKeyStore else { - return - } - - self.currentlyEnabledLogs.forEach { - keyStore.encryptionContext.setExtendedLogging(identifier: $0, enabled: true) - } - } - } -} - /// Show the user and client identifier private class DebugCommandShowIdentifiers: DebugCommandMixin { @@ -285,7 +176,7 @@ private class DebugCommandShowIdentifiers: DebugCommandMixin { info: "User: \(user.remoteIdentifier.uuidString)\n" + "Client: \(client.remoteIdentifier ?? "-")\n" + - "Session: \(client.sessionIdentifier?.rawValue ?? "-")" + "Session: \(client.proteusSessionID?.rawValue ?? "-")" )) } } From 0abc7ebcdc231a4dbc733177a764737881b2668c Mon Sep 17 00:00:00 2001 From: David-Henner Date: Fri, 28 Nov 2025 15:37:29 +0100 Subject: [PATCH 13/28] update tests --- .../CoreCryptoConfigProviderTests.swift | 22 ++++++++---- .../Source/Model/Messages/OtrBaseTest.swift | 19 ---------- .../Model/UserClient/UserClientTests.swift | 4 +-- .../Utils/FileManager+FileLocationTests.swift | 36 ------------------- .../Source/Model/ZMBaseManagedObjectTest.h | 2 -- .../Source/Model/ZMBaseManagedObjectTest.m | 11 ------ .../WireDataModel.xcodeproj/project.pbxproj | 12 ------- 7 files changed, 18 insertions(+), 88 deletions(-) diff --git a/wire-ios-data-model/Tests/Core Crypto/CoreCryptoConfigProviderTests.swift b/wire-ios-data-model/Tests/Core Crypto/CoreCryptoConfigProviderTests.swift index c854212b3d2..cc2928096b3 100644 --- a/wire-ios-data-model/Tests/Core Crypto/CoreCryptoConfigProviderTests.swift +++ b/wire-ios-data-model/Tests/Core Crypto/CoreCryptoConfigProviderTests.swift @@ -44,9 +44,18 @@ class CoreCryptoConfigProviderTests: ZMConversationTestsBase { private var mockCoreCryptoKeyProvider: MockCoreCryptoKeyProvider! private var sut: CoreCryptoConfigProvider! private var mockCoreCryptoKeyMigrationManager = MockCoreCryptoKeyMigrationManagerProtocol() - + private var sharedContainerURL: URL! + override func setUp() { super.setUp() + + sharedContainerURL = try! FileManager.default.url( + for: .applicationSupportDirectory, + in: .userDomainMask, + appropriateFor: nil, + create: true + ) + mockCoreCryptoKeyProvider = MockCoreCryptoKeyProvider( coreCryptoKeyMigrationManager: mockCoreCryptoKeyMigrationManager, @@ -59,6 +68,7 @@ class CoreCryptoConfigProviderTests: ZMConversationTestsBase { override func tearDown() { mockCoreCryptoKeyProvider = nil + sharedContainerURL = nil super.tearDown() } @@ -66,7 +76,7 @@ class CoreCryptoConfigProviderTests: ZMConversationTestsBase { func test_itReturnsInitialCoreCryptoConfiguration() async throws { // GIVEN - let selfUserID: UUID = syncMOC.performAndWait { + let selfUserID: UUID = syncMOC.performAndWait { [syncMOC] in let user = ZMUser.selfUser(in: syncMOC) user.remoteIdentifier = UUID.create() return user.remoteIdentifier @@ -80,7 +90,7 @@ class CoreCryptoConfigProviderTests: ZMConversationTestsBase { // WHEN let configuration = try await sut.createInitialConfiguration( - sharedContainerURL: OtrBaseTest.sharedContainerURL, + sharedContainerURL: sharedContainerURL, userID: selfUserID, allowKeyCreation: true ) @@ -92,7 +102,7 @@ class CoreCryptoConfigProviderTests: ZMConversationTestsBase { func test_itThrows_FailedToGetCoreCryptoKey() async { // GIVEN - let selfUserID: UUID = syncMOC.performAndWait { + let selfUserID: UUID = syncMOC.performAndWait { [syncMOC] in let user = ZMUser.selfUser(in: syncMOC) user.remoteIdentifier = UUID.create() return user.remoteIdentifier @@ -107,7 +117,7 @@ class CoreCryptoConfigProviderTests: ZMConversationTestsBase { await assertItThrows(error: CoreCryptoConfigProvider.ConfigurationSetupFailure.failedToGetCoreCryptoKey) { // WHEN _ = try await sut.createInitialConfiguration( - sharedContainerURL: OtrBaseTest.sharedContainerURL, + sharedContainerURL: sharedContainerURL, userID: selfUserID, allowKeyCreation: true ) @@ -119,7 +129,7 @@ class CoreCryptoConfigProviderTests: ZMConversationTestsBase { private func expectedPath(_ selfUserId: UUID) -> String { let accountDirectory = CoreDataStack.accountDataFolder( accountIdentifier: selfUserId, - applicationContainer: OtrBaseTest.sharedContainerURL + applicationContainer: sharedContainerURL ) return accountDirectory.appendingPathComponent("corecrypto").path } diff --git a/wire-ios-data-model/Tests/Source/Model/Messages/OtrBaseTest.swift b/wire-ios-data-model/Tests/Source/Model/Messages/OtrBaseTest.swift index 5c38d23844c..497e8f1019d 100644 --- a/wire-ios-data-model/Tests/Source/Model/Messages/OtrBaseTest.swift +++ b/wire-ios-data-model/Tests/Source/Model/Messages/OtrBaseTest.swift @@ -42,23 +42,4 @@ class OtrBaseTest: XCTestCase { ) } - static func otrDirectoryURL(accountIdentifier: UUID) -> URL { - let accountDirectory = CoreDataStack.accountDataFolder( - accountIdentifier: accountIdentifier, - applicationContainer: sharedContainerURL - ) - return FileManager.keyStoreURL(accountDirectory: accountDirectory, createParentIfNeeded: true) - } - - static var legacyOtrDirectory: URL { - FileManager.keyStoreURL(accountDirectory: sharedContainerURL, createParentIfNeeded: true) - } - - static func legacyAccountOtrDirectory(accountIdentifier: UUID) -> URL { - FileManager.keyStoreURL( - accountDirectory: sharedContainerURL.appendingPathComponent(accountIdentifier.uuidString), - createParentIfNeeded: true - ) - } - } diff --git a/wire-ios-data-model/Tests/Source/Model/UserClient/UserClientTests.swift b/wire-ios-data-model/Tests/Source/Model/UserClient/UserClientTests.swift index 3dbaf86405c..e6bed78d4bd 100644 --- a/wire-ios-data-model/Tests/Source/Model/UserClient/UserClientTests.swift +++ b/wire-ios-data-model/Tests/Source/Model/UserClient/UserClientTests.swift @@ -495,7 +495,7 @@ extension UserClientTests { XCTAssertNotNil(newClient) XCTAssertNotNil(newClient.user) XCTAssertEqual(newClient.user, ZMUser.selfUser(in: uiMOC)) - XCTAssertNotNil(newClient.sessionIdentifier) + XCTAssertNotNil(newClient.proteusSessionID) XCTAssertEqual(newClient.mlsPublicKeys.ed25519, "some key") XCTAssertEqual(newClient.isConsumableNotificationsCapable, false) } @@ -540,7 +540,7 @@ extension UserClientTests { XCTAssertNotNil(newClient) XCTAssertNotNil(newClient.user) XCTAssertEqual(newClient.user, ZMUser.selfUser(in: uiMOC)) - XCTAssertNil(newClient.sessionIdentifier) + XCTAssertNil(newClient.proteusSessionID) } func testThatItSetsNeedsSessionMigration_WhenInsertingANewSelfUserClientAndDomainIsNil() { diff --git a/wire-ios-data-model/Tests/Source/Model/Utils/FileManager+FileLocationTests.swift b/wire-ios-data-model/Tests/Source/Model/Utils/FileManager+FileLocationTests.swift index 8bf5e6e527e..a024d8bacba 100644 --- a/wire-ios-data-model/Tests/Source/Model/Utils/FileManager+FileLocationTests.swift +++ b/wire-ios-data-model/Tests/Source/Model/Utils/FileManager+FileLocationTests.swift @@ -18,42 +18,6 @@ import Foundation -class FileManager_CryptoboxTests: XCTestCase { - - func testThatItReturnsTheCryptoboxPath() { - // given - let url = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first! - - // when - let storeURL = FileManager.keyStoreURL(accountDirectory: url, createParentIfNeeded: false) - - // then - var components = storeURL.pathComponents - - guard !components.isEmpty else { return XCTFail() } - let otrComp = components.removeLast() - XCTAssertEqual(otrComp, FileManager.keyStoreFolderPrefix) - XCTAssertEqual(components, url.pathComponents) - } - - func testThatItCreatesTheParentDirectoryIfNeededAndExcludesItFromBackup() { - // given - let url = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first! - - // when - let storeURL = FileManager.keyStoreURL(accountDirectory: url, createParentIfNeeded: true) - - // then - let parentURL = storeURL.deletingLastPathComponent() - var isDirectory: ObjCBool = false - XCTAssertTrue(FileManager.default.fileExists(atPath: parentURL.path, isDirectory: &isDirectory)) - XCTAssertTrue(isDirectory.boolValue) - XCTAssertTrue(parentURL.isExcludedFromBackup) - - try? FileManager.default.removeItem(at: parentURL) - } -} - class FileManager_CacheTests: XCTestCase { func testThatItReturnsTheCachesDirectory_WithAccountId() { diff --git a/wire-ios-data-model/Tests/Source/Model/ZMBaseManagedObjectTest.h b/wire-ios-data-model/Tests/Source/Model/ZMBaseManagedObjectTest.h index 67681814f61..976ff13e93a 100644 --- a/wire-ios-data-model/Tests/Source/Model/ZMBaseManagedObjectTest.h +++ b/wire-ios-data-model/Tests/Source/Model/ZMBaseManagedObjectTest.h @@ -92,8 +92,6 @@ - (nonnull UserClient *)createSelfClient; - (nonnull UserClient *)createSelfClientOnMOC:(nonnull NSManagedObjectContext *)moc; -- (nonnull UserClient *)createClientForUser:(nonnull ZMUser *)user createSessionWithSelfUser:(BOOL)createSessionWithSeflUser; - @end diff --git a/wire-ios-data-model/Tests/Source/Model/ZMBaseManagedObjectTest.m b/wire-ios-data-model/Tests/Source/Model/ZMBaseManagedObjectTest.m index dd67a6515df..306580f9d84 100644 --- a/wire-ios-data-model/Tests/Source/Model/ZMBaseManagedObjectTest.m +++ b/wire-ios-data-model/Tests/Source/Model/ZMBaseManagedObjectTest.m @@ -103,7 +103,6 @@ - (void)setUp; ZM_SILENCE_CALL_TO_UNKNOWN_SELECTOR([self performSelector:selector]); } - [self setupKeyStore]; [self setupTimers]; [self setupCaches]; @@ -118,16 +117,6 @@ - (void)setupTimers [self.uiMOC zm_createMessageDeletionTimer]; } -- (void)setupKeyStore -{ - [self performPretendingUiMocIsSyncMoc:^{ - NSURL *url = [CoreDataStack accountDataFolderWithAccountIdentifier:self.userIdentifier - applicationContainer:self.storageDirectory]; - [self.uiMOC setupUserKeyStoreInAccountDirectory:url - applicationContainer:self.storageDirectory]; - }]; -} - - (void)tearDown; { ZMConversationDefaultLastReadTimestampSaveDelay = self.originalConversationLastReadTimestampTimerValue; diff --git a/wire-ios-data-model/WireDataModel.xcodeproj/project.pbxproj b/wire-ios-data-model/WireDataModel.xcodeproj/project.pbxproj index 2a1e5529022..75d389314d4 100644 --- a/wire-ios-data-model/WireDataModel.xcodeproj/project.pbxproj +++ b/wire-ios-data-model/WireDataModel.xcodeproj/project.pbxproj @@ -607,9 +607,6 @@ F92C992D1DAFC5AC0034AFDD /* ZMConversationTests+Ephemeral.swift in Sources */ = {isa = PBXBuildFile; fileRef = F92C992B1DAFC58A0034AFDD /* ZMConversationTests+Ephemeral.swift */; }; F93265211D8950F10076AAD6 /* NSManagedObjectContext+FetchRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = F93265201D8950F10076AAD6 /* NSManagedObjectContext+FetchRequest.swift */; }; F93265291D89648B0076AAD6 /* ZMAssetClientMessageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9B71F5D1CB2BC85001DB03F /* ZMAssetClientMessageTests.swift */; }; - F9331C521CB3BC6800139ECC /* CryptoBoxTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9331C501CB3BC6800139ECC /* CryptoBoxTests.swift */; }; - F9331C561CB3BCF300139ECC /* OtrBaseTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9331C541CB3BCDA00139ECC /* OtrBaseTest.swift */; }; - F9331C5C1CB3BF9F00139ECC /* UserClientKeyStoreTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9331C5B1CB3BF9F00139ECC /* UserClientKeyStoreTests.swift */; }; F9331C831CB4191B00139ECC /* NSPredicate+ZMSearch.h in Headers */ = {isa = PBXBuildFile; fileRef = F9331C811CB4191B00139ECC /* NSPredicate+ZMSearch.h */; settings = {ATTRIBUTES = (Public, ); }; }; F9331C841CB4191B00139ECC /* NSPredicate+ZMSearch.m in Sources */ = {isa = PBXBuildFile; fileRef = F9331C821CB4191B00139ECC /* NSPredicate+ZMSearch.m */; }; F9331C871CB419B500139ECC /* NSFetchRequest+ZMRelationshipKeyPaths.h in Headers */ = {isa = PBXBuildFile; fileRef = F9331C851CB419B500139ECC /* NSFetchRequest+ZMRelationshipKeyPaths.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -1350,9 +1347,6 @@ F92C99271DAE8D060034AFDD /* GenericMessageTests+Obfuscation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "GenericMessageTests+Obfuscation.swift"; sourceTree = ""; }; F92C992B1DAFC58A0034AFDD /* ZMConversationTests+Ephemeral.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ZMConversationTests+Ephemeral.swift"; sourceTree = ""; }; F93265201D8950F10076AAD6 /* NSManagedObjectContext+FetchRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSManagedObjectContext+FetchRequest.swift"; sourceTree = ""; }; - F9331C501CB3BC6800139ECC /* CryptoBoxTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CryptoBoxTests.swift; sourceTree = ""; }; - F9331C541CB3BCDA00139ECC /* OtrBaseTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OtrBaseTest.swift; sourceTree = ""; }; - F9331C5B1CB3BF9F00139ECC /* UserClientKeyStoreTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserClientKeyStoreTests.swift; sourceTree = ""; }; F9331C811CB4191B00139ECC /* NSPredicate+ZMSearch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSPredicate+ZMSearch.h"; sourceTree = ""; }; F9331C821CB4191B00139ECC /* NSPredicate+ZMSearch.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSPredicate+ZMSearch.m"; sourceTree = ""; }; F9331C851CB419B500139ECC /* NSFetchRequest+ZMRelationshipKeyPaths.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSFetchRequest+ZMRelationshipKeyPaths.h"; sourceTree = ""; }; @@ -2759,8 +2753,6 @@ 63370CF62431F4FA0072C37F /* Composite */, EEE83B491FBB496B00FC0296 /* ZMMessageTimerTests.swift */, 54EDE6811CBBF6260044A17E /* FileAssetCacheTests.swift */, - F9331C541CB3BCDA00139ECC /* OtrBaseTest.swift */, - F9331C501CB3BC6800139ECC /* CryptoBoxTests.swift */, 16C391E1214BD437003AB3AD /* MentionTests.swift */, F9B71F5D1CB2BC85001DB03F /* ZMAssetClientMessageTests.swift */, 168414292228421700FCB9BC /* ZMAssetClientMessageTests+AssetMessage.swift */, @@ -2880,7 +2872,6 @@ isa = PBXGroup; children = ( F13A89D22106293000AB40CB /* PushTokenTests.swift */, - F9331C5B1CB3BF9F00139ECC /* UserClientKeyStoreTests.swift */, F9B720021CB2C68B001DB03F /* UserClientTests.swift */, 631A0585240439470062B387 /* UserClientTests+SafeLogging.swift */, 1693155225A30D4E00709F15 /* UserClientTests+ResetSession.swift */, @@ -3925,7 +3916,6 @@ EEDE7DB728EC1618007DC6A3 /* MockMLSActionExecutor.swift in Sources */, 63C07015291144F70075D598 /* CoreCryptoConfigProviderTests.swift in Sources */, 1645ECC2243B643B007A82D6 /* ZMSearchUserTests+TeamUser.swift in Sources */, - F9331C521CB3BC6800139ECC /* CryptoBoxTests.swift in Sources */, 1693155325A30D4E00709F15 /* UserClientTests+ResetSession.swift in Sources */, F9A7083E1CAEEB7500C2F5FE /* NSFetchRequestTests+ZMRelationshipKeyPaths.m in Sources */, E6E504502BC571BF004948E7 /* AuthenticationContextTests.swift in Sources */, @@ -3993,7 +3983,6 @@ 017962982B83FC1400D6C7B6 /* DatabaseMigrationTests+UserUniqueness.swift in Sources */, 4058AAA82AAB65530013DE71 /* ReactionsSortingTests.swift in Sources */, F9A7083C1CAEEB7500C2F5FE /* MockModelObjectContextFactory.m in Sources */, - F9331C5C1CB3BF9F00139ECC /* UserClientKeyStoreTests.swift in Sources */, E6D9BAFD2BDA57BB00F59F81 /* SearchUserSnapshotTests.swift in Sources */, 01B7A5752B0FB6DA00FE5132 /* CoreDataMessagingMigrationVersionTests.swift in Sources */, 166D189E230E9E66001288CD /* ZMMessage+DataRetentionTests.swift in Sources */, @@ -4058,7 +4047,6 @@ 5476BA3E1DEDABCC00D047F8 /* AddressBookEntryTests.swift in Sources */, 068664A2256FB834001C8747 /* AppLockControllerTests.swift in Sources */, F93A302F1D6F2633005CCB1D /* ZMMessageTests+Confirmation.swift in Sources */, - F9331C561CB3BCF300139ECC /* OtrBaseTest.swift in Sources */, EE3EFEA1253090E0009499E5 /* PotentialChangeDetectorTests.swift in Sources */, 16DF3B5F2289510600D09365 /* ZMConversationTests+Legalhold.swift in Sources */, 169FF3AA27157F0100330C2E /* ZMSearchUserTests+Connections.swift in Sources */, From 74d2637760e89ddf672d941a19ad03151b3630e8 Mon Sep 17 00:00:00 2001 From: David-Henner Date: Fri, 28 Nov 2025 16:06:42 +0100 Subject: [PATCH 14/28] fix flaky test --- .../PushNotifications/ZMLocalNotificationTests_Event.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wire-ios-request-strategy/Tests/Sources/Notifications/PushNotifications/ZMLocalNotificationTests_Event.swift b/wire-ios-request-strategy/Tests/Sources/Notifications/PushNotifications/ZMLocalNotificationTests_Event.swift index 71169a46021..0378e37f771 100644 --- a/wire-ios-request-strategy/Tests/Sources/Notifications/PushNotifications/ZMLocalNotificationTests_Event.swift +++ b/wire-ios-request-strategy/Tests/Sources/Notifications/PushNotifications/ZMLocalNotificationTests_Event.swift @@ -458,7 +458,7 @@ final class ZMLocalNotificationTests_Event: ZMLocalNotificationTests { conversationID: self.groupConversationWithoutName.remoteIdentifier!, senderID: self.otherUser1.remoteIdentifier!, timer: 0, - timestamp: Date() + timestamp: Date().addingTimeInterval(.oneSecond) ) // when From f2c2c2c937cac1160ad86795c88abffdf0b9d920 Mon Sep 17 00:00:00 2001 From: David-Henner Date: Fri, 28 Nov 2025 16:07:21 +0100 Subject: [PATCH 15/28] update tests --- wire-ios-sync-engine/Tests/Source/MessagingTest.m | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/wire-ios-sync-engine/Tests/Source/MessagingTest.m b/wire-ios-sync-engine/Tests/Source/MessagingTest.m index 37c165cb2fe..3823432a8e1 100644 --- a/wire-ios-sync-engine/Tests/Source/MessagingTest.m +++ b/wire-ios-sync-engine/Tests/Source/MessagingTest.m @@ -133,9 +133,6 @@ - (void)setUp; self.sharedContainerURL = [fm containerURLForSecurityApplicationGroupIdentifier:self.groupIdentifier]; self.mockCallNotificationStyle = CallNotificationStylePushNotifications; - NSURL *otrFolder = [NSFileManager keyStoreURLForAccountInDirectory:self.accountDirectory createParentIfNeeded:NO]; - [fm removeItemAtURL:otrFolder error: nil]; - _application = [[ApplicationMock alloc] init]; self.originalConversationLastReadTimestampTimerValue = ZMConversationDefaultLastReadTimestampSaveDelay; @@ -156,10 +153,8 @@ - (void)setUp; }]; Require([self waitForAllGroupsToBeEmptyWithTimeout:5]); - [self setupKeyStore]; [self setupCaches]; - if (self.shouldUseRealKeychain) { [ZMPersistentCookieStorage setDoNotPersistToKeychain:NO]; @@ -184,16 +179,6 @@ - (void)setUp; sharedUserDefaults:self.sharedUserDefaults]; } -- (void)setupKeyStore -{ - [self performPretendingUiMocIsSyncMoc:^{ - NSURL *url = [CoreDataStack accountDataFolderWithAccountIdentifier:self.userIdentifier - applicationContainer:self.sharedContainerURL]; - [self.uiMOC setupUserKeyStoreInAccountDirectory:url - applicationContainer:self.sharedContainerURL]; - }]; -} - - (void)setupCaches { NSURL *cacheLocation = [[[NSFileManager defaultManager] URLsForDirectory:NSCachesDirectory inDomains:NSUserDomainMask] firstObject]; From 2194aaff9e95ccd3e044b82cda58d4dc2cc911cf Mon Sep 17 00:00:00 2001 From: David-Henner Date: Fri, 28 Nov 2025 16:11:35 +0100 Subject: [PATCH 16/28] update tests --- .../FetchingClientRequestStrategyTests.swift | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/wire-ios-request-strategy/Sources/Request Strategies/User Clients/FetchingClientRequestStrategyTests.swift b/wire-ios-request-strategy/Sources/Request Strategies/User Clients/FetchingClientRequestStrategyTests.swift index 11de77eb968..7a2e0623763 100644 --- a/wire-ios-request-strategy/Sources/Request Strategies/User Clients/FetchingClientRequestStrategyTests.swift +++ b/wire-ios-request-strategy/Sources/Request Strategies/User Clients/FetchingClientRequestStrategyTests.swift @@ -684,22 +684,9 @@ final class FetchClientRequestStrategyTests: MessagingTestBase { sut = createSUT(apiVersion: apiVersion) var payload: ZMTransportData! let remoteIdentifier = "aabbccdd0011" - var sessionIdentifier: ProteusSessionID! syncMOC.performGroupedAndWait { - sessionIdentifier = ProteusSessionID( - userID: self.otherUser!.remoteIdentifier.uuidString, - clientID: remoteIdentifier - ) self.otherUser.fetchUserClients() payload = [["id": remoteIdentifier, "class": "phone"]] as NSArray - // swiftlint:disable:next todo_requires_jira_link -// // TODO: [John] use flag here -// self.syncMOC.zm_cryptKeyStore.encryptionContext.perform { -// try! $0.createClientSession( -// sessionIdentifier, -// base64PreKeyString: self.syncMOC.zm_cryptKeyStore.lastPreKey() -// ) // just a bogus key is OK -// } } XCTAssertTrue(waitForAllGroupsToBeEmpty(withTimeout: 0.2)) let response = ZMTransportResponse( From d9cb72db49449916bcddf4949ff56885bab2e19d Mon Sep 17 00:00:00 2001 From: David-Henner Date: Fri, 28 Nov 2025 16:14:25 +0100 Subject: [PATCH 17/28] remove tests --- .../Model/Messages/CryptoBoxTests.swift | 60 ------------------- .../Source/Model/Messages/OtrBaseTest.swift | 45 -------------- 2 files changed, 105 deletions(-) delete mode 100644 wire-ios-data-model/Tests/Source/Model/Messages/CryptoBoxTests.swift delete mode 100644 wire-ios-data-model/Tests/Source/Model/Messages/OtrBaseTest.swift diff --git a/wire-ios-data-model/Tests/Source/Model/Messages/CryptoBoxTests.swift b/wire-ios-data-model/Tests/Source/Model/Messages/CryptoBoxTests.swift deleted file mode 100644 index bc9d3c469b9..00000000000 --- a/wire-ios-data-model/Tests/Source/Model/Messages/CryptoBoxTests.swift +++ /dev/null @@ -1,60 +0,0 @@ -// -// Wire -// Copyright (C) 2025 Wire Swiss GmbH -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see http://www.gnu.org/licenses/. -// - -import WireCryptobox -import XCTest -@testable import WireDataModel - -class CryptoBoxTest: OtrBaseTest { - - func testThatCryptoBoxFolderIsForbiddenFromBackup() { - // when - let accountId = UUID() - let accountFolder = CoreDataStack.accountDataFolder( - accountIdentifier: accountId, - applicationContainer: OtrBaseTest.sharedContainerURL - ) - let keyStore = UserClientKeysStore( - accountDirectory: accountFolder, - applicationContainer: OtrBaseTest.sharedContainerURL - ) - - // then - guard let values = try? keyStore.cryptoboxDirectory.resourceValues(forKeys: [.isExcludedFromBackupKey]) - else { return XCTFail() } - - XCTAssertTrue(values.isExcludedFromBackup!) - } - - func testThatCryptoBoxFolderIsMarkedForEncryption() { - #if targetEnvironment(simulator) - // File protection API is not available on simulator - XCTAssertTrue(true) - return - #else - // when - UserClientKeysStore.setupBox() - - // then - let attrs = try! NSFileManager.default.attributesOfItemAtPath(UserClientKeysStore.otrDirectoryURL.path) - let fileProtectionAttr = (attrs[NSFileProtectionKey]! as! String) - XCTAssertEqual(fileProtectionAttr, NSFileProtectionCompleteUntilFirstUserAuthentication) - #endif - } - -} diff --git a/wire-ios-data-model/Tests/Source/Model/Messages/OtrBaseTest.swift b/wire-ios-data-model/Tests/Source/Model/Messages/OtrBaseTest.swift deleted file mode 100644 index 497e8f1019d..00000000000 --- a/wire-ios-data-model/Tests/Source/Model/Messages/OtrBaseTest.swift +++ /dev/null @@ -1,45 +0,0 @@ -// -// Wire -// Copyright (C) 2025 Wire Swiss GmbH -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see http://www.gnu.org/licenses/. -// - -import Foundation -import XCTest - -class OtrBaseTest: XCTestCase { - override func setUp() { - super.setUp() - - // clean stored cryptobox files - if let items = (try? FileManager.default.contentsOfDirectory( - at: OtrBaseTest.sharedContainerURL, - includingPropertiesForKeys: nil, - options: [] - )) { - items.forEach { try? FileManager.default.removeItem(at: $0) } - } - } - - static var sharedContainerURL: URL { - try! FileManager.default.url( - for: .applicationSupportDirectory, - in: .userDomainMask, - appropriateFor: nil, - create: true - ) - } - -} From 31a8972cec4a118db4a128af4acea2fc2b9bf73d Mon Sep 17 00:00:00 2001 From: David-Henner Date: Tue, 2 Dec 2025 17:26:24 +0100 Subject: [PATCH 18/28] remove UserClientKeyStoreTests.swift --- .../UserClient/UserClientKeyStoreTests.swift | 144 ------------------ 1 file changed, 144 deletions(-) delete mode 100644 wire-ios-data-model/Tests/Source/Model/UserClient/UserClientKeyStoreTests.swift diff --git a/wire-ios-data-model/Tests/Source/Model/UserClient/UserClientKeyStoreTests.swift b/wire-ios-data-model/Tests/Source/Model/UserClient/UserClientKeyStoreTests.swift deleted file mode 100644 index a95720a3682..00000000000 --- a/wire-ios-data-model/Tests/Source/Model/UserClient/UserClientKeyStoreTests.swift +++ /dev/null @@ -1,144 +0,0 @@ -// -// Wire -// Copyright (C) 2025 Wire Swiss GmbH -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see http://www.gnu.org/licenses/. -// - -import WireCryptobox -import XCTest -@testable import WireDataModel - -class UserClientKeysStoreTests: OtrBaseTest { - - var sut: UserClientKeysStore! - var accountID: UUID! - var accountFolder: URL! - - override func setUp() { - super.setUp() - accountID = UUID() - accountFolder = CoreDataStack.accountDataFolder( - accountIdentifier: accountID, - applicationContainer: OtrBaseTest.sharedContainerURL - ) - cleanOTRFolder() - sut = UserClientKeysStore(accountDirectory: accountFolder, applicationContainer: OtrBaseTest.sharedContainerURL) - } - - override func tearDown() { - sut = nil - cleanOTRFolder() - accountID = nil - accountFolder = nil - super.tearDown() - } - - func cleanOTRFolder() { - let fm = FileManager.default - var paths: [String] = [] - if let accountID { - paths.append(OtrBaseTest.otrDirectoryURL(accountIdentifier: accountID).path) - } - paths.forEach { try? fm.removeItem(atPath: $0) } - } - - func testThatTheOTRFolderHasBackupDisabled() { - // when - guard let values = try? sut.cryptoboxDirectory - .resourceValues(forKeys: [URLResourceKey.isExcludedFromBackupKey]) else { return XCTFail() } - - // then - XCTAssertTrue(values.isExcludedFromBackup!) - } - - func testThatItCanGenerateMoreKeys() { - // when - do { - let newKeys = try sut.generateMoreKeys(1, start: 0) - XCTAssertNotEqual(newKeys.count, 0, "Should generate more keys") - - } catch let error as NSError { - XCTAssertNil(error, "Should not return error while generating key") - - } - } - - func testThatItWrapsKeysTo0WhenReachingTheMaximum() { - // given - let maxPreKey: UInt16 = UserClientKeysStore.MaxPreKeyID - let prekeyBatchSize: UInt16 = 50 - let startingPrekey = maxPreKey - prekeyBatchSize - 1 // -1 is to generate at least 2 batches - let maxIterations = 2 - - var previousMaxKeyId: UInt16 = startingPrekey - var iterations = 0 - - // when - while true { - var newKeys: [(id: UInt16, prekey: String)]! - var maxKey: UInt16! - var minKey: UInt16! - do { - newKeys = try sut.generateMoreKeys(50, start: previousMaxKeyId) - maxKey = newKeys.last?.id ?? 0 - minKey = newKeys.first?.id ?? 0 - } catch let error as NSError { - XCTAssertNil(error, "Should not return error while generating key: \(error)") - return - } - - // then - iterations += 1 - if iterations > maxIterations { - XCTFail( - "Too many keys are generated without wrapping: \(iterations) iterations, max key is \(String(describing: maxKey))" - ) - return - } - - XCTAssertGreaterThan(newKeys.count, 0, "Should generate more keys") - if minKey == 0 { // it wrapped!! - XCTAssertGreaterThan(iterations, 1) - // success! - return - } - - XCTAssertEqual(minKey, previousMaxKeyId) // is it the right starting point? - - previousMaxKeyId = maxKey - if maxKey > UserClientKeysStore.MaxPreKeyID { - XCTFail("Prekey \(String(describing: maxKey)) is too big") - return - } - - } - - } - - fileprivate func createLegacyOTRFolderWithDummyFile( - fileName: String, - data: Data, - folder: URL = OtrBaseTest.legacyOtrDirectory - ) -> URL { - try! FileManager.default.createDirectory( - atPath: folder.path, - withIntermediateDirectories: true, - attributes: [:] - ) - try! data.write(to: folder.appendingPathComponent(fileName)) - return folder - } - -} From 6dc516938f8acc8d3cde4f945da50b5c35615669 Mon Sep 17 00:00:00 2001 From: David-Henner Date: Fri, 28 Nov 2025 16:20:17 +0100 Subject: [PATCH 19/28] chore(cryptobox): remove cryptobox framework - WPB-7394 # Conflicts: # wire-ios-data-model/Tests/Source/Model/UserClient/UserClientKeyStoreTests.swift --- .github/workflows/test_pr_changes.yml | 2 - Cartfile | 1 - README.md | 2 +- .../Message/MessageLocalStore.swift | 1 - .../Sources/WireCrypto}/AEADEncryption.swift | 14 +- fastlane/framework.rb | 5 - .../Configurations/WireCryptobox.xcconfig | 22 - .../Resources/Configurations/version.xcconfig | 20 - .../zmc-config/ios-test-host.xcconfig | 17 - .../zmc-config/ios-test-target.xcconfig | 21 - .../zmc-config/project-common.xcconfig | 25 - .../zmc-config/project-debug.xcconfig | 42 - .../zmc-config/project.xcconfig | 35 - .../Configurations/zmc-config/tests.xcconfig | 21 - .../zmc-config/warnings.xcconfig | 71 -- wire-ios-cryptobox/Scripts/config.txt | 6 - .../WireCryptobox.xcodeproj/project.pbxproj | 520 ----------- .../contents.xcworkspacedata | 7 - .../xcshareddata/IDETemplateMacros.plist | 1 - .../xcshareddata/IDEWorkspaceChecks.plist | 8 - .../xcshareddata/WorkspaceSettings.xcsettings | 10 - .../xcschemes/WireCryptobox.xcscheme | 72 -- .../WireCryptobox/CryptoBoxError.swift | 44 - .../WireCryptobox/Data+SafeForLogging.swift | 36 - .../WireCryptobox/EncryptionContext.swift | 200 ----- .../EncryptionSessionsDirectory.swift | 788 ---------------- .../WireCryptobox/GenericHash.swift | 92 -- wire-ios-cryptobox/WireCryptobox/Info.plist | 26 - wire-ios-cryptobox/WireCryptobox/Logs.swift | 22 - .../WireCryptobox/NSData+CBox.swift | 35 - .../WireCryptobox/PointerWrapper.swift | 35 - .../WireCryptobox/WireCryptobox.h | 27 - wire-ios-cryptobox/WireCryptobox/cbox.h | 1 - .../ChaCha20AEADEncryptionTests.swift | 167 ---- .../CryptoboxTests-Bridging-Header.h | 17 - .../EncryptionContextCachingTests.swift | 152 ---- .../EncryptionContextTests.swift | 272 ------ .../EncryptionSessionsDirectoryTests.swift | 850 ------------------ .../GenericHashBuilderTests.swift | 85 -- .../WireCryptoboxTests/Info.plist | 24 - .../WireCryptoboxTests/TestHelper.swift | 39 - .../TestPlans/AllTests.xctestplan | 37 - wire-ios-cryptobox/test-host/AppDelegate.h | 27 - wire-ios-cryptobox/test-host/AppDelegate.m | 55 -- .../AppIcon.appiconset/Contents.json | 93 -- .../test-host/Base.lproj/Main.storyboard | 25 - wire-ios-cryptobox/test-host/Info.plist | 45 - wire-ios-cryptobox/test-host/ViewController.h | 25 - wire-ios-cryptobox/test-host/ViewController.m | 37 - wire-ios-cryptobox/test-host/main.m | 26 - ...anagedObjectContext+EncryptionAtRest.swift | 9 +- .../NSManagedObjectContext+zmessaging.m | 1 - .../ZMConversation+SecurityLevel.swift | 1 - .../Model/Conversation/ZMConversation.m | 1 - .../ConversationMessage+Deletion.swift | 1 - .../Message/ZMClientMessage+Encryption.swift | 1 - .../Model/Message/ZMGenericMessageData.swift | 1 - .../Source/Model/Message/ZMMessage.m | 18 - .../Source/Model/User/ZMUser.m | 1 - .../Source/Model/UserClient/UserClient.swift | 1 - .../Source/Public/DraftMessage.swift | 1 - .../ZMConversationTests+SecurityLevel.swift | 24 - .../ZMClientMessageTests+Ephemeral.swift | 1 - .../ZMMessageTests+SystemMessages.swift | 54 -- .../Source/Model/ZMBaseManagedObjectTest.h | 1 - .../WireDataModel.xcodeproj/project.pbxproj | 2 - .../MockPendingLegalHoldClient.swift | 2 +- .../Source/Clients and OTR/MockPreKey.h | 1 - .../Clients and OTR/MockUserClient.swift | 2 +- .../Source/Conversations/MockConversation.m | 1 - .../contents.xcworkspacedata | 3 - .../Helpers/EventDecoderDecryptionTests.swift | 1 - .../FetchingClientRequestStrategy.swift | 1 - .../FetchingClientRequestStrategyTests.swift | 1 - .../Decoding/EventDecoder+Proteus.swift | 1 - .../Decoding/EventDecoder.swift | 2 +- .../Source/Synchronization/ZMOperationLoop.m | 1 - .../E2EE/UserClientRequestFactoryTests.swift | 1 - .../Tests/Source/MessagingTest.h | 1 - .../SynchronizationMocks.swift | 1 - .../Synchronization/ZMOperationLoopTests.h | 1 - .../MockUserType+SelfLegalHoldSubject.swift | 1 - ...tDecryptSystemMessageCellDescription.swift | 19 - 83 files changed, 14 insertions(+), 4349 deletions(-) rename {wire-ios-cryptobox/WireCryptobox/ChaCha20Poly1305 => WireFoundation/Sources/WireCrypto}/AEADEncryption.swift (95%) delete mode 100644 wire-ios-cryptobox/Resources/Configurations/WireCryptobox.xcconfig delete mode 100644 wire-ios-cryptobox/Resources/Configurations/version.xcconfig delete mode 100644 wire-ios-cryptobox/Resources/Configurations/zmc-config/ios-test-host.xcconfig delete mode 100644 wire-ios-cryptobox/Resources/Configurations/zmc-config/ios-test-target.xcconfig delete mode 100644 wire-ios-cryptobox/Resources/Configurations/zmc-config/project-common.xcconfig delete mode 100644 wire-ios-cryptobox/Resources/Configurations/zmc-config/project-debug.xcconfig delete mode 100644 wire-ios-cryptobox/Resources/Configurations/zmc-config/project.xcconfig delete mode 100644 wire-ios-cryptobox/Resources/Configurations/zmc-config/tests.xcconfig delete mode 100644 wire-ios-cryptobox/Resources/Configurations/zmc-config/warnings.xcconfig delete mode 100644 wire-ios-cryptobox/Scripts/config.txt delete mode 100644 wire-ios-cryptobox/WireCryptobox.xcodeproj/project.pbxproj delete mode 100644 wire-ios-cryptobox/WireCryptobox.xcodeproj/project.xcworkspace/contents.xcworkspacedata delete mode 120000 wire-ios-cryptobox/WireCryptobox.xcodeproj/project.xcworkspace/xcshareddata/IDETemplateMacros.plist delete mode 100644 wire-ios-cryptobox/WireCryptobox.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist delete mode 100644 wire-ios-cryptobox/WireCryptobox.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings delete mode 100644 wire-ios-cryptobox/WireCryptobox.xcodeproj/xcshareddata/xcschemes/WireCryptobox.xcscheme delete mode 100644 wire-ios-cryptobox/WireCryptobox/CryptoBoxError.swift delete mode 100644 wire-ios-cryptobox/WireCryptobox/Data+SafeForLogging.swift delete mode 100644 wire-ios-cryptobox/WireCryptobox/EncryptionContext.swift delete mode 100644 wire-ios-cryptobox/WireCryptobox/EncryptionSessionsDirectory.swift delete mode 100644 wire-ios-cryptobox/WireCryptobox/GenericHash.swift delete mode 100644 wire-ios-cryptobox/WireCryptobox/Info.plist delete mode 100644 wire-ios-cryptobox/WireCryptobox/Logs.swift delete mode 100644 wire-ios-cryptobox/WireCryptobox/NSData+CBox.swift delete mode 100644 wire-ios-cryptobox/WireCryptobox/PointerWrapper.swift delete mode 100644 wire-ios-cryptobox/WireCryptobox/WireCryptobox.h delete mode 120000 wire-ios-cryptobox/WireCryptobox/cbox.h delete mode 100644 wire-ios-cryptobox/WireCryptoboxTests/ChaCha20AEADEncryptionTests.swift delete mode 100644 wire-ios-cryptobox/WireCryptoboxTests/CryptoboxTests-Bridging-Header.h delete mode 100644 wire-ios-cryptobox/WireCryptoboxTests/EncryptionContextCachingTests.swift delete mode 100644 wire-ios-cryptobox/WireCryptoboxTests/EncryptionContextTests.swift delete mode 100644 wire-ios-cryptobox/WireCryptoboxTests/EncryptionSessionsDirectoryTests.swift delete mode 100644 wire-ios-cryptobox/WireCryptoboxTests/GenericHashBuilderTests.swift delete mode 100644 wire-ios-cryptobox/WireCryptoboxTests/Info.plist delete mode 100644 wire-ios-cryptobox/WireCryptoboxTests/TestHelper.swift delete mode 100644 wire-ios-cryptobox/WireCryptoboxTests/TestPlans/AllTests.xctestplan delete mode 100644 wire-ios-cryptobox/test-host/AppDelegate.h delete mode 100644 wire-ios-cryptobox/test-host/AppDelegate.m delete mode 100644 wire-ios-cryptobox/test-host/Assets.xcassets/AppIcon.appiconset/Contents.json delete mode 100644 wire-ios-cryptobox/test-host/Base.lproj/Main.storyboard delete mode 100644 wire-ios-cryptobox/test-host/Info.plist delete mode 100644 wire-ios-cryptobox/test-host/ViewController.h delete mode 100644 wire-ios-cryptobox/test-host/ViewController.m delete mode 100644 wire-ios-cryptobox/test-host/main.m diff --git a/.github/workflows/test_pr_changes.yml b/.github/workflows/test_pr_changes.yml index a7fb957bcdd..4f8248a6cf2 100644 --- a/.github/workflows/test_pr_changes.yml +++ b/.github/workflows/test_pr_changes.yml @@ -71,8 +71,6 @@ jobs: - 'scripts/**' wire-ios: - 'wire-ios/**' - wire-ios-cryptobox: - - 'wire-ios-cryptobox/**' wire-ios-data-model: - 'wire-ios-data-model/**' wire-ios-images: diff --git a/Cartfile b/Cartfile index 886a8fe4990..22d9f9c9b9f 100644 --- a/Cartfile +++ b/Cartfile @@ -1,3 +1,2 @@ -github "wireapp/cryptobox-ios" "v1.1.0_xcframework_arm64simulator" github "wireapp/libPhoneNumber-iOS" "1.1" github "wireapp/ocmock" "v3.4.3_Xcode14.3.1" diff --git a/README.md b/README.md index 77984a3fbf1..cb3c24648bf 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ No license is granted to the Wire trademark and its associated logos, all of whi The Wire mobile app has an architectural layer that we call *sync engine*. It is the client-side layer that processes all the data that is displayed in the mobile app. It handles network communication and authentication with the backend, push notifications, local caching of data, client-side business logic, signaling with the audio-video libraries, encryption and decryption (using encryption libraries from a lower level) and other bits and pieces. The user interface layer of the mobile app is built on top of the *sync engine*, which provides the data to display to the UI. -The sync engine itself is built on top of a few third-party frameworks, and uses Wire components that are shared between platforms for cryptography (Proteus/Cryptobox) and audio-video signaling (AVS). +The sync engine itself is built on top of a few third-party frameworks, and uses Wire components that are shared between platforms for cryptography (Proteus/MLS) and audio-video signaling (AVS). ![Mobile app architecture](https://github.com/wireapp/wire/blob/master/assets/mobile-architecture.png?raw=true) diff --git a/WireDomain/Sources/WireDomain/Repositories/Message/MessageLocalStore.swift b/WireDomain/Sources/WireDomain/Repositories/Message/MessageLocalStore.swift index 37e344d76ea..f239208a6f7 100644 --- a/WireDomain/Sources/WireDomain/Repositories/Message/MessageLocalStore.swift +++ b/WireDomain/Sources/WireDomain/Repositories/Message/MessageLocalStore.swift @@ -18,7 +18,6 @@ import CoreData import GenericMessageProtocol -import WireCryptobox import WireDataModel import WireLogging diff --git a/wire-ios-cryptobox/WireCryptobox/ChaCha20Poly1305/AEADEncryption.swift b/WireFoundation/Sources/WireCrypto/AEADEncryption.swift similarity index 95% rename from wire-ios-cryptobox/WireCryptobox/ChaCha20Poly1305/AEADEncryption.swift rename to WireFoundation/Sources/WireCrypto/AEADEncryption.swift index a6967a34100..abaf3918cf9 100644 --- a/wire-ios-cryptobox/WireCryptobox/ChaCha20Poly1305/AEADEncryption.swift +++ b/WireFoundation/Sources/WireCrypto/AEADEncryption.swift @@ -17,9 +17,7 @@ // import Clibsodium -import Foundation -import WireCrypto -import WireUtilities +public import Foundation public extension ChaCha20Poly1305 { @@ -152,11 +150,11 @@ public extension ChaCha20Poly1305 { // MARK: - Verification - private static func verifyKey(bytes: [Byte]) throws { + private static func verifyKey(bytes: [UInt8]) throws { guard bytes.count == keyLength else { throw EncryptionError.malformedKey } } - private static func verifyNonce(bytes: [Byte]) throws { + private static func verifyNonce(bytes: [UInt8]) throws { guard bytes.count == nonceLength else { throw EncryptionError.malformedNonce } } @@ -182,14 +180,14 @@ public extension ChaCha20Poly1305 { // MARK: - Buffer creation - static func generateRandomNonceBytes() -> [Byte] { + static func generateRandomNonceBytes() -> [UInt8] { var nonce = createByteArray(length: nonceLength) randombytes_buf(&nonce, nonce.count) return nonce } - private static func createByteArray(length: Int) -> [Byte] { - [Byte](repeating: 0, count: length) + private static func createByteArray(length: Int) -> [UInt8] { + [UInt8](repeating: 0, count: length) } } diff --git a/fastlane/framework.rb b/fastlane/framework.rb index 0890b01ccdb..fadea14f0c8 100644 --- a/fastlane/framework.rb +++ b/fastlane/framework.rb @@ -16,7 +16,6 @@ def self.all "WireUI", "wire-ios", "wire-ios-canvas", - "wire-ios-cryptobox", "wire-ios-data-model", "wire-ios-images", "wire-ios-link-preview", @@ -65,7 +64,6 @@ def self.all frameworks["wire-ios-request-strategy"].add_dependency(frameworks["WireNetwork"]) frameworks["wire-ios-request-strategy"].add_dependency(frameworks["WireLogging"]) - frameworks["wire-ios-data-model"].add_dependency(frameworks["wire-ios-cryptobox"]) frameworks["wire-ios-data-model"].add_dependency(frameworks["wire-ios-images"]) frameworks["wire-ios-data-model"].add_dependency(frameworks["wire-ios-link-preview"]) frameworks["wire-ios-data-model"].add_dependency(frameworks["wire-ios-transport"]) @@ -75,9 +73,6 @@ def self.all frameworks["wire-ios-data-model"].add_dependency(frameworks["WireLogging"]) frameworks["wire-ios-mocktransport"].add_dependency(frameworks["wire-ios-testing"]) - frameworks["wire-ios-mocktransport"].add_dependency(frameworks["wire-ios-cryptobox"]) - - frameworks["wire-ios-cryptobox"].add_dependency(frameworks["wire-ios-utilities"]) frameworks["wire-ios-transport"].add_dependency(frameworks["wire-ios-utilities"]) frameworks["wire-ios-transport"].add_dependency(frameworks["wire-ios-testing"]) # included in WireTransportTests diff --git a/wire-ios-cryptobox/Resources/Configurations/WireCryptobox.xcconfig b/wire-ios-cryptobox/Resources/Configurations/WireCryptobox.xcconfig deleted file mode 100644 index f7770bbfec3..00000000000 --- a/wire-ios-cryptobox/Resources/Configurations/WireCryptobox.xcconfig +++ /dev/null @@ -1,22 +0,0 @@ -// -// Wire -// Copyright (C) 2025 Wire Swiss GmbH -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see http://www.gnu.org/licenses/. -// - -APPLICATION_EXTENSION_API_ONLY = YES -PRODUCT_NAME = WireCryptobox -SWIFT_INCLUDE_PATHS = $(inherited) $(SRCROOT)/WireCryptobox -CRYPTOBOX_VERSION = 1.1.0 diff --git a/wire-ios-cryptobox/Resources/Configurations/version.xcconfig b/wire-ios-cryptobox/Resources/Configurations/version.xcconfig deleted file mode 100644 index 9d103b53b56..00000000000 --- a/wire-ios-cryptobox/Resources/Configurations/version.xcconfig +++ /dev/null @@ -1,20 +0,0 @@ -// -// Wire -// Copyright (C) 2025 Wire Swiss GmbH -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see http://www.gnu.org/licenses/. -// - -MAJOR_VERSION = 34 -CURRENT_PROJECT_VERSION = 34.0.1 diff --git a/wire-ios-cryptobox/Resources/Configurations/zmc-config/ios-test-host.xcconfig b/wire-ios-cryptobox/Resources/Configurations/zmc-config/ios-test-host.xcconfig deleted file mode 100644 index 89c90917712..00000000000 --- a/wire-ios-cryptobox/Resources/Configurations/zmc-config/ios-test-host.xcconfig +++ /dev/null @@ -1,17 +0,0 @@ -// -// Wire -// Copyright (C) 2025 Wire Swiss GmbH -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see http://www.gnu.org/licenses/. -// diff --git a/wire-ios-cryptobox/Resources/Configurations/zmc-config/ios-test-target.xcconfig b/wire-ios-cryptobox/Resources/Configurations/zmc-config/ios-test-target.xcconfig deleted file mode 100644 index 7aa6aecfd77..00000000000 --- a/wire-ios-cryptobox/Resources/Configurations/zmc-config/ios-test-target.xcconfig +++ /dev/null @@ -1,21 +0,0 @@ -// -// Wire -// Copyright (C) 2025 Wire Swiss GmbH -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see http://www.gnu.org/licenses/. -// - -#include "tests.xcconfig" - -SWIFT_INCLUDE_PATHS = $(inherited) $(SRCROOT)/WireCryptobox diff --git a/wire-ios-cryptobox/Resources/Configurations/zmc-config/project-common.xcconfig b/wire-ios-cryptobox/Resources/Configurations/zmc-config/project-common.xcconfig deleted file mode 100644 index 928434aea8c..00000000000 --- a/wire-ios-cryptobox/Resources/Configurations/zmc-config/project-common.xcconfig +++ /dev/null @@ -1,25 +0,0 @@ -// -// Wire -// Copyright (C) 2025 Wire Swiss GmbH -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see http://www.gnu.org/licenses/. -// - -#include "warnings.xcconfig" -#include "../version.xcconfig" -#include "../../../../xcconfigs/ios-arm64Simulator.xcconfig" - - -// info.plist -INFOPLIST_PREPROCESS = YES diff --git a/wire-ios-cryptobox/Resources/Configurations/zmc-config/project-debug.xcconfig b/wire-ios-cryptobox/Resources/Configurations/zmc-config/project-debug.xcconfig deleted file mode 100644 index 152c0fc9c70..00000000000 --- a/wire-ios-cryptobox/Resources/Configurations/zmc-config/project-debug.xcconfig +++ /dev/null @@ -1,42 +0,0 @@ -// -// Wire -// Copyright (C) 2025 Wire Swiss GmbH -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see http://www.gnu.org/licenses/. -// - -#include "warnings.xcconfig" -#include "project-common.xcconfig" - -// Deployment -// -COPY_PHASE_STRIP = NO - - -// LLVM - Code Generation -// -GCC_OPTIMIZATION_LEVEL = 0 - - -// Swift Compiler - Code Generation -// -SWIFT_OPTIMIZATION_LEVEL = -Onone -OTHER_SWIFT_FLAGS = -D DEBUG -ENABLE_TESTABILITY = YES - - - -// LLVM - Preprocessing -// -GCC_PREPROCESSOR_DEFINITIONS = DEBUG=1 ZM_MAJOR_VERSION=$(MAJOR_VERSION) $(inherited) $(GCC_PREPROCESSOR_DEFINITIONS_shared) diff --git a/wire-ios-cryptobox/Resources/Configurations/zmc-config/project.xcconfig b/wire-ios-cryptobox/Resources/Configurations/zmc-config/project.xcconfig deleted file mode 100644 index adced5a0e1f..00000000000 --- a/wire-ios-cryptobox/Resources/Configurations/zmc-config/project.xcconfig +++ /dev/null @@ -1,35 +0,0 @@ -// -// Wire -// Copyright (C) 2025 Wire Swiss GmbH -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see http://www.gnu.org/licenses/. -// - -#include "warnings.xcconfig" -#include "project-common.xcconfig" - -// Build Options -// -DEBUG_INFORMATION_FORMAT = dwarf-with-dsym -VALIDATE_PRODUCT = YES - -// Deployment -// -COPY_PHASE_STRIP = YES - - -// LLVM - Preprocessing -// -GCC_PREPROCESSOR_DEFINITIONS = DEBUG=0 ZM_MAJOR_VERSION=$(MAJOR_VERSION) $(inherited) $(GCC_PREPROCESSOR_DEFINITIONS_shared) - diff --git a/wire-ios-cryptobox/Resources/Configurations/zmc-config/tests.xcconfig b/wire-ios-cryptobox/Resources/Configurations/zmc-config/tests.xcconfig deleted file mode 100644 index 4b94eb69c14..00000000000 --- a/wire-ios-cryptobox/Resources/Configurations/zmc-config/tests.xcconfig +++ /dev/null @@ -1,21 +0,0 @@ -// -// Wire -// Copyright (C) 2025 Wire Swiss GmbH -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see http://www.gnu.org/licenses/. -// - -// Preprocessing -// -GCC_PREPROCESSOR_DEFINITIONS_shared = TEST_TARGET=1 diff --git a/wire-ios-cryptobox/Resources/Configurations/zmc-config/warnings.xcconfig b/wire-ios-cryptobox/Resources/Configurations/zmc-config/warnings.xcconfig deleted file mode 100644 index b899f81ef4e..00000000000 --- a/wire-ios-cryptobox/Resources/Configurations/zmc-config/warnings.xcconfig +++ /dev/null @@ -1,71 +0,0 @@ -// -// Wire -// Copyright (C) 2025 Wire Swiss GmbH -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see http://www.gnu.org/licenses/. -// - -// Warnings - All languages -// -CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR -CLANG_WARN_EMPTY_BODY = YES -CLANG_WARN_BOOL_CONVERSION = YES -CLANG_WARN_CONSTANT_CONVERSION = YES -GCC_WARN_64_TO_32_BIT_CONVERSION = YES -CLANG_WARN_ENUM_CONVERSION = YES -CLANG_WARN_INT_CONVERSION = YES -GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR -GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE -GCC_WARN_UNUSED_FUNCTION = YES -GCC_WARN_UNUSED_VARIABLE = YES -GCC_WARN_FOUR_CHARACTER_CONSTANTS = YES -GCC_WARN_SHADOW = YES -GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES -GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES -GCC_WARN_ABOUT_MISSING_NEWLINE = NO -GCC_WARN_SIGN_COMPARE = YES -CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES -GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORS = YES -GCC_WARN_UNKNOWN_PRAGMAS = YES -GCC_WARN_UNUSED_LABEL = YES -GCC_WARN_UNUSED_PARAMETER = YES -CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES -GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES -CLANG_WARN_UNREACHABLE_CODE = YES - -// Warnings - Objective C -// -CLANG_WARN__DUPLICATE_METHOD_MATCH = YES -CLANG_WARN_OBJC_IMPLICIT_ATOMIC_PROPERTIES = YES -CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES -CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES -GCC_WARN_MULTIPLE_DEFINITION_TYPES_FOR_SELECTOR = YES -CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES -GCC_WARN_STRICT_SELECTOR_MATCH = YES -GCC_WARN_UNDECLARED_SELECTOR = YES -CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR - -// Static Analyzer - Issues - Security -// -CLANG_ANALYZER_SECURITY_FLOATLOOPCOUNTER = YES -CLANG_ANALYZER_SECURITY_INSECUREAPI_RAND = YES -CLANG_ANALYZER_SECURITY_INSECUREAPI_STRCPY = YES - -CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; -CLANG_WARN_COMMA = YES; -CLANG_WARN_INFINITE_RECURSION = YES; -CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; -CLANG_WARN_STRICT_PROTOTYPES = YES; -CLANG_WARN_SUSPICIOUS_MOVE = YES; -GCC_NO_COMMON_BLOCKS = YES; diff --git a/wire-ios-cryptobox/Scripts/config.txt b/wire-ios-cryptobox/Scripts/config.txt deleted file mode 100644 index 31dbf54b2ef..00000000000 --- a/wire-ios-cryptobox/Scripts/config.txt +++ /dev/null @@ -1,6 +0,0 @@ -framework=WireCryptobox -organization=wireapp -repository=wire-ios-cryptobox -release-name-format=You should never {VERB} in {CITY} -platforms=ios,osx -dont_test_platforms=osx \ No newline at end of file diff --git a/wire-ios-cryptobox/WireCryptobox.xcodeproj/project.pbxproj b/wire-ios-cryptobox/WireCryptobox.xcodeproj/project.pbxproj deleted file mode 100644 index d12b67d0307..00000000000 --- a/wire-ios-cryptobox/WireCryptobox.xcodeproj/project.pbxproj +++ /dev/null @@ -1,520 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 71; - objects = { - -/* Begin PBXBuildFile section */ - 591B6E5E2C8B09E8009F8A7B /* WireCryptobox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0928E2341BA0777A0057232E /* WireCryptobox.framework */; }; - 591B6E612C8B09ED009F8A7B /* WireCryptobox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0928E2341BA0777A0057232E /* WireCryptobox.framework */; }; - CBD35F2E2D09EBC10080DA37 /* Clibsodium in Frameworks */ = {isa = PBXBuildFile; productRef = CBD35F2D2D09EBC10080DA37 /* Clibsodium */; }; - CBD35F302D09EBD00080DA37 /* WireCrypto in Frameworks */ = {isa = PBXBuildFile; productRef = CBD35F2F2D09EBD00080DA37 /* WireCrypto */; }; - EE97411D295493B9000C9340 /* WireUtilities.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EE97411C295493B9000C9340 /* WireUtilities.framework */; }; - EEE25B2229719A240008B894 /* cryptobox.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = EE67F6B9296F05BF001D7C88 /* cryptobox.xcframework */; }; -/* End PBXBuildFile section */ - -/* Begin PBXContainerItemProxy section */ - 5471A66C1D24216D0092A9A9 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = BA7EF9561B7109B600204A8E /* Project object */; - proxyType = 1; - remoteGlobalIDString = 5471A6541D24215B0092A9A9; - remoteInfo = "test-host"; - }; - 5471A6701D24221A0092A9A9 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = BA7EF9561B7109B600204A8E /* Project object */; - proxyType = 1; - remoteGlobalIDString = 0928E2331BA0777A0057232E; - remoteInfo = "Cryptobox-ios"; - }; -/* End PBXContainerItemProxy section */ - -/* Begin PBXFileReference section */ - 0928E2341BA0777A0057232E /* WireCryptobox.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = WireCryptobox.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 5471A6551D24215B0092A9A9 /* WireCryptoboxTestHost.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = WireCryptoboxTestHost.app; sourceTree = BUILT_PRODUCTS_DIR; }; - BA7EF9691B7109B600204A8E /* WireCryptoboxTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = WireCryptoboxTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - EE67F6B8296F05BF001D7C88 /* Clibsodium.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = Clibsodium.xcframework; path = ../Carthage/Build/Clibsodium.xcframework; sourceTree = ""; }; - EE67F6B9296F05BF001D7C88 /* cryptobox.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = cryptobox.xcframework; path = ../Carthage/Build/cryptobox.xcframework; sourceTree = ""; }; - EE97411C295493B9000C9340 /* WireUtilities.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = WireUtilities.framework; sourceTree = BUILT_PRODUCTS_DIR; }; -/* End PBXFileReference section */ - -/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ - 594E14852D660CC200864BC1 /* PBXFileSystemSynchronizedBuildFileExceptionSet */ = { - isa = PBXFileSystemSynchronizedBuildFileExceptionSet; - membershipExceptions = ( - Info.plist, - ); - target = 5471A6541D24215B0092A9A9 /* WireCryptoboxTestHost */; - }; - 594E14992D660CCF00864BC1 /* PBXFileSystemSynchronizedBuildFileExceptionSet */ = { - isa = PBXFileSystemSynchronizedBuildFileExceptionSet; - membershipExceptions = ( - Info.plist, - TestPlans/AllTests.xctestplan, - ); - target = BA7EF9681B7109B600204A8E /* WireCryptoboxTests */; - }; - 594E14C42D660D1C00864BC1 /* PBXFileSystemSynchronizedBuildFileExceptionSet */ = { - isa = PBXFileSystemSynchronizedBuildFileExceptionSet; - membershipExceptions = ( - Info.plist, - ); - publicHeaders = ( - cbox.h, - WireCryptobox.h, - ); - target = 0928E2331BA0777A0057232E /* WireCryptobox */; - }; -/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */ - -/* Begin PBXFileSystemSynchronizedRootGroup section */ - 594E147E2D660CC200864BC1 /* test-host */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (594E14852D660CC200864BC1 /* PBXFileSystemSynchronizedBuildFileExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = "test-host"; sourceTree = ""; }; - 594E14902D660CCF00864BC1 /* WireCryptoboxTests */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (594E14992D660CCF00864BC1 /* PBXFileSystemSynchronizedBuildFileExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = WireCryptoboxTests; sourceTree = ""; }; - 594E14A52D660CD200864BC1 /* Resources */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = Resources; sourceTree = ""; }; - 594E14B72D660D1C00864BC1 /* WireCryptobox */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (594E14C42D660D1C00864BC1 /* PBXFileSystemSynchronizedBuildFileExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = WireCryptobox; sourceTree = ""; }; -/* End PBXFileSystemSynchronizedRootGroup section */ - -/* Begin PBXFrameworksBuildPhase section */ - 0928E2301BA0777A0057232E /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - EE97411D295493B9000C9340 /* WireUtilities.framework in Frameworks */, - CBD35F2E2D09EBC10080DA37 /* Clibsodium in Frameworks */, - CBD35F302D09EBD00080DA37 /* WireCrypto in Frameworks */, - EEE25B2229719A240008B894 /* cryptobox.xcframework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 5471A6521D24215B0092A9A9 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 591B6E612C8B09ED009F8A7B /* WireCryptobox.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - BA7EF9661B7109B600204A8E /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 591B6E5E2C8B09E8009F8A7B /* WireCryptobox.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - BA7EF9551B7109B600204A8E = { - isa = PBXGroup; - children = ( - 594E14B72D660D1C00864BC1 /* WireCryptobox */, - 594E14A52D660CD200864BC1 /* Resources */, - 594E14902D660CCF00864BC1 /* WireCryptoboxTests */, - 594E147E2D660CC200864BC1 /* test-host */, - F10C8E221E9296BA00020408 /* Frameworks */, - BA7EF95F1B7109B600204A8E /* Products */, - ); - indentWidth = 4; - sourceTree = ""; - tabWidth = 4; - usesTabs = 0; - wrapsLines = 1; - }; - BA7EF95F1B7109B600204A8E /* Products */ = { - isa = PBXGroup; - children = ( - BA7EF9691B7109B600204A8E /* WireCryptoboxTests.xctest */, - 0928E2341BA0777A0057232E /* WireCryptobox.framework */, - 5471A6551D24215B0092A9A9 /* WireCryptoboxTestHost.app */, - ); - name = Products; - sourceTree = ""; - }; - F10C8E221E9296BA00020408 /* Frameworks */ = { - isa = PBXGroup; - children = ( - EE97411C295493B9000C9340 /* WireUtilities.framework */, - EE67F6B8296F05BF001D7C88 /* Clibsodium.xcframework */, - EE67F6B9296F05BF001D7C88 /* cryptobox.xcframework */, - ); - name = Frameworks; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXHeadersBuildPhase section */ - 0928E2311BA0777A0057232E /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXHeadersBuildPhase section */ - -/* Begin PBXNativeTarget section */ - 0928E2331BA0777A0057232E /* WireCryptobox */ = { - isa = PBXNativeTarget; - buildConfigurationList = 0928E24B1BA0777A0057232E /* Build configuration list for PBXNativeTarget "WireCryptobox" */; - buildPhases = ( - 5459C4B81C0FB0F600F4963F /* Placeholder Carthage folder */, - 0928E22F1BA0777A0057232E /* Sources */, - 0928E2301BA0777A0057232E /* Frameworks */, - 0928E2311BA0777A0057232E /* Headers */, - 0928E2321BA0777A0057232E /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - fileSystemSynchronizedGroups = ( - 594E14B72D660D1C00864BC1 /* WireCryptobox */, - ); - name = WireCryptobox; - productName = "Cryptobox-ios"; - productReference = 0928E2341BA0777A0057232E /* WireCryptobox.framework */; - productType = "com.apple.product-type.framework"; - }; - 5471A6541D24215B0092A9A9 /* WireCryptoboxTestHost */ = { - isa = PBXNativeTarget; - buildConfigurationList = 5471A6691D24215B0092A9A9 /* Build configuration list for PBXNativeTarget "WireCryptoboxTestHost" */; - buildPhases = ( - 5471A6511D24215B0092A9A9 /* Sources */, - 5471A6521D24215B0092A9A9 /* Frameworks */, - 5471A6531D24215B0092A9A9 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - 5471A6711D24221A0092A9A9 /* PBXTargetDependency */, - ); - fileSystemSynchronizedGroups = ( - 594E147E2D660CC200864BC1 /* test-host */, - ); - name = WireCryptoboxTestHost; - productName = "test-host"; - productReference = 5471A6551D24215B0092A9A9 /* WireCryptoboxTestHost.app */; - productType = "com.apple.product-type.application"; - }; - BA7EF9681B7109B600204A8E /* WireCryptoboxTests */ = { - isa = PBXNativeTarget; - buildConfigurationList = BA7EF9751B7109B600204A8E /* Build configuration list for PBXNativeTarget "WireCryptoboxTests" */; - buildPhases = ( - BA7EF9651B7109B600204A8E /* Sources */, - BA7EF9661B7109B600204A8E /* Frameworks */, - BA7EF9671B7109B600204A8E /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - 5471A66D1D24216D0092A9A9 /* PBXTargetDependency */, - ); - fileSystemSynchronizedGroups = ( - 594E14902D660CCF00864BC1 /* WireCryptoboxTests */, - ); - name = WireCryptoboxTests; - productName = CryptoboxTests; - productReference = BA7EF9691B7109B600204A8E /* WireCryptoboxTests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - BA7EF9561B7109B600204A8E /* Project object */ = { - isa = PBXProject; - attributes = { - LastSwiftUpdateCheck = 0730; - LastUpgradeCheck = 1310; - ORGANIZATIONNAME = Cryptobox; - TargetAttributes = { - 0928E2331BA0777A0057232E = { - CreatedOnToolsVersion = 6.4; - LastSwiftMigration = 1000; - }; - 5471A6541D24215B0092A9A9 = { - CreatedOnToolsVersion = 7.3.1; - LastSwiftMigration = 0800; - ProvisioningStyle = Manual; - }; - BA7EF9681B7109B600204A8E = { - CreatedOnToolsVersion = 6.4; - LastSwiftMigration = 1000; - TestTargetID = 5471A6541D24215B0092A9A9; - }; - }; - }; - buildConfigurationList = BA7EF9591B7109B600204A8E /* Build configuration list for PBXProject "WireCryptobox" */; - compatibilityVersion = "Xcode 3.2"; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = BA7EF9551B7109B600204A8E; - productRefGroup = BA7EF95F1B7109B600204A8E /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 0928E2331BA0777A0057232E /* WireCryptobox */, - BA7EF9681B7109B600204A8E /* WireCryptoboxTests */, - 5471A6541D24215B0092A9A9 /* WireCryptoboxTestHost */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 0928E2321BA0777A0057232E /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 5471A6531D24215B0092A9A9 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - BA7EF9671B7109B600204A8E /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXShellScriptBuildPhase section */ - 5459C4B81C0FB0F600F4963F /* Placeholder Carthage folder */ = { - isa = PBXShellScriptBuildPhase; - alwaysOutOfDate = 1; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "Placeholder Carthage folder"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "mkdir -p Carthage/Build\n"; - }; -/* End PBXShellScriptBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 0928E22F1BA0777A0057232E /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 5471A6511D24215B0092A9A9 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - BA7EF9651B7109B600204A8E /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXTargetDependency section */ - 5471A66D1D24216D0092A9A9 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 5471A6541D24215B0092A9A9 /* WireCryptoboxTestHost */; - targetProxy = 5471A66C1D24216D0092A9A9 /* PBXContainerItemProxy */; - }; - 5471A6711D24221A0092A9A9 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 0928E2331BA0777A0057232E /* WireCryptobox */; - targetProxy = 5471A6701D24221A0092A9A9 /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - -/* Begin XCBuildConfiguration section */ - 0928E2471BA0777A0057232E /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReferenceAnchor = 594E14A52D660CD200864BC1 /* Resources */; - baseConfigurationReferenceRelativePath = Configurations/WireCryptobox.xcconfig; - buildSettings = { - CODE_SIGN_STYLE = Manual; - DEVELOPMENT_TEAM = ""; - INFOPLIST_FILE = WireCryptobox/Info.plist; - PRODUCT_BUNDLE_IDENTIFIER = com.wire.cryptobox; - PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = ""; - SKIP_INSTALL = YES; - }; - name = Debug; - }; - 0928E2481BA0777A0057232E /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReferenceAnchor = 594E14A52D660CD200864BC1 /* Resources */; - baseConfigurationReferenceRelativePath = Configurations/WireCryptobox.xcconfig; - buildSettings = { - CODE_SIGN_STYLE = Manual; - DEVELOPMENT_TEAM = ""; - INFOPLIST_FILE = WireCryptobox/Info.plist; - PRODUCT_BUNDLE_IDENTIFIER = com.wire.cryptobox; - PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = ""; - SKIP_INSTALL = YES; - }; - name = Release; - }; - 5471A66A1D24215B0092A9A9 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReferenceAnchor = 594E14A52D660CD200864BC1 /* Resources */; - baseConfigurationReferenceRelativePath = "Configurations/zmc-config/ios-test-host.xcconfig"; - buildSettings = { - INFOPLIST_FILE = "test-host/Info.plist"; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = "com.marco83.test-host"; - PRODUCT_NAME = "$(TARGET_NAME)"; - VALIDATE_WORKSPACE = YES; - }; - name = Debug; - }; - 5471A66B1D24215B0092A9A9 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReferenceAnchor = 594E14A52D660CD200864BC1 /* Resources */; - baseConfigurationReferenceRelativePath = "Configurations/zmc-config/ios-test-host.xcconfig"; - buildSettings = { - INFOPLIST_FILE = "test-host/Info.plist"; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = "com.marco83.test-host"; - PRODUCT_NAME = "$(TARGET_NAME)"; - VALIDATE_WORKSPACE = YES; - }; - name = Release; - }; - BA7EF9701B7109B600204A8E /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReferenceAnchor = 594E14A52D660CD200864BC1 /* Resources */; - baseConfigurationReferenceRelativePath = "Configurations/zmc-config/project-debug.xcconfig"; - buildSettings = { - ONLY_ACTIVE_ARCH = YES; - SDKROOT = iphoneos; - }; - name = Debug; - }; - BA7EF9711B7109B600204A8E /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReferenceAnchor = 594E14A52D660CD200864BC1 /* Resources */; - baseConfigurationReferenceRelativePath = "Configurations/zmc-config/project.xcconfig"; - buildSettings = { - SDKROOT = iphoneos; - }; - name = Release; - }; - BA7EF9761B7109B600204A8E /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReferenceAnchor = 594E14A52D660CD200864BC1 /* Resources */; - baseConfigurationReferenceRelativePath = "Configurations/zmc-config/ios-test-target.xcconfig"; - buildSettings = { - CODE_SIGN_STYLE = Manual; - DEVELOPMENT_TEAM = ""; - INFOPLIST_FILE = "$(PRODUCT_NAME)/Info.plist"; - PRODUCT_BUNDLE_IDENTIFIER = "com.wire.cryptobox.$(PRODUCT_NAME:rfc1034identifier)"; - PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = ""; - SWIFT_OBJC_BRIDGING_HEADER = "$(PRODUCT_NAME)/CryptoboxTests-Bridging-Header.h"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/WireCryptoboxTestHost.app/WireCryptoboxTestHost"; - VALIDATE_WORKSPACE = YES; - }; - name = Debug; - }; - BA7EF9771B7109B600204A8E /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReferenceAnchor = 594E14A52D660CD200864BC1 /* Resources */; - baseConfigurationReferenceRelativePath = "Configurations/zmc-config/ios-test-target.xcconfig"; - buildSettings = { - CODE_SIGN_STYLE = Manual; - DEVELOPMENT_TEAM = ""; - INFOPLIST_FILE = "$(PRODUCT_NAME)/Info.plist"; - PRODUCT_BUNDLE_IDENTIFIER = "com.wire.cryptobox.$(PRODUCT_NAME:rfc1034identifier)"; - PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = ""; - SWIFT_OBJC_BRIDGING_HEADER = "$(PRODUCT_NAME)/CryptoboxTests-Bridging-Header.h"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/WireCryptoboxTestHost.app/WireCryptoboxTestHost"; - VALIDATE_WORKSPACE = YES; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 0928E24B1BA0777A0057232E /* Build configuration list for PBXNativeTarget "WireCryptobox" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 0928E2471BA0777A0057232E /* Debug */, - 0928E2481BA0777A0057232E /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 5471A6691D24215B0092A9A9 /* Build configuration list for PBXNativeTarget "WireCryptoboxTestHost" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 5471A66A1D24215B0092A9A9 /* Debug */, - 5471A66B1D24215B0092A9A9 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - BA7EF9591B7109B600204A8E /* Build configuration list for PBXProject "WireCryptobox" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - BA7EF9701B7109B600204A8E /* Debug */, - BA7EF9711B7109B600204A8E /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - BA7EF9751B7109B600204A8E /* Build configuration list for PBXNativeTarget "WireCryptoboxTests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - BA7EF9761B7109B600204A8E /* Debug */, - BA7EF9771B7109B600204A8E /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - -/* Begin XCSwiftPackageProductDependency section */ - CBD35F2D2D09EBC10080DA37 /* Clibsodium */ = { - isa = XCSwiftPackageProductDependency; - productName = Clibsodium; - }; - CBD35F2F2D09EBD00080DA37 /* WireCrypto */ = { - isa = XCSwiftPackageProductDependency; - productName = WireCrypto; - }; -/* End XCSwiftPackageProductDependency section */ - }; - rootObject = BA7EF9561B7109B600204A8E /* Project object */; -} diff --git a/wire-ios-cryptobox/WireCryptobox.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/wire-ios-cryptobox/WireCryptobox.xcodeproj/project.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index cf961d2e914..00000000000 --- a/wire-ios-cryptobox/WireCryptobox.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/wire-ios-cryptobox/WireCryptobox.xcodeproj/project.xcworkspace/xcshareddata/IDETemplateMacros.plist b/wire-ios-cryptobox/WireCryptobox.xcodeproj/project.xcworkspace/xcshareddata/IDETemplateMacros.plist deleted file mode 120000 index 0aa12047039..00000000000 --- a/wire-ios-cryptobox/WireCryptobox.xcodeproj/project.xcworkspace/xcshareddata/IDETemplateMacros.plist +++ /dev/null @@ -1 +0,0 @@ -../../../../wire-ios-mono.xcworkspace/xcshareddata/IDETemplateMacros.plist \ No newline at end of file diff --git a/wire-ios-cryptobox/WireCryptobox.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/wire-ios-cryptobox/WireCryptobox.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d981003d6..00000000000 --- a/wire-ios-cryptobox/WireCryptobox.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/wire-ios-cryptobox/WireCryptobox.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/wire-ios-cryptobox/WireCryptobox.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings deleted file mode 100644 index dc8d12300fe..00000000000 --- a/wire-ios-cryptobox/WireCryptobox.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +++ /dev/null @@ -1,10 +0,0 @@ - - - - - BuildSystemType - Latest - IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded - - - diff --git a/wire-ios-cryptobox/WireCryptobox.xcodeproj/xcshareddata/xcschemes/WireCryptobox.xcscheme b/wire-ios-cryptobox/WireCryptobox.xcodeproj/xcshareddata/xcschemes/WireCryptobox.xcscheme deleted file mode 100644 index 583513c3e0c..00000000000 --- a/wire-ios-cryptobox/WireCryptobox.xcodeproj/xcshareddata/xcschemes/WireCryptobox.xcscheme +++ /dev/null @@ -1,72 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/wire-ios-cryptobox/WireCryptobox/CryptoBoxError.swift b/wire-ios-cryptobox/WireCryptobox/CryptoBoxError.swift deleted file mode 100644 index 735afe4fa9f..00000000000 --- a/wire-ios-cryptobox/WireCryptobox/CryptoBoxError.swift +++ /dev/null @@ -1,44 +0,0 @@ -// -// Wire -// Copyright (C) 2025 Wire Swiss GmbH -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see http://www.gnu.org/licenses/. -// - -import Clibsodium -import Foundation -import WireSystem - -extension CBoxResult: Error { - - /// Throw if self represents an error - func throwIfError() throws { - guard self == CBOX_SUCCESS else { - failIfCritical() - throw self - } - } - - func failIfCritical() { - if self == CBOX_PANIC || self == CBOX_INIT_ERROR { - fatalError("Cryptobox panic") - } - } -} - -extension CBoxResult: SafeForLoggingStringConvertible { - public var safeForLoggingDescription: String { - String(describing: self) - } -} diff --git a/wire-ios-cryptobox/WireCryptobox/Data+SafeForLogging.swift b/wire-ios-cryptobox/WireCryptobox/Data+SafeForLogging.swift deleted file mode 100644 index 6716f346ae7..00000000000 --- a/wire-ios-cryptobox/WireCryptobox/Data+SafeForLogging.swift +++ /dev/null @@ -1,36 +0,0 @@ -// -// Wire -// Copyright (C) 2025 Wire Swiss GmbH -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see http://www.gnu.org/licenses/. -// - -import WireUtilities - -extension Data: SafeForLoggingStringConvertible { - public var safeForLoggingDescription: String { - "<\(readableHash)>" - } -} - -// This allows for dump of data in safe logs. It's called "unsafe" because the data is -// dumped as-is, no hashing is applied. Be aware of what you are dumping here. -struct HexDumpUnsafeLoggingData: SafeForLoggingStringConvertible { - - let data: Data - - public var safeForLoggingDescription: String { - data.zmHexEncodedString() - } -} diff --git a/wire-ios-cryptobox/WireCryptobox/EncryptionContext.swift b/wire-ios-cryptobox/WireCryptobox/EncryptionContext.swift deleted file mode 100644 index e276be4166f..00000000000 --- a/wire-ios-cryptobox/WireCryptobox/EncryptionContext.swift +++ /dev/null @@ -1,200 +0,0 @@ -// -// Wire -// Copyright (C) 2025 Wire Swiss GmbH -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see http://www.gnu.org/licenses/. -// - -import Clibsodium -import Foundation -import WireSystem - -class _CBox: PointerWrapper {} - -/// A cryptobox context that manages access to sessions, allowing the -/// same sessions to be accessed by multuple processes in a safe way. -/// Inside a process, only a single session context should be used. -/// -/// - note: -/// In order to be used by multiple processes (see iOS extensions), cryptobox needs to lock the -/// directory with the key material as it works on it, so that no other process will touch it. -/// -/// This class introduces the concept of *encryption context*, similar to the concept of context in Core Data. -/// A context must be used only from a single thread. Multiple contexts can refer to the same -/// directory on disk, locking the directory when needed so that they don't interfere with -/// each other. -/// -/// Conflicts and race conditions are avoided by loading from disk and saving to disk -/// every time a context it used, and locking around these operations. -/// This is slow, but extensions are not supposed to need to access -/// cryptobox very frequently. -/// -/// The intended use of this class is: -/// -/// 1. Create context once, reuse the same context to avoid having to create/load identity -/// (which never changes once created, so no race condition other than during creation) -/// 2. use `perform:` with a block to create sessions, prekeys, encrypt and decrypt. -/// During the execution of the block, the directory is locked. -/// When decrypting, the decrypted data should be saved synchronously inside this block -/// (e.g. in case of Core Data, should be inserted and immediately saved) to enforce it -/// being saved before the session state is persisted later. -/// If the decrypted data is not persisted, and there is a crash before the data is -/// persisted, the data is lost forever as it can not be decrypted again once the session -/// is saved. -/// 3. When the block passed to `perform:` is completed, the sessions are persisted to disk. -/// The lock is relased. -public final class EncryptionContext: NSObject { - - /// What to do with modified sessions - public enum ModifiedSessionsBehaviour { - case save - case discard - } - - /// Set of session identifier that require full debugging logs - private var extensiveLoggingSessions = Set() - - /// Underlying C-style implementation - let implementation = _CBox() - - /// File directory with the implementation files - let path: URL - - /// The latest created and still open session directory - /// will be set to `nil` after calling `doneUsingSessions` - fileprivate(set) var currentSessionsDirectory: EncryptionSessionsDirectory? - - /// Folder file descriptor - fileprivate var fileDescriptor: CInt! - - /// Keeps track of how many times we enter a `perform` block, - /// to allow re-entry - fileprivate var performCount: UInt = 0 - - // The maximum size of the end-to-end encrypted payload is defined by ZMClientMessageByteSizeExternalThreshold - // It's currently 128KB of data. NOTE that this cache is shared between all sessions in an encryption context. - fileprivate let cache = Cache(maxCost: 10_000_000, maxElementsCount: 100_000) - - /// Opens cryptobox from a given folder - /// - throws: CryptoBox error in case of lower-level error - public init(path: URL) { - let result = cbox_file_open((path.path as NSString).utf8String, &implementation.ptr) - self.path = path - super.init() - if result != CBOX_SUCCESS { - fatal("Failed to open cryptobox: ERROR \(result.rawValue)") - } - self.fileDescriptor = open(self.path.path, 0) - if fileDescriptor <= 0 { - fatal("Can't obtain FD for folder \(self.path)") - } - zmLog.debug("Opened cryptobox at path: \(path)") - } - - deinit { - // unlock - self.releaseDirectoryLock() - // close - close(self.fileDescriptor) - // close cbox - cbox_close(implementation.ptr) - zmLog.debug("Closed cryptobox at path: \(path)") - - } - -} - -// MARK: - Start and stop using sessions - -extension EncryptionContext { - - /// Access sessions and other data in this context. While the block is executed, - /// no other process can use sessions from this context. If another process or thread is already - /// using sessions from a context with the same path, this call will block until the other process - /// stops using sessions. Nested calls to this method on the same objects on the same - /// thread are allowed. - /// - warning: this method is not thread safe - public func perform(_ block: (_ sessionsDirectory: EncryptionSessionsDirectory) -> Void) { - acquireDirectoryLock() - if currentSessionsDirectory == nil { - currentSessionsDirectory = - EncryptionSessionsDirectory( - generatingContext: self, - encryptionPayloadCache: cache, - extensiveLoggingSessions: extensiveLoggingSessions - ) - } - performCount += 1 - block(currentSessionsDirectory!) - performCount -= 1 - if performCount == 0 { - currentSessionsDirectory = nil - } - releaseDirectoryLock() - } - - // swiftlint:disable:next todo_requires_jira_link - // TODO: can this be removed? - public func performAsync(_ block: (_ sessionsDirectory: EncryptionSessionsDirectory) async -> Void) async { - acquireDirectoryLock() - if currentSessionsDirectory == nil { - currentSessionsDirectory = - EncryptionSessionsDirectory( - generatingContext: self, - encryptionPayloadCache: cache, - extensiveLoggingSessions: extensiveLoggingSessions - ) - } - performCount += 1 - await block(currentSessionsDirectory!) - performCount -= 1 - if performCount == 0 { - currentSessionsDirectory = nil - } - releaseDirectoryLock() - } - - private func acquireDirectoryLock() { - if flock(fileDescriptor, LOCK_EX) != 0 { - fatal("Failed to lock \(path)") - } - } - - private func releaseDirectoryLock() { - if flock(fileDescriptor, LOCK_UN) != 0 { - fatal("Failed to unlock \(path)") - } - } -} - -public extension EncryptionContext { - - /// Enables or disables extended logging for any message encrypted from or to - /// a specific session. - /// note: if the session is already cached in memory, this will apply from the - /// next time the session is reloaded - func setExtendedLogging(identifier: EncryptionSessionIdentifier, enabled: Bool) { - if enabled { - extensiveLoggingSessions.insert(identifier) - } else { - extensiveLoggingSessions.remove(identifier) - } - } - - /// Disable extensive logging on all sessions - func disableExtendedLoggingOnAllSessions() { - extensiveLoggingSessions.removeAll() - } - -} diff --git a/wire-ios-cryptobox/WireCryptobox/EncryptionSessionsDirectory.swift b/wire-ios-cryptobox/WireCryptobox/EncryptionSessionsDirectory.swift deleted file mode 100644 index c44027a11c3..00000000000 --- a/wire-ios-cryptobox/WireCryptobox/EncryptionSessionsDirectory.swift +++ /dev/null @@ -1,788 +0,0 @@ -// -// Wire -// Copyright (C) 2025 Wire Swiss GmbH -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see http://www.gnu.org/licenses/. -// - -import Foundation -import WireSystem -import WireUtilities - -@objc -enum EncryptionSessionError: Int { - - case unknown - case encryptionFailed - case decryptionFailed - - var userInfo: [String: AnyObject] { - let info = switch self { - case .unknown: - "Unknown EncryptionSessionError" - case .encryptionFailed: - "Encryption Failed" - case .decryptionFailed: - "Decryption Failed" - } - - return [kCFErrorLocalizedDescriptionKey as String: info as AnyObject] - } - - var error: NSError { - NSError(domain: "EncryptionSessionsDirectoryDomain", code: rawValue, userInfo: userInfo) - } - -} - -class _CBoxSession: PointerWrapper {} - -/// An encryption state that is usable to encrypt/decrypt messages -/// It maintains an in-memory cache of encryption sessions with other clients -/// that is persisted to disk as soon as it is deallocated. -public final class EncryptionSessionsDirectory: NSObject { - - /// Used for testing only. If set to true, - /// will not try to validate with the generating context - var debug_disableContextValidityCheck = false - - /// Set of session identifier that require full debugging logs - let extensiveLoggingSessions: Set - - /// Context that created this status - fileprivate weak var generatingContext: EncryptionContext! - - /// Local fingerprint - public var localFingerprint: Data - - fileprivate let encryptionPayloadCache: Cache - - /// Cache of transient sessions, indexed by client ID. - /// Transient sessions are session that are (potentially) modified in memory - /// and not yet committed to disk. When trying to load a session, - /// and that session is already in the list of transient sessions, - /// the transient session will be returned without any loading - /// occurring. As soon as a session is saved, it is removed from the cache. - /// - /// - note: This is an optimization: instead of loading, decrypting, - /// saving, unloading every time, if the same session is reused within - /// the same execution block, we don't need to spend time reading - /// and writing to disk every time we use the session, we can just - /// load once and save once at the end. - fileprivate var pendingSessionsCache: [EncryptionSessionIdentifier: EncryptionSession] = [:] - - init( - generatingContext: EncryptionContext, - encryptionPayloadCache: Cache, - extensiveLoggingSessions: Set - ) { - self.generatingContext = generatingContext - self.localFingerprint = generatingContext.implementation.localFingerprint - self.encryptionPayloadCache = encryptionPayloadCache - self.extensiveLoggingSessions = extensiveLoggingSessions - super.init() - zmLog.safePublic("Loaded encryption status - local fingerprint \(localFingerprint)") - } - - /// The underlying implementation of the box - var box: _CBox { - generatingContext!.implementation - } - - /// Checks whether self is in a valid state, i.e. the generating context is still open and - /// this is the current status. If not, it means that we are using this status after - /// the context was done using this status. - /// Will assert if this is the case. - fileprivate func validateContext() -> EncryptionContext { - guard debug_disableContextValidityCheck || generatingContext.currentSessionsDirectory === self else { - // If you hit this line, check if the status was stored in a variable for later use, - // or if it was used from different threads - it should never be. - fatalError("Using encryption status outside of a context") - } - return generatingContext! - } - - deinit { - self.commitCache() - } - - private func hash(for data: Data, recipient: EncryptionSessionIdentifier) -> GenericHash { - let builder = GenericHashBuilder() - builder.append(data) - builder.append(recipient.rawValue.data(using: .utf8)!) - return builder.build() - } - - /// Encrypts data for a client. Caches the encrypted payload based on `hash(data + recepient)` as the cache key. - /// It invokes @c encrypt() in case of the cache miss. - /// - throws: EncryptionSessionError in case no session with given recipient - public func encryptCaching(_ plainText: Data, for recipientIdentifier: EncryptionSessionIdentifier) throws -> Data { - let key = hash(for: plainText, recipient: recipientIdentifier) - - if let cachedObject = encryptionPayloadCache.value(for: key) { - zmLog.safePublic("Encrypting, cache hit") - return cachedObject - } else { - zmLog.debug("Encrypting, cache miss") - let data = try encrypt(plainText, for: recipientIdentifier) - let didPurgeData = encryptionPayloadCache.set(value: data, for: key, cost: data.count) - - if didPurgeData { - zmLog.safePublic("Encrypting, cache limit reached") - } - - return data - } - } - - /// Purges the cache of encrypted payloads created as the result of @c encryptCaching() call - public func purgeEncryptedPayloadCache() { - zmLog.safePublic("Encryption cache purged") - encryptionPayloadCache.purge() - } -} - -// MARK: - Encryption sessions - -public protocol EncryptionSessionManager { - /// Migrate session to a new identifier, if a session with the old identifier exists - /// and a session with the new identifier does not exist - func migrateSession(from previousIdentifier: String, to newIdentifier: EncryptionSessionIdentifier) - - /// Creates a session to a client using a prekey of that client - /// The session is not saved to disk until the cache is committed - /// - throws: CryptoBox error in case of lower-level error - func createClientSession(_ identifier: EncryptionSessionIdentifier, base64PreKeyString: String) throws - - /// Creates a session to a client using a prekey message from that client - /// The session is not saved to disk until the cache is committed - /// - returns: the plaintext - /// - throws: CryptoBox error in case of lower-level error - func createClientSessionAndReturnPlaintext(for identifier: EncryptionSessionIdentifier, prekeyMessage: Data) throws - -> Data - - /// Deletes a session with a client - func delete(_ identifier: EncryptionSessionIdentifier) - - /// Returns true if there is an existing session for this client ID - func hasSession(for identifier: EncryptionSessionIdentifier) -> Bool - - /// Closes all transient sessions without saving them - func discardCache() - - /// Returns the remote fingerprint of a encryption session - func fingerprint(for identifier: EncryptionSessionIdentifier) -> Data? -} - -extension EncryptionSessionsDirectory: EncryptionSessionManager { - - public func migrateSession(from previousIdentifier: String, to newIdentifier: EncryptionSessionIdentifier) { - - let previousSessionIdentifier = EncryptionSessionIdentifier(fromLegacyV1Identifier: previousIdentifier) - // this scopes guarantee that `old` is released - repeat { - guard let old = clientSession(for: previousSessionIdentifier) else { - return - } - - // save and close old one - old.save(box) - discardFromCache(previousSessionIdentifier) - } while false - - guard clientSession(for: newIdentifier) == nil else { - // There is an old and a new, delete the old - delete(previousSessionIdentifier) - return - } - - // copy to new one - let oldPath = filePath(for: previousSessionIdentifier) - let newPath = filePath(for: newIdentifier) - - guard FileManager.default.fileExists(atPath: oldPath.path) else { - fatal("Can't migrate session \(previousSessionIdentifier) because file \(oldPath) does not exist") - } - - guard (try? FileManager.default.moveItem(at: oldPath, to: newPath)) != nil else { - fatal("Can't migrate session \(newIdentifier) because the move failed") - } - - } - - public func createClientSession(_ identifier: EncryptionSessionIdentifier, base64PreKeyString: String) throws { - - // validate - guard let prekeyData = Data(base64Encoded: base64PreKeyString, options: []) else { - fatal("String is not base64 encoded from client: \(identifier)") - } - let context = validateContext() - - // check if pre-existing - if let session = clientSession(for: identifier) { - zmLog - .safePublic( - "Tried to create session for client \(identifier) with prekey but session already existed - fingerprint \(session.remoteFingerprint)" - ) - return - } - - // init - let cbsession = _CBoxSession() - let result = prekeyData.withUnsafeBytes { (prekeyDataPointer: UnsafeRawBufferPointer) -> CBoxResult in - cbox_session_init_from_prekey( - context.implementation.ptr, - identifier.rawValue, - prekeyDataPointer.bindMemory(to: UInt8.self).baseAddress!, - prekeyData.count, - &cbsession.ptr - ) - } - - try result.throwIfError() - - let session = EncryptionSession( - id: identifier, - session: cbsession, - requiresSave: true, - cryptoboxPath: generatingContext!.path, - extensiveLogging: extensiveLoggingSessions.contains(identifier) - ) - pendingSessionsCache[identifier] = session - - zmLog.safePublic("Created session for client \(identifier) - fingerprint \(session.remoteFingerprint)") - } - - public func createClientSessionAndReturnPlaintext( - for identifier: EncryptionSessionIdentifier, - prekeyMessage: Data - ) throws -> Data { - let context = validateContext() - let cbsession = _CBoxSession() - var plainTextBacking: OpaquePointer? - - let result = prekeyMessage.withUnsafeBytes { (prekeyMessagePointer: UnsafeRawBufferPointer) -> CBoxResult in - cbox_session_init_from_message( - context.implementation.ptr, - identifier.rawValue, - prekeyMessagePointer.baseAddress!.assumingMemoryBound(to: UInt8.self), - prekeyMessage.count, - &cbsession.ptr, - &plainTextBacking - ) - } - - let extensiveLogging = extensiveLoggingSessions.contains(identifier) - if extensiveLogging { - EncryptionSession.logSessionAndCyphertext( - sessionId: identifier, - reason: "decrypting prekey cyphertext", - data: prekeyMessage, - sessionURL: filePath(for: identifier) - ) - } - try result.throwIfError() - - let plainText = Data.moveFromCBoxVector(plainTextBacking)! - let session = EncryptionSession( - id: identifier, - session: cbsession, - requiresSave: true, - cryptoboxPath: generatingContext!.path, - extensiveLogging: extensiveLogging - ) - pendingSessionsCache[identifier] = session - - zmLog - .safePublic( - "Created session for client \(identifier) from prekey message - fingerprint \(session.remoteFingerprint)" - ) - - return plainText - } - - public func delete(_ identifier: EncryptionSessionIdentifier) { - let context = validateContext() - discardFromCache(identifier) - let result = cbox_session_delete(context.implementation.ptr, identifier.rawValue) - zmLog.safePublic("Delete session for client \(identifier)") - - guard result == CBOX_SUCCESS else { - fatal("Error in deletion in cbox: \(result)") - } - } - - /// Returns an existing session for a client - /// - returns: a session if it exists, or nil if not there - fileprivate func clientSession(for identifier: EncryptionSessionIdentifier) -> EncryptionSession? { - let context = validateContext() - - // check cache - if let transientSession = pendingSessionsCache[identifier] { - zmLog - .safePublic( - "Tried to load session for client \(identifier), session was already loaded - fingerprint \(transientSession.remoteFingerprint)" - ) - return transientSession - } - - let cbsession = _CBoxSession() - let result = cbox_session_load(context.implementation.ptr, identifier.rawValue, &cbsession.ptr) - switch result { - case CBOX_SESSION_NOT_FOUND: - zmLog.safePublic("Tried to load session for client \(identifier), no session found") - return nil - case CBOX_SUCCESS: - let session = EncryptionSession( - id: identifier, - session: cbsession, - requiresSave: false, - cryptoboxPath: generatingContext!.path, - extensiveLogging: extensiveLoggingSessions.contains(identifier) - ) - pendingSessionsCache[identifier] = session - zmLog.safePublic("Loaded session for client \(identifier) - fingerprint \(session.remoteFingerprint)") - return session - default: - fatalError("Error in loading from cbox: \(result)") - } - } - - public func hasSession(for identifier: EncryptionSessionIdentifier) -> Bool { - clientSession(for: identifier) != nil - } - - public func discardCache() { - zmLog.safePublic("Discarded all sessions from cache") - pendingSessionsCache = [:] - } - - /// Save and unload all transient sessions - fileprivate func commitCache() { - for (_, session) in pendingSessionsCache { - session.save(box) - } - discardCache() - } - - /// Closes a transient session. Any unsaved change will be lost - fileprivate func discardFromCache(_ identifier: EncryptionSessionIdentifier) { - zmLog.safePublic("Discarded session \(identifier) from cache") - pendingSessionsCache.removeValue(forKey: identifier) - } - - /// Saves the cached session for a client and removes it from the cache - fileprivate func saveSession(_ identifier: EncryptionSessionIdentifier) { - guard let session = pendingSessionsCache[identifier] else { - return - } - session.save(box) - discardFromCache(identifier) - } - - public func fingerprint(for identifier: EncryptionSessionIdentifier) -> Data? { - guard let session = clientSession(for: identifier) else { - return nil - } - return session.remoteFingerprint - } -} - -public protocol PrekeyGeneratorType { - func generatePrekey(_ id: UInt16) throws -> String - func generateLastPrekey() throws -> String - func generatePrekeys(_ range: CountableRange) throws -> [(id: UInt16, prekey: String)] - func generatePrekeys(_ nsRange: NSRange) throws -> [[String: AnyObject]] -} - -// MARK: - Prekeys - -extension EncryptionSessionsDirectory: PrekeyGeneratorType { - - /// Generates one prekey of the given ID. If the prekey exists already, - /// it will replace that prekey - /// - returns: base 64 encoded string - public func generatePrekey(_ id: UInt16) throws -> String { - guard id <= CBOX_LAST_PREKEY_ID else { - // this should never happen, as CBOX_LAST_PREKEY_ID is UInt16.max - fatal("Prekey out of bound \(id)") - } - var vectorBacking: OpaquePointer? - let context = validateContext() - let result = cbox_new_prekey(context.implementation.ptr, id, &vectorBacking) - let prekey = Data.moveFromCBoxVector(vectorBacking) - zmLog.debug("Generate prekey \(id)") - - try result.throwIfError() - - return prekey!.base64EncodedString(options: []) - } - - /// Generates the last prekey. If the prekey exists already, - /// it will replace that prekey - public func generateLastPrekey() throws -> String { - try generatePrekey(CBOX_LAST_PREKEY_ID) - } - - /// Generates prekeys from a range of IDs. If prekeys with those IDs exist already, - /// they will be replaced - public func generatePrekeys(_ range: CountableRange) throws -> [(id: UInt16, prekey: String)] { - try range.map { - let prekey = try self.generatePrekey($0) - return (id: $0, prekey: prekey) - } - } - - /// Generates prekeys from a range of IDs. If prekeys with those IDs exist already, - /// they will be replaced - /// This method wraps the Swift only method generatePrekeys(range: Range) for objC interoparability - @objc - public func generatePrekeys(_ nsRange: NSRange) throws -> [[String: AnyObject]] { - let prekeys = try generatePrekeys(UInt16(nsRange.location) ..< UInt16(nsRange.length)) - return prekeys.map { ["id": NSNumber(value: $0.id as UInt16), "prekey": $0.prekey as AnyObject] } - } - - /// Extracts the fingerprint from a prekey - /// - /// - returns: HEX encoded fingerprint - @objc(fingerprintFromPrekey:) - public static func fingerprint(fromPrekey prekey: Data) -> Data? { - prekey.withUnsafeBytes { - let bytes = $0.baseAddress?.assumingMemoryBound(to: UInt8.self) - var vectorBacking: OpaquePointer? - let result = cbox_fingerprint_prekey(bytes, $0.count, &vectorBacking) - - guard result == CBOX_SUCCESS else { - return nil - } - - return Data.moveFromCBoxVector(vectorBacking)! - } - } -} - -// MARK: - Fingerprint - -private extension _CBox { - - /// Local fingerprint - var localFingerprint: Data { - var vectorBacking: OpaquePointer? - let result = cbox_fingerprint_local(ptr, &vectorBacking) - guard result == CBOX_SUCCESS else { - fatal("Can't get local fingerprint") // this is so rare, that we don't even throw - } - return Data.moveFromCBoxVector(vectorBacking)! - } -} - -/// A cryptographic session used to encrypt/decrypt data send to and received from -/// another client -/// - note: This class is private because we want to make sure that no one can use -/// sessions outside of a status, that only dirty sessions are kept in memory, and -/// that sessions are unloaded as soon as possible, and that sessions are closed as soon -/// as they are unloaded. -/// We let the status manages closing sessions as there is no -/// other easy way to enforce (other than asserting) that we don't use a session to encrypt/decrypt -/// after it has been closed, and there is no easy way to ensure that sessions are always closed. -/// By hiding the implementation inside this file, only code in this file has the chance to screw up! -class EncryptionSession { - - /// Whether this session has changes that require saving - var hasChanges: Bool - - /// client ID - let id: EncryptionSessionIdentifier - - /// Underlying C-style implementation - let implementation: _CBoxSession - - /// The fingerpint of the client - let remoteFingerprint: Data - - /// Path of the containing cryptobox (used for debugging) - let cryptoboxPath: URL - - /// Whether to log additional information - let isExtensiveLoggingEnabled: Bool - - /// Creates a session from a C-level session pointer - /// - parameter id: id of the client - /// - parameter requiresSave: if true, mark this session as having pending changes to save - init( - id: EncryptionSessionIdentifier, - session: _CBoxSession, - requiresSave: Bool, - cryptoboxPath: URL, - extensiveLogging: Bool - ) { - self.id = id - self.implementation = session - self.remoteFingerprint = session.remoteFingerprint - self.hasChanges = requiresSave - self.cryptoboxPath = cryptoboxPath - self.isExtensiveLoggingEnabled = extensiveLogging - } - - /// Closes the session in CBox - private func closeInCryptobox() { - zmLog.safePublic("Closing cryptobox session \(id)") - cbox_session_close(implementation.ptr) - } - - /// Save the session to disk - fileprivate func save(_ cryptobox: _CBox) { - if hasChanges { - zmLog.safePublic("Saving cryptobox session \(id)") - let result = cbox_session_save(cryptobox.ptr, implementation.ptr) - switch result { - case CBOX_SUCCESS: - return - default: - fatal("Can't save session: error \(result)") - } - } - } - - deinit { - closeInCryptobox() - } -} - -// MARK: - Logging - -extension EncryptionSession { - - func logSessionAndCyphertext( - reason: SanitizedString, - data: Data - ) { - EncryptionSession.logSessionAndCyphertext( - sessionId: id, - reason: reason, - data: data, - sessionURL: path - ) - } - - static func logSessionAndCyphertext( - sessionId: EncryptionSessionIdentifier, - reason: SanitizedString, - data: Data, - sessionURL: URL - ) { - let encodedData = HexDumpUnsafeLoggingData(data: data) - let sessionContent = (try? Data(contentsOf: sessionURL)) - .map { HexDumpUnsafeLoggingData(data: $0) } - zmLog.safePublic( - SanitizedString("Extensive logging (session \(sessionId)): ") + - SanitizedString("\(reason): cyphertext: \(encodedData); ") + - SanitizedString("session content: \(sessionContent)"), - level: .public - ) - } -} - -// MARK: - Encryption - -public protocol Encryptor: AnyObject { - /// Encrypts data for a client - /// It immediately saves the session - /// - throws: EncryptionSessionError in case no session with given recipient - func encrypt(_ plainText: Data, for recipientIdentifier: EncryptionSessionIdentifier) throws -> Data -} - -// MARK: - Decryption - -public protocol Decryptor: AnyObject { - /// Decrypts data from a client - /// The session is not saved to disk until the cache is committed - /// - throws: EncryptionSessionError in case no session with given recipient - func decrypt(_ cypherText: Data, from senderIdentifier: EncryptionSessionIdentifier) throws -> Data -} - -extension EncryptionSessionsDirectory: Encryptor, Decryptor { - - public func encrypt(_ plainText: Data, for recipientIdentifier: EncryptionSessionIdentifier) throws -> Data { - _ = validateContext() - guard let session = clientSession(for: recipientIdentifier) else { - zmLog.safePublic("Can't find session to encrypt for client \(recipientIdentifier)") - throw EncryptionSessionError.encryptionFailed.error - } - let cypherText = try session.encrypt(plainText) - saveSession(recipientIdentifier) - return cypherText - } - - public func decrypt(_ cypherText: Data, from senderIdentifier: EncryptionSessionIdentifier) throws -> Data { - _ = validateContext() - guard let session = clientSession(for: senderIdentifier) else { - zmLog.safePublic("Can't find session to decrypt for client \(senderIdentifier)") - throw EncryptionSessionError.decryptionFailed.error - } - return try session.decrypt(cypherText) - } -} - -private extension EncryptionSession { - - /// Decrypts data using the session. This function modifies the session - /// and it should be saved later - func decrypt(_ cypher: Data) throws -> Data { - var vectorBacking: OpaquePointer? - - zmLog.safePublic("Decrypting with session \(id)") - - let result = cypher.withUnsafeBytes { (cypherPointer: UnsafeRawBufferPointer) -> CBoxResult in - cbox_decrypt( - self.implementation.ptr, - cypherPointer.baseAddress!.assumingMemoryBound(to: UInt8.self), - cypher.count, - &vectorBacking - ) - } - - let resultRequiresLogging = result != CBOX_DUPLICATE_MESSAGE && result != CBOX_SUCCESS - if resultRequiresLogging || isExtensiveLoggingEnabled { - if isExtensiveLoggingEnabled { - logSessionAndCyphertext( - reason: "decrypting cyphertext: result \(result)", - data: cypher - ) - } else { - let encodedData = HexDumpUnsafeLoggingData(data: cypher) - zmLog.safePublic("Failed to decrypt cyphertext: session \(id): \(encodedData)", level: .public) - } - } - - try result.throwIfError() - - hasChanges = true - return Data.moveFromCBoxVector(vectorBacking)! - } - - /// Encrypts data using the session. This function modifies the session - /// and it should be saved later - func encrypt(_ plainText: Data) throws -> Data { - var vectorBacking: OpaquePointer? - - zmLog.safePublic("Encrypting with session \(id)") - let result = plainText.withUnsafeBytes { (plainTextPointer: UnsafeRawBufferPointer) -> CBoxResult in - cbox_encrypt( - self.implementation.ptr, - plainTextPointer.baseAddress!.assumingMemoryBound(to: UInt8.self), - plainText.count, - &vectorBacking - ) - } - - try result.throwIfError() - - hasChanges = true - let data = Data.moveFromCBoxVector(vectorBacking)! - - if isExtensiveLoggingEnabled { - logSessionAndCyphertext( - reason: "encrypted to cyphertext", - data: data - ) - } - return data - } -} - -// MARK: - Fingerprint - -private extension _CBoxSession { - - /// Returns the remote fingerprint associated with a session - var remoteFingerprint: Data { - var backingVector: OpaquePointer? - let result = cbox_fingerprint_remote(ptr, &backingVector) - guard result == CBOX_SUCCESS else { - fatal("Can't access remote fingerprint of session \(result)") - } - return Data.moveFromCBoxVector(backingVector)! - } -} - -// MARK: - Backing files - -extension EncryptionSession { - - /// Returns the expected path of the session file, given the root folder - fileprivate static func expectedPath(root: URL, for identifier: EncryptionSessionIdentifier) -> URL { - root.appendingPathComponent("sessions").appendingPathComponent(identifier.rawValue) - } - - /// Returns the expected path of this session - var path: URL { - EncryptionSession.expectedPath(root: cryptoboxPath, for: id) - } -} - -private extension EncryptionSessionsDirectory { - - /// Returns the file path where the session with the given identifier would be saved - func filePath(for identifier: EncryptionSessionIdentifier) -> URL { - EncryptionSession.expectedPath(root: generatingContext.path, for: identifier) - } -} - -// MARK: - Session identifier - -public struct EncryptionSessionIdentifier: Hashable, Equatable { - - public let userId: String - public let clientId: String - public let domain: String - - public var rawValue: String { - guard !userId.isEmpty else { - return clientId - } - guard !domain.isEmpty else { - return "\(userId)_\(clientId)" - } - - return "\(domain)_\(userId)_\(clientId)" - } - - public init(domain: String? = nil, userId: String, clientId: String) { - self.userId = userId - self.clientId = clientId - self.domain = domain ?? "" - } - - /// Use when migrating from old session identifier to new session identifier - init(fromLegacyV1Identifier clientId: String) { - self.userId = String() - self.clientId = clientId - self.domain = String() - } - - public func hash(into hasher: inout Hasher) { - hasher.combine(rawValue) - } -} - -public func == (lhs: EncryptionSessionIdentifier, rhs: EncryptionSessionIdentifier) -> Bool { - lhs.rawValue == rhs.rawValue -} - -extension EncryptionSessionIdentifier: SafeForLoggingStringConvertible { - public var safeForLoggingDescription: String { - "<\(domain.readableHash)>_<\(userId.readableHash)>_<\(clientId.readableHash)>" - } -} diff --git a/wire-ios-cryptobox/WireCryptobox/GenericHash.swift b/wire-ios-cryptobox/WireCryptobox/GenericHash.swift deleted file mode 100644 index b1e3019b5f1..00000000000 --- a/wire-ios-cryptobox/WireCryptobox/GenericHash.swift +++ /dev/null @@ -1,92 +0,0 @@ -// -// Wire -// Copyright (C) 2025 Wire Swiss GmbH -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see http://www.gnu.org/licenses/. -// - -import Clibsodium -import Foundation - -/// Encapsulates the hash value. - -public struct GenericHash: Hashable { - private let value: Int - - init(value: Int) { - self.value = value - } -} - -extension GenericHash: CustomStringConvertible { - public var description: String { - "GenericHash \(hashValue)" - } -} - -/// This class is designed to generate the hash value for the given input data. -/// Sample usage: -/// -/// let builder = GenericHashBuilder() -/// builder.append(data1) -/// builder.append(data2) -/// let hash = builder.build() -public final class GenericHashBuilder { - private enum State { - case initial - case readyToBuild - case done - } - - private var cryptoState: UnsafeMutableRawBufferPointer - private var opaqueCryptoState: OpaquePointer - - private var state: State = .initial - private static let size = MemoryLayout.size - - init() { - self.cryptoState = UnsafeMutableRawBufferPointer.allocate( - byteCount: crypto_generichash_statebytes(), - alignment: 64 - ) - self.opaqueCryptoState = OpaquePointer(cryptoState.baseAddress!) - - crypto_generichash_init(opaqueCryptoState, nil, 0, GenericHashBuilder.size) - } - - public func append(_ data: Data) { - assert(state != .done, "This builder cannot be used any more: hash is already calculated") - state = data.withUnsafeBytes { (bytes: UnsafeRawBufferPointer) -> State in - crypto_generichash_update( - opaqueCryptoState, - bytes.baseAddress!.assumingMemoryBound(to: UInt8.self), - UInt64(data.count) - ) - return .readyToBuild - } - } - - public func build() -> GenericHash { - assert(state != .done, "This builder cannot be used any more: hash is already calculated") - var hashBytes: [UInt8] = Array(repeating: 0, count: GenericHashBuilder.size) - crypto_generichash_final(opaqueCryptoState, &hashBytes, GenericHashBuilder.size) - state = .done - let bigEndianUInt = hashBytes.withUnsafeBytes { $0.load(as: Int.self) } - let value = CFByteOrderGetCurrent() == CFByteOrder(CFByteOrderLittleEndian.rawValue) - ? Int(bigEndian: bigEndianUInt) - : bigEndianUInt - - return GenericHash(value: value) - } -} diff --git a/wire-ios-cryptobox/WireCryptobox/Info.plist b/wire-ios-cryptobox/WireCryptobox/Info.plist deleted file mode 100644 index 0e600e67e7d..00000000000 --- a/wire-ios-cryptobox/WireCryptobox/Info.plist +++ /dev/null @@ -1,26 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - FMWK - CFBundleShortVersionString - $(CURRENT_PROJECT_VERSION) - CFBundleSignature - ???? - CFBundleVersion - $(CURRENT_PROJECT_VERSION) - NSPrincipalClass - - - diff --git a/wire-ios-cryptobox/WireCryptobox/Logs.swift b/wire-ios-cryptobox/WireCryptobox/Logs.swift deleted file mode 100644 index 4f82bacaae8..00000000000 --- a/wire-ios-cryptobox/WireCryptobox/Logs.swift +++ /dev/null @@ -1,22 +0,0 @@ -// -// Wire -// Copyright (C) 2025 Wire Swiss GmbH -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see http://www.gnu.org/licenses/. -// - -import Foundation -import WireSystem - -let zmLog = ZMSLog(tag: "cryptobox") diff --git a/wire-ios-cryptobox/WireCryptobox/NSData+CBox.swift b/wire-ios-cryptobox/WireCryptobox/NSData+CBox.swift deleted file mode 100644 index 09d0a3ed477..00000000000 --- a/wire-ios-cryptobox/WireCryptobox/NSData+CBox.swift +++ /dev/null @@ -1,35 +0,0 @@ -// -// Wire -// Copyright (C) 2025 Wire Swiss GmbH -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see http://www.gnu.org/licenses/. -// - -import Clibsodium -import Foundation - -extension Data { - - /// Moves from a CBoxVector to this data - /// During this call, the CBoxVector is freed - static func moveFromCBoxVector(_ vector: OpaquePointer?) -> Data? { - guard let vector else { return nil } - - let data = cbox_vec_data(vector) - let length = cbox_vec_len(vector) - let finalData = Data(bytes: UnsafePointer(data!), count: length) // this ctor copies - cbox_vec_free(vector) - return finalData - } -} diff --git a/wire-ios-cryptobox/WireCryptobox/PointerWrapper.swift b/wire-ios-cryptobox/WireCryptobox/PointerWrapper.swift deleted file mode 100644 index fc8330d5936..00000000000 --- a/wire-ios-cryptobox/WireCryptobox/PointerWrapper.swift +++ /dev/null @@ -1,35 +0,0 @@ -// -// Wire -// Copyright (C) 2025 Wire Swiss GmbH -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see http://www.gnu.org/licenses/. -// - -import Foundation - -/// This class is used to add type safety to C opaque pointers. -/// Just subclass this class and add the subclass to all signatures -/// -/// E.g. -/// ``` -/// class CStruct : PointerWrapper {} -/// -/// func foo(struct: CStruct) -> Int { -/// return some_c_function(struct.ptr) -/// } -/// -/// ``` -class PointerWrapper { - var ptr: OpaquePointer? -} diff --git a/wire-ios-cryptobox/WireCryptobox/WireCryptobox.h b/wire-ios-cryptobox/WireCryptobox/WireCryptobox.h deleted file mode 100644 index a6b2e605cc4..00000000000 --- a/wire-ios-cryptobox/WireCryptobox/WireCryptobox.h +++ /dev/null @@ -1,27 +0,0 @@ -// -// Wire -// Copyright (C) 2025 Wire Swiss GmbH -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see http://www.gnu.org/licenses/. -// - -#import - -//! Project version number for WireCryptobox. -FOUNDATION_EXPORT double WireCryptobox_VersionNumber; - -//! Project version string for WireCryptobox. -FOUNDATION_EXPORT const unsigned char WireCryptobox_VersionString[]; - -#import diff --git a/wire-ios-cryptobox/WireCryptobox/cbox.h b/wire-ios-cryptobox/WireCryptobox/cbox.h deleted file mode 120000 index 5f3e4c2b3f9..00000000000 --- a/wire-ios-cryptobox/WireCryptobox/cbox.h +++ /dev/null @@ -1 +0,0 @@ -../../Carthage/Build/cryptobox.xcframework/ios-arm64/Headers/cbox.h \ No newline at end of file diff --git a/wire-ios-cryptobox/WireCryptoboxTests/ChaCha20AEADEncryptionTests.swift b/wire-ios-cryptobox/WireCryptoboxTests/ChaCha20AEADEncryptionTests.swift deleted file mode 100644 index a0c1848201e..00000000000 --- a/wire-ios-cryptobox/WireCryptoboxTests/ChaCha20AEADEncryptionTests.swift +++ /dev/null @@ -1,167 +0,0 @@ -// -// Wire -// Copyright (C) 2025 Wire Swiss GmbH -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see http://www.gnu.org/licenses/. -// - -import Clibsodium -import WireCrypto -import XCTest - -@testable import WireCryptobox - -class ChaCha20AEADEncryptionTests: XCTestCase { - - private let context = Data.secureRandomData(length: 8) - - // MARK: - Helpers - - private typealias Sut = ChaCha20Poly1305.AEADEncryption - - private func generateRandomCiphertext(length: UInt) -> Data { - // Large enough to include authentication bytes in the ciphertext. - Data.secureRandomData(length: length + UInt(crypto_aead_aes256gcm_ABYTES)) - } - - // MARK: - Positive Tests - - func testThatItEncryptsAndDecryptsMessage() throws { - // Given - let message = Data("Hello, world".utf8) - let key = Data.zmRandomSHA256Key() - - // When - let (ciphertext, nonce) = try Sut.encrypt(message: message, context: context, key: key) - - // Then - XCTAssertNotEqual(ciphertext, message) - XCTAssertFalse(nonce.isEmpty) - - // When - let decryptedMessage = try Sut.decrypt(ciphertext: ciphertext, nonce: nonce, context: context, key: key) - - // Then - XCTAssertEqual(decryptedMessage, message) - } - - // MARK: - Negative Tests - - func testThatItFailsToEncryptIfKeyIsMalformed() throws { - // Given - let keyOfWrongLength = Data.zmRandomSHA256Key().dropLast() - - do { - // When - _ = try Sut.encrypt(message: Data(), context: context, key: keyOfWrongLength) - } catch let error as Sut.EncryptionError { - // Then - XCTAssertEqual(error, .malformedKey) - } catch { - XCTFail("Unexpected error: \(error.localizedDescription)") - } - } - - func testThatItFailsToDecryptIfKeyIsMalformed() throws { - // Given - let ciphertext = generateRandomCiphertext(length: 8) - let nonce = Data(Sut.generateRandomNonceBytes()) - let keyOfWrongLength = Data.zmRandomSHA256Key().dropLast() - - do { - // When - _ = try Sut.decrypt(ciphertext: ciphertext, nonce: nonce, context: context, key: keyOfWrongLength) - } catch let error as Sut.EncryptionError { - // Then - XCTAssertEqual(error, .malformedKey) - } catch { - XCTFail("Unexpected error: \(error.localizedDescription)") - } - } - - func testThatItFailsToDecryptIfNonceIsMalformed() throws { - // Given - let ciphertext = generateRandomCiphertext(length: 8) - let nonceOfWrongLength = Data(Sut.generateRandomNonceBytes()).dropLast() - let key = Data.zmRandomSHA256Key() - - do { - // When - _ = try Sut.decrypt(ciphertext: ciphertext, nonce: nonceOfWrongLength, context: context, key: key) - } catch let error as Sut.EncryptionError { - // Then - XCTAssertEqual(error, .malformedNonce) - } catch { - XCTFail("Unexpected error: \(error.localizedDescription)") - } - } - - func testThatItFailsToDecryptWithDifferentKey() throws { - // Given - let message = Data("Hello, world".utf8) - let key1 = Data.zmRandomSHA256Key() - let key2 = Data.zmRandomSHA256Key() - - let (ciphertext, nonce) = try Sut.encrypt(message: message, context: context, key: key1) - - do { - // When - _ = try Sut.decrypt(ciphertext: ciphertext, nonce: nonce, context: context, key: key2) - } catch let error as Sut.EncryptionError { - // Then - XCTAssertEqual(error, .failedToDecrypt) - } catch { - XCTFail("Unexpected error: \(error.localizedDescription)") - } - } - - func testThatItFailsToDecryptWithDifferentNonce() throws { - // Given - let message = Data("Hello, world".utf8) - let key = Data.zmRandomSHA256Key() - let randomNonce = Data(Sut.generateRandomNonceBytes()) - - let (ciphertext, _) = try Sut.encrypt(message: message, context: context, key: key) - - do { - // When - _ = try Sut.decrypt(ciphertext: ciphertext, nonce: randomNonce, context: context, key: key) - } catch let error as Sut.EncryptionError { - // Then - XCTAssertEqual(error, .failedToDecrypt) - } catch { - XCTFail("Unexpected error: \(error.localizedDescription)") - } - } - - func testThatItFailsToDecryptWithDifferentContext() throws { - // Given - let message = Data("Hello, world".utf8) - let key = Data.zmRandomSHA256Key() - let randomNonce = Data(Sut.generateRandomNonceBytes()) - - let (ciphertext, _) = try Sut.encrypt(message: message, context: context, key: key) - - do { - // When - _ = try Sut.decrypt(ciphertext: ciphertext, nonce: randomNonce, context: context.dropFirst(), key: key) - } catch let error as Sut.EncryptionError { - // Then - XCTAssertEqual(error, .failedToDecrypt) - } catch { - XCTFail("Unexpected error: \(error.localizedDescription)") - } - } - -} diff --git a/wire-ios-cryptobox/WireCryptoboxTests/CryptoboxTests-Bridging-Header.h b/wire-ios-cryptobox/WireCryptoboxTests/CryptoboxTests-Bridging-Header.h deleted file mode 100644 index 89c90917712..00000000000 --- a/wire-ios-cryptobox/WireCryptoboxTests/CryptoboxTests-Bridging-Header.h +++ /dev/null @@ -1,17 +0,0 @@ -// -// Wire -// Copyright (C) 2025 Wire Swiss GmbH -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see http://www.gnu.org/licenses/. -// diff --git a/wire-ios-cryptobox/WireCryptoboxTests/EncryptionContextCachingTests.swift b/wire-ios-cryptobox/WireCryptoboxTests/EncryptionContextCachingTests.swift deleted file mode 100644 index 5d65a2346f6..00000000000 --- a/wire-ios-cryptobox/WireCryptoboxTests/EncryptionContextCachingTests.swift +++ /dev/null @@ -1,152 +0,0 @@ -// -// Wire -// Copyright (C) 2025 Wire Swiss GmbH -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see http://www.gnu.org/licenses/. -// - -import XCTest -@testable import WireCryptobox - -let someTextToEncrypt = "ENCRYPT THIS!" - -class DebugEncryptor: Encryptor { - var index: Int = 0 - func encrypt(_ plainText: Data, for recipientIdentifier: EncryptionSessionIdentifier) throws -> Data { - var result = plainText - result.append(recipientIdentifier.rawValue.data(using: .utf8)!) - result.append(Data("\(index)".utf8)) - index += 1 - return result - } -} - -class EncryptionContextCachingTests: XCTestCase { - func testThatItDoesNotCachePerDefault() { - // GIVEN - let tempDir = createTempFolder() - let mainContext = EncryptionContext(path: tempDir) - - let expectation = expectation(description: "Encryption succeeded") - - // WHEN - mainContext.perform { sessionContext in - try! sessionContext.createClientSession(hardcodedClientId, base64PreKeyString: hardcodedPrekey) - - let encryptedDataNonCachedFirst = try! sessionContext.encrypt( - someTextToEncrypt.data(using: .utf8)!, - for: hardcodedClientId - ) - let encryptedDataNonCachedSecond = try! sessionContext.encrypt( - someTextToEncrypt.data(using: .utf8)!, - for: hardcodedClientId - ) - - XCTAssertNotEqual(encryptedDataNonCachedFirst, encryptedDataNonCachedSecond) - - expectation.fulfill() - } - - // THEN - waitForExpectations(timeout: 0) { _ in } - } - - func testThatItCachesWhenRequested() { - // GIVEN - let tempDir = createTempFolder() - let mainContext = EncryptionContext(path: tempDir) - - let expectation = expectation(description: "Encryption succeeded") - - // WHEN - mainContext.perform { sessionContext in - try! sessionContext.createClientSession(hardcodedClientId, base64PreKeyString: hardcodedPrekey) - - let encryptedDataFirst = try! sessionContext.encryptCaching( - someTextToEncrypt.data(using: .utf8)!, - for: hardcodedClientId - ) - let encryptedDataSecond = try! sessionContext.encryptCaching( - someTextToEncrypt.data(using: .utf8)!, - for: hardcodedClientId - ) - - XCTAssertEqual(encryptedDataFirst, encryptedDataSecond) - - expectation.fulfill() - } - - // THEN - waitForExpectations(timeout: 0) { _ in } - } - - func testThatCacheKeyDependsOnData() { - // GIVEN - let tempDir = createTempFolder() - let mainContext = EncryptionContext(path: tempDir) - - let expectation = expectation(description: "Encryption succeeded") - - // WHEN - mainContext.perform { sessionContext in - try! sessionContext.createClientSession(hardcodedClientId, base64PreKeyString: hardcodedPrekey) - - let encryptedDataFirst = try! sessionContext.encryptCaching( - someTextToEncrypt.data(using: .utf8)!, - for: hardcodedClientId - ) - let encryptedDataSecond = try! sessionContext.encryptCaching( - someTextToEncrypt.appending(someTextToEncrypt).data(using: .utf8)!, - for: hardcodedClientId - ) - - XCTAssertNotEqual(encryptedDataFirst, encryptedDataSecond) - - expectation.fulfill() - } - - // THEN - waitForExpectations(timeout: 0) { _ in } - } - - func testThatItFlushesTheCache() { - // GIVEN - let tempDir = createTempFolder() - let mainContext = EncryptionContext(path: tempDir) - - let expectation = expectation(description: "Encryption succeeded") - - // WHEN - mainContext.perform { sessionContext in - try! sessionContext.createClientSession(hardcodedClientId, base64PreKeyString: hardcodedPrekey) - - let encryptedDataFirst = try! sessionContext.encryptCaching( - someTextToEncrypt.data(using: .utf8)!, - for: hardcodedClientId - ) - sessionContext.purgeEncryptedPayloadCache() - let encryptedDataSecond = try! sessionContext.encryptCaching( - someTextToEncrypt.data(using: .utf8)!, - for: hardcodedClientId - ) - - XCTAssertNotEqual(encryptedDataFirst, encryptedDataSecond) - - expectation.fulfill() - } - - // THEN - waitForExpectations(timeout: 0) { _ in } - } -} diff --git a/wire-ios-cryptobox/WireCryptoboxTests/EncryptionContextTests.swift b/wire-ios-cryptobox/WireCryptoboxTests/EncryptionContextTests.swift deleted file mode 100644 index 04d9953eb42..00000000000 --- a/wire-ios-cryptobox/WireCryptoboxTests/EncryptionContextTests.swift +++ /dev/null @@ -1,272 +0,0 @@ -// -// Wire -// Copyright (C) 2025 Wire Swiss GmbH -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see http://www.gnu.org/licenses/. -// - -import XCTest -@testable import WireCryptobox - -class EncryptionContextTests: XCTestCase { - - /// This test verifies that the critical section (in usingSessions) - /// can not be entered at the same time on two different EncryptionContext - func testThatItBlockWhileUsingSessionsOnTwoDifferentObjects() { - - // GIVEN - let tempDir = createTempFolder() - - // have to do work on other queues because the main thread can't be blocked - let queue1 = DispatchQueue(label: name) - let queue2 = DispatchQueue(label: name) - - // coordinate between the two threads to make sure that they are executed in the right order - let context2CanEnterSemaphore = DispatchSemaphore(value: 0) - let context1CanCompleteSemaphore = DispatchSemaphore(value: 0) - let queue2IsDoneSemaphore = DispatchSemaphore(value: 0) - - // whether the queues entered the critical section - var queue1EnteredCriticalSection = false - var queue1LeftCriticalSection = false - var queue2EnteredCriticalSection = false - var queue2LeftCriticalSection = false - - // WHEN - - // queue 1 will enter critical section and wait there until told to complete - queue1.async { - - // entering the critical section - EncryptionContext(path: tempDir).perform { _ in - queue1EnteredCriticalSection = true - // signal queue2 other thread that it should attempt to enter critical section - context2CanEnterSemaphore.signal() - // wait until it's told to leave critical section - context1CanCompleteSemaphore.wait() - } - queue1LeftCriticalSection = true - } - - // queue 2 will try to enter critical section, but should block (because of queue 1) - queue2.async { - - // make sure queue 1 is in the right place (critical section) before attempting to enter critical section - context2CanEnterSemaphore.wait() - EncryptionContext(path: tempDir).perform { _ in - // will not get here until queue1 has quit critical section - queue2EnteredCriticalSection = true - } - queue2LeftCriticalSection = true - queue2IsDoneSemaphore.signal() - } - - // wait a few ms so that all threads are ready - Thread.sleep(forTimeInterval: 0.3) - - // THEN - XCTAssertTrue(queue1EnteredCriticalSection) - XCTAssertFalse(queue1LeftCriticalSection) - XCTAssertFalse(queue2EnteredCriticalSection) - XCTAssertFalse(queue2LeftCriticalSection) - - // WHEN - context1CanCompleteSemaphore.signal() - - // THEN - queue2IsDoneSemaphore.wait() - XCTAssertTrue(queue1EnteredCriticalSection) - XCTAssertTrue(queue1LeftCriticalSection) - XCTAssertTrue(queue2EnteredCriticalSection) - XCTAssertTrue(queue2LeftCriticalSection) - } - - func testThatItDoesNotBlockWhileUsingSessionsMultipleTimesOnTheSameObject() { - - // GIVEN - let tempDir = createTempFolder() - - let mainContext = EncryptionContext(path: tempDir) - let invocation1 = expectation(description: "first begin using session") - let invocation2 = expectation(description: "second begin using session") - - // WHEN - // enter critical section - mainContext.perform { _ in - invocation1.fulfill() - - // enter again - mainContext.perform { _ in - invocation2.fulfill() - } - } - - // THEN - waitForExpectations(timeout: 0) { _ in } - - } - - func testThatItReceivesTheSameSessionStatusWithNestedPerform() { - - // GIVEN - let tempDir = createTempFolder() - - let mainContext = EncryptionContext(path: tempDir) - var lastStatus: EncryptionSessionsDirectory? - - let invocation1 = expectation(description: "first begin using session") - let invocation2 = expectation(description: "second begin using session") - - // WHEN - - // enter critical section - mainContext.perform { context1 in - lastStatus = context1 - invocation1.fulfill() - - mainContext.perform { context2 in - XCTAssertTrue(lastStatus === context2) - invocation2.fulfill() - } - } - - // THEN - waitForExpectations(timeout: 0) { _ in } - } - - func testThatItSafelyEncryptDecryptDuringNestedPerform() { - - // GIVEN - let tempDir = createTempFolder() - - let mainContext = EncryptionContext(path: tempDir) - - let someTextToEncrypt = "ENCRYPT THIS!" - - // WHEN - - // enter critical section - mainContext.perform { (context1: EncryptionSessionsDirectory) in - - try! context1.createClientSession(hardcodedClientId, base64PreKeyString: hardcodedPrekey) - - mainContext.perform { (context2: EncryptionSessionsDirectory) in - _ = try! context2.encrypt(someTextToEncrypt.data(using: .utf8)!, for: hardcodedClientId) - - } - - _ = try! context1.encrypt(someTextToEncrypt.data(using: .utf8)!, for: hardcodedClientId) - } - - // THEN - // it didn't crash - } - - func testThatItDoesNotReceivesTheSameSessionStatusIfDonePerforming() { - - // GIVEN - let tempDir = createTempFolder() - - let mainContext = EncryptionContext(path: tempDir) - var lastStatus: EncryptionSessionsDirectory? - - let invocation1 = expectation(description: "first begin using session") - let invocation2 = expectation(description: "second begin using session") - - // WHEN - - // enter critical section - mainContext.perform { context in - lastStatus = context - invocation1.fulfill() - } - - // THEN - // enter again - mainContext.perform { context in - invocation2.fulfill() - XCTAssertFalse(lastStatus === context) - } - - waitForExpectations(timeout: 0) { _ in } - } - -} - -// MARK: - Logging - -extension EncryptionContextTests { - - func testThatItSetsExtendedLoggingOnSessions() { - - // GIVEN - let identifier = EncryptionSessionIdentifier(domain: "example.com", userId: "user", clientId: "foo") - let tempDir = createTempFolder() - let mainContext = EncryptionContext(path: tempDir) - - // WHEN - mainContext.setExtendedLogging(identifier: identifier, enabled: true) - - // THEN - mainContext.perform { - XCTAssertEqual($0.extensiveLoggingSessions, Set([identifier])) - } - } - - func testThatItDoesSetExtendedLoggingOnSessions() { - - // GIVEN - let tempDir = createTempFolder() - let mainContext = EncryptionContext(path: tempDir) - - // THEN - mainContext.perform { - XCTAssert($0.extensiveLoggingSessions.isEmpty) - } - } - - func testThatItDoesNotLogEncryptionWhenRemovingExtendedLogging() { - - // GIVEN - let identifier = EncryptionSessionIdentifier(domain: "example.com", userId: "user", clientId: "foo") - let tempDir = createTempFolder() - let mainContext = EncryptionContext(path: tempDir) - - // WHEN - mainContext.setExtendedLogging(identifier: identifier, enabled: true) - mainContext.setExtendedLogging(identifier: identifier, enabled: false) - - // THEN - mainContext.perform { - XCTAssert($0.extensiveLoggingSessions.isEmpty) - } - } - - func testThatItDoesNotLogEncryptionWhenRemovingAllExtendedLogging() { - - // GIVEN - let identifier = EncryptionSessionIdentifier(domain: "example.com", userId: "user", clientId: "foo") - let tempDir = createTempFolder() - let mainContext = EncryptionContext(path: tempDir) - - // WHEN - mainContext.setExtendedLogging(identifier: identifier, enabled: true) - mainContext.disableExtendedLoggingOnAllSessions() - - // THEN - mainContext.perform { - XCTAssert($0.extensiveLoggingSessions.isEmpty) - } - } -} diff --git a/wire-ios-cryptobox/WireCryptoboxTests/EncryptionSessionsDirectoryTests.swift b/wire-ios-cryptobox/WireCryptoboxTests/EncryptionSessionsDirectoryTests.swift deleted file mode 100644 index f1d17bfe8b8..00000000000 --- a/wire-ios-cryptobox/WireCryptoboxTests/EncryptionSessionsDirectoryTests.swift +++ /dev/null @@ -1,850 +0,0 @@ -// -// Wire -// Copyright (C) 2025 Wire Swiss GmbH -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see http://www.gnu.org/licenses/. -// - -import Foundation -import WireSystem -import XCTest -@testable import WireCryptobox - -class EncryptionSessionsDirectoryTests: XCTestCase { - - var contextAlice: EncryptionContext! - var contextBob: EncryptionContext! - var statusAlice: EncryptionSessionsDirectory! - var statusBob: EncryptionSessionsDirectory! - - override func setUp() { - contextAlice = createEncryptionContext() - contextBob = createEncryptionContext() - recreateStatuses() - } - - override func tearDown() { - statusAlice = nil - statusBob = nil - contextAlice = nil - contextBob = nil - } - -} - -// MARK: - Session creation and encoding/decoding - -extension EncryptionSessionsDirectoryTests { - - func testThatItCanDecodeAfterInitializingWithAValidKey() throws { - - // GIVEN - let plainText = Data("foo".utf8) - - // WHEN - try statusAlice.createClientSession(Person.Bob.identifier, base64PreKeyString: statusBob.generatePrekey(2)) - - // THEN - let prekeyMessage = try! statusAlice.encrypt(plainText, for: Person.Bob.identifier) - let decoded = try! statusBob.createClientSessionAndReturnPlaintext( - for: Person.Alice.identifier, - prekeyMessage: prekeyMessage - ) - XCTAssertEqual(decoded, plainText) - } - - func testThatItCanCallCreateSessionWithTheSameKeyMultipleTimes() throws { - - // GIVEN - let plainText = Data("foo".utf8) - let prekey = try! statusBob.generatePrekey(34) - try statusAlice.createClientSession(Person.Bob.identifier, base64PreKeyString: prekey) - - // WHEN - try statusAlice.createClientSession(Person.Bob.identifier, base64PreKeyString: prekey) - - // THEN - let prekeyMessage = try! statusAlice.encrypt(plainText, for: Person.Bob.identifier) - let decoded = try! statusBob.createClientSessionAndReturnPlaintext( - for: Person.Alice.identifier, - prekeyMessage: prekeyMessage - ) - XCTAssertEqual(decoded, plainText) - } - - func testThatItCanNotCreateANewSessionWithAnInvalidKey() { - - // GIVEN - - // WHEN - do { - _ = try statusAlice.createClientSession(Person.Bob.identifier, base64PreKeyString: "aabb") - XCTFail("should have failed to use prekey") - } catch let err as CBoxResult { - XCTAssertEqual(err, CBOX_DECODE_ERROR) - } catch { - XCTFail("should have thrown a CBoxResult") - } - } - - func testThatItCanNotDecodePrekeyMessagesWithTheWrongKey() throws { - - // WHEN - _ = try statusAlice.createClientSession(Person.Bob.identifier, base64PreKeyString: hardcodedPrekey) - - // THEN - XCTAssertFalse(checkThatAMessageCanBeSent(.Alice)) - } -} - -// MARK: - Prekeys - -extension EncryptionSessionsDirectoryTests { - - func testThatFingerprintExtractedFromPrekeyMatchesLocalFingerprint() throws { - let prekeyId: UInt16 = 12 - let prekeyData = try statusAlice.generatePrekey(prekeyId) - let fingerprint = EncryptionSessionsDirectory.fingerprint(fromPrekey: Data(base64Encoded: prekeyData)!) - - XCTAssertEqual(fingerprint, statusAlice.localFingerprint) - } - - func testThatItGeneratesAPrekey() { - - // GIVEN - let prekeyId: UInt16 = 12 - - // WHEN - let prekey = try! statusAlice.generatePrekey(prekeyId) - - // THEN - var prekeyRetrievedId: UInt16 = 0 - let prekeyData = Data(base64Encoded: prekey, options: [])! - let result = prekeyData - .withUnsafeBytes { (prekeyDataPointer: UnsafeRawBufferPointer) -> CBoxResult in cbox_is_prekey( - prekeyDataPointer.baseAddress!.assumingMemoryBound(to: UInt8.self), - prekeyData.count, - &prekeyRetrievedId - ) } - XCTAssertEqual(result, CBOX_SUCCESS) - XCTAssertEqual(prekeyRetrievedId, prekeyId) - - } - - func testThatItGeneratesLastPrekey() { - - // GIVEN - let prekeyId: UInt16 = CBOX_LAST_PREKEY_ID - - // WHEN - let prekey = try! statusAlice.generateLastPrekey() - - // THEN - var prekeyRetrievedId: UInt16 = 0 - let prekeyData = Data(base64Encoded: prekey, options: [])! - let result = prekeyData - .withUnsafeBytes { (prekeyDataPointer: UnsafeRawBufferPointer) -> CBoxResult in cbox_is_prekey( - prekeyDataPointer.baseAddress!.assumingMemoryBound(to: UInt8.self), - prekeyData.count, - &prekeyRetrievedId - ) } - XCTAssertEqual(result, CBOX_SUCCESS) - XCTAssertEqual(prekeyRetrievedId, prekeyId) - - } - - func testThatItGeneratesARangeOfPrekeys() { - - // GIVEN - let rangeStart = 3 - let rangeLength = 10 - let prekeyIds: CountableRange = UInt16(rangeStart) ..< UInt16(rangeStart + rangeLength) - - // WHEN - var prekeys: [(id: UInt16, prekey: String)] = [] - prekeys = try! statusAlice.generatePrekeys(prekeyIds) - - // THEN - XCTAssertEqual(prekeyIds.count, rangeLength) - for i in 0 ..< rangeLength { - let (id, prekey) = prekeys[i] - let prekeyData = Data(base64Encoded: prekey, options: [])! - var prekeyRetrievedId: UInt16 = 0 - let result = prekeyData - .withUnsafeBytes { (prekeyDataPointer: UnsafeRawBufferPointer) -> CBoxResult in cbox_is_prekey( - prekeyDataPointer.baseAddress!.assumingMemoryBound(to: UInt8.self), - prekeyData.count, - &prekeyRetrievedId - ) } - XCTAssertEqual(result, CBOX_SUCCESS) - XCTAssertEqual(Int(prekeyRetrievedId), i + rangeStart) - XCTAssertEqual(prekeyRetrievedId, id) - } - } -} - -// MARK: - Local fingerprint - -extension EncryptionSessionsDirectoryTests { - - func testThatItReturnsTheLocalFingerprint() { - - // GIVEN - - // WHEN - let fingerprint = statusAlice.localFingerprint - - // THEN - // check it's consistent - XCTAssertEqual(statusAlice.localFingerprint, fingerprint) - } - - func testThatASessionHasAMatchingRemoteFingerprint() { - - // GIVEN - - // WHEN - establishSessionBetweenAliceAndBob() - - // THEN - let aliceLocalFingerprint = statusAlice.localFingerprint - let bobLocalFingerprint = statusBob.localFingerprint - let aliceRemoteFingerprint = statusBob.fingerprint(for: Person.Alice.identifier) - let bobRemoteFingerprint = statusAlice.fingerprint(for: Person.Bob.identifier) - XCTAssertEqual(aliceLocalFingerprint, aliceRemoteFingerprint) - XCTAssertEqual(bobLocalFingerprint, bobRemoteFingerprint) - XCTAssertNotNil(aliceLocalFingerprint) - XCTAssertNotNil(bobLocalFingerprint) - } - - func testThatAClientWithoutSessionHasNoRemoteFingerprint() { - - // GIVEN - // WHEN - // THEN - XCTAssertNil(statusAlice.fingerprint(for: EncryptionSessionIdentifier( - domain: "example.com", - userId: "aa22", - clientId: "8899" - ))) - } -} - -// MARK: - Deletion - -extension EncryptionSessionsDirectoryTests { - - func testThatItDeletesASession() { - - // GIVEN - establishSessionBetweenAliceAndBob() - - // WHEN - statusAlice.delete(Person.Bob.identifier) - - // THEN - let cypherText = try? statusAlice.encrypt(Data("foo".utf8), for: Person.Bob.identifier) - XCTAssertNil(cypherText) - } - - func testThatItCanDeleteASessionThatDoesNotExist() { - - // GIVEN - - // WHEN - statusAlice.delete(hardcodedClientId) - - // THEN - // no crash - } -} - -// MARK: - Session cache management - -extension EncryptionSessionsDirectoryTests { - - func testThatCreatedSessionsAreNotSavedImmediately() { - - // GIVEN - - // WHEN - try! statusAlice.createClientSession(Person.Bob.identifier, base64PreKeyString: statusBob.generatePrekey(1)) - - // THEN - let statusAliceCopy = EncryptionSessionsDirectory( - generatingContext: contextAlice, - encryptionPayloadCache: Cache(maxCost: 1000, maxElementsCount: 100), - extensiveLoggingSessions: Set() - ) - statusAliceCopy.debug_disableContextValidityCheck = true - let cypher = try? statusAliceCopy.encrypt(Data("foo".utf8), for: Person.Bob.identifier) - XCTAssertNil(cypher) - } - - func testThatNewlyCreatedSessionsAreSavedWhenReleasingTheStatus() { - - // GIVEN - let plainText = Data("foo".utf8) - establishSessionFromAliceToBob() - - // WHEN - statusAlice = nil - - // THEN - let statusAliceCopy = EncryptionSessionsDirectory( - generatingContext: contextAlice, - encryptionPayloadCache: Cache(maxCost: 1000, maxElementsCount: 100), - extensiveLoggingSessions: Set() - ) - statusAliceCopy.debug_disableContextValidityCheck = true - let prekeyMessage = try! statusAliceCopy.encrypt(plainText, for: Person.Bob.identifier) - let decoded = try! statusBob.createClientSessionAndReturnPlaintext( - for: Person.Alice.identifier, - prekeyMessage: prekeyMessage - ) - XCTAssertEqual(plainText, decoded) - } - - func testThatNewlyCreatedSessionsAreNotSavedWhenDiscarding() { - - // GIVEN - establishSessionFromAliceToBob() - - // WHEN - statusAlice.discardCache() - statusAlice = nil - - // THEN - let statusAliceCopy = EncryptionSessionsDirectory( - generatingContext: contextAlice, - encryptionPayloadCache: Cache(maxCost: 1000, maxElementsCount: 100), - extensiveLoggingSessions: Set() - ) - statusAliceCopy.debug_disableContextValidityCheck = true - let cypher = try? statusAliceCopy.encrypt(Data("foo".utf8), for: Person.Bob.identifier) - XCTAssertNil(cypher) - } - - func testThatModifiedSessionsAreNotSavedWhenDiscarding() { - - // GIVEN - let plainText = Data("foo".utf8) - establishSessionFromAliceToBob() - let prekeyMessage = try! statusAlice.encrypt(plainText, for: Person.Bob.identifier) - _ = try! statusBob.createClientSessionAndReturnPlaintext( - for: Person.Alice.identifier, - prekeyMessage: prekeyMessage - ) - recreateStatuses() // force save - - // WHEN - let cypherText = try! statusAlice.encrypt(plainText, for: Person.Bob.identifier) - _ = try! statusBob.decrypt(cypherText, from: Person.Alice.identifier) - statusBob.discardCache() - statusBob = nil - - // THEN - let statusBobCopy = EncryptionSessionsDirectory( - generatingContext: contextBob, - encryptionPayloadCache: Cache(maxCost: 1000, maxElementsCount: 100), - extensiveLoggingSessions: Set() - ) - statusBobCopy.debug_disableContextValidityCheck = true - let decoded = try! statusBobCopy.decrypt(cypherText, from: Person.Alice.identifier) - XCTAssertEqual(decoded, plainText) - } - - func testThatItCanNotDecodeAfterDiscardingCache() { - - // GIVEN - establishSessionFromAliceToBob() - - // WHEN - statusAlice.discardCache() - - // THEN - XCTAssertFalse(checkThatAMessageCanBeSent(.Alice)) - } - - func testThatItDecodeFutureMessageAfterDiscardingCacheOnTheReceivingSide() { - - // GIVEN - establishSessionBetweenAliceAndBob() - checkThatAMessageCanBeSent(.Alice, saveReceiverCache: false) - - // WHEN - statusBob.discardCache() - - // THEN - XCTAssertNotNil(checkThatAMessageCanBeSent(.Alice)) - } - - func testThatItCanNotDecodeDuplicatedMessageIfTheCacheIsNotDiscarded() { - - // GIVEN - establishSessionBetweenAliceAndBob() - let plainText = Data("foo".utf8) - let cypherText = try! statusAlice.encrypt(plainText, for: Person.Bob.identifier) - _ = try! statusBob.decrypt(cypherText, from: Person.Alice.identifier) - - // WHEN - do { - _ = try statusBob.decrypt(cypherText, from: Person.Alice.identifier) - XCTFail("Should have failed") - return - } catch let err as CBoxResult where err == CBOX_DUPLICATE_MESSAGE { - // pass - } catch { - XCTFail("Wrong error") - } - } - - func testThatItCanNotDecodeDuplicatedMessageIfTheCacheIsNotDiscardedAndReportsTheCorrectErrorInObjC() { - - // GIVEN - establishSessionBetweenAliceAndBob() - let plainText = Data("foo".utf8) - let cypherText = try! statusAlice.encrypt(plainText, for: Person.Bob.identifier) - _ = try! statusBob.decrypt(cypherText, from: Person.Alice.identifier) - - // WHEN - do { - _ = try statusBob.decrypt(cypherText, from: Person.Alice.identifier) - XCTFail("Should have failed") - return - } catch let error as CBoxResult { - XCTAssertEqual(error, CBOX_DUPLICATE_MESSAGE) - } catch { - XCTFail() - } - } - - func testThatItCanDecodeDuplicatedMessageIfTheCacheIsDiscarded() { - - // GIVEN - establishSessionBetweenAliceAndBob() - let plainText = Data("foo".utf8) - let cypherText = try! statusAlice.encrypt(plainText, for: Person.Bob.identifier) - _ = try! statusBob.decrypt(cypherText, from: Person.Alice.identifier) - - // WHEN - statusBob.discardCache() - - // THEN - do { - _ = try statusBob.decrypt(cypherText, from: Person.Alice.identifier) - } catch { - XCTFail("Should decrypt") - } - } - - func testThatItCanNotDecodeDuplicatedMessageIfTheCacheIsCommitted() { - - // GIVEN - establishSessionBetweenAliceAndBob() - let plainText = Data("foo".utf8) - let cypherText = try! statusAlice.encrypt(plainText, for: Person.Bob.identifier) - _ = try! statusBob.decrypt(cypherText, from: Person.Alice.identifier) - - // WHEN - recreateStatuses() // force save - - // THEN - do { - _ = try statusBob.decrypt(cypherText, from: Person.Alice.identifier) - XCTFail("Should have failed") - return - } catch let err as CBoxResult where err == CBOX_DUPLICATE_MESSAGE { - // pass - } catch { - XCTFail("Wrong error") - } - } - - func testThatItCanDecodeAfterSavingCache() { - - // GIVEN - let plainText = Data("foo".utf8) - establishSessionFromAliceToBob() - - // WHEN - recreateStatuses() // force save - - // THEN - let prekeyMessage = try! statusAlice.encrypt(plainText, for: Person.Bob.identifier) - let decoded = try! statusBob.createClientSessionAndReturnPlaintext( - for: Person.Alice.identifier, - prekeyMessage: prekeyMessage - ) - XCTAssertEqual(decoded, plainText) - } - - func testThatItCanDecodeMultipleMessagesWithoutSaving() { - - // GIVEN - establishSessionBetweenAliceAndBob() - - // WHEN - checkThatAMessageCanBeSent(.Alice, saveReceiverCache: false) - - // THEN - XCTAssertTrue(checkThatAMessageCanBeSent(.Alice)) - XCTAssertTrue(checkThatAMessageCanBeSent(.Alice)) - XCTAssertTrue(checkThatAMessageCanBeSent(.Alice)) - } -} - -// MARK: - Session migration tests - -extension EncryptionSessionsDirectoryTests { - - func testThatItCanMigrateASessionAndReceive() { - - // GIVEN - let oldIdentifier = "aabbccdd" - bobIdentifierOverride = oldIdentifier - - establishSessionBetweenAliceAndBob() - checkThatAMessageCanBeSent(.Alice) - checkThatAMessageCanBeSent(.Bob) - - // WHEN - bobIdentifierOverride = nil - statusAlice.migrateSession(from: oldIdentifier, to: Person.Bob.identifier) - - // THEN - XCTAssertTrue(checkThatAMessageCanBeSent(.Bob)) - } - - func testThatItCanMigrateASessionAndSend() { - - // GIVEN - let oldIdentifier = "aabbccdd" - bobIdentifierOverride = oldIdentifier - - establishSessionBetweenAliceAndBob() - checkThatAMessageCanBeSent(.Alice) - checkThatAMessageCanBeSent(.Bob) - - // WHEN - bobIdentifierOverride = nil - statusAlice.migrateSession(from: oldIdentifier, to: Person.Bob.identifier) - - // THEN - XCTAssertTrue(checkThatAMessageCanBeSent(.Alice)) - } - - func testThatItWontMigrateIfNewSessionAlreadyExists() { - - // GIVEN - let oldIdentifier = "aabbccdd" - - establishSessionBetweenAliceAndBob() - checkThatAMessageCanBeSent(.Alice) - checkThatAMessageCanBeSent(.Bob) - - bobIdentifierOverride = oldIdentifier - establishSessionFromAliceToBob() - - // WHEN - bobIdentifierOverride = nil - statusAlice.migrateSession(from: oldIdentifier, to: Person.Bob.identifier) - - // THEN - XCTAssertTrue(checkThatAMessageCanBeSent(.Bob)) - } - - func testThatItWontMigrateIfOldSessionDoesNotExists() { - - // GIVEN - let oldIdentifier = "aabbccdd" - - establishSessionBetweenAliceAndBob() - checkThatAMessageCanBeSent(.Alice) - checkThatAMessageCanBeSent(.Bob) - - // WHEN - statusAlice.migrateSession(from: oldIdentifier, to: Person.Bob.identifier) - - // THEN - XCTAssertTrue(checkThatAMessageCanBeSent(.Bob)) - } -} - -// MARK: - Extended logging - -extension EncryptionSessionsDirectoryTests { - - func testThatItLogsEncryptionWhenExtendedLoggingIsSet() { - - // GIVEN - recreateAliceStatus(extendedLoggingSession: Set([Person.Bob.identifier])) - let plainText = Data("foo".utf8) - establishSessionFromAliceToBob() - let logExpectation = expectation(description: "Encrypting") - - // EXPECT - let token = ZMSLog.addEntryHook { level, tag, entry, _ in - if level == .public, - tag == "cryptobox", - entry.text.contains("encrypted to cyphertext: cyphertext") { - logExpectation.fulfill() - } - } - - // WHEN - _ = try! statusAlice.encrypt(plainText, for: Person.Bob.identifier) - - // THEN - waitForExpectations(timeout: 1) - - // AFTER - ZMSLog.removeLogHook(token: token) - } - - func testThatItDoesNotLogEncryptionWhenExtendedLoggingIsNotSet() { - - // GIVEN - // set logging for a different identifier - let wrongIdentifier = EncryptionSessionIdentifier(domain: "example.com", userId: "foo", clientId: "bar") - recreateAliceStatus(extendedLoggingSession: Set([wrongIdentifier])) - - let plainText = Data("foo".utf8) - establishSessionFromAliceToBob() - - // EXPECT - let token = ZMSLog.addEntryHook { _, _, _, _ in - XCTFail("Should not have logged") - } - - // WHEN - _ = try! statusAlice.encrypt(plainText, for: Person.Bob.identifier) - - // AFTER - ZMSLog.removeLogHook(token: token) - } - - func testThatItLogsDecryptionWhenExtendedLoggingIsSet_prekeyMessage() { - - // GIVEN - recreateBobStatus(extendedLoggingSession: Set([Person.Alice.identifier])) - let plainText = Data("foo".utf8) - establishSessionFromAliceToBob() - let logExpectation = expectation(description: "Encrypting") - let prekeyMessage = try! statusAlice.encrypt(plainText, for: Person.Bob.identifier) - - // EXPECT - let token = ZMSLog.addEntryHook { level, tag, entry, _ in - if level == .public, - tag == "cryptobox", - entry.text.contains("decrypting prekey cyphertext:") { - logExpectation.fulfill() - } - } - - // WHEN - _ = try! statusBob.createClientSessionAndReturnPlaintext( - for: Person.Alice.identifier, - prekeyMessage: prekeyMessage - ) - - // THEN - waitForExpectations(timeout: 1) - - // AFTER - ZMSLog.removeLogHook(token: token) - } - - func testThatItLogsDecryptionWhenExtendedLoggingIsSet_nonPrekeyMessage() { - - // GIVEN - - let plainText = Data("foo".utf8) - establishSessionBetweenAliceAndBob() - recreateBobStatus(extendedLoggingSession: Set([Person.Alice.identifier])) - let logExpectation = expectation(description: "Encrypting") - let message = try! statusAlice.encrypt(plainText, for: Person.Bob.identifier) - - // EXPECT - let token = ZMSLog.addEntryHook { level, tag, entry, _ in - if level == .public, - tag == "cryptobox", - entry.text.contains("decrypting cyphertext:") { - logExpectation.fulfill() - } - } - - // WHEN - _ = try! statusBob.decrypt(message, from: Person.Alice.identifier) - - // THEN - waitForExpectations(timeout: 1) - - // AFTER - ZMSLog.removeLogHook(token: token) - } - - func testThatItDoesNotLogDecryptionWhenExtendedLoggingIsNotSet() { - - // GIVEN - // set logging for a different identifier - let wrongIdentifier = EncryptionSessionIdentifier(domain: "example.com", userId: "foo", clientId: "bar") - recreateBobStatus(extendedLoggingSession: Set([wrongIdentifier])) - - let plainText = Data("foo".utf8) - establishSessionFromAliceToBob() - - let prekeyMessage = try! statusAlice.encrypt(plainText, for: Person.Bob.identifier) - - // EXPECT - let token = ZMSLog.addEntryHook { _, _, _, _ in - XCTFail("Should not have logged") - } - - // WHEN - _ = try! statusBob.createClientSessionAndReturnPlaintext( - for: Person.Alice.identifier, - prekeyMessage: prekeyMessage - ) - - // AFTER - ZMSLog.removeLogHook(token: token) - } -} - -// MARK: - Helpers - -/// Custom session identifier for Bob -private var bobIdentifierOverride: String? - -extension EncryptionSessionsDirectoryTests { - - /// Recreate the statuses, reloading from disk. This also forces a save of the previous - /// statuses, if any. - func recreateStatuses( - only: Person? = nil - ) { - if only == nil || only == .Alice { - recreateAliceStatus() - } - if only == nil || only == .Bob { - recreateBobStatus() - } - } - - func recreateAliceStatus( - extendedLoggingSession: Set = Set() - ) { - statusAlice = EncryptionSessionsDirectory( - generatingContext: contextAlice, - encryptionPayloadCache: Cache(maxCost: 1000, maxElementsCount: 100), - extensiveLoggingSessions: extendedLoggingSession - ) - statusAlice.debug_disableContextValidityCheck = true - } - - func recreateBobStatus( - extendedLoggingSession: Set = Set() - ) { - statusBob = EncryptionSessionsDirectory( - generatingContext: contextBob, - encryptionPayloadCache: Cache(maxCost: 1000, maxElementsCount: 100), - extensiveLoggingSessions: extendedLoggingSession - ) - statusBob.debug_disableContextValidityCheck = true - } - - /// Sends a prekey message from Alice to Bob, decrypts it on Bob's side, and save both - func establishSessionBetweenAliceAndBob() { - establishSessionFromAliceToBob() - let prekeyMessage = try! statusAlice.encrypt(Data("foo".utf8), for: Person.Bob.identifier) - _ = try! statusBob.createClientSessionAndReturnPlaintext( - for: Person.Alice.identifier, - prekeyMessage: prekeyMessage - ) - - /// This will force commit - recreateStatuses() - } - - /// Creates a client session from Alice to Bob - func establishSessionFromAliceToBob() { - let prekey = try! statusBob.generatePrekey(2) - try! statusAlice.createClientSession(Person.Bob.identifier, base64PreKeyString: prekey) - } - - enum Person { - case Alice - case Bob - - var identifier: EncryptionSessionIdentifier { - switch self { - case .Alice: - EncryptionSessionIdentifier(domain: "example.com", userId: "234ab2e4", clientId: "c45-a11c30") - case .Bob: - EncryptionSessionIdentifier(fromLegacyV1Identifier: bobIdentifierOverride ?? "a34affe3366-b0b0b0b") - } - } - - var other: Person { - switch self { - case .Alice: - .Bob - case .Bob: - .Alice - } - } - } - - /// Checks if a person already decrypted a message - /// Reverts the session after performing the check - /// Will only work after after calling `establishSessionBetweenAliceAndBob` - func checkIfPersonAlreadyDecryptedMessage(_ person: Person, message: Data) -> Bool { - let clientId = person.identifier - let status = person == .Alice ? statusAlice : statusBob - guard (try? status?.decrypt(message, from: clientId)) != nil else { - return true - } - status?.discardCache() - return false - } - - /// Checks if a message can be encrypted and successfully decrypted - /// by the other person - /// - note: it does commit the session cache - @discardableResult - func checkThatAMessageCanBeSent(_ from: Person, saveReceiverCache: Bool = true) -> Bool { - let senderId = from.identifier - let receiverId = from.other.identifier - - let status1 = from == .Alice ? statusAlice : statusBob - let status2 = from == .Alice ? statusBob : statusAlice - - defer { - self.recreateStatuses(only: from) - if saveReceiverCache { - self.recreateStatuses(only: from.other) - } - } - - let plainText = Data("निर्वाण".utf8) - do { - let cypherText = try status1?.encrypt(plainText, for: receiverId) - let decoded = try status2?.decrypt(cypherText!, from: senderId) - return decoded == plainText - } catch { - return false - } - } -} diff --git a/wire-ios-cryptobox/WireCryptoboxTests/GenericHashBuilderTests.swift b/wire-ios-cryptobox/WireCryptoboxTests/GenericHashBuilderTests.swift deleted file mode 100644 index 4cce4537a1b..00000000000 --- a/wire-ios-cryptobox/WireCryptoboxTests/GenericHashBuilderTests.swift +++ /dev/null @@ -1,85 +0,0 @@ -// -// Wire -// Copyright (C) 2025 Wire Swiss GmbH -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see http://www.gnu.org/licenses/. -// - -import XCTest -@testable import WireCryptobox - -final class GenericHashBuilderTests: XCTestCase { - func testThatItHashesTheData() { - // GIVEN - let data = Data("some data".utf8) - // WHEN - let builder = GenericHashBuilder() - builder.append(data) - let hash = builder.build() - // THEN - let genericHash = GenericHash(value: 108_806_884_620_190_685) - XCTAssertEqual(hash, genericHash) - XCTAssertEqual(hash.hashValue, genericHash.hashValue) - } - - func testThatDifferentDataHasDifferentHash() { - // GIVEN - let data = Data("some data".utf8) - let otherData = Data("some other data".utf8) - // WHEN - let builder = GenericHashBuilder() - builder.append(data) - let hash = builder.build() - - let otherBuilder = GenericHashBuilder() - otherBuilder.append(otherData) - let otherHash = otherBuilder.build() - // THEN - XCTAssertNotEqual(hash.hashValue, otherHash.hashValue) - } - - func testThatSameDataHasSameHash() { - // GIVEN - let data = Data("some data".utf8) - let otherData = Data("some data".utf8) - // WHEN - let builder = GenericHashBuilder() - builder.append(data) - let hash = builder.build() - - let otherBuilder = GenericHashBuilder() - otherBuilder.append(otherData) - let otherHash = otherBuilder.build() - // THEN - XCTAssertEqual(hash.hashValue, otherHash.hashValue) - } - - func testThatDataCanBeAppended() { - // GIVEN - let data = Data("some data".utf8) - let otherData1 = Data("some ".utf8) - let otherData2 = Data("data".utf8) - // WHEN - let builder = GenericHashBuilder() - builder.append(data) - let hash = builder.build() - - let otherBuilder = GenericHashBuilder() - otherBuilder.append(otherData1) - otherBuilder.append(otherData2) - let otherHash = otherBuilder.build() - // THEN - XCTAssertEqual(hash.hashValue, otherHash.hashValue) - } -} diff --git a/wire-ios-cryptobox/WireCryptoboxTests/Info.plist b/wire-ios-cryptobox/WireCryptoboxTests/Info.plist deleted file mode 100644 index ba72822e872..00000000000 --- a/wire-ios-cryptobox/WireCryptoboxTests/Info.plist +++ /dev/null @@ -1,24 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - BNDL - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 1 - - diff --git a/wire-ios-cryptobox/WireCryptoboxTests/TestHelper.swift b/wire-ios-cryptobox/WireCryptoboxTests/TestHelper.swift deleted file mode 100644 index 295dcaca596..00000000000 --- a/wire-ios-cryptobox/WireCryptoboxTests/TestHelper.swift +++ /dev/null @@ -1,39 +0,0 @@ -// -// Wire -// Copyright (C) 2025 Wire Swiss GmbH -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see http://www.gnu.org/licenses/. -// - -import Foundation -import WireCryptobox - -/// sample client ID -let hardcodedClientId = EncryptionSessionIdentifier(domain: "example.com", userId: "1e9b4e18", clientId: "7a9eb715") - -/// sample prekey -let hardcodedPrekey = - "pQABAQUCoQBYIEIir0myj5MJTvs19t585RfVi1dtmL2nJsImTaNXszRwA6EAoQBYIGpa1sQFpCugwFJRfD18d9+TNJN2ZL3H0Mfj/0qZw0ruBPY=" - -/// Creates a temporary folder and returns its URL -func createTempFolder() -> URL { - let url = URL(fileURLWithPath: [NSTemporaryDirectory(), UUID().uuidString].joined(separator: "/")) - try! FileManager.default.createDirectory(at: url, withIntermediateDirectories: true, attributes: [:]) - return url -} - -func createEncryptionContext() -> EncryptionContext { - let folder = createTempFolder() - return EncryptionContext(path: folder) -} diff --git a/wire-ios-cryptobox/WireCryptoboxTests/TestPlans/AllTests.xctestplan b/wire-ios-cryptobox/WireCryptoboxTests/TestPlans/AllTests.xctestplan deleted file mode 100644 index c4a0defe0c1..00000000000 --- a/wire-ios-cryptobox/WireCryptoboxTests/TestPlans/AllTests.xctestplan +++ /dev/null @@ -1,37 +0,0 @@ -{ - "configurations" : [ - { - "id" : "72E20F8C-AC1C-449E-A381-FF60B98DC3B7", - "name" : "Configuration 1", - "options" : { - - } - } - ], - "defaultOptions" : { - "environmentVariableEntries" : [ - { - "key" : "CI", - "value" : "${CI}" - } - ], - "language" : "en", - "region" : "DE", - "targetForVariableExpansion" : { - "containerPath" : "container:WireCryptobox.xcodeproj", - "identifier" : "BA7EF9681B7109B600204A8E", - "name" : "WireCryptoboxTests" - }, - "testExecutionOrdering" : "random" - }, - "testTargets" : [ - { - "target" : { - "containerPath" : "container:WireCryptobox.xcodeproj", - "identifier" : "BA7EF9681B7109B600204A8E", - "name" : "WireCryptoboxTests" - } - } - ], - "version" : 1 -} diff --git a/wire-ios-cryptobox/test-host/AppDelegate.h b/wire-ios-cryptobox/test-host/AppDelegate.h deleted file mode 100644 index 8880062a31d..00000000000 --- a/wire-ios-cryptobox/test-host/AppDelegate.h +++ /dev/null @@ -1,27 +0,0 @@ -// -// Wire -// Copyright (C) 2025 Wire Swiss GmbH -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see http://www.gnu.org/licenses/. -// - -#import - -@interface AppDelegate : UIResponder - -@property (strong, nonatomic) UIWindow *window; - - -@end - diff --git a/wire-ios-cryptobox/test-host/AppDelegate.m b/wire-ios-cryptobox/test-host/AppDelegate.m deleted file mode 100644 index 24341339c41..00000000000 --- a/wire-ios-cryptobox/test-host/AppDelegate.m +++ /dev/null @@ -1,55 +0,0 @@ -// -// Wire -// Copyright (C) 2025 Wire Swiss GmbH -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see http://www.gnu.org/licenses/. -// - -#import "AppDelegate.h" - -@interface AppDelegate () - -@end - -@implementation AppDelegate - - -- (BOOL)application:(UIApplication * __unused)application didFinishLaunchingWithOptions:(NSDictionary * __unused)launchOptions { - // Override point for customization after application launch. - return YES; -} - -- (void)applicationWillResignActive:(UIApplication * __unused)application { - // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. - // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. -} - -- (void)applicationDidEnterBackground:(UIApplication * __unused)application { - // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. - // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. -} - -- (void)applicationWillEnterForeground:(UIApplication * __unused)application { - // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. -} - -- (void)applicationDidBecomeActive:(UIApplication * __unused)application { - // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. -} - -- (void)applicationWillTerminate:(UIApplication * __unused)application { - // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. -} - -@end diff --git a/wire-ios-cryptobox/test-host/Assets.xcassets/AppIcon.appiconset/Contents.json b/wire-ios-cryptobox/test-host/Assets.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index 1d060ed2882..00000000000 --- a/wire-ios-cryptobox/test-host/Assets.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,93 +0,0 @@ -{ - "images" : [ - { - "idiom" : "iphone", - "size" : "20x20", - "scale" : "2x" - }, - { - "idiom" : "iphone", - "size" : "20x20", - "scale" : "3x" - }, - { - "idiom" : "iphone", - "size" : "29x29", - "scale" : "2x" - }, - { - "idiom" : "iphone", - "size" : "29x29", - "scale" : "3x" - }, - { - "idiom" : "iphone", - "size" : "40x40", - "scale" : "2x" - }, - { - "idiom" : "iphone", - "size" : "40x40", - "scale" : "3x" - }, - { - "idiom" : "iphone", - "size" : "60x60", - "scale" : "2x" - }, - { - "idiom" : "iphone", - "size" : "60x60", - "scale" : "3x" - }, - { - "idiom" : "ipad", - "size" : "20x20", - "scale" : "1x" - }, - { - "idiom" : "ipad", - "size" : "20x20", - "scale" : "2x" - }, - { - "idiom" : "ipad", - "size" : "29x29", - "scale" : "1x" - }, - { - "idiom" : "ipad", - "size" : "29x29", - "scale" : "2x" - }, - { - "idiom" : "ipad", - "size" : "40x40", - "scale" : "1x" - }, - { - "idiom" : "ipad", - "size" : "40x40", - "scale" : "2x" - }, - { - "idiom" : "ipad", - "size" : "76x76", - "scale" : "1x" - }, - { - "idiom" : "ipad", - "size" : "76x76", - "scale" : "2x" - }, - { - "idiom" : "ipad", - "size" : "83.5x83.5", - "scale" : "2x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/wire-ios-cryptobox/test-host/Base.lproj/Main.storyboard b/wire-ios-cryptobox/test-host/Base.lproj/Main.storyboard deleted file mode 100644 index f56d2f3bb56..00000000000 --- a/wire-ios-cryptobox/test-host/Base.lproj/Main.storyboard +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/wire-ios-cryptobox/test-host/Info.plist b/wire-ios-cryptobox/test-host/Info.plist deleted file mode 100644 index fa00b2c967b..00000000000 --- a/wire-ios-cryptobox/test-host/Info.plist +++ /dev/null @@ -1,45 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - APPL - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 1 - LSRequiresIPhoneOS - - UIMainStoryboardFile - Main - UIRequiredDeviceCapabilities - - armv7 - - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - - diff --git a/wire-ios-cryptobox/test-host/ViewController.h b/wire-ios-cryptobox/test-host/ViewController.h deleted file mode 100644 index 7a8a843211c..00000000000 --- a/wire-ios-cryptobox/test-host/ViewController.h +++ /dev/null @@ -1,25 +0,0 @@ -// -// Wire -// Copyright (C) 2025 Wire Swiss GmbH -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see http://www.gnu.org/licenses/. -// - -#import - -@interface ViewController : UIViewController - - -@end - diff --git a/wire-ios-cryptobox/test-host/ViewController.m b/wire-ios-cryptobox/test-host/ViewController.m deleted file mode 100644 index f2c0532ab70..00000000000 --- a/wire-ios-cryptobox/test-host/ViewController.m +++ /dev/null @@ -1,37 +0,0 @@ -// -// Wire -// Copyright (C) 2025 Wire Swiss GmbH -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see http://www.gnu.org/licenses/. -// - -#import "ViewController.h" - -@interface ViewController () - -@end - -@implementation ViewController - -- (void)viewDidLoad { - [super viewDidLoad]; - // Do any additional setup after loading the view, typically from a nib. -} - -- (void)didReceiveMemoryWarning { - [super didReceiveMemoryWarning]; - // Dispose of any resources that can be recreated. -} - -@end diff --git a/wire-ios-cryptobox/test-host/main.m b/wire-ios-cryptobox/test-host/main.m deleted file mode 100644 index 49fbe4b791a..00000000000 --- a/wire-ios-cryptobox/test-host/main.m +++ /dev/null @@ -1,26 +0,0 @@ -// -// Wire -// Copyright (C) 2025 Wire Swiss GmbH -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see http://www.gnu.org/licenses/. -// - -#import -#import "AppDelegate.h" - -int main(int argc, char * argv[]) { - @autoreleasepool { - return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); - } -} diff --git a/wire-ios-data-model/Source/ManagedObjectContext/NSManagedObjectContext+EncryptionAtRest.swift b/wire-ios-data-model/Source/ManagedObjectContext/NSManagedObjectContext+EncryptionAtRest.swift index 8c6bb9959e3..65c4aa73029 100644 --- a/wire-ios-data-model/Source/ManagedObjectContext/NSManagedObjectContext+EncryptionAtRest.swift +++ b/wire-ios-data-model/Source/ManagedObjectContext/NSManagedObjectContext+EncryptionAtRest.swift @@ -18,7 +18,6 @@ import Foundation import WireCrypto -import WireCryptobox import WireLogging extension Sequence where Element: NSManagedObject { @@ -203,7 +202,7 @@ extension NSManagedObjectContext { case missingDatabaseKey case missingContextData - case cryptobox(error: ChaCha20Poly1305.AEADEncryption.EncryptionError) + case crypto(error: ChaCha20Poly1305.AEADEncryption.EncryptionError) var errorDescription: String? { switch self { @@ -213,7 +212,7 @@ extension NSManagedObjectContext { case .missingContextData: "Couldn't obtain context data." - case let .cryptobox(error): + case let .crypto(error): error.errorDescription } } @@ -247,7 +246,7 @@ extension NSManagedObjectContext { ) return (ciphertext, nonce) } catch let error as ChaCha20Poly1305.AEADEncryption.EncryptionError { - throw EncryptionError.cryptobox(error: error) + throw EncryptionError.crypto(error: error) } } @@ -283,7 +282,7 @@ extension NSManagedObjectContext { key: key._storage ) } catch let error as ChaCha20Poly1305.AEADEncryption.EncryptionError { - throw EncryptionError.cryptobox(error: error) + throw EncryptionError.crypto(error: error) } } diff --git a/wire-ios-data-model/Source/ManagedObjectContext/NSManagedObjectContext+zmessaging.m b/wire-ios-data-model/Source/ManagedObjectContext/NSManagedObjectContext+zmessaging.m index 055fb4964fe..b4c975e00c8 100644 --- a/wire-ios-data-model/Source/ManagedObjectContext/NSManagedObjectContext+zmessaging.m +++ b/wire-ios-data-model/Source/ManagedObjectContext/NSManagedObjectContext+zmessaging.m @@ -17,7 +17,6 @@ // @import UIKit; -@import WireCryptobox; @import WireUtilities; #import "NSManagedObjectContext+zmessaging-Internal.h" diff --git a/wire-ios-data-model/Source/Model/Conversation/ZMConversation+SecurityLevel.swift b/wire-ios-data-model/Source/Model/Conversation/ZMConversation+SecurityLevel.swift index 6a38f0c0bab..c3db51fc2e5 100644 --- a/wire-ios-data-model/Source/Model/Conversation/ZMConversation+SecurityLevel.swift +++ b/wire-ios-data-model/Source/Model/Conversation/ZMConversation+SecurityLevel.swift @@ -19,7 +19,6 @@ import Foundation import GenericMessageProtocol import WireCoreCrypto -import WireCryptobox import WireLogging @objc diff --git a/wire-ios-data-model/Source/Model/Conversation/ZMConversation.m b/wire-ios-data-model/Source/Model/Conversation/ZMConversation.m index 6f4d40047f6..34aa909b031 100644 --- a/wire-ios-data-model/Source/Model/Conversation/ZMConversation.m +++ b/wire-ios-data-model/Source/Model/Conversation/ZMConversation.m @@ -20,7 +20,6 @@ @import WireImages; @import WireUtilities; @import WireTransport; -@import WireCryptobox; @import MobileCoreServices; @import WireImages; diff --git a/wire-ios-data-model/Source/Model/Message/ConversationMessage+Deletion.swift b/wire-ios-data-model/Source/Model/Message/ConversationMessage+Deletion.swift index 6ee9189ca8a..4a6d2a26125 100644 --- a/wire-ios-data-model/Source/Model/Message/ConversationMessage+Deletion.swift +++ b/wire-ios-data-model/Source/Model/Message/ConversationMessage+Deletion.swift @@ -18,7 +18,6 @@ import Foundation import GenericMessageProtocol -import WireCryptobox extension ZMConversation { static func appendHideMessageToSelfConversation(_ message: ZMMessage) throws { diff --git a/wire-ios-data-model/Source/Model/Message/ZMClientMessage+Encryption.swift b/wire-ios-data-model/Source/Model/Message/ZMClientMessage+Encryption.swift index 110248b4788..2bf67f245be 100644 --- a/wire-ios-data-model/Source/Model/Message/ZMClientMessage+Encryption.swift +++ b/wire-ios-data-model/Source/Model/Message/ZMClientMessage+Encryption.swift @@ -18,7 +18,6 @@ import Foundation import GenericMessageProtocol -import WireCryptobox import WireLogging private var zmLog = ZMSLog(tag: "message encryption") diff --git a/wire-ios-data-model/Source/Model/Message/ZMGenericMessageData.swift b/wire-ios-data-model/Source/Model/Message/ZMGenericMessageData.swift index 58f66d43a74..af0d3414e09 100644 --- a/wire-ios-data-model/Source/Model/Message/ZMGenericMessageData.swift +++ b/wire-ios-data-model/Source/Model/Message/ZMGenericMessageData.swift @@ -18,7 +18,6 @@ import Foundation import GenericMessageProtocol -import WireCryptobox import WireLogging @objc(ZMGenericMessageData) diff --git a/wire-ios-data-model/Source/Model/Message/ZMMessage.m b/wire-ios-data-model/Source/Model/Message/ZMMessage.m index 4695735f100..2c4535183c4 100644 --- a/wire-ios-data-model/Source/Model/Message/ZMMessage.m +++ b/wire-ios-data-model/Source/Model/Message/ZMMessage.m @@ -31,8 +31,6 @@ #import "ZMUpdateEvent+WireDataModel.h" #import -#import - static NSString *ZMLogTag ZM_UNUSED = @"ephemeral"; @@ -770,22 +768,6 @@ - (void)updateNeedsUpdatingUsersIfNeeded } } -- (BOOL)isDecryptionErrorRecoverable { - if (self.decryptionErrorCode == nil) { - return NO; - } - - NSInteger errorCode = self.decryptionErrorCode.integerValue; - - if (errorCode == CBOX_TOO_DISTANT_FUTURE || - errorCode == CBOX_DEGENERATED_KEY || - errorCode == CBOX_PREKEY_NOT_FOUND) { - return YES; - } - - return NO; -} - + (ZMSystemMessageType)systemMessageTypeFromUpdateEvent:(ZMUpdateEvent *)updateEvent; { switch (updateEvent.type) { diff --git a/wire-ios-data-model/Source/Model/User/ZMUser.m b/wire-ios-data-model/Source/Model/User/ZMUser.m index 701ac7cdd66..d7425534496 100644 --- a/wire-ios-data-model/Source/Model/User/ZMUser.m +++ b/wire-ios-data-model/Source/Model/User/ZMUser.m @@ -18,7 +18,6 @@ @import WireImages; @import WireUtilities; -@import WireCryptobox; @import WireTransport; @import Foundation; diff --git a/wire-ios-data-model/Source/Model/UserClient/UserClient.swift b/wire-ios-data-model/Source/Model/UserClient/UserClient.swift index cef1abba94f..5524c07711d 100644 --- a/wire-ios-data-model/Source/Model/UserClient/UserClient.swift +++ b/wire-ios-data-model/Source/Model/UserClient/UserClient.swift @@ -18,7 +18,6 @@ import CoreLocation import Foundation -import WireCryptobox import WireLogging import WireUtilities diff --git a/wire-ios-data-model/Source/Public/DraftMessage.swift b/wire-ios-data-model/Source/Public/DraftMessage.swift index 768d37d6a28..a595749516c 100644 --- a/wire-ios-data-model/Source/Public/DraftMessage.swift +++ b/wire-ios-data-model/Source/Public/DraftMessage.swift @@ -17,7 +17,6 @@ // import Foundation -import WireCryptobox /// This object holds information about a message draft that has not yet been sent /// by the user but was put into the input field. diff --git a/wire-ios-data-model/Tests/Source/Model/Conversation/ZMConversationTests+SecurityLevel.swift b/wire-ios-data-model/Tests/Source/Model/Conversation/ZMConversationTests+SecurityLevel.swift index 19e41b9695e..13c6001474b 100644 --- a/wire-ios-data-model/Tests/Source/Model/Conversation/ZMConversationTests+SecurityLevel.swift +++ b/wire-ios-data-model/Tests/Source/Model/Conversation/ZMConversationTests+SecurityLevel.swift @@ -490,30 +490,6 @@ final class ZMConversationTests_SecurityLevel: ZMConversationTestsBase { XCTAssertEqual(lastMessage.systemMessageType, ZMSystemMessageType.decryptionFailed_RemoteIdentityChanged) } - func testThatItAppendsASystemMessageOfGeneralTypeForCBErrorCodeInvalidMessage() { - // given - let conversation = ZMConversation.insertNewObject(in: uiMOC) - conversation.conversationType = .group - let user = ZMUser.insertNewObject(in: uiMOC) - user.name = "Fancy One" - let decryptionError = CBOX_INVALID_MESSAGE - - // when - conversation.appendDecryptionFailedSystemMessage( - at: Date(), - sender: user, - client: nil, - error: .Other(UInt16(decryptionError.rawValue)) - ) - - // then - guard let lastMessage = conversation.lastMessage as? ZMSystemMessage else { - return XCTFail() - } - XCTAssertEqual(lastMessage.systemMessageType, ZMSystemMessageType.decryptionFailed) - XCTAssertEqual(lastMessage.decryptionErrorCode?.intValue, Int(decryptionError.rawValue)) - } - func testThatAConversationIsNotTrustedIfItHasNoOtherParticipants() { // GIVEN let conversation = ZMConversation.insertNewObject(in: uiMOC) diff --git a/wire-ios-data-model/Tests/Source/Model/Messages/ZMClientMessageTests+Ephemeral.swift b/wire-ios-data-model/Tests/Source/Model/Messages/ZMClientMessageTests+Ephemeral.swift index ba3483a5a4c..1be6874fad0 100644 --- a/wire-ios-data-model/Tests/Source/Model/Messages/ZMClientMessageTests+Ephemeral.swift +++ b/wire-ios-data-model/Tests/Source/Model/Messages/ZMClientMessageTests+Ephemeral.swift @@ -18,7 +18,6 @@ import Foundation import GenericMessageProtocol -import WireCryptobox import WireLinkPreview @testable import WireDataModel diff --git a/wire-ios-data-model/Tests/Source/Model/Messages/ZMMessageTests+SystemMessages.swift b/wire-ios-data-model/Tests/Source/Model/Messages/ZMMessageTests+SystemMessages.swift index be3400daf6f..867d6b73974 100644 --- a/wire-ios-data-model/Tests/Source/Model/Messages/ZMMessageTests+SystemMessages.swift +++ b/wire-ios-data-model/Tests/Source/Model/Messages/ZMMessageTests+SystemMessages.swift @@ -21,60 +21,6 @@ import XCTest class ZMMessageTests_SystemMessages: BaseZMMessageTests { - func testThatOnlyRecoverableDecryptionErrorsAreReportedAsRecoverable() throws { - let allEncryptionErrors = [ - CBOX_STORAGE_ERROR, - CBOX_SESSION_NOT_FOUND, - CBOX_DECODE_ERROR, - CBOX_REMOTE_IDENTITY_CHANGED, - CBOX_INVALID_SIGNATURE, - CBOX_INVALID_MESSAGE, - CBOX_DUPLICATE_MESSAGE, - CBOX_TOO_DISTANT_FUTURE, - CBOX_OUTDATED_MESSAGE, - CBOX_UTF8_ERROR, - CBOX_NUL_ERROR, - CBOX_ENCODE_ERROR, - CBOX_IDENTITY_ERROR, - CBOX_PREKEY_NOT_FOUND, - CBOX_PANIC, - CBOX_INIT_ERROR, - CBOX_DEGENERATED_KEY - ] - - let recoverableEncryptionErrors = [ - CBOX_TOO_DISTANT_FUTURE, - CBOX_DEGENERATED_KEY, - CBOX_PREKEY_NOT_FOUND - ] - - for encryptionError in allEncryptionErrors { - assertDecryptionErrorIsReportedAsRecoverable( - encryptionError, - recoverable: recoverableEncryptionErrors.contains(encryptionError) - ) - } - } - - private func assertDecryptionErrorIsReportedAsRecoverable( - _ decryptionError: CBoxResult, - recoverable: Bool, - file: StaticString = #filePath, - line: UInt = #line - ) { - // given - let systemMessage = ZMSystemMessage(nonce: UUID(), managedObjectContext: uiMOC) - systemMessage.systemMessageType = .decryptionFailed - systemMessage.decryptionErrorCode = NSNumber(value: decryptionError.rawValue) - - // then - XCTAssertEqual(systemMessage.isDecryptionErrorRecoverable, recoverable, file: file, line: line) - } - -} - -extension ZMMessageTests_SystemMessages { - func testThatItGeneratesTheCorrectSystemMessageTypesFromUpdateEvents() { // expect a message checkThatUpdateEventTypeGeneratesSystemMessage( diff --git a/wire-ios-data-model/Tests/Source/Model/ZMBaseManagedObjectTest.h b/wire-ios-data-model/Tests/Source/Model/ZMBaseManagedObjectTest.h index 976ff13e93a..f6eccbcadb1 100644 --- a/wire-ios-data-model/Tests/Source/Model/ZMBaseManagedObjectTest.h +++ b/wire-ios-data-model/Tests/Source/Model/ZMBaseManagedObjectTest.h @@ -31,7 +31,6 @@ @protocol ZMObjectStrategyDirectory; @class ZMAssetClientMessage; -@import WireCryptobox; @import WireImages; @class UserClient; diff --git a/wire-ios-data-model/WireDataModel.xcodeproj/project.pbxproj b/wire-ios-data-model/WireDataModel.xcodeproj/project.pbxproj index 75d389314d4..22599629f8a 100644 --- a/wire-ios-data-model/WireDataModel.xcodeproj/project.pbxproj +++ b/wire-ios-data-model/WireDataModel.xcodeproj/project.pbxproj @@ -537,7 +537,6 @@ EE7A90F02B21E29E00B58E84 /* OneOnOneMigratorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE7A90EF2B21E29E00B58E84 /* OneOnOneMigratorTests.swift */; }; EE84227028EC353900B80FE5 /* MLSActionExecutorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE84226F28EC353900B80FE5 /* MLSActionExecutorTests.swift */; }; EE85C8B22B557C3D00D3182D /* DatabaseMigrationTests+OneOnOneConversation.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE85C8B12B557C3D00D3182D /* DatabaseMigrationTests+OneOnOneConversation.swift */; }; - EE8DA9672954A02B00F58B79 /* WireCryptobox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EE8DA9662954A02B00F58B79 /* WireCryptobox.framework */; }; EE8DA96A2954A03100F58B79 /* WireTransport.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EE8DA9692954A03100F58B79 /* WireTransport.framework */; }; EE8DA96D2954A03800F58B79 /* WireLinkPreview.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EE8DA96C2954A03800F58B79 /* WireLinkPreview.framework */; }; EE8DA9702954A03E00F58B79 /* WireImages.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EE8DA96F2954A03E00F58B79 /* WireImages.framework */; }; @@ -1576,7 +1575,6 @@ buildActionMask = 2147483647; files = ( 34DC44B52E01C210004D5DD5 /* WireNetwork in Frameworks */, - EE8DA9672954A02B00F58B79 /* WireCryptobox.framework in Frameworks */, CBD35F2C2D09EBB20080DA37 /* WireCrypto in Frameworks */, 1657FA9A2D9C2EB800A7B337 /* WireCoreCryptoUniffi in Frameworks */, 591B6E4C2C8B09C6009F8A7B /* CoreData.framework in Frameworks */, diff --git a/wire-ios-mocktransport/Source/Clients and OTR/MockPendingLegalHoldClient.swift b/wire-ios-mocktransport/Source/Clients and OTR/MockPendingLegalHoldClient.swift index 490855c42a3..023215f0a8a 100644 --- a/wire-ios-mocktransport/Source/Clients and OTR/MockPendingLegalHoldClient.swift +++ b/wire-ios-mocktransport/Source/Clients and OTR/MockPendingLegalHoldClient.swift @@ -83,7 +83,7 @@ public extension MockUser { forEntityName: "PreKey", into: managedObjectContext ) as! MockPreKey - mockLastPrekey.identifier = Int(CBOX_LAST_PREKEY_ID) + mockLastPrekey.identifier = Int(UInt16.max) mockLastPrekey.value = lastPrekey pendingClient.lastPrekey = mockLastPrekey diff --git a/wire-ios-mocktransport/Source/Clients and OTR/MockPreKey.h b/wire-ios-mocktransport/Source/Clients and OTR/MockPreKey.h index 12fe57bb7ed..db5d68639af 100644 --- a/wire-ios-mocktransport/Source/Clients and OTR/MockPreKey.h +++ b/wire-ios-mocktransport/Source/Clients and OTR/MockPreKey.h @@ -17,7 +17,6 @@ // @import CoreData; -@import WireCryptobox; @class MockUserClient; diff --git a/wire-ios-mocktransport/Source/Clients and OTR/MockUserClient.swift b/wire-ios-mocktransport/Source/Clients and OTR/MockUserClient.swift index cd79781d94f..6954dcbfa4c 100644 --- a/wire-ios-mocktransport/Source/Clients and OTR/MockUserClient.swift +++ b/wire-ios-mocktransport/Source/Clients and OTR/MockUserClient.swift @@ -189,7 +189,7 @@ public extension MockUserClient { newClient.prekeys = Set(mockPrekey) let mockLastPrekey = MockPreKey.insertNewKey(withPrekey: lastPrekey, for: newClient, in: context) - mockLastPrekey.identifier = Int(CBOX_LAST_PREKEY_ID) + mockLastPrekey.identifier = Int(UInt16.max) newClient.lastPrekey = mockLastPrekey return newClient } diff --git a/wire-ios-mocktransport/Source/Conversations/MockConversation.m b/wire-ios-mocktransport/Source/Conversations/MockConversation.m index 4799ad0d496..65944a01320 100644 --- a/wire-ios-mocktransport/Source/Conversations/MockConversation.m +++ b/wire-ios-mocktransport/Source/Conversations/MockConversation.m @@ -18,7 +18,6 @@ @import WireTransport; @import WireUtilities; -@import WireCryptobox; #import "MockConversation.h" #import "MockEvent.h" diff --git a/wire-ios-mono.xcworkspace/contents.xcworkspacedata b/wire-ios-mono.xcworkspace/contents.xcworkspacedata index 61bd04eebc3..0d49c87b85c 100644 --- a/wire-ios-mono.xcworkspace/contents.xcworkspacedata +++ b/wire-ios-mono.xcworkspace/contents.xcworkspacedata @@ -173,9 +173,6 @@ - - diff --git a/wire-ios-request-strategy/Sources/Helpers/EventDecoderDecryptionTests.swift b/wire-ios-request-strategy/Sources/Helpers/EventDecoderDecryptionTests.swift index c9547f14d30..7ca2011649a 100644 --- a/wire-ios-request-strategy/Sources/Helpers/EventDecoderDecryptionTests.swift +++ b/wire-ios-request-strategy/Sources/Helpers/EventDecoderDecryptionTests.swift @@ -19,7 +19,6 @@ import Foundation import GenericMessageProtocol import WireCoreCrypto -import WireCryptobox import WireDataModel import XCTest diff --git a/wire-ios-request-strategy/Sources/Request Strategies/User Clients/FetchingClientRequestStrategy.swift b/wire-ios-request-strategy/Sources/Request Strategies/User Clients/FetchingClientRequestStrategy.swift index 56edc96595a..249f536375f 100644 --- a/wire-ios-request-strategy/Sources/Request Strategies/User Clients/FetchingClientRequestStrategy.swift +++ b/wire-ios-request-strategy/Sources/Request Strategies/User Clients/FetchingClientRequestStrategy.swift @@ -17,7 +17,6 @@ // import Foundation -import WireCryptobox import WireDataModel import WireSystem import WireTransport diff --git a/wire-ios-request-strategy/Sources/Request Strategies/User Clients/FetchingClientRequestStrategyTests.swift b/wire-ios-request-strategy/Sources/Request Strategies/User Clients/FetchingClientRequestStrategyTests.swift index 7a2e0623763..0730f5af9ee 100644 --- a/wire-ios-request-strategy/Sources/Request Strategies/User Clients/FetchingClientRequestStrategyTests.swift +++ b/wire-ios-request-strategy/Sources/Request Strategies/User Clients/FetchingClientRequestStrategyTests.swift @@ -17,7 +17,6 @@ // import Foundation -import WireCryptobox import WireDataModel import WireTesting import WireTransport diff --git a/wire-ios-request-strategy/Sources/Synchronization/Decoding/EventDecoder+Proteus.swift b/wire-ios-request-strategy/Sources/Synchronization/Decoding/EventDecoder+Proteus.swift index ccac818cd17..2c6b296654a 100644 --- a/wire-ios-request-strategy/Sources/Synchronization/Decoding/EventDecoder+Proteus.swift +++ b/wire-ios-request-strategy/Sources/Synchronization/Decoding/EventDecoder+Proteus.swift @@ -18,7 +18,6 @@ import Foundation import WireCoreCrypto -import WireCryptobox import WireLogging private let zmLog = ZMSLog(tag: "EventDecoder") diff --git a/wire-ios-request-strategy/Sources/Synchronization/Decoding/EventDecoder.swift b/wire-ios-request-strategy/Sources/Synchronization/Decoding/EventDecoder.swift index 5f22bc8471f..edc8d9f7640 100644 --- a/wire-ios-request-strategy/Sources/Synchronization/Decoding/EventDecoder.swift +++ b/wire-ios-request-strategy/Sources/Synchronization/Decoding/EventDecoder.swift @@ -90,7 +90,7 @@ public final class EventDecoder: NSObject, EventDecoderProtocol { extension EventDecoder { /// Decrypts passed in events and stores them in chronological order in a persisted database, - /// it then saves the database and cryptobox + /// it then saves the database /// /// - Parameters: /// - events: Encrypted events diff --git a/wire-ios-sync-engine/Source/Synchronization/ZMOperationLoop.m b/wire-ios-sync-engine/Source/Synchronization/ZMOperationLoop.m index 60d6d76785e..91f59eadfc1 100644 --- a/wire-ios-sync-engine/Source/Synchronization/ZMOperationLoop.m +++ b/wire-ios-sync-engine/Source/Synchronization/ZMOperationLoop.m @@ -19,7 +19,6 @@ @import WireUtilities; @import WireSystem; @import WireTransport; -@import WireCryptobox; @import WireDataModel; #import "ZMOperationLoop+Private.h" diff --git a/wire-ios-sync-engine/Tests/Source/E2EE/UserClientRequestFactoryTests.swift b/wire-ios-sync-engine/Tests/Source/E2EE/UserClientRequestFactoryTests.swift index 433022111fe..7ec361ac6e5 100644 --- a/wire-ios-sync-engine/Tests/Source/E2EE/UserClientRequestFactoryTests.swift +++ b/wire-ios-sync-engine/Tests/Source/E2EE/UserClientRequestFactoryTests.swift @@ -17,7 +17,6 @@ // import Foundation -import WireCryptobox import WireDataModel import WireDataModelSupport import WireMockTransport diff --git a/wire-ios-sync-engine/Tests/Source/MessagingTest.h b/wire-ios-sync-engine/Tests/Source/MessagingTest.h index 513ed6cd7d7..cb958556e42 100644 --- a/wire-ios-sync-engine/Tests/Source/MessagingTest.h +++ b/wire-ios-sync-engine/Tests/Source/MessagingTest.h @@ -33,7 +33,6 @@ @class ZMAssetClientMessage; @class LastUpdateEventRepository; -@import WireCryptobox; @import WireImages; @class UserClient; diff --git a/wire-ios-sync-engine/Tests/Source/Synchronization/SynchronizationMocks.swift b/wire-ios-sync-engine/Tests/Source/Synchronization/SynchronizationMocks.swift index 20df8dbdcdb..6cdbff981c9 100644 --- a/wire-ios-sync-engine/Tests/Source/Synchronization/SynchronizationMocks.swift +++ b/wire-ios-sync-engine/Tests/Source/Synchronization/SynchronizationMocks.swift @@ -18,7 +18,6 @@ import avs import Foundation -import WireCryptobox import WireDataModel @testable import WireSyncEngine diff --git a/wire-ios-sync-engine/Tests/Source/Synchronization/ZMOperationLoopTests.h b/wire-ios-sync-engine/Tests/Source/Synchronization/ZMOperationLoopTests.h index e7c7b56525d..34f7c01704a 100644 --- a/wire-ios-sync-engine/Tests/Source/Synchronization/ZMOperationLoopTests.h +++ b/wire-ios-sync-engine/Tests/Source/Synchronization/ZMOperationLoopTests.h @@ -17,7 +17,6 @@ // @import WireTransport; -@import WireCryptobox; @import WireDataModel; @import avs; diff --git a/wire-ios/Tests/Mocks/MockUserType+SelfLegalHoldSubject.swift b/wire-ios/Tests/Mocks/MockUserType+SelfLegalHoldSubject.swift index 6e90181aedd..8d1e03ea4bd 100644 --- a/wire-ios/Tests/Mocks/MockUserType+SelfLegalHoldSubject.swift +++ b/wire-ios/Tests/Mocks/MockUserType+SelfLegalHoldSubject.swift @@ -17,7 +17,6 @@ // import Foundation -import WireCryptobox extension MockUserType: SelfLegalHoldSubject { diff --git a/wire-ios/Wire-iOS/Sources/UserInterface/Conversation/Content/Cells/ConfigurationMessageCell/Components/ConversationCannotDecryptSystemMessageCellDescription.swift b/wire-ios/Wire-iOS/Sources/UserInterface/Conversation/Content/Cells/ConfigurationMessageCell/Components/ConversationCannotDecryptSystemMessageCellDescription.swift index b3801284721..ec90e8fd09f 100644 --- a/wire-ios/Wire-iOS/Sources/UserInterface/Conversation/Content/Cells/ConfigurationMessageCell/Components/ConversationCannotDecryptSystemMessageCellDescription.swift +++ b/wire-ios/Wire-iOS/Sources/UserInterface/Conversation/Content/Cells/ConfigurationMessageCell/Components/ConversationCannotDecryptSystemMessageCellDescription.swift @@ -29,8 +29,6 @@ final class ConversationCannotDecryptSystemMessageCellDescription: ConversationM let configuration: View.Configuration - private static let resetSessionURL: URL = .init(string: "action://reset-session")! - var message: ZMConversationMessage? weak var delegate: ConversationMessageCellDelegate? weak var actionController: ConversationMessageActionController? @@ -89,7 +87,6 @@ final class ConversationCannotDecryptSystemMessageCellDescription: ConversationM ) -> NSAttributedString { let messageString = messageString(systemMessage.systemMessageType, sender: sender) - let resetSessionString = resetSessionString(accentColor: accentColor) let errorDetailsString = errorDetailsString( errorCode: systemMessage.decryptionErrorCode?.intValue ?? 0, clientIdentifier: systemMessage.senderClientID ?? "N/A" @@ -101,9 +98,6 @@ final class ConversationCannotDecryptSystemMessageCellDescription: ConversationM case .decryptionFailed: components = [messageString] - if systemMessage.isDecryptionErrorRecoverable { - components.append(resetSessionString) - } case .decryptionFailedResolved: components = [ messageString, @@ -153,19 +147,6 @@ final class ConversationCannotDecryptSystemMessageCellDescription: ConversationM return NSMutableAttributedString.markdown(from: localizationKey.localized(args: name), style: .systemMessage) } - private static func resetSessionString(accentColor: UIColor) -> NSAttributedString { - let string = L10n.Localizable.Content.System.CannotDecrypt.resetSession - - return NSAttributedString( - string: string.localizedUppercase, - attributes: [ - .link: resetSessionURL, - .foregroundColor: accentColor, - .font: UIFont.mediumSemiboldFont - ] - ) - } - private static func errorDetailsString(errorCode: Int, clientIdentifier: String) -> NSAttributedString { let string = L10n.Localizable.Content.System.CannotDecrypt.errorDetails(errorCode, clientIdentifier) From 64c9efbb78f8469af41a8c9e0497fbe679b53a40 Mon Sep 17 00:00:00 2001 From: David-Henner Date: Tue, 2 Dec 2025 15:14:06 +0100 Subject: [PATCH 20/28] remove cryptobox dependency in mock transport --- .../MockPendingLegalHoldClient.swift | 24 +--- .../MockTransportSession+OTR.swift | 5 +- .../MockUserClient+Protos.swift | 2 +- .../Clients and OTR/MockUserClient.swift | 112 ++---------------- .../Source/Conversations/MockConversation.h | 5 - .../Source/Conversations/MockConversation.m | 11 -- .../MockTransportSession+conversations.swift | 4 +- .../MockTransportSessionClientsTests.m | 19 --- .../MockTransportSessionConversationsTests.m | 16 +-- ...ckTransportSessionConversationsTests.swift | 8 +- .../Tests/Source/MockTransportSessionTests.m | 1 - .../project.pbxproj | 2 - 12 files changed, 35 insertions(+), 174 deletions(-) diff --git a/wire-ios-mocktransport/Source/Clients and OTR/MockPendingLegalHoldClient.swift b/wire-ios-mocktransport/Source/Clients and OTR/MockPendingLegalHoldClient.swift index 023215f0a8a..0cdfa0558fe 100644 --- a/wire-ios-mocktransport/Source/Clients and OTR/MockPendingLegalHoldClient.swift +++ b/wire-ios-mocktransport/Source/Clients and OTR/MockPendingLegalHoldClient.swift @@ -58,33 +58,21 @@ public extension MockUser { let identifier = String.randomClientIdentifier() pendingClient.identifier = identifier - // Generate the prekeys - let encryptionContext = MockUserClient.encryptionContext(for: self, clientId: identifier) + // Generate mock prekey strings + let prekeysStrings = (0..<5).map { _ in UUID().uuidString } - var generatedPrekeys: [[String: Any]]? - var generatedLastPrekey: String? - - encryptionContext.perform { session in - generatedPrekeys = try? session.generatePrekeys(NSRange(location: 0, length: 5)) - generatedLastPrekey = try? session.generateLastPrekey() - } - - guard let prekeys = generatedPrekeys, !prekeys.isEmpty, let lastPrekey = generatedLastPrekey else { - return false - } - - let mockPrekey = MockPreKey.insertNewKeys( - withPayload: prekeys.map { $0["prekey"] as! String }, + let prekeys = MockPreKey.insertNewKeys( + withPayload: prekeysStrings, context: managedObjectContext ) - pendingClient.prekeys = Set(mockPrekey) + pendingClient.prekeys = Set(prekeys) let mockLastPrekey = NSEntityDescription.insertNewObject( forEntityName: "PreKey", into: managedObjectContext ) as! MockPreKey mockLastPrekey.identifier = Int(UInt16.max) - mockLastPrekey.value = lastPrekey + mockLastPrekey.value = UUID().uuidString pendingClient.lastPrekey = mockLastPrekey return true diff --git a/wire-ios-mocktransport/Source/Clients and OTR/MockTransportSession+OTR.swift b/wire-ios-mocktransport/Source/Clients and OTR/MockTransportSession+OTR.swift index 827c34c1ebf..03edb65b8b5 100644 --- a/wire-ios-mocktransport/Source/Clients and OTR/MockTransportSession+OTR.swift +++ b/wire-ios-mocktransport/Source/Clients and OTR/MockTransportSession+OTR.swift @@ -185,7 +185,7 @@ extension MockTransportSession { toConversation conversation: MockConversation, recipients: [Proteus_UserEntry], senderClient: MockUserClient, - createEventBlock: (MockUserClient, Data, Data) -> MockEvent + createEventBlock: (MockUserClient, Data) -> MockEvent ) { let activeUsers = conversation.activeUsers.array as? [MockUser] @@ -210,8 +210,7 @@ extension MockTransportSession { return } - let decryptedData = MockUserClient.decryptMessage(data: entry.text, from: senderClient, to: client) - _ = createEventBlock(client, entry.text, decryptedData) + _ = createEventBlock(client, entry.text) } } } diff --git a/wire-ios-mocktransport/Source/Clients and OTR/MockUserClient+Protos.swift b/wire-ios-mocktransport/Source/Clients and OTR/MockUserClient+Protos.swift index 20bd7a1fb21..32c5eed540d 100644 --- a/wire-ios-mocktransport/Source/Clients and OTR/MockUserClient+Protos.swift +++ b/wire-ios-mocktransport/Source/Clients and OTR/MockUserClient+Protos.swift @@ -72,7 +72,7 @@ extension MockUserClient { } return Proteus_ClientEntry.with { $0.client = clientId - $0.text = MockUserClient.encrypted(data: plainText, from: self, to: client) + $0.text = plainText } } diff --git a/wire-ios-mocktransport/Source/Clients and OTR/MockUserClient.swift b/wire-ios-mocktransport/Source/Clients and OTR/MockUserClient.swift index 6954dcbfa4c..7930eb71efe 100644 --- a/wire-ios-mocktransport/Source/Clients and OTR/MockUserClient.swift +++ b/wire-ios-mocktransport/Source/Clients and OTR/MockUserClient.swift @@ -68,14 +68,6 @@ public class MockUserClient: NSManagedObject { public extension MockUserClient { - /// Identifier for the session in Cryptobox - var sessionIdentifier: EncryptionSessionIdentifier? { - guard let identifier, let userIdentifier = user?.identifier else { - return nil - } - return EncryptionSessionIdentifier(userId: userIdentifier, clientId: identifier) - } - /// Returns a fetch request to fetch MockUserClients with the given predicate @objc static func fetchRequest(predicate: NSPredicate) -> NSFetchRequest { @@ -169,28 +161,20 @@ public extension MockUserClient { newClient.deviceClass = deviceClass newClient.time = Date() - var generatedPrekeys: [[String: Any]]? - var generatedLastPrekey: String? - newClient.encryptionContext.perform { session in - generatedPrekeys = try? session.generatePrekeys(NSRange(location: 0, length: 5)) - generatedLastPrekey = try? session.generateLastPrekey() - } - - guard let prekeys = generatedPrekeys, !prekeys.isEmpty, - let lastPrekey = generatedLastPrekey - else { - return nil - } + // Generate mock prekey strings (no encryption needed for mock transport) + let mockPrekeys = (0..<5).map { _ in UUID().uuidString } + let mockLastPrekey = UUID().uuidString - let mockPrekey = MockPreKey.insertNewKeys( - withPayload: prekeys.map { $0["prekey"] as! String }, + let prekeys = MockPreKey.insertNewKeys( + withPayload: mockPrekeys, context: context ) - newClient.prekeys = Set(mockPrekey) + newClient.prekeys = Set(prekeys) + + let lastPrekey = MockPreKey.insertNewKey(withPrekey: mockLastPrekey, for: newClient, in: context) + lastPrekey.identifier = Int(UInt16.max) + newClient.lastPrekey = lastPrekey - let mockLastPrekey = MockPreKey.insertNewKey(withPrekey: lastPrekey, for: newClient, in: context) - mockLastPrekey.identifier = Int(UInt16.max) - newClient.lastPrekey = mockLastPrekey return newClient } @@ -221,81 +205,5 @@ public extension MockUserClient { } } -// MARK: - Encryption and sessions - -@objc -public extension MockUserClient { - - static var mockEncryptionSessionDirectory: URL { - FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask).first! - .appendingPathComponent("mocktransport-encryptionDirectory") - } - - internal static func encryptionContext(for user: MockUser?, clientId: String?) -> EncryptionContext { - let directory = MockUserClient.mockEncryptionSessionDirectory - .appendingPathComponent("mockclient_\(user?.identifier ?? "USER")_\(clientId ?? "IDENTIFIER")") - try! FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true, attributes: [:]) - return EncryptionContext(path: directory) - } - - private var encryptionContext: EncryptionContext { - MockUserClient.encryptionContext(for: user, clientId: identifier) - } - - /// Make sure that there is a session established between this client and the given client - /// If needed, it will use the last prekey to create a session - /// - returns: false if it was not possible to establish a session - func establishSession(client: MockUserClient) -> Bool { - guard let identifier = client.sessionIdentifier else { return false } - var hasSession = false - encryptionContext.perform { session in - if !session.hasSession(for: identifier) { - try? session.createClientSession(identifier, base64PreKeyString: client.lastPrekey.value) - hasSession = session.hasSession(for: identifier) - } else { - hasSession = true - } - } - return hasSession - } - - /// Encrypt data from a client to a client. If there is no session between the two clients, it will create - /// one using the last prekey - static func encrypted(data: Data, from: MockUserClient, to: MockUserClient) -> Data { - var encryptedData: Data? - guard from.establishSession(client: to) else { fatalError() } - from.encryptionContext.perform { session in - encryptedData = try? session.encrypt(data, for: to.sessionIdentifier!) - } - return encryptedData! - } - - /// Decrypt a message (possibly establishing a session, if there is no session) from a client to a client - static func decryptMessage(data: Data, from: MockUserClient, to: MockUserClient) -> Data { - var decryptedData: Data? - to.encryptionContext.perform { session in - if !session.hasSession(for: from.sessionIdentifier!) { - decryptedData = try? session.createClientSessionAndReturnPlaintext( - for: from.sessionIdentifier!, - prekeyMessage: data - ) - } else { - decryptedData = try? session.decrypt(data, from: from.sessionIdentifier!) - } - } - return decryptedData ?? Data() - } - - /// Returns whether there is a encryption session between self and the give client - func hasSession(with client: MockUserClient) -> Bool { - guard let identifier = client.sessionIdentifier else { return false } - var hasSession = false - encryptionContext.perform { session in - hasSession = session.hasSession(for: identifier) - } - return hasSession - } -} - /// Allowed client types private let validClientTypes = Set(["temporary", "permanent", "legalhold"]) diff --git a/wire-ios-mocktransport/Source/Conversations/MockConversation.h b/wire-ios-mocktransport/Source/Conversations/MockConversation.h index 18f2b2fe645..dc432e20052 100644 --- a/wire-ios-mocktransport/Source/Conversations/MockConversation.h +++ b/wire-ios-mocktransport/Source/Conversations/MockConversation.h @@ -81,11 +81,6 @@ typedef NS_ENUM(int16_t, ZMTConversationType) { - (nonnull MockEvent *)insertClientMessageFromUser:(nonnull MockUser *)fromUser data:(nonnull NSData *)data; -/// Encrypts and inserts a OTR message using the gerneric message data sent from the given client to the given client -- (nonnull MockEvent *)encryptAndInsertDataFromClient:(nonnull MockUserClient *)fromClient - toClient:(nonnull MockUserClient *)toClient - data:(nonnull NSData *)data; - - (nonnull MockEvent *)insertOTRMessageFromClient:(nonnull MockUserClient *)fromClient toClient:(nonnull MockUserClient *)toClient data:(nonnull NSData *)data; diff --git a/wire-ios-mocktransport/Source/Conversations/MockConversation.m b/wire-ios-mocktransport/Source/Conversations/MockConversation.m index 65944a01320..801dd45e1b1 100644 --- a/wire-ios-mocktransport/Source/Conversations/MockConversation.m +++ b/wire-ios-mocktransport/Source/Conversations/MockConversation.m @@ -263,17 +263,6 @@ - (MockEvent *)insertOTRMessageFromClient:(MockUserClient *)fromClient return [self eventIfNeededByUser:fromClient.user type:ZMUpdateEventTypeConversationOtrMessageAdd data:eventData]; } -- (MockEvent *)encryptAndInsertDataFromClient:(MockUserClient *)fromClient - toClient:(MockUserClient *)toClient - data:(NSData *)data; -{ - Require(fromClient.identifier != nil); - Require(toClient.identifier != nil); - Require(data != nil); - NSData *encrypted = [MockUserClient encryptedWithData:data from:fromClient to:toClient]; - return [self insertOTRMessageFromClient:fromClient toClient:toClient data:encrypted]; -} - - (MockEvent *)insertOTRAssetFromClient:(MockUserClient *)fromClient toClient:(MockUserClient *)toClient metaData:(NSData *)metaData diff --git a/wire-ios-mocktransport/Source/Conversations/MockTransportSession+conversations.swift b/wire-ios-mocktransport/Source/Conversations/MockTransportSession+conversations.swift index 16980e27ff7..6370674ff9f 100644 --- a/wire-ios-mocktransport/Source/Conversations/MockTransportSession+conversations.swift +++ b/wire-ios-mocktransport/Source/Conversations/MockTransportSession+conversations.swift @@ -525,9 +525,9 @@ public extension MockTransportSession { toConversation: conversation, recipients: otrMetaData.recipients, senderClient: senderClient, - createEventBlock: { recipient, messageData, decryptedData in + createEventBlock: { recipient, messageData in let event = conversation.insertOTRMessage(from: senderClient, to: recipient, data: messageData) - event.decryptedOTRData = decryptedData + event.decryptedOTRData = messageData return event } ) diff --git a/wire-ios-mocktransport/Tests/Source/Clients and OTR/MockTransportSessionClientsTests.m b/wire-ios-mocktransport/Tests/Source/Clients and OTR/MockTransportSessionClientsTests.m index 178b7392b05..0be4140b2aa 100644 --- a/wire-ios-mocktransport/Tests/Source/Clients and OTR/MockTransportSessionClientsTests.m +++ b/wire-ios-mocktransport/Tests/Source/Clients and OTR/MockTransportSessionClientsTests.m @@ -711,23 +711,4 @@ - (void)testThatItDoesNotSendsANoficationWhenRemovingAClientOfAnotherUser { XCTAssertEqual(self.sut.generatedPushEvents.count, 0u); } -- (void)testThatEncryptEncryptDataBetweenTwoClients; -{ - __block MockUserClient *selfClient; - __block MockUserClient *destClient; - [self.sut performRemoteChanges:^(id session) { - - MockUser *selfUser = [session insertSelfUserWithName:@"Brigite Sorço"]; - selfClient = [session registerClientForUser:selfUser label:@"moi" type:@"permanent" deviceClass:@"phone"]; - destClient = [session registerClientForUser:selfUser label:@"autre" type:@"permanent" deviceClass:@"phone"]; - }]; - WaitForAllGroupsToBeEmpty(0.5); - - NSData *clearData = [@"Please, encrypt me!" dataUsingEncoding:NSUTF8StringEncoding]; - NSData *encryptedData = [MockUserClient encryptedWithData:clearData from:destClient to:selfClient]; - - XCTAssertNotNil(encryptedData); - XCTAssertNotEqualObjects(clearData, encryptedData); -} - @end diff --git a/wire-ios-mocktransport/Tests/Source/Conversations/MockTransportSessionConversationsTests.m b/wire-ios-mocktransport/Tests/Source/Conversations/MockTransportSessionConversationsTests.m index 40c8bdfc717..a304655f815 100644 --- a/wire-ios-mocktransport/Tests/Source/Conversations/MockTransportSessionConversationsTests.m +++ b/wire-ios-mocktransport/Tests/Source/Conversations/MockTransportSessionConversationsTests.m @@ -699,21 +699,21 @@ - (void)testThatItCreatesPushEventsWhenReceivingEncryptedOTRMessageWithCorrectDa NSData *messageData = [@"Fofooof" dataUsingEncoding:NSUTF8StringEncoding]; NSString *base64Content = [messageData base64EncodedStringWithOptions:0]; - + // WHEN [self.sut performRemoteChanges:^(__unused id session) { - NSData *encryptedData = [MockUserClient encryptedWithData:messageData from:otherUserClient to:selfClient]; - [conversation insertOTRMessageFromClient:otherUserClient toClient:selfClient data:encryptedData]; + // Mock transport doesn't encrypt - just passes data through + [conversation insertOTRMessageFromClient:otherUserClient toClient:selfClient data:messageData]; }]; WaitForAllGroupsToBeEmpty(0.5); - + // THEN MockPushEvent *lastEvent = self.sut.generatedPushEvents.lastObject; NSDictionary *lastEventPayload = [lastEvent.payload asDictionary]; XCTAssertEqualObjects(lastEventPayload[@"type"], @"conversation.otr-message-add"); XCTAssertEqualObjects(lastEventPayload[@"data"][@"recipient"], selfClient.identifier); XCTAssertEqualObjects(lastEventPayload[@"data"][@"sender"], otherUserClient.identifier); - XCTAssertNotEqualObjects(lastEventPayload[@"data"][@"text"], base64Content); + XCTAssertEqualObjects(lastEventPayload[@"data"][@"text"], base64Content); } - (void)testThatItCreatesPushEventsWhenReceivingEncryptedOTRAssetWithCorrectData; @@ -746,8 +746,8 @@ - (void)testThatItCreatesPushEventsWhenReceivingEncryptedOTRAssetWithCorrectData NSString *base64Content = [messageData base64EncodedStringWithOptions:0]; // WHEN [self.sut performRemoteChanges:^(__unused id session) { - NSData *encryptedData = [MockUserClient encryptedWithData:messageData from:otherUserClient to:selfClient]; - [conversation insertOTRAssetFromClient:otherUserClient toClient:selfClient metaData:encryptedData imageData:imageData assetId:assetID isInline:YES]; + // Mock transport doesn't encrypt - just passes data through + [conversation insertOTRAssetFromClient:otherUserClient toClient:selfClient metaData:messageData imageData:imageData assetId:assetID isInline:YES]; }]; WaitForAllGroupsToBeEmpty(0.5); @@ -758,7 +758,7 @@ - (void)testThatItCreatesPushEventsWhenReceivingEncryptedOTRAssetWithCorrectData XCTAssertEqualObjects(lastEventPayload[@"data"][@"recipient"], selfClient.identifier); XCTAssertEqualObjects(lastEventPayload[@"data"][@"sender"], otherUserClient.identifier); XCTAssertEqualObjects(lastEventPayload[@"data"][@"data"], [imageData base64String]); - XCTAssertNotEqualObjects(lastEventPayload[@"data"][@"key"], base64Content); + XCTAssertNotNil(lastEventPayload[@"data"][@"key"]); } - (void)testThatInsertingArbitraryEventWithBlock:(MockEvent *(^)(id session, MockConversation *conversation))eventBlock expectedPayloadData:(id)expectedPayloadData diff --git a/wire-ios-mocktransport/Tests/Source/Conversations/MockTransportSessionConversationsTests.swift b/wire-ios-mocktransport/Tests/Source/Conversations/MockTransportSessionConversationsTests.swift index 8fc6731f4eb..59b76520a15 100644 --- a/wire-ios-mocktransport/Tests/Source/Conversations/MockTransportSessionConversationsTests.swift +++ b/wire-ios-mocktransport/Tests/Source/Conversations/MockTransportSessionConversationsTests.swift @@ -341,8 +341,12 @@ class MockTransportSessionConversationsTests_Swift: MockTransportSessionTests { let lastEvent = conversation!.events.lastObject as! MockEvent XCTAssertNotNil(lastEvent) XCTAssertEqual(lastEvent.eventType, ZMUpdateEventType.conversationOtrMessageAdd) - XCTAssertNotNil(lastEvent.decryptedOTRData) - let decryptedMessage = try! GenericMessage(serializedData: lastEvent.decryptedOTRData!) + + // Extract message data from event (mock transport doesn't decrypt) + let eventData = lastEvent.data as! NSDictionary + let messageDataBase64 = eventData["text"] as! String + let messageBytes = Data(base64Encoded: messageDataBase64)! + let decryptedMessage = try! GenericMessage(serializedData: messageBytes) XCTAssertEqual(decryptedMessage.text.content, messageText) } diff --git a/wire-ios-mocktransport/Tests/Source/MockTransportSessionTests.m b/wire-ios-mocktransport/Tests/Source/MockTransportSessionTests.m index bc126d46e6a..f62afa31ffc 100644 --- a/wire-ios-mocktransport/Tests/Source/MockTransportSessionTests.m +++ b/wire-ios-mocktransport/Tests/Source/MockTransportSessionTests.m @@ -138,7 +138,6 @@ - (void)tearDown self.cookieStorage = nil; self.pushChannelDidOpenCount = 0; self.pushChannelDidCloseCount = 0; - [NSFileManager.defaultManager removeItemAtURL:[MockUserClient mockEncryptionSessionDirectory] error:nil]; [super tearDown]; } diff --git a/wire-ios-mocktransport/WireMockTransport.xcodeproj/project.pbxproj b/wire-ios-mocktransport/WireMockTransport.xcodeproj/project.pbxproj index 3a0faa2b1dc..ae92f4b522a 100644 --- a/wire-ios-mocktransport/WireMockTransport.xcodeproj/project.pbxproj +++ b/wire-ios-mocktransport/WireMockTransport.xcodeproj/project.pbxproj @@ -13,7 +13,6 @@ 59EC73FB2C20B82F00E5C036 /* WireSystem.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 59EC73FA2C20B82F00E5C036 /* WireSystem.framework */; }; CB79AC682C6D0009003CF5F4 /* WireTransportSupport.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CB79AC672C6D0009003CF5F4 /* WireTransportSupport.framework */; }; EE67F700296F09D8001D7C88 /* WireTesting.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EE67F6FF296F09D8001D7C88 /* WireTesting.framework */; }; - EE974125295498DA000C9340 /* WireCryptobox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EE974124295498DA000C9340 /* WireCryptobox.framework */; }; EE974128295498E2000C9340 /* WireTransport.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EE974127295498E2000C9340 /* WireTransport.framework */; }; EE9AEC922BD1596000F7853F /* WireMockTransport.docc in Sources */ = {isa = PBXBuildFile; fileRef = EE9AEC912BD1596000F7853F /* WireMockTransport.docc */; }; /* End PBXBuildFile section */ @@ -90,7 +89,6 @@ buildActionMask = 2147483647; files = ( 5950B03A2E2EA5990051B07A /* GenericMessageProtocol in Frameworks */, - EE974125295498DA000C9340 /* WireCryptobox.framework in Frameworks */, 59EC73FB2C20B82F00E5C036 /* WireSystem.framework in Frameworks */, EE67F700296F09D8001D7C88 /* WireTesting.framework in Frameworks */, CB79AC682C6D0009003CF5F4 /* WireTransportSupport.framework in Frameworks */, From 12be7c6e46577c0299317fde7fad7c21acadf182 Mon Sep 17 00:00:00 2001 From: David-Henner Date: Tue, 2 Dec 2025 15:14:36 +0100 Subject: [PATCH 21/28] use readableHash instead of deleted safeForLoggingDescription --- .../Source/SessionManager/PushTokenService.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wire-ios-sync-engine/Source/SessionManager/PushTokenService.swift b/wire-ios-sync-engine/Source/SessionManager/PushTokenService.swift index b833844e442..7c9ffe69496 100644 --- a/wire-ios-sync-engine/Source/SessionManager/PushTokenService.swift +++ b/wire-ios-sync-engine/Source/SessionManager/PushTokenService.swift @@ -85,12 +85,12 @@ public final class PushTokenService: PushTokenServiceInterface { do { for remoteToken in remoteTokens where remoteToken != excludedToken { - WireLogger.push.debug("unregister invalid token: \(remoteToken.deviceToken.safeForLoggingDescription)") + WireLogger.push.debug("unregister invalid token: <\(remoteToken.deviceToken.readableHash)>") var removeAction = RemovePushTokenAction(deviceToken: remoteToken.deviceTokenString) try await removeAction.perform(in: context) WireLogger.push .debug( - "unregister invalid token of type: \(remoteToken.deviceToken.safeForLoggingDescription), success" + "unregister invalid token of type: <\(remoteToken.deviceToken.readableHash)>, success" ) } } catch let error as RemovePushTokenAction.Failure { From b7dedfb8d6d662ddd609604dca5640128c190650 Mon Sep 17 00:00:00 2001 From: David-Henner Date: Tue, 2 Dec 2025 17:03:30 +0100 Subject: [PATCH 22/28] remove cryptobox from wire-ios --- wire-ios/Wire-iOS.xcodeproj/project.pbxproj | 4 ---- 1 file changed, 4 deletions(-) diff --git a/wire-ios/Wire-iOS.xcodeproj/project.pbxproj b/wire-ios/Wire-iOS.xcodeproj/project.pbxproj index c4f7ac391b8..413ce88314e 100644 --- a/wire-ios/Wire-iOS.xcodeproj/project.pbxproj +++ b/wire-ios/Wire-iOS.xcodeproj/project.pbxproj @@ -138,8 +138,6 @@ EE7905F627F326EB00FCBF8C /* Wire Share Extension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 168A16A91D9597C2005CFA6C /* Wire Share Extension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; EE9A8586298B0A3B00064A9C /* WireNotificationEngine.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EE9A8585298B0A3B00064A9C /* WireNotificationEngine.framework */; }; EEE25B2A29719A730008B894 /* cryptobox.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = EEE25B2729719A730008B894 /* cryptobox.xcframework */; }; - EEE25B3529719BD30008B894 /* WireCryptobox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EEE25B3429719BD30008B894 /* WireCryptobox.framework */; }; - EEE25B3629719BD30008B894 /* WireCryptobox.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = EEE25B3429719BD30008B894 /* WireCryptobox.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; EEE25B3829719C170008B894 /* WireDataModel.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EEE25B3729719C170008B894 /* WireDataModel.framework */; }; EEE25B3929719C170008B894 /* WireDataModel.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = EEE25B3729719C170008B894 /* WireDataModel.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; EEE25B3B29719C370008B894 /* WireLinkPreview.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EEE25B3A29719C370008B894 /* WireLinkPreview.framework */; }; @@ -238,7 +236,6 @@ files = ( 5996E8A72C19D090007A52F0 /* WireSyncEngine.framework in Embed Frameworks */, 591B6E182C8B095B009F8A7B /* WireNotificationEngine.framework in Embed Frameworks */, - EEE25B3629719BD30008B894 /* WireCryptobox.framework in Embed Frameworks */, EE33C48E296485FA00C058D1 /* WireShareEngine.framework in Embed Frameworks */, EE67F73F296F0E7D001D7C88 /* Ziphy.framework in Embed Frameworks */, EEE25B4229719C750008B894 /* WireRequestStrategy.framework in Embed Frameworks */, @@ -957,7 +954,6 @@ 0619A95C2D3FE78700876BDE /* WireAuthenticationUI in Frameworks */, EEE25B4129719C750008B894 /* WireRequestStrategy.framework in Frameworks */, EE543B4A2D564AC000CA96BA /* WireAuthentication in Frameworks */, - EEE25B3529719BD30008B894 /* WireCryptobox.framework in Frameworks */, 06EF6B552E8EBEF200A131A7 /* WireCallingUI in Frameworks */, 01C774272D9EECE100A2FF07 /* WireAVS in Frameworks */, CBE5BB862E2D4A9200AC731F /* WireMessagingAssembly in Frameworks */, From d490e3316344aa61382c4bb4a61113f229fcf632 Mon Sep 17 00:00:00 2001 From: David-Henner Date: Tue, 2 Dec 2025 17:12:12 +0100 Subject: [PATCH 23/28] remove dead functionality to fix decryption errors by resetting session --- .../testDecryptionFailed_Other.320-0.png | 4 ++-- .../testDecryptionFailed_Other.375-0.png | 4 ++-- .../testDecryptionFailed_Other.414-0.png | 4 ++-- .../testDecryptionFailed_Self.320-0.png | 4 ++-- .../testDecryptionFailed_Self.375-0.png | 4 ++-- .../testDecryptionFailed_Self.414-0.png | 4 ++-- .../Wire-iOS/Generated/Strings+Generated.swift | 2 -- .../Localization/Base.lproj/Localizable.strings | 1 - ...ersationCannotDecryptSystemMessageCell.swift | 13 ------------- .../ConversationMessageActionController.swift | 3 +-- .../Content/Cells/MessageAction.swift | 8 +------- ...ionContentViewController+MessageAction.swift | 17 ----------------- .../ConversationContentViewController.swift | 1 - 13 files changed, 14 insertions(+), 55 deletions(-) diff --git a/wire-ios/Wire-iOS Tests/ReferenceImages/ConversationSystemMessageTests/testDecryptionFailed_Other.320-0.png b/wire-ios/Wire-iOS Tests/ReferenceImages/ConversationSystemMessageTests/testDecryptionFailed_Other.320-0.png index 7df76b995fc..cebca05541c 100644 --- a/wire-ios/Wire-iOS Tests/ReferenceImages/ConversationSystemMessageTests/testDecryptionFailed_Other.320-0.png +++ b/wire-ios/Wire-iOS Tests/ReferenceImages/ConversationSystemMessageTests/testDecryptionFailed_Other.320-0.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a98f83ef922cc0305f872f3da585a6b8b121372f5651a5df3954c98900dbece6 -size 19949 +oid sha256:6ea16c8001dccd8356cc9b40e1ea77d8c8997f9dcd12bfed11f57f877760b8e2 +size 13481 diff --git a/wire-ios/Wire-iOS Tests/ReferenceImages/ConversationSystemMessageTests/testDecryptionFailed_Other.375-0.png b/wire-ios/Wire-iOS Tests/ReferenceImages/ConversationSystemMessageTests/testDecryptionFailed_Other.375-0.png index fc8dc7b70c2..5cd15869619 100644 --- a/wire-ios/Wire-iOS Tests/ReferenceImages/ConversationSystemMessageTests/testDecryptionFailed_Other.375-0.png +++ b/wire-ios/Wire-iOS Tests/ReferenceImages/ConversationSystemMessageTests/testDecryptionFailed_Other.375-0.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fbb41d98b61009829da0496657a53bf1d6454fcda49890612072d7bd3c0e212f -size 17745 +oid sha256:b7df28fc95d488e83f201f7cf5229b1c8df126f20b9413a45e2a1ccefc3578c8 +size 12165 diff --git a/wire-ios/Wire-iOS Tests/ReferenceImages/ConversationSystemMessageTests/testDecryptionFailed_Other.414-0.png b/wire-ios/Wire-iOS Tests/ReferenceImages/ConversationSystemMessageTests/testDecryptionFailed_Other.414-0.png index 1fbcf81501e..9e81f4aaed6 100644 --- a/wire-ios/Wire-iOS Tests/ReferenceImages/ConversationSystemMessageTests/testDecryptionFailed_Other.414-0.png +++ b/wire-ios/Wire-iOS Tests/ReferenceImages/ConversationSystemMessageTests/testDecryptionFailed_Other.414-0.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9250cfa2ed597b019569f3549d53ecaf0c9a4a09b70d0781d4c3be3f04693c76 -size 18261 +oid sha256:a1074f96c37c93fc1077167248962cd05a763f07aead73432141224e78d060e3 +size 12483 diff --git a/wire-ios/Wire-iOS Tests/ReferenceImages/ConversationSystemMessageTests/testDecryptionFailed_Self.320-0.png b/wire-ios/Wire-iOS Tests/ReferenceImages/ConversationSystemMessageTests/testDecryptionFailed_Self.320-0.png index 1bcfba9af9b..2c0903f3a54 100644 --- a/wire-ios/Wire-iOS Tests/ReferenceImages/ConversationSystemMessageTests/testDecryptionFailed_Self.320-0.png +++ b/wire-ios/Wire-iOS Tests/ReferenceImages/ConversationSystemMessageTests/testDecryptionFailed_Self.320-0.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e3e431481617d33e9fc27990462b80ad6253bb79021e1742c7312e22a3762ec7 -size 19616 +oid sha256:f29aca1203b0e8666fe86d7c5f52860ab91e19e2b415051c48a245885f4fc514 +size 13154 diff --git a/wire-ios/Wire-iOS Tests/ReferenceImages/ConversationSystemMessageTests/testDecryptionFailed_Self.375-0.png b/wire-ios/Wire-iOS Tests/ReferenceImages/ConversationSystemMessageTests/testDecryptionFailed_Self.375-0.png index ac45e528431..6cb6b43fcf5 100644 --- a/wire-ios/Wire-iOS Tests/ReferenceImages/ConversationSystemMessageTests/testDecryptionFailed_Self.375-0.png +++ b/wire-ios/Wire-iOS Tests/ReferenceImages/ConversationSystemMessageTests/testDecryptionFailed_Self.375-0.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:89144dd538fdd21dfa69a09be7fb996ec88c080484486dda87b848163018bb5c -size 17194 +oid sha256:8d3996487e95a59c65492172eefba75071730d1aad6a2c951964b0914b4dc6ac +size 11585 diff --git a/wire-ios/Wire-iOS Tests/ReferenceImages/ConversationSystemMessageTests/testDecryptionFailed_Self.414-0.png b/wire-ios/Wire-iOS Tests/ReferenceImages/ConversationSystemMessageTests/testDecryptionFailed_Self.414-0.png index 5adb73e191c..9978c048fea 100644 --- a/wire-ios/Wire-iOS Tests/ReferenceImages/ConversationSystemMessageTests/testDecryptionFailed_Self.414-0.png +++ b/wire-ios/Wire-iOS Tests/ReferenceImages/ConversationSystemMessageTests/testDecryptionFailed_Self.414-0.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9c0cd11e8f6ac78d7b84911a51f715615fc8d7086f03a331fa992c0fb85ccb6e -size 17671 +oid sha256:e15a478f0b3093921bb885b27302bf801814d9285926ac719a6a65dd48b07247 +size 11867 diff --git a/wire-ios/Wire-iOS/Generated/Strings+Generated.swift b/wire-ios/Wire-iOS/Generated/Strings+Generated.swift index af49e7e0106..e0e191d73a4 100644 --- a/wire-ios/Wire-iOS/Generated/Strings+Generated.swift +++ b/wire-ios/Wire-iOS/Generated/Strings+Generated.swift @@ -2164,8 +2164,6 @@ internal enum L10n { internal static func other(_ p1: Any) -> String { return L10n.tr("Localizable", "content.system.cannot_decrypt.other", String(describing: p1), fallback: "A message from %@ could not be decrypted.") } - /// Fix future messages - internal static let resetSession = L10n.tr("Localizable", "content.system.cannot_decrypt.reset_session", fallback: "Fix future messages") /// A message from you could not be decrypted. internal static let `self` = L10n.tr("Localizable", "content.system.cannot_decrypt.self", fallback: "A message from you could not be decrypted.") } diff --git a/wire-ios/Wire-iOS/Resources/Localization/Base.lproj/Localizable.strings b/wire-ios/Wire-iOS/Resources/Localization/Base.lproj/Localizable.strings index 931da846017..5a9b0f7eb8c 100644 --- a/wire-ios/Wire-iOS/Resources/Localization/Base.lproj/Localizable.strings +++ b/wire-ios/Wire-iOS/Resources/Localization/Base.lproj/Localizable.strings @@ -658,7 +658,6 @@ "content.system.cannot_decrypt.self" = "A message from you could not be decrypted."; "content.system.cannot_decrypt.other" = "A message from %@ could not be decrypted."; -"content.system.cannot_decrypt.reset_session" = "Fix future messages"; "content.system.cannot_decrypt.error_details" = "(Fixed error: %d ID: %@)"; "content.system.cannot_decrypt_resolved.self" = "You can now decrypt messages from yourself. To recover lost messages, you need to resend them."; diff --git a/wire-ios/Wire-iOS/Sources/UserInterface/Conversation/Content/Cells/ConfigurationMessageCell/Components/ConversationCannotDecryptSystemMessageCell.swift b/wire-ios/Wire-iOS/Sources/UserInterface/Conversation/Content/Cells/ConfigurationMessageCell/Components/ConversationCannotDecryptSystemMessageCell.swift index 8cdc77dbeca..e8c3b0c8da2 100644 --- a/wire-ios/Wire-iOS/Sources/UserInterface/Conversation/Content/Cells/ConfigurationMessageCell/Components/ConversationCannotDecryptSystemMessageCell.swift +++ b/wire-ios/Wire-iOS/Sources/UserInterface/Conversation/Content/Cells/ConfigurationMessageCell/Components/ConversationCannotDecryptSystemMessageCell.swift @@ -42,17 +42,4 @@ final class ConversationCannotDecryptSystemMessageCell: textLabel.linkTextAttributes = [:] } - // MARK: - UITextViewDelegate - - override func textView( - _ textView: UITextView, - shouldInteractWith url: URL, - in characterRange: NSRange, - interaction: UITextItemInteraction - ) -> Bool { - delegate?.perform(action: .resetSession, for: message!, view: self) - - return false - } - } diff --git a/wire-ios/Wire-iOS/Sources/UserInterface/Conversation/Content/Cells/ConfigurationMessageCell/ConversationMessageActionController.swift b/wire-ios/Wire-iOS/Sources/UserInterface/Conversation/Content/Cells/ConfigurationMessageCell/ConversationMessageActionController.swift index ab0c9d1c5d2..06ee12a9c8f 100644 --- a/wire-ios/Wire-iOS/Sources/UserInterface/Conversation/Content/Cells/ConfigurationMessageCell/ConversationMessageActionController.swift +++ b/wire-ios/Wire-iOS/Sources/UserInterface/Conversation/Content/Cells/ConfigurationMessageCell/ConversationMessageActionController.swift @@ -148,8 +148,7 @@ final class ConversationMessageActionController { return message.isSentBySelfUser && message.isCollapsingSupported case .present, - .openQuote, - .resetSession: + .openQuote: return false } } diff --git a/wire-ios/Wire-iOS/Sources/UserInterface/Conversation/Content/Cells/MessageAction.swift b/wire-ios/Wire-iOS/Sources/UserInterface/Conversation/Content/Cells/MessageAction.swift index 19bfec3dd01..ac1c1d6a8d3 100644 --- a/wire-ios/Wire-iOS/Sources/UserInterface/Conversation/Content/Cells/MessageAction.swift +++ b/wire-ios/Wire-iOS/Sources/UserInterface/Conversation/Content/Cells/MessageAction.swift @@ -40,7 +40,6 @@ enum MessageAction: CaseIterable, Equatable { .sketchEmoji, .present, .openQuote, - .resetSession, .delete, .react("❤️") ] @@ -62,7 +61,6 @@ enum MessageAction: CaseIterable, Equatable { // Not included in ConversationMessageActionController.allMessageActions, for image viewer/open quote present, openQuote, - resetSession, react(Emoji.ID), visitLink, collapse @@ -102,7 +100,6 @@ enum MessageAction: CaseIterable, Equatable { return MessageActionLocale.collapse case .present, .openQuote, - .resetSession, .react: return nil } @@ -139,7 +136,6 @@ enum MessageAction: CaseIterable, Equatable { case .present, .openQuote, .digitallySign, - .resetSession, .react, .collapse: nil @@ -189,7 +185,6 @@ enum MessageAction: CaseIterable, Equatable { case .present, .openQuote, .digitallySign, - .resetSession, .react, .visitLink, .collapse: @@ -230,8 +225,7 @@ enum MessageAction: CaseIterable, Equatable { case .present, .sketchDraw, .sketchEmoji, - .openQuote, - .resetSession: + .openQuote: // no message related actions are not handled in ConversationMessageActionController nil } diff --git a/wire-ios/Wire-iOS/Sources/UserInterface/Conversation/Content/ConversationContentViewController+MessageAction.swift b/wire-ios/Wire-iOS/Sources/UserInterface/Conversation/Content/ConversationContentViewController+MessageAction.swift index 0ceda253fab..59d46f1cca3 100644 --- a/wire-ios/Wire-iOS/Sources/UserInterface/Conversation/Content/ConversationContentViewController+MessageAction.swift +++ b/wire-ios/Wire-iOS/Sources/UserInterface/Conversation/Content/ConversationContentViewController+MessageAction.swift @@ -180,11 +180,6 @@ extension ConversationContentViewController { navigationController.modalPresentationStyle = .formSheet parent?.present(navigationController, animated: true) - case .resetSession: - guard let client = message.systemMessageData?.clients.first as? UserClient else { return } - activityIndicator.start() - userClientToken = UserClientChangeInfo.add(observer: self, for: client) - client.resetSession() case let .react(reaction): userSession.perform { let useCase = self.userSession.makeToggleMessageReactionUseCase() @@ -220,18 +215,6 @@ extension ConversationContentViewController { } } -// MARK: - UserClientObserver - -extension ConversationContentViewController: UserClientObserver { - - func userClientDidChange(_ changeInfo: UserClientChangeInfo) { - if changeInfo.sessionHasBeenReset { - userClientToken = nil - activityIndicator.stop() - } - } -} - // MARK: - SignatureObserver extension ConversationContentViewController: SignatureObserver { diff --git a/wire-ios/Wire-iOS/Sources/UserInterface/Conversation/Content/ConversationContentViewController.swift b/wire-ios/Wire-iOS/Sources/UserInterface/Conversation/Content/ConversationContentViewController.swift index cb5a8e0e923..ca6894d24ac 100644 --- a/wire-ios/Wire-iOS/Sources/UserInterface/Conversation/Content/ConversationContentViewController.swift +++ b/wire-ios/Wire-iOS/Sources/UserInterface/Conversation/Content/ConversationContentViewController.swift @@ -119,7 +119,6 @@ final class ConversationContentViewController: UIViewController { let selfProfileUIBuilder: SelfProfileViewControllerBuilderProtocol var connectionViewController: UserConnectionViewController? var digitalSignatureToken: Any? - var userClientToken: Any? var isDigitalSignatureVerificationShown: Bool = false private var mediaPlaybackManager: MediaPlaybackManager? From b0155aee086b0c7d262497ef158bbfeeb94905fc Mon Sep 17 00:00:00 2001 From: David-Henner Date: Tue, 2 Dec 2025 17:25:13 +0100 Subject: [PATCH 24/28] remove dependency on cryptobox from `MockUserType` --- .../Tests/Mocks/MockUserType+SelfLegalHoldSubject.swift | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/wire-ios/Tests/Mocks/MockUserType+SelfLegalHoldSubject.swift b/wire-ios/Tests/Mocks/MockUserType+SelfLegalHoldSubject.swift index 8d1e03ea4bd..9dbc4ca3512 100644 --- a/wire-ios/Tests/Mocks/MockUserType+SelfLegalHoldSubject.swift +++ b/wire-ios/Tests/Mocks/MockUserType+SelfLegalHoldSubject.swift @@ -35,11 +35,7 @@ extension MockUserType: SelfLegalHoldSubject { } var fingerprint: String? { - guard let preKey = legalHoldDataSource.legalHoldRequest?.lastPrekey, - let fingerprintData = EncryptionSessionsDirectory.fingerprint(fromPrekey: preKey.key) else { - return nil - } - return String(decoding: fingerprintData, as: UTF8.self) + nil } func legalHoldRequestWasCancelled() { From 8f5c215ebca23c8bfcbb177b16457602c96282ea Mon Sep 17 00:00:00 2001 From: David-Henner Date: Wed, 3 Dec 2025 12:02:33 +0100 Subject: [PATCH 25/28] remove share cryptobox developer setting --- ...tingsCellDescriptorFactory+Developer.swift | 1 - .../SettingsShareDatabaseCellDescriptor.swift | 33 ------------------- 2 files changed, 34 deletions(-) diff --git a/wire-ios/Wire-iOS/Sources/UserInterface/Settings/CellDescriptors/SettingsCellDescriptorFactory+Developer.swift b/wire-ios/Wire-iOS/Sources/UserInterface/Settings/CellDescriptors/SettingsCellDescriptorFactory+Developer.swift index a228925b267..954dd54994d 100644 --- a/wire-ios/Wire-iOS/Sources/UserInterface/Settings/CellDescriptors/SettingsCellDescriptorFactory+Developer.swift +++ b/wire-ios/Wire-iOS/Sources/UserInterface/Settings/CellDescriptors/SettingsCellDescriptorFactory+Developer.swift @@ -65,7 +65,6 @@ extension SettingsCellDescriptorFactory { ) developerCellDescriptors.append(SettingsShareDatabaseCellDescriptor()) - developerCellDescriptors.append(SettingsShareCryptoboxCellDescriptor()) developerCellDescriptors.append( Button( diff --git a/wire-ios/Wire-iOS/Sources/UserInterface/Settings/CellDescriptors/SettingsShareDatabaseCellDescriptor.swift b/wire-ios/Wire-iOS/Sources/UserInterface/Settings/CellDescriptors/SettingsShareDatabaseCellDescriptor.swift index 2b8b7586bdb..51b0ba513e3 100644 --- a/wire-ios/Wire-iOS/Sources/UserInterface/Settings/CellDescriptors/SettingsShareDatabaseCellDescriptor.swift +++ b/wire-ios/Wire-iOS/Sources/UserInterface/Settings/CellDescriptors/SettingsShareDatabaseCellDescriptor.swift @@ -58,36 +58,3 @@ final class SettingsShareDatabaseCellDescriptor: SettingsButtonCellDescriptor { } } - -final class SettingsShareCryptoboxCellDescriptor: SettingsButtonCellDescriptor { - - let documentDelegate: DocumentDelegate - - init() { - let documentDelegate = DocumentDelegate() - self.documentDelegate = documentDelegate - - super.init(title: "Share Cryptobox", isDestructive: false) { _ in - guard let userSession = ZMUserSession.shared() else { return } - let fileURL = userSession.managedObjectContext.zm_storeURL! - .deletingLastPathComponent() - .deletingLastPathComponent() - .appending(path: "otr", directoryHint: .isDirectory) - let archiveURL = fileURL.appendingPathExtension("zip") - - try? FileManager.default.removeItem(at: archiveURL) - try? FileManager.default.zipItem( - at: fileURL, - to: archiveURL, - shouldKeepParent: false, - compressionMethod: .deflate - ) - - let shareDatabaseDocumentController = UIDocumentInteractionController(url: archiveURL) - shareDatabaseDocumentController.delegate = documentDelegate - shareDatabaseDocumentController.presentPreview(animated: true) - } - - } - -} From aab99a81e07fc6082587436e0f9d083010d2becf Mon Sep 17 00:00:00 2001 From: David-Henner Date: Wed, 3 Dec 2025 12:02:43 +0100 Subject: [PATCH 26/28] rename test --- .../Source/UserSession/ZMClientRegistrationStatusTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wire-ios-sync-engine/Tests/Source/UserSession/ZMClientRegistrationStatusTests.swift b/wire-ios-sync-engine/Tests/Source/UserSession/ZMClientRegistrationStatusTests.swift index 05f32c4b31a..24f84ed9795 100644 --- a/wire-ios-sync-engine/Tests/Source/UserSession/ZMClientRegistrationStatusTests.swift +++ b/wire-ios-sync-engine/Tests/Source/UserSession/ZMClientRegistrationStatusTests.swift @@ -232,7 +232,7 @@ final class ZMClientRegistrationStatusTests: MessagingTest { } } - func testThatItInvalidatesSelfClientAndDeletesAndRecreatesCryptoBoxOnDidDetectCurrentClientDeletion() { + func testThatItInvalidatesSelfClient_OnDidDetectCurrentClientDeletion() { syncMOC.performAndWait { // given let selfUser = ZMUser.selfUser(in: self.syncMOC) From bf3da3261f580c9adb1a12f0142c75b669b2cbc7 Mon Sep 17 00:00:00 2001 From: David-Henner Date: Fri, 12 Dec 2025 14:42:41 +0100 Subject: [PATCH 27/28] format --- .../Source/Clients and OTR/MockPendingLegalHoldClient.swift | 2 +- .../Source/Clients and OTR/MockUserClient.swift | 2 +- .../ConversationCannotDecryptSystemMessageCellDescription.swift | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/wire-ios-mocktransport/Source/Clients and OTR/MockPendingLegalHoldClient.swift b/wire-ios-mocktransport/Source/Clients and OTR/MockPendingLegalHoldClient.swift index 0cdfa0558fe..c76506480d5 100644 --- a/wire-ios-mocktransport/Source/Clients and OTR/MockPendingLegalHoldClient.swift +++ b/wire-ios-mocktransport/Source/Clients and OTR/MockPendingLegalHoldClient.swift @@ -59,7 +59,7 @@ public extension MockUser { pendingClient.identifier = identifier // Generate mock prekey strings - let prekeysStrings = (0..<5).map { _ in UUID().uuidString } + let prekeysStrings = (0 ..< 5).map { _ in UUID().uuidString } let prekeys = MockPreKey.insertNewKeys( withPayload: prekeysStrings, diff --git a/wire-ios-mocktransport/Source/Clients and OTR/MockUserClient.swift b/wire-ios-mocktransport/Source/Clients and OTR/MockUserClient.swift index 7930eb71efe..c339163fb5f 100644 --- a/wire-ios-mocktransport/Source/Clients and OTR/MockUserClient.swift +++ b/wire-ios-mocktransport/Source/Clients and OTR/MockUserClient.swift @@ -162,7 +162,7 @@ public extension MockUserClient { newClient.time = Date() // Generate mock prekey strings (no encryption needed for mock transport) - let mockPrekeys = (0..<5).map { _ in UUID().uuidString } + let mockPrekeys = (0 ..< 5).map { _ in UUID().uuidString } let mockLastPrekey = UUID().uuidString let prekeys = MockPreKey.insertNewKeys( diff --git a/wire-ios/Wire-iOS/Sources/UserInterface/Conversation/Content/Cells/ConfigurationMessageCell/Components/ConversationCannotDecryptSystemMessageCellDescription.swift b/wire-ios/Wire-iOS/Sources/UserInterface/Conversation/Content/Cells/ConfigurationMessageCell/Components/ConversationCannotDecryptSystemMessageCellDescription.swift index ec90e8fd09f..b1a65c27f1a 100644 --- a/wire-ios/Wire-iOS/Sources/UserInterface/Conversation/Content/Cells/ConfigurationMessageCell/Components/ConversationCannotDecryptSystemMessageCellDescription.swift +++ b/wire-ios/Wire-iOS/Sources/UserInterface/Conversation/Content/Cells/ConfigurationMessageCell/Components/ConversationCannotDecryptSystemMessageCellDescription.swift @@ -97,7 +97,6 @@ final class ConversationCannotDecryptSystemMessageCellDescription: ConversationM switch systemMessage.systemMessageType { case .decryptionFailed: components = [messageString] - case .decryptionFailedResolved: components = [ messageString, From f79a37c267ae8983ba3386115feec8f076b825ec Mon Sep 17 00:00:00 2001 From: David-Henner Date: Sun, 14 Dec 2025 18:34:26 +0100 Subject: [PATCH 28/28] Apply suggestion from @David-Henner --- wire-ios/Wire-iOS.xcodeproj/project.pbxproj | 1 - 1 file changed, 1 deletion(-) diff --git a/wire-ios/Wire-iOS.xcodeproj/project.pbxproj b/wire-ios/Wire-iOS.xcodeproj/project.pbxproj index b33548318dd..f3c018f9aa7 100644 --- a/wire-ios/Wire-iOS.xcodeproj/project.pbxproj +++ b/wire-ios/Wire-iOS.xcodeproj/project.pbxproj @@ -232,7 +232,6 @@ dstSubfolderSpec = 10; files = ( 5996E8A72C19D090007A52F0 /* WireSyncEngine.framework in Embed Frameworks */, - 591B6E182C8B095B009F8A7B /* WireNotificationEngine.framework in Embed Frameworks */, EE33C48E296485FA00C058D1 /* WireShareEngine.framework in Embed Frameworks */, EE67F73F296F0E7D001D7C88 /* Ziphy.framework in Embed Frameworks */, EEE25B4229719C750008B894 /* WireRequestStrategy.framework in Embed Frameworks */,