Move to Bytes from Data, simplify result cases

This commit is contained in:
Christoph Hagen 2022-04-08 13:33:09 +02:00
parent f9039c7b3a
commit 1c6c29d585
5 changed files with 33 additions and 41 deletions

View File

@ -24,14 +24,10 @@ struct DeviceResponse {
.init(event: .noBodyData) .init(event: .noBodyData)
} }
static var corruptkeyData: DeviceResponse {
.init(event: .corruptkeyData)
}
static var operationInProgress: DeviceResponse { static var operationInProgress: DeviceResponse {
.init(event: .operationInProgress) .init(event: .operationInProgress)
} }
/// The response to a key from the server /// The response to a key from the server
let event: MessageResult let event: MessageResult
@ -39,7 +35,7 @@ struct DeviceResponse {
let response: Message? let response: Message?
init?(_ buffer: ByteBuffer) { 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") print("No bytes received from device")
return nil return nil
} }

View File

@ -1,7 +1,6 @@
import Foundation import Foundation
import CryptoKit import CryptoKit
import NIOCore import NIOCore
import Vapor
struct Message: Equatable, Hashable { struct Message: Equatable, Hashable {
@ -20,9 +19,9 @@ struct Message: Equatable, Hashable {
self.id = id self.id = id
} }
init(decodeFrom data: Data) { init<T: Sequence>(decodeFrom data: T) where T.Element == UInt8 {
self.time = UInt32(data: data[data.startIndex..<data.startIndex+4]) self.time = UInt32(data: data.prefix(4))
self.id = UInt32(data: data[data.startIndex+4..<data.startIndex+8]) self.id = UInt32(data: data.dropFirst(4))
} }
static var length: Int { static var length: Int {
@ -59,16 +58,16 @@ struct Message: Equatable, Hashable {
} }
init?(decodeFrom buffer: ByteBuffer) { 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 return nil
} }
self.init(decodeFrom: data) self.init(decodeFrom: data)
} }
private init(decodeFrom data: Data) { private init<T: Sequence>(decodeFrom data: T) where T.Element == UInt8 {
let count = SHA256Digest.byteCount let count = SHA256Digest.byteCount
self.mac = data[data.startIndex..<data.startIndex+count] self.mac = Data(data.prefix(count))
self.content = .init(decodeFrom: data.advanced(by: count)) self.content = .init(decodeFrom: Array(data.dropFirst(count)))
} }
func isValid(using key: SymmetricKey) -> Bool { func isValid(using key: SymmetricKey) -> Bool {
@ -86,13 +85,12 @@ struct Message: Equatable, Hashable {
extension UInt32 { extension UInt32 {
init(data: Data) { init<T: Sequence>(data: T) where T.Element == UInt8 {
self = data self = data
.reversed() .reversed()
.enumerated() .enumerated()
.map { UInt32($0.element) << ($0.offset * 8) } .map { UInt32($0.element) << ($0.offset * 8) }
.reduce(0, +) .reduce(0, +)
} }
var encoded: Data { var encoded: Data {

View File

@ -4,41 +4,35 @@ import Foundation
A result from sending a key to the device. A result from sending a key to the device.
*/ */
enum MessageResult: UInt8 { enum MessageResult: UInt8 {
/// Text content was received, although binary data was expected /// Text content was received, although binary data was expected
case textReceived = 1 case textReceived = 1
/// A socket event on the device was unexpected (not binary data) /// A socket event on the device was unexpected (not binary data)
case unexpectedSocketEvent = 2 case unexpectedSocketEvent = 2
/// The size of the payload (i.e. message) was invalid, or the data could not be read /// The size of the payload (i.e. message) was invalid, or the data could not be read
case invalidMessageData = 3 case invalidMessageData = 3
/// The transmitted message could not be authenticated using the key /// The transmitted message could not be authenticated using the key
case messageAuthenticationFailed = 4 case messageAuthenticationFailed = 4
/// The message time was not within the acceptable bounds /// The message time was not within the acceptable bounds
case messageTimeMismatch = 5 case messageTimeMismatch = 5
/// A later key has been used, invalidating this key (to prevent replay attacks after blocked communication) /// A later key has been used, invalidating this key (to prevent replay attacks after blocked communication)
case messageCounterInvalid = 6 case messageCounterInvalid = 6
/// The key was accepted by the device, and the door will be opened /// The key was accepted by the device, and the door will be opened
case messageAccepted = 7 case messageAccepted = 7
/// The device produced an unknown error
case unknownDeviceError = 9
/// The request did not contain body data with the key /// The request did not contain body data with the key
case noBodyData = 10 case noBodyData = 10
/// The body data could not be read
case corruptkeyData = 11
/// The device is not connected /// The device is not connected
case deviceNotConnected = 12 case deviceNotConnected = 12
/// The device did not respond within the timeout /// The device did not respond within the timeout
case deviceTimedOut = 13 case deviceTimedOut = 13
@ -47,7 +41,7 @@ enum MessageResult: UInt8 {
} }
extension MessageResult: CustomStringConvertible { extension MessageResult: CustomStringConvertible {
var description: String { var description: String {
switch self { switch self {
case .textReceived: case .textReceived:
@ -64,12 +58,8 @@ extension MessageResult: CustomStringConvertible {
return "Message counter invalid" return "Message counter invalid"
case .messageAccepted: case .messageAccepted:
return "Message accepted" return "Message accepted"
case .unknownDeviceError:
return "The device experienced an unknown error"
case .noBodyData: case .noBodyData:
return "No body data included in the request" return "No body data included in the request"
case .corruptkeyData:
return "Key data corrupted"
case .deviceNotConnected: case .deviceNotConnected:
return "Device not connected" return "Device not connected"
case .deviceTimedOut: case .deviceTimedOut:

View File

@ -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`, 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`. 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 - 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 app.webSocket(PublicAPI.socket.path) { req, socket in
socket.onBinary { _, data in socket.onBinary { _, data in

View File

@ -12,11 +12,11 @@ final class AppTests: XCTestCase {
func testEncodingContent() { func testEncodingContent() {
let input = Message.Content(time: 1234567890, id: 23456789) let input = Message.Content(time: 1234567890, id: 23456789)
let data = input.encoded let data = input.bytes
let output = Message.Content(decodeFrom: data) let output = Message.Content(decodeFrom: data)
XCTAssertEqual(input, output) XCTAssertEqual(input, output)
let data2 = Data([42, 42]) + data let data2 = [42, 42] + data
let output2 = Message.Content(decodeFrom: Data(data2[2...])) let output2 = Message.Content(decodeFrom: data2[2...])
XCTAssertEqual(input, output2) XCTAssertEqual(input, output2)
} }
@ -42,4 +42,12 @@ final class AppTests: XCTestCase {
XCTAssertEqual(decoded!, input) XCTAssertEqual(decoded!, input)
XCTAssertEqual(content, input.content) XCTAssertEqual(content, input.content)
} }
func testMessageTransmission() throws {
let app = Application(.testing)
defer { app.shutdown() }
try configure(app)
// How to open a socket via request?
}
} }