V0.6.0 Arroyo Toad

Added the capability to sign and verify P521 Signature
This commit is contained in:
Christian Risi 2024-12-06 11:31:07 +00:00
parent 6859e27368
commit ad4fd555f1
25 changed files with 764 additions and 537 deletions

36
LICENSE
View File

@ -1,18 +1,18 @@
LICENSE
=======
Permission to use, copy, modify, and distribute this software and its
documentation for any purpose and without fee is hereby granted, provided
that the original copyright notices appear in all copies and that both
copyright notice and this permission notice appear in supporting
documentation, and that the name of the author not be used in advertising
or publicity pertaining to distribution of the software without specific
prior written permission.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
LICENSE
=======
Permission to use, copy, modify, and distribute this software and its
documentation for any purpose and without fee is hereby granted, provided
that the original copyright notices appear in all copies and that both
copyright notice and this permission notice appear in supporting
documentation, and that the name of the author not be used in advertising
or publicity pertaining to distribution of the software without specific
prior written permission.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

24
Package.resolved Normal file
View File

@ -0,0 +1,24 @@
{
"originHash" : "dc0be3b6ca36aebf9cc5cf891bb7e7064d6b816c6ad89f60f5647f097455aa46",
"pins" : [
{
"identity" : "swift-asn1",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-asn1.git",
"state" : {
"revision" : "7faebca1ea4f9aaf0cda1cef7c43aecd2311ddf6",
"version" : "1.3.0"
}
},
{
"identity" : "swift-crypto",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-crypto.git",
"state" : {
"branch" : "main",
"revision" : "dc4c2c14e7ff95ee3aa8d3c2a217a248f51d3688"
}
}
],
"version" : 3
}

View File

@ -1,24 +1,32 @@
// swift-tools-version: 6.0
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "MessageUtils",
products: [
// Products define the executables and libraries a package produces, making them visible to other packages.
.library(
name: "MessageUtils",
targets: ["MessageUtils"]),
],
targets: [
// Targets are the basic building blocks of a package, defining a module or a test suite.
// Targets can depend on other targets in this package and products from dependencies.
.target(
name: "MessageUtils"),
.testTarget(
name: "MessageUtilsTests",
dependencies: ["MessageUtils"]
),
]
)
// swift-tools-version: 6.0
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "MessageUtils",
products: [
// Products define the executables and libraries a package produces, making them visible to other packages.
.library(
name: "MessageUtils",
targets: ["MessageUtils"]),
],
dependencies: [
.package(url: "https://github.com/apple/swift-crypto.git", branch: "main"),
],
targets: [
// Targets are the basic building blocks of a package, defining a module or a test suite.
// Targets can depend on other targets in this package and products from dependencies.
.target(
name: "MessageUtils",
dependencies: [
.product(name: "Crypto", package: "swift-crypto"),
]
),
.testTarget(
name: "MessageUtilsTests",
dependencies: ["MessageUtils"]
),
]
)

0
Private/.gitkeep Normal file
View File

16
Private/cert.pem Normal file
View File

@ -0,0 +1,16 @@
-----BEGIN CERTIFICATE-----
MIICeTCCAdqgAwIBAgIUeKyiiDJdVGH3ParIry5vn/YGnaowCgYIKoZIzj0EAwIw
TjELMAkGA1UEBhMCSVQxDTALBgNVBAgMBEJhcmkxDTALBgNVBAcMBEJhcmkxITAf
BgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yNDEyMDIxNjM0NDVa
Fw0yNTAxMDExNjM0NDVaME4xCzAJBgNVBAYTAklUMQ0wCwYDVQQIDARCYXJpMQ0w
CwYDVQQHDARCYXJpMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQw
gZswEAYHKoZIzj0CAQYFK4EEACMDgYYABAAAfWnGEUMElY/XIWUTPvX65HX3N5Ik
JKPdVFzDRtaTHRJKnEEvU7Z5iLAT9NpbVfCabvQXKo7LD5sjoJ1ZpSVcogDgCFCo
pmVin2ZLs5lyMtaetpVDH8m+AIlRQkkuGmkasM+OV62kzSoHl/CL4eNz1xXwqsPt
oBgvPiRFxNIE/0dz96NTMFEwHQYDVR0OBBYEFKW5mYrSXJn68diXLDjhbiEGAxJu
MB8GA1UdIwQYMBaAFKW5mYrSXJn68diXLDjhbiEGAxJuMA8GA1UdEwEB/wQFMAMB
Af8wCgYIKoZIzj0EAwIDgYwAMIGIAkIAhVgtxgnZd6KeefLjZ6Mazgr5xLDcAHyI
NsKtTw3YzT/Pztnk2ccV+NyDZyoTG72lHoPMTiB5mRSUTqORg59XQTkCQgDqHRoN
tRQlPWY3abohilRRdvYZrsoPR8FzB/M4KxT0nk10jc1wtosQ7l/XZGcKe8/k+iVs
HC5CsESzsvnp+Qslyw==
-----END CERTIFICATE-----

