Add external session management and support for remote servers

This commit is contained in:
netkas 2025-01-10 14:58:24 -05:00
parent da3fe9c5a7
commit fde3ccfc68
7 changed files with 272 additions and 47 deletions

View file

@ -1,35 +1,22 @@
create table external_sessions
(
uuid varchar(36) default uuid() not null comment 'The UUID of the session for the external connection'
primary key comment 'The Unique Primary Index for the session UUID',
peer_uuid varchar(36) not null comment 'The peer UUID that opened the connection',
session_uuid varchar(36) null comment 'The UUID of the parent session responsible for this external session',
server varchar(255) null comment 'The domain of the remote server that ths external session is authorized for',
created timestamp default current_timestamp() not null comment 'The Timestamp for when this record was created',
last_used timestamp default current_timestamp() not null comment 'The Timestamp for when this session was last used',
constraint external_sessions_uuid_uindex
unique (uuid) comment 'The Unique Primary Index for the session UUID',
constraint external_sessions_registered_peers_uuid_fk
foreign key (peer_uuid) references registered_peers (uuid)
on update cascade on delete cascade,
constraint external_sessions_sessions_uuid_fk
foreign key (session_uuid) references sessions (uuid)
domain varchar(256) not null comment 'The unique domain name that this session belongs to'
primary key comment 'The Unique Primary index for the external session',
rpc_endpoint text not null comment 'The RPC endpoint of the external connection',
session_uuid varchar(36) not null comment 'The UUID of the session to the external server',
transport_encryption_algorithm enum ('xchacha20', 'chacha20', 'aes256gcm') default 'xchacha20' not null comment 'The transport encryption algorithm used',
server_keypair_expires int not null comment 'The Timestamp for when the server keypair expires',
server_public_signing_key varchar(64) not null comment 'The public signing key of the server resolved from DNS records',
server_public_encryption_key varchar(64) not null comment 'The public encryption key of the server for this session',
host_public_encryption_key varchar(64) not null comment 'The public encryption key for the host',
host_private_encryption_key varchar(64) not null comment 'The private encryption key for host',
private_shared_secret varchar(64) not null comment 'The private shared secret obtained from the DHE procedure',
host_transport_encryption_key varchar(64) not null comment 'The transport encryption key for the host',
server_transport_encryption_key varchar(64) not null comment 'The transport encryption key for the server',
last_accessed timestamp default current_timestamp() not null comment 'The Timestamp for when the record was last accessed',
created timestamp default current_timestamp() not null comment 'The Timestamp for when this record was created',
constraint external_sessions_domain_uindex
unique (domain) comment 'The Unique Primary index for the external session'
)
comment 'Table for housing external sessions from local to remote servers';
create index external_sessions_created_index
on external_sessions (created)
comment 'The Index for the created column';
create index external_sessions_last_used_index
on external_sessions (last_used)
comment 'The inex for the last used column';
create index external_sessions_peer_uuid_index
on external_sessions (peer_uuid)
comment 'The Index for the peer UUID';
create index external_sessions_session_uuid_index
on external_sessions (session_uuid)
comment 'The index for the original session uuid';
comment 'Table for housing external sessions to external servers';

View file

