import Foundation /** The message content without authentication. */ struct Message: Equatable, Hashable { /// The type of message being sent. let messageType: MessageType /** * The random nonce created by the remote * * This nonce is a random number created by the remote, different for each unlock request. * It is set for all message types. */ let clientChallenge: UInt32 /** * A random number to sign by the remote * * This nonce is set by the server after receiving an initial message. * It is set for the message types `challenge`, `request`, and `response`. */ let serverChallenge: UInt32 /** * The response status for the previous message. * * It is set only for messages from the server, e.g. the `challenge` and `response` message types. * Must be set to `MessageAccepted` for other messages. */ let result: MessageResult init(messageType: MessageType, clientChallenge: UInt32, serverChallenge: UInt32, result: MessageResult) { self.messageType = messageType self.clientChallenge = clientChallenge self.serverChallenge = serverChallenge self.result = result } /** Decode message content from data. The data consists of two `UInt32` encoded in little endian format - Warning: The sequence must contain at least 8 bytes, or the function will crash. - Parameter data: The sequence containing the bytes. */ init(decodeFrom data: Data) throws { guard data.count == Message.size else { print("Invalid message size \(data.count)") throw MessageResult.invalidMessageSizeFromDevice } guard let messageType = MessageType(rawValue: data.first!) else { print("Invalid message type \(data.first!)") throw MessageResult.invalidMessageTypeFromDevice } self.messageType = messageType let messageTypeEndIndex = data.startIndex+1 let clientChallengeEndIndex = messageTypeEndIndex + UInt32.byteSize let clientChallengeData = Array(data[messageTypeEndIndex.. Message { .init( messageType: .initial, clientChallenge: .random(), serverChallenge: 0, result: .messageAccepted) } func with(result: MessageResult) -> Message { .init( messageType: messageType.responseType, clientChallenge: clientChallenge, serverChallenge: serverChallenge, result: result) } /** Create the message to respond to this challenge */ func requestMessage() -> Message { .init( messageType: .request, clientChallenge: clientChallenge, serverChallenge: serverChallenge, result: .messageAccepted) } } extension Message: CustomStringConvertible { var description: String { "\(messageType)(\(clientChallenge)->\(serverChallenge), \(result))" } }