Signal-iOS/SignalServiceKit/src/Account/CreatePreKeysOperation.swift
Jordan Rose 36ec5454e9 Reset the identity key if initial pre-key creation fails
If the CreatePreKeysOperation fails on registration, we don't ever
retry it, and registration is considered to have failed anyway. If it
fails for the first time we create PNI keys, though, the identity key
will still be saved locally, and therefore we won't try again until
regular pre-key rotation happens. To guard against this, wipe a
newly-generated identity key if the upload to the service fails.
2022-04-22 18:11:42 -07:00

80 lines
3.7 KiB
Swift

//
// Copyright (c) 2022 Open Whisper Systems. All rights reserved.
//
import Foundation
@objc(SSKCreatePreKeysOperation)
public class CreatePreKeysOperation: OWSOperation {
private let identity: OWSIdentity
@objc(initForIdentity:)
public init(for identity: OWSIdentity) {
self.identity = identity
}
public override func run() {
Logger.debug("")
let identityKeyPair: ECKeyPair
let isNewIdentityKey: Bool
if let existingIdentityKeyPair = identityManager.identityKeyPair(for: identity) {
identityKeyPair = existingIdentityKeyPair
isNewIdentityKey = false
} else if tsAccountManager.isPrimaryDevice {
identityKeyPair = identityManager.generateNewIdentityKey(for: identity)
isNewIdentityKey = true
} else {
Logger.warn("cannot create \(identity) pre-keys; missing identity key")
owsAssertDebug(identity != .aci)
self.reportCancelled()
return
}
let signalProtocolStore = self.signalProtocolStore(for: identity)
let signedPreKeyRecord: SignedPreKeyRecord = signalProtocolStore.signedPreKeyStore.generateRandomSignedRecord()
let preKeyRecords: [PreKeyRecord] = signalProtocolStore.preKeyStore.generatePreKeyRecords()
let identityKey: Data = identityKeyPair.publicKey
self.databaseStorage.write { transaction in
signalProtocolStore.signedPreKeyStore.storeSignedPreKey(signedPreKeyRecord.id,
signedPreKeyRecord: signedPreKeyRecord,
transaction: transaction)
signalProtocolStore.preKeyStore.storePreKeyRecords(preKeyRecords, transaction: transaction)
}
firstly(on: .global()) { () -> Promise<Void> in
guard self.tsAccountManager.isRegisteredAndReady else {
return Promise.value(())
}
return self.messageProcessor.fetchingAndProcessingCompletePromise()
}.then(on: .global()) { () -> Promise<Void> in
self.accountServiceClient.setPreKeys(for: self.identity,
identityKey: identityKey,
signedPreKeyRecord: signedPreKeyRecord,
preKeyRecords: preKeyRecords)
}.done {
signedPreKeyRecord.markAsAcceptedByService()
self.databaseStorage.write { transaction in
signalProtocolStore.signedPreKeyStore.storeSignedPreKey(signedPreKeyRecord.id,
signedPreKeyRecord: signedPreKeyRecord,
transaction: transaction)
signalProtocolStore.signedPreKeyStore.setCurrentSignedPrekeyId(signedPreKeyRecord.id,
transaction: transaction)
}
Logger.debug("done")
self.reportSuccess()
}.catch { error in
if isNewIdentityKey && self.identity != .aci {
// Clear out the identity key we just generated if it failed to upload.
// (Except if it's the ACI identity key, because we're not allowed to clear that without a full reset.)
self.databaseStorage.write { transaction in
self.identityManager.storeIdentityKeyPair(nil, for: self.identity, transaction: transaction)
}
}
self.reportError(withUndefinedRetry: error)
}
}
}