Skip to content
yutils

How Encryption Actually Works

Symmetric (AES) vs asymmetric (RSA / ECC), why you never see RSA encrypting a file (hybrid encryption), block cipher modes (ECB pitfall vs GCM), key derivation (PBKDF2 / Argon2), at-rest vs in-transit, and the never-roll-your-own-crypto rule.

~10 min read

Every backend touches this daily, yet the precise meanings often aren't clear. Symmetric vs asymmetric? AES vs RSA? Why "encrypt a big file with RSA" doesn't work? This guide covers the core cryptographic primitives without algorithm internals — algorithms are the library's job; ours is to know which tool when.

Symmetric vs Asymmetric — One-Line Difference

Symmetric:
  encrypt(plaintext, KEY)   = ciphertext
  decrypt(ciphertext, KEY)  = plaintext
  → same key both ways

Representatives: AES, ChaCha20

Pros: fast (GB/s), small keys
Cons: how do you share the key? Both sides must already have it

────────────────────────────────────

Asymmetric:
  encrypt(plaintext, PUBLIC_KEY)    = ciphertext
  decrypt(ciphertext, PRIVATE_KEY)  = plaintext
  → different keys per direction

Representatives: RSA, ECC (Elliptic Curve)

Pros: safe key sharing (public can be public)
Cons: slow (MB/s), big keys (RSA 2048-bit = 256 bytes)
      Can only encrypt small data (~hundreds of bytes)

Why You Need Both — Hybrid Encryption

Why "RSA-encrypt a 100 MB file" doesn't work:
- RSA encrypt input limit = key size - padding (e.g. 245 bytes)
- 100 MB / 245 B = 408K calls = very slow + inefficient

Fix: hybrid encryption (TLS, GPG, age all use this pattern)

1. Generate a random AES key (32 bytes)
2. Encrypt the 100 MB with AES (fast)
3. Encrypt that AES key with an RSA public key (small, OK)
4. Send encrypted file + encrypted AES key together

Receiver:
1. Decrypt the AES key with RSA private key
2. Decrypt the file with the AES key

→ Best of both — AES's speed + RSA's safe key exchange.

Block Cipher Modes — Same Algorithm, Different Result

AES works on 16-byte blocks. Bigger data needs a way to chain blocks; the choice changes the outcome.

ECB — Never Use This

ECB (Electronic Codebook):
  Encrypt each block independently

block 1: AES(KEY, block1) = X
block 2: AES(KEY, block2) = Y
...

Problem: same plaintext block → same ciphertext block
→ Patterns are visible (especially in images)

Famous example: ECB-encrypted Tux Linux logo — outline still clearly
visible. Search "ECB penguin" — there it is.

CBC, CTR, GCM — Standard Modes

ModeCharacteristicsRecommendation
ECBIndependent blocks, reveals patterns❌ Never
CBCXOR with previous block, requires IV⚠ Legacy (padding-oracle risk)
CTRXOR with a counter (stream-like)✓ Fast, parallelizable
GCMCTR + integrity (authentication tag)✓✓ Recommended (encrypt + authenticate together)
ChaCha20-Poly1305Alternative (mobile / low-power CPU)✓✓ Recommended

Modern standard — AES-GCM or ChaCha20-Poly1305. Both AEAD (Authenticated Encryption with Associated Data) — encrypt + integrity together.

Key Derivation — Password → Key

Don't use the user's password "hunter2" directly as an AES key —
too short and low-entropy.
→ Use a KDF (Key Derivation Function) to derive a 32-byte AES key.

PBKDF2 (2000):
  output = PBKDF2(password, salt, iterations=600_000, sha256, 32 bytes)
  - More iterations = more expensive brute-force
  - Salt: same password → different key (per-user randomness)
  - Relatively weak against GPUs/ASICs

Argon2 (2015, modern recommendation):
  output = Argon2id(password, salt, time=3, memory=64MB, parallelism=4)
  - Strong against GPUs/ASICs (memory-hard)
  - Successor to bcrypt/scrypt
  - Standard for new applications

scrypt (2009):
  Similar, memory-hard. Recommended before Argon2.

bcrypt (1999):
  Standard for password hashing (not encryption KDF but password storage).

At-Rest vs In-Transit

At-Rest: encryption of stored data (DB, files, backups)
  - Default cloud DB encryption (AWS RDS, GCP Cloud SQL, etc.)
  - Whole-disk encryption (LUKS, BitLocker)
  - File-level or application-level encryption
  - Keys in KMS / Vault (envelope encryption)

In-Transit: data over the wire
  - TLS (HTTPS, SMTPS, IMAPS, DB connections)
  - mTLS between services
  - VPN, SSH

→ Both needed. At-rest only = network sniffing exposes everything.
→ In-transit only = disk leak exposes everything.

Never Roll Your Own Crypto

Common violations:
- "I implemented AES myself" — hard to dodge side channels (timing, power)
- "XOR-based simple encryption" — broken instantly (frequency analysis)
- Custom KDF — too few iterations, missing salt
- Custom random — Math.random() is NOT cryptographically secure

Rules:
- Algorithms: use libraries (libsodium, OpenSSL, Web Crypto API)
- Modes: standard (AES-GCM, ChaCha20-Poly1305)
- Random: crypto.getRandomValues / crypto.randomBytes
- KDF: Argon2 or PBKDF2

Why:
- Algorithms are vetted + standardized by cryptographers
- Side-channel defenses are implementation details
- Home-grown is almost always wrong — Schneier's adage, 25+ years old

Related Tools

  • SHA Hash — SHA-256 etc. (hash, different from encryption — one-way)
  • Bcrypt Hash — password hashing (a kind of KDF)
  • HMAC Generator — message authentication (integrity, not encryption)

Common Pitfalls

  • Using ECB — see above. No modern app should.
  • Reusing the same IV / nonce — breaks GCM security. Use a unique random IV per encryption.
  • Confusing hash with encryption — SHA-256 is one-way (can't decrypt). Encryption is two-way. Passwords → hash; data protection → encryption.
  • Math.random() for keys — predictable. Always crypto.getRandomValues / SecureRandom.
  • Encryption without integrity — attackers can tamper (bit flip). Use AEAD like GCM.

Wrap-up

At its core — algorithm internals belong to libraries; our job is picking the right tool. Symmetric vs asymmetric (or hybrid), AEAD modes (GCM / ChaCha20), proper KDF (Argon2 / PBKDF2).

Practical: modern application baseline — AES-256-GCM (or ChaCha20-Poly1305) + Argon2id (password KDF) + TLS 1.3 (in-transit) + cloud KMS (key management). Zero custom code.

Back to guides