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

Closed
netkas wants to merge 421 commits from master into dev
10 changed files with 189 additions and 6 deletions
Showing only changes of commit 38b96db83a - Show all commits

View file

@ -2,6 +2,8 @@
<module type="WEB_MODULE" version="4"> <module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager"> <component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$"> <content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/tests" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/.idea/dataSources" /> <excludeFolder url="file://$MODULE_DIR$/.idea/dataSources" />
<excludeFolder url="file://$MODULE_DIR$/build" /> <excludeFolder url="file://$MODULE_DIR$/build" />
</content> </content>

View file

@ -4,7 +4,7 @@ namespace Socialbox\Classes\Configuration;
class SecurityConfiguration class SecurityConfiguration
{ {
private bool $displayInternalErrors; private bool $displayInternalExceptions;
private int $resolvedServersTtl; private int $resolvedServersTtl;
private int $captchaTtl; private int $captchaTtl;
@ -17,7 +17,7 @@ class SecurityConfiguration
*/ */
public function __construct(array $data) public function __construct(array $data)
{ {
$this->displayInternalErrors = $data['display_internal_errors']; $this->displayInternalExceptions = $data['display_internal_exceptions'];
$this->resolvedServersTtl = $data['resolved_servers_ttl']; $this->resolvedServersTtl = $data['resolved_servers_ttl'];
$this->captchaTtl = $data['captcha_ttl']; $this->captchaTtl = $data['captcha_ttl'];
} }
@ -27,9 +27,9 @@ class SecurityConfiguration
* *
* @return bool True if the display of internal errors is enabled, false otherwise. * @return bool True if the display of internal errors is enabled, false otherwise.
*/ */
public function isDisplayInternalErrors(): bool public function isDisplayInternalExceptions(): bool
{ {
return $this->displayInternalErrors; return $this->displayInternalExceptions;
} }
/** /**

View file

@ -0,0 +1,83 @@
<?php
namespace Socialbox\Classes\StandardMethods;
use Gregwar\Captcha\CaptchaBuilder;
use Socialbox\Abstracts\Method;
use Socialbox\Classes\Logger;
use Socialbox\Enums\Flags\PeerFlags;
use Socialbox\Enums\StandardError;
use Socialbox\Exceptions\DatabaseOperationException;
use Socialbox\Exceptions\StandardException;
use Socialbox\Interfaces\SerializableInterface;
use Socialbox\Managers\CaptchaManager;
use Socialbox\Managers\RegisteredPeerManager;
use Socialbox\Managers\SessionManager;
use Socialbox\Objects\ClientRequest;
use Socialbox\Objects\RpcRequest;
use Socialbox\Objects\Standard\ImageCaptcha;
class GetCaptcha extends Method
{
/**
* @inheritDoc
*/
public static function execute(ClientRequest $request, RpcRequest $rpcRequest): ?SerializableInterface
{
// Check if the request has a Session UUID
if($request->getSessionUuid() === null)
{
return $rpcRequest->produceError(StandardError::SESSION_REQUIRED);
}
// Get the session and check if it's already authenticated
try
{
$session = SessionManager::getSession($request->getSessionUuid());
}
catch(DatabaseOperationException $e)
{
throw new StandardException("There was an unexpected error while trying to get the session", StandardError::INTERNAL_SERVER_ERROR, $e);
}
// Check for session conditions
if($session->getPeerUuid() === null)
{
return $rpcRequest->produceError(StandardError::AUTHENTICATION_REQUIRED);
}
// Get the peer
try
{
$peer = RegisteredPeerManager::getPeer($session->getPeerUuid());
}
catch(DatabaseOperationException $e)
{
throw new StandardException("There was unexpected error while trying to get the peer", StandardError::INTERNAL_SERVER_ERROR, $e);
}
// Check if the VER_SOLVE_IMAGE_CAPTCHA flag exists.
if(!$peer->flagExists(PeerFlags::VER_SOLVE_IMAGE_CAPTCHA))
{
return $rpcRequest->produceError(StandardError::CAPTCHA_NOT_AVAILABLE, 'You are not required to complete a captcha at this time');
}
try
{
Logger::getLogger()->debug('Creating a new captcha for peer ' . $peer->getUuid());
$answer = CaptchaManager::createCaptcha($peer);
$captchaRecord = CaptchaManager::getCaptcha($peer);
}
catch (DatabaseOperationException $e)
{
throw new StandardException("There was an unexpected error while trying create the captcha", StandardError::INTERNAL_SERVER_ERROR, $e);
}
// Build the captcha
Logger::getLogger()->debug('Building captcha for peer ' . $peer->getUuid());
return $rpcRequest->produceResponse(new ImageCaptcha([
'expires' => $captchaRecord->getExpires()->getTimestamp(),
'image' => (new CaptchaBuilder($answer))->build()->inline()] // Returns HTML base64 encoded image of the captcha
));
}
}

View file

@ -23,6 +23,9 @@ enum StandardError : int
case SESSION_NOT_FOUND = -3004; case SESSION_NOT_FOUND = -3004;
case SESSION_REQUIRED = -3005; case SESSION_REQUIRED = -3005;
case REGISTRATION_DISABLED = -3006; case REGISTRATION_DISABLED = -3006;
case CAPTCHA_NOT_AVAILABLE = -3007;
case INCORRECT_CAPTCHA_ANSWER = -3008;
case CAPTCHA_EXPIRED = -3009;
// General Error Messages // General Error Messages
case PEER_NOT_FOUND = -4000; case PEER_NOT_FOUND = -4000;
@ -52,6 +55,10 @@ enum StandardError : int
self::AUTHENTICATION_REQUIRED => 'Authentication is required to preform this action', self::AUTHENTICATION_REQUIRED => 'Authentication is required to preform this action',
self::SESSION_NOT_FOUND => 'The requested session UUID was not found', self::SESSION_NOT_FOUND => 'The requested session UUID was not found',
self::SESSION_REQUIRED => 'A session is required to preform this action', self::SESSION_REQUIRED => 'A session is required to preform this action',
self::REGISTRATION_DISABLED => 'Registration is disabled on the server',
self::CAPTCHA_NOT_AVAILABLE => 'Captcha is not available',
self::INCORRECT_CAPTCHA_ANSWER => 'The Captcha answer is incorrect',
self::CAPTCHA_EXPIRED => 'The captcha has expired and a new captcha needs to be requested',
self::PEER_NOT_FOUND => 'The requested peer was not found', self::PEER_NOT_FOUND => 'The requested peer was not found',
self::INVALID_USERNAME => 'The given username is invalid, it must be Alphanumeric with a minimum of 3 character but no greater than 255 characters', self::INVALID_USERNAME => 'The given username is invalid, it must be Alphanumeric with a minimum of 3 character but no greater than 255 characters',

View file

@ -3,6 +3,7 @@
namespace Socialbox\Enums; namespace Socialbox\Enums;
use Socialbox\Classes\StandardMethods\CreateSession; use Socialbox\Classes\StandardMethods\CreateSession;
use Socialbox\Classes\StandardMethods\GetCaptcha;
use Socialbox\Classes\StandardMethods\GetMe; use Socialbox\Classes\StandardMethods\GetMe;
use Socialbox\Classes\StandardMethods\Ping; use Socialbox\Classes\StandardMethods\Ping;
use Socialbox\Classes\StandardMethods\Register; use Socialbox\Classes\StandardMethods\Register;
@ -17,6 +18,7 @@ enum StandardMethods : string
case CREATE_SESSION = 'createSession'; case CREATE_SESSION = 'createSession';
case REGISTER = 'register'; case REGISTER = 'register';
case GET_ME = 'getMe'; case GET_ME = 'getMe';
case GET_CAPTCHA = 'getCaptcha';
/** /**
* @param ClientRequest $request * @param ClientRequest $request
@ -32,6 +34,7 @@ enum StandardMethods : string
self::CREATE_SESSION => CreateSession::execute($request, $rpcRequest), self::CREATE_SESSION => CreateSession::execute($request, $rpcRequest),
self::REGISTER => Register::execute($request, $rpcRequest), self::REGISTER => Register::execute($request, $rpcRequest),
self::GET_ME => GetMe::execute($request, $rpcRequest), self::GET_ME => GetMe::execute($request, $rpcRequest),
self::GET_CAPTCHA => GetCaptcha::execute($request, $rpcRequest),
}; };
} }
} }

View file

@ -6,6 +6,7 @@ use DateTime;
use DateTimeInterface; use DateTimeInterface;
use PDOException; use PDOException;
use Socialbox\Classes\Database; use Socialbox\Classes\Database;
use Socialbox\Classes\Logger;
use Socialbox\Classes\Utilities; use Socialbox\Classes\Utilities;
use Socialbox\Enums\Status\CaptchaStatus; use Socialbox\Enums\Status\CaptchaStatus;
use Socialbox\Exceptions\DatabaseOperationException; use Socialbox\Exceptions\DatabaseOperationException;
@ -21,7 +22,7 @@ class CaptchaManager
* @return string The answer to the captcha. * @return string The answer to the captcha.
* @throws DatabaseOperationException If the operation fails. * @throws DatabaseOperationException If the operation fails.
*/ */
private static function createCaptcha(string|RegisteredPeerRecord $peer_uuid): string public static function createCaptcha(string|RegisteredPeerRecord $peer_uuid): string
{ {
// If the peer_uuid is a RegisteredPeerRecord, get the UUID // If the peer_uuid is a RegisteredPeerRecord, get the UUID
if($peer_uuid instanceof RegisteredPeerRecord) if($peer_uuid instanceof RegisteredPeerRecord)
@ -33,6 +34,7 @@ class CaptchaManager
if(!self::captchaExists($peer_uuid)) if(!self::captchaExists($peer_uuid))
{ {
Logger::getLogger()->debug('Creating a new captcha record for peer ' . $peer_uuid);
$statement = Database::getConnection()->prepare("INSERT INTO captcha_images (peer_uuid, answer) VALUES (?, ?)"); $statement = Database::getConnection()->prepare("INSERT INTO captcha_images (peer_uuid, answer) VALUES (?, ?)");
$statement->bindParam(1, $peer_uuid); $statement->bindParam(1, $peer_uuid);
$statement->bindParam(2, $answer); $statement->bindParam(2, $answer);
@ -49,6 +51,7 @@ class CaptchaManager
return $answer; return $answer;
} }
Logger::getLogger()->debug('Updating an existing captcha record for peer ' . $peer_uuid);
$statement = Database::getConnection()->prepare("UPDATE captcha_images SET answer=?, status='UNSOLVED', created=NOW() WHERE peer_uuid=?"); $statement = Database::getConnection()->prepare("UPDATE captcha_images SET answer=?, status='UNSOLVED', created=NOW() WHERE peer_uuid=?");
$statement->bindParam(1, $answer); $statement->bindParam(1, $answer);
$statement->bindParam(2, $peer_uuid); $statement->bindParam(2, $peer_uuid);
@ -83,6 +86,7 @@ class CaptchaManager
// Return false if the captcha does not exist // Return false if the captcha does not exist
if(!self::captchaExists($peer_uuid)) if(!self::captchaExists($peer_uuid))
{ {
Logger::getLogger()->warning(sprintf("The requested captcha does not exist for the peer %s", $peer_uuid));
return false; return false;
} }
@ -91,21 +95,25 @@ class CaptchaManager
// Return false if the captcha has already been solved // Return false if the captcha has already been solved
if($captcha->getStatus() === CaptchaStatus::SOLVED) if($captcha->getStatus() === CaptchaStatus::SOLVED)
{ {
Logger::getLogger()->warning(sprintf("The requested captcha has already been solved for the peer %s", $peer_uuid));
return false; return false;
} }
// Return false if the captcha is older than 5 minutes // Return false if the captcha is older than 5 minutes
if ($captcha->getCreated() instanceof DateTimeInterface && $captcha->getCreated()->diff(new DateTime())->i > 5) if ($captcha->isExpired())
{ {
Logger::getLogger()->warning(sprintf("The requested captcha is older than 5 minutes for the peer %s", $peer_uuid));
return false; return false;
} }
// Verify the answer // Verify the answer
if($captcha->getAnswer() !== $answer) if($captcha->getAnswer() !== $answer)
{ {
Logger::getLogger()->warning(sprintf("The answer to the requested captcha is incorrect for the peer %s", $peer_uuid));
return false; return false;
} }
Logger::getLogger()->debug(sprintf("The answer to the requested captcha is correct for the peer %s", $peer_uuid));
$statement = Database::getConnection()->prepare("UPDATE captcha_images SET status='SOLVED', answered=NOW() WHERE peer_uuid=?"); $statement = Database::getConnection()->prepare("UPDATE captcha_images SET status='SOLVED', answered=NOW() WHERE peer_uuid=?");
$statement->bindParam(1, $peer_uuid); $statement->bindParam(1, $peer_uuid);
@ -136,6 +144,8 @@ class CaptchaManager
$peer_uuid = $peer_uuid->getUuid(); $peer_uuid = $peer_uuid->getUuid();
} }
Logger::getLogger()->debug('Getting the captcha record for peer ' . $peer_uuid);
try try
{ {
$statement = Database::getConnection()->prepare("SELECT * FROM captcha_images WHERE peer_uuid=? LIMIT 1"); $statement = Database::getConnection()->prepare("SELECT * FROM captcha_images WHERE peer_uuid=? LIMIT 1");
@ -171,6 +181,8 @@ class CaptchaManager
$peer_uuid = $peer_uuid->getUuid(); $peer_uuid = $peer_uuid->getUuid();
} }
Logger::getLogger()->debug('Checking if a captcha exists for peer ' . $peer_uuid);
try try
{ {
$statement = Database::getConnection()->prepare("SELECT COUNT(*) FROM captcha_images WHERE peer_uuid=?"); $statement = Database::getConnection()->prepare("SELECT COUNT(*) FROM captcha_images WHERE peer_uuid=?");

View file

@ -3,6 +3,7 @@
namespace Socialbox\Objects\Database; namespace Socialbox\Objects\Database;
use DateTime; use DateTime;
use Socialbox\Classes\Configuration;
use Socialbox\Enums\Status\CaptchaStatus; use Socialbox\Enums\Status\CaptchaStatus;
use Socialbox\Interfaces\SerializableInterface; use Socialbox\Interfaces\SerializableInterface;
@ -55,6 +56,16 @@ class CaptchaRecord implements SerializableInterface
return $this->created; return $this->created;
} }
public function getExpires(): DateTime
{
return $this->created->modify(sprintf("+%s seconds", Configuration::getSecurityConfiguration()->getCaptchaTtl()));
}
public function isExpired(): bool
{
return $this->getExpires() < new DateTime();
}
/** /**
* @inheritDoc * @inheritDoc
*/ */

View file

@ -0,0 +1,53 @@
<?php
namespace Socialbox\Objects\Standard;
use Socialbox\Interfaces\SerializableInterface;
use Socialbox\Objects\Database\CaptchaRecord;
class ImageCaptcha implements SerializableInterface
{
private int $expires;
private string $image;
public function __construct(array $data)
{
$this->expires = $data['expires'];
$this->image = $data['image'];
}
/**
* @return int
*/
public function getExpires(): int
{
return $this->expires;
}
/**
* @return string
*/
public function getImage(): string
{
return $this->image;
}
/**
* @inheritDoc
*/
public static function fromArray(array $data): object
{
return new self($data);
}
/**
* @inheritDoc
*/
public function toArray(): array
{
return [
'expires' => $this->expires,
'image' => $this->image
];
}
}

View file

@ -52,6 +52,7 @@
{ {
Logger::getLogger()->debug(sprintf('Processing RPC request for method %s', $rpcRequest->getMethod())); Logger::getLogger()->debug(sprintf('Processing RPC request for method %s', $rpcRequest->getMethod()));
$response = $method->execute($clientRequest, $rpcRequest); $response = $method->execute($clientRequest, $rpcRequest);
Logger::getLogger()->debug(sprintf('%s method executed successfully', $rpcRequest->getMethod()));
} }
catch(StandardException $e) catch(StandardException $e)
{ {

View file

@ -0,0 +1,11 @@
<?php
namespace Socialbox;
class Socialclient
{
public function __construct()
{
echo "Socialclient class has been initialized";
}
}