7
Private/privateKey.pem Normal file
View File

@ -0,0 +1,7 @@
-----BEGIN EC PRIVATE KEY-----
MIHcAgEBBEIBAQAy+3ElWUTttb9xxVDshJlGt/clGdhPkp76aJ3LJySugsnC8RRO
UracnWQi2A+XnEI1ZskzYAFUfh7G5o5ViDygBwYFK4EEACOhgYkDgYYABAAAfWnG
EUMElY/XIWUTPvX65HX3N5IkJKPdVFzDRtaTHRJKnEEvU7Z5iLAT9NpbVfCabvQX
Ko7LD5sjoJ1ZpSVcogDgCFCopmVin2ZLs5lyMtaetpVDH8m+AIlRQkkuGmkasM+O
V62kzSoHl/CL4eNz1xXwqsPtoBgvPiRFxNIE/0dz9w==
-----END EC PRIVATE KEY-----

View File

@ -0,0 +1,8 @@
-----BEGIN PRIVATE KEY-----
MIHuAgEAMBAGByqGSM49AgEGBSuBBAAjBIHWMIHTAgEBBEIBAQAy+3ElWUTttb9x
xVDshJlGt/clGdhPkp76aJ3LJySugsnC8RROUracnWQi2A+XnEI1ZskzYAFUfh7G
5o5ViDyhgYkDgYYABAAAfWnGEUMElY/XIWUTPvX65HX3N5IkJKPdVFzDRtaTHRJK
nEEvU7Z5iLAT9NpbVfCabvQXKo7LD5sjoJ1ZpSVcogDgCFCopmVin2ZLs5lyMtae
tpVDH8m+AIlRQkkuGmkasM+OV62kzSoHl/CL4eNz1xXwqsPtoBgvPiRFxNIE/0dz
9w==
-----END PRIVATE KEY-----

Binary file not shown.

View File

@ -1,2 +1,2 @@
# Swift-MessageUtils
# Swift-MessageUtils

View File

