import Combine import Security import SwiftUI import WatchConnectivity private enum SecureSnapshotStore { private static let service = "de.rfc1437.ironstorage.watch.snapshot" private static let account = "selected-totp" static func load() throws -> Data? { var result: CFTypeRef? let status = SecItemCopyMatching([ kSecClass: kSecClassGenericPassword, kSecAttrService: service, kSecAttrAccount: account, kSecReturnData: true, kSecMatchLimit: kSecMatchLimitOne, ] as CFDictionary, &result) if status == errSecItemNotFound { return nil } guard status == errSecSuccess, let data = result as? Data else { throw SnapshotStoreError(status: status) } return data } static func replace(_ snapshot: Data) throws { let query = [ kSecClass: kSecClassGenericPassword, kSecAttrService: service, kSecAttrAccount: account, ] as CFDictionary let status = SecItemUpdate(query, [kSecValueData: snapshot] as CFDictionary) if status == errSecItemNotFound { let added = SecItemAdd([ kSecClass: kSecClassGenericPassword, kSecAttrService: service, kSecAttrAccount: account, kSecValueData: snapshot, kSecAttrAccessible: kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly, ] as CFDictionary, nil) guard added == errSecSuccess else { throw SnapshotStoreError(status: added) } } else if status != errSecSuccess { throw SnapshotStoreError(status: status) } } static func delete() throws { let status = SecItemDelete([ kSecClass: kSecClassGenericPassword, kSecAttrService: service, kSecAttrAccount: account, ] as CFDictionary) guard status == errSecSuccess || status == errSecItemNotFound else { throw SnapshotStoreError(status: status) } } } private struct SnapshotStoreError: Error { let status: OSStatus } @MainActor private final class WatchSnapshotTransport: NSObject, ObservableObject, WCSessionDelegate { private static let snapshotKey = "de.rfc1437.ironstorage.watch.snapshot" private static let receiptKey = "de.rfc1437.ironstorage.watch.delivered" @Published private(set) var presentation: WatchPresentation? private let core = watchCore() override init() { super.init() restoreSnapshot() guard WCSession.isSupported() else { try? core.syncUnavailable() refreshPresentation() return } let session = WCSession.default session.delegate = self try? core.syncStarted() refreshPresentation() session.activate() } func sceneBecameActive() { restoreSnapshot() } func sceneBecameInactive() { try? core.protectedDataUnavailable() refreshPresentation() } private func restoreSnapshot() { do { if var snapshot = try SecureSnapshotStore.load() { defer { snapshot.resetBytes(in: 0..