Exemples de Cryptographie Web Go

Exemples de cryptographie et sécurité Web Go incluant le calcul de hachage, le chiffrement symétrique et l'encodage/décodage Base64

💻 Calcul de Hachage go

🟢 simple ⭐⭐

Calculer les hachages MD5, SHA256 et autres pour chaînes et fichiers en utilisant les paquets crypto Go

⏱️ 20 min 🏷️ go, web, cryptography
Prerequisites: Basic Go, crypto package
// Web Go Hash Calculation Examples
// Using Go crypto packages for hash calculation

package main

import (
	"crypto/md5"
	"crypto/sha1"
	"crypto/sha256"
	"crypto/sha512"
	"encoding/hex"
	"fmt"
	"hash"
	"io"
	"os"
)

// 1. String Hash Calculation

// CalculateMD5 calculates MD5 hash of a string
func CalculateMD5(input string) string {
	hash := md5.Sum([]byte(input))
	return hex.EncodeToString(hash[:])
}

// CalculateSHA1 calculates SHA1 hash of a string
func CalculateSHA1(input string) string {
	hash := sha1.Sum([]byte(input))
	return hex.EncodeToString(hash[:])
}

// CalculateSHA256 calculates SHA256 hash of a string
func CalculateSHA256(input string) string {
	hash := sha256.Sum256([]byte(input))
	return hex.EncodeToString(hash[:])
}

// CalculateSHA512 calculates SHA512 hash of a string
func CalculateSHA512(input string) string {
	hash := sha512.Sum512([]byte(input))
	return hex.EncodeToString(hash[:])
}

// CalculateHash calculates hash using specified algorithm
func CalculateHash(input string, algorithm string) (string, error) {
	var h hash.Hash

	switch algorithm {
	case "md5":
		h = md5.New()
	case "sha1":
		h = sha1.New()
	case "sha256":
		h = sha256.New()
	case "sha512":
		h = sha512.New()
	default:
		return "", fmt.Errorf("unsupported algorithm: %s", algorithm)
	}

	h.Write([]byte(input))
	return hex.EncodeToString(h.Sum(nil)), nil
}

// CalculateMultipleHashes calculates multiple hashes of a string
func CalculateMultipleHashes(input string) map[string]string {
	result := make(map[string]string)

	result["md5"] = CalculateMD5(input)
	result["sha1"] = CalculateSHA1(input)
	result["sha256"] = CalculateSHA256(input)
	result["sha512"] = CalculateSHA512(input)

	return result
}

// 2. File Hash Calculation

// CalculateFileHash calculates hash of a file
func CalculateFileHash(filePath string, algorithm string) (string, error) {
	file, err := os.Open(filePath)
	if err != nil {
		return "", fmt.Errorf("error opening file: %v", err)
	}
	defer file.Close()

	var h hash.Hash

	switch algorithm {
	case "md5":
		h = md5.New()
	case "sha1":
		h = sha1.New()
	case "sha256":
		h = sha256.New()
	case "sha512":
		h = sha512.New()
	default:
		return "", fmt.Errorf("unsupported algorithm: %s", algorithm)
	}

	if _, err := io.Copy(h, file); err != nil {
		return "", fmt.Errorf("error reading file: %v", err)
	}

	return hex.EncodeToString(h.Sum(nil)), nil
}

// CalculateFileMD5 calculates MD5 hash of a file
func CalculateFileMD5(filePath string) (string, error) {
	return CalculateFileHash(filePath, "md5")
}

// CalculateFileSHA256 calculates SHA256 hash of a file
func CalculateFileSHA256(filePath string) (string, error) {
	return CalculateFileHash(filePath, "sha256")
}

