# Cryptography Security Rules

Cryptography vulnerabilities threaten confidentiality and integrity of sensitive data.

**Rules:**

1. TLS MUST use 1.2+.
2. NEVER use DES, RC4, MD5, or SHA1 for security purposes.
3. SSH host keys MUST be verified — NEVER use `InsecureIgnoreHostKey`.
4. Passwords MUST be hashed with Argon2id (preferred) or bcrypt.
5. Security-critical randomness MUST use `crypto/rand`.

---

## Algorithm Selection Guide

Choose the right algorithm for the job — using the wrong primitive (e.g. SHA256 for passwords) is as dangerous as using a broken one:

| Use Case | Recommended | Avoid | Why |
| --- | --- | --- | --- |
| Symmetric encryption | AES-256-GCM, ChaCha20-Poly1305 | DES, 3DES, AES-ECB, RC4 | ECB reveals patterns; DES/RC4 are broken |
| Password hashing | Argon2id (preferred), bcrypt, scrypt | MD5, SHA-1, plain SHA-256 | Fast hashes enable brute-force; memory-hard functions resist GPU attacks |
| Message authentication | HMAC-SHA256, Poly1305 | HMAC-MD5, HMAC-SHA1 | MD5/SHA1 have known collision weaknesses |
| Digital signatures | Ed25519, ECDSA P-256 | RSA-PKCS1v1.5 | PKCS1v1.5 has padding oracle vulnerabilities |
| Key exchange | X25519, ECDH P-256 | Static RSA key transport | Forward secrecy requires ephemeral keys |
| Random generation | `crypto/rand` | `math/rand` | `math/rand` output is predictable |
| TLS | TLS 1.2+ (prefer 1.3) | TLS 1.0, 1.1, SSL | Known attacks (BEAST, POODLE) on older versions |

### Key Size Requirements

| Algorithm | Minimum Key Size         | Recommended      |
| --------- | ------------------------ | ---------------- |
| RSA       | 2048 bits                | 4096 bits        |
| AES       | 128 bits                 | 256 bits         |
| ECDSA     | P-256 (128-bit security) | P-256 or Ed25519 |

---

## Key Rotation Pattern

Keys should be rotated periodically. Use envelope encryption so rotating the Key Encryption Key (KEK) doesn't require re-encrypting all data:

```go
// Envelope encryption: encrypt data with a DEK, encrypt DEK with KEK
func EnvelopeEncrypt(kek, plaintext []byte) (encryptedDEK, ciphertext []byte, err error) {
    // 1. Generate random Data Encryption Key
    dek := make([]byte, 32)
    if _, err := rand.Read(dek); err != nil {
        return nil, nil, err
    }

    // 2. Encrypt data with DEK
    ciphertext, err = EncryptAESGCM(dek, plaintext)
    if err != nil {
        return nil, nil, err
    }

    // 3. Encrypt DEK with KEK
    encryptedDEK, err = EncryptAESGCM(kek, dek)
    if err != nil {
        return nil, nil, err
    }

    return encryptedDEK, ciphertext, nil
}

func EnvelopeDecrypt(kek, encryptedDEK, ciphertext []byte) ([]byte, error) {
    dek, err := DecryptAESGCM(kek, encryptedDEK)
    if err != nil {
        return nil, err
    }
    return DecryptAESGCM(dek, ciphertext)
}

func EncryptAESGCM(key, plaintext []byte) ([]byte, error) {
    block, err := aes.NewCipher(key)
    if err != nil { return nil, err }
    aead, err := cipher.NewGCM(block)
    if err != nil { return nil, err }
    nonce := make([]byte, aead.NonceSize())
    if _, err := rand.Read(nonce); err != nil { return nil, err }
    return aead.Seal(nonce, nonce, plaintext, nil), nil
}

func DecryptAESGCM(key, ciphertext []byte) ([]byte, error) {
    block, err := aes.NewCipher(key)
    if err != nil { return nil, err }
    aead, err := cipher.NewGCM(block)
    if err != nil { return nil, err }
    nonceSize := aead.NonceSize()
    if len(ciphertext) < nonceSize {
        return nil, errors.New("ciphertext too short")
    }
    return aead.Open(nil, ciphertext[:nonceSize], ciphertext[nonceSize:], nil)
}
```

When the KEK is rotated, only re-encrypt the DEKs (small), not the data (potentially large).

---

## Common Cryptographic Mistakes

### Mistake 1: AES-ECB reveals patterns — High

ECB encrypts each block independently — identical plaintext blocks produce identical ciphertext blocks, revealing data structure:

```go
// Bad — ECB mode reveals patterns in structured data
block, _ := aes.NewCipher(key)
// Using block.Encrypt directly = ECB mode

// Good — GCM provides authenticated encryption
aead, _ := cipher.NewGCM(block) // randomized, authenticated
nonce := make([]byte, aead.NonceSize())
rand.Read(nonce)
ciphertext := aead.Seal(nonce, nonce, plaintext, nil)
```

### Mistake 2: Reusing nonces — Critical

A nonce reuse with AES-GCM completely breaks confidentiality and authentication:

```go
// Bad — static or reused nonce
nonce := []byte("fixed_nonce!") // catastrophic with GCM

// Good — random nonce per encryption
nonce := make([]byte, 12) // 96-bit for GCM
rand.Read(nonce)
```

### Mistake 3: Non-constant-time comparison for secrets — Medium

Comparing secrets with `==` short-circuits on the first differing byte, leaking timing information. See [Network/Web Security — Observable Timing](./network.md) for constant-time comparison patterns using `crypto/subtle`.

