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

Closed
netkas wants to merge 421 commits from master into dev
5 changed files with 138 additions and 44 deletions
Showing only changes of commit c380556255 - Show all commits

View file

@ -2,6 +2,7 @@
namespace Socialbox\Classes; namespace Socialbox\Classes;
use Socialbox\Enums\Options\ClientOptions;
use Socialbox\Enums\StandardHeaders; use Socialbox\Enums\StandardHeaders;
use Socialbox\Enums\Types\RequestType; use Socialbox\Enums\Types\RequestType;
use Socialbox\Exceptions\CryptographyException; use Socialbox\Exceptions\CryptographyException;
@ -60,7 +61,19 @@
// Set the initial properties // Set the initial properties
$this->peerAddress = $peerAddress; $this->peerAddress = $peerAddress;
$this->keyPair = Cryptography::generateKeyPair();
// If the username is `host` and the domain is the same as this server's domain, we use our keypair
// Essentially this is a special case for the server to contact another server
if($this->peerAddress->isHost())
{
$this->keyPair = new KeyPair(Configuration::getInstanceConfiguration()->getPublicKey(), Configuration::getInstanceConfiguration()->getPrivateKey());
}
// Otherwise we generate a random keypair
else
{
$this->keyPair = Cryptography::generateKeyPair();
}
$this->encryptionKey = Cryptography::generateEncryptionKey(); $this->encryptionKey = Cryptography::generateEncryptionKey();
// Resolve the domain and get the server's Public Key & RPC Endpoint // Resolve the domain and get the server's Public Key & RPC Endpoint
@ -97,16 +110,25 @@
{ {
$ch = curl_init(); $ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $this->rpcEndpoint); // Basic session details
curl_setopt($ch, CURLOPT_HTTPGET, true); $headers = [
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
StandardHeaders::REQUEST_TYPE->value . ': ' . RequestType::INITIATE_SESSION->value, StandardHeaders::REQUEST_TYPE->value . ': ' . RequestType::INITIATE_SESSION->value,
StandardHeaders::CLIENT_NAME->value . ': ' . self::CLIENT_NAME, StandardHeaders::CLIENT_NAME->value . ': ' . self::CLIENT_NAME,
StandardHeaders::CLIENT_VERSION->value . ': ' . self::CLIENT_VERSION, StandardHeaders::CLIENT_VERSION->value . ': ' . self::CLIENT_VERSION,
StandardHeaders::PUBLIC_KEY->value . ': ' . $this->keyPair->getPublicKey(),
StandardHeaders::IDENTIFY_AS->value . ': ' . $this->peerAddress->getAddress(), StandardHeaders::IDENTIFY_AS->value . ': ' . $this->peerAddress->getAddress(),
]); ];
// If we're not connecting as the host, we need to provide our public key
// Otherwise, the server will obtain the public key itself from DNS records rather than trusting the client
if(!$this->peerAddress->isHost())
{
$headers[] = StandardHeaders::PUBLIC_KEY->value . ': ' . $this->keyPair->getPublicKey();
}
curl_setopt($ch, CURLOPT_URL, $this->rpcEndpoint);
curl_setopt($ch, CURLOPT_HTTPGET, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
$response = curl_exec($ch); $response = curl_exec($ch);

View file

@ -0,0 +1,8 @@
<?php
namespace Socialbox\Enums\Options;
enum ClientOptions
{
case EXTERNAL_CONNECTION;
}

View file

@ -3,6 +3,7 @@
namespace Socialbox\Objects; namespace Socialbox\Objects;
use InvalidArgumentException; use InvalidArgumentException;
use Socialbox\Classes\Configuration;
use Socialbox\Classes\Validator; use Socialbox\Classes\Validator;
use Socialbox\Enums\ReservedUsernames; use Socialbox\Enums\ReservedUsernames;
@ -67,7 +68,7 @@
*/ */
public function isHost(): bool public function isHost(): bool
{ {
return $this->username === ReservedUsernames::HOST->value; return $this->username === ReservedUsernames::HOST->value && $this->domain === Configuration::getInstanceConfiguration()->getDomain();
} }
/** /**

View file

@ -23,6 +23,7 @@
* *
* @param string|PeerAddress $peerAddress The address of the peer to connect to. * @param string|PeerAddress $peerAddress The address of the peer to connect to.
* @param ExportedSession|null $exportedSession Optional. The exported session to use for communication. * @param ExportedSession|null $exportedSession Optional. The exported session to use for communication.
* @param array $options Optional. Additional options to pass to the client.
* @throws CryptographyException If the public key is invalid. * @throws CryptographyException If the public key is invalid.
* @throws ResolutionException If the domain cannot be resolved. * @throws ResolutionException If the domain cannot be resolved.
* @throws RpcException If the RPC request fails. * @throws RpcException If the RPC request fails.

View file

@ -7,8 +7,10 @@
use Socialbox\Classes\Configuration; use Socialbox\Classes\Configuration;
use Socialbox\Classes\Cryptography; use Socialbox\Classes\Cryptography;
use Socialbox\Classes\Logger; use Socialbox\Classes\Logger;
use Socialbox\Classes\ServerResolver;
use Socialbox\Classes\Utilities; use Socialbox\Classes\Utilities;
use Socialbox\Classes\Validator; use Socialbox\Classes\Validator;
use Socialbox\Enums\ReservedUsernames;
use Socialbox\Enums\SessionState; use Socialbox\Enums\SessionState;
use Socialbox\Enums\StandardError; use Socialbox\Enums\StandardError;
use Socialbox\Enums\StandardHeaders; use Socialbox\Enums\StandardHeaders;
@ -68,6 +70,58 @@
} }
} }
/**
* Validates the headers in an initialization request to ensure that all
* required information is present and properly formatted. This includes
* checking for headers such as Client Name, Client Version, Public Key,
* and Identify-As, as well as validating the Identify-As header value.
* If any validation fails, a corresponding HTTP response code and message
* are returned.
*
* @param ClientRequest $clientRequest The client request containing headers to validate.
*
* @return bool Returns true if all required headers are valid, otherwise false.
*/
private static function validateInitHeaders(ClientRequest $clientRequest): bool
{
if(!$clientRequest->getClientName())
{
http_response_code(400);
print('Missing required header: ' . StandardHeaders::CLIENT_NAME->value);
return false;
}
if(!$clientRequest->getClientVersion())
{
http_response_code(400);
print('Missing required header: ' . StandardHeaders::CLIENT_VERSION->value);
return false;
}
if(!$clientRequest->headerExists(StandardHeaders::PUBLIC_KEY))
{
http_response_code(400);
print('Missing required header: ' . StandardHeaders::PUBLIC_KEY->value);
return false;
}
if(!$clientRequest->headerExists(StandardHeaders::IDENTIFY_AS))
{
http_response_code(400);
print('Missing required header: ' . StandardHeaders::IDENTIFY_AS->value);
return false;
}
if(!Validator::validatePeerAddress($clientRequest->getHeader(StandardHeaders::IDENTIFY_AS)))
{
http_response_code(400);
print('Invalid Identify-As header: ' . $clientRequest->getHeader(StandardHeaders::IDENTIFY_AS));
return false;
}
return true;
}
/** /**
* Processes a client request to initiate a session. Validates required headers, * Processes a client request to initiate a session. Validates required headers,
* ensures the peer is authorized and enabled, and creates a new session UUID * ensures the peer is authorized and enabled, and creates a new session UUID
@ -80,58 +134,66 @@
*/ */
private static function handleInitiateSession(ClientRequest $clientRequest): void private static function handleInitiateSession(ClientRequest $clientRequest): void
{ {
if(!self::validateInitHeaders($clientRequest))
if(!$clientRequest->getClientName())
{ {
http_response_code(400);
print('Missing required header: ' . StandardHeaders::CLIENT_NAME->value);
return; return;
} }
if(!$clientRequest->getClientVersion()) // We always accept the client's public key at first
{ $publicKey = $clientRequest->getHeader(StandardHeaders::PUBLIC_KEY);
http_response_code(400);
print('Missing required header: ' . StandardHeaders::CLIENT_VERSION->value);
return;
}
if(!$clientRequest->headerExists(StandardHeaders::PUBLIC_KEY))
{
http_response_code(400);
print('Missing required header: ' . StandardHeaders::PUBLIC_KEY->value);
return;
}
if(!$clientRequest->headerExists(StandardHeaders::IDENTIFY_AS))
{
http_response_code(400);
print('Missing required header: ' . StandardHeaders::IDENTIFY_AS->value);
return;
}
if(!Validator::validatePeerAddress($clientRequest->getHeader(StandardHeaders::IDENTIFY_AS)))
{
http_response_code(400);
print('Invalid Identify-As header: ' . $clientRequest->getHeader(StandardHeaders::IDENTIFY_AS));
return;
}
// If the peer is identifying as the same domain // If the peer is identifying as the same domain
if($clientRequest->getIdentifyAs()->getDomain() === Configuration::getInstanceConfiguration()->getDomain()) if($clientRequest->getIdentifyAs()->getDomain() === Configuration::getInstanceConfiguration()->getDomain())
{ {
// Prevent the peer from identifying as the host unless it's coming from an external domain // Prevent the peer from identifying as the host unless it's coming from an external domain
if($clientRequest->getIdentifyAs()->getUsername() === 'host') if($clientRequest->getIdentifyAs()->getUsername() === ReservedUsernames::HOST->value)
{ {
http_response_code(403); http_response_code(403);
print('Unauthorized: The requested peer is not allowed to identify as the host'); print('Unauthorized: The requested peer is not allowed to identify as the host');
return; return;
} }
} }
// If the peer is identifying as an external domain
else else
{ {
http_response_code(400); // Only allow the host to identify as an external peer
print('External domains are not supported yet'); if($clientRequest->getIdentifyAs()->getUsername() !== ReservedUsernames::HOST->value)
return; {
http_response_code(403);
print('Unauthorized: The requested peer is not allowed to identify as an external peer');
return;
}
try
{
// We need to obtain the public key of the host, since we can't trust the client
$resolvedServer = ServerResolver::resolveDomain($clientRequest->getIdentifyAs()->getDomain());
// Override the public key with the resolved server's public key
$publicKey = $resolvedServer->getPublicKey();
}
catch (Exceptions\ResolutionException $e)
{
Logger::getLogger()->error('Failed to resolve the host domain', $e);
http_response_code(409);
print('Conflict: Failed to resolve the host domain: ' . $e->getMessage());
return;
}
catch (Exception $e)
{
Logger::getLogger()->error('An internal error occurred while resolving the host domain', $e);
http_response_code(500);
if(Configuration::getSecurityConfiguration()->isDisplayInternalExceptions())
{
print(Utilities::throwableToString($e));
}
else
{
print('An internal error occurred');
}
return;
}
} }
try try
@ -165,7 +227,7 @@
} }
// Create the session UUID // Create the session UUID
$sessionUuid = SessionManager::createSession($clientRequest->getHeader(StandardHeaders::PUBLIC_KEY), $registeredPeer, $clientRequest->getClientName(), $clientRequest->getClientVersion()); $sessionUuid = SessionManager::createSession($publicKey, $registeredPeer, $clientRequest->getClientName(), $clientRequest->getClientVersion());
http_response_code(201); // Created http_response_code(201); // Created
print($sessionUuid); // Return the session UUID print($sessionUuid); // Return the session UUID
} }