← Back to all articles

AES in Practice: Modes, IVs and Authenticated Encryption

EncryptionPitfalls

AES is only a block cipher

AES transforms 16 bytes at a time. Encrypting arbitrary data requires a mode of operation (GCM, CBC…) that chains blocks together. Saying "I use AES" is therefore incomplete, the mode and parameters matter just as much.

Mode comparison

ModeAuthenticatedIV requirementRecommendation
GCMYes (AEAD)12 random bytes, never reuseFirst choice: secrecy plus tamper detection
CBCNo16 random bytesRequires a separate HMAC
ECBNoNoneAvoid: identical plaintext blocks leak patterns

Why the IV must never repeat

GCM derives its keystream from the IV and a counter. Reusing an IV under the same key overlaps two keystreams, an attacker XORs the ciphertexts to recover plaintext, and it also breaks the authentication tag. In practice, generate a fresh random IV per encryption and store it next to the ciphertext (the IV does not need to be secret).

A correct encryption flow

  1. Generate 12 random bytes as the IV;
  2. Encrypt with AES-GCM, producing ciphertext and an authentication tag;
  3. Store or transmit IV + ciphertext + tag together (do not encrypt the IV);
  4. On decryption, always verify the tag and fail hard, never use partial plaintext.

Where does the key come from

A user password is not a key, its entropy is far below 128 bits. Derive a key with a KDF (PBKDF2, scrypt, Argon2) using a strong work factor, or generate a random key with a CSPRNG and solve key storage separately.

Common questions

Is an IV the same as a salt? No: the IV randomises the encryption so the same plaintext encrypts differently; the salt randomises key derivation so the same password yields a different key. Do I still need to hash after encrypting? AEAD modes already provide integrity; only CBC needs an additional MAC.

A storage format that ages well

// Keep the IV and tag next to the ciphertext, self-describing
{
  "v": 1,              // version, so you can migrate later
  "alg": "AES-GCM",
  "iv": "base64...",   // 12 random bytes
  "ct": "base64...",   // ciphertext
  "tag": "base64..."   // auth tag (some libraries append it to ct)
}

Keeping the key safe

  1. Server keys belong in a KMS or environment variables, never in source control;
  2. For long-lived data, use envelope encryption: a master key in a KMS wraps per-record data keys;
  3. On the client, store keys in a password manager or the OS keychain, not localStorage;
  4. Plan rotation: an algorithm upgrade or a suspected leak must migrate smoothly, which is why the version field exists.

The consequences of nonce reuse

  • Catastrophic in GCM: reusing a nonce under one key lets an attacker recover the authentication key and forge messages, worse than plaintext leakage;
  • Inferable plaintext in CBC: the same IV and a shared plaintext prefix yield identical ciphertext blocks, revealing structure (ECB's famous penguin is the extreme case);
  • How to generate them: prefer a random 96-bit nonce with a cap on encryptions per key, or a counter you are certain cannot wrap;
  • Rotate keys: when approaching the limit, change the key rather than pushing on.