diff --git a/Sources/App/DeviceResponse.swift b/Sources/App/DeviceResponse.swift index 6e47de0..78cda5b 100644 --- a/Sources/App/DeviceResponse.swift +++ b/Sources/App/DeviceResponse.swift @@ -24,14 +24,10 @@ struct DeviceResponse { .init(event: .noBodyData) } - static var corruptkeyData: DeviceResponse { - .init(event: .corruptkeyData) - } - static var operationInProgress: DeviceResponse { .init(event: .operationInProgress) } - + /// The response to a key from the server let event: MessageResult @@ -39,7 +35,7 @@ struct DeviceResponse { let response: Message? init?(_ buffer: ByteBuffer) { - guard let byte = buffer.getData(at: 0, length: 1) else { + guard let byte = buffer.getBytes(at: 0, length: 1) else { print("No bytes received from device") return nil } diff --git a/Sources/App/Message.swift b/Sources/App/Message.swift index 42811eb..0c6083c 100644 --- a/Sources/App/Message.swift +++ b/Sources/App/Message.swift @@ -1,7 +1,6 @@ import Foundation import CryptoKit import NIOCore -import Vapor struct Message: Equatable, Hashable { @@ -20,9 +19,9 @@ struct Message: Equatable, Hashable { self.id = id } - init(decodeFrom data: Data) { - self.time = UInt32(data: data[data.startIndex..(decodeFrom data: T) where T.Element == UInt8 { + self.time = UInt32(data: data.prefix(4)) + self.id = UInt32(data: data.dropFirst(4)) } static var length: Int { @@ -59,16 +58,16 @@ struct Message: Equatable, Hashable { } init?(decodeFrom buffer: ByteBuffer) { - guard let data = buffer.getData(at: 0, length: Message.length) else { + guard let data = buffer.getBytes(at: 0, length: Message.length) else { return nil } self.init(decodeFrom: data) } - private init(decodeFrom data: Data) { + private init(decodeFrom data: T) where T.Element == UInt8 { let count = SHA256Digest.byteCount - self.mac = data[data.startIndex.. Bool { @@ -86,13 +85,12 @@ struct Message: Equatable, Hashable { extension UInt32 { - init(data: Data) { + init(data: T) where T.Element == UInt8 { self = data .reversed() .enumerated() .map { UInt32($0.element) << ($0.offset * 8) } .reduce(0, +) - } var encoded: Data { diff --git a/Sources/App/MessageResult.swift b/Sources/App/MessageResult.swift index 7b4dfa0..384332f 100644 --- a/Sources/App/MessageResult.swift +++ b/Sources/App/MessageResult.swift @@ -4,41 +4,35 @@ import Foundation A result from sending a key to the device. */ enum MessageResult: UInt8 { - + /// Text content was received, although binary data was expected case textReceived = 1 - + /// A socket event on the device was unexpected (not binary data) case unexpectedSocketEvent = 2 - + /// The size of the payload (i.e. message) was invalid, or the data could not be read case invalidMessageData = 3 /// The transmitted message could not be authenticated using the key case messageAuthenticationFailed = 4 - + /// The message time was not within the acceptable bounds case messageTimeMismatch = 5 - + /// A later key has been used, invalidating this key (to prevent replay attacks after blocked communication) case messageCounterInvalid = 6 - + /// The key was accepted by the device, and the door will be opened case messageAccepted = 7 - /// The device produced an unknown error - case unknownDeviceError = 9 - /// The request did not contain body data with the key case noBodyData = 10 - - /// The body data could not be read - case corruptkeyData = 11 - + /// The device is not connected case deviceNotConnected = 12 - + /// The device did not respond within the timeout case deviceTimedOut = 13 @@ -47,7 +41,7 @@ enum MessageResult: UInt8 { } extension MessageResult: CustomStringConvertible { - + var description: String { switch self { case .textReceived: @@ -64,12 +58,8 @@ extension MessageResult: CustomStringConvertible { return "Message counter invalid" case .messageAccepted: return "Message accepted" - case .unknownDeviceError: - return "The device experienced an unknown error" case .noBodyData: return "No body data included in the request" - case .corruptkeyData: - return "Key data corrupted" case .deviceNotConnected: return "Device not connected" case .deviceTimedOut: diff --git a/Sources/App/routes.swift b/Sources/App/routes.swift index 663a84d..c4c7ccf 100755 --- a/Sources/App/routes.swift +++ b/Sources/App/routes.swift @@ -33,7 +33,7 @@ func routes(_ app: Application) throws { } /** - Post a key to the device for unlocking. + Post a message to the device for unlocking. The request returns one or `Message.length+1` bytes of data, where the first byte is the raw value of a `MessageResult`, and the optional following bytes contain the response message of the device. This request does not complete until either the device responds or the request times out. The timeout is specified by `KeyManagement.deviceTimeout`. @@ -45,9 +45,9 @@ func routes(_ app: Application) throws { } /** - Start a new websocket connection for the client to receive table updates from the server + Start a new websocket connection for the device to receive messages from the server - Returns: Nothing - - Note: The first (and only) message from the client over the connection must be a valid session token. + - Note: The first message from the device over the connection must be a valid auth token. */ app.webSocket(PublicAPI.socket.path) { req, socket in socket.onBinary { _, data in diff --git a/Tests/AppTests/AppTests.swift b/Tests/AppTests/AppTests.swift index 12ecc39..2074e64 100644 --- a/Tests/AppTests/AppTests.swift +++ b/Tests/AppTests/AppTests.swift @@ -12,11 +12,11 @@ final class AppTests: XCTestCase { func testEncodingContent() { let input = Message.Content(time: 1234567890, id: 23456789) - let data = input.encoded + let data = input.bytes let output = Message.Content(decodeFrom: data) XCTAssertEqual(input, output) - let data2 = Data([42, 42]) + data - let output2 = Message.Content(decodeFrom: Data(data2[2...])) + let data2 = [42, 42] + data + let output2 = Message.Content(decodeFrom: data2[2...]) XCTAssertEqual(input, output2) } @@ -42,4 +42,12 @@ final class AppTests: XCTestCase { XCTAssertEqual(decoded!, input) XCTAssertEqual(content, input.content) } + + func testMessageTransmission() throws { + let app = Application(.testing) + defer { app.shutdown() } + try configure(app) + + // How to open a socket via request? + } }