package main

import (
	"crypto/rand"
	"crypto/sha256"
	"crypto/subtle"
	"encoding/hex"
	"fmt"
	"log"
)

// hashDenganSalt menghitung SHA-256 dari salt + password, lalu mengembalikan hex.
// SHA-256 dipakai di sini hanya untuk membedah struktur salt + hash.
func hashDenganSalt(salt, password string) string {
	jumlah := sha256.Sum256([]byte(salt + password))
	return hex.EncodeToString(jumlah[:])
}

// verifikasi menghitung ulang hash lalu membandingkan dengan waktu-konstan.
func verifikasi(salt, hashTersimpan, password string) bool {
	hashBaru := hashDenganSalt(salt, password)
	sama := subtle.ConstantTimeCompare([]byte(hashBaru), []byte(hashTersimpan))
	return sama == 1
}

// saltAcak membuat salt 16 byte dari crypto/rand dalam bentuk hex.
func saltAcak() (string, error) {
	b := make([]byte, 16)
	if _, err := rand.Read(b); err != nil {
		return "", err
	}
	return hex.EncodeToString(b), nil
}

func main() {
	password := "sandiRahasia123"

	// Bagian 1: salt tetap supaya output contoh dapat diprediksi.
	saltTetap := "garam-demo-0123456789"
	hash := hashDenganSalt(saltTetap, password)
	fmt.Println("== Bagian 1: struktur salt + hash ==")
	fmt.Println("password :", password)
	fmt.Println("salt     :", saltTetap)
	fmt.Println("hash     :", hash)
	fmt.Println("hash untuk password lain berbeda :", hash != hashDenganSalt(saltTetap, "passwordSalah"))

	// Bagian 2: salt acak seperti implementasi sungguhan.
	salt, err := saltAcak()
	if err != nil {
		log.Fatal(err)
	}
	hashAcak := hashDenganSalt(salt, password)
	saltKedua, err := saltAcak()
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println()
	fmt.Println("== Bagian 2: salt acak + verifikasi ==")
	fmt.Println("panjang salt         :", len(salt), "karakter hex")
	fmt.Println("password benar       :", verifikasi(salt, hashAcak, "sandiRahasia123"))
	fmt.Println("password salah       :", verifikasi(salt, hashAcak, "passwordSalah"))
	fmt.Println("salt beda, hash beda :", hashAcak != hashDenganSalt(saltKedua, password))

	// Bagian 3: hubungan cost bcrypt dengan jumlah iterasi (2^cost).
	fmt.Println()
	fmt.Println("== Bagian 3: cost bcrypt ==")
	for _, cost := range []int{10, 12, 14} {
		note := ""
		if cost == 10 {
			note = " (bcrypt.DefaultCost)"
		}
		fmt.Printf("cost %d : %d iterasi%s\n", cost, 1<<cost, note)
	}
}
