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

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)
}
}