AES Encryption
๐ฅ Vibe Prompt
"Implement AES-256-GCM encryption: input a string and password, output base64 ciphertext."
from cryptography.fernet import Fernet
key = Fernet.generate_key()
cipher = Fernet(key)
encrypted = cipher.encrypt(b"Hello, Vibe Coding!")
print(f"Encrypted: {encrypted.decode()}")
decrypted = cipher.decrypt(encrypted)
print(f"Decrypted: {decrypted.decode()}")
Manual AES-256-GCM
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
import os
aes_key = AESGCM.generate_key(bit_length=256)
aesgcm = AESGCM(aes_key)
nonce = os.urandom(12)
ct = aesgcm.encrypt(nonce, b"Secret data", None)
pt = aesgcm.decrypt(nonce, ct, None)
print(f"AES-256-GCM: {pt.decode()}")
Applications
- Database field encryption
- Config file secrets
- Communication encryption
- File encryption
Best Practices
- โ Use GCM mode (authenticated encryption)
- โ Generate new nonce for each encryption
- โ Store key securely (e.g., KMS, Vault)
- โ Rotate keys regularly
Chapter Summary
- Understand core concepts and principles
- Master implementation methods and techniques
- Familiar with common issues and solutions
- Able to apply in real projects
Further Reading
- Official documentation and API references
- Open source examples on GitHub
- Technical books and online courses
- Community discussions and tech blogs
Implementation Example
Basic Example
# This section provides a complete implementation example
Steps
- Setup: Configure development environment
- Data: Prepare required data
- Implementation: Build core functionality
- Testing: Verify correctness
- Optimization: Improve performance
Common Errors
| Error Type | Cause | Solution | |------------|-------|----------| | Compilation | Syntax | Check code syntax | | Runtime | Environment | Verify dependencies installed | | Logic | Algorithm | Step-by-step debugging | | Performance | Efficiency | Use profilers |
Code Example
import sys
def main():
print("Hello, World!")
if __name__ == "__main__":
main()
References
- Official documentation
- API reference
- Open source examples
- Community discussions
How AES Works
AES operates on 128-bit blocks using a substitution-permutation network.
Encryption Round Structure
| Step | Name | What Happens | |------|------|--------------| | 1 | SubBytes | Each byte replaced using S-box (non-linear substitution) | | 2 | ShiftRows | Rows shifted left by different amounts (diffusion) | | 3 | MixColumns | Columns mixed using matrix multiplication (diffusion) | | 4 | AddRoundKey | Round key XORed with state (encryption) |
Key Sizes and Rounds
| AES Variant | Key Size | Rounds | Security Level | |-------------|----------|--------|----------------| | AES-128 | 128 bits | 10 | Secure for now | | AES-192 | 192 bits | 12 | Highly secure | | AES-256 | 256 bits | 14 | Maximum security |
Modes of Operation
Block cipher modes determine how blocks are chained together.
Comparison Table
| Mode | IV Required | Parallelizable | Error Propagation | Common Use | |------|-------------|----------------|-------------------|------------| | ECB | No | Yes | None (block) | โ Avoid! Patterns visible | | CBC | Yes (16 bytes) | Decryption only | Full block | Legacy systems | | GCM | Yes (12 bytes) | Yes | Detectable | โ Preferred for most uses | | CTR | Yes (16 bytes) | Yes | None (bit) | Streaming, disk encryption |
ECB vs CBC Visual Example
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
import os
# ECB mode โ identical plaintext blocks produce identical ciphertext
def aes_ecb_encrypt(plaintext: bytes, key: bytes) -> bytes:
cipher = AES.new(key, AES.MODE_ECB)
return cipher.encrypt(pad(plaintext, AES.block_size))
# CBC mode โ each block depends on previous (more secure)
def aes_cbc_encrypt(plaintext: bytes, key: bytes) -> tuple:
iv = os.urandom(16)
cipher = AES.new(key, AES.MODE_CBC, iv)
return iv + cipher.encrypt(pad(plaintext, AES.block_size))
# Never use ECB in practice โ patterns leak!
AES-GCM (Recommended)
GCM provides both encryption and authentication (no separate HMAC needed).
from Crypto.Cipher import AES
import os
def encrypt_gcm(plaintext: bytes, key: bytes) -> dict:
"""Encrypt with AES-256-GCM (authenticated encryption)."""
cipher = AES.new(key, AES.MODE_GCM)
ciphertext, tag = cipher.encrypt_and_digest(plaintext)
return {
"ciphertext": ciphertext,
"nonce": cipher.nonce, # 12 bytes (stored with ciphertext)
"tag": tag # 16 bytes (authentication tag)
}
def decrypt_gcm(encrypted: dict, key: bytes) -> bytes:
"""Decrypt and verify AES-256-GCM."""
cipher = AES.new(key, AES.MODE_GCM, nonce=encrypted["nonce"])
plaintext = cipher.decrypt_and_verify(
encrypted["ciphertext"],
encrypted["tag"]
)
return plaintext
# Example usage
key = os.urandom(32) # AES-256
message = b"This is a secret message. Encrypt it!"
encrypted = encrypt_gcm(message, key)
print(f"Ciphertext: {encrypted['ciphertext'].hex()}")
print(f"Nonce: {encrypted['nonce'].hex()}")
print(f"Tag: {encrypted['tag'].hex()}")
# Decrypt
decrypted = decrypt_gcm(encrypted, key)
print(f"Decrypted: {decrypted.decode()}")
# Tampered ciphertext fails
encrypted["ciphertext"] = encrypted["ciphertext"][:-1] + b"\x00"
try:
decrypt_gcm(encrypted, key)
print("Tampered: BAD โ should have failed!")
except Exception as e:
print(f"Tampered: Correctly rejected โ {e}")
Key Management Best Practices
| Practice | Why | |----------|-----| | Never hardcode keys | Keys in code are exposed in breaches | | Use environment variables | Separates config from code | | Rotate keys periodically | Limits damage if key is compromised | | Use key derivation (PBKDF2) | Converts passwords to strong keys | | Store in vault/kms | AWS KMS, HashiCorp Vault | | Separate keys per service | One breach doesn't expose everything |
from Crypto.Protocol.KDF import PBKDF2
import os
def derive_key(password: str, salt: bytes = None) -> tuple:
"""Derive a strong AES key from a password."""
if salt is None:
salt = os.urandom(16)
key = PBKDF2(password, salt, dkLen=32, count=100000)
return key, salt
Summary
AES is the global standard for symmetric encryption. Use AES-256-GCM for authenticated encryption โ it provides confidentiality, integrity, and authenticity in one operation.
Key takeaways:
- AES: 128-bit block cipher, key sizes 128/192/256 bits |
- ECB mode is insecure โ identical blocks produce identical ciphertext |
- CBC mode needs IV, provides chaining, legacy but usable |
- GCM mode provides authenticated encryption (encrypt + MAC) |
- Use AES-256-GCM for all new applications |
- Never hardcode keys โ use env vars, vault, or KMS |
- Derive keys from passwords using PBKDF2 with high iteration count |
- Store nonce alongside ciphertext (nonce is NOT secret) |
What's Next: RSA and Asymmetric Encryption
The next chapter covers RSA public-key cryptography.