diff --git a/SignalServiceKit/Backups/Archiving/Archivers/AccountData/BackupArchiveAccountDataArchiver.swift b/SignalServiceKit/Backups/Archiving/Archivers/AccountData/BackupArchiveAccountDataArchiver.swift index d1bfd979f4..26c3e73b60 100644 --- a/SignalServiceKit/Backups/Archiving/Archivers/AccountData/BackupArchiveAccountDataArchiver.swift +++ b/SignalServiceKit/Backups/Archiving/Archivers/AccountData/BackupArchiveAccountDataArchiver.swift @@ -162,15 +162,6 @@ public class BackupArchiveAccountDataArchiver: BackupArchiveProtoStreamWriter { accountData.svrPin = pin } - if - let keyTransparencyBlob = keyTransparencyStore.getKeyTransparencyBlob( - aci: context.localIdentifiers.aci, - tx: context.tx, - ) - { - accountData.keyTransparencyData = keyTransparencyBlob - } - if let isSystemCallLogEnabled = preferences.isSystemCallLogEnabled(tx: context.tx) { var iosSpecificSettings = BackupProto_AccountData.IOSSpecificSettings() iosSpecificSettings.isSystemCallLogEnabled = isSystemCallLogEnabled @@ -637,14 +628,6 @@ public class BackupArchiveAccountDataArchiver: BackupArchiveProtoStreamWriter { ows2FAManager.restorePinFromBackup(accountData.svrPin, tx: context.tx) } - if accountData.hasKeyTransparencyData { - keyTransparencyStore.setKeyTransparencyBlob( - accountData.keyTransparencyData, - aci: context.localIdentifiers.aci, - tx: context.tx, - ) - } - if accountData.hasIosSpecificSettings { preferences.setIsSystemCallLogEnabled( accountData.iosSpecificSettings.isSystemCallLogEnabled, diff --git a/SignalServiceKit/KeyTransparency/KeyTransparencyManager.swift b/SignalServiceKit/KeyTransparency/KeyTransparencyManager.swift index f800e03845..e44f641a3c 100644 --- a/SignalServiceKit/KeyTransparency/KeyTransparencyManager.swift +++ b/SignalServiceKit/KeyTransparency/KeyTransparencyManager.swift @@ -21,6 +21,7 @@ public final class KeyTransparencyManager { private let tsAccountManager: TSAccountManager private let udManager: OWSUDManager + private var notificationObservers: [NotificationCenter.Observer] = [] private let taskQueue: KeyedConcurrentTaskQueue init( @@ -47,6 +48,33 @@ public final class KeyTransparencyManager { self.udManager = udManager self.taskQueue = KeyedConcurrentTaskQueue(concurrentLimitPerKey: 1) + + observeNotifications() + } + + deinit { + for observer in notificationObservers { + NotificationCenter.default.removeObserver(observer) + } + } + + private func observeNotifications() { + notificationObservers = [ + NotificationCenter.default.addObserver( + name: Usernames.localUsernameStateChangedNotification, + block: { [weak self] _ in + guard let self else { return } + handleSelfCheckIdentifierChanged(field: .usernameHash) + }, + ), + NotificationCenter.default.addObserver( + name: .localNumberDidChange, + block: { [weak self] _ in + guard let self else { return } + handleSelfCheckIdentifierChanged(field: .e164) + }, + ), + ] } // MARK: Opt-out @@ -246,6 +274,8 @@ public final class KeyTransparencyManager { // MARK: - Self-check + /// When the value of an `AccountDataField` identifier for the local user + /// changes, we need to inform LibSignal so it can update internal state. /// Use `Cron` to periodically perform a Key Transparency validation on the /// local user. public func registerSelfCheckForCron(cron: Cron) { @@ -443,6 +473,45 @@ public final class KeyTransparencyManager { tx: tx, ) } + + /// If an `AccountDataField` value changes for the local user, we want to + /// inform LibSignal so they can adjust state accordingly. + private func handleSelfCheckIdentifierChanged(field: KeyTransparency.AccountDataField) { + Task { + guard + let localIdentifiers = db.read(block: { tx in + tsAccountManager.localIdentifiers(tx: tx) + }) + else { + return + } + + try await taskQueue.run(forKey: localIdentifiers.aci) { + await _handleSelfCheckIdentifierChanged(field: field, localAci: localIdentifiers.aci) + } + } + } + + private func _handleSelfCheckIdentifierChanged( + field: KeyTransparency.AccountDataField, + localAci: Aci, + ) async { + let libSignalStore = KeyTransparencyStoreForLibSignal( + db: db, + keyTransparencyStore: keyTransparencyStore, + ) + + do { + try await KeyTransparency.resetField( + field, + for: localAci, + store: libSignalStore, + ) + } catch { + // We should only end up here if there's malformed data. + owsFailDebug("Failed to reset \(field) for local user!") + } + } } // MARK: - KeyTransparencyStore diff --git a/SignalServiceKit/Protos/Backups/Backup.pb.swift b/SignalServiceKit/Protos/Backups/Backup.pb.swift index e48dbf749a..8e41f2da35 100644 --- a/SignalServiceKit/Protos/Backups/Backup.pb.swift +++ b/SignalServiceKit/Protos/Backups/Backup.pb.swift @@ -384,16 +384,6 @@ public struct BackupProto_AccountData: @unchecked Sendable { set {_uniqueStorage()._bioEmoji = newValue} } - /// Opaque blob containing key transparency data for the account - public var keyTransparencyData: Data { - get {_storage._keyTransparencyData ?? Data()} - set {_uniqueStorage()._keyTransparencyData = newValue} - } - /// Returns true if `keyTransparencyData` has been explicitly set. - public var hasKeyTransparencyData: Bool {_storage._keyTransparencyData != nil} - /// Clears the value of `keyTransparencyData`. Subsequent reads from it will return its default value. - public mutating func clearKeyTransparencyData() {_uniqueStorage()._keyTransparencyData = nil} - public var iosSpecificSettings: BackupProto_AccountData.IOSSpecificSettings { get {_storage._iosSpecificSettings ?? BackupProto_AccountData.IOSSpecificSettings()} set {_uniqueStorage()._iosSpecificSettings = newValue} @@ -6855,7 +6845,7 @@ extension BackupProto_Frame: SwiftProtobuf.Message, SwiftProtobuf._MessageImplem extension BackupProto_AccountData: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".AccountData" - public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}profileKey\0\u{1}username\0\u{1}usernameLink\0\u{1}givenName\0\u{1}familyName\0\u{1}avatarUrlPath\0\u{1}donationSubscriberData\0\u{2}\u{2}accountSettings\0\u{1}backupsSubscriberData\0\u{1}svrPin\0\u{1}androidSpecificSettings\0\u{1}bioText\0\u{1}bioEmoji\0\u{1}keyTransparencyData\0\u{1}iosSpecificSettings\0\u{c}\u{8}\u{1}") + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}profileKey\0\u{1}username\0\u{1}usernameLink\0\u{1}givenName\0\u{1}familyName\0\u{1}avatarUrlPath\0\u{1}donationSubscriberData\0\u{2}\u{2}accountSettings\0\u{1}backupsSubscriberData\0\u{1}svrPin\0\u{1}androidSpecificSettings\0\u{1}bioText\0\u{1}bioEmoji\0\u{2}\u{2}iosSpecificSettings\0\u{c}\u{8}\u{1}\u{c}\u{f}\u{1}") fileprivate class _StorageClass { var _profileKey: Data = Data() @@ -6871,7 +6861,6 @@ extension BackupProto_AccountData: SwiftProtobuf.Message, SwiftProtobuf._Message var _androidSpecificSettings: BackupProto_AccountData.AndroidSpecificSettings? = nil var _bioText: String = String() var _bioEmoji: String = String() - var _keyTransparencyData: Data? = nil var _iosSpecificSettings: BackupProto_AccountData.IOSSpecificSettings? = nil // This property is used as the initial default value for new instances of the type. @@ -6896,7 +6885,6 @@ extension BackupProto_AccountData: SwiftProtobuf.Message, SwiftProtobuf._Message _androidSpecificSettings = source._androidSpecificSettings _bioText = source._bioText _bioEmoji = source._bioEmoji - _keyTransparencyData = source._keyTransparencyData _iosSpecificSettings = source._iosSpecificSettings } } @@ -6929,7 +6917,6 @@ extension BackupProto_AccountData: SwiftProtobuf.Message, SwiftProtobuf._Message case 12: try { try decoder.decodeSingularMessageField(value: &_storage._androidSpecificSettings) }() case 13: try { try decoder.decodeSingularStringField(value: &_storage._bioText) }() case 14: try { try decoder.decodeSingularStringField(value: &_storage._bioEmoji) }() - case 15: try { try decoder.decodeSingularBytesField(value: &_storage._keyTransparencyData) }() case 16: try { try decoder.decodeSingularMessageField(value: &_storage._iosSpecificSettings) }() default: break } @@ -6982,9 +6969,6 @@ extension BackupProto_AccountData: SwiftProtobuf.Message, SwiftProtobuf._Message if !_storage._bioEmoji.isEmpty { try visitor.visitSingularStringField(value: _storage._bioEmoji, fieldNumber: 14) } - try { if let v = _storage._keyTransparencyData { - try visitor.visitSingularBytesField(value: v, fieldNumber: 15) - } }() try { if let v = _storage._iosSpecificSettings { try visitor.visitSingularMessageField(value: v, fieldNumber: 16) } }() @@ -7010,7 +6994,6 @@ extension BackupProto_AccountData: SwiftProtobuf.Message, SwiftProtobuf._Message if _storage._androidSpecificSettings != rhs_storage._androidSpecificSettings {return false} if _storage._bioText != rhs_storage._bioText {return false} if _storage._bioEmoji != rhs_storage._bioEmoji {return false} - if _storage._keyTransparencyData != rhs_storage._keyTransparencyData {return false} if _storage._iosSpecificSettings != rhs_storage._iosSpecificSettings {return false} return true } diff --git a/SignalServiceKit/Protos/Backups/Backup.proto b/SignalServiceKit/Protos/Backups/Backup.proto index a6ea1cf394..04cb3705cb 100644 --- a/SignalServiceKit/Protos/Backups/Backup.proto +++ b/SignalServiceKit/Protos/Backups/Backup.proto @@ -191,8 +191,7 @@ message AccountData { AndroidSpecificSettings androidSpecificSettings = 12; string bioText = 13; string bioEmoji = 14; - // Opaque blob containing key transparency data for the account - optional bytes keyTransparencyData = 15; + reserved /*keyTransparencyData*/ 15; IOSSpecificSettings iosSpecificSettings = 16; } diff --git a/SignalServiceKit/tests/MessageBackup/Signal-Message-Backup-Tests b/SignalServiceKit/tests/MessageBackup/Signal-Message-Backup-Tests index 33b3d0cd43..a0f9002432 160000 --- a/SignalServiceKit/tests/MessageBackup/Signal-Message-Backup-Tests +++ b/SignalServiceKit/tests/MessageBackup/Signal-Message-Backup-Tests @@ -1 +1 @@ -Subproject commit 33b3d0cd4367a898f5f8b4a2c57ee12ba7ec38ea +Subproject commit a0f900243210efbedc72f0907c5d2f140385daa4