diff --git a/WireAPI/Sources/WireAPI/APIs/MLSAPI/MLSAPI.swift b/WireAPI/Sources/WireAPI/APIs/MLSAPI/MLSAPI.swift index 7999f10b4cf..f10d23174e8 100644 --- a/WireAPI/Sources/WireAPI/APIs/MLSAPI/MLSAPI.swift +++ b/WireAPI/Sources/WireAPI/APIs/MLSAPI/MLSAPI.swift @@ -25,4 +25,14 @@ public protocol MLSAPI { func getBackendMLSPublicKeys() async throws -> BackendMLSPublicKeys + /// Post a commit bundle. + /// + /// - Parameter bundle: commit bundle to post + /// - Returns: updates events generated by the commit + /// + /// Available from ``APIVersion`` v5. + /// + + func postCommitBundle(_ bundle: CommitBundle) async throws -> [UpdateEvent] + } diff --git a/WireAPI/Sources/WireAPI/APIs/MLSAPI/MLSAPIError.swift b/WireAPI/Sources/WireAPI/APIs/MLSAPI/MLSAPIError.swift index 59b8f584ce4..443e1457caf 100644 --- a/WireAPI/Sources/WireAPI/APIs/MLSAPI/MLSAPIError.swift +++ b/WireAPI/Sources/WireAPI/APIs/MLSAPI/MLSAPIError.swift @@ -20,7 +20,17 @@ import Foundation /// Errors originating from `MLSAPI`. -public enum MLSAPIError: Error { +public enum MLSAPIError: Error, Codable, Equatable { + + public init(from string: String) throws { + self = try JSONDecoder().decode(MLSAPIError.self, from: Data(string.utf8)) + } + + public func encodeAsString() throws -> String { + let encoder = JSONEncoder() + encoder.outputFormatting = .sortedKeys + return String(decoding: try encoder.encode(self), as: UTF8.self) + } /// Unsupported endpoint for API version @@ -30,4 +40,20 @@ public enum MLSAPIError: Error { case mlsNotEnabled + /// Message was sent in an too old epoch + + case mlsStaleMessage + + /// A proposal of type Add or Remove does not apply to the full list of clients for a user + + case mlsClientMismatch + + /// The commit is not referencing all pending proposals + + case mlsCommitMissingReferences + + /// Generic error for all non recoverable MLS error + + case mlsError(_ label: String, _ message: String) + } diff --git a/WireAPI/Sources/WireAPI/APIs/MLSAPI/MLSAPIV0.swift b/WireAPI/Sources/WireAPI/APIs/MLSAPI/MLSAPIV0.swift index a6b3e71bdd8..d1103525876 100644 --- a/WireAPI/Sources/WireAPI/APIs/MLSAPI/MLSAPIV0.swift +++ b/WireAPI/Sources/WireAPI/APIs/MLSAPI/MLSAPIV0.swift @@ -38,4 +38,8 @@ class MLSAPIV0: MLSAPI, VersionedAPI { throw MLSAPIError.unsupportedEndpointForAPIVersion } + func postCommitBundle(_ bundle: CommitBundle) async throws -> [UpdateEvent] { + throw MLSAPIError.unsupportedEndpointForAPIVersion + } + } diff --git a/WireAPI/Sources/WireAPI/APIs/MLSAPI/MLSAPIV5.swift b/WireAPI/Sources/WireAPI/APIs/MLSAPI/MLSAPIV5.swift index d2bd7314a87..b5920a9cc51 100644 --- a/WireAPI/Sources/WireAPI/APIs/MLSAPI/MLSAPIV5.swift +++ b/WireAPI/Sources/WireAPI/APIs/MLSAPI/MLSAPIV5.swift @@ -41,6 +41,40 @@ class MLSAPIV5: MLSAPIV4 { .parse(code: response.statusCode, data: data) } + override func postCommitBundle(_ bundle: CommitBundle) async throws -> [UpdateEvent] { + let request = try URLRequestBuilder(path: "\(pathPrefix)/mls/commit-bundles") + .withMethod(.post) + .withAcceptType(.json) + .withBody(bundle.transportData(), contentType: .mls) + .build() + + let (data, response) = try await apiService.executeRequest( + request, + requiringAccessToken: true + ) + + do { + return try ResponseParser() + .success(code: .created, type: CommitBundleResponseV5.self) + .failure(code: .conflict, label: "mls-stale-message", error: MLSAPIError.mlsStaleMessage) + .failure(code: .conflict, label: "mls-client-mismatch", error: MLSAPIError.mlsClientMismatch) + .failure( + code: .badRequest, + label: "mls-commit-missing-references", + error: MLSAPIError.mlsCommitMissingReferences + ) + .failure(code: .conflict, decodableError: FailureResponse.self) + .parse(code: response.statusCode, data: data) + } catch { + if let failureResponse = error as? FailureResponse { + throw MLSAPIError.mlsError(failureResponse.label, failureResponse.message) + } else { + throw error + } + } + + } + } private struct BackendMLSPublicKeysResponseV5: Decodable, ToAPIModelConvertible { @@ -52,3 +86,14 @@ private struct BackendMLSPublicKeysResponseV5: Decodable, ToAPIModelConvertible } } + +private struct CommitBundleResponseV5: Decodable, ToAPIModelConvertible { + + let time: UTCTime? + let events: [UpdateEventDecodingProxy] + + func toAPIModel() -> [UpdateEvent] { + events.map(\.updateEvent) + } + +} diff --git a/wire-ios-data-model/Source/MLS/ExternalCommitError.swift b/WireAPI/Sources/WireAPI/Models/MLS/CommitBundle.swift similarity index 54% rename from wire-ios-data-model/Source/MLS/ExternalCommitError.swift rename to WireAPI/Sources/WireAPI/Models/MLS/CommitBundle.swift index 829d54a3365..48ed543eac1 100644 --- a/wire-ios-data-model/Source/MLS/ExternalCommitError.swift +++ b/WireAPI/Sources/WireAPI/Models/MLS/CommitBundle.swift @@ -18,35 +18,30 @@ import Foundation -enum ExternalCommitError: Error, Equatable { +public struct CommitBundle: Sendable, Equatable { - case failedToSendCommit(recovery: RecoveryStrategy, cause: SendCommitBundleAction.Failure) - case failedToMergePendingGroup - case failedToClearPendingGroup + public var welcome: Data? - enum RecoveryStrategy { + public var commit: Data - /// Retry the action from the beginning - case retry - - /// Abort the action and log the error - case giveUp + public var groupInfo: Data + public init(welcome: Data?, commit: Data, groupInfo: Data) { + self.welcome = welcome + self.commit = commit + self.groupInfo = groupInfo } -} -extension ExternalCommitError.RecoveryStrategy { + func transportData() -> Data { + var data = Data() + data.append(commit) - /// Whether the pending group should be cleared + if let welcome { + data.append(welcome) + } - var shouldClearPendingGroup: Bool { - switch self { - case .retry: - false + data.append(groupInfo) - case .giveUp: - true - } + return data } - } diff --git a/WireAPI/Sources/WireAPI/Network/URLRequestBuilder/HTTPContentType.swift b/WireAPI/Sources/WireAPI/Network/URLRequestBuilder/HTTPContentType.swift index 7fe4c8dc725..a9f5d60826d 100644 --- a/WireAPI/Sources/WireAPI/Network/URLRequestBuilder/HTTPContentType.swift +++ b/WireAPI/Sources/WireAPI/Network/URLRequestBuilder/HTTPContentType.swift @@ -20,4 +20,6 @@ enum HTTPContentType: String { case json = "application/json" + case mls = "message/mls" + } diff --git a/WireAPI/Tests/WireAPITests/APIs/MLSAPI/MLSAPITests.swift b/WireAPI/Tests/WireAPITests/APIs/MLSAPI/MLSAPITests.swift index f95e6fede02..a28ee713e23 100644 --- a/WireAPI/Tests/WireAPITests/APIs/MLSAPI/MLSAPITests.swift +++ b/WireAPI/Tests/WireAPITests/APIs/MLSAPI/MLSAPITests.swift @@ -103,6 +103,83 @@ final class MLSAPITests: XCTestCase { try await api.getBackendMLSPublicKeys() } } + + // MARK: - Send commit bundle + + func testPostCommitBundleRequest() async throws { + // Given + let apiVersions = APIVersion.v5.andNextVersions + + // Then + try await apiSnapshotHelper.verifyRequest(for: apiVersions) { sut in + // When + _ = try await sut.postCommitBundle(Scaffolding.commitBundle) + } + } + + func testPostCommitBundle_SuccessResponse_201_V5_And_Next_Versions() async throws { + // Given + try await withThrowingTaskGroup(of: [UpdateEvent].self) { taskGroup in + let testedVersions = APIVersion.v5.andNextVersions + + for version in testedVersions { + let apiService = MockAPIServiceProtocol.withResponses([ + (.created, "PostCommitBundleSuccessResponse1") + ]) + let sut = version.buildAPI(apiService: apiService) + + taskGroup.addTask { + // When + try await sut.postCommitBundle(Scaffolding.commitBundle) + } + + for try await value in taskGroup { + // Then + XCTAssertEqual(value, [], "should get 201 for APIVersion \(version)") + } + } + } + } + + func testPostCommitBundle_SuccessResponseWithEvents_201_V5_And_Next_Versions() async throws { + // Given + try await withThrowingTaskGroup(of: [UpdateEvent].self) { taskGroup in + let testedVersions = APIVersion.v5.andNextVersions + + for version in testedVersions { + let apiService = MockAPIServiceProtocol.withResponses([ + (.created, "PostCommitBundleSuccessResponse2") + ]) + let sut = version.buildAPI(apiService: apiService) + + taskGroup.addTask { + // When + try await sut.postCommitBundle(Scaffolding.commitBundle) + } + + for try await value in taskGroup { + // Then + XCTAssertEqual(value, Scaffolding.updateEvents, "should get 201 for APIVersion \(version)") + } + } + } + } + + func testPostCommitBundle_givenV5AndErrorResponse() async throws { + // Given + let apiService = MockAPIServiceProtocol.withError( + statusCode: .conflict, + label: "mls-stale-message" + ) + + let api = MLSAPIV5(apiService: apiService) + + // Then + await XCTAssertThrowsErrorAsync(MLSAPIError.mlsStaleMessage) { + // When + try await api.postCommitBundle(Scaffolding.commitBundle) + } + } } private extension APIVersion { @@ -113,3 +190,19 @@ private extension APIVersion { } } + +// MARK: Helpers + +private enum Scaffolding { + + static let commitBundle = CommitBundle( + welcome: nil, + commit: Data("commit".utf8), + groupInfo: Data("groupinfo".utf8) + ) + + static let updateEvents = [ + UpdateEvent.unknown(eventType: "some event") + ] + +} diff --git a/WireAPI/Tests/WireAPITests/APIs/MLSAPI/Resources/PostCommitBundleSuccessResponse1.json b/WireAPI/Tests/WireAPITests/APIs/MLSAPI/Resources/PostCommitBundleSuccessResponse1.json new file mode 100644 index 00000000000..4ce2e945720 --- /dev/null +++ b/WireAPI/Tests/WireAPITests/APIs/MLSAPI/Resources/PostCommitBundleSuccessResponse1.json @@ -0,0 +1,4 @@ +{ + "events":[], + "time":"2025-03-31T15:09:38.196Z" +} diff --git a/WireAPI/Tests/WireAPITests/APIs/MLSAPI/Resources/PostCommitBundleSuccessResponse2.json b/WireAPI/Tests/WireAPITests/APIs/MLSAPI/Resources/PostCommitBundleSuccessResponse2.json new file mode 100644 index 00000000000..4d0c4a571d6 --- /dev/null +++ b/WireAPI/Tests/WireAPITests/APIs/MLSAPI/Resources/PostCommitBundleSuccessResponse2.json @@ -0,0 +1,6 @@ +{ + "events":[{ + "type": "some event" + }], + "time":"2025-04-01T10:28:55.622Z" +} diff --git a/WireAPI/Tests/WireAPITests/APIs/MLSAPI/__Snapshots__/MLSAPITests/testPostCommitBundleRequest.request-0-v5.txt b/WireAPI/Tests/WireAPITests/APIs/MLSAPI/__Snapshots__/MLSAPITests/testPostCommitBundleRequest.request-0-v5.txt new file mode 100644 index 00000000000..1c9009cd194 --- /dev/null +++ b/WireAPI/Tests/WireAPITests/APIs/MLSAPI/__Snapshots__/MLSAPITests/testPostCommitBundleRequest.request-0-v5.txt @@ -0,0 +1,6 @@ +curl \ + --request POST \ + --header "Accept: application/json" \ + --header "Content-Type: message/mls" \ + --data "commitgroupinfo" \ + "/v5/mls/commit-bundles" \ No newline at end of file diff --git a/WireAPI/Tests/WireAPITests/APIs/MLSAPI/__Snapshots__/MLSAPITests/testPostCommitBundleRequest.request-0-v6.txt b/WireAPI/Tests/WireAPITests/APIs/MLSAPI/__Snapshots__/MLSAPITests/testPostCommitBundleRequest.request-0-v6.txt new file mode 100644 index 00000000000..be38b5975ea --- /dev/null +++ b/WireAPI/Tests/WireAPITests/APIs/MLSAPI/__Snapshots__/MLSAPITests/testPostCommitBundleRequest.request-0-v6.txt @@ -0,0 +1,6 @@ +curl \ + --request POST \ + --header "Accept: application/json" \ + --header "Content-Type: message/mls" \ + --data "commitgroupinfo" \ + "/v6/mls/commit-bundles" \ No newline at end of file diff --git a/WireAPI/Tests/WireAPITests/APIs/MLSAPI/__Snapshots__/MLSAPITests/testPostCommitBundleRequest.request-0-v7.txt b/WireAPI/Tests/WireAPITests/APIs/MLSAPI/__Snapshots__/MLSAPITests/testPostCommitBundleRequest.request-0-v7.txt new file mode 100644 index 00000000000..dd134f5a26c --- /dev/null +++ b/WireAPI/Tests/WireAPITests/APIs/MLSAPI/__Snapshots__/MLSAPITests/testPostCommitBundleRequest.request-0-v7.txt @@ -0,0 +1,6 @@ +curl \ + --request POST \ + --header "Accept: application/json" \ + --header "Content-Type: message/mls" \ + --data "commitgroupinfo" \ + "/v7/mls/commit-bundles" \ No newline at end of file diff --git a/WireAPI/Tests/WireAPITests/APIs/MLSAPI/__Snapshots__/MLSAPITests/testPostCommitBundleRequest.request-0-v8.txt b/WireAPI/Tests/WireAPITests/APIs/MLSAPI/__Snapshots__/MLSAPITests/testPostCommitBundleRequest.request-0-v8.txt new file mode 100644 index 00000000000..c7528d48150 --- /dev/null +++ b/WireAPI/Tests/WireAPITests/APIs/MLSAPI/__Snapshots__/MLSAPITests/testPostCommitBundleRequest.request-0-v8.txt @@ -0,0 +1,6 @@ +curl \ + --request POST \ + --header "Accept: application/json" \ + --header "Content-Type: message/mls" \ + --data "commitgroupinfo" \ + "/v8/mls/commit-bundles" \ No newline at end of file diff --git a/WireCoreCrypto/Package.swift b/WireCoreCrypto/Package.swift index 4da0d90fb74..ca0afcdfdb8 100644 --- a/WireCoreCrypto/Package.swift +++ b/WireCoreCrypto/Package.swift @@ -11,14 +11,25 @@ let package = Package( .library( name: "WireCoreCrypto", targets: ["WireCoreCrypto"] + ), + .library( + name: "WireCoreCryptoUniffi", + targets: ["WireCoreCryptoUniffi"] ) ], dependencies: [], targets: [ .binaryTarget( name: "WireCoreCrypto", - url: "https://github.com/wireapp/core-crypto/releases/download/v3.1.1/WireCoreCrypto.xcframework.zip", - checksum: "fc1ec9eb58d6324ab32c34d131c7a22838e076e16461b00da3506e3be3488011" + url: "https://github.com/wireapp/core-crypto/releases/download/v5.3.0/WireCoreCrypto.xcframework.zip", + checksum: "d291cf8ef997b1414448890446893465bb2147f23b396e92e6a8098de948b7f9" + ), + // this is an internal dependency to WireCoreCrypto but currently needs to explictly + // added as a dependency due to limitations of Swift packages. + .binaryTarget( + name: "WireCoreCryptoUniffi", + url: "https://github.com/wireapp/core-crypto/releases/download/v5.3.0/WireCoreCryptoUniffi.xcframework.zip", + checksum: "4931c7473c83e157f5c89a6e6dda9a087d746e97f9b0a4443b106cb56e5b8789" ) ] ) diff --git a/WireDomain/Sources/WireDomain/Components/ClientSessionComponent.swift b/WireDomain/Sources/WireDomain/Components/ClientSessionComponent.swift index 0473e677e53..73b5f9e9b6a 100644 --- a/WireDomain/Sources/WireDomain/Components/ClientSessionComponent.swift +++ b/WireDomain/Sources/WireDomain/Components/ClientSessionComponent.swift @@ -19,6 +19,7 @@ import Combine import Foundation import WireAPI +import WireCoreCrypto import WireDataModel public final class ClientSessionComponent { @@ -584,24 +585,25 @@ public final class ClientSessionComponent { localStore: conversationLocalStore ) + private lazy var conversationEventProcessor = ConversationEventProcessor( + accessUpdateEventProcessor: conversationAccessUpdateEventProcessor, + createEventProcessor: conversationCreateEventProcessor, + deleteEventProcessor: conversationDeleteEventProcessor, + memberJoinEventProcessor: conversationMemberJoinEventProcessor, + memberLeaveEventProcessor: conversationMemberLeaveEventProcessor, + memberUpdateEventProcessor: conversationMemberUpdateEventProcessor, + messageTimerUpdateEventProcessor: conversationMessageTimerUpdateEventProcessor, + mlsMessageAddEventProcessor: conversationMLSMessageAddEventProcessor, + mlsWelcomeEventProcessor: conversationMLSWelcomeEventProcessor, + proteusMessageAddEventProcessor: conversationProteusMessageAddEventProcessor, + protocolUpdateEventProcessor: conversationProtocolUpdateEventProcessor, + receiptModeUpdateEventProcessor: conversationReceiptModeUpdateEventProcessor, + renameEventProcessor: conversationRenameEventProcessor, + typingEventProcessor: conversationTypingEventProcessor, + addPermissionEventProcessor: addPermissionEventProcessor + ) + private lazy var updateEventProcessor: UpdateEventProcessor = { - let conversationEventProcessor = ConversationEventProcessor( - accessUpdateEventProcessor: conversationAccessUpdateEventProcessor, - createEventProcessor: conversationCreateEventProcessor, - deleteEventProcessor: conversationDeleteEventProcessor, - memberJoinEventProcessor: conversationMemberJoinEventProcessor, - memberLeaveEventProcessor: conversationMemberLeaveEventProcessor, - memberUpdateEventProcessor: conversationMemberUpdateEventProcessor, - messageTimerUpdateEventProcessor: conversationMessageTimerUpdateEventProcessor, - mlsMessageAddEventProcessor: conversationMLSMessageAddEventProcessor, - mlsWelcomeEventProcessor: conversationMLSWelcomeEventProcessor, - proteusMessageAddEventProcessor: conversationProteusMessageAddEventProcessor, - protocolUpdateEventProcessor: conversationProtocolUpdateEventProcessor, - receiptModeUpdateEventProcessor: conversationReceiptModeUpdateEventProcessor, - renameEventProcessor: conversationRenameEventProcessor, - typingEventProcessor: conversationTypingEventProcessor, - addPermissionEventProcessor: addPermissionEventProcessor - ) let featureConfigEventProcessor = FeatureConfigEventProcessor( updateEventProcessor: featureConfigUpdateEventProcessor @@ -676,4 +678,9 @@ public final class ClientSessionComponent { isMLSEnabled: isMLSEnabled ) + public lazy var mlsTransport: any WireCoreCryptoUniffi.MlsTransport = MLSTransportImpl( + mlsAPI: mlsAPI, + conversationEventProcessor: conversationEventProcessor + ) + } diff --git a/WireDomain/Sources/WireDomain/Event Processing/ConversationEventProcessor/Protocols/ConversationEventProcessorProtocol.swift b/WireDomain/Sources/WireDomain/Event Processing/ConversationEventProcessor/Protocols/ConversationEventProcessorProtocol.swift index 9baa424339f..e8a9b6fb89b 100644 --- a/WireDomain/Sources/WireDomain/Event Processing/ConversationEventProcessor/Protocols/ConversationEventProcessorProtocol.swift +++ b/WireDomain/Sources/WireDomain/Event Processing/ConversationEventProcessor/Protocols/ConversationEventProcessorProtocol.swift @@ -18,8 +18,8 @@ import WireAPI +// sourcery: AutoMockable /// Process conversation update events. - protocol ConversationEventProcessorProtocol { /// Process a conversation update event. diff --git a/WireDomain/Sources/WireDomain/Notifications/Components/PullEventsStep.swift b/WireDomain/Sources/WireDomain/Notifications/Components/PullEventsStep.swift index babf4739143..46077e28fe1 100644 --- a/WireDomain/Sources/WireDomain/Notifications/Components/PullEventsStep.swift +++ b/WireDomain/Sources/WireDomain/Notifications/Components/PullEventsStep.swift @@ -145,14 +145,8 @@ extension PullEventsStep { } var mlsMessageDecryptor: any MLSMessageDecryptorProtocol { - let commitSender = CommitSender( - coreCryptoProvider: coreCryptoProvider, - notificationContext: dependency.coreData.syncContext.notificationContext - ) - let mlsActionExecutor = MLSActionExecutor( coreCryptoProvider: coreCryptoProvider, - commitSender: commitSender, featureRepository: featureRepository ) diff --git a/WireDomain/Sources/WireDomain/Synchronization/MLSModelMappings.swift b/WireDomain/Sources/WireDomain/Synchronization/MLSModelMappings.swift new file mode 100644 index 00000000000..cc2fa08c568 --- /dev/null +++ b/WireDomain/Sources/WireDomain/Synchronization/MLSModelMappings.swift @@ -0,0 +1,31 @@ +// +// 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 WireAPI +import WireCoreCrypto + +extension WireCoreCryptoUniffi.CommitBundle { + + func toAPIModel() -> WireAPI.CommitBundle { + WireAPI.CommitBundle( + welcome: welcome, + commit: commit, + groupInfo: groupInfo.payload + ) + } +} diff --git a/WireDomain/Sources/WireDomain/Synchronization/MLSTransportImpl.swift b/WireDomain/Sources/WireDomain/Synchronization/MLSTransportImpl.swift new file mode 100644 index 00000000000..cbb0d414625 --- /dev/null +++ b/WireDomain/Sources/WireDomain/Synchronization/MLSTransportImpl.swift @@ -0,0 +1,69 @@ +// +// 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 WireAPI +import WireCoreCrypto +import WireLogging + +final class MLSTransportImpl: MlsTransport { + + let mlsAPI: MLSAPI + let conversationEventProcessor: ConversationEventProcessorProtocol + + init(mlsAPI: MLSAPI, conversationEventProcessor: ConversationEventProcessorProtocol) { + self.mlsAPI = mlsAPI + self.conversationEventProcessor = conversationEventProcessor + } + + func sendCommitBundle(commitBundle: WireCoreCryptoUniffi.CommitBundle) async -> WireCoreCryptoUniffi + .MlsTransportResponse { + let events: [UpdateEvent] + + do { + events = try await mlsAPI.postCommitBundle(commitBundle.toAPIModel()) + } catch let error as MLSAPIError { + do { + return .abort(reason: try error.encodeAsString()) + } catch { + return .abort(reason: "failed to encode error") + } + } catch { + return .abort(reason: error.localizedDescription) + } + + for event in events { + do { + if case let .conversation(conversationEvent) = event { + try await conversationEventProcessor.processEvent(conversationEvent) + } + } catch { + WireLogger.mls + .error( + "Commit bundle was accepted by the backend so can't roll back after failing to process conversation event)" + ) + } + } + + return .success + } + + func sendMessage(mlsMessage: Data) async -> WireCoreCryptoUniffi.MlsTransportResponse { + .abort(reason: "not implemented") + } + +} diff --git a/WireDomain/Sources/WireDomain/Synchronization/PullMLSStatusSync.swift b/WireDomain/Sources/WireDomain/Synchronization/PullMLSStatusSync.swift index 7800a5b1ef4..9bb98076b47 100644 --- a/WireDomain/Sources/WireDomain/Synchronization/PullMLSStatusSync.swift +++ b/WireDomain/Sources/WireDomain/Synchronization/PullMLSStatusSync.swift @@ -44,6 +44,8 @@ public struct PullMLSStatusSync: PullMLSStatusSyncProtocol { case .unsupportedEndpointForAPIVersion, .mlsNotEnabled: WireLogger.mls.info("backend has no MLS public keys") store.storeIsMLSEnabledStatus(newValue: false) + default: + throw error } } } diff --git a/WireDomain/Sources/WireDomainSupport/Sourcery/config.yml b/WireDomain/Sources/WireDomainSupport/Sourcery/config.yml index a5426ded18c..ae245c42394 100644 --- a/WireDomain/Sources/WireDomainSupport/Sourcery/config.yml +++ b/WireDomain/Sources/WireDomainSupport/Sourcery/config.yml @@ -5,5 +5,5 @@ templates: output: ./generated args: - autoMockableImports: ["WireAPI", "WireDataModel", "WireDomainPackage"] + autoMockableImports: ["WireAPI", "WireDataModel", "WireDomainPackage", "WireCoreCrypto"] autoMockableTestableImports: ["WireDomain"] diff --git a/WireDomain/Sources/WireDomainSupport/Sourcery/generated/AutoMockable.generated.swift b/WireDomain/Sources/WireDomainSupport/Sourcery/generated/AutoMockable.generated.swift index 5a43a921cfe..fdf172ec729 100644 --- a/WireDomain/Sources/WireDomainSupport/Sourcery/generated/AutoMockable.generated.swift +++ b/WireDomain/Sources/WireDomainSupport/Sourcery/generated/AutoMockable.generated.swift @@ -1,4 +1,4 @@ -// Generated using Sourcery 2.2.6 — https://github.com/krzysztofzablocki/Sourcery +// Generated using Sourcery 2.2.4 — https://github.com/krzysztofzablocki/Sourcery // DO NOT EDIT // // Wire @@ -27,6 +27,7 @@ import WireAPI import WireDataModel import WireDomainPackage +import WireCoreCrypto @testable import WireDomain @@ -373,6 +374,34 @@ class MockConversationEventNotificationBuilderProtocol: ConversationEventNotific } +class MockConversationEventProcessorProtocol: ConversationEventProcessorProtocol { + + // MARK: - Life cycle + + + + // MARK: - processEvent + + var processEvent_Invocations: [ConversationEvent] = [] + var processEvent_MockError: Error? + var processEvent_MockMethod: ((ConversationEvent) async throws -> Void)? + + func processEvent(_ event: ConversationEvent) async throws { + processEvent_Invocations.append(event) + + if let error = processEvent_MockError { + throw error + } + + guard let mock = processEvent_MockMethod else { + fatalError("no mock for `processEvent`") + } + + try await mock(event) + } + +} + class MockConversationFileUploadMessageNotificationBuilderProtocol: ConversationFileUploadMessageNotificationBuilderProtocol { // MARK: - Life cycle diff --git a/WireDomain/Tests/WireDomainTests/Synchronization/MLSTransportTests.swift b/WireDomain/Tests/WireDomainTests/Synchronization/MLSTransportTests.swift new file mode 100644 index 00000000000..4db8c65c7ca --- /dev/null +++ b/WireDomain/Tests/WireDomainTests/Synchronization/MLSTransportTests.swift @@ -0,0 +1,142 @@ +// +// 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 WireAPI +import WireCoreCrypto +import XCTest + +@testable import WireAPISupport +@testable import WireDomain +@testable import WireDomainSupport + +final class MLSTransportTests: XCTestCase { + + private var sut: MLSTransportImpl! + private var mlsAPI: MockMLSAPI! + private var conversationEventProcessor: MockConversationEventProcessorProtocol! + + override func setUp() async throws { + mlsAPI = MockMLSAPI() + conversationEventProcessor = MockConversationEventProcessorProtocol() + sut = MLSTransportImpl( + mlsAPI: mlsAPI, + conversationEventProcessor: conversationEventProcessor + ) + } + + override func tearDown() async throws { + sut = nil + mlsAPI = nil + conversationEventProcessor = nil + } + + func testOnSendCommitBundle_CommitBundleIsPostedToMLSAPI() async throws { + // Given + mlsAPI.postCommitBundle_MockValue = [] + + // When + _ = await sut.sendCommitBundle(commitBundle: Scaffolding.commitBundle) + + // Then + XCTAssertEqual(mlsAPI.postCommitBundle_Invocations, [Scaffolding.commitBundle.toAPIModel()]) + } + + func testOnSendCommitBundle_ConversationEventsAreForwaredToConversationEventProcessor() async throws { + // Given + mlsAPI.postCommitBundle_MockValue = [Scaffolding.conversationUpdateEvent] + conversationEventProcessor.processEvent_MockMethod = { _ in } + + // When + _ = await sut.sendCommitBundle(commitBundle: Scaffolding.commitBundle) + + // Then + XCTAssertEqual(conversationEventProcessor.processEvent_Invocations, [Scaffolding.conversationEvent]) + } + + func testOnSendCommitBundle_UnknownEventsAreNotForwaredToConversationEventProcessor() async throws { + // Given + mlsAPI.postCommitBundle_MockValue = [Scaffolding.unknownUpdateEvent] + conversationEventProcessor.processEvent_MockMethod = { _ in } + + // When + _ = await sut.sendCommitBundle(commitBundle: Scaffolding.commitBundle) + + // Then + XCTAssertEqual(conversationEventProcessor.processEvent_Invocations, []) + } + + func testOnSendCommitBundle_ReturnsSuccessWhenThereIsNoError() async throws { + // Given + mlsAPI.postCommitBundle_MockValue = [] + + // When + let result = await sut.sendCommitBundle(commitBundle: Scaffolding.commitBundle) + + // Then + XCTAssertEqual(result, .success) + } + + func testOnSendCommitBundle_ReturnsAbortWhenThereIsAnError() async throws { + // Given + mlsAPI.postCommitBundle_MockError = MLSAPIError.mlsStaleMessage + + // When + let result = await sut.sendCommitBundle(commitBundle: Scaffolding.commitBundle) + + // Then + XCTAssertEqual(result, .abort(reason: try MLSAPIError.mlsStaleMessage.encodeAsString())) + } + + enum Scaffolding { + + static let conversationID = ConversationID( + uuid: UUID(uuidString: "a644fa88-2d83-406b-8a85-d4fd8dedad6b")!, + domain: "example.com" + ) + + static let senderID = UserID( + uuid: UUID(uuidString: "f55fe9b0-a0cc-4b11-944b-125c834d9b6a")!, + domain: "example.com" + ) + + static let commitBundle = CommitBundle( + welcome: .random(), + commit: .random(), + groupInfo: + GroupInfoBundle( + encryptionType: .plaintext, + ratchetTreeType: .full, + payload: .random() + ) + ) + + static let conversationEvent = ConversationEvent.typing( + ConversationTypingEvent( + conversationID: conversationID, + senderID: senderID, + isTyping: true + ) + ) + + static let conversationUpdateEvent = UpdateEvent.conversation(conversationEvent) + + static let unknownUpdateEvent = UpdateEvent.unknown(eventType: "Unknown event") + + } +} diff --git a/wire-ios-data-model/Source/Core Crypto/CoreCryptoCallbacks.swift b/wire-ios-data-model/Source/Core Crypto/CoreCryptoCallbacks.swift deleted file mode 100644 index c27d3172305..00000000000 --- a/wire-ios-data-model/Source/Core Crypto/CoreCryptoCallbacks.swift +++ /dev/null @@ -1,52 +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 WireCoreCrypto - -typealias ConversationId = Data - -class CoreCryptoCallbacksImpl: CoreCryptoCallbacks { - - init() {} - - func authorize(conversationId: ConversationId, clientId: ClientId) -> Bool { - // This check is performed by the backend. - true - } - - func userAuthorize( - conversationId: ConversationId, - externalClientId: ClientId, - existingClients: [ClientId] - ) -> Bool { - // This check is performed by the backend. - true - } - - func clientIsExistingGroupUser( - conversationId: ConversationId, - clientId: ClientId, - existingClients: [ClientId], - parentConversationClients: [ClientId]? - ) -> Bool { - // This check is performed by the backend. - true - } - -} diff --git a/wire-ios-data-model/Source/Core Crypto/CoreCryptoProvider.swift b/wire-ios-data-model/Source/Core Crypto/CoreCryptoProvider.swift index 2750c9241fa..cd77fd63d50 100644 --- a/wire-ios-data-model/Source/Core Crypto/CoreCryptoProvider.swift +++ b/wire-ios-data-model/Source/Core Crypto/CoreCryptoProvider.swift @@ -42,6 +42,18 @@ public protocol CoreCryptoProviderProtocol { func initialiseMLSWithEndToEndIdentity(enrollment: E2eiEnrollment, certificateChain: String) async throws -> CRLsDistributionPoints? + /// Provide the mls transport which will be registered with the core crypto instance + /// + /// - parameters: + /// - transport: mls transport which sends mls messages to the backend + func registerMlsTransport(_ transport: any MlsTransport) + + /// Register observer of epochs + /// + /// - parameters: + /// - epochObserver: observer which will be informed on epoch changes + func registerEpochObserver(_ epochObserver: any WireCoreCryptoUniffi.EpochObserver) async + } public actor CoreCryptoProvider: CoreCryptoProviderProtocol { @@ -56,7 +68,11 @@ public actor CoreCryptoProvider: CoreCryptoProviderProtocol { private var loadingCoreCrypto = false private var initialisatingMLS = false private var hasInitialisedMLS = false + private var hasRegisteredMlsTransport = false + private var hasRegisteredEpochObserver = false private var coreCryptoContinuations: [CheckedContinuation] = [] + private nonisolated(unsafe) var mlsTransport: MlsTransport? + private var epochObserver: WireCoreCryptoUniffi.EpochObserver? public init( selfUserID: UUID, @@ -76,20 +92,24 @@ public actor CoreCryptoProvider: CoreCryptoProviderProtocol { } public func coreCrypto() async throws -> SafeCoreCryptoProtocol { - try await getCoreCrypto() + let coreCrypto = try await getCoreCrypto() + try await registerMlsTransportIfNecessary(coreCrypto: coreCrypto) + return coreCrypto } public func initialiseMLSWithBasicCredentials(mlsClientID: MLSClientID) async throws { WireLogger.mls.info("Initialising MLS client with basic credentials") let defaultCiphersuite = await featureRespository.fetchMLS().config.defaultCipherSuite - _ = try await coreCrypto().perform { coreCrypto in - try await coreCrypto.mlsInit( + let coreCrypto = try await coreCrypto() + _ = try await coreCrypto.perform { context in + try await context.mlsInit( clientId: Data(mlsClientID.rawValue.utf8), ciphersuites: [UInt16(defaultCiphersuite.rawValue)], nbKeyPackage: nil ) - try await generateClientPublicKeys(with: coreCrypto, credentialType: .basic) + try await self.generateClientPublicKeys(with: context, credentialType: .basic) } + try await registerEpochObserverIfNecessary(with: coreCrypto) } public func initialiseMLSWithEndToEndIdentity( @@ -97,15 +117,53 @@ public actor CoreCryptoProvider: CoreCryptoProviderProtocol { certificateChain: String ) async throws -> CRLsDistributionPoints? { WireLogger.mls.info("Initialising MLS client from end-to-end identity enrollment") - return try await coreCrypto().perform { coreCrypto in - let crlsDistributionPoints = try await coreCrypto.e2eiMlsInitOnly( + let coreCrypto = try await coreCrypto() + let crls = try await coreCrypto.perform { context in + let crlsDistributionPoints = try await context.e2eiMlsInitOnly( enrollment: enrollment, certificateChain: certificateChain, nbKeyPackage: nil ) - try await generateClientPublicKeys(with: coreCrypto, credentialType: .x509) + try await self.generateClientPublicKeys(with: context, credentialType: .x509) return CRLsDistributionPoints(from: crlsDistributionPoints) } + try await registerEpochObserverIfNecessary(with: coreCrypto) + return crls + } + + public func registerEpochObserver(_ epochObserver: any EpochObserver) async { + self.epochObserver = epochObserver + + do { + try await registerEpochObserverIfNecessary(with: coreCrypto()) + } catch { + WireLogger.mls.warn("Failed to register epoch observer, will try again later") + } + } + + private func registerEpochObserverIfNecessary(with coreCrypto: SafeCoreCryptoProtocol) async throws { + guard let epochObserver, !hasRegisteredEpochObserver else { + return + } + try await coreCrypto.configure { configure in + try await configure.registerEpochObserver(epochObserver) + } + hasRegisteredEpochObserver = true + } + + public nonisolated func registerMlsTransport(_ transport: any MlsTransport) { + mlsTransport = transport + } + + private func registerMlsTransportIfNecessary(coreCrypto: SafeCoreCrypto) async throws { + guard let mlsTransport, !hasRegisteredMlsTransport else { + return + } + + try await coreCrypto.configure { coreCrypto in + try await coreCrypto.provideTransport(transport: mlsTransport) + } + hasRegisteredMlsTransport = true } // Create an CoreCrypto instance with guranteees that only one task is performing @@ -197,6 +255,7 @@ public actor CoreCryptoProvider: CoreCryptoProviderProtocol { ciphersuites: [cipherSuite], nbKeyPackage: nil ) } + try await registerEpochObserverIfNecessary(with: coreCrypto) } } @@ -254,7 +313,7 @@ public actor CoreCryptoProvider: CoreCryptoProviderProtocol { } private func generateClientPublicKeys( - with coreCrypto: CoreCryptoProtocol, + with coreCrypto: CoreCryptoContextProtocol, credentialType: MlsCredentialType ) async throws { WireLogger.mls.info("generating public key") diff --git a/wire-ios-data-model/Source/Core Crypto/SafeCoreCrypto.swift b/wire-ios-data-model/Source/Core Crypto/SafeCoreCrypto.swift index 2bbcc1dfeb5..39013a05c00 100644 --- a/wire-ios-data-model/Source/Core Crypto/SafeCoreCrypto.swift +++ b/wire-ios-data-model/Source/Core Crypto/SafeCoreCrypto.swift @@ -23,8 +23,9 @@ import WireLogging // MARK: - Protocols public protocol SafeCoreCryptoProtocol { - func perform(_ block: (CoreCryptoProtocol) async throws -> T) async rethrows -> T - func unsafePerform(_ block: (CoreCryptoProtocol) async throws -> T) async rethrows -> T + func perform(_ block: @escaping (CoreCryptoContextProtocol) async throws -> T) async throws -> T + func unsafePerform(_ block: @escaping (CoreCryptoContextProtocol) async throws -> T) async throws -> T + func configure(block: (CoreCryptoProtocol) async throws -> Void) async throws func tearDown() throws } @@ -39,12 +40,12 @@ public class SafeCoreCrypto: SafeCoreCryptoProtocol { private let databasePath: String public convenience init(path: String, key: String) async throws { - let coreCrypto = try await coreCryptoDeferredInit( - path: path, - key: key + + let coreCrypto = try await CoreCrypto( + keystorePath: path, + keystoreSecret: Data(key.utf8) ) - try await coreCrypto.setCallbacks(callbacks: CoreCryptoCallbacksImpl()) setLogger(logger: CoreCryptoLoggerProxy(), level: .info) self.init(coreCrypto: coreCrypto, databasePath: path) } @@ -60,7 +61,7 @@ public class SafeCoreCrypto: SafeCoreCryptoProtocol { _ = try FileManager.default.removeItem(atPath: databasePath) } - public func perform(_ block: (CoreCryptoProtocol) async throws -> T) async rethrows -> T { + public func perform(_ block: @escaping (CoreCryptoContextProtocol) async throws -> T) async throws -> T { safeContext.acquireDirectoryLock() await restoreFromDisk() @@ -68,16 +69,23 @@ public class SafeCoreCrypto: SafeCoreCryptoProtocol { safeContext.releaseDirectoryLock() } - return try await block(coreCrypto) + return try await coreCrypto.transaction(block) + } + + public func unsafePerform(_ block: @escaping (CoreCryptoContextProtocol) async throws -> T) async throws -> T { + try await coreCrypto.transaction(block) } - public func unsafePerform(_ block: (CoreCryptoProtocol) async throws -> T) async rethrows -> T { + public func configure(block: (any CoreCryptoProtocol) async throws -> Void) async throws { try await block(coreCrypto) } private func restoreFromDisk() async { do { - try await coreCrypto.restoreFromDisk() + + try await coreCrypto.transaction { context in + try await context.proteusReloadSessions() + } } catch { WireLogger.coreCrypto.error("coreCrypto.restoreFromDisk() failed: \(error)", attributes: .safePublic) } diff --git a/wire-ios-data-model/Source/Core Crypto/TransactionExecutor.swift b/wire-ios-data-model/Source/Core Crypto/TransactionExecutor.swift deleted file mode 100644 index 9c9d7011827..00000000000 --- a/wire-ios-data-model/Source/Core Crypto/TransactionExecutor.swift +++ /dev/null @@ -1,47 +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 WireCoreCrypto - -/// Wrapper to execute CoreCrypto transactions -final class TransactionExecutor: CoreCryptoCommand { - let block: (_ context: CoreCryptoContext) async throws -> Result? - var result: Result? - - init( - _ block: @escaping (_ context: CoreCryptoContext) async throws -> Result? - ) { - self.block = block - } - - func execute(context: CoreCryptoContext) async throws { - result = try await block(context) - } -} - -extension CoreCryptoProtocol { - /// note: here we could return Result instead of Result? but to solve [WPB-16231] - /// this is the only way right now, once CC don't throw these errors we can adapt this - func transaction( - _ block: @escaping (_ context: CoreCryptoContext) async throws -> Result? - ) async throws -> Result? { - let transactionExecutor = TransactionExecutor(block) - try await transaction(command: transactionExecutor) - return transactionExecutor.result - } -} diff --git a/wire-ios-data-model/Source/E2EIdentity/E2EIService.swift b/wire-ios-data-model/Source/E2EIdentity/E2EIService.swift index 73eeb9f078d..7e3dd0ab9d0 100644 --- a/wire-ios-data-model/Source/E2EIdentity/E2EIService.swift +++ b/wire-ios-data-model/Source/E2EIdentity/E2EIService.swift @@ -131,11 +131,11 @@ public final class E2EIService: E2EIServiceInterface { public func setOIDCChallengeResponse(challenge: Data) async throws { try await coreCrypto.perform { - guard let coreCrypto = $0 as? CoreCrypto else { + guard let coreCrypto = $0 as? CoreCryptoContext else { throw E2EIServiceFailure.missingCoreCrypto } - return try await e2eIdentity.newOidcChallengeResponse(cc: coreCrypto, challenge: challenge) + return try await self.e2eIdentity.contextNewOidcChallengeResponse(cc: coreCrypto, challenge: challenge) } } diff --git a/wire-ios-data-model/Source/E2EIdentity/E2EISetupService.swift b/wire-ios-data-model/Source/E2EIdentity/E2EISetupService.swift index 908e7f318eb..bc9f04d6d99 100644 --- a/wire-ios-data-model/Source/E2EIdentity/E2EISetupService.swift +++ b/wire-ios-data-model/Source/E2EIdentity/E2EISetupService.swift @@ -72,7 +72,7 @@ public final class E2EISetupService: E2EISetupServiceInterface { public func isTrustAnchorRegistered() async throws -> Bool { try await coreCryptoProvider.coreCrypto().perform { coreCrypto in - await coreCrypto.e2eiIsPkiEnvSetup() + try await coreCrypto.e2eiIsPkiEnvSetup() } } diff --git a/wire-ios-data-model/Source/MLS/CommitError.swift b/wire-ios-data-model/Source/MLS/CommitError.swift deleted file mode 100644 index 5faca2e87a4..00000000000 --- a/wire-ios-data-model/Source/MLS/CommitError.swift +++ /dev/null @@ -1,76 +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 - -enum CommitError: Error, Equatable { - - case failedToGenerateCommit - case failedToSendCommit(recovery: RecoveryStrategy, cause: SendCommitBundleAction.Failure) - case failedToMergeCommit - case failedToClearCommit - case noPendingProposals - - enum RecoveryStrategy: Equatable { - - /// Perform a quick sync, then commit pending proposals. - /// - /// Core Crypto can automatically recover if it processes all - /// incoming handshake messages. It will migrate any pending - /// commits/proposals, which can then be committed as pending - /// proposals. - - case commitPendingProposalsAfterQuickSync - - /// Perform a quick sync, then retry the action in its entirety. - /// - /// Core Crypto can not automatically recover by itself. It needs - /// to process incoming handshake messages then generate a new commit. - - case retryAfterQuickSync - - /// Repair (re-join) the group and retry the action - /// - /// We may have missed a few commits so we will rejoin the group - /// and try again. - - case retryAfterRepairingGroup - - /// Abort the action and inform the user. - /// - /// There is no way to automatically recover from the error. - - case giveUp - - } -} - -extension CommitError.RecoveryStrategy { - - /// Whether the pending commit should be discarded. - - var shouldDiscardCommit: Bool { - switch self { - case .commitPendingProposalsAfterQuickSync: - false - - case .retryAfterQuickSync, .giveUp, .retryAfterRepairingGroup: - true - } - } -} diff --git a/wire-ios-data-model/Source/MLS/CommitSender.swift b/wire-ios-data-model/Source/MLS/CommitSender.swift deleted file mode 100644 index da0b17c3251..00000000000 --- a/wire-ios-data-model/Source/MLS/CommitSender.swift +++ /dev/null @@ -1,316 +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 Combine -import Foundation -import WireCoreCrypto -import WireLogging - -// sourcery: AutoMockable -public protocol CommitSending { - - /// Sends a commit bundle. - /// - /// - Parameters: - /// - bundle: The commit bundle to send. - /// - groupID: The group ID of the group to send the commit to. - /// - Returns: Any update events returned by the backend. - /// - Throws: `CommitError` if the operation fails. - /// - /// If the commit is sent successfully, it will be merged and the new epoch will be published. - /// New epochs can be observed with ``onEpochChanged()``. - /// - /// If the commit fails to send, the error will contain a recovery strategy to handle the failure. - /// The pending commit may be discarded based on the recovery strategy. - - func sendCommitBundle( - _ bundle: CommitBundle, - for groupID: MLSGroupID - ) async throws -> [ZMUpdateEvent] - - /// Sends an external commit bundle. - /// - /// - Parameters: - /// - bundle: The commit bundle to send. - /// - groupID: The group ID of the group to send the commit to. - /// - Returns: Any update events returned by the backend. - /// - Throws: `ExternalCommitError` if the operation fails. - /// - /// If the commit is sent successfully, the pending group will be merged. - /// - /// If the commit fails to send, the error will contain a recovery strategy to handle the failure. - /// If the recovery strategy is to give up, then the pending group will be cleared. - - func sendExternalCommitBundle( - _ bundle: CommitBundle, - for groupID: MLSGroupID - ) async throws -> [ZMUpdateEvent] - - /// Returns a publisher that emits the group ID of the group when the epoch changes. - - func onEpochChanged() -> AnyPublisher - -} - -/// An actor responsible for sending commits and external commits and handling the results. -/// In case of failures, it will provide a recovery strategy to handle the failure. - -public actor CommitSender: CommitSending { - - // MARK: - Properties - - private let coreCryptoProvider: CoreCryptoProviderProtocol - private let notificationContext: NotificationContext - private let actionsProvider: MLSActionsProviderProtocol - private let onEpochChangedSubject = PassthroughSubject() - - private var coreCrypto: SafeCoreCryptoProtocol { - get async throws { - try await coreCryptoProvider.coreCrypto() - } - } - - // MARK: - Life cycle - - public init( - coreCryptoProvider: CoreCryptoProviderProtocol, - notificationContext: NotificationContext - ) { - self.init( - coreCryptoProvider: coreCryptoProvider, - notificationContext: notificationContext, - actionsProvider: nil - ) - } - - init( - coreCryptoProvider: CoreCryptoProviderProtocol, - notificationContext: NotificationContext, - actionsProvider: MLSActionsProviderProtocol? = nil - ) { - self.coreCryptoProvider = coreCryptoProvider - self.notificationContext = notificationContext - self.actionsProvider = actionsProvider ?? MLSActionsProvider() - } - - // MARK: - Public interface - - public func sendCommitBundle( - _ bundle: CommitBundle, - for groupID: MLSGroupID - ) async throws -> [ZMUpdateEvent] { - - do { - WireLogger.mls.info( - "sending commit bundle for group", - attributes: [.mlsGroupID: groupID.safeForLoggingDescription] - ) - let events = try await sendCommitBundle(bundle) - - WireLogger.mls.info( - "merging commit for group", - attributes: [.mlsGroupID: groupID.safeForLoggingDescription] - ) - try await mergeCommit(in: groupID) - - return events - - } catch let error as SendCommitBundleAction.Failure { - WireLogger.mls.warn( - "failed to send commit bundle: \(String(describing: error))", - attributes: [.mlsGroupID: groupID.safeForLoggingDescription] - ) - - let recoveryStrategy = CommitError.RecoveryStrategy(from: error) - - if recoveryStrategy.shouldDiscardCommit { - try await discardPendingCommit(in: groupID) - } - - throw CommitError.failedToSendCommit(recovery: recoveryStrategy, cause: error) - } - } - - public func sendExternalCommitBundle( - _ bundle: CommitBundle, - for groupID: MLSGroupID - ) async throws -> [ZMUpdateEvent] { - - do { - WireLogger.mls.info( - "sending external commit bundle for group", - attributes: [.mlsGroupID: groupID.safeForLoggingDescription] - ) - let events = try await sendCommitBundle(bundle) - - WireLogger.mls.info("merging pending group", attributes: [.mlsGroupID: groupID.safeForLoggingDescription]) - try await mergePendingGroup(in: groupID) - - return events - - } catch let error as SendCommitBundleAction.Failure { - WireLogger.mls.warn( - "failed to send external commit bundle: \(String(describing: error))", - attributes: [.mlsGroupID: groupID.safeForLoggingDescription] - ) - - let recoveryStrategy = ExternalCommitError.RecoveryStrategy(from: error) - - if recoveryStrategy.shouldClearPendingGroup { - try await clearPendingGroup(in: groupID) - } - - throw ExternalCommitError.failedToSendCommit(recovery: recoveryStrategy, cause: error) - } - } - - public nonisolated - func onEpochChanged() -> AnyPublisher { - onEpochChangedSubject.eraseToAnyPublisher() - } - - // MARK: - Helpers - - private func sendCommitBundle(_ bundle: CommitBundle) async throws -> [ZMUpdateEvent] { - try await actionsProvider.sendCommitBundle( - bundle.transportData(), - in: notificationContext - ) - } - - // MARK: - Post sending - - private func mergeCommit(in groupID: MLSGroupID) async throws { - do { - WireLogger.mls.info( - "merging commit for group", - attributes: [.mlsGroupID: groupID.safeForLoggingDescription] - ) - // No need to handle buffered messages here. We will not run into a scenario where we need to handle - // buffered decrypted messages, because sending a commit and decrypting a message are non-rentrant - // operations and therefore we will never attempt to decrypt a message while sending a commit. - _ = try await coreCrypto.perform { - try await $0.commitAccepted(conversationId: groupID.data) - } - onEpochChangedSubject.send(groupID) - } catch { - WireLogger.mls.error( - "failed to merge commit for group. Error: \(error.localizedDescription)", - attributes: [.mlsGroupID: groupID.safeForLoggingDescription] - ) - throw CommitError.failedToMergeCommit - } - } - - private func discardPendingCommit(in groupID: MLSGroupID) async throws { - do { - WireLogger.mls.info( - "discarding pending commit for group", - attributes: [.mlsGroupID: groupID.safeForLoggingDescription] - ) - try await coreCrypto.perform { - try await $0.clearPendingCommit(conversationId: groupID.data) - } - } catch { - WireLogger.mls.error( - "failed to discard pending commit for group (failedToClearCommit). Error: \(error.localizedDescription)", - attributes: [.mlsGroupID: groupID.safeForLoggingDescription] - ) - throw CommitError.failedToClearCommit - } - } - - private func mergePendingGroup(in groupID: MLSGroupID) async throws { - do { - WireLogger.mls.info("merging pending group", attributes: [.mlsGroupID: groupID.safeForLoggingDescription]) - // No need to handle buffered messages here. We will not run into a scenario where we need to handle - // buffered decrypted messages, because sending a commit and decrypting a message are non-rentrant - // operations and therefore we will never attempt to decrypt a message while sending a commit. - _ = try await coreCrypto.perform { - try await $0.mergePendingGroupFromExternalCommit( - conversationId: groupID.data - ) - } - } catch { - WireLogger.mls.error( - "failed to merge pending group. Error: \(error.localizedDescription)", - attributes: [.mlsGroupID: groupID.safeForLoggingDescription] - ) - throw ExternalCommitError.failedToMergePendingGroup - } - } - - private func clearPendingGroup(in groupID: MLSGroupID) async throws { - do { - WireLogger.mls.info("clearing pending group", attributes: [.mlsGroupID: groupID.safeForLoggingDescription]) - try await coreCrypto.perform { - try await $0.clearPendingGroupFromExternalCommit(conversationId: groupID.data) - } - } catch { - WireLogger.mls.error( - "failed to clear pending group. Error: \(error.localizedDescription)", - attributes: [.mlsGroupID: groupID.safeForLoggingDescription] - ) - throw ExternalCommitError.failedToClearPendingGroup - } - } -} - -private extension CommitError.RecoveryStrategy { - - init(from error: SendCommitBundleAction.Failure) { - switch error { - case .mlsClientMismatch: - self = .retryAfterQuickSync - case .mlsCommitMissingReferences: - self = .retryAfterQuickSync - case .mlsStaleMessage: - self = .retryAfterRepairingGroup - default: - self = .giveUp - } - } - -} - -private extension ExternalCommitError.RecoveryStrategy { - - init(from error: SendCommitBundleAction.Failure) { - switch error { - case .mlsStaleMessage: - self = .retry - default: - self = .giveUp - } - - } -} - -extension CommitBundle { - - func transportData() -> Data { - var data = Data() - data.append(commit) - if let welcome { - data.append(welcome) - } - data.append(groupInfo.payload) - return data - } - -} diff --git a/wire-ios-data-model/Source/MLS/MLSActionExecutor.swift b/wire-ios-data-model/Source/MLS/MLSActionExecutor.swift index 64dba2041c8..0fed4fd5ff3 100644 --- a/wire-ios-data-model/Source/MLS/MLSActionExecutor.swift +++ b/wire-ios-data-model/Source/MLS/MLSActionExecutor.swift @@ -46,7 +46,7 @@ public protocol MLSActionExecutorProtocol { func addMembers( _ invitees: [KeyPackage], to groupID: MLSGroupID - ) async throws -> [ZMUpdateEvent] + ) async throws /// Creates and sends a commit bundle to remove clients from a group. /// @@ -58,14 +58,14 @@ public protocol MLSActionExecutorProtocol { func removeClients( _ clients: [ClientId], from groupID: MLSGroupID - ) async throws -> [ZMUpdateEvent] + ) async throws /// Creates and sends a commit bundle to update the key material for a group. /// /// - Parameter groupID: The group ID of the group to update key material for. /// - Returns: Update events returned by the backend. - func updateKeyMaterial(for groupID: MLSGroupID) async throws -> [ZMUpdateEvent] + func updateKeyMaterial(for groupID: MLSGroupID) async throws /// Creates and sends a commit bundle to commit the pending proposals for a group. /// @@ -73,7 +73,7 @@ public protocol MLSActionExecutorProtocol { /// - Returns: Update events returned by the backend. /// - Throws: `CommitError.noPendingProposals` if there are no proposals to commit. - func commitPendingProposals(in groupID: MLSGroupID) async throws -> [ZMUpdateEvent] + func commitPendingProposals(in groupID: MLSGroupID) async throws /// Creates and sends an **external** commit to join a group. /// @@ -88,7 +88,7 @@ public protocol MLSActionExecutorProtocol { func joinGroup( _ groupID: MLSGroupID, groupInfo: Data - ) async throws -> [ZMUpdateEvent] + ) async throws /// Decrypts a message for a group. /// @@ -102,10 +102,6 @@ public protocol MLSActionExecutorProtocol { in groupID: MLSGroupID ) async throws -> DecryptedMessage - /// Returns a publisher that emits the group ID of the group when the epoch changes. - - func onEpochChanged() -> AnyPublisher - /// Returns a publisher that emits the new CRL distribution points when they are found func onNewCRLsDistributionPoints() -> AnyPublisher @@ -138,7 +134,6 @@ public actor MLSActionExecutor: MLSActionExecutorProtocol { // MARK: - Properties private let coreCryptoProvider: CoreCryptoProviderProtocol - private let commitSender: CommitSending private var continuationsByGroupID: [MLSGroupID: [CheckedContinuation]] = [:] private let onNewCRLsDistributionPointsSubject = PassthroughSubject() private let featureRepository: FeatureRepositoryInterface @@ -153,11 +148,9 @@ public actor MLSActionExecutor: MLSActionExecutorProtocol { public init( coreCryptoProvider: CoreCryptoProviderProtocol, - commitSender: CommitSending, featureRepository: FeatureRepositoryInterface ) { self.coreCryptoProvider = coreCryptoProvider - self.commitSender = commitSender self.featureRepository = featureRepository } @@ -227,14 +220,25 @@ public actor MLSActionExecutor: MLSActionExecutorProtocol { return MLSGroupID(welcomeBundle.id) } - public func addMembers(_ invitees: [KeyPackage], to groupID: MLSGroupID) async throws -> [ZMUpdateEvent] { + public func addMembers(_ invitees: [KeyPackage], to groupID: MLSGroupID) async throws { try await performNonReentrant(groupID: groupID) { do { WireLogger.mls.info("adding members to group (\(groupID.safeForLoggingDescription))...") - let bundle = try await commitBundle(for: .addMembers(invitees), in: groupID) - let result = try await commitSender.sendCommitBundle(bundle, for: groupID) + + let crlNewDistributionPoints = try await coreCrypto.perform { + try await $0.addClientsToConversation( + conversationId: groupID.data, + keyPackages: invitees.compactMap(\.keyPackage.base64DecodedData) + ) + } + + if let newDistributionPoints = CRLsDistributionPoints( + from: crlNewDistributionPoints + ) { + onNewCRLsDistributionPointsSubject.send(newDistributionPoints) + } + WireLogger.mls.info("success: adding members to group (\(groupID.safeForLoggingDescription))") - return result } catch { WireLogger.mls .info( @@ -245,14 +249,17 @@ public actor MLSActionExecutor: MLSActionExecutorProtocol { } } - public func removeClients(_ clients: [ClientId], from groupID: MLSGroupID) async throws -> [ZMUpdateEvent] { + public func removeClients(_ clients: [ClientId], from groupID: MLSGroupID) async throws { try await performNonReentrant(groupID: groupID) { do { WireLogger.mls.info("removing clients from group (\(groupID.safeForLoggingDescription))...") - let bundle = try await commitBundle(for: .removeClients(clients), in: groupID) - let result = try await commitSender.sendCommitBundle(bundle, for: groupID) + return try await coreCrypto.perform { + try await $0.removeClientsFromConversation( + conversationId: groupID.data, + clients: clients + ) + } WireLogger.mls.info("success: removing clients from group (\(groupID.safeForLoggingDescription))") - return result } catch { WireLogger.mls .info( @@ -263,14 +270,14 @@ public actor MLSActionExecutor: MLSActionExecutorProtocol { } } - public func updateKeyMaterial(for groupID: MLSGroupID) async throws -> [ZMUpdateEvent] { + public func updateKeyMaterial(for groupID: MLSGroupID) async throws { try await performNonReentrant(groupID: groupID) { do { WireLogger.mls.info("updating key material for group (\(groupID.safeForLoggingDescription))...") - let bundle = try await commitBundle(for: .updateKeyMaterial, in: groupID) - let result = try await commitSender.sendCommitBundle(bundle, for: groupID) + return try await coreCrypto.perform { + try await $0.updateKeyingMaterial(conversationId: groupID.data) + } WireLogger.mls.info("success: updating key material for group (\(groupID.safeForLoggingDescription))") - return result } catch { WireLogger.mls .info( @@ -281,17 +288,15 @@ public actor MLSActionExecutor: MLSActionExecutorProtocol { } } - public func commitPendingProposals(in groupID: MLSGroupID) async throws -> [ZMUpdateEvent] { + public func commitPendingProposals(in groupID: MLSGroupID) async throws { try await performNonReentrant(groupID: groupID) { do { WireLogger.mls.info("committing pending proposals for group (\(groupID.safeForLoggingDescription))...") - let bundle = try await commitBundle(for: .proposal, in: groupID) - let result = try await commitSender.sendCommitBundle(bundle, for: groupID) + try await coreCrypto.perform { + try await $0.commitPendingProposals(conversationId: groupID.data) + } WireLogger.mls .info("success: committing pending proposals for group (\(groupID.safeForLoggingDescription))") - return result - } catch CommitError.noPendingProposals { - throw CommitError.noPendingProposals } catch { WireLogger.mls .info( @@ -302,14 +307,25 @@ public actor MLSActionExecutor: MLSActionExecutorProtocol { } } - public func joinGroup(_ groupID: MLSGroupID, groupInfo: Data) async throws -> [ZMUpdateEvent] { + public func joinGroup(_ groupID: MLSGroupID, groupInfo: Data) async throws { try await performNonReentrant(groupID: groupID) { do { WireLogger.mls.info("joining group (\(groupID.safeForLoggingDescription)) via external commit") - let bundle = try await commitBundle(for: .joinGroup(groupInfo), in: groupID) - let result = try await commitSender.sendExternalCommitBundle(bundle, for: groupID) + let ciphersuite = UInt16(await featureRepository.fetchMLS().config.defaultCipherSuite.rawValue) + let conversationInitBundle = try await coreCrypto.perform { + let e2eiIsEnabled = try await $0.e2eiIsEnabled(ciphersuite: ciphersuite) + return try await $0.joinByExternalCommit( + groupInfo: groupInfo, + customConfiguration: .init(keyRotationSpan: nil, wirePolicy: nil), + credentialType: e2eiIsEnabled ? .x509 : .basic + ) + } + if let newDistributionPoints = CRLsDistributionPoints( + from: conversationInitBundle.crlNewDistributionPoints + ) { + onNewCRLsDistributionPointsSubject.send(newDistributionPoints) + } WireLogger.mls.info("success: joining group (\(groupID.safeForLoggingDescription)) via external commit") - return result } catch { WireLogger.mls .info( @@ -323,138 +339,31 @@ public actor MLSActionExecutor: MLSActionExecutorProtocol { // MARK: - Decryption public func decryptMessage(_ message: Data, in groupID: MLSGroupID) async throws -> DecryptedMessage { - try await performNonReentrant(groupID: groupID) { + let result: DecryptedMessage? = try await performNonReentrant(groupID: groupID) { try await coreCrypto.perform { - var capturedError: Error? do { - let result: DecryptedMessage? = try await $0.transaction { context in - do { - return try await context.decryptMessage(conversationId: groupID.data, payload: message) - } catch let CoreCryptoError.Mls(error) { - switch error { - case .BufferedFutureMessage, .Other(coreCryptoCommitForMissingProposalError): - // ignore error so transaction is saved and message is saved too. - return nil - default: - capturedError = CoreCryptoError.Mls(error) - throw CoreCryptoError.Mls(error) - } - } catch { - capturedError = error - throw error - } - } - if let result { - return result - } else { - throw Failure.bufferedDecryptedMessage + return try await $0.decryptMessage(conversationId: groupID.data, payload: message) + } catch let CoreCryptoError.Mls(error) { + switch error { + case .BufferedFutureMessage, .BufferedCommit: + // ignore error so transaction is saved and message is saved too. + return nil + default: + throw error } } catch { - throw capturedError ?? error + throw error } } } - } - // MARK: - Commit generation - - private func commitBundle(for action: Action, in groupID: MLSGroupID) async throws -> CommitBundle { - do { - WireLogger.mls - .info( - "generating commit for action (\(String(describing: action))) for group (\(groupID.safeForLoggingDescription))..." - ) - switch action { - case let .addMembers(clients): - let memberAddMessages = try await coreCrypto.perform { - try await $0.addClientsToConversation( - conversationId: groupID.data, - keyPackages: clients.compactMap(\.keyPackage.base64DecodedData) - ) - } - - if let newDistributionPoints = CRLsDistributionPoints( - from: memberAddMessages.crlNewDistributionPoints - ) { - onNewCRLsDistributionPointsSubject.send(newDistributionPoints) - } - - return CommitBundle( - welcome: memberAddMessages.welcome, - commit: memberAddMessages.commit, - groupInfo: memberAddMessages.groupInfo - ) - - case let .removeClients(clients): - return try await coreCrypto.perform { - try await $0.removeClientsFromConversation( - conversationId: groupID.data, - clients: clients - ) - } - - case .updateKeyMaterial: - return try await coreCrypto.perform { - try await $0.updateKeyingMaterial(conversationId: groupID.data) - } - - case .proposal: - guard let bundle = try await coreCrypto.perform({ - do { - return try await $0.commitPendingProposals(conversationId: groupID.data) - } catch { - // if we already have a pending commit `commitPendingProposals()` will fail - // and we must first clear it in order to generate the commit again. - try? await $0.clearPendingCommit(conversationId: groupID.data) - return try await $0.commitPendingProposals(conversationId: groupID.data) - } - }) else { - throw CommitError.noPendingProposals - } - - return bundle - - case let .joinGroup(groupInfo): - let ciphersuite = UInt16(await featureRepository.fetchMLS().config.defaultCipherSuite.rawValue) - let conversationInitBundle = try await coreCrypto.perform { - let e2eiIsEnabled = try await $0.e2eiIsEnabled(ciphersuite: ciphersuite) - return try await $0.joinByExternalCommit( - groupInfo: groupInfo, - customConfiguration: .init(keyRotationSpan: nil, wirePolicy: nil), - credentialType: e2eiIsEnabled ? .x509 : .basic - ) - } - - if let newDistributionPoints = CRLsDistributionPoints( - from: conversationInitBundle.crlNewDistributionPoints - ) { - onNewCRLsDistributionPointsSubject.send(newDistributionPoints) - } - - return CommitBundle( - welcome: nil, - commit: conversationInitBundle.commit, - groupInfo: conversationInitBundle.groupInfo - ) - } - } catch CommitError.noPendingProposals { - throw CommitError.noPendingProposals - } catch { - WireLogger.mls - .warn( - "failed: generating commit for action (\(String(describing: action))) for group (\(groupID.safeForLoggingDescription)): \(String(describing: error))" - ) - throw CommitError.failedToGenerateCommit + if let result { + return result + } else { + throw Failure.bufferedDecryptedMessage } } - // MARK: - Epoch publisher - - public nonisolated - func onEpochChanged() -> AnyPublisher { - commitSender.onEpochChanged() - } - // MARK: - CRLs distribution points publisher public nonisolated diff --git a/wire-ios-data-model/Source/MLS/MLSDecryptionService.swift b/wire-ios-data-model/Source/MLS/MLSDecryptionService.swift index 83397451d3b..07b08ef8729 100644 --- a/wire-ios-data-model/Source/MLS/MLSDecryptionService.swift +++ b/wire-ios-data-model/Source/MLS/MLSDecryptionService.swift @@ -25,10 +25,6 @@ import WireSystem // sourcery: AutoMockable public protocol MLSDecryptionServiceInterface { - /// Publishes an event when the epoch has changed. - - func onEpochChanged() -> AnyPublisher - /// Publishes an event when new CRL distribution points are found. func onNewCRLsDistributionPoints() -> AnyPublisher @@ -102,13 +98,8 @@ public final class MLSDecryptionService: MLSDecryptionServiceInterface { private weak var context: NSManagedObjectContext? private let subconverationGroupIDRepository: SubconversationGroupIDRepositoryInterface - private let onEpochChangedSubject = PassthroughSubject() private let onNewCRLsDistributionPointsSubject = PassthroughSubject() - public func onEpochChanged() -> AnyPublisher { - onEpochChangedSubject.eraseToAnyPublisher() - } - public func onNewCRLsDistributionPoints() -> AnyPublisher { onNewCRLsDistributionPointsSubject.eraseToAnyPublisher() } @@ -127,13 +118,23 @@ public final class MLSDecryptionService: MLSDecryptionServiceInterface { // MARK: - Message decryption - public enum MLSMessageDecryptionError: Error { - + public enum MLSMessageDecryptionError: Error, Equatable { case failedToConvertMessageToBytes - case failedToDecryptMessage + case failedToDecryptMessage(reason: Error) case failedToDecodeSenderClientID case wrongEpoch + public static func == ( + lhs: MLSDecryptionService.MLSMessageDecryptionError, + rhs: MLSDecryptionService.MLSMessageDecryptionError + ) -> Bool { + switch (lhs, rhs) { + case (.failedToConvertMessageToBytes, .failedToConvertMessageToBytes): true + case (.failedToDecryptMessage, .failedToDecryptMessage): true + case (.failedToDecodeSenderClientID, .failedToDecodeSenderClientID): true + default: false + } + } } public func processWelcomeMessage(welcomeMessage: String) async throws -> MLSGroupID { @@ -174,10 +175,6 @@ public final class MLSDecryptionService: MLSDecryptionServiceInterface { do { let decryptedMessage = try await mlsActionExecutor.decryptMessage(messageData, in: groupID) - if decryptedMessage.hasEpochChanged { - onEpochChangedSubject.send(groupID) - } - if let newDistributionPoints = CRLsDistributionPoints(from: decryptedMessage.crlNewDistributionPoints) { onNewCRLsDistributionPointsSubject.send(newDistributionPoints) } @@ -203,6 +200,9 @@ public final class MLSDecryptionService: MLSDecryptionServiceInterface { // Message arrive in future epoch, it has been buffered and will be consumed later. case .BufferedFutureMessage: return [] + // Commit arrive in future epoch, it has been buffered and will be consumed later. + case .BufferedCommit: return [] + // Received already sent or received message, can safely be ignored. case .DuplicateMessage: return [] @@ -224,8 +224,11 @@ public final class MLSDecryptionService: MLSDecryptionServiceInterface { // commit has been buffered, and will be automatically unbuffered when possible. case .Other(coreCryptoCommitForMissingProposalError): return [] - case .Other, .ConversationAlreadyExists, .MessageEpochTooOld, .OrphanWelcome: - throw MLSMessageDecryptionError.failedToDecryptMessage + case .Other, .ConversationAlreadyExists, .MessageEpochTooOld, .OrphanWelcome, .MessageRejected: + throw MLSMessageDecryptionError.failedToDecryptMessage(reason: error) + + @unknown default: + throw MLSMessageDecryptionError.failedToDecryptMessage(reason: error) } } catch MLSActionExecutor.Failure.bufferedDecryptedMessage { // [WPB-16231] fix CC transaction is not saved @@ -236,7 +239,7 @@ public final class MLSDecryptionService: MLSDecryptionServiceInterface { "failed to decrypt message for group (\(groupID.safeForLoggingDescription)) and subconversation type (\(String(describing: subconversationType))): \(String(describing: error)) | \(debugInfo)" ) - throw MLSMessageDecryptionError.failedToDecryptMessage + throw MLSMessageDecryptionError.failedToDecryptMessage(reason: error) } } diff --git a/wire-ios-data-model/Source/MLS/MLSService.swift b/wire-ios-data-model/Source/MLS/MLSService.swift index e1f8e75c5df..676fddb590f 100644 --- a/wire-ios-data-model/Source/MLS/MLSService.swift +++ b/wire-ios-data-model/Source/MLS/MLSService.swift @@ -18,6 +18,7 @@ import Combine import Foundation +import WireAPI import WireCoreCrypto import WireFoundation import WireLogging @@ -116,9 +117,6 @@ public protocol MLSServiceInterface: MLSEncryptionServiceInterface, MLSDecryptio /// If the group is joined successfully, the conversation's ``ZMConversation/mlsStatus`` /// will be set to ``MLSGroupStatus/ready`` /// - /// Finally, it will call ``ConversationEventProcessorProtocol/processConversationEvents(_:)`` - /// to process the events returned by the backend. - /// /// The operation is covered by a retry mechanism that will perform a recovery strategy based on errors thrown /// while sending commits. See `MLSService/retryOnCommitFailure(for:operation:)` /// @@ -187,9 +185,6 @@ public protocol MLSServiceInterface: MLSEncryptionServiceInterface, MLSDecryptio /// If there are no key packages, it will call ``MLSActionExecutor/updateKeyMaterial(for:)`` to send a commit /// to the backend to inform we are in the group. /// - /// Finally, it will call ``ConversationEventProcessorProtocol/processConversationEvents(_:)`` - /// to process the events returned by the backend. - /// /// The operation is covered by a retry mechanism that will perform a recovery strategy based on errors thrown /// while sending commits. See `MLSService/retryOnCommitFailure(for:operation:)` /// @@ -212,9 +207,6 @@ public protocol MLSServiceInterface: MLSEncryptionServiceInterface, MLSDecryptio /// Then calls ``MLSActionExecutor/removeClients(_:from:)`` to send a commit bundle to the backend /// to remove the clients from the group. /// - /// Finally, it will call ``ConversationEventProcessorProtocol/processConversationEvents(_:)`` - /// to process the events returned by the backend. - /// /// The operation is covered by a retry mechanism that will perform a recovery strategy based on errors thrown /// while sending commits. See `MLSService/retryOnCommitFailure(for:operation:)` /// @@ -339,8 +331,6 @@ public protocol MLSServiceInterface: MLSEncryptionServiceInterface, MLSDecryptio /// /// Commiting proposals entails sending a commit to the backend /// with ``MLSActionExecutor/commitPendingProposals(in:)`` - /// and processing the events that are returned - /// with ``ConversationEventProcessorProtocol/processConversationEvents(_:)``. /// /// If the commit is successful, or if there are no proposals to commit, /// we clear the ``ZMConversation/commitPendingProposalDate`` @@ -361,9 +351,7 @@ public protocol MLSServiceInterface: MLSEncryptionServiceInterface, MLSDecryptio /// by calling ``MLSActionExecutor/updateKeyMaterial(for:)`` /// /// If successful it notifies that the key material has been updated - /// via ``StaleMLSKeyDetector/keyingMaterialUpdated(for:)``, - /// then it processes the conversation events returned by the backend - /// with ``ConversationEventProcessorProtocol/processConversationEvents(_:)`` + /// via ``StaleMLSKeyDetector/keyingMaterialUpdated(for:)`` /// /// Updating the key material is covered by a retry mechanism that will perform a recovery strategy /// based on errors thrown while sending commits. @@ -432,9 +420,7 @@ public protocol MLSServiceInterface: MLSEncryptionServiceInterface, MLSDecryptio /// which sends a commit to the backend. /// /// Then the ``StaleMLSKeyDetector/keyingMaterialUpdated(for:)`` - /// method is called to note when the key material was updated for the group, - /// and any events returned by the backend are processed - /// with ``ConversationEventProcessorProtocol/processConversationEvents(_:)``. + /// method is called to note when the key material was updated for the group /// /// The operation is covered by a retry mechanism that will perform a recovery strategy based on errors thrown /// while sending commits. See `MLSService/retryOnCommitFailure(for:operation:)` @@ -537,13 +523,13 @@ public final class MLSService: MLSServiceInterface { private let decryptionService: MLSDecryptionServiceInterface private let mlsActionExecutor: MLSActionExecutorProtocol - private let conversationEventProcessor: ConversationEventProcessorProtocol private let staleKeyMaterialDetector: StaleMLSKeyDetectorProtocol private let userDefaults: PrivateUserDefaults private let logger = WireLogger.mls private let groupsBeingRepaired = GroupsBeingRepaired() private let featureRepository: FeatureRepositoryInterface private weak var mlsSyncDelegate: (any MLSSyncDelegate)? + private let onEpochChangedSubject = PassthroughSubject() private var coreCrypto: SafeCoreCryptoProtocol { get async throws { @@ -579,7 +565,6 @@ public final class MLSService: MLSServiceInterface { context: NSManagedObjectContext, notificationContext: any NotificationContext, coreCryptoProvider: CoreCryptoProviderProtocol, - conversationEventProcessor: ConversationEventProcessorProtocol, featureRepository: FeatureRepositoryInterface, userDefaults: UserDefaults, userID: UUID @@ -588,7 +573,6 @@ public final class MLSService: MLSServiceInterface { context: context, notificationContext: notificationContext, coreCryptoProvider: coreCryptoProvider, - conversationEventProcessor: conversationEventProcessor, staleKeyMaterialDetector: StaleMLSKeyDetector(context: context), userDefaults: userDefaults, actionsProvider: MLSActionsProvider(), @@ -604,7 +588,6 @@ public final class MLSService: MLSServiceInterface { encryptionService: MLSEncryptionServiceInterface? = nil, decryptionService: MLSDecryptionServiceInterface? = nil, mlsActionExecutor: MLSActionExecutorProtocol? = nil, - conversationEventProcessor: ConversationEventProcessorProtocol, staleKeyMaterialDetector: StaleMLSKeyDetectorProtocol, userDefaults: UserDefaults, actionsProvider: MLSActionsProviderProtocol = MLSActionsProvider(), @@ -613,21 +596,14 @@ public final class MLSService: MLSServiceInterface { featureRepository: FeatureRepositoryInterface, subconversationGroupIDRepository: SubconversationGroupIDRepositoryInterface = SubconversationGroupIDRepository() ) { - let commitSender = CommitSender( - coreCryptoProvider: coreCryptoProvider, - notificationContext: context.notificationContext - ) - self.context = context self.notificationContext = notificationContext self.coreCryptoProvider = coreCryptoProvider self.featureRepository = featureRepository self.mlsActionExecutor = mlsActionExecutor ?? MLSActionExecutor( coreCryptoProvider: coreCryptoProvider, - commitSender: commitSender, featureRepository: featureRepository ) - self.conversationEventProcessor = conversationEventProcessor self.staleKeyMaterialDetector = staleKeyMaterialDetector self.actionsProvider = actionsProvider self.userDefaults = PrivateUserDefaults(userID: userID, storage: userDefaults) @@ -645,6 +621,7 @@ public final class MLSService: MLSServiceInterface { ) schedulePeriodicKeyMaterialUpdateCheck() + startObservingEpochs() } deinit { @@ -811,9 +788,8 @@ public final class MLSService: MLSServiceInterface { private func internalUpdateKeyMaterial(for groupID: MLSGroupID) async throws { do { WireLogger.mls.info("updating key material for group (\(groupID.safeForLoggingDescription))") - let events = try await mlsActionExecutor.updateKeyMaterial(for: groupID) + try await mlsActionExecutor.updateKeyMaterial(for: groupID) staleKeyMaterialDetector.keyingMaterialUpdated(for: groupID) - await conversationEventProcessor.processConversationEvents(events) } catch { WireLogger.mls .warn( @@ -939,7 +915,7 @@ public final class MLSService: MLSServiceInterface { } let keyPackages = try await claimKeyPackages(for: users, ciphersuite: ciphersuite) - let events = if keyPackages.isEmpty { + if keyPackages.isEmpty { // CC does not accept empty keypackages in addMembers, but // when creating a group we still need to send a commit to backend // to inform we are in the group @@ -947,8 +923,6 @@ public final class MLSService: MLSServiceInterface { } else { try await mlsActionExecutor.addMembers(keyPackages, to: groupID) } - await conversationEventProcessor.processConversationEvents(events) - } catch { logger .warn( @@ -1018,8 +992,7 @@ public final class MLSService: MLSServiceInterface { logger.info("removing members from group (\(groupID.safeForLoggingDescription)), members: \(clientIds)") guard !clientIds.isEmpty else { throw MLSRemoveParticipantsError.noClientsToRemove } let clientIds = clientIds.compactMap(\.rawValue.utf8Data) - let events = try await mlsActionExecutor.removeClients(clientIds, from: groupID) - await conversationEventProcessor.processConversationEvents(events) + try await mlsActionExecutor.removeClients(clientIds, from: groupID) } catch { logger .warn( @@ -1500,7 +1473,7 @@ public final class MLSService: MLSServiceInterface { var outOfSyncConversations = [ZMConversation]() for conversation in allMLSConversations { - guard await isConversationOutOfSync(conversation, coreCrypto: coreCrypto, context: context) + guard await self.isConversationOutOfSync(conversation, coreCrypto: coreCrypto, context: context) else { continue } outOfSyncConversations.append(conversation) } @@ -1520,7 +1493,7 @@ public final class MLSService: MLSServiceInterface { private func isConversationOutOfSync( _ conversation: ZMConversation, subgroup: MLSSubgroup? = nil, - coreCrypto: CoreCryptoProtocol, + coreCrypto: CoreCryptoContextProtocol, context: NSManagedObjectContext ) async -> Bool { var groupID: MLSGroupID? @@ -1557,7 +1530,7 @@ public final class MLSService: MLSServiceInterface { context: NSManagedObjectContext ) async throws -> Bool { try await coreCrypto.perform { - await isConversationOutOfSync( + await self.isConversationOutOfSync( conversation, subgroup: subgroup, coreCrypto: $0, @@ -1628,15 +1601,13 @@ public final class MLSService: MLSServiceInterface { context: context.notificationContext ) - let updateEvents: [ZMUpdateEvent] - if let subgroupID { - updateEvents = try await mlsActionExecutor.joinGroup( + try await mlsActionExecutor.joinGroup( subgroupID, groupInfo: groupInfo ) } else { - updateEvents = try await mlsActionExecutor.joinGroup( + try await mlsActionExecutor.joinGroup( parentID, groupInfo: groupInfo ) @@ -1646,7 +1617,6 @@ public final class MLSService: MLSServiceInterface { } } - await conversationEventProcessor.processConversationEvents(updateEvents) logger.info("success: joined group with external commit (\(logInfo))") } catch { @@ -1865,13 +1835,9 @@ public final class MLSService: MLSServiceInterface { private func internalCommitPendingProposals(in groupID: MLSGroupID) async throws { do { logger.info("committing pending proposals in: \(groupID.safeForLoggingDescription)") - let events = try await mlsActionExecutor.commitPendingProposals(in: groupID) - await conversationEventProcessor.processConversationEvents(events) + try await mlsActionExecutor.commitPendingProposals(in: groupID) clearPendingProposalCommitDate(for: groupID) delegate?.mlsServiceDidCommitPendingProposal(for: groupID) - } catch CommitError.noPendingProposals { - logger.info("no proposals to commit in group (\(groupID.safeForLoggingDescription))...") - clearPendingProposalCommitDate(for: groupID) } catch { logger .info( @@ -1894,63 +1860,99 @@ public final class MLSService: MLSServiceInterface { // MARK: - Error recovery - private func retryOnCommitFailure( - for groupID: MLSGroupID, - operation: @escaping () async throws -> Void, - retryCount: Int = 0 - ) async throws { + enum MLSRetryError: Error, Equatable { + case retryLimitReached + case nonRecoverableError(_ reason: String) + } - do { - try await operation() + enum RecoveryStrategy: Equatable { - } catch CommitError.failedToSendCommit(recovery: .commitPendingProposalsAfterQuickSync, _) { - logger.warn("failed to send commit, syncing then committing pending proposals...") - try await mlsSyncDelegate?.recoverWithIncrementalSync() + /// Perform a quick sync, then retry the action in its entirety. + /// + /// Core Crypto can not automatically recover by itself. It needs + /// to process incoming handshake messages then generate a new commit. - logger.info("sync finished, committing pending proposals...") - try await commitPendingProposals(in: groupID) + case retryAfterQuickSync - } catch CommitError.failedToSendCommit(recovery: .retryAfterQuickSync, cause: let error) { - logger.warn("failed to send commit, syncing then retrying operation...") - try await mlsSyncDelegate?.recoverWithIncrementalSync() + /// Repair (re-join) the group and retry the action + /// + /// We may have missed a few commits so we will rejoin the group + /// and try again. - logger.info("sync finished, retying operation...") + case retryAfterRepairingGroup - guard retryCount <= maxRetryAttempts else { - throw error - } + /// Abort the action and inform the user. + /// + /// There is no way to automatically recover from the error. - var currentRetryCount = retryCount - currentRetryCount += 1 + case giveUp - try await retryOnCommitFailure(for: groupID, operation: operation, retryCount: currentRetryCount) + init(from reason: String) { + if let error = try? MLSAPIError(from: reason) { + switch error { + case .mlsClientMismatch, .mlsCommitMissingReferences: + self = .retryAfterQuickSync + case .mlsStaleMessage: + self = .retryAfterRepairingGroup + default: + self = .giveUp + } + } else { + self = .giveUp + } + } - } catch CommitError.failedToSendCommit(recovery: .retryAfterRepairingGroup, _) { - logger.warn("failed to send commit, repairing group then retrying operation...") - await fetchAndRepairGroup(with: groupID) - logger.info("repair finished, retrying operation...") - try await operation() + } - } catch CommitError.failedToSendCommit(recovery: .giveUp, cause: let error) { - logger.warn("failed to send commit, giving up...") - throw error + private func retryOnCommitFailure( + for groupID: MLSGroupID, + operation: @escaping () async throws -> Void, + retryCount: Int = 0 + ) async throws { - } catch ExternalCommitError.failedToSendCommit(recovery: .retry, cause: let error) { - logger.warn("failed to send external commit, retrying operation...") + do { + try await operation() + } catch let CoreCryptoError.Mls(.MessageRejected(reason: reason)) { + switch RecoveryStrategy(from: reason) { + case .retryAfterQuickSync: + logger.warn( + "failed to send commit, syncing then retrying operation...", + attributes: [.mlsGroupID: groupID.safeForLoggingDescription] + ) + try await mlsSyncDelegate?.recoverWithIncrementalSync() + logger.info( + "sync finished, retrying operation...", + attributes: [.mlsGroupID: groupID.safeForLoggingDescription] + ) - guard retryCount <= maxRetryAttempts else { - throw error - } + guard retryCount <= maxRetryAttempts else { + throw MLSRetryError.retryLimitReached + } - var currentRetryCount = retryCount - currentRetryCount += 1 + var currentRetryCount = retryCount + currentRetryCount += 1 - try await retryOnCommitFailure(for: groupID, operation: operation, retryCount: currentRetryCount) + try await retryOnCommitFailure(for: groupID, operation: operation, retryCount: currentRetryCount) - } catch ExternalCommitError.failedToSendCommit(recovery: .giveUp, cause: let error) { - logger.warn("failed to send external commit, giving up...") - throw error + case .retryAfterRepairingGroup: + logger.warn( + "failed to send commit, repairing group then retrying operation...", + attributes: [.mlsGroupID: groupID.safeForLoggingDescription] + ) + await fetchAndRepairGroup(with: groupID) + logger.info( + "repair finished, retrying operation...", + attributes: [.mlsGroupID: groupID.safeForLoggingDescription] + ) + try await operation() + case .giveUp: + logger.warn( + "failed to send commit, giving up...", + attributes: [.mlsGroupID: groupID.safeForLoggingDescription] + ) + throw MLSRetryError.nonRecoverableError(reason) + } } } @@ -2212,10 +2214,18 @@ public final class MLSService: MLSServiceInterface { // MARK: - Epoch + public func startObservingEpochs() { + Task { + await coreCryptoProvider.registerEpochObserver(self) + } + } + public func onEpochChanged() -> AnyPublisher { - decryptionService.onEpochChanged() - .merge(with: mlsActionExecutor.onEpochChanged()) - .eraseToAnyPublisher() + onEpochChangedSubject.eraseToAnyPublisher() + } + + public func epochChanged(conversationId: Data, epoch: UInt64) async throws { + onEpochChangedSubject.send(MLSGroupID(conversationId)) } // MARK: - Generate new epoch @@ -2304,6 +2314,8 @@ public final class MLSService: MLSServiceInterface { } } +extension MLSService: EpochObserver {} + // MARK: - Helper types public struct MLSUser: Equatable { @@ -2363,7 +2375,7 @@ private extension TimeInterval { } // sourcery: AutoMockable -public protocol ConversationEventProcessorProtocol { +public protocol LegacyConversationEventProcessorProtocol { /// Decodes event's payload and transform it to local model func processConversationEvents(_ events: [ZMUpdateEvent]) async diff --git a/wire-ios-data-model/Source/Utilis/CoreCryptoContextProtocolExt.swift b/wire-ios-data-model/Source/Utilis/CoreCryptoContextProtocolExt.swift new file mode 100644 index 00000000000..188b352b734 --- /dev/null +++ b/wire-ios-data-model/Source/Utilis/CoreCryptoContextProtocolExt.swift @@ -0,0 +1,275 @@ +// +// 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 WireCoreCrypto + +// sourcery: AutoMockable +public protocol CoreCryptoContextProtocol: WireCoreCryptoUniffi.CoreCryptoContextProtocol { + + /// See [core_crypto::mls::conversation::conversation_guard::ConversationGuard::add_members] + func addClientsToConversation(conversationId: Data, keyPackages: [Data]) async throws -> WireCoreCryptoUniffi + .NewCrlDistributionPoints + + /// See [core_crypto::context::CentralContext::get_or_create_client_keypackages] + func clientKeypackages( + ciphersuite: WireCoreCryptoUniffi.Ciphersuite, + credentialType: WireCoreCryptoUniffi.MlsCredentialType, + amountRequested: UInt32 + ) async throws -> [Data] + + /// See [core_crypto::mls::MlsCentral::client_public_key] + func clientPublicKey( + ciphersuite: WireCoreCryptoUniffi.Ciphersuite, + credentialType: WireCoreCryptoUniffi.MlsCredentialType + ) async throws -> Data + + /// See [core_crypto::context::CentralContext::client_valid_key_packages_count] + func clientValidKeypackagesCount( + ciphersuite: WireCoreCryptoUniffi.Ciphersuite, + credentialType: WireCoreCryptoUniffi.MlsCredentialType + ) async throws -> UInt64 + + /// See [core_crypto::mls::conversation::conversation_guard::ConversationGuard::commit_pending_proposals] + func commitPendingProposals(conversationId: Data) async throws + + /// See [core_crypto::mls::conversation::ConversationGuard::ciphersuite] + func conversationCiphersuite(conversationId: Data) async throws -> WireCoreCryptoUniffi.Ciphersuite + + /// See [core_crypto::mls::conversation::ConversationGuard::epoch] + func conversationEpoch(conversationId: Data) async throws -> UInt64 + + /// See [core_crypto::mls::MlsCentral::conversation_exists] + func conversationExists(conversationId: Data) async throws -> Bool + + /// See [core_crypto::context::CentralContext::new_conversation] + func createConversation( + conversationId: Data, + creatorCredentialType: WireCoreCryptoUniffi.MlsCredentialType, + config: WireCoreCryptoUniffi.ConversationConfiguration + ) async throws + + /// See [core_crypto::mls::conversation::conversation_guard::ConversationGuard::decrypt_message] + func decryptMessage(conversationId: Data, payload: Data) async throws -> WireCoreCryptoUniffi.DecryptedMessage + + /// See [core_crypto::context::CentralContext::delete_keypackages] + func deleteKeypackages(refs: [Data]) async throws + + /// See [core_crypto::context::CentralContext::delete_stale_key_packages] + func deleteStaleKeyPackages(ciphersuite: WireCoreCryptoUniffi.Ciphersuite) async throws + + /// See [core_crypto::mls::conversation::conversation_guard::ConversationGuard::e2ei_conversation_state] + func e2eiConversationState(conversationId: Data) async throws -> WireCoreCryptoUniffi.E2eiConversationState + + func e2eiDumpPkiEnv() async throws -> WireCoreCryptoUniffi.E2eiDumpedPkiEnv? + + /// See [core_crypto::context::CentralContext::e2ei_enrollment_stash] + func e2eiEnrollmentStash(enrollment: WireCoreCryptoUniffi.E2eiEnrollment) async throws -> Data + + /// See [core_crypto::context::CentralContext::e2ei_enrollment_stash_pop] + func e2eiEnrollmentStashPop(handle: Data) async throws -> WireCoreCryptoUniffi.E2eiEnrollment + + /// See [core_crypto::mls::MlsCentral::e2ei_is_enabled] + func e2eiIsEnabled(ciphersuite: WireCoreCryptoUniffi.Ciphersuite) async throws -> Bool + + /// See [core_crypto::mls::MlsCentral::e2ei_is_pki_env_setup] + func e2eiIsPkiEnvSetup() async throws -> Bool + + /// See [core_crypto::context::CentralContext::e2ei_mls_init_only] + func e2eiMlsInitOnly( + enrollment: WireCoreCryptoUniffi.E2eiEnrollment, + certificateChain: String, + nbKeyPackage: UInt32? + ) async throws -> WireCoreCryptoUniffi.NewCrlDistributionPoints + + /// See [core_crypto::context::CentralContext::e2ei_new_activation_enrollment] + func e2eiNewActivationEnrollment( + displayName: String, + handle: String, + team: String?, + expirySec: UInt32, + ciphersuite: WireCoreCryptoUniffi.Ciphersuite + ) async throws -> WireCoreCryptoUniffi.E2eiEnrollment + + /// See [core_crypto::context::CentralContext::e2ei_new_enrollment] + func e2eiNewEnrollment( + clientId: String, + displayName: String, + handle: String, + team: String?, + expirySec: UInt32, + ciphersuite: WireCoreCryptoUniffi.Ciphersuite + ) async throws -> WireCoreCryptoUniffi.E2eiEnrollment + + /// See [core_crypto::context::CentralContext::e2ei_new_rotate_enrollment] + func e2eiNewRotateEnrollment( + displayName: String?, + handle: String?, + team: String?, + expirySec: UInt32, + ciphersuite: WireCoreCryptoUniffi.Ciphersuite + ) async throws -> WireCoreCryptoUniffi.E2eiEnrollment + + /// See [core_crypto::context::CentralContext::e2ei_register_acme_ca] + func e2eiRegisterAcmeCa(trustAnchorPem: String) async throws + + /// See [core_crypto::context::CentralContext::e2ei_register_crl] + func e2eiRegisterCrl(crlDp: String, crlDer: Data) async throws -> WireCoreCryptoUniffi.CrlRegistration + + /// See [core_crypto::context::CentralContext::e2ei_register_intermediate_ca_pem] + func e2eiRegisterIntermediateCa(certPem: String) async throws -> WireCoreCryptoUniffi.NewCrlDistributionPoints + + /// See [core_crypto::mls::conversation::ConversationGuard::e2ei_rotate] + func e2eiRotate(conversationId: Data) async throws + + /// See [core_crypto::mls::conversation::conversation_guard::ConversationGuard::encrypt_message] + func encryptMessage(conversationId: Data, message: Data) async throws -> Data + + /// See [core_crypto::mls::conversation::ImmutableConversation::export_secret_key] + func exportSecretKey(conversationId: Data, keyLength: UInt32) async throws -> Data + + /// See [core_crypto::mls::conversation::ImmutableConversation::get_client_ids] + func getClientIds(conversationId: Data) async throws -> [WireCoreCryptoUniffi.ClientId] + + /// See [core_crypto::mls::MlsCentral::get_credential_in_use] + func getCredentialInUse(groupInfo: Data, credentialType: WireCoreCryptoUniffi.MlsCredentialType) async throws + -> WireCoreCryptoUniffi.E2eiConversationState + + /// See [core_crypto::context::CentralContext::get_data]. + func getData() async throws -> Data? + + /// See [core_crypto::mls::MlsCentral::get_device_identities] + func getDeviceIdentities(conversationId: Data, deviceIds: [WireCoreCryptoUniffi.ClientId]) async throws + -> [WireCoreCryptoUniffi.WireIdentity] + + /// See [core_crypto::mls::conversation::ImmutableConversation::get_external_sender] + func getExternalSender(conversationId: Data) async throws -> Data + + /// See [core_crypto::mls::MlsCentral::get_user_identities] + func getUserIdentities(conversationId: Data, userIds: [String]) async throws + -> [String: [WireCoreCryptoUniffi.WireIdentity]] + + /// See [core_crypto::context::CentralContext::join_by_external_commit] + func joinByExternalCommit( + groupInfo: Data, + customConfiguration: WireCoreCryptoUniffi.CustomConfiguration, + credentialType: WireCoreCryptoUniffi.MlsCredentialType + ) async throws -> WireCoreCryptoUniffi.WelcomeBundle + + /// See [core_crypto::mls::conversation::ConversationGuard::mark_as_child_of] + func markConversationAsChildOf(childId: Data, parentId: Data) async throws + + /// See [core_crypto::context::CentralContext::mls_generate_keypairs] + func mlsGenerateKeypairs(ciphersuites: WireCoreCryptoUniffi.Ciphersuites) async throws + -> [WireCoreCryptoUniffi.ClientId] + + /// See [core_crypto::context::CentralContext::mls_init] + func mlsInit( + clientId: WireCoreCryptoUniffi.ClientId, + ciphersuites: WireCoreCryptoUniffi.Ciphersuites, + nbKeyPackage: UInt32? + ) async throws + + /// See [core_crypto::context::CentralContext::mls_init_with_client_id] + func mlsInitWithClientId( + clientId: WireCoreCryptoUniffi.ClientId, + tmpClientIds: [WireCoreCryptoUniffi.ClientId], + ciphersuites: WireCoreCryptoUniffi.Ciphersuites + ) async throws + + /// See [core_crypto::context::CentralContext::process_raw_welcome_message] + func processWelcomeMessage( + welcomeMessage: Data, + customConfiguration: WireCoreCryptoUniffi.CustomConfiguration + ) async throws -> WireCoreCryptoUniffi.WelcomeBundle + + /// See [core_crypto::context::CentralContext::proteus_cryptobox_migrate] + func proteusCryptoboxMigrate(path: String) async throws + + /// See [core_crypto::context::CentralContext::proteus_decrypt] + func proteusDecrypt(sessionId: String, ciphertext: Data) async throws -> Data + + /// See [core_crypto::context::CentralContext::proteus_encrypt] + func proteusEncrypt(sessionId: String, plaintext: Data) async throws -> Data + + /// See [core_crypto::context::CentralContext::proteus_encrypt_batched] + func proteusEncryptBatched(sessions: [String], plaintext: Data) async throws -> [String: Data] + + /// See [core_crypto::context::CentralContext::proteus_fingerprint] + func proteusFingerprint() async throws -> String + + /// See [core_crypto::context::CentralContext::proteus_fingerprint_local] + func proteusFingerprintLocal(sessionId: String) async throws -> String + + /// See [core_crypto::proteus::ProteusCentral::fingerprint_prekeybundle] + /// NOTE: uniffi doesn't support associated functions, so we have to have the self here + func proteusFingerprintPrekeybundle(prekey: Data) throws -> String + + /// See [core_crypto::context::CentralContext::proteus_fingerprint_remote] + func proteusFingerprintRemote(sessionId: String) async throws -> String + + /// See [core_crypto::proteus::ProteusCentral::try_new] + func proteusInit() async throws + + /// See [core_crypto::context::CentralContext::proteus_last_resort_prekey] + func proteusLastResortPrekey() async throws -> Data + + /// See [core_crypto::context::CentralContext::proteus_last_resort_prekey_id] + func proteusLastResortPrekeyId() throws -> UInt16 + + /// See [core_crypto::context::CentralContext::proteus_new_prekey] + func proteusNewPrekey(prekeyId: UInt16) async throws -> Data + + /// See [core_crypto::context::CentralContext::proteus_new_prekey_auto] + func proteusNewPrekeyAuto() async throws -> WireCoreCryptoUniffi.ProteusAutoPrekeyBundle + + /// See [core_crypto::context::CentralContext::proteus_reload_sessions] + func proteusReloadSessions() async throws + + /// See [core_crypto::context::CentralContext::proteus_session_delete] + func proteusSessionDelete(sessionId: String) async throws + + /// See [core_crypto::context::CentralContext::proteus_session_exists] + func proteusSessionExists(sessionId: String) async throws -> Bool + + /// See [core_crypto::context::CentralContext::proteus_session_from_message] + func proteusSessionFromMessage(sessionId: String, envelope: Data) async throws -> Data + + /// See [core_crypto::context::CentralContext::proteus_session_from_prekey] + func proteusSessionFromPrekey(sessionId: String, prekey: Data) async throws + + /// See [core_crypto::context::CentralContext::proteus_session_save] + /// **Note**: This isn't usually needed as persisting sessions happens automatically when decrypting/encrypting + /// messages and initializing Sessions + func proteusSessionSave(sessionId: String) async throws + + /// See [core_crypto::context::CentralContext::remove_members_from_conversation] + func removeClientsFromConversation(conversationId: Data, clients: [WireCoreCryptoUniffi.ClientId]) async throws + + /// See [core_crypto::context::CentralContext::save_x509_credential] + func saveX509Credential(enrollment: WireCoreCryptoUniffi.E2eiEnrollment, certificateChain: String) async throws + -> WireCoreCryptoUniffi.NewCrlDistributionPoints + + /// See [core_crypto::context::CentralContext::set_data]. + func setData(data: Data) async throws + + /// See [core_crypto::context::CentralContext::update_keying_material] + func updateKeyingMaterial(conversationId: Data) async throws + + /// see [core_crypto::context::CentralContext::wipe_conversation] + func wipeConversation(conversationId: Data) async throws +} diff --git a/wire-ios-data-model/Source/Utilis/CoreCryptoProtocolExt.swift b/wire-ios-data-model/Source/Utilis/CoreCryptoProtocolExt.swift deleted file mode 100644 index f27764dca94..00000000000 --- a/wire-ios-data-model/Source/Utilis/CoreCryptoProtocolExt.swift +++ /dev/null @@ -1,250 +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 WireCoreCrypto - -// A copy of `WireCoreCrypto.CoreCryptoProtocol` in order to be able to auto-generate a mock for it. -// Keep the protocol updated with any changes on the WireCoreCrypto side. -// This file is not member of any target! - -// sourcery: AutoMockable -public protocol CoreCryptoProtocol: WireCoreCrypto.CoreCryptoProtocol { - - func addClientsToConversation(conversationId: Data, keyPackages: [Data]) async throws -> WireCoreCrypto - .MemberAddedMessages - - func clearPendingCommit(conversationId: Data) async throws - - func clearPendingGroupFromExternalCommit(conversationId: Data) async throws - - func clearPendingProposal(conversationId: Data, proposalRef: Data) async throws - - func clientKeypackages( - ciphersuite: WireCoreCrypto.Ciphersuite, - credentialType: WireCoreCrypto.MlsCredentialType, - amountRequested: UInt32 - ) async throws -> [Data] - - func clientPublicKey( - ciphersuite: WireCoreCrypto.Ciphersuite, - credentialType: WireCoreCrypto.MlsCredentialType - ) async throws -> Data - - func clientValidKeypackagesCount( - ciphersuite: WireCoreCrypto.Ciphersuite, - credentialType: WireCoreCrypto.MlsCredentialType - ) async throws -> UInt64 - - func commitAccepted(conversationId: Data) async throws -> [WireCoreCrypto.BufferedDecryptedMessage]? - - func commitPendingProposals(conversationId: Data) async throws -> WireCoreCrypto.CommitBundle? - - func conversationCiphersuite(conversationId: Data) async throws -> WireCoreCrypto.Ciphersuite - - func conversationEpoch(conversationId: Data) async throws -> UInt64 - - func conversationExists(conversationId: Data) async throws -> Bool - - func createConversation( - conversationId: Data, - creatorCredentialType: WireCoreCrypto.MlsCredentialType, - config: WireCoreCrypto.ConversationConfiguration - ) async throws - - func decryptMessage(conversationId: Data, payload: Data) async throws -> WireCoreCrypto.DecryptedMessage - - func deleteKeypackages(refs: [Data]) async throws - - func e2eiConversationState(conversationId: Data) async throws -> WireCoreCrypto.E2eiConversationState - - func e2eiDumpPkiEnv() async throws -> WireCoreCrypto.E2eiDumpedPkiEnv? - - func e2eiEnrollmentStash(enrollment: WireCoreCrypto.E2eiEnrollment) async throws -> Data - - func e2eiEnrollmentStashPop(handle: Data) async throws -> WireCoreCrypto.E2eiEnrollment - - func e2eiIsEnabled(ciphersuite: WireCoreCrypto.Ciphersuite) async throws -> Bool - - func e2eiIsPkiEnvSetup() async -> Bool - - func e2eiMlsInitOnly( - enrollment: WireCoreCrypto.E2eiEnrollment, - certificateChain: String, - nbKeyPackage: UInt32? - ) async throws -> [String]? - - func e2eiNewActivationEnrollment( - displayName: String, - handle: String, - team: String?, - expirySec: UInt32, - ciphersuite: WireCoreCrypto.Ciphersuite - ) async throws -> WireCoreCrypto.E2eiEnrollment - - func e2eiNewEnrollment( - clientId: String, - displayName: String, - handle: String, - team: String?, - expirySec: UInt32, - ciphersuite: WireCoreCrypto.Ciphersuite - ) async throws -> WireCoreCrypto.E2eiEnrollment - - func e2eiNewRotateEnrollment( - displayName: String?, - handle: String?, - team: String?, - expirySec: UInt32, - ciphersuite: WireCoreCrypto.Ciphersuite - ) async throws -> WireCoreCrypto.E2eiEnrollment - - func e2eiRegisterAcmeCa(trustAnchorPem: String) async throws - - func e2eiRegisterCrl(crlDp: String, crlDer: Data) async throws -> WireCoreCrypto.CrlRegistration - - func e2eiRegisterIntermediateCa(certPem: String) async throws -> [String]? - - func e2eiRotateAll( - enrollment: WireCoreCrypto.E2eiEnrollment, - certificateChain: String, - newKeyPackagesCount: UInt32 - ) async throws -> WireCoreCrypto.RotateBundle - - func e2eiRotate(conversationId: Data) async throws -> WireCoreCrypto.CommitBundle - - func encryptMessage(conversationId: Data, message: Data) async throws -> Data - - func exportSecretKey(conversationId: Data, keyLength: UInt32) async throws -> Data - - func getClientIds(conversationId: Data) async throws -> [WireCoreCrypto.ClientId] - - func getCredentialInUse(groupInfo: Data, credentialType: WireCoreCrypto.MlsCredentialType) async throws - -> WireCoreCrypto.E2eiConversationState - - func getDeviceIdentities(conversationId: Data, deviceIds: [WireCoreCrypto.ClientId]) async throws - -> [WireCoreCrypto.WireIdentity] - - func getExternalSender(conversationId: Data) async throws -> Data - - func getUserIdentities(conversationId: Data, userIds: [String]) async throws - -> [String: [WireCoreCrypto.WireIdentity]] - - func joinByExternalCommit( - groupInfo: Data, - customConfiguration: WireCoreCrypto.CustomConfiguration, - credentialType: WireCoreCrypto.MlsCredentialType - ) async throws -> WireCoreCrypto.ConversationInitBundle - - func markConversationAsChildOf(childId: Data, parentId: Data) async throws - - func mergePendingGroupFromExternalCommit(conversationId: Data) async throws - -> [WireCoreCrypto.BufferedDecryptedMessage]? - - func mlsGenerateKeypairs(ciphersuites: WireCoreCrypto.Ciphersuites) async throws -> [WireCoreCrypto.ClientId] - - func mlsInit( - clientId: WireCoreCrypto.ClientId, - ciphersuites: WireCoreCrypto.Ciphersuites, - nbKeyPackage: UInt32? - ) async throws - - func mlsInitWithClientId( - clientId: WireCoreCrypto.ClientId, - tmpClientIds: [WireCoreCrypto.ClientId], - ciphersuites: WireCoreCrypto.Ciphersuites - ) async throws - - func newAddProposal(conversationId: Data, keypackage: Data) async throws -> WireCoreCrypto.ProposalBundle - - func newExternalAddProposal( - conversationId: Data, - epoch: UInt64, - ciphersuite: WireCoreCrypto.Ciphersuite, - credentialType: WireCoreCrypto.MlsCredentialType - ) async throws -> Data - - func newRemoveProposal(conversationId: Data, clientId: WireCoreCrypto.ClientId) async throws -> WireCoreCrypto - .ProposalBundle - - func newUpdateProposal(conversationId: Data) async throws -> WireCoreCrypto.ProposalBundle - - func processWelcomeMessage( - welcomeMessage: Data, - customConfiguration: WireCoreCrypto.CustomConfiguration - ) async throws -> WireCoreCrypto.WelcomeBundle - - func proteusCryptoboxMigrate(path: String) async throws - - func proteusDecrypt(sessionId: String, ciphertext: Data) async throws -> Data - - func proteusEncrypt(sessionId: String, plaintext: Data) async throws -> Data - - func proteusEncryptBatched(sessions: [String], plaintext: Data) async throws -> [String: Data] - - func proteusFingerprint() async throws -> String - - func proteusFingerprintLocal(sessionId: String) async throws -> String - - func proteusFingerprintPrekeybundle(prekey: Data) throws -> String - - func proteusFingerprintRemote(sessionId: String) async throws -> String - - func proteusInit() async throws - - func proteusLastErrorCode() -> UInt16? - - func proteusLastResortPrekey() async throws -> Data - - func proteusLastResortPrekeyId() throws -> UInt16 - - func proteusNewPrekey(prekeyId: UInt16) async throws -> Data - - func proteusNewPrekeyAuto() async throws -> WireCoreCrypto.ProteusAutoPrekeyBundle - - func proteusSessionDelete(sessionId: String) async throws - - func proteusSessionExists(sessionId: String) async throws -> Bool - - func proteusSessionFromMessage(sessionId: String, envelope: Data) async throws -> Data - - func proteusSessionFromPrekey(sessionId: String, prekey: Data) async throws - - func proteusSessionSave(sessionId: String) async throws - - func randomBytes(len: UInt32) async throws -> Data - - func removeClientsFromConversation(conversationId: Data, clients: [WireCoreCrypto.ClientId]) async throws - -> WireCoreCrypto.CommitBundle - - func reseedRng(seed: Data) async throws - - func restoreFromDisk() async throws - - func setCallbacks(callbacks: any WireCoreCrypto.CoreCryptoCallbacks) async throws - - func transaction(command: any WireCoreCrypto.CoreCryptoCommand) async throws - - func unload() async throws - - func updateKeyingMaterial(conversationId: Data) async throws -> WireCoreCrypto.CommitBundle - - func wipe() async throws - - func wipeConversation(conversationId: Data) async throws - -} diff --git a/wire-ios-data-model/Support/Sourcery/generated/AutoMockable.generated.swift b/wire-ios-data-model/Support/Sourcery/generated/AutoMockable.generated.swift index 55d17bff24e..043a56fb9c9 100644 --- a/wire-ios-data-model/Support/Sourcery/generated/AutoMockable.generated.swift +++ b/wire-ios-data-model/Support/Sourcery/generated/AutoMockable.generated.swift @@ -207,118 +207,6 @@ public class MockCRLExpirationDatesRepositoryProtocol: CRLExpirationDatesReposit } -public class MockCommitSending: CommitSending { - - // MARK: - Life cycle - - public init() {} - - - // MARK: - sendCommitBundle - - public var sendCommitBundleFor_Invocations: [(bundle: CommitBundle, groupID: MLSGroupID)] = [] - public var sendCommitBundleFor_MockError: Error? - public var sendCommitBundleFor_MockMethod: ((CommitBundle, MLSGroupID) async throws -> [ZMUpdateEvent])? - public var sendCommitBundleFor_MockValue: [ZMUpdateEvent]? - - public func sendCommitBundle(_ bundle: CommitBundle, for groupID: MLSGroupID) async throws -> [ZMUpdateEvent] { - sendCommitBundleFor_Invocations.append((bundle: bundle, groupID: groupID)) - - if let error = sendCommitBundleFor_MockError { - throw error - } - - if let mock = sendCommitBundleFor_MockMethod { - return try await mock(bundle, groupID) - } else if let mock = sendCommitBundleFor_MockValue { - return mock - } else { - fatalError("no mock for `sendCommitBundleFor`") - } - } - - // MARK: - sendExternalCommitBundle - - public var sendExternalCommitBundleFor_Invocations: [(bundle: CommitBundle, groupID: MLSGroupID)] = [] - public var sendExternalCommitBundleFor_MockError: Error? - public var sendExternalCommitBundleFor_MockMethod: ((CommitBundle, MLSGroupID) async throws -> [ZMUpdateEvent])? - public var sendExternalCommitBundleFor_MockValue: [ZMUpdateEvent]? - - public func sendExternalCommitBundle(_ bundle: CommitBundle, for groupID: MLSGroupID) async throws -> [ZMUpdateEvent] { - sendExternalCommitBundleFor_Invocations.append((bundle: bundle, groupID: groupID)) - - if let error = sendExternalCommitBundleFor_MockError { - throw error - } - - if let mock = sendExternalCommitBundleFor_MockMethod { - return try await mock(bundle, groupID) - } else if let mock = sendExternalCommitBundleFor_MockValue { - return mock - } else { - fatalError("no mock for `sendExternalCommitBundleFor`") - } - } - - // MARK: - onEpochChanged - - public var onEpochChanged_Invocations: [Void] = [] - public var onEpochChanged_MockMethod: (() -> AnyPublisher)? - public var onEpochChanged_MockValue: AnyPublisher? - - public func onEpochChanged() -> AnyPublisher { - onEpochChanged_Invocations.append(()) - - if let mock = onEpochChanged_MockMethod { - return mock() - } else if let mock = onEpochChanged_MockValue { - return mock - } else { - fatalError("no mock for `onEpochChanged`") - } - } - -} - -public class MockConversationEventProcessorProtocol: ConversationEventProcessorProtocol { - - // MARK: - Life cycle - - public init() {} - - - // MARK: - processConversationEvents - - public var processConversationEvents_Invocations: [[ZMUpdateEvent]] = [] - public var processConversationEvents_MockMethod: (([ZMUpdateEvent]) async -> Void)? - - public func processConversationEvents(_ events: [ZMUpdateEvent]) async { - processConversationEvents_Invocations.append(events) - - guard let mock = processConversationEvents_MockMethod else { - fatalError("no mock for `processConversationEvents`") - } - - await mock(events) - } - - // MARK: - processAndSaveConversationEvents - - public var processAndSaveConversationEvents_Invocations: [[ZMUpdateEvent]] = [] - public var processAndSaveConversationEvents_MockMethod: (([ZMUpdateEvent]) async -> Void)? - - public func processAndSaveConversationEvents(_ events: [ZMUpdateEvent]) async { - processAndSaveConversationEvents_Invocations.append(events) - - guard let mock = processAndSaveConversationEvents_MockMethod else { - fatalError("no mock for `processAndSaveConversationEvents`") - } - - await mock(events) - } - -} - public class MockConversationLike: ConversationLike { // MARK: - Life cycle @@ -506,7 +394,7 @@ public class MockConversationLike: ConversationLike { } -public class MockCoreCryptoProtocol: CoreCryptoProtocol { +public class MockCoreCryptoContextProtocol: CoreCryptoContextProtocol { // MARK: - Life cycle @@ -517,10 +405,10 @@ public class MockCoreCryptoProtocol: CoreCryptoProtocol { public var addClientsToConversationConversationIdKeyPackages_Invocations: [(conversationId: Data, keyPackages: [Data])] = [] public var addClientsToConversationConversationIdKeyPackages_MockError: Error? - public var addClientsToConversationConversationIdKeyPackages_MockMethod: ((Data, [Data]) async throws -> WireCoreCrypto.MemberAddedMessages)? - public var addClientsToConversationConversationIdKeyPackages_MockValue: WireCoreCrypto.MemberAddedMessages? + public var addClientsToConversationConversationIdKeyPackages_MockMethod: ((Data, [Data]) async throws -> WireCoreCryptoUniffi.NewCrlDistributionPoints)? + public var addClientsToConversationConversationIdKeyPackages_MockValue: WireCoreCryptoUniffi.NewCrlDistributionPoints? - public func addClientsToConversation(conversationId: Data, keyPackages: [Data]) async throws -> WireCoreCrypto.MemberAddedMessages { + public func addClientsToConversation(conversationId: Data, keyPackages: [Data]) async throws -> WireCoreCryptoUniffi.NewCrlDistributionPoints { addClientsToConversationConversationIdKeyPackages_Invocations.append((conversationId: conversationId, keyPackages: keyPackages)) if let error = addClientsToConversationConversationIdKeyPackages_MockError { @@ -536,74 +424,14 @@ public class MockCoreCryptoProtocol: CoreCryptoProtocol { } } - // MARK: - clearPendingCommit - - public var clearPendingCommitConversationId_Invocations: [Data] = [] - public var clearPendingCommitConversationId_MockError: Error? - public var clearPendingCommitConversationId_MockMethod: ((Data) async throws -> Void)? - - public func clearPendingCommit(conversationId: Data) async throws { - clearPendingCommitConversationId_Invocations.append(conversationId) - - if let error = clearPendingCommitConversationId_MockError { - throw error - } - - guard let mock = clearPendingCommitConversationId_MockMethod else { - fatalError("no mock for `clearPendingCommitConversationId`") - } - - try await mock(conversationId) - } - - // MARK: - clearPendingGroupFromExternalCommit - - public var clearPendingGroupFromExternalCommitConversationId_Invocations: [Data] = [] - public var clearPendingGroupFromExternalCommitConversationId_MockError: Error? - public var clearPendingGroupFromExternalCommitConversationId_MockMethod: ((Data) async throws -> Void)? - - public func clearPendingGroupFromExternalCommit(conversationId: Data) async throws { - clearPendingGroupFromExternalCommitConversationId_Invocations.append(conversationId) - - if let error = clearPendingGroupFromExternalCommitConversationId_MockError { - throw error - } - - guard let mock = clearPendingGroupFromExternalCommitConversationId_MockMethod else { - fatalError("no mock for `clearPendingGroupFromExternalCommitConversationId`") - } - - try await mock(conversationId) - } - - // MARK: - clearPendingProposal - - public var clearPendingProposalConversationIdProposalRef_Invocations: [(conversationId: Data, proposalRef: Data)] = [] - public var clearPendingProposalConversationIdProposalRef_MockError: Error? - public var clearPendingProposalConversationIdProposalRef_MockMethod: ((Data, Data) async throws -> Void)? - - public func clearPendingProposal(conversationId: Data, proposalRef: Data) async throws { - clearPendingProposalConversationIdProposalRef_Invocations.append((conversationId: conversationId, proposalRef: proposalRef)) - - if let error = clearPendingProposalConversationIdProposalRef_MockError { - throw error - } - - guard let mock = clearPendingProposalConversationIdProposalRef_MockMethod else { - fatalError("no mock for `clearPendingProposalConversationIdProposalRef`") - } - - try await mock(conversationId, proposalRef) - } - // MARK: - clientKeypackages - public var clientKeypackagesCiphersuiteCredentialTypeAmountRequested_Invocations: [(ciphersuite: WireCoreCrypto.Ciphersuite, credentialType: WireCoreCrypto.MlsCredentialType, amountRequested: UInt32)] = [] + public var clientKeypackagesCiphersuiteCredentialTypeAmountRequested_Invocations: [(ciphersuite: WireCoreCryptoUniffi.Ciphersuite, credentialType: WireCoreCryptoUniffi.MlsCredentialType, amountRequested: UInt32)] = [] public var clientKeypackagesCiphersuiteCredentialTypeAmountRequested_MockError: Error? - public var clientKeypackagesCiphersuiteCredentialTypeAmountRequested_MockMethod: ((WireCoreCrypto.Ciphersuite, WireCoreCrypto.MlsCredentialType, UInt32) async throws -> [Data])? + public var clientKeypackagesCiphersuiteCredentialTypeAmountRequested_MockMethod: ((WireCoreCryptoUniffi.Ciphersuite, WireCoreCryptoUniffi.MlsCredentialType, UInt32) async throws -> [Data])? public var clientKeypackagesCiphersuiteCredentialTypeAmountRequested_MockValue: [Data]? - public func clientKeypackages(ciphersuite: WireCoreCrypto.Ciphersuite, credentialType: WireCoreCrypto.MlsCredentialType, amountRequested: UInt32) async throws -> [Data] { + public func clientKeypackages(ciphersuite: WireCoreCryptoUniffi.Ciphersuite, credentialType: WireCoreCryptoUniffi.MlsCredentialType, amountRequested: UInt32) async throws -> [Data] { clientKeypackagesCiphersuiteCredentialTypeAmountRequested_Invocations.append((ciphersuite: ciphersuite, credentialType: credentialType, amountRequested: amountRequested)) if let error = clientKeypackagesCiphersuiteCredentialTypeAmountRequested_MockError { @@ -621,12 +449,12 @@ public class MockCoreCryptoProtocol: CoreCryptoProtocol { // MARK: - clientPublicKey - public var clientPublicKeyCiphersuiteCredentialType_Invocations: [(ciphersuite: WireCoreCrypto.Ciphersuite, credentialType: WireCoreCrypto.MlsCredentialType)] = [] + public var clientPublicKeyCiphersuiteCredentialType_Invocations: [(ciphersuite: WireCoreCryptoUniffi.Ciphersuite, credentialType: WireCoreCryptoUniffi.MlsCredentialType)] = [] public var clientPublicKeyCiphersuiteCredentialType_MockError: Error? - public var clientPublicKeyCiphersuiteCredentialType_MockMethod: ((WireCoreCrypto.Ciphersuite, WireCoreCrypto.MlsCredentialType) async throws -> Data)? + public var clientPublicKeyCiphersuiteCredentialType_MockMethod: ((WireCoreCryptoUniffi.Ciphersuite, WireCoreCryptoUniffi.MlsCredentialType) async throws -> Data)? public var clientPublicKeyCiphersuiteCredentialType_MockValue: Data? - public func clientPublicKey(ciphersuite: WireCoreCrypto.Ciphersuite, credentialType: WireCoreCrypto.MlsCredentialType) async throws -> Data { + public func clientPublicKey(ciphersuite: WireCoreCryptoUniffi.Ciphersuite, credentialType: WireCoreCryptoUniffi.MlsCredentialType) async throws -> Data { clientPublicKeyCiphersuiteCredentialType_Invocations.append((ciphersuite: ciphersuite, credentialType: credentialType)) if let error = clientPublicKeyCiphersuiteCredentialType_MockError { @@ -644,12 +472,12 @@ public class MockCoreCryptoProtocol: CoreCryptoProtocol { // MARK: - clientValidKeypackagesCount - public var clientValidKeypackagesCountCiphersuiteCredentialType_Invocations: [(ciphersuite: WireCoreCrypto.Ciphersuite, credentialType: WireCoreCrypto.MlsCredentialType)] = [] + public var clientValidKeypackagesCountCiphersuiteCredentialType_Invocations: [(ciphersuite: WireCoreCryptoUniffi.Ciphersuite, credentialType: WireCoreCryptoUniffi.MlsCredentialType)] = [] public var clientValidKeypackagesCountCiphersuiteCredentialType_MockError: Error? - public var clientValidKeypackagesCountCiphersuiteCredentialType_MockMethod: ((WireCoreCrypto.Ciphersuite, WireCoreCrypto.MlsCredentialType) async throws -> UInt64)? + public var clientValidKeypackagesCountCiphersuiteCredentialType_MockMethod: ((WireCoreCryptoUniffi.Ciphersuite, WireCoreCryptoUniffi.MlsCredentialType) async throws -> UInt64)? public var clientValidKeypackagesCountCiphersuiteCredentialType_MockValue: UInt64? - public func clientValidKeypackagesCount(ciphersuite: WireCoreCrypto.Ciphersuite, credentialType: WireCoreCrypto.MlsCredentialType) async throws -> UInt64 { + public func clientValidKeypackagesCount(ciphersuite: WireCoreCryptoUniffi.Ciphersuite, credentialType: WireCoreCryptoUniffi.MlsCredentialType) async throws -> UInt64 { clientValidKeypackagesCountCiphersuiteCredentialType_Invocations.append((ciphersuite: ciphersuite, credentialType: credentialType)) if let error = clientValidKeypackagesCountCiphersuiteCredentialType_MockError { @@ -665,60 +493,34 @@ public class MockCoreCryptoProtocol: CoreCryptoProtocol { } } - // MARK: - commitAccepted - - public var commitAcceptedConversationId_Invocations: [Data] = [] - public var commitAcceptedConversationId_MockError: Error? - public var commitAcceptedConversationId_MockMethod: ((Data) async throws -> [WireCoreCrypto.BufferedDecryptedMessage]?)? - public var commitAcceptedConversationId_MockValue: [WireCoreCrypto.BufferedDecryptedMessage]?? - - public func commitAccepted(conversationId: Data) async throws -> [WireCoreCrypto.BufferedDecryptedMessage]? { - commitAcceptedConversationId_Invocations.append(conversationId) - - if let error = commitAcceptedConversationId_MockError { - throw error - } - - if let mock = commitAcceptedConversationId_MockMethod { - return try await mock(conversationId) - } else if let mock = commitAcceptedConversationId_MockValue { - return mock - } else { - fatalError("no mock for `commitAcceptedConversationId`") - } - } - // MARK: - commitPendingProposals public var commitPendingProposalsConversationId_Invocations: [Data] = [] public var commitPendingProposalsConversationId_MockError: Error? - public var commitPendingProposalsConversationId_MockMethod: ((Data) async throws -> WireCoreCrypto.CommitBundle?)? - public var commitPendingProposalsConversationId_MockValue: WireCoreCrypto.CommitBundle?? + public var commitPendingProposalsConversationId_MockMethod: ((Data) async throws -> Void)? - public func commitPendingProposals(conversationId: Data) async throws -> WireCoreCrypto.CommitBundle? { + public func commitPendingProposals(conversationId: Data) async throws { commitPendingProposalsConversationId_Invocations.append(conversationId) if let error = commitPendingProposalsConversationId_MockError { throw error } - if let mock = commitPendingProposalsConversationId_MockMethod { - return try await mock(conversationId) - } else if let mock = commitPendingProposalsConversationId_MockValue { - return mock - } else { + guard let mock = commitPendingProposalsConversationId_MockMethod else { fatalError("no mock for `commitPendingProposalsConversationId`") } + + try await mock(conversationId) } // MARK: - conversationCiphersuite public var conversationCiphersuiteConversationId_Invocations: [Data] = [] public var conversationCiphersuiteConversationId_MockError: Error? - public var conversationCiphersuiteConversationId_MockMethod: ((Data) async throws -> WireCoreCrypto.Ciphersuite)? - public var conversationCiphersuiteConversationId_MockValue: WireCoreCrypto.Ciphersuite? + public var conversationCiphersuiteConversationId_MockMethod: ((Data) async throws -> WireCoreCryptoUniffi.Ciphersuite)? + public var conversationCiphersuiteConversationId_MockValue: WireCoreCryptoUniffi.Ciphersuite? - public func conversationCiphersuite(conversationId: Data) async throws -> WireCoreCrypto.Ciphersuite { + public func conversationCiphersuite(conversationId: Data) async throws -> WireCoreCryptoUniffi.Ciphersuite { conversationCiphersuiteConversationId_Invocations.append(conversationId) if let error = conversationCiphersuiteConversationId_MockError { @@ -782,11 +584,11 @@ public class MockCoreCryptoProtocol: CoreCryptoProtocol { // MARK: - createConversation - public var createConversationConversationIdCreatorCredentialTypeConfig_Invocations: [(conversationId: Data, creatorCredentialType: WireCoreCrypto.MlsCredentialType, config: WireCoreCrypto.ConversationConfiguration)] = [] + public var createConversationConversationIdCreatorCredentialTypeConfig_Invocations: [(conversationId: Data, creatorCredentialType: WireCoreCryptoUniffi.MlsCredentialType, config: WireCoreCryptoUniffi.ConversationConfiguration)] = [] public var createConversationConversationIdCreatorCredentialTypeConfig_MockError: Error? - public var createConversationConversationIdCreatorCredentialTypeConfig_MockMethod: ((Data, WireCoreCrypto.MlsCredentialType, WireCoreCrypto.ConversationConfiguration) async throws -> Void)? + public var createConversationConversationIdCreatorCredentialTypeConfig_MockMethod: ((Data, WireCoreCryptoUniffi.MlsCredentialType, WireCoreCryptoUniffi.ConversationConfiguration) async throws -> Void)? - public func createConversation(conversationId: Data, creatorCredentialType: WireCoreCrypto.MlsCredentialType, config: WireCoreCrypto.ConversationConfiguration) async throws { + public func createConversation(conversationId: Data, creatorCredentialType: WireCoreCryptoUniffi.MlsCredentialType, config: WireCoreCryptoUniffi.ConversationConfiguration) async throws { createConversationConversationIdCreatorCredentialTypeConfig_Invocations.append((conversationId: conversationId, creatorCredentialType: creatorCredentialType, config: config)) if let error = createConversationConversationIdCreatorCredentialTypeConfig_MockError { @@ -804,10 +606,10 @@ public class MockCoreCryptoProtocol: CoreCryptoProtocol { public var decryptMessageConversationIdPayload_Invocations: [(conversationId: Data, payload: Data)] = [] public var decryptMessageConversationIdPayload_MockError: Error? - public var decryptMessageConversationIdPayload_MockMethod: ((Data, Data) async throws -> WireCoreCrypto.DecryptedMessage)? - public var decryptMessageConversationIdPayload_MockValue: WireCoreCrypto.DecryptedMessage? + public var decryptMessageConversationIdPayload_MockMethod: ((Data, Data) async throws -> WireCoreCryptoUniffi.DecryptedMessage)? + public var decryptMessageConversationIdPayload_MockValue: WireCoreCryptoUniffi.DecryptedMessage? - public func decryptMessage(conversationId: Data, payload: Data) async throws -> WireCoreCrypto.DecryptedMessage { + public func decryptMessage(conversationId: Data, payload: Data) async throws -> WireCoreCryptoUniffi.DecryptedMessage { decryptMessageConversationIdPayload_Invocations.append((conversationId: conversationId, payload: payload)) if let error = decryptMessageConversationIdPayload_MockError { @@ -843,14 +645,34 @@ public class MockCoreCryptoProtocol: CoreCryptoProtocol { try await mock(refs) } + // MARK: - deleteStaleKeyPackages + + public var deleteStaleKeyPackagesCiphersuite_Invocations: [WireCoreCryptoUniffi.Ciphersuite] = [] + public var deleteStaleKeyPackagesCiphersuite_MockError: Error? + public var deleteStaleKeyPackagesCiphersuite_MockMethod: ((WireCoreCryptoUniffi.Ciphersuite) async throws -> Void)? + + public func deleteStaleKeyPackages(ciphersuite: WireCoreCryptoUniffi.Ciphersuite) async throws { + deleteStaleKeyPackagesCiphersuite_Invocations.append(ciphersuite) + + if let error = deleteStaleKeyPackagesCiphersuite_MockError { + throw error + } + + guard let mock = deleteStaleKeyPackagesCiphersuite_MockMethod else { + fatalError("no mock for `deleteStaleKeyPackagesCiphersuite`") + } + + try await mock(ciphersuite) + } + // MARK: - e2eiConversationState public var e2eiConversationStateConversationId_Invocations: [Data] = [] public var e2eiConversationStateConversationId_MockError: Error? - public var e2eiConversationStateConversationId_MockMethod: ((Data) async throws -> WireCoreCrypto.E2eiConversationState)? - public var e2eiConversationStateConversationId_MockValue: WireCoreCrypto.E2eiConversationState? + public var e2eiConversationStateConversationId_MockMethod: ((Data) async throws -> WireCoreCryptoUniffi.E2eiConversationState)? + public var e2eiConversationStateConversationId_MockValue: WireCoreCryptoUniffi.E2eiConversationState? - public func e2eiConversationState(conversationId: Data) async throws -> WireCoreCrypto.E2eiConversationState { + public func e2eiConversationState(conversationId: Data) async throws -> WireCoreCryptoUniffi.E2eiConversationState { e2eiConversationStateConversationId_Invocations.append(conversationId) if let error = e2eiConversationStateConversationId_MockError { @@ -870,10 +692,10 @@ public class MockCoreCryptoProtocol: CoreCryptoProtocol { public var e2eiDumpPkiEnv_Invocations: [Void] = [] public var e2eiDumpPkiEnv_MockError: Error? - public var e2eiDumpPkiEnv_MockMethod: (() async throws -> WireCoreCrypto.E2eiDumpedPkiEnv?)? - public var e2eiDumpPkiEnv_MockValue: WireCoreCrypto.E2eiDumpedPkiEnv?? + public var e2eiDumpPkiEnv_MockMethod: (() async throws -> WireCoreCryptoUniffi.E2eiDumpedPkiEnv?)? + public var e2eiDumpPkiEnv_MockValue: WireCoreCryptoUniffi.E2eiDumpedPkiEnv?? - public func e2eiDumpPkiEnv() async throws -> WireCoreCrypto.E2eiDumpedPkiEnv? { + public func e2eiDumpPkiEnv() async throws -> WireCoreCryptoUniffi.E2eiDumpedPkiEnv? { e2eiDumpPkiEnv_Invocations.append(()) if let error = e2eiDumpPkiEnv_MockError { @@ -891,12 +713,12 @@ public class MockCoreCryptoProtocol: CoreCryptoProtocol { // MARK: - e2eiEnrollmentStash - public var e2eiEnrollmentStashEnrollment_Invocations: [WireCoreCrypto.E2eiEnrollment] = [] + public var e2eiEnrollmentStashEnrollment_Invocations: [WireCoreCryptoUniffi.E2eiEnrollment] = [] public var e2eiEnrollmentStashEnrollment_MockError: Error? - public var e2eiEnrollmentStashEnrollment_MockMethod: ((WireCoreCrypto.E2eiEnrollment) async throws -> Data)? + public var e2eiEnrollmentStashEnrollment_MockMethod: ((WireCoreCryptoUniffi.E2eiEnrollment) async throws -> Data)? public var e2eiEnrollmentStashEnrollment_MockValue: Data? - public func e2eiEnrollmentStash(enrollment: WireCoreCrypto.E2eiEnrollment) async throws -> Data { + public func e2eiEnrollmentStash(enrollment: WireCoreCryptoUniffi.E2eiEnrollment) async throws -> Data { e2eiEnrollmentStashEnrollment_Invocations.append(enrollment) if let error = e2eiEnrollmentStashEnrollment_MockError { @@ -916,10 +738,10 @@ public class MockCoreCryptoProtocol: CoreCryptoProtocol { public var e2eiEnrollmentStashPopHandle_Invocations: [Data] = [] public var e2eiEnrollmentStashPopHandle_MockError: Error? - public var e2eiEnrollmentStashPopHandle_MockMethod: ((Data) async throws -> WireCoreCrypto.E2eiEnrollment)? - public var e2eiEnrollmentStashPopHandle_MockValue: WireCoreCrypto.E2eiEnrollment? + public var e2eiEnrollmentStashPopHandle_MockMethod: ((Data) async throws -> WireCoreCryptoUniffi.E2eiEnrollment)? + public var e2eiEnrollmentStashPopHandle_MockValue: WireCoreCryptoUniffi.E2eiEnrollment? - public func e2eiEnrollmentStashPop(handle: Data) async throws -> WireCoreCrypto.E2eiEnrollment { + public func e2eiEnrollmentStashPop(handle: Data) async throws -> WireCoreCryptoUniffi.E2eiEnrollment { e2eiEnrollmentStashPopHandle_Invocations.append(handle) if let error = e2eiEnrollmentStashPopHandle_MockError { @@ -937,12 +759,12 @@ public class MockCoreCryptoProtocol: CoreCryptoProtocol { // MARK: - e2eiIsEnabled - public var e2eiIsEnabledCiphersuite_Invocations: [WireCoreCrypto.Ciphersuite] = [] + public var e2eiIsEnabledCiphersuite_Invocations: [WireCoreCryptoUniffi.Ciphersuite] = [] public var e2eiIsEnabledCiphersuite_MockError: Error? - public var e2eiIsEnabledCiphersuite_MockMethod: ((WireCoreCrypto.Ciphersuite) async throws -> Bool)? + public var e2eiIsEnabledCiphersuite_MockMethod: ((WireCoreCryptoUniffi.Ciphersuite) async throws -> Bool)? public var e2eiIsEnabledCiphersuite_MockValue: Bool? - public func e2eiIsEnabled(ciphersuite: WireCoreCrypto.Ciphersuite) async throws -> Bool { + public func e2eiIsEnabled(ciphersuite: WireCoreCryptoUniffi.Ciphersuite) async throws -> Bool { e2eiIsEnabledCiphersuite_Invocations.append(ciphersuite) if let error = e2eiIsEnabledCiphersuite_MockError { @@ -961,14 +783,19 @@ public class MockCoreCryptoProtocol: CoreCryptoProtocol { // MARK: - e2eiIsPkiEnvSetup public var e2eiIsPkiEnvSetup_Invocations: [Void] = [] - public var e2eiIsPkiEnvSetup_MockMethod: (() async -> Bool)? + public var e2eiIsPkiEnvSetup_MockError: Error? + public var e2eiIsPkiEnvSetup_MockMethod: (() async throws -> Bool)? public var e2eiIsPkiEnvSetup_MockValue: Bool? - public func e2eiIsPkiEnvSetup() async -> Bool { + public func e2eiIsPkiEnvSetup() async throws -> Bool { e2eiIsPkiEnvSetup_Invocations.append(()) + if let error = e2eiIsPkiEnvSetup_MockError { + throw error + } + if let mock = e2eiIsPkiEnvSetup_MockMethod { - return await mock() + return try await mock() } else if let mock = e2eiIsPkiEnvSetup_MockValue { return mock } else { @@ -978,12 +805,12 @@ public class MockCoreCryptoProtocol: CoreCryptoProtocol { // MARK: - e2eiMlsInitOnly - public var e2eiMlsInitOnlyEnrollmentCertificateChainNbKeyPackage_Invocations: [(enrollment: WireCoreCrypto.E2eiEnrollment, certificateChain: String, nbKeyPackage: UInt32?)] = [] + public var e2eiMlsInitOnlyEnrollmentCertificateChainNbKeyPackage_Invocations: [(enrollment: WireCoreCryptoUniffi.E2eiEnrollment, certificateChain: String, nbKeyPackage: UInt32?)] = [] public var e2eiMlsInitOnlyEnrollmentCertificateChainNbKeyPackage_MockError: Error? - public var e2eiMlsInitOnlyEnrollmentCertificateChainNbKeyPackage_MockMethod: ((WireCoreCrypto.E2eiEnrollment, String, UInt32?) async throws -> [String]?)? - public var e2eiMlsInitOnlyEnrollmentCertificateChainNbKeyPackage_MockValue: [String]?? + public var e2eiMlsInitOnlyEnrollmentCertificateChainNbKeyPackage_MockMethod: ((WireCoreCryptoUniffi.E2eiEnrollment, String, UInt32?) async throws -> WireCoreCryptoUniffi.NewCrlDistributionPoints)? + public var e2eiMlsInitOnlyEnrollmentCertificateChainNbKeyPackage_MockValue: WireCoreCryptoUniffi.NewCrlDistributionPoints? - public func e2eiMlsInitOnly(enrollment: WireCoreCrypto.E2eiEnrollment, certificateChain: String, nbKeyPackage: UInt32?) async throws -> [String]? { + public func e2eiMlsInitOnly(enrollment: WireCoreCryptoUniffi.E2eiEnrollment, certificateChain: String, nbKeyPackage: UInt32?) async throws -> WireCoreCryptoUniffi.NewCrlDistributionPoints { e2eiMlsInitOnlyEnrollmentCertificateChainNbKeyPackage_Invocations.append((enrollment: enrollment, certificateChain: certificateChain, nbKeyPackage: nbKeyPackage)) if let error = e2eiMlsInitOnlyEnrollmentCertificateChainNbKeyPackage_MockError { @@ -1001,12 +828,12 @@ public class MockCoreCryptoProtocol: CoreCryptoProtocol { // MARK: - e2eiNewActivationEnrollment - public var e2eiNewActivationEnrollmentDisplayNameHandleTeamExpirySecCiphersuite_Invocations: [(displayName: String, handle: String, team: String?, expirySec: UInt32, ciphersuite: WireCoreCrypto.Ciphersuite)] = [] + public var e2eiNewActivationEnrollmentDisplayNameHandleTeamExpirySecCiphersuite_Invocations: [(displayName: String, handle: String, team: String?, expirySec: UInt32, ciphersuite: WireCoreCryptoUniffi.Ciphersuite)] = [] public var e2eiNewActivationEnrollmentDisplayNameHandleTeamExpirySecCiphersuite_MockError: Error? - public var e2eiNewActivationEnrollmentDisplayNameHandleTeamExpirySecCiphersuite_MockMethod: ((String, String, String?, UInt32, WireCoreCrypto.Ciphersuite) async throws -> WireCoreCrypto.E2eiEnrollment)? - public var e2eiNewActivationEnrollmentDisplayNameHandleTeamExpirySecCiphersuite_MockValue: WireCoreCrypto.E2eiEnrollment? + public var e2eiNewActivationEnrollmentDisplayNameHandleTeamExpirySecCiphersuite_MockMethod: ((String, String, String?, UInt32, WireCoreCryptoUniffi.Ciphersuite) async throws -> WireCoreCryptoUniffi.E2eiEnrollment)? + public var e2eiNewActivationEnrollmentDisplayNameHandleTeamExpirySecCiphersuite_MockValue: WireCoreCryptoUniffi.E2eiEnrollment? - public func e2eiNewActivationEnrollment(displayName: String, handle: String, team: String?, expirySec: UInt32, ciphersuite: WireCoreCrypto.Ciphersuite) async throws -> WireCoreCrypto.E2eiEnrollment { + public func e2eiNewActivationEnrollment(displayName: String, handle: String, team: String?, expirySec: UInt32, ciphersuite: WireCoreCryptoUniffi.Ciphersuite) async throws -> WireCoreCryptoUniffi.E2eiEnrollment { e2eiNewActivationEnrollmentDisplayNameHandleTeamExpirySecCiphersuite_Invocations.append((displayName: displayName, handle: handle, team: team, expirySec: expirySec, ciphersuite: ciphersuite)) if let error = e2eiNewActivationEnrollmentDisplayNameHandleTeamExpirySecCiphersuite_MockError { @@ -1024,12 +851,12 @@ public class MockCoreCryptoProtocol: CoreCryptoProtocol { // MARK: - e2eiNewEnrollment - public var e2eiNewEnrollmentClientIdDisplayNameHandleTeamExpirySecCiphersuite_Invocations: [(clientId: String, displayName: String, handle: String, team: String?, expirySec: UInt32, ciphersuite: WireCoreCrypto.Ciphersuite)] = [] + public var e2eiNewEnrollmentClientIdDisplayNameHandleTeamExpirySecCiphersuite_Invocations: [(clientId: String, displayName: String, handle: String, team: String?, expirySec: UInt32, ciphersuite: WireCoreCryptoUniffi.Ciphersuite)] = [] public var e2eiNewEnrollmentClientIdDisplayNameHandleTeamExpirySecCiphersuite_MockError: Error? - public var e2eiNewEnrollmentClientIdDisplayNameHandleTeamExpirySecCiphersuite_MockMethod: ((String, String, String, String?, UInt32, WireCoreCrypto.Ciphersuite) async throws -> WireCoreCrypto.E2eiEnrollment)? - public var e2eiNewEnrollmentClientIdDisplayNameHandleTeamExpirySecCiphersuite_MockValue: WireCoreCrypto.E2eiEnrollment? + public var e2eiNewEnrollmentClientIdDisplayNameHandleTeamExpirySecCiphersuite_MockMethod: ((String, String, String, String?, UInt32, WireCoreCryptoUniffi.Ciphersuite) async throws -> WireCoreCryptoUniffi.E2eiEnrollment)? + public var e2eiNewEnrollmentClientIdDisplayNameHandleTeamExpirySecCiphersuite_MockValue: WireCoreCryptoUniffi.E2eiEnrollment? - public func e2eiNewEnrollment(clientId: String, displayName: String, handle: String, team: String?, expirySec: UInt32, ciphersuite: WireCoreCrypto.Ciphersuite) async throws -> WireCoreCrypto.E2eiEnrollment { + public func e2eiNewEnrollment(clientId: String, displayName: String, handle: String, team: String?, expirySec: UInt32, ciphersuite: WireCoreCryptoUniffi.Ciphersuite) async throws -> WireCoreCryptoUniffi.E2eiEnrollment { e2eiNewEnrollmentClientIdDisplayNameHandleTeamExpirySecCiphersuite_Invocations.append((clientId: clientId, displayName: displayName, handle: handle, team: team, expirySec: expirySec, ciphersuite: ciphersuite)) if let error = e2eiNewEnrollmentClientIdDisplayNameHandleTeamExpirySecCiphersuite_MockError { @@ -1047,12 +874,12 @@ public class MockCoreCryptoProtocol: CoreCryptoProtocol { // MARK: - e2eiNewRotateEnrollment - public var e2eiNewRotateEnrollmentDisplayNameHandleTeamExpirySecCiphersuite_Invocations: [(displayName: String?, handle: String?, team: String?, expirySec: UInt32, ciphersuite: WireCoreCrypto.Ciphersuite)] = [] + public var e2eiNewRotateEnrollmentDisplayNameHandleTeamExpirySecCiphersuite_Invocations: [(displayName: String?, handle: String?, team: String?, expirySec: UInt32, ciphersuite: WireCoreCryptoUniffi.Ciphersuite)] = [] public var e2eiNewRotateEnrollmentDisplayNameHandleTeamExpirySecCiphersuite_MockError: Error? - public var e2eiNewRotateEnrollmentDisplayNameHandleTeamExpirySecCiphersuite_MockMethod: ((String?, String?, String?, UInt32, WireCoreCrypto.Ciphersuite) async throws -> WireCoreCrypto.E2eiEnrollment)? - public var e2eiNewRotateEnrollmentDisplayNameHandleTeamExpirySecCiphersuite_MockValue: WireCoreCrypto.E2eiEnrollment? + public var e2eiNewRotateEnrollmentDisplayNameHandleTeamExpirySecCiphersuite_MockMethod: ((String?, String?, String?, UInt32, WireCoreCryptoUniffi.Ciphersuite) async throws -> WireCoreCryptoUniffi.E2eiEnrollment)? + public var e2eiNewRotateEnrollmentDisplayNameHandleTeamExpirySecCiphersuite_MockValue: WireCoreCryptoUniffi.E2eiEnrollment? - public func e2eiNewRotateEnrollment(displayName: String?, handle: String?, team: String?, expirySec: UInt32, ciphersuite: WireCoreCrypto.Ciphersuite) async throws -> WireCoreCrypto.E2eiEnrollment { + public func e2eiNewRotateEnrollment(displayName: String?, handle: String?, team: String?, expirySec: UInt32, ciphersuite: WireCoreCryptoUniffi.Ciphersuite) async throws -> WireCoreCryptoUniffi.E2eiEnrollment { e2eiNewRotateEnrollmentDisplayNameHandleTeamExpirySecCiphersuite_Invocations.append((displayName: displayName, handle: handle, team: team, expirySec: expirySec, ciphersuite: ciphersuite)) if let error = e2eiNewRotateEnrollmentDisplayNameHandleTeamExpirySecCiphersuite_MockError { @@ -1092,10 +919,10 @@ public class MockCoreCryptoProtocol: CoreCryptoProtocol { public var e2eiRegisterCrlCrlDpCrlDer_Invocations: [(crlDp: String, crlDer: Data)] = [] public var e2eiRegisterCrlCrlDpCrlDer_MockError: Error? - public var e2eiRegisterCrlCrlDpCrlDer_MockMethod: ((String, Data) async throws -> WireCoreCrypto.CrlRegistration)? - public var e2eiRegisterCrlCrlDpCrlDer_MockValue: WireCoreCrypto.CrlRegistration? + public var e2eiRegisterCrlCrlDpCrlDer_MockMethod: ((String, Data) async throws -> WireCoreCryptoUniffi.CrlRegistration)? + public var e2eiRegisterCrlCrlDpCrlDer_MockValue: WireCoreCryptoUniffi.CrlRegistration? - public func e2eiRegisterCrl(crlDp: String, crlDer: Data) async throws -> WireCoreCrypto.CrlRegistration { + public func e2eiRegisterCrl(crlDp: String, crlDer: Data) async throws -> WireCoreCryptoUniffi.CrlRegistration { e2eiRegisterCrlCrlDpCrlDer_Invocations.append((crlDp: crlDp, crlDer: crlDer)) if let error = e2eiRegisterCrlCrlDpCrlDer_MockError { @@ -1115,10 +942,10 @@ public class MockCoreCryptoProtocol: CoreCryptoProtocol { public var e2eiRegisterIntermediateCaCertPem_Invocations: [String] = [] public var e2eiRegisterIntermediateCaCertPem_MockError: Error? - public var e2eiRegisterIntermediateCaCertPem_MockMethod: ((String) async throws -> [String]?)? - public var e2eiRegisterIntermediateCaCertPem_MockValue: [String]?? + public var e2eiRegisterIntermediateCaCertPem_MockMethod: ((String) async throws -> WireCoreCryptoUniffi.NewCrlDistributionPoints)? + public var e2eiRegisterIntermediateCaCertPem_MockValue: WireCoreCryptoUniffi.NewCrlDistributionPoints? - public func e2eiRegisterIntermediateCa(certPem: String) async throws -> [String]? { + public func e2eiRegisterIntermediateCa(certPem: String) async throws -> WireCoreCryptoUniffi.NewCrlDistributionPoints { e2eiRegisterIntermediateCaCertPem_Invocations.append(certPem) if let error = e2eiRegisterIntermediateCaCertPem_MockError { @@ -1134,50 +961,24 @@ public class MockCoreCryptoProtocol: CoreCryptoProtocol { } } - // MARK: - e2eiRotateAll - - public var e2eiRotateAllEnrollmentCertificateChainNewKeyPackagesCount_Invocations: [(enrollment: WireCoreCrypto.E2eiEnrollment, certificateChain: String, newKeyPackagesCount: UInt32)] = [] - public var e2eiRotateAllEnrollmentCertificateChainNewKeyPackagesCount_MockError: Error? - public var e2eiRotateAllEnrollmentCertificateChainNewKeyPackagesCount_MockMethod: ((WireCoreCrypto.E2eiEnrollment, String, UInt32) async throws -> WireCoreCrypto.RotateBundle)? - public var e2eiRotateAllEnrollmentCertificateChainNewKeyPackagesCount_MockValue: WireCoreCrypto.RotateBundle? - - public func e2eiRotateAll(enrollment: WireCoreCrypto.E2eiEnrollment, certificateChain: String, newKeyPackagesCount: UInt32) async throws -> WireCoreCrypto.RotateBundle { - e2eiRotateAllEnrollmentCertificateChainNewKeyPackagesCount_Invocations.append((enrollment: enrollment, certificateChain: certificateChain, newKeyPackagesCount: newKeyPackagesCount)) - - if let error = e2eiRotateAllEnrollmentCertificateChainNewKeyPackagesCount_MockError { - throw error - } - - if let mock = e2eiRotateAllEnrollmentCertificateChainNewKeyPackagesCount_MockMethod { - return try await mock(enrollment, certificateChain, newKeyPackagesCount) - } else if let mock = e2eiRotateAllEnrollmentCertificateChainNewKeyPackagesCount_MockValue { - return mock - } else { - fatalError("no mock for `e2eiRotateAllEnrollmentCertificateChainNewKeyPackagesCount`") - } - } - // MARK: - e2eiRotate public var e2eiRotateConversationId_Invocations: [Data] = [] public var e2eiRotateConversationId_MockError: Error? - public var e2eiRotateConversationId_MockMethod: ((Data) async throws -> WireCoreCrypto.CommitBundle)? - public var e2eiRotateConversationId_MockValue: WireCoreCrypto.CommitBundle? + public var e2eiRotateConversationId_MockMethod: ((Data) async throws -> Void)? - public func e2eiRotate(conversationId: Data) async throws -> WireCoreCrypto.CommitBundle { + public func e2eiRotate(conversationId: Data) async throws { e2eiRotateConversationId_Invocations.append(conversationId) if let error = e2eiRotateConversationId_MockError { throw error } - if let mock = e2eiRotateConversationId_MockMethod { - return try await mock(conversationId) - } else if let mock = e2eiRotateConversationId_MockValue { - return mock - } else { + guard let mock = e2eiRotateConversationId_MockMethod else { fatalError("no mock for `e2eiRotateConversationId`") } + + try await mock(conversationId) } // MARK: - encryptMessage @@ -1230,10 +1031,10 @@ public class MockCoreCryptoProtocol: CoreCryptoProtocol { public var getClientIdsConversationId_Invocations: [Data] = [] public var getClientIdsConversationId_MockError: Error? - public var getClientIdsConversationId_MockMethod: ((Data) async throws -> [WireCoreCrypto.ClientId])? - public var getClientIdsConversationId_MockValue: [WireCoreCrypto.ClientId]? + public var getClientIdsConversationId_MockMethod: ((Data) async throws -> [WireCoreCryptoUniffi.ClientId])? + public var getClientIdsConversationId_MockValue: [WireCoreCryptoUniffi.ClientId]? - public func getClientIds(conversationId: Data) async throws -> [WireCoreCrypto.ClientId] { + public func getClientIds(conversationId: Data) async throws -> [WireCoreCryptoUniffi.ClientId] { getClientIdsConversationId_Invocations.append(conversationId) if let error = getClientIdsConversationId_MockError { @@ -1251,12 +1052,12 @@ public class MockCoreCryptoProtocol: CoreCryptoProtocol { // MARK: - getCredentialInUse - public var getCredentialInUseGroupInfoCredentialType_Invocations: [(groupInfo: Data, credentialType: WireCoreCrypto.MlsCredentialType)] = [] + public var getCredentialInUseGroupInfoCredentialType_Invocations: [(groupInfo: Data, credentialType: WireCoreCryptoUniffi.MlsCredentialType)] = [] public var getCredentialInUseGroupInfoCredentialType_MockError: Error? - public var getCredentialInUseGroupInfoCredentialType_MockMethod: ((Data, WireCoreCrypto.MlsCredentialType) async throws -> WireCoreCrypto.E2eiConversationState)? - public var getCredentialInUseGroupInfoCredentialType_MockValue: WireCoreCrypto.E2eiConversationState? + public var getCredentialInUseGroupInfoCredentialType_MockMethod: ((Data, WireCoreCryptoUniffi.MlsCredentialType) async throws -> WireCoreCryptoUniffi.E2eiConversationState)? + public var getCredentialInUseGroupInfoCredentialType_MockValue: WireCoreCryptoUniffi.E2eiConversationState? - public func getCredentialInUse(groupInfo: Data, credentialType: WireCoreCrypto.MlsCredentialType) async throws -> WireCoreCrypto.E2eiConversationState { + public func getCredentialInUse(groupInfo: Data, credentialType: WireCoreCryptoUniffi.MlsCredentialType) async throws -> WireCoreCryptoUniffi.E2eiConversationState { getCredentialInUseGroupInfoCredentialType_Invocations.append((groupInfo: groupInfo, credentialType: credentialType)) if let error = getCredentialInUseGroupInfoCredentialType_MockError { @@ -1272,14 +1073,37 @@ public class MockCoreCryptoProtocol: CoreCryptoProtocol { } } + // MARK: - getData + + public var getData_Invocations: [Void] = [] + public var getData_MockError: Error? + public var getData_MockMethod: (() async throws -> Data?)? + public var getData_MockValue: Data?? + + public func getData() async throws -> Data? { + getData_Invocations.append(()) + + if let error = getData_MockError { + throw error + } + + if let mock = getData_MockMethod { + return try await mock() + } else if let mock = getData_MockValue { + return mock + } else { + fatalError("no mock for `getData`") + } + } + // MARK: - getDeviceIdentities - public var getDeviceIdentitiesConversationIdDeviceIds_Invocations: [(conversationId: Data, deviceIds: [WireCoreCrypto.ClientId])] = [] + public var getDeviceIdentitiesConversationIdDeviceIds_Invocations: [(conversationId: Data, deviceIds: [WireCoreCryptoUniffi.ClientId])] = [] public var getDeviceIdentitiesConversationIdDeviceIds_MockError: Error? - public var getDeviceIdentitiesConversationIdDeviceIds_MockMethod: ((Data, [WireCoreCrypto.ClientId]) async throws -> [WireCoreCrypto.WireIdentity])? - public var getDeviceIdentitiesConversationIdDeviceIds_MockValue: [WireCoreCrypto.WireIdentity]? + public var getDeviceIdentitiesConversationIdDeviceIds_MockMethod: ((Data, [WireCoreCryptoUniffi.ClientId]) async throws -> [WireCoreCryptoUniffi.WireIdentity])? + public var getDeviceIdentitiesConversationIdDeviceIds_MockValue: [WireCoreCryptoUniffi.WireIdentity]? - public func getDeviceIdentities(conversationId: Data, deviceIds: [WireCoreCrypto.ClientId]) async throws -> [WireCoreCrypto.WireIdentity] { + public func getDeviceIdentities(conversationId: Data, deviceIds: [WireCoreCryptoUniffi.ClientId]) async throws -> [WireCoreCryptoUniffi.WireIdentity] { getDeviceIdentitiesConversationIdDeviceIds_Invocations.append((conversationId: conversationId, deviceIds: deviceIds)) if let error = getDeviceIdentitiesConversationIdDeviceIds_MockError { @@ -1322,10 +1146,10 @@ public class MockCoreCryptoProtocol: CoreCryptoProtocol { public var getUserIdentitiesConversationIdUserIds_Invocations: [(conversationId: Data, userIds: [String])] = [] public var getUserIdentitiesConversationIdUserIds_MockError: Error? - public var getUserIdentitiesConversationIdUserIds_MockMethod: ((Data, [String]) async throws -> [String: [WireCoreCrypto.WireIdentity]])? - public var getUserIdentitiesConversationIdUserIds_MockValue: [String: [WireCoreCrypto.WireIdentity]]? + public var getUserIdentitiesConversationIdUserIds_MockMethod: ((Data, [String]) async throws -> [String: [WireCoreCryptoUniffi.WireIdentity]])? + public var getUserIdentitiesConversationIdUserIds_MockValue: [String: [WireCoreCryptoUniffi.WireIdentity]]? - public func getUserIdentities(conversationId: Data, userIds: [String]) async throws -> [String: [WireCoreCrypto.WireIdentity]] { + public func getUserIdentities(conversationId: Data, userIds: [String]) async throws -> [String: [WireCoreCryptoUniffi.WireIdentity]] { getUserIdentitiesConversationIdUserIds_Invocations.append((conversationId: conversationId, userIds: userIds)) if let error = getUserIdentitiesConversationIdUserIds_MockError { @@ -1343,12 +1167,12 @@ public class MockCoreCryptoProtocol: CoreCryptoProtocol { // MARK: - joinByExternalCommit - public var joinByExternalCommitGroupInfoCustomConfigurationCredentialType_Invocations: [(groupInfo: Data, customConfiguration: WireCoreCrypto.CustomConfiguration, credentialType: WireCoreCrypto.MlsCredentialType)] = [] + public var joinByExternalCommitGroupInfoCustomConfigurationCredentialType_Invocations: [(groupInfo: Data, customConfiguration: WireCoreCryptoUniffi.CustomConfiguration, credentialType: WireCoreCryptoUniffi.MlsCredentialType)] = [] public var joinByExternalCommitGroupInfoCustomConfigurationCredentialType_MockError: Error? - public var joinByExternalCommitGroupInfoCustomConfigurationCredentialType_MockMethod: ((Data, WireCoreCrypto.CustomConfiguration, WireCoreCrypto.MlsCredentialType) async throws -> WireCoreCrypto.ConversationInitBundle)? - public var joinByExternalCommitGroupInfoCustomConfigurationCredentialType_MockValue: WireCoreCrypto.ConversationInitBundle? + public var joinByExternalCommitGroupInfoCustomConfigurationCredentialType_MockMethod: ((Data, WireCoreCryptoUniffi.CustomConfiguration, WireCoreCryptoUniffi.MlsCredentialType) async throws -> WireCoreCryptoUniffi.WelcomeBundle)? + public var joinByExternalCommitGroupInfoCustomConfigurationCredentialType_MockValue: WireCoreCryptoUniffi.WelcomeBundle? - public func joinByExternalCommit(groupInfo: Data, customConfiguration: WireCoreCrypto.CustomConfiguration, credentialType: WireCoreCrypto.MlsCredentialType) async throws -> WireCoreCrypto.ConversationInitBundle { + public func joinByExternalCommit(groupInfo: Data, customConfiguration: WireCoreCryptoUniffi.CustomConfiguration, credentialType: WireCoreCryptoUniffi.MlsCredentialType) async throws -> WireCoreCryptoUniffi.WelcomeBundle { joinByExternalCommitGroupInfoCustomConfigurationCredentialType_Invocations.append((groupInfo: groupInfo, customConfiguration: customConfiguration, credentialType: credentialType)) if let error = joinByExternalCommitGroupInfoCustomConfigurationCredentialType_MockError { @@ -1384,37 +1208,14 @@ public class MockCoreCryptoProtocol: CoreCryptoProtocol { try await mock(childId, parentId) } - // MARK: - mergePendingGroupFromExternalCommit - - public var mergePendingGroupFromExternalCommitConversationId_Invocations: [Data] = [] - public var mergePendingGroupFromExternalCommitConversationId_MockError: Error? - public var mergePendingGroupFromExternalCommitConversationId_MockMethod: ((Data) async throws -> [WireCoreCrypto.BufferedDecryptedMessage]?)? - public var mergePendingGroupFromExternalCommitConversationId_MockValue: [WireCoreCrypto.BufferedDecryptedMessage]?? - - public func mergePendingGroupFromExternalCommit(conversationId: Data) async throws -> [WireCoreCrypto.BufferedDecryptedMessage]? { - mergePendingGroupFromExternalCommitConversationId_Invocations.append(conversationId) - - if let error = mergePendingGroupFromExternalCommitConversationId_MockError { - throw error - } - - if let mock = mergePendingGroupFromExternalCommitConversationId_MockMethod { - return try await mock(conversationId) - } else if let mock = mergePendingGroupFromExternalCommitConversationId_MockValue { - return mock - } else { - fatalError("no mock for `mergePendingGroupFromExternalCommitConversationId`") - } - } - // MARK: - mlsGenerateKeypairs - public var mlsGenerateKeypairsCiphersuites_Invocations: [WireCoreCrypto.Ciphersuites] = [] + public var mlsGenerateKeypairsCiphersuites_Invocations: [WireCoreCryptoUniffi.Ciphersuites] = [] public var mlsGenerateKeypairsCiphersuites_MockError: Error? - public var mlsGenerateKeypairsCiphersuites_MockMethod: ((WireCoreCrypto.Ciphersuites) async throws -> [WireCoreCrypto.ClientId])? - public var mlsGenerateKeypairsCiphersuites_MockValue: [WireCoreCrypto.ClientId]? + public var mlsGenerateKeypairsCiphersuites_MockMethod: ((WireCoreCryptoUniffi.Ciphersuites) async throws -> [WireCoreCryptoUniffi.ClientId])? + public var mlsGenerateKeypairsCiphersuites_MockValue: [WireCoreCryptoUniffi.ClientId]? - public func mlsGenerateKeypairs(ciphersuites: WireCoreCrypto.Ciphersuites) async throws -> [WireCoreCrypto.ClientId] { + public func mlsGenerateKeypairs(ciphersuites: WireCoreCryptoUniffi.Ciphersuites) async throws -> [WireCoreCryptoUniffi.ClientId] { mlsGenerateKeypairsCiphersuites_Invocations.append(ciphersuites) if let error = mlsGenerateKeypairsCiphersuites_MockError { @@ -1432,11 +1233,11 @@ public class MockCoreCryptoProtocol: CoreCryptoProtocol { // MARK: - mlsInit - public var mlsInitClientIdCiphersuitesNbKeyPackage_Invocations: [(clientId: WireCoreCrypto.ClientId, ciphersuites: WireCoreCrypto.Ciphersuites, nbKeyPackage: UInt32?)] = [] + public var mlsInitClientIdCiphersuitesNbKeyPackage_Invocations: [(clientId: WireCoreCryptoUniffi.ClientId, ciphersuites: WireCoreCryptoUniffi.Ciphersuites, nbKeyPackage: UInt32?)] = [] public var mlsInitClientIdCiphersuitesNbKeyPackage_MockError: Error? - public var mlsInitClientIdCiphersuitesNbKeyPackage_MockMethod: ((WireCoreCrypto.ClientId, WireCoreCrypto.Ciphersuites, UInt32?) async throws -> Void)? + public var mlsInitClientIdCiphersuitesNbKeyPackage_MockMethod: ((WireCoreCryptoUniffi.ClientId, WireCoreCryptoUniffi.Ciphersuites, UInt32?) async throws -> Void)? - public func mlsInit(clientId: WireCoreCrypto.ClientId, ciphersuites: WireCoreCrypto.Ciphersuites, nbKeyPackage: UInt32?) async throws { + public func mlsInit(clientId: WireCoreCryptoUniffi.ClientId, ciphersuites: WireCoreCryptoUniffi.Ciphersuites, nbKeyPackage: UInt32?) async throws { mlsInitClientIdCiphersuitesNbKeyPackage_Invocations.append((clientId: clientId, ciphersuites: ciphersuites, nbKeyPackage: nbKeyPackage)) if let error = mlsInitClientIdCiphersuitesNbKeyPackage_MockError { @@ -1452,11 +1253,11 @@ public class MockCoreCryptoProtocol: CoreCryptoProtocol { // MARK: - mlsInitWithClientId - public var mlsInitWithClientIdClientIdTmpClientIdsCiphersuites_Invocations: [(clientId: WireCoreCrypto.ClientId, tmpClientIds: [WireCoreCrypto.ClientId], ciphersuites: WireCoreCrypto.Ciphersuites)] = [] + public var mlsInitWithClientIdClientIdTmpClientIdsCiphersuites_Invocations: [(clientId: WireCoreCryptoUniffi.ClientId, tmpClientIds: [WireCoreCryptoUniffi.ClientId], ciphersuites: WireCoreCryptoUniffi.Ciphersuites)] = [] public var mlsInitWithClientIdClientIdTmpClientIdsCiphersuites_MockError: Error? - public var mlsInitWithClientIdClientIdTmpClientIdsCiphersuites_MockMethod: ((WireCoreCrypto.ClientId, [WireCoreCrypto.ClientId], WireCoreCrypto.Ciphersuites) async throws -> Void)? + public var mlsInitWithClientIdClientIdTmpClientIdsCiphersuites_MockMethod: ((WireCoreCryptoUniffi.ClientId, [WireCoreCryptoUniffi.ClientId], WireCoreCryptoUniffi.Ciphersuites) async throws -> Void)? - public func mlsInitWithClientId(clientId: WireCoreCrypto.ClientId, tmpClientIds: [WireCoreCrypto.ClientId], ciphersuites: WireCoreCrypto.Ciphersuites) async throws { + public func mlsInitWithClientId(clientId: WireCoreCryptoUniffi.ClientId, tmpClientIds: [WireCoreCryptoUniffi.ClientId], ciphersuites: WireCoreCryptoUniffi.Ciphersuites) async throws { mlsInitWithClientIdClientIdTmpClientIdsCiphersuites_Invocations.append((clientId: clientId, tmpClientIds: tmpClientIds, ciphersuites: ciphersuites)) if let error = mlsInitWithClientIdClientIdTmpClientIdsCiphersuites_MockError { @@ -1470,106 +1271,14 @@ public class MockCoreCryptoProtocol: CoreCryptoProtocol { try await mock(clientId, tmpClientIds, ciphersuites) } - // MARK: - newAddProposal - - public var newAddProposalConversationIdKeypackage_Invocations: [(conversationId: Data, keypackage: Data)] = [] - public var newAddProposalConversationIdKeypackage_MockError: Error? - public var newAddProposalConversationIdKeypackage_MockMethod: ((Data, Data) async throws -> WireCoreCrypto.ProposalBundle)? - public var newAddProposalConversationIdKeypackage_MockValue: WireCoreCrypto.ProposalBundle? - - public func newAddProposal(conversationId: Data, keypackage: Data) async throws -> WireCoreCrypto.ProposalBundle { - newAddProposalConversationIdKeypackage_Invocations.append((conversationId: conversationId, keypackage: keypackage)) - - if let error = newAddProposalConversationIdKeypackage_MockError { - throw error - } - - if let mock = newAddProposalConversationIdKeypackage_MockMethod { - return try await mock(conversationId, keypackage) - } else if let mock = newAddProposalConversationIdKeypackage_MockValue { - return mock - } else { - fatalError("no mock for `newAddProposalConversationIdKeypackage`") - } - } - - // MARK: - newExternalAddProposal - - public var newExternalAddProposalConversationIdEpochCiphersuiteCredentialType_Invocations: [(conversationId: Data, epoch: UInt64, ciphersuite: WireCoreCrypto.Ciphersuite, credentialType: WireCoreCrypto.MlsCredentialType)] = [] - public var newExternalAddProposalConversationIdEpochCiphersuiteCredentialType_MockError: Error? - public var newExternalAddProposalConversationIdEpochCiphersuiteCredentialType_MockMethod: ((Data, UInt64, WireCoreCrypto.Ciphersuite, WireCoreCrypto.MlsCredentialType) async throws -> Data)? - public var newExternalAddProposalConversationIdEpochCiphersuiteCredentialType_MockValue: Data? - - public func newExternalAddProposal(conversationId: Data, epoch: UInt64, ciphersuite: WireCoreCrypto.Ciphersuite, credentialType: WireCoreCrypto.MlsCredentialType) async throws -> Data { - newExternalAddProposalConversationIdEpochCiphersuiteCredentialType_Invocations.append((conversationId: conversationId, epoch: epoch, ciphersuite: ciphersuite, credentialType: credentialType)) - - if let error = newExternalAddProposalConversationIdEpochCiphersuiteCredentialType_MockError { - throw error - } - - if let mock = newExternalAddProposalConversationIdEpochCiphersuiteCredentialType_MockMethod { - return try await mock(conversationId, epoch, ciphersuite, credentialType) - } else if let mock = newExternalAddProposalConversationIdEpochCiphersuiteCredentialType_MockValue { - return mock - } else { - fatalError("no mock for `newExternalAddProposalConversationIdEpochCiphersuiteCredentialType`") - } - } - - // MARK: - newRemoveProposal - - public var newRemoveProposalConversationIdClientId_Invocations: [(conversationId: Data, clientId: WireCoreCrypto.ClientId)] = [] - public var newRemoveProposalConversationIdClientId_MockError: Error? - public var newRemoveProposalConversationIdClientId_MockMethod: ((Data, WireCoreCrypto.ClientId) async throws -> WireCoreCrypto.ProposalBundle)? - public var newRemoveProposalConversationIdClientId_MockValue: WireCoreCrypto.ProposalBundle? - - public func newRemoveProposal(conversationId: Data, clientId: WireCoreCrypto.ClientId) async throws -> WireCoreCrypto.ProposalBundle { - newRemoveProposalConversationIdClientId_Invocations.append((conversationId: conversationId, clientId: clientId)) - - if let error = newRemoveProposalConversationIdClientId_MockError { - throw error - } - - if let mock = newRemoveProposalConversationIdClientId_MockMethod { - return try await mock(conversationId, clientId) - } else if let mock = newRemoveProposalConversationIdClientId_MockValue { - return mock - } else { - fatalError("no mock for `newRemoveProposalConversationIdClientId`") - } - } - - // MARK: - newUpdateProposal - - public var newUpdateProposalConversationId_Invocations: [Data] = [] - public var newUpdateProposalConversationId_MockError: Error? - public var newUpdateProposalConversationId_MockMethod: ((Data) async throws -> WireCoreCrypto.ProposalBundle)? - public var newUpdateProposalConversationId_MockValue: WireCoreCrypto.ProposalBundle? - - public func newUpdateProposal(conversationId: Data) async throws -> WireCoreCrypto.ProposalBundle { - newUpdateProposalConversationId_Invocations.append(conversationId) - - if let error = newUpdateProposalConversationId_MockError { - throw error - } - - if let mock = newUpdateProposalConversationId_MockMethod { - return try await mock(conversationId) - } else if let mock = newUpdateProposalConversationId_MockValue { - return mock - } else { - fatalError("no mock for `newUpdateProposalConversationId`") - } - } - // MARK: - processWelcomeMessage - public var processWelcomeMessageWelcomeMessageCustomConfiguration_Invocations: [(welcomeMessage: Data, customConfiguration: WireCoreCrypto.CustomConfiguration)] = [] + public var processWelcomeMessageWelcomeMessageCustomConfiguration_Invocations: [(welcomeMessage: Data, customConfiguration: WireCoreCryptoUniffi.CustomConfiguration)] = [] public var processWelcomeMessageWelcomeMessageCustomConfiguration_MockError: Error? - public var processWelcomeMessageWelcomeMessageCustomConfiguration_MockMethod: ((Data, WireCoreCrypto.CustomConfiguration) async throws -> WireCoreCrypto.WelcomeBundle)? - public var processWelcomeMessageWelcomeMessageCustomConfiguration_MockValue: WireCoreCrypto.WelcomeBundle? + public var processWelcomeMessageWelcomeMessageCustomConfiguration_MockMethod: ((Data, WireCoreCryptoUniffi.CustomConfiguration) async throws -> WireCoreCryptoUniffi.WelcomeBundle)? + public var processWelcomeMessageWelcomeMessageCustomConfiguration_MockValue: WireCoreCryptoUniffi.WelcomeBundle? - public func processWelcomeMessage(welcomeMessage: Data, customConfiguration: WireCoreCrypto.CustomConfiguration) async throws -> WireCoreCrypto.WelcomeBundle { + public func processWelcomeMessage(welcomeMessage: Data, customConfiguration: WireCoreCryptoUniffi.CustomConfiguration) async throws -> WireCoreCryptoUniffi.WelcomeBundle { processWelcomeMessageWelcomeMessageCustomConfiguration_Invocations.append((welcomeMessage: welcomeMessage, customConfiguration: customConfiguration)) if let error = processWelcomeMessageWelcomeMessageCustomConfiguration_MockError { @@ -1786,24 +1495,6 @@ public class MockCoreCryptoProtocol: CoreCryptoProtocol { try await mock() } - // MARK: - proteusLastErrorCode - - public var proteusLastErrorCode_Invocations: [Void] = [] - public var proteusLastErrorCode_MockMethod: (() -> UInt16?)? - public var proteusLastErrorCode_MockValue: UInt16?? - - public func proteusLastErrorCode() -> UInt16? { - proteusLastErrorCode_Invocations.append(()) - - if let mock = proteusLastErrorCode_MockMethod { - return mock() - } else if let mock = proteusLastErrorCode_MockValue { - return mock - } else { - fatalError("no mock for `proteusLastErrorCode`") - } - } - // MARK: - proteusLastResortPrekey public var proteusLastResortPrekey_Invocations: [Void] = [] @@ -1877,10 +1568,10 @@ public class MockCoreCryptoProtocol: CoreCryptoProtocol { public var proteusNewPrekeyAuto_Invocations: [Void] = [] public var proteusNewPrekeyAuto_MockError: Error? - public var proteusNewPrekeyAuto_MockMethod: (() async throws -> WireCoreCrypto.ProteusAutoPrekeyBundle)? - public var proteusNewPrekeyAuto_MockValue: WireCoreCrypto.ProteusAutoPrekeyBundle? + public var proteusNewPrekeyAuto_MockMethod: (() async throws -> WireCoreCryptoUniffi.ProteusAutoPrekeyBundle)? + public var proteusNewPrekeyAuto_MockValue: WireCoreCryptoUniffi.ProteusAutoPrekeyBundle? - public func proteusNewPrekeyAuto() async throws -> WireCoreCrypto.ProteusAutoPrekeyBundle { + public func proteusNewPrekeyAuto() async throws -> WireCoreCryptoUniffi.ProteusAutoPrekeyBundle { proteusNewPrekeyAuto_Invocations.append(()) if let error = proteusNewPrekeyAuto_MockError { @@ -1896,6 +1587,26 @@ public class MockCoreCryptoProtocol: CoreCryptoProtocol { } } + // MARK: - proteusReloadSessions + + public var proteusReloadSessions_Invocations: [Void] = [] + public var proteusReloadSessions_MockError: Error? + public var proteusReloadSessions_MockMethod: (() async throws -> Void)? + + public func proteusReloadSessions() async throws { + proteusReloadSessions_Invocations.append(()) + + if let error = proteusReloadSessions_MockError { + throw error + } + + guard let mock = proteusReloadSessions_MockMethod else { + fatalError("no mock for `proteusReloadSessions`") + } + + try await mock() + } + // MARK: - proteusSessionDelete public var proteusSessionDeleteSessionId_Invocations: [String] = [] @@ -2002,193 +1713,87 @@ public class MockCoreCryptoProtocol: CoreCryptoProtocol { try await mock(sessionId) } - // MARK: - randomBytes - - public var randomBytesLen_Invocations: [UInt32] = [] - public var randomBytesLen_MockError: Error? - public var randomBytesLen_MockMethod: ((UInt32) async throws -> Data)? - public var randomBytesLen_MockValue: Data? - - public func randomBytes(len: UInt32) async throws -> Data { - randomBytesLen_Invocations.append(len) - - if let error = randomBytesLen_MockError { - throw error - } - - if let mock = randomBytesLen_MockMethod { - return try await mock(len) - } else if let mock = randomBytesLen_MockValue { - return mock - } else { - fatalError("no mock for `randomBytesLen`") - } - } - // MARK: - removeClientsFromConversation - public var removeClientsFromConversationConversationIdClients_Invocations: [(conversationId: Data, clients: [WireCoreCrypto.ClientId])] = [] + public var removeClientsFromConversationConversationIdClients_Invocations: [(conversationId: Data, clients: [WireCoreCryptoUniffi.ClientId])] = [] public var removeClientsFromConversationConversationIdClients_MockError: Error? - public var removeClientsFromConversationConversationIdClients_MockMethod: ((Data, [WireCoreCrypto.ClientId]) async throws -> WireCoreCrypto.CommitBundle)? - public var removeClientsFromConversationConversationIdClients_MockValue: WireCoreCrypto.CommitBundle? + public var removeClientsFromConversationConversationIdClients_MockMethod: ((Data, [WireCoreCryptoUniffi.ClientId]) async throws -> Void)? - public func removeClientsFromConversation(conversationId: Data, clients: [WireCoreCrypto.ClientId]) async throws -> WireCoreCrypto.CommitBundle { + public func removeClientsFromConversation(conversationId: Data, clients: [WireCoreCryptoUniffi.ClientId]) async throws { removeClientsFromConversationConversationIdClients_Invocations.append((conversationId: conversationId, clients: clients)) if let error = removeClientsFromConversationConversationIdClients_MockError { throw error } - if let mock = removeClientsFromConversationConversationIdClients_MockMethod { - return try await mock(conversationId, clients) - } else if let mock = removeClientsFromConversationConversationIdClients_MockValue { - return mock - } else { + guard let mock = removeClientsFromConversationConversationIdClients_MockMethod else { fatalError("no mock for `removeClientsFromConversationConversationIdClients`") } - } - - // MARK: - reseedRng - - public var reseedRngSeed_Invocations: [Data] = [] - public var reseedRngSeed_MockError: Error? - public var reseedRngSeed_MockMethod: ((Data) async throws -> Void)? - - public func reseedRng(seed: Data) async throws { - reseedRngSeed_Invocations.append(seed) - - if let error = reseedRngSeed_MockError { - throw error - } - - guard let mock = reseedRngSeed_MockMethod else { - fatalError("no mock for `reseedRngSeed`") - } - - try await mock(seed) - } - - // MARK: - restoreFromDisk - - public var restoreFromDisk_Invocations: [Void] = [] - public var restoreFromDisk_MockError: Error? - public var restoreFromDisk_MockMethod: (() async throws -> Void)? - - public func restoreFromDisk() async throws { - restoreFromDisk_Invocations.append(()) - - if let error = restoreFromDisk_MockError { - throw error - } - guard let mock = restoreFromDisk_MockMethod else { - fatalError("no mock for `restoreFromDisk`") - } - - try await mock() + try await mock(conversationId, clients) } - // MARK: - setCallbacks + // MARK: - saveX509Credential - public var setCallbacksCallbacks_Invocations: [any WireCoreCrypto.CoreCryptoCallbacks] = [] - public var setCallbacksCallbacks_MockError: Error? - public var setCallbacksCallbacks_MockMethod: ((any WireCoreCrypto.CoreCryptoCallbacks) async throws -> Void)? + public var saveX509CredentialEnrollmentCertificateChain_Invocations: [(enrollment: WireCoreCryptoUniffi.E2eiEnrollment, certificateChain: String)] = [] + public var saveX509CredentialEnrollmentCertificateChain_MockError: Error? + public var saveX509CredentialEnrollmentCertificateChain_MockMethod: ((WireCoreCryptoUniffi.E2eiEnrollment, String) async throws -> WireCoreCryptoUniffi.NewCrlDistributionPoints)? + public var saveX509CredentialEnrollmentCertificateChain_MockValue: WireCoreCryptoUniffi.NewCrlDistributionPoints? - public func setCallbacks(callbacks: any WireCoreCrypto.CoreCryptoCallbacks) async throws { - setCallbacksCallbacks_Invocations.append(callbacks) + public func saveX509Credential(enrollment: WireCoreCryptoUniffi.E2eiEnrollment, certificateChain: String) async throws -> WireCoreCryptoUniffi.NewCrlDistributionPoints { + saveX509CredentialEnrollmentCertificateChain_Invocations.append((enrollment: enrollment, certificateChain: certificateChain)) - if let error = setCallbacksCallbacks_MockError { + if let error = saveX509CredentialEnrollmentCertificateChain_MockError { throw error } - guard let mock = setCallbacksCallbacks_MockMethod else { - fatalError("no mock for `setCallbacksCallbacks`") - } - - try await mock(callbacks) - } - - // MARK: - transaction - - public var transactionCommand_Invocations: [any WireCoreCrypto.CoreCryptoCommand] = [] - public var transactionCommand_MockError: Error? - public var transactionCommand_MockMethod: ((any WireCoreCrypto.CoreCryptoCommand) async throws -> Void)? - - public func transaction(command: any WireCoreCrypto.CoreCryptoCommand) async throws { - transactionCommand_Invocations.append(command) - - if let error = transactionCommand_MockError { - throw error - } - - guard let mock = transactionCommand_MockMethod else { - fatalError("no mock for `transactionCommand`") + if let mock = saveX509CredentialEnrollmentCertificateChain_MockMethod { + return try await mock(enrollment, certificateChain) + } else if let mock = saveX509CredentialEnrollmentCertificateChain_MockValue { + return mock + } else { + fatalError("no mock for `saveX509CredentialEnrollmentCertificateChain`") } - - try await mock(command) } - // MARK: - unload + // MARK: - setData - public var unload_Invocations: [Void] = [] - public var unload_MockError: Error? - public var unload_MockMethod: (() async throws -> Void)? + public var setDataData_Invocations: [Data] = [] + public var setDataData_MockError: Error? + public var setDataData_MockMethod: ((Data) async throws -> Void)? - public func unload() async throws { - unload_Invocations.append(()) + public func setData(data: Data) async throws { + setDataData_Invocations.append(data) - if let error = unload_MockError { + if let error = setDataData_MockError { throw error } - guard let mock = unload_MockMethod else { - fatalError("no mock for `unload`") + guard let mock = setDataData_MockMethod else { + fatalError("no mock for `setDataData`") } - try await mock() + try await mock(data) } // MARK: - updateKeyingMaterial public var updateKeyingMaterialConversationId_Invocations: [Data] = [] public var updateKeyingMaterialConversationId_MockError: Error? - public var updateKeyingMaterialConversationId_MockMethod: ((Data) async throws -> WireCoreCrypto.CommitBundle)? - public var updateKeyingMaterialConversationId_MockValue: WireCoreCrypto.CommitBundle? + public var updateKeyingMaterialConversationId_MockMethod: ((Data) async throws -> Void)? - public func updateKeyingMaterial(conversationId: Data) async throws -> WireCoreCrypto.CommitBundle { + public func updateKeyingMaterial(conversationId: Data) async throws { updateKeyingMaterialConversationId_Invocations.append(conversationId) if let error = updateKeyingMaterialConversationId_MockError { throw error } - if let mock = updateKeyingMaterialConversationId_MockMethod { - return try await mock(conversationId) - } else if let mock = updateKeyingMaterialConversationId_MockValue { - return mock - } else { + guard let mock = updateKeyingMaterialConversationId_MockMethod else { fatalError("no mock for `updateKeyingMaterialConversationId`") } - } - - // MARK: - wipe - - public var wipe_Invocations: [Void] = [] - public var wipe_MockError: Error? - public var wipe_MockMethod: (() async throws -> Void)? - - public func wipe() async throws { - wipe_Invocations.append(()) - - if let error = wipe_MockError { - throw error - } - - guard let mock = wipe_MockMethod else { - fatalError("no mock for `wipe`") - } - try await mock() + try await mock(conversationId) } // MARK: - wipeConversation @@ -2286,6 +1891,36 @@ public class MockCoreCryptoProviderProtocol: CoreCryptoProviderProtocol { } } + // MARK: - registerMlsTransport + + public var registerMlsTransport_Invocations: [any MlsTransport] = [] + public var registerMlsTransport_MockMethod: ((any MlsTransport) -> Void)? + + public func registerMlsTransport(_ transport: any MlsTransport) { + registerMlsTransport_Invocations.append(transport) + + guard let mock = registerMlsTransport_MockMethod else { + fatalError("no mock for `registerMlsTransport`") + } + + mock(transport) + } + + // MARK: - registerEpochObserver + + public var registerEpochObserver_Invocations: [any WireCoreCryptoUniffi.EpochObserver] = [] + public var registerEpochObserver_MockMethod: ((any WireCoreCryptoUniffi.EpochObserver) async -> Void)? + + public func registerEpochObserver(_ epochObserver: any WireCoreCryptoUniffi.EpochObserver) async { + registerEpochObserver_Invocations.append(epochObserver) + + guard let mock = registerEpochObserver_MockMethod else { + fatalError("no mock for `registerEpochObserver`") + } + + await mock(epochObserver) + } + } class MockCoreDataMessagingMigratorProtocol: CoreDataMessagingMigratorProtocol { @@ -3926,6 +3561,45 @@ public class MockLastEventIDRepositoryInterface: LastEventIDRepositoryInterface } +public class MockLegacyConversationEventProcessorProtocol: LegacyConversationEventProcessorProtocol { + + // MARK: - Life cycle + + public init() {} + + + // MARK: - processConversationEvents + + public var processConversationEvents_Invocations: [[ZMUpdateEvent]] = [] + public var processConversationEvents_MockMethod: (([ZMUpdateEvent]) async -> Void)? + + public func processConversationEvents(_ events: [ZMUpdateEvent]) async { + processConversationEvents_Invocations.append(events) + + guard let mock = processConversationEvents_MockMethod else { + fatalError("no mock for `processConversationEvents`") + } + + await mock(events) + } + + // MARK: - processAndSaveConversationEvents + + public var processAndSaveConversationEvents_Invocations: [[ZMUpdateEvent]] = [] + public var processAndSaveConversationEvents_MockMethod: (([ZMUpdateEvent]) async -> Void)? + + public func processAndSaveConversationEvents(_ events: [ZMUpdateEvent]) async { + processAndSaveConversationEvents_Invocations.append(events) + + guard let mock = processAndSaveConversationEvents_MockMethod else { + fatalError("no mock for `processAndSaveConversationEvents`") + } + + await mock(events) + } + +} + class MockMLSActionsProviderProtocol: MLSActionsProviderProtocol { // MARK: - Life cycle @@ -4223,24 +3897,6 @@ public class MockMLSDecryptionServiceInterface: MLSDecryptionServiceInterface { public init() {} - // MARK: - onEpochChanged - - public var onEpochChanged_Invocations: [Void] = [] - public var onEpochChanged_MockMethod: (() -> AnyPublisher)? - public var onEpochChanged_MockValue: AnyPublisher? - - public func onEpochChanged() -> AnyPublisher { - onEpochChanged_Invocations.append(()) - - if let mock = onEpochChanged_MockMethod { - return mock() - } else if let mock = onEpochChanged_MockValue { - return mock - } else { - fatalError("no mock for `onEpochChanged`") - } - } - // MARK: - onNewCRLsDistributionPoints public var onNewCRLsDistributionPoints_Invocations: [Void] = [] diff --git a/wire-ios-data-model/Support/Sources/MockCoreCryptoProtocol.swift b/wire-ios-data-model/Support/Sources/MockCoreCryptoProtocol.swift new file mode 100644 index 00000000000..46416017112 --- /dev/null +++ b/wire-ios-data-model/Support/Sources/MockCoreCryptoProtocol.swift @@ -0,0 +1,165 @@ +// +// 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 WireCoreCrypto + +public class MockCoreCryptoProtocol: WireCoreCrypto.CoreCryptoProtocol { + + // MARK: - Life cycle + + public init() {} + + // MARK: - transaction + + public typealias transaction_MethodType = + ((_ context: any WireCoreCryptoUniffi.CoreCryptoContextProtocol) async throws -> Result) async throws -> Void + + public var transaction_Invocations: [ + (_ context: any WireCoreCryptoUniffi.CoreCryptoContextProtocol) async throws + -> Any + ] = [] + public var transaction_MockError: Error? + public var transaction_MockMethod: transaction_MethodType? + public var transaction_MockValue: Any? + + public func transaction(_ block: @escaping ( + _ context: any WireCoreCryptoUniffi + .CoreCryptoContextProtocol + ) async throws -> Result) async throws -> Result { + transaction_Invocations.append(block) + + if let error = transaction_MockError { + throw error + } + + if let mock = transaction_MockMethod { + return try await mock(block) as! Result + } else if let mock = transaction_MockValue { + return mock as! Result + } else { + fatalError("no mock for `transaction`") + } + } + + // MARK: - registerEpochObserver + + public var registerEpochObserver_Invocations: [any WireCoreCryptoUniffi.EpochObserver] = [] + public var registerEpochObserver_MockError: Error? + public var registerEpochObserver_MockMethod: ((any WireCoreCryptoUniffi.EpochObserver) async throws -> Void)? + + public func registerEpochObserver(_ epochObserver: any WireCoreCryptoUniffi.EpochObserver) async throws { + registerEpochObserver_Invocations.append(epochObserver) + + if let error = registerEpochObserver_MockError { + throw error + } + + guard let mock = registerEpochObserver_MockMethod else { + fatalError("no mock for `registerEpochObserver`") + } + + try await mock(epochObserver) + } + + // MARK: - provideTransport + + public var provideTransportTransport_Invocations: [any WireCoreCryptoUniffi.MlsTransport] = [] + public var provideTransportTransport_MockError: Error? + public var provideTransportTransport_MockMethod: ((any WireCoreCryptoUniffi.MlsTransport) async throws -> Void)? + + public func provideTransport(transport: any WireCoreCryptoUniffi.MlsTransport) async throws { + provideTransportTransport_Invocations.append(transport) + + if let error = provideTransportTransport_MockError { + throw error + } + + guard let mock = provideTransportTransport_MockMethod else { + fatalError("no mock for `provideTransportTransport`") + } + + try await mock(transport) + } + + // MARK: - setLogger + + public static var setLogger_Invocations: [any WireCoreCryptoUniffi.CoreCryptoLogger] = [] + public static var setLogger_MockMethod: ((any WireCoreCryptoUniffi.CoreCryptoLogger) -> Void)? + + public static func setLogger(_ logger: any WireCoreCryptoUniffi.CoreCryptoLogger) { + setLogger_Invocations.append(logger) + + guard let mock = setLogger_MockMethod else { + fatalError("no mock for `setLogger`") + } + + mock(logger) + } + + // MARK: - setMaxLogLevel + + public static var setMaxLogLevel_Invocations: [WireCoreCryptoUniffi.CoreCryptoLogLevel] = [] + public static var setMaxLogLevel_MockMethod: ((WireCoreCryptoUniffi.CoreCryptoLogLevel) -> Void)? + + public static func setMaxLogLevel(_ level: WireCoreCryptoUniffi.CoreCryptoLogLevel) { + setMaxLogLevel_Invocations.append(level) + + guard let mock = setMaxLogLevel_MockMethod else { + fatalError("no mock for `setMaxLogLevel`") + } + + mock(level) + } + + // MARK: - version + + public static var version_Invocations: [Void] = [] + public static var version_MockMethod: (() -> String)? + public static var version_MockValue: String? + + public static func version() -> String { + version_Invocations.append(()) + + if let mock = version_MockMethod { + return mock() + } else if let mock = version_MockValue { + return mock + } else { + fatalError("no mock for `version`") + } + } + + // MARK: - buildMetadata + + public static var buildMetadata_Invocations: [Void] = [] + public static var buildMetadata_MockMethod: (() -> WireCoreCryptoUniffi.BuildMetadata)? + public static var buildMetadata_MockValue: WireCoreCryptoUniffi.BuildMetadata? + + public static func buildMetadata() -> WireCoreCryptoUniffi.BuildMetadata { + buildMetadata_Invocations.append(()) + + if let mock = buildMetadata_MockMethod { + return mock() + } else if let mock = buildMetadata_MockValue { + return mock + } else { + fatalError("no mock for `buildMetadata`") + } + } + +} diff --git a/wire-ios-data-model/Support/Sources/MockSafeCoreCrypto.swift b/wire-ios-data-model/Support/Sources/MockSafeCoreCrypto.swift index 8868fda9516..64e02287faa 100644 --- a/wire-ios-data-model/Support/Sources/MockSafeCoreCrypto.swift +++ b/wire-ios-data-model/Support/Sources/MockSafeCoreCrypto.swift @@ -23,25 +23,34 @@ import WireDataModel public class MockSafeCoreCrypto: SafeCoreCryptoProtocol { var coreCrypto: MockCoreCryptoProtocol + var coreCryptoContext: MockCoreCryptoContextProtocol - public init(coreCrypto: MockCoreCryptoProtocol = .init()) { + public init( + coreCrypto: MockCoreCryptoProtocol = .init(), + coreCryptoContext: MockCoreCryptoContextProtocol = .init() + ) { self.coreCrypto = coreCrypto + self.coreCryptoContext = coreCryptoContext } var performCount = 0 - func perform(_ block: (CoreCryptoProtocol) throws -> T) async rethrows -> T { + func perform(_ block: (CoreCryptoContextProtocol) throws -> T) async rethrows -> T { performCount += 1 - return try block(coreCrypto) + return try block(coreCryptoContext) } var unsafePerformCount = 0 - public func unsafePerform(_ block: (CoreCryptoProtocol) async throws -> T) async rethrows -> T { + public func unsafePerform(_ block: (CoreCryptoContextProtocol) async throws -> T) async rethrows -> T { unsafePerformCount += 1 - return try await block(coreCrypto) + return try await block(coreCryptoContext) } var performAsyncCount = 0 - public func perform(_ block: (WireCoreCrypto.CoreCryptoProtocol) async throws -> T) async rethrows -> T { + public func perform(_ block: (WireCoreCrypto.CoreCryptoContextProtocol) async throws -> T) async rethrows -> T { + try await block(coreCryptoContext) + } + + public func configure(block: (any WireCoreCrypto.CoreCryptoProtocol) async throws -> Void) async throws { try await block(coreCrypto) } diff --git a/wire-ios-data-model/Tests/MLS/CommitSenderTests.swift b/wire-ios-data-model/Tests/MLS/CommitSenderTests.swift deleted file mode 100644 index 0be16899073..00000000000 --- a/wire-ios-data-model/Tests/MLS/CommitSenderTests.swift +++ /dev/null @@ -1,289 +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 Combine -import Foundation -import WireCoreCrypto -import XCTest -@testable import WireDataModel -@testable import WireDataModelSupport - -class CommitSenderTests: ZMBaseManagedObjectTest { - - // MARK: - Properties - - private var sut: CommitSender! - private var mockActionsProvider: MockMLSActionsProviderProtocol! - private var mockCoreCrypto: MockCoreCryptoProtocol! - private var mockCoreCryptoProvider: MockCoreCryptoProviderProtocol! - private var mockClearPendingCommitInvocations: [Data]! - private var mockClearPendingGroupInvocations: [Data]! - private var cancellables: Set! - - private lazy var groupID: MLSGroupID = .random() - private lazy var commitBundle = CommitBundle( - welcome: nil, - commit: .random(), - groupInfo: .init( - encryptionType: .plaintext, - ratchetTreeType: .full, - payload: .random() - ) - ) - - // MARK: - Life cycle - - override func setUp() { - super.setUp() - - mockCoreCrypto = MockCoreCryptoProtocol() - mockActionsProvider = MockMLSActionsProviderProtocol() - mockCoreCryptoProvider = MockCoreCryptoProviderProtocol() - mockCoreCryptoProvider.coreCrypto_MockValue = MockSafeCoreCrypto(coreCrypto: mockCoreCrypto) - cancellables = .init() - - sut = CommitSender( - coreCryptoProvider: mockCoreCryptoProvider, - notificationContext: syncMOC.notificationContext, - actionsProvider: mockActionsProvider - ) - - mockClearPendingCommitInvocations = [] - mockCoreCrypto.clearPendingCommitConversationId_MockMethod = { [self] groupID in - mockClearPendingCommitInvocations.append(groupID) - } - - mockClearPendingGroupInvocations = [] - mockCoreCrypto.clearPendingGroupFromExternalCommitConversationId_MockMethod = { [self] groupID in - mockClearPendingGroupInvocations.append(groupID) - } - } - - override func tearDown() { - mockCoreCrypto = nil - mockCoreCryptoProvider = nil - mockActionsProvider = nil - sut = nil - cancellables = nil - super.tearDown() - } - - // MARK: - Send Commit - - func test_SendCommitBundle_Success() async throws { - // Given - let event = ZMUpdateEvent() - - // Mock action provider - mockActionsProvider.sendCommitBundleIn_MockMethod = { _, _ in - [event] - } - - // Mock core crypto - var commitAcceptedInvocations = [Data]() - mockCoreCrypto.commitAcceptedConversationId_MockMethod = { groupID in - commitAcceptedInvocations.append(groupID) - return nil - } - - // When - let receivedEvents = try await sut.sendCommitBundle(commitBundle, for: groupID) - - // Then - // Send commit bundle was called on the action provider - XCTAssertEqual(mockActionsProvider.sendCommitBundleIn_Invocations.count, 1) - XCTAssertEqual(mockActionsProvider.sendCommitBundleIn_Invocations.first?.bundle, commitBundle.transportData()) - - // Commit accepted was called - XCTAssertEqual(commitAcceptedInvocations.count, 1) - XCTAssertEqual(commitAcceptedInvocations.first, groupID.data) - - // Events were received - XCTAssertEqual(receivedEvents, [event]) - } - - func test_SendCommitBundleMlsClientMismatch_ThrowsWithRecoveryStrategy_RetryAfterQuickSync() async { - await assertSendCommitBundleThrows( - withRecovery: .retryAfterQuickSync, - for: .mlsClientMismatch, - shouldClearPendingCommit: true - ) - } - - func test_SendCommitBundleMlsCommitMissingReferences_ThrowsWithRecoveryStrategy_RetryAfterQuickSync() async { - await assertSendCommitBundleThrows( - withRecovery: .retryAfterQuickSync, - for: .mlsCommitMissingReferences(message: ""), - shouldClearPendingCommit: true - ) - } - - func test_SendCommitBundleMlsStaleMessage_ThrowsWithRecoveryStrategy_RetryAfterRepairingGroup() async { - await assertSendCommitBundleThrows( - withRecovery: .retryAfterRepairingGroup, - for: .mlsStaleMessage, - shouldClearPendingCommit: true - ) - } - - func test_SendCommitBundleUnknownError_ThrowsWithRecoveryStrategy_GiveUp() async { - await assertSendCommitBundleThrows( - withRecovery: .giveUp, - for: .unknown(status: 400, label: "", message: ""), - shouldClearPendingCommit: true - ) - } - - private func assertSendCommitBundleThrows( - withRecovery recovery: CommitError.RecoveryStrategy, - for error: SendCommitBundleAction.Failure, - shouldClearPendingCommit: Bool, - file: StaticString = #filePath, - line: UInt = #line - ) async { - // Given - // Mock action provider throwing an error - mockActionsProvider.sendCommitBundleIn_MockMethod = { _, _ in - throw error - } - - // Then - await assertItThrows(error: CommitError.failedToSendCommit(recovery: recovery, cause: error)) { - // When - _ = try await sut.sendCommitBundle(commitBundle, for: groupID) - } - - if shouldClearPendingCommit { - // It clears pending commit - XCTAssertEqual(mockClearPendingCommitInvocations.count, 1) - XCTAssertEqual(mockClearPendingCommitInvocations.first, groupID.data) - } else { - // It doesn't clear pending commit - XCTAssertEqual(mockClearPendingCommitInvocations.count, 0) - } - } - - // MARK: - Send External Commit - - func test_SendExternalCommitBundle_Success() async throws { - // Given - let event = ZMUpdateEvent() - - // Mock action provider - mockActionsProvider.sendCommitBundleIn_MockMethod = { _, _ in - [event] - } - - // Mock core crypto - var mergePendingGroupInvocations = [Data]() - mockCoreCrypto.mergePendingGroupFromExternalCommitConversationId_MockMethod = { groupID in - mergePendingGroupInvocations.append(groupID) - return nil - } - - // When - let receivedEvents = try await sut.sendExternalCommitBundle(commitBundle, for: groupID) - - // Then - // Send commit bundle was called on the action provider - XCTAssertEqual(mockActionsProvider.sendCommitBundleIn_Invocations.count, 1) - XCTAssertEqual(mockActionsProvider.sendCommitBundleIn_Invocations.first?.bundle, commitBundle.transportData()) - - // Commit accepted was called - XCTAssertEqual(mergePendingGroupInvocations.count, 1) - XCTAssertEqual(mergePendingGroupInvocations.first, groupID.data) - - // Events were received - XCTAssertEqual(receivedEvents, [event]) - } - - func test_SendExternalCommitBundle_ThrowsWithRecoveryStrategy_Retry() async { - await assertSendExternalCommitBundleThrows( - withRecovery: .retry, - for: .mlsStaleMessage, - shouldClearPendingGroup: false - ) - } - - func test_SendExternalCommitBundle_ThrowsWithRecoveryStrategy_GiveUp() async { - await assertSendExternalCommitBundleThrows( - withRecovery: .giveUp, - for: .unknown(status: 400, label: "", message: ""), - shouldClearPendingGroup: true - ) - } - - private func assertSendExternalCommitBundleThrows( - withRecovery recovery: ExternalCommitError.RecoveryStrategy, - for error: SendCommitBundleAction.Failure, - shouldClearPendingGroup: Bool, - file: StaticString = #filePath, - line: UInt = #line - ) async { - // Given - // Mock action provider throwing an error - mockActionsProvider.sendCommitBundleIn_MockMethod = { _, _ in - throw error - } - - // Then - await assertItThrows(error: ExternalCommitError.failedToSendCommit(recovery: recovery, cause: error)) { - // When - _ = try await sut.sendExternalCommitBundle(commitBundle, for: groupID) - } - - if shouldClearPendingGroup { - // It clears pending commit - XCTAssertEqual(mockClearPendingGroupInvocations.count, 1, file: file, line: line) - XCTAssertEqual(mockClearPendingGroupInvocations.first, groupID.data, file: file, line: line) - } else { - // It doesn't clear pending commit - XCTAssertEqual(mockClearPendingGroupInvocations.count, 0, file: file, line: line) - } - } - - // MARK: - Epoch change - - func test_OnEpochChanged() async throws { - // Given - - // Mock action provider - mockActionsProvider.sendCommitBundleIn_MockMethod = { _, _ in - [] - } - - // Mock commit accepted - mockCoreCrypto.commitAcceptedConversationId_MockMethod = { _ in nil } - - // Set up expectation - let expectation = XCTestExpectation(description: "observed epoch change") - var receivedGroupIDs = [MLSGroupID]() - - sut.onEpochChanged().collect(1).sink { - receivedGroupIDs = $0 - expectation.fulfill() - }.store(in: &cancellables) - - // When - _ = try await sut.sendCommitBundle(commitBundle, for: groupID) - - // Then - await fulfillment(of: [expectation]) - XCTAssertEqual(receivedGroupIDs, [groupID]) - } -} diff --git a/wire-ios-data-model/Tests/MLS/CoreCryptoCallbacksTests.swift b/wire-ios-data-model/Tests/MLS/CoreCryptoCallbacksTests.swift deleted file mode 100644 index f8befb68ea7..00000000000 --- a/wire-ios-data-model/Tests/MLS/CoreCryptoCallbacksTests.swift +++ /dev/null @@ -1,54 +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 -@testable import WireDataModel - -class CoreCryptoCallbacksTests: XCTestCase { - - func test_HardcodedValues() async { - // Given - let sut = CoreCryptoCallbacksImpl() - - // When - let authorizeResult = sut.authorize( - conversationId: .random(), - clientId: .random() - ) - - let userAuthorizeResult = sut.userAuthorize( - conversationId: .random(), - externalClientId: .random(), - existingClients: [.random()] - ) - - let clientIsExistingGroupUserResult = sut.clientIsExistingGroupUser( - conversationId: .random(), - clientId: .random(), - existingClients: [.random()], - parentConversationClients: [.random()] - ) - - // Then - XCTAssertTrue(authorizeResult) - XCTAssertTrue(userAuthorizeResult) - XCTAssertTrue(clientIsExistingGroupUserResult) - } - -} diff --git a/wire-ios-data-model/Tests/MLS/FakeCoreCryptoContext.swift b/wire-ios-data-model/Tests/MLS/FakeCoreCryptoContext.swift deleted file mode 100644 index f0efd6e8bc8..00000000000 --- a/wire-ios-data-model/Tests/MLS/FakeCoreCryptoContext.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 WireCoreCrypto - -// Stub to replace corecryptoContext. This is temporary to test transactional API, a more robust solution should be -// provided -final class FakeCoreCryptoContext: WireCoreCrypto.CoreCryptoContext { - - var decryptMessageConversationIdPayload_Invocations: [(conversationId: Data, payload: Data)] = [] - - var decryptMessage: (WireCoreCrypto.DecryptedMessage?, Error?)? - - override func decryptMessage(conversationId: Data, payload: Data) async throws -> WireCoreCrypto.DecryptedMessage { - decryptMessageConversationIdPayload_Invocations.append((conversationId, payload)) - - if let message = decryptMessage?.0 { - return message - } - if let error = decryptMessage?.1 { - throw error - } - fatalError("missing mock for decryptMessage(conversationId:payload:") - } -} diff --git a/wire-ios-data-model/Tests/MLS/MLSActionExecutorTests.swift b/wire-ios-data-model/Tests/MLS/MLSActionExecutorTests.swift index 1854f7225af..e44b4011f42 100644 --- a/wire-ios-data-model/Tests/MLS/MLSActionExecutorTests.swift +++ b/wire-ios-data-model/Tests/MLS/MLSActionExecutorTests.swift @@ -27,36 +27,32 @@ import XCTest class MLSActionExecutorTests: ZMBaseManagedObjectTest { - var mockCoreCrypto: MockCoreCryptoProtocol! + var mockCoreCryptoContext: MockCoreCryptoContextProtocol! var mockSafeCoreCrypto: MockSafeCoreCrypto! var mockCoreCryptoProvider: MockCoreCryptoProviderProtocol! - var mockCommitSender: MockCommitSending! var mockFeatureRepository: MockFeatureRepositoryInterface! var sut: MLSActionExecutor! var cancellable: AnyCancellable! override func setUp() { super.setUp() - mockCoreCrypto = MockCoreCryptoProtocol() - mockCoreCrypto.e2eiIsEnabledCiphersuite_MockValue = false - mockSafeCoreCrypto = MockSafeCoreCrypto(coreCrypto: mockCoreCrypto) + mockCoreCryptoContext = MockCoreCryptoContextProtocol() + mockCoreCryptoContext.e2eiIsEnabledCiphersuite_MockValue = false + mockSafeCoreCrypto = MockSafeCoreCrypto(coreCryptoContext: mockCoreCryptoContext) mockCoreCryptoProvider = MockCoreCryptoProviderProtocol() mockCoreCryptoProvider.coreCrypto_MockValue = mockSafeCoreCrypto - mockCommitSender = MockCommitSending() mockFeatureRepository = MockFeatureRepositoryInterface() sut = MLSActionExecutor( coreCryptoProvider: mockCoreCryptoProvider, - commitSender: mockCommitSender, featureRepository: mockFeatureRepository ) } override func tearDown() { - mockCoreCrypto = nil + mockCoreCryptoContext = nil mockSafeCoreCrypto = nil mockCoreCryptoProvider = nil - mockCommitSender = nil cancellable = nil sut = nil super.tearDown() @@ -86,20 +82,8 @@ class MLSActionExecutorTests: ZMBaseManagedObjectTest { func test_TwoOperationsOnSameGroupAreExecutedSerially() async throws { // Given let groupID = MLSGroupID.random() - let mockCommit = Data.random() - let mockGroupInfo = GroupInfoBundle( - encryptionType: .plaintext, - ratchetTreeType: .full, - payload: .random() - ) - let mockCommitBundle = CommitBundle( - welcome: nil, - commit: mockCommit, - groupInfo: mockGroupInfo - ) - - let sendCommitExpectation = XCTestExpectation(description: "send commit") - var sendCommitContinuation: CheckedContinuation? + let updateKeyMaterialExpectation = XCTestExpectation(description: "update key material") + var updateKeyMaterialContinuation: CheckedContinuation? let beforeDecryptMessageExpectation = XCTestExpectation(description: "Task to decrypt message has been started/is running") @@ -109,18 +93,12 @@ class MLSActionExecutorTests: ZMBaseManagedObjectTest { // Mock Update key material. var mockUpdateKeyMaterialArguments = [Data]() - mockCoreCrypto.updateKeyingMaterialConversationId_MockMethod = { + mockCoreCryptoContext.updateKeyingMaterialConversationId_MockMethod = { mockUpdateKeyMaterialArguments.append($0) - return mockCommitBundle - } - - // Mock send commit bundle. - mockCommitSender.sendCommitBundleFor_MockMethod = { _, _ in await withCheckedContinuation { continuation in - sendCommitContinuation = continuation - sendCommitExpectation.fulfill() + updateKeyMaterialContinuation = continuation + updateKeyMaterialExpectation.fulfill() } - return [] } // Mock decrypt message @@ -136,14 +114,7 @@ class MLSActionExecutorTests: ZMBaseManagedObjectTest { crlNewDistributionPoints: nil ) - let fakeCoreCryptoContext = FakeCoreCryptoContext(noPointer: .init()) - mockCoreCrypto.transactionCommand_MockMethod = { command in - - fakeCoreCryptoContext.decryptMessage = (decryptedMessage, nil) - - try await command.execute(context: fakeCoreCryptoContext) - insideDecryptMessageInvertedExpectation.fulfill() - } + mockCoreCryptoContext.decryptMessageConversationIdPayload_MockValue = decryptedMessage // When Task { @@ -155,7 +126,7 @@ class MLSActionExecutorTests: ZMBaseManagedObjectTest { } // the decrypt message operation should wait for update key material to finish - await fulfillment(of: [sendCommitExpectation]) + await fulfillment(of: [updateKeyMaterialExpectation]) // the `updateKeyMaterial` is blocked/suspended, now ensure that no other call using the same groupID can enter Task { @@ -169,12 +140,12 @@ class MLSActionExecutorTests: ZMBaseManagedObjectTest { } // ensure the task is executing, but we haven't entered `performNonReentrant` await fulfillment(of: [beforeDecryptMessageExpectation, insideDecryptMessageInvertedExpectation], timeout: 0.3) - XCTAssertEqual(mockCoreCrypto.decryptMessageConversationIdPayload_Invocations.count, 0) + XCTAssertEqual(mockCoreCryptoContext.decryptMessageConversationIdPayload_Invocations.count, 0) // allow update key material to finish - sendCommitContinuation?.resume() + updateKeyMaterialContinuation?.resume() await fulfillment(of: [afterDecryptMessageExpectation], timeout: 0.5) - XCTAssertEqual(fakeCoreCryptoContext.decryptMessageConversationIdPayload_Invocations.count, 1) + XCTAssertEqual(mockCoreCryptoContext.decryptMessageConversationIdPayload_Invocations.count, 1) } // maybe it makes sense to test performNonReentrant directly instead @@ -182,36 +153,19 @@ class MLSActionExecutorTests: ZMBaseManagedObjectTest { // Given let groupID1 = MLSGroupID.random() let groupID2 = MLSGroupID.random() - let mockCommit = Data.random() - let mockGroupInfo = GroupInfoBundle( - encryptionType: .plaintext, - ratchetTreeType: .full, - payload: .random() - ) - let mockCommitBundle = CommitBundle( - welcome: nil, - commit: mockCommit, - groupInfo: mockGroupInfo - ) - let sendCommitExpectation = XCTestExpectation(description: "send commit") let decryptMessageExpectation = XCTestExpectation(description: "decrypted message") - var sendCommitContinuation: CheckedContinuation? + let updateKeyMaterialExpectation = XCTestExpectation(description: "send commit") + var updateKeyMaterialContinuation: CheckedContinuation? // Mock Update key material. var mockUpdateKeyMaterialArguments = [Data]() - mockCoreCrypto.updateKeyingMaterialConversationId_MockMethod = { + mockCoreCryptoContext.updateKeyingMaterialConversationId_MockMethod = { mockUpdateKeyMaterialArguments.append($0) - return mockCommitBundle - } - - // Mock send commit bundle. - mockCommitSender.sendCommitBundleFor_MockMethod = { _, _ in await withCheckedContinuation { continuation in - sendCommitContinuation = continuation - sendCommitExpectation.fulfill() + updateKeyMaterialContinuation = continuation + updateKeyMaterialExpectation.fulfill() } - return [] } // Mock decrypt message @@ -226,13 +180,9 @@ class MLSActionExecutorTests: ZMBaseManagedObjectTest { bufferedMessages: nil, crlNewDistributionPoints: nil ) - let fakeCoreCryptoContext = FakeCoreCryptoContext(noPointer: .init()) - mockCoreCrypto.transactionCommand_MockMethod = { command in - - fakeCoreCryptoContext.decryptMessage = (decryptedMessage, nil) - - try await command.execute(context: fakeCoreCryptoContext) + mockCoreCryptoContext.decryptMessageConversationIdPayload_MockMethod = { _, _ in decryptMessageExpectation.fulfill() + return decryptedMessage } // When @@ -245,7 +195,7 @@ class MLSActionExecutorTests: ZMBaseManagedObjectTest { } // ensure we entered via sendCommit, block further execution - await fulfillment(of: [sendCommitExpectation], timeout: .tenSeconds) + await fulfillment(of: [updateKeyMaterialExpectation], timeout: .tenSeconds) Task { do { @@ -258,8 +208,8 @@ class MLSActionExecutorTests: ZMBaseManagedObjectTest { // the update key material operation shouldn't block the decrypt message await fulfillment(of: [decryptMessageExpectation], timeout: .tenSeconds) - XCTAssertEqual(fakeCoreCryptoContext.decryptMessageConversationIdPayload_Invocations.count, 1) - sendCommitContinuation?.resume() + XCTAssertEqual(mockCoreCryptoContext.decryptMessageConversationIdPayload_Invocations.count, 1) + updateKeyMaterialContinuation?.resume() } // MARK: - Process welcome message @@ -271,7 +221,7 @@ class MLSActionExecutorTests: ZMBaseManagedObjectTest { let welcomeBundle = WelcomeBundle(id: groupID.data, crlNewDistributionPoints: nil) // Mock - mockCoreCrypto.processWelcomeMessageWelcomeMessageCustomConfiguration_MockMethod = { _, _ in + mockCoreCryptoContext.processWelcomeMessageWelcomeMessageCustomConfiguration_MockMethod = { _, _ in welcomeBundle } @@ -280,7 +230,10 @@ class MLSActionExecutorTests: ZMBaseManagedObjectTest { // Then XCTAssertEqual(groupID, result) - XCTAssertEqual(mockCoreCrypto.processWelcomeMessageWelcomeMessageCustomConfiguration_Invocations.count, 1) + XCTAssertEqual( + mockCoreCryptoContext.processWelcomeMessageWelcomeMessageCustomConfiguration_Invocations.count, + 1 + ) } func test_processWelcomeMessage_PublishesNewDistributionPoints() async throws { @@ -291,7 +244,7 @@ class MLSActionExecutorTests: ZMBaseManagedObjectTest { let welcomeBundle = WelcomeBundle(id: groupID.data, crlNewDistributionPoints: [distributionPoint]) // Mock - mockCoreCrypto.processWelcomeMessageWelcomeMessageCustomConfiguration_MockMethod = { _, _ in + mockCoreCryptoContext.processWelcomeMessageWelcomeMessageCustomConfiguration_MockMethod = { _, _ in welcomeBundle } @@ -330,27 +283,16 @@ class MLSActionExecutorTests: ZMBaseManagedObjectTest { ratchetTreeType: .full, payload: .random() ) - let mockMemberAddedMessages = MemberAddedMessages( - welcome: mockWelcome, - commit: mockCommit, - groupInfo: mockGroupInfo, - crlNewDistributionPoints: nil - ) // Mock add clients. var mockAddClientsArguments = [(Data, [Data])]() - mockCoreCrypto.addClientsToConversationConversationIdKeyPackages_MockMethod = { + mockCoreCryptoContext.addClientsToConversationConversationIdKeyPackages_MockMethod = { mockAddClientsArguments.append(($0, $1)) - return mockMemberAddedMessages - } - - // Mock send commit bundle. - mockCommitSender.sendCommitBundleFor_MockMethod = { _, _ in - [mockUpdateEvent] + return [] } // When - let updateEvents = try await sut.addMembers(keyPackages, to: groupID) + try await sut.addMembers(keyPackages, to: groupID) // Then core crypto added the members. XCTAssertEqual(mockAddClientsArguments.count, 1) @@ -363,12 +305,6 @@ class MLSActionExecutorTests: ZMBaseManagedObjectTest { commit: mockCommit, groupInfo: mockGroupInfo ) - - XCTAssertEqual(mockCommitSender.sendCommitBundleFor_Invocations.count, 1) - XCTAssertEqual(mockCommitSender.sendCommitBundleFor_Invocations.first?.bundle, expectedCommitBundle) - - // Then the update event was returned. - XCTAssertEqual(updateEvents, [mockUpdateEvent]) } func test_AddMembers_PublishesNewDistributionPoints() async throws { @@ -376,22 +312,8 @@ class MLSActionExecutorTests: ZMBaseManagedObjectTest { let distributionPoint = "example.domain.com/dp" // Mock adding clients returns new distribution point - mockCoreCrypto.addClientsToConversationConversationIdKeyPackages_MockMethod = { _, _ in - .init( - welcome: .random(), - commit: .random(), - groupInfo: .init( - encryptionType: .plaintext, - ratchetTreeType: .full, - payload: .random() - ), - crlNewDistributionPoints: [distributionPoint] - ) - } - - // Mock commit sending - mockCommitSender.sendCommitBundleFor_MockMethod = { _, _ in - [] + mockCoreCryptoContext.addClientsToConversationConversationIdKeyPackages_MockMethod = { _, _ in + [distributionPoint] } // Set up expectation to receive the new distribution points @@ -421,45 +343,19 @@ class MLSActionExecutorTests: ZMBaseManagedObjectTest { let clientIds = [mlsClientID].compactMap(\.rawValue.utf8Data) - let mockCommit = Data.random() - let mockUpdateEvent = mockMemberLeaveUpdateEvent() - let mockGroupInfo = GroupInfoBundle( - encryptionType: .plaintext, - ratchetTreeType: .full, - payload: .random() - ) - let mockCommitBundle = CommitBundle( - welcome: nil, - commit: mockCommit, - groupInfo: mockGroupInfo - ) - // Mock remove clients. var mockRemoveClientsArguments = [(Data, [ClientId])]() - mockCoreCrypto.removeClientsFromConversationConversationIdClients_MockMethod = { + mockCoreCryptoContext.removeClientsFromConversationConversationIdClients_MockMethod = { mockRemoveClientsArguments.append(($0, $1)) - return mockCommitBundle - } - - // Mock send commit bundle. - mockCommitSender.sendCommitBundleFor_MockMethod = { _, _ in - [mockUpdateEvent] } // When - let updateEvents = try await sut.removeClients(clientIds, from: groupID) + try await sut.removeClients(clientIds, from: groupID) // Then core crypto removes the members. XCTAssertEqual(mockRemoveClientsArguments.count, 1) XCTAssertEqual(mockRemoveClientsArguments.first?.0, groupID.data) XCTAssertEqual(mockRemoveClientsArguments.first?.1, clientIds) - - // Then the commit bundle was sent. - XCTAssertEqual(mockCommitSender.sendCommitBundleFor_Invocations.count, 1) - XCTAssertEqual(mockCommitSender.sendCommitBundleFor_Invocations.first?.bundle, mockCommitBundle) - - // Then the update event was returned. - XCTAssertEqual(updateEvents, [mockUpdateEvent]) } // MARK: - Update key material @@ -467,43 +363,19 @@ class MLSActionExecutorTests: ZMBaseManagedObjectTest { func test_UpdateKeyMaterial() async throws { // Given let groupID = MLSGroupID.random() - let mockCommit = Data.random() - let mockGroupInfo = GroupInfoBundle( - encryptionType: .plaintext, - ratchetTreeType: .full, - payload: .random() - ) - let mockCommitBundle = CommitBundle( - welcome: nil, - commit: mockCommit, - groupInfo: mockGroupInfo - ) // Mock Update key material. var mockUpdateKeyMaterialArguments = [Data]() - mockCoreCrypto.updateKeyingMaterialConversationId_MockMethod = { + mockCoreCryptoContext.updateKeyingMaterialConversationId_MockMethod = { mockUpdateKeyMaterialArguments.append($0) - return mockCommitBundle - } - - // Mock send commit bundle. - mockCommitSender.sendCommitBundleFor_MockMethod = { _, _ in - [] } // When - let updateEvents = try await sut.updateKeyMaterial(for: groupID) + try await sut.updateKeyMaterial(for: groupID) // Then core crypto update key materials. XCTAssertEqual(mockUpdateKeyMaterialArguments.count, 1) XCTAssertEqual(mockUpdateKeyMaterialArguments.first, groupID.data) - - // Then the commit bundle was sent. - XCTAssertEqual(mockCommitSender.sendCommitBundleFor_Invocations.count, 1) - XCTAssertEqual(mockCommitSender.sendCommitBundleFor_Invocations.first?.bundle, mockCommitBundle) - - // Then no update events were returned. - XCTAssertEqual(updateEvents, [ZMUpdateEvent]()) } // MARK: - Commit pending proposals @@ -512,49 +384,18 @@ class MLSActionExecutorTests: ZMBaseManagedObjectTest { // Given let groupID = MLSGroupID.random() - let mockCommit = Data.random() - let mockWelcome = Data.random() - let mockUpdateEvent = mockMemberLeaveUpdateEvent() - let mockGroupInfo = GroupInfoBundle( - encryptionType: .plaintext, - ratchetTreeType: .full, - payload: .random() - ) - let mockCommitBundle = CommitBundle( - welcome: mockWelcome, - commit: mockCommit, - groupInfo: mockGroupInfo - ) - // Mock Commit pending proposals. var mockCommitPendingProposals = [Data]() - mockCoreCrypto.commitPendingProposalsConversationId_MockMethod = { + mockCoreCryptoContext.commitPendingProposalsConversationId_MockMethod = { mockCommitPendingProposals.append($0) - return CommitBundle( - welcome: mockWelcome, - commit: mockCommit, - groupInfo: mockGroupInfo - ) - } - - // Mock send commit bundle. - mockCommitSender.sendCommitBundleFor_MockMethod = { _, _ in - [mockUpdateEvent] } // When - let updateEvents = try await sut.commitPendingProposals(in: groupID) + try await sut.commitPendingProposals(in: groupID) // Then core crypto commit pending proposals. XCTAssertEqual(mockCommitPendingProposals.count, 1) XCTAssertEqual(mockCommitPendingProposals.first, groupID.data) - - // Then the commit bundle was sent. - XCTAssertEqual(mockCommitSender.sendCommitBundleFor_Invocations.count, 1) - XCTAssertEqual(mockCommitSender.sendCommitBundleFor_Invocations.first?.bundle, mockCommitBundle) - - // Then the update event was returned. - XCTAssertEqual(updateEvents, [mockUpdateEvent]) } // MARK: - Join Group @@ -569,14 +410,6 @@ class MLSActionExecutorTests: ZMBaseManagedObjectTest { ratchetTreeType: .full, payload: Data() ) - let mockCommitBundle = CommitBundle( - welcome: nil, - commit: mockCommit, - groupInfo: mockGroupInfoBundle - ) - // swiftlint:disable:next todo_requires_jira_link - // TODO: Mock properly - let mockUpdateEvents = [ZMUpdateEvent]() // Mock join by external commit var mockJoinByExternalCommitArguments = [Data]() @@ -587,34 +420,18 @@ class MLSActionExecutorTests: ZMBaseManagedObjectTest { config: .init(defaultCipherSuite: .MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519) ) - mockCoreCrypto.joinByExternalCommitGroupInfoCustomConfigurationCredentialType_MockMethod = { groupState, _, _ in - mockJoinByExternalCommitArguments.append(groupState) - return .init( - conversationId: groupID.data, - commit: mockCommit, - groupInfo: mockGroupInfoBundle, - crlNewDistributionPoints: nil - ) - } - - // Mock send commit bundle - mockCommitSender.sendExternalCommitBundleFor_MockMethod = { _, _ in - mockUpdateEvents - } + mockCoreCryptoContext + .joinByExternalCommitGroupInfoCustomConfigurationCredentialType_MockMethod = { groupState, _, _ in + mockJoinByExternalCommitArguments.append(groupState) + return .init(id: .random(), crlNewDistributionPoints: []) + } // When - let updateEvents = try await sut.joinGroup(groupID, groupInfo: mockGroupInfo) + try await sut.joinGroup(groupID, groupInfo: mockGroupInfo) // Then core crypto creates conversation init bundle XCTAssertEqual(mockJoinByExternalCommitArguments.count, 1) XCTAssertEqual(mockJoinByExternalCommitArguments.first, mockGroupInfo) - - // Then commit bundle was sent - XCTAssertEqual(mockCommitSender.sendExternalCommitBundleFor_Invocations.count, 1) - XCTAssertEqual(mockCommitSender.sendExternalCommitBundleFor_Invocations.first?.bundle, mockCommitBundle) - - // Then the update event was returned - XCTAssertEqual(updateEvents, mockUpdateEvents) } func test_JoinGroup_PublishesNewDistributionPoints() async throws { @@ -622,25 +439,13 @@ class MLSActionExecutorTests: ZMBaseManagedObjectTest { let distributionPoint = "example.domain.com/dp" // Mock joining by external commit - mockCoreCrypto.joinByExternalCommitGroupInfoCustomConfigurationCredentialType_MockMethod = { _, _, _ in - + mockCoreCryptoContext.joinByExternalCommitGroupInfoCustomConfigurationCredentialType_MockMethod = { _, _, _ in .init( - conversationId: .random(), - commit: .random(), - groupInfo: .init( - encryptionType: .plaintext, - ratchetTreeType: .full, - payload: .random() - ), + id: .random(), crlNewDistributionPoints: [distributionPoint] ) } - // Mock external commit sending - mockCommitSender.sendExternalCommitBundleFor_MockMethod = { _, _ in - [] - } - // Mock MLS feature config mockFeatureRepository.fetchMLS_MockValue = Feature.MLS( status: .enabled, @@ -664,14 +469,14 @@ class MLSActionExecutorTests: ZMBaseManagedObjectTest { // MARK: - Decrypt Message func test_decryptMessage_throwsBufferedDecryptedMessage_withCC_BufferedFutureMessageError() async throws { - try await internalTest_decryptMessage_throwsError(CoreCryptoError.Mls(.BufferedFutureMessage)) + try await internalTest_decryptMessage_throwsError( + CoreCryptoError.Mls(.BufferedFutureMessage) + ) } - func test_decryptMessage_throwsBufferedDecryptedMessage_withCC_OtherError() async throws { + func test_decryptMessage_throwsBufferedDecryptedMessage_withBufferedCommit() async throws { try await internalTest_decryptMessage_throwsError( - CoreCryptoError.Mls(.Other( - "Incoming message is a commit for which we have not yet received all the proposals. Buffering until all proposals have arrived." - )) + CoreCryptoError.Mls(.BufferedCommit) ) } @@ -681,13 +486,7 @@ class MLSActionExecutorTests: ZMBaseManagedObjectTest { let groupID = MLSGroupID.random() let encryptedMessage = Data.random(byteCount: 1) - let fakeCoreCryptoContext = FakeCoreCryptoContext(noPointer: .init()) - mockCoreCrypto.transactionCommand_MockMethod = { command in - - fakeCoreCryptoContext.decryptMessage = (nil, error) - - try await command.execute(context: fakeCoreCryptoContext) - } + mockCoreCryptoContext.decryptMessageConversationIdPayload_MockError = error // When await assertItThrows(error: MLSActionExecutor.Failure.bufferedDecryptedMessage) { @@ -695,7 +494,7 @@ class MLSActionExecutorTests: ZMBaseManagedObjectTest { } // Then - XCTAssertEqual(fakeCoreCryptoContext.decryptMessageConversationIdPayload_Invocations.count, 1) + XCTAssertEqual(mockCoreCryptoContext.decryptMessageConversationIdPayload_Invocations.count, 1) } func test_decryptMessage_successfully() async throws { @@ -715,19 +514,13 @@ class MLSActionExecutorTests: ZMBaseManagedObjectTest { crlNewDistributionPoints: nil ) - let fakeCoreCryptoContext = FakeCoreCryptoContext(noPointer: .init()) - mockCoreCrypto.transactionCommand_MockMethod = { command in - - fakeCoreCryptoContext.decryptMessage = (decryptedMessage, nil) - - try await command.execute(context: fakeCoreCryptoContext) - } + mockCoreCryptoContext.decryptMessageConversationIdPayload_MockValue = decryptedMessage // When let result = try await sut.decryptMessage(encryptedMessage, in: groupID) // Then XCTAssertEqual(result, decryptedMessage) - XCTAssertEqual(fakeCoreCryptoContext.decryptMessageConversationIdPayload_Invocations.count, 1) + XCTAssertEqual(mockCoreCryptoContext.decryptMessageConversationIdPayload_Invocations.count, 1) } } diff --git a/wire-ios-data-model/Tests/MLS/MLSDecryptionServiceTests.swift b/wire-ios-data-model/Tests/MLS/MLSDecryptionServiceTests.swift index 4c0ee45bf56..53540dbc4b0 100644 --- a/wire-ios-data-model/Tests/MLS/MLSDecryptionServiceTests.swift +++ b/wire-ios-data-model/Tests/MLS/MLSDecryptionServiceTests.swift @@ -76,13 +76,14 @@ final class MLSDecryptionServiceTests: ZMConversationTestsBase { // Given let groupID = MLSGroupID.random() let message = Data.random().base64EncodedString() + let error = CoreCryptoError.Other("conversation not found") mockMLSActionExecutor.mockDecryptMessage = { _, _ in - throw CryptoError.ConversationNotFound(message: "conversation not found") + throw error } // Then - await assertItThrows(error: DecryptionError.failedToDecryptMessage) { + await assertItThrows(error: DecryptionError.failedToDecryptMessage(reason: error)) { // When try _ = await sut.decrypt( message: message, @@ -319,51 +320,6 @@ final class MLSDecryptionServiceTests: ZMConversationTestsBase { XCTAssertEqual(results.first, MLSDecryptResult.message(messageData, sender.clientID)) } - func test_Decrypt_PublishesEpochChanges() async throws { - // Given - let groupID = MLSGroupID.random() - let messageData = Data.random() - let hasEpochChanged = true - let sender = MLSClientID( - userID: UUID.create().transportString(), - clientID: "client", - domain: "example.com" - ) - - var receivedGroupIDs = [MLSGroupID]() - let didReceiveGroupIDs = customExpectation(description: "didReceiveGroupIDs") - let cancellable = sut.onEpochChanged().collect(1).sink { - receivedGroupIDs = $0 - didReceiveGroupIDs.fulfill() - } - - mockMLSActionExecutor.mockDecryptMessage = { _, _ in - DecryptedMessage( - message: messageData, - proposals: [], - isActive: false, - commitDelay: nil, - senderClientId: sender.rawValue.data(using: .utf8)!, - hasEpochChanged: hasEpochChanged, - identity: .withBasicCredentials(), - bufferedMessages: nil, - crlNewDistributionPoints: nil - ) - } - - // When - _ = try await sut.decrypt( - message: messageData.base64EncodedString(), - for: groupID, - subconversationType: nil - ) - - // Then - XCTAssert(waitForCustomExpectations(withTimeout: 0.5)) - cancellable.cancel() - XCTAssertEqual(receivedGroupIDs, [groupID]) - } - func test_Decrypt_PublishesNewDistributionPoints() async throws { // Given let distributionPoint = "example.domain.com" diff --git a/wire-ios-data-model/Tests/MLS/MLSEncryptionServiceTests.swift b/wire-ios-data-model/Tests/MLS/MLSEncryptionServiceTests.swift index de8f14a59ee..77be6e7ff5f 100644 --- a/wire-ios-data-model/Tests/MLS/MLSEncryptionServiceTests.swift +++ b/wire-ios-data-model/Tests/MLS/MLSEncryptionServiceTests.swift @@ -25,7 +25,7 @@ import XCTest final class MLSEncryptionServiceTests: XCTestCase { var sut: MLSEncryptionService! - var mockCoreCrypto: MockCoreCryptoProtocol! + var mockCoreCryptoContext: MockCoreCryptoContextProtocol! var mockSafeCoreCrypto: MockSafeCoreCrypto! var mockCoreCryptoProvider: MockCoreCryptoProviderProtocol! @@ -33,8 +33,8 @@ final class MLSEncryptionServiceTests: XCTestCase { override func setUp() { super.setUp() - mockCoreCrypto = MockCoreCryptoProtocol() - mockSafeCoreCrypto = MockSafeCoreCrypto(coreCrypto: mockCoreCrypto) + mockCoreCryptoContext = MockCoreCryptoContextProtocol() + mockSafeCoreCrypto = MockSafeCoreCrypto(coreCryptoContext: mockCoreCryptoContext) mockCoreCryptoProvider = MockCoreCryptoProviderProtocol() mockCoreCryptoProvider.coreCrypto_MockValue = mockSafeCoreCrypto sut = MLSEncryptionService(coreCryptoProvider: mockCoreCryptoProvider) @@ -42,7 +42,7 @@ final class MLSEncryptionServiceTests: XCTestCase { override func tearDown() { sut = nil - mockCoreCrypto = nil + mockCoreCryptoContext = nil mockSafeCoreCrypto = nil super.tearDown() } @@ -60,7 +60,7 @@ final class MLSEncryptionServiceTests: XCTestCase { // Mock var mockEncryptMessageCount = 0 - mockCoreCrypto.encryptMessageConversationIdMessage_MockMethod = { + mockCoreCryptoContext.encryptMessageConversationIdMessage_MockMethod = { mockEncryptMessageCount += 1 XCTAssertEqual($0, groupID.data) XCTAssertEqual($1, unencryptedMessage) @@ -84,8 +84,8 @@ final class MLSEncryptionServiceTests: XCTestCase { let unencryptedMessage = Data.random() // Mock - mockCoreCrypto.encryptMessageConversationIdMessage_MockMethod = { _, _ in - throw CryptoError.InvalidByteArrayError(message: "invalid byte array error") + mockCoreCryptoContext.encryptMessageConversationIdMessage_MockMethod = { _, _ in + throw CoreCryptoError.Other("invalid byte array error") } // Then diff --git a/wire-ios-data-model/Tests/MLS/MLSServiceTests.swift b/wire-ios-data-model/Tests/MLS/MLSServiceTests.swift index 3f0b56008ac..dac6087e551 100644 --- a/wire-ios-data-model/Tests/MLS/MLSServiceTests.swift +++ b/wire-ios-data-model/Tests/MLS/MLSServiceTests.swift @@ -18,6 +18,7 @@ import Combine import Foundation +import WireAPI import WireCoreCrypto import WireFoundation import WireTesting @@ -26,13 +27,11 @@ import XCTest @testable import WireDataModel @testable import WireDataModelSupport -// NOTE: necessary since CryptoError doesn't publically conform to Error -extension CryptoError: Error {} - final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { var sut: MLSService! var mockCoreCrypto: MockCoreCryptoProtocol! + var mockCoreCryptoContext: MockCoreCryptoContextProtocol! var mockSafeCoreCrypto: MockSafeCoreCrypto! var mockCoreCryptoProvider: MockCoreCryptoProviderProtocol! var mockEncryptionService: MockMLSEncryptionServiceInterface! @@ -40,7 +39,6 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { var mockMLSActionExecutor: MockMLSActionExecutor! var mockSyncDelegate: MockMLSSyncDelegate! var mockActionsProvider: MockMLSActionsProviderProtocol! - var mockConversationEventProcessor: MockConversationEventProcessorProtocol! var mockStaleMLSKeyDetector: MockStaleMLSKeyDetectorProtocol! var userDefaultsTestSuite: UserDefaults! var privateUserDefaults: PrivateUserDefaults! @@ -56,7 +54,8 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { super.setUp() mockCoreCrypto = MockCoreCryptoProtocol() - mockSafeCoreCrypto = MockSafeCoreCrypto(coreCrypto: mockCoreCrypto) + mockCoreCryptoContext = MockCoreCryptoContextProtocol() + mockSafeCoreCrypto = MockSafeCoreCrypto(coreCrypto: mockCoreCrypto, coreCryptoContext: mockCoreCryptoContext) mockCoreCryptoProvider = MockCoreCryptoProviderProtocol() mockCoreCryptoProvider.coreCrypto_MockValue = mockSafeCoreCrypto mockEncryptionService = MockMLSEncryptionServiceInterface() @@ -64,8 +63,6 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { mockMLSActionExecutor = MockMLSActionExecutor() mockSyncDelegate = MockMLSSyncDelegate() mockActionsProvider = MockMLSActionsProviderProtocol() - mockConversationEventProcessor = MockConversationEventProcessorProtocol() - mockConversationEventProcessor.processConversationEvents_MockMethod = { _ in } mockStaleMLSKeyDetector = MockStaleMLSKeyDetectorProtocol() userDefaultsTestSuite = UserDefaults.temporary() privateUserDefaults = PrivateUserDefaults(userID: userIdentifier, storage: userDefaultsTestSuite) @@ -73,8 +70,9 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { mockFeatureRepository = MockFeatureRepositoryInterface() mockStaleMLSKeyDetector.keyingMaterialUpdatedFor_MockMethod = { _ in } - mockCoreCrypto.e2eiIsEnabledCiphersuite_MockValue = false - mockCoreCrypto.clientValidKeypackagesCountCiphersuiteCredentialType_MockMethod = { _, _ in + mockCoreCryptoProvider.registerEpochObserver_MockMethod = { _ in } + mockCoreCryptoContext.e2eiIsEnabledCiphersuite_MockValue = false + mockCoreCryptoContext.clientValidKeypackagesCountCiphersuiteCredentialType_MockMethod = { _, _ in 100 } @@ -97,7 +95,6 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { encryptionService: mockEncryptionService, decryptionService: mockDecryptionService, mlsActionExecutor: mockMLSActionExecutor, - conversationEventProcessor: mockConversationEventProcessor, staleKeyMaterialDetector: mockStaleMLSKeyDetector, userDefaults: userDefaultsTestSuite, actionsProvider: mockActionsProvider, @@ -112,7 +109,7 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { override func tearDown() { sut = nil keyMaterialUpdatedExpectation = nil - mockCoreCrypto = nil + mockCoreCryptoContext = nil mockSafeCoreCrypto = nil mockEncryptionService = nil mockDecryptionService = nil @@ -186,19 +183,19 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { let member3 = MLSClientID.random() var mockExportSecretKeyCount = 0 - mockCoreCrypto.exportSecretKeyConversationIdKeyLength_MockMethod = { _, _ in + mockCoreCryptoContext.exportSecretKeyConversationIdKeyLength_MockMethod = { _, _ in mockExportSecretKeyCount += 1 return secretKey } var mockConversationEpochCount = 0 - mockCoreCrypto.conversationEpochConversationId_MockMethod = { _ in + mockCoreCryptoContext.conversationEpochConversationId_MockMethod = { _ in mockConversationEpochCount += 1 return epoch } var mockGetClientIDsCount = 0 - mockCoreCrypto.getClientIdsConversationId_MockMethod = { groupID in + mockCoreCryptoContext.getClientIdsConversationId_MockMethod = { groupID in mockGetClientIDsCount += 1 switch groupID { @@ -245,13 +242,13 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { let subconversationGroupID = MLSGroupID.random() var mockConversationEpochCount = 0 - mockCoreCrypto.conversationEpochConversationId_MockMethod = { _ in + mockCoreCryptoContext.conversationEpochConversationId_MockMethod = { _ in mockConversationEpochCount += 1 return 0 } - mockCoreCrypto.exportSecretKeyConversationIdKeyLength_MockMethod = { _, _ in - throw CryptoError.ConversationNotFound(message: "conversation not found") + mockCoreCryptoContext.exportSecretKeyConversationIdKeyLength_MockMethod = { _, _ in + throw CoreCryptoError.Other("conversation not found") } // When / Then @@ -365,7 +362,7 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { ) var mockCreateConversationCount = 0 - mockCoreCrypto + mockCoreCryptoContext .createConversationConversationIdCreatorCredentialTypeConfig_MockMethod = { conversationID, creatorCredentialType, config in mockCreateConversationCount += 1 @@ -397,14 +394,14 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { ) var mockCreateConversationCount = 0 - mockCoreCrypto.createConversationConversationIdCreatorCredentialTypeConfig_MockMethod = { + mockCoreCryptoContext.createConversationConversationIdCreatorCredentialTypeConfig_MockMethod = { mockCreateConversationCount += 1 XCTAssertEqual($0, groupID.data) XCTAssertEqual($1, .basic) XCTAssertEqual($2, config) - throw CryptoError.MalformedIdentifier(message: "malformed identifier") + throw CoreCryptoError.Other("malformed identifier") } // when / then @@ -427,7 +424,7 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { fetchBackendPublicKeysExpectation.fulfill() return backendPublicKeys } - mockCoreCrypto.createConversationConversationIdCreatorCredentialTypeConfig_MockMethod = { _, _, _ in } + mockCoreCryptoContext.createConversationConversationIdCreatorCredentialTypeConfig_MockMethod = { _, _, _ in } // When _ = try await sut.createGroup(for: groupID) @@ -446,13 +443,14 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { mockActionsProvider.fetchBackendPublicKeysIn_MockMethod = { _ in backendPublicKeys } - mockCoreCrypto.createConversationConversationIdCreatorCredentialTypeConfig_MockMethod = { _, _, _ in } + mockCoreCryptoContext.createConversationConversationIdCreatorCredentialTypeConfig_MockMethod = { _, _, _ in } // When _ = try await sut.createGroup(for: groupID, removalKeys: removalKeys) // Then - let invocation = mockCoreCrypto.createConversationConversationIdCreatorCredentialTypeConfig_Invocations.first + let invocation = mockCoreCryptoContext.createConversationConversationIdCreatorCredentialTypeConfig_Invocations + .first XCTAssertEqual( invocation?.config.externalSenders, removalKeys.externalSenderKey(for: .MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519) @@ -470,16 +468,12 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { mockActionsProvider.fetchBackendPublicKeysIn_MockValue = .init( removal: .init(ed25519: removalKey) ) - mockMLSActionExecutor.mockCommitPendingProposals = { _ in - [] - } + mockMLSActionExecutor.mockCommitPendingProposals = { _ in } - mockMLSActionExecutor.mockUpdateKeyMaterial = { _ in - [] - } + mockMLSActionExecutor.mockUpdateKeyMaterial = { _ in } var mockCreateConversationCount = 0 - mockCoreCrypto + mockCoreCryptoContext .createConversationConversationIdCreatorCredentialTypeConfig_MockMethod = { conversationID, creatorCredentialType, config in mockCreateConversationCount += 1 @@ -514,8 +508,8 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { removal: .init(ed25519: removalKey) ) - mockMLSActionExecutor.mockCommitPendingProposals = { _ in [] } - mockMLSActionExecutor.mockUpdateKeyMaterial = { _ in [] } + mockMLSActionExecutor.mockCommitPendingProposals = { _ in } + mockMLSActionExecutor.mockUpdateKeyMaterial = { _ in } mockActionsProvider .claimKeyPackagesUserIDDomainCiphersuiteExcludedSelfClientIDIn_MockMethod = { _, _, _, _, _ in @@ -532,11 +526,10 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { var mockAddMembersCalled = false mockMLSActionExecutor.mockAddMembers = { _, _ in mockAddMembersCalled = true - return [ZMUpdateEvent(), ZMUpdateEvent()] } var mockCreateConversationCount = 0 - mockCoreCrypto + mockCoreCryptoContext .createConversationConversationIdCreatorCredentialTypeConfig_MockMethod = { conversationID, creatorCredentialType, config in mockCreateConversationCount += 1 @@ -577,14 +570,14 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { removal: .init(ed25519: removalKey) ) - mockMLSActionExecutor.mockCommitPendingProposals = { _ in [] } + mockMLSActionExecutor.mockCommitPendingProposals = { _ in } mockActionsProvider .claimKeyPackagesUserIDDomainCiphersuiteExcludedSelfClientIDIn_MockError = ClaimMLSKeyPackageAction.Failure .emptyKeyPackages var mockCreateConversationCount = 0 - mockCoreCrypto + mockCoreCryptoContext .createConversationConversationIdCreatorCredentialTypeConfig_MockMethod = { conversationID, creatorCredentialType, config in mockCreateConversationCount += 1 @@ -597,7 +590,7 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { custom: .init(keyRotationSpan: nil, wirePolicy: nil) )) } - mockCoreCrypto.wipeConversationConversationId_MockMethod = { _ in } + mockCoreCryptoContext.wipeConversationConversationId_MockMethod = { _ in } // When await assertItThrows(error: MLSService.MLSAddMembersError.failedToClaimKeyPackages(users: usersIncludingSelf)) { @@ -606,7 +599,7 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { // Then XCTAssertEqual(mockCreateConversationCount, 1) - XCTAssertEqual(mockCoreCrypto.wipeConversationConversationId_Invocations.count, 1) + XCTAssertEqual(mockCoreCryptoContext.wipeConversationConversationId_Invocations.count, 1) } // MARK: - Adding participants @@ -619,9 +612,7 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { let mlsUser = [MLSUser(id: id, domain: domain)] // Mock no pending proposals. - mockMLSActionExecutor.mockCommitPendingProposals = { _ in - throw CommitError.noPendingProposals - } + mockMLSActionExecutor.mockCommitPendingProposals = { _ in } // Mock claiming a key package. var keyPackage: KeyPackage! @@ -633,11 +624,9 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { // Mock adding members to the conversation. var mockAddMembersArguments = [([KeyPackage], MLSGroupID)]() - let updateEvent = dummyMemberJoinEvent() mockMLSActionExecutor.mockAddMembers = { mockAddMembersArguments.append(($0, $1)) - return [updateEvent] } // When @@ -647,11 +636,6 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { XCTAssertEqual(mockAddMembersArguments.count, 1) XCTAssertEqual(mockAddMembersArguments.first?.0, [keyPackage]) XCTAssertEqual(mockAddMembersArguments.first?.1, mlsGroupID) - - // And processd the update event. - let processConversationEventsCalls = mockConversationEventProcessor.processConversationEvents_Invocations - XCTAssertEqual(processConversationEventsCalls.count, 1) - XCTAssertEqual(processConversationEventsCalls[0], [updateEvent]) } func test_ClaimKeyPackagesWithCorrectCipherSuite_BeforeAddingMembersToConversation_Successfully() async throws { @@ -662,9 +646,7 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { let mlsUser = [MLSUser(id: id, domain: domain)] // Mock no pending proposals. - mockMLSActionExecutor.mockCommitPendingProposals = { _ in - throw CommitError.noPendingProposals - } + mockMLSActionExecutor.mockCommitPendingProposals = { _ in } // Mock claiming a key package. var keyPackage: KeyPackage! @@ -676,11 +658,9 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { // Mock adding members to the conversation. var mockAddMembersArguments = [([KeyPackage], MLSGroupID)]() - let updateEvent = dummyMemberJoinEvent() mockMLSActionExecutor.mockAddMembers = { mockAddMembersArguments.append(($0, $1)) - return [updateEvent] } // When @@ -707,10 +687,8 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { // Mock commiting a pending proposal var mockCommitPendingProposalsArgument = [MLSGroupID]() - let updateEvent1 = dummyMemberJoinEvent() mockMLSActionExecutor.mockCommitPendingProposals = { mockCommitPendingProposalsArgument.append($0) - return [updateEvent1] } // The user to add. @@ -728,11 +706,9 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { // Mock adding members to the conversation. var mockAddMembersArguments = [([KeyPackage], MLSGroupID)]() - let updateEvent2 = dummyMemberJoinEvent() mockMLSActionExecutor.mockAddMembers = { mockAddMembersArguments.append(($0, $1)) - return [updateEvent2] } // When @@ -749,10 +725,6 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { XCTAssertEqual(mockAddMembersArguments.count, 1) XCTAssertEqual(mockAddMembersArguments.first?.0, [keyPackage]) XCTAssertEqual(mockAddMembersArguments.first?.1, groupID) - - // We processed the conversation events. - let processConversationEventsCalls = mockConversationEventProcessor.processConversationEvents_Invocations - XCTAssertEqual(processConversationEventsCalls, [[updateEvent1], [updateEvent2]]) } func test_AddingMembersToConversation_ThrowsNoParticipantsToAdd() async { @@ -760,9 +732,7 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { let mlsGroupID = MLSGroupID(Data([1, 2, 3])) // Mock no pending proposals. - mockMLSActionExecutor.mockCommitPendingProposals = { _ in - throw CommitError.noPendingProposals - } + mockMLSActionExecutor.mockCommitPendingProposals = { _ in } // when / then await assertItThrows(error: MLSService.MLSAddMembersError.noMembersToAdd) { @@ -781,9 +751,7 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { let groupID = MLSGroupID.random() // Mock no pending proposals. - mockMLSActionExecutor.mockCommitPendingProposals = { _ in - throw CommitError.noPendingProposals - } + mockMLSActionExecutor.mockCommitPendingProposals = { _ in } // Mock claiming a key package. Works for user1, throws for user2 and user3 mockActionsProvider @@ -810,9 +778,7 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { let mlsUser: [MLSUser] = [MLSUser(id: id, domain: domain)] // Mock no pending proposals. - mockMLSActionExecutor.mockCommitPendingProposals = { _ in - throw CommitError.noPendingProposals - } + mockMLSActionExecutor.mockCommitPendingProposals = { _ in } // Mock key package. var keyPackage: KeyPackage! @@ -823,11 +789,11 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { } mockMLSActionExecutor.mockAddMembers = { _, _ in - throw CommitError.failedToGenerateCommit + throw CoreCryptoError.Mls(.StaleCommit) } // when / then - await assertItThrows(error: CommitError.failedToGenerateCommit) { + await assertItThrows(error: CoreCryptoError.Mls(.StaleCommit)) { try await sut.addMembersToConversation(with: mlsUser, for: mlsGroupID) } } @@ -843,17 +809,13 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { let mlsClientID = MLSClientID(userID: id, clientID: clientID, domain: domain) // Mock no pending proposals. - mockMLSActionExecutor.mockCommitPendingProposals = { _ in - throw CommitError.noPendingProposals - } + mockMLSActionExecutor.mockCommitPendingProposals = { _ in } // Mock removing clients from the group. var mockRemoveClientsArguments = [([ClientId], MLSGroupID)]() - let updateEvent = dummyMemberLeaveEvent() mockMLSActionExecutor.mockRemoveClients = { mockRemoveClientsArguments.append(($0, $1)) - return [updateEvent] } // When @@ -864,10 +826,6 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { XCTAssertEqual(mockRemoveClientsArguments.count, 1) XCTAssertEqual(mockRemoveClientsArguments.first?.0, [clientIDData]) XCTAssertEqual(mockRemoveClientsArguments.first?.1, mlsGroupID) - - // Then we process the update event. - let processConversationEventsCalls = mockConversationEventProcessor.processConversationEvents_Invocations - XCTAssertEqual(processConversationEventsCalls, [[updateEvent]]) } func test_CommitPendingProposals_BeforeRemoveMembersFromConversation_IsSuccessful() async throws { @@ -885,10 +843,8 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { // Mock commiting a pending proposal. var mockCommitPendingProposalsArgument = [MLSGroupID]() - let updateEvent1 = dummyMemberJoinEvent() mockMLSActionExecutor.mockCommitPendingProposals = { mockCommitPendingProposalsArgument.append($0) - return [updateEvent1] } // The user to remove. @@ -899,11 +855,9 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { // Mock removing clients from the group. var mockRemoveClientsArguments = [([ClientId], MLSGroupID)]() - let updateEvent2 = dummyMemberLeaveEvent() mockMLSActionExecutor.mockRemoveClients = { mockRemoveClientsArguments.append(($0, $1)) - return [updateEvent2] } // When @@ -921,10 +875,6 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { XCTAssertEqual(mockRemoveClientsArguments.count, 1) XCTAssertEqual(mockRemoveClientsArguments.first?.0, [clientIDData]) XCTAssertEqual(mockRemoveClientsArguments.first?.1, groupID) - - // Then we process the update events. - let processConversationEventsCalls = mockConversationEventProcessor.processConversationEvents_Invocations - XCTAssertEqual(processConversationEventsCalls, [[updateEvent1], [updateEvent2]]) } func test_RemovingMembersToConversation_ThrowsNoClientsToRemove() async { @@ -932,9 +882,7 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { let mlsGroupID = MLSGroupID(Data([1, 2, 3])) // Mock no pending proposals. - mockMLSActionExecutor.mockCommitPendingProposals = { _ in - throw CommitError.noPendingProposals - } + mockMLSActionExecutor.mockCommitPendingProposals = { _ in } // When / Then await assertItThrows(error: MLSService.MLSRemoveParticipantsError.noClientsToRemove) { @@ -951,17 +899,15 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { let mlsClientID = MLSClientID(userID: id, clientID: clientID, domain: domain) // Mock no pending proposals. - mockMLSActionExecutor.mockCommitPendingProposals = { _ in - throw CommitError.noPendingProposals - } + mockMLSActionExecutor.mockCommitPendingProposals = { _ in } // Mock executor error. mockMLSActionExecutor.mockRemoveClients = { _, _ in - throw CommitError.failedToGenerateCommit + throw CoreCryptoError.Mls(.StaleCommit) } // When / Then - await assertItThrows(error: CommitError.failedToGenerateCommit) { + await assertItThrows(error: CoreCryptoError.Mls(.StaleCommit)) { try await sut.removeMembersFromConversation(with: [mlsClientID], for: mlsGroupID) } } @@ -985,15 +931,7 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { mockSubconversationGroupIDRepository.fetchSubconversationGroupIDForTypeParentGroupID_MockValue = .some(nil) // Mock committing pending proposal. - var mockCommitPendingProposalArguments = [(MLSGroupID, Date)]() - let updateEvent = dummyMemberJoinEvent() - - var commitPendingProposalsCount = 0 - - mockMLSActionExecutor.mockCommitPendingProposals = { _ in - commitPendingProposalsCount += 1 - return [updateEvent] - } + mockMLSActionExecutor.mockCommitPendingProposals = { _ in } // When await withTaskGroup(of: Void.self) { group in @@ -1009,8 +947,7 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { } // Then, 9 calls within the throttle interval have been discarded, only 1 went through. - XCTAssertEqual(commitPendingProposalsCount, 1) - XCTAssertEqual(mockConversationEventProcessor.processConversationEvents_Invocations, [[updateEvent]]) + XCTAssertEqual(mockMLSActionExecutor.commitPendingProposalsCount, 1) } func test_CommitPendingProposals_NoProposalsExist() async throws { @@ -1030,9 +967,7 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { mockSubconversationGroupIDRepository.fetchSubconversationGroupIDForTypeParentGroupID_MockValue = .some(nil) // Mock no pending proposals. - mockMLSActionExecutor.mockCommitPendingProposals = { _ in - throw CommitError.noPendingProposals - } + mockMLSActionExecutor.mockCommitPendingProposals = { _ in } // When await sut.commitPendingProposals() @@ -1061,11 +996,9 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { // Mock committing pending proposal. var mockCommitPendingProposalArguments = [(MLSGroupID, Date)]() - let updateEvent = dummyMemberJoinEvent() mockMLSActionExecutor.mockCommitPendingProposals = { mockCommitPendingProposalArguments.append(($0, Date())) - return [updateEvent] } // When @@ -1080,9 +1013,6 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { await uiMOC.perform { XCTAssertNil(conversation.commitPendingProposalDate) } - - // Then we processed the update event. - XCTAssertEqual(mockConversationEventProcessor.processConversationEvents_Invocations, [[updateEvent]]) } func test_CommitPendingProposals_OneFutureCommit() async throws { @@ -1103,11 +1033,9 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { // Mock committing pending proposal. var mockCommitPendingProposalArguments = [(MLSGroupID, Date)]() - let updateEvent = dummyMemberJoinEvent() mockMLSActionExecutor.mockCommitPendingProposals = { mockCommitPendingProposalArguments.append(($0, Date())) - return [updateEvent] } // When @@ -1122,9 +1050,6 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { await uiMOC.perform { XCTAssertNil(conversation.commitPendingProposalDate) } - - // Then we processed the update event. - XCTAssertEqual(mockConversationEventProcessor.processConversationEvents_Invocations, [[updateEvent]]) } func test_CommitPendingProposals_MultipleCommits() async throws { @@ -1163,19 +1088,9 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { // Mock committing pending proposal. var mockCommitPendingProposalArguments = [(MLSGroupID, Date)]() - let updateEvent1 = dummyMemberJoinEvent() - let updateEvent2 = dummyMemberJoinEvent() - let updateEvent3 = dummyMemberJoinEvent() mockMLSActionExecutor.mockCommitPendingProposals = { mockCommitPendingProposalArguments.append(($0, Date())) - - switch mockCommitPendingProposalArguments.count { - case 1: return [updateEvent1] - case 2: return [updateEvent2] - case 3: return [updateEvent3] - default: return [] - } } // When @@ -1219,12 +1134,6 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { XCTAssertNil(conversation2.commitPendingProposalDate) XCTAssertNil(conversation3.commitPendingProposalDate) } - - // Then we processed the update event. - XCTAssertEqual( - mockConversationEventProcessor.processConversationEvents_Invocations, - [[updateEvent1], [updateEvent2], [updateEvent3]] - ) } func test_CommitPendingProposals_ForSubconversation() async throws { @@ -1248,7 +1157,6 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { let commitPendingProposalsArgumentsActor = GenericArrayActor<(MLSGroupID, Date)>() mockMLSActionExecutor.mockCommitPendingProposals = { await commitPendingProposalsArgumentsActor.append(($0, Date())) - return [] } // When @@ -1299,40 +1207,21 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { return conversation } - // swiftlint:disable:next todo_requires_jira_link - // TODO: Mock properly - let mockUpdateEvents = [ZMUpdateEvent]() - - // expectation - let expectation = XCTestExpectation(description: "Send Message") - // mock MLS action executor - mockMLSActionExecutor.mockCommitPendingProposals = { _ in - mockUpdateEvents - } - mockMLSActionExecutor.mockUpdateKeyMaterial = { _ in - mockUpdateEvents - } + mockMLSActionExecutor.mockCommitPendingProposals = { _ in } + mockMLSActionExecutor.mockUpdateKeyMaterial = { _ in } // mock CC - mockCoreCrypto.conversationExistsConversationId_MockValue = false - mockCoreCrypto.createConversationConversationIdCreatorCredentialTypeConfig_MockMethod = { _, _, _ in } - - // mock processing conversation events - var processConversationEventsArguments = [[ZMUpdateEvent]]() - mockConversationEventProcessor.processConversationEvents_MockMethod = { - processConversationEventsArguments.append($0) - expectation.fulfill() - } + mockCoreCryptoContext.conversationExistsConversationId_MockValue = false + mockCoreCryptoContext.createConversationConversationIdCreatorCredentialTypeConfig_MockMethod = { _, _, _ in } // When try await sut.performPendingJoins() // Then - await fulfillment(of: [expectation], timeout: 1) // it creates CC conversation - let createCoreCryptoConversationInvocations = mockCoreCrypto + let createCoreCryptoConversationInvocations = mockCoreCryptoContext .createConversationConversationIdCreatorCredentialTypeConfig_Invocations XCTAssertEqual(createCoreCryptoConversationInvocations.count, 1) @@ -1345,10 +1234,6 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { // it sets conversation state to ready let conversationMLSStatus = await uiMOC.perform { conversation.mlsStatus } XCTAssertEqual(conversationMLSStatus, .ready) - - // it processes conversation events - XCTAssertEqual(processConversationEventsArguments.count, 2) - XCTAssertEqual(processConversationEventsArguments, [mockUpdateEvents, mockUpdateEvents]) } func test_PerformPendingJoins_IsSuccessful() async throws { @@ -1367,13 +1252,6 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { return conversation } - // swiftlint:disable:next todo_requires_jira_link - // TODO: Mock properly - let mockUpdateEvents = [ZMUpdateEvent]() - - // expectation - let expectation = XCTestExpectation(description: "Send Message") - // mock fetching group info mockActionsProvider .fetchConversationGroupInfoConversationIdDomainSubgroupTypeContext_MockValue = publicGroupState @@ -1382,24 +1260,15 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { var joinGroupArguments = [(groupID: MLSGroupID, groupState: Data)]() mockMLSActionExecutor.mockJoinGroup = { joinGroupArguments.append(($0, $1)) - return mockUpdateEvents } // mock CC conversation exists - mockCoreCrypto.conversationExistsConversationId_MockValue = true - - // mock processing conversation events - var processConversationEventsArguments = [[ZMUpdateEvent]]() - mockConversationEventProcessor.processConversationEvents_MockMethod = { - processConversationEventsArguments.append($0) - expectation.fulfill() - } + mockCoreCryptoContext.conversationExistsConversationId_MockValue = true // When try await sut.performPendingJoins() // Then - await fulfillment(of: [expectation], timeout: 1) // it fetches public group state let groupStateInvocations = mockActionsProvider @@ -1416,28 +1285,23 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { // it sets conversation state to ready let conversationMLSStatus = await uiMOC.perform { conversation.mlsStatus } XCTAssertEqual(conversationMLSStatus, .ready) - - // it processes conversation events - XCTAssertEqual(processConversationEventsArguments.count, 1) - XCTAssertEqual(processConversationEventsArguments.first, mockUpdateEvents) } func test_PerformPendingJoins_Retries() async throws { - try await test_PerformPendingJoinsRecovery(.retry, cause: .mlsStaleMessage) + try await test_PerformPendingJoinsRecovery(MLSAPIError.mlsClientMismatch, shouldRetry: true) } func test_PerformPendingJoins_GivesUp() async throws { - try await test_PerformPendingJoinsRecovery(.giveUp, cause: .mlsCommitMissingReferences(message: "")) + try await test_PerformPendingJoinsRecovery(MLSAPIError.mlsNotEnabled, shouldRetry: false) } private func test_PerformPendingJoinsRecovery( - _ recovery: ExternalCommitError.RecoveryStrategy, - cause: SendCommitBundleAction.Failure, + _ error: MLSAPIError, + shouldRetry: Bool, file: StaticString = #filePath, line: UInt = #line ) async throws { // Given - let shouldRetry = recovery == .retry let groupID = MLSGroupID.random() let conversationID = UUID.create() let domain = "example.domain.com" @@ -1452,9 +1316,8 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { return conversation } - // set up expectations - let expectation = XCTestExpectation(description: "Send Message") - expectation.isInverted = !shouldRetry + // mock recovering with incremental sync + mockSyncDelegate.recoverWithIncrementalSync_MockMethod = {} // mock fetching group info mockActionsProvider.fetchConversationGroupInfoConversationIdDomainSubgroupTypeContext_MockValue = groupInfo @@ -1465,30 +1328,17 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { joinGroupCount += 1 if joinGroupCount == 1 { - throw ExternalCommitError.failedToSendCommit( - recovery: recovery, - cause: cause - ) + throw CoreCryptoError.Mls(.MessageRejected(reason: try error.encodeAsString())) } - - return [] } // mock CC conversation exists - mockCoreCrypto.conversationExistsConversationId_MockValue = true - - // mock processing conversation events - var processConversationEventsCount = 0 - mockConversationEventProcessor.processConversationEvents_MockMethod = { _ in - processConversationEventsCount += 1 - expectation.fulfill() - } + mockCoreCryptoContext.conversationExistsConversationId_MockValue = true // When try await sut.performPendingJoins() // Then - await fulfillment(of: [expectation], timeout: 1) // it fetches group info let groupInfoInvocations = mockActionsProvider @@ -1501,9 +1351,6 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { // it sets conversation state to ready let conversationMLSStatus = await uiMOC.perform { conversation.mlsStatus } XCTAssertEqual(conversationMLSStatus, shouldRetry ? .ready : .pendingJoin, file: file, line: line) - - // it processes conversation events - XCTAssertEqual(processConversationEventsCount, shouldRetry ? 1 : 0, file: file, line: line) } func test_PerformPendingJoins_DoesntJoinGroupNotPending() async throws { @@ -1527,7 +1374,6 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { // mock joining group mockMLSActionExecutor.mockJoinGroup = { _, _ in expectation.fulfill() - return [] } // When @@ -1560,7 +1406,7 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { await uiMOC.perform { // mock conversation epoch - self.mockCoreCrypto.conversationEpochConversationId_MockMethod = { groupID in + self.mockCoreCryptoContext.conversationEpochConversationId_MockMethod = { groupID in let isOutOfSync = (groupID == outOfSyncGroupID1.data) || (groupID == outOfSyncGroupID2.data) return isOutOfSync ? currentEpoch - 1 : currentEpoch @@ -1575,7 +1421,6 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { mockMLSActionExecutor.mockJoinGroup = { groupID, _ in XCTAssert(groupID.data.isOne(of: outOfSyncGroupID1.data, outOfSyncGroupID2.data)) XCTAssertNotEqual(groupID.data, inSyncGroupID3.data) - return [] } } @@ -1737,7 +1582,7 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { onJoinGroup: @escaping (MLSGroupID) -> Void ) { // mock conversation epoch - mockCoreCrypto.conversationEpochConversationId_MockMethod = { _ in + mockCoreCryptoContext.conversationEpochConversationId_MockMethod = { _ in epoch } @@ -1761,7 +1606,6 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { // mock join group mockMLSActionExecutor.mockJoinGroup = { groupID, _ in onJoinGroup(groupID) - return [] } } @@ -1789,7 +1633,7 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { let groupID = MLSGroupID.random() var count = 0 - mockCoreCrypto.wipeConversationConversationId_MockMethod = { (id: ConversationId) in + mockCoreCryptoContext.wipeConversationConversationId_MockMethod = { (id: Data) in count += 1 XCTAssertEqual(id, groupID.data) } @@ -1826,17 +1670,18 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { userDefaultsTestSuite.set(Date(), forKey: MLSService.Keys.keyPackageQueriedTime.rawValue) // mock that we don't have enough unclaimed kp locally - mockCoreCrypto.clientValidKeypackagesCountCiphersuiteCredentialType_MockMethod = { _, _ in + mockCoreCryptoContext.clientValidKeypackagesCountCiphersuiteCredentialType_MockMethod = { _, _ in UInt64(unsufficientKeyPackagesAmount) } // mock keyPackages returned by core cryto var mockClientKeypackagesCount = 0 - mockCoreCrypto.clientKeypackagesCiphersuiteCredentialTypeAmountRequested_MockMethod = { _, _, amountRequested in - mockClientKeypackagesCount += 1 - XCTAssertEqual(amountRequested, UInt32(self.sut.targetUnclaimedKeyPackageCount)) - return keyPackages - } + mockCoreCryptoContext + .clientKeypackagesCiphersuiteCredentialTypeAmountRequested_MockMethod = { _, _, amountRequested in + mockClientKeypackagesCount += 1 + XCTAssertEqual(amountRequested, UInt32(self.sut.targetUnclaimedKeyPackageCount)) + return keyPackages + } // mock return value for unclaimed key packages count mockActionsProvider.countUnclaimedKeyPackagesClientIDCiphersuiteContext_MockMethod = { _, _, _ in @@ -1878,7 +1723,7 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { privateUserDefaults.set(Date(), forKey: .keyPackageQueriedTime) // mock that there are enough kp locally - mockCoreCrypto.clientValidKeypackagesCountCiphersuiteCredentialType_MockMethod = { _, _ in + mockCoreCryptoContext.clientValidKeypackagesCountCiphersuiteCredentialType_MockMethod = { _, _ in UInt64(self.sut.targetUnclaimedKeyPackageCount) } @@ -1903,7 +1748,7 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { await uiMOC.perform { _ = self.createSelfClient(onMOC: self.uiMOC) } // mock that there are enough kp locally - mockCoreCrypto.clientValidKeypackagesCountCiphersuiteCredentialType_MockMethod = { _, _ in + mockCoreCryptoContext.clientValidKeypackagesCountCiphersuiteCredentialType_MockMethod = { _, _ in UInt64(self.sut.targetUnclaimedKeyPackageCount) } @@ -1912,7 +1757,7 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { } mockActionsProvider.uploadKeyPackagesClientIDKeyPackagesContext_MockMethod = { _, _, _ in } - mockCoreCrypto.clientKeypackagesCiphersuiteCredentialTypeAmountRequested_MockMethod = { _, _, _ in + mockCoreCryptoContext.clientKeypackagesCiphersuiteCredentialTypeAmountRequested_MockMethod = { _, _, _ in [Data.random()] } // When @@ -1927,7 +1772,7 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { await uiMOC.perform { _ = self.createSelfClient(onMOC: self.uiMOC) } // mock that there are enough kp locally - mockCoreCrypto.clientValidKeypackagesCountCiphersuiteCredentialType_MockMethod = { _, _ in + mockCoreCryptoContext.clientValidKeypackagesCountCiphersuiteCredentialType_MockMethod = { _, _ in UInt64(self.sut.targetUnclaimedKeyPackageCount) } @@ -1936,7 +1781,7 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { } mockActionsProvider.uploadKeyPackagesClientIDKeyPackagesContext_MockMethod = { _, _, _ in } - mockCoreCrypto.clientKeypackagesCiphersuiteCredentialTypeAmountRequested_MockMethod = { _, _, _ in + mockCoreCryptoContext.clientKeypackagesCiphersuiteCredentialTypeAmountRequested_MockMethod = { _, _, _ in [Data.random()] } // When @@ -1962,7 +1807,7 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { privateUserDefaults.set(Date.distantPast, forKey: .keyPackageQueriedTime) // mock that we don't have enough unclaimed kp locally - mockCoreCrypto.clientValidKeypackagesCountCiphersuiteCredentialType_MockMethod = { _, _ in + mockCoreCryptoContext.clientValidKeypackagesCountCiphersuiteCredentialType_MockMethod = { _, _ in UInt64(unsufficientKeyPackagesAmount) } @@ -1976,7 +1821,7 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { uploadKeyPackages.fulfill() } - mockCoreCrypto.clientKeypackagesCiphersuiteCredentialTypeAmountRequested_MockMethod = { _, _, _ in + mockCoreCryptoContext.clientKeypackagesCiphersuiteCredentialTypeAmountRequested_MockMethod = { _, _, _ in XCTFail("shouldn't be generating key packages") return [] } @@ -1996,9 +1841,7 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { let group2 = MLSGroupID.random() // Mock no pending proposals. - mockMLSActionExecutor.mockCommitPendingProposals = { _ in - throw CommitError.noPendingProposals - } + mockMLSActionExecutor.mockCommitPendingProposals = { _ in } // Mock stale groups. mockStaleMLSKeyDetector.groupsWithStaleKeyingMaterial = [group1, group2] @@ -2007,7 +1850,6 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { var mockUpdateKeyingMaterialArguments = Set() mockMLSActionExecutor.mockUpdateKeyMaterial = { mockUpdateKeyingMaterialArguments.insert($0) - return [] } // Expectations @@ -2031,12 +1873,6 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { Set([group1, group2]) ) - // Then we didn't process any events. - XCTAssertEqual( - mockConversationEventProcessor.processConversationEvents_Invocations.flatMap(\.self), - [] - ) - // Then we updated the last check date. XCTAssertEqual( sut.lastKeyMaterialUpdateCheck.timeIntervalSinceNow, @@ -2064,9 +1900,7 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { let groupID = MLSGroupID.random() // Mock no pending proposals. - mockMLSActionExecutor.mockCommitPendingProposals = { _ in - throw CommitError.noPendingProposals - } + mockMLSActionExecutor.mockCommitPendingProposals = { _ in } // Mock one failure to update key material, then a success. var mockUpdateKeyMaterialCount = 0 @@ -2074,9 +1908,11 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { defer { mockUpdateKeyMaterialCount += 1 } switch mockUpdateKeyMaterialCount { case 0: - throw CommitError.failedToSendCommit(recovery: .retryAfterQuickSync, cause: .mlsStaleMessage) + throw CoreCryptoError.Mls(.MessageRejected( + reason: try MLSAPIError.mlsClientMismatch.encodeAsString() + )) default: - return [] + return } } @@ -2091,9 +1927,6 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { // Then it performed an incremental sync once. XCTAssertEqual(mockSyncDelegate.recoverWithIncrementalSync_Invocations.count, 1) - - // Then processed the result once. - XCTAssertEqual(mockConversationEventProcessor.processConversationEvents_Invocations, [[]]) } func test_RetryOnCommitFailure_MultipleRetries() async throws { @@ -2101,9 +1934,7 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { let groupID = MLSGroupID.random() // Mock no pending proposals. - mockMLSActionExecutor.mockCommitPendingProposals = { _ in - throw CommitError.noPendingProposals - } + mockMLSActionExecutor.mockCommitPendingProposals = { _ in } // Mock three failures to update key material, then a success. var mockUpdateKeyMaterialCount = 0 @@ -2111,9 +1942,11 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { defer { mockUpdateKeyMaterialCount += 1 } switch mockUpdateKeyMaterialCount { case 0 ..< 3: - throw CommitError.failedToSendCommit(recovery: .retryAfterQuickSync, cause: .mlsStaleMessage) + throw CoreCryptoError.Mls(.MessageRejected( + reason: try MLSAPIError.mlsClientMismatch.encodeAsString() + )) default: - return [] + return } } @@ -2128,9 +1961,6 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { // Then it performed an incremental sync 3 times (for 3 failures). XCTAssertEqual(mockSyncDelegate.recoverWithIncrementalSync_Invocations.count, 3) - - // Then processed the result once. - XCTAssertEqual(mockConversationEventProcessor.processConversationEvents_Invocations, [[]]) } func test_RetryOnCommitFailure_Keep_Throwing_Commit_Error_Prevents_Infinite_Loop() async throws { @@ -2138,14 +1968,19 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { let groupID = MLSGroupID.random() // Since `retryOnCommitFailure` is a recursive function for specific error - // `CommitError.failedToSendCommit(recovery: .retryAfterQuickSync`, we'll try to create an infinite loop by keep throwing the same error over and over again. + // `mlsClientMismatch`, we'll try to create an infinite loop by keep throwing the same error over and over + // again. mockMLSActionExecutor.mockCommitPendingProposals = { _ in - throw CommitError.failedToSendCommit(recovery: .retryAfterQuickSync, cause: .mlsStaleMessage) + throw CoreCryptoError.Mls(.MessageRejected( + reason: try MLSAPIError.mlsClientMismatch.encodeAsString() + )) } mockMLSActionExecutor.mockUpdateKeyMaterial = { _ in - throw CommitError.failedToSendCommit(recovery: .retryAfterQuickSync, cause: .mlsStaleMessage) + throw CoreCryptoError.Mls(.MessageRejected( + reason: try MLSAPIError.mlsClientMismatch.encodeAsString() + )) } // Mock incremental sync. @@ -2154,9 +1989,9 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { do { // When try await sut.updateKeyMaterial(for: groupID) - } catch let error as SendMLSMessageFailure { + } catch let error as MLSService.MLSRetryError { // Then, infinite loop is broken after a few attempts, it throws an error - XCTAssertEqual(error, .mlsStaleMessage) + XCTAssertEqual(error, .retryLimitReached) } } @@ -2165,14 +2000,19 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { let groupID = MLSGroupID.random() // Since `retryOnCommitFailure` is a recursive function for specific error - // `ExternalCommitError.failedToSendCommit(recovery: .retry)`, we'll try to create an infinite loop by keep throwing the same error over and over again. + // `mlsClientMismatch`, we'll try to create an infinite loop by keep throwing the same error over and over + // again. mockMLSActionExecutor.mockCommitPendingProposals = { _ in - throw ExternalCommitError.failedToSendCommit(recovery: .retry, cause: .mlsStaleMessage) + throw CoreCryptoError.Mls(.MessageRejected( + reason: try MLSAPIError.mlsClientMismatch.encodeAsString() + )) } mockMLSActionExecutor.mockUpdateKeyMaterial = { _ in - throw ExternalCommitError.failedToSendCommit(recovery: .retry, cause: .mlsStaleMessage) + throw CoreCryptoError.Mls(.MessageRejected( + reason: try MLSAPIError.mlsClientMismatch.encodeAsString() + )) } // Mock incremental sync. @@ -2181,9 +2021,9 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { do { // When try await sut.updateKeyMaterial(for: groupID) - } catch let error as SendMLSMessageFailure { + } catch let error as MLSService.MLSRetryError { // Then, infinite loop is broken after a few attempts, it throws an error - XCTAssertEqual(error, .mlsStaleMessage) + XCTAssertEqual(error, .retryLimitReached) } } @@ -2197,9 +2037,11 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { defer { mockCommitPendingProposalsCount += 1 } switch mockCommitPendingProposalsCount { case 0 ..< 2: - throw CommitError.failedToSendCommit(recovery: .retryAfterQuickSync, cause: .mlsStaleMessage) + throw CoreCryptoError.Mls(.MessageRejected( + reason: try MLSAPIError.mlsClientMismatch.encodeAsString() + )) default: - return [] + return } } @@ -2209,9 +2051,11 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { defer { mockUpdateKeyMaterialCount += 1 } switch mockUpdateKeyMaterialCount { case 0 ..< 3: - throw CommitError.failedToSendCommit(recovery: .retryAfterQuickSync, cause: .mlsStaleMessage) + throw CoreCryptoError.Mls(.MessageRejected( + reason: try MLSAPIError.mlsClientMismatch.encodeAsString() + )) default: - return [] + return } } @@ -2229,43 +2073,38 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { // Then it performed an incremental sync 5 times (for 2 + 3 failures). XCTAssertEqual(mockSyncDelegate.recoverWithIncrementalSync_Invocations.count, 5) - - // Then processed the results twice (1 for each success). - XCTAssertEqual(mockConversationEventProcessor.processConversationEvents_Invocations, [[], []]) } func test_RetryOnCommitFailure_CommitPendingProposalsAfterRetry() async throws { // Given a group. let groupID = MLSGroupID.random() - // Mock no pending proposals when first trying to update key material, but - // then a successful pending proposal commit after the failed commit is migrated - // by Core Crypto. var mockCommitPendingProposalsCount = 0 mockMLSActionExecutor.mockCommitPendingProposals = { _ in defer { mockCommitPendingProposalsCount += 1 } switch mockCommitPendingProposalsCount { case 0: - throw CommitError.noPendingProposals + throw CoreCryptoError.Mls(.MessageRejected( + reason: try MLSAPIError.mlsCommitMissingReferences.encodeAsString() + )) default: - return [] + return } } - // Mock failures to update key material: but no success since the commit was - // migrated to a pending proposal. var mockUpdateKeyMaterialCount = 0 mockMLSActionExecutor.mockUpdateKeyMaterial = { _ in - defer { mockUpdateKeyMaterialCount += 1 } - throw CommitError.failedToSendCommit( - recovery: .commitPendingProposalsAfterQuickSync, - cause: .mlsStaleMessage - ) + mockUpdateKeyMaterialCount += 1 } // Mock incremental sync. mockSyncDelegate.recoverWithIncrementalSync_MockMethod = {} + // Mock no subgroup + mockSubconversationGroupIDRepository.findSubgroupTypeAndParentIDFor_MockMethod = { _ in + nil + } + // When try await sut.updateKeyMaterial(for: groupID) @@ -2277,32 +2116,33 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { // Then it performed an incremental sync once. XCTAssertEqual(mockSyncDelegate.recoverWithIncrementalSync_Invocations.count, 1) - - // Then processed the result once. - XCTAssertEqual(mockConversationEventProcessor.processConversationEvents_Invocations, [[]]) } func test_RetryOnCommitFailure_ItGivesUp() async throws { // Given a group. let groupID = MLSGroupID.random() + let unrecoverableError = MLSAPIError.mlsError("unrecoverable-error", "give up") // Mock no pending proposals. - mockMLSActionExecutor.mockCommitPendingProposals = { _ in - throw CommitError.noPendingProposals - } + mockMLSActionExecutor.mockCommitPendingProposals = { _ in } // Mock failures to update key material, no successes. var mockUpdateKeyMaterialCount = 0 mockMLSActionExecutor.mockUpdateKeyMaterial = { _ in defer { mockUpdateKeyMaterialCount += 1 } - throw CommitError.failedToSendCommit(recovery: .giveUp, cause: .mlsProtocolError(message: "message")) + throw CoreCryptoError.Mls(.MessageRejected( + reason: try unrecoverableError.encodeAsString() + )) } // Mock incremental sync. mockSyncDelegate.recoverWithIncrementalSync_MockMethod = {} // Then - await assertItThrows(error: SendCommitBundleAction.Failure.mlsProtocolError(message: "message")) { + await assertItThrows( + error: try MLSService.MLSRetryError + .nonRecoverableError(unrecoverableError.encodeAsString()) + ) { // When try await sut.updateKeyMaterial(for: groupID) } @@ -2312,9 +2152,6 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { // Then it didn't perform an incremental sync. XCTAssertEqual(mockSyncDelegate.recoverWithIncrementalSync_Invocations.count, 0) - - // Then it didn't process any result. - XCTAssertEqual(mockConversationEventProcessor.processConversationEvents_Invocations, []) } func test_UpdateKeyMaterial_ContinuesOnFailureForSomeGroups() async throws { @@ -2329,16 +2166,13 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { mockMLSActionExecutor.mockUpdateKeyMaterial = { groupID in if groupID == group2 { // Given one of the group fails - throw CommitError.failedToSendCommit(recovery: .giveUp, cause: .mlsStaleMessage) + throw CoreCryptoError.Mls(.MessageRejected(reason: "mls stale mesage")) } else { updatedGroups.append(groupID) - return [] } } - mockMLSActionExecutor.mockCommitPendingProposals = { _ in - [ZMUpdateEvent()] - } + mockMLSActionExecutor.mockCommitPendingProposals = { _ in } keyMaterialUpdatedExpectation = customExpectation(description: "did update key material") @@ -2381,24 +2215,23 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { ) } - mockCoreCrypto.getExternalSenderConversationId_MockMethod = { groupID in + mockCoreCryptoContext.getExternalSenderConversationId_MockMethod = { groupID in XCTAssertEqual(groupID, parentID.data) return externalSender } - mockCoreCrypto.createConversationConversationIdCreatorCredentialTypeConfig_MockMethod = { groupID, _, config in - XCTAssertEqual(config.externalSenders, [externalSender]) - XCTAssertEqual(groupID, subgroupID.data) - } + mockCoreCryptoContext + .createConversationConversationIdCreatorCredentialTypeConfig_MockMethod = { groupID, _, config in + XCTAssertEqual(config.externalSenders, [externalSender]) + XCTAssertEqual(groupID, subgroupID.data) + } mockMLSActionExecutor.mockCommitPendingProposals = { groupID in XCTAssertEqual(groupID, subgroupID) - return [] } mockMLSActionExecutor.mockUpdateKeyMaterial = { groupID in XCTAssertEqual(groupID, subgroupID) - return [] } mockSubconversationGroupIDRepository.storeSubconversationGroupIDForTypeParentGroupID_MockMethod = { _, _, _ in @@ -2423,7 +2256,10 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { XCTAssertEqual(fetchSubroupInvocation.domain, parentQualifiedID.domain) XCTAssertEqual(fetchSubroupInvocation.type, .conference) - XCTAssertEqual(mockCoreCrypto.createConversationConversationIdCreatorCredentialTypeConfig_Invocations.count, 1) + XCTAssertEqual( + mockCoreCryptoContext.createConversationConversationIdCreatorCredentialTypeConfig_Invocations.count, + 1 + ) XCTAssertEqual(mockMLSActionExecutor.commitPendingProposalsCount, 1) XCTAssertEqual(mockMLSActionExecutor.updateKeyMaterialCount, 1) @@ -2465,24 +2301,23 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { ) } - mockCoreCrypto.getExternalSenderConversationId_MockMethod = { groupID in + mockCoreCryptoContext.getExternalSenderConversationId_MockMethod = { groupID in XCTAssertEqual(groupID, parentID.data) return externalSender } - mockCoreCrypto.createConversationConversationIdCreatorCredentialTypeConfig_MockMethod = { groupID, _, config in - XCTAssertEqual(config.externalSenders, [externalSender]) - XCTAssertEqual(groupID, subgroupID.data) - } + mockCoreCryptoContext + .createConversationConversationIdCreatorCredentialTypeConfig_MockMethod = { groupID, _, config in + XCTAssertEqual(config.externalSenders, [externalSender]) + XCTAssertEqual(groupID, subgroupID.data) + } mockMLSActionExecutor.mockCommitPendingProposals = { groupID in XCTAssertEqual(groupID, subgroupID) - return [] } mockMLSActionExecutor.mockUpdateKeyMaterial = { groupID in XCTAssertEqual(groupID, subgroupID) - return [] } mockSubconversationGroupIDRepository.storeSubconversationGroupIDForTypeParentGroupID_MockMethod = { _, _, _ in @@ -2519,7 +2354,10 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { XCTAssertEqual(fetchSubroupInvocation.domain, parentQualifiedID.domain) XCTAssertEqual(fetchSubroupInvocation.type, .conference) - XCTAssertEqual(mockCoreCrypto.createConversationConversationIdCreatorCredentialTypeConfig_Invocations.count, 1) + XCTAssertEqual( + mockCoreCryptoContext.createConversationConversationIdCreatorCredentialTypeConfig_Invocations.count, + 1 + ) XCTAssertEqual(mockMLSActionExecutor.commitPendingProposalsCount, 1) XCTAssertEqual(mockMLSActionExecutor.updateKeyMaterialCount, 1) @@ -2569,7 +2407,6 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { mockMLSActionExecutor.mockJoinGroup = { XCTAssertEqual($0, subgroupID) XCTAssertEqual($1, publicGroupState) - return [] } mockSubconversationGroupIDRepository.storeSubconversationGroupIDForTypeParentGroupID_MockMethod = { _, _, _ in @@ -2634,7 +2471,7 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { } var mockWipeConversationArguments = [Data]() - mockCoreCrypto.wipeConversationConversationId_MockMethod = { + mockCoreCryptoContext.wipeConversationConversationId_MockMethod = { mockWipeConversationArguments.append($0) } @@ -2682,7 +2519,7 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { mockSubconversationGroupIDRepository .fetchSubconversationGroupIDForTypeParentGroupID_MockValue = subconversationGroupID - mockCoreCrypto.conversationExistsConversationId_MockMethod = { + mockCoreCryptoContext.conversationExistsConversationId_MockMethod = { XCTAssertEqual($0, subconversationGroupID.data) return true } @@ -2693,7 +2530,7 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { } var mockWipeConversationArguments = [Data]() - mockCoreCrypto.wipeConversationConversationId_MockMethod = { + mockCoreCryptoContext.wipeConversationConversationId_MockMethod = { mockWipeConversationArguments.append($0) } @@ -2761,7 +2598,7 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { } var mockWipeConversationArguments = [Data]() - mockCoreCrypto.wipeConversationConversationId_MockMethod = { + mockCoreCryptoContext.wipeConversationConversationId_MockMethod = { mockWipeConversationArguments.append($0) } @@ -2802,38 +2639,6 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { // MARK: - On conference info changed - func test_OnEpochChanged_InterleavesSources() throws { - // Given - let groupID1 = MLSGroupID.random() - let groupID2 = MLSGroupID.random() - let groupID3 = MLSGroupID.random() - - // Mock epoch changes - let epochChangedFromDecryptionSerivce = PassthroughSubject() - mockDecryptionService.onEpochChanged_MockValue = epochChangedFromDecryptionSerivce.eraseToAnyPublisher() - - let epochChangedFromActionExecutor = PassthroughSubject() - mockMLSActionExecutor.mockOnEpochChanged = epochChangedFromActionExecutor.eraseToAnyPublisher - - // Colect ids for groups with changed epochs - var receivedGroupIDs = [MLSGroupID]() - let didReceiveGroupIDs = customExpectation(description: "didReceiveGroupIDs") - let cancellable = sut.onEpochChanged().collect(3).sink { - receivedGroupIDs = $0 - didReceiveGroupIDs.fulfill() - } - - // When - epochChangedFromDecryptionSerivce.send(groupID1) - epochChangedFromActionExecutor.send(groupID2) - epochChangedFromDecryptionSerivce.send(groupID3) - - // Then - XCTAssert(waitForCustomExpectations(withTimeout: 0.5)) - cancellable.cancel() - XCTAssertEqual(receivedGroupIDs, [groupID1, groupID2, groupID3]) - } - func test_OnConferenceInfoChanged_WhenEpochChangesForParentConversation() async throws { // Given let parentGroupID = MLSGroupID.random() @@ -2865,30 +2670,23 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { subconversationGroupID: MLSGroupID, epochChangeSequence: MLSGroupID... ) async throws { - // Mock epoch changes - let epochChangedFromDecryptionSerivce = PassthroughSubject() - mockDecryptionService.onEpochChanged_MockValue = epochChangedFromDecryptionSerivce.eraseToAnyPublisher() - - let epochChangedFromActionExecutor = PassthroughSubject() - mockMLSActionExecutor.mockOnEpochChanged = epochChangedFromActionExecutor.eraseToAnyPublisher - // Mock conference info let epoch: UInt64 = 42 let key = Data.random(byteCount: 32) let clientID = MLSClientID.random() let clientIDData = try XCTUnwrap(clientID.rawValue.utf8Data) - mockCoreCrypto.conversationEpochConversationId_MockMethod = { groupID in + mockCoreCryptoContext.conversationEpochConversationId_MockMethod = { groupID in XCTAssertEqual(groupID, subconversationGroupID.data) return epoch } - mockCoreCrypto.exportSecretKeyConversationIdKeyLength_MockMethod = { groupID, _ in + mockCoreCryptoContext.exportSecretKeyConversationIdKeyLength_MockMethod = { groupID, _ in XCTAssertEqual(groupID, subconversationGroupID.data) return key } - mockCoreCrypto.getClientIdsConversationId_MockMethod = { groupID in + mockCoreCryptoContext.getClientIdsConversationId_MockMethod = { groupID in XCTAssertTrue(groupID.isOne(of: parentGroupID.data, subconversationGroupID.data)) return [clientIDData] } @@ -2901,7 +2699,7 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { // When for groupID in epochChangeSequence { - epochChangedFromDecryptionSerivce.send(groupID) + try await sut.epochChanged(conversationId: groupID.data, epoch: epoch) } // Then @@ -2959,13 +2757,11 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { let expectation1 = customExpectation(description: "CreateConversation should be called") let expectation2 = customExpectation(description: "UpdateKeyMaterial should be called") - mockCoreCrypto.createConversationConversationIdCreatorCredentialTypeConfig_MockMethod = { _, _, _ in + mockCoreCryptoContext.createConversationConversationIdCreatorCredentialTypeConfig_MockMethod = { _, _, _ in expectation1.fulfill() } - mockMLSActionExecutor.mockCommitPendingProposals = { _ in - [] - } + mockMLSActionExecutor.mockCommitPendingProposals = { _ in } mockActionsProvider.claimKeyPackagesUserIDDomainCiphersuiteExcludedSelfClientIDIn_MockValue = [] @@ -2973,7 +2769,6 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { mockMLSActionExecutor.mockUpdateKeyMaterial = { defer { expectation2.fulfill() } mockUpdateKeyingMaterialArguments.append($0) - return [] } // WHEN @@ -2988,7 +2783,7 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { // Given a group. let expectation1 = customExpectation(description: "CreateConversation should be called") let expectation2 = customExpectation(description: "AddMembers should be called") - mockCoreCrypto.createConversationConversationIdCreatorCredentialTypeConfig_MockMethod = { _, _, _ in + mockCoreCryptoContext.createConversationConversationIdCreatorCredentialTypeConfig_MockMethod = { _, _, _ in expectation1.fulfill() } @@ -2998,12 +2793,11 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { } mockMLSActionExecutor.mockCommitPendingProposals = { _ in - [ZMUpdateEvent()] + } mockMLSActionExecutor.mockAddMembers = { _, _ in expectation2.fulfill() - return [ZMUpdateEvent()] } let groupID = MLSGroupID.random() @@ -3022,13 +2816,11 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { var commitPendingProposalsInvocations = [MLSGroupID]() mockMLSActionExecutor.mockCommitPendingProposals = { commitPendingProposalsInvocations.append($0) - return [] } var updateKeyMaterialInvocations = [MLSGroupID]() mockMLSActionExecutor.mockUpdateKeyMaterial = { updateKeyMaterialInvocations.append($0) - return [] } // When @@ -3056,20 +2848,17 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { let expectation1 = customExpectation(description: "CreateConversation should be called") - mockMLSActionExecutor.mockJoinGroup = { _, _ in - [ZMUpdateEvent()] - } + mockMLSActionExecutor.mockJoinGroup = { _, _ in } mockActionsProvider.fetchConversationGroupInfoConversationIdDomainSubgroupTypeContext_MockValue = Data() - mockCoreCrypto.conversationExistsConversationId_MockMethod = { _ in + mockCoreCryptoContext.conversationExistsConversationId_MockMethod = { _ in false } - mockCoreCrypto.createConversationConversationIdCreatorCredentialTypeConfig_MockMethod = { _, _, _ in + mockCoreCryptoContext.createConversationConversationIdCreatorCredentialTypeConfig_MockMethod = { _, _, _ in expectation1.fulfill() } mockMLSActionExecutor.mockCommitPendingProposals = { id in XCTAssertEqual(id, groupID) - return [] } mockActionsProvider.claimKeyPackagesUserIDDomainCiphersuiteExcludedSelfClientIDIn_MockValue = [.init( @@ -3082,7 +2871,6 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { mockMLSActionExecutor.mockAddMembers = { _, id in XCTAssertEqual(id, groupID) - return [] } // WHEN @@ -3105,17 +2893,14 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { conversation.domain = "example.com" } - mockMLSActionExecutor.mockJoinGroup = { _, _ in - [ZMUpdateEvent()] - } + mockMLSActionExecutor.mockJoinGroup = { _, _ in } mockActionsProvider.fetchConversationGroupInfoConversationIdDomainSubgroupTypeContext_MockValue = Data() - mockCoreCrypto.conversationExistsConversationId_MockMethod = { _ in + mockCoreCryptoContext.conversationExistsConversationId_MockMethod = { _ in true } mockMLSActionExecutor.mockCommitPendingProposals = { id in XCTAssertEqual(id, groupID) - return [] } mockActionsProvider.claimKeyPackagesUserIDDomainCiphersuiteExcludedSelfClientIDIn_MockValue = [.init( @@ -3128,7 +2913,6 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { mockMLSActionExecutor.mockAddMembers = { _, id in XCTAssertEqual(id, groupID) - return [] } // WHEN @@ -3174,7 +2958,7 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { } let createConversationExpectation = XCTestExpectation(description: "createConversation must be called") - mockCoreCrypto + mockCoreCryptoContext .createConversationConversationIdCreatorCredentialTypeConfig_MockMethod = { conversationID, _, _ in XCTAssertEqual(conversationID, mlsGroupID.data) createConversationExpectation.fulfill() @@ -3184,13 +2968,11 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { mockMLSActionExecutor.mockUpdateKeyMaterial = { [self] mlsGroupID in XCTAssertEqual(mlsGroupID, uiMOC.performAndWait { conversation.mlsGroupID }) updateKeyMaterialExpectation.fulfill() - return [] } let commitPendingProposalsExpectation = XCTestExpectation(description: "commitPendingProposals must be called") mockMLSActionExecutor.mockCommitPendingProposals = { _ in commitPendingProposalsExpectation.fulfill() - throw CommitError.noPendingProposals } // Mock claiming a key package. @@ -3204,10 +2986,8 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { // Mock adding members to the conversation. var addedMembers = [(keyPackages: [KeyPackage], mlsGroupID: MLSGroupID)]() - let updateEvent = dummyMemberJoinEvent() mockMLSActionExecutor.mockAddMembers = { addedMembers.append(($0, $1)) - return [updateEvent] } // When @@ -3229,11 +3009,6 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { XCTAssertEqual(addedMembers.count, 1) XCTAssertEqual(addedMembers.first?.keyPackages, [keyPackage]) XCTAssertEqual(addedMembers.first?.mlsGroupID, mlsGroupID) - - // And processed the update event. - let processConversationEventsCalls = mockConversationEventProcessor.processConversationEvents_Invocations - XCTAssertEqual(processConversationEventsCalls.flatMap(\.self).count, 1) - XCTAssertEqual(processConversationEventsCalls.flatMap(\.self).first, updateEvent) } func test_startProteusToMLSMigration_staleMessageErrorWipesGroup() async throws { @@ -3254,12 +3029,12 @@ final class MLSServiceTests: ZMConversationTestsBase, MLSServiceDelegate { mockActionsProvider.updateConversationProtocolQualifiedIDMessageProtocolContext_MockMethod = { _, _, _ in } mockActionsProvider.syncConversationQualifiedIDContext_MockMethod = { _, _ in } - mockCoreCrypto.createConversationConversationIdCreatorCredentialTypeConfig_MockMethod = { _, _, _ in } + mockCoreCryptoContext.createConversationConversationIdCreatorCredentialTypeConfig_MockMethod = { _, _, _ in } mockMLSActionExecutor.mockUpdateKeyMaterial = { _ in throw SendMLSMessageFailure.mlsStaleMessage } let wipeConversationExpectation = XCTestExpectation(description: "wipeConversation must be called") - mockCoreCrypto.wipeConversationConversationId_MockMethod = { conversationID in + mockCoreCryptoContext.wipeConversationConversationId_MockMethod = { conversationID in XCTAssertEqual(conversationID, mlsGroupID.data) wipeConversationExpectation.fulfill() } diff --git a/wire-ios-data-model/Tests/MLS/MockMLSActionExecutor.swift b/wire-ios-data-model/Tests/MLS/MockMLSActionExecutor.swift index 6560eedf397..6d8d9943809 100644 --- a/wire-ios-data-model/Tests/MLS/MockMLSActionExecutor.swift +++ b/wire-ios-data-model/Tests/MLS/MockMLSActionExecutor.swift @@ -55,14 +55,14 @@ final class MockMLSActionExecutor: MLSActionExecutorProtocol { // MARK: - Add members - typealias AddMembersMock = ([KeyPackage], MLSGroupID) async throws -> [ZMUpdateEvent] + typealias AddMembersMock = ([KeyPackage], MLSGroupID) async throws -> Void private var _mockAddMembers: AddMembersMock? var mockAddMembers: AddMembersMock? { get { serialQueue.sync { _mockAddMembers } } set { serialQueue.sync { _mockAddMembers = newValue } } } - func addMembers(_ keyPackages: [KeyPackage], to groupID: MLSGroupID) async throws -> [ZMUpdateEvent] { + func addMembers(_ keyPackages: [KeyPackage], to groupID: MLSGroupID) async throws { guard let mock = mockAddMembers else { fatalError("no mock for `addMembers`") } @@ -72,14 +72,14 @@ final class MockMLSActionExecutor: MLSActionExecutorProtocol { // MARK: - Remove clients - typealias RemoveClientsMock = ([ClientId], MLSGroupID) async throws -> [ZMUpdateEvent] + typealias RemoveClientsMock = ([ClientId], MLSGroupID) async throws -> Void private var mockRemoveClients_: RemoveClientsMock? var mockRemoveClients: RemoveClientsMock? { get { serialQueue.sync { mockRemoveClients_ } } set { serialQueue.sync { mockRemoveClients_ = newValue } } } - func removeClients(_ clients: [ClientId], from groupID: MLSGroupID) async throws -> [ZMUpdateEvent] { + func removeClients(_ clients: [ClientId], from groupID: MLSGroupID) async throws { guard let mock = mockRemoveClients else { fatalError("no mock for `removeClients`") } @@ -89,7 +89,7 @@ final class MockMLSActionExecutor: MLSActionExecutorProtocol { // MARK: - Update key material - typealias UpdateKeyMaterialMock = (MLSGroupID) async throws -> [ZMUpdateEvent] + typealias UpdateKeyMaterialMock = (MLSGroupID) async throws -> Void private var mockUpdateKeyMaterial_: UpdateKeyMaterialMock? var mockUpdateKeyMaterial: UpdateKeyMaterialMock? { get { serialQueue.sync { mockUpdateKeyMaterial_ } } @@ -102,7 +102,7 @@ final class MockMLSActionExecutor: MLSActionExecutorProtocol { set { serialQueue.sync { updateKeyMaterialCount_ = newValue } } } - func updateKeyMaterial(for groupID: MLSGroupID) async throws -> [ZMUpdateEvent] { + func updateKeyMaterial(for groupID: MLSGroupID) async throws { guard let mock = mockUpdateKeyMaterial else { fatalError("no mock for `updateKeyMaterial`") } @@ -113,7 +113,7 @@ final class MockMLSActionExecutor: MLSActionExecutorProtocol { // MARK: - Commit pending proposals - typealias CommitPendingProposalsMock = (MLSGroupID) async throws -> [ZMUpdateEvent] + typealias CommitPendingProposalsMock = (MLSGroupID) async throws -> Void private var mockCommitPendingProposals_: CommitPendingProposalsMock? var mockCommitPendingProposals: CommitPendingProposalsMock? { get { serialQueue.sync { mockCommitPendingProposals_ } } @@ -126,7 +126,7 @@ final class MockMLSActionExecutor: MLSActionExecutorProtocol { set { serialQueue.sync { commitPendingProposalsCount_ = newValue } } } - func commitPendingProposals(in groupID: MLSGroupID) async throws -> [ZMUpdateEvent] { + func commitPendingProposals(in groupID: MLSGroupID) async throws { guard let mock = mockCommitPendingProposals else { fatalError("no mock for `commitPendingProposals`") } @@ -137,7 +137,7 @@ final class MockMLSActionExecutor: MLSActionExecutorProtocol { // MARK: - Join group - typealias JoinGroupMock = (MLSGroupID, Data) async throws -> [ZMUpdateEvent] + typealias JoinGroupMock = (MLSGroupID, Data) async throws -> Void private var mockJoinGroup_: JoinGroupMock? var mockJoinGroup: JoinGroupMock? { get { serialQueue.sync { mockJoinGroup_ } } @@ -150,7 +150,7 @@ final class MockMLSActionExecutor: MLSActionExecutorProtocol { set { serialQueue.sync { mockJoinGroupCount_ = newValue } } } - func joinGroup(_ groupID: MLSGroupID, groupInfo: Data) async throws -> [ZMUpdateEvent] { + func joinGroup(_ groupID: MLSGroupID, groupInfo: Data) async throws { guard let mock = mockJoinGroup else { fatalError("no mock for `joinGroup`") } diff --git a/wire-ios-data-model/Tests/MLS/SafeCoreCryptoTests.swift b/wire-ios-data-model/Tests/MLS/SafeCoreCryptoTests.swift index 242d543cf48..58dea312c0a 100644 --- a/wire-ios-data-model/Tests/MLS/SafeCoreCryptoTests.swift +++ b/wire-ios-data-model/Tests/MLS/SafeCoreCryptoTests.swift @@ -27,30 +27,38 @@ class SafeCoreCryptoTests: ZMBaseManagedObjectTest { // GIVEN let tempURL = createTempFolder() let mockCoreCrypto = MockCoreCryptoProtocol() - mockCoreCrypto.restoreFromDisk_MockMethod = {} + let mockCoreCryptoContext = MockCoreCryptoContextProtocol() + + mockCoreCrypto.transaction_MockMethod = { block in + _ = try await block(mockCoreCryptoContext) + } + + mockCoreCryptoContext.proteusReloadSessions_MockMethod = {} let sut = SafeCoreCrypto(coreCrypto: mockCoreCrypto, databasePath: tempURL.path) // WHEN / THEN - await sut.perform { _ in } + try await sut.perform { _ in } } func test_performDoesCallRestoreFromDisk() async throws { let tempURL = createTempFolder() let mockCoreCrypto = MockCoreCryptoProtocol() - var called = false - mockCoreCrypto.setCallbacksCallbacks_MockMethod = { _ in } - mockCoreCrypto.restoreFromDisk_MockMethod = { - called = true + let mockCoreCryptoContext = MockCoreCryptoContextProtocol() + + mockCoreCrypto.transaction_MockMethod = { block in + _ = try await block(mockCoreCryptoContext) } + mockCoreCryptoContext.proteusReloadSessions_MockMethod = {} + let sut = SafeCoreCrypto(coreCrypto: mockCoreCrypto, databasePath: tempURL.path) // WHEN - await sut.perform { _ in } + try await sut.perform { _ in } // THEN - XCTAssertTrue(called) + XCTAssertEqual(mockCoreCryptoContext.proteusReloadSessions_Invocations.count, 1) } } diff --git a/wire-ios-data-model/Tests/Proteus/CryptoboxMigrationManagerTests.swift b/wire-ios-data-model/Tests/Proteus/CryptoboxMigrationManagerTests.swift index 3d614fa4b66..ec8757a9192 100644 --- a/wire-ios-data-model/Tests/Proteus/CryptoboxMigrationManagerTests.swift +++ b/wire-ios-data-model/Tests/Proteus/CryptoboxMigrationManagerTests.swift @@ -110,7 +110,7 @@ class CryptoboxMigrationManagerTests: ZMBaseManagedObjectTest { let migrated = customExpectation(description: "Cryptobox was migrated") mockFileManager.fileExistsAtPath_MockValue = true proteusViaCoreCryptoFlag.isOn = true - mockSafeCoreCrypto.coreCrypto.proteusCryptoboxMigratePath_MockMethod = { _ in + mockSafeCoreCrypto.coreCryptoContext.proteusCryptoboxMigratePath_MockMethod = { _ in migrated.fulfill() } @@ -127,7 +127,7 @@ class CryptoboxMigrationManagerTests: ZMBaseManagedObjectTest { mockFileManager.fileExistsAtPath_MockValue = true proteusViaCoreCryptoFlag.isOn = true - mockSafeCoreCrypto.coreCrypto.proteusCryptoboxMigratePath_MockMethod = { _ in + mockSafeCoreCrypto.coreCryptoContext.proteusCryptoboxMigratePath_MockMethod = { _ in throw CryptoboxMigrationManager.Failure.failedToMigrateData } diff --git a/wire-ios-data-model/Tests/Proteus/ProteusServiceTests.swift b/wire-ios-data-model/Tests/Proteus/ProteusServiceTests.swift index e2644f2f10c..b42eac957b3 100644 --- a/wire-ios-data-model/Tests/Proteus/ProteusServiceTests.swift +++ b/wire-ios-data-model/Tests/Proteus/ProteusServiceTests.swift @@ -27,7 +27,7 @@ class ProteusServiceTests: XCTestCase { struct MockError: Error, Equatable {} - var mockCoreCrypto: MockCoreCryptoProtocol! + var mockCoreCryptoContext: MockCoreCryptoContextProtocol! var mockSafeCoreCrypto: MockSafeCoreCrypto! var mockCoreCryptoProvider: MockCoreCryptoProviderProtocol! var sut: ProteusService! @@ -36,16 +36,16 @@ class ProteusServiceTests: XCTestCase { override func setUpWithError() throws { try super.setUpWithError() - mockCoreCrypto = MockCoreCryptoProtocol() - mockCoreCrypto.proteusInit_MockMethod = {} - mockSafeCoreCrypto = MockSafeCoreCrypto(coreCrypto: mockCoreCrypto) + mockCoreCryptoContext = MockCoreCryptoContextProtocol() + mockCoreCryptoContext.proteusInit_MockMethod = {} + mockSafeCoreCrypto = MockSafeCoreCrypto(coreCryptoContext: mockCoreCryptoContext) mockCoreCryptoProvider = MockCoreCryptoProviderProtocol() mockCoreCryptoProvider.coreCrypto_MockValue = mockSafeCoreCrypto sut = ProteusService(coreCryptoProvider: mockCoreCryptoProvider) } override func tearDown() { - mockCoreCrypto = nil + mockCoreCryptoContext = nil mockSafeCoreCrypto = nil sut = nil super.tearDown() @@ -59,12 +59,12 @@ class ProteusServiceTests: XCTestCase { let encryptedData = Data.secureRandomData(length: 8) // Mock - mockCoreCrypto.proteusSessionExistsSessionId_MockMethod = { id in + mockCoreCryptoContext.proteusSessionExistsSessionId_MockMethod = { id in XCTAssertEqual(id, sessionID.rawValue) return true } - mockCoreCrypto.proteusDecryptSessionIdCiphertext_MockMethod = { id, ciphertext in + mockCoreCryptoContext.proteusDecryptSessionIdCiphertext_MockMethod = { id, ciphertext in XCTAssertEqual(id, sessionID.rawValue) XCTAssertEqual(ciphertext, encryptedData) return Data([0, 1, 2, 3, 4, 5]) @@ -87,16 +87,12 @@ class ProteusServiceTests: XCTestCase { let encryptedData = Data.secureRandomData(length: 8) // Mock - mockCoreCrypto.proteusSessionExistsSessionId_MockMethod = { id in + mockCoreCryptoContext.proteusSessionExistsSessionId_MockMethod = { id in XCTAssertEqual(id, sessionID.rawValue) return true } - mockCoreCrypto.proteusLastErrorCode_MockMethod = { - 209 - } - - mockCoreCrypto.proteusDecryptSessionIdCiphertext_MockMethod = { _, _ in + mockCoreCryptoContext.proteusDecryptSessionIdCiphertext_MockMethod = { _, _ in throw CoreCryptoError.Proteus(.DuplicateMessage) } @@ -122,12 +118,12 @@ class ProteusServiceTests: XCTestCase { let encryptedData = Data.secureRandomData(length: 8) // Mock - mockCoreCrypto.proteusSessionExistsSessionId_MockMethod = { id in + mockCoreCryptoContext.proteusSessionExistsSessionId_MockMethod = { id in XCTAssertEqual(id, sessionID.rawValue) return false } - mockCoreCrypto.proteusSessionFromMessageSessionIdEnvelope_MockMethod = { id, ciphertext in + mockCoreCryptoContext.proteusSessionFromMessageSessionIdEnvelope_MockMethod = { id, ciphertext in XCTAssertEqual(id, sessionID.rawValue) XCTAssertEqual(ciphertext, encryptedData) return Data([0, 1, 2, 3, 4, 5]) @@ -150,16 +146,12 @@ class ProteusServiceTests: XCTestCase { let encryptedData = Data.secureRandomData(length: 8) // Mock - mockCoreCrypto.proteusSessionExistsSessionId_MockMethod = { id in + mockCoreCryptoContext.proteusSessionExistsSessionId_MockMethod = { id in XCTAssertEqual(id, sessionID.rawValue) return false } - mockCoreCrypto.proteusLastErrorCode_MockMethod = { - 209 - } - - mockCoreCrypto.proteusSessionFromMessageSessionIdEnvelope_MockMethod = { _, _ in + mockCoreCryptoContext.proteusSessionFromMessageSessionIdEnvelope_MockMethod = { _, _ in throw CoreCryptoError.Proteus(.DuplicateMessage) } @@ -188,7 +180,7 @@ class ProteusServiceTests: XCTestCase { // Mock var encryptCalls = 0 - mockCoreCrypto.proteusEncryptSessionIdPlaintext_MockMethod = { sessionIDString, plaintextData in + mockCoreCryptoContext.proteusEncryptSessionIdPlaintext_MockMethod = { sessionIDString, plaintextData in encryptCalls += 1 XCTAssertEqual(sessionIDString, sessionID.rawValue) XCTAssertEqual(plaintextData, plaintext) @@ -214,7 +206,7 @@ class ProteusServiceTests: XCTestCase { let error = MockError() // Mock var encryptCalls = 0 - mockCoreCrypto.proteusEncryptSessionIdPlaintext_MockMethod = { sessionIDString, plaintextData in + mockCoreCryptoContext.proteusEncryptSessionIdPlaintext_MockMethod = { sessionIDString, plaintextData in encryptCalls += 1 XCTAssertEqual(sessionIDString, sessionID.rawValue) XCTAssertEqual(plaintextData, plaintext) @@ -241,7 +233,7 @@ class ProteusServiceTests: XCTestCase { // Mock var sessionDeleteCalls = [String]() - mockCoreCrypto.proteusSessionDeleteSessionId_MockMethod = { + mockCoreCryptoContext.proteusSessionDeleteSessionId_MockMethod = { sessionDeleteCalls.append($0) } @@ -257,7 +249,7 @@ class ProteusServiceTests: XCTestCase { let sessionID = ProteusSessionID.random() // Mock - mockCoreCrypto.proteusSessionDeleteSessionId_MockMethod = { _ in + mockCoreCryptoContext.proteusSessionDeleteSessionId_MockMethod = { _ in throw MockError() } diff --git a/wire-ios-data-model/Tests/Source/E2EIdentity/E2EIServiceTests.swift b/wire-ios-data-model/Tests/Source/E2EIdentity/E2EIServiceTests.swift index b1df8dbe619..c5543245ed3 100644 --- a/wire-ios-data-model/Tests/Source/E2EIdentity/E2EIServiceTests.swift +++ b/wire-ios-data-model/Tests/Source/E2EIdentity/E2EIServiceTests.swift @@ -25,7 +25,7 @@ final class E2EIServiceTests: ZMConversationTestsBase { var sut: E2EIService! var mockE2eIdentity: MockE2EIEnrollment! - var mockCoreCrypto: MockCoreCryptoProtocol! + var mockCoreCryptoContext: MockCoreCryptoContextProtocol! var mockSafeCoreCrypto: MockSafeCoreCrypto! var mockCoreCryptoProvider: MockCoreCryptoProviderProtocol! @@ -33,8 +33,8 @@ final class E2EIServiceTests: ZMConversationTestsBase { super.setUp() mockE2eIdentity = MockE2EIEnrollment() - mockCoreCrypto = MockCoreCryptoProtocol() - mockSafeCoreCrypto = MockSafeCoreCrypto(coreCrypto: mockCoreCrypto) + mockCoreCryptoContext = MockCoreCryptoContextProtocol() + mockSafeCoreCrypto = MockSafeCoreCrypto(coreCryptoContext: mockCoreCryptoContext) mockCoreCryptoProvider = MockCoreCryptoProviderProtocol() mockCoreCryptoProvider.coreCrypto_MockValue = mockSafeCoreCrypto sut = E2EIService( @@ -45,7 +45,7 @@ final class E2EIServiceTests: ZMConversationTestsBase { } override func tearDown() { - mockCoreCrypto = nil + mockCoreCryptoContext = nil mockSafeCoreCrypto = nil mockCoreCryptoProvider = nil mockE2eIdentity = nil diff --git a/wire-ios-data-model/Tests/Source/E2EIdentity/E2EIVerificationStatusServiceTests.swift b/wire-ios-data-model/Tests/Source/E2EIdentity/E2EIVerificationStatusServiceTests.swift index 0880f6694e9..e40aa3430a9 100644 --- a/wire-ios-data-model/Tests/Source/E2EIdentity/E2EIVerificationStatusServiceTests.swift +++ b/wire-ios-data-model/Tests/Source/E2EIdentity/E2EIVerificationStatusServiceTests.swift @@ -21,25 +21,25 @@ import Foundation @testable import WireDataModel @testable import WireDataModelSupport -class E2EIVerificationStatusServiceTests: ZMConversationTestsBase { +class E2EIVerificationStatusServiceTests: XCTestCase { var sut: E2EIVerificationStatusService! - var mockCoreCrypto: MockCoreCryptoProtocol! + var mockCoreCryptoContext: MockCoreCryptoContextProtocol! var mockSafeCoreCrypto: MockSafeCoreCrypto! var mockCoreCryptoProvider: MockCoreCryptoProviderProtocol! override func setUp() { super.setUp() - mockCoreCrypto = MockCoreCryptoProtocol() - mockSafeCoreCrypto = MockSafeCoreCrypto(coreCrypto: mockCoreCrypto) + mockCoreCryptoContext = MockCoreCryptoContextProtocol() + mockSafeCoreCrypto = MockSafeCoreCrypto(coreCryptoContext: mockCoreCryptoContext) mockCoreCryptoProvider = MockCoreCryptoProviderProtocol() mockCoreCryptoProvider.coreCrypto_MockValue = mockSafeCoreCrypto sut = E2EIVerificationStatusService(coreCryptoProvider: mockCoreCryptoProvider) } override func tearDown() { - mockCoreCrypto = nil + mockCoreCryptoContext = nil mockSafeCoreCrypto = nil mockCoreCryptoProvider = nil @@ -53,7 +53,7 @@ class E2EIVerificationStatusServiceTests: ZMConversationTestsBase { func test_GetConversationStatus_IsSuccessful() async throws { // Given let groupID = MLSGroupID.random() - mockCoreCrypto.e2eiConversationStateConversationId_MockMethod = { _ in + mockCoreCryptoContext.e2eiConversationStateConversationId_MockMethod = { _ in .verified } diff --git a/wire-ios-data-model/Tests/UseCases/IsUserE2EICertifiedUseCaseTests.swift b/wire-ios-data-model/Tests/UseCases/IsUserE2EICertifiedUseCaseTests.swift index aaaae56197a..25079dfd796 100644 --- a/wire-ios-data-model/Tests/UseCases/IsUserE2EICertifiedUseCaseTests.swift +++ b/wire-ios-data-model/Tests/UseCases/IsUserE2EICertifiedUseCaseTests.swift @@ -43,9 +43,9 @@ final class IsUserE2EICertifiedUseCaseTests: ZMBaseManagedObjectTest { setupMLSSelfConversations(in: context) setupOneOnOneConversations(in: context) setupClientIDs(in: context) - let mockCoreCrypto = MockCoreCryptoProtocol() - mockCoreCrypto.getClientIdsConversationId_MockValue = clientIDs.compactMap(\.data) - mockSafeCoreCrypto = MockSafeCoreCrypto(coreCrypto: mockCoreCrypto) + let mockCoreCryptoContext = MockCoreCryptoContextProtocol() + mockCoreCryptoContext.getClientIdsConversationId_MockValue = clientIDs.compactMap(\.data) + mockSafeCoreCrypto = MockSafeCoreCrypto(coreCryptoContext: mockCoreCryptoContext) mockCoreCryptoProvider = MockCoreCryptoProviderProtocol() mockCoreCryptoProvider.coreCrypto_MockValue = mockSafeCoreCrypto mockFeatureRepository = .init() @@ -76,7 +76,7 @@ final class IsUserE2EICertifiedUseCaseTests: ZMBaseManagedObjectTest { func testExpiredCertificateForSelfUserResultsInFalse() async throws { // Given - mockSafeCoreCrypto.coreCrypto + mockSafeCoreCrypto.coreCryptoContext .getUserIdentitiesConversationIdUserIds_MockMethod = { [clientIDs] conversationID, userIDs in XCTAssertEqual(conversationID, .init(base64Encoded: "qE4EdglNFI53Cm4soIFZ/rUMVL4JfCgcE4eo86QVxSc=")!) // eventually a userID will have the suffix "@example.com", but it's low prio on the Core Crypto team @@ -101,14 +101,15 @@ final class IsUserE2EICertifiedUseCaseTests: ZMBaseManagedObjectTest { func testRevokedCertificateForSelfUserResultsInFalse() async throws { // Given - mockSafeCoreCrypto.coreCrypto.getUserIdentitiesConversationIdUserIds_MockMethod = { [clientIDs] _, userIDs in - [ - userIDs[0]: [ - .with(clientID: clientIDs![0].rawValue, status: .valid), - .with(clientID: clientIDs![1].rawValue, status: .revoked) + mockSafeCoreCrypto.coreCryptoContext + .getUserIdentitiesConversationIdUserIds_MockMethod = { [clientIDs] _, userIDs in + [ + userIDs[0]: [ + .with(clientID: clientIDs![0].rawValue, status: .valid), + .with(clientID: clientIDs![1].rawValue, status: .revoked) + ] ] - ] - } + } // When let isCertified = try await sut.invoke( @@ -122,14 +123,15 @@ final class IsUserE2EICertifiedUseCaseTests: ZMBaseManagedObjectTest { func testValidCertificatesForSelfUserResultsInTrue() async throws { // Given - mockSafeCoreCrypto.coreCrypto.getUserIdentitiesConversationIdUserIds_MockMethod = { [clientIDs] _, userIDs in - [ - userIDs[0]: [ - .with(clientID: clientIDs![0].rawValue, status: .valid), - .with(clientID: clientIDs![1].rawValue, status: .valid) + mockSafeCoreCrypto.coreCryptoContext + .getUserIdentitiesConversationIdUserIds_MockMethod = { [clientIDs] _, userIDs in + [ + userIDs[0]: [ + .with(clientID: clientIDs![0].rawValue, status: .valid), + .with(clientID: clientIDs![1].rawValue, status: .valid) + ] ] - ] - } + } // When let isCertified = try await sut.invoke( @@ -143,8 +145,8 @@ final class IsUserE2EICertifiedUseCaseTests: ZMBaseManagedObjectTest { func testEmptyResultEvaluatesToFalse() async throws { // Given - mockSafeCoreCrypto.coreCrypto.getClientIdsConversationId_MockValue = [] - mockSafeCoreCrypto.coreCrypto.getUserIdentitiesConversationIdUserIds_MockMethod = { _, _ in + mockSafeCoreCrypto.coreCryptoContext.getClientIdsConversationId_MockValue = [] + mockSafeCoreCrypto.coreCryptoContext.getUserIdentitiesConversationIdUserIds_MockMethod = { _, _ in [:] } @@ -160,8 +162,8 @@ final class IsUserE2EICertifiedUseCaseTests: ZMBaseManagedObjectTest { func testEmptyIdentitiesEvaluatesToFalse() async throws { // Given - mockSafeCoreCrypto.coreCrypto.getClientIdsConversationId_MockValue = [] - mockSafeCoreCrypto.coreCrypto.getUserIdentitiesConversationIdUserIds_MockMethod = { _, userIDs in + mockSafeCoreCrypto.coreCryptoContext.getClientIdsConversationId_MockValue = [] + mockSafeCoreCrypto.coreCryptoContext.getUserIdentitiesConversationIdUserIds_MockMethod = { _, userIDs in [userIDs[0]: []] } @@ -179,14 +181,15 @@ final class IsUserE2EICertifiedUseCaseTests: ZMBaseManagedObjectTest { func testRevokedCertificateOfOtherUserResultsInFalse() async throws { // Given - mockSafeCoreCrypto.coreCrypto.getUserIdentitiesConversationIdUserIds_MockMethod = { [clientIDs] _, userIDs in - [ - userIDs[0]: [ - .with(clientID: clientIDs![2].rawValue, status: .valid), - .with(clientID: clientIDs![3].rawValue, status: .revoked) + mockSafeCoreCrypto.coreCryptoContext + .getUserIdentitiesConversationIdUserIds_MockMethod = { [clientIDs] _, userIDs in + [ + userIDs[0]: [ + .with(clientID: clientIDs![2].rawValue, status: .valid), + .with(clientID: clientIDs![3].rawValue, status: .revoked) + ] ] - ] - } + } // When let isCertified = try await sut.invoke( @@ -200,14 +203,15 @@ final class IsUserE2EICertifiedUseCaseTests: ZMBaseManagedObjectTest { func testValidCertificatesForOtherUserResultsInTrue() async throws { // Given - mockSafeCoreCrypto.coreCrypto.getUserIdentitiesConversationIdUserIds_MockMethod = { [clientIDs] _, userIDs in - [ - userIDs[0]: [ - .with(clientID: clientIDs![2].rawValue, status: .valid), - .with(clientID: clientIDs![3].rawValue, status: .valid) + mockSafeCoreCrypto.coreCryptoContext + .getUserIdentitiesConversationIdUserIds_MockMethod = { [clientIDs] _, userIDs in + [ + userIDs[0]: [ + .with(clientID: clientIDs![2].rawValue, status: .valid), + .with(clientID: clientIDs![3].rawValue, status: .valid) + ] ] - ] - } + } // When let isCertified = try await sut.invoke( @@ -224,14 +228,15 @@ final class IsUserE2EICertifiedUseCaseTests: ZMBaseManagedObjectTest { func testPassingSelfConversationFromViewContext() async throws { // Given setupMLSSelfConversations(in: uiMOC) - mockSafeCoreCrypto.coreCrypto.getUserIdentitiesConversationIdUserIds_MockMethod = { [clientIDs] _, userIDs in - [ - userIDs[0]: [ - .with(clientID: clientIDs![0].rawValue, status: .valid), - .with(clientID: clientIDs![1].rawValue, status: .valid) + mockSafeCoreCrypto.coreCryptoContext + .getUserIdentitiesConversationIdUserIds_MockMethod = { [clientIDs] _, userIDs in + [ + userIDs[0]: [ + .with(clientID: clientIDs![0].rawValue, status: .valid), + .with(clientID: clientIDs![1].rawValue, status: .valid) + ] ] - ] - } + } // When let isCertified = try await sut.invoke( @@ -247,15 +252,16 @@ final class IsUserE2EICertifiedUseCaseTests: ZMBaseManagedObjectTest { // Given setupUsersAndClients(in: uiMOC) setupClientIDs(in: uiMOC) - mockSafeCoreCrypto.coreCrypto.getClientIdsConversationId_MockValue = clientIDs.compactMap(\.data) - mockSafeCoreCrypto.coreCrypto.getUserIdentitiesConversationIdUserIds_MockMethod = { [clientIDs] _, userIDs in - [ - userIDs[0]: [ - .with(clientID: clientIDs![0].rawValue, status: .valid), - .with(clientID: clientIDs![1].rawValue, status: .valid) + mockSafeCoreCrypto.coreCryptoContext.getClientIdsConversationId_MockValue = clientIDs.compactMap(\.data) + mockSafeCoreCrypto.coreCryptoContext + .getUserIdentitiesConversationIdUserIds_MockMethod = { [clientIDs] _, userIDs in + [ + userIDs[0]: [ + .with(clientID: clientIDs![0].rawValue, status: .valid), + .with(clientID: clientIDs![1].rawValue, status: .valid) + ] ] - ] - } + } // When let isCertified = try await sut.invoke( @@ -270,14 +276,15 @@ final class IsUserE2EICertifiedUseCaseTests: ZMBaseManagedObjectTest { func testPassingOneOnOneConversationFromViewContext() async throws { // Given setupMLSSelfConversations(in: uiMOC) - mockSafeCoreCrypto.coreCrypto.getUserIdentitiesConversationIdUserIds_MockMethod = { [clientIDs] _, userIDs in - [ - userIDs[0]: [ - .with(clientID: clientIDs![0].rawValue, status: .valid), - .with(clientID: clientIDs![1].rawValue, status: .valid) + mockSafeCoreCrypto.coreCryptoContext + .getUserIdentitiesConversationIdUserIds_MockMethod = { [clientIDs] _, userIDs in + [ + userIDs[0]: [ + .with(clientID: clientIDs![0].rawValue, status: .valid), + .with(clientID: clientIDs![1].rawValue, status: .valid) + ] ] - ] - } + } // When let isCertified = try await sut.invoke( @@ -293,15 +300,16 @@ final class IsUserE2EICertifiedUseCaseTests: ZMBaseManagedObjectTest { // Given setupUsersAndClients(in: uiMOC) setupClientIDs(in: uiMOC) - mockSafeCoreCrypto.coreCrypto.getClientIdsConversationId_MockValue = clientIDs.compactMap(\.data) - mockSafeCoreCrypto.coreCrypto.getUserIdentitiesConversationIdUserIds_MockMethod = { [clientIDs] _, userIDs in - [ - userIDs[0]: [ - .with(clientID: clientIDs![2].rawValue, status: .valid), - .with(clientID: clientIDs![3].rawValue, status: .valid) + mockSafeCoreCrypto.coreCryptoContext.getClientIdsConversationId_MockValue = clientIDs.compactMap(\.data) + mockSafeCoreCrypto.coreCryptoContext + .getUserIdentitiesConversationIdUserIds_MockMethod = { [clientIDs] _, userIDs in + [ + userIDs[0]: [ + .with(clientID: clientIDs![2].rawValue, status: .valid), + .with(clientID: clientIDs![3].rawValue, status: .valid) + ] ] - ] - } + } // When let isCertified = try await sut.invoke( diff --git a/wire-ios-data-model/WireDataModel.xcodeproj/project.pbxproj b/wire-ios-data-model/WireDataModel.xcodeproj/project.pbxproj index 2b670f022b3..80a256a7928 100644 --- a/wire-ios-data-model/WireDataModel.xcodeproj/project.pbxproj +++ b/wire-ios-data-model/WireDataModel.xcodeproj/project.pbxproj @@ -22,12 +22,10 @@ 01482E8B2B10EEB000F3B2CB /* FetchSubgroupAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63F0780C29F292770031E19D /* FetchSubgroupAction.swift */; }; 014DD8D42B6D1FF6007ECFD1 /* UUID+SafeLogging.swift in Sources */ = {isa = PBXBuildFile; fileRef = 014DD8D22B6D1FE9007ECFD1 /* UUID+SafeLogging.swift */; }; 0153CA932B8554EC000000CA /* store2-113-0.wiredatabase in Resources */ = {isa = PBXBuildFile; fileRef = 0153CA8F2B855456000000CA /* store2-113-0.wiredatabase */; }; - 0156100F2D6DFA5000D5AA33 /* FakeCoreCryptoContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0156100E2D6DFA5000D5AA33 /* FakeCoreCryptoContext.swift */; }; 01586D422CAED03900C3BCE1 /* FixDuplicateOneOnOneConversationsAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01586D402CAED03900C3BCE1 /* FixDuplicateOneOnOneConversationsAction.swift */; }; 01586D442CAED2B200C3BCE1 /* store2-119-0.wiredatabase in Resources */ = {isa = PBXBuildFile; fileRef = 01586D432CAED2B200C3BCE1 /* store2-119-0.wiredatabase */; }; 0158DF212C594B3600C7BFFD /* ZMEventModel.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 0158DF1A2C594B1600C7BFFD /* ZMEventModel.xcdatamodeld */; }; 0158DF232C5A3C6700C7BFFD /* event_4.0.sqlite in Resources */ = {isa = PBXBuildFile; fileRef = 0158DF222C5A3C6600C7BFFD /* event_4.0.sqlite */; }; - 01639A882D6CC32300F2E868 /* TransactionExecutor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01639A872D6CC32300F2E868 /* TransactionExecutor.swift */; }; 016D293A2C10FB2F00DB969A /* ZMMessageTimer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 016D29392C10FB2F00DB969A /* ZMMessageTimer.swift */; }; 0176BDD12D515764002C33DE /* store2-122-0.wiredatabase in Resources */ = {isa = PBXBuildFile; fileRef = 0176BDD02D515764002C33DE /* store2-122-0.wiredatabase */; }; 017962982B83FC1400D6C7B6 /* DatabaseMigrationTests+UserUniqueness.swift in Sources */ = {isa = PBXBuildFile; fileRef = 017962952B83FC1400D6C7B6 /* DatabaseMigrationTests+UserUniqueness.swift */; }; @@ -162,6 +160,8 @@ 16519D36231D1BB200C9D76D /* ZMConversation+Deletion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 16519D35231D1BB200C9D76D /* ZMConversation+Deletion.swift */; }; 16519D54231D6F8200C9D76D /* ZMConversationTests+Deletion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 16519D53231D6F8200C9D76D /* ZMConversationTests+Deletion.swift */; }; 1651F9BE1D3554C800A9FAE8 /* ZMClientMessageTests+TextMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1651F9BD1D3554C800A9FAE8 /* ZMClientMessageTests+TextMessage.swift */; }; + 1657FA642D9BED6600A7B337 /* WireAPI in Frameworks */ = {isa = PBXBuildFile; productRef = 1657FA632D9BED6600A7B337 /* WireAPI */; }; + 1657FA9A2D9C2EB800A7B337 /* WireCoreCryptoUniffi in Frameworks */ = {isa = PBXBuildFile; productRef = 1657FA992D9C2EB800A7B337 /* WireCoreCryptoUniffi */; }; 165911551DF054AD007FA847 /* ZMConversation+Predicates.swift in Sources */ = {isa = PBXBuildFile; fileRef = 165911541DF054AD007FA847 /* ZMConversation+Predicates.swift */; }; 165D3A2D1E1D47AB0052E654 /* ZMCallState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 165D3A2B1E1D47AB0052E654 /* ZMCallState.swift */; }; 165DC51F21491C0400090B7B /* Mention.swift in Sources */ = {isa = PBXBuildFile; fileRef = 165DC51E21491C0400090B7B /* Mention.swift */; }; @@ -378,10 +378,6 @@ 6391A7FF2A6FDB9100832665 /* store2-107-0.wiredatabase in Resources */ = {isa = PBXBuildFile; fileRef = 6391A7FE2A6FDB9100832665 /* store2-107-0.wiredatabase */; }; 6393EE7F2BC961150068A6D1 /* UserType+QualifiedID.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6393EE7E2BC961150068A6D1 /* UserType+QualifiedID.swift */; }; 639971AA2B1E301E009DD5CF /* ReplaceSelfMLSKeyPackagesAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 639971A92B1E301E009DD5CF /* ReplaceSelfMLSKeyPackagesAction.swift */; }; - 639971AE2B2732E6009DD5CF /* CommitSender.swift in Sources */ = {isa = PBXBuildFile; fileRef = 639971AD2B2732E6009DD5CF /* CommitSender.swift */; }; - 639971B02B27346B009DD5CF /* CommitError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 639971AF2B27346B009DD5CF /* CommitError.swift */; }; - 639971B22B2734C0009DD5CF /* ExternalCommitError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 639971B12B2734C0009DD5CF /* ExternalCommitError.swift */; }; - 639971C22B28C5FF009DD5CF /* CommitSenderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 639971C02B28C5FA009DD5CF /* CommitSenderTests.swift */; }; 63AFE2D6244F49A90003F619 /* GenericMessage+MessageCapable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63AFE2D5244F49A90003F619 /* GenericMessage+MessageCapable.swift */; }; 63B1335429A503D100009D84 /* ProteusServiceInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63B1333729A503D000009D84 /* ProteusServiceInterface.swift */; }; 63B1335529A503D100009D84 /* ProteusService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63B1333829A503D000009D84 /* ProteusService.swift */; }; @@ -402,7 +398,6 @@ 63B1336829A503D100009D84 /* CountSelfMLSKeyPackagesAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63B1334D29A503D000009D84 /* CountSelfMLSKeyPackagesAction.swift */; }; 63B1336A29A503D100009D84 /* ClaimMLSKeyPackageAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63B1334F29A503D000009D84 /* ClaimMLSKeyPackageAction.swift */; }; 63B1336B29A503D100009D84 /* MLSGroupStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63B1335029A503D000009D84 /* MLSGroupStatus.swift */; }; - 63B1336C29A503D100009D84 /* CoreCryptoCallbacks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63B1335129A503D000009D84 /* CoreCryptoCallbacks.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 */; }; @@ -694,7 +689,6 @@ EEF0BC3128EEC02400ED16CA /* MockSyncStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEF0BC3028EEC02400ED16CA /* MockSyncStatus.swift */; }; EEF4010723A9213B007B1A97 /* UserType+Team.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEF4010623A9213B007B1A97 /* UserType+Team.swift */; }; EEF6E3CA28D89251001C1799 /* StaleMLSKeyDetectorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEF6E3C928D89251001C1799 /* StaleMLSKeyDetectorTests.swift */; }; - EEFAAC3528DDE2B1009940E7 /* CoreCryptoCallbacksTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEFAAC3328DDE27F009940E7 /* CoreCryptoCallbacksTests.swift */; }; EEFC3EE72208311200D3091A /* ZMConversation+HasMessages.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEFC3EE62208311200D3091A /* ZMConversation+HasMessages.swift */; }; EEFC3EE922083B0900D3091A /* ZMConversationTests+HasMessages.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEFC3EE822083B0900D3091A /* ZMConversationTests+HasMessages.swift */; }; EEFFBEB52A67D9CF0058C343 /* store2-106-0.wiredatabase in Resources */ = {isa = PBXBuildFile; fileRef = EEFFBEB42A67D9CF0058C343 /* store2-106-0.wiredatabase */; }; @@ -934,7 +928,6 @@ 014DD8D22B6D1FE9007ECFD1 /* UUID+SafeLogging.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UUID+SafeLogging.swift"; sourceTree = ""; }; 0153CA842B8540F0000000CA /* zmessaging2.114.0.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = zmessaging2.114.0.xcdatamodel; sourceTree = ""; }; 0153CA8F2B855456000000CA /* store2-113-0.wiredatabase */ = {isa = PBXFileReference; lastKnownFileType = file; path = "store2-113-0.wiredatabase"; sourceTree = ""; }; - 0156100E2D6DFA5000D5AA33 /* FakeCoreCryptoContext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FakeCoreCryptoContext.swift; sourceTree = ""; }; 01586D3F2CAECE8B00C3BCE1 /* zmessaging2.119.0.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = zmessaging2.119.0.xcdatamodel; sourceTree = ""; }; 01586D402CAED03900C3BCE1 /* FixDuplicateOneOnOneConversationsAction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FixDuplicateOneOnOneConversationsAction.swift; sourceTree = ""; }; 01586D432CAED2B200C3BCE1 /* store2-119-0.wiredatabase */ = {isa = PBXFileReference; lastKnownFileType = file; path = "store2-119-0.wiredatabase"; sourceTree = ""; }; @@ -945,7 +938,6 @@ 0158DF1F2C594B1600C7BFFD /* ZMEventModel3.0.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = ZMEventModel3.0.xcdatamodel; sourceTree = ""; }; 0158DF202C594B1600C7BFFD /* ZMEventModel4.0.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = ZMEventModel4.0.xcdatamodel; sourceTree = ""; }; 0158DF222C5A3C6600C7BFFD /* event_4.0.sqlite */ = {isa = PBXFileReference; lastKnownFileType = file; path = event_4.0.sqlite; sourceTree = ""; }; - 01639A872D6CC32300F2E868 /* TransactionExecutor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransactionExecutor.swift; sourceTree = ""; }; 016D29392C10FB2F00DB969A /* ZMMessageTimer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZMMessageTimer.swift; sourceTree = ""; }; 0176BDCF2D515224002C33DE /* zmessaging2.122.0.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = zmessaging2.122.0.xcdatamodel; sourceTree = ""; }; 0176BDD02D515764002C33DE /* store2-122-0.wiredatabase */ = {isa = PBXFileReference; lastKnownFileType = file; path = "store2-122-0.wiredatabase"; sourceTree = ""; }; @@ -1083,6 +1075,7 @@ 16519D35231D1BB200C9D76D /* ZMConversation+Deletion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ZMConversation+Deletion.swift"; sourceTree = ""; }; 16519D53231D6F8200C9D76D /* ZMConversationTests+Deletion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ZMConversationTests+Deletion.swift"; sourceTree = ""; }; 1651F9BD1D3554C800A9FAE8 /* ZMClientMessageTests+TextMessage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ZMClientMessageTests+TextMessage.swift"; sourceTree = ""; }; + 1657FA9B2D9C432200A7B337 /* CoreCryptoContextProtocolExt.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoreCryptoContextProtocolExt.swift; sourceTree = ""; }; 165911541DF054AD007FA847 /* ZMConversation+Predicates.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ZMConversation+Predicates.swift"; sourceTree = ""; }; 165D3A2B1E1D47AB0052E654 /* ZMCallState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ZMCallState.swift; sourceTree = ""; }; 165DC51E21491C0400090B7B /* Mention.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Mention.swift; sourceTree = ""; }; @@ -1221,7 +1214,6 @@ 5966D8312BD6AA8200305BBC /* UserPropertyNormalizer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserPropertyNormalizer.swift; sourceTree = ""; }; 5966D8332BD6ADCA00305BBC /* UserPropertyNormalizerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserPropertyNormalizerTests.swift; sourceTree = ""; }; 5966D8352BD6AF1700305BBC /* UserPropertyNormalizationResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserPropertyNormalizationResult.swift; sourceTree = ""; }; - 5972D9792B480E790089D1D3 /* CoreCryptoProtocolExt.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoreCryptoProtocolExt.swift; sourceTree = ""; }; 5979E8472D478DD20051080F /* store2-121-0.wiredatabase */ = {isa = PBXFileReference; lastKnownFileType = file; path = "store2-121-0.wiredatabase"; sourceTree = ""; }; 597A3BB32B6416CA0020E337 /* Availability.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Availability.swift; sourceTree = ""; }; 597A3BB62B6418170020E337 /* Availability+WireProtos.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Availability+WireProtos.swift"; sourceTree = ""; }; @@ -1294,10 +1286,6 @@ 6393EE7E2BC961150068A6D1 /* UserType+QualifiedID.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UserType+QualifiedID.swift"; sourceTree = ""; }; 6398545E2B346A0700AA10DE /* zmessaging2.112.0.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = zmessaging2.112.0.xcdatamodel; sourceTree = ""; }; 639971A92B1E301E009DD5CF /* ReplaceSelfMLSKeyPackagesAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReplaceSelfMLSKeyPackagesAction.swift; sourceTree = ""; }; - 639971AD2B2732E6009DD5CF /* CommitSender.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommitSender.swift; sourceTree = ""; }; - 639971AF2B27346B009DD5CF /* CommitError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommitError.swift; sourceTree = ""; }; - 639971B12B2734C0009DD5CF /* ExternalCommitError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExternalCommitError.swift; sourceTree = ""; }; - 639971C02B28C5FA009DD5CF /* CommitSenderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommitSenderTests.swift; sourceTree = ""; }; 63AFE2D5244F49A90003F619 /* GenericMessage+MessageCapable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "GenericMessage+MessageCapable.swift"; sourceTree = ""; }; 63B1333729A503D000009D84 /* ProteusServiceInterface.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProteusServiceInterface.swift; sourceTree = ""; }; 63B1333829A503D000009D84 /* ProteusService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProteusService.swift; sourceTree = ""; }; @@ -1318,7 +1306,6 @@ 63B1334D29A503D000009D84 /* CountSelfMLSKeyPackagesAction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CountSelfMLSKeyPackagesAction.swift; sourceTree = ""; }; 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 = ""; }; - 63B1335129A503D000009D84 /* CoreCryptoCallbacks.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CoreCryptoCallbacks.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 = ""; }; @@ -1644,7 +1631,6 @@ EEF0BC3028EEC02400ED16CA /* MockSyncStatus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockSyncStatus.swift; sourceTree = ""; }; EEF4010623A9213B007B1A97 /* UserType+Team.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UserType+Team.swift"; sourceTree = ""; }; EEF6E3C928D89251001C1799 /* StaleMLSKeyDetectorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StaleMLSKeyDetectorTests.swift; sourceTree = ""; }; - EEFAAC3328DDE27F009940E7 /* CoreCryptoCallbacksTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoreCryptoCallbacksTests.swift; sourceTree = ""; }; EEFC3EE62208311200D3091A /* ZMConversation+HasMessages.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ZMConversation+HasMessages.swift"; sourceTree = ""; }; EEFC3EE822083B0900D3091A /* ZMConversationTests+HasMessages.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ZMConversationTests+HasMessages.swift"; sourceTree = ""; }; EEFFBEB42A67D9CF0058C343 /* store2-106-0.wiredatabase */ = {isa = PBXFileReference; lastKnownFileType = file; path = "store2-106-0.wiredatabase"; sourceTree = ""; }; @@ -1912,8 +1898,10 @@ buildActionMask = 2147483647; files = ( 59D058402D15939800B687F2 /* WireAnalytics in Frameworks */, + 1657FA642D9BED6600A7B337 /* WireAPI in Frameworks */, EE8DA9672954A02B00F58B79 /* WireCryptobox.framework in Frameworks */, CBD35F2C2D09EBB20080DA37 /* WireCrypto in Frameworks */, + 1657FA9A2D9C2EB800A7B337 /* WireCoreCryptoUniffi in Frameworks */, 591B6E4C2C8B09C6009F8A7B /* CoreData.framework in Frameworks */, EE8DA96D2954A03800F58B79 /* WireLinkPreview.framework in Frameworks */, E6707E902BBD683F00469D57 /* PINCache in Frameworks */, @@ -2370,10 +2358,7 @@ 06EB77A02B29E09F00A64DD8 /* MLSVerificationStatus.swift */, E662328A2BF4911E002B680A /* MLSGroupVerification.swift */, 6326E4752AEBB936006EEA28 /* Migration */, - 639971AD2B2732E6009DD5CF /* CommitSender.swift */, 06F1E4BE2D19886000D0D07A /* MLSClientManager.swift */, - 639971B12B2734C0009DD5CF /* ExternalCommitError.swift */, - 639971AF2B27346B009DD5CF /* CommitError.swift */, 16058B292BDA9D27003C82C2 /* MLSCiphersuite.swift */, 16058B2B2BDA9D83003C82C2 /* MLSSignatureAlgorithm.swift */, ); @@ -2656,13 +2641,10 @@ isa = PBXGroup; children = ( 06D4B1AA2D1F0731004627EB /* MLSClientManagerTests.swift */, - 639971C02B28C5FA009DD5CF /* CommitSenderTests.swift */, - EEFAAC3328DDE27F009940E7 /* CoreCryptoCallbacksTests.swift */, 63C07014291144F70075D598 /* CoreCryptoConfigProviderTests.swift */, 6374562129C3323D001D1A33 /* CoreCryptoKeyProviderTests.swift */, 59AAB6482AFCF541005B39E6 /* MessageProtocolTests.swift */, EE84226F28EC353900B80FE5 /* MLSActionExecutorTests.swift */, - 0156100E2D6DFA5000D5AA33 /* FakeCoreCryptoContext.swift */, 63FACD55291BC598003AB25D /* MLSClientIDTests.swift */, EEC794F72A385359008E1A3B /* MLSDecryptionServiceTests.swift */, EEC794F92A38963F008E1A3B /* MLSEncryptionServiceTests.swift */, @@ -2709,8 +2691,6 @@ EEC737B22D01ADAA003C0FC9 /* Core Crypto */ = { isa = PBXGroup; children = ( - 01639A872D6CC32300F2E868 /* TransactionExecutor.swift */, - 63B1335129A503D000009D84 /* CoreCryptoCallbacks.swift */, 63B1334429A503D000009D84 /* CoreCryptoConfiguration.swift */, 63B1334629A503D000009D84 /* CoreCryptoKeyProvider.swift */, EEC737B32D01AECC003C0FC9 /* CoreCryptoLogger.swift */, @@ -3190,7 +3170,7 @@ 0129E7FA29A520EB0065E6DB /* SafeFileContext.swift */, 0614E96C2A863EED007BB1F6 /* NSPredicate+BaseCompounds.swift */, E6A5BBAD2B0E564200ACC236 /* WireDataModelBundle.swift */, - 5972D9792B480E790089D1D3 /* CoreCryptoProtocolExt.swift */, + 1657FA9B2D9C432200A7B337 /* CoreCryptoContextProtocolExt.swift */, E66029D12BDA43E10033C524 /* SearchUsersCache.swift */, ); path = Utilis; @@ -3870,6 +3850,8 @@ CBD35F2B2D09EBB20080DA37 /* WireCrypto */, 59D0583F2D15939800B687F2 /* WireAnalytics */, 01D2B7A22D64A8F50030D28D /* WireCoreCrypto */, + 1657FA632D9BED6600A7B337 /* WireAPI */, + 1657FA992D9C2EB800A7B337 /* WireCoreCryptoUniffi */, ); productName = WireDataModel; productReference = F9C9A4FC1CAD5DF10039E10C /* WireDataModel.framework */; @@ -4151,7 +4133,6 @@ EE28991E26B4422800E7BAF0 /* Feature.ConferenceCalling.swift in Sources */, 63B1336A29A503D100009D84 /* ClaimMLSKeyPackageAction.swift in Sources */, 01BDA3192C59210400675DCB /* CoreDataEventsMigrationVersion.swift in Sources */, - 639971AE2B2732E6009DD5CF /* CommitSender.swift in Sources */, 6308F8A62A273CB70072A177 /* FetchMLSConversationGroupInfoAction.swift in Sources */, E999AC402BC9467E00569E79 /* CreateConversationGuestLinkAction.swift in Sources */, 63B1335929A503D100009D84 /* MLSGroupID.swift in Sources */, @@ -4188,7 +4169,6 @@ F929C1751E41EBE20018ADA4 /* PersonName.swift in Sources */, 591F8A022B8CB4DE00D562A6 /* IsSelfUserE2EICertifiedUseCaseProtocol.swift in Sources */, 16BA4303233CD8E50018E883 /* Label.swift in Sources */, - 63B1336C29A503D100009D84 /* CoreCryptoCallbacks.swift in Sources */, EE42938C252C443000E70670 /* ManagedObjectObserverToken.swift in Sources */, 161E056A2667C4D100DADC3D /* AccountDeletedObserver.swift in Sources */, F9A706B61CAEE01D00C2F5FE /* UserClientChangeInfo.swift in Sources */, @@ -4377,7 +4357,6 @@ 165DC523214A614100090B7B /* ZMConversation+Message.swift in Sources */, 5980C7012BE4E17100278363 /* E2EINotifications.swift in Sources */, F9A706811CAEE01D00C2F5FE /* ZMMessage.m in Sources */, - 639971B02B27346B009DD5CF /* CommitError.swift in Sources */, 631A0578240420380062B387 /* UserClient+SafeLogging.swift in Sources */, 165DC52121491D8700090B7B /* ZMClientMessage+TextMessageData.swift in Sources */, EEE95CD52A432FA100E136CB /* LeaveSubconversationAction.swift in Sources */, @@ -4387,7 +4366,6 @@ EE8B09AD25B86AB10057E85C /* AppLockError.swift in Sources */, 63C2EABD2A93B174008A0AB7 /* AddParticipantAction.swift in Sources */, CB181C7E2D3F8D8400A80AB4 /* OneOnOneSource.swift in Sources */, - 01639A882D6CC32300F2E868 /* TransactionExecutor.swift in Sources */, 1687ABAE20ECD51E0007C240 /* ZMSearchUser.swift in Sources */, EEF09CA02B1DB0C600D729A1 /* OneOnOneProtocolSelector.swift in Sources */, 163C92AA2630A80400F8DC14 /* NSManagedObjectContext+SelfUser.swift in Sources */, @@ -4413,7 +4391,6 @@ E6E504392BC542C5004948E7 /* EARKeyRepositoryFailure.swift in Sources */, 5E9EA4E22243E0D300D401B2 /* ConversationMessage+Attachments.swift in Sources */, EE5F54CC259B22C400F11F3C /* Account+Keychain.swift in Sources */, - 639971B22B2734C0009DD5CF /* ExternalCommitError.swift in Sources */, 639971AA2B1E301E009DD5CF /* ReplaceSelfMLSKeyPackagesAction.swift in Sources */, F93C4C7D1E24E1B1007E9CEE /* NotificationDispatcher.swift in Sources */, 06D48735241F930A00881B08 /* GenericMessage+Obfuscation.swift in Sources */, @@ -4755,7 +4732,6 @@ EE46B92828A511630063B38D /* ZMClientMessageTests+MLSEncryptedPayloadGenerator.swift in Sources */, 017962982B83FC1400D6C7B6 /* DatabaseMigrationTests+UserUniqueness.swift in Sources */, 4058AAA82AAB65530013DE71 /* ReactionsSortingTests.swift in Sources */, - 639971C22B28C5FF009DD5CF /* CommitSenderTests.swift in Sources */, F9A7083C1CAEEB7500C2F5FE /* MockModelObjectContextFactory.m in Sources */, F9331C5C1CB3BF9F00139ECC /* UserClientKeyStoreTests.swift in Sources */, 87C1C261207F812F0083BF6B /* InvalidGenericMessageDataRemovalTests.swift in Sources */, @@ -4798,7 +4774,6 @@ 598796302B45880400A6FC63 /* ZMUpdateEvent+allTypes.swift in Sources */, 164A55D320F3AF6700AE62A6 /* ZMSearchUserTests+ProfileImages.swift in Sources */, EE74E4E02A37B3BF00B63E6E /* SubconversationGroupIDRepositoryTests.swift in Sources */, - 0156100F2D6DFA5000D5AA33 /* FakeCoreCryptoContext.swift in Sources */, F9B71FA01CB2BF2B001DB03F /* ZMConversationListTests.m in Sources */, BF794FE61D1442B100E618C6 /* ZMClientMessageTests+Location.swift in Sources */, CEB15E531D7EE5AB0048A011 /* ZMClientMessagesTests+Reaction.swift in Sources */, @@ -4856,7 +4831,6 @@ 06D4B1AB2D1F0736004627EB /* MLSClientManagerTests.swift in Sources */, 1672A6162344A14E00380537 /* LabelObserverTests.swift in Sources */, BF1B980B1EC31D6100DE033B /* TeamDeletionRuleTests.swift in Sources */, - EEFAAC3528DDE2B1009940E7 /* CoreCryptoCallbacksTests.swift in Sources */, BF735CFD1E7050D5003BC61F /* ZMConversationMissedCallSystemMessageTests.swift in Sources */, E6BFE8382B3320C7000F0FBE /* DatabaseMigrationHelper.swift in Sources */, 6388054A240EA8990043B641 /* ZMClientMessageTests+Composite.swift in Sources */, @@ -5242,6 +5216,14 @@ isa = XCSwiftPackageProductDependency; productName = WireCoreCrypto; }; + 1657FA632D9BED6600A7B337 /* WireAPI */ = { + isa = XCSwiftPackageProductDependency; + productName = WireAPI; + }; + 1657FA992D9C2EB800A7B337 /* WireCoreCryptoUniffi */ = { + isa = XCSwiftPackageProductDependency; + productName = WireCoreCryptoUniffi; + }; 5902AC742DA92365000A8F7F /* WireFoundationSupport */ = { isa = XCSwiftPackageProductDependency; productName = WireFoundationSupport; diff --git a/wire-ios-notification-engine/Sources/NotificationSession.swift b/wire-ios-notification-engine/Sources/NotificationSession.swift index 79860b70768..d3c9feeb2dc 100644 --- a/wire-ios-notification-engine/Sources/NotificationSession.swift +++ b/wire-ios-notification-engine/Sources/NotificationSession.swift @@ -246,14 +246,9 @@ public final class NotificationSession { cryptoboxMigrationManager: cryptoboxMigrationManager, allowCreation: false ) - let commitSender = CommitSender( - coreCryptoProvider: coreCryptoProvider, - notificationContext: coreDataStack.syncContext.notificationContext - ) let featureRepository = FeatureRepository(context: coreDataStack.syncContext) let mlsActionExecutor = MLSActionExecutor( coreCryptoProvider: coreCryptoProvider, - commitSender: commitSender, featureRepository: featureRepository ) diff --git a/wire-ios-request-strategy/Sources/E2EIdentity/E2EIKeyPackageRotator.swift b/wire-ios-request-strategy/Sources/E2EIdentity/E2EIKeyPackageRotator.swift index e793fafb18b..104ffcee8a2 100644 --- a/wire-ios-request-strategy/Sources/E2EIdentity/E2EIKeyPackageRotator.swift +++ b/wire-ios-request-strategy/Sources/E2EIdentity/E2EIKeyPackageRotator.swift @@ -46,9 +46,7 @@ public class E2EIKeyPackageRotator: E2EIKeyPackageRotating { // MARK: - Properties private let coreCryptoProvider: CoreCryptoProviderProtocol - private let conversationEventProcessor: ConversationEventProcessorProtocol private let context: NSManagedObjectContext - private let commitSender: CommitSending private let newKeyPackageCount: UInt32 = 100 private let featureRepository: FeatureRepositoryInterface private let onNewCRLsDistributionPointsSubject: PassthroughSubject @@ -63,20 +61,13 @@ public class E2EIKeyPackageRotator: E2EIKeyPackageRotating { public init( coreCryptoProvider: CoreCryptoProviderProtocol, - conversationEventProcessor: ConversationEventProcessorProtocol, context: NSManagedObjectContext, onNewCRLsDistributionPointsSubject: PassthroughSubject, - commitSender: CommitSending? = nil, featureRepository: FeatureRepositoryInterface ) { self.coreCryptoProvider = coreCryptoProvider - self.conversationEventProcessor = conversationEventProcessor self.context = context self.onNewCRLsDistributionPointsSubject = onNewCRLsDistributionPointsSubject - self.commitSender = commitSender ?? CommitSender( - coreCryptoProvider: coreCryptoProvider, - notificationContext: context.notificationContext - ) self.featureRepository = featureRepository } @@ -94,41 +85,54 @@ public class E2EIKeyPackageRotator: E2EIKeyPackageRotating { throw Error.invalidIdentity } - // Get the rotate bundle from core crypto - let rotateBundle = try await coreCrypto.perform { - try await $0.e2eiRotateAll( + let crlNewDistributionPoints = try await coreCrypto.perform { context in + try await context.saveX509Credential( enrollment: enrollment, - certificateChain: certificateChain, - newKeyPackagesCount: newKeyPackageCount + certificateChain: certificateChain ) } - guard !rotateBundle.commits.isEmpty else { - // TODO: [WPB-6281] [jacob] remove this guard when implementing - return + try await replaceKeyPackages() + try await replaceCredentialsInExistingConversations() + + // Publish new certificate revocation lists (CRLs) distribution points + if let newDistributionPoints = CRLsDistributionPoints(from: crlNewDistributionPoints) { + onNewCRLsDistributionPointsSubject.send(newDistributionPoints) } + } + + // MARK: - Helpers - // Replace the key packages with the ones including the certificate - try await replaceKeyPackages(rotateBundle: rotateBundle) + private func replaceCredentialsInExistingConversations() async throws { + let mlsConversationsToMigrate = try await context.perform { + var mlsGroupIDs = try ZMConversation.fetchConversationsWithMLSGroupStatus( + mlsGroupStatus: .ready, + in: self.context + ).compactMap(\.mlsGroupID) - // Send migration commits after key packages rotations - for (groupID, commit) in rotateBundle.commits { - do { - try await migrateConversation(with: groupID, commit: commit) - } catch { - WireLogger.e2ei.warn("failed to rotate keys for group: \(String(describing: error))") + if let selfMLSGroupID = ZMConversation.fetchSelfMLSConversation(in: self.context)?.mlsGroupID { + mlsGroupIDs.append(selfMLSGroupID) } + + return mlsGroupIDs } - // Publish new certificate revocation lists (CRLs) distribution points - if let newDistributionPoints = CRLsDistributionPoints(from: rotateBundle.crlNewDistributionPoints) { - onNewCRLsDistributionPointsSubject.send(newDistributionPoints) + try await coreCrypto.perform { context in + for groupID in mlsConversationsToMigrate { + do { + try await context.e2eiRotate(conversationId: groupID.data) + } catch { + WireLogger.e2ei + .warn( + "failed to rotate keys for group \(groupID.safeForLoggingDescription): \(String(describing: error))" + ) + } + } } } - // MARK: - Helpers - - private func replaceKeyPackages(rotateBundle: RotateBundle) async throws { + private func replaceKeyPackages() async throws { + let mlsConfig = await featureRepository.fetchMLS().config guard let clientID = await context.perform({ [self] in ZMUser.selfUser(in: context).selfClient()?.remoteIdentifier @@ -136,31 +140,28 @@ public class E2EIKeyPackageRotator: E2EIKeyPackageRotating { throw Error.noSelfClient } - let newKeyPackages = rotateBundle.newKeyPackages.map { $0.base64String() } - let mlsConfig = await featureRepository.fetchMLS().config guard let ciphersuite = MLSCipherSuite(rawValue: mlsConfig.defaultCipherSuite.rawValue) else { throw Error.invalidCiphersuite } - var action = ReplaceSelfMLSKeyPackagesAction( - clientID: clientID, - keyPackages: newKeyPackages, - ciphersuite: ciphersuite - ) - try await action.perform(in: context.notificationContext) - } - private func migrateConversation(with groupID: String, commit: CommitBundle) async throws { - guard let groupData = groupID.zmHexDecodedData() else { - throw Error.invalidGroupID + try await coreCrypto.perform { coreCryptoContext in + let rawCiphersuite = UInt16(ciphersuite.rawValue) + let newKeyPackages = try await coreCryptoContext.clientKeypackages( + ciphersuite: rawCiphersuite, + credentialType: .x509, + amountRequested: self.newKeyPackageCount + ).map { $0.base64String() } + + var action = ReplaceSelfMLSKeyPackagesAction( + clientID: clientID, + keyPackages: newKeyPackages, + ciphersuite: ciphersuite + ) + try await action.perform(in: self.context.notificationContext) + try await coreCryptoContext.deleteStaleKeyPackages( + ciphersuite: rawCiphersuite + ) } - - let groupID = MLSGroupID(groupData) - let events = try await commitSender.sendCommitBundle( - commit, - for: groupID - ) - - await conversationEventProcessor.processConversationEvents(events) } } diff --git a/wire-ios-request-strategy/Sources/E2EIdentity/E2EIKeyPackageRotatorTests.swift b/wire-ios-request-strategy/Sources/E2EIdentity/E2EIKeyPackageRotatorTests.swift index ccd5ef2a7b5..af5e034b1f4 100644 --- a/wire-ios-request-strategy/Sources/E2EIdentity/E2EIKeyPackageRotatorTests.swift +++ b/wire-ios-request-strategy/Sources/E2EIdentity/E2EIKeyPackageRotatorTests.swift @@ -27,8 +27,6 @@ class E2EIKeyPackageRotatorTests: MessagingTestBase { private var mockCoreCrypto: MockCoreCryptoProtocol! private var mockCoreCryptoProvider: MockCoreCryptoProviderProtocol! - private var mockCommitSender: MockCommitSending! - private var mockConversationEventProcessor: MockConversationEventProcessorProtocol! private var mockFeatureRepository: MockFeatureRepositoryInterface! private var sut: E2EIKeyPackageRotator! @@ -36,18 +34,14 @@ class E2EIKeyPackageRotatorTests: MessagingTestBase { super.setUp() mockCoreCrypto = MockCoreCryptoProtocol() - mockCommitSender = MockCommitSending() - mockConversationEventProcessor = MockConversationEventProcessorProtocol() mockCoreCryptoProvider = MockCoreCryptoProviderProtocol() mockCoreCryptoProvider.coreCrypto_MockValue = MockSafeCoreCrypto(coreCrypto: mockCoreCrypto) mockFeatureRepository = .init() sut = E2EIKeyPackageRotator( coreCryptoProvider: mockCoreCryptoProvider, - conversationEventProcessor: mockConversationEventProcessor, context: syncMOC, onNewCRLsDistributionPointsSubject: .init(), - commitSender: mockCommitSender, featureRepository: mockFeatureRepository ) } @@ -55,7 +49,6 @@ class E2EIKeyPackageRotatorTests: MessagingTestBase { override func tearDown() { mockCoreCrypto = nil mockCoreCryptoProvider = nil - mockConversationEventProcessor = nil sut = nil super.tearDown() diff --git a/wire-ios-request-strategy/Sources/Request Strategies/Conversation/Actions/AddParticipantActionHandler.swift b/wire-ios-request-strategy/Sources/Request Strategies/Conversation/Actions/AddParticipantActionHandler.swift index d40cfeeee1b..595d7285519 100644 --- a/wire-ios-request-strategy/Sources/Request Strategies/Conversation/Actions/AddParticipantActionHandler.swift +++ b/wire-ios-request-strategy/Sources/Request Strategies/Conversation/Actions/AddParticipantActionHandler.swift @@ -39,7 +39,7 @@ class AddParticipantActionHandler: ActionHandler { let decoder: JSONDecoder = .defaultDecoder - private let eventProcessor: ConversationEventProcessorProtocol + private let eventProcessor: LegacyConversationEventProcessorProtocol override convenience init(context: NSManagedObjectContext) { self.init( @@ -50,7 +50,7 @@ class AddParticipantActionHandler: ActionHandler { init( context: NSManagedObjectContext, - eventProcessor: ConversationEventProcessorProtocol + eventProcessor: LegacyConversationEventProcessorProtocol ) { self.eventProcessor = eventProcessor super.init(context: context) diff --git a/wire-ios-request-strategy/Sources/Request Strategies/Conversation/ConversationEventProcessor.swift b/wire-ios-request-strategy/Sources/Request Strategies/Conversation/ConversationEventProcessor.swift index 08b6e7f39c0..305bb07ae8f 100644 --- a/wire-ios-request-strategy/Sources/Request Strategies/Conversation/ConversationEventProcessor.swift +++ b/wire-ios-request-strategy/Sources/Request Strategies/Conversation/ConversationEventProcessor.swift @@ -20,7 +20,7 @@ import Foundation import WireDataModel import WireLogging -public class ConversationEventProcessor: NSObject, ConversationEventProcessorProtocol, ZMEventAsyncConsumer { +public class ConversationEventProcessor: NSObject, LegacyConversationEventProcessorProtocol, ZMEventAsyncConsumer { // MARK: - Properties diff --git a/wire-ios-request-strategy/Sources/Synchronization/Decoding/EventDecoderTest.swift b/wire-ios-request-strategy/Sources/Synchronization/Decoding/EventDecoderTest.swift index 12fda858407..1a5358d2077 100644 --- a/wire-ios-request-strategy/Sources/Synchronization/Decoding/EventDecoderTest.swift +++ b/wire-ios-request-strategy/Sources/Synchronization/Decoding/EventDecoderTest.swift @@ -30,6 +30,8 @@ public enum EventConversation { static let addOTRAsset = "conversation.otr-asset-add" } +struct DummyError: Error {} + class EventDecoderTest: MessagingTestBase { var sut: EventDecoder! @@ -521,13 +523,14 @@ extension EventDecoderTest { DeveloperFlag.proteusViaCoreCrypto.enable(false, storage: .standard) } let mockProteusService = MockProteusServiceInterface() + let decryptionErrorReason = DummyError() mockProteusService.decryptDataForSession_MockMethod = { data, _ in (didCreateNewSession: false, decryptedData: data) } mockMLSService.decryptMessageForSubconversationType_MockMethod = { _, _, _ in - throw MLSDecryptionService.MLSMessageDecryptionError.failedToDecryptMessage + throw MLSDecryptionService.MLSMessageDecryptionError.failedToDecryptMessage(reason: decryptionErrorReason) } let mlsEvent: ZMUpdateEvent = await syncMOC.perform { [self] in @@ -546,7 +549,7 @@ extension EventDecoderTest { _ = try await sut.decryptAndStoreEvents([mlsEvent]) } catch let error as MLSDecryptionService.MLSMessageDecryptionError { // Then - XCTAssert(error == .failedToDecryptMessage) + XCTAssert(error == .failedToDecryptMessage(reason: decryptionErrorReason)) XCTAssertEqual(lastEventIDRepository.storeLastEventID_Invocations.count, 0) } @@ -839,8 +842,9 @@ extension EventDecoderTest { func test_DecryptMLSMessage_ReturnsNoEvent_WhenmlsServiceThrows() async throws { // Given + let decryptionErrorReason = DummyError() mockMLSService.decryptMessageForSubconversationType_MockMethod = { _, _, _ in - throw MLSDecryptionService.MLSMessageDecryptionError.failedToDecryptMessage + throw MLSDecryptionService.MLSMessageDecryptionError.failedToDecryptMessage(reason: decryptionErrorReason) } let event = await syncMOC.perform { [self] in @@ -855,7 +859,7 @@ extension EventDecoderTest { _ = try await sut.decryptMlsMessage(from: event, context: syncMOC) } catch let error as MLSDecryptionService.MLSMessageDecryptionError { // Then - XCTAssert(error == .failedToDecryptMessage) + XCTAssert(error == .failedToDecryptMessage(reason: decryptionErrorReason)) } } diff --git a/wire-ios-share-engine/Sources/SharingSession.swift b/wire-ios-share-engine/Sources/SharingSession.swift index a2bf9872dcc..9f24df8bcbc 100644 --- a/wire-ios-share-engine/Sources/SharingSession.swift +++ b/wire-ios-share-engine/Sources/SharingSession.swift @@ -17,7 +17,9 @@ // import Foundation +import WireAPI import WireDataModel +import WireDomain import WireLinkPreview import WireRequestStrategy import WireTransport @@ -252,7 +254,7 @@ public final class SharingSession { applicationGroupIdentifier: String, accountIdentifier: UUID, hostBundleIdentifier: String, - environment: BackendEnvironmentProvider, + environment: WireTransport.BackendEnvironment, appLockConfig: AppLockController.LegacyConfig?, sharedUserDefaults: UserDefaults, minTLSVersion: String? @@ -313,8 +315,56 @@ public final class SharingSession { isSyncV2Enabled: false ) + let proxySettings: WireAPI.ProxySettings? = { + guard let proxy = environment.proxy else { return nil } + + if proxy.needsAuthentication { + guard + let proxyUsername = credentials?.username, + let proxyPassword = credentials?.password else { + fatalInternal("Proxy needs authentication but credentials are missing") + return nil + } + + return .authenticated( + host: proxy.host, + port: proxy.port, + username: proxyUsername, + password: proxyPassword + ) + } else { + return .unauthenticated(host: proxy.host, port: proxy.port) + } + }() + + let wireAPIBackendEnvironment = BackendEnvironment( + url: environment.backendURL, + webSocketURL: environment.backendWSURL, + pinnedKeys: environment.trustData.map { trustData in + PinnedKey( + key: trustData.certificateKey, + hosts: trustData.hosts.map { host in + switch host.rule { + case .equals: + .equals(host.value) + case .endsWith: + .endsWith(host.value) + } + } + ) + }, + proxySettings: proxySettings + ) + + guard let apiVersion = BackendInfo.apiVersion, + let wireAPIVersion = WireAPI.APIVersion(rawValue: UInt(apiVersion.rawValue)) else { + fatal("cannot resolve api version") + + } + try self.init( accountIdentifier: accountIdentifier, + selfClientID: selfClientID!, coreDataStack: coreDataStack, transportSession: transportSession, cachesDirectory: FileManager.default.cachesURLForAccount(with: accountIdentifier, in: sharedContainerURL), @@ -323,6 +373,9 @@ public final class SharingSession { applicationContainer: sharedContainerURL ), appLockConfig: appLockConfig, + wireAPIBackendEnvironment: wireAPIBackendEnvironment, + minTLSVersion: .minVersionFrom(minTLSVersion), + apiVersion: wireAPIVersion, sharedUserDefaults: sharedUserDefaults ) } @@ -397,11 +450,15 @@ public final class SharingSession { public convenience init( accountIdentifier: UUID, + selfClientID: String, coreDataStack: CoreDataStack, transportSession: ZMTransportSession, cachesDirectory: URL, accountContainer: URL, appLockConfig: AppLockController.LegacyConfig?, + wireAPIBackendEnvironment: WireAPI.BackendEnvironment, + minTLSVersion: WireAPI.TLSVersion, + apiVersion: WireAPI.APIVersion, sharedUserDefaults: UserDefaults ) throws { @@ -443,14 +500,9 @@ public final class SharingSession { cryptoboxMigrationManager: cryptoboxMigrationManager, allowCreation: false ) - let commitSender = CommitSender( - coreCryptoProvider: coreCryptoProvider, - notificationContext: coreDataStack.syncContext.notificationContext - ) let featureRepository = FeatureRepository(context: coreDataStack.syncContext) let mlsActionExecutor = MLSActionExecutor( coreCryptoProvider: coreCryptoProvider, - commitSender: commitSender, featureRepository: featureRepository ) let contextStorage = LAContextStorage() @@ -473,12 +525,40 @@ public final class SharingSession { context: coreDataStack.syncContext, notificationContext: coreDataStack.syncContext.notificationContext, coreCryptoProvider: coreCryptoProvider, - conversationEventProcessor: ConversationEventProcessor(context: coreDataStack.syncContext), featureRepository: FeatureRepository(context: coreDataStack.syncContext), userDefaults: .standard, userID: coreDataStack.account.userIdentifier ) + let userSessionComponent = UserSessionComponent( + selfUserID: accountIdentifier, + backendEnvironment: wireAPIBackendEnvironment, + minTLSVersion: minTLSVersion, + apiVersion: apiVersion, + localDomain: WireTransport.BackendInfo.domain!, + isFederationEnabled: WireTransport.BackendInfo.isFederationEnabled, + isMLSEnabled: WireTransport.BackendInfo.isMLSEnabled, + sharedUserDefaults: sharedUserDefaults, + syncContext: coreDataStack.syncContext, + eventContext: coreDataStack.eventContext, + mlsService: mlsService, + mlsDecryptionService: mlsService, + proteusService: proteusService + ) + + let processHandlers = ClientSessionComponent.ProcessorHandlers( + onProcessedCallEvent: { _ in }, + onSelfClientInvalidated: {}, + onProcessedTypingUsers: { _ in } + ) + + let clientUserSessionComponent = userSessionComponent.clientSessionComponent( + clientID: selfClientID, + processorHandlers: processHandlers + ) + + coreCryptoProvider.registerMlsTransport(clientUserSessionComponent.mlsTransport) + try self.init( accountIdentifier: accountIdentifier, coreDataStack: coreDataStack, @@ -564,7 +644,7 @@ extension SharingSession: LinkPreviewDetectorType { // MARK: - Helper -private extension ConversationList { +private extension WireDataModel.ConversationList { var writeableConversations: [Conversation] { items.filter { !$0.isReadOnly } diff --git a/wire-ios-share-engine/WireShareEngine.xcodeproj/project.pbxproj b/wire-ios-share-engine/WireShareEngine.xcodeproj/project.pbxproj index cf06c3c4c52..9fc9c2f86b3 100644 --- a/wire-ios-share-engine/WireShareEngine.xcodeproj/project.pbxproj +++ b/wire-ios-share-engine/WireShareEngine.xcodeproj/project.pbxproj @@ -7,6 +7,8 @@ objects = { /* Begin PBXBuildFile section */ + 1606AD122DC5136000C95115 /* WireDomain.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1606AD112DC5136000C95115 /* WireDomain.framework */; }; + 1606AD162DC5136F00C95115 /* WireAPI in Frameworks */ = {isa = PBXBuildFile; productRef = 1606AD152DC5136F00C95115 /* WireAPI */; }; 165C55F62551AF1300731CA9 /* SharingSession+EncryptionAtRest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 165C55F52551AF1300731CA9 /* SharingSession+EncryptionAtRest.swift */; }; 166DCD9E2552969B004F4F59 /* SharingSessionTests+EncryptionAtRest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 166DCD9D2552969B004F4F59 /* SharingSessionTests+EncryptionAtRest.swift */; }; 5470D3F31D76E1B000FDE440 /* WireShareEngine.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5470D3E81D76E1B000FDE440 /* WireShareEngine.framework */; }; @@ -53,6 +55,7 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 1606AD112DC5136000C95115 /* WireDomain.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = WireDomain.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 165C55F52551AF1300731CA9 /* SharingSession+EncryptionAtRest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SharingSession+EncryptionAtRest.swift"; sourceTree = ""; }; 166DCD9D2552969B004F4F59 /* SharingSessionTests+EncryptionAtRest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SharingSessionTests+EncryptionAtRest.swift"; sourceTree = ""; }; 5470D3E81D76E1B000FDE440 /* WireShareEngine.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = WireShareEngine.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -113,6 +116,8 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 1606AD162DC5136F00C95115 /* WireAPI in Frameworks */, + 1606AD122DC5136000C95115 /* WireDomain.framework in Frameworks */, 591B6E322C8B098C009F8A7B /* WireRequestStrategy.framework in Frameworks */, 59537D8B2CFF9F8F00920B59 /* WireLogging in Frameworks */, ); @@ -217,6 +222,7 @@ 5470D4331D77165E00FDE440 /* Frameworks */ = { isa = PBXGroup; children = ( + 1606AD112DC5136000C95115 /* WireDomain.framework */, E6E504512BC58ACD004948E7 /* WireDataModelSupport.framework */, EE67F6F8296F094C001D7C88 /* WireMockTransport.framework */, EE668BC12954B82900D939E7 /* WireRequestStrategy.framework */, @@ -596,6 +602,10 @@ /* End XCConfigurationList section */ /* Begin XCSwiftPackageProductDependency section */ + 1606AD152DC5136F00C95115 /* WireAPI */ = { + isa = XCSwiftPackageProductDependency; + productName = WireAPI; + }; 59537D8A2CFF9F8F00920B59 /* WireLogging */ = { isa = XCSwiftPackageProductDependency; productName = WireLogging; diff --git a/wire-ios-sync-engine/Source/Data Model/Conversation+Join.swift b/wire-ios-sync-engine/Source/Data Model/Conversation+Join.swift index d9f753d05ab..88e6ad1bf6d 100644 --- a/wire-ios-sync-engine/Source/Data Model/Conversation+Join.swift +++ b/wire-ios-sync-engine/Source/Data Model/Conversation+Join.swift @@ -74,7 +74,7 @@ extension ZMConversation { code: String, password: String?, transportSession: TransportSessionType, - eventProcessor: ConversationEventProcessorProtocol, + eventProcessor: LegacyConversationEventProcessorProtocol, contextProvider: ContextProvider, completion: @escaping (Result) -> Void ) { diff --git a/wire-ios-sync-engine/Source/Data Model/ServiceUser.swift b/wire-ios-sync-engine/Source/Data Model/ServiceUser.swift index e12340e4386..d4126ccbdaf 100644 --- a/wire-ios-sync-engine/Source/Data Model/ServiceUser.swift +++ b/wire-ios-sync-engine/Source/Data Model/ServiceUser.swift @@ -213,7 +213,7 @@ public extension ServiceUser { internal func createConversation( transportSession: TransportSessionType, - eventProcessor: ConversationEventProcessorProtocol, + eventProcessor: LegacyConversationEventProcessorProtocol, contextProvider: ContextProvider, completionHandler: @escaping (Result) -> Void ) { @@ -325,7 +325,7 @@ public extension ZMConversation { internal func add( serviceUser serviceUserData: ServiceUserData, transportSession: TransportSessionType, - eventProcessor: ConversationEventProcessorProtocol, + eventProcessor: LegacyConversationEventProcessorProtocol, contextProvider: ContextProvider, completionHandler: @escaping (Result) -> Void ) { diff --git a/wire-ios-sync-engine/Source/SessionManager/SessionFactories.swift b/wire-ios-sync-engine/Source/SessionManager/SessionFactories.swift index 1750e97e87d..10961090391 100644 --- a/wire-ios-sync-engine/Source/SessionManager/SessionFactories.swift +++ b/wire-ios-sync-engine/Source/SessionManager/SessionFactories.swift @@ -118,6 +118,16 @@ open class AuthenticatedSessionFactory { isSyncV2Enabled: journal[.isSyncV2Enabled] ) + let cryptoboxMigrationManager = CryptoboxMigrationManager() + + let coreCryptoProvider = CoreCryptoProvider( + selfUserID: account.userIdentifier, + sharedContainerURL: coreDataStack.applicationContainer, + accountDirectory: coreDataStack.accountContainer, + syncContext: coreDataStack.syncContext, + cryptoboxMigrationManager: cryptoboxMigrationManager + ) + var userSessionBuilder = ZMUserSessionBuilder() userSessionBuilder.withAllDependencies( apiServiceFactory: apiServiceFactory, @@ -127,6 +137,7 @@ open class AuthenticatedSessionFactory { application: application, cryptoboxMigrationManager: CryptoboxMigrationManager(), coreDataStack: coreDataStack, + coreCryptoProvider: coreCryptoProvider, configuration: configuration, contextStorage: LAContextStorage(), earService: nil, diff --git a/wire-ios-sync-engine/Source/Synchronization/SyncAgent.swift b/wire-ios-sync-engine/Source/Synchronization/SyncAgent.swift index d774898c308..f4791c34f4f 100644 --- a/wire-ios-sync-engine/Source/Synchronization/SyncAgent.swift +++ b/wire-ios-sync-engine/Source/Synchronization/SyncAgent.swift @@ -55,6 +55,7 @@ final class SyncAgent: NSObject, SyncAgentProtocol { private let initialSyncProvider: any InitialSyncProvider private let incrementalSyncProvider: any IncrementalSyncProvider private let legacySyncStatus: any SyncStatusProtocol + private let coreCryptoProvider: any CoreCryptoProviderProtocol private let incrementalSyncTaskManager = NonReentrantTaskManager() private var incrementalSyncToken: IncrementalSync.Token? @@ -76,6 +77,7 @@ final class SyncAgent: NSObject, SyncAgentProtocol { init( journal: Journal, lastUpdateEventIDRepository: any LastEventIDRepositoryInterface, + coreCryptoProvider: any CoreCryptoProviderProtocol, initialSyncProvider: any InitialSyncProvider, incrementalSyncProvider: any IncrementalSyncProvider, legacySyncStatus: any SyncStatusProtocol, @@ -83,6 +85,7 @@ final class SyncAgent: NSObject, SyncAgentProtocol { ) { self.journal = journal self.lastUpdateEventIDRepository = lastUpdateEventIDRepository + self.coreCryptoProvider = coreCryptoProvider self.initialSyncProvider = initialSyncProvider self.incrementalSyncProvider = incrementalSyncProvider self.legacySyncStatus = legacySyncStatus diff --git a/wire-ios-sync-engine/Source/UserSession/URLActionProcessors/ConnectToBotURLActionProcessor.swift b/wire-ios-sync-engine/Source/UserSession/URLActionProcessors/ConnectToBotURLActionProcessor.swift index 08e4137ca40..8407a3b892b 100644 --- a/wire-ios-sync-engine/Source/UserSession/URLActionProcessors/ConnectToBotURLActionProcessor.swift +++ b/wire-ios-sync-engine/Source/UserSession/URLActionProcessors/ConnectToBotURLActionProcessor.swift @@ -21,14 +21,14 @@ import Foundation final class ConnectToBotURLActionProcessor: NSObject, URLActionProcessor { var transportSession: TransportSessionType - var eventProcessor: ConversationEventProcessorProtocol + var eventProcessor: LegacyConversationEventProcessorProtocol var contextProvider: ContextProvider var searchUsersCache: SearchUsersCache? init( contextprovider: ContextProvider, transportSession: TransportSessionType, - eventProcessor: ConversationEventProcessorProtocol, + eventProcessor: LegacyConversationEventProcessorProtocol, searchUsersCache: SearchUsersCache? ) { self.contextProvider = contextprovider diff --git a/wire-ios-sync-engine/Source/UserSession/URLActionProcessors/DeepLinkURLActionProcessor.swift b/wire-ios-sync-engine/Source/UserSession/URLActionProcessors/DeepLinkURLActionProcessor.swift index 492e327fe68..655bfba2894 100644 --- a/wire-ios-sync-engine/Source/UserSession/URLActionProcessors/DeepLinkURLActionProcessor.swift +++ b/wire-ios-sync-engine/Source/UserSession/URLActionProcessors/DeepLinkURLActionProcessor.swift @@ -24,12 +24,12 @@ class DeepLinkURLActionProcessor: URLActionProcessor { var contextProvider: ContextProvider var transportSession: TransportSessionType - var eventProcessor: ConversationEventProcessorProtocol + var eventProcessor: LegacyConversationEventProcessorProtocol init( contextProvider: ContextProvider, transportSession: TransportSessionType, - eventProcessor: ConversationEventProcessorProtocol + eventProcessor: LegacyConversationEventProcessorProtocol ) { self.contextProvider = contextProvider self.transportSession = transportSession diff --git a/wire-ios-sync-engine/Source/UserSession/ZMUserSession/ZMUserSession.swift b/wire-ios-sync-engine/Source/UserSession/ZMUserSession/ZMUserSession.swift index 2f777d87e91..0ba919b31a9 100644 --- a/wire-ios-sync-engine/Source/UserSession/ZMUserSession/ZMUserSession.swift +++ b/wire-ios-sync-engine/Source/UserSession/ZMUserSession/ZMUserSession.swift @@ -20,6 +20,7 @@ import Combine import Foundation import WireAnalytics import WireAPI +import WireCoreCrypto import WireDataModel import WireDomain import WireLogging @@ -287,7 +288,6 @@ public final class ZMUserSession: NSObject { let keyRotator = E2EIKeyPackageRotator( coreCryptoProvider: coreCryptoProvider, - conversationEventProcessor: conversationEventProcessor, context: syncContext, onNewCRLsDistributionPointsSubject: onNewCRLsDistributionPointsSubject, featureRepository: featureRepository @@ -568,6 +568,8 @@ public final class ZMUserSession: NSObject { ) ) + coreCryptoProvider.registerMlsTransport(clientSessionComponent.mlsTransport) + let incrementalSyncProvider: IncrementalSyncProvider = if !asyncStreamEnabled { clientSessionComponent } else { @@ -578,6 +580,7 @@ public final class ZMUserSession: NSObject { let syncAgent = SyncAgent( journal: journal, lastUpdateEventIDRepository: lastEventIDRepository, + coreCryptoProvider: coreCryptoProvider, initialSyncProvider: clientSessionComponent, incrementalSyncProvider: incrementalSyncProvider, legacySyncStatus: applicationStatusDirectory.syncStatus, diff --git a/wire-ios-sync-engine/Source/UserSession/ZMUserSession/ZMUserSessionBuilder.swift b/wire-ios-sync-engine/Source/UserSession/ZMUserSession/ZMUserSessionBuilder.swift index 01b4088b520..51490969cb7 100644 --- a/wire-ios-sync-engine/Source/UserSession/ZMUserSession/ZMUserSessionBuilder.swift +++ b/wire-ios-sync-engine/Source/UserSession/ZMUserSession/ZMUserSessionBuilder.swift @@ -133,6 +133,7 @@ struct ZMUserSessionBuilder { application: any ZMApplication, cryptoboxMigrationManager: any CryptoboxMigrationManagerInterface, coreDataStack: CoreDataStack, + coreCryptoProvider: CoreCryptoProviderProtocol, configuration: ZMUserSession.Configuration, contextStorage: any LAContextStorable, earService: (any EARServiceInterface)?, @@ -149,13 +150,6 @@ struct ZMUserSessionBuilder { ) { // reused dependencies - let coreCryptoProvider = CoreCryptoProvider( - selfUserID: userId, - sharedContainerURL: coreDataStack.applicationContainer, - accountDirectory: coreDataStack.accountContainer, - syncContext: coreDataStack.syncContext, - cryptoboxMigrationManager: cryptoboxMigrationManager - ) let lastEventIDRepository = LastEventIDRepository( userID: userId, sharedUserDefaults: sharedUserDefaults @@ -203,7 +197,6 @@ struct ZMUserSessionBuilder { context: coreDataStack.syncContext, notificationContext: coreDataStack.syncContext.notificationContext, coreCryptoProvider: coreCryptoProvider, - conversationEventProcessor: ConversationEventProcessor(context: coreDataStack.syncContext), featureRepository: FeatureRepository(context: coreDataStack.syncContext), userDefaults: .standard, userID: coreDataStack.account.userIdentifier diff --git a/wire-ios-sync-engine/Tests/Source/Calling/WireCallCenterV3Tests.swift b/wire-ios-sync-engine/Tests/Source/Calling/WireCallCenterV3Tests.swift index 15e91032776..a1fceb56ad4 100644 --- a/wire-ios-sync-engine/Tests/Source/Calling/WireCallCenterV3Tests.swift +++ b/wire-ios-sync-engine/Tests/Source/Calling/WireCallCenterV3Tests.swift @@ -1157,9 +1157,11 @@ final class WireCallCenterV3Tests: MessagingTest { } // So we can inform of new conference infos + let listentingOnConferenceInfoChange = expectation(description: "listenting to onConferenceInfoChange") let conferenceInfoChangeSubject = PassthroughSubject() mlsService.onConferenceInfoChangeParentGroupIDSubConversationGroupID_MockMethod = { _, _ in var iterator = conferenceInfoChangeSubject.values.makeAsyncIterator() + listentingOnConferenceInfoChange.fulfill() return AsyncThrowingStream { await iterator.next() } @@ -1174,6 +1176,8 @@ final class WireCallCenterV3Tests: MessagingTest { try block() } + wait(for: [listentingOnConferenceInfoChange]) + let didSetConferenceInfo2 = customExpectation(description: "didSetConferenceInfo2") mockAVSWrapper.mockSetMLSConferenceInfo = { XCTAssertEqual( diff --git a/wire-ios-sync-engine/Tests/Source/E2EI/CertificateRevocationListsCheckerTests.swift b/wire-ios-sync-engine/Tests/Source/E2EI/CertificateRevocationListsCheckerTests.swift index 3e9d3ab836b..6986312e023 100644 --- a/wire-ios-sync-engine/Tests/Source/E2EI/CertificateRevocationListsCheckerTests.swift +++ b/wire-ios-sync-engine/Tests/Source/E2EI/CertificateRevocationListsCheckerTests.swift @@ -27,7 +27,7 @@ final class CertificateRevocationListsCheckerTests: XCTestCase { private var coreDataHelper: CoreDataStackHelper! private var sut: CertificateRevocationListsChecker! - private var mockCoreCrypto: MockCoreCryptoProtocol! + private var mockCoreCryptoContext: MockCoreCryptoContextProtocol! private var mockCRLAPI: MockCertificateRevocationListAPIProtocol! private var mockMLSGroupVerification: MockMLSGroupVerificationProtocol! private var mockSelfClientCertificateProvider: MockSelfClientCertificateProviderProtocol! @@ -38,8 +38,8 @@ final class CertificateRevocationListsCheckerTests: XCTestCase { override func setUp() async throws { try await super.setUp() - mockCoreCrypto = MockCoreCryptoProtocol() - let safeCoreCrypto = MockSafeCoreCrypto(coreCrypto: mockCoreCrypto) + mockCoreCryptoContext = MockCoreCryptoContextProtocol() + let safeCoreCrypto = MockSafeCoreCrypto(coreCryptoContext: mockCoreCryptoContext) let provider = MockCoreCryptoProviderProtocol() provider.coreCrypto_MockValue = safeCoreCrypto @@ -68,7 +68,7 @@ final class CertificateRevocationListsCheckerTests: XCTestCase { override func tearDown() async throws { sut = nil - mockCoreCrypto = nil + mockCoreCryptoContext = nil mockCRLAPI = nil mockMLSGroupVerification = nil mockSelfClientCertificateProvider = nil @@ -117,9 +117,9 @@ final class CertificateRevocationListsCheckerTests: XCTestCase { ) // It registers the CRLs with core crypto - XCTAssertEqual(mockCoreCrypto.e2eiRegisterCrlCrlDpCrlDer_Invocations.count, 2) + XCTAssertEqual(mockCoreCryptoContext.e2eiRegisterCrlCrlDpCrlDer_Invocations.count, 2) XCTAssertEqual( - Set(mockCoreCrypto.e2eiRegisterCrlCrlDpCrlDer_Invocations.map(\.crlDp)), + Set(mockCoreCryptoContext.e2eiRegisterCrlCrlDpCrlDer_Invocations.map(\.crlDp)), Set([dp2, dp3]) ) @@ -153,7 +153,7 @@ final class CertificateRevocationListsCheckerTests: XCTestCase { XCTAssertTrue(mockCRLAPI.getRevocationListFrom_Invocations.isEmpty) // It doesn't register any CRL with core crypto - XCTAssertTrue(mockCoreCrypto.e2eiRegisterCrlCrlDpCrlDer_Invocations.isEmpty) + XCTAssertTrue(mockCoreCryptoContext.e2eiRegisterCrlCrlDpCrlDer_Invocations.isEmpty) // It desn't store expiration date XCTAssertTrue(mockCRLExpirationDatesRepository.storeCRLExpirationDateFor_Invocations.isEmpty) @@ -212,9 +212,9 @@ final class CertificateRevocationListsCheckerTests: XCTestCase { ) // It registers the fetched CRLs with core crypto - XCTAssertEqual(mockCoreCrypto.e2eiRegisterCrlCrlDpCrlDer_Invocations.count, 1) + XCTAssertEqual(mockCoreCryptoContext.e2eiRegisterCrlCrlDpCrlDer_Invocations.count, 1) XCTAssertEqual( - Set(mockCoreCrypto.e2eiRegisterCrlCrlDpCrlDer_Invocations.map(\.crlDp)), + Set(mockCoreCryptoContext.e2eiRegisterCrlCrlDpCrlDer_Invocations.map(\.crlDp)), Set([dp2]) ) @@ -244,7 +244,7 @@ final class CertificateRevocationListsCheckerTests: XCTestCase { private func mockCRLRegistration(with configurations: [String: (dirty: Bool, expiration: Date?)]) { // Mock registering the CRL with core crypto and returning the registration - mockCoreCrypto.e2eiRegisterCrlCrlDpCrlDer_MockMethod = { dp, _ in + mockCoreCryptoContext.e2eiRegisterCrlCrlDpCrlDer_MockMethod = { dp, _ in guard let configuration = configurations[dp] else { return .init(dirty: false, expiration: nil) } diff --git a/wire-ios-sync-engine/Tests/Source/SessionManager/APIMigrationManagerTests.swift b/wire-ios-sync-engine/Tests/Source/SessionManager/APIMigrationManagerTests.swift index 22b5916a7f1..85ed257b06b 100644 --- a/wire-ios-sync-engine/Tests/Source/SessionManager/APIMigrationManagerTests.swift +++ b/wire-ios-sync-engine/Tests/Source/SessionManager/APIMigrationManagerTests.swift @@ -240,6 +240,14 @@ final class APIMigrationManagerTests: MessagingTest { @MainActor private func stubUserSession() -> ZMUserSession { let mockStrategyDirectory = MockStrategyDirectory() + let mockCoreCrypto = MockCoreCryptoProtocol() + let mockSafeCoreCrypto = MockSafeCoreCrypto(coreCrypto: mockCoreCrypto) + let mockCoreCryptoProvider = MockCoreCryptoProviderProtocol() + mockCoreCrypto.registerEpochObserver_MockMethod = { _ in } + mockCoreCryptoProvider.coreCrypto_MockValue = mockSafeCoreCrypto + mockCoreCryptoProvider.registerMlsTransport_MockMethod = { _ in } + mockCoreCryptoProvider.registerEpochObserver_MockMethod = { _ in } + let mockCryptoboxMigrationManager = MockCryptoboxMigrationManagerInterface() let cookieStorage = ZMPersistentCookieStorage( @@ -301,6 +309,7 @@ final class APIMigrationManagerTests: MessagingTest { application: application, cryptoboxMigrationManager: mockCryptoboxMigrationManager, coreDataStack: createCoreDataStack(), + coreCryptoProvider: mockCoreCryptoProvider, configuration: configuration, contextStorage: mockContextStorable, earService: nil, diff --git a/wire-ios-sync-engine/Tests/Source/Synchronization/SyncAgentTests.swift b/wire-ios-sync-engine/Tests/Source/Synchronization/SyncAgentTests.swift index 940b3378994..63b430379d6 100644 --- a/wire-ios-sync-engine/Tests/Source/Synchronization/SyncAgentTests.swift +++ b/wire-ios-sync-engine/Tests/Source/Synchronization/SyncAgentTests.swift @@ -33,6 +33,7 @@ final class SyncAgentTests: XCTestCase, InitialSyncProvider, IncrementalSyncProv var initialSync: MockInitialSyncProtocol! var incrementalSync: MockIncrementalSyncProtocol! var syncStateSubject: CurrentValueSubject! + var coreCryptoProvider: MockCoreCryptoProviderProtocol! override func setUp() { journal = Journal( @@ -44,9 +45,11 @@ final class SyncAgentTests: XCTestCase, InitialSyncProvider, IncrementalSyncProv initialSync = MockInitialSyncProtocol() incrementalSync = MockIncrementalSyncProtocol() syncStateSubject = CurrentValueSubject(.idle) + coreCryptoProvider = MockCoreCryptoProviderProtocol() sut = SyncAgent( journal: journal, lastUpdateEventIDRepository: lastUpdateEventIDRepository, + coreCryptoProvider: coreCryptoProvider, initialSyncProvider: self, incrementalSyncProvider: self, legacySyncStatus: legacySyncStatus, diff --git a/wire-ios-sync-engine/Tests/Source/Use cases/CheckOneOnOneConversationIsReadyUseCaseTests.swift b/wire-ios-sync-engine/Tests/Source/Use cases/CheckOneOnOneConversationIsReadyUseCaseTests.swift index 95ddbeb2304..756d23b542a 100644 --- a/wire-ios-sync-engine/Tests/Source/Use cases/CheckOneOnOneConversationIsReadyUseCaseTests.swift +++ b/wire-ios-sync-engine/Tests/Source/Use cases/CheckOneOnOneConversationIsReadyUseCaseTests.swift @@ -27,7 +27,7 @@ class CheckOneOnOneConversationIsReadyUseCaseTests: XCTestCase { private var sut: CheckOneOnOneConversationIsReadyUseCase! private var coreDataStack: CoreDataStack! private var mockCoreCryptoProvider: MockCoreCryptoProviderProtocol! - private var mockCoreCrypto: MockCoreCryptoProtocol! + private var mockCoreCryptoContext: MockCoreCryptoContextProtocol! private var syncMOC: NSManagedObjectContext! private var user: ZMUser! private var userID: QualifiedID! @@ -38,9 +38,9 @@ class CheckOneOnOneConversationIsReadyUseCaseTests: XCTestCase { coreDataStack = try await coreDataStackHelper.createStack() syncMOC = coreDataStack.syncContext - mockCoreCrypto = MockCoreCryptoProtocol() + mockCoreCryptoContext = MockCoreCryptoContextProtocol() mockCoreCryptoProvider = MockCoreCryptoProviderProtocol() - mockCoreCryptoProvider.coreCrypto_MockValue = MockSafeCoreCrypto(coreCrypto: mockCoreCrypto) + mockCoreCryptoProvider.coreCrypto_MockValue = MockSafeCoreCrypto(coreCryptoContext: mockCoreCryptoContext) sut = CheckOneOnOneConversationIsReadyUseCase( context: syncMOC, @@ -54,7 +54,7 @@ class CheckOneOnOneConversationIsReadyUseCaseTests: XCTestCase { sut = nil coreDataStack = nil mockCoreCryptoProvider = nil - mockCoreCrypto = nil + mockCoreCryptoContext = nil syncMOC = nil user = nil super.tearDown() @@ -88,7 +88,7 @@ class CheckOneOnOneConversationIsReadyUseCaseTests: XCTestCase { func test_ItReturnsTrue_WhenConversationExists_MLS_Established() async throws { // Given await setupOneOnOne(messageProtocol: .mls, groupID: .random()) - mockCoreCrypto.conversationExistsConversationId_MockValue = true + mockCoreCryptoContext.conversationExistsConversationId_MockValue = true // When let isReady = try await sut.invoke(userID: userID) @@ -100,7 +100,7 @@ class CheckOneOnOneConversationIsReadyUseCaseTests: XCTestCase { func test_ItReturnsFalse_WhenConversationExists_MLS_NotEstablished() async throws { // Given await setupOneOnOne(messageProtocol: .mls, groupID: .random()) - mockCoreCrypto.conversationExistsConversationId_MockValue = false + mockCoreCryptoContext.conversationExistsConversationId_MockValue = false // When let isReady = try await sut.invoke(userID: userID) diff --git a/wire-ios-sync-engine/Tests/Source/Use cases/GetE2eIdentityCertificatesUseCaseTests.swift b/wire-ios-sync-engine/Tests/Source/Use cases/GetE2eIdentityCertificatesUseCaseTests.swift index d5d4f866f9b..76c67792d7a 100644 --- a/wire-ios-sync-engine/Tests/Source/Use cases/GetE2eIdentityCertificatesUseCaseTests.swift +++ b/wire-ios-sync-engine/Tests/Source/Use cases/GetE2eIdentityCertificatesUseCaseTests.swift @@ -30,13 +30,13 @@ final class GetE2eIdentityCertificatesUseCaseTests: XCTestCase { private var sut: GetE2eIdentityCertificatesUseCase! private var coreCryptoProvider: MockCoreCryptoProviderProtocol! private var safeCoreCrypto: MockSafeCoreCrypto! - private var coreCrypto: MockCoreCryptoProtocol! + private var coreCryptoContext: MockCoreCryptoContextProtocol! override func setUp() async throws { try await super.setUp() stack = try await coreDataStackHelper.createStack() - coreCrypto = MockCoreCryptoProtocol() - safeCoreCrypto = MockSafeCoreCrypto(coreCrypto: coreCrypto) + coreCryptoContext = MockCoreCryptoContextProtocol() + safeCoreCrypto = MockSafeCoreCrypto(coreCryptoContext: coreCryptoContext) coreCryptoProvider = MockCoreCryptoProviderProtocol() coreCryptoProvider.coreCrypto_MockValue = safeCoreCrypto sut = GetE2eIdentityCertificatesUseCase( @@ -48,7 +48,7 @@ final class GetE2eIdentityCertificatesUseCaseTests: XCTestCase { override func tearDown() async throws { stack = nil sut = nil - coreCrypto = nil + coreCryptoContext = nil safeCoreCrypto = nil coreCryptoProvider = nil try coreDataStackHelper.cleanupDirectory() @@ -135,7 +135,7 @@ final class GetE2eIdentityCertificatesUseCaseTests: XCTestCase { status: .revoked ) - coreCrypto.getDeviceIdentitiesConversationIdDeviceIds_MockMethod = { _, _ in + coreCryptoContext.getDeviceIdentitiesConversationIdDeviceIds_MockMethod = { _, _ in [ validIdentity, identityWithInvalidHandle, diff --git a/wire-ios-sync-engine/Tests/Source/UserSession/DeepLinkURLActionProcessorTests.swift b/wire-ios-sync-engine/Tests/Source/UserSession/DeepLinkURLActionProcessorTests.swift index 0e7e96471a7..488572301ec 100644 --- a/wire-ios-sync-engine/Tests/Source/UserSession/DeepLinkURLActionProcessorTests.swift +++ b/wire-ios-sync-engine/Tests/Source/UserSession/DeepLinkURLActionProcessorTests.swift @@ -27,13 +27,13 @@ final class DeepLinkURLActionProcessorTests: DatabaseTest { var presentationDelegate: MockPresentationDelegate! var sut: WireSyncEngine.DeepLinkURLActionProcessor! var mockTransportSession: MockTransportSession! - var mockEventProcessor: MockConversationEventProcessorProtocol! + var mockEventProcessor: MockLegacyConversationEventProcessorProtocol! override func setUp() { super.setUp() mockTransportSession = MockTransportSession(dispatchGroup: dispatchGroup) - mockEventProcessor = MockConversationEventProcessorProtocol() + mockEventProcessor = MockLegacyConversationEventProcessorProtocol() mockEventProcessor.processConversationEvents_MockMethod = { _ in } presentationDelegate = MockPresentationDelegate() diff --git a/wire-ios-sync-engine/Tests/Source/UserSession/ZMUserSessionTests+AccessToken.swift b/wire-ios-sync-engine/Tests/Source/UserSession/ZMUserSessionTests+AccessToken.swift index 0aafa76b111..bce98d74d9f 100644 --- a/wire-ios-sync-engine/Tests/Source/UserSession/ZMUserSessionTests+AccessToken.swift +++ b/wire-ios-sync-engine/Tests/Source/UserSession/ZMUserSessionTests+AccessToken.swift @@ -49,6 +49,9 @@ final class ZMUserSessionTests_AccessToken: ZMUserSessionTestsBase { } BackendInfo.apiVersion = apiVersion + mockCoreCryptoProvider.registerMlsTransport_MockMethod = { _ in } + mockCoreCryptoProvider.registerEpochObserver_MockMethod = { _ in } + // when sut.didRegisterSelfUserClient(userClient) diff --git a/wire-ios-sync-engine/Tests/Source/UserSession/ZMUserSessionTests.swift b/wire-ios-sync-engine/Tests/Source/UserSession/ZMUserSessionTests.swift index 071c9d414b0..df5f2fa2b3e 100644 --- a/wire-ios-sync-engine/Tests/Source/UserSession/ZMUserSessionTests.swift +++ b/wire-ios-sync-engine/Tests/Source/UserSession/ZMUserSessionTests.swift @@ -109,6 +109,9 @@ final class ZMUserSessionTests: ZMUserSessionTestsBase { self.createSelfClient() } + mockCoreCryptoProvider.registerMlsTransport_MockMethod = { _ in } + mockCoreCryptoProvider.registerEpochObserver_MockMethod = { _ in } + // WHEN syncMOC.performGroupedBlock { [self] in sut.didRegisterSelfUserClient(userClient) @@ -520,6 +523,7 @@ final class ZMUserSessionTests: ZMUserSessionTestsBase { mockMLSService.commitPendingProposalsIfNeeded_MockMethod = {} mockMLSService.uploadKeyPackagesIfNeeded_MockMethod = {} mockMLSService.updateKeyMaterialForAllStaleGroupsIfNeeded_MockMethod = {} + mockCoreCryptoProvider.initialiseMLSWithBasicCredentialsMlsClientID_MockMethod = { _ in } syncMOC.performAndWait { XCTAssertTrue(selfUserClient.mlsPublicKeys.isEmpty) @@ -545,8 +549,7 @@ final class ZMUserSessionTests: ZMUserSessionTestsBase { // THEN syncMOC.performAndWait { - XCTAssertFalse(selfUserClient.mlsPublicKeys.isEmpty) - + XCTAssertEqual(mockCoreCryptoProvider.initialiseMLSWithBasicCredentialsMlsClientID_Invocations.count, 1) XCTAssertTrue(BackendInfo.isMLSEnabled) XCTAssertTrue(sut.featureRepository.fetchMLS().isEnabled) } diff --git a/wire-ios-sync-engine/Tests/Source/UserSession/ZMUserSessionTestsBase.swift b/wire-ios-sync-engine/Tests/Source/UserSession/ZMUserSessionTestsBase.swift index 1a23989fc8a..78e20e608e1 100644 --- a/wire-ios-sync-engine/Tests/Source/UserSession/ZMUserSessionTestsBase.swift +++ b/wire-ios-sync-engine/Tests/Source/UserSession/ZMUserSessionTestsBase.swift @@ -45,6 +45,7 @@ class ZMUserSessionTestsBase: MessagingTest { var mockGetFeatureConfigsActionHandler: MockActionHandler! var mockFetchBackendMLSPublicKeysActionHandler: MockActionHandler! var mockRecurringActionService: MockRecurringActionServiceInterface! + var mockCoreCryptoProvider: MockCoreCryptoProviderProtocol! var sut: ZMUserSession! @@ -145,6 +146,7 @@ class ZMUserSessionTestsBase: MessagingTest { self.sut = nil mockGetFeatureConfigsActionHandler = nil mockFetchBackendMLSPublicKeysActionHandler = nil + mockCoreCryptoProvider = nil sut?.tearDown() super.tearDown() @@ -155,6 +157,12 @@ class ZMUserSessionTestsBase: MessagingTest { } func createSut(earService: EARServiceInterface) -> ZMUserSession { + let mockCoreCrypto = MockCoreCryptoProtocol() + mockCoreCrypto.registerEpochObserver_MockMethod = { _ in } + let mockSafeCoreCrypto = MockSafeCoreCrypto(coreCrypto: mockCoreCrypto) + mockCoreCryptoProvider = MockCoreCryptoProviderProtocol() + mockCoreCryptoProvider.coreCrypto_MockValue = mockSafeCoreCrypto + let mockCryptoboxMigrationManager = MockCryptoboxMigrationManagerInterface() mockCryptoboxMigrationManager.isMigrationNeededAccountDirectory_MockValue = false @@ -177,6 +185,7 @@ class ZMUserSessionTestsBase: MessagingTest { application: application, cryptoboxMigrationManager: mockCryptoboxMigrationManager, coreDataStack: coreDataStack, + coreCryptoProvider: mockCoreCryptoProvider, configuration: configuration, contextStorage: mockContextStorable, earService: earService, diff --git a/wire-ios-sync-engine/Tests/Source/UserSession/ZMUserSessionTests_NetworkState.swift b/wire-ios-sync-engine/Tests/Source/UserSession/ZMUserSessionTests_NetworkState.swift index 7dc073a6619..59e120c511a 100644 --- a/wire-ios-sync-engine/Tests/Source/UserSession/ZMUserSessionTests_NetworkState.swift +++ b/wire-ios-sync-engine/Tests/Source/UserSession/ZMUserSessionTests_NetworkState.swift @@ -34,6 +34,13 @@ final class ZMUserSessionTests_NetworkState: ZMUserSessionTestsBase { useCache: true ) let transportSession = RecordingMockTransportSession(cookieStorage: cookieStorage, pushChannel: mockPushChannel) + let mockCoreCrypto = MockCoreCryptoProtocol() + mockCoreCrypto.registerEpochObserver_MockMethod = { _ in } + let mockSafeCoreCrypto = MockSafeCoreCrypto(coreCrypto: mockCoreCrypto) + let coreCryptoProvider = MockCoreCryptoProviderProtocol() + coreCryptoProvider.coreCrypto_MockValue = mockSafeCoreCrypto + coreCryptoProvider.registerMlsTransport_MockMethod = { _ in } + coreCryptoProvider.registerEpochObserver_MockMethod = { _ in } let mockCryptoboxMigrationManager = MockCryptoboxMigrationManagerInterface() let coreDataStack = createCoreDataStack() let selfClient = coreDataStack.syncContext.performAndWait { @@ -59,6 +66,7 @@ final class ZMUserSessionTests_NetworkState: ZMUserSessionTestsBase { application: application, cryptoboxMigrationManager: mockCryptoboxMigrationManager, coreDataStack: coreDataStack, + coreCryptoProvider: coreCryptoProvider, configuration: configuration, contextStorage: mockContextStore, earService: mockEARService, diff --git a/wire-ios-system/Support/Sourcery/generated/AutoMockable.generated.swift b/wire-ios-system/Support/Sourcery/generated/AutoMockable.generated.swift index eae1bf785a8..d669c60420a 100644 --- a/wire-ios-system/Support/Sourcery/generated/AutoMockable.generated.swift +++ b/wire-ios-system/Support/Sourcery/generated/AutoMockable.generated.swift @@ -1,4 +1,4 @@ -// Generated using Sourcery 2.2.6 — https://github.com/krzysztofzablocki/Sourcery +// Generated using Sourcery 2.2.4 — https://github.com/krzysztofzablocki/Sourcery // DO NOT EDIT // // Wire diff --git a/wire-ios-utilities/Support/Sourcery/generated/AutoMockable.generated.swift b/wire-ios-utilities/Support/Sourcery/generated/AutoMockable.generated.swift index 946307c9b0d..57aa58298e4 100644 --- a/wire-ios-utilities/Support/Sourcery/generated/AutoMockable.generated.swift +++ b/wire-ios-utilities/Support/Sourcery/generated/AutoMockable.generated.swift @@ -1,4 +1,4 @@ -// Generated using Sourcery 2.2.6 — https://github.com/krzysztofzablocki/Sourcery +// Generated using Sourcery 2.2.4 — https://github.com/krzysztofzablocki/Sourcery // DO NOT EDIT // // Wire diff --git a/wire-ios/Tests/Sourcery/generated/AutoMockable.generated.swift b/wire-ios/Tests/Sourcery/generated/AutoMockable.generated.swift index 3fa43b3680f..cf7577e9af1 100644 --- a/wire-ios/Tests/Sourcery/generated/AutoMockable.generated.swift +++ b/wire-ios/Tests/Sourcery/generated/AutoMockable.generated.swift @@ -1,4 +1,4 @@ -// Generated using Sourcery 2.2.6 — https://github.com/krzysztofzablocki/Sourcery +// Generated using Sourcery 2.2.4 — https://github.com/krzysztofzablocki/Sourcery // DO NOT EDIT // // Wire diff --git a/wire-ios/Wire-iOS.xcodeproj/project.pbxproj b/wire-ios/Wire-iOS.xcodeproj/project.pbxproj index 9febcec0325..2b7cb71d239 100644 --- a/wire-ios/Wire-iOS.xcodeproj/project.pbxproj +++ b/wire-ios/Wire-iOS.xcodeproj/project.pbxproj @@ -51,6 +51,7 @@ 061F34192B1E2ED50099CBAB /* AppAuth in Frameworks */ = {isa = PBXBuildFile; productRef = 061F34182B1E2ED50099CBAB /* AppAuth */; }; 061F341B2B1E2ED50099CBAB /* AppAuthCore in Frameworks */ = {isa = PBXBuildFile; productRef = 061F341A2B1E2ED50099CBAB /* AppAuthCore */; }; 065A5AE5279B07AC002D4D1E /* Wire Notification Service Extension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 065A5ADE279B07AC002D4D1E /* Wire Notification Service Extension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + 16EE08AE2D92E861001B372C /* WireCoreCryptoUniffi in Frameworks */ = {isa = PBXBuildFile; productRef = 16EE08AD2D92E861001B372C /* WireCoreCryptoUniffi */; }; 407831AD2AC4636F005C2978 /* DifferenceKit in Frameworks */ = {isa = PBXBuildFile; productRef = 407831AC2AC4636F005C2978 /* DifferenceKit */; }; 59076F952C934A9800AE7529 /* WireAccountImageUI in Frameworks */ = {isa = PBXBuildFile; productRef = 59076F942C934A9800AE7529 /* WireAccountImageUI */; }; 590DCA082C971A56002D0A2C /* WireSidebarUI in Frameworks */ = {isa = PBXBuildFile; productRef = 590DCA072C971A56002D0A2C /* WireSidebarUI */; }; @@ -961,6 +962,7 @@ 5996E8A62C19D090007A52F0 /* WireSyncEngine.framework in Frameworks */, EEE25B5929719DA80008B894 /* WireImages.framework in Frameworks */, 76BFA65F2D89E6D8000F2D0A /* WireConversationsUIBindings in Frameworks */, + 16EE08AE2D92E861001B372C /* WireCoreCryptoUniffi in Frameworks */, 5929CB222CA6C9C800070488 /* WireConversationListUI in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1356,6 +1358,7 @@ 01C774262D9EECE100A2FF07 /* WireAVS */, 76BFA65C2D89E6B8000F2D0A /* WireConversationsAPI */, 76BFA65E2D89E6D8000F2D0A /* WireConversationsUIBindings */, + 16EE08AD2D92E861001B372C /* WireCoreCryptoUniffi */, ); productName = "ZClient iOS"; productReference = 8F42C538199244A700288E4D /* Wire.app */; @@ -2785,6 +2788,10 @@ package = 061F34172B1E2E8F0099CBAB /* XCRemoteSwiftPackageReference "AppAuth-iOS" */; productName = AppAuthCore; }; + 16EE08AD2D92E861001B372C /* WireCoreCryptoUniffi */ = { + isa = XCSwiftPackageProductDependency; + productName = WireCoreCryptoUniffi; + }; 407831AC2AC4636F005C2978 /* DifferenceKit */ = { isa = XCSwiftPackageProductDependency; package = 407831AB2AC4636F005C2978 /* XCRemoteSwiftPackageReference "DifferenceKit" */;