Explainer · 7 June 2026

How symmetric encryption and AES work

You back up a database to object storage. The file is encrypted with a 256-bit key stored in your secrets manager. An attacker who steals the backup blob without the key sees pseudorandom noise. That is symmetric encryption: the same secret key transforms plaintext into ciphertext and back again. The Advanced Encryption Standard (AES) is the algorithm behind most of that scrambling today — inside TLS sessions, BitLocker and FileVault volumes, encrypted messaging backups, and countless application-level vaults. Understanding AES is less about memorizing S-box math and more about knowing which mode you are using, how IVs are generated, and why "we use AES-256" on a slide does not automatically mean the system is secure.

Symmetric vs asymmetric: one key vs a key pair

In symmetric schemes, Alice and Bob share one key K. Encrypt: C = E(K, P). Decrypt: P = D(K, C). Speed is the advantage — modern CPUs have AES-NI instructions that encrypt gigabytes per second. The weakness is distribution: how do you get K to Bob without an attacker listening?

Asymmetric cryptography (RSA, ECDSA, Ed25519) solves distribution with a public/private key pair — see passkeys and WebAuthn for a user-facing example. In practice, TLS and most encrypted protocols use asymmetric crypto only to authenticate the server and negotiate a fresh symmetric session key, then switch to AES for bulk data. Symmetric does the heavy lifting; asymmetric bootstraps trust.

Block ciphers and the AES round structure

AES is a block cipher: it encrypts fixed 128-bit (16-byte) blocks. Keys can be 128, 192, or 256 bits — more rounds for longer keys (10, 12, or 14 rounds). Each round applies substitutions, row shifts, column mixing, and XOR with a round key derived from the master key via a key schedule. Decryption inverts those steps.

Real messages are rarely exactly 16 bytes. Modes of operation define how to chain block encryptions across longer plaintext. Picking AES without specifying the mode is like saying "we use SHA" without saying SHA-256 vs MD5 — the primitive is only half the story.

  • ECB (Electronic Codebook) — encrypt each block independently. Identical plaintext blocks produce identical ciphertext blocks. The classic "ECB penguin" image shows why this leaks structure. Do not use ECB for structured data.
  • CBC (Cipher Block Chaining) — XOR each plaintext block with the previous ciphertext block before encrypting. Requires a random initialization vector (IV) for the first block. Decryption errors propagate but encryption is sequential — harder to parallelize.
  • CTR (Counter) — encrypt a counter value per block and XOR the keystream with plaintext. Turns AES into a stream cipher: parallelizable, no padding needed for partial blocks. Nonce reuse in CTR is catastrophic (two plaintexts XOR to recover data).
  • GCM (Galois/Counter Mode) — CTR encryption plus a polynomial MAC for authenticated encryption (AEAD). Outputs ciphertext and an authentication tag; tampering is detected on decrypt. GCM is the default pairing with AES in TLS 1.3 and modern libraries. Prefer AES-GCM or ChaCha20-Poly1305 over bare CBC.

IVs, nonces, and the reuse rule

An IV (CBC) or nonce (CTR/GCM) ensures the same plaintext encrypted twice under the same key produces different ciphertext — stopping dictionary-style attacks and pattern leakage. Rules:

  • Random IV per message — generate with a CSPRNG; store alongside ciphertext (not secret, but must be unique per key).
  • Never reuse a (key, nonce) pair in GCM — nonce reuse breaks confidentiality and forgery resistance; attackers can recover the authentication key and forge tags.
  • Deterministic IVs are dangerous — deriving IV from a hash of the plaintext leaks equality of messages.

Libraries like libsodium and Google Tink hide IV handling behind high-level APIs (secretbox, Aead). If you are manually concatenating IV || ciphertext, document the byte layout and test decrypt round-trips in CI.

From passwords to keys: derivation and stretching

User passwords are not valid AES keys — they are low-entropy and guessable. Key derivation functions (KDFs) stretch passwords into cryptographic keys:

  • PBKDF2 — HMAC iterated thousands of times; still acceptable with high iteration counts but weaker against GPU crackers than memory-hard options.
  • scrypt / Argon2 — memory-hard KDFs that make parallel brute force expensive. Argon2id is the current recommendation for password-based encryption.
  • HKDF — expands a already-strong shared secret (from ECDH in TLS, for example) into separate keys for encryption and MAC without reusing material.

Wallet software that encrypts a seed phrase with a user passphrase is running this pipeline: Argon2 or PBKDF2 produces a key, then AES-GCM encrypts the seed with a random nonce. Losing the passphrase is equivalent to losing the key — no back door in correct designs.

Where AES shows up in production

  • TLS 1.3 — after the handshake, application data flows as AES-128-GCM or ChaCha20-Poly1305 records. Certificate validation and key exchange are separate concerns covered in our TLS certificate chains explainer.
  • Disk and database encryption — LUKS, BitLocker, RDS encryption at rest, S3 SSE — all AES-based with per-volume or per-object keys wrapped by a master key in KMS/HSM.
  • Application secrets — encrypting API tokens in a database column, envelope encryption where a data key encrypts payloads and a KMS key encrypts the data key.
  • JWT HS256 — HMAC-SHA256 is symmetric signing, not AES, but the same key-distribution problem applies; our JWT guide contrasts HS256 with asymmetric RS256.

On-chain systems rarely put AES inside smart contracts — gas cost and public state make private symmetric encryption a poor fit. Off-chain indexers, custodial APIs, and wallet local storage are where AES still protects Solana-adjacent data.

Stream ciphers: ChaCha20 as an alternative

Not all fast symmetric encryption is AES. ChaCha20-Poly1305 is a stream cipher plus MAC, designed by Daniel Bernstein, that avoids hardware AES-NI yet performs well on mobile and ARM chips. TLS 1.3 and WireGuard often prefer ChaCha20 when AES-NI is absent. Choice between AES-GCM and ChaCha20 is usually about hardware support and side-channel history, not "which is more secure" at equal implementations.

Implementation pitfalls

  • ECB or unauthenticated CBC — padding oracle attacks (CVE classics against ASP.NET and older TLS) exploited CBC without authentication. Use AEAD.
  • Static IV across messages — undermines semantic security; attackers learn when two ciphertexts share prefixes.
  • Nonce counter collisions in distributed systems — two servers incrementing the same counter under one key can repeat nonces. Partition keys or use random 96-bit nonces with collision probability negligible.
  • Rolling your own crypto — composing AES + HMAC-SHA256 manually with incorrect encrypt-then-MAC ordering has burned teams. Use vetted constructions from libsodium, Tink, or language stdlib high-level APIs.
  • Key in source code — symmetric keys in git history are symmetric keys for everyone. Use KMS, sealed secrets, or envelope encryption with rotation.

Practical checklist

  • Specify algorithm and mode (e.g. AES-256-GCM), not "AES" alone.
  • Generate unique random nonces/IVs per encryption under a given key.
  • Prefer AEAD (GCM, ChaCha20-Poly1305) over encrypt-then-MAC hand-rolls.
  • Derive keys from passwords with Argon2id or scrypt, not raw SHA-256.
  • Store keys in HSM/KMS; rotate data keys without re-encrypting entire archives when envelope encryption allows.
  • Test tamper detection — flip a ciphertext bit and confirm decrypt fails.

Related on Solana Garden: TLS and HTTPS explained, JWT explained, Passkeys and WebAuthn explained, Zero-knowledge proofs explained, Explainers hub.