Made message signing in Cryptography use SHA512 as the message content for... #1

Closed
netkas wants to merge 421 commits from master into dev
20 changed files with 523 additions and 662 deletions
Showing only changes of commit 29a3d42538 - Show all commits

View file

@ -166,6 +166,10 @@
// value that exceeds this limit, the server will use this limit instead.
// recommendation: 100
$config->setDefault('policies.get_contacts_limit', 100);
$config->setDefault('policies.get_encryption_channel_requests_limit', 100);
$config->setDefault('policies.get_encryption_channels_limit', 100);
$config->setDefault('policies.get_encryption_channel_incoming_limit', 100);
$config->setDefault('policies.get_encryption_channel_outgoing_limit', 100);
// Default privacy states for information fields associated with the peer
$config->setDefault('policies.default_display_picture_privacy', 'PUBLIC');

View file

@ -12,6 +12,10 @@
private int $imageCaptchaExpires;
private int $peerSyncInterval;
private int $getContactsLimit;
private int $getEncryptionChannelRequestsLimit;
private int $getEncryptionChannelsLimit;
private int $getEncryptionChannelIncomingLimit;
private int $getEncryptionChannelOutgoingLimit;
private PrivacyState $defaultDisplayPicturePrivacy;
private PrivacyState $defaultFirstNamePrivacy;
private PrivacyState $defaultMiddleNamePrivacy;
@ -43,6 +47,10 @@
$this->imageCaptchaExpires = $data['image_captcha_expires'];
$this->peerSyncInterval = $data['peer_sync_interval'];
$this->getContactsLimit = $data['get_contacts_limit'];
$this->getEncryptionChannelRequestsLimit = $data['get_encryption_channel_requests_limit'];
$this->getEncryptionChannelsLimit = $data['get_encryption_channels_limit'];
$this->getEncryptionChannelIncomingLimit = $data['get_encryption_channel_incoming_limit'];
$this->getEncryptionChannelOutgoingLimit = $data['get_encryption_channel_outgoing_limit'];
$this->defaultDisplayPicturePrivacy = PrivacyState::tryFrom($data['default_display_picture_privacy']) ?? PrivacyState::PRIVATE;
$this->defaultFirstNamePrivacy = PrivacyState::tryFrom($data['default_first_name_privacy']) ?? PrivacyState::PRIVATE;
$this->defaultMiddleNamePrivacy = PrivacyState::tryFrom($data['default_middle_name_privacy']) ?? PrivacyState::PRIVATE;
@ -110,6 +118,46 @@
return $this->getContactsLimit;
}
/**
* Returns the maximum number of encryption channel requests that can be retrieved in a single request
*
* @return int
*/
public function getEncryptionChannelRequestsLimit(): int
{
return $this->getEncryptionChannelRequestsLimit;
}
/**
* Returns the maximum number of encryption channels that can be retrieved in a single request
*
* @return int
*/
public function getEncryptionChannelsLimit(): int
{
return $this->getEncryptionChannelsLimit;
}
/**
* Returns the maximum number of incoming encryption channels that can be retrieved in a single request
*
* @return int
*/
public function getEncryptionChannelIncomingLimit(): int
{
return $this->getEncryptionChannelIncomingLimit;
}
/**
* Returns the maximum number of outgoing encryption channels that can be retrieved in a single request
*
* @return int
*/
public function getEncryptionChannelOutgoingLimit(): int
{
return $this->getEncryptionChannelOutgoingLimit;
}
/**
* Returns the default privacy state for the display picture
*

View file

@ -10,6 +10,7 @@
use Socialbox\Exceptions\DatabaseOperationException;
use Socialbox\Exceptions\ResolutionException;
use Socialbox\Exceptions\RpcException;
use Socialbox\Objects\Client\EncryptionChannelSecret;
use Socialbox\Objects\Client\ExportedSession;
use Socialbox\Objects\Client\SignatureKeyPair;
use Socialbox\Objects\KeyPair;
@ -39,6 +40,7 @@
private string $sessionUuid;
private ?string $defaultSigningKey;
private array $signingKeys;
private array $encryptionChannelSecrets;
/**
* Constructs a new instance with the specified peer address.
@ -78,6 +80,7 @@
$this->serverTransportEncryptionKey = $exportedSession->getServerTransportEncryptionKey();
$this->signingKeys = $exportedSession->getSigningKeys();
$this->defaultSigningKey = $exportedSession->getDefaultSigningKey();
$this->encryptionChannelSecrets = $exportedSession->getEncryptionChannelSecrets();
// Still solve the server information
$this->serverInformation = self::getServerInformation();
@ -107,6 +110,7 @@
// Set the initial properties
$this->signingKeys = [];
$this->encryptionChannelSecrets = [];
$this->defaultSigningKey = null;
$this->identifiedAs = $identifiedAs;
$this->remoteServer = $server ?? $identifiedAs->getDomain();
@ -771,6 +775,17 @@
return $this->signingKeys[$uuid] ?? null;
}
/**
* Deletes a signing key from the current instance.
*
* @param string $uuid The UUID of the signing key to be deleted.
* @return void
*/
public function deleteSigningKey(string $uuid): void
{
unset($this->signingKeys[$uuid]);
}
/**
* Retrieves the default signing key associated with the current instance.
*
@ -797,6 +812,71 @@
$this->defaultSigningKey = $uuid;
}
/**
* Retrieves the encryption channel keys associated with the current instance.
*
* @return EncryptionChannelSecret[] The encryption channel keys.
*/
public function getEncryptionChannelSecrets(): array
{
return $this->encryptionChannelSecrets;
}
/**
* Adds a new encryption channel key to the current instance.
*
* @param EncryptionChannelSecret $key The encryption channel key to be added.
* @return void
*/
public function addEncryptionChannelSecret(EncryptionChannelSecret $key): void
{
$this->encryptionChannelSecrets[$key->getChannelUuid()] = $key;
}
/**
* Removes an encryption channel key from the current instance.
*
* @param string $uuid The UUID of the encryption channel key to be removed.
* @return void
*/
public function removeEncryptionChannelKey(string $uuid): void
{
unset($this->encryptionChannelSecrets[$uuid]);
}
/**
* Retrieves the encryption channel key associated with the specified UUID.
*
* @param string $uuid The UUID of the encryption channel key to be retrieved.
* @return EncryptionChannelSecret|null The encryption channel key associated with the UUID, or null if not found.
*/
public function getEncryptionChannelKey(string $uuid): ?EncryptionChannelSecret
{
return $this->encryptionChannelSecrets[$uuid] ?? null;
}
/**
* Checks if an encryption channel key exists with the specified UUID.
*
* @param string $uuid The UUID of the encryption channel key to check.
* @return bool True if the encryption channel key exists, false otherwise.
*/
public function encryptionChannelKeyExists(string $uuid): bool
{
return isset($this->encryptionChannelSecrets[$uuid]);
}
/**
* Deletes an encryption channel key from the current instance.
*
* @param string $uuid The UUID of the encryption channel key to be deleted.
* @return void
*/
public function deleteEncryptionChannelKey(string $uuid): void
{
unset($this->encryptionChannelSecrets[$uuid]);
}
/**
* Exports the current session details into an ExportedSession object.
*
@ -821,7 +901,8 @@
'client_transport_encryption_key' => $this->clientTransportEncryptionKey,
'server_transport_encryption_key' => $this->serverTransportEncryptionKey,
'default_signing_key' => $this->defaultSigningKey,
'signing_keys' => array_map(fn(SignatureKeyPair $key) => $key->toArray(), $this->signingKeys)
'signing_keys' => array_map(fn(SignatureKeyPair $key) => $key->toArray(), $this->signingKeys),
'encryption_channel_secrets' => array_map(fn(EncryptionChannelSecret $key) => $key->toArray(), $this->encryptionChannelSecrets)
]);
}
}

View file

@ -5,6 +5,7 @@
use Exception;
use InvalidArgumentException;
use Socialbox\Abstracts\Method;
use Socialbox\Classes\Validator;
use Socialbox\Enums\StandardError;
use Socialbox\Exceptions\Standard\InvalidRpcArgumentException;
use Socialbox\Exceptions\Standard\MissingRpcArgumentException;
@ -16,7 +17,7 @@
use Socialbox\Socialbox;
use Symfony\Component\Uid\Uuid;
class ResolvePeerSignature extends Method
class ResolveSignature extends Method
{
/**
@ -30,30 +31,17 @@
throw new MissingRpcArgumentException('peer');
}
if(!$rpcRequest->containsParameter('uuid'))
if(!$rpcRequest->containsParameter('signature_uuid'))
{
throw new MissingRpcArgumentException('uuid');
throw new MissingRpcArgumentException('signature_uuid');
}
elseif(!Validator::validateUuid($rpcRequest->getParameter('signature_uuid')))
{
throw new InvalidRpcArgumentException('signature_uuid', 'Invalid UUID V4');
}
try
{
$uuid = Uuid::fromString($rpcRequest->getParameter('uuid'));
}
catch(InvalidArgumentException $e)
{
throw new InvalidRpcArgumentException('uuid', $e);
}
// Parse the peer address
try
{
$peerAddress = PeerAddress::fromAddress($rpcRequest->getParameter('peer'));
}
catch(InvalidArgumentException $e)
{
throw new InvalidRpcArgumentException('peer', $e);
}
return $rpcRequest->produceResponse(Socialbox::resolvePeerSignature($peerAddress, $uuid->toRfc4122()));
return $rpcRequest->produceResponse(Socialbox::resolvePeerSignature(
$rpcRequest->getParameter('peer'), $rpcRequest->getParameter('signature_uuid')
));
}
}

View file

@ -14,7 +14,7 @@
use Socialbox\Objects\RpcRequest;
use Socialbox\Socialbox;
class VerifyPeerSignature extends Method
class VerifySignature extends Method
{
/**
@ -37,11 +37,6 @@
throw new InvalidRpcArgumentException('signature_uuid', 'Invalid UUID V4');
}
if(!$rpcRequest->containsParameter('signature_public_key'))
{
throw new MissingRpcArgumentException('signature_public_key');
}
if(!$rpcRequest->containsParameter('signature'))
{
throw new MissingRpcArgumentException('signature');
@ -66,27 +61,25 @@
throw new InvalidRpcArgumentException('peer', $e);
}
if($rpcRequest->containsParameter('signature_time'))
if($rpcRequest->containsParameter('time'))
{
if(!is_numeric($rpcRequest->getParameter('signature_time')))
if(!is_numeric($rpcRequest->getParameter('time')))
{
throw new InvalidRpcArgumentException('signature_time', 'Invalid timestamp, must be a Unix Timestamp');
throw new InvalidRpcArgumentException('time', 'Invalid timestamp, must be a Unix Timestamp');
}
return $rpcRequest->produceResponse(Socialbox::verifyTimedSignature(
signingPeer: $peerAddress,
signatureUuid: $rpcRequest->getParameter('signature_uuid'),
signatureKey: $rpcRequest->getParameter('signature_public_key'),
signature: $rpcRequest->getParameter('signature'),
messageHash: $rpcRequest->getParameter('sha512'),
signatureTime: (int)$rpcRequest->getParameter('signature_time')
signatureTime: (int)$rpcRequest->getParameter('time')
)->value);
}
return $rpcRequest->produceResponse(Socialbox::verifySignature(
signingPeer: $peerAddress,
signatureUuid: $rpcRequest->getParameter('signature_uuid'),
signatureKey: $rpcRequest->getParameter('signature_public_key'),
signature: $rpcRequest->getParameter('signature'),
messageHash: $rpcRequest->getParameter('sha512'),
)->value);

View file

@ -1,31 +0,0 @@
<?php
namespace Socialbox\Classes\StandardMethods\Encryption;
use Exception;
use InvalidArgumentException;
use Socialbox\Abstracts\Method;
use Socialbox\Classes\Cryptography;
use Socialbox\Classes\Validator;
use Socialbox\Enums\ReservedUsernames;
use Socialbox\Enums\StandardError;
use Socialbox\Exceptions\Standard\InvalidRpcArgumentException;
use Socialbox\Exceptions\Standard\MissingRpcArgumentException;
use Socialbox\Exceptions\Standard\StandardRpcException;
use Socialbox\Interfaces\SerializableInterface;
use Socialbox\Managers\EncryptionChannelManager;
use Socialbox\Objects\ClientRequest;
use Socialbox\Objects\PeerAddress;
use Socialbox\Objects\RpcRequest;
class EncryptionAcceptChannel extends Method
{
/**
* @inheritDoc
*/
public static function execute(ClientRequest $request, RpcRequest $rpcRequest): ?SerializableInterface
{
}
}

