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

  1. Setup: Configure development environment
  2. Data: Prepare required data
  3. Implementation: Build core functionality
  4. Testing: Verify correctness
  5. 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.

Member Exclusive Free Tutorial

This chapter is free exclusive content for registered members! Please login or register to unlock immediately.

Login / Register Now