Proper serialization and cleanup
This commit is contained in:
parent
360833ae5f
commit
921b9237f7
@ -20,8 +20,8 @@
|
||||
E24EE77427FF95920011CFD2 /* DeviceResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = E24EE77327FF95920011CFD2 /* DeviceResponse.swift */; };
|
||||
E24EE77727FF95C00011CFD2 /* NIOCore in Frameworks */ = {isa = PBXBuildFile; productRef = E24EE77627FF95C00011CFD2 /* NIOCore */; };
|
||||
E24EE77927FF95E00011CFD2 /* Message.swift in Sources */ = {isa = PBXBuildFile; fileRef = E24EE77827FF95E00011CFD2 /* Message.swift */; };
|
||||
E24EE77B280058240011CFD2 /* Message+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E24EE77A280058240011CFD2 /* Message+Extensions.swift */; };
|
||||
E2C5C1DB2806FE8900769EF6 /* RouteAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2C5C1DA2806FE8900769EF6 /* RouteAPI.swift */; };
|
||||
E2C5C1DD281B3AC400769EF6 /* UInt32+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2C5C1DC281B3AC400769EF6 /* UInt32+Extensions.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
@ -38,8 +38,8 @@
|
||||
E24EE77127FDCCC00011CFD2 /* Data+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Data+Extensions.swift"; sourceTree = "<group>"; };
|
||||
E24EE77327FF95920011CFD2 /* DeviceResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceResponse.swift; sourceTree = "<group>"; };
|
||||
E24EE77827FF95E00011CFD2 /* Message.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Message.swift; sourceTree = "<group>"; };
|
||||
E24EE77A280058240011CFD2 /* Message+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Message+Extensions.swift"; sourceTree = "<group>"; };
|
||||
E2C5C1DA2806FE8900769EF6 /* RouteAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RouteAPI.swift; sourceTree = "<group>"; };
|
||||
E2C5C1DC281B3AC400769EF6 /* UInt32+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UInt32+Extensions.swift"; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
@ -102,7 +102,7 @@
|
||||
E24EE77827FF95E00011CFD2 /* Message.swift */,
|
||||
884A45CE27A5402D00D6E650 /* MessageResult.swift */,
|
||||
E2C5C1DA2806FE8900769EF6 /* RouteAPI.swift */,
|
||||
E24EE77A280058240011CFD2 /* Message+Extensions.swift */,
|
||||
E2C5C1DC281B3AC400769EF6 /* UInt32+Extensions.swift */,
|
||||
);
|
||||
path = API;
|
||||
sourceTree = "<group>";
|
||||
@ -186,8 +186,8 @@
|
||||
884A45CF27A5402D00D6E650 /* MessageResult.swift in Sources */,
|
||||
884A45B9279F48C100D6E650 /* ContentView.swift in Sources */,
|
||||
E2C5C1DB2806FE8900769EF6 /* RouteAPI.swift in Sources */,
|
||||
E2C5C1DD281B3AC400769EF6 /* UInt32+Extensions.swift in Sources */,
|
||||
884A45CD27A465F500D6E650 /* Client.swift in Sources */,
|
||||
E24EE77B280058240011CFD2 /* Message+Extensions.swift in Sources */,
|
||||
E24EE77227FDCCC00011CFD2 /* Data+Extensions.swift in Sources */,
|
||||
E24EE77427FF95920011CFD2 /* DeviceResponse.swift in Sources */,
|
||||
884A45CB27A464C000D6E650 /* SymmetricKey+Extensions.swift in Sources */,
|
||||
|
Binary file not shown.
@ -1,38 +0,0 @@
|
||||
import Foundation
|
||||
|
||||
#if canImport(CryptoKit)
|
||||
import CryptoKit
|
||||
#else
|
||||
import Crypto
|
||||
#endif
|
||||
|
||||
extension Message {
|
||||
|
||||
static var length: Int {
|
||||
SHA256.byteCount + Content.length
|
||||
}
|
||||
|
||||
init<T: Sequence>(decodeFrom data: T) where T.Element == UInt8 {
|
||||
let count = SHA256.byteCount
|
||||
self.mac = Data(data.prefix(count))
|
||||
self.content = .init(decodeFrom: Array(data.dropFirst(count)))
|
||||
}
|
||||
|
||||
func isValid(using key: SymmetricKey) -> Bool {
|
||||
HMAC<SHA256>.isValidAuthenticationCode(mac, authenticating: content.encoded, using: key)
|
||||
}
|
||||
}
|
||||
|
||||
extension Message.Content {
|
||||
|
||||
func authenticate(using key: SymmetricKey) -> Message {
|
||||
let mac = HMAC<SHA256>.authenticationCode(for: encoded, using: key)
|
||||
return .init(mac: Data(mac.map { $0 }), content: self)
|
||||
}
|
||||
|
||||
func authenticateAndSerialize(using key: SymmetricKey) -> Data {
|
||||
let encoded = self.encoded
|
||||
let mac = HMAC<SHA256>.authenticationCode(for: encoded, using: key)
|
||||
return Data(mac.map { $0 }) + encoded
|
||||
}
|
||||
}
|
@ -1,11 +1,36 @@
|
||||
import Foundation
|
||||
import NIOCore
|
||||
|
||||
#if canImport(CryptoKit)
|
||||
import CryptoKit
|
||||
#else
|
||||
import Crypto
|
||||
#endif
|
||||
|
||||
/**
|
||||
An authenticated message to or from the device.
|
||||
*/
|
||||
struct Message: Equatable, Hashable {
|
||||
|
||||
/// The message authentication code for the message (32 bytes)
|
||||
let mac: Data
|
||||
|
||||
/// The message content
|
||||
let content: Content
|
||||
|
||||
/**
|
||||
Create an authenticated message
|
||||
- Parameter mac: The message authentication code
|
||||
- Parameter content: The message content
|
||||
*/
|
||||
init(mac: Data, content: Content) {
|
||||
self.mac = mac
|
||||
self.content = content
|
||||
}
|
||||
}
|
||||
|
||||
extension Message {
|
||||
|
||||
/**
|
||||
The message content without authentication.
|
||||
*/
|
||||
@ -30,13 +55,13 @@ struct Message: Equatable, Hashable {
|
||||
/**
|
||||
Decode message content from data.
|
||||
|
||||
The data consists of two `UInt32` encoded in big endian format (MSB at index 0)
|
||||
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<T: Sequence>(decodeFrom data: T) where T.Element == UInt8 {
|
||||
self.time = UInt32(data: data.prefix(MemoryLayout<UInt32>.size))
|
||||
self.id = UInt32(data: data.dropFirst(MemoryLayout<UInt32>.size))
|
||||
self.time = UInt32(data: Data(data.prefix(MemoryLayout<UInt32>.size)))
|
||||
self.id = UInt32(data: Data(data.dropFirst(MemoryLayout<UInt32>.size)))
|
||||
}
|
||||
|
||||
/// The byte length of an encoded message content
|
||||
@ -48,27 +73,15 @@ struct Message: Equatable, Hashable {
|
||||
var encoded: Data {
|
||||
time.encoded + id.encoded
|
||||
}
|
||||
|
||||
/// The message content encoded to bytes
|
||||
var bytes: [UInt8] {
|
||||
time.bytes + id.bytes
|
||||
}
|
||||
}
|
||||
|
||||
/// The message authentication code for the message (32 bytes)
|
||||
let mac: Data
|
||||
}
|
||||
|
||||
/// The message content
|
||||
let content: Content
|
||||
extension Message {
|
||||
|
||||
/**
|
||||
Create an authenticated message
|
||||
- Parameter mac: The message authentication code
|
||||
- Parameter content: The message content
|
||||
*/
|
||||
init(mac: Data, content: Content) {
|
||||
self.mac = mac
|
||||
self.content = content
|
||||
/// The length of a message in bytes
|
||||
static var length: Int {
|
||||
SHA256.byteCount + Content.length
|
||||
}
|
||||
|
||||
/**
|
||||
@ -88,35 +101,47 @@ struct Message: Equatable, Hashable {
|
||||
mac + content.encoded
|
||||
}
|
||||
|
||||
/// The message encoded to bytes
|
||||
var bytes: [UInt8] {
|
||||
Array(mac) + content.bytes
|
||||
/**
|
||||
Create a message from received bytes.
|
||||
- Parameter data: The sequence of bytes
|
||||
- Note: The sequence must contain at least `Message.length` bytes, or the function will crash.
|
||||
*/
|
||||
init<T: Sequence>(decodeFrom data: T) where T.Element == UInt8 {
|
||||
let count = SHA256.byteCount
|
||||
self.mac = Data(data.prefix(count))
|
||||
self.content = .init(decodeFrom: Array(data.dropFirst(count)))
|
||||
}
|
||||
}
|
||||
|
||||
extension UInt32 {
|
||||
|
||||
/**
|
||||
Create a value from a big-endian data representation (MSB first)
|
||||
- Note: The data must contain exactly four bytes.
|
||||
Check if the message contains a valid authentication code
|
||||
- Parameter key: The key used to sign the message.
|
||||
- Returns: `true`, if the message is valid.
|
||||
*/
|
||||
init<T: Sequence>(data: T) where T.Element == UInt8 {
|
||||
self = data
|
||||
.reversed()
|
||||
.enumerated()
|
||||
.map { UInt32($0.element) << ($0.offset * 8) }
|
||||
.reduce(0, +)
|
||||
func isValid(using key: SymmetricKey) -> Bool {
|
||||
HMAC<SHA256>.isValidAuthenticationCode(mac, authenticating: content.encoded, using: key)
|
||||
}
|
||||
}
|
||||
|
||||
/// The value encoded to a big-endian representation
|
||||
var encoded: Data {
|
||||
.init(bytes)
|
||||
extension Message.Content {
|
||||
|
||||
/**
|
||||
Calculate an authentication code for the message content.
|
||||
- Parameter key: The key to use to sign the content.
|
||||
- Returns: The new message signed with the key.
|
||||
*/
|
||||
func authenticate(using key: SymmetricKey) -> Message {
|
||||
let mac = HMAC<SHA256>.authenticationCode(for: encoded, using: key)
|
||||
return .init(mac: Data(mac.map { $0 }), content: self)
|
||||
}
|
||||
|
||||
/// The value encoded to a big-endian byte array
|
||||
var bytes: [UInt8] {
|
||||
(0..<4).reversed().map {
|
||||
UInt8((self >> ($0*8)) & 0xFF)
|
||||
}
|
||||
/**
|
||||
Calculate an authentication code for the message content and convert everything to data.
|
||||
- Parameter key: The key to use to sign the content.
|
||||
- Returns: The new message signed with the key, serialized to bytes.
|
||||
*/
|
||||
func authenticateAndSerialize(using key: SymmetricKey) -> Data {
|
||||
let encoded = self.encoded
|
||||
let mac = HMAC<SHA256>.authenticationCode(for: encoded, using: key)
|
||||
return Data(mac.map { $0 }) + encoded
|
||||
}
|
||||
}
|
||||
|
18
Sesame/API/UInt32+Extensions.swift
Normal file
18
Sesame/API/UInt32+Extensions.swift
Normal file
@ -0,0 +1,18 @@
|
||||
import Foundation
|
||||
|
||||
extension UInt32 {
|
||||
|
||||
/**
|
||||
Create a value from a little-endian data representation (MSB first)
|
||||
- Note: The data must contain exactly four bytes.
|
||||
*/
|
||||
init(data: Data) {
|
||||
let value = data.convert(into: UInt32.zero)
|
||||
self = CFSwapInt32LittleToHost(value)
|
||||
}
|
||||
|
||||
/// The value encoded to a little-endian representation
|
||||
var encoded: Data {
|
||||
Data(from: CFSwapInt32HostToLittle(self))
|
||||
}
|
||||
}
|
@ -40,3 +40,20 @@ extension Data {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Data {
|
||||
|
||||
|
||||
func convert<T>(into value: T) -> T {
|
||||
withUnsafeBytes {
|
||||
$0.baseAddress!.load(as: T.self)
|
||||
}
|
||||
}
|
||||
|
||||
init<T>(from value: T) {
|
||||
var target = value
|
||||
self = Swift.withUnsafeBytes(of: &target) {
|
||||
Data($0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user