1// GroupCryptoMigration.swift2// SophaxChatCore3//4// Phase 5: Opt-in migration from Sender Keys v2 to MLS for existing groups.5//6// Design:7// - Only the group creator can initiate migration (they are the MLS coordinator).8// - All current members must have an MLS KeyPackage available (from their Hello bundle).9// - A new MLS group is created with the same name and members; the old SKv2 group is10// left in place so existing message history remains accessible.11// - SKv2 Keychain state for the old group expires naturally (7-day rotation window).12// - The UI flow for presenting migration to users is a future sprint concern.1314import Foundation1516// MARK: - Result type1718public enum GroupMigrationResult: Sendable {19/// The group is already running MLS — nothing to do.20 case notNeeded21/// One or more members lack an MLS KeyPackage; wait until they reconnect.22 case requiresAllOnline([String])// peerIDs that are missing a KeyPackage23/// Migration initiated — new MLS group was created and Welcomes are being distributed.24 case initiated(newGroup: GroupInfo)25}2627// MARK: - Migration entry point2829/// Attempt to migrate `group` from Sender Keys v2 to MLS.30///31/// - Parameters:32/// - group: The existing SKv2 GroupInfo to migrate.33/// - chatManager: The active ChatManager instance (must be the group creator's).34/// - Returns: A `GroupMigrationResult` describing the outcome.35/// - Throws: If the local identity's signing key is unavailable.36@discardableResult37public func migrateGroupToMLS(38 _ group: GroupInfo,39 in chatManager: ChatManager40) throws -> GroupMigrationResult {4142// Already MLS — nothing to do.43 guard group.cryptoVersion ==.senderKeysV2 else { return .notNeeded }4445// Only the coordinator (creator) may initiate MLS migration.46 let myID = chatManager.identity.publicIdentity.peerID47 guard group.creatorID == myID else {48 throw SophaxError.invalidMessageFormat("Only the group creator can initiate MLS migration")49}5051// Verify every non-self member has a KeyPackage in their stored PreKeyBundle.52 let otherMembers = group.memberIDs.filter { $0 != myID }53 let missing = otherMembers.filter { peerID in54 chatManager.peerBundles[peerID]?.mlsKeyPackage == nil55}56 guard missing.isEmpty else { return .requiresAllOnline(missing)}5758// Create the new MLS group (same name, same members, new groupID).59 guard let newGroup = chatManager.createMLSGroup(60 name: group.name,61 memberPeerIDs: otherMembers62) else {63 throw SophaxError.encryptionFailed("MLS group creation failed during migration")64}6566 return .initiated(newGroup: newGroup)67}