View file

@ -1,317 +0,0 @@
<?php
namespace Socialbox\Classes\StandardMethods\Encryption;
use Exception;
use InvalidArgumentException;
use Socialbox\Abstracts\Method;
use Socialbox\Classes\Configuration;
use Socialbox\Classes\Cryptography;
use Socialbox\Classes\Validator;
use Socialbox\Enums\ReservedUsernames;
use Socialbox\Enums\SigningKeyState;
use Socialbox\Enums\StandardError;
use Socialbox\Exceptions\DatabaseOperationException;
use Socialbox\Exceptions\Standard\InvalidRpcArgumentException;
use Socialbox\Exceptions\Standard\MissingRpcArgumentException;
use Socialbox\Exceptions\Standard\StandardRpcException;
use Socialbox\Interfaces\SerializableInterface;
use Socialbox\Managers\EncryptionChannelManager;
use Socialbox\Objects\ClientRequest;
use Socialbox\Objects\PeerAddress;
use Socialbox\Objects\RpcRequest;
use Socialbox\Objects\Standard\Signature;
use Socialbox\Socialbox;
class EncryptionCreateChannel extends Method
{
/**
* @inheritDoc
*/
public static function execute(ClientRequest $request, RpcRequest $rpcRequest): ?SerializableInterface
{
// Check the calling peer, if a server is making the request, it must be identified
// Otherwise, we assume the authenticated user is the calling peer
// But a server must provide a UUID. This is to prevent a user from creating a channel with a UUID
$callingPeer = self::getCallingPeer($request, $rpcRequest);
$callingPeerSignature = self::getCallingSignature($callingPeer, $rpcRequest);
$receivingPeer = self::getReceivingPeer($rpcRequest);
$receivingPeerSignature = self::getReceivingSignature($receivingPeer, $rpcRequest);
$channelUuid = self::getChannelUuid($request, $rpcRequest);
// Verify the calling encryption public key
if(!$rpcRequest->containsParameter('calling_encryption_public_key'))
{
throw new MissingRpcArgumentException('calling_encryption_public_key');
}
if(!Cryptography::validatePublicEncryptionKey($rpcRequest->getParameter('calling_encryption_public_key')))
{
throw new InvalidRpcArgumentException('calling_encryption_public_key', 'Invalid calling encryption public key');
}
// Transport Algorithm Validation
if(!$rpcRequest->containsParameter('transport_encryption_algorithm'))
{
throw new MissingRpcArgumentException('transport_encryption_algorithm');
}
if(!Cryptography::isSupportedAlgorithm($rpcRequest->getParameter('transport_encryption_algorithm')))
{
throw new InvalidRpcArgumentException('transport_encryption_algorithm', 'Unsupported Transport Encryption Algorithm');
}
// Create/Import the encryption channel
try
{
$channelUuid = EncryptionChannelManager::createChannel(
callingPeer: $callingPeer,
receivingPeer: $receivingPeer,
signatureUuid: $callingPeerSignature->getUuid(),
signingPublicKey: $callingPeerSignature->getPublicKey(),
encryptionPublicKey: $rpcRequest->getParameter('calling_encryption_public_key'),
transportEncryptionAlgorithm: $rpcRequest->getParameter('transport_encryption_algorithm'),
uuid: $channelUuid
);
}
catch (DatabaseOperationException $e)
{
throw new StandardRpcException('Failed to create the encryption channel', StandardError::INTERNAL_SERVER_ERROR, $e);
}
// If the receiving peer resides on an external server, then we need to tell the external server
// about the encryption channel so that the receiving peer can see it.
if($receivingPeer->getDomain() !== Configuration::getInstanceConfiguration()->getDomain())
{
$rpcClient = Socialbox::getExternalSession($receivingPeer->getDomain());
}
return $rpcRequest->produceResponse($channelUuid);
}
/**
* Returns the PeerAddress of the calling peer, if a server is making a request then the server must provide
* both the UUID of the encryption channel and the PeerAddress of the calling peer to prevent UUID conflicts
*
* Otherwise, the calling peer is assumed to be the authenticated user and no UUID is required
*
* @param ClientRequest $request The full client request
* @param RpcRequest $rpcRequest The focused RPC request
* @return PeerAddress The calling peer
* @throws StandardRpcException If the calling peer cannot be resolved
*/
private static function getCallingPeer(ClientRequest $request, RpcRequest $rpcRequest): PeerAddress
{
if($request->getIdentifyAs() !== null)
{
try
{
// Prevent UUID conflicts if the server is trying to use an UUID that already exists on this server
if (EncryptionChannelManager::channelExists($rpcRequest->getParameter('uuid')))
{
throw new StandardRpcException('UUID Conflict, a channel with this UUID already exists', StandardError::UUID_CONFLICT);
}
}
catch (DatabaseOperationException $e)
{
throw new StandardRpcException('Failed to resolve channel UUID', StandardError::INTERNAL_SERVER_ERROR, $e);
}
if($request->getIdentifyAs()->getUsername() == ReservedUsernames::HOST)
{
throw new StandardRpcException('The identifier cannot be a host', StandardError::BAD_REQUEST);
}
if($request->getIdentifyAs()->getDomain() !== Configuration::getInstanceConfiguration()->getDomain())
{
Socialbox::resolvePeer($request->getIdentifyAs());
}
return $request->getIdentifyAs();
}
try
{
return PeerAddress::fromAddress($request->getPeer()->getAddress());
}
catch(StandardRpcException $e)
{
throw $e;
}
catch(Exception $e)
{
throw new StandardRpcException('The calling peer cannot be resolved', StandardError::INTERNAL_SERVER_ERROR, $e);
}
}
/**
* Resolves and returns the calling peer's signing key, if the calling peer is coming from an external server
* then the signature returned is the resolved signature from the external server, otherwise the signature
* is locally resolved and returned
*
* @param PeerAddress $callingPeer The calling peer
* @param RpcRequest $rpcRequest The focused RPC request
* @return Signature The resolved signing key
* @throws InvalidRpcArgumentException If one or more RPC parameters are invalid
* @throws MissingRpcArgumentException If one or more RPC parameters are missing
* @throws StandardRpcException If the calling signature cannot be resolved
*/
private static function getCallingSignature(PeerAddress $callingPeer, RpcRequest $rpcRequest): Signature
{
// Caller signature verification
if(!$rpcRequest->containsParameter('calling_signature_uuid'))
{
throw new MissingRpcArgumentException('calling_signature_uuid');
}
if(!Validator::validateUuid($rpcRequest->getParameter('calling_signature_uuid')))
{
throw new InvalidRpcArgumentException('calling_signature_uuid', 'Invalid UUID V4');
}
if(!$rpcRequest->containsParameter('calling_signature_public_key'))
{
throw new MissingRpcArgumentException('calling_signature_public_key');
}
if(!Cryptography::validatePublicSigningKey($rpcRequest->getParameter('calling_signature_public_key')))
{
throw new InvalidRpcArgumentException('calling_signature_public_key', 'Invalid Public Key');
}
// Resolve the signature
$resolvedCallingSignature = Socialbox::resolvePeerSignature($callingPeer, $rpcRequest->getParameter('calling_signature_uuid'));
if($resolvedCallingSignature->getPublicKey() !== $rpcRequest->getParameter('calling_signature_public_key'))
{
throw new InvalidRpcArgumentException('calling_signature_public_key', 'Public signing key of the calling peer does not match the resolved signature');
}
if($resolvedCallingSignature->getState() === SigningKeyState::EXPIRED)
{
throw new StandardRpcException('The public signing key of the calling peer has expired', StandardError::EXPIRED);
}
$resolvedSignature = Socialbox::resolvePeerSignature($callingPeer, $rpcRequest->getParameter('calling_signature_uuid'));
if($resolvedSignature === null)
{
throw new StandardRpcException('The calling peer signature could not be resolved', StandardError::NOT_FOUND);
}
return $resolvedSignature;
}
/**
* Returns the PeerAddress of the receiving peer, if the receiving peer is from an external server then the
* receiving peer is resolved and returned, otherwise the receiving peer is locally resolved and returned
*
* @param RpcRequest $rpcRequest The focused RPC request
* @return PeerAddress The receiving peer
* @throws InvalidRpcArgumentException If one or more RPC parameters are invalid
* @throws MissingRpcArgumentException If one or more RPC parameters are missing
* @throws StandardRpcException If the receiving peer cannot be resolved
*/
private static function getReceivingPeer(RpcRequest $rpcRequest): PeerAddress
{
if(!$rpcRequest->containsParameter('receiving_peer'))
{
throw new MissingRpcArgumentException('receiving_peer');
}
try
{
$receivingPeer = PeerAddress::fromAddress($rpcRequest->getParameter('receiving_peer'));
}
catch(InvalidArgumentException $e)
{
throw new InvalidRpcArgumentException('receiving_peer', $e);
}
if($receivingPeer->getUsername() == ReservedUsernames::HOST)
{
throw new InvalidRpcArgumentException('receiving_peer', 'Hosts cannot receive channels');
}
// Resolve the receiving peer if it's from an external server
if($receivingPeer->getDomain() !== Configuration::getInstanceConfiguration()->getDomain())
{
Socialbox::resolvePeer($receivingPeer);
}
return $receivingPeer;
}
/**
* @param PeerAddress $receivingPeer
* @param RpcRequest $rpcRequest
* @return Signature
* @throws InvalidRpcArgumentException
* @throws MissingRpcArgumentException
* @throws StandardRpcException
*/
private static function getReceivingSignature(PeerAddress $receivingPeer, RpcRequest $rpcRequest): Signature
{
// Receiving signature verification
if(!$rpcRequest->containsParameter('receiving_signature_uuid'))
{
throw new MissingRpcArgumentException('receiving_signature_uuid');
}
if(!Validator::validateUuid($rpcRequest->getParameter('receiving_signature_uuid')))
{
throw new InvalidRpcArgumentException('receiving_signature_uuid', 'Invalid UUID V4');
}
if(!$rpcRequest->containsParameter('receiving_signature_public_key'))
{
throw new MissingRpcArgumentException('receiving_signature_public_key');
}
if(!Cryptography::validatePublicSigningKey($rpcRequest->getParameter('receiving_signature_public_key')))
{
throw new InvalidRpcArgumentException('receiving_signature_public_key', 'Invalid Public Key');
}
// Resolve the signature
$resolvedReceivingSignature = Socialbox::resolvePeerSignature($receivingPeer, $rpcRequest->getParameter('receiving_signature_uuid'));
if($resolvedReceivingSignature->getPublicKey() !== $rpcRequest->getParameter('receiving_signature_public_key'))
{
throw new InvalidRpcArgumentException('receiving_signature_public_key', 'Public signing key of the receiving peer does not match the resolved signature');
}
if($resolvedReceivingSignature->getState() === SigningKeyState::EXPIRED)
{
throw new StandardRpcException('The public signing key of the receiving peer has expired', StandardError::EXPIRED);
}
$resolvedSignature = Socialbox::resolvePeerSignature($receivingPeer, $rpcRequest->getParameter('receiving_signature_uuid'));
if($resolvedSignature === null)
{
throw new StandardRpcException('The receiving peer signature could not be resolved', StandardError::NOT_FOUND);
}
return $resolvedSignature;
}
/**
* @param ClientRequest $request
* @param RpcRequest $rpcRequest
* @return string|null
* @throws InvalidRpcArgumentException
* @throws MissingRpcArgumentException
*/
private static function getChannelUuid(ClientRequest $request, RpcRequest $rpcRequest): ?string
{
if($request->getIdentifyAs() !== null)
{
if(!$rpcRequest->containsParameter('uuid'))
{
throw new MissingRpcArgumentException('uuid');
}
if(!Validator::validateUuid($rpcRequest->getParameter('uuid')))
{
throw new InvalidRpcArgumentException('uuid', 'Invalid UUID V4');
}
if(EncryptionChannelManager::channelExists($rpcRequest->getParameter('uuid')))
{
throw new StandardRpcException('UUID Conflict, a channel with this UUID already exists', StandardError::UUID_CONFLICT);
}
return $rpcRequest->getParameter('uuid');
}
return null;
}
}