// CalculateFileHashWithProgress calculates file hash with progress tracking
func CalculateFileHashWithProgress(filePath string, algorithm string, progressCallback func(written, total int64)) (string, error) {
	file, err := os.Open(filePath)
	if err != nil {
		return "", fmt.Errorf("error opening file: %v", err)
	}
	defer file.Close()

	info, err := file.Stat()
	if err != nil {
		return "", fmt.Errorf("error getting file info: %v", err)
	}

	var h hash.Hash

	switch algorithm {
	case "md5":
		h = md5.New()
	case "sha1":
		h = sha1.New()
	case "sha256":
		h = sha256.New()
	case "sha512":
		h = sha512.New()
	default:
		return "", fmt.Errorf("unsupported algorithm: %s", algorithm)
	}

	// Copy with progress
	buffer := make([]byte, 32*1024) // 32KB buffer
	var written int64

	for {
		n, err := file.Read(buffer)
		if n > 0 {
			h.Write(buffer[:n])
			written += int64(n)

			if progressCallback != nil {
				progressCallback(written, info.Size())
			}
		}

		if err != nil {
			if err == io.EOF {
				break
			}
			return "", fmt.Errorf("error reading file: %v", err)
		}
	}

	return hex.EncodeToString(h.Sum(nil)), nil
}

// 3. Hash Comparison

// VerifyHash verifies if a string matches the expected hash
func VerifyHash(input, expectedHash, algorithm string) (bool, error) {
	calculatedHash, err := CalculateHash(input, algorithm)
	if err != nil {
		return false, err
	}
	return calculatedHash == expectedHash, nil
}

// VerifyFileHash verifies if a file matches the expected hash
func VerifyFileHash(filePath, expectedHash, algorithm string) (bool, error) {
	calculatedHash, err := CalculateFileHash(filePath, algorithm)
	if err != nil {
		return false, err
	}
	return calculatedHash == expectedHash, nil
}

// 4. HMAC Calculation

import (
	"crypto/hmac"
)

// CalculateHMAC calculates HMAC using specified hash algorithm
func CalculateHMAC(key, message, algorithm string) (string, error) {
	var h func() hash.Hash

	switch algorithm {
	case "md5":
		h = md5.New
	case "sha1":
		h = sha1.New
	case "sha256":
		h = sha256.New
	case "sha512":
		h = sha512.New
	default:
		return "", fmt.Errorf("unsupported algorithm: %s", algorithm)
	}

	mac := hmac.New(h, []byte(key))
	mac.Write([]byte(message))

	return hex.EncodeToString(mac.Sum(nil)), nil
}

// VerifyHMAC verifies HMAC signature
func VerifyHMAC(key, message, expectedMAC, algorithm string) (bool, error) {
	calculatedMAC, err := CalculateHMAC(key, message, algorithm)
	if err != nil {
		return false, err
	}

	// Use constant-time comparison to prevent timing attacks
	return hmac.Equal([]byte(calculatedMAC), []byte(expectedMAC)), nil
}

// 5. Hash Utilities

// GenerateHashFromBytes calculates hash from byte slice
func GenerateHashFromBytes(data []byte, algorithm string) (string, error) {
	var h hash.Hash

	switch algorithm {
	case "md5":
		h = md5.New()
	case "sha1":
		h = sha1.New()
	case "sha256":
		h = sha256.New()
	case "sha512":
		h = sha512.New()
	default:
		return "", fmt.Errorf("unsupported algorithm: %s", algorithm)
	}

	h.Write(data)
	return hex.EncodeToString(h.Sum(nil)), nil
}

// HashFormat formats hash string with specified casing and separator
func HashFormat(hash string, uppercase bool, separator string, chunkSize int) string {
	if uppercase {
		hash = strings.ToUpper(hash)
	}

	if chunkSize > 0 && separator != "" {
		var parts []string
		for i := 0; i < len(hash); i += chunkSize {
			end := i + chunkSize
			if end > len(hash) {
				end = len(hash)
			}
			parts = append(parts, hash[i:end])
		}
		return strings.Join(parts, separator)
	}

	return hash
}

