From 881fb4d0c8884c03e28e517842cf2c0210112f73 Mon Sep 17 00:00:00 2001 From: Pete Walters Date: Tue, 22 Jul 2025 14:16:58 -0500 Subject: [PATCH] Asyncify RegistrationCoordinator backupTo/restoreFrom SVR --- .../RegistrationCoordinatorImpl.swift | 211 +++++++++--------- 1 file changed, 103 insertions(+), 108 deletions(-) diff --git a/Signal/Registration/RegistrationCoordinatorImpl.swift b/Signal/Registration/RegistrationCoordinatorImpl.swift index ed5e6837ee..d218ce09e3 100644 --- a/Signal/Registration/RegistrationCoordinatorImpl.swift +++ b/Signal/Registration/RegistrationCoordinatorImpl.swift @@ -3487,7 +3487,9 @@ public class RegistrationCoordinatorImpl: RegistrationCoordinator { !persistedState.hasGivenUpTryingToRestoreWithSVR { // If we have no SVR data, fetch it. - return restoreSVRBackupPostRegistration(pin: pin, accountIdentity: accountIdentity) + return Guarantee.wrapAsync { + await self.restoreSVRBackupPostRegistration(pin: pin, accountIdentity: accountIdentity) + } } return nil } @@ -3505,7 +3507,9 @@ public class RegistrationCoordinatorImpl: RegistrationCoordinator { if !persistedState.hasSkippedPinEntry { if inMemoryState.shouldBackUpToSVR { // If we haven't backed up, do so now. - return backupToSVR(pin: pin, accountIdentity: accountIdentity) + return Guarantee.wrapAsync { + await self.backupToSVR(pin: pin, accountIdentity: accountIdentity) + } } switch attributes2FAMode(e164: accountIdentity.e164) { @@ -3523,11 +3527,12 @@ public class RegistrationCoordinatorImpl: RegistrationCoordinator { return nil } + @MainActor private func restoreSVRBackupPostRegistration( pin: String, accountIdentity: AccountIdentity, retriesLeft: Int = Constants.networkErrorRetries - ) -> Guarantee { + ) async -> RegistrationStep { Logger.info("") let backupAuthMethod = SVR.AuthMethod.chatServerAuth(accountIdentity.authedAccount) @@ -3537,82 +3542,78 @@ public class RegistrationCoordinatorImpl: RegistrationCoordinator { } else { authMethod = backupAuthMethod } - return deps.svr - .restoreKeys( - pin: pin, - authMethod: authMethod - ) - .then(on: DispatchQueue.main) { [weak self] result -> Guarantee in - guard let self else { - return unretainedSelfError() - } - switch result { - case .success(let masterKey): - self.inMemoryState.shouldRestoreSVRMasterKeyAfterRegistration = false - self.db.write { tx in - self.updatePersistedState(tx) { $0.recoveredSVRMasterKey = masterKey } - } - return self.nextStep() - case let .invalidPin(remainingAttempts): - return .value(.pinEntry(RegistrationPinState( - operation: .enteringExistingPin( - skippability: .canSkipAndCreateNew, - remainingAttempts: UInt(remainingAttempts) - ), - error: .wrongPin(wrongPin: pin), - contactSupportMode: self.contactSupportRegistrationPINMode(), - exitConfiguration: self.pinCodeEntryExitConfiguration() - ))) - case .backupMissing: - // If we are unable to talk to SVR, it got wiped and we can't - // recover. Keep going like if nothing happened. - self.inMemoryState.pinFromUser = nil - self.inMemoryState.shouldRestoreSVRMasterKeyAfterRegistration = false - self.db.write { tx in - self.updatePersistedState(tx) { $0.hasGivenUpTryingToRestoreWithSVR = true } - } - return .value(.pinAttemptsExhaustedWithoutReglock( - .init(mode: .restoringBackup) - )) - case .networkError: - if retriesLeft > 0 { - return self.restoreSVRBackupPostRegistration( - pin: pin, - accountIdentity: accountIdentity, - retriesLeft: retriesLeft - 1 - ) - } - return .value(.showErrorSheet(.networkError)) - case .genericError(let error): - if error.isPostRegDeregisteredError { - return self.becameDeregisteredBeforeCompleting(accountIdentity: accountIdentity) - } else if retriesLeft > 0 { - return self.restoreSVRBackupPostRegistration( - pin: pin, - accountIdentity: accountIdentity, - retriesLeft: retriesLeft - 1 - ) - } else { - self.inMemoryState.pinFromUser = nil - return .value(.pinEntry(RegistrationPinState( - operation: .enteringExistingPin( - skippability: .canSkipAndCreateNew, - remainingAttempts: nil - ), - error: .serverError, - contactSupportMode: self.contactSupportRegistrationPINMode(), - exitConfiguration: self.pinCodeEntryExitConfiguration() - ))) - } - } + let result = await deps.svr.restoreKeys( + pin: pin, + authMethod: authMethod + ).awaitable() + + switch result { + case .success(let masterKey): + inMemoryState.shouldRestoreSVRMasterKeyAfterRegistration = false + await db.awaitableWrite { tx in + updatePersistedState(tx) { $0.recoveredSVRMasterKey = masterKey } } + return await nextStep().awaitable() + case let .invalidPin(remainingAttempts): + return .pinEntry(RegistrationPinState( + operation: .enteringExistingPin( + skippability: .canSkipAndCreateNew, + remainingAttempts: UInt(remainingAttempts) + ), + error: .wrongPin(wrongPin: pin), + contactSupportMode: contactSupportRegistrationPINMode(), + exitConfiguration: pinCodeEntryExitConfiguration() + )) + case .backupMissing: + // If we are unable to talk to SVR, it got wiped and we can't + // recover. Keep going like if nothing happened. + inMemoryState.pinFromUser = nil + inMemoryState.shouldRestoreSVRMasterKeyAfterRegistration = false + await db.awaitableWrite { tx in + updatePersistedState(tx) { $0.hasGivenUpTryingToRestoreWithSVR = true } + } + return .pinAttemptsExhaustedWithoutReglock( + .init(mode: .restoringBackup) + ) + case .networkError: + if retriesLeft > 0 { + return await restoreSVRBackupPostRegistration( + pin: pin, + accountIdentity: accountIdentity, + retriesLeft: retriesLeft - 1 + ) + } + return .showErrorSheet(.networkError) + case .genericError(let error): + if error.isPostRegDeregisteredError { + return await becameDeregisteredBeforeCompleting(accountIdentity: accountIdentity).awaitable() + } else if retriesLeft > 0 { + return await restoreSVRBackupPostRegistration( + pin: pin, + accountIdentity: accountIdentity, + retriesLeft: retriesLeft - 1 + ) + } else { + self.inMemoryState.pinFromUser = nil + return .pinEntry(RegistrationPinState( + operation: .enteringExistingPin( + skippability: .canSkipAndCreateNew, + remainingAttempts: nil + ), + error: .serverError, + contactSupportMode: self.contactSupportRegistrationPINMode(), + exitConfiguration: self.pinCodeEntryExitConfiguration() + )) + } + } } + @MainActor private func backupToSVR( pin: String, accountIdentity: AccountIdentity, retriesLeft: Int = Constants.networkErrorRetries - ) -> Guarantee { + ) async -> RegistrationStep { Logger.info("") let authMethod: SVR.AuthMethod @@ -3628,50 +3629,44 @@ public class RegistrationCoordinatorImpl: RegistrationCoordinator { guard let masterKey else { Logger.error("Failed to back up to SVR due to missing root key") self.inMemoryState.didSkipSVRBackup = true - return .value(.showErrorSheet(.genericError)) + return .showErrorSheet(.genericError) } - return deps.svr - .backupMasterKey( + do { + let masterKey = try await deps.svr.backupMasterKey( pin: pin, masterKey: masterKey, authMethod: authMethod - ) - .then(on: DispatchQueue.main) { [weak self] masterKey -> Guarantee in - guard let strongSelf = self else { - return unretainedSelfError() - } - strongSelf.inMemoryState.hasBackedUpToSVR = true - strongSelf.db.write { tx in - Logger.info("Setting pin code after SVR backup") - strongSelf.updateMasterKeyAndLocalState( - masterKey: masterKey, - tx: tx + ).awaitable() + + inMemoryState.hasBackedUpToSVR = true + await db.awaitableWrite { tx in + Logger.info("Setting pin code after SVR backup") + updateMasterKeyAndLocalState( + masterKey: masterKey, + tx: tx + ) + deps.ows2FAManager.markPinEnabled(pin, tx) + } + + return await nextStep().awaitable() + } catch { + if error.isNetworkFailureOrTimeout { + if retriesLeft > 0 { + return await backupToSVR( + pin: pin, + accountIdentity: accountIdentity, + retriesLeft: retriesLeft - 1 ) - strongSelf.deps.ows2FAManager.markPinEnabled(pin, tx) } - return strongSelf.nextStep() - } - .recover(on: DispatchQueue.main) { [weak self] error -> Guarantee in - guard let self else { - return unretainedSelfError() - } - if error.isNetworkFailureOrTimeout { - if retriesLeft > 0 { - return self.backupToSVR( - pin: pin, - accountIdentity: accountIdentity, - retriesLeft: retriesLeft - 1 - ) - } - return .value(.showErrorSheet(.networkError)) - } - Logger.error("Failed to back up to SVR with error: \(error)") - // We want to let people get through registration even if backups - // go wrong. Show an error but let the user continue when they try the next step. - self.inMemoryState.didSkipSVRBackup = true - return .value(.showErrorSheet(.genericError)) + return .showErrorSheet(.networkError) } + Logger.error("Failed to back up to SVR with error: \(error)") + // We want to let people get through registration even if backups + // go wrong. Show an error but let the user continue when they try the next step. + inMemoryState.didSkipSVRBackup = true + return .showErrorSheet(.genericError) + } } private func restoreFromStorageService(