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

Closed
netkas wants to merge 421 commits from master into dev
3 changed files with 494 additions and 16 deletions
Showing only changes of commit 02b3f1931c - Show all commits

View file

@ -0,0 +1,263 @@
<?php
namespace Socialbox\Classes\StandardMethods\EncryptionChannel;
use Exception;
use InvalidArgumentException;
use Socialbox\Abstracts\Method;
use Socialbox\Classes\Cryptography;
use Socialbox\Classes\Logger;
use Socialbox\Classes\Validator;
use Socialbox\Enums\StandardError;
use Socialbox\Exceptions\DatabaseOperationException;
use Socialbox\Exceptions\RpcException;
use Socialbox\Exceptions\Standard\InvalidRpcArgumentException;
use Socialbox\Exceptions\Standard\MissingRpcArgumentException;
use Socialbox\Exceptions\Standard\StandardRpcException;
use Socialbox\Interfaces\SerializableInterface;
use Socialbox\Managers\EncryptionChannelManager;
use Socialbox\Managers\RegisteredPeerManager;
use Socialbox\Objects\ClientRequest;
use Socialbox\Objects\PeerAddress;
use Socialbox\Objects\RpcRequest;
use Socialbox\Socialbox;
class EncryptionCreateChannel extends Method
{
/**
* @inheritDoc
*/
public static function execute(ClientRequest $request, RpcRequest $rpcRequest): ?SerializableInterface
{
try
{
if ($request->isExternal())
{
return self::handleExternal($request, $rpcRequest);
}
return self::handleInternal($request, $rpcRequest);
}
catch (DatabaseOperationException $e)
{
throw new StandardRpcException('An error occurred while checking the request type', StandardError::INTERNAL_SERVER_ERROR, $e);
}
}
/**
* @param ClientRequest $request
* @param RpcRequest $rpcRequest
* @return SerializableInterface|null
* @throws StandardRpcException
*/
private static function handleInternal(ClientRequest $request, RpcRequest $rpcRequest): ?SerializableInterface
{
if(!$rpcRequest->containsParameter('receiving_peer'))
{
throw new MissingRpcArgumentException('receiving_peer');
}
elseif(!Validator::validatePeerAddress($rpcRequest->getParameter('receiving_peer')))
{
throw new InvalidRpcArgumentException('receiving_peer', 'Invalid Receiving Peer Address');
}
if(!$rpcRequest->containsParameter('public_encryption_key'))
{
throw new MissingRpcArgumentException('public_encryption_key');
}
elseif(!Cryptography::validatePublicEncryptionKey($rpcRequest->getParameter('public_encryption_key')))
{
throw new InvalidRpcArgumentException('public_encryption_key', 'The given public encryption key is invalid');
}
$receivingPeerAddress = PeerAddress::fromAddress($rpcRequest->getParameter('receiving_peer'));
Socialbox::resolvePeer($receivingPeerAddress);
try
{
$callingPeer = $request->getPeer();
$callingPeerAddress = PeerAddress::fromAddress($callingPeer->getAddress());
}
catch (DatabaseOperationException $e)
{
throw new StandardRpcException('There was an error while trying to obtain the calling peer', StandardError::INTERNAL_SERVER_ERROR, $e);
}
try
{
$uuid = EncryptionChannelManager::createChannel(
callingPeer: $callingPeerAddress,
receivingPeer: $receivingPeerAddress,
callingPublicEncryptionKey: $rpcRequest->getParameter('public_encryption_ke')
);
}
catch(InvalidArgumentException $e)
{
throw new InvalidRpcArgumentException(null, $e);
}
catch (DatabaseOperationException $e)
{
throw new StandardRpcException('There was an error while trying to create a new encryption channel', StandardError::INTERNAL_SERVER_ERROR, $e);
}
// If the receiver is in an external server, we must notify the external server as a client
if($receivingPeerAddress->isExternal())
{
// Obtain the RPC Client, if for any reason it fails; we set the encryption channel as declined.
try
{
$rpcClient = Socialbox::getExternalSession($receivingPeerAddress->getDomain());
$externalUuid = $rpcClient->encryptionCreateChannel(
receivingPeer: $receivingPeerAddress,
publicEncryptionKey: $rpcRequest->getParameter('public_encryption_key'),
channelUuid: $uuid,
identifiedAs: $callingPeerAddress
);
}
catch(Exception $e)
{
try
{
EncryptionChannelManager::declineChannel($uuid, true);
}
catch(DatabaseOperationException $e)
{
Logger::getLogger()->error('Error declining channel as server', $e);
}
if($e instanceof RpcException)
{
throw StandardRpcException::fromRpcException($e);
}
throw new StandardRpcException('There was an error while trying to notify the external server of the encryption channel', StandardError::INTERNAL_SERVER_ERROR, $e);
}
// Check for sanity reasons
if($externalUuid !== $uuid)
{
try
{
EncryptionChannelManager::declineChannel($uuid, true);
}
catch(DatabaseOperationException $e)
{
Logger::getLogger()->error('Error declining channel as server', $e);
}
throw new StandardRpcException('The external server did not return the correct UUID', StandardError::UUID_MISMATCH);
}
}
return null;
}
/**
* @param ClientRequest $request
* @param RpcRequest $rpcRequest
* @return SerializableInterface|null
* @throws StandardRpcException
*/
private static function handleExternal(ClientRequest $request, RpcRequest $rpcRequest): ?SerializableInterface
{
if($request->getIdentifyAs() === null)
{
return $rpcRequest->produceError(StandardError::BAD_REQUEST, 'Missing IdentifyAs request header');
}
$callingPeer = $request->getIdentifyAs();
Socialbox::resolvePeer($callingPeer);
if(!$rpcRequest->containsParameter('receiving_peer'))
{
throw new MissingRpcArgumentException('receiving_peer');
}
elseif(!Validator::validatePeerAddress($rpcRequest->getParameter('receiving_peer')))
{
throw new InvalidRpcArgumentException('receiving_peer', 'Invalid Receiving Peer Address');
}
if(!$rpcRequest->containsParameter('public_encryption_key'))
{
throw new MissingRpcArgumentException('public_encryption_key');
}
elseif(!Cryptography::validatePublicEncryptionKey($rpcRequest->getParameter('public_encryption_key')))
{
throw new InvalidRpcArgumentException('public_encryption_key', 'The given public encryption key is invalid');
}
// Check for an additional required parameter 'channel_uuid'
if(!$rpcRequest->containsParameter('channel_uuid'))
{
throw new MissingRpcArgumentException('channel_uuid');
}
elseif(!Validator::validateUuid($rpcRequest->getParameter('channel_uuid')))
{
throw new InvalidRpcArgumentException('channel_uuid', 'The given UUID is not a valid UUID v4 format');
}
// Check if the UUID already is used on this server
try
{
if(EncryptionChannelManager::channelUuidExists($rpcRequest->getParameter('channel_uuid')))
{
return $rpcRequest->produceError(StandardError::UUID_CONFLICT, 'The given UUID already exists with another existing encryption channel on this server');
}
}
catch(DatabaseOperationException $e)
{
throw new StandardRpcException('There was an error while checking the existence of the channel UUID', StandardError::INTERNAL_SERVER_ERROR, $e);
}
$receivingPeerAddress = PeerAddress::fromAddress($rpcRequest->getParameter('receiving_peer'));
if($receivingPeerAddress->isExternal())
{
return $rpcRequest->produceError(StandardError::PEER_NOT_FOUND, 'The receiving peer does not belong to this server');
}
try
{
$receivingPeer = RegisteredPeerManager::getPeerByAddress($rpcRequest->getParameter('receiving_peer'));
}
catch (DatabaseOperationException $e)
{
throw new StandardRpcException('There was an error while trying to obtain the receiving peer', StandardError::INTERNAL_SERVER_ERROR, $e);
}
if($receivingPeer === null)
{
return $rpcRequest->produceError(StandardError::PEER_NOT_FOUND, 'The receiving peer does not exist on this server');
}
try
{
$uuid = EncryptionChannelManager::createChannel(
callingPeer: $callingPeer,
receivingPeer: $receivingPeerAddress,
callingPublicEncryptionKey: $rpcRequest->getParameter('public_encryption_key'),
channelUUid: $rpcRequest->getParameter('channel_uuid')
);
}
catch(DatabaseOperationException $e)
{
throw new StandardRpcException('There was an error while trying to create the encryption channel', StandardError::INTERNAL_SERVER_ERROR, $e);
}
if($uuid !== $rpcRequest->getParameter('channel_uuid'))
{
try
{
EncryptionChannelManager::declineChannel($rpcRequest->getParameter('channel_uuid'), true);
}
catch(DatabaseOperationException $e)
{
Logger::getLogger()->error('There was an error while trying to decline the encryption channel as a server', $e);
}
return $rpcRequest->produceError(StandardError::UUID_MISMATCH, 'The created UUID in the server does not match the UUID that was received');
}
return $rpcRequest->produceResponse($uuid);
}
}

View file

@ -18,9 +18,6 @@
use Socialbox\Classes\StandardMethods\Core\ResolvePeer;
use Socialbox\Classes\StandardMethods\Core\ResolveSignature;
use Socialbox\Classes\StandardMethods\Core\VerifySignature;
use Socialbox\Classes\StandardMethods\Encryption\EncryptionAcceptChannel;
use Socialbox\Classes\StandardMethods\Encryption\EncryptionCloseChannel;
use Socialbox\Classes\StandardMethods\Encryption\EncryptionCreateChannel;
use Socialbox\Classes\StandardMethods\ServerDocuments\AcceptCommunityGuidelines;
use Socialbox\Classes\StandardMethods\ServerDocuments\AcceptPrivacyPolicy;
use Socialbox\Classes\StandardMethods\ServerDocuments\AcceptTermsOfService;
@ -77,8 +74,17 @@
case GET_SESSION_STATE = 'getSessionState';
case PING = 'ping';
case RESOLVE_PEER = 'resolvePeer';
case RESOLVE_PEER_SIGNATURE = 'resolvePeerSignature';
case VERIFY_PEER_SIGNATURE = 'verifyPeerSignature';
case RESOLVE_SIGNATURE = 'resolveSignature';
case VERIFY_SIGNATURE = 'verifySignature';
// Encryption Channel Methods
case ENCRYPTION_ACCEPT_CHANNEL = 'encryptionAcceptChannel';
case ENCRYPTION_CHANNEL_EXISTS = 'encryptionChannelExists';
case ENCRYPTION_CHANNEL_SEND = 'encryptionChannelSend';
case ENCRYPTION_CLOSE_CHANNEL = 'encryptionCloseChannel';
case ENCRYPTION_CREATE_CHANNEL = 'encryptionCreateChannel';
case ENCRYPTION_DECLINE_CHANNEL = 'encryptionDeclineChannel';
case ENCRYPTION_GET_CHANNEL = 'encryptionGetChannel';
// ServerDocument Methods
case ACCEPT_COMMUNITY_GUIDELINES = 'acceptCommunityGuidelines';
@ -184,8 +190,8 @@
self::GET_SESSION_STATE => GetSessionState::execute($request, $rpcRequest),
self::PING => Ping::execute($request, $rpcRequest),
self::RESOLVE_PEER => ResolvePeer::execute($request, $rpcRequest),
self::RESOLVE_PEER_SIGNATURE => ResolveSignature::execute($request, $rpcRequest),
self::VERIFY_PEER_SIGNATURE => VerifySignature::execute($request, $rpcRequest),
self::RESOLVE_SIGNATURE => ResolveSignature::execute($request, $rpcRequest),
self::VERIFY_SIGNATURE => VerifySignature::execute($request, $rpcRequest),
// Server Document Methods
self::ACCEPT_PRIVACY_POLICY => AcceptPrivacyPolicy::execute($request, $rpcRequest),
@ -457,8 +463,8 @@
self::GET_SESSION_STATE,
self::PING,
self::RESOLVE_PEER,
self::RESOLVE_PEER_SIGNATURE,
self::VERIFY_PEER_SIGNATURE
self::RESOLVE_SIGNATURE,
self::VERIFY_SIGNATURE
];
}