// Usage Examples
func main() {
	fmt.Println("=== Web Go Hash Calculation Examples ===\n")

	// 1. String hash calculation
	fmt.Println("--- 1. String Hash Calculation ---")
	text := "Hello, World!"
	fmt.Printf("MD5: %s\n", CalculateMD5(text))
	fmt.Printf("SHA1: %s\n", CalculateSHA1(text))
	fmt.Printf("SHA256: %s\n", CalculateSHA256(text))
	fmt.Printf("SHA512: %s\n", CalculateSHA512(text))

	// 2. Multiple hashes
	fmt.Println("\n--- 2. Multiple Hashes ---")
	hashes := CalculateMultipleHashes(text)
	for algo, hash := range hashes {
		fmt.Printf("%s: %s\n", strings.ToUpper(algo), hash)
	}

	// 3. File hash
	fmt.Println("\n--- 3. File Hash ---")
	fileHash, err := CalculateFileSHA256("example.txt")
	if err == nil {
		fmt.Printf("File SHA256: %s\n", fileHash)
	}

	// 4. Hash verification
	fmt.Println("\n--- 4. Hash Verification ---")
	expectedHash := CalculateSHA256(text)
	valid, _ := VerifyHash(text, expectedHash, "sha256")
	fmt.Printf("Hash valid: %v\n", valid)

	// 5. HMAC
	fmt.Println("\n--- 5. HMAC Calculation ---")
	key := "secret-key"
	message := "authenticated message"
	hmac, err := CalculateHMAC(key, message, "sha256")
	if err == nil {
		fmt.Printf("HMAC-SHA256: %s\n", hmac)
	}

	// 6. Hash with progress
	fmt.Println("\n--- 6. File Hash with Progress ---")
	_, err = CalculateFileHashWithProgress("large_file.bin", "sha256", func(written, total int64) {
		fmt.Printf("Progress: %.1f%%\n", float64(written)/float64(total)*100)
	})
	if err == nil {
		fmt.Println("File hash calculated")
	}

	fmt.Println("\n=== All Hash Calculation Examples Completed ===")
}

💻 Encodage/Décodage Base64 go

🟢 simple ⭐⭐

Encodage et décodage Base64 et Base64URL pour chaînes et données binaires

⏱️ 15 min 🏷️ go, web, cryptography, encoding
Prerequisites: Basic Go, encoding/base64
// Web Go Base64 Encoding Examples
// Base64 and Base64URL encoding and decoding

package main

import (
	"encoding/base64"
	"fmt"
	"strings"
)

// 1. Basic Base64 Encoding

// EncodeBase64 encodes a string to Base64
func EncodeBase64(input string) string {
	return base64.StdEncoding.EncodeToString([]byte(input))
}

// DecodeBase64 decodes a Base64 string
func DecodeBase64(input string) (string, error) {
	data, err := base64.StdEncoding.DecodeString(input)
	if err != nil {
		return "", fmt.Errorf("error decoding Base64: %v", err)
	}
	return string(data), nil
}

// EncodeBase64Bytes encodes bytes to Base64
func EncodeBase64Bytes(data []byte) string {
	return base64.StdEncoding.EncodeToString(data)
}

// DecodeBase64Bytes decodes Base64 to bytes
func DecodeBase64Bytes(input string) ([]byte, error) {
	data, err := base64.StdEncoding.DecodeString(input)
	if err != nil {
		return nil, fmt.Errorf("error decoding Base64: %v", err)
	}
	return data, nil
}

// 2. URL-Safe Base64 Encoding

// EncodeBase64URL encodes a string to URL-safe Base64
func EncodeBase64URL(input string) string {
	return base64.URLEncoding.EncodeToString([]byte(input))
}

// DecodeBase64URL decodes a URL-safe Base64 string
func DecodeBase64URL(input string) (string, error) {
	data, err := base64.URLEncoding.DecodeString(input)
	if err != nil {
		return "", fmt.Errorf("error decoding Base64URL: %v", err)
	}
	return string(data), nil
}

// EncodeBase64URLBytes encodes bytes to URL-safe Base64
func EncodeBase64URLBytes(data []byte) string {
	return base64.URLEncoding.EncodeToString(data)
}

// DecodeBase64URLBytes decodes URL-safe Base64 to bytes
func DecodeBase64URLBytes(input string) ([]byte, error) {
	data, err := base64.URLEncoding.DecodeString(input)
	if err != nil {
		return nil, fmt.Errorf("error decoding Base64URL: %v", err)
	}
	return data, nil
}

