import CryptoKit import Foundation enum AppSection: String, CaseIterable, Identifiable, Hashable { case overview case requests case activity case account var id: String { rawValue } var title: String { switch self { case .overview: "Passport" case .requests: "Requests" case .activity: "Activity" case .account: "Account" } } var systemImage: String { switch self { case .overview: "person.crop.square.fill" case .requests: "checklist.checked" case .activity: "clock.arrow.trianglehead.counterclockwise.rotate.90" case .account: "person.crop.circle.fill" } } } enum NotificationPermissionState: String, CaseIterable, Identifiable { case unknown case allowed case provisional case denied var id: String { rawValue } var title: String { switch self { case .unknown: "Not Asked Yet" case .allowed: "Enabled" case .provisional: "Delivered Quietly" case .denied: "Disabled" } } var systemImage: String { switch self { case .unknown: "bell" case .allowed: "bell.badge.fill" case .provisional: "bell.badge" case .denied: "bell.slash.fill" } } var summary: String { switch self { case .unknown: "The app has not asked for notification delivery yet." case .allowed: "Identity proof alerts can break through immediately when a check arrives." case .provisional: "Identity proof alerts can be delivered quietly until the user promotes them." case .denied: "Identity proof events stay in-app until the user re-enables notifications." } } } struct BootstrapContext { let suggestedPairingPayload: String } enum PairingTransport: String, Hashable { case qr case nfc case manual case preview var title: String { switch self { case .qr: "QR" case .nfc: "NFC" case .manual: "Manual" case .preview: "Preview" } } } struct PairingAuthenticationRequest: Hashable { let pairingPayload: String let transport: PairingTransport let signedGPSPosition: SignedGPSPosition? } struct SignedGPSPosition: Hashable { let latitude: Double let longitude: Double let horizontalAccuracyMeters: Double let capturedAt: Date let signatureBase64: String let publicKeyBase64: String init( latitude: Double, longitude: Double, horizontalAccuracyMeters: Double, capturedAt: Date, signatureBase64: String = "", publicKeyBase64: String = "" ) { self.latitude = latitude self.longitude = longitude self.horizontalAccuracyMeters = horizontalAccuracyMeters self.capturedAt = capturedAt self.signatureBase64 = signatureBase64 self.publicKeyBase64 = publicKeyBase64 } var coordinateSummary: String { "\(Self.normalized(latitude, precision: 5)), \(Self.normalized(longitude, precision: 5))" } var accuracySummary: String { "±\(Int(horizontalAccuracyMeters.rounded())) m" } func signingPayload(for pairingPayload: String) -> Data { let lines = [ "payload=\(pairingPayload)", "latitude=\(Self.normalized(latitude, precision: 6))", "longitude=\(Self.normalized(longitude, precision: 6))", "accuracy=\(Self.normalized(horizontalAccuracyMeters, precision: 2))", "captured_at=\(Self.timestampFormatter.string(from: capturedAt))" ] return Data(lines.joined(separator: "\n").utf8) } func verified(for pairingPayload: String) -> Bool { guard let signatureData = Data(base64Encoded: signatureBase64), let publicKeyData = Data(base64Encoded: publicKeyBase64), let publicKey = try? P256.Signing.PublicKey(x963Representation: publicKeyData), let signature = try? P256.Signing.ECDSASignature(derRepresentation: signatureData) else { return false } return publicKey.isValidSignature(signature, for: signingPayload(for: pairingPayload)) } func signed(signatureData: Data, publicKeyData: Data) -> SignedGPSPosition { SignedGPSPosition( latitude: latitude, longitude: longitude, horizontalAccuracyMeters: horizontalAccuracyMeters, capturedAt: capturedAt, signatureBase64: signatureData.base64EncodedString(), publicKeyBase64: publicKeyData.base64EncodedString() ) } private static let timestampFormatter: ISO8601DateFormatter = { let formatter = ISO8601DateFormatter() formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds] return formatter }() private static func normalized(_ value: Double, precision: Int) -> String { String(format: "%.\(precision)f", locale: Locale(identifier: "en_US_POSIX"), value) } } struct DashboardSnapshot { let profile: MemberProfile let requests: [ApprovalRequest] let notifications: [AppNotification] } struct SignInResult { let session: AuthSession let snapshot: DashboardSnapshot } struct MemberProfile: Identifiable, Hashable { let id: UUID let name: String let handle: String let organization: String let deviceCount: Int let recoverySummary: String init( id: UUID = UUID(), name: String, handle: String, organization: String, deviceCount: Int, recoverySummary: String ) { self.id = id self.name = name self.handle = handle self.organization = organization self.deviceCount = deviceCount self.recoverySummary = recoverySummary } } struct AuthSession: Identifiable, Hashable { let id: UUID let deviceName: String let originHost: String let pairedAt: Date let tokenPreview: String let pairingCode: String let pairingTransport: PairingTransport let signedGPSPosition: SignedGPSPosition? init( id: UUID = UUID(), deviceName: String, originHost: String, pairedAt: Date, tokenPreview: String, pairingCode: String, pairingTransport: PairingTransport = .manual, signedGPSPosition: SignedGPSPosition? = nil ) { self.id = id self.deviceName = deviceName self.originHost = originHost self.pairedAt = pairedAt self.tokenPreview = tokenPreview self.pairingCode = pairingCode self.pairingTransport = pairingTransport self.signedGPSPosition = signedGPSPosition } } enum ApprovalRequestKind: String, CaseIterable, Hashable { case signIn case accessGrant case elevatedAction var title: String { switch self { case .signIn: "Identity Check" case .accessGrant: "Strong Proof" case .elevatedAction: "Sensitive Proof" } } var systemImage: String { switch self { case .signIn: "qrcode.viewfinder" case .accessGrant: "person.badge.shield.checkmark.fill" case .elevatedAction: "shield.checkered" } } } enum ApprovalRisk: String, Hashable { case routine case elevated var title: String { switch self { case .routine: "Routine" case .elevated: "Elevated" } } var summary: String { switch self { case .routine: "A familiar identity proof for a normal sign-in or check." case .elevated: "A higher-assurance identity proof for a sensitive check." } } var guidance: String { switch self { case .routine: "Review the origin and continue only if it matches the proof you started." case .elevated: "Only continue if you initiated this proof and trust the origin asking for it." } } } enum ApprovalStatus: String, Hashable { case pending case approved case rejected var title: String { switch self { case .pending: "Pending" case .approved: "Verified" case .rejected: "Declined" } } var systemImage: String { switch self { case .pending: "clock.badge" case .approved: "checkmark.circle.fill" case .rejected: "xmark.circle.fill" } } } struct ApprovalRequest: Identifiable, Hashable { let id: UUID let title: String let subtitle: String let source: String let createdAt: Date let kind: ApprovalRequestKind let risk: ApprovalRisk let scopes: [String] var status: ApprovalStatus init( id: UUID = UUID(), title: String, subtitle: String, source: String, createdAt: Date, kind: ApprovalRequestKind, risk: ApprovalRisk, scopes: [String], status: ApprovalStatus ) { self.id = id self.title = title self.subtitle = subtitle self.source = source self.createdAt = createdAt self.kind = kind self.risk = risk self.scopes = scopes self.status = status } var scopeSummary: String { if scopes.isEmpty { return "No proof details listed" } let suffix = scopes.count == 1 ? "" : "s" return "\(scopes.count) proof detail\(suffix)" } var trustHeadline: String { switch (kind, risk) { case (.signIn, .routine): "Standard identity proof" case (.signIn, .elevated): "High-assurance sign-in proof" case (.accessGrant, _): "Cross-device identity proof" case (.elevatedAction, _): "Sensitive identity proof" } } var trustDetail: String { switch kind { case .signIn: "This request proves that the person at the browser, CLI, or device is really you." case .accessGrant: "This request asks for a stronger proof so the relying party can trust the session with higher confidence." case .elevatedAction: "This request asks for the highest confidence proof before continuing with a sensitive flow." } } } enum AppNotificationKind: String, Hashable { case approval case security case system var title: String { switch self { case .approval: "Proof" case .security: "Security" case .system: "System" } } var systemImage: String { switch self { case .approval: "checkmark.seal.fill" case .security: "shield.fill" case .system: "sparkles" } } var summary: String { switch self { case .approval: "Identity proof activity" case .security: "Passport and security posture updates" case .system: "Product and environment status messages" } } } struct AppNotification: Identifiable, Hashable { let id: UUID let title: String let message: String let sentAt: Date let kind: AppNotificationKind var isUnread: Bool init( id: UUID = UUID(), title: String, message: String, sentAt: Date, kind: AppNotificationKind, isUnread: Bool ) { self.id = id self.title = title self.message = message self.sentAt = sentAt self.kind = kind self.isUnread = isUnread } } enum AppError: LocalizedError { case invalidPairingPayload case missingSignedGPSPosition case invalidSignedGPSPosition case locationPermissionDenied case locationUnavailable case requestNotFound var errorDescription: String? { switch self { case .invalidPairingPayload: "That idp.global payload is not valid for this action." case .missingSignedGPSPosition: "Tap NFC requires a signed GPS position." case .invalidSignedGPSPosition: "The signed GPS position attached to this NFC proof could not be verified." case .locationPermissionDenied: "Location access is required so Tap NFC can attach a signed GPS position." case .locationUnavailable: "Unable to determine the current GPS position for Tap NFC." case .requestNotFound: "The selected identity check could not be found." } } }