Skip to content
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
6579e76
chore(cryptobox): retire `ProteusProvider` - WPB-21821
David-Henner Nov 13, 2025
b974900
remove dead code
David-Henner Nov 24, 2025
f8b7b62
fix legal hold tests
David-Henner Nov 24, 2025
ecd6b28
format
David-Henner Nov 24, 2025
0a40018
Merge branch 'develop' into chore/retire-proteus-provider-WPB-21821
David-Henner Nov 24, 2025
c93bc57
fix tests
David-Henner Nov 25, 2025
3464629
Merge branch 'chore/retire-proteus-provider-WPB-21821' of github.com:…
David-Henner Nov 25, 2025
6b41d50
Merge branch 'develop' into chore/retire-proteus-provider-WPB-21821
David-Henner Nov 28, 2025
37d02a7
chore(cryptobox): remove proteusViaCoreCrypto flag - WPB-14604
David-Henner Nov 25, 2025
4fe5399
avoid force unwrapping note
David-Henner Nov 25, 2025
c81fd15
fix `CallingRequestStrategyTests`
David-Henner Nov 25, 2025
f404214
remove `try`s for non-throwing methods
David-Henner Nov 28, 2025
ae55be2
fix typo
David-Henner Nov 28, 2025
8ce6241
formatting
David-Henner Nov 28, 2025
cf13810
async proteusService getter
David-Henner Dec 4, 2025
d64cb07
Merge branch 'develop' into chore/retire-proteus-provider-WPB-21821
David-Henner Dec 4, 2025
42e4349
remove empty `defer`s
David-Henner Dec 4, 2025
f34ba9a
Merge branch 'develop' into chore/retire-proteus-provider-WPB-21821
David-Henner Dec 4, 2025
a046348
Merge branch 'chore/retire-proteus-provider-WPB-21821' into chore/rem…
David-Henner Dec 4, 2025
846e482
Merge branch 'develop' into chore/remove-proteusViaCoreCrypto-flag-WP…
David-Henner Dec 5, 2025
c3fc77d
update submodule
David-Henner Dec 5, 2025
8382488
fix bad conflict resolution
David-Henner Dec 5, 2025
e7710b8
Merge branch 'develop' into chore/remove-proteusViaCoreCrypto-flag-WP…
David-Henner Dec 5, 2025
8f7315f
Merge branch 'develop' into chore/remove-proteusViaCoreCrypto-flag-WP…
David-Henner Dec 5, 2025
fe84f7c
record snapshots
David-Henner Dec 5, 2025
3e410a7
Merge branch 'develop' into chore/remove-proteusViaCoreCrypto-flag-WP…
David-Henner Dec 8, 2025
a33edf9
reset snapshots
David-Henner Dec 8, 2025
507b78d
disable cryptobox migration
David-Henner Dec 8, 2025
2c5c733
format
David-Henner Dec 8, 2025
100c36f
remove tests for cryptobox migration manager
David-Henner Dec 8, 2025
3cb665d
add timeout
David-Henner Dec 9, 2025
76b6180
Merge branch 'develop' into chore/remove-proteusViaCoreCrypto-flag-WP…
David-Henner Dec 11, 2025
5658cb2
Merge branch 'develop' into chore/remove-proteusViaCoreCrypto-flag-WP…
David-Henner Dec 11, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,7 @@