// 3. Raw Base64 Encoding (No padding)

// EncodeBase64Raw encodes a string to raw Base64 (no padding)
func EncodeBase64Raw(input string) string {
	return base64.RawStdEncoding.EncodeToString([]byte(input))
}

// DecodeBase64Raw decodes a raw Base64 string
func DecodeBase64Raw(input string) (string, error) {
	data, err := base64.RawStdEncoding.DecodeString(input)
	if err != nil {
		return "", fmt.Errorf("error decoding raw Base64: %v", err)
	}
	return string(data), nil
}

// EncodeBase64URLRaw encodes a string to raw URL-safe Base64
func EncodeBase64URLRaw(input string) string {
	return base64.RawURLEncoding.EncodeToString([]byte(input))
}

// DecodeBase64URLRaw decodes a raw URL-safe Base64 string
func DecodeBase64URLRaw(input string) (string, error) {
	data, err := base64.RawURLEncoding.DecodeString(input)
	if err != nil {
		return "", fmt.Errorf("error decoding raw Base64URL: %v", err)
	}
	return string(data), nil
}

// 4. Base64 with Custom Encoding

// EncodeBase64WithPadding encodes with optional padding
func EncodeBase64WithPadding(input string, withPadding bool) string {
	data := []byte(input)

	if withPadding {
		return base64.StdEncoding.EncodeToString(data)
	}
	return base64.RawStdEncoding.EncodeToString(data)
}

// DecodeBase64Auto decodes Base64 with or without padding
func DecodeBase64Auto(input string) (string, error) {
	// Try StdEncoding first
	data, err := base64.StdEncoding.DecodeString(input)
	if err == nil {
		return string(data), nil
	}

	// Try RawStdEncoding
	data, err = base64.RawStdEncoding.DecodeString(input)
	if err == nil {
		return string(data), nil
	}

	return "", fmt.Errorf("failed to decode Base64")
}

// 5. Base64 Validation

// IsValidBase64 checks if a string is valid Base64
func IsValidBase64(input string) bool {
	_, err := base64.StdEncoding.DecodeString(input)
	return err == nil
}

// IsValidBase64URL checks if a string is valid URL-safe Base64
func IsValidBase64URL(input string) bool {
	_, err := base64.URLEncoding.DecodeString(input)
	return err == nil
}

// DetectBase64Type detects the type of Base64 encoding
func DetectBase64Type(input string) string {
	// Check for URL-safe characters
	if strings.ContainsAny(input, "-_") {
		if !strings.HasSuffix(input, "=") && !strings.HasSuffix(input, "==") {
			return "Base64URL (Raw)"
		}
		return "Base64URL"
	}

	// Check for standard Base64
	if !strings.HasSuffix(input, "=") && !strings.HasSuffix(input, "==") {
		return "Base64 (Raw)"
	}
	return "Base64 (Standard)"
}

// 6. Base64 Utilities

// Base64EncodeChunked encodes with line wrapping
func Base64EncodeChunked(input string, chunkSize int) []string {
	encoded := base64.StdEncoding.EncodeToString([]byte(input))
	var chunks []string

	for i := 0; i < len(encoded); i += chunkSize {
		end := i + chunkSize
		if end > len(encoded) {
			end = len(encoded)
		}
		chunks = append(chunks, encoded[i:end])
	}

	return chunks
}

// RemoveBase64Padding removes padding from Base64 string
func RemoveBase64Padding(input string) string {
	return strings.TrimRight(input, "=")
}

// AddBase64Padding adds padding to Base64 string
func AddBase64Padding(input string) string {
	padding := len(input) % 4
	if padding != 0 {
		input += strings.Repeat("=", 4-padding)
	}
	return input
}

// 7. Batch Operations

// EncodeBatch encodes multiple strings
func EncodeBatch(inputs []string) map[string]string {
	results := make(map[string]string)

	for _, input := range inputs {
		results[input] = EncodeBase64(input)
	}

	return results
}

