diff --git a/.idea/sqldialects.xml b/.idea/sqldialects.xml
index b4dcf43..8fb5dbe 100644
--- a/.idea/sqldialects.xml
+++ b/.idea/sqldialects.xml
@@ -4,8 +4,10 @@
+
+
@@ -14,6 +16,7 @@
+
diff --git a/src/Socialbox/Classes/Resources/database/channel_com.sql b/src/Socialbox/Classes/Resources/database/channel_com.sql
new file mode 100644
index 0000000..ab75fd7
--- /dev/null
+++ b/src/Socialbox/Classes/Resources/database/channel_com.sql
@@ -0,0 +1,32 @@
+create table channel_com
+(
+ uuid varchar(36) default uuid() not null comment 'The UUID of the message',
+ channel_uuid varchar(36) not null comment 'The UUID of the encryption channel used',
+ recipient enum ('SENDER', 'RECEIVER') not null comment 'The recipient of the message',
+ message text not null comment 'The encrypted message content',
+ signature varchar(64) not null comment 'The signature of the decrypted message',
+ received tinyint(1) default 0 not null comment 'True if the message was received by the recipient',
+ timestamp timestamp default current_timestamp() not null comment 'The timestamp of the mssage being sent',
+ primary key (uuid, channel_uuid) comment 'The Unique Pair Index for the channel UUID and message UUID',
+ constraint channel_com_uuid_channel_uuid_uindex
+ unique (uuid, channel_uuid) comment 'The Unique Pair Index for the channel UUID and message UUID',
+ constraint channel_com_uuid_channel_uuid_uindex_2
+ unique (uuid, channel_uuid) comment 'The Unique Index Pair for the channel UUID and message UUID',
+ constraint channel_com_encryption_channels_uuid_fk
+ foreign key (channel_uuid) references encryption_channels (uuid)
+ on update cascade on delete cascade
+)
+ comment 'Table for housing communication messages over encryption channels';
+
+create index channel_com_received_index
+ on channel_com (received)
+ comment 'The index for the received column';
+
+create index channel_com_recipient_index
+ on channel_com (recipient)
+ comment 'The index for the recipient column';
+
+create index channel_com_timestamp_index
+ on channel_com (timestamp)
+ comment 'The index for the Timestamp column';
+
diff --git a/src/Socialbox/Classes/Resources/database/encrypted_channels.sql b/src/Socialbox/Classes/Resources/database/encrypted_channels.sql
new file mode 100644
index 0000000..f906d93
--- /dev/null
+++ b/src/Socialbox/Classes/Resources/database/encrypted_channels.sql
@@ -0,0 +1,36 @@
+create table encryption_channels
+(
+ uuid varchar(36) not null comment 'The Unique Universal Identifier for the encryption channel'
+ primary key comment 'The Unique Index of the encryption channel UUID',
+ calling_peer varchar(320) not null comment 'The address of the calling peer',
+ calling_signature_uuid varchar(64) not null comment 'The UUID of the signing key that the calling peer is going to use to sign their messages',
+ calling_signature_public_key varchar(32) not null,
+ calling_encryption_public_key varchar(32) not null comment 'The public encryption key of the caller',
+ receiving_peer varchar(320) not null comment 'The address of the receiving peer',
+ receiving_signature_uuid varchar(256) null comment 'The UUID of the signature that the receiver peer will use to sign messages with',
+ receiving_signature_public_key varchar(32) null comment 'The public key of the receiver''s signing key',
+ receiving_encryption_public_key varchar(32) null comment 'The encryption key of the receiver',
+ transport_encryption_algorithm enum ('xchacha20', 'chacha20', 'aes256gcm') default 'xchacha20' not null comment 'The transport encryption algorithm used as selected by the caller',
+ transport_encryption_key varchar(256) null comment 'The transport encryption key encrypted using the caller''s public encryption key',
+ state enum ('AWAITING_RECEIVER', 'ERROR', 'DECLINED', 'AWAITING_DHE', 'OPENED', 'CLOSED') default 'AWAITING_RECEIVER' not null comment 'The current state of the encryption channel',
+ created timestamp default current_timestamp() not null comment 'The Timestamp for when this record was created',
+ constraint encryption_channels_uuid_uindex
+ unique (uuid) comment 'The Unique Index of the encryption channel UUID'
+);
+
+create index encryption_channels_calling_peer_index
+ on encryption_channels (calling_peer)
+ comment 'The index of the calling peer address';
+
+create index encryption_channels_created_index
+ on encryption_channels (created)
+ comment 'The Index for when the record was created';
+
+create index encryption_channels_receiving_peer_index
+ on encryption_channels (receiving_peer)
+ comment 'The index of the receiving peer address';
+
+create index encryption_channels_state_index
+ on encryption_channels (state)
+ comment 'The index for the state column';
+
diff --git a/src/Socialbox/Enums/DatabaseObjects.php b/src/Socialbox/Enums/DatabaseObjects.php
index 327698b..f2ad497 100644
--- a/src/Socialbox/Enums/DatabaseObjects.php
+++ b/src/Socialbox/Enums/DatabaseObjects.php
@@ -18,6 +18,9 @@
case SIGNING_KEYS = 'signing_keys.sql';
case EXTERNAL_SESSIONS = 'external_sessions.sql';
+ case ENCRYPTED_CHANNELS = 'encrypted_channels.sql';
+ case CHANNEL_COM = 'channel_com.sql';
+
case CONTACT_KNOWN_KEYS = 'contact_known_keys.sql';
/**
@@ -43,6 +46,8 @@
self::SIGNING_KEYS,
self::EXTERNAL_SESSIONS => 2,
+ self::ENCRYPTED_CHANNELS,
+ self::CHANNEL_COM,
self::CONTACT_KNOWN_KEYS => 3,
};
}
diff --git a/src/Socialbox/Enums/StandardError.php b/src/Socialbox/Enums/StandardError.php
index 2184f55..0cf213d 100644
--- a/src/Socialbox/Enums/StandardError.php
+++ b/src/Socialbox/Enums/StandardError.php
@@ -18,6 +18,7 @@
case CONFLICT = -107;
case EXPIRED = -108;
case CRYPTOGRAPHIC_ERROR = -109;
+ case UUID_CONFLICT = -110;
// RPC Errors
case RPC_METHOD_NOT_FOUND = -1000;
diff --git a/src/Socialbox/Enums/Status/EncryptionChannelState.php b/src/Socialbox/Enums/Status/EncryptionChannelState.php
new file mode 100644
index 0000000..fd46611
--- /dev/null
+++ b/src/Socialbox/Enums/Status/EncryptionChannelState.php
@@ -0,0 +1,12 @@
+toRfc4122();
+ }
+
+ try
+ {
+ $stmt = Database::getConnection()->prepare('INSERT INTO encryption_channels (uuid, calling_peer, calling_signature_uuid, calling_signature_public_key, calling_encryption_public_key, receiving_peer, transport_encryption_algorithm) VALUES (:uuid, :calling_peer, :calling_signature_uuid, :calling_signature_public_key, :calling_encryption_public_key, :receiving_peer, :transport_encryption_algorithm)');
+ $stmt->bindParam(':uuid', $uuid);
+ $callingPeerAddress = $callingPeer->getAddress();
+ $stmt->bindParam(':calling_peer', $callingPeerAddress);
+ $stmt->bindParam(':calling_signature_uuid', $signatureUuid);
+ $stmt->bindParam(':calling_signature_public_key', $signingPublicKey);
+ $stmt->bindParam(':calling_encryption_public_key', $encryptionPublicKey);
+ $receivingPeerAddress = $receivingPeer->getAddress();
+ $stmt->bindParam(':receiving_peer', $receivingPeerAddress);
+ $stmt->bindParam(':transport_encryption_algorithm', $transportEncryptionAlgorithm);
+ $stmt->execute();
+ }
+ catch(PDOException $e)
+ {
+ throw new DatabaseOperationException('Failed to create the encryption channel', $e);
+ }
+
+ return $uuid;
+ }
+
+ /**
+ * Retrieves the incoming encryption channels for the specified peer.
+ *
+ * @param string|PeerAddress $peerAddress The peer to retrieve the channels for.
+ * @param int $limit The maximum number of channels to retrieve.
+ * @param int $page The page of channels to retrieve.
+ * @return EncryptionChannelRecord[] The incoming channels for the peer.
+ * @throws DatabaseOperationException If an error occurs while retrieving the channels.
+ * @throws \DateMalformedStringException If the created date is not a valid date string.
+ */
+ public static function getChannels(string|PeerAddress $peerAddress, int $limit=100, int $page=0): array
+ {
+ if($peerAddress instanceof PeerAddress)
+ {
+ $peerAddress = $peerAddress->getAddress();
+ }
+
+ try
+ {
+ $stmt = Database::getConnection()->prepare('SELECT * FROM encryption_channels WHERE calling_peer=:address OR receiving_peer=:address LIMIT :limit OFFSET :offset');
+ $stmt->bindParam(':address', $peerAddress);
+ $stmt->bindParam(':limit', $limit, PDO::PARAM_INT);
+ $offset = $page * $limit;
+ $stmt->bindParam(':offset', $offset, PDO::PARAM_INT);
+ $stmt->execute();
+ $results = $stmt->fetchAll();
+
+ $channels = [];
+ foreach($results as $result)
+ {
+ $channels[] = new EncryptionChannelRecord($result);
+ }
+
+ return $channels;
+ }
+ catch(PDOException $e)
+ {
+ throw new DatabaseOperationException('Failed to retrieve the encryption channels', $e);
+ }
+ }
+
+ /**
+ * Retrieves the incoming encryption channels for the specified peer.
+ *
+ * @param string|PeerAddress $peerAddress The peer to retrieve the channels for.
+ * @param int $limit The maximum number of channels to retrieve.
+ * @param int $page The page of channels to retrieve.
+ * @return EncryptionChannelRecord[] The incoming channels for the peer.
+ * @throws DatabaseOperationException If an error occurs while retrieving the channels.
+ * @throws \DateMalformedStringException If the created date is not a valid date string.
+ */
+ public static function getRequests(string|PeerAddress $peerAddress, int $limit=100, int $page=0): array
+ {
+ if($peerAddress instanceof PeerAddress)
+ {
+ $peerAddress = $peerAddress->getAddress();
+ }
+
+ try
+ {
+ $stmt = Database::getConnection()->prepare('SELECT * FROM encryption_channels WHERE receiving_peer=:address AND state=:state LIMIT :limit OFFSET :offset');
+ $stmt->bindParam(':address', $peerAddress);
+ $state = EncryptionChannelState::AWAITING_RECEIVER->value;
+ $stmt->bindParam(':state', $state);
+ $stmt->bindParam(':limit', $limit, PDO::PARAM_INT);
+ $offset = $page * $limit;
+ $stmt->bindParam(':offset', $offset, PDO::PARAM_INT);
+ $stmt->execute();
+ $results = $stmt->fetchAll();
+
+ $channels = [];
+ foreach($results as $result)
+ {
+ $channels[] = new EncryptionChannelRecord($result);
+ }
+
+ return $channels;
+ }
+ catch(PDOException $e)
+ {
+ throw new DatabaseOperationException('Failed to retrieve the encryption channels', $e);
+ }
+ }
+
+ /**
+ * Retrieves the incoming encryption channels for the specified peer.
+ *
+ * @param string|PeerAddress $peerAddress The peer to retrieve the channels for.
+ * @param int $limit The maximum number of channels to retrieve.
+ * @param int $page The page of channels to retrieve.
+ * @return EncryptionChannelRecord[] The incoming channels for the peer.
+ * @throws DatabaseOperationException If an error occurs while retrieving the channels.
+ * @throws \DateMalformedStringException If the created date is not a valid date string.
+ */
+ public static function getIncomingChannels(string|PeerAddress $peerAddress, int $limit=100, int $page=0): array
+ {
+ if($peerAddress instanceof PeerAddress)
+ {
+ $peerUuid = $peerAddress->getAddress();
+ }
+
+ try
+ {
+ $stmt = Database::getConnection()->prepare('SELECT * FROM encryption_channels WHERE receiving_peer=:address LIMIT :limit OFFSET :offset');
+ $stmt->bindParam(':address', $peerUuid);
+ $stmt->bindParam(':limit', $limit, PDO::PARAM_INT);
+ $offset = $page * $limit;
+ $stmt->bindParam(':offset', $offset, PDO::PARAM_INT);
+ $stmt->execute();
+ $results = $stmt->fetchAll();
+
+ $channels = [];
+ foreach($results as $result)
+ {
+ $channels[] = new EncryptionChannelRecord($result);
+ }
+
+ return $channels;
+ }
+ catch(PDOException $e)
+ {
+ throw new DatabaseOperationException('Failed to retrieve the encryption channels', $e);
+ }
+ }
+
+ /**
+ * Retrieves the outgoing channels for the specified peer.
+ *
+ * @param string|PeerAddress $peerAddress The peer to retrieve the channels for.
+ * @param int $limit The maximum number of channels to retrieve.
+ * @param int $page The page of channels to retrieve.
+ * @return EncryptionChannelRecord[] The outgoing channels for the specified peer.
+ * @throws DatabaseOperationException If an error occurs while retrieving the channels.
+ * @throws \DateMalformedStringException If the created date is not a valid date string.
+ */
+ public static function getOutgoingChannels(string|PeerAddress $peerAddress, int $limit=100, int $page=0): array
+ {
+ if($peerAddress instanceof PeerAddress)
+ {
+ $peerAddress = $peerAddress->getAddress();
+ }
+
+ try
+ {
+ $stmt = Database::getConnection()->prepare('SELECT * FROM encryption_channels WHERE calling_peer=:address LIMIT :limit OFFSET :offset');
+ $stmt->bindParam(':address', $peerAddress);
+ $stmt->bindParam(':limit', $limit, PDO::PARAM_INT);
+ $offset = $page * $limit;
+ $stmt->bindParam(':offset', $offset, PDO::PARAM_INT);
+ $stmt->execute();
+ $results = $stmt->fetchAll();
+
+ $channels = [];
+ foreach($results as $result)
+ {
+ $channels[] = new EncryptionChannelRecord($result);
+ }
+
+ return $channels;
+ }
+ catch(PDOException $e)
+ {
+ throw new DatabaseOperationException('Failed to retrieve the encryption channels', $e);
+ }
+ }
+
+ /**
+ * Declines the encryption channel with the specified UUID.
+ *
+ * @param string $channelUuid The UUID of the channel to decline.
+ * @throws DatabaseOperationException If an error occurs while declining the channel.
+ */
+ public static function declineChannel(string $channelUuid): void
+ {
+ try
+ {
+ $stmt = Database::getConnection()->prepare('UPDATE encryption_channels SET state=:state WHERE uuid=:uuid');
+ $state = EncryptionChannelState::DECLINED->value;
+ $stmt->bindParam(':state', $state);
+ $stmt->bindParam(':uuid', $channelUuid);
+ $stmt->execute();
+ }
+ catch(PDOException $e)
+ {
+ throw new DatabaseOperationException('Failed to decline the encryption channel', $e);
+ }
+ }
+
+ /**
+ * Accepts the encryption channel with the specified UUID.
+ *
+ * @param string $channelUuid The UUID of the channel to accept.
+ * @param string $signatureUuid The UUID of the signature used to create the channel.
+ * @param string $signaturePublicKey The public key used for signing.
+ * @param string $encryptionPublicKey The public key used for encryption.
+ * @param string $transportEncryptionAlgorithm The algorithm used for transport encryption.
+ * @param string $encryptedTransportEncryptionKey The encrypted transport encryption key.
+ * @throws DatabaseOperationException If an error occurs while accepting the channel.
+ */
+ public static function acceptChannel(string $channelUuid, string $signatureUuid, string $signaturePublicKey, string $encryptionPublicKey, string $transportEncryptionAlgorithm, string $encryptedTransportEncryptionKey): void
+ {
+ try
+ {
+ $stmt = Database::getConnection()->prepare('UPDATE encryption_channels SET state=:state, receiving_signature_uuid=:receiving_signature_uuid, receiving_signature_public_key=:receiving_signature_public_key, receiving_encryption_public_key=:receiving_encryption_public_key, transport_encryption_algorithm=:transport_encryption_algorithm, transport_encryption_key=:transport_encryption_key WHERE uuid=:uuid');
+ $state = EncryptionChannelState::OPENED->value;
+ $stmt->bindParam(':state', $state);
+ $stmt->bindParam(':receiving_signature_uuid', $signatureUuid);
+ $stmt->bindParam(':receiving_signature_public_key', $signaturePublicKey);
+ $stmt->bindParam(':receiving_encryption_public_key', $encryptionPublicKey);
+ $stmt->bindParam(':transport_encryption_algorithm', $transportEncryptionAlgorithm);
+ $stmt->bindParam(':transport_encryption_key', $encryptedTransportEncryptionKey);
+ $stmt->bindParam(':uuid', $channelUuid);
+ $stmt->execute();
+ }
+ catch(PDOException $e)
+ {
+ throw new DatabaseOperationException('Failed to accept the encryption channel', $e);
+ }
+ }
+
+ /**
+ * Retrieves the encryption channel with the specified UUID.
+ *
+ * @param string $channelUuid The UUID of the channel to retrieve.
+ * @return EncryptionChannelRecord|null The record of the encryption channel. Null if the channel does not exist.
+ * @throws DatabaseOperationException If an error occurs while retrieving the channel.
+ * @throws \DateMalformedStringException If the created date is not a valid date string.
+ */
+ public static function getChannel(string $channelUuid): ?EncryptionChannelRecord
+ {
+ try
+ {
+ $stmt = Database::getConnection()->prepare('SELECT * FROM encryption_channels WHERE uuid=:uuid');
+ $stmt->bindParam(':uuid', $channelUuid);
+ $stmt->execute();
+ $result = $stmt->fetch();
+
+ if($result === false)
+ {
+ return null;
+ }
+
+ return new EncryptionChannelRecord($result);
+ }
+ catch(PDOException $e)
+ {
+ throw new DatabaseOperationException('Failed to retrieve the encryption channel', $e);
+ }
+ }
+
+ /**
+ * Deletes the encryption channel with the specified UUID.
+ *
+ * @param string $channelUuid The UUID of the channel to delete.
+ * @return void
+ *@throws DatabaseOperationException If an error occurs while deleting the channel.
+ */
+ public static function deleteChannel(string $channelUuid): void
+ {
+ try
+ {
+ $stmt = Database::getConnection()->prepare('DELETE FROM encryption_channels WHERE uuid=:uuid');
+ $stmt->bindParam(':uuid', $channelUuid);
+ $stmt->execute();
+ }
+ catch(PDOException $e)
+ {
+ throw new DatabaseOperationException('Failed to delete the encryption channel', $e);
+ }
+ }
+
+ /**
+ * Updates the state of the encryption channel with the specified UUID.
+ *
+ * @param string $channelUuid The UUID of the channel to update.
+ * @return EncryptionChannelState The current state of the channel.
+ * @throws DatabaseOperationException If an error occurs while updating the channel state.
+ */
+ public static function getChannelState(string $channelUuid): EncryptionChannelState
+ {
+ try
+ {
+ $stmt = Database::getConnection()->prepare('SELECT state FROM encryption_channels WHERE uuid=:uuid');
+ $stmt->bindParam(':uuid', $channelUuid);
+ $stmt->execute();
+
+ return EncryptionChannelState::from($stmt->fetchColumn());
+ }
+ catch(PDOException $e)
+ {
+ throw new DatabaseOperationException('Failed to retrieve the encryption channel state', $e);
+ }
+ }
+
+ /**
+ * Updates the state of the encryption channel with the specified UUID.
+ *
+ * @param string $channelUuid The UUID of the channel to update.
+ * @param EncryptionChannelState $state The new state of the channel.
+ * @return void The current state of the channel.
+ * @throws DatabaseOperationException If an error occurs while updating the channel state.
+ */
+ public static function updateChannelState(string $channelUuid, EncryptionChannelState $state): void
+ {
+ try
+ {
+ $stmt = Database::getConnection()->prepare('UPDATE encryption_channels SET state=:state WHERE uuid=:uuid');
+ $state = $state->value;
+ $stmt->bindParam(':state', $state);
+ $stmt->bindParam(':uuid', $channelUuid);
+ $stmt->execute();
+ }
+ catch(PDOException $e)
+ {
+ throw new DatabaseOperationException('Failed to update the encryption channel state', $e);
+ }
+ }
+
+ /**
+ * Checks if a channel with the provided UUID exists.
+ *
+ * @param string $uuid The UUID of the channel to check.
+ * @return bool True if the channel exists, False otherwise.
+ * @throws DatabaseOperationException If an error occurs while checking the channel.
+ */
+ public static function channelExists(string $uuid): bool
+ {
+ try
+ {
+ $stmt = Database::getConnection()->prepare('SELECT COUNT(*) FROM encryption_channels WHERE uuid=:uuid');
+ $stmt->bindParam(':uuid', $uuid);
+ $stmt->execute();
+
+ return $stmt->fetchColumn() > 0;
+ }
+ catch(PDOException $e)
+ {
+ throw new DatabaseOperationException('There was an error while trying to check if the channel UUID exists', $e);
+ }
+ }
+
+ /**
+ * Sends data to the specified channel.
+ *
+ * @param string $channelUuid The UUID of the channel to send the data to.
+ * @param string $message The message to send.
+ * @param string $signature The signature of the message.
+ * @param CommunicationRecipientType $recipient The recipient type.
+ * @return string The UUID of the sent message.
+ * @throws DatabaseOperationException If an error occurs while sending the message.
+ */
+ public static function sendData(string $channelUuid, string $message, string $signature, CommunicationRecipientType $recipient): string
+ {
+ $uuid = UuidV4::v4()->toRfc4122();
+
+ try
+ {
+ $stmt = Database::getConnection()->prepare('INSERT INTO channel_com (uuid, channel_uuid, recipient, message, signature) VALUES (:uuid, :channel_uuid, :recipient, :message, :signature)');
+ $stmt->bindParam(':uuid', $uuid);
+ $stmt->bindParam(':channel_uuid', $channelUuid);
+ $recipient = $recipient->value;
+ $stmt->bindParam(':recipient', $recipient);
+ $stmt->bindParam(':message', $message);
+ $stmt->bindParam(':signature', $signature);
+
+ $stmt->execute();
+ }
+ catch(PDOException $e)
+ {
+ throw new DatabaseOperationException('Failed to send the message', $e);
+ }
+
+ return $uuid;
+ }
+
+ /**
+ * Retrieves the messages for the specified channel and recipient.
+ *
+ * @param string $channelUuid The UUID of the channel to retrieve the messages for.
+ * @param CommunicationRecipientType $recipient The recipient type to retrieve the messages for.
+ * @return ChannelMessageRecord[] The messages for the specified channel and recipient.
+ * @throws DatabaseOperationException If an error occurs while retrieving the messages.
+ */
+ public static function receiveData(string $channelUuid, CommunicationRecipientType $recipient): array
+ {
+ try
+ {
+ $stmt = Database::getConnection()->prepare('SELECT * FROM channel_com WHERE channel_uuid=:channel_uuid AND recipient=:recipient AND received=0 ORDER BY timestamp');
+ $stmt->bindParam(':channel_uuid', $channelUuid);
+ $recipient = $recipient->value;
+ $stmt->bindParam(':recipient', $recipient);
+ $stmt->execute();
+ $results = $stmt->fetchAll();
+
+ $messages = [];
+ foreach($results as $result)
+ {
+ $messages[] = new ChannelMessageRecord($result);
+ }
+
+ return $messages;
+ }
+ catch(PDOException $e)
+ {
+ throw new DatabaseOperationException('Failed to retrieve the messages', $e);
+ }
+ }
+
+ /**
+ * Retrieves the message with the specified UUID.
+ *
+ * @param string $channelUuid The UUID of the channel to retrieve the message for.
+ * @param string $messageUuid The UUID of the message to retrieve.
+ * @return ChannelMessageRecord|null The message with the specified UUID. Null if the message does not exist.
+ * @throws DatabaseOperationException If an error occurs while retrieving the message.
+ */
+ public static function getData(string $channelUuid, string $messageUuid): ?ChannelMessageRecord
+ {
+ try
+ {
+ $stmt = Database::getConnection()->prepare('SELECT * FROM channel_com WHERE channel_uuid=:channel_uuid AND uuid=:uuid');
+ $stmt->bindParam(':channel_uuid', $channelUuid);
+ $stmt->bindParam(':uuid', $messageUuid);
+ $stmt->execute();
+ $result = $stmt->fetch();
+
+ if($result === false)
+ {
+ return null;
+ }
+
+ return new ChannelMessageRecord($result);
+ }
+ catch(PDOException $e)
+ {
+ throw new DatabaseOperationException('Failed to retrieve the message', $e);
+ }
+ }
+
+ /**
+ * Marks the message with the specified UUID as received.
+ *
+ * @param string $uuid The UUID of the message to mark as received.
+ * @throws DatabaseOperationException If an error occurs while marking the message as received.
+ */
+ public static function markDataAsReceived(string $uuid): void
+ {
+ try
+ {
+ $stmt = Database::getConnection()->prepare('UPDATE channel_com SET received=1 WHERE uuid=:uuid');
+ $stmt->bindParam(':uuid', $uuid);
+ $stmt->execute();
+ }
+ catch(PDOException $e)
+ {
+ throw new DatabaseOperationException('Failed to mark the message as received', $e);
+ }
+ }
+
+ /**
+ * Deletes the message with the specified UUID.
+ *
+ * @param string $uuid The UUID of the message to delete.
+ * @throws DatabaseOperationException If an error occurs while deleting the message.
+ */
+ public static function deleteData(string $uuid): void
+ {
+ try
+ {
+ $stmt = Database::getConnection()->prepare('DELETE FROM channel_com WHERE uuid=:uuid');
+ $stmt->bindParam(':uuid', $uuid);
+ $stmt->execute();
+ }
+ catch(PDOException $e)
+ {
+ throw new DatabaseOperationException('Failed to delete the message', $e);
+ }
+ }
+ }
\ No newline at end of file
diff --git a/src/Socialbox/Objects/Database/ChannelMessageRecord.php b/src/Socialbox/Objects/Database/ChannelMessageRecord.php
new file mode 100644
index 0000000..aed0925
--- /dev/null
+++ b/src/Socialbox/Objects/Database/ChannelMessageRecord.php
@@ -0,0 +1,141 @@
+uuid = $data['uuid'];
+ $this->channelUuid = $data['channel_uuid'];
+ $this->recipient = CommunicationRecipientType::from($data['recipient']);
+ $this->message = $data['message'];
+ $this->signature = $data['signature'];
+ $this->received = (bool)$data['received'];
+
+ if($data['timestamp'] instanceof \DateTime)
+ {
+ $this->timestamp = $data['timestamp'];
+ }
+ elseif(is_int($data['timestamp']))
+ {
+ $this->timestamp = (new \DateTime())->setTimestamp($data['timestamp']);
+ }
+ elseif(is_string($data['timestamp']))
+ {
+ $this->timestamp = new \DateTime($data['timestamp']);
+ }
+ else
+ {
+ throw new \InvalidArgumentException('Invalid timestamp type, got ' . gettype($data['timestamp']));
+ }
+ }
+
+ /**
+ * Returns the unique identifier for the message.
+ *
+ * @return string
+ */
+ public function getUuid(): string
+ {
+ return $this->uuid;
+ }
+
+ /**
+ * Returns the UUID of the channel that the message belongs to.
+ *
+ * @return string
+ */
+ public function getChannelUuid(): string
+ {
+ return $this->channelUuid;
+ }
+
+ /**
+ * Returns the recipient type of the message.
+ *
+ * @return CommunicationRecipientType
+ */
+ public function getRecipient(): CommunicationRecipientType
+ {
+ return $this->recipient;
+ }
+
+ /**
+ * Returns the message content.
+ *
+ * @return string
+ */
+ public function getMessage(): string
+ {
+ return $this->message;
+ }
+
+ /**
+ * Returns the signature of the message.
+ *
+ * @return string
+ */
+ public function getSignature(): string
+ {
+ return $this->signature;
+ }
+
+ public function isReceived(): bool
+ {
+ return $this->received;
+ }
+
+ public function getTimestamp(): \DateTime
+ {
+ return $this->timestamp;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function fromArray(array $data): ChannelMessageRecord
+ {
+ return new self($data);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function toArray(): array
+ {
+ return [
+ 'uuid' => $this->uuid,
+ 'channel_uuid' => $this->channelUuid,
+ 'recipient' => $this->recipient->value,
+ 'message' => $this->message,
+ 'signature' => $this->signature,
+ 'received' => $this->received,
+ 'timestamp' => $this->timestamp->format('Y-m-d H:i:s')
+ ];
+ }
+ }
\ No newline at end of file
diff --git a/src/Socialbox/Objects/Database/EncryptionChannelRecord.php b/src/Socialbox/Objects/Database/EncryptionChannelRecord.php
new file mode 100644
index 0000000..c8979f0
--- /dev/null
+++ b/src/Socialbox/Objects/Database/EncryptionChannelRecord.php
@@ -0,0 +1,259 @@
+uuid = $data['uuid'];
+
+ if(!isset($data['calling_peer']))
+ {
+ throw new InvalidArgumentException('Missing property calling_peer');
+ }
+ else
+ {
+ if(is_string($data['calling_peer']))
+ {
+ $this->callingPeer = PeerAddress::fromAddress($data['calling_peer']);
+ }
+ elseif($data['calling_peer'] instanceof PeerAddress)
+ {
+ $this->callingPeer = $data['calling_peer'];
+ }
+ else
+ {
+ throw new InvalidArgumentException('Unexpected calling_peer type, got ' . gettype($data['calling_peer']));
+ }
+ }
+
+ $this->callingSignatureUuid = $data['calling_signature_uuid'];
+ $this->callingEncryptionPublicKey = $data['calling_encryption_public_key'];
+
+ if(!isset($data['receiving_peer']))
+ {
+ throw new InvalidArgumentException('Missing property receiving_peer');
+ }
+ else
+ {
+ if(is_string($data['receiving_peer']))
+ {
+ $this->receivingPeer = PeerAddress::fromAddress($data['receiving_peer']);
+ }
+ elseif($data['receiving_peer'] instanceof PeerAddress)
+ {
+ $this->receivingPeer = $data['receiving_peer'];
+ }
+ else
+ {
+ throw new InvalidArgumentException('Unexpected receiving_peer type, got ' . gettype($data['receiving_peer']));
+ }
+ }
+
+ $this->receivingSignatureUuid = $data['receiving_signature_uuid'] ?? null;
+ $this->receivingSignaturePublicKey = $data['receiving_signature_public_key'] ?? null;
+ $this->receivingEncryptionPublicKey = $data['receiving_encryption_public_key'] ?? null;
+ $this->transportEncryptionAlgorithm = $data['transport_encryption_algorithm'];
+ $this->transportEncryptionKey = $data['transport_encryption_key'] ?? null;
+ $this->state = EncryptionChannelState::tryFrom($data['state']) ?? EncryptionChannelState::ERROR;
+
+ if(!isset($data['created']))
+ {
+ throw new InvalidArgumentException('Missing property created');
+ }
+ else
+ {
+ if(is_string($data['created']))
+ {
+ $this->created = new DateTime($data['created']);
+ }
+ elseif(is_int($data['created']))
+ {
+ $this->created = (new DateTime())->setTimestamp($data['created']);
+ }
+ elseif($data['created'] instanceof DateTime)
+ {
+ $this->created = $data['created'];
+ }
+ else
+ {
+ throw new InvalidArgumentException('Unexpected created type, got ' . gettype($data['created']));
+ }
+ }
+ }
+
+ /**
+ * Returns the Unique Universal Identifier for the encryption record
+ *
+ * @return string
+ */
+ public function getUuid(): string
+ {
+ return $this->uuid;
+ }
+
+ /**
+ * Returns the address of the calling peer
+ *
+ * @return PeerAddress
+ */
+ public function getCallingPeer(): PeerAddress
+ {
+ return $this->callingPeer;
+ }
+
+ /**
+ * Returns the UUID of the signing keypair that the caller is using
+ *
+ * @return string
+ */
+ public function getCallingSignatureUuid(): string
+ {
+ return $this->callingSignatureUuid;
+ }
+
+ /**
+ * Returns the public key of the encryption keypair that the caller is using
+ *
+ * @return string
+ */
+ public function getCallingEncryptionPublicKey(): string
+ {
+ return $this->callingEncryptionPublicKey;
+ }
+
+ /**
+ * Returns the address of the receiving peer
+ *
+ * @return PeerAddress
+ */
+ public function getReceivingPeer(): PeerAddress
+ {
+ return $this->receivingPeer;
+ }
+
+ /**
+ * Returns the UUID of the signing keypair that the receiver is using
+ *
+ * @return string|null
+ */
+ public function getReceivingSignatureUuid(): ?string
+ {
+ return $this->receivingSignatureUuid;
+ }
+
+ /**
+ * Returns the public key of the signing keypair that the receiver is using
+ *
+ * @return string|null
+ */
+ public function getReceivingSignaturePublicKey(): ?string
+ {
+ return $this->receivingSignaturePublicKey;
+ }
+
+ /**
+ * Returns the public key of the encryption keypair that the receiver is using
+ *
+ * @return string|null
+ */
+ public function getReceivingEncryptionPublicKey(): ?string
+ {
+ return $this->receivingEncryptionPublicKey;
+ }
+
+ /**
+ * Returns the algorithm used for transport encryption
+ *
+ * @return string
+ */
+ public function getTransportEncryptionAlgorithm(): string
+ {
+ return $this->transportEncryptionAlgorithm;
+ }
+
+ /**
+ * Returns the key used for transport encryption
+ *
+ * @return string|null
+ */
+ public function getTransportEncryptionKey(): ?string
+ {
+ return $this->transportEncryptionKey;
+ }
+
+ /**
+ * Returns the current state of the encryption channel
+ *
+ * @return EncryptionChannelState
+ */
+ public function getState(): EncryptionChannelState
+ {
+ return $this->state;
+ }
+
+ /**
+ * Returns the creation date of the encryption channel
+ *
+ * @return DateTime
+ */
+ public function getCreated(): DateTime
+ {
+ return $this->created;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function fromArray(array $data): EncryptionChannelRecord
+ {
+ return new self($data);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function toArray(): array
+ {
+ return [
+ 'uuid' => $this->uuid,
+ 'calling_peer' => $this->callingPeer->getAddress(),
+ 'calling_signature_uuid' => $this->callingSignatureUuid,
+ 'calling_encryption_public_key' => $this->callingEncryptionPublicKey,
+ 'receiving_peer' => $this->receivingPeer->getAddress(),
+ 'receiving_signature_uuid' => $this->receivingSignatureUuid,
+ 'receiving_signature_public_key' => $this->receivingSignaturePublicKey,
+ 'receiving_encryption_public_key' => $this->receivingEncryptionPublicKey,
+ 'transport_encryption_algorithm' => $this->transportEncryptionAlgorithm,
+ 'transport_encryption_key' => $this->transportEncryptionKey,
+ 'state' => $this->state->value,
+ 'created' => $this->created->format('Y-m-d H:i:s')
+ ];
+ }
+ }
\ No newline at end of file