func configureViewContext(_ context: NSManagedObjectContext) async {
context.markAsUIContext()
await context.perform {

Check warning on line 372 in wire-ios-data-model/Source/ManagedObjectContext/CoreDataStack.swift

View workflow job for this annotation

GitHub Actions / Test Results

Capture of 'self' with non-Sendable type 'CoreDataStack' in a '@Sendable' closure

Capture of 'self' with non-Sendable type 'CoreDataStack' in a '@sendable' closure
context.localDomain = self.localDomain
context.isFederationEnabled = self.isFederationEnabled
context.createDispatchGroups()
Expand All @@ -381,17 +381,17 @@
}

func configureContextReferences() async {
await viewContext.perform {

Check warning on line 384 in wire-ios-data-model/Source/ManagedObjectContext/CoreDataStack.swift

View workflow job for this annotation

GitHub Actions / Test Results

Capture of 'self' with non-Sendable type 'CoreDataStack' in a '@Sendable' closure

Capture of 'self' with non-Sendable type 'CoreDataStack' in a '@sendable' closure
self.viewContext.zm_sync = self.syncContext
}
await syncContext.perform {

Check warning on line 387 in wire-ios-data-model/Source/ManagedObjectContext/CoreDataStack.swift

View workflow job for this annotation

GitHub Actions / Test Results

Capture of 'self' with non-Sendable type 'CoreDataStack' in a '@Sendable' closure

Capture of 'self' with non-Sendable type 'CoreDataStack' in a '@sendable' closure
self.syncContext.zm_userInterface = self.viewContext
}
}

func configureSyncContext(_ context: NSManagedObjectContext) async {
context.markAsSyncContext()
await context.perform {

Check warning on line 394 in wire-ios-data-model/Source/ManagedObjectContext/CoreDataStack.swift

View workflow job for this annotation

GitHub Actions / Test Results

Capture of 'self' with non-Sendable type 'CoreDataStack' in a '@Sendable' closure

Capture of 'self' with non-Sendable type 'CoreDataStack' in a '@sendable' closure
context.localDomain = self.localDomain
context.isFederationEnabled = self.isFederationEnabled
context.createDispatchGroups()
Expand All @@ -401,13 +401,6 @@
context.accountDirectoryURL = self.accountContainer
context.applicationContainerURL = self.applicationContainer

if !DeveloperFlag.proteusViaCoreCrypto.isOn {
context.setupUserKeyStore(
accountDirectory: self.accountContainer,
applicationContainer: self.applicationContainer
)
}

context.undoManager = nil
context.mergePolicy = NSMergePolicy(merge: .mergeByPropertyObjectTrumpMergePolicyType)

Expand All @@ -417,7 +410,7 @@

func configureSearchContext(_ context: NSManagedObjectContext) async {
context.markAsSearch()
await context.perform {

Check warning on line 413 in wire-ios-data-model/Source/ManagedObjectContext/CoreDataStack.swift

View workflow job for this annotation

GitHub Actions / Test Results

Capture of 'self' with non-Sendable type 'CoreDataStack' in a '@Sendable' closure

Capture of 'self' with non-Sendable type 'CoreDataStack' in a '@sendable' closure
context.localDomain = self.localDomain
context.isFederationEnabled = self.isFederationEnabled
context.createDispatchGroups()
Expand All @@ -430,7 +423,7 @@

func configureEventContext(_ context: NSManagedObjectContext) async {
await context.perform {
context.createDispatchGroups()

Check warning on line 426 in wire-ios-data-model/Source/ManagedObjectContext/CoreDataStack.swift

View workflow job for this annotation

GitHub Actions / Test Results

Capture of 'self' with non-Sendable type 'CoreDataStack' in a '@Sendable' closure

Capture of 'self' with non-Sendable type 'CoreDataStack' in a '@sendable' closure
self.dispatchGroup.map(context.addGroup(_:))
}
}
Expand Down Expand Up @@ -559,7 +552,7 @@
}

// MARK: -

Check warning on line 555 in wire-ios-data-model/Source/ManagedObjectContext/CoreDataStack.swift

View workflow job for this annotation

GitHub Actions / Test Results

Class 'PersistentContainer' must restate inherited '@unchecked Sendable' conformance

Class 'PersistentContainer' must restate inherited '@unchecked Sendable' conformance
public class PersistentContainer: NSPersistentContainer {

var storeURL: URL? {
Expand Down

This file was deleted.

48 changes: 18 additions & 30 deletions wire-ios-data-model/Source/Model/User/ZMUser+LegalHoldRequest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@
//

import Foundation
import WireCryptobox
import WireLogging

private let log = ZMSLog(tag: "UserClient")
private let log = WireLogger.userClient

public typealias SelfUserLegalHoldable = EditableUserType & SelfLegalHoldSubject & UserType

Expand Down Expand Up @@ -227,8 +227,8 @@

public func addLegalHoldClient(from request: LegalHoldRequest) async -> UserClient? {
guard
let context = managedObjectContext,

Check warning on line 230 in wire-ios-data-model/Source/Model/User/ZMUser+LegalHoldRequest.swift

View workflow job for this annotation

GitHub Actions / Test Results

Capture of 'self' with non-Sendable type 'ZMUser' in a '@Sendable' closure

Capture of 'self' with non-Sendable type 'ZMUser' in a '@sendable' closure
let selfClient = await context.perform({ self.selfClient() }),

Check warning on line 231 in wire-ios-data-model/Source/Model/User/ZMUser+LegalHoldRequest.swift

View workflow job for this annotation

GitHub Actions / Test Results

Capture of 'self' with non-Sendable type 'ZMUser' in a '@Sendable' closure

Capture of 'self' with non-Sendable type 'ZMUser' in a '@sendable' closure

Check warning on line 231 in wire-ios-data-model/Source/Model/User/ZMUser+LegalHoldRequest.swift

View workflow job for this annotation

GitHub Actions / Test Results

Capture of 'request' with non-Sendable type 'LegalHoldRequest' in a '@Sendable' closure

Capture of 'request' with non-Sendable type 'LegalHoldRequest' in a '@sendable' closure
let legalHoldClient = await context.perform({ self.insertLegalHoldClient(from: request, in: context) })
else { return nil }

Expand All @@ -236,7 +236,7 @@
legalHoldClient,
usingPreKey: request.lastPrekey.key.base64String()
) else {
log.error("Could not establish session with new legal hold device.")

Check warning on line 239 in wire-ios-data-model/Source/Model/User/ZMUser+LegalHoldRequest.swift

View workflow job for this annotation

GitHub Actions / Test Results

Capture of 'legalHoldClient' with non-Sendable type 'UserClient' in a '@Sendable' closure

Capture of 'legalHoldClient' with non-Sendable type 'UserClient' in a '@sendable' closure
await context.perform { context.delete(legalHoldClient) }
return nil
}
Expand Down Expand Up @@ -285,37 +285,25 @@

public var fingerprint: String? {
get async {
guard let (syncContext, prekey) = await managedObjectContext?.perform({
(self.managedObjectContext?.zm_sync, self.legalHoldRequest?.lastPrekey)
}), let syncContext, let prekey else { return nil }

let proteusProvider = await syncContext.perform { syncContext.proteusProvider }
return await proteusProvider.performAsync { proteusService in
await fetchFingerprint(for: prekey, through: proteusService)
} withKeyStore: { keyStore in
fetchFingerprint(for: prekey, through: keyStore)
let syncContext = await managedObjectContext?.perform { [managedObjectContext] in
managedObjectContext?.zm_sync
}
}
}

private func fetchFingerprint(
for prekey: LegalHoldRequest.Prekey,
through proteusService: ProteusServiceInterface
) async -> String? {
do {
return try await proteusService.fingerprint(fromPrekey: prekey.key.base64EncodedString())
} catch {
log.error("Could not fetch fingerprint for \(self)")
return nil
}
}
guard
let syncContext,
let prekey = legalHoldRequest?.lastPrekey,
let proteusService = await syncContext.perform({ syncContext.proteusService })
else {
return nil
}

private func fetchFingerprint(
for prekey: LegalHoldRequest.Prekey,
through keystore: UserClientKeysStore
) -> String? {
guard let fingerprintData = EncryptionSessionsDirectory.fingerprint(fromPrekey: prekey.key) else { return nil }
return String(decoding: fingerprintData, as: UTF8.self)
do {
return try await proteusService.fingerprint(fromPrekey: prekey.key.base64EncodedString())
} catch {
log.error("Could not fetch fingerprint for \(self)")
return nil
}
}
}

}
118 changes: 11 additions & 107 deletions wire-ios-data-model/Source/Model/UserClient/UserClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@
try await deleteSession()
} catch {
WireLogger.userClient.error("error deleting session: \(String(reflecting: error))")
}

Check warning on line 238 in wire-ios-data-model/Source/Model/UserClient/UserClient.swift

View workflow job for this annotation

GitHub Actions / Test Results

Capture of 'self' with non-Sendable type 'UserClient' in a '@Sendable' closure

Capture of 'self' with non-Sendable type 'UserClient' in a '@sendable' closure
await managedObjectContext?.perform { self.deleteClient() }
}

Expand Down Expand Up @@ -278,30 +278,15 @@

public var hasSessionWithSelfClient: Bool {
get async {
guard

Check warning on line 281 in wire-ios-data-model/Source/Model/UserClient/UserClient.swift

View workflow job for this annotation

GitHub Actions / Test Results

Capture of 'self' with non-Sendable type 'UserClient' in a '@Sendable' closure

Capture of 'self' with non-Sendable type 'UserClient' in a '@sendable' closure
let sessionID = await managedObjectContext?.perform({ self.proteusSessionID }),
let proteusProvider = await managedObjectContext?
.perform({ self.managedObjectContext?.proteusProvider })
let proteusService = await managedObjectContext?
.perform({ [managedObjectContext] in managedObjectContext?.proteusService })
else {
return false
}

var hasSession = false

await proteusProvider.performAsync(
withProteusService: { proteusService in
hasSession = await proteusService.sessionExists(id: sessionID)
},
withKeyStore: { keyStore in
managedObjectContext?.performAndWait {
keyStore.encryptionContext.perform { sessionsDirectory in
hasSession = sessionsDirectory.hasSession(for: sessionID.mapToEncryptionSessionID())
}
}
}
)

return hasSession
return await proteusService.sessionExists(id: sessionID)
}
}

Expand All @@ -317,7 +302,7 @@
}

WaitingGroupTask(context: syncMOC) {
guard let syncClient = await syncMOC.perform({

Check warning on line 305 in wire-ios-data-model/Source/Model/UserClient/UserClient.swift

View workflow job for this annotation

GitHub Actions / Test Results

Capture of 'self' with non-Sendable type 'UserClient' in a '@Sendable' closure

Capture of 'self' with non-Sendable type 'UserClient' in a '@sendable' closure
(try? syncMOC.existingObject(with: self.objectID)) as? UserClient
}) else {
return
Expand All @@ -328,7 +313,7 @@

// Delete should happen on sync context since the cryptobox could be accessed only from there
await syncMOC.perform {
// Mark that we need notify the other party about the session reset

Check warning on line 316 in wire-ios-data-model/Source/Model/UserClient/UserClient.swift

View workflow job for this annotation

GitHub Actions / Test Results

Capture of 'syncClient' with non-Sendable type 'UserClient' in a '@Sendable' closure

Capture of 'syncClient' with non-Sendable type 'UserClient' in a '@sendable' closure
syncClient.needsToNotifyOtherUserAboutSessionReset = true

syncMOC.saveOrRollback()
Expand Down Expand Up @@ -570,121 +555,40 @@
/// If there is no session it does nothing
func deleteSession() async throws {
guard
let context = managedObjectContext,

Check warning on line 558 in wire-ios-data-model/Source/Model/UserClient/UserClient.swift

View workflow job for this annotation

GitHub Actions / Test Results

Capture of 'self' with non-Sendable type 'UserClient' in a '@Sendable' closure

Capture of 'self' with non-Sendable type 'UserClient' in a '@sendable' closure
await context.perform({ !self.isSelfClient() }),

Check warning on line 559 in wire-ios-data-model/Source/Model/UserClient/UserClient.swift

View workflow job for this annotation

GitHub Actions / Test Results

Capture of 'self' with non-Sendable type 'UserClient' in a '@Sendable' closure

Capture of 'self' with non-Sendable type 'UserClient' in a '@sendable' closure
let sessionID = await context.perform({ self.proteusSessionID })
let sessionID = await context.perform({ self.proteusSessionID }),
let proteusService = await context.perform({ context.proteusService })
else {
return
}
let proteusProvider = await context.perform { context.proteusProvider }
try await proteusProvider.performAsync(
withProteusService: { proteusService in
try await proteusService.deleteSession(id: sessionID)
},
withKeyStore: { keyStore in
keyStore.encryptionContext.perform { sessionsDirectory in
sessionsDirectory.delete(sessionID.mapToEncryptionSessionID())
}
}
)
try await proteusService.deleteSession(id: sessionID)
}

func establishSessionWithClient(
_ client: UserClient,
usingPreKey preKey: String
) async -> Bool {
guard
let proteusProvider = await managedObjectContext?.perform({ self.managedObjectContext?.proteusProvider }),
let sessionId = await managedObjectContext?.perform({ client.sessionIdentifier })
let context = managedObjectContext,
let proteusService = await context.perform({ context.proteusService }),

Check warning on line 574 in wire-ios-data-model/Source/Model/UserClient/UserClient.swift

View workflow job for this annotation

GitHub Actions / Test Results

Capture of 'client' with non-Sendable type 'UserClient' in a '@Sendable' closure

Capture of 'client' with non-Sendable type 'UserClient' in a '@sendable' closure
let sessionId = await context.perform({ client.proteusSessionID })
else {
return false
}

return await establishSessionWithClient(
sessionId: sessionId,
usingPreKey: preKey,
proteusProviding: proteusProvider
)
}

/// Creates a session between the selfClient and the given userClient
/// Returns false if the session could not be established
/// Use this method only for the selfClient
func establishSessionWithClient(
sessionId: EncryptionSessionIdentifier,
usingPreKey preKey: String,
proteusProviding: ProteusProviding
) async -> Bool {
await proteusProviding.performAsync { proteusService in
await establishSession(
through: proteusService,
sessionId: sessionId,
preKey: preKey
)
} withKeyStore: { keystore in
establishSession(
through: keystore,
sessionId: sessionId,
preKey: preKey
)
}
}

private func establishSession(
through proteusService: ProteusServiceInterface,
sessionId: EncryptionSessionIdentifier,
preKey: String
) async -> Bool {
do {
// swiftlint:disable:next todo_requires_jira_link
// TODO: check if we should delete session if it exists before creating new one
let proteusSessionId = ProteusSessionID(
domain: sessionId.domain,
userID: sessionId.userId,
clientID: sessionId.clientId
)
try await proteusService.establishSession(id: proteusSessionId, fromPrekey: preKey)

try await proteusService.establishSession(id: sessionId, fromPrekey: preKey)
return true
} catch {
zmLog.error("Cannot create session for prekey \(preKey): \(String(describing: error))")
return false
}
}

func establishSession(
through keystore: UserClientKeysStore,
sessionId: EncryptionSessionIdentifier,
preKey: String
) -> Bool {
var didEstablishSession = false
managedObjectContext?.performAndWait {

keystore.encryptionContext.perform { sessionsDirectory in

// Session is already established?
if sessionsDirectory.hasSession(for: sessionId) {
zmLog.debug("Session with \(sessionId) was already established, re-creating")
sessionsDirectory.delete(sessionId)
}
}

// Because of caching within the `perform` block, it commits to disk only at the end of a block.
// I don't think the cache is smart enough to perform the sum of operations (delete + recreate)
// if at the end of the block the session is still there. Just to be safe, I split the operations
// in two separate `perform` blocks.

keystore.encryptionContext.perform { sessionsDirectory in
do {
try sessionsDirectory.createClientSession(sessionId, base64PreKeyString: preKey)
didEstablishSession = true
} catch {
zmLog.error("Cannot create session for prekey \(preKey)")
}
}
}
return didEstablishSession
}

/// Use this method only for the selfClient
@objc
func decrementNumberOfRemainingProteusKeys() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import Foundation

public extension UserClient {
override var description: String {
"Client: \(String(describing: sessionIdentifier?.rawValue)), user name: \(String(describing: user?.name)) email: \(String(describing: user?.emailAddress)) platform: \(String(describing: deviceClass)), label: \(String(describing: label)), model: \(String(describing: model))"
"Client: \(String(describing: proteusSessionID?.rawValue)), user name: \(String(describing: user?.name)) email: \(String(describing: user?.emailAddress)) platform: \(String(describing: deviceClass)), label: \(String(describing: label)), model: \(String(describing: model))"
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@ public class CryptoboxMigrationManager: CryptoboxMigrationManagerInterface {
// MARK: - Methods

public func isMigrationNeeded(accountDirectory: URL) -> Bool {
guard DeveloperFlag.proteusViaCoreCrypto.isOn else { return false }
let cryptoboxDirectory = fileManager.cryptoboxDirectory(in: accountDirectory)
return fileManager.fileExists(atPath: cryptoboxDirectory.path)
}
Expand Down
Loading
Loading