Setting the file. One moment.
Subchapter 21.5
references/cryptography.mdMarkdown8 KBView on GitHub
Recommended:
Avoid:
| Mode | Use Case | Notes |
|---|---|---|
| GCM | General purpose | Authenticated encryption (preferred) |
| CCM | Constrained environments | Authenticated encryption |
| CTR + HMAC | When GCM unavailable | Encrypt-then-MAC pattern |
| CBC | Legacy only | Requires separate MAC |
| ECB | Never for data | Reveals patterns |
# VULNERABLE: ECB mode
from Crypto.Cipher import AES
cipher = AES.new(key, AES.MODE_ECB)
# SAFE: GCM mode
cipher = AES.new(key, AES.MODE_GCM, nonce=nonce)
ciphertext, tag = cipher.encrypt_and_digest(plaintext)Recommended:
Avoid:
| Language | Safe | Unsafe |
|---|---|---|
| Python | secrets, os.urandom() | random module |
| JavaScript | crypto.randomBytes(), crypto.randomUUID() | Math.random() |
| Java | SecureRandom, UUID.randomUUID() | Math.random(), java.util.Random |
| PHP | random_bytes(), random_int() | rand(), mt_rand(), uniqid() |
| .NET | RandomNumberGenerator | Random() |
| Go | crypto/rand | math/rand |
| Ruby | SecureRandom | rand() |
# VULNERABLE: Predictable random
import random
token = ''.join(random.choices(string.ascii_letters, k=32))
# SAFE: Cryptographically secure
import secrets
token = secrets.token_urlsafe(32)# Check if UUID v4 is actually random
import uuid
# uuid.uuid4() uses os.urandom() in Python - SAFE
token = str(uuid.uuid4())# VULNERABLE: Key from password directly
key = password.encode()
# SAFE: Key derivation function
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
kdf = PBKDF2HMAC(
algorithm=hashes.SHA256(),
length=32,
salt=salt,
iterations=600000,
)
key = kdf.derive(password.encode())Do:
Don’t:
# VULNERABLE: Hardcoded key
KEY = b'super_secret_key_12345'
# VULNERABLE: Key in code as base64
KEY = base64.b64decode('c3VwZXJfc2VjcmV0X2tleQ==')
# SAFE: Load from secure source
KEY = secrets_manager.get_secret('encryption_key')When to rotate:
Rotation strategies:
# Two-key structure:
# - Data Encryption Key (DEK): Encrypts actual data
# - Key Encryption Key (KEK): Encrypts the DEK
def encrypt_with_envelope(plaintext, kek):
# Generate random DEK
dek = secrets.token_bytes(32)
# Encrypt data with DEK
cipher = AES.new(dek, AES.MODE_GCM)
ciphertext, tag = cipher.encrypt_and_digest(plaintext)
# Encrypt DEK with KEK
kek_cipher = AES.new(kek, AES.MODE_GCM)
encrypted_dek, dek_tag = kek_cipher.encrypt_and_digest(dek)
# Store encrypted_dek with ciphertext
return {
'ciphertext': ciphertext,
'tag': tag,
'encrypted_dek': encrypted_dek,
'dek_tag': dek_tag,
'nonce': cipher.nonce,
'dek_nonce': kek_cipher.nonce
}See authentication.md for password-specific hashing.
| Use Case | Algorithm |
|---|---|
| Integrity verification | SHA-256 or SHA-3 |
| HMAC | HMAC-SHA-256 |
| Key derivation | HKDF, PBKDF2 |
| Content addressing | SHA-256 |
Avoid for new systems:
# For integrity/checksums
import hashlib
digest = hashlib.sha256(data).hexdigest()
# For authentication (HMAC)
import hmac
mac = hmac.new(key, data, hashlib.sha256).digest()# VULNERABLE: MD5 for security purposes
import hashlib
checksum = hashlib.md5(data).hexdigest()
# VULNERABLE: SHA1 for signatures
signature = hashlib.sha1(data + secret).hexdigest()
# SAFE: SHA-256
checksum = hashlib.sha256(data).hexdigest()# VULNERABLE: Short key
key = b'short_key' # 9 bytes
# SAFE: Adequate key length
key = secrets.token_bytes(32) # 256 bits# VULNERABLE: Reused or predictable nonce
nonce = b'\x00' * 12 # Static nonce
# VULNERABLE: Counter-based without persistence
nonce = counter.to_bytes(12, 'big')
# SAFE: Random nonce
nonce = secrets.token_bytes(12)# VULNERABLE: ECB reveals patterns
cipher = AES.new(key, AES.MODE_ECB)
# SAFE: GCM hides patterns
cipher = AES.new(key, AES.MODE_GCM, nonce=nonce)# VULNERABLE: Encryption without authentication
cipher = AES.new(key, AES.MODE_CBC, iv=iv)
ciphertext = cipher.encrypt(pad(plaintext, 16))
# Vulnerable to bit-flipping, padding oracle
# SAFE: Authenticated encryption
cipher = AES.new(key, AES.MODE_GCM, nonce=nonce)
ciphertext, tag = cipher.encrypt_and_digest(plaintext)# Weak algorithms
grep -rn "MD5\|md5\|SHA1\|sha1\|DES\|des\|RC4\|rc4" --include="*.py" --include="*.js"
grep -rn "MODE_ECB\|ecb" --include="*.py" --include="*.js"
# Insecure random
grep -rn "Math\.random\|random\.random\|random\.randint" --include="*.py" --include="*.js"
grep -rn "mt_rand\|rand()" --include="*.php"
# Hardcoded keys
grep -rn "key\s*=\s*['\"]" --include="*.py" --include="*.js"
grep -rn "secret\s*=\s*['\"]" --include="*.py" --include="*.js"
grep -rn "AES\.new.*b'" --include="*.py"
# Static IVs/nonces
grep -rn "iv\s*=\s*b'\|nonce\s*=\s*b'" --include="*.py"
grep -rn "\\x00.*\\x00.*\\x00" --include="*.py"
# CBC without HMAC
grep -rn "MODE_CBC" --include="*.py" | grep -v "hmac\|mac\|tag"