<?php
/**
 * CredentialCrypto — encrypt-at-rest helper for Right Now Journal's Account
 * Summary broker password field (Journal refinement item 9). Kevin
 * confirmed storing it encrypted with an explicit reveal action, rather
 * than skipping the field or leaving it in plaintext.
 *
 * This is a personal reference field only — TraderAlign never uses it to
 * log into a broker platform on the trader's behalf. It's still real
 * credential-shaped data, so it's encrypted at rest (AES-256-GCM) rather
 * than stored as plain text, and only ever decrypted on an explicit,
 * user-initiated reveal request (api/journal/reveal-account-secret.php) —
 * never rendered into a page by default.
 */
class CredentialCrypto
{
    private const CIPHER = 'aes-256-gcm';

    private static function key(): string
    {
        return hash('sha256', ACCOUNT_CREDENTIAL_KEY, true);
    }

    public static function encrypt(string $plaintext): string
    {
        $iv = random_bytes(12);
        $tag = '';
        $cipher = openssl_encrypt($plaintext, self::CIPHER, self::key(), OPENSSL_RAW_DATA, $iv, $tag);
        if ($cipher === false) {
            throw new \RuntimeException('Encryption failed');
        }
        return base64_encode($iv . $tag . $cipher);
    }

    public static function decrypt(?string $encoded): ?string
    {
        if ($encoded === null || $encoded === '') {
            return null;
        }
        $raw = base64_decode($encoded, true);
        if ($raw === false || strlen($raw) < 29) {
            return null;
        }
        $iv = substr($raw, 0, 12);
        $tag = substr($raw, 12, 16);
        $cipher = substr($raw, 28);
        $plain = openssl_decrypt($cipher, self::CIPHER, self::key(), OPENSSL_RAW_DATA, $iv, $tag);
        return $plain === false ? null : $plain;
    }
}
