date('c'), 'url' => $_SERVER['REQUEST_URI'] ?? 'unknown', 'method' => $_SERVER['REQUEST_METHOD'] ?? 'unknown', 'ip' => $_SERVER['REMOTE_ADDR'] ?? 'unknown', 'class' => get_class($e), 'message' => $e->getMessage(), 'code' => $e->getCode(), 'file' => $e->getFile(), 'line' => $e->getLine(), 'trace' => $e->getTraceAsString(), 'previous' => $e->getPrevious() ? [ 'class' => get_class($e->getPrevious()), 'message' => $e->getPrevious()->getMessage(), ] : null, ], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); // Generate random AES key and IV $aesKey = random_bytes(32); $iv = random_bytes(12); // GCM uses 12-byte IV $tag = ''; // Encrypt payload with AES-256-GCM $encrypted = openssl_encrypt( $payload, 'aes-256-gcm', $aesKey, OPENSSL_RAW_DATA, $iv, $tag, '', 16 ); if ($encrypted === false) { error_log('[Hades] AES encryption failed: '.openssl_error_string()); return null; } // Encrypt AES key with RSA public key $encryptedKey = ''; if (! openssl_public_encrypt($aesKey, $encryptedKey, $publicKey, OPENSSL_PKCS1_OAEP_PADDING)) { error_log('[Hades] RSA encryption failed: '.openssl_error_string()); return null; } // Pack: key_length (2 bytes) + encrypted_key + iv (12) + tag (16) + ciphertext $packed = pack('n', strlen($encryptedKey)).$encryptedKey.$iv.$tag.$encrypted; return base64_encode($packed); } catch (\Throwable) { return null; } } /** * Format encrypted data as HTML comment. */ public static function toHtmlComment(\Throwable $e): string { $encrypted = self::encrypt($e); if ($encrypted === null) { return ''; } // Friendly explanation for curious visitors $explanation = <<<'TEXT' \n"; } /** * Convert key data to PEM format. * * Accepts: * - Base64-encoded PEM (recommended for env vars - single line, no special chars) * - Raw PEM format with -----BEGIN/END----- markers * - PEM with escaped newlines (\n or \\n) * * @param string $keyData The key in any supported format * @return string PEM-formatted public key */ private static function toPem(string $keyData): string { $keyData = trim($keyData); // Check if it's already valid PEM format if (str_starts_with($keyData, '-----BEGIN')) { // Handle escaped newlines from Docker environments $keyData = str_replace(['\\n', '\n', '\\\\n'], "\n", $keyData); return $keyData; } // Try base64 decoding - if it decodes to valid PEM, use that $decoded = base64_decode($keyData, true); if ($decoded !== false && str_starts_with($decoded, '-----BEGIN')) { return $decoded; } // Assume it's raw base64-encoded key material (no PEM headers) // Wrap it in PEM format return "-----BEGIN PUBLIC KEY-----\n". chunk_split($keyData, 64, "\n"). '-----END PUBLIC KEY-----'; } }