From f599cb790b0f745e638289781a1730a1e18dd8d5 Mon Sep 17 00:00:00 2001 From: Christoph Hagen Date: Mon, 7 Aug 2023 15:57:09 +0200 Subject: [PATCH] Create Apple Watch App --- .../AccentColor.colorset/Contents.json | 11 + .../AppIcon.appiconset/Contents.json | 13 + .../Assets.xcassets/Contents.json | 6 + Sesame-Watch Watch App/ContentView.swift | 150 ++++++++++++ Sesame-Watch Watch App/Date+Extensions.swift | 12 + Sesame-Watch Watch App/HistoryView.swift | 13 + Sesame-Watch Watch App/KeyManagement.swift | 121 ++++++++++ .../Preview Assets.xcassets/Contents.json | 6 + Sesame-Watch Watch App/Sesame_WatchApp.swift | 19 ++ Sesame-Watch Watch App/SettingsView.swift | 19 ++ Sesame.xcodeproj/project.pbxproj | 225 +++++++++++++++++- .../UserInterfaceState.xcuserstate | Bin 30473 -> 64302 bytes .../xcschemes/xcschememanagement.plist | 7 +- Sesame/SymmetricKey+Extensions.swift | 23 ++ 14 files changed, 620 insertions(+), 5 deletions(-) create mode 100644 Sesame-Watch Watch App/Assets.xcassets/AccentColor.colorset/Contents.json create mode 100644 Sesame-Watch Watch App/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 Sesame-Watch Watch App/Assets.xcassets/Contents.json create mode 100644 Sesame-Watch Watch App/ContentView.swift create mode 100644 Sesame-Watch Watch App/Date+Extensions.swift create mode 100644 Sesame-Watch Watch App/HistoryView.swift create mode 100644 Sesame-Watch Watch App/KeyManagement.swift create mode 100644 Sesame-Watch Watch App/Preview Content/Preview Assets.xcassets/Contents.json create mode 100644 Sesame-Watch Watch App/Sesame_WatchApp.swift create mode 100644 Sesame-Watch Watch App/SettingsView.swift diff --git a/Sesame-Watch Watch App/Assets.xcassets/AccentColor.colorset/Contents.json b/Sesame-Watch Watch App/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/Sesame-Watch Watch App/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Sesame-Watch Watch App/Assets.xcassets/AppIcon.appiconset/Contents.json b/Sesame-Watch Watch App/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..49c81cd --- /dev/null +++ b/Sesame-Watch Watch App/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,13 @@ +{ + "images" : [ + { + "idiom" : "universal", + "platform" : "watchos", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Sesame-Watch Watch App/Assets.xcassets/Contents.json b/Sesame-Watch Watch App/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/Sesame-Watch Watch App/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Sesame-Watch Watch App/ContentView.swift b/Sesame-Watch Watch App/ContentView.swift new file mode 100644 index 0000000..455ee26 --- /dev/null +++ b/Sesame-Watch Watch App/ContentView.swift @@ -0,0 +1,150 @@ +import SwiftUI +import CryptoKit + +struct ContentView: View { + + @AppStorage("server") + var serverPath: String = "https://christophhagen.de/sesame/" + + @AppStorage("localIP") + var localAddress: String = "192.168.178.104/" + + @AppStorage("counter") + var nextMessageCounter: Int = 0 + + @AppStorage("compensate") + var isCompensatingDaylightTime: Bool = false + + @AppStorage("local") + private var useLocalConnection = false + + @AppStorage("deviceId") + private var deviceId: Int = 0 + + @EnvironmentObject + var keyManager: KeyManagement + + @State + var state: ClientState = .noKeyAvailable + + @State + private var hasActiveRequest = false + + let server = Client() + + var buttonBackground: Color { + state.allowsAction ? + .white.opacity(0.2) : + .black.opacity(0.2) + } + + let buttonBorderWidth: CGFloat = 3 + + var buttonColor: Color { + state.allowsAction ? .white : .gray + } + + private let sidePaddingRatio: CGFloat = 0.05 + private let buttonSizeRatio: CGFloat = 0.9 + + private let smallButtonHeight: CGFloat = 50 + + private let smallButtonWidth: CGFloat = 120 + + private let smallButtonBorderWidth: CGFloat = 1 + + var compensationTime: UInt32 { + isCompensatingDaylightTime ? 3600 : 0 + } + + var isPerformingRequests: Bool { + hasActiveRequest || + state == .waitingForResponse + } + + var body: some View { + VStack(alignment: .center) { + Spacer() + GeometryReader { geo in + HStack(alignment: .center) { + Spacer() + let buttonWidth = min(geo.size.width, geo.size.height) + Text(state.actionText) + .frame(width: buttonWidth, height: buttonWidth) + .background(buttonBackground) + .cornerRadius(buttonWidth / 2) + .overlay(RoundedRectangle(cornerRadius: buttonWidth / 2) + .stroke(lineWidth: buttonBorderWidth).foregroundColor(buttonColor)) + .foregroundColor(buttonColor) + .font(.title) + .disabled(!state.allowsAction) + .onTapGesture(perform: mainButtonPressed) + Spacer() + } + } + Spacer() + } + .background(state.color) + .animation(.easeInOut, value: state.color) + } + + func mainButtonPressed() { + guard let key = keyManager.get(.remoteKey), + let token = keyManager.get(.authToken)?.data, + let deviceId = UInt8(exactly: deviceId) else { + return + } + let count = UInt32(nextMessageCounter) + let sentTime = Date() + // Add time to compensate that the device is using daylight savings time + let content = Message.Content( + time: sentTime.timestamp + compensationTime, + id: count, + device: deviceId) + let message = content.authenticate(using: key) + let historyItem = HistoryItem(sent: message.content, date: sentTime, local: useLocalConnection) + state = .waitingForResponse + print("Sending message \(count)") + Task { + let (newState, responseMessage) = await send(message, authToken: token) + let receivedTime = Date.now + //responseTime = receivedTime + state = newState + let finishedItem = historyItem.didReceive(response: newState, date: receivedTime, message: responseMessage?.content) + guard let key = keyManager.get(.deviceKey) else { + save(historyItem: finishedItem.notAuthenticated()) + return + } + guard let responseMessage else { + save(historyItem: finishedItem) + return + } + guard responseMessage.isValid(using: key) else { + save(historyItem: finishedItem.invalidated()) + return + } + + nextMessageCounter = Int(responseMessage.content.id) + save(historyItem: finishedItem) + } + } + + private func send(_ message: Message, authToken: Data) async -> (state: ClientState, response: Message?) { + if useLocalConnection { + return await server.sendMessageOverLocalNetwork(message, server: localAddress) + } else { + return await server.send(message, server: serverPath, authToken: authToken) + } + } + + private func save(historyItem: HistoryItem) { + + } +} + +struct ContentView_Previews: PreviewProvider { + static var previews: some View { + ContentView() + .environmentObject(KeyManagement()) + } +} diff --git a/Sesame-Watch Watch App/Date+Extensions.swift b/Sesame-Watch Watch App/Date+Extensions.swift new file mode 100644 index 0000000..188a45f --- /dev/null +++ b/Sesame-Watch Watch App/Date+Extensions.swift @@ -0,0 +1,12 @@ +import Foundation + +extension Date { + + var timestamp: UInt32 { + UInt32(timeIntervalSince1970.rounded()) + } + + init(timestamp: UInt32) { + self.init(timeIntervalSince1970: TimeInterval(timestamp)) + } +} diff --git a/Sesame-Watch Watch App/HistoryView.swift b/Sesame-Watch Watch App/HistoryView.swift new file mode 100644 index 0000000..4037310 --- /dev/null +++ b/Sesame-Watch Watch App/HistoryView.swift @@ -0,0 +1,13 @@ +import SwiftUI + +struct HistoryView: View { + var body: some View { + Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/) + } +} + +struct HistoryView_Previews: PreviewProvider { + static var previews: some View { + HistoryView() + } +} diff --git a/Sesame-Watch Watch App/KeyManagement.swift b/Sesame-Watch Watch App/KeyManagement.swift new file mode 100644 index 0000000..8558a1f --- /dev/null +++ b/Sesame-Watch Watch App/KeyManagement.swift @@ -0,0 +1,121 @@ +import Foundation +import CryptoKit +import SwiftUI + +private let localKey: [UInt8] = [ + 0x98, 0x36, 0x91, 0x09, 0x29, 0xa0, 0x54, 0x44, + 0x03, 0x0c, 0xa5, 0xb4, 0x20, 0x16, 0x10, 0x0d, + 0xaf, 0x41, 0x9b, 0x26, 0x4f, 0x75, 0xa4, 0x61, + 0xed, 0x15, 0x0c, 0xb3, 0x06, 0x39, 0x92, 0x59] + + +private let remoteKey: [UInt8] = [ + 0xfa, 0x23, 0xf6, 0x98, 0xea, 0x87, 0x23, 0xa0, + 0xa0, 0xbe, 0x9a, 0xdb, 0x31, 0x28, 0xcb, 0x7d, + 0xd3, 0xa5, 0x7b, 0xf0, 0xc0, 0xeb, 0x45, 0x65, + 0x4d, 0x94, 0x50, 0x1a, 0x2f, 0x6f, 0xeb, 0x70] + +private let authToken: [UInt8] = { + let s = "Y6QzDK5DaFK1w2oEX5OkzoC0nTqP8w5IxpvWAR1mpro=" + let t = Data(base64Encoded: s.data(using: .utf8)!)! + return Array(t) +}() + +extension KeyManagement { + + enum KeyType: String, Identifiable, CaseIterable { + + case deviceKey = "sesame-device" + case remoteKey = "sesame-remote" + case authToken = "sesame-remote-auth" + + var id: String { + rawValue + } + + var displayName: String { + switch self { + case .deviceKey: + return "Device Key" + case .remoteKey: + return "Remote Key" + case .authToken: + return "Authentication Token" + } + } + + var keyLength: SymmetricKeySize { + .bits256 + } + + var usesHashing: Bool { + switch self { + case .authToken: + return true + default: + return false + } + } + } +} + +extension KeyManagement.KeyType: CustomStringConvertible { + + var description: String { + displayName + } +} + +final class KeyManagement: ObservableObject { + + + @Published + private(set) var hasRemoteKey = true + + @Published + private(set) var hasDeviceKey = true + + @Published + private(set) var hasAuthToken = true + + var hasAllKeys: Bool { + hasRemoteKey && hasDeviceKey && hasAuthToken + } + + init() {} + + func has(_ type: KeyType) -> Bool { + switch type { + case .deviceKey: + return hasDeviceKey + case .remoteKey: + return hasRemoteKey + case .authToken: + return hasAuthToken + } + } + + func get(_ type: KeyType) -> SymmetricKey? { + let bytes: [UInt8] = get(type) + return SymmetricKey(data: bytes) + } + + private func get(_ type: KeyType) -> [UInt8] { + switch type { + case .deviceKey: + return remoteKey + case .remoteKey: + return localKey + case .authToken: + return authToken + } + } + + func delete(_ type: KeyType) { + + } + + func generate(_ type: KeyType) { + + } +} diff --git a/Sesame-Watch Watch App/Preview Content/Preview Assets.xcassets/Contents.json b/Sesame-Watch Watch App/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/Sesame-Watch Watch App/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Sesame-Watch Watch App/Sesame_WatchApp.swift b/Sesame-Watch Watch App/Sesame_WatchApp.swift new file mode 100644 index 0000000..849c66d --- /dev/null +++ b/Sesame-Watch Watch App/Sesame_WatchApp.swift @@ -0,0 +1,19 @@ +import SwiftUI + +@main +struct Sesame_Watch_Watch_AppApp: App { + + let keyManagement = KeyManagement() + + var body: some Scene { + WindowGroup { + TabView { + ContentView() + .environmentObject(keyManagement) + SettingsView() + HistoryView() + } + .tabViewStyle(PageTabViewStyle()) + } + } +} diff --git a/Sesame-Watch Watch App/SettingsView.swift b/Sesame-Watch Watch App/SettingsView.swift new file mode 100644 index 0000000..2f084af --- /dev/null +++ b/Sesame-Watch Watch App/SettingsView.swift @@ -0,0 +1,19 @@ +import SwiftUI + +struct SettingsView: View { + var body: some View { + ScrollView { + VStack { + Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/) + } + } + .navigationTitle("Settings") + } +} + +struct SettingsView_Previews: PreviewProvider { + static var previews: some View { + SettingsView() + .previewDevice("Apple Watch Series 7 - 41mm") + } +} diff --git a/Sesame.xcodeproj/project.pbxproj b/Sesame.xcodeproj/project.pbxproj index 995eafd..b17d741 100644 --- a/Sesame.xcodeproj/project.pbxproj +++ b/Sesame.xcodeproj/project.pbxproj @@ -14,10 +14,31 @@ 884A45C5279F4BBE00D6E650 /* KeyManagement.swift in Sources */ = {isa = PBXBuildFile; fileRef = 884A45C4279F4BBE00D6E650 /* KeyManagement.swift */; }; 884A45C927A43D7900D6E650 /* ClientState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 884A45C827A43D7900D6E650 /* ClientState.swift */; }; 884A45CB27A464C000D6E650 /* SymmetricKey+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 884A45CA27A464C000D6E650 /* SymmetricKey+Extensions.swift */; }; - 884A45CD27A465F500D6E650 /* Client.swift in Sources */ = {isa = PBXBuildFile; fileRef = 884A45CC27A465F500D6E650 /* Client.swift */; }; 884A45CF27A5402D00D6E650 /* MessageResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 884A45CE27A5402D00D6E650 /* MessageResult.swift */; }; 8864664F29E5684C004FE2BE /* CBORCoding in Frameworks */ = {isa = PBXBuildFile; productRef = 8864664E29E5684C004FE2BE /* CBORCoding */; }; 8864665229E5939C004FE2BE /* SFSafeSymbols in Frameworks */ = {isa = PBXBuildFile; productRef = 8864665129E5939C004FE2BE /* SFSafeSymbols */; }; + 888362342A80F3F90032BBB2 /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 888362332A80F3F90032BBB2 /* SettingsView.swift */; }; + 888362362A80F4420032BBB2 /* HistoryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 888362352A80F4420032BBB2 /* HistoryView.swift */; }; + 88E197B229EDC9BC00BF1D19 /* Sesame_WatchApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88E197B129EDC9BC00BF1D19 /* Sesame_WatchApp.swift */; }; + 88E197B429EDC9BC00BF1D19 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88E197B329EDC9BC00BF1D19 /* ContentView.swift */; }; + 88E197B629EDC9BD00BF1D19 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 88E197B529EDC9BD00BF1D19 /* Assets.xcassets */; }; + 88E197B929EDC9BD00BF1D19 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 88E197B829EDC9BD00BF1D19 /* Preview Assets.xcassets */; }; + 88E197C229EDCB0900BF1D19 /* KeyManagement.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88E197C129EDCB0900BF1D19 /* KeyManagement.swift */; }; + 88E197C429EDCC8900BF1D19 /* Client.swift in Sources */ = {isa = PBXBuildFile; fileRef = 884A45CC27A465F500D6E650 /* Client.swift */; }; + 88E197C729EDCCBD00BF1D19 /* Client.swift in Sources */ = {isa = PBXBuildFile; fileRef = 884A45CC27A465F500D6E650 /* Client.swift */; }; + 88E197C829EDCCCE00BF1D19 /* ClientState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 884A45C827A43D7900D6E650 /* ClientState.swift */; }; + 88E197C929EDCCE100BF1D19 /* Message.swift in Sources */ = {isa = PBXBuildFile; fileRef = E24EE77827FF95E00011CFD2 /* Message.swift */; }; + 88E197CC29EDCD4900BF1D19 /* NIOCore in Frameworks */ = {isa = PBXBuildFile; productRef = 88E197CB29EDCD4900BF1D19 /* NIOCore */; }; + 88E197CE29EDCD7500BF1D19 /* CBORCoding in Frameworks */ = {isa = PBXBuildFile; productRef = 88E197CD29EDCD7500BF1D19 /* CBORCoding */; }; + 88E197D029EDCD7D00BF1D19 /* SFSafeSymbols in Frameworks */ = {isa = PBXBuildFile; productRef = 88E197CF29EDCD7D00BF1D19 /* SFSafeSymbols */; }; + 88E197D129EDCE5F00BF1D19 /* Data+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E24EE77127FDCCC00011CFD2 /* Data+Extensions.swift */; }; + 88E197D229EDCE6600BF1D19 /* RouteAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2C5C1DA2806FE8900769EF6 /* RouteAPI.swift */; }; + 88E197D329EDCE6E00BF1D19 /* MessageResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 884A45CE27A5402D00D6E650 /* MessageResult.swift */; }; + 88E197D429EDCE7600BF1D19 /* UInt32+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2C5C1DC281B3AC400769EF6 /* UInt32+Extensions.swift */; }; + 88E197D529EDCE8800BF1D19 /* ServerMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2C5C1F7281E769F00769EF6 /* ServerMessage.swift */; }; + 88E197D729EDCFE800BF1D19 /* Date+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88E197D629EDCFE800BF1D19 /* Date+Extensions.swift */; }; + 88E197D829EDD13B00BF1D19 /* SymmetricKey+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 884A45CA27A464C000D6E650 /* SymmetricKey+Extensions.swift */; }; + 88E197D929EDD14D00BF1D19 /* HistoryItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = E28DED34281EB17600259690 /* HistoryItem.swift */; }; E24EE77227FDCCC00011CFD2 /* Data+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E24EE77127FDCCC00011CFD2 /* Data+Extensions.swift */; }; E24EE77427FF95920011CFD2 /* DeviceResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = E24EE77327FF95920011CFD2 /* DeviceResponse.swift */; }; E24EE77727FF95C00011CFD2 /* NIOCore in Frameworks */ = {isa = PBXBuildFile; productRef = E24EE77627FF95C00011CFD2 /* NIOCore */; }; @@ -44,6 +65,15 @@ 884A45CA27A464C000D6E650 /* SymmetricKey+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SymmetricKey+Extensions.swift"; sourceTree = ""; }; 884A45CC27A465F500D6E650 /* Client.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Client.swift; sourceTree = ""; }; 884A45CE27A5402D00D6E650 /* MessageResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageResult.swift; sourceTree = ""; }; + 888362332A80F3F90032BBB2 /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = ""; }; + 888362352A80F4420032BBB2 /* HistoryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryView.swift; sourceTree = ""; }; + 88E197AC29EDC9BC00BF1D19 /* Sesame-Watch Watch App.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Sesame-Watch Watch App.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 88E197B129EDC9BC00BF1D19 /* Sesame_WatchApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Sesame_WatchApp.swift; sourceTree = ""; }; + 88E197B329EDC9BC00BF1D19 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; + 88E197B529EDC9BD00BF1D19 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 88E197B829EDC9BD00BF1D19 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; + 88E197C129EDCB0900BF1D19 /* KeyManagement.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyManagement.swift; sourceTree = ""; }; + 88E197D629EDCFE800BF1D19 /* Date+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+Extensions.swift"; sourceTree = ""; }; E24EE77127FDCCC00011CFD2 /* Data+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Data+Extensions.swift"; sourceTree = ""; }; E24EE77327FF95920011CFD2 /* DeviceResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceResponse.swift; sourceTree = ""; }; E24EE77827FF95E00011CFD2 /* Message.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Message.swift; sourceTree = ""; }; @@ -70,6 +100,16 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 88E197A929EDC9BC00BF1D19 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 88E197D029EDCD7D00BF1D19 /* SFSafeSymbols in Frameworks */, + 88E197CE29EDCD7500BF1D19 /* CBORCoding in Frameworks */, + 88E197CC29EDCD4900BF1D19 /* NIOCore in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ @@ -77,7 +117,9 @@ isa = PBXGroup; children = ( 884A45B5279F48C100D6E650 /* Sesame */, + 88E197B029EDC9BC00BF1D19 /* Sesame-Watch Watch App */, 884A45B4279F48C100D6E650 /* Products */, + 88E197CA29EDCD4900BF1D19 /* Frameworks */, ); sourceTree = ""; }; @@ -85,6 +127,7 @@ isa = PBXGroup; children = ( 884A45B3279F48C100D6E650 /* Sesame.app */, + 88E197AC29EDC9BC00BF1D19 /* Sesame-Watch Watch App.app */, ); name = Products; sourceTree = ""; @@ -102,8 +145,8 @@ E28DED36281EC7FB00259690 /* HistoryManager.swift */, E28DED2C281E840B00259690 /* SettingsView.swift */, E28DED2E281E8A0500259690 /* SingleKeyView.swift */, - 884A45CC27A465F500D6E650 /* Client.swift */, 884A45C827A43D7900D6E650 /* ClientState.swift */, + 884A45CC27A465F500D6E650 /* Client.swift */, 884A45C4279F4BBE00D6E650 /* KeyManagement.swift */, 884A45CA27A464C000D6E650 /* SymmetricKey+Extensions.swift */, 884A45BA279F48C300D6E650 /* Assets.xcassets */, @@ -120,6 +163,36 @@ path = "Preview Content"; sourceTree = ""; }; + 88E197B029EDC9BC00BF1D19 /* Sesame-Watch Watch App */ = { + isa = PBXGroup; + children = ( + 88E197B129EDC9BC00BF1D19 /* Sesame_WatchApp.swift */, + 88E197B329EDC9BC00BF1D19 /* ContentView.swift */, + 888362332A80F3F90032BBB2 /* SettingsView.swift */, + 888362352A80F4420032BBB2 /* HistoryView.swift */, + 88E197C129EDCB0900BF1D19 /* KeyManagement.swift */, + 88E197B529EDC9BD00BF1D19 /* Assets.xcassets */, + 88E197B729EDC9BD00BF1D19 /* Preview Content */, + 88E197D629EDCFE800BF1D19 /* Date+Extensions.swift */, + ); + path = "Sesame-Watch Watch App"; + sourceTree = ""; + }; + 88E197B729EDC9BD00BF1D19 /* Preview Content */ = { + isa = PBXGroup; + children = ( + 88E197B829EDC9BD00BF1D19 /* Preview Assets.xcassets */, + ); + path = "Preview Content"; + sourceTree = ""; + }; + 88E197CA29EDCD4900BF1D19 /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; E2C5C1D92806FE4A00769EF6 /* API */ = { isa = PBXGroup; children = ( @@ -159,6 +232,28 @@ productReference = 884A45B3279F48C100D6E650 /* Sesame.app */; productType = "com.apple.product-type.application"; }; + 88E197AB29EDC9BC00BF1D19 /* Sesame-Watch Watch App */ = { + isa = PBXNativeTarget; + buildConfigurationList = 88E197BF29EDC9BD00BF1D19 /* Build configuration list for PBXNativeTarget "Sesame-Watch Watch App" */; + buildPhases = ( + 88E197A829EDC9BC00BF1D19 /* Sources */, + 88E197A929EDC9BC00BF1D19 /* Frameworks */, + 88E197AA29EDC9BC00BF1D19 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "Sesame-Watch Watch App"; + packageProductDependencies = ( + 88E197CB29EDCD4900BF1D19 /* NIOCore */, + 88E197CD29EDCD7500BF1D19 /* CBORCoding */, + 88E197CF29EDCD7D00BF1D19 /* SFSafeSymbols */, + ); + productName = "Sesame-Watch Watch App"; + productReference = 88E197AC29EDC9BC00BF1D19 /* Sesame-Watch Watch App.app */; + productType = "com.apple.product-type.application"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -166,13 +261,16 @@ isa = PBXProject; attributes = { BuildIndependentTargetsInParallel = 1; - LastSwiftUpdateCheck = 1320; + LastSwiftUpdateCheck = 1430; LastUpgradeCheck = 1320; TargetAttributes = { 884A45B2279F48C100D6E650 = { CreatedOnToolsVersion = 13.2.1; LastSwiftMigration = 1430; }; + 88E197AB29EDC9BC00BF1D19 = { + CreatedOnToolsVersion = 14.3; + }; }; }; buildConfigurationList = 884A45AE279F48C100D6E650 /* Build configuration list for PBXProject "Sesame" */; @@ -194,6 +292,7 @@ projectRoot = ""; targets = ( 884A45B2279F48C100D6E650 /* Sesame */, + 88E197AB29EDC9BC00BF1D19 /* Sesame-Watch Watch App */, ); }; /* End PBXProject section */ @@ -208,6 +307,15 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 88E197AA29EDC9BC00BF1D19 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 88E197B929EDC9BD00BF1D19 /* Preview Assets.xcassets in Resources */, + 88E197B629EDC9BD00BF1D19 /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -221,7 +329,6 @@ 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 */, @@ -232,11 +339,35 @@ E28DED33281EB15B00259690 /* HistoryListItem.swift in Sources */, E28DED2D281E840B00259690 /* SettingsView.swift in Sources */, 884A45B7279F48C100D6E650 /* SesameApp.swift in Sources */, + 88E197C429EDCC8900BF1D19 /* Client.swift in Sources */, 884A45C5279F4BBE00D6E650 /* KeyManagement.swift in Sources */, E2C5C1F8281E769F00769EF6 /* ServerMessage.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; + 88E197A829EDC9BC00BF1D19 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 888362342A80F3F90032BBB2 /* SettingsView.swift in Sources */, + 88E197B429EDC9BC00BF1D19 /* ContentView.swift in Sources */, + 888362362A80F4420032BBB2 /* HistoryView.swift in Sources */, + 88E197D329EDCE6E00BF1D19 /* MessageResult.swift in Sources */, + 88E197D529EDCE8800BF1D19 /* ServerMessage.swift in Sources */, + 88E197D129EDCE5F00BF1D19 /* Data+Extensions.swift in Sources */, + 88E197D229EDCE6600BF1D19 /* RouteAPI.swift in Sources */, + 88E197D729EDCFE800BF1D19 /* Date+Extensions.swift in Sources */, + 88E197C829EDCCCE00BF1D19 /* ClientState.swift in Sources */, + 88E197B229EDC9BC00BF1D19 /* Sesame_WatchApp.swift in Sources */, + 88E197C929EDCCE100BF1D19 /* Message.swift in Sources */, + 88E197D929EDD14D00BF1D19 /* HistoryItem.swift in Sources */, + 88E197C729EDCCBD00BF1D19 /* Client.swift in Sources */, + 88E197D429EDCE7600BF1D19 /* UInt32+Extensions.swift in Sources */, + 88E197D829EDD13B00BF1D19 /* SymmetricKey+Extensions.swift in Sources */, + 88E197C229EDCB0900BF1D19 /* KeyManagement.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ /* Begin XCBuildConfiguration section */ @@ -423,6 +554,68 @@ }; name = Release; }; + 88E197BD29EDC9BD00BF1D19 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"Sesame-Watch Watch App/Preview Content\""; + DEVELOPMENT_TEAM = H8WR4M6QQ4; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_CFBundleDisplayName = "Sesame-Watch"; + INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; + INFOPLIST_KEY_WKWatchOnly = YES; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = "de.christophhagen.Sesame-Watch.watchkitapp"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = watchos; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 4; + WATCHOS_DEPLOYMENT_TARGET = 9.4; + }; + name = Debug; + }; + 88E197BE29EDC9BD00BF1D19 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"Sesame-Watch Watch App/Preview Content\""; + DEVELOPMENT_TEAM = H8WR4M6QQ4; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_CFBundleDisplayName = "Sesame-Watch"; + INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; + INFOPLIST_KEY_WKWatchOnly = YES; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = "de.christophhagen.Sesame-Watch.watchkitapp"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = watchos; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 4; + WATCHOS_DEPLOYMENT_TARGET = 9.4; + }; + name = Release; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -444,6 +637,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 88E197BF29EDC9BD00BF1D19 /* Build configuration list for PBXNativeTarget "Sesame-Watch Watch App" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 88E197BD29EDC9BD00BF1D19 /* Debug */, + 88E197BE29EDC9BD00BF1D19 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; /* End XCConfigurationList section */ /* Begin XCRemoteSwiftPackageReference section */ @@ -484,6 +686,21 @@ package = 8864665029E5939C004FE2BE /* XCRemoteSwiftPackageReference "SFSafeSymbols" */; productName = SFSafeSymbols; }; + 88E197CB29EDCD4900BF1D19 /* NIOCore */ = { + isa = XCSwiftPackageProductDependency; + package = E24EE77527FF95C00011CFD2 /* XCRemoteSwiftPackageReference "swift-nio" */; + productName = NIOCore; + }; + 88E197CD29EDCD7500BF1D19 /* CBORCoding */ = { + isa = XCSwiftPackageProductDependency; + package = 8864664D29E5684C004FE2BE /* XCRemoteSwiftPackageReference "CBORCoding" */; + productName = CBORCoding; + }; + 88E197CF29EDCD7D00BF1D19 /* SFSafeSymbols */ = { + isa = XCSwiftPackageProductDependency; + package = 8864665029E5939C004FE2BE /* XCRemoteSwiftPackageReference "SFSafeSymbols" */; + productName = SFSafeSymbols; + }; E24EE77627FF95C00011CFD2 /* NIOCore */ = { isa = XCSwiftPackageProductDependency; package = E24EE77527FF95C00011CFD2 /* XCRemoteSwiftPackageReference "swift-nio" */; diff --git a/Sesame.xcodeproj/project.xcworkspace/xcuserdata/imac.xcuserdatad/UserInterfaceState.xcuserstate b/Sesame.xcodeproj/project.xcworkspace/xcuserdata/imac.xcuserdatad/UserInterfaceState.xcuserstate index 902307e0a98a7792950cb4e6777471fce4b8f2f9..baf532c5d41db331c51ff2bcd268693cda730aa9 100644 GIT binary patch literal 64302 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 GiT?+pA~Ng% diff --git a/Sesame.xcodeproj/xcuserdata/imac.xcuserdatad/xcschemes/xcschememanagement.plist b/Sesame.xcodeproj/xcuserdata/imac.xcuserdatad/xcschemes/xcschememanagement.plist index 5ac8256..c304408 100644 --- a/Sesame.xcodeproj/xcuserdata/imac.xcuserdatad/xcschemes/xcschememanagement.plist +++ b/Sesame.xcodeproj/xcuserdata/imac.xcuserdatad/xcschemes/xcschememanagement.plist @@ -4,11 +4,16 @@ SchemeUserState - Sesame.xcscheme_^#shared#^_ + Sesame-Watch Watch App.xcscheme_^#shared#^_ orderHint 0 + Sesame.xcscheme_^#shared#^_ + + orderHint + 1 + diff --git a/Sesame/SymmetricKey+Extensions.swift b/Sesame/SymmetricKey+Extensions.swift index 2066a91..1e517eb 100644 --- a/Sesame/SymmetricKey+Extensions.swift +++ b/Sesame/SymmetricKey+Extensions.swift @@ -46,3 +46,26 @@ extension String { return results.map { String($0) } } } + +let protocolSalt = "CryptoKit Playgrounds Putting It Together".data(using: .utf8)! + +/// Generates an ephemeral key agreement key and performs key agreement to get the shared secret and derive the symmetric encryption key. +func encrypt(_ data: Data, to theirEncryptionKey: Curve25519.KeyAgreement.PublicKey, signedBy ourSigningKey: Curve25519.Signing.PrivateKey) throws -> + (ephemeralPublicKeyData: Data, ciphertext: Data, signature: Data) { + let ephemeralKey = Curve25519.KeyAgreement.PrivateKey() + let ephemeralPublicKey = ephemeralKey.publicKey.rawRepresentation + + let sharedSecret = try ephemeralKey.sharedSecretFromKeyAgreement(with: theirEncryptionKey) + + let symmetricKey = sharedSecret.hkdfDerivedSymmetricKey(using: SHA256.self, + salt: protocolSalt, + sharedInfo: ephemeralPublicKey + + theirEncryptionKey.rawRepresentation + + ourSigningKey.publicKey.rawRepresentation, + outputByteCount: 32) + + let ciphertext = try ChaChaPoly.seal(data, using: symmetricKey).combined + let signature = try ourSigningKey.signature(for: ciphertext + ephemeralPublicKey + theirEncryptionKey.rawRepresentation) + + return (ephemeralPublicKey, ciphertext, signature) +}