From 9f86b58abaa3ba175729711ce9643dde25726bfb Mon Sep 17 00:00:00 2001 From: Christian Risi <75698846+CnF-Gris@users.noreply.github.com> Date: Mon, 16 Dec 2024 10:14:44 +0100 Subject: [PATCH] V0.6.5 Arroyo Toad Added configuration to send messages V0.6.5.a Arroyo Toad Problems with a detached head --- .vscode/launch.json | 43 +++--- .../CustomCode/Utils/P256-keys-creation.swift | 70 +++++++++ Sources/App/entrypoint.swift | 68 +++++---- Sources/App/simulator-configuration.swift | 144 ++++++++++++++++++ env/debug/.debug.env | 3 + env/debug/.debug.env.example | 3 + 6 files changed, 279 insertions(+), 52 deletions(-) create mode 100644 Sources/App/CustomCode/Utils/P256-keys-creation.swift create mode 100644 Sources/App/simulator-configuration.swift create mode 100644 env/debug/.debug.env create mode 100644 env/debug/.debug.env.example diff --git a/.vscode/launch.json b/.vscode/launch.json index 00bed33..247d3b6 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,22 +1,23 @@ -{ - "configurations": [ - { - "type": "lldb", - "request": "launch", - "args": [], - "cwd": "${workspaceFolder:workspace}", - "name": "Debug IoT-Simulator", - "program": "${workspaceFolder:workspace}/.build/debug/IoT-Simulator", - "preLaunchTask": "swift: Build Debug IoT-Simulator" - }, - { - "type": "lldb", - "request": "launch", - "args": [], - "cwd": "${workspaceFolder:workspace}", - "name": "Release IoT-Simulator", - "program": "${workspaceFolder:workspace}/.build/release/IoT-Simulator", - "preLaunchTask": "swift: Build Release IoT-Simulator" - } - ] +{ + "configurations": [ + { + "type": "lldb", + "request": "launch", + "args": [], + "cwd": "${workspaceFolder:workspace}", + "name": "Debug IoT-Simulator", + "envFile": "${workspaceFolder}/env/debug/.debug.env", + "program": "${workspaceFolder}/.build/debug/App", + "preLaunchTask": "swift: Build Debug IoT-Simulator" + }, + { + "type": "lldb", + "request": "launch", + "args": [], + "cwd": "${workspaceFolder:workspace}", + "name": "Release IoT-Simulator", + "program": "${workspaceFolder:workspace}/.build/release/IoT-Simulator", + "preLaunchTask": "swift: Build Release IoT-Simulator" + } + ] } \ No newline at end of file diff --git a/Sources/App/CustomCode/Utils/P256-keys-creation.swift b/Sources/App/CustomCode/Utils/P256-keys-creation.swift new file mode 100644 index 0000000..f9ce78d --- /dev/null +++ b/Sources/App/CustomCode/Utils/P256-keys-creation.swift @@ -0,0 +1,70 @@ +import Foundation +import FoundationNetworking +import Crypto + +public func createPrivateP256Key() -> P256.Signing.PrivateKey { + return P256.Signing.PrivateKey() +} + +public func createPublickP256Key(privateKey: P256.Signing.PrivateKey ) -> P256.Signing.PublicKey { + return privateKey.publicKey +} + +public func publicP256_2_Spki(publicKey: P256.Signing.PublicKey) -> String { + return publicKey.pemRepresentation +} + +public func privateP256_2_pem(privateKey: P256.Signing.PrivateKey) -> String { + return privateKey.pemRepresentation +} + +// UGLY: Refactor to make it easier to comprehend +public func fetchPrivateP256Key(deviceID: UInt) async throws -> P256.Signing.PrivateKey { + + // UGLY: but fast + let privateKeyFolder = ProcessInfo.processInfo.environment["PRIVATE_KEY_FOLDER"] ?? "./Private/PrivateKeysP256" + + let keyFilePath = "\(privateKeyFolder)/\(deviceID)-Kr.pem" + + do { + let key = try pem2_P265_PrivateKey(filePath: keyFilePath) + // TODO: send public key to another server + + + return key + } catch { + // Do nothing + } + + + let key = createPrivateP256Key() + + let publicKey = key.publicKey.pemRepresentation + + try privateP256_2_pem(privateKey: key).write(to: URL(filePath: keyFilePath), atomically: true, encoding: String.Encoding.utf8) + + + // UGLY: hardcoded + var httpRequest = URLRequest(url: URL(string: "http://public-key-db.internal/key")!) + httpRequest.setValue("application/json", forHTTPHeaderField: "Content-Type") + httpRequest.httpMethod = "POST" + + let message = PublicKeyMessage(deviceID: deviceID, publicKey: publicKey) + let encoder = JSONEncoder() + let data = try encoder.encode(message) + httpRequest.httpBody = data + + let _ = try await URLSession.shared.upload(for: httpRequest, from: data) + return key + + + + +} + +private func pem2_P265_PrivateKey(filePath: String) throws -> P256.Signing.PrivateKey { + + let pemEncodedKey = try String(contentsOf: URL(filePath: filePath), encoding: .utf8) + return try P256.Signing.PrivateKey(pemRepresentation: pemEncodedKey) + +} \ No newline at end of file diff --git a/Sources/App/entrypoint.swift b/Sources/App/entrypoint.swift index 2e85ece..1301bbb 100644 --- a/Sources/App/entrypoint.swift +++ b/Sources/App/entrypoint.swift @@ -1,31 +1,37 @@ -import Vapor -import Logging -import NIOCore -import NIOPosix - -@main -enum Entrypoint { - static func main() async throws { - var env = try Environment.detect() - try LoggingSystem.bootstrap(from: &env) - - let app = try await Application.make(env) - - // This attempts to install NIO as the Swift Concurrency global executor. - // You can enable it if you'd like to reduce the amount of context switching between NIO and Swift Concurrency. - // Note: this has caused issues with some libraries that use `.wait()` and cleanly shutting down. - // If enabled, you should be careful about calling async functions before this point as it can cause assertion failures. - // let executorTakeoverSuccess = NIOSingletons.unsafeTryInstallSingletonPosixEventLoopGroupAsConcurrencyGlobalExecutor() - // app.logger.debug("Tried to install SwiftNIO's EventLoopGroup as Swift's global concurrency executor", metadata: ["success": .stringConvertible(executorTakeoverSuccess)]) - - do { - try await configure(app) - } catch { - app.logger.report(error: error) - try? await app.asyncShutdown() - throw error - } - try await app.execute() - try await app.asyncShutdown() - } -} +import Vapor +import Logging +import NIOCore +import NIOPosix + +@main +enum Entrypoint { + static func main() async throws { + var env = try Environment.detect() + try LoggingSystem.bootstrap(from: &env) + + let app = try await Application.make(env) + + // This attempts to install NIO as the Swift Concurrency global executor. + // You can enable it if you'd like to reduce the amount of context switching between NIO and Swift Concurrency. + // Note: this has caused issues with some libraries that use `.wait()` and cleanly shutting down. + // If enabled, you should be careful about calling async functions before this point as it can cause assertion failures. + // let executorTakeoverSuccess = NIOSingletons.unsafeTryInstallSingletonPosixEventLoopGroupAsConcurrencyGlobalExecutor() + // app.logger.debug("Tried to install SwiftNIO's EventLoopGroup as Swift's global concurrency executor", metadata: ["success": .stringConvertible(executorTakeoverSuccess)]) + + do { + try await configure(app) + } catch { + app.logger.report(error: error) + try? await app.asyncShutdown() + throw error + } + + do { + try await configureSimulator() + } catch { + print("\(error)") + } + try await app.execute() + try await app.asyncShutdown() + } +} diff --git a/Sources/App/simulator-configuration.swift b/Sources/App/simulator-configuration.swift new file mode 100644 index 0000000..7f94f6f --- /dev/null +++ b/Sources/App/simulator-configuration.swift @@ -0,0 +1,144 @@ +import Crypto +import Foundation +import FoundationNetworking +import IoT_Simulator_Core +import MessageUtils + +public func configureSimulator() async throws { + + guard + let configurationFilePath: String = ProcessInfo.processInfo.environment[ + "CONFIGURATION_FILE"] + else { + throw ParsingError.ConfigFileNotExistent + } + + let jsonData: Data = try Data(contentsOf: URL(filePath: configurationFilePath)) + + guard + let jsonObject: [String: Any] = try JSONSerialization.jsonObject(with: jsonData) + as? [String: Any] + else { + throw ParsingError.MalformedJSON(reason: "This is not a JSON file") + } + + try await jsonConfigurationParser(jsonObject) + +} + +private func jsonConfigurationParser(_ json: [String: Any]) async throws { + + guard + let version = json["version"] as? Int, + let environments = json["environments"] as? [[String: Any]] + else { + throw ParsingError.MalformedJSON(reason: "Missing either version or environemnt") + } + + for environmentJSON in environments { + + let env = try json2env(environmentJSON) + + IoTSimulatorCore.addEnv(environment: env) + + if let devices = environmentJSON["devices"] as? [[String: Any]] { + for deviceJSON in devices { + let devices = try await json2edge_dev(deviceJSON) + + for dev in devices { + try IoTSimulatorCore.addDevice(location: env.location, device: dev) { msg in + // UGLY: But fast + // TODO: add sending code here + let backendURL = ProcessInfo.processInfo.environment["BACKEND_URL"]! + + var httpRequest = URLRequest(url: URL(string: backendURL)!) + httpRequest.setValue("application/octet-stream", forHTTPHeaderField: "Content-Type") + httpRequest.httpMethod = "POST" + + httpRequest.httpBody = msg + let _ = try await URLSession.shared.upload(for: httpRequest, from: msg) + + } failure: { + print("Failed") + } + } + } + } + + } + +} + +private func json2env(_ json: [String: Any]) throws -> PhysicalEnvironment { + + guard + let envName = json["name"] as? String + else { + throw ParsingError.MalformedJSON(reason: "Missing name of environment") + } + + let environment = PhysicalEnvironment(envName) + + if let physicalData = json["physicalData"] as? [[String: Any]] { + + for physicalDatum in physicalData { + guard + let dataTypeString = physicalDatum["dataType"] as? String, + let value = physicalDatum["value"] as? Double, + let dataType = DataType(rawValue: dataTypeString) + else { + throw ParsingError.MalformedJSON(reason: "Physical Data is Misconfigured") + } + + let datum = PhysicalData(dataType, Float(value)) + environment.setPhysicalData(datum.type, datum) + } + + } + + return environment +} + +private func json2edge_dev(_ json: [String: Any]) async throws -> [EdgeDevice] { + + var devices: [EdgeDevice] = [] + + guard + let _dataType = json["dataType"] as? String, + let dataType = DataType(rawValue: _dataType) + else { + throw ParsingError.MalformedJSON(reason: "Data Type missing in one device") + } + + if let number = json["number"] as? UInt { + for _ in 0..