Files
api/services/nginx/app/classes/replication_secret_box.php
T

103 lines
2.8 KiB
PHP

<?php
namespace classes;
use RuntimeException;
class replication_secret_box
{
private const PREFIX = 'twsec:v1:';
private const CIPHER = 'aes-256-gcm';
public static function encrypt(string $plaintext): string
{
$nonce = random_bytes(12);
$tag = '';
$ciphertext = openssl_encrypt(
$plaintext,
self::CIPHER,
self::key(),
OPENSSL_RAW_DATA,
$nonce,
$tag,
'',
16
);
if ($ciphertext === false || $tag === '') {
throw new RuntimeException('Secret encryption failed.');
}
return self::PREFIX . base64_encode(json_encode([
'nonce' => base64_encode($nonce),
'tag' => base64_encode($tag),
'ciphertext' => base64_encode($ciphertext),
], JSON_UNESCAPED_SLASHES));
}
public static function decrypt(?string $secret): string
{
$secret = (string)$secret;
if ($secret === '') {
return '';
}
if (!str_starts_with($secret, self::PREFIX)) {
return $secret;
}
$payload = json_decode(base64_decode(substr($secret, strlen(self::PREFIX)), true) ?: '', true);
if (!is_array($payload)) {
throw new RuntimeException('Encrypted secret payload is invalid.');
}
$nonce = base64_decode((string)($payload['nonce'] ?? ''), true);
$tag = base64_decode((string)($payload['tag'] ?? ''), true);
$ciphertext = base64_decode((string)($payload['ciphertext'] ?? ''), true);
if ($nonce === false || $tag === false || $ciphertext === false) {
throw new RuntimeException('Encrypted secret payload is incomplete.');
}
$plaintext = openssl_decrypt(
$ciphertext,
self::CIPHER,
self::key(),
OPENSSL_RAW_DATA,
$nonce,
$tag
);
if ($plaintext === false) {
throw new RuntimeException('Secret decryption failed.');
}
return $plaintext;
}
public static function mask(?string $value): string
{
$value = (string)$value;
if ($value === '') {
return '';
}
$length = strlen($value);
if ($length <= 4) {
return str_repeat('*', $length);
}
return substr($value, 0, 2) . str_repeat('*', max(4, $length - 4)) . substr($value, -2);
}
private static function key(): string
{
$keyMaterial = (string)($GLOBALS['ENCRYPTION_KEY'] ?? getenv('ENCRYPTION_KEY') ?: '');
if (trim($keyMaterial) === '') {
throw new RuntimeException('ENCRYPTION_KEY is required for replication secret encryption.');
}
return hash('sha256', $keyMaterial, true);
}
}