---

## Insecure TLS Configuration — High

Using insecure TLS configurations can expose your application to man-in-the-middle attacks.

**Bad:**

```go
transport := &http.Transport{
    TLSClientConfig: &tls.Config{
        InsecureSkipVerify: true, // DON'T: verify certificates
    },
}
```

**Good:**

```go
import "crypto/tls"

func secureConfig() *tls.Config {
    return &tls.Config{
        MinVersion:       tls.VersionTLS12,
        CurvePreferences: []tls.CurveID{tls.X25519, tls.CurveP256},
    }
}
```

---

## DES Encryption — High

DES is cryptographically broken.

**Bad:**

```go
import "crypto/des"
block, _ := des.NewCipher(key) // DON'T: broken
```

**Good:**

```go
import "crypto/aes"
block, _ := aes.NewCipher(key)     // OK: AES
cipher.NewGCM(block)              // OK: GCM for auth
```

---

## Insecure SSH Host Key Verification — High

**Bad:**

```go
import "golang.org/x/crypto/ssh"
&ssh.ClientConfig{
    HostKeyCallback: ssh.InsecureIgnoreHostKey(), // DON'T
}
```

**Good:**

```go
import "golang.org/x/crypto/ssh"
&ssh.ClientConfig{
    HostKeyCallback: ssh.FixedHostKey(publicKey),
}
```

---

## MD5 Hash — High

MD5 is collision-prone and weak for security.

**Bad:**

```go
import "crypto/md5"
hash := md5.Sum([]byte(data)) // DON'T: weak
```

**Good:**

```go
// For password hashing:
import "golang.org/x/crypto/argon2"
hash := argon2.IDKey([]byte(pw), salt, 3, 64*1024, 4, 32)

// Or bcrypt (simpler API, no salt management):
import "golang.org/x/crypto/bcrypt"
hash, _ := bcrypt.GenerateFromPassword([]byte(pw), bcrypt.DefaultCost)

// For general-purpose hashing (not passwords):
import "crypto/sha256"
digest := sha256.Sum256(data)
```

---

## RC4 Cipher — High

RC4 is cryptographically broken.

**Bad:**

```go
import "crypto/rc4"
cipher, _ := rc4.NewCipher(key) // DON'T: broken
```

**Good:**

```go
import "crypto/cipher"
import "crypto/aes"
aead, _ := cipher.NewGCM(block) // OK: AES-GCM

// Or ChaCha20:
import "golang.org/x/crypto/chacha20poly1305"
aead, _ := chacha20poly1305.New(key)
```

---

## SHA1 Hash — Medium

SHA1 provides insufficient collision resistance.

**Bad:**

```go
import "crypto/sha1"
hash := sha1.Sum(data) // DON'T: weak
```

**Good:**

```go
import "crypto/sha256"
hash := sha256.Sum256(data)
```

---

## Weak Cryptographic Algorithms — Medium

**Bad:**

```go
import "crypto/hmac"
import "crypto/md5"
mac := hmac.New(md5.New, key) // DON'T: HMAC-MD5
```

**Good:**

```go
import "crypto/sha256"
mac := hmac.New(sha256.New, key)
```

---

## Insufficient Key Strength — Medium

RSA keys smaller than 2048 bits are insufficient.

**Bad:**

```go
import "crypto/rsa"
key, _ := rsa.GenerateKey(rand.Reader, 1024) // DON'T: too weak
```

**Good:**

```go
key, _ := rsa.GenerateKey(rand.Reader, 4096) // OK: 2048+ bits
```

---

## Weak Random Number Generators — High

`math/rand` is predictable, never use for security.

**Bad:**

```go
import "math/rand"
bytes := make([]byte, 16)
rand.Read(bytes) // DON'T: predictable
```

**Good:**

```go
import "crypto/rand"
_, err := rand.Read(bytes) // OK: cryptographically secure
```

---

## Weak TLS Versions — High

TLS 1.0 and 1.1 have known vulnerabilities.

**Bad:**

```go
import "crypto/tls"
&tls.Config{MinVersion: tls.VersionTLS10} // DON'T
```

**Good:**

```go
&tls.Config{MinVersion: tls.VersionTLS12} // OK
```

---

## Password Hashing — High

Don't use MD5, SHA1, or single-iteration hashes for passwords.

**Bad:**

```go
import "crypto/sha256"
hash := sha256.Sum256([]byte(password)) // DON'T: too fast
```

**Good:**

```go
// Argon2id (preferred) — memory-hard, resists GPU attacks:
import "golang.org/x/crypto/argon2"
key := argon2.IDKey([]byte(password), salt, 3, 64*1024, 4, 32)

// Or bcrypt (simpler API, widely supported):
import "golang.org/x/crypto/bcrypt"
hash, _ := bcrypt.GenerateFromPassword([]byte(pw), bcrypt.DefaultCost)

// Or PBKDF2 with 600,000+ iterations:
import "golang.org/x/crypto/pbkdf2"
key := pbkdf2.Key([]byte(password), salt, 600000, 32, sha512.New)

// Or scrypt:
import "golang.org/x/crypto/scrypt"
key := scrypt.Key([]byte(password), salt, 32768, 8, 1, 32)
```

---

## CWE References

- **CWE-327**: Use of a Broken or Risky Cryptographic Algorithm
- **CWE-331**: Insufficient Entropy
- **CWE-326**: Inadequate Encryption Strength
- **CWE-295**: Improper Certificate Validation
- **CWE-330**: Use of Insufficiently Random Values
- **CWE-916**: Use of Password Hash With Insufficient Computational Effort
