Files
papa-kind-treff.info/src/App/Crypto.php
Lars Gebhardt-Kusche f6cb211859
All checks were successful
Deploy / deploy (push) Successful in 58s
adasd
2026-07-30 00:04:51 +02:00

86 lines
2.9 KiB
PHP
Executable File

<?php
declare(strict_types=1);
namespace App;
final class Crypto
{
private const ENCRYPTED_PREFIX = 'enc:';
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 self::ENCRYPTED_PREFIX . base64_encode($nonce . $cipher);
}
public function decrypt(?string $blob): string
{
if ($blob === null || $blob === '') {
return '';
}
$payload = str_starts_with($blob, self::ENCRYPTED_PREFIX) ? substr($blob, strlen(self::ENCRYPTED_PREFIX)) : $blob;
$raw = base64_decode($payload, 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 '';
}
}
public static function looksEncrypted(?string $blob): bool
{
if ($blob === null || $blob === '') {
return false;
}
$payload = str_starts_with($blob, self::ENCRYPTED_PREFIX) ? substr($blob, strlen(self::ENCRYPTED_PREFIX)) : $blob;
$raw = base64_decode($payload, true);
if ($raw === false) {
return false;
}
return strlen($raw) > SODIUM_CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES;
}
}