Made message signing in Cryptography use SHA512 as the message content for... #1
7 changed files with 345 additions and 26 deletions
1
.idea/sqldialects.xml
generated
1
.idea/sqldialects.xml
generated
|
@ -7,6 +7,7 @@
|
||||||
<file url="file://$PROJECT_DIR$/src/Socialbox/Classes/Resources/database/variables.sql" dialect="MariaDB" />
|
<file url="file://$PROJECT_DIR$/src/Socialbox/Classes/Resources/database/variables.sql" dialect="MariaDB" />
|
||||||
<file url="file://$PROJECT_DIR$/src/Socialbox/Managers/CaptchaManager.php" dialect="MariaDB" />
|
<file url="file://$PROJECT_DIR$/src/Socialbox/Managers/CaptchaManager.php" dialect="MariaDB" />
|
||||||
<file url="file://$PROJECT_DIR$/src/Socialbox/Managers/RegisteredPeerManager.php" dialect="MariaDB" />
|
<file url="file://$PROJECT_DIR$/src/Socialbox/Managers/RegisteredPeerManager.php" dialect="MariaDB" />
|
||||||
|
<file url="file://$PROJECT_DIR$/src/Socialbox/Managers/ResolvedServersManager.php" dialect="MariaDB" />
|
||||||
<file url="file://$PROJECT_DIR$/src/Socialbox/Managers/SessionManager.php" dialect="MariaDB" />
|
<file url="file://$PROJECT_DIR$/src/Socialbox/Managers/SessionManager.php" dialect="MariaDB" />
|
||||||
<file url="file://$PROJECT_DIR$/src/Socialbox/Managers/VariableManager.php" dialect="MariaDB" />
|
<file url="file://$PROJECT_DIR$/src/Socialbox/Managers/VariableManager.php" dialect="MariaDB" />
|
||||||
</component>
|
</component>
|
||||||
|
|
|
@ -60,9 +60,9 @@ class Configuration
|
||||||
$config->save();
|
$config->save();
|
||||||
|
|
||||||
self::$configuration = $config->getConfiguration();
|
self::$configuration = $config->getConfiguration();
|
||||||
self::$databaseConfiguration = self::$configuration['database'];
|
self::$databaseConfiguration = new DatabaseConfiguration(self::$configuration['database']);
|
||||||
self::$cacheConfiguration = self::$configuration['cache'];
|
self::$cacheConfiguration = new CacheConfiguration(self::$configuration['cache']);
|
||||||
self::$registrationConfiguration = self::$configuration['registration'];
|
self::$registrationConfiguration = new RegistrationConfiguration(self::$configuration['registration']);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -2,51 +2,96 @@
|
||||||
|
|
||||||
namespace Socialbox\Classes;
|
namespace Socialbox\Classes;
|
||||||
|
|
||||||
|
use Socialbox\Exceptions\DatabaseOperationException;
|
||||||
use Socialbox\Exceptions\ResolutionException;
|
use Socialbox\Exceptions\ResolutionException;
|
||||||
|
use Socialbox\Managers\ResolvedServersManager;
|
||||||
use Socialbox\Objects\ResolvedServer;
|
use Socialbox\Objects\ResolvedServer;
|
||||||
|
|
||||||
class ServerResolver
|
class ServerResolver
|
||||||
{
|
{
|
||||||
|
private const string PATTERN = '/v=socialbox;sb-rpc=(https?:\/\/[^;]+);sb-key=([^;]+)/';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolves a given domain to fetch the RPC endpoint and public key from its DNS TXT records.
|
* Resolves a given domain to fetch the RPC endpoint and public key from its DNS TXT records.
|
||||||
*
|
*
|
||||||
* @param string $domain The domain to be resolved.
|
* @param string $domain The domain to be resolved.
|
||||||
* @return ResolvedServer An instance of ResolvedServer containing the endpoint and public key.
|
* @return ResolvedServer An instance of ResolvedServer containing the endpoint and public key.
|
||||||
* @throws ResolutionException If the DNS TXT records cannot be resolved or if required information is missing.
|
* @throws ResolutionException If the DNS TXT records cannot be resolved or if required information is missing.
|
||||||
|
* @throws DatabaseOperationException
|
||||||
*/
|
*/
|
||||||
public static function resolveDomain(string $domain): ResolvedServer
|
public static function resolveDomain(string $domain): ResolvedServer
|
||||||
{
|
{
|
||||||
$txtRecords = dns_get_record($domain, DNS_TXT);
|
// First query the database to check if the domain is already resolved
|
||||||
|
if(ResolvedServersManager::resolvedServerExists($domain))
|
||||||
|
{
|
||||||
|
// If the resolved server was updated in the last 30 minutes, return it
|
||||||
|
if(ResolvedServersManager::getResolvedServerUpdated($domain) > (time() - 1800))
|
||||||
|
{
|
||||||
|
return ResolvedServersManager::getResolvedServer($domain)->toResolvedServer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$txtRecords = self::dnsGetTxtRecords($domain);
|
||||||
if ($txtRecords === false)
|
if ($txtRecords === false)
|
||||||
{
|
{
|
||||||
throw new ResolutionException(sprintf("Failed to resolve DNS TXT records for %s", $domain));
|
throw new ResolutionException(sprintf("Failed to resolve DNS TXT records for %s", $domain));
|
||||||
}
|
}
|
||||||
|
|
||||||
$endpoint = null;
|
$fullRecord = self::concatenateTxtRecords($txtRecords);
|
||||||
$publicKey = null;
|
|
||||||
|
if (preg_match(self::PATTERN, $fullRecord, $matches))
|
||||||
|
{
|
||||||
|
$endpoint = trim($matches[1]);
|
||||||
|
$publicKey = trim(str_replace(' ', '', $matches[2]));
|
||||||
|
|
||||||
|
if (empty($endpoint))
|
||||||
|
{
|
||||||
|
throw new ResolutionException(sprintf("Failed to resolve RPC endpoint for %s", $domain));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($publicKey))
|
||||||
|
{
|
||||||
|
throw new ResolutionException(sprintf("Failed to resolve public key for %s", $domain));
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ResolvedServer($endpoint, $publicKey);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new ResolutionException(sprintf("Failed to find valid SocialBox record for %s", $domain));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the TXT records for a given domain using the dns_get_record function.
|
||||||
|
*
|
||||||
|
* @param string $domain The domain name to fetch TXT records for.
|
||||||
|
* @return array|false An array of DNS TXT records on success, or false on failure.
|
||||||
|
*/
|
||||||
|
private static function dnsGetTxtRecords(string $domain)
|
||||||
|
{
|
||||||
|
return dns_get_record($domain, DNS_TXT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Concatenates an array of TXT records into a single string.
|
||||||
|
*
|
||||||
|
* @param array $txtRecords An array of TXT records, where each record is expected to have a 'txt' key.
|
||||||
|
* @return string A concatenated string of all TXT records.
|
||||||
|
*/
|
||||||
|
private static function concatenateTxtRecords(array $txtRecords): string
|
||||||
|
{
|
||||||
|
$fullRecordBuilder = '';
|
||||||
|
|
||||||
foreach ($txtRecords as $txt)
|
foreach ($txtRecords as $txt)
|
||||||
{
|
{
|
||||||
if (isset($txt['txt']) && str_starts_with($txt['txt'], 'socialbox='))
|
if (isset($txt['txt']))
|
||||||
{
|
{
|
||||||
$endpoint = substr($txt['txt'], strlen('socialbox='));
|
$fullRecordBuilder .= trim($txt['txt'], '" ');
|
||||||
}
|
|
||||||
elseif (isset($txt['txt']) && str_starts_with($txt['txt'], 'socialbox-key='))
|
|
||||||
{
|
|
||||||
$publicKey = substr($txt['txt'], strlen('socialbox-key='));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($endpoint === null)
|
return $fullRecordBuilder;
|
||||||
{
|
|
||||||
throw new ResolutionException(sprintf("Failed to resolve RPC endpoint for %s", $domain));
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($publicKey === null)
|
|
||||||
{
|
|
||||||
throw new ResolutionException(sprintf("Failed to resolve public key for %s", $domain));
|
|
||||||
}
|
|
||||||
|
|
||||||
return new ResolvedServer($endpoint, $publicKey);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
145
src/Socialbox/Managers/ResolvedServersManager.php
Normal file
145
src/Socialbox/Managers/ResolvedServersManager.php
Normal file
|
@ -0,0 +1,145 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Socialbox\Managers;
|
||||||
|
|
||||||
|
use DateTime;
|
||||||
|
use Exception;
|
||||||
|
use PDOException;
|
||||||
|
use Socialbox\Classes\Database;
|
||||||
|
use Socialbox\Exceptions\DatabaseOperationException;
|
||||||
|
use Socialbox\Objects\Database\ResolvedServerRecord;
|
||||||
|
use Socialbox\Objects\ResolvedServer;
|
||||||
|
|
||||||
|
class ResolvedServersManager
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Checks if a resolved server exists in the database for the given domain.
|
||||||
|
*
|
||||||
|
* @param string $domain The domain to check in the resolved_servers table.
|
||||||
|
* @return bool True if the server exists in the database, otherwise false.
|
||||||
|
* @throws DatabaseOperationException If there is an error during the database operation.
|
||||||
|
*/
|
||||||
|
public static function resolvedServerExists(string $domain): bool
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$statement = Database::getConnection()->prepare("SELECT COUNT(*) FROM resolved_servers WHERE domain=?");
|
||||||
|
$statement->bindParam(1, $domain);
|
||||||
|
$statement->execute();
|
||||||
|
return $statement->fetchColumn() > 0;
|
||||||
|
}
|
||||||
|
catch(PDOException $e)
|
||||||
|
{
|
||||||
|
throw new DatabaseOperationException('Failed to check if a resolved server exists in the database', $e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes a resolved server from the database.
|
||||||
|
*
|
||||||
|
* @param string $domain The domain name of the server to be deleted.
|
||||||
|
* @return void
|
||||||
|
* @throws DatabaseOperationException If the deletion operation fails.
|
||||||
|
*/
|
||||||
|
public static function deleteResolvedServer(string $domain): void
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$statement = Database::getConnection()->prepare("DELETE FROM resolved_servers WHERE domain=?");
|
||||||
|
$statement->bindParam(1, $domain);
|
||||||
|
$statement->execute();
|
||||||
|
}
|
||||||
|
catch(PDOException $e)
|
||||||
|
{
|
||||||
|
throw new DatabaseOperationException('Failed to delete a resolved server from the database', $e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the last updated date of a resolved server based on its domain.
|
||||||
|
*
|
||||||
|
* @param string $domain The domain of the resolved server.
|
||||||
|
* @return DateTime The last updated date and time of the resolved server.
|
||||||
|
*/
|
||||||
|
public static function getResolvedServerUpdated(string $domain): DateTime
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$statement = Database::getConnection()->prepare("SELECT updated FROM resolved_servers WHERE domain=?");
|
||||||
|
$statement->bindParam(1, $domain);
|
||||||
|
$statement->execute();
|
||||||
|
$result = $statement->fetchColumn();
|
||||||
|
return new DateTime($result);
|
||||||
|
}
|
||||||
|
catch(Exception $e)
|
||||||
|
{
|
||||||
|
throw new DatabaseOperationException('Failed to get the updated date of a resolved server from the database', $e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the resolved server record from the database for a given domain.
|
||||||
|
*
|
||||||
|
* @param string $domain The domain name for which to retrieve the resolved server record.
|
||||||
|
* @return ResolvedServerRecord The resolved server record associated with the given domain.
|
||||||
|
* @throws DatabaseOperationException If there is an error retrieving the resolved server record from the database.
|
||||||
|
*/
|
||||||
|
public static function getResolvedServer(string $domain): ResolvedServerRecord
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$statement = Database::getConnection()->prepare("SELECT * FROM resolved_servers WHERE domain=?");
|
||||||
|
$statement->bindParam(1, $domain);
|
||||||
|
$statement->execute();
|
||||||
|
$result = $statement->fetch();
|
||||||
|
return new ResolvedServerRecord($result);
|
||||||
|
}
|
||||||
|
catch(PDOException $e)
|
||||||
|
{
|
||||||
|
throw new DatabaseOperationException('Failed to get a resolved server from the database', $e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds or updates a resolved server in the database.
|
||||||
|
*
|
||||||
|
* @param string $domain The domain name of the resolved server.
|
||||||
|
* @param ResolvedServer $resolvedServer The resolved server object containing endpoint and public key.
|
||||||
|
* @return void
|
||||||
|
* @throws DatabaseOperationException If a database operation fails.
|
||||||
|
*/
|
||||||
|
public static function addResolvedServer(string $domain, ResolvedServer $resolvedServer): void
|
||||||
|
{
|
||||||
|
$endpoint = $resolvedServer->getEndpoint();
|
||||||
|
$publicKey = $resolvedServer->getPublicKey();
|
||||||
|
|
||||||
|
if(self::resolvedServerExists($domain))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$statement = Database::getConnection()->prepare("UPDATE resolved_servers SET endpoint=?, public_key=?, updated=NOW() WHERE domain=?");
|
||||||
|
$statement->bindParam(1, $endpoint);
|
||||||
|
$statement->bindParam(2, $publicKey);
|
||||||
|
$statement->bindParam(3, $domain);
|
||||||
|
$statement->execute();
|
||||||
|
}
|
||||||
|
catch(PDOException $e)
|
||||||
|
{
|
||||||
|
throw new DatabaseOperationException('Failed to update a resolved server in the database', $e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$statement = Database::getConnection()->prepare("INSERT INTO resolved_servers (domain, endpoint, public_key) VALUES (?, ?, ?)");
|
||||||
|
$statement->bindParam(1, $domain);
|
||||||
|
$statement->bindParam(2, $endpoint);
|
||||||
|
$statement->bindParam(3, $publicKey);
|
||||||
|
$statement->execute();
|
||||||
|
}
|
||||||
|
catch(PDOException $e)
|
||||||
|
{
|
||||||
|
throw new DatabaseOperationException('Failed to add a resolved server to the database', $e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,17 +2,97 @@
|
||||||
|
|
||||||
namespace Socialbox\Objects\Database;
|
namespace Socialbox\Objects\Database;
|
||||||
|
|
||||||
|
use DateTime;
|
||||||
use Socialbox\Interfaces\SerializableInterface;
|
use Socialbox\Interfaces\SerializableInterface;
|
||||||
|
use Socialbox\Objects\ResolvedServer;
|
||||||
|
|
||||||
class ResolvedServerRecord implements SerializableInterface
|
class ResolvedServerRecord implements SerializableInterface
|
||||||
{
|
{
|
||||||
|
private string $domain;
|
||||||
|
private string $endpoint;
|
||||||
|
private string $publicKey;
|
||||||
|
private DateTime $updated;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new instance of the class.
|
||||||
|
*
|
||||||
|
* @param array $data An associative array containing the domain, endpoint, public_key, and updated values.
|
||||||
|
* @throws \DateMalformedStringException
|
||||||
|
*/
|
||||||
|
public function __construct(array $data)
|
||||||
|
{
|
||||||
|
$this->domain = (string)$data['domain'];
|
||||||
|
$this->endpoint = (string)$data['endpoint'];
|
||||||
|
$this->publicKey = (string)$data['public_key'];
|
||||||
|
|
||||||
|
if(is_null($data['updated']))
|
||||||
|
{
|
||||||
|
$this->updated = new DateTime();
|
||||||
|
}
|
||||||
|
elseif (is_string($data['updated']))
|
||||||
|
{
|
||||||
|
$this->updated = new DateTime($data['updated']);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$this->updated = $data['updated'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return string The domain value.
|
||||||
|
*/
|
||||||
|
public function getDomain(): string
|
||||||
|
{
|
||||||
|
return $this->domain;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return string The endpoint value.
|
||||||
|
*/
|
||||||
|
public function getEndpoint(): string
|
||||||
|
{
|
||||||
|
return $this->endpoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return string The public key.
|
||||||
|
*/
|
||||||
|
public function getPublicKey(): string
|
||||||
|
{
|
||||||
|
return $this->publicKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the timestamp of the last update.
|
||||||
|
*
|
||||||
|
* @return DateTime The DateTime object representing the last update time.
|
||||||
|
*/
|
||||||
|
public function getUpdated(): DateTime
|
||||||
|
{
|
||||||
|
return $this->updated;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the record to a ResolvedServer object.
|
||||||
|
*
|
||||||
|
* @return ResolvedServer The ResolvedServer object.
|
||||||
|
*/
|
||||||
|
public function toResolvedServer(): ResolvedServer
|
||||||
|
{
|
||||||
|
return new ResolvedServer($this->endpoint, $this->publicKey);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* @inheritDoc
|
||||||
|
* @throws \DateMalformedStringException
|
||||||
*/
|
*/
|
||||||
public static function fromArray(array $data): object
|
public static function fromArray(array $data): object
|
||||||
{
|
{
|
||||||
// TODO: Implement fromArray() method.
|
return new self($data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -20,6 +100,11 @@ class ResolvedServerRecord implements SerializableInterface
|
||||||
*/
|
*/
|
||||||
public function toArray(): array
|
public function toArray(): array
|
||||||
{
|
{
|
||||||
// TODO: Implement toArray() method.
|
return [
|
||||||
|
'domain' => $this->domain,
|
||||||
|
'endpoint' => $this->endpoint,
|
||||||
|
'public_key' => $this->publicKey,
|
||||||
|
'updated' => $this->updated->format('Y-m-d H:i:s')
|
||||||
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
43
tests/Socialbox/Managers/ResolvedServersManagerTest.php
Normal file
43
tests/Socialbox/Managers/ResolvedServersManagerTest.php
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Socialbox\Managers;
|
||||||
|
|
||||||
|
use DateTime;
|
||||||
|
use ncc\Utilities\Resolver;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use Socialbox\Classes\ServerResolver;
|
||||||
|
|
||||||
|
class ResolvedServersManagerTest extends TestCase
|
||||||
|
{
|
||||||
|
|
||||||
|
public function setUp(): void
|
||||||
|
{
|
||||||
|
if(ResolvedServersManager::resolvedServerExists('n64.cc'))
|
||||||
|
{
|
||||||
|
ResolvedServersManager::deleteResolvedServer('n64.cc');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetResolvedServerUpdated()
|
||||||
|
{
|
||||||
|
ResolvedServersManager::addResolvedServer('n64.cc', ServerResolver::resolveDomain('n64.cc'));
|
||||||
|
$this->assertInstanceOf(DateTime::class, ResolvedServersManager::getResolvedServerUpdated('n64.cc'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testResolvedServerExists()
|
||||||
|
{
|
||||||
|
ResolvedServersManager::addResolvedServer('n64.cc', ServerResolver::resolveDomain('n64.cc'));
|
||||||
|
$this->assertTrue(ResolvedServersManager::resolvedServerExists('n64.cc'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetResolvedServer()
|
||||||
|
{
|
||||||
|
ResolvedServersManager::addResolvedServer('n64.cc', ServerResolver::resolveDomain('n64.cc'));
|
||||||
|
$resolvedServer = ResolvedServersManager::getResolvedServer('n64.cc');
|
||||||
|
|
||||||
|
$this->assertEquals('n64.cc', $resolvedServer->getDomain());
|
||||||
|
$this->assertIsString($resolvedServer->getEndpoint());
|
||||||
|
$this->assertIsString($resolvedServer->getPublicKey());
|
||||||
|
$this->assertInstanceOf(DateTime::class, $resolvedServer->getUpdated());
|
||||||
|
}
|
||||||
|
}
|
|
@ -35,6 +35,6 @@ class SessionManagerTest extends TestCase
|
||||||
|
|
||||||
$this->assertInstanceOf(SessionRecord::class, $session);
|
$this->assertInstanceOf(SessionRecord::class, $session);
|
||||||
$this->assertEquals($uuid, $session->getUuid());
|
$this->assertEquals($uuid, $session->getUuid());
|
||||||
$this->assertEquals($keyPair->getPublicKey(), Utilities::base64encode($session->getPublicKey()));
|
$this->assertEquals($keyPair->getPublicKey(), $session->getPublicKey());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue