From c334996d3ed10adffaa19b2181e2278f20b83b2d Mon Sep 17 00:00:00 2001 From: Christoph Hagen Date: Sun, 1 May 2022 18:30:30 +0200 Subject: [PATCH] Add unlock history, improve color scheme --- Sesame.xcodeproj/project.pbxproj | 24 ++- .../UserInterfaceState.xcuserstate | Bin 95155 -> 98597 bytes Sesame/API/Message.swift | 8 + Sesame/ClientState.swift | 137 ++++++++++++-- Sesame/ContentView.swift | 59 +++--- Sesame/HistoryItem.swift | 168 ++++++++++++++++++ Sesame/HistoryListItem.swift | 89 ++++++++++ Sesame/HistoryManager.swift | 59 ++++++ Sesame/HistoryView.swift | 18 ++ Sesame/Info.plist | 8 + Sesame/KeyManagement.swift | 10 +- Sesame/SingleKeyView.swift | 19 +- Sesame/SymmetricKey+Extensions.swift | 7 + 13 files changed, 561 insertions(+), 45 deletions(-) create mode 100644 Sesame/HistoryItem.swift create mode 100644 Sesame/HistoryListItem.swift create mode 100644 Sesame/HistoryManager.swift create mode 100644 Sesame/HistoryView.swift create mode 100644 Sesame/Info.plist diff --git a/Sesame.xcodeproj/project.pbxproj b/Sesame.xcodeproj/project.pbxproj index b3132e3..a3894c1 100644 --- a/Sesame.xcodeproj/project.pbxproj +++ b/Sesame.xcodeproj/project.pbxproj @@ -22,6 +22,10 @@ E24EE77927FF95E00011CFD2 /* Message.swift in Sources */ = {isa = PBXBuildFile; fileRef = E24EE77827FF95E00011CFD2 /* Message.swift */; }; E28DED2D281E840B00259690 /* KeyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E28DED2C281E840B00259690 /* KeyView.swift */; }; E28DED2F281E8A0500259690 /* SingleKeyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E28DED2E281E8A0500259690 /* SingleKeyView.swift */; }; + E28DED31281EAE9100259690 /* HistoryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E28DED30281EAE9100259690 /* HistoryView.swift */; }; + E28DED33281EB15B00259690 /* HistoryListItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = E28DED32281EB15B00259690 /* HistoryListItem.swift */; }; + E28DED35281EB17600259690 /* HistoryItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = E28DED34281EB17600259690 /* HistoryItem.swift */; }; + E28DED37281EC7FB00259690 /* HistoryManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = E28DED36281EC7FB00259690 /* HistoryManager.swift */; }; E2C5C1DB2806FE8900769EF6 /* RouteAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2C5C1DA2806FE8900769EF6 /* RouteAPI.swift */; }; E2C5C1DD281B3AC400769EF6 /* UInt32+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2C5C1DC281B3AC400769EF6 /* UInt32+Extensions.swift */; }; E2C5C1F8281E769F00769EF6 /* ServerMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2C5C1F7281E769F00769EF6 /* ServerMessage.swift */; }; @@ -43,6 +47,11 @@ E24EE77827FF95E00011CFD2 /* Message.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Message.swift; sourceTree = ""; }; E28DED2C281E840B00259690 /* KeyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyView.swift; sourceTree = ""; }; E28DED2E281E8A0500259690 /* SingleKeyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SingleKeyView.swift; sourceTree = ""; }; + E28DED30281EAE9100259690 /* HistoryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryView.swift; sourceTree = ""; }; + E28DED32281EB15B00259690 /* HistoryListItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryListItem.swift; sourceTree = ""; }; + E28DED34281EB17600259690 /* HistoryItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryItem.swift; sourceTree = ""; }; + E28DED36281EC7FB00259690 /* HistoryManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryManager.swift; sourceTree = ""; }; + E28DED38281EE9CF00259690 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; E2C5C1DA2806FE8900769EF6 /* RouteAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RouteAPI.swift; sourceTree = ""; }; E2C5C1DC281B3AC400769EF6 /* UInt32+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UInt32+Extensions.swift"; sourceTree = ""; }; E2C5C1F7281E769F00769EF6 /* ServerMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerMessage.swift; sourceTree = ""; }; @@ -79,9 +88,14 @@ 884A45B5279F48C100D6E650 /* Sesame */ = { isa = PBXGroup; children = ( + E28DED38281EE9CF00259690 /* Info.plist */, E2C5C1D92806FE4A00769EF6 /* API */, 884A45B6279F48C100D6E650 /* SesameApp.swift */, 884A45B8279F48C100D6E650 /* ContentView.swift */, + E28DED30281EAE9100259690 /* HistoryView.swift */, + E28DED32281EB15B00259690 /* HistoryListItem.swift */, + E28DED34281EB17600259690 /* HistoryItem.swift */, + E28DED36281EC7FB00259690 /* HistoryManager.swift */, E28DED2C281E840B00259690 /* KeyView.swift */, E28DED2E281E8A0500259690 /* SingleKeyView.swift */, 884A45CC27A465F500D6E650 /* Client.swift */, @@ -195,14 +209,18 @@ 884A45CF27A5402D00D6E650 /* MessageResult.swift in Sources */, 884A45B9279F48C100D6E650 /* ContentView.swift in Sources */, E28DED2F281E8A0500259690 /* SingleKeyView.swift in Sources */, + E28DED37281EC7FB00259690 /* HistoryManager.swift in Sources */, E2C5C1DB2806FE8900769EF6 /* RouteAPI.swift in Sources */, E2C5C1DD281B3AC400769EF6 /* UInt32+Extensions.swift in Sources */, 884A45CD27A465F500D6E650 /* Client.swift in Sources */, E24EE77227FDCCC00011CFD2 /* Data+Extensions.swift in Sources */, E24EE77427FF95920011CFD2 /* DeviceResponse.swift in Sources */, 884A45CB27A464C000D6E650 /* SymmetricKey+Extensions.swift in Sources */, + E28DED31281EAE9100259690 /* HistoryView.swift in Sources */, E24EE77927FF95E00011CFD2 /* Message.swift in Sources */, + E28DED35281EB17600259690 /* HistoryItem.swift in Sources */, 884A45C927A43D7900D6E650 /* ClientState.swift in Sources */, + E28DED33281EB15B00259690 /* HistoryListItem.swift in Sources */, E28DED2D281E840B00259690 /* KeyView.swift in Sources */, 884A45B7279F48C100D6E650 /* SesameApp.swift in Sources */, 884A45C5279F4BBE00D6E650 /* KeyManagement.swift in Sources */, @@ -340,12 +358,13 @@ DEVELOPMENT_TEAM = H8WR4M6QQ4; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = Sesame/Info.plist; INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES; INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = UIInterfaceOrientationPortrait; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -370,12 +389,13 @@ DEVELOPMENT_TEAM = H8WR4M6QQ4; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = Sesame/Info.plist; INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES; INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = UIInterfaceOrientationPortrait; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", diff --git a/Sesame.xcodeproj/project.xcworkspace/xcuserdata/ch.xcuserdatad/UserInterfaceState.xcuserstate b/Sesame.xcodeproj/project.xcworkspace/xcuserdata/ch.xcuserdatad/UserInterfaceState.xcuserstate index a4bb03d6d7475f9e6adcd73cb3edfb3d6def9544..763be606519b19058a3fe0402a134eb127dadc24 100644 GIT binary patch literal 98597 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 z zT#g>&&!MaM>)1)$PR3Tzgtx|S%l3Seo!AB6p|Shn?Xgoigu^(3D|nc{Qwos=f3Csr zYd9X=HOSK-Ps7<5&{xB9p2kfzJj*K9@DIKUk;XtRJ0nx$ zchE)Se{&q(Z9J3NoW;e++GtmeGBnE2C`02fxdj;-Z|6?#W*tKz(xii?6PU-f+=%Z& z)6Lw<1IXPZcaz*reP_`&6C&-f4+GS_QtN755c_6M{+dBa30s; zbDKBdo|-@9b2`bi4z!eXag~>sO?ZsZ~#{_Uz|dL|O~zAX$Z7C-soj zUsArLeJ2y_gMB9LE@?kWdr7{+I@V(^N$)1Tk@T5KGbTqUOV literal 95155 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 diff --git a/Sesame/API/Message.swift b/Sesame/API/Message.swift index b29024c..7403182 100644 --- a/Sesame/API/Message.swift +++ b/Sesame/API/Message.swift @@ -96,6 +96,14 @@ extension Message { self.init(decodeFrom: data) } + init?(decodeFrom data: Data, index: inout Int) { + guard index + Message.length <= data.count else { + return nil + } + self.init(decodeFrom: data.advanced(by: index)) + index += Message.length + } + /// The message encoded to data var encoded: Data { mac + content.encoded diff --git a/Sesame/ClientState.swift b/Sesame/ClientState.swift index 356be3f..b06af68 100644 --- a/Sesame/ClientState.swift +++ b/Sesame/ClientState.swift @@ -23,6 +23,7 @@ enum RejectionCause { case invalidTime case invalidAuthentication case timeout + case missingKey } extension RejectionCause: CustomStringConvertible { @@ -37,6 +38,8 @@ extension RejectionCause: CustomStringConvertible { return "Invalid authentication" case .timeout: return "Device not responding" + case .missingKey: + return "No key to verify message" } } } @@ -61,6 +64,8 @@ enum ClientState { /// The transmitted message was rejected (multiple possible reasons) case messageRejected(RejectionCause) + case responseRejected(RejectionCause) + /// The device responded that the opening action was started case openSesame @@ -104,7 +109,7 @@ enum ClientState { var requiresDescription: Bool { switch self { - case .deviceNotAvailable, .messageRejected, .internalError: + case .deviceNotAvailable, .messageRejected, .internalError, .responseRejected: return true default: return false @@ -114,21 +119,19 @@ enum ClientState { var color: Color { switch self { case .noKeyAvailable: - return .gray - case .requestingStatus: - return .yellow + return Color(red: 50/255, green: 50/255, blue: 50/255) case .deviceNotAvailable: - return Color(red: 1.0, green: 0.6, blue: 0.6) - case .messageRejected: - return .red + return Color(red: 150/255, green: 90/255, blue: 90/255) + case .messageRejected, .responseRejected: + return Color(red: 160/255, green: 30/255, blue: 30/255) case .internalError: - return Color(red: 0.7, green: 0, blue: 0) + return Color(red: 100/255, green: 0/255, blue: 0/255) case .ready: - return Color(red: 0.7, green: 1.0, blue: 0.5) - case .waitingForResponse: - return Color(red: 0.9, green: 1.0, blue: 0.5) + return Color(red: 115/255, green: 140/255, blue: 90/255) + case .requestingStatus, .waitingForResponse: + return Color(red: 160/255, green: 170/255, blue: 110/255) case .openSesame: - return .green + return Color(red: 65/255, green: 110/255, blue: 60/255) } } @@ -166,7 +169,115 @@ extension ClientState: CustomStringConvertible { return "Unlocked" case .internalError(let e): return "Error: \(e)" + case .responseRejected(let cause): + switch cause { + case .invalidAuthentication: + return "Device message not authenticated" + default: + return cause.description + } + } + } +} + +// MARK: Coding + +extension ClientState { + + var encoded: Data { + Data([code]) + } + + private var code: UInt8 { + switch self { + case .noKeyAvailable: + return 1 + case .requestingStatus: + return 2 + case .deviceNotAvailable(let connectionError): + switch connectionError { + case .serverNotReached: + return 3 + case .deviceDisconnected: + return 4 + } + case .ready: + return 5 + case .waitingForResponse: + return 6 + case .messageRejected(let rejectionCause): + switch rejectionCause { + case .invalidCounter: + return 7 + case .invalidTime: + return 8 + case .invalidAuthentication: + return 9 + case .timeout: + return 10 + case .missingKey: + return 11 + } + case .responseRejected(let rejectionCause): + switch rejectionCause { + case .invalidCounter: + return 12 + case .invalidTime: + return 13 + case .invalidAuthentication: + return 14 + case .timeout: + return 15 + case .missingKey: + return 16 + } + case .openSesame: + return 17 + case .internalError(_): + return 18 + } + } + + init(code: UInt8) { + switch code { + case 1: + self = .noKeyAvailable + case 2: + self = .requestingStatus + case 3: + self = .deviceNotAvailable(.serverNotReached) + case 4: + self = .deviceNotAvailable(.deviceDisconnected) + case 5: + self = .ready + case 6: + self = .waitingForResponse + case 7: + self = .messageRejected(.invalidCounter) + case 8: + self = .messageRejected(.invalidTime) + case 9: + self = .messageRejected(.invalidAuthentication) + case 10: + self = .messageRejected(.timeout) + case 11: + self = .messageRejected(.missingKey) + case 12: + self = .responseRejected(.invalidCounter) + case 13: + self = .responseRejected(.invalidTime) + case 14: + self = .responseRejected(.invalidAuthentication) + case 15: + self = .responseRejected(.timeout) + case 16: + self = .responseRejected(.missingKey) + case 17: + self = .openSesame + case 18: + self = .internalError("") + default: + self = .internalError("Unknown code \(code)") } } - } diff --git a/Sesame/ContentView.swift b/Sesame/ContentView.swift index 41dff83..491a5c1 100644 --- a/Sesame/ContentView.swift +++ b/Sesame/ContentView.swift @@ -10,6 +10,8 @@ struct ContentView: View { @State var keyManager = KeyManagement() + + let history = HistoryManager() @State var state: ClientState = .noKeyAvailable @@ -109,7 +111,11 @@ struct ContentView: View { .sheet(isPresented: $showKeySheet) { KeyView(keyManager: $keyManager) } + .sheet(isPresented: $showHistorySheet) { + HistoryView(manager: history) + } } + .preferredColorScheme(.dark) } func mainButtonPressed() { @@ -119,51 +125,52 @@ struct ContentView: View { } let count = UInt32(nextMessageCounter) - let now = Date() + let sentTime = Date() let content = Message.Content( - time: now.timestamp, + time: sentTime.timestamp, id: count) let message = content.authenticate(using: key) + let historyItem = HistoryItem(sent: message, date: sentTime) state = .waitingForResponse print("Sending message \(count)") Task { let (newState, message) = await server.send(message, authToken: token) - responseTime = now + let receivedTime = Date.now + responseTime = receivedTime state = newState - if let message = message { - processResponse(message, sendTime: now) - } + let finishedItem = historyItem.didReceive(response: newState, date: receivedTime, message: message) + print("Interval: \(receivedTime.timeIntervalSince(sentTime))", "\(finishedItem.roundTripTime ?? -1)") + process(item: finishedItem) } } - private func processResponse(_ message: Message, sendTime: Date) { + private func process(item: HistoryItem) { + guard let message = item.incomingMessage else { + save(historyItem: item) + return + } + guard let key = keyManager.get(.deviceKey) else { + save(historyItem: item.notAuthenticated()) return } guard message.isValid(using: key) else { + save(historyItem: item.invalidated()) return } nextMessageCounter = Int(message.content.id) - print("Next counter is \(message.content.id)") - let now = Date() - let total = now.timeIntervalSince(sendTime) - print("Total time: \(Int(total * 1000)) ms") - let deviceTime = Date(timestamp: message.content.time) - let time1 = deviceTime.timeIntervalSince(sendTime) - let time2 = now.timeIntervalSince(deviceTime) - if time1 < 0 { - print("Device time behind by at least \(Int(-time1 * 1000)) ms") - print("Device: \(deviceTime)") - print("Remote: \(now)") - } else if time2 < 0 { - print("Device time ahead by at least \(Int(-time2 * 1000)) ms") - print("Device: \(deviceTime)") - print("Remote: \(now)") - } else { - print("Device time synchronized") + save(historyItem: item) + } + + private func save(historyItem: HistoryItem) { + do { + try history.save(item: historyItem) + } catch { + print("Failed to save item: \(error)") } } + private func startRegularStatusUpdates() { guard timer == nil else { return @@ -187,7 +194,6 @@ struct ContentView: View { return } hasActiveRequest = true - print("Checking device status") Task { let newState = await server.deviceStatus(authToken: authToken.data) hasActiveRequest = false @@ -198,13 +204,14 @@ struct ContentView: View { state = newState case .waitingForResponse: return - case .messageRejected, .openSesame, .internalError: + case .messageRejected, .openSesame, .internalError, .responseRejected: guard let time = responseTime else { state = newState return } responseTime = nil // Wait at least 5 seconds after these states have been reached before changing the + // interface to allow sufficient time to see the result let elapsed = Date.now.timeIntervalSince(time) guard elapsed < 5 else { state = newState diff --git a/Sesame/HistoryItem.swift b/Sesame/HistoryItem.swift new file mode 100644 index 0000000..c863c75 --- /dev/null +++ b/Sesame/HistoryItem.swift @@ -0,0 +1,168 @@ +import Foundation + +struct HistoryItem { + + let outgoingDate: Date + + let outgoingMessage: Message + + let incomingDate: Date? + + let incomingMessage: Message? + + let response: ClientState? + + init(sent message: Message, date: Date) { + self.outgoingDate = date + self.outgoingMessage = message + self.incomingDate = nil + self.incomingMessage = nil + self.response = nil + } + + func didReceive(response: ClientState, date: Date?, message: Message?) -> HistoryItem { + .init(sent: self, response: response, date: date, message: message) + } + + func invalidated() -> HistoryItem { + didReceive(response: .responseRejected(.invalidAuthentication), date: incomingDate, message: incomingMessage) + } + + func notAuthenticated() -> HistoryItem { + didReceive(response: .responseRejected(.missingKey), date: incomingDate, message: incomingMessage) + } + + private init(sent: HistoryItem, response: ClientState, date: Date?, message: Message?) { + self.outgoingDate = sent.outgoingDate + self.outgoingMessage = sent.outgoingMessage + self.incomingDate = date + self.incomingMessage = message + self.response = response + } + + // MARK: Statistics + + var roundTripTime: TimeInterval? { + incomingDate?.timeIntervalSince(outgoingDate) + } + + var deviceTime: Date? { + guard let timestamp = incomingMessage?.content.time else { + return nil + } + return Date(timestamp: timestamp) + } + + var requestLatency: TimeInterval? { + deviceTime?.timeIntervalSince(outgoingDate) + } + + var responseLatency: TimeInterval? { + guard let deviceTime = deviceTime else { + return nil + } + return incomingDate?.timeIntervalSince(deviceTime) + } + + var clockOffset: Int? { + guard let interval = roundTripTime, let deviceTime = deviceTime else { + return nil + } + let estimatedArrival = outgoingDate.advanced(by: interval / 2) + return Int(deviceTime.timeIntervalSince(estimatedArrival)) + } + + // MARK: Coding + + static func testEncoding() { + + } + + var encoded: Data { + var result = outgoingDate.encoded + outgoingMessage.encoded + if let date = incomingDate { + result += Data([1]) + date.encoded + } else { + result += Data([0]) + } + if let message = incomingMessage { + result += Data([1]) + message.encoded + } else { + result += Data([0]) + } + result += response?.encoded ?? Data([0]) + return result + } + + init?(decodeFrom data: Data, index: inout Int) { + guard let outgoingDate = Date(decodeFrom: data, index: &index) else { + return nil + } + self.outgoingDate = outgoingDate + + guard let outgoingMessage = Message(decodeFrom: data, index: &index) else { + return nil + } + self.outgoingMessage = outgoingMessage + + if data[index] > 0 { + index += 1 + guard let incomingDate = Date(decodeFrom: data, index: &index) else { + return nil + } + self.incomingDate = incomingDate + } else { + self.incomingDate = nil + index += 1 + } + + if data[index] > 0 { + index += 1 + guard let incomingMessage = Message(decodeFrom: data, index: &index) else { + return nil + } + self.incomingMessage = incomingMessage + } else { + self.incomingMessage = nil + index += 1 + } + guard index < data.count else { + return nil + } + self.response = ClientState(code: data[index]) + index += 1 + } +} + +private extension Date { + + static var encodedSize: Int { + MemoryLayout.size + } + + var encoded: Data { + .init(from: timeIntervalSince1970) + } + + init?(decodeFrom data: Data, index: inout Int) { + guard index + Date.encodedSize <= data.count else { + return nil + } + self.init(timeIntervalSince1970: data.advanced(by: index).convert(into: .zero)) + index += Date.encodedSize + } +} + +extension HistoryItem: Identifiable { + + var id: UInt32 { + outgoingDate.timestamp + } +} + +extension HistoryItem: Comparable { + + static func < (lhs: HistoryItem, rhs: HistoryItem) -> Bool { + lhs.outgoingDate < rhs.outgoingDate + } +} diff --git a/Sesame/HistoryListItem.swift b/Sesame/HistoryListItem.swift new file mode 100644 index 0000000..27a620b --- /dev/null +++ b/Sesame/HistoryListItem.swift @@ -0,0 +1,89 @@ +import SwiftUI + +private let df: DateFormatter = { + let df = DateFormatter() + df.dateStyle = .short + df.timeStyle = .short + return df +}() + +struct HistoryListItem: View { + + let entry: HistoryItem + + var entryTime: String { + df.string(from: entry.outgoingDate) + } + + var roundTripText: String { + guard let time = entry.roundTripTime else { + return "" + } + return "⇆ \(Int(time * 1000)) ms" + } + + var counterText: String { + let sentCounter = entry.outgoingMessage.content.id + let startText = "🔗 \(sentCounter)" + guard let rCounter = entry.incomingMessage?.content.id else { + return startText + } + let diff = Int(rCounter) - Int(sentCounter) + guard diff != 1 else { + return startText + } + return startText + "→\(rCounter)" + } + + var timeOffsetText: String { + guard let offset = entry.clockOffset, offset != 0 else { + return "" + } + return "🕓 \(offset) s" + } + + var body: some View { + VStack { + HStack { + Text(entry.response?.description ?? "") + .font(.headline) + Spacer() + Text(entryTime) + }.padding(.bottom, 1) + HStack { + Text(roundTripText) + .font(.subheadline) + .foregroundColor(.secondary) + Text(counterText) + .font(.subheadline) + .foregroundColor(.secondary) + Text(timeOffsetText) + .font(.subheadline) + .foregroundColor(.secondary) + Spacer() + } + }.padding() + } +} + +struct HistoryListItem_Previews: PreviewProvider { + static var previews: some View { + HistoryListItem(entry: .mock) + } +} + +private extension HistoryItem { + + static var mock: HistoryItem { + let mac = Data(repeating: 42, count: 32) + let content = Message.Content(time: Date.now.timestamp, id: 123) + let content2 = Message.Content(time: (Date.now + 1).timestamp, id: 124) + return .init( + sent: Message(mac: mac, content: content), + date: .now) + .didReceive( + response: .openSesame, + date: .now + 2, + message: Message(mac: mac, content: content2)) + } +} diff --git a/Sesame/HistoryManager.swift b/Sesame/HistoryManager.swift new file mode 100644 index 0000000..86cade8 --- /dev/null +++ b/Sesame/HistoryManager.swift @@ -0,0 +1,59 @@ +import Foundation + +final class HistoryManager { + + private var fm: FileManager { + .default + } + + var documentDirectory: URL { + try! fm.url( + for: .documentDirectory, + in: .userDomainMask, + appropriateFor: nil, create: true) + } + + private var fileUrl: URL { + documentDirectory.appendingPathComponent("history.bin") + } + + func loadEntries() -> [HistoryItem] { + let url = fileUrl + guard fm.fileExists(atPath: url.path) else { + print("No history data found") + return [] + } + let content: Data + do { + content = try Data(contentsOf: url) + } catch { + print("Failed to read history data: \(error)") + return [] + } + var index = 0 + var entries = [HistoryItem]() + while index < content.count { + guard let entry = HistoryItem(decodeFrom: content, index: &index) else { + print("Failed to read entry at index \(index)") + return entries + } + entries.append(entry) + } + return entries.sorted().reversed() + } + + func save(item: HistoryItem) throws { + let url = fileUrl + let data = item.encoded + guard fm.fileExists(atPath: url.path) else { + try data.write(to: url) + print("First history item written") + return + } + let handle = try FileHandle(forWritingTo: url) + try handle.seekToEnd() + try handle.write(contentsOf: data) + try handle.close() + print("History item written") + } +} diff --git a/Sesame/HistoryView.swift b/Sesame/HistoryView.swift new file mode 100644 index 0000000..5c7573e --- /dev/null +++ b/Sesame/HistoryView.swift @@ -0,0 +1,18 @@ +import SwiftUI + +struct HistoryView: View { + + let manager: HistoryManager + + var body: some View { + List(manager.loadEntries()) { entry in + HistoryListItem(entry: entry) + } + } +} + +struct HistoryView_Previews: PreviewProvider { + static var previews: some View { + HistoryView(manager: .init()) + } +} diff --git a/Sesame/Info.plist b/Sesame/Info.plist new file mode 100644 index 0000000..4c2206e --- /dev/null +++ b/Sesame/Info.plist @@ -0,0 +1,8 @@ + + + + + UIStatusBarStyle + UIStatusBarStyleDarkContent + + diff --git a/Sesame/KeyManagement.swift b/Sesame/KeyManagement.swift index c6e8078..4bd56af 100644 --- a/Sesame/KeyManagement.swift +++ b/Sesame/KeyManagement.swift @@ -28,6 +28,15 @@ extension KeyManagement { var keyLength: SymmetricKeySize { .bits256 } + + var usesHashing: Bool { + switch self { + case .authToken: + return true + default: + return false + } + } } } @@ -78,7 +87,6 @@ private struct KeyChain { return nil } let key = item as! CFData - print("\(type) loaded from keychain") return SymmetricKey(data: key as Data) } diff --git a/Sesame/SingleKeyView.swift b/Sesame/SingleKeyView.swift index 4cdd976..28d59c1 100644 --- a/Sesame/SingleKeyView.swift +++ b/Sesame/SingleKeyView.swift @@ -1,4 +1,5 @@ import SwiftUI +import CryptoKit struct SingleKeyView: View { @@ -11,7 +12,7 @@ struct SingleKeyView: View { let type: KeyManagement.KeyType private var generateText: String { - hasKey ? "Generate" : "Regenerate" + hasKey ? "Regenerate" : "Generate" } var hasKey: Bool { @@ -22,20 +23,32 @@ struct SingleKeyView: View { keyManager.get(type)?.displayString ?? "-" } + var copyText: String { + guard let key = keyManager.get(type)?.data else { + return "" + } + guard type.usesHashing else { + return key.hexEncoded + } + return SHA256.hash(data: key).hexEncoded + } + var body: some View { VStack(alignment: .leading, spacing: 8) { Text(type.displayName) .bold() Text(needRefresh ? content : content) .font(.system(.body, design: .monospaced)) + .foregroundColor(.secondary) HStack() { Button(generateText) { keyManager.generate(type) needRefresh.toggle() } .padding() - Button("Copy") { - UIPasteboard.general.string = content + + Button(type.usesHashing ? "Copy hash" : "Copy") { + UIPasteboard.general.string = copyText } .disabled(!hasKey) .padding() diff --git a/Sesame/SymmetricKey+Extensions.swift b/Sesame/SymmetricKey+Extensions.swift index e0f11ee..2066a91 100644 --- a/Sesame/SymmetricKey+Extensions.swift +++ b/Sesame/SymmetricKey+Extensions.swift @@ -24,6 +24,13 @@ extension SymmetricKey { } } +extension SHA256.Digest { + + var hexEncoded: String { + Data(map { $0 }).hexEncoded + } +} + extension String { func split(by length: Int) -> [String] {