// DecodeBatch decodes multiple Base64 strings
func DecodeBatch(inputs map[string]string) (map[string]string, error) {
	results := make(map[string]string)

	for key, encoded := range inputs {
		decoded, err := DecodeBase64(encoded)
		if err != nil {
			return nil, err
		}
		results[key] = decoded
	}

	return results, nil
}

// 8. File Operations

// EncodeFileToBase64 encodes a file to Base64
func EncodeFileToBase64(filePath string) (string, error) {
	data, err := os.ReadFile(filePath)
	if err != nil {
		return "", fmt.Errorf("error reading file: %v", err)
	}
	return base64.StdEncoding.EncodeToString(data), nil
}

// DecodeBase64ToFile decodes Base64 and writes to file
func DecodeBase64ToFile(encoded, filePath string) error {
	data, err := base64.StdEncoding.DecodeString(encoded)
	if err != nil {
		return fmt.Errorf("error decoding Base64: %v", err)
	}

	err = os.WriteFile(filePath, data, 0644)
	if err != nil {
		return fmt.Errorf("error writing file: %v", err)
	}

	return nil
}

// 9. Specialized Encoding

// EncodeDataURL creates a data URL (for web use)
func EncodeDataURL mimeType, data string) string {
	encoded := base64.StdEncoding.EncodeToString([]byte(data))
	return fmt.Sprintf("data:%s;base64,%s", mimeType, encoded)
}

// EncodeImageDataURL creates a data URL for images
func EncodeImageDataURL(imageData []byte, mimeType string) string {
	encoded := base64.StdEncoding.EncodeToString(imageData)
	return fmt.Sprintf("data:%s;base64,%s", mimeType, encoded)
}

// Usage Examples
func main() {
	fmt.Println("=== Web Go Base64 Encoding Examples ===\n")

	// 1. Basic encoding
	fmt.Println("--- 1. Basic Base64 ---")
	text := "Hello, World!"
	encoded := EncodeBase64(text)
	fmt.Printf("Encoded: %s\n", encoded)

	decoded, _ := DecodeBase64(encoded)
	fmt.Printf("Decoded: %s\n", decoded)

	// 2. URL-safe encoding
	fmt.Println("\n--- 2. URL-Safe Base64 ---")
	urlText := "Hello?name=value&data=test"
	urlEncoded := EncodeBase64URL(urlText)
	fmt.Printf("URL Encoded: %s\n", urlEncoded)

	urlDecoded, _ := DecodeBase64URL(urlEncoded)
	fmt.Printf("URL Decoded: %s\n", urlDecoded)

	// 3. Raw encoding (no padding)
	fmt.Println("\n--- 3. Raw Base64 ---")
	rawEncoded := EncodeBase64Raw(text)
	fmt.Printf("Raw Encoded: %s\n", rawEncoded)

	rawDecoded, _ := DecodeBase64Raw(rawEncoded)
	fmt.Printf("Raw Decoded: %s\n", rawDecoded)

	// 4. Validation
	fmt.Println("\n--- 4. Validation ---")
	fmt.Printf("Valid Base64: %v\n", IsValidBase64(encoded))
	fmt.Printf("Valid URL Base64: %v\n", IsValidBase64URL(urlEncoded))
	fmt.Printf("Type: %s\n", DetectBase64Type(encoded))

	// 5. Padding operations
	fmt.Println("\n--- 5. Padding Operations ---")
	noPadding := RemoveBase64Padding(encoded)
	fmt.Printf("Without padding: %s\n", noPadding)

	withPadding := AddBase64Padding(noPadding)
	fmt.Printf("With padding: %s\n", withPadding)

	// 6. Chunked encoding
	fmt.Println("\n--- 6. Chunked Encoding ---")
	chunks := Base64EncodeChunked("This is a longer string for chunked encoding", 20)
	fmt.Printf("Chunks: %d\n", len(chunks))
	for i, chunk := range chunks {
		fmt.Printf("  Chunk %d: %s\n", i+1, chunk)
	}

	// 7. Batch encoding
	fmt.Println("\n--- 7. Batch Encoding ---")
	batchInputs := []string{"text1", "text2", "text3"}
	batchResults := EncodeBatch(batchInputs)
	fmt.Printf("Encoded %d items\n", len(batchResults))

	// 8. Data URL
	fmt.Println("\n--- 8. Data URL ---")
	dataURL := EncodeDataURL("text/plain", text)
	fmt.Printf("Data URL: %s\n", dataURL[:50]+"...")

	fmt.Println("\n=== All Base64 Encoding Examples Completed ===")
}

