- Add DISCOVERY_LOG_LEVEL=debug - Add DISCOVERY_SHOW_PROGRESS=true - Temporary changes for debugging InitializerProcessor fixes on production
143 lines
4.0 KiB
PHP
143 lines
4.0 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Framework\Encryption;
|
|
|
|
use App\Framework\Random\RandomGenerator;
|
|
use RuntimeException;
|
|
|
|
/**
|
|
* Basic encryption fallback for environments without OpenSSL
|
|
* WARNING: This is NOT cryptographically secure and should only be used for development!
|
|
*/
|
|
final readonly class BasicEncryption implements EncryptionInterface
|
|
{
|
|
private const METHOD = 'BASIC-XOR';
|
|
private const PREFIX = 'BASIC[';
|
|
private const SUFFIX = ']';
|
|
private const MIN_KEY_LENGTH = 16;
|
|
|
|
public function __construct(
|
|
private RandomGenerator $randomGenerator,
|
|
private string $encryptionKey
|
|
) {
|
|
if (! $this->validateKey($this->encryptionKey)) {
|
|
throw new RuntimeException('Encryption key must be at least ' . self::MIN_KEY_LENGTH . ' characters');
|
|
}
|
|
}
|
|
|
|
public function encrypt(string $data): string
|
|
{
|
|
// Generate a random salt
|
|
$salt = $this->randomGenerator->bytes(8);
|
|
$saltHex = bin2hex($salt);
|
|
|
|
// Create expanded key from base key + salt
|
|
$expandedKey = hash('sha256', $this->encryptionKey . $saltHex, true);
|
|
|
|
// Simple XOR encryption (NOT secure, only for fallback!)
|
|
$encrypted = $this->xorEncrypt($data, $expandedKey);
|
|
|
|
// Combine salt + encrypted data
|
|
$payload = base64_encode($salt . $encrypted);
|
|
|
|
return self::PREFIX . $payload . self::SUFFIX;
|
|
}
|
|
|
|
public function decrypt(string $encryptedData): string
|
|
{
|
|
if (! $this->isEncrypted($encryptedData)) {
|
|
throw new RuntimeException('Data does not appear to be encrypted with this method');
|
|
}
|
|
|
|
// Remove prefix and suffix
|
|
$payload = substr(
|
|
$encryptedData,
|
|
strlen(self::PREFIX),
|
|
-strlen(self::SUFFIX)
|
|
);
|
|
|
|
$data = base64_decode($payload);
|
|
if ($data === false) {
|
|
throw new RuntimeException('Invalid base64 encoding in encrypted data');
|
|
}
|
|
|
|
if (strlen($data) < 8) {
|
|
throw new RuntimeException('Encrypted data is too short to contain valid salt');
|
|
}
|
|
|
|
// Extract salt and encrypted data
|
|
$salt = substr($data, 0, 8);
|
|
$encrypted = substr($data, 8);
|
|
$saltHex = bin2hex($salt);
|
|
|
|
// Recreate expanded key
|
|
$expandedKey = hash('sha256', $this->encryptionKey . $saltHex, true);
|
|
|
|
// Decrypt using XOR
|
|
return $this->xorDecrypt($encrypted, $expandedKey);
|
|
}
|
|
|
|
public function isEncrypted(string $data): bool
|
|
{
|
|
return str_starts_with($data, self::PREFIX) && str_ends_with($data, self::SUFFIX);
|
|
}
|
|
|
|
public function getMethod(): string
|
|
{
|
|
return self::METHOD;
|
|
}
|
|
|
|
public function validateKey(string $key): bool
|
|
{
|
|
return strlen($key) >= self::MIN_KEY_LENGTH;
|
|
}
|
|
|
|
/**
|
|
* Simple XOR encryption (NOT cryptographically secure!)
|
|
*/
|
|
private function xorEncrypt(string $data, string $key): string
|
|
{
|
|
$result = '';
|
|
$keyLen = strlen($key);
|
|
$dataLen = strlen($data);
|
|
|
|
for ($i = 0; $i < $dataLen; $i++) {
|
|
$result .= $data[$i] ^ $key[$i % $keyLen];
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Simple XOR decryption (same as encryption for XOR)
|
|
*/
|
|
private function xorDecrypt(string $encrypted, string $key): string
|
|
{
|
|
return $this->xorEncrypt($encrypted, $key); // XOR is symmetric
|
|
}
|
|
|
|
/**
|
|
* Generate a new encryption key suitable for basic encryption
|
|
*/
|
|
public function generateKey(): string
|
|
{
|
|
return base64_encode($this->randomGenerator->bytes(self::MIN_KEY_LENGTH));
|
|
}
|
|
|
|
/**
|
|
* Get encryption metadata for debugging/logging
|
|
*/
|
|
public function getMetadata(): array
|
|
{
|
|
return [
|
|
'method' => self::METHOD,
|
|
'key_length' => self::MIN_KEY_LENGTH,
|
|
'prefix' => self::PREFIX,
|
|
'suffix' => self::SUFFIX,
|
|
'warning' => 'NOT CRYPTOGRAPHICALLY SECURE - DEVELOPMENT ONLY',
|
|
];
|
|
}
|
|
}
|