Changed Encryption to use LibSodium instead of OpenSSL, refactored many things and overall improved the code quality and performance with magic.
This commit is contained in:
parent
46ad03a54d
commit
367399f0fd
44 changed files with 2971 additions and 2016 deletions
|
@ -1,205 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Socialbox\Managers;
|
||||
|
||||
use PDOException;
|
||||
use Random\RandomException;
|
||||
use Socialbox\Classes\Configuration;
|
||||
use Socialbox\Classes\Database;
|
||||
use Socialbox\Classes\SecuredPassword;
|
||||
use Socialbox\Exceptions\CryptographyException;
|
||||
use Socialbox\Exceptions\DatabaseOperationException;
|
||||
use Socialbox\Objects\Database\EncryptionRecord;
|
||||
|
||||
class EncryptionRecordsManager
|
||||
{
|
||||
private const int KEY_LENGTH = 256; // Increased key length
|
||||
|
||||
/**
|
||||
* Retrieves the total count of records in the encryption_records table.
|
||||
*
|
||||
* @return int The number of records in the encryption_records table.
|
||||
* @throws DatabaseOperationException If a database operation error occurs while fetching the record count.
|
||||
*/
|
||||
public static function getRecordCount(): int
|
||||
{
|
||||
try
|
||||
{
|
||||
$stmt = Database::getConnection()->prepare('SELECT COUNT(*) FROM encryption_records');
|
||||
$stmt->execute();
|
||||
return $stmt->fetchColumn();
|
||||
}
|
||||
catch (PDOException $e)
|
||||
{
|
||||
throw new DatabaseOperationException('Failed to retrieve encryption record count', $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts a new encryption record into the encryption_records table.
|
||||
*
|
||||
* @param EncryptionRecord $record The encryption record to insert, containing data, IV, and tag.
|
||||
* @return void
|
||||
* @throws DatabaseOperationException If the insertion into the database fails.
|
||||
*/
|
||||
private static function insertRecord(EncryptionRecord $record): void
|
||||
{
|
||||
try
|
||||
{
|
||||
$stmt = Database::getConnection()->prepare('INSERT INTO encryption_records (data, iv, tag) VALUES (?, ?, ?)');
|
||||
|
||||
$data = $record->getData();
|
||||
$stmt->bindParam(1, $data);
|
||||
|
||||
$iv = $record->getIv();
|
||||
$stmt->bindParam(2, $iv);
|
||||
|
||||
$tag = $record->getTag();
|
||||
$stmt->bindParam(3, $tag);
|
||||
|
||||
$stmt->execute();
|
||||
}
|
||||
catch(PDOException $e)
|
||||
{
|
||||
|
||||
throw new DatabaseOperationException('Failed to insert encryption record into the database', $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a random encryption record from the database.
|
||||
*
|
||||
* @return EncryptionRecord An instance of EncryptionRecord containing the data of a randomly selected record.
|
||||
* @throws DatabaseOperationException If an error occurs while attempting to retrieve the record from the database.
|
||||
*/
|
||||
public static function getRandomRecord(): EncryptionRecord
|
||||
{
|
||||
try
|
||||
{
|
||||
$stmt = Database::getConnection()->prepare('SELECT * FROM encryption_records ORDER BY RAND() LIMIT 1');
|
||||
$stmt->execute();
|
||||
$data = $stmt->fetch();
|
||||
|
||||
return new EncryptionRecord($data);
|
||||
}
|
||||
catch(PDOException $e)
|
||||
{
|
||||
throw new DatabaseOperationException('Failed to retrieve a random encryption record', $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves all encryption records from the database.
|
||||
*
|
||||
* @return EncryptionRecord[] An array of EncryptionRecord instances, each representing a record from the database.
|
||||
* @throws DatabaseOperationException If an error occurs while attempting to retrieve the records from the database.
|
||||
*/
|
||||
public static function getAllRecords(): array
|
||||
{
|
||||
try
|
||||
{
|
||||
$stmt = Database::getConnection()->prepare('SELECT * FROM encryption_records');
|
||||
$stmt->execute();
|
||||
$data = $stmt->fetchAll();
|
||||
|
||||
$records = [];
|
||||
foreach ($data as $record)
|
||||
{
|
||||
$records[] = new EncryptionRecord($record);
|
||||
}
|
||||
|
||||
return $records;
|
||||
}
|
||||
catch(PDOException $e)
|
||||
{
|
||||
throw new DatabaseOperationException('Failed to retrieve all encryption records', $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates encryption records and inserts them into the database until the specified total count is reached.
|
||||
*
|
||||
* @param int $count The total number of encryption records desired in the database.
|
||||
* @return int The number of new records that were created and inserted.
|
||||
* @throws CryptographyException
|
||||
* @throws DatabaseOperationException
|
||||
*/
|
||||
public static function generateRecords(int $count): int
|
||||
{
|
||||
$currentCount = self::getRecordCount();
|
||||
if($currentCount >= $count)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
$created = 0;
|
||||
for($i = 0; $i < $count - $currentCount; $i++)
|
||||
{
|
||||
self::insertRecord(self::generateEncryptionRecord());
|
||||
$created++;
|
||||
}
|
||||
|
||||
return $created;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a new encryption record containing a key, pepper, and salt.
|
||||
*
|
||||
* @return EncryptionRecord An instance of EncryptionRecord containing an encrypted structure
|
||||
* with the generated key, pepper, and salt.
|
||||
* @throws CryptographyException If random byte generation fails during the creation of the encryption record.
|
||||
*/
|
||||
private static function generateEncryptionRecord(): EncryptionRecord
|
||||
{
|
||||
try
|
||||
{
|
||||
$key = random_bytes(self::KEY_LENGTH / 8);
|
||||
$pepper = bin2hex(random_bytes(SecuredPassword::PEPPER_LENGTH / 2));
|
||||
$salt = bin2hex(random_bytes(self::KEY_LENGTH / 16));
|
||||
|
||||
}
|
||||
catch (RandomException $e)
|
||||
{
|
||||
throw new CryptographyException("Random bytes generation failed", $e->getCode(), $e);
|
||||
}
|
||||
|
||||
return self::encrypt(['key' => base64_encode($key), 'pepper' => $pepper, 'salt' => $salt,]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypts the given vault item and returns an EncryptionRecord containing the encrypted data.
|
||||
*
|
||||
* @param array $vaultItem The associative array representing the vault item to be encrypted.
|
||||
* @return EncryptionRecord An instance of EncryptionRecord containing the encrypted vault data, initialization vector (IV), and authentication tag.
|
||||
* @throws CryptographyException If the initialization vector generation or vault encryption process fails.
|
||||
*/
|
||||
private static function encrypt(array $vaultItem): EncryptionRecord
|
||||
{
|
||||
$serializedVault = json_encode($vaultItem);
|
||||
|
||||
try
|
||||
{
|
||||
$iv = random_bytes(openssl_cipher_iv_length(SecuredPassword::ENCRYPTION_ALGORITHM));
|
||||
}
|
||||
catch (RandomException $e)
|
||||
{
|
||||
throw new CryptographyException("IV generation failed", $e->getCode(), $e);
|
||||
}
|
||||
$tag = null;
|
||||
|
||||
$encryptedVault = openssl_encrypt($serializedVault, SecuredPassword::ENCRYPTION_ALGORITHM,
|
||||
Configuration::getInstanceConfiguration()->getRandomEncryptionKey(), OPENSSL_RAW_DATA, $iv, $tag
|
||||
);
|
||||
|
||||
if ($encryptedVault === false)
|
||||
{
|
||||
throw new CryptographyException("Vault encryption failed");
|
||||
}
|
||||
|
||||
return new EncryptionRecord([
|
||||
'data' => base64_encode($encryptedVault),
|
||||
'iv' => base64_encode($iv),
|
||||
'tag' => base64_encode($tag),
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -2,13 +2,15 @@
|
|||
|
||||
namespace Socialbox\Managers;
|
||||
|
||||
use DateTime;
|
||||
use PDO;
|
||||
use PDOException;
|
||||
use Socialbox\Classes\Configuration;
|
||||
use Socialbox\Classes\Cryptography;
|
||||
use Socialbox\Classes\Database;
|
||||
use Socialbox\Classes\SecuredPassword;
|
||||
use Socialbox\Exceptions\CryptographyException;
|
||||
use Socialbox\Exceptions\DatabaseOperationException;
|
||||
use Socialbox\Objects\Database\RegisteredPeerRecord;
|
||||
use Socialbox\Objects\Database\SecurePasswordRecord;
|
||||
|
||||
class PasswordManager
|
||||
{
|
||||
|
@ -34,154 +36,139 @@
|
|||
|
||||
return $stmt->fetchColumn() > 0;
|
||||
}
|
||||
catch (\PDOException $e)
|
||||
catch (PDOException $e)
|
||||
{
|
||||
throw new DatabaseOperationException('An error occurred while checking the password usage in the database', $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a password for a given user or peer record by securely encrypting it
|
||||
* and storing it in the authentication_passwords database table.
|
||||
* Sets a secured password for the given peer UUID or registered peer record.
|
||||
*
|
||||
* @param string|RegisteredPeerRecord $peerUuid The UUID of the peer or an instance of RegisteredPeerRecord.
|
||||
* @param string $password The plaintext password to be securely stored.
|
||||
* @throws CryptographyException If an error occurs while securing the password.
|
||||
* @throws DatabaseOperationException If an error occurs while attempting to store the password in the database.
|
||||
* @throws \DateMalformedStringException If the updated timestamp cannot be formatted.
|
||||
* @param string|RegisteredPeerRecord $peerUuid The unique identifier or registered peer record of the user.
|
||||
* @param string $hash The plaintext password to be securely stored.
|
||||
* @return void
|
||||
* @throws DatabaseOperationException If an error occurs while storing the password in the database.
|
||||
* @throws CryptographyException If an error occurs during password encryption or hashing.
|
||||
*/
|
||||
public static function setPassword(string|RegisteredPeerRecord $peerUuid, string $password): void
|
||||
public static function setPassword(string|RegisteredPeerRecord $peerUuid, string $hash): void
|
||||
{
|
||||
if($peerUuid instanceof RegisteredPeerRecord)
|
||||
{
|
||||
$peerUuid = $peerUuid->getUuid();
|
||||
}
|
||||
|
||||
$encryptionRecord = EncryptionRecordsManager::getRandomRecord();
|
||||
$securedPassword = SecuredPassword::securePassword($peerUuid, $password, $encryptionRecord);
|
||||
|
||||
// Throws an exception if the hash is invalid
|
||||
Cryptography::validatePasswordHash($hash);
|
||||
|
||||
$encryptionKey = Configuration::getCryptographyConfiguration()->getRandomInternalEncryptionKey();
|
||||
$securedPassword = Cryptography::encryptMessage($hash, $encryptionKey, Configuration::getCryptographyConfiguration()->getEncryptionKeysAlgorithm());
|
||||
|
||||
try
|
||||
{
|
||||
$stmt = Database::getConnection()->prepare("INSERT INTO authentication_passwords (peer_uuid, iv, encrypted_password, encrypted_tag) VALUES (:peer_uuid, :iv, :encrypted_password, :encrypted_tag)");
|
||||
$stmt = Database::getConnection()->prepare("INSERT INTO authentication_passwords (peer_uuid, hash) VALUES (:peer_uuid, :hash)");
|
||||
$stmt->bindParam(":peer_uuid", $peerUuid);
|
||||
|
||||
$iv = $securedPassword->getIv();
|
||||
$stmt->bindParam(':iv', $iv);
|
||||
|
||||
$encryptedPassword = $securedPassword->getEncryptedPassword();
|
||||
$stmt->bindParam(':encrypted_password', $encryptedPassword);
|
||||
|
||||
$encryptedTag = $securedPassword->getEncryptedTag();
|
||||
$stmt->bindParam(':encrypted_tag', $encryptedTag);
|
||||
$stmt->bindParam(':hash', $securedPassword);
|
||||
|
||||
$stmt->execute();
|
||||
}
|
||||
catch(\PDOException $e)
|
||||
catch(PDOException $e)
|
||||
{
|
||||
throw new DatabaseOperationException(sprintf('Failed to set password for user %s', $peerUuid), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the password for a given peer identified by their UUID or a RegisteredPeerRecord.
|
||||
* Updates the secured password associated with the given peer UUID.
|
||||
*
|
||||
* @param string|RegisteredPeerRecord $peerUuid The UUID of the peer or an instance of RegisteredPeerRecord.
|
||||
* @param string $newPassword The new password to be set for the peer.
|
||||
* @throws CryptographyException If an error occurs while securing the new password.
|
||||
* @throws DatabaseOperationException If the update operation fails due to a database error.
|
||||
* @throws \DateMalformedStringException If the updated timestamp cannot be formatted.
|
||||
* @returns void
|
||||
* @param string|RegisteredPeerRecord $peerUuid The unique identifier or registered peer record of the user.
|
||||
* @param string $hash The new password to be stored securely.
|
||||
* @return void
|
||||
* @throws DatabaseOperationException If an error occurs while updating the password in the database.
|
||||
* @throws CryptographyException If an error occurs while encrypting the password or validating the hash.
|
||||
*/
|
||||
public static function updatePassword(string|RegisteredPeerRecord $peerUuid, string $newPassword): void
|
||||
public static function updatePassword(string|RegisteredPeerRecord $peerUuid, string $hash): void
|
||||
{
|
||||
if($peerUuid instanceof RegisteredPeerRecord)
|
||||
{
|
||||
$peerUuid = $peerUuid->getUuid();
|
||||
}
|
||||
|
||||
Cryptography::validatePasswordHash($hash);
|
||||
|
||||
$encryptionRecord = EncryptionRecordsManager::getRandomRecord();
|
||||
$securedPassword = SecuredPassword::securePassword($peerUuid, $newPassword, $encryptionRecord);
|
||||
$encryptionKey = Configuration::getCryptographyConfiguration()->getRandomInternalEncryptionKey();
|
||||
$securedPassword = Cryptography::encryptMessage($hash, $encryptionKey, Configuration::getCryptographyConfiguration()->getEncryptionKeysAlgorithm());
|
||||
|
||||
try
|
||||
{
|
||||
$stmt = Database::getConnection()->prepare("UPDATE authentication_passwords SET iv=:iv, encrypted_password=:encrypted_password, encrypted_tag=:encrypted_tag, updated=:updated WHERE peer_uuid=:peer_uuid");
|
||||
$stmt->bindParam(":peer_uuid", $peerUuid);
|
||||
|
||||
$iv = $securedPassword->getIv();
|
||||
$stmt->bindParam(':iv', $iv);
|
||||
|
||||
$encryptedPassword = $securedPassword->getEncryptedPassword();
|
||||
$stmt->bindParam(':encrypted_password', $encryptedPassword);
|
||||
|
||||
$encryptedTag = $securedPassword->getEncryptedTag();
|
||||
$stmt->bindParam(':encrypted_tag', $encryptedTag);
|
||||
|
||||
$updated = $securedPassword->getUpdated()->format('Y-m-d H:i:s');
|
||||
$stmt = Database::getConnection()->prepare("UPDATE authentication_passwords SET hash=:hash, updated=:updated WHERE peer_uuid=:peer_uuid");
|
||||
$updated = (new DateTime())->setTimestamp(time());
|
||||
$stmt->bindParam(':hash', $securedPassword);
|
||||
$stmt->bindParam(':updated', $updated);
|
||||
$stmt->bindParam(':peer_uuid', $peerUuid);
|
||||
|
||||
$stmt->execute();
|
||||
}
|
||||
catch(\PDOException $e)
|
||||
catch(PDOException $e)
|
||||
{
|
||||
throw new DatabaseOperationException(sprintf('Failed to update password for user %s', $peerUuid), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the password record associated with the given peer UUID.
|
||||
* Verifies a given password against a stored password hash for a specific peer.
|
||||
*
|
||||
* @param string|RegisteredPeerRecord $peerUuid The UUID of the peer or an instance of RegisteredPeerRecord.
|
||||
* @return SecurePasswordRecord|null Returns a SecurePasswordRecord if found, or null if no record is present.
|
||||
* @throws DatabaseOperationException If a database operation error occurs during the retrieval process.
|
||||
* @param string|RegisteredPeerRecord $peerUuid The unique identifier of the peer, or an instance of RegisteredPeerRecord.
|
||||
* @param string $hash The password hash to verify.
|
||||
* @return bool Returns true if the password matches the stored hash; false otherwise.
|
||||
* @throws CryptographyException If the password hash is invalid or an error occurs during the cryptographic operation.
|
||||
* @throws DatabaseOperationException If an error occurs during the database operation.
|
||||
*/
|
||||
private static function getPassword(string|RegisteredPeerRecord $peerUuid): ?SecurePasswordRecord
|
||||
public static function verifyPassword(string|RegisteredPeerRecord $peerUuid, string $hash): bool
|
||||
{
|
||||
if($peerUuid instanceof RegisteredPeerRecord)
|
||||
{
|
||||
$peerUuid = $peerUuid->getUuid();
|
||||
}
|
||||
|
||||
Cryptography::validatePasswordHash($hash);
|
||||
|
||||
try
|
||||
{
|
||||
$statement = Database::getConnection()->prepare("SELECT * FROM authentication_passwords WHERE peer_uuid=:peer_uuid LIMIT 1");
|
||||
$statement->bindParam(':peer_uuid', $peerUuid);
|
||||
$stmt = Database::getConnection()->prepare('SELECT hash FROM authentication_passwords WHERE peer_uuid=:uuid');
|
||||
$stmt->bindParam(':uuid', $peerUuid);
|
||||
$stmt->execute();
|
||||
|
||||
$statement->execute();
|
||||
$data = $statement->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if ($data === false)
|
||||
$record = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
if($record === false)
|
||||
{
|
||||
return null;
|
||||
return false;
|
||||
}
|
||||
|
||||
return SecurePasswordRecord::fromArray($data);
|
||||
}
|
||||
catch(\PDOException $e)
|
||||
{
|
||||
throw new DatabaseOperationException(sprintf('Failed to retrieve password record for user %s', $peerUuid), $e);
|
||||
}
|
||||
}
|
||||
$encryptedHash = $record['hash'];
|
||||
$decryptedHash = null;
|
||||
foreach(Configuration::getCryptographyConfiguration()->getInternalEncryptionKeys() as $key)
|
||||
{
|
||||
try
|
||||
{
|
||||
$decryptedHash = Cryptography::decryptMessage($encryptedHash, $key, Configuration::getCryptographyConfiguration()->getEncryptionKeysAlgorithm());
|
||||
}
|
||||
catch(CryptographyException)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies if the provided password matches the secured password associated with the given peer UUID.
|
||||
*
|
||||
* @param string|RegisteredPeerRecord $peerUuid The unique identifier or registered peer record of the user.
|
||||
* @param string $password The password to be verified.
|
||||
* @return bool Returns true if the password is verified successfully; otherwise, false.
|
||||
* @throws DatabaseOperationException If an error occurs while retrieving the password record from the database.
|
||||
* @throws CryptographyException If an error occurs while verifying the password.
|
||||
*/
|
||||
public static function verifyPassword(string|RegisteredPeerRecord $peerUuid, string $password): bool
|
||||
{
|
||||
$securedPassword = self::getPassword($peerUuid);
|
||||
if($securedPassword === null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if($decryptedHash === null)
|
||||
{
|
||||
throw new CryptographyException('Cannot decrypt hashed password');
|
||||
}
|
||||
|
||||
$encryptionRecords = EncryptionRecordsManager::getAllRecords();
|
||||
return SecuredPassword::verifyPassword($password, $securedPassword, $encryptionRecords);
|
||||
return Cryptography::verifyPassword($hash, $decryptedHash);
|
||||
}
|
||||
catch(PDOException $e)
|
||||
{
|
||||
throw new DatabaseOperationException('An error occurred while verifying the password', $e);
|
||||
}
|
||||
}
|
||||
}
|
184
src/Socialbox/Managers/ResolvedDnsRecordsManager.php
Normal file
184
src/Socialbox/Managers/ResolvedDnsRecordsManager.php
Normal file
|
@ -0,0 +1,184 @@
|
|||
<?php
|
||||
|
||||
namespace Socialbox\Managers;
|
||||
|
||||
use DateTime;
|
||||
use Exception;
|
||||
use PDOException;
|
||||
use Socialbox\Classes\Database;
|
||||
use Socialbox\Exceptions\DatabaseOperationException;
|
||||
use Socialbox\Objects\DnsRecord;
|
||||
|
||||
class ResolvedDnsRecordsManager
|
||||
{
|
||||
/**
|
||||
* Checks whether a resolved server record exists in the database for the provided domain.
|
||||
*
|
||||
* @param string $domain The domain name to check for existence in the resolved records.
|
||||
* @return bool True if the resolved server record exists, otherwise false.
|
||||
* @throws DatabaseOperationException If the process encounters a database error.
|
||||
*/
|
||||
public static function resolvedServerExists(string $domain): bool
|
||||
{
|
||||
try
|
||||
{
|
||||
$statement = Database::getConnection()->prepare("SELECT COUNT(*) FROM resolved_dns_records 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 record from the database for the provided domain.
|
||||
*
|
||||
* @param string $domain The domain name of the resolved server to be deleted.
|
||||
* @return void
|
||||
* @throws DatabaseOperationException If the deletion process encounters a database error.
|
||||
*/
|
||||
public static function deleteResolvedServer(string $domain): void
|
||||
{
|
||||
try
|
||||
{
|
||||
$statement = Database::getConnection()->prepare("DELETE FROM resolved_dns_records 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 timestamp of a resolved server from the database for a given domain.
|
||||
*
|
||||
* This method queries the database to fetch the timestamp indicating when the resolved server
|
||||
* associated with the specified domain was last updated.
|
||||
*
|
||||
* @param string $domain The domain name for which the last updated timestamp is to be retrieved.
|
||||
* @return DateTime The DateTime object representing the last updated timestamp of the resolved server.
|
||||
*
|
||||
* @throws DatabaseOperationException If the operation to retrieve the updated timestamp from the
|
||||
* database fails.
|
||||
*/
|
||||
public static function getResolvedServerUpdated(string $domain): DateTime
|
||||
{
|
||||
try
|
||||
{
|
||||
$statement = Database::getConnection()->prepare("SELECT updated FROM resolved_dns_records 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 a DNS record for the specified domain from the database.
|
||||
*
|
||||
* This method fetches the DNS record details, such as the RPC endpoint, public key,
|
||||
* and expiration details, associated with the provided domain. If no record is found,
|
||||
* it returns null.
|
||||
*
|
||||
* @param string $domain The domain name for which the DNS record is to be retrieved.
|
||||
* @return DnsRecord|null The DNS record object if found, or null if no record exists for the given domain.
|
||||
*
|
||||
* @throws DatabaseOperationException If the operation to retrieve the DNS record from
|
||||
* the database fails.
|
||||
*/
|
||||
public static function getDnsRecord(string $domain): ?DnsRecord
|
||||
{
|
||||
try
|
||||
{
|
||||
$statement = Database::getConnection()->prepare("SELECT * FROM resolved_dns_records WHERE domain=?");
|
||||
$statement->bindParam(1, $domain);
|
||||
$statement->execute();
|
||||
$result = $statement->fetch();
|
||||
|
||||
if($result === false)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return DnsRecord::fromArray([
|
||||
'rpc_endpoint' => $result['rpc_endpoint'],
|
||||
'public_key' => $result['public_key'],
|
||||
'expires' => $result['expires']
|
||||
]);
|
||||
}
|
||||
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 based on the provided domain and DNS record.
|
||||
*
|
||||
* If a resolved server for the given domain already exists in the database, the server's details
|
||||
* will be updated. Otherwise, a new record will be inserted into the database.
|
||||
*
|
||||
* @param string $domain The domain name associated with the resolved server.
|
||||
* @param DnsRecord $dnsRecord An object containing DNS record details such as the RPC endpoint,
|
||||
* public key, and expiration details.
|
||||
* @return void
|
||||
* @throws DatabaseOperationException If the operation to add or update the resolved server in
|
||||
* the database fails.
|
||||
*/
|
||||
public static function addResolvedServer(string $domain, DnsRecord $dnsRecord): void
|
||||
{
|
||||
$endpoint = $dnsRecord->getRpcEndpoint();
|
||||
$publicKey = $dnsRecord->getPublicSigningKey();
|
||||
|
||||
if(self::resolvedServerExists($domain))
|
||||
{
|
||||
$statement = Database::getConnection()->prepare("UPDATE resolved_dns_records SET rpc_endpoint=?, public_key=?, expires=?, updated=? WHERE domain=?");
|
||||
$statement->bindParam(1, $endpoint);
|
||||
$statement->bindParam(2, $publicKey);
|
||||
$expires = (new DateTime())->setTimestamp($dnsRecord->getExpires());
|
||||
$statement->bindParam(3, $expires);
|
||||
$updated = new DateTime();
|
||||
$statement->bindParam(4, $updated);
|
||||
$statement->bindParam(5, $domain);
|
||||
$statement->execute();
|
||||
|
||||
if($statement->rowCount() === 0)
|
||||
{
|
||||
throw new DatabaseOperationException('Failed to update a resolved server in the database');
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
$statement = Database::getConnection()->prepare("INSERT INTO resolved_dns_records (domain, rpc_endpoint, public_key, expires, updated) VALUES (?, ?, ?, ?, ?)");
|
||||
$statement->bindParam(1, $domain);
|
||||
$statement->bindParam(2, $endpoint);
|
||||
$statement->bindParam(3, $publicKey);
|
||||
$expires = (new DateTime())->setTimestamp($dnsRecord->getExpires());
|
||||
$statement->bindParam(4, $expires);
|
||||
$updated = new DateTime();
|
||||
$statement->bindParam(5, $updated);
|
||||
$statement->execute();
|
||||
|
||||
if($statement->rowCount() === 0)
|
||||
{
|
||||
throw new DatabaseOperationException('Failed to add a resolved server to the database');
|
||||
}
|
||||
}
|
||||
catch(PDOException $e)
|
||||
{
|
||||
throw new DatabaseOperationException('Failed to add a resolved server to the database', $e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,152 +0,0 @@
|
|||
<?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|null The resolved server record associated with the given domain.
|
||||
* @throws DatabaseOperationException If there is an error retrieving the resolved server record from the database.
|
||||
* @throws \DateMalformedStringException If the date string is malformed.
|
||||
*/
|
||||
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();
|
||||
|
||||
if($result === false)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return ResolvedServerRecord::fromArray($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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -19,34 +19,27 @@
|
|||
use Socialbox\Exceptions\StandardException;
|
||||
use Socialbox\Objects\Database\RegisteredPeerRecord;
|
||||
use Socialbox\Objects\Database\SessionRecord;
|
||||
use Socialbox\Objects\KeyPair;
|
||||
use Symfony\Component\Uid\Uuid;
|
||||
|
||||
class SessionManager
|
||||
{
|
||||
/**
|
||||
* Creates a new session with the given public key.
|
||||
*
|
||||
* @param string $publicKey The public key to associate with the new session.
|
||||
* @param RegisteredPeerRecord $peer
|
||||
*
|
||||
* @throws InvalidArgumentException If the public key is empty or invalid.
|
||||
* @throws DatabaseOperationException If there is an error while creating the session in the database.
|
||||
*/
|
||||
public static function createSession(string $publicKey, RegisteredPeerRecord $peer, string $clientName, string $clientVersion): string
|
||||
public static function createSession(RegisteredPeerRecord $peer, string $clientName, string $clientVersion, string $clientPublicSigningKey, string $clientPublicEncryptionKey, KeyPair $serverEncryptionKeyPair): string
|
||||
{
|
||||
if($publicKey === '')
|
||||
if($clientPublicSigningKey === '' || Cryptography::validatePublicSigningKey($clientPublicSigningKey) === false)
|
||||
{
|
||||
throw new InvalidArgumentException('The public key cannot be empty');
|
||||
throw new InvalidArgumentException('The public key is not a valid Ed25519 public key');
|
||||
}
|
||||
|
||||
if(!Cryptography::validatePublicKey($publicKey))
|
||||
if($clientPublicEncryptionKey === '' || Cryptography::validatePublicEncryptionKey($clientPublicEncryptionKey) === false)
|
||||
{
|
||||
throw new InvalidArgumentException('The given public key is invalid');
|
||||
throw new InvalidArgumentException('The public key is not a valid X25519 public key');
|
||||
}
|
||||
|
||||
$uuid = Uuid::v4()->toRfc4122();
|
||||
$flags = [];
|
||||
|
||||
// TODO: Update this to support `host` peers
|
||||
if($peer->isEnabled())
|
||||
{
|
||||
$flags[] = SessionFlags::AUTHENTICATION_REQUIRED;
|
||||
|
@ -119,13 +112,18 @@
|
|||
|
||||
try
|
||||
{
|
||||
$statement = Database::getConnection()->prepare("INSERT INTO sessions (uuid, peer_uuid, client_name, client_version, public_key, flags) VALUES (?, ?, ?, ?, ?, ?)");
|
||||
$statement->bindParam(1, $uuid);
|
||||
$statement->bindParam(2, $peerUuid);
|
||||
$statement->bindParam(3, $clientName);
|
||||
$statement->bindParam(4, $clientVersion);
|
||||
$statement->bindParam(5, $publicKey);
|
||||
$statement->bindParam(6, $implodedFlags);
|
||||
$statement = Database::getConnection()->prepare("INSERT INTO sessions (uuid, peer_uuid, client_name, client_version, client_public_signing_key, client_public_encryption_key, server_public_encryption_key, server_private_encryption_key, flags) VALUES (:uuid, :peer_uuid, :client_name, :client_version, :client_public_signing_key, :client_public_encryption_key, :server_public_encryption_key, :server_private_encryption_key, :flags)");
|
||||
$statement->bindParam(':uuid', $uuid);
|
||||
$statement->bindParam(':peer_uuid', $peerUuid);
|
||||
$statement->bindParam(':client_name', $clientName);
|
||||
$statement->bindParam(':client_version', $clientVersion);
|
||||
$statement->bindParam(':client_public_signing_key', $clientPublicSigningKey);
|
||||
$statement->bindParam(':client_public_encryption_key', $clientPublicEncryptionKey);
|
||||
$serverPublicEncryptionKey = $serverEncryptionKeyPair->getPublicKey();
|
||||
$statement->bindParam(':server_public_encryption_key', $serverPublicEncryptionKey);
|
||||
$serverPrivateEncryptionKey = $serverEncryptionKeyPair->getPrivateKey();
|
||||
$statement->bindParam(':server_private_encryption_key', $serverPrivateEncryptionKey);
|
||||
$statement->bindParam(':flags', $implodedFlags);
|
||||
$statement->execute();
|
||||
}
|
||||
catch(PDOException $e)
|
||||
|
@ -186,7 +184,6 @@
|
|||
|
||||
// Convert the timestamp fields to DateTime objects
|
||||
$data['created'] = new DateTime($data['created']);
|
||||
|
||||
if(isset($data['last_request']) && $data['last_request'] !== null)
|
||||
{
|
||||
$data['last_request'] = new DateTime($data['last_request']);
|
||||
|
@ -205,53 +202,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the authenticated peer associated with the given session UUID.
|
||||
*
|
||||
* @param string $uuid The UUID of the session to update.
|
||||
* @param RegisteredPeerRecord|string $registeredPeerUuid
|
||||
* @return void
|
||||
* @throws DatabaseOperationException
|
||||
*/
|
||||
public static function updatePeer(string $uuid, RegisteredPeerRecord|string $registeredPeerUuid): void
|
||||
{
|
||||
if($registeredPeerUuid instanceof RegisteredPeerRecord)
|
||||
{
|
||||
$registeredPeerUuid = $registeredPeerUuid->getUuid();
|
||||
}
|
||||
|
||||
Logger::getLogger()->verbose(sprintf("Assigning peer %s to session %s", $registeredPeerUuid, $uuid));
|
||||
|
||||
try
|
||||
{
|
||||
$statement = Database::getConnection()->prepare("UPDATE sessions SET peer_uuid=? WHERE uuid=?");
|
||||
$statement->bindParam(1, $registeredPeerUuid);
|
||||
$statement->bindParam(2, $uuid);
|
||||
$statement->execute();
|
||||
}
|
||||
catch (PDOException $e)
|
||||
{
|
||||
throw new DatabaseOperationException('Failed to update authenticated peer', $e);
|
||||
}
|
||||
}
|
||||
|
||||
public static function updateAuthentication(string $uuid, bool $authenticated): void
|
||||
{
|
||||
Logger::getLogger()->verbose(sprintf("Marking session %s as authenticated: %s", $uuid, $authenticated ? 'true' : 'false'));
|
||||
|
||||
try
|
||||
{
|
||||
$statement = Database::getConnection()->prepare("UPDATE sessions SET authenticated=? WHERE uuid=?");
|
||||
$statement->bindParam(1, $authenticated);
|
||||
$statement->bindParam(2, $uuid);
|
||||
$statement->execute();
|
||||
}
|
||||
catch (PDOException $e)
|
||||
{
|
||||
throw new DatabaseOperationException('Failed to update authenticated peer', $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the last request timestamp for a given session by its UUID.
|
||||
*
|
||||
|
@ -305,24 +255,28 @@
|
|||
}
|
||||
|
||||
/**
|
||||
* Updates the encryption key for the specified session.
|
||||
* Updates the encryption keys and session state for a specific session UUID in the database.
|
||||
*
|
||||
* @param string $uuid The unique identifier of the session for which the encryption key is to be set.
|
||||
* @param string $encryptionKey The new encryption key to be assigned.
|
||||
* @param string $uuid The unique identifier for the session to update.
|
||||
* @param string $privateSharedSecret The private shared secret to secure communication.
|
||||
* @param string $clientEncryptionKey The client's encryption key used for transport security.
|
||||
* @param string $serverEncryptionKey The server's encryption key used for transport security.
|
||||
* @return void
|
||||
* @throws DatabaseOperationException If the database operation fails.
|
||||
* @throws DatabaseOperationException If an error occurs during the database operation.
|
||||
*/
|
||||
public static function setEncryptionKey(string $uuid, string $encryptionKey): void
|
||||
public static function setEncryptionKeys(string $uuid, string $privateSharedSecret, string $clientEncryptionKey, string $serverEncryptionKey): void
|
||||
{
|
||||
Logger::getLogger()->verbose(sprintf('Setting the encryption key for %s', $uuid));
|
||||
|
||||
try
|
||||
{
|
||||
$state_value = SessionState::ACTIVE->value;
|
||||
$statement = Database::getConnection()->prepare('UPDATE sessions SET state=?, encryption_key=? WHERE uuid=?');
|
||||
$statement->bindParam(1, $state_value);
|
||||
$statement->bindParam(2, $encryptionKey);
|
||||
$statement->bindParam(3, $uuid);
|
||||
$statement = Database::getConnection()->prepare('UPDATE sessions SET state=:state, private_shared_secret=:private_shared_secret, client_transport_encryption_key=:client_transport_encryption_key, server_transport_encryption_key=:server_transport_encryption_key WHERE uuid=:uuid');
|
||||
$statement->bindParam(':state', $state_value);
|
||||
$statement->bindParam(':private_shared_secret', $privateSharedSecret);
|
||||
$statement->bindParam(':client_transport_encryption_key', $clientEncryptionKey);
|
||||
$statement->bindParam(':server_transport_encryption_key', $serverEncryptionKey);
|
||||
$statement->bindParam(':uuid', $uuid);
|
||||
|
||||
$statement->execute();
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue