package main

import (
	"bytes"
	"crypto/aes"
	"crypto/cipher"
	"crypto/ed25519"
	"crypto/hmac"
	"crypto/rand"
	"crypto/sha256"
	"crypto/subtle"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/base64"
	"fmt"
	"golang.org/x/crypto/argon2"
	"golang.org/x/crypto/bcrypt"
	"math/big"
	"time"
)

func random(n int) []byte {
	b := make([]byte, n)
	if _, err := rand.Read(b); err != nil {
		panic(err)
	}
	return b
}
func derive(password, salt []byte) []byte { return argon2.IDKey(password, salt, 2, 19*1024, 1, 32) }
func passwordChecks() (bool, bool, bool, bool, error) {
	password := []byte("public demonstration password")
	salt := random(16)
	stored := derive(password, salt)
	good := subtle.ConstantTimeCompare(stored, derive(password, salt)) == 1
	bad := subtle.ConstantTimeCompare(stored, derive([]byte("wrong"), salt)) == 1
	different := !bytes.Equal(stored, derive(password, random(16)))
	hash, err := bcrypt.GenerateFromPassword(password, 10)
	if err != nil {
		return false, false, false, false, err
	}
	bgood := bcrypt.CompareHashAndPassword(hash, password) == nil
	return good, bad, different, bgood, nil
}
func aeadChecks() (bool, bool, bool, int, int, error) {
	block, err := aes.NewCipher(random(32))
	if err != nil {
		return false, false, false, 0, 0, err
	}
	a, err := cipher.NewGCMWithRandomNonce(block)
	if err != nil {
		return false, false, false, 0, 0, err
	}
	msg := []byte("public fixture")
	aad := []byte("tenant=a;record=7;version=1")
	sealed := a.Seal(nil, nil, msg, aad)
	plain, err := a.Open(nil, nil, sealed, aad)
	good := err == nil && bytes.Equal(msg, plain)
	tampered := bytes.Clone(sealed)
	tampered[len(tampered)-1] ^= 1
	_, bad := a.Open(nil, nil, tampered, aad)
	_, wrongAAD := a.Open(nil, nil, sealed, []byte("tenant=b;record=7;version=1"))
	return good, bad != nil, wrongAAD != nil, a.NonceSize(), a.Overhead(), nil
}
func macAndSignature() (bool, bool, bool, bool) {
	key := random(32)
	msg := []byte("public invoice fixture")
	mac := func(b []byte) []byte { h := hmac.New(sha256.New, key); h.Write(b); return h.Sum(nil) }
	tag := mac(msg)
	mgood := hmac.Equal(tag, mac(msg))
	mbad := hmac.Equal(tag, mac([]byte("changed")))
	pub, priv, err := ed25519.GenerateKey(rand.Reader)
	must(err)
	sig := ed25519.Sign(priv, msg)
	return mgood, mbad, ed25519.Verify(pub, msg, sig), ed25519.Verify(pub, []byte("changed"), sig)
}

type certificates struct {
	ca, leaf *x509.Certificate
	roots    *x509.CertPool
}

func makeCertificates() (certificates, error) {
	var out certificates
	pub, caKey, err := ed25519.GenerateKey(rand.Reader)
	if err != nil {
		return out, err
	}
	now := time.Date(2026, 10, 13, 12, 0, 0, 0, time.UTC)
	ca := &x509.Certificate{SerialNumber: big.NewInt(1), Subject: pkix.Name{CommonName: "PUBLIC TEST CA"}, NotBefore: now.Add(-time.Hour), NotAfter: now.Add(time.Hour), IsCA: true, BasicConstraintsValid: true, KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign}
	der, err := x509.CreateCertificate(rand.Reader, ca, ca, pub, caKey)
	if err != nil {
		return out, err
	}
	ca, err = x509.ParseCertificate(der)
	if err != nil {
		return out, err
	}
	leafPub, _, err := ed25519.GenerateKey(rand.Reader)
	if err != nil {
		return out, err
	}
	leaf := &x509.Certificate{SerialNumber: big.NewInt(2), DNSNames: []string{"service.example"}, NotBefore: now.Add(-time.Hour), NotAfter: now.Add(time.Hour), KeyUsage: x509.KeyUsageDigitalSignature, ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}}
	der, err = x509.CreateCertificate(rand.Reader, leaf, ca, leafPub, caKey)
	if err != nil {
		return out, err
	}
	leaf, err = x509.ParseCertificate(der)
	if err != nil {
		return out, err
	}
	roots := x509.NewCertPool()
	roots.AddCert(ca)
	return certificates{ca, leaf, roots}, nil
}
func verifyCert(c certificates, name string, usage x509.ExtKeyUsage, roots *x509.CertPool) bool {
	_, err := c.leaf.Verify(x509.VerifyOptions{Roots: roots, DNSName: name, CurrentTime: time.Date(2026, 10, 13, 12, 0, 0, 0, time.UTC), KeyUsages: []x509.ExtKeyUsage{usage}})
	return err == nil
}
func main() {
	fmt.Println("base64:", base64.StdEncoding.EncodeToString([]byte("hello")))
	good, bad, salts, bgood, err := passwordChecks()
	must(err)
	fmt.Printf("argon-good=%v argon-wrong=%v different-salts=%v bcrypt-good=%v\n", good, bad, salts, bgood)
	_, err = bcrypt.GenerateFromPassword(bytes.Repeat([]byte("x"), 73), 10)
	fmt.Println("bcrypt-73-bytes-rejected:", err == bcrypt.ErrPasswordTooLong)
	g, t, a, n, o, err := aeadChecks()
	must(err)
	fmt.Printf("decrypt=%v tamper-rejected=%v aad-rejected=%v nonce-size=%d overhead=%d\n", g, t, a, n, o)
	mg, mb, sg, sb := macAndSignature()
	fmt.Printf("hmac-good=%v hmac-changed=%v signature-good=%v signature-changed=%v\n", mg, mb, sg, sb)
	c, err := makeCertificates()
	must(err)
	fmt.Printf("chain-host=%v wrong-host=%v wrong-usage=%v missing-root=%v\n", verifyCert(c, "service.example", x509.ExtKeyUsageServerAuth, c.roots), verifyCert(c, "other.example", x509.ExtKeyUsageServerAuth, c.roots), verifyCert(c, "service.example", x509.ExtKeyUsageClientAuth, c.roots), verifyCert(c, "service.example", x509.ExtKeyUsageServerAuth, x509.NewCertPool()))
}
func must(err error) {
	if err != nil {
		panic(err)
	}
}