View file

@ -16,8 +16,11 @@
use Socialbox\Classes\StandardMethods\Core\GetSessionState;
use Socialbox\Classes\StandardMethods\Core\Ping;
use Socialbox\Classes\StandardMethods\Core\ResolvePeer;
use Socialbox\Classes\StandardMethods\Core\ResolvePeerSignature;
use Socialbox\Classes\StandardMethods\Core\VerifyPeerSignature;
use Socialbox\Classes\StandardMethods\Core\ResolveSignature;
use Socialbox\Classes\StandardMethods\Core\VerifySignature;
use Socialbox\Classes\StandardMethods\Encryption\EncryptionAcceptChannel;
use Socialbox\Classes\StandardMethods\Encryption\EncryptionCloseChannel;
use Socialbox\Classes\StandardMethods\Encryption\EncryptionCreateChannel;
use Socialbox\Classes\StandardMethods\ServerDocuments\AcceptCommunityGuidelines;
use Socialbox\Classes\StandardMethods\ServerDocuments\AcceptPrivacyPolicy;
use Socialbox\Classes\StandardMethods\ServerDocuments\AcceptTermsOfService;
@ -127,14 +130,6 @@
// MISC
case GET_STATE = 'getState';
// End-to-End channels for communication purposes
case END_TO_END_CREATE_REQUEST = 'e2eCreateRequest';
case END_TO_END_GET_REQUESTS = 'e2eGetRequests';
case END_TO_END_ACCEPT_REQUEST = 'e2eAcceptRequest';
case END_TO_END_REJECT_REQUEST = 'e2eRejectRequest';
case END_TO_END_GET_CHANNELS = 'e2eGetChannels';
case END_TO_END_CLOSE_CHANNEL = 'e2eCloseChannel';
// Messaging methods
case MESSAGES_GET_INBOX = 'messagesGetInbox';
case MESSAGES_GET_UNTRUSTED = 'messagesGetUntrusted';
@ -189,8 +184,8 @@
self::GET_SESSION_STATE => GetSessionState::execute($request, $rpcRequest),
self::PING => Ping::execute($request, $rpcRequest),
self::RESOLVE_PEER => ResolvePeer::execute($request, $rpcRequest),
self::RESOLVE_PEER_SIGNATURE => ResolvePeerSignature::execute($request, $rpcRequest),
self::VERIFY_PEER_SIGNATURE => VerifyPeerSignature::execute($request, $rpcRequest),
self::RESOLVE_PEER_SIGNATURE => ResolveSignature::execute($request, $rpcRequest),
self::VERIFY_PEER_SIGNATURE => VerifySignature::execute($request, $rpcRequest),
// Server Document Methods
self::ACCEPT_PRIVACY_POLICY => AcceptPrivacyPolicy::execute($request, $rpcRequest),
@ -235,18 +230,13 @@
* Checks if the access method is allowed for the given client request.
*
* @param ClientRequest $clientRequest The client request instance to check access against.
* @return void
* @throws DatabaseOperationException If an error occurs while checking the database for session information.
* @throws StandardRpcException If the method is not allowed for the given client request.
* @return bool
*/
public function checkAccess(ClientRequest $clientRequest): void
public function checkAccess(ClientRequest $clientRequest): bool
{
if(in_array($this, self::getAllowedMethods($clientRequest)))
{
return;
}
throw new StandardRpcException(StandardError::METHOD_NOT_ALLOWED->getMessage(), StandardError::METHOD_NOT_ALLOWED);
return in_array($this, self::getAllowedMethods($clientRequest));
}
/**

View file

@ -5,32 +5,32 @@
enum SignatureVerificationStatus : string
{
/**
* The provided signature does not match the expected signature.
* Returned if the signature is invalid
*/
case INVALID = 'INVALID';
/**
* The provided signature was valid, but the public key used to verify the signature was not the expected public key.
* Returned if one or more of the parameters are invalid resulting in a failure to verify the signature
*/
case PUBLIC_KEY_MISMATCH = 'PUBLIC_KEY_MISMATCH';
case ERROR = 'ERROR';
/**
* The provided signature was valid, but the UUID used to verify the signature was not the expected UUID.
* Returned if the signing key is not found
*/
case UUID_MISMATCH = 'UUID_MISMATCH';
case NOT_FOUND = 'NOT_FOUND';
/**
* The provided signature was valid, but the signing key has expired.
* Returned if the signature has expired
*/
case EXPIRED = 'EXPIRED';
/**
* The provided signature was valid but unable to be verified against the peer's known public key.
* Returned if there was an error while trying to resolve the signature locally or externally
*/
case UNVERIFIED = 'UNVERIFIED';
case RESOLUTION_ERROR = 'RESOLUTION_ERROR';
/**
* The provided signature was valid and verified locally and against the peer's known public key successfully.
* Returned if the signature has been successfully verified
*/
case VERIFIED = 'VERIFIED';
}

