Cryptography Basics

Exercise: Cryptography Basics

Difficulty - Intermediate

Learning Objectives

  • Master hashing with SHA-256 and bcrypt
  • Learn AES encryption and decryption
  • Practice secure random generation
  • Implement password hashing and verification
  • Understand cryptographic best practices

Problem Statement

Create a crypto package with common cryptographic operations.

Function Signatures

 1package crypto
 2
 3// HashPassword hashes a password using bcrypt
 4func HashPassword(password string)
 5
 6// VerifyPassword verifies a password against a hash
 7func VerifyPassword(password, hash string) bool
 8
 9// GenerateRandomBytes generates cryptographically secure random bytes
10func GenerateRandomBytes(n int)
11
12// GenerateRandomString generates a random string
13func GenerateRandomString(length int)
14
15// EncryptAES encrypts data using AES-256-GCM
16func EncryptAES(plaintext, key []byte)
17
18// DecryptAES decrypts data using AES-256-GCM
19func DecryptAES(ciphertext, key []byte)
20
21// HashSHA256 computes SHA-256 hash
22func HashSHA256(data []byte) []byte
23
24// GenerateHMAC generates HMAC-SHA256
25func GenerateHMAC(message, key []byte) []byte
26
27// VerifyHMAC verifies HMAC-SHA256
28func VerifyHMAC(message, key, mac []byte) bool

Solution

Click to see the complete solution
  1package crypto
  2
  3import (
  4	"crypto/aes"
  5	"crypto/cipher"
  6	"crypto/hmac"
  7	"crypto/rand"
  8	"crypto/sha256"
  9	"encoding/base64"
 10	"errors"
 11	"fmt"
 12
 13	"golang.org/x/crypto/bcrypt"
 14)
 15
 16// HashPassword hashes a password using bcrypt
 17func HashPassword(password string) {
 18	hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
 19	if err != nil {
 20		return "", err
 21	}
 22	return string(hash), nil
 23}
 24
 25// VerifyPassword verifies a password against a hash
 26func VerifyPassword(password, hash string) bool {
 27	err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
 28	return err == nil
 29}
 30
 31// GenerateRandomBytes generates cryptographically secure random bytes
 32func GenerateRandomBytes(n int) {
 33	b := make([]byte, n)
 34	_, err := rand.Read(b)
 35	if err != nil {
 36		return nil, err
 37	}
 38	return b, nil
 39}
 40
 41// GenerateRandomString generates a random base64 string
 42func GenerateRandomString(length int) {
 43	b, err := GenerateRandomBytes(length)
 44	if err != nil {
 45		return "", err
 46	}
 47	return base64.URLEncoding.EncodeToString(b)[:length], nil
 48}
 49
 50// EncryptAES encrypts data using AES-256-GCM
 51func EncryptAES(plaintext, key []byte) {
 52	if len(key) != 32 {
 53		return nil, errors.New("key must be 32 bytes for AES-256")
 54	}
 55
 56	block, err := aes.NewCipher(key)
 57	if err != nil {
 58		return nil, err
 59	}
 60
 61	gcm, err := cipher.NewGCM(block)
 62	if err != nil {
 63		return nil, err
 64	}
 65
 66	nonce := make([]byte, gcm.NonceSize())
 67	if _, err := rand.Read(nonce); err != nil {
 68		return nil, err
 69	}
 70
 71	ciphertext := gcm.Seal(nonce, nonce, plaintext, nil)
 72	return ciphertext, nil
 73}
 74
 75// DecryptAES decrypts data using AES-256-GCM
 76func DecryptAES(ciphertext, key []byte) {
 77	if len(key) != 32 {
 78		return nil, errors.New("key must be 32 bytes for AES-256")
 79	}
 80
 81	block, err := aes.NewCipher(key)
 82	if err != nil {
 83		return nil, err
 84	}
 85
 86	gcm, err := cipher.NewGCM(block)
 87	if err != nil {
 88		return nil, err
 89	}
 90
 91	nonceSize := gcm.NonceSize()
 92	if len(ciphertext) < nonceSize {
 93		return nil, errors.New("ciphertext too short")
 94	}
 95
 96	nonce, ciphertext := ciphertext[:nonceSize], ciphertext[nonceSize:]
 97	plaintext, err := gcm.Open(nil, nonce, ciphertext, nil)
 98	if err != nil {
 99		return nil, err
100	}
101
102	return plaintext, nil
103}
104
105// HashSHA256 computes SHA-256 hash
106func HashSHA256(data []byte) []byte {
107	hash := sha256.Sum256(data)
108	return hash[:]
109}
110
111// GenerateHMAC generates HMAC-SHA256
112func GenerateHMAC(message, key []byte) []byte {
113	h := hmac.New(sha256.New, key)
114	h.Write(message)
115	return h.Sum(nil)
116}
117
118// VerifyHMAC verifies HMAC-SHA256
119func VerifyHMAC(message, key, mac []byte) bool {
120	expected := GenerateHMAC(message, key)
121	return hmac.Equal(mac, expected)
122}

Key Takeaways

  1. Use crypto/rand: Never use math/rand for cryptographic operations
  2. Bcrypt for Passwords: Use bcrypt, not plain hashing
  3. AES-GCM: Provides both encryption and authentication
  4. Key Size: AES-256 requires 32-byte keys
  5. Constant-Time Comparison: Use hmac.Equal to prevent timing attacks