Simplify sendVerificationStateSyncMessage

This commit is contained in:
Max Radermacher 2026-01-26 14:19:48 -06:00 committed by GitHub
parent 0f8f40ee0a
commit 207023272a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 12 additions and 137 deletions

View File

@ -46,36 +46,12 @@ final class OutgoingVerificationStateSyncMessage: OutgoingSyncMessage {
override class var supportsSecureCoding: Bool { true }
override func encode(with coder: NSCoder) {
super.encode(with: coder)
coder.encode(self.identityKey, forKey: "identityKey")
coder.encode(NSNumber(value: self.paddingBytesLength), forKey: "paddingBytesLength")
coder.encode(self.verificationForRecipientAddress, forKey: "verificationForRecipientAddress")
coder.encode(NSNumber(value: self.verificationState.rawValue), forKey: "verificationState")
owsFail("Doesn't support serialization.")
}
required init?(coder: NSCoder) {
guard let identityKey = coder.decodeObject(of: NSData.self, forKey: "identityKey") as Data? else {
return nil
}
self.identityKey = identityKey
guard let paddingBytesLength = coder.decodeObject(of: NSNumber.self, forKey: "paddingBytesLength") else {
return nil
}
self.paddingBytesLength = paddingBytesLength.uintValue
let modernAddress = coder.decodeObject(of: SignalServiceAddress.self, forKey: "verificationForRecipientAddress")
self.verificationForRecipientAddress = modernAddress ?? SignalServiceAddress.legacyAddress(
serviceIdString: nil,
phoneNumber: coder.decodeObject(of: NSString.self, forKey: "verificationForRecipientId") as String?,
)
owsAssertDebug(self.verificationForRecipientAddress.isValid)
guard
let rawVerificationState = coder.decodeObject(of: NSNumber.self, forKey: "verificationState"),
let verificationState = OWSVerificationState(rawValue: rawVerificationState.uint64Value)
else {
return nil
}
self.verificationState = verificationState
super.init(coder: coder)
// Doesn't support serialization.
return nil
}
override var hash: Int {
@ -122,24 +98,5 @@ final class OutgoingVerificationStateSyncMessage: OutgoingSyncMessage {
return syncMessageBuilder
}
func unpaddedVerifiedLength() -> UInt {
guard let verificationForRecipientAci = self.verificationForRecipientAddress.aci else {
return 0
}
let verifiedProto = OWSRecipientIdentity.buildVerifiedProto(
destinationAci: verificationForRecipientAci,
identityKey: self.identityKey,
verificationState: self.verificationState,
paddingBytesLength: 0,
)
do {
return UInt(try verifiedProto.serializedData().count)
} catch {
owsFailDebug("could not serialize protobuf.")
return 0
}
}
override var isUrgent: Bool { false }
}

View File

@ -686,55 +686,22 @@ public class OWSIdentityManagerImpl: OWSIdentityManager {
}
private func sendVerificationStateSyncMessage(for recipientUniqueId: RecipientUniqueId, message: OutgoingVerificationStateSyncMessage) {
let address = message.verificationForRecipientAddress
let contactThread = TSContactThread.getOrCreateThread(contactAddress: address)
// DURABLE CLEANUP - we could replace the custom durability logic in this
// class with a durable JobQueue.
let nullMessagePromise = db.write { tx in
// Send null message to appear as though we're sending a normal message to
// cover the sync message sent subsequently
let nullMessage = OutgoingNullMessage(
contactThread: contactThread,
verificationStateSyncMessage: message,
tx: tx,
)
let syncMessagePromise = self.db.write { tx in
let preparedMessage = PreparedOutgoingMessage.preprepared(
transientMessageWithoutAttachments: nullMessage,
transientMessageWithoutAttachments: message,
)
return messageSenderJobQueue.add(
return self.messageSenderJobQueue.add(
.promise,
message: preparedMessage,
limitToCurrentProcessLifetime: true,
transaction: tx,
)
}
nullMessagePromise.done(on: DispatchQueue.global()) {
Logger.info("Successfully sent verification state NullMessage")
let syncMessagePromise = self.db.write { tx in
let preparedMessage = PreparedOutgoingMessage.preprepared(
transientMessageWithoutAttachments: message,
)
return self.messageSenderJobQueue.add(
.promise,
message: preparedMessage,
limitToCurrentProcessLifetime: true,
transaction: tx,
)
}
syncMessagePromise.done(on: DispatchQueue.global()) {
Logger.info("Successfully sent verification state sync message")
self.db.write { tx in self.clearSyncMessage(for: recipientUniqueId, tx: tx) }
}.catch(on: DispatchQueue.global()) { error in
Logger.error("Failed to send verification state sync message: \(error)")
}
syncMessagePromise.done(on: DispatchQueue.global()) {
Logger.info("Successfully sent verification state sync message")
self.db.write { tx in self.clearSyncMessage(for: recipientUniqueId, tx: tx) }
}.catch(on: DispatchQueue.global()) { error in
Logger.error("Failed to send verification state NullMessage: \(error)")
if error is MessageSenderNoSuchSignalRecipientError {
Logger.info("Removing retries for syncing verification for unregistered user: \(address)")
self.db.write { tx in self.clearSyncMessage(for: recipientUniqueId, tx: tx) }
}
Logger.error("Failed to send verification state sync message: \(error)")
}
}

View File

@ -8,10 +8,7 @@ import Foundation
@objc(OWSOutgoingNullMessage)
final class OutgoingNullMessage: TransientOutgoingMessage {
let verificationStateSyncMessage: OutgoingVerificationStateSyncMessage?
init(contactThread: TSContactThread, verificationStateSyncMessage: OutgoingVerificationStateSyncMessage? = nil, tx: DBReadTransaction) {
self.verificationStateSyncMessage = verificationStateSyncMessage
init(contactThread: TSContactThread, tx: DBReadTransaction) {
let messageBuilder = TSOutgoingMessageBuilder.outgoingMessageBuilder(thread: contactThread)
super.init(
outgoingMessageWith: messageBuilder,
@ -24,59 +21,13 @@ final class OutgoingNullMessage: TransientOutgoingMessage {
override class var supportsSecureCoding: Bool { true }
override func encode(with coder: NSCoder) {
super.encode(with: coder)
if let verificationStateSyncMessage {
coder.encode(verificationStateSyncMessage, forKey: "verificationStateSyncMessage")
}
}
required init?(coder: NSCoder) {
self.verificationStateSyncMessage = coder.decodeObject(of: OutgoingVerificationStateSyncMessage.self, forKey: "verificationStateSyncMessage")
super.init(coder: coder)
}
override var hash: Int {
var hasher = Hasher()
hasher.combine(super.hash)
hasher.combine(self.verificationStateSyncMessage)
return hasher.finalize()
}
override func isEqual(_ object: Any?) -> Bool {
guard let object = object as? Self else { return false }
guard super.isEqual(object) else { return false }
guard self.verificationStateSyncMessage == object.verificationStateSyncMessage else { return false }
return true
}
override func contentBuilder(thread: TSThread, transaction: DBReadTransaction) -> SSKProtoContentBuilder? {
let nullMessageBuilder = SSKProtoNullMessage.builder()
if let verificationStateSyncMessage {
var contentLength = verificationStateSyncMessage.unpaddedVerifiedLength()
owsAssertDebug(verificationStateSyncMessage.paddingBytesLength > 0)
// We add the same amount of padding in the VerificationStateSync message
// and its corresponding NullMessage so that the sync message is
// indistinguishable from an outgoing Sent transcript corresponding to the
// NullMessage. We pad the NullMessage so as to obscure its content. The
// sync message (like all sync messages) will be *additionally* padded by
// the superclass while being sent. The end result is we send a NullMessage
// of a non-distinct size, and a verification sync which is ~1-512 bytes
// larger than that.
contentLength += verificationStateSyncMessage.paddingBytesLength
owsAssertDebug(contentLength > 0)
nullMessageBuilder.setPadding(Randomness.generateRandomBytes(contentLength))
}
let nullMessage = nullMessageBuilder.buildInfallibly()
let contentBuilder = SSKProtoContent.builder()
contentBuilder.setNullMessage(nullMessage)
contentBuilder.setNullMessage(SSKProtoNullMessage.builder().buildInfallibly())
return contentBuilder
}