This commit is contained in:
2025-12-27 02:02:42 +01:00
parent 8139b1b47e
commit 54e6e10f4f
7 changed files with 164 additions and 18 deletions

68
src/App/Crypto.php Normal file
View File

@@ -0,0 +1,68 @@
<?php
declare(strict_types=1);
namespace App;
final class Crypto
{
private string $key;
public function __construct(Config $config)
{
if (!extension_loaded('sodium')) {
throw new \RuntimeException('libsodium extension not available');
}
$raw = getenv('DATA_KEY') ?: '';
$raw = trim($raw);
if ($raw === '') {
throw new \RuntimeException('DATA_KEY env not set');
}
// base64?
if (str_starts_with($raw, 'base64:')) {
$raw = substr($raw, 7);
}
$decoded = base64_decode($raw, true);
if ($decoded !== false && strlen($decoded) >= SODIUM_CRYPTO_AEAD_XCHACHA20POLY1305_IETF_KEYBYTES) {
$raw = $decoded;
} elseif (ctype_xdigit($raw) && strlen($raw) >= SODIUM_CRYPTO_AEAD_XCHACHA20POLY1305_IETF_KEYBYTES * 2) {
$raw = hex2bin($raw);
}
if (strlen($raw) < SODIUM_CRYPTO_AEAD_XCHACHA20POLY1305_IETF_KEYBYTES) {
throw new \RuntimeException('DATA_KEY invalid length');
}
$this->key = substr($raw, 0, SODIUM_CRYPTO_AEAD_XCHACHA20POLY1305_IETF_KEYBYTES);
}
public function encrypt(string $plaintext): string
{
if ($plaintext === '') {
return '';
}
$nonce = random_bytes(SODIUM_CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES);
$cipher = sodium_crypto_aead_xchacha20poly1305_ietf_encrypt($plaintext, '', $nonce, $this->key);
return base64_encode($nonce . $cipher);
}
public function decrypt(?string $blob): string
{
if ($blob === null || $blob === '') {
return '';
}
$raw = base64_decode($blob, true);
if ($raw === false || strlen($raw) <= SODIUM_CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES) {
return '';
}
$nonce = substr($raw, 0, SODIUM_CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES);
$cipher = substr($raw, SODIUM_CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES);
try {
$plain = sodium_crypto_aead_xchacha20poly1305_ietf_decrypt($cipher, '', $nonce, $this->key);
return $plain === false ? '' : $plain;
} catch (\Throwable) {
return '';
}
}
}