Files
michaelschiemer/src/Framework/Encryption/BasicEncryption.php
Michael Schiemer 55a330b223 Enable Discovery debug logging for production troubleshooting
- Add DISCOVERY_LOG_LEVEL=debug
- Add DISCOVERY_SHOW_PROGRESS=true
- Temporary changes for debugging InitializerProcessor fixes on production
2025-08-11 20:13:26 +02:00

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',
];
}
}