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); } }