💻 Chiffrement Symétrique (AES) go

🟡 intermediate ⭐⭐⭐⭐

Chiffrement et déchiffrement AES avec différents modes (CBC, GCM, CTR) en utilisant les paquets crypto Go

⏱️ 30 min 🏷️ go, web, cryptography, encryption
Prerequisites: Intermediate Go, crypto/aes, crypto/cipher
// Web Go Symmetric Encryption Examples
// AES encryption and decryption with different modes

package main

import (
	"crypto/aes"
	"crypto/cipher"
	"crypto/rand"
	"encoding/base64"
	"errors"
	"fmt"
	"io"
	"strings"
)

// 1. AES-GCM Encryption (Recommended)

// EncryptGCM encrypts plaintext using AES-GCM
func EncryptGCM(plaintext, key []byte) (string, error) {
	block, err := aes.NewCipher(key)
	if err != nil {
		return "", fmt.Errorf("error creating cipher: %v", err)
	}

	gcm, err := cipher.NewGCM(block)
	if err != nil {
		return "", fmt.Errorf("error creating GCM: %v", err)
	}

	// Generate random nonce
	nonce := make([]byte, gcm.NonceSize())
	if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
		return "", fmt.Errorf("error generating nonce: %v", err)
	}

	// Encrypt and authenticate
	ciphertext := gcm.Seal(nonce, nonce, plaintext, nil)

	// Encode to base64
	return base64.StdEncoding.EncodeToString(ciphertext), nil
}

// DecryptGCM decrypts ciphertext using AES-GCM
func DecryptGCM(ciphertext string, key []byte) ([]byte, error) {
	// Decode from base64
	data, err := base64.StdEncoding.DecodeString(ciphertext)
	if err != nil {
		return nil, fmt.Errorf("error decoding ciphertext: %v", err)
	}

	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, fmt.Errorf("error creating cipher: %v", err)
	}

	gcm, err := cipher.NewGCM(block)
	if err != nil {
		return nil, fmt.Errorf("error creating GCM: %v", err)
	}

	nonceSize := gcm.NonceSize()
	if len(data) < nonceSize {
		return nil, errors.New("ciphertext too short")
	}

	nonce, cipherData := data[:nonceSize], data[nonceSize:]

	// Decrypt and verify
	plaintext, err := gcm.Open(nil, nonce, cipherData, nil)
	if err != nil {
		return nil, fmt.Errorf("error decrypting: %v", err)
	}

	return plaintext, nil
}

// 2. AES-CBC Encryption

// EncryptCBCEncrypts plaintext using AES-CBC with PKCS7 padding
func EncryptCBC(plaintext, key, iv []byte) (string, error) {
	block, err := aes.NewCipher(key)
	if err != nil {
		return "", fmt.Errorf("error creating cipher: %v", err)
	}

	// PKCS7 padding
	padding := aes.BlockSize - len(plaintext)%aes.BlockSize
	padtext := bytes.Repeat([]byte{byte(padding)}, padding)
	plaintext = append(plaintext, padtext...)

	if len(iv) != aes.BlockSize {
		return "", errors.New("incorrect IV length")
	}

	ciphertext := make([]byte, len(plaintext))
	mode := cipher.NewCBCEncrypter(block, iv)
	mode.CryptBlocks(ciphertext, plaintext)

	return base64.StdEncoding.EncodeToString(ciphertext), nil
}

