import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.MessageDigest;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

/**
 * Tanda tangan HMAC-SHA256 untuk token akses: menghitung dan memverifikasi
 * tanda tangan atas muatan token dengan kunci rahasia yang hanya dipegang
 * penerbit. Pembandingan memakai MessageDigest.isEqual sehingga lama
 * verifikasi tidak bergantung pada isi tanda tangan yang diperiksa.
 */
public final class TandaTangan {

    private TandaTangan() {
        // Kelas utilitas: seluruh anggota static, tidak perlu instance.
    }

    private static final char[] HEX = "0123456789abcdef".toCharArray();

    /**
     * Menghitung HMAC-SHA256 atas data dengan kunci rahasia.
     * Hasilnya teks heksadesimal kecil sepanjang 64 karakter (32 byte).
     */
    public static String hmacHex(String kunciRahasia, String data) {
        try {
            Mac mac = Mac.getInstance("HmacSHA256");
            mac.init(new SecretKeySpec(
                    kunciRahasia.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
            return keHex(mac.doFinal(data.getBytes(StandardCharsets.UTF_8)));
        } catch (GeneralSecurityException e) {
            // HmacSHA256 wajib tersedia pada setiap implementasi JDK.
            throw new IllegalStateException("mesin HMAC tidak tersedia", e);
        }
    }

    /**
     * Memverifikasi tanda tangan atas muatan. Pembandingan memakai
     * MessageDigest.isEqual yang membandingkan seluruh byte tanpa berhenti
     * di byte pertama yang berbeda, sehingga waktu pembandingan tidak
     * membocorkan isi tanda tangan.
     */
    public static boolean cocok(String kunciRahasia, String muatan, String tandaDiterima) {
        String tandaDihitung = hmacHex(kunciRahasia, muatan);
        return MessageDigest.isEqual(
                tandaDihitung.getBytes(StandardCharsets.UTF_8),
                tandaDiterima.getBytes(StandardCharsets.UTF_8));
    }

    /** Mengubah byte menjadi teks heksadesimal kecil, dua karakter per byte. */
    private static String keHex(byte[] bytes) {
        StringBuilder hasil = new StringBuilder(bytes.length * 2);
        for (byte b : bytes) {
            hasil.append(HEX[(b >> 4) & 0xf]).append(HEX[b & 0xf]);
        }
        return hasil.toString();
    }
}
