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

Closed
netkas wants to merge 421 commits from master into dev
11 changed files with 234 additions and 23 deletions
Showing only changes of commit 3a10e01bd8 - Show all commits

View file

@ -0,0 +1,117 @@
<?php
namespace Socialbox\Classes\ClientCommands;
use Socialbox\Classes\Cryptography;
use Socialbox\Classes\Logger;
use Socialbox\Classes\Utilities;
use Socialbox\Exceptions\CryptographyException;
use Socialbox\Exceptions\DatabaseOperationException;
use Socialbox\Exceptions\ResolutionException;
use Socialbox\Exceptions\RpcException;
use Socialbox\Interfaces\CliCommandInterface;
use Socialbox\Objects\ClientSession;
use Socialbox\SocialClient;
class ConnectCommand implements CliCommandInterface
{
public static function execute(array $args): int
{
if(!isset($args['name']))
{
Logger::getLogger()->error('The name argument is required, this is the name of the session');
}
$workingDirectory = getcwd();
if(isset($args['directory']))
{
if(!is_dir($args['directory']))
{
Logger::getLogger()->error('The directory provided does not exist');
return 1;
}
$workingDirectory = $args['directory'];
}
$sessionFile = $workingDirectory . DIRECTORY_SEPARATOR . Utilities::sanitizeFileName($args['name']) . '.json';
if(!file_exists($sessionFile))
{
return self::createSession($args, $sessionFile);
}
Logger::getLogger()->info(sprintf('Session file already exists at %s', $sessionFile));
return 0;
}
private static function createSession(array $args, string $sessionFile): int
{
if(!isset($args['domain']))
{
Logger::getLogger()->error('The domain argument is required, this is the domain of the socialbox instance');
return 1;
}
try
{
$client = new SocialClient($args['domain']);
}
catch (DatabaseOperationException $e)
{
Logger::getLogger()->error('Failed to create the client session', $e);
return 1;
}
catch (ResolutionException $e)
{
Logger::getLogger()->error('Failed to resolve the domain', $e);
return 1;
}
try
{
$keyPair = Cryptography::generateKeyPair();
$session = $client->createSession($keyPair);
}
catch (CryptographyException | RpcException $e)
{
Logger::getLogger()->error('Failed to create the session', $e);
return 1;
}
$sessionData = new ClientSession([
'domain' => $args['domain'],
'session_uuid' => $session,
'public_key' => $keyPair->getPublicKey(),
'private_key' => $keyPair->getPrivateKey()
]);
$sessionData->save($sessionFile);
Logger::getLogger()->info(sprintf('Session created and saved to %s', $sessionFile));
return 0;
}
public static function getHelpMessage(): string
{
return <<<HELP
Usage: socialbox connect --name <name> --domain <domain> [--directory <directory>]
Creates a new session with the specified name and domain. The session will be saved to the current working directory by default, or to the specified directory if provided.
Options:
--name The name of the session to create.
--domain The domain of the socialbox instance.
--directory The directory where the session file should be saved.
Example:
socialbox connect --name mysession --domain socialbox.example.com
HELP;
}
public static function getShortHelpMessage(): string
{
return 'Connect Command - Creates a new session with the specified name and domain';
}
}

View file

@ -27,8 +27,8 @@
* Constructor for initializing the server connection with a given domain.
*
* @param string $domain The domain used to resolve the server's endpoint and public key.
* @throws DatabaseOperationException
* @throws ResolutionException
* @noinspection PhpUnhandledExceptionInspection
*/
public function __construct(string $domain)
{

View file

@ -75,8 +75,8 @@ class VerificationGetImageCaptcha extends Method
// Build the captcha
return $rpcRequest->produceResponse(new ImageCaptcha([
'expires' => $captchaRecord->getExpires()->getTimestamp(),
'image' => (new CaptchaBuilder($answer))->build()->inline()] // Returns HTML base64 encoded image of the captcha
));
'expires' => $captchaRecord->getExpires(),
'image' => (new CaptchaBuilder($answer))->build()->inline()
])); // Returns HTML base64 encoded image of the captcha
}
}

View file