// DecryptCBC decrypts ciphertext using AES-CBC
func DecryptCBC(ciphertext string, key, iv []byte) ([]byte, error) {
	// Decode from base64
	data, err := base64.StdEncoding.DecodeString(ciphertext)
	if err != nil {
		return nil, fmt.Errorf("error decoding ciphertext: %v", err)
	}

	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, fmt.Errorf("error creating cipher: %v", err)
	}

	if len(iv) != aes.BlockSize {
		return nil, errors.New("incorrect IV length")
	}

	if len(data)%aes.BlockSize != 0 {
		return nil, errors.New("ciphertext is not a multiple of the block size")
	}

	plaintext := make([]byte, len(data))
	mode := cipher.NewCBCDecrypter(block, iv)
	mode.CryptBlocks(plaintext, data)

	// Remove PKCS7 padding
	padding := int(plaintext[len(plaintext)-1])
	if padding > aes.BlockSize || padding > len(plaintext) {
		return nil, errors.New("invalid padding")
	}

	return plaintext[:len(plaintext)-padding], nil
}

// 3. AES-CTR Encryption (Stream Cipher)

// EncryptCTR encrypts plaintext using AES-CTR
func EncryptCTR(plaintext, key, iv []byte) (string, error) {
	block, err := aes.NewCipher(key)
	if err != nil {
		return "", fmt.Errorf("error creating cipher: %v", err)
	}

	if len(iv) != aes.BlockSize {
		return "", errors.New("incorrect IV length")
	}

	ciphertext := make([]byte, len(plaintext))
	stream := cipher.NewCTR(block, iv)
	stream.XORKeyStream(ciphertext, plaintext)

	return base64.StdEncoding.EncodeToString(ciphertext), nil
}

// DecryptCTR decrypts ciphertext using AES-CTR
func DecryptCTR(ciphertext string, key, iv []byte) ([]byte, error) {
	// Decode from base64
	data, err := base64.StdEncoding.DecodeString(ciphertext)
	if err != nil {
		return nil, fmt.Errorf("error decoding ciphertext: %v", err)
	}

	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, fmt.Errorf("error creating cipher: %v", err)
	}

	if len(iv) != aes.BlockSize {
		return nil, errors.New("incorrect IV length")
	}

	plaintext := make([]byte, len(data))
	stream := cipher.NewCTR(block, iv)
	stream.XORKeyStream(plaintext, data)

	return plaintext, nil
}

// 4. Key Management

// GenerateRandomKey generates a random encryption key
func GenerateRandomKey(size int) ([]byte, error) {
	key := make([]byte, size)
	if _, err := io.ReadFull(rand.Reader, key); err != nil {
		return nil, fmt.Errorf("error generating key: %v", err)
	}
	return key, nil
}

// GenerateAES128Key generates a 128-bit AES key
func GenerateAES128Key() ([]byte, error) {
	return GenerateRandomKey(16)
}

// GenerateAES192Key generates a 192-bit AES key
func GenerateAES192Key() ([]byte, error) {
	return GenerateRandomKey(24)
}

// GenerateAES256Key generates a 256-bit AES key
func GenerateAES256Key() ([]byte, error) {
	return GenerateRandomKey(32)
}

// GenerateRandomIV generates a random IV
func GenerateRandomIV() ([]byte, error) {
	iv := make([]byte, aes.BlockSize)
	if _, err := io.ReadFull(rand.Reader, iv); err != nil {
		return nil, fmt.Errorf("error generating IV: %v", err)
	}
	return iv, nil
}

// 5. High-Level Encryption Functions

// EncryptString encrypts a string using AES-GCM with a generated key
func EncryptString(plaintext string) (string, string, error) {
	key, err := GenerateAES256Key()
	if err != nil {
		return "", "", err
	}

	ciphertext, err := EncryptGCM([]byte(plaintext), key)
	if err != nil {
		return "", "", err
	}

	// Encode key as hex for storage
	keyHex := hex.EncodeToString(key)

	return ciphertext, keyHex, nil
}

// DecryptString decrypts a string using the provided key
func DecryptString(ciphertext, keyHex string) (string, error) {
	key, err := hex.DecodeString(keyHex)
	if err != nil {
		return "", fmt.Errorf("error decoding key: %v", err)
	}

	plaintext, err := DecryptGCM(ciphertext, key)
	if err != nil {
		return "", err
	}

	return string(plaintext), nil
}

