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
- Use crypto/rand: Never use math/rand for cryptographic operations
- Bcrypt for Passwords: Use bcrypt, not plain hashing
- AES-GCM: Provides both encryption and authentication
- Key Size: AES-256 requires 32-byte keys
- Constant-Time Comparison: Use hmac.Equal to prevent timing attacks
Related Topics
- Cryptography - Main crypto tutorial
- Security Practices - Security guidelines