@ -6,6 +6,7 @@
use Socialbox\Enums\StandardHeaders;
use Socialbox\Enums\Types\RequestType;
use Socialbox\Exceptions\CryptographyException;
use Socialbox\Exceptions\DatabaseOperationException;
use Socialbox\Exceptions\ResolutionException;
use Socialbox\Exceptions\RpcException;
use Socialbox\Objects\ExportedSession;
@ -21,7 +22,7 @@
private const string CLIENT_VERSION = '1.0';
private bool $bypassSignatureVerification;
private PeerAddress $peerAddress;
private PeerAddress $identifiedAs;
private string $serverPublicSigningKey;
private string $serverPublicEncryptionKey;
private KeyPair $clientSigningKeyPair;
@ -31,18 +32,21 @@
private string $serverTransportEncryptionKey;
private ServerInformation $serverInformation;
private string $rpcEndpoint;
private string $remoteServer;
private string $sessionUuid;
/**
* Constructs a new instance with the specified peer address.
*
* @param string|PeerAddress $peerAddress The peer address to be used for the instance (eg; johndoe@example.com)
* @param string|PeerAddress $identifiedAs The peer address to be used for the instance (eg; johndoe@example.com)
* @param string|null $server Optional. The domain of the server to connect to if different from the identified
* @param ExportedSession|null $exportedSession Optional. An exported session to be used to re-connect.
* @throws CryptographyException If there is an error in the cryptographic operations.
* @throws RpcException If there is an error in the RPC request or if no response is received.
* @throws ResolutionException If there is an error in the resolution process.
* @throws RpcException If there is an error in the RPC request or if no response is received.
* @throws DatabaseOperationException
*/
public function __construct(string|PeerAddress $peerAddress, ?ExportedSession $exportedSession=null)
public function __construct(string|PeerAddress $identifiedAs, ?string $server=null, ?ExportedSession $exportedSession=null)
{
$this->bypassSignatureVerification = false;
@ -56,8 +60,9 @@
throw new RpcException('The server keypair has expired, a new session must be created');
}
$this->peerAddress = PeerAddress::fromAddress($exportedSession->getPeerAddress());
$this->identifiedAs = PeerAddress::fromAddress($exportedSession->getPeerAddress());
$this->rpcEndpoint = $exportedSession->getRpcEndpoint();
$this->remoteServer = $exportedSession->getRemoteServer();
$this->sessionUuid = $exportedSession->getSessionUuid();
$this->serverPublicSigningKey = $exportedSession->getServerPublicSigningKey();
$this->serverPublicEncryptionKey = $exportedSession->getServerPublicEncryptionKey();
@ -86,16 +91,17 @@
}
// If the peer address is a string, we need to convert it to a PeerAddress object
if(is_string($peerAddress))
if(is_string($identifiedAs))
{
$peerAddress = PeerAddress::fromAddress($peerAddress);
$identifiedAs = PeerAddress::fromAddress($identifiedAs);
}
// Set the initial properties
$this->peerAddress = $peerAddress;
$this->identifiedAs = $identifiedAs;
$this->remoteServer = $server ?? $identifiedAs->getDomain();
// Resolve the domain and get the server's Public Key & RPC Endpoint
$resolvedServer = ServerResolver::resolveDomain($this->peerAddress->getDomain(), false);
$resolvedServer = ServerResolver::resolveDomain($this->remoteServer, false);
// Import the RPC Endpoint & the server's public key.
$this->serverPublicSigningKey = $resolvedServer->getPublicSigningKey();
@ -117,7 +123,7 @@
// If the username is `host` and the domain is the same as this server's domain, we use our signing keypair
// Essentially this is a special case for the server to contact another server
if($this->peerAddress->isHost())
if($this->identifiedAs->isHost())
{
$this->clientSigningKeyPair = new KeyPair(Configuration::getCryptographyConfiguration()->getHostPublicKey(), Configuration::getCryptographyConfiguration()->getHostPrivateKey());
}
@ -157,14 +163,14 @@
StandardHeaders::REQUEST_TYPE->value . ': ' . RequestType::INITIATE_SESSION->value,
StandardHeaders::CLIENT_NAME->value . ': ' . self::CLIENT_NAME,
StandardHeaders::CLIENT_VERSION->value . ': ' . self::CLIENT_VERSION,
StandardHeaders::IDENTIFY_AS->value . ': ' . $this->peerAddress->getAddress(),
StandardHeaders::IDENTIFY_AS->value . ': ' . $this->identifiedAs->getAddress(),
// Always provide our generated encrypted public key
StandardHeaders::ENCRYPTION_PUBLIC_KEY->value . ': ' . $this->clientEncryptionKeyPair->getPublicKey()
];
// 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())
if(!$this->identifiedAs->isHost())
{
$headers[] = StandardHeaders::SIGNING_PUBLIC_KEY->value . ': ' . $this->clientSigningKeyPair->getPublicKey();
}
@ -567,8 +573,9 @@
public function exportSession(): ExportedSession
{
return new ExportedSession([
'peer_address' => $this->peerAddress->getAddress(),
'peer_address' => $this->identifiedAs->getAddress(),
'rpc_endpoint' => $this->rpcEndpoint,
'remote_server' => $this->remoteServer,
'session_uuid' => $this->sessionUuid,
'transport_encryption_algorithm' => $this->serverInformation->getTransportEncryptionAlgorithm(),
'server_keypair_expires' => $this->serverInformation->getServerKeypairExpires(),