// 6. Batch Encryption

// EncryptBatch encrypts multiple strings
func EncryptBatch(messages []string, key []byte) (map[string]string, error) {
	results := make(map[string]string)

	for _, msg := range messages {
		ciphertext, err := EncryptGCM([]byte(msg), key)
		if err != nil {
			return nil, err
		}
		results[msg] = ciphertext
	}

	return results, nil
}

// DecryptBatch decrypts multiple strings
func DecryptBatch(ciphertexts map[string]string, key []byte) (map[string]string, error) {
	results := make(map[string]string)

	for id, ciphertext := range ciphertexts {
		plaintext, err := DecryptGCM(ciphertext, key)
		if err != nil {
			return nil, err
		}
		results[id] = string(plaintext)
	}

	return results, nil
}

// 7. File Encryption

// EncryptFile encrypts a file
func EncryptFile(inputPath, outputPath string, key []byte) error {
	plaintext, err := os.ReadFile(inputPath)
	if err != nil {
		return fmt.Errorf("error reading file: %v", err)
	}

	ciphertext, err := EncryptGCM(plaintext, key)
	if err != nil {
		return err
	}

	return os.WriteFile(outputPath, []byte(ciphertext), 0644)
}

// DecryptFile decrypts a file
func DecryptFile(inputPath, outputPath string, key []byte) error {
	data, err := os.ReadFile(inputPath)
	if err != nil {
		return fmt.Errorf("error reading file: %v", err)
	}

	plaintext, err := DecryptGCM(string(data), key)
	if err != nil {
		return err
	}

	return os.WriteFile(outputPath, plaintext, 0644)
}

// Usage Examples
func main() {
	fmt.Println("=== Web Go Symmetric Encryption Examples ===\n")

	// 1. AES-GCM encryption
	fmt.Println("--- 1. AES-GCM Encryption ---")
	key, _ := GenerateAES256Key()
	plaintext := "Secret message"

	ciphertext, err := EncryptGCM([]byte(plaintext), key)
	if err == nil {
		fmt.Printf("Encrypted: %s\n", ciphertext[:50]+"...")
	}

	decrypted, err := DecryptGCM(ciphertext, key)
	if err == nil {
		fmt.Printf("Decrypted: %s\n", string(decrypted))
	}

	// 2. String encryption with key generation
	fmt.Println("\n--- 2. String Encryption ---")
	msg := "Important data"
	encrypted, keyHex, _ := EncryptString(msg)
	fmt.Printf("Encrypted: %s\n", encrypted[:50]+"...")
	fmt.Printf("Key: %s\n", keyHex)

	decrypted, _ = DecryptString(encrypted, keyHex)
	fmt.Printf("Decrypted: %s\n", decrypted)

	// 3. AES-CBC encryption
	fmt.Println("\n--- 3. AES-CBC Encryption ---")
	iv, _ := GenerateRandomIV()
	cbcCipher, _ := EncryptCBC([]byte("CBC Mode"), key, iv)
	fmt.Printf("CBC Encrypted: %s\n", cbcCipher[:50]+"...")

	// 4. Batch encryption
	fmt.Println("\n--- 4. Batch Encryption ---")
	messages := []string{"msg1", "msg2", "msg3"}
	batchResults, _ := EncryptBatch(messages, key)
	fmt.Printf("Encrypted %d messages\n", len(batchResults))

	// 5. Key sizes
	fmt.Println("\n--- 5. Key Sizes ---")
	key128, _ := GenerateAES128Key()
	key192, _ := GenerateAES192Key()
	key256, _ := GenerateAES256Key()
	fmt.Printf("AES-128: %d bits\n", len(key128)*8)
	fmt.Printf("AES-192: %d bits\n", len(key192)*8)
	fmt.Printf("AES-256: %d bits\n", len(key256)*8)

	fmt.Println("\n=== All Symmetric Encryption Examples Completed ===")
}