View file

@ -1,5 +1,6 @@
<?php
namespace Socialbox\Managers;
use InvalidArgumentException;
@ -14,7 +15,6 @@
use Socialbox\Objects\Database\ChannelMessageRecord;
use Socialbox\Objects\Database\EncryptionChannelRecord;
use Socialbox\Objects\PeerAddress;
use Socialbox\Objects\Standard\EncryptionChannelMessage;
class EncryptionChannelManager
{
@ -24,14 +24,13 @@
* @param PeerAddress|string $callingPeer The peer that is creating the channel.
* @param PeerAddress|string $receivingPeer The peer that is receiving the channel.
* @param string $signatureUuid The UUID of the signature used to create the channel.
* @param string $signingPublicKey The public key used for signing.
* @param string $encryptionPublicKey The public key used for encryption.
* @param string $transportEncryptionAlgorithm The algorithm used for transport encryption.
* @return string The UUID of the created channel.
* @throws DatabaseOperationException If an error occurs while creating the channel.
*/
public static function createChannel(PeerAddress|string $callingPeer, PeerAddress|string $receivingPeer,
string $signatureUuid, string $signingPublicKey, string $encryptionPublicKey, string $transportEncryptionAlgorithm,
string $signatureUuid, string $encryptionPublicKey, string $transportEncryptionAlgorithm,
?string $uuid=null
): string
{
@ -45,10 +44,6 @@
$receivingPeer = PeerAddress::fromAddress($receivingPeer);
}
if(!Cryptography::validatePublicSigningKey($signingPublicKey))
{
throw new InvalidArgumentException('Invalid signing public key provided');
}
if(!Cryptography::validatePublicEncryptionKey($encryptionPublicKey))
{
@ -68,12 +63,11 @@
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 = Database::getConnection()->prepare('INSERT INTO encryption_channels (uuid, calling_peer, calling_signature_uuid, calling_encryption_public_key, receiving_peer, transport_encryption_algorithm) VALUES (:uuid, :calling_peer, :calling_signature_uuid, :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);
@ -96,7 +90,6 @@
* @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
{
@ -137,7 +130,6 @@
* @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
{
@ -180,7 +172,6 @@
* @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
{
@ -221,7 +212,6 @@
* @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
{
@ -281,23 +271,19 @@
*
* @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
public static function acceptChannel(string $channelUuid, string $signatureUuid, string $encryptionPublicKey, 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');
$stmt = Database::getConnection()->prepare('UPDATE encryption_channels SET state=:state, receiving_signature_uuid=:receiving_signature_uuid, 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();
@ -314,7 +300,6 @@
* @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
{
@ -343,7 +328,7 @@
*
* @param string $channelUuid The UUID of the channel to delete.
* @return void
*@throws DatabaseOperationException If an error occurs while deleting the channel.
* @throws DatabaseOperationException If an error occurs while deleting the channel.
*/
public static function deleteChannel(string $channelUuid): void
{
@ -503,7 +488,6 @@
* @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.
* @throws \DateMalformedStringException If the created date is not a valid date string.
*/
public static function getData(string $channelUuid, string $messageUuid): ?ChannelMessageRecord
{
@ -531,23 +515,29 @@
/**
* Imports the specified message data into the database.
*
* @param EncryptionChannelMessage|ChannelMessageRecord $message The message data to import.
* @param ChannelMessageRecord $message The message data to import.
* @throws DatabaseOperationException If an error occurs while importing the message.
*/
public static function importData(EncryptionChannelMessage|ChannelMessageRecord $message): void
public static function importData(ChannelMessageRecord $message): void
{
$data = $message->toArray();
try
{
$stmt = Database::getConnection()->prepare('INSERT INTO channel_com (uuid, channel_uuid, recipient, message, signature, received, timestamp) VALUES (:uuid, :channel_uuid, :recipient, :message, :signature, :received, :timestamp)');
$stmt->bindParam(':uuid', $data['uuid']);
$stmt->bindParam(':channel_uuid', $data['channel_uuid']);
$stmt->bindParam(':recipient', $data['recipient']);
$stmt->bindParam(':message', $data['message']);
$stmt->bindParam(':signature', $data['signature']);
$stmt->bindParam(':received', $data['received']);
$stmt->bindParam(':timestamp', $data['timestamp']);
$uuid = $message->getUuid();
$stmt->bindParam(':uuid', $uuid);
$channelUuid = $message->getChannelUuid();
$stmt->bindParam(':channel_uuid', $channelUuid);
$recipient = $message->getRecipient()->value;
$stmt->bindParam(':recipient', $recipient);
$messageData = $message->getMessage();
$stmt->bindParam(':message', $messageData);
$signature = $message->getSignature();
$stmt->bindParam(':signature', $signature);
$received = $message->isReceived() ? 1 : 0;
$stmt->bindParam(':received', $received);
$timestamp = $message->getTimestamp();
$stmt->bindParam(':timestamp', $timestamp);
$stmt->execute();
}
catch(PDOException $e)

View file

@ -34,8 +34,8 @@
try
{
$statement = Database::getConnection()->prepare('SELECT COUNT(*) FROM peers WHERE username=?');
$statement->bindParam(1, $username);
$statement = Database::getConnection()->prepare("SELECT COUNT(*) FROM peers WHERE username=:username AND server='host'");
$statement->bindParam(':username', $username);
$statement->execute();
$result = $statement->fetchColumn();
@ -163,9 +163,9 @@
try
{
$statement = Database::getConnection()->prepare('SELECT * FROM peers WHERE username=? AND server=?');
$statement = Database::getConnection()->prepare('SELECT * FROM peers WHERE username=:username AND server=:server');
$username = $address->getUsername();
$statement->bindParam(1, $username);
$statement->bindParam(':username', $username);
$server = $address->getDomain();
// Convert to 'host' if the domain is the same as the server's host
@ -174,7 +174,7 @@
$server = 'host';
}
$statement->bindParam(2, $server);
$statement->bindParam(':server', $server);
$statement->execute();
$result = $statement->fetch(PDO::FETCH_ASSOC);

View file

@ -0,0 +1,129 @@
<?php
namespace Socialbox\Objects\Client;
use Socialbox\Interfaces\SerializableInterface;
use Socialbox\Objects\PeerAddress;
class EncryptionChannelSecret implements SerializableInterface
{
private string $channelUuid;
private PeerAddress $receiver;
private string $signatureUuid;
private string $publicEncryptionKey;
private string $privateEncryptionKey;
private string $transportEncryptionAlgorithm;
private ?string $transportEncryptionKey;
/**
* Public constructor
*
* @param array $data The data to create the object
*/
public function __construct(array $data)
{
$this->channelUuid = $data['uuid'];
$this->receiver = PeerAddress::fromAddress($data['receiver']);
$this->signatureUuid = $data['signature_uuid'];
$this->publicEncryptionKey = $data['public_encryption_key'];
$this->privateEncryptionKey = $data['private_encryption_key'];
$this->transportEncryptionAlgorithm = $data['transport_encryption_algorithm'];
$this->transportEncryptionKey = $data['transport_encryption_key'] ?? null;
}
/**
* Returns the UUID of the key pair
*
* @return string The UUID of the key pair
*/
public function getChannelUuid(): string
{
return $this->channelUuid;
}
/**
* @return PeerAddress
*/
public function getReceiver(): PeerAddress
{
return $this->receiver;
}
/**
* Returns the UUID of the signature
*
* @return string The UUID of the signature
*/
public function getSignatureUuid(): string
{
return $this->signatureUuid;
}
/**
* Returns the public key of the key pair
*
* @return string The public key of the key pair
*/
public function getPublicEncryptionKey(): string
{
return $this->publicEncryptionKey;
}
/**
* Returns the private key of the key pair
*
* @return string The private key of the key pair
*/
public function getPrivateEncryptionKey(): string
{
return $this->privateEncryptionKey;
}
/**
* @return string
*/
public function getTransportEncryptionAlgorithm(): string
{
return $this->transportEncryptionAlgorithm;
}
/**
* @return string|null
*/
public function getTransportEncryptionKey(): ?string
{
return $this->transportEncryptionKey;
}
/**
* @param string|null $transportEncryptionKey
*/
public function setTransportEncryptionKey(?string $transportEncryptionKey): void
{
$this->transportEncryptionKey = $transportEncryptionKey;
}
/**
* @inheritDoc
*/
public static function fromArray(array $data): EncryptionChannelSecret
{
return new self($data);
}
/**
* @inheritDoc
*/
public function toArray(): array
{
return [
'uuid' => $this->channelUuid,
'receiver' => $this->receiver->getAddress(),
'signature_uuid' => $this->signatureUuid,
'public_key' => $this->publicEncryptionKey,
'private_key' => $this->privateEncryptionKey,
'transport_encryption_algorithm' => $this->transportEncryptionAlgorithm,
'transport_encryption_key' => $this->transportEncryptionKey
];
}
}

View file

@ -29,6 +29,10 @@
* @var SignatureKeyPair[]
*/
private array $signingKeys;
/**
* @var EncryptionChannelSecret[]
*/
private array $encryptionChannelSecrets;
/**
* Constructor method to initialize class properties from the provided data array.
@ -62,6 +66,7 @@
$this->serverTransportEncryptionKey = $data['server_transport_encryption_key'];
$this->defaultSigningKey = $data['default_signing_key'] ?? null;
$this->signingKeys = array_map(fn($key) => SignatureKeyPair::fromArray($key), $data['signing_keys']);
$this->encryptionChannelSecrets = array_map(fn($key) => EncryptionChannelSecret::fromArray($key), $data['encryption_channel_secrets']);
}
/**
@ -234,6 +239,60 @@
return $this->signingKeys;
}
/**
* Retrieves the encrypted channel keys associated with the current instance.
*
* @return EncryptionChannelSecret[] The encrypted channel keys.
*/
public function getEncryptionChannelSecrets(): array
{
return $this->encryptionChannelSecrets;
}
/**
* Retrieves the signing key associated with the provided UUID.
*
* @param string $uuid The UUID of the signing key.
* @return SignatureKeyPair|null The signing key.
*/
public function getEncryptionChannelSecret(string $uuid): ?EncryptionChannelSecret
{
return $this->encryptionChannelSecrets[$uuid] ?? null;
}
/**
* Adds a new signing key to the current instance.
*
* @param EncryptionChannelSecret $key The signing key to add.
* @return void
*/
public function addEncryptionChannelSecret(EncryptionChannelSecret $key): void
{
$this->encryptionChannelSecrets[$key->getChannelUuid()] = $key;
}
/**
* Removes the signing key associated with the provided UUID.
*
* @param string $uuid The UUID of the signing key to remove.
* @return void
*/
public function removeEncryptionChannelSecret(string $uuid): void
{
unset($this->encryptionChannelSecrets[$uuid]);
}
/**
* Checks if a signing key exists for the provided UUID.
*
* @param string $uuid The UUID of the signing key.
* @return bool True if the signing key exists, false otherwise.
*/
public function encryptionChannelSecretExists(string $uuid): bool
{
return isset($this->encryptionChannelSecrets[$uuid]);
}
/**
* @inheritDoc
*/
@ -256,7 +315,8 @@
'client_transport_encryption_key' => $this->clientTransportEncryptionKey,
'server_transport_encryption_key' => $this->serverTransportEncryptionKey,
'default_signing_key' => $this->defaultSigningKey,
'signing_keys' => array_map(fn($key) => $key->toArray(), $this->signingKeys)
'signing_keys' => array_map(fn($key) => $key->toArray(), $this->signingKeys),
'encryption_channel_secrets' => array_map(fn($key) => $key->toArray(), $this->encryptionChannelSecrets),
];
}

View file

@ -170,7 +170,6 @@
*
* @return SessionRecord|null Returns the associated SessionRecord if the session UUID exists, or null if no session UUID is set.
* @throws DatabaseOperationException Thrown if an error occurs while retrieving the session.
* @throws StandardRpcException Thrown if the session UUID is invalid.
*/
public function getSession(): ?SessionRecord
{
@ -187,7 +186,6 @@
*
* @return PeerDatabaseRecord|null Returns the associated RegisteredPeerRecord if available, or null if no session exists.
* @throws DatabaseOperationException Thrown if an error occurs while retrieving the peer.
* @throws StandardRpcException Thrown if the session UUID is invalid.
*/
public function getPeer(): ?PeerDatabaseRecord
{
@ -201,6 +199,36 @@
return RegisteredPeerManager::getPeer($session->getPeerUuid());
}
/**
* Returns the Peer Database Record of the identified peer of the request
*
* @return PeerDatabaseRecord|null The Peer Database Record of the identified peer or null if not set
* @throws DatabaseOperationException Thrown if an error occurs while retrieving the peer.
*/
public function getIdentifiedAsPeer(): ?PeerDatabaseRecord
{
$identifiedAs = $this->getIdentifyAs();
if($identifiedAs === null)
{
return null;
}
return RegisteredPeerManager::getPeerByAddress($identifiedAs);
}
/**
* Returns whether the request is external or not. As in, if the request is coming from server rather than
* a client.
*
* @return bool True if the request is external, false otherwise.
* @throws DatabaseOperationException Thrown if an error occurs while retrieving the peer.
* @throws StandardRpcException Thrown if the session UUID is invalid.
*/
public function isExternal(): bool
{
return $this->getPeer()->isExternal();
}
/**
* Retrieves the signature value.
*

View file

@ -30,7 +30,6 @@
* - 'signature' (string): The signature.
* - 'received' (bool): Whether the message has been received.
* - 'timestamp' (int|string|\DateTime): The timestamp of the message.
* @throws DateMalformedStringException If the timestamp is a string that cannot be parsed.
*/
public function __construct(array $data)
{

View file

@ -17,7 +17,6 @@
private string $callingEncryptionPublicKey;
private PeerAddress $receivingPeer;
private ?string $receivingSignatureUuid;
private ?string $receivingSignaturePublicKey;
private ?string $receivingEncryptionPublicKey;
private string $transportEncryptionAlgorithm;
private ?string $transportEncryptionKey;
@ -28,7 +27,6 @@
* Public Constructor for the encryption channel record
*
* @param array $data
* @throws \DateMalformedStringException
*/
public function __construct(array $data)
{
@ -78,7 +76,6 @@
}
$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;
@ -169,16 +166,6 @@
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
*
@ -249,7 +236,6 @@
'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,

View file

@ -83,7 +83,7 @@
return false;
}
return $this->username === ReservedUsernames::HOST->value;
return $this->domain !== Configuration::getInstanceConfiguration()->getDomain();
}
/**

View file

@ -87,6 +87,26 @@
return isset($this->parameters[$parameter]);
}
/**
* Checks if the parameters exist within the RPC request
*
* @param array $parameters The parameters to check
* @param bool $nullAllowed True if the parameter value can be null, False otherwise.
* @return bool True if the parameters exist, False otherwise.
*/
public function containsParameters(array $parameters, bool $nullAllowed=false): bool
{
foreach($parameters as $parameter)
{
if(!$this->containsParameter($parameter, $nullAllowed))
{
return false;
}
}
return true;
}
/**
* Returns the parameter value from the RPC request
*

View file

@ -76,53 +76,6 @@
return $uuid;
}
/**
* Creates a new encryption channel with the given peer, using the signature UUID to identify the calling peer.
* This method is a wrapper around the encryptionCreateChannel method, and is used to simplify the process of
* creating a new encryption channel. The signature UUID is used to identify the calling peer, and the public key
* of the signature is used as the encryption key.
*
* @param PeerAddress|string $receivingPeer
* @param string $signatureUuid
* @param string $transportEncryptionAlgorithm
* @return string
* @throws CryptographyException
* @throws RpcException
*/
public function newEncryptionChannel(PeerAddress|string $receivingPeer, string $signatureUuid, string $transportEncryptionAlgorithm): string
{
if(!$this->signingKeyExists($signatureUuid))
{
throw new InvalidArgumentException('The signature UUID does not exist in the client');
}
$signature = $this->getSigningKey($signatureUuid);
if($signature === null)
{
throw new InvalidArgumentException('The signature UUID does not exist in the client');
}
$encryptionKeypair = Cryptography::generateEncryptionKeyPair();
$channelUuid = $this->encryptionCreateChannel(
receivingPeer: $receivingPeer,
signatureUUid: $signature->getUuid(),
encryptionPublicKey: $encryptionKeypair->getPublicKey(),
transportEncryptionAlgorithm: $transportEncryptionAlgorithm
);
$this->addEncryptionChannelSecret(new EncryptionChannelSecret([
'uuid' => $channelUuid,
'receiver' => $receivingPeer->getAddress(),
'signature_uuid' => $signature->getUuid(),
'public_encryption_key' => $encryptionKeypair->getPublicKey(),
'private_encryption_key' => $encryptionKeypair->getPrivateKey(),
'transport_encryption_algorithm' => $transportEncryptionAlgorithm,
'transport_encryption_key' => null
]));
return $channelUuid;
}
/**
* Adds a new peer to the AddressBook, returns True upon success or False if the contact already exists in
* the address book.
@ -459,74 +412,6 @@
)->getResponse()->getResult()) ?? SignatureVerificationStatus::INVALID;
}
public function encryptionAcceptChannel(string $channelUuid, string $signatureUuid, string $encryptionPublicKey, string $encryptedTransportEncryptionKey, null|PeerAddress|string $identifiedAs=null): true
{
if($identifiedAs instanceof PeerAddress)
{
$identifiedAs = $identifiedAs->getAddress();
}
return $this->sendRequest(
new RpcRequest(StandardMethods::ENCRYPTION_ACCEPT_CHANNEL, parameters: [
'channel_uuid' => $channelUuid,
'signature_uuid' => $signatureUuid,
'encryption_public_key' => $encryptionPublicKey,
'encrypted_transport_encryption_key' => $encryptedTransportEncryptionKey
]),
identifiedAs: $identifiedAs
)->getResponse()->getResult();
}
public function encryptionCloseChannel(string $channelUuid, null|PeerAddress|string $identifiedAs=null): true
{
if($identifiedAs instanceof PeerAddress)
{
$identifiedAs = $identifiedAs->getAddress();
}
return $this->sendRequest(
new RpcRequest(StandardMethods::ENCRYPTION_CLOSE_CHANNEL, parameters: [
'channel_uuid' => $channelUuid,
]),
identifiedAs: $identifiedAs
)->getResponse()->getResult();
}
/**
* Accepts an encryption channel request, returns True if the channel was accepted.
*
* @param PeerAddress|string $receivingPeer The address of the receiving peer that the channel is being requested to
* @param string $signatureUUid The UUID of the calling signature
* @param string $encryptionPublicKey The public key of the calling encryption key
* @param string $transportEncryptionAlgorithm The transport encryption algorithm to use
* @param string|null $identifyAs Optional. The address of the peer to identify as
* @param string|null $channelUuid Optional. If calling to an external server, the server must provide the other server the UUID to use
* @return string Returns True if the channel was accepted
* @throws RpcException Thrown if there was an error with the RPC request
*/
public function encryptionCreateChannel(
PeerAddress|string $receivingPeer, string $signatureUUid,
string $encryptionPublicKey, string $transportEncryptionAlgorithm='xchacha20',
?string $identifyAs=null, ?string $channelUuid=null
): string
{
if($receivingPeer instanceof PeerAddress)
{
$receivingPeer = $receivingPeer->getAddress();
}
return $this->sendRequest(
new RpcRequest(StandardMethods::ENCRYPTION_CREATE_CHANNEL, parameters: [
'receiving_peer' => $receivingPeer,
'signature_uuid' => $signatureUUid,
'encryption_public_key' => $encryptionPublicKey,
'transport_encryption_algorithm' => $transportEncryptionAlgorithm,
'channel_uuid' => $channelUuid
]),
identifiedAs: $identifyAs
)->getResponse()->getResult();
}
/**
* Accepts the community guidelines, returns True if the guidelines were accepted.
*

View file

@ -200,7 +200,7 @@
// Prevent the peer from identifying as the host unless it's coming from an external domain
if($clientRequest->getIdentifyAs()->getUsername() === ReservedUsernames::HOST->value)
{
self::returnError(403, StandardError::FORBIDDEN, 'Unauthorized: Not allowed to identify as the host');
self::returnError(403, StandardError::FORBIDDEN, 'Forbidden: Not allowed to identify as the host');
return;
}
}
@ -225,7 +225,7 @@
}
catch (Exception $e)
{
self::returnError(502, StandardError::RESOLUTION_FAILED, 'Conflict: Failed to resolve the host domain: ' . $e->getMessage(), $e);
self::returnError(502, StandardError::RESOLUTION_FAILED, 'Conflict: Failed to resolve incoming host, ' . $e->getMessage(), $e);
return;
}
}
@ -258,14 +258,6 @@
RegisteredPeerManager::enablePeer($peerUuid);
$registeredPeer = RegisteredPeerManager::getPeer($peerUuid);
}
else
{
// If the host is registered, but disabled, enable it
if(!$registeredPeer->isEnabled())
{
RegisteredPeerManager::enablePeer($registeredPeer->getUuid());
}
}
}
if($registeredPeer === null)
@ -355,7 +347,16 @@
return;
}
$session = $clientRequest->getSession();
try
{
$session = $clientRequest->getSession();
}
catch (DatabaseOperationException $e)
{
self::returnError(500, StandardError::INTERNAL_SERVER_ERROR, 'Failed to retrieve session', $e);
return;
}
if($session === null)
{
self::returnError(404, StandardError::SESSION_NOT_FOUND, 'Session not found');
@ -525,36 +526,41 @@
{
try
{
$peer = $clientRequest->getPeer();
$hostPeer = $clientRequest->getPeer();
}
catch (DatabaseOperationException $e)
{
self::returnError(500, StandardError::INTERNAL_SERVER_ERROR, 'Failed to resolve host peer', $e);
}
// First check if the client is identifying as the host
if($peer->getAddress() !== ReservedUsernames::HOST->value)
{
// TODO: Maybe allow user client to change identification but within an RPC method rather than the headers
self::returnError(403, StandardError::FORBIDDEN, 'Unauthorized: Not allowed to identify as a different peer');
return;
}
if($clientRequest->getIdentifyAs()->getDomain() != $)
// If for some reason the host peer was not found, this shouldn't happen.
if($hostPeer === null)
{
self::returnError(404, StandardError::INTERNAL_SERVER_ERROR, 'The host peer was not found in the system');
return;
}
// First check if the client is identifying as the host
elseif($hostPeer->getAddress() !== ReservedUsernames::HOST->value)
{
self::returnError(403, StandardError::FORBIDDEN, 'Forbidden: External servers must identify as a host');
return;
}
// Secondly, check if the peer's server belongs to another server than the server is identified as
elseif($hostPeer->getServer() !== $clientRequest->getIdentifyAs()->getDomain())
{
self::returnError(403, StandardError::FORBIDDEN, 'Forbidden: Not allowed to identify as a peer outside from the host server');
return;
}
// Synchronize the peer
try
{
self::resolvePeer($clientRequest->getIdentifyAs());
}
catch (DatabaseOperationException $e)
catch (StandardRpcException $e)
{
self::returnError(500, StandardError::INTERNAL_SERVER_ERROR, 'Failed to synchronize external peer', $e);
return;
}
catch (Exception $e)
{
throw new ResolutionException(sprintf('Failed to synchronize external peer %s: %s', $clientRequest->getIdentifyAs()->getAddress(), $e->getMessage()), $e->getCode(), $e);
throw new ResolutionException(sprintf('Failed to resolve peer %s: %s', $clientRequest->getIdentifyAs()->getAddress(), $e->getMessage()), $e->getCode(), $e);
}
}
@ -568,68 +574,76 @@
return;
}
if(count($clientRequests) === 0)
{
Logger::getLogger()->warning(sprintf('Received no RPC requests from %s', $_SERVER['REMOTE_ADDR']));
http_response_code(204);
return;
}
Logger::getLogger()->verbose(sprintf('Received %d RPC request(s) from %s', count($clientRequests), $_SERVER['REMOTE_ADDR']));
$results = [];
foreach($clientRequests as $rpcRequest)
{
$method = StandardMethods::tryFrom($rpcRequest->getMethod());
try
{
$method->checkAccess($clientRequest);
}
catch (StandardRpcException $e)
{
$response = $e->produceError($rpcRequest);
$results[] = $response->toArray();
continue;
}
if($method === false)
{
Logger::getLogger()->warning('The requested method does not exist');
$response = $rpcRequest->produceError(StandardError::RPC_METHOD_NOT_FOUND, 'The requested method does not exist');
}
else
{
try
{
Logger::getLogger()->debug(sprintf('Processing RPC request for method %s', $rpcRequest->getMethod()));
$response = $method->execute($clientRequest, $rpcRequest);
Logger::getLogger()->debug(sprintf('%s method executed successfully', $rpcRequest->getMethod()));
}
catch(StandardRpcException $e)
{
Logger::getLogger()->error('An error occurred while processing the RPC request', $e);
$response = $e->produceError($rpcRequest);
}
catch(Exception $e)
{
Logger::getLogger()->error('An internal error occurred while processing the RPC request', $e);
if(Configuration::getSecurityConfiguration()->isDisplayInternalExceptions())
{
$response = $rpcRequest->produceError(StandardError::INTERNAL_SERVER_ERROR, Utilities::throwableToString($e));
}
else
{
$response = $rpcRequest->produceError(StandardError::INTERNAL_SERVER_ERROR);
}
}
$results[] = $rpcRequest->produceError(StandardError::RPC_METHOD_NOT_FOUND, 'The requested method does not exist');
continue;
}
if($response !== null)
try
{
Logger::getLogger()->debug(sprintf('Producing response for method %s', $rpcRequest->getMethod()));
$results[] = $response->toArray();
if (!$method->checkAccess($clientRequest))
{
$results[] = $rpcRequest->produceError(StandardError::METHOD_NOT_ALLOWED, 'Insufficient access requirements to invoke the session');
continue;
}
}
catch (DatabaseOperationException $e)
{
Logger::getLogger()->error('Failed to check method access', $e);
$results[] = $rpcRequest->produceError(StandardError::INTERNAL_SERVER_ERROR, 'Failed to check method access due to an internal server error');
continue;
}
catch (StandardRpcException $e)
{
$results[] = $e->produceError($rpcRequest);
continue;
}
try
{
Logger::getLogger()->debug(sprintf('Processing RPC request for method %s', $rpcRequest->getMethod()));
$results[] = $method->execute($clientRequest, $rpcRequest);
Logger::getLogger()->debug(sprintf('%s method executed successfully', $rpcRequest->getMethod()));
}
catch(StandardRpcException $e)
{
Logger::getLogger()->error('An error occurred while processing the RPC request', $e);
$results[] = $e->produceError($rpcRequest);
}
catch(Exception $e)
{
Logger::getLogger()->error('An internal error occurred while processing the RPC request', $e);
if(Configuration::getSecurityConfiguration()->isDisplayInternalExceptions())
{
$results[] = $rpcRequest->produceError(StandardError::INTERNAL_SERVER_ERROR, Utilities::throwableToString($e));
}
else
{
$results[] = $rpcRequest->produceError(StandardError::INTERNAL_SERVER_ERROR);
}
}
}
$response = null;
$results = array_map(fn($result) => $result->toArray(), $results);
if(count($results) == 0)
{
$response = null;
http_response_code(204);
return;
}
elseif(count($results) == 1)
{
@ -640,12 +654,6 @@
$response = json_encode($results, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
}
if($response === null)
{
http_response_code(204);
return;
}
$session = $clientRequest->getSession();
try