@ -2,6 +2,7 @@
namespace Socialbox\Classes;
use DateTime;
use InvalidArgumentException;
use JsonException;
use RuntimeException;
@ -170,4 +171,25 @@ class Utilities
return $randomString;
}
/**
* Generates a random CRC32 hash.
*
* @return string The generated CRC32 hash as a string.
*/
public static function randomCrc32(): string
{
return hash('crc32b', uniqid());
}
/**
* Sanitizes a file name by removing any characters that are not alphanumeric, hyphen, or underscore.
*
* @param string $name The file name to be sanitized.
* @return string The sanitized file name.
*/
public static function sanitizeFileName(string $name): string
{
return preg_replace('/[^a-zA-Z0-9-_]/', '', $name);
}
}

View file

@ -8,6 +8,7 @@ use Socialbox\Classes\CliCommands\InitializeCommand;
enum CliCommands : string
{
case INITIALIZE = 'init';
case CLIENT = 'client';
/**
* Handles the command execution, returns the exit code.
@ -20,6 +21,7 @@ enum CliCommands : string
return match ($this)
{
self::INITIALIZE => InitializeCommand::execute($args),
self::CLIENT => ClientCommand::execute($args)
};
}
public function getHelpMessage(): string

View file

@ -0,0 +1,8 @@
<?php
namespace Socialbox\Enums;
enum ClientCommands : string
{
case CONNECT = 'CONNECT';
}

View file

@ -3,6 +3,7 @@
namespace Socialbox\Exceptions;
use Exception;
use Socialbox\Objects\RpcError;
use Throwable;
class RpcException extends Exception
@ -18,4 +19,16 @@ class RpcException extends Exception
{
parent::__construct($message, $code, $previous);
}
/**
* Creates an RpcException instance from an RpcError.
*
* @param RpcError $error The RPC error object containing details of the error.
* @param Throwable|null $e The previous throwable used for exception chaining.
* @return RpcException The constructed RpcException instance.
*/
public static function fromRpcError(RpcError $error, ?Throwable $e=null): RpcException
{
return new RpcException($error->getError(), $error->getCode()->value, $e);
}
}

View file

@ -3,7 +3,6 @@
namespace Socialbox\Managers;
use DateTime;
use DateTimeInterface;
use PDOException;
use Socialbox\Classes\Database;
use Socialbox\Classes\Logger;
@ -31,13 +30,15 @@ class CaptchaManager
}
$answer = Utilities::randomString(6, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789');
$current_time = (new DateTime())->setTimestamp(time());
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, created, answer) VALUES (?, ?, ?)");
$statement->bindParam(1, $peer_uuid);
$statement->bindParam(2, $answer);
$statement->bindParam(2, $current_time);
$statement->bindParam(3, $answer);
try
{
@ -52,9 +53,10 @@ class CaptchaManager
}
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=? WHERE peer_uuid=?");
$statement->bindParam(1, $answer);
$statement->bindParam(2, $peer_uuid);
$statement->bindParam(2, $current_time);
$statement->bindParam(3, $peer_uuid);
try
{

View file

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

View file

@ -0,0 +1,57 @@
<?php
namespace Socialbox;
use Socialbox\Classes\RpcClient;
use Socialbox\Classes\Utilities;
use Socialbox\Exceptions\CryptographyException;
use Socialbox\Exceptions\ResolutionException;
use Socialbox\Exceptions\RpcException;
use Socialbox\Objects\KeyPair;
use Socialbox\Objects\RpcError;
use Socialbox\Objects\RpcRequest;
class SocialClient extends RpcClient
{
/**
* Constructs a new instance with the specified domain.
*
* @param string $domain The domain to be set for the instance.
* @throws ResolutionException
*/
public function __construct(string $domain)
{
parent::__construct($domain);
}
/**
* Creates a new session using the provided key pair.
*
* @param KeyPair $keyPair The key pair to be used for creating the session.
* @return string The UUID of the created session.
* @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.
*/
public function createSession(KeyPair $keyPair): string
{
$response = $this->sendRequest(new RpcRequest('createSession', Utilities::randomCrc32(), [
'public_key' => $keyPair->getPublicKey()
]));
if($response === null)
{
throw new RpcException('Failed to create the session, no response received');
}
if($response instanceof RpcError)
{
throw RpcException::fromRpcError($response);
}
$this->setSessionUuid($response->getResult());
$this->setPrivateKey($keyPair->getPrivateKey());
return $response->getResult();
}
}

View file

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