@ -1,4 +1,4 @@
public enum DeviceType: UInt8 {
case EDGE_SENSOR = 0
case SCANNER_SENSOR = 1
public enum DeviceType: UInt8 {
case EDGE_SENSOR = 0
case SCANNER_SENSOR = 1
}

View File

@ -1,8 +1,8 @@
public enum MessageType: UInt8 {
case KEEPALIVE = 0
case DATA = 1
case INFO = 2
case WARNING = 50
case ERROR = 100
case CRITICAL = 255
public enum MessageType: UInt8 {
case KEEPALIVE = 0
case DATA = 1
case INFO = 2
case WARNING = 50
case ERROR = 100
case CRITICAL = 255
}

View File

@ -1,3 +1,3 @@
public enum SignType: UInt32 {
case P521 = 10
public enum SignType: UInt32 {
case P521 = 10
}

View File

@ -1,13 +1,13 @@
public enum SerializationError : Error {
case SIGNATURE_NOT_SUPPORTED
}
public enum CommonError : Error {
case SIGNATURE_NOT_SUPPORTED
}
public enum DeserializationError: Error {
case UNMATCHING_SIGNATURE_TYPE
public enum SerializationError : Error {
case SIGNATURE_NOT_SUPPORTED
}
public enum CommonError : Error {
case SIGNATURE_NOT_SUPPORTED
}
public enum DeserializationError: Error {
case UNMATCHING_SIGNATURE_TYPE
}

View File

@ -1,203 +1,213 @@
// The Swift Programming Language
// https://docs.swift.org/swift-book
import Foundation
public func serializeV1(msg: Message) throws -> Data {
let MESSAGE_CAPACITY: Int = try countBytes(msg: msg)
var serializedData: Data = Data(count: MESSAGE_CAPACITY)
// Serialize Header
// UGLY: extrapolate in functions
serializedData[0] = msg.version
serializedData[1] = msg.messageType.rawValue
serializedData[2] = msg.devType.rawValue
serializedData[3] = msg.RESERVED
serializedData[4...7] = msg.signType.rawValue.data
// First 8 bytes
serializedData[8...15] = msg.timestamp.data
// 8 Bytes
serializedData[16...31] = msg.devID.data
// 16 bytes
serializedData[32...39] = msg.location.x.data
// 8 Bytes
serializedData[40...47] = msg.location.y.data
// 8 Bytes
serializedData[48...55] = msg.location.z.data
// 8 Bytes
var index = 56
for field in msg.fields {
serializedData[index...(index + 3)] = UInt32(field.key.count).data
serializedData[(index + 4)...(index + 7)] = UInt32(field.key.count).data
index += 8
serializedData[index..<(index + field.key.count)] = Data(field.key)
index += field.key.count
serializedData[index..<(index + field.value.count)] = Data(field.value)
index += field.value.count
}
let paddingBytes = (8 - (index % 8)) % 8
serializedData[index..<(index + paddingBytes)] = Data(count: paddingBytes)
index += paddingBytes
// Add STOP FIELDS
serializedData[index..<(index + 8)] = UInt64(0).data
print(UInt64(0).data.base64EncodedString())
index += 8
serializedData[index..<serializedData.count] = Data(msg.signature)
if serializedData.count % 8 != 0 {
print(serializedData.count % 8)
print(MESSAGE_CAPACITY % 8)
}
return serializedData
}
public func derializeV1(serializedData: Data) throws -> Message {
// Serialize Header
// UGLY: extrapolate in functions
let version: UInt8 = serializedData[0]
let messageType: MessageType = MessageType(rawValue: serializedData[1])!
let devType: DeviceType = DeviceType(rawValue: serializedData[2])!
let RESERVED: UInt8 = serializedData[3]
let signType: SignType = SignType(rawValue: serializedData[4...7].uint32)!
// First 8 bytes
let signBytes = try signatureBytes(signature: signType)
let timestamp = serializedData[8...15].timestamp
// 8 Bytes
let devID = serializedData[16...31].uint128
// 16 bytes
let locationX = serializedData[32...39].uint64
// 8 Bytes
let locationY = serializedData[40...47].uint64
// 8 Bytes
let locationZ = serializedData[48...55].uint64
// 8 Bytes
var index = 56
// Deserializing Fields
var MORE_FIELDS = true
var fields: [Field] = []
while MORE_FIELDS {
let nextChunk = serializedData[index..<(index + 8)]
let fieldKeyCount = serializedData[index...(index + 4)].uint32
let fieldValueCount = serializedData[(index + 4)..<(index + 8)].uint32
index += 8
let a = nextChunk.uint64
if nextChunk.uint64 == 0 {
MORE_FIELDS = false
continue
}
let key = serializedData[index..<(index + Int(fieldKeyCount))].map { value in
return value
}
index += Int(fieldKeyCount)
let value = serializedData[index..<(index + Int(fieldValueCount))].map { value in
return value
}
index += Int(fieldValueCount)
fields.append(Field(key: key, value: value))
}
let paddingBytes = (8 - (index % 8)) % 8
// Skip padding bytes
index += paddingBytes
let signature = serializedData[index..<serializedData.count].map { value in
return value
}
// Sanity check signature with signatureType
if signature.count != signBytes {
throw DeserializationError.UNMATCHING_SIGNATURE_TYPE
}
return Message(
version: version,
messageType: messageType,
devType: devType,
RESERVED: RESERVED,
signType: signType,
timestamp: timestamp,
devID: devID,
location: Location(x: locationX, y: locationY, z: locationZ),
fields: fields,
signature: signature
)
}
public func countBytes(msg: Message) throws -> Int {
///
/// Author: Christian Risi
///
/// This is computed as all the bits for the fixed fields
///
/// In this case we have:
/// - 4 B --> Generic Info
/// - 4 B --> Timestamp
/// - 8 B --> Device ID
/// - 12 B --> Device Location
/// - 8 B --> FIELDS STOP
let INITIAL_CAPACITY_BYTES: Int = 36
let FIELD_HEADER_CAPACITY_BYTES: Int = 4
var fieldReveservedCapacity: Int = 0
for field in msg.fields {
fieldReveservedCapacity += FIELD_HEADER_CAPACITY_BYTES + field.key.count + field.value.count
}
// UGLY: We are assuming P521 signature
let SIGNATURE_CAPACITY_BYTES: Int = try signatureBytes(signature: msg.signType)
return (INITIAL_CAPACITY_BYTES + fieldReveservedCapacity + SIGNATURE_CAPACITY_BYTES)
}
public func signatureBytes(signature: SignType) throws -> Int {
switch signature {
case .P521:
return 132
default:
throw CommonError.SIGNATURE_NOT_SUPPORTED
}
}
// The Swift Programming Language
// https://docs.swift.org/swift-book
import Foundation
import Crypto
public func serializeV1(msg: MessageP) -> Data {
let MESSAGE_CAPACITY: Int = countBytes(msg: msg)
var serializedData: Data = Data(count: MESSAGE_CAPACITY)
// Serialize Header
// UGLY: extrapolate in functions
serializedData[0] = msg.version
serializedData[1] = msg.messageType.rawValue
serializedData[2] = msg.devType.rawValue
serializedData[3] = msg.RESERVED
serializedData[4...7] = msg.signType.rawValue.data
// First 8 bytes
serializedData[8...15] = msg.timestamp.data
// 8 Bytes
serializedData[16...31] = msg.devID.data
// 16 bytes
serializedData[32...39] = msg.location.x.data
// 8 Bytes
serializedData[40...47] = msg.location.y.data
// 8 Bytes
serializedData[48...55] = msg.location.z.data
// 8 Bytes
var index = 56
for field in msg.fields {
serializedData[index..<(index + 4)] = UInt32(field.key.count).data
serializedData[(index + 4)..<(index + 8)] = UInt32(field.value.count).data
index += 8
serializedData[index..<(index + field.key.count)] = Data(field.key)
index += field.key.count
serializedData[index..<(index + field.value.count)] = Data(field.value)
index += field.value.count
}
let paddingBytes = (8 - (index % 8)) % 8
serializedData[index..<(index + paddingBytes)] = Data(count: paddingBytes)
index += paddingBytes
// Add STOP FIELDS
serializedData[index..<(index + 8)] = UInt64(0).data
return serializedData
}
public func signMessage(msgData: Data, signType: SignType, key: P521.Signing.PrivateKey) throws -> [UInt8] {
let signatureBytes = try signatureBytes(signature: signType)
// UGLY We are hypothesisying that signType is P521
let signature = try signP521(object: msgData, key: key).map { value in
return value
}
return signature
}
public func verifyMessageSignature(message: SignedMessage, key: P521.Signing.PublicKey) throws -> Bool {
// UGLY Assuming P521 Signature
let msgData = serializeV1(msg: message)
return try verifySignatureP521(signature: Data(message.signature), object: msgData, key: key)
}
public func deserializeV1(serializedData: Data) throws -> SignedMessage {
// Serialize Header
// UGLY: extrapolate in functions
let version: UInt8 = serializedData[0]
let messageType: MessageType = MessageType(rawValue: serializedData[1])!
let devType: DeviceType = DeviceType(rawValue: serializedData[2])!
let RESERVED: UInt8 = serializedData[3]
let signType: SignType = SignType(rawValue: serializedData[4...7].uint32)!
// First 8 bytes
let signBytes = try signatureBytes(signature: signType)
let timestamp = serializedData[8...15].timestamp
// 8 Bytes
let devID = serializedData[16...31].uint128
// 16 bytes
let locationX = serializedData[32...39].uint64
// 8 Bytes
let locationY = serializedData[40...47].uint64
// 8 Bytes
let locationZ = serializedData[48...55].uint64
// 8 Bytes
var index = 56
// Deserializing Fields
var MORE_FIELDS = true
var fields: [Field] = []
while MORE_FIELDS {
let nextChunk = serializedData[index..<(index + 8)]
let a = nextChunk.map { value in
return value
}
if nextChunk.uint64 == 0 {
MORE_FIELDS = false
continue
}
let fieldKeyCount = serializedData[index..<(index + 4)].uint32
let fieldValueCount = serializedData[(index + 4)..<(index + 8)].uint32
index += 8
let key = serializedData[index..<(index + Int(fieldKeyCount))].map { value in
return value
}
index += Int(fieldKeyCount)
let value = serializedData[index..<(index + Int(fieldValueCount))].map { value in
return value
}
index += Int(fieldValueCount)
fields.append(Field(key: key, value: value))
}
let paddingBytes = (8 - (index % 8)) % 8
// Skip padding bytes
index += paddingBytes + 8
let signature = serializedData[index..<serializedData.count].map { value in
return value
}
// Sanity check signature with signatureType
if signature.count != signBytes {
throw DeserializationError.UNMATCHING_SIGNATURE_TYPE
}
return SignedMessage(
version: version,
messageType: messageType,
devType: devType,
RESERVED: RESERVED,
signType: signType,
timestamp: timestamp,
devID: devID,
location: Location(x: locationX, y: locationY, z: locationZ),
fields: fields,
signature: signature
)
}
public func countBytes(msg: MessageP) -> Int {
///
/// Author: Christian Risi
///
/// This is computed as all the bits for the fixed fields
///
let INITIAL_CAPACITY_BYTES: Int = 56
let FIELD_HEADER_CAPACITY_BYTES: Int = 8
var fieldReveservedCapacity: Int = 0
for field in msg.fields {
fieldReveservedCapacity += FIELD_HEADER_CAPACITY_BYTES + field.key.count + field.value.count
}
fieldReveservedCapacity += (8 - (fieldReveservedCapacity % 8)) % 8 + 8
return INITIAL_CAPACITY_BYTES + fieldReveservedCapacity
}
public func signatureBytes(signature: SignType) throws -> Int {
switch signature {
case .P521:
return 132
default:
throw CommonError.SIGNATURE_NOT_SUPPORTED
}
}

View File

@ -1,8 +1,8 @@
import Foundation
public struct Field {
public let key: [UInt8]
public let value: [UInt8]
}
import Foundation
public struct Field {
public let key: [UInt8]
public let value: [UInt8]
}

View File

@ -1,7 +1,7 @@
public struct Location {
public let x: UInt64
public let y: UInt64
public let z: UInt64
public struct Location {
public let x: UInt64
public let y: UInt64
public let z: UInt64
}

View File

@ -1,37 +1,71 @@
import Foundation
public struct Message{
public let version: UInt8
public let messageType: MessageType
public let devType: DeviceType
public let RESERVED: UInt8
public let signType: SignType
public let timestamp : Date
public let devID : UInt128
public let location: Location
public let fields: [Field]
public let signature: [UInt8]
public func toString() -> String{
var description = ""
description += "MESSAGE -------\n"
description += "V: \t\(self.version)\n"
description += "Message Type: \t\(self.messageType.rawValue)\n"
description += "Device Type: \t\(self.devType.rawValue)\n"
description += "RESERVED: \t\(self.RESERVED)\n"
description += "Signature Type: \t\(self.signType.rawValue)\n"
description += "Timestamp: \t\(self.timestamp)\n"
description += "Device ID: \t\(self.devID)\n"
description += "Location: \tX: \(self.location.x)\tY: \(self.location.y)\tZ: \(self.location.z)\n"
description += "Fields: \n"
for field in self.fields {
description += "\t\(String(data: Data(field.key), encoding: .ascii) ?? "UNABLE TO DECODE"): \(String(data: Data(field.value), encoding: .ascii) ?? "UNABLE TO DECODE")"
}
description += "Signature: \t\(self.signature)\n"
return description
}
import Foundation
public struct Message : MessageP{
public let version: UInt8
public let messageType: MessageType
public let devType: DeviceType
public let RESERVED: UInt8
public let signType: SignType
public let timestamp : Date
public let devID : UInt128
public let location: Location
public let fields: [Field]
public func toString() -> String{
var description = ""
description += "MESSAGE -------\n"
description += "V: \t\(self.version)\n"
description += "Message Type: \t\(self.messageType.rawValue)\n"
description += "Device Type: \t\(self.devType.rawValue)\n"
description += "RESERVED: \t\(self.RESERVED)\n"
description += "Signature Type: \t\(self.signType.rawValue)\n"
description += "Timestamp: \t\(self.timestamp)\n"
description += "Device ID: \t\(self.devID)\n"
description += "Location: \tX: \(self.location.x)\tY: \(self.location.y)\tZ: \(self.location.z)\n"
description += "Fields: \n"
for field in self.fields {
description += "\t\(String(data: Data(field.key), encoding: .ascii) ?? "UNABLE TO DECODE"): \(String(data: Data(field.value), encoding: .ascii) ?? "UNABLE TO DECODE")\n"
}
return description
}
}
public struct SignedMessage : MessageP {
public let version: UInt8
public let messageType: MessageType
public let devType: DeviceType
public let RESERVED: UInt8
public let signType: SignType
public let timestamp : Date
public let devID : UInt128
public let location: Location
public let fields: [Field]
public let signature: [UInt8]
public func toString() -> String{
var description = ""
description += "MESSAGE -------\n"
description += "V: \t\(self.version)\n"
description += "Message Type: \t\(self.messageType.rawValue)\n"
description += "Device Type: \t\(self.devType.rawValue)\n"
description += "RESERVED: \t\(self.RESERVED)\n"
description += "Signature Type: \t\(self.signType.rawValue)\n"
description += "Timestamp: \t\(self.timestamp)\n"
description += "Device ID: \t\(self.devID)\n"
description += "Location: \tX: \(self.location.x)\tY: \(self.location.y)\tZ: \(self.location.z)\n"
description += "Fields: \n"
for field in self.fields {
description += "\t\(String(data: Data(field.key), encoding: .ascii) ?? "UNABLE TO DECODE"): \(String(data: Data(field.value), encoding: .ascii) ?? "UNABLE TO DECODE")\n"
}
description += "Signature: \t\(self.signature)\n"
return description
}
}

View File

@ -0,0 +1,58 @@
import Crypto // Equivalent to CryptoKit (more or less)
import Foundation
// ------------------
// --- Sign ---------
// ------------------
public func signP521(object: Data, key: P521.Signing.PrivateKey)throws -> Data {
return try key.signature<Data>(for: object).rawRepresentation
}
/*
public func sign<T>(object: T, key: P521.Signing.PrivateKey) throws -> String {
var _object = object
let data: Data = Data(bytes: &_object, count: MemoryLayout<T>.stride)
} */
// ------------------
// --- Decrypt ------
// ------------------
public func verifySignatureP521(signature: Data, object: Data, key: P521.Signing.PublicKey) throws -> Bool {
let ecdsa: P521.Signing.ECDSASignature
do {
ecdsa = try P521.Signing.ECDSASignature(rawRepresentation: signature)
} catch {
throw SecurityError.NotDecodableError
}
return key.isValidSignature<Data>(ecdsa, for: object)
}
// ------------------
// --- PEM 2 Key ----
// ------------------
public func pem2key(filePath: String) throws -> P521.Signing.PrivateKey {
let pemURL: URL = URL(filePath: filePath)
return try pem2key(filePem: pemURL)
}
public func pem2key(filePem: URL) throws -> P521.Signing.PrivateKey {
let fileString: String = try String(contentsOf: filePem, encoding: String.Encoding.utf8)
return try pem2key(pemString: fileString)
}
public func pem2key(pemString: String) throws -> P521.Signing.PrivateKey {
return try P521.Signing.PrivateKey(pemRepresentation: pemString)
}

View File

@ -0,0 +1,4 @@
enum SecurityError: Error {
case NotEncodableError
case NotDecodableError
}

View File

@ -1,5 +1,5 @@
import Foundation
public protocol DataCompatibleP {
var data : Data {get}
import Foundation
public protocol DataCompatibleP {
var data : Data {get}
}

View File

@ -1,32 +1,32 @@
import Foundation
public extension Data {
var uint8 : UInt8 {
return self.withUnsafeBytes{ $0.bindMemory(to: UInt8.self) }[0]
}
var uint16: UInt16 {
return self.withUnsafeBytes{ $0.bindMemory(to: UInt16.self) }[0]
}
var uint32: UInt32 {
return self.withUnsafeBytes{ $0.bindMemory(to: UInt32.self) }[0]
}
var uint64: UInt64 {
return self.withUnsafeBytes{ $0.bindMemory(to: UInt64.self) }[0]
}
var uint128: UInt128 {
return self.withUnsafeBytes{ $0.bindMemory(to: UInt128.self) }[0]
}
var uint: UInt {
return self.withUnsafeBytes{ $0.bindMemory(to: UInt.self) }[0]
}
var timestamp: Date {
return self.withUnsafeBytes{ $0.bindMemory(to: Date.self) }[0]
}
import Foundation
public extension Data {
var uint8 : UInt8 {
return self.withUnsafeBytes{ $0.bindMemory(to: UInt8.self) }[0]
}
var uint16: UInt16 {
return self.withUnsafeBytes{ $0.bindMemory(to: UInt16.self) }[0]
}
var uint32: UInt32 {
return self.withUnsafeBytes{ $0.bindMemory(to: UInt32.self) }[0]
}
var uint64: UInt64 {
return self.withUnsafeBytes{ $0.bindMemory(to: UInt64.self) }[0]
}
var uint128: UInt128 {
return self.withUnsafeBytes{ $0.bindMemory(to: UInt128.self) }[0]
}
var uint: UInt {
return self.withUnsafeBytes{ $0.bindMemory(to: UInt.self) }[0]
}
var timestamp: Date {
return self.withUnsafeBytes{ $0.bindMemory(to: Date.self) }[0]
}
}

View File

@ -1,52 +1,61 @@
# Message Structure
```
+-+ 64 - bits---+----------------+-------------+----------------+-----------------------+
|0| Version 7 | Message Type 8 | Dev Type 8 | RESERVED 16 | Sign Type 32 |
+-+-------------+----------------+-------------+----------------+-----------------------+
| Timestamp 64 |
+---------------------------------------------------------------------------------------+
| DevID 128 bits |
| |
+---------------------------------------------------------------------------------------+
| Location 192 bits |
| |
| |
+---------------------------------------------------------------------------------------+
\ /
| Fields -----------------------------------------------------------------------------|
/ \
+---------------------------------------------------------------------------------------+
| 0 Padding 64 - n |
+---------------------------------------------------------------------------------------+
| Signature up to-512 bits |
| |
| |
| |
| |
| |
| |
| |
+---------------------------------------------------------------------------------------+
```
# Fields
each field can be at most 8 * 2^32 bits of length:
- 34359738368 bits
- 4294967296 Bytes
## Key
**MUST** be a `String`
## Value
It's up to the Application to decide whether the value is
a string or another type of datum
```
+-- 64 - bits--------------------------+----------------------------------------+
| Key-Length 32 | Value-Length 32 |
+--------------------------------------+----------------------------------------+
\ Key /
|-----------------------------------------------------------------------------|
/ Value \
+-------------------------------------------------------------------------------+
# Message Structure
## Possible Improvements
As for now, we are not having a max abount of bytes the message may have,
so we are relying on the ability of the receiver to dynamically allocate a buffer for the message.
However, we could add a field to tell a PRIORI the length of the whole message.
Probably we would use between 8 B or 24 B to put this info
(This won't be a 2.0 version, as this is not a final document yet)
```
+-+ 64 - bits---+----------------+-------------+----------------+-----------------------+
|0| Version 7 | Message Type 8 | Dev Type 8 | RESERVED 16 | Sign Type 32 |
+-+-------------+----------------+-------------+----------------+-----------------------+
| Timestamp 64 |
+---------------------------------------------------------------------------------------+
| DevID 128 bits |
| |
+---------------------------------------------------------------------------------------+
| Location 192 bits |
| |
| |
+---------------------------------------------------------------------------------------+
\ /
| Fields -----------------------------------------------------------------------------|
/ \
+---------------------------------------------------------------------------------------+
| 0 Padding 64 - n |
+---------------------------------------------------------------------------------------+
| Signature up to-512 bits |
| |
| |
| |
| |
| |
| |
| |
+---------------------------------------------------------------------------------------+
```
# Fields
each field can be at most 8 * 2^32 bits of length:
- 34359738368 bits
- 4294967296 Bytes
## Key
**MUST** be a `String`
## Value
It's up to the Application to decide whether the value is
a string or another type of datum
```
+-- 64 - bits--------------------------+----------------------------------------+
| Key-Length 32 | Value-Length 32 |
+--------------------------------------+----------------------------------------+
\ Key /
|-----------------------------------------------------------------------------|
/ Value \
+-------------------------------------------------------------------------------+
```

View File

@ -0,0 +1,16 @@
import Foundation
public protocol MessageP {
var version: UInt8 {get}
var messageType: MessageType {get}
var devType: DeviceType {get}
var RESERVED: UInt8 {get}
var signType: SignType {get}
var timestamp: Date {get}
var devID: UInt128 {get}
var location: Location {get}
var fields: [Field] {get}
func toString() -> String
}

View File

@ -1,43 +1,43 @@
import Foundation
extension UInt8: DataCompatibleP {
public var data: Data {
var obj = self
return Data(bytes: &obj, count: MemoryLayout<Self>.stride)
}
}
extension UInt16: DataCompatibleP {
public var data: Data {
var obj = self
return Data(bytes: &obj, count: MemoryLayout<Self>.stride)
}
}
extension UInt32: DataCompatibleP {
public var data: Data {
var obj = self
return Data(bytes: &obj, count: MemoryLayout<Self>.stride)
}
}
extension UInt64: DataCompatibleP {
public var data: Data {
var obj = self
return Data(bytes: &obj, count: MemoryLayout<Self>.stride)
}
}
extension UInt128: DataCompatibleP {
public var data: Data {
var obj = self
return Data(bytes: &obj, count: MemoryLayout<Self>.stride)
}
}
extension Date: DataCompatibleP {
public var data: Data {
var obj = self
return Data(bytes: &obj, count: MemoryLayout<Self>.stride)
}
}
import Foundation
extension UInt8: DataCompatibleP {
public var data: Data {
var obj = self
return Data(bytes: &obj, count: MemoryLayout<Self>.stride)
}
}
extension UInt16: DataCompatibleP {
public var data: Data {
var obj = self
return Data(bytes: &obj, count: MemoryLayout<Self>.stride)
}
}
extension UInt32: DataCompatibleP {
public var data: Data {
var obj = self
return Data(bytes: &obj, count: MemoryLayout<Self>.stride)
}
}
extension UInt64: DataCompatibleP {
public var data: Data {
var obj = self
return Data(bytes: &obj, count: MemoryLayout<Self>.stride)
}
}
extension UInt128: DataCompatibleP {
public var data: Data {
var obj = self
return Data(bytes: &obj, count: MemoryLayout<Self>.stride)
}
}
extension Date: DataCompatibleP {
public var data: Data {
var obj = self
return Data(bytes: &obj, count: MemoryLayout<Self>.stride)
}
}

View File

@ -1,87 +1,120 @@
import Foundation
import Testing
@testable import MessageUtils
@Test func serializeMessage() async throws {
// Write your test here and use APIs like `#expect(...)` to check expected conditions.
let msg = Message(
version: 1,
messageType: .KEEPALIVE,
devType: .EDGE_SENSOR,
RESERVED: 0,
signType: .P521,
timestamp: Date(),
devID: 1,
location: Location(x: 10,y: 20,z: 1),
fields: [],
signature: Array(repeating: 255, count: 132)
)
let data = try serializeV1(msg: msg)
}
@Test func serializeMessageWithField() async throws {
// Write your test here and use APIs like `#expect(...)` to check expected conditions.
let msg = Message(
version: 1,
messageType: .KEEPALIVE,
devType: .EDGE_SENSOR,
RESERVED: 0,
signType: .P521,
timestamp: Date(),
devID: 1,
location: Location(x: 10,y: 20,z: 1),
fields: [
Field(key: Array("valueOfLife".utf8), value: Array("42".utf8)),
Field(key: Array("valueOfLife".utf8), value: Array("42".utf8))
],
signature: Array(repeating: 255, count: 132)
)
let data = try serializeV1(msg: msg)
}
@Test func serializeDeserializeMessage() async throws {
// Write your test here and use APIs like `#expect(...)` to check expected conditions.
let msg = Message(
version: 1,
messageType: .KEEPALIVE,
devType: .EDGE_SENSOR,
RESERVED: 0,
signType: .P521,
timestamp: Date(),
devID: 1,
location: Location(x: 10,y: 20,z: 1),
fields: [
Field(key: Array("valueOfLife".utf8), value: Array("42".utf8)),
Field(key: Array("valueOfLife".utf8), value: Array("42".utf8))
],
signature: Array(repeating: 255, count: 132)
)
print(msg.toString())
let data = try serializeV1(msg: msg)
let bytes = data.map { value in
return value
}
print(bytes.count % 8 == 0)
let msg2 = try derializeV1(serializedData: data)
print(msg2.toString())
}
import Foundation
import Testing
@testable import MessageUtils
@Test func serializeMessage() async throws {
// Write your test here and use APIs like `#expect(...)` to check expected conditions.
let msg = SignedMessage(
version: 1,
messageType: .KEEPALIVE,
devType: .EDGE_SENSOR,
RESERVED: 0,
signType: .P521,
timestamp: Date(),
devID: 1,
location: Location(x: 10, y: 20, z: 1),
fields: [],
signature: Array(repeating: 255, count: 132)
)
let data = try serializeV1(msg: msg)
}
@Test func serializeMessageWithField() async throws {
// Write your test here and use APIs like `#expect(...)` to check expected conditions.
let msg = Message(
version: 1,
messageType: .KEEPALIVE,
devType: .EDGE_SENSOR,
RESERVED: 0,
signType: .P521,
timestamp: Date(),
devID: 1,
location: Location(x: 10, y: 20, z: 1),
fields: [
Field(key: Array("valueOfLife".utf8), value: Array("42".utf8)),
Field(key: Array("valueOfLife".utf8), value: Array("42".utf8)),
]
)
let data = try serializeV1(msg: msg)
}
@Test func serializeDeserializeMessage() async throws {
let keyPath = "./Private/privateKey.pem"
let key = try pem2key(filePath: keyPath)
let publicKey = key.publicKey
// Write your test here and use APIs like `#expect(...)` to check expected conditions.
let msg = Message(
version: 1,
messageType: .KEEPALIVE,
devType: .EDGE_SENSOR,
RESERVED: 0,
signType: .P521,
timestamp: Date(),
devID: 1,
location: Location(x: 10, y: 20, z: 1),
fields: [
Field(key: Array("valueOfLife".utf8), value: Array("42".utf8)),
Field(key: Array("valueOfLife".utf8), value: Array("42".utf8)),
Field(key: Array("69".utf8), value: Array("Nice".utf8)),
Field(key: Array("valueOfLife".utf8), value: Array("42".utf8)),
Field(key: Array("Live Love".utf8), value: Array("Laugh".utf8)),
]
)
print(msg.toString())
var data = serializeV1(msg: msg)
let bytes = data.map { value in
return value
}
let actualBytes = countBytes(msg: msg)
print("Number of Bytes for the message: \(bytes.count)")
print("Number of Computed Bytes: \(actualBytes)")
let signature = try signMessage(msgData: data, signType: msg.signType, key: key)
data.append(contentsOf: signature)
let msg2 = try deserializeV1(serializedData: data)
let authenticMessage = try verifyMessageSignature(message: msg2, key: publicKey)
#expect(authenticMessage, "If this is not true, then we are doing some deserialization errors")
print(msg2.toString())
}
@Test func serializeMessageForLaterUse() async throws {
// Write your test here and use APIs like `#expect(...)` to check expected conditions.
let keyPath = "./Private/privateKey.pem"
let key = try pem2key(filePath: keyPath)
let msg = Message(
version: 1,
messageType: .KEEPALIVE,
devType: .EDGE_SENSOR,
RESERVED: 0,
signType: .P521,
timestamp: Date(),
devID: 1,
location: Location(x: 10, y: 20, z: 1),
fields: [
Field(key: Array("valueOfLife".utf8), value: Array("42".utf8)),
Field(key: Array("Live Love".utf8), value: Array("Laugh".utf8)),
]
)
var data = try serializeV1(msg: msg)
let signature = try signMessage(msgData: data, signType: msg.signType, key: key)
data.append(Data(signature))
try data.write(to: URL(filePath: "./Private/signedMessage/Message.bin"))
}