Guide

Symmetric encryption uses one key to encrypt and decrypt. The standard cipher is AES, which works on 16-byte blocks. Modern CPUs run AES in hardware, so encryption is rarely the bottleneck.

What matters is the mode and the integrity check. Use AEAD (authenticated encryption with associated data). It gives confidentiality and integrity in one call. Decryption either returns the exact original bytes or fails loudly.

Concept

A mode says how to chain 16-byte blocks to encrypt a long message. A nonce is a number used once per message. It makes the same plaintext encrypt to different bytes each time.

The big idea

ECB leaks patterns, since identical blocks give identical output. CBC and CTR hide patterns but don't detect tampering. AEAD fixes both. Two AEAD ciphers cover most needs:

AEADKeyNonceTagNotes
AES-256-GCM32 bytes12 bytes16 bytesFastest with AES hardware. The default in TLS and cloud KMS
XChaCha20-Poly130532 bytes24 bytes16 bytesFast in pure software. The nonce is big enough to pick at random

Walking through it

Python
import os
from cryptography.hazmat.primitives.ciphers.aead import AESGCM

aead = AESGCM(AESGCM.generate_key(bit_length=256))

def encrypt(plaintext: bytes, context: bytes) -> bytes:
    nonce = os.urandom(12)                                  # fresh for every message
    return nonce + aead.encrypt(nonce, plaintext, context)

def decrypt(blob: bytes, context: bytes) -> bytes:
    return aead.decrypt(blob[:12], blob[12:], context)      # raises InvalidTag on any change

blob = encrypt(b"123-45-6789", b"users.ssn:user_42")
print(decrypt(blob, b"users.ssn:user_42"))
try:
    decrypt(blob, b"users.ssn:user_43")
except Exception as e:
    print("rejected:", type(e).__name__)

Output:

b'123-45-6789'
rejected: InvalidTag

The context is associated data: authenticated, not encrypted. An attacker who copies user 42's encrypted SSN into another row fails decryption, because the context no longer matches.

The nonce rule

Reusing a nonce with the same key is catastrophic. XORing two ciphertexts cancels the keystream and leaves the XOR of the two plaintexts. With GCM, it can also expose the authentication key, which lets an attacker forge messages.

Random 96-bit nonces are safe only up to a limit, because of the birthday bound. NIST caps random-nonce GCM at 2^32 messages per key. A service that encrypts 10,000 messages per second reaches that in about 5 days. So rotate keys, or use XChaCha20-Poly1305 with its 192-bit nonce.

Important

Don't compose primitives yourself. Use a maintained library such as libsodium, Google Tink or Python's cryptography.

Envelope encryption

Where does the AES key live? If it sits in an environment variable next to the database password, one leak exposes both. A cloud KMS, such as AWS KMS, Google Cloud KMS or Azure Key Vault, keeps top-level keys in hardware security modules. You ask the KMS to perform operations, and it never reveals the key.

KMS calls are slow, and AWS KMS's Encrypt accepts only 4 KB. So you use envelope encryption:

A key encryption key (KEK) stays inside the KMS. A data encryption key (DEK) encrypts your data locally. The KMS returns the DEK in two forms: plaintext, and wrapped, meaning encrypted under the KEK. You store the wrapped copy beside the ciphertext.

This gives you local-speed bulk encryption, and a stolen database dump is useless without KMS access. Rotating the KEK only rewraps small DEKs. Deleting a tenant's DEK makes their data unreadable, a technique called crypto-shredding.

Common mistakes

  • Reusing a nonce under one key, or using a counter that resets on restart.
  • Using ECB, CBC or CTR without a MAC, or any custom "encrypt then hash" scheme.
  • Storing the KEK or a plaintext DEK next to the data it protects.

Check yourself

  • Why is a repeated GCM nonce so damaging? Answer: XORing the two ciphertexts reveals the XOR of the plaintexts, and it can expose the authentication key.
  • Why wrap a data key with the KMS instead of sending every record to the KMS? Answer: KMS calls are slow and the API accepts only small payloads. The DEK encrypts bulk data locally.

Key takeaways

  • Use an AEAD, such as AES-256-GCM or XChaCha20-Poly1305, through a high-level library.
  • Never reuse a nonce under the same key. Random 96-bit nonces run out of safe room at about 2^32 messages.
  • Envelope encryption keeps the master key in a KMS and does the bulk work locally.
  • Bind ciphertext to its context, so it can't be moved to another row.

Further learning

The operational side of key storage is covered in secrets and encryption. Next, read public-key crypto and signatures.