View file

@ -16,12 +16,14 @@
use Socialbox\Exceptions\DatabaseOperationException;
use Socialbox\Exceptions\ResolutionException;
use Socialbox\Exceptions\RpcException;
use Socialbox\Objects\Client\EncryptionChannelInstance;
use Socialbox\Objects\Client\EncryptionChannelSecret;
use Socialbox\Objects\Client\ExportedSession;
use Socialbox\Objects\Client\SignatureKeyPair;
use Socialbox\Objects\PeerAddress;
use Socialbox\Objects\RpcRequest;
use Socialbox\Objects\Standard\Contact;
use Socialbox\Objects\Standard\EncryptionChannel;
use Socialbox\Objects\Standard\ImageCaptchaVerification;
use Socialbox\Objects\Standard\InformationFieldState;
use Socialbox\Objects\Standard\Peer;
@ -76,6 +78,113 @@
return $uuid;
}
/**
* Creates a new encryption channel with the given peer, generates a new encryption key pair and sends the public
* key to the receiving peer. The private key is stored locally and is never sent to the server.
*
* @param PeerAddress|string $receivingPeer The address of the peer to create the channel with
* @return string The UUID of the encryption channel
* @throws CryptographyException Thrown if there was an error while generating the encryption key pair
*/
public function newEncryptionChannel(string|PeerAddress $receivingPeer): string
{
if($receivingPeer instanceof PeerAddress)
{
$receivingPeer = $receivingPeer->getAddress();
}
$encryptionKeyPair = Cryptography::generateEncryptionKeyPair();
$encryptionChannelUuid = $this->encryptionCreateChannel($receivingPeer, $encryptionKeyPair->getPublicKey());
$this->addEncryptionChannelSecret(new EncryptionChannelSecret([
'channel_uuid' => $encryptionChannelUuid,
'receiver' => $receivingPeer,
'local_public_encryption_key' => $encryptionKeyPair->getPublicKey(),
'local_private_encryption_key' => $encryptionKeyPair->getPrivateKey()
]));
return $encryptionChannelUuid;
}
/**
* Waits for the encryption channel to be accepted by the receiving peer, returns True if the channel was accepted
* or False if the channel was not accepted within the timeout period.
*
* @param string $channelUuid
* @param int|null $timeout
* @return bool
*/
public function waitForEncryptionChannel(string $channelUuid, ?int $timeout=30): bool
{
if($this->getEncryptionChannelSecret($channelUuid) === null)
{
throw new InvalidArgumentException('Encryption Channel was not created with newEncryptionChannel() or defined with addEncryptionChannelSecret()');
}
$start = time();
while(true)
{
if($timeout !== null && time() - $start > $timeout)
{
break;
}
$encryptionChannel = $this->encryptionGetChannel($channelUuid);
if($encryptionChannel->getReceivingPublicEncryptionKey() !== null)
{
$this->getEncryptionChannelSecret($channelUuid)->setReceivingPublicEncryptionKey($encryptionChannel->getReceivingPublicEncryptionKey());
return true;
}
sleep(1);
}
return false;
}
/**
* Accepts an encryption channel with the given UUID, generates a new encryption key pair and sends the public key
* to the calling peer. The private key is stored locally and is never sent to the server.
*
* @param string $channelUuid The UUID of the encryption channel to accept
* @return bool Returns True if the channel was accepted
* @throws CryptographyException Thrown if there was an error while generating the encryption key pair
* @throws RpcException Thrown if there was an error with the RPC request
*/
public function acceptEncryptionChannel(string $channelUuid): bool
{
$encryptionChannel = $this->encryptionGetChannel($channelUuid);
$encryptionKeyPair = Cryptography::generateEncryptionKeyPair();
$this->encryptionAcceptChannel($channelUuid, $encryptionKeyPair->getPublicKey(), $encryptionChannel->getRecipient());
$this->addEncryptionChannelSecret(new EncryptionChannelSecret([
'channel_uuid' => $channelUuid,
'receiver' => $encryptionChannel->getCallingPeer(),
'local_public_encryption_key' => $encryptionKeyPair->getPublicKey(),
'local_private_encryption_key' => $encryptionKeyPair->getPrivateKey(),
'receiving_public_encryption_key' => $encryptionChannel->getCallingPublicEncryptionKey()
]));
return true;
}
/**
* Creates a new EncryptionChannelInstance object for the given channel UUID.
*
* @param string $channelUuid The UUID of the encryption channel
* @return EncryptionChannelInstance The EncryptionChannelInstance object
* @throws InvalidArgumentException Thrown if the encryption channel secret does not exist
*/
public function createEncryptionChannelInstance(string $channelUuid): EncryptionChannelInstance
{
if($this->getEncryptionChannelSecret($channelUuid) === null)
{
throw new InvalidArgumentException('Encryption Channel was not created with newEncryptionChannel() or defined with addEncryptionChannelSecret()');
}
$encryptionChannelSecret = $this->getEncryptionChannelSecret($channelUuid);
return new EncryptionChannelInstance($this, $encryptionChannelSecret);
}
/**
* Adds a new peer to the AddressBook, returns True upon success or False if the contact already exists in
* the address book.
@ -356,7 +465,7 @@
* @return Signature|null The signature as a Signature object, or null if the signature does not exist
* @throws RpcException Thrown if there was an error with the RPC request
*/
public function resolvePeerSignature(PeerAddress|string $peer, string $signatureUuid): ?Signature
public function resolveSignature(PeerAddress|string $peer, string $signatureUuid): ?Signature
{
if($peer instanceof PeerAddress)
{
@ -364,7 +473,7 @@
}
$result = $this->sendRequest(
new RpcRequest(StandardMethods::RESOLVE_PEER_SIGNATURE, parameters: [
new RpcRequest(StandardMethods::RESOLVE_SIGNATURE, parameters: [
'peer' => $peer,
'signature_uuid' => $signatureUuid
])
@ -393,7 +502,7 @@
* @return SignatureVerificationStatus the status of the verification
* @throws RpcException Thrown if there was an error with the RPC request
*/
public function verifyPeerSignature(PeerAddress|string $peer, string $signatureUuid, string $signaturePublicKey, string $signature, string $sha512, ?int $signatureTime=null): SignatureVerificationStatus
public function verifySignature(PeerAddress|string $peer, string $signatureUuid, string $signaturePublicKey, string $signature, string $sha512, ?int $signatureTime=null): SignatureVerificationStatus
{
if($peer instanceof PeerAddress)
{
@ -401,15 +510,115 @@
}
return SignatureVerificationStatus::tryFrom($this->sendRequest(
new RpcRequest(StandardMethods::VERIFY_PEER_SIGNATURE, parameters: [
new RpcRequest(StandardMethods::VERIFY_SIGNATURE, parameters: [
'peer' => $peer,
'signature_uuid' => $signatureUuid,
'signature_public_key' => $signaturePublicKey,
'signature' => $signature,
'sha512' => $sha512,
'signature_time' => $signatureTime
])
)->getResponse()->getResult()) ?? SignatureVerificationStatus::INVALID;
)->getResponse()->getResult()) ?? SignatureVerificationStatus::ERROR;
}
public function encryptionAcceptChannel(string $channelUuid, string $publicEncryptionKey, PeerAddress|string|null $identifiedAs=null): bool
{
if($identifiedAs instanceof PeerAddress)
{
$identifiedAs = $identifiedAs->getAddress();
}
return $this->sendRequest(
new RpcRequest(StandardMethods::ENCRYPTION_ACCEPT_CHANNEL, parameters: [
'channel_uuid' => $channelUuid,
'public_encryption_key' => $publicEncryptionKey
]), true, $identifiedAs
)->getResponse()->getResult();
}
public function encryptionChannelExists(string $channelUuid): bool
{
return $this->sendRequest(
new RpcRequest(StandardMethods::ENCRYPTION_CHANNEL_EXISTS, parameters: [
'channel_uuid' => $channelUuid
])
)->getResponse()->getResult();
}
public function encryptionChannelSend(string $channelUuid, string $checksum, string $data, PeerAddress|string|null $identifiedAs=null, ?string $messageUuid=null, ?int $timestamp=null): string
{
if($identifiedAs instanceof PeerAddress)
{
$identifiedAs = $identifiedAs->getAddress();
}
return $this->sendRequest(
new RpcRequest(StandardMethods::ENCRYPTION_CHANNEL_SEND, parameters: [
'channel_uuid' => $channelUuid,
'checksum' => $checksum,
'data' => $data,
'uuid' => $messageUuid,
'timestamp' => $timestamp
]), true, $identifiedAs
)->getResponse()->getResult();
}
public function encryptionCloseChannel(string $channelUuid, PeerAddress|string|null $identifiedAs=null): bool
{
if($identifiedAs instanceof PeerAddress)
{
$identifiedAs = $identifiedAs->getAddress();
}
return $this->sendRequest(
new RpcRequest(StandardMethods::ENCRYPTION_CLOSE_CHANNEL, parameters: [
'channel_uuid' => $channelUuid
]), true, $identifiedAs
)->getResponse()->getResult();
}
public function encryptionCreateChannel(string|PeerAddress $receivingPeer, string $publicEncryptionKey, ?string $channelUuid=null, PeerAddress|string|null $identifiedAs=null): string
{
if($receivingPeer instanceof PeerAddress)
{
$receivingPeer = $receivingPeer->getAddress();
}
if($identifiedAs instanceof PeerAddress)
{
$identifiedAs = $identifiedAs->getAddress();
}
return $this->sendRequest(
new RpcRequest(StandardMethods::ENCRYPTION_CREATE_CHANNEL, parameters: [
'receiving_peer' => $receivingPeer,
'public_encryption_key' => $publicEncryptionKey,
'channel_uuid' => $channelUuid
]), true, $identifiedAs
)->getResponse()->getResult();
}
public function encryptionDeclineChannel(string $channelUuid, PeerAddress|string|null $identifiedAs=null): bool
{
if($identifiedAs instanceof PeerAddress)
{
$identifiedAs = $identifiedAs->getAddress();
}
return $this->sendRequest(
new RpcRequest(StandardMethods::ENCRYPTION_DECLINE_CHANNEL, parameters: [
'channel_uuid' => $channelUuid
]), true, $identifiedAs
)->getResponse()->getResult();
}
public function encryptionGetChannel(string $channelUuid): EncryptionChannel
{
return new EncryptionChannel($this->sendRequest(
new RpcRequest(StandardMethods::ENCRYPTION_GET_CHANNEL, parameters: [
'channel_uuid' => $channelUuid
])
)->getResponse()->getResult());
}
/**